回転後のコン
トロールをマウスドラッグ移動するときに
グリッド移動(グリッドスナップ)
グリッドスナップする頂点は四角形の各4頂点すべての中で一番グリッドに近いもの
この条件での処理がなんとかできた感じ
回転していない四角形の移動は今のPixtack紫陽花2ndや無印とほぼ同じ
今回作ったのは回転させて斜めになっている四角形の動き
少し怪しいwけどだいたい期待通り
見た目だけが変化するので内部と見た目で差(ズレ)ができる
実際に座標を指定するときには内部のものを使うので
見た目と合わせるにはその差を取得する必要がある
最寄りのグリッド座標を取得する
左上に注目した場合に一番近いのはBなので
実際には左上、左下、右上、右下の4頂点の中から探すから
16通りの中で一番近いところになる
Imports System.Windows.Controls.Primitives
Class MainWindow
Private Const grid As Integer = 70
Private Sub DrawGridLine()
Dim pFigure As PathFigure
Dim pGeometry As New PathGeometry
For i As Integer = 0 To 50
pFigure = New PathFigure
pFigure.StartPoint = New Point(i * grid, 0)
pFigure.Segments.Add(New LineSegment(New Point(i * grid, 350), True))
pGeometry.Figures.Add(pFigure)
Next
For i As Integer = 0 To 35
pFigure = New PathFigure
pFigure.StartPoint = New Point(0, i * grid)
pFigure.Segments.Add(New LineSegment(New Point(500, i * grid), True))
pGeometry.Figures.Add(pFigure)
Next
Dim mPath As New Path With {.Stroke = Brushes.Blue, .StrokeThickness = 1, .Data = pGeometry}
canvas1.Children.Add(mPath)
Panel.SetZIndex(mPath, -1)
End Sub
Private Sub thumb1_DragDelta(sender As Object, e As DragDeltaEventArgs) Handles thumb1.DragDelta
Dim p As Point = GetOnCanvas(sender)
Dim x As Double = p.X + e.HorizontalChange
Dim y As Double = p.Y + e.VerticalChange
x = x - (x Mod grid)
y = y - (y Mod grid)
Dim t As ExThumb = DirectCast(sender, ExThumb)
SetOnCanvas(t, x, y)
End Sub
Private Overloads Sub SetOnCanvas(t As ExThumb, x As Double, y As Double)
Canvas.SetLeft(t, x)
Canvas.SetTop(t, y)
t.Locate = New Point(x, y)
End Sub
Private Overloads Sub SetOnCanvas(t As ExThumb, p As Point)
Call SetOnCanvas(t, p.X, p.Y)
End Sub
Private Function GetOnCanvas(obj As Object) As Point
Return New Point(Canvas.GetLeft(obj), Canvas.GetTop(obj))
End Function
Private Sub SetDiffPoint(t As ExThumb)
Dim gt As GeneralTransform = t.TransformToVisual(canvas1)
Dim BaseP As Point = New Point(Canvas.GetLeft(t), Canvas.GetTop(t))
With t
.Locate = BaseP
.DiffLTop = gt.Transform(New Point(0, 0)) - BaseP
.DiffRTop = gt.Transform(New Point(t.Width, 0)) - BaseP
.DiffRDown = gt.Transform(New Point(t.Width, t.Height)) - BaseP
.DiffLDown = gt.Transform(New Point(0, t.Height)) - BaseP
End With
End Sub
Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
Call DrawGridLine()
End Sub
Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Call SetDiffPoint(thumb2)
End Sub
Private Sub btLTop_Click(sender As Object, e As RoutedEventArgs) Handles btLTop.Click
Call SetFitPoint(thumb2, thumb2.DiffLTop)
End Sub
Private Sub btRTop_Click(sender As Object, e As RoutedEventArgs) Handles btRTop.Click
Call SetFitPoint(thumb2, thumb2.DiffRTop)
End Sub
Private Sub SetFitPoint(t As ExThumb, fit As Point)
Dim fp As Point = t.Locate + fit
Dim x As Double = GetNearGridPoint(fp.X)
Dim y As Double = GetNearGridPoint(fp.Y)
Dim nx As Double = x - fp.X
nx = t.Locate.X + nx
Dim ny As Double = y - fp.Y
ny = t.Locate.Y + ny
Call SetOnCanvas(t, nx, ny)
End Sub
Private Function GetNearGridPoint(xy As Double) As Double
Dim m As Double = xy Mod grid
If m > grid / 2 Then
Return xy + grid - m
Else
Return xy - m
End If
End Function
Private Sub thumb2_DragDelta(sender As Object, e As DragDeltaEventArgs) Handles thumb2.DragDelta
Dim t As ExThumb = DirectCast(sender, ExThumb)
Dim mp As New Point(e.HorizontalChange, e.VerticalChange)
Dim np As Point = GetNearPoint(t, mp)
np = np + t.Locate + mp
Call SetOnCanvas(t, np)
End Sub
Private Function GetNearPoint(t As ExThumb, addPoint As Point) As Point
Dim bp As Point = t.Locate + addPoint
Dim LT As Point = t.DiffLTop + bp
Dim RT As Point = t.DiffRTop + bp
Dim RD As Point = t.DiffRDown + bp
Dim LD As Point = t.DiffLDown + bp
Dim ltg As Point = GetXYDistance(LT)
Dim rtg As Point = GetXYDistance(RT)
Dim rdg As Point = GetXYDistance(RD)
Dim ldg As Point = GetXYDistance(LD)
Dim ltd As Double = GetDistance(LT, ltg)
Dim rtd As Double = GetDistance(RT, rtg)
Dim rdd As Double = GetDistance(RD, rdg)
Dim ldd As Double = GetDistance(LD, ldg)
LT = ltg - LT
RT = rtg - RT
RD = rdg - RD
LD = ldg - LD
Dim sl As New SortedList(Of Double, Point)
sl.Add(ltd, LT)
sl.Add(rtd, RT)
sl.Add(rdd, RD)
sl.Add(ldd, LD)
Dim neko As Point = sl.Values(0)
Return sl.Values(0)
End Function
Private Function GetXYDistance(dp As Point) As Point
Dim x As Double = GetNearGridPoint(dp.X)
Dim y As Double = GetNearGridPoint(dp.Y)
Return New Point(x, y)
End Function
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
Private Sub bt2_Click(sender As Object, e As RoutedEventArgs) Handles bt2.Click
Dim neko As ExThumb = thumb2
Dim p As Point = GetNearPoint(thumb2, New Point(0, 0))
End Sub
Private Sub btAutoFit_Click(sender As Object, e As RoutedEventArgs) Handles btAutoFit.Click
Dim p As Point = GetNearPoint(thumb2, New Point(0, 0))
Dim np As Point = p + thumb2.Locate
Call SetOnCanvas(thumb2, np)
thumb2.Locate = np
End Sub
End Class
Public Class ExThumb
Inherits Thumb
Public Property Locate As Point
Public Property DiffLTop As Point
Public Property DiffRTop As Point
Public Property DiffRDown As Point
Public Property DiffLDown As Point
End Class
グリッドの大きさ指定
ExThumbクラス
マウスドラッグ移動に適したコン
トロールのThumbを継承したクラスを作成
名前はExThumbにした
このクラスには値をもたせているだけ
自身の座標のほかは四隅の座標までとの差
例えば自身の座標が(1, 2)の時に左上が(10, 20)だったら
DiffLTopは(10 - 1, 20 - 2) なので (9, 18)を入れておくことになる
この差が変化するのは回転させた時だけで、今回は回転角度は固定なので
アプリの起動直後に値を入れるだけになる
Locateだけは移動した時に書き換える
さっきのプロパティに値を入れる
SetDiffPoint
TransformToVisualで得られるGeneralTransformのTransformメソッドで元の位置から回転後の座標を取得できる
これをアプリの起動直後に実行するので
Loadedイベントで
2点間の距離を返す
GetDistance
中学校で習ったらしいけど全く憶えていなかったのでググってそのまま
指定した位置から一番近いグリッドの位置を返す
GetNearGridPoint
指定した座標から一番近いグリッドの座標を返す
GetXYDistance
さっきのGetNearGridPointを使っているけど
ひとつにまとめたほうが良かったかも、よくわからん
↑の3つのGet~を使って
どれくらい移動させればぴったりになるかを返す
GetNearPoint
引数は移動させるExThumbとマウスで移動した分の距離のPoint
16通りの中から一番近いところと場所を探す
四隅の頂点それぞれの一番近いグリッドとの距離を取得し終わっているのが
148行目
そこからその4つの中でさらに一番近いものとその場所を取得するために
SortedListっていう
ジェネリックコレクションってのを初めて使ってみた
他のコレクションと同じように値(
Value)を入れるんだけど、値と一緒にペアとなる
Keyも入れられてさらにKey順に自動で並べ替えてくれるスゴイヤツ
SortedListにKeyと
Valueを入れたところ
この自動並べ替え機能を利用して最小値となる座標を取得している
Keyに距離を入れて、
Valueに座標を入れていけばKey順に並べてくれるので
先頭の
Valueを取り出せば、それが最短距離の座標になる
これが164行目
この最短距離の座標はグリッドまでの差なので、今の座標に足せばピッタリになる
あとはThumbのDragDeltaのイベントの時に呼び出すだけ
2017/07/10追記ここから
SortedListへの追加方法を↓のように修正
SortedListのkeyは同じ値を指定できないけど今回は
距離をkeyしているからもし同じ距離があった場合にエラーになってしまう
同じ距離のものはリストに追加しないで次の値を入れていきたいので
Try構文を使うようにした
今回のケースでは同じ距離になることはないみたいで一回もエラーにならなかったけどグリッドサイズや要素のサイズを変えた場合は同じ距離になることがあるので修正した
Dim dList As New List(Of Double) From {ltd, rtd, rdd, ldd}
Dim pList As New List(Of Point) From {LT, RT, RD, LD}
Dim sl As New SortedList(Of Double, Point)
For i As Integer = 0 To 3
Try
sl.Add(dList(i), pList(i))
Catch ex As Exception
End Try
Next
Return sl.Values(0)
2017/07/10追記ここまで
ThumbのDragDeltaイベント
120行目でさっきのGetNearPointで差を取得
それに今の座標とマウスの移動分を足してピッタリの座標が121行目
122行目で
Canvasに配置するSetOnCanvasで移動完了
SetOnCanvas
ここまで作ったけどPixtack紫陽花2ndに使うかどうかまだわからない
今はグループ化に挑戦しているけどかなり難しくてもう10日くらい経ったのかな
いろいろ試しているうちにできあがったのが今回のもの
グループ化はできたとしても基礎からの大幅書き直しになるからすんごい時間かかりそう
無印の時と違ってテストを重ねて慎重になっていたはずなんだけど
やっぱりこうなったかあって感じ
グループ化もグリッド移動もエクセルのものを目指して作っているんだけど
今回改めてエクセルのグリッド移動の動きを見ていたら思っていたのとは違って
思っていたのは今回作った動きなんだけど、エクセルの方は回転後の図形を
グリッド移動(Altキー押しながらの移動)しても四隅の頂点とセルのグリッドには合わせて移動していない
エクセルで
回転した四角形を横方向にグリッド移動(Altキー押しながらの移動)した時
実際の動きは四隅の頂点とグリッドの頂点は合わないみたいで
4辺のどれかに合わせる感じなんだねえ
この動きもいつか作ってみたい
↓
2017/07/07は2ヶ月後、できた!
ありがとうございます
5年後
コード全部