Exam Solution all the way to Part (g)

This is pretty hard. I wasn’t expecting many people to have time to get it right. Take a look at how complicated the bounce() method got.

Maybe you can find a simpler way!

Implementation

final float DELTA_T = 0.15;

class Obstacle {
  PVector location;
  float length;

  Obstacle(PVector location_, float length_) {
    location = location_.copy();
    length = length_;
  }

  void display() {
    rectMode(CENTER);
    fill(0);
    rect(location.x, location.y, length, length);
  }
}

class Bouncer {
  PVector location;
  PVector velocity;
  float radius;

  Bouncer(PVector location_, PVector velocity_, float radius_) {
    location = location_.copy();
    velocity = velocity_.copy();
    radius = radius_;
  }

  void update() {
    location.add(velocity.copy().mult(DELTA_T));
  }

  void display() {
    ellipseMode(CENTER);
    noFill();
    stroke(0);
    ellipse(location.x, location.y, 2 * radius, 2 * radius);
  }

  void checkEdges() {
    if (location.x < radius) {
      location.x = radius;
      velocity.x *= -1;
    }
    if (location.x > width - radius) {
      location.x = width - radius;
      velocity.x *= -1;
    }
    if (location.y < radius) {
      location.y = radius;
      velocity.y *= -1;
    }
    if (location.y > height - radius) {
      location.y = height - radius;
      velocity.y *= -1;
    }
  }

  void bounce(Obstacle obstacle) {
    PVector obstacleLocation = obstacle.location;
    float obstacleLength = obstacle.length;
    // These aren't really distances -- they are coordinate differences -- they can be negative:
    float distanceFromLeft = location.x + radius - (obstacleLocation.x - obstacleLength / 2);
    float distanceFromRight = location.x - radius - (obstacleLocation.x + obstacleLength / 2);
    float distanceFromTop = location.y + radius - (obstacleLocation.y - obstacleLength / 2);
    float distanceFromBottom = location.y - radius - (obstacleLocation.y + obstacleLength / 2);

    if (distanceFromLeft > 0 && distanceFromRight < 0 && distanceFromTop > 0 && distanceFromBottom < 0) {
      // We have hit the obstacle!
      // Which side have we hit?
      // These really are distances:
      float smallestLeftRightDistance = Float.min(abs(distanceFromLeft), abs(distanceFromRight));
      float smallestTopBottomDistance = Float.min(abs(distanceFromTop), abs(distanceFromBottom));
      if (smallestLeftRightDistance < smallestTopBottomDistance) {
        if (abs(distanceFromLeft) < abs(distanceFromRight)) {
          location.x -= distanceFromLeft;
        } else {
          location.x -= distanceFromRight;
        }
        velocity.x *= -1;
      } else {
        if (abs(distanceFromBottom) < abs(distanceFromTop)) {
          location.y -= distanceFromBottom;
        } else {
          location.y -= distanceFromTop;
        }
        velocity.y *= -1;
      }
    }
  }
}

Obstacle[] obstacles = new Obstacle[5];
Bouncer[] bouncers = new Bouncer[50];

void setup() {
  size(800, 600);
  for (int i = 0; i < obstacles.length; ++i) {
    obstacles[i] = new Obstacle(new PVector(random(0, width), random(0, height)), random(25, 50));
  }
  for (int i = 0; i < bouncers.length; ++i) {
    bouncers[i] = new Bouncer(new PVector(random(0, width), random(0, height)), new PVector(random(-20, 20), random(-20, 20)), random(5, 10));
  }
}

void draw() {
  background(255);
  for (int i = 0; i < obstacles.length; ++i) {
    obstacles[i].display();
  }
  for (int j = 0; j < bouncers.length; ++j) {
    bouncers[j].update();
    for (int i = 0; i < obstacles.length; ++i) {
      bouncers[j].bounce(obstacles[i]);
    }
    bouncers[j].checkEdges();
    bouncers[j].display();
  }
}