3

我有一些水平直线,我希望用户能够垂直拖动。这怎么可能?我认为线选择的最佳参数是线附近的固定数量的像素。因此,如果鼠标是 +/- 2 像素,我应该更改鼠标光标并使线条可拖动。我看到 CurveItem 类具有属性 IsSelectable 和 IsSelected。这些对解决这个问题有什么作用吗?通过阅读课程文档,我无法真正理解它们的用途..


编辑:

似乎FindNearestPoint(和FindNearestObject)只搜索实际点。我将如何选择沿直线的整个部分工作?我想我需要制作自己的自定义“查找”例程,循环遍历我要检查的所有行,并根据鼠标 X 位置()为每个计算它的假想 Y 点我也在考虑用于此目的的斜线,对于水平/垂直线,它会稍微简单一些。至少曲线项似乎需要这样做,但我认为选择LineObj(在中间部分)也必须这样做?

我实际上不知道LineObj的存在。似乎 LineObj 无法更改X2/Y2坐标,因为它们是ReadOnly。那么是否可以拖动 LineObj 的 X2/Y2 点?


编辑2:

JapaneseCandleStick 图上的 FindNearestPoint 似乎是个问题;当我在图表窗格中单击时,它不会返回最近柱的索引,但我相信它会选择具有最接近 Y 值的索引,无论它在 x 轴上有多远。有时它在鼠标右侧,有时在鼠标左侧。这是它的工作方式吗?

我自己制作了这个自定义函数,所以我想这没关系..仍然很高兴理解 FindNearestPoint 为什么会这样。

这是mouseDown上的代码:

   ' 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
4

3 回答 3

1

首先回答 bretddog:

JapaneseCandleStick 图上的 FindNearestPoint 似乎是个问题;当我在图表窗格中单击时,它不会返回最近柱的索引,但我相信它会选择具有最接近 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

我已经测试过这个函数,它似乎工作得很好,但我不能保证在所有情况下(事实上,如果曲线的第一个/最后一个点是最近的点,它就不会被检测到)。关于使用的一些说明:

  • 仅在可见曲线上工作,要删除它,请删除If Curve.IsVisible = True Then line ;
  • 我在计算之前对 X、Y 值进行了归一化,以防止与非正交轴不一致;
  • 当有错误时,返回False并且你有NearestCurve = NothingNearestCurveIndex = -1
  • 您要拖动的线光标应该是曲线(无论是to点还是更多),而不是LineObj;
  • 间隔是否接近的测试是代码的薄弱部分,我认为它应该会发生一些错误(我已经确定了一个 - 罕见的 - 案例,如前所述)。如果您的垂直线不完美(因此 a 系数非常大),也会出现问题。

最后,我不确定这段代码是否针对速度进行了优化,但我并没有冻结。最好的方法应该是将函数集成到 ZedGraph 类中,并在调用Add函数时计算每个系数(a 和 b),这样就不用每次都计算它们(所以每次鼠标移动)。

所以我希望这些代码能帮助一些人创建一个可移动的光标,这是 ZedGraph 中非常缺少的东西。

于 2011-05-04T15:22:31.097 回答
1

看看这个关于用鼠标拖动点的教程。

如果您使用的是 aLineObj而不是曲线,请查看该FindNearestObject方法。

此外,如果您想为单击创建一些“敏感区域”,此方法应该可以帮助您将鼠标坐标(以像素为单位)转换为窗格比例坐标。

主要思想是:
- 订阅MouseDown,MouseUpMouseMove事件
- 在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. 

接下来处理MouseMoveMouseUp事件以找出移动曲线所需的量。您只需要知道 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属性可以很容易地设置,所以你可以这样做。

于 2010-10-01T13:42:53.633 回答
1

我需要在绘图上拖动一个线对象。我花了很长时间才弄清楚该怎么做。以下代码特定于我的应用程序,并不完整,但它确实有效,我认为对于其他需要这样做的人来说,这是一个很好的起点。我的代码在 VB 中。它的本质是使用 MouseDownEvent 来判断光标是否足够靠近要拖动的对象。然后在 MouseMoveEvent 中确定新位置并更新绘图。

Private Function zgPlot_MouseDown(sender As ZedGraphControl, e As MouseEventArgs) As Boolean Handles zgPlot.MouseDownEvent

    'Return true if you have handled the mouse event entirely, and you do not want the ZedGraphControl to do any further action (e.g., starting a zoom operation). 
    'Return False If ZedGraph should go ahead And process the mouse Event.
    mbDraggingThresholdLine = False
    mbThresholdHasChanged = False

    If e.Button = MouseButtons.Right Then
        'this was a right click for the context menu, we dont want to process it here
        Return False
    End If

    Dim ptClicked As New Point(e.X, e.Y)
    Dim dblX_ClickedScaleValue As Double
    Dim dblY_ClickedScaleValue As Double

    'this function passes in the mouse point and gets back the graph pane x and y scale value
    'In this case I only care about the Y value
    zgPlot.GraphPane.ReverseTransform(ptClicked, dblX_ClickedScaleValue, dblY_ClickedScaleValue)

    If Me.mcTrack.mbChangeThresholdIsValid = True Then
        'this plot has a threshold line, if it doesnt have a threshold line then there is nothing to do

        'find out if the mouse down event is close enough to the threshold line to consider the threshold line as draggable
        If Math.Abs(Me.mcTrack.mdYaxisThresholdPlotValue - dblY_ClickedScaleValue) < 1 Then
            'the mouse down event occured within 1 of the threshold line
            mbDraggingThresholdLine = True 'set flag to be used during the MouseMoveEvent to see if a drag is in progress
            mdOriginalThresholdValue = Me.mcTrack.mdYaxisThresholdPlotValue 'save the original Y value of the line
            Return True
        End If

    End If

    Return False

End Function

Private Function zgPlot_MouseMove(sender As ZedGraphControl, e As MouseEventArgs) As Boolean Handles zgPlot.MouseMoveEvent

    If mbDraggingThresholdLine = True Then
        'we are dragging the threshold line

        'get the latest values under the cursor
        Dim ptClicked As New Point(e.X, e.Y)
        Dim dblX_ClickedScaleValue As Double
        Dim dblY_ClickedScaleValue As Double
        zgPlot.GraphPane.ReverseTransform(ptClicked, dblX_ClickedScaleValue, dblY_ClickedScaleValue)
        mdNewThresholdValue = dblY_ClickedScaleValue
        mbThresholdHasChanged = True

        'do an update of the plot objects using the new cursor value
        'the following just moves the threshold on this one plot, without changing the underlying protocol defined value of the threshold and without changing all the other plots using it.
        Me.mcTrack.mdYaxisThresholdPlotValue = mdNewThresholdValue
        Me.doPlot()
        Return True
    End If

    Return False
End Function
于 2020-04-29T15:56:35.153 回答