Using Linear Transformations In PaperJS

I was building a 2D board game and wanted to skew and rotate the game board to create a 3D effect. But after skewing the board, it wasn’t easy to place the game pieces in the altered coordinate system. I didn’t want to manually position the game pieces, because that would mean I couldn’t change the skew and rotation of the board without having to reposition the pieces.

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 was 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