💾 Archived View for gemini.robrohan.com › 2021-04-24-matrix-fun.md captured on 2022-06-11 at 23:32:15. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2022-04-28)

-=-=-=-=-=-=-

---

author: rob

date: 2020-10-16 08:00:00+11:00

slug: matrix-fun

title: Fun with Matrices

tags: [kale, maths]

---

This post is a little bit contrived.

Since I've been playing around with 3D graphics and GPUs, I've become

fascinated with matrices (and maths in general). On top of that, I've been

looking for a reason to play with Jupyter Notebooks. I've become interested

in [literate programming](https://en.wikipedia.org/wiki/Literate_programming)

as well - it seems like a fantastic teaching tool.

So instead of doing what I was supposed to be doing this lovely Saturday, I

decided to try to write a super basic (non compatible) version of Jupyter

Notebooks for Javascript posts (I call it

[Kale](https://en.wikipedia.org/wiki/Kale_(moon))). I decided to write a

simple post about using Matrices in Javascript to try it out.

When you click the run button on any of the code blocks on this post, the

output of one block will be the input to the next block. You'll need to

execute them in order for them to work - if you skip one you'll get an

error.

One last thing - I am not a maths major or anything of the sort. If you've

found this while doing homework for school, you're probably better off

reading something else. If you're just a tinkerer / hacker / explorer you

might like it.

Ok, lets try out this slightly interactive post...

Why a Matrix is Useful

I think of matrices as little black boxes that transform a thing (usually a

set of numbers) into another thing (usually a set of numbers). I believe the

proper term is System of Equations.

Imagine you have a multi-threaded application that can run a set of

functions in parallel. You could define all the functions, store them in a

table, and then run them all at the same time.

If you wired up a bunch of these, you could make some interesting things -

like maybe a GPU or a quantum computer.

It'll be clearer with some examples.

Anatomy of a Matrix

For this post I am going to be using a matrix layout that is useful for 2D

programming. This was the easiest for me to get started with, and it scales

to more dimensions once you have it down.

The first matrix we'll talk about is the identity matrix. It looks like this:

<figure>

$

I=

\begin{bmatrix}

1 & 0 & 0 \\

0 & 1 & 0 \\

0 & 0 & 1 \\

\end{bmatrix}

$

</figure>

The identity matrix is like a 1 in standard maths. If you multiply anything

by 1, you'll get that same number back. Same thing with the identity matrix.

Anything you multiply by the identity matrix will give you back the original

matrix.

Also note that the main feature of a matrix is made up of rows and columns.

That's all that is up there. It's just an array of numbers layed out in rows

and columns - called a 3x3 matrix.

To represent this in some code, let's make a simple class in Javascript

(click run after having a look (you can make the textarea bigger)):

class Matrix {
  /** Create a new matrix with the given row and column size */
  constructor(row, col, data) {
    if (!col || !row) {
      throw Error("Need column and row size");
    }
    this.rc = [row, col];
    if (data) {
      this.d = data;
    } else {
      this.d = new Array(row * col);
    }
  }
}

return Matrix;

Use a Matrix to Move Stuff Around

On top of the identity matrix, there are a few other commonly used matrices

that are used for translation (moving on the x and y axis), rotation, and

scale. Here are what those look like:

<figure>

$

T=

\begin{bmatrix}

1 & 0 & tx \\

0 & 1 & ty \\

0 & 0 & 1 \\

\end{bmatrix}

$

$

S=

\begin{bmatrix}

sx & 0 & 0 \\

0 & sy & 0 \\

0 & 0 & 1 \\

\end{bmatrix}

$

$

R=

\begin{bmatrix}

\cos(\theta) & -\sin(\theta) & 0 \\

\sin(\theta) & \cos(\theta) & 0 \\

0 & 0 & 1 \\

\end{bmatrix}

$

</figure>

Where _tx_ and _ty_ are translation on the x and y axis, _sx_ and _sy_ are

scale on the x and y axis, and theta is the degree in radians to rotate. The

coolest one being rotation.

One thing you can do with these matrices is multiply them together to

describe how something should move from one place to another. Using the

class we made above, lets build a few matrices to describe a translation and

rotation:

const [Matrix, me] = arguments;

// Translate to 30,30
const m1 = new Matrix(3,3,
[
  1, 0, 30,
  0, 1, 30,
  0, 0, 1,
]);

const deg = 270;
const rad = (deg * Math.PI) / 180;

// Rotate by 270 degrees
const m2 = new Matrix(3,3,
[
  Math.cos(rad), -Math.sin(rad), 0,
  Math.sin(rad), Math.cos(rad), 0,
  0, 0, 1,
]);

return [m1, m2];

One of these matrices describe a translation by 30px on the X and Y axis,

and the other a rotation by 270 degrees.

yourself (or learn about) the [unit circle](https://en.wikipedia.org/wiki/Unit_circle).

However, you don't have to understand how it works "under the hood"

if you don't want to.

Now we have two matrices that we're going to multiply together. Multiplying

two matrices together is not intuitive at first. What you have to do is

multiply the rows of one matrix against the columns of another. So we are

going to do the following:

<figure>

$

M_{1}=\begin{bmatrix}

1 & 0 & tx \\

0 & 1 & ty \\

0 & 0 & 1 \\

\end{bmatrix}

\times

\begin{bmatrix}

\cos(\theta) & -\sin(\theta) & 0 \\

\sin(\theta) & \cos(\theta) & 0 \\

0 & 0 & 1 \\

\end{bmatrix}

$

</figure>

There isn't a built-in way to do this in Javascript, and this isn't the

fastest way to do it, but here is a, hopefully, straightforward example of

multiplying our two matrices together:

const [[m1, m2], me] = arguments;

const ROW = 0;
const COL = 1;

// Make an array big enough to hold our result
const out = new Array(m1.rc[ROW] * m2.rc[COL]);

function mat_mul(m1, m2, out) {
  if (m1.rc[COL] != m2.rc[ROW]) {
    throw Error("column size of m1 must match row size of m2");
  }
  const row = new Array(m1.rc[ROW]);
  const col = new Array(m2.rc[COL]);

  // Loop over each row of the first matrix
  for (let i = 0; i < m1.rc[ROW]; i++) {
    // Load a single Row
    for (let r = 0; r < m1.rc[COL]; r++) {
      row[r] = m1.d[r + i * m1.rc[COL]];
    }
    // Loop over the columns to use when multiplying 
    // against the row loaded above
    for (let j = 0; j < m2.rc[COL]; j++) {
      let v = 0;
      // Load a single column
      for (let c = 0; c < m2.rc[ROW]; c++) {
        col[c] = m2.d[j + c * m2.rc[COL]];
        v += row[c] * col[c];
      }
      out[j + i * m1.rc[ROW]] = v;
    }
  }
}

mat_mul(m1, m2, out);
K.Print(me, out);
return out;

Note: If you're into it, trying to figure out how to quickly multiply two

matrices together is a really fun project. I want to play with a quantum

computer :-D.

Do Something Already

Ok now that we've got this matrix that will move and rotate something, what

do we do with it? Let's use it to move my profile picture up there on the

left (if you're on a desktop computer).

One problem with using a matrix in CSS is that the way CSS uses matrices

is... well... special.

Our created matrix looks like this:

<figure>

$

M_{1} =

\begin{bmatrix}

a & c & tx \\

b & d & ty \\

0 & 0 & 1 \\

\end{bmatrix}

$

</figure>

But CSS [defines the input](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix())

for it's matrix transform like this:

<figure>

$ CSS = \left[ \begin{array}{ccc} a & b & c & d & tx & ty \end{array} \right] $

</figure>

Which is neither row nor column order. I guess they decided to split the

difference and just make everyone slightly irritated.

So, lets make a quick little function that will make our awesomely cool

matrix into the version CSS wants, and apply it to that profile picture over

there:

const [out, me] = arguments;

function mat_2d_to_css(a) {
  if(a.length < 6) {
    throw Error("Array must have at least 6 elements to be formatted");
  }
  const out3 = [a[0], a[1], a[3], a[4], a[2], a[5]];
  return `matrix(${out3.join(",")})`;
}

const transform = mat_2d_to_css(out);

K.Print(me, transform);

const b = document.querySelector("img");
b.style.transition = "all 3s ease-out";
b.style.transform = transform;

Don't forget you need to have run all the code fragments for the image to

move. Or, you can:

<form>

<input type="button" value="Run them all" onclick="kale_runAll()">

</form>

Hopefully that was interesting. Matrices are one of my favourite things to

play with. If you're interested in graphics programming or quantum

computing, I highly recommend you dig into them and play around.

More Kale

Kale can also load 3rd party Javascript and run it. Here it is using d3 to

make a simple graph:

await K.Include("https://d3js.org/d3.v4.js");

const me = arguments[1];
const svg = K.GetD3Canvas(me);

const x = d3.scaleLinear().domain([0, 100]).range([0, 400]);

svg.call(d3.axisBottom(x));

svg
  .append("circle")
  .attr("cx", x(10))
  .attr("cy", 100)
  .attr("r", 40)
  .style("fill", "blue");
svg
  .append("circle")
  .attr("cx", x(50))
  .attr("cy", 100)
  .attr("r", 40)
  .style("fill", "purple");
svg
  .append("circle")
  .attr("cx", x(100))
  .attr("cy", 100)
  .attr("r", 40)
  .style("fill", "green");

return x;