A very simple Verlet physics engine...
A Pen by Gerard Ferrandez on CodePen.
<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> |
A very simple Verlet physics engine...
A Pen by Gerard Ferrandez on CodePen.
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; | |
} |