Dots and Lines

Lets write a program that draws a dot wherever the user clicks on the screen. Between each dot a line is drawn so that we can see the path of the clicks.

Note

Drawing connected points and lines turns out to be fairly common, and so Processing provides some built-in functions to do it for you: see the documentation for beginShape if you are curious.

Designing Our Program

Lets spend a moment thinking about how we want our program to work. When the user clicks the screen the first time, a red dot appears at the click point. Then, for the every click after that, a red dot is drawn at the click point, along with a straight line from its center to the center of the previous point.

We’ll represent the dots as follows:

class Sprite {
  float x;
  float y;
  float dx;
  float dy;

  void update() {
    x += dx;
    y += dy;
  }
}

class Dot extends Sprite {
  void render() {
    fill(255, 0, 0);
    noStroke();
    ellipse(x, y, 10, 10);
  }
} // class Dot

With the Point class in hand, lets now write a high-level sketch of the entire program:

ArrayList<Dot> dots;  // dots is initially null

void setup() {
  size(500, 500);
  smooth();

  dots = new ArrayList<Dot>(); // make an empty ArrayList of dots
}

void draw() {
  background(255);

  // draw all the lines

  // draw all the points (on top of the lines)
}

void mousePressed() {
   // add (mouseX, mouseY) to points
}

By using comments like this, we can see the overall structure of the program without yet needing to worry about the low-level details.

Filling in the Details

Now lets replace the comments with working code. We’ll start with mousePressed:

void mousePressed() {
  Dot d = new Dot();   // create a new dot
  d.x = mouseX;        // set its center to be
  d.y = mouseY;        // at (mouseX, mouseY)
  dots.add(d);         // add it to the dots ArrayList
}

Next, lets write the code for drawing all the points. This is not too hard:

void draw() {
  background(255);

  // draw the lines

  // draw the dots
  for (Dot d : dots) {   // for each Dot d in dots
    d.render();          // draw d
    d.update();
  }
}

Drawing the Lines with a For-each Loop

Next, we need to draw the lines between the dots. To draw a line, we will need two dots, one for each endpoint of the line. One day to do it is like this:

void draw() {
  background(255);

  // draw the lines
  stroke(0);
  Dot prev = null;
  for (Dot d : dots) {
    if (prev != null) {
      line(d.x, d.y, prev.x, prev.y);
    }
    prev = d;
  }

  // draw the dots
  for (Dot d : dots) {
    d.render();
  }
}

The variable prev keeps track of the dot that comes just before the dot d in the for-loop. When the loop starts, prev is set to the special value null because there is no previous dot. Inside the loop, we need an if-statement to make sure that prev is non-null before drawing a line.

Recall that null is a special value that is used with object variables to indicate they are not referring to any object (yet). That makes sense here: there’s is no point before the first one.

Drawing the Lines with a While Loop

An alternative way to draw the lines is to use a while-loop like this:

// draw the lines
if (dots.size() > 1) {
  int i = 1; // i starts at 1 because dots.get(i - 1) is an error when i == 0
  while (i < dots.size()) {
    Dot a = dots.get(i - 1);
    Dot b = dots.get(i);
    stroke(0);
    line(a.x, a.y, b.x, b.y);
    i += 1;
  }
}

The idea here is to make the index variable i take on the values 1, 2, 3, ..., n - 1 (where n is dots.size()), and then to draw a line from the Dot at location i - 1 to the Dot at location i.

Bouncing Points

As a fun example, lets modify the above program so that the points fall down and bounce on the bottom of the screen. While not terribly practical, it is a nice-looking effect that might give you ideas for other kinds of animation.

We want this new program to work just like the previous one, except that when you press any key suddenly gravity is turned on and the dots all begin to fall downwards.

We saw in earlier notes how to make a ball fall down and bounce on the screen. The idea is add two new variables: gravity, to control how fast a dot falls downwards, and elasticity to control how high it bounces:

class Dot extends Sprite {
  float gravity;
  float elasticity;

  void render() {
    fill(255, 0, 0);
    noStroke();
    ellipse(x, y, 10, 10);
  }

  void update() {
    x += dx;
    y += dy;

    dy += gravity;
    if (y >= height) {  // check if ball has hit the bottom edge
      dy = -(dy * elasticity);
      y = height - 1;
    }
  }
} // class Dot

When the mouse is clicked, we need to set the dots gravity and elasticity:

void mousePressed() {
  Dot d = new Dot();
  d.x = mouseX;
  d.y = mouseY;
  d.gravity = 0.1;
  d.elasticity = 0.8;
  dots.add(d);
}

Now when you run the program, the dots bounce on the bottom of the screen.

Programming Questions

  1. Modify the bouncing dots program so that the dots don’t start moving until the user presses a key. If the user then presses a key while the dots are moving, they should immediately stop where they are and not move again until the user presses any key.

Final Program (no bouncing)

class Sprite {
  float x;
  float y;
  float dx;
  float dy;

  void update() {
    x += dx;
    y += dy;
  }
}

class Dot extends Sprite {
  void render() {
    fill(255, 0, 0);
    noStroke();
    ellipse(x, y, 10, 10);
  }
} // class Dot

ArrayList<Dot> dots;  // dots is initially null

void setup() {
  size(500, 500);
  smooth();

  dots = new ArrayList<Dot>(); // make an empty ArrayList of dots
}

void draw() {
  background(255);

  // draw the lines
  Dot prev = null;
  for (Dot d : dots) {
    if (prev != null) {
      stroke(0);
      line(d.x, d.y, prev.x, prev.y);
    }
    prev = d;
  }

  // draw the dots
  for (Dot d : dots) {
    d.render();
  }
}

void mousePressed() {
  Dot d = new Dot();
  d.x = mouseX;
  d.y = mouseY;
  dots.add(d);
}

Final Program (bouncing)

class Sprite {
  float x;
  float y;
  float dx;
  float dy;

  void update() {
    x += dx;
    y += dy;
  }
}

class Dot extends Sprite {
  float gravity;
  float elasticity;

  void render() {
    fill(255, 0, 0);
    noStroke();
    ellipse(x, y, 10, 10);
  }

  void update() {
    x += dx;
    y += dy;

    dy += gravity;
    if (y >= height) {  // check if ball has hit the bottom edge
      dy = -(dy * elasticity);
      y = height - 1;
    }
  }
} // class Dot

ArrayList<Dot> dots;  // dots is initially null


void setup() {
  size(500, 500);
  smooth();

  dots = new ArrayList<Dot>(); // make an empty ArrayList of dots
}

void draw() {
  background(255);

  // draw the lines
  Dot prev = null;
  for (Dot d : dots) {
    if (prev != null) {
      stroke(0);
      line(d.x, d.y, prev.x, prev.y);
    }
    prev = d;
  }

  // draw the dots
  for (Dot d : dots) {
    d.render();
    d.update();
  }
}

void mousePressed() {
  Dot d = new Dot();
  d.x = mouseX;
  d.y = mouseY;
  d.gravity = 0.1;
  d.elasticity = 0.8;
  dots.add(d);
}