Skip to content

Instantly share code, notes, and snippets.

@lsevero
Created July 20, 2017 11:09
Show Gist options
  • Save lsevero/2474105062ffd6b61dfb53a47364af92 to your computer and use it in GitHub Desktop.
Save lsevero/2474105062ffd6b61dfb53a47364af92 to your computer and use it in GitHub Desktop.
Simple Verlet Physics Engine
<canvas id="canvas"></canvas>
(function () {
"use strict";
// animation loop
var run = function () {
requestAnimationFrame(run);
// clear screen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// integration
nodes.forEach(function (n) {
n.integrate();
n.checkScreenLimits();
})
// dragging
if (drag) {
drag.x += (pointer.x - drag.x) / (drag.mass * 100);
drag.y += (pointer.y - drag.y) / (drag.mass * 100);
ctx.beginPath();
ctx.moveTo(drag.x, drag.y);
ctx.lineTo(pointer.x, pointer.y);
ctx.strokeStyle = "#0f0";
ctx.stroke();
}
// solve constraints
for (var i = 0; i < 5; i++) {
constraints.forEach(function (n) {
n.solve();
});
}
// draw constraints
constraints.forEach(function (n) {
n.draw();
});
// draw nodes
nodes.forEach(function (n) {
n.draw();
})
}
// Node constructor
var Node = function (node) {
this.x = node.x;
this.y = node.y;
this.w = node.w;
this.oldX = node.x;
this.oldY = node.y;
this.mass = node.mass || 1.0;
this.color = node.color;
}
// verlet integration
Node.prototype.integrate = function () {
var x = this.x;
var y = this.y;
this.x += this.x - this.oldX;
this.y += this.y - this.oldY + 0.1;
this.oldX = x;
this.oldY = y;
}
// draw node
Node.prototype.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.w, 0, 2 * Math.PI);
ctx.fillStyle = this.color;
ctx.fill();
// drag
if (pointer.isDown && !drag) {
if (ctx.isPointInPath(pointer.x, pointer.y)) {
drag = this;
}
}
}
Node.prototype.checkScreenLimits = function () {
// bottom + friction
if (this.y > canvas.height - this.w) {
var d = this.y - canvas.height + this.w;
this.x -= d * (this.x - this.oldX) / 10;
this.y = canvas.height - this.w;
}
// top
if (this.y < this.w) this.y = this.w;
// left
if (this.x > canvas.width - this.w) this.x = canvas.width - this.w;
// right
if (this.x < this.w) this.x = this.w;
}
// constraint constructor
var Constraint = function (n0, n1) {
this.n0 = n0;
this.n1 = n1;
var dx = n0.x - n1.x;
var dy = n0.y - n1.y;
this.dist = Math.sqrt(dx * dx + dy * dy);
}
// solve constraint
Constraint.prototype.solve = function () {
var dx = this.n0.x - this.n1.x;
var dy = this.n0.y - this.n1.y;
var currentDist = Math.sqrt(dx * dx + dy * dy);
var delta = 0.5 * (currentDist - this.dist) / currentDist;
dx *= delta;
dy *= delta;
var m1 = (this.n0.mass + this.n1.mass);
var m2 = this.n0.mass / m1;
m1 = this.n1.mass / m1;
this.n1.x += dx * m2;
this.n1.y += dy * m2;
this.n0.x -= dx * m1;
this.n0.y -= dy * m1;
}
// draw constraint
Constraint.prototype.draw = function () {
ctx.beginPath();
ctx.moveTo(this.n0.x, this.n0.y);
ctx.lineTo(this.n1.x, this.n1.y);
ctx.strokeStyle = "#f00";
ctx.stroke();
}
///////////////////////////////////////////////
// prepare the canvas
var canvas = ge1doot.canvas("canvas");
var ctx = canvas.ctx;
var drag = null;
// pointer
var pointer = canvas.pointer;
pointer.up = function () {
drag = null;
}
// def variables
var nodes = [], constraints = [];
// def structure
var init = function (json) {
for (var i = 0; i < json.objects.length; i++) {
var object = json.objects[i];
// load nodes
for (var n in object.nodes) {
var node = new Node(object.nodes[n]);
object.nodes[n].id = node;
nodes.push(node);
}
// define constraints
for (var i = 0; i < object.constraints.length; i++) {
constraints.push(new Constraint(
object.nodes[object.constraints[i][0]].id,
object.nodes[object.constraints[i][1]].id
));
}
}
// start
nodes[0].x += 2;
run();
}
return {
init : init
}
})().init({
// structure
objects: [{
nodes: {
n0: {x:100, y:200, w:30, mass:1.0, color:"#fff"},
n1: {x:300, y:200, w:30, mass:1.0, color:"#fff"},
n2: {x:200, y:0, w:10, mass:0.1, color:"#f00"},
n3: {x:200, y:20, w:10, mass:0.1, color:"#fff"},
n4: {x:200, y:40, w:10, mass:0.1, color:"#fff"},
n5: {x:200, y:60, w:10, mass:0.1, color:"#fff"},
n6: {x:200, y:80, w:10, mass:0.1, color:"#fff"},
n7: {x:200, y:100, w:10, mass:0.1, color:"#fff"},
n8: {x:200, y:140, w:30, mass:1.0, color:"#f00"}
},
constraints: [
["n0", "n1"],
["n1", "n2"],
["n2", "n0"],
["n2", "n3"],
["n3", "n4"],
["n4", "n5"],
["n5", "n6"],
["n6", "n7"],
["n7", "n8"]
]
}]
});
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222599/canvas-pointer.js"></script>
html {
overflow: hidden;
touch-action: none;
content-zooming: none;
}
body {
position: absolute;
margin: 0;
padding: 0;
background: #222;
width: 100%;
height: 100%;
}
#canvas {
position:absolute;
width:100%;
height:100%;
background:#000;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment