# SVG, Inkscape, and the Isometric Projection

How to use affine transformation matrices to transform flat two-dimensional drawings in SVG to sides of an isometric projection with an Inkscape extension.

For simple woodworking projects I like to use free software drawing tool Inkscape to sketch up a plan for what I intend to build. Depending on the type of project, these drawings range from rough sketches, to fully detailed top-down, side, and frontal views. This is great for finding out how much timber is needed and if I can reuse anything from my stock. It also allows me to easily tweak parts of the design.

For a recent project I experimented with the isometric projection of the object I was designing. This type of drawing can help to get a better feel for the spatial layout of the design — it also makes your drawings look like they belong on the instruction sheets of a well-known Swedish manufacturer of flat-pack furniture. Inkscape offers an axonometric grid for this purpose in addition to its default rectangular grid. With both grid angles set to 30°, this grid is suitable for drawing in an isometric projection.

For simple shapes consisting of straight lines, drawing the lines directly on this grid works quite well, but as soon as the drawing gets more complicated (e.g., round shapes indicating drill holes) this becomes tricky to get right, and inaccurate to boot. So I started wondering; is there a way to convert a flat two-dimensional representation a plane to each of the three visible sides in an isometric projection?

A bit of experimenting with the transformation tools in Inkscape shows that moving from a flat projection to isometric appears to be a matter of applying a sequence of transformations to the object.

All of these transformations use 30° angles, or its derivates; the scaling value of
`≈0.866`

is the cosine of 30°. This makes sense considering the angles of the
isometric grid — although I will freely admit that I arrived at these values by
experimenting in Inkscape rather than sound mathematical reasoning.

So now I know the exact steps needed to go from one projection to another, but executing a bunch of transformations each time soon gets tedious, so can this be automated?

## Enter linear algebra

Transforming vectors sounds suspiciously like maths, so time to brush up on some theory.
As it turns out, all four basic transformations — i.e., *scaling*, *shearing*
(or skewing), *rotating*, and *translating* (which is simply moving) — can be
expressed in the form of a matrix. Multiplying a vector coordinate by this matrix
results in a new transformed coordinate, and if you apply the same matrix to each vector
coordinate in an object, the object is transformed. These matrices are called affine transformation
matrices.

Now even though I am transforming a flat projection of an object to its isometric
projection, the actual vector drawing remains rooted in the good old two dimensions of
`x`

and `y`

. So in theory, these matrices ought to be 2×2 matrices
right?

Not quite. It turns out that this holds true for the three transformations I am using
here (scaling, shearing, and rotating), but for translation, a third dimension is
needed. To solve this, a virtual third dimension is added to the two-dimensional
`(x, y)`

vectors, with a fixed coordinate of `1`

. To distinguish
this fake dimension from a normal three-dimensional one, it is called `w`

instead of `z`

. So for purposes of transformation, all vectors are extended
to `(x, y, w)`

coordinates, where `w = 1`

.

For each of the four basic transformations there exists a 3×3 matrix that can be applied to each vector coordinate of an object, resulting in a transformed object. These matrices look like this:

Conveniently, the Scalable Vector Graphics
standard (SVG) supports these matrix transformations, as does Inkscape which
uses this standard as its file format. In SVG, any object can have a
`transform`

attribute, where an affine transformation matrix may be specified
— either directly through the `matrix`

notation, or by using the
`scale`

, `skewX`

, `skewY`

, `rotate`

, and
`translate`

short-cuts. The `matrix`

notation used in SVG is a bit
confusing at first, because it looks like `matrix(A,B,C,D,E,F)`

. It turns out
the bottom row of the matrix is omitted, because it is always `[0, 0, 1]`

,
and the rest of the values map to our 3×3 matrices like this:

## And now for something completely different

Knowing how to define a matrix that rotates, shears, or scales, the next step is to
combine matrices into combined transformation matrices that transform objects from
a flat projection to each of the three isometric sides — and back again. To combine
multiple transformation matrices, they need to be multiplied in the correct order, which
is the reverse of the sequence of transformations you want to apply. So for the
conversion from flat projection to isometric top-down view described earlier, where an
object is first *scaled*, then *sheared*, and finally *rotated*, the
multiplication goes like this:

`M`_{to-iso-topdown} = M_{rotate} × (M_{shear} × M_{scale})

Performing matrix multiplication on paper certainly proved to be nostalgic for a couple of tests, but performing maths is why we invented computers in the first place. Time to let them do some work for a change.

My first instinct was to open a Python console and do the multiplication there, and unsurprisingly Python comes with everything I need in the form of the Numpy library. Taking the case of the transformation to the top-side view of the isometric projection again, I can define and multiply the matrices like this:

```
$ python3
>>> import math
>>> import numpy
>>>
>>> cos30 = math.cos(math.radians(30))
>>> sin30 = math.sin(math.radians(30))
>>> tan30 = math.tan(math.radians(30))
>>>
>>> m_scale_y = numpy.matrix([[1, 0, 0],
... [0, cos30, 0],
... [0, 0, 1]])
>>> m_shear_x = numpy.matrix([[1, -tan30, 0],
... [0, 1, 0],
... [0, 0, 1]])
>>> m_rotate = numpy.matrix([[cos30, -sin30, 0],
... [sin30, cos30, 0],
... [0, 0, 1]])
>>> m_rotate * (m_shear_x * m_scale_y)
matrix([[ 0.8660254, -0.8660254, 0. ],
[ 0.5 , 0.5 , 0. ],
[ 0. , 0. , 1. ]])
```

When this matrix is converted to the SVG transformation notation it looks like this (rounded down to three decimal places for clarity):

`matrix(0.866,0.5,-0.866,0.5,0,0)`

I can repeat this process for all six transformations — i.e., three for converting
*from* a flat projection to each of the three sides in an isometric projection,
and another three to *reverse* those transformations — to arrive at a set of
matrices I can apply.

## Extending Inkscape

Now that I have my six combined matrices, I can manually set the
`transform`

attribute on an object in SVG (in Inkscape you can use the XML
editor to do this), or I can enter the matrix in Inkscape's transformation tool. This is
fine for one or two objects, but it gets cumbersome after a while. Luckily, Inkscape can
be extended with a trivial amount of Python code, so I wrote an extension to do the
grunt work for me.

To help authors of extensions, Inkscape developers have thoughtfully included a number
of helpful Python classes and methods for extensions to depend on. For matrix operations
`simpletransform.py`

provides us with `formatTransform`

and
`parseTransform`

. The former converts a 2×3 matrix to its corresponding SVG
notation; the latter greatly simplifies my extension by solving the problem of
transforming objects that * already have their transform attribute set
with a transformation*. So if an object has its transformation set to

`rotate(45)`

, `parseTransform`

will convert that
`rotate`

short-cut to its corresponding matrix, and multiply the matrix you
want to apply to it.
The amount of code needed for this extension turned out to be trivial, with most of the work being done by the six transformation matrices, and the code provided by Inkscape.

## This works in the browser too

SVG is of course not limited to Inkscape; it works right here in today's web browsers,
and integrates surprisingly well with *HTML5*, and its friends
*EcmaScript* and CSS. Have a look at
the source of this web page if you are curious, or just experiment with this live
demonstration of an affine transformation matrix. The list of transformations can be
reordered by dragging and dropping them.

## Conclusion

With this Inkscape extension, drawing objects in the isometric projection became a lot
easier for me. I like how I can draft parts of my design in a flat projection with the
rectangular grid, and have the benefit using of Inkscape's useful *Align and
Distribute* tool, before I transform the object to one of the isometric
projection's sides.

I was pleasantly surprised at how powerful SVG can be once you get a better grasp of
some of the mathematical underpinnings. I also took the opportunity to explore the
current state of SVG and HTML integration targeting modern *evergreen* web
browsers for this write-up, and it is encouraging to see how much can now be achieved
even without any third-party EcmaScript libraries.