Issue Details (XML | Word | Printable)

Key: VISUALVM-478
Type: Bug Bug
Status: Open Open
Priority: Major Major
Assignee: jsedlacek
Reporter: bebbo
Votes: 1
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
VisualVM

spikey and sometimes scattered graph

Created: 21/Nov/11 09:22 AM   Updated: 10/Apr/12 05:16 PM
Component/s: code
Affects Version/s: 1.3.2, 1.3.3
Fix Version/s: None

Time Tracking:
Not Specified

File Attachments: 1. Java Source File XYPainter.java (15 kB) 21/Nov/11 10:46 AM - bebbo

Image Attachments:

1. graph.gif
(24 kB)

2. graph1.gif
(24 kB)

3. graph2.gif
(21 kB)
Environment:

any


Tags: graph
Participants: bebbo and jsedlacek


 Description  « Hide

Once there are more data values than pixels the graphs are very spikey.
Also sometimes the graph is completely broken.

Find attached some code changes which provide a way steadier graph.

The broken graph results from bogus handling of the clipping rect. That bug is inside the Netbeans code.



bebbo added a comment - 21/Nov/11 10:46 AM

More improved version - max details and stable graph


jsedlacek added a comment - 29/Nov/11 01:45 PM

Thanks for your fix! Actually the current code works as designed, it's a low-quality painter with extremely low overhead which paints in almost constant time no matter how many items are to be displayed.

Your change implements a better-quality painter which is more sensitive to items count in terms of performance. We're thinking of providing more painters to give the user a choice between performance and display quality, you code might be a basis for the other painters.

BTW your changes are hard to read, there's a lot of whitespace and formatting changes in the file which are unrelated to the algorithm modifications. A simple diff without reformatting would be easier to understand.


bebbo added a comment - 01/Dec/11 03:05 PM - edited

I am not sure if it works as intented.

Have a look at the screenshots taken in intervals of few seconds.

1. graph vs graph1: The shape shifts with each tick. Graphs appear or vanish.
2. graph vs graph2: Sometimes the display is screwed up - paint occurs with bogus offset.


bebbo added a comment - 06/Dec/11 07:33 PM

The cause for broken graphs

The broken graphs results from wrong calculation of deltas in TransformableCanvasComponent.setDataBounds(...) which is invoked when the data starts shifting.

Since the values for dx (and dy?) are totaly bogus, I provide a patch which omitts the shifting since dx and dy are set to 0.

diff -r d7d9af603a28 lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java
--- a/lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java	Fri Nov 18 04:06:47 2011 +0100
+++ b/lib.profiler.charts/src/org/netbeans/lib/profiler/charts/canvas/TransformableCanvasComponent.java	Tue Dec 06 20:18:44 2011 +0100
@@ -430,9 +430,12 @@
             dataBoundsChanged(dataOffsetX, dataOffsetY, dataWidth, dataHeight,
                               oldDataOffsetX, oldDataOffsetY, oldDataWidth, oldDataHeight);
 
-            dx = dxx + (oldContentsOffsetX - contentsOffsetX) - (offsetX - oldOffsetX);
-            dy = dyy + (oldContentsOffsetY - contentsOffsetY) - (offsetY - oldOffsetY);
+//            dx = dxx + (oldContentsOffsetX - contentsOffsetX) - (offsetX - oldOffsetX);
+//            dy = dyy + (oldContentsOffsetY - contentsOffsetY) - (offsetY - oldOffsetY);
 
+            // shifting does not work correctly - set it to zero
+            dx = dy = 0;
+            
             oldScaleX = scaleX;
             oldScaleY = scaleY;

XYPainter.java

There is only a rewrite of one function. Here is again the code for that rewritten function. (Using a diff-ignore-white-space or reformat with your own formatter would show it too).

private static int[][] createPoints(XYItem item, Rectangle dirtyArea, SynchronousXYChartContext context, int type,
            int maxValueOffset) {

        // even if this looks like a getter - it updates the context and is needed for mouse over info!
        context.getVisibleBounds(dirtyArea);
        
        int valuesCount = item.getValuesCount();
        double itemValueFactor = type == TYPE_RELATIVE ? getItemValueFactor(context, maxValueOffset,
                item.getBounds().height) : 0;
        int maxValueIndex = 0;
        int extrema = Integer.MIN_VALUE;
        for (int x = 0; x < valuesCount; ++x) {
            int v = Utils.checkedInt(Math.ceil(getYValue(item, x, type, context, itemValueFactor)));
            if (v > extrema) {
                extrema = v;
                maxValueIndex = x;
            }
        }

        // more values than visible? use the visible width
        int visibleCount = valuesCount;
        if (visibleCount > context.getViewWidth())
            visibleCount = (int) context.getViewWidth();
        // use only integers to scale the diagram
        int valsPerPixel = valuesCount / visibleCount;
        if (valuesCount > visibleCount * valsPerPixel) {
            ++valsPerPixel;
            visibleCount = valuesCount / valsPerPixel;
        }

        // + 2 for the polygon - copy this later to match the clip
        int[] xPoints = new int[visibleCount + 2];
        int[] yPoints = new int[visibleCount + 2];

        // calculate an offset to get a stable graph.
        int offset = 0;
        if (valsPerPixel > 1) {
            offset = (-maxValueIndex % valsPerPixel);
        }

        // start from max at median to the right and in 2nd pass to the left
        int median = (maxValueIndex + offset) / valsPerPixel;
        for (int pass = 0, add = 1; pass < 2; ++pass) {
            int lastExt = Integer.MAX_VALUE;
            boolean isMax = true;
            int lastMean = extrema;
            for (int index = median; index >= 0 && index < visibleCount; index += add) {
                int dataIndex = index * valsPerPixel;
                xPoints[index] = Utils.checkedInt(Math.ceil(context.getViewX(item.getXValue(dataIndex))));
                dataIndex -= offset;
                // get the min, max and sum for the values per pixel.
                int min = Integer.MAX_VALUE;
                int max = Integer.MIN_VALUE;
                long sum = 0;
                for (int delta = 0; delta < valsPerPixel; ++delta) {
                    int effectiveIndex = dataIndex + delta;
                    // skip index out of bounds
                    if (effectiveIndex < 0 || effectiveIndex >= valuesCount)
                        continue;
                    int v = Utils
                            .checkedInt(Math.ceil(getYValue(item, effectiveIndex, type, context, itemValueFactor)));
                    if (v < min)
                        min = v;
                    if (v > max)
                        max = v;
                    sum += v;
                }
                if (sum == 0)
                    min = max = lastMean + 0;

                if (isMax) {
                    // the last was a maximum
                    if (max - lastExt > lastExt - min) {
                        // maximum difference wins - patch previous pixel with mean value 
                        yPoints[index - add] = lastMean;
                        yPoints[index] = max;
                        lastExt = max;
                    } else {
                        // first maximum - use it.
                        yPoints[index] = min;
                        lastExt = min;
                        isMax = false;
                    }
                } else {
                    // last was a minimum
                    if (max - lastExt < lastExt - min) {
                        // minimum difference wins - patch the previous pixel with mean value
                        yPoints[index - add] = lastMean;
                        yPoints[index] = min;
                        lastExt = min;
                    } else {
                        // first minimum - use it
                        yPoints[index] = max;
                        lastExt = max;
                        isMax = true;
                    }
                }
                // update the lastMean
                lastMean = (int) (sum / valsPerPixel);
            }
            // change direction
            add = -add;
        }

        return new int[][] { xPoints, yPoints };
    }

The code style differs from your code since I added comments and applied my formatting.

You can also download a patched visualvm at http://franke.ms/download/visualvm_133b1.zip


bebbo added a comment - 13/Dec/11 07:36 PM
Thanks for your fix! Actually the current code works as designed, 

Find my most recent fix here: http://franke.ms/download/visualvm_133b2.zip


bebbo added a comment - 10/Apr/12 05:16 PM

The bug still exists in 1.3.4.

To see the bug in action watch this: http://franke.ms/download/20120410_1505_48.avi

You can download a patched version at http://franke.ms/download/visualvm_134b1.zip