# Homogeneous coordinates in 2D, from scratch

Here’s an animation of a spinning, orbiting rectangle.
You can also see, in black, the *projection* of this rectangle,
as seen from the origin point (marked with a cross).
All of this is described with a tiny library
that uses *homogeneous coordinates*
to describe the rotation, scaling, translation, and projection.

In my previous post
I showed a matrix library in 5 lines of code.
Matrices can describe several operations you will use in graphics programming;
most importantly, *rotation* and *scaling*.

But for graphics programming,
plain linear algebra has at least two big deficiencies.
Linear functions can’t describe *translation* (that is, moving stuff!),
because they preserve the origin point.
And linear functions can’t describe *projection*
(that is, simulating a camera),
because they keep parallel lines parallel.

The *homogeneous coordinates* system is a kind of mathematical hack
that allows describing translation and projection.
It builds on top of plain linear algebra,
but adds an extra dimension, usually called *w*.
Imagine all true 2D points being drawn on the plane `w=1`

by a laser pen that sits at the origin.
Or again, the point `[2,3]`

is modelled by all points that pass through the straight line going through `[0,0,0]`

and `[2,3,1]`

(the laser line).
Or again more formally,
the two-dimensional point `[2,3]`

is modelled by all three-dimensional points `[2/w, 3/w, w]`

.

We can *translate* the two-dimensional drawing on the `w=1`

plane
by skewing space.
We move the `w`

basis vector by the amount to translate.

And we can *project* the two-dimensional drawing onto a one-dimensional line.
The very definition of homogeneous coordinates behaves like projection.
We can exploit this by squashing and skewing space.
(This projection transformation is a bit hard to describe.
I’ll try to animate it in a future post.)

Here is my 6-line homogeneous coordinates library (which builds on the 5-line matrix library in my previous post):

```
const rotateHom2d = a => [[Math.cos(a), Math.sin(a), 0], [-Math.sin(a), Math.cos(a), 0], [0, 0, 1]];
const scaleSepHom2d = (s) => [ [s[0], 0, 0], [0, s[1], 0], [0, 0, 1] ];
const scaleHom2d = s => scaleSepHom2d([s,s]);
const translateHom2d = v => [[1, 0, 0], [0, 1, 0], [v[0], v[1], 1]];
const unHom2d = ([x,y,w]) => [x/w, y/w];
// Projects from origin onto line y=1. Results are in x-coord after normalizing with `unHom2d`.
const projectHom2d = [ [1,0,0], [0,1,1], [0,0,0] ];
```

In this post, I described homogeneous coordinates for transforming 2D space, and projecting it onto a line. But it can be used in much the same way to transform 3D space, and project it onto a plane. I’ll show this in a future post.

With Vidrio

With generic competitor

### More by Jim

- Your syntax highlighter is wrong
- Granddad died today
- The Three Ts of Time, Thought and Typing: measuring cost on the web
- I hate telephones
- The sorry state of OpenSSL usability
- The dots do matter: how to scam a Gmail user
- My parents are Flat-Earthers
- How Hacker News stays interesting
- Project C-43: the lost origins of asymmetric crypto
- The hacker hype cycle
- The inception bar: a new phishing method
- Time is running out to catch COVID-19
- A probabilistic pub quiz for nerds
- Smear phishing: a new Android vulnerability

Tagged #mathematics, #programming, #js, #graphics. All content copyright James Fisher 2020. This post is not associated with my employer. Found an error? Edit this page.