0
0
Fork 0
mirror of https://github.com/liabru/matter-js.git synced 2024-12-26 13:49:01 -05:00

Merge branch 'runner-improve' into browserify

This commit is contained in:
liabru 2015-08-13 21:07:58 +01:00
commit 9e3438f359
99 changed files with 1404935 additions and 534 deletions

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ matter-doc-theme
build/matter-dev.js build/matter-dev.js
build/matter-dev.min.js build/matter-dev.min.js
demo/js/lib/matter-dev.js demo/js/lib/matter-dev.js
test/browser/diffs

View file

@ -25,5 +25,18 @@
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : false, // Prohibit trailing whitespaces. "trailing" : false, // Prohibit trailing whitespaces.
"white" : false, // Check against strict whitespace and indentation rules. "white" : false, // Check against strict whitespace and indentation rules.
"indent" : 4 // Specify indentation spacing "indent" : 4, // Specify indentation spacing
// variables
"undef": true,
"-W079": true, // Silence redefinition errors (they are false positives).
"predef": [
"Matter", "window", "document", "Element", "MatterTools", "PIXI", "phantom", "module",
"$", "Image", "navigator", "setTimeout", "decomp", "HTMLElement", "require",
"Body", "Composite", "World", "Contact", "Detector", "Grid",
"Pairs", "Pair", "Resolver", "SAT", "Constraint", "MouseConstraint",
"Common", "Engine", "Mouse", "Sleeping", "Bodies", "Composites",
"Axes", "Bounds", "Vector", "Vertices", "Render", "RenderPixi",
"Events", "Query", "Runner", "Svg", "Metrics"
]
} }

View file

@ -1,7 +1,9 @@
.idea
node_modules node_modules
npm-debug.log npm-debug.log
doc doc
matter-doc-theme matter-doc-theme
build/matter-dev.js build/matter-dev.js
build/matter-dev.min.js build/matter-dev.min.js
demo/js/lib/matter-dev.js demo/js/lib/matter-dev.js
test/browser/diffs

View file

@ -1,6 +1,11 @@
language: node_js language: node_js
sudo: false
node_js: node_js:
- "0.10" - "0.10"
before_install: npm install -g grunt-cli before_install:
install: npm install - npm install -g grunt-cli
before_script: grunt - mkdir travis-phantomjs
- wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2
- tar -xvf $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -C $PWD/travis-phantomjs
- export PATH=$PWD/travis-phantomjs:$PATH
install: npm install

View file

@ -45,7 +45,7 @@ module.exports = function(grunt) {
options: { options: {
jshintrc: '.jshintrc' jshintrc: '.jshintrc'
}, },
all: ['src/**/*.js', 'demo/js/*.js', '!src/module/*'] all: ['src/**/*.js', 'demo/js/*.js', 'test/browser/TestDemo.js', '!src/module/*']
}, },
connect: { connect: {
watch: { watch: {
@ -54,6 +54,11 @@ module.exports = function(grunt) {
open: 'http://localhost:9000/demo/dev.html', open: 'http://localhost:9000/demo/dev.html',
livereload: 9001 livereload: 9001
} }
},
serve: {
options: {
port: 8000
}
} }
}, },
watch: { watch: {
@ -95,6 +100,19 @@ module.exports = function(grunt) {
src: 'build/<%= buildName %>.js', src: 'build/<%= buildName %>.js',
dest: 'build/<%= buildName %>.js' dest: 'build/<%= buildName %>.js'
} }
},
shell: {
testDemo: {
command: function(arg) {
arg = arg ? ' --' + arg : '';
return 'phantomjs test/browser/TestDemo.js' + arg;
},
options: {
execOptions: {
timeout: 1000 * 60
}
}
}
} }
}); });
@ -106,11 +124,25 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-yuidoc'); grunt.loadNpmTasks('grunt-contrib-yuidoc');
grunt.loadNpmTasks('grunt-preprocess'); grunt.loadNpmTasks('grunt-preprocess');
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('default', ['test', 'build']); grunt.registerTask('default', ['test', 'build']);
grunt.registerTask('test', ['jshint']); grunt.registerTask('test', ['build:dev', 'connect:serve', 'jshint', 'test:demo']);
grunt.registerTask('dev', ['build:dev', 'connect:watch', 'watch']); grunt.registerTask('dev', ['build:dev', 'connect:watch', 'watch']);
grunt.registerTask('test:demo', function() {
var updateAll = grunt.option('updateAll'),
diff = grunt.option('diff');
if (updateAll) {
grunt.task.run('shell:testDemo:updateAll');
} else if (diff) {
grunt.task.run('shell:testDemo:diff');
} else {
grunt.task.run('shell:testDemo');
}
});
grunt.registerTask('build', function(mode) { grunt.registerTask('build', function(mode) {
var isDev = (mode === 'dev'), var isDev = (mode === 'dev'),
isRelease = (mode === 'release'), isRelease = (mode === 'release'),

File diff suppressed because it is too large Load diff

8
build/matter.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -25,15 +25,18 @@
} }
var Demo = {}; var Demo = {};
Matter.Demo = Demo;
var _engine, var _engine,
_runner,
_gui, _gui,
_inspector, _inspector,
_sceneName, _sceneName,
_mouseConstraint, _mouseConstraint,
_sceneEvents = [], _sceneEvents = [],
_useInspector = window.location.hash.indexOf('-inspect') !== -1, _useInspector = window.location.hash.indexOf('-inspect') !== -1,
_isMobile = /(ipad|iphone|ipod|android)/gi.test(navigator.userAgent); _isMobile = /(ipad|iphone|ipod|android)/gi.test(navigator.userAgent),
_isAutomatedTest = window._phantom ? true : false;
// initialise the demo // initialise the demo
@ -56,8 +59,14 @@
_mouseConstraint = MouseConstraint.create(_engine); _mouseConstraint = MouseConstraint.create(_engine);
World.add(_engine.world, _mouseConstraint); World.add(_engine.world, _mouseConstraint);
// engine reference for external use
Matter.Demo._engine = _engine;
// skip runner when performing automated tests
if (_isAutomatedTest) return;
// run the engine // run the engine
Engine.run(_engine); _runner = Engine.run(_engine);
// default scene function name // default scene function name
_sceneName = 'mixed'; _sceneName = 'mixed';
@ -344,7 +353,7 @@
World.add(_engine.world, [ground, pyramid, ground2, pyramid2, rock, elastic]); World.add(_engine.world, [ground, pyramid, ground2, pyramid2, rock, elastic]);
Events.on(_engine, 'tick', function() { Events.on(_engine, 'afterUpdate', function() {
if (_mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) { if (_mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) {
rock = Bodies.polygon(170, 450, 7, 20, rockOptions); rock = Bodies.polygon(170, 450, 7, 20, rockOptions);
World.add(_engine.world, rock); World.add(_engine.world, rock);
@ -491,7 +500,7 @@
_world.gravity.y = 0; _world.gravity.y = 0;
_sceneEvents.push( _sceneEvents.push(
Events.on(_engine, 'tick', function(event) { Events.on(_engine, 'afterUpdate', function(event) {
var time = _engine.timing.timestamp; var time = _engine.timing.timestamp;
Composite.translate(stack, { Composite.translate(stack, {
@ -986,7 +995,7 @@
counter = 0; counter = 0;
_sceneEvents.push( _sceneEvents.push(
Events.on(_engine, 'tick', function(event) { Events.on(_engine, 'afterUpdate', function(event) {
// tween the timescale for bullet time slow-mo // tween the timescale for bullet time slow-mo
_engine.timing.timeScale += (timeScaleTarget - _engine.timing.timeScale) * 0.05; _engine.timing.timeScale += (timeScaleTarget - _engine.timing.timeScale) * 0.05;
@ -1482,7 +1491,7 @@
World.add(_world, [stack, concave]); World.add(_world, [stack, concave]);
_sceneEvents.push( _sceneEvents.push(
Events.on(_engine, 'afterRender', function() { Events.on(_engine.render, 'afterRender', function() {
var mouse = _mouseConstraint.mouse, var mouse = _mouseConstraint.mouse,
context = _engine.render.context, context = _engine.render.context,
bodies = Composite.allBodies(_engine.world), bodies = Composite.allBodies(_engine.world),
@ -1610,7 +1619,7 @@
// create a Matter.Gui // create a Matter.Gui
if (!_isMobile && Gui) { if (!_isMobile && Gui) {
_gui = Gui.create(_engine); _gui = Gui.create(_engine, _runner);
// need to add mouse constraint back in after gui clear or load is pressed // need to add mouse constraint back in after gui clear or load is pressed
Events.on(_gui, 'clear load', function() { Events.on(_gui, 'clear load', function() {
@ -1626,7 +1635,7 @@
// create a Matter.Inspector // create a Matter.Inspector
if (!_isMobile && Inspector && _useInspector) { if (!_isMobile && Inspector && _useInspector) {
_inspector = Inspector.create(_engine); _inspector = Inspector.create(_engine, _runner);
Events.on(_inspector, 'import', function() { Events.on(_inspector, 'import', function() {
_mouseConstraint = MouseConstraint.create(_engine); _mouseConstraint = MouseConstraint.create(_engine);
@ -1729,6 +1738,16 @@
Events.off(_world, _sceneEvents[i]); Events.off(_world, _sceneEvents[i]);
} }
if (_runner && _runner.events) {
for (i = 0; i < _sceneEvents.length; i++)
Events.off(_runner, _sceneEvents[i]);
}
if (_engine.render.events) {
for (i = 0; i < _sceneEvents.length; i++)
Events.off(_engine.render, _sceneEvents[i]);
}
_sceneEvents = []; _sceneEvents = [];
// reset id pool // reset id pool

View file

@ -17,7 +17,8 @@
var _engine, var _engine,
_sceneName = 'mixed', _sceneName = 'mixed',
_sceneWidth, _sceneWidth,
_sceneHeight; _sceneHeight,
_deviceOrientationEvent;
Demo.init = function() { Demo.init = function() {
var canvasContainer = document.getElementById('canvas-container'), var canvasContainer = document.getElementById('canvas-container'),
@ -44,10 +45,15 @@
}, 800); }, 800);
}); });
window.addEventListener('deviceorientation', Demo.updateGravity, true); window.addEventListener('deviceorientation', function(event) {
_deviceOrientationEvent = event;
Demo.updateGravity(event);
}, true);
window.addEventListener('touchstart', Demo.fullscreen); window.addEventListener('touchstart', Demo.fullscreen);
window.addEventListener('orientationchange', function() { window.addEventListener('orientationchange', function() {
Demo.updateGravity(); Demo.updateGravity(_deviceOrientationEvent);
Demo.updateScene(); Demo.updateScene();
Demo.fullscreen(); Demo.fullscreen();
}, false); }, false);
@ -101,7 +107,7 @@
Demo[_sceneName](); Demo[_sceneName]();
}; };
Demo.updateGravity = function () { Demo.updateGravity = function(event) {
if (!_engine) if (!_engine)
return; return;

View file

@ -1,5 +1,5 @@
/** /**
* matter-tools-dev.min.js 0.5.0-dev 2015-05-24 * matter-tools-dev.min.js 0.5.0-dev 2015-07-27
* https://github.com/liabru/matter-tools * https://github.com/liabru/matter-tools
* License: MIT * License: MIT
*/ */
@ -10,7 +10,7 @@
var Gui = {}; var Gui = {};
(function() { (function() {
var _isWebkit = "WebkitAppearance" in document.documentElement.style; var _isWebkit = "WebkitAppearance" in document.documentElement.style;
Gui.create = function(engine, options) { Gui.create = function(engine, runner, options) {
var _datGuiSupported = window.dat && window.localStorage; var _datGuiSupported = window.dat && window.localStorage;
if (!_datGuiSupported) { if (!_datGuiSupported) {
console.log("Could not create GUI. Check dat.gui library is loaded first."); console.log("Could not create GUI. Check dat.gui library is loaded first.");
@ -19,6 +19,7 @@
var datGui = new dat.GUI(options); var datGui = new dat.GUI(options);
var gui = { var gui = {
engine:engine, engine:engine,
runner:runner,
datGui:datGui, datGui:datGui,
broadphase:"grid", broadphase:"grid",
broadphaseCache:{ broadphaseCache:{
@ -98,7 +99,7 @@
return clone; return clone;
}; };
var _initDatGui = function(gui) { var _initDatGui = function(gui) {
var engine = gui.engine, datGui = gui.datGui; var engine = gui.engine, runner = gui.runner, datGui = gui.datGui;
var funcs = { var funcs = {
addBody:function() { addBody:function() {
_addBody(gui); _addBody(gui);
@ -115,7 +116,7 @@
Events.trigger(gui, "load"); Events.trigger(gui, "load");
}, },
inspect:function() { inspect:function() {
if (!Inspector.instance) gui.inspector = Inspector.create(gui.engine); if (!Inspector.instance) gui.inspector = Inspector.create(gui.engine, gui.runner);
}, },
recordGif:function() { recordGif:function() {
if (!gui.isRecording) { if (!gui.isRecording) {
@ -152,10 +153,10 @@
} }
}; };
var metrics = datGui.addFolder("Metrics"); var metrics = datGui.addFolder("Metrics");
metrics.add(engine.timing, "fps").listen(); metrics.add(runner, "fps").listen();
if (engine.metrics.extended) { if (engine.metrics.extended) {
metrics.add(engine.timing, "delta").listen(); metrics.add(runner, "delta").listen();
metrics.add(engine.timing, "correction").listen(); metrics.add(runner, "correction").listen();
metrics.add(engine.metrics, "bodies").listen(); metrics.add(engine.metrics, "bodies").listen();
metrics.add(engine.metrics, "collisions").listen(); metrics.add(engine.metrics, "collisions").listen();
metrics.add(engine.metrics, "pairs").listen(); metrics.add(engine.metrics, "pairs").listen();
@ -201,7 +202,7 @@
physics.add(engine.timing, "timeScale", 0, 1.2).step(.05).listen(); physics.add(engine.timing, "timeScale", 0, 1.2).step(.05).listen();
physics.add(engine, "velocityIterations", 1, 10).step(1); physics.add(engine, "velocityIterations", 1, 10).step(1);
physics.add(engine, "positionIterations", 1, 10).step(1); physics.add(engine, "positionIterations", 1, 10).step(1);
physics.add(engine, "enabled"); physics.add(runner, "enabled");
physics.open(); physics.open();
var render = datGui.addFolder("Render"); var render = datGui.addFolder("Render");
render.add(gui, "renderer", [ "canvas", "webgl" ]).onFinishChange(function(value) { render.add(gui, "renderer", [ "canvas", "webgl" ]).onFinishChange(function(value) {
@ -269,7 +270,7 @@
return; return;
} }
var engine = gui.engine, skipFrame = false; var engine = gui.engine, skipFrame = false;
Matter.Events.on(engine, "beforeTick", function(event) { Matter.Events.on(gui.runner, "beforeTick", function(event) {
if (gui.isRecording && !skipFrame) { if (gui.isRecording && !skipFrame) {
gui.gif.addFrame(engine.render.context, { gui.gif.addFrame(engine.render.context, {
copy:true, copy:true,
@ -283,13 +284,14 @@
var Inspector = {}; var Inspector = {};
(function() { (function() {
var _key, _isWebkit = "WebkitAppearance" in document.documentElement.style, $body; var _key, _isWebkit = "WebkitAppearance" in document.documentElement.style, $body;
Inspector.create = function(engine, options) { Inspector.create = function(engine, runner, options) {
if (!jQuery || !$.fn.jstree || !window.key) { if (!jQuery || !$.fn.jstree || !window.key) {
console.log("Could not create inspector. Check keymaster, jQuery, jsTree libraries are loaded first."); console.log("Could not create inspector. Check keymaster, jQuery, jsTree libraries are loaded first.");
return; return;
} }
var inspector = { var inspector = {
engine:engine, engine:engine,
runner:runner,
isPaused:false, isPaused:false,
selected:[], selected:[],
selectStart:null, selectStart:null,
@ -566,7 +568,7 @@
}; };
var _initEngineEvents = function(inspector) { var _initEngineEvents = function(inspector) {
var engine = inspector.engine, mouse = inspector.mouse, mousePosition = _getMousePosition(inspector), controls = inspector.controls; var engine = inspector.engine, mouse = inspector.mouse, mousePosition = _getMousePosition(inspector), controls = inspector.controls;
Events.on(engine, "tick", function() { Events.on(inspector.engine, "beforeUpdate", function() {
mousePosition = _getMousePosition(inspector); mousePosition = _getMousePosition(inspector);
var mouseDelta = mousePosition.x - inspector.mousePrevPosition.x, keyDelta = _key.isPressed("up") + _key.isPressed("right") - _key.isPressed("down") - _key.isPressed("left"), delta = mouseDelta + keyDelta; var mouseDelta = mousePosition.x - inspector.mousePrevPosition.x, keyDelta = _key.isPressed("up") + _key.isPressed("right") - _key.isPressed("down") - _key.isPressed("left"), delta = mouseDelta + keyDelta;
if (engine.world.isModified) { if (engine.world.isModified) {
@ -672,7 +674,7 @@
_updateSelectedMouseDownOffset(inspector); _updateSelectedMouseDownOffset(inspector);
} }
}); });
Events.on(engine, "afterRender", function() { Events.on(inspector.engine.render, "afterRender", function() {
var renderController = engine.render.controller, context = engine.render.context; var renderController = engine.render.controller, context = engine.render.context;
if (renderController.inspector) renderController.inspector(inspector, context); if (renderController.inspector) renderController.inspector(inspector, context);
}); });

View file

@ -20,6 +20,7 @@
"rigid body physics" "rigid body physics"
], ],
"devDependencies": { "devDependencies": {
"fast-json-patch": "^0.5.4",
"grunt": "~0.4.2", "grunt": "~0.4.2",
"grunt-browserify": "~3.7.0", "grunt-browserify": "~3.7.0",
"grunt-contrib-connect": "~0.6.0", "grunt-contrib-connect": "~0.6.0",
@ -28,7 +29,8 @@
"grunt-contrib-uglify": "~0.2.7", "grunt-contrib-uglify": "~0.2.7",
"grunt-contrib-watch": "~0.5.3", "grunt-contrib-watch": "~0.5.3",
"grunt-contrib-yuidoc": "~0.5.1", "grunt-contrib-yuidoc": "~0.5.1",
"grunt-preprocess": "^4.1.0" "grunt-preprocess": "^4.1.0",
"grunt-shell": "^1.1.2"
}, },
"scripts": { "scripts": {
"dev": "npm install && grunt dev", "dev": "npm install && grunt dev",

View file

@ -58,6 +58,7 @@ module.exports = Resolver;
normal, normal,
bodyBtoA, bodyBtoA,
contactShare, contactShare,
positionImpulse,
contactCount = {}, contactCount = {},
tempA = Vector._temp[0], tempA = Vector._temp[0],
tempB = Vector._temp[1], tempB = Vector._temp[1],

View file

@ -13,7 +13,7 @@ var Body = require('../body/Body');
/** /**
* The `Matter.Engine` module contains methods for creating and manipulating engines. * The `Matter.Engine` module contains methods for creating and manipulating engines.
* An engine is a controller that manages updating and rendering the simulation of the world. * An engine is a controller that manages updating the simulation of the world.
* See `Matter.Runner` for an optional game loop utility. * See `Matter.Runner` for an optional game loop utility.
* *
* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) * See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
@ -28,9 +28,6 @@ module.exports = Engine;
(function() { (function() {
var _fps = 60,
_delta = 1000 / _fps;
/** /**
* Creates a new engine. The options parameter is an object that specifies any properties you wish to override the defaults. * Creates a new engine. The options parameter is an object that specifies any properties you wish to override the defaults.
* All properties have default values, and many are pre-calculated automatically based on other properties. * All properties have default values, and many are pre-calculated automatically based on other properties.
@ -47,49 +44,55 @@ module.exports = Engine;
element = Common.isElement(element) ? element : null; element = Common.isElement(element) ? element : null;
var defaults = { var defaults = {
enabled: true,
positionIterations: 6, positionIterations: 6,
velocityIterations: 4, velocityIterations: 4,
constraintIterations: 2, constraintIterations: 2,
enableSleeping: false, enableSleeping: false,
events: [], events: [],
timing: { timing: {
fps: _fps,
timestamp: 0, timestamp: 0,
delta: _delta, timeScale: 1
correction: 1,
deltaMin: 1000 / _fps,
deltaMax: 1000 / (_fps * 0.5),
timeScale: 1,
isFixed: false,
frameRequestId: 0
},
render: {
element: element,
controller: Render
}, },
broadphase: { broadphase: {
controller: Grid controller: Grid
} }
}; };
var engine = Common.extend(defaults, options); var engine = Common.extend(defaults, options);
engine.render = engine.render.controller.create(engine.render); if (element || engine.render) {
var renderDefaults = {
element: element,
controller: Render
};
engine.render = Common.extend(renderDefaults, engine.render);
}
if (engine.render && engine.render.controller) {
engine.render = engine.render.controller.create(engine.render);
}
engine.world = World.create(engine.world); engine.world = World.create(engine.world);
engine.pairs = Pairs.create(); engine.pairs = Pairs.create();
engine.broadphase = engine.broadphase.controller.create(engine.broadphase); engine.broadphase = engine.broadphase.controller.create(engine.broadphase);
engine.metrics = engine.metrics || { extended: false }; engine.metrics = engine.metrics || { extended: false };
// @if DEBUG // @if DEBUG
engine.metrics = engine.metrics || Metrics.create(); engine.metrics = Metrics.create(engine.metrics);
// @endif // @endif
return engine; return engine;
}; };
/** /**
* Moves the simulation forward in time by `delta` ms. * Moves the simulation forward in time by `delta` ms.
* The `correction` argument is an optional `Number` that specifies the time correction factor to apply to the update.
* This can help improve the accuracy of the simulation in cases where `delta` is changing between updates.
* The value of `correction` is defined as `delta / lastDelta`, i.e. the percentage change of `delta` over the last step.
* Therefore the value is always `1` (no correction) when `delta` constant (or when no correction is desired, which is the default).
* See the paper on <a href="http://lonesock.net/article/verlet.html">Time Corrected Verlet</a> for more information.
*
* Triggers `beforeUpdate` and `afterUpdate` events. * Triggers `beforeUpdate` and `afterUpdate` events.
* Triggers `collisionStart`, `collisionActive` and `collisionEnd` events. * Triggers `collisionStart`, `collisionActive` and `collisionEnd` events.
* @method update * @method update
@ -108,11 +111,10 @@ module.exports = Engine;
// increment timestamp // increment timestamp
timing.timestamp += delta * timing.timeScale; timing.timestamp += delta * timing.timeScale;
timing.correction = correction;
// create an event object // create an event object
var event = { var event = {
timestamp: engine.timing.timestamp timestamp: timing.timestamp
}; };
Events.trigger(engine, 'beforeUpdate', event); Events.trigger(engine, 'beforeUpdate', event);
@ -211,22 +213,6 @@ module.exports = Engine;
return engine; return engine;
}; };
/**
* Renders the world by calling its defined renderer `engine.render.controller`. Triggers `beforeRender` and `afterRender` events.
* @method render
* @param {engine} engine
*/
Engine.render = function(engine) {
// create an event object
var event = {
timestamp: engine.timing.timestamp
};
Events.trigger(engine, 'beforeRender', event);
engine.render.controller.world(engine);
Events.trigger(engine, 'afterRender', event);
};
/** /**
* Merges two engines by keeping the configuration of `engineA` but replacing the world with the one from `engineB`. * Merges two engines by keeping the configuration of `engineA` but replacing the world with the one from `engineB`.
@ -336,38 +322,12 @@ module.exports = Engine;
* @param {engine} engine * @param {engine} engine
*/ */
/*
*
* Events Documentation
*
*/
/**
* Fired at the start of a tick, before any updates to the engine or timing
*
* @event beforeTick
* @param {} event An event object
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired after engine timing updated, but just before engine state updated
*
* @event tick
* @param {} event An event object
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/** /**
* Fired just before an update * Fired just before an update
* *
* @event beforeUpdate * @event beforeUpdate
* @param {} event An event object * @param {} event An event object
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {} event.source The source object of the event
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */
@ -377,37 +337,7 @@ module.exports = Engine;
* *
* @event afterUpdate * @event afterUpdate
* @param {} event An event object * @param {} event An event object
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired just before rendering
*
* @event beforeRender
* @param {} event An event object
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired after rendering
*
* @event afterRender
* @param {} event An event object
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired after engine update and after rendering
*
* @event afterTick
* @param {} event An event object
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick
* @param {} event.source The source object of the event * @param {} event.source The source object of the event
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */
@ -418,7 +348,7 @@ module.exports = Engine;
* @event collisionStart * @event collisionStart
* @param {} event An event object * @param {} event An event object
* @param {} event.pairs List of affected pairs * @param {} event.pairs List of affected pairs
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {} event.source The source object of the event
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */
@ -429,7 +359,7 @@ module.exports = Engine;
* @event collisionActive * @event collisionActive
* @param {} event An event object * @param {} event An event object
* @param {} event.pairs List of affected pairs * @param {} event.pairs List of affected pairs
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {} event.source The source object of the event
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */
@ -440,7 +370,7 @@ module.exports = Engine;
* @event collisionEnd * @event collisionEnd
* @param {} event An event object * @param {} event An event object
* @param {} event.pairs List of affected pairs * @param {} event.pairs List of affected pairs
* @param {DOMHighResTimeStamp} event.timestamp The timestamp of the current tick * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event * @param {} event.source The source object of the event
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */
@ -451,14 +381,6 @@ module.exports = Engine;
* *
*/ */
/**
* A flag that specifies whether the engine is running or not.
*
* @property enabled
* @type boolean
* @default true
*/
/** /**
* An integer `Number` that specifies the number of position iterations to perform each update. * An integer `Number` that specifies the number of position iterations to perform each update.
* The higher the value, the higher quality the simulation will be at the expense of performance. * The higher the value, the higher quality the simulation will be at the expense of performance.
@ -516,46 +438,13 @@ module.exports = Engine;
/** /**
* A `Number` that specifies the current simulation-time in milliseconds starting from `0`. * A `Number` that specifies the current simulation-time in milliseconds starting from `0`.
* It is incremented on every `Engine.update` by the `timing.delta`. * It is incremented on every `Engine.update` by the given `delta` argument.
* *
* @property timing.timestamp * @property timing.timestamp
* @type number * @type number
* @default 0 * @default 0
*/ */
/**
* A `Boolean` that specifies if the `Engine.run` game loop should use a fixed timestep (otherwise it is variable).
* If timing is fixed, then the apparent simulation speed will change depending on the frame rate (but behaviour will be deterministic).
* If the timing is variable, then the apparent simulation speed will be constant (approximately, but at the cost of determininism).
*
* @property timing.isFixed
* @type boolean
* @default false
*/
/**
* A `Number` that specifies the time step between updates in milliseconds.
* If `engine.timing.isFixed` is set to `true`, then `delta` is fixed.
* If it is `false`, then `delta` can dynamically change to maintain the correct apparent simulation speed.
*
* @property timing.delta
* @type number
* @default 1000 / 60
*/
/**
* A `Number` that specifies the time correction factor to apply to the current timestep.
* It is automatically handled when using `Engine.run`, but is also only optional even if you use your own game loop.
* The value is defined as `delta / lastDelta`, i.e. the percentage change of `delta` between steps.
* This value is always `1` (no correction) when frame rate is constant or `engine.timing.isFixed` is `true`.
* If the framerate and hence `delta` are changing, then correction should be applied to the current update to account for the change.
* See the paper on <a href="http://lonesock.net/article/verlet.html">Time Corrected Verlet</a> for more information.
*
* @property timing.correction
* @type number
* @default 1
*/
/** /**
* An instance of a `Render` controller. The default value is a `Matter.Render` instance created by `Engine.create`. * An instance of a `Render` controller. The default value is a `Matter.Render` instance created by `Engine.create`.
* One may also develop a custom renderer module based on `Matter.Render` and pass an instance of it to `Engine.create` via `options.render`. * One may also develop a custom renderer module based on `Matter.Render` and pass an instance of it to `Engine.create` via `options.render`.

View file

@ -1,5 +1,6 @@
// @if DEBUG // @if DEBUG
var Composite = require('../body/Composite'); var Composite = require('../body/Composite');
var Common = require('./Common');
/** /**
* _Internal Class_, not generally used outside of the engine's internals. * _Internal Class_, not generally used outside of the engine's internals.
@ -18,8 +19,8 @@ module.exports = Metrics;
* @private * @private
* @return {metrics} A new metrics * @return {metrics} A new metrics
*/ */
Metrics.create = function() { Metrics.create = function(options) {
return { var defaults = {
extended: false, extended: false,
narrowDetections: 0, narrowDetections: 0,
narrowphaseTests: 0, narrowphaseTests: 0,
@ -35,6 +36,8 @@ module.exports = Metrics;
bodies: 0, bodies: 0,
pairs: 0 pairs: 0
}; };
return Common.extend(defaults, false, options);
}; };
/** /**

View file

@ -5,6 +5,9 @@ var Common = require('./Common');
/** /**
* The `Matter.Runner` module is an optional utility which provides a game loop, * The `Matter.Runner` module is an optional utility which provides a game loop,
* that handles updating and rendering a `Matter.Engine` for you within a browser. * that handles updating and rendering a `Matter.Engine` for you within a browser.
* It is intended for demo and testing purposes, but may be adequate for simple games.
* If you are using your own game loop instead, then you do not need the `Matter.Runner` module.
* Instead just call `Engine.update(engine, delta)` in your own loop.
* Note that the method `Engine.run` is an alias for `Runner.run`. * Note that the method `Engine.run` is an alias for `Runner.run`.
* *
* See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js) * See [Demo.js](https://github.com/liabru/matter-js/blob/master/demo/js/Demo.js)
@ -19,114 +22,285 @@ module.exports = Runner;
(function() { (function() {
var _fps = 60, if (typeof window === 'undefined') {
_deltaSampleSize = _fps, // TODO: support Runner on non-browser environments.
_delta = 1000 / _fps; return;
}
var _requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame var _requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame
|| function(callback){ window.setTimeout(function() { callback(Common.now()); }, _delta); }; || function(callback){ window.setTimeout(function() { callback(Common.now()); }, 1000 / 60); };
var _cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame var _cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame;
/** /**
* Provides a basic game loop that handles updating the engine for you. * Creates a new Runner. The options parameter is an object that specifies any properties you wish to override the defaults.
* Calls `Engine.update` and `Engine.render` on the `requestAnimationFrame` event automatically. * @method create
* Handles time correction and non-fixed dynamic timing (if enabled). * @param {} options
* Triggers `beforeTick`, `tick` and `afterTick` events.
* @method run
* @param {engine} engine
*/ */
Runner.run = function(engine) { Runner.create = function(options) {
var counterTimestamp = 0, var defaults = {
frameCounter = 0, fps: 60,
deltaHistory = [], correction: 1,
timePrev, deltaSampleSize: 60,
timeScalePrev = 1; counterTimestamp: 0,
frameCounter: 0,
deltaHistory: [],
timePrev: null,
timeScalePrev: 1,
frameRequestId: null,
isFixed: false,
enabled: true
};
(function render(time){ var runner = Common.extend(defaults, options);
var timing = engine.timing,
delta,
correction = 1;
timing.frameRequestId = _requestAnimationFrame(render); runner.delta = runner.delta || 1000 / runner.fps;
runner.deltaMin = runner.deltaMin || 1000 / runner.fps;
runner.deltaMax = runner.deltaMax || 1000 / (runner.fps * 0.5);
runner.fps = 1000 / runner.delta;
if (!engine.enabled) return runner;
return;
// create an event object
var event = {
timestamp: time
};
Events.trigger(engine, 'beforeTick', event);
if (timing.isFixed) {
// fixed timestep
delta = timing.delta;
} else {
// dynamic timestep based on wall clock between calls
delta = (time - timePrev) || timing.delta;
timePrev = time;
// optimistically filter delta over a few frames, to improve stability
deltaHistory.push(delta);
deltaHistory = deltaHistory.slice(-_deltaSampleSize);
delta = Math.min.apply(null, deltaHistory);
// limit delta
delta = delta < timing.deltaMin ? timing.deltaMin : delta;
delta = delta > timing.deltaMax ? timing.deltaMax : delta;
// time correction for delta
correction = delta / timing.delta;
// update engine timing object
timing.delta = delta;
}
// time correction for time scaling
if (timeScalePrev !== 0)
correction *= timing.timeScale / timeScalePrev;
if (timing.timeScale === 0)
correction = 0;
timeScalePrev = timing.timeScale;
// fps counter
frameCounter += 1;
if (time - counterTimestamp >= 1000) {
timing.fps = frameCounter * ((time - counterTimestamp) / 1000);
counterTimestamp = time;
frameCounter = 0;
}
Events.trigger(engine, 'tick', event);
// if world has been modified, clear the render scene graph
if (engine.world.isModified && engine.render.controller.clear)
engine.render.controller.clear(engine.render);
// update
Engine.update(engine, delta, correction);
// render
Engine.render(engine);
Events.trigger(engine, 'afterTick', event);
})();
}; };
/** /**
* Ends execution of `Runner.run` on the given `engine`, by canceling the animation frame request event loop. * Continuously ticks a `Matter.Engine` by calling `Runner.tick` on the `requestAnimationFrame` event.
* If you wish to only temporarily pause the engine, see `engine.enabled` instead. * @method run
* @method stop
* @param {engine} engine * @param {engine} engine
*/ */
Runner.stop = function(engine) { Runner.run = function(runner, engine) {
_cancelAnimationFrame(engine.timing.frameRequestId); // create runner if engine is first argument
if (typeof runner.positionIterations !== 'undefined') {
engine = runner;
runner = Runner.create();
}
(function render(time){
runner.frameRequestId = _requestAnimationFrame(render);
if (time && runner.enabled) {
Runner.tick(runner, engine, time);
}
})();
return runner;
}; };
/**
* A game loop utility that updates the engine and renderer by one step (a 'tick').
* Features delta smoothing, time correction and fixed or dynamic timing.
* Triggers `beforeTick`, `tick` and `afterTick` events on the engine.
* Consider just `Engine.update(engine, delta)` if you're using your own loop.
* @method tick
* @param {runner} runner
* @param {engine} engine
* @param {number} time
*/
Runner.tick = function(runner, engine, time) {
var timing = engine.timing,
correction = 1,
delta;
// create an event object
var event = {
timestamp: timing.timestamp
};
Events.trigger(runner, 'beforeTick', event);
Events.trigger(engine, 'beforeTick', event); // @deprecated
if (runner.isFixed) {
// fixed timestep
delta = runner.delta;
} else {
// dynamic timestep based on wall clock between calls
delta = (time - runner.timePrev) || runner.delta;
runner.timePrev = time;
// optimistically filter delta over a few frames, to improve stability
runner.deltaHistory.push(delta);
runner.deltaHistory = runner.deltaHistory.slice(-runner.deltaSampleSize);
delta = Math.min.apply(null, runner.deltaHistory);
// limit delta
delta = delta < runner.deltaMin ? runner.deltaMin : delta;
delta = delta > runner.deltaMax ? runner.deltaMax : delta;
// correction for delta
correction = delta / runner.delta;
// update engine timing object
runner.delta = delta;
}
// time correction for time scaling
if (runner.timeScalePrev !== 0)
correction *= timing.timeScale / runner.timeScalePrev;
if (timing.timeScale === 0)
correction = 0;
runner.timeScalePrev = timing.timeScale;
runner.correction = correction;
// fps counter
runner.frameCounter += 1;
if (time - runner.counterTimestamp >= 1000) {
runner.fps = runner.frameCounter * ((time - runner.counterTimestamp) / 1000);
runner.counterTimestamp = time;
runner.frameCounter = 0;
}
Events.trigger(runner, 'tick', event);
Events.trigger(engine, 'tick', event); // @deprecated
// if world has been modified, clear the render scene graph
if (engine.world.isModified
&& engine.render
&& engine.render.controller
&& engine.render.controller.clear) {
engine.render.controller.clear(engine.render);
}
// update
Events.trigger(runner, 'beforeUpdate', event);
Engine.update(engine, delta, correction);
Events.trigger(runner, 'afterUpdate', event);
// render
if (engine.render) {
Events.trigger(runner, 'beforeRender', event);
Events.trigger(engine, 'beforeRender', event); // @deprecated
engine.render.controller.world(engine);
Events.trigger(runner, 'afterRender', event);
Events.trigger(engine, 'afterRender', event); // @deprecated
}
Events.trigger(runner, 'afterTick', event);
Events.trigger(engine, 'afterTick', event); // @deprecated
};
/**
* Ends execution of `Runner.run` on the given `runner`, by canceling the animation frame request event loop.
* If you wish to only temporarily pause the engine, see `engine.enabled` instead.
* @method stop
* @param {runner} runner
*/
Runner.stop = function(runner) {
_cancelAnimationFrame(runner.frameRequestId);
};
/*
*
* Events Documentation
*
*/
/**
* Fired at the start of a tick, before any updates to the engine or timing
*
* @event beforeTick
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired after engine timing updated, but just before update
*
* @event tick
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired at the end of a tick, after engine update and after rendering
*
* @event afterTick
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired before update
*
* @event beforeUpdate
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired after update
*
* @event afterUpdate
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired before rendering
*
* @event beforeRender
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired after rendering
*
* @event afterRender
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/*
*
* Properties Documentation
*
*/
/**
* A flag that specifies whether the runner is running or not.
*
* @property enabled
* @type boolean
* @default true
*/
/**
* A `Boolean` that specifies if the runner should use a fixed timestep (otherwise it is variable).
* If timing is fixed, then the apparent simulation speed will change depending on the frame rate (but behaviour will be deterministic).
* If the timing is variable, then the apparent simulation speed will be constant (approximately, but at the cost of determininism).
*
* @property isFixed
* @type boolean
* @default false
*/
/**
* A `Number` that specifies the time step between updates in milliseconds.
* If `engine.timing.isFixed` is set to `true`, then `delta` is fixed.
* If it is `false`, then `delta` can dynamically change to maintain the correct apparent simulation speed.
*
* @property delta
* @type number
* @default 1000 / 60
*/
})(); })();

View file

@ -30,7 +30,7 @@ module.exports = Svg;
var i, il, total, point, segment, segments, var i, il, total, point, segment, segments,
segmentsQueue, lastSegment, segmentsQueue, lastSegment,
lastPoint, segmentIndex, points = [], lastPoint, segmentIndex, points = [],
length = 0, x = 0, y = 0; lx, ly, length = 0, x = 0, y = 0;
sampleLength = sampleLength || 15; sampleLength = sampleLength || 15;

View file

@ -1,6 +1,7 @@
var Common = require('../core/Common'); var Common = require('../core/Common');
var Composite = require('../body/Composite'); var Composite = require('../body/Composite');
var Bounds = require('../geometry/Bounds'); var Bounds = require('../geometry/Bounds');
var Events = require('../core/Events');
var Grid = require('../collision/Grid'); var Grid = require('../collision/Grid');
/** /**
@ -134,6 +135,12 @@ module.exports = Render;
constraints = [], constraints = [],
i; i;
var event = {
timestamp: engine.timing.timestamp
};
Events.trigger(render, 'beforeRender', event);
// apply background if it has changed // apply background if it has changed
if (render.currentBackground !== background) if (render.currentBackground !== background)
_applyBackground(render, background); _applyBackground(render, background);
@ -231,6 +238,8 @@ module.exports = Render;
// revert view transforms // revert view transforms
context.setTransform(options.pixelRatio, 0, 0, options.pixelRatio, 0, 0); context.setTransform(options.pixelRatio, 0, 0, options.pixelRatio, 0, 0);
} }
Events.trigger(render, 'afterRender', event);
}; };
/** /**
@ -394,7 +403,8 @@ module.exports = Render;
options = render.options, options = render.options,
body, body,
part, part,
i; i,
k;
for (i = 0; i < bodies.length; i++) { for (i = 0; i < bodies.length; i++) {
body = bodies[i]; body = bodies[i];
@ -691,7 +701,8 @@ module.exports = Render;
options = render.options, options = render.options,
body, body,
part, part,
i; i,
k;
c.beginPath(); c.beginPath();
@ -1027,7 +1038,7 @@ module.exports = Render;
} }
context.setLineDash([0]); context.setLineDash([]);
context.translate(-0.5, -0.5); context.translate(-0.5, -0.5);
} }
@ -1123,6 +1134,32 @@ module.exports = Render;
render.currentBackground = background; render.currentBackground = background;
}; };
/*
*
* Events Documentation
*
*/
/**
* Fired before rendering
*
* @event beforeRender
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/**
* Fired after rendering
*
* @event afterRender
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
*/
/* /*
* *
* Properties Documentation * Properties Documentation

192
test/browser/TestDemo.js Normal file
View file

@ -0,0 +1,192 @@
var page = require('webpage').create();
var fs = require('fs');
var Resurrect = require('./lib/resurrect');
var compare = require('fast-json-patch').compare;
var system = require('system');
var demo,
frames = 10,
testUrl = 'http://localhost:8000/demo/dev.html',
refsPath = 'test/browser/refs',
diffsPath = 'test/browser/diffs';
var update = arg('--update'),
updateAll = typeof arg('--updateAll') !== 'undefined',
diff = arg('--diff');
var resurrect = new Resurrect({ cleanup: true, revive: false }),
created = [],
changed = [];
var test = function(status) {
if (status === 'fail') {
console.log('failed to load', testUrl);
console.log('check dev server is running!');
console.log('use `grunt dev`');
phantom.exit(1);
return;
}
var demos = page.evaluate(function() {
var demoSelect = document.getElementById('demo-select'),
options = Array.prototype.slice.call(demoSelect);
return options.map(function(o) { return o.value; });
});
fs.removeTree(diffsPath);
if (diff) {
fs.makeDirectory(diffsPath);
}
for (var i = 0; i < demos.length; i += 1) {
demo = demos[i];
var hasChanged = false,
hasCreated = false,
forceUpdate = update === demo || updateAll,
worldStartPath = refsPath + '/' + demo + '/' + demo + '-0.json',
worldEndPath = refsPath + '/' + demo + '/' + demo + '-' + frames + '.json',
worldStartDiffPath = diffsPath + '/' + demo + '/' + demo + '-0.json',
worldEndDiffPath = diffsPath + '/' + demo + '/' + demo + '-' + frames + '.json';
var worldStart = page.evaluate(function(demo) {
var engine = Matter.Demo._engine;
if (!(demo in Matter.Demo)) {
throw '\'' + demo + '\' is not defined in Matter.Demo';
}
Matter.Demo[demo]();
return engine.world;
}, demo);
var worldEnd = page.evaluate(function(demo, frames) {
var engine = Matter.Demo._engine,
runner = Matter.Runner.create();
for (var j = 0; j <= frames; j += 1) {
Matter.Runner.tick(runner, engine, j * runner.delta);
}
return engine.world;
}, demo, frames);
worldEnd = resurrect.resurrect(resurrect.stringify(worldEnd, precisionLimiter));
worldStart = resurrect.resurrect(resurrect.stringify(worldStart, precisionLimiter));
if (fs.exists(worldStartPath)) {
var worldStartRef = resurrect.resurrect(fs.read(worldStartPath));
var worldStartDiff = compare(worldStartRef, worldStart);
if (worldStartDiff.length !== 0) {
if (diff) {
fs.write(worldStartDiffPath, JSON.stringify(worldStartDiff, precisionLimiter, 2), 'w');
}
if (forceUpdate) {
hasCreated = true;
fs.write(worldStartPath, resurrect.stringify(worldStart, precisionLimiter, 2), 'w');
} else {
hasChanged = true;
}
}
} else {
hasCreated = true;
fs.write(worldStartPath, resurrect.stringify(worldStart, precisionLimiter, 2), 'w');
}
if (fs.exists(worldEndPath)) {
var worldEndRef = resurrect.resurrect(fs.read(worldEndPath));
var worldEndDiff = compare(worldEndRef, worldEnd);
if (worldEndDiff.length !== 0) {
if (diff) {
fs.write(worldEndDiffPath, JSON.stringify(worldEndDiff, precisionLimiter, 2), 'w');
}
if (forceUpdate) {
hasCreated = true;
fs.write(worldEndPath, resurrect.stringify(worldEnd, precisionLimiter, 2), 'w');
} else {
hasChanged = true;
}
}
} else {
hasCreated = true;
fs.write(worldEndPath, resurrect.stringify(worldEnd, precisionLimiter, 2), 'w');
}
if (hasChanged) {
changed.push("'" + demo + "'");
system.stdout.write('x');
} else if (hasCreated) {
created.push("'" + demo + "'");
system.stdout.write('+');
} else {
system.stdout.write('.');
}
}
if (created.length > 0) {
console.log('\nupdated', created.join(', '));
}
var isOk = changed.length === 0 ? 1 : 0;
console.log('');
if (isOk) {
console.log('ok');
} else {
console.log('\nchanges detected on:');
console.log(changed.join(', '));
console.log('\nreview, then --update [name] or --updateAll');
console.log('use --diff for diff log');
}
phantom.exit(!isOk);
};
var precisionLimiter = function(key, value) {
if (typeof value === 'number') {
return parseFloat(value.toFixed(5));
}
return value;
};
function arg(name) {
var index = system.args.indexOf(name);
if (index >= 0) {
return system.args[index + 1] || true;
}
return undefined;
}
page.onError = function(msg, trace) {
setTimeout(function() {
var msgStack = ['testing \'' + demo + '\'', msg];
if (trace && trace.length) {
trace.forEach(function(t) {
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (fn: ' + t.function +')' : ''));
});
}
console.log(msgStack.join('\n'));
phantom.exit(1);
}, 0);
};
page.onResourceReceived = function(res) {
setTimeout(function() {
if (res.stage === 'end'
&& (res.status !== 304 && res.status !== 200 && res.status !== null)) {
console.log('error', res.status, res.url);
phantom.exit(1);
}
}, 0);
};
phantom.onError = page.onError;
page.open(testUrl, test);

View file

@ -0,0 +1,542 @@
/**
* # ResurrectJS
* @version 1.0.3
* @license Public Domain
*
* ResurrectJS preserves object behavior (prototypes) and reference
* circularity with a special JSON encoding. Unlike regular JSON,
* Date, RegExp, DOM objects, and `undefined` are also properly
* preserved.
*
* ## Examples
*
* function Foo() {}
* Foo.prototype.greet = function() { return "hello"; };
*
* // Behavior is preserved:
* var necromancer = new Resurrect();
* var json = necromancer.stringify(new Foo());
* var foo = necromancer.resurrect(json);
* foo.greet(); // => "hello"
*
* // References to the same object are preserved:
* json = necromancer.stringify([foo, foo]);
* var array = necromancer.resurrect(json);
* array[0] === array[1]; // => true
* array[1].greet(); // => "hello"
*
* // Dates are restored properly
* json = necromancer.stringify(new Date());
* var date = necromancer.resurrect(json);
* Object.prototype.toString.call(date); // => "[object Date]"
*
* ## Options
*
* Options are provided to the constructor as an object with these
* properties:
*
* prefix ('#'): A prefix string used for temporary properties added
* to objects during serialization and deserialization. It is
* important that you don't use any properties beginning with this
* string. This option must be consistent between both
* serialization and deserialization.
*
* cleanup (false): Perform full property cleanup after both
* serialization and deserialization using the `delete`
* operator. This may cause performance penalties (breaking hidden
* classes in V8) on objects that ResurrectJS touches, so enable
* with care.
*
* revive (true): Restore behavior (__proto__) to objects that have
* been resurrected. If this is set to false during serialization,
* resurrection information will not be encoded. You still get
* circularity and Date support.
*
* resolver (Resurrect.NamespaceResolver(window)): Converts between
* a name and a prototype. Create a custom resolver if your
* constructors are not stored in global variables. The resolver
* has two methods: getName(object) and getPrototype(string).
*
* For example,
*
* var necromancer = new Resurrect({
* prefix: '__#',
* cleanup: true
* });
*
* ## Caveats
*
* * With the default resolver, all constructors must be named and
* stored in the global variable under that name. This is required
* so that the prototypes can be looked up and reconnected at
* resurrection time.
*
* * The wrapper objects Boolean, String, and Number will be
* unwrapped. This means extra properties added to these objects
* will not be preserved.
*
* * Functions cannot ever be serialized. Resurrect will throw an
* error if a function is found when traversing a data structure.
*
* @see http://nullprogram.com/blog/2013/03/28/
*/
/**
* @param {Object} [options] See options documentation.
* @namespace
* @constructor
*/
function Resurrect(options) {
this.table = null;
this.prefix = '#';
this.cleanup = false;
this.revive = true;
for (var option in options) {
if (options.hasOwnProperty(option)) {
this[option] = options[option];
}
}
this.refcode = this.prefix + 'id';
this.origcode = this.prefix + 'original';
this.buildcode = this.prefix + '.';
this.valuecode = this.prefix + 'v';
}
if (module)
module.exports = Resurrect;
/**
* Portable access to the global object (window, global).
* Uses indirect eval.
* @constant
*/
Resurrect.GLOBAL = (0, eval)('this');
/**
* Escape special regular expression characters in a string.
* @param {string} string
* @returns {string} The string escaped for exact matches.
* @see http://stackoverflow.com/a/6969486
*/
Resurrect.escapeRegExp = function (string) {
return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
};
/* Helper Objects */
/**
* @param {string} [message]
* @constructor
*/
Resurrect.prototype.Error = function ResurrectError(message) {
this.message = message || '';
this.stack = new Error().stack;
};
Resurrect.prototype.Error.prototype = Object.create(Error.prototype);
Resurrect.prototype.Error.prototype.name = 'ResurrectError';
/**
* Resolves prototypes through the properties on an object and
* constructor names.
* @param {Object} scope
* @constructor
*/
Resurrect.NamespaceResolver = function(scope) {
this.scope = scope;
};
/**
* Gets the prototype of the given property name from an object. If
* not found, it throws an error.
* @param {string} name
* @returns {Object}
* @method
*/
Resurrect.NamespaceResolver.prototype.getPrototype = function(name) {
var constructor = this.scope[name];
if (constructor) {
return constructor.prototype;
} else {
throw new Resurrect.prototype.Error('Unknown constructor: ' + name);
}
};
/**
* Get the prototype name for an object, to be fetched later with getPrototype.
* @param {Object} object
* @returns {?string} Null if the constructor is Object.
* @method
*/
Resurrect.NamespaceResolver.prototype.getName = function(object) {
var constructor = object.constructor.name;
if (constructor == null) { // IE
var funcPattern = /^\s*function\s*([A-Za-z0-9_$]*)/;
constructor = funcPattern.exec(object.constructor)[1];
}
if (constructor === '') {
var msg = "Can't serialize objects with anonymous constructors.";
throw new Resurrect.prototype.Error(msg);
} else if (constructor === 'Object' || constructor === 'Array') {
return null;
} else {
return constructor;
}
};
/* Set the default resolver searches the global object. */
Resurrect.prototype.resolver =
new Resurrect.NamespaceResolver(Resurrect.GLOBAL);
/**
* Create a DOM node from HTML source; behaves like a constructor.
* @param {string} html
* @constructor
*/
Resurrect.Node = function(html) {
var div = document.createElement('div');
div.innerHTML = html;
return div.firstChild;
};
/* Type Tests */
/**
* @param {string} type
* @returns {Function} A function that tests for type.
*/
Resurrect.is = function(type) {
var string = '[object ' + type + ']';
return function(object) {
return Object.prototype.toString.call(object) === string;
};
};
Resurrect.isArray = Resurrect.is('Array');
Resurrect.isString = Resurrect.is('String');
Resurrect.isBoolean = Resurrect.is('Boolean');
Resurrect.isNumber = Resurrect.is('Number');
Resurrect.isFunction = Resurrect.is('Function');
Resurrect.isDate = Resurrect.is('Date');
Resurrect.isRegExp = Resurrect.is('RegExp');
Resurrect.isObject = Resurrect.is('Object');
Resurrect.isAtom = function(object) {
return !Resurrect.isObject(object) && !Resurrect.isArray(object);
};
/**
* @param {*} object
* @returns {boolean} True if object is a primitive or a primitive wrapper.
*/
Resurrect.isPrimitive = function(object) {
return object == null ||
Resurrect.isNumber(object) ||
Resurrect.isString(object) ||
Resurrect.isBoolean(object);
};
/* Methods */
/**
* Create a reference (encoding) to an object.
* @param {(Object|undefined)} object
* @returns {Object}
* @method
*/
Resurrect.prototype.ref = function(object) {
var ref = {};
if (object === undefined) {
ref[this.prefix] = -1;
} else {
ref[this.prefix] = object[this.refcode];
}
return ref;
};
/**
* Lookup an object in the table by reference object.
* @param {Object} ref
* @returns {(Object|undefined)}
* @method
*/
Resurrect.prototype.deref = function(ref) {
return this.table[ref[this.prefix]];
};
/**
* Put a temporary identifier on an object and store it in the table.
* @param {Object} object
* @returns {number} The unique identifier number.
* @method
*/
Resurrect.prototype.tag = function(object) {
if (this.revive) {
var constructor = this.resolver.getName(object);
if (constructor) {
var proto = Object.getPrototypeOf(object);
if (this.resolver.getPrototype(constructor) !== proto) {
throw new this.Error('Constructor mismatch!');
} else {
object[this.prefix] = constructor;
}
}
}
object[this.refcode] = this.table.length;
this.table.push(object);
return object[this.refcode];
};
/**
* Create a builder object (encoding) for serialization.
* @param {string} name The name of the constructor.
* @param value The value to pass to the constructor.
* @returns {Object}
* @method
*/
Resurrect.prototype.builder = function(name, value) {
var builder = {};
builder[this.buildcode] = name;
builder[this.valuecode] = value;
return builder;
};
/**
* Build a value from a deserialized builder.
* @param {Object} ref
* @returns {Object}
* @method
* @see http://stackoverflow.com/a/14378462
* @see http://nullprogram.com/blog/2013/03/24/
*/
Resurrect.prototype.build = function(ref) {
var type = ref[this.buildcode].split(/\./).reduce(function(object, name) {
return object[name];
}, Resurrect.GLOBAL);
/* Brilliant hack by kybernetikos: */
var args = [null].concat(ref[this.valuecode]);
var factory = type.bind.apply(type, args);
var result = new factory();
if (Resurrect.isPrimitive(result)) {
return result.valueOf(); // unwrap
} else {
return result;
}
};
/**
* Dereference or build an object or value from an encoding.
* @param {Object} ref
* @returns {(Object|undefined)}
* @method
*/
Resurrect.prototype.decode = function(ref) {
if (this.prefix in ref) {
return this.deref(ref);
} else if (this.buildcode in ref) {
return this.build(ref);
} else {
throw new this.Error('Unknown encoding.');
}
};
/**
* @param {Object} object
* @returns {boolean} True if the provided object is tagged for serialization.
* @method
*/
Resurrect.prototype.isTagged = function(object) {
return (this.refcode in object) && (object[this.refcode] != null);
};
/**
* Visit root and all its ancestors, visiting atoms with f.
* @param {*} root
* @param {Function} f
* @param {Function} replacer
* @returns {*} A fresh copy of root to be serialized.
* @method
*/
Resurrect.prototype.visit = function(root, f, replacer) {
if (Resurrect.isAtom(root)) {
return f(root);
} else if (!this.isTagged(root)) {
var copy = null;
if (Resurrect.isArray(root)) {
copy = [];
root[this.refcode] = this.tag(copy);
for (var i = 0; i < root.length; i++) {
copy.push(this.visit(root[i], f, replacer));
}
} else { /* Object */
copy = Object.create(Object.getPrototypeOf(root));
root[this.refcode] = this.tag(copy);
for (var key in root) {
var value = root[key];
if (root.hasOwnProperty(key)) {
if (replacer && value !== undefined) {
// Call replacer like JSON.stringify's replacer
value = replacer.call(root, key, root[key]);
if (value === undefined) {
continue; // Omit from result
}
}
copy[key] = this.visit(value, f, replacer);
}
}
}
copy[this.origcode] = root;
return this.ref(copy);
} else {
return this.ref(root);
}
};
/**
* Manage special atom values, possibly returning an encoding.
* @param {*} atom
* @returns {*}
* @method
*/
Resurrect.prototype.handleAtom = function(atom) {
var Node = Resurrect.GLOBAL.Node || function() {};
if (Resurrect.isFunction(atom)) {
throw new this.Error("Can't serialize functions.");
} else if (atom instanceof Node) {
var xmls = new XMLSerializer();
return this.builder('Resurrect.Node', [xmls.serializeToString(atom)]);
} else if (Resurrect.isDate(atom)) {
return this.builder('Date', [atom.toISOString()]);
} else if (Resurrect.isRegExp(atom)) {
var args = atom.toString().match(/\/(.+)\/([gimy]*)/).slice(1);
return this.builder('RegExp', args);
} else if (atom === undefined) {
return this.ref(undefined);
} else if (Resurrect.isNumber(atom) && (isNaN(atom) || !isFinite(atom))) {
return this.builder('Number', [atom.toString()]);
} else {
return atom;
}
};
/**
* Hides intrusive keys from a user-supplied replacer.
* @param {Function} replacer function of two arguments (key, value)
* @returns {Function} A function that skips the replacer for intrusive keys.
* @method
*/
Resurrect.prototype.replacerWrapper = function(replacer) {
var skip = new RegExp('^' + Resurrect.escapeRegExp(this.prefix));
return function(k, v) {
if (skip.test(k)) {
return v;
} else {
return replacer(k, v);
}
};
};
/**
* Serialize an arbitrary JavaScript object, carefully preserving it.
* @param {*} object
* @param {(Function|Array)} replacer
* @param {string} space
* @method
*/
Resurrect.prototype.stringify = function(object, replacer, space) {
if (Resurrect.isFunction(replacer)) {
replacer = this.replacerWrapper(replacer);
} else if (Resurrect.isArray(replacer)) {
var acceptKeys = replacer;
replacer = function(k, v) {
return acceptKeys.indexOf(k) >= 0 ? v : undefined;
};
}
if (Resurrect.isAtom(object)) {
return JSON.stringify(this.handleAtom(object), replacer, space);
} else {
this.table = [];
this.visit(object, this.handleAtom.bind(this), replacer);
for (var i = 0; i < this.table.length; i++) {
if (this.cleanup) {
delete this.table[i][this.origcode][this.refcode];
} else {
this.table[i][this.origcode][this.refcode] = null;
}
delete this.table[i][this.refcode];
delete this.table[i][this.origcode];
}
var table = this.table;
this.table = null;
return JSON.stringify(table, null, space);
}
};
/**
* Restore the __proto__ of the given object to the proper value.
* @param {Object} object
* @returns {Object} Its argument, or a copy, with the prototype restored.
* @method
*/
Resurrect.prototype.fixPrototype = function(object) {
if (this.prefix in object) {
var name = object[this.prefix];
var prototype = this.resolver.getPrototype(name);
if ('__proto__' in object) {
object.__proto__ = prototype;
if (this.cleanup) {
delete object[this.prefix];
}
return object;
} else { // IE
var copy = Object.create(prototype);
for (var key in object) {
if (object.hasOwnProperty(key) && key !== this.prefix) {
copy[key] = object[key];
}
}
return copy;
}
} else {
return object;
}
};
/**
* Deserialize an encoded object, restoring circularity and behavior.
* @param {string} string
* @returns {*} The decoded object or value.
* @method
*/
Resurrect.prototype.resurrect = function(string) {
var result = null;
var data = JSON.parse(string);
if (Resurrect.isArray(data)) {
this.table = data;
/* Restore __proto__. */
if (this.revive) {
for (var i = 0; i < this.table.length; i++) {
this.table[i] = this.fixPrototype(this.table[i]);
}
}
/* Re-establish object references and construct atoms. */
for (i = 0; i < this.table.length; i++) {
var object = this.table[i];
for (var key in object) {
if (object.hasOwnProperty(key)) {
if (!(Resurrect.isAtom(object[key]))) {
object[key] = this.decode(object[key]);
}
}
}
}
result = this.table[0];
} else if (Resurrect.isObject(data)) {
this.table = [];
result = this.decode(data);
} else {
result = data;
}
this.table = null;
return result;
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,889 @@
[
{
"bodies": {
"#": 1
},
"bounds": {
"#": 86
},
"composites": {
"#": 89
},
"constraints": {
"#": 90
},
"gravity": {
"#": 94
},
"id": 0,
"isModified": true,
"label": "World",
"parent": "",
"type": "composite"
},
[
{
"#": 2
},
{
"#": 23
},
{
"#": 44
},
{
"#": 65
}
],
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 40930.25,
"axes": {
"#": 3
},
"bounds": {
"#": 6
},
"collisionFilter": {
"#": 9
},
"constraintImpulse": {
"#": 10
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 11
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 0,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 12
},
"positionImpulse": {
"#": 13
},
"positionPrev": {
"#": 14
},
"render": {
"#": 15
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 17
},
"vertices": {
"#": 18
}
},
[
{
"#": 4
},
{
"#": 5
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 7
},
"min": {
"#": 8
}
},
{
"x": 805.25,
"y": 20.25
},
{
"x": -5.25,
"y": -30.25
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": -5
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": -5
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 16
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 19
},
{
"#": 20
},
{
"#": 21
},
{
"#": 22
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": -5.25,
"y": -30.25
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 805.25,
"y": -30.25
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 805.25,
"y": 20.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": -5.25,
"y": 20.25
},
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 40930.25,
"axes": {
"#": 24
},
"bounds": {
"#": 27
},
"collisionFilter": {
"#": 30
},
"constraintImpulse": {
"#": 31
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 32
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 1,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 33
},
"positionImpulse": {
"#": 34
},
"positionPrev": {
"#": 35
},
"render": {
"#": 36
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 38
},
"vertices": {
"#": 39
}
},
[
{
"#": 25
},
{
"#": 26
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 28
},
"min": {
"#": 29
}
},
{
"x": 805.25,
"y": 630.25
},
{
"x": -5.25,
"y": 579.75
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": 605
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": 605
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 37
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 40
},
{
"#": 41
},
{
"#": 42
},
{
"#": 43
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": -5.25,
"y": 579.75
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 805.25,
"y": 579.75
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 805.25,
"y": 630.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": -5.25,
"y": 630.25
},
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 30830.25,
"axes": {
"#": 45
},
"bounds": {
"#": 48
},
"collisionFilter": {
"#": 51
},
"constraintImpulse": {
"#": 52
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 53
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 2,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 54
},
"positionImpulse": {
"#": 55
},
"positionPrev": {
"#": 56
},
"render": {
"#": 57
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 59
},
"vertices": {
"#": 60
}
},
[
{
"#": 46
},
{
"#": 47
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 49
},
"min": {
"#": 50
}
},
{
"x": 830.25,
"y": 605.25
},
{
"x": 779.75,
"y": -5.25
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 805,
"y": 300
},
{
"x": 0,
"y": 0
},
{
"x": 805,
"y": 300
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 58
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 61
},
{
"#": 62
},
{
"#": 63
},
{
"#": 64
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": 779.75,
"y": -5.25
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 830.25,
"y": -5.25
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 830.25,
"y": 605.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": 779.75,
"y": 605.25
},
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 30830.25,
"axes": {
"#": 66
},
"bounds": {
"#": 69
},
"collisionFilter": {
"#": 72
},
"constraintImpulse": {
"#": 73
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 74
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 3,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 75
},
"positionImpulse": {
"#": 76
},
"positionPrev": {
"#": 77
},
"render": {
"#": 78
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 80
},
"vertices": {
"#": 81
}
},
[
{
"#": 67
},
{
"#": 68
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 70
},
"min": {
"#": 71
}
},
{
"x": 20.25,
"y": 605.25
},
{
"x": -30.25,
"y": -5.25
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": -5,
"y": 300
},
{
"x": 0,
"y": 0
},
{
"x": -5,
"y": 300
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 79
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 82
},
{
"#": 83
},
{
"#": 84
},
{
"#": 85
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": -30.25,
"y": -5.25
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 20.25,
"y": -5.25
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 20.25,
"y": 605.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": -30.25,
"y": 605.25
},
{
"max": {
"#": 87
},
"min": {
"#": 88
}
},
{
"x": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"Infinity"
]
}
},
{
"x": {
"#.": "Number",
"#v": [
"-Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"-Infinity"
]
}
},
[],
[
{
"#": 91
}
],
{
"angularStiffness": 1,
"bodyB": "",
"id": 1,
"label": "Mouse Constraint",
"length": 0.01,
"pointA": {
"#": 92
},
"pointB": "",
"render": {
"#": 93
},
"stiffness": 0.1,
"type": "constraint"
},
{
"x": 0,
"y": 0
},
{
"lineWidth": 3,
"strokeStyle": "#90EE90",
"visible": true
},
{
"x": 0,
"y": 1
}
]

View file

@ -0,0 +1,929 @@
[
{
"bodies": {
"#": 1
},
"bounds": {
"#": 90
},
"composites": {
"#": 93
},
"constraints": {
"#": 94
},
"gravity": {
"#": 98
},
"id": 0,
"isModified": false,
"label": "World",
"parent": "",
"type": "composite"
},
[
{
"#": 2
},
{
"#": 24
},
{
"#": 46
},
{
"#": 68
}
],
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 40930.25,
"axes": {
"#": 3
},
"bounds": {
"#": 6
},
"collisionFilter": {
"#": 9
},
"constraintImpulse": {
"#": 10
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 11
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 0,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 12
},
"positionImpulse": {
"#": 13
},
"positionPrev": {
"#": 14
},
"region": {
"#": 15
},
"render": {
"#": 16
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 18
},
"vertices": {
"#": 19
}
},
[
{
"#": 4
},
{
"#": 5
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 7
},
"min": {
"#": 8
}
},
{
"x": 805.25,
"y": 20.25
},
{
"x": -5.25,
"y": -30.25
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": -5
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": -5
},
{
"endCol": 16,
"endRow": 0,
"id": "-1,16,-1,0",
"startCol": -1,
"startRow": -1
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 17
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 20
},
{
"#": 21
},
{
"#": 22
},
{
"#": 23
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": -5.25,
"y": -30.25
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 805.25,
"y": -30.25
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 805.25,
"y": 20.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": -5.25,
"y": 20.25
},
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 40930.25,
"axes": {
"#": 25
},
"bounds": {
"#": 28
},
"collisionFilter": {
"#": 31
},
"constraintImpulse": {
"#": 32
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 33
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 1,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 34
},
"positionImpulse": {
"#": 35
},
"positionPrev": {
"#": 36
},
"region": {
"#": 37
},
"render": {
"#": 38
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 40
},
"vertices": {
"#": 41
}
},
[
{
"#": 26
},
{
"#": 27
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 29
},
"min": {
"#": 30
}
},
{
"x": 805.25,
"y": 630.25
},
{
"x": -5.25,
"y": 579.75
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": 605
},
{
"x": 0,
"y": 0
},
{
"x": 400,
"y": 605
},
{
"endCol": 16,
"endRow": 13,
"id": "-1,16,12,13",
"startCol": -1,
"startRow": 12
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 39
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 42
},
{
"#": 43
},
{
"#": 44
},
{
"#": 45
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": -5.25,
"y": 579.75
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 805.25,
"y": 579.75
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 805.25,
"y": 630.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": -5.25,
"y": 630.25
},
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 30830.25,
"axes": {
"#": 47
},
"bounds": {
"#": 50
},
"collisionFilter": {
"#": 53
},
"constraintImpulse": {
"#": 54
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 55
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 2,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 56
},
"positionImpulse": {
"#": 57
},
"positionPrev": {
"#": 58
},
"region": {
"#": 59
},
"render": {
"#": 60
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 62
},
"vertices": {
"#": 63
}
},
[
{
"#": 48
},
{
"#": 49
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 51
},
"min": {
"#": 52
}
},
{
"x": 830.25,
"y": 605.25
},
{
"x": 779.75,
"y": -5.25
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": 805,
"y": 300
},
{
"x": 0,
"y": 0
},
{
"x": 805,
"y": 300
},
{
"endCol": 17,
"endRow": 12,
"id": "16,17,-1,12",
"startCol": 16,
"startRow": -1
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 61
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 64
},
{
"#": 65
},
{
"#": 66
},
{
"#": 67
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": 779.75,
"y": -5.25
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 830.25,
"y": -5.25
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 830.25,
"y": 605.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": 779.75,
"y": 605.25
},
{
"angle": 0,
"anglePrev": 0,
"angularSpeed": 0,
"angularVelocity": 0,
"area": 30830.25,
"axes": {
"#": 69
},
"bounds": {
"#": 72
},
"collisionFilter": {
"#": 75
},
"constraintImpulse": {
"#": 76
},
"density": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"force": {
"#": 77
},
"friction": 1,
"frictionAir": 0.01,
"frictionStatic": 0.5,
"id": 3,
"inertia": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"inverseInertia": 0,
"inverseMass": 0,
"isSleeping": false,
"isStatic": true,
"label": "Rectangle Body",
"mass": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"motion": 0,
"parent": null,
"position": {
"#": 78
},
"positionImpulse": {
"#": 79
},
"positionPrev": {
"#": 80
},
"region": {
"#": 81
},
"render": {
"#": 82
},
"restitution": 0,
"sleepCounter": 0,
"sleepThreshold": 60,
"slop": 0.05,
"speed": 0,
"timeScale": 1,
"torque": 0,
"totalContacts": 0,
"type": "body",
"velocity": {
"#": 84
},
"vertices": {
"#": 85
}
},
[
{
"#": 70
},
{
"#": 71
}
],
{
"x": 0,
"y": 1
},
{
"x": -1,
"y": 0
},
{
"max": {
"#": 73
},
"min": {
"#": 74
}
},
{
"x": 20.25,
"y": 605.25
},
{
"x": -30.25,
"y": -5.25
},
{
"category": 1,
"group": 0,
"mask": 4294967295
},
{
"angle": 0,
"x": 0,
"y": 0
},
{
"x": 0,
"y": 0
},
{
"x": -5,
"y": 300
},
{
"x": 0,
"y": 0
},
{
"x": -5,
"y": 300
},
{
"endCol": 0,
"endRow": 12,
"id": "-1,0,-1,12",
"startCol": -1,
"startRow": -1
},
{
"fillStyle": "#eeeeee",
"lineWidth": 1.5,
"sprite": {
"#": 83
},
"strokeStyle": "#bbbbbb",
"visible": true
},
{
"xScale": 1,
"yScale": 1
},
{
"x": 0,
"y": 0
},
[
{
"#": 86
},
{
"#": 87
},
{
"#": 88
},
{
"#": 89
}
],
{
"body": null,
"index": 0,
"isInternal": false,
"x": -30.25,
"y": -5.25
},
{
"body": null,
"index": 1,
"isInternal": false,
"x": 20.25,
"y": -5.25
},
{
"body": null,
"index": 2,
"isInternal": false,
"x": 20.25,
"y": 605.25
},
{
"body": null,
"index": 3,
"isInternal": false,
"x": -30.25,
"y": 605.25
},
{
"max": {
"#": 91
},
"min": {
"#": 92
}
},
{
"x": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"Infinity"
]
}
},
{
"x": {
"#.": "Number",
"#v": [
"-Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"-Infinity"
]
}
},
[],
[
{
"#": 95
}
],
{
"angularStiffness": 1,
"bodyB": "",
"id": 1,
"label": "Mouse Constraint",
"length": 0.01,
"pointA": {
"#": 96
},
"pointB": "",
"render": {
"#": 97
},
"stiffness": 0.1,
"type": "constraint"
},
{
"x": 0,
"y": 0
},
{
"lineWidth": 3,
"strokeStyle": "#90EE90",
"visible": true
},
{
"x": 0,
"y": 1
}
]

View file

@ -0,0 +1,96 @@
[
{
"bodies": {
"#": 1
},
"bounds": {
"#": 2
},
"composites": {
"#": 5
},
"constraints": {
"#": 6
},
"gravity": {
"#": 10
},
"id": 0,
"isModified": true,
"label": "World",
"parent": "",
"type": "composite"
},
[],
{
"max": {
"#": 3
},
"min": {
"#": 4
}
},
{
"x": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"Infinity"
]
}
},
{
"x": {
"#.": "Number",
"#v": [
"-Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"-Infinity"
]
}
},
[],
[
{
"#": 7
}
],
{
"angularStiffness": 1,
"bodyB": "",
"id": 1,
"label": "Mouse Constraint",
"length": 0.01,
"pointA": {
"#": 8
},
"pointB": "",
"render": {
"#": 9
},
"stiffness": 0.1,
"type": "constraint"
},
{
"x": 0,
"y": 0
},
{
"lineWidth": 3,
"strokeStyle": "#90EE90",
"visible": true
},
{
"x": 0,
"y": 1
}
]

View file

@ -0,0 +1,96 @@
[
{
"bodies": {
"#": 1
},
"bounds": {
"#": 2
},
"composites": {
"#": 5
},
"constraints": {
"#": 6
},
"gravity": {
"#": 10
},
"id": 0,
"isModified": false,
"label": "World",
"parent": "",
"type": "composite"
},
[],
{
"max": {
"#": 3
},
"min": {
"#": 4
}
},
{
"x": {
"#.": "Number",
"#v": [
"Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"Infinity"
]
}
},
{
"x": {
"#.": "Number",
"#v": [
"-Infinity"
]
},
"y": {
"#.": "Number",
"#v": [
"-Infinity"
]
}
},
[],
[
{
"#": 7
}
],
{
"angularStiffness": 1,
"bodyB": "",
"id": 1,
"label": "Mouse Constraint",
"length": 0.01,
"pointA": {
"#": 8
},
"pointB": "",
"render": {
"#": 9
},
"stiffness": 0.1,
"type": "constraint"
},
{
"x": 0,
"y": 0
},
{
"lineWidth": 3,
"strokeStyle": "#90EE90",
"visible": true
},
{
"x": 0,
"y": 1
}
]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff