Isometric projection


22 Jan 2012 Code on Github

Introduction

Isometric projections are commonly used in technical drawings and used to be used in some computer game graphics. In an isometric 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° ($arctan\left(\frac{1}{\sqrt{2}} \right)$) through the horizontal axis (see Wikipedia for more information).

The code for all these examples are on Github.

Getting started

We start with an image, such as this floor plan.

We can rotate the image by wrapping it in a group element (<g>) and adding a transform attribute, transform="rotate(45)". The problem is this will move the image partly off screen as rotations are centred on the point (0, 0).

Translation

To stop the image from rotating off the page, we first translate our image so the point around which we want to rotate (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 wrapping it in a group like 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 this 3 × 3 matrix:

$R_z(\theta) = \begin{bmatrix} cos(\theta) & -sin(\theta) & 0\\ sin(\theta) & cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix}$

A rotation is a rotation about the z-axis by 45° degree is therefore.

$R_z(45^\circ) = \begin{bmatrix} \frac{\sqrt{2}}{2} & -\frac{\sqrt{2}}{2} & 0\\ \frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2} & 0 \\ 0 & 0 & 1 \end{bmatrix}$

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="rotate(45)" with:

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 3 × 3 matrix:

$R_x(\theta) = \begin{bmatrix} 1 & 0 & 0 \\ 0 & cos(\theta) & -sin(\theta) \\ 0 & sin(\theta) & cos(\theta) \end{bmatrix}$

In an isometric projection, the camera moves through an angle of $arctan\left(\frac{1}{\sqrt{2}} \right)$. However, this assumes that what we're looking at is lying on the floor (i.e. the xz-plane) and we're tilting it up. Our image is "upright", in the xy-plane, so we actually want to tilt it down by the complementary angle, 90° - $arctan\left(\frac{1}{\sqrt{2}} \right)$. This basically means flipping the sides of the triangle, so the angle is $arctan(\sqrt 2)$.

When we plug this value into the matrix we get:

$R_x(arctan(\sqrt 2)) = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \frac{\sqrt{3}}{3} & -\frac{\sqrt{6}}{3} \\ 0 & \frac{\sqrt{6}}{3} & \frac{\sqrt{3}}{3} \end{bmatrix}$

Combining rotations

We can then multiply the matrices to get a single transformation:

$R_{iso} = \begin{bmatrix} \frac{\sqrt{2}}{2} & -\frac{\sqrt{2}}{2} & 0 \\ \frac{\sqrt{6}}{6} & \frac{\sqrt{6}}{6} & -\frac{\sqrt{6}}{3} \\ \frac{\sqrt{3}}{3} & \frac{\sqrt{3}}{3} & \frac{\sqrt{3}}{3} \end{bmatrix}$

Or in SVG terms:

transform="matrix(0.707 0.409 -0.707 0.409 0 -0.816)"

Combining transformations

If your image is centered on (0, 0) then you can just use the isometric matrix above. If not you have to first translate your image to the origin, then apply the isometric matrix, then translate back. You may need to adjust the viewBox to fit the new shape.

Note that matrices are applied in the reverse order, so for my image, centered on (150, 80), I use:

<g transform="translate(150, 80) matrix(0.707 0.409 -0.707 0.409 0 -0.816) translate(-150, -80)">

If you're feeling adventurous you can combine all three transforms into one matrix. If the center of your image is $(x, y)$, then the final transform will be:

$R_{iso} = \begin{bmatrix} \frac{\sqrt{2}}{2} & -\frac{\sqrt{2}}{2} & x - \frac{\sqrt{2}}{2}(x - y) \\ \frac{\sqrt{6}}{6} & \frac{\sqrt{6}}{6} & y - \frac{\sqrt{6}}{6} (x + y - 2) \\ \frac{\sqrt{3}}{3} & \frac{\sqrt{3}}{3} & 1 - \frac{\sqrt{3}}{3}(x + y + 1) \end{bmatrix}$

So for the particular case of the image centered at (150, 80), I get:

<g transform="matrix(0.707 0.409 -0.707 0.409 100.5 -14.71)">

Comments (5)

Alexandre Valdetaro on 23 Aug 2014, 7:17 a.m.

Thanks very much! This is exactly what I was looking for!

Kris J on 2 Feb 2016, 7:23 p.m.

I have been looking for a decent explanation on this for a while! Thank you!

I just wanted to know; to implement this in HTML5, would it be the same as you would normally implement an SVG file? I have been trying, and I'm not sure if it's just the browser I'm using (Safari) or if I'm doing something wrong, because while the style change shows up, the hover effect does not.

lbineau on 12 Jul 2016, 11:29 a.m.

Thank you for this really usefull tutorial !

I try to implement it with Javascript to automate the calculations of the final matrix.

http://codepen.io/lbineau/pen/PzOzgw?editors=0010 (use http://mathjs.org/ for matrix)

I find the correct values for Riso but not when I multiply it with the translation matrixes.

Did I miss something, values, operation order ?

Anonymous on 25 Jun 2017, 10:41 a.m.

Nice tutorial .

anon on 19 Nov 2019, 12:38 a.m.

Arghhhhhh, you're brilliant!