JavaScript Canvas Transformations
Written by Ian Elliot   
Monday, 02 March 2020
Article Index
JavaScript Canvas Transformations
Active Transformations

 

If you want to draw the second rectangle at 200,50 in the original co‑ordinate system you have to use a more complicated algorithm. First move the origin of the co-ordinate system to 200,50, rotate 45 degrees, then draw the second rectangle at 0,0.

ctx.setTransform(1,0,0,1,0,0);
ctx.fillRect (0, 0, 50, 50);
ctx.translate(200,50);
ctx.rotate(Math.PI/4);
ctx.fillRect (0, 0, 50, 50);

Notice that the second rectangle is rotated about its top left corner.

rotate2

Of course as you have changed the co-ordinate system this affects anything you subsequently draw. The solution is to return the co-ordinate system back to what it was before you changed it.

In this case it is simple as we are using the default co-ordinate system and we can set the default co-ordinate system simply by setting the transformation matrix back to the identity using:

ctx.setTransform(1,0,0,1,0,0);

If you aren’t using the default co-ordinate system then you can save and restore the current transform matrix using the save and restore functions, see later.

There is a currentTransform property which can be read and written to access the current transform matrix. The only problem is that this isn’t well supported at the time of writing and is best avoided.

largecover360

A Logical Approach to Transforms

Transformations are key to making drawing on Canvas easy, but if you think about things in the wrong way it is very easy to make mistakes.

There are two ways to think about transformations – active and passive – and humans tend to prefer thinking about active transformations. An active transformation is one that actually changes what you have already drawn. Unfortunately, Canvas transformations are passive and change the co‑ordinate system so that what is drawn next is changed and what is already drawn is unchanged.

As already mentioned, in general we find it easier to imagine what happens with active transformations. For example, one approach to working with transformations is to draw everything centered on the origin, and then translate, scale and rotate it to its final position. This is a good approach, but if you think of it as an active transformation then it doesn’t work with Canvas.

For example, to draw the rectangle:

ctx.fillRect (200,350,160,160);

rotated through 45 degrees you would first draw a unit square centered on the origin, then you would then scale it to its desired size, rotate it about the origin and finally you would move it to its correct location:

ctx.fillRect (-0.5, -0.5, 1, 1);
ctx.scale(160,160);
ctx.rotate(Math.PI/4);
ctx.translate(200,350);

These are imagined to be active transformations and of course they don’t work as imagined. The reason is that we have been transforming the object: draw a square, scale the square, rotate it and move it to the desired location. However, Canvas transformations don't transform objects but the co‑ordinate system. You can immediately see that this means you should draw the square last after you have performed all of the transformations.

Indeed this is the rule:

 

  • Do everything you would have done to the geometric shape using active transformations in the reverse order when changing the co‑ordinate system. 

 

 

So the correct transformation sequence is:

ctx.translate(200,350);
ctx.rotate(Math.PI/4);
ctx.scale(160,160);
ctx.fillRect (-0.5, -0.5, 1, 1);

Notice that when you do a scale this applies to any strokeWidth you may set, i.e. double the scale and a strokeWidth of 1 becomes an effective strokeWidth of 2. To set it you need to use an explicit setting of lineWidth and you also need to remember that you are working in the current co-ordinate system. For example to set a line width of 1 pixel in the previous example after a scaling of 160 you would need to use:

ctx.lineWidth=1/160;

You can always work out the transformation sequence you need by considering the graphical object, working out the transforms needed to change it to what you want and applying them in the reverse order. This leads to the approach where every object is drawn centered on the origin at unit size and in a “normal” orientation. The object is then transformed into the size, location and orientation you need.

Some programmers take to this idea and think it is the best and only way to do logical systematic graphics, some adopt it a little bit, and others draw things where they are needed in the size and orientation needed. 

Included in book but not in this extract

  • Setting Your Own Co-ordinates
  • Plotting A Graph
  • Stack of States
  • Active Transformations and State

Summary

  • Transformations include rotation, scaling and translation.

  • You can write all three as a simple matrix if you use homogeneous co-ordinates (x,y,1)

  • You can set the transform matrix directly or use one of the utility methods to set scaling, rotation and translation.

  • The order in which you apply transformations makes a difference.

  • You can think of transformations as actively moving something or just changing the co-ordinates. Canvas transforms change the co-ordinate system.

  • If you want to think “actively” then think of the transformations you want to perform on a shape and then apply them in the reverse order before drawing the shape.

  • One approach to organizing graphics is to draw everything centered on the origin and at unit scale and then use transformations to size, rotation and position where you really want to draw the shape.

  • You can change the co-ordinate system in use to anything that suits the current drawing task.

  • You can save the drawing state before changing the co-ordinate system so that it can be restored.

  • The drawing state includes the current transformation matrix, clipping region and all drawing attributes.

 

Now available as a paperback or ebook from Amazon.

JavaScript Bitmap Graphics
With Canvas

largecover360

 

Contents

  1. JavaScript Graphics
  2. Getting Started With Canvas
  3. Drawing Paths
      Extract: Basic Paths
      Extract: SVG Paths
      Extract: Bezier Curves
  4. Stroke and Fill
      Extract: Stroke Properties 
      Extract: Fill and Holes
      Extract: Gradient & Pattern Fills
  5. Transformations
      Extract: Transformations
      Extract: Custom Coordinates 
      Extract  Graphics State
  6. Text
      Extract: Text, Typography & SVG 
      Extract: Unicode
  7. Clipping, Compositing and Effects
      Extract: Clipping & Basic Compositing
  8. Generating Bitmaps
      Extract:  Introduction To Bitmaps
      Extract :  Animation 
  9. WebWorkers & OffscreenCanvas
      Extract: Web Workers
      Extract: OffscreenCanvas
  10. Bit Manipulation In JavaScript
      Extract: Bit Manipulation
  11. Typed Arrays
      Extract: Typed Arrays **NEW!
  12. Files, blobs, URLs & Fetch
      Extract: Blobs & Files
      Extract: Read/Writing Local Files
  13. Image Processing
      Extract: ImageData
      Extract:The Filter API
  14. 3D WebGL
      Extract: WebGL 3D
  15. 2D WebGL
    Extract: WebGL Convolutions

<ASIN:1871962625>

<ASIN:B07XJQDS4Z>

<ASIN:1871962579>

<ASIN:1871962560>

<ASIN:1871962501>

<ASIN:1871962528>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

raspberry pi books

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 02 March 2020 )