p5js week 04: arrays and transformations

(pic from https://www.trustedreviews.com/reviews/hotpoint-mwh-26321-mb)

We've got two pieces of toast stacked, with one moved and rotated a bit. We might still need some jam or something, but it's a good start for a breakfast. We can make a list of what's on that plate: piece 1 on the bottom, and then piece 2 on top rotated. That's what we want to learn how to do in JavaScript this week. We will learn about making lists of things in JavaScript. Then we want to use those lists to make cooler drawings. We also want to learn how we can move or rotate parts of our p5js drawings.

When you are done today, you should have:

  1. drawn some toast in different colors according to the colors in a list (array);
  2. moved some toast around in your drawing (using translate);
  3. rotated the earth little by little using rotate and a for-loop;
  4. used arrays, translate, and rotate to improve your drawing of a wolf, robot, or flower.

(image from: https://www.houstonpublicmedia.org/articles/arts-culture/food/2019/05/09/332670/how-did-texas-become-the-only-state-with-its-own-toast/)

The picture above reminds me of where I'm from (Texas!); it's a pan of Texas toast being buttered. You'll be on your way to representing such things after this lesson. Let's take a look at these topics so we can make such lists of toast and other things.

  1. arrays
  2. index/indices
  3. push
  4. pop
  5. concat
  6. Math.floor
  7. pick

That's a lot of topics to cover, so let's get started.

(Bread slice photo created by wayhomestudio https://www.freepik.com/photos/bread-slice )

You know now that functions are very important in JavaScript. They are one of the most important parts of programming. Another important part of programming is structuring your data so that it can be processed by functions. Structuring your data means putting it in the right shape. Arrays are one way to structure your data.

Sometimes we want to process a list of things. Arrays are like lists. We make arrays with square brackets, like this:

let listOfToast = ["regular toast", "toast that isn't ready","regular toast", "burnt toast"]

listOfToast is an array with four items. Each item has a position in the array. It might be strange for you, but the positions start from zero, not one. That means that “regular toast” is at position 0, “toast that isn't ready” at position 1, and so on. Another word meaning “position number” is “index”. We can get the item from an array by using square brackets and the index. Try running each of these lines and see what happens. Do you understand why that happens?

listOfToast[0]
listOfToast[1]
listOfToast[2]
listOfToast[3]

We can find out how many items are in an array using a technique we haven't talked about much before: dot notation. We've used it, though, for the length of strings and in function calls like Math.floor. Dot notation is related to objects, which we'll study soon. We give the name of an object, then a dot, then the name of a property of the object. Arrays are a kind of object, so we can use this technique on arrays. One of the properties of arrays is their length. When we use it, it looks like this:

listOfToast.length

Length for arrays is like length for strings. It tells us how many items there are. In a string, it tells us the number of characters. For an array, it tells us how many items are in the array.

We can then use these inside a function, like this:

function toastListProcessor (toastList) {
  console.log("The first piece of toast is " + toastList[0] + ".");
  console.log("The last piece of toast is " + toastList[(toastList.length - 1)]);
  console.log("There are " + toastList.length + " pieces of toast in the list.");
  return toastList.length
}

Then we can call the function like this:

toastListProcessor (listOfToast)

Think about this function. Why can it tell us the last piece of toast in the list?

That's great for that list of toast, but what happens when we have a new piece of toast that we need to put into the list? In such a case, we need to use the push method for arrays to add it to the end of our list.

We can add some pieces of toast like this. The argument is the item that you want to add to the array:

listOfToast.push("buttered toast")

It's important to remember that a method is a function that only works on a certain set of objects using dot notation; that means you write the array name, then a dot, then the function name. That means that this won't work:

push(listOfToast, "buttered toast") // doesn't work!

JavaScript tells you that push isn't defined; that's because it's only defined for the array class (class means type of object; we'll study this more soon).

We can add another piece in the same way:

listOfToast.push("regular toast")

Notice that this function returns the length of the array. The pieces are added to the end of the array. Take a look.

listOfToast

I'll go ahead and add one more piece that nobody wants to eat:

listOfToast.push("really burnt toast")

Take a look again:

listOfToast

Nobody wants to eat that really burnt piece of toast. Let's get rid of it! In a similar way to push, we can also remove items from the end of an array using the pop method.

listOfToast.pop()

Notice that it doesn't have to have an argument. It takes the last item from the array and returns it.

Take a look at our array of toast again:

listOfToast

Push always puts our toast at the end of the list. What if we want to put it at the beginning? The unshift method adds an item to the beginning of an array. Try this:

listOfToast.unshift("perfect toast")

OK, while you were making toast, my chef friend was busy making toast, too. She's a chef, so her toast is perfect every time. Take a look:

let chefToast = ["perfect toast","perfect toast","perfect toast","perfect toast","almost perfect toast"]

Well, it's close to perfect every time ;) Take a look at her list:

chefToast

We can join (connect) two arrays using the concat method. Notice that you need to assign the result of this method to a new variable. That's different from push, pop, and slice; these change the existing array. The concat method gives us a new array.

newListOfToast = listOfToast.concat(chefToast)

Take a look at our new list:

newListOfToast

You'll notice that our old lists are still the same as before:

listOfToast
chefToast

(picture from https://commons.wikimedia.org/wiki/File:LA_LEY_DE_MURPHY.jpg)

Do you remember how we used Math.floor with Math.random to get a random number without a lot of numbers after the decimal? By the way, a number without anything after the decimal is called an integer. In JavaScript (and programming and computers), we call the numbers with lots of numbers after the decimal floating point numbers.

Anyway, it was like this.

Math.floor(Math.random() * 6)

That's great if we want a random number between 0 and 5, but what if we want to change our random number each time? Let's make a function!

function randomRange (max) {
  return Math.floor(max * Math.random()))
}

Actually, this only works for numbers between 0 and the max. What if we want to get 1 to 6? We could just add 1.

function randomRange (max) {
  return 1 + Math.floor(max * Math.random()))
}

That's not as useful though as a function that lets us choose any minimum and maximum number. Remember that this one doesn't include the maximum number. Since it returns an integer, let's change the name:

function randomInteger (min, max) {
  return Math.floor(min + ((max-min) * Math.random()))
}

As a puzzle, you can think about how this function works.

That means that if we one of a normal pair of dice, we need to do this:

function regularDice () {
  return 1 + randomInteger(0,6)
}

Try it!

regularDice () 

Now it's time to serve the toast. Since some of our toast is better than others, maybe it's most fair if people get the toast randomly.

We can start doing that by being able to get a random item from an array. Let's use the randomInteger function above to make another useful function. We can use inputArray.length because our randomInteger doesn't include the maximum number.

function pick (inputArray) {
  return inputArray[randomInteger(0,inputArray.length)]
}

Now we can call it to get random pieces of toast!

pick (newListOfToast)

Now that we can make lists of toast, let's move some around and rotate them a bit on the plate. That might be fun, or it might make a mess, but at least we'll learn some useful tools!

  1. translate
  2. rotate
  3. resetMatrix
  4. adding rotate to your for-loop to rotate your toast each time
  5. using pick to choose random colors for our toast

We know how to choose the position of a shape that we draw by giving arguments for the x position and y position. There's another way to draw in different places, though. That way is using the function called translate. You can think of that function like moving the paper where you are drawing. Normally, the coordinates (0,0) are in the top left corner of the paper. When we use translate, we are changing the location of (0,0), making a new point (0,0). Watch what happens in this simple example:

Even though the x position and y position arguments are the same in each call to the rect function, the second rect is in a different place! That's because the call to translate between them changes the position of (0,0) to (200,200), making it the new (0,0). Another word for (0,0) is origin. We can also move the origin in the other direct using translate with negative numbers. Look at this:

We have to use negative numbers because we want to move the origin up and left. Positive numbers will move the origin right or down. The numbers you use as arguments for translate are not the new coordinates; they are ADDED to the previous origin coordinates.

There's much more about translate on this page in the p5js reference:

https://p5js.org/reference/#/p5/translate

In the same way, we can use the rotate function to rotate rects…

One thing that is really important to remember is that in p5js, there are two different types of numbers that can be used to rotate things: degrees and radians. Probably in school you have learned about degrees but not radians. That's OK; we just need to be sure to tell p5js that we are using degrees. To do that, you should make sure that your setup function has this inside:

angleMode(DEGREES);

Like translate, don't think about rotate as rotating just the shape you are drawing. Rotate is rotating the whole canvas where you are drawing. That means that after you rotate, everything will be drawn in a new way until you change the rotation again. You can change it back to the way that it was before, or you can change it to a new way, but everything you draw after rotate is going to be rotated.

To understand that, look at this example:

That's weird! Why are they moving so much? It's because rotate means rotating the canvas, not just the shape. Usually when we think of rotating, especially if you have used some other graphic design software, you think of rotating around the center of the object. If we want to do that in p5js, we have to move the origin to the center of the shape.

It's also important that you get the order correct. Changing the order of translate and rotate will give you different results. Look at this example now. All the rects are rotated, but there's only one call to rotate. Why?

That's because we rotated the canvas, so everything that is drawn after that is rotated.

If we want the rects to extend diagonally down and right but also be rotated, we have to do different calculations about the x and y position.

One more thing that may help you when rotating rects is changing the rect mode. There's a good explanation of rect mode on in the p5js reference:

https://p5js.org/reference/#/p5/rectMode

Using these together, we can get something like three rotated rects in a diagonal line like this:

I'll admit that it's not super easy to understand or use, but with practice you'll get used to it.

There's much more on rotate on this page in the p5js reference:

https://p5js.org/reference/#/p5/rotate

One reason it's hard to use translate and rotate is that the changes accumulate. Sometimes this is useful, but sometimes it's confusing. If you're feeling confused, you can call resetMatrix to change everything back to the way it was when the canvas was first created.

resetMatrix()

See how this can be used to make the same diagonal line of rects.

There's more on resetMatrix on the p5js reference here: https://p5js.org/reference/#/p5/resetMatrix

Now, let's use them together inside a for-loop to get something interesting!

Notice that there's one function which draws a random rect, called transformedRect.

Then we call that function inside the for-loop. You can control how many rects appear by changing the number in the second part of the conditions of the for-loop.

Be sure to run this several times; it's different every time.

You can also do cool things like this:

Now, try to use this to rotate the earths in your earth drawing!

Above, we looked at arrays of toast; now let's use arrays of colors to make our p5js drawings more interesting.

We can use color words in p5js to fill our shapes. We just have to give the fill function a string with the color name.

You can learn more about the fill function here on the p5js reference:

https://p5js.org/reference/#/p5/fill

A full list of the color names that we can use is here. There are 147 of them:

https://www.december.com/html/spec/colorsvg.html

Let's try this one:

let myColors = ['black','darkorange','cyan'];
pick(myColors)

Check this out:

Notice that we put our useful functions at the top of the document so that we can use them in our drawing.

OK, here's all of the parts put together.

We just add a part to choose colors using pick to the version from above.

Be sure to run this several times; it's different every time.

Now, can you use these things to make a better drawing of rainbow toast?

Now that you can make arrays, translate things, and rotate them, let's apply that to your drawings of robots, wolves, or flowers that you have been working on for the past few weeks. You could try:

  1. putting some arguments for your functions in an array and then calling the function within a for-loop while retrieving the arguments from the array
  2. translating and/or rotating some of the shapes that you are drawing
  3. make an array of colors, and then use pick to choose colors for drawing some of the shapes in your drawing

When you've made some progress, please share your screenshots, and put your code on your wiki page!

  • p5js-week-04.txt
  • Last modified: 6 weeks ago
  • by renick