xxxxxxxxxx
157
let solver;
let currTime = 0;
let lastTime = 0;
function setup() {
createCanvas(400, 400);
colorMode(HSB);
solver = new Solver();
}
function draw() {
background(0);
lastTime = currTime;
currTime = millis();
const deltaS = (currTime - lastTime)/1000;
if(solver.objs.length < 400 && frameCount % 5 === 0) {
const vx = sin(millis()/4000);
const obj = new Verlet(150, 110, 2, vx, 0.1);
obj.setHue(vx * 255);
solver.addObject(obj);
}
solver.update(deltaS);
solver.draw();
}
class Solver {
constructor(objs) {
this.objs = objs ? objs : [];
this.grav = createVector(0, 1000);
this.constraintPos = createVector(200, 200);
this.constraintRad = 150;
}
addObject(obj) {
this.objs.push(obj);
}
update(deltaS) {
const subSteps = 8;
const subDt = deltaS/subSteps;
for(let i = 0; i < subSteps; i ++) {
this.applyGravity();
this.applyConstraint();
this.solveCollisions();
this.updatePositions(subDt);
}
}
updatePositions(deltaS) {
this.objs.forEach(obj => {
obj.updatePosition(deltaS);
});
}
applyGravity() {
this.objs.forEach(obj => {
obj.accelerate(this.grav);
});
}
applyConstraint() {
this.objs.forEach(obj => {
const toObj = p5.Vector.sub(obj.pos, this.constraintPos);
const d = toObj.mag();
const maxD = this.constraintRad - obj.radius;
if(d > maxD) {
const n = p5.Vector.div(toObj, d);
n.mult(maxD);
obj.pos = p5.Vector.add(this.constraintPos, n);
}
});
}
solveCollisions() {
for(let i = 0; i < this.objs.length; i ++) {
const obj1 = this.objs[i];
for(let j = i + 1; j < this.objs.length; j ++) {
const obj2 = this.objs[j];
const collisionAxis = p5.Vector.sub(obj1.pos, obj2.pos);
const d = collisionAxis.mag();
const combinedRad = obj1.radius + obj2.radius;
if(d < combinedRad) {
const n = p5.Vector.div(collisionAxis, d);
const delta = combinedRad - d;
n.mult(0.5 * delta);
obj1.pos.add(n);
obj2.pos.sub(n);
}
}
}
}
draw() {
noStroke();
fill(255);
circle(this.constraintPos.x, this.constraintPos.y, this.constraintRad * 2);
this.objs.forEach(obj => {
obj.draw();
})
}
}
class Verlet {
constructor(x, y, r, vx = 0, vy = 0) {
this.oldPos = createVector(x-vx, y-vy);
this.pos = createVector(x, y);
this.acc = createVector(0, 0);
this.radius = r;
this.hue = color(255);
}
setHue(h) {
this.hue = h;
}
updatePosition(deltaS) {
const vel = p5.Vector.sub(this.pos, this.oldPos);
this.oldPos = this.pos.copy();
this.acc.mult(deltaS);
this.acc.mult(deltaS);
this.pos.add(vel);
this.pos.add(this.acc);
this.acc.x = 0;
this.acc.y = 0;
}
accelerate(acc) {
this.acc.add(acc);
}
draw() {
fill(this.hue, 255, 255);
circle(this.pos.x, this.pos.y, this.radius * 2);
}
}