This commit is contained in:
Richard Davey 2022-02-03 21:50:54 +00:00
commit 3546140b51
72 changed files with 93052 additions and 90179 deletions

View file

@ -70,6 +70,9 @@ Development of this feature was kindly sponsored by Club Penguin Rewritten (http
* `Math.LinearXY` is a new function that will interpolate between 2 given Vector2s and return a new Vector2 as a result (thanks @GregDevProjects) * `Math.LinearXY` is a new function that will interpolate between 2 given Vector2s and return a new Vector2 as a result (thanks @GregDevProjects)
* `Curves.Path.getCurveAt` is a new method that will return the curve that forms the path at the given location (thanks @natureofcode) * `Curves.Path.getCurveAt` is a new method that will return the curve that forms the path at the given location (thanks @natureofcode)
* You can now use any `Shape` Game Object as a Geometry Mask. Fix #5900 (thanks @rexrainbow) * You can now use any `Shape` Game Object as a Geometry Mask. Fix #5900 (thanks @rexrainbow)
* `Mesh.setTint` is a new method that will set the tint color across all vertices of a Mesh (thanks @rexrainbow)
* `Mesh.tint` is a new setter that will set the tint color across all vertices of a Mesh (thanks @rexrainbow)
* `Mesh.clearTint` is a new method that will clear the tint from all vertices of a Mesh (thanks @rexrainbow)
### Geom Updates ### Geom Updates
@ -117,6 +120,9 @@ The following are API-breaking, in that a new optional parameter has been insert
* `TileMap.createStaticLayer` has now been removed as it was deprecated in 3.50. * `TileMap.createStaticLayer` has now been removed as it was deprecated in 3.50.
* `Animations.AnimationManager.createFromAseprite` has a new optional 3rd parameter `target`. This allows you to create the animations directly on a Sprite, instead of in the global Animation Manager (thanks Telemako) * `Animations.AnimationManager.createFromAseprite` has a new optional 3rd parameter `target`. This allows you to create the animations directly on a Sprite, instead of in the global Animation Manager (thanks Telemako)
* `Animations.AnimationState.createFromAseprite` is a new method that allows you to create animations from exported Aseprite data directly on a Sprite, rather than always in the global Animation Manager (thanks Telemako) * `Animations.AnimationState.createFromAseprite` is a new method that allows you to create animations from exported Aseprite data directly on a Sprite, rather than always in the global Animation Manager (thanks Telemako)
* The `path` package used by the TS Defs generator has been moved to `devDependencies` (thanks @antkhnvsk)
* The `GetValue` function has a new optional parameter `altSource` which allows you to provide an alternative object to source the value from.
* The `Renderer.Snapshot.WebGL` function has had its first parameter changed from an `HTMLCanvasElement` to a `WebGLRenderingContext`. This is now passed in from the `snapshot` methods inside the WebGL Renderer. The change was made to allow it to work with WebGL2 custom contexts (thanks @andymikulski)
### Bug Fixes ### Bug Fixes
@ -165,9 +171,24 @@ The following are API-breaking, in that a new optional parameter has been insert
* `InputPlugin.disable` will now also reset the drag state of the Game Object as well as remove it from all of the internal temporary arrays. This fixes issues where if you disabled a Game Object for input during an input event it would still remain in the temporary internal arrays. This method now also returns the Input Plugin, to match `enable`. Fix #5828 (thank @natureofcode @thewaver) * `InputPlugin.disable` will now also reset the drag state of the Game Object as well as remove it from all of the internal temporary arrays. This fixes issues where if you disabled a Game Object for input during an input event it would still remain in the temporary internal arrays. This method now also returns the Input Plugin, to match `enable`. Fix #5828 (thank @natureofcode @thewaver)
* The `GetBounds` component has been removed from the Point Light Game Object. Fix #5934 (thanks @x-wk @samme) * The `GetBounds` component has been removed from the Point Light Game Object. Fix #5934 (thanks @x-wk @samme)
* `SceneManager.moveAbove` and `moveBelow` now take into account the modified indexes after the move (thanks @EmilSV) * `SceneManager.moveAbove` and `moveBelow` now take into account the modified indexes after the move (thanks @EmilSV)
* When forcing a game to use `setTimeout` and then sending the game to sleep, it would accidentally restart by using Request Animation Frame instead (thanks @andymikulski)
* Including a `render` object within the Game Config will no longer erase any top-level config render settings. The `render` object will now take priority over the game config, but both will be used (thanks @vzhou842)
* Calling `Tween.stop(0)` would run for an additional delta before stopping, causing the Tween to not be truly 100% "reset". Fix #5986 (thanks @Mesonyx)
* The `Utils.Array.SafeRange` function would exclude valid certain ranges. Fix #5979 (thanks @ksritharan)
* The "Skip intersects check by argument" change in Arcade Physics has been reverted. Fix #5956 (thanks @samme)
* The `Container.pointToContainer` method would ignore the provided `output` parameter, but now uses it (thanks @vforsh)
* The `Polygon` Game Object would ignore its `closePath` property when rendering in Canvas. Fix #5983 (thanks @optimumsuave)
* IE9 Fix: Added 2 missing Typed Array polyfills (thanks @jcyuan)
* IE9 Fix: CanvasRenderer ignores frames with zero dimensions (thanks @jcyuan)
* `RenderTexture.batchTextureFrame` will now skip the `drawImage` call in canvas if the frame width or height are zero. Fix #5951 (thanks @Hoshinokoe)
* `BlitterCanvasRenderer` will now skip the `drawImage` call in canvas if the frame width or height are zero.
* `ParticleManagerCanvasRenderer` will now skip the `drawImage` call in canvas if the frame width or height are zero.
* `CanvasSnapshot` will now skip the `drawImage` call in canvas if the frame width or height are zero.
* `TextureManager.getBase64` will now skip the `drawImage` call in canvas if the frame width or height are zero.
* `TilemapLayerCanvasRenderer` will now skip the `drawImage` call in canvas if the frame width or height are zero.
### Examples, Documentation and TypeScript ### Examples, Documentation and TypeScript
My thanks to the following for helping with the Phaser 3 Examples, Docs, and TypeScript definitions, either by reporting errors, fixing them, or helping author the docs: My thanks to the following for helping with the Phaser 3 Examples, Docs, and TypeScript definitions, either by reporting errors, fixing them, or helping author the docs:
@necrokot Golen @Pythux @samme @danfoster @eltociear @sylvainpolletvillard @hanzooo @etherealmachine @DeweyHur @twoco @austinlyon @Arcanorum OmniOwl @EsteFilipe @necrokot Golen @Pythux @samme @danfoster @eltociear @sylvainpolletvillard @hanzooo @etherealmachine @DeweyHur @twoco @austinlyon @Arcanorum OmniOwl @EsteFilipe @PhaserEditor2D @Fake

651
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -64,27 +64,28 @@
"web audio" "web audio"
], ],
"devDependencies": { "devDependencies": {
"@types/offscreencanvas": "^2019.6.4",
"@types/source-map": "^0.5.7", "@types/source-map": "^0.5.7",
"clean-webpack-plugin": "^4.0.0", "clean-webpack-plugin": "^4.0.0",
"dts-dom": "^3.6.0", "dts-dom": "^3.6.0",
"eslint": "^8.4.1", "eslint": "^8.8.0",
"eslint-plugin-es5": "^1.5.0", "eslint-plugin-es5": "^1.5.0",
"exports-loader": "^3.1.0", "exports-loader": "^3.1.0",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"imports-loader": "^3.1.1", "imports-loader": "^3.1.1",
"jsdoc": "^3.6.7", "jsdoc": "^3.6.10",
"node-sloc": "^0.2.1", "node-sloc": "^0.2.1",
"path": "^0.12.7",
"remove-files-webpack-plugin": "^1.5.0", "remove-files-webpack-plugin": "^1.5.0",
"source-map": "^0.7.3", "source-map": "^0.7.3",
"terser-webpack-plugin": "^5.2.5", "terser-webpack-plugin": "^5.3.1",
"typescript": "^4.5.4", "typescript": "^4.5.5",
"vivid-cli": "^1.1.2", "vivid-cli": "^1.1.2",
"webpack": "^5.65.0", "webpack": "^5.68.0",
"webpack-cli": "^4.9.1", "webpack-cli": "^4.9.2",
"webpack-shell-plugin": "^0.5.0" "webpack-shell-plugin": "^0.5.0"
}, },
"dependencies": { "dependencies": {
"eventemitter3": "^4.0.7", "eventemitter3": "^4.0.7"
"path": "^0.12.7"
} }
} }

View file

@ -2,10 +2,11 @@
1. Checkout the Esoteric Spine Runtimes repo to the `spine-runtimes` folder: https://github.com/EsotericSoftware/spine-runtimes/ and make sure this is in the `plugins/spine` folder, not the `plugins/spine/src` folder. 1. Checkout the Esoteric Spine Runtimes repo to the `spine-runtimes` folder: https://github.com/EsotericSoftware/spine-runtimes/ and make sure this is in the `plugins/spine` folder, not the `plugins/spine/src` folder.
2. Run `npm i` inside the `spine-runtimes` folder. 2. Run `npm i` inside the `spine-runtimes` folder.
3. Add the `source-map` module: `npm i --save-dev source-map`. 3. Add the `offscreencanvas` module: `npm i --save-dev @types/offscreencanvas`.
4. Run `npm run plugin.spine.runtimes` to build the new runtimes to the `plugins/spine/src/runtimes` folder. 4. Add the `source-map` module: `npm i --save-dev source-map`.
5. Run `npm run plugin.spine.runtimes` to build the new runtimes to the `plugins/spine/src/runtimes` folder.
You can now build a new version of the Spine Plugin: You can now build a new version of the Spine Plugin:
5. `npm run plugin.spine.dist`. 6. `npm run plugin.spine.dist`.

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,4 @@
/// <reference types="offscreencanvas" />
declare module spine { declare module spine {
class Animation { class Animation {
name: string; name: string;
@ -1402,7 +1403,7 @@ declare module spine.webgl {
static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL: boolean; static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL: boolean;
constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement | ImageBitmap, useMipMaps?: boolean); constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement | ImageBitmap, useMipMaps?: boolean);
setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void; setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void;
static validateMagFilter(magFilter: TextureFilter): TextureFilter.Nearest | TextureFilter.Linear | TextureFilter.Linear; static validateMagFilter(magFilter: TextureFilter): TextureFilter.Nearest | TextureFilter.Linear;
setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void; setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void;
update(useMipMaps: boolean): void; update(useMipMaps: boolean): void;
restore(): void; restore(): void;

View file

@ -6,6 +6,8 @@ var __extends = (this && this.__extends) || (function () {
return extendStatics(d, b); return extendStatics(d, b);
}; };
return function (d, b) { return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b); extendStatics(d, b);
function __() { this.constructor = d; } function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@ -2239,9 +2241,9 @@ var spine;
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load binary " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load binary ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load binary " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load binary ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -2259,9 +2261,9 @@ var spine;
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load text " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load text " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load text ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -2284,11 +2286,11 @@ var spine;
success(path, img); success(path, img);
}; };
img.onerror = function (ev) { img.onerror = function (ev) {
_this.errors[path] = "Couldn't load image " + path; _this.errors[path] = "Couldn't load image ".concat(path);
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
if (error) if (error)
error(path, "Couldn't load image " + path); error(path, "Couldn't load image ".concat(path));
}; };
if (this.rawDataUris[path]) if (this.rawDataUris[path])
path = this.rawDataUris[path]; path = this.rawDataUris[path];
@ -2315,9 +2317,9 @@ var spine;
} }
catch (e) { catch (e) {
var ex = e; var ex = e;
_this.errors[path] = "Couldn't load texture atlas " + path + ": " + ex.message; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": " + ex.message); error(path, "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
return; return;
@ -2340,17 +2342,17 @@ var spine;
} }
catch (e) { catch (e) {
var ex = e; var ex = e;
_this.errors[path] = "Couldn't load texture atlas " + path + ": " + ex.message; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": " + ex.message); error(path, "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
} }
else { else {
_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path; _this.errors[path] = "Couldn't load texture atlas page ".concat(imagePath, "} of atlas ").concat(path);
if (error) if (error)
error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path); error(path, "Couldn't load texture atlas page ".concat(imagePath, " of atlas ").concat(path));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
@ -2359,9 +2361,9 @@ var spine;
pageLoadError = true; pageLoadError = true;
pagesLoaded.count++; pagesLoaded.count++;
if (pagesLoaded.count == atlasPages.length) { if (pagesLoaded.count == atlasPages.length) {
_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path; _this.errors[path] = "Couldn't load texture atlas page ".concat(imagePath, "} of atlas ").concat(path);
if (error) if (error)
error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path); error(path, "Couldn't load texture atlas page ".concat(imagePath, " of atlas ").concat(path));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
@ -2372,9 +2374,9 @@ var spine;
_loop_1(atlasPage); _loop_1(atlasPage);
} }
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load texture atlas " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load texture atlas ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -3540,7 +3542,7 @@ var spine;
_this.rawAssets[path] = request.responseText; _this.rawAssets[path] = request.responseText;
} }
else { else {
_this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(request.status, ", ").concat(request.responseText);
} }
} }
}; };
@ -3560,7 +3562,7 @@ var spine;
_this.rawAssets[path] = JSON.parse(request.responseText); _this.rawAssets[path] = JSON.parse(request.responseText);
} }
else { else {
_this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(request.status, ", ").concat(request.responseText);
} }
} }
}; };
@ -3597,7 +3599,7 @@ var spine;
_this.rawAssets[path] = img_1; _this.rawAssets[path] = img_1;
}; };
img_1.onerror = function (ev) { img_1.onerror = function (ev) {
_this.errors[path] = "Couldn't load image " + path; _this.errors[path] = "Couldn't load image ".concat(path);
}; };
img_1.src = path; img_1.src = path;
} }
@ -6251,7 +6253,7 @@ var spine;
return spine.BlendMode.Multiply; return spine.BlendMode.Multiply;
if (str == "screen") if (str == "screen")
return spine.BlendMode.Screen; return spine.BlendMode.Screen;
throw new Error("Unknown blend mode: " + str); throw new Error("Unknown blend mode: ".concat(str));
}; };
SkeletonJson.positionModeFromString = function (str) { SkeletonJson.positionModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6259,7 +6261,7 @@ var spine;
return spine.PositionMode.Fixed; return spine.PositionMode.Fixed;
if (str == "percent") if (str == "percent")
return spine.PositionMode.Percent; return spine.PositionMode.Percent;
throw new Error("Unknown position mode: " + str); throw new Error("Unknown position mode: ".concat(str));
}; };
SkeletonJson.spacingModeFromString = function (str) { SkeletonJson.spacingModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6269,7 +6271,7 @@ var spine;
return spine.SpacingMode.Fixed; return spine.SpacingMode.Fixed;
if (str == "percent") if (str == "percent")
return spine.SpacingMode.Percent; return spine.SpacingMode.Percent;
throw new Error("Unknown position mode: " + str); throw new Error("Unknown position mode: ".concat(str));
}; };
SkeletonJson.rotateModeFromString = function (str) { SkeletonJson.rotateModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6279,7 +6281,7 @@ var spine;
return spine.RotateMode.Chain; return spine.RotateMode.Chain;
if (str == "chainscale") if (str == "chainscale")
return spine.RotateMode.ChainScale; return spine.RotateMode.ChainScale;
throw new Error("Unknown rotate mode: " + str); throw new Error("Unknown rotate mode: ".concat(str));
}; };
SkeletonJson.transformModeFromString = function (str) { SkeletonJson.transformModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6293,7 +6295,7 @@ var spine;
return spine.TransformMode.NoScale; return spine.TransformMode.NoScale;
if (str == "noscaleorreflection") if (str == "noscaleorreflection")
return spine.TransformMode.NoScaleOrReflection; return spine.TransformMode.NoScaleOrReflection;
throw new Error("Unknown transform mode: " + str); throw new Error("Unknown transform mode: ".concat(str));
}; };
return SkeletonJson; return SkeletonJson;
}()); }());
@ -6558,7 +6560,7 @@ var spine;
case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest; case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest;
case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear; case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear;
case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear; case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear;
default: throw new Error("Unknown texture filter " + text); default: throw new Error("Unknown texture filter ".concat(text));
} }
}; };
Texture.wrapFromString = function (text) { Texture.wrapFromString = function (text) {
@ -6566,7 +6568,7 @@ var spine;
case "mirroredtepeat": return TextureWrap.MirroredRepeat; case "mirroredtepeat": return TextureWrap.MirroredRepeat;
case "clamptoedge": return TextureWrap.ClampToEdge; case "clamptoedge": return TextureWrap.ClampToEdge;
case "repeat": return TextureWrap.Repeat; case "repeat": return TextureWrap.Repeat;
default: throw new Error("Unknown texture wrap " + text); default: throw new Error("Unknown texture wrap ".concat(text));
} }
}; };
return Texture; return Texture;
@ -9796,14 +9798,14 @@ var spine;
var gl = this.context.gl; var gl = this.context.gl;
var location = gl.getUniformLocation(this.program, uniform); var location = gl.getUniformLocation(this.program, uniform);
if (!location && !gl.isContextLost()) if (!location && !gl.isContextLost())
throw new Error("Couldn't find location for uniform " + uniform); throw new Error("Couldn't find location for uniform ".concat(uniform));
return location; return location;
}; };
Shader.prototype.getAttributeLocation = function (attribute) { Shader.prototype.getAttributeLocation = function (attribute) {
var gl = this.context.gl; var gl = this.context.gl;
var location = gl.getAttribLocation(this.program, attribute); var location = gl.getAttribLocation(this.program, attribute);
if (location == -1 && !gl.isContextLost()) if (location == -1 && !gl.isContextLost())
throw new Error("Couldn't find location for attribute " + attribute); throw new Error("Couldn't find location for attribute ".concat(attribute));
return location; return location;
}; };
Shader.prototype.dispose = function () { Shader.prototype.dispose = function () {
@ -9823,17 +9825,17 @@ var spine;
} }
}; };
Shader.newColoredTextured = function (context) { Shader.newColoredTextured = function (context) {
var vs = "\n\t\t\t\tattribute vec4 " + Shader.POSITION + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR + ";\n\t\t\t\tattribute vec2 " + Shader.TEXCOORDS + ";\n\t\t\t\tuniform mat4 " + Shader.MVP_MATRIX + ";\n\t\t\t\tvarying vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = " + Shader.COLOR + ";\n\t\t\t\t\tv_texCoords = " + Shader.TEXCOORDS + ";\n\t\t\t\t\tgl_Position = " + Shader.MVP_MATRIX + " * " + Shader.POSITION + ";\n\t\t\t\t}\n\t\t\t"; var vs = "\n\t\t\t\tattribute vec4 ".concat(Shader.POSITION, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR, ";\n\t\t\t\tattribute vec2 ").concat(Shader.TEXCOORDS, ";\n\t\t\t\tuniform mat4 ").concat(Shader.MVP_MATRIX, ";\n\t\t\t\tvarying vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = ").concat(Shader.COLOR, ";\n\t\t\t\t\tv_texCoords = ").concat(Shader.TEXCOORDS, ";\n\t\t\t\t\tgl_Position = ").concat(Shader.MVP_MATRIX, " * ").concat(Shader.POSITION, ";\n\t\t\t\t}\n\t\t\t");
var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n\t\t\t\t}\n\t\t\t"; var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n\t\t\t\t}\n\t\t\t";
return new Shader(context, vs, fs); return new Shader(context, vs, fs);
}; };
Shader.newTwoColoredTextured = function (context) { Shader.newTwoColoredTextured = function (context) {
var vs = "\n\t\t\t\tattribute vec4 " + Shader.POSITION + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR2 + ";\n\t\t\t\tattribute vec2 " + Shader.TEXCOORDS + ";\n\t\t\t\tuniform mat4 " + Shader.MVP_MATRIX + ";\n\t\t\t\tvarying vec4 v_light;\n\t\t\t\tvarying vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_light = " + Shader.COLOR + ";\n\t\t\t\t\tv_dark = " + Shader.COLOR2 + ";\n\t\t\t\t\tv_texCoords = " + Shader.TEXCOORDS + ";\n\t\t\t\t\tgl_Position = " + Shader.MVP_MATRIX + " * " + Shader.POSITION + ";\n\t\t\t\t}\n\t\t\t"; var vs = "\n\t\t\t\tattribute vec4 ".concat(Shader.POSITION, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR2, ";\n\t\t\t\tattribute vec2 ").concat(Shader.TEXCOORDS, ";\n\t\t\t\tuniform mat4 ").concat(Shader.MVP_MATRIX, ";\n\t\t\t\tvarying vec4 v_light;\n\t\t\t\tvarying vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_light = ").concat(Shader.COLOR, ";\n\t\t\t\t\tv_dark = ").concat(Shader.COLOR2, ";\n\t\t\t\t\tv_texCoords = ").concat(Shader.TEXCOORDS, ";\n\t\t\t\t\tgl_Position = ").concat(Shader.MVP_MATRIX, " * ").concat(Shader.POSITION, ";\n\t\t\t\t}\n\t\t\t");
var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_light;\n\t\t\t\tvarying LOWP vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tvec4 texColor = texture2D(u_texture, v_texCoords);\n\t\t\t\t\tgl_FragColor.a = texColor.a * v_light.a;\n\t\t\t\t\tgl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb;\n\t\t\t\t}\n\t\t\t"; var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_light;\n\t\t\t\tvarying LOWP vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tvec4 texColor = texture2D(u_texture, v_texCoords);\n\t\t\t\t\tgl_FragColor.a = texColor.a * v_light.a;\n\t\t\t\t\tgl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb;\n\t\t\t\t}\n\t\t\t";
return new Shader(context, vs, fs); return new Shader(context, vs, fs);
}; };
Shader.newColored = function (context) { Shader.newColored = function (context) {
var vs = "\n\t\t\t\tattribute vec4 " + Shader.POSITION + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR + ";\n\t\t\t\tuniform mat4 " + Shader.MVP_MATRIX + ";\n\t\t\t\tvarying vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = " + Shader.COLOR + ";\n\t\t\t\t\tgl_Position = " + Shader.MVP_MATRIX + " * " + Shader.POSITION + ";\n\t\t\t\t}\n\t\t\t"; var vs = "\n\t\t\t\tattribute vec4 ".concat(Shader.POSITION, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR, ";\n\t\t\t\tuniform mat4 ").concat(Shader.MVP_MATRIX, ";\n\t\t\t\tvarying vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = ").concat(Shader.COLOR, ";\n\t\t\t\t\tgl_Position = ").concat(Shader.MVP_MATRIX, " * ").concat(Shader.POSITION, ";\n\t\t\t\t}\n\t\t\t");
var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color;\n\t\t\t\t}\n\t\t\t"; var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color;\n\t\t\t\t}\n\t\t\t";
return new Shader(context, vs, fs); return new Shader(context, vs, fs);
}; };

File diff suppressed because one or more lines are too long

View file

@ -6,6 +6,8 @@ var __extends = (this && this.__extends) || (function () {
return extendStatics(d, b); return extendStatics(d, b);
}; };
return function (d, b) { return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b); extendStatics(d, b);
function __() { this.constructor = d; } function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@ -2239,9 +2241,9 @@ var spine;
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load binary " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load binary ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load binary " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load binary ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -2259,9 +2261,9 @@ var spine;
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load text " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load text " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load text ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -2284,11 +2286,11 @@ var spine;
success(path, img); success(path, img);
}; };
img.onerror = function (ev) { img.onerror = function (ev) {
_this.errors[path] = "Couldn't load image " + path; _this.errors[path] = "Couldn't load image ".concat(path);
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
if (error) if (error)
error(path, "Couldn't load image " + path); error(path, "Couldn't load image ".concat(path));
}; };
if (this.rawDataUris[path]) if (this.rawDataUris[path])
path = this.rawDataUris[path]; path = this.rawDataUris[path];
@ -2315,9 +2317,9 @@ var spine;
} }
catch (e) { catch (e) {
var ex = e; var ex = e;
_this.errors[path] = "Couldn't load texture atlas " + path + ": " + ex.message; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": " + ex.message); error(path, "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
return; return;
@ -2340,17 +2342,17 @@ var spine;
} }
catch (e) { catch (e) {
var ex = e; var ex = e;
_this.errors[path] = "Couldn't load texture atlas " + path + ": " + ex.message; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": " + ex.message); error(path, "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
} }
else { else {
_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path; _this.errors[path] = "Couldn't load texture atlas page ".concat(imagePath, "} of atlas ").concat(path);
if (error) if (error)
error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path); error(path, "Couldn't load texture atlas page ".concat(imagePath, " of atlas ").concat(path));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
@ -2359,9 +2361,9 @@ var spine;
pageLoadError = true; pageLoadError = true;
pagesLoaded.count++; pagesLoaded.count++;
if (pagesLoaded.count == atlasPages.length) { if (pagesLoaded.count == atlasPages.length) {
_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path; _this.errors[path] = "Couldn't load texture atlas page ".concat(imagePath, "} of atlas ").concat(path);
if (error) if (error)
error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path); error(path, "Couldn't load texture atlas page ".concat(imagePath, " of atlas ").concat(path));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
@ -2372,9 +2374,9 @@ var spine;
_loop_1(atlasPage); _loop_1(atlasPage);
} }
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load texture atlas " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load texture atlas ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -3540,7 +3542,7 @@ var spine;
_this.rawAssets[path] = request.responseText; _this.rawAssets[path] = request.responseText;
} }
else { else {
_this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(request.status, ", ").concat(request.responseText);
} }
} }
}; };
@ -3560,7 +3562,7 @@ var spine;
_this.rawAssets[path] = JSON.parse(request.responseText); _this.rawAssets[path] = JSON.parse(request.responseText);
} }
else { else {
_this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(request.status, ", ").concat(request.responseText);
} }
} }
}; };
@ -3597,7 +3599,7 @@ var spine;
_this.rawAssets[path] = img_1; _this.rawAssets[path] = img_1;
}; };
img_1.onerror = function (ev) { img_1.onerror = function (ev) {
_this.errors[path] = "Couldn't load image " + path; _this.errors[path] = "Couldn't load image ".concat(path);
}; };
img_1.src = path; img_1.src = path;
} }
@ -6251,7 +6253,7 @@ var spine;
return spine.BlendMode.Multiply; return spine.BlendMode.Multiply;
if (str == "screen") if (str == "screen")
return spine.BlendMode.Screen; return spine.BlendMode.Screen;
throw new Error("Unknown blend mode: " + str); throw new Error("Unknown blend mode: ".concat(str));
}; };
SkeletonJson.positionModeFromString = function (str) { SkeletonJson.positionModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6259,7 +6261,7 @@ var spine;
return spine.PositionMode.Fixed; return spine.PositionMode.Fixed;
if (str == "percent") if (str == "percent")
return spine.PositionMode.Percent; return spine.PositionMode.Percent;
throw new Error("Unknown position mode: " + str); throw new Error("Unknown position mode: ".concat(str));
}; };
SkeletonJson.spacingModeFromString = function (str) { SkeletonJson.spacingModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6269,7 +6271,7 @@ var spine;
return spine.SpacingMode.Fixed; return spine.SpacingMode.Fixed;
if (str == "percent") if (str == "percent")
return spine.SpacingMode.Percent; return spine.SpacingMode.Percent;
throw new Error("Unknown position mode: " + str); throw new Error("Unknown position mode: ".concat(str));
}; };
SkeletonJson.rotateModeFromString = function (str) { SkeletonJson.rotateModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6279,7 +6281,7 @@ var spine;
return spine.RotateMode.Chain; return spine.RotateMode.Chain;
if (str == "chainscale") if (str == "chainscale")
return spine.RotateMode.ChainScale; return spine.RotateMode.ChainScale;
throw new Error("Unknown rotate mode: " + str); throw new Error("Unknown rotate mode: ".concat(str));
}; };
SkeletonJson.transformModeFromString = function (str) { SkeletonJson.transformModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6293,7 +6295,7 @@ var spine;
return spine.TransformMode.NoScale; return spine.TransformMode.NoScale;
if (str == "noscaleorreflection") if (str == "noscaleorreflection")
return spine.TransformMode.NoScaleOrReflection; return spine.TransformMode.NoScaleOrReflection;
throw new Error("Unknown transform mode: " + str); throw new Error("Unknown transform mode: ".concat(str));
}; };
return SkeletonJson; return SkeletonJson;
}()); }());
@ -6558,7 +6560,7 @@ var spine;
case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest; case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest;
case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear; case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear;
case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear; case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear;
default: throw new Error("Unknown texture filter " + text); default: throw new Error("Unknown texture filter ".concat(text));
} }
}; };
Texture.wrapFromString = function (text) { Texture.wrapFromString = function (text) {
@ -6566,7 +6568,7 @@ var spine;
case "mirroredtepeat": return TextureWrap.MirroredRepeat; case "mirroredtepeat": return TextureWrap.MirroredRepeat;
case "clamptoedge": return TextureWrap.ClampToEdge; case "clamptoedge": return TextureWrap.ClampToEdge;
case "repeat": return TextureWrap.Repeat; case "repeat": return TextureWrap.Repeat;
default: throw new Error("Unknown texture wrap " + text); default: throw new Error("Unknown texture wrap ".concat(text));
} }
}; };
return Texture; return Texture;

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,4 @@
/// <reference types="offscreencanvas" />
declare module spine { declare module spine {
class Animation { class Animation {
name: string; name: string;
@ -1371,7 +1372,7 @@ declare module spine.webgl {
static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL: boolean; static DISABLE_UNPACK_PREMULTIPLIED_ALPHA_WEBGL: boolean;
constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement | ImageBitmap, useMipMaps?: boolean); constructor(context: ManagedWebGLRenderingContext | WebGLRenderingContext, image: HTMLImageElement | ImageBitmap, useMipMaps?: boolean);
setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void; setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void;
static validateMagFilter(magFilter: TextureFilter): TextureFilter.Nearest | TextureFilter.Linear | TextureFilter.Linear; static validateMagFilter(magFilter: TextureFilter): TextureFilter.Nearest | TextureFilter.Linear;
setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void; setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void;
update(useMipMaps: boolean): void; update(useMipMaps: boolean): void;
restore(): void; restore(): void;

View file

@ -6,6 +6,8 @@ var __extends = (this && this.__extends) || (function () {
return extendStatics(d, b); return extendStatics(d, b);
}; };
return function (d, b) { return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b); extendStatics(d, b);
function __() { this.constructor = d; } function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@ -2239,9 +2241,9 @@ var spine;
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load binary " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load binary ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load binary " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load binary ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -2259,9 +2261,9 @@ var spine;
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load text " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load text " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load text ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -2284,11 +2286,11 @@ var spine;
success(path, img); success(path, img);
}; };
img.onerror = function (ev) { img.onerror = function (ev) {
_this.errors[path] = "Couldn't load image " + path; _this.errors[path] = "Couldn't load image ".concat(path);
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
if (error) if (error)
error(path, "Couldn't load image " + path); error(path, "Couldn't load image ".concat(path));
}; };
if (this.rawDataUris[path]) if (this.rawDataUris[path])
path = this.rawDataUris[path]; path = this.rawDataUris[path];
@ -2315,9 +2317,9 @@ var spine;
} }
catch (e) { catch (e) {
var ex = e; var ex = e;
_this.errors[path] = "Couldn't load texture atlas " + path + ": " + ex.message; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": " + ex.message); error(path, "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
return; return;
@ -2340,17 +2342,17 @@ var spine;
} }
catch (e) { catch (e) {
var ex = e; var ex = e;
_this.errors[path] = "Couldn't load texture atlas " + path + ": " + ex.message; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": " + ex.message); error(path, "Couldn't load texture atlas ".concat(path, ": ").concat(ex.message));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
} }
else { else {
_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path; _this.errors[path] = "Couldn't load texture atlas page ".concat(imagePath, "} of atlas ").concat(path);
if (error) if (error)
error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path); error(path, "Couldn't load texture atlas page ".concat(imagePath, " of atlas ").concat(path));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
@ -2359,9 +2361,9 @@ var spine;
pageLoadError = true; pageLoadError = true;
pagesLoaded.count++; pagesLoaded.count++;
if (pagesLoaded.count == atlasPages.length) { if (pagesLoaded.count == atlasPages.length) {
_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path; _this.errors[path] = "Couldn't load texture atlas page ".concat(imagePath, "} of atlas ").concat(path);
if (error) if (error)
error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path); error(path, "Couldn't load texture atlas page ".concat(imagePath, " of atlas ").concat(path));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
} }
@ -2372,9 +2374,9 @@ var spine;
_loop_1(atlasPage); _loop_1(atlasPage);
} }
}, function (state, responseText) { }, function (state, responseText) {
_this.errors[path] = "Couldn't load texture atlas " + path + ": status " + status + ", " + responseText; _this.errors[path] = "Couldn't load texture atlas ".concat(path, ": status ").concat(status, ", ").concat(responseText);
if (error) if (error)
error(path, "Couldn't load texture atlas " + path + ": status " + status + ", " + responseText); error(path, "Couldn't load texture atlas ".concat(path, ": status ").concat(status, ", ").concat(responseText));
_this.toLoad--; _this.toLoad--;
_this.loaded++; _this.loaded++;
}); });
@ -3540,7 +3542,7 @@ var spine;
_this.rawAssets[path] = request.responseText; _this.rawAssets[path] = request.responseText;
} }
else { else {
_this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(request.status, ", ").concat(request.responseText);
} }
} }
}; };
@ -3560,7 +3562,7 @@ var spine;
_this.rawAssets[path] = JSON.parse(request.responseText); _this.rawAssets[path] = JSON.parse(request.responseText);
} }
else { else {
_this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; _this.errors[path] = "Couldn't load text ".concat(path, ": status ").concat(request.status, ", ").concat(request.responseText);
} }
} }
}; };
@ -3597,7 +3599,7 @@ var spine;
_this.rawAssets[path] = img_1; _this.rawAssets[path] = img_1;
}; };
img_1.onerror = function (ev) { img_1.onerror = function (ev) {
_this.errors[path] = "Couldn't load image " + path; _this.errors[path] = "Couldn't load image ".concat(path);
}; };
img_1.src = path; img_1.src = path;
} }
@ -6251,7 +6253,7 @@ var spine;
return spine.BlendMode.Multiply; return spine.BlendMode.Multiply;
if (str == "screen") if (str == "screen")
return spine.BlendMode.Screen; return spine.BlendMode.Screen;
throw new Error("Unknown blend mode: " + str); throw new Error("Unknown blend mode: ".concat(str));
}; };
SkeletonJson.positionModeFromString = function (str) { SkeletonJson.positionModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6259,7 +6261,7 @@ var spine;
return spine.PositionMode.Fixed; return spine.PositionMode.Fixed;
if (str == "percent") if (str == "percent")
return spine.PositionMode.Percent; return spine.PositionMode.Percent;
throw new Error("Unknown position mode: " + str); throw new Error("Unknown position mode: ".concat(str));
}; };
SkeletonJson.spacingModeFromString = function (str) { SkeletonJson.spacingModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6269,7 +6271,7 @@ var spine;
return spine.SpacingMode.Fixed; return spine.SpacingMode.Fixed;
if (str == "percent") if (str == "percent")
return spine.SpacingMode.Percent; return spine.SpacingMode.Percent;
throw new Error("Unknown position mode: " + str); throw new Error("Unknown position mode: ".concat(str));
}; };
SkeletonJson.rotateModeFromString = function (str) { SkeletonJson.rotateModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6279,7 +6281,7 @@ var spine;
return spine.RotateMode.Chain; return spine.RotateMode.Chain;
if (str == "chainscale") if (str == "chainscale")
return spine.RotateMode.ChainScale; return spine.RotateMode.ChainScale;
throw new Error("Unknown rotate mode: " + str); throw new Error("Unknown rotate mode: ".concat(str));
}; };
SkeletonJson.transformModeFromString = function (str) { SkeletonJson.transformModeFromString = function (str) {
str = str.toLowerCase(); str = str.toLowerCase();
@ -6293,7 +6295,7 @@ var spine;
return spine.TransformMode.NoScale; return spine.TransformMode.NoScale;
if (str == "noscaleorreflection") if (str == "noscaleorreflection")
return spine.TransformMode.NoScaleOrReflection; return spine.TransformMode.NoScaleOrReflection;
throw new Error("Unknown transform mode: " + str); throw new Error("Unknown transform mode: ".concat(str));
}; };
return SkeletonJson; return SkeletonJson;
}()); }());
@ -6558,7 +6560,7 @@ var spine;
case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest; case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest;
case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear; case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear;
case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear; case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear;
default: throw new Error("Unknown texture filter " + text); default: throw new Error("Unknown texture filter ".concat(text));
} }
}; };
Texture.wrapFromString = function (text) { Texture.wrapFromString = function (text) {
@ -6566,7 +6568,7 @@ var spine;
case "mirroredtepeat": return TextureWrap.MirroredRepeat; case "mirroredtepeat": return TextureWrap.MirroredRepeat;
case "clamptoedge": return TextureWrap.ClampToEdge; case "clamptoedge": return TextureWrap.ClampToEdge;
case "repeat": return TextureWrap.Repeat; case "repeat": return TextureWrap.Repeat;
default: throw new Error("Unknown texture wrap " + text); default: throw new Error("Unknown texture wrap ".concat(text));
} }
}; };
return Texture; return Texture;
@ -9528,14 +9530,14 @@ var spine;
var gl = this.context.gl; var gl = this.context.gl;
var location = gl.getUniformLocation(this.program, uniform); var location = gl.getUniformLocation(this.program, uniform);
if (!location && !gl.isContextLost()) if (!location && !gl.isContextLost())
throw new Error("Couldn't find location for uniform " + uniform); throw new Error("Couldn't find location for uniform ".concat(uniform));
return location; return location;
}; };
Shader.prototype.getAttributeLocation = function (attribute) { Shader.prototype.getAttributeLocation = function (attribute) {
var gl = this.context.gl; var gl = this.context.gl;
var location = gl.getAttribLocation(this.program, attribute); var location = gl.getAttribLocation(this.program, attribute);
if (location == -1 && !gl.isContextLost()) if (location == -1 && !gl.isContextLost())
throw new Error("Couldn't find location for attribute " + attribute); throw new Error("Couldn't find location for attribute ".concat(attribute));
return location; return location;
}; };
Shader.prototype.dispose = function () { Shader.prototype.dispose = function () {
@ -9555,17 +9557,17 @@ var spine;
} }
}; };
Shader.newColoredTextured = function (context) { Shader.newColoredTextured = function (context) {
var vs = "\n\t\t\t\tattribute vec4 " + Shader.POSITION + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR + ";\n\t\t\t\tattribute vec2 " + Shader.TEXCOORDS + ";\n\t\t\t\tuniform mat4 " + Shader.MVP_MATRIX + ";\n\t\t\t\tvarying vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = " + Shader.COLOR + ";\n\t\t\t\t\tv_texCoords = " + Shader.TEXCOORDS + ";\n\t\t\t\t\tgl_Position = " + Shader.MVP_MATRIX + " * " + Shader.POSITION + ";\n\t\t\t\t}\n\t\t\t"; var vs = "\n\t\t\t\tattribute vec4 ".concat(Shader.POSITION, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR, ";\n\t\t\t\tattribute vec2 ").concat(Shader.TEXCOORDS, ";\n\t\t\t\tuniform mat4 ").concat(Shader.MVP_MATRIX, ";\n\t\t\t\tvarying vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = ").concat(Shader.COLOR, ";\n\t\t\t\t\tv_texCoords = ").concat(Shader.TEXCOORDS, ";\n\t\t\t\t\tgl_Position = ").concat(Shader.MVP_MATRIX, " * ").concat(Shader.POSITION, ";\n\t\t\t\t}\n\t\t\t");
var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n\t\t\t\t}\n\t\t\t"; var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n\t\t\t\t}\n\t\t\t";
return new Shader(context, vs, fs); return new Shader(context, vs, fs);
}; };
Shader.newTwoColoredTextured = function (context) { Shader.newTwoColoredTextured = function (context) {
var vs = "\n\t\t\t\tattribute vec4 " + Shader.POSITION + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR2 + ";\n\t\t\t\tattribute vec2 " + Shader.TEXCOORDS + ";\n\t\t\t\tuniform mat4 " + Shader.MVP_MATRIX + ";\n\t\t\t\tvarying vec4 v_light;\n\t\t\t\tvarying vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_light = " + Shader.COLOR + ";\n\t\t\t\t\tv_dark = " + Shader.COLOR2 + ";\n\t\t\t\t\tv_texCoords = " + Shader.TEXCOORDS + ";\n\t\t\t\t\tgl_Position = " + Shader.MVP_MATRIX + " * " + Shader.POSITION + ";\n\t\t\t\t}\n\t\t\t"; var vs = "\n\t\t\t\tattribute vec4 ".concat(Shader.POSITION, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR2, ";\n\t\t\t\tattribute vec2 ").concat(Shader.TEXCOORDS, ";\n\t\t\t\tuniform mat4 ").concat(Shader.MVP_MATRIX, ";\n\t\t\t\tvarying vec4 v_light;\n\t\t\t\tvarying vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_light = ").concat(Shader.COLOR, ";\n\t\t\t\t\tv_dark = ").concat(Shader.COLOR2, ";\n\t\t\t\t\tv_texCoords = ").concat(Shader.TEXCOORDS, ";\n\t\t\t\t\tgl_Position = ").concat(Shader.MVP_MATRIX, " * ").concat(Shader.POSITION, ";\n\t\t\t\t}\n\t\t\t");
var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_light;\n\t\t\t\tvarying LOWP vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tvec4 texColor = texture2D(u_texture, v_texCoords);\n\t\t\t\t\tgl_FragColor.a = texColor.a * v_light.a;\n\t\t\t\t\tgl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb;\n\t\t\t\t}\n\t\t\t"; var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_light;\n\t\t\t\tvarying LOWP vec4 v_dark;\n\t\t\t\tvarying vec2 v_texCoords;\n\t\t\t\tuniform sampler2D u_texture;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tvec4 texColor = texture2D(u_texture, v_texCoords);\n\t\t\t\t\tgl_FragColor.a = texColor.a * v_light.a;\n\t\t\t\t\tgl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb;\n\t\t\t\t}\n\t\t\t";
return new Shader(context, vs, fs); return new Shader(context, vs, fs);
}; };
Shader.newColored = function (context) { Shader.newColored = function (context) {
var vs = "\n\t\t\t\tattribute vec4 " + Shader.POSITION + ";\n\t\t\t\tattribute vec4 " + Shader.COLOR + ";\n\t\t\t\tuniform mat4 " + Shader.MVP_MATRIX + ";\n\t\t\t\tvarying vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = " + Shader.COLOR + ";\n\t\t\t\t\tgl_Position = " + Shader.MVP_MATRIX + " * " + Shader.POSITION + ";\n\t\t\t\t}\n\t\t\t"; var vs = "\n\t\t\t\tattribute vec4 ".concat(Shader.POSITION, ";\n\t\t\t\tattribute vec4 ").concat(Shader.COLOR, ";\n\t\t\t\tuniform mat4 ").concat(Shader.MVP_MATRIX, ";\n\t\t\t\tvarying vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tv_color = ").concat(Shader.COLOR, ";\n\t\t\t\t\tgl_Position = ").concat(Shader.MVP_MATRIX, " * ").concat(Shader.POSITION, ";\n\t\t\t\t}\n\t\t\t");
var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color;\n\t\t\t\t}\n\t\t\t"; var fs = "\n\t\t\t\t#ifdef GL_ES\n\t\t\t\t\t#define LOWP lowp\n\t\t\t\t\tprecision mediump float;\n\t\t\t\t#else\n\t\t\t\t\t#define LOWP\n\t\t\t\t#endif\n\t\t\t\tvarying LOWP vec4 v_color;\n\n\t\t\t\tvoid main () {\n\t\t\t\t\tgl_FragColor = v_color;\n\t\t\t\t}\n\t\t\t";
return new Shader(context, vs, fs); return new Shader(context, vs, fs);
}; };

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const webpack = require('webpack'); const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const exec = require('child_process').exec; const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin'); const RemovePlugin = require('remove-files-webpack-plugin');
@ -53,18 +53,19 @@ module.exports = {
optimization: { optimization: {
minimizer: [ minimizer: [
new UglifyJSPlugin({ new TerserPlugin({
include: /\.min\.js$/, include: /\.min\.js$/,
parallel: true, parallel: true,
sourceMap: false, extractComments: false,
uglifyOptions: { terserOptions: {
format: {
comments: false
},
compress: true, compress: true,
ie8: false, ie8: false,
ecma: 5, ecma: 5,
output: {comments: false},
warnings: false warnings: false
}, }
warningsFilter: () => false
}) })
] ]
}, },

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const webpack = require('webpack'); const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const exec = require('child_process').exec; const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin'); const RemovePlugin = require('remove-files-webpack-plugin');
@ -53,18 +53,19 @@ module.exports = {
optimization: { optimization: {
minimizer: [ minimizer: [
new UglifyJSPlugin({ new TerserPlugin({
include: /\.min\.js$/, include: /\.min\.js$/,
parallel: true, parallel: true,
sourceMap: false, extractComments: false,
uglifyOptions: { terserOptions: {
format: {
comments: false
},
compress: true, compress: true,
ie8: false, ie8: false,
ecma: 5, ecma: 5,
output: {comments: false},
warnings: false warnings: false
}, }
warningsFilter: () => false
}) })
] ]
}, },

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const webpack = require('webpack'); const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin');
const exec = require('child_process').exec; const exec = require('child_process').exec;
const RemovePlugin = require('remove-files-webpack-plugin'); const RemovePlugin = require('remove-files-webpack-plugin');
@ -53,18 +53,19 @@ module.exports = {
optimization: { optimization: {
minimizer: [ minimizer: [
new UglifyJSPlugin({ new TerserPlugin({
include: /\.min\.js$/, include: /\.min\.js$/,
parallel: true, parallel: true,
sourceMap: false, extractComments: false,
uglifyOptions: { terserOptions: {
format: {
comments: false
},
compress: true, compress: true,
ie8: false, ie8: false,
ecma: 5, ecma: 5,
output: {comments: false},
warnings: false warnings: false
}, }
warningsFilter: () => false
}) })
] ]
}, },

View file

@ -603,6 +603,7 @@ var AnimationManager = new Class({
* Generates objects with string based frame names, as configured by the given {@link Phaser.Types.Animations.GenerateFrameNames}. * Generates objects with string based frame names, as configured by the given {@link Phaser.Types.Animations.GenerateFrameNames}.
* *
* It's a helper method, designed to make it easier for you to extract all of the frame names from texture atlases. * It's a helper method, designed to make it easier for you to extract all of the frame names from texture atlases.
*
* If you're working with a sprite sheet, see the `generateFrameNumbers` method instead. * If you're working with a sprite sheet, see the `generateFrameNumbers` method instead.
* *
* Example: * Example:
@ -697,7 +698,6 @@ var AnimationManager = new Class({
* If you're working with a texture atlas, see the `generateFrameNames` method instead. * If you're working with a texture atlas, see the `generateFrameNames` method instead.
* *
* It's a helper method, designed to make it easier for you to extract frames from sprite sheets. * It's a helper method, designed to make it easier for you to extract frames from sprite sheets.
* If you're working with a texture atlas, see the `generateFrameNames` method instead.
* *
* Example: * Example:
* *

View file

@ -472,7 +472,7 @@ var AnimationState = new Class({
* @method Phaser.Animations.AnimationState#chain * @method Phaser.Animations.AnimationState#chain
* @since 3.16.0 * @since 3.16.0
* *
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig|string[]|Phaser.Animations.Animation[]|Phaser.Types.Animations.PlayAnimationConfig[])} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object, or an array of them. * @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig|string[]|Phaser.Animations.Animation[]|Phaser.Types.Animations.PlayAnimationConfig[])} [key] - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object, or an array of them.
* *
* @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component. * @return {Phaser.GameObjects.GameObject} The Game Object that owns this Animation Component.
*/ */

View file

@ -46,97 +46,79 @@ var Config = new Class({
var defaultBannerTextColor = '#ffffff'; var defaultBannerTextColor = '#ffffff';
// Scale Manager - Anything set in here over-rides anything set in the core game config
var scaleConfig = GetValue(config, 'scale', null);
/** /**
* @const {(number|string)} Phaser.Core.Config#width - The width of the underlying canvas, in pixels. * @const {(number|string)} Phaser.Core.Config#width - The width of the underlying canvas, in pixels.
*/ */
this.width = GetValue(config, 'width', 1024); this.width = GetValue(scaleConfig, 'width', 1024, config);
/** /**
* @const {(number|string)} Phaser.Core.Config#height - The height of the underlying canvas, in pixels. * @const {(number|string)} Phaser.Core.Config#height - The height of the underlying canvas, in pixels.
*/ */
this.height = GetValue(config, 'height', 768); this.height = GetValue(scaleConfig, 'height', 768, config);
/** /**
* @const {(Phaser.Scale.ZoomType|number)} Phaser.Core.Config#zoom - The zoom factor, as used by the Scale Manager. * @const {(Phaser.Scale.ZoomType|number)} Phaser.Core.Config#zoom - The zoom factor, as used by the Scale Manager.
*/ */
this.zoom = GetValue(config, 'zoom', 1); this.zoom = GetValue(scaleConfig, 'zoom', 1, config);
/** /**
* @const {?*} Phaser.Core.Config#parent - A parent DOM element into which the canvas created by the renderer will be injected. * @const {?*} Phaser.Core.Config#parent - A parent DOM element into which the canvas created by the renderer will be injected.
*/ */
this.parent = GetValue(config, 'parent', undefined); this.parent = GetValue(scaleConfig, 'parent', undefined, config);
/** /**
* @const {Phaser.Scale.ScaleModeType} Phaser.Core.Config#scaleMode - The scale mode as used by the Scale Manager. The default is zero, which is no scaling. * @const {Phaser.Scale.ScaleModeType} Phaser.Core.Config#scaleMode - The scale mode as used by the Scale Manager. The default is zero, which is no scaling.
*/ */
this.scaleMode = GetValue(config, 'scaleMode', 0); this.scaleMode = GetValue(scaleConfig, 'scaleMode', 0, config);
/** /**
* @const {boolean} Phaser.Core.Config#expandParent - Is the Scale Manager allowed to adjust the CSS height property of the parent to be 100%? * @const {boolean} Phaser.Core.Config#expandParent - Is the Scale Manager allowed to adjust the CSS height property of the parent to be 100%?
*/ */
this.expandParent = GetValue(config, 'expandParent', true); this.expandParent = GetValue(scaleConfig, 'expandParent', true, config);
/** /**
* @const {boolean} Phaser.Core.Config#autoRound - Automatically round the display and style sizes of the canvas. This can help with performance in lower-powered devices. * @const {boolean} Phaser.Core.Config#autoRound - Automatically round the display and style sizes of the canvas. This can help with performance in lower-powered devices.
*/ */
this.autoRound = GetValue(config, 'autoRound', false); this.autoRound = GetValue(scaleConfig, 'autoRound', false, config);
/** /**
* @const {Phaser.Scale.CenterType} Phaser.Core.Config#autoCenter - Automatically center the canvas within the parent? * @const {Phaser.Scale.CenterType} Phaser.Core.Config#autoCenter - Automatically center the canvas within the parent?
*/ */
this.autoCenter = GetValue(config, 'autoCenter', 0); this.autoCenter = GetValue(scaleConfig, 'autoCenter', 0, config);
/** /**
* @const {number} Phaser.Core.Config#resizeInterval - How many ms should elapse before checking if the browser size has changed? * @const {number} Phaser.Core.Config#resizeInterval - How many ms should elapse before checking if the browser size has changed?
*/ */
this.resizeInterval = GetValue(config, 'resizeInterval', 500); this.resizeInterval = GetValue(scaleConfig, 'resizeInterval', 500, config);
/** /**
* @const {?(HTMLElement|string)} Phaser.Core.Config#fullscreenTarget - The DOM element that will be sent into full screen mode, or its `id`. If undefined Phaser will create its own div and insert the canvas into it when entering fullscreen mode. * @const {?(HTMLElement|string)} Phaser.Core.Config#fullscreenTarget - The DOM element that will be sent into full screen mode, or its `id`. If undefined Phaser will create its own div and insert the canvas into it when entering fullscreen mode.
*/ */
this.fullscreenTarget = GetValue(config, 'fullscreenTarget', null); this.fullscreenTarget = GetValue(scaleConfig, 'fullscreenTarget', null, config);
/** /**
* @const {number} Phaser.Core.Config#minWidth - The minimum width, in pixels, the canvas will scale down to. A value of zero means no minimum. * @const {number} Phaser.Core.Config#minWidth - The minimum width, in pixels, the canvas will scale down to. A value of zero means no minimum.
*/ */
this.minWidth = GetValue(config, 'minWidth', 0); this.minWidth = GetValue(scaleConfig, 'minWidth', 0, config);
/** /**
* @const {number} Phaser.Core.Config#maxWidth - The maximum width, in pixels, the canvas will scale up to. A value of zero means no maximum. * @const {number} Phaser.Core.Config#maxWidth - The maximum width, in pixels, the canvas will scale up to. A value of zero means no maximum.
*/ */
this.maxWidth = GetValue(config, 'maxWidth', 0); this.maxWidth = GetValue(scaleConfig, 'maxWidth', 0, config);
/** /**
* @const {number} Phaser.Core.Config#minHeight - The minimum height, in pixels, the canvas will scale down to. A value of zero means no minimum. * @const {number} Phaser.Core.Config#minHeight - The minimum height, in pixels, the canvas will scale down to. A value of zero means no minimum.
*/ */
this.minHeight = GetValue(config, 'minHeight', 0); this.minHeight = GetValue(scaleConfig, 'minHeight', 0, config);
/** /**
* @const {number} Phaser.Core.Config#maxHeight - The maximum height, in pixels, the canvas will scale up to. A value of zero means no maximum. * @const {number} Phaser.Core.Config#maxHeight - The maximum height, in pixels, the canvas will scale up to. A value of zero means no maximum.
*/ */
this.maxHeight = GetValue(config, 'maxHeight', 0); this.maxHeight = GetValue(scaleConfig, 'maxHeight', 0, config);
// Scale Manager - Anything set in here over-rides anything set above
var scaleConfig = GetValue(config, 'scale', null);
if (scaleConfig)
{
this.width = GetValue(scaleConfig, 'width', this.width);
this.height = GetValue(scaleConfig, 'height', this.height);
this.zoom = GetValue(scaleConfig, 'zoom', this.zoom);
this.parent = GetValue(scaleConfig, 'parent', this.parent);
this.scaleMode = GetValue(scaleConfig, 'mode', this.scaleMode);
this.expandParent = GetValue(scaleConfig, 'expandParent', this.expandParent);
this.autoRound = GetValue(scaleConfig, 'autoRound', this.autoRound);
this.autoCenter = GetValue(scaleConfig, 'autoCenter', this.autoCenter);
this.resizeInterval = GetValue(scaleConfig, 'resizeInterval', this.resizeInterval);
this.fullscreenTarget = GetValue(scaleConfig, 'fullscreenTarget', this.fullscreenTarget);
this.minWidth = GetValue(scaleConfig, 'min.width', this.minWidth);
this.maxWidth = GetValue(scaleConfig, 'max.width', this.maxWidth);
this.minHeight = GetValue(scaleConfig, 'min.height', this.minHeight);
this.maxHeight = GetValue(scaleConfig, 'max.height', this.maxHeight);
}
/** /**
* @const {number} Phaser.Core.Config#renderType - Force Phaser to use a specific renderer. Can be `CONST.CANVAS`, `CONST.WEBGL`, `CONST.HEADLESS` or `CONST.AUTO` (default) * @const {number} Phaser.Core.Config#renderType - Force Phaser to use a specific renderer. Can be `CONST.CANVAS`, `CONST.WEBGL`, `CONST.HEADLESS` or `CONST.AUTO` (default)
@ -336,45 +318,44 @@ var Config = new Class({
*/ */
this.fps = GetValue(config, 'fps', null); this.fps = GetValue(config, 'fps', null);
// Renderer Settings // Render Settings - Anything set in here over-rides anything set in the core game config
// These can either be in a `render` object within the Config, or specified on their own
var renderConfig = GetValue(config, 'render', config); var renderConfig = GetValue(config, 'render', null);
/** /**
* @const {Phaser.Types.Core.PipelineConfig} Phaser.Core.Config#pipeline - An object mapping WebGL names to WebGLPipeline classes. These should be class constructors, not instances. * @const {Phaser.Types.Core.PipelineConfig} Phaser.Core.Config#pipeline - An object mapping WebGL names to WebGLPipeline classes. These should be class constructors, not instances.
*/ */
this.pipeline = GetValue(renderConfig, 'pipeline', null); this.pipeline = GetValue(renderConfig, 'pipeline', null, config);
/** /**
* @const {boolean} Phaser.Core.Config#antialias - When set to `true`, WebGL uses linear interpolation to draw scaled or rotated textures, giving a smooth appearance. When set to `false`, WebGL uses nearest-neighbor interpolation, giving a crisper appearance. `false` also disables antialiasing of the game canvas itself, if the browser supports it, when the game canvas is scaled. * @const {boolean} Phaser.Core.Config#antialias - When set to `true`, WebGL uses linear interpolation to draw scaled or rotated textures, giving a smooth appearance. When set to `false`, WebGL uses nearest-neighbor interpolation, giving a crisper appearance. `false` also disables antialiasing of the game canvas itself, if the browser supports it, when the game canvas is scaled.
*/ */
this.antialias = GetValue(renderConfig, 'antialias', true); this.antialias = GetValue(renderConfig, 'antialias', true, config);
/** /**
* @const {boolean} Phaser.Core.Config#antialiasGL - Sets the `antialias` property when the WebGL context is created. Setting this value does not impact any subsequent textures that are created, or the canvas style attributes. * @const {boolean} Phaser.Core.Config#antialiasGL - Sets the `antialias` property when the WebGL context is created. Setting this value does not impact any subsequent textures that are created, or the canvas style attributes.
*/ */
this.antialiasGL = GetValue(renderConfig, 'antialiasGL', true); this.antialiasGL = GetValue(renderConfig, 'antialiasGL', true, config);
/** /**
* @const {string} Phaser.Core.Config#mipmapFilter - Sets the `mipmapFilter` property when the WebGL renderer is created. * @const {string} Phaser.Core.Config#mipmapFilter - Sets the `mipmapFilter` property when the WebGL renderer is created.
*/ */
this.mipmapFilter = GetValue(renderConfig, 'mipmapFilter', 'LINEAR'); this.mipmapFilter = GetValue(renderConfig, 'mipmapFilter', 'LINEAR', config);
/** /**
* @const {boolean} Phaser.Core.Config#desynchronized - When set to `true` it will create a desynchronized context for both 2D and WebGL. See https://developers.google.com/web/updates/2019/05/desynchronized for details. * @const {boolean} Phaser.Core.Config#desynchronized - When set to `true` it will create a desynchronized context for both 2D and WebGL. See https://developers.google.com/web/updates/2019/05/desynchronized for details.
*/ */
this.desynchronized = GetValue(renderConfig, 'desynchronized', false); this.desynchronized = GetValue(renderConfig, 'desynchronized', false, config);
/** /**
* @const {boolean} Phaser.Core.Config#roundPixels - Draw texture-based Game Objects at only whole-integer positions. Game Objects without textures, like Graphics, ignore this property. * @const {boolean} Phaser.Core.Config#roundPixels - Draw texture-based Game Objects at only whole-integer positions. Game Objects without textures, like Graphics, ignore this property.
*/ */
this.roundPixels = GetValue(renderConfig, 'roundPixels', false); this.roundPixels = GetValue(renderConfig, 'roundPixels', false, config);
/** /**
* @const {boolean} Phaser.Core.Config#pixelArt - Prevent pixel art from becoming blurred when scaled. It will remain crisp (tells the WebGL renderer to automatically create textures using a linear filter mode). * @const {boolean} Phaser.Core.Config#pixelArt - Prevent pixel art from becoming blurred when scaled. It will remain crisp (tells the WebGL renderer to automatically create textures using a linear filter mode).
*/ */
this.pixelArt = GetValue(renderConfig, 'pixelArt', this.zoom !== 1); this.pixelArt = GetValue(renderConfig, 'pixelArt', this.zoom !== 1, config);
if (this.pixelArt) if (this.pixelArt)
{ {
@ -386,47 +367,47 @@ var Config = new Class({
/** /**
* @const {boolean} Phaser.Core.Config#transparent - Whether the game canvas will have a transparent background. * @const {boolean} Phaser.Core.Config#transparent - Whether the game canvas will have a transparent background.
*/ */
this.transparent = GetValue(renderConfig, 'transparent', false); this.transparent = GetValue(renderConfig, 'transparent', false, config);
/** /**
* @const {boolean} Phaser.Core.Config#clearBeforeRender - Whether the game canvas will be cleared between each rendering frame. You can disable this if you have a full-screen background image or game object. * @const {boolean} Phaser.Core.Config#clearBeforeRender - Whether the game canvas will be cleared between each rendering frame. You can disable this if you have a full-screen background image or game object.
*/ */
this.clearBeforeRender = GetValue(renderConfig, 'clearBeforeRender', true); this.clearBeforeRender = GetValue(renderConfig, 'clearBeforeRender', true, config);
/** /**
* @const {boolean} Phaser.Core.Config#preserveDrawingBuffer - If the value is true the WebGL buffers will not be cleared and will preserve their values until cleared or overwritten by the author. * @const {boolean} Phaser.Core.Config#preserveDrawingBuffer - If the value is true the WebGL buffers will not be cleared and will preserve their values until cleared or overwritten by the author.
*/ */
this.preserveDrawingBuffer = GetValue(renderConfig, 'preserveDrawingBuffer', false); this.preserveDrawingBuffer = GetValue(renderConfig, 'preserveDrawingBuffer', false, config);
/** /**
* @const {boolean} Phaser.Core.Config#premultipliedAlpha - In WebGL mode, sets the drawing buffer to contain colors with pre-multiplied alpha. * @const {boolean} Phaser.Core.Config#premultipliedAlpha - In WebGL mode, sets the drawing buffer to contain colors with pre-multiplied alpha.
*/ */
this.premultipliedAlpha = GetValue(renderConfig, 'premultipliedAlpha', true); this.premultipliedAlpha = GetValue(renderConfig, 'premultipliedAlpha', true, config);
/** /**
* @const {boolean} Phaser.Core.Config#failIfMajorPerformanceCaveat - Let the browser abort creating a WebGL context if it judges performance would be unacceptable. * @const {boolean} Phaser.Core.Config#failIfMajorPerformanceCaveat - Let the browser abort creating a WebGL context if it judges performance would be unacceptable.
*/ */
this.failIfMajorPerformanceCaveat = GetValue(renderConfig, 'failIfMajorPerformanceCaveat', false); this.failIfMajorPerformanceCaveat = GetValue(renderConfig, 'failIfMajorPerformanceCaveat', false, config);
/** /**
* @const {string} Phaser.Core.Config#powerPreference - "high-performance", "low-power" or "default". A hint to the browser on how much device power the game might use. * @const {string} Phaser.Core.Config#powerPreference - "high-performance", "low-power" or "default". A hint to the browser on how much device power the game might use.
*/ */
this.powerPreference = GetValue(renderConfig, 'powerPreference', 'default'); this.powerPreference = GetValue(renderConfig, 'powerPreference', 'default', config);
/** /**
* @const {number} Phaser.Core.Config#batchSize - The default WebGL Batch size. Represents the number of _quads_ that can be added to a single batch. * @const {number} Phaser.Core.Config#batchSize - The default WebGL Batch size. Represents the number of _quads_ that can be added to a single batch.
*/ */
this.batchSize = GetValue(renderConfig, 'batchSize', 4096); this.batchSize = GetValue(renderConfig, 'batchSize', 4096, config);
/** /**
* @const {number} Phaser.Core.Config#maxTextures - When in WebGL mode, this sets the maximum number of GPU Textures to use. The default, -1, will use all available units. The WebGL1 spec says all browsers should provide a minimum of 8. * @const {number} Phaser.Core.Config#maxTextures - When in WebGL mode, this sets the maximum number of GPU Textures to use. The default, -1, will use all available units. The WebGL1 spec says all browsers should provide a minimum of 8.
*/ */
this.maxTextures = GetValue(renderConfig, 'maxTextures', -1); this.maxTextures = GetValue(renderConfig, 'maxTextures', -1, config);
/** /**
* @const {number} Phaser.Core.Config#maxLights - The maximum number of lights allowed to be visible within range of a single Camera in the LightManager. * @const {number} Phaser.Core.Config#maxLights - The maximum number of lights allowed to be visible within range of a single Camera in the LightManager.
*/ */
this.maxLights = GetValue(renderConfig, 'maxLights', 10); this.maxLights = GetValue(renderConfig, 'maxLights', 10, config);
var bgc = GetValue(config, 'backgroundColor', 0); var bgc = GetValue(config, 'backgroundColor', 0);

View file

@ -651,7 +651,7 @@ var TimeStep = new Class({
this.startTime += -this.lastTime + (this.lastTime + window.performance.now()); this.startTime += -this.lastTime + (this.lastTime + window.performance.now());
} }
this.raf.start(this.step.bind(this), this.useRAF); this.raf.start(this.step.bind(this), this.forceSetTimeOut, this._target);
this.running = true; this.running = true;

View file

@ -85,17 +85,20 @@ var BlitterCanvasRenderer = function (renderer, src, camera, parentMatrix)
dy = Math.round(dy); dy = Math.round(dy);
} }
ctx.drawImage( if (cd.width > 0 && cd.height > 0)
frame.source.image, {
cd.x, ctx.drawImage(
cd.y, frame.source.image,
cd.width, cd.x,
cd.height, cd.y,
dx + bob.x + cameraScrollX, cd.width,
dy + bob.y + cameraScrollY, cd.height,
cd.width, dx + bob.x + cameraScrollX,
cd.height dy + bob.y + cameraScrollY,
); cd.width,
cd.height
);
}
} }
else else
{ {
@ -111,11 +114,14 @@ var BlitterCanvasRenderer = function (renderer, src, camera, parentMatrix)
dy -= cd.height; dy -= cd.height;
} }
ctx.save(); if (cd.width > 0 && cd.height > 0)
ctx.translate(bob.x + cameraScrollX, bob.y + cameraScrollY); {
ctx.scale(fx, fy); ctx.save();
ctx.drawImage(frame.source.image, cd.x, cd.y, cd.width, cd.height, dx, dy, cd.width, cd.height); ctx.translate(bob.x + cameraScrollX, bob.y + cameraScrollY);
ctx.restore(); ctx.scale(fx, fy);
ctx.drawImage(frame.source.image, cd.x, cd.y, cd.width, cd.height, dx, dy, cd.width, cd.height);
ctx.restore();
}
} }
} }

View file

@ -498,7 +498,8 @@ var Container = new Class({
} }
else else
{ {
output = new Vector2(source.x, source.y); output.x = source.x;
output.y = source.y;
} }
var tempMatrix = this.tempTransformMatrix; var tempMatrix = this.tempTransformMatrix;

View file

@ -1064,8 +1064,74 @@ var Mesh = new Class({
this.debugCallback = null; this.debugCallback = null;
this.debugGraphic = null; this.debugGraphic = null;
} },
/**
* Clears all tint values associated with this Game Object.
*
* Immediately sets the color values back to 0xffffff on all vertices,
* which results in no visible change to the texture.
*
* @method Phaser.GameObjects.Mesh#clearTint
* @webglOnly
* @since 3.60.0
*
* @return {this} This Game Object instance.
*/
clearTint: function ()
{
return this.setTint();
},
/**
* Sets an additive tint on all vertices of this Mesh Game Object.
*
* The tint works by taking the pixel color values from the Game Objects texture, and then
* multiplying it by the color value of the tint.
*
* To modify the tint color once set, either call this method again with new values or use the
* `tint` property to set all colors at once.
*
* To remove a tint call `clearTint`.
*
* @method Phaser.GameObjects.Mesh#setTint
* @webglOnly
* @since 3.60.0
*
* @param {number} [tint=0xffffff] - The tint being applied to all vertices of this Mesh Game Object.
*
* @return {this} This Game Object instance.
*/
setTint: function (tint)
{
if (tint === undefined) { tint = 0xffffff; }
var vertices = this.vertices;
for (var i = 0; i < vertices.length; i++)
{
vertices[i].color = tint;
}
return this;
},
/**
* The tint value being applied to the whole of the Game Object.
* This property is a setter-only.
*
* @method Phaser.GameObjects.Mesh#tint
* @type {number}
* @webglOnly
* @since 3.60.0
*/
tint: {
set: function (value)
{
this.setTint(value);
}
}
}); });
module.exports = Mesh; module.exports = Mesh;

View file

@ -103,26 +103,29 @@ var ParticleManagerCanvasRenderer = function (renderer, emitterManager, camera,
var frame = particle.frame; var frame = particle.frame;
var cd = frame.canvasData; var cd = frame.canvasData;
var x = -(frame.halfWidth); if (cd.width > 0 && cd.height > 0)
var y = -(frame.halfHeight);
ctx.globalAlpha = alpha;
ctx.save();
calcMatrix.setToContext(ctx);
if (roundPixels)
{ {
x = Math.round(x); var x = -(frame.halfWidth);
y = Math.round(y); var y = -(frame.halfHeight);
ctx.globalAlpha = alpha;
ctx.save();
calcMatrix.setToContext(ctx);
if (roundPixels)
{
x = Math.round(x);
y = Math.round(y);
}
ctx.imageSmoothingEnabled = !(!renderer.antialias || frame.source.scaleMode);
ctx.drawImage(frame.source.image, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height);
ctx.restore();
} }
ctx.imageSmoothingEnabled = !(!renderer.antialias || frame.source.scaleMode);
ctx.drawImage(frame.source.image, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height);
ctx.restore();
} }
ctx.restore(); ctx.restore();

View file

@ -664,7 +664,10 @@ var RenderTexture = new Class({
* * A Texture Frame instance. * * A Texture Frame instance.
* * A string. This is used to look-up a texture from the Texture Manager. * * A string. This is used to look-up a texture from the Texture Manager.
* *
* Note: You cannot draw a Render Texture to itself. * Note 1: You cannot draw a Render Texture to itself.
*
* Note 2: For Game Objects that have Post FX Pipelines, the pipeline _cannot_ be
* used when drawn to this Render Texture.
* *
* If passing in a Group or Container it will only draw children that return `true` * If passing in a Group or Container it will only draw children that return `true`
* when their `willRender()` method is called. I.e. a Container with 10 children, * when their `willRender()` method is called. I.e. a Container with 10 children,
@ -1279,7 +1282,10 @@ var RenderTexture = new Class({
matrix.setToContext(ctx); matrix.setToContext(ctx);
ctx.drawImage(source, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height); if (cd.width > 0 && cd.height > 0)
{
ctx.drawImage(source, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height);
}
ctx.restore(); ctx.restore();
} }

View file

@ -56,7 +56,10 @@ var PolygonCanvasRenderer = function (renderer, src, camera, parentMatrix)
ctx.lineTo(px2, py2); ctx.lineTo(px2, py2);
} }
ctx.closePath(); if (src.closePath)
{
ctx.closePath();
}
if (src.isFilled) if (src.isFilled)
{ {

View file

@ -346,7 +346,7 @@ var Sprite = new Class({
* @method Phaser.GameObjects.Sprite#chain * @method Phaser.GameObjects.Sprite#chain
* @since 3.50.0 * @since 3.50.0
* *
* @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig|string[]|Phaser.Animations.Animation[]|Phaser.Types.Animations.PlayAnimationConfig[])} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object, or an array of them. * @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig|string[]|Phaser.Animations.Animation[]|Phaser.Types.Animations.PlayAnimationConfig[])} [key] - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object, or an array of them.
* *
* @return {this} This Game Object. * @return {this} This Game Object.
*/ */

View file

@ -8,5 +8,5 @@
* @property {number} [width=512] - The width of the Tile Sprite. If zero it will use the size of the texture frame. * @property {number} [width=512] - The width of the Tile Sprite. If zero it will use the size of the texture frame.
* @property {number} [height=512] - The height of the Tile Sprite. If zero it will use the size of the texture frame. * @property {number} [height=512] - The height of the Tile Sprite. If zero it will use the size of the texture frame.
* @property {string} [key=''] - The key of the Texture this Tile Sprite will use to render with, as stored in the Texture Manager. * @property {string} [key=''] - The key of the Texture this Tile Sprite will use to render with, as stored in the Texture Manager.
* @property {string} [frame=''] - An optional frame from the Texture this Tile Sprite is rendering with. * @property {(number|string|Phaser.Textures.Frame)} [frame=''] - An optional frame from the Texture this Tile Sprite is rendering with.
*/ */

View file

@ -158,13 +158,21 @@ var HTML5AudioFile = new Class({
var audio = new Audio(); var audio = new Audio();
var dataset = audio.dataset; var dataset = audio.dataset;
if (dataset === undefined)
{
audio.dataset = dataset = {
name: '',
used: ''
};
}
dataset.name = this.key + '-' + i.toString(); dataset.name = this.key + '-' + i.toString();
dataset.used = 'false'; dataset.used = 'false';
if (this.locked) if (this.locked)
{ {
dataset.locked = 'true'; dataset.locked = 'true';
console.log('HTML5AudioFile:', dataset.name, 'locked'); // console.log('HTML5AudioFile:', dataset.name, 'locked');
} }
else else
{ {
@ -174,7 +182,7 @@ var HTML5AudioFile = new Class({
audio.oncanplaythrough = this.onProgress.bind(this); audio.oncanplaythrough = this.onProgress.bind(this);
audio.onerror = this.onError.bind(this); audio.onerror = this.onError.bind(this);
console.log('HTML5AudioFile:', dataset.name, 'unlocked'); // console.log('HTML5AudioFile:', dataset.name, 'unlocked');
} }
this.data.push(audio); this.data.push(audio);
@ -189,7 +197,7 @@ var HTML5AudioFile = new Class({
if (!this.locked) if (!this.locked)
{ {
audio.load(); audio.load();
console.log('HTML5AudioFile:', dataset.name, 'load called'); // console.log('HTML5AudioFile:', dataset.name, 'load called');
} }
} }

View file

@ -1346,14 +1346,12 @@ var World = new Class({
* @param {ArcadePhysicsCallback} [processCallback] - The process callback. * @param {ArcadePhysicsCallback} [processCallback] - The process callback.
* @param {*} [callbackContext] - The context in which to invoke the callback. * @param {*} [callbackContext] - The context in which to invoke the callback.
* @param {boolean} [overlapOnly] - If this a collide or overlap check? * @param {boolean} [overlapOnly] - If this a collide or overlap check?
* @param {boolean} [intersects] - Assert that the bodies intersect and should not be tested before separation.
* *
* @return {boolean} True if separation occurred, otherwise false. * @return {boolean} True if separation occurred, otherwise false.
*/ */
separate: function (body1, body2, processCallback, callbackContext, overlapOnly, intersects) separate: function (body1, body2, processCallback, callbackContext, overlapOnly)
{ {
if ( if (
!intersects &&
!body1.enable || !body1.enable ||
!body2.enable || !body2.enable ||
body1.checkCollision.none || body1.checkCollision.none ||
@ -2002,7 +2000,7 @@ var World = new Class({
continue; continue;
} }
if (this.separate(bodyA, bodyB, processCallback, callbackContext, overlapOnly, true)) if (this.separate(bodyA, bodyB, processCallback, callbackContext, overlapOnly))
{ {
if (collideCallback) if (collideCallback)
{ {

View file

@ -11,6 +11,7 @@ var Body = require('./lib/body/Body');
var BodyBounds = require('./BodyBounds'); var BodyBounds = require('./BodyBounds');
var Bounds = require('./lib/geometry/Bounds'); var Bounds = require('./lib/geometry/Bounds');
var Class = require('../../utils/Class'); var Class = require('../../utils/Class');
var Collision = require('./lib/collision/Collision');
var Composite = require('./lib/body/Composite'); var Composite = require('./lib/body/Composite');
var Composites = require('./lib/factory/Composites'); var Composites = require('./lib/factory/Composites');
var Constraint = require('./lib/constraint/Constraint'); var Constraint = require('./lib/constraint/Constraint');
@ -41,10 +42,10 @@ var World = require('./World');
/** /**
* @classdesc * @classdesc
* The Phaser Matter plugin provides the ability to use the Matter JS Physics Engine within your Phaser games. * The Phaser Matter plugin provides the ability to use the Matter JS Physics Engine within your Phaser games.
* *
* Unlike Arcade Physics, the other physics system provided with Phaser, Matter JS is a full-body physics system. * Unlike Arcade Physics, the other physics system provided with Phaser, Matter JS is a full-body physics system.
* It features: * It features:
* *
* * Rigid bodies * * Rigid bodies
* * Compound bodies * * Compound bodies
* * Composite bodies * * Composite bodies
@ -62,10 +63,10 @@ var World = require('./World');
* * Views (translate, zoom) * * Views (translate, zoom)
* * Collision queries (raycasting, region tests) * * Collision queries (raycasting, region tests)
* * Time scaling (slow-mo, speed-up) * * Time scaling (slow-mo, speed-up)
* *
* Configuration of Matter is handled via the Matter World Config object, which can be passed in either the * Configuration of Matter is handled via the Matter World Config object, which can be passed in either the
* Phaser Game Config, or Phaser Scene Config. Here is a basic example: * Phaser Game Config, or Phaser Scene Config. Here is a basic example:
* *
* ```js * ```js
* physics: { * physics: {
* default: 'matter', * default: 'matter',
@ -81,11 +82,11 @@ var World = require('./World');
* } * }
* } * }
* ``` * ```
* *
* This class acts as an interface between a Phaser Scene and a single instance of the Matter Engine. * This class acts as an interface between a Phaser Scene and a single instance of the Matter Engine.
* *
* Use it to access the most common Matter features and helper functions. * Use it to access the most common Matter features and helper functions.
* *
* You can find details, documentation and examples on the Matter JS website: https://brm.io/matter-js/ * You can find details, documentation and examples on the Matter JS website: https://brm.io/matter-js/
* *
* @class MatterPhysics * @class MatterPhysics
@ -141,7 +142,7 @@ var MatterPhysics = new Class({
/** /**
* An instance of the Matter Factory. This class provides lots of functions for creating a * An instance of the Matter Factory. This class provides lots of functions for creating a
* wide variety of physics objects and adds them automatically to the Matter World. * wide variety of physics objects and adds them automatically to the Matter World.
* *
* You can use this class to cut-down on the amount of code required in your game, however, * You can use this class to cut-down on the amount of code required in your game, however,
* use of the Factory is entirely optional and should be seen as a development aid. It's * use of the Factory is entirely optional and should be seen as a development aid. It's
* perfectly possible to create and add components to the Matter world without using it. * perfectly possible to create and add components to the Matter world without using it.
@ -166,7 +167,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Body` module. * A reference to the `Matter.Body` module.
* *
* The `Matter.Body` module contains methods for creating and manipulating body models. * The `Matter.Body` module contains methods for creating and manipulating body models.
* A `Matter.Body` is a rigid body that can be simulated by a `Matter.Engine`. * A `Matter.Body` is a rigid body that can be simulated by a `Matter.Engine`.
* Factories for commonly used body configurations (such as rectangles, circles and other polygons) can be found in the `Bodies` module. * Factories for commonly used body configurations (such as rectangles, circles and other polygons) can be found in the `Bodies` module.
@ -179,7 +180,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Composite` module. * A reference to the `Matter.Composite` module.
* *
* The `Matter.Composite` module contains methods for creating and manipulating composite bodies. * The `Matter.Composite` module contains methods for creating and manipulating composite bodies.
* A composite body is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite`, therefore composites form a tree structure. * 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. * It is important to use the functions in this module to modify composites, rather than directly modifying their properties.
@ -193,9 +194,22 @@ var MatterPhysics = new Class({
// Collision: // Collision:
/**
* A reference to the `Matter.Collision` module.
*
* 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`.
*
* @name Phaser.Physics.Matter.MatterPhysics#collision
* @type {MatterJS.Collision}
* @since 3.60.0
*/
this.collision = Collision;
/** /**
* A reference to the `Matter.Detector` module. * A reference to the `Matter.Detector` module.
* *
* The `Matter.Detector` module contains methods for detecting collisions given a set of pairs. * The `Matter.Detector` module contains methods for detecting collisions given a set of pairs.
* *
* @name Phaser.Physics.Matter.MatterPhysics#detector * @name Phaser.Physics.Matter.MatterPhysics#detector
@ -206,7 +220,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Grid` module. * A reference to the `Matter.Grid` module.
* *
* 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.
* *
* @name Phaser.Physics.Matter.MatterPhysics#grid * @name Phaser.Physics.Matter.MatterPhysics#grid
@ -217,7 +231,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Pair` module. * A reference to the `Matter.Pair` module.
* *
* The `Matter.Pair` module contains methods for creating and manipulating collision pairs. * The `Matter.Pair` module contains methods for creating and manipulating collision pairs.
* *
* @name Phaser.Physics.Matter.MatterPhysics#pair * @name Phaser.Physics.Matter.MatterPhysics#pair
@ -228,7 +242,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Pairs` module. * A reference to the `Matter.Pairs` module.
* *
* The `Matter.Pairs` module contains methods for creating and manipulating collision pair sets. * The `Matter.Pairs` module contains methods for creating and manipulating collision pair sets.
* *
* @name Phaser.Physics.Matter.MatterPhysics#pairs * @name Phaser.Physics.Matter.MatterPhysics#pairs
@ -239,7 +253,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Query` module. * A reference to the `Matter.Query` module.
* *
* The `Matter.Query` module contains methods for performing collision queries. * The `Matter.Query` module contains methods for performing collision queries.
* *
* @name Phaser.Physics.Matter.MatterPhysics#query * @name Phaser.Physics.Matter.MatterPhysics#query
@ -250,7 +264,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Resolver` module. * A reference to the `Matter.Resolver` module.
* *
* The `Matter.Resolver` module contains methods for resolving collision pairs. * The `Matter.Resolver` module contains methods for resolving collision pairs.
* *
* @name Phaser.Physics.Matter.MatterPhysics#resolver * @name Phaser.Physics.Matter.MatterPhysics#resolver
@ -261,7 +275,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.SAT` module. * A reference to the `Matter.SAT` module.
* *
* 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.
* *
* @name Phaser.Physics.Matter.MatterPhysics#sat * @name Phaser.Physics.Matter.MatterPhysics#sat
@ -274,7 +288,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Constraint` module. * A reference to the `Matter.Constraint` module.
* *
* The `Matter.Constraint` module contains methods for creating and manipulating constraints. * The `Matter.Constraint` module contains methods for creating and manipulating constraints.
* Constraints are used for specifying that a fixed distance must be maintained between two bodies (or a body and a fixed world-space position). * Constraints are used for specifying that a fixed distance must be maintained between two bodies (or a body and a fixed world-space position).
* The stiffness of constraints can be modified to create springs or elastic. * The stiffness of constraints can be modified to create springs or elastic.
@ -289,7 +303,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Bodies` module. * A reference to the `Matter.Bodies` module.
* *
* The `Matter.Bodies` module contains factory methods for creating rigid bodies * The `Matter.Bodies` module contains factory methods for creating rigid bodies
* with commonly used body configurations (such as rectangles, circles and other polygons). * with commonly used body configurations (such as rectangles, circles and other polygons).
* *
@ -301,7 +315,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Composites` module. * A reference to the `Matter.Composites` module.
* *
* The `Matter.Composites` module contains factory methods for creating composite bodies * The `Matter.Composites` module contains factory methods for creating composite bodies
* with commonly used configurations (such as stacks and chains). * with commonly used configurations (such as stacks and chains).
* *
@ -315,7 +329,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Axes` module. * A reference to the `Matter.Axes` module.
* *
* The `Matter.Axes` module contains methods for creating and manipulating sets of axes. * The `Matter.Axes` module contains methods for creating and manipulating sets of axes.
* *
* @name Phaser.Physics.Matter.MatterPhysics#axes * @name Phaser.Physics.Matter.MatterPhysics#axes
@ -326,7 +340,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Bounds` module. * A reference to the `Matter.Bounds` module.
* *
* The `Matter.Bounds` module contains methods for creating and manipulating axis-aligned bounding boxes (AABB). * The `Matter.Bounds` module contains methods for creating and manipulating axis-aligned bounding boxes (AABB).
* *
* @name Phaser.Physics.Matter.MatterPhysics#bounds * @name Phaser.Physics.Matter.MatterPhysics#bounds
@ -337,7 +351,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Svg` module. * A reference to the `Matter.Svg` module.
* *
* The `Matter.Svg` module contains methods for converting SVG images into an array of vector points. * The `Matter.Svg` module contains methods for converting SVG images into an array of vector points.
* *
* To use this module you also need the SVGPathSeg polyfill: https://github.com/progers/pathseg * To use this module you also need the SVGPathSeg polyfill: https://github.com/progers/pathseg
@ -350,7 +364,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Vector` module. * A reference to the `Matter.Vector` module.
* *
* The `Matter.Vector` module contains methods for creating and manipulating vectors. * The `Matter.Vector` module contains methods for creating and manipulating vectors.
* Vectors are the basis of all the geometry related operations in the engine. * Vectors are the basis of all the geometry related operations in the engine.
* A `Matter.Vector` object is of the form `{ x: 0, y: 0 }`. * A `Matter.Vector` object is of the form `{ x: 0, y: 0 }`.
@ -363,7 +377,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Vertices` module. * A reference to the `Matter.Vertices` module.
* *
* The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices. * The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices.
* A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`. * A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`.
* A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull). * A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull).
@ -376,7 +390,7 @@ var MatterPhysics = new Class({
/** /**
* A reference to the `Matter.Vertices` module. * A reference to the `Matter.Vertices` module.
* *
* The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices. * The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices.
* A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`. * A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`.
* A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull). * A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull).
@ -488,18 +502,18 @@ var MatterPhysics = new Class({
/** /**
* Enables the Matter Attractors Plugin. * Enables the Matter Attractors Plugin.
* *
* The attractors plugin that makes it easy to apply continual forces on bodies. * The attractors plugin that makes it easy to apply continual forces on bodies.
* It's possible to simulate effects such as wind, gravity and magnetism. * It's possible to simulate effects such as wind, gravity and magnetism.
* *
* https://github.com/liabru/matter-attractors * https://github.com/liabru/matter-attractors
* *
* This method is called automatically if `plugins.attractors` is set in the Matter World Config. * This method is called automatically if `plugins.attractors` is set in the Matter World Config.
* However, you can also call it directly from within your game. * However, you can also call it directly from within your game.
* *
* @method Phaser.Physics.Matter.MatterPhysics#enableAttractorPlugin * @method Phaser.Physics.Matter.MatterPhysics#enableAttractorPlugin
* @since 3.0.0 * @since 3.0.0
* *
* @return {this} This Matter Physics instance. * @return {this} This Matter Physics instance.
*/ */
enableAttractorPlugin: function () enableAttractorPlugin: function ()
@ -512,19 +526,19 @@ var MatterPhysics = new Class({
/** /**
* Enables the Matter Wrap Plugin. * Enables the Matter Wrap Plugin.
* *
* The coordinate wrapping plugin that automatically wraps the position of bodies such that they always stay * The coordinate wrapping plugin that automatically wraps the position of bodies such that they always stay
* within the given bounds. Upon crossing a boundary the body will appear on the opposite side of the bounds, * within the given bounds. Upon crossing a boundary the body will appear on the opposite side of the bounds,
* while maintaining its velocity. * while maintaining its velocity.
* *
* https://github.com/liabru/matter-wrap * https://github.com/liabru/matter-wrap
* *
* This method is called automatically if `plugins.wrap` is set in the Matter World Config. * This method is called automatically if `plugins.wrap` is set in the Matter World Config.
* However, you can also call it directly from within your game. * However, you can also call it directly from within your game.
* *
* @method Phaser.Physics.Matter.MatterPhysics#enableWrapPlugin * @method Phaser.Physics.Matter.MatterPhysics#enableWrapPlugin
* @since 3.0.0 * @since 3.0.0
* *
* @return {this} This Matter Physics instance. * @return {this} This Matter Physics instance.
*/ */
enableWrapPlugin: function () enableWrapPlugin: function ()
@ -537,33 +551,33 @@ var MatterPhysics = new Class({
/** /**
* Enables the Matter Collision Events Plugin. * Enables the Matter Collision Events Plugin.
* *
* Note that this plugin is enabled by default. So you should only ever need to call this * Note that this plugin is enabled by default. So you should only ever need to call this
* method if you have specifically disabled the plugin in your Matter World Config. * method if you have specifically disabled the plugin in your Matter World Config.
* You can disable it by setting `plugins.collisionevents: false` in your Matter World Config. * You can disable it by setting `plugins.collisionevents: false` in your Matter World Config.
* *
* This plugin triggers three new events on Matter.Body: * This plugin triggers three new events on Matter.Body:
* *
* 1. `onCollide` * 1. `onCollide`
* 2. `onCollideEnd` * 2. `onCollideEnd`
* 3. `onCollideActive` * 3. `onCollideActive`
* *
* These events correspond to the Matter.js events `collisionStart`, `collisionActive` and `collisionEnd`, respectively. * These events correspond to the Matter.js events `collisionStart`, `collisionActive` and `collisionEnd`, respectively.
* You can listen to these events via Matter.Events or they will also be emitted from the Matter World. * You can listen to these events via Matter.Events or they will also be emitted from the Matter World.
* *
* This plugin also extends Matter.Body with three convenience functions: * This plugin also extends Matter.Body with three convenience functions:
* *
* `Matter.Body.setOnCollide(callback)` * `Matter.Body.setOnCollide(callback)`
* `Matter.Body.setOnCollideEnd(callback)` * `Matter.Body.setOnCollideEnd(callback)`
* `Matter.Body.setOnCollideActive(callback)` * `Matter.Body.setOnCollideActive(callback)`
* *
* You can register event callbacks by providing a function of type (pair: Matter.Pair) => void * You can register event callbacks by providing a function of type (pair: Matter.Pair) => void
* *
* https://github.com/dxu/matter-collision-events * https://github.com/dxu/matter-collision-events
* *
* @method Phaser.Physics.Matter.MatterPhysics#enableCollisionEventsPlugin * @method Phaser.Physics.Matter.MatterPhysics#enableCollisionEventsPlugin
* @since 3.22.0 * @since 3.22.0
* *
* @return {this} This Matter Physics instance. * @return {this} This Matter Physics instance.
*/ */
enableCollisionEventsPlugin: function () enableCollisionEventsPlugin: function ()
@ -576,7 +590,7 @@ var MatterPhysics = new Class({
/** /**
* Pauses the Matter World instance and sets `enabled` to `false`. * Pauses the Matter World instance and sets `enabled` to `false`.
* *
* A paused world will not run any simulations for the duration it is paused. * A paused world will not run any simulations for the duration it is paused.
* *
* @method Phaser.Physics.Matter.MatterPhysics#pause * @method Phaser.Physics.Matter.MatterPhysics#pause
@ -639,12 +653,12 @@ var MatterPhysics = new Class({
/** /**
* Manually advances the physics simulation by one iteration. * Manually advances the physics simulation by one iteration.
* *
* You can optionally pass in the `delta` and `correction` values to be used by Engine.update. * You can optionally pass in the `delta` and `correction` values to be used by Engine.update.
* If undefined they use the Matter defaults of 60Hz and no correction. * If undefined they use the Matter defaults of 60Hz and no correction.
* *
* Calling `step` directly bypasses any checks of `enabled` or `autoUpdate`. * Calling `step` directly bypasses any checks of `enabled` or `autoUpdate`.
* *
* It also ignores any custom `getDelta` functions, as you should be passing the delta * It also ignores any custom `getDelta` functions, as you should be passing the delta
* value in to this call. * value in to this call.
* *
@ -671,22 +685,22 @@ var MatterPhysics = new Class({
/** /**
* Checks if the vertices of the given body, or an array of bodies, contains the given point, or not. * Checks if the vertices of the given body, or an array of bodies, contains the given point, or not.
* *
* You can pass in either a single body, or an array of bodies to be checked. This method will * You can pass in either a single body, or an array of bodies to be checked. This method will
* return `true` if _any_ of the bodies in the array contain the point. See the `intersectPoint` method if you need * return `true` if _any_ of the bodies in the array contain the point. See the `intersectPoint` method if you need
* to get a list of intersecting bodies. * to get a list of intersecting bodies.
* *
* The point should be transformed into the Matter World coordinate system in advance. This happens by * The point should be transformed into the Matter World coordinate system in advance. This happens by
* default with Input Pointers, but if you wish to use points from another system you may need to * default with Input Pointers, but if you wish to use points from another system you may need to
* transform them before passing them. * transform them before passing them.
* *
* @method Phaser.Physics.Matter.MatterPhysics#containsPoint * @method Phaser.Physics.Matter.MatterPhysics#containsPoint
* @since 3.22.0 * @since 3.22.0
* *
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} body - The body, or an array of bodies, to check against the point. * @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} body - The body, or an array of bodies, to check against the point.
* @param {number} x - The horizontal coordinate of the point. * @param {number} x - The horizontal coordinate of the point.
* @param {number} y - The vertical coordinate of the point. * @param {number} y - The vertical coordinate of the point.
* *
* @return {boolean} `true` if the point is within one of the bodies given, otherwise `false`. * @return {boolean} `true` if the point is within one of the bodies given, otherwise `false`.
*/ */
containsPoint: function (body, x, y) containsPoint: function (body, x, y)
@ -702,20 +716,20 @@ var MatterPhysics = new Class({
/** /**
* Checks the given coordinates to see if any vertices of the given bodies contain it. * Checks the given coordinates to see if any vertices of the given bodies contain it.
* *
* If no bodies are provided it will search all bodies in the Matter World, including within Composites. * If no bodies are provided it will search all bodies in the Matter World, including within Composites.
* *
* The coordinates should be transformed into the Matter World coordinate system in advance. This happens by * The coordinates should be transformed into the Matter World coordinate system in advance. This happens by
* default with Input Pointers, but if you wish to use coordinates from another system you may need to * default with Input Pointers, but if you wish to use coordinates from another system you may need to
* transform them before passing them. * transform them before passing them.
* *
* @method Phaser.Physics.Matter.MatterPhysics#intersectPoint * @method Phaser.Physics.Matter.MatterPhysics#intersectPoint
* @since 3.22.0 * @since 3.22.0
* *
* @param {number} x - The horizontal coordinate of the point. * @param {number} x - The horizontal coordinate of the point.
* @param {number} y - The vertical coordinate of the point. * @param {number} y - The vertical coordinate of the point.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world. * @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world.
* *
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies which contain the given point. * @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies which contain the given point.
*/ */
intersectPoint: function (x, y, bodies) intersectPoint: function (x, y, bodies)
@ -743,9 +757,9 @@ var MatterPhysics = new Class({
* Checks the given rectangular area to see if any vertices of the given bodies intersect with it. * Checks the given rectangular area to see if any vertices of the given bodies intersect with it.
* Or, if the `outside` parameter is set to `true`, it checks to see which bodies do not * Or, if the `outside` parameter is set to `true`, it checks to see which bodies do not
* intersect with it. * intersect with it.
* *
* If no bodies are provided it will search all bodies in the Matter World, including within Composites. * If no bodies are provided it will search all bodies in the Matter World, including within Composites.
* *
* @method Phaser.Physics.Matter.MatterPhysics#intersectRect * @method Phaser.Physics.Matter.MatterPhysics#intersectRect
* @since 3.22.0 * @since 3.22.0
* *
@ -755,7 +769,7 @@ var MatterPhysics = new Class({
* @param {number} height - The height of the area. * @param {number} height - The height of the area.
* @param {boolean} [outside=false] - If `false` it checks for vertices inside the area, if `true` it checks for vertices outside the area. * @param {boolean} [outside=false] - If `false` it checks for vertices inside the area, if `true` it checks for vertices outside the area.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world. * @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world.
* *
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies that intersect with the given area. * @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies that intersect with the given area.
*/ */
intersectRect: function (x, y, width, height, outside, bodies) intersectRect: function (x, y, width, height, outside, bodies)
@ -786,11 +800,11 @@ var MatterPhysics = new Class({
/** /**
* Checks the given ray segment to see if any vertices of the given bodies intersect with it. * Checks the given ray segment to see if any vertices of the given bodies intersect with it.
* *
* If no bodies are provided it will search all bodies in the Matter World. * If no bodies are provided it will search all bodies in the Matter World.
* *
* The width of the ray can be specified via the `rayWidth` parameter. * The width of the ray can be specified via the `rayWidth` parameter.
* *
* @method Phaser.Physics.Matter.MatterPhysics#intersectRay * @method Phaser.Physics.Matter.MatterPhysics#intersectRay
* @since 3.22.0 * @since 3.22.0
* *
@ -800,13 +814,13 @@ var MatterPhysics = new Class({
* @param {number} y2 - The vertical coordinate of the end of the ray segment. * @param {number} y2 - The vertical coordinate of the end of the ray segment.
* @param {number} [rayWidth=1] - The width of the ray segment. * @param {number} [rayWidth=1] - The width of the ray segment.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world. * @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world.
* *
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies whos vertices intersect with the ray segment. * @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies whos vertices intersect with the ray segment.
*/ */
intersectRay: function (x1, y1, x2, y2, rayWidth, bodies) intersectRay: function (x1, y1, x2, y2, rayWidth, bodies)
{ {
if (rayWidth === undefined) { rayWidth = 1; } if (rayWidth === undefined) { rayWidth = 1; }
bodies = this.getMatterBodies(bodies); bodies = this.getMatterBodies(bodies);
var result = []; var result = [];
@ -822,15 +836,15 @@ var MatterPhysics = new Class({
/** /**
* Checks the given Matter Body to see if it intersects with any of the given bodies. * Checks the given Matter Body to see if it intersects with any of the given bodies.
* *
* If no bodies are provided it will check against all bodies in the Matter World. * If no bodies are provided it will check against all bodies in the Matter World.
* *
* @method Phaser.Physics.Matter.MatterPhysics#intersectBody * @method Phaser.Physics.Matter.MatterPhysics#intersectBody
* @since 3.22.0 * @since 3.22.0
* *
* @param {Phaser.Types.Physics.Matter.MatterBody} body - The target body. * @param {Phaser.Types.Physics.Matter.MatterBody} body - The target body.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check the target body against. If not provided it will search all bodies in the world. * @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check the target body against. If not provided it will search all bodies in the world.
* *
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies whos vertices intersect with target body. * @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies whos vertices intersect with target body.
*/ */
intersectBody: function (body, bodies) intersectBody: function (body, bodies)
@ -859,22 +873,22 @@ var MatterPhysics = new Class({
/** /**
* Checks to see if the target body, or an array of target bodies, intersects with any of the given bodies. * Checks to see if the target body, or an array of target bodies, intersects with any of the given bodies.
* *
* If intersection occurs this method will return `true` and, if provided, invoke the callbacks. * If intersection occurs this method will return `true` and, if provided, invoke the callbacks.
* *
* If no bodies are provided for the second parameter the target will check again all bodies in the Matter World. * If no bodies are provided for the second parameter the target will check again all bodies in the Matter World.
* *
* Note that bodies can only overlap if they are in non-colliding collision groups or categories. * Note that bodies can only overlap if they are in non-colliding collision groups or categories.
* *
* If you provide a `processCallback` then the two bodies that overlap are sent to it. This callback * If you provide a `processCallback` then the two bodies that overlap are sent to it. This callback
* must return a boolean and is used to allow you to perform additional processing tests before a final * must return a boolean and is used to allow you to perform additional processing tests before a final
* outcome is decided. If it returns `true` then the bodies are finally passed to the `overlapCallback`, if set. * outcome is decided. If it returns `true` then the bodies are finally passed to the `overlapCallback`, if set.
* *
* If you provide an `overlapCallback` then the matching pairs of overlapping bodies will be sent to it. * If you provide an `overlapCallback` then the matching pairs of overlapping bodies will be sent to it.
* *
* Both callbacks have the following signature: `function (bodyA, bodyB, collisionInfo)` where `bodyA` is always * Both callbacks have the following signature: `function (bodyA, bodyB, collisionInfo)` where `bodyA` is always
* the target body. The `collisionInfo` object contains additional data, such as the angle and depth of penetration. * the target body. The `collisionInfo` object contains additional data, such as the angle and depth of penetration.
* *
* @method Phaser.Physics.Matter.MatterPhysics#overlap * @method Phaser.Physics.Matter.MatterPhysics#overlap
* @since 3.22.0 * @since 3.22.0
* *
@ -883,7 +897,7 @@ var MatterPhysics = new Class({
* @param {ArcadePhysicsCallback} [overlapCallback] - An optional callback function that is called if the bodies overlap. * @param {ArcadePhysicsCallback} [overlapCallback] - An optional callback function that is called if the bodies overlap.
* @param {ArcadePhysicsCallback} [processCallback] - An optional callback function that lets you perform additional checks against the two bodies if they overlap. If this is set then `overlapCallback` will only be invoked if this callback returns `true`. * @param {ArcadePhysicsCallback} [processCallback] - An optional callback function that lets you perform additional checks against the two bodies if they overlap. If this is set then `overlapCallback` will only be invoked if this callback returns `true`.
* @param {*} [callbackContext] - The context, or scope, in which to run the callbacks. * @param {*} [callbackContext] - The context, or scope, in which to run the callbacks.
* *
* @return {boolean} `true` if the target body intersects with _any_ of the bodies given, otherwise `false`. * @return {boolean} `true` if the target body intersects with _any_ of the bodies given, otherwise `false`.
*/ */
overlap: function (target, bodies, overlapCallback, processCallback, callbackContext) overlap: function (target, bodies, overlapCallback, processCallback, callbackContext)
@ -935,9 +949,9 @@ var MatterPhysics = new Class({
/** /**
* Sets the collision filter category of all given Matter Bodies to the given value. * Sets the collision filter category of all given Matter Bodies to the given value.
* *
* This number must be a power of two between 2^0 (= 1) and 2^31. * This number must be a power of two between 2^0 (= 1) and 2^31.
* *
* Bodies with different collision groups (see {@link #setCollisionGroup}) will only collide if their collision * Bodies with different collision groups (see {@link #setCollisionGroup}) will only collide if their collision
* categories are included in their collision masks (see {@link #setCollidesWith}). * categories are included in their collision masks (see {@link #setCollidesWith}).
* *
@ -963,10 +977,10 @@ var MatterPhysics = new Class({
/** /**
* Sets the collision filter group of all given Matter Bodies to the given value. * Sets the collision filter group of all given Matter Bodies to the given value.
* *
* If the group value is zero, or if two Matter Bodies have different group values, * If the group value is zero, or if two Matter Bodies have different group values,
* they will collide according to the usual collision filter rules (see {@link #setCollisionCategory} and {@link #setCollisionGroup}). * they will collide according to the usual collision filter rules (see {@link #setCollisionCategory} and {@link #setCollisionGroup}).
* *
* If two Matter Bodies have the same positive group value, they will always collide; * If two Matter Bodies have the same positive group value, they will always collide;
* if they have the same negative group value they will never collide. * if they have the same negative group value they will never collide.
* *
@ -992,9 +1006,9 @@ var MatterPhysics = new Class({
/** /**
* Sets the collision filter mask of all given Matter Bodies to the given value. * Sets the collision filter mask of all given Matter Bodies to the given value.
* *
* Two Matter Bodies with different collision groups will only collide if each one includes the others * Two Matter Bodies with different collision groups will only collide if each one includes the others
* category in its mask based on a bitwise AND operation: `(categoryA & maskB) !== 0` and * category in its mask based on a bitwise AND operation: `(categoryA & maskB) !== 0` and
* `(categoryB & maskA) !== 0` are both true. * `(categoryB & maskA) !== 0` are both true.
* *
* @method Phaser.Physics.Matter.MatterPhysics#setCollidesWith * @method Phaser.Physics.Matter.MatterPhysics#setCollidesWith
@ -1033,10 +1047,10 @@ var MatterPhysics = new Class({
/** /**
* Takes an array and returns a new array made from all of the Matter Bodies found in the original array. * Takes an array and returns a new array made from all of the Matter Bodies found in the original array.
* *
* For example, passing in Matter Game Objects, such as a bunch of Matter Sprites, to this method, would * For example, passing in Matter Game Objects, such as a bunch of Matter Sprites, to this method, would
* return an array containing all of their native Matter Body objects. * return an array containing all of their native Matter Body objects.
* *
* If the `bodies` argument is falsey, it will return all bodies in the world. * If the `bodies` argument is falsey, it will return all bodies in the world.
* *
* @method Phaser.Physics.Matter.MatterPhysics#getMatterBodies * @method Phaser.Physics.Matter.MatterPhysics#getMatterBodies
@ -1212,7 +1226,7 @@ var MatterPhysics = new Class({
/** /**
* Applies a force to a body, from the given world position, including resulting torque. * Applies a force to a body, from the given world position, including resulting torque.
* If no angle is given, the current body angle is used. * If no angle is given, the current body angle is used.
* *
* Use very small speed values, such as 0.1, depending on the mass and required velocity. * Use very small speed values, such as 0.1, depending on the mass and required velocity.
* *
* @method Phaser.Physics.Matter.MatterPhysics#applyForceFromPosition * @method Phaser.Physics.Matter.MatterPhysics#applyForceFromPosition
@ -1250,7 +1264,7 @@ var MatterPhysics = new Class({
/** /**
* Apply a force to a body based on the given angle and speed. * Apply a force to a body based on the given angle and speed.
* If no angle is given, the current body angle is used. * If no angle is given, the current body angle is used.
* *
* Use very small speed values, such as 0.1, depending on the mass and required velocity. * Use very small speed values, such as 0.1, depending on the mass and required velocity.
* *
* @method Phaser.Physics.Matter.MatterPhysics#applyForceFromAngle * @method Phaser.Physics.Matter.MatterPhysics#applyForceFromAngle
@ -1318,22 +1332,22 @@ var MatterPhysics = new Class({
/** /**
* Aligns a Body, or Matter Game Object, against the given coordinates. * Aligns a Body, or Matter Game Object, against the given coordinates.
* *
* The alignment takes place using the body bounds, which take into consideration things * The alignment takes place using the body bounds, which take into consideration things
* like body scale and rotation. * like body scale and rotation.
* *
* Although a Body has a `position` property, it is based on the center of mass for the body, * Although a Body has a `position` property, it is based on the center of mass for the body,
* not a dimension based center. This makes aligning bodies difficult, especially if they have * not a dimension based center. This makes aligning bodies difficult, especially if they have
* rotated or scaled. This method will derive the correct position based on the body bounds and * rotated or scaled. This method will derive the correct position based on the body bounds and
* its center of mass offset, in order to align the body with the given coordinate. * its center of mass offset, in order to align the body with the given coordinate.
* *
* For example, if you wanted to align a body so it sat in the bottom-center of the * For example, if you wanted to align a body so it sat in the bottom-center of the
* Scene, and the world was 800 x 600 in size: * Scene, and the world was 800 x 600 in size:
* *
* ```javascript * ```javascript
* this.matter.alignBody(body, 400, 600, Phaser.Display.Align.BOTTOM_CENTER); * this.matter.alignBody(body, 400, 600, Phaser.Display.Align.BOTTOM_CENTER);
* ``` * ```
* *
* You pass in 400 for the x coordinate, because that is the center of the world, and 600 for * You pass in 400 for the x coordinate, because that is the center of the world, and 600 for
* the y coordinate, as that is the base of the world. * the y coordinate, as that is the base of the world.
* *
@ -1363,7 +1377,7 @@ var MatterPhysics = new Class({
case ALIGN_CONST.TOP_CENTER: case ALIGN_CONST.TOP_CENTER:
pos = this.bodyBounds.getTopCenter(body, x, y); pos = this.bodyBounds.getTopCenter(body, x, y);
break; break;
case ALIGN_CONST.TOP_RIGHT: case ALIGN_CONST.TOP_RIGHT:
case ALIGN_CONST.RIGHT_TOP: case ALIGN_CONST.RIGHT_TOP:
pos = this.bodyBounds.getTopRight(body, x, y); pos = this.bodyBounds.getTopRight(body, x, y);

View file

@ -37,7 +37,12 @@ var Body = require('./Body');
constraints: [], constraints: [],
composites: [], composites: [],
label: 'Composite', label: 'Composite',
plugin: {} plugin: {},
cache: {
allBodies: null,
allConstraints: null,
allComposites: null
}
}, options); }, options);
}; };
@ -57,12 +62,18 @@ var Body = require('./Body');
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);
} }
@ -70,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) {
@ -120,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,10 +191,9 @@ var Body = require('./Body');
* @return {composite} The original compositeA with the composite removed * @return {composite} The original compositeA with the composite removed
*/ */
Composite.removeComposite = function(compositeA, compositeB, deep) { Composite.removeComposite = function(compositeA, compositeB, deep) {
var position = compositeA.composites.indexOf(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,10 +243,9 @@ var Body = require('./Body');
* @return {composite} The original composite with the body removed * @return {composite} The original composite with the body removed
*/ */
Composite.removeBody = function(composite, body, deep) { Composite.removeBody = function(composite, body, deep) {
var position = composite.bodies.indexOf(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) {
@ -286,7 +295,7 @@ var Body = require('./Body');
* @return {composite} The original composite with the constraint removed * @return {composite} The original composite with the constraint removed
*/ */
Composite.removeConstraint = function(composite, constraint, deep) { Composite.removeConstraint = function(composite, constraint, deep) {
var position = composite.constraints.indexOf(constraint); var position = Common.indexOf(composite.constraints, constraint);
if (position !== -1) { if (position !== -1) {
Composite.removeConstraintAt(composite, position); Composite.removeConstraintAt(composite, position);
} }
@ -337,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;
@ -349,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;
}; };
@ -364,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;
}; };
@ -379,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;
}; };
@ -450,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;
}; };
@ -470,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;
}; };
@ -501,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;
}; };
@ -531,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;
}; };
@ -685,4 +711,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,11 +1,13 @@
/** /**
* 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 * @extends Composite
@ -16,37 +18,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');
(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);
};
/* /*
* *
@ -95,7 +79,7 @@ var Common = require('../core/Common');
// World is a Composite body // World is a Composite body
// see src/module/Outro.js for these aliases: // see src/module/Outro.js for these aliases:
/** /**
* An alias for Composite.add * An alias for Composite.add
* @method add * @method add
@ -127,7 +111,7 @@ var Common = require('../core/Common');
* @param {composite} composite * @param {composite} composite
* @return {world} The original world with the objects from composite added * @return {world} The original world with the objects from composite added
*/ */
/** /**
* An alias for Composite.addBody * An alias for Composite.addBody
* @method addBody * @method addBody

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

@ -0,0 +1,27 @@
/**
* The `Matter.Contact` module contains methods for creating and manipulating collision contacts.
*
* @class Contact
*/
var Contact = {};
module.exports = Contact;
(function() {
/**
* Creates a new contact.
* @method create
* @param {vertex} vertex
* @return {contact} A new contact
*/
Contact.create = function(vertex) {
return {
vertex: vertex,
normalImpulse: 0,
tangentImpulse: 0
};
};
})();

View file

@ -1,84 +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
* @return {array} collisions
*/ */
Detector.collisions = function(broadphasePairs, engine) { 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);
// @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
} }
} }
} }
@ -104,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(bucket.indexOf(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

@ -8,6 +8,8 @@ var Pair = {};
module.exports = Pair; module.exports = Pair;
var Contact = require('./Contact');
(function() { (function() {
/** /**
@ -25,6 +27,8 @@ module.exports = Pair;
id: Pair.id(bodyA, bodyB), id: Pair.id(bodyA, bodyB),
bodyA: bodyA, bodyA: bodyA,
bodyB: bodyB, bodyB: bodyB,
collision: collision,
contacts: [],
activeContacts: [], activeContacts: [],
separation: 0, separation: 0,
isActive: true, isActive: true,
@ -32,7 +36,6 @@ module.exports = Pair;
isSensor: bodyA.isSensor || bodyB.isSensor, isSensor: bodyA.isSensor || bodyB.isSensor,
timeCreated: timestamp, timeCreated: timestamp,
timeUpdated: timestamp, timeUpdated: timestamp,
collision: null,
inverseMass: 0, inverseMass: 0,
friction: 0, friction: 0,
frictionStatic: 0, frictionStatic: 0,
@ -53,35 +56,36 @@ module.exports = Pair;
* @param {number} timestamp * @param {number} timestamp
*/ */
Pair.update = function(pair, collision, timestamp) { Pair.update = function(pair, collision, timestamp) {
var contacts = pair.contacts,
supports = collision.supports,
activeContacts = pair.activeContacts,
parentA = collision.parentA,
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.friction = parentA.friction < parentB.friction ? parentA.friction : parentB.friction;
pair.frictionStatic = parentA.frictionStatic > parentB.frictionStatic ? parentA.frictionStatic : parentB.frictionStatic;
pair.restitution = parentA.restitution > parentB.restitution ? parentA.restitution : parentB.restitution;
pair.slop = parentA.slop > parentB.slop ? parentA.slop : parentB.slop;
if (collision.collided) { collision.pair = pair;
var supports = collision.supports, activeContacts.length = 0;
activeContacts = pair.activeContacts,
parentA = collision.parentA, for (var i = 0; i < supports.length; i++) {
parentB = collision.parentB; var support = supports[i],
contactId = support.body === parentA ? support.index : parentAVerticesLength + support.index,
contact = contacts[contactId];
pair.inverseMass = parentA.inverseMass + parentB.inverseMass; if (contact) {
pair.friction = Math.min(parentA.friction, parentB.friction); activeContacts.push(contact);
pair.frictionStatic = Math.max(parentA.frictionStatic, parentB.frictionStatic); } else {
pair.restitution = Math.max(parentA.restitution, parentB.restitution); activeContacts.push(contacts[contactId] = Contact.create(support));
pair.slop = Math.max(parentA.slop, parentB.slop);
for (var i = 0; i < supports.length; i++) {
activeContacts[i] = supports[i].contact;
} }
// optimise array size
var supportCount = supports.length;
if (supportCount < activeContacts.length) {
activeContacts.length = supportCount;
}
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,28 +23,34 @@ 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;
// Phaser addition - skip same body checks // Phaser addition - skip same body checks
if (body === bodyA) if (body === bodyA)
{ {
continue; continue;
} }
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;
} }
@ -63,7 +69,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;
@ -77,7 +83,7 @@ var Vertices = require('../geometry/Vertices');
for (var i = 0; i < collisions.length; i += 1) { for (var i = 0; i < collisions.length; i += 1) {
var collision = collisions[i]; var collision = collisions[i];
collision.body = collision.bodyB = collision.bodyA; collision.body = collision.bodyB = collision.bodyA;
} }
return collisions; return collisions;
@ -116,7 +122,7 @@ var Vertices = require('../geometry/Vertices');
for (var i = 0; i < bodies.length; i++) { for (var i = 0; i < bodies.length; i++) {
var body = bodies[i]; var body = bodies[i];
if (Bounds.contains(body.bounds, point)) { if (Bounds.contains(body.bounds, point)) {
for (var j = body.parts.length === 1 ? 0 : 1; j < body.parts.length; j++) { for (var j = body.parts.length === 1 ? 0 : 1; j < body.parts.length; j++) {
var part = body.parts[j]; var part = body.parts[j];

View file

@ -9,8 +9,6 @@ var Resolver = {};
module.exports = Resolver; module.exports = Resolver;
var Vertices = require('../geometry/Vertices'); var Vertices = require('../geometry/Vertices');
var Vector = require('../geometry/Vector');
var Common = require('../core/Common');
var Bounds = require('../geometry/Bounds'); var Bounds = require('../geometry/Bounds');
(function() { (function() {
@ -29,10 +27,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)
@ -48,36 +47,22 @@ var Bounds = require('../geometry/Bounds');
* Find a solution for pair positions. * Find a solution for pair positions.
* @method solvePosition * @method solvePosition
* @param {pair[]} pairs * @param {pair[]} pairs
* @param {body[]} bodies
* @param {number} timeScale * @param {number} timeScale
*/ */
Resolver.solvePosition = function(pairs, bodies, timeScale) { Resolver.solvePosition = function(pairs, timeScale) {
var i, var i,
normalX,
normalY,
pair, pair,
collision, collision,
bodyA, bodyA,
bodyB, bodyB,
normal, normal,
separation,
penetration,
positionImpulseA,
positionImpulseB,
contactShare, contactShare,
bodyBtoAX,
bodyBtoAY,
positionImpulse, positionImpulse,
impulseCoefficient = timeScale * Resolver._positionDampen; positionDampen = Resolver._positionDampen,
pairsLength = pairs.length;
for (i = 0; i < bodies.length; i++) {
var body = bodies[i];
body.previousPositionImpulse.x = body.positionImpulse.x;
body.previousPositionImpulse.y = body.positionImpulse.y;
}
// 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)
@ -88,35 +73,37 @@ var Bounds = require('../geometry/Bounds');
bodyB = collision.parentB; bodyB = collision.parentB;
normal = collision.normal; normal = collision.normal;
positionImpulseA = bodyA.previousPositionImpulse; // get current separation between body edges involved in collision
positionImpulseB = bodyB.previousPositionImpulse; pair.separation =
normal.x * (bodyB.positionImpulse.x + collision.penetration.x - bodyA.positionImpulse.x)
+ normal.y * (bodyB.positionImpulse.y + collision.penetration.y - bodyA.positionImpulse.y);
}
for (i = 0; i < pairsLength; i++) {
pair = pairs[i];
penetration = collision.penetration; if (!pair.isActive || pair.isSensor)
continue;
bodyBtoAX = positionImpulseB.x - positionImpulseA.x + penetration.x;
bodyBtoAY = positionImpulseB.y - positionImpulseA.y + penetration.y; collision = pair.collision;
bodyA = collision.parentA;
normalX = normal.x; bodyB = collision.parentB;
normalY = normal.y; normal = collision.normal;
positionImpulse = (pair.separation - pair.slop) * timeScale;
separation = normalX * bodyBtoAX + normalY * bodyBtoAY;
pair.separation = separation;
positionImpulse = (separation - pair.slop) * impulseCoefficient;
if (bodyA.isStatic || bodyB.isStatic) if (bodyA.isStatic || bodyB.isStatic)
positionImpulse *= 2; positionImpulse *= 2;
if (!(bodyA.isStatic || bodyA.isSleeping)) { if (!(bodyA.isStatic || bodyA.isSleeping)) {
contactShare = positionImpulse / bodyA.totalContacts; contactShare = positionDampen / bodyA.totalContacts;
bodyA.positionImpulse.x += normalX * contactShare; bodyA.positionImpulse.x += normal.x * positionImpulse * contactShare;
bodyA.positionImpulse.y += normalY * contactShare; bodyA.positionImpulse.y += normal.y * positionImpulse * contactShare;
} }
if (!(bodyB.isStatic || bodyB.isSleeping)) { if (!(bodyB.isStatic || bodyB.isSleeping)) {
contactShare = positionImpulse / bodyB.totalContacts; contactShare = positionDampen / bodyB.totalContacts;
bodyB.positionImpulse.x -= normalX * contactShare; bodyB.positionImpulse.x -= normal.x * positionImpulse * contactShare;
bodyB.positionImpulse.y -= normalY * contactShare; bodyB.positionImpulse.y -= normal.y * positionImpulse * contactShare;
} }
} }
}; };
@ -127,34 +114,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;
} }
} }
} }
@ -166,61 +162,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
);
} }
} }
} }
@ -235,14 +223,17 @@ var Bounds = require('../geometry/Bounds');
*/ */
Resolver.solveVelocity = function(pairs, timeScale) { Resolver.solveVelocity = function(pairs, timeScale) {
var timeScaleSquared = timeScale * timeScale, var timeScaleSquared = timeScale * timeScale,
impulse = Vector._temp[0], restingThresh = Resolver._restingThresh * timeScaleSquared,
tempA = Vector._temp[1], frictionNormalMultiplier = Resolver._frictionNormalMultiplier,
tempB = Vector._temp[2], restingThreshTangent = Resolver._restingThreshTangent * timeScaleSquared,
tempC = Vector._temp[3], NumberMaxValue = Number.MAX_VALUE,
tempD = Vector._temp[4], pairsLength = pairs.length,
tempE = Vector._temp[5]; tangentImpulse,
maxFriction,
for (var i = 0; i < pairs.length; i++) { 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)
@ -251,97 +242,119 @@ 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 normalOverlap = pair.separation + normalVelocity;
maxFriction = Infinity; var normalForce = Math.min(normalOverlap, 1);
normalForce = normalOverlap < 0 ? 0 : normalForce;
var frictionLimit = normalForce * friction;
if (tangentSpeed > pair.friction * pair.frictionStatic * normalForce * timeScaleSquared) { if (tangentVelocity > frictionLimit || -tangentVelocity > frictionLimit) {
maxFriction = tangentSpeed; maxFriction = tangentVelocity > 0 ? tangentVelocity : -tangentVelocity;
tangentImpulse = Common.clamp( tangentImpulse = pair.friction * (tangentVelocity > 0 ? 1 : -1) * timeScaleSquared;
pair.friction * tangentVelocityDirection * timeScaleSquared,
-maxFriction, maxFriction 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
if (normalVelocity < 0 && normalVelocity * normalVelocity > Resolver._restingThresh * timeScaleSquared) { if (normalVelocity * normalVelocity > restingThresh && normalVelocity < 0) {
// high normal velocity so clear cached contact normal impulse // high normal velocity so clear cached contact normal impulse
contact.normalImpulse = 0; contact.normalImpulse = 0;
} else { } else {
// 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;
} }
// handle high velocity and resting collisions separately // handle high velocity and resting collisions separately
if (tangentVelocity * tangentVelocity > Resolver._restingThreshTangent * timeScaleSquared) { if (tangentVelocity * tangentVelocity > restingThreshTangent) {
// high tangent velocity so clear cached contact tangent impulse // high tangent velocity so clear cached contact tangent impulse
contact.tangentImpulse = 0; contact.tangentImpulse = 0;
} else { } else {
// 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,270 +1,37 @@
/** /**
* 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 deprecated = Common.deprecated;
(function() { (function() {
/** /**
* 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
* @return {collision} collision * @return {collision} collision
*/ */
SAT.collides = function(bodyA, bodyB, previousCollision) { SAT.collides = function(bodyA, bodyB) {
var overlapAB, return Collision.collides(bodyA, bodyB);
overlapBA,
minOverlap,
collision,
canReusePrevCol = false;
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 < 0.2;
// 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

@ -312,8 +312,10 @@ var Common = require('../core/Common');
*/ */
Constraint.pointAWorld = function(constraint) { Constraint.pointAWorld = function(constraint) {
return { return {
x: (constraint.bodyA ? constraint.bodyA.position.x : 0) + constraint.pointA.x, x: (constraint.bodyA ? constraint.bodyA.position.x : 0)
y: (constraint.bodyA ? constraint.bodyA.position.y : 0) + constraint.pointA.y + (constraint.pointA ? constraint.pointA.x : 0),
y: (constraint.bodyA ? constraint.bodyA.position.y : 0)
+ (constraint.pointA ? constraint.pointA.y : 0)
}; };
}; };
@ -325,8 +327,10 @@ var Common = require('../core/Common');
*/ */
Constraint.pointBWorld = function(constraint) { Constraint.pointBWorld = function(constraint) {
return { return {
x: (constraint.bodyB ? constraint.bodyB.position.x : 0) + constraint.pointB.x, x: (constraint.bodyB ? constraint.bodyB.position.x : 0)
y: (constraint.bodyB ? constraint.bodyB.position.y : 0) + constraint.pointB.y + (constraint.pointB ? constraint.pointB.x : 0),
y: (constraint.bodyB ? constraint.bodyB.position.y : 0)
+ (constraint.pointB ? constraint.pointB.y : 0)
}; };
}; };

View file

@ -13,7 +13,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
@ -221,7 +223,7 @@ module.exports = Common;
* @return {boolean} True if the object is a string, otherwise false * @return {boolean} True if the object is a string, otherwise false
*/ */
Common.isString = function(obj) { Common.isString = function(obj) {
return Object.prototype.toString.call(obj) === '[object String]'; return toString.call(obj) === '[object String]';
}; };
/** /**
@ -252,9 +254,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) {
@ -265,6 +267,10 @@ module.exports = Common;
} }
} }
if (Date.now) {
return Date.now();
}
return (new Date()) - Common._nowStartTime; return (new Date()) - Common._nowStartTime;
}; };
@ -358,6 +364,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
@ -535,4 +570,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,12 +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 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');
@ -34,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,
@ -51,26 +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);
engine.world = options.world || World.create(engine.world); engine.world = options.world || Composite.create({ label: 'World' });
engine.pairs = Pairs.create(); engine.pairs = options.pairs || Pairs.create();
engine.broadphase = engine.broadphase.controller.create(engine.broadphase); engine.detector = options.detector || Detector.create();
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;
}; };
@ -90,17 +86,21 @@ var Body = require('../body/Body');
* @param {number} [correction=1] * @param {number} [correction=1]
*/ */
Engine.update = function(engine, delta, correction) { Engine.update = function(engine, delta, correction) {
var startTime = Common.now();
delta = delta || 1000 / 60; delta = delta || 1000 / 60;
correction = correction || 1; correction = correction || 1;
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;
// increment timestamp // increment timestamp
timing.timestamp += delta * timing.timeScale; timing.timestamp += delta * timing.timeScale;
timing.lastDelta = delta * timing.timeScale;
// create an event object // create an event object
var event = { var event = {
@ -109,21 +109,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, timing.timeScale); Sleeping.update(allBodies, timing.timeScale);
// 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
Engine._bodiesUpdate(allBodies, delta, timing.timeScale, correction, world.bounds); Engine._bodiesUpdate(allBodies, delta, timing.timeScale, correction, world.bounds);
@ -135,33 +140,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);
// 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)
@ -174,7 +158,7 @@ var Body = require('../body/Body');
// iteratively resolve position between collisions // iteratively resolve position between collisions
Resolver.preSolvePosition(pairs.list); Resolver.preSolvePosition(pairs.list);
for (i = 0; i < engine.positionIterations; i++) { for (i = 0; i < engine.positionIterations; i++) {
Resolver.solvePosition(pairs.list, allBodies, timing.timeScale); Resolver.solvePosition(pairs.list, timing.timeScale);
} }
Resolver.postSolvePosition(allBodies); Resolver.postSolvePosition(allBodies);
@ -198,16 +182,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;
}; };
@ -236,21 +218,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);
}
}; };
/** /**
@ -320,7 +294,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
*/ */
@ -329,53 +304,53 @@ 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 {} 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 {} 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 {} 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 {} 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 {} 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
*/ */
/* /*
@ -449,32 +424,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
*/ */
/** /**
@ -484,4 +483,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

@ -27,7 +27,7 @@ var Common = require('./Common');
* @readOnly * @readOnly
* @type {String} * @type {String}
*/ */
Matter.version = '0.14.2'; Matter.version = '0.18.0';
/** /**
* A list of plugin dependencies to be installed. These are normally set and installed through `Matter.use`. * A list of plugin dependencies to be installed. These are normally set and installed through `Matter.use`.

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

@ -46,7 +46,7 @@ var Common = require('./Common');
}; };
/** /**
* Resolves a dependency to a plugin object from the registry if it exists. * Resolves a dependency to a plugin object from the registry if it exists.
* The `dependency` may contain a version, but only the name matters when resolving. * The `dependency` may contain a version, but only the name matters when resolving.
* @method resolve * @method resolve
* @param dependency {string} The dependency. * @param dependency {string} The dependency.
@ -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.');
@ -259,13 +259,15 @@ var Common = require('./Common');
}; };
/** /**
* Parses a version string into its components. * Parses a version string into its components.
* Versions are strictly of the format `x.y.z` (as in [semver](http://semver.org/)). * Versions are strictly of the format `x.y.z` (as in [semver](http://semver.org/)).
* Versions may optionally have a prerelease tag in the format `x.y.z-alpha`. * Versions may optionally have a prerelease tag in the format `x.y.z-alpha`.
* Ranges are a strict subset of [npm ranges](https://docs.npmjs.com/misc/semver#advanced-range-syntax). * Ranges are a strict subset of [npm ranges](https://docs.npmjs.com/misc/semver#advanced-range-syntax).
* Only the following range types are supported: * Only the following range types are supported:
* - Tilde ranges e.g. `~1.2.3` * - Tilde ranges e.g. `~1.2.3`
* - Caret ranges e.g. `^1.2.3` * - Caret ranges e.g. `^1.2.3`
* - Greater than ranges e.g. `>1.2.3`
* - Greater than or equal ranges e.g. `>=1.2.3`
* - Exact version e.g. `1.2.3` * - Exact version e.g. `1.2.3`
* - Any version `*` * - Any version `*`
* @method versionParse * @method versionParse
@ -273,29 +275,28 @@ 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 = /^\*|[\^~]?\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.');
} }
var identifiers = range.split('-'); var parts = pattern.exec(range);
range = identifiers[0]; var major = Number(parts[4]);
var minor = Number(parts[5]);
var isRange = isNaN(Number(range[0])), var patch = Number(parts[6]);
version = isRange ? range.substr(1) : range,
parts = Common.map(version.split('.'), function(part) {
return Number(part);
});
return { return {
isRange: isRange, isRange: Boolean(parts[1] || parts[2]),
version: version, version: parts[3],
range: range, range: range,
operator: isRange ? range[0] : '', operator: parts[1] || parts[2] || '',
parts: parts, major: major,
prerelease: identifiers[1], minor: minor,
number: parts[0] * 1e8 + parts[1] * 1e4 + parts[2] patch: patch,
parts: [major, minor, patch],
prerelease: parts[7],
number: major * 1e8 + minor * 1e4 + patch
}; };
}; };
@ -311,30 +312,36 @@ var Common = require('./Common');
Plugin.versionSatisfies = function(version, range) { Plugin.versionSatisfies = function(version, range) {
range = range || '*'; range = range || '*';
var rangeParsed = Plugin.versionParse(range), var r = Plugin.versionParse(range),
rangeParts = rangeParsed.parts, v = Plugin.versionParse(version);
versionParsed = Plugin.versionParse(version),
versionParts = versionParsed.parts;
if (rangeParsed.isRange) { if (r.isRange) {
if (rangeParsed.operator === '*' || version === '*') { if (r.operator === '*' || version === '*') {
return true; return true;
} }
if (rangeParsed.operator === '~') { if (r.operator === '>') {
return versionParts[0] === rangeParts[0] && versionParts[1] === rangeParts[1] && versionParts[2] >= rangeParts[2]; return v.number > r.number;
} }
if (rangeParsed.operator === '^') { if (r.operator === '>=') {
if (rangeParts[0] > 0) { return v.number >= r.number;
return versionParts[0] === rangeParts[0] && versionParsed.number >= rangeParsed.number; }
if (r.operator === '~') {
return v.major === r.major && v.minor === r.minor && v.patch >= r.patch;
}
if (r.operator === '^') {
if (r.major > 0) {
return v.major === r.major && v.number >= r.number;
} }
if (rangeParts[1] > 0) { if (r.minor > 0) {
return versionParts[1] === rangeParts[1] && versionParts[2] >= rangeParts[2]; return v.minor === r.minor && v.patch >= r.patch;
} }
return versionParts[2] === rangeParts[2]; return v.patch === r.patch;
} }
} }

View file

@ -101,7 +101,6 @@ 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, time correction 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
@ -119,7 +118,6 @@ var Common = require('./Common');
}; };
Events.trigger(runner, 'beforeTick', event); Events.trigger(runner, 'beforeTick', event);
Events.trigger(engine, 'beforeTick', event); // @deprecated
if (runner.isFixed) { if (runner.isFixed) {
// fixed timestep // fixed timestep
@ -164,35 +162,13 @@ 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);
Engine.update(engine, delta, correction); Engine.update(engine, delta, correction);
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
}; };
/** /**
@ -271,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

@ -18,7 +18,6 @@ var Common = require('../core/Common');
var Body = require('../body/Body'); var Body = require('../body/Body');
var Bounds = require('../geometry/Bounds'); var Bounds = require('../geometry/Bounds');
var Vector = require('../geometry/Vector'); var Vector = require('../geometry/Vector');
var decomp = require('../../poly-decomp');
(function() { (function() {
@ -177,29 +176,46 @@ var decomp = require('../../poly-decomp');
}; };
/** /**
* 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 body, var decomp = Common.getDecomp(),
canDecomp,
body,
parts, parts,
isConvex, isConvex,
isConcave,
vertices, vertices,
i, i,
j, j,
@ -207,16 +223,16 @@ var decomp = require('../../poly-decomp');
v, v,
z; z;
// check decomp is as expected
canDecomp = Boolean(decomp && decomp.quickDecomp);
options = options || {}; options = options || {};
parts = []; parts = [];
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])) {
@ -226,8 +242,15 @@ var decomp = require('../../poly-decomp');
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 {
@ -249,6 +272,8 @@ var decomp = require('../../poly-decomp');
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);
@ -283,14 +308,45 @@ var decomp = require('../../poly-decomp');
parts[i] = Body.create(Common.extend(parts[i], options)); parts[i] = Body.create(Common.extend(parts[i], options));
} }
if (flagInternal) // flag internal edges (coincident part edges)
{ if (flagInternal) {
Bodies.flagCoincidentParts(parts, 5); var coincident_max_dist = 5;
for (i = 0; i < parts.length; i++) {
var partA = parts[i];
for (j = i + 1; j < parts.length; j++) {
var partB = parts[j];
if (Bounds.overlaps(partA.bounds, partB.bounds)) {
var pav = partA.vertices,
pbv = partB.vertices;
// iterate vertices of both parts
for (k = 0; k < partA.vertices.length; k++) {
for (z = 0; z < partB.vertices.length; z++) {
// find distances between the vertices
var da = Vector.magnitudeSquared(Vector.sub(pav[(k + 1) % pav.length], pbv[z])),
db = Vector.magnitudeSquared(Vector.sub(pav[k], pbv[(z + 1) % pbv.length]));
// if both vertices are very close, consider the edge concident (internal)
if (da < coincident_max_dist && db < coincident_max_dist) {
pav[k].isInternal = true;
pbv[z].isInternal = true;
}
}
}
}
}
}
} }
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;
@ -299,54 +355,4 @@ var decomp = require('../../poly-decomp');
} }
}; };
/**
* Takes an array of Body objects and flags all internal edges (coincident parts) based on the maxDistance
* value. The array is changed in-place and returned, so you can pass this function a `Body.parts` property.
*
* @method flagCoincidentParts
* @param {body[]} parts - The Body parts, or array of bodies, to flag.
* @param {number} [maxDistance=5]
* @return {body[]} The modified `parts` parameter.
*/
Bodies.flagCoincidentParts = function (parts, maxDistance)
{
if (maxDistance === undefined) { maxDistance = 5; }
for (var i = 0; i < parts.length; i++)
{
var partA = parts[i];
for (var j = i + 1; j < parts.length; j++)
{
var partB = parts[j];
if (Bounds.overlaps(partA.bounds, partB.bounds))
{
var pav = partA.vertices;
var pbv = partB.vertices;
// iterate vertices of both parts
for (var k = 0; k < partA.vertices.length; k++)
{
for (var z = 0; z < partB.vertices.length; z++)
{
// find distances between the vertices
var da = Vector.magnitudeSquared(Vector.sub(pav[(k + 1) % pav.length], pbv[z]));
var db = Vector.magnitudeSquared(Vector.sub(pav[k], pbv[(z + 1) % pbv.length]));
// if both vertices are very close, consider the edge concident (internal)
if (da < maxDistance && db < maxDistance)
{
pav[k].isInternal = true;
pbv[z].isInternal = true;
}
}
}
}
}
}
return parts;
};
})(); })();

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

@ -44,17 +44,9 @@ var Common = require('../core/Common');
y: point.y, y: point.y,
index: i, index: i,
body: body, body: body,
isInternal: false, isInternal: false
contact: null,
offset: null
}; };
vertex.contact = {
vertex: vertex,
normalImpulse: 0,
tangentImpulse: 0
};
vertices.push(vertex); vertices.push(vertex);
} }
@ -177,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;
@ -205,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;
@ -227,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

@ -9,7 +9,7 @@ var MatterAttractors =
{ {
name: 'matter-attractors', name: 'matter-attractors',
version: '0.1.7', version: '0.1.7',
for: 'matter-js@^0.14.2', for: 'matter-js@^0.18.0',
silent: true, silent: true,
// installs the plugin where `base` is `Matter` // installs the plugin where `base` is `Matter`

View file

@ -8,7 +8,7 @@ var MatterCollisionEvents = {
name: 'matter-collision-events', name: 'matter-collision-events',
version: '0.1.6', version: '0.1.6',
for: 'matter-js@^0.14.2', for: 'matter-js@^0.18.0',
silent: true, silent: true,
install: function (matter) install: function (matter)

View file

@ -9,7 +9,7 @@ var MatterWrap = {
// plugin meta // plugin meta
name: 'matter-wrap', // PLUGIN_NAME name: 'matter-wrap', // PLUGIN_NAME
version: '0.1.4', // PLUGIN_VERSION version: '0.1.4', // PLUGIN_VERSION
for: 'matter-js@^0.14.2', for: 'matter-js@^0.18.0',
silent: true, // no console log please silent: true, // no console log please
// installs the plugin where `base` is `Matter` // installs the plugin where `base` is `Matter`
@ -90,8 +90,8 @@ var MatterWrap = {
Body: { Body: {
/** /**
* Wraps the `body` position such that it always stays within the given bounds. * 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, * Upon crossing a boundary the body will appear on the opposite side of the bounds,
* while maintaining its velocity. * while maintaining its velocity.
* This is called automatically by the plugin. * This is called automatically by the plugin.
* @function MatterWrap.Body.wrap * @function MatterWrap.Body.wrap
@ -121,7 +121,7 @@ var MatterWrap = {
bounds: function(composite) { bounds: function(composite) {
var bodies = Matter.Composite.allBodies(composite), var bodies = Matter.Composite.allBodies(composite),
vertices = []; vertices = [];
for (var i = 0; i < bodies.length; i += 1) { for (var i = 0; i < bodies.length; i += 1) {
var body = bodies[i]; var body = bodies[i];
vertices.push(body.bounds.min, body.bounds.max); vertices.push(body.bounds.min, body.bounds.max);
@ -131,8 +131,8 @@ var MatterWrap = {
}, },
/** /**
* Wraps the `composite` position such that it always stays within the given bounds. * 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, * Upon crossing a boundary the composite will appear on the opposite side of the bounds,
* while maintaining its velocity. * while maintaining its velocity.
* This is called automatically by the plugin. * This is called automatically by the plugin.
* @function MatterWrap.Composite.wrap * @function MatterWrap.Composite.wrap
@ -142,7 +142,7 @@ var MatterWrap = {
*/ */
wrap: function(composite, bounds) { wrap: function(composite, bounds) {
var translation = MatterWrap.Bounds.wrap( var translation = MatterWrap.Bounds.wrap(
MatterWrap.Composite.bounds(composite), MatterWrap.Composite.bounds(composite),
bounds bounds
); );
@ -163,15 +163,15 @@ module.exports = MatterWrap;
*/ */
/** /**
* This plugin adds a new property `body.plugin.wrap` to instances of `Matter.Body`. * 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. * This is a `Matter.Bounds` instance that specifies the wrapping region.
* @property {Matter.Bounds} body.plugin.wrap * @property {Matter.Bounds} body.plugin.wrap
* @memberof Matter.Body * @memberof Matter.Body
*/ */
/** /**
* This plugin adds a new property `composite.plugin.wrap` to instances of `Matter.Composite`. * 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. * This is a `Matter.Bounds` instance that specifies the wrapping region.
* @property {Matter.Bounds} composite.plugin.wrap * @property {Matter.Bounds} composite.plugin.wrap
* @memberof Matter.Composite * @memberof Matter.Composite
*/ */

View file

@ -44,4 +44,6 @@ if (typeof window.Uint32Array !== 'function' && typeof window.Uint32Array !== 'o
CheapArray('Uint16Array'); // jshint ignore:line CheapArray('Uint16Array'); // jshint ignore:line
CheapArray('Int16Array'); // jshint ignore:line CheapArray('Int16Array'); // jshint ignore:line
CheapArray('ArrayBuffer'); // jshint ignore:line CheapArray('ArrayBuffer'); // jshint ignore:line
CheapArray('Int8Array'); // jshint ignore:line
CheapArray('Uint8Array'); // jshint ignore:line
} }

View file

@ -818,7 +818,10 @@ var CanvasRenderer = new Class({
sprite.mask.preRenderCanvas(this, sprite, camera); sprite.mask.preRenderCanvas(this, sprite, camera);
} }
ctx.drawImage(frame.source.image, frameX, frameY, frameWidth, frameHeight, x, y, frameWidth / res, frameHeight / res); if (frameWidth > 0 && frameHeight > 0)
{
ctx.drawImage(frame.source.image, frameX, frameY, frameWidth, frameHeight, x, y, frameWidth / res, frameHeight / res);
}
if (sprite.mask) if (sprite.mask)
{ {

View file

@ -10,7 +10,7 @@ var GetFastValue = require('../../utils/object/GetFastValue');
/** /**
* Takes a snapshot of an area from the current frame displayed by a canvas. * Takes a snapshot of an area from the current frame displayed by a canvas.
* *
* This is then copied to an Image object. When this loads, the results are sent * This is then copied to an Image object. When this loads, the results are sent
* to the callback provided in the Snapshot Configuration object. * to the callback provided in the Snapshot Configuration object.
* *
@ -45,10 +45,13 @@ var CanvasSnapshot = function (canvas, config)
var copyCanvas = CanvasPool.createWebGL(this, width, height); var copyCanvas = CanvasPool.createWebGL(this, width, height);
var ctx = copyCanvas.getContext('2d'); var ctx = copyCanvas.getContext('2d');
ctx.drawImage(canvas, x, y, width, height, 0, 0, width, height); if (width > 0 && height > 0)
{
ctx.drawImage(canvas, x, y, width, height, 0, 0, width, height);
}
var image1 = new Image(); var image1 = new Image();
image1.onerror = function () image1.onerror = function ()
{ {
callback.call(null); callback.call(null);
@ -69,7 +72,7 @@ var CanvasSnapshot = function (canvas, config)
{ {
// Full Grab // Full Grab
var image2 = new Image(); var image2 = new Image();
image2.onerror = function () image2.onerror = function ()
{ {
callback.call(null); callback.call(null);

View file

@ -17,12 +17,12 @@ var GetFastValue = require('../../utils/object/GetFastValue');
* @function Phaser.Renderer.Snapshot.WebGL * @function Phaser.Renderer.Snapshot.WebGL
* @since 3.0.0 * @since 3.0.0
* *
* @param {HTMLCanvasElement} sourceCanvas - The canvas to take a snapshot of. * @param {WebGLRenderingContext} sourceContext - The WebGL context to take a snapshot of.
* @param {Phaser.Types.Renderer.Snapshot.SnapshotState} config - The snapshot configuration object. * @param {Phaser.Types.Renderer.Snapshot.SnapshotState} config - The snapshot configuration object.
*/ */
var WebGLSnapshot = function (sourceCanvas, config) var WebGLSnapshot = function (sourceContext, config)
{ {
var gl = sourceCanvas.getContext('experimental-webgl'); var gl = sourceContext;
var callback = GetFastValue(config, 'callback'); var callback = GetFastValue(config, 'callback');
var type = GetFastValue(config, 'type', 'image/png'); var type = GetFastValue(config, 'type', 'image/png');

View file

@ -2654,7 +2654,7 @@ var WebGLRenderer = new Class({
if (state.callback) if (state.callback)
{ {
WebGLSnapshot(this.canvas, state); WebGLSnapshot(this.gl, state);
state.callback = null; state.callback = null;
} }
@ -2814,7 +2814,7 @@ var WebGLRenderer = new Class({
this.setFramebuffer(framebuffer); this.setFramebuffer(framebuffer);
WebGLSnapshot(this.canvas, state); WebGLSnapshot(this.gl, state);
this.setFramebuffer(currentFramebuffer); this.setFramebuffer(currentFramebuffer);

View file

@ -194,30 +194,28 @@ var BaseSoundManager = new Class({
var _this = this; var _this = this;
var body = document.body; var body = document.body;
var bodyAdd = body.addEventListener;
var bodyRemove = body.removeEventListener;
var unlockHandler = function unlockHandler ()
{
if (!_this.pendingUnlock)
{
return;
}
_this.unlockHandler();
bodyRemove('touchstart', unlockHandler);
bodyRemove('touchend', unlockHandler);
bodyRemove('click', unlockHandler);
bodyRemove('keydown', unlockHandler);
};
if (body) if (body)
{ {
bodyAdd('touchstart', unlockHandler, false); var unlockHandler = function unlockHandler ()
bodyAdd('touchend', unlockHandler, false); {
bodyAdd('click', unlockHandler, false); if (!_this.pendingUnlock)
bodyAdd('keydown', unlockHandler, false); {
return;
}
_this.unlockHandler();
body.removeEventListener('touchstart', unlockHandler);
body.removeEventListener('touchend', unlockHandler);
body.removeEventListener('click', unlockHandler);
body.removeEventListener('keydown', unlockHandler);
};
body.addEventListener('touchstart', unlockHandler, false);
body.addEventListener('touchend', unlockHandler, false);
body.addEventListener('click', unlockHandler, false);
body.addEventListener('keydown', unlockHandler, false);
this.pendingUnlock = true; this.pendingUnlock = true;
} }

View file

@ -156,7 +156,15 @@ var WebAudioSoundManager = new Class({
} }
else if (window.hasOwnProperty('webkitAudioContext')) else if (window.hasOwnProperty('webkitAudioContext'))
{ {
context = new window.webkitAudioContext({ latencyHint: 'interactive' }); try
{
context = new window.webkitAudioContext({ latencyHint: 'interactive' });
}
catch (e)
{
// For iOS10 and legacy devices we create without arguments:
context = new window.webkitAudioContext();
}
} }
this.setAudioContext(context); this.setAudioContext(context);

View file

@ -331,17 +331,20 @@ var TextureManager = new Class({
var canvas = CanvasPool.create2D(this, cd.width, cd.height); var canvas = CanvasPool.create2D(this, cd.width, cd.height);
var ctx = canvas.getContext('2d'); var ctx = canvas.getContext('2d');
ctx.drawImage( if (cd.width > 0 && cd.height > 0)
textureFrame.source.image, {
cd.x, ctx.drawImage(
cd.y, textureFrame.source.image,
cd.width, cd.x,
cd.height, cd.y,
0, cd.width,
0, cd.height,
cd.width, 0,
cd.height 0,
); cd.width,
cd.height
);
}
data = canvas.toDataURL(type, encoderOptions); data = canvas.toDataURL(type, encoderOptions);

View file

@ -90,15 +90,14 @@ var TilemapLayerCanvasRenderer = function (renderer, src, camera, parentMatrix)
var image = tileset.image.getSourceImage(); var image = tileset.image.getSourceImage();
var tileTexCoords = tileset.getTileTextureCoordinates(tile.index); var tileTexCoords = tileset.getTileTextureCoordinates(tile.index);
var tileWidth = tileset.tileWidth;
var tileHeight = tileset.tileHeight;
if (tileTexCoords === null) if (tileTexCoords === null || tileWidth === 0 || tileHeight === 0)
{ {
continue; continue;
} }
var tileWidth = tileset.tileWidth;
var tileHeight = tileset.tileHeight;
var halfWidth = tileWidth * 0.5; var halfWidth = tileWidth * 0.5;
var halfHeight = tileHeight * 0.5; var halfHeight = tileHeight * 0.5;

View file

@ -967,13 +967,16 @@ var Tween = new Class({
this.state = TWEEN_CONST.ACTIVE; this.state = TWEEN_CONST.ACTIVE;
} }
this.isSeeking = true; if (toPosition > 0)
do
{ {
this.update(0, delta); this.isSeeking = true;
} while (this.totalProgress < toPosition); do
{
this.update(0, delta);
} while (this.totalProgress < toPosition);
}
this.isSeeking = false; this.isSeeking = false;

View file

@ -24,8 +24,7 @@ var SafeRange = function (array, startIndex, endIndex, throwError)
if (startIndex < 0 || if (startIndex < 0 ||
startIndex > len || startIndex > len ||
startIndex >= endIndex || startIndex >= endIndex ||
endIndex > len || endIndex > len)
startIndex + endIndex > len)
{ {
if (throwError) if (throwError)
{ {

View file

@ -4,47 +4,79 @@
* @license {@link https://opensource.org/licenses/MIT|MIT License} * @license {@link https://opensource.org/licenses/MIT|MIT License}
*/ */
// Source object
// The key as a string, or an array of keys, i.e. 'banner', or 'banner.hideBanner'
// The default value to use if the key doesn't exist
/** /**
* Retrieves a value from an object. * Retrieves a value from an object, or an alternative object, falling to a back-up default value if not found.
*
* The key is a string, which can be split based on the use of the period character.
*
* For example:
*
* ```javascript
* const source = {
* lives: 3,
* render: {
* screen: {
* width: 1024
* }
* }
* }
*
* const lives = GetValue(source, 'lives', 1);
* const width = GetValue(source, 'render.screen.width', 800);
* const height = GetValue(source, 'render.screen.height', 600);
* ```
*
* In the code above, `lives` will be 3 because it's defined at the top level of `source`.
* The `width` value will be 1024 because it can be found inside the `render.screen` object.
* The `height` value will be 600, the default value, because it is missing from the `render.screen` object.
* *
* @function Phaser.Utils.Objects.GetValue * @function Phaser.Utils.Objects.GetValue
* @since 3.0.0 * @since 3.0.0
* *
* @param {object} source - The object to retrieve the value from. * @param {object} source - The primary object to try to retrieve the value from. If not found in here, `altSource` is checked.
* @param {string} key - The name of the property to retrieve from the object. If a property is nested, the names of its preceding properties should be separated by a dot (`.`) - `banner.hideBanner` would return the value of the `hideBanner` property from the object stored in the `banner` property of the `source` object. * @param {string} key - The name of the property to retrieve from the object. If a property is nested, the names of its preceding properties should be separated by a dot (`.`) - `banner.hideBanner` would return the value of the `hideBanner` property from the object stored in the `banner` property of the `source` object.
* @param {*} defaultValue - The value to return if the `key` isn't found in the `source` object. * @param {*} defaultValue - The value to return if the `key` isn't found in the `source` object.
* @param {object} [altSource] - An alternative object to retrieve the value from. If the property exists in `source` then `altSource` will not be used.
* *
* @return {*} The value of the requested key. * @return {*} The value of the requested key.
*/ */
var GetValue = function (source, key, defaultValue) var GetValue = function (source, key, defaultValue, altSource)
{ {
if (!source || typeof source === 'number') if ((!source && !altSource) || typeof source === 'number')
{ {
return defaultValue; return defaultValue;
} }
else if (source.hasOwnProperty(key)) else if (source && source.hasOwnProperty(key))
{ {
return source[key]; return source[key];
} }
else if (altSource && altSource.hasOwnProperty(key))
{
return altSource[key];
}
else if (key.indexOf('.') !== -1) else if (key.indexOf('.') !== -1)
{ {
var keys = key.split('.'); var keys = key.split('.');
var parent = source; var parentA = source;
var parentB = altSource;
var value = defaultValue; var value = defaultValue;
// Use for loop here so we can break early // Use for loop here so we can break early
for (var i = 0; i < keys.length; i++) for (var i = 0; i < keys.length; i++)
{ {
if (parent.hasOwnProperty(keys[i])) if (parentA && parentA.hasOwnProperty(keys[i]))
{ {
// Yes it has a key property, let's carry on down // Yes parentA has a key property, let's carry on down
value = parent[keys[i]]; value = parentA[keys[i]];
parent = parent[keys[i]]; parentA = parentA[keys[i]];
}
else if (parentB && parentB.hasOwnProperty(keys[i]))
{
// Yes parentB has a key property, let's carry on down
value = parentB[keys[i]];
parentB = parentB[keys[i]];
} }
else else
{ {