Reputation: 35450
Given a transform matrix M1, original center of rotation P1 and new center of rotation P2, what is the correct way to compute the new transform matrix M2 so as to keep object's current position and rotation intact?
I'm working on a vector drawing application that allows user to rotate objects using Thumb
controls. Rotation works fine around the center of the object. I have set RenderTransformOrigin
to 0.5,0.5
, so simply setting RenderTransform
to <RotateTransform Angle="{Binding Rotation}" />
does the job for me.
Now I need to allow user to change the pivot point, or center of rotation. I added another Thumb
that allows users to place the pivot point anywhere they like. My idea was to simply bind this thumb's location to RenderTransformOrigin
or CenterX
and CenterY
properties of my RotateTransform
to start rotating around the new center.
The problem however is that as soon as user moves the pivot thumb to a new location, RotateTransform
computes the new transform matrix and moves the object accordingly, which is counter-productive, since the pivot thumb should only act as the center of rotation during the rotate operation.
After having spent the better part of the past 2 weeks with this, I have realized that I need to compute the new transform matrix for my object the keeps the current position and rotation intact while moving the center of rotation. Is there a built-in or custom way to do that?
Just to clarify it further, here is how it should go:
RotateTransform
's CenterX and CenterY are bound to pivot's position.RotateTransform
to Pivot's position, but at whatever point I assign the new center to it, the RotateTransform will compute the new position and move object accordingly.Upvotes: 1
Views: 733
Reputation: 35450
Answering @KamilNowak's request in the comments above, a long time has passed since then, but I just managed to dig this out from my code repository. Hope this helps you find your way forward. You basically need to call this function AFTER you change the location of your pivot point (is in vb.net but u should be able translate):
'_Designer is the main Canvas control on which all drawing objects are placed
Friend Sub AdjustLocationAfterPivotShift(_Designer As Canvas)
'RectangularObject is the underlying VM object that represents my shape
Dim RO = DirectCast(Me.DataContext, RectangularObject)
'My control template has Thumb elements for translation, rotation, scaling and pivot. Here I'm using rotate and pivot thumbs. Use your controls instead.
Dim DIRotateThumb As RotateThumb = VisualTreeExtensions.GetVisualDescendent(Of RotateThumb)(Me)
Dim _RotateHandle = DirectCast(DIRotateThumb.Template.FindName("PART_RotateEllipse", DIRotateThumb), Shapes.Ellipse)
Dim RotateHandlePos As Point = _RotateHandle.TranslatePoint(New Point(5, 5), _Designer)
Dim RotateCenterPos As Point = Me.TranslatePoint(New Point(RO.PivotX + RO.Size.Width / 2, RO.PivotY + RO.Size.Height / 2), _Designer)
'ROTATION_HANDLE_OFFSET is vertical distance between control and rotation thumb
Dim RotationThumbOffsetFromPivot As New Point(-RO.PivotX, -RO.PivotY - Me.ActualHeight / 2 - ROTATION_HANDLE_OFFSET)
RO.Rotation = Me.ComputeAngle(RotateCenterPos, RotateHandlePos, RotationThumbOffsetFromPivot)
Dim NewLocation As Point = ComputeLocationDelta(New Point(RO.PivotX, RO.PivotY), RO.LastPivot.ToPoint(), RO.Rotation)
RO.Location.X += NewLocation.X
RO.Location.Y += NewLocation.Y
'Set LastPivot to current pivot location after adjusting location
RO.LastPivot.X = RO.PivotX
RO.LastPivot.Y = RO.PivotY
End Sub
''' <summary>
''' Computes X and Y delta that must be added to object's current location to
account for pivot's changed location.
''' </summary>
''' <param name="currentPivot"></param>
''' <param name="originalPivot"></param>
''' <param name="angle"></param>
''' <returns></returns>
Private Function ComputeLocationDelta(currentPivot As Point, originalPivot As
Point, angle As Double) As Point
Dim h = currentPivot.X - originalPivot.X
Dim v = currentPivot.Y - originalPivot.Y
Dim trans As System.Windows.Vector = RotateVector2d(h, v, D2R(angle))
Return New Point(trans.X - h, trans.Y - v)
End Function
Friend Function ComputeAngle(center As Point, pos As Point, offset As Point) As Double
Dim xDiff = pos.X - center.X
Dim yDiff = pos.Y - center.Y
Dim offsetAngle = Math.Atan2(offset.Y, offset.X) * 180 / Math.PI
Return Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI - offsetAngle
End Function
Friend Shared Function RotateVector2d(x0 As Double, y0 As Double, rad As Double) As Vector
Dim result As New Vector With {
.X = x0 * Math.Cos(rad) - y0 * Math.Sin(rad),
.Y = x0 * Math.Sin(rad) + y0 * Math.Cos(rad)
}
Return result
End Function
Friend Shared Function D2R(degree As Double) As Double
Return (degree Mod 360) * Math.PI / 180
End Function
Hope this is not as clear as mud and you can use it to your benefit.
Upvotes: 2