Menu

#36 Use DrawPolyLine instead of Drawline for linechart

v0.8.0
closed-fixed
nobody
None
5
2014-02-19
2014-02-03
No

Hi,

In SWTChart the drawLine method in http://sourceforge.net/p/swt-chart/code/300/tree/trunk/org.swtchart/src/org/swtchart/internal/series/LineSeries.java#l520 uses drawline instead of drawPolyLine. The problem with this is when you have 1 point per pixel:
1- it slows down tremendously as a series needs to pumps twice as many vertices,
2- the style is reset, so everything becomes a solid line.

Would a fix be possible?

Thanks, and I love your work!
Matthew

Related

Bugs: #36

Discussion

  • Matthew Khouzam

    Matthew Khouzam - 2014-02-04

    I did some testing:
    with the following code, polylines are 5x faster and diplay styles properly.

    package org.sandbox.swtPlayground;
    
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.events.PaintEvent;
    import org.eclipse.swt.events.PaintListener;
    import org.eclipse.swt.graphics.Color;
    import org.eclipse.swt.graphics.Device;
    import org.eclipse.swt.graphics.GC;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Shell;
    
    public class SWTTest {
    
      public static void main(String[] args) {
        final int res = 1920;
        int height = 1200;
        final int points[] = new int[(res + 1) * 2];
    
        for (int i = 0; i <= res; i++) {
          points[2 * i] = i;
          points[2 * i + 1] = (int) (height / 2 + height / 4
              * Math.sin(i * 1.0 / 180));
        }
        Display display = Display.getDefault();
    
        Device device = (Device) display;
        final Color lineColor = new Color(device, 255, 0, 0);
        final Color polyColor = new Color(device, 0, 255, 0);
        final Color labelColor = new Color(device, 0, 0, 255);
    
        Shell shell = new Shell(display);
        shell.setSize(res, height);
        shell.addPaintListener(new PaintListener() {
    
          @Override
          public void paintControl(PaintEvent e) {
            long tls, tle, tps, tpe;
            GC gc = e.gc;
            gc.setForeground(lineColor);
            gc.setLineStyle(SWT.LINE_DASHDOTDOT);
            tls = System.nanoTime();
            for (int i = 0; i < res; i++) {
              gc.drawLine(points[i * 2], points[i * 2 + 1] + 50,
                  points[(i + 1) * 2], points[(i + 1) * 2 + 1] + 50);
            }
            tle = System.nanoTime();
            gc.setForeground(polyColor);
            gc.setLineStyle(SWT.LINE_DASHDOTDOT);
            tps = System.nanoTime();
            gc.drawPolyline(points);
            tpe = System.nanoTime();
            gc.setForeground(labelColor);
            ((Shell) e.getSource()).setText("Line " + (tle - tls) / 1000
                + "us - poly " + (tpe - tps) / 1000 + "us");
          }
        });
        shell.open();
        while (!shell.isDisposed()) {
          if (!display.readAndDispatch())
            display.sleep();
        }
    
      }
    }
    
     

    Last edit: Matthew Khouzam 2014-02-04
  • yoshitaka

    yoshitaka - 2014-02-05

    Hi Matthew,

    Thanks for your feedback.

    I believe it is the right direction to use drawPolyline(), but some investigation is needed.

    There are two things to take into account.

    1) In LineSeires.drawLine(), vertical lines and slope lines are drawn separately, and they are not always connected. In order to use drawPolyline(), additional line segments are required in order to connect each other.

    2) Using drawPolyline() is 5x faster than drawLine() with your attached code, but it is only 2x faster if setting line width to non-zero value on Windows.

    I have to check how to create a polyline without causing performance degradation (i.e. less than 2x vertices). Also, memory consumption has to be checked, when instantiating polyline array dynamically for each new view port.

    Best Regards,
    Yoshitaka

     
    • Matthew Khouzam

      Matthew Khouzam - 2014-02-05

      I've modified lineseries as a test, this may help things out.

      On Wed, Feb 5, 2014 at 11:47 AM, yoshitaka yoshitaka@users.sf.net wrote:

      Hi Matthew,

      Thanks for your feedback.

      I believe it is the right direction to use drawPolyline(), but some
      investigation is needed.

      There are two things to take into account.

      1) In LineSeires.drawLine(), vertical lines and slope lines are drawn
      separately, and they are not always connected. In order to use
      drawPolyline(), additional line segments are required in order to connect
      each other.

      2) Using drawPolyline() is 5x faster than drawLine() with your attached
      code, but it is only 2x faster if setting line width to non-zero value on
      Windows.

      I have to check how to create a polyline without causing performance
      degradation (i.e. less than 2x vertices). Also, memory consumption has to
      be checked, when instantiating polyline array dynamically for each new view
      port.

      Best Regards,
      Yoshitaka


      Status: open
      Created: Mon Feb 03, 2014 11:20 PM UTC by Matthew Khouzam
      Last Updated: Tue Feb 04, 2014 03:08 PM UTC
      Owner: nobody

      Hi,

      In SWTChart the drawLine method in
      http://sourceforge.net/p/swt-chart/code/300/tree/trunk/org.swtchart/src/org/swtchart/internal/series/LineSeries.java#l520uses drawline instead of drawPolyLine. The problem with this is when you
      have 1 point per pixel:
      1- it slows down tremendously as a series needs to pumps twice as many
      vertices,
      2- the style is reset, so everything becomes a solid line.

      Would a fix be possible?

      Thanks, and I love your work!
      Matthew


      Sent from sourceforge.net because you indicated interest in
      https://sourceforge.net/p/swt-chart/bugs/36/

      To unsubscribe from further messages, please visit
      https://sourceforge.net/auth/subscriptions/

      --
      Matthew Khouzam, Human Being

       

      Related

      Bugs: #36

  • yoshitaka

    yoshitaka - 2014-02-07

    Depending on line width and y series values, performance is much different. For example, with the attached code (DrawPolyline.java), drawPolyline is 100x slower than drawLine.

    Since SWTChart should support handling arbitrary y series, the attached code (DrawingPerformance.java) should also work with reasonable performance, but doesn't show good result as below in my environment.

    Case 1) as is
    => 30 ms

    Case 2) replaced drawLine with drawPolyline: (LineSeries.java.case2)
    => 40000 ms

    Case 3) replaced drawLine with drawPolyline with optimization: (LineSeries.java.case3)
    => 6000 ms

    Further investigation is needed (considering the way to switch the drawing method depending on given series and configuration).

     
    • Matthew Khouzam

      Matthew Khouzam - 2014-02-07

      That is interesting, did drawline show the styles? That was the main issue,
      the performance was a theoretical bonus. :)

      Our first issue was that lineseries with too many points (let's say spaced
      a pixel apart, the lines are not "dashdots" but rather solid. Polyline
      appears to be well displayed.

      On Fri, Feb 7, 2014 at 11:49 AM, yoshitaka yoshitaka@users.sf.net wrote:

      Depending on line width and y series values, performance is much
      different. For example, with the attached code (DrawPolyline.java),
      drawPolyline is 100x slower than drawLine.

      Since SWTChart should support handling arbitrary y series, the attached
      code (DrawingPerformance.java) should also work with reasonable
      performance, but doesn't show good result as below in my environment.

      Case 1) as is
      => 30 ms

      Case 2) replaced drawLine with drawPolyline: (LineSeries.java.case2)
      => 40000 ms

      Case 3) replaced drawLine with drawPolyline with optimization:
      (LineSeries.java.case3)
      => 6000 ms

      Further investigation is needed (considering the way to switch the drawing
      method depending on given series and configuration).

      Attachment: DrawingPerformance.java (1.5 kB; application/octet-stream)
      DrawPolyline.java (2.5 kB; application/octet-stream) LineSeries.java.case2
      (23.7 kB; application/octet-stream) LineSeries.java.case3 (24.3 kB;
      application/octet-stream)


      Status: open
      Created: Mon Feb 03, 2014 11:20 PM UTC by Matthew Khouzam
      Last Updated: Wed Feb 05, 2014 04:47 PM UTC
      Owner: nobody

      Hi,

      In SWTChart the drawLine method in
      http://sourceforge.net/p/swt-chart/code/300/tree/trunk/org.swtchart/src/org/swtchart/internal/series/LineSeries.java#l520uses drawline instead of drawPolyLine. The problem with this is when you
      have 1 point per pixel:
      1- it slows down tremendously as a series needs to pumps twice as many
      vertices,
      2- the style is reset, so everything becomes a solid line.

      Would a fix be possible?

      Thanks, and I love your work!
      Matthew


      Sent from sourceforge.net because you indicated interest in
      https://sourceforge.net/p/swt-chart/bugs/36/

      To unsubscribe from further messages, please visit
      https://sourceforge.net/auth/subscriptions/

      --
      Matthew Khouzam, Human Being

       

      Related

      Bugs: #36

  • Matthew Khouzam

    Matthew Khouzam - 2014-02-07

    Hi Yoshitaka,

    I got your drawingperformance test up and running, I just need to fin where the draw function is that will be called by the profiler. Could you please point me to it.
    Also, I see why it works here:
    a- the random seed is not set, so the test results are not super reproductible.
    b- The swing is so big that the line style is maintained in that example.
    c- you tested with more points than we have pixels. This is an interesting case. I will look into it more. :)

    Thanks for your super fast help! :)

    Matt

     
  • yoshitaka

    yoshitaka - 2014-02-10

    To workaround the bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=243588 on Windows, GC.setAdvanced(true) would be called before drawing polyline. Until that bug is fixed, original code calling drawLine() would be still used when line style is not set, in order to minimize the risk of side effect.

     
  • Matthew Khouzam

    Matthew Khouzam - 2014-02-10

    So I am using linux, I didn't experience that bug, and am now curious if mac has a third behaviour. I tested this thouroughly imo using the sin wave and growing point samples, these results are only on my machine:

    points, window resolution, acceleration from drawline to polyline, are styles well displayed in drawline?, polyline?
    10, 500x400, 0.75 , no, yes
    10, 1920x1200, 0.6 , no, yes
    100, 500x400, 1.5 , no, yes
    100, 1920x1200, 2 , no, yes
    1000, 500x400, 4.2 , no, yes
    1000, 1920x1200, 4.1 , no, yes
    5000, 500x400, 5.4 , no, yes
    5000, 1920x1200, 20 , no, yes
    10000, 500x400, 8 , no, yes
    10000, 1920x1200, 8 , no, yes
    50000, 500x400, 174
    , no, yes
    50000, 1920x1200, 12 , no, yes

    • these are probably glitches.

    please take a look at this patch if you wish to reproduce the results. Run the test called LineChartBenchmarkExample located in org.swtchart.examples

     
  • yoshitaka

    yoshitaka - 2014-02-12

    Hi Matthew,

    The attached file (LineSeries.java) has the changes to switch the drawing method depending on whether line style is set. That is, if line style is set, drawPolyline() is used with workaround of setting ‘advanced’, otherwise drawLine() is used as it is today. From functionality point of view, that idea should be ok, but the performance looks not very good. The way of creating polyline is not optimal. Especially when there is vertical line in every pixel, there is around 50% redundancy. Your optimization looks interesting, but I have seen some cases where line is not correctly drawn (reproducible with Example.java). I will spend some additional effort to look for better optimization, but if not found, I would take the attached code as solution for now. Any other idea or feedback is welcome. Thanks.

    Best Regards,
    Yoshitaka

     
  • Matthew Khouzam

    Matthew Khouzam - 2014-02-12

    This patch looks very good to me, it solves our immediate issue here.

    Thanks!

    ps. as for the optimisation, and this will be at a later time, I see 3 easy optimisations:
    1- avoid boxing/unboxing, so always staying int, that means ditching lists. :(
    2- removing consecutive points that overlap. this irl will save in some cases here 90-99% of the points to draw.
    3- removing middle points that are aligned. This I found in my tests save 5-10% of points but requires more code to check and you have to deal with all the div by 0.

     
  • yoshitaka

    yoshitaka - 2014-02-19
    • status: open --> closed-fixed