Page 1 of 2

Teechart Version 3 .NET Cursor Performance

Posted: Tue Jan 23, 2007 1:55 pm
by 9642161
I downloaded the new version and like some of the new features. I was hoping for some improvements in the cursor's performance. With large datasets the cursor lags the mouse. On the example that you supply in the demo this is also the case. Are there plans to improve this before release.

I will be spending some time going through the new additions later this week.

Posted: Tue Jan 23, 2007 2:18 pm
by narcis
Hi histry,

Could you please post the exact steps we should follow to reproduce this issue here?

Thanks in advance.

Posted: Tue Jan 23, 2007 4:15 pm
by 9642161
NarcĂ­s

If I use your example and look at the Synchronizing Two and move the mouse accross the chart there is a lag between cursor and mouse. In some of the charts we create we could have a number of traces with over 10,000 points and what we see if that the reaction between mouse and cursor gets worst.

Posted: Wed Jan 24, 2007 8:58 am
by Chris
Hello histry,

First of all, may I thank you very much for your interest in the TeeChart for .NET v3 BETA.

The drawing of the cursor on the chart is an interesting and challenging problem. In order for the cursor to be seen to be moving when the mouse is moved, the chart has to be redrawn every time the mousemove event is fired. This means drawing everything you want to be visible including your tens of thousands of points. We have considered introducing further clipping regions into the chart painting algorithmns, but we feel that in this case the situation will not be improved given that the area that the cursor covers and therefore the area that needs to be repainted represents the vast majority of chart objects that need to be painted anyway.

Posted: Wed Jan 24, 2007 1:02 pm
by 9642161
I am sorry to hear that as now we will need to change to cursor move by click. Do other products use a similiar method to move the cursor as in our evaluation of a couple of other products cursor movement was very responsive.

Thanks.

Posted: Thu Jan 25, 2007 8:38 am
by Chris
Hello histry,

I'm afraid I can't tell you how other charting tools implement cursor drawing, however, it is interesting that you say that they are more responsive and it is certainly an area we are investigating.

Posted: Thu Jan 25, 2007 12:55 pm
by 9642161
If you want to send me an email I can give you the name of another chart object that is very responsive. We have been using this object for a number of years but do not feel that their features are as rich as TeeChart

Posted: Thu Jan 25, 2007 12:59 pm
by Chris
Hello histry,

Is it a 100% managed .NET component? If it is not and is a Win32 component (COM?), then using an GDI XOR pen to draw the cursor would solve the performance problem. We did experiment using the ControlPaint classes methods (.NET's XOR equivalent) but from what I remember the results were excessively flickery. Maybe it's time we looked at such a technique again to see if we can perfect it. I'll let you know.

Posted: Thu Jan 25, 2007 1:23 pm
by Chris
Hello again histry,

The use of ControlPaint can be seen in this example:

Code: Select all

    int X, Y;
    Point start = Point.Empty;
    Point end = Point.Empty;

    public Form3()
    {
      InitializeComponent();
      InitializeChart();
    }

    private Steema.TeeChart.Styles.Line bar;
    private void InitializeChart()
    {
      tChart1.Series.Add(bar = new Steema.TeeChart.Styles.Line());

      for (int i = 0; i < tChart1.Series.Count; i++)
      {
        tChart1[i].FillSampleValues(500);
      }
      
    }

    private void tChart1_MouseMove(object sender, MouseEventArgs e)
    {
      X = e.X;
      Y = e.Y;
     // tChart1.Invalidate();
      DrawCursor();
    }

    private void DrawCursor()
    {
      Rectangle rect = tChart1.Chart.ChartRect;
      start.X = X;
      start.Y = rect.Top;
      end.X = X;
      end.Y = rect.Bottom;

      start = tChart1.PointToScreen(start);
      end = tChart1.PointToScreen(end);

      ControlPaint.DrawReversibleLine(start, end, tChart1.Panel.Color);
    }

    private void tChart1_MouseHover(object sender, EventArgs e)
    {
      DrawCursor();
    }
Here we can see that ControlPaint has no performance effect on the chart when it paints but leaves a trace of it's path as the mouse mouse. Uncommenting the tChart1.Invalidate() line removes the trace but then takes you back to square one as the whole chart needs to repaint itself. Any suggestions gratefully received :D

Posted: Thu Jan 25, 2007 4:13 pm
by 9642161
The component is a .net control that wraps their dll. The dll is not a .net assembly and their activex component uses the same dll.

Can we not invalidate only the region that needs to be repainted

Posted: Thu Jan 25, 2007 5:22 pm
by Chris
Hello histry,

I believe your version of TeeChart for .NET also shipped with the TeeChart Pro ActiveX control if you're happy to use an unsafe dll. I'm quite sure the cursor of the AX control is quicker than the .NET version due to the XOR pen mentioned above.

The smallest TeeChart object/area which would need to be repainted would be the chart rectangle (the area within the axis), which is where all the points are. The elements within the chart rectangle, the points, axis grids, panel colours/gradients etc. are drawn as wholes to the chartrect and as such the chartrect can't be repainted in 'strips' a little bit bigger in size than the cursor line. This is not to say that improving cursor performance is impossible, it's just saying that increases in performance are very complex propositions, IMO.

Posted: Fri Jan 26, 2007 2:06 pm
by 9333098
I use cursors with VCL in WIN32 and do not know if the following applies to the .NET version. When the cursor moves, is the matching data point being searched for ? If so, I believe the VCL version always starts it search from the begiining of the data values list. It seems a quicker approach would be to begin from the previous data index and search above and below from there, assuming the points are ordered.

Alos, if the cursor Snap is true, then use the OnSnapChange event instead of OnChange so the cursor event fires (and thus requires the chart to be redrawn) only when the data point to which it is associated actualy changes, and not when the pixel on the chart changes.

Steve

Posted: Mon Jan 29, 2007 10:57 am
by Chris
Hello histry,
The smallest TeeChart object/area which would need to be repainted would be the chart rectangle (the area within the axis), which is where all the points are. The elements within the chart rectangle, the points, axis grids, panel colours/gradients etc. are drawn as wholes to the chartrect and as such the chartrect can't be repainted in 'strips' a little bit bigger in size than the cursor line. This is not to say that improving cursor performance is impossible, it's just saying that increases in performance are very complex propositions, IMO.
Or not <g>. Silly me, there is a much simpler way than repainting the whole chart or invalidating only the area around the cursor which is simply to paint the Reversible line (.NET's XOR pen) twice. The following code produces a cursor which is lightening quick even for 50,000 points:

Code: Select all

 int X, Y;
    Point start = Point.Empty;
    Point end = Point.Empty;

    public Form3()
    {
      InitializeComponent();
      InitializeChart();
    }

    private Steema.TeeChart.Styles.Line bar;
    private void InitializeChart()
    {
      tChart1.Series.Add(bar = new Steema.TeeChart.Styles.Line());

      for (int i = 0; i < tChart1.Series.Count; i++)
      {
        tChart1[i].FillSampleValues(50000);
      }
      
    }

    private void tChart1_MouseMove(object sender, MouseEventArgs e)
    {
      DrawCursor();
      X = e.X;
      Y = e.Y;
      DrawCursor();
    }

    private void DrawCursor()
    {
      Rectangle rect = tChart1.Chart.ChartRect;
      if (X > 0 && Y > 0)
      {
        start.X = X;
        start.Y = rect.Top;
        end.X = X;
        end.Y = rect.Bottom;

        start = tChart1.PointToScreen(start);
        end = tChart1.PointToScreen(end);

        ControlPaint.DrawReversibleLine(start, end, tChart1.Panel.Color);

        label1.Text = "XValue: " + tChart1.Axes.Bottom.CalcPosPoint(X);
      }
    }

Posted: Mon Jan 29, 2007 11:57 am
by Chris
Hello histry,

This code has now been added to the CursorTool class and can be called when the new FastCursor property is set to true.

Posted: Mon Jan 29, 2007 12:53 pm
by Chris
Hello Steve,
I use cursors with VCL in WIN32 and do not know if the following applies to the .NET version. When the cursor moves, is the matching data point being searched for ? If so, I believe the VCL version always starts it search from the begiining of the data values list. It seems a quicker approach would be to begin from the previous data index and search above and below from there, assuming the points are ordered.
The .NET code and the VCL code is to all extents and purposes indentical in this area. The data point, that is, the axis xvalue and axis yvalue, is being calculated in a private method called CalcValuePositions() which is invoked on every mousemove (when FollowMouse=true). This method, in turn, invokes the Axis.CalcPosPoint() method of the respective vertical and horizontal axes which calculates the point using very simply pixel calculations on the axis length and range instead of trailing through the ValueLists to calculate the value.
Alos, if the cursor Snap is true, then use the OnSnapChange event instead of OnChange so the cursor event fires (and thus requires the chart to be redrawn) only when the data point to which it is associated actualy changes, and not when the pixel on the chart changes.
Well, firstly neither the invoking of the OnSnapChange event nor the invoking of the OnChange event cause a Chart Repaint. I think that the OnSnapChange event is only being called when the snap actually changes and not when the mouse pixel position changes, as can be seen in this code example (teechart vcl v8 beta code):

Code: Select all

procedure TForm1.ChartTool1Change(Sender: TCursorTool; x, y: Integer;
  const XValue, YValue: Double; Series: TChartSeries; ValueIndex: Integer);
begin
  Inc(count); //count is a private form variable
  Label1.Caption := IntToStr(count);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    InitializeChart;
end;

procedure TForm1.InitializeChart;
var
  Series : TLineSeries;
  Tool : TCursorTool;
begin
   Series := TLineSeries.Create(Self);
   Series.ParentChart := Chart1;
   Series.FillSampleValues(5);

   Tool := TCursorTool.Create(Self);
   Tool.ParentChart := Chart1;
   Tool.Series := Series;
   Tool.Style := cssVertical;
   Tool.FollowMouse := true;
   Tool.Snap := true;

   Tool.OnSnapChange := ChartTool1Change;
end;