Stardust

Under Construction

How would you draw Earth on a computer?

p5.js Web Editor

Domain and Range

Have you ever looked up at a sky full of stars? If you can find a spot free of light pollution you may even be able to observe our galaxy in all its splendor. The sight is truly awesome and it raises some big questions.

Are we alone in the cosmos? Will we ever travel to another star system? And just how full is the sky, anyway? Let’s start with that last one.

Discrete

You’ve probably drawn countless graphs in math classes over the years. About the simplest thing you could draw is a single, solitary point like the one below.

How much of the graph does the point take up? Certainly not much. To be precise, the point only takes up, well, a single point. Looking from left to right, it appears the -values, or domain, of the graph is just . The -values, or range, is just .

Let’s expand the graph a little.

It appears our domain and range have both grown! The domain is now and the range is . Graphs that are comprised of collections, or sets, of individual points are known as discrete graphs.

What happens when we connect the dots?

Continuous

The graph below connects the points and with a straight line.

If you zoom in and move from left to right, you would find that the graph is filled in somewhere at every -value between and . You could do the same from top to bottom. When a graph’s domain and range take on every value in a given interval, we say it is continuous. In this case, the domain is and the range is .

Constellation

The sky is full of more stars than you could hope to count. Take a walk outside tonight and try to spot a constellation.

How would you describe the constellation’s position? You might start by saying, “Well, it’s in the sky over there, and I know the stars are really in outer space.”

A mathematician would look at the situation and say something like, “I know the photons from those stars just arrived at my eye after traveling for a long time. From my perspective, the stars appear to be arranged in such-and-such shape. I bet I could describe each star’s position precisely—let me get a pencil.”

The sketch below gives an example of drawing the Big Dipper. The statement let x = 5; declares that you’d like to use a variable named x and assign it the value .

Protip: Good variable names can make it much easier to understand what’s happening in your code.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  stroke(255);

  let alkaidX = 100;
  let alkaidY = 100;
  let mizarX = 150;
  let mizarY = 110;
  let aliothX = 170;
  let aliothY = 130;
  let megrezX = 190;
  let megrezY = 150;
  let phecdaX = 190;
  let phecdaY = 180;
  let merakX = 240;
  let merakY = 190;
  let dubheX = 260;
  let dubheY = 160;

  strokeWeight(5);
  point(alkaidX, alkaidY);
  point(mizarX, mizarY);
  point(aliothX, aliothY);
  point(megrezX, megrezY);
  point(phecdaX, phecdaY);
  point(merakX, merakY);
  point(dubheX, dubheY);

  strokeWeight(1);
  // look up "line" in the p5.js reference
  line(alkaidX, alkaidY, mizarX, mizarY);
  line(mizarX, mizarY, aliothX, aliothY);
  line(aliothX, aliothY, megrezX, megrezY);
  line(megrezX, megrezY, phecdaX, phecdaY);
  line(phecdaX, phecdaY, merakX, merakY);
  line(merakX, merakY, dubheX, dubheY);
}

Map out your own constellation on paper then in code. Research the proper names of the stars or come up with new ones.

Domain and Range Review

What are the domain and range of the stars in your constellation? How about when you connect them?

Variables

Take another walk outside tomorrow afternoon. Depending on your location and the weather, you may enjoy the sunshine on your face. Close your eyes and compare the feeling with the previous evening.

With some exceptions, you’d probably notice it’s cooler at night than in the daytime. Our sensations of heat and cold depend primarily on the temperature of the air, how much radiation we absorb from the sun, and the wind.

But why male models?

— Derek Zoolander

A mathematician would look at the situation and develop a general description, or model. She would say something like the following: “My sensation of heat changes and it depends on temperature, radiation, and wind.”

Mathematics

Sensations and other quantities that change are known as variables. You may be acquainted with variables like and from solving equations or graphing points and lines. Try to think of variables as containers, or buckets, or boxes—as you prefer. The big idea is that they hold values that can change, or vary.

Math students often have trouble distinguishing between variables and the values they represent. may represent the number at a particular point along the graph of a line, so it’s easy to conclude (incorrectly) that , now and always.

In our model of sensation, heat is dependent upon the values of three variables that are independent, or taken as an input to the model. A mathematician would represent the heat model as a function using notation like .

Computation

At some point, you may have been asked to read a brief scenario and make up, or formulate, a model.

Perhaps you were in science class and formulated a model like describing an object’s displacement with respect to velocity and time. If you ran in a straight line at meters per second for seconds, you would find yourself meters away from your starting position.

You could define the corresponding function in JavaScript like so.

function d(v, t) {
  return v*t;
}

v and t are part of our function definition, called parameters. If we called d(5, 10) somewhere in the sketch, the argument would be assigned to the parameter v and would be assigned to the parameter t.

Parameters don’t change; the arguments we assign to them do.

The following sketch of a meteor declares the variable velocity and assigns it the value . This statement is like asking JavaScript to create a new box labelled “velocity” and putting a in it to start.

time works similarly but with one big difference.

Note that time is increased, or incremented, by when draw() executes. You could imagine this statement as taking the value held in the “time” box, adding to it, and putting the new value back in the box.

The print() function is used to keep a running log of the current value of time.

Protip: Printing values is a useful technique for debugging.

// Variables you declare up here can be used
// throughout the sketch.
let velocity = 5;
let time = 0;

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0, 25);
  stroke(255);
  fill(255);

  print(time);

  let x = d(velocity, time);
  let y = 200;
  circle(x, y, 10);
  
  time = time + 1;
}

function d(v, t) {
  return v*t;
}

Look up fill() and circle() in the p5.js reference.

The variables velocity and time were passed as arguments to d() where they were assigned to the parameters v and t, respectively.

Variables Review

What are variables? Describe how they relate to arguments and parameters.

Transformation

Drawing collections of objects can get tricky fast. The sketch below draws a simple representation of Orion’s Belt.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  stroke(255);
  strokeWeight(5);
  // alnitak
  point(50, 100);
  // alnilam
  point(100, 75);
  // mintaka
  point(150, 50);
}

What if you wanted to shift everything to the left a little? Or how about representing the view from another location on Earth? It turns out realignments, or transformations, like these are easily handled by playing a little with our coordinate system.

Translate

How would you move the constellation pixels to the right?

One way to go about it would be to comb through all your calls to point(), adding to the first argument.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  stroke(255);
  strokeWeight(5);
  // alnitak
  point(150, 100);
  // alnilam
  point(200, 75);
  // mintaka
  point(250, 50);
}

What if you wanted to move the constellation a smidge to the left? Or a little down?

Each adjustment would require you to update all your calls to point() again. As you can imagine, this would quickly become a hassle for sketches composed of many objects.

Shifting, or translating, is a transformation simplifies this sort of work.

Add a call to translate() with two arguments for the amount left/right and up/down you’d like to shift your constellation.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  stroke(255);
  strokeWeight(5);
  translate(75, 50);
  // alnitak
  point(50, 100);
  // alnilam
  point(100, 75);
  // mintaka
  point(150, 50);
}

Move your call to translate() to the end of draw(). What happened?

One way to think about transformations is as an adjustment to your coordinate system. Calling translate(1, 2) effectively shifts the origin from the top-left corner to . Anything drawn afterward will appear relative to the new origin.

Translations really start to shine when combined with other transformations.

Rotate

The Moon is Earth’s natural satellite and a source of inspiration for numerous creative works. Two notable examples are Nick Drake’s album Pink Moon and the Lua programming language.

Let’s see if we can put the Moon in orbit around Earth. This sketch is a little more ambitious, so it will help to lay out some initial ideas.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
  // drawEarth();
  // drawMoon();
}

Breaking a big problem down into manageable pieces is known as decomposition in computer science. The practice will serve you well in many walks of life.

To keep things simple, draw Earth as a big, blue circle in the middle of your canvas. Remove the // when you’re ready to go.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  drawEarth();
  // drawMoon();
}

function drawEarth() {
  fill(0, 0, 255);
  circle(200, 200, 200);
}

You could make the drawEarth() function a bit more flexible by adding parameters for Earth’s position. Let’s make use of translate() as well.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  drawEarth(200, 200);
  // drawMoon();
}

function drawEarth(x, y) {
  fill(0, 0, 255);
  translate(x, y);
  circle(0, 0, 200);
}

Pass mouseX and mouseY as arguments to drawEarth(). You’ll have the whole world in your hands.

Not bad. Now we’ll draw the Moon as another circle some distance away from Earth.

Transformations accumulate, so let’s take advantage of the fact that the origin is now at Earth’s center.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  drawEarth(200, 200);
  drawMoon(50);
}

function drawEarth(x, y) {
  fill(0, 0, 255);
  translate(x, y);
  circle(0, 0, 200);
}

function drawMoon(radius) {
  fill(200);
  translate(radius, 0);
  circle(0, 0, 50);
}

Switch the order in which you call drawEarth() and drawMoon(). What happened?

The sketch works for the moment, but it’s worth getting ahead of some common sources of error. Transformations accumulate by default, but we have the ability to isolate groups of related transformations.

It’s easy to rewrite drawEarth() and drawMoon() so that their transformations no longer impact one another—just call push() before the first transformation and pop() after the last piece of the object.

We’ll rewrite drawMoon() so that it’s position is based on two transformations, first to Earth’s center and then to its place in orbit.

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(0);
  drawEarth(200, 200);
  drawMoon(200, 200, 150);
}

function drawEarth(x, y) {
  push();
  fill(0, 0, 255);
  translate(x, y);
  circle(0, 0, 200);
  pop();
}

function drawMoon(centerX, centerY, radius) {
  push();
  fill(200);
  translate(centerX, centerY);
  translate(radius, 0);
  circle(0, 0, 50);
  pop();
}

Switch the order in which you call drawEarth() and drawMoon(). What happened this time?

You can think of rotations as an adjustment to your coordinate system, just like translations. Rotating your coordinate system degrees clockwise would leave it looking something like the following.

The -axis label wound up off the canvas after rotating. Let’s try to get it back.

You can compose a sequence of transformations to generate many interesting effects. Here’s the same degree clockwise rotation followed by a translation along the (rotated) positive -axis.

p5.js measures degrees in radians by default. They can be simpler to work with once you’ve gotten the hang of them, but we’ll stick to degrees for the time being.

The Moon’s place in orbit will change, and when things change, it’s probably time to use variables.

The following sketch adds the variable angle and uses it to control rotation. Note the call to angleMode() in setup().

let angle = 0;

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
}

function draw() {
  background(0);
  drawEarth(200, 200);
  drawMoon(200, 200, 150, angle);
  angle = angle + 0.25;
}

function drawEarth(x, y) {
  push();
  fill(0, 0, 255);
  translate(x, y);
  circle(0, 0, 200);
  pop();
}

function drawMoon(centerX, centerY, radius, angle) {
  push();
  fill(200);
  translate(centerX, centerY);
  rotate(angle);
  translate(radius, 0);
  circle(0, 0, 50);
  pop();
}

Rearrange the transformations in drawMoon() to see what happens. Breaking programs can provide great insight into how they work.

Scale

Earth and the Moon look good, but the sky is a little too empty, even by space standards. Let’s add our constellation to help fill out the sky.

let angle = 0;

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
}

function draw() {
  background(0);
  drawConstellation(20, 50);
  drawEarth(200, 200);
  drawMoon(200, 200, 150, angle);
  angle = angle + 0.25;
}

function drawConstellation(x, y) {
  push();
  translate(x, y);
  stroke(255);
  strokeWeight(5);
  // alnitak
  point(50, 100);
  // alnilam
  point(100, 75);
  // mintaka
  point(150, 50);
  pop();
}

function drawEarth(x, y) {
  push();
  translate(x, y);
  fill(0, 0, 255);
  circle(0, 0, 200);
  pop();
}

function drawMoon(centerX, centerY, radius, angle) {
  push();
  translate(centerX, centerY);
  rotate(angle);
  translate(radius, 0);
  fill(200);
  circle(0, 0, 50);
  pop();
}

Write your own drawConstellation() function with parameters x and y.

The stars are arranged just fine but they’re a little too big. We can adjust the overall size, or scale, of objects using the scale() function.

let angle = 0;

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES);
}

function draw() {
  background(0);
  drawConstellation(20, 50, 0.5);
  drawEarth(200, 200);
  drawMoon(200, 200, 150, angle);
  angle = angle + 0.25;
}

function drawConstellation(x, y, theScale) {
  push();
  translate(x, y);
  scale(theScale);
  stroke(255);
  strokeWeight(5);
  // alnitak
  point(50, 100);
  // alnilam
  point(100, 75);
  // mintaka
  point(150, 50);
  pop();
}

function drawEarth(x, y) {
  push();
  translate(x, y);
  fill(0, 0, 255);
  circle(0, 0, 200);
  pop();
}

function drawMoon(centerX, centerY, radius, angle) {
  push();
  translate(centerX, centerY);
  rotate(angle);
  translate(radius, 0);
  fill(200);
  circle(0, 0, 50);
  pop();
}

Change the order you call functions in draw() to see what happens.

Like translate() and rotate(), you can think of scale() visually as a stretch or compression of your coordinate system. The graph paper below was compressed by a factor of in the -direction and stretched by a factor of in the -direction.

Transformation Review

How do translate(), rotate(), and scale() work?

Project Ideas

Interactivity Rotate the Moon using your mouse.

Analysis Use the print() function to log your constellation’s domain and range.

Animation Fill out the surface terrains of Earth and the Moon using 2D Primitives.


This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.