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]