EduCSE Blog

Share

Adding Constraint and Friction

Author: Win Aung Cho
Position Verlet Algorithm

x(t+Δt)=2x(t)-x(t-Δt)+Δt²•F(x(t))/m + O[Δt⁴]
v(t)=1/(2Δt)•[x(t+Δt) - x(t-Δt)] + O[Δt²]


Adding damping such as retarding force by friction.
Velocity is modified by damping or friction facto β.
v(t) = (1 - β)*v(t)
where
v(t) = x(t) - x(t-Δt)

Particle model become as following.
class Point {
		constructor(x, y, vx, vy, lock) {
			this.pos = new Vector(x, y);
			this.oldpos = new Vector(x + (vx || 0), y + (vy || 0)); // velocity x, y
			this.friction = 0.99;
			this.groundFriction = 0.99;
			this.radius = 15;
			this.color = "e62a4f";
			this.mass = 1;
			this.fixed = lock || false;
		}
		update() {
			if(!this.fixed) {
				let vel = SUB(this.pos, this.oldpos);
				vel.mult(this.friction);
				this.oldpos.set(this.pos.x, this.pos.y);
				this.pos.add(vel);
				this.pos.add(MUL(Gravity, stepTime * stepTime));
			}
		}
		checkborder() {
			if(this.fixed) return;
			let vel = SUB(this.pos, this.oldpos);
			vel.mult(this.friction);
			vel.mult(this.groundFriction);
			if(this.pos.x > CANVAS_WIDTH - this.radius) {
				this.pos.x = CANVAS_WIDTH - this.radius;
				this.oldpos.x = this.pos.x + vel.x;
			}
			if(this.pos.x < this.radius) {
				this.pos.x = this.radius;
				this.oldpos.x = this.pos.x + vel.x;
			}
			if(this.pos.y > CANVAS_HEIGHT - this.radius) {
				this.pos.y = CANVAS_HEIGHT - this.radius;
				this.oldpos.y = this.pos.y + vel.y;
			}
			if(this.pos.y < this.radius) {
				this.pos.y = this.radius;
				this.oldpos.y = this.pos.y + vel.y;
			}
		};
		render(ctx) {
			ctx.beginPath();
			ctx.fillStyle = this.color;
			ctx.arc(this.pos.x, this.pos.y, this.radius, 0, Math.PI * 2);
			ctx.fill();
			ctx.closePath();
		}
	}
Cloth and Truss Simulation
Particles are link with rigid arm and connected particle must move accordingly with the connected arm. Distance between particles must maintain to be equle with the rest length of arm. In this model arm has no strain.

class Link {
		constructor(p1, p2, length) {
			this.p1 = p1;
			this.p2 = p2;
			this.color = 'f5476a';
			// if the length is not given then calculate the distance based on position
			if(!length) {
				this.length = this.p1.pos.dist(this.p2.pos);
			} else {
				this.length = length;
			}
		}
		update() {
			const dir = SUB(this.p1.pos, this.p2.pos);
			const dist = dir.mag();
			const percent = (this.length - dist) / dist / 2;
			const offset = dir.mult(percent);
			if(!this.p1.fixed) {
				this.p1.pos.add(offset);
			}
			if(!this.p2.fixed) {
				this.p2.pos.sub(offset);
			}
		}
		render(ctx) {
			ctx.beginPath();
			ctx.strokeStyle = this.color;
			ctx.moveTo(this.p1.pos.x, this.p1.pos.y);
			ctx.lineTo(this.p2.pos.x, this.p2.pos.y);
			ctx.stroke();
			ctx.closePath();
		}
	}
Process will run as following function.


function animate() {
		ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
		for(let d of points) {
			d.update();
			d.checkborder();
			d.render(ctx);
		}
		for(let s of links) {
			s.update();
			s.render(ctx);
		}
		requestAnimationFrame(animate);
	}
In this example, 2 objects cloth and truss are created using points and links.

Demo


Author: Win Aung Cho
12-Jun-2022 08:58:11 PM*