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

Merge branch 'master' into timing-improve

* master: (123 commits)
  release 0.18.0
  prevent source map in demo builds
  updated test scripts
  added note about webpack performance to readme
  added benchmark test command
  increase iterations on Example.stress3
  add triangles to mixed bodies example
  added example for Composite.remove
  updated examples
  deprecated Matter.Grid
  added broadphase to Matter.Detector
  replaced Matter.SAT with Matter.Collision
  use force exit in tests
  added cache checks to Matter.Composite
  change raycasting example events to enable use in tests
  optimised collisions
  added Matter.Collision
  use Matter.Runner in test worker
  optimised Matter.Pairs
  optimised Resolver.solvePosition
  ...

# Conflicts:
#	examples/car.js
#	examples/manipulation.js
#	examples/ragdoll.js
#	examples/slingshot.js
#	examples/timescale.js
#	src/collision/Detector.js
#	src/collision/Resolver.js
#	src/collision/SAT.js
#	src/core/Engine.js
#	src/core/Runner.js
This commit is contained in:
liabru 2021-12-20 18:52:58 +00:00
commit 7fce5d861c
107 changed files with 26980 additions and 18446 deletions

View file

@ -29,7 +29,6 @@
"process": false, "process": false,
"HTMLElement": false, "HTMLElement": false,
"require": false, "require": false,
"PIXI": false,
"$": false, "$": false,
"Image": false, "Image": false,
"navigator": false, "navigator": false,
@ -59,14 +58,13 @@
"Vector": false, "Vector": false,
"Vertices": false, "Vertices": false,
"Render": false, "Render": false,
"RenderPixi": false,
"Events": false, "Events": false,
"Query": false, "Query": false,
"Runner": false, "Runner": false,
"Svg": false, "Svg": false,
"Metrics": false,
"Example": false, "Example": false,
"__MATTER_VERSION__": false, "__MATTER_VERSION__": false,
"__MATTER_IS_DEV__": false,
"jest": false, "jest": false,
"test": false, "test": false,
"expect": false, "expect": false,

5
.gitignore vendored
View file

@ -1,10 +1,13 @@
.idea .idea
*.map
node_modules node_modules
npm-debug.log npm-debug.log
docs docs
matter-doc-theme matter-doc-theme
build/matter-dev.js build/matter-dev.js
build/matter-dev.min.js build/matter-dev.min.js
build/matter.dev.js
build/matter.dev.min.js
demo/js/lib/matter-dev.js demo/js/lib/matter-dev.js
demo/js/Examples.min.js demo/js/Examples.min.js
examples/build examples/build
@ -13,4 +16,4 @@ test/browser/refs
test/node/diffs test/node/diffs
test/node/refs test/node/refs
__snapshots__ __snapshots__
__compare__ __compare__

View file

@ -3,8 +3,10 @@ sudo: false
node_js: node_js:
- "node" - "node"
install: install:
- npm install - npm ci
script: script:
- npm run lint - npm run lint
- npm run benchmark
- npm run test - npm run test
- npm run build - npm run build
- npm run build-demo

View file

@ -1,3 +1,127 @@
## 0.18.0 (2021-12-15)
* added test capture sort to improve comparison ([ea3c11b](https://github.com/liabru/matter-js/commit/ea3c11b))
* added triangles to mixed bodies example ([b116f64](https://github.com/liabru/matter-js/commit/b116f64))
* added behaviour metric to tests and refactor tests ([8125966](https://github.com/liabru/matter-js/commit/8125966))
* added benchmark test command ([7f34c45](https://github.com/liabru/matter-js/commit/7f34c45))
* added broadphase to Matter.Detector ([a6b5e7d](https://github.com/liabru/matter-js/commit/a6b5e7d))
* added cache checks to Matter.Composite ([32fd285](https://github.com/liabru/matter-js/commit/32fd285))
* added example for Composite.remove ([bc07f56](https://github.com/liabru/matter-js/commit/bc07f56))
* added example stress 3 ([d0ee246](https://github.com/liabru/matter-js/commit/d0ee246))
* added filesize to test comparison report ([b3a8aa3](https://github.com/liabru/matter-js/commit/b3a8aa3))
* added Matter.Collision ([9037f36](https://github.com/liabru/matter-js/commit/9037f36))
* added memory comparison to tests ([bedf84c](https://github.com/liabru/matter-js/commit/bedf84c))
* added note about webpack performance to readme ([80cf76b](https://github.com/liabru/matter-js/commit/80cf76b))
* added stable sorting to test worker and refactor ([81dd2fb](https://github.com/liabru/matter-js/commit/81dd2fb))
* added support for build metadata in Plugin.versionParse ([8bfaff0](https://github.com/liabru/matter-js/commit/8bfaff0))
* changed raycasting example events to enable use in tests ([10afaea](https://github.com/liabru/matter-js/commit/10afaea))
* changed build config to 'source-map' devtool ([e909b04](https://github.com/liabru/matter-js/commit/e909b04))
* changed tests to use a production build rather than source ([55feb89](https://github.com/liabru/matter-js/commit/55feb89))
* deprecated Matter.Grid ([e366d0e](https://github.com/liabru/matter-js/commit/e366d0e))
* fixed sync issues on demo compare tool ([826ed46](https://github.com/liabru/matter-js/commit/826ed46))
* improved performance measurement accuracy in tests ([cd289ec](https://github.com/liabru/matter-js/commit/cd289ec))
* improved test comparison report ([de04c00](https://github.com/liabru/matter-js/commit/de04c00))
* optimised Matter.Detector ([c7cec16](https://github.com/liabru/matter-js/commit/c7cec16)), ([fd1a70e](https://github.com/liabru/matter-js/commit/fd1a70e)), ([caeb07e](https://github.com/liabru/matter-js/commit/caeb07e)), ([efede6e](https://github.com/liabru/matter-js/commit/efede6e))
* optimised Matter.Composite ([52e7977](https://github.com/liabru/matter-js/commit/52e7977))
* optimised Matter.Pair ([d8a6380](https://github.com/liabru/matter-js/commit/d8a6380)), ([48673db](https://github.com/liabru/matter-js/commit/48673db)), ([1073dde](https://github.com/liabru/matter-js/commit/1073dde))
* optimised Matter.Pairs ([a30707f](https://github.com/liabru/matter-js/commit/a30707f)), ([a882a74](https://github.com/liabru/matter-js/commit/a882a74))
* optimised Matter.Resolver ([fceb0ca](https://github.com/liabru/matter-js/commit/fceb0ca)), ([49fbfba](https://github.com/liabru/matter-js/commit/49fbfba)), ([0b07a31](https://github.com/liabru/matter-js/commit/0b07a31)), ([f847f4c](https://github.com/liabru/matter-js/commit/f847f4c)), ([3cf65e8](https://github.com/liabru/matter-js/commit/3cf65e8)), ([30b899c](https://github.com/liabru/matter-js/commit/30b899c)), ([e4b35d3](https://github.com/liabru/matter-js/commit/e4b35d3))
* optimised Matter.SAT ([0d90a17](https://github.com/liabru/matter-js/commit/0d90a17)), ([2096961](https://github.com/liabru/matter-js/commit/2096961)), ([0af144c](https://github.com/liabru/matter-js/commit/0af144c))
* optimised Matter.Vertices ([c198878](https://github.com/liabru/matter-js/commit/c198878)), ([6883d0d](https://github.com/liabru/matter-js/commit/6883d0d)), ([792ae2e](https://github.com/liabru/matter-js/commit/792ae2e))
* refactor test worker and prevent test cache ([ca2fe75](https://github.com/liabru/matter-js/commit/ca2fe75)), ([bcc3168](https://github.com/liabru/matter-js/commit/bcc3168))
* replaced Matter.SAT with Matter.Collision ([b9e7d9d](https://github.com/liabru/matter-js/commit/b9e7d9d))
* show debug stats in dev demo ([2f14ec5](https://github.com/liabru/matter-js/commit/2f14ec5))
* updated dev dependencies ([c5028d5](https://github.com/liabru/matter-js/commit/c5028d5))
* updated examples ([c80ed5c](https://github.com/liabru/matter-js/commit/c80ed5c))
* updated test scripts ([afa467a](https://github.com/liabru/matter-js/commit/afa467a))
* use force exit in tests ([8adf810](https://github.com/liabru/matter-js/commit/8adf810))
* use Matter.Runner in test worker ([2581595](https://github.com/liabru/matter-js/commit/2581595))
## <small>0.17.1 (2021-04-14)</small>
* deprecate Engine.run alias replaced by Runner.run ([5817046](https://github.com/liabru/matter-js/commit/5817046))
## 0.17.0 (2021-04-11)
* add Common.setDecomp and Common.getDecomp ([313c150](https://github.com/liabru/matter-js/commit/313c150))
* add demo build config and refactor demo ([653a647](https://github.com/liabru/matter-js/commit/653a647))
* add docs for all Matter.Render options ([ec3eecc](https://github.com/liabru/matter-js/commit/ec3eecc))
* add title to all examples ([f4d72ba](https://github.com/liabru/matter-js/commit/f4d72ba))
* added Common.deprecated ([ffa3193](https://github.com/liabru/matter-js/commit/ffa3193))
* added Common.warnOnce ([6957dbf](https://github.com/liabru/matter-js/commit/6957dbf))
* added Date.now fallback to Common.now, closes #739 ([c06c107](https://github.com/liabru/matter-js/commit/c06c107)), closes [#739](https://github.com/liabru/matter-js/issues/739)
* added debug stats and performance monitoring to Matter.Render ([119881b](https://github.com/liabru/matter-js/commit/119881b))
* added doc watch script ([164456b](https://github.com/liabru/matter-js/commit/164456b))
* added docs for additional engine timing and render properties ([8017bdb](https://github.com/liabru/matter-js/commit/8017bdb))
* added Example.stats ([9915007](https://github.com/liabru/matter-js/commit/9915007))
* added lastDelta and lastElapsed to engine.timing ([6dc703f](https://github.com/liabru/matter-js/commit/6dc703f))
* build demo ([0816454](https://github.com/liabru/matter-js/commit/0816454))
* change all examples to use Composite.add instead of the alias World.add ([a3f298f](https://github.com/liabru/matter-js/commit/a3f298f))
* changed engine.broadphase to engine.grid ([b74e400](https://github.com/liabru/matter-js/commit/b74e400))
* changed Example.views to use render events ([3ac3498](https://github.com/liabru/matter-js/commit/3ac3498))
* changed world.gravity to engine.gravity ([6abb3b7](https://github.com/liabru/matter-js/commit/6abb3b7))
* deprecated Composites.car and added to car example ([cd9c5d4](https://github.com/liabru/matter-js/commit/cd9c5d4))
* deprecated Composites.newtonsCradle and added to newtonsCradle example ([9ad980b](https://github.com/liabru/matter-js/commit/9ad980b))
* deprecated Composites.softBody and added to softBody and cloth examples ([818f354](https://github.com/liabru/matter-js/commit/818f354))
* improve Render.debug ([1753bf0](https://github.com/liabru/matter-js/commit/1753bf0))
* migrate Matter.World to the equivalent Matter.Composite ([5dbec9b](https://github.com/liabru/matter-js/commit/5dbec9b))
* remove deprecated backwards compatibility engine events from Matter.Runner ([76bf80e](https://github.com/liabru/matter-js/commit/76bf80e))
* remove deprecated backwards compatibility render integration from Matter.Engine ([1aa8ed0](https://github.com/liabru/matter-js/commit/1aa8ed0))
* remove deprecated backwards compatibility render integration from Matter.Runner ([6805f85](https://github.com/liabru/matter-js/commit/6805f85))
* remove deprecated Matter.Metrics ([63a9e98](https://github.com/liabru/matter-js/commit/63a9e98))
* remove deprecated Matter.RenderPixi ([08a515b](https://github.com/liabru/matter-js/commit/08a515b))
* remove unused render shadow function ([e49834d](https://github.com/liabru/matter-js/commit/e49834d))
* run all examples in browser tests ([5734bfd](https://github.com/liabru/matter-js/commit/5734bfd))
* set render showDebug option on examples stress and stress2 ([f2ef3aa](https://github.com/liabru/matter-js/commit/f2ef3aa))
* update matter-tools ([f8d366a](https://github.com/liabru/matter-js/commit/f8d366a))
## <small>0.16.1 (2021-01-31)</small>
* add log capture and reporting in tests ([7bfd3c2](https://github.com/liabru/matter-js/commit/7bfd3c2))
* add matter-wrap as dev dependency ([ef7c4c6](https://github.com/liabru/matter-js/commit/ef7c4c6))
* catch missing plugin require in examples ([1ba1255](https://github.com/liabru/matter-js/commit/1ba1255))
* change example matter-wrap require precedence ([aa1e4e7](https://github.com/liabru/matter-js/commit/aa1e4e7))
* change perf threshold in test ([7b00354](https://github.com/liabru/matter-js/commit/7b00354))
* change test worker decomp require ([5e746b0](https://github.com/liabru/matter-js/commit/5e746b0))
* disable jest cache ([8ee0ebb](https://github.com/liabru/matter-js/commit/8ee0ebb))
* enable useful dev server features ([839f7d9](https://github.com/liabru/matter-js/commit/839f7d9))
* fix decomp require and improve warning message ([e87f64a](https://github.com/liabru/matter-js/commit/e87f64a))
* fix dev server externals ([1f2f9fe](https://github.com/liabru/matter-js/commit/1f2f9fe))
* fix named require in test ([92080ff](https://github.com/liabru/matter-js/commit/92080ff))
* improve CI speed ([1af64eb](https://github.com/liabru/matter-js/commit/1af64eb))
* improve docs for Bodies.fromVertices ([2ade78f](https://github.com/liabru/matter-js/commit/2ade78f))
* improve stack examples with exact contact ([522f4e8](https://github.com/liabru/matter-js/commit/522f4e8))
* improve test report log ([b5326f8](https://github.com/liabru/matter-js/commit/b5326f8))
## 0.16.0 (2021-01-17)
* added removeDuplicatePoints option from poly-decomp 0.3.0 to Bodies.fromVertices ([#639](https://github.com/liabru/matter-js/issues/639), [947cb97](https://github.com/liabru/matter-js/commit/947cb97), [a9694e6](https://github.com/liabru/matter-js/commit/a9694e6))
* added support for > and >= operators in plugin version ranges ([0792716](https://github.com/liabru/matter-js/commit/0792716))
* added support for example versioning in tests ([31d0ace](https://github.com/liabru/matter-js/commit/31d0ace))
* added version targets for examples ([142b7de](https://github.com/liabru/matter-js/commit/142b7de))
* added warning check on browser tests ([12377cc](https://github.com/liabru/matter-js/commit/12377cc))
* changed svg and terrain example to use fetch ([5551cd5](https://github.com/liabru/matter-js/commit/5551cd5))
* changed deprecated comments to avoid confusion ([1e73cab](https://github.com/liabru/matter-js/commit/1e73cab))
* fixed poly-decomp external require ([882e07c](https://github.com/liabru/matter-js/commit/882e07c))
* removed bower.json ([f71d4c0](https://github.com/liabru/matter-js/commit/f71d4c0))
* removed unused dev dependency run-sequence ([be592fd](https://github.com/liabru/matter-js/commit/be592fd))
* updated dev dependencies ([151eb30](https://github.com/liabru/matter-js/commit/151eb30))
* updated matter-tools ([5c66458](https://github.com/liabru/matter-js/commit/5c66458))
* updated matter-tools ([33e8fe8](https://github.com/liabru/matter-js/commit/33e8fe8))
* updated demo matter-tools ([a694ae5](https://github.com/liabru/matter-js/commit/a694ae5))
* updated demo pathseg ([9c5325b](https://github.com/liabru/matter-js/commit/9c5325b))
* updated readme ([3089b41](https://github.com/liabru/matter-js/commit/3089b41))
## 0.15.0 (2020-12-24) ## 0.15.0 (2020-12-24)
* add window global, stub require and handle bad values in test tools ([497ac80](https://github.com/liabru/matter-js/commit/497ac80)) * add window global, stub require and handle bad values in test tools ([497ac80](https://github.com/liabru/matter-js/commit/497ac80))

View file

@ -18,7 +18,7 @@ To build you must first install [node.js](http://nodejs.org), then run
which will install the required build dependencies, then run which will install the required build dependencies, then run
npm run dev npm start
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. 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.
@ -36,13 +36,15 @@ The output of `npm run test` also includes a [comparison report](https://github.
The following development commands can be run at the terminal The following development commands can be run at the terminal
- **npm run dev** - **npm start**
runs development server runs development server and opens demo page
- **npm run build** - **npm run build**
creates a release build creates a release build
- **npm run build-demo**
builds the demo
- **npm run lint** - **npm run lint**
runs the linter runs the linter
- **npm run test** - **npm run test**
runs the tests and compares results runs the tests and compares results
- **npm run test-browser** - **npm run test-browser**
runs the browser tests runs the browser tests

View file

@ -58,7 +58,6 @@
<li><a href="https://brm.io/matter-js/demo/#airFriction">Air Friction</a></li> <li><a href="https://brm.io/matter-js/demo/#airFriction">Air Friction</a></li>
<li><a href="https://brm.io/matter-js/demo/#staticFriction">Static Friction</a></li> <li><a href="https://brm.io/matter-js/demo/#staticFriction">Static Friction</a></li>
<li><a href="https://brm.io/matter-js/demo/#sleeping">Sleeping</a></li> <li><a href="https://brm.io/matter-js/demo/#sleeping">Sleeping</a></li>
<li><a href="https://brm.io/matter-js/demo/#broadphase">Grid Broadphase</a></li>
<li><a href="https://brm.io/matter-js/demo/#beachBalls">Beach Balls</a></li> <li><a href="https://brm.io/matter-js/demo/#beachBalls">Beach Balls</a></li>
<li><a href="https://brm.io/matter-js/demo/#stress">Stress 1</a></li> <li><a href="https://brm.io/matter-js/demo/#stress">Stress 1</a></li>
<li><a href="https://brm.io/matter-js/demo/#stress2">Stress 2</a></li> <li><a href="https://brm.io/matter-js/demo/#stress2">Stress 2</a></li>
@ -125,6 +124,10 @@ Alternatively you can download a [stable release](https://github.com/liabru/matt
<script src="matter.js" type="text/javascript"></script> <script src="matter.js" type="text/javascript"></script>
### Webpack
Some [webpack](https://webpack.js.org/) configs including the default may impact your project's performance during development, for a solution see [issue](https://github.com/liabru/matter-js/issues/1001).
### Usage ### 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. 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.

View file

@ -1,3 +1,158 @@
## ▲.●matter.js`0.18.0`
Release notes for `0.18.0`. See the release [readme](https://github.com/liabru/matter-js/blob/0.18.0/README.md) for further information.
### Highlights
- **Up to ~40% performance improvement (on average measured over all examples, in Node on a Mac Air M1)**
- Replaces `Matter.Grid` with a faster and more efficient broadphase in `Matter.Detector`
- Reduced memory usage and garbage collection
- Resolves issues in `Matter.SAT` related to collision reuse
- Removes performance issues from `Matter.Grid`
- Improved collision accuracy
- Improved API and docs for collision modules
- Improved [documentation](https://brm.io/matter-js/docs/) pages
- Added note about avoiding Webpack [performance problems](https://github.com/liabru/matter-js/blob/master/README.md#install)
### Changes
See the release [compare page](https://github.com/liabru/matter-js/compare/0.17.1...0.18.0) and the [changelog](https://github.com/liabru/matter-js/blob/0.18.0/CHANGELOG.md) for a detailed list of changes.
### Migration ⌲
- Behaviour and similarity is in practice the same (a fractional difference from improved accuracy)
- API is the same other than:
- [Matter.Detector](https://brm.io/matter-js/docs/classes/Detector.html) replaces [Matter.Grid](https://brm.io/matter-js/docs/classes/Grid.html) (which is now deprecated but will remain for a short term)
- [render.options.showBroadphase](https://brm.io/matter-js/docs/classes/Render.html#property_options.showBroadphase) is deprecated (no longer implemented)
- [Matter.SAT](https://brm.io/matter-js/docs/classes/SAT.html) is renamed [Matter.Collision](https://brm.io/matter-js/docs/classes/Collision.html)
- [Matter.SAT.collides](https://brm.io/matter-js/docs/classes/SAT.html#method_collides) is now [Matter.Collision.collides](https://brm.io/matter-js/docs/classes/Collision.html#method_collides) with a slightly different set of arguments
### Comparison
Differences in behaviour, quality and performance against the previous release `0.17.1`. For more information see [comparison method](https://github.com/liabru/matter-js/pull/794).
```ocaml
Output comparison of 43 examples against previous release matter-js@0.17.1
Behaviour 99.99% Similarity 99.98% Overlap -0.00%
Performance +40.62% Memory -6.18% Filesize -0.16% 77.73 KB
airFriction · · avalanche ● · ballPool · · bridge · · car · · catapult · ·
chains · · circleStack · · cloth · · collisionFiltering · · compositeManipulation ● ·
compound · · compoundStack · · concave · · constraints ● · doublePendulum · ·
events · · friction · · gravity · · gyro · · manipulation · ◆
mixed · · newtonsCradle · · pyramid · · ragdoll · · raycasting · ·
remove · · restitution · · rounded · · sensors · · sleeping · ◆
slingshot · · softBody · · sprites · · stack · · staticFriction · ·
stats · · stress · · stress2 · · stress3 · · timescale · ·
views · · wreckingBall · ·
where · no change ● extrinsics changed ◆ intrinsics changed
```
### Contributors
Many thanks to the [contributors](https://github.com/liabru/matter-js/compare/0.17.1...0.18.0) of this release, [past contributors](https://github.com/liabru/matter-js/graphs/contributors) as well those involved in the [community](https://github.com/liabru/matter-js/issues) for your input and support.
---
## ▲.●matter.js`0.17.0`
Release notes for `0.17.0`. See the release [readme](https://github.com/liabru/matter-js/blob/0.17.0/README.md) for further information.
### Highlights
- Added performance and stats monitoring overlays to `Matter.Render`
- See the [stats demo](https://brm.io/matter-js/demo/#stats) or enable [render.options.showPerformance](https://brm.io/matter-js/docs/classes/Render.html#property_options.showPerformance) and [render.options.showStats](https://brm.io/matter-js/docs/classes/Render.html#property_options.showStats)
- Stats shown include
- render frequency (e.g. `60 fps`)
- engine delta time (e.g. `16.66ms`)
- engine execution duration (e.g. `5.00ms`)
- render execution duration (e.g.` 0.40ms`)
- effective play speed (e.g. `1.00x` is real-time)
- various other engine internal stats for debugging
- Improved [documentation](https://brm.io/matter-js/docs/) pages
- Added [Common.setDecomp](https://brm.io/matter-js/docs/classes/Common.html#method_setDecomp) and [Common.getDecomp](https://brm.io/matter-js/docs/classes/Common.html#method_getDecomp) to fix [bundler issues](https://github.com/liabru/matter-js/issues/981)
- Added docs for all [Matter.Render options](https://brm.io/matter-js/docs/classes/Render.html#properties)
- Migrated usage of `Matter.World` over to `Matter.Composite` (more info in [docs](https://brm.io/matter-js/docs/classes/World.html))
- Migrated, deprecated and removed various old functionality (see the [changelog](https://github.com/liabru/matter-js/blob/0.17.0/CHANGELOG.md) for details)
### Changes
See the release [compare page](https://github.com/liabru/matter-js/compare/0.16.1...0.17.0) and the [changelog](https://github.com/liabru/matter-js/blob/0.17.0/CHANGELOG.md) for a detailed list of changes.
### Comparison
Differences in behaviour, quality and performance against the previous release `0.16.1`. For more information see [comparison method](https://github.com/liabru/matter-js/pull/794).
```ocaml
Output comparison of 37 examples against previous release matter-js@0.16.1
Similarity 100% Performance +0.00% Overlap +0.00%
airFriction · · avalanche · · ballPool · · bridge · · broadphase · · car · ·
catapult · · chains · · circleStack · · cloth · · collisionFiltering · ·
compound · · compoundStack · · constraints · · events · · friction · ·
gyro · · manipulation · · mixed · · newtonsCradle · · pyramid · ·
ragdoll · · restitution · · rounded · · sensors · · sleeping · ·
slingshot · · softBody · · sprites · · stack · · staticFriction · ·
stats · · stress · · stress2 · · timescale · · views · ·
wreckingBall · ·
where · no change ● extrinsics changed ◆ intrinsics changed
```
### Contributors
Many thanks to the [contributors](https://github.com/liabru/matter-js/compare/0.16.1...0.17.0) of this release, [past contributors](https://github.com/liabru/matter-js/graphs/contributors) as well those involved in the [community](https://github.com/liabru/matter-js/issues) for your input and support.
---
## ▲.●matter.js`0.16.0`
Release notes for `0.16.0`. See the release [readme](https://github.com/liabru/matter-js/blob/0.16.0/README.md) for further information.
### Highlights
- Changed external require method for `poly-decomp` ([882e07c](https://github.com/liabru/matter-js/commit/882e07c))
- Improved `Bodies.fromVertices` decomposition quality using `removeDuplicatePoints` ([#639](https://github.com/liabru/matter-js/pull/639))
- Added support for `>x.x.x` and `>=x.x.x` semver ranges in plugins ([0792716](https://github.com/liabru/matter-js/commit/0792716))
- Changed demo example select background colour for Windows ([matter-tools #5](https://github.com/liabru/matter-tools/pull/5))
- Updated demo to use latest [matter-tools](https://github.com/liabru/matter-tools) ([#33e8fe8](https://github.com/liabru/matter-js/commit/33e8fe8))
- Updated SVG and terrain examples to use `fetch` ([5551cd5](https://github.com/liabru/matter-js/commit/5551cd5))
### Changes
See the release [compare page](https://github.com/liabru/matter-js/compare/0.15.0...0.16.0) and the [changelog](https://github.com/liabru/matter-js/blob/0.16.0/CHANGELOG.md) for a detailed list of changes.
### Comparison
Differences in behaviour, quality and performance against the previous release `0.15.0`. For more information see [comparison method](https://github.com/liabru/matter-js/pull/794).
```ocaml
Output comparison of 41 examples against matter-js@0.15.0 build on last run
Similarity 100% Performance +0.00% Overlap +0.00%
airFriction · · avalanche · · ballPool · · bridge · · broadphase · · car · ·
catapult · · chains · · circleStack · · cloth · · collisionFiltering · ·
compositeManipulation · · compound · · compoundStack · · concave · · constraints · ·
doublePendulum · · events · · friction · · gravity · · gyro · ·
manipulation · · mixed · · newtonsCradle · · pyramid · · ragdoll · ·
raycasting · · restitution · · rounded · · sensors · · sleeping · ·
slingshot · · softBody · · sprites · · stack · · staticFriction · ·
stress · · stress2 · · timescale · · views · · wreckingBall · ·
where · no change ● extrinsics changed ◆ intrinsics changed
```
### Contributors
Many thanks to the [contributors](https://github.com/liabru/matter-js/compare/0.15.0...0.16.0) of this release, [past contributors](https://github.com/liabru/matter-js/graphs/contributors) as well those involved in the [community](https://github.com/liabru/matter-js/issues) for your input and support.
---
## ▲.●matter.js`0.15.0` ## ▲.●matter.js`0.15.0`
Release notes for `0.15.0`. See the release [readme](https://github.com/liabru/matter-js/blob/0.15.0/README.md) for further information. Release notes for `0.15.0`. See the release [readme](https://github.com/liabru/matter-js/blob/0.15.0/README.md) for further information.

File diff suppressed because it is too large Load diff

4
build/matter.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -9,28 +9,8 @@
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="robots" content="noindex"> <meta name="robots" content="noindex">
<title>Matter.js Demo</title> <title>Matter.js Demo</title>
<!-- Libs -->
<script type="text/javascript" src="./lib/decomp.js"></script>
<script type="text/javascript" src="./lib/pathseg.js"></script>
<!-- Examples -->
<script src="./js/Examples.js"></script>
<!-- Matter -->
<script src="../build/matter.js"></script>
<!-- MatterTools -->
<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="./lib/matter-wrap.js"></script>
<style type="text/css"> <style type="text/css">
* { * {
box-sizing: border-box; box-sizing: border-box;
@ -41,7 +21,13 @@
padding: 0; padding: 0;
} }
.matter-js-compare-build { .matter-btn-compare.matter-btn {
font-size: 18px;
text-align: center;
line-height: 32px;
}
.matter-js-compare-build.matter-demo {
position: absolute; position: absolute;
background: none; background: none;
top: 0; top: 0;
@ -53,21 +39,22 @@
pointer-events: none; pointer-events: none;
} }
.matter-js-compare-build .matter-header-outer { .matter-js-compare-build.matter-demo .matter-header-outer {
display: none; display: none;
} }
.matter-js-compare-build canvas { .matter-js-compare-build.matter-demo canvas {
opacity: 0.5; opacity: 0.5;
background: transparent !important; background: transparent !important;
} }
.matter-demo canvas {
border: 1px solid rgba(255, 255, 255, 0.07);
}
</style> </style>
</head> </head>
<body> <body>
<script src="./js/Demo.js"></script> <script src="./js/matter-demo.main.5754e1.min.js"></script>
<script src="./js/matter-demo.matter-tools.97f38a.min.js"></script>
<script src="./js/matter-demo.matter-wrap.dbda1f.min.js"></script>
<script src="./js/matter-demo.pathseg.cf21c2.min.js"></script>
<script src="./js/matter-demo.poly-decomp.c3d015.min.js"></script>
<script src="./js/matter-demo.a280d3.min.js"></script>
</body> </body>
</html> </html>

View file

@ -1,179 +0,0 @@
/**
* 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);
})();

View file

@ -1,107 +0,0 @@
/**
* 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/`.
*
* @class Demo
*/
(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',
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
});
window.MatterDemo = demo;
document.body.appendChild(demo.dom.root);
MatterTools.Demo.start(demo);
})();

File diff suppressed because it is too large Load diff

View file

@ -1,3 +0,0 @@
// bundle entry point for the development server
Example = require('../../examples/index.js');
module.exports = require('../../src/module/main.js');

6
demo/js/matter-demo.a280d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,5 @@
/*!
* matter-demo bundle 0.18.0 by @liabru
* http://brm.io/matter-js/
* License MIT
*/!function(e){function t(t){for(var n,l,a=t[0],f=t[1],i=t[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(p&&p(t);s.length;)s.shift()();return u.push.apply(u,i||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,a=1;a<r.length;a++){var f=r[a];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=l(l.s=r[0]))}return e}var n={},o={1:0},u=[];function l(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,l),r.l=!0,r.exports}l.m=e,l.c=n,l.d=function(e,t,r){l.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,t){if(1&t&&(e=l(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(l.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)l.d(r,n,function(t){return e[t]}.bind(null,n));return r},l.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(t,"a",t),t},l.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},l.p="./js";var a=this.webpackJsonpMatterDemo=this.webpackJsonpMatterDemo||[],f=a.push.bind(a);a.push=t,a=a.slice();for(var i=0;i<a.length;i++)t(a[i]);var p=f;r()}([]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,12 @@
/*!
* matter-demo bundle 0.18.0 by @liabru
* http://brm.io/matter-js/
* License MIT
*/
(this.webpackJsonpMatterDemo=this.webpackJsonpMatterDemo||[]).push([[3],{OPlj:function(n,r,t){
/*!
* matter-wrap 0.2.0 by Liam Brummitt 2017-07-04
* https://github.com/liabru/matter-wrap
* License MIT
*/
var o;o=function(n){return function(n){var r={};function t(o){if(r[o])return r[o].exports;var e=r[o]={i:o,l:!1,exports:{}};return n[o].call(e.exports,e,e.exports,t),e.l=!0,e.exports}return t.m=n,t.c=r,t.i=function(n){return n},t.d=function(n,r,o){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:o})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,r){return Object.prototype.hasOwnProperty.call(n,r)},t.p="/libs",t(t.s=1)}([function(r,t){r.exports=n},function(n,r,t){"use strict";var o=t(0),e={name:"matter-wrap",version:"0.1.3",for:"matter-js@^0.12.0",install:function(n){n.after("Engine.update",(function(){e.Engine.update(this)}))},Engine:{update:function(n){for(var r=n.world,t=o.Composite.allBodies(r),i=o.Composite.allComposites(r),a=0;a<t.length;a+=1){var u=t[a];u.plugin.wrap&&e.Body.wrap(u,u.plugin.wrap)}for(a=0;a<i.length;a+=1){var s=i[a];s.plugin.wrap&&e.Composite.wrap(s,s.plugin.wrap)}}},Bounds:{wrap:function(n,r){var t=null,o=null;if(void 0!==r.min.x&&void 0!==r.max.x&&(n.min.x>r.max.x?t=r.min.x-n.max.x:n.max.x<r.min.x&&(t=r.max.x-n.min.x)),void 0!==r.min.y&&void 0!==r.max.y&&(n.min.y>r.max.y?o=r.min.y-n.max.y:n.max.y<r.min.y&&(o=r.max.y-n.min.y)),null!==t||null!==o)return{x:t||0,y:o||0}}},Body:{wrap:function(n,r){var t=e.Bounds.wrap(n.bounds,r);return t&&o.Body.translate(n,t),t}},Composite:{bounds:function(n){for(var r=o.Composite.allBodies(n),t=[],e=0;e<r.length;e+=1){var i=r[e];t.push(i.bounds.min,i.bounds.max)}return o.Bounds.create(t)},wrap:function(n,r){var t=e.Bounds.wrap(e.Composite.bounds(n),r);return t&&o.Composite.translate(n,t),t}}};o.Plugin.register(e),n.exports=e}])},n.exports=o(t("lniP"))}}]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,6 @@
/*!
* matter-demo bundle 0.18.0 by @liabru
* http://brm.io/matter-js/
* License MIT
*/
(this.webpackJsonpMatterDemo=this.webpackJsonpMatterDemo||[]).push([[5],{Dded:function(n,r){function t(n,r,t){t=t||0;var e,u,f,o,i,h,a,l=[0,0];return e=n[1][1]-n[0][1],u=n[0][0]-n[1][0],f=e*n[0][0]+u*n[0][1],o=r[1][1]-r[0][1],i=r[0][0]-r[1][0],h=o*r[0][0]+i*r[0][1],x(a=e*i-o*u,0,t)||(l[0]=(i*f-u*h)/a,l[1]=(e*h-o*f)/a),l}function e(n,r,t,e){var u=r[0]-n[0],f=r[1]-n[1],o=e[0]-t[0],i=e[1]-t[1];if(o*f-i*u==0)return!1;var h=(u*(t[1]-n[1])+f*(n[0]-t[0]))/(o*f-i*u),a=(o*(n[1]-t[1])+i*(t[0]-n[0]))/(i*u-o*f);return h>=0&&h<=1&&a>=0&&a<=1}function u(n,r,t){return(r[0]-n[0])*(t[1]-n[1])-(t[0]-n[0])*(r[1]-n[1])}function f(n,r,t){return u(n,r,t)>0}function o(n,r,t){return u(n,r,t)>=0}function i(n,r,t){return u(n,r,t)<0}function h(n,r,t){return u(n,r,t)<=0}n.exports={decomp:function(n){var r=k(n);return r.length>0?D(n,r):[n]},quickDecomp:function n(r,t,e,u,a,l,c){l=l||100,c=c||0,a=a||25,t=void 0!==t?t:[],e=e||[],u=u||[];var m=[0,0],A=[0,0],M=[0,0],d=0,k=0,D=0,x=0,w=0,y=0,C=0,E=[],L=[],N=r,U=r;if(U.length<3)return t;if(++c>l)return console.warn("quickDecomp: max level ("+l+") reached."),t;for(var V=0;V<r.length;++V)if(p(N,V)){e.push(N[V]),d=k=Number.MAX_VALUE;for(var X=0;X<r.length;++X)f(s(N,V-1),s(N,V),s(N,X))&&h(s(N,V-1),s(N,V),s(N,X-1))&&(M=q(s(N,V-1),s(N,V),s(N,X),s(N,X-1)),i(s(N,V+1),s(N,V),M)&&(D=g(N[V],M))<k&&(k=D,A=M,y=X)),f(s(N,V+1),s(N,V),s(N,X+1))&&h(s(N,V+1),s(N,V),s(N,X))&&(M=q(s(N,V+1),s(N,V),s(N,X),s(N,X+1)),f(s(N,V-1),s(N,V),M)&&(D=g(N[V],M))<d&&(d=D,m=M,w=X));if(y===(w+1)%r.length)M[0]=(A[0]+m[0])/2,M[1]=(A[1]+m[1])/2,u.push(M),V<w?(v(E,N,V,w+1),E.push(M),L.push(M),0!==y&&v(L,N,y,N.length),v(L,N,0,V+1)):(0!==V&&v(E,N,V,N.length),v(E,N,0,w+1),E.push(M),L.push(M),v(L,N,y,V+1));else{if(y>w&&(w+=r.length),x=Number.MAX_VALUE,w<y)return t;for(X=y;X<=w;++X)o(s(N,V-1),s(N,V),s(N,X))&&h(s(N,V+1),s(N,V),s(N,X))&&(D=g(s(N,V),s(N,X)))<x&&b(N,V,X)&&(x=D,C=X%r.length);V<C?(v(E,N,V,C+1),0!==C&&v(L,N,C,U.length),v(L,N,0,V+1)):(0!==V&&v(E,N,V,U.length),v(E,N,0,C+1),v(L,N,C,V+1))}return E.length<L.length?(n(E,t,e,u,a,l,c),n(L,t,e,u,a,l,c)):(n(L,t,e,u,a,l,c),n(E,t,e,u,a,l,c)),t}return t.push(r),t},isSimple:function(n){var r,t=n;for(r=0;r<t.length-1;r++)for(var u=0;u<r-1;u++)if(e(t[r],t[r+1],t[u],t[u+1]))return!1;for(r=1;r<t.length-2;r++)if(e(t[0],t[t.length-1],t[r],t[r+1]))return!1;return!0},removeCollinearPoints:function(n,r){for(var t=0,e=n.length-1;n.length>3&&e>=0;--e)c(s(n,e-1),s(n,e),s(n,e+1),r)&&(n.splice(e%n.length,1),t++);return t},removeDuplicatePoints:function(n,r){for(var t=n.length-1;t>=1;--t)for(var e=n[t],u=t-1;u>=0;--u)w(e,n[u],r)&&n.splice(t,1)},makeCCW:function(n){for(var r=0,t=n,e=1;e<n.length;++e)(t[e][1]<t[r][1]||t[e][1]===t[r][1]&&t[e][0]>t[r][0])&&(r=e);return!f(s(n,r-1),s(n,r),s(n,r+1))&&(function(n){for(var r=[],t=n.length,e=0;e!==t;e++)r.push(n.pop());for(e=0;e!==t;e++)n[e]=r[e]}(n),!0)}};var a=[],l=[];function c(n,r,t,e){if(e){var f=a,o=l;f[0]=r[0]-n[0],f[1]=r[1]-n[1],o[0]=t[0]-r[0],o[1]=t[1]-r[1];var i=f[0]*o[0]+f[1]*o[1],h=Math.sqrt(f[0]*f[0]+f[1]*f[1]),c=Math.sqrt(o[0]*o[0]+o[1]*o[1]);return Math.acos(i/(h*c))<e}return 0===u(n,r,t)}function g(n,r){var t=r[0]-n[0],e=r[1]-n[1];return t*t+e*e}function s(n,r){var t=n.length;return n[r<0?r%t+t:r%t]}function v(n,r,t,e){for(var u=t;u<e;u++)n.push(r[u])}function p(n,r){return i(s(n,r-1),s(n,r),s(n,r+1))}var m=[],A=[];function M(n,r,e){var u,f,i=m,a=A;if(o(s(n,r+1),s(n,r),s(n,e))&&h(s(n,r-1),s(n,r),s(n,e)))return!1;f=g(s(n,r),s(n,e));for(var l=0;l!==n.length;++l)if((l+1)%n.length!==r&&l!==r&&o(s(n,r),s(n,e),s(n,l+1))&&h(s(n,r),s(n,e),s(n,l))&&(i[0]=s(n,r),i[1]=s(n,e),a[0]=s(n,l),a[1]=s(n,l+1),u=t(i,a),g(s(n,r),u)<f))return!1;return!0}function b(n,r,t){for(var u=0;u!==n.length;++u)if(u!==r&&u!==t&&(u+1)%n.length!==r&&(u+1)%n.length!==t&&e(s(n,r),s(n,t),s(n,u),s(n,u+1)))return!1;return!0}function d(n,r,t,e){var u=e||[];if(function(n){n.length=0}(u),r<t)for(var f=r;f<=t;f++)u.push(n[f]);else{for(f=0;f<=t;f++)u.push(n[f]);for(f=r;f<n.length;f++)u.push(n[f])}return u}function k(n){for(var r=[],t=[],e=[],u=[],f=Number.MAX_VALUE,o=0;o<n.length;++o)if(p(n,o))for(var i=0;i<n.length;++i)if(M(n,o,i)){t=k(d(n,o,i,u)),e=k(d(n,i,o,u));for(var h=0;h<e.length;h++)t.push(e[h]);t.length<f&&(r=t,f=t.length,r.push([s(n,o),s(n,i)]))}return r}function D(n,r){if(0===r.length)return[n];if(r instanceof Array&&r.length&&r[0]instanceof Array&&2===r[0].length&&r[0][0]instanceof Array){for(var t=[n],e=0;e<r.length;e++)for(var u=r[e],f=0;f<t.length;f++){var o=D(t[f],u);if(o){t.splice(f,1),t.push(o[0],o[1]);break}}return t}u=r,e=n.indexOf(u[0]),f=n.indexOf(u[1]);return-1!==e&&-1!==f&&[d(n,e,f),d(n,f,e)]}function q(n,r,t,e,u){u=u||0;var f=r[1]-n[1],o=n[0]-r[0],i=f*n[0]+o*n[1],h=e[1]-t[1],a=t[0]-e[0],l=h*t[0]+a*t[1],c=f*a-h*o;return x(c,0,u)?[0,0]:[(a*i-o*l)/c,(f*l-h*i)/c]}function x(n,r,t){return t=t||0,Math.abs(n-r)<=t}function w(n,r,t){return x(n[0],r[0],t)&&x(n[1],r[1],t)}}}]);

View file

@ -1,604 +0,0 @@
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.decomp=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
module.exports = {
decomp: polygonDecomp,
quickDecomp: polygonQuickDecomp,
isSimple: polygonIsSimple,
removeCollinearPoints: polygonRemoveCollinearPoints,
makeCCW: polygonMakeCCW
};
/**
* Compute the intersection between two lines.
* @static
* @method lineInt
* @param {Array} l1 Line vector 1
* @param {Array} l2 Line vector 2
* @param {Number} precision Precision to use when checking if the lines are parallel
* @return {Array} The intersection point.
*/
function lineInt(l1,l2,precision){
precision = precision || 0;
var i = [0,0]; // point
var a1, b1, c1, a2, b2, c2, det; // scalars
a1 = l1[1][1] - l1[0][1];
b1 = l1[0][0] - l1[1][0];
c1 = a1 * l1[0][0] + b1 * l1[0][1];
a2 = l2[1][1] - l2[0][1];
b2 = l2[0][0] - l2[1][0];
c2 = a2 * l2[0][0] + b2 * l2[0][1];
det = a1 * b2 - a2*b1;
if (!scalar_eq(det, 0, precision)) { // lines are not parallel
i[0] = (b2 * c1 - b1 * c2) / det;
i[1] = (a1 * c2 - a2 * c1) / det;
}
return i;
}
/**
* Checks if two line segments intersects.
* @method segmentsIntersect
* @param {Array} p1 The start vertex of the first line segment.
* @param {Array} p2 The end vertex of the first line segment.
* @param {Array} q1 The start vertex of the second line segment.
* @param {Array} q2 The end vertex of the second line segment.
* @return {Boolean} True if the two line segments intersect
*/
function lineSegmentsIntersect(p1, p2, q1, q2){
var dx = p2[0] - p1[0];
var dy = p2[1] - p1[1];
var da = q2[0] - q1[0];
var db = q2[1] - q1[1];
// segments are parallel
if((da*dy - db*dx) === 0){
return false;
}
var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx);
var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy);
return (s>=0 && s<=1 && t>=0 && t<=1);
}
/**
* Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order.
* @static
* @method area
* @param {Array} a
* @param {Array} b
* @param {Array} c
* @return {Number}
*/
function triangleArea(a,b,c){
return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1])));
}
function isLeft(a,b,c){
return triangleArea(a,b,c) > 0;
}
function isLeftOn(a,b,c) {
return triangleArea(a, b, c) >= 0;
}
function isRight(a,b,c) {
return triangleArea(a, b, c) < 0;
}
function isRightOn(a,b,c) {
return triangleArea(a, b, c) <= 0;
}
var tmpPoint1 = [],
tmpPoint2 = [];
/**
* Check if three points are collinear
* @method collinear
* @param {Array} a
* @param {Array} b
* @param {Array} c
* @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision.
* @return {Boolean}
*/
function collinear(a,b,c,thresholdAngle) {
if(!thresholdAngle){
return triangleArea(a, b, c) === 0;
} else {
var ab = tmpPoint1,
bc = tmpPoint2;
ab[0] = b[0]-a[0];
ab[1] = b[1]-a[1];
bc[0] = c[0]-b[0];
bc[1] = c[1]-b[1];
var dot = ab[0]*bc[0] + ab[1]*bc[1],
magA = Math.sqrt(ab[0]*ab[0] + ab[1]*ab[1]),
magB = Math.sqrt(bc[0]*bc[0] + bc[1]*bc[1]),
angle = Math.acos(dot/(magA*magB));
return angle < thresholdAngle;
}
}
function sqdist(a,b){
var dx = b[0] - a[0];
var dy = b[1] - a[1];
return dx * dx + dy * dy;
}
/**
* Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle.
* @method at
* @param {Number} i
* @return {Array}
*/
function polygonAt(polygon, i){
var s = polygon.length;
return polygon[i < 0 ? i % s + s : i % s];
}
/**
* Clear the polygon data
* @method clear
* @return {Array}
*/
function polygonClear(polygon){
polygon.length = 0;
}
/**
* Append points "from" to "to"-1 from an other polygon "poly" onto this one.
* @method append
* @param {Polygon} poly The polygon to get points from.
* @param {Number} from The vertex index in "poly".
* @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending.
* @return {Array}
*/
function polygonAppend(polygon, poly, from, to){
for(var i=from; i<to; i++){
polygon.push(poly[i]);
}
}
/**
* Make sure that the polygon vertices are ordered counter-clockwise.
* @method makeCCW
*/
function polygonMakeCCW(polygon){
var br = 0,
v = polygon;
// find bottom right point
for (var i = 1; i < polygon.length; ++i) {
if (v[i][1] < v[br][1] || (v[i][1] === v[br][1] && v[i][0] > v[br][0])) {
br = i;
}
}
// reverse poly if clockwise
if (!isLeft(polygonAt(polygon, br - 1), polygonAt(polygon, br), polygonAt(polygon, br + 1))) {
polygonReverse(polygon);
}
}
/**
* Reverse the vertices in the polygon
* @method reverse
*/
function polygonReverse(polygon){
var tmp = [];
var N = polygon.length;
for(var i=0; i!==N; i++){
tmp.push(polygon.pop());
}
for(var i=0; i!==N; i++){
polygon[i] = tmp[i];
}
}
/**
* Check if a point in the polygon is a reflex point
* @method isReflex
* @param {Number} i
* @return {Boolean}
*/
function polygonIsReflex(polygon, i){
return isRight(polygonAt(polygon, i - 1), polygonAt(polygon, i), polygonAt(polygon, i + 1));
}
var tmpLine1=[],
tmpLine2=[];
/**
* Check if two vertices in the polygon can see each other
* @method canSee
* @param {Number} a Vertex index 1
* @param {Number} b Vertex index 2
* @return {Boolean}
*/
function polygonCanSee(polygon, a,b) {
var p, dist, l1=tmpLine1, l2=tmpLine2;
if (isLeftOn(polygonAt(polygon, a + 1), polygonAt(polygon, a), polygonAt(polygon, b)) && isRightOn(polygonAt(polygon, a - 1), polygonAt(polygon, a), polygonAt(polygon, b))) {
return false;
}
dist = sqdist(polygonAt(polygon, a), polygonAt(polygon, b));
for (var i = 0; i !== polygon.length; ++i) { // for each edge
if ((i + 1) % polygon.length === a || i === a){ // ignore incident edges
continue;
}
if (isLeftOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i + 1)) && isRightOn(polygonAt(polygon, a), polygonAt(polygon, b), polygonAt(polygon, i))) { // if diag intersects an edge
l1[0] = polygonAt(polygon, a);
l1[1] = polygonAt(polygon, b);
l2[0] = polygonAt(polygon, i);
l2[1] = polygonAt(polygon, i + 1);
p = lineInt(l1,l2);
if (sqdist(polygonAt(polygon, a), p) < dist) { // if edge is blocking visibility to b
return false;
}
}
}
return true;
}
/**
* Copy the polygon from vertex i to vertex j.
* @method copy
* @param {Number} i
* @param {Number} j
* @param {Polygon} [targetPoly] Optional target polygon to save in.
* @return {Polygon} The resulting copy.
*/
function polygonCopy(polygon, i,j,targetPoly){
var p = targetPoly || [];
polygonClear(p);
if (i < j) {
// Insert all vertices from i to j
for(var k=i; k<=j; k++){
p.push(polygon[k]);
}
} else {
// Insert vertices 0 to j
for(var k=0; k<=j; k++){
p.push(polygon[k]);
}
// Insert vertices i to end
for(var k=i; k<polygon.length; k++){
p.push(polygon[k]);
}
}
return p;
}
/**
* Decomposes the polygon into convex pieces. Returns a list of edges [[p1,p2],[p2,p3],...] that cuts the polygon.
* Note that this algorithm has complexity O(N^4) and will be very slow for polygons with many vertices.
* @method getCutEdges
* @return {Array}
*/
function polygonGetCutEdges(polygon) {
var min=[], tmp1=[], tmp2=[], tmpPoly = [];
var nDiags = Number.MAX_VALUE;
for (var i = 0; i < polygon.length; ++i) {
if (polygonIsReflex(polygon, i)) {
for (var j = 0; j < polygon.length; ++j) {
if (polygonCanSee(polygon, i, j)) {
tmp1 = polygonGetCutEdges(polygonCopy(polygon, i, j, tmpPoly));
tmp2 = polygonGetCutEdges(polygonCopy(polygon, j, i, tmpPoly));
for(var k=0; k<tmp2.length; k++){
tmp1.push(tmp2[k]);
}
if (tmp1.length < nDiags) {
min = tmp1;
nDiags = tmp1.length;
min.push([polygonAt(polygon, i), polygonAt(polygon, j)]);
}
}
}
}
}
return min;
}
/**
* Decomposes the polygon into one or more convex sub-Polygons.
* @method decomp
* @return {Array} An array or Polygon objects.
*/
function polygonDecomp(polygon){
var edges = polygonGetCutEdges(polygon);
if(edges.length > 0){
return polygonSlice(polygon, edges);
} else {
return [polygon];
}
}
/**
* Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons.
* @method slice
* @param {Array} cutEdges A list of edges, as returned by .getCutEdges()
* @return {Array}
*/
function polygonSlice(polygon, cutEdges){
if(cutEdges.length === 0){
return [polygon];
}
if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length===2 && cutEdges[0][0] instanceof Array){
var polys = [polygon];
for(var i=0; i<cutEdges.length; i++){
var cutEdge = cutEdges[i];
// Cut all polys
for(var j=0; j<polys.length; j++){
var poly = polys[j];
var result = polygonSlice(poly, cutEdge);
if(result){
// Found poly! Cut and quit
polys.splice(j,1);
polys.push(result[0],result[1]);
break;
}
}
}
return polys;
} else {
// Was given one edge
var cutEdge = cutEdges;
var i = polygon.indexOf(cutEdge[0]);
var j = polygon.indexOf(cutEdge[1]);
if(i !== -1 && j !== -1){
return [polygonCopy(polygon, i,j),
polygonCopy(polygon, j,i)];
} else {
return false;
}
}
}
/**
* Checks that the line segments of this polygon do not intersect each other.
* @method isSimple
* @param {Array} path An array of vertices e.g. [[0,0],[0,1],...]
* @return {Boolean}
* @todo Should it check all segments with all others?
*/
function polygonIsSimple(polygon){
var path = polygon, i;
// Check
for(i=0; i<path.length-1; i++){
for(var j=0; j<i-1; j++){
if(lineSegmentsIntersect(path[i], path[i+1], path[j], path[j+1] )){
return false;
}
}
}
// Check the segment between the last and the first point to all others
for(i=1; i<path.length-2; i++){
if(lineSegmentsIntersect(path[0], path[path.length-1], path[i], path[i+1] )){
return false;
}
}
return true;
}
function getIntersectionPoint(p1, p2, q1, q2, delta){
delta = delta || 0;
var a1 = p2[1] - p1[1];
var b1 = p1[0] - p2[0];
var c1 = (a1 * p1[0]) + (b1 * p1[1]);
var a2 = q2[1] - q1[1];
var b2 = q1[0] - q2[0];
var c2 = (a2 * q1[0]) + (b2 * q1[1]);
var det = (a1 * b2) - (a2 * b1);
if(!scalar_eq(det,0,delta)){
return [((b2 * c1) - (b1 * c2)) / det, ((a1 * c2) - (a2 * c1)) / det];
} else {
return [0,0];
}
}
/**
* Quickly decompose the Polygon into convex sub-polygons.
* @method quickDecomp
* @param {Array} result
* @param {Array} [reflexVertices]
* @param {Array} [steinerPoints]
* @param {Number} [delta]
* @param {Number} [maxlevel]
* @param {Number} [level]
* @return {Array}
*/
function polygonQuickDecomp(polygon, result,reflexVertices,steinerPoints,delta,maxlevel,level){
maxlevel = maxlevel || 100;
level = level || 0;
delta = delta || 25;
result = typeof(result)!=="undefined" ? result : [];
reflexVertices = reflexVertices || [];
steinerPoints = steinerPoints || [];
var upperInt=[0,0], lowerInt=[0,0], p=[0,0]; // Points
var upperDist=0, lowerDist=0, d=0, closestDist=0; // scalars
var upperIndex=0, lowerIndex=0, closestIndex=0; // Integers
var lowerPoly=[], upperPoly=[]; // polygons
var poly = polygon,
v = polygon;
if(v.length < 3){
return result;
}
level++;
if(level > maxlevel){
console.warn("quickDecomp: max level ("+maxlevel+") reached.");
return result;
}
for (var i = 0; i < polygon.length; ++i) {
if (polygonIsReflex(poly, i)) {
reflexVertices.push(poly[i]);
upperDist = lowerDist = Number.MAX_VALUE;
for (var j = 0; j < polygon.length; ++j) {
if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j - 1))) { // if line intersects with an edge
p = getIntersectionPoint(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j - 1)); // find the point of intersection
if (isRight(polygonAt(poly, i + 1), polygonAt(poly, i), p)) { // make sure it's inside the poly
d = sqdist(poly[i], p);
if (d < lowerDist) { // keep only the closest intersection
lowerDist = d;
lowerInt = p;
lowerIndex = j;
}
}
}
if (isLeft(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j + 1)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) {
p = getIntersectionPoint(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j), polygonAt(poly, j + 1));
if (isLeft(polygonAt(poly, i - 1), polygonAt(poly, i), p)) {
d = sqdist(poly[i], p);
if (d < upperDist) {
upperDist = d;
upperInt = p;
upperIndex = j;
}
}
}
}
// if there are no vertices to connect to, choose a point in the middle
if (lowerIndex === (upperIndex + 1) % polygon.length) {
//console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+polygon.length+")");
p[0] = (lowerInt[0] + upperInt[0]) / 2;
p[1] = (lowerInt[1] + upperInt[1]) / 2;
steinerPoints.push(p);
if (i < upperIndex) {
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1);
polygonAppend(lowerPoly, poly, i, upperIndex+1);
lowerPoly.push(p);
upperPoly.push(p);
if (lowerIndex !== 0){
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end());
polygonAppend(upperPoly, poly,lowerIndex,poly.length);
}
//upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1);
polygonAppend(upperPoly, poly,0,i+1);
} else {
if (i !== 0){
//lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end());
polygonAppend(lowerPoly, poly,i,poly.length);
}
//lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1);
polygonAppend(lowerPoly, poly,0,upperIndex+1);
lowerPoly.push(p);
upperPoly.push(p);
//upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1);
polygonAppend(upperPoly, poly,lowerIndex,i+1);
}
} else {
// connect to the closest point within the triangle
//console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+polygon.length+")\n");
if (lowerIndex > upperIndex) {
upperIndex += polygon.length;
}
closestDist = Number.MAX_VALUE;
if(upperIndex < lowerIndex){
return result;
}
for (var j = lowerIndex; j <= upperIndex; ++j) {
if (isLeftOn(polygonAt(poly, i - 1), polygonAt(poly, i), polygonAt(poly, j)) && isRightOn(polygonAt(poly, i + 1), polygonAt(poly, i), polygonAt(poly, j))) {
d = sqdist(polygonAt(poly, i), polygonAt(poly, j));
if (d < closestDist) {
closestDist = d;
closestIndex = j % polygon.length;
}
}
}
if (i < closestIndex) {
polygonAppend(lowerPoly, poly,i,closestIndex+1);
if (closestIndex !== 0){
polygonAppend(upperPoly, poly,closestIndex,v.length);
}
polygonAppend(upperPoly, poly,0,i+1);
} else {
if (i !== 0){
polygonAppend(lowerPoly, poly,i,v.length);
}
polygonAppend(lowerPoly, poly,0,closestIndex+1);
polygonAppend(upperPoly, poly,closestIndex,i+1);
}
}
// solve smallest poly first
if (lowerPoly.length < upperPoly.length) {
polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
} else {
polygonQuickDecomp(upperPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
polygonQuickDecomp(lowerPoly,result,reflexVertices,steinerPoints,delta,maxlevel,level);
}
return result;
}
}
result.push(polygon);
return result;
}
/**
* Remove collinear points in the polygon.
* @method removeCollinearPoints
* @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision.
* @return {Number} The number of points removed
*/
function polygonRemoveCollinearPoints(polygon, precision){
var num = 0;
for(var i=polygon.length-1; polygon.length>3 && i>=0; --i){
if(collinear(polygonAt(polygon, i-1),polygonAt(polygon, i),polygonAt(polygon, i+1),precision)){
// Remove the middle point
polygon.splice(i%polygon.length,1);
num++;
}
}
return num;
}
/**
* Check if two scalars are equal
* @static
* @method eq
* @param {Number} a
* @param {Number} b
* @param {Number} [precision]
* @return {Boolean}
*/
function scalar_eq(a,b,precision){
precision = precision || 0;
return Math.abs(a-b) < precision;
}
},{}]},{},[1])
(1)
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,275 +0,0 @@
/*!
* matter-wrap 0.2.0 by Liam Brummitt 2017-07-04
* https://github.com/liabru/matter-wrap
* License MIT
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("matter-js"));
else if(typeof define === 'function' && define.amd)
define(["matter-js"], factory);
else if(typeof exports === 'object')
exports["MatterWrap"] = factory(require("matter-js"));
else
root["MatterWrap"] = factory(root["Matter"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/libs";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var Matter = __webpack_require__(0);
/**
* A coordinate wrapping plugin for matter.js.
* See the readme for usage and examples.
* @module MatterWrap
*/
var MatterWrap = {
// plugin meta
name: 'matter-wrap', // PLUGIN_NAME
version: '0.1.3', // PLUGIN_VERSION
for: 'matter-js@^0.12.0',
// installs the plugin where `base` is `Matter`
// you should not need to call this directly.
install: function install(base) {
base.after('Engine.update', function () {
MatterWrap.Engine.update(this);
});
},
Engine: {
/**
* Updates the engine by wrapping bodies and composites inside `engine.world`.
* This is called automatically by the plugin.
* @function MatterWrap.Engine.update
* @param {Matter.Engine} engine The engine to update.
* @returns {void} No return value.
*/
update: function update(engine) {
var world = engine.world,
bodies = Matter.Composite.allBodies(world),
composites = Matter.Composite.allComposites(world);
for (var i = 0; i < bodies.length; i += 1) {
var body = bodies[i];
if (body.plugin.wrap) {
MatterWrap.Body.wrap(body, body.plugin.wrap);
}
}
for (i = 0; i < composites.length; i += 1) {
var composite = composites[i];
if (composite.plugin.wrap) {
MatterWrap.Composite.wrap(composite, composite.plugin.wrap);
}
}
}
},
Bounds: {
/**
* Returns a translation vector that wraps the `objectBounds` inside the `bounds`.
* @function MatterWrap.Bounds.wrap
* @param {Matter.Bounds} objectBounds The bounds of the object to wrap inside the bounds.
* @param {Matter.Bounds} bounds The bounds to wrap the body inside.
* @returns {?Matter.Vector} A translation vector (only if wrapping is required).
*/
wrap: function wrap(objectBounds, bounds) {
var x = null,
y = null;
if (typeof bounds.min.x !== 'undefined' && typeof bounds.max.x !== 'undefined') {
if (objectBounds.min.x > bounds.max.x) {
x = bounds.min.x - objectBounds.max.x;
} else if (objectBounds.max.x < bounds.min.x) {
x = bounds.max.x - objectBounds.min.x;
}
}
if (typeof bounds.min.y !== 'undefined' && typeof bounds.max.y !== 'undefined') {
if (objectBounds.min.y > bounds.max.y) {
y = bounds.min.y - objectBounds.max.y;
} else if (objectBounds.max.y < bounds.min.y) {
y = bounds.max.y - objectBounds.min.y;
}
}
if (x !== null || y !== null) {
return {
x: x || 0,
y: y || 0
};
}
}
},
Body: {
/**
* Wraps the `body` position such that it always stays within the given bounds.
* Upon crossing a boundary the body will appear on the opposite side of the bounds,
* while maintaining its velocity.
* This is called automatically by the plugin.
* @function MatterWrap.Body.wrap
* @param {Matter.Body} body The body to wrap.
* @param {Matter.Bounds} bounds The bounds to wrap the body inside.
* @returns {?Matter.Vector} The translation vector that was applied (only if wrapping was required).
*/
wrap: function wrap(body, bounds) {
var translation = MatterWrap.Bounds.wrap(body.bounds, bounds);
if (translation) {
Matter.Body.translate(body, translation);
}
return translation;
}
},
Composite: {
/**
* Returns the union of the bounds of all of the composite's bodies
* (not accounting for constraints).
* @function MatterWrap.Composite.bounds
* @param {Matter.Composite} composite The composite.
* @returns {Matter.Bounds} The composite bounds.
*/
bounds: function bounds(composite) {
var bodies = Matter.Composite.allBodies(composite),
vertices = [];
for (var i = 0; i < bodies.length; i += 1) {
var body = bodies[i];
vertices.push(body.bounds.min, body.bounds.max);
}
return Matter.Bounds.create(vertices);
},
/**
* Wraps the `composite` position such that it always stays within the given bounds.
* Upon crossing a boundary the composite will appear on the opposite side of the bounds,
* while maintaining its velocity.
* This is called automatically by the plugin.
* @function MatterWrap.Composite.wrap
* @param {Matter.Composite} composite The composite to wrap.
* @param {Matter.Bounds} bounds The bounds to wrap the composite inside.
* @returns {?Matter.Vector} The translation vector that was applied (only if wrapping was required).
*/
wrap: function wrap(composite, bounds) {
var translation = MatterWrap.Bounds.wrap(MatterWrap.Composite.bounds(composite), bounds);
if (translation) {
Matter.Composite.translate(composite, translation);
}
return translation;
}
}
};
Matter.Plugin.register(MatterWrap);
module.exports = MatterWrap;
/**
* @namespace Matter.Body
* @see http://brm.io/matter-js/docs/classes/Body.html
*/
/**
* This plugin adds a new property `body.plugin.wrap` to instances of `Matter.Body`.
* This is a `Matter.Bounds` instance that specifies the wrapping region.
* @property {Matter.Bounds} body.plugin.wrap
* @memberof Matter.Body
*/
/**
* This plugin adds a new property `composite.plugin.wrap` to instances of `Matter.Composite`.
* This is a `Matter.Bounds` instance that specifies the wrapping region.
* @property {Matter.Bounds} composite.plugin.wrap
* @memberof Matter.Composite
*/
/***/ })
/******/ ]);
});

View file

@ -1,849 +0,0 @@
// SVGPathSeg API polyfill
// https://github.com/progers/pathseg
//
// This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from
// SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec
// changes which were implemented in Firefox 43 and Chrome 46.
(function() { "use strict";
if (!("SVGPathSeg" in window)) {
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg
window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) {
this.pathSegType = type;
this.pathSegTypeAsLetter = typeAsLetter;
this._owningPathSegList = owningPathSegList;
}
window.SVGPathSeg.prototype.classname = "SVGPathSeg";
window.SVGPathSeg.PATHSEG_UNKNOWN = 0;
window.SVGPathSeg.PATHSEG_CLOSEPATH = 1;
window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2;
window.SVGPathSeg.PATHSEG_MOVETO_REL = 3;
window.SVGPathSeg.PATHSEG_LINETO_ABS = 4;
window.SVGPathSeg.PATHSEG_LINETO_REL = 5;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9;
window.SVGPathSeg.PATHSEG_ARC_ABS = 10;
window.SVGPathSeg.PATHSEG_ARC_REL = 11;
window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12;
window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13;
window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14;
window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16;
window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
// Notify owning PathSegList on any changes so they can be synchronized back to the path element.
window.SVGPathSeg.prototype._segmentChanged = function() {
if (this._owningPathSegList)
this._owningPathSegList.segmentChanged(this);
}
window.SVGPathSegClosePath = function(owningPathSegList) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList);
}
window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegClosePath.prototype.toString = function() { return "[object SVGPathSegClosePath]"; }
window.SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter; }
window.SVGPathSegClosePath.prototype.clone = function() { return new window.SVGPathSegClosePath(undefined); }
window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegMovetoAbs.prototype.toString = function() { return "[object SVGPathSegMovetoAbs]"; }
window.SVGPathSegMovetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegMovetoAbs.prototype.clone = function() { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegMovetoRel.prototype.toString = function() { return "[object SVGPathSegMovetoRel]"; }
window.SVGPathSegMovetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegMovetoRel.prototype.clone = function() { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoAbs.prototype.toString = function() { return "[object SVGPathSegLinetoAbs]"; }
window.SVGPathSegLinetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegLinetoAbs.prototype.clone = function() { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoRel.prototype.toString = function() { return "[object SVGPathSegLinetoRel]"; }
window.SVGPathSegLinetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegLinetoRel.prototype.clone = function() { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicAbs = function(owningPathSegList, x, y, x1, y1, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicAbs]"; }
window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicRel = function(owningPathSegList, x, y, x1, y1, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicRel]"; }
window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticAbs = function(owningPathSegList, x, y, x1, y1) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
}
window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticAbs]"; }
window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticRel = function(owningPathSegList, x, y, x1, y1) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList);
this._x = x;
this._y = y;
this._x1 = x1;
this._y1 = y1;
}
window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticRel]"; }
window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x1", { get: function() { return this._x1; }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y1", { get: function() { return this._y1; }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegArcAbs = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList);
this._x = x;
this._y = y;
this._r1 = r1;
this._r2 = r2;
this._angle = angle;
this._largeArcFlag = largeArcFlag;
this._sweepFlag = sweepFlag;
}
window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegArcAbs.prototype.toString = function() { return "[object SVGPathSegArcAbs]"; }
window.SVGPathSegArcAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; }
window.SVGPathSegArcAbs.prototype.clone = function() { return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcAbs.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegArcRel = function(owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList);
this._x = x;
this._y = y;
this._r1 = r1;
this._r2 = r2;
this._angle = angle;
this._largeArcFlag = largeArcFlag;
this._sweepFlag = sweepFlag;
}
window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegArcRel.prototype.toString = function() { return "[object SVGPathSegArcRel]"; }
window.SVGPathSegArcRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y; }
window.SVGPathSegArcRel.prototype.clone = function() { return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }
Object.defineProperty(window.SVGPathSegArcRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "r1", { get: function() { return this._r1; }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "r2", { get: function() { return this._r2; }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "angle", { get: function() { return this._angle; }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "largeArcFlag", { get: function() { return this._largeArcFlag; }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegArcRel.prototype, "sweepFlag", { get: function() { return this._sweepFlag; }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList);
this._x = x;
}
window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalAbs]"; }
window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; }
window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x); }
Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList);
this._x = x;
}
window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return "[object SVGPathSegLinetoHorizontalRel]"; }
window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x; }
window.SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x); }
Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList);
this._y = y;
}
window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalAbs]"; }
window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; }
window.SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y); }
Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList);
this._y = y;
}
window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegLinetoVerticalRel.prototype.toString = function() { return "[object SVGPathSegLinetoVerticalRel]"; }
window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._y; }
window.SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y); }
Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicSmoothAbs = function(owningPathSegList, x, y, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList);
this._x = x;
this._y = y;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothAbs]"; }
window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoCubicSmoothRel = function(owningPathSegList, x, y, x2, y2) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList);
this._x = x;
this._y = y;
this._x2 = x2;
this._y2 = y2;
}
window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoCubicSmoothRel]"; }
window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); }
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", { get: function() { return this._x2; }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", { get: function() { return this._y2; }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticSmoothAbs = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothAbs]"; }
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
window.SVGPathSegCurvetoQuadraticSmoothRel = function(owningPathSegList, x, y) {
window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList);
this._x = x;
this._y = y;
}
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return "[object SVGPathSegCurvetoQuadraticSmoothRel]"; }
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + " " + this._x + " " + this._y; }
window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); }
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", { get: function() { return this._x; }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true });
Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", { get: function() { return this._y; }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true });
// Add createSVGPathSeg* functions to window.SVGPathElement.
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement.
window.SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new window.SVGPathSegClosePath(undefined); }
window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function(x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function(x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function(x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function(x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); }
window.SVGPathElement.prototype.createSVGPathSegArcAbs = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }
window.SVGPathElement.prototype.createSVGPathSegArcRel = function(x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }
window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function(x) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x); }
window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function(x) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x); }
window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function(y) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y); }
window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function(y) { return new window.SVGPathSegLinetoVerticalRel(undefined, y); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function(x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function(x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function(x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); }
window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function(x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); }
if (!("getPathSegAtLength" in window.SVGPathElement.prototype)) {
// Add getPathSegAtLength to SVGPathElement.
// Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength
// This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm.
window.SVGPathElement.prototype.getPathSegAtLength = function(distance) {
if (distance === undefined || !isFinite(distance))
throw "Invalid arguments.";
var measurementElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
measurementElement.setAttribute("d", this.getAttribute("d"));
var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1;
// If the path is empty, return 0.
if (lastPathSegment <= 0)
return 0;
do {
measurementElement.pathSegList.removeItem(lastPathSegment);
if (distance > measurementElement.getTotalLength())
break;
lastPathSegment--;
} while (lastPathSegment > 0);
return lastPathSegment;
}
}
}
// Checking for SVGPathSegList in window checks for the case of an implementation without the
// SVGPathSegList API.
// The second check for appendItem is specific to Firefox 59+ which removed only parts of the
// SVGPathSegList API (e.g., appendItem). In this case we need to re-implement the entire API
// so the polyfill data (i.e., _list) is used throughout.
if (!("SVGPathSegList" in window) || !("appendItem" in window.SVGPathSegList.prototype)) {
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList
window.SVGPathSegList = function(pathElement) {
this._pathElement = pathElement;
this._list = this._parsePath(this._pathElement.getAttribute("d"));
// Use a MutationObserver to catch changes to the path's "d" attribute.
this._mutationObserverConfig = { "attributes": true, "attributeFilter": ["d"] };
this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this));
this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
}
window.SVGPathSegList.prototype.classname = "SVGPathSegList";
Object.defineProperty(window.SVGPathSegList.prototype, "numberOfItems", {
get: function() {
this._checkPathSynchronizedToList();
return this._list.length;
},
enumerable: true
});
// Add the pathSegList accessors to window.SVGPathElement.
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData
Object.defineProperty(window.SVGPathElement.prototype, "pathSegList", {
get: function() {
if (!this._pathSegList)
this._pathSegList = new window.SVGPathSegList(this);
return this._pathSegList;
},
enumerable: true
});
// FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList.
Object.defineProperty(window.SVGPathElement.prototype, "normalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true });
Object.defineProperty(window.SVGPathElement.prototype, "animatedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true });
Object.defineProperty(window.SVGPathElement.prototype, "animatedNormalizedPathSegList", { get: function() { return this.pathSegList; }, enumerable: true });
// Process any pending mutations to the path element and update the list as needed.
// This should be the first call of all public functions and is needed because
// MutationObservers are not synchronous so we can have pending asynchronous mutations.
window.SVGPathSegList.prototype._checkPathSynchronizedToList = function() {
this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords());
}
window.SVGPathSegList.prototype._updateListFromPathMutations = function(mutationRecords) {
if (!this._pathElement)
return;
var hasPathMutations = false;
mutationRecords.forEach(function(record) {
if (record.attributeName == "d")
hasPathMutations = true;
});
if (hasPathMutations)
this._list = this._parsePath(this._pathElement.getAttribute("d"));
}
// Serialize the list and update the path's 'd' attribute.
window.SVGPathSegList.prototype._writeListToPath = function() {
this._pathElementMutationObserver.disconnect();
this._pathElement.setAttribute("d", window.SVGPathSegList._pathSegArrayAsString(this._list));
this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
}
// When a path segment changes the list needs to be synchronized back to the path element.
window.SVGPathSegList.prototype.segmentChanged = function(pathSeg) {
this._writeListToPath();
}
window.SVGPathSegList.prototype.clear = function() {
this._checkPathSynchronizedToList();
this._list.forEach(function(pathSeg) {
pathSeg._owningPathSegList = null;
});
this._list = [];
this._writeListToPath();
}
window.SVGPathSegList.prototype.initialize = function(newItem) {
this._checkPathSynchronizedToList();
this._list = [newItem];
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
}
window.SVGPathSegList.prototype._checkValidIndex = function(index) {
if (isNaN(index) || index < 0 || index >= this.numberOfItems)
throw "INDEX_SIZE_ERR";
}
window.SVGPathSegList.prototype.getItem = function(index) {
this._checkPathSynchronizedToList();
this._checkValidIndex(index);
return this._list[index];
}
window.SVGPathSegList.prototype.insertItemBefore = function(newItem, index) {
this._checkPathSynchronizedToList();
// Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
if (index > this.numberOfItems)
index = this.numberOfItems;
if (newItem._owningPathSegList) {
// SVG2 spec says to make a copy.
newItem = newItem.clone();
}
this._list.splice(index, 0, newItem);
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
}
window.SVGPathSegList.prototype.replaceItem = function(newItem, index) {
this._checkPathSynchronizedToList();
if (newItem._owningPathSegList) {
// SVG2 spec says to make a copy.
newItem = newItem.clone();
}
this._checkValidIndex(index);
this._list[index] = newItem;
newItem._owningPathSegList = this;
this._writeListToPath();
return newItem;
}
window.SVGPathSegList.prototype.removeItem = function(index) {
this._checkPathSynchronizedToList();
this._checkValidIndex(index);
var item = this._list[index];
this._list.splice(index, 1);
this._writeListToPath();
return item;
}
window.SVGPathSegList.prototype.appendItem = function(newItem) {
this._checkPathSynchronizedToList();
if (newItem._owningPathSegList) {
// SVG2 spec says to make a copy.
newItem = newItem.clone();
}
this._list.push(newItem);
newItem._owningPathSegList = this;
// TODO: Optimize this to just append to the existing attribute.
this._writeListToPath();
return newItem;
}
window.SVGPathSegList._pathSegArrayAsString = function(pathSegArray) {
var string = "";
var first = true;
pathSegArray.forEach(function(pathSeg) {
if (first) {
first = false;
string += pathSeg._asPathString();
} else {
string += " " + pathSeg._asPathString();
}
});
return string;
}
// This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp.
window.SVGPathSegList.prototype._parsePath = function(string) {
if (!string || string.length == 0)
return [];
var owningPathSegList = this;
var Builder = function() {
this.pathSegList = [];
}
Builder.prototype.appendSegment = function(pathSeg) {
this.pathSegList.push(pathSeg);
}
var Source = function(string) {
this._string = string;
this._currentIndex = 0;
this._endIndex = this._string.length;
this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN;
this._skipOptionalSpaces();
}
Source.prototype._isCurrentSpace = function() {
var character = this._string[this._currentIndex];
return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f");
}
Source.prototype._skipOptionalSpaces = function() {
while (this._currentIndex < this._endIndex && this._isCurrentSpace())
this._currentIndex++;
return this._currentIndex < this._endIndex;
}
Source.prototype._skipOptionalSpacesOrDelimiter = function() {
if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ",")
return false;
if (this._skipOptionalSpaces()) {
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") {
this._currentIndex++;
this._skipOptionalSpaces();
}
}
return this._currentIndex < this._endIndex;
}
Source.prototype.hasMoreData = function() {
return this._currentIndex < this._endIndex;
}
Source.prototype.peekSegmentType = function() {
var lookahead = this._string[this._currentIndex];
return this._pathSegTypeFromChar(lookahead);
}
Source.prototype._pathSegTypeFromChar = function(lookahead) {
switch (lookahead) {
case "Z":
case "z":
return window.SVGPathSeg.PATHSEG_CLOSEPATH;
case "M":
return window.SVGPathSeg.PATHSEG_MOVETO_ABS;
case "m":
return window.SVGPathSeg.PATHSEG_MOVETO_REL;
case "L":
return window.SVGPathSeg.PATHSEG_LINETO_ABS;
case "l":
return window.SVGPathSeg.PATHSEG_LINETO_REL;
case "C":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;
case "c":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;
case "Q":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;
case "q":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;
case "A":
return window.SVGPathSeg.PATHSEG_ARC_ABS;
case "a":
return window.SVGPathSeg.PATHSEG_ARC_REL;
case "H":
return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;
case "h":
return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;
case "V":
return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;
case "v":
return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;
case "S":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
case "s":
return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
case "T":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
case "t":
return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
default:
return window.SVGPathSeg.PATHSEG_UNKNOWN;
}
}
Source.prototype._nextCommandHelper = function(lookahead, previousCommand) {
// Check for remaining coordinates in the current command.
if ((lookahead == "+" || lookahead == "-" || lookahead == "." || (lookahead >= "0" && lookahead <= "9")) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) {
if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS)
return window.SVGPathSeg.PATHSEG_LINETO_ABS;
if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL)
return window.SVGPathSeg.PATHSEG_LINETO_REL;
return previousCommand;
}
return window.SVGPathSeg.PATHSEG_UNKNOWN;
}
Source.prototype.initialCommandIsMoveTo = function() {
// If the path is empty it is still valid, so return true.
if (!this.hasMoreData())
return true;
var command = this.peekSegmentType();
// Path must start with moveTo.
return command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL;
}
// Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp.
// Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF
Source.prototype._parseNumber = function() {
var exponent = 0;
var integer = 0;
var frac = 1;
var decimal = 0;
var sign = 1;
var expsign = 1;
var startIndex = this._currentIndex;
this._skipOptionalSpaces();
// Read the sign.
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "+")
this._currentIndex++;
else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") {
this._currentIndex++;
sign = -1;
}
if (this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") && this._string.charAt(this._currentIndex) != "."))
// The first character of a number must be one of [0-9+-.].
return undefined;
// Read the integer part, build right-to-left.
var startIntPartIndex = this._currentIndex;
while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9")
this._currentIndex++; // Advance to first non-digit.
if (this._currentIndex != startIntPartIndex) {
var scanIntPartIndex = this._currentIndex - 1;
var multiplier = 1;
while (scanIntPartIndex >= startIntPartIndex) {
integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0");
multiplier *= 10;
}
}
// Read the decimals.
if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") {
this._currentIndex++;
// There must be a least one digit following the .
if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9")
return undefined;
while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
frac *= 10;
decimal += (this._string.charAt(this._currentIndex) - "0") / frac;
this._currentIndex += 1;
}
}
// Read the exponent part.
if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") && (this._string.charAt(this._currentIndex + 1) != "x" && this._string.charAt(this._currentIndex + 1) != "m")) {
this._currentIndex++;
// Read the sign of the exponent.
if (this._string.charAt(this._currentIndex) == "+") {
this._currentIndex++;
} else if (this._string.charAt(this._currentIndex) == "-") {
this._currentIndex++;
expsign = -1;
}
// There must be an exponent.
if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9")
return undefined;
while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
exponent *= 10;
exponent += (this._string.charAt(this._currentIndex) - "0");
this._currentIndex++;
}
}
var number = integer + decimal;
number *= sign;
if (exponent)
number *= Math.pow(10, expsign * exponent);
if (startIndex == this._currentIndex)
return undefined;
this._skipOptionalSpacesOrDelimiter();
return number;
}
Source.prototype._parseArcFlag = function() {
if (this._currentIndex >= this._endIndex)
return undefined;
var flag = false;
var flagChar = this._string.charAt(this._currentIndex++);
if (flagChar == "0")
flag = false;
else if (flagChar == "1")
flag = true;
else
return undefined;
this._skipOptionalSpacesOrDelimiter();
return flag;
}
Source.prototype.parseSegment = function() {
var lookahead = this._string[this._currentIndex];
var command = this._pathSegTypeFromChar(lookahead);
if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) {
// Possibly an implicit command. Not allowed if this is the first command.
if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN)
return null;
command = this._nextCommandHelper(lookahead, this._previousCommand);
if (command == window.SVGPathSeg.PATHSEG_UNKNOWN)
return null;
} else {
this._currentIndex++;
}
this._previousCommand = command;
switch (command) {
case window.SVGPathSeg.PATHSEG_MOVETO_REL:
return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_MOVETO_ABS:
return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_REL:
return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_ABS:
return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber());
case window.SVGPathSeg.PATHSEG_CLOSEPATH:
this._skipOptionalSpaces();
return new window.SVGPathSegClosePath(owningPathSegList);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
var points = {x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1);
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
case window.SVGPathSeg.PATHSEG_ARC_REL:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
case window.SVGPathSeg.PATHSEG_ARC_ABS:
var points = {x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber()};
return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
default:
throw "Unknown path seg type."
}
}
var builder = new Builder();
var source = new Source(string);
if (!source.initialCommandIsMoveTo())
return [];
while (source.hasMoreData()) {
var pathSeg = source.parseSegment();
if (!pathSeg)
return [];
builder.appendSegment(pathSeg);
}
return builder.pathSegList;
}
}
}());

173
demo/src/Compare.js Normal file
View file

@ -0,0 +1,173 @@
/**
* A Matter.js version comparison testbed based on MatterTools.
*
* Tool to interactively compare engine results of
* development version against the previous release.
*
* USAGE: [host]?compare[=frames]#[example]
* e.g. http://localhost:8000/?compare=120#mixed
*
* @module Compare
*/
var MatterTools = require('matter-tools');
var MatterDev = require('MatterDev');
var MatterBuild = require('MatterBuild');
var compare = function(examples, isDev) {
// create primary demo for dev build
var demo = MatterTools.Demo.create({
toolbar: {
title: 'matter-js ・ ' + (isDev ? 'dev' : '') + ' ・ comparing to ' + MatterBuild.version,
url: 'https://github.com/liabru/matter-js',
reset: true,
source: true,
inspector: false,
tools: false,
fullscreen: true,
exampleSelect: true
},
// tools disabled to keep sync between instances
tools: {
inspector: false,
gui: false
},
inline: false,
preventZoom: true,
resetOnOrientation: true,
routing: true,
startExample: false,
examples: examples
});
// create secondary demo for release build
var demoBuild = MatterTools.Demo.create({
toolbar: {
title: 'matter-js-compare-build',
reset: false,
source: false,
inspector: false,
tools: false,
fullscreen: false,
exampleSelect: false
},
// tools disabled to keep sync between instances
tools: {
inspector: false,
gui: false
},
inline: false,
preventZoom: true,
resetOnOrientation: true,
routing: false,
startExample: false,
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);
document.title = 'Matter.js Compare' + (isDev ? ' ・ Dev' : '');
console.info(
'Demo.Compare: matter-js@' + MatterDev.version +
' with matter-js@' + MatterBuild.version
);
};
module.exports = { compare: compare };

65
demo/src/Demo.js Normal file
View file

@ -0,0 +1,65 @@
/**
* A Matter.js demo and development testbed based on MatterTools.
*
* For a simpler, minimal Matter.js example see:
* https://github.com/liabru/matter-js/wiki/Getting-started
*
* The source for examples can be found at `/examples/`.
*
* @module Demo
*/
var MatterTools = require('matter-tools');
var demo = function(examples, isDev) {
var demo = MatterTools.Demo.create({
toolbar: {
title: 'matter-js' + (isDev ? ' ・ dev' : ''),
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
});
window.MatterDemoInstance = demo;
document.body.appendChild(demo.dom.root);
document.title = 'Matter.js Demo' + (isDev ? ' ・ Dev' : '');
if (isDev) {
var buttonSource = demo.dom.buttonSource;
var buttonCompare = buttonSource.cloneNode(true);
buttonCompare.textContent = '⎄';
buttonCompare.title = 'Compare';
buttonCompare.href = '?compare';
buttonCompare.target = '';
buttonCompare.className = 'matter-btn matter-btn-compare';
buttonCompare.addEventListener('click', function(event) {
window.location = '?compare#' + demo.example.id;
event.preventDefault();
});
buttonSource.parentNode.insertBefore(buttonCompare, buttonSource.nextSibling);
Matter.before('Render.create', function(renderOptions) {
renderOptions.options.showDebug = true;
});
}
MatterTools.Demo.start(demo);
};
module.exports = { demo: demo };

55
demo/src/index.ejs Normal file
View file

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no,minimal-ui">
<meta name="theme-color" content="#000000">
<meta name="msapplication-navbutton-color" content="#000000">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="robots" content="noindex">
<title>Matter.js Demo</title>
<style type="text/css">
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
.matter-btn-compare.matter-btn {
font-size: 18px;
text-align: center;
line-height: 32px;
}
.matter-js-compare-build.matter-demo {
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-demo .matter-header-outer {
display: none;
}
.matter-js-compare-build.matter-demo canvas {
opacity: 0.5;
background: transparent !important;
}
</style>
</head>
<body><% for (var chunk in htmlWebpackPlugin.files.js) { %>
<script src="<%= htmlWebpackPlugin.files.js[chunk] %>"></script><% } %>
</body>
</html>

39
demo/src/index.js Normal file
View file

@ -0,0 +1,39 @@
/**
* Initialise and start the browser demo / compare tool.
*
* For a simpler, minimal Matter.js example see:
* https://github.com/liabru/matter-js/wiki/Getting-started
*
* The source for examples can be found at `/examples/`.
*
* @module Index
*/
var Matter = require('matter-js');
var Examples = require('../../examples/index');
var compare = require('./Compare').compare;
var demo = require('./Demo').demo;
// browser globals
window.pathseg = require('pathseg');
window.MatterTools = require('matter-tools');
window.Matter = Matter;
// prepare examples
var examples = Matter.Common.keys(Examples).map(function(id){
return {
id: id,
sourceLink: 'https://github.com/liabru/matter-js/blob/master/examples/' + id + '.js',
name: Examples[id].title,
init: Examples[id]
};
});
// start the requested tool
var isCompare = window.location.search.indexOf('compare') >= 0;
var isDev = __MATTER_IS_DEV__;
if (isCompare) {
compare(examples, isDev);
} else {
demo(examples, isDev);
}

View file

@ -6,7 +6,7 @@ Example.airFriction = function() {
Runner = Matter.Runner, Runner = Matter.Runner,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -31,7 +31,7 @@ Example.airFriction = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
// falling blocks // falling blocks
Bodies.rectangle(200, 100, 60, 60, { frictionAir: 0.001 }), Bodies.rectangle(200, 100, 60, 60, { frictionAir: 0.001 }),
Bodies.rectangle(400, 100, 60, 60, { frictionAir: 0.05 }), Bodies.rectangle(400, 100, 60, 60, { frictionAir: 0.05 }),
@ -56,7 +56,7 @@ Example.airFriction = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -80,6 +80,7 @@ Example.airFriction = function() {
}; };
}; };
Example.airFriction.title = 'Air Friction';
Example.airFriction.for = '>=0.14.2'; Example.airFriction.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -1,9 +1,17 @@
var Example = Example || {}; var Example = Example || {};
Example.avalanche = function() { Example.avalanche = function() {
Matter.use( try {
'matter-wrap' if (typeof MatterWrap !== 'undefined') {
); // either use by name from plugin registry (Browser global)
Matter.use('matter-wrap');
} else {
// or require and use the plugin directly (Node.js, Webpack etc.)
Matter.use(require('matter-wrap'));
}
} catch (e) {
// could not require the plugin or install needed
}
var Engine = Matter.Engine, var Engine = Matter.Engine,
Render = Matter.Render, Render = Matter.Render,
@ -13,7 +21,6 @@ Example.avalanche = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -42,9 +49,9 @@ Example.avalanche = function() {
return Bodies.circle(x, y, Common.random(10, 20), { friction: 0.00001, restitution: 0.5, density: 0.001 }); return Bodies.circle(x, y, Common.random(10, 20), { friction: 0.00001, restitution: 0.5, density: 0.001 });
}); });
World.add(world, stack); Composite.add(world, stack);
World.add(world, [ Composite.add(world, [
Bodies.rectangle(200, 150, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }), Bodies.rectangle(200, 150, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }),
Bodies.rectangle(500, 350, 700, 20, { isStatic: true, angle: -Math.PI * 0.06, render: { fillStyle: '#060a19' } }), Bodies.rectangle(500, 350, 700, 20, { isStatic: true, angle: -Math.PI * 0.06, render: { fillStyle: '#060a19' } }),
Bodies.rectangle(340, 580, 700, 20, { isStatic: true, angle: Math.PI * 0.04, render: { fillStyle: '#060a19' } }) Bodies.rectangle(340, 580, 700, 20, { isStatic: true, angle: Math.PI * 0.04, render: { fillStyle: '#060a19' } })
@ -62,7 +69,7 @@ Example.avalanche = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -91,6 +98,7 @@ Example.avalanche = function() {
}; };
}; };
Example.avalanche.title = 'Avalanche';
Example.avalanche.for = '>=0.14.2'; Example.avalanche.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -1,9 +1,17 @@
var Example = Example || {}; var Example = Example || {};
Example.ballPool = function() { Example.ballPool = function() {
Matter.use( try {
'matter-wrap' if (typeof MatterWrap !== 'undefined') {
); // either use by name from plugin registry (Browser global)
Matter.use('matter-wrap');
} else {
// or require and use the plugin directly (Node.js, Webpack etc.)
Matter.use(require('matter-wrap'));
}
} catch (e) {
// could not require the plugin or install needed
}
var Engine = Matter.Engine, var Engine = Matter.Engine,
Render = Matter.Render, Render = Matter.Render,
@ -13,7 +21,6 @@ Example.ballPool = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -38,7 +45,7 @@ Example.ballPool = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
Bodies.rectangle(400, 600, 1200, 50.5, { isStatic: true, render: { fillStyle: '#060a19' } }) Bodies.rectangle(400, 600, 1200, 50.5, { isStatic: true, render: { fillStyle: '#060a19' } })
]); ]);
@ -46,7 +53,7 @@ Example.ballPool = function() {
return Bodies.circle(x, y, Common.random(15, 30), { restitution: 0.6, friction: 0.1 }); return Bodies.circle(x, y, Common.random(15, 30), { restitution: 0.6, friction: 0.1 });
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
Bodies.polygon(200, 460, 3, 60), Bodies.polygon(200, 460, 3, 60),
Bodies.polygon(400, 460, 5, 60), Bodies.polygon(400, 460, 5, 60),
@ -65,7 +72,7 @@ Example.ballPool = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -99,6 +106,7 @@ Example.ballPool = function() {
}; };
}; };
Example.ballPool.title = 'Ball Pool';
Example.ballPool.for = '>=0.14.2'; Example.ballPool.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -10,7 +10,7 @@ Example.bridge = function() {
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -61,7 +61,7 @@ Example.bridge = function() {
return Bodies.rectangle(x, y, 50, 50, Common.random(20, 40)); return Bodies.rectangle(x, y, 50, 50, Common.random(20, 40));
}); });
World.add(world, [ Composite.add(world, [
bridge, bridge,
stack, stack,
Bodies.rectangle(30, 490, 220, 380, { Bodies.rectangle(30, 490, 220, 380, {
@ -100,7 +100,7 @@ Example.bridge = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -124,6 +124,7 @@ Example.bridge = function() {
}; };
}; };
Example.bridge.title = 'Bridge';
Example.bridge.for = '>=0.14.2'; Example.bridge.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -7,7 +7,7 @@ Example.car = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -33,7 +33,7 @@ Example.car = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -41,13 +41,14 @@ Example.car = function() {
Bodies.rectangle(0, 300, 50, 600, { isStatic: true }) Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]); ]);
// see car function defined later in this file
var scale = 0.9; var scale = 0.9;
World.add(world, Composites.car(150, 80, 150 * scale, 30 * scale, 30 * scale)); Composite.add(world, Example.car.car(150, 100, 150 * scale, 30 * scale, 30 * scale));
scale = 0.8; scale = 0.8;
World.add(world, Composites.car(350, 300, 150 * scale, 30 * scale, 30 * scale)); Composite.add(world, Example.car.car(350, 300, 150 * scale, 30 * scale, 30 * scale));
World.add(world, [ Composite.add(world, [
Bodies.rectangle(200, 150, 400, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' }}), Bodies.rectangle(200, 150, 400, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' }}),
Bodies.rectangle(500, 350, 650, 20, { isStatic: true, angle: -Math.PI * 0.06, render: { fillStyle: '#060a19' }}), Bodies.rectangle(500, 350, 650, 20, { isStatic: true, angle: -Math.PI * 0.06, render: { fillStyle: '#060a19' }}),
Bodies.rectangle(300, 560, 600, 20, { isStatic: true, angle: Math.PI * 0.04, render: { fillStyle: '#060a19' }}) Bodies.rectangle(300, 560, 600, 20, { isStatic: true, angle: Math.PI * 0.04, render: { fillStyle: '#060a19' }})
@ -65,7 +66,7 @@ Example.car = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -89,8 +90,81 @@ Example.car = function() {
}; };
}; };
Example.car.title = 'Car';
Example.car.for = '>=0.14.2'; Example.car.for = '>=0.14.2';
/**
* Creates a composite with simple car setup of bodies and constraints.
* @method car
* @param {number} xx
* @param {number} yy
* @param {number} width
* @param {number} height
* @param {number} wheelSize
* @return {composite} A new composite car body
*/
Example.car.car = function(xx, yy, width, height, wheelSize) {
var Body = Matter.Body,
Bodies = Matter.Bodies,
Composite = Matter.Composite,
Constraint = Matter.Constraint;
var group = Body.nextGroup(true),
wheelBase = 20,
wheelAOffset = -width * 0.5 + wheelBase,
wheelBOffset = width * 0.5 - wheelBase,
wheelYOffset = 0;
var car = Composite.create({ label: 'Car' }),
body = Bodies.rectangle(xx, yy, width, height, {
collisionFilter: {
group: group
},
chamfer: {
radius: height * 0.5
},
density: 0.0002
});
var wheelA = Bodies.circle(xx + wheelAOffset, yy + wheelYOffset, wheelSize, {
collisionFilter: {
group: group
},
friction: 0.8
});
var wheelB = Bodies.circle(xx + wheelBOffset, yy + wheelYOffset, wheelSize, {
collisionFilter: {
group: group
},
friction: 0.8
});
var axelA = Constraint.create({
bodyB: body,
pointB: { x: wheelAOffset, y: wheelYOffset },
bodyA: wheelA,
stiffness: 1,
length: 0
});
var axelB = Constraint.create({
bodyB: body,
pointB: { x: wheelBOffset, y: wheelYOffset },
bodyA: wheelB,
stiffness: 1,
length: 0
});
Composite.addBody(car, body);
Composite.addBody(car, wheelA);
Composite.addBody(car, wheelB);
Composite.addConstraint(car, axelA);
Composite.addConstraint(car, axelB);
return car;
};
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.car; module.exports = Example.car;
} }

View file

@ -8,7 +8,7 @@ Example.catapult = function() {
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies, Bodies = Matter.Bodies,
Body = Matter.Body, Body = Matter.Body,
Vector = Matter.Vector; Vector = Matter.Vector;
@ -45,7 +45,7 @@ Example.catapult = function() {
var catapult = Bodies.rectangle(400, 520, 320, 20, { collisionFilter: { group: group } }); var catapult = Bodies.rectangle(400, 520, 320, 20, { collisionFilter: { group: group } });
World.add(world, [ Composite.add(world, [
stack, stack,
catapult, catapult,
Bodies.rectangle(400, 600, 800, 50.5, { isStatic: true, render: { fillStyle: '#060a19' } }), Bodies.rectangle(400, 600, 800, 50.5, { isStatic: true, render: { fillStyle: '#060a19' } }),
@ -72,7 +72,7 @@ Example.catapult = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -96,6 +96,7 @@ Example.catapult = function() {
}; };
}; };
Example.catapult.title = 'Catapult';
Example.catapult.for = '>=0.14.2'; Example.catapult.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -10,7 +10,6 @@ Example.chains = function() {
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -79,7 +78,7 @@ Example.chains = function() {
stiffness: 0.5 stiffness: 0.5
})); }));
World.add(world, [ Composite.add(world, [
ropeA, ropeA,
ropeB, ropeB,
ropeC, ropeC,
@ -98,7 +97,7 @@ Example.chains = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -122,6 +121,7 @@ Example.chains = function() {
}; };
}; };
Example.chains.title = 'Chains';
Example.chains.for = '>=0.14.2'; Example.chains.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -7,7 +7,7 @@ Example.circleStack = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -32,11 +32,11 @@ Example.circleStack = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
var stack = Composites.stack(100, 185, 10, 10, 20, 0, function(x, y) { var stack = Composites.stack(100, 600 - 21 - 20 * 20, 10, 10, 20, 0, function(x, y) {
return Bodies.circle(x, y, 20); return Bodies.circle(x, y, 20);
}); });
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -57,7 +57,7 @@ Example.circleStack = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -81,6 +81,7 @@ Example.circleStack = function() {
}; };
}; };
Example.circleStack.title = 'Circle Stack';
Example.circleStack.for = '>=0.14.2'; Example.circleStack.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -8,7 +8,7 @@ Example.cloth = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -31,17 +31,14 @@ Example.cloth = function() {
var runner = Runner.create(); var runner = Runner.create();
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // see cloth function defined later in this file
var group = Body.nextGroup(true), var cloth = Example.cloth.cloth(200, 200, 20, 12, 5, 5, false, 8);
particleOptions = { friction: 0.00001, collisionFilter: { group: group }, render: { visible: false }},
constraintOptions = { stiffness: 0.06 },
cloth = Composites.softBody(200, 200, 20, 12, 5, 5, false, 8, particleOptions, constraintOptions);
for (var i = 0; i < 20; i++) { for (var i = 0; i < 20; i++) {
cloth.bodies[i].isStatic = true; cloth.bodies[i].isStatic = true;
} }
World.add(world, [ Composite.add(world, [
cloth, cloth,
Bodies.circle(300, 500, 80, { isStatic: true, render: { fillStyle: '#060a19' }}), Bodies.circle(300, 500, 80, { isStatic: true, render: { fillStyle: '#060a19' }}),
Bodies.rectangle(500, 480, 80, 80, { isStatic: true, render: { fillStyle: '#060a19' }}), Bodies.rectangle(500, 480, 80, 80, { isStatic: true, render: { fillStyle: '#060a19' }}),
@ -60,7 +57,7 @@ Example.cloth = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -84,8 +81,45 @@ Example.cloth = function() {
}; };
}; };
Example.cloth.title = 'Cloth';
Example.cloth.for = '>=0.14.2'; Example.cloth.for = '>=0.14.2';
/**
* Creates a simple cloth like object.
* @method cloth
* @param {number} xx
* @param {number} yy
* @param {number} columns
* @param {number} rows
* @param {number} columnGap
* @param {number} rowGap
* @param {boolean} crossBrace
* @param {number} particleRadius
* @param {} particleOptions
* @param {} constraintOptions
* @return {composite} A new composite cloth
*/
Example.cloth.cloth = function(xx, yy, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) {
var Body = Matter.Body,
Bodies = Matter.Bodies,
Common = Matter.Common,
Composites = Matter.Composites;
var group = Body.nextGroup(true);
particleOptions = Common.extend({ inertia: Infinity, friction: 0.00001, collisionFilter: { group: group }, render: { visible: false }}, particleOptions);
constraintOptions = Common.extend({ stiffness: 0.06, render: { type: 'line', anchors: false } }, constraintOptions);
var cloth = Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y) {
return Bodies.circle(x, y, particleRadius, particleOptions);
});
Composites.mesh(cloth, columns, rows, crossBrace, constraintOptions);
cloth.label = 'Cloth Body';
return cloth;
};
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.cloth; module.exports = Example.cloth;
} }

View file

@ -9,7 +9,6 @@ Example.collisionFiltering = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -44,7 +43,7 @@ Example.collisionFiltering = function() {
colorC = '#f5d259'; colorC = '#f5d259';
// add floor // add floor
World.add(world, Bodies.rectangle(400, 600, 900, 50, { Composite.add(world, Bodies.rectangle(400, 600, 900, 50, {
isStatic: true, isStatic: true,
render: { render: {
fillStyle: 'transparent', fillStyle: 'transparent',
@ -53,7 +52,7 @@ Example.collisionFiltering = function() {
})); }));
// create a stack with varying body categories (but these bodies can all collide with each other) // create a stack with varying body categories (but these bodies can all collide with each other)
World.add(world, Composite.add(world,
Composites.stack(275, 100, 5, 9, 10, 10, function(x, y, column, row) { Composites.stack(275, 100, 5, 9, 10, 10, function(x, y, column, row) {
var category = redCategory, var category = redCategory,
color = colorA; color = colorA;
@ -80,7 +79,7 @@ Example.collisionFiltering = function() {
); );
// this body will only collide with the walls and the green bodies // this body will only collide with the walls and the green bodies
World.add(world, Composite.add(world,
Bodies.circle(310, 40, 30, { Bodies.circle(310, 40, 30, {
collisionFilter: { collisionFilter: {
mask: defaultCategory | greenCategory mask: defaultCategory | greenCategory
@ -92,7 +91,7 @@ Example.collisionFiltering = function() {
); );
// this body will only collide with the walls and the red bodies // this body will only collide with the walls and the red bodies
World.add(world, Composite.add(world,
Bodies.circle(400, 40, 30, { Bodies.circle(400, 40, 30, {
collisionFilter: { collisionFilter: {
mask: defaultCategory | redCategory mask: defaultCategory | redCategory
@ -104,7 +103,7 @@ Example.collisionFiltering = function() {
); );
// this body will only collide with the walls and the blue bodies // this body will only collide with the walls and the blue bodies
World.add(world, Composite.add(world,
Bodies.circle(480, 40, 30, { Bodies.circle(480, 40, 30, {
collisionFilter: { collisionFilter: {
mask: defaultCategory | blueCategory mask: defaultCategory | blueCategory
@ -127,7 +126,7 @@ Example.collisionFiltering = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -154,6 +153,7 @@ Example.collisionFiltering = function() {
}; };
}; };
Example.collisionFiltering.title = 'Collision Filtering';
Example.collisionFiltering.for = '>=0.14.2'; Example.collisionFiltering.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -9,7 +9,6 @@ Example.compositeManipulation = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -34,7 +33,7 @@ Example.compositeManipulation = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -46,9 +45,9 @@ Example.compositeManipulation = function() {
return Bodies.rectangle(x, y, 40, 40); return Bodies.rectangle(x, y, 40, 40);
}); });
World.add(world, stack); Composite.add(world, stack);
world.gravity.y = 0; engine.gravity.y = 0;
Events.on(engine, 'afterUpdate', function(event) { Events.on(engine, 'afterUpdate', function(event) {
var time = engine.timing.timestamp, var time = engine.timing.timestamp,
@ -84,7 +83,7 @@ Example.compositeManipulation = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -108,7 +107,8 @@ Example.compositeManipulation = function() {
}; };
}; };
Example.compositeManipulation.for = '>=0.14.2'; Example.compositeManipulation.title = 'Composite Manipulation';
Example.compositeManipulation.for = '>0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.compositeManipulation; module.exports = Example.compositeManipulation;

View file

@ -6,9 +6,9 @@ Example.compound = function() {
Runner = Matter.Runner, Runner = Matter.Runner,
Body = Matter.Body, Body = Matter.Body,
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
Composite = Matter.Composite,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -63,7 +63,7 @@ Example.compound = function() {
pointB: { x: 0, y: 0 } pointB: { x: 0, y: 0 }
}); });
World.add(world, [ Composite.add(world, [
compoundBodyA, compoundBodyA,
compoundBodyB, compoundBodyB,
constraint, constraint,
@ -82,7 +82,7 @@ Example.compound = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -106,6 +106,7 @@ Example.compound = function() {
}; };
}; };
Example.compound.title = 'Compound Bodies';
Example.compound.for = '>=0.14.2'; Example.compound.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -8,7 +8,7 @@ Example.compoundStack = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -35,7 +35,7 @@ Example.compoundStack = function() {
// add bodies // add bodies
var size = 50; var size = 50;
var stack = Composites.stack(100, 280, 12, 6, 0, 0, function(x, y) { var stack = Composites.stack(100, 600 - 17 - size * 6, 12, 6, 0, 0, function(x, y) {
var partA = Bodies.rectangle(x, y, size, size / 5), var partA = Bodies.rectangle(x, y, size, size / 5),
partB = Bodies.rectangle(x, y, size / 5, size, { render: partA.render }); partB = Bodies.rectangle(x, y, size / 5, size, { render: partA.render });
@ -44,7 +44,7 @@ Example.compoundStack = function() {
}); });
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
@ -65,7 +65,7 @@ Example.compoundStack = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -89,6 +89,7 @@ Example.compoundStack = function() {
}; };
}; };
Example.compoundStack.title = 'Compound Stack';
Example.compoundStack.for = '>=0.14.2'; Example.compoundStack.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -8,10 +8,13 @@ Example.concave = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Vertices = Matter.Vertices, Vertices = Matter.Vertices,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// provide concave decomposition support library
Common.setDecomp(require('poly-decomp'));
// create engine // create engine
var engine = Engine.create(), var engine = Engine.create(),
world = engine.world; world = engine.world;
@ -33,7 +36,7 @@ Example.concave = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -57,7 +60,7 @@ Example.concave = function() {
}, true); }, true);
}); });
World.add(world, stack); Composite.add(world, stack);
// add mouse control // add mouse control
var mouse = Mouse.create(render.canvas), var mouse = Mouse.create(render.canvas),
@ -71,7 +74,7 @@ Example.concave = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -95,7 +98,8 @@ Example.concave = function() {
}; };
}; };
Example.concave.for = '>=0.14.2'; Example.concave.title = 'Concave';
Example.concave.for = '>0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.concave; module.exports = Example.concave;

View file

@ -9,7 +9,7 @@ Example.constraints = function() {
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -42,7 +42,7 @@ Example.constraints = function() {
pointB: { x: -10, y: -10 } pointB: { x: -10, y: -10 }
}); });
World.add(world, [body, constraint]); Composite.add(world, [body, constraint]);
// add soft global constraint // add soft global constraint
var body = Bodies.polygon(280, 100, 3, 30); var body = Bodies.polygon(280, 100, 3, 30);
@ -54,7 +54,7 @@ Example.constraints = function() {
stiffness: 0.001 stiffness: 0.001
}); });
World.add(world, [body, constraint]); Composite.add(world, [body, constraint]);
// add damped soft global constraint // add damped soft global constraint
var body = Bodies.polygon(400, 100, 4, 30); var body = Bodies.polygon(400, 100, 4, 30);
@ -67,7 +67,7 @@ Example.constraints = function() {
damping: 0.05 damping: 0.05
}); });
World.add(world, [body, constraint]); Composite.add(world, [body, constraint]);
// add revolute constraint // add revolute constraint
var body = Bodies.rectangle(600, 200, 200, 20); var body = Bodies.rectangle(600, 200, 200, 20);
@ -79,7 +79,7 @@ Example.constraints = function() {
length: 0 length: 0
}); });
World.add(world, [body, ball, constraint]); Composite.add(world, [body, ball, constraint]);
// add revolute multi-body constraint // add revolute multi-body constraint
var body = Bodies.rectangle(500, 400, 100, 20, { collisionFilter: { group: -1 } }); var body = Bodies.rectangle(500, 400, 100, 20, { collisionFilter: { group: -1 } });
@ -90,7 +90,7 @@ Example.constraints = function() {
bodyB: ball bodyB: ball
}); });
World.add(world, [body, ball, constraint]); Composite.add(world, [body, ball, constraint]);
// add stiff multi-body constraint // add stiff multi-body constraint
var bodyA = Bodies.polygon(100, 400, 6, 20); var bodyA = Bodies.polygon(100, 400, 6, 20);
@ -103,7 +103,7 @@ Example.constraints = function() {
pointB: { x: -10, y: -10 } pointB: { x: -10, y: -10 }
}); });
World.add(world, [bodyA, bodyB, constraint]); Composite.add(world, [bodyA, bodyB, constraint]);
// add soft global constraint // add soft global constraint
var bodyA = Bodies.polygon(300, 400, 4, 20); var bodyA = Bodies.polygon(300, 400, 4, 20);
@ -117,7 +117,7 @@ Example.constraints = function() {
stiffness: 0.001 stiffness: 0.001
}); });
World.add(world, [bodyA, bodyB, constraint]); Composite.add(world, [bodyA, bodyB, constraint]);
// add damped soft global constraint // add damped soft global constraint
var bodyA = Bodies.polygon(500, 400, 6, 30); var bodyA = Bodies.polygon(500, 400, 6, 30);
@ -132,9 +132,9 @@ Example.constraints = function() {
damping: 0.1 damping: 0.1
}); });
World.add(world, [bodyA, bodyB, constraint]); Composite.add(world, [bodyA, bodyB, constraint]);
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -155,7 +155,7 @@ Example.constraints = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -179,6 +179,7 @@ Example.constraints = function() {
}; };
}; };
Example.constraints.title = 'Constraints';
Example.constraints.for = '>=0.14.2'; Example.constraints.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -11,7 +11,6 @@ Example.doublePendulum = function() {
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies, Bodies = Matter.Bodies,
Vector = Matter.Vector; Vector = Matter.Vector;
@ -53,7 +52,7 @@ Example.doublePendulum = function() {
}); });
}); });
world.gravity.scale = 0.002; engine.gravity.scale = 0.002;
Composites.chain(pendulum, 0.45, 0, -0.45, 0, { Composites.chain(pendulum, 0.45, 0, -0.45, 0, {
stiffness: 0.9, stiffness: 0.9,
@ -82,7 +81,7 @@ Example.doublePendulum = function() {
y: lowerArm.position.y y: lowerArm.position.y
}); });
World.add(world, pendulum); Composite.add(world, pendulum);
var trail = []; var trail = [];
@ -124,7 +123,7 @@ Example.doublePendulum = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -148,7 +147,8 @@ Example.doublePendulum = function() {
}; };
}; };
Example.doublePendulum.for = '>=0.14.2'; Example.doublePendulum.title = 'Double Pendulum';
Example.doublePendulum.for = '>0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.doublePendulum; module.exports = Example.doublePendulum;

View file

@ -11,7 +11,6 @@ Example.events = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -37,7 +36,7 @@ Example.events = function() {
// an example of using composite events on the world // an example of using composite events on the world
Events.on(world, 'afterAdd', function(event) { Events.on(world, 'afterAdd', function(event) {
console.log('added to world:', event.object); // do something with event.object
}); });
var lastTime = Common.now(); var lastTime = Common.now();
@ -95,7 +94,7 @@ Example.events = function() {
var bodyStyle = { fillStyle: '#222' }; var bodyStyle = { fillStyle: '#222' };
// scene code // scene code
World.add(world, [ Composite.add(world, [
Bodies.rectangle(400, 0, 800, 50, { isStatic: true, render: bodyStyle }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true, render: bodyStyle }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true, render: bodyStyle }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true, render: bodyStyle }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true, render: bodyStyle }), Bodies.rectangle(800, 300, 50, 600, { isStatic: true, render: bodyStyle }),
@ -106,7 +105,7 @@ Example.events = function() {
return Bodies.circle(x, y, 15, { restitution: 1, render: bodyStyle }); return Bodies.circle(x, y, 15, { restitution: 1, render: bodyStyle });
}); });
World.add(world, stack); Composite.add(world, stack);
var shakeScene = function(engine, delta) { var shakeScene = function(engine, delta) {
var timeScale = delta / 1000; var timeScale = delta / 1000;
@ -139,7 +138,7 @@ Example.events = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -186,6 +185,7 @@ Example.events = function() {
}; };
}; };
Example.events.title = 'Events';
Example.events.for = '>=0.14.2'; Example.events.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -6,7 +6,7 @@ Example.friction = function() {
Runner = Matter.Runner, Runner = Matter.Runner,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -31,7 +31,7 @@ Example.friction = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -39,17 +39,17 @@ Example.friction = function() {
Bodies.rectangle(0, 300, 50, 600, { isStatic: true }) Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]); ]);
World.add(world, [ Composite.add(world, [
Bodies.rectangle(300, 180, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }), Bodies.rectangle(300, 180, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }),
Bodies.rectangle(300, 70, 40, 40, { friction: 0.001 }) Bodies.rectangle(300, 70, 40, 40, { friction: 0.001 })
]); ]);
World.add(world, [ Composite.add(world, [
Bodies.rectangle(300, 350, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }), Bodies.rectangle(300, 350, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }),
Bodies.rectangle(300, 250, 40, 40, { friction: 0.0005 }) Bodies.rectangle(300, 250, 40, 40, { friction: 0.0005 })
]); ]);
World.add(world, [ Composite.add(world, [
Bodies.rectangle(300, 520, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }), Bodies.rectangle(300, 520, 700, 20, { isStatic: true, angle: Math.PI * 0.06, render: { fillStyle: '#060a19' } }),
Bodies.rectangle(300, 430, 40, 40, { friction: 0 }) Bodies.rectangle(300, 430, 40, 40, { friction: 0 })
]); ]);
@ -66,7 +66,7 @@ Example.friction = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -90,6 +90,7 @@ Example.friction = function() {
}; };
}; };
Example.friction.title = 'Friction';
Example.friction.for = '>=0.14.2'; Example.friction.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -8,7 +8,7 @@ Example.gravity = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -34,14 +34,14 @@ Example.gravity = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50.5, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50.5, { isStatic: true }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true }), Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
Bodies.rectangle(0, 300, 50, 600, { isStatic: true }) Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]); ]);
engine.world.gravity.y = -1; engine.gravity.y = -1;
var stack = Composites.stack(50, 120, 11, 5, 0, 0, function(x, y) { var stack = Composites.stack(50, 120, 11, 5, 0, 0, function(x, y) {
switch (Math.round(Common.random(0, 1))) { switch (Math.round(Common.random(0, 1))) {
@ -58,7 +58,7 @@ Example.gravity = function() {
} }
}); });
World.add(world, stack); Composite.add(world, stack);
// add mouse control // add mouse control
var mouse = Mouse.create(render.canvas), var mouse = Mouse.create(render.canvas),
@ -72,7 +72,7 @@ Example.gravity = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -96,7 +96,8 @@ Example.gravity = function() {
}; };
}; };
Example.gravity.for = '>=0.14.2'; Example.gravity.title = 'Reverse Gravity';
Example.gravity.for = '>0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.gravity; module.exports = Example.gravity;

View file

@ -8,7 +8,7 @@ Example.gyro = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -36,9 +36,6 @@ Example.gyro = function() {
var stack = Composites.stack(20, 20, 10, 5, 0, 0, function(x, y) { var stack = Composites.stack(20, 20, 10, 5, 0, 0, function(x, y) {
var sides = Math.round(Common.random(1, 8)); var sides = Math.round(Common.random(1, 8));
// triangles can be a little unstable, so avoid until fixed
sides = (sides === 3) ? 4 : sides;
// round the edges of some bodies // round the edges of some bodies
var chamfer = null; var chamfer = null;
if (sides > 2 && Common.random() > 0.7) { if (sides > 2 && Common.random() > 0.7) {
@ -59,7 +56,7 @@ Example.gyro = function() {
} }
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -71,7 +68,7 @@ Example.gyro = function() {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
var updateGravity = function(event) { var updateGravity = function(event) {
var orientation = typeof window.orientation !== 'undefined' ? window.orientation : 0, var orientation = typeof window.orientation !== 'undefined' ? window.orientation : 0,
gravity = engine.world.gravity; gravity = engine.gravity;
if (orientation === 0) { if (orientation === 0) {
gravity.x = Common.clamp(event.gamma, -90, 90) / 90; gravity.x = Common.clamp(event.gamma, -90, 90) / 90;
@ -103,7 +100,7 @@ Example.gyro = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -130,6 +127,7 @@ Example.gyro = function() {
}; };
}; };
Example.gyro.title = 'Gyroscope';
Example.gyro.for = '>=0.14.2'; Example.gyro.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -3,7 +3,6 @@ module.exports = {
avalanche: require('./avalanche.js'), avalanche: require('./avalanche.js'),
ballPool: require('./ballPool.js'), ballPool: require('./ballPool.js'),
bridge: require('./bridge.js'), bridge: require('./bridge.js'),
broadphase: require('./broadphase.js'),
car: require('./car.js'), car: require('./car.js'),
catapult: require('./catapult.js'), catapult: require('./catapult.js'),
chains: require('./chains.js'), chains: require('./chains.js'),
@ -28,6 +27,7 @@ module.exports = {
raycasting: require('./raycasting.js'), raycasting: require('./raycasting.js'),
restitution: require('./restitution.js'), restitution: require('./restitution.js'),
rounded: require('./rounded.js'), rounded: require('./rounded.js'),
remove: require('./remove.js'),
sensors: require('./sensors.js'), sensors: require('./sensors.js'),
sleeping: require('./sleeping.js'), sleeping: require('./sleeping.js'),
slingshot: require('./slingshot.js'), slingshot: require('./slingshot.js'),
@ -35,8 +35,10 @@ module.exports = {
sprites: require('./sprites.js'), sprites: require('./sprites.js'),
stack: require('./stack.js'), stack: require('./stack.js'),
staticFriction: require('./staticFriction.js'), staticFriction: require('./staticFriction.js'),
stats: require('./stats.js'),
stress: require('./stress.js'), stress: require('./stress.js'),
stress2: require('./stress2.js'), stress2: require('./stress2.js'),
stress3: require('./stress3.js'),
svg: require('./svg.js'), svg: require('./svg.js'),
terrain: require('./terrain.js'), terrain: require('./terrain.js'),
timescale: require('./timescale.js'), timescale: require('./timescale.js'),

View file

@ -9,7 +9,7 @@ Example.manipulation = function() {
Events = Matter.Events, Events = Matter.Events,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -50,9 +50,9 @@ Example.manipulation = function() {
isStatic: true isStatic: true
}); });
World.add(world, [bodyA, bodyB, bodyC, bodyD, bodyE, bodyF, bodyG, compound]); Composite.add(world, [bodyA, bodyB, bodyC, bodyD, bodyE, bodyF, bodyG, compound]);
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -115,7 +115,7 @@ Example.manipulation = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -139,6 +139,7 @@ Example.manipulation = function() {
}; };
}; };
Example.manipulation.title = 'Manipulation';
Example.manipulation.for = '>=0.14.2'; Example.manipulation.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -8,7 +8,7 @@ Example.mixed = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -36,9 +36,6 @@ Example.mixed = function() {
var stack = Composites.stack(20, 20, 10, 5, 0, 0, function(x, y) { var stack = Composites.stack(20, 20, 10, 5, 0, 0, function(x, y) {
var sides = Math.round(Common.random(1, 8)); var sides = Math.round(Common.random(1, 8));
// triangles can be a little unstable, so avoid until fixed
sides = (sides === 3) ? 4 : sides;
// round the edges of some bodies // round the edges of some bodies
var chamfer = null; var chamfer = null;
if (sides > 2 && Common.random() > 0.7) { if (sides > 2 && Common.random() > 0.7) {
@ -59,9 +56,9 @@ Example.mixed = function() {
} }
}); });
World.add(world, stack); Composite.add(world, stack);
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -81,7 +78,7 @@ Example.mixed = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -105,6 +102,7 @@ Example.mixed = function() {
}; };
}; };
Example.mixed.title = 'Mixed Shapes';
Example.mixed.for = '>=0.14.2'; Example.mixed.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -8,7 +8,7 @@ Example.newtonsCradle = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World; Composite = Matter.Composite;
// create engine // create engine
var engine = Engine.create(), var engine = Engine.create(),
@ -31,13 +31,13 @@ Example.newtonsCradle = function() {
var runner = Runner.create(); var runner = Runner.create();
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // see newtonsCradle function defined later in this file
var cradle = Composites.newtonsCradle(280, 100, 5, 30, 200); var cradle = Example.newtonsCradle.newtonsCradle(280, 100, 5, 30, 200);
World.add(world, cradle); Composite.add(world, cradle);
Body.translate(cradle.bodies[0], { x: -180, y: -100 }); Body.translate(cradle.bodies[0], { x: -180, y: -100 });
cradle = Composites.newtonsCradle(280, 380, 7, 20, 140); cradle = Example.newtonsCradle.newtonsCradle(280, 380, 7, 20, 140);
World.add(world, cradle); Composite.add(world, cradle);
Body.translate(cradle.bodies[0], { x: -140, y: -100 }); Body.translate(cradle.bodies[0], { x: -140, y: -100 });
// add mouse control // add mouse control
@ -52,7 +52,7 @@ Example.newtonsCradle = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -76,8 +76,39 @@ Example.newtonsCradle = function() {
}; };
}; };
Example.newtonsCradle.title = 'Newton\'s Cradle';
Example.newtonsCradle.for = '>=0.14.2'; Example.newtonsCradle.for = '>=0.14.2';
/**
* Creates a composite with a Newton's Cradle setup of bodies and constraints.
* @method newtonsCradle
* @param {number} xx
* @param {number} yy
* @param {number} number
* @param {number} size
* @param {number} length
* @return {composite} A new composite newtonsCradle body
*/
Example.newtonsCradle.newtonsCradle = function(xx, yy, number, size, length) {
var Composite = Matter.Composite,
Constraint = Matter.Constraint,
Bodies = Matter.Bodies;
var newtonsCradle = Composite.create({ label: 'Newtons Cradle' });
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 }),
constraint = Constraint.create({ pointA: { x: xx + i * (size * separation), y: yy }, bodyB: circle });
Composite.addBody(newtonsCradle, circle);
Composite.addConstraint(newtonsCradle, constraint);
}
return newtonsCradle;
};
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.newtonsCradle; module.exports = Example.newtonsCradle;
} }

View file

@ -7,7 +7,7 @@ Example.pyramid = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -32,11 +32,11 @@ Example.pyramid = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
var stack = Composites.pyramid(100, 258, 15, 10, 0, 0, function(x, y) { var stack = Composites.pyramid(100, 605 - 25 - 16 * 20, 15, 10, 0, 0, function(x, y) {
return Bodies.rectangle(x, y, 40, 40); return Bodies.rectangle(x, y, 40, 40);
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
@ -57,7 +57,7 @@ Example.pyramid = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -81,6 +81,7 @@ Example.pyramid = function() {
}; };
}; };
Example.pyramid.title = 'Pyramid';
Example.pyramid.for = '>=0.14.2'; Example.pyramid.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -11,7 +11,6 @@ Example.ragdoll = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -78,7 +77,7 @@ Example.ragdoll = function() {
Composite.add(ragdolls, ragdoll); Composite.add(ragdolls, ragdoll);
} }
World.add(world, [stack, obstacles, ragdolls]); Composite.add(world, [stack, obstacles, ragdolls]);
var timeScaleTarget = 1, var timeScaleTarget = 1,
lastTime = Common.now(); lastTime = Common.now();
@ -171,7 +170,7 @@ Example.ragdoll = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -496,6 +495,7 @@ Example.ragdoll.ragdoll = function(x, y, scale, options) {
return person; return person;
}; };
Example.ragdoll.title = 'Ragdoll';
Example.ragdoll.for = '>=0.14.2'; Example.ragdoll.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -11,7 +11,6 @@ Example.raycasting = function() {
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
Events = Matter.Events, Events = Matter.Events,
World = Matter.World,
Vertices = Matter.Vertices, Vertices = Matter.Vertices,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
@ -53,10 +52,13 @@ Example.raycasting = function() {
} }
}); });
// for testing raycasting on concave bodies
Common.setDecomp(require('poly-decomp'));
var star = Vertices.fromPath('50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38'), var star = Vertices.fromPath('50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38'),
concave = Bodies.fromVertices(200, 200, star); concave = Bodies.fromVertices(200, 200, star);
World.add(world, [ Composite.add(world, [
stack, stack,
concave, concave,
// walls // walls
@ -66,14 +68,21 @@ Example.raycasting = function() {
Bodies.rectangle(0, 300, 50, 600, { isStatic: true }) Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]); ]);
var collisions = [],
startPoint = { x: 400, y: 100 };
Events.on(engine, 'afterUpdate', function() {
var mouse = mouseConstraint.mouse,
bodies = Composite.allBodies(engine.world),
endPoint = mouse.position || { x: 100, y: 600 };
collisions = Query.ray(bodies, startPoint, endPoint);
});
Events.on(render, 'afterRender', function() { Events.on(render, 'afterRender', function() {
var mouse = mouseConstraint.mouse, var mouse = mouseConstraint.mouse,
context = render.context, context = render.context,
bodies = Composite.allBodies(engine.world), endPoint = mouse.position || { x: 100, y: 600 };
startPoint = { x: 400, y: 100 },
endPoint = mouse.position;
var collisions = Query.ray(bodies, startPoint, endPoint);
Render.startViewTransform(render); Render.startViewTransform(render);
@ -111,7 +120,7 @@ Example.raycasting = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -135,7 +144,8 @@ Example.raycasting = function() {
}; };
}; };
Example.raycasting.for = '>=0.14.2'; Example.raycasting.title = 'Raycasting';
Example.raycasting.for = '>0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.raycasting; module.exports = Example.raycasting;

138
examples/remove.js Normal file
View file

@ -0,0 +1,138 @@
var Example = Example || {};
Example.remove = function() {
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Composites = Matter.Composites,
Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse,
Composite = Matter.Composite,
Bodies = Matter.Bodies,
Events = Matter.Events;
// create engine
var engine = Engine.create(),
world = engine.world;
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600,
showAngleIndicator: true,
}
});
Render.run(render);
// create runner
var runner = Runner.create();
Runner.run(runner, engine);
var stack = null,
updateCount = 0;
var createStack = function() {
return Composites.stack(20, 20, 10, 5, 0, 0, function(x, y) {
var sides = Math.round(Common.random(1, 8));
// round the edges of some bodies
var chamfer = null;
if (sides > 2 && Common.random() > 0.7) {
chamfer = {
radius: 10
};
}
switch (Math.round(Common.random(0, 1))) {
case 0:
if (Common.random() < 0.8) {
return Bodies.rectangle(x, y, Common.random(25, 50), Common.random(25, 50), { chamfer: chamfer });
} else {
return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(25, 30), { chamfer: chamfer });
}
case 1:
return Bodies.polygon(x, y, sides, Common.random(25, 50), { chamfer: chamfer });
}
});
};
// add and remove stacks every few updates
Events.on(engine, 'afterUpdate', function() {
// limit rate
if (stack && updateCount <= 50) {
updateCount += 1;
return;
}
updateCount = 0;
// remove last stack
if (stack) {
Composite.remove(world, stack);
}
// create a new stack
stack = createStack();
// add the new stack
Composite.add(world, stack);
});
// add another stack that will not be removed
Composite.add(world, createStack());
Composite.add(world, [
// walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]);
// add mouse control
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: {
visible: false
}
}
});
Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering
render.mouse = mouse;
// fit the render viewport to the scene
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: 800, y: 600 }
});
// context for MatterTools.Demo
return {
engine: engine,
runner: runner,
render: render,
canvas: render.canvas,
stop: function() {
Matter.Render.stop(render);
Matter.Runner.stop(runner);
}
};
};
Example.remove.title = 'Composite Remove';
Example.remove.for = '>=0.14.2';
if (typeof module !== 'undefined') {
module.exports = Example.remove;
}

View file

@ -6,7 +6,7 @@ Example.restitution = function() {
Runner = Matter.Runner, Runner = Matter.Runner,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -36,7 +36,7 @@ Example.restitution = function() {
var rest = 0.9, var rest = 0.9,
space = 600 / 5; space = 600 / 5;
World.add(world, [ Composite.add(world, [
Bodies.rectangle(100 + space * 0, 150, 50, 50, { restitution: rest }), Bodies.rectangle(100 + space * 0, 150, 50, 50, { restitution: rest }),
Bodies.rectangle(100 + space * 1, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.15 }), Bodies.rectangle(100 + space * 1, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.15 }),
Bodies.rectangle(100 + space * 2, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.25 }), Bodies.rectangle(100 + space * 2, 150, 50, 50, { restitution: rest, angle: -Math.PI * 0.25 }),
@ -61,7 +61,7 @@ Example.restitution = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -85,6 +85,7 @@ Example.restitution = function() {
}; };
}; };
Example.restitution.title = 'Restitution';
Example.restitution.for = '>=0.14.2'; Example.restitution.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -6,7 +6,7 @@ Example.rounded = function() {
Runner = Matter.Runner, Runner = Matter.Runner,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -31,7 +31,7 @@ Example.rounded = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -39,7 +39,7 @@ Example.rounded = function() {
Bodies.rectangle(0, 300, 50, 600, { isStatic: true }) Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]); ]);
World.add(world, [ Composite.add(world, [
Bodies.rectangle(200, 200, 100, 100, { Bodies.rectangle(200, 200, 100, 100, {
chamfer: { radius: 20 } chamfer: { radius: 20 }
}), }),
@ -85,7 +85,7 @@ Example.rounded = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -109,6 +109,7 @@ Example.rounded = function() {
}; };
}; };
Example.rounded.title = 'Rounded Corners (Chamfering)';
Example.rounded.for = '>=0.14.2'; Example.rounded.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -7,7 +7,7 @@ Example.sensors = function() {
Events = Matter.Events, Events = Matter.Events,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -45,7 +45,7 @@ Example.sensors = function() {
} }
}); });
World.add(world, [ Composite.add(world, [
collider, collider,
Bodies.rectangle(400, 600, 800, 50, { Bodies.rectangle(400, 600, 800, 50, {
isStatic: true, isStatic: true,
@ -56,7 +56,7 @@ Example.sensors = function() {
}) })
]); ]);
World.add(world, Composite.add(world,
Bodies.circle(400, 40, 30, { Bodies.circle(400, 40, 30, {
render: { render: {
strokeStyle: colorB, strokeStyle: colorB,
@ -106,7 +106,7 @@ Example.sensors = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -130,6 +130,7 @@ Example.sensors = function() {
}; };
}; };
Example.sensors.title = 'Sensors';
Example.sensors.for = '>=0.14.2'; Example.sensors.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -9,7 +9,7 @@ Example.sleeping = function() {
Events = Matter.Events, Events = Matter.Events,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -36,7 +36,7 @@ Example.sleeping = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -59,14 +59,17 @@ Example.sleeping = function() {
} }
}); });
World.add(world, stack); Composite.add(world, stack);
/*
// sleep events
for (var i = 0; i < stack.bodies.length; i++) { for (var i = 0; i < stack.bodies.length; i++) {
Events.on(stack.bodies[i], 'sleepStart sleepEnd', function(event) { Events.on(stack.bodies[i], 'sleepStart sleepEnd', function(event) {
var body = this; var body = this;
console.log('body id', body.id, 'sleeping:', body.isSleeping); console.log('body id', body.id, 'sleeping:', body.isSleeping);
}); });
} }
*/
// add mouse control // add mouse control
var mouse = Mouse.create(render.canvas), var mouse = Mouse.create(render.canvas),
@ -80,7 +83,7 @@ Example.sleeping = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -104,6 +107,7 @@ Example.sleeping = function() {
}; };
}; };
Example.sleeping.title = 'Sleeping';
Example.sleeping.for = '>=0.14.2'; Example.sleeping.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -9,8 +9,8 @@ Example.slingshot = function() {
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Body = Matter.Body, Body = Matter.Body,
Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -55,7 +55,7 @@ Example.slingshot = function() {
return Bodies.rectangle(x, y, 25, 40); return Bodies.rectangle(x, y, 25, 40);
}); });
World.add(engine.world, [ground, pyramid, ground2, pyramid2, rock, elastic]); Composite.add(engine.world, [ground, pyramid, ground2, pyramid2, rock, elastic]);
Events.on(engine, 'afterUpdate', function() { Events.on(engine, 'afterUpdate', function() {
if (mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) { if (mouseConstraint.mouse.button === -1 && (rock.position.x > 190 || rock.position.y < 430)) {
@ -66,7 +66,7 @@ Example.slingshot = function() {
// Release current rock and add a new one. // Release current rock and add a new one.
rock = Bodies.polygon(170, 450, 7, 20, rockOptions); rock = Bodies.polygon(170, 450, 7, 20, rockOptions);
World.add(engine.world, rock); Composite.add(engine.world, rock);
elastic.bodyB = rock; elastic.bodyB = rock;
} }
}); });
@ -83,7 +83,7 @@ Example.slingshot = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -107,6 +107,7 @@ Example.slingshot = function() {
}; };
}; };
Example.slingshot.title = 'Slingshot';
Example.slingshot.for = '>=0.14.2'; Example.slingshot.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -7,7 +7,7 @@ Example.softBody = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -38,10 +38,11 @@ Example.softBody = function() {
render: { visible: true } render: { visible: true }
}; };
World.add(world, [ Composite.add(world, [
Composites.softBody(250, 100, 5, 5, 0, 0, true, 18, particleOptions), // see softBody function defined later in this file
Composites.softBody(400, 300, 8, 3, 0, 0, true, 15, particleOptions), Example.softBody.softBody(250, 100, 5, 5, 0, 0, true, 18, particleOptions),
Composites.softBody(250, 400, 4, 4, 0, 0, true, 15, particleOptions), Example.softBody.softBody(400, 300, 8, 3, 0, 0, true, 15, particleOptions),
Example.softBody.softBody(250, 400, 4, 4, 0, 0, true, 15, particleOptions),
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -61,7 +62,7 @@ Example.softBody = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -85,8 +86,43 @@ Example.softBody = function() {
}; };
}; };
Example.softBody.title = 'Soft Body';
Example.softBody.for = '>=0.14.2'; Example.softBody.for = '>=0.14.2';
/**
* Creates a simple soft body like object.
* @method softBody
* @param {number} xx
* @param {number} yy
* @param {number} columns
* @param {number} rows
* @param {number} columnGap
* @param {number} rowGap
* @param {boolean} crossBrace
* @param {number} particleRadius
* @param {} particleOptions
* @param {} constraintOptions
* @return {composite} A new composite softBody
*/
Example.softBody.softBody = function(xx, yy, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) {
var Common = Matter.Common,
Composites = Matter.Composites,
Bodies = Matter.Bodies;
particleOptions = Common.extend({ inertia: Infinity }, particleOptions);
constraintOptions = Common.extend({ stiffness: 0.2, render: { type: 'line', anchors: false } }, constraintOptions);
var softBody = Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y) {
return Bodies.circle(x, y, particleRadius, particleOptions);
});
Composites.mesh(softBody, columns, rows, crossBrace, constraintOptions);
softBody.label = 'Soft Body';
return softBody;
};
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.softBody; module.exports = Example.softBody;
} }

View file

@ -8,7 +8,7 @@ Example.sprites = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -42,7 +42,7 @@ Example.sprites = function() {
world.bodies = []; world.bodies = [];
// these static walls will not be rendered in this sprites example, see options // these static walls will not be rendered in this sprites example, see options
World.add(world, [ Composite.add(world, [
Bodies.rectangle(400, -offset, 800.5 + 2 * offset, 50.5, options), Bodies.rectangle(400, -offset, 800.5 + 2 * offset, 50.5, options),
Bodies.rectangle(400, 600 + offset, 800.5 + 2 * offset, 50.5, options), Bodies.rectangle(400, 600 + offset, 800.5 + 2 * offset, 50.5, options),
Bodies.rectangle(800 + offset, 300, 50.5, 600.5 + 2 * offset, options), Bodies.rectangle(800 + offset, 300, 50.5, 600.5 + 2 * offset, options),
@ -74,7 +74,7 @@ Example.sprites = function() {
} }
}); });
World.add(world, stack); Composite.add(world, stack);
// add mouse control // add mouse control
var mouse = Mouse.create(render.canvas), var mouse = Mouse.create(render.canvas),
@ -88,7 +88,7 @@ Example.sprites = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -112,6 +112,7 @@ Example.sprites = function() {
}; };
}; };
Example.sprites.title = 'Sprites';
Example.sprites.for = '>=0.14.2'; Example.sprites.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -7,7 +7,7 @@ Example.stack = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -32,11 +32,11 @@ Example.stack = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
var stack = Composites.stack(200, 380, 10, 5, 0, 0, function(x, y) { var stack = Composites.stack(200, 606 - 25.25 - 5 * 40, 10, 5, 0, 0, function(x, y) {
return Bodies.rectangle(x, y, 40, 40); return Bodies.rectangle(x, y, 40, 40);
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
@ -57,7 +57,7 @@ Example.stack = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -81,6 +81,7 @@ Example.stack = function() {
}; };
}; };
Example.stack.title = 'Stack';
Example.stack.for = '>=0.14.2'; Example.stack.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -9,7 +9,7 @@ Example.staticFriction = function() {
Events = Matter.Events, Events = Matter.Events,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -45,7 +45,7 @@ Example.staticFriction = function() {
}); });
}); });
World.add(world, [ Composite.add(world, [
body, body,
stack, stack,
// walls // walls
@ -77,7 +77,7 @@ Example.staticFriction = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -101,6 +101,7 @@ Example.staticFriction = function() {
}; };
}; };
Example.staticFriction.title = 'Static Friction';
Example.staticFriction.for = '>=0.14.2'; Example.staticFriction.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -1,11 +1,11 @@
var Example = Example || {}; var Example = Example || {};
Example.broadphase = function() { Example.stats = function() {
var Engine = Matter.Engine, var Engine = Matter.Engine,
Render = Matter.Render, Render = Matter.Render,
Runner = Matter.Runner, Runner = Matter.Runner,
Composites = Matter.Composites,
Common = Matter.Common, Common = Matter.Common,
Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, World = Matter.World,
@ -22,8 +22,9 @@ Example.broadphase = function() {
options: { options: {
width: 800, width: 800,
height: 600, height: 600,
showAngleIndicator: true, // show stats and performance monitors
showBroadphase: true showStats: true,
showPerformance: true
} }
}); });
@ -33,32 +34,19 @@ Example.broadphase = function() {
var runner = Runner.create(); var runner = Runner.create();
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // scene code
var stack = Composites.stack(70, 30, 13, 9, 5, 5, function(x, y) {
return Bodies.circle(x, y, 10 + Common.random() * 20);
});
World.add(world, [ World.add(world, [
// walls stack,
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true }), Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
Bodies.rectangle(0, 300, 50, 600, { isStatic: true }) Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]); ]);
var stack = Composites.stack(20, 20, 12, 5, 0, 0, function(x, y) {
switch (Math.round(Common.random(0, 1))) {
case 0:
if (Common.random() < 0.8) {
return Bodies.rectangle(x, y, Common.random(20, 50), Common.random(20, 50));
} else {
return Bodies.rectangle(x, y, Common.random(80, 120), Common.random(20, 30));
}
case 1:
return Bodies.polygon(x, y, Math.round(Common.random(1, 8)), Common.random(20, 50));
}
});
World.add(world, stack);
// add mouse control // add mouse control
var mouse = Mouse.create(render.canvas), var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, { mouseConstraint = MouseConstraint.create(engine, {
@ -95,8 +83,9 @@ Example.broadphase = function() {
}; };
}; };
Example.broadphase.for = '>=0.14.2'; Example.stats.title = 'Stats & Performance';
Example.stats.for = '>=0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.broadphase; module.exports = Example.stats;
} }

View file

@ -7,7 +7,7 @@ Example.stress = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -20,7 +20,9 @@ Example.stress = function() {
engine: engine, engine: engine,
options: { options: {
width: 800, width: 800,
height: 600 height: 600,
showStats: true,
showPerformance: true
} }
}); });
@ -31,11 +33,11 @@ Example.stress = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// scene code // scene code
var stack = Composites.stack(90, 50, 18, 15, 0, 0, function(x, y) { var stack = Composites.stack(90, 600 - 25 - 15 * 35, 18, 15, 0, 0, function(x, y) {
return Bodies.rectangle(x, y, 35, 35); return Bodies.rectangle(x, y, 35, 35);
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -55,7 +57,7 @@ Example.stress = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -79,6 +81,7 @@ Example.stress = function() {
}; };
}; };
Example.stress.title = 'Stress';
Example.stress.for = '>=0.14.2'; Example.stress.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -7,7 +7,7 @@ Example.stress2 = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -20,7 +20,9 @@ Example.stress2 = function() {
engine: engine, engine: engine,
options: { options: {
width: 800, width: 800,
height: 600 height: 600,
showStats: true,
showPerformance: true
} }
}); });
@ -31,11 +33,11 @@ Example.stress2 = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// scene code // scene code
var stack = Composites.stack(100, 120, 25, 18, 0, 0, function(x, y) { var stack = Composites.stack(100, 600 - 25 - 18 * 25, 25, 18, 0, 0, function(x, y) {
return Bodies.rectangle(x, y, 25, 25); return Bodies.rectangle(x, y, 25, 25);
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
@ -55,7 +57,7 @@ Example.stress2 = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -79,6 +81,7 @@ Example.stress2 = function() {
}; };
}; };
Example.stress2.title = 'Stress 2';
Example.stress2.for = '>=0.14.2'; Example.stress2.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

111
examples/stress3.js Normal file
View file

@ -0,0 +1,111 @@
var Example = Example || {};
Example.stress3 = function() {
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Composites = Matter.Composites,
Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse,
Composite = Matter.Composite,
Bodies = Matter.Bodies;
// create engine
var engine = Engine.create({
positionIterations: 10,
velocityIterations: 10
});
var world = engine.world;
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600,
showStats: true,
showPerformance: true
}
});
Render.run(render);
// create runner
var runner = Runner.create({
isFixed: true
});
Runner.run(runner, engine);
// add bodies
var scale = 0.3;
var stack = Composites.stack(40, 40, 38, 18, 0, 0, function(x, y) {
var sides = Math.round(Common.random(1, 8));
switch (Math.round(Common.random(0, 1))) {
case 0:
if (Common.random() < 0.8) {
return Bodies.rectangle(x, y, Common.random(25, 50) * scale, Common.random(25, 50) * scale);
} else {
return Bodies.rectangle(x, y, Common.random(80, 120) * scale, Common.random(25, 30) * scale);
}
case 1:
return Bodies.polygon(x, y, sides, Common.random(25, 50) * scale);
}
});
Composite.add(world, stack);
Composite.add(world, [
// walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
Bodies.rectangle(0, 300, 50, 600, { isStatic: true })
]);
// add mouse control
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.2,
render: {
visible: false
}
}
});
Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering
render.mouse = mouse;
// fit the render viewport to the scene
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: 800, y: 600 }
});
// context for MatterTools.Demo
return {
engine: engine,
runner: runner,
render: render,
canvas: render.canvas,
stop: function() {
Matter.Render.stop(render);
Matter.Runner.stop(runner);
}
};
};
Example.stress3.title = 'Stress 3';
Example.stress3.for = '>=0.14.2';
if (typeof module !== 'undefined') {
module.exports = Example.stress3;
}

View file

@ -7,11 +7,14 @@ Example.svg = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Vertices = Matter.Vertices, Vertices = Matter.Vertices,
Svg = Matter.Svg, Svg = Matter.Svg,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// provide concave decomposition support library
Common.setDecomp(require('poly-decomp'));
// create engine // create engine
var engine = Engine.create(), var engine = Engine.create(),
world = engine.world; world = engine.world;
@ -33,45 +36,46 @@ Example.svg = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
var svgs = [ if (typeof fetch !== 'undefined') {
'iconmonstr-check-mark-8-icon', var select = function(root, selector) {
'iconmonstr-paperclip-2-icon', return Array.prototype.slice.call(root.querySelectorAll(selector));
'iconmonstr-puzzle-icon', };
'iconmonstr-user-icon'
];
if (typeof $ !== 'undefined') { var loadSvg = function(url) {
for (var i = 0; i < svgs.length; i += 1) { return fetch(url)
(function(i) { .then(function(response) { return response.text(); })
$.get('./svg/' + svgs[i] + '.svg').done(function(data) { .then(function(raw) { return (new window.DOMParser()).parseFromString(raw, 'image/svg+xml'); });
var vertexSets = [], };
color = Common.choose(['#f19648', '#f5d259', '#f55a3c', '#063e7b', '#ececd1']);
$(data).find('path').each(function(i, path) { ([
var points = Svg.pathToVertices(path, 30); './svg/iconmonstr-check-mark-8-icon.svg',
vertexSets.push(Vertices.scale(points, 0.4, 0.4)); './svg/iconmonstr-paperclip-2-icon.svg',
}); './svg/iconmonstr-puzzle-icon.svg',
'./svg/iconmonstr-user-icon.svg'
]).forEach(function(path, i) {
loadSvg(path).then(function(root) {
var color = Common.choose(['#f19648', '#f5d259', '#f55a3c', '#063e7b', '#ececd1']);
World.add(world, Bodies.fromVertices(100 + i * 150, 200 + i * 50, vertexSets, { var vertexSets = select(root, 'path')
render: { .map(function(path) { return Vertices.scale(Svg.pathToVertices(path, 30), 0.4, 0.4); });
fillStyle: color,
strokeStyle: color,
lineWidth: 1
}
}, true));
});
})(i);
}
$.get('./svg/svg.svg').done(function(data) { Composite.add(world, Bodies.fromVertices(100 + i * 150, 200 + i * 50, vertexSets, {
var vertexSets = [], render: {
color = Common.choose(['#f19648', '#f5d259', '#f55a3c', '#063e7b', '#ececd1']); fillStyle: color,
strokeStyle: color,
$(data).find('path').each(function(i, path) { lineWidth: 1
vertexSets.push(Svg.pathToVertices(path, 30)); }
}, true));
}); });
});
World.add(world, Bodies.fromVertices(400, 80, vertexSets, { loadSvg('./svg/svg.svg').then(function(root) {
var color = Common.choose(['#f19648', '#f5d259', '#f55a3c', '#063e7b', '#ececd1']);
var vertexSets = select(root, 'path')
.map(function(path) { return Svg.pathToVertices(path, 30); });
Composite.add(world, Bodies.fromVertices(400, 80, vertexSets, {
render: { render: {
fillStyle: color, fillStyle: color,
strokeStyle: color, strokeStyle: color,
@ -79,9 +83,11 @@ Example.svg = function() {
} }
}, true)); }, true));
}); });
} else {
Common.warn('Fetch is not available. Could not load SVG.');
} }
World.add(world, [ Composite.add(world, [
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true }), Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
@ -100,7 +106,7 @@ Example.svg = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -124,7 +130,8 @@ Example.svg = function() {
}; };
}; };
Example.svg.for = '>=0.14.2'; Example.svg.title = 'Concave SVG Paths';
Example.svg.for = '>0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.svg; module.exports = Example.svg;

View file

@ -8,11 +8,14 @@ Example.terrain = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Query = Matter.Query, Query = Matter.Query,
Svg = Matter.Svg, Svg = Matter.Svg,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// provide concave decomposition support library
Common.setDecomp(require('poly-decomp'));
// create engine // create engine
var engine = Engine.create(), var engine = Engine.create(),
world = engine.world; world = engine.world;
@ -34,39 +37,48 @@ Example.terrain = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
var terrain; if (typeof fetch !== 'undefined') {
var select = function(root, selector) {
return Array.prototype.slice.call(root.querySelectorAll(selector));
};
if (typeof $ !== 'undefined') { var loadSvg = function(url) {
$.get('./svg/terrain.svg').done(function(data) { return fetch(url)
var vertexSets = []; .then(function(response) { return response.text(); })
.then(function(raw) { return (new window.DOMParser()).parseFromString(raw, 'image/svg+xml'); });
};
$(data).find('path').each(function(i, path) { loadSvg('./svg/terrain.svg')
vertexSets.push(Svg.pathToVertices(path, 30)); .then(function(root) {
var paths = select(root, 'path');
var vertexSets = paths.map(function(path) { return Svg.pathToVertices(path, 30); });
var terrain = Bodies.fromVertices(400, 350, vertexSets, {
isStatic: true,
render: {
fillStyle: '#060a19',
strokeStyle: '#060a19',
lineWidth: 1
}
}, true);
Composite.add(world, terrain);
var bodyOptions = {
frictionAir: 0,
friction: 0.0001,
restitution: 0.6
};
Composite.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);
}
}));
}); });
} else {
terrain = Bodies.fromVertices(400, 350, vertexSets, { Common.warn('Fetch is not available. Could not load SVG.');
isStatic: true,
render: {
fillStyle: '#060a19',
strokeStyle: '#060a19',
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 // add mouse control
@ -81,7 +93,7 @@ Example.terrain = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -105,7 +117,8 @@ Example.terrain = function() {
}; };
}; };
Example.terrain.for = '>=0.14.2'; Example.terrain.title = 'Terrain';
Example.terrain.for = '>0.16.1';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {
module.exports = Example.terrain; module.exports = Example.terrain;

View file

@ -11,7 +11,6 @@ Example.timescale = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
// create engine // create engine
@ -36,7 +35,7 @@ Example.timescale = function() {
Runner.run(runner, engine); Runner.run(runner, engine);
// add bodies // add bodies
World.add(world, [ Composite.add(world, [
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
Bodies.rectangle(400, 600, 800, 50, { isStatic: true }), Bodies.rectangle(400, 600, 800, 50, { isStatic: true }),
Bodies.rectangle(800, 300, 50, 600, { isStatic: true }), Bodies.rectangle(800, 300, 50, 600, { isStatic: true }),
@ -95,11 +94,11 @@ Example.timescale = function() {
restitution: 0.8 restitution: 0.8
}; };
World.add(world, Composites.stack(20, 100, 15, 3, 20, 40, function(x, y) { Composite.add(world, Composites.stack(20, 100, 15, 3, 20, 40, function(x, y) {
return Bodies.circle(x, y, Common.random(10, 20), bodyOptions); return Bodies.circle(x, y, Common.random(10, 20), bodyOptions);
})); }));
World.add(world, Composites.stack(50, 50, 8, 3, 0, 0, function(x, y) { Composite.add(world, Composites.stack(50, 50, 8, 3, 0, 0, function(x, y) {
switch (Math.round(Common.random(0, 1))) { switch (Math.round(Common.random(0, 1))) {
case 0: case 0:
@ -126,7 +125,7 @@ Example.timescale = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -150,6 +149,7 @@ Example.timescale = function() {
}; };
}; };
Example.timescale.title = 'Time Scaling';
Example.timescale.for = '>=0.14.2'; Example.timescale.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -9,7 +9,7 @@ Example.views = function() {
Common = Matter.Common, Common = Matter.Common,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Vector = Matter.Vector, Vector = Matter.Vector,
Bounds = Matter.Bounds, Bounds = Matter.Bounds,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
@ -48,13 +48,13 @@ Example.views = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
// add bodies // add bodies
var stack = Composites.stack(20, 20, 15, 4, 0, 0, function(x, y) { var stack = Composites.stack(20, 20, 10, 4, 0, 0, function(x, y) {
switch (Math.round(Common.random(0, 1))) { switch (Math.round(Common.random(0, 1))) {
case 0: case 0:
@ -70,7 +70,7 @@ Example.views = function() {
} }
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
@ -85,11 +85,11 @@ Example.views = function() {
y: render.options.height * 0.5 y: render.options.height * 0.5
}; };
// make the world bounds a little bigger than the render bounds // create limits for the viewport
world.bounds.min.x = -300; var extents = {
world.bounds.min.y = -300; min: { x: -300, y: -300 },
world.bounds.max.x = 1100; max: { x: 1100, y: 900 }
world.bounds.max.y = 900; };
// keep track of current bounds scale (view zoom) // keep track of current bounds scale (view zoom)
var boundsScaleTarget = 1, var boundsScaleTarget = 1,
@ -98,8 +98,8 @@ Example.views = function() {
y: 1 y: 1
}; };
// use the engine tick event to control our view // use a render event to control our view
Events.on(engine, 'beforeTick', function() { Events.on(render, 'beforeRender', function() {
var world = engine.world, var world = engine.world,
mouse = mouseConstraint.mouse, mouse = mouseConstraint.mouse,
translate; translate;
@ -148,18 +148,18 @@ Example.views = function() {
translate = Vector.mult(direction, speed); translate = Vector.mult(direction, speed);
// prevent the view moving outside the world bounds // prevent the view moving outside the extents
if (render.bounds.min.x + translate.x < world.bounds.min.x) if (render.bounds.min.x + translate.x < extents.min.x)
translate.x = world.bounds.min.x - render.bounds.min.x; translate.x = extents.min.x - render.bounds.min.x;
if (render.bounds.max.x + translate.x > world.bounds.max.x) if (render.bounds.max.x + translate.x > extents.max.x)
translate.x = world.bounds.max.x - render.bounds.max.x; translate.x = extents.max.x - render.bounds.max.x;
if (render.bounds.min.y + translate.y < world.bounds.min.y) if (render.bounds.min.y + translate.y < extents.min.y)
translate.y = world.bounds.min.y - render.bounds.min.y; translate.y = extents.min.y - render.bounds.min.y;
if (render.bounds.max.y + translate.y > world.bounds.max.y) if (render.bounds.max.y + translate.y > extents.max.y)
translate.y = world.bounds.max.y - render.bounds.max.y; translate.y = extents.max.y - render.bounds.max.y;
// move the view // move the view
Bounds.translate(render.bounds, translate); Bounds.translate(render.bounds, translate);
@ -182,6 +182,7 @@ Example.views = function() {
}; };
}; };
Example.views.title = 'Views';
Example.views.for = '>=0.14.2'; Example.views.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

View file

@ -7,7 +7,7 @@ Example.wreckingBall = function() {
Composites = Matter.Composites, Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint, MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse, Mouse = Matter.Mouse,
World = Matter.World, Composite = Matter.Composite,
Constraint = Matter.Constraint, Constraint = Matter.Constraint,
Bodies = Matter.Bodies; Bodies = Matter.Bodies;
@ -34,13 +34,13 @@ Example.wreckingBall = function() {
// add bodies // add bodies
var rows = 10, var rows = 10,
yy = 600 - 21 - 40 * rows; yy = 600 - 25 - 40 * rows;
var stack = Composites.stack(400, yy, 5, rows, 0, 0, function(x, y) { var stack = Composites.stack(400, yy, 5, rows, 0, 0, function(x, y) {
return Bodies.rectangle(x, y, 40, 40); return Bodies.rectangle(x, y, 40, 40);
}); });
World.add(world, [ Composite.add(world, [
stack, stack,
// walls // walls
Bodies.rectangle(400, 0, 800, 50, { isStatic: true }), Bodies.rectangle(400, 0, 800, 50, { isStatic: true }),
@ -51,8 +51,8 @@ Example.wreckingBall = function() {
var ball = Bodies.circle(100, 400, 50, { density: 0.04, frictionAir: 0.005}); var ball = Bodies.circle(100, 400, 50, { density: 0.04, frictionAir: 0.005});
World.add(world, ball); Composite.add(world, ball);
World.add(world, Constraint.create({ Composite.add(world, Constraint.create({
pointA: { x: 300, y: 100 }, pointA: { x: 300, y: 100 },
bodyB: ball bodyB: ball
})); }));
@ -69,7 +69,7 @@ Example.wreckingBall = function() {
} }
}); });
World.add(world, mouseConstraint); Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering // keep the mouse in sync with rendering
render.mouse = mouse; render.mouse = mouse;
@ -93,6 +93,7 @@ Example.wreckingBall = function() {
}; };
}; };
Example.wreckingBall.title = 'Wrecking Ball';
Example.wreckingBall.for = '>=0.14.2'; Example.wreckingBall.for = '>=0.14.2';
if (typeof module !== 'undefined') { if (typeof module !== 'undefined') {

20857
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "matter-js", "name": "matter-js",
"version": "0.15.0", "version": "0.18.0",
"license": "MIT", "license": "MIT",
"homepage": "http://brm.io/matter-js/", "homepage": "http://brm.io/matter-js/",
"author": "Liam Brummitt <liam@brm.io> (http://brm.io/)", "author": "Liam Brummitt <liam@brm.io> (http://brm.io/)",
@ -22,35 +22,46 @@
"devDependencies": { "devDependencies": {
"conventional-changelog-cli": "^2.1.1", "conventional-changelog-cli": "^2.1.1",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"html-webpack-plugin": "^4.5.1",
"jest": "^25.1.0", "jest": "^25.1.0",
"jest-worker": "^24.9.0", "jest-worker": "^24.9.0",
"json-stringify-pretty-compact": "^2.0.0", "json-stringify-pretty-compact": "^2.0.0",
"matter-tools": "^0.14.0",
"matter-wrap": "^0.2.0",
"mock-require": "^3.0.3",
"pathseg": "^1.2.0",
"poly-decomp": "^0.3.0",
"puppeteer-core": "^5.5.0", "puppeteer-core": "^5.5.0",
"run-sequence": "^2.2.1", "terser-webpack-plugin": "^4.2.3",
"webpack": "^4.44.2", "webpack": "^4.46.0",
"webpack-bundle-analyzer": "^4.4.0",
"webpack-cli": "^3.3.11", "webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^3.11.1"
}, },
"scripts": { "scripts": {
"dev": "webpack-dev-server --watch-content-base", "start": "npm run dev",
"build": "webpack --mode=production & webpack --mode=production --env.MINIMIZE", "dev": "npm run serve -- --open",
"build-alpha": "webpack --mode=production --env.ALPHA & webpack --mode=production --env.MINIMIZE --env.ALPHA", "serve": "webpack-dev-server --no-cache --mode development --config webpack.demo.config.js",
"build-examples": "webpack --config webpack.examples.config.js --mode=production & webpack --config webpack.examples.config.js --mode=production --env.MINIMIZE", "watch": "nodemon --watch webpack.demo.config.js --exec \"npm run serve\"",
"build-examples-alpha": "webpack --config webpack.examples.config.js --mode=production --env.ALPHA & webpack --config webpack.examples.config.js --mode=production --env.MINIMIZE --env.ALPHA", "build": "webpack --mode=production --no-hot --no-watch & webpack --mode=production --no-hot --no-watch --env.MINIMIZE",
"lint": "eslint 'src/**/*.js' 'demo/js/Demo.js' 'demo/js/Compare.js' 'examples/*.js' 'webpack.*.js'", "build-alpha": "webpack --mode=production --no-hot --no-watch --env.KIND=alpha & webpack --mode=production --no-hot --no-watch --env.MINIMIZE --env.KIND=alpha",
"build-dev": "webpack --mode=production --no-hot --no-watch --env.KIND=dev & webpack --mode=production --no-hot --no-watch --env.MINIMIZE --env.KIND=dev",
"build-demo": "rm -rf ./demo/js && webpack --no-hot --no-watch --config webpack.demo.config.js --mode=production && webpack --no-hot --no-watch --config webpack.demo.config.js --mode=production --env.MINIMIZE",
"lint": "eslint 'src/**/*.js' 'demo/src/**/*.js' 'examples/*.js' 'webpack.*.js'",
"doc": "yuidoc --config yuidoc.json --project-version $npm_package_version", "doc": "yuidoc --config yuidoc.json --project-version $npm_package_version",
"doc-watch": "nodemon --delay 3 --watch 'matter-doc-theme' --watch src -e 'js,html,css,handlebars' --exec 'npm run doc'",
"benchmark": "EXAMPLES=stress3 npm run test-node",
"test": "npm run test-node", "test": "npm run test-node",
"test-all": "jest", "test-node": "npm run build-dev && node --expose-gc node_modules/.bin/jest --force-exit --no-cache --runInBand ./test/Examples.spec.js",
"test-browser": "node --expose-gc node_modules/.bin/jest --force-exit --no-cache --runInBand ./test/Browser.spec.js",
"test-all": "npm run test-node && npm run test-browser",
"test-save": "SAVE=true npm run test-node", "test-save": "SAVE=true npm run test-node",
"test-watch": "npm run test-node -- --watch", "test-watch": "npm run test-node -- --watch",
"test-node": "jest ./test/Examples.spec.js",
"test-browser": "jest ./test/Browser.spec.js",
"changelog": "conventional-changelog -i CHANGELOG.md -s -r", "changelog": "conventional-changelog -i CHANGELOG.md -s -r",
"release": "npm version --no-git-tag-version", "release": "npm version --no-git-tag-version",
"preversion": "git checkout master && npm run lint && SAVE=true npm run test-all", "preversion": "git checkout master && npm run lint && SAVE=true npm run test-all",
"version": "git checkout -b release/$npm_package_version && npm run build" "version": "git checkout -b release/$npm_package_version && npm run build"
}, },
"dependencies": {},
"files": [ "files": [
"src", "src",
"build", "build",

View file

@ -1,8 +1,10 @@
/** /**
* The `Matter.Composite` module contains methods for creating and manipulating composite bodies. * A composite is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite` objects.
* A composite body is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite`, therefore composites form a tree structure. *
* It is important to use the functions in this module to modify composites, rather than directly modifying their properties. * They are a container that can represent complex objects made of multiple parts, even if they are not physically connected.
* Note that the `Matter.World` object is also a type of `Matter.Composite` and as such all composite methods here can also operate on a `Matter.World`. * A composite could contain anything from a single body all the way up to a whole world.
*
* When making any changes to composites, use the included functions rather than changing their properties directly.
* *
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). * See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples).
* *
@ -37,7 +39,12 @@ var Body = require('./Body');
constraints: [], constraints: [],
composites: [], composites: [],
label: 'Composite', label: 'Composite',
plugin: {} plugin: {},
cache: {
allBodies: null,
allConstraints: null,
allComposites: null
}
}, options); }, options);
}; };
@ -45,6 +52,7 @@ var Body = require('./Body');
* Sets the composite's `isModified` flag. * Sets the composite's `isModified` flag.
* If `updateParents` is true, all parents will be set (default: false). * If `updateParents` is true, all parents will be set (default: false).
* If `updateChildren` is true, all children will be set (default: false). * If `updateChildren` is true, all children will be set (default: false).
* @private
* @method setModified * @method setModified
* @param {composite} composite * @param {composite} composite
* @param {boolean} isModified * @param {boolean} isModified
@ -54,12 +62,18 @@ var Body = require('./Body');
Composite.setModified = function(composite, isModified, updateParents, updateChildren) { Composite.setModified = function(composite, isModified, updateParents, updateChildren) {
composite.isModified = isModified; composite.isModified = isModified;
if (isModified && composite.cache) {
composite.cache.allBodies = null;
composite.cache.allConstraints = null;
composite.cache.allComposites = null;
}
if (updateParents && composite.parent) { if (updateParents && composite.parent) {
Composite.setModified(composite.parent, isModified, updateParents, updateChildren); Composite.setModified(composite.parent, isModified, updateParents, updateChildren);
} }
if (updateChildren) { if (updateChildren) {
for(var i = 0; i < composite.composites.length; i++) { for (var i = 0; i < composite.composites.length; i++) {
var childComposite = composite.composites[i]; var childComposite = composite.composites[i];
Composite.setModified(childComposite, isModified, updateParents, updateChildren); Composite.setModified(childComposite, isModified, updateParents, updateChildren);
} }
@ -67,11 +81,11 @@ var Body = require('./Body');
}; };
/** /**
* Generic add function. Adds one or many body(s), constraint(s) or a composite(s) to the given composite. * Generic single or multi-add function. Adds a single or an array of body(s), constraint(s) or composite(s) to the given composite.
* Triggers `beforeAdd` and `afterAdd` events on the `composite`. * Triggers `beforeAdd` and `afterAdd` events on the `composite`.
* @method add * @method add
* @param {composite} composite * @param {composite} composite
* @param {} object * @param {object|array} object A single or an array of body(s), constraint(s) or composite(s)
* @return {composite} The original composite with the objects added * @return {composite} The original composite with the objects added
*/ */
Composite.add = function(composite, object) { Composite.add = function(composite, object) {
@ -117,7 +131,7 @@ var Body = require('./Body');
* Triggers `beforeRemove` and `afterRemove` events on the `composite`. * Triggers `beforeRemove` and `afterRemove` events on the `composite`.
* @method remove * @method remove
* @param {composite} composite * @param {composite} composite
* @param {} object * @param {object|array} object
* @param {boolean} [deep=false] * @param {boolean} [deep=false]
* @return {composite} The original composite with the objects removed * @return {composite} The original composite with the objects removed
*/ */
@ -180,7 +194,6 @@ var Body = require('./Body');
var position = Common.indexOf(compositeA.composites, compositeB); var position = Common.indexOf(compositeA.composites, compositeB);
if (position !== -1) { if (position !== -1) {
Composite.removeCompositeAt(compositeA, position); Composite.removeCompositeAt(compositeA, position);
Composite.setModified(compositeA, true, true, false);
} }
if (deep) { if (deep) {
@ -233,7 +246,6 @@ var Body = require('./Body');
var position = Common.indexOf(composite.bodies, body); var position = Common.indexOf(composite.bodies, body);
if (position !== -1) { if (position !== -1) {
Composite.removeBodyAt(composite, position); Composite.removeBodyAt(composite, position);
Composite.setModified(composite, true, true, false);
} }
if (deep) { if (deep) {
@ -334,6 +346,7 @@ var Body = require('./Body');
composite.constraints.length = 0; composite.constraints.length = 0;
composite.composites.length = 0; composite.composites.length = 0;
Composite.setModified(composite, true, true, false); Composite.setModified(composite, true, true, false);
return composite; return composite;
@ -346,11 +359,19 @@ var Body = require('./Body');
* @return {body[]} All the bodies * @return {body[]} All the bodies
*/ */
Composite.allBodies = function(composite) { Composite.allBodies = function(composite) {
if (composite.cache && composite.cache.allBodies) {
return composite.cache.allBodies;
}
var bodies = [].concat(composite.bodies); var bodies = [].concat(composite.bodies);
for (var i = 0; i < composite.composites.length; i++) for (var i = 0; i < composite.composites.length; i++)
bodies = bodies.concat(Composite.allBodies(composite.composites[i])); bodies = bodies.concat(Composite.allBodies(composite.composites[i]));
if (composite.cache) {
composite.cache.allBodies = bodies;
}
return bodies; return bodies;
}; };
@ -361,11 +382,19 @@ var Body = require('./Body');
* @return {constraint[]} All the constraints * @return {constraint[]} All the constraints
*/ */
Composite.allConstraints = function(composite) { Composite.allConstraints = function(composite) {
if (composite.cache && composite.cache.allConstraints) {
return composite.cache.allConstraints;
}
var constraints = [].concat(composite.constraints); var constraints = [].concat(composite.constraints);
for (var i = 0; i < composite.composites.length; i++) for (var i = 0; i < composite.composites.length; i++)
constraints = constraints.concat(Composite.allConstraints(composite.composites[i])); constraints = constraints.concat(Composite.allConstraints(composite.composites[i]));
if (composite.cache) {
composite.cache.allConstraints = constraints;
}
return constraints; return constraints;
}; };
@ -376,11 +405,19 @@ var Body = require('./Body');
* @return {composite[]} All the composites * @return {composite[]} All the composites
*/ */
Composite.allComposites = function(composite) { Composite.allComposites = function(composite) {
if (composite.cache && composite.cache.allComposites) {
return composite.cache.allComposites;
}
var composites = [].concat(composite.composites); var composites = [].concat(composite.composites);
for (var i = 0; i < composite.composites.length; i++) for (var i = 0; i < composite.composites.length; i++)
composites = composites.concat(Composite.allComposites(composite.composites[i])); composites = composites.concat(Composite.allComposites(composite.composites[i]));
if (composite.cache) {
composite.cache.allComposites = composites;
}
return composites; return composites;
}; };
@ -447,8 +484,6 @@ var Body = require('./Body');
objects[i].id = Common.nextId(); objects[i].id = Common.nextId();
} }
Composite.setModified(composite, true, true, false);
return composite; return composite;
}; };
@ -467,8 +502,6 @@ var Body = require('./Body');
Body.translate(bodies[i], translation); Body.translate(bodies[i], translation);
} }
Composite.setModified(composite, true, true, false);
return composite; return composite;
}; };
@ -498,8 +531,6 @@ var Body = require('./Body');
Body.rotate(body, rotation); Body.rotate(body, rotation);
} }
Composite.setModified(composite, true, true, false);
return composite; return composite;
}; };
@ -528,8 +559,6 @@ var Body = require('./Body');
Body.scale(body, scaleX, scaleY); Body.scale(body, scaleX, scaleY);
} }
Composite.setModified(composite, true, true, false);
return composite; return composite;
}; };
@ -629,8 +658,7 @@ var Body = require('./Body');
/** /**
* A flag that specifies whether the composite has been modified during the current step. * A flag that specifies whether the composite has been modified during the current step.
* Most `Matter.Composite` methods will automatically set this flag to `true` to inform the engine of changes to be handled. * This is automatically managed when bodies, constraints or composites are added or removed.
* If you need to change it manually, you should use the `Composite.setModified` method.
* *
* @property isModified * @property isModified
* @type boolean * @type boolean
@ -682,4 +710,13 @@ var Body = require('./Body');
* @type {} * @type {}
*/ */
/**
* An object used for storing cached results for performance reasons.
* This is used internally only and is automatically managed.
*
* @private
* @property cache
* @type {}
*/
})(); })();

View file

@ -1,14 +1,15 @@
/** /**
* The `Matter.World` module contains methods for creating and manipulating the world composite. * This module has now been replaced by `Matter.Composite`.
* A `Matter.World` is a `Matter.Composite` body, which is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite`.
* A `Matter.World` has a few additional properties including `gravity` and `bounds`.
* It is important to use the functions in the `Matter.Composite` module to modify the world composite, rather than directly modifying its properties.
* There are also a few methods here that alias those in `Matter.Composite` for easier readability.
* *
* See the included usage [examples](https://github.com/liabru/matter-js/tree/master/examples). * All usage should be migrated to the equivalent functions found on `Matter.Composite`.
* For example `World.add(world, body)` now becomes `Composite.add(world, body)`.
*
* The property `world.gravity` has been moved to `engine.gravity`.
*
* For back-compatibility purposes this module will remain as a direct alias to `Matter.Composite` in the short term during migration.
* Eventually this alias module will be marked as deprecated and then later removed in a future release.
* *
* @class World * @class World
* @extends Composite
*/ */
var World = {}; var World = {};
@ -16,132 +17,19 @@ var World = {};
module.exports = World; module.exports = World;
var Composite = require('./Composite'); var Composite = require('./Composite');
var Constraint = require('../constraint/Constraint');
var Common = require('../core/Common'); var Common = require('../core/Common');
(function() { (function() {
/** /**
* Creates a new world composite. The options parameter is an object that specifies any properties you wish to override the defaults. * See above, aliases for back compatibility only
* See the properties section below for detailed information on what you can pass via the `options` object.
* @method create
* @constructor
* @param {} options
* @return {world} A new world
*/ */
World.create = function(options) { World.create = Composite.create;
var composite = Composite.create(); World.add = Composite.add;
World.remove = Composite.remove;
var defaults = { World.clear = Composite.clear;
label: 'World', World.addComposite = Composite.addComposite;
gravity: { World.addBody = Composite.addBody;
x: 0, World.addConstraint = Composite.addConstraint;
y: 1,
scale: 0.001
},
bounds: {
min: { x: -Infinity, y: -Infinity },
max: { x: Infinity, y: Infinity }
}
};
return Common.extend(composite, defaults, options);
};
/*
*
* Properties Documentation
*
*/
/**
* The gravity to apply on the world.
*
* @property gravity
* @type object
*/
/**
* The gravity x component.
*
* @property gravity.x
* @type object
* @default 0
*/
/**
* The gravity y component.
*
* @property gravity.y
* @type object
* @default 1
*/
/**
* The gravity scale factor.
*
* @property gravity.scale
* @type object
* @default 0.001
*/
/**
* A `Bounds` object that defines the world bounds for collision detection.
*
* @property bounds
* @type bounds
* @default { min: { x: -Infinity, y: -Infinity }, max: { x: Infinity, y: Infinity } }
*/
// World is a Composite body
// see src/module/Outro.js for these aliases:
/**
* An alias for Composite.add
* @method add
* @param {world} world
* @param {} object
* @return {composite} The original world with the objects added
*/
/**
* An alias for Composite.remove
* @method remove
* @param {world} world
* @param {} object
* @param {boolean} [deep=false]
* @return {composite} The original world with the objects removed
*/
/**
* An alias for Composite.clear
* @method clear
* @param {world} world
* @param {boolean} keepStatic
*/
/**
* An alias for Composite.addComposite
* @method addComposite
* @param {world} world
* @param {composite} composite
* @return {world} The original world with the objects from composite added
*/
/**
* An alias for Composite.addBody
* @method addBody
* @param {world} world
* @param {body} body
* @return {world} The original world with the body added
*/
/**
* An alias for Composite.addConstraint
* @method addConstraint
* @param {world} world
* @param {constraint} constraint
* @return {world} The original world with the constraint added
*/
})(); })();

408
src/collision/Collision.js Normal file
View file

@ -0,0 +1,408 @@
/**
* The `Matter.Collision` module contains methods for detecting collisions between a given pair of bodies.
*
* For efficient detection between a list of bodies, see `Matter.Detector` and `Matter.Query`.
*
* See `Matter.Engine` for collision events.
*
* @class Collision
*/
var Collision = {};
module.exports = Collision;
var Vertices = require('../geometry/Vertices');
var Pair = require('./Pair');
(function() {
var _supports = [];
var _overlapAB = {
overlap: 0,
axis: null
};
var _overlapBA = {
overlap: 0,
axis: null
};
/**
* Creates a new collision record.
* @method create
* @param {body} bodyA The first body part represented by the collision record
* @param {body} bodyB The second body part represented by the collision record
* @return {collision} A new collision record
*/
Collision.create = function(bodyA, bodyB) {
return {
pair: null,
collided: false,
bodyA: bodyA,
bodyB: bodyB,
parentA: bodyA.parent,
parentB: bodyB.parent,
depth: 0,
normal: { x: 0, y: 0 },
tangent: { x: 0, y: 0 },
penetration: { x: 0, y: 0 },
supports: []
};
};
/**
* Detect collision between two bodies.
* @method collides
* @param {body} bodyA
* @param {body} bodyB
* @param {pairs} [pairs] Optionally reuse collision records from existing pairs.
* @return {collision|null} A collision record if detected, otherwise null
*/
Collision.collides = function(bodyA, bodyB, pairs) {
Collision._overlapAxes(_overlapAB, bodyA.vertices, bodyB.vertices, bodyA.axes);
if (_overlapAB.overlap <= 0) {
return null;
}
Collision._overlapAxes(_overlapBA, bodyB.vertices, bodyA.vertices, bodyB.axes);
if (_overlapBA.overlap <= 0) {
return null;
}
// reuse collision records for gc efficiency
var pair = pairs && pairs.table[Pair.id(bodyA, bodyB)],
collision;
if (!pair) {
collision = Collision.create(bodyA, bodyB);
collision.collided = true;
collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
collision.parentA = collision.bodyA.parent;
collision.parentB = collision.bodyB.parent;
} else {
collision = pair.collision;
}
bodyA = collision.bodyA;
bodyB = collision.bodyB;
var minOverlap;
if (_overlapAB.overlap < _overlapBA.overlap) {
minOverlap = _overlapAB;
} else {
minOverlap = _overlapBA;
}
var normal = collision.normal,
supports = collision.supports,
minAxis = minOverlap.axis,
minAxisX = minAxis.x,
minAxisY = minAxis.y;
// ensure normal is facing away from bodyA
if (minAxisX * (bodyB.position.x - bodyA.position.x) + minAxisY * (bodyB.position.y - bodyA.position.y) < 0) {
normal.x = minAxisX;
normal.y = minAxisY;
} else {
normal.x = -minAxisX;
normal.y = -minAxisY;
}
collision.tangent.x = -normal.y;
collision.tangent.y = normal.x;
collision.depth = minOverlap.overlap;
collision.penetration.x = normal.x * collision.depth;
collision.penetration.y = normal.y * collision.depth;
// find support points, there is always either exactly one or two
var supportsB = Collision._findSupports(bodyA, bodyB, normal, 1),
supportCount = 0;
// find the supports from bodyB that are inside bodyA
if (Vertices.contains(bodyA.vertices, supportsB[0])) {
supports[supportCount++] = supportsB[0];
}
if (Vertices.contains(bodyA.vertices, supportsB[1])) {
supports[supportCount++] = supportsB[1];
}
// find the supports from bodyA that are inside bodyB
if (supportCount < 2) {
var supportsA = Collision._findSupports(bodyB, bodyA, normal, -1);
if (Vertices.contains(bodyB.vertices, supportsA[0])) {
supports[supportCount++] = supportsA[0];
}
if (supportCount < 2 && Vertices.contains(bodyB.vertices, supportsA[1])) {
supports[supportCount++] = supportsA[1];
}
}
// account for the edge case of overlapping but no vertex containment
if (supportCount === 0) {
supports[supportCount++] = supportsB[0];
}
// update supports array size
supports.length = supportCount;
return collision;
};
/**
* Find the overlap between two sets of vertices.
* @method _overlapAxes
* @private
* @param {object} result
* @param {vertices} verticesA
* @param {vertices} verticesB
* @param {axes} axes
*/
Collision._overlapAxes = function(result, verticesA, verticesB, axes) {
var verticesALength = verticesA.length,
verticesBLength = verticesB.length,
verticesAX = verticesA[0].x,
verticesAY = verticesA[0].y,
verticesBX = verticesB[0].x,
verticesBY = verticesB[0].y,
axesLength = axes.length,
overlapMin = Number.MAX_VALUE,
overlapAxisNumber = 0,
overlap,
overlapAB,
overlapBA,
dot,
i,
j;
for (i = 0; i < axesLength; i++) {
var axis = axes[i],
axisX = axis.x,
axisY = axis.y,
minA = verticesAX * axisX + verticesAY * axisY,
minB = verticesBX * axisX + verticesBY * axisY,
maxA = minA,
maxB = minB;
for (j = 1; j < verticesALength; j += 1) {
dot = verticesA[j].x * axisX + verticesA[j].y * axisY;
if (dot > maxA) {
maxA = dot;
} else if (dot < minA) {
minA = dot;
}
}
for (j = 1; j < verticesBLength; j += 1) {
dot = verticesB[j].x * axisX + verticesB[j].y * axisY;
if (dot > maxB) {
maxB = dot;
} else if (dot < minB) {
minB = dot;
}
}
overlapAB = maxA - minB;
overlapBA = maxB - minA;
overlap = overlapAB < overlapBA ? overlapAB : overlapBA;
if (overlap < overlapMin) {
overlapMin = overlap;
overlapAxisNumber = i;
if (overlap <= 0) {
// can not be intersecting
break;
}
}
}
result.axis = axes[overlapAxisNumber];
result.overlap = overlapMin;
};
/**
* Projects vertices on an axis and returns an interval.
* @method _projectToAxis
* @private
* @param {} projection
* @param {} vertices
* @param {} axis
*/
Collision._projectToAxis = function(projection, vertices, axis) {
var min = vertices[0].x * axis.x + vertices[0].y * axis.y,
max = min;
for (var i = 1; i < vertices.length; i += 1) {
var dot = vertices[i].x * axis.x + vertices[i].y * axis.y;
if (dot > max) {
max = dot;
} else if (dot < min) {
min = dot;
}
}
projection.min = min;
projection.max = max;
};
/**
* Finds supporting vertices given two bodies along a given direction using hill-climbing.
* @method _findSupports
* @private
* @param {body} bodyA
* @param {body} bodyB
* @param {vector} normal
* @param {number} direction
* @return [vector]
*/
Collision._findSupports = function(bodyA, bodyB, normal, direction) {
var vertices = bodyB.vertices,
verticesLength = vertices.length,
bodyAPositionX = bodyA.position.x,
bodyAPositionY = bodyA.position.y,
normalX = normal.x * direction,
normalY = normal.y * direction,
nearestDistance = Number.MAX_VALUE,
vertexA,
vertexB,
vertexC,
distance,
j;
// find deepest vertex relative to the axis
for (j = 0; j < verticesLength; j += 1) {
vertexB = vertices[j];
distance = normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y);
// convex hill-climbing
if (distance < nearestDistance) {
nearestDistance = distance;
vertexA = vertexB;
}
}
// measure next vertex
vertexC = vertices[(verticesLength + vertexA.index - 1) % verticesLength];
nearestDistance = normalX * (bodyAPositionX - vertexC.x) + normalY * (bodyAPositionY - vertexC.y);
// compare with previous vertex
vertexB = vertices[(vertexA.index + 1) % verticesLength];
if (normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y) < nearestDistance) {
_supports[0] = vertexA;
_supports[1] = vertexB;
return _supports;
}
_supports[0] = vertexA;
_supports[1] = vertexC;
return _supports;
};
/*
*
* Properties Documentation
*
*/
/**
* A reference to the pair using this collision record, if there is one.
*
* @property pair
* @type {pair|null}
* @default null
*/
/**
* A flag that indicates if the bodies were colliding when the collision was last updated.
*
* @property collided
* @type boolean
* @default false
*/
/**
* The first body part represented by the collision (see also `collision.parentA`).
*
* @property bodyA
* @type body
*/
/**
* The second body part represented by the collision (see also `collision.parentB`).
*
* @property bodyB
* @type body
*/
/**
* The first body represented by the collision (i.e. `collision.bodyA.parent`).
*
* @property parentA
* @type body
*/
/**
* The second body represented by the collision (i.e. `collision.bodyB.parent`).
*
* @property parentB
* @type body
*/
/**
* A `Number` that represents the minimum separating distance between the bodies along the collision normal.
*
* @readOnly
* @property depth
* @type number
* @default 0
*/
/**
* A normalised `Vector` that represents the direction between the bodies that provides the minimum separating distance.
*
* @property normal
* @type vector
* @default { x: 0, y: 0 }
*/
/**
* A normalised `Vector` that is the tangent direction to the collision normal.
*
* @property tangent
* @type vector
* @default { x: 0, y: 0 }
*/
/**
* A `Vector` that represents the direction and depth of the collision.
*
* @property penetration
* @type vector
* @default { x: 0, y: 0 }
*/
/**
* An array of body vertices that represent the support points in the collision.
* These are the deepest vertices (along the collision normal) of each body that are contained by the other body's vertices.
*
* @property supports
* @type vector[]
* @default []
*/
})();

View file

@ -18,21 +18,10 @@ module.exports = Contact;
*/ */
Contact.create = function(vertex) { Contact.create = function(vertex) {
return { return {
id: Contact.id(vertex),
vertex: vertex, vertex: vertex,
normalImpulse: 0, normalImpulse: 0,
tangentImpulse: 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;
};
})(); })();

View file

@ -1,85 +1,132 @@
/** /**
* The `Matter.Detector` module contains methods for detecting collisions given a set of pairs. * The `Matter.Detector` module contains methods for efficiently detecting collisions between a list of bodies using a broadphase algorithm.
* *
* @class Detector * @class Detector
*/ */
// TODO: speculative contacts
var Detector = {}; var Detector = {};
module.exports = Detector; module.exports = Detector;
var SAT = require('./SAT'); var Common = require('../core/Common');
var Pair = require('./Pair'); var Collision = require('./Collision');
var Bounds = require('../geometry/Bounds');
(function() { (function() {
/** /**
* Finds all collisions given a list of pairs. * Creates a new collision detector.
* @method collisions * @method create
* @param {pair[]} broadphasePairs * @param {} options
* @param {engine} engine * @return {detector} A new collision detector
* @param {number} delta
* @return {array} collisions
*/ */
Detector.collisions = function(broadphasePairs, engine, delta) { Detector.create = function(options) {
var defaults = {
bodies: [],
pairs: null
};
return Common.extend(defaults, options);
};
/**
* Sets the list of bodies in the detector.
* @method setBodies
* @param {detector} detector
* @param {body[]} bodies
*/
Detector.setBodies = function(detector, bodies) {
detector.bodies = bodies.slice(0);
};
/**
* Clears the detector including its list of bodies.
* @method clear
* @param {detector} detector
*/
Detector.clear = function(detector) {
detector.bodies = [];
};
/**
* Efficiently finds all collisions among all the bodies in `detector.bodies` using a broadphase algorithm.
*
* _Note:_ The specific ordering of collisions returned is not guaranteed between releases and may change for performance reasons.
* If a specific ordering is required then apply a sort to the resulting array.
* @method collisions
* @param {detector} detector
* @return {collision[]} collisions
*/
Detector.collisions = function(detector) {
var collisions = [], var collisions = [],
pairsTable = engine.pairs.table; pairs = detector.pairs,
bodies = detector.bodies,
bodiesLength = bodies.length,
canCollide = Detector.canCollide,
collides = Collision.collides,
i,
j;
// @if DEBUG bodies.sort(Detector._compareBoundsX);
var metrics = engine.metrics;
// @endif
for (var i = 0; i < broadphasePairs.length; i++) {
var bodyA = broadphasePairs[i][0],
bodyB = broadphasePairs[i][1];
if ((bodyA.isStatic || bodyA.isSleeping) && (bodyB.isStatic || bodyB.isSleeping)) for (i = 0; i < bodiesLength; i++) {
continue; var bodyA = bodies[i],
boundsA = bodyA.bounds,
if (!Detector.canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) boundXMax = bodyA.bounds.max.x,
continue; boundYMax = bodyA.bounds.max.y,
boundYMin = bodyA.bounds.min.y,
bodyAStatic = bodyA.isStatic || bodyA.isSleeping,
partsALength = bodyA.parts.length,
partsASingle = partsALength === 1;
// @if DEBUG for (j = i + 1; j < bodiesLength; j++) {
metrics.midphaseTests += 1; var bodyB = bodies[j],
// @endif boundsB = bodyB.bounds;
// mid phase if (boundsB.min.x > boundXMax) {
if (Bounds.overlaps(bodyA.bounds, bodyB.bounds)) { break;
for (var j = bodyA.parts.length > 1 ? 1 : 0; j < bodyA.parts.length; j++) { }
var partA = bodyA.parts[j];
for (var k = bodyB.parts.length > 1 ? 1 : 0; k < bodyB.parts.length; k++) { if (boundYMax < boundsB.min.y || boundYMin > boundsB.max.y) {
var partB = bodyB.parts[k]; continue;
}
if ((partA === bodyA && partB === bodyB) || Bounds.overlaps(partA.bounds, partB.bounds)) { if (bodyAStatic && (bodyB.isStatic || bodyB.isSleeping)) {
// find a previous collision we could reuse continue;
var pairId = Pair.id(partA, partB), }
pair = pairsTable[pairId],
previousCollision;
if (pair && pair.isActive) { if (!canCollide(bodyA.collisionFilter, bodyB.collisionFilter)) {
previousCollision = pair.collision; continue;
} else { }
previousCollision = null;
var partsBLength = bodyB.parts.length;
if (partsASingle && partsBLength === 1) {
var collision = collides(bodyA, bodyB, pairs);
if (collision) {
collisions.push(collision);
}
} else {
var partsAStart = partsALength > 1 ? 1 : 0,
partsBStart = partsBLength > 1 ? 1 : 0;
for (var k = partsAStart; k < partsALength; k++) {
var partA = bodyA.parts[k],
boundsA = partA.bounds;
for (var z = partsBStart; z < partsBLength; z++) {
var partB = bodyB.parts[z],
boundsB = partB.bounds;
if (boundsA.min.x > boundsB.max.x || boundsA.max.x < boundsB.min.x
|| boundsA.max.y < boundsB.min.y || boundsA.min.y > boundsB.max.y) {
continue;
} }
// narrow phase var collision = collides(partA, partB, pairs);
var collision = SAT.collides(partA, partB, previousCollision, delta);
// @if DEBUG if (collision) {
metrics.narrowphaseTests += 1;
if (collision.reused)
metrics.narrowReuseCount += 1;
// @endif
if (collision.collided) {
collisions.push(collision); collisions.push(collision);
// @if DEBUG
metrics.narrowDetections += 1;
// @endif
} }
} }
} }
@ -105,4 +152,39 @@ var Bounds = require('../geometry/Bounds');
return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0; return (filterA.mask & filterB.category) !== 0 && (filterB.mask & filterA.category) !== 0;
}; };
/**
* The comparison function used in the broadphase algorithm.
* Returns the signed delta of the bodies bounds on the x-axis.
* @private
* @method _sortCompare
* @param {body} bodyA
* @param {body} bodyB
* @return {number} The signed delta used for sorting
*/
Detector._compareBoundsX = function(bodyA, bodyB) {
return bodyA.bounds.min.x - bodyB.bounds.min.x;
};
/*
*
* Properties Documentation
*
*/
/**
* The array of `Matter.Body` between which the detector finds collisions.
*
* _Note:_ The order of bodies in this array _is not fixed_ and will be continually managed by the detector.
* @property bodies
* @type body[]
* @default []
*/
/**
* Optional. A `Matter.Pairs` object from which previous collision objects may be reused. Intended for internal `Matter.Engine` usage.
* @property pairs
* @type {pairs|null}
* @default null
*/
})(); })();

View file

@ -1,7 +1,13 @@
/** /**
* This module has now been replaced by `Matter.Detector`.
*
* All usage should be migrated to `Matter.Detector` or another alternative.
* For back-compatibility purposes this module will remain for a short term and then later removed in a future release.
*
* The `Matter.Grid` module contains methods for creating and manipulating collision broadphase grid structures. * The `Matter.Grid` module contains methods for creating and manipulating collision broadphase grid structures.
* *
* @class Grid * @class Grid
* @deprecated
*/ */
var Grid = {}; var Grid = {};
@ -9,21 +15,20 @@ var Grid = {};
module.exports = Grid; module.exports = Grid;
var Pair = require('./Pair'); var Pair = require('./Pair');
var Detector = require('./Detector');
var Common = require('../core/Common'); var Common = require('../core/Common');
var deprecated = Common.deprecated;
(function() { (function() {
/** /**
* Creates a new grid. * Creates a new grid.
* @deprecated replaced by Matter.Detector
* @method create * @method create
* @param {} options * @param {} options
* @return {grid} A new grid * @return {grid} A new grid
*/ */
Grid.create = function(options) { Grid.create = function(options) {
var defaults = { var defaults = {
controller: Grid,
detector: Detector.collisions,
buckets: {}, buckets: {},
pairs: {}, pairs: {},
pairsList: [], pairsList: [],
@ -52,6 +57,7 @@ var Common = require('../core/Common');
/** /**
* Updates the grid. * Updates the grid.
* @deprecated replaced by Matter.Detector
* @method update * @method update
* @param {grid} grid * @param {grid} grid
* @param {body[]} bodies * @param {body[]} bodies
@ -66,20 +72,15 @@ var Common = require('../core/Common');
bucketId, bucketId,
gridChanged = false; gridChanged = false;
// @if DEBUG
var metrics = engine.metrics;
metrics.broadphaseTests = 0;
// @endif
for (i = 0; i < bodies.length; i++) { for (i = 0; i < bodies.length; i++) {
var body = bodies[i]; var body = bodies[i];
if (body.isSleeping && !forceUpdate) if (body.isSleeping && !forceUpdate)
continue; continue;
// don't update out of world bodies // temporary back compatibility bounds check
if (body.bounds.max.x < world.bounds.min.x || body.bounds.min.x > world.bounds.max.x if (world.bounds && (body.bounds.max.x < world.bounds.min.x || body.bounds.min.x > world.bounds.max.x
|| body.bounds.max.y < world.bounds.min.y || body.bounds.min.y > world.bounds.max.y) || body.bounds.max.y < world.bounds.min.y || body.bounds.min.y > world.bounds.max.y))
continue; continue;
var newRegion = Grid._getRegion(grid, body); var newRegion = Grid._getRegion(grid, body);
@ -87,10 +88,6 @@ var Common = require('../core/Common');
// if the body has changed grid region // if the body has changed grid region
if (!body.region || newRegion.id !== body.region.id || forceUpdate) { if (!body.region || newRegion.id !== body.region.id || forceUpdate) {
// @if DEBUG
metrics.broadphaseTests += 1;
// @endif
if (!body.region || forceUpdate) if (!body.region || forceUpdate)
body.region = newRegion; body.region = newRegion;
@ -139,8 +136,11 @@ var Common = require('../core/Common');
grid.pairsList = Grid._createActivePairsList(grid); grid.pairsList = Grid._createActivePairsList(grid);
}; };
deprecated(Grid, 'update', 'Grid.update ➤ replaced by Matter.Detector');
/** /**
* Clears the grid. * Clears the grid.
* @deprecated replaced by Matter.Detector
* @method clear * @method clear
* @param {grid} grid * @param {grid} grid
*/ */
@ -150,9 +150,12 @@ var Common = require('../core/Common');
grid.pairsList = []; grid.pairsList = [];
}; };
deprecated(Grid, 'clear', 'Grid.clear ➤ replaced by Matter.Detector');
/** /**
* Finds the union of two regions. * Finds the union of two regions.
* @method _regionUnion * @method _regionUnion
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} regionA * @param {} regionA
* @param {} regionB * @param {} regionB
@ -170,6 +173,7 @@ var Common = require('../core/Common');
/** /**
* Gets the region a given body falls in for a given grid. * Gets the region a given body falls in for a given grid.
* @method _getRegion * @method _getRegion
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} grid * @param {} grid
* @param {} body * @param {} body
@ -188,6 +192,7 @@ var Common = require('../core/Common');
/** /**
* Creates a region. * Creates a region.
* @method _createRegion * @method _createRegion
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} startCol * @param {} startCol
* @param {} endCol * @param {} endCol
@ -208,6 +213,7 @@ var Common = require('../core/Common');
/** /**
* Gets the bucket id at the given position. * Gets the bucket id at the given position.
* @method _getBucketId * @method _getBucketId
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} column * @param {} column
* @param {} row * @param {} row
@ -220,6 +226,7 @@ var Common = require('../core/Common');
/** /**
* Creates a bucket. * Creates a bucket.
* @method _createBucket * @method _createBucket
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} buckets * @param {} buckets
* @param {} bucketId * @param {} bucketId
@ -233,14 +240,20 @@ var Common = require('../core/Common');
/** /**
* Adds a body to a bucket. * Adds a body to a bucket.
* @method _bucketAddBody * @method _bucketAddBody
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} grid * @param {} grid
* @param {} bucket * @param {} bucket
* @param {} body * @param {} body
*/ */
Grid._bucketAddBody = function(grid, bucket, body) { Grid._bucketAddBody = function(grid, bucket, body) {
var gridPairs = grid.pairs,
pairId = Pair.id,
bucketLength = bucket.length,
i;
// add new pairs // add new pairs
for (var i = 0; i < bucket.length; i++) { for (i = 0; i < bucketLength; i++) {
var bodyB = bucket[i]; var bodyB = bucket[i];
if (body.id === bodyB.id || (body.isStatic && bodyB.isStatic)) if (body.id === bodyB.id || (body.isStatic && bodyB.isStatic))
@ -248,13 +261,13 @@ var Common = require('../core/Common');
// keep track of the number of buckets the pair exists in // keep track of the number of buckets the pair exists in
// important for Grid.update to work // important for Grid.update to work
var pairId = Pair.id(body, bodyB), var id = pairId(body, bodyB),
pair = grid.pairs[pairId]; pair = gridPairs[id];
if (pair) { if (pair) {
pair[2] += 1; pair[2] += 1;
} else { } else {
grid.pairs[pairId] = [body, bodyB, 1]; gridPairs[id] = [body, bodyB, 1];
} }
} }
@ -265,22 +278,27 @@ var Common = require('../core/Common');
/** /**
* Removes a body from a bucket. * Removes a body from a bucket.
* @method _bucketRemoveBody * @method _bucketRemoveBody
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} grid * @param {} grid
* @param {} bucket * @param {} bucket
* @param {} body * @param {} body
*/ */
Grid._bucketRemoveBody = function(grid, bucket, body) { Grid._bucketRemoveBody = function(grid, bucket, body) {
var gridPairs = grid.pairs,
pairId = Pair.id,
i;
// remove from bucket // remove from bucket
bucket.splice(Common.indexOf(bucket, body), 1); bucket.splice(Common.indexOf(bucket, body), 1);
var bucketLength = bucket.length;
// update pair counts // update pair counts
for (var i = 0; i < bucket.length; i++) { for (i = 0; i < bucketLength; i++) {
// keep track of the number of buckets the pair exists in // keep track of the number of buckets the pair exists in
// important for _createActivePairsList to work // important for _createActivePairsList to work
var bodyB = bucket[i], var pair = gridPairs[pairId(body, bucket[i])];
pairId = Pair.id(body, bodyB),
pair = grid.pairs[pairId];
if (pair) if (pair)
pair[2] -= 1; pair[2] -= 1;
@ -290,28 +308,29 @@ var Common = require('../core/Common');
/** /**
* Generates a list of the active pairs in the grid. * Generates a list of the active pairs in the grid.
* @method _createActivePairsList * @method _createActivePairsList
* @deprecated replaced by Matter.Detector
* @private * @private
* @param {} grid * @param {} grid
* @return [] pairs * @return [] pairs
*/ */
Grid._createActivePairsList = function(grid) { Grid._createActivePairsList = function(grid) {
var pairKeys, var pair,
pair, gridPairs = grid.pairs,
pairs = []; pairKeys = Common.keys(gridPairs),
pairKeysLength = pairKeys.length,
// grid.pairs is used as a hashmap pairs = [],
pairKeys = Common.keys(grid.pairs); k;
// iterate over grid.pairs // iterate over grid.pairs
for (var k = 0; k < pairKeys.length; k++) { for (k = 0; k < pairKeysLength; k++) {
pair = grid.pairs[pairKeys[k]]; pair = gridPairs[pairKeys[k]];
// if pair exists in at least one bucket // if pair exists in at least one bucket
// it is a pair that needs further collision testing so push it // it is a pair that needs further collision testing so push it
if (pair[2] > 0) { if (pair[2] > 0) {
pairs.push(pair); pairs.push(pair);
} else { } else {
delete grid.pairs[pairKeys[k]]; delete gridPairs[pairKeys[k]];
} }
} }

View file

@ -21,15 +21,14 @@ var Contact = require('./Contact');
*/ */
Pair.create = function(collision, timestamp) { Pair.create = function(collision, timestamp) {
var bodyA = collision.bodyA, var bodyA = collision.bodyA,
bodyB = collision.bodyB, bodyB = collision.bodyB;
parentA = collision.parentA,
parentB = collision.parentB;
var pair = { var pair = {
id: Pair.id(bodyA, bodyB), id: Pair.id(bodyA, bodyB),
bodyA: bodyA, bodyA: bodyA,
bodyB: bodyB, bodyB: bodyB,
contacts: {}, collision: collision,
contacts: [],
activeContacts: [], activeContacts: [],
separation: 0, separation: 0,
isActive: true, isActive: true,
@ -37,11 +36,11 @@ var Contact = require('./Contact');
isSensor: bodyA.isSensor || bodyB.isSensor, isSensor: bodyA.isSensor || bodyB.isSensor,
timeCreated: timestamp, timeCreated: timestamp,
timeUpdated: timestamp, timeUpdated: timestamp,
inverseMass: parentA.inverseMass + parentB.inverseMass, inverseMass: 0,
friction: Math.min(parentA.friction, parentB.friction), friction: 0,
frictionStatic: Math.max(parentA.frictionStatic, parentB.frictionStatic), frictionStatic: 0,
restitution: Math.max(parentA.restitution, parentB.restitution), restitution: 0,
slop: Math.max(parentA.slop, parentB.slop) slop: 0
}; };
Pair.update(pair, collision, timestamp); Pair.update(pair, collision, timestamp);
@ -61,34 +60,32 @@ var Contact = require('./Contact');
supports = collision.supports, supports = collision.supports,
activeContacts = pair.activeContacts, activeContacts = pair.activeContacts,
parentA = collision.parentA, parentA = collision.parentA,
parentB = collision.parentB; parentB = collision.parentB,
parentAVerticesLength = parentA.vertices.length;
pair.isActive = true;
pair.timeUpdated = timestamp;
pair.collision = collision; pair.collision = collision;
pair.separation = collision.depth;
pair.inverseMass = parentA.inverseMass + parentB.inverseMass; pair.inverseMass = parentA.inverseMass + parentB.inverseMass;
pair.friction = Math.min(parentA.friction, parentB.friction); pair.friction = parentA.friction < parentB.friction ? parentA.friction : parentB.friction;
pair.frictionStatic = Math.max(parentA.frictionStatic, parentB.frictionStatic); pair.frictionStatic = parentA.frictionStatic > parentB.frictionStatic ? parentA.frictionStatic : parentB.frictionStatic;
pair.restitution = Math.max(parentA.restitution, parentB.restitution); pair.restitution = parentA.restitution > parentB.restitution ? parentA.restitution : parentB.restitution;
pair.slop = Math.max(parentA.slop, parentB.slop); pair.slop = parentA.slop > parentB.slop ? parentA.slop : parentB.slop;
collision.pair = pair;
activeContacts.length = 0; activeContacts.length = 0;
if (collision.collided) { for (var i = 0; i < supports.length; i++) {
for (var i = 0; i < supports.length; i++) { var support = supports[i],
var support = supports[i], contactId = support.body === parentA ? support.index : parentAVerticesLength + support.index,
contactId = Contact.id(support), contact = contacts[contactId];
contact = contacts[contactId];
if (contact) { if (contact) {
activeContacts.push(contact); activeContacts.push(contact);
} else { } else {
activeContacts.push(contacts[contactId] = Contact.create(support)); activeContacts.push(contacts[contactId] = Contact.create(support));
}
} }
pair.separation = collision.depth;
Pair.setActive(pair, true, timestamp);
} else {
if (pair.isActive === true)
Pair.setActive(pair, false, timestamp);
} }
}; };

View file

@ -12,8 +12,6 @@ var Pair = require('./Pair');
var Common = require('../core/Common'); var Common = require('../core/Common');
(function() { (function() {
Pairs._pairMaxIdleLife = 1000;
/** /**
* Creates a new pairs structure. * Creates a new pairs structure.
@ -40,12 +38,14 @@ var Common = require('../core/Common');
*/ */
Pairs.update = function(pairs, collisions, timestamp) { Pairs.update = function(pairs, collisions, timestamp) {
var pairsList = pairs.list, var pairsList = pairs.list,
pairsListLength = pairsList.length,
pairsTable = pairs.table, pairsTable = pairs.table,
collisionsLength = collisions.length,
collisionStart = pairs.collisionStart, collisionStart = pairs.collisionStart,
collisionEnd = pairs.collisionEnd, collisionEnd = pairs.collisionEnd,
collisionActive = pairs.collisionActive, collisionActive = pairs.collisionActive,
collision, collision,
pairId, pairIndex,
pair, pair,
i; i;
@ -54,90 +54,61 @@ var Common = require('../core/Common');
collisionEnd.length = 0; collisionEnd.length = 0;
collisionActive.length = 0; collisionActive.length = 0;
for (i = 0; i < pairsList.length; i++) { for (i = 0; i < pairsListLength; i++) {
pairsList[i].confirmedActive = false; pairsList[i].confirmedActive = false;
} }
for (i = 0; i < collisions.length; i++) { for (i = 0; i < collisionsLength; i++) {
collision = collisions[i]; collision = collisions[i];
pair = collision.pair;
if (collision.collided) { if (pair) {
pairId = Pair.id(collision.bodyA, collision.bodyB); // pair already exists (but may or may not be active)
if (pair.isActive) {
pair = pairsTable[pairId]; // pair exists and is active
collisionActive.push(pair);
if (pair) {
// pair already exists (but may or may not be active)
if (pair.isActive) {
// pair exists and is active
collisionActive.push(pair);
} else {
// pair exists but was inactive, so a collision has just started again
collisionStart.push(pair);
}
// update the pair
Pair.update(pair, collision, timestamp);
pair.confirmedActive = true;
} else { } else {
// pair did not exist, create a new pair // pair exists but was inactive, so a collision has just started again
pair = Pair.create(collision, timestamp);
pairsTable[pairId] = pair;
// push the new pair
collisionStart.push(pair); collisionStart.push(pair);
pairsList.push(pair); }
// update the pair
Pair.update(pair, collision, timestamp);
pair.confirmedActive = true;
} else {
// pair did not exist, create a new pair
pair = Pair.create(collision, timestamp);
pairsTable[pair.id] = pair;
// push the new pair
collisionStart.push(pair);
pairsList.push(pair);
}
}
// find pairs that are no longer active
var removePairIndex = [];
pairsListLength = pairsList.length;
for (i = 0; i < pairsListLength; i++) {
pair = pairsList[i];
if (!pair.confirmedActive) {
Pair.setActive(pair, false, timestamp);
collisionEnd.push(pair);
if (!pair.collision.bodyA.isSleeping && !pair.collision.bodyB.isSleeping) {
removePairIndex.push(i);
} }
} }
} }
// deactivate previously active pairs that are now inactive // remove inactive pairs
for (i = 0; i < pairsList.length; i++) { for (i = 0; i < removePairIndex.length; i++) {
pair = pairsList[i]; pairIndex = removePairIndex[i] - i;
if (pair.isActive && !pair.confirmedActive) {
Pair.setActive(pair, false, timestamp);
collisionEnd.push(pair);
}
}
};
/**
* Finds and removes pairs that have been inactive for a set amount of time.
* @method removeOld
* @param {object} pairs
* @param {number} timestamp
*/
Pairs.removeOld = function(pairs, timestamp) {
var pairsList = pairs.list,
pairsTable = pairs.table,
indexesToRemove = [],
pair,
collision,
pairIndex,
i;
for (i = 0; i < pairsList.length; i++) {
pair = pairsList[i];
collision = pair.collision;
// never remove sleeping pairs
if (collision.bodyA.isSleeping || collision.bodyB.isSleeping) {
pair.timeUpdated = timestamp;
continue;
}
// if pair is inactive for too long, mark it to be removed
if (timestamp - pair.timeUpdated > Pairs._pairMaxIdleLife) {
indexesToRemove.push(i);
}
}
// remove marked pairs
for (i = 0; i < indexesToRemove.length; i++) {
pairIndex = indexesToRemove[i] - i;
pair = pairsList[pairIndex]; pair = pairsList[pairIndex];
delete pairsTable[pair.id];
pairsList.splice(pairIndex, 1); pairsList.splice(pairIndex, 1);
delete pairsTable[pair.id];
} }
}; };

View file

@ -11,7 +11,7 @@ var Query = {};
module.exports = Query; module.exports = Query;
var Vector = require('../geometry/Vector'); var Vector = require('../geometry/Vector');
var SAT = require('./SAT'); var Collision = require('./Collision');
var Bounds = require('../geometry/Bounds'); var Bounds = require('../geometry/Bounds');
var Bodies = require('../factory/Bodies'); var Bodies = require('../factory/Bodies');
var Vertices = require('../geometry/Vertices'); var Vertices = require('../geometry/Vertices');
@ -23,22 +23,28 @@ var Vertices = require('../geometry/Vertices');
* @method collides * @method collides
* @param {body} body * @param {body} body
* @param {body[]} bodies * @param {body[]} bodies
* @return {object[]} Collisions * @return {collision[]} Collisions
*/ */
Query.collides = function(body, bodies) { Query.collides = function(body, bodies) {
var collisions = []; var collisions = [],
bodiesLength = bodies.length,
bounds = body.bounds,
collides = Collision.collides,
overlaps = Bounds.overlaps;
for (var i = 0; i < bodies.length; i++) { for (var i = 0; i < bodiesLength; i++) {
var bodyA = bodies[i]; var bodyA = bodies[i],
partsALength = bodyA.parts.length,
partsAStart = partsALength === 1 ? 0 : 1;
if (Bounds.overlaps(bodyA.bounds, body.bounds)) { if (overlaps(bodyA.bounds, bounds)) {
for (var j = bodyA.parts.length === 1 ? 0 : 1; j < bodyA.parts.length; j++) { for (var j = partsAStart; j < partsALength; j++) {
var part = bodyA.parts[j]; var part = bodyA.parts[j];
if (Bounds.overlaps(part.bounds, body.bounds)) { if (overlaps(part.bounds, bounds)) {
var collision = SAT.collides(part, body); var collision = collides(part, body);
if (collision.collided) { if (collision) {
collisions.push(collision); collisions.push(collision);
break; break;
} }
@ -57,7 +63,7 @@ var Vertices = require('../geometry/Vertices');
* @param {vector} startPoint * @param {vector} startPoint
* @param {vector} endPoint * @param {vector} endPoint
* @param {number} [rayWidth] * @param {number} [rayWidth]
* @return {object[]} Collisions * @return {collision[]} Collisions
*/ */
Query.ray = function(bodies, startPoint, endPoint, rayWidth) { Query.ray = function(bodies, startPoint, endPoint, rayWidth) {
rayWidth = rayWidth || 1e-100; rayWidth = rayWidth || 1e-100;

View file

@ -29,10 +29,11 @@ var Bounds = require('../geometry/Bounds');
Resolver.preSolvePosition = function(pairs) { Resolver.preSolvePosition = function(pairs) {
var i, var i,
pair, pair,
activeCount; activeCount,
pairsLength = pairs.length;
// find total contacts on each body // find total contacts on each body
for (i = 0; i < pairs.length; i++) { for (i = 0; i < pairsLength; i++) {
pair = pairs[i]; pair = pairs[i];
if (!pair.isActive) if (!pair.isActive)
@ -57,18 +58,15 @@ var Bounds = require('../geometry/Bounds');
bodyA, bodyA,
bodyB, bodyB,
normal, normal,
bodyBtoA,
contactShare, contactShare,
positionImpulse, positionImpulse,
timeScale = delta / Common._timeUnit, timeScale = delta / Common._timeUnit,
damping = Common.clamp(Resolver._positionDampen * timeScale, 0, 1), damping = Common.clamp(Resolver._positionDampen * timeScale, 0, 1),
tempA = Vector._temp[0], positionDampen = Resolver._positionDampen,
tempB = Vector._temp[1], pairsLength = pairs.length;
tempC = Vector._temp[2],
tempD = Vector._temp[3];
// find impulses required to resolve penetration // find impulses required to resolve penetration
for (i = 0; i < pairs.length; i++) { for (i = 0; i < pairsLength; i++) {
pair = pairs[i]; pair = pairs[i];
if (!pair.isActive || pair.isSensor) if (!pair.isActive || pair.isSensor)
@ -80,14 +78,12 @@ var Bounds = require('../geometry/Bounds');
normal = collision.normal; normal = collision.normal;
// get current separation between body edges involved in collision // get current separation between body edges involved in collision
bodyBtoA = Vector.sub(Vector.add(bodyB.positionImpulse, bodyB.position, tempA), pair.separation =
Vector.add(bodyA.positionImpulse, normal.x * (bodyB.positionImpulse.x + collision.penetration.x - bodyA.positionImpulse.x)
Vector.sub(bodyB.position, collision.penetration, tempB), tempC), tempD); + normal.y * (bodyB.positionImpulse.y + collision.penetration.y - bodyA.positionImpulse.y);
pair.separation = Vector.dot(normal, bodyBtoA);
} }
for (i = 0; i < pairs.length; i++) { for (i = 0; i < pairsLength; i++) {
pair = pairs[i]; pair = pairs[i];
if (!pair.isActive || pair.isSensor) if (!pair.isActive || pair.isSensor)
@ -103,13 +99,13 @@ var Bounds = require('../geometry/Bounds');
positionImpulse *= 2; positionImpulse *= 2;
if (!(bodyA.isStatic || bodyA.isSleeping)) { if (!(bodyA.isStatic || bodyA.isSleeping)) {
contactShare = Resolver._positionDampen / bodyA.totalContacts; contactShare = positionDampen / bodyA.totalContacts;
bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare; bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare; bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare;
} }
if (!(bodyB.isStatic || bodyB.isSleeping)) { if (!(bodyB.isStatic || bodyB.isSleeping)) {
contactShare = Resolver._positionDampen / bodyB.totalContacts; contactShare = positionDampen / bodyB.totalContacts;
bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare; bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare;
bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare; bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare;
} }
@ -122,34 +118,43 @@ var Bounds = require('../geometry/Bounds');
* @param {body[]} bodies * @param {body[]} bodies
*/ */
Resolver.postSolvePosition = function(bodies) { Resolver.postSolvePosition = function(bodies) {
for (var i = 0; i < bodies.length; i++) { var positionWarming = Resolver._positionWarming,
var body = bodies[i]; bodiesLength = bodies.length,
verticesTranslate = Vertices.translate,
boundsUpdate = Bounds.update;
for (var i = 0; i < bodiesLength; i++) {
var body = bodies[i],
positionImpulse = body.positionImpulse,
positionImpulseX = positionImpulse.x,
positionImpulseY = positionImpulse.y,
velocity = body.velocity;
// reset contact count // reset contact count
body.totalContacts = 0; body.totalContacts = 0;
if (body.positionImpulse.x !== 0 || body.positionImpulse.y !== 0) { if (positionImpulseX !== 0 || positionImpulseY !== 0) {
// update body geometry // update body geometry
for (var j = 0; j < body.parts.length; j++) { for (var j = 0; j < body.parts.length; j++) {
var part = body.parts[j]; var part = body.parts[j];
Vertices.translate(part.vertices, body.positionImpulse); verticesTranslate(part.vertices, positionImpulse);
Bounds.update(part.bounds, part.vertices, body.velocity); boundsUpdate(part.bounds, part.vertices, velocity);
part.position.x += body.positionImpulse.x; part.position.x += positionImpulseX;
part.position.y += body.positionImpulse.y; part.position.y += positionImpulseY;
} }
// move the body without changing velocity // move the body without changing velocity
body.positionPrev.x += body.positionImpulse.x; body.positionPrev.x += positionImpulseX;
body.positionPrev.y += body.positionImpulse.y; body.positionPrev.y += positionImpulseY;
if (Vector.dot(body.positionImpulse, body.velocity) < 0) { if (positionImpulseX * velocity.x + positionImpulseY * velocity.y < 0) {
// reset cached impulse if the body has velocity along it // reset cached impulse if the body has velocity along it
body.positionImpulse.x = 0; positionImpulse.x = 0;
body.positionImpulse.y = 0; positionImpulse.y = 0;
} else { } else {
// warm the next iteration // warm the next iteration
body.positionImpulse.x *= Resolver._positionWarming; positionImpulse.x *= positionWarming;
body.positionImpulse.y *= Resolver._positionWarming; positionImpulse.y *= positionWarming;
} }
} }
} }
@ -161,61 +166,53 @@ var Bounds = require('../geometry/Bounds');
* @param {pair[]} pairs * @param {pair[]} pairs
*/ */
Resolver.preSolveVelocity = function(pairs) { Resolver.preSolveVelocity = function(pairs) {
var i, var pairsLength = pairs.length,
j, i,
pair, j;
contacts,
collision,
bodyA,
bodyB,
normal,
tangent,
contact,
contactVertex,
normalImpulse,
tangentImpulse,
offset,
impulse = Vector._temp[0],
tempA = Vector._temp[1];
for (i = 0; i < pairs.length; i++) { for (i = 0; i < pairsLength; i++) {
pair = pairs[i]; var pair = pairs[i];
if (!pair.isActive || pair.isSensor) if (!pair.isActive || pair.isSensor)
continue; continue;
contacts = pair.activeContacts; var contacts = pair.activeContacts,
collision = pair.collision; contactsLength = contacts.length,
bodyA = collision.parentA; collision = pair.collision,
bodyB = collision.parentB; bodyA = collision.parentA,
normal = collision.normal; bodyB = collision.parentB,
tangent = collision.tangent; normal = collision.normal,
tangent = collision.tangent;
// resolve each contact // resolve each contact
for (j = 0; j < contacts.length; j++) { for (j = 0; j < contactsLength; j++) {
contact = contacts[j]; var contact = contacts[j],
contactVertex = contact.vertex; contactVertex = contact.vertex,
normalImpulse = contact.normalImpulse; normalImpulse = contact.normalImpulse,
tangentImpulse = contact.tangentImpulse; tangentImpulse = contact.tangentImpulse;
if (normalImpulse !== 0 || tangentImpulse !== 0) { if (normalImpulse !== 0 || tangentImpulse !== 0) {
// total impulse from contact // total impulse from contact
impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse); var impulseX = normal.x * normalImpulse + tangent.x * tangentImpulse,
impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse); impulseY = normal.y * normalImpulse + tangent.y * tangentImpulse;
// apply impulse from contact // apply impulse from contact
if (!(bodyA.isStatic || bodyA.isSleeping)) { if (!(bodyA.isStatic || bodyA.isSleeping)) {
offset = Vector.sub(contactVertex, bodyA.position, tempA); bodyA.positionPrev.x += impulseX * bodyA.inverseMass;
bodyA.positionPrev.x += impulse.x * bodyA.inverseMass; bodyA.positionPrev.y += impulseY * bodyA.inverseMass;
bodyA.positionPrev.y += impulse.y * bodyA.inverseMass; bodyA.anglePrev += bodyA.inverseInertia * (
bodyA.anglePrev += Vector.cross(offset, impulse) * bodyA.inverseInertia; (contactVertex.x - bodyA.position.x) * impulseY
- (contactVertex.y - bodyA.position.y) * impulseX
);
} }
if (!(bodyB.isStatic || bodyB.isSleeping)) { if (!(bodyB.isStatic || bodyB.isSleeping)) {
offset = Vector.sub(contactVertex, bodyB.position, tempA); bodyB.positionPrev.x -= impulseX * bodyB.inverseMass;
bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass; bodyB.positionPrev.y -= impulseY * bodyB.inverseMass;
bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass; bodyB.anglePrev -= bodyB.inverseInertia * (
bodyB.anglePrev -= Vector.cross(offset, impulse) * bodyB.inverseInertia; (contactVertex.x - bodyB.position.x) * impulseY
- (contactVertex.y - bodyB.position.y) * impulseX
);
} }
} }
} }
@ -237,9 +234,19 @@ var Bounds = require('../geometry/Bounds');
tempB = Vector._temp[2], tempB = Vector._temp[2],
tempC = Vector._temp[3], tempC = Vector._temp[3],
tempD = Vector._temp[4], tempD = Vector._temp[4],
tempE = Vector._temp[5]; tempE = Vector._temp[5],
timeScaleSquared = timeScale * timeScale,
for (var i = 0; i < pairs.length; i++) { restingThresh = Resolver._restingThresh * timeScaleSquared,
frictionNormalMultiplier = Resolver._frictionNormalMultiplier,
restingThreshTangent = Resolver._restingThreshTangent * timeScaleSquared,
NumberMaxValue = Number.MAX_VALUE,
pairsLength = pairs.length,
tangentImpulse,
maxFriction,
i,
j;
for (i = 0; i < pairsLength; i++) {
var pair = pairs[i]; var pair = pairs[i];
if (!pair.isActive || pair.isSensor) if (!pair.isActive || pair.isSensor)
@ -248,56 +255,84 @@ var Bounds = require('../geometry/Bounds');
var collision = pair.collision, var collision = pair.collision,
bodyA = collision.parentA, bodyA = collision.parentA,
bodyB = collision.parentB, bodyB = collision.parentB,
normal = collision.normal, bodyAVelocity = bodyA.velocity,
tangent = collision.tangent, bodyBVelocity = bodyB.velocity,
normalX = collision.normal.x,
normalY = collision.normal.y,
tangentX = collision.tangent.x,
tangentY = collision.tangent.y,
contacts = pair.activeContacts, contacts = pair.activeContacts,
contactShare = 1 / contacts.length; contactsLength = contacts.length,
contactShare = 1 / contactsLength,
inverseMassTotal = bodyA.inverseMass + bodyB.inverseMass,
friction = pair.friction * pair.frictionStatic * frictionNormalMultiplier * timeScaleSquared;
// update body velocities // update body velocities
bodyA.velocity.x = bodyA.position.x - bodyA.positionPrev.x; bodyAVelocity.x = bodyA.position.x - bodyA.positionPrev.x;
bodyA.velocity.y = bodyA.position.y - bodyA.positionPrev.y; bodyAVelocity.y = bodyA.position.y - bodyA.positionPrev.y;
bodyB.velocity.x = bodyB.position.x - bodyB.positionPrev.x; bodyBVelocity.x = bodyB.position.x - bodyB.positionPrev.x;
bodyB.velocity.y = bodyB.position.y - bodyB.positionPrev.y; bodyBVelocity.y = bodyB.position.y - bodyB.positionPrev.y;
bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev; bodyA.angularVelocity = bodyA.angle - bodyA.anglePrev;
bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev; bodyB.angularVelocity = bodyB.angle - bodyB.anglePrev;
// resolve each contact // resolve each contact
for (var j = 0; j < contacts.length; j++) { for (j = 0; j < contactsLength; j++) {
var contact = contacts[j], var contact = contacts[j],
contactVertex = contact.vertex, contactVertex = contact.vertex;
offsetA = Vector.sub(contactVertex, bodyA.position, tempA),
offsetB = Vector.sub(contactVertex, bodyB.position, tempB),
velocityPointA = Vector.add(bodyA.velocity, Vector.mult(Vector.perp(offsetA), bodyA.angularVelocity), tempC),
velocityPointB = Vector.add(bodyB.velocity, Vector.mult(Vector.perp(offsetB), bodyB.angularVelocity), tempD),
relativeVelocity = Vector.sub(velocityPointA, velocityPointB, tempE),
normalVelocity = Vector.dot(normal, relativeVelocity);
var tangentVelocity = Vector.dot(tangent, relativeVelocity), var offsetAX = contactVertex.x - bodyA.position.x,
tangentSpeed = Math.abs(tangentVelocity), offsetAY = contactVertex.y - bodyA.position.y,
tangentVelocityDirection = Common.sign(tangentVelocity); offsetBX = contactVertex.x - bodyB.position.x,
offsetBY = contactVertex.y - bodyB.position.y;
var velocityPointAX = bodyAVelocity.x - offsetAY * bodyA.angularVelocity,
velocityPointAY = bodyAVelocity.y + offsetAX * bodyA.angularVelocity,
velocityPointBX = bodyBVelocity.x - offsetBY * bodyB.angularVelocity,
velocityPointBY = bodyBVelocity.y + offsetBX * bodyB.angularVelocity;
// raw impulses var relativeVelocityX = velocityPointAX - velocityPointBX,
var normalImpulse = (1 + pair.restitution) * normalVelocity, relativeVelocityY = velocityPointAY - velocityPointBY;
normalForce = Common.clamp(pair.separation + normalVelocity, 0, 1) * Resolver._frictionNormalMultiplier;
var normalVelocity = normalX * relativeVelocityX + normalY * relativeVelocityY,
tangentVelocity = tangentX * relativeVelocityX + tangentY * relativeVelocityY;
// coulomb friction // coulomb friction
var tangentImpulse = tangentVelocity, // var tangentImpulse = tangentVelocity,
maxFriction = Infinity; // maxFriction = Infinity;
if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScale3) { // if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScale3) {
maxFriction = tangentSpeed * timeScale; // maxFriction = tangentSpeed * timeScale;
tangentImpulse = Common.clamp( // tangentImpulse = Common.clamp(
pair.friction * tangentVelocityDirection * timeScale3, // pair.friction * tangentVelocityDirection * timeScale3,
-maxFriction, maxFriction // -maxFriction, maxFriction
); // );
var normalOverlap = pair.separation + normalVelocity;
var normalForce = Math.min(normalOverlap, 1) * timeScale3;
normalForce = normalOverlap < 0 ? 0 : normalForce;
var frictionLimit = normalForce * friction;
if (tangentVelocity > frictionLimit || -tangentVelocity > frictionLimit) {
maxFriction = (tangentVelocity > 0 ? tangentVelocity : -tangentVelocity) * timeScale;
tangentImpulse = pair.friction * (tangentVelocity > 0 ? 1 : -1) * timeScale3;
if (tangentImpulse < -maxFriction) {
tangentImpulse = -maxFriction;
} else if (tangentImpulse > maxFriction) {
tangentImpulse = maxFriction;
}
} else {
tangentImpulse = tangentVelocity;
maxFriction = NumberMaxValue;
} }
// modify impulses accounting for mass, inertia and offset // account for mass, inertia and contact offset
var oAcN = Vector.cross(offsetA, normal), var oAcN = offsetAX * normalY - offsetAY * normalX,
oBcN = Vector.cross(offsetB, normal), oBcN = offsetBX * normalY - offsetBY * normalX,
share = contactShare / (bodyA.inverseMass + bodyB.inverseMass + bodyA.inverseInertia * oAcN * oAcN + bodyB.inverseInertia * oBcN * oBcN); share = contactShare / (inverseMassTotal + bodyA.inverseInertia * oAcN * oAcN + bodyB.inverseInertia * oBcN * oBcN);
normalImpulse *= share; // raw impulses
var normalImpulse = (1 + pair.restitution) * normalVelocity * share;
tangentImpulse *= share; tangentImpulse *= share;
// handle high velocity and resting collisions separately // handle high velocity and resting collisions separately
@ -308,7 +343,8 @@ var Bounds = require('../geometry/Bounds');
// solve resting collision constraints using Erin Catto's method (GDC08) // solve resting collision constraints using Erin Catto's method (GDC08)
// impulse constraint tends to 0 // impulse constraint tends to 0
var contactNormalImpulse = contact.normalImpulse; var contactNormalImpulse = contact.normalImpulse;
contact.normalImpulse = Math.min(contact.normalImpulse + normalImpulse, 0); contact.normalImpulse += normalImpulse;
contact.normalImpulse = Math.min(contact.normalImpulse, 0);
normalImpulse = contact.normalImpulse - contactNormalImpulse; normalImpulse = contact.normalImpulse - contactNormalImpulse;
} }
@ -320,25 +356,27 @@ var Bounds = require('../geometry/Bounds');
// solve resting collision constraints using Erin Catto's method (GDC08) // solve resting collision constraints using Erin Catto's method (GDC08)
// tangent impulse tends to -tangentSpeed or +tangentSpeed // tangent impulse tends to -tangentSpeed or +tangentSpeed
var contactTangentImpulse = contact.tangentImpulse; var contactTangentImpulse = contact.tangentImpulse;
contact.tangentImpulse = Common.clamp(contact.tangentImpulse + tangentImpulse, -maxFriction, maxFriction); contact.tangentImpulse += tangentImpulse;
if (contact.tangentImpulse < -maxFriction) contact.tangentImpulse = -maxFriction;
if (contact.tangentImpulse > maxFriction) contact.tangentImpulse = maxFriction;
tangentImpulse = contact.tangentImpulse - contactTangentImpulse; tangentImpulse = contact.tangentImpulse - contactTangentImpulse;
} }
// total impulse from contact // total impulse from contact
impulse.x = (normal.x * normalImpulse) + (tangent.x * tangentImpulse); var impulseX = normalX * normalImpulse + tangentX * tangentImpulse,
impulse.y = (normal.y * normalImpulse) + (tangent.y * tangentImpulse); impulseY = normalY * normalImpulse + tangentY * tangentImpulse;
// apply impulse from contact // apply impulse from contact
if (!(bodyA.isStatic || bodyA.isSleeping)) { if (!(bodyA.isStatic || bodyA.isSleeping)) {
bodyA.positionPrev.x += impulse.x * bodyA.inverseMass; bodyA.positionPrev.x += impulseX * bodyA.inverseMass;
bodyA.positionPrev.y += impulse.y * bodyA.inverseMass; bodyA.positionPrev.y += impulseY * bodyA.inverseMass;
bodyA.anglePrev += Vector.cross(offsetA, impulse) * bodyA.inverseInertia; bodyA.anglePrev += (offsetAX * impulseY - offsetAY * impulseX) * bodyA.inverseInertia;
} }
if (!(bodyB.isStatic || bodyB.isSleeping)) { if (!(bodyB.isStatic || bodyB.isSleeping)) {
bodyB.positionPrev.x -= impulse.x * bodyB.inverseMass; bodyB.positionPrev.x -= impulseX * bodyB.inverseMass;
bodyB.positionPrev.y -= impulse.y * bodyB.inverseMass; bodyB.positionPrev.y -= impulseY * bodyB.inverseMass;
bodyB.anglePrev -= Vector.cross(offsetB, impulse) * bodyB.inverseInertia; bodyB.anglePrev -= (offsetBX * impulseY - offsetBY * impulseX) * bodyB.inverseInertia;
} }
} }
} }

View file

@ -1,18 +1,22 @@
/** /**
* This module has now been replaced by `Matter.Collision`.
*
* All usage should be migrated to `Matter.Collision`.
* For back-compatibility purposes this module will remain for a short term and then later removed in a future release.
*
* The `Matter.SAT` module contains methods for detecting collisions using the Separating Axis Theorem. * The `Matter.SAT` module contains methods for detecting collisions using the Separating Axis Theorem.
* *
* @class SAT * @class SAT
* @deprecated
*/ */
// TODO: true circles and curves
var SAT = {}; var SAT = {};
module.exports = SAT; module.exports = SAT;
var Vertices = require('../geometry/Vertices'); var Collision = require('./Collision');
var Vector = require('../geometry/Vector');
var Common = require('../core/Common'); var Common = require('../core/Common');
var deprecated = Common.deprecated;
(function() { (function() {
@ -20,258 +24,16 @@ var Common = require('../core/Common');
/** /**
* Detect collision between two bodies using the Separating Axis Theorem. * Detect collision between two bodies using the Separating Axis Theorem.
* @deprecated replaced by Collision.collides
* @method collides * @method collides
* @param {body} bodyA * @param {body} bodyA
* @param {body} bodyB * @param {body} bodyB
* @param {collision} previousCollision
* @param {number} [delta=0]
* @return {collision} collision * @return {collision} collision
*/ */
SAT.collides = function(bodyA, bodyB, previousCollision, delta) { SAT.collides = function(bodyA, bodyB) {
var overlapAB, return Collision.collides(bodyA, bodyB);
overlapBA,
minOverlap,
collision,
canReusePrevCol = false,
timeScale = delta / Common._timeUnit;
delta = typeof delta !== 'undefined' ? delta : 0;
if (previousCollision) {
// estimate total motion
var parentA = bodyA.parent,
parentB = bodyB.parent,
motion = parentA.speed * parentA.speed + parentA.angularSpeed * parentA.angularSpeed
+ parentB.speed * parentB.speed + parentB.angularSpeed * parentB.angularSpeed;
// we may be able to (partially) reuse collision result
// but only safe if collision was resting
canReusePrevCol = previousCollision && previousCollision.collided && motion < SAT._reuseMotionThresh * timeScale * timeScale;
// reuse collision object
collision = previousCollision;
} else {
collision = { collided: false, bodyA: bodyA, bodyB: bodyB };
}
if (previousCollision && canReusePrevCol) {
// if we can reuse the collision result
// we only need to test the previously found axis
var axisBodyA = collision.axisBody,
axisBodyB = axisBodyA === bodyA ? bodyB : bodyA,
axes = [axisBodyA.axes[previousCollision.axisNumber]];
minOverlap = SAT._overlapAxes(axisBodyA.vertices, axisBodyB.vertices, axes);
collision.reused = true;
if (minOverlap.overlap <= 0) {
collision.collided = false;
return collision;
}
} else {
// if we can't reuse a result, perform a full SAT test
overlapAB = SAT._overlapAxes(bodyA.vertices, bodyB.vertices, bodyA.axes);
if (overlapAB.overlap <= 0) {
collision.collided = false;
return collision;
}
overlapBA = SAT._overlapAxes(bodyB.vertices, bodyA.vertices, bodyB.axes);
if (overlapBA.overlap <= 0) {
collision.collided = false;
return collision;
}
if (overlapAB.overlap < overlapBA.overlap) {
minOverlap = overlapAB;
collision.axisBody = bodyA;
} else {
minOverlap = overlapBA;
collision.axisBody = bodyB;
}
// important for reuse later
collision.axisNumber = minOverlap.axisNumber;
}
collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
collision.collided = true;
collision.depth = minOverlap.overlap;
collision.parentA = collision.bodyA.parent;
collision.parentB = collision.bodyB.parent;
bodyA = collision.bodyA;
bodyB = collision.bodyB;
// ensure normal is facing away from bodyA
if (Vector.dot(minOverlap.axis, Vector.sub(bodyB.position, bodyA.position)) < 0) {
collision.normal = {
x: minOverlap.axis.x,
y: minOverlap.axis.y
};
} else {
collision.normal = {
x: -minOverlap.axis.x,
y: -minOverlap.axis.y
};
}
collision.tangent = Vector.perp(collision.normal);
collision.penetration = collision.penetration || {};
collision.penetration.x = collision.normal.x * collision.depth;
collision.penetration.y = collision.normal.y * collision.depth;
// find support points, there is always either exactly one or two
var verticesB = SAT._findSupports(bodyA, bodyB, collision.normal),
supports = [];
// find the supports from bodyB that are inside bodyA
if (Vertices.contains(bodyA.vertices, verticesB[0]))
supports.push(verticesB[0]);
if (Vertices.contains(bodyA.vertices, verticesB[1]))
supports.push(verticesB[1]);
// find the supports from bodyA that are inside bodyB
if (supports.length < 2) {
var verticesA = SAT._findSupports(bodyB, bodyA, Vector.neg(collision.normal));
if (Vertices.contains(bodyB.vertices, verticesA[0]))
supports.push(verticesA[0]);
if (supports.length < 2 && Vertices.contains(bodyB.vertices, verticesA[1]))
supports.push(verticesA[1]);
}
// account for the edge case of overlapping but no vertex containment
if (supports.length < 1)
supports = [verticesB[0]];
collision.supports = supports;
return collision;
}; };
/** deprecated(SAT, 'collides', 'SAT.collides ➤ replaced by Collision.collides');
* Find the overlap between two sets of vertices.
* @method _overlapAxes
* @private
* @param {} verticesA
* @param {} verticesB
* @param {} axes
* @return result
*/
SAT._overlapAxes = function(verticesA, verticesB, axes) {
var projectionA = Vector._temp[0],
projectionB = Vector._temp[1],
result = { overlap: Number.MAX_VALUE },
overlap,
axis;
for (var i = 0; i < axes.length; i++) {
axis = axes[i];
SAT._projectToAxis(projectionA, verticesA, axis);
SAT._projectToAxis(projectionB, verticesB, axis);
overlap = Math.min(projectionA.max - projectionB.min, projectionB.max - projectionA.min);
if (overlap <= 0) {
result.overlap = overlap;
return result;
}
if (overlap < result.overlap) {
result.overlap = overlap;
result.axis = axis;
result.axisNumber = i;
}
}
return result;
};
/**
* Projects vertices on an axis and returns an interval.
* @method _projectToAxis
* @private
* @param {} projection
* @param {} vertices
* @param {} axis
*/
SAT._projectToAxis = function(projection, vertices, axis) {
var min = Vector.dot(vertices[0], axis),
max = min;
for (var i = 1; i < vertices.length; i += 1) {
var dot = Vector.dot(vertices[i], axis);
if (dot > max) {
max = dot;
} else if (dot < min) {
min = dot;
}
}
projection.min = min;
projection.max = max;
};
/**
* Finds supporting vertices given two bodies along a given direction using hill-climbing.
* @method _findSupports
* @private
* @param {} bodyA
* @param {} bodyB
* @param {} normal
* @return [vector]
*/
SAT._findSupports = function(bodyA, bodyB, normal) {
var nearestDistance = Number.MAX_VALUE,
vertexToBody = Vector._temp[0],
vertices = bodyB.vertices,
bodyAPosition = bodyA.position,
distance,
vertex,
vertexA,
vertexB;
// find closest vertex on bodyB
for (var i = 0; i < vertices.length; i++) {
vertex = vertices[i];
vertexToBody.x = vertex.x - bodyAPosition.x;
vertexToBody.y = vertex.y - bodyAPosition.y;
distance = -Vector.dot(normal, vertexToBody);
if (distance < nearestDistance) {
nearestDistance = distance;
vertexA = vertex;
}
}
// find next closest vertex using the two connected to it
var prevIndex = vertexA.index - 1 >= 0 ? vertexA.index - 1 : vertices.length - 1;
vertex = vertices[prevIndex];
vertexToBody.x = vertex.x - bodyAPosition.x;
vertexToBody.y = vertex.y - bodyAPosition.y;
nearestDistance = -Vector.dot(normal, vertexToBody);
vertexB = vertex;
var nextIndex = (vertexA.index + 1) % vertices.length;
vertex = vertices[nextIndex];
vertexToBody.x = vertex.x - bodyAPosition.x;
vertexToBody.y = vertex.y - bodyAPosition.y;
distance = -Vector.dot(normal, vertexToBody);
if (distance < nearestDistance) {
vertexB = vertex;
}
return [vertexA, vertexB];
};
})(); })();

View file

@ -14,7 +14,9 @@ module.exports = Common;
Common._nextId = 0; Common._nextId = 0;
Common._seed = 0; Common._seed = 0;
Common._nowStartTime = +(new Date()); Common._nowStartTime = +(new Date());
Common._warnedOnce = {};
Common._decomp = null;
/** /**
* Extends the object in the first argument using the object in the second argument. * Extends the object in the first argument using the object in the second argument.
* @method extend * @method extend
@ -253,9 +255,9 @@ module.exports = Common;
/** /**
* Returns the current timestamp since the time origin (e.g. from page load). * Returns the current timestamp since the time origin (e.g. from page load).
* The result will be high-resolution including decimal places if available. * The result is in milliseconds and will use high-resolution timing if available.
* @method now * @method now
* @return {number} the current timestamp * @return {number} the current timestamp in milliseconds
*/ */
Common.now = function() { Common.now = function() {
if (typeof window !== 'undefined' && window.performance) { if (typeof window !== 'undefined' && window.performance) {
@ -266,6 +268,10 @@ module.exports = Common;
} }
} }
if (Date.now) {
return Date.now();
}
return (new Date()) - Common._nowStartTime; return (new Date()) - Common._nowStartTime;
}; };
@ -359,6 +365,35 @@ module.exports = Common;
} }
}; };
/**
* Uses `Common.warn` to log the given message one time only.
* @method warnOnce
* @param ...objs {} The objects to log.
*/
Common.warnOnce = function() {
var message = Array.prototype.slice.call(arguments).join(' ');
if (!Common._warnedOnce[message]) {
Common.warn(message);
Common._warnedOnce[message] = true;
}
};
/**
* Shows a deprecated console warning when the function on the given object is called.
* The target function will be replaced with a new function that first shows the warning
* and then calls the original function.
* @method deprecated
* @param {object} obj The object or module
* @param {string} name The property name of the function on obj
* @param {string} warning The one-time message to show if the function is called
*/
Common.deprecated = function(obj, prop, warning) {
obj[prop] = Common.chain(function() {
Common.warnOnce('🔅 deprecated 🔅', warning);
}, obj[prop]);
};
/** /**
* Returns the next unique sequential ID. * Returns the next unique sequential ID.
* @method nextId * @method nextId
@ -536,4 +571,42 @@ module.exports = Common;
func func
)); ));
}; };
/**
* Provide the [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module to enable
* concave vertex decomposition support when using `Bodies.fromVertices` e.g. `Common.setDecomp(require('poly-decomp'))`.
* @method setDecomp
* @param {} decomp The [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module.
*/
Common.setDecomp = function(decomp) {
Common._decomp = decomp;
};
/**
* Returns the [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module provided through `Common.setDecomp`,
* otherwise returns the global `decomp` if set.
* @method getDecomp
* @return {} The [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module if provided.
*/
Common.getDecomp = function() {
// get user provided decomp if set
var decomp = Common._decomp;
try {
// otherwise from window global
if (!decomp && typeof window !== 'undefined') {
decomp = window.decomp;
}
// otherwise from node global
if (!decomp && typeof global !== 'undefined') {
decomp = global.decomp;
}
} catch (e) {
// decomp not available
decomp = null;
}
return decomp;
};
})(); })();

View file

@ -12,13 +12,10 @@ var Engine = {};
module.exports = Engine; module.exports = Engine;
var World = require('../body/World');
var Sleeping = require('./Sleeping'); var Sleeping = require('./Sleeping');
var Resolver = require('../collision/Resolver'); var Resolver = require('../collision/Resolver');
var Render = require('../render/Render'); var Detector = require('../collision/Detector');
var Pairs = require('../collision/Pairs'); var Pairs = require('../collision/Pairs');
var Metrics = require('./Metrics');
var Grid = require('../collision/Grid');
var Events = require('./Events'); var Events = require('./Events');
var Composite = require('../body/Composite'); var Composite = require('../body/Composite');
var Constraint = require('../constraint/Constraint'); var Constraint = require('../constraint/Constraint');
@ -35,16 +32,9 @@ var Body = require('../body/Body');
* @param {object} [options] * @param {object} [options]
* @return {engine} engine * @return {engine} engine
*/ */
Engine.create = function(element, options) { Engine.create = function(options) {
// options may be passed as the first (and only) argument
options = Common.isElement(element) ? options : element;
element = Common.isElement(element) ? element : null;
options = options || {}; options = options || {};
if (element || options.render) {
Common.warn('Engine.create: engine.render is deprecated (see docs)');
}
var defaults = { var defaults = {
positionIterations: 6, positionIterations: 6,
velocityIterations: 4, velocityIterations: 4,
@ -52,46 +42,31 @@ var Body = require('../body/Body');
enableSleeping: false, enableSleeping: false,
events: [], events: [],
plugin: {}, plugin: {},
gravity: {
x: 0,
y: 1,
scale: 0.001
},
timing: { timing: {
timestamp: 0, timestamp: 0,
timeScale: 1 timeScale: 1,
}, lastDelta: 0,
broadphase: { lastElapsed: 0
controller: Grid
} }
}; };
var engine = Common.extend(defaults, options); var engine = Common.extend(defaults, options);
// @deprecated engine.world = options.world || Composite.create({ label: 'World' });
if (element || engine.render) { engine.pairs = options.pairs || Pairs.create();
var renderDefaults = { engine.detector = options.detector || Detector.create();
element: element,
controller: Render
};
engine.render = Common.extend(renderDefaults, engine.render);
}
// @deprecated
if (engine.render && engine.render.controller) {
engine.render = engine.render.controller.create(engine.render);
}
// @deprecated
if (engine.render) {
engine.render.engine = engine;
}
engine.world = options.world || World.create(engine.world);
engine.pairs = Pairs.create();
engine.broadphase = engine.broadphase.controller.create(engine.broadphase);
engine.metrics = engine.metrics || { extended: false };
// @if DEBUG
engine.metrics = Metrics.create(engine.metrics);
// @endif
// for temporary back compatibility only
engine.grid = { buckets: [] };
engine.world.gravity = engine.gravity;
engine.broadphase = engine.grid;
engine.metrics = {};
return engine; return engine;
}; };
@ -104,17 +79,21 @@ var Body = require('../body/Body');
* @param {number} [delta=16.666] * @param {number} [delta=16.666]
*/ */
Engine.update = function(engine, delta) { Engine.update = function(engine, delta) {
var startTime = Common.now();
var world = engine.world, var world = engine.world,
detector = engine.detector,
pairs = engine.pairs,
timing = engine.timing, timing = engine.timing,
broadphase = engine.broadphase, timestamp = timing.timestamp,
broadphasePairs,
i; i;
delta = typeof delta !== 'undefined' ? delta : Common._timeUnit; delta = typeof delta !== 'undefined' ? delta : Common._timeUnit;
delta *= timing.timeScale; delta *= timing.timeScale;
// increment timestamp // increment timestamp
timing.timestamp += delta; timing.timestamp += delta * timing.timeScale;
timing.lastDelta = delta * timing.timeScale;
// create an event object // create an event object
var event = { var event = {
@ -124,21 +103,26 @@ var Body = require('../body/Body');
Events.trigger(engine, 'beforeUpdate', event); Events.trigger(engine, 'beforeUpdate', event);
// get lists of all bodies and constraints, no matter what composites they are in // get all bodies and all constraints in the world
var allBodies = Composite.allBodies(world), var allBodies = Composite.allBodies(world),
allConstraints = Composite.allConstraints(world); allConstraints = Composite.allConstraints(world);
// @if DEBUG // update the detector bodies if they have changed
// reset metrics logging if (world.isModified) {
Metrics.reset(engine.metrics); Detector.setBodies(detector, allBodies);
// @endif }
// if sleeping enabled, call the sleeping controller // reset all composite modified flags
if (world.isModified) {
Composite.setModified(world, false, false, true);
}
// update sleeping if enabled
if (engine.enableSleeping) if (engine.enableSleeping)
Sleeping.update(allBodies, delta); Sleeping.update(allBodies, delta);
// applies gravity to all bodies // apply gravity to all bodies
Engine._bodiesApplyGravity(allBodies, world.gravity); Engine._bodiesApplyGravity(allBodies, engine.gravity);
// update all body position and rotation by integration // update all body position and rotation by integration
if (delta > 0) { if (delta > 0) {
@ -152,33 +136,12 @@ var Body = require('../body/Body');
} }
Constraint.postSolveAll(allBodies); Constraint.postSolveAll(allBodies);
// broadphase pass: find potential collision pairs // find all collisions
if (broadphase.controller) { detector.pairs = engine.pairs;
// if world is dirty, we must flush the whole grid var collisions = Detector.collisions(detector);
if (world.isModified)
broadphase.controller.clear(broadphase);
// update the grid buckets based on current bodies
broadphase.controller.update(broadphase, allBodies, engine, world.isModified);
broadphasePairs = broadphase.pairsList;
} else {
// if no broadphase set, we just pass all bodies
broadphasePairs = allBodies;
}
// clear all composite modified flags
if (world.isModified) {
Composite.setModified(world, false, false, true);
}
// narrowphase pass: find actual collisions, then create or update collision pairs
var collisions = broadphase.detector(broadphasePairs, engine, delta);
// update collision pairs // update collision pairs
var pairs = engine.pairs,
timestamp = timing.timestamp;
Pairs.update(pairs, collisions, timestamp); Pairs.update(pairs, collisions, timestamp);
Pairs.removeOld(pairs, timestamp);
// wake up bodies involved in collisions // wake up bodies involved in collisions
if (engine.enableSleeping) if (engine.enableSleeping)
@ -215,16 +178,14 @@ var Body = require('../body/Body');
if (pairs.collisionEnd.length > 0) if (pairs.collisionEnd.length > 0)
Events.trigger(engine, 'collisionEnd', { pairs: pairs.collisionEnd }); Events.trigger(engine, 'collisionEnd', { pairs: pairs.collisionEnd });
// @if DEBUG
// update metrics log
Metrics.update(engine.metrics, engine);
// @endif
// clear force buffers // clear force buffers
Engine._bodiesClearForces(allBodies); Engine._bodiesClearForces(allBodies);
Events.trigger(engine, 'afterUpdate', event); Events.trigger(engine, 'afterUpdate', event);
// log the time elapsed computing this update
engine.timing.lastElapsed = Common.now() - startTime;
return engine; return engine;
}; };
@ -253,21 +214,13 @@ var Body = require('../body/Body');
}; };
/** /**
* Clears the engine including the world, pairs and broadphase. * Clears the engine pairs and detector.
* @method clear * @method clear
* @param {engine} engine * @param {engine} engine
*/ */
Engine.clear = function(engine) { Engine.clear = function(engine) {
var world = engine.world;
Pairs.clear(engine.pairs); Pairs.clear(engine.pairs);
Detector.clear(engine.detector);
var broadphase = engine.broadphase;
if (broadphase.controller) {
var bodies = Composite.allBodies(world);
broadphase.controller.clear(broadphase);
broadphase.controller.update(broadphase, bodies, engine, true);
}
}; };
/** /**
@ -332,7 +285,8 @@ var Body = require('../body/Body');
}; };
/** /**
* An alias for `Runner.run`, see `Matter.Runner` for more information. * A deprecated alias for `Runner.run`, use `Matter.Runner.run(engine)` instead and see `Matter.Runner` for more information.
* @deprecated use Matter.Runner.run(engine) instead
* @method run * @method run
* @param {engine} engine * @param {engine} engine
*/ */
@ -341,58 +295,58 @@ var Body = require('../body/Body');
* Fired just before an update * Fired just before an update
* *
* @event beforeUpdate * @event beforeUpdate
* @param {} event An event object * @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update * @param {number} event.delta The delta time in milliseconds value used in the update
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update and all collision events * Fired after engine update and all collision events
* *
* @event afterUpdate * @event afterUpdate
* @param {} event An event object * @param {object} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update * @param {number} event.delta The delta time in milliseconds value used in the update
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update, provides a list of all pairs that have started to collide in the current tick (if any) * Fired after engine update, provides a list of all pairs that have started to collide in the current tick (if any)
* *
* @event collisionStart * @event collisionStart
* @param {} event An event object * @param {object} event An event object
* @param {} event.pairs List of affected pairs * @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update * @param {number} event.delta The delta time in milliseconds value used in the update
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update, provides a list of all pairs that are colliding in the current tick (if any) * Fired after engine update, provides a list of all pairs that are colliding in the current tick (if any)
* *
* @event collisionActive * @event collisionActive
* @param {} event An event object * @param {object} event An event object
* @param {} event.pairs List of affected pairs * @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update * @param {number} event.delta The delta time in milliseconds value used in the update
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/** /**
* Fired after engine update, provides a list of all pairs that have ended collision in the current tick (if any) * Fired after engine update, provides a list of all pairs that have ended collision in the current tick (if any)
* *
* @event collisionEnd * @event collisionEnd
* @param {} event An event object * @param {object} event An event object
* @param {} event.pairs List of affected pairs * @param {pair[]} event.pairs List of affected pairs
* @param {number} event.timestamp The engine.timing.timestamp of the event * @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {number} event.delta The delta time in milliseconds value used in the update * @param {number} event.delta The delta time in milliseconds value used in the update
* @param {} event.source The source object of the event * @param {engine} event.source The source object of the event
* @param {} event.name The name of the event * @param {string} event.name The name of the event
*/ */
/* /*
@ -466,32 +420,56 @@ var Body = require('../body/Body');
*/ */
/** /**
* An instance of a `Render` controller. The default value is a `Matter.Render` instance created by `Engine.create`. * A `Number` that represents the total execution time elapsed during the last `Engine.update` in milliseconds.
* One may also develop a custom renderer module based on `Matter.Render` and pass an instance of it to `Engine.create` via `options.render`. * It is updated by timing from the start of the last `Engine.update` call until it ends.
* *
* A minimal custom renderer object must define at least three functions: `create`, `clear` and `world` (see `Matter.Render`). * This value will also include the total execution time of all event handlers directly or indirectly triggered by the engine update.
* It is also possible to instead pass the _module_ reference via `options.render.controller` and `Engine.create` will instantiate one for you.
* *
* @property render * @property timing.lastElapsed
* @type render * @type number
* @deprecated see Demo.js for an example of creating a renderer * @default 0
* @default a Matter.Render instance
*/ */
/** /**
* An instance of a broadphase controller. The default value is a `Matter.Grid` instance created by `Engine.create`. * A `Number` that represents the `delta` value used in the last engine update.
* *
* @property timing.lastDelta
* @type number
* @default 0
*/
/**
* A `Matter.Detector` instance.
*
* @property detector
* @type detector
* @default a Matter.Detector instance
*/
/**
* A `Matter.Grid` instance.
*
* @deprecated replaced by `engine.detector`
* @property grid
* @type grid
* @default a Matter.Grid instance
*/
/**
* Replaced by and now alias for `engine.grid`.
*
* @deprecated replaced by `engine.detector`
* @property broadphase * @property broadphase
* @type grid * @type grid
* @default a Matter.Grid instance * @default a Matter.Grid instance
*/ */
/** /**
* A `World` composite object that will contain all simulated bodies and constraints. * The root `Matter.Composite` instance that will contain all bodies, constraints and other composites to be simulated by this engine.
* *
* @property world * @property world
* @type world * @type composite
* @default a Matter.World instance * @default a Matter.Composite instance
*/ */
/** /**
@ -501,4 +479,35 @@ var Body = require('../body/Body');
* @type {} * @type {}
*/ */
/**
* The gravity to apply on all bodies in `engine.world`.
*
* @property gravity
* @type object
*/
/**
* The gravity x component.
*
* @property gravity.x
* @type object
* @default 0
*/
/**
* The gravity y component.
*
* @property gravity.y
* @type object
* @default 1
*/
/**
* The gravity scale factor.
*
* @property gravity.scale
* @type object
* @default 0.001
*/
})(); })();

View file

@ -1,93 +0,0 @@
// @if DEBUG
/**
* _Internal Class_, not generally used outside of the engine's internals.
*
*/
var Metrics = {};
module.exports = Metrics;
var Composite = require('../body/Composite');
var Common = require('./Common');
(function() {
/**
* Creates a new metrics.
* @method create
* @private
* @return {metrics} A new metrics
*/
Metrics.create = function(options) {
var defaults = {
extended: false,
narrowDetections: 0,
narrowphaseTests: 0,
narrowReuse: 0,
narrowReuseCount: 0,
midphaseTests: 0,
broadphaseTests: 0,
narrowEff: 0.0001,
midEff: 0.0001,
broadEff: 0.0001,
collisions: 0,
buckets: 0,
bodies: 0,
pairs: 0
};
return Common.extend(defaults, false, options);
};
/**
* Resets metrics.
* @method reset
* @private
* @param {metrics} metrics
*/
Metrics.reset = function(metrics) {
if (metrics.extended) {
metrics.narrowDetections = 0;
metrics.narrowphaseTests = 0;
metrics.narrowReuse = 0;
metrics.narrowReuseCount = 0;
metrics.midphaseTests = 0;
metrics.broadphaseTests = 0;
metrics.narrowEff = 0;
metrics.midEff = 0;
metrics.broadEff = 0;
metrics.collisions = 0;
metrics.buckets = 0;
metrics.pairs = 0;
metrics.bodies = 0;
}
};
/**
* Updates metrics.
* @method update
* @private
* @param {metrics} metrics
* @param {engine} engine
*/
Metrics.update = function(metrics, engine) {
if (metrics.extended) {
var world = engine.world,
bodies = Composite.allBodies(world);
metrics.collisions = metrics.narrowDetections;
metrics.pairs = engine.pairs.list.length;
metrics.bodies = bodies.length;
metrics.midEff = (metrics.narrowDetections / (metrics.midphaseTests || 1)).toFixed(2);
metrics.narrowEff = (metrics.narrowDetections / (metrics.narrowphaseTests || 1)).toFixed(2);
metrics.broadEff = (1 - (metrics.broadphaseTests / (bodies.length || 1))).toFixed(2);
metrics.narrowReuse = (metrics.narrowReuseCount / (metrics.narrowphaseTests || 1)).toFixed(2);
//var broadphase = engine.broadphase[engine.broadphase.current];
//if (broadphase.instance)
// metrics.buckets = Common.keys(broadphase.instance.buckets).length;
}
};
})();
// @endif

View file

@ -240,7 +240,7 @@ var Common = require('./Common');
*/ */
Plugin.dependencyParse = function(dependency) { Plugin.dependencyParse = function(dependency) {
if (Common.isString(dependency)) { if (Common.isString(dependency)) {
var pattern = /^[\w-]+(@(\*|[\^~]?\d+\.\d+\.\d+(-[0-9A-Za-z-]+)?))?$/; var pattern = /^[\w-]+(@(\*|[\^~]?\d+\.\d+\.\d+(-[0-9A-Za-z-+]+)?))?$/;
if (!pattern.test(dependency)) { if (!pattern.test(dependency)) {
Common.warn('Plugin.dependencyParse:', dependency, 'is not a valid dependency string.'); Common.warn('Plugin.dependencyParse:', dependency, 'is not a valid dependency string.');
@ -275,7 +275,7 @@ var Common = require('./Common');
* @return {object} The version range parsed into its components. * @return {object} The version range parsed into its components.
*/ */
Plugin.versionParse = function(range) { Plugin.versionParse = function(range) {
var pattern = /^(\*)|(\^|~|>=|>)?\s*((\d+)\.(\d+)\.(\d+))(-[0-9A-Za-z-]+)?$/; var pattern = /^(\*)|(\^|~|>=|>)?\s*((\d+)\.(\d+)\.(\d+))(-[0-9A-Za-z-+]+)?$/;
if (!pattern.test(range)) { if (!pattern.test(range)) {
Common.warn('Plugin.versionParse:', range, 'is not a valid version or range.'); Common.warn('Plugin.versionParse:', range, 'is not a valid version or range.');

View file

@ -99,8 +99,7 @@ var Common = require('./Common');
/** /**
* A game loop utility that updates the engine and renderer by one step (a 'tick'). * A game loop utility that updates the engine and renderer by one step (a 'tick').
* Features delta smoothing and fixed or dynamic timing. * Features delta smoothing, time correction and fixed or dynamic timing.
* Triggers `beforeTick`, `tick` and `afterTick` events on the engine.
* Consider just `Engine.update(engine, delta)` if you're using your own loop. * Consider just `Engine.update(engine, delta)` if you're using your own loop.
* @method tick * @method tick
* @param {runner} runner * @param {runner} runner
@ -111,6 +110,13 @@ var Common = require('./Common');
var timing = engine.timing, var timing = engine.timing,
delta; delta;
// create an event object
var event = {
timestamp: timing.timestamp
};
Events.trigger(runner, 'beforeTick', event);
if (runner.isFixed) { if (runner.isFixed) {
// fixed timestep // fixed timestep
delta = runner.delta; delta = runner.delta;
@ -149,15 +155,6 @@ var Common = require('./Common');
} }
Events.trigger(runner, 'tick', event); Events.trigger(runner, 'tick', event);
Events.trigger(engine, 'tick', event); // @deprecated
// if world has been modified, clear the render scene graph
if (engine.world.isModified
&& engine.render
&& engine.render.controller
&& engine.render.controller.clear) {
engine.render.controller.clear(engine.render); // @deprecated
}
// update // update
Events.trigger(runner, 'beforeUpdate', event); Events.trigger(runner, 'beforeUpdate', event);
@ -171,20 +168,7 @@ var Common = require('./Common');
Events.trigger(runner, 'afterUpdate', event); Events.trigger(runner, 'afterUpdate', event);
// render
// @deprecated
if (engine.render && engine.render.controller) {
Events.trigger(runner, 'beforeRender', event);
Events.trigger(engine, 'beforeRender', event); // @deprecated
engine.render.controller.world(engine.render);
Events.trigger(runner, 'afterRender', event);
Events.trigger(engine, 'afterRender', event); // @deprecated
}
Events.trigger(runner, 'afterTick', event); Events.trigger(runner, 'afterTick', event);
Events.trigger(engine, 'afterTick', event); // @deprecated
}; };
/** /**
@ -263,28 +247,6 @@ var Common = require('./Common');
* @param {} event.name The name of the event * @param {} event.name The name of the event
*/ */
/**
* Fired before rendering
*
* @event beforeRender
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
* @deprecated
*/
/**
* Fired after rendering
*
* @event afterRender
* @param {} event An event object
* @param {number} event.timestamp The engine.timing.timestamp of the event
* @param {} event.source The source object of the event
* @param {} event.name The name of the event
* @deprecated
*/
/* /*
* *
* Properties Documentation * Properties Documentation

View file

@ -176,31 +176,46 @@ var Vector = require('../geometry/Vector');
}; };
/** /**
* Creates a body using the supplied vertices (or an array containing multiple sets of vertices). * Utility to create a compound body based on set(s) of vertices.
* If the vertices are convex, they will pass through as supplied. *
* Otherwise if the vertices are concave, they will be decomposed if [poly-decomp.js](https://github.com/schteppe/poly-decomp.js) is available. * _Note:_ To optionally enable automatic concave vertices decomposition the [poly-decomp](https://github.com/schteppe/poly-decomp.js)
* Note that this process is not guaranteed to support complex sets of vertices (e.g. those with holes may fail). * package must be first installed and provided see `Common.setDecomp`, otherwise the convex hull of each vertex set will be used.
* By default the decomposition will discard collinear edges (to improve performance). *
* It can also optionally discard any parts that have an area less than `minimumArea`. * The resulting vertices are reorientated about their centre of mass,
* If the vertices can not be decomposed, the result will fall back to using the convex hull. * and offset such that `body.position` corresponds to this point.
* The options parameter is an object that specifies any `Matter.Body` properties you wish to override the defaults. *
* The resulting offset may be found if needed by subtracting `body.bounds` from the original input bounds.
* To later move the centre of mass see `Body.setCentre`.
*
* Note that automatic conconcave decomposition results are not always optimal.
* For best results, simplify the input vertices as much as possible first.
* By default this function applies some addtional simplification to help.
*
* Some outputs may also require further manual processing afterwards to be robust.
* In particular some parts may need to be overlapped to avoid collision gaps.
* Thin parts and sharp points should be avoided or removed where possible.
*
* The options parameter object specifies any `Matter.Body` properties you wish to override the defaults.
*
* See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object. * See the properties section of the `Matter.Body` module for detailed information on what you can pass via the `options` object.
* @method fromVertices * @method fromVertices
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param [[vector]] vertexSets * @param {array} vertexSets One or more arrays of vertex points e.g. `[[{ x: 0, y: 0 }...], ...]`.
* @param {object} [options] * @param {object} [options] The body options.
* @param {bool} [flagInternal=false] * @param {bool} [flagInternal=false] Optionally marks internal edges with `isInternal`.
* @param {number} [removeCollinear=0.01] * @param {number} [removeCollinear=0.01] Threshold when simplifying vertices along the same edge.
* @param {number} [minimumArea=10] * @param {number} [minimumArea=10] Threshold when removing small parts.
* @param {number} [removeDuplicatePoints=0.01] Threshold when simplifying nearby vertices.
* @return {body} * @return {body}
*/ */
Bodies.fromVertices = function(x, y, vertexSets, options, flagInternal, removeCollinear, minimumArea) { Bodies.fromVertices = function(x, y, vertexSets, options, flagInternal, removeCollinear, minimumArea, removeDuplicatePoints) {
var globals = typeof global !== 'undefined' ? global : window, var decomp = Common.getDecomp(),
decomp, canDecomp,
body, body,
parts, parts,
isConvex, isConvex,
isConcave,
vertices, vertices,
i, i,
j, j,
@ -208,11 +223,8 @@ var Vector = require('../geometry/Vector');
v, v,
z; z;
try { // check decomp is as expected
decomp = globals.decomp || require('poly-decomp'); canDecomp = Boolean(decomp && decomp.quickDecomp);
} catch (e) {
// decomp is undefined
}
options = options || {}; options = options || {};
parts = []; parts = [];
@ -220,10 +232,7 @@ var Vector = require('../geometry/Vector');
flagInternal = typeof flagInternal !== 'undefined' ? flagInternal : false; flagInternal = typeof flagInternal !== 'undefined' ? flagInternal : false;
removeCollinear = typeof removeCollinear !== 'undefined' ? removeCollinear : 0.01; removeCollinear = typeof removeCollinear !== 'undefined' ? removeCollinear : 0.01;
minimumArea = typeof minimumArea !== 'undefined' ? minimumArea : 10; minimumArea = typeof minimumArea !== 'undefined' ? minimumArea : 10;
removeDuplicatePoints = typeof removeDuplicatePoints !== 'undefined' ? removeDuplicatePoints : 0.01;
if (!decomp) {
Common.warn('Bodies.fromVertices: poly-decomp.js required. Could not decompose vertices. Fallback to convex hull.');
}
// ensure vertexSets is an array of arrays // ensure vertexSets is an array of arrays
if (!Common.isArray(vertexSets[0])) { if (!Common.isArray(vertexSets[0])) {
@ -233,8 +242,15 @@ var Vector = require('../geometry/Vector');
for (v = 0; v < vertexSets.length; v += 1) { for (v = 0; v < vertexSets.length; v += 1) {
vertices = vertexSets[v]; vertices = vertexSets[v];
isConvex = Vertices.isConvex(vertices); isConvex = Vertices.isConvex(vertices);
isConcave = !isConvex;
if (isConvex || !decomp) { if (isConcave && !canDecomp) {
Common.warnOnce(
'Bodies.fromVertices: Install the \'poly-decomp\' library and use Common.setDecomp or provide \'decomp\' as a global to decompose concave vertices.'
);
}
if (isConvex || !canDecomp) {
if (isConvex) { if (isConvex) {
vertices = Vertices.clockwiseSort(vertices); vertices = Vertices.clockwiseSort(vertices);
} else { } else {
@ -256,6 +272,8 @@ var Vector = require('../geometry/Vector');
decomp.makeCCW(concave); decomp.makeCCW(concave);
if (removeCollinear !== false) if (removeCollinear !== false)
decomp.removeCollinearPoints(concave, removeCollinear); decomp.removeCollinearPoints(concave, removeCollinear);
if (removeDuplicatePoints !== false && decomp.removeDuplicatePoints)
decomp.removeDuplicatePoints(concave, removeDuplicatePoints);
// use the quick decomposition algorithm (Bayazit) // use the quick decomposition algorithm (Bayazit)
var decomposed = decomp.quickDecomp(concave); var decomposed = decomp.quickDecomp(concave);
@ -327,6 +345,8 @@ var Vector = require('../geometry/Vector');
if (parts.length > 1) { if (parts.length > 1) {
// create the parent body to be returned, that contains generated compound parts // create the parent body to be returned, that contains generated compound parts
body = Body.create(Common.extend({ parts: parts.slice(0) }, options)); body = Body.create(Common.extend({ parts: parts.slice(0) }, options));
// offset such that body.position is at the centre off mass
Body.setPosition(body, { x: x, y: y }); Body.setPosition(body, { x: x, y: y });
return body; return body;

View file

@ -16,6 +16,7 @@ var Constraint = require('../constraint/Constraint');
var Common = require('../core/Common'); var Common = require('../core/Common');
var Body = require('../body/Body'); var Body = require('../body/Body');
var Bodies = require('./Bodies'); var Bodies = require('./Bodies');
var deprecated = Common.deprecated;
(function() { (function() {
@ -202,7 +203,8 @@ var Bodies = require('./Bodies');
}; };
/** /**
* Creates a composite with a Newton's Cradle setup of bodies and constraints. * This has now moved to the [newtonsCradle example](https://github.com/liabru/matter-js/blob/master/examples/newtonsCradle.js), follow that instead as this function is deprecated here.
* @deprecated moved to newtonsCradle example
* @method newtonsCradle * @method newtonsCradle
* @param {number} xx * @param {number} xx
* @param {number} yy * @param {number} yy
@ -226,9 +228,12 @@ var Bodies = require('./Bodies');
return newtonsCradle; return newtonsCradle;
}; };
deprecated(Composites, 'newtonsCradle', 'Composites.newtonsCradle ➤ moved to newtonsCradle example');
/** /**
* Creates a composite with simple car setup of bodies and constraints. * This has now moved to the [car example](https://github.com/liabru/matter-js/blob/master/examples/car.js), follow that instead as this function is deprecated here.
* @deprecated moved to car example
* @method car * @method car
* @param {number} xx * @param {number} xx
* @param {number} yy * @param {number} yy
@ -294,8 +299,12 @@ var Bodies = require('./Bodies');
return car; return car;
}; };
deprecated(Composites, 'car', 'Composites.car ➤ moved to car example');
/** /**
* Creates a simple soft body like object. * This has now moved to the [softBody example](https://github.com/liabru/matter-js/blob/master/examples/softBody.js)
* and the [cloth example](https://github.com/liabru/matter-js/blob/master/examples/cloth.js), follow those instead as this function is deprecated here.
* @deprecated moved to softBody and cloth examples
* @method softBody * @method softBody
* @param {number} xx * @param {number} xx
* @param {number} yy * @param {number} yy
@ -324,4 +333,5 @@ var Bodies = require('./Bodies');
return softBody; return softBody;
}; };
deprecated(Composites, 'softBody', 'Composites.softBody ➤ moved to softBody and cloth examples');
})(); })();

View file

@ -169,17 +169,16 @@ var Common = require('../core/Common');
* @param {number} scalar * @param {number} scalar
*/ */
Vertices.translate = function(vertices, vector, scalar) { Vertices.translate = function(vertices, vector, scalar) {
var i; scalar = typeof scalar !== 'undefined' ? scalar : 1;
if (scalar) {
for (i = 0; i < vertices.length; i++) { var verticesLength = vertices.length,
vertices[i].x += vector.x * scalar; translateX = vector.x * scalar,
vertices[i].y += vector.y * scalar; translateY = vector.y * scalar,
} i;
} else {
for (i = 0; i < vertices.length; i++) { for (i = 0; i < verticesLength; i++) {
vertices[i].x += vector.x; vertices[i].x += translateX;
vertices[i].y += vector.y; vertices[i].y += translateY;
}
} }
return vertices; return vertices;
@ -197,15 +196,21 @@ var Common = require('../core/Common');
return; return;
var cos = Math.cos(angle), var cos = Math.cos(angle),
sin = Math.sin(angle); sin = Math.sin(angle),
pointX = point.x,
pointY = point.y,
verticesLength = vertices.length,
vertex,
dx,
dy,
i;
for (var i = 0; i < vertices.length; i++) { for (i = 0; i < verticesLength; i++) {
var vertice = vertices[i], vertex = vertices[i];
dx = vertice.x - point.x, dx = vertex.x - pointX;
dy = vertice.y - point.y; dy = vertex.y - pointY;
vertex.x = pointX + (dx * cos - dy * sin);
vertice.x = point.x + (dx * cos - dy * sin); vertex.y = pointY + (dx * sin + dy * cos);
vertice.y = point.y + (dx * sin + dy * cos);
} }
return vertices; return vertices;
@ -219,12 +224,21 @@ var Common = require('../core/Common');
* @return {boolean} True if the vertices contains point, otherwise false * @return {boolean} True if the vertices contains point, otherwise false
*/ */
Vertices.contains = function(vertices, point) { Vertices.contains = function(vertices, point) {
for (var i = 0; i < vertices.length; i++) { var pointX = point.x,
var vertice = vertices[i], pointY = point.y,
nextVertice = vertices[(i + 1) % vertices.length]; verticesLength = vertices.length,
if ((point.x - vertice.x) * (nextVertice.y - vertice.y) + (point.y - vertice.y) * (vertice.x - nextVertice.x) > 0) { vertex = vertices[verticesLength - 1],
nextVertex;
for (var i = 0; i < verticesLength; i++) {
nextVertex = vertices[i];
if ((pointX - vertex.x) * (nextVertex.y - vertex.y)
+ (pointY - vertex.y) * (vertex.x - nextVertex.x) > 0) {
return false; return false;
} }
vertex = nextVertex;
} }
return true; return true;

View file

@ -1,51 +1,35 @@
var Matter = module.exports = require('../core/Matter'); var Matter = module.exports = require('../core/Matter');
Matter.Axes = require('../geometry/Axes');
Matter.Bodies = require('../factory/Bodies');
Matter.Body = require('../body/Body'); Matter.Body = require('../body/Body');
Matter.Bounds = require('../geometry/Bounds');
Matter.Collision = require('../collision/Collision');
Matter.Common = require('../core/Common');
Matter.Composite = require('../body/Composite'); Matter.Composite = require('../body/Composite');
Matter.World = require('../body/World'); Matter.Composites = require('../factory/Composites');
Matter.Constraint = require('../constraint/Constraint');
Matter.Contact = require('../collision/Contact'); Matter.Contact = require('../collision/Contact');
Matter.Detector = require('../collision/Detector'); Matter.Detector = require('../collision/Detector');
Matter.Grid = require('../collision/Grid');
Matter.Pairs = require('../collision/Pairs');
Matter.Pair = require('../collision/Pair');
Matter.Query = require('../collision/Query');
Matter.Resolver = require('../collision/Resolver');
Matter.SAT = require('../collision/SAT');
Matter.Constraint = require('../constraint/Constraint');
Matter.MouseConstraint = require('../constraint/MouseConstraint');
Matter.Common = require('../core/Common');
Matter.Engine = require('../core/Engine'); Matter.Engine = require('../core/Engine');
Matter.Events = require('../core/Events'); Matter.Events = require('../core/Events');
Matter.Grid = require('../collision/Grid');
Matter.Mouse = require('../core/Mouse'); Matter.Mouse = require('../core/Mouse');
Matter.Runner = require('../core/Runner'); Matter.MouseConstraint = require('../constraint/MouseConstraint');
Matter.Sleeping = require('../core/Sleeping'); Matter.Pair = require('../collision/Pair');
Matter.Pairs = require('../collision/Pairs');
Matter.Plugin = require('../core/Plugin'); Matter.Plugin = require('../core/Plugin');
Matter.Query = require('../collision/Query');
// @if DEBUG Matter.Render = require('../render/Render');
Matter.Metrics = require('../core/Metrics'); Matter.Resolver = require('../collision/Resolver');
// @endif Matter.Runner = require('../core/Runner');
Matter.SAT = require('../collision/SAT');
Matter.Bodies = require('../factory/Bodies'); Matter.Sleeping = require('../core/Sleeping');
Matter.Composites = require('../factory/Composites');
Matter.Axes = require('../geometry/Axes');
Matter.Bounds = require('../geometry/Bounds');
Matter.Svg = require('../geometry/Svg'); Matter.Svg = require('../geometry/Svg');
Matter.Vector = require('../geometry/Vector'); Matter.Vector = require('../geometry/Vector');
Matter.Vertices = require('../geometry/Vertices'); Matter.Vertices = require('../geometry/Vertices');
Matter.World = require('../body/World');
Matter.Render = require('../render/Render'); // temporary back compatibility
Matter.RenderPixi = require('../render/RenderPixi');
// aliases
Matter.World.add = Matter.Composite.add;
Matter.World.remove = Matter.Composite.remove;
Matter.World.addComposite = Matter.Composite.addComposite;
Matter.World.addBody = Matter.Composite.addBody;
Matter.World.addConstraint = Matter.Composite.addConstraint;
Matter.World.clear = Matter.Composite.clear;
Matter.Engine.run = Matter.Runner.run; Matter.Engine.run = Matter.Runner.run;
Matter.Common.deprecated(Matter.Engine, 'run', 'Engine.run ➤ use Matter.Runner.run(engine) instead');

View file

@ -1,5 +1,5 @@
/** /**
* The `Matter.Render` module is a simple HTML5 canvas based renderer for visualising instances of `Matter.Engine`. * The `Matter.Render` module is a simple canvas based renderer for visualising instances of `Matter.Engine`.
* It is intended for development and debugging purposes, but may also be suitable for simple games. * It is intended for development and debugging purposes, but may also be suitable for simple games.
* It includes a number of drawing options including wireframe, vector with support for sprites and viewports. * It includes a number of drawing options including wireframe, vector with support for sprites and viewports.
* *
@ -14,7 +14,6 @@ var Common = require('../core/Common');
var Composite = require('../body/Composite'); var Composite = require('../body/Composite');
var Bounds = require('../geometry/Bounds'); var Bounds = require('../geometry/Bounds');
var Events = require('../core/Events'); var Events = require('../core/Events');
var Grid = require('../collision/Grid');
var Vector = require('../geometry/Vector'); var Vector = require('../geometry/Vector');
var Mouse = require('../core/Mouse'); var Mouse = require('../core/Mouse');
@ -32,6 +31,9 @@ var Mouse = require('../core/Mouse');
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame; || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame;
} }
Render._goodFps = 30;
Render._goodDelta = 1000 / 60;
/** /**
* Creates a new renderer. The options parameter is an object that specifies any properties you wish to override the defaults. * Creates a new renderer. The options parameter is an object that specifies any properties you wish to override the defaults.
* All properties have default values, and many are pre-calculated automatically based on other properties. * All properties have default values, and many are pre-calculated automatically based on other properties.
@ -48,6 +50,19 @@ var Mouse = require('../core/Mouse');
canvas: null, canvas: null,
mouse: null, mouse: null,
frameRequestId: null, frameRequestId: null,
timing: {
historySize: 60,
delta: 0,
deltaHistory: [],
lastTime: 0,
lastTimestamp: 0,
lastElapsed: 0,
timestampElapsed: 0,
timestampElapsedHistory: [],
engineDeltaHistory: [],
engineElapsedHistory: [],
elapsedHistory: []
},
options: { options: {
width: 800, width: 800,
height: 600, height: 600,
@ -59,7 +74,8 @@ var Mouse = require('../core/Mouse');
wireframes: true, wireframes: true,
showSleeping: true, showSleeping: true,
showDebug: false, showDebug: false,
showBroadphase: false, showStats: false,
showPerformance: false,
showBounds: false, showBounds: false,
showVelocity: false, showVelocity: false,
showCollisions: false, showCollisions: false,
@ -68,7 +84,6 @@ var Mouse = require('../core/Mouse');
showPositions: false, showPositions: false,
showAngleIndicator: false, showAngleIndicator: false,
showIds: false, showIds: false,
showShadows: false,
showVertexNumbers: false, showVertexNumbers: false,
showConvexHulls: false, showConvexHulls: false,
showInternalEdges: false, showInternalEdges: false,
@ -100,6 +115,9 @@ var Mouse = require('../core/Mouse');
} }
}; };
// for temporary back compatibility only
render.options.showBroadphase = false;
if (render.options.pixelRatio !== 1) { if (render.options.pixelRatio !== 1) {
Render.setPixelRatio(render, render.options.pixelRatio); Render.setPixelRatio(render, render.options.pixelRatio);
} }
@ -121,7 +139,18 @@ var Mouse = require('../core/Mouse');
Render.run = function(render) { Render.run = function(render) {
(function loop(time){ (function loop(time){
render.frameRequestId = _requestAnimationFrame(loop); render.frameRequestId = _requestAnimationFrame(loop);
Render.world(render);
_updateTiming(render, time);
Render.world(render, time);
if (render.options.showStats || render.options.showDebug) {
Render.stats(render, render.context, time);
}
if (render.options.showPerformance || render.options.showDebug) {
Render.performance(render, render.context, time);
}
})(); })();
}; };
@ -289,13 +318,16 @@ var Mouse = require('../core/Mouse');
* @method world * @method world
* @param {render} render * @param {render} render
*/ */
Render.world = function(render) { Render.world = function(render, time) {
var engine = render.engine, var startTime = Common.now(),
engine = render.engine,
world = engine.world, world = engine.world,
canvas = render.canvas, canvas = render.canvas,
context = render.context, context = render.context,
options = render.options, options = render.options,
allBodies = Composite.allBodies(world), timing = render.timing;
var allBodies = Composite.allBodies(world),
allConstraints = Composite.allConstraints(world), allConstraints = Composite.allConstraints(world),
background = options.wireframes ? options.wireframeBackground : options.background, background = options.wireframes ? options.wireframeBackground : options.background,
bodies = [], bodies = [],
@ -406,84 +438,188 @@ var Mouse = require('../core/Mouse');
Render.constraints(constraints, context); Render.constraints(constraints, context);
if (options.showBroadphase && engine.broadphase.controller === Grid)
Render.grid(render, engine.broadphase, context);
if (options.showDebug)
Render.debug(render, context);
if (options.hasBounds) { if (options.hasBounds) {
// revert view transforms // revert view transforms
Render.endViewTransform(render); Render.endViewTransform(render);
} }
Events.trigger(render, 'afterRender', event); Events.trigger(render, 'afterRender', event);
// log the time elapsed computing this update
timing.lastElapsed = Common.now() - startTime;
}; };
/** /**
* Description * Renders statistics about the engine and world useful for debugging.
* @private * @private
* @method debug * @method stats
* @param {render} render
* @param {RenderingContext} context
* @param {Number} time
*/
Render.stats = function(render, context, time) {
var engine = render.engine,
world = engine.world,
bodies = Composite.allBodies(world),
parts = 0,
width = 55,
height = 44,
x = 0,
y = 0;
// count parts
for (var i = 0; i < bodies.length; i += 1) {
parts += bodies[i].parts.length;
}
// sections
var sections = {
'Part': parts,
'Body': bodies.length,
'Cons': Composite.allConstraints(world).length,
'Comp': Composite.allComposites(world).length,
'Pair': engine.pairs.list.length
};
// background
context.fillStyle = '#0e0f19';
context.fillRect(x, y, width * 5.5, height);
context.font = '12px Arial';
context.textBaseline = 'top';
context.textAlign = 'right';
// sections
for (var key in sections) {
var section = sections[key];
// label
context.fillStyle = '#aaa';
context.fillText(key, x + width, y + 8);
// value
context.fillStyle = '#eee';
context.fillText(section, x + width, y + 26);
x += width;
}
};
/**
* Renders engine and render performance information.
* @private
* @method performance
* @param {render} render * @param {render} render
* @param {RenderingContext} context * @param {RenderingContext} context
*/ */
Render.debug = function(render, context) { Render.performance = function(render, context) {
var c = context, var engine = render.engine,
engine = render.engine, timing = render.timing,
world = engine.world, deltaHistory = timing.deltaHistory,
metrics = engine.metrics, elapsedHistory = timing.elapsedHistory,
options = render.options, timestampElapsedHistory = timing.timestampElapsedHistory,
bodies = Composite.allBodies(world), engineDeltaHistory = timing.engineDeltaHistory,
space = " "; engineElapsedHistory = timing.engineElapsedHistory,
lastEngineDelta = engine.timing.lastDelta;
var deltaMean = _mean(deltaHistory),
elapsedMean = _mean(elapsedHistory),
engineDeltaMean = _mean(engineDeltaHistory),
engineElapsedMean = _mean(engineElapsedHistory),
timestampElapsedMean = _mean(timestampElapsedHistory),
rateMean = (timestampElapsedMean / deltaMean) || 0,
fps = (1000 / deltaMean) || 0;
if (engine.timing.timestamp - (render.debugTimestamp || 0) >= 500) { var graphHeight = 4,
var text = ""; gap = 12,
width = 60,
height = 34,
x = 10,
y = 69;
if (metrics.timing) { // background
text += "fps: " + Math.round(metrics.timing.fps) + space; context.fillStyle = '#0e0f19';
} context.fillRect(0, 50, gap * 4 + width * 5 + 22, height);
// @if DEBUG // show FPS
if (metrics.extended) { Render.status(
if (metrics.timing) { context, x, y, width, graphHeight, deltaHistory.length,
text += "delta: " + metrics.timing.delta.toFixed(3) + space; Math.round(fps) + ' fps',
text += "correction: " + metrics.timing.correction.toFixed(3) + space; fps / Render._goodFps,
} function(i) { return (deltaHistory[i] / deltaMean) - 1; }
);
text += "bodies: " + bodies.length + space; // show engine delta
Render.status(
context, x + gap + width, y, width, graphHeight, engineDeltaHistory.length,
lastEngineDelta.toFixed(2) + ' dt',
Render._goodDelta / lastEngineDelta,
function(i) { return (engineDeltaHistory[i] / engineDeltaMean) - 1; }
);
if (engine.broadphase.controller === Grid) // show engine update time
text += "buckets: " + metrics.buckets + space; Render.status(
context, x + (gap + width) * 2, y, width, graphHeight, engineElapsedHistory.length,
engineElapsedMean.toFixed(2) + ' ut',
1 - (engineElapsedMean / Render._goodFps),
function(i) { return (engineElapsedHistory[i] / engineElapsedMean) - 1; }
);
text += "\n"; // show render time
Render.status(
context, x + (gap + width) * 3, y, width, graphHeight, elapsedHistory.length,
elapsedMean.toFixed(2) + ' rt',
1 - (elapsedMean / Render._goodFps),
function(i) { return (elapsedHistory[i] / elapsedMean) - 1; }
);
text += "collisions: " + metrics.collisions + space; // show effective speed
text += "pairs: " + engine.pairs.list.length + space; Render.status(
text += "broad: " + metrics.broadEff + space; context, x + (gap + width) * 4, y, width, graphHeight, timestampElapsedHistory.length,
text += "mid: " + metrics.midEff + space; rateMean.toFixed(2) + ' x',
text += "narrow: " + metrics.narrowEff + space; rateMean * rateMean * rateMean,
} function(i) { return (((timestampElapsedHistory[i] / deltaHistory[i]) / rateMean) || 0) - 1; }
// @endif );
};
render.debugString = text; /**
render.debugTimestamp = engine.timing.timestamp; * Renders a label, indicator and a chart.
* @private
* @method status
* @param {RenderingContext} context
* @param {number} x
* @param {number} y
* @param {number} width
* @param {number} height
* @param {number} count
* @param {string} label
* @param {string} indicator
* @param {function} plotY
*/
Render.status = function(context, x, y, width, height, count, label, indicator, plotY) {
// background
context.strokeStyle = '#888';
context.fillStyle = '#444';
context.lineWidth = 1;
context.fillRect(x, y + 7, width, 1);
// chart
context.beginPath();
context.moveTo(x, y + 7 - height * Common.clamp(0.4 * plotY(0), -2, 2));
for (var i = 0; i < width; i += 1) {
context.lineTo(x + i, y + 7 - (i < count ? height * Common.clamp(0.4 * plotY(i), -2, 2) : 0));
} }
context.stroke();
if (render.debugString) { // indicator
c.font = "12px Arial"; context.fillStyle = 'hsl(' + Common.clamp(25 + 95 * indicator, 0, 120) + ',100%,60%)';
context.fillRect(x, y - 7, 4, 4);
if (options.wireframes) { // label
c.fillStyle = 'rgba(255,255,255,0.5)'; context.font = '12px Arial';
} else { context.textBaseline = 'middle';
c.fillStyle = 'rgba(0,0,0,0.5)'; context.textAlign = 'right';
} context.fillStyle = '#eee';
context.fillText(label, x + width, y - 5);
var split = render.debugString.split('\n');
for (var i = 0; i < split.length; i++) {
c.fillText(split[i], 50, 50 + i * 18);
}
}
}; };
/** /**
@ -563,55 +699,6 @@ var Mouse = require('../core/Mouse');
} }
}; };
/**
* Description
* @private
* @method bodyShadows
* @param {render} render
* @param {body[]} bodies
* @param {RenderingContext} context
*/
Render.bodyShadows = function(render, bodies, context) {
var c = context,
engine = render.engine;
for (var i = 0; i < bodies.length; i++) {
var body = bodies[i];
if (!body.render.visible)
continue;
if (body.circleRadius) {
c.beginPath();
c.arc(body.position.x, body.position.y, body.circleRadius, 0, 2 * Math.PI);
c.closePath();
} else {
c.beginPath();
c.moveTo(body.vertices[0].x, body.vertices[0].y);
for (var j = 1; j < body.vertices.length; j++) {
c.lineTo(body.vertices[j].x, body.vertices[j].y);
}
c.closePath();
}
var distanceX = body.position.x - render.options.width * 0.5,
distanceY = body.position.y - render.options.height * 0.2,
distance = Math.abs(distanceX) + Math.abs(distanceY);
c.shadowColor = 'rgba(0,0,0,0.15)';
c.shadowOffsetX = 0.05 * distanceX;
c.shadowOffsetY = 0.05 * distanceY;
c.shadowBlur = 1 + 12 * Math.min(1, distance / 1000);
c.fill();
c.shadowColor = null;
c.shadowOffsetX = null;
c.shadowOffsetY = null;
c.shadowBlur = null;
}
};
/** /**
* Description * Description
* @private * @private
@ -1197,45 +1284,6 @@ var Mouse = require('../core/Mouse');
c.stroke(); c.stroke();
}; };
/**
* Description
* @private
* @method grid
* @param {render} render
* @param {grid} grid
* @param {RenderingContext} context
*/
Render.grid = function(render, grid, context) {
var c = context,
options = render.options;
if (options.wireframes) {
c.strokeStyle = 'rgba(255,180,0,0.1)';
} else {
c.strokeStyle = 'rgba(255,180,0,0.5)';
}
c.beginPath();
var bucketKeys = Common.keys(grid.buckets);
for (var i = 0; i < bucketKeys.length; i++) {
var bucketId = bucketKeys[i];
if (grid.buckets[bucketId].length < 2)
continue;
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);
}
c.lineWidth = 1;
c.stroke();
};
/** /**
* Description * Description
* @private * @private
@ -1322,7 +1370,56 @@ var Mouse = require('../core/Mouse');
}; };
/** /**
* Description * Updates render timing.
* @method _updateTiming
* @private
* @param {render} render
* @param {number} time
*/
var _updateTiming = function(render, time) {
var engine = render.engine,
timing = render.timing,
historySize = timing.historySize,
timestamp = engine.timing.timestamp;
timing.delta = time - timing.lastTime || Render._goodDelta;
timing.lastTime = time;
timing.timestampElapsed = timestamp - timing.lastTimestamp || 0;
timing.lastTimestamp = timestamp;
timing.deltaHistory.unshift(timing.delta);
timing.deltaHistory.length = Math.min(timing.deltaHistory.length, historySize);
timing.engineDeltaHistory.unshift(engine.timing.lastDelta);
timing.engineDeltaHistory.length = Math.min(timing.engineDeltaHistory.length, historySize);
timing.timestampElapsedHistory.unshift(timing.timestampElapsed);
timing.timestampElapsedHistory.length = Math.min(timing.timestampElapsedHistory.length, historySize);
timing.engineElapsedHistory.unshift(engine.timing.lastElapsed);
timing.engineElapsedHistory.length = Math.min(timing.engineElapsedHistory.length, historySize);
timing.elapsedHistory.unshift(timing.lastElapsed);
timing.elapsedHistory.length = Math.min(timing.elapsedHistory.length, historySize);
};
/**
* Returns the mean value of the given numbers.
* @method _mean
* @private
* @param {Number[]} values
* @return {Number} the mean of given values
*/
var _mean = function(values) {
var result = 0;
for (var i = 0; i < values.length; i += 1) {
result += values[i];
}
return (result / values.length) || 0;
};
/**
* @method _createCanvas * @method _createCanvas
* @private * @private
* @param {} width * @param {} width
@ -1455,37 +1552,6 @@ var Mouse = require('../core/Mouse');
* @default null * @default null
*/ */
/**
* The configuration options of the renderer.
*
* @property options
* @type {}
*/
/**
* The target width in pixels of the `render.canvas` to be created.
*
* @property options.width
* @type number
* @default 800
*/
/**
* The target height in pixels of the `render.canvas` to be created.
*
* @property options.height
* @type number
* @default 600
*/
/**
* A flag that specifies if `render.bounds` should be used when rendering.
*
* @property options.hasBounds
* @type boolean
* @default false
*/
/** /**
* A `Bounds` object that specifies the drawing view region. * A `Bounds` object that specifies the drawing view region.
* Rendering will be automatically transformed and scaled to fit within the canvas size (`render.options.width` and `render.options.height`). * Rendering will be automatically transformed and scaled to fit within the canvas size (`render.options.width` and `render.options.height`).
@ -1510,4 +1576,255 @@ var Mouse = require('../core/Mouse');
* @type {} * @type {}
*/ */
/**
* The mouse to render if `render.options.showMousePosition` is enabled.
*
* @property mouse
* @type mouse
* @default null
*/
/**
* The configuration options of the renderer.
*
* @property options
* @type {}
*/
/**
* The target width in pixels of the `render.canvas` to be created.
* See also the `options.pixelRatio` property to change render quality.
*
* @property options.width
* @type number
* @default 800
*/
/**
* The target height in pixels of the `render.canvas` to be created.
* See also the `options.pixelRatio` property to change render quality.
*
* @property options.height
* @type number
* @default 600
*/
/**
* The [pixel ratio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) to use when rendering.
*
* @property options.pixelRatio
* @type number
* @default 1
*/
/**
* A CSS background color string to use when `render.options.wireframes` is disabled.
* This may be also set to `'transparent'` or equivalent.
*
* @property options.background
* @type string
* @default '#14151f'
*/
/**
* A CSS background color string to use when `render.options.wireframes` is enabled.
* This may be also set to `'transparent'` or equivalent.
*
* @property options.wireframeBackground
* @type string
* @default '#14151f'
*/
/**
* A flag that specifies if `render.bounds` should be used when rendering.
*
* @property options.hasBounds
* @type boolean
* @default false
*/
/**
* A flag to enable or disable all debug information overlays together.
* This includes and has priority over the values of:
*
* - `render.options.showStats`
* - `render.options.showPerformance`
*
* @property options.showDebug
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the engine stats info overlay.
* From left to right, the values shown are:
*
* - body parts total
* - body total
* - constraints total
* - composites total
* - collision pairs total
*
* @property options.showStats
* @type boolean
* @default false
*/
/**
* A flag to enable or disable performance charts.
* From left to right, the values shown are:
*
* - average render frequency (e.g. 60 fps)
* - exact engine delta time used for last update (e.g. 16.66ms)
* - average engine execution duration (e.g. 5.00ms)
* - average render execution duration (e.g. 0.40ms)
* - average effective play speed (e.g. '1.00x' is 'real-time')
*
* Each value is recorded over a fixed sample of past frames (60 frames).
*
* A chart shown below each value indicates the variance from the average over the sample.
* The more stable or fixed the value is the flatter the chart will appear.
*
* @property options.showPerformance
* @type boolean
* @default false
*/
/**
* A flag to enable or disable rendering entirely.
*
* @property options.enabled
* @type boolean
* @default false
*/
/**
* A flag to toggle wireframe rendering otherwise solid fill rendering is used.
*
* @property options.wireframes
* @type boolean
* @default true
*/
/**
* A flag to enable or disable sleeping bodies indicators.
*
* @property options.showSleeping
* @type boolean
* @default true
*/
/**
* A flag to enable or disable the debug information overlay.
*
* @property options.showDebug
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the collision broadphase debug overlay.
*
* @deprecated no longer implemented
* @property options.showBroadphase
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body bounds debug overlay.
*
* @property options.showBounds
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body velocity debug overlay.
*
* @property options.showVelocity
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body collisions debug overlay.
*
* @property options.showCollisions
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the collision resolver separations debug overlay.
*
* @property options.showSeparations
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body axes debug overlay.
*
* @property options.showAxes
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body positions debug overlay.
*
* @property options.showPositions
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body angle debug overlay.
*
* @property options.showAngleIndicator
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body and part ids debug overlay.
*
* @property options.showIds
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body vertex numbers debug overlay.
*
* @property options.showVertexNumbers
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body convex hulls debug overlay.
*
* @property options.showConvexHulls
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the body internal edges debug overlay.
*
* @property options.showInternalEdges
* @type boolean
* @default false
*/
/**
* A flag to enable or disable the mouse position debug overlay.
*
* @property options.showMousePosition
* @type boolean
* @default false
*/
})(); })();

View file

@ -1,515 +0,0 @@
/**
* The `Matter.RenderPixi` module is an example renderer using pixi.js.
* See also `Matter.Render` for a canvas based renderer.
*
* @class RenderPixi
* @deprecated the Matter.RenderPixi module will soon be removed from the Matter.js core.
* It will likely be moved to its own repository (but maintenance will be limited).
*/
var RenderPixi = {};
module.exports = RenderPixi;
var Bounds = require('../geometry/Bounds');
var Composite = require('../body/Composite');
var Common = require('../core/Common');
var Events = require('../core/Events');
var Vector = require('../geometry/Vector');
(function() {
var _requestAnimationFrame,
_cancelAnimationFrame;
if (typeof window !== 'undefined') {
_requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame || window.msRequestAnimationFrame
|| function(callback){ window.setTimeout(function() { callback(Common.now()); }, 1000 / 60); };
_cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame
|| window.webkitCancelAnimationFrame || window.msCancelAnimationFrame;
}
/**
* Creates a new Pixi.js WebGL renderer
* @method create
* @param {object} options
* @return {RenderPixi} A new renderer
* @deprecated
*/
RenderPixi.create = function(options) {
Common.warn('RenderPixi.create: Matter.RenderPixi is deprecated (see docs)');
var defaults = {
controller: RenderPixi,
engine: null,
element: null,
frameRequestId: null,
canvas: null,
renderer: null,
container: null,
spriteContainer: null,
pixiOptions: null,
options: {
width: 800,
height: 600,
background: '#fafafa',
wireframeBackground: '#222',
hasBounds: false,
enabled: true,
wireframes: true,
showSleeping: true,
showDebug: false,
showBroadphase: false,
showBounds: false,
showVelocity: false,
showCollisions: false,
showAxes: false,
showPositions: false,
showAngleIndicator: false,
showIds: false,
showShadows: false
}
};
var render = Common.extend(defaults, options),
transparent = !render.options.wireframes && render.options.background === 'transparent';
// init pixi
render.pixiOptions = render.pixiOptions || {
view: render.canvas,
transparent: transparent,
antialias: true,
backgroundColor: options.background
};
render.mouse = options.mouse;
render.engine = options.engine;
render.renderer = render.renderer || new PIXI.WebGLRenderer(render.options.width, render.options.height, render.pixiOptions);
render.container = render.container || new PIXI.Container();
render.spriteContainer = render.spriteContainer || new PIXI.Container();
render.canvas = render.canvas || render.renderer.view;
render.bounds = render.bounds || {
min: {
x: 0,
y: 0
},
max: {
x: render.options.width,
y: render.options.height
}
};
// event listeners
Events.on(render.engine, 'beforeUpdate', function() {
RenderPixi.clear(render);
});
// caches
render.textures = {};
render.sprites = {};
render.primitives = {};
// use a sprite batch for performance
render.container.addChild(render.spriteContainer);
// insert canvas
if (Common.isElement(render.element)) {
render.element.appendChild(render.canvas);
} else {
Common.warn('No "render.element" passed, "render.canvas" was not inserted into document.');
}
// prevent menus on canvas
render.canvas.oncontextmenu = function() { return false; };
render.canvas.onselectstart = function() { return false; };
return render;
};
/**
* Continuously updates the render canvas on the `requestAnimationFrame` event.
* @method run
* @param {render} render
* @deprecated
*/
RenderPixi.run = function(render) {
(function loop(time){
render.frameRequestId = _requestAnimationFrame(loop);
RenderPixi.world(render);
})();
};
/**
* Ends execution of `Render.run` on the given `render`, by canceling the animation frame request event loop.
* @method stop
* @param {render} render
* @deprecated
*/
RenderPixi.stop = function(render) {
_cancelAnimationFrame(render.frameRequestId);
};
/**
* Clears the scene graph
* @method clear
* @param {RenderPixi} render
* @deprecated
*/
RenderPixi.clear = function(render) {
var container = render.container,
spriteContainer = render.spriteContainer;
// clear stage container
while (container.children[0]) {
container.removeChild(container.children[0]);
}
// clear sprite batch
while (spriteContainer.children[0]) {
spriteContainer.removeChild(spriteContainer.children[0]);
}
var bgSprite = render.sprites['bg-0'];
// clear caches
render.textures = {};
render.sprites = {};
render.primitives = {};
// set background sprite
render.sprites['bg-0'] = bgSprite;
if (bgSprite)
container.addChildAt(bgSprite, 0);
// add sprite batch back into container
render.container.addChild(render.spriteContainer);
// reset background state
render.currentBackground = null;
// reset bounds transforms
container.scale.set(1, 1);
container.position.set(0, 0);
};
/**
* Sets the background of the canvas
* @method setBackground
* @param {RenderPixi} render
* @param {string} background
* @deprecated
*/
RenderPixi.setBackground = function(render, background) {
if (render.currentBackground !== background) {
var isColor = background.indexOf && background.indexOf('#') !== -1,
bgSprite = render.sprites['bg-0'];
if (isColor) {
// if solid background color
var color = Common.colorToNumber(background);
render.renderer.backgroundColor = color;
// remove background sprite if existing
if (bgSprite)
render.container.removeChild(bgSprite);
} else {
// initialise background sprite if needed
if (!bgSprite) {
var texture = _getTexture(render, background);
bgSprite = render.sprites['bg-0'] = new PIXI.Sprite(texture);
bgSprite.position.x = 0;
bgSprite.position.y = 0;
render.container.addChildAt(bgSprite, 0);
}
}
render.currentBackground = background;
}
};
/**
* Description
* @method world
* @param {engine} engine
* @deprecated
*/
RenderPixi.world = function(render) {
var engine = render.engine,
world = engine.world,
renderer = render.renderer,
container = render.container,
options = render.options,
bodies = Composite.allBodies(world),
allConstraints = Composite.allConstraints(world),
constraints = [],
i;
if (options.wireframes) {
RenderPixi.setBackground(render, options.wireframeBackground);
} else {
RenderPixi.setBackground(render, options.background);
}
// handle bounds
var boundsWidth = render.bounds.max.x - render.bounds.min.x,
boundsHeight = render.bounds.max.y - render.bounds.min.y,
boundsScaleX = boundsWidth / render.options.width,
boundsScaleY = boundsHeight / render.options.height;
if (options.hasBounds) {
// Hide bodies that are not in view
for (i = 0; i < bodies.length; i++) {
var body = bodies[i];
body.render.sprite.visible = Bounds.overlaps(body.bounds, render.bounds);
}
// filter out constraints that are not in view
for (i = 0; i < allConstraints.length; i++) {
var constraint = allConstraints[i],
bodyA = constraint.bodyA,
bodyB = constraint.bodyB,
pointAWorld = constraint.pointA,
pointBWorld = constraint.pointB;
if (bodyA) pointAWorld = Vector.add(bodyA.position, constraint.pointA);
if (bodyB) pointBWorld = Vector.add(bodyB.position, constraint.pointB);
if (!pointAWorld || !pointBWorld)
continue;
if (Bounds.contains(render.bounds, pointAWorld) || Bounds.contains(render.bounds, pointBWorld))
constraints.push(constraint);
}
// transform the view
container.scale.set(1 / boundsScaleX, 1 / boundsScaleY);
container.position.set(-render.bounds.min.x * (1 / boundsScaleX), -render.bounds.min.y * (1 / boundsScaleY));
} else {
constraints = allConstraints;
}
for (i = 0; i < bodies.length; i++)
RenderPixi.body(render, bodies[i]);
for (i = 0; i < constraints.length; i++)
RenderPixi.constraint(render, constraints[i]);
renderer.render(container);
};
/**
* Description
* @method constraint
* @param {engine} engine
* @param {constraint} constraint
* @deprecated
*/
RenderPixi.constraint = function(render, constraint) {
var engine = render.engine,
bodyA = constraint.bodyA,
bodyB = constraint.bodyB,
pointA = constraint.pointA,
pointB = constraint.pointB,
container = render.container,
constraintRender = constraint.render,
primitiveId = 'c-' + constraint.id,
primitive = render.primitives[primitiveId];
// initialise constraint primitive if not existing
if (!primitive)
primitive = render.primitives[primitiveId] = new PIXI.Graphics();
// don't render if constraint does not have two end points
if (!constraintRender.visible || !constraint.pointA || !constraint.pointB) {
primitive.clear();
return;
}
// add to scene graph if not already there
if (Common.indexOf(container.children, primitive) === -1)
container.addChild(primitive);
// render the constraint on every update, since they can change dynamically
primitive.clear();
primitive.beginFill(0, 0);
primitive.lineStyle(constraintRender.lineWidth, Common.colorToNumber(constraintRender.strokeStyle), 1);
if (bodyA) {
primitive.moveTo(bodyA.position.x + pointA.x, bodyA.position.y + pointA.y);
} else {
primitive.moveTo(pointA.x, pointA.y);
}
if (bodyB) {
primitive.lineTo(bodyB.position.x + pointB.x, bodyB.position.y + pointB.y);
} else {
primitive.lineTo(pointB.x, pointB.y);
}
primitive.endFill();
};
/**
* Description
* @method body
* @param {engine} engine
* @param {body} body
* @deprecated
*/
RenderPixi.body = function(render, body) {
var engine = render.engine,
bodyRender = body.render;
if (!bodyRender.visible)
return;
if (bodyRender.sprite && bodyRender.sprite.texture) {
var spriteId = 'b-' + body.id,
sprite = render.sprites[spriteId],
spriteContainer = render.spriteContainer;
// initialise body sprite if not existing
if (!sprite)
sprite = render.sprites[spriteId] = _createBodySprite(render, body);
// add to scene graph if not already there
if (Common.indexOf(spriteContainer.children, sprite) === -1)
spriteContainer.addChild(sprite);
// update body sprite
sprite.position.x = body.position.x;
sprite.position.y = body.position.y;
sprite.rotation = body.angle;
sprite.scale.x = bodyRender.sprite.xScale || 1;
sprite.scale.y = bodyRender.sprite.yScale || 1;
} else {
var primitiveId = 'b-' + body.id,
primitive = render.primitives[primitiveId],
container = render.container;
// initialise body primitive if not existing
if (!primitive) {
primitive = render.primitives[primitiveId] = _createBodyPrimitive(render, body);
primitive.initialAngle = body.angle;
}
// add to scene graph if not already there
if (Common.indexOf(container.children, primitive) === -1)
container.addChild(primitive);
// update body primitive
primitive.position.x = body.position.x;
primitive.position.y = body.position.y;
primitive.rotation = body.angle - primitive.initialAngle;
}
};
/**
* Creates a body sprite
* @method _createBodySprite
* @private
* @param {RenderPixi} render
* @param {body} body
* @return {PIXI.Sprite} sprite
* @deprecated
*/
var _createBodySprite = function(render, body) {
var bodyRender = body.render,
texturePath = bodyRender.sprite.texture,
texture = _getTexture(render, texturePath),
sprite = new PIXI.Sprite(texture);
sprite.anchor.x = body.render.sprite.xOffset;
sprite.anchor.y = body.render.sprite.yOffset;
return sprite;
};
/**
* Creates a body primitive
* @method _createBodyPrimitive
* @private
* @param {RenderPixi} render
* @param {body} body
* @return {PIXI.Graphics} graphics
* @deprecated
*/
var _createBodyPrimitive = function(render, body) {
var bodyRender = body.render,
options = render.options,
primitive = new PIXI.Graphics(),
fillStyle = Common.colorToNumber(bodyRender.fillStyle),
strokeStyle = Common.colorToNumber(bodyRender.strokeStyle),
strokeStyleIndicator = Common.colorToNumber(bodyRender.strokeStyle),
strokeStyleWireframe = Common.colorToNumber('#bbb'),
strokeStyleWireframeIndicator = Common.colorToNumber('#CD5C5C'),
part;
primitive.clear();
// handle compound parts
for (var k = body.parts.length > 1 ? 1 : 0; k < body.parts.length; k++) {
part = body.parts[k];
if (!options.wireframes) {
primitive.beginFill(fillStyle, 1);
primitive.lineStyle(bodyRender.lineWidth, strokeStyle, 1);
} else {
primitive.beginFill(0, 0);
primitive.lineStyle(1, strokeStyleWireframe, 1);
}
primitive.moveTo(part.vertices[0].x - body.position.x, part.vertices[0].y - body.position.y);
for (var j = 1; j < part.vertices.length; j++) {
primitive.lineTo(part.vertices[j].x - body.position.x, part.vertices[j].y - body.position.y);
}
primitive.lineTo(part.vertices[0].x - body.position.x, part.vertices[0].y - body.position.y);
primitive.endFill();
// angle indicator
if (options.showAngleIndicator || options.showAxes) {
primitive.beginFill(0, 0);
if (options.wireframes) {
primitive.lineStyle(1, strokeStyleWireframeIndicator, 1);
} else {
primitive.lineStyle(1, strokeStyleIndicator);
}
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));
primitive.endFill();
}
}
return primitive;
};
/**
* Gets the requested texture (a PIXI.Texture) via its path
* @method _getTexture
* @private
* @param {RenderPixi} render
* @param {string} imagePath
* @return {PIXI.Texture} texture
* @deprecated
*/
var _getTexture = function(render, imagePath) {
var texture = render.textures[imagePath];
if (!texture)
texture = render.textures[imagePath] = PIXI.Texture.fromImage(imagePath);
return texture;
};
})();

Some files were not shown because too many files have changed in this diff Show more