├── .gitignore ├── .npmignore ├── demo ├── dev.html ├── line.html ├── triangulate.html ├── draw-triangles.js ├── bounce.js ├── triangulate.js └── line.js ├── 2d.js ├── 3d.js ├── LICENSE.md ├── lib ├── box-collision.js └── build.js ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ -------------------------------------------------------------------------------- /demo/dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/triangulate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /2d.js: -------------------------------------------------------------------------------- 1 | var vec2 = { 2 | create: require('gl-vec2/create'), 3 | add: require('gl-vec2/add'), 4 | multiply: require('gl-vec2/multiply'), 5 | sub: require('gl-vec2/subtract'), 6 | scale: require('gl-vec2/scale'), 7 | copy: require('gl-vec2/copy'), 8 | sqrLen: require('gl-vec2/squaredLength'), 9 | fromValues: require('gl-vec2/fromValues'), 10 | } 11 | module.exports = require('./lib/build')(vec2) -------------------------------------------------------------------------------- /3d.js: -------------------------------------------------------------------------------- 1 | var vec3 = { 2 | create: require('gl-vec3/create'), 3 | add: require('gl-vec3/add'), 4 | multiply: require('gl-vec3/multiply'), 5 | sub: require('gl-vec3/subtract'), 6 | scale: require('gl-vec3/scale'), 7 | copy: require('gl-vec3/copy'), 8 | sqrLen: require('gl-vec3/squaredLength'), 9 | fromValues: require('gl-vec3/fromValues'), 10 | } 11 | module.exports = require('./lib/build')(vec3) -------------------------------------------------------------------------------- /demo/draw-triangles.js: -------------------------------------------------------------------------------- 1 | var vec2 = require('gl-matrix').vec2 2 | 3 | module.exports = function drawTriangles(ctx, positions, cells, start, end) { 4 | var v = positions 5 | start = (start|0) 6 | end = typeof end === 'number' ? (end|0) : cells.length 7 | 8 | for (; start < end && start < cells.length; start++) { 9 | var f = cells[start] 10 | 11 | var v0 = v[f[0]], 12 | v1 = v[f[1]], 13 | v2 = v[f[2]] 14 | 15 | ctx.moveTo(v0[0], v0[1]) 16 | ctx.lineTo(v1[0], v1[1]) 17 | ctx.lineTo(v2[0], v2[1]) 18 | ctx.lineTo(v0[0], v0[1]) 19 | } 20 | } -------------------------------------------------------------------------------- /demo/bounce.js: -------------------------------------------------------------------------------- 1 | require('canvas-testbed')(render, start) 2 | 3 | var Point = require('verlet-point') 4 | var World = require('../') 5 | 6 | var world, 7 | point, 8 | radius = 25 9 | 10 | function render(ctx, width, height, dt) { 11 | ctx.clearRect(0, 0, width, height) 12 | 13 | world.max = [width, height] 14 | world.integratePoint(point, dt/1000) 15 | 16 | ctx.beginPath() 17 | ctx.arc(point.position[0], point.position[1], radius, 0, Math.PI*2, false) 18 | ctx.fill() 19 | } 20 | 21 | function start(ctx, width, height) { 22 | world = World({ 23 | gravity: [500, 500], 24 | bounce: 0.5 //collision friction 25 | }) 26 | 27 | point = Point({ 28 | position: [width/2, height/2], 29 | radius: radius 30 | }) 31 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2014 Matt DesLauriers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /lib/box-collision.js: -------------------------------------------------------------------------------- 1 | module.exports = function(vec) { 2 | var negInfinity = vec.fromValues(-Infinity, -Infinity, -Infinity) 3 | var posInfinity = vec.fromValues(Infinity, Infinity, Infinity) 4 | var ones = vec.fromValues(1, 1, 1) 5 | var reflect = vec.create() 6 | var EPSILON = 0.000001 7 | 8 | return function collider(p, velocity, min, max, friction) { 9 | if (!min && !max) 10 | return 11 | 12 | //reset reflection 13 | vec.copy(reflect, ones) 14 | 15 | min = min || negInfinity 16 | max = max || posInfinity 17 | 18 | var i = 0, 19 | n = p.position.length, 20 | hit = false, 21 | radius = p.radius || 0 22 | 23 | //bounce and clamp 24 | for (i=0; i max[i]) { 32 | reflect[i] = -1 33 | p.position[i] = max[i]-radius 34 | hit = true 35 | } 36 | 37 | //no bounce 38 | var len2 = vec.sqrLen(velocity) 39 | if (!hit || len2 <= EPSILON) 40 | return 41 | 42 | var m = Math.sqrt(len2) 43 | if (m !== 0) 44 | vec.scale(velocity, velocity, 1/m) 45 | 46 | //scale bounce by friction 47 | vec.scale(reflect, reflect, m * friction) 48 | 49 | //bounce back 50 | vec.multiply(velocity, velocity, reflect) 51 | } 52 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verlet-system", 3 | "version": "1.0.15", 4 | "description": "2D and 3D verlet integration", 5 | "main": "2d.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Matt DesLauriers", 9 | "email": "dave.des@gmail.com", 10 | "url": "https://github.com/mattdesl" 11 | }, 12 | "dependencies": { 13 | "as-number": "^1.0.0", 14 | "clamp": "^1.0.1", 15 | "gl-vec2": "^1.0.0", 16 | "gl-vec3": "^1.0.2" 17 | }, 18 | "devDependencies": { 19 | "add-event-listener": "0.0.1", 20 | "array-range": "^1.0.1", 21 | "canvas-testbed": "^1.0.2", 22 | "color-style": "^1.0.0", 23 | "delaunay-triangulate": "^1.1.6", 24 | "lerp": "^1.0.0", 25 | "lerp-array": "^1.0.2", 26 | "randf": "^1.0.0", 27 | "smoothstep": "^1.0.1", 28 | "tape": "^3.0.3", 29 | "touch-position": "^1.0.2", 30 | "verlet-constraint": "^1.0.3", 31 | "verlet-point": "^1.2.1", 32 | "xtend": "^4.0.0" 33 | }, 34 | "scripts": { 35 | "test": "node test.js", 36 | "line": "beefy --open --index=./demo/dev.html ./demo/line.js", 37 | "triangulate": "beefy --open --index=./demo/dev.html ./demo/triangulate.js", 38 | "build-triangulate": "mkdir -p demo/static && browserify demo/triangulate.js | uglifyjs -cm > demo/static/triangulate.js", 39 | "build-line": "mkdir -p demo/static && browserify demo/line.js | uglifyjs -cm > demo/static/line.js" 40 | }, 41 | "keywords": [ 42 | "verlet", 43 | "physics", 44 | "integrate", 45 | "integration" 46 | ], 47 | "repository": { 48 | "type": "git", 49 | "url": "git://github.com/mattdesl/verlet-system.git" 50 | }, 51 | "homepage": "https://github.com/mattdesl/verlet-system", 52 | "bugs": { 53 | "url": "https://github.com/mattdesl/verlet-system/issues" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/build.js: -------------------------------------------------------------------------------- 1 | var number = require('as-number') 2 | var clamp = require('clamp') 3 | var createCollider = require('./box-collision') 4 | 5 | module.exports = function create(vec) { 6 | 7 | var collide = createCollider(vec) 8 | 9 | var velocity = vec.create() 10 | var tmp = vec.create() 11 | var zero = vec.create() 12 | 13 | function VerletSystem(opt) { 14 | if (!(this instanceof VerletSystem)) 15 | return new VerletSystem(opt) 16 | 17 | opt = opt||{} 18 | 19 | this.gravity = opt.gravity || vec.create() 20 | this.friction = number(opt.friction, 0.98) 21 | this.min = opt.min 22 | this.max = opt.max 23 | this.bounce = number(opt.bounce, 1) 24 | } 25 | 26 | VerletSystem.prototype.collision = function(p, velocity) { 27 | collide(p, velocity, this.min, this.max, this.bounce) 28 | } 29 | 30 | VerletSystem.prototype.integratePoint = function(point, delta) { 31 | var mass = typeof point.mass === 'number' ? point.mass : 1 32 | 33 | //if mass is zero, assume body is static / unmovable 34 | if (mass === 0) { 35 | this.collision(point, zero) 36 | vec.copy(point.acceleration, zero) 37 | return 38 | } 39 | 40 | vec.add(point.acceleration, point.acceleration, this.gravity) 41 | vec.scale(point.acceleration, point.acceleration, mass) 42 | 43 | //difference in positions 44 | vec.sub(velocity, point.position, point.previous) 45 | 46 | //dampen velocity 47 | vec.scale(velocity, velocity, this.friction) 48 | 49 | //handle custom collisions in 2D or 3D space 50 | this.collision(point, velocity) 51 | 52 | //set last position 53 | vec.copy(point.previous, point.position) 54 | var tSqr = delta * delta 55 | 56 | //integrate 57 | vec.scale(tmp, point.acceleration, 0.5 * tSqr) 58 | vec.add(point.position, point.position, velocity) 59 | vec.add(point.position, point.position, tmp) 60 | 61 | //reset acceleration 62 | vec.copy(point.acceleration, zero) 63 | } 64 | 65 | VerletSystem.prototype.integrate = function(points, delta) { 66 | for (var i=0; i