Point[] p = new Point[30];
textFont (createFont("Palatino Linotype", tSize));
t = new SpecialText(tSize);
ms = new MainScreen(tSize);
projectiles = new Projectiles();
myIncline = new MyIncline();
showSprings = new ShowSprings();
pg = new PlanetGravity();
for (int i = 0; i < p.length; i++) {
showSprings.displayHorizontal();
showSprings.displayVertical();
if (showSprings.mouseIsDragging) {
showSprings.mouseIsDragging = false;
if (showSprings.vmouseIsDragging) {
showSprings.vmouseIsDragging = false;
boolean followMouse = false;
if (followMouse == false) {
deltaTScale = slider(deltaTScale, 600, 40, "Orbit Speed");
deltaT = .01 + ((deltaTScale + 40 - 600)/80)*(.2 - .01);
ellipse(340, 340, 10, 10);
ellipse(340, 340, 260, 260);
float slider(float a, float x, float y, String title) {
line(x - 40, y, x + 40, y);
line(x - 40, y - 5, x - 40, y + 5);
line(x + 40, y - 5, x + 40, y + 5);
if (mouseX >= x - 40 && mouseX <= x + 40 && mouseY >= y - 5 && mouseY <= y + 5 && mousePressed) {
ellipse(a, y + 1, 10, 10);
float restingDistance = 2;
PVector velVector = new PVector(0, 0);
PVector centripAccel = new PVector(0, 0);
diffX = new float[p.length];
diffY = new float[p.length];
distance = new float[p.length];
difference = new float[p.length];
translateX = new float[p.length];
translateY = new float[p.length];
for (int i = 0; i < p.length-1; i++) {
diffX[i] = p[i].x - p[i+1].x;
diffY[i] = p[i].y - p[i+1].y;
distance[i] = sqrt(diffX[i] * diffX[i] + diffY[i] * diffY[i]) ;
difference[i] = (restingDistance - distance[i]) / distance[i];
translateX[i] = diffX[i] * 0.5 * difference[i];
translateY[i] = diffY[i] * 0.5 * difference[i];
p[i+1].x -= translateX[i];
p[i+1].y -= translateY[i];
for (int i = 1; i < p.length; i++) {
for (int i = 0; i < p.length-1; i++) {
line(p[i].x, p[i].y, p[i+1].x, p[i+1].y);
ellipse(p[i].x, p[i].y, 2, 2);
ellipse(p[i+1].x, p[i+1].y, 2, 2);
ellipse(p[p.length-1].x, p[p.length-1].y, 20, 20);
PVector last = new PVector(p[p.length-1].lastX - width/2, p[p.length-1].lastY - height/2);
PVector next = new PVector(p[p.length-1].nextX - width/2, p[p.length-1].nextY - height/2);
deltaTheta = PVector.angleBetween(last, next);
omega = deltaTheta*frameRate;
prettyOmega = int(omega/PI * 100);
if (p[0].followMouse == false) {
text("Angular velocity ω = " + prettyOmega/100 + "π radians/sec", 40, 120);
float prettyCA = int(centripAccel.mag()*100)/100;
text("Centripetal acceleration = = " + prettyCA, 40, 80);
t.createVectorWithSubscript("a", "c", 192, 80);
t.createDivision("v^2", "r", 237, 80);
line (350, 60, 350, 100);
float prettyV = int(velVector.mag()*100)/100;
text("Velocity = " + prettyV, 40, 40);
rect(500, 70, 170, 35, 10);
rect(560, 120, 55, 35, 10);
if (mouseX >= 560 && mouseX <= 615 && mouseY >= 120 && mouseY <=155 && mousePressed == true) {
if (mouseX >= 500 && mouseX <= 670 && mouseY >= 70 && mouseY <= 105 && mousePressed == true) {
if (p[0].followMouse == false && mouseTimer > 7) {
if (p[0].followMouse == true && mouseTimer > 7) {
p[0].followMouse = false;
text("Toggle Mouse Control", 515, 92);
text("Back", 560+textWidth("Back")/2, 142);
translate(p[p.length-1].x, p[p.length-1].y);
velVector.x = p[p.length-1].nextX - p[p.length-1].lastX;
velVector.y = p[p.length-1].nextY - p[p.length-1].lastY;
velVector.mult(8*sqrt(p[p.length-1].velX*p[p.length-1].velX + p[p.length-1].velY*p[p.length-1].velY));
line (0, 0, velVector.x, velVector.y);
if (p[0].followMouse == false) {
centripAccel.x = velVector.x;
centripAccel.y = velVector.y;
centripAccel.normalize();
centripAccel.mult(velVector.mag()*velVector.mag() / dist(width/2, height/2, p[p.length-1].x, p[p.length-1].y));
line (0, 0, centripAccel.x, centripAccel.y);
translate(centripAccel.x, centripAccel.y);
line (0, 0, centripAccel.x, centripAccel.y);
line (0, 0, centripAccel.x, centripAccel.y);
translate(velVector.x, velVector.y);
line (0, 0, velVector.x, velVector.y);
line (0, 0, velVector.x, velVector.y);
void projectilesDescription() {
text("Observe the path of a projectile", 400, 160);
text("as it is launched at different", 400, 178);
text("angles, initial velocities, and", 400, 196);
text("heights. Also change the height", 400, 214);
text("of the landing site and examine", 400, 232);
text("the projectile's air time, maximum", 400, 250);
text("height, and displacement.", 400, 268);
t.createDivision("Assumptions", " ", 505, 340);
t.createBullet("No wind resistance", 400, 360);
t.createBullet("Force applied to center of mass", 400, 378);
t.createBullet("Acceleration due to gravity = -.1", 400, 396);
t.createBullet("No energy lost to heat", 400, 414);
void inclineDescription() {
text("Examine the physics behind a ", 400, 160);
text("block sliding down an incline.", 400, 178);
text("Change the block's mass, the angle", 400, 196);
text("of the incline, and the friction.", 400, 214);
text("A visual diagram breaks down the", 400, 232);
text("force of gravity into its parallel", 400, 250);
text("and perpendicular components", 400, 268);
text("with respect to the incline.", 400, 286);
t.createDivision("Assumptions", " ", 505, 340);
t.createBullet("No wind resistance", 400, 360);
t.createBullet("Force applied to center of mass", 400, 378);
t.createBullet("No energy lost to heat", 400, 396);
void springsDescription() {
text("Observe the motion of a spring", 400, 160);
text("and change its K value, resting", 400, 178);
text("position, and mass. Also examine", 400, 196);
text("the effects of these variables", 400, 214);
text("on the restorative force of the", 400, 232);
text("spring. Drag blocks to move them.", 400, 250);
t.createDivision("Assumptions", " ", 505, 340);
t.createBullet("No wind resistance", 400, 360);
t.createBullet("Force applied to center of mass", 400, 378);
t.createBullet("No energy lost to heat", 400, 396);
t.createBullet("Zero friction", 400, 414);
t.createBullet("Vectors represented as +/- values", 400, 432);
t.createBullet("Spring itself has no mass", 400, 450);
void curvedDescription() {
text("Watch as motion along a curved", 400, 160);
text("path changes angular velocity", 400, 178);
text("and centripetal acceleration.", 400, 196);
text("Adjust the speed of rotation and", 400, 214);
text("use the mouse to swing the object.", 400, 232);
text("The \"tether\" uses verlet integration.", 400, 250);
t.createDivision("Assumptions", " ", 505, 340);
t.createBullet("No wind resistance", 395, 360);
t.createBullet("Force applied to center of mass", 395, 378);
t.createBullet("No energy lost to heat", 395, 396);
t.createBullet("No gravity", 395, 414);
t.createBullet("Angular velocity is a pseudovector", 395, 432);
t.createBullet("r = distance from center of circle", 395, 450);
void gravityDescription() {
text("Examine the effects of Newton's", 400, 160);
text("Law of Universal Gravitation on", 400, 178);
text("circular moving bodies. Bodies", 400, 196);
text("can be added to the system by", 400, 214);
text("clicking. Optionally, bodies", 400, 232);
text("form larger masses upon collision", 400, 250);
text("until a black hole is created.", 400, 268);
t.createDivision("Assumptions", " ", 505, 340);
t.createBullet("No wind resistance", 400, 360);
t.createBullet("Force applied to center of mass", 400, 378);
t.createBullet("No energy lost to heat", 400, 396);
t.createBullet("The black hole is just for fun", 400, 414);
t.createBullet("G = 5.0 × 10^-8", 400, 432);
void thermoDescription() {
text("Observe the relationship between", 400, 160);
text("pressure, volume, and temperature", 400, 178);
text("in a closed chamber full of", 400, 196);
text("particles. Particle velocity and", 400, 214);
text("mass are adjustable, as is the mass", 400, 232);
text("of the chamber's lid. These", 400, 250);
text("variables determine internal", 400, 268);
text("energy and kinetic energy.", 400, 286);
t.createDivision("Assumptions", " ", 505, 340);
t.createBullet("No wind resistance", 400, 360);
t.createBullet("Force applied to center of mass", 400, 378);
t.createBullet("No energy lost to heat", 400, 396);
t.createBullet("Constant number of particles", 400, 414);
t.createBullet("Ideal gas constant = 8.31", 400, 432);
t.createBullet("Acceleration due to gravity = -.07", 400, 450);
t.createBullet("100% elastic particle collisions", 400, 468);
t.createBullet("Particles do not interact", 400, 486);
boolean useBlackHoles = false;
boolean isOverButton = false;
planetList = new ArrayList();
for (int i = 0; i < numPlanets; i++) {
planetList.add(new MyPlanet(0, 0, i*300+100, 340, 20, false));
if (keyPressed && key == 'p' && keyTimer > 7) {
for (int i = 0; i < numPlanets; i++) {
MyPlanet p = (MyPlanet) planetList.get(i);
numPlanets = p.display(planetList, useBlackHoles, paused);
if (mousePressed && mouseTimer > 7 && isOverButton == false) {
planetList.add(new MyPlanet(0, 0, mouseX, mouseY, 30, false));
rect(560, 40, 55, 35, 10);
rect(510, 80, 150, 35, 10);
rect(560, 120, 55, 35, 10);
text("Toggle Collisions", 530, 101);
text("Click to add bodies", 530, 180);
text("Press P to pause", 540, 200);
t.createVector("F", 40, 40);
t.createDivision("mass 1 × mass 2", "distance^2", 130, 40);
if (mouseX >= 560 && mouseX <= 615 && mouseY >= 40 && mouseY <= 75 && mousePressed) {
if (mouseX >= 510 && mouseX <= 660 && mouseY >= 80 && mouseY <= 115) {
if (mousePressed && buttonTimer > 7) {
if (mouseX >= 560 && mouseX <= 615 && mouseY >= 120 && mouseY <= 155 && mousePressed) {
float criticalMass = 320;
PVector force = new PVector(0, 0);
PVector accel = new PVector(0, 0);
PVector velocity = new PVector(0, 0);
PVector position = new PVector(0, 0);
MyPlanet(float velX, float velY, float x, float y, float radius, boolean blackHole) {
if (isBlackHole == false) {
c = color(random(0, 180), random(0, 180), random(0, 180), 170);
int display(ArrayList planetList, boolean useBlackHoles, boolean paused) {
if (isBlackHole == false) {
if (planetList.size() > 1) {
setForce(planetList, position.x, position.y);
accel.x = force.x / mass;
accel.y = force.y / mass;
for (int i = 0; i < planetList.size(); i++) {
MyPlanet p = (MyPlanet) planetList.get(i);
hit = checkCollision(this, p, useBlackHoles);
float newVelX = (mass*velocity.x + p.mass*p.velocity.x) / (mass + p.mass);
float newVelY = (mass*velocity.y + p.mass*p.velocity.y) / (mass + p.mass);
float newPosX = (position.x+p.position.x)/2;
float newPosY = (position.y+p.position.y)/2;
if (p.isBlackHole == false) {
planetList.add(new MyPlanet(newVelX, newVelY, newPosX, newPosY, newR, false));
if (position.x+r > width) {
if (position.y+r > height) {
if (r >= criticalMass && isBlackHole == false) {
createBlackHole(planetList);
ellipse(position.x, position.y, 2*r, 2*r);
return planetList.size();
void setForce(ArrayList planetList, float x, float y) {
for (int i = 0; i < planetList.size(); i++) {
MyPlanet p = (MyPlanet) planetList.get(i);
totalX += p.position.x - x;
totalY += p.position.y - y;
totalDist += dist(x, y, p.position.x, p.position.y);
if (p.isBlackHole == false) {
force.x = totalX / (planetList.size()-1);
force.y = totalY / (planetList.size()-1);
avgDist = totalDist / (planetList.size()-1);
avgMass = totalMass / (planetList.size()-1);
force.mult( (G*mass*avgMass) / (avgDist*avgDist) );
boolean checkCollision (MyPlanet a, MyPlanet b, boolean useBlackHoles) {
if (dist(a.position.x, a.position.y, b.position.x, b.position.y) < a.r+b.r && useBlackHoles == true) {
void createBlackHole(ArrayList planetList) {
MyPlanet bh = new MyPlanet(0, 0, position.x, position.y, 10, true);
for (int i = 0; i < 22; i++) {
translate(position.x, position.y);
bezier(0, 0, 15, -25, 55, -25, 70, 0);
boolean[] mouseIsHovering;
"Projectiles", "Friction and Incline", "Springs", "Curved Motion", "Gravity", "Thermodynamics"
numBoxes = boxTitle.length;
clicked = new boolean[numBoxes];
mouseIsHovering = new boolean[numBoxes];
for (int i = 0; i < clicked.length; i++) {
mouseIsHovering[i] = false;
boolean menuBox(float x, float y, float w, float h, String label) {
boolean selected = false;
if (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY < y + h) {
rect(380, 130, 250, 450, 10);
triangle(x + w, y + h/2, 381, y + h/2 - 22, 381, y + h/2 + 22);
if (mousePressed == true) {
text(label, x + w/2, y + h/2 + 8);
boolean hovering(float x, float y, float w, float h) {
if (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY < y + h) {
text("Physics", 340, 70);
for (int i = 0; i < numBoxes; i++) {
mouseIsHovering[i] = hovering(100, i*80 + 130, 250, 50);
clicked[i] = menuBox(100, i*80 + 130, 250, 50, boxTitle[i]);
if (mouseIsHovering[0]) {
d.projectilesDescription();
if (mouseIsHovering[1]) {
if (mouseIsHovering[2]) {
if (mouseIsHovering[3]) {
if (mouseIsHovering[4]) {
if (mouseIsHovering[5]) {
text("TrevPhil", 630, 665);
float iHeightScale = 340;
float frictionScale = 340;
PVector velocity = new PVector(0, 0);
PVector position = new PVector(x, y);
PVector gravPerp = new PVector(-sin(radians(36.3))*grav*cos(radians(36.3)), cos(radians(36.3))*grav*cos(radians(36.3)));
PVector gravParallel = new PVector(cos(radians(36.3))*grav*sin(radians(36.3)), sin(radians(36.3))*grav*sin(radians(36.3)));
PVector gravForce = new PVector(gravPerp.x + gravParallel.x, gravPerp.y + gravParallel.y);
PVector normalForce = new PVector(gravPerp.x*-1, gravPerp.y*-1);
PVector friction = new PVector(gravParallel.x*-1, gravParallel.y*-1);
iHeightScale = slider(iHeightScale, 340, 40, "Incline Height");
frictionScale = slider(frictionScale, 340, 80, "Friction");
massScale = slider(massScale, 340, 120, "Mass of Block");
velocity.x += gravForce.x/mass;
velocity.y += gravForce.y/mass;
inclineAngle = degrees(atan(iHeight/340));
gravPerp.x = -sin(radians(inclineAngle))*grav*cos(radians(inclineAngle));
gravPerp.y = cos(radians(inclineAngle))*grav*cos(radians(inclineAngle));
normalForce.x = gravPerp.x*-1;
normalForce.y = gravPerp.y*-1;
gravParallel.x = cos(radians(inclineAngle))*grav*sin(radians(inclineAngle));
gravParallel.y = sin(radians(inclineAngle))*grav*sin(radians(inclineAngle));
friction.x = gravParallel.x*-1;
friction.y = gravParallel.y*-1;
friction.mult(frictionLevel);
if (friction.mag() > gravParallel.mag()) {
friction.mult(gravParallel.mag());
gravForce.x = gravPerp.x + gravParallel.x;
gravForce.y = gravPerp.y + gravParallel.y;
rect(22.5, 220, 55, 35, 10);
rect(22.5, 170, 55, 35, 10);
if (mouseX >= 22.5 && mouseX <= 77.5 && mouseY >= 220 && mouseY <= 255 && mousePressed == true) {
if (mouseX >= 22.5 && mouseX <= 77.5 && mouseY >= 170 && mouseY <= 192 && mousePressed == true) {
text(int(iHeight), 390, 40);
text(frictionLevel, 390, 80);
text(int(mass), 390, 120);
text("Force of gravity (where = -.07)", 100, 300);
t.createVectorWithSubscript("F", "g", 100+textWidth("Force of gravity "), 300);
t.createVector("a", 100+textWidth("Force of gravity (where "), 300);
text("Perpendicular component = cos Ó¨", 100, 420);
t.createVectorWithSubscript("F", "g", 100+textWidth("Perpendicular component = "), 420);
text("Parallel component = sin Ó¨", 100, 460);
t.createVectorWithSubscript("F", "g", 100+textWidth("Parallel component = "), 460);
text("Normal Force", 100, 500);
text("Force of Friction", 100, 540);
float prettyAngle = int(inclineAngle*100)/100;
text("Incline Angle (Ө) = " + prettyAngle + "°", 100, 340);
float prettyNum = friction.mag()/normalForce.mag();
text("Coefficient of friction μ = = " + prettyNum, 100, 380);
t.createDivision("|force of friction|", "|normal force|", 100+textWidth("Coefficient of static friction = "), 380);
iHeight = 0 + ((iHeightScale + 40 - 340)/80)*(500 - 0);
frictionLevel = 0 + ((frictionScale + 40 - 340)/80)*(60 - 0);
frictionLine = frictionLevel/25;
mass = 800 + ((massScale + 40 - 340)/80)*(1700 - 800);
if (position.y < (iHeight/340)*(position.x-340)) {
velocity.x += normalForce.x/mass;
velocity.y += normalForce.y/mass;
velocity.x += friction.x/mass;
velocity.y += friction.y/mass;
if (rotateAngle < inclineAngle) {
translate(position.x, position.y);
rotate(radians(rotateAngle));
line(0, 0, gravForce.x, gravForce.y);
line(gravForce.x, gravForce.y, gravForce.x - 5, gravForce.y + 5);
line(gravForce.x, gravForce.y, gravForce.x + 5, gravForce.y + 5);
line(0, 0, normalForce.x, normalForce.y);
line(0, 0, gravPerp.x, gravPerp.y);
translate(gravPerp.x, gravPerp.y);
line(0, 0, gravParallel.x, gravParallel.y);
line(0, 0, friction.x, friction.y);
for (int i = 0; i < sqrt(iHeight*iHeight + 340*340); i += 3) {
float yBump = (iHeight/340)*(i - 340);
line(680-i, -yBump, 680-i, -yBump+frictionLine);
float slider(float a, float x, float y, String title) {
line(x - 40, y, x + 40, y);
line(x - 40, y - 5, x - 40, y + 5);
line(x + 40, y - 5, x + 40, y + 5);
if (mouseX >= x - 40 && mouseX <= x + 40 && mouseY >= y - 5 && mouseY <= y + 5 && mousePressed) {
ellipse(a, y + 1, 10, 10);
float cannonHeightScale = 540;
float cannonAngleScale = 520;
float landingYScale = 540;
float initialVScale = 580;
PVector accel = new PVector(0, .1);
PVector velocity = new PVector(0, 0);
PVector position = new PVector(0, 680);
ellipse(position.x, position.y, 20, 20);
rect(22.5, 220, 55, 35, 10);
rect(22.5, 170, 55, 35, 10);
if (mouseX >= 22.5 && mouseX <= 77.5 && mouseY >= 220 && mouseY <= 255 && mousePressed == true) {
velocity.x = -cos(radians(angle-450))*initialV;
velocity.y = -sin(radians(angle-450))*initialV;
if (mouseX >= 22.5 && mouseX <= 77.5 && mouseY >= 170 && mouseY <= 192 && mousePressed == true) {
cannonHeightScale = slider(cannonHeightScale, 540, 40, "Cannon Height");
cannonAngleScale = slider(cannonAngleScale, 540, 80, "Cannon Angle");
landingYScale = slider(landingYScale, 540, 120, "Platform Height");
t.createVectorWithSubscript("v", "i", 504, 150);
text(int(680-y+62.5), 590, 40);
text(270-angle + "°", 590, 80);
text(int(680-landingY), 590, 120);
text(initialV, 590, 160);
initialVScale = slider(initialVScale, 540, 160, " Magnitude");
y = 660 + ((cannonHeightScale + 40 - 540)/80)*(380 - 660);
angle = 255 + ((cannonAngleScale + 40 - 540)/80)*(20 - 75);
landingY = 660 + ((landingYScale + 40 - 540)/80)*(380 - 660);
initialV = 3 + ((initialVScale + 40 - 540)/80)*(6.2 - 3);
for (int pointX = 0; pointX < 630; pointX++) {
float beforePointY = sin(radians(angle-450))*initialV*(pointX-1) + (-.1/2)*(pointX-1)*(pointX-1);
float pointY = sin(radians(angle-450))*initialV*pointX + (-.1/2)*pointX*pointX;
float afterPointY = sin(radians(angle-450))*initialV*(pointX+1) + (-.1/2)*(pointX+1)*(pointX+1);
point(pointX*initialV*-cos(radians(angle-450)) + 50, y-62.5-pointY);
if (pointY > beforePointY && pointY > afterPointY) {
ellipse(pointX*initialV*-cos(radians(angle-450)) + 50, y-62.5-pointY, 10, 10);
text(pointY+62.5+(680-y), 40+textWidth("Max Height = = ( sin Ó¨ )^2 / 2 = "), 120);
bezierVertex(-10, -60, 10, -60, 15, -40);
rect(0, y, 100, 680 - y);
rect(380, landingY, 300, 680 - landingY);
if (position.x+10 >= 380 && position.y-5 >= landingY && position.y+10 <= 660) {
if (position.x-5 >= 380 && position.x <= 680 && position.y+10 >= landingY) {
position.y = landingY-10;
text("Displacement = ( + .5 Δt ) Δt = ", 40, 40);
t.createVectorWithSubscript("v", "i", 40+textWidth("Displacement = ( "), 40);
t.createVector("a", 40+textWidth("Displacement = ( + .5 "), 40);
text(dist(position.x, position.y, 50, y - 62.5), 40+textWidth("Displacement = ( + .5 Δt ) Δt = "), 40);
text("Air Time = ", 40, 80);
text(time, 40+textWidth("Air Time = "), 80);
text("Max Height = = ( sin Ó¨ )^2 / 2 = ", 40, 120);
t.createVectorWithSubscript("s", "y", 40+textWidth("Max Height = "), 120);
t.createVectorWithSubscript("v", "i", 40+textWidth("Max Height = = ( "), 120);
t.createVector("a", 40+textWidth("Max Height = = ( sin Ó¨ )^2 / 2 "), 120);
float slider(float a, float x, float y, String title) {
line(x - 40, y, x + 40, y);
line(x - 40, y - 5, x - 40, y + 5);
line(x + 40, y - 5, x + 40, y + 5);
if (mouseX >= x - 40 && mouseX <= x + 40 && mouseY >= y - 5 && mouseY <= y + 5 && mousePressed) {
ellipse(a, y + 1, 10, 10);
float A = abs(initialPos - restPos);
boolean mouseIsDragging = false;
float vrestPos = vmass*vacceleration;
float vA = abs(vinitialPos - vrestPos);
boolean vmouseIsDragging = false;
void displayHorizontal() {
kScale = slider(kScale, 70, 500, "k Value");
restScale = slider(restScale, 170, 500, "Rest Position");
massScale = slider(massScale, 270, 500, "Mass of Block");
K = .1 + ((kScale + 40 - 70)/80)*(.9 - .1);
restPos = -100 + ((restScale + 40 - 170)/80)*(200 - -100);
mass = 10 + ((massScale + 40 - 270)/80)*(500 - 10);
rect(50, 400, 55, 35, 10);
text(int(restPos), 170, 470);
text(int(mass), 270, 470);
text("Displacement = A cos( √(k / mass) × time) = ", 30, 200);
float prettyVD = int(vdisplacement*100)/100;
text("(vertical spring) " + prettyVD, 30, 225);
float prettyD = int(displacement*100)/100;
text("(horizontal spring) " + prettyD, 30, 250);
text("Restorative force = -k × displacement = ", 30, 290);
float prettyVR = int(vrestorativeForce*100)/100;
text("(vertical spring) " + -prettyVR, 30, 315);
float prettyR = int(restorativeForce*100)/100;
text("(horizontal spring) " + prettyR, 30, 340);
text("Click and drag to move blocks", 40, 650);
text("Back", 50-textWidth("Back")/2, 404);
text("Given one-dimensional motion, assume", 40, 40);
text("that positive values represent vector", 40, 65);
text("directions to the right/downwards and", 40, 90);
text("negative values to the left/upwards", 40, 115);
if (mouseX >= 22.5 && mouseX <= 77.5 && mouseY >= 382.5 && mouseY <= 417.5 && mousePressed == true) {
if (mouseIsDragging == false) {
displacement = A*cos(sqrt(K/mass)*time);
restorativeForce = -1 * K * displacement;
line(restPos, 60, restPos, -60);
text("Rest Position", restPos-textWidth("Rest Position")/2, 75);
w = displacement + restPos;
for (int i = 0; i < numCoils; i++) {
bezier(i*(w/numCoils), -15, i*(w/numCoils)+5, -45, (i+1)*(w/numCoils)-5, -45, (i+1)*(w/numCoils), -15);
if (mouseX >= displacement+restPos-25+340 && mouseX <= displacement+restPos+25+340 && mouseY >= -50+600 && mouseY <= 0+600 && mousePressed) {
displacement = mouseX - 340 - restPos;
rect(displacement, -25, 50, 50);
vkScale = slider(vkScale, 400, 40, "k Value");
vrestScale = slider(vrestScale, 400, 80, "Rest Position");
vmassScale = slider(vmassScale, 400, 120, "Mass of Block");
vK = -.9 + ((vkScale + 40 - 480)/80)*(.1 - .9);
vrestPos = 300 + ((vrestScale + 40 - 480)/80)*(550 - 300) - vmass/30;
vmass = -500 + ((vmassScale + 40 - 480)/80)*(10 - 500);
text(int(vrestPos), 335, 80);
text(-int(vmass), 335, 120);
if (vmouseIsDragging == false) {
vdisplacement = vA*cos(sqrt(vK/vmass)*vtime);
vrestorativeForce = -1 * vK * vdisplacement;
line(490, vrestPos, 610, vrestPos);
text("Rest", 620, vrestPos-10);
text("Position", 620, vrestPos+10);
vh = vdisplacement + vrestPos;
for (int i = 0; i < numCoils; i++) {
bezier(540, i*(vh/numCoils), 555, i*(vh/numCoils)+5, 585, (i+1)*(vh/numCoils)-5, 540, (i+1)*(vh/numCoils));
if (mouseY >= vdisplacement+vrestPos-25 && mouseY <= vdisplacement+vrestPos+25 && mouseX >= 550-25 && mouseX <= 550+25 && mousePressed) {
vdisplacement = mouseY - vrestPos;
rect(550, vdisplacement, 50, 50);
float slider(float a, float x, float y, String title) {
line(x - 40, y, x + 40, y);
line(x - 40, y - 5, x - 40, y + 5);
line(x + 40, y - 5, x + 40, y + 5);
if (mouseX >= x - 40 && mouseX <= x + 40 && mouseY >= y - 5 && mouseY <= y + 5 && mousePressed) {
ellipse(a, y + 1, 10, 10);
void createBullet(String writing, float x, float y) {
text(writing, x + 11, y + 5);
void createSubscript(String bigLetter, String subscript, float x, float y) {
float sw = textWidth(bigLetter);
textFont (createFont("Palatino Linotype Italic", tSize - 5));
text(subscript, x + sw, y + 5);
textFont (createFont("Palatino Linotype", tSize));
void createDivision(String top, String bottom, float x, float y) {
text(bottom, x, y + tSize);
float topWidth = textWidth(top);
float bottomWidth = textWidth(bottom);
if (topWidth >= bottomWidth) {
line (x - topWidth/2, y - 5, x + topWidth/2, y - 5);
line (x - bottomWidth/2, y - 5, x + bottomWidth/2, y - 5);
void createVector(String vec, float x, float y) {
float vecWidth = textWidth(vec);
line(x, y - tSize + 2, x + vecWidth, y - tSize + 2);
line(x + vecWidth, y - tSize + 2, x + vecWidth - 2, y - tSize);
void createVectorWithSubscript(String vec, String subscript, float x, float y) {
float vecWidth = textWidth(vec);
line(x, y - tSize + 2, x + vecWidth, y - tSize + 2);
line(x + vecWidth, y - tSize + 2, x + vecWidth - 2, y - tSize);
textFont (createFont("Palatino Linotype Italic", tSize - 5));
text(subscript, x + vecWidth, y + 5);
textFont (createFont("Palatino Linotype", tSize));
Particle[][] p = new Particle[col][row];
for (int i = 0; i < col; i++) {
for (int j = 0; j < row; j++) {
p[i][j] = new Particle(random(130, 360), random(220, 580));
totalVelocity += p[i][j].velocity.mag();
avgVelocity = totalVelocity / (col*row);
initialMass = (p[0][0].r*p[0][0].r*PI);
avgKE = .5 * initialMass * avgVelocity * avgVelocity;
float totalParticleMomentum = 0;
float lidPos = p[0][0].displayOtherFeatures(totalParticleMomentum);
velocityMag = p[0][0].velocitySlider();
for (int i = 0; i < col; i++) {
for (int j = 0; j < row; j++) {
totalParticleMomentum += p[i][j].display(lidPos, velocityMag, r);
totalVelocity += p[i][j].velocity.mag();
lidPos = p[0][0].displayOtherFeatures(totalParticleMomentum);
avgVelocity = totalVelocity / (col*row);
avgPressure = ((r*r*PI * avgVelocity*avgVelocity) / volume) * ((col*row) / 2);
lidHeight = 580 - lidPos;
volume = lidLength * lidHeight;
temp = (avgPressure * volume)/(col*row*8.31);
avgKE = (r*r*PI * avgVelocity*avgVelocity) / 2;
rect(560, 560, 55, 35, 10);
t.createSubscript("P", "i", 440, 200);
t.createSubscript("V", "i", 452, 200);
t.createSubscript("P", "f", 480, 200);
t.createSubscript("V", "f", 495, 200);
text(" = = nRT", 440, 200);
float prettyPressure = int(avgPressure*100)/100;
text("P = " + prettyPressure, 440, 230);
text("V = " + int(volume), 440, 260);
text("n = 900", 440, 290);
text("R = 8.31 (arbitrary)", 440, 320);
float prettyTemp = int(temp*100)/100;
text("T = " + prettyTemp, 440, 350);
float prettyPV = int(avgPressure*volume*100)/100;
text("PV = " + prettyPV, 440, 380);
text("internal energy U = 3/2 PV = " + int((3/2)*avgPressure*volume), 440, 410);
text("average KE = 1/2 m v^2 = " + int(avgKE), 440, 440);
text(velocityMag, 510, 80);
text("Back", 558+textWidth("Back")/2, 582);
if (mouseX >= 560 && mouseX <= 615 && mouseY >= 560 && mouseY <= 595 && mousePressed) {
float velocityScale = 592.94118;
PVector velocity = new PVector(0, 0);
PVector position = new PVector(0, 0);
float lidMassScale = 580;
Particle(float x, float y) {
velocity.x = random(-5, 5);
velocity.y = random(-5, 5);
float display(float lidTop, float vMag, float r) {
velocity.x = initialVx * vMag;
velocity.y = initialVy * vMag;
float particleMomentum = 0;
ellipse(position.x, position.y, r, r);
if (position.x-r < 120) {
if (position.x+r > 380) {
if (position.y-r < lidTop-20) {
position.y = r+lidTop-20;
particleMomentum = (PI*r*r) * velocity.y;
if (position.y+r > 580) {
float displayOtherFeatures(float totalParticleMomentum) {
lidForce = lidMass * grav;
lidAccel = lidForce / lidMass;
lidMomentum = lidVelocity * lidMass;
lidMomentum += totalParticleMomentum;
lidVelocity = lidMomentum / lidMass;
ellipse(250, lidPos-70, 50, 50);
ellipse(250, lidPos-70, 30, 30);
lidMassScale = slider(lidMassScale, 600, 40, "Lid Mass");
lidMass = 1000 + ((lidMassScale + 40 - 600)/80)*(2000 - 1000);
text(int(lidMass), 515, 40);
velocityScale = slider(velocityScale, 600, 80, "Velocity Scalar");
velocityMag = .3 + ((velocityScale + 40 - 600)/80)*(2 - .3);
rScale = slider(rScale, 600, 120, "Particle Mass");
r = .3 + ((rScale + 40 - 600)/80)*(2.5 - .3);
float slider(float a, float x, float y, String title) {
line(x - 40, y, x + 40, y);
line(x - 40, y - 5, x - 40, y + 5);
line(x + 40, y - 5, x + 40, y + 5);
if (mouseX >= x - 40 && mouseX <= x + 40 && mouseY >= y - 5 && mouseY <= y + 5 && mousePressed) {
ellipse(a, y + 1, 10, 10);