午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPF、変形後の要素(Thumb)の頂点をマウスドラッグで最寄りのグリッド頂点にスナップ

 
前回からの続きで、今回ので満足できるところまでできた
 
変形後の要素の4頂点をグリッド頂点にスナップ

動作の様子
変形(20度回転)後の要素の四隅の頂点を順番にを100,100の位置にスナップ移動させている
できたなあ
一年前に失敗したのと同じ考え方なんだけど、書き方を変えてみたら期待どおりの動きにできた
今回の動画もマウスの調子が良くないから何回も撮り直した
 
今回のアプリのダウンロード先
 
一年前と同じ考え方
この左上頂点の場合はBが一番近い、これを左上頂点の目標グリッドとして
これを各4頂点調べるから
4*4=16通りの中で一番近いところへ移動させる
 
 
 
 
各4頂点の目標グリッドなどの情報を入れておくStructure(構造体)を
 
Public Structure MyTargetGridPointData
    Dim TargetGridPoint As Point '目標グリッド位置
    Dim Distance As Double '距離
    Dim IsValid As Boolean '
    Dim DiffPoint As Point '元の位置からの差分
End Structure
 
こんなふうに作っておいて
あとは
   '指定位置から一番近いグリッド位置を返す
    Private Function GetNearGridPointDistance(xy As Double, gridSize As Integer) As Double
        Dim m As Double = xy Mod gridSize
        If m > gridSize / 2 Then
            Return xy + gridSize - m
        Else
            Return xy - m
        End If
    End Function
   '指定座標から一番近いグリッド頂点の座標を返す
    Private Function GetNearestGridPoint(dp As Point, gridSize As Integer) As Point
        Dim x As Double = GetNearGridPointDistance(dp.X, gridSize) '最寄りのグリッドx座標
        Dim y As Double = GetNearGridPointDistance(dp.Y, gridSize) 'y座標
        Return New Point(x, y)
    End Function
   '2点間の距離を返す
    Private Function GetDistance(p1 As Point, p2 As Point) As Double
        Dim x As Double = p1.X - p2.X
        Dim y As Double = p1.Y - p2.Y
        Dim rd As Double = Math.Sqrt(x ^ 2 + y ^ 2)
        Return rd
    End Function
   ''' <summary>
   ''' 移動後の座標から一番近いグリッド頂点や距離、移動前からの距離などを返す
   ''' </summary>
   ''' <param name="gridSize">グリッドサイズ</param>
   ''' <param name="transformedPoint">移動前の座標</param>
   ''' <param name="mMove">マウスの移動座標</param>
   ''' <returns></returns>
    Private Function GetPointData(gridSize As Integer, transformedPoint As Point, mMove As Point) As MyTargetGridPointData
        Dim tPoint As New MyTargetGridPointData
        With tPoint
           '移動後地点の最寄りのグリッド頂点
            .TargetGridPoint = GetNearestGridPoint(transformedPoint + mMove, gridSize)
           'その距離
            .Distance = GetDistance(transformedPoint + mMove, .TargetGridPoint)
           '今回は未使用、値が有効かどうか
            .IsValid = True
           '移動前の地点から最寄りのグリッド頂点までの差分
            .DiffPoint = transformedPoint - .TargetGridPoint

        End With
        Return tPoint
    End Function
   ''' <summary>
   ''' 元の4頂点グリッドスナップ用、4頂点の最寄りのグリッド頂点を取得、一番近いグリッド頂点情報を返す
   ''' </summary>
   ''' <param name="gridSize">グリッドサイズ</param>
   ''' <param name="mMove">マウスの移動距離</param>
   ''' <param name="TopLeftXY">変形後の左上の位置</param>
   ''' <param name="TopRightXY">変形後の右上の位置</param>
   ''' <param name="BottomRightXY">変形後の右下の位置</param>
   ''' <param name="BottomLeftXY">変形後の左下の位置</param>
   ''' <returns></returns>
    Private Function GetMyTargetGrid4Point2(gridSize As Integer, mMove As Point,
                          TopLeftXY As Point, TopRightXY As Point, BottomRightXY As Point, BottomLeftXY As Point) As MyTargetGridPointData
       '移動後の各4頂点の最寄りのグリッド頂点のData取得
        Dim TLData As MyTargetGridPointData = GetPointData(gridSize, TopLeftXY, mMove)
        Dim TRData As MyTargetGridPointData = GetPointData(gridSize, TopRightXY, mMove)
        Dim BRData As MyTargetGridPointData = GetPointData(gridSize, BottomRightXY, mMove)
        Dim BLData As MyTargetGridPointData = GetPointData(gridSize, BottomLeftXY, mMove)
       '一番近いグリッド頂点を取得する
       '4データを一旦リストに入れる
        Dim dataList As New List(Of MyTargetGridPointData) From {TLData, TRData, BRData, BLData}

       'SortedListに入れて並べ替える、距離をkeyにして昇順
        Dim sl As New SortedList(Of Double, MyTargetGridPointData)
       '同じ距離があった場合にリストに追加しようとするとエラーになるのでtryで無視して次のDataを入れていく
        For i As Integer = 0 To 3
            Try
                sl.Add(dataList(i).Distance, dataList(i))
            Catch ex As Exception

            End Try
        Next
       'Dim rv As MyTargetGridPointData = sl.Values(0)
       '一番近いDataを返す
        Return sl.Values(0)
        

    End Function
 
これをDragDeltaイベントのところで
 
Dim GridSize As Integer = sldGrid.Value'グリッドサイズ
Dim hChange As Double = e.HorizontalChange'マウス横移動距離
Dim vChange As Double = e.VerticalChange'マウス縦移動距離
'変形後の元の4頂点をグリッドの頂点にスナップ
'4頂点それぞれの最寄りのグリッド頂点の中から一番近いグリッド頂点やその距離の差分などを取得
Dim pData As MyTargetGridPointData = GetMyTargetGrid4Point2(GridSize, New Point(hChange, vChange), MyExThumb.MyTransformedTopLeft, MyExThumb.MyTransformedTopRight, MyExThumb.MyTransformedBottomRight, MyExThumb.MyTransformedBottomLeft)
'今と違う位置(距離の差分が0以外)ならその場所へ移動
If pData.IsValid Then
    If pData.DiffPoint.X <> 0 Then MyExThumb.MyLeft -= pData.DiffPoint.X
    If pData.DiffPoint.Y <> 0 Then MyExThumb.MyTop -= pData.DiffPoint.Y
End If
MyExThumbはThumbを継承して作ったクラスで
MyExThumb.MyLeftプロパティは値変更のところでCanvas.SetLeftを実行している
 
 
こんなふうに書いたらうまく行ったけど、なんかよくわかっていないんだよねえ、暑くて考えることができない
 
SortedListにデータ追加するところを書き換えた
一番近い距離のものを取得するのにSortedListクラスを使うのも一年前と同じなんだけど、少し書き直した
SortedListはkeyとValueを対でデータを追加していく、そうするとkeyの値の順番で自動で並び替えて登録される、なのでkeyに距離を指定すれば距離順に並ぶので簡単に一番近いものが取得できるけど、同じ値のkeyは登録しようとしてもできない(エラーになる)ので、もし別の場所で同じ距離の頂点データが出たときには、その頂点は無視して次のデータへ行くようにする処理にしたのが赤背景のところ62~78行目
 
 
おまけで
変形後の要素の4辺or4頂点をグリッドスナップ
っていうのもできた
 
イメージ 2
変形後の要素がぴったり収まる枠(青枠)の上辺と左辺がグリッドスナップしている状態
ここから下に移動させると
 
イメージ 3
左下頂点がグリッド頂点にスナップ!
ここから下に移動させると
 
イメージ 4
右上頂点がグリッドラインにスナップ
次は
 
イメージ 5
青枠下辺がグリッドラインにスナップ
こんなふうに頂点や枠を近くのグリッドラインや頂点にスナップする
動きが細かすぎて使うかどうかはわからないけどこういうのもできた
この処理は前回の記事のコードの延長みたいなもので難しくないはずなんだけど今見たらよくわかんないや
暑いと頭動かない
只今(午後9時43分)の室温33.3、湿度54
でも昨日、一昨日に比べたら少しマシだなあ
 
 
 
今回のコード全部は
20170708_4頂点グリッドスナップ - Visual Studio Team Services
今回のでグリッドスナップの動きは全部できたかなあ、細かい不満はあるけど1年前と違って実用できる動きにできた
 
 
 
 
 
前回の記事
WPF、変形後の要素の4辺をグリッドスナップしながらドラッグ移動 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15011638.html
 
 
2016/05/14
一年前の記事、このときはいまいちな結果だった
WPFVB.NET、回転したコントロールをマウスドラッグでグリッドスナップ、SortedListはスゴイヤツ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14136957.html