Tuesday, 30th October 2012
Crude matrix multiplier
For part of my Pygame 3D graphics program I wanted to work out the matrix required to rotate an object around a point other than the origin. To rotate an object around an arbitrary point (cx, cy, cz) you first translate the object along the vector (-cx, -cy, -cz), so that the (cx, cy, cz) is now at the origin; then use the standard rotation matrix to rotate the object around the origin; then translate the object back along the vector (cx, cy, cz).
Naturally I wanted to work out what the final matrix was, but after several attempts to do it by hand, I wasn't convinced that I hadn't made a mistake. So I decided to write a program to calculate matrix multiplications for me. Of course, it's easy enough to multiply matrices of numbers with numpy, I couldn't think of a way that kept variables as variables.
The first program I made just went through, converting the values to strings then multiplying and adding the relevant bits. This gave a very long, messy answer. The last entry in the first row of the matrix, for example, was:
(1.(c.-cx + -s.-cy + 0.-cz + 0.1) + 0.(s.-cx + c.-cy + 0.-cz + 0.1) + 0.(0.-cx + 0.-cy + 1.-cz + 0.1) + cx.(0.-cx + 0.-cy + 0.-cz + 1.1))
Note that c is short for cos(theta), s for sin(theta) and dots indicate multiplication, so 1.0 is 1 times 0, not a decimal. I added some code to cancel anything multiplied by 0 and make any variable multiplied by 1 equal itself. This simplified the answer significantly:
((c.-cx + -s.-cy) + cx.(1))
Then I changed the code to only add parentheses where necessary:
c.-cx + -s.-cy + cx
Finally I added some code to keep track of minus signs so I could cancel them where possible and otherwise move minus signs to the front, giving a final answer for the whole matrix:
c -s 0 -c.cx + s.cy + cx s c 0 -s.cx - c.cy + cy 0 0 1 -cz + cz 0 0 0 1