ZedGraph - Как сделать горизонтальную линию перетаскиваемой? - PullRequest
3 голосов
/ 30 сентября 2010

У меня есть несколько прямых горизонтальных линий, которые я хочу, чтобы пользователь мог перетаскивать вертикально. Как это было бы возможно? Я думаю, что лучшим параметром для выбора линии будет фиксированное количество пикселей возле линии. Поэтому, если размер мыши составляет +/- 2 пикселя, я должен изменить курсор мыши и сделать линию перетаскиваемой. Я вижу, что класс CurveItem имеет свойства IsSelectable и IsSelected. Будут ли они иметь какую-либо функцию в решении этой проблемы? Я не могу понять, для чего они, читая документацию к классу ..


РЕДАКТИРОВАТЬ:

Похоже, что FindNearestPoint FindNearestObject ) только ищут фактические точки. Как бы я сделал выбор для работы по всему отрезку прямой? Полагаю, мне нужно было бы создать собственную процедуру «Поиск», которая проходит по всем строкам, которые я хочу проверить, и для каждого вычисляю свою мнимую точку Y на основе позиции X мыши (? ) Я также думаю о наклонных линиях для этой цели, для горизонтальных / вертикальных линий это будет немного проще. По крайней мере, кажется, что это необходимо для объекта кривой, но я предполагаю, что то же самое должно быть сделано для выбора (в середине) LineObj?

Я на самом деле не знал о существовании LineObj . Кажется, что LineObj не может изменить координаты X2 / Y2 , так как они ReadOnly . Так можно ли вообще перетащить точку X2 / Y2 на LineObj?


РЕДАКТИРОВАТЬ 2:

Кажется, проблема с FindNearestPoint для графа JapaneseCandleStick; Когда я щелкаю по графической панели, он не возвращает индекс ближайшего бара, но я считаю, что вместо этого он выбирает индекс с ближайшим значением Y, независимо от того, как далеко он находится на оси х является. Иногда это полоса справа от мыши, иногда слева от мыши. Это так, как это должно работать?

Я сделал эту пользовательскую функцию сам, так что я думаю, что все в порядке .. Тем не менее, было бы неплохо понять, почему FindNearestPoint действует таким образом.

Это код мыши.Вниз:

   ' Find nearest curve point:
   Dim ciNearestCurve As CurveItem
   Dim iNearestCurve As Integer
   Dim b As Boolean = zgc.GraphPane.FindNearestPoint(mousePt, zgc.GraphPane.CurveList, ciNearestCurve, iNearestCurve)
   If b Then
       With ciNearestCurve(iNearestCurve)
           Debug.Print(Date.FromOADate(.X) & " " & .Y & " " & .Z)
       End With

Ответы [ 2 ]

1 голос
/ 04 мая 2011

Сначала ответим 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.

1 голос
/ 01 октября 2010

Посмотрите этот урок на , перетаскивая точки с помощью мыши .

Если вы используете LineObj вместо кривой, взгляните на FindNearestObject метод.

Также, если вы хотите создать некоторую «область чувствительности» для нажатия, этот метод должен помочь вам преобразовать координаты мыши в пикселях в координаты масштаба панели.

Основная идея заключается в следующем:
- подписаться на события MouseDown, MouseUp и MouseMove
- в обработчике для проверки события MouseDown, если точка щелчка находится рядом с кривой/ объект графика, который вы хотите переместить
- выполните изменение аналогично тому, как это показано в примере с первой ссылки

РЕДАКТИРОВАТЬ
Относительно вашего редактирования:
Предположим, у вас есть горизонтальная кривая myCurve, содержащая две точки.Используя FindNearestPoint, вы можете найти ближайшую точку нажатия и кривую, содержащую эту точку.

Итак, у вас есть:

// mousePt is where you clicked
CurveItem nearestCurve = null;
int nearestID = -1;

myPane.FindNearestPoint(mousePt, out nearestCurve, out nearestID);
if(nearestCurve!=null)
   // remember the curve somewhere. 

Затем обработайте события MouseMove и MouseUp, чтобы узнать, сколько вам нужно для перемещения кривой.Вам нужно знать только изменение направления Y (или Y2), поскольку кривая горизонтальна, и вы, вероятно, не хотите перемещать ее вдоль оси X.

Когда вы узнаете, сколько вам нужно переместитьсвою кривую (dy), просто сделайте:

for(int i=0; i<nearestCurve.Points.Count; i++)
    nearestCurve.Points[i].Y += dy;

Что касается вашего второго вопроса, в документации для LineObj.Location.Y2 у вас есть:

Обратите внимание, что позиция Y2 хранится внутри как смещение высоты от Y. Свойства

и Width / Height могут быть легко установлены, поэтому вы можете сделать это следующим образом.

...