前回からの続きで、今回ので満足できるところまでできた
変形後の要素の4頂点をグリッド頂点にスナップ
できたなあ
一年前に失敗したのと同じ考え方なんだけど、書き方を変えてみたら期待どおりの動きにできた
今回の動画もマウスの調子が良くないから何回も撮り直した
一年前と同じ考え方
この左上頂点の場合は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は登録しようとしてもできない(エラーになる)ので、もし別の場所で同じ距離の頂点データが出たときには、その頂点は無視して次のデータへ行くようにする処理にしたのが赤背景のところ
おまけで
変形後の要素の4辺or4頂点をグリッドスナップ
っていうのもできた
ここから下に移動させると
左下頂点がグリッド頂点にスナップ!
ここから下に移動させると
右上頂点がグリッドラインにスナップ
次は
青枠下辺がグリッドラインにスナップ
こんなふうに頂点や枠を近くのグリッドラインや頂点にスナップする
動きが細かすぎて使うかどうかはわからないけどこういうのもできた
この処理は前回の記事のコードの延長みたいなもので難しくないはずなんだけど今見たらよくわかんないや
暑いと頭動かない
只今(午後9時43分)の室温33.3、湿度54
でも昨日、一昨日に比べたら少しマシだなあ
今回のコード全部は今回のでグリッドスナップの動きは全部できたかなあ、細かい不満はあるけど1年前と違って実用できる動きにできた
20170708_4頂点グリッドスナップ - Visual Studio Team Services
前回の記事
WPF、変形後の要素の4辺をグリッドスナップしながらドラッグ移動 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15011638.html
2016/05/14
一年前の記事、このときはいまいちな結果だった
WPFとVB.NET、回転したコントロールをマウスドラッグでグリッドスナップ、SortedListはスゴイヤツ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/14136957.html