I have a javascript Matrix class for affine transforms, and a function to set the rotation to an absolute number of radians, around a given center:
this.setRotation = function (radians, center) {
var cos = Math.cos(radians);
var sin = Math.sin(radians);
this.a = cos;
this.b = sin;
this.c = -sin;
this.d = cos;
this.tx += center.x - center.x * cos + center.y * sin;
this.ty += center.y - center.x * sin - center.y * cos;
}
I'm trying to rotate around the center of the object itself, so I'm passing in a center point of half the object's width, and half its height, so for a 100 x 100 object I'm passing in 50, 50.
If my object starts from a rotation of zero, this works fine:
... but if I rotate the shape again, or start with a rotation of other than zero, the tx and ty values end up wrong:
What's wrong with my formula above? Setting the rotation seems to be accurate, but not the tx and ty.
I have seen a few other questions on this subject, in particular this one, but nothing that has helped.
Update To add some numbers to this:
If I begin with a 100x100 rectangle, positioned at 100,100, then my initial matrix is: {Matrix: [a:1, b:0, c:0, d:1, tx:100, ty:100]}
To rotate this clockwise 45 degrees, I feed the above function 0.7853981633974483 (45 degrees in radians), and the center: {Point: [x:50, y: 50]}
This produces the following matrix:
{Matrix: [a:0.7071067812, b:0.7071067812, c:-0.7071067812, d:0.7071067812, tx:150, ty:79.28932188]}
which is exactly right.
But if I then start with that matrix, and try to return it to zero, by feeding the function arguments of 0 and {Point: [x:70.71067812000001, y: 70.71067812000001]}
(the center of the new, rotated shape), then the output is {Matrix: [a:1, b:0, c:0, d:1, tx:150, ty:79.28932188]}
, which is the correct rotation but not the correct translation. I'm expecting to get {Matrix: [a:1, b:0, c:0, d:1, tx:100, ty:100]}
I've tried several other variants of the function, including using the center of the rectangle in the parent coordinate space 150,150 instead of the local center of 50,50, and replacing += with = as suggested below, but nothing seems to help. If I comment out the tx calculation then my shape rotates beautifully around it's origin, so the issue must be either with the tx/ty calculation, or with the center point that I'm passing in.
Does anyone have any further ideas?
My problem was to do with getting the object back to the origin - removing the top left coordinates as well as half the height and width before rotating, then adding them back again.
I found it easier to use a rotate function than a setRotate function, and ended up with the following code:
// Matrix class functions:
this.rotate = function (radians) {
var cos = parseFloat(Math.cos(radians).toFixed(10));
var sin = parseFloat(Math.sin(radians).toFixed(10));
a = this.a,
b = this.b,
c = this.c,
d = this.d,
tx = this.tx,
ty = this.ty;
this.a = a * cos - b * sin;
this.b = a * sin + b * cos;
this.c = c * cos - d * sin;
this.d = c * sin + d * cos;
this.tx = tx * cos - ty * sin;
this.ty = tx * sin + ty * cos;
}
this.setRotation = function (radians) {
var rotation = this.rotation();
this.rotate(radians - rotation);
}
this.translate = function (tx, ty) {
this.tx += tx;
this.ty += ty;
}
// Called from outside the class
var transformedBounds = pe.Model.ReadTransformedBounds(selection[0]);
var itemTransform = pe.Model.ReadItemTransform(selection[0]);
// Cache the coordinates before translating them away:
var ox = transformedBounds.Left + (transformedBounds.Width / 2);
var oy = transformedBounds.Top + (transformedBounds.Height / 2);
itemTransform.translate(-ox, -oy);
// Rotate:
itemTransform.setRotation(radians);
// Restore the translation:
itemTransform.translate(ox, oy);
All of which is probably pretty obvious if you can actually do sums, but I post it here in case anyone has a day as dim as mine...