3

ユーザーが垂直方向にドラッグできるようにする直線の水平線がいくつかあります。これはどのように可能でしょうか?線を選択するための最良のパラメータは、線の近くの固定ピクセル数だと思います。したがって、マウスが+/- 2ピクセルの場合は、マウスカーソルを変更して、線をドラッグ可能にする必要があります。CurveItemクラスにIsSelectableプロパティとIsSelectedプロパティがあることがわかります。これらはこの問題を解決するのに何か機能がありますか?クラスのドキュメントを読んでも、彼らが何のためにあるのか本当に理解できません。


編集:

FindNearestPoint(およびFindNearestObject)は実際のポイントのみを検索するようです。直線のセクション全体に沿って機能するように選択するにはどうすればよいですか?チェックしたいすべての行をループする独自のカスタム「検索」ルーチンを作成する必要があると思います。計算するたびに、マウスのX位置()に基づいて架空のYポイントを計算します。この目的のための傾斜した線、水平/垂直線の場合、それはわずかに単純になります。少なくともこれはcurveItemに必要なようですが、LineObj(の中央セクション)を選択するためにも同じことを行う必要があると思いますか?

私は実際にLineObjが存在することを知りませんでした。LineObjはReadOnlyであるため、 X2/Y2座標を変更できないようです。では、LineObjのX2 / Y2ポイントをドラッグすることは可能ですか?


編集2:

これは、JapaneseCandleStickグラフのFindNearestPointに問題があるようです。グラフペインをクリックしても、最も近いバーのインデックスは返されませが、x軸上でどれだけ離れていても、代わりに最も近いY値のインデックスが選択されると思います。マウスの右側にあるバーの場合もあれば、マウスの左側にあるバーの場合もあります。これはそれが機能することを意図した方法ですか?

このカスタム関数を自分で作成したので、大丈夫だと思います。それでも、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

マウスでポイントをドラッグする方法については、このチュートリアルをご覧ください。

LineObj曲線の代わりに使用している場合は、FindNearestObjectメソッドを確認してください。

また、クリックするための「感度の領域」を作成する場合、このメソッドは、ピクセル単位のマウス座標をペインスケール座標に変換するのに役立ちます。

主なアイデアは次のとおりです
。-サブスクライブMouseDownMouseUpおよびMouseMoveイベント
-イベントのハンドラーで、MouseDownクリックしたポイントが移動する曲線/グラフオブジェクトの近くにあるかどうかを確認します
-最初のリンクからの例に示されているのと同様の方法で変更を行います

編集編集
について:2つのポイントを含む
水平曲線があると仮定します。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イベントを処理して、カーブを移動するために必要な量を見つけます。曲線は水平であり、X軸に沿って移動したくない場合があるため、Y(またはY2)方向の変化のみを知る必要があります。

カーブを移動するために必要な量(dy)がわかったら、次のようにします。

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

2番目の質問に関して、ドキュメントにはLineObj.Location.Y2次のようなものがあります。

Y2の位置は、Yからの高さオフセットとして内部に保存されることに注意してください。

また、Width/Heightプロパティは簡単に設定できるので、この方法で行うことができます。

于 2010-10-01T13:42:53.633 に答える
1

最初にbretddogに答える:

これは、JapaneseCandleStickグラフのFindNearestPointに問題があるようです。グラフペインをクリックしても、最も近いバーのインデックスは返されませんが、x軸上でどれだけ離れていても、代わりに最も近いY値のインデックスが選択されると思います。マウスの右側にあるバーの場合もあれば、マウスの左側にあるバーの場合もあります。これはそれが機能することを意図した方法ですか?

このカスタム関数を自分で作成したので、大丈夫だと思います。それでも、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 = TrueThenline ;を削除します。
  • 非正規直交軸との不一致を防ぐために、計算の前にX、Y値を正規化しました。
  • エラーが発生した場合は、Falseを返すと、 NearestCurve=NothingおよびNearestCurveIndex=-1になります。
  • ドラッグするラインカーソルは、LineObjではなく、曲線(ポイント以上)である必要があります。
  • 間隔が近いかどうかのテストはコードの弱点であり、いくつかの間違いが発生するはずだと思います(前述のように、1つ-まれな-ケースをすでに特定しました)。垂直線が完全ではない場合(係数が非常に大きい場合)にも問題が発生する可能性があります。

最後に、このコードが速度に最適化されているかどうかはわかりませんが、私は自分の側でフリーズすることはありません。最良の方法は、関数をZedGraphクラスに統合し、Add関数が呼び出されたときに各係数(aおよびb)を計算して、毎回計算しないようにすることです(つまり、各マウスが移動します)。

したがって、コードが、ZedGraphには非常に欠けている移動可能なカーソルを作成するのに役立つことを願っています。

于 2011-05-04T15:22:31.097 に答える
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 に答える