Using Linear Transformations In PaperJS

I was recently developing a 2D board game and wanted to skew and rotate the game board to give it a more 3D look. After skewing the board though, I wasn’t able to easily place the game pieces at the correct positions. I didn’t want to manually position the game pieces, because that would mean I couldn’t easily change the skew and rotation of the board.

Since I was using PaperJS, it was easy to skew and rotate the game board…

  // Create the basic shapes
  let boardSpaces = [new Shape(...), ...]
  let boardBase = [new Shape(...)];

  // Group them and then transform them with
  // skew and rotation.
  let board = new Group(boardBase.concat(boardSpaces));
  board.transform(new Matrix(1.0, 0.0, 0.3, 1.0, 0.0, 0.0));
  board.rotation = 146.98;

The problem was, if I queried PaperJS for the position of a game space, the return value was the position in the board’s transformed space. So if I tried to place an object in the global context at the same position as a game space, it didn’t line up with the game space (Remember they’re both in two different coordinate systems.)

  // Get the item named "1,1", which is a board space.
  let boardSpacePosition = project.getItem({name : "1,1"});

  // This position is not where you think it is.
  console.log(boardSpacePosition.position);

So what I needed to do was transform the game space’s position into the global coordinate system. To do that, I used the matrix property on the Group I created earlier.

  var globalPosition = board.matrix.transform(
    boardSpacePosition.bounds.center
  );

Then I was able to use the globalPosition Point to place a game piece at the same position as a game space in the global coordinate system.

Transforming Coordinates Without PaperJS

Before I started using PaperJS, I was using the HTML5 canvas and worked out the transformations that converts a point in one coordinate system to the coordinate system of another. The HTML5 Game Canvas uses the following matrix transformation in it’s transform function.

  HorizontalScaling, VerticalSkewing, HorizontalMoving
  HorizontalSkewing, VerticalScaling, VerticalMoving
                  0,               0,                1

  transform(HorizontalScaling, HorizontalSkewing, VerticalSkewing, VerticalScaling, HorizontalMoving, VerticalMoving)

In my game, I as using transform(1.0, 0.0, 0.3, 1.0, 0.0, 0.0). If we convert
that into a matrix and multiply by [x,y,0] we get…

  [1.0, 0.3, 0.0,  [x,
   0.0, 1.0, 0.0,   y,
   0.0, 0.0, 1.1]   0]

And if we simplify…

  [1.0, 0.3,  [x,
   0.0, 1.0]   y]

This matrix transforms points from regular space into a skewed coordinate space. This means, we can take take the global x and y position and get the new coordinates by multiplying the two matrices together.

  newX = 1x + .3y
  newY = 0x + y

If we want to think more generally, the equation is..

  newX = (hscale * x) + (vskew  * y)
  newY = (hskew  * x) + (vscale * y)

And if we want to transform a point into a rotated coordinate space, we can multiply our global point by the rotation matrix transformation.

  [ cos a, -sin a  [x,
  , sin a,  cos a]  y]

  newX =  (cos a) * x + (sin a) * y
  newY = -(sin a) * x + (cos a) * y