Page 1 of 1

Display values on mouse move

Posted: Tue Dec 07, 2010 10:49 am
by 15054354
Hi,

is it possible to display values for all series on mouse move as shown in the example below?

We would like to have on mouse move :

1. On left axe: highlighted values for each curve
2. On bottom axe highlighted value for the date
3. Dots on series to show where the mouse is
mouseMove_Values.png
mouseMove_Values.png (92.66 KiB) Viewed 10010 times

Thanks,

Petar

Re: Display values on mouse move

Posted: Tue Dec 07, 2010 11:19 am
by narcis
Hi Petar,

Yes, this is possible using interpolation as in this example:

Code: Select all

Option Explicit

Dim XCursorValue As Double

Private Sub Form_Load()
    TChart1.Aspect.View3D = False
    
    TChart1.AddSeries scLine
    TChart1.AddSeries scLine
    TChart1.AddSeries scLine
    
    TChart1.Tools.Add tcCursor
    TChart1.Tools.Items(0).asTeeCursor.FollowMouse = True
    TChart1.Tools.Items(0).asTeeCursor.Style = cssVertical
    
    Dim i As Integer
    
    For i = 0 To TChart1.SeriesCount - 1
        TChart1.Series(i).FillSampleValues (i + 1) * 20
    Next
   
End Sub

Function InterpolateSeries(ByVal SeriesIndex As Long, XValue As Double) As Double
    InterpolateSeries = InterpolateLineSeries(SeriesIndex, TChart1.Series(SeriesIndex).FirstValueIndex, TChart1.Series(SeriesIndex).LastValueIndex, XValue)
End Function

Function InterpolateLineSeries(ByVal SeriesIndex As Long, FirstIndex As Integer, LastIndex As Integer, XValue As Double) As Double
  Dim index As Integer
  Dim dx, dy, val As Double

  index = FirstIndex

  Do While ((TChart1.Series(SeriesIndex).XValues.Value(index) <= XValue) And (index < LastIndex))
    index = index + 1
  Loop
 
  ' safeguard
  If (index < 1) Then
    index = 1
  ElseIf (index >= TChart1.Series(SeriesIndex).Count) Then
    index = TChart1.Series(SeriesIndex).Count - 1
  End If
 
  ' y=(y2-y1)/(x2-x1)*(x-x1)+y1
  dx = TChart1.Series(SeriesIndex).XValues.Value(index) - TChart1.Series(SeriesIndex).XValues.Value(index - 1)
  dy = TChart1.Series(SeriesIndex).YValues.Value(index) - TChart1.Series(SeriesIndex).YValues.Value(index - 1)
 
  If (dx <> 0) Then
    InterpolateLineSeries = dy * (XValue - TChart1.Series(SeriesIndex).XValues.Value(index - 1)) / dx + TChart1.Series(SeriesIndex).YValues.Value(index - 1)
  Else
    InterpolateLineSeries = 0
  End If
End Function

Private Sub TChart1_OnCursorToolChange(ByVal Tool As Long, ByVal X As Long, ByVal y As Long, ByVal XVal As Double, ByVal YVal As Double, ByVal Series As Long, ByVal ValueIndex As Long)
    XCursorValue = XVal
    
    TChart1.Header.Text.Clear
    
    Dim i As Integer
    
    For i = 0 To TChart1.SeriesCount - 1
        TChart1.Header.Text.Add ("Series" + CStr(i) + ": Y(" + CStr(XVal) + ")= " + _
                 CStr(InterpolateLineSeries(i, TChart1.Series(i).FirstValueIndex, TChart1.Series(i).LastValueIndex, XVal)))
    Next
End Sub

Private Sub TChart1_OnAfterDraw()
    Dim xs, ys, i As Integer
    
    xs = TChart1.Axis.Bottom.CalcXPosValue(XCursorValue)
    
    For i = 0 To TChart1.SeriesCount - 1
        ys = TChart1.Axis.Left.CalcYPosValue(InterpolateSeries(i, XCursorValue))
        TChart1.Canvas.Brush.Color = TChart1.Series(i).Color
        TChart1.Canvas.Ellipse xs - 4, ys - 4, xs + 4, ys + 4
    Next
End Sub

Re: Display values on mouse move

Posted: Tue Dec 07, 2010 11:40 am
by 15054354
Thanks Narcis,

this is perfect for the dots part. Is it possible now to highlight values directly on the left and the bottom axis.

Thanks,

Petar

Re: Display values on mouse move

Posted: Tue Dec 07, 2010 2:27 pm
by 16657923
Hi Narcis,

This method works fine but there is some performances issue with a large number of points (>3000 points) :?
interpolate.png
interpolate.png (42.39 KiB) Viewed 10102 times
Is it possible to improve it especially when we move mouse cursor on the right part of the bottom axis ?

Thanks for your help :wink:

Guilz

Re: Display values on mouse move

Posted: Tue Dec 07, 2010 3:37 pm
by narcis
Hi Guilz,

Yes, highlighting could be done using Annotation tools, for example:

Code: Select all

Option Explicit

Dim XCursorValue As Double

Private Sub Form_Load()
    TChart1.Aspect.View3D = False
   
    TChart1.AddSeries scLine
    TChart1.AddSeries scLine
    TChart1.AddSeries scLine
   
    TChart1.Tools.Add tcCursor
    TChart1.Tools.Items(0).asTeeCursor.FollowMouse = True
    TChart1.Tools.Items(0).asTeeCursor.Style = cssVertical
   
    Dim i As Integer
   
    For i = 0 To TChart1.SeriesCount - 1
        TChart1.Series(i).FillSampleValues (i + 1) * 20
        TChart1.Tools.Add tcAnnotate 'one annotation for each series
    Next
   
    TChart1.Tools.Add tcAnnotate 'one annotation for X values.
End Sub

Function InterpolateSeries(ByVal SeriesIndex As Long, XValue As Double) As Double
    InterpolateSeries = InterpolateLineSeries(SeriesIndex, TChart1.Series(SeriesIndex).FirstValueIndex, TChart1.Series(SeriesIndex).LastValueIndex, XValue)
End Function

Function InterpolateLineSeries(ByVal SeriesIndex As Long, FirstIndex As Integer, LastIndex As Integer, XValue As Double) As Double
    Dim index As Integer
    Dim dx, dy, val As Double
    
    index = FirstIndex
    
    Do While ((TChart1.Series(SeriesIndex).XValues.Value(index) <= XValue) And (index < LastIndex))
      index = index + 1
    Loop
    
    ' safeguard
    If (index < 1) Then
      index = 1
    ElseIf (index >= TChart1.Series(SeriesIndex).Count) Then
      index = TChart1.Series(SeriesIndex).Count - 1
    End If
    
    ' y=(y2-y1)/(x2-x1)*(x-x1)+y1
    dx = TChart1.Series(SeriesIndex).XValues.Value(index) - TChart1.Series(SeriesIndex).XValues.Value(index - 1)
    dy = TChart1.Series(SeriesIndex).YValues.Value(index) - TChart1.Series(SeriesIndex).YValues.Value(index - 1)
    
    If (dx <> 0) Then
      InterpolateLineSeries = dy * (XValue - TChart1.Series(SeriesIndex).XValues.Value(index - 1)) / dx + TChart1.Series(SeriesIndex).YValues.Value(index - 1)
    Else
      InterpolateLineSeries = 0
    End If
End Function

Private Sub TChart1_OnCursorToolChange(ByVal Tool As Long, ByVal X As Long, ByVal y As Long, ByVal XVal As Double, ByVal YVal As Double, ByVal Series As Long, ByVal ValueIndex As Long)
    XCursorValue = XVal
   
    TChart1.Header.Text.Clear
   
    Dim i As Integer
   
    For i = 0 To TChart1.SeriesCount - 1
        TChart1.Header.Text.Add ("Series" + CStr(i) + ": Y(" + CStr(XVal) + ")= " + _
                 CStr(InterpolateLineSeries(i, TChart1.Series(i).FirstValueIndex, TChart1.Series(i).LastValueIndex, XVal)))
    Next
End Sub

Private Sub TChart1_OnAfterDraw()
    Dim xs, ys, y, i As Integer
   
    xs = TChart1.Axis.Bottom.CalcXPosValue(XCursorValue)
   
    For i = 0 To TChart1.SeriesCount - 1
        y = InterpolateSeries(i, XCursorValue)
        ys = TChart1.Axis.Left.CalcYPosValue(y)
        TChart1.Canvas.Brush.Color = TChart1.Series(i).Color
        TChart1.Canvas.Ellipse xs - 4, ys - 4, xs + 4, ys + 4
        
        With TChart1.Tools.Items(i + 1).asAnnotation
            .Text = Format(y, "0.00")
            .Shape.Color = vbRed
            .Shape.CustomPosition = True
            .Shape.Left = TChart1.Axis.Left.Position - 40
            .Shape.Top = ys - 10
        End With
    Next
    
    With TChart1.Tools.Items(4).asAnnotation
        .Text = Format(XCursorValue, "0.00")
        .Shape.Color = vbRed
        .Shape.CustomPosition = True
        .Shape.Left = xs - 10
        .Shape.Top = TChart1.Axis.Bottom.Position + 10
    End With
End Sub
Regarding the performance issue, the problem is that interpolation is calculated from first visible point in the series and loops through all the points in the series. Hence it takes longer the further the point is from the origin. Some optimization could be done in the InterpolateLineSeries so that loop doesn't go through all point in the series.

Re: Display values on mouse move

Posted: Tue Dec 07, 2010 3:50 pm
by 15054354
Thank you Narcis,

that's exactly what we were looking for. Extremely useful.

Thanks,

Petar

Re: Display values on mouse move

Posted: Tue Dec 07, 2010 4:30 pm
by 16657923
>Some optimization could be done in the InterpolateLineSeries so that loop doesn't go through all point in the series.

Yes. I tried to use the code bellow to optimize the InterpolateLineSeries method (loop is now starting near the mouse position and not from the FirstValueIndex)

Code: Select all

Private Sub TChart1_OnMouseMove(ByVal Shift As TeeChart.EShiftState, ByVal X As Long, ByVal Y As Long)
    firstIndex = TChart1.Axis.Bottom.CalcPosPoint(X)
End Sub
It's seem to work fine

Thanks a lot for your help :D

Guilz