Optimal width and height after image rotation

A while ago, a blog reader Fabien sent me some code (you can read it here. Thanks Fabien!). The PHP code is a modification of my image rotation code with some upgrades.

I was looking through his code (French variable names!) and was puzzled by the initial section. I believe he based his code on my code where the resulting image wasn’t clipped after rotation, meaning the whole image was still in the picture/bitmap (though rotated).

In that piece of code, I just used the diagonal length of the image (from top-left corner to bottom-right corner) as the final length and breadth of the resulting image. This gave the simplest resulting image dimension without doing complicated maths calculations (a square in this case).

However, what if you want to know the optimal width and height of the resulting image after rotation? Meaning the best-fit width and height that just manages to contain the resulting rotated image. For that, I need to tell you some basic trigonometry and geometry.

Image rotation, optimal width and height

Suppose you have a rectangle with L as the length and H as the height. It is rotated t angles. I’m not going to explain the maths behind it. It involves complementary angles, supplementary angles, rotation symmetry and trigonometry with sines and cosines. Convince yourself that the diagram is true.

So after rotating t angles, the optimal width is L * cos(t) + H * cos(90 – t)

The optimal height is L * sin(t) + H * sin(90 – t)

Short digression: You might notice that any lengths that lie parallel to the x-axis usually involve cosines, and lengths that lie parallel to the y-axis usually involve sines. It’s just the way trigonometry works.

Now, although the image rotation is carried out with respect to the image’s centre, rotating by the top-left corner will result in the same optimal width and height. Again, this is basic maths so you’ll just have to convince yourself it’s true (and that I don’t really want to explain it…).

But that’s if t is an acute angle. What about other angles?

Image rotation, optimal width and height

For those angles, we just need to calculate the acute angle based on the initial rotation angle. After that, just substitute that calculated acute angle into our formula above. I have absolute confidence in your ability to check which quadrant in the Cartesian coordinate system does your rotation angle lie in.

UPDATE: In case you are unable to view images, if your rotation angle is in the 2nd quadrant, the calculated angle is (180 – t). If in the 3rd quadrant, it’s (t – 180). And if in the 4th quadrant, it’s (360 – t).

In practice, you might still want to pad a couple of pixels around. But that should give you the smallest image dimension which can still contain your rotated image.

Image rotation with bilinear interpolation and no clipping

I wrote an article on image rotation with bilinear interpolation to calculate the pixel information. Commenter Jahn wanted to know if the resulting image could be obtained without clipping. I replied with the required code changes (it was fairly minimal), then I thought it might be useful to you too.

The code was cleaned up a bit, and here it is:

const int cnSizeBuffer = 20;
// 30 deg = PI/6 rad
// rotating clockwise, so it's negative relative to Cartesian quadrants
const double cnAngle = -0.52359877559829887307710723054658;
// use whatever image you fancy
Bitmap bm = new Bitmap("slantedtreetopsky.jpg");
// general iterators
int i, j;
// calculated indices in Cartesian coordinates
int x, y;
double fDistance, fPolarAngle;
// for use in neighbouring indices in Cartesian coordinates
int iFloorX, iCeilingX, iFloorY, iCeilingY;
// calculated indices in Cartesian coordinates with trailing decimals
double fTrueX, fTrueY;
// for interpolation
double fDeltaX, fDeltaY;
// pixel colours
Color clrTopLeft, clrTopRight, clrBottomLeft, clrBottomRight;
// interpolated "top" pixels
double fTopRed, fTopGreen, fTopBlue;
// interpolated "bottom" pixels
double fBottomRed, fBottomGreen, fBottomBlue;
// final interpolated colour components
int iRed, iGreen, iBlue;
int iCentreX, iCentreY;
int iDestCentre;
int iWidth, iHeight;
int iDiagonal;
iWidth = bm.Width;
iHeight = bm.Height;
iDiagonal = (int)(Math.Ceiling(Math.Sqrt((double)(bm.Width * bm.Width + bm.Height * bm.Height)))) + cnSizeBuffer;

iCentreX = iWidth / 2;
iCentreY = iHeight / 2;
iDestCentre = iDiagonal / 2;

Bitmap bmBilinearInterpolation = new Bitmap(iDiagonal, iDiagonal);

for (i = 0; i < iDiagonal; ++i)
{
	for (j = 0; j < iDiagonal; ++j)
	{
		bmBilinearInterpolation.SetPixel(j, i, Color.Black);
	}
}

// assigning pixels of destination image from source image
// with bilinear interpolation
for (i = 0; i < iDiagonal; ++i)
{
	for (j = 0; j < iDiagonal; ++j)
	{
		// convert raster to Cartesian
		x = j - iDestCentre;
		y = iDestCentre - i;

		// convert Cartesian to polar
		fDistance = Math.Sqrt(x * x + y * y);
		fPolarAngle = 0.0;
		if (x == 0)
		{
			if (y == 0)
			{
				// centre of image, no rotation needed
				bmBilinearInterpolation.SetPixel(j, i, bm.GetPixel(iCentreX, iCentreY));
				continue;
			}
			else if (y < 0)
			{
				fPolarAngle = 1.5 * Math.PI;
			}
			else
			{
				fPolarAngle = 0.5 * Math.PI;
			}
		}
		else
		{
			fPolarAngle = Math.Atan2((double)y, (double)x);
		}

		// the crucial rotation part
		// "reverse" rotate, so minus instead of plus
		fPolarAngle -= cnAngle;

		// convert polar to Cartesian
		fTrueX = fDistance * Math.Cos(fPolarAngle);
		fTrueY = fDistance * Math.Sin(fPolarAngle);

		// convert Cartesian to raster
		fTrueX = fTrueX + (double)iCentreX;
		fTrueY = (double)iCentreY - fTrueY;

		iFloorX = (int)(Math.Floor(fTrueX));
		iFloorY = (int)(Math.Floor(fTrueY));
		iCeilingX = (int)(Math.Ceiling(fTrueX));
		iCeilingY = (int)(Math.Ceiling(fTrueY));

		// check bounds
		if (iFloorX < 0 || iCeilingX < 0 || iFloorX >= iWidth || iCeilingX >= iWidth || iFloorY < 0 || iCeilingY < 0 || iFloorY >= iHeight || iCeilingY >= iHeight) continue;

		fDeltaX = fTrueX - (double)iFloorX;
		fDeltaY = fTrueY - (double)iFloorY;

		clrTopLeft = bm.GetPixel(iFloorX, iFloorY);
		clrTopRight = bm.GetPixel(iCeilingX, iFloorY);
		clrBottomLeft = bm.GetPixel(iFloorX, iCeilingY);
		clrBottomRight = bm.GetPixel(iCeilingX, iCeilingY);

		// linearly interpolate horizontally between top neighbours
		fTopRed = (1 - fDeltaX) * clrTopLeft.R + fDeltaX * clrTopRight.R;
		fTopGreen = (1 - fDeltaX) * clrTopLeft.G + fDeltaX * clrTopRight.G;
		fTopBlue = (1 - fDeltaX) * clrTopLeft.B + fDeltaX * clrTopRight.B;

		// linearly interpolate horizontally between bottom neighbours
		fBottomRed = (1 - fDeltaX) * clrBottomLeft.R + fDeltaX * clrBottomRight.R;
		fBottomGreen = (1 - fDeltaX) * clrBottomLeft.G + fDeltaX * clrBottomRight.G;
		fBottomBlue = (1 - fDeltaX) * clrBottomLeft.B + fDeltaX * clrBottomRight.B;

		// linearly interpolate vertically between top and bottom interpolated results
		iRed = (int)(Math.Round((1 - fDeltaY) * fTopRed + fDeltaY * fBottomRed));
		iGreen = (int)(Math.Round((1 - fDeltaY) * fTopGreen + fDeltaY * fBottomGreen));
		iBlue = (int)(Math.Round((1 - fDeltaY) * fTopBlue + fDeltaY * fBottomBlue));

		// make sure colour values are valid
		if (iRed < 0) iRed = 0;
		if (iRed > 255) iRed = 255;
		if (iGreen < 0) iGreen = 0;
		if (iGreen > 255) iGreen = 255;
		if (iBlue < 0) iBlue = 0;
		if (iBlue > 255) iBlue = 255;

		bmBilinearInterpolation.SetPixel(j, i, Color.FromArgb(iRed, iGreen, iBlue));
	}
}
bmBilinearInterpolation.Save("rotatedslantedtreetopsky.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

In the original article, the resulting image had the same dimensions as the source image. This meant that after rotation, some pixel information would be lost. To counter that, we make the dimensions of the resulting image “large enough”.

Considering that this is a rotation operation with an arbitrary angle, when all possible rotations of the source image are placed on top of one another, you get a smudgy circle. Not sure if you can visualise that. So the resulting image must be able to contain a circle, and because our images are stored in rectangular format, we need a square. That’s why in the code, I didn’t use a separate X and Y component of the dimensions, because they’re the same.

The width of the square has to be, at the minimum, the diagonal of the source image (which you can use Pythagoras’ Theorem to calculate). I added a buffer (of 20 pixels) to ensure that our square can contain the resulting image.

Here’s the source image:
Slanted treetop and sky

And here’s the resulting image:
Rotated slanted treetop with sky

Alright, have fun with the code.

Stationary camera, moving scene

Previously, we talked about revolving the entire 3D scene about the camera, and also the problem of the camera looking directly downwards. Today, we’ll look at the mechanics of implementing that stationary camera (it ain’t pretty).

There are 2 transformations to take care of: translation and rotation. Translation takes care of the distance between the camera and the point it’s looking at. Rotation takes care of simulating the camera turning around to look at objects, roughly speaking. Let me use a 2D version to illustrate the concept.

Reverse translation and rotation of 2D scene

Suppose the camera is at some arbitrary position looking at an object. Based on the positions of the camera and the object, you can find the distance between them. You know, with this:
d = sqrt( (cx-ox)^2 + (cy-oy)^2 )
where cx and cy are the x-coordinate and y-coordinate of the camera respectively, and ox and oy are the x-coordinate and y-coordinate of the object respectively.

The camera is looking at the object, so the angle (theta) of its line of sight with respect to the (for example) x-axis can be calculated.

Suppose we want the stationary camera to look in the direction of the positive y-axis, and be positioned at the origin (0,0). To make the scene viewed through a stationary camera the same as that in the original version (the default by the 3D engine), we would rotate the entire scene (90 – theta) degrees, then translate the result of that d units along the positive y-axis.

Remember that order of transformations is important. Rotating first then translating, is (generally) different from translating then rotating.

So that’s the general idea of making a stationary camera work, by moving and rotating the entire scene. The fun part comes because it’s in 3D.

The distance calculation still holds true:
d = sqrt(x^2 + y^2 + z^2)

The angle… not so much. Because it’s in 3D, I adopted spherical coordinates. The radius would simply be the distance calculated previously. But there are now 2 angles to calculate, theta and phi.

Spherical coordinate angles

Suppose the camera is at (a,b,c) and the viewed object is at (p,q,r). We make the viewed object the centre of our attention, so we start our calculations with the object at the origin. Therefore, the camera is at (a-p, b-q, c-r).

We can calculate the distance between them as
d = sqrt( (a-p)^2 + (b-q)^2 + (c-r)^2 )

Then we also solve for the following set of simultaneous equations (note I’m using y-axis as the “upward” axis)
x = r * sin(theta) * sin(phi)
y = r * cos(phi)
z = r * cos(theta) * sin(phi)

==>

a-p = d * sin(theta) * sin(phi)
b-q = d * cos(phi)
c-r = d * cos(theta) * sin(phi)

to look for the angles theta and phi, where
0 <= theta <= 2*PI 0 <= phi < PI Once found, the rendering occurs by rotating the entire scene phi degrees about the positive z-axis (starting from negative y-axis as 0 degrees), then rotate about the positive y-axis (starting from the positive z-axis as 0 degrees), then translate by (-a,-b,-c) (this moves the entire scene away from the camera positioned at the origin). Well, that was a lot of trouble. What was I trying to solve again? Oh yeah, that looking down and losing the "up" vector problem. Notice anything wrong in this implementation? The "up" vector of the camera was never considered. But figuring out all the math was fun... if only it solved something too... *sigh* [Note: all use of "degrees" in this article can be substituted with "radians", depending on your situation. Use accordingly.]

Rotate backwards to stay level

It was a university programming assignment. I was to write an OpenGL program to render a Ferris wheel. The requirements were simple. There had to be 7 spokes emanating from the centre, each at an equal angle from each other. At the end of each spoke, there was to be a carriage. No outer rim was required. All 7 spokes and 7 carriages were to be simple cuboids. The wheel was to turn slowly. Colour aesthetics up to the individual.

I’ve already had lessons on simple rotation and translation operations in OpenGL. Ambient colouring, materials and shading were also taught. And simple cuboids were like basic rendering stuff.

The hard part that my fellow students found was in keeping the carriages level, while rotating the Ferris wheel.

My professor, being the evil mind that he was, chose 7 spokes, so the angle between each spoke was “weird” (no nice number). Believe it or not, that confused a heck of a lot of students… Rendering the spokes were easy. Render a long cuboid with one end at the origin, and rotate multiples of 360/7 degrees. The carriages on the other hand, needed some work…

A simple way of orienting the Ferris wheel is to align it with the XY plane, with the centre of the wheel at the origin. There are then 2 methods to render the carriages. The first is to calculate the XY coordinates of centres of all the carriages, and simply translate them there. Yes, there will be sines and cosines in the calculation. I’ll leave it to you as an exercise. If you were able to follow the article on bilinear interpolation in image rotation, you can do this.

The second method is to just use the rendering engine’s in-built functions. For example, you render a vertical spoke with one end at the origin, and the other end along the positive Y-axis. Then you render a carriage at the latter end of the spoke.

Then what do you do? Render the 2nd spoke-carriage combination exactly the same as the 1st, but rotate the whole thing 360/7 degrees clockwise. Here’s where the problem comes. Since the carriage is “tied” to the spoke, the rotation operation affects the carriage as well.

To keep the carriage level, you have to undo the rotation operation. How do you do that? Rotate the carriage in the other direction with the same angle.

Let’s leave the spokes out of the picture. To render a carriage in the correct position, at the correct angle, this is the series of steps to take (assuming the carriage is at the origin):

  • Rotate -i * (360/7) degrees (anti-clockwise)
  • Translate len units in positive Y direction
  • Rotate i * (360/7) degrees (clockwise)

where i is the number of multiples required, and len is the length of the spoke.

If you’re following this with OpenGL or DirectX, take care. That series of steps have to be reversed, because the 2 rendering engines apply the transformations in reverse order.

Hmm… that was a long story…

The one about chicken heads

Did you know chickens have this ability to keep their heads stable, even if their bodies are moving? Check this video out.

I believe chickens use a similar principle as discussed in the Ferris wheel. For example, if a chicken’s body was moved forwards (in the direction of its head), to keep its head in the same position, it has to move its head backwards.

A fluttering thought

To stay the same in the face of change, one must replicate and execute the change in the opposite manner.

Sort of like Newton’s First and Third Laws combined.

I was just thinking, in the face of changes in these times, staying the same is actually more tiring than going with the flow. I mean, you expend the exact same amount of effort to stay the same, and you have to expend more to improve (on business, on technology and so on). That doesn’t quite make sense…

It’s a cliche, I know, but the only constant in life is change. Expect it. Embrace it. Besides, staying the same is boring at some level…

A simple experiment

To convince yourself of the “backwards” principle discussed in the rendering of a Ferris wheel, try the following.

  • Stand up straight, face and body forwards
  • Turn your body, from the shoulders down, clockwise
  • You must keep your head still facing the same direction as at the start

Did you have to turn your head anti-clockwise to keep facing the same original direction?

Rotating a matrix cannot be done with matrix multiplication

Note that this is different from rotation matrices in our previous discussion on transformation matrices. We were rotating (3D) objects and vertices (points) then. We’re talking about rotating a matrix here.

I read this article by Raymond Chen discussing the rotation of a 2 dimensional array (which is equivalent to a matrix in our case). In it, he stated:

The punch line for people who actually know matrix algebra: Matrix multiplication doesn’t solve the problem anyway.

Yeah, I’m one of those people.

I’ve never quite thought about it before, so I decided to explore it further. Why can’t matrix multiplication be used?

Before we go into that, let’s look at a reference link in the above article from which this whole topic came about. In it, Chris Williams (the author) gave some code for rotating a matrix. I’m not sure what he referred to by “left” and “right” turn because I feel it’s a bit ambiguous.

Duty calls by XKCD

Anyway, the code on the left turn is wrong. This is what’s given:

' For LEFT turns

For Y = 0 to 3

    For X = 0 to 3

        Destination(Y,X) = Source(X,Y)

    Next

Next

That is the algorithm for transposing a matrix.

He also gave code for the “right” turns, which is correct. I prefer to have “messy” indices on the right side of the assignment. To each his own…

Anyway, here’s what I came up with:

const int cnSize = 4;
int[,] Source = new int[cnSize, cnSize];
int[,] Destination = new int[cnSize, cnSize];
int i, j;

Console.WriteLine("Source matrix:");
for (i = 0; i < cnSize; ++i)
{
    for (j = 0; j < cnSize; ++j)
    {
        Source[i, j] = i * cnSize + (j + 1);
        Console.Write("{0:d2} ", Source[i, j]);
        Destination[i, j] = -1;
    }
    Console.WriteLine();
}
Console.WriteLine();

Console.WriteLine("Using given 'clockwise turn' formula");
// given left turn
for (j = 0; j < cnSize; ++j)
{
    for (i = 0; i < cnSize; ++i)
    {
        Destination[j, i] = Source[i, j];
    }
}
for (i = 0; i < cnSize; ++i)
{
    for (j = 0; j < cnSize; ++j)
    {
        Console.Write("{0:d2} ", Destination[i, j]);
    }
    Console.WriteLine();
}
Console.WriteLine();

Console.WriteLine("Using corrected 'clockwise turn' formula");
// correct given left turn
for (j = 0; j < cnSize; ++j)
{
    for (i = 0; i < cnSize; ++i)
    {
        Destination[j, cnSize - 1 - i] = Source[i, j];
    }
}
for (i = 0; i < cnSize; ++i)
{
    for (j = 0; j < cnSize; ++j)
    {
        Console.Write("{0:d2} ", Destination[i, j]);
    }
    Console.WriteLine();
}
Console.WriteLine();

Console.WriteLine("Using given 'anticlockwise turn' formula");
// given right turn
for (j = 0; j < cnSize; ++j)
{
    for (i = 0; i < cnSize; ++i)
    {
        Destination[cnSize - 1 - j, i] = Source[i, j];
    }
}
for (i = 0; i < cnSize; ++i)
{
    for (j = 0; j < cnSize; ++j)
    {
        Console.Write("{0:d2} ", Destination[i, j]);
    }
    Console.WriteLine();
}
Console.WriteLine();

Console.WriteLine("End of program");
Console.ReadLine();

I said you'd have to get used to nested for loops, didn't I? *smile* The output looks like this:

Rotating a matrix

Ok, back to the issue at hand. Let me phrase the question as "Is there a general transformation matrix that rotates a square matrix with size N (N > 1) clockwise?" I'm going to try answering that question using proof by contradiction.

Suppose there is such a transformation matrix. Without loss of generality, we'll assume N to be 2. So there is a 2 by 2 matrix A such that

[ A(0,0)  A(0,1) ]  [ a  b ]  =  [ c  a ]
[ A(1,0)  A(1,1) ]  [ c  d ]     [ d  b ]

Let's look at the top left and top right entries of the resulting matrix, which gives us two simultaneous equations:
A(0,0)a + A(0,1)c = c
A(0,0)b + A(0,1)d = a

Taking the 1st equation, we have
A(0,0)a = c - A(0,1)c

Dividing both sides by a, we have
A(0,0) = (c/a) * (1 - A(0,1))

You might find this ok, but take a look at the (c/a) part. This assumes that a is non-zero. Think about that. Our general transformation matrix assumes that the top left entry "a" to be rotated is non-zero. Hmm... Let's continue for a bit.

Substituting the value of A(0,0) into the 2nd equation, we have
b*(c/a)*(1 - A(0,1)) + A(0,1)d = a

Do the algebraic simplifications, and we'll get this
A(0,1) = (a^2 - bc) / (da - bc)

Take a look at the denominator. This assumes that (da - bc) is non-zero. If you have some knowledge of matrices, this is the determinant of the matrix.

So, our general transformation matrix assumes that the top left entry is non-zero and the determinant of the 2 by 2 matrix to be rotated is non-zero. Do you see problems yet? And we're not even looking at the other 2 simultaneous equations yet...

We have arrived at a contradiction. Our "general" transformation matrix isn't general at all. There are hidden assumptions. This means there's no such general transformation matrix for rotating a matrix.

Q.E.D.

I feel my proof given above is kinda weak. Maybe you can come up with a stronger proof?

Cartesian coordinates and transformation matrices

If you’re doing any work in 3D, you will need to know about the Cartesian coordinate system and transformation matrices. Cartesian coordinates are typically used to represent the world in 3D programming. Transformation matrices are matrices representing operations on 3D points and objects. The typical operations are translation, rotation, scaling.

2 dimensional Cartesian coordinates

You should have seen something like this in your math class:

2D Cartesian coordinates
[original image]

The Roman letters I, II, III, and IV represent the quadrants of the Cartesian plane. For example, III represents the third quadrant. Not a lot to say here, so moving on…

3 dimensional Cartesian coordinates

And for 3 dimensions, we have this:

3D Cartesian coordinates
[original image]

I don’t quite like the way the z-axis points upward. The idea probably stems from having a piece of paper representing the 2D plane formed by the x and y axes. The paper is placed on a flat horizontal table, and the z-axis sticks right up.

Mathematically speaking, there’s no difference.

However, I find it easier to look at it this way:

Another 3D Cartesian representation

The XY Cartesian plane is upright, representing the screen. The z-axis simply protrudes out of the screen. The viewport can cover all four quadrants of the XY plane. The illustration only covered the first quadrant so I don’t poke your eye out with the z-axis *smile*

There is also something called the right-hand rule, and correspondingly the left-hand rule. The right-hand rule has the z-axis pointing out of the screen, as illustrated above. The left-hand rule has the z-axis pointing into the screen. Observe the right-hand rule:

Right-hand rule

The thumb represents the x-axis, the index finger represents the y-axis and the middle finger represents the z-axis. As for the left-hand rule, we have:

Left-hand rule

We’re looking at the other side of the XY plane, but it’s the same thing. The z-axis points in the other direction. And yes, I have long fingers. My hand span can cover an entire octave on a piano.

What’s the big deal? Because your 3D graphics engine might use a certain rule by default, and you must follow. Otherwise, you could be hunting down errors like why an object doesn’t appear on the screen. Because the object was behind the camera when you thought it’s in front. Your selected graphics engine should also allow you to use the other rule if you so choose.

In case you’re wondering, here’s the right-hand rule illustration with the z-axis pointing upwards:

Right-hand rule with z-axis upwards

I still don’t like a skyward-pointing z-axis. It irks me for some reason…

Scaling (or making something larger or smaller)

So how do you enlarge or shrink something in 3D? You apply the scaling matrix. Let’s look at the 2D version:

Scaling a circle in 2D

If your scaling factor is greater than 1, you’re enlarging an object. If your scaling factor is less than 1, you’re shrinking an object. What do you think happens when your scaling factor is 1? Or when your scaling factor is negative?

So how does the scaling factor look like in a scaling matrix?

Scaling matrix 2D

If you don’t know what that means, or don’t know what the result should be like, review the lesson on matrices and the corresponding program code.

You will notice there are separate scaling factors for x- and y- axes. This means you can scale them independently. For example, we have this:

Sphere above water

And we only enlarge along the x-axis:

Enlarge sphere along x-axis

We can also only enlarge along the y-axis:

Enlarge sphere along y-axis

Yeah, I got tired of drawing 2D pictures, so I decided to render some 3D ones. Speaking of which, you should now be able to come up with the 3D version of the scaling matrix. Hint: just add a scaling factor for the z-axis.

Rotating (or spinning till you puke)

This is what a rotation matrix for 2 dimensions looks like:

Rotation matrix 2D

That symbol that looks like an O with a slit in the middle? That’s theta (pronounced th-ay-tuh), a Greek alphabet. It’s commonly used to represent unknown angles.

I’ll spare you the mathematical derivation of the formula. Just use it.

You can convince yourself with a simple experiment. Use the vector (1,0), or unit vector lying on the x-axis. Plug in 90 degrees for theta and you should get (0,1), the unit vector lying on the y-axis.

That’s anti-clockwise rotation. To rotate clockwise, just use the value with a change of sign. So you’ll have -90 degrees.

Depending on the underlying math libraries you use, you might need to use radians instead of degrees (which is typical in most math libraries). I’m sure you’re clever enough to find out the conversion formula for degree-to-radian yourself…

Now for the hard part. The 3D version of rotation is … a little difficult. You see, what you’ve read above actually rotates about the implied z-axis. Wait, that means you can rotate about the x-axis! And the y-axis! Sacrebleu! You can rotate about any arbitrary axis!

I’ll write another article on this. If you’re into this, then you might want to take a look at this article on 3D rotation. I’ll also touch on a concept where you rotate about one axis and then rotate about another axis. Be prepared for lots of sine’s and cosine’s in the formula. Stop weeping; it’s unseemly of you.

Translating (nothing linguistic here)

What it means is you’re moving points and objects from one position to another. Let’s look at a 1 dimensional example:

Translation in 1 dimension

The squiggly unstable looking d-wannabe? It’s the Greek alphabet delta. Delta-x is a standard notation for “change in x”. In this case “x” refers to distance along the x-axis. We’ll use an easier-to-type version called “dx” for our remaining discussion.

Translation in 2 dimensions

In 2 dimensions, we have the corresponding dy, for “change in y”. Note that there’s no stopping you from using negative values for dx or dy. In the illustration above, dx and dy are negative.

You’ll have to imagine the case for 3D because the diagram is likely to be messy. But it’s easy to visualise. You just do the same for z-axis.

So what’s the transformation matrix for translation? First, you need to extend your matrix size and vector size by one dimension. The exact reasons are due to affine transformations and homogeneous coordinates (I’ve mentioned them briefly earlier).

Consider this: You have a point (x,y,z) and you want it to be at the new position (x + dx, y + dy, z + dz). The matrix will then look like this:

Translation matrix

Notice that for scaling, the important entries are the diagonal entries. For rotation, there are sine’s and cosine’s and they’re all over the place. But for translation, the “main body” of the matrix is actually an identity matrix. The fun stuff happens in the alleyway column on the extreme right of the matrix.

That reminds me. Because you’ll be using all the transformation matrices together, all matrices must be of the same size. So scaling and rotation matrices need to be 4 by 4 too. Just extend them with zero entries except the bottom right entry, which is 1.

Conclusion

We talked about 2D and 3D Cartesian coordinates. I’ve also shown you the right-hand and left-hand rules. This forms the basis for learning basic transformations such as scaling, rotation and translation.

There are two other interesting transformation matrices: shearing and reflection. I thought what we have is good enough for now. You are free to do your own research. When the situation arise, I’ll talk about those two transformations.

If you enjoyed this article and found it useful, please share it with your friends. You should also subscribe to get the latest articles (it’s free).

You might find this article on converting between raster, Cartesian and polar coordinates useful.

You might find these books on “coordinate transformation” useful.