Bouncy

Dan Shiffman gave us several code examples of how to simulate springs and pendulums (pendula?). I was really stuck for ideas, and I thought it might be fun to stack some of them together. Somehow, all I could think of was one of those bouncy rubber bats that many people use for Halloween decorations. To do this, I put together a spring with a bob, and added a pendulum to each end of the bob. This was the result:

Main sketch code:

[code lang="java"]//Kim Ash
//approximates a bouncy bat toy
 
Bob bob;
Spring spring;
//Pendulum p1, p2;
 
void setup()
{
  size(640, 360);
  smooth();
  spring = new Spring(width/2,10,100);
  bob = new Bob(width/2,100);
 
}
 
void draw()
{
  background(199, 252,252);
  // Apply a gravity force to the bob
  PVector gravity = new PVector(0,1);
  bob.applyForce(gravity);
   
  // Connect the bob to the spring (this calculates the force)
  spring.connect(bob);
  // Constrain spring distance between min and max
  spring.constrainLength(bob,30,200);
  //springA.constraingLength(bob,30,200);
   
  // Update bob
  bob.update();
  // If it's being dragged
  bob.drag(mouseX,mouseY);
   
  // Draw everything
  spring.displayLine(bob); // Draw a line between spring and bob
  bob.display();
  spring.display();
  bob.updatePendulum();
   
  fill(255);
}
 
// For mouse interaction with bob
 
void mousePressed()  {
  bob.clicked(mouseX,mouseY);
}
 
void mouseReleased()  {
  bob.stopDragging();
}[/code]

Bob class:

[code lang="java"]
// Bob class, just like our regular Mover (location, velocity, acceleration, mass)
 
class Bob {
  PVector location;
  PVector velocity;
  PVector acceleration;
  float mass = 10;
  Pendulum p1, p2;
   
  // Arbitrary damping to simulate friction / drag
  float damping = 0.98;
 
  // For mouse interaction
  PVector drag;
  boolean dragging = false;
 
  // Constructor
  Bob(float x, float y) {
    location = new PVector(x,y);
    velocity = new PVector();
    acceleration = new PVector();
    drag = new PVector();
    p1 = new Pendulum(new PVector(location.x + mass, location.y), 50, -PI/2);
    p2 = new Pendulum(new PVector(location.x - mass, location.y), 50, PI/2);
  }
   
  void updatePendulum() {
   p1.setOrigin(new PVector(location.x + mass, location.y));
   p2.setOrigin(new PVector(location.x - mass, location.y));
 }
 
  // Standard Euler integration
  void update() {
    velocity.add(acceleration);
    velocity.mult(damping);
    location.add(velocity);
    acceleration.mult(0);
    p1.update();
    p2.update();
  }
 
  // Newton's law: F = M * A
  void applyForce(PVector force) {
    PVector f = force.get();
    f.div(mass);
    acceleration.add(f);
  }
 
 
  // Draw the bob
  void display() {
    stroke(0);
    fill(100);
    if (dragging) {
      fill(255);
    }
    ellipse(location.x,location.y,mass*2,mass*2);
    p1.display();
    p2.display();
  }
 
  // The methods below are for mouse interaction
 
  // This checks to see if we clicked on the mover
  void clicked(int mx, int my) {
    float d = dist(mx,my,location.x,location.y);
    if (d < mass) {
      dragging = true;
      drag.x = location.x-mx;
      drag.y = location.y-my;
    }
  }
 
  void stopDragging() {
    dragging = false;
  }
 
  void drag(int mx, int my) {
    if (dragging) {
      location.x = mx + drag.x;
      location.y = my + drag.y;
    }
  }
}[/code]

Spring class:

[code lang="java"]class Spring {
 
  // Location
  PVector anchor;
 
  // Rest length and spring constant
  float len;
  float k = 0.1;
   
  // Constructor
  Spring(float x, float y, int l) {
    anchor = new PVector(x,y);
    len = l;
  }
 
  // Calculate spring force
  void connect(Bob b) {
    // Vector pointing from anchor to bob location
    PVector force = PVector.sub(b.location,anchor);
    // What is distance
    float d = force.mag();
    // Stretch is difference between current distance and rest length
    float stretch = d - len;
     
    // Calculate force according to Hooke's Law
    // F = k * stretch
    force.normalize();
    force.mult(-1 * k * stretch);
    b.applyForce(force);
  }
 
  // Constrain the distance between bob and anchor between min and max
  void constrainLength(Bob b, float minlen, float maxlen) {
    PVector dir = PVector.sub(b.location,anchor);
    float d = dir.mag();
    // Is it too short?
    if (d < minlen) {
      dir.normalize();
      dir.mult(minlen);
      // Reset location and stop from moving (not realistic physics)
      b.location = PVector.add(anchor,dir);
      b.velocity.mult(0);
    // Is it too long?
    } else if (d > maxlen) {
      dir.normalize();
      dir.mult(maxlen);
      // Reset location and stop from moving (not realistic physics)
      b.location = PVector.add(anchor,dir);
      b.velocity.mult(0);
    }
  }
 
  void display() {
    fill(100);
    rectMode(CENTER);
    rect(anchor.x,anchor.y,10,10);
  }
   
  void displayLine(Bob b) {
    stroke(0);
    line(b.location.x,b.location.y,anchor.x,anchor.y);
  }
   
}[/code]

Pendulum class:

[code lang="java"]class Pendulum  {
 
  PVector location;    // Location of pendulum ball
  PVector origin;      // Location of arm origin
  float r;             // Length of arm
  float angle;         // Pendulum arm angle
  float aVelocity;     // Angle velocity
  float aAcceleration; // Angle acceleration
  float damping;       // Arbitary damping amount
 
  // This constructor could be improved to allow a greater variety of pendulums
  Pendulum(PVector origin_, float r_, float a) {
    // Fill all variables
    origin = origin_.get();
    location = new PVector();
    r = r_;
    angle = a;
 
    aVelocity = 0.0;
    aAcceleration = 0.0;
    damping = 0.995;   // Arbitrary damping
  }
 
  void go() {
    update();
    display();
  }
 
  // Function to update location
  void update() {
    float G = 0.4;                              // Arbitrary universal gravitational acceleration
    aAcceleration = (-1 * G / r) * sin(angle);  // Calculate acceleration (see: http://www.myphysicslab.com/pendulum1.html)
    aVelocity += aAcceleration;                 // Increment velocity
    aVelocity *= damping;                       // Arbitrary damping
    angle += aVelocity;                         // Increment angle
  }
 
  void display() {
    location.set(r*sin(angle),r*cos(angle),0);         // Polar to cartesian conversion
    location.add(origin);                              // Make sure the location is relative to the pendulum's origin
 
    stroke(0);
    // Draw the arm
    line(origin.x,origin.y,location.x,location.y);
    ellipseMode(CENTER); 
    fill(175);
    // Draw the ball
    ellipse(location.x,location.y,16,16);
  }
   
  void setOrigin(PVector v) {
   origin.x = v.x;
   origin.y = v.y;
 }
}[/code]