THU | 2D transformations
- October 3, 2024
- 9:15–12:00
- Room 2420 (Marsio)
Inspiration #
Summary as an AI Podcast #
I am trying out something new this time. I used NotebookLM to generate a podcast based on the course materials on this page, YouTube video from the Coding Train, p5.js reference, and the Wikipedia article on Transformation Matrix.
Let me know if you feel that it is helpful and I can keep on making more of them from the other topics. Or better yet, learn how to use NotebookLM yourself.
2D Transformations #
This week is going to be about finding ways to structure, simplify and compartmentalize your code. We will learn about transformations that allow you to rotate, move and scale things that you draw. Additionally, we will learn the basics of using functions.
Let’s take an example sketch, where we would like to draw a simple face that is centerd on the canvas. We could use some variables to store the x and y coordinates of the center of the screen and use these as a reference point for drawing the shapes. I am calling these variables offsetX
and offsetY
.
I can even make this interctive by connecting the mouseX
and mouseY
coordinates to these offset values.
You will notice that there is a lot of repetition of these variable names offsetX
and offsetY
. As I have said before, whenever you see your code repeating the same things over and over again, there probably is a better way to do it. There is. It’s called
translate().
translate() #
The translate() transformation essentially moves anything you draw after calling it by the amount you define in the parameters. Here is an example based on what we did earlier.
Note how the code looks much simpler. We call the translate() only once and everything we draw after that inside the draw function uses this location as the reference point.
That is the key to understanding how translate()
works. It doesn’t really move the shapes themselves. It moves the reference point for all the drawing functions away from the 0,0 corner in the top-left of the canvas. So for the circle() 0,0 is now in the middle of the screen (or whateve you define as the coordinates). We can do the same thing with simple interaction.
Let’s look at how this same idea would work with images. You can dowload the image below to follow these examples.
rotate() #
Here is an example with the grid image.
scale() #
We can also control the size of things we draw using the scale() transformation.
Please note! The order of transformation operations matters. You will get very different results depending on the order you translate, rotate and scale.
Transformations stack! #
Please note! All of the transformations that you do in one frame (one loop of the draw() function) keep on adding on top of each other. Sometimes this could be useful, sometimes it creates lots of confusion and problems.
A useful feature of this stacking would be using the transformation in a for loop. We could say that a shape should rotate 10 degrees more each time that the for loop repeats.
If we want to draw anything above these shapes–let’s say some text connected to the mouse coordinates–we end up with the text also offset and rotated.
We could try something where we undo all the translations and rotations after drawing the rectangles by repeating the rotations and translate in the negative direction.
// undo rotations
for(let i = 0; i < num; i++){
rotate(radians(-10));
}
// undo translate
translate(-offsetX,-offsetY);
Don’t do this, we will learn a better way very soon. It’s good to still understand that you can do this.
For this simple example, it’s not such a complicated thing to do. But what if we want to have lots of different shapes, images or text moving, rotating or scaling independently of each other? Keeping track of all this would get really complicated. Surely there must be some way to get around this issue? The solution is to use push() and pop().
push() and pop() #
These two handy functions are used to save and restore the transformation matrix back to a certain state.
- push() tells your program to save whatever the state of the transformation is at that specific point in the code.
- pop() tells your program to restore the state of the transformation matrix to the previously saved state. Meaning the last time you called push().
You could think of them like layers in Photoshop. Each section of your code that is between the push() and pop() commands is sort of like its own layer.
This example does the following:
- Draw the grid in the background without any transformations
- Draw the green rectangle where the mouse is and rotate it constantly clockwise
- Draw the red rectangle in the middle of the screen and rotate it constantly anti-clockwise
- Note that the transformations reset back to the origin when the next frame starts
References and more information #
I have been talking about the transformation matrix. What is that? Are you expected to choose between a red or blue pill? Is reality just a simulation? Maybe, but none of this has to do with the movie.
You can learn more about transformation matrices on Wikipedia.
Does everything on that page look completely alien to you? If so, you can keep on living in the simulation and just ignore all of the technical details. For our purposes this level of understanding that we covered on this page is enough. You do not have to necessarily know all the technical details. That is why we have computers. They can do all the complex technical stuff and we can focus on making something interesting with them.