Сначала ответим bretddog:
Кажется, это проблема с FindNearestPoint на графике JapaneseCandleStick;Когда я щелкаю в области графика, он не возвращает индекс ближайшего бара, но я полагаю, что вместо этого он выбирает индекс с ближайшим значением Y, независимо от того, насколько далеко он находится на оси x.Иногда это полоса справа от мыши, иногда слева от мыши.Это так, как это должно работать?
Я сделал эту пользовательскую функцию сам, так что я думаю, что все в порядке .. Тем не менее, было бы неплохо понять, почему FindNearestPoint действует так
Я работаю не с JapaneseCandleStick, а с Line, но я думаю, что это та же проблема.ZedGraph работает с координатами, то есть с точками, а не с функциями, поэтому для определения ближайшей «кривой» она должна интерполироваться, и это кажется очень сложным.
Тем не менее, для линейной графики я разработал функциюполучить ближайшую кривую.Итак, я сделал прямую интерполяцию между каждой последовательной точкой для каждой кривой, и я использовал математическое расстояние для определения ближайшей кривой.Код:
''' <summary>
''' To obtain the nearest curve and its index on ZedGraph stick
''' </summary>
''' <param name="GraphPane">The graphpane on wich you are working</param>
''' <param name="PointLocation">Mouse location</param>
''' <param name="NearestCurve">Reference of the nearest curve</param>
''' <param name="NearestCurveIndex">Index of the nearest curve</param>
''' <returns>True if a curve is found</returns>
''' <remarks></remarks>
Private Function FindNearestCurve(ByVal GraphPane As ZedGraph.GraphPane, ByVal PointLocation As System.Drawing.Point, ByRef NearestCurve As CurveItem, ByRef NearestCurveIndex As Integer) As Boolean
Try
Dim MinDist As Double = -1 'error if < 0
Dim DistTemp As Double
Dim a, b As Double
Dim Curve As CurveItem
Dim ValX, ValY As Double
Dim NormX, NormY As Double
'ini
NearestCurveIndex = -1
GraphPane.ReverseTransform(PointLocation, ValX, ValY) 'To use real values
NormX = GraphPane.XAxis.Scale.Max - GraphPane.XAxis.Scale.Min 'To normalize value when we haven't orthonormal axis
NormY = GraphPane.YAxis.Scale.Max - GraphPane.YAxis.Scale.Min 'To normalize value when we haven't orthonormal axis
'We looking for the nearest curve
For j = 0 To GraphPane.CurveList.Count - 1
Curve = GraphPane.CurveList.Item(j)
If Curve.IsVisible = True Then
'We generate all coefficient (a and b) of straight line interpolation (equation y=ax+b)
For i = 0 To Curve.NPts - 2 '-2 because we work on intervals
'we check if interval is close to the point (to prevent case where the complete interpolation curve is the nearest curve but the real segment is far to the point)
If (Curve.Points.Item(i + 1).Y >= ValY And Curve.Points.Item(i).Y <= ValY) Or
(Curve.Points.Item(i + 1).Y <= ValY And Curve.Points.Item(i).Y >= ValY) Or
(Curve.Points.Item(i + 1).X >= ValX And Curve.Points.Item(i).X <= ValX) Or
(Curve.Points.Item(i + 1).X <= ValX And Curve.Points.Item(i).X >= ValX) Then
'We calculate straight line interpolation coefficient a and b
'Vertical line case
If (Curve.Points.Item(i + 1).X / NormX - Curve.Points.Item(i).X / NormX) = 0 Then
'We calculate directly the distance
DistTemp = Math.Abs(Curve.Points.Item(i).X / NormX - ValX / NormX)
Else 'All other case
'a = (yi+1 - yi) / (xi+1 - xi)
a = (Curve.Points.Item(i + 1).Y / NormY - Curve.Points.Item(i).Y / NormY) / (Curve.Points.Item(i + 1).X / NormX - Curve.Points.Item(i).X / NormX)
'b = yi - a*xi
b = Curve.Points.Item(i).Y / NormY - a * Curve.Points.Item(i).X / NormX
'We calculate the minimum distance between the point and all straight line interpolation
DistTemp = Math.Abs(a * ValX / NormX - ValY / NormY + b) / Math.Sqrt(1 + a * a)
End If
'We test if it's the minimum and save corresponding curve
If MinDist = -1 Then
MinDist = DistTemp 'first time
NearestCurveIndex = j
ElseIf DistTemp < MinDist Then
MinDist = DistTemp
NearestCurveIndex = j
End If
End If
Next
End If
Next
'Return the result
If NearestCurveIndex >= 0 And NearestCurveIndex < GraphPane.CurveList.Count Then
NearestCurve = GraphPane.CurveList.Item(NearestCurveIndex)
Return True
Else
NearestCurve = Nothing
NearestCurveIndex = -1
Return False
End If
Catch ex As Exception
NearestCurve = Nothing
NearestCurveIndex = -1
Return False
End Try
End Function
Я протестировал эту функцию, и она, кажется, работает хорошо, но я не могу гарантировать во всех случаях (действительно, если первая / последняя точка кривой - этоближайшая точка, она не будет обнаружена как таковая).Несколько замечаний по поводу использования:
- Работать только на видимой кривой, чтобы удалить ее, удалить Если Curve.IsVisible = True Тогда line;
- Я нормализовалЗначения X, Y перед расчетом для предотвращения несогласия с неортонормальной осью;
- При возникновении ошибки верните False , и у вас будет NearestCurve = Nothing и NearestCurveIndex = -1 ;
- Курсор линии, который вы хотите перетащить, должен быть кривой (с точками или более, неважно), а не LineObj;
- Проверка, является ли интервал близким или нет, является слабой частью кода, и, на мой взгляд, должно произойти несколько ошибок (я уже определил один - редкий - случай, как было сказано ранее).Проблема также может возникнуть, если у вас не идеальная вертикальная линия (например, с очень большим коэффициентом).
Наконец, я не уверен, что этот код оптимизирован для скорости, но яЯ не замерзну на моей стороне.Лучше всего интегрировать функцию в класс ZedGraph и вычислять каждый коэффициент (a и b) при вызове функции Add , чтобы не вычислять их каждый раз (чтобы каждая мышь двигалась).
Так что я надеюсь, что код поможет некоторым людям создать подвижный курсор, чего очень не хватает в ZedGraph.