Sunday, 22nd January 2012
Isometeric projections are commonly used in technical drawings and used to be used in some computer game graphics. In an isometeric projection the three axes appear 120° from each other and are equally foreshortened. It can be achieved by rotating an object 45° in the plane of the screen and ~35.3° through the horizontal axis (see Wikipedia for more information).
We start with an image, such as this floor plan.
If we rotate this image 45° now, it will move almost off screen as rotations are always centred on the point (0, 0).
So we first translate our image so the point around which we want to rotate it (probably the centre of the image) is now at the point (0, 0). For example, the centre of this image is at (150, 80), so we translate the room by wrappping it in a group ike so:
<g transform="translate(-150, -80)"> room shapes... </g>
Rotation in the plane of the screen
We can now rotate our image. A rotation of 45° in the plane of the screen is easy enough with
transform="rotate(45)", but a rotation through the horizontal axis is more tricky. The solution is to use matrices. A rotation in the plane of the screen is a rotation through the z-axis and given by the following 3x3 matrix:
Because SVGs are 2D, they don't bother with the final row of this matrix (although it is important for intermediate steps as we'll see). SVG matrices are given as a single line of parameters in the order (column 1, row 1), (column 1, row 2), (column 2, row 1) and so on. Therefore we can replace
transform="matrix(0.707 0.707 -0.707 0.707 0 0)"
Rotation through the horizontal axis
Rotating something through the horizontal (or x-) axis means effectively tilting the image away from us. Such a rotation is given by the following 3x3 matrix:
In an isometric projection, the angle the camera moves is ~35.3°, however, this assumes that what we're looking at is lying on the floor (i.e. the xz-plane). Our image is "upright", in the xy-plane, so we actually need to rotate by 90°-35.3°. Theta is therefore 54.7°.
If we now multiply Rz(45°) by Rx(54.7°) we get:
Or in SVG terms:
transform="matrix(0.707 0.409 -0.707 0.409 0 -0.816)"
We can now add this to a group that contains the group with the translate transformation.
Translation back to the centre
Finally, we need to move our image back to the centre of the screen. We can do this a few ways. We could leave the image where it is and change the viewBox to be centred on (0, 0):
viewBox="-150, -80, 300, 160"
Alternatively, we could just add another group with a translation back:
<g transform="translate(150, 80)">
You may find that you have to tweak the translation a bit to ensure the corners stay within the bounds of the image.
Finally, if you're feeling very adventurous or want to minimise the size of the SVG and processing require to display it, you can combine the two translation matrices with the rotation matrix. The first translation matrix, T1, is given by:
The second translation matrix, T2, would be the same, but with positive 150 and 80. If we multiply T2 x Riso x T1 we get:
<g transform="matrix(0.707 0.409 -0.707 0.409 100.51 -14.78)">
Note that this transformation will only work well if you want to rotate about the point (150, 80). As you can see from the file sizes below, we've saved ourselves 94 bytes.