// size
int height = 700;
int width = 600;
// time
int nbFramePerSec = 80;
float drawPeriod = 1/nbFramePerSec;
// ground variables -------------------------------------------
int nbSegment = 40;
Ground[] ground = new Ground[nbSegment];
float[] groundHeight = new float[nbSegment + 1];
//physics variables
float mass = 0.1; // kg
float radius = 15; // 1px = 1cm <<--------
float bounceFactor = -0.95; // dimensionless
float Cd = 0.47; // drag coef, dimensionless
float rho = 1.22; // density, kg / m^3
float ag = 9.81; // acceleration, m/s^2
float Fx, Fy, ax, ay;
// build ball -------------------------------------------
// top left corner is (x, y) = (0, 0)
// bottom right corner is (width, height) = (0, 0)
// x (cm), y (cm), vx (cm/s), vy (cm/s)
int xinit = round(width * 0.0);
int yinit = round(width * 0.95);
int vxinit = 8; // cm/s
int vyinit = -18; // cm/s
Ball ball = new Ball(xinit, yinit, vxinit, vyinit, radius, mass, bounceFactor);
float A = PI * (ball.r/100) * (ball.r/100); // surface, m^2
// setup(), executed once at inception -----------------------------------
void setup() {
size(width, height);
background(255);
smooth();
frameRate(nbFramePerSec); // frames per sec
// calculate ground height for each nbSegment+1 points
for (int i = 0; i < groundHeight.length; i++) {
groundHeight[i] = random(height - 45, height - 30);
}
// build ground over width of canvas, regardless of segment number
float nbSegmentFloat = nbSegment;
for (int i = 0; i < nbSegment; i++) {
ground[i] = new Ground(i*width/nbSegmentFloat, groundHeight[i], (i+1)*width/nbSegmentFloat, groundHeight[i + 1]);
}
}
void draw() {
// display white background with opacity
noStroke();
fill(255, 999); // first arg is white=255, second arg is opacity 999=opaque and 0=transparent
rect(0, 0, width, height);
// draw ground
fill(153);
beginShape();
for (int i = 0; i < nbSegment; i++) {
vertex(ground[i].x1, ground[i].y1);
}
vertex(ground[nbSegment-1].x2, ground[nbSegment-1].y2);
vertex(ground[nbSegment - 1].x2, height);
vertex(ground[0].x1, height);
endShape(CLOSE);
// ball physics
// drag force: Fd = -1/2 * Cd * A * rho * v * v
Fx = -0.5 * Cd * A * rho * pow(ball.vx, 3) / abs(ball.vx);
Fy = -0.5 * Cd * A * rho * pow(ball.vy, 3) / abs(ball.vy);
//Fx = 0;
//Fy = 0;
// acceleration ( sum of forces = mass * acceleration )
ax = Fx / ball.m;
ay = ag + (Fy / ball.m);
// Integrate to get velocity
ball.vx += ax * drawPeriod;
ball.vy += ay * drawPeriod;
// Integrate to get position
ball.x += ball.vx * drawPeriod * 100; // factor 100 due to x and vx defined in cm
ball.y += ball.vy * drawPeriod * 100; // factor 100 due to x and vx defined in cm
// draw ball
stroke(0);
fill(200,0,0);
ellipse(ball.x, ball.y, ball.r * 2, ball.r * 2);
// collision
checkWallCollision();
for (int i = 0; i < nbSegment; i++) {
checkGroundCollision(ground[i]);
// print out variable in console
//println("ball.vy="+ball.vy);
}
}
// wall collision
void checkWallCollision() {
// right wall
if (ball.x > width - ball.r) {
ball.x = width - ball.r;
ball.vx *= ball.bounce;
} else /* left wall */ if (ball.x < ball.r) {
ball.x = ball.r;
ball.vx *= ball.bounce;
}
}
// ground collision
void checkGroundCollision(Ground groundSegment) {
// distance ball and ground
float deltaX = ball.x - groundSegment.x;
float deltaY = ball.y - groundSegment.y;
// trig values
float cosRot = cos(groundSegment.rot);
float sinRot = sin(groundSegment.rot);
// rotate to ground referential
float deltaXRefGround = cosRot * deltaX + sinRot * deltaY;
float deltaYRefGround = cosRot * deltaY - sinRot * deltaX;
float vxRefGround = cosRot * ball.vx + sinRot * ball.vy;
float vyRefGround = cosRot * ball.vy - sinRot * ball.vx;
// ground collision segment check
if (deltaYRefGround > -ball.r && ball.x > groundSegment.x1 && ball.x < groundSegment.x2) {
deltaYRefGround = -ball.r;
vyRefGround *= ball.bounce;
}
// reset ground, velocity and orb
deltaX = cosRot * deltaXRefGround - sinRot * deltaYRefGround;
deltaY = cosRot * deltaYRefGround + sinRot * deltaXRefGround;
ball.vx = cosRot * vxRefGround - sinRot * vyRefGround;
ball.vy = cosRot * vyRefGround + sinRot * vxRefGround;
ball.x = groundSegment.x + deltaX;
ball.y = groundSegment.y + deltaY;
}
// Classes ----------------------------------------------------
class Ground {
float x1, y1, x2, y2;
float x, y, len, rot;
// default constructor
Ground() {}
// constructor
Ground(float x1, float y1, float x2, float y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
x = (x1 + x2) / 2;
y = (y1 + y2) / 2;
len = dist(x1, y1, x2, y2);
rot = atan2((y2 - y1), (x2 - x1));
}
}
class Ball {
float x, y, vx, vy, r, m, bounce;
// default constructor
Ball() {}
Ball(float x, float y, float vx, float vy, float r, float m, float bounce) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.r = r;
this.m = m;
this.bounce = bounce;
}
}
<canvas width="600px" height="700px"></canvas>
canvas {
display:block;
margin:20px;
border:1px solid #666;
}
</style>
<script type="text/javascript"> window.addEventListener('load', function() {
var scripts=document.body.getElementsByTagName('script');
var canvases=document.body.getElementsByTagName('canvas');
new Processing(canvases[0], scripts[0].text);
}
, false);
// Here prevents javascript in body from throwing error
</script>
<style>