diff --git a/.gitignore b/.gitignore index e8fa5d7d..c4cc9421 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ matter-doc-theme build/matter-dev.js build/matter-dev.min.js demo/js/lib/matter-dev.js +demo/js/Examples.js test/browser/diffs test/node/diffs \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index 3d840f14..f3229122 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -38,6 +38,12 @@ module.exports = function(grunt) { dest: 'build/<%= buildName %>.js' } }, + concat: { + examples: { + src: 'examples/**/*.js', + dest: 'demo/js/Examples.js' + } + }, copy: { demo: { src: 'build/<%= buildName %>.js', @@ -48,7 +54,7 @@ module.exports = function(grunt) { options: { jshintrc: '.jshintrc' }, - all: ['src/**/*.js', 'demo/js/*.js', 'test/browser/TestDemo.js', 'test/node/TestDemo.js', '!src/module/*'] + all: ['src/**/*.js', 'demo/js/*.js', 'examples/*.js', 'test/browser/TestDemo.js', 'test/node/TestDemo.js', '!src/module/*', '!demo/js/Examples.js'] }, connect: { watch: { @@ -76,6 +82,10 @@ module.exports = function(grunt) { }, demo: { files: ['build/matter.js', 'demo/js/**/*.html', 'demo/js/**/*.js', 'demo/css/**/*.css'] + }, + examples: { + files: ['examples/**/*.js'], + tasks: ['concat:examples'] } }, yuidoc: { @@ -131,6 +141,7 @@ module.exports = function(grunt) { }); grunt.loadNpmTasks('grunt-browserify'); + grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-watch'); @@ -140,7 +151,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-preprocess'); grunt.loadNpmTasks('grunt-shell'); - grunt.registerTask('default', ['test', 'build']); + grunt.registerTask('default', ['concat:examples', 'test', 'build']); grunt.registerTask('test', ['build:dev', 'connect:serve', 'jshint', 'test:demo', 'test:demoNode']); grunt.registerTask('dev', ['build:dev', 'connect:watch', 'watch']); diff --git a/demo/dev.html b/demo/dev.html index 98267ad5..fb66f7ed 100644 --- a/demo/dev.html +++ b/demo/dev.html @@ -23,6 +23,7 @@ + Matter.js Demo (Dev. Build) diff --git a/demo/js/Demo.js b/demo/js/Demo.js index 2dde49c3..db3eef44 100644 --- a/demo/js/Demo.js +++ b/demo/js/Demo.js @@ -1,6 +1,6 @@ (function() { - var _isBrowser = typeof window !== 'undefined', + var _isBrowser = typeof window !== 'undefined' && window.location, Matter = _isBrowser ? window.Matter : require('../../build/matter-dev.js'); var Demo = {}; @@ -12,22 +12,12 @@ } // Matter aliases - var Engine = Matter.Engine, - World = Matter.World, - Bodies = Matter.Bodies, - Body = Matter.Body, - Composite = Matter.Composite, - Composites = Matter.Composites, + var Example = Matter.Example, + Engine = Matter.Engine, + World = Matter.World Common = Matter.Common, - Constraint = Matter.Constraint, - Events = Matter.Events, - Bounds = Matter.Bounds, - Vector = Matter.Vector, - Vertices = Matter.Vertices, - MouseConstraint = Matter.MouseConstraint, - Mouse = Matter.Mouse, - Query = Matter.Query, - Svg = Matter.Svg; + Bodies = Matter.Bodies, + Events = Matter.Events; // MatterTools aliases if (window.MatterTools) { @@ -48,6 +38,16 @@ // initialise the demo + Demo.create = function() { + return { + engine: _engine, + runner: _runner, + mouseConstraint: _mouseConstraint, + sceneEvents: _sceneEvents, + isMobile: _isMobile + }; + }; + Demo.init = function() { // some example engine options var options = { @@ -89,7 +89,8 @@ _sceneName = window.location.hash.replace('#', '').replace('-inspect', ''); // set up a scene with bodies - Demo[_sceneName](); + Demo.reset(); + Example[_sceneName](Demo.create()); // set up demo interface (see end of this file) Demo.initControls(); @@ -103,1524 +104,6 @@ window.attachEvent('load', Demo.init); } - // each demo scene is set up in its own function, see below - - Demo.mixed = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(20, 20, 15, 4, 0, 0, function(x, y, column, row) { - var sides = Math.round(Common.random(1, 8)); - - // triangles can be a little unstable, so avoid until fixed - sides = (sides === 3) ? 4 : sides; - - // round the edges of some bodies - var chamfer = null; - if (sides > 2 && Common.random() > 0.7) { - chamfer = { - radius: 10 - }; - } - - switch (Math.round(Common.random(0, 1))) { - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(25, 50), Common.random(25, 50), { chamfer: chamfer }); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(25, 30), { chamfer: chamfer }); - } - break; - case 1: - return Bodies.polygon(x, y, sides, Common.random(25, 50), { chamfer: chamfer }); - } - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - }; - - Demo.compound = function() { - var _world = _engine.world; - - Demo.reset(); - - var size = 200, - x = 200, - y = 200, - partA = Bodies.rectangle(x, y, size, size / 5), - partB = Bodies.rectangle(x, y, size / 5, size, { render: partA.render }); - - var compoundBodyA = Body.create({ - parts: [partA, partB] - }); - - size = 150; - x = 400; - y = 300; - - var partC = Bodies.circle(x, y, 30), - partD = Bodies.circle(x + size, y, 30), - partE = Bodies.circle(x + size, y + size, 30), - partF = Bodies.circle(x, y + size, 30); - - var compoundBodyB = Body.create({ - parts: [partC, partD, partE, partF] - }); - - var constraint = Constraint.create({ - pointA: { x: 400, y: 100 }, - bodyB: compoundBodyB, - pointB: { x: 0, y: -50 } - }); - - World.add(_world, [compoundBodyA, compoundBodyB, constraint]); - - var renderOptions = _engine.render.options; - renderOptions.showAxes = true; - renderOptions.showPositions = true; - renderOptions.showConvexHulls = true; - }; - - Demo.compoundStack = function() { - var _world = _engine.world; - - Demo.reset(); - - var size = 50; - - var stack = Composites.stack(100, 220, 12, 6, 0, 0, function(x, y, column, row) { - var partA = Bodies.rectangle(x, y, size, size / 5), - partB = Bodies.rectangle(x, y, size / 5, size, { render: partA.render }); - - return Body.create({ - parts: [partA, partB] - }); - }); - - World.add(_world, stack); - }; - - Demo.concave = function() { - var _world = _engine.world; - - Demo.reset(); - - var arrow = Vertices.fromPath('40 0 40 20 100 20 100 80 40 80 40 100 0 50'), - chevron = Vertices.fromPath('100 0 75 50 100 100 25 100 0 50 25 0'), - star = Vertices.fromPath('50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38'), - horseShoe = Vertices.fromPath('35 7 19 17 14 38 14 58 25 79 45 85 65 84 65 66 46 67 34 59 30 44 33 29 45 23 66 23 66 7 53 7'); - - var stack = Composites.stack(50, 50, 6, 4, 10, 10, function(x, y, column, row) { - var color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); - return Bodies.fromVertices(x, y, Common.choose([arrow, chevron, star, horseShoe]), { - render: { - fillStyle: color, - strokeStyle: color - } - }, true); - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - }; - - Demo.svg = function() { - var _world = _engine.world; - - Demo.reset(); - - var svgs = [ - 'iconmonstr-check-mark-8-icon', - 'iconmonstr-paperclip-2-icon', - 'iconmonstr-puzzle-icon', - 'iconmonstr-user-icon' - ]; - - for (var i = 0; i < svgs.length; i += 1) { - (function(i) { - $.get('./svg/' + svgs[i] + '.svg').done(function(data) { - var vertexSets = [], - color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); - - $(data).find('path').each(function(i, path) { - var points = Svg.pathToVertices(path, 30); - vertexSets.push(Vertices.scale(points, 0.4, 0.4)); - }); - - World.add(_world, Bodies.fromVertices(100 + i * 150, 200 + i * 50, vertexSets, { - render: { - fillStyle: color, - strokeStyle: color - } - }, true)); - }); - })(i); - } - - $.get('./svg/svg.svg').done(function(data) { - var vertexSets = [], - color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); - - $(data).find('path').each(function(i, path) { - vertexSets.push(Svg.pathToVertices(path, 30)); - }); - - World.add(_world, Bodies.fromVertices(400, 80, vertexSets, { - render: { - fillStyle: color, - strokeStyle: color - } - }, true)); - }); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - }; - - Demo.terrain = function() { - var _world = _engine.world; - - Demo.reset(); - _world.bodies = []; - - var terrain; - - $.get('./svg/terrain.svg').done(function(data) { - var vertexSets = [], - color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); - - $(data).find('path').each(function(i, path) { - vertexSets.push(Svg.pathToVertices(path, 30)); - }); - - terrain = Bodies.fromVertices(400, 350, vertexSets, { - isStatic: true, - render: { - fillStyle: color, - strokeStyle: color - } - }, true); - - World.add(_world, terrain); - - var bodyOptions = { - frictionAir: 0, - friction: 0.0001, - restitution: 0.6 - }; - - World.add(_world, Composites.stack(80, 100, 20, 20, 10, 10, function(x, y, column, row) { - if (Query.point([terrain], { x: x, y: y }).length === 0) { - return Bodies.polygon(x, y, 5, 12, bodyOptions); - } - })); - }); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - renderOptions.showVelocity = true; - }; - - Demo.slingshot = function() { - var _world = _engine.world; - - Demo.reset(); - _world.bodies = []; - - var ground = Bodies.rectangle(395, 600, 815, 50, { isStatic: true, render: { visible: false } }), - rockOptions = { density: 0.004, render: { sprite: { texture: './img/rock.png' } } }, - rock = Bodies.polygon(170, 450, 8, 20, rockOptions), - anchor = { x: 170, y: 450 }, - elastic = Constraint.create({ - pointA: anchor, - bodyB: rock, - stiffness: 0.05, - render: { - lineWidth: 5, - strokeStyle: '#dfa417' - } - }); - - var pyramid = Composites.pyramid(500, 300, 9, 10, 0, 0, function(x, y, column, row) { - var texture = column % 2 === 0 ? './img/block.png' : './img/block-2.png'; - return Bodies.rectangle(x, y, 25, 40, { render: { sprite: { texture: texture } } }); - }); - - var ground2 = Bodies.rectangle(610, 250, 200, 20, { - isStatic: true, - render: { - fillStyle: '#edc51e', - strokeStyle: '#b5a91c' - } - }); - - var pyramid2 = Composites.pyramid(550, 0, 5, 10, 0, 0, function(x, y, column, row) { - var texture = column % 2 === 0 ? './img/block.png' : './img/block-2.png'; - return Bodies.rectangle(x, y, 25, 40, { render: { sprite: { texture: texture } } }); - }); - - World.add(_engine.world, [ground, pyramid, ground2, pyramid2, rock, elastic]); - - Events.on(_engine, 'afterUpdate', function() { - if (_mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) { - rock = Bodies.polygon(170, 450, 7, 20, rockOptions); - World.add(_engine.world, rock); - elastic.bodyB = rock; - } - }); - - var renderOptions = _engine.render.options; - renderOptions.wireframes = false; - renderOptions.showAngleIndicator = false; - renderOptions.background = './img/background.png'; - }; - - Demo.rounded = function() { - var _world = _engine.world; - - Demo.reset(); - - World.add(_world, [ - Bodies.rectangle(200, 200, 100, 100, { - chamfer: { radius: 20 } - }), - - Bodies.rectangle(300, 200, 100, 100, { - chamfer: { radius: [90, 0, 0, 0] } - }), - - Bodies.rectangle(400, 200, 200, 200, { - chamfer: { radius: [150, 20, 40, 20] } - }), - - Bodies.rectangle(200, 200, 200, 200, { - chamfer: { radius: [150, 20, 150, 20] } - }), - - Bodies.rectangle(300, 200, 200, 50, { - chamfer: { radius: [25, 25, 0, 0] } - }), - - Bodies.polygon(200, 100, 8, 80, { - chamfer: { radius: 30 } - }), - - Bodies.polygon(300, 100, 5, 80, { - chamfer: { radius: [10, 40, 20, 40, 10] } - }), - - Bodies.polygon(400, 200, 3, 50, { - chamfer: { radius: [20, 0, 20] } - }) - ]); - - var renderOptions = _engine.render.options; - renderOptions.showAxes = true; - renderOptions.showCollisions = true; - }; - - Demo.manipulation = function() { - var _world = _engine.world; - - Demo.reset(); - - var bodyA = Bodies.rectangle(100, 200, 50, 50, { isStatic: true }), - bodyB = Bodies.rectangle(200, 200, 50, 50), - bodyC = Bodies.rectangle(300, 200, 50, 50), - bodyD = Bodies.rectangle(400, 200, 50, 50), - bodyE = Bodies.rectangle(550, 200, 50, 50), - bodyF = Bodies.rectangle(700, 200, 50, 50), - bodyG = Bodies.circle(400, 100, 25), - partA = Bodies.rectangle(600, 200, 120, 50), - partB = Bodies.rectangle(660, 200, 50, 190), - compound = Body.create({ - parts: [partA, partB], - isStatic: true - }); - - World.add(_world, [bodyA, bodyB, bodyC, bodyD, bodyE, bodyF, bodyG, compound]); - - var counter = 0, - scaleFactor = 1.01; - - _sceneEvents.push( - Events.on(_engine, 'beforeUpdate', function(event) { - counter += 1; - - if (counter === 40) - Body.setStatic(bodyG, true); - - if (scaleFactor > 1) { - Body.scale(bodyF, scaleFactor, scaleFactor); - Body.scale(compound, 0.995, 0.995); - - // modify bodyE vertices - bodyE.vertices[0].x -= 0.2; - bodyE.vertices[0].y -= 0.2; - bodyE.vertices[1].x += 0.2; - bodyE.vertices[1].y -= 0.2; - Body.setVertices(bodyE, bodyE.vertices); - } - - // make bodyA move up and down - // body is static so must manually update velocity for friction to work - var py = 300 + 100 * Math.sin(_engine.timing.timestamp * 0.002); - Body.setVelocity(bodyA, { x: 0, y: py - bodyA.position.y }); - Body.setPosition(bodyA, { x: 100, y: py }); - - // make compound body move up and down and rotate constantly - Body.setVelocity(compound, { x: 0, y: py - compound.position.y }); - Body.setAngularVelocity(compound, 0.02); - Body.setPosition(compound, { x: 600, y: py }); - Body.rotate(compound, 0.02); - - // every 1.5 sec - if (counter >= 60 * 1.5) { - Body.setVelocity(bodyB, { x: 0, y: -10 }); - Body.setAngle(bodyC, -Math.PI * 0.26); - Body.setAngularVelocity(bodyD, 0.2); - - // reset counter - counter = 0; - scaleFactor = 1; - } - }) - ); - - var renderOptions = _engine.render.options; - renderOptions.showAxes = true; - renderOptions.showCollisions = true; - renderOptions.showPositions = true; - renderOptions.showConvexHulls = true; - }; - - Demo.compositeManipulation = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(200, 200, 4, 4, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, 40, 40); - }); - - World.add(_world, stack); - - _world.gravity.y = 0; - - _sceneEvents.push( - Events.on(_engine, 'afterUpdate', function(event) { - var time = _engine.timing.timestamp; - - Composite.translate(stack, { - x: Math.sin(time * 0.001) * 2, - y: 0 - }); - - Composite.rotate(stack, Math.sin(time * 0.001) * 0.01, { - x: 300, - y: 300 - }); - - var scale = 1 + (Math.sin(time * 0.001) * 0.01); - - Composite.scale(stack, scale, scale, { - x: 300, - y: 300 - }); - }) - ); - - var renderOptions = _engine.render.options; - renderOptions.wireframes = false; - renderOptions.showAxes = true; - renderOptions.showCollisions = true; - }; - - Demo.views = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(20, 20, 15, 4, 0, 0, function(x, y, column, row) { - switch (Math.round(Common.random(0, 1))) { - - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); - } - break; - case 1: - var sides = Math.round(Common.random(1, 8)); - sides = (sides === 3) ? 4 : sides; - return Bodies.polygon(x, y, sides, Common.random(20, 50)); - } - }); - - World.add(_world, stack); - - // get the centre of the viewport - var viewportCentre = { - x: _engine.render.options.width * 0.5, - y: _engine.render.options.height * 0.5 - }; - - // make the world bounds a little bigger than the render bounds - _world.bounds.min.x = -300; - _world.bounds.min.y = -300; - _world.bounds.max.x = 1100; - _world.bounds.max.y = 900; - - // keep track of current bounds scale (view zoom) - var boundsScaleTarget = 1, - boundsScale = { - x: 1, - y: 1 - }; - - // use the engine tick event to control our view - _sceneEvents.push( - Events.on(_engine, 'beforeTick', function() { - var world = _engine.world, - mouse = _mouseConstraint.mouse, - render = _engine.render, - translate; - - // mouse wheel controls zoom - var scaleFactor = mouse.wheelDelta * -0.1; - if (scaleFactor !== 0) { - if ((scaleFactor < 0 && boundsScale.x >= 0.6) || (scaleFactor > 0 && boundsScale.x <= 1.4)) { - boundsScaleTarget += scaleFactor; - } - } - - // if scale has changed - if (Math.abs(boundsScale.x - boundsScaleTarget) > 0.01) { - // smoothly tween scale factor - scaleFactor = (boundsScaleTarget - boundsScale.x) * 0.2; - boundsScale.x += scaleFactor; - boundsScale.y += scaleFactor; - - // scale the render bounds - render.bounds.max.x = render.bounds.min.x + render.options.width * boundsScale.x; - render.bounds.max.y = render.bounds.min.y + render.options.height * boundsScale.y; - - // translate so zoom is from centre of view - translate = { - x: render.options.width * scaleFactor * -0.5, - y: render.options.height * scaleFactor * -0.5 - }; - - Bounds.translate(render.bounds, translate); - - // update mouse - Mouse.setScale(mouse, boundsScale); - Mouse.setOffset(mouse, render.bounds.min); - } - - // get vector from mouse relative to centre of viewport - var deltaCentre = Vector.sub(mouse.absolute, viewportCentre), - centreDist = Vector.magnitude(deltaCentre); - - // translate the view if mouse has moved over 50px from the centre of viewport - if (centreDist > 50) { - // create a vector to translate the view, allowing the user to control view speed - var direction = Vector.normalise(deltaCentre), - speed = Math.min(10, Math.pow(centreDist - 50, 2) * 0.0002); - - translate = Vector.mult(direction, speed); - - // prevent the view moving outside the world bounds - if (render.bounds.min.x + translate.x < world.bounds.min.x) - translate.x = world.bounds.min.x - render.bounds.min.x; - - if (render.bounds.max.x + translate.x > world.bounds.max.x) - translate.x = world.bounds.max.x - render.bounds.max.x; - - if (render.bounds.min.y + translate.y < world.bounds.min.y) - translate.y = world.bounds.min.y - render.bounds.min.y; - - if (render.bounds.max.y + translate.y > world.bounds.max.y) - translate.y = world.bounds.max.y - render.bounds.max.y; - - // move the view - Bounds.translate(render.bounds, translate); - - // we must update the mouse too - Mouse.setOffset(mouse, render.bounds.min); - } - }) - ); - - // must enable renderOptions.hasBounds for views to work - var renderOptions = _engine.render.options; - renderOptions.hasBounds = true; - }; - - Demo.mixedSolid = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(50, 50, 12, 3, 0, 0, function(x, y, column, row) { - switch (Math.round(Common.random(0, 1))) { - - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); - } - break; - case 1: - return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); - - } - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - renderOptions.wireframes = false; - renderOptions.showAngleIndicator = false; - }; - - Demo.chains = function() { - var _world = _engine.world, - group = Body.nextGroup(true); - - Demo.reset(); - - var ropeA = Composites.stack(200, 100, 5, 2, 10, 10, function(x, y, column, row) { - return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { group: group } }); - }); - - Composites.chain(ropeA, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 }); - Composite.add(ropeA, Constraint.create({ - bodyB: ropeA.bodies[0], - pointB: { x: -25, y: 0 }, - pointA: { x: 200, y: 100 }, - stiffness: 0.5 - })); - - World.add(_world, ropeA); - - group = Body.nextGroup(true); - - var ropeB = Composites.stack(500, 100, 5, 2, 10, 10, function(x, y, column, row) { - return Bodies.circle(x, y, 20, { collisionFilter: { group: group } }); - }); - - Composites.chain(ropeB, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 }); - Composite.add(ropeB, Constraint.create({ - bodyB: ropeB.bodies[0], - pointB: { x: -20, y: 0 }, - pointA: { x: 500, y: 100 }, - stiffness: 0.5 - })); - - World.add(_world, ropeB); - }; - - Demo.bridge = function() { - var _world = _engine.world, - group = Body.nextGroup(true); - - Demo.reset(); - - var bridge = Composites.stack(150, 300, 9, 1, 10, 10, function(x, y, column, row) { - return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { group: group } }); - }); - - Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 }); - - var stack = Composites.stack(200, 40, 6, 3, 0, 0, function(x, y, column, row) { - return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 40)); - }); - - World.add(_world, [ - bridge, - Bodies.rectangle(80, 440, 120, 280, { isStatic: true }), - Bodies.rectangle(720, 440, 120, 280, { isStatic: true }), - Constraint.create({ pointA: { x: 140, y: 300 }, bodyB: bridge.bodies[0], pointB: { x: -25, y: 0 } }), - Constraint.create({ pointA: { x: 660, y: 300 }, bodyB: bridge.bodies[8], pointB: { x: 25, y: 0 } }), - stack - ]); - }; - - Demo.car = function() { - var _world = _engine.world, - scale; - - Demo.reset(); - - scale = 0.9; - World.add(_world, Composites.car(150, 100, 100 * scale, 40 * scale, 30 * scale)); - - scale = 0.8; - World.add(_world, Composites.car(350, 300, 100 * scale, 40 * scale, 30 * scale)); - - World.add(_world, [ - Bodies.rectangle(200, 150, 650, 20, { isStatic: true, angle: Math.PI * 0.06 }), - Bodies.rectangle(500, 350, 650, 20, { isStatic: true, angle: -Math.PI * 0.06 }), - Bodies.rectangle(340, 580, 700, 20, { isStatic: true, angle: Math.PI * 0.04 }) - ]); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = true; - renderOptions.showCollisions = true; - }; - - Demo.friction = function() { - var _world = _engine.world; - - Demo.reset(); - - World.add(_world, [ - Bodies.rectangle(300, 180, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), - Bodies.rectangle(300, 70, 40, 40, { friction: 0.001 }) - ]); - - World.add(_world, [ - Bodies.rectangle(300, 350, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), - Bodies.rectangle(300, 250, 40, 40, { friction: 0.0005 }) - ]); - - World.add(_world, [ - Bodies.rectangle(300, 520, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), - Bodies.rectangle(300, 430, 40, 40, { friction: 0 }) - ]); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - renderOptions.showVelocity = true; - }; - - Demo.airFriction = function() { - var _world = _engine.world; - - Demo.reset(); - - World.add(_world, [ - Bodies.rectangle(200, 100, 60, 60, { frictionAir: 0.001 }), - Bodies.rectangle(400, 100, 60, 60, { frictionAir: 0.05 }), - Bodies.rectangle(600, 100, 60, 60, { frictionAir: 0.1 }) - ]); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - renderOptions.showVelocity = true; - }; - - Demo.staticFriction = function() { - var _world = _engine.world; - - Demo.reset(); - - var body = Bodies.rectangle(400, 500, 200, 60, { isStatic: true, chamfer: 10 }), - size = 50, - counter = -1; - - var stack = Composites.stack(350, 470 - 6 * size, 1, 6, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, size * 2, size, { - slop: 0.5, - friction: 1, - frictionStatic: Infinity - }); - }); - - World.add(_world, [body, stack]); - - _sceneEvents.push( - Events.on(_engine, 'beforeUpdate', function(event) { - counter += 0.014; - - if (counter < 0) { - return; - } - - var px = 400 + 100 * Math.sin(counter); - - // body is static so must manually update velocity for friction to work - Body.setVelocity(body, { x: px - body.position.x, y: 0 }); - Body.setPosition(body, { x: px, y: body.position.y }); - }) - ); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - renderOptions.showVelocity = true; - }; - - Demo.sleeping = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(50, 50, 12, 3, 0, 0, function(x, y, column, row) { - switch (Math.round(Common.random(0, 1))) { - - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); - } - break; - case 1: - return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); - - } - }); - - World.add(_world, stack); - - for (var i = 0; i < stack.bodies.length; i++) { - Events.on(stack.bodies[i], 'sleepStart sleepEnd', function(event) { - var body = this; - console.log('body id', body.id, 'sleeping:', body.isSleeping); - }); - } - - _engine.enableSleeping = true; - }; - - Demo.broadphase = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(20, 20, 20, 5, 0, 0, function(x, y, column, row) { - switch (Math.round(Common.random(0, 1))) { - - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); - } - break; - case 1: - return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); - - } - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - renderOptions.showBroadphase = true; - }; - - Demo.gravity = function() { - var _world = _engine.world; - - Demo.reset(); - - _engine.world.gravity.y = -1; - - var stack = Composites.stack(20, 20, 20, 5, 0, 0, function(x, y, column, row) { - switch (Math.round(Common.random(0, 1))) { - - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); - } - break; - case 1: - return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); - - } - }); - - World.add(_world, stack); - }; - - Demo.avalanche = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(20, 20, 20, 5, 0, 0, function(x, y, column, row) { - return Bodies.circle(x, y, Common.random(10, 20), { friction: 0.00001, restitution: 0.5, density: 0.001 }); - }); - - World.add(_world, stack); - - World.add(_world, [ - Bodies.rectangle(200, 150, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), - Bodies.rectangle(500, 350, 700, 20, { isStatic: true, angle: -Math.PI * 0.06 }), - Bodies.rectangle(340, 580, 700, 20, { isStatic: true, angle: Math.PI * 0.04 }) - ]); - }; - - Demo.newtonsCradle = function() { - var _world = _engine.world; - - Demo.reset(); - - var cradle = Composites.newtonsCradle(280, 100, 5, 30, 200); - World.add(_world, cradle); - Body.translate(cradle.bodies[0], { x: -180, y: -100 }); - - cradle = Composites.newtonsCradle(280, 380, 7, 20, 140); - World.add(_world, cradle); - Body.translate(cradle.bodies[0], { x: -140, y: -100 }); - - var renderOptions = _engine.render.options; - renderOptions.showVelocity = true; - }; - - Demo.timescale = function() { - var _world = _engine.world; - - Demo.reset(); - - var explosion = function(engine) { - var bodies = Composite.allBodies(engine.world); - - for (var i = 0; i < bodies.length; i++) { - var body = bodies[i]; - - if (!body.isStatic && body.position.y >= 500) { - var forceMagnitude = 0.04 * body.mass; - - Body.applyForce(body, { x: 0, y: 0 }, { - x: (forceMagnitude + Common.random() * forceMagnitude) * Common.choose([1, -1]), - y: -forceMagnitude + Common.random() * -forceMagnitude - }); - } - } - }; - - var timeScaleTarget = 1, - counter = 0; - - _sceneEvents.push( - Events.on(_engine, 'afterUpdate', function(event) { - // tween the timescale for bullet time slow-mo - _engine.timing.timeScale += (timeScaleTarget - _engine.timing.timeScale) * 0.05; - - counter += 1; - - // every 1.5 sec - if (counter >= 60 * 1.5) { - - // flip the timescale - if (timeScaleTarget < 1) { - timeScaleTarget = 1; - } else { - timeScaleTarget = 0.05; - } - - // create some random forces - explosion(_engine); - - // reset counter - counter = 0; - } - }) - ); - - var bodyOptions = { - frictionAir: 0, - friction: 0.0001, - restitution: 0.8 - }; - - // add some small bouncy circles... remember Swordfish? - World.add(_world, Composites.stack(20, 100, 15, 3, 20, 40, function(x, y, column, row) { - return Bodies.circle(x, y, Common.random(10, 20), bodyOptions); - })); - - // add some larger random bouncy objects - World.add(_world, Composites.stack(50, 50, 8, 3, 0, 0, function(x, y, column, row) { - switch (Math.round(Common.random(0, 1))) { - - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50), bodyOptions); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30), bodyOptions); - } - break; - case 1: - return Bodies.polygon(x, y, Math.round(Common.random(4, 8)), Common.random(20, 50), bodyOptions); - - } - })); - }; - - Demo.stack = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(100, 300, 10, 5, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, 40, 40); - }); - - World.add(_world, stack); - }; - - Demo.circleStack = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(100, 100, 10, 10, 20, 0, function(x, y, column, row) { - return Bodies.circle(x, y, 20); - }); - - World.add(_world, stack); - }; - - Demo.wreckingBall = function() { - var _world = _engine.world; - - Demo.reset(); - - var rows = 10, - yy = 600 - 21 - 40 * rows; - - var stack = Composites.stack(400, yy, 5, rows, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, 40, 40); - }); - - World.add(_world, stack); - - var ball = Bodies.circle(100, 400, 50, { density: 0.04, frictionAir: 0.005}); - - World.add(_world, ball); - World.add(_world, Constraint.create({ - pointA: { x: 300, y: 100 }, - bodyB: ball - })); - - if (!_isMobile) { - var renderOptions = _engine.render.options; - renderOptions.showCollisions = true; - renderOptions.showVelocity = true; - } - }; - - Demo.ballPool = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(100, 50, 10, 15, 10, 10, function(x, y, column, row) { - return Bodies.circle(x, y, Common.random(15, 30), { restitution: 0.6, friction: 0.1 }); - }); - - World.add(_world, [ - stack, - Bodies.polygon(200, 560, 3, 60), - Bodies.polygon(400, 560, 5, 60), - Bodies.rectangle(600, 560, 80, 80) - ]); - }; - - Demo.stress = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(90, 50, 18, 15, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, 35, 35); - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - }; - - Demo.stress2 = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(100, 120, 25, 18, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, 25, 25); - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - }; - - Demo.pyramid = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.pyramid(100, 258, 15, 10, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, 40, 40); - }); - - World.add(_world, stack); - }; - - Demo.restitution = function() { - var _world = _engine.world; - - Demo.reset(); - - var rest = 0.9, - space = 600 / 5; - - World.add(_world, [ - Bodies.rectangle(100 + space * 0, 150, 50, 50, { restitution: rest }), - Bodies.rectangle(100 + space * 1, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.15 }), - Bodies.rectangle(100 + space * 2, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.25 }), - Bodies.circle(100 + space * 3, 150, 25, { restitution: rest }), - Bodies.rectangle(100 + space * 5, 150, 180, 20, { restitution: rest, angle: -Math.PI * 0.5 }) - ]); - - var renderOptions = _engine.render.options; - renderOptions.showCollisions = true; - renderOptions.showVelocity = true; - renderOptions.showAngleIndicator = true; - }; - - Demo.softBody = function() { - var _world = _engine.world; - - Demo.reset(); - - var particleOptions = { - friction: 0.05, - frictionStatic: 0.1, - render: { visible: true } - }; - - World.add(_world, [ - Composites.softBody(250, 100, 5, 5, 0, 0, true, 18, particleOptions), - Composites.softBody(250, 300, 8, 3, 0, 0, true, 15, particleOptions), - Composites.softBody(250, 400, 4, 4, 0, 0, true, 15, particleOptions) - ]); - - var renderOptions = _engine.render.options; - renderOptions.showAngleIndicator = false; - }; - - Demo.cloth = function() { - var _world = _engine.world; - - Demo.reset(); - - var group = Body.nextGroup(true), - particleOptions = { friction: 0.00001, collisionFilter: { group: group }, render: { visible: false }}, - cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions); - - for (var i = 0; i < 20; i++) { - cloth.bodies[i].isStatic = true; - } - - World.add(_world, [ - cloth, - Bodies.circle(300, 500, 80, { isStatic: true }), - Bodies.rectangle(500, 480, 80, 80, { isStatic: true }) - ]); - }; - - Demo.catapult = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(250, 255, 1, 6, 0, 0, function(x, y, column, row) { - return Bodies.rectangle(x, y, 30, 30); - }); - - var catapult = Bodies.rectangle(400, 520, 320, 20, { }); - - World.add(_world, [ - stack, - catapult, - Bodies.rectangle(250, 555, 20, 50, { isStatic: true }), - Bodies.circle(560, 100, 50, { density: 0.005 }), - Constraint.create({ bodyA: catapult, pointB: { x: 390, y: 580 } }), - Constraint.create({ bodyA: catapult, pointB: { x: 410, y: 580 } }) - ]); - - var renderOptions = _engine.render.options; - renderOptions.showCollisions = true; - renderOptions.showVelocity = true; - renderOptions.showAngleIndicator = true; - }; - - Demo.beachBalls = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(0, 100, 5, 1, 20, 0, function(x, y, column, row) { - return Bodies.circle(x, y, 75, { restitution: 0.9 }); - }); - - World.add(_world, stack); - }; - - Demo.events = function() { - var _world = _engine.world; - - Demo.reset(); - - // bind events (_sceneEvents is only used for this demo) - - _sceneEvents.push( - - // an example of using composite events on the world - Events.on(_world, 'afterAdd', function(event) { - console.log('added to world:', event.object); - }) - - ); - - _sceneEvents.push( - - // an example of using beforeUpdate event on an engine - Events.on(_engine, 'beforeUpdate', function(event) { - var engine = event.source; - - // apply random forces every 5 secs - if (event.timestamp % 5000 < 50) - shakeScene(engine); - }) - - ); - - _sceneEvents.push( - - // an example of using collisionStart event on an engine - Events.on(_engine, 'collisionStart', function(event) { - var pairs = event.pairs; - - // change object colours to show those starting a collision - for (var i = 0; i < pairs.length; i++) { - var pair = pairs[i]; - pair.bodyA.render.fillStyle = '#bbbbbb'; - pair.bodyB.render.fillStyle = '#bbbbbb'; - } - }) - - ); - - _sceneEvents.push( - - // an example of using collisionActive event on an engine - Events.on(_engine, 'collisionActive', function(event) { - var pairs = event.pairs; - - // change object colours to show those in an active collision (e.g. resting contact) - for (var i = 0; i < pairs.length; i++) { - var pair = pairs[i]; - pair.bodyA.render.fillStyle = '#aaaaaa'; - pair.bodyB.render.fillStyle = '#aaaaaa'; - } - }) - - ); - - _sceneEvents.push( - - // an example of using collisionEnd event on an engine - Events.on(_engine, 'collisionEnd', function(event) { - var pairs = event.pairs; - - // change object colours to show those ending a collision - for (var i = 0; i < pairs.length; i++) { - var pair = pairs[i]; - pair.bodyA.render.fillStyle = '#999999'; - pair.bodyB.render.fillStyle = '#999999'; - } - }) - - ); - - _sceneEvents.push( - - // an example of using mouse events on a mouse - Events.on(_mouseConstraint, 'mousedown', function(event) { - var mousePosition = event.mouse.position; - console.log('mousedown at ' + mousePosition.x + ' ' + mousePosition.y); - _engine.render.options.background = 'cornsilk'; - shakeScene(_engine); - }) - - ); - - _sceneEvents.push( - - // an example of using mouse events on a mouse - Events.on(_mouseConstraint, 'mouseup', function(event) { - var mousePosition = event.mouse.position; - _engine.render.options.background = "white"; - console.log('mouseup at ' + mousePosition.x + ' ' + mousePosition.y); - }) - - ); - - _sceneEvents.push( - - // an example of using mouse events on a mouse - Events.on(_mouseConstraint, 'startdrag', function(event) { - console.log('startdrag', event); - }) - - ); - - _sceneEvents.push( - - // an example of using mouse events on a mouse - Events.on(_mouseConstraint, 'enddrag', function(event) { - console.log('enddrag', event); - }) - - ); - - // scene code - - var stack = Composites.stack(50, 100, 8, 4, 50, 50, function(x, y, column, row) { - return Bodies.circle(x, y, 15, { restitution: 1, render: { strokeStyle: '#777' } }); - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - renderOptions.wireframes = false; - - var shakeScene = function(engine) { - var bodies = Composite.allBodies(engine.world); - - for (var i = 0; i < bodies.length; i++) { - var body = bodies[i]; - - if (!body.isStatic && body.position.y >= 500) { - var forceMagnitude = 0.01 * body.mass; - - Body.applyForce(body, { x: 0, y: 0 }, { - x: (forceMagnitude + Common.random() * forceMagnitude) * Common.choose([1, -1]), - y: -forceMagnitude + Common.random() * -forceMagnitude - }); - } - } - }; - }; - - Demo.sprites = function() { - var _world = _engine.world, - offset = 10, - options = { - isStatic: true, - render: { - visible: false - } - }; - - Demo.reset(); - _world.bodies = []; - - // these static walls will not be rendered in this sprites example, see options - World.add(_world, [ - Bodies.rectangle(400, -offset, 800.5 + 2 * offset, 50.5, options), - Bodies.rectangle(400, 600 + offset, 800.5 + 2 * offset, 50.5, options), - Bodies.rectangle(800 + offset, 300, 50.5, 600.5 + 2 * offset, options), - Bodies.rectangle(-offset, 300, 50.5, 600.5 + 2 * offset, options) - ]); - - var stack = Composites.stack(20, 20, 10, 4, 0, 0, function(x, y, column, row) { - if (Common.random() > 0.35) { - return Bodies.rectangle(x, y, 64, 64, { - render: { - strokeStyle: '#ffffff', - sprite: { - texture: './img/box.png' - } - } - }); - } else { - return Bodies.circle(x, y, 46, { - density: 0.0005, - frictionAir: 0.06, - restitution: 0.3, - friction: 0.01, - render: { - sprite: { - texture: './img/ball.png' - } - } - }); - } - }); - - World.add(_world, stack); - - var renderOptions = _engine.render.options; - renderOptions.background = './img/wall-bg.jpg'; - renderOptions.showAngleIndicator = false; - renderOptions.wireframes = false; - }; - - Demo.raycasting = function() { - var _world = _engine.world; - - Demo.reset(); - - var stack = Composites.stack(20, 20, 15, 4, 0, 0, function(x, y, column, row) { - switch (Math.round(Common.random(0, 1))) { - - case 0: - if (Common.random() < 0.8) { - return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); - } else { - return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); - } - break; - case 1: - var sides = Math.round(Common.random(1, 8)); - sides = (sides === 3) ? 4 : sides; - return Bodies.polygon(x, y, sides, Common.random(20, 50)); - } - }); - - var star = Vertices.fromPath('50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38'), - concave = Bodies.fromVertices(200, 200, star); - - World.add(_world, [stack, concave]); - - _sceneEvents.push( - Events.on(_engine.render, 'afterRender', function() { - var mouse = _mouseConstraint.mouse, - context = _engine.render.context, - bodies = Composite.allBodies(_engine.world), - startPoint = { x: 400, y: 100 }, - endPoint = mouse.position; - - var collisions = Query.ray(bodies, startPoint, endPoint); - - context.beginPath(); - context.moveTo(startPoint.x, startPoint.y); - context.lineTo(endPoint.x, endPoint.y); - if (collisions.length > 0) { - context.strokeStyle = '#fff'; - } else { - context.strokeStyle = '#555'; - } - context.lineWidth = 0.5; - context.stroke(); - - for (var i = 0; i < collisions.length; i++) { - var collision = collisions[i]; - context.rect(collision.bodyA.position.x - 4.5, collision.bodyA.position.y - 4.5, 8, 8); - } - - context.fillStyle = 'rgba(255,165,0,0.7)'; - context.fill(); - }) - ); - }; - - Demo.collisionFiltering = function() { - var _world = _engine.world; - - // define our categories (as bit fields, there are up to 32 available) - var defaultCategory = 0x0001, - redCategory = 0x0002, - greenCategory = 0x0004, - blueCategory = 0x0008; - - var redColor = '#C44D58', - blueColor = '#4ECDC4', - greenColor = '#C7F464'; - - Demo.reset(); - - // create a stack with varying body categories (but these bodies can all collide with each other) - World.add(_world, - Composites.stack(275, 150, 5, 10, 10, 10, function(x, y, column, row) { - var category = redCategory, - color = redColor; - - if (row > 5) { - category = blueCategory; - color = blueColor; - } else if (row > 2) { - category = greenCategory; - color = greenColor; - } - - return Bodies.circle(x, y, 20, { - collisionFilter: { - category: category - }, - render: { - strokeStyle: color, - fillStyle: 'transparent' - } - }); - }) - ); - - // this body will only collide with the walls and the green bodies - World.add(_world, - Bodies.circle(310, 40, 30, { - collisionFilter: { - mask: defaultCategory | greenCategory - }, - render: { - strokeStyle: Common.shadeColor(greenColor, -20), - fillStyle: greenColor - } - }) - ); - - // this body will only collide with the walls and the red bodies - World.add(_world, - Bodies.circle(400, 40, 30, { - collisionFilter: { - mask: defaultCategory | redCategory - }, - render: { - strokeStyle: Common.shadeColor(redColor, -20), - fillStyle: redColor - } - }) - ); - - // this body will only collide with the walls and the blue bodies - World.add(_world, - Bodies.circle(480, 40, 30, { - collisionFilter: { - mask: defaultCategory | blueCategory - }, - render: { - strokeStyle: Common.shadeColor(blueColor, -20), - fillStyle: blueColor - } - }) - ); - - // red category objects should not be draggable with the mouse - _mouseConstraint.collisionFilter.mask = defaultCategory | blueCategory | greenCategory; - - var renderOptions = _engine.render.options; - renderOptions.wireframes = false; - renderOptions.background = '#222'; - renderOptions.showAngleIndicator = false; - }; - // the functions for the demo interface and controls below Demo.initControls = function() { @@ -1695,7 +178,8 @@ demoSelect.value = _sceneName; demoSelect.addEventListener('change', function(e) { - Demo[_sceneName = e.target.value](); + Demo.reset(); + Example[_sceneName = e.target.value](Demo.create()); Gui.update(_gui); var scrollY = window.scrollY; @@ -1704,7 +188,8 @@ }); demoReset.addEventListener('click', function(e) { - Demo[_sceneName](); + Demo.reset(); + Example[_sceneName](Demo.create()); Gui.update(_gui); }); }; diff --git a/demo/js/DemoMobile.js b/demo/js/DemoMobile.js index f89172d5..47abc836 100644 --- a/demo/js/DemoMobile.js +++ b/demo/js/DemoMobile.js @@ -2,14 +2,10 @@ // Matter aliases var Engine = Matter.Engine, - Gui = Matter.Gui, World = Matter.World, Bodies = Matter.Bodies, - Body = Matter.Body, - Composite = Matter.Composite, Composites = Matter.Composites, Common = Matter.Common, - Constraint = Matter.Constraint, MouseConstraint = Matter.MouseConstraint; var Demo = {}; diff --git a/examples/airFriction.js b/examples/airFriction.js new file mode 100644 index 00000000..b46f7dd6 --- /dev/null +++ b/examples/airFriction.js @@ -0,0 +1,31 @@ +var _isBrowser = typeof window !== 'undefined' && window.location, + Matter = _isBrowser ? window.Matter : require('../../build/matter-dev.js'); + +var Example = {}; +Matter.Example = Example; + +if (!_isBrowser) { + module.exports = Example; +} + +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies; + + Example.airFriction = function(demo) { + var engine = demo.engine, + world = engine.world; + + World.add(world, [ + Bodies.rectangle(200, 100, 60, 60, { frictionAir: 0.001 }), + Bodies.rectangle(400, 100, 60, 60, { frictionAir: 0.05 }), + Bodies.rectangle(600, 100, 60, 60, { frictionAir: 0.1 }) + ]); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + renderOptions.showVelocity = true; + }; + +})(); \ No newline at end of file diff --git a/examples/avalanche.js b/examples/avalanche.js new file mode 100644 index 00000000..65079bc8 --- /dev/null +++ b/examples/avalanche.js @@ -0,0 +1,25 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.avalanche = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(20, 20, 20, 5, 0, 0, function(x, y, column, row) { + return Bodies.circle(x, y, Common.random(10, 20), { friction: 0.00001, restitution: 0.5, density: 0.001 }); + }); + + World.add(world, stack); + + World.add(world, [ + Bodies.rectangle(200, 150, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), + Bodies.rectangle(500, 350, 700, 20, { isStatic: true, angle: -Math.PI * 0.06 }), + Bodies.rectangle(340, 580, 700, 20, { isStatic: true, angle: Math.PI * 0.04 }) + ]); + }; + +})(); \ No newline at end of file diff --git a/examples/ballPool.js b/examples/ballPool.js new file mode 100644 index 00000000..8d3a60c8 --- /dev/null +++ b/examples/ballPool.js @@ -0,0 +1,24 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.ballPool = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(100, 50, 10, 15, 10, 10, function(x, y, column, row) { + return Bodies.circle(x, y, Common.random(15, 30), { restitution: 0.6, friction: 0.1 }); + }); + + World.add(world, [ + stack, + Bodies.polygon(200, 560, 3, 60), + Bodies.polygon(400, 560, 5, 60), + Bodies.rectangle(600, 560, 80, 80) + ]); + }; + +})(); \ No newline at end of file diff --git a/examples/beachBalls.js b/examples/beachBalls.js new file mode 100644 index 00000000..ea5ce8ee --- /dev/null +++ b/examples/beachBalls.js @@ -0,0 +1,18 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.beachBalls = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(0, 100, 5, 1, 20, 0, function(x, y, column, row) { + return Bodies.circle(x, y, 75, { restitution: 0.9 }); + }); + + World.add(world, stack); + }; + +})(); \ No newline at end of file diff --git a/examples/bridge.js b/examples/bridge.js new file mode 100644 index 00000000..fa6dee5d --- /dev/null +++ b/examples/bridge.js @@ -0,0 +1,35 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composites = Matter.Composites, + Common = Matter.Common, + Constraint = Matter.Constraint; + + Example.bridge = function(demo) { + var engine = demo.engine, + world = engine.world, + group = Body.nextGroup(true); + + var bridge = Composites.stack(150, 300, 9, 1, 10, 10, function(x, y, column, row) { + return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { group: group } }); + }); + + Composites.chain(bridge, 0.5, 0, -0.5, 0, { stiffness: 0.9 }); + + var stack = Composites.stack(200, 40, 6, 3, 0, 0, function(x, y, column, row) { + return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 40)); + }); + + World.add(world, [ + bridge, + Bodies.rectangle(80, 440, 120, 280, { isStatic: true }), + Bodies.rectangle(720, 440, 120, 280, { isStatic: true }), + Constraint.create({ pointA: { x: 140, y: 300 }, bodyB: bridge.bodies[0], pointB: { x: -25, y: 0 } }), + Constraint.create({ pointA: { x: 660, y: 300 }, bodyB: bridge.bodies[8], pointB: { x: 25, y: 0 } }), + stack + ]); + }; + +})(); \ No newline at end of file diff --git a/examples/broadphase.js b/examples/broadphase.js new file mode 100644 index 00000000..1613f419 --- /dev/null +++ b/examples/broadphase.js @@ -0,0 +1,34 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.broadphase = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(20, 20, 20, 5, 0, 0, function(x, y, column, row) { + switch (Math.round(Common.random(0, 1))) { + + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); + } + break; + case 1: + return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); + + } + }); + + World.add(world, stack); + + var renderOptions = engine.render.options; + renderOptions.showBroadphase = true; + }; + +})(); \ No newline at end of file diff --git a/examples/car.js b/examples/car.js new file mode 100644 index 00000000..abd12930 --- /dev/null +++ b/examples/car.js @@ -0,0 +1,29 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.car = function(demo) { + var engine = demo.engine, + world = engine.world, + scale; + + scale = 0.9; + World.add(world, Composites.car(150, 100, 100 * scale, 40 * scale, 30 * scale)); + + scale = 0.8; + World.add(world, Composites.car(350, 300, 100 * scale, 40 * scale, 30 * scale)); + + World.add(world, [ + Bodies.rectangle(200, 150, 650, 20, { isStatic: true, angle: Math.PI * 0.06 }), + Bodies.rectangle(500, 350, 650, 20, { isStatic: true, angle: -Math.PI * 0.06 }), + Bodies.rectangle(340, 580, 700, 20, { isStatic: true, angle: Math.PI * 0.04 }) + ]); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = true; + renderOptions.showCollisions = true; + }; + +})(); \ No newline at end of file diff --git a/examples/catapult.js b/examples/catapult.js new file mode 100644 index 00000000..4f8860c6 --- /dev/null +++ b/examples/catapult.js @@ -0,0 +1,33 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Constraint = Matter.Constraint; + + Example.catapult = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(250, 255, 1, 6, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, 30, 30); + }); + + var catapult = Bodies.rectangle(400, 520, 320, 20, { }); + + World.add(world, [ + stack, + catapult, + Bodies.rectangle(250, 555, 20, 50, { isStatic: true }), + Bodies.circle(560, 100, 50, { density: 0.005 }), + Constraint.create({ bodyA: catapult, pointB: { x: 390, y: 580 } }), + Constraint.create({ bodyA: catapult, pointB: { x: 410, y: 580 } }) + ]); + + var renderOptions = engine.render.options; + renderOptions.showCollisions = true; + renderOptions.showVelocity = true; + renderOptions.showAngleIndicator = true; + }; + +})(); \ No newline at end of file diff --git a/examples/chains.js b/examples/chains.js new file mode 100644 index 00000000..596b9490 --- /dev/null +++ b/examples/chains.js @@ -0,0 +1,46 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composite = Matter.Composite, + Composites = Matter.Composites, + Constraint = Matter.Constraint; + + Example.chains = function(demo) { + var engine = demo.engine, + world = engine.world, + group = Body.nextGroup(true); + + var ropeA = Composites.stack(200, 100, 5, 2, 10, 10, function(x, y, column, row) { + return Bodies.rectangle(x, y, 50, 20, { collisionFilter: { group: group } }); + }); + + Composites.chain(ropeA, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 }); + Composite.add(ropeA, Constraint.create({ + bodyB: ropeA.bodies[0], + pointB: { x: -25, y: 0 }, + pointA: { x: 200, y: 100 }, + stiffness: 0.5 + })); + + World.add(world, ropeA); + + group = Body.nextGroup(true); + + var ropeB = Composites.stack(500, 100, 5, 2, 10, 10, function(x, y, column, row) { + return Bodies.circle(x, y, 20, { collisionFilter: { group: group } }); + }); + + Composites.chain(ropeB, 0.5, 0, -0.5, 0, { stiffness: 0.8, length: 2 }); + Composite.add(ropeB, Constraint.create({ + bodyB: ropeB.bodies[0], + pointB: { x: -20, y: 0 }, + pointA: { x: 500, y: 100 }, + stiffness: 0.5 + })); + + World.add(world, ropeB); + }; + +})(); \ No newline at end of file diff --git a/examples/circleStack.js b/examples/circleStack.js new file mode 100644 index 00000000..62c59ef5 --- /dev/null +++ b/examples/circleStack.js @@ -0,0 +1,18 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.circleStack = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(100, 100, 10, 10, 20, 0, function(x, y, column, row) { + return Bodies.circle(x, y, 20); + }); + + World.add(world, stack); + }; + +})(); \ No newline at end of file diff --git a/examples/cloth.js b/examples/cloth.js new file mode 100644 index 00000000..90b96bd1 --- /dev/null +++ b/examples/cloth.js @@ -0,0 +1,27 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composites = Matter.Composites; + + Example.cloth = function(demo) { + var engine = demo.engine, + world = engine.world; + + var group = Body.nextGroup(true), + particleOptions = { friction: 0.00001, collisionFilter: { group: group }, render: { visible: false }}, + cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions); + + for (var i = 0; i < 20; i++) { + cloth.bodies[i].isStatic = true; + } + + World.add(world, [ + cloth, + Bodies.circle(300, 500, 80, { isStatic: true }), + Bodies.rectangle(500, 480, 80, 80, { isStatic: true }) + ]); + }; + +})(); \ No newline at end of file diff --git a/examples/collisionFiltering.js b/examples/collisionFiltering.js new file mode 100644 index 00000000..4a7044ac --- /dev/null +++ b/examples/collisionFiltering.js @@ -0,0 +1,97 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.collisionFiltering = function(demo) { + var engine = demo.engine, + world = engine.world, + mouseConstraint = demo.mouseConstraint; + + // define our categories (as bit fields, there are up to 32 available) + var defaultCategory = 0x0001, + redCategory = 0x0002, + greenCategory = 0x0004, + blueCategory = 0x0008; + + var redColor = '#C44D58', + blueColor = '#4ECDC4', + greenColor = '#C7F464'; + + // create a stack with varying body categories (but these bodies can all collide with each other) + World.add(world, + Composites.stack(275, 150, 5, 10, 10, 10, function(x, y, column, row) { + var category = redCategory, + color = redColor; + + if (row > 5) { + category = blueCategory; + color = blueColor; + } else if (row > 2) { + category = greenCategory; + color = greenColor; + } + + return Bodies.circle(x, y, 20, { + collisionFilter: { + category: category + }, + render: { + strokeStyle: color, + fillStyle: 'transparent' + } + }); + }) + ); + + // this body will only collide with the walls and the green bodies + World.add(world, + Bodies.circle(310, 40, 30, { + collisionFilter: { + mask: defaultCategory | greenCategory + }, + render: { + strokeStyle: Common.shadeColor(greenColor, -20), + fillStyle: greenColor + } + }) + ); + + // this body will only collide with the walls and the red bodies + World.add(world, + Bodies.circle(400, 40, 30, { + collisionFilter: { + mask: defaultCategory | redCategory + }, + render: { + strokeStyle: Common.shadeColor(redColor, -20), + fillStyle: redColor + } + }) + ); + + // this body will only collide with the walls and the blue bodies + World.add(world, + Bodies.circle(480, 40, 30, { + collisionFilter: { + mask: defaultCategory | blueCategory + }, + render: { + strokeStyle: Common.shadeColor(blueColor, -20), + fillStyle: blueColor + } + }) + ); + + // red category objects should not be draggable with the mouse + mouseConstraint.collisionFilter.mask = defaultCategory | blueCategory | greenCategory; + + var renderOptions = engine.render.options; + renderOptions.wireframes = false; + renderOptions.background = '#222'; + renderOptions.showAngleIndicator = false; + }; + +})(); \ No newline at end of file diff --git a/examples/compositeManipulation.js b/examples/compositeManipulation.js new file mode 100644 index 00000000..2be54da6 --- /dev/null +++ b/examples/compositeManipulation.js @@ -0,0 +1,52 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composite = Matter.Composite, + Composites = Matter.Composites, + Events = Matter.Events; + + Example.compositeManipulation = function(demo) { + var engine = demo.engine, + world = engine.world, + mouseConstraint = demo.mouseConstraint, + sceneEvents = demo.sceneEvents; + + var stack = Composites.stack(200, 200, 4, 4, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, 40, 40); + }); + + World.add(world, stack); + + world.gravity.y = 0; + + sceneEvents.push( + Events.on(engine, 'afterUpdate', function(event) { + var time = engine.timing.timestamp; + + Composite.translate(stack, { + x: Math.sin(time * 0.001) * 2, + y: 0 + }); + + Composite.rotate(stack, Math.sin(time * 0.001) * 0.01, { + x: 300, + y: 300 + }); + + var scale = 1 + (Math.sin(time * 0.001) * 0.01); + + Composite.scale(stack, scale, scale, { + x: 300, + y: 300 + }); + }) + ); + + var renderOptions = engine.render.options; + renderOptions.wireframes = false; + renderOptions.showAxes = true; + renderOptions.showCollisions = true; + }; + +})(); \ No newline at end of file diff --git a/examples/compound.js b/examples/compound.js new file mode 100644 index 00000000..e02afd55 --- /dev/null +++ b/examples/compound.js @@ -0,0 +1,49 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Constraint = Matter.Constraint; + + Example.compound = function(demo) { + var engine = demo.engine, + world = engine.world; + + var size = 200, + x = 200, + y = 200, + partA = Bodies.rectangle(x, y, size, size / 5), + partB = Bodies.rectangle(x, y, size / 5, size, { render: partA.render }); + + var compoundBodyA = Body.create({ + parts: [partA, partB] + }); + + size = 150; + x = 400; + y = 300; + + var partC = Bodies.circle(x, y, 30), + partD = Bodies.circle(x + size, y, 30), + partE = Bodies.circle(x + size, y + size, 30), + partF = Bodies.circle(x, y + size, 30); + + var compoundBodyB = Body.create({ + parts: [partC, partD, partE, partF] + }); + + var constraint = Constraint.create({ + pointA: { x: 400, y: 100 }, + bodyB: compoundBodyB, + pointB: { x: 0, y: -50 } + }); + + World.add(world, [compoundBodyA, compoundBodyB, constraint]); + + var renderOptions = engine.render.options; + renderOptions.showAxes = true; + renderOptions.showPositions = true; + renderOptions.showConvexHulls = true; + }; + +})(); \ No newline at end of file diff --git a/examples/compoundStack.js b/examples/compoundStack.js new file mode 100644 index 00000000..58518968 --- /dev/null +++ b/examples/compoundStack.js @@ -0,0 +1,25 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composites = Matter.Composites; + + Example.compoundStack = function(demo) { + var engine = demo.engine, + world = engine.world; + var size = 50; + + var stack = Composites.stack(100, 220, 12, 6, 0, 0, function(x, y, column, row) { + var partA = Bodies.rectangle(x, y, size, size / 5), + partB = Bodies.rectangle(x, y, size / 5, size, { render: partA.render }); + + return Body.create({ + parts: [partA, partB] + }); + }); + + World.add(world, stack); + }; + +})(); \ No newline at end of file diff --git a/examples/concave.js b/examples/concave.js new file mode 100644 index 00000000..76835280 --- /dev/null +++ b/examples/concave.js @@ -0,0 +1,40 @@ +(function() { + + var Engine = Matter.Engine, + World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composite = Matter.Composite, + Composites = Matter.Composites, + Common = Matter.Common, + Constraint = Matter.Constraint, + Events = Matter.Events, + Bounds = Matter.Bounds, + Vertices = Matter.Vertices; + + Example.concave = function(demo) { + var engine = demo.engine, + world = engine.world; + + var arrow = Vertices.fromPath('40 0 40 20 100 20 100 80 40 80 40 100 0 50'), + chevron = Vertices.fromPath('100 0 75 50 100 100 25 100 0 50 25 0'), + star = Vertices.fromPath('50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38'), + horseShoe = Vertices.fromPath('35 7 19 17 14 38 14 58 25 79 45 85 65 84 65 66 46 67 34 59 30 44 33 29 45 23 66 23 66 7 53 7'); + + var stack = Composites.stack(50, 50, 6, 4, 10, 10, function(x, y, column, row) { + var color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); + return Bodies.fromVertices(x, y, Common.choose([arrow, chevron, star, horseShoe]), { + render: { + fillStyle: color, + strokeStyle: color + } + }, true); + }); + + World.add(world, stack); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + }; + +})(); \ No newline at end of file diff --git a/examples/events.js b/examples/events.js new file mode 100644 index 00000000..9a148477 --- /dev/null +++ b/examples/events.js @@ -0,0 +1,159 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composite = Matter.Composite, + Composites = Matter.Composites, + Common = Matter.Common, + Events = Matter.Events; + + Example.events = function(demo) { + var engine = demo.engine, + world = engine.world, + mouseConstraint = demo.mouseConstraint, + sceneEvents = demo.sceneEvents; + + // bind events (sceneEvents is only used for this demo) + + sceneEvents.push( + + // an example of using composite events on the world + Events.on(world, 'afterAdd', function(event) { + console.log('added to world:', event.object); + }) + + ); + + sceneEvents.push( + + // an example of using beforeUpdate event on an engine + Events.on(engine, 'beforeUpdate', function(event) { + var engine = event.source; + + // apply random forces every 5 secs + if (event.timestamp % 5000 < 50) + shakeScene(engine); + }) + + ); + + sceneEvents.push( + + // an example of using collisionStart event on an engine + Events.on(engine, 'collisionStart', function(event) { + var pairs = event.pairs; + + // change object colours to show those starting a collision + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i]; + pair.bodyA.render.fillStyle = '#bbbbbb'; + pair.bodyB.render.fillStyle = '#bbbbbb'; + } + }) + + ); + + sceneEvents.push( + + // an example of using collisionActive event on an engine + Events.on(engine, 'collisionActive', function(event) { + var pairs = event.pairs; + + // change object colours to show those in an active collision (e.g. resting contact) + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i]; + pair.bodyA.render.fillStyle = '#aaaaaa'; + pair.bodyB.render.fillStyle = '#aaaaaa'; + } + }) + + ); + + sceneEvents.push( + + // an example of using collisionEnd event on an engine + Events.on(engine, 'collisionEnd', function(event) { + var pairs = event.pairs; + + // change object colours to show those ending a collision + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i]; + pair.bodyA.render.fillStyle = '#999999'; + pair.bodyB.render.fillStyle = '#999999'; + } + }) + + ); + + sceneEvents.push( + + // an example of using mouse events on a mouse + Events.on(mouseConstraint, 'mousedown', function(event) { + var mousePosition = event.mouse.position; + console.log('mousedown at ' + mousePosition.x + ' ' + mousePosition.y); + engine.render.options.background = 'cornsilk'; + shakeScene(engine); + }) + + ); + + sceneEvents.push( + + // an example of using mouse events on a mouse + Events.on(mouseConstraint, 'mouseup', function(event) { + var mousePosition = event.mouse.position; + engine.render.options.background = "white"; + console.log('mouseup at ' + mousePosition.x + ' ' + mousePosition.y); + }) + + ); + + sceneEvents.push( + + // an example of using mouse events on a mouse + Events.on(mouseConstraint, 'startdrag', function(event) { + console.log('startdrag', event); + }) + + ); + + sceneEvents.push( + + // an example of using mouse events on a mouse + Events.on(mouseConstraint, 'enddrag', function(event) { + console.log('enddrag', event); + }) + + ); + + // scene code + + var stack = Composites.stack(50, 100, 8, 4, 50, 50, function(x, y, column, row) { + return Bodies.circle(x, y, 15, { restitution: 1, render: { strokeStyle: '#777' } }); + }); + + World.add(world, stack); + + var renderOptions = engine.render.options; + renderOptions.wireframes = false; + + var shakeScene = function(engine) { + var bodies = Composite.allBodies(engine.world); + + for (var i = 0; i < bodies.length; i++) { + var body = bodies[i]; + + if (!body.isStatic && body.position.y >= 500) { + var forceMagnitude = 0.01 * body.mass; + + Body.applyForce(body, { x: 0, y: 0 }, { + x: (forceMagnitude + Common.random() * forceMagnitude) * Common.choose([1, -1]), + y: -forceMagnitude + Common.random() * -forceMagnitude + }); + } + } + }; + }; + +})(); \ No newline at end of file diff --git a/examples/friction.js b/examples/friction.js new file mode 100644 index 00000000..f925a92f --- /dev/null +++ b/examples/friction.js @@ -0,0 +1,30 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies; + + Example.friction = function(demo) { + var engine = demo.engine, + world = engine.world; + + World.add(world, [ + Bodies.rectangle(300, 180, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), + Bodies.rectangle(300, 70, 40, 40, { friction: 0.001 }) + ]); + + World.add(world, [ + Bodies.rectangle(300, 350, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), + Bodies.rectangle(300, 250, 40, 40, { friction: 0.0005 }) + ]); + + World.add(world, [ + Bodies.rectangle(300, 520, 700, 20, { isStatic: true, angle: Math.PI * 0.06 }), + Bodies.rectangle(300, 430, 40, 40, { friction: 0 }) + ]); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + renderOptions.showVelocity = true; + }; + +})(); \ No newline at end of file diff --git a/examples/gravity.js b/examples/gravity.js new file mode 100644 index 00000000..c4878292 --- /dev/null +++ b/examples/gravity.js @@ -0,0 +1,33 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.gravity = function(demo) { + var engine = demo.engine, + world = engine.world; + + engine.world.gravity.y = -1; + + var stack = Composites.stack(20, 20, 20, 5, 0, 0, function(x, y, column, row) { + switch (Math.round(Common.random(0, 1))) { + + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); + } + break; + case 1: + return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); + + } + }); + + World.add(world, stack); + }; + +})(); \ No newline at end of file diff --git a/examples/manipulation.js b/examples/manipulation.js new file mode 100644 index 00000000..23a2de3a --- /dev/null +++ b/examples/manipulation.js @@ -0,0 +1,83 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Events = Matter.Events; + + Example.manipulation = function(demo) { + var engine = demo.engine, + world = engine.world, + sceneEvents = demo.sceneEvents; + + var bodyA = Bodies.rectangle(100, 200, 50, 50, { isStatic: true }), + bodyB = Bodies.rectangle(200, 200, 50, 50), + bodyC = Bodies.rectangle(300, 200, 50, 50), + bodyD = Bodies.rectangle(400, 200, 50, 50), + bodyE = Bodies.rectangle(550, 200, 50, 50), + bodyF = Bodies.rectangle(700, 200, 50, 50), + bodyG = Bodies.circle(400, 100, 25), + partA = Bodies.rectangle(600, 200, 120, 50), + partB = Bodies.rectangle(660, 200, 50, 190), + compound = Body.create({ + parts: [partA, partB], + isStatic: true + }); + + World.add(world, [bodyA, bodyB, bodyC, bodyD, bodyE, bodyF, bodyG, compound]); + + var counter = 0, + scaleFactor = 1.01; + + sceneEvents.push( + Events.on(engine, 'beforeUpdate', function(event) { + counter += 1; + + if (counter === 40) + Body.setStatic(bodyG, true); + + if (scaleFactor > 1) { + Body.scale(bodyF, scaleFactor, scaleFactor); + Body.scale(compound, 0.995, 0.995); + + // modify bodyE vertices + bodyE.vertices[0].x -= 0.2; + bodyE.vertices[0].y -= 0.2; + bodyE.vertices[1].x += 0.2; + bodyE.vertices[1].y -= 0.2; + Body.setVertices(bodyE, bodyE.vertices); + } + + // make bodyA move up and down + // body is static so must manually update velocity for friction to work + var py = 300 + 100 * Math.sin(engine.timing.timestamp * 0.002); + Body.setVelocity(bodyA, { x: 0, y: py - bodyA.position.y }); + Body.setPosition(bodyA, { x: 100, y: py }); + + // make compound body move up and down and rotate constantly + Body.setVelocity(compound, { x: 0, y: py - compound.position.y }); + Body.setAngularVelocity(compound, 0.02); + Body.setPosition(compound, { x: 600, y: py }); + Body.rotate(compound, 0.02); + + // every 1.5 sec + if (counter >= 60 * 1.5) { + Body.setVelocity(bodyB, { x: 0, y: -10 }); + Body.setAngle(bodyC, -Math.PI * 0.26); + Body.setAngularVelocity(bodyD, 0.2); + + // reset counter + counter = 0; + scaleFactor = 1; + } + }) + ); + + var renderOptions = engine.render.options; + renderOptions.showAxes = true; + renderOptions.showCollisions = true; + renderOptions.showPositions = true; + renderOptions.showConvexHulls = true; + }; + +})(); \ No newline at end of file diff --git a/examples/mixed.js b/examples/mixed.js new file mode 100644 index 00000000..4874593e --- /dev/null +++ b/examples/mixed.js @@ -0,0 +1,42 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.mixed = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(20, 20, 15, 4, 0, 0, function(x, y, column, row) { + var sides = Math.round(Common.random(1, 8)); + + // triangles can be a little unstable, so avoid until fixed + sides = (sides === 3) ? 4 : sides; + + // round the edges of some bodies + var chamfer = null; + if (sides > 2 && Common.random() > 0.7) { + chamfer = { + radius: 10 + }; + } + + switch (Math.round(Common.random(0, 1))) { + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(25, 50), Common.random(25, 50), { chamfer: chamfer }); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(25, 30), { chamfer: chamfer }); + } + break; + case 1: + return Bodies.polygon(x, y, sides, Common.random(25, 50), { chamfer: chamfer }); + } + }); + + World.add(world, stack); + }; + +})(); \ No newline at end of file diff --git a/examples/mixedSolid.js b/examples/mixedSolid.js new file mode 100644 index 00000000..49d5e8d6 --- /dev/null +++ b/examples/mixedSolid.js @@ -0,0 +1,35 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.mixedSolid = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(50, 50, 12, 3, 0, 0, function(x, y, column, row) { + switch (Math.round(Common.random(0, 1))) { + + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); + } + break; + case 1: + return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); + + } + }); + + World.add(world, stack); + + var renderOptions = engine.render.options; + renderOptions.wireframes = false; + renderOptions.showAngleIndicator = false; + }; + +})(); \ No newline at end of file diff --git a/examples/newtonsCradle.js b/examples/newtonsCradle.js new file mode 100644 index 00000000..b12c4d1f --- /dev/null +++ b/examples/newtonsCradle.js @@ -0,0 +1,23 @@ +(function() { + + var World = Matter.World, + Body = Matter.Body, + Composites = Matter.Composites; + + Example.newtonsCradle = function(demo) { + var engine = demo.engine, + world = engine.world; + + var cradle = Composites.newtonsCradle(280, 100, 5, 30, 200); + World.add(world, cradle); + Body.translate(cradle.bodies[0], { x: -180, y: -100 }); + + cradle = Composites.newtonsCradle(280, 380, 7, 20, 140); + World.add(world, cradle); + Body.translate(cradle.bodies[0], { x: -140, y: -100 }); + + var renderOptions = engine.render.options; + renderOptions.showVelocity = true; + }; + +})(); \ No newline at end of file diff --git a/examples/pyramid.js b/examples/pyramid.js new file mode 100644 index 00000000..4aa4beba --- /dev/null +++ b/examples/pyramid.js @@ -0,0 +1,18 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.pyramid = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.pyramid(100, 258, 15, 10, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, 40, 40); + }); + + World.add(world, stack); + }; + +})(); \ No newline at end of file diff --git a/examples/raycasting.js b/examples/raycasting.js new file mode 100644 index 00000000..09b4e1ba --- /dev/null +++ b/examples/raycasting.js @@ -0,0 +1,74 @@ +(function() { + + var Engine = Matter.Engine, + World = Matter.World, + Bodies = Matter.Bodies, + Composite = Matter.Composite, + Composites = Matter.Composites, + Common = Matter.Common, + Events = Matter.Events, + Vertices = Matter.Vertices, + Mouse = Matter.Mouse, + Query = Matter.Query; + + Example.raycasting = function(demo) { + var engine = demo.engine, + world = engine.world, + sceneEvents = demo.sceneEvents, + mouseConstraint = demo.mouseConstraint; + + var stack = Composites.stack(20, 20, 15, 4, 0, 0, function(x, y, column, row) { + switch (Math.round(Common.random(0, 1))) { + + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); + } + break; + case 1: + var sides = Math.round(Common.random(1, 8)); + sides = (sides === 3) ? 4 : sides; + return Bodies.polygon(x, y, sides, Common.random(20, 50)); + } + }); + + var star = Vertices.fromPath('50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38'), + concave = Bodies.fromVertices(200, 200, star); + + World.add(world, [stack, concave]); + + sceneEvents.push( + Events.on(engine.render, 'afterRender', function() { + var mouse = mouseConstraint.mouse, + context = engine.render.context, + bodies = Composite.allBodies(engine.world), + startPoint = { x: 400, y: 100 }, + endPoint = mouse.position; + + var collisions = Query.ray(bodies, startPoint, endPoint); + + context.beginPath(); + context.moveTo(startPoint.x, startPoint.y); + context.lineTo(endPoint.x, endPoint.y); + if (collisions.length > 0) { + context.strokeStyle = '#fff'; + } else { + context.strokeStyle = '#555'; + } + context.lineWidth = 0.5; + context.stroke(); + + for (var i = 0; i < collisions.length; i++) { + var collision = collisions[i]; + context.rect(collision.bodyA.position.x - 4.5, collision.bodyA.position.y - 4.5, 8, 8); + } + + context.fillStyle = 'rgba(255,165,0,0.7)'; + context.fill(); + }) + ); + }; + +})(); \ No newline at end of file diff --git a/examples/restitution.js b/examples/restitution.js new file mode 100644 index 00000000..320aeeaf --- /dev/null +++ b/examples/restitution.js @@ -0,0 +1,28 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Common = Matter.Common; + + Example.restitution = function(demo) { + var engine = demo.engine, + world = engine.world; + + var rest = 0.9, + space = 600 / 5; + + World.add(world, [ + Bodies.rectangle(100 + space * 0, 150, 50, 50, { restitution: rest }), + Bodies.rectangle(100 + space * 1, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.15 }), + Bodies.rectangle(100 + space * 2, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.25 }), + Bodies.circle(100 + space * 3, 150, 25, { restitution: rest }), + Bodies.rectangle(100 + space * 5, 150, 180, 20, { restitution: rest, angle: -Math.PI * 0.5 }) + ]); + + var renderOptions = engine.render.options; + renderOptions.showCollisions = true; + renderOptions.showVelocity = true; + renderOptions.showAngleIndicator = true; + }; + +})(); \ No newline at end of file diff --git a/examples/rounded.js b/examples/rounded.js new file mode 100644 index 00000000..03356df0 --- /dev/null +++ b/examples/rounded.js @@ -0,0 +1,49 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies; + + Example.rounded = function(demo) { + var engine = demo.engine, + world = engine.world; + + World.add(world, [ + Bodies.rectangle(200, 200, 100, 100, { + chamfer: { radius: 20 } + }), + + Bodies.rectangle(300, 200, 100, 100, { + chamfer: { radius: [90, 0, 0, 0] } + }), + + Bodies.rectangle(400, 200, 200, 200, { + chamfer: { radius: [150, 20, 40, 20] } + }), + + Bodies.rectangle(200, 200, 200, 200, { + chamfer: { radius: [150, 20, 150, 20] } + }), + + Bodies.rectangle(300, 200, 200, 50, { + chamfer: { radius: [25, 25, 0, 0] } + }), + + Bodies.polygon(200, 100, 8, 80, { + chamfer: { radius: 30 } + }), + + Bodies.polygon(300, 100, 5, 80, { + chamfer: { radius: [10, 40, 20, 40, 10] } + }), + + Bodies.polygon(400, 200, 3, 50, { + chamfer: { radius: [20, 0, 20] } + }) + ]); + + var renderOptions = engine.render.options; + renderOptions.showAxes = true; + renderOptions.showCollisions = true; + }; + +})(); \ No newline at end of file diff --git a/examples/sleeping.js b/examples/sleeping.js new file mode 100644 index 00000000..c5a4c159 --- /dev/null +++ b/examples/sleeping.js @@ -0,0 +1,40 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Common = Matter.Common; + + Example.sleeping = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(50, 50, 12, 3, 0, 0, function(x, y, column, row) { + switch (Math.round(Common.random(0, 1))) { + + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); + } + break; + case 1: + return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50)); + + } + }); + + World.add(world, stack); + + for (var i = 0; i < stack.bodies.length; i++) { + Events.on(stack.bodies[i], 'sleepStart sleepEnd', function(event) { + var body = this; + console.log('body id', body.id, 'sleeping:', body.isSleeping); + }); + } + + engine.enableSleeping = true; + }; + +})(); \ No newline at end of file diff --git a/examples/slingshot.js b/examples/slingshot.js new file mode 100644 index 00000000..4040c202 --- /dev/null +++ b/examples/slingshot.js @@ -0,0 +1,64 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites, + Constraint = Matter.Constraint, + Events = Matter.Events; + + Example.slingshot = function(demo) { + var engine = demo.engine, + world = engine.world, + mouseConstraint = demo.mouseConstraint; + + world.bodies = []; + + var ground = Bodies.rectangle(395, 600, 815, 50, { isStatic: true, render: { visible: false } }), + rockOptions = { density: 0.004, render: { sprite: { texture: './img/rock.png' } } }, + rock = Bodies.polygon(170, 450, 8, 20, rockOptions), + anchor = { x: 170, y: 450 }, + elastic = Constraint.create({ + pointA: anchor, + bodyB: rock, + stiffness: 0.05, + render: { + lineWidth: 5, + strokeStyle: '#dfa417' + } + }); + + var pyramid = Composites.pyramid(500, 300, 9, 10, 0, 0, function(x, y, column, row) { + var texture = column % 2 === 0 ? './img/block.png' : './img/block-2.png'; + return Bodies.rectangle(x, y, 25, 40, { render: { sprite: { texture: texture } } }); + }); + + var ground2 = Bodies.rectangle(610, 250, 200, 20, { + isStatic: true, + render: { + fillStyle: '#edc51e', + strokeStyle: '#b5a91c' + } + }); + + var pyramid2 = Composites.pyramid(550, 0, 5, 10, 0, 0, function(x, y, column, row) { + var texture = column % 2 === 0 ? './img/block.png' : './img/block-2.png'; + return Bodies.rectangle(x, y, 25, 40, { render: { sprite: { texture: texture } } }); + }); + + World.add(engine.world, [ground, pyramid, ground2, pyramid2, rock, elastic]); + + Events.on(engine, 'afterUpdate', function() { + if (mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) { + rock = Bodies.polygon(170, 450, 7, 20, rockOptions); + World.add(engine.world, rock); + elastic.bodyB = rock; + } + }); + + var renderOptions = engine.render.options; + renderOptions.wireframes = false; + renderOptions.showAngleIndicator = false; + renderOptions.background = './img/background.png'; + }; + +})(); \ No newline at end of file diff --git a/examples/softBody.js b/examples/softBody.js new file mode 100644 index 00000000..8415c909 --- /dev/null +++ b/examples/softBody.js @@ -0,0 +1,26 @@ +(function() { + + var World = Matter.World, + Composites = Matter.Composites; + + Example.softBody = function(demo) { + var engine = demo.engine, + world = engine.world; + + var particleOptions = { + friction: 0.05, + frictionStatic: 0.1, + render: { visible: true } + }; + + World.add(world, [ + Composites.softBody(250, 100, 5, 5, 0, 0, true, 18, particleOptions), + Composites.softBody(250, 300, 8, 3, 0, 0, true, 15, particleOptions), + Composites.softBody(250, 400, 4, 4, 0, 0, true, 15, particleOptions) + ]); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + }; + +})(); \ No newline at end of file diff --git a/examples/sprites.js b/examples/sprites.js new file mode 100644 index 00000000..52728126 --- /dev/null +++ b/examples/sprites.js @@ -0,0 +1,62 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.sprites = function(demo) { + var engine = demo.engine, + world = engine.world, + mouseConstraint = demo.mouseConstraint, + offset = 10, + options = { + isStatic: true, + render: { + visible: false + } + }; + + world.bodies = []; + + // these static walls will not be rendered in this sprites example, see options + World.add(world, [ + Bodies.rectangle(400, -offset, 800.5 + 2 * offset, 50.5, options), + Bodies.rectangle(400, 600 + offset, 800.5 + 2 * offset, 50.5, options), + Bodies.rectangle(800 + offset, 300, 50.5, 600.5 + 2 * offset, options), + Bodies.rectangle(-offset, 300, 50.5, 600.5 + 2 * offset, options) + ]); + + var stack = Composites.stack(20, 20, 10, 4, 0, 0, function(x, y, column, row) { + if (Common.random() > 0.35) { + return Bodies.rectangle(x, y, 64, 64, { + render: { + strokeStyle: '#ffffff', + sprite: { + texture: './img/box.png' + } + } + }); + } else { + return Bodies.circle(x, y, 46, { + density: 0.0005, + frictionAir: 0.06, + restitution: 0.3, + friction: 0.01, + render: { + sprite: { + texture: './img/ball.png' + } + } + }); + } + }); + + World.add(world, stack); + + var renderOptions = engine.render.options; + renderOptions.background = './img/wall-bg.jpg'; + renderOptions.showAngleIndicator = false; + renderOptions.wireframes = false; + }; + +})(); \ No newline at end of file diff --git a/examples/stack.js b/examples/stack.js new file mode 100644 index 00000000..ecc6ec17 --- /dev/null +++ b/examples/stack.js @@ -0,0 +1,18 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.stack = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(100, 300, 10, 5, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, 40, 40); + }); + + World.add(world, stack); + }; + +})(); \ No newline at end of file diff --git a/examples/staticFriction.js b/examples/staticFriction.js new file mode 100644 index 00000000..1d2e34f7 --- /dev/null +++ b/examples/staticFriction.js @@ -0,0 +1,49 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composites = Matter.Composites, + Events = Matter.Events; + + Example.staticFriction = function(demo) { + var engine = demo.engine, + world = engine.world, + sceneEvents = demo.sceneEvents; + + var body = Bodies.rectangle(400, 500, 200, 60, { isStatic: true, chamfer: 10 }), + size = 50, + counter = -1; + + var stack = Composites.stack(350, 470 - 6 * size, 1, 6, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, size * 2, size, { + slop: 0.5, + friction: 1, + frictionStatic: Infinity + }); + }); + + World.add(world, [body, stack]); + + sceneEvents.push( + Events.on(engine, 'beforeUpdate', function(event) { + counter += 0.014; + + if (counter < 0) { + return; + } + + var px = 400 + 100 * Math.sin(counter); + + // body is static so must manually update velocity for friction to work + Body.setVelocity(body, { x: px - body.position.x, y: 0 }); + Body.setPosition(body, { x: px, y: body.position.y }); + }) + ); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + renderOptions.showVelocity = true; + }; + +})(); \ No newline at end of file diff --git a/examples/stress.js b/examples/stress.js new file mode 100644 index 00000000..13663ec6 --- /dev/null +++ b/examples/stress.js @@ -0,0 +1,21 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.stress = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(90, 50, 18, 15, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, 35, 35); + }); + + World.add(world, stack); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + }; + +})(); \ No newline at end of file diff --git a/examples/stress2.js b/examples/stress2.js new file mode 100644 index 00000000..8c6f8536 --- /dev/null +++ b/examples/stress2.js @@ -0,0 +1,21 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Composites = Matter.Composites; + + Example.stress2 = function(demo) { + var engine = demo.engine, + world = engine.world; + + var stack = Composites.stack(100, 120, 25, 18, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, 25, 25); + }); + + World.add(world, stack); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + }; + +})(); \ No newline at end of file diff --git a/examples/svg.js b/examples/svg.js new file mode 100644 index 00000000..09b6c1f9 --- /dev/null +++ b/examples/svg.js @@ -0,0 +1,61 @@ +(function() { + + var World = Matter.World, + Bodies = Matter.Bodies, + Common = Matter.Common, + Vertices = Matter.Vertices, + Svg = Matter.Svg; + + Example.svg = function(demo) { + var engine = demo.engine, + world = engine.world; + + var svgs = [ + 'iconmonstr-check-mark-8-icon', + 'iconmonstr-paperclip-2-icon', + 'iconmonstr-puzzle-icon', + 'iconmonstr-user-icon' + ]; + + for (var i = 0; i < svgs.length; i += 1) { + (function(i) { + $.get('./svg/' + svgs[i] + '.svg').done(function(data) { + var vertexSets = [], + color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); + + $(data).find('path').each(function(i, path) { + var points = Svg.pathToVertices(path, 30); + vertexSets.push(Vertices.scale(points, 0.4, 0.4)); + }); + + World.add(world, Bodies.fromVertices(100 + i * 150, 200 + i * 50, vertexSets, { + render: { + fillStyle: color, + strokeStyle: color + } + }, true)); + }); + })(i); + } + + $.get('./svg/svg.svg').done(function(data) { + var vertexSets = [], + color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); + + $(data).find('path').each(function(i, path) { + vertexSets.push(Svg.pathToVertices(path, 30)); + }); + + World.add(world, Bodies.fromVertices(400, 80, vertexSets, { + render: { + fillStyle: color, + strokeStyle: color + } + }, true)); + }); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + }; + +})(); \ No newline at end of file diff --git a/examples/terrain.js b/examples/terrain.js new file mode 100644 index 00000000..d7378eca --- /dev/null +++ b/examples/terrain.js @@ -0,0 +1,64 @@ +(function() { + + var Engine = Matter.Engine, + World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composite = Matter.Composite, + Composites = Matter.Composites, + Common = Matter.Common, + Constraint = Matter.Constraint, + Events = Matter.Events, + Bounds = Matter.Bounds, + Vector = Matter.Vector, + Vertices = Matter.Vertices, + MouseConstraint = Matter.MouseConstraint, + Mouse = Matter.Mouse, + Query = Matter.Query, + Svg = Matter.Svg; + + Example.terrain = function(demo) { + var engine = demo.engine, + world = engine.world; + + world.bodies = []; + + var terrain; + + $.get('./svg/terrain.svg').done(function(data) { + var vertexSets = [], + color = Common.choose(['#556270', '#4ECDC4', '#C7F464', '#FF6B6B', '#C44D58']); + + $(data).find('path').each(function(i, path) { + vertexSets.push(Svg.pathToVertices(path, 30)); + }); + + terrain = Bodies.fromVertices(400, 350, vertexSets, { + isStatic: true, + render: { + fillStyle: color, + strokeStyle: color + } + }, true); + + World.add(world, terrain); + + var bodyOptions = { + frictionAir: 0, + friction: 0.0001, + restitution: 0.6 + }; + + World.add(world, Composites.stack(80, 100, 20, 20, 10, 10, function(x, y, column, row) { + if (Query.point([terrain], { x: x, y: y }).length === 0) { + return Bodies.polygon(x, y, 5, 12, bodyOptions); + } + })); + }); + + var renderOptions = engine.render.options; + renderOptions.showAngleIndicator = false; + renderOptions.showVelocity = true; + }; + +})(); \ No newline at end of file diff --git a/examples/timescale.js b/examples/timescale.js new file mode 100644 index 00000000..e55b23de --- /dev/null +++ b/examples/timescale.js @@ -0,0 +1,100 @@ +(function() { + + var Engine = Matter.Engine, + World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composite = Matter.Composite, + Composites = Matter.Composites, + Common = Matter.Common, + Constraint = Matter.Constraint, + Events = Matter.Events, + Bounds = Matter.Bounds, + Vector = Matter.Vector, + Vertices = Matter.Vertices, + MouseConstraint = Matter.MouseConstraint, + Mouse = Matter.Mouse, + Query = Matter.Query, + Svg = Matter.Svg; + + Example.timescale = function(demo) { + var engine = demo.engine, + world = engine.world, + sceneEvents = demo.sceneEvents; + + var explosion = function(engine) { + var bodies = Composite.allBodies(engine.world); + + for (var i = 0; i < bodies.length; i++) { + var body = bodies[i]; + + if (!body.isStatic && body.position.y >= 500) { + var forceMagnitude = 0.04 * body.mass; + + Body.applyForce(body, { x: 0, y: 0 }, { + x: (forceMagnitude + Common.random() * forceMagnitude) * Common.choose([1, -1]), + y: -forceMagnitude + Common.random() * -forceMagnitude + }); + } + } + }; + + var timeScaleTarget = 1, + counter = 0; + + sceneEvents.push( + Events.on(engine, 'afterUpdate', function(event) { + // tween the timescale for bullet time slow-mo + engine.timing.timeScale += (timeScaleTarget - engine.timing.timeScale) * 0.05; + + counter += 1; + + // every 1.5 sec + if (counter >= 60 * 1.5) { + + // flip the timescale + if (timeScaleTarget < 1) { + timeScaleTarget = 1; + } else { + timeScaleTarget = 0.05; + } + + // create some random forces + explosion(engine); + + // reset counter + counter = 0; + } + }) + ); + + var bodyOptions = { + frictionAir: 0, + friction: 0.0001, + restitution: 0.8 + }; + + // add some small bouncy circles... remember Swordfish? + World.add(world, Composites.stack(20, 100, 15, 3, 20, 40, function(x, y, column, row) { + return Bodies.circle(x, y, Common.random(10, 20), bodyOptions); + })); + + // add some larger random bouncy objects + World.add(world, Composites.stack(50, 50, 8, 3, 0, 0, function(x, y, column, row) { + switch (Math.round(Common.random(0, 1))) { + + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50), bodyOptions); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30), bodyOptions); + } + break; + case 1: + return Bodies.polygon(x, y, Math.round(Common.random(4, 8)), Common.random(20, 50), bodyOptions); + + } + })); + }; + +})(); \ No newline at end of file diff --git a/examples/views.js b/examples/views.js new file mode 100644 index 00000000..205068a4 --- /dev/null +++ b/examples/views.js @@ -0,0 +1,143 @@ +(function() { + + var Engine = Matter.Engine, + World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composite = Matter.Composite, + Composites = Matter.Composites, + Common = Matter.Common, + Constraint = Matter.Constraint, + Events = Matter.Events, + Bounds = Matter.Bounds, + Vector = Matter.Vector, + Vertices = Matter.Vertices, + MouseConstraint = Matter.MouseConstraint, + Mouse = Matter.Mouse, + Query = Matter.Query, + Svg = Matter.Svg; + + Example.views = function(demo) { + var engine = demo.engine, + world = engine.world, + sceneEvents = demo.sceneEvents, + mouseConstraint = demo.mouseConstraint; + + var stack = Composites.stack(20, 20, 15, 4, 0, 0, function(x, y, column, row) { + switch (Math.round(Common.random(0, 1))) { + + case 0: + if (Common.random() < 0.8) { + return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50)); + } else { + return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30)); + } + break; + case 1: + var sides = Math.round(Common.random(1, 8)); + sides = (sides === 3) ? 4 : sides; + return Bodies.polygon(x, y, sides, Common.random(20, 50)); + } + }); + + World.add(world, stack); + + // get the centre of the viewport + var viewportCentre = { + x: engine.render.options.width * 0.5, + y: engine.render.options.height * 0.5 + }; + + // make the world bounds a little bigger than the render bounds + world.bounds.min.x = -300; + world.bounds.min.y = -300; + world.bounds.max.x = 1100; + world.bounds.max.y = 900; + + // keep track of current bounds scale (view zoom) + var boundsScaleTarget = 1, + boundsScale = { + x: 1, + y: 1 + }; + + // use the engine tick event to control our view + sceneEvents.push( + Events.on(engine, 'beforeTick', function() { + var world = engine.world, + mouse = mouseConstraint.mouse, + render = engine.render, + translate; + + // mouse wheel controls zoom + var scaleFactor = mouse.wheelDelta * -0.1; + if (scaleFactor !== 0) { + if ((scaleFactor < 0 && boundsScale.x >= 0.6) || (scaleFactor > 0 && boundsScale.x <= 1.4)) { + boundsScaleTarget += scaleFactor; + } + } + + // if scale has changed + if (Math.abs(boundsScale.x - boundsScaleTarget) > 0.01) { + // smoothly tween scale factor + scaleFactor = (boundsScaleTarget - boundsScale.x) * 0.2; + boundsScale.x += scaleFactor; + boundsScale.y += scaleFactor; + + // scale the render bounds + render.bounds.max.x = render.bounds.min.x + render.options.width * boundsScale.x; + render.bounds.max.y = render.bounds.min.y + render.options.height * boundsScale.y; + + // translate so zoom is from centre of view + translate = { + x: render.options.width * scaleFactor * -0.5, + y: render.options.height * scaleFactor * -0.5 + }; + + Bounds.translate(render.bounds, translate); + + // update mouse + Mouse.setScale(mouse, boundsScale); + Mouse.setOffset(mouse, render.bounds.min); + } + + // get vector from mouse relative to centre of viewport + var deltaCentre = Vector.sub(mouse.absolute, viewportCentre), + centreDist = Vector.magnitude(deltaCentre); + + // translate the view if mouse has moved over 50px from the centre of viewport + if (centreDist > 50) { + // create a vector to translate the view, allowing the user to control view speed + var direction = Vector.normalise(deltaCentre), + speed = Math.min(10, Math.pow(centreDist - 50, 2) * 0.0002); + + translate = Vector.mult(direction, speed); + + // prevent the view moving outside the world bounds + if (render.bounds.min.x + translate.x < world.bounds.min.x) + translate.x = world.bounds.min.x - render.bounds.min.x; + + if (render.bounds.max.x + translate.x > world.bounds.max.x) + translate.x = world.bounds.max.x - render.bounds.max.x; + + if (render.bounds.min.y + translate.y < world.bounds.min.y) + translate.y = world.bounds.min.y - render.bounds.min.y; + + if (render.bounds.max.y + translate.y > world.bounds.max.y) + translate.y = world.bounds.max.y - render.bounds.max.y; + + // move the view + Bounds.translate(render.bounds, translate); + + // we must update the mouse too + Mouse.setOffset(mouse, render.bounds.min); + } + }) + ); + + // must enable renderOptions.hasBounds for views to work + var renderOptions = engine.render.options; + renderOptions.hasBounds = true; + }; + +})(); \ No newline at end of file diff --git a/examples/wreckingBall.js b/examples/wreckingBall.js new file mode 100644 index 00000000..9f5ddfc3 --- /dev/null +++ b/examples/wreckingBall.js @@ -0,0 +1,49 @@ +(function() { + + var Engine = Matter.Engine, + World = Matter.World, + Bodies = Matter.Bodies, + Body = Matter.Body, + Composite = Matter.Composite, + Composites = Matter.Composites, + Common = Matter.Common, + Constraint = Matter.Constraint, + Events = Matter.Events, + Bounds = Matter.Bounds, + Vector = Matter.Vector, + Vertices = Matter.Vertices, + MouseConstraint = Matter.MouseConstraint, + Mouse = Matter.Mouse, + Query = Matter.Query, + Svg = Matter.Svg; + + Example.wreckingBall = function(demo) { + var engine = demo.engine, + world = engine.world, + mouseConstraint = demo.mouseConstraint; + + var rows = 10, + yy = 600 - 21 - 40 * rows; + + var stack = Composites.stack(400, yy, 5, rows, 0, 0, function(x, y, column, row) { + return Bodies.rectangle(x, y, 40, 40); + }); + + World.add(world, stack); + + var ball = Bodies.circle(100, 400, 50, { density: 0.04, frictionAir: 0.005}); + + World.add(world, ball); + World.add(world, Constraint.create({ + pointA: { x: 300, y: 100 }, + bodyB: ball + })); + + if (!demo.isMobile) { + var renderOptions = engine.render.options; + renderOptions.showCollisions = true; + renderOptions.showVelocity = true; + } + }; + +})(); \ No newline at end of file diff --git a/package.json b/package.json index 93ea921a..c3247c61 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "fast-json-patch": "^0.5.4", "grunt": "~0.4.2", "grunt-browserify": "~3.7.0", + "grunt-concat": "^0.1.6", "grunt-contrib-connect": "~0.6.0", "grunt-contrib-copy": "~0.5.0", "grunt-contrib-jshint": "~0.6.3", diff --git a/test/browser/TestDemo.js b/test/browser/TestDemo.js index dbf7bd8c..1639ecba 100644 --- a/test/browser/TestDemo.js +++ b/test/browser/TestDemo.js @@ -52,10 +52,11 @@ var test = function(status) { var worldStart = page.evaluate(function(demo) { var engine = Matter.Demo._engine; - if (!(demo in Matter.Demo)) { + if (!(demo in Matter.Example)) { throw '\'' + demo + '\' is not defined in Matter.Demo'; } - Matter.Demo[demo](); + Matter.Demo.reset(); + Matter.Example[demo](Matter.Demo.create()); return engine.world; }, demo); diff --git a/test/node/TestDemo.js b/test/node/TestDemo.js index 04a5eb85..0296c400 100644 --- a/test/node/TestDemo.js +++ b/test/node/TestDemo.js @@ -7,6 +7,7 @@ var path = require('path'); var $ = require('cheerio'); var Matter = require('../../build/matter-dev.js'); Matter.Demo = require('../../demo/js/Demo.js'); +Matter.Example = require('../../demo/js/Examples.js'); var demo, frames = 10, @@ -46,11 +47,12 @@ var test = function(status) { var engine = Matter.Demo._engine, runner = Matter.Runner.create(); - if (!(demo in Matter.Demo)) { - throw '\'' + demo + '\' is not defined in Matter.Demo'; + if (!(demo in Matter.Example)) { + throw '\'' + demo + '\' is not defined in Matter.Example'; } - Matter.Demo[demo](); + Matter.Demo.reset(); + Matter.Example[demo](Matter.Demo.create()); var worldStart = JSON.parse(resurrect.stringify(engine.world, precisionLimiter));