mirror of
https://github.com/liabru/matter-js.git
synced 2025-01-13 16:18:50 -05:00
Merge branch 'master' into timing-improve
* master: (32 commits) fix lint update dependencies Revert "Merge branch 'pr/526'" Revert "Merge branch 'pr/527'" changed alpha build configuration add window global, stub require and handle bad values in test tools added overlap metric to test tools fix path to build in test worker implemented threaded comparison testing fixed plugins in compare Added build comparison tools and tests Added config and test files to lint Set loose build version on dev server Added watch content base to dev server added timing to engine snapshot updated readme added tag push to release task updated readme removed yuidocjs dev dependency removed unused gulp release tasks ... # Conflicts: # src/collision/Resolver.js # src/core/Engine.js
This commit is contained in:
commit
8cfc234b5b
80 changed files with 15644 additions and 1799 deletions
|
@ -65,7 +65,12 @@
|
|||
"Runner": false,
|
||||
"Svg": false,
|
||||
"Metrics": false,
|
||||
"Example": false
|
||||
"Example": false,
|
||||
"__MATTER_VERSION__": false,
|
||||
"jest": false,
|
||||
"test": false,
|
||||
"expect": false,
|
||||
"describe": false
|
||||
},
|
||||
"extends": "eslint:recommended"
|
||||
}
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -7,8 +7,11 @@ build/matter-dev.js
|
|||
build/matter-dev.min.js
|
||||
demo/js/lib/matter-dev.js
|
||||
demo/js/Examples.js
|
||||
demo/js/Examples.min.js
|
||||
examples/build
|
||||
test/browser/diffs
|
||||
test/browser/refs
|
||||
test/node/diffs
|
||||
test/node/refs
|
||||
test/node/refs
|
||||
__snapshots__
|
||||
__compare__
|
13
.travis.yml
13
.travis.yml
|
@ -2,10 +2,9 @@ language: node_js
|
|||
sudo: false
|
||||
node_js:
|
||||
- "node"
|
||||
before_install:
|
||||
- npm install -g gulp
|
||||
- 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
|
||||
install:
|
||||
- npm install
|
||||
script:
|
||||
- npm run lint
|
||||
- npm run test
|
||||
- npm run build
|
|
@ -2,22 +2,39 @@
|
|||
|
||||
## License Agreement
|
||||
|
||||
When providing any contributions, you must agree and be legally entitled to provide them for use and distribution in the project under the same terms as the [license](https://github.com/liabru/matter-js/blob/master/LICENSE), otherwise they can not be accepted.
|
||||
|
||||
## Building
|
||||
|
||||
To build you must first install [node.js](http://nodejs.org/) and [gulp](http://gulpjs.com/), then run
|
||||
|
||||
npm install
|
||||
|
||||
This will install the required build dependencies, then run
|
||||
|
||||
gulp dev
|
||||
|
||||
which is a task that builds the `matter-dev.js` file, spawns a development server and opens `http://localhost:8000/demo/index.html` in your browser. Any changes you make to the source will automatically rebuild `matter-dev.js` and reload your browser.
|
||||
By providing any kind of contribution to this project, **you must agree and be legally entitled** to provide them for use and distribution as a part of this project **wholly under the same terms as in the original included [license](https://github.com/liabru/matter-js/blob/master/LICENSE)**.
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions by pull request are welcome! Please ensure they follow the same style and architecture as the rest of the code. You should run `gulp test` and ensure there are no reported errors. Please do not include any changes to the files in the `build` directory. All contributors must agree to the license agreement described at the beginning of this document.
|
||||
Contributions by pull request or issues are welcome. Please ensure they follow the same style and architecture as the rest of the code. Use `npm run lint` before submitting. Please **do not include** any changes to the files in the `build` directory.
|
||||
|
||||
If you'd like to contribute but not sure what to work on, feel free to get in touch.
|
||||
Before contributing please read the license agreement described at the beginning of this document.
|
||||
|
||||
## Building
|
||||
|
||||
To build you must first install [node.js](http://nodejs.org), then run
|
||||
|
||||
npm install
|
||||
|
||||
which will install the required build dependencies, then run
|
||||
|
||||
npm run dev
|
||||
|
||||
which will run the development server and opens `http://localhost:8000/` in your browser. Any changes you make to the source will automatically rebuild and reload the page.
|
||||
|
||||
## Commands
|
||||
|
||||
The following development commands can be run at the terminal
|
||||
|
||||
- **npm run dev**
|
||||
runs development server
|
||||
- **npm run build**
|
||||
creates a release build
|
||||
- **npm run lint**
|
||||
runs the linter
|
||||
- **npm run test**
|
||||
runs the tests
|
||||
- **npm run compare**
|
||||
compares the output of examples for current source against release build
|
||||
- **npm run doc**
|
||||
builds the documentation
|
||||
|
|
355
Gulpfile.js
355
Gulpfile.js
|
@ -1,55 +1,83 @@
|
|||
var gulp = require('gulp');
|
||||
var uglify = require('gulp-uglify');
|
||||
var rename = require('gulp-rename');
|
||||
var header = require('gulp-header');
|
||||
var eslint = require('gulp-eslint');
|
||||
var bump = require('gulp-bump');
|
||||
var changelog = require('gulp-conventional-changelog');
|
||||
var tag = require('gulp-tag-version');
|
||||
var release = require('gulp-github-release');
|
||||
var sequence = require('run-sequence');
|
||||
var gutil = require('gulp-util');
|
||||
var replace = require('gulp-replace');
|
||||
var webserver = require('gulp-webserver');
|
||||
var concat = require('gulp-concat');
|
||||
var preprocess = require('gulp-preprocess');
|
||||
var browserify = require('browserify');
|
||||
var derequire = require('gulp-derequire');
|
||||
var transform = require('vinyl-transform');
|
||||
var through2 = require('through2');
|
||||
var pkg = require('./package.json');
|
||||
var clone = require('gulp-clone');
|
||||
var livereload = require('connect-livereload');
|
||||
var es = require('event-stream');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var watchify = require('watchify');
|
||||
var extend = require('util')._extend;
|
||||
var exec = require('child_process').exec;
|
||||
var buildDirectory = 'build';
|
||||
var server;
|
||||
/* eslint-env es6 */
|
||||
"use strict";
|
||||
|
||||
gulp.task('default', ['build:dev']);
|
||||
const gulp = require('gulp');
|
||||
const bump = require('gulp-bump');
|
||||
const changelog = require('gulp-conventional-changelog');
|
||||
const tag = require('gulp-tag-version');
|
||||
const sequence = require('run-sequence');
|
||||
const gutil = require('gulp-util');
|
||||
const pkg = require('./package.json');
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
gulp.task('dev', function(callback) {
|
||||
sequence('watch', 'serve', callback);
|
||||
const shellExec = (command, callback) => {
|
||||
const args = process.argv.slice(3).join(' '),
|
||||
proc = exec(command + ' ' + args, (err, stdout, stderr) => {
|
||||
callback(err, stdout, stderr, proc);
|
||||
});
|
||||
|
||||
proc.stdout.on('data', data => process.stdout.write(data));
|
||||
proc.stderr.on('data', data => process.stderr.write(data));
|
||||
};
|
||||
|
||||
const shell = command => (callback => { shellExec(command, callback); });
|
||||
|
||||
const hint = command => (callback => {
|
||||
gutil.log(gutil.colors.red('Error'), 'use', gutil.colors.yellow(command), 'instead.');
|
||||
callback();
|
||||
});
|
||||
|
||||
gulp.task('release', function(callback) {
|
||||
shell('git status --porcelain', function(err, stdout) {
|
||||
gulp.task('default', hint('npm run build'));
|
||||
gulp.task('dev', hint('npm run dev'));
|
||||
gulp.task('build', hint('npm run build'));
|
||||
gulp.task('test', hint('npm run test'));
|
||||
gulp.task('lint', hint('npm run lint'));
|
||||
|
||||
gulp.task('doc', callback => {
|
||||
shellExec(`yuidoc --config yuidoc.json --project-version ${pkg.version}`, callback);
|
||||
});
|
||||
|
||||
gulp.task('bump', () => {
|
||||
return gulp.src(['package.json', 'bower.json'])
|
||||
.pipe(bump({ type: process.argv[4] || 'minor' }))
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
gulp.task('tag', () => {
|
||||
return gulp.src('package.json')
|
||||
.pipe(tag({ prefix: '' }));
|
||||
});
|
||||
|
||||
gulp.task('changelog', () => {
|
||||
return gulp.src('CHANGELOG.md')
|
||||
.pipe(changelog())
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
gulp.task('release', callback => {
|
||||
shellExec('git status --porcelain', (err, stdout) => {
|
||||
if (stdout && stdout.trim()) {
|
||||
throw new gutil.PluginError({
|
||||
plugin: 'release',
|
||||
message: 'cannot build release as there are uncomitted changes'
|
||||
});
|
||||
} else {
|
||||
sequence('test', 'bump', 'reload', 'build:release', 'doc', 'changelog', callback);
|
||||
sequence(
|
||||
'release:lint', 'bump', 'release:build', 'release:test',
|
||||
'doc', 'changelog', callback
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('release:push', function(callback) {
|
||||
shell('git status --porcelain', function(err, stdout) {
|
||||
gulp.task('release:lint', shell('npm run lint'));
|
||||
gulp.task('release:build', shell('npm run build'));
|
||||
gulp.task('release:test', shell('TEST_BUILD=true npm run test'));
|
||||
gulp.task('release:push:git', shell('git push origin && git push origin --tags'));
|
||||
gulp.task('release:push:npm', shell('npm publish'));
|
||||
|
||||
gulp.task('release:push', callback => {
|
||||
shellExec('git status --porcelain', (err, stdout) => {
|
||||
if (stdout && stdout.trim()) {
|
||||
throw new gutil.PluginError({
|
||||
plugin: 'release',
|
||||
|
@ -60,254 +88,3 @@ gulp.task('release:push', function(callback) {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('release:push:github', function(callback) {
|
||||
return gulp.src([
|
||||
'CHANGELOG.md',
|
||||
'LICENSE',
|
||||
buildDirectory + '/matter.min.js',
|
||||
buildDirectory + '/matter.js'
|
||||
]).pipe(release({
|
||||
owner: 'liabru',
|
||||
repo: pkg.name,
|
||||
tag: pkg.version,
|
||||
name: 'Matter.js ' + pkg.version
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('release:push:git', function(callback) {
|
||||
shell('git push', callback);
|
||||
});
|
||||
|
||||
gulp.task('release:push:npm', function(callback) {
|
||||
shell('npm publish', callback);
|
||||
});
|
||||
|
||||
gulp.task('build:dev', function() {
|
||||
return build(extend(extend({}, pkg), { version: pkg.version + '-dev' }));
|
||||
});
|
||||
|
||||
gulp.task('build:edge', function() {
|
||||
return build(extend(extend({}, pkg), { version: pkg.version + '-alpha' }));
|
||||
});
|
||||
|
||||
gulp.task('build:release', function() {
|
||||
return build(extend(extend({}, pkg), { version: pkg.version }));
|
||||
});
|
||||
|
||||
gulp.task('build:examples', function() {
|
||||
var options = extend(extend({}, pkg), { version: pkg.version + '-dev' });
|
||||
options.name = options.name + '-examples';
|
||||
options.date = options.date || new Date().toISOString().slice(0, 10);
|
||||
options.author = '@liabru';
|
||||
|
||||
return gulp.src('examples/**/*.js')
|
||||
.pipe(concat('examples.js'))
|
||||
.pipe(header(banner, { context: options }))
|
||||
.pipe(gulp.dest('examples/build'));
|
||||
});
|
||||
|
||||
gulp.task('watch', function() {
|
||||
var b = browserify({
|
||||
entries: ['src/module/main.js'],
|
||||
standalone: 'Matter',
|
||||
plugin: [watchify]
|
||||
});
|
||||
|
||||
var bundle = function() {
|
||||
gutil.log('Updated bundle build/matter-dev.js');
|
||||
b.bundle()
|
||||
.on('error', function(err) {
|
||||
gutil.log('ERROR', err.message);
|
||||
this.emit('end');
|
||||
})
|
||||
.pipe(through2({ objectMode: true }, function(chunk, encoding, callback) {
|
||||
return callback(
|
||||
null,
|
||||
chunk.toString().replace(/@@VERSION@@/g, pkg.version + '-dev')
|
||||
);
|
||||
}))
|
||||
.pipe(fs.createWriteStream('build/matter-dev.js'));
|
||||
};
|
||||
|
||||
b.on('update', bundle);
|
||||
bundle();
|
||||
});
|
||||
|
||||
gulp.task('bump', function() {
|
||||
return gulp.src(['package.json', 'bower.json'])
|
||||
.pipe(bump({ type: process.argv[4] || 'minor' }))
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
gulp.task('reload', function(callback) {
|
||||
delete require.cache[require.resolve('./package.json')];
|
||||
pkg = require('./package.json');
|
||||
callback();
|
||||
});
|
||||
|
||||
gulp.task('tag', function() {
|
||||
return gulp.src('package.json')
|
||||
.pipe(tag({ prefix: '' }));
|
||||
});
|
||||
|
||||
gulp.task('changelog', function () {
|
||||
return gulp.src('CHANGELOG.md')
|
||||
.pipe(changelog())
|
||||
.pipe(gulp.dest('.'));
|
||||
});
|
||||
|
||||
gulp.task('serve', function() {
|
||||
serve(false);
|
||||
});
|
||||
|
||||
gulp.task('serve:test', function() {
|
||||
serve(true);
|
||||
});
|
||||
|
||||
gulp.task('serve:stop', function() {
|
||||
if (server) {
|
||||
try {
|
||||
server.emit('kill');
|
||||
} catch (e) {} // eslint-disable-line no-empty
|
||||
gutil.log('Web server stopped');
|
||||
}
|
||||
});
|
||||
|
||||
gulp.task('test', function(callback) {
|
||||
// TODO: fix tests by switching to nightmare instead of phantom
|
||||
// sequence('serve:test', 'lint', 'build:dev', 'test:browser', 'test:node', 'serve:stop', callback);
|
||||
sequence('lint', callback);
|
||||
});
|
||||
|
||||
gulp.task('test:browser', function(callback) {
|
||||
shell('phantomjs test/browser/TestDemo.js', callback);
|
||||
});
|
||||
|
||||
gulp.task('test:node', function(callback) {
|
||||
shell('node test/node/TestDemo.js', callback);
|
||||
});
|
||||
|
||||
gulp.task('lint', function() {
|
||||
return gulp.src([
|
||||
'src/**/*.js',
|
||||
'demo/js/*.js',
|
||||
'examples/*.js',
|
||||
'test/browser/TestDemo.js',
|
||||
'test/node/TestDemo.js',
|
||||
'Gulpfile.js'
|
||||
])
|
||||
.pipe(eslint())
|
||||
.pipe(eslint.format())
|
||||
.pipe(eslint.failAfterError());
|
||||
});
|
||||
|
||||
gulp.task('doc', function(callback) {
|
||||
var options = {
|
||||
paths: ['src'],
|
||||
themedir: 'matter-doc-theme',
|
||||
outdir: 'doc/build',
|
||||
linkNatives: true,
|
||||
project: {
|
||||
name: pkg.name + ' ' + pkg.version + ' Physics Engine API Docs',
|
||||
description: pkg.description,
|
||||
version: pkg.version,
|
||||
url: pkg.homepage
|
||||
}
|
||||
};
|
||||
|
||||
var Y = require('yuidocjs');
|
||||
var json = new Y.YUIDoc(options).run();
|
||||
json.project = options.project;
|
||||
|
||||
var builder = new Y.DocBuilder(options, json);
|
||||
builder.compile(callback);
|
||||
});
|
||||
|
||||
var serve = function(isTest) {
|
||||
process.on('uncaughtException', function(err) {
|
||||
if (err.errno === 'EADDRINUSE') {
|
||||
gutil.log('Server already running (or port is otherwise in use)');
|
||||
}
|
||||
});
|
||||
|
||||
server = gulp.src('.')
|
||||
.pipe(webserver({
|
||||
host: '0.0.0.0',
|
||||
livereload: {
|
||||
enable: !isTest,
|
||||
filter: function(filename) {
|
||||
return filename.match(/build|demo/);
|
||||
}
|
||||
},
|
||||
middleware: livereload(),
|
||||
open: isTest ? false : 'http://localhost:8000/demo/index.html',
|
||||
directoryListing: true
|
||||
}));
|
||||
};
|
||||
|
||||
var build = function(options) {
|
||||
var isDev = options.version.indexOf('-dev') !== -1,
|
||||
filename = buildDirectory + (isDev ? '/matter-dev' : '/matter'),
|
||||
dest = filename + '.js',
|
||||
destMin = filename + '.min.js';
|
||||
|
||||
options.date = options.date || new Date().toISOString().slice(0, 10);
|
||||
options.author = '@liabru';
|
||||
|
||||
gutil.log('Building', filename, options.date);
|
||||
|
||||
var compiled = gulp.src(['src/module/main.js'])
|
||||
.pipe(through2.obj(function(file, enc, next){
|
||||
browserify(file.path, { standalone: 'Matter' })
|
||||
.bundle(function(err, res){
|
||||
file.contents = res;
|
||||
next(null, file);
|
||||
});
|
||||
}))
|
||||
.pipe(derequire())
|
||||
.pipe(replace('@@VERSION@@', options.version));
|
||||
|
||||
if (!isDev) {
|
||||
compiled.pipe(preprocess({ context: { DEBUG: false } }));
|
||||
}
|
||||
|
||||
var build = compiled.pipe(clone())
|
||||
.pipe(header(banner + '\n' + license + '\n\n', { context: options }))
|
||||
.pipe(rename(dest))
|
||||
.pipe(gulp.dest('.'));
|
||||
|
||||
var buildMin = compiled.pipe(clone())
|
||||
.pipe(uglify({ output: { max_line_len: 1000 } }))
|
||||
.pipe(header(banner, { context: options }))
|
||||
.pipe(rename(destMin))
|
||||
.pipe(gulp.dest('.'));
|
||||
|
||||
return es.merge(build, buildMin);
|
||||
};
|
||||
|
||||
var shell = function(command, callback) {
|
||||
var args = process.argv.slice(3).join(' '),
|
||||
proc = exec(command + ' ' + args, function(err, stdout, stderr) {
|
||||
callback(err, stdout, stderr, proc);
|
||||
});
|
||||
|
||||
proc.stdout.on('data', function(data) {
|
||||
process.stdout.write(data);
|
||||
});
|
||||
|
||||
proc.stderr.on('data', function(data) {
|
||||
process.stderr.write(data);
|
||||
});
|
||||
};
|
||||
|
||||
var license = fs.readFileSync('src/module/license.js');
|
||||
|
||||
var banner = [
|
||||
'/**',
|
||||
'* <%= context.name %> <%= context.version %> by <%= context.author %> <%= context.date %>',
|
||||
'* <%= context.homepage %>',
|
||||
'* License <%= context.license %>',
|
||||
'*/',
|
||||
''
|
||||
].join('\n');
|
||||
|
|
15
README.md
15
README.md
|
@ -111,15 +111,14 @@ See how others are using matter.js physics
|
|||
|
||||
### Install
|
||||
|
||||
Download the [edge build (master)](https://github.com/liabru/matter-js/blob/master/build/matter.js) or get a [stable release](https://github.com/liabru/matter-js/releases) and include the script in your web page:
|
||||
You can install using package managers [npm](https://www.npmjs.org/package/matter-js) and [Yarn](https://yarnpkg.com/) using:
|
||||
|
||||
npm install matter-js
|
||||
|
||||
Alternatively you can download a [stable release](https://github.com/liabru/matter-js/tags) or try the latest experimental [alpha build](https://github.com/liabru/matter-js/tree/master/build) (master) and include the script in your web page:
|
||||
|
||||
<script src="matter.js" type="text/javascript"></script>
|
||||
|
||||
You can also install using the package managers [Bower](http://bower.io/search/?q=matter-js) and [NPM](https://www.npmjs.org/package/matter-js).
|
||||
|
||||
bower install matter-js
|
||||
npm install matter-js
|
||||
|
||||
### Usage
|
||||
|
||||
Visit the [Getting started](https://github.com/liabru/matter-js/wiki/Getting-started) wiki page for a minimal usage example which should work in both browsers and Node.js.
|
||||
|
@ -149,13 +148,13 @@ See the [API Documentation](http://brm.io/matter-js/docs/) and the [wiki](https:
|
|||
|
||||
### Building and Contributing
|
||||
|
||||
To build you must first install [node.js](http://nodejs.org/) and [gulp](http://gulpjs.com/), then run
|
||||
To build you must first install [node.js](http://nodejs.org/), then run
|
||||
|
||||
npm install
|
||||
|
||||
This will install the required build dependencies, then run
|
||||
|
||||
gulp dev
|
||||
npm run dev
|
||||
|
||||
to spawn a development server. For information on contributing see [CONTRIBUTING.md](https://github.com/liabru/matter-js/blob/master/CONTRIBUTING.md).
|
||||
|
||||
|
|
|
@ -12,21 +12,21 @@
|
|||
|
||||
<title>Matter.js Demo</title>
|
||||
|
||||
<!-- libs -->
|
||||
<script type="text/javascript" src="/demo/lib/decomp.js"></script>
|
||||
<script type="text/javascript" src="/demo/lib/pathseg.js"></script>
|
||||
<!-- Libs -->
|
||||
<script type="text/javascript" src="./lib/decomp.js"></script>
|
||||
<script type="text/javascript" src="./lib/pathseg.js"></script>
|
||||
|
||||
<!-- Matter -->
|
||||
<script src="../build/matter-dev.js"></script>
|
||||
|
||||
<script src="../build/matter.js"></script>
|
||||
|
||||
<!-- MatterTools -->
|
||||
<script src="//code.jquery.com/jquery-3.1.1.js"></script>
|
||||
<script src="/demo/lib/matter-tools.gui.js"></script>
|
||||
<script src="/demo/lib/matter-tools.inspector.js"></script>
|
||||
<script src="/demo/lib/matter-tools.demo.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.js"></script>
|
||||
<script src="./lib/matter-tools.gui.js"></script>
|
||||
<script src="./lib/matter-tools.inspector.js"></script>
|
||||
<script src="./lib/matter-tools.demo.js"></script>
|
||||
|
||||
<!-- Plugins -->
|
||||
<script src="/demo/lib/matter-wrap.js"></script>
|
||||
<script src="./lib/matter-wrap.js"></script>
|
||||
|
||||
<!-- Examples -->
|
||||
<script src="../examples/airFriction.js"></script>
|
||||
|
@ -72,7 +72,7 @@
|
|||
<script src="../examples/timescale.js"></script>
|
||||
<script src="../examples/views.js"></script>
|
||||
<script src="../examples/wreckingBall.js"></script>
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
|
@ -82,9 +82,30 @@
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.matter-js-compare-build {
|
||||
position: absolute;
|
||||
background: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.matter-js-compare-build .matter-header-outer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.matter-js-compare-build canvas {
|
||||
opacity: 0.5;
|
||||
background: transparent !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="/demo/js/Demo.js"></script>
|
||||
<script src="./js/Demo.js"></script>
|
||||
</body>
|
||||
</html>
|
179
demo/js/Compare.js
Normal file
179
demo/js/Compare.js
Normal file
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* A Matter.js build comparison testbed.
|
||||
*
|
||||
* Tool for Matter.js maintainers to compare results of
|
||||
* the current source build with the release build in the browser.
|
||||
*
|
||||
* USAGE: open http://localhost:8000/?compare=120#mixed
|
||||
*
|
||||
* NOTE: For the actual example code, refer to the source files in `/examples/`.
|
||||
*
|
||||
* @class Compare
|
||||
*/
|
||||
|
||||
(function() {
|
||||
// maintain reference to dev version of Matter already loaded
|
||||
var MatterDev = window.Matter;
|
||||
|
||||
// load the build version of Matter
|
||||
var matterBuildScript = document.createElement('script');
|
||||
matterBuildScript.src = '../build/matter.min.js';
|
||||
|
||||
// wait for load
|
||||
matterBuildScript.addEventListener('load', function() {
|
||||
var examples = window.MatterDemo.examples;
|
||||
|
||||
// maintain reference of build version and set dev version as main
|
||||
var MatterBuild = window.Matter;
|
||||
window.Matter = MatterDev;
|
||||
|
||||
var demo = MatterTools.Demo.create({
|
||||
toolbar: {
|
||||
title: 'matter-js',
|
||||
url: 'https://github.com/liabru/matter-js',
|
||||
reset: true,
|
||||
source: true,
|
||||
inspector: true,
|
||||
tools: true,
|
||||
fullscreen: true,
|
||||
exampleSelect: true
|
||||
},
|
||||
tools: {
|
||||
inspector: true,
|
||||
gui: true
|
||||
},
|
||||
inline: false,
|
||||
preventZoom: true,
|
||||
resetOnOrientation: true,
|
||||
routing: true,
|
||||
startExample: 'mixed',
|
||||
examples: examples
|
||||
});
|
||||
|
||||
var demoBuild = MatterTools.Demo.create({
|
||||
toolbar: {
|
||||
title: 'matter-js-compare-build',
|
||||
reset: false,
|
||||
source: false,
|
||||
inspector: false,
|
||||
tools: false,
|
||||
fullscreen: false,
|
||||
exampleSelect: false
|
||||
},
|
||||
tools: {
|
||||
inspector: false,
|
||||
gui: false
|
||||
},
|
||||
inline: false,
|
||||
preventZoom: true,
|
||||
resetOnOrientation: true,
|
||||
routing: false,
|
||||
startExample: 'mixed',
|
||||
examples: examples.map(function(example) {
|
||||
return Matter.Common.extend({}, example);
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* NOTE: For the actual example code, refer to the source files in `/examples/`.
|
||||
* The code below is tooling for Matter.js maintainers to compare versions of Matter.
|
||||
*/
|
||||
|
||||
// build version should not run itself
|
||||
MatterBuild.Runner.run = function() {};
|
||||
MatterBuild.Render.run = function() {};
|
||||
|
||||
// maintain original references to patched methods
|
||||
MatterDev.Runner._tick = MatterDev.Runner.tick;
|
||||
MatterDev.Render._world = MatterDev.Render.world;
|
||||
MatterBuild.Mouse._setElement = MatterBuild.Mouse.setElement;
|
||||
|
||||
// patch MatterTools to control both demo versions simultaneously
|
||||
MatterTools.Demo._setExample = MatterTools.Demo.setExample;
|
||||
MatterTools.Demo.setExample = function(_demo, example) {
|
||||
MatterBuild.Common._nextId = MatterBuild.Common._seed = 0;
|
||||
MatterDev.Common._nextId = MatterDev.Common._seed = 0;
|
||||
|
||||
MatterBuild.Plugin._registry = MatterDev.Plugin._registry;
|
||||
MatterBuild.use.apply(null, MatterDev.used);
|
||||
|
||||
window.Matter = MatterDev;
|
||||
MatterTools.Demo._setExample(
|
||||
demo, demo.examples.find(function(e) { return e.name === example.name; })
|
||||
);
|
||||
|
||||
var maxTicks = parseFloat(window.location.search.split('=')[1]);
|
||||
var ticks = 0;
|
||||
|
||||
MatterDev.Runner.tick = function(runner, engine, time) {
|
||||
if (ticks === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ticks >= maxTicks) {
|
||||
console.info(
|
||||
'Demo.Compare: ran ' + ticks + ' ticks, timestamp is now '
|
||||
+ engine.timing.timestamp.toFixed(2)
|
||||
);
|
||||
|
||||
ticks = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ticks += 1;
|
||||
|
||||
var demoBuildInstance = demoBuild.example.instance;
|
||||
runner.isFixed = demoBuildInstance.runner.isFixed = true;
|
||||
runner.delta = demoBuildInstance.runner.delta = 1000 / 60;
|
||||
|
||||
window.Matter = MatterBuild;
|
||||
MatterBuild.Runner.tick(demoBuildInstance.runner, demoBuildInstance.engine, time);
|
||||
window.Matter = MatterDev;
|
||||
return MatterDev.Runner._tick(runner, engine, time);
|
||||
};
|
||||
|
||||
MatterDev.Render.world = function(render) {
|
||||
window.Matter = MatterBuild;
|
||||
MatterBuild.Render.world(demoBuild.example.instance.render);
|
||||
window.Matter = MatterDev;
|
||||
return MatterDev.Render._world(render);
|
||||
};
|
||||
|
||||
MatterBuild.Mouse.setElement = function(mouse) {
|
||||
return MatterBuild.Mouse._setElement(mouse, demo.example.instance.render.canvas);
|
||||
};
|
||||
|
||||
window.Matter = MatterBuild;
|
||||
MatterTools.Demo._setExample(
|
||||
demoBuild, demoBuild.examples.find(function(e) { return e.name === example.name; })
|
||||
);
|
||||
|
||||
window.Matter = MatterDev;
|
||||
};
|
||||
|
||||
// reset both engine versions simultaneously
|
||||
MatterTools.Demo._reset = MatterTools.Demo.reset;
|
||||
MatterTools.Demo.reset = function(_demo) {
|
||||
MatterBuild.Common._nextId = MatterBuild.Common._seed = 0;
|
||||
MatterDev.Common._nextId = MatterDev.Common._seed = 0;
|
||||
|
||||
window.Matter = MatterBuild;
|
||||
MatterTools.Demo._reset(demoBuild);
|
||||
|
||||
window.Matter = MatterDev;
|
||||
MatterTools.Demo._reset(demo);
|
||||
};
|
||||
|
||||
document.body.appendChild(demo.dom.root);
|
||||
document.body.appendChild(demoBuild.dom.root);
|
||||
|
||||
MatterTools.Demo.start(demo);
|
||||
|
||||
console.info(
|
||||
'Demo.Compare: comparing matter-js@' + MatterDev.version + ' with matter-js@' + MatterBuild.version
|
||||
);
|
||||
});
|
||||
|
||||
document.body.append(matterBuildScript);
|
||||
})();
|
330
demo/js/Demo.js
330
demo/js/Demo.js
|
@ -1,5 +1,8 @@
|
|||
/**
|
||||
* The Matter.js demo page controller and example runner.
|
||||
* The Matter.js development demo and testing tool.
|
||||
*
|
||||
* This demo uses MatterTools, you can see the wiki for a simple example instead:
|
||||
* https://github.com/liabru/matter-js/wiki/Getting-started
|
||||
*
|
||||
* NOTE: For the actual example code, refer to the source files in `/examples/`.
|
||||
*
|
||||
|
@ -7,8 +10,72 @@
|
|||
*/
|
||||
|
||||
(function() {
|
||||
var examples = [
|
||||
{ name: 'Air Friction', id: 'airFriction' },
|
||||
{ name: 'Avalanche', id: 'avalanche' },
|
||||
{ name: 'Ball Pool', id: 'ballPool' },
|
||||
{ name: 'Bridge', id: 'bridge' },
|
||||
{ name: 'Broadphase', id: 'broadphase' },
|
||||
{ name: 'Car', id: 'car' },
|
||||
{ name: 'Catapult', id: 'catapult' },
|
||||
{ name: 'Chains', id: 'chains' },
|
||||
{ name: 'Circle Stack', id: 'circleStack' },
|
||||
{ name: 'Cloth', id: 'cloth' },
|
||||
{ name: 'Collision Filtering', id: 'collisionFiltering' },
|
||||
{ name: 'Composite Manipulation', id: 'compositeManipulation' },
|
||||
{ name: 'Compound Bodies', id: 'compound' },
|
||||
{ name: 'Compound Stack', id: 'compoundStack' },
|
||||
{ name: 'Concave', id: 'concave' },
|
||||
{ name: 'Constraints', id: 'constraints' },
|
||||
{ name: 'Double Pendulum', id: 'doublePendulum' },
|
||||
{ name: 'Events', id: 'events' },
|
||||
{ name: 'Friction', id: 'friction' },
|
||||
{ name: 'Reverse Gravity', id: 'gravity' },
|
||||
{ name: 'Gyroscope', id: 'gyro' },
|
||||
{ name: 'Manipulation', id: 'manipulation' },
|
||||
{ name: 'Mixed Shapes', id: 'mixed' },
|
||||
{ name: 'Newton\'s Cradle', id: 'newtonsCradle' },
|
||||
{ name: 'Ragdoll', id: 'ragdoll' },
|
||||
{ name: 'Pyramid', id: 'pyramid' },
|
||||
{ name: 'Raycasting', id: 'raycasting' },
|
||||
{ name: 'Restitution', id: 'restitution' },
|
||||
{ name: 'Rounded Corners (Chamfering)', id: 'rounded' },
|
||||
{ name: 'Sensors', id: 'sensors' },
|
||||
{ name: 'Sleeping', id: 'sleeping' },
|
||||
{ name: 'Slingshot', id: 'slingshot' },
|
||||
{ name: 'Soft Body', id: 'softBody' },
|
||||
{ name: 'Sprites', id: 'sprites' },
|
||||
{ name: 'Stack', id: 'stack' },
|
||||
{ name: 'Static Friction', id: 'staticFriction' },
|
||||
{ name: 'Stress', id: 'stress' },
|
||||
{ name: 'Stress 2', id: 'stress2' },
|
||||
{ name: 'Concave SVG Paths', id: 'svg' },
|
||||
{ name: 'Terrain', id: 'terrain' },
|
||||
{ name: 'Time Scaling', id: 'timescale' },
|
||||
{ name: 'Views', id: 'views' },
|
||||
{ name: 'Wrecking Ball', id: 'wreckingBall' }
|
||||
];
|
||||
|
||||
var sourceLinkRoot = 'https://github.com/liabru/matter-js/blob/master/examples';
|
||||
|
||||
for (var i = 0; i < examples.length; i += 1) {
|
||||
var example = examples[i];
|
||||
example.sourceLink = sourceLinkRoot + '/' + example.id + '.js';
|
||||
example.init = window.Example[example.id];
|
||||
|
||||
if (!example.init) {
|
||||
console.warn('Example not loaded:', example.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.location.search.indexOf('compare') >= 0) {
|
||||
var compareScript = document.createElement('script');
|
||||
compareScript.src = '../js/Compare.js';
|
||||
window.MatterDemo = { examples: examples };
|
||||
document.body.append(compareScript);
|
||||
return;
|
||||
}
|
||||
|
||||
var demo = MatterTools.Demo.create({
|
||||
toolbar: {
|
||||
title: 'matter-js',
|
||||
|
@ -29,266 +96,7 @@
|
|||
resetOnOrientation: true,
|
||||
routing: true,
|
||||
startExample: 'mixed',
|
||||
examples: [
|
||||
{
|
||||
name: 'Air Friction',
|
||||
id: 'airFriction',
|
||||
init: Example.airFriction,
|
||||
sourceLink: sourceLinkRoot + '/airFriction.js'
|
||||
},
|
||||
{
|
||||
name: 'Avalanche',
|
||||
id: 'avalanche',
|
||||
init: Example.avalanche,
|
||||
sourceLink: sourceLinkRoot + '/avalanche.js'
|
||||
},
|
||||
{
|
||||
name: 'Ball Pool',
|
||||
id: 'ballPool',
|
||||
init: Example.ballPool,
|
||||
sourceLink: sourceLinkRoot + '/ballPool.js'
|
||||
},
|
||||
{
|
||||
name: 'Bridge',
|
||||
id: 'bridge',
|
||||
init: Example.bridge,
|
||||
sourceLink: sourceLinkRoot + '/bridge.js'
|
||||
},
|
||||
{
|
||||
name: 'Broadphase',
|
||||
id: 'broadphase',
|
||||
init: Example.broadphase,
|
||||
sourceLink: sourceLinkRoot + '/broadphase.js'
|
||||
},
|
||||
{
|
||||
name: 'Car',
|
||||
id: 'car',
|
||||
init: Example.car,
|
||||
sourceLink: sourceLinkRoot + '/car.js'
|
||||
},
|
||||
{
|
||||
name: 'Catapult',
|
||||
id: 'catapult',
|
||||
init: Example.catapult,
|
||||
sourceLink: sourceLinkRoot + '/catapult.js'
|
||||
},
|
||||
{
|
||||
name: 'Chains',
|
||||
id: 'chains',
|
||||
init: Example.chains,
|
||||
sourceLink: sourceLinkRoot + '/chains.js'
|
||||
},
|
||||
{
|
||||
name: 'Circle Stack',
|
||||
id: 'circleStack',
|
||||
init: Example.circleStack,
|
||||
sourceLink: sourceLinkRoot + '/circleStack.js'
|
||||
},
|
||||
{
|
||||
name: 'Cloth',
|
||||
id: 'cloth',
|
||||
init: Example.cloth,
|
||||
sourceLink: sourceLinkRoot + '/cloth.js'
|
||||
},
|
||||
{
|
||||
name: 'Collision Filtering',
|
||||
id: 'collisionFiltering',
|
||||
init: Example.collisionFiltering,
|
||||
sourceLink: sourceLinkRoot + '/collisionFiltering.js'
|
||||
},
|
||||
{
|
||||
name: 'Composite Manipulation',
|
||||
id: 'compositeManipulation',
|
||||
init: Example.compositeManipulation,
|
||||
sourceLink: sourceLinkRoot + '/compositeManipulation.js'
|
||||
},
|
||||
{
|
||||
name: 'Compound Bodies',
|
||||
id: 'compound',
|
||||
init: Example.compound,
|
||||
sourceLink: sourceLinkRoot + '/compound.js'
|
||||
},
|
||||
{
|
||||
name: 'Compound Stack',
|
||||
id: 'compoundStack',
|
||||
init: Example.compoundStack,
|
||||
sourceLink: sourceLinkRoot + '/compoundStack.js'
|
||||
},
|
||||
{
|
||||
name: 'Concave',
|
||||
id: 'concave',
|
||||
init: Example.concave,
|
||||
sourceLink: sourceLinkRoot + '/concave.js'
|
||||
},
|
||||
{
|
||||
name: 'Constraints',
|
||||
id: 'constraints',
|
||||
init: Example.constraints,
|
||||
sourceLink: sourceLinkRoot + '/constraints.js'
|
||||
},
|
||||
{
|
||||
name: 'Double Pendulum',
|
||||
id: 'doublePendulum',
|
||||
init: Example.doublePendulum,
|
||||
sourceLink: sourceLinkRoot + '/doublePendulum.js'
|
||||
},
|
||||
{
|
||||
name: 'Events',
|
||||
id: 'events',
|
||||
init: Example.events,
|
||||
sourceLink: sourceLinkRoot + '/events.js'
|
||||
},
|
||||
{
|
||||
name: 'Friction',
|
||||
id: 'friction',
|
||||
init: Example.friction,
|
||||
sourceLink: sourceLinkRoot + '/friction.js'
|
||||
},
|
||||
{
|
||||
name: 'Reverse Gravity',
|
||||
id: 'gravity',
|
||||
init: Example.gravity,
|
||||
sourceLink: sourceLinkRoot + '/gravity.js'
|
||||
},
|
||||
{
|
||||
name: 'Gyroscope',
|
||||
id: 'gyro',
|
||||
init: Example.gyro,
|
||||
sourceLink: sourceLinkRoot + '/gyro.js'
|
||||
},
|
||||
{
|
||||
name: 'Manipulation',
|
||||
id: 'manipulation',
|
||||
init: Example.manipulation,
|
||||
sourceLink: sourceLinkRoot + '/manipulation.js'
|
||||
},
|
||||
{
|
||||
name: 'Mixed Shapes',
|
||||
id: 'mixed',
|
||||
init: Example.mixed,
|
||||
sourceLink: sourceLinkRoot + '/mixed.js'
|
||||
},
|
||||
{
|
||||
name: 'Newton\'s Cradle',
|
||||
id: 'newtonsCradle',
|
||||
init: Example.newtonsCradle,
|
||||
sourceLink: sourceLinkRoot + '/newtonsCradle.js'
|
||||
},
|
||||
{
|
||||
name: 'Ragdoll',
|
||||
id: 'ragdoll',
|
||||
init: Example.ragdoll,
|
||||
sourceLink: sourceLinkRoot + '/ragdoll.js'
|
||||
},
|
||||
{
|
||||
name: 'Pyramid',
|
||||
id: 'pyramid',
|
||||
init: Example.pyramid,
|
||||
sourceLink: sourceLinkRoot + '/pyramid.js'
|
||||
},
|
||||
{
|
||||
name: 'Raycasting',
|
||||
id: 'raycasting',
|
||||
init: Example.raycasting,
|
||||
sourceLink: sourceLinkRoot + '/raycasting.js'
|
||||
},
|
||||
{
|
||||
name: 'Restitution',
|
||||
id: 'restitution',
|
||||
init: Example.restitution,
|
||||
sourceLink: sourceLinkRoot + '/restitution.js'
|
||||
},
|
||||
{
|
||||
name: 'Rounded Corners (Chamfering)',
|
||||
id: 'rounded',
|
||||
init: Example.rounded,
|
||||
sourceLink: sourceLinkRoot + '/rounded.js'
|
||||
},
|
||||
{
|
||||
name: 'Sensors',
|
||||
id: 'sensors',
|
||||
init: Example.sensors,
|
||||
sourceLink: sourceLinkRoot + '/sensors.js'
|
||||
},
|
||||
{
|
||||
name: 'Sleeping',
|
||||
id: 'sleeping',
|
||||
init: Example.sleeping,
|
||||
sourceLink: sourceLinkRoot + '/sleeping.js'
|
||||
},
|
||||
{
|
||||
name: 'Slingshot',
|
||||
id: 'slingshot',
|
||||
init: Example.slingshot,
|
||||
sourceLink: sourceLinkRoot + '/slingshot.js'
|
||||
},
|
||||
{
|
||||
name: 'Soft Body',
|
||||
id: 'softBody',
|
||||
init: Example.softBody,
|
||||
sourceLink: sourceLinkRoot + '/softBody.js'
|
||||
},
|
||||
{
|
||||
name: 'Sprites',
|
||||
id: 'sprites',
|
||||
init: Example.sprites,
|
||||
sourceLink: sourceLinkRoot + '/sprites.js'
|
||||
},
|
||||
{
|
||||
name: 'Stack',
|
||||
id: 'stack',
|
||||
init: Example.stack,
|
||||
sourceLink: sourceLinkRoot + '/stack.js'
|
||||
},
|
||||
{
|
||||
name: 'Static Friction',
|
||||
id: 'staticFriction',
|
||||
init: Example.staticFriction,
|
||||
sourceLink: sourceLinkRoot + '/staticFriction.js'
|
||||
},
|
||||
{
|
||||
name: 'Stress',
|
||||
id: 'stress',
|
||||
init: Example.stress,
|
||||
sourceLink: sourceLinkRoot + '/stress.js'
|
||||
},
|
||||
{
|
||||
name: 'Stress 2',
|
||||
id: 'stress2',
|
||||
init: Example.stress2,
|
||||
sourceLink: sourceLinkRoot + '/stress2.js'
|
||||
},
|
||||
{
|
||||
name: 'Concave SVG Paths',
|
||||
id: 'svg',
|
||||
init: Example.svg,
|
||||
sourceLink: sourceLinkRoot + '/svg.js'
|
||||
},
|
||||
{
|
||||
name: 'Terrain',
|
||||
id: 'terrain',
|
||||
init: Example.terrain,
|
||||
sourceLink: sourceLinkRoot + '/terrain.js'
|
||||
},
|
||||
{
|
||||
name: 'Time Scaling',
|
||||
id: 'timescale',
|
||||
init: Example.timescale,
|
||||
sourceLink: sourceLinkRoot + '/timescale.js'
|
||||
},
|
||||
{
|
||||
name: 'Views',
|
||||
id: 'views',
|
||||
init: Example.views,
|
||||
sourceLink: sourceLinkRoot + '/views.js'
|
||||
},
|
||||
{
|
||||
name: 'Wrecking Ball',
|
||||
id: 'wreckingBall',
|
||||
init: Example.wreckingBall,
|
||||
sourceLink: sourceLinkRoot + '/wreckingBall.js'
|
||||
}
|
||||
]
|
||||
examples: examples
|
||||
});
|
||||
|
||||
document.body.appendChild(demo.dom.root);
|
||||
|
|
|
@ -78,4 +78,8 @@ Example.airFriction = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
var Example = Example || {};
|
||||
|
||||
Matter.use(
|
||||
'matter-wrap'
|
||||
);
|
||||
|
||||
Example.avalanche = function() {
|
||||
Matter.use(
|
||||
'matter-wrap'
|
||||
);
|
||||
|
||||
var Engine = Matter.Engine,
|
||||
Render = Matter.Render,
|
||||
Runner = Matter.Runner,
|
||||
|
@ -89,4 +89,8 @@ Example.avalanche = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
var Example = Example || {};
|
||||
|
||||
Matter.use(
|
||||
'matter-wrap'
|
||||
);
|
||||
|
||||
Example.ballPool = function() {
|
||||
Matter.use(
|
||||
'matter-wrap'
|
||||
);
|
||||
|
||||
var Engine = Matter.Engine,
|
||||
Render = Matter.Render,
|
||||
Runner = Matter.Runner,
|
||||
|
@ -97,4 +97,8 @@ Example.ballPool = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -122,4 +122,8 @@ Example.bridge = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -93,4 +93,8 @@ Example.broadphase = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -87,4 +87,8 @@ Example.car = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -94,4 +94,8 @@ Example.catapult = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -120,4 +120,8 @@ Example.chains = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -79,4 +79,8 @@ Example.circleStack = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -82,4 +82,8 @@ Example.cloth = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -153,4 +153,8 @@ Example.collisionFiltering = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -106,4 +106,8 @@ Example.compositeManipulation = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -104,4 +104,8 @@ Example.compound = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -87,4 +87,8 @@ Example.compoundStack = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -93,4 +93,8 @@ Example.concave = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -177,4 +177,8 @@ Example.constraints = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -150,4 +150,8 @@ Example.doublePendulum = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -184,4 +184,8 @@ Example.events = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -88,4 +88,8 @@ Example.friction = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -94,4 +94,8 @@ Example.gravity = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -68,26 +68,28 @@ Example.gyro = function() {
|
|||
]);
|
||||
|
||||
// add gyro control
|
||||
var updateGravity = function(event) {
|
||||
var orientation = typeof window.orientation !== 'undefined' ? window.orientation : 0,
|
||||
gravity = engine.world.gravity;
|
||||
if (typeof window !== 'undefined') {
|
||||
var updateGravity = function(event) {
|
||||
var orientation = typeof window.orientation !== 'undefined' ? window.orientation : 0,
|
||||
gravity = engine.world.gravity;
|
||||
|
||||
if (orientation === 0) {
|
||||
gravity.x = Common.clamp(event.gamma, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(event.beta, -90, 90) / 90;
|
||||
} else if (orientation === 180) {
|
||||
gravity.x = Common.clamp(event.gamma, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(-event.beta, -90, 90) / 90;
|
||||
} else if (orientation === 90) {
|
||||
gravity.x = Common.clamp(event.beta, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(-event.gamma, -90, 90) / 90;
|
||||
} else if (orientation === -90) {
|
||||
gravity.x = Common.clamp(-event.beta, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(event.gamma, -90, 90) / 90;
|
||||
}
|
||||
};
|
||||
if (orientation === 0) {
|
||||
gravity.x = Common.clamp(event.gamma, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(event.beta, -90, 90) / 90;
|
||||
} else if (orientation === 180) {
|
||||
gravity.x = Common.clamp(event.gamma, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(-event.beta, -90, 90) / 90;
|
||||
} else if (orientation === 90) {
|
||||
gravity.x = Common.clamp(event.beta, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(-event.gamma, -90, 90) / 90;
|
||||
} else if (orientation === -90) {
|
||||
gravity.x = Common.clamp(-event.beta, -90, 90) / 90;
|
||||
gravity.y = Common.clamp(event.gamma, -90, 90) / 90;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('deviceorientation', updateGravity);
|
||||
window.addEventListener('deviceorientation', updateGravity);
|
||||
}
|
||||
|
||||
// add mouse control
|
||||
var mouse = Mouse.create(render.canvas),
|
||||
|
@ -121,7 +123,13 @@ Example.gyro = function() {
|
|||
stop: function() {
|
||||
Matter.Render.stop(render);
|
||||
Matter.Runner.stop(runner);
|
||||
window.removeEventListener('deviceorientation', updateGravity);
|
||||
if (typeof window !== 'undefined') {
|
||||
window.removeEventListener('deviceorientation', updateGravity);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
45
examples/index.js
Normal file
45
examples/index.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
module.exports = {
|
||||
airFriction: require('./airFriction.js'),
|
||||
avalanche: require('./avalanche.js'),
|
||||
ballPool: require('./ballPool.js'),
|
||||
bridge: require('./bridge.js'),
|
||||
broadphase: require('./broadphase.js'),
|
||||
car: require('./car.js'),
|
||||
catapult: require('./catapult.js'),
|
||||
chains: require('./chains.js'),
|
||||
circleStack: require('./circleStack.js'),
|
||||
cloth: require('./cloth.js'),
|
||||
collisionFiltering: require('./collisionFiltering.js'),
|
||||
compositeManipulation: require('./compositeManipulation.js'),
|
||||
compound: require('./compound.js'),
|
||||
compoundStack: require('./compoundStack.js'),
|
||||
concave: require('./concave.js'),
|
||||
constraints: require('./constraints.js'),
|
||||
doublePendulum: require('./doublePendulum.js'),
|
||||
events: require('./events.js'),
|
||||
friction: require('./friction.js'),
|
||||
gravity: require('./gravity.js'),
|
||||
gyro: require('./gyro.js'),
|
||||
manipulation: require('./manipulation.js'),
|
||||
mixed: require('./mixed.js'),
|
||||
newtonsCradle: require('./newtonsCradle.js'),
|
||||
ragdoll: require('./ragdoll.js'),
|
||||
pyramid: require('./pyramid.js'),
|
||||
raycasting: require('./raycasting.js'),
|
||||
restitution: require('./restitution.js'),
|
||||
rounded: require('./rounded.js'),
|
||||
sensors: require('./sensors.js'),
|
||||
sleeping: require('./sleeping.js'),
|
||||
slingshot: require('./slingshot.js'),
|
||||
softBody: require('./softBody.js'),
|
||||
sprites: require('./sprites.js'),
|
||||
stack: require('./stack.js'),
|
||||
staticFriction: require('./staticFriction.js'),
|
||||
stress: require('./stress.js'),
|
||||
stress2: require('./stress2.js'),
|
||||
svg: require('./svg.js'),
|
||||
terrain: require('./terrain.js'),
|
||||
timescale: require('./timescale.js'),
|
||||
views: require('./views.js'),
|
||||
wreckingBall: require('./wreckingBall.js')
|
||||
};
|
|
@ -137,4 +137,8 @@ Example.manipulation = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -103,4 +103,8 @@ Example.mixed = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -74,4 +74,8 @@ Example.newtonsCradle = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -79,4 +79,8 @@ Example.pyramid = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -493,4 +493,8 @@ Example.ragdoll.ragdoll = function(x, y, scale, options) {
|
|||
});
|
||||
|
||||
return person;
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -133,4 +133,8 @@ Example.raycasting = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -83,4 +83,8 @@ Example.restitution = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -107,4 +107,8 @@ Example.rounded = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -129,4 +129,8 @@ Example.sensors = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -102,4 +102,8 @@ Example.sleeping = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -105,4 +105,8 @@ Example.slingshot = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -83,4 +83,8 @@ Example.softBody = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -111,4 +111,8 @@ Example.sprites = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -79,4 +79,8 @@ Example.stack = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -99,4 +99,8 @@ Example.staticFriction = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -77,4 +77,8 @@ Example.stress = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -77,4 +77,8 @@ Example.stress2 = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -40,44 +40,46 @@ Example.svg = function() {
|
|||
'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']);
|
||||
if (typeof $ !== 'undefined') {
|
||||
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));
|
||||
$(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,
|
||||
lineWidth: 1
|
||||
}
|
||||
}, true));
|
||||
});
|
||||
})(i);
|
||||
}
|
||||
|
||||
World.add(world, Bodies.fromVertices(100 + i * 150, 200 + i * 50, vertexSets, {
|
||||
render: {
|
||||
fillStyle: color,
|
||||
strokeStyle: color,
|
||||
lineWidth: 1
|
||||
}
|
||||
}, true));
|
||||
$.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));
|
||||
});
|
||||
})(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,
|
||||
lineWidth: 1
|
||||
}
|
||||
}, true));
|
||||
});
|
||||
|
||||
World.add(world, Bodies.fromVertices(400, 80, vertexSets, {
|
||||
render: {
|
||||
fillStyle: color,
|
||||
strokeStyle: color,
|
||||
lineWidth: 1
|
||||
}
|
||||
}, true));
|
||||
});
|
||||
}
|
||||
|
||||
World.add(world, [
|
||||
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
|
||||
|
@ -120,4 +122,8 @@ Example.svg = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -36,36 +36,38 @@ Example.terrain = function() {
|
|||
// add bodies
|
||||
var terrain;
|
||||
|
||||
$.get('./svg/terrain.svg').done(function(data) {
|
||||
var vertexSets = [];
|
||||
if (typeof $ !== 'undefined') {
|
||||
$.get('./svg/terrain.svg').done(function(data) {
|
||||
var vertexSets = [];
|
||||
|
||||
$(data).find('path').each(function(i, path) {
|
||||
vertexSets.push(Svg.pathToVertices(path, 30));
|
||||
$(data).find('path').each(function(i, path) {
|
||||
vertexSets.push(Svg.pathToVertices(path, 30));
|
||||
});
|
||||
|
||||
terrain = Bodies.fromVertices(400, 350, vertexSets, {
|
||||
isStatic: true,
|
||||
render: {
|
||||
fillStyle: '#2e2b44',
|
||||
strokeStyle: '#2e2b44',
|
||||
lineWidth: 1
|
||||
}
|
||||
}, 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) {
|
||||
if (Query.point([terrain], { x: x, y: y }).length === 0) {
|
||||
return Bodies.polygon(x, y, 5, 12, bodyOptions);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
terrain = Bodies.fromVertices(400, 350, vertexSets, {
|
||||
isStatic: true,
|
||||
render: {
|
||||
fillStyle: '#2e2b44',
|
||||
strokeStyle: '#2e2b44',
|
||||
lineWidth: 1
|
||||
}
|
||||
}, 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) {
|
||||
if (Query.point([terrain], { x: x, y: y }).length === 0) {
|
||||
return Bodies.polygon(x, y, 5, 12, bodyOptions);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// add mouse control
|
||||
var mouse = Mouse.create(render.canvas),
|
||||
|
@ -101,4 +103,8 @@ Example.terrain = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example;
|
||||
}
|
|
@ -148,4 +148,8 @@ Example.timescale = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -180,4 +180,8 @@ Example.views = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
|
@ -91,4 +91,8 @@ Example.wreckingBall = function() {
|
|||
Matter.Runner.stop(runner);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = Example[Object.keys(Example)[0]];
|
||||
}
|
14132
package-lock.json
generated
Normal file
14132
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
42
package.json
42
package.json
|
@ -20,38 +20,30 @@
|
|||
"rigid body physics"
|
||||
],
|
||||
"devDependencies": {
|
||||
"browserify": "^14.0.0",
|
||||
"cheerio": "^0.22.0",
|
||||
"connect-livereload": "^0.6.0",
|
||||
"event-stream": "^3.3.2",
|
||||
"fast-json-patch": "^1.1.4",
|
||||
"gulp": "^3.9.0",
|
||||
"eslint": "^6.8.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-bump": "^2.6.1",
|
||||
"gulp-clone": "^1.0.0",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-conventional-changelog": "^1.1.0",
|
||||
"gulp-derequire": "^2.1.0",
|
||||
"gulp-eslint": "^3.0.1",
|
||||
"gulp-github-release": "^1.2.1",
|
||||
"gulp-header": "^1.7.1",
|
||||
"gulp-preprocess": "^2.0.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-replace": "^0.5.4",
|
||||
"gulp-tag-version": "^1.3.0",
|
||||
"gulp-uglify": "^2.0.1",
|
||||
"gulp-util": "^3.0.8",
|
||||
"gulp-webdriver": "^2.0.3",
|
||||
"gulp-webserver": "^0.9.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"rimraf": "^2.4.2",
|
||||
"jest": "^25.1.0",
|
||||
"jest-worker": "^24.9.0",
|
||||
"json-stringify-pretty-compact": "^2.0.0",
|
||||
"run-sequence": "^1.1.4",
|
||||
"through2": "^2.0.3",
|
||||
"vinyl-transform": "^1.0.0",
|
||||
"watchify": "^3.9.0",
|
||||
"yuidocjs": "^0.10.2"
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.10.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "gulp build:dev && gulp lint"
|
||||
"dev": "webpack-dev-server --watch-content-base",
|
||||
"build": "webpack --mode=production & webpack --mode=production --env.MINIMIZE",
|
||||
"build-alpha": "webpack --mode=production --env.ALPHA & webpack --mode=production --env.MINIMIZE --env.ALPHA",
|
||||
"build-examples": "webpack --config webpack.examples.config.js --mode=production --env.ALPHA & webpack --config webpack.examples.config.js --mode=production --env.MINIMIZE --env.ALPHA",
|
||||
"lint": "eslint 'src/**/*.js' 'demo/js/Demo.js' 'demo/js/Compare.js' 'examples/*.js' 'webpack.*.js' 'Gulpfile.js'",
|
||||
"doc": "gulp doc",
|
||||
"test": "jest",
|
||||
"test-save": "SAVE=true jest",
|
||||
"test-watch": "jest --watch"
|
||||
},
|
||||
"dependencies": {},
|
||||
"files": [
|
||||
|
|
|
@ -49,7 +49,6 @@ var Axes = require('../geometry/Axes');
|
|||
force: { x: 0, y: 0 },
|
||||
torque: 0,
|
||||
positionImpulse: { x: 0, y: 0 },
|
||||
previousPositionImpulse: { x: 0, y: 0 },
|
||||
constraintImpulse: { x: 0, y: 0, angle: 0 },
|
||||
totalContacts: 0,
|
||||
speed: 0,
|
||||
|
@ -193,8 +192,7 @@ var Axes = require('../geometry/Axes');
|
|||
}
|
||||
|
||||
for (property in settings) {
|
||||
|
||||
if (!settings.hasOwnProperty(property))
|
||||
if (!Object.prototype.hasOwnProperty.call(settings, property))
|
||||
continue;
|
||||
|
||||
value = settings[property];
|
||||
|
@ -1264,7 +1262,7 @@ var Axes = require('../geometry/Axes');
|
|||
* @default 1
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* A `Number` that defines the offset in the x-axis for the sprite (normalised by texture width).
|
||||
*
|
||||
* @property render.sprite.xOffset
|
||||
|
@ -1272,7 +1270,7 @@ var Axes = require('../geometry/Axes');
|
|||
* @default 0
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* A `Number` that defines the offset in the y-axis for the sprite (normalised by texture height).
|
||||
*
|
||||
* @property render.sprite.yOffset
|
||||
|
|
|
@ -440,8 +440,8 @@ var Body = require('./Body');
|
|||
*/
|
||||
Composite.rebase = function(composite) {
|
||||
var objects = Composite.allBodies(composite)
|
||||
.concat(Composite.allConstraints(composite))
|
||||
.concat(Composite.allComposites(composite));
|
||||
.concat(Composite.allConstraints(composite))
|
||||
.concat(Composite.allComposites(composite));
|
||||
|
||||
for (var i = 0; i < objects.length; i++) {
|
||||
objects[i].id = Common.nextId();
|
||||
|
|
|
@ -128,7 +128,7 @@ var Common = require('../core/Common');
|
|||
* @return {world} The original world with the objects from composite added
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* An alias for Composite.addBody
|
||||
* @method addBody
|
||||
* @param {world} world
|
||||
|
@ -136,7 +136,7 @@ var Common = require('../core/Common');
|
|||
* @return {world} The original world with the body added
|
||||
*/
|
||||
|
||||
/**
|
||||
/**
|
||||
* An alias for Composite.addConstraint
|
||||
* @method addConstraint
|
||||
* @param {world} world
|
||||
|
|
38
src/collision/Contact.js
Normal file
38
src/collision/Contact.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* The `Matter.Contact` module contains methods for creating and manipulating collision contacts.
|
||||
*
|
||||
* @class Contact
|
||||
*/
|
||||
|
||||
var Contact = {};
|
||||
|
||||
module.exports = Contact;
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Creates a new contact.
|
||||
* @method create
|
||||
* @param {vertex} vertex
|
||||
* @return {contact} A new contact
|
||||
*/
|
||||
Contact.create = function(vertex) {
|
||||
return {
|
||||
id: Contact.id(vertex),
|
||||
vertex: vertex,
|
||||
normalImpulse: 0,
|
||||
tangentImpulse: 0
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a contact id.
|
||||
* @method id
|
||||
* @param {vertex} vertex
|
||||
* @return {string} Unique contactID
|
||||
*/
|
||||
Contact.id = function(vertex) {
|
||||
return vertex.body.id + '_' + vertex.index;
|
||||
};
|
||||
|
||||
})();
|
|
@ -8,6 +8,8 @@ var Pair = {};
|
|||
|
||||
module.exports = Pair;
|
||||
|
||||
var Contact = require('./Contact');
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
|
@ -19,12 +21,15 @@ module.exports = Pair;
|
|||
*/
|
||||
Pair.create = function(collision, timestamp) {
|
||||
var bodyA = collision.bodyA,
|
||||
bodyB = collision.bodyB;
|
||||
bodyB = collision.bodyB,
|
||||
parentA = collision.parentA,
|
||||
parentB = collision.parentB;
|
||||
|
||||
var pair = {
|
||||
id: Pair.id(bodyA, bodyB),
|
||||
bodyA: bodyA,
|
||||
bodyB: bodyB,
|
||||
contacts: {},
|
||||
activeContacts: [],
|
||||
separation: 0,
|
||||
isActive: true,
|
||||
|
@ -32,12 +37,11 @@ module.exports = Pair;
|
|||
isSensor: bodyA.isSensor || bodyB.isSensor,
|
||||
timeCreated: timestamp,
|
||||
timeUpdated: timestamp,
|
||||
collision: null,
|
||||
inverseMass: 0,
|
||||
friction: 0,
|
||||
frictionStatic: 0,
|
||||
restitution: 0,
|
||||
slop: 0
|
||||
inverseMass: parentA.inverseMass + parentB.inverseMass,
|
||||
friction: Math.min(parentA.friction, parentB.friction),
|
||||
frictionStatic: Math.max(parentA.frictionStatic, parentB.frictionStatic),
|
||||
restitution: Math.max(parentA.restitution, parentB.restitution),
|
||||
slop: Math.max(parentA.slop, parentB.slop)
|
||||
};
|
||||
|
||||
Pair.update(pair, collision, timestamp);
|
||||
|
@ -53,28 +57,31 @@ module.exports = Pair;
|
|||
* @param {number} timestamp
|
||||
*/
|
||||
Pair.update = function(pair, collision, timestamp) {
|
||||
var contacts = pair.contacts,
|
||||
supports = collision.supports,
|
||||
activeContacts = pair.activeContacts,
|
||||
parentA = collision.parentA,
|
||||
parentB = collision.parentB;
|
||||
|
||||
pair.collision = collision;
|
||||
|
||||
pair.inverseMass = parentA.inverseMass + parentB.inverseMass;
|
||||
pair.friction = Math.min(parentA.friction, parentB.friction);
|
||||
pair.frictionStatic = Math.max(parentA.frictionStatic, parentB.frictionStatic);
|
||||
pair.restitution = Math.max(parentA.restitution, parentB.restitution);
|
||||
pair.slop = Math.max(parentA.slop, parentB.slop);
|
||||
activeContacts.length = 0;
|
||||
|
||||
if (collision.collided) {
|
||||
var supports = collision.supports,
|
||||
activeContacts = pair.activeContacts,
|
||||
parentA = collision.parentA,
|
||||
parentB = collision.parentB;
|
||||
|
||||
pair.inverseMass = parentA.inverseMass + parentB.inverseMass;
|
||||
pair.friction = Math.min(parentA.friction, parentB.friction);
|
||||
pair.frictionStatic = Math.max(parentA.frictionStatic, parentB.frictionStatic);
|
||||
pair.restitution = Math.max(parentA.restitution, parentB.restitution);
|
||||
pair.slop = Math.max(parentA.slop, parentB.slop);
|
||||
|
||||
for (var i = 0; i < supports.length; i++) {
|
||||
activeContacts[i] = supports[i].contact;
|
||||
}
|
||||
var support = supports[i],
|
||||
contactId = Contact.id(support),
|
||||
contact = contacts[contactId];
|
||||
|
||||
// optimise array size
|
||||
var supportCount = supports.length;
|
||||
if (supportCount < activeContacts.length) {
|
||||
activeContacts.length = supportCount;
|
||||
if (contact) {
|
||||
activeContacts.push(contact);
|
||||
} else {
|
||||
activeContacts.push(contacts[contactId] = Contact.create(support));
|
||||
}
|
||||
}
|
||||
|
||||
pair.separation = collision.depth;
|
||||
|
@ -117,4 +124,4 @@ module.exports = Pair;
|
|||
}
|
||||
};
|
||||
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -48,34 +48,24 @@ var Bounds = require('../geometry/Bounds');
|
|||
* Find a solution for pair positions.
|
||||
* @method solvePosition
|
||||
* @param {pair[]} pairs
|
||||
* @param {body[]} bodies
|
||||
* @param {number} delta
|
||||
*/
|
||||
Resolver.solvePosition = function(pairs, bodies, delta) {
|
||||
Resolver.solvePosition = function(pairs, delta) {
|
||||
var i,
|
||||
normalX,
|
||||
normalY,
|
||||
pair,
|
||||
collision,
|
||||
bodyA,
|
||||
bodyB,
|
||||
normal,
|
||||
separation,
|
||||
penetration,
|
||||
positionImpulseA,
|
||||
positionImpulseB,
|
||||
bodyBtoA,
|
||||
contactShare,
|
||||
bodyBtoAX,
|
||||
bodyBtoAY,
|
||||
positionImpulse,
|
||||
timeScale = delta / Common._timeUnit,
|
||||
damping = Common.clamp(Resolver._positionDampen * timeScale, 0, 1);
|
||||
|
||||
for (i = 0; i < bodies.length; i++) {
|
||||
var body = bodies[i];
|
||||
body.previousPositionImpulse.x = body.positionImpulse.x;
|
||||
body.previousPositionImpulse.y = body.positionImpulse.y;
|
||||
}
|
||||
damping = Common.clamp(Resolver._positionDampen * timeScale, 0, 1),
|
||||
tempA = Vector._temp[0],
|
||||
tempB = Vector._temp[1],
|
||||
tempC = Vector._temp[2],
|
||||
tempD = Vector._temp[3];
|
||||
|
||||
// find impulses required to resolve penetration
|
||||
for (i = 0; i < pairs.length; i++) {
|
||||
|
@ -89,35 +79,39 @@ var Bounds = require('../geometry/Bounds');
|
|||
bodyB = collision.parentB;
|
||||
normal = collision.normal;
|
||||
|
||||
positionImpulseA = bodyA.previousPositionImpulse;
|
||||
positionImpulseB = bodyB.previousPositionImpulse;
|
||||
// get current separation between body edges involved in collision
|
||||
bodyBtoA = Vector.sub(Vector.add(bodyB.positionImpulse, bodyB.position, tempA),
|
||||
Vector.add(bodyA.positionImpulse,
|
||||
Vector.sub(bodyB.position, collision.penetration, tempB), tempC), tempD);
|
||||
|
||||
penetration = collision.penetration;
|
||||
pair.separation = Vector.dot(normal, bodyBtoA);
|
||||
}
|
||||
|
||||
for (i = 0; i < pairs.length; i++) {
|
||||
pair = pairs[i];
|
||||
|
||||
bodyBtoAX = positionImpulseB.x - positionImpulseA.x + penetration.x;
|
||||
bodyBtoAY = positionImpulseB.y - positionImpulseA.y + penetration.y;
|
||||
|
||||
normalX = normal.x;
|
||||
normalY = normal.y;
|
||||
|
||||
separation = normalX * bodyBtoAX + normalY * bodyBtoAY;
|
||||
pair.separation = separation;
|
||||
|
||||
positionImpulse = (separation - pair.slop) * damping;
|
||||
if (!pair.isActive || pair.isSensor)
|
||||
continue;
|
||||
|
||||
collision = pair.collision;
|
||||
bodyA = collision.parentA;
|
||||
bodyB = collision.parentB;
|
||||
normal = collision.normal;
|
||||
positionImpulse = (pair.separation - pair.slop) * damping;
|
||||
|
||||
if (bodyA.isStatic || bodyB.isStatic)
|
||||
positionImpulse *= 2;
|
||||
|
||||
if (!(bodyA.isStatic || bodyA.isSleeping)) {
|
||||
contactShare = positionImpulse / bodyA.totalContacts;
|
||||
bodyA.positionImpulse.x += normalX * contactShare;
|
||||
bodyA.positionImpulse.y += normalY * contactShare;
|
||||
contactShare = Resolver._positionDampen / bodyA.totalContacts;
|
||||
bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
|
||||
bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare;
|
||||
}
|
||||
|
||||
if (!(bodyB.isStatic || bodyB.isSleeping)) {
|
||||
contactShare = positionImpulse / bodyB.totalContacts;
|
||||
bodyB.positionImpulse.x -= normalX * contactShare;
|
||||
bodyB.positionImpulse.y -= normalY * contactShare;
|
||||
contactShare = Resolver._positionDampen / bodyB.totalContacts;
|
||||
bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare;
|
||||
bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -350,4 +344,4 @@ var Bounds = require('../geometry/Bounds');
|
|||
}
|
||||
};
|
||||
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -304,6 +304,32 @@ var Common = require('../core/Common');
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the world-space position of `constraint.pointA`, accounting for `constraint.bodyA`.
|
||||
* @method pointAWorld
|
||||
* @param {constraint} constraint
|
||||
* @returns {vector} the world-space position
|
||||
*/
|
||||
Constraint.pointAWorld = function(constraint) {
|
||||
return {
|
||||
x: (constraint.bodyA ? constraint.bodyA.position.x : 0) + constraint.pointA.x,
|
||||
y: (constraint.bodyA ? constraint.bodyA.position.y : 0) + constraint.pointA.y
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the world-space position of `constraint.pointB`, accounting for `constraint.bodyB`.
|
||||
* @method pointBWorld
|
||||
* @param {constraint} constraint
|
||||
* @returns {vector} the world-space position
|
||||
*/
|
||||
Constraint.pointBWorld = function(constraint) {
|
||||
return {
|
||||
x: (constraint.bodyB ? constraint.bodyB.position.x : 0) + constraint.pointB.x,
|
||||
y: (constraint.bodyB ? constraint.bodyB.position.y : 0) + constraint.pointB.y
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* Properties Documentation
|
||||
|
|
|
@ -258,7 +258,7 @@ module.exports = Common;
|
|||
* @return {number} the current timestamp
|
||||
*/
|
||||
Common.now = function() {
|
||||
if (window.performance) {
|
||||
if (typeof window !== 'undefined' && window.performance) {
|
||||
if (window.performance.now) {
|
||||
return window.performance.now();
|
||||
} else if (window.performance.webkitNow) {
|
||||
|
@ -536,19 +536,4 @@ module.exports = Common;
|
|||
func
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to require external libraries outside of the bundle.
|
||||
* It first looks for the `globalName` on the environment's global namespace.
|
||||
* If the global is not found, it will fall back to using the standard `require` using the `moduleName`.
|
||||
* @private
|
||||
* @method _requireGlobal
|
||||
* @param {string} globalName The global module name
|
||||
* @param {string} moduleName The fallback CommonJS module name
|
||||
* @return {} The loaded module
|
||||
*/
|
||||
Common._requireGlobal = function(globalName, moduleName) {
|
||||
var obj = (typeof window !== 'undefined' ? window[globalName] : typeof global !== 'undefined' ? global[globalName] : null);
|
||||
return obj || require(moduleName);
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -191,7 +191,7 @@ var Body = require('../body/Body');
|
|||
// iteratively resolve position between collisions
|
||||
Resolver.preSolvePosition(pairs.list);
|
||||
for (i = 0; i < engine.positionIterations; i++) {
|
||||
Resolver.solvePosition(pairs.list, allBodies, delta);
|
||||
Resolver.solvePosition(pairs.list, delta);
|
||||
}
|
||||
Resolver.postSolvePosition(allBodies);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ var Common = require('./Common');
|
|||
* @readOnly
|
||||
* @type {String}
|
||||
*/
|
||||
Matter.version = '@@VERSION@@';
|
||||
Matter.version = typeof __MATTER_VERSION__ !== 'undefined' ? __MATTER_VERSION__ : '*';
|
||||
|
||||
/**
|
||||
* A list of plugin dependencies to be installed. These are normally set and installed through `Matter.use`.
|
||||
|
|
|
@ -18,7 +18,6 @@ var Common = require('../core/Common');
|
|||
var Body = require('../body/Body');
|
||||
var Bounds = require('../geometry/Bounds');
|
||||
var Vector = require('../geometry/Vector');
|
||||
var decomp;
|
||||
|
||||
(function() {
|
||||
|
||||
|
@ -46,7 +45,7 @@ var decomp;
|
|||
if (options.chamfer) {
|
||||
var chamfer = options.chamfer;
|
||||
rectangle.vertices = Vertices.chamfer(rectangle.vertices, chamfer.radius,
|
||||
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax);
|
||||
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax);
|
||||
delete options.chamfer;
|
||||
}
|
||||
|
||||
|
@ -92,7 +91,7 @@ var decomp;
|
|||
if (options.chamfer) {
|
||||
var chamfer = options.chamfer;
|
||||
trapezoid.vertices = Vertices.chamfer(trapezoid.vertices, chamfer.radius,
|
||||
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax);
|
||||
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax);
|
||||
delete options.chamfer;
|
||||
}
|
||||
|
||||
|
@ -169,7 +168,7 @@ var decomp;
|
|||
if (options.chamfer) {
|
||||
var chamfer = options.chamfer;
|
||||
polygon.vertices = Vertices.chamfer(polygon.vertices, chamfer.radius,
|
||||
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax);
|
||||
chamfer.quality, chamfer.qualityMin, chamfer.qualityMax);
|
||||
delete options.chamfer;
|
||||
}
|
||||
|
||||
|
@ -197,11 +196,8 @@ var decomp;
|
|||
* @return {body}
|
||||
*/
|
||||
Bodies.fromVertices = function(x, y, vertexSets, options, flagInternal, removeCollinear, minimumArea) {
|
||||
if (!decomp) {
|
||||
decomp = Common._requireGlobal('decomp', 'poly-decomp');
|
||||
}
|
||||
|
||||
var body,
|
||||
var decomp = global.decomp || require('poly-decomp'),
|
||||
body,
|
||||
parts,
|
||||
isConvex,
|
||||
vertices,
|
||||
|
|
|
@ -217,7 +217,7 @@ var Bodies = require('./Bodies');
|
|||
for (var i = 0; i < number; i++) {
|
||||
var separation = 1.9,
|
||||
circle = Bodies.circle(xx + i * (size * separation), yy + length, size,
|
||||
{ inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0.0001, slop: 1 }),
|
||||
{ inertia: Infinity, restitution: 1, friction: 0, frictionAir: 0.0001, slop: 1 }),
|
||||
constraint = Constraint.create({ pointA: { x: xx + i * (size * separation), y: yy }, bodyB: circle });
|
||||
|
||||
Composite.addBody(newtonsCradle, circle);
|
||||
|
|
|
@ -44,16 +44,9 @@ var Common = require('../core/Common');
|
|||
y: point.y,
|
||||
index: i,
|
||||
body: body,
|
||||
isInternal: false,
|
||||
contact: null
|
||||
isInternal: false
|
||||
};
|
||||
|
||||
vertex.contact = {
|
||||
vertex: vertex,
|
||||
normalImpulse: 0,
|
||||
tangentImpulse: 0
|
||||
};
|
||||
|
||||
vertices.push(vertex);
|
||||
}
|
||||
|
||||
|
@ -70,7 +63,7 @@ var Common = require('../core/Common');
|
|||
* @return {vertices} vertices
|
||||
*/
|
||||
Vertices.fromPath = function(path, body) {
|
||||
var pathPattern = /L?\s*([\-\d\.e]+)[\s,]*([\-\d\.e]+)*/ig,
|
||||
var pathPattern = /L?\s*([-\d.e]+)[\s,]*([-\d.e]+)*/ig,
|
||||
points = [];
|
||||
|
||||
path.replace(pathPattern, function(match, x, y) {
|
||||
|
@ -451,4 +444,4 @@ var Common = require('../core/Common');
|
|||
return upper.concat(lower);
|
||||
};
|
||||
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -4,6 +4,7 @@ Matter.Body = require('../body/Body');
|
|||
Matter.Composite = require('../body/Composite');
|
||||
Matter.World = require('../body/World');
|
||||
|
||||
Matter.Contact = require('../collision/Contact');
|
||||
Matter.Detector = require('../collision/Detector');
|
||||
Matter.Grid = require('../collision/Grid');
|
||||
Matter.Pairs = require('../collision/Pairs');
|
||||
|
|
|
@ -927,7 +927,7 @@ var Mouse = require('../core/Mouse');
|
|||
// render a single axis indicator
|
||||
c.moveTo(part.position.x, part.position.y);
|
||||
c.lineTo((part.vertices[0].x + part.vertices[part.vertices.length-1].x) / 2,
|
||||
(part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2);
|
||||
(part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1227,9 +1227,9 @@ var Mouse = require('../core/Mouse');
|
|||
|
||||
var region = bucketId.split(/C|R/);
|
||||
c.rect(0.5 + parseInt(region[1], 10) * grid.bucketWidth,
|
||||
0.5 + parseInt(region[2], 10) * grid.bucketHeight,
|
||||
grid.bucketWidth,
|
||||
grid.bucketHeight);
|
||||
0.5 + parseInt(region[2], 10) * grid.bucketHeight,
|
||||
grid.bucketWidth,
|
||||
grid.bucketHeight);
|
||||
}
|
||||
|
||||
c.lineWidth = 1;
|
||||
|
@ -1276,7 +1276,7 @@ var Mouse = require('../core/Mouse');
|
|||
bounds = item.bounds;
|
||||
context.beginPath();
|
||||
context.rect(Math.floor(bounds.min.x - 3), Math.floor(bounds.min.y - 3),
|
||||
Math.floor(bounds.max.x - bounds.min.x + 6), Math.floor(bounds.max.y - bounds.min.y + 6));
|
||||
Math.floor(bounds.max.x - bounds.min.x + 6), Math.floor(bounds.max.y - bounds.min.y + 6));
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
|
||||
|
@ -1310,7 +1310,7 @@ var Mouse = require('../core/Mouse');
|
|||
bounds = inspector.selectBounds;
|
||||
context.beginPath();
|
||||
context.rect(Math.floor(bounds.min.x), Math.floor(bounds.min.y),
|
||||
Math.floor(bounds.max.x - bounds.min.x), Math.floor(bounds.max.y - bounds.min.y));
|
||||
Math.floor(bounds.max.x - bounds.min.x), Math.floor(bounds.max.y - bounds.min.y));
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
context.fill();
|
||||
|
|
|
@ -485,7 +485,7 @@ var Vector = require('../geometry/Vector');
|
|||
|
||||
primitive.moveTo(part.position.x - body.position.x, part.position.y - body.position.y);
|
||||
primitive.lineTo(((part.vertices[0].x + part.vertices[part.vertices.length-1].x) / 2 - body.position.x),
|
||||
((part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2 - body.position.y));
|
||||
((part.vertices[0].y + part.vertices[part.vertices.length-1].y) / 2 - body.position.y));
|
||||
|
||||
primitive.endFill();
|
||||
}
|
||||
|
|
93
test/ExampleWorker.js
Normal file
93
test/ExampleWorker.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* eslint-env es6 */
|
||||
/* eslint no-global-assign: 0 */
|
||||
"use strict";
|
||||
|
||||
const stubBrowserFeatures = M => {
|
||||
const noop = () => ({ collisionFilter: {}, mouse: {} });
|
||||
M.Common._requireGlobal = name => global[name];
|
||||
M.Render.create = () => ({ options: {}, bounds: { min: { x: 0, y: 0 }, max: { x: 800, y: 600 }}});
|
||||
M.Render.run = M.Render.lookAt = noop;
|
||||
M.Runner.create = M.Runner.run = noop;
|
||||
M.MouseConstraint.create = M.Mouse.create = noop;
|
||||
M.Common.log = M.Common.info = M.Common.warn = noop;
|
||||
return M;
|
||||
};
|
||||
|
||||
const reset = M => {
|
||||
M.Common._nextId = M.Common._seed = 0;
|
||||
M.Body._nextCollidingGroupId = 1;
|
||||
M.Body._nextNonCollidingGroupId = -1;
|
||||
M.Body._nextCategory = 0x0001;
|
||||
};
|
||||
|
||||
const { engineCapture } = require('./TestTools');
|
||||
const MatterDev = stubBrowserFeatures(require('../src/module/main'));
|
||||
const MatterBuild = stubBrowserFeatures(require('../build/matter'));
|
||||
const Example = require('../examples/index');
|
||||
const decomp = require('../demo/lib/decomp');
|
||||
|
||||
const runExample = options => {
|
||||
const Matter = options.useDev ? MatterDev : MatterBuild;
|
||||
const consoleOriginal = global.console;
|
||||
|
||||
global.console = { log: () => {} };
|
||||
global.document = global.window = { addEventListener: () => {} };
|
||||
global.decomp = decomp;
|
||||
global.Matter = Matter;
|
||||
|
||||
reset(Matter);
|
||||
|
||||
const example = Example[options.name]();
|
||||
const engine = example.engine;
|
||||
|
||||
let totalDuration = 0;
|
||||
let overlapTotal = 0;
|
||||
let overlapCount = 0;
|
||||
|
||||
const bodies = Matter.Composite.allBodies(engine.world);
|
||||
|
||||
if (options.jitter) {
|
||||
for (let i = 0; i < bodies.length; i += 1) {
|
||||
const body = bodies[i];
|
||||
|
||||
Matter.Body.applyForce(body, body.position, {
|
||||
x: Math.cos(i * i) * options.jitter * body.mass,
|
||||
y: Math.sin(i * i) * options.jitter * body.mass
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < options.totalUpdates; i += 1) {
|
||||
const startTime = process.hrtime();
|
||||
|
||||
Matter.Engine.update(engine, 1000 / 60);
|
||||
|
||||
const duration = process.hrtime(startTime);
|
||||
totalDuration += duration[0] * 1e9 + duration[1];
|
||||
|
||||
for (let p = 0; p < engine.pairs.list.length; p += 1) {
|
||||
const pair = engine.pairs.list[p];
|
||||
const separation = pair.separation - pair.slop;
|
||||
|
||||
if (pair.isActive && !pair.isSensor) {
|
||||
overlapTotal += separation > 0 ? separation : 0;
|
||||
overlapCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global.console = consoleOriginal;
|
||||
global.window = undefined;
|
||||
global.document = undefined;
|
||||
global.decomp = undefined;
|
||||
global.Matter = undefined;
|
||||
|
||||
return {
|
||||
name: options.name,
|
||||
duration: totalDuration,
|
||||
overlap: overlapTotal / (overlapCount || 1),
|
||||
...engineCapture(engine)
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = { runExample };
|
74
test/Examples.spec.js
Normal file
74
test/Examples.spec.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
/* eslint-env es6 */
|
||||
"use strict";
|
||||
|
||||
jest.setTimeout(30 * 1000);
|
||||
|
||||
const { comparisonReport, toMatchExtrinsics, toMatchIntrinsics } = require('./TestTools');
|
||||
|
||||
const Example = require('../examples/index');
|
||||
const MatterBuild = require('../build/matter');
|
||||
const Worker = require('jest-worker').default;
|
||||
|
||||
const testComparison = process.env.COMPARE === 'true';
|
||||
const saveComparison = process.env.SAVE === 'true';
|
||||
const excludeExamples = ['svg', 'terrain'];
|
||||
const excludeJitter = ['stack', 'circleStack', 'restitution', 'staticFriction', 'friction', 'newtonsCradle', 'catapult'];
|
||||
const examples = Object.keys(Example).filter(key => !excludeExamples.includes(key));
|
||||
|
||||
const runExamples = async useDev => {
|
||||
const worker = new Worker(require.resolve('./ExampleWorker'), {
|
||||
enableWorkerThreads: true
|
||||
});
|
||||
|
||||
const result = await Promise.all(examples.map(name => worker.runExample({
|
||||
name,
|
||||
useDev,
|
||||
totalUpdates: 120,
|
||||
jitter: excludeJitter.includes(name) ? 0 : 1e-10
|
||||
})));
|
||||
|
||||
await worker.end();
|
||||
|
||||
return result.reduce((out, capture) => (out[capture.name] = capture, out), {});
|
||||
};
|
||||
|
||||
const capturesDev = runExamples(true);
|
||||
const capturesBuild = runExamples(false);
|
||||
|
||||
afterAll(async () => {
|
||||
// Report experimental capture comparison.
|
||||
const dev = await capturesDev;
|
||||
const build = await capturesBuild;
|
||||
console.log(comparisonReport(dev, build, MatterBuild.version, saveComparison));
|
||||
});
|
||||
|
||||
describe(`Integration checks (${examples.length})`, () => {
|
||||
test(`Examples run without throwing`, async () => {
|
||||
const dev = await capturesDev;
|
||||
const build = await capturesBuild;
|
||||
expect(Object.keys(dev)).toEqual(examples);
|
||||
expect(Object.keys(build)).toEqual(examples);
|
||||
});
|
||||
});
|
||||
|
||||
// Experimental regression comparison checks.
|
||||
if (testComparison) {
|
||||
describe(`Regression checks (${examples.length})`, () => {
|
||||
expect.extend(toMatchExtrinsics);
|
||||
expect.extend(toMatchIntrinsics);
|
||||
|
||||
test(`Examples match intrinsic properties with release build`, async () => {
|
||||
const dev = await capturesDev;
|
||||
const build = await capturesBuild;
|
||||
// compare mass, inertia, friction etc.
|
||||
expect(dev).toMatchIntrinsics(build);
|
||||
});
|
||||
|
||||
test(`Examples match extrinsic positions and velocities with release build`, async () => {
|
||||
const dev = await capturesDev;
|
||||
const build = await capturesBuild;
|
||||
// compare position, linear and angular velocity
|
||||
expect(dev).toMatchExtrinsics(build);
|
||||
});
|
||||
});
|
||||
}
|
278
test/TestTools.js
Normal file
278
test/TestTools.js
Normal file
|
@ -0,0 +1,278 @@
|
|||
/* eslint-env es6 */
|
||||
"use strict";
|
||||
|
||||
const fs = require('fs');
|
||||
const compactStringify = require('json-stringify-pretty-compact');
|
||||
const { Composite, Constraint } = require('../src/module/main');
|
||||
|
||||
const comparePath = './test/__compare__';
|
||||
const compareCommand = 'open http://localhost:8000/?compare';
|
||||
const diffSaveCommand = 'npm run test-save';
|
||||
const diffCommand = 'code -n -d test/__compare__/examples-build.json test/__compare__/examples-dev.json';
|
||||
const equalityThreshold = 0.99999;
|
||||
|
||||
const intrinsicProps = [
|
||||
// Common
|
||||
'id', 'label',
|
||||
|
||||
// Constraint
|
||||
'angularStiffness', 'bodyA', 'bodyB', 'damping', 'length', 'stiffness',
|
||||
|
||||
// Body
|
||||
'area', 'axes', 'collisionFilter', 'category', 'mask',
|
||||
'group', 'density', 'friction', 'frictionAir', 'frictionStatic', 'inertia', 'inverseInertia', 'inverseMass', 'isSensor',
|
||||
'isSleeping', 'isStatic', 'mass', 'parent', 'parts', 'restitution', 'sleepThreshold', 'slop',
|
||||
'timeScale', 'vertices',
|
||||
|
||||
// Composite
|
||||
'bodies', 'constraints', 'composites'
|
||||
];
|
||||
|
||||
const colors = { Red: 31, Green: 32, Yellow: 33, White: 37, BrightWhite: 90, BrightCyan: 36 };
|
||||
const color = (text, number) => number ? `\x1b[${number}m${text}\x1b[0m` : text;
|
||||
const limit = (val, precision=3) => parseFloat(val.toPrecision(precision));
|
||||
const toPercent = val => (100 * val).toPrecision(3);
|
||||
|
||||
const engineCapture = (engine) => ({
|
||||
timestamp: limit(engine.timing.timestamp),
|
||||
extrinsic: worldCaptureExtrinsic(engine.world),
|
||||
intrinsic: worldCaptureIntrinsic(engine.world)
|
||||
});
|
||||
|
||||
const worldCaptureExtrinsic = world => ({
|
||||
bodies: Composite.allBodies(world).reduce((bodies, body) => {
|
||||
bodies[body.id] = [
|
||||
body.position.x,
|
||||
body.position.y,
|
||||
body.positionPrev.x,
|
||||
body.positionPrev.y,
|
||||
body.angle,
|
||||
body.anglePrev,
|
||||
...body.vertices.reduce((flat, vertex) => (flat.push(vertex.x, vertex.y), flat), [])
|
||||
];
|
||||
|
||||
return bodies;
|
||||
}, {}),
|
||||
constraints: Composite.allConstraints(world).reduce((constraints, constraint) => {
|
||||
const positionA = Constraint.pointAWorld(constraint);
|
||||
const positionB = Constraint.pointBWorld(constraint);
|
||||
|
||||
constraints[constraint.id] = [
|
||||
positionA.x,
|
||||
positionA.y,
|
||||
positionB.x,
|
||||
positionB.y
|
||||
];
|
||||
|
||||
return constraints;
|
||||
}, {})
|
||||
});
|
||||
|
||||
const worldCaptureIntrinsic = world => worldCaptureIntrinsicBase({
|
||||
bodies: Composite.allBodies(world).reduce((bodies, body) => {
|
||||
bodies[body.id] = body;
|
||||
return bodies;
|
||||
}, {}),
|
||||
constraints: Composite.allConstraints(world).reduce((constraints, constraint) => {
|
||||
constraints[constraint.id] = constraint;
|
||||
return constraints;
|
||||
}, {}),
|
||||
composites: Composite.allComposites(world).reduce((composites, composite) => {
|
||||
composites[composite.id] = {
|
||||
bodies: Composite.allBodies(composite).map(body => body.id),
|
||||
constraints: Composite.allConstraints(composite).map(constraint => constraint.id),
|
||||
composites: Composite.allComposites(composite).map(composite => composite.id)
|
||||
};
|
||||
return composites;
|
||||
}, {})
|
||||
});
|
||||
|
||||
const worldCaptureIntrinsicBase = (obj, depth=0) => {
|
||||
if (obj === Infinity) {
|
||||
return 'Infinity';
|
||||
} else if (typeof obj === 'number') {
|
||||
return limit(obj);
|
||||
} else if (Array.isArray(obj)) {
|
||||
return obj.map(item => worldCaptureIntrinsicBase(item, depth + 1));
|
||||
} else if (typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const result = Object.entries(obj)
|
||||
.filter(([key]) => depth <= 1 || intrinsicProps.includes(key))
|
||||
.reduce((cleaned, [key, val]) => {
|
||||
if (val && val.id && String(val.id) !== key) {
|
||||
val = val.id;
|
||||
}
|
||||
|
||||
if (Array.isArray(val) && !['composites', 'constraints', 'bodies'].includes(key)) {
|
||||
val = `[${val.length}]`;
|
||||
}
|
||||
|
||||
cleaned[key] = worldCaptureIntrinsicBase(val, depth + 1);
|
||||
return cleaned;
|
||||
}, {});
|
||||
|
||||
return Object.keys(result).sort()
|
||||
.reduce((sorted, key) => (sorted[key] = result[key], sorted), {});
|
||||
};
|
||||
|
||||
const similarity = (a, b) => {
|
||||
const distance = Math.sqrt(a.reduce(
|
||||
(sum, _val, i) => sum + Math.pow((a[i] || 0) - (b[i] || 0), 2), 0)
|
||||
);
|
||||
return 1 / (1 + (distance / a.length));
|
||||
};
|
||||
|
||||
const captureSimilarityExtrinsic = (currentCaptures, referenceCaptures) => {
|
||||
const result = {};
|
||||
|
||||
Object.entries(currentCaptures).forEach(([name, current]) => {
|
||||
const reference = referenceCaptures[name];
|
||||
const worldVector = [];
|
||||
const worldVectorRef = [];
|
||||
|
||||
Object.keys(current.extrinsic).forEach(objectType => {
|
||||
Object.keys(current.extrinsic[objectType]).forEach(objectId => {
|
||||
worldVector.push(...current.extrinsic[objectType][objectId]);
|
||||
worldVectorRef.push(...reference.extrinsic[objectType][objectId]);
|
||||
});
|
||||
});
|
||||
|
||||
result[name] = similarity(worldVector, worldVectorRef);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const writeCaptures = (name, obj) => {
|
||||
try {
|
||||
fs.mkdirSync(comparePath, { recursive: true });
|
||||
} catch (err) {
|
||||
if (err.code !== 'EEXIST') throw err;
|
||||
}
|
||||
fs.writeFileSync(`${comparePath}/${name}.json`, compactStringify(obj, { maxLength: 100 }), 'utf8');
|
||||
};
|
||||
|
||||
const toMatchExtrinsics = {
|
||||
toMatchExtrinsics(received, value) {
|
||||
const similaritys = captureSimilarityExtrinsic(received, value);
|
||||
const pass = Object.values(similaritys).every(similarity => similarity >= equalityThreshold);
|
||||
|
||||
return {
|
||||
message: () => 'Expected positions and velocities to match between builds.',
|
||||
pass
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const toMatchIntrinsics = {
|
||||
toMatchIntrinsics(currentCaptures, referenceCaptures) {
|
||||
const entries = Object.entries(currentCaptures);
|
||||
let changed = false;
|
||||
|
||||
entries.forEach(([name, current]) => {
|
||||
const reference = referenceCaptures[name];
|
||||
if (!this.equals(current.intrinsic, reference.intrinsic)) {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
message: () => 'Expected intrinsic properties to match between builds.',
|
||||
pass: !changed
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const similarityRatings = similarity => similarity < equalityThreshold ? color('●', colors.Yellow) : '·';
|
||||
const changeRatings = isChanged => isChanged ? color('◆', colors.White) : '·';
|
||||
|
||||
const equals = (a, b) => {
|
||||
try {
|
||||
expect(a).toEqual(b);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const comparisonReport = (capturesDev, capturesBuild, buildVersion, save) => {
|
||||
const similaritys = captureSimilarityExtrinsic(capturesDev, capturesBuild);
|
||||
const similarityEntries = Object.entries(similaritys);
|
||||
const devIntrinsicsChanged = {};
|
||||
const buildIntrinsicsChanged = {};
|
||||
let intrinsicChangeCount = 0;
|
||||
let totalTimeBuild = 0;
|
||||
let totalTimeDev = 0;
|
||||
let totalOverlapBuild = 0;
|
||||
let totalOverlapDev = 0;
|
||||
|
||||
const capturePerformance = Object.entries(capturesDev).map(([name]) => {
|
||||
totalTimeBuild += capturesBuild[name].duration;
|
||||
totalTimeDev += capturesDev[name].duration;
|
||||
totalOverlapBuild += capturesBuild[name].overlap;
|
||||
totalOverlapDev += capturesDev[name].overlap;
|
||||
|
||||
const changedIntrinsics = !equals(capturesDev[name].intrinsic, capturesBuild[name].intrinsic);
|
||||
if (changedIntrinsics) {
|
||||
capturesDev[name].changedIntrinsics = true;
|
||||
if (intrinsicChangeCount < 2) {
|
||||
devIntrinsicsChanged[name] = capturesDev[name].intrinsic;
|
||||
buildIntrinsicsChanged[name] = capturesBuild[name].intrinsic;
|
||||
intrinsicChangeCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return { name };
|
||||
});
|
||||
|
||||
capturePerformance.sort((a, b) => a.name.localeCompare(b.name));
|
||||
similarityEntries.sort((a, b) => a[1] - b[1]);
|
||||
|
||||
let perfChange = 1 - (totalTimeDev / totalTimeBuild);
|
||||
perfChange = perfChange < -0.05 || perfChange > 0.05 ? perfChange : 0;
|
||||
|
||||
let similarityAvg = 0;
|
||||
similarityEntries.forEach(([_, similarity]) => {
|
||||
similarityAvg += similarity;
|
||||
});
|
||||
|
||||
similarityAvg /= similarityEntries.length;
|
||||
|
||||
const overlapChange = (totalOverlapDev / (totalOverlapBuild || 1)) - 1;
|
||||
|
||||
if (save) {
|
||||
writeCaptures('examples-dev', devIntrinsicsChanged);
|
||||
writeCaptures('examples-build', buildIntrinsicsChanged);
|
||||
}
|
||||
|
||||
return [
|
||||
[`Output comparison of ${similarityEntries.length}`,
|
||||
`examples against ${color('matter-js@' + buildVersion, colors.Yellow)} build on last run`
|
||||
].join(' '),
|
||||
`\n\n${color('Similarity', colors.White)}`,
|
||||
`${color(toPercent(similarityAvg), similarityAvg === 1 ? colors.Green : colors.Yellow)}%`,
|
||||
`${color('Performance', colors.White)}`,
|
||||
`${color((perfChange >= 0 ? '+' : '') + toPercent(perfChange), perfChange >= 0 ? colors.Green : colors.Red)}%`,
|
||||
`${color('Overlap', colors.White)}`,
|
||||
`${color((overlapChange >= 0 ? '+' : '') + toPercent(overlapChange), overlapChange > 0 ? colors.Red : colors.Green)}%`,
|
||||
capturePerformance.reduce((output, p, i) => {
|
||||
output += `${p.name} `;
|
||||
output += `${similarityRatings(similaritys[p.name])} `;
|
||||
output += `${changeRatings(capturesDev[p.name].changedIntrinsics)} `;
|
||||
if (i > 0 && i < capturePerformance.length && i % 5 === 0) {
|
||||
output += '\n';
|
||||
}
|
||||
return output;
|
||||
}, '\n\n'),
|
||||
`\nwhere · no change ● extrinsics changed ◆ intrinsics changed\n`,
|
||||
similarityAvg < 1 ? `\n${color('▶', colors.White)} ${color(compareCommand + '=' + 120 + '#' + similarityEntries[0][0], colors.BrightCyan)}` : '',
|
||||
intrinsicChangeCount > 0 ? `\n${color('▶', colors.White)} ${color((save ? diffCommand : diffSaveCommand), colors.BrightCyan)}` : ''
|
||||
].join(' ');
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
engineCapture, comparisonReport,
|
||||
toMatchExtrinsics, toMatchIntrinsics
|
||||
};
|
|
@ -1,206 +0,0 @@
|
|||
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/index.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) {
|
||||
if (!(demo in Matter.Example)) {
|
||||
throw '\'' + demo + '\' is not defined in Matter.Demo';
|
||||
}
|
||||
|
||||
var _demo = Matter.Demo.create(),
|
||||
engine = Matter.Example.engine(_demo),
|
||||
runner = Matter.Runner.create();
|
||||
|
||||
Matter.Demo._demo = _demo;
|
||||
_demo.engine = engine;
|
||||
_demo.engine.render = {};
|
||||
_demo.engine.render.options = {};
|
||||
_demo.runner = runner;
|
||||
_demo.render = { options: {} };
|
||||
_demo.mouseConstraint = Matter.MouseConstraint.create(engine);
|
||||
|
||||
Matter.Demo.reset(_demo);
|
||||
Matter.Example[demo](_demo);
|
||||
|
||||
return engine.world;
|
||||
}, demo);
|
||||
|
||||
var worldEnd = page.evaluate(function(demo, frames) {
|
||||
var engine = Matter.Demo._demo.engine,
|
||||
runner = Matter.Demo._demo.runner;
|
||||
|
||||
for (var j = 0; j <= frames; j += 1) {
|
||||
Matter.Runner.tick(runner, engine, j * runner.delta);
|
||||
}
|
||||
|
||||
return engine.world;
|
||||
}, demo, frames);
|
||||
|
||||
worldEnd = JSON.parse(resurrect.stringify(worldEnd, precisionLimiter));
|
||||
worldStart = JSON.parse(resurrect.stringify(worldStart, precisionLimiter));
|
||||
|
||||
if (fs.exists(worldStartPath)) {
|
||||
var worldStartRef = JSON.parse(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, JSON.stringify(worldStart, precisionLimiter, 2), 'w');
|
||||
} else {
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasCreated = true;
|
||||
fs.write(worldStartPath, JSON.stringify(worldStart, precisionLimiter, 2), 'w');
|
||||
}
|
||||
|
||||
if (fs.exists(worldEndPath)) {
|
||||
var worldEndRef = JSON.parse(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, JSON.stringify(worldEnd, precisionLimiter, 2), 'w');
|
||||
} else {
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasCreated = true;
|
||||
fs.write(worldEndPath, JSON.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(' at ' + (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);
|
|
@ -1,542 +0,0 @@
|
|||
/**
|
||||
* # 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;
|
||||
};
|
|
@ -1,185 +0,0 @@
|
|||
var fs = require('fs');
|
||||
var mkdirp = require('mkdirp').sync;
|
||||
var removeDir = require('rimraf').sync;
|
||||
var Resurrect = require('../lib/resurrect');
|
||||
var compare = require('fast-json-patch').compare;
|
||||
var path = require('path');
|
||||
var $ = require('cheerio');
|
||||
var Matter = require('../../build/matter-dev.js');
|
||||
Matter.Example = require('../../demo/js/Examples.js');
|
||||
Matter.Demo = require('../../demo/js/Demo.js');
|
||||
|
||||
var demo,
|
||||
frames = 10,
|
||||
refsPath = 'test/node/refs',
|
||||
diffsPath = 'test/node/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() {
|
||||
var demos = getDemoNames();
|
||||
|
||||
removeDir(diffsPath);
|
||||
|
||||
if (diff) {
|
||||
mkdirp(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 _demo = Matter.Demo.create(),
|
||||
engine = Matter.Example.engine(_demo),
|
||||
runner = Matter.Runner.create();
|
||||
|
||||
_demo.engine = engine;
|
||||
_demo.engine.render = {};
|
||||
_demo.engine.render.options = {};
|
||||
_demo.runner = runner;
|
||||
_demo.render = { options: {} };
|
||||
|
||||
if (!(demo in Matter.Example)) {
|
||||
throw '\'' + demo + '\' is not defined in Matter.Example';
|
||||
}
|
||||
|
||||
Matter.Demo.reset(_demo);
|
||||
Matter.Example[demo](_demo);
|
||||
|
||||
var worldStart = JSON.parse(resurrect.stringify(engine.world, precisionLimiter));
|
||||
|
||||
for (var j = 0; j <= frames; j += 1) {
|
||||
Matter.Runner.tick(runner, engine, j * runner.delta);
|
||||
}
|
||||
|
||||
var worldEnd = JSON.parse(resurrect.stringify(engine.world, precisionLimiter));
|
||||
|
||||
if (fs.existsSync(worldStartPath)) {
|
||||
var worldStartRef = JSON.parse(fs.readFileSync(worldStartPath));
|
||||
var worldStartDiff = compare(worldStartRef, worldStart);
|
||||
|
||||
if (worldStartDiff.length !== 0) {
|
||||
if (diff) {
|
||||
writeFile(worldStartDiffPath, JSON.stringify(worldStartDiff, precisionLimiter, 2));
|
||||
}
|
||||
|
||||
if (forceUpdate) {
|
||||
hasCreated = true;
|
||||
writeFile(worldStartPath, JSON.stringify(worldStart, precisionLimiter, 2));
|
||||
} else {
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasCreated = true;
|
||||
writeFile(worldStartPath, JSON.stringify(worldStart, precisionLimiter, 2));
|
||||
}
|
||||
|
||||
if (fs.existsSync(worldEndPath)) {
|
||||
var worldEndRef = JSON.parse(fs.readFileSync(worldEndPath));
|
||||
var worldEndDiff = compare(worldEndRef, worldEnd);
|
||||
|
||||
if (worldEndDiff.length !== 0) {
|
||||
if (diff) {
|
||||
writeFile(worldEndDiffPath, JSON.stringify(worldEndDiff, precisionLimiter, 2));
|
||||
}
|
||||
|
||||
if (forceUpdate) {
|
||||
hasCreated = true;
|
||||
writeFile(worldEndPath, JSON.stringify(worldEnd, precisionLimiter, 2));
|
||||
} else {
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasCreated = true;
|
||||
writeFile(worldEndPath, JSON.stringify(worldEnd, precisionLimiter, 2));
|
||||
}
|
||||
|
||||
if (hasChanged) {
|
||||
changed.push("'" + demo + "'");
|
||||
process.stdout.write('x');
|
||||
} else if (hasCreated) {
|
||||
created.push("'" + demo + "'");
|
||||
process.stdout.write('+');
|
||||
} else {
|
||||
process.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');
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
process.exit(!isOk);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
var precisionLimiter = function(key, value) {
|
||||
if (typeof value === 'number') {
|
||||
return parseFloat(value.toFixed(5));
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
function arg(name) {
|
||||
var index = process.argv.indexOf(name);
|
||||
if (index >= 0) {
|
||||
return process.argv[index + 1] || true;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var getDemoNames = function() {
|
||||
var demos = [],
|
||||
skip = [
|
||||
'terrain', 'svg', 'concave',
|
||||
'slingshot', 'views', 'raycasting',
|
||||
'events', 'collisionFiltering', 'sleeping',
|
||||
'attractors'
|
||||
];
|
||||
|
||||
$('#demo-select option', fs.readFileSync('demo/index.html').toString())
|
||||
.each(function() {
|
||||
var name = $(this).val();
|
||||
if (skip.indexOf(name) === -1) {
|
||||
demos.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
return demos;
|
||||
};
|
||||
|
||||
var writeFile = function(filePath, string) {
|
||||
mkdirp(path.dirname(filePath));
|
||||
fs.writeFileSync(filePath, string);
|
||||
};
|
||||
|
||||
test();
|
77
webpack.config.js
Normal file
77
webpack.config.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
/* eslint-env es6 */
|
||||
"use strict";
|
||||
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const pkg = require('./package.json');
|
||||
const execSync = require('child_process').execSync;
|
||||
|
||||
module.exports = (env = {}) => {
|
||||
const minimize = env.MINIMIZE || false;
|
||||
const alpha = env.ALPHA || false;
|
||||
const maxSize = minimize ? 100 * 1024 : 512 * 1024;
|
||||
const isDevServer = process.env.WEBPACK_DEV_SERVER;
|
||||
|
||||
const commitHash = execSync('git rev-parse --short HEAD').toString().trim();
|
||||
const version = !alpha ? pkg.version : `${pkg.version}-alpha+${commitHash}`;
|
||||
const date = new Date().toISOString().slice(0, 10);
|
||||
const name = 'matter';
|
||||
const alphaInfo = 'Experimental pre-release build.\n ';
|
||||
const banner =
|
||||
` ${pkg.name} ${version} by @liabru (c) ${date}
|
||||
${alpha ? alphaInfo : ''}${pkg.homepage}
|
||||
License ${pkg.license}`;
|
||||
|
||||
return {
|
||||
entry: { [name]: './src/module/main.js' },
|
||||
output: {
|
||||
library: 'Matter',
|
||||
libraryTarget: 'umd',
|
||||
umdNamedDefine: true,
|
||||
globalObject: 'this',
|
||||
path: path.resolve(__dirname, './build'),
|
||||
filename: `[name]${alpha ? '.alpha' : ''}${minimize ? '.min' : ''}.js`
|
||||
},
|
||||
node: false,
|
||||
optimization: { minimize },
|
||||
performance: {
|
||||
maxEntrypointSize: maxSize,
|
||||
maxAssetSize: maxSize
|
||||
},
|
||||
plugins: [
|
||||
new webpack.BannerPlugin(banner),
|
||||
new webpack.DefinePlugin({
|
||||
__MATTER_VERSION__: JSON.stringify(!isDevServer ? version : '*'),
|
||||
})
|
||||
],
|
||||
externals: {
|
||||
'poly-decomp': {
|
||||
commonjs: 'poly-decomp',
|
||||
commonjs2: 'poly-decomp',
|
||||
amd: 'poly-decomp',
|
||||
root: 'decomp'
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
contentBase: [
|
||||
path.resolve(__dirname, './demo'),
|
||||
path.resolve(__dirname, './examples'),
|
||||
path.resolve(__dirname, './build')
|
||||
],
|
||||
open: true,
|
||||
openPage: '',
|
||||
compress: true,
|
||||
port: 8000,
|
||||
proxy: {
|
||||
'/build': {
|
||||
target: 'http://localhost:8000/',
|
||||
pathRewrite: { '^/build' : '/' }
|
||||
},
|
||||
'/examples': {
|
||||
target: 'http://localhost:8000/',
|
||||
pathRewrite: { '^/examples' : '/' }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
50
webpack.examples.config.js
Normal file
50
webpack.examples.config.js
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* eslint-env es6 */
|
||||
"use strict";
|
||||
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const pkg = require('./package.json');
|
||||
const execSync = require('child_process').execSync;
|
||||
|
||||
module.exports = (env = {}) => {
|
||||
const minimize = env.MINIMIZE || false;
|
||||
const edge = env.EDGE || false;
|
||||
const maxSize = minimize ? 100 * 1024 : 512 * 1024;
|
||||
|
||||
const commitHash = execSync('git rev-parse --short HEAD').toString().trim();
|
||||
const version = !edge ? pkg.version : `${pkg.version}-alpha-${commitHash}`;
|
||||
const date = new Date().toISOString().slice(0, 10);
|
||||
const name = 'matter-examples';
|
||||
const banner = `${name} ${version} by @liabru ${date}
|
||||
${pkg.homepage}
|
||||
License ${pkg.license}`;
|
||||
|
||||
return {
|
||||
entry: './examples/index.js',
|
||||
output: {
|
||||
library: 'Example',
|
||||
libraryTarget: 'umd',
|
||||
umdNamedDefine: true,
|
||||
globalObject: 'this',
|
||||
path: path.resolve(__dirname, './demo/js'),
|
||||
filename: `Examples${minimize ? '.min' : ''}.js`
|
||||
},
|
||||
node: false,
|
||||
optimization: { minimize },
|
||||
performance: {
|
||||
maxEntrypointSize: maxSize,
|
||||
maxAssetSize: maxSize
|
||||
},
|
||||
plugins: [
|
||||
new webpack.BannerPlugin(banner)
|
||||
],
|
||||
externals: {
|
||||
'matter-js': {
|
||||
commonjs: 'matter-js',
|
||||
commonjs2: 'matter-js',
|
||||
amd: 'matter-js',
|
||||
root: 'Matter'
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
11
yuidoc.json
Normal file
11
yuidoc.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "Matter.js Physics Engine API Docs",
|
||||
"description": "a 2D rigid body physics engine for the web",
|
||||
"url": "http://brm.io/matter-js/",
|
||||
"options": {
|
||||
"linkNatives": true,
|
||||
"outdir": "doc/build",
|
||||
"themedir": "matter-doc-theme",
|
||||
"paths": "src"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue