EARTH 801
Computation and Visualization in the Earth Sciences

Lesson 3: Further "If" Examples

PrintPrint
//draw a circle.  Make it move across the screen following the path f(x)=sin(x)
float x=0;
float y=100;
int direction = 1;


void setup(){
  size(360,200);
}       

void draw(){
  rectMode(CENTER);
  background(200);
  fill(0);
  
  if (x<0 || x>width){
    direction = direction*-1;
  }
  
  if (direction == 1){
    ellipse(x,y+20*sin(direction*x*PI/90),20,20);
    x=x+direction;
  }
  else if (direction == -1) {
    rect(x,y+20*sin(direction*x*PI/90),20,20);
    x=x+direction;
  }
  else {
    println("I don't know what to do!");
  }
}

What it looks like running:

screenshot of sine wave animation program while it runs, see caption
Screenshot of program above running. A black circle moves from left to right tracing out a sine wave. A black square moves from right to left tracing out a sine wave.
program by E. Richardson in Processing

Behind the scenes of this program

Let me explain the thought process behind building the program above. I could go through the program from top to bottom, but I didn't really write it in that order. I kind of wrote it from the middle to the outsides. First came the idea: I want to make a shape move across the window according some functional form other than a straight line, and I want it to stay on the screen so I have to make it turn around and go backwards when it gets to the side.

We haven't really talked about this yet, but Processing can make mathematical calculations such addition, subtraction, multiplication, division, exponentiation, logarithms, and trig functions. For my example I decided to draw a shape that traces out a sine wave across the screen. I decided to make it a circle, but I could have made it any shape I wanted. So the meat of the program is going to be something like this:

ellipse(x,sin(x),diameter,diameter);
x++;

This will make an ellipse of some diameter at the initial (x,y) position given by (x, sin(x)). Then we increment x by 1 with each run through draw() and so the circle will trace out a sine wave. So then I'll write some stuff around these lines that will make a runnable program, to see what it looks like so far. I need to write  setup block that has the window dimensions in it, and I'll set the values of x and diameter while I'm at it.

float x=0;
float y=100;
int diameter=20;

void setup(){
size(200,200);
}

void draw(){
background(200);
fill(0);
ellipse(x,y+sin(x),diameter,diameter);
x++;
}

This works, except the circle doesn't go back and forth, and also it looks jittery. We can fix the back-and-forth problem by using an if test that tests where the circle is and what direction it is going and then changes the direction when we exceed the dimensions of our display window. We know how to do that. We just went over that in the previous couple of pages.

The jittery "problem" (I'm calling it a problem because I think it looks ugly. If you like it, then we're all done here!) arises because trig functions in Processing are in radians, not degrees. So the value of sin(x) is sweeping over its entire range very fast. One cycle of sine takes 360 degrees which is 2pi, a number between 6 and 6 and a half. How about if we make this shape sweep out exactly 2 cycles of sin(x) over the whole window? That would look cool. So we'll do a little math inside of where we wrote sin(x). While we're at it let's increase the amplitude so it looks nicer, too. In order to convert radians to degrees you do radians*PI/180. We want 2 cycles so let's multiply by PI/90 instead, and then let's help ourselves out by making the width of the window 360, so that each pixel is like 2 degrees of arc for our sine wave. I basically just trial-and-errored it until I got something that looked nice to me:

ellipse(x,y+20*sin(x*PI/90),diameter,diameter);

Now let's make it go back and forth. I'll do it with an if test, like this:

if (x<0 || x>width){
    direction = direction*-1;
  }

And then I'll change the line that says x++; to x = x + direction;. That way when direction is negative, x will decrement, and when direction is positive x will increment.

Great, I did it. But now that I got that to work, I realized that it would look even better if the bounceback on the window edges traced sin(-x) instead, so it looks like the ellipse grazes the side of the display window and turns in an arc instead of retracing its exact path. And while I'm at it, I'll change it so it's a square on the way back. The way I'm going to do this is to write another if test. This one in pseudo-English, will do the following:

if(I'm going from left to right){

draw a circle that goes like sin(x)

}

else if (I'm going from right to left){

draw a square that goes like sin(-x)

}

else {do some error handling in case of unexpected results}

Here's how it looks now:

//draw a circle.  Make it move across the screen following the path f(x)=sin(x)
float x=0;
float y=100;
int direction = 1;


void setup(){
  size(360,200);
}       

void draw(){
  rectMode(CENTER);
  background(200);
  fill(0);
  
  if (x<0 || x>width){
    direction = direction*-1;
  }
  
  if (direction == 1){
    ellipse(x,y+20*sin(direction*x*PI/90),20,20);
    x=x+direction;
  }
  else if (direction == -1) {
    rect(x,y+20*sin(direction*x*PI/90),20,20);
    x=x+direction;
  }
  else {
    println("I don't know what to do!");
  }
}

When I added the square, I changed rectMode to CENTER because otherwise there would be a jump when the shape changed from circle to square. Instead of doing sin(x) and sin(-x) I took advantage of the fact that direction was going from positive to negative so I could just use that variable to set which kind of sine function I wanted, and then I could write the same line both times. I could have just done an if and an else to do the switching between circle and square since direction can only be 1 or -1 in this program. However, it's not a bad idea to get in the habit of putting an else in there to mop up in case you made a mistake somewhere else in your code and there is actually an option that gets triggered that you didn't count on.