Page 3: Matrices in Code

CS559 Spring 2021 Sample Solution - Workbook 4

Written by CS559 course staff

You can try out the example solutions here. The interesting code is on page 4 and page 5.

Vectors and Matrices in JavaScript

By itself, JavaScript doesn’t deal with matrices very well. We will almost always want to use a library that implements matrices. However, for now, we will do it ourselves. Part of this is so that we don’t need to take the time to learn about a matrix library until later in the class when we’ll get one as part of a graphics API. Part of this is so that we can understand what happens inside.

Part of the problem is that arrays in JavaScript are very flexible. You can use them to store sets of numbers, but they aren’t necessarily optimized to do that: they are designed to be very flexible.

For 1D arrays of numbers (that we use to represent points and vectors), we can just use JavaScript arrays. There are some efficiency reasons why we might prefer something fancier, but for now, this will be fine. (If you’re curious, check out TypedArray objects).

Note that we could make a proper class for 2D points/vectors. But we’re not going to - yet.

For 2D arrays of numbers (matrices), the choice is less obvious. JavaScript doesn’t actually have 2D arrays. Arrays are 1D. You can make an array of arrays, but this can be cumbersome. Instead, we’ll put the numbers into a 1D array. This is common practice in many situations because ultimately, the numbers are going to be stored as a 1D array in memory anyway.

When we put a matrix into a 1D array, we have to choose what order to put the elements in. The most common choice is row major format, which basically puts the elements in the order that we would read them (across rows, then down). So the matrix: $$ \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix} $$ will become the array [ 1,2,3,4,5,6,7,8,9 ]

The other choice is column major format, where we read things down the columns first, then across. So the array would be [1,4,7,2,5,8,3,6,9]

Historically, people used this (it is the way Fortran worked). But in the modern era, row major is more common. We mention column major because it will come up when we have to consider legacy systems (like OpenGL).

Even modern systems mix row and column major formats. When we get to using THREE.js, we’ll see that it uses column major internally, but row major in its documentation and certain commands.

Transformations in Canvas

Internally, Canvas uses matrices to represent coordinate systems. You can access the transformation matrix in modern web browsers using the getTransform method of the 2D context (see this for documentation). (this has only been available recently)

When we perform a transformation function on a Canvas, it changes the current transformation to be the composition of the current transformation with the transformation from the command. The old transformation goes away - this is why it is so important to save (so you can restore).

When we start out with Canvas, the current transformation is the identity matrix: when we draw, the coordinates are used directly as Canvas coordinates.

Each transformation function we apply (translate, rotate, scale, and the generic transform we will see in a moment) changes the current transformation such that it includes the new one as well. It multiplies the current transformation matrix with what the command provides, and makes this the new current matrix. The current transformation is the transformation between the coordinate system we’re working in (how coordinates in the drawing command is interpreted), and the Canvas coordinate system.

The transform method of the Canvas allows us to give a transformation as a matrix. Because Canvas only supports affine transformations, the bottom row of the 3x3 matrix is always (0,0,1), and we only need to pass the transform function 6 numbers (not 9). Note that the order that the parameters are passed are not row-major. (see Canvas Transform).

So the command context.transform(a,b,c,d,e,f) multiplies the current transformation matrix with the transformation: $$\begin{bmatrix} a & c & e \\ b & d & f \\ 0 & 0 & 1 \end{bmatrix}$$

So, for example, e and f are the translation. Notice that this is different than the row-major convention we had above.

Canvas does allow us to replace the current transformation: this is via the setTransform method. Even the setTransform documentation describes this as first setting the transformation to the identity, and then applying the new transformation.

Trying the transformations in Canvas

If we want to confirm that the transformations inside of Canvas match what you expect, we can try it out - we can perform the transformations, get the matrix, and look at it.

Here is a really boring example. I checked that

  1. the initial matrix is the identity;
  2. translate, rotate and scale are the matrices I expect; and
  3. when I compose these matrices, they compose as we expect.

To look at the matrices, we can just write them to the page or to the console. 04-03-01.html

Summary: Matrices in Canvas and Code

That hopefully explains how we turn the matrices that we see in the textbook into something we can use in our Canvas code.

On the Page  4  (Matrix Exercises) we’ll try using it.

There are no points associated with this page.