Page 1: Translate Transformations

CS559 Spring 2021 Sample Solution - Workbook 3

Written by CS559 course staff

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

In the previous workbook, we commented on the idea of a coordinate system. Recall that in Canvas and SVG, we interpret a coordinate (x,y) by starting at the top left corner of the canvas element and measuring x units (“html pixels”) to the right, and y units down.

Each box on this page has an html file and a javascript file (so the first box is 03-01-01.html and 03-01-01.js).

Box 1: The idea of transformation

Let’s start with a really simple example.

Let’s draw a simple object: a square with a triangle inside of it. Notice that this shape involves two primitives (a square and a triangle), 7 “points” (the corners of the 2 polygons), and a whole bunch of pixels (all the pixels that get colored when things get drawn). This first box is 03-01-01.html and 03-01-01.js.

The object is drawn with pretty simple code:

function drawTriSquare(context) {
    context.fillStyle = "goldenrod";
    context.fillRect(20, 20, 20, 20);
    context.fillStyle = "red";
    context.beginPath();
    context.moveTo(25, 25);
    context.lineTo(25, 35);
    context.lineTo(35, 30);
    context.fill();
}

Now, suppose that we want to move this shape around with a slider. We need to redraw it in different positions. We don’t need to move all the pixels (since we figure them out from the points), but we do need to move all the points.

Let’s look at two ways to do this. Be sure to look at the JavaScript file as well.

Box 2: Way 1, Parametric Geometry

The obvious way to do this is to just replace every position in our code with a parameter so we can move it around: (03-01-02.html and 03-01-02.js)

function drawTriSquareParameter(context, xval) {
    context.fillStyle = "goldenrod";
    context.fillRect(20 + xval, 20, 20, 20);
    context.fillStyle = "red";
    context.beginPath();
    context.moveTo(25 + xval, 25);
    context.lineTo(25 + xval, 35);
    context.lineTo(35 + xval, 30);
    context.fill();
}

Notice that we have to (carefully) change every x coordinate in the code to use a new x value. Each coordinate (x,y) is changed to (x+xval,y). This box is 03-01-02.html and 03-01-02.js. For the rectangle, only its position is a “coordinate” - we do not have to change the size.

Box 3: Way 2, Transformations

Instead, rather than moving the points, we move the coordinate system.

function drawTriSquareTransform(context, xval) {
    context.save();
    context.translate(xval, 0);
    drawTriSquare(context);
    context.restore();
}

This code deserves careful understanding, from the inside out. Look at 03-01-03.html and 03-01-03.js.

  • Notice that it calls drawTriSquare - the original drawing code from the beginning which draws the shape at the “default” position. We don’t need to change that code at all. It just draws things with the same coordinates as it used in the original example. When we switch to a different Canvas, we get a different coordinate system. We will move the coordinate system so that things end up in the right place.
  • The line that says translate(xval,0) moves the origin of the coordinate system xval units along the X axis of the coordinate system. This means that things that are drawn will be drawn into this (updated) coordinate system. The coordinate (0,0) is no longer the top left corner of the Canvas: it is xval units to the right (since the X axis is going to the right in the original coordinate system).
  • We encountered save and restore in the last Workbook 2. The coordinate system is part of the context (just like the styles we used in Workbook 2). We need to save the current context before we make changes so that we can return the context to its original state. Because translate tells how much to move the coordinate system, if we don’t put it back to where we started, each time we apply the translation, the movements will add up.

You can think about translate as applying an addition of the translation amount to each of the coordinates that are used for drawing. All drawing commands take coordinates and apply the “current translation” to it before using it. This is built in to all the drawing commands. Part of the context is to keep track of the “current translation.”

More generally, translate is a specific type of transformation. A transformation is a function that takes a point and returns a new point. So, translate(a,b) can be thought of as a function f(x,y) => (x+a,y+b). This function is applied to all coordinates when we’re drawing.

However, we can also think of translate (or any transformation) as changing the coordinate system that we use to interpret the coordinates for drawing. For various reasons (that may not be obvious until you’ve been doing graphics for a while), this is a more convenient way to think about things.

Not a box, but important…

Learning to think about transformations as changing the coordinate system is important.

When we say translate(a,b) we are saying “move the origin of the coordinate system to (a,b).” This means that when we draw point 0,0 (in the current coordinate system), we will be drawing at the position (a,b) in the old coordinate system (the coordinate system that was current when we issued the translate command).

You can think of this in terms of a sheet of graph paper on a table. When we draw, we always measure where we are relative to the piece of paper. (0,0) is the corner of the paper. The translate command moves the sheet of paper around. When we start out, the paper is at the corner of the table, so (0,0) is not only the corner of the paper, it is also the corner of the table. Issuing a translate command moves the paper, so (0,0) on the paper is no longer (0,0) on the table. The only thing that is weird about this analogy is that when we leave ink on the paper, it actually marks the table - so when I move the paper after drawing, the ink stays in place.

In class, I illustrated this with a 2D Transform demo. That demo is online. For the simple translation case (on this page), the demos are part of the simpleTrans demo set.

The other thing that is important: changing the coordinate system affects future drawing operations. Things that are already drawn stay where they were drawn.

The concept of transformations (in this case translation) changing the coordinate system is really important. Make sure you understand it now, before we go on to more complex transformations.

Box 4: Repeat after me…

Remember: translate moves the current coordinate system, relative to the current coordinate system. Understanding this idea is important. Try to understand it now, when we’re dealing with a single, simple transformation (translation). Soon, we will add more types of transformations.

Because transformations (including translate) change the current coordinate system, they combine (this is known as composition). If you move the coordinate system to the right, and then you move it to the right again, you’ve moved it twice as much to the right.

Also, because we can make many different coordinate systems, we can draw the same object multiple times. With translation, we can make it appear in different places. In the future, we can create other differences. Look at this example:

drawTriSquare(context);
context.translate(40, 0);
drawTriSquare(context);
context.translate(40, 0);
drawTriSquare(context);
context.translate(40, 0);
drawTriSquare(context);

Notice how in the code (03-01-04.html and 03-01-04.js), the translations add (since we don’t save and restore). We’ll discuss this more below (Multiple Translations).

This idea of re-using the same “object” over and over is known as instancing. Here, the object is only represented in code, since we’re using an immediate mode API. Later, we’ll see it in a retained mode API. Either way, we define it once, and re-use it over and over.

You should notice that this example uses JavaScript modules. The triangle/square drawing code is in a separate file (03-01-TriSquare.js) so we don’t need to keep repeating it. This is a good opportunity to learn about modules. Read about them in your favorite JavaScript book, or try this chapter in Understanding ES6.

Box 5: Using Transformations for Convenient Coordinates

Right from the beginning we saw the advantages of working in convenient coordinates. The fact that we program in Canvas relative to the Canvas element (instead of the coordinate system of the window) means we don’t need to worry about where on the screen the Canvas element is.

Don’t take this concept for granted: the ability to work in convenient coordinate systems is really important. It becomes useful because we can change coordinate systems easily.

Often, it is useful to define objects such that the object origin is at 0,0. This way, all coordinates in the object’s definition at relative to the object. When the object is placed into some other coordinate system, things will get moved appropriately.

So, from the original example, you might notice that the “object” of the triangle in square is positioned at 20,20. The insides had to be positioned relative to that. We had to know 25,25 was the corner of the triangle.

Instead, we have a convention that all objects are drawn with their origin at 0,0. This makes it easier to define objects, but also to use them. There is still a question of where the “origin” should be. For this example, we’ll say it’s the upper left of the square. So, we can do:

For this one, you have to go look at the code. You can find it in 03-01-05.html and 03-01-05.js. But make sure you understand why this one uses save and restore, while the previous example did not.

Multiple Translations

We aren’t limited to just one translation. After we do a first translation, we can do a second translation, and a third, and so forth.

The way to think about this: the first translation moves the initial coordinate system. The second translation moves that coordinate system (the result of the first movement). The third translation moves that coordinate system. And so forth. Using the paper analogy, each translation moves the piece of paper from wherever it is. When we draw, we draw on the piece of paper wherever it is.

We already took advantage of that in the code Box 4: Repeat after me…, where we translated, drew something, translated again, drew some more. You can think of this as keeping your pen in the same place and moving the piece of paper.

The process of combining transformations is called composition. When we apply one transformation after the other, the result is the composition of the two.

When the transformations are translation, the composition process is simple: we just add things up. Since addition is commutative (remember that word from high school algebra?), the order that we do the additions (or translations) doesn’t matter. However, this is not true for transformations in general. When we start to combine transformations we will see cases where order matters.

Box 6: Exercise 1, Fix the code

The canvas has a triangle in it. When the button is pressed, the triangle should move to the right. When the button is released, the triangle should move to its original position. Right now, each time it jumps farther to the right!

Understand why the initial code is wrong (it’s in 03-01-06.html and 03-01-06.js) and does what it does. Fix it without using negative numbers. Note: if you move the mouse outside of the button, the mouseup event is missed. You don’t have to fix that problem.

You should only change the draw function, and not use negative numbers.

SPOILER HINT: (read this only after trying to fix it yourself)
Remember that the translation is part of the drawing context. We need to worry about save and restore.

Even if you needed the hint, the grader will check that you made this work correctly.

Summary - Transformations and Coordinate Systems

Hopefully, you now have an idea of what we mean by using transformations to change the coordinate system. We only did simple changes (moving the coordinate system with translate), but we’ll see some other transformations next, and why these things are so useful.

On to page two!

Next: Scale Transformations

Page 1 Rubric (5 points total)
Points (5):
Box 03-01-06
5 pt
restore the triangle to original position when the “Jump Right” button is released