diff --git a/Gruntfile.js b/Gruntfile.js
index f228334f..17b86ec4 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -11,43 +11,53 @@ module.exports = function (grunt) {
compile_dir: 'dist',
src: {
phaser: [
+ 'build/p2.js',
'src/Intro.js',
'src/pixi/Pixi.js',
'src/Phaser.js',
'src/utils/Utils.js',
+
+ 'src/geom/Circle.js',
+ 'src/geom/Point.js',
+ 'src/geom/Rectangle.js',
+ 'src/geom/Line.js',
+ 'src/geom/Ellipse.js',
+ 'src/geom/Polygon.js',
+
'src/pixi/core/Matrix.js',
- 'src/pixi/core/Point.js',
- 'src/pixi/core/Rectangle.js',
- 'src/pixi/core/Polygon.js',
'src/pixi/display/DisplayObject.js',
'src/pixi/display/DisplayObjectContainer.js',
'src/pixi/display/Sprite.js',
- 'src/pixi/display/Stage.js',
- 'src/pixi/extras/CustomRenderable.js',
- 'src/pixi/extras/Strip.js',
- 'src/pixi/extras/Rope.js',
- 'src/pixi/extras/TilingSprite.js',
- 'src/pixi/filters/AbstractFilter.js',
+ 'src/pixi/display/SpriteBatch.js',
'src/pixi/filters/FilterBlock.js',
- 'src/pixi/primitives/Graphics.js',
- 'src/pixi/renderers/canvas/CanvasGraphics.js',
- 'src/pixi/renderers/canvas/CanvasRenderer.js',
- 'src/pixi/renderers/webgl/PixiShader.js',
- 'src/pixi/renderers/webgl/PrimitiveShader.js',
- 'src/pixi/renderers/webgl/StripShader.js',
- 'src/pixi/renderers/webgl/WebGLBatch.js',
- 'src/pixi/renderers/webgl/WebGLFilterManager.js',
- 'src/pixi/renderers/webgl/WebGLGraphics.js',
- 'src/pixi/renderers/webgl/WebGLRenderer.js',
- 'src/pixi/renderers/webgl/WebGLRenderGroup.js',
- 'src/pixi/renderers/webgl/WebGLShaders.js',
- 'src/pixi/text/BitmapText.js',
'src/pixi/text/Text.js',
+ 'src/pixi/text/BitmapText.js',
+ 'src/pixi/display/Stage.js',
+ 'src/pixi/utils/EventTarget.js',
+ 'src/pixi/utils/Polyk.js',
+ 'src/pixi/renderers/webgl/utils/WebGLShaderUtils.js',
+ 'src/pixi/renderers/webgl/shaders/PixiShader.js',
+ 'src/pixi/renderers/webgl/shaders/PixiFastShader.js',
+ 'src/pixi/renderers/webgl/shaders/StripShader.js',
+ 'src/pixi/renderers/webgl/shaders/PrimitiveShader.js',
+ 'src/pixi/renderers/webgl/utils/WebGLGraphics.js',
+ 'src/pixi/renderers/webgl/WebGLRenderer.js',
+ 'src/pixi/renderers/webgl/utils/WebGLMaskManager.js',
+ 'src/pixi/renderers/webgl/utils/WebGLShaderManager.js',
+ 'src/pixi/renderers/webgl/utils/WebGLSpriteBatch.js',
+ 'src/pixi/renderers/webgl/utils/WebGLFastSpriteBatch.js',
+ 'src/pixi/renderers/webgl/utils/WebGLFilterManager.js',
+ 'src/pixi/renderers/webgl/utils/FilterTexture.js',
+ 'src/pixi/renderers/canvas/utils/CanvasMaskManager.js',
+ 'src/pixi/renderers/canvas/utils/CanvasTinter.js',
+ 'src/pixi/renderers/canvas/CanvasRenderer.js',
+ 'src/pixi/renderers/canvas/CanvasGraphics.js',
+ 'src/pixi/primitives/Graphics.js',
+ 'src/pixi/extras/TilingSprite.js',
'src/pixi/textures/BaseTexture.js',
'src/pixi/textures/Texture.js',
'src/pixi/textures/RenderTexture.js',
- 'src/pixi/utils/EventTarget.js',
- 'src/pixi/utils/Polyk.js',
+
'src/core/Camera.js',
'src/core/State.js',
'src/core/StateManager.js',
@@ -61,6 +71,7 @@ module.exports = function (grunt) {
'src/core/Group.js',
'src/core/World.js',
'src/core/Game.js',
+
'src/input/Input.js',
'src/input/Key.js',
'src/input/Keyboard.js',
@@ -68,62 +79,77 @@ module.exports = function (grunt) {
'src/input/MSPointer.js',
'src/input/Pointer.js',
'src/input/Touch.js',
- 'src/input/InputHandler.js',
'src/input/Gamepad.js',
'src/input/SinglePad.js',
'src/input/GamepadButton.js',
+ 'src/input/InputHandler.js',
+
'src/gameobjects/Events.js',
'src/gameobjects/GameObjectFactory.js',
'src/gameobjects/BitmapData.js',
'src/gameobjects/Sprite.js',
+ 'src/gameobjects/Image.js',
'src/gameobjects/TileSprite.js',
'src/gameobjects/Text.js',
'src/gameobjects/BitmapText.js',
'src/gameobjects/Button.js',
'src/gameobjects/Graphics.js',
'src/gameobjects/RenderTexture.js',
+ 'src/gameobjects/SpriteBatch.js',
+ 'src/gameobjects/BitmapFont.js',
+
'src/system/Canvas.js',
'src/system/StageScaleMode.js',
'src/system/Device.js',
'src/system/RequestAnimationFrame.js',
- 'src/math/RandomDataGenerator.js',
+
'src/math/Math.js',
- 'src/math/QuadTree.js',
- 'src/geom/Circle.js',
- 'src/geom/Point.js',
- 'src/geom/Rectangle.js',
- 'src/geom/Polygon.js',
+ 'src/math/RandomDataGenerator.js',
+
'src/net/Net.js',
+
'src/tween/TweenManager.js',
'src/tween/Tween.js',
'src/tween/Easing.js',
+
'src/time/Time.js',
'src/time/Timer.js',
'src/time/TimerEvent.js',
+
'src/animation/AnimationManager.js',
'src/animation/Animation.js',
'src/animation/Frame.js',
'src/animation/FrameData.js',
'src/animation/AnimationParser.js',
+
'src/loader/Cache.js',
'src/loader/Loader.js',
'src/loader/LoaderParser.js',
+
'src/sound/Sound.js',
'src/sound/SoundManager.js',
+
'src/utils/Debug.js',
'src/utils/Color.js',
- 'src/physics/arcade/SAT.js',
- 'src/physics/arcade/ArcadePhysics.js',
- 'src/physics/arcade/Body.js',
+
+ 'src/physics/World.js',
+ 'src/physics/PointProxy.js',
+ 'src/physics/InversePointProxy.js',
+ 'src/physics/Body.js',
+ 'src/physics/Spring.js',
+ 'src/physics/Material.js',
+ 'src/physics/ContactMaterial.js',
+ 'src/physics/CollisionGroup.js',
+
'src/particles/Particles.js',
'src/particles/arcade/ArcadeParticles.js',
'src/particles/arcade/Emitter.js',
+
'src/tilemap/Tile.js',
'src/tilemap/Tilemap.js',
'src/tilemap/TilemapLayer.js',
'src/tilemap/TilemapParser.js',
- 'src/tilemap/Tileset.js',
- 'src/PixiPatch.js'
+ 'src/tilemap/Tileset.js'
]
},
pkg: grunt.file.readJSON('package.json'),
@@ -151,7 +177,7 @@ module.exports = function (grunt) {
uglify: {
phaser: {
options: {
- banner: '/*! Phaser v<%= pkg.version %> | (c) 2013 Photon Storm Ltd. */\n'
+ banner: '/*! Phaser v<%= pkg.version %> | (c) 2014 Photon Storm Ltd. */\n'
},
src: ['<%= umd.phaser.dest %>'],
dest: '<%= compile_dir %>/phaser.min.js'
@@ -170,7 +196,8 @@ module.exports = function (grunt) {
connect: {
root: {
options: {
- keepalive: true
+ keepalive: true,
+ hostname: '*'
}
}
}
diff --git a/README.md b/README.md
index 24d9d041..4e5daaae 100644
--- a/README.md
+++ b/README.md
@@ -61,8 +61,8 @@ Version 1.2 - "Shienar" - -in development-
Significant API changes:
-* Upgraded to Pixi.js 1.4.4
-* Group now extends PIXI.DisplayObjectContainer, rather than owning a _container property, which makes life a whole lot easier re: nesting.
+* Upgraded to Pixi.js 1.5.2
+* Group now extends PIXI.DisplayObjectContainer, rather than owning a _container property, which makes life a whole lot easier re: nesting and child iteration.
* Removed Sprite.group property. You can use Sprite.parent for all similar needs now.
* PIXI.Point is now aliased to Phaser.Point - saves on code duplication and works exactly the same.
* PIXI.Rectangle is now aliased to Phaser.Rectangle - saves on code duplication and works exactly the same.
@@ -70,28 +70,69 @@ Significant API changes:
* Sprite.deltaX and deltaY swapped to functions: Sprite.deltaX() and Sprite.deltaY()
* Sprite.crop() now takes a Phaser.Rectangle instead of explicit parameters.
* PixiPatch no longer needed, all features that it patched are now native in Pixi :)
-* Removed: Sprite.offset, center, topLeft, topRight, bottomRight, bottomLeft and bounds as no longer needed internally. Use Sprite.getBounds() to derive them.
+* Removed: Sprite.offset, center, topLeft, topRight, bottomRight, bottomLeft and bounds, as no longer needed internally. Use Sprite.getBounds() to derive them.
* Button now extends Phaser.Image not Phaser.Sprite, all the same functionality as before remains, just no animations or physics body.
+* Text.content has been replaced with Text.text. The Text class has a lot of new methods, check the docs!
+* Loader won't set crossOrigin on Images unless it's set to something. The default is false, it used to be '' and can be any valid string.
+* Sprite.input.pixelPerfect has been split into two: Sprite.input.pixelPerfectClick and Sprite.input.pixelPerfectOver (see new features)
+* Phaser.Stage now extends PIXI.Stage, rather than containing a _stage object.
+* If you set Sprite.exists to false it will also set Sprite.visible to false and remove its body from the physics world (if it has one).
+* If you set Sprite.exists to true it will also set Sprite.visible to true and add its body back into the physics world (if it has one).
+* Stage.scale has been moved to Game.scale. The same game scaling properties exist as before, but now accessed via Game.scale instead.
+* Stage.aspectRatio has been moved to StageScaleMode.sourceAspectRatio (so now game.scale.sourceAspectRatio)
+* Stage.scaleMode has been moved to StageScaleMode.scaleMode (so now game.scale.scaleMode)
+* Stage.fullScreenScaleMode has been moved to StageScaleMode.fullScreenScaleMode (so now game.scale.fullScreenScaleMode)
+* Stage.canvas has been moved to Game.canvas (which used to be a reference to Stage.canvas, but is now the actual object).
+* BitmapData.addTo removed and enhanced BitmapData.add so it can accept either a single Sprite/Image or an Array of them.
+* BitmapData has had all of the EaselJS functions removed. It was just taking up space and you can do it all via BitmapData.context directly.
+* BitmapText has had a bit of an overhaul - the signature for adding a BitmapText has changed to: x, y, font, text, size. See the docs and examples for details.
+* World preUpdate, update and postUpdate have all been moved to Stage. So all children are updated regardless where on the display list they live.
+* Cache.getImageKeys and similar has been removed, please use Cache.getKeys(Phaser.Cache.IMAGE) instead, this now supports all 10 Cache data types.
+* After defining tiles that collide on a Tilemap, you need to call Tilemap.generateCollisionData(layer) to populate the physics world with the data required.
+* Phaser.QuadTree has been removed from core and moved to a plugin. It's no longer required, nor works with the physics system.
New features:
-* Phaser.Image is a brand new display object perfect for logos, backgrounds, etc. You can scale, rotate, tint and blend and Image, but it has no animation, physics body or input events.
+* Phaser.Image is a brand new display object perfect for logos, backgrounds, etc. You can scale, rotate, tint, blend an get input events from an Image, but it has no animation, physics body.
* You can now use the hitArea property on Sprites and Image objects. hitArea can be a geometry object (Rectangle, Circle, Polygon, Ellipse) and is used in pointerOver checks.
* InputManager.getLocalPosition(displayObject, pointer, output) will return the local coordinates of the specified displayObject and pointer.
* InputManager.hitTest will test for pointer hits against a Sprite/Image, its hitArea (if set) or any of its children.
-
-
-New Examples:
+* Text has lots of new methods to help style it: Text.fill, Text.align, Text.stroke, etc.
+* Text now works happily with font names with spaces in them.
+* Text.setShadow applies a drop shadow to the Text being rendered. Control the x, y, color and blur.
+* Text.lineSpacing allows you to set additional spacing between each line that is rendered.
+* Text.inputEnabled allows you to enable all input events over Text objects: dragging, clicking, etc - anything that works on a Sprite works on Text now too.
+* Phaser.Ellipse added. A fully compatible port of the PIXI.Ellipse class, can be used in Sprite/Image hitArea tests.
+* Phaser.Polygon added. A fully compatible port of the PIXI.Polygon class, can be used in Sprite/Image hitArea tests.
+* InputHandler.pixelPerfectOver - performs a pixel perfect check to see if any pointer is over the current object (warning: very expensive!)
+* InputHandler.pixelPerfectClick - performs a pixel perfect check but only when the pointer touches/clicks on the current object.
+* TileSprite can now use a frame from a texture atlas or a sprite sheet.
+* TileSprites can now be animated. See new example :)
+* TileSprites have a new method: autoScroll(x, y) which sets an automatic scroll running (until stopped with TileSprite.stopScroll).
+* BitmapText now uses the new XML parser which should work under CocoonJS without clashes.
+* BitmapText signature changed so you can support fonts with spaces in their names.
+* Loader.bitmapFont now has 2 extra parameters: xSpacing and ySpacing. These allow you to add extra spacing to each letter or line of the font.
+* Added the new BitmapFont class. This is for rendering retro style fixed-width bitmap fonts into an Image object. It's a texture you can apply to a Sprite/Image.
+* Added Cache.updateFrameData which is really useful for swapping FrameData blocks in the cache.
+* Loader.physics now lets you load Lime + Corona JSON Physics data, which can be used with Body.loadPolygon and Body.loadData.
+* Cache.addPhysicsData and Cache.getPhysicsData allow you to store parsed JSON physics data in the cache, for sharing between Bodies.
+* fixedToCamera now works across all display objects. When enabled it will fix at its current x/y coordinate, but can be changed via cameraOffset.
+* fixedToCamrea now works for Groups as well :) You can fix a Group to the camera and it will influence its children.
+* Tilemap.createCollisionObjects will parse Tiled data for objectgroups and convert polyline instances into physics objects you can collide with in the world.
Updates:
+* Massive thanks to clark-stevenson for doing an amazing job update the TypeScript definitions file.
* Debug.renderRectangle has a new parameter: filled. If true it renders as with fillRect, if false strokeRect.
* Phaser.AnimationParser now sets the trimmed data directly for Pixi Texture frames. Tested across JSON Hash, JSON Data, Sprite Sheet and XML.
* Game.add.renderTexture now has the addToCache parameter. If set the texture will be stored in Game.Cache and can be retrieved with Cache.getTexture(key).
* Game.add.bitmapData now has the addToCache parameter. If set the texture will be stored in Game.Cache and can be retrieved with Cache.getBitmapData(key).
* The InputManager now sets the canvas style cursor to 'inherit' instead of 'default'.
+* World.reset now calls Camera.reset which sends the camera back to 0,0 and un-follows any object it may have been tracking.
+* Added hostname: '*' to the grunt-connect in Gruntfile.js (fixes #426)
+* Device, Canvas and GamePad classes all updated for better CocoonJS support (thanks Videlais)
Bug Fixes:
@@ -99,8 +140,21 @@ Bug Fixes:
* Explicitly paused Timer continues if you un-focus and focus the browser window (thanks georgiee)
* Added TimerEvent.pendingDelete and checks in Timer.update, so that removing an event in a callback no longer throws an exception (thanks georgiee)
* Fixed TypeScript defs on lines 1741-1748 (thanks wombatbuddy)
-* Previously if you used Sprite.crop() it would crop all Sprites using the same base image. It now takes a local copy of the texture data and crops just that.
+* Previously if you used Sprite.crop() it would crop all Sprites that shared the same base image. It now takes a local copy of the texture data and crops just that.
* Tilemap had the wrong @method signatures so most were missing from the docs.
+* Fixed bug where changing State would cause the camera to not reset if it was following an object.
+* Tile had 2 properties (callback and callbackContext) that were never assigned, updated to use the proper names (thanks ratkingsimon)
+* Fixed an error that would occur if you used InputHandler.onInputUp and the Sprite destroys itself during the event.
+* IE11 didn't populate the Device.ieVersion value. Now extracted from Trident revision, but still use Device.trident instead for IE11+ checks.
+* Fixed bug in Math.angleBetween where it was using the coordinates in the wrong order.
+* Previously using a Pixel Perfect check didn't work if the Sprite was rotated or had a non-zero anchor point, now works under all conditions + atlas frames.
+* If pixelPerfect Input Sprites overlapped each other the pixel check wasn't taken into consieration in the Pointer move method.
+
+
+TO DO:
+
+* If you change the frame name of a pixel precise input Sprite, it will fail all clicks on it after the frame change.
+
You can view the Change Log for all previous versions at https://github.com/photonstorm/phaser/changelog.md
diff --git a/build/config.php b/build/config.php
index 5e21cefd..de9615d2 100644
--- a/build/config.php
+++ b/build/config.php
@@ -36,6 +36,10 @@
+
+
+
+
@@ -44,11 +48,16 @@
+
+
+
+
*/
echo <<
@@ -58,16 +67,14 @@
+
+
-
-
-
-
@@ -92,8 +99,6 @@
-
-
@@ -136,6 +141,8 @@
+
+
@@ -144,7 +151,6 @@
-
@@ -172,9 +178,14 @@
-
-
-
+
+
+
+
+
+
+
+
diff --git a/build/p2.js b/build/p2.js
new file mode 100644
index 00000000..8835ae42
--- /dev/null
+++ b/build/p2.js
@@ -0,0 +1,10228 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013 p2.js authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.p2=e():"undefined"!=typeof global?self.p2=e():"undefined"!=typeof self&&(self.p2=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
+ //TODO: evaluate use of glm_invsqrt here?
+ len = 1 / Math.sqrt(len);
+ out[0] = a[0] * len;
+ out[1] = a[1] * len;
+ }
+ return out;
+};
+
+/**
+ * Caclulates the dot product of two vec2's
+ *
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {Number} dot product of a and b
+ */
+vec2.dot = function (a, b) {
+ return a[0] * b[0] + a[1] * b[1];
+};
+
+/**
+ * Computes the cross product of two vec2's
+ * Note that the cross product must by definition produce a 3D vector
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec3} out
+ */
+vec2.cross = function(out, a, b) {
+ var z = a[0] * b[1] - a[1] * b[0];
+ out[0] = out[1] = 0;
+ out[2] = z;
+ return out;
+};
+
+/**
+ * Performs a linear interpolation between two vec2's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {vec2} out
+ */
+vec2.lerp = function (out, a, b, t) {
+ var ax = a[0],
+ ay = a[1];
+ out[0] = ax + t * (b[0] - ax);
+ out[1] = ay + t * (b[1] - ay);
+ return out;
+};
+
+/**
+ * Transforms the vec2 with a mat2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the vector to transform
+ * @param {mat2} m matrix to transform with
+ * @returns {vec2} out
+ */
+vec2.transformMat2 = function(out, a, m) {
+ var x = a[0],
+ y = a[1];
+ out[0] = x * m[0] + y * m[1];
+ out[1] = x * m[2] + y * m[3];
+ return out;
+};
+
+/**
+ * Perform some operation over an array of vec2s.
+ *
+ * @param {Array} a the array of vectors to iterate over
+ * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed
+ * @param {Number} offset Number of elements to skip at the beginning of the array
+ * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array
+ * @param {Function} fn Function to call for each vector in the array
+ * @param {Object} [arg] additional argument to pass to fn
+ * @returns {Array} a
+ * @function
+ */
+vec2.forEach = (function() {
+ var vec = new Float32Array(2);
+
+ return function(a, stride, offset, count, fn, arg) {
+ var i, l;
+ if(!stride) {
+ stride = 2;
+ }
+
+ if(!offset) {
+ offset = 0;
+ }
+
+ if(count) {
+ l = Math.min((count * stride) + offset, a.length);
+ } else {
+ l = a.length;
+ }
+
+ for(i = offset; i < l; i += stride) {
+ vec[0] = a[i]; vec[1] = a[i+1];
+ fn(vec, vec, arg);
+ a[i] = vec[0]; a[i+1] = vec[1];
+ }
+
+ return a;
+ };
+})();
+
+/**
+ * Returns a string representation of a vector
+ *
+ * @param {vec2} vec vector to represent as a string
+ * @returns {String} string representation of the vector
+ */
+vec2.str = function (a) {
+ return 'vec2(' + a[0] + ', ' + a[1] + ')';
+};
+
+if(typeof(exports) !== 'undefined') {
+ exports.vec2 = vec2;
+}
+
+},{}],3:[function(require,module,exports){
+var Scalar = require('./Scalar');
+
+module.exports = Line;
+
+/**
+ * Container for line-related functions
+ * @class Line
+ */
+function Line(){};
+
+/**
+ * Compute the intersection between two lines.
+ * @static
+ * @method lineInt
+ * @param {Array} l1 Line vector 1
+ * @param {Array} l2 Line vector 2
+ * @param {Number} precision Precision to use when checking if the lines are parallel
+ * @return {Array} The intersection point.
+ */
+Line.lineInt = function(l1,l2,precision){
+ precision = precision || 0;
+ var i = [0,0]; // point
+ var a1, b1, c1, a2, b2, c2, det; // scalars
+ a1 = l1[1][1] - l1[0][1];
+ b1 = l1[0][0] - l1[1][0];
+ c1 = a1 * l1[0][0] + b1 * l1[0][1];
+ a2 = l2[1][1] - l2[0][1];
+ b2 = l2[0][0] - l2[1][0];
+ c2 = a2 * l2[0][0] + b2 * l2[0][1];
+ det = a1 * b2 - a2*b1;
+ if (!Scalar.eq(det, 0, precision)) { // lines are not parallel
+ i[0] = (b2 * c1 - b1 * c2) / det;
+ i[1] = (a1 * c2 - a2 * c1) / det;
+ }
+ return i;
+};
+
+/**
+ * Checks if two line segments intersects.
+ * @method segmentsIntersect
+ * @param {Array} p1 The start vertex of the first line segment.
+ * @param {Array} p2 The end vertex of the first line segment.
+ * @param {Array} q1 The start vertex of the second line segment.
+ * @param {Array} q2 The end vertex of the second line segment.
+ * @return {Boolean} True if the two line segments intersect
+ */
+Line.segmentsIntersect = function(p1, p2, q1, q2){
+ var dx = p2[0] - p1[0];
+ var dy = p2[1] - p1[1];
+ var da = q2[0] - q1[0];
+ var db = q2[1] - q1[1];
+
+ // segments are parallel
+ if(da*dy - db*dx == 0)
+ return false;
+
+ var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx)
+ var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy)
+
+ return (s>=0 && s<=1 && t>=0 && t<=1);
+};
+
+
+},{"./Scalar":6}],4:[function(require,module,exports){
+module.exports = Point;
+
+/**
+ * Point related functions
+ * @class Point
+ */
+function Point(){};
+
+/**
+ * Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order.
+ * @static
+ * @method area
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @return {Number}
+ */
+Point.area = function(a,b,c){
+ return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1])));
+};
+
+Point.left = function(a,b,c){
+ return Point.area(a,b,c) > 0;
+};
+
+Point.leftOn = function(a,b,c) {
+ return Point.area(a, b, c) >= 0;
+};
+
+Point.right = function(a,b,c) {
+ return Point.area(a, b, c) < 0;
+};
+
+Point.rightOn = function(a,b,c) {
+ return Point.area(a, b, c) <= 0;
+};
+
+var tmpPoint1 = [],
+ tmpPoint2 = [];
+
+/**
+ * Check if three points are collinear
+ * @method collinear
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision.
+ * @return {Boolean}
+ */
+Point.collinear = function(a,b,c,thresholdAngle) {
+ if(!thresholdAngle)
+ return Point.area(a, b, c) == 0;
+ else {
+ var ab = tmpPoint1,
+ bc = tmpPoint2;
+
+ ab[0] = b[0]-a[0];
+ ab[1] = b[1]-a[1];
+ bc[0] = c[0]-b[0];
+ bc[1] = c[1]-b[1];
+
+ var dot = ab[0]*bc[0] + ab[1]*bc[1],
+ magA = Math.sqrt(ab[0]*ab[0] + ab[1]*ab[1]),
+ magB = Math.sqrt(bc[0]*bc[0] + bc[1]*bc[1]),
+ angle = Math.acos(dot/(magA*magB));
+ return angle < thresholdAngle;
+ }
+};
+
+Point.sqdist = function(a,b){
+ var dx = b[0] - a[0];
+ var dy = b[1] - a[1];
+ return dx * dx + dy * dy;
+};
+
+},{}],5:[function(require,module,exports){
+var Line = require("./Line")
+, Point = require("./Point")
+, Scalar = require("./Scalar")
+
+module.exports = Polygon;
+
+/**
+ * Polygon class.
+ * @class Polygon
+ * @constructor
+ */
+function Polygon(){
+
+ /**
+ * Vertices that this polygon consists of. An array of array of numbers, example: [[0,0],[1,0],..]
+ * @property vertices
+ * @type {Array}
+ */
+ this.vertices = [];
+}
+
+/**
+ * Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle.
+ * @method at
+ * @param {Number} i
+ * @return {Array}
+ */
+Polygon.prototype.at = function(i){
+ var v = this.vertices,
+ s = v.length;
+ return v[i < 0 ? i % s + s : i % s];
+};
+
+/**
+ * Get first vertex
+ * @method first
+ * @return {Array}
+ */
+Polygon.prototype.first = function(){
+ return this.vertices[0];
+};
+
+/**
+ * Get last vertex
+ * @method last
+ * @return {Array}
+ */
+Polygon.prototype.last = function(){
+ return this.vertices[this.vertices.length-1];
+};
+
+/**
+ * Clear the polygon data
+ * @method clear
+ * @return {Array}
+ */
+Polygon.prototype.clear = function(){
+ this.vertices.length = 0;
+};
+
+/**
+ * Append points "from" to "to"-1 from an other polygon "poly" onto this one.
+ * @method append
+ * @param {Polygon} poly The polygon to get points from.
+ * @param {Number} from The vertex index in "poly".
+ * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending.
+ * @return {Array}
+ */
+Polygon.prototype.append = function(poly,from,to){
+ if(typeof(from) == "undefined") throw new Error("From is not given!");
+ if(typeof(to) == "undefined") throw new Error("To is not given!");
+
+ if(to-1 < from) throw new Error("lol1");
+ if(to > poly.vertices.length) throw new Error("lol2");
+ if(from < 0) throw new Error("lol3");
+
+ for(var i=from; i v[br][0])) {
+ br = i;
+ }
+ }
+
+ // reverse poly if clockwise
+ if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) {
+ this.reverse();
+ }
+};
+
+/**
+ * Reverse the vertices in the polygon
+ * @method reverse
+ */
+Polygon.prototype.reverse = function(){
+ var tmp = [];
+ for(var i=0, N=this.vertices.length; i!==N; i++){
+ tmp.push(this.vertices.pop());
+ }
+ this.vertices = tmp;
+};
+
+/**
+ * Check if a point in the polygon is a reflex point
+ * @method isReflex
+ * @param {Number} i
+ * @return {Boolean}
+ */
+Polygon.prototype.isReflex = function(i){
+ return Point.right(this.at(i - 1), this.at(i), this.at(i + 1));
+};
+
+var tmpLine1=[],
+ tmpLine2=[];
+
+/**
+ * Check if two vertices in the polygon can see each other
+ * @method canSee
+ * @param {Number} a Vertex index 1
+ * @param {Number} b Vertex index 2
+ * @return {Boolean}
+ */
+Polygon.prototype.canSee = function(a,b) {
+ var p, dist, l1=tmpLine1, l2=tmpLine2;
+
+ if (Point.leftOn(this.at(a + 1), this.at(a), this.at(b)) && Point.rightOn(this.at(a - 1), this.at(a), this.at(b))) {
+ return false;
+ }
+ dist = Point.sqdist(this.at(a), this.at(b));
+ for (var i = 0; i !== this.vertices.length; ++i) { // for each edge
+ if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges
+ continue;
+ if (Point.leftOn(this.at(a), this.at(b), this.at(i + 1)) && Point.rightOn(this.at(a), this.at(b), this.at(i))) { // if diag intersects an edge
+ l1[0] = this.at(a);
+ l1[1] = this.at(b);
+ l2[0] = this.at(i);
+ l2[1] = this.at(i + 1);
+ p = Line.lineInt(l1,l2);
+ if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b
+ return false;
+ }
+ }
+ }
+
+ return true;
+};
+
+/**
+ * Copy the polygon from vertex i to vertex j.
+ * @method copy
+ * @param {Number} i
+ * @param {Number} j
+ * @param {Polygon} [targetPoly] Optional target polygon to save in.
+ * @return {Polygon} The resulting copy.
+ */
+Polygon.prototype.copy = function(i,j,targetPoly){
+ var p = targetPoly || new Polygon();
+ p.clear();
+ if (i < j) {
+ // Insert all vertices from i to j
+ for(var k=i; k<=j; k++)
+ p.vertices.push(this.vertices[k]);
+
+ } else {
+
+ // Insert vertices 0 to j
+ for(var k=0; k<=j; k++)
+ p.vertices.push(this.vertices[k]);
+
+ // Insert vertices i to end
+ for(var k=i; k 0)
+ return this.slice(edges);
+ else
+ return [this];
+};
+
+/**
+ * Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons.
+ * @method slice
+ * @param {Array} cutEdges A list of edges, as returned by .getCutEdges()
+ * @return {Array}
+ */
+Polygon.prototype.slice = function(cutEdges){
+ if(cutEdges.length == 0) return [this];
+ if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length==2 && cutEdges[0][0] instanceof Array){
+
+ var polys = [this];
+
+ for(var i=0; i maxlevel){
+ console.warn("quickDecomp: max level ("+maxlevel+") reached.");
+ return result;
+ }
+
+ for (var i = 0; i < this.vertices.length; ++i) {
+ if (poly.isReflex(i)) {
+ reflexVertices.push(poly.vertices[i]);
+ upperDist = lowerDist = Number.MAX_VALUE;
+
+
+ for (var j = 0; j < this.vertices.length; ++j) {
+ if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j))
+ && Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge
+ p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection
+ if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly
+ d = Point.sqdist(poly.vertices[i], p);
+ if (d < lowerDist) { // keep only the closest intersection
+ lowerDist = d;
+ lowerInt = p;
+ lowerIndex = j;
+ }
+ }
+ }
+ if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1))
+ && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) {
+ p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1));
+ if (Point.left(poly.at(i - 1), poly.at(i), p)) {
+ d = Point.sqdist(poly.vertices[i], p);
+ if (d < upperDist) {
+ upperDist = d;
+ upperInt = p;
+ upperIndex = j;
+ }
+ }
+ }
+ }
+
+ // if there are no vertices to connect to, choose a point in the middle
+ if (lowerIndex == (upperIndex + 1) % this.vertices.length) {
+ //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")");
+ p[0] = (lowerInt[0] + upperInt[0]) / 2;
+ p[1] = (lowerInt[1] + upperInt[1]) / 2;
+ steinerPoints.push(p);
+
+ if (i < upperIndex) {
+ //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1);
+ lowerPoly.append(poly, i, upperIndex+1);
+ lowerPoly.vertices.push(p);
+ upperPoly.vertices.push(p);
+ if (lowerIndex != 0){
+ //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end());
+ upperPoly.append(poly,lowerIndex,poly.vertices.length);
+ }
+ //upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1);
+ upperPoly.append(poly,0,i+1);
+ } else {
+ if (i != 0){
+ //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end());
+ lowerPoly.append(poly,i,poly.vertices.length);
+ }
+ //lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1);
+ lowerPoly.append(poly,0,upperIndex+1);
+ lowerPoly.vertices.push(p);
+ upperPoly.vertices.push(p);
+ //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1);
+ upperPoly.append(poly,lowerIndex,i+1);
+ }
+ } else {
+ // connect to the closest point within the triangle
+ //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n");
+
+ if (lowerIndex > upperIndex) {
+ upperIndex += this.vertices.length;
+ }
+ closestDist = Number.MAX_VALUE;
+
+ if(upperIndex < lowerIndex){
+ return result;
+ }
+
+ for (var j = lowerIndex; j <= upperIndex; ++j) {
+ if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j))
+ && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) {
+ d = Point.sqdist(poly.at(i), poly.at(j));
+ if (d < closestDist) {
+ closestDist = d;
+ closestIndex = j % this.vertices.length;
+ }
+ }
+ }
+
+ if (i < closestIndex) {
+ lowerPoly.append(poly,i,closestIndex+1);
+ if (closestIndex != 0){
+ upperPoly.append(poly,closestIndex,v.length);
+ }
+ upperPoly.append(poly,0,i+1);
+ } else {
+ if (i != 0){
+ lowerPoly.append(poly,i,v.length);
+ }
+ lowerPoly.append(poly,0,closestIndex+1);
+ upperPoly.append(poly,closestIndex,i+1);
+ }
+ }
+
+ // solve smallest poly first
+ if (lowerPoly.vertices.length < upperPoly.vertices.length) {
+ lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ } else {
+ upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ }
+
+ return result;
+ }
+ }
+ result.push(this);
+
+ return result;
+};
+
+/**
+ * Remove collinear points in the polygon.
+ * @method removeCollinearPoints
+ * @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision.
+ * @return {Number} The number of points removed
+ */
+Polygon.prototype.removeCollinearPoints = function(precision){
+ var num = 0;
+ for(var i=this.vertices.length-1; this.vertices.length>3 && i>=0; --i){
+ if(Point.collinear(this.at(i-1),this.at(i),this.at(i+1),precision)){
+ // Remove the middle point
+ this.vertices.splice(i%this.vertices.length,1);
+ i--; // Jump one point forward. Otherwise we may get a chain removal
+ num++;
+ }
+ }
+ return num;
+};
+
+},{"./Line":3,"./Point":4,"./Scalar":6}],6:[function(require,module,exports){
+module.exports = Scalar;
+
+/**
+ * Scalar functions
+ * @class Scalar
+ */
+function Scalar(){}
+
+/**
+ * Check if two scalars are equal
+ * @static
+ * @method eq
+ * @param {Number} a
+ * @param {Number} b
+ * @param {Number} [precision]
+ * @return {Boolean}
+ */
+Scalar.eq = function(a,b,precision){
+ precision = precision || 0;
+ return Math.abs(a-b) < precision;
+};
+
+},{}],7:[function(require,module,exports){
+module.exports = {
+ Polygon : require("./Polygon"),
+ Point : require("./Point"),
+};
+
+},{"./Point":4,"./Polygon":5}],8:[function(require,module,exports){
+module.exports={
+ "name": "p2",
+ "version": "0.4.0",
+ "description": "A JavaScript 2D physics engine.",
+ "author": "Stefan Hedman (http://steffe.se)",
+ "keywords": [
+ "p2.js",
+ "p2",
+ "physics",
+ "engine",
+ "2d"
+ ],
+ "main": "./src/p2.js",
+ "engines": {
+ "node": "*"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/schteppe/p2.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/schteppe/p2.js/issues"
+ },
+ "licenses" : [
+ {
+ "type" : "MIT"
+ }
+ ],
+ "devDependencies" : {
+ "jshint" : "latest",
+ "nodeunit" : "latest",
+ "grunt": "~0.4.0",
+ "grunt-contrib-jshint": "~0.1.1",
+ "grunt-contrib-nodeunit": "~0.1.2",
+ "grunt-contrib-concat": "~0.1.3",
+ "grunt-contrib-uglify": "*",
+ "grunt-browserify" : "*",
+ "browserify":"*"
+ },
+ "dependencies" : {
+ "underscore":"*",
+ "poly-decomp" : "git://github.com/schteppe/poly-decomp.js",
+ "gl-matrix":"2.0.0",
+ "jsonschema":"*"
+ }
+}
+
+},{}],9:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, Utils = require('../utils/Utils')
+
+module.exports = AABB;
+
+/**
+ * Axis aligned bounding box class.
+ * @class AABB
+ * @constructor
+ * @param {Object} options
+ * @param {Array} upperBound
+ * @param {Array} lowerBound
+ */
+function AABB(options){
+
+ /**
+ * The lower bound of the bounding box.
+ * @property lowerBound
+ * @type {Array}
+ */
+ this.lowerBound = vec2.create();
+ if(options && options.lowerBound) vec2.copy(this.lowerBound, options.lowerBound);
+
+ /**
+ * The upper bound of the bounding box.
+ * @property upperBound
+ * @type {Array}
+ */
+ this.upperBound = vec2.create();
+ if(options && options.upperBound) vec2.copy(this.upperBound, options.upperBound);
+}
+
+var tmp = vec2.create();
+
+/**
+ * Set the AABB bounds from a set of points.
+ * @method setFromPoints
+ * @param {Array} points An array of vec2's.
+ */
+AABB.prototype.setFromPoints = function(points,position,angle){
+ var l = this.lowerBound,
+ u = this.upperBound;
+ vec2.set(l, Number.MAX_VALUE, Number.MAX_VALUE);
+ vec2.set(u, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ for(var i=0; i u[j]){
+ u[j] = p[j];
+ }
+ if(p[j] < l[j]){
+ l[j] = p[j];
+ }
+ }
+ }
+
+ // Add offset
+ if(position){
+ vec2.add(this.lowerBound, this.lowerBound, position);
+ vec2.add(this.upperBound, this.upperBound, position);
+ }
+};
+
+/**
+ * Copy bounds from an AABB to this AABB
+ * @method copy
+ * @param {AABB} aabb
+ */
+AABB.prototype.copy = function(aabb){
+ vec2.copy(this.lowerBound, aabb.lowerBound);
+ vec2.copy(this.upperBound, aabb.upperBound);
+};
+
+/**
+ * Extend this AABB so that it covers the given AABB too.
+ * @method extend
+ * @param {AABB} aabb
+ */
+AABB.prototype.extend = function(aabb){
+ // Loop over x and y
+ for(var i=0; i<2; i++){
+ // Extend lower bound
+ if(aabb.lowerBound[i] < this.lowerBound[i])
+ this.lowerBound[i] = aabb.lowerBound[i];
+
+ // Upper
+ if(aabb.upperBound[i] > this.upperBound[i])
+ this.upperBound[i] = aabb.upperBound[i];
+ }
+};
+
+/**
+ * Returns true if the given AABB overlaps this AABB.
+ * @method overlaps
+ * @param {AABB} aabb
+ * @return {Boolean}
+ */
+AABB.prototype.overlaps = function(aabb){
+ var l1 = this.lowerBound,
+ u1 = this.upperBound,
+ l2 = aabb.lowerBound,
+ u2 = aabb.upperBound;
+
+ // l2 u2
+ // |---------|
+ // |--------|
+ // l1 u1
+
+ return ((l2[0] <= u1[0] && u1[0] <= u2[0]) || (l1[0] <= u2[0] && u2[0] <= u1[0])) &&
+ ((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1]));
+};
+
+},{"../math/vec2":33,"../utils/Utils":49}],10:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+var Body = require('../objects/Body')
+
+module.exports = Broadphase;
+
+/**
+ * Base class for broadphase implementations.
+ * @class Broadphase
+ * @constructor
+ */
+function Broadphase(){
+
+ /**
+ * The resulting overlapping pairs. Will be filled with results during .getCollisionPairs().
+ * @property result
+ * @type {Array}
+ */
+ this.result = [];
+
+ /**
+ * The world to search for collision pairs in. To change it, use .setWorld()
+ * @property world
+ * @type {World}
+ */
+ this.world = null;
+};
+
+/**
+ * Set the world that we are searching for collision pairs in
+ * @method setWorld
+ * @param {World} world
+ */
+Broadphase.prototype.setWorld = function(world){
+ this.world = world;
+};
+
+/**
+ * Get all potential intersecting body pairs.
+ * @method getCollisionPairs
+ * @param {World} world The world to search in.
+ * @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d).
+ */
+Broadphase.prototype.getCollisionPairs = function(world){
+ throw new Error("getCollisionPairs must be implemented in a subclass!");
+};
+
+var dist = vec2.create();
+
+/**
+ * Check whether the bounding radius of two bodies overlap.
+ * @method boundingRadiusCheck
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {Boolean}
+ */
+Broadphase.boundingRadiusCheck = function(bodyA, bodyB){
+ vec2.sub(dist, bodyA.position, bodyB.position);
+ var d2 = vec2.squaredLength(dist),
+ r = bodyA.boundingRadius + bodyB.boundingRadius;
+ return d2 <= r*r;
+};
+
+/**
+ * Check whether the bounding radius of two bodies overlap.
+ * @method boundingRadiusCheck
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {Boolean}
+ */
+Broadphase.aabbCheck = function(bodyA, bodyB){
+ if(bodyA.aabbNeedsUpdate) bodyA.updateAABB();
+ if(bodyB.aabbNeedsUpdate) bodyB.updateAABB();
+ return bodyA.aabb.overlaps(bodyB.aabb);
+};
+
+/**
+ * Check whether two bodies are allowed to collide at all.
+ * @method canCollide
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {Boolean}
+ */
+Broadphase.canCollide = function(bodyA, bodyB){
+ // Cannot collide static bodies
+ if(bodyA.motionState & Body.STATIC && bodyB.motionState & Body.STATIC)
+ return false;
+
+ // Cannot collide sleeping bodies
+ if(bodyA.sleepState & Body.SLEEPING && bodyB.sleepState & Body.SLEEPING)
+ return false;
+
+ return true;
+};
+
+},{"../math/vec2":33,"../objects/Body":34}],11:[function(require,module,exports){
+var Circle = require('../shapes/Circle')
+, Plane = require('../shapes/Plane')
+, Particle = require('../shapes/Particle')
+, Broadphase = require('../collision/Broadphase')
+, vec2 = require('../math/vec2')
+
+module.exports = GridBroadphase;
+
+/**
+ * Broadphase that uses axis-aligned bins.
+ * @class GridBroadphase
+ * @constructor
+ * @extends Broadphase
+ * @param {number} xmin Lower x bound of the grid
+ * @param {number} xmax Upper x bound
+ * @param {number} ymin Lower y bound
+ * @param {number} ymax Upper y bound
+ * @param {number} nx Number of bins along x axis
+ * @param {number} ny Number of bins along y axis
+ * @todo test
+ */
+function GridBroadphase(xmin,xmax,ymin,ymax,nx,ny){
+ Broadphase.apply(this);
+
+ nx = nx || 10;
+ ny = ny || 10;
+
+ this.binsizeX = (xmax-xmin) / nx;
+ this.binsizeY = (ymax-ymin) / ny;
+ this.nx = nx;
+ this.ny = ny;
+ this.xmin = xmin;
+ this.ymin = ymin;
+ this.xmax = xmax;
+ this.ymax = ymax;
+};
+GridBroadphase.prototype = new Broadphase();
+
+/**
+ * Get a bin index given a world coordinate
+ * @method getBinIndex
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Number} Integer index
+ */
+GridBroadphase.prototype.getBinIndex = function(x,y){
+ var nx = this.nx,
+ ny = this.ny,
+ xmin = this.xmin,
+ ymin = this.ymin,
+ xmax = this.xmax,
+ ymax = this.ymax;
+
+ var xi = Math.floor(nx * (x - xmin) / (xmax-xmin));
+ var yi = Math.floor(ny * (y - ymin) / (ymax-ymin));
+ return xi*ny + yi;
+}
+
+/**
+ * Get collision pairs.
+ * @method getCollisionPairs
+ * @param {World} world
+ * @return {Array}
+ */
+GridBroadphase.prototype.getCollisionPairs = function(world){
+ var result = [],
+ collidingBodies = world.bodies,
+ Ncolliding = Ncolliding=collidingBodies.length,
+ binsizeX = this.binsizeX,
+ binsizeY = this.binsizeY;
+
+ var bins=[], Nbins=nx*ny;
+ for(var i=0; i= 0 && xi*(ny-1) + yi < Nbins)
+ bins[ xi*(ny-1) + yi ].push(bi);
+ }
+ }
+ } else if(si instanceof Plane){
+ // Put in all bins for now
+ if(bi.angle == 0){
+ var y = bi.position[1];
+ for(var j=0; j!==Nbins && ymin+binsizeY*(j-1) id2){
+ var tmp = id1;
+ id1 = id2;
+ id2 = tmp;
+ }
+ return !!this.collidingBodiesLastStep[id1 + " " + id2];
+};
+
+// "for in" loops aren't optimised in chrome... is there a better way to handle last-step collision memory?
+// Maybe do this: http://jsperf.com/reflection-vs-array-of-keys
+function clearObject(obj){
+ for(var i = 0, l = obj.keys.length; i < l; i++) {
+ delete obj[obj.keys[i]];
+ }
+ obj.keys.length = 0;
+ /*
+ for(var key in this.collidingBodiesLastStep)
+ delete this.collidingBodiesLastStep[key];
+ */
+}
+
+/**
+ * Throws away the old equations and gets ready to create new
+ * @method reset
+ * @param {World} world
+ */
+Narrowphase.prototype.reset = function(world){
+
+ // Save the colliding bodies data
+ clearObject(this.collidingBodiesLastStep);
+ for(var i=0; i!==this.contactEquations.length; i++){
+ var eq = this.contactEquations[i],
+ id1 = eq.bi.id,
+ id2 = eq.bj.id;
+ if(id1 > id2){
+ var tmp = id1;
+ id1 = id2;
+ id2 = tmp;
+ }
+ var key = id1 + " " + id2;
+ if(!this.collidingBodiesLastStep[key]){
+ this.collidingBodiesLastStep[key] = true;
+ this.collidingBodiesLastStep.keys.push(key);
+ }
+ }
+
+ if(this.reuseObjects){
+ var ce = this.contactEquations,
+ fe = this.frictionEquations,
+ rfe = this.reusableFrictionEquations,
+ rce = this.reusableContactEquations;
+ Utils.appendArray(rce,ce);
+ Utils.appendArray(rfe,fe);
+ }
+
+ // Reset
+ this.contactEquations.length = this.frictionEquations.length = 0;
+};
+
+/**
+ * Creates a ContactEquation, either by reusing an existing object or creating a new one.
+ * @method createContactEquation
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {ContactEquation}
+ */
+Narrowphase.prototype.createContactEquation = function(bodyA,bodyB,shapeA,shapeB){
+ var c = this.reusableContactEquations.length ? this.reusableContactEquations.pop() : new ContactEquation(bodyA,bodyB);
+ c.bi = bodyA;
+ c.bj = bodyB;
+ c.shapeA = shapeA;
+ c.shapeB = shapeB;
+ c.restitution = this.restitution;
+ c.firstImpact = !this.collidedLastStep(bodyA,bodyB);
+ c.enabled = true;
+
+ if(bodyA.allowSleep && (bodyA.motionState & Body.DYNAMIC) && !(bodyB.motionState & Body.STATIC || bodyB.sleepState === Body.SLEEPY))
+ bodyA.wakeUp();
+ if(bodyB.allowSleep && (bodyB.motionState & Body.DYNAMIC) && !(bodyA.motionState & Body.STATIC || bodyA.sleepState === Body.SLEEPY))
+ bodyB.wakeUp();
+
+ return c;
+};
+
+/**
+ * Creates a FrictionEquation, either by reusing an existing object or creating a new one.
+ * @method createFrictionEquation
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {FrictionEquation}
+ */
+Narrowphase.prototype.createFrictionEquation = function(bodyA,bodyB,shapeA,shapeB){
+ var c = this.reusableFrictionEquations.length ? this.reusableFrictionEquations.pop() : new FrictionEquation(bodyA,bodyB);
+ c.bi = bodyA;
+ c.bj = bodyB;
+ c.shapeA = shapeA;
+ c.shapeB = shapeB;
+ c.setSlipForce(this.slipForce);
+ c.frictionCoefficient = this.frictionCoefficient;
+ c.relativeVelocity = this.surfaceVelocity;
+ c.enabled = true;
+ return c;
+};
+
+/**
+ * Creates a FrictionEquation given the data in the ContactEquation. Uses same offset vectors ri and rj, but the tangent vector will be constructed from the collision normal.
+ * @method createFrictionFromContact
+ * @param {ContactEquation} contactEquation
+ * @return {FrictionEquation}
+ */
+Narrowphase.prototype.createFrictionFromContact = function(c){
+ var eq = this.createFrictionEquation(c.bi,c.bj,c.shapeA,c.shapeB);
+ vec2.copy(eq.ri, c.ri);
+ vec2.copy(eq.rj, c.rj);
+ vec2.rotate(eq.t, c.ni, -Math.PI / 2);
+ eq.contactEquation = c;
+ return eq;
+}
+
+/**
+ * Convex/line narrowphase
+ * @method convexLine
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Line} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.LINE | Shape.CONVEX] =
+Narrowphase.prototype.convexLine = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Line/rectangle narrowphase
+ * @method lineRectangle
+ * @param {Body} bi
+ * @param {Line} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Rectangle} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.LINE | Shape.RECTANGLE] =
+Narrowphase.prototype.lineRectangle = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Rectangle/capsule narrowphase
+ * @method rectangleCapsule
+ * @param {Body} bi
+ * @param {Rectangle} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.RECTANGLE] =
+Narrowphase.prototype.rectangleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Convex/capsule narrowphase
+ * @method convexCapsule
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.CONVEX] =
+Narrowphase.prototype.convexCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Capsule/line narrowphase
+ * @method lineCapsule
+ * @param {Body} bi
+ * @param {Line} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.LINE] =
+Narrowphase.prototype.lineCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Capsule/capsule narrowphase
+ * @method capsuleCapsule
+ * @param {Body} bi
+ * @param {Capsule} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.CAPSULE] =
+Narrowphase.prototype.capsuleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Line/line narrowphase
+ * @method lineLine
+ * @param {Body} bi
+ * @param {Line} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Line} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.LINE | Shape.LINE] =
+Narrowphase.prototype.lineLine = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Plane/line Narrowphase
+ * @method planeLine
+ * @param {Body} planeBody
+ * @param {Plane} planeShape
+ * @param {Array} planeOffset
+ * @param {Number} planeAngle
+ * @param {Body} lineBody
+ * @param {Line} lineShape
+ * @param {Array} lineOffset
+ * @param {Number} lineAngle
+ */
+Narrowphase.prototype[Shape.PLANE | Shape.LINE] =
+Narrowphase.prototype.planeLine = function(planeBody, planeShape, planeOffset, planeAngle,
+ lineBody, lineShape, lineOffset, lineAngle, justTest){
+ var worldVertex0 = tmp1,
+ worldVertex1 = tmp2,
+ worldVertex01 = tmp3,
+ worldVertex11 = tmp4,
+ worldEdge = tmp5,
+ worldEdgeUnit = tmp6,
+ dist = tmp7,
+ worldNormal = tmp8,
+ worldTangent = tmp9,
+ verts = tmpArray
+ numContacts = 0;
+
+ // Get start and end points
+ vec2.set(worldVertex0, -lineShape.length/2, 0);
+ vec2.set(worldVertex1, lineShape.length/2, 0);
+
+ // Not sure why we have to use worldVertex*1 here, but it won't work otherwise. Tired.
+ vec2.rotate(worldVertex01, worldVertex0, lineAngle);
+ vec2.rotate(worldVertex11, worldVertex1, lineAngle);
+
+ add(worldVertex01, worldVertex01, lineOffset);
+ add(worldVertex11, worldVertex11, lineOffset);
+
+ vec2.copy(worldVertex0,worldVertex01);
+ vec2.copy(worldVertex1,worldVertex11);
+
+ // Get vector along the line
+ sub(worldEdge, worldVertex1, worldVertex0);
+ vec2.normalize(worldEdgeUnit, worldEdge);
+
+ // Get tangent to the edge.
+ vec2.rotate(worldTangent, worldEdgeUnit, -Math.PI/2);
+
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ // Check line ends
+ verts[0] = worldVertex0;
+ verts[1] = worldVertex1;
+ for(var i=0; i pos0 && pos < pos1){
+ // We got contact!
+
+ if(justTest) return true;
+
+ var c = this.createContactEquation(circleBody,lineBody,si,sj);
+
+ vec2.scale(c.ni, orthoDist, -1);
+ vec2.normalize(c.ni, c.ni);
+
+ vec2.scale( c.ri, c.ni, circleRadius);
+ add(c.ri, c.ri, circleOffset);
+ sub(c.ri, c.ri, circleBody.position);
+
+ sub(c.rj, projectedPoint, lineOffset);
+ add(c.rj, c.rj, lineOffset);
+ sub(c.rj, c.rj, lineBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+
+ return 1;
+ }
+ }
+
+ // Add corner
+ // @todo reuse array object
+ verts[0] = worldVertex0;
+ verts[1] = worldVertex1;
+
+ for(var i=0; i 0){
+
+ // Now project the circle onto the edge
+ vec2.scale(orthoDist, worldTangent, d);
+ sub(projectedPoint, circleOffset, orthoDist);
+
+
+ // Check if the point is within the edge span
+ var pos = dot(worldEdgeUnit, projectedPoint);
+ var pos0 = dot(worldEdgeUnit, worldVertex0);
+ var pos1 = dot(worldEdgeUnit, worldVertex1);
+
+ if(pos > pos0 && pos < pos1){
+ // We got contact!
+
+ if(justTest) return true;
+
+ if(closestEdgeDistance === null || d*d 0){
+ for(var i=0; i= 0){
+
+ // Now project the particle onto the edge
+ vec2.scale(orthoDist, worldTangent, d);
+ sub(projectedPoint, particleOffset, orthoDist);
+
+ // Check if the point is within the edge span
+ var pos = dot(worldEdgeUnit, projectedPoint);
+ var pos0 = dot(worldEdgeUnit, worldVertex0);
+ var pos1 = dot(worldEdgeUnit, worldVertex1);
+
+ if(pos > pos0 && pos < pos1){
+ // We got contact!
+ if(justTest) return true;
+
+ if(closestEdgeDistance === null || d*d r*r)
+ return 0;
+
+ if(justTest) return true;
+
+ var c = this.createContactEquation(bodyA,bodyB,si,sj);
+ sub(c.ni, offsetB, offsetA);
+ vec2.normalize(c.ni,c.ni);
+
+ vec2.scale( c.ri, c.ni, shapeA.radius);
+ vec2.scale( c.rj, c.ni, -shapeB.radius);
+
+ add(c.ri, c.ri, offsetA);
+ sub(c.ri, c.ri, bodyA.position);
+
+ add(c.rj, c.rj, offsetB);
+ sub(c.rj, c.rj, bodyB.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+ return 1;
+};
+
+/**
+ * Plane/Convex Narrowphase
+ * @method planeConvex
+ * @param {Body} bi
+ * @param {Plane} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Convex} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Narrowphase.prototype[Shape.PLANE | Shape.CONVEX] =
+Narrowphase.prototype.planeConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var convexBody = bj,
+ convexOffset = xj,
+ convexShape = sj,
+ convexAngle = aj,
+ planeBody = bi,
+ planeShape = si,
+ planeOffset = xi,
+ planeAngle = ai;
+
+ var worldVertex = tmp1,
+ worldNormal = tmp2,
+ dist = tmp3;
+
+ var numReported = 0;
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ for(var i=0; i= 2)
+ break;
+ }
+ }
+
+ return numReported;
+};
+
+/**
+ * @method convexPlane
+ * @deprecated Use .planeConvex() instead!
+ */
+Narrowphase.prototype.convexPlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ console.warn("Narrowphase.prototype.convexPlane is deprecated. Use planeConvex instead!");
+ return this.planeConvex( bj,sj,xj,aj, bi,si,xi,ai, justTest );
+}
+
+/**
+ * Narrowphase for particle vs plane
+ * @method particlePlane
+ * @param {Body} bi The particle body
+ * @param {Particle} si Particle shape
+ * @param {Array} xi World position for the particle
+ * @param {Number} ai World angle for the particle
+ * @param {Body} bj Plane body
+ * @param {Plane} sj Plane shape
+ * @param {Array} xj World position for the plane
+ * @param {Number} aj World angle for the plane
+ */
+Narrowphase.prototype[Shape.PARTICLE | Shape.PLANE] =
+Narrowphase.prototype.particlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var particleBody = bi,
+ particleShape = si,
+ particleOffset = xi,
+ planeBody = bj,
+ planeShape = sj,
+ planeOffset = xj,
+ planeAngle = aj;
+
+ var dist = tmp1,
+ worldNormal = tmp2;
+
+ planeAngle = planeAngle || 0;
+
+ sub(dist, particleOffset, planeOffset);
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ var d = dot(dist, worldNormal);
+
+ if(d > 0) return 0;
+ if(justTest) return true;
+
+ var c = this.createContactEquation(planeBody,particleBody,sj,si);
+
+ vec2.copy(c.ni, worldNormal);
+ vec2.scale( dist, c.ni, d );
+ // dist is now the distance vector in the normal direction
+
+ // ri is the particle position projected down onto the plane, from the plane center
+ sub( c.ri, particleOffset, dist);
+ sub( c.ri, c.ri, planeBody.position);
+
+ // rj is from the body center to the particle center
+ sub( c.rj, particleOffset, particleBody.position );
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+ return 1;
+};
+
+/**
+ * Circle/Particle Narrowphase
+ * @method circleParticle
+ * @param {Body} bi
+ * @param {Circle} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Particle} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Narrowphase.prototype[Shape.CIRCLE | Shape.PARTICLE] =
+Narrowphase.prototype.circleParticle = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var circleBody = bi,
+ circleShape = si,
+ circleOffset = xi,
+ particleBody = bj,
+ particleShape = sj,
+ particleOffset = xj,
+ dist = tmp1;
+
+ sub(dist, particleOffset, circleOffset);
+ if(vec2.squaredLength(dist) > circleShape.radius*circleShape.radius) return 0;
+ if(justTest) return true;
+
+ var c = this.createContactEquation(circleBody,particleBody,si,sj);
+ vec2.copy(c.ni, dist);
+ vec2.normalize(c.ni,c.ni);
+
+ // Vector from circle to contact point is the normal times the circle radius
+ vec2.scale(c.ri, c.ni, circleShape.radius);
+ add(c.ri, c.ri, circleOffset);
+ sub(c.ri, c.ri, circleBody.position);
+
+ // Vector from particle center to contact point is zero
+ sub(c.rj, particleOffset, particleBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+
+ return 1;
+};
+
+var capsulePlane_tmpCircle = new Circle(1),
+ capsulePlane_tmp1 = vec2.create(),
+ capsulePlane_tmp2 = vec2.create(),
+ capsulePlane_tmp3 = vec2.create();
+
+Narrowphase.prototype[Shape.PLANE | Shape.CAPSULE] =
+Narrowphase.prototype.planeCapsule = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var end1 = capsulePlane_tmp1,
+ end2 = capsulePlane_tmp2,
+ circle = capsulePlane_tmpCircle,
+ dst = capsulePlane_tmp3;
+
+ // Compute world end positions
+ vec2.set(end1, -sj.length/2, 0);
+ vec2.rotate(end1,end1,aj);
+ add(end1,end1,xj);
+
+ vec2.set(end2, sj.length/2, 0);
+ vec2.rotate(end2,end2,aj);
+ add(end2,end2,xj);
+
+ circle.radius = sj.radius;
+
+ // Do Narrowphase as two circles
+ var numContacts1 = this.circlePlane(bj,circle,end1,0, bi,si,xi,ai, justTest),
+ numContacts2 = this.circlePlane(bj,circle,end2,0, bi,si,xi,ai, justTest);
+
+ if(justTest)
+ return numContacts1 || numContacts2;
+ else
+ return numContacts1 + numContacts2;
+};
+
+/**
+ * @method capsulePlane
+ * @deprecated Use .planeCapsule() instead!
+ */
+Narrowphase.prototype.capsulePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ console.warn("Narrowphase.prototype.capsulePlane() is deprecated. Use .planeCapsule() instead!");
+ return this.planeCapsule( bj,sj,xj,aj, bi,si,xi,ai, justTest );
+}
+
+/**
+ * Creates ContactEquations and FrictionEquations for a collision.
+ * @method circlePlane
+ * @param {Body} bi The first body that should be connected to the equations.
+ * @param {Circle} si The circle shape participating in the collision.
+ * @param {Array} xi Extra offset to take into account for the Shape, in addition to the one in circleBody.position. Will *not* be rotated by circleBody.angle (maybe it should, for sake of homogenity?). Set to null if none.
+ * @param {Body} bj The second body that should be connected to the equations.
+ * @param {Plane} sj The Plane shape that is participating
+ * @param {Array} xj Extra offset for the plane shape.
+ * @param {Number} aj Extra angle to apply to the plane
+ */
+Narrowphase.prototype[Shape.CIRCLE | Shape.PLANE] =
+Narrowphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var circleBody = bi,
+ circleShape = si,
+ circleOffset = xi, // Offset from body center, rotated!
+ planeBody = bj,
+ shapeB = sj,
+ planeOffset = xj,
+ planeAngle = aj;
+
+ planeAngle = planeAngle || 0;
+
+ // Vector from plane to circle
+ var planeToCircle = tmp1,
+ worldNormal = tmp2,
+ temp = tmp3;
+
+ sub(planeToCircle, circleOffset, planeOffset);
+
+ // World plane normal
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ // Normal direction distance
+ var d = dot(worldNormal, planeToCircle);
+
+ if(d > circleShape.radius) return 0; // No overlap. Abort.
+
+ if(justTest) return true;
+
+ // Create contact
+ var contact = this.createContactEquation(planeBody,circleBody,sj,si);
+
+ // ni is the plane world normal
+ vec2.copy(contact.ni, worldNormal);
+
+ // rj is the vector from circle center to the contact point
+ vec2.scale(contact.rj, contact.ni, -circleShape.radius);
+ add(contact.rj, contact.rj, circleOffset);
+ sub(contact.rj, contact.rj, circleBody.position);
+
+ // ri is the distance from plane center to contact.
+ vec2.scale(temp, contact.ni, d);
+ sub(contact.ri, planeToCircle, temp ); // Subtract normal distance vector from the distance vector
+ add(contact.ri, contact.ri, planeOffset);
+ sub(contact.ri, contact.ri, planeBody.position);
+
+ this.contactEquations.push(contact);
+
+ if(this.enableFriction){
+ this.frictionEquations.push( this.createFrictionFromContact(contact) );
+ }
+
+ return 1;
+};
+
+
+/**
+ * Convex/convex Narrowphase.See this article for more info.
+ * @method convexConvex
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Convex} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Narrowphase.prototype[Shape.CONVEX] =
+Narrowphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest, precision ){
+ var sepAxis = tmp1,
+ worldPoint = tmp2,
+ worldPoint0 = tmp3,
+ worldPoint1 = tmp4,
+ worldEdge = tmp5,
+ projected = tmp6,
+ penetrationVec = tmp7,
+ dist = tmp8,
+ worldNormal = tmp9,
+ numContacts = 0,
+ precision = precision || 1e-10;
+
+ var found = Narrowphase.findSeparatingAxis(si,xi,ai,sj,xj,aj,sepAxis);
+ if(!found) return 0;
+
+ // Make sure the separating axis is directed from shape i to shape j
+ sub(dist,xj,xi);
+ if(dot(sepAxis,dist) > 0){
+ vec2.scale(sepAxis,sepAxis,-1);
+ }
+
+ // Find edges with normals closest to the separating axis
+ var closestEdge1 = Narrowphase.getClosestEdge(si,ai,sepAxis,true), // Flipped axis
+ closestEdge2 = Narrowphase.getClosestEdge(sj,aj,sepAxis);
+
+ if(closestEdge1==-1 || closestEdge2==-1) return 0;
+
+ // Loop over the shapes
+ for(var k=0; k<2; k++){
+
+ var closestEdgeA = closestEdge1,
+ closestEdgeB = closestEdge2,
+ shapeA = si, shapeB = sj,
+ offsetA = xi, offsetB = xj,
+ angleA = ai, angleB = aj,
+ bodyA = bi, bodyB = bj;
+
+ if(k==0){
+ // Swap!
+ var tmp;
+ tmp = closestEdgeA; closestEdgeA = closestEdgeB; closestEdgeB = tmp;
+ tmp = shapeA; shapeA = shapeB; shapeB = tmp;
+ tmp = offsetA; offsetA = offsetB; offsetB = tmp;
+ tmp = angleA; angleA = angleB; angleB = tmp;
+ tmp = bodyA; bodyA = bodyB; bodyB = tmp;
+ }
+
+ // Loop over 2 points in convex B
+ for(var j=closestEdgeB; j max) max = value;
+ if(min === null || value < min) min = value;
+ }
+
+ if(min > max){
+ var t = min;
+ min = max;
+ max = t;
+ }
+
+ // Project the position of the body onto the axis - need to add this to the result
+ var offset = dot(convexOffset, worldAxis);
+
+ vec2.set( result, min + offset, max + offset);
+};
+
+// .findSeparatingAxis is called by other functions, need local tmp vectors
+var fsa_tmp1 = vec2.fromValues(0,0)
+, fsa_tmp2 = vec2.fromValues(0,0)
+, fsa_tmp3 = vec2.fromValues(0,0)
+, fsa_tmp4 = vec2.fromValues(0,0)
+, fsa_tmp5 = vec2.fromValues(0,0)
+, fsa_tmp6 = vec2.fromValues(0,0)
+
+/**
+ * Find a separating axis between the shapes, that maximizes the separating distance between them.
+ * @method findSeparatingAxis
+ * @static
+ * @param {Convex} c1
+ * @param {Array} offset1
+ * @param {Number} angle1
+ * @param {Convex} c2
+ * @param {Array} offset2
+ * @param {Number} angle2
+ * @param {Array} sepAxis The resulting axis
+ * @return {Boolean} Whether the axis could be found.
+ */
+Narrowphase.findSeparatingAxis = function(c1,offset1,angle1,c2,offset2,angle2,sepAxis){
+ var maxDist = null,
+ overlap = false,
+ found = false,
+ edge = fsa_tmp1,
+ worldPoint0 = fsa_tmp2,
+ worldPoint1 = fsa_tmp3,
+ normal = fsa_tmp4,
+ span1 = fsa_tmp5,
+ span2 = fsa_tmp6;
+
+ for(var j=0; j!==2; j++){
+ var c = c1,
+ angle = angle1;
+ if(j===1){
+ c = c2;
+ angle = angle2;
+ }
+
+ for(var i=0; i!==c.vertices.length; i++){
+ // Get the world edge
+ vec2.rotate(worldPoint0, c.vertices[i], angle);
+ vec2.rotate(worldPoint1, c.vertices[(i+1)%c.vertices.length], angle);
+
+ sub(edge, worldPoint1, worldPoint0);
+
+ // Get normal - just rotate 90 degrees since vertices are given in CCW
+ vec2.rotate(normal, edge, -Math.PI / 2);
+ vec2.normalize(normal,normal);
+
+ // Project hulls onto that normal
+ Narrowphase.projectConvexOntoAxis(c1,offset1,angle1,normal,span1);
+ Narrowphase.projectConvexOntoAxis(c2,offset2,angle2,normal,span2);
+
+ // Order by span position
+ var a=span1,
+ b=span2,
+ swapped = false;
+ if(span1[0] > span2[0]){
+ b=span1;
+ a=span2;
+ swapped = true;
+ }
+
+ // Get separating distance
+ var dist = b[0] - a[1];
+ overlap = dist < 0;
+
+ if(maxDist===null || dist > maxDist){
+ vec2.copy(sepAxis, normal);
+ maxDist = dist;
+ found = overlap;
+ }
+ }
+ }
+
+ return found;
+};
+
+// .getClosestEdge is called by other functions, need local tmp vectors
+var gce_tmp1 = vec2.fromValues(0,0)
+, gce_tmp2 = vec2.fromValues(0,0)
+, gce_tmp3 = vec2.fromValues(0,0)
+
+/**
+ * Get the edge that has a normal closest to an axis.
+ * @method getClosestEdge
+ * @static
+ * @param {Convex} c
+ * @param {Number} angle
+ * @param {Array} axis
+ * @param {Boolean} flip
+ * @return {Number} Index of the edge that is closest. This index and the next spans the resulting edge. Returns -1 if failed.
+ */
+Narrowphase.getClosestEdge = function(c,angle,axis,flip){
+ var localAxis = gce_tmp1,
+ edge = gce_tmp2,
+ normal = gce_tmp3;
+
+ // Convert the axis to local coords of the body
+ vec2.rotate(localAxis, axis, -angle);
+ if(flip){
+ vec2.scale(localAxis,localAxis,-1);
+ }
+
+ var closestEdge = -1,
+ N = c.vertices.length,
+ halfPi = Math.PI / 2;
+ for(var i=0; i!==N; i++){
+ // Get the edge
+ sub(edge, c.vertices[(i+1)%N], c.vertices[i%N]);
+
+ // Get normal - just rotate 90 degrees since vertices are given in CCW
+ vec2.rotate(normal, edge, -halfPi);
+ vec2.normalize(normal,normal);
+
+ var d = dot(normal,localAxis);
+ if(closestEdge == -1 || d > maxDot){
+ closestEdge = i % N;
+ maxDot = d;
+ }
+ }
+
+ return closestEdge;
+};
+
+
+},{"../equations/ContactEquation":23,"../equations/FrictionEquation":25,"../math/vec2":33,"../objects/Body":34,"../shapes/Circle":38,"../shapes/Shape":44,"../utils/Utils":49}],14:[function(require,module,exports){
+var Plane = require("../shapes/Plane");
+var Broadphase = require("../collision/Broadphase");
+
+module.exports = {
+ QuadTree : QuadTree,
+ Node : Node,
+ BoundsNode : BoundsNode,
+};
+
+/**
+ * QuadTree data structure. See https://github.com/mikechambers/ExamplesByMesh/tree/master/JavaScript/QuadTree
+ * @class QuadTree
+ * @constructor
+ * @param {Object} An object representing the bounds of the top level of the QuadTree. The object
+ * should contain the following properties : x, y, width, height
+ * @param {Boolean} pointQuad Whether the QuadTree will contain points (true), or items with bounds
+ * (width / height)(false). Default value is false.
+ * @param {Number} maxDepth The maximum number of levels that the quadtree will create. Default is 4.
+ * @param {Number} maxChildren The maximum number of children that a node can contain before it is split into sub-nodes.
+ */
+function QuadTree(bounds, pointQuad, maxDepth, maxChildren){
+ var node;
+ if(pointQuad){
+ node = new Node(bounds, 0, maxDepth, maxChildren);
+ } else {
+ node = new BoundsNode(bounds, 0, maxDepth, maxChildren);
+ }
+
+ /**
+ * The root node of the QuadTree which covers the entire area being segmented.
+ * @property root
+ * @type Node
+ */
+ this.root = node;
+}
+
+/**
+ * Inserts an item into the QuadTree.
+ * @method insert
+ * @param {Object|Array} item The item or Array of items to be inserted into the QuadTree. The item should expose x, y
+ * properties that represents its position in 2D space.
+ */
+QuadTree.prototype.insert = function(item){
+ if(item instanceof Array){
+ var len = item.length;
+ for(var i = 0; i < len; i++){
+ this.root.insert(item[i]);
+ }
+ } else {
+ this.root.insert(item);
+ }
+}
+
+/**
+ * Clears all nodes and children from the QuadTree
+ * @method clear
+ */
+QuadTree.prototype.clear = function(){
+ this.root.clear();
+}
+
+/**
+ * Retrieves all items / points in the same node as the specified item / point. If the specified item
+ * overlaps the bounds of a node, then all children in both nodes will be returned.
+ * @method retrieve
+ * @param {Object} item An object representing a 2D coordinate point (with x, y properties), or a shape
+ * with dimensions (x, y, width, height) properties.
+ */
+QuadTree.prototype.retrieve = function(item){
+ //get a copy of the array of items
+ var out = this.root.retrieve(item).slice(0);
+ return out;
+}
+
+QuadTree.prototype.getCollisionPairs = function(world){
+
+ var result = [];
+
+ // Add all bodies
+ this.insert(world.bodies);
+
+ /*
+ console.log("bodies",world.bodies.length);
+ console.log("maxDepth",this.root.maxDepth,"maxChildren",this.root.maxChildren);
+ */
+
+ for(var i=0; i!==world.bodies.length; i++){
+ var b = world.bodies[i],
+ items = this.retrieve(b);
+
+ //console.log("items",items.length);
+
+ // Check results
+ for(var j=0, len=items.length; j!==len; j++){
+ var item = items[j];
+
+ if(b === item) continue; // Do not add self
+
+ // Check if they were already added
+ var found = false;
+ for(var k=0, numAdded=result.length; k= this.maxDepth) && len > this.maxChildren) {
+ this.subdivide();
+
+ for(var i = 0; i < len; i++){
+ this.insert(this.children[i]);
+ }
+
+ this.children.length = 0;
+ }
+}
+
+Node.prototype.retrieve = function(item){
+ if(this.nodes.length){
+ var index = this.findIndex(item);
+ return this.nodes[index].retrieve(item);
+ }
+
+ return this.children;
+}
+
+Node.prototype.findIndex = function(item){
+ var b = this.bounds;
+ var left = (item.position[0]-item.boundingRadius > b.x + b.width / 2) ? false : true;
+ var top = (item.position[1]-item.boundingRadius > b.y + b.height / 2) ? false : true;
+
+ if(item instanceof Plane){
+ left = top = false; // Will overlap the left/top boundary since it is infinite
+ }
+
+ //top left
+ var index = Node.TOP_LEFT;
+ if(left){
+ if(!top){
+ index = Node.BOTTOM_LEFT;
+ }
+ } else {
+ if(top){
+ index = Node.TOP_RIGHT;
+ } else {
+ index = Node.BOTTOM_RIGHT;
+ }
+ }
+
+ return index;
+}
+
+
+Node.prototype.subdivide = function(){
+ var depth = this.depth + 1;
+
+ var bx = this.bounds.x;
+ var by = this.bounds.y;
+
+ //floor the values
+ var b_w_h = (this.bounds.width / 2);
+ var b_h_h = (this.bounds.height / 2);
+ var bx_b_w_h = bx + b_w_h;
+ var by_b_h_h = by + b_h_h;
+
+ //top left
+ this.nodes[Node.TOP_LEFT] = new this.classConstructor({
+ x:bx,
+ y:by,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+ //top right
+ this.nodes[Node.TOP_RIGHT] = new this.classConstructor({
+ x:bx_b_w_h,
+ y:by,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+ //bottom left
+ this.nodes[Node.BOTTOM_LEFT] = new this.classConstructor({
+ x:bx,
+ y:by_b_h_h,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+
+ //bottom right
+ this.nodes[Node.BOTTOM_RIGHT] = new this.classConstructor({
+ x:bx_b_w_h,
+ y:by_b_h_h,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+}
+
+Node.prototype.clear = function(){
+ this.children.length = 0;
+
+ var len = this.nodes.length;
+ for(var i = 0; i < len; i++){
+ this.nodes[i].clear();
+ }
+
+ this.nodes.length = 0;
+}
+
+
+// BoundsQuadTree
+
+function BoundsNode(bounds, depth, maxChildren, maxDepth){
+ Node.call(this, bounds, depth, maxChildren, maxDepth);
+ this.stuckChildren = [];
+}
+
+BoundsNode.prototype = new Node();
+BoundsNode.prototype.classConstructor = BoundsNode;
+BoundsNode.prototype.stuckChildren = null;
+
+//we use this to collect and conctenate items being retrieved. This way
+//we dont have to continuously create new Array instances.
+//Note, when returned from QuadTree.retrieve, we then copy the array
+BoundsNode.prototype.out = [];
+
+BoundsNode.prototype.insert = function(item){
+ if(this.nodes.length){
+ var index = this.findIndex(item);
+ var node = this.nodes[index];
+
+ /*
+ console.log("radius:",item.boundingRadius);
+ console.log("item x:",item.position[0] - item.boundingRadius,"x range:",node.bounds.x,node.bounds.x+node.bounds.width);
+ console.log("item y:",item.position[1] - item.boundingRadius,"y range:",node.bounds.y,node.bounds.y+node.bounds.height);
+ */
+
+ //todo: make _bounds bounds
+ if( !(item instanceof Plane) && // Plane is infinite.. Make it a "stuck" child
+ item.position[0] - item.boundingRadius >= node.bounds.x &&
+ item.position[0] + item.boundingRadius <= node.bounds.x + node.bounds.width &&
+ item.position[1] - item.boundingRadius >= node.bounds.y &&
+ item.position[1] + item.boundingRadius <= node.bounds.y + node.bounds.height){
+ this.nodes[index].insert(item);
+ } else {
+ this.stuckChildren.push(item);
+ }
+
+ return;
+ }
+
+ this.children.push(item);
+
+ var len = this.children.length;
+
+ if(this.depth < this.maxDepth && len > this.maxChildren){
+ this.subdivide();
+
+ for(var i=0; i=0;j--) {
+ if(a[j].aabb.lowerBound[0] <= v.aabb.lowerBound[0])
+ break;
+ a[j+1] = a[j];
+ }
+ a[j+1] = v;
+ }
+ return a;
+};
+
+/**
+ * Sorts bodies along the Y axis.
+ * @method sortAxisListY
+ * @param {Array} a
+ * @return {Array}
+ */
+SAPBroadphase.sortAxisListY = function(a){
+ for(var i=1,l=a.length;i=0;j--) {
+ if(a[j].aabb.lowerBound[1] <= v.aabb.lowerBound[1])
+ break;
+ a[j+1] = a[j];
+ }
+ a[j+1] = v;
+ }
+ return a;
+};
+
+var preliminaryList = { keys:[] };
+
+/**
+ * Get the colliding pairs
+ * @method getCollisionPairs
+ * @param {World} world
+ * @return {Array}
+ */
+SAPBroadphase.prototype.getCollisionPairs = function(world){
+ var bodiesX = this.axisListX,
+ bodiesY = this.axisListY,
+ result = this.result,
+ axisIndex = this.axisIndex,
+ i,j;
+
+ result.length = 0;
+
+ // Update all AABBs if needed
+ for(i=0; i!==bodiesX.length; i++){
+ var b = bodiesX[i];
+ if(b.aabbNeedsUpdate) b.updateAABB();
+ }
+
+ // Sort the lists
+ SAPBroadphase.sortAxisListX(bodiesX);
+ SAPBroadphase.sortAxisListY(bodiesY);
+
+ // Look through the X list
+ for(i=0, N=bodiesX.length; i!==N; i++){
+ var bi = bodiesX[i];
+
+ for(j=i+1; jthis tutorial.
+ *
+ * @class PrismaticConstraint
+ * @constructor
+ * @extends {Constraint}
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Object} options
+ * @param {Number} options.maxForce Max force to be applied by the constraint
+ * @param {Array} options.localAnchorA Body A's anchor point, defined in its own local frame.
+ * @param {Array} options.localAnchorB Body B's anchor point, defined in its own local frame.
+ * @param {Array} options.localAxisA An axis, defined in body A frame, that body B's anchor point may slide along.
+ * @param {Boolean} options.disableRotationalLock If set to true, bodyB will be free to rotate around its anchor point.
+ */
+function PrismaticConstraint(bodyA,bodyB,options){
+ options = options || {};
+ Constraint.call(this,bodyA,bodyB);
+
+ // Get anchors
+ var localAnchorA = vec2.fromValues(0,0),
+ localAxisA = vec2.fromValues(1,0),
+ localAnchorB = vec2.fromValues(0,0);
+ if(options.localAnchorA) vec2.copy(localAnchorA, options.localAnchorA);
+ if(options.localAxisA) vec2.copy(localAxisA, options.localAxisA);
+ if(options.localAnchorB) vec2.copy(localAnchorB, options.localAnchorB);
+
+ /**
+ * @property localAnchorA
+ * @type {Array}
+ */
+ this.localAnchorA = localAnchorA;
+
+ /**
+ * @property localAnchorB
+ * @type {Array}
+ */
+ this.localAnchorB = localAnchorB;
+
+ /**
+ * @property localAxisA
+ * @type {Array}
+ */
+ this.localAxisA = localAxisA;
+
+ /*
+
+ The constraint violation for the common axis point is
+
+ g = ( xj + rj - xi - ri ) * t := gg*t
+
+ where r are body-local anchor points, and t is a tangent to the constraint axis defined in body i frame.
+
+ gdot = ( vj + wj x rj - vi - wi x ri ) * t + ( xj + rj - xi - ri ) * ( wi x t )
+
+ Note the use of the chain rule. Now we identify the jacobian
+
+ G*W = [ -t -ri x t + t x gg t rj x t ] * [vi wi vj wj]
+
+ The rotational part is just a rotation lock.
+
+ */
+
+ var maxForce = this.maxForce = typeof(options.maxForce)!="undefined" ? options.maxForce : Number.MAX_VALUE;
+
+ // Translational part
+ var trans = new Equation(bodyA,bodyB,-maxForce,maxForce);
+ var ri = new vec2.create(),
+ rj = new vec2.create(),
+ gg = new vec2.create(),
+ t = new vec2.create();
+ trans.computeGq = function(){
+ // g = ( xj + rj - xi - ri ) * t
+ return vec2.dot(gg,t);
+ };
+ trans.update = function(){
+ var G = this.G,
+ xi = bodyA.position,
+ xj = bodyB.position;
+ vec2.rotate(ri,localAnchorA,bodyA.angle);
+ vec2.rotate(rj,localAnchorB,bodyB.angle);
+ vec2.add(gg,xj,rj);
+ vec2.sub(gg,gg,xi);
+ vec2.sub(gg,gg,ri);
+ vec2.rotate(t,localAxisA,bodyA.angle+Math.PI/2);
+
+ G[0] = -t[0];
+ G[1] = -t[1];
+ G[2] = -vec2.crossLength(ri,t) + vec2.crossLength(t,gg);
+ G[3] = t[0];
+ G[4] = t[1];
+ G[5] = vec2.crossLength(rj,t);
+ }
+ this.equations.push(trans);
+
+ // Rotational part
+ if(!options.disableRotationalLock){
+ var rot = new RotationalLockEquation(bodyA,bodyB,-maxForce,maxForce);
+ this.equations.push(rot);
+ }
+
+ /**
+ * The position of anchor A relative to anchor B, along the constraint axis.
+ * @property position
+ * @type {Number}
+ */
+ this.position = 0;
+
+ this.velocity = 0;
+
+ /**
+ * Set to true to enable lower limit.
+ * @property lowerLimitEnabled
+ * @type {Boolean}
+ */
+ this.lowerLimitEnabled = false;
+
+ /**
+ * Set to true to enable upper limit.
+ * @property upperLimitEnabled
+ * @type {Boolean}
+ */
+ this.upperLimitEnabled = false;
+
+ /**
+ * Lower constraint limit. The constraint position is forced to be larger than this value.
+ * @property lowerLimit
+ * @type {Number}
+ */
+ this.lowerLimit = 0;
+
+ /**
+ * Upper constraint limit. The constraint position is forced to be smaller than this value.
+ * @property upperLimit
+ * @type {Number}
+ */
+ this.upperLimit = 1;
+
+ // Equations used for limits
+ this.upperLimitEquation = new ContactEquation(bodyA,bodyB);
+ this.lowerLimitEquation = new ContactEquation(bodyA,bodyB);
+
+ // Set max/min forces
+ this.upperLimitEquation.minForce = this.lowerLimitEquation.minForce = 0;
+ this.upperLimitEquation.maxForce = this.lowerLimitEquation.maxForce = maxForce;
+
+ /**
+ * Equation used for the motor.
+ * @property motorEquation
+ * @type {Equation}
+ */
+ this.motorEquation = new Equation(bodyA,bodyB);
+
+ /**
+ * The current motor state. Enable or disable the motor using .enableMotor
+ * @property motorEnabled
+ * @type {Boolean}
+ */
+ this.motorEnabled = false;
+
+ /**
+ * Set the target speed for the motor.
+ * @property motorSpeed
+ * @type {Number}
+ */
+ this.motorSpeed = 0;
+
+ var that = this;
+ var motorEquation = this.motorEquation;
+ var old = motorEquation.computeGW;
+ motorEquation.computeGq = function(){ return 0; };
+ motorEquation.computeGW = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ vi = bi.velocity,
+ vj = bj.velocity,
+ wi = bi.angularVelocity,
+ wj = bj.angularVelocity;
+ return this.transformedGmult(G,vi,wi,vj,wj) + that.motorSpeed;
+ };
+}
+
+PrismaticConstraint.prototype = new Constraint();
+
+var worldAxisA = vec2.create(),
+ worldAnchorA = vec2.create(),
+ worldAnchorB = vec2.create(),
+ orientedAnchorA = vec2.create(),
+ orientedAnchorB = vec2.create(),
+ tmp = vec2.create();
+
+/**
+ * Update the constraint equations. Should be done if any of the bodies changed position, before solving.
+ * @method update
+ */
+PrismaticConstraint.prototype.update = function(){
+ var eqs = this.equations,
+ trans = eqs[0],
+ upperLimit = this.upperLimit,
+ lowerLimit = this.lowerLimit,
+ upperLimitEquation = this.upperLimitEquation,
+ lowerLimitEquation = this.lowerLimitEquation,
+ bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ localAxisA = this.localAxisA,
+ localAnchorA = this.localAnchorA,
+ localAnchorB = this.localAnchorB;
+
+ trans.update();
+
+ // Transform local things to world
+ vec2.rotate(worldAxisA, localAxisA, bodyA.angle);
+ vec2.rotate(orientedAnchorA, localAnchorA, bodyA.angle);
+ vec2.add(worldAnchorA, orientedAnchorA, bodyA.position);
+ vec2.rotate(orientedAnchorB, localAnchorB, bodyB.angle);
+ vec2.add(worldAnchorB, orientedAnchorB, bodyB.position);
+
+ var relPosition = this.position = vec2.dot(worldAnchorB,worldAxisA) - vec2.dot(worldAnchorA,worldAxisA);
+
+ // Motor
+ if(this.motorEnabled){
+ // G = [ a a x ri -a -a x rj ]
+ var G = this.motorEquation.G;
+ G[0] = worldAxisA[0];
+ G[1] = worldAxisA[1];
+ G[2] = vec2.crossLength(worldAxisA,orientedAnchorB);
+ G[3] = -worldAxisA[0];
+ G[4] = -worldAxisA[1];
+ G[5] = -vec2.crossLength(worldAxisA,orientedAnchorA);
+ }
+
+ /*
+ Limits strategy:
+ Add contact equation, with normal along the constraint axis.
+ min/maxForce is set so the constraint is repulsive in the correct direction.
+ Some offset is added to either equation.ri or .rj to get the correct upper/lower limit.
+
+ ^
+ |
+ upperLimit x
+ | ------
+ anchorB x<---| B |
+ | | |
+ ------ | ------
+ | | |
+ | A |-->x anchorA
+ ------ |
+ x lowerLimit
+ |
+ axis
+ */
+
+ if(this.upperLimitEnabled && relPosition > upperLimit){
+ // Update contact constraint normal, etc
+ vec2.scale(upperLimitEquation.ni, worldAxisA, -1);
+ vec2.sub(upperLimitEquation.ri, worldAnchorA, bodyA.position);
+ vec2.sub(upperLimitEquation.rj, worldAnchorB, bodyB.position);
+ vec2.scale(tmp,worldAxisA,upperLimit);
+ vec2.add(upperLimitEquation.ri,upperLimitEquation.ri,tmp);
+ if(eqs.indexOf(upperLimitEquation)==-1)
+ eqs.push(upperLimitEquation);
+ } else {
+ var idx = eqs.indexOf(upperLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+
+ if(this.lowerLimitEnabled && relPosition < lowerLimit){
+ // Update contact constraint normal, etc
+ vec2.scale(lowerLimitEquation.ni, worldAxisA, 1);
+ vec2.sub(lowerLimitEquation.ri, worldAnchorA, bodyA.position);
+ vec2.sub(lowerLimitEquation.rj, worldAnchorB, bodyB.position);
+ vec2.scale(tmp,worldAxisA,lowerLimit);
+ vec2.sub(lowerLimitEquation.rj,lowerLimitEquation.rj,tmp);
+ if(eqs.indexOf(lowerLimitEquation)==-1)
+ eqs.push(lowerLimitEquation);
+ } else {
+ var idx = eqs.indexOf(lowerLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+};
+
+/**
+ * Enable the motor
+ * @method enableMotor
+ */
+PrismaticConstraint.prototype.enableMotor = function(){
+ if(this.motorEnabled) return;
+ this.equations.push(this.motorEquation);
+ this.motorEnabled = true;
+};
+
+/**
+ * Disable the rotational motor
+ * @method disableMotor
+ */
+PrismaticConstraint.prototype.disableMotor = function(){
+ if(!this.motorEnabled) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.equations.splice(i,1);
+ this.motorEnabled = false;
+};
+
+},{"../equations/ContactEquation":23,"../equations/Equation":24,"../equations/RotationalLockEquation":26,"../math/vec2":33,"./Constraint":16}],21:[function(require,module,exports){
+var Constraint = require('./Constraint')
+, Equation = require('../equations/Equation')
+, RotationalVelocityEquation = require('../equations/RotationalVelocityEquation')
+, RotationalLockEquation = require('../equations/RotationalLockEquation')
+, vec2 = require('../math/vec2')
+
+module.exports = RevoluteConstraint;
+
+var worldPivotA = vec2.create(),
+ worldPivotB = vec2.create(),
+ xAxis = vec2.fromValues(1,0),
+ yAxis = vec2.fromValues(0,1),
+ g = vec2.create();
+
+/**
+ * Connects two bodies at given offset points, letting them rotate relative to each other around this point.
+ * @class RevoluteConstraint
+ * @constructor
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Float32Array} pivotA The point relative to the center of mass of bodyA which bodyA is constrained to.
+ * @param {Body} bodyB Body that will be constrained in a similar way to the same point as bodyA. We will therefore get sort of a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.
+ * @param {Float32Array} pivotB See pivotA.
+ * @param {Number} maxForce The maximum force that should be applied to constrain the bodies.
+ * @extends {Constraint}
+ * @todo Ability to specify world points
+ */
+function RevoluteConstraint(bodyA, pivotA, bodyB, pivotB, maxForce){
+ Constraint.call(this,bodyA,bodyB);
+
+ maxForce = this.maxForce = typeof(maxForce)!="undefined" ? maxForce : Number.MAX_VALUE;
+
+ this.pivotA = pivotA;
+ this.pivotB = pivotB;
+
+ // Equations to be fed to the solver
+ var eqs = this.equations = [
+ new Equation(bodyA,bodyB,-maxForce,maxForce),
+ new Equation(bodyA,bodyB,-maxForce,maxForce),
+ ];
+
+ var x = eqs[0];
+ var y = eqs[1];
+
+ x.computeGq = function(){
+ vec2.rotate(worldPivotA, pivotA, bodyA.angle);
+ vec2.rotate(worldPivotB, pivotB, bodyB.angle);
+ vec2.add(g, bodyB.position, worldPivotB);
+ vec2.sub(g, g, bodyA.position);
+ vec2.sub(g, g, worldPivotA);
+ return vec2.dot(g,xAxis);
+ };
+
+ y.computeGq = function(){
+ vec2.rotate(worldPivotA, pivotA, bodyA.angle);
+ vec2.rotate(worldPivotB, pivotB, bodyB.angle);
+ vec2.add(g, bodyB.position, worldPivotB);
+ vec2.sub(g, g, bodyA.position);
+ vec2.sub(g, g, worldPivotA);
+ return vec2.dot(g,yAxis);
+ };
+
+ y.minForce = x.minForce = -maxForce;
+ y.maxForce = x.maxForce = maxForce;
+
+ this.motorEquation = new RotationalVelocityEquation(bodyA,bodyB);
+ this.motorEnabled = false;
+
+ /**
+ * The constraint position
+ * @property angle
+ * @type {Number}
+ */
+ this.angle = 0;
+
+ /**
+ * Set to true to enable lower limit
+ * @property lowerLimitEnabled
+ * @type {Boolean}
+ */
+ this.lowerLimitEnabled = false;
+
+ /**
+ * Set to true to enable upper limit
+ * @property upperLimitEnabled
+ * @type {Boolean}
+ */
+ this.upperLimitEnabled = false;
+
+ /**
+ * The lower limit on the constraint angle.
+ * @property lowerLimit
+ * @type {Boolean}
+ */
+ this.lowerLimit = 0;
+
+ /**
+ * The upper limit on the constraint angle.
+ * @property upperLimit
+ * @type {Boolean}
+ */
+ this.upperLimit = 0;
+
+ this.upperLimitEquation = new RotationalLockEquation(bodyA,bodyB);
+ this.lowerLimitEquation = new RotationalLockEquation(bodyA,bodyB);
+ this.upperLimitEquation.minForce = 0;
+ this.lowerLimitEquation.maxForce = 0;
+}
+RevoluteConstraint.prototype = new Constraint();
+
+RevoluteConstraint.prototype.update = function(){
+ var bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ pivotA = this.pivotA,
+ pivotB = this.pivotB,
+ eqs = this.equations,
+ normal = eqs[0],
+ tangent= eqs[1],
+ x = eqs[0],
+ y = eqs[1],
+ upperLimit = this.upperLimit,
+ lowerLimit = this.lowerLimit,
+ upperLimitEquation = this.upperLimitEquation,
+ lowerLimitEquation = this.lowerLimitEquation;
+
+ var relAngle = this.angle = bodyB.angle - bodyA.angle;
+
+ if(this.upperLimitEnabled && relAngle > upperLimit){
+ upperLimitEquation.angle = upperLimit;
+ if(eqs.indexOf(upperLimitEquation)==-1)
+ eqs.push(upperLimitEquation);
+ } else {
+ var idx = eqs.indexOf(upperLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+
+ if(this.lowerLimitEnabled && relAngle < lowerLimit){
+ lowerLimitEquation.angle = lowerLimit;
+ if(eqs.indexOf(lowerLimitEquation)==-1)
+ eqs.push(lowerLimitEquation);
+ } else {
+ var idx = eqs.indexOf(lowerLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+
+ /*
+
+ The constraint violation is
+
+ g = xj + rj - xi - ri
+
+ ...where xi and xj are the body positions and ri and rj world-oriented offset vectors. Differentiate:
+
+ gdot = vj + wj x rj - vi - wi x ri
+
+ We split this into x and y directions. (let x and y be unit vectors along the respective axes)
+
+ gdot * x = ( vj + wj x rj - vi - wi x ri ) * x
+ = ( vj*x + (wj x rj)*x -vi*x -(wi x ri)*x
+ = ( vj*x + (rj x x)*wj -vi*x -(ri x x)*wi
+ = [ -x -(ri x x) x (rj x x)] * [vi wi vj wj]
+ = G*W
+
+ ...and similar for y. We have then identified the jacobian entries for x and y directions:
+
+ Gx = [ x (rj x x) -x -(ri x x)]
+ Gy = [ y (rj x y) -y -(ri x y)]
+
+ */
+
+ vec2.rotate(worldPivotA, pivotA, bodyA.angle);
+ vec2.rotate(worldPivotB, pivotB, bodyB.angle);
+
+ // todo: these are a bit sparse. We could save some computations on making custom eq.computeGW functions, etc
+
+ x.G[0] = -1;
+ x.G[1] = 0;
+ x.G[2] = -vec2.crossLength(worldPivotA,xAxis);
+ x.G[3] = 1;
+ x.G[4] = 0;
+ x.G[5] = vec2.crossLength(worldPivotB,xAxis);
+
+ y.G[0] = 0;
+ y.G[1] = -1;
+ y.G[2] = -vec2.crossLength(worldPivotA,yAxis);
+ y.G[3] = 0;
+ y.G[4] = 1;
+ y.G[5] = vec2.crossLength(worldPivotB,yAxis);
+};
+
+/**
+ * Enable the rotational motor
+ * @method enableMotor
+ */
+RevoluteConstraint.prototype.enableMotor = function(){
+ if(this.motorEnabled) return;
+ this.equations.push(this.motorEquation);
+ this.motorEnabled = true;
+};
+
+/**
+ * Disable the rotational motor
+ * @method disableMotor
+ */
+RevoluteConstraint.prototype.disableMotor = function(){
+ if(!this.motorEnabled) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.equations.splice(i,1);
+ this.motorEnabled = false;
+};
+
+/**
+ * Check if the motor is enabled.
+ * @method motorIsEnabled
+ * @return {Boolean}
+ */
+RevoluteConstraint.prototype.motorIsEnabled = function(){
+ return !!this.motorEnabled;
+};
+
+/**
+ * Set the speed of the rotational constraint motor
+ * @method setMotorSpeed
+ * @param {Number} speed
+ */
+RevoluteConstraint.prototype.setMotorSpeed = function(speed){
+ if(!this.motorEnabled) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.equations[i].relativeVelocity = speed;
+};
+
+/**
+ * Get the speed of the rotational constraint motor
+ * @method getMotorSpeed
+ * @return {Number} The current speed, or false if the motor is not enabled.
+ */
+RevoluteConstraint.prototype.getMotorSpeed = function(){
+ if(!this.motorEnabled) return false;
+ return this.motorEquation.relativeVelocity;
+};
+
+},{"../equations/Equation":24,"../equations/RotationalLockEquation":26,"../equations/RotationalVelocityEquation":27,"../math/vec2":33,"./Constraint":16}],22:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2');
+
+module.exports = AngleLockEquation;
+
+/**
+ * Locks the relative angle between two bodies. The constraint tries to keep the dot product between two vectors, local in each body, to zero. The local angle in body i is a parameter.
+ *
+ * @class AngleLockEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ * @param {Object} options
+ * @param {Number} options.angle Angle to add to the local vector in body i.
+ * @param {Number} options.ratio Gear ratio
+ */
+function AngleLockEquation(bi,bj,options){
+ options = options || {};
+ Equation.call(this,bi,bj,-Number.MAX_VALUE,Number.MAX_VALUE);
+ this.angle = options.angle || 0;
+ this.ratio = typeof(options.ratio)=="number" ? options.ratio : 1;
+ this.setRatio(this.ratio);
+};
+AngleLockEquation.prototype = new Equation();
+AngleLockEquation.prototype.constructor = AngleLockEquation;
+
+AngleLockEquation.prototype.computeGq = function(){
+ return this.ratio*this.bi.angle - this.bj.angle + this.angle;
+};
+
+AngleLockEquation.prototype.setRatio = function(ratio){
+ var G = this.G;
+ G[2] = ratio;
+ G[5] = -1;
+ this.ratio = ratio;
+};
+
+},{"../math/vec2":33,"./Equation":24}],23:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2'),
+ mat2 = require('../math/mat2');
+
+module.exports = ContactEquation;
+
+/**
+ * Non-penetration constraint equation. Tries to make the ri and rj vectors the same point.
+ *
+ * @class ContactEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ */
+function ContactEquation(bi,bj){
+ Equation.call(this,bi,bj,0,Number.MAX_VALUE);
+
+ /**
+ * Vector from body i center of mass to the contact point.
+ * @property ri
+ * @type {Array}
+ */
+ this.ri = vec2.create();
+ this.penetrationVec = vec2.create();
+
+ /**
+ * Vector from body j center of mass to the contact point.
+ * @property rj
+ * @type {Array}
+ */
+ this.rj = vec2.create();
+
+ /**
+ * The normal vector, pointing out of body i
+ * @property ni
+ * @type {Array}
+ */
+ this.ni = vec2.create();
+
+ /**
+ * The restitution to use. 0=no bounciness, 1=max bounciness.
+ * @property restitution
+ * @type {Number}
+ */
+ this.restitution = 0;
+
+ /**
+ * Set to true if this is the first impact between the bodies (not persistant contact).
+ * @property firstImpact
+ * @type {Boolean}
+ */
+ this.firstImpact = false;
+
+ /**
+ * The shape in body i that triggered this contact.
+ * @property shapeA
+ * @type {Shape}
+ */
+ this.shapeA = null;
+
+ /**
+ * The shape in body j that triggered this contact.
+ * @property shapeB
+ * @type {Shape}
+ */
+ this.shapeB = null;
+};
+ContactEquation.prototype = new Equation();
+ContactEquation.prototype.constructor = ContactEquation;
+ContactEquation.prototype.computeB = function(a,b,h){
+ var bi = this.bi,
+ bj = this.bj,
+ ri = this.ri,
+ rj = this.rj,
+ xi = bi.position,
+ xj = bj.position;
+
+ var penetrationVec = this.penetrationVec,
+ n = this.ni,
+ G = this.G;
+
+ // Caluclate cross products
+ var rixn = vec2.crossLength(ri,n),
+ rjxn = vec2.crossLength(rj,n);
+
+ // G = [-n -rixn n rjxn]
+ G[0] = -n[0];
+ G[1] = -n[1];
+ G[2] = -rixn;
+ G[3] = n[0];
+ G[4] = n[1];
+ G[5] = rjxn;
+
+ // Calculate q = xj+rj -(xi+ri) i.e. the penetration vector
+ vec2.add(penetrationVec,xj,rj);
+ vec2.sub(penetrationVec,penetrationVec,xi);
+ vec2.sub(penetrationVec,penetrationVec,ri);
+
+ // Compute iteration
+ var GW, Gq;
+ if(this.firstImpact && this.restitution !== 0){
+ Gq = 0;
+ GW = (1/b)*(1+this.restitution) * this.computeGW();
+ } else {
+ GW = this.computeGW();
+ Gq = vec2.dot(n,penetrationVec);
+ }
+
+ var GiMf = this.computeGiMf();
+ var B = - Gq * a - GW * b - h*GiMf;
+
+ return B;
+};
+
+},{"../math/mat2":31,"../math/vec2":33,"./Equation":24}],24:[function(require,module,exports){
+module.exports = Equation;
+
+var vec2 = require('../math/vec2'),
+ mat2 = require('../math/mat2'),
+ Utils = require('../utils/Utils');
+
+/**
+ * Base class for constraint equations.
+ * @class Equation
+ * @constructor
+ * @param {Body} bi First body participating in the equation
+ * @param {Body} bj Second body participating in the equation
+ * @param {number} minForce Minimum force to apply. Default: -1e6
+ * @param {number} maxForce Maximum force to apply. Default: 1e6
+ */
+function Equation(bi,bj,minForce,maxForce){
+
+ /**
+ * Minimum force to apply when solving
+ * @property minForce
+ * @type {Number}
+ */
+ this.minForce = typeof(minForce)=="undefined" ? -1e6 : minForce;
+
+ /**
+ * Max force to apply when solving
+ * @property maxForce
+ * @type {Number}
+ */
+ this.maxForce = typeof(maxForce)=="undefined" ? 1e6 : maxForce;
+
+ /**
+ * First body participating in the constraint
+ * @property bi
+ * @type {Body}
+ */
+ this.bi = bi;
+
+ /**
+ * Second body participating in the constraint
+ * @property bj
+ * @type {Body}
+ */
+ this.bj = bj;
+
+ /**
+ * The stiffness of this equation. Typically chosen to a large number (~1e7), but can be chosen somewhat freely to get a stable simulation.
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = 1e6;
+
+ /**
+ * The number of time steps needed to stabilize the constraint equation. Typically between 3 and 5 time steps.
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = 4;
+
+ /**
+ * The Jacobian entry of this equation. 6 numbers, 3 per body (x,y,angle).
+ * @property G
+ * @type {Array}
+ */
+ this.G = new Utils.ARRAY_TYPE(6);
+
+ // Constraint frames for body i and j
+ /*
+ this.xi = vec2.create();
+ this.xj = vec2.create();
+ this.ai = 0;
+ this.aj = 0;
+ */
+ this.offset = 0;
+
+ this.a = 0;
+ this.b = 0;
+ this.eps = 0;
+ this.h = 0;
+ this.updateSpookParams(1/60);
+
+ /**
+ * The resulting constraint multiplier from the last solve. This is mostly equivalent to the force produced by the constraint.
+ * @property multiplier
+ * @type {Number}
+ */
+ this.multiplier = 0;
+
+ /**
+ * Relative velocity.
+ * @property {Number} relativeVelocity
+ */
+ this.relativeVelocity = 0;
+
+ /**
+ * Whether this equation is enabled or not. If true, it will be added to the solver.
+ * @property {Boolean} enabled
+ */
+ this.enabled = true;
+};
+Equation.prototype.constructor = Equation;
+
+/**
+ * Update SPOOK parameters .a, .b and .eps according to the given time step. See equations 9, 10 and 11 in the SPOOK notes.
+ * @method updateSpookParams
+ * @param {number} timeStep
+ */
+Equation.prototype.updateSpookParams = function(timeStep){
+ var k = this.stiffness,
+ d = this.relaxation,
+ h = timeStep;
+ this.a = 4.0 / (h * (1 + 4 * d));
+ this.b = (4.0 * d) / (1 + 4 * d);
+ this.eps = 4.0 / (h * h * k * (1 + 4 * d));
+ this.h = timeStep;
+};
+
+function Gmult(G,vi,wi,vj,wj){
+ return G[0] * vi[0] +
+ G[1] * vi[1] +
+ G[2] * wi +
+ G[3] * vj[0] +
+ G[4] * vj[1] +
+ G[5] * wj;
+}
+
+/**
+ * Computes the RHS of the SPOOK equation
+ * @method computeB
+ * @return {Number}
+ */
+Equation.prototype.computeB = function(a,b,h){
+ var GW = this.computeGW();
+ var Gq = this.computeGq();
+ var GiMf = this.computeGiMf();
+ return - Gq * a - GW * b - GiMf*h;
+};
+
+/**
+ * Computes G*q, where q are the generalized body coordinates
+ * @method computeGq
+ * @return {Number}
+ */
+var qi = vec2.create(),
+ qj = vec2.create();
+Equation.prototype.computeGq = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ xi = bi.position,
+ xj = bj.position,
+ ai = bi.angle,
+ aj = bj.angle;
+
+ // Transform to the given body frames
+ /*
+ vec2.rotate(qi,this.xi,ai);
+ vec2.rotate(qj,this.xj,aj);
+ vec2.add(qi,qi,xi);
+ vec2.add(qj,qj,xj);
+ */
+
+ return Gmult(G, qi, ai, qj, aj) + this.offset;
+};
+
+var tmp_i = vec2.create(),
+ tmp_j = vec2.create();
+Equation.prototype.transformedGmult = function(G,vi,wi,vj,wj){
+ // Transform velocity to the given body frames
+ // v_p = v + w x r
+ /*
+ vec2.rotate(tmp_i,this.xi,Math.PI / 2 + this.bi.angle); // Get r, and rotate 90 degrees. We get the "x r" part
+ vec2.rotate(tmp_j,this.xj,Math.PI / 2 + this.bj.angle);
+ vec2.scale(tmp_i,tmp_i,wi); // Temp vectors are now (w x r)
+ vec2.scale(tmp_j,tmp_j,wj);
+ vec2.add(tmp_i,tmp_i,vi);
+ vec2.add(tmp_j,tmp_j,vj);
+ */
+
+ // Note: angular velocity is same
+ return Gmult(G,vi,wi,vj,wj);
+};
+
+/**
+ * Computes G*W, where W are the body velocities
+ * @method computeGW
+ * @return {Number}
+ */
+Equation.prototype.computeGW = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ vi = bi.velocity,
+ vj = bj.velocity,
+ wi = bi.angularVelocity,
+ wj = bj.angularVelocity;
+ return this.transformedGmult(G,vi,wi,vj,wj) + this.relativeVelocity;
+};
+
+/**
+ * Computes G*Wlambda, where W are the body velocities
+ * @method computeGWlambda
+ * @return {Number}
+ */
+Equation.prototype.computeGWlambda = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ vi = bi.vlambda,
+ vj = bj.vlambda,
+ wi = bi.wlambda,
+ wj = bj.wlambda;
+ return Gmult(G,vi,wi,vj,wj);
+};
+
+/**
+ * Computes G*inv(M)*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.
+ * @method computeGiMf
+ * @return {Number}
+ */
+var iMfi = vec2.create(),
+ iMfj = vec2.create();
+Equation.prototype.computeGiMf = function(){
+ var bi = this.bi,
+ bj = this.bj,
+ fi = bi.force,
+ ti = bi.angularForce,
+ fj = bj.force,
+ tj = bj.angularForce,
+ invMassi = bi.invMass,
+ invMassj = bj.invMass,
+ invIi = bi.invInertia,
+ invIj = bj.invInertia,
+ G = this.G;
+
+ vec2.scale(iMfi, fi,invMassi);
+ vec2.scale(iMfj, fj,invMassj);
+
+ return this.transformedGmult(G,iMfi,ti*invIi,iMfj,tj*invIj);
+};
+
+/**
+ * Computes G*inv(M)*G'
+ * @method computeGiMGt
+ * @return {Number}
+ */
+Equation.prototype.computeGiMGt = function(){
+ var bi = this.bi,
+ bj = this.bj,
+ invMassi = bi.invMass,
+ invMassj = bj.invMass,
+ invIi = bi.invInertia,
+ invIj = bj.invInertia,
+ G = this.G;
+
+ return G[0] * G[0] * invMassi +
+ G[1] * G[1] * invMassi +
+ G[2] * G[2] * invIi +
+ G[3] * G[3] * invMassj +
+ G[4] * G[4] * invMassj +
+ G[5] * G[5] * invIj;
+};
+
+var addToWlambda_temp = vec2.create(),
+ addToWlambda_Gi = vec2.create(),
+ addToWlambda_Gj = vec2.create(),
+ addToWlambda_ri = vec2.create(),
+ addToWlambda_rj = vec2.create(),
+ addToWlambda_Mdiag = vec2.create();
+var tmpMat1 = mat2.create(),
+ tmpMat2 = mat2.create();
+
+/**
+ * Add constraint velocity to the bodies.
+ * @method addToWlambda
+ * @param {Number} deltalambda
+ */
+Equation.prototype.addToWlambda = function(deltalambda){
+ var bi = this.bi,
+ bj = this.bj,
+ temp = addToWlambda_temp,
+ imMat1 = tmpMat1,
+ imMat2 = tmpMat2,
+ Gi = addToWlambda_Gi,
+ Gj = addToWlambda_Gj,
+ ri = addToWlambda_ri,
+ rj = addToWlambda_rj,
+ Mdiag = addToWlambda_Mdiag,
+ G = this.G;
+
+ Gi[0] = G[0];
+ Gi[1] = G[1];
+ Gj[0] = G[3];
+ Gj[1] = G[4];
+
+ /*
+ mat2.identity(imMat1);
+ mat2.identity(imMat2);
+ imMat1[0] = imMat1[3] = bi.invMass;
+ imMat2[0] = imMat2[3] = bj.invMass;
+ */
+
+
+ /*
+ vec2.rotate(ri,this.xi,bi.angle);
+ vec2.rotate(rj,this.xj,bj.angle);
+ */
+
+ // Add to linear velocity
+ // v_lambda += inv(M) * delta_lamba * G
+ //vec2.set(Mdiag,bi.invMass,bi.invMass);
+ //vec2.scale(temp,vec2.transformMat2(temp,Gi,imMat1),deltalambda);
+ vec2.scale(temp,Gi,bi.invMass*deltalambda);
+ vec2.add( bi.vlambda, bi.vlambda, temp);
+ // This impulse is in the offset frame
+ // Also add contribution to angular
+ //bi.wlambda -= vec2.crossLength(temp,ri);
+
+ //vec2.set(Mdiag,bj.invMass,bj.invMass);
+ //vec2.scale(temp,vec2.transformMat2(temp,Gj,imMat2),deltalambda);
+ vec2.scale(temp,Gj,bj.invMass*deltalambda);
+ vec2.add( bj.vlambda, bj.vlambda, temp);
+ //bj.wlambda -= vec2.crossLength(temp,rj);
+
+ // Add to angular velocity
+ bi.wlambda += bi.invInertia * G[2] * deltalambda;
+ bj.wlambda += bj.invInertia * G[5] * deltalambda;
+};
+
+function massMatVecMultiply(out, m, v) {
+ out[0] = v[0] * m;
+ out[1] = v[1] * m;
+ return out;
+}
+
+/**
+ * Compute the denominator part of the SPOOK equation: C = G*inv(M)*G' + eps
+ * @method computeInvC
+ * @param {Number} eps
+ * @return {Number}
+ */
+Equation.prototype.computeInvC = function(eps){
+ return 1.0 / (this.computeGiMGt() + eps);
+};
+
+},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":49}],25:[function(require,module,exports){
+var mat2 = require('../math/mat2')
+, vec2 = require('../math/vec2')
+, Equation = require('./Equation')
+, Utils = require('../utils/Utils')
+
+module.exports = FrictionEquation;
+
+/**
+ * Constrains the slipping in a contact along a tangent
+ *
+ * @class FrictionEquation
+ * @constructor
+ * @param {Body} bi
+ * @param {Body} bj
+ * @param {Number} slipForce
+ * @extends {Equation}
+ */
+function FrictionEquation(bi,bj,slipForce){
+ Equation.call(this,bi,bj,-slipForce,slipForce);
+
+ /**
+ * Relative vector from center of body i to the contact point, in world coords.
+ * @property ri
+ * @type {Float32Array}
+ */
+ this.ri = vec2.create();
+
+ /**
+ * Relative vector from center of body j to the contact point, in world coords.
+ * @property rj
+ * @type {Float32Array}
+ */
+ this.rj = vec2.create();
+
+ /**
+ * Tangent vector that the friction force will act along, in world coords.
+ * @property t
+ * @type {Float32Array}
+ */
+ this.t = vec2.create();
+
+ /**
+ * A ContactEquation connected to this friction. The contact equation can be used to rescale the max force for the friction.
+ * @property contactEquation
+ * @type {ContactEquation}
+ */
+ this.contactEquation = null;
+
+ /**
+ * The shape in body i that triggered this friction.
+ * @property shapeA
+ * @type {Shape}
+ * @todo Needed? The shape can be looked up via contactEquation.shapeA...
+ */
+ this.shapeA = null;
+
+ /**
+ * The shape in body j that triggered this friction.
+ * @property shapeB
+ * @type {Shape}
+ * @todo Needed? The shape can be looked up via contactEquation.shapeB...
+ */
+ this.shapeB = null;
+
+ /**
+ * The friction coefficient to use.
+ * @property frictionCoefficient
+ * @type {Number}
+ */
+ this.frictionCoefficient = 0.3;
+};
+FrictionEquation.prototype = new Equation();
+FrictionEquation.prototype.constructor = FrictionEquation;
+
+/**
+ * Set the slipping condition for the constraint. The friction force cannot be
+ * larger than this value.
+ * @method setSlipForce
+ * @param {Number} slipForce
+ * @deprecated Use .frictionCoefficient instead
+ */
+FrictionEquation.prototype.setSlipForce = function(slipForce){
+ this.maxForce = slipForce;
+ this.minForce = -slipForce;
+};
+
+FrictionEquation.prototype.computeB = function(a,b,h){
+ var bi = this.bi,
+ bj = this.bj,
+ ri = this.ri,
+ rj = this.rj,
+ t = this.t,
+ G = this.G;
+
+ // G = [-t -rixt t rjxt]
+ // And remember, this is a pure velocity constraint, g is always zero!
+ G[0] = -t[0];
+ G[1] = -t[1];
+ G[2] = -vec2.crossLength(ri,t);
+ G[3] = t[0];
+ G[4] = t[1];
+ G[5] = vec2.crossLength(rj,t);
+
+ var GW = this.computeGW(),
+ GiMf = this.computeGiMf();
+
+ var B = /* - g * a */ - GW * b - h*GiMf;
+
+ return B;
+};
+
+},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":49,"./Equation":24}],26:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2');
+
+module.exports = RotationalLockEquation;
+
+/**
+ * Locks the relative angle between two bodies. The constraint tries to keep the dot product between two vectors, local in each body, to zero. The local angle in body i is a parameter.
+ *
+ * @class RotationalLockEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ * @param {Object} options
+ * @param {Number} options.angle Angle to add to the local vector in body i.
+ */
+function RotationalLockEquation(bi,bj,options){
+ options = options || {};
+ Equation.call(this,bi,bj,-Number.MAX_VALUE,Number.MAX_VALUE);
+ this.angle = options.angle || 0;
+
+ var G = this.G;
+ G[2] = 1;
+ G[5] = -1;
+};
+RotationalLockEquation.prototype = new Equation();
+RotationalLockEquation.prototype.constructor = RotationalLockEquation;
+
+var worldVectorA = vec2.create(),
+ worldVectorB = vec2.create(),
+ xAxis = vec2.fromValues(1,0),
+ yAxis = vec2.fromValues(0,1);
+RotationalLockEquation.prototype.computeGq = function(){
+ vec2.rotate(worldVectorA,xAxis,this.bi.angle+this.angle);
+ vec2.rotate(worldVectorB,yAxis,this.bj.angle);
+ return vec2.dot(worldVectorA,worldVectorB);
+};
+
+},{"../math/vec2":33,"./Equation":24}],27:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2');
+
+module.exports = RotationalVelocityEquation;
+
+/**
+ * Syncs rotational velocity of two bodies, or sets a relative velocity (motor).
+ *
+ * @class RotationalVelocityEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ */
+function RotationalVelocityEquation(bi,bj){
+ Equation.call(this,bi,bj,-Number.MAX_VALUE,Number.MAX_VALUE);
+ this.relativeVelocity = 1;
+ this.ratio = 1;
+};
+RotationalVelocityEquation.prototype = new Equation();
+RotationalVelocityEquation.prototype.constructor = RotationalVelocityEquation;
+RotationalVelocityEquation.prototype.computeB = function(a,b,h){
+ var G = this.G;
+ G[2] = -1;
+ G[5] = this.ratio;
+
+ var GiMf = this.computeGiMf();
+ var GW = this.computeGW();
+ var B = - GW * b - h*GiMf;
+
+ return B;
+};
+
+},{"../math/vec2":33,"./Equation":24}],28:[function(require,module,exports){
+/**
+ * Base class for objects that dispatches events.
+ * @class EventEmitter
+ * @constructor
+ */
+var EventEmitter = function () {}
+
+module.exports = EventEmitter;
+
+EventEmitter.prototype = {
+ constructor: EventEmitter,
+
+ /**
+ * Add an event listener
+ * @method on
+ * @param {String} type
+ * @param {Function} listener
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ on: function ( type, listener, context ) {
+ listener.context = context || this;
+ if ( this._listeners === undefined ) this._listeners = {};
+ var listeners = this._listeners;
+ if ( listeners[ type ] === undefined ) {
+ listeners[ type ] = [];
+ }
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+ listeners[ type ].push( listener );
+ }
+ return this;
+ },
+
+ /**
+ * Check if an event listener is added
+ * @method has
+ * @param {String} type
+ * @param {Function} listener
+ * @return {Boolean}
+ */
+ has: function ( type, listener ) {
+ if ( this._listeners === undefined ) return false;
+ var listeners = this._listeners;
+ if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Remove an event listener
+ * @method off
+ * @param {String} type
+ * @param {Function} listener
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ off: function ( type, listener ) {
+ if ( this._listeners === undefined ) return this;
+ var listeners = this._listeners;
+ var index = listeners[ type ].indexOf( listener );
+ if ( index !== - 1 ) {
+ listeners[ type ].splice( index, 1 );
+ }
+ return this;
+ },
+
+ /**
+ * Emit an event.
+ * @method emit
+ * @param {Object} event
+ * @param {String} event.type
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ emit: function ( event ) {
+ if ( this._listeners === undefined ) return this;
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+ if ( listenerArray !== undefined ) {
+ event.target = this;
+ for ( var i = 0, l = listenerArray.length; i < l; i ++ ) {
+ var listener = listenerArray[ i ];
+ listener.call( listener.context, event );
+ }
+ }
+ return this;
+ }
+};
+
+},{}],29:[function(require,module,exports){
+module.exports = ContactMaterial;
+
+var idCounter = 0;
+
+/**
+ * Defines a physics material.
+ * @class ContactMaterial
+ * @constructor
+ * @param {Material} materialA
+ * @param {Material} materialB
+ * @param {Object} [options]
+ * @param {Number} options.friction
+ * @param {Number} options.restitution
+ * @author schteppe
+ */
+function ContactMaterial(materialA, materialB, options){
+ options = options || {};
+
+ /**
+ * The contact material identifier
+ * @property id
+ * @type {Number}
+ */
+ this.id = idCounter++;
+
+ /**
+ * First material participating in the contact material
+ * @property materialA
+ * @type {Material}
+ */
+ this.materialA = materialA;
+
+ /**
+ * Second material participating in the contact material
+ * @property materialB
+ * @type {Material}
+ */
+ this.materialB = materialB;
+
+ /**
+ * Friction to use in the contact of these two materials
+ * @property friction
+ * @type {Number}
+ */
+ this.friction = typeof(options.friction) !== "undefined" ? Number(options.friction) : 0.3;
+
+ /**
+ * Restitution to use in the contact of these two materials
+ * @property restitution
+ * @type {Number}
+ */
+ this.restitution = typeof(options.restitution) !== "undefined" ? Number(options.restitution) : 0.0;
+
+ /**
+ * Stiffness of the resulting ContactEquation that this ContactMaterial generate
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = typeof(options.stiffness) !== "undefined" ? Number(options.stiffness) : 1e7;
+
+ /**
+ * Relaxation of the resulting ContactEquation that this ContactMaterial generate
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = typeof(options.relaxation) !== "undefined" ? Number(options.relaxation) : 3;
+
+ /**
+ * Stiffness of the resulting FrictionEquation that this ContactMaterial generate
+ * @property frictionStiffness
+ * @type {Number}
+ */
+ this.frictionStiffness = typeof(options.frictionStiffness) !== "undefined" ? Number(options.frictionStiffness) : 1e7;
+
+ /**
+ * Relaxation of the resulting FrictionEquation that this ContactMaterial generate
+ * @property frictionRelaxation
+ * @type {Number}
+ */
+ this.frictionRelaxation = typeof(options.frictionRelaxation) !== "undefined" ? Number(options.frictionRelaxation) : 3;
+
+ /**
+ * Will add surface velocity to this material. If bodyA rests on top if bodyB, and the surface velocity is positive, bodyA will slide to the right.
+ * @property {Number} surfaceVelocity
+ */
+ this.surfaceVelocity = typeof(options.surfaceVelocity) !== "undefined" ? Number(options.surfaceVelocity) : 0
+};
+
+},{}],30:[function(require,module,exports){
+module.exports = Material;
+
+var idCounter = 0;
+
+/**
+ * Defines a physics material.
+ * @class Material
+ * @constructor
+ * @param string name
+ * @author schteppe
+ */
+function Material(){
+ /**
+ * The material identifier
+ * @property id
+ * @type {Number}
+ */
+ this.id = idCounter++;
+};
+
+},{}],31:[function(require,module,exports){
+/**
+ * The mat2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
+ * @class mat2
+ */
+
+// Only import mat2 from gl-matrix and skip the rest
+var mat2 = require('../../node_modules/gl-matrix/src/gl-matrix/mat2').mat2;
+
+// Export everything
+module.exports = mat2;
+
+},{"../../node_modules/gl-matrix/src/gl-matrix/mat2":1}],32:[function(require,module,exports){
+
+ /*
+ PolyK library
+ url: http://polyk.ivank.net
+ Released under MIT licence.
+
+ Copyright (c) 2012 Ivan Kuckir
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ var PolyK = {};
+
+ /*
+ Is Polygon self-intersecting?
+
+ O(n^2)
+ */
+ /*
+ PolyK.IsSimple = function(p)
+ {
+ var n = p.length>>1;
+ if(n<4) return true;
+ var a1 = new PolyK._P(), a2 = new PolyK._P();
+ var b1 = new PolyK._P(), b2 = new PolyK._P();
+ var c = new PolyK._P();
+
+ for(var i=0; i>1;
+ if(n<3) return [];
+ var tgs = [];
+ var avl = [];
+ for(var i=0; i 3)
+ {
+ var i0 = avl[(i+0)%al];
+ var i1 = avl[(i+1)%al];
+ var i2 = avl[(i+2)%al];
+
+ var ax = p[2*i0], ay = p[2*i0+1];
+ var bx = p[2*i1], by = p[2*i1+1];
+ var cx = p[2*i2], cy = p[2*i2+1];
+
+ var earFound = false;
+ if(PolyK._convex(ax, ay, bx, by, cx, cy))
+ {
+ earFound = true;
+ for(var j=0; j 3*al) break; // no convex angles :(
+ }
+ tgs.push(avl[0], avl[1], avl[2]);
+ return tgs;
+ }
+ /*
+ PolyK.ContainsPoint = function(p, px, py)
+ {
+ var n = p.length>>1;
+ var ax, ay, bx = p[2*n-2]-px, by = p[2*n-1]-py;
+ var depth = 0;
+ for(var i=0; i=0 && by>=0) continue; // both "up" or both "donw"
+ if(ax< 0 && bx< 0) continue;
+
+ var lx = ax + (bx-ax)*(-ay)/(by-ay);
+ if(lx>0) depth++;
+ }
+ return (depth & 1) == 1;
+ }
+
+ PolyK.Slice = function(p, ax, ay, bx, by)
+ {
+ if(PolyK.ContainsPoint(p, ax, ay) || PolyK.ContainsPoint(p, bx, by)) return [p.slice(0)];
+
+ var a = new PolyK._P(ax, ay);
+ var b = new PolyK._P(bx, by);
+ var iscs = []; // intersections
+ var ps = []; // points
+ for(var i=0; i 0)
+ {
+ var n = ps.length;
+ var i0 = iscs[0];
+ var i1 = iscs[1];
+ var ind0 = ps.indexOf(i0);
+ var ind1 = ps.indexOf(i1);
+ var solved = false;
+
+ if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
+ else
+ {
+ i0 = iscs[1];
+ i1 = iscs[0];
+ ind0 = ps.indexOf(i0);
+ ind1 = ps.indexOf(i1);
+ if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
+ }
+ if(solved)
+ {
+ dir--;
+ var pgn = PolyK._getPoints(ps, ind0, ind1);
+ pgs.push(pgn);
+ ps = PolyK._getPoints(ps, ind1, ind0);
+ i0.flag = i1.flag = false;
+ iscs.splice(0,2);
+ if(iscs.length == 0) pgs.push(ps);
+ }
+ else { dir++; iscs.reverse(); }
+ if(dir>1) break;
+ }
+ var result = [];
+ for(var i=0; i>1, isc);
+ }
+ b1.x = b2.x; b1.y = b2.y;
+ b2.x = p[0]; b2.y = p[1];
+ PolyK._pointLineDist(a1, b1, b2, l>>1, isc);
+
+ var idst = 1/isc.dist;
+ isc.norm.x = (x-isc.point.x)*idst;
+ isc.norm.y = (y-isc.point.y)*idst;
+ return isc;
+ }
+
+ PolyK._pointLineDist = function(p, a, b, edge, isc)
+ {
+ var x = p.x, y = p.y, x1 = a.x, y1 = a.y, x2 = b.x, y2 = b.y;
+
+ var A = x - x1;
+ var B = y - y1;
+ var C = x2 - x1;
+ var D = y2 - y1;
+
+ var dot = A * C + B * D;
+ var len_sq = C * C + D * D;
+ var param = dot / len_sq;
+
+ var xx, yy;
+
+ if (param < 0 || (x1 == x2 && y1 == y2)) {
+ xx = x1;
+ yy = y1;
+ }
+ else if (param > 1) {
+ xx = x2;
+ yy = y2;
+ }
+ else {
+ xx = x1 + param * C;
+ yy = y1 + param * D;
+ }
+
+ var dx = x - xx;
+ var dy = y - yy;
+ var dst = Math.sqrt(dx * dx + dy * dy);
+ if(dst= 0) && (v >= 0) && (u + v < 1);
+ }
+ /*
+ PolyK._RayLineIntersection = function(a1, a2, b1, b2, c)
+ {
+ var dax = (a1.x-a2.x), dbx = (b1.x-b2.x);
+ var day = (a1.y-a2.y), dby = (b1.y-b2.y);
+
+ var Den = dax*dby - day*dbx;
+ if (Den == 0) return null; // parallel
+
+ var A = (a1.x * a2.y - a1.y * a2.x);
+ var B = (b1.x * b2.y - b1.y * b2.x);
+
+ var I = c;
+ var iDen = 1/Den;
+ I.x = ( A*dbx - dax*B ) * iDen;
+ I.y = ( A*dby - day*B ) * iDen;
+
+ if(!PolyK._InRect(I, b1, b2)) return null;
+ if((day>0 && I.y>a1.y) || (day<0 && I.y0 && I.x>a1.x) || (dax<0 && I.x=Math.min(b.y, c.y) && a.y<=Math.max(b.y, c.y));
+ if (b.y == c.y) return (a.x>=Math.min(b.x, c.x) && a.x<=Math.max(b.x, c.x));
+
+ if(a.x >= Math.min(b.x, c.x) && a.x <= Math.max(b.x, c.x)
+ && a.y >= Math.min(b.y, c.y) && a.y <= Math.max(b.y, c.y))
+ return true;
+ return false;
+ }
+ */
+ PolyK._convex = function(ax, ay, bx, by, cx, cy)
+ {
+ return (ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0;
+ }
+ /*
+ PolyK._P = function(x,y)
+ {
+ this.x = x;
+ this.y = y;
+ this.flag = false;
+ }
+ PolyK._P.prototype.toString = function()
+ {
+ return "Point ["+this.x+", "+this.y+"]";
+ }
+ PolyK._P.dist = function(a,b)
+ {
+ var dx = b.x-a.x;
+ var dy = b.y-a.y;
+ return Math.sqrt(dx*dx + dy*dy);
+ }
+
+ PolyK._tp = [];
+ for(var i=0; i<10; i++) PolyK._tp.push(new PolyK._P(0,0));
+ */
+
+module.exports = PolyK;
+
+},{}],33:[function(require,module,exports){
+/**
+ * The vec2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
+ * @class vec2
+ */
+
+// Only import vec2 from gl-matrix and skip the rest
+var vec2 = require('../../node_modules/gl-matrix/src/gl-matrix/vec2').vec2;
+
+// Now add some extensions
+
+/**
+ * Get the vector x component
+ * @method getX
+ * @static
+ * @param {Float32Array} a
+ * @return {Number}
+ */
+vec2.getX = function(a){
+ return a[0];
+};
+
+/**
+ * Get the vector y component
+ * @method getY
+ * @static
+ * @param {Float32Array} a
+ * @return {Number}
+ */
+vec2.getY = function(a){
+ return a[1];
+};
+
+/**
+ * Make a cross product and only return the z component
+ * @method crossLength
+ * @static
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @return {Number}
+ */
+vec2.crossLength = function(a,b){
+ return a[0] * b[1] - a[1] * b[0];
+};
+
+/**
+ * Cross product between a vector and the Z component of a vector
+ * @method crossVZ
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} vec
+ * @param {Number} zcomp
+ * @return {Number}
+ */
+vec2.crossVZ = function(out, vec, zcomp){
+ vec2.rotate(out,vec,-Math.PI/2);// Rotate according to the right hand rule
+ vec2.scale(out,out,zcomp); // Scale with z
+ return out;
+};
+
+/**
+ * Cross product between a vector and the Z component of a vector
+ * @method crossZV
+ * @static
+ * @param {Float32Array} out
+ * @param {Number} zcomp
+ * @param {Float32Array} vec
+ * @return {Number}
+ */
+vec2.crossZV = function(out, zcomp, vec){
+ vec2.rotate(out,vec,Math.PI/2); // Rotate according to the right hand rule
+ vec2.scale(out,out,zcomp); // Scale with z
+ return out;
+};
+
+/**
+ * Rotate a vector by an angle
+ * @method rotate
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} a
+ * @param {Number} angle
+ */
+vec2.rotate = function(out,a,angle){
+ var c = Math.cos(angle),
+ s = Math.sin(angle),
+ x = a[0],
+ y = a[1];
+ out[0] = c*x -s*y;
+ out[1] = s*x +c*y;
+};
+
+vec2.toLocalFrame = function(out, worldPoint, framePosition, frameAngle){
+ vec2.copy(out, worldPoint);
+ vec2.sub(out, out, framePosition);
+ vec2.rotate(out, out, -frameAngle);
+};
+
+vec2.toGlobalFrame = function(out, localPoint, framePosition, frameAngle){
+ vec2.copy(out, localPoint);
+ vec2.rotate(out, out, frameAngle);
+ vec2.add(out, out, framePosition);
+};
+
+/**
+ * Compute centroid of a triangle spanned by vectors a,b,c. See http://easycalculation.com/analytical/learn-centroid.php
+ * @method centroid
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @param {Float32Array} c
+ * @return {Float32Array} The out object
+ */
+vec2.centroid = function(out, a, b, c){
+ vec2.add(out, a, b);
+ vec2.add(out, out, c);
+ vec2.scale(out, out, 1/3);
+ return out;
+};
+
+// Export everything
+module.exports = vec2;
+
+},{"../../node_modules/gl-matrix/src/gl-matrix/vec2":2}],34:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, decomp = require('poly-decomp')
+, Convex = require('../shapes/Convex')
+, AABB = require('../collision/AABB')
+, EventEmitter = require('../events/EventEmitter')
+
+module.exports = Body;
+
+/**
+ * A rigid body. Has got a center of mass, position, velocity and a number of
+ * shapes that are used for collisions.
+ *
+ * @class Body
+ * @constructor
+ * @param {Object} [options]
+ * @param {Number} [options.mass=0] A number >= 0. If zero, the .motionState will be set to Body.STATIC.
+ * @param {Float32Array|Array} [options.position]
+ * @param {Float32Array|Array} [options.velocity]
+ * @param {Number} [options.angle=0]
+ * @param {Number} [options.angularVelocity=0]
+ * @param {Float32Array|Array} [options.force]
+ * @param {Number} [options.angularForce=0]
+ * @param {Number} [options.fixedRotation=false]
+ *
+ * @todo Should not take mass as argument to Body, but as density to each Shape
+ */
+function Body(options){
+ options = options || {};
+
+ EventEmitter.call(this);
+
+ /**
+ * The body identifyer
+ * @property id
+ * @type {Number}
+ */
+ this.id = ++Body._idCounter;
+
+ /**
+ * The world that this body is added to. This property is set to NULL if the body is not added to any world.
+ * @property world
+ * @type {World}
+ */
+ this.world = null;
+
+ /**
+ * The shapes of the body. The local transform of the shape in .shapes[i] is
+ * defined by .shapeOffsets[i] and .shapeAngles[i].
+ *
+ * @property shapes
+ * @type {Array}
+ */
+ this.shapes = [];
+
+ /**
+ * The local shape offsets, relative to the body center of mass. This is an
+ * array of Float32Array.
+ * @property shapeOffsets
+ * @type {Array}
+ */
+ this.shapeOffsets = [];
+
+ /**
+ * The body-local shape angle transforms. This is an array of numbers (angles).
+ * @property shapeAngles
+ * @type {Array}
+ */
+ this.shapeAngles = [];
+
+ /**
+ * The mass of the body.
+ * @property mass
+ * @type {number}
+ */
+ this.mass = options.mass || 0;
+
+ /**
+ * The inverse mass of the body.
+ * @property invMass
+ * @type {number}
+ */
+ this.invMass = 0;
+
+ /**
+ * The inertia of the body around the Z axis.
+ * @property inertia
+ * @type {number}
+ */
+ this.inertia = 0;
+
+ /**
+ * The inverse inertia of the body.
+ * @property invInertia
+ * @type {number}
+ */
+ this.invInertia = 0;
+
+ /**
+ * Set to true if you want to fix the rotation of the body.
+ * @property fixedRotation
+ * @type {Boolean}
+ */
+ this.fixedRotation = !!options.fixedRotation || false;
+
+ this.updateMassProperties();
+
+ /**
+ * The position of the body
+ * @property position
+ * @type {Array}
+ */
+ this.position = vec2.fromValues(0,0);
+ if(options.position) vec2.copy(this.position, options.position);
+
+ /**
+ * The interpolated position of the body.
+ * @property interpolatedPosition
+ * @type {Array}
+ */
+ this.interpolatedPosition = vec2.fromValues(0,0);
+
+ /**
+ * The velocity of the body
+ * @property velocity
+ * @type {Float32Array}
+ */
+ this.velocity = vec2.fromValues(0,0);
+ if(options.velocity) vec2.copy(this.velocity, options.velocity);
+
+ /**
+ * Constraint velocity that was added to the body during the last step.
+ * @property vlambda
+ * @type {Float32Array}
+ */
+ this.vlambda = vec2.fromValues(0,0);
+
+ /**
+ * Angular constraint velocity that was added to the body during last step.
+ * @property wlambda
+ * @type {Float32Array}
+ */
+ this.wlambda = 0;
+
+ /**
+ * The angle of the body
+ * @property angle
+ * @type {number}
+ */
+ this.angle = options.angle || 0;
+
+ /**
+ * The angular velocity of the body
+ * @property angularVelocity
+ * @type {number}
+ */
+ this.angularVelocity = options.angularVelocity || 0;
+
+ /**
+ * The force acting on the body
+ * @property force
+ * @type {Float32Array}
+ */
+ this.force = vec2.create();
+ if(options.force) vec2.copy(this.force, options.force);
+
+ /**
+ * The angular force acting on the body
+ * @property angularForce
+ * @type {number}
+ */
+ this.angularForce = options.angularForce || 0;
+
+ /**
+ * The linear damping acting on the body in the velocity direction
+ * @property damping
+ * @type {Number}
+ */
+ this.damping = typeof(options.damping)=="number" ? options.damping : 0.1;
+
+ /**
+ * The angular force acting on the body
+ * @property angularDamping
+ * @type {Number}
+ */
+ this.angularDamping = typeof(options.angularDamping)=="number" ? options.angularDamping : 0.1;
+
+ /**
+ * The type of motion this body has. Should be one of: Body.STATIC (the body
+ * does not move), Body.DYNAMIC (body can move and respond to collisions)
+ * and Body.KINEMATIC (only moves according to its .velocity).
+ *
+ * @property motionState
+ * @type {number}
+ *
+ * @example
+ * // This body will move and interact with other bodies
+ * var dynamicBody = new Body();
+ * dynamicBody.motionState = Body.DYNAMIC;
+ *
+ * @example
+ * // This body will not move at all
+ * var staticBody = new Body();
+ * staticBody.motionState = Body.STATIC;
+ *
+ * @example
+ * // This body will only move if you change its velocity
+ * var kinematicBody = new Body();
+ * kinematicBody.motionState = Body.KINEMATIC;
+ */
+ this.motionState = this.mass == 0 ? Body.STATIC : Body.DYNAMIC;
+
+ /**
+ * Bounding circle radius
+ * @property boundingRadius
+ * @type {Number}
+ */
+ this.boundingRadius = 0;
+
+ /**
+ * Bounding box of this body
+ * @property aabb
+ * @type {AABB}
+ */
+ this.aabb = new AABB();
+
+ /**
+ * Indicates if the AABB needs update. Update it with .updateAABB()
+ * @property aabbNeedsUpdate
+ * @type {Boolean}
+ */
+ this.aabbNeedsUpdate = true;
+
+ /**
+ * If true, the body will automatically fall to sleep.
+ * @property allowSleep
+ * @type {Boolean}
+ */
+ this.allowSleep = false;
+
+ /**
+ * One of Body.AWAKE, Body.SLEEPY, Body.SLEEPING
+ * @property sleepState
+ * @type {Number}
+ */
+ this.sleepState = Body.AWAKE;
+
+ /**
+ * If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy.
+ * @property sleepSpeedLimit
+ * @type {Number}
+ */
+ this.sleepSpeedLimit = 0.1;
+
+ /**
+ * If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping.
+ * @property sleepTimeLimit
+ * @type {Number}
+ */
+ this.sleepTimeLimit = 1;
+
+ this.timeLastSleepy = 0;
+
+ this.concavePath = null;
+
+ this.lastDampingScale = 1;
+ this.lastAngularDampingScale = 1;
+ this.lastDampingTimeStep = -1;
+};
+
+Body.prototype = new EventEmitter();
+
+Body._idCounter = 0;
+
+/**
+ * Set the total density of the body
+ * @method setDensity
+ */
+Body.prototype.setDensity = function(density) {
+ var totalArea = this.getArea();
+ this.mass = totalArea * density;
+ this.updateMassProperties();
+};
+
+/**
+ * Get the total area of all shapes in the body
+ * @method setDensity
+ */
+Body.prototype.getArea = function() {
+ var totalArea = 0;
+ for(var i=0; i radius)
+ radius = offset + r;
+ }
+
+ this.boundingRadius = radius;
+};
+
+/**
+ * Add a shape to the body. You can pass a local transform when adding a shape,
+ * so that the shape gets an offset and angle relative to the body center of mass.
+ * Will automatically update the mass properties and bounding radius.
+ *
+ * @method addShape
+ * @param {Shape} shape
+ * @param {Float32Array|Array} [offset] Local body offset of the shape.
+ * @param {Number} [angle] Local body angle.
+ *
+ * @example
+ * var body = new Body(),
+ * shape = new Circle();
+ *
+ * // Add the shape to the body, positioned in the center
+ * body.addShape(shape);
+ *
+ * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local x-axis.
+ * body.addShape(shape,[1,0]);
+ *
+ * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local y-axis, and rotated 90 degrees CCW.
+ * body.addShape(shape,[0,1],Math.PI/2);
+ */
+Body.prototype.addShape = function(shape,offset,angle){
+ angle = angle || 0.0;
+
+ // Copy the offset vector
+ if(offset){
+ offset = vec2.fromValues(offset[0],offset[1]);
+ } else {
+ offset = vec2.fromValues(0,0);
+ }
+
+ this.shapes .push(shape);
+ this.shapeOffsets.push(offset);
+ this.shapeAngles .push(angle);
+ this.updateMassProperties();
+ this.updateBoundingRadius();
+
+ this.aabbNeedsUpdate = true;
+};
+
+/**
+ * Remove a shape
+ * @method removeShape
+ * @param {Shape} shape
+ * @return {Boolean} True if the shape was found and removed, else false.
+ */
+Body.prototype.removeShape = function(shape){
+ var idx = this.shapes.indexOf(shape);
+
+ if(idx != -1){
+ this.shapes.splice(idx,1);
+ this.shapeOffsets.splice(idx,1);
+ this.shapeAngles.splice(idx,1);
+ this.aabbNeedsUpdate = true;
+ return true;
+ } else
+ return false;
+
+};
+
+/**
+ * Updates .inertia, .invMass, .invInertia for this Body. Should be called when
+ * changing the structure or mass of the Body.
+ *
+ * @method updateMassProperties
+ *
+ * @example
+ * body.mass += 1;
+ * body.updateMassProperties();
+ */
+Body.prototype.updateMassProperties = function(){
+ var shapes = this.shapes,
+ N = shapes.length,
+ m = this.mass / N,
+ I = 0;
+
+ if(!this.fixedRotation){
+ for(var i=0; i 0 ? 1/this.mass : 0;
+ this.invInertia = I>0 ? 1/I : 0;
+};
+
+var Body_applyForce_r = vec2.create();
+
+/**
+ * Apply force to a world point. This could for example be a point on the RigidBody surface. Applying force this way will add to Body.force and Body.angularForce.
+ * @method applyForce
+ * @param {Float32Array} force The force to add.
+ * @param {Float32Array} worldPoint A world point to apply the force on.
+ */
+Body.prototype.applyForce = function(force,worldPoint){
+ // Compute point position relative to the body center
+ var r = Body_applyForce_r;
+ vec2.sub(r,worldPoint,this.position);
+
+ // Add linear force
+ vec2.add(this.force,this.force,force);
+
+ // Compute produced rotational force
+ var rotForce = vec2.crossLength(r,force);
+
+ // Add rotational force
+ this.angularForce += rotForce;
+};
+
+/**
+ * Transform a world point to local body frame.
+ * @method toLocalFrame
+ * @param {Float32Array|Array} out The vector to store the result in
+ * @param {Float32Array|Array} worldPoint The input world vector
+ */
+Body.prototype.toLocalFrame = function(out, worldPoint){
+ vec2.toLocalFrame(out, worldPoint, this.position, this.angle);
+};
+
+/**
+ * Transform a local point to world frame.
+ * @method toWorldFrame
+ * @param {Array} out The vector to store the result in
+ * @param {Array} localPoint The input local vector
+ */
+Body.prototype.toWorldFrame = function(out, localPoint){
+ vec2.toGlobalFrame(out, localPoint, this.position, this.angle);
+};
+
+/**
+ * Reads a polygon shape path, and assembles convex shapes from that and puts them at proper offset points.
+ * @method fromPolygon
+ * @param {Array} path An array of 2d vectors, e.g. [[0,0],[0,1],...] that resembles a concave or convex polygon. The shape must be simple and without holes.
+ * @param {Object} [options]
+ * @param {Boolean} [options.optimalDecomp=false] Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
+ * @param {Boolean} [options.skipSimpleCheck=false] Set to true if you already know that the path is not intersecting itself.
+ * @param {Boolean|Number} [options.removeCollinearPoints=false] Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
+ * @return {Boolean} True on success, else false.
+ */
+Body.prototype.fromPolygon = function(path,options){
+ options = options || {};
+
+ // Remove all shapes
+ for(var i=this.shapes.length; i>=0; --i)
+ this.removeShape(this.shapes[i]);
+
+ var p = new decomp.Polygon();
+ p.vertices = path;
+ // Make it counter-clockwise
+ p.makeCCW();
+
+ if(typeof(options.removeCollinearPoints)=="number"){
+ p.removeCollinearPoints(options.removeCollinearPoints);
+ }
+
+ // Check if any line segment intersects the path itself
+ if(typeof(options.skipSimpleCheck) == "undefined"){
+ if(!p.isSimple()) return false;
+ }
+
+ // Save this path for later
+ this.concavePath = p.vertices.slice(0);
+ for(var i=0; ithis for details.
+ * @method applyDamping
+ * @param {number} dt Current time step
+ */
+Body.prototype.applyDamping = function(dt){
+ if(this.motionState & Body.DYNAMIC){ // Only for dynamic bodies
+
+ // Since Math.pow generates garbage we check if we can reuse the scaling coefficient from last step
+ if(dt != this.lastDampingTimeStep){
+ this.lastDampingScale = Math.pow(1.0 - this.damping,dt);
+ this.lastAngularDampingScale = Math.pow(1.0 - this.angularDamping,dt);
+ this.lastDampingTimeStep = dt;
+ }
+
+ var v = this.velocity;
+ vec2.scale(v,v,this.lastDampingScale);
+ this.angularVelocity *= this.lastAngularDampingScale;
+ }
+};
+
+/**
+ * @method wakeUp
+ * @brief Wake the body up.
+ */
+Body.prototype.wakeUp = function(){
+ var s = this.sleepState;
+ this.sleepState = Body.AWAKE;
+ if(s !== Body.AWAKE){
+ this.emit(Body.wakeUpEvent);
+ }
+};
+
+/**
+ * @method sleep
+ * @brief Force body sleep
+ */
+Body.prototype.sleep = function(){
+ this.sleepState = Body.SLEEPING;
+ this.emit(Body.sleepEvent);
+};
+
+/**
+ * @method sleepTick
+ * @param float time The world time in seconds
+ * @brief Called every timestep to update internal sleep timer and change sleep state if needed.
+ */
+Body.prototype.sleepTick = function(time){
+ if(!this.allowSleep)
+ return;
+
+ var sleepState = this.sleepState,
+ speedSquared = vec2.squaredLength(this.velocity) + Math.pow(this.angularVelocity,2),
+ speedLimitSquared = Math.pow(this.sleepSpeedLimit,2);
+ if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){
+ this.sleepState = Body.SLEEPY; // Sleepy
+ this.timeLastSleepy = time;
+ this.emit(Body.sleepyEvent);
+ } else if(sleepState===Body.SLEEPY && speedSquared > speedLimitSquared){
+ this.wakeUp(); // Wake up
+ } else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){
+ this.sleep();
+ }
+};
+
+Body.sleepyEvent = {
+ type: "sleepy"
+};
+
+Body.sleepEvent = {
+ type: "sleep"
+};
+
+Body.wakeUpEvent = {
+ type: "wakeup"
+};
+
+/**
+ * Dynamic body.
+ * @property DYNAMIC
+ * @type {Number}
+ * @static
+ */
+Body.DYNAMIC = 1;
+
+/**
+ * Static body.
+ * @property STATIC
+ * @type {Number}
+ * @static
+ */
+Body.STATIC = 2;
+
+/**
+ * Kinematic body.
+ * @property KINEMATIC
+ * @type {Number}
+ * @static
+ */
+Body.KINEMATIC = 4;
+
+/**
+ * @property AWAKE
+ * @type {Number}
+ * @static
+ */
+Body.AWAKE = 0;
+
+/**
+ * @property SLEEPY
+ * @type {Number}
+ * @static
+ */
+Body.SLEEPY = 1;
+
+/**
+ * @property SLEEPING
+ * @type {Number}
+ * @static
+ */
+Body.SLEEPING = 2;
+
+
+},{"../collision/AABB":9,"../events/EventEmitter":28,"../math/vec2":33,"../shapes/Convex":39,"poly-decomp":7}],35:[function(require,module,exports){
+var vec2 = require('../math/vec2');
+
+module.exports = Spring;
+
+/**
+ * A spring, connecting two bodies.
+ *
+ * @class Spring
+ * @constructor
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Object} [options]
+ * @param {number} options.restLength A number > 0. Default: 1
+ * @param {number} options.stiffness A number >= 0. Default: 100
+ * @param {number} options.damping A number >= 0. Default: 1
+ * @param {Array} options.worldAnchorA Where to hook the spring to body A, in world coordinates.
+ * @param {Array} options.worldAnchorB
+ * @param {Array} options.localAnchorA Where to hook the spring to body A, in local body coordinates.
+ * @param {Array} options.localAnchorB
+ */
+function Spring(bodyA,bodyB,options){
+ options = options || {};
+
+ /**
+ * Rest length of the spring.
+ * @property restLength
+ * @type {number}
+ */
+ this.restLength = typeof(options.restLength)=="number" ? options.restLength : 1;
+
+ /**
+ * Stiffness of the spring.
+ * @property stiffness
+ * @type {number}
+ */
+ this.stiffness = options.stiffness || 100;
+
+ /**
+ * Damping of the spring.
+ * @property damping
+ * @type {number}
+ */
+ this.damping = options.damping || 1;
+
+ /**
+ * First connected body.
+ * @property bodyA
+ * @type {Body}
+ */
+ this.bodyA = bodyA;
+
+ /**
+ * Second connected body.
+ * @property bodyB
+ * @type {Body}
+ */
+ this.bodyB = bodyB;
+
+ /**
+ * Anchor for bodyA in local bodyA coordinates.
+ * @property localAnchorA
+ * @type {Array}
+ */
+ this.localAnchorA = vec2.fromValues(0,0);
+
+ /**
+ * Anchor for bodyB in local bodyB coordinates.
+ * @property localAnchorB
+ * @type {Array}
+ */
+ this.localAnchorB = vec2.fromValues(0,0);
+
+ if(options.localAnchorA) vec2.copy(this.localAnchorA, options.localAnchorA);
+ if(options.localAnchorB) vec2.copy(this.localAnchorB, options.localAnchorB);
+ if(options.worldAnchorA) this.setWorldAnchorA(options.worldAnchorA);
+ if(options.worldAnchorB) this.setWorldAnchorB(options.worldAnchorB);
+};
+
+/**
+ * Set the anchor point on body A, using world coordinates.
+ * @method setWorldAnchorA
+ * @param {Array} worldAnchorA
+ */
+Spring.prototype.setWorldAnchorA = function(worldAnchorA){
+ this.bodyA.toLocalFrame(this.localAnchorA, worldAnchorA);
+};
+
+/**
+ * Set the anchor point on body B, using world coordinates.
+ * @method setWorldAnchorB
+ * @param {Array} worldAnchorB
+ */
+Spring.prototype.setWorldAnchorB = function(worldAnchorB){
+ this.bodyB.toLocalFrame(this.localAnchorB, worldAnchorB);
+};
+
+/**
+ * Get the anchor point on body A, in world coordinates.
+ * @method getWorldAnchorA
+ * @param {Array} result The vector to store the result in.
+ */
+Spring.prototype.getWorldAnchorA = function(result){
+ this.bodyA.toWorldFrame(result, this.localAnchorA);
+};
+
+/**
+ * Get the anchor point on body B, in world coordinates.
+ * @method getWorldAnchorB
+ * @param {Array} result The vector to store the result in.
+ */
+Spring.prototype.getWorldAnchorB = function(result){
+ this.bodyB.toWorldFrame(result, this.localAnchorB);
+};
+
+var applyForce_r = vec2.create(),
+ applyForce_r_unit = vec2.create(),
+ applyForce_u = vec2.create(),
+ applyForce_f = vec2.create(),
+ applyForce_worldAnchorA = vec2.create(),
+ applyForce_worldAnchorB = vec2.create(),
+ applyForce_ri = vec2.create(),
+ applyForce_rj = vec2.create(),
+ applyForce_tmp = vec2.create();
+
+/**
+ * Apply the spring force to the connected bodies.
+ * @method applyForce
+ */
+Spring.prototype.applyForce = function(){
+ var k = this.stiffness,
+ d = this.damping,
+ l = this.restLength,
+ bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ r = applyForce_r,
+ r_unit = applyForce_r_unit,
+ u = applyForce_u,
+ f = applyForce_f,
+ tmp = applyForce_tmp;
+
+ var worldAnchorA = applyForce_worldAnchorA,
+ worldAnchorB = applyForce_worldAnchorB,
+ ri = applyForce_ri,
+ rj = applyForce_rj;
+
+ // Get world anchors
+ this.getWorldAnchorA(worldAnchorA);
+ this.getWorldAnchorB(worldAnchorB);
+
+ // Get offset points
+ vec2.sub(ri, worldAnchorA, bodyA.position);
+ vec2.sub(rj, worldAnchorB, bodyB.position);
+
+ // Compute distance vector between world anchor points
+ vec2.sub(r, worldAnchorB, worldAnchorA);
+ var rlen = vec2.len(r);
+ vec2.normalize(r_unit,r);
+
+ //console.log(rlen)
+ //console.log("A",vec2.str(worldAnchorA),"B",vec2.str(worldAnchorB))
+
+ // Compute relative velocity of the anchor points, u
+ vec2.sub(u, bodyB.velocity, bodyA.velocity);
+ vec2.crossZV(tmp, bodyB.angularVelocity, rj);
+ vec2.add(u, u, tmp);
+ vec2.crossZV(tmp, bodyA.angularVelocity, ri);
+ vec2.sub(u, u, tmp);
+
+ // F = - k * ( x - L ) - D * ( u )
+ vec2.scale(f, r_unit, -k*(rlen-l) - d*vec2.dot(u,r_unit));
+
+ // Add forces to bodies
+ vec2.sub( bodyA.force, bodyA.force, f);
+ vec2.add( bodyB.force, bodyB.force, f);
+
+ // Angular force
+ var ri_x_f = vec2.crossLength(ri, f);
+ var rj_x_f = vec2.crossLength(rj, f);
+ bodyA.angularForce -= ri_x_f;
+ bodyB.angularForce += rj_x_f;
+};
+
+},{"../math/vec2":33}],36:[function(require,module,exports){
+// Export p2 classes
+module.exports = {
+ AABB : require('./collision/AABB'),
+ AngleLockEquation : require('./equations/AngleLockEquation'),
+ Body : require('./objects/Body'),
+ Broadphase : require('./collision/Broadphase'),
+ Capsule : require('./shapes/Capsule'),
+ Circle : require('./shapes/Circle'),
+ Constraint : require('./constraints/Constraint'),
+ ContactEquation : require('./equations/ContactEquation'),
+ ContactMaterial : require('./material/ContactMaterial'),
+ Convex : require('./shapes/Convex'),
+ DistanceConstraint : require('./constraints/DistanceConstraint'),
+ Equation : require('./equations/Equation'),
+ EventEmitter : require('./events/EventEmitter'),
+ FrictionEquation : require('./equations/FrictionEquation'),
+ GearConstraint : require('./constraints/GearConstraint'),
+ GridBroadphase : require('./collision/GridBroadphase'),
+ GSSolver : require('./solver/GSSolver'),
+ Island : require('./solver/IslandSolver'),
+ IslandSolver : require('./solver/IslandSolver'),
+ Line : require('./shapes/Line'),
+ LockConstraint : require('./constraints/LockConstraint'),
+ Material : require('./material/Material'),
+ NaiveBroadphase : require('./collision/NaiveBroadphase'),
+ Particle : require('./shapes/Particle'),
+ Plane : require('./shapes/Plane'),
+ RevoluteConstraint : require('./constraints/RevoluteConstraint'),
+ PrismaticConstraint : require('./constraints/PrismaticConstraint'),
+ Rectangle : require('./shapes/Rectangle'),
+ RotationalVelocityEquation : require('./equations/RotationalVelocityEquation'),
+ SAPBroadphase : require('./collision/SAPBroadphase'),
+ Shape : require('./shapes/Shape'),
+ Solver : require('./solver/Solver'),
+ Spring : require('./objects/Spring'),
+ Utils : require('./utils/Utils'),
+ World : require('./world/World'),
+ QuadTree : require('./collision/QuadTree').QuadTree,
+ vec2 : require('./math/vec2'),
+ version : require('../package.json').version,
+};
+
+},{"../package.json":8,"./collision/AABB":9,"./collision/Broadphase":10,"./collision/GridBroadphase":11,"./collision/NaiveBroadphase":12,"./collision/QuadTree":14,"./collision/SAPBroadphase":15,"./constraints/Constraint":16,"./constraints/DistanceConstraint":17,"./constraints/GearConstraint":18,"./constraints/LockConstraint":19,"./constraints/PrismaticConstraint":20,"./constraints/RevoluteConstraint":21,"./equations/AngleLockEquation":22,"./equations/ContactEquation":23,"./equations/Equation":24,"./equations/FrictionEquation":25,"./equations/RotationalVelocityEquation":27,"./events/EventEmitter":28,"./material/ContactMaterial":29,"./material/Material":30,"./math/vec2":33,"./objects/Body":34,"./objects/Spring":35,"./shapes/Capsule":37,"./shapes/Circle":38,"./shapes/Convex":39,"./shapes/Line":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Rectangle":43,"./shapes/Shape":44,"./solver/GSSolver":45,"./solver/IslandSolver":47,"./solver/Solver":48,"./utils/Utils":49,"./world/World":50}],37:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Capsule;
+
+/**
+ * Capsule shape class.
+ * @class Capsule
+ * @constructor
+ * @extends {Shape}
+ * @param {Number} length The distance between the end points
+ * @param {Number} radius Radius of the capsule
+ */
+function Capsule(length,radius){
+ this.length = length || 1;
+ this.radius = radius || 1;
+
+ Shape.call(this,Shape.CAPSULE);
+};
+Capsule.prototype = new Shape();
+
+/**
+ * Compute the mass moment of inertia of the Capsule.
+ * @method conputeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number}
+ * @todo
+ */
+Capsule.prototype.computeMomentOfInertia = function(mass){
+ // Approximate with rectangle
+ var r = this.radius,
+ w = this.length + r, // 2*r is too much, 0 is too little
+ h = r*2;
+ return mass * (h*h + w*w) / 12;
+};
+
+Capsule.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.radius + this.length/2;
+};
+
+Capsule.prototype.updateArea = function(){
+ this.area = Math.PI * this.radius * this.radius + this.radius * 2 * this.length;
+};
+
+var r = vec2.create();
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Capsule.prototype.computeAABB = function(out, position, angle){
+ var radius = this.radius;
+
+ // Compute center position of one of the the circles, world oriented, but with local offset
+ vec2.set(r,this.length,0);
+ vec2.rotate(r,r,angle);
+
+ // Get bounds
+ vec2.set(out.upperBound, Math.max(r[0]+radius, -r[0]+radius),
+ Math.max(r[1]+radius, -r[1]+radius));
+ vec2.set(out.lowerBound, Math.min(r[0]-radius, -r[0]-radius),
+ Math.min(r[1]-radius, -r[1]-radius));
+
+ // Add offset
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+};
+
+},{"../math/vec2":33,"./Shape":44}],38:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Circle;
+
+/**
+ * Circle shape class.
+ * @class Circle
+ * @extends {Shape}
+ * @constructor
+ * @param {number} radius The radius of this circle
+ */
+function Circle(radius){
+
+ /**
+ * The radius of the circle.
+ * @property radius
+ * @type {number}
+ */
+ this.radius = radius || 1;
+
+ Shape.call(this,Shape.CIRCLE);
+};
+Circle.prototype = new Shape();
+Circle.prototype.computeMomentOfInertia = function(mass){
+ var r = this.radius;
+ return mass * r * r / 2;
+};
+
+Circle.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.radius;
+};
+
+Circle.prototype.updateArea = function(){
+ this.area = Math.PI * this.radius * this.radius;
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Circle.prototype.computeAABB = function(out, position, angle){
+ var r = this.radius;
+ vec2.set(out.upperBound, r, r);
+ vec2.set(out.lowerBound, -r, -r);
+ if(position){
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+ }
+};
+
+},{"../math/vec2":33,"./Shape":44}],39:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+, polyk = require('../math/polyk')
+, decomp = require('poly-decomp')
+
+module.exports = Convex;
+
+/**
+ * Convex shape class.
+ * @class Convex
+ * @constructor
+ * @extends {Shape}
+ * @param {Array} vertices An array of Float32Array vertices that span this shape. Vertices are given in counter-clockwise (CCW) direction.
+ */
+function Convex(vertices){
+
+ /**
+ * Vertices defined in the local frame.
+ * @property vertices
+ * @type {Array}
+ */
+ this.vertices = vertices || [];
+
+ // Copy the verts
+ for(var i=0; i r2) r2 = l2;
+ }
+
+ this.boundingRadius = Math.sqrt(r2);
+};
+
+/**
+ * Get the area of the triangle spanned by the three points a, b, c. The area is positive if the points are given in counter-clockwise order, otherwise negative.
+ * @static
+ * @method triangleArea
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @return {Number}
+ */
+Convex.triangleArea = function(a,b,c){
+ return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))) * 0.5;
+}
+
+/**
+ * Update the .area
+ * @method updateArea
+ */
+Convex.prototype.updateArea = function(){
+ this.updateTriangles();
+ this.area = 0;
+
+ var triangles = this.triangles,
+ verts = this.vertices;
+ for(var i=0; i!==triangles.length; i++){
+ var t = triangles[i],
+ a = verts[t[0]],
+ b = verts[t[1]],
+ c = verts[t[2]];
+
+ // Get mass for the triangle (density=1 in this case)
+ var m = Convex.triangleArea(a,b,c);
+ this.area += m;
+ }
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Convex.prototype.computeAABB = function(out, position, angle){
+ out.setFromPoints(this.vertices,position,angle);
+};
+
+},{"../math/polyk":32,"../math/vec2":33,"./Shape":44,"poly-decomp":7}],40:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Line;
+
+/**
+ * Line shape class. The line shape is along the x direction, and stretches from [-length/2, 0] to [length/2,0].
+ * @class Line
+ * @param {Number} length The total length of the line
+ * @extends {Shape}
+ * @constructor
+ */
+function Line(length){
+
+ /**
+ * Length of this line
+ * @property length
+ * @type {Number}
+ */
+ this.length = length;
+
+ Shape.call(this,Shape.LINE);
+};
+Line.prototype = new Shape();
+Line.prototype.computeMomentOfInertia = function(mass){
+ return mass * Math.pow(this.length,2) / 12;
+};
+
+Line.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.length/2;
+};
+
+var points = [vec2.create(),vec2.create()];
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Line.prototype.computeAABB = function(out, position, angle){
+ var l = this.length;
+ vec2.set(points[0], -l/2, 0);
+ vec2.set(points[1], l/2, 0);
+ out.setFromPoints(points,position,angle);
+};
+
+
+},{"../math/vec2":33,"./Shape":44}],41:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Particle;
+
+/**
+ * Particle shape class.
+ * @class Particle
+ * @constructor
+ * @extends {Shape}
+ */
+function Particle(){
+ Shape.call(this,Shape.PARTICLE);
+};
+Particle.prototype = new Shape();
+Particle.prototype.computeMomentOfInertia = function(mass){
+ return 0; // Can't rotate a particle
+};
+
+Particle.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = 0;
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Particle.prototype.computeAABB = function(out, position, angle){
+ var l = this.length;
+ vec2.copy(out.lowerBound, position);
+ vec2.copy(out.upperBound, position);
+};
+
+},{"../math/vec2":33,"./Shape":44}],42:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+, Utils = require('../utils/Utils')
+
+module.exports = Plane;
+
+/**
+ * Plane shape class. The plane is facing in the Y direction.
+ * @class Plane
+ * @extends {Shape}
+ * @constructor
+ */
+function Plane(){
+ Shape.call(this,Shape.PLANE);
+};
+Plane.prototype = new Shape();
+
+/**
+ * Compute moment of inertia
+ * @method computeMomentOfInertia
+ */
+Plane.prototype.computeMomentOfInertia = function(mass){
+ return 0; // Plane is infinite. The inertia should therefore be infinty but by convention we set 0 here
+};
+
+/**
+ * Update the bounding radius
+ * @method updateBoundingRadius
+ */
+Plane.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = Number.MAX_VALUE;
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Plane.prototype.computeAABB = function(out, position, angle){
+ var a = 0,
+ set = vec2.set;
+ if(typeof(angle) == "number")
+ a = angle % (2*Math.PI);
+
+ if(a == 0){
+ // y goes from -inf to 0
+ set(out.lowerBound, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ set(out.upperBound, Number.MAX_VALUE, 0);
+ } else if(a == Math.PI / 2){
+ // x goes from 0 to inf
+ set(out.lowerBound, 0, -Number.MAX_VALUE);
+ set(out.upperBound, Number.MAX_VALUE, Number.MAX_VALUE);
+ } else if(a == Math.PI){
+ // y goes from 0 to inf
+ set(out.lowerBound, -Number.MAX_VALUE, 0);
+ set(out.upperBound, Number.MAX_VALUE, Number.MAX_VALUE);
+ } else if(a == 3*Math.PI/2){
+ // x goes from -inf to 0
+ set(out.lowerBound, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ set(out.upperBound, 0, Number.MAX_VALUE);
+ } else {
+ // Set max bounds
+ set(out.lowerBound, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ set(out.upperBound, Number.MAX_VALUE, Number.MAX_VALUE);
+ }
+
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+};
+
+Plane.prototype.updateArea = function(){
+ this.area = Number.MAX_VALUE;
+};
+
+
+},{"../math/vec2":33,"../utils/Utils":49,"./Shape":44}],43:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, Shape = require('./Shape')
+, Convex = require('./Convex')
+
+module.exports = Rectangle;
+
+/**
+ * Rectangle shape class.
+ * @class Rectangle
+ * @constructor
+ * @param {Number} w Width
+ * @param {Number} h Height
+ * @extends {Convex}
+ */
+function Rectangle(w,h){
+ var verts = [ vec2.fromValues(-w/2, -h/2),
+ vec2.fromValues( w/2, -h/2),
+ vec2.fromValues( w/2, h/2),
+ vec2.fromValues(-w/2, h/2)];
+
+ /**
+ * Total width of the rectangle
+ * @property width
+ * @type {Number}
+ */
+ this.width = w;
+
+ /**
+ * Total height of the rectangle
+ * @property height
+ * @type {Number}
+ */
+ this.height = h;
+
+ Convex.call(this,verts);
+};
+Rectangle.prototype = new Convex();
+
+/**
+ * Compute moment of inertia
+ * @method computeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number}
+ */
+Rectangle.prototype.computeMomentOfInertia = function(mass){
+ var w = this.width,
+ h = this.height;
+ return mass * (h*h + w*w) / 12;
+};
+
+/**
+ * Update the bounding radius
+ * @method updateBoundingRadius
+ */
+Rectangle.prototype.updateBoundingRadius = function(){
+ var w = this.width,
+ h = this.height;
+ this.boundingRadius = Math.sqrt(w*w + h*h) / 2;
+};
+
+var corner1 = vec2.create(),
+ corner2 = vec2.create(),
+ corner3 = vec2.create(),
+ corner4 = vec2.create();
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Rectangle.prototype.computeAABB = function(out, position, angle){
+ /*
+ // Get world corners
+ vec2.rotate(corner1,this.vertices[0],angle);
+ vec2.rotate(corner2,this.vertices[1],angle);
+ vec2.rotate(corner3,this.vertices[2],angle);
+ vec2.rotate(corner4,this.vertices[3],angle);
+ vec2.set(out.upperBound, Math.max(corner1[0],corner2[0],corner3[0],corner4[0]),
+ Math.max(corner1[1],corner2[1],corner3[1],corner4[1]));
+ vec2.set(out.lowerBound, Math.min(corner1[0],corner2[0],corner3[0],corner4[0]),
+ Math.min(corner1[1],corner2[1],corner3[1],corner4[1]));
+
+ // Add world offset
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+ */
+ out.setFromPoints(this.vertices,position,angle);
+};
+
+Rectangle.prototype.updateArea = function(){
+ this.area = this.width * this.height;
+};
+
+
+},{"../math/vec2":33,"./Convex":39,"./Shape":44}],44:[function(require,module,exports){
+module.exports = Shape;
+
+/**
+ * Base class for shapes.
+ * @class Shape
+ * @constructor
+ */
+function Shape(type){
+ this.type = type;
+
+ /**
+ * Shape object identifier.
+ * @type {Number}
+ * @property id
+ */
+ this.id = Shape.idCounter++;
+
+ /**
+ * Bounding circle radius of this shape
+ * @property boundingRadius
+ * @type {Number}
+ */
+ this.boundingRadius = 0;
+
+ /**
+ * Collision group that this shape belongs to (bit mask). See this tutorial.
+ * @property collisionGroup
+ * @type {Number}
+ * @example
+ * // Setup bits for each available group
+ * var PLAYER = Math.pow(2,0),
+ * ENEMY = Math.pow(2,1),
+ * GROUND = Math.pow(2,2)
+ *
+ * // Put shapes into their groups
+ * player1Shape.collisionGroup = PLAYER;
+ * player2Shape.collisionGroup = PLAYER;
+ * enemyShape .collisionGroup = ENEMY;
+ * groundShape .collisionGroup = GROUND;
+ *
+ * // Assign groups that each shape collide with.
+ * // Note that the players can collide with ground and enemies, but not with other players.
+ * player1Shape.collisionMask = ENEMY | GROUND;
+ * player2Shape.collisionMask = ENEMY | GROUND;
+ * enemyShape .collisionMask = PLAYER | GROUND;
+ * groundShape .collisionMask = PLAYER | ENEMY;
+ *
+ * @example
+ * // How collision check is done
+ * if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
+ * // The shapes will collide
+ * }
+ */
+ this.collisionGroup = 1;
+
+ /**
+ * Collision mask of this shape. See .collisionGroup.
+ * @property collisionMask
+ * @type {Number}
+ */
+ this.collisionMask = 1;
+ if(type) this.updateBoundingRadius();
+
+ /**
+ * Material to use in collisions for this Shape. If this is set to null, the world will use default material properties instead.
+ * @property material
+ * @type {Material}
+ */
+ this.material = null;
+
+ /**
+ * Area of this shape.
+ * @property area
+ * @type {Number}
+ */
+ this.area = 0;
+
+ /**
+ * Set to true if you want this shape to be a sensor. A sensor does not generate contacts, but it still reports contact events. This is good if you want to know if a shape is overlapping another shape, without them generating contacts.
+ * @property {Boolean} sensor
+ */
+ this.sensor = false;
+
+ this.updateArea();
+};
+
+Shape.idCounter = 0;
+
+Shape.CIRCLE = 1;
+Shape.PARTICLE = 2;
+Shape.PLANE = 4;
+Shape.CONVEX = 8;
+Shape.LINE = 16;
+Shape.RECTANGLE = 32;
+Shape.CAPSULE = 64;
+
+/**
+ * Should return the moment of inertia around the Z axis of the body given the total mass. See Wikipedia's list of moments of inertia.
+ * @method computeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number} If the inertia is infinity or if the object simply isn't possible to rotate, return 0.
+ */
+Shape.prototype.computeMomentOfInertia = function(mass){
+ throw new Error("Shape.computeMomentOfInertia is not implemented in this Shape...");
+};
+
+/**
+ * Returns the bounding circle radius of this shape.
+ * @method updateBoundingRadius
+ * @return {Number}
+ */
+Shape.prototype.updateBoundingRadius = function(){
+ throw new Error("Shape.updateBoundingRadius is not implemented in this Shape...");
+};
+
+/**
+ * Update the .area property of the shape.
+ * @method updateArea
+ */
+Shape.prototype.updateArea = function(){
+ // To be implemented in all subclasses
+};
+
+/**
+ * Compute the world axis-aligned bounding box (AABB) of this shape.
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Shape.prototype.computeAABB = function(out, position, angle){
+ // To be implemented in each subclass
+};
+
+},{}],45:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, Solver = require('./Solver')
+, Utils = require('../utils/Utils')
+, FrictionEquation = require('../equations/FrictionEquation')
+
+module.exports = GSSolver;
+
+/**
+ * Iterative Gauss-Seidel constraint equation solver.
+ *
+ * @class GSSolver
+ * @constructor
+ * @extends Solver
+ * @param {Object} [options]
+ * @param {Number} options.iterations
+ * @param {Number} options.timeStep
+ * @param {Number} options.stiffness
+ * @param {Number} options.relaxation
+ * @param {Number} options.tolerance
+ */
+function GSSolver(options){
+ Solver.call(this,options);
+ options = options || {};
+
+ /**
+ * The number of iterations to do when solving. More gives better results, but is more expensive.
+ * @property iterations
+ * @type {Number}
+ */
+ this.iterations = options.iterations || 10;
+
+ /**
+ * The error tolerance. If the total error is below this limit, the solver will stop. Set to zero for as good solution as possible.
+ * @property tolerance
+ * @type {Number}
+ */
+ this.tolerance = options.tolerance || 0;
+
+ this.debug = options.debug || false;
+ this.arrayStep = 30;
+ this.lambda = new Utils.ARRAY_TYPE(this.arrayStep);
+ this.Bs = new Utils.ARRAY_TYPE(this.arrayStep);
+ this.invCs = new Utils.ARRAY_TYPE(this.arrayStep);
+
+ /**
+ * Whether to use .stiffness and .relaxation parameters from the Solver instead of each Equation individually.
+ * @type {Boolean}
+ * @property useGlobalEquationParameters
+ */
+ this.useGlobalEquationParameters = true;
+
+ /**
+ * Global equation stiffness. Larger number gives harder contacts, etc, but may also be more expensive to compute, or it will make your simulation explode.
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = 1e6;
+
+ /**
+ * Global equation relaxation. This is the number of timesteps required for a constraint to be resolved. Larger number will give softer contacts. Set to around 3 or 4 for good enough results.
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = 4;
+
+ /**
+ * Set to true to set all right hand side terms to zero when solving. Can be handy for a few applications.
+ * @property useZeroRHS
+ * @type {Boolean}
+ */
+ this.useZeroRHS = false;
+
+ /**
+ * Number of friction iterations to skip. If .skipFrictionIterations=2, then no FrictionEquations will be iterated until the third iteration.
+ * @property skipFrictionIterations
+ * @type {Number}
+ */
+ this.skipFrictionIterations = 0;
+};
+GSSolver.prototype = new Solver();
+
+function setArrayZero(array){
+ for(var i=0; i!==array.length; i++){
+ array[i] = 0.0;
+ }
+}
+
+/**
+ * Solve the system of equations
+ * @method solve
+ * @param {Number} h Time step
+ * @param {World} world World to solve
+ */
+GSSolver.prototype.solve = function(h,world){
+
+ this.sortEquations();
+
+ var iter = 0,
+ maxIter = this.iterations,
+ skipFrictionIter = this.skipFrictionIterations,
+ tolSquared = this.tolerance*this.tolerance,
+ equations = this.equations,
+ Neq = equations.length,
+ bodies = world.bodies,
+ Nbodies = world.bodies.length,
+ d = this.relaxation,
+ k = this.stiffness,
+ eps = 4.0 / (h * h * k * (1 + 4 * d)),
+ a = 4.0 / (h * (1 + 4 * d)),
+ b = (4.0 * d) / (1 + 4 * d),
+ useGlobalParams = this.useGlobalEquationParameters,
+ add = vec2.add,
+ set = vec2.set,
+ useZeroRHS = this.useZeroRHS,
+ lambda = this.lambda;
+
+ // Things that does not change during iteration can be computed once
+ if(lambda.length < Neq){
+ lambda = this.lambda = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
+ this.Bs = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
+ this.invCs = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
+ } else {
+ setArrayZero(lambda);
+ }
+ var invCs = this.invCs,
+ Bs = this.Bs,
+ lambda = this.lambda;
+ if(!useGlobalParams){
+ for(var i=0, c; c = equations[i]; i++){
+ if(h !== c.h) c.updateSpookParams(h);
+ Bs[i] = c.computeB(c.a,c.b,h);
+ invCs[i] = c.computeInvC(c.eps);
+ }
+ } else {
+ for(var i=0, c; c = equations[i]; i++){
+ Bs[i] = c.computeB(a,b,h);
+ invCs[i] = c.computeInvC(eps);
+ }
+ }
+
+ var q, B, c, deltalambdaTot,i,j;
+
+ if(Neq !== 0){
+
+ // Reset vlambda
+ for(i=0; i!==Nbodies; i++){
+ bodies[i].resetConstraintVelocity();
+ }
+
+ // Iterate over equations
+ for(iter=0; iter!==maxIter; iter++){
+
+ // Accumulate the total error for each iteration.
+ deltalambdaTot = 0.0;
+
+ for(j=0; j!==Neq; j++){
+ c = equations[j];
+
+ if(c instanceof FrictionEquation && iter < skipFrictionIter)
+ continue;
+
+ var _eps = useGlobalParams ? eps : c.eps;
+
+ var deltalambda = GSSolver.iterateEquation(j,c,_eps,Bs,invCs,lambda,useZeroRHS,h);
+ deltalambdaTot += Math.abs(deltalambda);
+ }
+
+ // If the total error is small enough - stop iterate
+ if(deltalambdaTot*deltalambdaTot <= tolSquared) break;
+ }
+
+ // Add result to velocity
+ for(i=0; i!==Nbodies; i++){
+ bodies[i].addConstraintVelocity();
+ }
+ }
+};
+
+GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt){
+ // Compute iteration
+ var B = Bs[j],
+ invC = invCs[j],
+ lambdaj = lambda[j],
+ GWlambda = eq.computeGWlambda(eps);
+
+ if(eq instanceof FrictionEquation){
+ // Rescale the max friction force according to the normal force
+ eq.maxForce = eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
+ eq.minForce = -eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
+ }
+
+ var maxForce = eq.maxForce,
+ minForce = eq.minForce;
+
+ if(useZeroRHS) B = 0;
+
+ var deltalambda = invC * ( B - GWlambda - eps * lambdaj );
+
+ // Clamp if we are not within the min/max interval
+ var lambdaj_plus_deltalambda = lambdaj + deltalambda;
+ if(lambdaj_plus_deltalambda < minForce){
+ deltalambda = minForce - lambdaj;
+ } else if(lambdaj_plus_deltalambda > maxForce){
+ deltalambda = maxForce - lambdaj;
+ }
+ lambda[j] += deltalambda;
+ eq.multiplier = lambda[j] / dt;
+ eq.addToWlambda(deltalambda);
+
+ return deltalambda;
+};
+
+},{"../equations/FrictionEquation":25,"../math/vec2":33,"../utils/Utils":49,"./Solver":48}],46:[function(require,module,exports){
+module.exports = Island;
+
+/**
+ * An island of bodies connected with equations.
+ * @class Island
+ * @constructor
+ */
+function Island(){
+
+ /**
+ * Current equations in this island.
+ * @property equations
+ * @type {Array}
+ */
+ this.equations = [];
+
+ /**
+ * Current bodies in this island.
+ * @property bodies
+ * @type {Array}
+ */
+ this.bodies = [];
+}
+
+/**
+ * Clean this island from bodies and equations.
+ * @method reset
+ */
+Island.prototype.reset = function(){
+ this.equations.length = this.bodies.length = 0;
+}
+
+
+/**
+ * Get all unique bodies in this island.
+ * @method getBodies
+ * @return {Array} An array of Body
+ */
+Island.prototype.getBodies = function(){
+ var bodies = [],
+ bodyIds = [],
+ eqs = this.equations;
+ for(var i=0; i!==eqs.length; i++){
+ var eq = eqs[i];
+ if(bodyIds.indexOf(eq.bi.id)===-1){
+ bodies.push(eq.bi);
+ bodyIds.push(eq.bi.id);
+ }
+ if(bodyIds.indexOf(eq.bj.id)===-1){
+ bodies.push(eq.bj);
+ bodyIds.push(eq.bj.id);
+ }
+ }
+ return bodies;
+};
+
+/**
+ * Solves all constraints in the group of islands.
+ * @method solve
+ * @param {Number} dt
+ * @param {Solver} solver
+ */
+Island.prototype.solve = function(dt,solver){
+ var bodies = [];
+
+ solver.removeAllEquations();
+
+ // Add equations to solver
+ var numEquations = this.equations.length;
+ for(var j=0; j!==numEquations; j++){
+ solver.addEquation(this.equations[j]);
+ }
+ var islandBodies = this.getBodies();
+ var numBodies = islandBodies.length;
+ for(var j=0; j!==numBodies; j++){
+ bodies.push(islandBodies[j]);
+ }
+
+ // Solve
+ solver.solve(dt,{bodies:bodies});
+};
+
+},{}],47:[function(require,module,exports){
+var Solver = require('./Solver')
+, vec2 = require('../math/vec2')
+, Island = require('../solver/Island')
+, Body = require('../objects/Body')
+, STATIC = Body.STATIC
+
+module.exports = IslandSolver;
+
+/**
+ * Splits the system of bodies and equations into independent islands
+ *
+ * @class IslandSolver
+ * @constructor
+ * @param {Solver} subsolver
+ * @param {Object} options
+ * @extends Solver
+ */
+function IslandSolver(subsolver,options){
+ Solver.call(this,options);
+ var that = this;
+
+ /**
+ * The solver used in the workers.
+ * @property subsolver
+ * @type {Solver}
+ */
+ this.subsolver = subsolver;
+
+ /**
+ * Number of islands. Read only.
+ * @property numIslands
+ * @type {number}
+ */
+ this.numIslands = 0;
+
+ // Pooling of node objects saves some GC load
+ this._nodePool = [];
+
+ this._islandPool = [];
+
+ /**
+ * Fires before an island is solved.
+ * @event beforeSolveIsland
+ * @param {Island} island
+ */
+ this.beforeSolveIslandEvent = {
+ type : "beforeSolveIsland",
+ island : null,
+ };
+};
+IslandSolver.prototype = new Solver();
+
+function getUnvisitedNode(nodes){
+ var Nnodes = nodes.length;
+ for(var i=0; i!==Nnodes; i++){
+ var node = nodes[i];
+ if(!node.visited && !(node.body.motionState & STATIC)){ // correct?
+ return node;
+ }
+ }
+ return false;
+}
+
+function visitFunc(node,bds,eqs){
+ bds.push(node.body);
+ var Neqs = node.eqs.length;
+ for(var i=0; i!==Neqs; i++){
+ var eq = node.eqs[i];
+ if(eqs.indexOf(eq) === -1){
+ eqs.push(eq);
+ }
+ }
+}
+
+var queue = [];
+function bfs(root,visitFunc,bds,eqs){
+ queue.length = 0;
+ queue.push(root);
+ root.visited = true;
+ visitFunc(root,bds,eqs);
+ while(queue.length) {
+ var node = queue.pop();
+ // Loop over unvisited child nodes
+ var child;
+ while((child = getUnvisitedNode(node.children))) {
+ child.visited = true;
+ visitFunc(child,bds,eqs);
+ queue.push(child);
+ }
+ }
+}
+
+var tmpArray = [],
+ tmpArray2 = [],
+ tmpArray3 = [],
+ tmpArray4 = [];
+
+/**
+ * Solves the full system.
+ * @method solve
+ * @param {Number} dt
+ * @param {World} world
+ */
+IslandSolver.prototype.solve = function(dt,world){
+ var nodes = tmpArray,
+ bodies=world.bodies,
+ equations=this.equations,
+ Neq=equations.length,
+ Nbodies=bodies.length,
+ subsolver=this.subsolver,
+ workers = this._workers,
+ workerData = this._workerData,
+ workerIslandGroups = this._workerIslandGroups,
+ islandPool = this._islandPool;
+
+ tmpArray.length = 0;
+
+ // Create needed nodes, reuse if possible
+ for(var i=0; i!==Nbodies; i++){
+ if(this._nodePool.length)
+ nodes.push( this._nodePool.pop() );
+ else {
+ nodes.push({
+ body:bodies[i],
+ children:[],
+ eqs:[],
+ visited:false
+ });
+ }
+ }
+
+ // Reset node values
+ for(var i=0; i!==Nbodies; i++){
+ var node = nodes[i];
+ node.body = bodies[i];
+ node.children.length = 0;
+ node.eqs.length = 0;
+ node.visited = false;
+ }
+
+ // Add connectivity data. Each equation connects 2 bodies.
+ for(var k=0; k!==Neq; k++){
+ var eq=equations[k],
+ i=bodies.indexOf(eq.bi),
+ j=bodies.indexOf(eq.bj),
+ ni=nodes[i],
+ nj=nodes[j];
+ ni.children.push(nj);
+ ni.eqs.push(eq);
+ nj.children.push(ni);
+ nj.eqs.push(eq);
+ }
+
+ // The BFS search algorithm needs a traversal function. What we do is gather all bodies and equations connected.
+ var child, n=0, eqs=tmpArray2, bds=tmpArray3;
+ eqs.length = 0;
+ bds.length = 0;
+
+ // Get islands
+ var islands = tmpArray4;
+ islands.length = 0;
+ while((child = getUnvisitedNode(nodes))){
+ var island = islandPool.length ? islandPool.pop() : new Island();
+ eqs.length = 0;
+ bds.length = 0;
+ bfs(child,visitFunc,bds,eqs); // run search algo to gather an island of bodies
+
+ // Add equations to island
+ var Neqs = eqs.length;
+ for(var i=0; i!==Neqs; i++){
+ var eq = eqs[i];
+ island.equations.push(eq);
+ }
+
+ n++;
+ islands.push(island);
+ }
+
+ this.numIslands = n;
+
+ // Solve islands
+ var e = this.beforeSolveIslandEvent;
+ for(var i=0; ithis for an explanation.
+ * @method appendArray
+ * @static
+ * @param {Array} a
+ * @param {Array} b
+ */
+Utils.appendArray = function(a,b){
+ if (b.length < 150000) {
+ a.push.apply(a, b)
+ } else {
+ for (var i = 0, len = b.length; i !== len; ++i) {
+ a.push(b[i]);
+ }
+ }
+};
+
+/**
+ * Garbage free Array.splice(). Does not allocate a new array.
+ * @method splice
+ * @static
+ * @param {Array} array
+ * @param {Number} index
+ * @param {Number} howmany
+ */
+Utils.splice = function(array,index,howmany){
+ howmany = howmany || 1;
+ for (var i=index, len=array.length-howmany; i < len; i++)
+ array[i] = array[i + howmany];
+ array.length = len;
+};
+
+/**
+ * The array type to use for internal numeric computations.
+ * @type {Array}
+ * @static
+ * @property ARRAY_TYPE
+ */
+Utils.ARRAY_TYPE = Float32Array || Array;
+
+},{}],50:[function(require,module,exports){
+var GSSolver = require('../solver/GSSolver')
+, NaiveBroadphase = require('../collision/NaiveBroadphase')
+, vec2 = require('../math/vec2')
+, Circle = require('../shapes/Circle')
+, Rectangle = require('../shapes/Rectangle')
+, Convex = require('../shapes/Convex')
+, Line = require('../shapes/Line')
+, Plane = require('../shapes/Plane')
+, Capsule = require('../shapes/Capsule')
+, Particle = require('../shapes/Particle')
+, EventEmitter = require('../events/EventEmitter')
+, Body = require('../objects/Body')
+, Spring = require('../objects/Spring')
+, Material = require('../material/Material')
+, ContactMaterial = require('../material/ContactMaterial')
+, DistanceConstraint = require('../constraints/DistanceConstraint')
+, LockConstraint = require('../constraints/LockConstraint')
+, RevoluteConstraint = require('../constraints/RevoluteConstraint')
+, PrismaticConstraint = require('../constraints/PrismaticConstraint')
+, pkg = require('../../package.json')
+, Broadphase = require('../collision/Broadphase')
+, Narrowphase = require('../collision/Narrowphase')
+, Utils = require('../utils/Utils')
+
+module.exports = World;
+
+var currentVersion = pkg.version.split(".").slice(0,2).join("."); // "X.Y"
+
+if(typeof performance === 'undefined')
+ performance = {};
+if(!performance.now){
+ var nowOffset = Date.now();
+ if (performance.timing && performance.timing.navigationStart){
+ nowOffset = performance.timing.navigationStart
+ }
+ performance.now = function(){
+ return Date.now() - nowOffset;
+ }
+}
+
+/**
+ * The dynamics world, where all bodies and constraints lives.
+ *
+ * @class World
+ * @constructor
+ * @param {Object} [options]
+ * @param {Solver} options.solver Defaults to GSSolver.
+ * @param {Float32Array} options.gravity Defaults to [0,-9.78]
+ * @param {Broadphase} options.broadphase Defaults to NaiveBroadphase
+ * @extends {EventEmitter}
+ */
+function World(options){
+ EventEmitter.apply(this);
+
+ options = options || {};
+
+ /**
+ * All springs in the world.
+ *
+ * @property springs
+ * @type {Array}
+ */
+ this.springs = [];
+
+ /**
+ * All bodies in the world.
+ *
+ * @property bodies
+ * @type {Array}
+ */
+ this.bodies = [];
+
+ /**
+ * The solver used to satisfy constraints and contacts.
+ *
+ * @property solver
+ * @type {Solver}
+ */
+ this.solver = options.solver || new GSSolver();
+
+ /**
+ * The narrowphase to use to generate contacts.
+ *
+ * @property narrowphase
+ * @type {Narrowphase}
+ */
+ this.narrowphase = new Narrowphase(this);
+
+ /**
+ * Gravity in the world. This is applied on all bodies in the beginning of each step().
+ *
+ * @property
+ * @type {Float32Array}
+ */
+ this.gravity = options.gravity || vec2.fromValues(0, -9.78);
+
+ /**
+ * Whether to do timing measurements during the step() or not.
+ *
+ * @property doPofiling
+ * @type {Boolean}
+ */
+ this.doProfiling = options.doProfiling || false;
+
+ /**
+ * How many millisecconds the last step() took. This is updated each step if .doProfiling is set to true.
+ *
+ * @property lastStepTime
+ * @type {Number}
+ */
+ this.lastStepTime = 0.0;
+
+ /**
+ * The broadphase algorithm to use.
+ *
+ * @property broadphase
+ * @type {Broadphase}
+ */
+ this.broadphase = options.broadphase || new NaiveBroadphase();
+
+ this.broadphase.setWorld(this);
+
+ /**
+ * User-added constraints.
+ *
+ * @property constraints
+ * @type {Array}
+ */
+ this.constraints = [];
+
+ /**
+ * Friction between colliding bodies. This value is used if no matching ContactMaterial is found for a Material pair.
+ * @property defaultFriction
+ * @type {Number}
+ */
+ this.defaultFriction = 0.3;
+
+ /**
+ * Default coefficient of restitution between colliding bodies. This value is used if no matching ContactMaterial is found for a Material pair.
+ * @property defaultRestitution
+ * @type {Number}
+ */
+ this.defaultRestitution = 0.0;
+
+ /**
+ * For keeping track of what time step size we used last step
+ * @property lastTimeStep
+ * @type {Number}
+ */
+ this.lastTimeStep = 1/60;
+
+ /**
+ * Enable to automatically apply spring forces each step.
+ * @property applySpringForces
+ * @type {Boolean}
+ */
+ this.applySpringForces = true;
+
+ /**
+ * Enable to automatically apply body damping each step.
+ * @property applyDamping
+ * @type {Boolean}
+ */
+ this.applyDamping = true;
+
+ /**
+ * Enable to automatically apply gravity each step.
+ * @property applyGravity
+ * @type {Boolean}
+ */
+ this.applyGravity = true;
+
+ /**
+ * Enable/disable constraint solving in each step.
+ * @property solveConstraints
+ * @type {Boolean}
+ */
+ this.solveConstraints = true;
+
+ /**
+ * The ContactMaterials added to the World.
+ * @property contactMaterials
+ * @type {Array}
+ */
+ this.contactMaterials = [];
+
+ /**
+ * World time.
+ * @property time
+ * @type {Number}
+ */
+ this.time = 0.0;
+
+ this.fixedStepTime = 0.0;
+
+ /**
+ * Set to true if you want to the world to emit the "impact" event. Turning this off could improve performance.
+ * @property emitImpactEvent
+ * @type {Boolean}
+ */
+ this.emitImpactEvent = true;
+
+ // Id counters
+ this._constraintIdCounter = 0;
+ this._bodyIdCounter = 0;
+
+ /**
+ * Fired after the step().
+ * @event postStep
+ */
+ this.postStepEvent = {
+ type : "postStep",
+ };
+
+ /**
+ * @event addBody
+ * @param {Body} body
+ */
+ this.addBodyEvent = {
+ type : "addBody",
+ body : null
+ };
+
+ /**
+ * @event removeBody
+ * @param {Body} body
+ */
+ this.removeBodyEvent = {
+ type : "removeBody",
+ body : null
+ };
+
+ /**
+ * Fired when a spring is added to the world.
+ * @event addSpring
+ * @param {Spring} spring
+ */
+ this.addSpringEvent = {
+ type : "addSpring",
+ spring : null,
+ };
+
+ /**
+ * Fired when a first contact is created between two bodies. This event is fired after the step has been done.
+ * @event impact
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ */
+ this.impactEvent = {
+ type: "impact",
+ bodyA : null,
+ bodyB : null,
+ shapeA : null,
+ shapeB : null,
+ contactEquation : null,
+ };
+
+ /**
+ * Fired after the Broadphase has collected collision pairs in the world.
+ * Inside the event handler, you can modify the pairs array as you like, to
+ * prevent collisions between objects that you don't want.
+ * @event postBroadphase
+ * @param {Array} pairs An array of collision pairs. If this array is [body1,body2,body3,body4], then the body pairs 1,2 and 3,4 would advance to narrowphase.
+ */
+ this.postBroadphaseEvent = {
+ type:"postBroadphase",
+ pairs:null,
+ };
+
+ /**
+ * Enable / disable automatic body sleeping
+ * @property allowSleep
+ * @type {Boolean}
+ */
+ this.enableBodySleeping = false;
+
+ /**
+ * Fired when two shapes starts start to touch (and create contacts).
+ * @event beginContact
+ * @param {Shape} shapeA
+ * @param {Shape} shapeB
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Array} contactEquations
+ */
+ this.beginContactEvent = {
+ type:"beginContact",
+ shapeA : null,
+ shapeB : null,
+ bodyA : null,
+ bodyB : null,
+ contactEquations : [],
+ };
+
+ /**
+ * Fired when two shapes stop touching.
+ * @event endContact
+ * @param {Shape} shapeA
+ * @param {Shape} shapeB
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Array} contactEquations
+ */
+ this.endContactEvent = {
+ type:"endContact",
+ shapeA : null,
+ shapeB : null,
+ bodyA : null,
+ bodyB : null,
+ };
+
+ /**
+ * Fired just before equations are added to the solver to be solved. Can be used to control what equations goes into the solver.
+ * @event preSolve
+ * @param {Array} contactEquations An array of contacts to be solved.
+ * @param {Array} frictionEquations An array of friction equations to be solved.
+ */
+ this.preSolveEvent = {
+ type:"preSolve",
+ contactEquations:null,
+ frictionEquations:null,
+ };
+
+ // For keeping track of overlapping shapes
+ this.overlappingShapesLastState = { keys:[] };
+ this.overlappingShapesCurrentState = { keys:[] };
+ this.overlappingShapeLookup = { keys:[] };
+};
+World.prototype = new Object(EventEmitter.prototype);
+
+/**
+ * Add a constraint to the simulation.
+ *
+ * @method addConstraint
+ * @param {Constraint} c
+ */
+World.prototype.addConstraint = function(c){
+ this.constraints.push(c);
+};
+
+/**
+ * Add a ContactMaterial to the simulation.
+ * @method addContactMaterial
+ * @param {ContactMaterial} contactMaterial
+ */
+World.prototype.addContactMaterial = function(contactMaterial){
+ this.contactMaterials.push(contactMaterial);
+};
+
+/**
+ * Removes a contact material
+ *
+ * @method removeContactMaterial
+ * @param {ContactMaterial} cm
+ */
+World.prototype.removeContactMaterial = function(cm){
+ var idx = this.contactMaterials.indexOf(cm);
+ if(idx!==-1)
+ Utils.splice(this.contactMaterials,idx,1);
+};
+
+/**
+ * Get a contact material given two materials
+ * @method getContactMaterial
+ * @param {Material} materialA
+ * @param {Material} materialB
+ * @return {ContactMaterial} The matching ContactMaterial, or false on fail.
+ * @todo Use faster hash map to lookup from material id's
+ */
+World.prototype.getContactMaterial = function(materialA,materialB){
+ var cmats = this.contactMaterials;
+ for(var i=0, N=cmats.length; i!==N; i++){
+ var cm = cmats[i];
+ if( (cm.materialA === materialA) && (cm.materialB === materialB) ||
+ (cm.materialA === materialB) && (cm.materialB === materialA) )
+ return cm;
+ }
+ return false;
+};
+
+/**
+ * Removes a constraint
+ *
+ * @method removeConstraint
+ * @param {Constraint} c
+ */
+World.prototype.removeConstraint = function(c){
+ var idx = this.constraints.indexOf(c);
+ if(idx!==-1){
+ Utils.splice(this.constraints,idx,1);
+ }
+};
+
+var step_r = vec2.create(),
+ step_runit = vec2.create(),
+ step_u = vec2.create(),
+ step_f = vec2.create(),
+ step_fhMinv = vec2.create(),
+ step_velodt = vec2.create(),
+ step_mg = vec2.create(),
+ xiw = vec2.fromValues(0,0),
+ xjw = vec2.fromValues(0,0),
+ zero = vec2.fromValues(0,0);
+
+/**
+ * Step the physics world forward in time.
+ *
+ * There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take.
+ *
+ * @method step
+ * @param {Number} dt The fixed time step size to use.
+ * @param {Number} [timeSinceLastCalled=0] The time elapsed since the function was last called.
+ * @param {Number} [maxSubSteps=10] Maximum number of fixed steps to take per function call.
+ *
+ * @example
+ * // fixed timestepping without interpolation
+ * var world = new World();
+ * world.step(0.01);
+ */
+World.prototype.step = function(dt,timeSinceLastCalled,maxSubSteps){
+ maxSubSteps = maxSubSteps || 10;
+ timeSinceLastCalled = timeSinceLastCalled || 0;
+
+ if(timeSinceLastCalled == 0){ // Fixed, simple stepping
+
+ this.internalStep(dt);
+
+ // Increment time
+ this.time += dt;
+
+ } else {
+
+ var internalSteps = Math.floor( (this.time+timeSinceLastCalled) / dt) - Math.floor(this.time / dt);
+ internalSteps = Math.min(internalSteps,maxSubSteps);
+
+ for(var i=0; i0){
+ World.integrateBody(body,dt);
+ }
+ }
+
+ // Reset force
+ for(var i=0; i!==Nbodies; i++){
+ bodies[i].setZeroForce();
+ }
+
+ if(doProfiling){
+ t1 = performance.now();
+ that.lastStepTime = t1-t0;
+ }
+
+ // Emit impact event
+ if(this.emitImpactEvent){
+ var ev = this.impactEvent;
+ for(var i=0; i!==np.contactEquations.length; i++){
+ var eq = np.contactEquations[i];
+ if(eq.firstImpact){
+ ev.bodyA = eq.bi;
+ ev.bodyB = eq.bj;
+ ev.shapeA = eq.shapeA;
+ ev.shapeB = eq.shapeB;
+ ev.contactEquation = eq;
+ this.emit(ev);
+ }
+ }
+ }
+
+ // Sleeping update
+ if(this.enableBodySleeping){
+ for(i=0; i!==Nbodies; i++){
+ bodies[i].sleepTick(this.time);
+ }
+ }
+
+ this.emit(this.postStepEvent);
+};
+
+var ib_fhMinv = vec2.create();
+var ib_velodt = vec2.create();
+
+/**
+ * Move a body forward in time.
+ * @static
+ * @method integrateBody
+ * @param {Body} body
+ * @param {Number} dt
+ */
+World.integrateBody = function(body,dt){
+ var minv = body.invMass,
+ f = body.force,
+ pos = body.position,
+ velo = body.velocity;
+
+ // Angular step
+ if(!body.fixedRotation){
+ body.angularVelocity += body.angularForce * body.invInertia * dt;
+ body.angle += body.angularVelocity * dt;
+ }
+
+ // Linear step
+ vec2.scale(ib_fhMinv,f,dt*minv);
+ vec2.add(velo,ib_fhMinv,velo);
+ vec2.scale(ib_velodt,velo,dt);
+ vec2.add(pos,pos,ib_velodt);
+
+ body.aabbNeedsUpdate = true;
+};
+
+/**
+ * Runs narrowphase for the shape pair i and j.
+ * @method runNarrowphase
+ * @param {Narrowphase} np
+ * @param {Body} bi
+ * @param {Shape} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Shape} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @param {Number} mu
+ */
+World.prototype.runNarrowphase = function(np,bi,si,xi,ai,bj,sj,xj,aj,mu,restitution,surfaceVelocity){
+
+ if(!((si.collisionGroup & sj.collisionMask) !== 0 && (sj.collisionGroup & si.collisionMask) !== 0))
+ return;
+
+ var reducedMass = bi.invMass + bj.invMass;
+ if(reducedMass > 0)
+ reducedMass = 1/reducedMass;
+
+ // Get world position and angle of each shape
+ vec2.rotate(xiw, xi, bi.angle);
+ vec2.rotate(xjw, xj, bj.angle);
+ vec2.add(xiw, xiw, bi.position);
+ vec2.add(xjw, xjw, bj.position);
+ var aiw = ai + bi.angle;
+ var ajw = aj + bj.angle;
+
+ // Run narrowphase
+ np.enableFriction = mu > 0;
+ np.frictionCoefficient = mu;
+ np.restitution = restitution;
+ np.surfaceVelocity = surfaceVelocity;
+
+ var resolver = np[si.type | sj.type],
+ numContacts = 0;
+ if (resolver) {
+ var sensor = si.sensor || sj.sensor;
+ if (si.type < sj.type) {
+ numContacts = resolver.call(np, bi,si,xiw,aiw, bj,sj,xjw,ajw, sensor);
+ } else {
+ numContacts = resolver.call(np, bj,sj,xjw,ajw, bi,si,xiw,aiw, sensor);
+ }
+
+ if(numContacts){
+ var key = si.id < sj.id ? si.id+" "+ sj.id : sj.id+" "+ si.id;
+ if(!this.overlappingShapesLastState[key]){
+
+ // Report new shape overlap
+ var e = this.beginContactEvent;
+ e.shapeA = si;
+ e.shapeB = sj;
+ e.bodyA = bi;
+ e.bodyB = bj;
+
+ if(typeof(numContacts)=="number"){
+ // Add contacts to the event object
+ e.contactEquations.length = 0;
+ for(var i=np.contactEquations.length-numContacts; i=0; i--){
+ this.removeConstraint(cs[i]);
+ }
+
+ // Remove all bodies
+ var bodies = this.bodies;
+ for(var i=bodies.length-1; i>=0; i--){
+ this.removeBody(bodies[i]);
+ }
+
+ // Remove all springs
+ var springs = this.springs;
+ for(var i=springs.length-1; i>=0; i--){
+ this.removeSpring(springs[i]);
+ }
+
+ // Remove all contact materials
+ var cms = this.contactMaterials;
+ for(var i=cms.length-1; i>=0; i--){
+ this.removeContactMaterial(cms[i]);
+ }
+};
+
+/**
+ * Get a copy of this World instance
+ * @method clone
+ * @return {World}
+ */
+World.prototype.clone = function(){
+ var world = new World();
+ world.fromJSON(this.toJSON());
+ return world;
+};
+
+var hitTest_tmp1 = vec2.create(),
+ hitTest_zero = vec2.fromValues(0,0),
+ hitTest_tmp2 = vec2.fromValues(0,0);
+
+/**
+ * Test if a world point overlaps bodies
+ * @method hitTest
+ * @param {Array} worldPoint Point to use for intersection tests
+ * @param {Array} bodies A list of objects to check for intersection
+ * @param {Number} precision Used for matching against particles and lines. Adds some margin to these infinitesimal objects.
+ * @return {Array} Array of bodies that overlap the point
+ */
+World.prototype.hitTest = function(worldPoint,bodies,precision){
+ precision = precision || 0;
+
+ // Create a dummy particle body with a particle shape to test against the bodies
+ var pb = new Body({ position:worldPoint }),
+ ps = new Particle(),
+ px = worldPoint,
+ pa = 0,
+ x = hitTest_tmp1,
+ zero = hitTest_zero,
+ tmp = hitTest_tmp2;
+ pb.addShape(ps);
+
+ var n = this.narrowphase,
+ result = [];
+
+ // Check bodies
+ for(var i=0, N=bodies.length; i!==N; i++){
+ var b = bodies[i];
+ for(var j=0, NS=b.shapes.length; j!==NS; j++){
+ var s = b.shapes[j],
+ offset = b.shapeOffsets[j] || zero,
+ angle = b.shapeAngles[j] || 0.0;
+
+ // Get shape world position + angle
+ vec2.rotate(x, offset, b.angle);
+ vec2.add(x, x, b.position);
+ var a = angle + b.angle;
+
+ if( (s instanceof Circle && n.circleParticle (b,s,x,a, pb,ps,px,pa, true)) ||
+ (s instanceof Convex && n.particleConvex (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Plane && n.particlePlane (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Capsule && n.particleCapsule (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Particle && vec2.squaredLength(vec2.sub(tmp,x,worldPoint)) < precision*precision)
+ ){
+ result.push(b);
+ }
+ }
+ }
+
+ return result;
+};
+
+},{"../../package.json":8,"../collision/Broadphase":10,"../collision/NaiveBroadphase":12,"../collision/Narrowphase":13,"../constraints/DistanceConstraint":17,"../constraints/LockConstraint":19,"../constraints/PrismaticConstraint":20,"../constraints/RevoluteConstraint":21,"../events/EventEmitter":28,"../material/ContactMaterial":29,"../material/Material":30,"../math/vec2":33,"../objects/Body":34,"../objects/Spring":35,"../shapes/Capsule":37,"../shapes/Circle":38,"../shapes/Convex":39,"../shapes/Line":40,"../shapes/Particle":41,"../shapes/Plane":42,"../shapes/Rectangle":43,"../solver/GSSolver":45,"../utils/Utils":49}]},{},[36])
+(36)
+});
+;
\ No newline at end of file
diff --git a/build/phaser.d.ts b/build/phaser.d.ts
index ff10a516..ef600a3b 100644
--- a/build/phaser.d.ts
+++ b/build/phaser.d.ts
@@ -845,6 +845,7 @@ declare module Phaser {
visible: boolean;
renderable: boolean;
width: number;
+ height: number;
health: number;
damage(amount: number): Phaser.Sprite;
}
@@ -1645,9 +1646,9 @@ declare module Phaser {
tilemap(key: string, tilesetURL: string, mapDataURL?: string, mapData?: Object, format?: string): void;
tileset(key: string, url: string, tileWidth: number, tileHeight: number, tileMargin?: number, tileSpacing?: number, rows?: number, columns?: number, limit?: number): void;
bitmapFont(key: string, textureURL: string, xmlURL?: string, xmlData?: Object): void;
- atlasJSONArray(key: string, textureURL: string, atlasURL: string, atlasData: Object): void;
- atlasJSONHash(key: string, textureURL: string, atlasURL: string, atlasData: Object): void;
- atlasXML(key: string, textureURL: string, atlasURL: string, atlasData: Object): void;
+ atlasJSONArray(key: string, textureURL: string, atlasURL?: string, atlasData?: Object): void;
+ atlasJSONHash(key: string, textureURL: string, atlasURL?: string, atlasData?: Object): void;
+ atlasXML(key: string, textureURL: string, atlasURL?: string, atlasData?: Object): void;
atlas(key: string, textureURL: string, atlasURL?: string, atlasData?: Object, format?: number): void;
removeFile(key: string): void;
removeAll(): void;
@@ -2111,9 +2112,18 @@ declare module Phaser {
toString(): string;
}
- class TilemapRenderer {
- constructor(game: Phaser.Game);
- game: Phaser.Game;
- render(tilemap: Tilemap): void;
+ class Tileset {
+ constructor(name: string, firstgid: number, width: number, height: number, margin: number, spacing: number, properties: any);
+ name: string;
+ firstgid: number;
+ tileWidth: number;
+ tileHeight: number;
+ tileMargin: number;
+ tileSpacing: number;
+ properties: any;
+ image: any;
+ rows: number;
+ setSpacing(margin: number, spacing: number);
}
+
}
diff --git a/build/phaser.js b/build/phaser.js
index d065a936..b347c6dc 100644
--- a/build/phaser.js
+++ b/build/phaser.js
@@ -7,6 +7,10234 @@
root.Phaser = factory();
}
}(this, function() {
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013 p2.js authors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.p2=e():"undefined"!=typeof global?self.p2=e():"undefined"!=typeof self&&(self.p2=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
+ //TODO: evaluate use of glm_invsqrt here?
+ len = 1 / Math.sqrt(len);
+ out[0] = a[0] * len;
+ out[1] = a[1] * len;
+ }
+ return out;
+};
+
+/**
+ * Caclulates the dot product of two vec2's
+ *
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {Number} dot product of a and b
+ */
+vec2.dot = function (a, b) {
+ return a[0] * b[0] + a[1] * b[1];
+};
+
+/**
+ * Computes the cross product of two vec2's
+ * Note that the cross product must by definition produce a 3D vector
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @returns {vec3} out
+ */
+vec2.cross = function(out, a, b) {
+ var z = a[0] * b[1] - a[1] * b[0];
+ out[0] = out[1] = 0;
+ out[2] = z;
+ return out;
+};
+
+/**
+ * Performs a linear interpolation between two vec2's
+ *
+ * @param {vec3} out the receiving vector
+ * @param {vec2} a the first operand
+ * @param {vec2} b the second operand
+ * @param {Number} t interpolation amount between the two inputs
+ * @returns {vec2} out
+ */
+vec2.lerp = function (out, a, b, t) {
+ var ax = a[0],
+ ay = a[1];
+ out[0] = ax + t * (b[0] - ax);
+ out[1] = ay + t * (b[1] - ay);
+ return out;
+};
+
+/**
+ * Transforms the vec2 with a mat2
+ *
+ * @param {vec2} out the receiving vector
+ * @param {vec2} a the vector to transform
+ * @param {mat2} m matrix to transform with
+ * @returns {vec2} out
+ */
+vec2.transformMat2 = function(out, a, m) {
+ var x = a[0],
+ y = a[1];
+ out[0] = x * m[0] + y * m[1];
+ out[1] = x * m[2] + y * m[3];
+ return out;
+};
+
+/**
+ * Perform some operation over an array of vec2s.
+ *
+ * @param {Array} a the array of vectors to iterate over
+ * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed
+ * @param {Number} offset Number of elements to skip at the beginning of the array
+ * @param {Number} count Number of vec2s to iterate over. If 0 iterates over entire array
+ * @param {Function} fn Function to call for each vector in the array
+ * @param {Object} [arg] additional argument to pass to fn
+ * @returns {Array} a
+ * @function
+ */
+vec2.forEach = (function() {
+ var vec = new Float32Array(2);
+
+ return function(a, stride, offset, count, fn, arg) {
+ var i, l;
+ if(!stride) {
+ stride = 2;
+ }
+
+ if(!offset) {
+ offset = 0;
+ }
+
+ if(count) {
+ l = Math.min((count * stride) + offset, a.length);
+ } else {
+ l = a.length;
+ }
+
+ for(i = offset; i < l; i += stride) {
+ vec[0] = a[i]; vec[1] = a[i+1];
+ fn(vec, vec, arg);
+ a[i] = vec[0]; a[i+1] = vec[1];
+ }
+
+ return a;
+ };
+})();
+
+/**
+ * Returns a string representation of a vector
+ *
+ * @param {vec2} vec vector to represent as a string
+ * @returns {String} string representation of the vector
+ */
+vec2.str = function (a) {
+ return 'vec2(' + a[0] + ', ' + a[1] + ')';
+};
+
+if(typeof(exports) !== 'undefined') {
+ exports.vec2 = vec2;
+}
+
+},{}],3:[function(require,module,exports){
+var Scalar = require('./Scalar');
+
+module.exports = Line;
+
+/**
+ * Container for line-related functions
+ * @class Line
+ */
+function Line(){};
+
+/**
+ * Compute the intersection between two lines.
+ * @static
+ * @method lineInt
+ * @param {Array} l1 Line vector 1
+ * @param {Array} l2 Line vector 2
+ * @param {Number} precision Precision to use when checking if the lines are parallel
+ * @return {Array} The intersection point.
+ */
+Line.lineInt = function(l1,l2,precision){
+ precision = precision || 0;
+ var i = [0,0]; // point
+ var a1, b1, c1, a2, b2, c2, det; // scalars
+ a1 = l1[1][1] - l1[0][1];
+ b1 = l1[0][0] - l1[1][0];
+ c1 = a1 * l1[0][0] + b1 * l1[0][1];
+ a2 = l2[1][1] - l2[0][1];
+ b2 = l2[0][0] - l2[1][0];
+ c2 = a2 * l2[0][0] + b2 * l2[0][1];
+ det = a1 * b2 - a2*b1;
+ if (!Scalar.eq(det, 0, precision)) { // lines are not parallel
+ i[0] = (b2 * c1 - b1 * c2) / det;
+ i[1] = (a1 * c2 - a2 * c1) / det;
+ }
+ return i;
+};
+
+/**
+ * Checks if two line segments intersects.
+ * @method segmentsIntersect
+ * @param {Array} p1 The start vertex of the first line segment.
+ * @param {Array} p2 The end vertex of the first line segment.
+ * @param {Array} q1 The start vertex of the second line segment.
+ * @param {Array} q2 The end vertex of the second line segment.
+ * @return {Boolean} True if the two line segments intersect
+ */
+Line.segmentsIntersect = function(p1, p2, q1, q2){
+ var dx = p2[0] - p1[0];
+ var dy = p2[1] - p1[1];
+ var da = q2[0] - q1[0];
+ var db = q2[1] - q1[1];
+
+ // segments are parallel
+ if(da*dy - db*dx == 0)
+ return false;
+
+ var s = (dx * (q1[1] - p1[1]) + dy * (p1[0] - q1[0])) / (da * dy - db * dx)
+ var t = (da * (p1[1] - q1[1]) + db * (q1[0] - p1[0])) / (db * dx - da * dy)
+
+ return (s>=0 && s<=1 && t>=0 && t<=1);
+};
+
+
+},{"./Scalar":6}],4:[function(require,module,exports){
+module.exports = Point;
+
+/**
+ * Point related functions
+ * @class Point
+ */
+function Point(){};
+
+/**
+ * Get the area of a triangle spanned by the three given points. Note that the area will be negative if the points are not given in counter-clockwise order.
+ * @static
+ * @method area
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @return {Number}
+ */
+Point.area = function(a,b,c){
+ return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1])));
+};
+
+Point.left = function(a,b,c){
+ return Point.area(a,b,c) > 0;
+};
+
+Point.leftOn = function(a,b,c) {
+ return Point.area(a, b, c) >= 0;
+};
+
+Point.right = function(a,b,c) {
+ return Point.area(a, b, c) < 0;
+};
+
+Point.rightOn = function(a,b,c) {
+ return Point.area(a, b, c) <= 0;
+};
+
+var tmpPoint1 = [],
+ tmpPoint2 = [];
+
+/**
+ * Check if three points are collinear
+ * @method collinear
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @param {Number} [thresholdAngle=0] Threshold angle to use when comparing the vectors. The function will return true if the angle between the resulting vectors is less than this value. Use zero for max precision.
+ * @return {Boolean}
+ */
+Point.collinear = function(a,b,c,thresholdAngle) {
+ if(!thresholdAngle)
+ return Point.area(a, b, c) == 0;
+ else {
+ var ab = tmpPoint1,
+ bc = tmpPoint2;
+
+ ab[0] = b[0]-a[0];
+ ab[1] = b[1]-a[1];
+ bc[0] = c[0]-b[0];
+ bc[1] = c[1]-b[1];
+
+ var dot = ab[0]*bc[0] + ab[1]*bc[1],
+ magA = Math.sqrt(ab[0]*ab[0] + ab[1]*ab[1]),
+ magB = Math.sqrt(bc[0]*bc[0] + bc[1]*bc[1]),
+ angle = Math.acos(dot/(magA*magB));
+ return angle < thresholdAngle;
+ }
+};
+
+Point.sqdist = function(a,b){
+ var dx = b[0] - a[0];
+ var dy = b[1] - a[1];
+ return dx * dx + dy * dy;
+};
+
+},{}],5:[function(require,module,exports){
+var Line = require("./Line")
+, Point = require("./Point")
+, Scalar = require("./Scalar")
+
+module.exports = Polygon;
+
+/**
+ * Polygon class.
+ * @class Polygon
+ * @constructor
+ */
+function Polygon(){
+
+ /**
+ * Vertices that this polygon consists of. An array of array of numbers, example: [[0,0],[1,0],..]
+ * @property vertices
+ * @type {Array}
+ */
+ this.vertices = [];
+}
+
+/**
+ * Get a vertex at position i. It does not matter if i is out of bounds, this function will just cycle.
+ * @method at
+ * @param {Number} i
+ * @return {Array}
+ */
+Polygon.prototype.at = function(i){
+ var v = this.vertices,
+ s = v.length;
+ return v[i < 0 ? i % s + s : i % s];
+};
+
+/**
+ * Get first vertex
+ * @method first
+ * @return {Array}
+ */
+Polygon.prototype.first = function(){
+ return this.vertices[0];
+};
+
+/**
+ * Get last vertex
+ * @method last
+ * @return {Array}
+ */
+Polygon.prototype.last = function(){
+ return this.vertices[this.vertices.length-1];
+};
+
+/**
+ * Clear the polygon data
+ * @method clear
+ * @return {Array}
+ */
+Polygon.prototype.clear = function(){
+ this.vertices.length = 0;
+};
+
+/**
+ * Append points "from" to "to"-1 from an other polygon "poly" onto this one.
+ * @method append
+ * @param {Polygon} poly The polygon to get points from.
+ * @param {Number} from The vertex index in "poly".
+ * @param {Number} to The end vertex index in "poly". Note that this vertex is NOT included when appending.
+ * @return {Array}
+ */
+Polygon.prototype.append = function(poly,from,to){
+ if(typeof(from) == "undefined") throw new Error("From is not given!");
+ if(typeof(to) == "undefined") throw new Error("To is not given!");
+
+ if(to-1 < from) throw new Error("lol1");
+ if(to > poly.vertices.length) throw new Error("lol2");
+ if(from < 0) throw new Error("lol3");
+
+ for(var i=from; i v[br][0])) {
+ br = i;
+ }
+ }
+
+ // reverse poly if clockwise
+ if (!Point.left(this.at(br - 1), this.at(br), this.at(br + 1))) {
+ this.reverse();
+ }
+};
+
+/**
+ * Reverse the vertices in the polygon
+ * @method reverse
+ */
+Polygon.prototype.reverse = function(){
+ var tmp = [];
+ for(var i=0, N=this.vertices.length; i!==N; i++){
+ tmp.push(this.vertices.pop());
+ }
+ this.vertices = tmp;
+};
+
+/**
+ * Check if a point in the polygon is a reflex point
+ * @method isReflex
+ * @param {Number} i
+ * @return {Boolean}
+ */
+Polygon.prototype.isReflex = function(i){
+ return Point.right(this.at(i - 1), this.at(i), this.at(i + 1));
+};
+
+var tmpLine1=[],
+ tmpLine2=[];
+
+/**
+ * Check if two vertices in the polygon can see each other
+ * @method canSee
+ * @param {Number} a Vertex index 1
+ * @param {Number} b Vertex index 2
+ * @return {Boolean}
+ */
+Polygon.prototype.canSee = function(a,b) {
+ var p, dist, l1=tmpLine1, l2=tmpLine2;
+
+ if (Point.leftOn(this.at(a + 1), this.at(a), this.at(b)) && Point.rightOn(this.at(a - 1), this.at(a), this.at(b))) {
+ return false;
+ }
+ dist = Point.sqdist(this.at(a), this.at(b));
+ for (var i = 0; i !== this.vertices.length; ++i) { // for each edge
+ if ((i + 1) % this.vertices.length === a || i === a) // ignore incident edges
+ continue;
+ if (Point.leftOn(this.at(a), this.at(b), this.at(i + 1)) && Point.rightOn(this.at(a), this.at(b), this.at(i))) { // if diag intersects an edge
+ l1[0] = this.at(a);
+ l1[1] = this.at(b);
+ l2[0] = this.at(i);
+ l2[1] = this.at(i + 1);
+ p = Line.lineInt(l1,l2);
+ if (Point.sqdist(this.at(a), p) < dist) { // if edge is blocking visibility to b
+ return false;
+ }
+ }
+ }
+
+ return true;
+};
+
+/**
+ * Copy the polygon from vertex i to vertex j.
+ * @method copy
+ * @param {Number} i
+ * @param {Number} j
+ * @param {Polygon} [targetPoly] Optional target polygon to save in.
+ * @return {Polygon} The resulting copy.
+ */
+Polygon.prototype.copy = function(i,j,targetPoly){
+ var p = targetPoly || new Polygon();
+ p.clear();
+ if (i < j) {
+ // Insert all vertices from i to j
+ for(var k=i; k<=j; k++)
+ p.vertices.push(this.vertices[k]);
+
+ } else {
+
+ // Insert vertices 0 to j
+ for(var k=0; k<=j; k++)
+ p.vertices.push(this.vertices[k]);
+
+ // Insert vertices i to end
+ for(var k=i; k 0)
+ return this.slice(edges);
+ else
+ return [this];
+};
+
+/**
+ * Slices the polygon given one or more cut edges. If given one, this function will return two polygons (false on failure). If many, an array of polygons.
+ * @method slice
+ * @param {Array} cutEdges A list of edges, as returned by .getCutEdges()
+ * @return {Array}
+ */
+Polygon.prototype.slice = function(cutEdges){
+ if(cutEdges.length == 0) return [this];
+ if(cutEdges instanceof Array && cutEdges.length && cutEdges[0] instanceof Array && cutEdges[0].length==2 && cutEdges[0][0] instanceof Array){
+
+ var polys = [this];
+
+ for(var i=0; i maxlevel){
+ console.warn("quickDecomp: max level ("+maxlevel+") reached.");
+ return result;
+ }
+
+ for (var i = 0; i < this.vertices.length; ++i) {
+ if (poly.isReflex(i)) {
+ reflexVertices.push(poly.vertices[i]);
+ upperDist = lowerDist = Number.MAX_VALUE;
+
+
+ for (var j = 0; j < this.vertices.length; ++j) {
+ if (Point.left(poly.at(i - 1), poly.at(i), poly.at(j))
+ && Point.rightOn(poly.at(i - 1), poly.at(i), poly.at(j - 1))) { // if line intersects with an edge
+ p = getIntersectionPoint(poly.at(i - 1), poly.at(i), poly.at(j), poly.at(j - 1)); // find the point of intersection
+ if (Point.right(poly.at(i + 1), poly.at(i), p)) { // make sure it's inside the poly
+ d = Point.sqdist(poly.vertices[i], p);
+ if (d < lowerDist) { // keep only the closest intersection
+ lowerDist = d;
+ lowerInt = p;
+ lowerIndex = j;
+ }
+ }
+ }
+ if (Point.left(poly.at(i + 1), poly.at(i), poly.at(j + 1))
+ && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) {
+ p = getIntersectionPoint(poly.at(i + 1), poly.at(i), poly.at(j), poly.at(j + 1));
+ if (Point.left(poly.at(i - 1), poly.at(i), p)) {
+ d = Point.sqdist(poly.vertices[i], p);
+ if (d < upperDist) {
+ upperDist = d;
+ upperInt = p;
+ upperIndex = j;
+ }
+ }
+ }
+ }
+
+ // if there are no vertices to connect to, choose a point in the middle
+ if (lowerIndex == (upperIndex + 1) % this.vertices.length) {
+ //console.log("Case 1: Vertex("+i+"), lowerIndex("+lowerIndex+"), upperIndex("+upperIndex+"), poly.size("+this.vertices.length+")");
+ p[0] = (lowerInt[0] + upperInt[0]) / 2;
+ p[1] = (lowerInt[1] + upperInt[1]) / 2;
+ steinerPoints.push(p);
+
+ if (i < upperIndex) {
+ //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.begin() + upperIndex + 1);
+ lowerPoly.append(poly, i, upperIndex+1);
+ lowerPoly.vertices.push(p);
+ upperPoly.vertices.push(p);
+ if (lowerIndex != 0){
+ //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.end());
+ upperPoly.append(poly,lowerIndex,poly.vertices.length);
+ }
+ //upperPoly.insert(upperPoly.end(), poly.begin(), poly.begin() + i + 1);
+ upperPoly.append(poly,0,i+1);
+ } else {
+ if (i != 0){
+ //lowerPoly.insert(lowerPoly.end(), poly.begin() + i, poly.end());
+ lowerPoly.append(poly,i,poly.vertices.length);
+ }
+ //lowerPoly.insert(lowerPoly.end(), poly.begin(), poly.begin() + upperIndex + 1);
+ lowerPoly.append(poly,0,upperIndex+1);
+ lowerPoly.vertices.push(p);
+ upperPoly.vertices.push(p);
+ //upperPoly.insert(upperPoly.end(), poly.begin() + lowerIndex, poly.begin() + i + 1);
+ upperPoly.append(poly,lowerIndex,i+1);
+ }
+ } else {
+ // connect to the closest point within the triangle
+ //console.log("Case 2: Vertex("+i+"), closestIndex("+closestIndex+"), poly.size("+this.vertices.length+")\n");
+
+ if (lowerIndex > upperIndex) {
+ upperIndex += this.vertices.length;
+ }
+ closestDist = Number.MAX_VALUE;
+
+ if(upperIndex < lowerIndex){
+ return result;
+ }
+
+ for (var j = lowerIndex; j <= upperIndex; ++j) {
+ if (Point.leftOn(poly.at(i - 1), poly.at(i), poly.at(j))
+ && Point.rightOn(poly.at(i + 1), poly.at(i), poly.at(j))) {
+ d = Point.sqdist(poly.at(i), poly.at(j));
+ if (d < closestDist) {
+ closestDist = d;
+ closestIndex = j % this.vertices.length;
+ }
+ }
+ }
+
+ if (i < closestIndex) {
+ lowerPoly.append(poly,i,closestIndex+1);
+ if (closestIndex != 0){
+ upperPoly.append(poly,closestIndex,v.length);
+ }
+ upperPoly.append(poly,0,i+1);
+ } else {
+ if (i != 0){
+ lowerPoly.append(poly,i,v.length);
+ }
+ lowerPoly.append(poly,0,closestIndex+1);
+ upperPoly.append(poly,closestIndex,i+1);
+ }
+ }
+
+ // solve smallest poly first
+ if (lowerPoly.vertices.length < upperPoly.vertices.length) {
+ lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ } else {
+ upperPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ lowerPoly.quickDecomp(result,reflexVertices,steinerPoints,delta,maxlevel,level);
+ }
+
+ return result;
+ }
+ }
+ result.push(this);
+
+ return result;
+};
+
+/**
+ * Remove collinear points in the polygon.
+ * @method removeCollinearPoints
+ * @param {Number} [precision] The threshold angle to use when determining whether two edges are collinear. Use zero for finest precision.
+ * @return {Number} The number of points removed
+ */
+Polygon.prototype.removeCollinearPoints = function(precision){
+ var num = 0;
+ for(var i=this.vertices.length-1; this.vertices.length>3 && i>=0; --i){
+ if(Point.collinear(this.at(i-1),this.at(i),this.at(i+1),precision)){
+ // Remove the middle point
+ this.vertices.splice(i%this.vertices.length,1);
+ i--; // Jump one point forward. Otherwise we may get a chain removal
+ num++;
+ }
+ }
+ return num;
+};
+
+},{"./Line":3,"./Point":4,"./Scalar":6}],6:[function(require,module,exports){
+module.exports = Scalar;
+
+/**
+ * Scalar functions
+ * @class Scalar
+ */
+function Scalar(){}
+
+/**
+ * Check if two scalars are equal
+ * @static
+ * @method eq
+ * @param {Number} a
+ * @param {Number} b
+ * @param {Number} [precision]
+ * @return {Boolean}
+ */
+Scalar.eq = function(a,b,precision){
+ precision = precision || 0;
+ return Math.abs(a-b) < precision;
+};
+
+},{}],7:[function(require,module,exports){
+module.exports = {
+ Polygon : require("./Polygon"),
+ Point : require("./Point"),
+};
+
+},{"./Point":4,"./Polygon":5}],8:[function(require,module,exports){
+module.exports={
+ "name": "p2",
+ "version": "0.4.0",
+ "description": "A JavaScript 2D physics engine.",
+ "author": "Stefan Hedman (http://steffe.se)",
+ "keywords": [
+ "p2.js",
+ "p2",
+ "physics",
+ "engine",
+ "2d"
+ ],
+ "main": "./src/p2.js",
+ "engines": {
+ "node": "*"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/schteppe/p2.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/schteppe/p2.js/issues"
+ },
+ "licenses" : [
+ {
+ "type" : "MIT"
+ }
+ ],
+ "devDependencies" : {
+ "jshint" : "latest",
+ "nodeunit" : "latest",
+ "grunt": "~0.4.0",
+ "grunt-contrib-jshint": "~0.1.1",
+ "grunt-contrib-nodeunit": "~0.1.2",
+ "grunt-contrib-concat": "~0.1.3",
+ "grunt-contrib-uglify": "*",
+ "grunt-browserify" : "*",
+ "browserify":"*"
+ },
+ "dependencies" : {
+ "underscore":"*",
+ "poly-decomp" : "git://github.com/schteppe/poly-decomp.js",
+ "gl-matrix":"2.0.0",
+ "jsonschema":"*"
+ }
+}
+
+},{}],9:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, Utils = require('../utils/Utils')
+
+module.exports = AABB;
+
+/**
+ * Axis aligned bounding box class.
+ * @class AABB
+ * @constructor
+ * @param {Object} options
+ * @param {Array} upperBound
+ * @param {Array} lowerBound
+ */
+function AABB(options){
+
+ /**
+ * The lower bound of the bounding box.
+ * @property lowerBound
+ * @type {Array}
+ */
+ this.lowerBound = vec2.create();
+ if(options && options.lowerBound) vec2.copy(this.lowerBound, options.lowerBound);
+
+ /**
+ * The upper bound of the bounding box.
+ * @property upperBound
+ * @type {Array}
+ */
+ this.upperBound = vec2.create();
+ if(options && options.upperBound) vec2.copy(this.upperBound, options.upperBound);
+}
+
+var tmp = vec2.create();
+
+/**
+ * Set the AABB bounds from a set of points.
+ * @method setFromPoints
+ * @param {Array} points An array of vec2's.
+ */
+AABB.prototype.setFromPoints = function(points,position,angle){
+ var l = this.lowerBound,
+ u = this.upperBound;
+ vec2.set(l, Number.MAX_VALUE, Number.MAX_VALUE);
+ vec2.set(u, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ for(var i=0; i u[j]){
+ u[j] = p[j];
+ }
+ if(p[j] < l[j]){
+ l[j] = p[j];
+ }
+ }
+ }
+
+ // Add offset
+ if(position){
+ vec2.add(this.lowerBound, this.lowerBound, position);
+ vec2.add(this.upperBound, this.upperBound, position);
+ }
+};
+
+/**
+ * Copy bounds from an AABB to this AABB
+ * @method copy
+ * @param {AABB} aabb
+ */
+AABB.prototype.copy = function(aabb){
+ vec2.copy(this.lowerBound, aabb.lowerBound);
+ vec2.copy(this.upperBound, aabb.upperBound);
+};
+
+/**
+ * Extend this AABB so that it covers the given AABB too.
+ * @method extend
+ * @param {AABB} aabb
+ */
+AABB.prototype.extend = function(aabb){
+ // Loop over x and y
+ for(var i=0; i<2; i++){
+ // Extend lower bound
+ if(aabb.lowerBound[i] < this.lowerBound[i])
+ this.lowerBound[i] = aabb.lowerBound[i];
+
+ // Upper
+ if(aabb.upperBound[i] > this.upperBound[i])
+ this.upperBound[i] = aabb.upperBound[i];
+ }
+};
+
+/**
+ * Returns true if the given AABB overlaps this AABB.
+ * @method overlaps
+ * @param {AABB} aabb
+ * @return {Boolean}
+ */
+AABB.prototype.overlaps = function(aabb){
+ var l1 = this.lowerBound,
+ u1 = this.upperBound,
+ l2 = aabb.lowerBound,
+ u2 = aabb.upperBound;
+
+ // l2 u2
+ // |---------|
+ // |--------|
+ // l1 u1
+
+ return ((l2[0] <= u1[0] && u1[0] <= u2[0]) || (l1[0] <= u2[0] && u2[0] <= u1[0])) &&
+ ((l2[1] <= u1[1] && u1[1] <= u2[1]) || (l1[1] <= u2[1] && u2[1] <= u1[1]));
+};
+
+},{"../math/vec2":33,"../utils/Utils":49}],10:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+var Body = require('../objects/Body')
+
+module.exports = Broadphase;
+
+/**
+ * Base class for broadphase implementations.
+ * @class Broadphase
+ * @constructor
+ */
+function Broadphase(){
+
+ /**
+ * The resulting overlapping pairs. Will be filled with results during .getCollisionPairs().
+ * @property result
+ * @type {Array}
+ */
+ this.result = [];
+
+ /**
+ * The world to search for collision pairs in. To change it, use .setWorld()
+ * @property world
+ * @type {World}
+ */
+ this.world = null;
+};
+
+/**
+ * Set the world that we are searching for collision pairs in
+ * @method setWorld
+ * @param {World} world
+ */
+Broadphase.prototype.setWorld = function(world){
+ this.world = world;
+};
+
+/**
+ * Get all potential intersecting body pairs.
+ * @method getCollisionPairs
+ * @param {World} world The world to search in.
+ * @return {Array} An array of the bodies, ordered in pairs. Example: A result of [a,b,c,d] means that the potential pairs are: (a,b), (c,d).
+ */
+Broadphase.prototype.getCollisionPairs = function(world){
+ throw new Error("getCollisionPairs must be implemented in a subclass!");
+};
+
+var dist = vec2.create();
+
+/**
+ * Check whether the bounding radius of two bodies overlap.
+ * @method boundingRadiusCheck
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {Boolean}
+ */
+Broadphase.boundingRadiusCheck = function(bodyA, bodyB){
+ vec2.sub(dist, bodyA.position, bodyB.position);
+ var d2 = vec2.squaredLength(dist),
+ r = bodyA.boundingRadius + bodyB.boundingRadius;
+ return d2 <= r*r;
+};
+
+/**
+ * Check whether the bounding radius of two bodies overlap.
+ * @method boundingRadiusCheck
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {Boolean}
+ */
+Broadphase.aabbCheck = function(bodyA, bodyB){
+ if(bodyA.aabbNeedsUpdate) bodyA.updateAABB();
+ if(bodyB.aabbNeedsUpdate) bodyB.updateAABB();
+ return bodyA.aabb.overlaps(bodyB.aabb);
+};
+
+/**
+ * Check whether two bodies are allowed to collide at all.
+ * @method canCollide
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {Boolean}
+ */
+Broadphase.canCollide = function(bodyA, bodyB){
+ // Cannot collide static bodies
+ if(bodyA.motionState & Body.STATIC && bodyB.motionState & Body.STATIC)
+ return false;
+
+ // Cannot collide sleeping bodies
+ if(bodyA.sleepState & Body.SLEEPING && bodyB.sleepState & Body.SLEEPING)
+ return false;
+
+ return true;
+};
+
+},{"../math/vec2":33,"../objects/Body":34}],11:[function(require,module,exports){
+var Circle = require('../shapes/Circle')
+, Plane = require('../shapes/Plane')
+, Particle = require('../shapes/Particle')
+, Broadphase = require('../collision/Broadphase')
+, vec2 = require('../math/vec2')
+
+module.exports = GridBroadphase;
+
+/**
+ * Broadphase that uses axis-aligned bins.
+ * @class GridBroadphase
+ * @constructor
+ * @extends Broadphase
+ * @param {number} xmin Lower x bound of the grid
+ * @param {number} xmax Upper x bound
+ * @param {number} ymin Lower y bound
+ * @param {number} ymax Upper y bound
+ * @param {number} nx Number of bins along x axis
+ * @param {number} ny Number of bins along y axis
+ * @todo test
+ */
+function GridBroadphase(xmin,xmax,ymin,ymax,nx,ny){
+ Broadphase.apply(this);
+
+ nx = nx || 10;
+ ny = ny || 10;
+
+ this.binsizeX = (xmax-xmin) / nx;
+ this.binsizeY = (ymax-ymin) / ny;
+ this.nx = nx;
+ this.ny = ny;
+ this.xmin = xmin;
+ this.ymin = ymin;
+ this.xmax = xmax;
+ this.ymax = ymax;
+};
+GridBroadphase.prototype = new Broadphase();
+
+/**
+ * Get a bin index given a world coordinate
+ * @method getBinIndex
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Number} Integer index
+ */
+GridBroadphase.prototype.getBinIndex = function(x,y){
+ var nx = this.nx,
+ ny = this.ny,
+ xmin = this.xmin,
+ ymin = this.ymin,
+ xmax = this.xmax,
+ ymax = this.ymax;
+
+ var xi = Math.floor(nx * (x - xmin) / (xmax-xmin));
+ var yi = Math.floor(ny * (y - ymin) / (ymax-ymin));
+ return xi*ny + yi;
+}
+
+/**
+ * Get collision pairs.
+ * @method getCollisionPairs
+ * @param {World} world
+ * @return {Array}
+ */
+GridBroadphase.prototype.getCollisionPairs = function(world){
+ var result = [],
+ collidingBodies = world.bodies,
+ Ncolliding = Ncolliding=collidingBodies.length,
+ binsizeX = this.binsizeX,
+ binsizeY = this.binsizeY;
+
+ var bins=[], Nbins=nx*ny;
+ for(var i=0; i= 0 && xi*(ny-1) + yi < Nbins)
+ bins[ xi*(ny-1) + yi ].push(bi);
+ }
+ }
+ } else if(si instanceof Plane){
+ // Put in all bins for now
+ if(bi.angle == 0){
+ var y = bi.position[1];
+ for(var j=0; j!==Nbins && ymin+binsizeY*(j-1) id2){
+ var tmp = id1;
+ id1 = id2;
+ id2 = tmp;
+ }
+ return !!this.collidingBodiesLastStep[id1 + " " + id2];
+};
+
+// "for in" loops aren't optimised in chrome... is there a better way to handle last-step collision memory?
+// Maybe do this: http://jsperf.com/reflection-vs-array-of-keys
+function clearObject(obj){
+ for(var i = 0, l = obj.keys.length; i < l; i++) {
+ delete obj[obj.keys[i]];
+ }
+ obj.keys.length = 0;
+ /*
+ for(var key in this.collidingBodiesLastStep)
+ delete this.collidingBodiesLastStep[key];
+ */
+}
+
+/**
+ * Throws away the old equations and gets ready to create new
+ * @method reset
+ * @param {World} world
+ */
+Narrowphase.prototype.reset = function(world){
+
+ // Save the colliding bodies data
+ clearObject(this.collidingBodiesLastStep);
+ for(var i=0; i!==this.contactEquations.length; i++){
+ var eq = this.contactEquations[i],
+ id1 = eq.bi.id,
+ id2 = eq.bj.id;
+ if(id1 > id2){
+ var tmp = id1;
+ id1 = id2;
+ id2 = tmp;
+ }
+ var key = id1 + " " + id2;
+ if(!this.collidingBodiesLastStep[key]){
+ this.collidingBodiesLastStep[key] = true;
+ this.collidingBodiesLastStep.keys.push(key);
+ }
+ }
+
+ if(this.reuseObjects){
+ var ce = this.contactEquations,
+ fe = this.frictionEquations,
+ rfe = this.reusableFrictionEquations,
+ rce = this.reusableContactEquations;
+ Utils.appendArray(rce,ce);
+ Utils.appendArray(rfe,fe);
+ }
+
+ // Reset
+ this.contactEquations.length = this.frictionEquations.length = 0;
+};
+
+/**
+ * Creates a ContactEquation, either by reusing an existing object or creating a new one.
+ * @method createContactEquation
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {ContactEquation}
+ */
+Narrowphase.prototype.createContactEquation = function(bodyA,bodyB,shapeA,shapeB){
+ var c = this.reusableContactEquations.length ? this.reusableContactEquations.pop() : new ContactEquation(bodyA,bodyB);
+ c.bi = bodyA;
+ c.bj = bodyB;
+ c.shapeA = shapeA;
+ c.shapeB = shapeB;
+ c.restitution = this.restitution;
+ c.firstImpact = !this.collidedLastStep(bodyA,bodyB);
+ c.enabled = true;
+
+ if(bodyA.allowSleep && (bodyA.motionState & Body.DYNAMIC) && !(bodyB.motionState & Body.STATIC || bodyB.sleepState === Body.SLEEPY))
+ bodyA.wakeUp();
+ if(bodyB.allowSleep && (bodyB.motionState & Body.DYNAMIC) && !(bodyA.motionState & Body.STATIC || bodyA.sleepState === Body.SLEEPY))
+ bodyB.wakeUp();
+
+ return c;
+};
+
+/**
+ * Creates a FrictionEquation, either by reusing an existing object or creating a new one.
+ * @method createFrictionEquation
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @return {FrictionEquation}
+ */
+Narrowphase.prototype.createFrictionEquation = function(bodyA,bodyB,shapeA,shapeB){
+ var c = this.reusableFrictionEquations.length ? this.reusableFrictionEquations.pop() : new FrictionEquation(bodyA,bodyB);
+ c.bi = bodyA;
+ c.bj = bodyB;
+ c.shapeA = shapeA;
+ c.shapeB = shapeB;
+ c.setSlipForce(this.slipForce);
+ c.frictionCoefficient = this.frictionCoefficient;
+ c.relativeVelocity = this.surfaceVelocity;
+ c.enabled = true;
+ return c;
+};
+
+/**
+ * Creates a FrictionEquation given the data in the ContactEquation. Uses same offset vectors ri and rj, but the tangent vector will be constructed from the collision normal.
+ * @method createFrictionFromContact
+ * @param {ContactEquation} contactEquation
+ * @return {FrictionEquation}
+ */
+Narrowphase.prototype.createFrictionFromContact = function(c){
+ var eq = this.createFrictionEquation(c.bi,c.bj,c.shapeA,c.shapeB);
+ vec2.copy(eq.ri, c.ri);
+ vec2.copy(eq.rj, c.rj);
+ vec2.rotate(eq.t, c.ni, -Math.PI / 2);
+ eq.contactEquation = c;
+ return eq;
+}
+
+/**
+ * Convex/line narrowphase
+ * @method convexLine
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Line} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.LINE | Shape.CONVEX] =
+Narrowphase.prototype.convexLine = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Line/rectangle narrowphase
+ * @method lineRectangle
+ * @param {Body} bi
+ * @param {Line} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Rectangle} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.LINE | Shape.RECTANGLE] =
+Narrowphase.prototype.lineRectangle = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Rectangle/capsule narrowphase
+ * @method rectangleCapsule
+ * @param {Body} bi
+ * @param {Rectangle} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.RECTANGLE] =
+Narrowphase.prototype.rectangleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Convex/capsule narrowphase
+ * @method convexCapsule
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.CONVEX] =
+Narrowphase.prototype.convexCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Capsule/line narrowphase
+ * @method lineCapsule
+ * @param {Body} bi
+ * @param {Line} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.LINE] =
+Narrowphase.prototype.lineCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Capsule/capsule narrowphase
+ * @method capsuleCapsule
+ * @param {Body} bi
+ * @param {Capsule} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Capsule} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.CAPSULE | Shape.CAPSULE] =
+Narrowphase.prototype.capsuleCapsule = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Line/line narrowphase
+ * @method lineLine
+ * @param {Body} bi
+ * @param {Line} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Line} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @todo Implement me!
+ */
+Narrowphase.prototype[Shape.LINE | Shape.LINE] =
+Narrowphase.prototype.lineLine = function(bi,si,xi,ai, bj,sj,xj,aj, justTest){
+ // TODO
+ if(justTest)
+ return false;
+ else
+ return 0;
+};
+
+/**
+ * Plane/line Narrowphase
+ * @method planeLine
+ * @param {Body} planeBody
+ * @param {Plane} planeShape
+ * @param {Array} planeOffset
+ * @param {Number} planeAngle
+ * @param {Body} lineBody
+ * @param {Line} lineShape
+ * @param {Array} lineOffset
+ * @param {Number} lineAngle
+ */
+Narrowphase.prototype[Shape.PLANE | Shape.LINE] =
+Narrowphase.prototype.planeLine = function(planeBody, planeShape, planeOffset, planeAngle,
+ lineBody, lineShape, lineOffset, lineAngle, justTest){
+ var worldVertex0 = tmp1,
+ worldVertex1 = tmp2,
+ worldVertex01 = tmp3,
+ worldVertex11 = tmp4,
+ worldEdge = tmp5,
+ worldEdgeUnit = tmp6,
+ dist = tmp7,
+ worldNormal = tmp8,
+ worldTangent = tmp9,
+ verts = tmpArray
+ numContacts = 0;
+
+ // Get start and end points
+ vec2.set(worldVertex0, -lineShape.length/2, 0);
+ vec2.set(worldVertex1, lineShape.length/2, 0);
+
+ // Not sure why we have to use worldVertex*1 here, but it won't work otherwise. Tired.
+ vec2.rotate(worldVertex01, worldVertex0, lineAngle);
+ vec2.rotate(worldVertex11, worldVertex1, lineAngle);
+
+ add(worldVertex01, worldVertex01, lineOffset);
+ add(worldVertex11, worldVertex11, lineOffset);
+
+ vec2.copy(worldVertex0,worldVertex01);
+ vec2.copy(worldVertex1,worldVertex11);
+
+ // Get vector along the line
+ sub(worldEdge, worldVertex1, worldVertex0);
+ vec2.normalize(worldEdgeUnit, worldEdge);
+
+ // Get tangent to the edge.
+ vec2.rotate(worldTangent, worldEdgeUnit, -Math.PI/2);
+
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ // Check line ends
+ verts[0] = worldVertex0;
+ verts[1] = worldVertex1;
+ for(var i=0; i pos0 && pos < pos1){
+ // We got contact!
+
+ if(justTest) return true;
+
+ var c = this.createContactEquation(circleBody,lineBody,si,sj);
+
+ vec2.scale(c.ni, orthoDist, -1);
+ vec2.normalize(c.ni, c.ni);
+
+ vec2.scale( c.ri, c.ni, circleRadius);
+ add(c.ri, c.ri, circleOffset);
+ sub(c.ri, c.ri, circleBody.position);
+
+ sub(c.rj, projectedPoint, lineOffset);
+ add(c.rj, c.rj, lineOffset);
+ sub(c.rj, c.rj, lineBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+
+ return 1;
+ }
+ }
+
+ // Add corner
+ // @todo reuse array object
+ verts[0] = worldVertex0;
+ verts[1] = worldVertex1;
+
+ for(var i=0; i 0){
+
+ // Now project the circle onto the edge
+ vec2.scale(orthoDist, worldTangent, d);
+ sub(projectedPoint, circleOffset, orthoDist);
+
+
+ // Check if the point is within the edge span
+ var pos = dot(worldEdgeUnit, projectedPoint);
+ var pos0 = dot(worldEdgeUnit, worldVertex0);
+ var pos1 = dot(worldEdgeUnit, worldVertex1);
+
+ if(pos > pos0 && pos < pos1){
+ // We got contact!
+
+ if(justTest) return true;
+
+ if(closestEdgeDistance === null || d*d 0){
+ for(var i=0; i= 0){
+
+ // Now project the particle onto the edge
+ vec2.scale(orthoDist, worldTangent, d);
+ sub(projectedPoint, particleOffset, orthoDist);
+
+ // Check if the point is within the edge span
+ var pos = dot(worldEdgeUnit, projectedPoint);
+ var pos0 = dot(worldEdgeUnit, worldVertex0);
+ var pos1 = dot(worldEdgeUnit, worldVertex1);
+
+ if(pos > pos0 && pos < pos1){
+ // We got contact!
+ if(justTest) return true;
+
+ if(closestEdgeDistance === null || d*d r*r)
+ return 0;
+
+ if(justTest) return true;
+
+ var c = this.createContactEquation(bodyA,bodyB,si,sj);
+ sub(c.ni, offsetB, offsetA);
+ vec2.normalize(c.ni,c.ni);
+
+ vec2.scale( c.ri, c.ni, shapeA.radius);
+ vec2.scale( c.rj, c.ni, -shapeB.radius);
+
+ add(c.ri, c.ri, offsetA);
+ sub(c.ri, c.ri, bodyA.position);
+
+ add(c.rj, c.rj, offsetB);
+ sub(c.rj, c.rj, bodyB.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+ return 1;
+};
+
+/**
+ * Plane/Convex Narrowphase
+ * @method planeConvex
+ * @param {Body} bi
+ * @param {Plane} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Convex} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Narrowphase.prototype[Shape.PLANE | Shape.CONVEX] =
+Narrowphase.prototype.planeConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var convexBody = bj,
+ convexOffset = xj,
+ convexShape = sj,
+ convexAngle = aj,
+ planeBody = bi,
+ planeShape = si,
+ planeOffset = xi,
+ planeAngle = ai;
+
+ var worldVertex = tmp1,
+ worldNormal = tmp2,
+ dist = tmp3;
+
+ var numReported = 0;
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ for(var i=0; i= 2)
+ break;
+ }
+ }
+
+ return numReported;
+};
+
+/**
+ * @method convexPlane
+ * @deprecated Use .planeConvex() instead!
+ */
+Narrowphase.prototype.convexPlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ console.warn("Narrowphase.prototype.convexPlane is deprecated. Use planeConvex instead!");
+ return this.planeConvex( bj,sj,xj,aj, bi,si,xi,ai, justTest );
+}
+
+/**
+ * Narrowphase for particle vs plane
+ * @method particlePlane
+ * @param {Body} bi The particle body
+ * @param {Particle} si Particle shape
+ * @param {Array} xi World position for the particle
+ * @param {Number} ai World angle for the particle
+ * @param {Body} bj Plane body
+ * @param {Plane} sj Plane shape
+ * @param {Array} xj World position for the plane
+ * @param {Number} aj World angle for the plane
+ */
+Narrowphase.prototype[Shape.PARTICLE | Shape.PLANE] =
+Narrowphase.prototype.particlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var particleBody = bi,
+ particleShape = si,
+ particleOffset = xi,
+ planeBody = bj,
+ planeShape = sj,
+ planeOffset = xj,
+ planeAngle = aj;
+
+ var dist = tmp1,
+ worldNormal = tmp2;
+
+ planeAngle = planeAngle || 0;
+
+ sub(dist, particleOffset, planeOffset);
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ var d = dot(dist, worldNormal);
+
+ if(d > 0) return 0;
+ if(justTest) return true;
+
+ var c = this.createContactEquation(planeBody,particleBody,sj,si);
+
+ vec2.copy(c.ni, worldNormal);
+ vec2.scale( dist, c.ni, d );
+ // dist is now the distance vector in the normal direction
+
+ // ri is the particle position projected down onto the plane, from the plane center
+ sub( c.ri, particleOffset, dist);
+ sub( c.ri, c.ri, planeBody.position);
+
+ // rj is from the body center to the particle center
+ sub( c.rj, particleOffset, particleBody.position );
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+ return 1;
+};
+
+/**
+ * Circle/Particle Narrowphase
+ * @method circleParticle
+ * @param {Body} bi
+ * @param {Circle} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Particle} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Narrowphase.prototype[Shape.CIRCLE | Shape.PARTICLE] =
+Narrowphase.prototype.circleParticle = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var circleBody = bi,
+ circleShape = si,
+ circleOffset = xi,
+ particleBody = bj,
+ particleShape = sj,
+ particleOffset = xj,
+ dist = tmp1;
+
+ sub(dist, particleOffset, circleOffset);
+ if(vec2.squaredLength(dist) > circleShape.radius*circleShape.radius) return 0;
+ if(justTest) return true;
+
+ var c = this.createContactEquation(circleBody,particleBody,si,sj);
+ vec2.copy(c.ni, dist);
+ vec2.normalize(c.ni,c.ni);
+
+ // Vector from circle to contact point is the normal times the circle radius
+ vec2.scale(c.ri, c.ni, circleShape.radius);
+ add(c.ri, c.ri, circleOffset);
+ sub(c.ri, c.ri, circleBody.position);
+
+ // Vector from particle center to contact point is zero
+ sub(c.rj, particleOffset, particleBody.position);
+
+ this.contactEquations.push(c);
+
+ if(this.enableFriction){
+ this.frictionEquations.push(this.createFrictionFromContact(c));
+ }
+
+ return 1;
+};
+
+var capsulePlane_tmpCircle = new Circle(1),
+ capsulePlane_tmp1 = vec2.create(),
+ capsulePlane_tmp2 = vec2.create(),
+ capsulePlane_tmp3 = vec2.create();
+
+Narrowphase.prototype[Shape.PLANE | Shape.CAPSULE] =
+Narrowphase.prototype.planeCapsule = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var end1 = capsulePlane_tmp1,
+ end2 = capsulePlane_tmp2,
+ circle = capsulePlane_tmpCircle,
+ dst = capsulePlane_tmp3;
+
+ // Compute world end positions
+ vec2.set(end1, -sj.length/2, 0);
+ vec2.rotate(end1,end1,aj);
+ add(end1,end1,xj);
+
+ vec2.set(end2, sj.length/2, 0);
+ vec2.rotate(end2,end2,aj);
+ add(end2,end2,xj);
+
+ circle.radius = sj.radius;
+
+ // Do Narrowphase as two circles
+ var numContacts1 = this.circlePlane(bj,circle,end1,0, bi,si,xi,ai, justTest),
+ numContacts2 = this.circlePlane(bj,circle,end2,0, bi,si,xi,ai, justTest);
+
+ if(justTest)
+ return numContacts1 || numContacts2;
+ else
+ return numContacts1 + numContacts2;
+};
+
+/**
+ * @method capsulePlane
+ * @deprecated Use .planeCapsule() instead!
+ */
+Narrowphase.prototype.capsulePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ console.warn("Narrowphase.prototype.capsulePlane() is deprecated. Use .planeCapsule() instead!");
+ return this.planeCapsule( bj,sj,xj,aj, bi,si,xi,ai, justTest );
+}
+
+/**
+ * Creates ContactEquations and FrictionEquations for a collision.
+ * @method circlePlane
+ * @param {Body} bi The first body that should be connected to the equations.
+ * @param {Circle} si The circle shape participating in the collision.
+ * @param {Array} xi Extra offset to take into account for the Shape, in addition to the one in circleBody.position. Will *not* be rotated by circleBody.angle (maybe it should, for sake of homogenity?). Set to null if none.
+ * @param {Body} bj The second body that should be connected to the equations.
+ * @param {Plane} sj The Plane shape that is participating
+ * @param {Array} xj Extra offset for the plane shape.
+ * @param {Number} aj Extra angle to apply to the plane
+ */
+Narrowphase.prototype[Shape.CIRCLE | Shape.PLANE] =
+Narrowphase.prototype.circlePlane = function( bi,si,xi,ai, bj,sj,xj,aj, justTest ){
+ var circleBody = bi,
+ circleShape = si,
+ circleOffset = xi, // Offset from body center, rotated!
+ planeBody = bj,
+ shapeB = sj,
+ planeOffset = xj,
+ planeAngle = aj;
+
+ planeAngle = planeAngle || 0;
+
+ // Vector from plane to circle
+ var planeToCircle = tmp1,
+ worldNormal = tmp2,
+ temp = tmp3;
+
+ sub(planeToCircle, circleOffset, planeOffset);
+
+ // World plane normal
+ vec2.rotate(worldNormal, yAxis, planeAngle);
+
+ // Normal direction distance
+ var d = dot(worldNormal, planeToCircle);
+
+ if(d > circleShape.radius) return 0; // No overlap. Abort.
+
+ if(justTest) return true;
+
+ // Create contact
+ var contact = this.createContactEquation(planeBody,circleBody,sj,si);
+
+ // ni is the plane world normal
+ vec2.copy(contact.ni, worldNormal);
+
+ // rj is the vector from circle center to the contact point
+ vec2.scale(contact.rj, contact.ni, -circleShape.radius);
+ add(contact.rj, contact.rj, circleOffset);
+ sub(contact.rj, contact.rj, circleBody.position);
+
+ // ri is the distance from plane center to contact.
+ vec2.scale(temp, contact.ni, d);
+ sub(contact.ri, planeToCircle, temp ); // Subtract normal distance vector from the distance vector
+ add(contact.ri, contact.ri, planeOffset);
+ sub(contact.ri, contact.ri, planeBody.position);
+
+ this.contactEquations.push(contact);
+
+ if(this.enableFriction){
+ this.frictionEquations.push( this.createFrictionFromContact(contact) );
+ }
+
+ return 1;
+};
+
+
+/**
+ * Convex/convex Narrowphase.See this article for more info.
+ * @method convexConvex
+ * @param {Body} bi
+ * @param {Convex} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Convex} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ */
+Narrowphase.prototype[Shape.CONVEX] =
+Narrowphase.prototype.convexConvex = function( bi,si,xi,ai, bj,sj,xj,aj, justTest, precision ){
+ var sepAxis = tmp1,
+ worldPoint = tmp2,
+ worldPoint0 = tmp3,
+ worldPoint1 = tmp4,
+ worldEdge = tmp5,
+ projected = tmp6,
+ penetrationVec = tmp7,
+ dist = tmp8,
+ worldNormal = tmp9,
+ numContacts = 0,
+ precision = precision || 1e-10;
+
+ var found = Narrowphase.findSeparatingAxis(si,xi,ai,sj,xj,aj,sepAxis);
+ if(!found) return 0;
+
+ // Make sure the separating axis is directed from shape i to shape j
+ sub(dist,xj,xi);
+ if(dot(sepAxis,dist) > 0){
+ vec2.scale(sepAxis,sepAxis,-1);
+ }
+
+ // Find edges with normals closest to the separating axis
+ var closestEdge1 = Narrowphase.getClosestEdge(si,ai,sepAxis,true), // Flipped axis
+ closestEdge2 = Narrowphase.getClosestEdge(sj,aj,sepAxis);
+
+ if(closestEdge1==-1 || closestEdge2==-1) return 0;
+
+ // Loop over the shapes
+ for(var k=0; k<2; k++){
+
+ var closestEdgeA = closestEdge1,
+ closestEdgeB = closestEdge2,
+ shapeA = si, shapeB = sj,
+ offsetA = xi, offsetB = xj,
+ angleA = ai, angleB = aj,
+ bodyA = bi, bodyB = bj;
+
+ if(k==0){
+ // Swap!
+ var tmp;
+ tmp = closestEdgeA; closestEdgeA = closestEdgeB; closestEdgeB = tmp;
+ tmp = shapeA; shapeA = shapeB; shapeB = tmp;
+ tmp = offsetA; offsetA = offsetB; offsetB = tmp;
+ tmp = angleA; angleA = angleB; angleB = tmp;
+ tmp = bodyA; bodyA = bodyB; bodyB = tmp;
+ }
+
+ // Loop over 2 points in convex B
+ for(var j=closestEdgeB; j max) max = value;
+ if(min === null || value < min) min = value;
+ }
+
+ if(min > max){
+ var t = min;
+ min = max;
+ max = t;
+ }
+
+ // Project the position of the body onto the axis - need to add this to the result
+ var offset = dot(convexOffset, worldAxis);
+
+ vec2.set( result, min + offset, max + offset);
+};
+
+// .findSeparatingAxis is called by other functions, need local tmp vectors
+var fsa_tmp1 = vec2.fromValues(0,0)
+, fsa_tmp2 = vec2.fromValues(0,0)
+, fsa_tmp3 = vec2.fromValues(0,0)
+, fsa_tmp4 = vec2.fromValues(0,0)
+, fsa_tmp5 = vec2.fromValues(0,0)
+, fsa_tmp6 = vec2.fromValues(0,0)
+
+/**
+ * Find a separating axis between the shapes, that maximizes the separating distance between them.
+ * @method findSeparatingAxis
+ * @static
+ * @param {Convex} c1
+ * @param {Array} offset1
+ * @param {Number} angle1
+ * @param {Convex} c2
+ * @param {Array} offset2
+ * @param {Number} angle2
+ * @param {Array} sepAxis The resulting axis
+ * @return {Boolean} Whether the axis could be found.
+ */
+Narrowphase.findSeparatingAxis = function(c1,offset1,angle1,c2,offset2,angle2,sepAxis){
+ var maxDist = null,
+ overlap = false,
+ found = false,
+ edge = fsa_tmp1,
+ worldPoint0 = fsa_tmp2,
+ worldPoint1 = fsa_tmp3,
+ normal = fsa_tmp4,
+ span1 = fsa_tmp5,
+ span2 = fsa_tmp6;
+
+ for(var j=0; j!==2; j++){
+ var c = c1,
+ angle = angle1;
+ if(j===1){
+ c = c2;
+ angle = angle2;
+ }
+
+ for(var i=0; i!==c.vertices.length; i++){
+ // Get the world edge
+ vec2.rotate(worldPoint0, c.vertices[i], angle);
+ vec2.rotate(worldPoint1, c.vertices[(i+1)%c.vertices.length], angle);
+
+ sub(edge, worldPoint1, worldPoint0);
+
+ // Get normal - just rotate 90 degrees since vertices are given in CCW
+ vec2.rotate(normal, edge, -Math.PI / 2);
+ vec2.normalize(normal,normal);
+
+ // Project hulls onto that normal
+ Narrowphase.projectConvexOntoAxis(c1,offset1,angle1,normal,span1);
+ Narrowphase.projectConvexOntoAxis(c2,offset2,angle2,normal,span2);
+
+ // Order by span position
+ var a=span1,
+ b=span2,
+ swapped = false;
+ if(span1[0] > span2[0]){
+ b=span1;
+ a=span2;
+ swapped = true;
+ }
+
+ // Get separating distance
+ var dist = b[0] - a[1];
+ overlap = dist < 0;
+
+ if(maxDist===null || dist > maxDist){
+ vec2.copy(sepAxis, normal);
+ maxDist = dist;
+ found = overlap;
+ }
+ }
+ }
+
+ return found;
+};
+
+// .getClosestEdge is called by other functions, need local tmp vectors
+var gce_tmp1 = vec2.fromValues(0,0)
+, gce_tmp2 = vec2.fromValues(0,0)
+, gce_tmp3 = vec2.fromValues(0,0)
+
+/**
+ * Get the edge that has a normal closest to an axis.
+ * @method getClosestEdge
+ * @static
+ * @param {Convex} c
+ * @param {Number} angle
+ * @param {Array} axis
+ * @param {Boolean} flip
+ * @return {Number} Index of the edge that is closest. This index and the next spans the resulting edge. Returns -1 if failed.
+ */
+Narrowphase.getClosestEdge = function(c,angle,axis,flip){
+ var localAxis = gce_tmp1,
+ edge = gce_tmp2,
+ normal = gce_tmp3;
+
+ // Convert the axis to local coords of the body
+ vec2.rotate(localAxis, axis, -angle);
+ if(flip){
+ vec2.scale(localAxis,localAxis,-1);
+ }
+
+ var closestEdge = -1,
+ N = c.vertices.length,
+ halfPi = Math.PI / 2;
+ for(var i=0; i!==N; i++){
+ // Get the edge
+ sub(edge, c.vertices[(i+1)%N], c.vertices[i%N]);
+
+ // Get normal - just rotate 90 degrees since vertices are given in CCW
+ vec2.rotate(normal, edge, -halfPi);
+ vec2.normalize(normal,normal);
+
+ var d = dot(normal,localAxis);
+ if(closestEdge == -1 || d > maxDot){
+ closestEdge = i % N;
+ maxDot = d;
+ }
+ }
+
+ return closestEdge;
+};
+
+
+},{"../equations/ContactEquation":23,"../equations/FrictionEquation":25,"../math/vec2":33,"../objects/Body":34,"../shapes/Circle":38,"../shapes/Shape":44,"../utils/Utils":49}],14:[function(require,module,exports){
+var Plane = require("../shapes/Plane");
+var Broadphase = require("../collision/Broadphase");
+
+module.exports = {
+ QuadTree : QuadTree,
+ Node : Node,
+ BoundsNode : BoundsNode,
+};
+
+/**
+ * QuadTree data structure. See https://github.com/mikechambers/ExamplesByMesh/tree/master/JavaScript/QuadTree
+ * @class QuadTree
+ * @constructor
+ * @param {Object} An object representing the bounds of the top level of the QuadTree. The object
+ * should contain the following properties : x, y, width, height
+ * @param {Boolean} pointQuad Whether the QuadTree will contain points (true), or items with bounds
+ * (width / height)(false). Default value is false.
+ * @param {Number} maxDepth The maximum number of levels that the quadtree will create. Default is 4.
+ * @param {Number} maxChildren The maximum number of children that a node can contain before it is split into sub-nodes.
+ */
+function QuadTree(bounds, pointQuad, maxDepth, maxChildren){
+ var node;
+ if(pointQuad){
+ node = new Node(bounds, 0, maxDepth, maxChildren);
+ } else {
+ node = new BoundsNode(bounds, 0, maxDepth, maxChildren);
+ }
+
+ /**
+ * The root node of the QuadTree which covers the entire area being segmented.
+ * @property root
+ * @type Node
+ */
+ this.root = node;
+}
+
+/**
+ * Inserts an item into the QuadTree.
+ * @method insert
+ * @param {Object|Array} item The item or Array of items to be inserted into the QuadTree. The item should expose x, y
+ * properties that represents its position in 2D space.
+ */
+QuadTree.prototype.insert = function(item){
+ if(item instanceof Array){
+ var len = item.length;
+ for(var i = 0; i < len; i++){
+ this.root.insert(item[i]);
+ }
+ } else {
+ this.root.insert(item);
+ }
+}
+
+/**
+ * Clears all nodes and children from the QuadTree
+ * @method clear
+ */
+QuadTree.prototype.clear = function(){
+ this.root.clear();
+}
+
+/**
+ * Retrieves all items / points in the same node as the specified item / point. If the specified item
+ * overlaps the bounds of a node, then all children in both nodes will be returned.
+ * @method retrieve
+ * @param {Object} item An object representing a 2D coordinate point (with x, y properties), or a shape
+ * with dimensions (x, y, width, height) properties.
+ */
+QuadTree.prototype.retrieve = function(item){
+ //get a copy of the array of items
+ var out = this.root.retrieve(item).slice(0);
+ return out;
+}
+
+QuadTree.prototype.getCollisionPairs = function(world){
+
+ var result = [];
+
+ // Add all bodies
+ this.insert(world.bodies);
+
+ /*
+ console.log("bodies",world.bodies.length);
+ console.log("maxDepth",this.root.maxDepth,"maxChildren",this.root.maxChildren);
+ */
+
+ for(var i=0; i!==world.bodies.length; i++){
+ var b = world.bodies[i],
+ items = this.retrieve(b);
+
+ //console.log("items",items.length);
+
+ // Check results
+ for(var j=0, len=items.length; j!==len; j++){
+ var item = items[j];
+
+ if(b === item) continue; // Do not add self
+
+ // Check if they were already added
+ var found = false;
+ for(var k=0, numAdded=result.length; k= this.maxDepth) && len > this.maxChildren) {
+ this.subdivide();
+
+ for(var i = 0; i < len; i++){
+ this.insert(this.children[i]);
+ }
+
+ this.children.length = 0;
+ }
+}
+
+Node.prototype.retrieve = function(item){
+ if(this.nodes.length){
+ var index = this.findIndex(item);
+ return this.nodes[index].retrieve(item);
+ }
+
+ return this.children;
+}
+
+Node.prototype.findIndex = function(item){
+ var b = this.bounds;
+ var left = (item.position[0]-item.boundingRadius > b.x + b.width / 2) ? false : true;
+ var top = (item.position[1]-item.boundingRadius > b.y + b.height / 2) ? false : true;
+
+ if(item instanceof Plane){
+ left = top = false; // Will overlap the left/top boundary since it is infinite
+ }
+
+ //top left
+ var index = Node.TOP_LEFT;
+ if(left){
+ if(!top){
+ index = Node.BOTTOM_LEFT;
+ }
+ } else {
+ if(top){
+ index = Node.TOP_RIGHT;
+ } else {
+ index = Node.BOTTOM_RIGHT;
+ }
+ }
+
+ return index;
+}
+
+
+Node.prototype.subdivide = function(){
+ var depth = this.depth + 1;
+
+ var bx = this.bounds.x;
+ var by = this.bounds.y;
+
+ //floor the values
+ var b_w_h = (this.bounds.width / 2);
+ var b_h_h = (this.bounds.height / 2);
+ var bx_b_w_h = bx + b_w_h;
+ var by_b_h_h = by + b_h_h;
+
+ //top left
+ this.nodes[Node.TOP_LEFT] = new this.classConstructor({
+ x:bx,
+ y:by,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+ //top right
+ this.nodes[Node.TOP_RIGHT] = new this.classConstructor({
+ x:bx_b_w_h,
+ y:by,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+ //bottom left
+ this.nodes[Node.BOTTOM_LEFT] = new this.classConstructor({
+ x:bx,
+ y:by_b_h_h,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+
+
+ //bottom right
+ this.nodes[Node.BOTTOM_RIGHT] = new this.classConstructor({
+ x:bx_b_w_h,
+ y:by_b_h_h,
+ width:b_w_h,
+ height:b_h_h
+ },
+ depth);
+}
+
+Node.prototype.clear = function(){
+ this.children.length = 0;
+
+ var len = this.nodes.length;
+ for(var i = 0; i < len; i++){
+ this.nodes[i].clear();
+ }
+
+ this.nodes.length = 0;
+}
+
+
+// BoundsQuadTree
+
+function BoundsNode(bounds, depth, maxChildren, maxDepth){
+ Node.call(this, bounds, depth, maxChildren, maxDepth);
+ this.stuckChildren = [];
+}
+
+BoundsNode.prototype = new Node();
+BoundsNode.prototype.classConstructor = BoundsNode;
+BoundsNode.prototype.stuckChildren = null;
+
+//we use this to collect and conctenate items being retrieved. This way
+//we dont have to continuously create new Array instances.
+//Note, when returned from QuadTree.retrieve, we then copy the array
+BoundsNode.prototype.out = [];
+
+BoundsNode.prototype.insert = function(item){
+ if(this.nodes.length){
+ var index = this.findIndex(item);
+ var node = this.nodes[index];
+
+ /*
+ console.log("radius:",item.boundingRadius);
+ console.log("item x:",item.position[0] - item.boundingRadius,"x range:",node.bounds.x,node.bounds.x+node.bounds.width);
+ console.log("item y:",item.position[1] - item.boundingRadius,"y range:",node.bounds.y,node.bounds.y+node.bounds.height);
+ */
+
+ //todo: make _bounds bounds
+ if( !(item instanceof Plane) && // Plane is infinite.. Make it a "stuck" child
+ item.position[0] - item.boundingRadius >= node.bounds.x &&
+ item.position[0] + item.boundingRadius <= node.bounds.x + node.bounds.width &&
+ item.position[1] - item.boundingRadius >= node.bounds.y &&
+ item.position[1] + item.boundingRadius <= node.bounds.y + node.bounds.height){
+ this.nodes[index].insert(item);
+ } else {
+ this.stuckChildren.push(item);
+ }
+
+ return;
+ }
+
+ this.children.push(item);
+
+ var len = this.children.length;
+
+ if(this.depth < this.maxDepth && len > this.maxChildren){
+ this.subdivide();
+
+ for(var i=0; i=0;j--) {
+ if(a[j].aabb.lowerBound[0] <= v.aabb.lowerBound[0])
+ break;
+ a[j+1] = a[j];
+ }
+ a[j+1] = v;
+ }
+ return a;
+};
+
+/**
+ * Sorts bodies along the Y axis.
+ * @method sortAxisListY
+ * @param {Array} a
+ * @return {Array}
+ */
+SAPBroadphase.sortAxisListY = function(a){
+ for(var i=1,l=a.length;i=0;j--) {
+ if(a[j].aabb.lowerBound[1] <= v.aabb.lowerBound[1])
+ break;
+ a[j+1] = a[j];
+ }
+ a[j+1] = v;
+ }
+ return a;
+};
+
+var preliminaryList = { keys:[] };
+
+/**
+ * Get the colliding pairs
+ * @method getCollisionPairs
+ * @param {World} world
+ * @return {Array}
+ */
+SAPBroadphase.prototype.getCollisionPairs = function(world){
+ var bodiesX = this.axisListX,
+ bodiesY = this.axisListY,
+ result = this.result,
+ axisIndex = this.axisIndex,
+ i,j;
+
+ result.length = 0;
+
+ // Update all AABBs if needed
+ for(i=0; i!==bodiesX.length; i++){
+ var b = bodiesX[i];
+ if(b.aabbNeedsUpdate) b.updateAABB();
+ }
+
+ // Sort the lists
+ SAPBroadphase.sortAxisListX(bodiesX);
+ SAPBroadphase.sortAxisListY(bodiesY);
+
+ // Look through the X list
+ for(i=0, N=bodiesX.length; i!==N; i++){
+ var bi = bodiesX[i];
+
+ for(j=i+1; jthis tutorial.
+ *
+ * @class PrismaticConstraint
+ * @constructor
+ * @extends {Constraint}
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Object} options
+ * @param {Number} options.maxForce Max force to be applied by the constraint
+ * @param {Array} options.localAnchorA Body A's anchor point, defined in its own local frame.
+ * @param {Array} options.localAnchorB Body B's anchor point, defined in its own local frame.
+ * @param {Array} options.localAxisA An axis, defined in body A frame, that body B's anchor point may slide along.
+ * @param {Boolean} options.disableRotationalLock If set to true, bodyB will be free to rotate around its anchor point.
+ */
+function PrismaticConstraint(bodyA,bodyB,options){
+ options = options || {};
+ Constraint.call(this,bodyA,bodyB);
+
+ // Get anchors
+ var localAnchorA = vec2.fromValues(0,0),
+ localAxisA = vec2.fromValues(1,0),
+ localAnchorB = vec2.fromValues(0,0);
+ if(options.localAnchorA) vec2.copy(localAnchorA, options.localAnchorA);
+ if(options.localAxisA) vec2.copy(localAxisA, options.localAxisA);
+ if(options.localAnchorB) vec2.copy(localAnchorB, options.localAnchorB);
+
+ /**
+ * @property localAnchorA
+ * @type {Array}
+ */
+ this.localAnchorA = localAnchorA;
+
+ /**
+ * @property localAnchorB
+ * @type {Array}
+ */
+ this.localAnchorB = localAnchorB;
+
+ /**
+ * @property localAxisA
+ * @type {Array}
+ */
+ this.localAxisA = localAxisA;
+
+ /*
+
+ The constraint violation for the common axis point is
+
+ g = ( xj + rj - xi - ri ) * t := gg*t
+
+ where r are body-local anchor points, and t is a tangent to the constraint axis defined in body i frame.
+
+ gdot = ( vj + wj x rj - vi - wi x ri ) * t + ( xj + rj - xi - ri ) * ( wi x t )
+
+ Note the use of the chain rule. Now we identify the jacobian
+
+ G*W = [ -t -ri x t + t x gg t rj x t ] * [vi wi vj wj]
+
+ The rotational part is just a rotation lock.
+
+ */
+
+ var maxForce = this.maxForce = typeof(options.maxForce)!="undefined" ? options.maxForce : Number.MAX_VALUE;
+
+ // Translational part
+ var trans = new Equation(bodyA,bodyB,-maxForce,maxForce);
+ var ri = new vec2.create(),
+ rj = new vec2.create(),
+ gg = new vec2.create(),
+ t = new vec2.create();
+ trans.computeGq = function(){
+ // g = ( xj + rj - xi - ri ) * t
+ return vec2.dot(gg,t);
+ };
+ trans.update = function(){
+ var G = this.G,
+ xi = bodyA.position,
+ xj = bodyB.position;
+ vec2.rotate(ri,localAnchorA,bodyA.angle);
+ vec2.rotate(rj,localAnchorB,bodyB.angle);
+ vec2.add(gg,xj,rj);
+ vec2.sub(gg,gg,xi);
+ vec2.sub(gg,gg,ri);
+ vec2.rotate(t,localAxisA,bodyA.angle+Math.PI/2);
+
+ G[0] = -t[0];
+ G[1] = -t[1];
+ G[2] = -vec2.crossLength(ri,t) + vec2.crossLength(t,gg);
+ G[3] = t[0];
+ G[4] = t[1];
+ G[5] = vec2.crossLength(rj,t);
+ }
+ this.equations.push(trans);
+
+ // Rotational part
+ if(!options.disableRotationalLock){
+ var rot = new RotationalLockEquation(bodyA,bodyB,-maxForce,maxForce);
+ this.equations.push(rot);
+ }
+
+ /**
+ * The position of anchor A relative to anchor B, along the constraint axis.
+ * @property position
+ * @type {Number}
+ */
+ this.position = 0;
+
+ this.velocity = 0;
+
+ /**
+ * Set to true to enable lower limit.
+ * @property lowerLimitEnabled
+ * @type {Boolean}
+ */
+ this.lowerLimitEnabled = false;
+
+ /**
+ * Set to true to enable upper limit.
+ * @property upperLimitEnabled
+ * @type {Boolean}
+ */
+ this.upperLimitEnabled = false;
+
+ /**
+ * Lower constraint limit. The constraint position is forced to be larger than this value.
+ * @property lowerLimit
+ * @type {Number}
+ */
+ this.lowerLimit = 0;
+
+ /**
+ * Upper constraint limit. The constraint position is forced to be smaller than this value.
+ * @property upperLimit
+ * @type {Number}
+ */
+ this.upperLimit = 1;
+
+ // Equations used for limits
+ this.upperLimitEquation = new ContactEquation(bodyA,bodyB);
+ this.lowerLimitEquation = new ContactEquation(bodyA,bodyB);
+
+ // Set max/min forces
+ this.upperLimitEquation.minForce = this.lowerLimitEquation.minForce = 0;
+ this.upperLimitEquation.maxForce = this.lowerLimitEquation.maxForce = maxForce;
+
+ /**
+ * Equation used for the motor.
+ * @property motorEquation
+ * @type {Equation}
+ */
+ this.motorEquation = new Equation(bodyA,bodyB);
+
+ /**
+ * The current motor state. Enable or disable the motor using .enableMotor
+ * @property motorEnabled
+ * @type {Boolean}
+ */
+ this.motorEnabled = false;
+
+ /**
+ * Set the target speed for the motor.
+ * @property motorSpeed
+ * @type {Number}
+ */
+ this.motorSpeed = 0;
+
+ var that = this;
+ var motorEquation = this.motorEquation;
+ var old = motorEquation.computeGW;
+ motorEquation.computeGq = function(){ return 0; };
+ motorEquation.computeGW = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ vi = bi.velocity,
+ vj = bj.velocity,
+ wi = bi.angularVelocity,
+ wj = bj.angularVelocity;
+ return this.transformedGmult(G,vi,wi,vj,wj) + that.motorSpeed;
+ };
+}
+
+PrismaticConstraint.prototype = new Constraint();
+
+var worldAxisA = vec2.create(),
+ worldAnchorA = vec2.create(),
+ worldAnchorB = vec2.create(),
+ orientedAnchorA = vec2.create(),
+ orientedAnchorB = vec2.create(),
+ tmp = vec2.create();
+
+/**
+ * Update the constraint equations. Should be done if any of the bodies changed position, before solving.
+ * @method update
+ */
+PrismaticConstraint.prototype.update = function(){
+ var eqs = this.equations,
+ trans = eqs[0],
+ upperLimit = this.upperLimit,
+ lowerLimit = this.lowerLimit,
+ upperLimitEquation = this.upperLimitEquation,
+ lowerLimitEquation = this.lowerLimitEquation,
+ bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ localAxisA = this.localAxisA,
+ localAnchorA = this.localAnchorA,
+ localAnchorB = this.localAnchorB;
+
+ trans.update();
+
+ // Transform local things to world
+ vec2.rotate(worldAxisA, localAxisA, bodyA.angle);
+ vec2.rotate(orientedAnchorA, localAnchorA, bodyA.angle);
+ vec2.add(worldAnchorA, orientedAnchorA, bodyA.position);
+ vec2.rotate(orientedAnchorB, localAnchorB, bodyB.angle);
+ vec2.add(worldAnchorB, orientedAnchorB, bodyB.position);
+
+ var relPosition = this.position = vec2.dot(worldAnchorB,worldAxisA) - vec2.dot(worldAnchorA,worldAxisA);
+
+ // Motor
+ if(this.motorEnabled){
+ // G = [ a a x ri -a -a x rj ]
+ var G = this.motorEquation.G;
+ G[0] = worldAxisA[0];
+ G[1] = worldAxisA[1];
+ G[2] = vec2.crossLength(worldAxisA,orientedAnchorB);
+ G[3] = -worldAxisA[0];
+ G[4] = -worldAxisA[1];
+ G[5] = -vec2.crossLength(worldAxisA,orientedAnchorA);
+ }
+
+ /*
+ Limits strategy:
+ Add contact equation, with normal along the constraint axis.
+ min/maxForce is set so the constraint is repulsive in the correct direction.
+ Some offset is added to either equation.ri or .rj to get the correct upper/lower limit.
+
+ ^
+ |
+ upperLimit x
+ | ------
+ anchorB x<---| B |
+ | | |
+ ------ | ------
+ | | |
+ | A |-->x anchorA
+ ------ |
+ x lowerLimit
+ |
+ axis
+ */
+
+ if(this.upperLimitEnabled && relPosition > upperLimit){
+ // Update contact constraint normal, etc
+ vec2.scale(upperLimitEquation.ni, worldAxisA, -1);
+ vec2.sub(upperLimitEquation.ri, worldAnchorA, bodyA.position);
+ vec2.sub(upperLimitEquation.rj, worldAnchorB, bodyB.position);
+ vec2.scale(tmp,worldAxisA,upperLimit);
+ vec2.add(upperLimitEquation.ri,upperLimitEquation.ri,tmp);
+ if(eqs.indexOf(upperLimitEquation)==-1)
+ eqs.push(upperLimitEquation);
+ } else {
+ var idx = eqs.indexOf(upperLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+
+ if(this.lowerLimitEnabled && relPosition < lowerLimit){
+ // Update contact constraint normal, etc
+ vec2.scale(lowerLimitEquation.ni, worldAxisA, 1);
+ vec2.sub(lowerLimitEquation.ri, worldAnchorA, bodyA.position);
+ vec2.sub(lowerLimitEquation.rj, worldAnchorB, bodyB.position);
+ vec2.scale(tmp,worldAxisA,lowerLimit);
+ vec2.sub(lowerLimitEquation.rj,lowerLimitEquation.rj,tmp);
+ if(eqs.indexOf(lowerLimitEquation)==-1)
+ eqs.push(lowerLimitEquation);
+ } else {
+ var idx = eqs.indexOf(lowerLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+};
+
+/**
+ * Enable the motor
+ * @method enableMotor
+ */
+PrismaticConstraint.prototype.enableMotor = function(){
+ if(this.motorEnabled) return;
+ this.equations.push(this.motorEquation);
+ this.motorEnabled = true;
+};
+
+/**
+ * Disable the rotational motor
+ * @method disableMotor
+ */
+PrismaticConstraint.prototype.disableMotor = function(){
+ if(!this.motorEnabled) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.equations.splice(i,1);
+ this.motorEnabled = false;
+};
+
+},{"../equations/ContactEquation":23,"../equations/Equation":24,"../equations/RotationalLockEquation":26,"../math/vec2":33,"./Constraint":16}],21:[function(require,module,exports){
+var Constraint = require('./Constraint')
+, Equation = require('../equations/Equation')
+, RotationalVelocityEquation = require('../equations/RotationalVelocityEquation')
+, RotationalLockEquation = require('../equations/RotationalLockEquation')
+, vec2 = require('../math/vec2')
+
+module.exports = RevoluteConstraint;
+
+var worldPivotA = vec2.create(),
+ worldPivotB = vec2.create(),
+ xAxis = vec2.fromValues(1,0),
+ yAxis = vec2.fromValues(0,1),
+ g = vec2.create();
+
+/**
+ * Connects two bodies at given offset points, letting them rotate relative to each other around this point.
+ * @class RevoluteConstraint
+ * @constructor
+ * @author schteppe
+ * @param {Body} bodyA
+ * @param {Float32Array} pivotA The point relative to the center of mass of bodyA which bodyA is constrained to.
+ * @param {Body} bodyB Body that will be constrained in a similar way to the same point as bodyA. We will therefore get sort of a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.
+ * @param {Float32Array} pivotB See pivotA.
+ * @param {Number} maxForce The maximum force that should be applied to constrain the bodies.
+ * @extends {Constraint}
+ * @todo Ability to specify world points
+ */
+function RevoluteConstraint(bodyA, pivotA, bodyB, pivotB, maxForce){
+ Constraint.call(this,bodyA,bodyB);
+
+ maxForce = this.maxForce = typeof(maxForce)!="undefined" ? maxForce : Number.MAX_VALUE;
+
+ this.pivotA = pivotA;
+ this.pivotB = pivotB;
+
+ // Equations to be fed to the solver
+ var eqs = this.equations = [
+ new Equation(bodyA,bodyB,-maxForce,maxForce),
+ new Equation(bodyA,bodyB,-maxForce,maxForce),
+ ];
+
+ var x = eqs[0];
+ var y = eqs[1];
+
+ x.computeGq = function(){
+ vec2.rotate(worldPivotA, pivotA, bodyA.angle);
+ vec2.rotate(worldPivotB, pivotB, bodyB.angle);
+ vec2.add(g, bodyB.position, worldPivotB);
+ vec2.sub(g, g, bodyA.position);
+ vec2.sub(g, g, worldPivotA);
+ return vec2.dot(g,xAxis);
+ };
+
+ y.computeGq = function(){
+ vec2.rotate(worldPivotA, pivotA, bodyA.angle);
+ vec2.rotate(worldPivotB, pivotB, bodyB.angle);
+ vec2.add(g, bodyB.position, worldPivotB);
+ vec2.sub(g, g, bodyA.position);
+ vec2.sub(g, g, worldPivotA);
+ return vec2.dot(g,yAxis);
+ };
+
+ y.minForce = x.minForce = -maxForce;
+ y.maxForce = x.maxForce = maxForce;
+
+ this.motorEquation = new RotationalVelocityEquation(bodyA,bodyB);
+ this.motorEnabled = false;
+
+ /**
+ * The constraint position
+ * @property angle
+ * @type {Number}
+ */
+ this.angle = 0;
+
+ /**
+ * Set to true to enable lower limit
+ * @property lowerLimitEnabled
+ * @type {Boolean}
+ */
+ this.lowerLimitEnabled = false;
+
+ /**
+ * Set to true to enable upper limit
+ * @property upperLimitEnabled
+ * @type {Boolean}
+ */
+ this.upperLimitEnabled = false;
+
+ /**
+ * The lower limit on the constraint angle.
+ * @property lowerLimit
+ * @type {Boolean}
+ */
+ this.lowerLimit = 0;
+
+ /**
+ * The upper limit on the constraint angle.
+ * @property upperLimit
+ * @type {Boolean}
+ */
+ this.upperLimit = 0;
+
+ this.upperLimitEquation = new RotationalLockEquation(bodyA,bodyB);
+ this.lowerLimitEquation = new RotationalLockEquation(bodyA,bodyB);
+ this.upperLimitEquation.minForce = 0;
+ this.lowerLimitEquation.maxForce = 0;
+}
+RevoluteConstraint.prototype = new Constraint();
+
+RevoluteConstraint.prototype.update = function(){
+ var bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ pivotA = this.pivotA,
+ pivotB = this.pivotB,
+ eqs = this.equations,
+ normal = eqs[0],
+ tangent= eqs[1],
+ x = eqs[0],
+ y = eqs[1],
+ upperLimit = this.upperLimit,
+ lowerLimit = this.lowerLimit,
+ upperLimitEquation = this.upperLimitEquation,
+ lowerLimitEquation = this.lowerLimitEquation;
+
+ var relAngle = this.angle = bodyB.angle - bodyA.angle;
+
+ if(this.upperLimitEnabled && relAngle > upperLimit){
+ upperLimitEquation.angle = upperLimit;
+ if(eqs.indexOf(upperLimitEquation)==-1)
+ eqs.push(upperLimitEquation);
+ } else {
+ var idx = eqs.indexOf(upperLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+
+ if(this.lowerLimitEnabled && relAngle < lowerLimit){
+ lowerLimitEquation.angle = lowerLimit;
+ if(eqs.indexOf(lowerLimitEquation)==-1)
+ eqs.push(lowerLimitEquation);
+ } else {
+ var idx = eqs.indexOf(lowerLimitEquation);
+ if(idx != -1) eqs.splice(idx,1);
+ }
+
+ /*
+
+ The constraint violation is
+
+ g = xj + rj - xi - ri
+
+ ...where xi and xj are the body positions and ri and rj world-oriented offset vectors. Differentiate:
+
+ gdot = vj + wj x rj - vi - wi x ri
+
+ We split this into x and y directions. (let x and y be unit vectors along the respective axes)
+
+ gdot * x = ( vj + wj x rj - vi - wi x ri ) * x
+ = ( vj*x + (wj x rj)*x -vi*x -(wi x ri)*x
+ = ( vj*x + (rj x x)*wj -vi*x -(ri x x)*wi
+ = [ -x -(ri x x) x (rj x x)] * [vi wi vj wj]
+ = G*W
+
+ ...and similar for y. We have then identified the jacobian entries for x and y directions:
+
+ Gx = [ x (rj x x) -x -(ri x x)]
+ Gy = [ y (rj x y) -y -(ri x y)]
+
+ */
+
+ vec2.rotate(worldPivotA, pivotA, bodyA.angle);
+ vec2.rotate(worldPivotB, pivotB, bodyB.angle);
+
+ // todo: these are a bit sparse. We could save some computations on making custom eq.computeGW functions, etc
+
+ x.G[0] = -1;
+ x.G[1] = 0;
+ x.G[2] = -vec2.crossLength(worldPivotA,xAxis);
+ x.G[3] = 1;
+ x.G[4] = 0;
+ x.G[5] = vec2.crossLength(worldPivotB,xAxis);
+
+ y.G[0] = 0;
+ y.G[1] = -1;
+ y.G[2] = -vec2.crossLength(worldPivotA,yAxis);
+ y.G[3] = 0;
+ y.G[4] = 1;
+ y.G[5] = vec2.crossLength(worldPivotB,yAxis);
+};
+
+/**
+ * Enable the rotational motor
+ * @method enableMotor
+ */
+RevoluteConstraint.prototype.enableMotor = function(){
+ if(this.motorEnabled) return;
+ this.equations.push(this.motorEquation);
+ this.motorEnabled = true;
+};
+
+/**
+ * Disable the rotational motor
+ * @method disableMotor
+ */
+RevoluteConstraint.prototype.disableMotor = function(){
+ if(!this.motorEnabled) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.equations.splice(i,1);
+ this.motorEnabled = false;
+};
+
+/**
+ * Check if the motor is enabled.
+ * @method motorIsEnabled
+ * @return {Boolean}
+ */
+RevoluteConstraint.prototype.motorIsEnabled = function(){
+ return !!this.motorEnabled;
+};
+
+/**
+ * Set the speed of the rotational constraint motor
+ * @method setMotorSpeed
+ * @param {Number} speed
+ */
+RevoluteConstraint.prototype.setMotorSpeed = function(speed){
+ if(!this.motorEnabled) return;
+ var i = this.equations.indexOf(this.motorEquation);
+ this.equations[i].relativeVelocity = speed;
+};
+
+/**
+ * Get the speed of the rotational constraint motor
+ * @method getMotorSpeed
+ * @return {Number} The current speed, or false if the motor is not enabled.
+ */
+RevoluteConstraint.prototype.getMotorSpeed = function(){
+ if(!this.motorEnabled) return false;
+ return this.motorEquation.relativeVelocity;
+};
+
+},{"../equations/Equation":24,"../equations/RotationalLockEquation":26,"../equations/RotationalVelocityEquation":27,"../math/vec2":33,"./Constraint":16}],22:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2');
+
+module.exports = AngleLockEquation;
+
+/**
+ * Locks the relative angle between two bodies. The constraint tries to keep the dot product between two vectors, local in each body, to zero. The local angle in body i is a parameter.
+ *
+ * @class AngleLockEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ * @param {Object} options
+ * @param {Number} options.angle Angle to add to the local vector in body i.
+ * @param {Number} options.ratio Gear ratio
+ */
+function AngleLockEquation(bi,bj,options){
+ options = options || {};
+ Equation.call(this,bi,bj,-Number.MAX_VALUE,Number.MAX_VALUE);
+ this.angle = options.angle || 0;
+ this.ratio = typeof(options.ratio)=="number" ? options.ratio : 1;
+ this.setRatio(this.ratio);
+};
+AngleLockEquation.prototype = new Equation();
+AngleLockEquation.prototype.constructor = AngleLockEquation;
+
+AngleLockEquation.prototype.computeGq = function(){
+ return this.ratio*this.bi.angle - this.bj.angle + this.angle;
+};
+
+AngleLockEquation.prototype.setRatio = function(ratio){
+ var G = this.G;
+ G[2] = ratio;
+ G[5] = -1;
+ this.ratio = ratio;
+};
+
+},{"../math/vec2":33,"./Equation":24}],23:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2'),
+ mat2 = require('../math/mat2');
+
+module.exports = ContactEquation;
+
+/**
+ * Non-penetration constraint equation. Tries to make the ri and rj vectors the same point.
+ *
+ * @class ContactEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ */
+function ContactEquation(bi,bj){
+ Equation.call(this,bi,bj,0,Number.MAX_VALUE);
+
+ /**
+ * Vector from body i center of mass to the contact point.
+ * @property ri
+ * @type {Array}
+ */
+ this.ri = vec2.create();
+ this.penetrationVec = vec2.create();
+
+ /**
+ * Vector from body j center of mass to the contact point.
+ * @property rj
+ * @type {Array}
+ */
+ this.rj = vec2.create();
+
+ /**
+ * The normal vector, pointing out of body i
+ * @property ni
+ * @type {Array}
+ */
+ this.ni = vec2.create();
+
+ /**
+ * The restitution to use. 0=no bounciness, 1=max bounciness.
+ * @property restitution
+ * @type {Number}
+ */
+ this.restitution = 0;
+
+ /**
+ * Set to true if this is the first impact between the bodies (not persistant contact).
+ * @property firstImpact
+ * @type {Boolean}
+ */
+ this.firstImpact = false;
+
+ /**
+ * The shape in body i that triggered this contact.
+ * @property shapeA
+ * @type {Shape}
+ */
+ this.shapeA = null;
+
+ /**
+ * The shape in body j that triggered this contact.
+ * @property shapeB
+ * @type {Shape}
+ */
+ this.shapeB = null;
+};
+ContactEquation.prototype = new Equation();
+ContactEquation.prototype.constructor = ContactEquation;
+ContactEquation.prototype.computeB = function(a,b,h){
+ var bi = this.bi,
+ bj = this.bj,
+ ri = this.ri,
+ rj = this.rj,
+ xi = bi.position,
+ xj = bj.position;
+
+ var penetrationVec = this.penetrationVec,
+ n = this.ni,
+ G = this.G;
+
+ // Caluclate cross products
+ var rixn = vec2.crossLength(ri,n),
+ rjxn = vec2.crossLength(rj,n);
+
+ // G = [-n -rixn n rjxn]
+ G[0] = -n[0];
+ G[1] = -n[1];
+ G[2] = -rixn;
+ G[3] = n[0];
+ G[4] = n[1];
+ G[5] = rjxn;
+
+ // Calculate q = xj+rj -(xi+ri) i.e. the penetration vector
+ vec2.add(penetrationVec,xj,rj);
+ vec2.sub(penetrationVec,penetrationVec,xi);
+ vec2.sub(penetrationVec,penetrationVec,ri);
+
+ // Compute iteration
+ var GW, Gq;
+ if(this.firstImpact && this.restitution !== 0){
+ Gq = 0;
+ GW = (1/b)*(1+this.restitution) * this.computeGW();
+ } else {
+ GW = this.computeGW();
+ Gq = vec2.dot(n,penetrationVec);
+ }
+
+ var GiMf = this.computeGiMf();
+ var B = - Gq * a - GW * b - h*GiMf;
+
+ return B;
+};
+
+},{"../math/mat2":31,"../math/vec2":33,"./Equation":24}],24:[function(require,module,exports){
+module.exports = Equation;
+
+var vec2 = require('../math/vec2'),
+ mat2 = require('../math/mat2'),
+ Utils = require('../utils/Utils');
+
+/**
+ * Base class for constraint equations.
+ * @class Equation
+ * @constructor
+ * @param {Body} bi First body participating in the equation
+ * @param {Body} bj Second body participating in the equation
+ * @param {number} minForce Minimum force to apply. Default: -1e6
+ * @param {number} maxForce Maximum force to apply. Default: 1e6
+ */
+function Equation(bi,bj,minForce,maxForce){
+
+ /**
+ * Minimum force to apply when solving
+ * @property minForce
+ * @type {Number}
+ */
+ this.minForce = typeof(minForce)=="undefined" ? -1e6 : minForce;
+
+ /**
+ * Max force to apply when solving
+ * @property maxForce
+ * @type {Number}
+ */
+ this.maxForce = typeof(maxForce)=="undefined" ? 1e6 : maxForce;
+
+ /**
+ * First body participating in the constraint
+ * @property bi
+ * @type {Body}
+ */
+ this.bi = bi;
+
+ /**
+ * Second body participating in the constraint
+ * @property bj
+ * @type {Body}
+ */
+ this.bj = bj;
+
+ /**
+ * The stiffness of this equation. Typically chosen to a large number (~1e7), but can be chosen somewhat freely to get a stable simulation.
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = 1e6;
+
+ /**
+ * The number of time steps needed to stabilize the constraint equation. Typically between 3 and 5 time steps.
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = 4;
+
+ /**
+ * The Jacobian entry of this equation. 6 numbers, 3 per body (x,y,angle).
+ * @property G
+ * @type {Array}
+ */
+ this.G = new Utils.ARRAY_TYPE(6);
+
+ // Constraint frames for body i and j
+ /*
+ this.xi = vec2.create();
+ this.xj = vec2.create();
+ this.ai = 0;
+ this.aj = 0;
+ */
+ this.offset = 0;
+
+ this.a = 0;
+ this.b = 0;
+ this.eps = 0;
+ this.h = 0;
+ this.updateSpookParams(1/60);
+
+ /**
+ * The resulting constraint multiplier from the last solve. This is mostly equivalent to the force produced by the constraint.
+ * @property multiplier
+ * @type {Number}
+ */
+ this.multiplier = 0;
+
+ /**
+ * Relative velocity.
+ * @property {Number} relativeVelocity
+ */
+ this.relativeVelocity = 0;
+
+ /**
+ * Whether this equation is enabled or not. If true, it will be added to the solver.
+ * @property {Boolean} enabled
+ */
+ this.enabled = true;
+};
+Equation.prototype.constructor = Equation;
+
+/**
+ * Update SPOOK parameters .a, .b and .eps according to the given time step. See equations 9, 10 and 11 in the SPOOK notes.
+ * @method updateSpookParams
+ * @param {number} timeStep
+ */
+Equation.prototype.updateSpookParams = function(timeStep){
+ var k = this.stiffness,
+ d = this.relaxation,
+ h = timeStep;
+ this.a = 4.0 / (h * (1 + 4 * d));
+ this.b = (4.0 * d) / (1 + 4 * d);
+ this.eps = 4.0 / (h * h * k * (1 + 4 * d));
+ this.h = timeStep;
+};
+
+function Gmult(G,vi,wi,vj,wj){
+ return G[0] * vi[0] +
+ G[1] * vi[1] +
+ G[2] * wi +
+ G[3] * vj[0] +
+ G[4] * vj[1] +
+ G[5] * wj;
+}
+
+/**
+ * Computes the RHS of the SPOOK equation
+ * @method computeB
+ * @return {Number}
+ */
+Equation.prototype.computeB = function(a,b,h){
+ var GW = this.computeGW();
+ var Gq = this.computeGq();
+ var GiMf = this.computeGiMf();
+ return - Gq * a - GW * b - GiMf*h;
+};
+
+/**
+ * Computes G*q, where q are the generalized body coordinates
+ * @method computeGq
+ * @return {Number}
+ */
+var qi = vec2.create(),
+ qj = vec2.create();
+Equation.prototype.computeGq = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ xi = bi.position,
+ xj = bj.position,
+ ai = bi.angle,
+ aj = bj.angle;
+
+ // Transform to the given body frames
+ /*
+ vec2.rotate(qi,this.xi,ai);
+ vec2.rotate(qj,this.xj,aj);
+ vec2.add(qi,qi,xi);
+ vec2.add(qj,qj,xj);
+ */
+
+ return Gmult(G, qi, ai, qj, aj) + this.offset;
+};
+
+var tmp_i = vec2.create(),
+ tmp_j = vec2.create();
+Equation.prototype.transformedGmult = function(G,vi,wi,vj,wj){
+ // Transform velocity to the given body frames
+ // v_p = v + w x r
+ /*
+ vec2.rotate(tmp_i,this.xi,Math.PI / 2 + this.bi.angle); // Get r, and rotate 90 degrees. We get the "x r" part
+ vec2.rotate(tmp_j,this.xj,Math.PI / 2 + this.bj.angle);
+ vec2.scale(tmp_i,tmp_i,wi); // Temp vectors are now (w x r)
+ vec2.scale(tmp_j,tmp_j,wj);
+ vec2.add(tmp_i,tmp_i,vi);
+ vec2.add(tmp_j,tmp_j,vj);
+ */
+
+ // Note: angular velocity is same
+ return Gmult(G,vi,wi,vj,wj);
+};
+
+/**
+ * Computes G*W, where W are the body velocities
+ * @method computeGW
+ * @return {Number}
+ */
+Equation.prototype.computeGW = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ vi = bi.velocity,
+ vj = bj.velocity,
+ wi = bi.angularVelocity,
+ wj = bj.angularVelocity;
+ return this.transformedGmult(G,vi,wi,vj,wj) + this.relativeVelocity;
+};
+
+/**
+ * Computes G*Wlambda, where W are the body velocities
+ * @method computeGWlambda
+ * @return {Number}
+ */
+Equation.prototype.computeGWlambda = function(){
+ var G = this.G,
+ bi = this.bi,
+ bj = this.bj,
+ vi = bi.vlambda,
+ vj = bj.vlambda,
+ wi = bi.wlambda,
+ wj = bj.wlambda;
+ return Gmult(G,vi,wi,vj,wj);
+};
+
+/**
+ * Computes G*inv(M)*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.
+ * @method computeGiMf
+ * @return {Number}
+ */
+var iMfi = vec2.create(),
+ iMfj = vec2.create();
+Equation.prototype.computeGiMf = function(){
+ var bi = this.bi,
+ bj = this.bj,
+ fi = bi.force,
+ ti = bi.angularForce,
+ fj = bj.force,
+ tj = bj.angularForce,
+ invMassi = bi.invMass,
+ invMassj = bj.invMass,
+ invIi = bi.invInertia,
+ invIj = bj.invInertia,
+ G = this.G;
+
+ vec2.scale(iMfi, fi,invMassi);
+ vec2.scale(iMfj, fj,invMassj);
+
+ return this.transformedGmult(G,iMfi,ti*invIi,iMfj,tj*invIj);
+};
+
+/**
+ * Computes G*inv(M)*G'
+ * @method computeGiMGt
+ * @return {Number}
+ */
+Equation.prototype.computeGiMGt = function(){
+ var bi = this.bi,
+ bj = this.bj,
+ invMassi = bi.invMass,
+ invMassj = bj.invMass,
+ invIi = bi.invInertia,
+ invIj = bj.invInertia,
+ G = this.G;
+
+ return G[0] * G[0] * invMassi +
+ G[1] * G[1] * invMassi +
+ G[2] * G[2] * invIi +
+ G[3] * G[3] * invMassj +
+ G[4] * G[4] * invMassj +
+ G[5] * G[5] * invIj;
+};
+
+var addToWlambda_temp = vec2.create(),
+ addToWlambda_Gi = vec2.create(),
+ addToWlambda_Gj = vec2.create(),
+ addToWlambda_ri = vec2.create(),
+ addToWlambda_rj = vec2.create(),
+ addToWlambda_Mdiag = vec2.create();
+var tmpMat1 = mat2.create(),
+ tmpMat2 = mat2.create();
+
+/**
+ * Add constraint velocity to the bodies.
+ * @method addToWlambda
+ * @param {Number} deltalambda
+ */
+Equation.prototype.addToWlambda = function(deltalambda){
+ var bi = this.bi,
+ bj = this.bj,
+ temp = addToWlambda_temp,
+ imMat1 = tmpMat1,
+ imMat2 = tmpMat2,
+ Gi = addToWlambda_Gi,
+ Gj = addToWlambda_Gj,
+ ri = addToWlambda_ri,
+ rj = addToWlambda_rj,
+ Mdiag = addToWlambda_Mdiag,
+ G = this.G;
+
+ Gi[0] = G[0];
+ Gi[1] = G[1];
+ Gj[0] = G[3];
+ Gj[1] = G[4];
+
+ /*
+ mat2.identity(imMat1);
+ mat2.identity(imMat2);
+ imMat1[0] = imMat1[3] = bi.invMass;
+ imMat2[0] = imMat2[3] = bj.invMass;
+ */
+
+
+ /*
+ vec2.rotate(ri,this.xi,bi.angle);
+ vec2.rotate(rj,this.xj,bj.angle);
+ */
+
+ // Add to linear velocity
+ // v_lambda += inv(M) * delta_lamba * G
+ //vec2.set(Mdiag,bi.invMass,bi.invMass);
+ //vec2.scale(temp,vec2.transformMat2(temp,Gi,imMat1),deltalambda);
+ vec2.scale(temp,Gi,bi.invMass*deltalambda);
+ vec2.add( bi.vlambda, bi.vlambda, temp);
+ // This impulse is in the offset frame
+ // Also add contribution to angular
+ //bi.wlambda -= vec2.crossLength(temp,ri);
+
+ //vec2.set(Mdiag,bj.invMass,bj.invMass);
+ //vec2.scale(temp,vec2.transformMat2(temp,Gj,imMat2),deltalambda);
+ vec2.scale(temp,Gj,bj.invMass*deltalambda);
+ vec2.add( bj.vlambda, bj.vlambda, temp);
+ //bj.wlambda -= vec2.crossLength(temp,rj);
+
+ // Add to angular velocity
+ bi.wlambda += bi.invInertia * G[2] * deltalambda;
+ bj.wlambda += bj.invInertia * G[5] * deltalambda;
+};
+
+function massMatVecMultiply(out, m, v) {
+ out[0] = v[0] * m;
+ out[1] = v[1] * m;
+ return out;
+}
+
+/**
+ * Compute the denominator part of the SPOOK equation: C = G*inv(M)*G' + eps
+ * @method computeInvC
+ * @param {Number} eps
+ * @return {Number}
+ */
+Equation.prototype.computeInvC = function(eps){
+ return 1.0 / (this.computeGiMGt() + eps);
+};
+
+},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":49}],25:[function(require,module,exports){
+var mat2 = require('../math/mat2')
+, vec2 = require('../math/vec2')
+, Equation = require('./Equation')
+, Utils = require('../utils/Utils')
+
+module.exports = FrictionEquation;
+
+/**
+ * Constrains the slipping in a contact along a tangent
+ *
+ * @class FrictionEquation
+ * @constructor
+ * @param {Body} bi
+ * @param {Body} bj
+ * @param {Number} slipForce
+ * @extends {Equation}
+ */
+function FrictionEquation(bi,bj,slipForce){
+ Equation.call(this,bi,bj,-slipForce,slipForce);
+
+ /**
+ * Relative vector from center of body i to the contact point, in world coords.
+ * @property ri
+ * @type {Float32Array}
+ */
+ this.ri = vec2.create();
+
+ /**
+ * Relative vector from center of body j to the contact point, in world coords.
+ * @property rj
+ * @type {Float32Array}
+ */
+ this.rj = vec2.create();
+
+ /**
+ * Tangent vector that the friction force will act along, in world coords.
+ * @property t
+ * @type {Float32Array}
+ */
+ this.t = vec2.create();
+
+ /**
+ * A ContactEquation connected to this friction. The contact equation can be used to rescale the max force for the friction.
+ * @property contactEquation
+ * @type {ContactEquation}
+ */
+ this.contactEquation = null;
+
+ /**
+ * The shape in body i that triggered this friction.
+ * @property shapeA
+ * @type {Shape}
+ * @todo Needed? The shape can be looked up via contactEquation.shapeA...
+ */
+ this.shapeA = null;
+
+ /**
+ * The shape in body j that triggered this friction.
+ * @property shapeB
+ * @type {Shape}
+ * @todo Needed? The shape can be looked up via contactEquation.shapeB...
+ */
+ this.shapeB = null;
+
+ /**
+ * The friction coefficient to use.
+ * @property frictionCoefficient
+ * @type {Number}
+ */
+ this.frictionCoefficient = 0.3;
+};
+FrictionEquation.prototype = new Equation();
+FrictionEquation.prototype.constructor = FrictionEquation;
+
+/**
+ * Set the slipping condition for the constraint. The friction force cannot be
+ * larger than this value.
+ * @method setSlipForce
+ * @param {Number} slipForce
+ * @deprecated Use .frictionCoefficient instead
+ */
+FrictionEquation.prototype.setSlipForce = function(slipForce){
+ this.maxForce = slipForce;
+ this.minForce = -slipForce;
+};
+
+FrictionEquation.prototype.computeB = function(a,b,h){
+ var bi = this.bi,
+ bj = this.bj,
+ ri = this.ri,
+ rj = this.rj,
+ t = this.t,
+ G = this.G;
+
+ // G = [-t -rixt t rjxt]
+ // And remember, this is a pure velocity constraint, g is always zero!
+ G[0] = -t[0];
+ G[1] = -t[1];
+ G[2] = -vec2.crossLength(ri,t);
+ G[3] = t[0];
+ G[4] = t[1];
+ G[5] = vec2.crossLength(rj,t);
+
+ var GW = this.computeGW(),
+ GiMf = this.computeGiMf();
+
+ var B = /* - g * a */ - GW * b - h*GiMf;
+
+ return B;
+};
+
+},{"../math/mat2":31,"../math/vec2":33,"../utils/Utils":49,"./Equation":24}],26:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2');
+
+module.exports = RotationalLockEquation;
+
+/**
+ * Locks the relative angle between two bodies. The constraint tries to keep the dot product between two vectors, local in each body, to zero. The local angle in body i is a parameter.
+ *
+ * @class RotationalLockEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ * @param {Object} options
+ * @param {Number} options.angle Angle to add to the local vector in body i.
+ */
+function RotationalLockEquation(bi,bj,options){
+ options = options || {};
+ Equation.call(this,bi,bj,-Number.MAX_VALUE,Number.MAX_VALUE);
+ this.angle = options.angle || 0;
+
+ var G = this.G;
+ G[2] = 1;
+ G[5] = -1;
+};
+RotationalLockEquation.prototype = new Equation();
+RotationalLockEquation.prototype.constructor = RotationalLockEquation;
+
+var worldVectorA = vec2.create(),
+ worldVectorB = vec2.create(),
+ xAxis = vec2.fromValues(1,0),
+ yAxis = vec2.fromValues(0,1);
+RotationalLockEquation.prototype.computeGq = function(){
+ vec2.rotate(worldVectorA,xAxis,this.bi.angle+this.angle);
+ vec2.rotate(worldVectorB,yAxis,this.bj.angle);
+ return vec2.dot(worldVectorA,worldVectorB);
+};
+
+},{"../math/vec2":33,"./Equation":24}],27:[function(require,module,exports){
+var Equation = require("./Equation"),
+ vec2 = require('../math/vec2');
+
+module.exports = RotationalVelocityEquation;
+
+/**
+ * Syncs rotational velocity of two bodies, or sets a relative velocity (motor).
+ *
+ * @class RotationalVelocityEquation
+ * @constructor
+ * @extends Equation
+ * @param {Body} bi
+ * @param {Body} bj
+ */
+function RotationalVelocityEquation(bi,bj){
+ Equation.call(this,bi,bj,-Number.MAX_VALUE,Number.MAX_VALUE);
+ this.relativeVelocity = 1;
+ this.ratio = 1;
+};
+RotationalVelocityEquation.prototype = new Equation();
+RotationalVelocityEquation.prototype.constructor = RotationalVelocityEquation;
+RotationalVelocityEquation.prototype.computeB = function(a,b,h){
+ var G = this.G;
+ G[2] = -1;
+ G[5] = this.ratio;
+
+ var GiMf = this.computeGiMf();
+ var GW = this.computeGW();
+ var B = - GW * b - h*GiMf;
+
+ return B;
+};
+
+},{"../math/vec2":33,"./Equation":24}],28:[function(require,module,exports){
+/**
+ * Base class for objects that dispatches events.
+ * @class EventEmitter
+ * @constructor
+ */
+var EventEmitter = function () {}
+
+module.exports = EventEmitter;
+
+EventEmitter.prototype = {
+ constructor: EventEmitter,
+
+ /**
+ * Add an event listener
+ * @method on
+ * @param {String} type
+ * @param {Function} listener
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ on: function ( type, listener, context ) {
+ listener.context = context || this;
+ if ( this._listeners === undefined ) this._listeners = {};
+ var listeners = this._listeners;
+ if ( listeners[ type ] === undefined ) {
+ listeners[ type ] = [];
+ }
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+ listeners[ type ].push( listener );
+ }
+ return this;
+ },
+
+ /**
+ * Check if an event listener is added
+ * @method has
+ * @param {String} type
+ * @param {Function} listener
+ * @return {Boolean}
+ */
+ has: function ( type, listener ) {
+ if ( this._listeners === undefined ) return false;
+ var listeners = this._listeners;
+ if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Remove an event listener
+ * @method off
+ * @param {String} type
+ * @param {Function} listener
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ off: function ( type, listener ) {
+ if ( this._listeners === undefined ) return this;
+ var listeners = this._listeners;
+ var index = listeners[ type ].indexOf( listener );
+ if ( index !== - 1 ) {
+ listeners[ type ].splice( index, 1 );
+ }
+ return this;
+ },
+
+ /**
+ * Emit an event.
+ * @method emit
+ * @param {Object} event
+ * @param {String} event.type
+ * @return {EventEmitter} The self object, for chainability.
+ */
+ emit: function ( event ) {
+ if ( this._listeners === undefined ) return this;
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+ if ( listenerArray !== undefined ) {
+ event.target = this;
+ for ( var i = 0, l = listenerArray.length; i < l; i ++ ) {
+ var listener = listenerArray[ i ];
+ listener.call( listener.context, event );
+ }
+ }
+ return this;
+ }
+};
+
+},{}],29:[function(require,module,exports){
+module.exports = ContactMaterial;
+
+var idCounter = 0;
+
+/**
+ * Defines a physics material.
+ * @class ContactMaterial
+ * @constructor
+ * @param {Material} materialA
+ * @param {Material} materialB
+ * @param {Object} [options]
+ * @param {Number} options.friction
+ * @param {Number} options.restitution
+ * @author schteppe
+ */
+function ContactMaterial(materialA, materialB, options){
+ options = options || {};
+
+ /**
+ * The contact material identifier
+ * @property id
+ * @type {Number}
+ */
+ this.id = idCounter++;
+
+ /**
+ * First material participating in the contact material
+ * @property materialA
+ * @type {Material}
+ */
+ this.materialA = materialA;
+
+ /**
+ * Second material participating in the contact material
+ * @property materialB
+ * @type {Material}
+ */
+ this.materialB = materialB;
+
+ /**
+ * Friction to use in the contact of these two materials
+ * @property friction
+ * @type {Number}
+ */
+ this.friction = typeof(options.friction) !== "undefined" ? Number(options.friction) : 0.3;
+
+ /**
+ * Restitution to use in the contact of these two materials
+ * @property restitution
+ * @type {Number}
+ */
+ this.restitution = typeof(options.restitution) !== "undefined" ? Number(options.restitution) : 0.0;
+
+ /**
+ * Stiffness of the resulting ContactEquation that this ContactMaterial generate
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = typeof(options.stiffness) !== "undefined" ? Number(options.stiffness) : 1e7;
+
+ /**
+ * Relaxation of the resulting ContactEquation that this ContactMaterial generate
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = typeof(options.relaxation) !== "undefined" ? Number(options.relaxation) : 3;
+
+ /**
+ * Stiffness of the resulting FrictionEquation that this ContactMaterial generate
+ * @property frictionStiffness
+ * @type {Number}
+ */
+ this.frictionStiffness = typeof(options.frictionStiffness) !== "undefined" ? Number(options.frictionStiffness) : 1e7;
+
+ /**
+ * Relaxation of the resulting FrictionEquation that this ContactMaterial generate
+ * @property frictionRelaxation
+ * @type {Number}
+ */
+ this.frictionRelaxation = typeof(options.frictionRelaxation) !== "undefined" ? Number(options.frictionRelaxation) : 3;
+
+ /**
+ * Will add surface velocity to this material. If bodyA rests on top if bodyB, and the surface velocity is positive, bodyA will slide to the right.
+ * @property {Number} surfaceVelocity
+ */
+ this.surfaceVelocity = typeof(options.surfaceVelocity) !== "undefined" ? Number(options.surfaceVelocity) : 0
+};
+
+},{}],30:[function(require,module,exports){
+module.exports = Material;
+
+var idCounter = 0;
+
+/**
+ * Defines a physics material.
+ * @class Material
+ * @constructor
+ * @param string name
+ * @author schteppe
+ */
+function Material(){
+ /**
+ * The material identifier
+ * @property id
+ * @type {Number}
+ */
+ this.id = idCounter++;
+};
+
+},{}],31:[function(require,module,exports){
+/**
+ * The mat2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
+ * @class mat2
+ */
+
+// Only import mat2 from gl-matrix and skip the rest
+var mat2 = require('../../node_modules/gl-matrix/src/gl-matrix/mat2').mat2;
+
+// Export everything
+module.exports = mat2;
+
+},{"../../node_modules/gl-matrix/src/gl-matrix/mat2":1}],32:[function(require,module,exports){
+
+ /*
+ PolyK library
+ url: http://polyk.ivank.net
+ Released under MIT licence.
+
+ Copyright (c) 2012 Ivan Kuckir
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ var PolyK = {};
+
+ /*
+ Is Polygon self-intersecting?
+
+ O(n^2)
+ */
+ /*
+ PolyK.IsSimple = function(p)
+ {
+ var n = p.length>>1;
+ if(n<4) return true;
+ var a1 = new PolyK._P(), a2 = new PolyK._P();
+ var b1 = new PolyK._P(), b2 = new PolyK._P();
+ var c = new PolyK._P();
+
+ for(var i=0; i>1;
+ if(n<3) return [];
+ var tgs = [];
+ var avl = [];
+ for(var i=0; i 3)
+ {
+ var i0 = avl[(i+0)%al];
+ var i1 = avl[(i+1)%al];
+ var i2 = avl[(i+2)%al];
+
+ var ax = p[2*i0], ay = p[2*i0+1];
+ var bx = p[2*i1], by = p[2*i1+1];
+ var cx = p[2*i2], cy = p[2*i2+1];
+
+ var earFound = false;
+ if(PolyK._convex(ax, ay, bx, by, cx, cy))
+ {
+ earFound = true;
+ for(var j=0; j 3*al) break; // no convex angles :(
+ }
+ tgs.push(avl[0], avl[1], avl[2]);
+ return tgs;
+ }
+ /*
+ PolyK.ContainsPoint = function(p, px, py)
+ {
+ var n = p.length>>1;
+ var ax, ay, bx = p[2*n-2]-px, by = p[2*n-1]-py;
+ var depth = 0;
+ for(var i=0; i=0 && by>=0) continue; // both "up" or both "donw"
+ if(ax< 0 && bx< 0) continue;
+
+ var lx = ax + (bx-ax)*(-ay)/(by-ay);
+ if(lx>0) depth++;
+ }
+ return (depth & 1) == 1;
+ }
+
+ PolyK.Slice = function(p, ax, ay, bx, by)
+ {
+ if(PolyK.ContainsPoint(p, ax, ay) || PolyK.ContainsPoint(p, bx, by)) return [p.slice(0)];
+
+ var a = new PolyK._P(ax, ay);
+ var b = new PolyK._P(bx, by);
+ var iscs = []; // intersections
+ var ps = []; // points
+ for(var i=0; i 0)
+ {
+ var n = ps.length;
+ var i0 = iscs[0];
+ var i1 = iscs[1];
+ var ind0 = ps.indexOf(i0);
+ var ind1 = ps.indexOf(i1);
+ var solved = false;
+
+ if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
+ else
+ {
+ i0 = iscs[1];
+ i1 = iscs[0];
+ ind0 = ps.indexOf(i0);
+ ind1 = ps.indexOf(i1);
+ if(PolyK._firstWithFlag(ps, ind0) == ind1) solved = true;
+ }
+ if(solved)
+ {
+ dir--;
+ var pgn = PolyK._getPoints(ps, ind0, ind1);
+ pgs.push(pgn);
+ ps = PolyK._getPoints(ps, ind1, ind0);
+ i0.flag = i1.flag = false;
+ iscs.splice(0,2);
+ if(iscs.length == 0) pgs.push(ps);
+ }
+ else { dir++; iscs.reverse(); }
+ if(dir>1) break;
+ }
+ var result = [];
+ for(var i=0; i>1, isc);
+ }
+ b1.x = b2.x; b1.y = b2.y;
+ b2.x = p[0]; b2.y = p[1];
+ PolyK._pointLineDist(a1, b1, b2, l>>1, isc);
+
+ var idst = 1/isc.dist;
+ isc.norm.x = (x-isc.point.x)*idst;
+ isc.norm.y = (y-isc.point.y)*idst;
+ return isc;
+ }
+
+ PolyK._pointLineDist = function(p, a, b, edge, isc)
+ {
+ var x = p.x, y = p.y, x1 = a.x, y1 = a.y, x2 = b.x, y2 = b.y;
+
+ var A = x - x1;
+ var B = y - y1;
+ var C = x2 - x1;
+ var D = y2 - y1;
+
+ var dot = A * C + B * D;
+ var len_sq = C * C + D * D;
+ var param = dot / len_sq;
+
+ var xx, yy;
+
+ if (param < 0 || (x1 == x2 && y1 == y2)) {
+ xx = x1;
+ yy = y1;
+ }
+ else if (param > 1) {
+ xx = x2;
+ yy = y2;
+ }
+ else {
+ xx = x1 + param * C;
+ yy = y1 + param * D;
+ }
+
+ var dx = x - xx;
+ var dy = y - yy;
+ var dst = Math.sqrt(dx * dx + dy * dy);
+ if(dst= 0) && (v >= 0) && (u + v < 1);
+ }
+ /*
+ PolyK._RayLineIntersection = function(a1, a2, b1, b2, c)
+ {
+ var dax = (a1.x-a2.x), dbx = (b1.x-b2.x);
+ var day = (a1.y-a2.y), dby = (b1.y-b2.y);
+
+ var Den = dax*dby - day*dbx;
+ if (Den == 0) return null; // parallel
+
+ var A = (a1.x * a2.y - a1.y * a2.x);
+ var B = (b1.x * b2.y - b1.y * b2.x);
+
+ var I = c;
+ var iDen = 1/Den;
+ I.x = ( A*dbx - dax*B ) * iDen;
+ I.y = ( A*dby - day*B ) * iDen;
+
+ if(!PolyK._InRect(I, b1, b2)) return null;
+ if((day>0 && I.y>a1.y) || (day<0 && I.y0 && I.x>a1.x) || (dax<0 && I.x=Math.min(b.y, c.y) && a.y<=Math.max(b.y, c.y));
+ if (b.y == c.y) return (a.x>=Math.min(b.x, c.x) && a.x<=Math.max(b.x, c.x));
+
+ if(a.x >= Math.min(b.x, c.x) && a.x <= Math.max(b.x, c.x)
+ && a.y >= Math.min(b.y, c.y) && a.y <= Math.max(b.y, c.y))
+ return true;
+ return false;
+ }
+ */
+ PolyK._convex = function(ax, ay, bx, by, cx, cy)
+ {
+ return (ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0;
+ }
+ /*
+ PolyK._P = function(x,y)
+ {
+ this.x = x;
+ this.y = y;
+ this.flag = false;
+ }
+ PolyK._P.prototype.toString = function()
+ {
+ return "Point ["+this.x+", "+this.y+"]";
+ }
+ PolyK._P.dist = function(a,b)
+ {
+ var dx = b.x-a.x;
+ var dy = b.y-a.y;
+ return Math.sqrt(dx*dx + dy*dy);
+ }
+
+ PolyK._tp = [];
+ for(var i=0; i<10; i++) PolyK._tp.push(new PolyK._P(0,0));
+ */
+
+module.exports = PolyK;
+
+},{}],33:[function(require,module,exports){
+/**
+ * The vec2 object from glMatrix, extended with the functions documented here. See http://glmatrix.net for full doc.
+ * @class vec2
+ */
+
+// Only import vec2 from gl-matrix and skip the rest
+var vec2 = require('../../node_modules/gl-matrix/src/gl-matrix/vec2').vec2;
+
+// Now add some extensions
+
+/**
+ * Get the vector x component
+ * @method getX
+ * @static
+ * @param {Float32Array} a
+ * @return {Number}
+ */
+vec2.getX = function(a){
+ return a[0];
+};
+
+/**
+ * Get the vector y component
+ * @method getY
+ * @static
+ * @param {Float32Array} a
+ * @return {Number}
+ */
+vec2.getY = function(a){
+ return a[1];
+};
+
+/**
+ * Make a cross product and only return the z component
+ * @method crossLength
+ * @static
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @return {Number}
+ */
+vec2.crossLength = function(a,b){
+ return a[0] * b[1] - a[1] * b[0];
+};
+
+/**
+ * Cross product between a vector and the Z component of a vector
+ * @method crossVZ
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} vec
+ * @param {Number} zcomp
+ * @return {Number}
+ */
+vec2.crossVZ = function(out, vec, zcomp){
+ vec2.rotate(out,vec,-Math.PI/2);// Rotate according to the right hand rule
+ vec2.scale(out,out,zcomp); // Scale with z
+ return out;
+};
+
+/**
+ * Cross product between a vector and the Z component of a vector
+ * @method crossZV
+ * @static
+ * @param {Float32Array} out
+ * @param {Number} zcomp
+ * @param {Float32Array} vec
+ * @return {Number}
+ */
+vec2.crossZV = function(out, zcomp, vec){
+ vec2.rotate(out,vec,Math.PI/2); // Rotate according to the right hand rule
+ vec2.scale(out,out,zcomp); // Scale with z
+ return out;
+};
+
+/**
+ * Rotate a vector by an angle
+ * @method rotate
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} a
+ * @param {Number} angle
+ */
+vec2.rotate = function(out,a,angle){
+ var c = Math.cos(angle),
+ s = Math.sin(angle),
+ x = a[0],
+ y = a[1];
+ out[0] = c*x -s*y;
+ out[1] = s*x +c*y;
+};
+
+vec2.toLocalFrame = function(out, worldPoint, framePosition, frameAngle){
+ vec2.copy(out, worldPoint);
+ vec2.sub(out, out, framePosition);
+ vec2.rotate(out, out, -frameAngle);
+};
+
+vec2.toGlobalFrame = function(out, localPoint, framePosition, frameAngle){
+ vec2.copy(out, localPoint);
+ vec2.rotate(out, out, frameAngle);
+ vec2.add(out, out, framePosition);
+};
+
+/**
+ * Compute centroid of a triangle spanned by vectors a,b,c. See http://easycalculation.com/analytical/learn-centroid.php
+ * @method centroid
+ * @static
+ * @param {Float32Array} out
+ * @param {Float32Array} a
+ * @param {Float32Array} b
+ * @param {Float32Array} c
+ * @return {Float32Array} The out object
+ */
+vec2.centroid = function(out, a, b, c){
+ vec2.add(out, a, b);
+ vec2.add(out, out, c);
+ vec2.scale(out, out, 1/3);
+ return out;
+};
+
+// Export everything
+module.exports = vec2;
+
+},{"../../node_modules/gl-matrix/src/gl-matrix/vec2":2}],34:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, decomp = require('poly-decomp')
+, Convex = require('../shapes/Convex')
+, AABB = require('../collision/AABB')
+, EventEmitter = require('../events/EventEmitter')
+
+module.exports = Body;
+
+/**
+ * A rigid body. Has got a center of mass, position, velocity and a number of
+ * shapes that are used for collisions.
+ *
+ * @class Body
+ * @constructor
+ * @param {Object} [options]
+ * @param {Number} [options.mass=0] A number >= 0. If zero, the .motionState will be set to Body.STATIC.
+ * @param {Float32Array|Array} [options.position]
+ * @param {Float32Array|Array} [options.velocity]
+ * @param {Number} [options.angle=0]
+ * @param {Number} [options.angularVelocity=0]
+ * @param {Float32Array|Array} [options.force]
+ * @param {Number} [options.angularForce=0]
+ * @param {Number} [options.fixedRotation=false]
+ *
+ * @todo Should not take mass as argument to Body, but as density to each Shape
+ */
+function Body(options){
+ options = options || {};
+
+ EventEmitter.call(this);
+
+ /**
+ * The body identifyer
+ * @property id
+ * @type {Number}
+ */
+ this.id = ++Body._idCounter;
+
+ /**
+ * The world that this body is added to. This property is set to NULL if the body is not added to any world.
+ * @property world
+ * @type {World}
+ */
+ this.world = null;
+
+ /**
+ * The shapes of the body. The local transform of the shape in .shapes[i] is
+ * defined by .shapeOffsets[i] and .shapeAngles[i].
+ *
+ * @property shapes
+ * @type {Array}
+ */
+ this.shapes = [];
+
+ /**
+ * The local shape offsets, relative to the body center of mass. This is an
+ * array of Float32Array.
+ * @property shapeOffsets
+ * @type {Array}
+ */
+ this.shapeOffsets = [];
+
+ /**
+ * The body-local shape angle transforms. This is an array of numbers (angles).
+ * @property shapeAngles
+ * @type {Array}
+ */
+ this.shapeAngles = [];
+
+ /**
+ * The mass of the body.
+ * @property mass
+ * @type {number}
+ */
+ this.mass = options.mass || 0;
+
+ /**
+ * The inverse mass of the body.
+ * @property invMass
+ * @type {number}
+ */
+ this.invMass = 0;
+
+ /**
+ * The inertia of the body around the Z axis.
+ * @property inertia
+ * @type {number}
+ */
+ this.inertia = 0;
+
+ /**
+ * The inverse inertia of the body.
+ * @property invInertia
+ * @type {number}
+ */
+ this.invInertia = 0;
+
+ /**
+ * Set to true if you want to fix the rotation of the body.
+ * @property fixedRotation
+ * @type {Boolean}
+ */
+ this.fixedRotation = !!options.fixedRotation || false;
+
+ this.updateMassProperties();
+
+ /**
+ * The position of the body
+ * @property position
+ * @type {Array}
+ */
+ this.position = vec2.fromValues(0,0);
+ if(options.position) vec2.copy(this.position, options.position);
+
+ /**
+ * The interpolated position of the body.
+ * @property interpolatedPosition
+ * @type {Array}
+ */
+ this.interpolatedPosition = vec2.fromValues(0,0);
+
+ /**
+ * The velocity of the body
+ * @property velocity
+ * @type {Float32Array}
+ */
+ this.velocity = vec2.fromValues(0,0);
+ if(options.velocity) vec2.copy(this.velocity, options.velocity);
+
+ /**
+ * Constraint velocity that was added to the body during the last step.
+ * @property vlambda
+ * @type {Float32Array}
+ */
+ this.vlambda = vec2.fromValues(0,0);
+
+ /**
+ * Angular constraint velocity that was added to the body during last step.
+ * @property wlambda
+ * @type {Float32Array}
+ */
+ this.wlambda = 0;
+
+ /**
+ * The angle of the body
+ * @property angle
+ * @type {number}
+ */
+ this.angle = options.angle || 0;
+
+ /**
+ * The angular velocity of the body
+ * @property angularVelocity
+ * @type {number}
+ */
+ this.angularVelocity = options.angularVelocity || 0;
+
+ /**
+ * The force acting on the body
+ * @property force
+ * @type {Float32Array}
+ */
+ this.force = vec2.create();
+ if(options.force) vec2.copy(this.force, options.force);
+
+ /**
+ * The angular force acting on the body
+ * @property angularForce
+ * @type {number}
+ */
+ this.angularForce = options.angularForce || 0;
+
+ /**
+ * The linear damping acting on the body in the velocity direction
+ * @property damping
+ * @type {Number}
+ */
+ this.damping = typeof(options.damping)=="number" ? options.damping : 0.1;
+
+ /**
+ * The angular force acting on the body
+ * @property angularDamping
+ * @type {Number}
+ */
+ this.angularDamping = typeof(options.angularDamping)=="number" ? options.angularDamping : 0.1;
+
+ /**
+ * The type of motion this body has. Should be one of: Body.STATIC (the body
+ * does not move), Body.DYNAMIC (body can move and respond to collisions)
+ * and Body.KINEMATIC (only moves according to its .velocity).
+ *
+ * @property motionState
+ * @type {number}
+ *
+ * @example
+ * // This body will move and interact with other bodies
+ * var dynamicBody = new Body();
+ * dynamicBody.motionState = Body.DYNAMIC;
+ *
+ * @example
+ * // This body will not move at all
+ * var staticBody = new Body();
+ * staticBody.motionState = Body.STATIC;
+ *
+ * @example
+ * // This body will only move if you change its velocity
+ * var kinematicBody = new Body();
+ * kinematicBody.motionState = Body.KINEMATIC;
+ */
+ this.motionState = this.mass == 0 ? Body.STATIC : Body.DYNAMIC;
+
+ /**
+ * Bounding circle radius
+ * @property boundingRadius
+ * @type {Number}
+ */
+ this.boundingRadius = 0;
+
+ /**
+ * Bounding box of this body
+ * @property aabb
+ * @type {AABB}
+ */
+ this.aabb = new AABB();
+
+ /**
+ * Indicates if the AABB needs update. Update it with .updateAABB()
+ * @property aabbNeedsUpdate
+ * @type {Boolean}
+ */
+ this.aabbNeedsUpdate = true;
+
+ /**
+ * If true, the body will automatically fall to sleep.
+ * @property allowSleep
+ * @type {Boolean}
+ */
+ this.allowSleep = false;
+
+ /**
+ * One of Body.AWAKE, Body.SLEEPY, Body.SLEEPING
+ * @property sleepState
+ * @type {Number}
+ */
+ this.sleepState = Body.AWAKE;
+
+ /**
+ * If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy.
+ * @property sleepSpeedLimit
+ * @type {Number}
+ */
+ this.sleepSpeedLimit = 0.1;
+
+ /**
+ * If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping.
+ * @property sleepTimeLimit
+ * @type {Number}
+ */
+ this.sleepTimeLimit = 1;
+
+ this.timeLastSleepy = 0;
+
+ this.concavePath = null;
+
+ this.lastDampingScale = 1;
+ this.lastAngularDampingScale = 1;
+ this.lastDampingTimeStep = -1;
+};
+
+Body.prototype = new EventEmitter();
+
+Body._idCounter = 0;
+
+/**
+ * Set the total density of the body
+ * @method setDensity
+ */
+Body.prototype.setDensity = function(density) {
+ var totalArea = this.getArea();
+ this.mass = totalArea * density;
+ this.updateMassProperties();
+};
+
+/**
+ * Get the total area of all shapes in the body
+ * @method setDensity
+ */
+Body.prototype.getArea = function() {
+ var totalArea = 0;
+ for(var i=0; i radius)
+ radius = offset + r;
+ }
+
+ this.boundingRadius = radius;
+};
+
+/**
+ * Add a shape to the body. You can pass a local transform when adding a shape,
+ * so that the shape gets an offset and angle relative to the body center of mass.
+ * Will automatically update the mass properties and bounding radius.
+ *
+ * @method addShape
+ * @param {Shape} shape
+ * @param {Float32Array|Array} [offset] Local body offset of the shape.
+ * @param {Number} [angle] Local body angle.
+ *
+ * @example
+ * var body = new Body(),
+ * shape = new Circle();
+ *
+ * // Add the shape to the body, positioned in the center
+ * body.addShape(shape);
+ *
+ * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local x-axis.
+ * body.addShape(shape,[1,0]);
+ *
+ * // Add another shape to the body, positioned 1 unit length from the body center of mass along the local y-axis, and rotated 90 degrees CCW.
+ * body.addShape(shape,[0,1],Math.PI/2);
+ */
+Body.prototype.addShape = function(shape,offset,angle){
+ angle = angle || 0.0;
+
+ // Copy the offset vector
+ if(offset){
+ offset = vec2.fromValues(offset[0],offset[1]);
+ } else {
+ offset = vec2.fromValues(0,0);
+ }
+
+ this.shapes .push(shape);
+ this.shapeOffsets.push(offset);
+ this.shapeAngles .push(angle);
+ this.updateMassProperties();
+ this.updateBoundingRadius();
+
+ this.aabbNeedsUpdate = true;
+};
+
+/**
+ * Remove a shape
+ * @method removeShape
+ * @param {Shape} shape
+ * @return {Boolean} True if the shape was found and removed, else false.
+ */
+Body.prototype.removeShape = function(shape){
+ var idx = this.shapes.indexOf(shape);
+
+ if(idx != -1){
+ this.shapes.splice(idx,1);
+ this.shapeOffsets.splice(idx,1);
+ this.shapeAngles.splice(idx,1);
+ this.aabbNeedsUpdate = true;
+ return true;
+ } else
+ return false;
+
+};
+
+/**
+ * Updates .inertia, .invMass, .invInertia for this Body. Should be called when
+ * changing the structure or mass of the Body.
+ *
+ * @method updateMassProperties
+ *
+ * @example
+ * body.mass += 1;
+ * body.updateMassProperties();
+ */
+Body.prototype.updateMassProperties = function(){
+ var shapes = this.shapes,
+ N = shapes.length,
+ m = this.mass / N,
+ I = 0;
+
+ if(!this.fixedRotation){
+ for(var i=0; i 0 ? 1/this.mass : 0;
+ this.invInertia = I>0 ? 1/I : 0;
+};
+
+var Body_applyForce_r = vec2.create();
+
+/**
+ * Apply force to a world point. This could for example be a point on the RigidBody surface. Applying force this way will add to Body.force and Body.angularForce.
+ * @method applyForce
+ * @param {Float32Array} force The force to add.
+ * @param {Float32Array} worldPoint A world point to apply the force on.
+ */
+Body.prototype.applyForce = function(force,worldPoint){
+ // Compute point position relative to the body center
+ var r = Body_applyForce_r;
+ vec2.sub(r,worldPoint,this.position);
+
+ // Add linear force
+ vec2.add(this.force,this.force,force);
+
+ // Compute produced rotational force
+ var rotForce = vec2.crossLength(r,force);
+
+ // Add rotational force
+ this.angularForce += rotForce;
+};
+
+/**
+ * Transform a world point to local body frame.
+ * @method toLocalFrame
+ * @param {Float32Array|Array} out The vector to store the result in
+ * @param {Float32Array|Array} worldPoint The input world vector
+ */
+Body.prototype.toLocalFrame = function(out, worldPoint){
+ vec2.toLocalFrame(out, worldPoint, this.position, this.angle);
+};
+
+/**
+ * Transform a local point to world frame.
+ * @method toWorldFrame
+ * @param {Array} out The vector to store the result in
+ * @param {Array} localPoint The input local vector
+ */
+Body.prototype.toWorldFrame = function(out, localPoint){
+ vec2.toGlobalFrame(out, localPoint, this.position, this.angle);
+};
+
+/**
+ * Reads a polygon shape path, and assembles convex shapes from that and puts them at proper offset points.
+ * @method fromPolygon
+ * @param {Array} path An array of 2d vectors, e.g. [[0,0],[0,1],...] that resembles a concave or convex polygon. The shape must be simple and without holes.
+ * @param {Object} [options]
+ * @param {Boolean} [options.optimalDecomp=false] Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
+ * @param {Boolean} [options.skipSimpleCheck=false] Set to true if you already know that the path is not intersecting itself.
+ * @param {Boolean|Number} [options.removeCollinearPoints=false] Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
+ * @return {Boolean} True on success, else false.
+ */
+Body.prototype.fromPolygon = function(path,options){
+ options = options || {};
+
+ // Remove all shapes
+ for(var i=this.shapes.length; i>=0; --i)
+ this.removeShape(this.shapes[i]);
+
+ var p = new decomp.Polygon();
+ p.vertices = path;
+ // Make it counter-clockwise
+ p.makeCCW();
+
+ if(typeof(options.removeCollinearPoints)=="number"){
+ p.removeCollinearPoints(options.removeCollinearPoints);
+ }
+
+ // Check if any line segment intersects the path itself
+ if(typeof(options.skipSimpleCheck) == "undefined"){
+ if(!p.isSimple()) return false;
+ }
+
+ // Save this path for later
+ this.concavePath = p.vertices.slice(0);
+ for(var i=0; ithis for details.
+ * @method applyDamping
+ * @param {number} dt Current time step
+ */
+Body.prototype.applyDamping = function(dt){
+ if(this.motionState & Body.DYNAMIC){ // Only for dynamic bodies
+
+ // Since Math.pow generates garbage we check if we can reuse the scaling coefficient from last step
+ if(dt != this.lastDampingTimeStep){
+ this.lastDampingScale = Math.pow(1.0 - this.damping,dt);
+ this.lastAngularDampingScale = Math.pow(1.0 - this.angularDamping,dt);
+ this.lastDampingTimeStep = dt;
+ }
+
+ var v = this.velocity;
+ vec2.scale(v,v,this.lastDampingScale);
+ this.angularVelocity *= this.lastAngularDampingScale;
+ }
+};
+
+/**
+ * @method wakeUp
+ * @brief Wake the body up.
+ */
+Body.prototype.wakeUp = function(){
+ var s = this.sleepState;
+ this.sleepState = Body.AWAKE;
+ if(s !== Body.AWAKE){
+ this.emit(Body.wakeUpEvent);
+ }
+};
+
+/**
+ * @method sleep
+ * @brief Force body sleep
+ */
+Body.prototype.sleep = function(){
+ this.sleepState = Body.SLEEPING;
+ this.emit(Body.sleepEvent);
+};
+
+/**
+ * @method sleepTick
+ * @param float time The world time in seconds
+ * @brief Called every timestep to update internal sleep timer and change sleep state if needed.
+ */
+Body.prototype.sleepTick = function(time){
+ if(!this.allowSleep)
+ return;
+
+ var sleepState = this.sleepState,
+ speedSquared = vec2.squaredLength(this.velocity) + Math.pow(this.angularVelocity,2),
+ speedLimitSquared = Math.pow(this.sleepSpeedLimit,2);
+ if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){
+ this.sleepState = Body.SLEEPY; // Sleepy
+ this.timeLastSleepy = time;
+ this.emit(Body.sleepyEvent);
+ } else if(sleepState===Body.SLEEPY && speedSquared > speedLimitSquared){
+ this.wakeUp(); // Wake up
+ } else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){
+ this.sleep();
+ }
+};
+
+Body.sleepyEvent = {
+ type: "sleepy"
+};
+
+Body.sleepEvent = {
+ type: "sleep"
+};
+
+Body.wakeUpEvent = {
+ type: "wakeup"
+};
+
+/**
+ * Dynamic body.
+ * @property DYNAMIC
+ * @type {Number}
+ * @static
+ */
+Body.DYNAMIC = 1;
+
+/**
+ * Static body.
+ * @property STATIC
+ * @type {Number}
+ * @static
+ */
+Body.STATIC = 2;
+
+/**
+ * Kinematic body.
+ * @property KINEMATIC
+ * @type {Number}
+ * @static
+ */
+Body.KINEMATIC = 4;
+
+/**
+ * @property AWAKE
+ * @type {Number}
+ * @static
+ */
+Body.AWAKE = 0;
+
+/**
+ * @property SLEEPY
+ * @type {Number}
+ * @static
+ */
+Body.SLEEPY = 1;
+
+/**
+ * @property SLEEPING
+ * @type {Number}
+ * @static
+ */
+Body.SLEEPING = 2;
+
+
+},{"../collision/AABB":9,"../events/EventEmitter":28,"../math/vec2":33,"../shapes/Convex":39,"poly-decomp":7}],35:[function(require,module,exports){
+var vec2 = require('../math/vec2');
+
+module.exports = Spring;
+
+/**
+ * A spring, connecting two bodies.
+ *
+ * @class Spring
+ * @constructor
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Object} [options]
+ * @param {number} options.restLength A number > 0. Default: 1
+ * @param {number} options.stiffness A number >= 0. Default: 100
+ * @param {number} options.damping A number >= 0. Default: 1
+ * @param {Array} options.worldAnchorA Where to hook the spring to body A, in world coordinates.
+ * @param {Array} options.worldAnchorB
+ * @param {Array} options.localAnchorA Where to hook the spring to body A, in local body coordinates.
+ * @param {Array} options.localAnchorB
+ */
+function Spring(bodyA,bodyB,options){
+ options = options || {};
+
+ /**
+ * Rest length of the spring.
+ * @property restLength
+ * @type {number}
+ */
+ this.restLength = typeof(options.restLength)=="number" ? options.restLength : 1;
+
+ /**
+ * Stiffness of the spring.
+ * @property stiffness
+ * @type {number}
+ */
+ this.stiffness = options.stiffness || 100;
+
+ /**
+ * Damping of the spring.
+ * @property damping
+ * @type {number}
+ */
+ this.damping = options.damping || 1;
+
+ /**
+ * First connected body.
+ * @property bodyA
+ * @type {Body}
+ */
+ this.bodyA = bodyA;
+
+ /**
+ * Second connected body.
+ * @property bodyB
+ * @type {Body}
+ */
+ this.bodyB = bodyB;
+
+ /**
+ * Anchor for bodyA in local bodyA coordinates.
+ * @property localAnchorA
+ * @type {Array}
+ */
+ this.localAnchorA = vec2.fromValues(0,0);
+
+ /**
+ * Anchor for bodyB in local bodyB coordinates.
+ * @property localAnchorB
+ * @type {Array}
+ */
+ this.localAnchorB = vec2.fromValues(0,0);
+
+ if(options.localAnchorA) vec2.copy(this.localAnchorA, options.localAnchorA);
+ if(options.localAnchorB) vec2.copy(this.localAnchorB, options.localAnchorB);
+ if(options.worldAnchorA) this.setWorldAnchorA(options.worldAnchorA);
+ if(options.worldAnchorB) this.setWorldAnchorB(options.worldAnchorB);
+};
+
+/**
+ * Set the anchor point on body A, using world coordinates.
+ * @method setWorldAnchorA
+ * @param {Array} worldAnchorA
+ */
+Spring.prototype.setWorldAnchorA = function(worldAnchorA){
+ this.bodyA.toLocalFrame(this.localAnchorA, worldAnchorA);
+};
+
+/**
+ * Set the anchor point on body B, using world coordinates.
+ * @method setWorldAnchorB
+ * @param {Array} worldAnchorB
+ */
+Spring.prototype.setWorldAnchorB = function(worldAnchorB){
+ this.bodyB.toLocalFrame(this.localAnchorB, worldAnchorB);
+};
+
+/**
+ * Get the anchor point on body A, in world coordinates.
+ * @method getWorldAnchorA
+ * @param {Array} result The vector to store the result in.
+ */
+Spring.prototype.getWorldAnchorA = function(result){
+ this.bodyA.toWorldFrame(result, this.localAnchorA);
+};
+
+/**
+ * Get the anchor point on body B, in world coordinates.
+ * @method getWorldAnchorB
+ * @param {Array} result The vector to store the result in.
+ */
+Spring.prototype.getWorldAnchorB = function(result){
+ this.bodyB.toWorldFrame(result, this.localAnchorB);
+};
+
+var applyForce_r = vec2.create(),
+ applyForce_r_unit = vec2.create(),
+ applyForce_u = vec2.create(),
+ applyForce_f = vec2.create(),
+ applyForce_worldAnchorA = vec2.create(),
+ applyForce_worldAnchorB = vec2.create(),
+ applyForce_ri = vec2.create(),
+ applyForce_rj = vec2.create(),
+ applyForce_tmp = vec2.create();
+
+/**
+ * Apply the spring force to the connected bodies.
+ * @method applyForce
+ */
+Spring.prototype.applyForce = function(){
+ var k = this.stiffness,
+ d = this.damping,
+ l = this.restLength,
+ bodyA = this.bodyA,
+ bodyB = this.bodyB,
+ r = applyForce_r,
+ r_unit = applyForce_r_unit,
+ u = applyForce_u,
+ f = applyForce_f,
+ tmp = applyForce_tmp;
+
+ var worldAnchorA = applyForce_worldAnchorA,
+ worldAnchorB = applyForce_worldAnchorB,
+ ri = applyForce_ri,
+ rj = applyForce_rj;
+
+ // Get world anchors
+ this.getWorldAnchorA(worldAnchorA);
+ this.getWorldAnchorB(worldAnchorB);
+
+ // Get offset points
+ vec2.sub(ri, worldAnchorA, bodyA.position);
+ vec2.sub(rj, worldAnchorB, bodyB.position);
+
+ // Compute distance vector between world anchor points
+ vec2.sub(r, worldAnchorB, worldAnchorA);
+ var rlen = vec2.len(r);
+ vec2.normalize(r_unit,r);
+
+ //console.log(rlen)
+ //console.log("A",vec2.str(worldAnchorA),"B",vec2.str(worldAnchorB))
+
+ // Compute relative velocity of the anchor points, u
+ vec2.sub(u, bodyB.velocity, bodyA.velocity);
+ vec2.crossZV(tmp, bodyB.angularVelocity, rj);
+ vec2.add(u, u, tmp);
+ vec2.crossZV(tmp, bodyA.angularVelocity, ri);
+ vec2.sub(u, u, tmp);
+
+ // F = - k * ( x - L ) - D * ( u )
+ vec2.scale(f, r_unit, -k*(rlen-l) - d*vec2.dot(u,r_unit));
+
+ // Add forces to bodies
+ vec2.sub( bodyA.force, bodyA.force, f);
+ vec2.add( bodyB.force, bodyB.force, f);
+
+ // Angular force
+ var ri_x_f = vec2.crossLength(ri, f);
+ var rj_x_f = vec2.crossLength(rj, f);
+ bodyA.angularForce -= ri_x_f;
+ bodyB.angularForce += rj_x_f;
+};
+
+},{"../math/vec2":33}],36:[function(require,module,exports){
+// Export p2 classes
+module.exports = {
+ AABB : require('./collision/AABB'),
+ AngleLockEquation : require('./equations/AngleLockEquation'),
+ Body : require('./objects/Body'),
+ Broadphase : require('./collision/Broadphase'),
+ Capsule : require('./shapes/Capsule'),
+ Circle : require('./shapes/Circle'),
+ Constraint : require('./constraints/Constraint'),
+ ContactEquation : require('./equations/ContactEquation'),
+ ContactMaterial : require('./material/ContactMaterial'),
+ Convex : require('./shapes/Convex'),
+ DistanceConstraint : require('./constraints/DistanceConstraint'),
+ Equation : require('./equations/Equation'),
+ EventEmitter : require('./events/EventEmitter'),
+ FrictionEquation : require('./equations/FrictionEquation'),
+ GearConstraint : require('./constraints/GearConstraint'),
+ GridBroadphase : require('./collision/GridBroadphase'),
+ GSSolver : require('./solver/GSSolver'),
+ Island : require('./solver/IslandSolver'),
+ IslandSolver : require('./solver/IslandSolver'),
+ Line : require('./shapes/Line'),
+ LockConstraint : require('./constraints/LockConstraint'),
+ Material : require('./material/Material'),
+ NaiveBroadphase : require('./collision/NaiveBroadphase'),
+ Particle : require('./shapes/Particle'),
+ Plane : require('./shapes/Plane'),
+ RevoluteConstraint : require('./constraints/RevoluteConstraint'),
+ PrismaticConstraint : require('./constraints/PrismaticConstraint'),
+ Rectangle : require('./shapes/Rectangle'),
+ RotationalVelocityEquation : require('./equations/RotationalVelocityEquation'),
+ SAPBroadphase : require('./collision/SAPBroadphase'),
+ Shape : require('./shapes/Shape'),
+ Solver : require('./solver/Solver'),
+ Spring : require('./objects/Spring'),
+ Utils : require('./utils/Utils'),
+ World : require('./world/World'),
+ QuadTree : require('./collision/QuadTree').QuadTree,
+ vec2 : require('./math/vec2'),
+ version : require('../package.json').version,
+};
+
+},{"../package.json":8,"./collision/AABB":9,"./collision/Broadphase":10,"./collision/GridBroadphase":11,"./collision/NaiveBroadphase":12,"./collision/QuadTree":14,"./collision/SAPBroadphase":15,"./constraints/Constraint":16,"./constraints/DistanceConstraint":17,"./constraints/GearConstraint":18,"./constraints/LockConstraint":19,"./constraints/PrismaticConstraint":20,"./constraints/RevoluteConstraint":21,"./equations/AngleLockEquation":22,"./equations/ContactEquation":23,"./equations/Equation":24,"./equations/FrictionEquation":25,"./equations/RotationalVelocityEquation":27,"./events/EventEmitter":28,"./material/ContactMaterial":29,"./material/Material":30,"./math/vec2":33,"./objects/Body":34,"./objects/Spring":35,"./shapes/Capsule":37,"./shapes/Circle":38,"./shapes/Convex":39,"./shapes/Line":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Rectangle":43,"./shapes/Shape":44,"./solver/GSSolver":45,"./solver/IslandSolver":47,"./solver/Solver":48,"./utils/Utils":49,"./world/World":50}],37:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Capsule;
+
+/**
+ * Capsule shape class.
+ * @class Capsule
+ * @constructor
+ * @extends {Shape}
+ * @param {Number} length The distance between the end points
+ * @param {Number} radius Radius of the capsule
+ */
+function Capsule(length,radius){
+ this.length = length || 1;
+ this.radius = radius || 1;
+
+ Shape.call(this,Shape.CAPSULE);
+};
+Capsule.prototype = new Shape();
+
+/**
+ * Compute the mass moment of inertia of the Capsule.
+ * @method conputeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number}
+ * @todo
+ */
+Capsule.prototype.computeMomentOfInertia = function(mass){
+ // Approximate with rectangle
+ var r = this.radius,
+ w = this.length + r, // 2*r is too much, 0 is too little
+ h = r*2;
+ return mass * (h*h + w*w) / 12;
+};
+
+Capsule.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.radius + this.length/2;
+};
+
+Capsule.prototype.updateArea = function(){
+ this.area = Math.PI * this.radius * this.radius + this.radius * 2 * this.length;
+};
+
+var r = vec2.create();
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Capsule.prototype.computeAABB = function(out, position, angle){
+ var radius = this.radius;
+
+ // Compute center position of one of the the circles, world oriented, but with local offset
+ vec2.set(r,this.length,0);
+ vec2.rotate(r,r,angle);
+
+ // Get bounds
+ vec2.set(out.upperBound, Math.max(r[0]+radius, -r[0]+radius),
+ Math.max(r[1]+radius, -r[1]+radius));
+ vec2.set(out.lowerBound, Math.min(r[0]-radius, -r[0]-radius),
+ Math.min(r[1]-radius, -r[1]-radius));
+
+ // Add offset
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+};
+
+},{"../math/vec2":33,"./Shape":44}],38:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Circle;
+
+/**
+ * Circle shape class.
+ * @class Circle
+ * @extends {Shape}
+ * @constructor
+ * @param {number} radius The radius of this circle
+ */
+function Circle(radius){
+
+ /**
+ * The radius of the circle.
+ * @property radius
+ * @type {number}
+ */
+ this.radius = radius || 1;
+
+ Shape.call(this,Shape.CIRCLE);
+};
+Circle.prototype = new Shape();
+Circle.prototype.computeMomentOfInertia = function(mass){
+ var r = this.radius;
+ return mass * r * r / 2;
+};
+
+Circle.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.radius;
+};
+
+Circle.prototype.updateArea = function(){
+ this.area = Math.PI * this.radius * this.radius;
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Circle.prototype.computeAABB = function(out, position, angle){
+ var r = this.radius;
+ vec2.set(out.upperBound, r, r);
+ vec2.set(out.lowerBound, -r, -r);
+ if(position){
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+ }
+};
+
+},{"../math/vec2":33,"./Shape":44}],39:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+, polyk = require('../math/polyk')
+, decomp = require('poly-decomp')
+
+module.exports = Convex;
+
+/**
+ * Convex shape class.
+ * @class Convex
+ * @constructor
+ * @extends {Shape}
+ * @param {Array} vertices An array of Float32Array vertices that span this shape. Vertices are given in counter-clockwise (CCW) direction.
+ */
+function Convex(vertices){
+
+ /**
+ * Vertices defined in the local frame.
+ * @property vertices
+ * @type {Array}
+ */
+ this.vertices = vertices || [];
+
+ // Copy the verts
+ for(var i=0; i r2) r2 = l2;
+ }
+
+ this.boundingRadius = Math.sqrt(r2);
+};
+
+/**
+ * Get the area of the triangle spanned by the three points a, b, c. The area is positive if the points are given in counter-clockwise order, otherwise negative.
+ * @static
+ * @method triangleArea
+ * @param {Array} a
+ * @param {Array} b
+ * @param {Array} c
+ * @return {Number}
+ */
+Convex.triangleArea = function(a,b,c){
+ return (((b[0] - a[0])*(c[1] - a[1]))-((c[0] - a[0])*(b[1] - a[1]))) * 0.5;
+}
+
+/**
+ * Update the .area
+ * @method updateArea
+ */
+Convex.prototype.updateArea = function(){
+ this.updateTriangles();
+ this.area = 0;
+
+ var triangles = this.triangles,
+ verts = this.vertices;
+ for(var i=0; i!==triangles.length; i++){
+ var t = triangles[i],
+ a = verts[t[0]],
+ b = verts[t[1]],
+ c = verts[t[2]];
+
+ // Get mass for the triangle (density=1 in this case)
+ var m = Convex.triangleArea(a,b,c);
+ this.area += m;
+ }
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Convex.prototype.computeAABB = function(out, position, angle){
+ out.setFromPoints(this.vertices,position,angle);
+};
+
+},{"../math/polyk":32,"../math/vec2":33,"./Shape":44,"poly-decomp":7}],40:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Line;
+
+/**
+ * Line shape class. The line shape is along the x direction, and stretches from [-length/2, 0] to [length/2,0].
+ * @class Line
+ * @param {Number} length The total length of the line
+ * @extends {Shape}
+ * @constructor
+ */
+function Line(length){
+
+ /**
+ * Length of this line
+ * @property length
+ * @type {Number}
+ */
+ this.length = length;
+
+ Shape.call(this,Shape.LINE);
+};
+Line.prototype = new Shape();
+Line.prototype.computeMomentOfInertia = function(mass){
+ return mass * Math.pow(this.length,2) / 12;
+};
+
+Line.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = this.length/2;
+};
+
+var points = [vec2.create(),vec2.create()];
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Line.prototype.computeAABB = function(out, position, angle){
+ var l = this.length;
+ vec2.set(points[0], -l/2, 0);
+ vec2.set(points[1], l/2, 0);
+ out.setFromPoints(points,position,angle);
+};
+
+
+},{"../math/vec2":33,"./Shape":44}],41:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+
+module.exports = Particle;
+
+/**
+ * Particle shape class.
+ * @class Particle
+ * @constructor
+ * @extends {Shape}
+ */
+function Particle(){
+ Shape.call(this,Shape.PARTICLE);
+};
+Particle.prototype = new Shape();
+Particle.prototype.computeMomentOfInertia = function(mass){
+ return 0; // Can't rotate a particle
+};
+
+Particle.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = 0;
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Particle.prototype.computeAABB = function(out, position, angle){
+ var l = this.length;
+ vec2.copy(out.lowerBound, position);
+ vec2.copy(out.upperBound, position);
+};
+
+},{"../math/vec2":33,"./Shape":44}],42:[function(require,module,exports){
+var Shape = require('./Shape')
+, vec2 = require('../math/vec2')
+, Utils = require('../utils/Utils')
+
+module.exports = Plane;
+
+/**
+ * Plane shape class. The plane is facing in the Y direction.
+ * @class Plane
+ * @extends {Shape}
+ * @constructor
+ */
+function Plane(){
+ Shape.call(this,Shape.PLANE);
+};
+Plane.prototype = new Shape();
+
+/**
+ * Compute moment of inertia
+ * @method computeMomentOfInertia
+ */
+Plane.prototype.computeMomentOfInertia = function(mass){
+ return 0; // Plane is infinite. The inertia should therefore be infinty but by convention we set 0 here
+};
+
+/**
+ * Update the bounding radius
+ * @method updateBoundingRadius
+ */
+Plane.prototype.updateBoundingRadius = function(){
+ this.boundingRadius = Number.MAX_VALUE;
+};
+
+/**
+ * @method computeAABB
+ * @param {AABB} out
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Plane.prototype.computeAABB = function(out, position, angle){
+ var a = 0,
+ set = vec2.set;
+ if(typeof(angle) == "number")
+ a = angle % (2*Math.PI);
+
+ if(a == 0){
+ // y goes from -inf to 0
+ set(out.lowerBound, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ set(out.upperBound, Number.MAX_VALUE, 0);
+ } else if(a == Math.PI / 2){
+ // x goes from 0 to inf
+ set(out.lowerBound, 0, -Number.MAX_VALUE);
+ set(out.upperBound, Number.MAX_VALUE, Number.MAX_VALUE);
+ } else if(a == Math.PI){
+ // y goes from 0 to inf
+ set(out.lowerBound, -Number.MAX_VALUE, 0);
+ set(out.upperBound, Number.MAX_VALUE, Number.MAX_VALUE);
+ } else if(a == 3*Math.PI/2){
+ // x goes from -inf to 0
+ set(out.lowerBound, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ set(out.upperBound, 0, Number.MAX_VALUE);
+ } else {
+ // Set max bounds
+ set(out.lowerBound, -Number.MAX_VALUE, -Number.MAX_VALUE);
+ set(out.upperBound, Number.MAX_VALUE, Number.MAX_VALUE);
+ }
+
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+};
+
+Plane.prototype.updateArea = function(){
+ this.area = Number.MAX_VALUE;
+};
+
+
+},{"../math/vec2":33,"../utils/Utils":49,"./Shape":44}],43:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, Shape = require('./Shape')
+, Convex = require('./Convex')
+
+module.exports = Rectangle;
+
+/**
+ * Rectangle shape class.
+ * @class Rectangle
+ * @constructor
+ * @param {Number} w Width
+ * @param {Number} h Height
+ * @extends {Convex}
+ */
+function Rectangle(w,h){
+ var verts = [ vec2.fromValues(-w/2, -h/2),
+ vec2.fromValues( w/2, -h/2),
+ vec2.fromValues( w/2, h/2),
+ vec2.fromValues(-w/2, h/2)];
+
+ /**
+ * Total width of the rectangle
+ * @property width
+ * @type {Number}
+ */
+ this.width = w;
+
+ /**
+ * Total height of the rectangle
+ * @property height
+ * @type {Number}
+ */
+ this.height = h;
+
+ Convex.call(this,verts);
+};
+Rectangle.prototype = new Convex();
+
+/**
+ * Compute moment of inertia
+ * @method computeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number}
+ */
+Rectangle.prototype.computeMomentOfInertia = function(mass){
+ var w = this.width,
+ h = this.height;
+ return mass * (h*h + w*w) / 12;
+};
+
+/**
+ * Update the bounding radius
+ * @method updateBoundingRadius
+ */
+Rectangle.prototype.updateBoundingRadius = function(){
+ var w = this.width,
+ h = this.height;
+ this.boundingRadius = Math.sqrt(w*w + h*h) / 2;
+};
+
+var corner1 = vec2.create(),
+ corner2 = vec2.create(),
+ corner3 = vec2.create(),
+ corner4 = vec2.create();
+
+/**
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Rectangle.prototype.computeAABB = function(out, position, angle){
+ /*
+ // Get world corners
+ vec2.rotate(corner1,this.vertices[0],angle);
+ vec2.rotate(corner2,this.vertices[1],angle);
+ vec2.rotate(corner3,this.vertices[2],angle);
+ vec2.rotate(corner4,this.vertices[3],angle);
+ vec2.set(out.upperBound, Math.max(corner1[0],corner2[0],corner3[0],corner4[0]),
+ Math.max(corner1[1],corner2[1],corner3[1],corner4[1]));
+ vec2.set(out.lowerBound, Math.min(corner1[0],corner2[0],corner3[0],corner4[0]),
+ Math.min(corner1[1],corner2[1],corner3[1],corner4[1]));
+
+ // Add world offset
+ vec2.add(out.lowerBound, out.lowerBound, position);
+ vec2.add(out.upperBound, out.upperBound, position);
+ */
+ out.setFromPoints(this.vertices,position,angle);
+};
+
+Rectangle.prototype.updateArea = function(){
+ this.area = this.width * this.height;
+};
+
+
+},{"../math/vec2":33,"./Convex":39,"./Shape":44}],44:[function(require,module,exports){
+module.exports = Shape;
+
+/**
+ * Base class for shapes.
+ * @class Shape
+ * @constructor
+ */
+function Shape(type){
+ this.type = type;
+
+ /**
+ * Shape object identifier.
+ * @type {Number}
+ * @property id
+ */
+ this.id = Shape.idCounter++;
+
+ /**
+ * Bounding circle radius of this shape
+ * @property boundingRadius
+ * @type {Number}
+ */
+ this.boundingRadius = 0;
+
+ /**
+ * Collision group that this shape belongs to (bit mask). See this tutorial.
+ * @property collisionGroup
+ * @type {Number}
+ * @example
+ * // Setup bits for each available group
+ * var PLAYER = Math.pow(2,0),
+ * ENEMY = Math.pow(2,1),
+ * GROUND = Math.pow(2,2)
+ *
+ * // Put shapes into their groups
+ * player1Shape.collisionGroup = PLAYER;
+ * player2Shape.collisionGroup = PLAYER;
+ * enemyShape .collisionGroup = ENEMY;
+ * groundShape .collisionGroup = GROUND;
+ *
+ * // Assign groups that each shape collide with.
+ * // Note that the players can collide with ground and enemies, but not with other players.
+ * player1Shape.collisionMask = ENEMY | GROUND;
+ * player2Shape.collisionMask = ENEMY | GROUND;
+ * enemyShape .collisionMask = PLAYER | GROUND;
+ * groundShape .collisionMask = PLAYER | ENEMY;
+ *
+ * @example
+ * // How collision check is done
+ * if(shapeA.collisionGroup & shapeB.collisionMask)!=0 && (shapeB.collisionGroup & shapeA.collisionMask)!=0){
+ * // The shapes will collide
+ * }
+ */
+ this.collisionGroup = 1;
+
+ /**
+ * Collision mask of this shape. See .collisionGroup.
+ * @property collisionMask
+ * @type {Number}
+ */
+ this.collisionMask = 1;
+ if(type) this.updateBoundingRadius();
+
+ /**
+ * Material to use in collisions for this Shape. If this is set to null, the world will use default material properties instead.
+ * @property material
+ * @type {Material}
+ */
+ this.material = null;
+
+ /**
+ * Area of this shape.
+ * @property area
+ * @type {Number}
+ */
+ this.area = 0;
+
+ /**
+ * Set to true if you want this shape to be a sensor. A sensor does not generate contacts, but it still reports contact events. This is good if you want to know if a shape is overlapping another shape, without them generating contacts.
+ * @property {Boolean} sensor
+ */
+ this.sensor = false;
+
+ this.updateArea();
+};
+
+Shape.idCounter = 0;
+
+Shape.CIRCLE = 1;
+Shape.PARTICLE = 2;
+Shape.PLANE = 4;
+Shape.CONVEX = 8;
+Shape.LINE = 16;
+Shape.RECTANGLE = 32;
+Shape.CAPSULE = 64;
+
+/**
+ * Should return the moment of inertia around the Z axis of the body given the total mass. See Wikipedia's list of moments of inertia.
+ * @method computeMomentOfInertia
+ * @param {Number} mass
+ * @return {Number} If the inertia is infinity or if the object simply isn't possible to rotate, return 0.
+ */
+Shape.prototype.computeMomentOfInertia = function(mass){
+ throw new Error("Shape.computeMomentOfInertia is not implemented in this Shape...");
+};
+
+/**
+ * Returns the bounding circle radius of this shape.
+ * @method updateBoundingRadius
+ * @return {Number}
+ */
+Shape.prototype.updateBoundingRadius = function(){
+ throw new Error("Shape.updateBoundingRadius is not implemented in this Shape...");
+};
+
+/**
+ * Update the .area property of the shape.
+ * @method updateArea
+ */
+Shape.prototype.updateArea = function(){
+ // To be implemented in all subclasses
+};
+
+/**
+ * Compute the world axis-aligned bounding box (AABB) of this shape.
+ * @method computeAABB
+ * @param {AABB} out The resulting AABB.
+ * @param {Array} position
+ * @param {Number} angle
+ */
+Shape.prototype.computeAABB = function(out, position, angle){
+ // To be implemented in each subclass
+};
+
+},{}],45:[function(require,module,exports){
+var vec2 = require('../math/vec2')
+, Solver = require('./Solver')
+, Utils = require('../utils/Utils')
+, FrictionEquation = require('../equations/FrictionEquation')
+
+module.exports = GSSolver;
+
+/**
+ * Iterative Gauss-Seidel constraint equation solver.
+ *
+ * @class GSSolver
+ * @constructor
+ * @extends Solver
+ * @param {Object} [options]
+ * @param {Number} options.iterations
+ * @param {Number} options.timeStep
+ * @param {Number} options.stiffness
+ * @param {Number} options.relaxation
+ * @param {Number} options.tolerance
+ */
+function GSSolver(options){
+ Solver.call(this,options);
+ options = options || {};
+
+ /**
+ * The number of iterations to do when solving. More gives better results, but is more expensive.
+ * @property iterations
+ * @type {Number}
+ */
+ this.iterations = options.iterations || 10;
+
+ /**
+ * The error tolerance. If the total error is below this limit, the solver will stop. Set to zero for as good solution as possible.
+ * @property tolerance
+ * @type {Number}
+ */
+ this.tolerance = options.tolerance || 0;
+
+ this.debug = options.debug || false;
+ this.arrayStep = 30;
+ this.lambda = new Utils.ARRAY_TYPE(this.arrayStep);
+ this.Bs = new Utils.ARRAY_TYPE(this.arrayStep);
+ this.invCs = new Utils.ARRAY_TYPE(this.arrayStep);
+
+ /**
+ * Whether to use .stiffness and .relaxation parameters from the Solver instead of each Equation individually.
+ * @type {Boolean}
+ * @property useGlobalEquationParameters
+ */
+ this.useGlobalEquationParameters = true;
+
+ /**
+ * Global equation stiffness. Larger number gives harder contacts, etc, but may also be more expensive to compute, or it will make your simulation explode.
+ * @property stiffness
+ * @type {Number}
+ */
+ this.stiffness = 1e6;
+
+ /**
+ * Global equation relaxation. This is the number of timesteps required for a constraint to be resolved. Larger number will give softer contacts. Set to around 3 or 4 for good enough results.
+ * @property relaxation
+ * @type {Number}
+ */
+ this.relaxation = 4;
+
+ /**
+ * Set to true to set all right hand side terms to zero when solving. Can be handy for a few applications.
+ * @property useZeroRHS
+ * @type {Boolean}
+ */
+ this.useZeroRHS = false;
+
+ /**
+ * Number of friction iterations to skip. If .skipFrictionIterations=2, then no FrictionEquations will be iterated until the third iteration.
+ * @property skipFrictionIterations
+ * @type {Number}
+ */
+ this.skipFrictionIterations = 0;
+};
+GSSolver.prototype = new Solver();
+
+function setArrayZero(array){
+ for(var i=0; i!==array.length; i++){
+ array[i] = 0.0;
+ }
+}
+
+/**
+ * Solve the system of equations
+ * @method solve
+ * @param {Number} h Time step
+ * @param {World} world World to solve
+ */
+GSSolver.prototype.solve = function(h,world){
+
+ this.sortEquations();
+
+ var iter = 0,
+ maxIter = this.iterations,
+ skipFrictionIter = this.skipFrictionIterations,
+ tolSquared = this.tolerance*this.tolerance,
+ equations = this.equations,
+ Neq = equations.length,
+ bodies = world.bodies,
+ Nbodies = world.bodies.length,
+ d = this.relaxation,
+ k = this.stiffness,
+ eps = 4.0 / (h * h * k * (1 + 4 * d)),
+ a = 4.0 / (h * (1 + 4 * d)),
+ b = (4.0 * d) / (1 + 4 * d),
+ useGlobalParams = this.useGlobalEquationParameters,
+ add = vec2.add,
+ set = vec2.set,
+ useZeroRHS = this.useZeroRHS,
+ lambda = this.lambda;
+
+ // Things that does not change during iteration can be computed once
+ if(lambda.length < Neq){
+ lambda = this.lambda = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
+ this.Bs = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
+ this.invCs = new Utils.ARRAY_TYPE(Neq + this.arrayStep);
+ } else {
+ setArrayZero(lambda);
+ }
+ var invCs = this.invCs,
+ Bs = this.Bs,
+ lambda = this.lambda;
+ if(!useGlobalParams){
+ for(var i=0, c; c = equations[i]; i++){
+ if(h !== c.h) c.updateSpookParams(h);
+ Bs[i] = c.computeB(c.a,c.b,h);
+ invCs[i] = c.computeInvC(c.eps);
+ }
+ } else {
+ for(var i=0, c; c = equations[i]; i++){
+ Bs[i] = c.computeB(a,b,h);
+ invCs[i] = c.computeInvC(eps);
+ }
+ }
+
+ var q, B, c, deltalambdaTot,i,j;
+
+ if(Neq !== 0){
+
+ // Reset vlambda
+ for(i=0; i!==Nbodies; i++){
+ bodies[i].resetConstraintVelocity();
+ }
+
+ // Iterate over equations
+ for(iter=0; iter!==maxIter; iter++){
+
+ // Accumulate the total error for each iteration.
+ deltalambdaTot = 0.0;
+
+ for(j=0; j!==Neq; j++){
+ c = equations[j];
+
+ if(c instanceof FrictionEquation && iter < skipFrictionIter)
+ continue;
+
+ var _eps = useGlobalParams ? eps : c.eps;
+
+ var deltalambda = GSSolver.iterateEquation(j,c,_eps,Bs,invCs,lambda,useZeroRHS,h);
+ deltalambdaTot += Math.abs(deltalambda);
+ }
+
+ // If the total error is small enough - stop iterate
+ if(deltalambdaTot*deltalambdaTot <= tolSquared) break;
+ }
+
+ // Add result to velocity
+ for(i=0; i!==Nbodies; i++){
+ bodies[i].addConstraintVelocity();
+ }
+ }
+};
+
+GSSolver.iterateEquation = function(j,eq,eps,Bs,invCs,lambda,useZeroRHS,dt){
+ // Compute iteration
+ var B = Bs[j],
+ invC = invCs[j],
+ lambdaj = lambda[j],
+ GWlambda = eq.computeGWlambda(eps);
+
+ if(eq instanceof FrictionEquation){
+ // Rescale the max friction force according to the normal force
+ eq.maxForce = eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
+ eq.minForce = -eq.contactEquation.multiplier * eq.frictionCoefficient * dt;
+ }
+
+ var maxForce = eq.maxForce,
+ minForce = eq.minForce;
+
+ if(useZeroRHS) B = 0;
+
+ var deltalambda = invC * ( B - GWlambda - eps * lambdaj );
+
+ // Clamp if we are not within the min/max interval
+ var lambdaj_plus_deltalambda = lambdaj + deltalambda;
+ if(lambdaj_plus_deltalambda < minForce){
+ deltalambda = minForce - lambdaj;
+ } else if(lambdaj_plus_deltalambda > maxForce){
+ deltalambda = maxForce - lambdaj;
+ }
+ lambda[j] += deltalambda;
+ eq.multiplier = lambda[j] / dt;
+ eq.addToWlambda(deltalambda);
+
+ return deltalambda;
+};
+
+},{"../equations/FrictionEquation":25,"../math/vec2":33,"../utils/Utils":49,"./Solver":48}],46:[function(require,module,exports){
+module.exports = Island;
+
+/**
+ * An island of bodies connected with equations.
+ * @class Island
+ * @constructor
+ */
+function Island(){
+
+ /**
+ * Current equations in this island.
+ * @property equations
+ * @type {Array}
+ */
+ this.equations = [];
+
+ /**
+ * Current bodies in this island.
+ * @property bodies
+ * @type {Array}
+ */
+ this.bodies = [];
+}
+
+/**
+ * Clean this island from bodies and equations.
+ * @method reset
+ */
+Island.prototype.reset = function(){
+ this.equations.length = this.bodies.length = 0;
+}
+
+
+/**
+ * Get all unique bodies in this island.
+ * @method getBodies
+ * @return {Array} An array of Body
+ */
+Island.prototype.getBodies = function(){
+ var bodies = [],
+ bodyIds = [],
+ eqs = this.equations;
+ for(var i=0; i!==eqs.length; i++){
+ var eq = eqs[i];
+ if(bodyIds.indexOf(eq.bi.id)===-1){
+ bodies.push(eq.bi);
+ bodyIds.push(eq.bi.id);
+ }
+ if(bodyIds.indexOf(eq.bj.id)===-1){
+ bodies.push(eq.bj);
+ bodyIds.push(eq.bj.id);
+ }
+ }
+ return bodies;
+};
+
+/**
+ * Solves all constraints in the group of islands.
+ * @method solve
+ * @param {Number} dt
+ * @param {Solver} solver
+ */
+Island.prototype.solve = function(dt,solver){
+ var bodies = [];
+
+ solver.removeAllEquations();
+
+ // Add equations to solver
+ var numEquations = this.equations.length;
+ for(var j=0; j!==numEquations; j++){
+ solver.addEquation(this.equations[j]);
+ }
+ var islandBodies = this.getBodies();
+ var numBodies = islandBodies.length;
+ for(var j=0; j!==numBodies; j++){
+ bodies.push(islandBodies[j]);
+ }
+
+ // Solve
+ solver.solve(dt,{bodies:bodies});
+};
+
+},{}],47:[function(require,module,exports){
+var Solver = require('./Solver')
+, vec2 = require('../math/vec2')
+, Island = require('../solver/Island')
+, Body = require('../objects/Body')
+, STATIC = Body.STATIC
+
+module.exports = IslandSolver;
+
+/**
+ * Splits the system of bodies and equations into independent islands
+ *
+ * @class IslandSolver
+ * @constructor
+ * @param {Solver} subsolver
+ * @param {Object} options
+ * @extends Solver
+ */
+function IslandSolver(subsolver,options){
+ Solver.call(this,options);
+ var that = this;
+
+ /**
+ * The solver used in the workers.
+ * @property subsolver
+ * @type {Solver}
+ */
+ this.subsolver = subsolver;
+
+ /**
+ * Number of islands. Read only.
+ * @property numIslands
+ * @type {number}
+ */
+ this.numIslands = 0;
+
+ // Pooling of node objects saves some GC load
+ this._nodePool = [];
+
+ this._islandPool = [];
+
+ /**
+ * Fires before an island is solved.
+ * @event beforeSolveIsland
+ * @param {Island} island
+ */
+ this.beforeSolveIslandEvent = {
+ type : "beforeSolveIsland",
+ island : null,
+ };
+};
+IslandSolver.prototype = new Solver();
+
+function getUnvisitedNode(nodes){
+ var Nnodes = nodes.length;
+ for(var i=0; i!==Nnodes; i++){
+ var node = nodes[i];
+ if(!node.visited && !(node.body.motionState & STATIC)){ // correct?
+ return node;
+ }
+ }
+ return false;
+}
+
+function visitFunc(node,bds,eqs){
+ bds.push(node.body);
+ var Neqs = node.eqs.length;
+ for(var i=0; i!==Neqs; i++){
+ var eq = node.eqs[i];
+ if(eqs.indexOf(eq) === -1){
+ eqs.push(eq);
+ }
+ }
+}
+
+var queue = [];
+function bfs(root,visitFunc,bds,eqs){
+ queue.length = 0;
+ queue.push(root);
+ root.visited = true;
+ visitFunc(root,bds,eqs);
+ while(queue.length) {
+ var node = queue.pop();
+ // Loop over unvisited child nodes
+ var child;
+ while((child = getUnvisitedNode(node.children))) {
+ child.visited = true;
+ visitFunc(child,bds,eqs);
+ queue.push(child);
+ }
+ }
+}
+
+var tmpArray = [],
+ tmpArray2 = [],
+ tmpArray3 = [],
+ tmpArray4 = [];
+
+/**
+ * Solves the full system.
+ * @method solve
+ * @param {Number} dt
+ * @param {World} world
+ */
+IslandSolver.prototype.solve = function(dt,world){
+ var nodes = tmpArray,
+ bodies=world.bodies,
+ equations=this.equations,
+ Neq=equations.length,
+ Nbodies=bodies.length,
+ subsolver=this.subsolver,
+ workers = this._workers,
+ workerData = this._workerData,
+ workerIslandGroups = this._workerIslandGroups,
+ islandPool = this._islandPool;
+
+ tmpArray.length = 0;
+
+ // Create needed nodes, reuse if possible
+ for(var i=0; i!==Nbodies; i++){
+ if(this._nodePool.length)
+ nodes.push( this._nodePool.pop() );
+ else {
+ nodes.push({
+ body:bodies[i],
+ children:[],
+ eqs:[],
+ visited:false
+ });
+ }
+ }
+
+ // Reset node values
+ for(var i=0; i!==Nbodies; i++){
+ var node = nodes[i];
+ node.body = bodies[i];
+ node.children.length = 0;
+ node.eqs.length = 0;
+ node.visited = false;
+ }
+
+ // Add connectivity data. Each equation connects 2 bodies.
+ for(var k=0; k!==Neq; k++){
+ var eq=equations[k],
+ i=bodies.indexOf(eq.bi),
+ j=bodies.indexOf(eq.bj),
+ ni=nodes[i],
+ nj=nodes[j];
+ ni.children.push(nj);
+ ni.eqs.push(eq);
+ nj.children.push(ni);
+ nj.eqs.push(eq);
+ }
+
+ // The BFS search algorithm needs a traversal function. What we do is gather all bodies and equations connected.
+ var child, n=0, eqs=tmpArray2, bds=tmpArray3;
+ eqs.length = 0;
+ bds.length = 0;
+
+ // Get islands
+ var islands = tmpArray4;
+ islands.length = 0;
+ while((child = getUnvisitedNode(nodes))){
+ var island = islandPool.length ? islandPool.pop() : new Island();
+ eqs.length = 0;
+ bds.length = 0;
+ bfs(child,visitFunc,bds,eqs); // run search algo to gather an island of bodies
+
+ // Add equations to island
+ var Neqs = eqs.length;
+ for(var i=0; i!==Neqs; i++){
+ var eq = eqs[i];
+ island.equations.push(eq);
+ }
+
+ n++;
+ islands.push(island);
+ }
+
+ this.numIslands = n;
+
+ // Solve islands
+ var e = this.beforeSolveIslandEvent;
+ for(var i=0; ithis for an explanation.
+ * @method appendArray
+ * @static
+ * @param {Array} a
+ * @param {Array} b
+ */
+Utils.appendArray = function(a,b){
+ if (b.length < 150000) {
+ a.push.apply(a, b)
+ } else {
+ for (var i = 0, len = b.length; i !== len; ++i) {
+ a.push(b[i]);
+ }
+ }
+};
+
+/**
+ * Garbage free Array.splice(). Does not allocate a new array.
+ * @method splice
+ * @static
+ * @param {Array} array
+ * @param {Number} index
+ * @param {Number} howmany
+ */
+Utils.splice = function(array,index,howmany){
+ howmany = howmany || 1;
+ for (var i=index, len=array.length-howmany; i < len; i++)
+ array[i] = array[i + howmany];
+ array.length = len;
+};
+
+/**
+ * The array type to use for internal numeric computations.
+ * @type {Array}
+ * @static
+ * @property ARRAY_TYPE
+ */
+Utils.ARRAY_TYPE = Float32Array || Array;
+
+},{}],50:[function(require,module,exports){
+var GSSolver = require('../solver/GSSolver')
+, NaiveBroadphase = require('../collision/NaiveBroadphase')
+, vec2 = require('../math/vec2')
+, Circle = require('../shapes/Circle')
+, Rectangle = require('../shapes/Rectangle')
+, Convex = require('../shapes/Convex')
+, Line = require('../shapes/Line')
+, Plane = require('../shapes/Plane')
+, Capsule = require('../shapes/Capsule')
+, Particle = require('../shapes/Particle')
+, EventEmitter = require('../events/EventEmitter')
+, Body = require('../objects/Body')
+, Spring = require('../objects/Spring')
+, Material = require('../material/Material')
+, ContactMaterial = require('../material/ContactMaterial')
+, DistanceConstraint = require('../constraints/DistanceConstraint')
+, LockConstraint = require('../constraints/LockConstraint')
+, RevoluteConstraint = require('../constraints/RevoluteConstraint')
+, PrismaticConstraint = require('../constraints/PrismaticConstraint')
+, pkg = require('../../package.json')
+, Broadphase = require('../collision/Broadphase')
+, Narrowphase = require('../collision/Narrowphase')
+, Utils = require('../utils/Utils')
+
+module.exports = World;
+
+var currentVersion = pkg.version.split(".").slice(0,2).join("."); // "X.Y"
+
+if(typeof performance === 'undefined')
+ performance = {};
+if(!performance.now){
+ var nowOffset = Date.now();
+ if (performance.timing && performance.timing.navigationStart){
+ nowOffset = performance.timing.navigationStart
+ }
+ performance.now = function(){
+ return Date.now() - nowOffset;
+ }
+}
+
+/**
+ * The dynamics world, where all bodies and constraints lives.
+ *
+ * @class World
+ * @constructor
+ * @param {Object} [options]
+ * @param {Solver} options.solver Defaults to GSSolver.
+ * @param {Float32Array} options.gravity Defaults to [0,-9.78]
+ * @param {Broadphase} options.broadphase Defaults to NaiveBroadphase
+ * @extends {EventEmitter}
+ */
+function World(options){
+ EventEmitter.apply(this);
+
+ options = options || {};
+
+ /**
+ * All springs in the world.
+ *
+ * @property springs
+ * @type {Array}
+ */
+ this.springs = [];
+
+ /**
+ * All bodies in the world.
+ *
+ * @property bodies
+ * @type {Array}
+ */
+ this.bodies = [];
+
+ /**
+ * The solver used to satisfy constraints and contacts.
+ *
+ * @property solver
+ * @type {Solver}
+ */
+ this.solver = options.solver || new GSSolver();
+
+ /**
+ * The narrowphase to use to generate contacts.
+ *
+ * @property narrowphase
+ * @type {Narrowphase}
+ */
+ this.narrowphase = new Narrowphase(this);
+
+ /**
+ * Gravity in the world. This is applied on all bodies in the beginning of each step().
+ *
+ * @property
+ * @type {Float32Array}
+ */
+ this.gravity = options.gravity || vec2.fromValues(0, -9.78);
+
+ /**
+ * Whether to do timing measurements during the step() or not.
+ *
+ * @property doPofiling
+ * @type {Boolean}
+ */
+ this.doProfiling = options.doProfiling || false;
+
+ /**
+ * How many millisecconds the last step() took. This is updated each step if .doProfiling is set to true.
+ *
+ * @property lastStepTime
+ * @type {Number}
+ */
+ this.lastStepTime = 0.0;
+
+ /**
+ * The broadphase algorithm to use.
+ *
+ * @property broadphase
+ * @type {Broadphase}
+ */
+ this.broadphase = options.broadphase || new NaiveBroadphase();
+
+ this.broadphase.setWorld(this);
+
+ /**
+ * User-added constraints.
+ *
+ * @property constraints
+ * @type {Array}
+ */
+ this.constraints = [];
+
+ /**
+ * Friction between colliding bodies. This value is used if no matching ContactMaterial is found for a Material pair.
+ * @property defaultFriction
+ * @type {Number}
+ */
+ this.defaultFriction = 0.3;
+
+ /**
+ * Default coefficient of restitution between colliding bodies. This value is used if no matching ContactMaterial is found for a Material pair.
+ * @property defaultRestitution
+ * @type {Number}
+ */
+ this.defaultRestitution = 0.0;
+
+ /**
+ * For keeping track of what time step size we used last step
+ * @property lastTimeStep
+ * @type {Number}
+ */
+ this.lastTimeStep = 1/60;
+
+ /**
+ * Enable to automatically apply spring forces each step.
+ * @property applySpringForces
+ * @type {Boolean}
+ */
+ this.applySpringForces = true;
+
+ /**
+ * Enable to automatically apply body damping each step.
+ * @property applyDamping
+ * @type {Boolean}
+ */
+ this.applyDamping = true;
+
+ /**
+ * Enable to automatically apply gravity each step.
+ * @property applyGravity
+ * @type {Boolean}
+ */
+ this.applyGravity = true;
+
+ /**
+ * Enable/disable constraint solving in each step.
+ * @property solveConstraints
+ * @type {Boolean}
+ */
+ this.solveConstraints = true;
+
+ /**
+ * The ContactMaterials added to the World.
+ * @property contactMaterials
+ * @type {Array}
+ */
+ this.contactMaterials = [];
+
+ /**
+ * World time.
+ * @property time
+ * @type {Number}
+ */
+ this.time = 0.0;
+
+ this.fixedStepTime = 0.0;
+
+ /**
+ * Set to true if you want to the world to emit the "impact" event. Turning this off could improve performance.
+ * @property emitImpactEvent
+ * @type {Boolean}
+ */
+ this.emitImpactEvent = true;
+
+ // Id counters
+ this._constraintIdCounter = 0;
+ this._bodyIdCounter = 0;
+
+ /**
+ * Fired after the step().
+ * @event postStep
+ */
+ this.postStepEvent = {
+ type : "postStep",
+ };
+
+ /**
+ * @event addBody
+ * @param {Body} body
+ */
+ this.addBodyEvent = {
+ type : "addBody",
+ body : null
+ };
+
+ /**
+ * @event removeBody
+ * @param {Body} body
+ */
+ this.removeBodyEvent = {
+ type : "removeBody",
+ body : null
+ };
+
+ /**
+ * Fired when a spring is added to the world.
+ * @event addSpring
+ * @param {Spring} spring
+ */
+ this.addSpringEvent = {
+ type : "addSpring",
+ spring : null,
+ };
+
+ /**
+ * Fired when a first contact is created between two bodies. This event is fired after the step has been done.
+ * @event impact
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ */
+ this.impactEvent = {
+ type: "impact",
+ bodyA : null,
+ bodyB : null,
+ shapeA : null,
+ shapeB : null,
+ contactEquation : null,
+ };
+
+ /**
+ * Fired after the Broadphase has collected collision pairs in the world.
+ * Inside the event handler, you can modify the pairs array as you like, to
+ * prevent collisions between objects that you don't want.
+ * @event postBroadphase
+ * @param {Array} pairs An array of collision pairs. If this array is [body1,body2,body3,body4], then the body pairs 1,2 and 3,4 would advance to narrowphase.
+ */
+ this.postBroadphaseEvent = {
+ type:"postBroadphase",
+ pairs:null,
+ };
+
+ /**
+ * Enable / disable automatic body sleeping
+ * @property allowSleep
+ * @type {Boolean}
+ */
+ this.enableBodySleeping = false;
+
+ /**
+ * Fired when two shapes starts start to touch (and create contacts).
+ * @event beginContact
+ * @param {Shape} shapeA
+ * @param {Shape} shapeB
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Array} contactEquations
+ */
+ this.beginContactEvent = {
+ type:"beginContact",
+ shapeA : null,
+ shapeB : null,
+ bodyA : null,
+ bodyB : null,
+ contactEquations : [],
+ };
+
+ /**
+ * Fired when two shapes stop touching.
+ * @event endContact
+ * @param {Shape} shapeA
+ * @param {Shape} shapeB
+ * @param {Body} bodyA
+ * @param {Body} bodyB
+ * @param {Array} contactEquations
+ */
+ this.endContactEvent = {
+ type:"endContact",
+ shapeA : null,
+ shapeB : null,
+ bodyA : null,
+ bodyB : null,
+ };
+
+ /**
+ * Fired just before equations are added to the solver to be solved. Can be used to control what equations goes into the solver.
+ * @event preSolve
+ * @param {Array} contactEquations An array of contacts to be solved.
+ * @param {Array} frictionEquations An array of friction equations to be solved.
+ */
+ this.preSolveEvent = {
+ type:"preSolve",
+ contactEquations:null,
+ frictionEquations:null,
+ };
+
+ // For keeping track of overlapping shapes
+ this.overlappingShapesLastState = { keys:[] };
+ this.overlappingShapesCurrentState = { keys:[] };
+ this.overlappingShapeLookup = { keys:[] };
+};
+World.prototype = new Object(EventEmitter.prototype);
+
+/**
+ * Add a constraint to the simulation.
+ *
+ * @method addConstraint
+ * @param {Constraint} c
+ */
+World.prototype.addConstraint = function(c){
+ this.constraints.push(c);
+};
+
+/**
+ * Add a ContactMaterial to the simulation.
+ * @method addContactMaterial
+ * @param {ContactMaterial} contactMaterial
+ */
+World.prototype.addContactMaterial = function(contactMaterial){
+ this.contactMaterials.push(contactMaterial);
+};
+
+/**
+ * Removes a contact material
+ *
+ * @method removeContactMaterial
+ * @param {ContactMaterial} cm
+ */
+World.prototype.removeContactMaterial = function(cm){
+ var idx = this.contactMaterials.indexOf(cm);
+ if(idx!==-1)
+ Utils.splice(this.contactMaterials,idx,1);
+};
+
+/**
+ * Get a contact material given two materials
+ * @method getContactMaterial
+ * @param {Material} materialA
+ * @param {Material} materialB
+ * @return {ContactMaterial} The matching ContactMaterial, or false on fail.
+ * @todo Use faster hash map to lookup from material id's
+ */
+World.prototype.getContactMaterial = function(materialA,materialB){
+ var cmats = this.contactMaterials;
+ for(var i=0, N=cmats.length; i!==N; i++){
+ var cm = cmats[i];
+ if( (cm.materialA === materialA) && (cm.materialB === materialB) ||
+ (cm.materialA === materialB) && (cm.materialB === materialA) )
+ return cm;
+ }
+ return false;
+};
+
+/**
+ * Removes a constraint
+ *
+ * @method removeConstraint
+ * @param {Constraint} c
+ */
+World.prototype.removeConstraint = function(c){
+ var idx = this.constraints.indexOf(c);
+ if(idx!==-1){
+ Utils.splice(this.constraints,idx,1);
+ }
+};
+
+var step_r = vec2.create(),
+ step_runit = vec2.create(),
+ step_u = vec2.create(),
+ step_f = vec2.create(),
+ step_fhMinv = vec2.create(),
+ step_velodt = vec2.create(),
+ step_mg = vec2.create(),
+ xiw = vec2.fromValues(0,0),
+ xjw = vec2.fromValues(0,0),
+ zero = vec2.fromValues(0,0);
+
+/**
+ * Step the physics world forward in time.
+ *
+ * There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take.
+ *
+ * @method step
+ * @param {Number} dt The fixed time step size to use.
+ * @param {Number} [timeSinceLastCalled=0] The time elapsed since the function was last called.
+ * @param {Number} [maxSubSteps=10] Maximum number of fixed steps to take per function call.
+ *
+ * @example
+ * // fixed timestepping without interpolation
+ * var world = new World();
+ * world.step(0.01);
+ */
+World.prototype.step = function(dt,timeSinceLastCalled,maxSubSteps){
+ maxSubSteps = maxSubSteps || 10;
+ timeSinceLastCalled = timeSinceLastCalled || 0;
+
+ if(timeSinceLastCalled == 0){ // Fixed, simple stepping
+
+ this.internalStep(dt);
+
+ // Increment time
+ this.time += dt;
+
+ } else {
+
+ var internalSteps = Math.floor( (this.time+timeSinceLastCalled) / dt) - Math.floor(this.time / dt);
+ internalSteps = Math.min(internalSteps,maxSubSteps);
+
+ for(var i=0; i0){
+ World.integrateBody(body,dt);
+ }
+ }
+
+ // Reset force
+ for(var i=0; i!==Nbodies; i++){
+ bodies[i].setZeroForce();
+ }
+
+ if(doProfiling){
+ t1 = performance.now();
+ that.lastStepTime = t1-t0;
+ }
+
+ // Emit impact event
+ if(this.emitImpactEvent){
+ var ev = this.impactEvent;
+ for(var i=0; i!==np.contactEquations.length; i++){
+ var eq = np.contactEquations[i];
+ if(eq.firstImpact){
+ ev.bodyA = eq.bi;
+ ev.bodyB = eq.bj;
+ ev.shapeA = eq.shapeA;
+ ev.shapeB = eq.shapeB;
+ ev.contactEquation = eq;
+ this.emit(ev);
+ }
+ }
+ }
+
+ // Sleeping update
+ if(this.enableBodySleeping){
+ for(i=0; i!==Nbodies; i++){
+ bodies[i].sleepTick(this.time);
+ }
+ }
+
+ this.emit(this.postStepEvent);
+};
+
+var ib_fhMinv = vec2.create();
+var ib_velodt = vec2.create();
+
+/**
+ * Move a body forward in time.
+ * @static
+ * @method integrateBody
+ * @param {Body} body
+ * @param {Number} dt
+ */
+World.integrateBody = function(body,dt){
+ var minv = body.invMass,
+ f = body.force,
+ pos = body.position,
+ velo = body.velocity;
+
+ // Angular step
+ if(!body.fixedRotation){
+ body.angularVelocity += body.angularForce * body.invInertia * dt;
+ body.angle += body.angularVelocity * dt;
+ }
+
+ // Linear step
+ vec2.scale(ib_fhMinv,f,dt*minv);
+ vec2.add(velo,ib_fhMinv,velo);
+ vec2.scale(ib_velodt,velo,dt);
+ vec2.add(pos,pos,ib_velodt);
+
+ body.aabbNeedsUpdate = true;
+};
+
+/**
+ * Runs narrowphase for the shape pair i and j.
+ * @method runNarrowphase
+ * @param {Narrowphase} np
+ * @param {Body} bi
+ * @param {Shape} si
+ * @param {Array} xi
+ * @param {Number} ai
+ * @param {Body} bj
+ * @param {Shape} sj
+ * @param {Array} xj
+ * @param {Number} aj
+ * @param {Number} mu
+ */
+World.prototype.runNarrowphase = function(np,bi,si,xi,ai,bj,sj,xj,aj,mu,restitution,surfaceVelocity){
+
+ if(!((si.collisionGroup & sj.collisionMask) !== 0 && (sj.collisionGroup & si.collisionMask) !== 0))
+ return;
+
+ var reducedMass = bi.invMass + bj.invMass;
+ if(reducedMass > 0)
+ reducedMass = 1/reducedMass;
+
+ // Get world position and angle of each shape
+ vec2.rotate(xiw, xi, bi.angle);
+ vec2.rotate(xjw, xj, bj.angle);
+ vec2.add(xiw, xiw, bi.position);
+ vec2.add(xjw, xjw, bj.position);
+ var aiw = ai + bi.angle;
+ var ajw = aj + bj.angle;
+
+ // Run narrowphase
+ np.enableFriction = mu > 0;
+ np.frictionCoefficient = mu;
+ np.restitution = restitution;
+ np.surfaceVelocity = surfaceVelocity;
+
+ var resolver = np[si.type | sj.type],
+ numContacts = 0;
+ if (resolver) {
+ var sensor = si.sensor || sj.sensor;
+ if (si.type < sj.type) {
+ numContacts = resolver.call(np, bi,si,xiw,aiw, bj,sj,xjw,ajw, sensor);
+ } else {
+ numContacts = resolver.call(np, bj,sj,xjw,ajw, bi,si,xiw,aiw, sensor);
+ }
+
+ if(numContacts){
+ var key = si.id < sj.id ? si.id+" "+ sj.id : sj.id+" "+ si.id;
+ if(!this.overlappingShapesLastState[key]){
+
+ // Report new shape overlap
+ var e = this.beginContactEvent;
+ e.shapeA = si;
+ e.shapeB = sj;
+ e.bodyA = bi;
+ e.bodyB = bj;
+
+ if(typeof(numContacts)=="number"){
+ // Add contacts to the event object
+ e.contactEquations.length = 0;
+ for(var i=np.contactEquations.length-numContacts; i=0; i--){
+ this.removeConstraint(cs[i]);
+ }
+
+ // Remove all bodies
+ var bodies = this.bodies;
+ for(var i=bodies.length-1; i>=0; i--){
+ this.removeBody(bodies[i]);
+ }
+
+ // Remove all springs
+ var springs = this.springs;
+ for(var i=springs.length-1; i>=0; i--){
+ this.removeSpring(springs[i]);
+ }
+
+ // Remove all contact materials
+ var cms = this.contactMaterials;
+ for(var i=cms.length-1; i>=0; i--){
+ this.removeContactMaterial(cms[i]);
+ }
+};
+
+/**
+ * Get a copy of this World instance
+ * @method clone
+ * @return {World}
+ */
+World.prototype.clone = function(){
+ var world = new World();
+ world.fromJSON(this.toJSON());
+ return world;
+};
+
+var hitTest_tmp1 = vec2.create(),
+ hitTest_zero = vec2.fromValues(0,0),
+ hitTest_tmp2 = vec2.fromValues(0,0);
+
+/**
+ * Test if a world point overlaps bodies
+ * @method hitTest
+ * @param {Array} worldPoint Point to use for intersection tests
+ * @param {Array} bodies A list of objects to check for intersection
+ * @param {Number} precision Used for matching against particles and lines. Adds some margin to these infinitesimal objects.
+ * @return {Array} Array of bodies that overlap the point
+ */
+World.prototype.hitTest = function(worldPoint,bodies,precision){
+ precision = precision || 0;
+
+ // Create a dummy particle body with a particle shape to test against the bodies
+ var pb = new Body({ position:worldPoint }),
+ ps = new Particle(),
+ px = worldPoint,
+ pa = 0,
+ x = hitTest_tmp1,
+ zero = hitTest_zero,
+ tmp = hitTest_tmp2;
+ pb.addShape(ps);
+
+ var n = this.narrowphase,
+ result = [];
+
+ // Check bodies
+ for(var i=0, N=bodies.length; i!==N; i++){
+ var b = bodies[i];
+ for(var j=0, NS=b.shapes.length; j!==NS; j++){
+ var s = b.shapes[j],
+ offset = b.shapeOffsets[j] || zero,
+ angle = b.shapeAngles[j] || 0.0;
+
+ // Get shape world position + angle
+ vec2.rotate(x, offset, b.angle);
+ vec2.add(x, x, b.position);
+ var a = angle + b.angle;
+
+ if( (s instanceof Circle && n.circleParticle (b,s,x,a, pb,ps,px,pa, true)) ||
+ (s instanceof Convex && n.particleConvex (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Plane && n.particlePlane (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Capsule && n.particleCapsule (pb,ps,px,pa, b,s,x,a, true)) ||
+ (s instanceof Particle && vec2.squaredLength(vec2.sub(tmp,x,worldPoint)) < precision*precision)
+ ){
+ result.push(b);
+ }
+ }
+ }
+
+ return result;
+};
+
+},{"../../package.json":8,"../collision/Broadphase":10,"../collision/NaiveBroadphase":12,"../collision/Narrowphase":13,"../constraints/DistanceConstraint":17,"../constraints/LockConstraint":19,"../constraints/PrismaticConstraint":20,"../constraints/RevoluteConstraint":21,"../events/EventEmitter":28,"../material/ContactMaterial":29,"../material/Material":30,"../math/vec2":33,"../objects/Body":34,"../objects/Spring":35,"../shapes/Capsule":37,"../shapes/Circle":38,"../shapes/Convex":39,"../shapes/Line":40,"../shapes/Particle":41,"../shapes/Plane":42,"../shapes/Rectangle":43,"../solver/GSSolver":45,"../utils/Utils":49}]},{},[36])
+(36)
+});
+;
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -18,7 +10246,7 @@
*
* Phaser - http://www.phaser.io
*
-* v1.1.4 - Built at: Wed Feb 05 2014 16:54:13
+* v1.2 - Built at: Wed Feb 19 2014 05:27:35
*
* By Richard Davey http://www.photonstorm.com @photonstorm
*
@@ -46,6 +10274,51 @@
*/
var PIXI = PIXI || {};
+/*
+*
+* This file contains a lot of pixi consts which are used across the rendering engine
+* @class Consts
+*/
+PIXI.WEBGL_RENDERER = 0;
+PIXI.CANVAS_RENDERER = 1;
+
+// useful for testing against if your lib is using pixi.
+PIXI.VERSION = "v1.5.0";
+
+// the various blend modes supported by pixi
+PIXI.blendModes = {
+ NORMAL:0,
+ ADD:1,
+ MULTIPLY:2,
+ SCREEN:3,
+ OVERLAY:4,
+ DARKEN:5,
+ LIGHTEN:6,
+ COLOR_DODGE:7,
+ COLOR_BURN:8,
+ HARD_LIGHT:9,
+ SOFT_LIGHT:10,
+ DIFFERENCE:11,
+ EXCLUSION:12,
+ HUE:13,
+ SATURATION:14,
+ COLOR:15,
+ LUMINOSITY:16
+};
+
+// the scale modes
+PIXI.scaleModes = {
+ DEFAULT:0,
+ LINEAR:0,
+ NEAREST:1
+};
+
+// interaction frequency
+PIXI.INTERACTION_FREQUENCY = 30;
+PIXI.AUTO_PREVENT_DEFAULT = true;
+
+PIXI.RAD_TO_DEG = 180 / Math.PI;
+PIXI.DEG_TO_RAD = Math.PI / 180;
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -57,8 +10330,8 @@ var PIXI = PIXI || {};
*/
var Phaser = Phaser || {
- VERSION: '1.1.4',
- DEV_VERSION: '1.1.4',
+ VERSION: '1.2',
+ DEV_VERSION: '1.2',
GAMES: [],
AUTO: 0,
@@ -68,7 +10341,7 @@ var Phaser = Phaser || {
SPRITE: 0,
BUTTON: 1,
- BULLET: 2,
+ IMAGE: 2,
GRAPHICS: 3,
TEXT: 4,
TILESPRITE: 5,
@@ -82,6 +10355,9 @@ var Phaser = Phaser || {
BITMAPDATA: 13,
CANVAS_FILTER: 14,
WEBGL_FILTER: 15,
+ ELLIPSE: 16,
+ SPRITEBATCH: 17,
+ BITMAPFONT: 18,
NONE: 0,
LEFT: 1,
@@ -89,10 +10365,14 @@ var Phaser = Phaser || {
UP: 3,
DOWN: 4,
+ DYNAMIC: 1,
+ STATIC: 2,
+ KINEMATIC: 4,
+
CANVAS_PX_ROUND: false,
CANVAS_CLEAR_RECT: true
- };
+};
PIXI.InteractionManager = function (dummy) {
// We don't need this in Pixi, so we've removed it to save space
@@ -139,9 +10419,9 @@ Phaser.Utils = {
* dir = 1 (left), 2 (right), 3 (both)
* @method Phaser.Utils.pad
* @param {string} str - The target string.
- * @param {number} len - Description.
- * @param {number} pad - the string to pad it out with (defaults to a space).
- * @param {number} [dir=3] the direction dir = 1 (left), 2 (right), 3 (both).
+ * @param {number} len - The number of characters to be added.
+ * @param {number} pad - The string to pad it out with (defaults to a space).
+ * @param {number} [dir=3] The direction dir = 1 (left), 2 (right), 3 (both).
* @return {string} The padded string
*/
pad: function (str, len, pad, dir) {
@@ -298,12 +10578,64 @@ Phaser.Utils = {
};
-function HEXtoRGB(hex) {
+/**
+ * Converts a hex color number to an [R, G, B] array
+ *
+ * @method hex2rgb
+ * @param hex {Number}
+ */
+PIXI.hex2rgb = function(hex) {
return [(hex >> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255];
-}
+};
-PIXI.hex2rgb = function hex2rgb(hex) {
- return [(hex >> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255];
+/**
+ * Converts a color as an [R, G, B] array to a hex number
+ *
+ * @method rgb2hex
+ * @param rgb {Array}
+ */
+PIXI.rgb2hex = function(rgb) {
+ return ((rgb[0]*255 << 16) + (rgb[1]*255 << 8) + rgb[2]*255);
+};
+
+/**
+ * Checks whether the Canvas BlendModes are supported by the current browser
+ *
+ * @method canUseNewCanvasBlendModes
+ * @return {Boolean} whether they are supported
+ */
+PIXI.canUseNewCanvasBlendModes = function()
+{
+ var canvas = document.createElement('canvas');
+ canvas.width = 1;
+ canvas.height = 1;
+ var context = canvas.getContext('2d');
+ context.fillStyle = '#000';
+ context.fillRect(0,0,1,1);
+ context.globalCompositeOperation = 'multiply';
+ context.fillStyle = '#fff';
+ context.fillRect(0,0,1,1);
+ return context.getImageData(0,0,1,1).data[0] === 0;
+};
+
+/**
+ * Given a number, this function returns the closest number that is a power of two
+ * this function is taken from Starling Framework as its pretty neat ;)
+ *
+ * @method getNextPowerOfTwo
+ * @param number {Number}
+ * @return {Number} the closest number that is a power of two
+ */
+PIXI.getNextPowerOfTwo = function(number)
+{
+ if (number > 0 && (number & (number - 1)) === 0) // see: http://goo.gl/D9kPj
+ return number;
+ else
+ {
+ var result = 1;
+ while (result < number) result <<= 1;
+ return result;
+ }
};
/**
@@ -353,509 +10685,2545 @@ if (!Array.isArray) {
}
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+/**
+* Creates a new Circle object with the center coordinate specified by the x and y parameters and the diameter specified by the diameter parameter. If you call this function without parameters, a circle with x, y, diameter and radius properties set to 0 is created.
+* @class Circle
+* @classdesc Phaser - Circle
+* @constructor
+* @param {number} [x=0] - The x coordinate of the center of the circle.
+* @param {number} [y=0] - The y coordinate of the center of the circle.
+* @param {number} [diameter=0] - The diameter of the circle.
+* @return {Phaser.Circle} This circle object
+*/
+Phaser.Circle = function (x, y, diameter) {
-/*
- * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV
- * you both rock!
- */
+ x = x || 0;
+ y = y || 0;
+ diameter = diameter || 0;
-function determineMatrixArrayType() {
- PIXI.Matrix = (typeof Float32Array !== 'undefined') ? Float32Array : Array;
- return PIXI.Matrix;
-}
+ /**
+ * @property {number} x - The x coordinate of the center of the circle.
+ */
+ this.x = x;
-determineMatrixArrayType();
+ /**
+ * @property {number} y - The y coordinate of the center of the circle.
+ */
+ this.y = y;
-PIXI.mat3 = {};
+ /**
+ * @property {number} _diameter - The diameter of the circle.
+ * @private
+ */
+ this._diameter = diameter;
-PIXI.mat3.create = function()
-{
- var matrix = new PIXI.Matrix(9);
-
- matrix[0] = 1;
- matrix[1] = 0;
- matrix[2] = 0;
- matrix[3] = 0;
- matrix[4] = 1;
- matrix[5] = 0;
- matrix[6] = 0;
- matrix[7] = 0;
- matrix[8] = 1;
-
- return matrix;
-};
-
-
-PIXI.mat3.identity = function(matrix)
-{
- matrix[0] = 1;
- matrix[1] = 0;
- matrix[2] = 0;
- matrix[3] = 0;
- matrix[4] = 1;
- matrix[5] = 0;
- matrix[6] = 0;
- matrix[7] = 0;
- matrix[8] = 1;
-
- return matrix;
-};
-
-
-PIXI.mat4 = {};
-
-PIXI.mat4.create = function()
-{
- var matrix = new PIXI.Matrix(16);
-
- matrix[0] = 1;
- matrix[1] = 0;
- matrix[2] = 0;
- matrix[3] = 0;
- matrix[4] = 0;
- matrix[5] = 1;
- matrix[6] = 0;
- matrix[7] = 0;
- matrix[8] = 0;
- matrix[9] = 0;
- matrix[10] = 1;
- matrix[11] = 0;
- matrix[12] = 0;
- matrix[13] = 0;
- matrix[14] = 0;
- matrix[15] = 1;
-
- return matrix;
-};
-
-PIXI.mat3.multiply = function (mat, mat2, dest)
-{
- if (!dest) { dest = mat; }
-
- // Cache the matrix values (makes for huge speed increases!)
- var a00 = mat[0], a01 = mat[1], a02 = mat[2],
- a10 = mat[3], a11 = mat[4], a12 = mat[5],
- a20 = mat[6], a21 = mat[7], a22 = mat[8],
-
- b00 = mat2[0], b01 = mat2[1], b02 = mat2[2],
- b10 = mat2[3], b11 = mat2[4], b12 = mat2[5],
- b20 = mat2[6], b21 = mat2[7], b22 = mat2[8];
-
- dest[0] = b00 * a00 + b01 * a10 + b02 * a20;
- dest[1] = b00 * a01 + b01 * a11 + b02 * a21;
- dest[2] = b00 * a02 + b01 * a12 + b02 * a22;
-
- dest[3] = b10 * a00 + b11 * a10 + b12 * a20;
- dest[4] = b10 * a01 + b11 * a11 + b12 * a21;
- dest[5] = b10 * a02 + b11 * a12 + b12 * a22;
-
- dest[6] = b20 * a00 + b21 * a10 + b22 * a20;
- dest[7] = b20 * a01 + b21 * a11 + b22 * a21;
- dest[8] = b20 * a02 + b21 * a12 + b22 * a22;
-
- return dest;
-};
-
-PIXI.mat3.clone = function(mat)
-{
- var matrix = new PIXI.Matrix(9);
-
- matrix[0] = mat[0];
- matrix[1] = mat[1];
- matrix[2] = mat[2];
- matrix[3] = mat[3];
- matrix[4] = mat[4];
- matrix[5] = mat[5];
- matrix[6] = mat[6];
- matrix[7] = mat[7];
- matrix[8] = mat[8];
-
- return matrix;
-};
-
-PIXI.mat3.transpose = function (mat, dest)
-{
- // If we are transposing ourselves we can skip a few steps but have to cache some values
- if (!dest || mat === dest) {
- var a01 = mat[1], a02 = mat[2],
- a12 = mat[5];
-
- mat[1] = mat[3];
- mat[2] = mat[6];
- mat[3] = a01;
- mat[5] = mat[7];
- mat[6] = a02;
- mat[7] = a12;
- return mat;
+ if (diameter > 0)
+ {
+ /**
+ * @property {number} _radius - The radius of the circle.
+ * @private
+ */
+ this._radius = diameter * 0.5;
+ }
+ else
+ {
+ this._radius = 0;
}
- dest[0] = mat[0];
- dest[1] = mat[3];
- dest[2] = mat[6];
- dest[3] = mat[1];
- dest[4] = mat[4];
- dest[5] = mat[7];
- dest[6] = mat[2];
- dest[7] = mat[5];
- dest[8] = mat[8];
- return dest;
};
-PIXI.mat3.toMat4 = function (mat, dest)
-{
- if (!dest) { dest = PIXI.mat4.create(); }
-
- dest[15] = 1;
- dest[14] = 0;
- dest[13] = 0;
- dest[12] = 0;
-
- dest[11] = 0;
- dest[10] = mat[8];
- dest[9] = mat[7];
- dest[8] = mat[6];
-
- dest[7] = 0;
- dest[6] = mat[5];
- dest[5] = mat[4];
- dest[4] = mat[3];
-
- dest[3] = 0;
- dest[2] = mat[2];
- dest[1] = mat[1];
- dest[0] = mat[0];
-
- return dest;
-};
-
-
-/////
-
-
-PIXI.mat4.create = function()
-{
- var matrix = new PIXI.Matrix(16);
-
- matrix[0] = 1;
- matrix[1] = 0;
- matrix[2] = 0;
- matrix[3] = 0;
- matrix[4] = 0;
- matrix[5] = 1;
- matrix[6] = 0;
- matrix[7] = 0;
- matrix[8] = 0;
- matrix[9] = 0;
- matrix[10] = 1;
- matrix[11] = 0;
- matrix[12] = 0;
- matrix[13] = 0;
- matrix[14] = 0;
- matrix[15] = 1;
-
- return matrix;
-};
-
-PIXI.mat4.transpose = function (mat, dest)
-{
- // If we are transposing ourselves we can skip a few steps but have to cache some values
- if (!dest || mat === dest)
- {
- var a01 = mat[1], a02 = mat[2], a03 = mat[3],
- a12 = mat[6], a13 = mat[7],
- a23 = mat[11];
-
- mat[1] = mat[4];
- mat[2] = mat[8];
- mat[3] = mat[12];
- mat[4] = a01;
- mat[6] = mat[9];
- mat[7] = mat[13];
- mat[8] = a02;
- mat[9] = a12;
- mat[11] = mat[14];
- mat[12] = a03;
- mat[13] = a13;
- mat[14] = a23;
- return mat;
- }
-
- dest[0] = mat[0];
- dest[1] = mat[4];
- dest[2] = mat[8];
- dest[3] = mat[12];
- dest[4] = mat[1];
- dest[5] = mat[5];
- dest[6] = mat[9];
- dest[7] = mat[13];
- dest[8] = mat[2];
- dest[9] = mat[6];
- dest[10] = mat[10];
- dest[11] = mat[14];
- dest[12] = mat[3];
- dest[13] = mat[7];
- dest[14] = mat[11];
- dest[15] = mat[15];
- return dest;
-};
-
-PIXI.mat4.multiply = function (mat, mat2, dest)
-{
- if (!dest) { dest = mat; }
-
- // Cache the matrix values (makes for huge speed increases!)
- var a00 = mat[ 0], a01 = mat[ 1], a02 = mat[ 2], a03 = mat[3];
- var a10 = mat[ 4], a11 = mat[ 5], a12 = mat[ 6], a13 = mat[7];
- var a20 = mat[ 8], a21 = mat[ 9], a22 = mat[10], a23 = mat[11];
- var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15];
-
- // Cache only the current line of the second matrix
- var b0 = mat2[0], b1 = mat2[1], b2 = mat2[2], b3 = mat2[3];
- dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
- dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
- dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
- dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-
- b0 = mat2[4];
- b1 = mat2[5];
- b2 = mat2[6];
- b3 = mat2[7];
- dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
- dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
- dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
- dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-
- b0 = mat2[8];
- b1 = mat2[9];
- b2 = mat2[10];
- b3 = mat2[11];
- dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
- dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
- dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
- dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-
- b0 = mat2[12];
- b1 = mat2[13];
- b2 = mat2[14];
- b3 = mat2[15];
- dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
- dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
- dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
- dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
-
- return dest;
-};
-
-/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
- */
-
-/**
- * The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis.
- *
- * @class Point
- * @constructor
- * @param x {Number} position of the point
- * @param y {Number} position of the point
- */
-PIXI.Point = function(x, y)
-{
- /**
- * @property x
- * @type Number
- * @default 0
- */
- this.x = x || 0;
+Phaser.Circle.prototype = {
/**
- * @property y
- * @type Number
- * @default 0
- */
- this.y = y || 0;
-};
-
-/**
- * Creates a clone of this point
- *
- * @method clone
- * @return {Point} a copy of the point
- */
-PIXI.Point.prototype.clone = function()
-{
- return new PIXI.Point(this.x, this.y);
-};
-
-// constructor
-PIXI.Point.prototype.constructor = PIXI.Point;
-
-
-/**
- * @author Mat Groves http://matgroves.com/
- */
-
-/**
- * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height.
- *
- * @class Rectangle
- * @constructor
- * @param x {Number} The X coord of the upper-left corner of the rectangle
- * @param y {Number} The Y coord of the upper-left corner of the rectangle
- * @param width {Number} The overall width of this rectangle
- * @param height {Number} The overall height of this rectangle
- */
-PIXI.Rectangle = function(x, y, width, height)
-{
- /**
- * @property x
- * @type Number
- * @default 0
- */
- this.x = x || 0;
+ * The circumference of the circle.
+ * @method Phaser.Circle#circumference
+ * @return {number}
+ */
+ circumference: function () {
+ return 2 * (Math.PI * this._radius);
+ },
/**
- * @property y
- * @type Number
- * @default 0
- */
- this.y = y || 0;
+ * Sets the members of Circle to the specified values.
+ * @method Phaser.Circle#setTo
+ * @param {number} x - The x coordinate of the center of the circle.
+ * @param {number} y - The y coordinate of the center of the circle.
+ * @param {number} diameter - The diameter of the circle in pixels.
+ * @return {Circle} This circle object.
+ */
+ setTo: function (x, y, diameter) {
+
+ this.x = x;
+ this.y = y;
+ this._diameter = diameter;
+ this._radius = diameter * 0.5;
+
+ return this;
+
+ },
/**
- * @property width
- * @type Number
- * @default 0
- */
- this.width = width || 0;
+ * Copies the x, y and diameter properties from any given object to this Circle.
+ * @method Phaser.Circle#copyFrom
+ * @param {any} source - The object to copy from.
+ * @return {Circle} This Circle object.
+ */
+ copyFrom: function (source) {
+
+ return this.setTo(source.x, source.y, source.diameter);
+
+ },
/**
- * @property height
- * @type Number
- * @default 0
- */
- this.height = height || 0;
-};
+ * Copies the x, y and diameter properties from this Circle to any given object.
+ * @method Phaser.Circle#copyTo
+ * @param {any} dest - The object to copy to.
+ * @return {Object} This dest object.
+ */
+ copyTo: function (dest) {
-/**
- * Creates a clone of this Rectangle
- *
- * @method clone
- * @return {Rectangle} a copy of the rectangle
- */
-PIXI.Rectangle.prototype.clone = function()
-{
- return new PIXI.Rectangle(this.x, this.y, this.width, this.height);
-};
+ dest.x = this.x;
+ dest.y = this.y;
+ dest.diameter = this._diameter;
-/**
- * Checks if the x, and y coords passed to this function are contained within this Rectangle
- *
- * @method contains
- * @param x {Number} The X coord of the point to test
- * @param y {Number} The Y coord of the point to test
- * @return {Boolean} if the x/y coords are within this Rectangle
- */
-PIXI.Rectangle.prototype.contains = function(x, y)
-{
- if(this.width <= 0 || this.height <= 0)
- return false;
+ return dest;
- var x1 = this.x;
- if(x >= x1 && x <= x1 + this.width)
- {
- var y1 = this.y;
+ },
- if(y >= y1 && y <= y1 + this.height)
+ /**
+ * Returns the distance from the center of the Circle object to the given object
+ * (can be Circle, Point or anything with x/y properties)
+ * @method Phaser.Circle#distance
+ * @param {object} dest - The target object. Must have visible x and y properties that represent the center of the object.
+ * @param {boolean} [round] - Round the distance to the nearest integer (default false).
+ * @return {number} The distance between this Point object and the destination Point object.
+ */
+ distance: function (dest, round) {
+
+ if (typeof round === "undefined") { round = false }
+
+ if (round)
{
- return true;
+ return Phaser.Math.distanceRound(this.x, this.y, dest.x, dest.y);
+ }
+ else
+ {
+ return Phaser.Math.distance(this.x, this.y, dest.x, dest.y);
+ }
+
+ },
+
+ /**
+ * Returns a new Circle object with the same values for the x, y, width, and height properties as this Circle object.
+ * @method Phaser.Circle#clone
+ * @param {Phaser.Circle} out - Optional Circle object. If given the values will be set into the object, otherwise a brand new Circle object will be created and returned.
+ * @return {Phaser.Circle} The cloned Circle object.
+ */
+ clone: function (out) {
+
+ if (typeof out === "undefined")
+ {
+ out = new Phaser.Circle(this.x, this.y, this.diameter);
+ }
+ else
+ {
+ out.setTo(this.x, this.y, this.diameter);
+ }
+
+ return out;
+
+ },
+
+ /**
+ * Return true if the given x/y coordinates are within this Circle object.
+ * @method Phaser.Circle#contains
+ * @param {number} x - The X value of the coordinate to test.
+ * @param {number} y - The Y value of the coordinate to test.
+ * @return {boolean} True if the coordinates are within this circle, otherwise false.
+ */
+ contains: function (x, y) {
+
+ return Phaser.Circle.contains(this, x, y);
+
+ },
+
+ /**
+ * Returns a Point object containing the coordinates of a point on the circumference of the Circle based on the given angle.
+ * @method Phaser.Circle#circumferencePoint
+ * @param {number} angle - The angle in radians (unless asDegrees is true) to return the point from.
+ * @param {boolean} asDegrees - Is the given angle in radians (false) or degrees (true)?
+ * @param {Phaser.Point} [out] - An optional Point object to put the result in to. If none specified a new Point object will be created.
+ * @return {Phaser.Point} The Point object holding the result.
+ */
+ circumferencePoint: function (angle, asDegrees, out) {
+
+ return Phaser.Circle.circumferencePoint(this, angle, asDegrees, out);
+
+ },
+
+ /**
+ * Adjusts the location of the Circle object, as determined by its center coordinate, by the specified amounts.
+ * @method Phaser.Circle#offset
+ * @param {number} dx - Moves the x value of the Circle object by this amount.
+ * @param {number} dy - Moves the y value of the Circle object by this amount.
+ * @return {Circle} This Circle object.
+ */
+ offset: function (dx, dy) {
+
+ this.x += dx;
+ this.y += dy;
+
+ return this;
+
+ },
+
+ /**
+ * Adjusts the location of the Circle object using a Point object as a parameter. This method is similar to the Circle.offset() method, except that it takes a Point object as a parameter.
+ * @method Phaser.Circle#offsetPoint
+ * @param {Point} point A Point object to use to offset this Circle object (or any valid object with exposed x and y properties).
+ * @return {Circle} This Circle object.
+ */
+ offsetPoint: function (point) {
+ return this.offset(point.x, point.y);
+ },
+
+ /**
+ * Returns a string representation of this object.
+ * @method Phaser.Circle#toString
+ * @return {string} a string representation of the instance.
+ */
+ toString: function () {
+ return "[{Phaser.Circle (x=" + this.x + " y=" + this.y + " diameter=" + this.diameter + " radius=" + this.radius + ")}]";
+ }
+
+};
+
+Phaser.Circle.prototype.constructor = Phaser.Circle;
+
+/**
+* The largest distance between any two points on the circle. The same as the radius * 2.
+* @name Phaser.Circle#diameter
+* @property {number} diameter - Gets or sets the diameter of the circle.
+*/
+Object.defineProperty(Phaser.Circle.prototype, "diameter", {
+
+ get: function () {
+ return this._diameter;
+ },
+
+ set: function (value) {
+
+ if (value > 0)
+ {
+ this._diameter = value;
+ this._radius = value * 0.5;
}
}
- return false;
+});
+
+/**
+* The length of a line extending from the center of the circle to any point on the circle itself. The same as half the diameter.
+* @name Phaser.Circle#radius
+* @property {number} radius - Gets or sets the radius of the circle.
+*/
+Object.defineProperty(Phaser.Circle.prototype, "radius", {
+
+ get: function () {
+ return this._radius;
+ },
+
+ set: function (value) {
+
+ if (value > 0)
+ {
+ this._radius = value;
+ this._diameter = value * 2;
+ }
+
+ }
+
+});
+
+/**
+* The x coordinate of the leftmost point of the circle. Changing the left property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
+* @name Phaser.Circle#left
+* @propety {number} left - Gets or sets the value of the leftmost point of the circle.
+*/
+Object.defineProperty(Phaser.Circle.prototype, "left", {
+
+ get: function () {
+ return this.x - this._radius;
+ },
+
+ set: function (value) {
+
+ if (value > this.x)
+ {
+ this._radius = 0;
+ this._diameter = 0;
+ }
+ else
+ {
+ this.radius = this.x - value;
+ }
+
+ }
+
+});
+
+/**
+* The x coordinate of the rightmost point of the circle. Changing the right property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
+* @name Phaser.Circle#right
+* @property {number} right - Gets or sets the value of the rightmost point of the circle.
+*/
+Object.defineProperty(Phaser.Circle.prototype, "right", {
+
+ get: function () {
+ return this.x + this._radius;
+ },
+
+ set: function (value) {
+
+ if (value < this.x)
+ {
+ this._radius = 0;
+ this._diameter = 0;
+ }
+ else
+ {
+ this.radius = value - this.x;
+ }
+
+ }
+
+});
+
+/**
+* The sum of the y minus the radius property. Changing the top property of a Circle object has no effect on the x and y properties, but does change the diameter.
+* @name Phaser.Circle#top
+* @property {number} top - Gets or sets the top of the circle.
+*/
+Object.defineProperty(Phaser.Circle.prototype, "top", {
+
+ get: function () {
+ return this.y - this._radius;
+ },
+
+ set: function (value) {
+
+ if (value > this.y)
+ {
+ this._radius = 0;
+ this._diameter = 0;
+ }
+ else
+ {
+ this.radius = this.y - value;
+ }
+
+ }
+
+});
+
+/**
+* The sum of the y and radius properties. Changing the bottom property of a Circle object has no effect on the x and y properties, but does change the diameter.
+* @name Phaser.Circle#bottom
+* @property {number} bottom - Gets or sets the bottom of the circle.
+*/
+Object.defineProperty(Phaser.Circle.prototype, "bottom", {
+
+ get: function () {
+ return this.y + this._radius;
+ },
+
+ set: function (value) {
+
+ if (value < this.y)
+ {
+ this._radius = 0;
+ this._diameter = 0;
+ }
+ else
+ {
+ this.radius = value - this.y;
+ }
+
+ }
+
+});
+
+/**
+* The area of this Circle.
+* @name Phaser.Circle#area
+* @property {number} area - The area of this circle.
+* @readonly
+*/
+Object.defineProperty(Phaser.Circle.prototype, "area", {
+
+ get: function () {
+
+ if (this._radius > 0)
+ {
+ return Math.PI * this._radius * this._radius;
+ }
+ else
+ {
+ return 0;
+ }
+
+ }
+
+});
+
+/**
+* Determines whether or not this Circle object is empty. Will return a value of true if the Circle objects diameter is less than or equal to 0; otherwise false.
+* If set to true it will reset all of the Circle objects properties to 0. A Circle object is empty if its diameter is less than or equal to 0.
+* @name Phaser.Circle#empty
+* @property {boolean} empty - Gets or sets the empty state of the circle.
+*/
+Object.defineProperty(Phaser.Circle.prototype, "empty", {
+
+ get: function () {
+ return (this._diameter === 0);
+ },
+
+ set: function (value) {
+
+ if (value === true)
+ {
+ this.setTo(0, 0, 0);
+ }
+
+ }
+
+});
+
+/**
+* Return true if the given x/y coordinates are within the Circle object.
+* @method Phaser.Circle.contains
+* @param {Phaser.Circle} a - The Circle to be checked.
+* @param {number} x - The X value of the coordinate to test.
+* @param {number} y - The Y value of the coordinate to test.
+* @return {boolean} True if the coordinates are within this circle, otherwise false.
+*/
+Phaser.Circle.contains = function (a, x, y) {
+
+ // Check if x/y are within the bounds first
+ if (a.radius > 0 && x >= a.left && x <= a.right && y >= a.top && y <= a.bottom)
+ {
+ var dx = (a.x - x) * (a.x - x);
+ var dy = (a.y - y) * (a.y - y);
+
+ return (dx + dy) <= (a.radius * a.radius);
+ }
+ else
+ {
+ return false;
+ }
+
};
-// constructor
-PIXI.Rectangle.prototype.constructor = PIXI.Rectangle;
-
+/**
+* Determines whether the two Circle objects match. This method compares the x, y and diameter properties.
+* @method Phaser.Circle.equals
+* @param {Phaser.Circle} a - The first Circle object.
+* @param {Phaser.Circle} b - The second Circle object.
+* @return {boolean} A value of true if the object has exactly the same values for the x, y and diameter properties as this Circle object; otherwise false.
+*/
+Phaser.Circle.equals = function (a, b) {
+ return (a.x == b.x && a.y == b.y && a.diameter == b.diameter);
+};
/**
- * @author Adrien Brault
- */
+* Determines whether the two Circle objects intersect.
+* This method checks the radius distances between the two Circle objects to see if they intersect.
+* @method Phaser.Circle.intersects
+* @param {Phaser.Circle} a - The first Circle object.
+* @param {Phaser.Circle} b - The second Circle object.
+* @return {boolean} A value of true if the specified object intersects with this Circle object; otherwise false.
+*/
+Phaser.Circle.intersects = function (a, b) {
+ return (Phaser.Math.distance(a.x, a.y, b.x, b.y) <= (a.radius + b.radius));
+};
/**
- * @class Polygon
- * @constructor
- * @param points* {Array|Array|Point...|Number...} This can be an array of Points that form the polygon,
- * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be
- * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the
- * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are
- * Numbers.
- */
-PIXI.Polygon = function(points)
-{
+* Returns a Point object containing the coordinates of a point on the circumference of the Circle based on the given angle.
+* @method Phaser.Circle.circumferencePoint
+* @param {Phaser.Circle} a - The first Circle object.
+* @param {number} angle - The angle in radians (unless asDegrees is true) to return the point from.
+* @param {boolean} asDegrees - Is the given angle in radians (false) or degrees (true)?
+* @param {Phaser.Point} [out] - An optional Point object to put the result in to. If none specified a new Point object will be created.
+* @return {Phaser.Point} The Point object holding the result.
+*/
+Phaser.Circle.circumferencePoint = function (a, angle, asDegrees, out) {
+
+ if (typeof asDegrees === "undefined") { asDegrees = false; }
+ if (typeof out === "undefined") { out = new Phaser.Point(); }
+
+ if (asDegrees === true)
+ {
+ angle = Phaser.Math.radToDeg(angle);
+ }
+
+ out.x = a.x + a.radius * Math.cos(angle);
+ out.y = a.y + a.radius * Math.sin(angle);
+
+ return out;
+
+};
+
+/**
+* Checks if the given Circle and Rectangle objects intersect.
+* @method Phaser.Circle.intersectsRectangle
+* @param {Phaser.Circle} c - The Circle object to test.
+* @param {Phaser.Rectangle} r - The Rectangle object to test.
+* @return {boolean} True if the two objects intersect, otherwise false.
+*/
+Phaser.Circle.intersectsRectangle = function (c, r) {
+
+ var cx = Math.abs(c.x - r.x - r.halfWidth);
+ var xDist = r.halfWidth + c.radius;
+
+ if (cx > xDist)
+ {
+ return false;
+ }
+
+ var cy = Math.abs(c.y - r.y - r.halfHeight);
+ var yDist = r.halfHeight + c.radius;
+
+ if (cy > yDist)
+ {
+ return false;
+ }
+
+ if (cx <= r.halfWidth || cy <= r.halfHeight)
+ {
+ return true;
+ }
+
+ var xCornerDist = cx - r.halfWidth;
+ var yCornerDist = cy - r.halfHeight;
+ var xCornerDistSq = xCornerDist * xCornerDist;
+ var yCornerDistSq = yCornerDist * yCornerDist;
+ var maxCornerDistSq = c.radius * c.radius;
+
+ return xCornerDistSq + yCornerDistSq <= maxCornerDistSq;
+
+};
+
+// Because PIXI uses its own Circle, we'll replace it with ours to avoid duplicating code or confusion.
+PIXI.Circle = Phaser.Circle;
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Creates a new Point. If you pass no parameters a Point is created set to (0,0).
+* @class Phaser.Point
+* @classdesc The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis.
+* @constructor
+* @param {number} x The horizontal position of this Point (default 0)
+* @param {number} y The vertical position of this Point (default 0)
+*/
+Phaser.Point = function (x, y) {
+
+ x = x || 0;
+ y = y || 0;
+
+ /**
+ * @property {number} x - The x coordinate of the point.
+ */
+ this.x = x;
+
+ /**
+ * @property {number} y - The y coordinate of the point.
+ */
+ this.y = y;
+
+};
+
+Phaser.Point.prototype = {
+
+ /**
+ * Copies the x and y properties from any given object to this Point.
+ * @method Phaser.Point#copyFrom
+ * @param {any} source - The object to copy from.
+ * @return {Point} This Point object.
+ */
+ copyFrom: function (source) {
+ return this.setTo(source.x, source.y);
+ },
+
+ /**
+ * Inverts the x and y values of this Point
+ * @method Phaser.Point#invert
+ * @return {Point} This Point object.
+ */
+ invert: function () {
+ return this.setTo(this.y, this.x);
+ },
+
+ /**
+ * Sets the x and y values of this Point object to the given coordinates.
+ * @method Phaser.Point#setTo
+ * @param {number} x - The horizontal position of this point.
+ * @param {number} y - The vertical position of this point.
+ * @return {Point} This Point object. Useful for chaining method calls.
+ */
+ setTo: function (x, y) {
+
+ this.x = x || 0;
+ this.y = y || ( (y !== 0) ? this.x : 0 );
+
+ return this;
+
+ },
+
+ /**
+ * Sets the x and y values of this Point object to the given coordinates.
+ * @method Phaser.Point#set
+ * @param {number} x - The horizontal position of this point.
+ * @param {number} y - The vertical position of this point.
+ * @return {Point} This Point object. Useful for chaining method calls.
+ */
+ set: function (x, y) {
+
+ this.x = x || 0;
+ this.y = y || ( (y !== 0) ? this.x : 0 );
+
+ return this;
+
+ },
+
+ /**
+ * Adds the given x and y values to this Point.
+ * @method Phaser.Point#add
+ * @param {number} x - The value to add to Point.x.
+ * @param {number} y - The value to add to Point.y.
+ * @return {Phaser.Point} This Point object. Useful for chaining method calls.
+ */
+ add: function (x, y) {
+
+ this.x += x;
+ this.y += y;
+ return this;
+
+ },
+
+ /**
+ * Subtracts the given x and y values from this Point.
+ * @method Phaser.Point#subtract
+ * @param {number} x - The value to subtract from Point.x.
+ * @param {number} y - The value to subtract from Point.y.
+ * @return {Phaser.Point} This Point object. Useful for chaining method calls.
+ */
+ subtract: function (x, y) {
+
+ this.x -= x;
+ this.y -= y;
+ return this;
+
+ },
+
+ /**
+ * Multiplies Point.x and Point.y by the given x and y values.
+ * @method Phaser.Point#multiply
+ * @param {number} x - The value to multiply Point.x by.
+ * @param {number} y - The value to multiply Point.x by.
+ * @return {Phaser.Point} This Point object. Useful for chaining method calls.
+ */
+ multiply: function (x, y) {
+
+ this.x *= x;
+ this.y *= y;
+ return this;
+
+ },
+
+ /**
+ * Divides Point.x and Point.y by the given x and y values.
+ * @method Phaser.Point#divide
+ * @param {number} x - The value to divide Point.x by.
+ * @param {number} y - The value to divide Point.x by.
+ * @return {Phaser.Point} This Point object. Useful for chaining method calls.
+ */
+ divide: function (x, y) {
+
+ this.x /= x;
+ this.y /= y;
+ return this;
+
+ },
+
+ /**
+ * Clamps the x value of this Point to be between the given min and max.
+ * @method Phaser.Point#clampX
+ * @param {number} min - The minimum value to clamp this Point to.
+ * @param {number} max - The maximum value to clamp this Point to.
+ * @return {Phaser.Point} This Point object.
+ */
+ clampX: function (min, max) {
+
+ this.x = Phaser.Math.clamp(this.x, min, max);
+ return this;
+
+ },
+
+ /**
+ * Clamps the y value of this Point to be between the given min and max
+ * @method Phaser.Point#clampY
+ * @param {number} min - The minimum value to clamp this Point to.
+ * @param {number} max - The maximum value to clamp this Point to.
+ * @return {Phaser.Point} This Point object.
+ */
+ clampY: function (min, max) {
+
+ this.y = Phaser.Math.clamp(this.y, min, max);
+ return this;
+
+ },
+
+ /**
+ * Clamps this Point object values to be between the given min and max.
+ * @method Phaser.Point#clamp
+ * @param {number} min - The minimum value to clamp this Point to.
+ * @param {number} max - The maximum value to clamp this Point to.
+ * @return {Phaser.Point} This Point object.
+ */
+ clamp: function (min, max) {
+
+ this.x = Phaser.Math.clamp(this.x, min, max);
+ this.y = Phaser.Math.clamp(this.y, min, max);
+ return this;
+
+ },
+
+ /**
+ * Creates a copy of the given Point.
+ * @method Phaser.Point#clone
+ * @param {Phaser.Point} [output] Optional Point object. If given the values will be set into this object, otherwise a brand new Point object will be created and returned.
+ * @return {Phaser.Point} The new Point object.
+ */
+ clone: function (output) {
+
+ if (typeof output === "undefined")
+ {
+ output = new Phaser.Point(this.x, this.y);
+ }
+ else
+ {
+ output.setTo(this.x, this.y);
+ }
+
+ return output;
+
+ },
+
+ /**
+ * Copies the x and y properties from this Point to any given object.
+ * @method Phaser.Point#copyTo
+ * @param {any} dest - The object to copy to.
+ * @return {Object} The dest object.
+ */
+ copyTo: function(dest) {
+
+ dest.x = this.x;
+ dest.y = this.y;
+
+ return dest;
+
+ },
+
+ /**
+ * Returns the distance of this Point object to the given object (can be a Circle, Point or anything with x/y properties)
+ * @method Phaser.Point#distance
+ * @param {object} dest - The target object. Must have visible x and y properties that represent the center of the object.
+ * @param {boolean} [round] - Round the distance to the nearest integer (default false).
+ * @return {number} The distance between this Point object and the destination Point object.
+ */
+ distance: function (dest, round) {
+ return Phaser.Point.distance(this, dest, round);
+ },
+
+ /**
+ * Determines whether the given objects x/y values are equal to this Point object.
+ * @method Phaser.Point#equals
+ * @param {Phaser.Point} a - The first object to compare.
+ * @return {boolean} A value of true if the Points are equal, otherwise false.
+ */
+ equals: function (a) {
+ return (a.x == this.x && a.y == this.y);
+ },
+
+ /**
+ * Rotates this Point around the x/y coordinates given to the desired angle.
+ * @method Phaser.Point#rotate
+ * @param {number} x - The x coordinate of the anchor point
+ * @param {number} y - The y coordinate of the anchor point
+ * @param {number} angle - The angle in radians (unless asDegrees is true) to rotate the Point to.
+ * @param {boolean} asDegrees - Is the given rotation in radians (false) or degrees (true)?
+ * @param {number} [distance] - An optional distance constraint between the Point and the anchor.
+ * @return {Phaser.Point} The modified point object.
+ */
+ rotate: function (x, y, angle, asDegrees, distance) {
+ return Phaser.Point.rotate(this, x, y, angle, asDegrees, distance);
+ },
+
+ /**
+ * Calculates the length of the vector
+ * @method Phaser.Point#getMagnitude
+ * @return {number} the length of the vector
+ */
+ getMagnitude: function() {
+ return Math.sqrt((this.x * this.x) + (this.y * this.y));
+ },
+
+ /**
+ * Alters the length of the vector without changing the direction
+ * @method Phaser.Point#setMagnitude
+ * @param {number} magnitude the desired magnitude of the resulting vector
+ * @return {Phaser.Point} the modified original vector
+ */
+ setMagnitude: function(magnitude) {
+ return this.normalize().multiply(magnitude, magnitude);
+ },
+
+ /**
+ * Alters the vector so that its length is 1, but it retains the same direction
+ * @method Phaser.Point#normalize
+ * @return {Phaser.Point} the modified original vector
+ */
+ normalize: function() {
+
+ if(!this.isZero()) {
+ var m = this.getMagnitude();
+ this.x /= m;
+ this.y /= m;
+ }
+
+ return this;
+
+ },
+
+ /**
+ * Determine if this point is at 0,0
+ * @method Phaser.Point#isZero
+ * @return {boolean} True if this Point is 0,0, otherwise false
+ */
+ isZero: function() {
+ return (this.x === 0 && this.y === 0);
+ },
+
+ /**
+ * Returns a string representation of this object.
+ * @method Phaser.Point#toString
+ * @return {string} A string representation of the instance.
+ */
+ toString: function () {
+ return '[{Point (x=' + this.x + ' y=' + this.y + ')}]';
+ }
+
+};
+
+Phaser.Point.prototype.constructor = Phaser.Point;
+
+/**
+* Adds the coordinates of two points together to create a new point.
+* @method Phaser.Point.add
+* @param {Phaser.Point} a - The first Point object.
+* @param {Phaser.Point} b - The second Point object.
+* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
+* @return {Phaser.Point} The new Point object.
+*/
+Phaser.Point.add = function (a, b, out) {
+
+ if (typeof out === "undefined") { out = new Phaser.Point(); }
+
+ out.x = a.x + b.x;
+ out.y = a.y + b.y;
+
+ return out;
+
+};
+
+/**
+* Subtracts the coordinates of two points to create a new point.
+* @method Phaser.Point.subtract
+* @param {Phaser.Point} a - The first Point object.
+* @param {Phaser.Point} b - The second Point object.
+* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
+* @return {Phaser.Point} The new Point object.
+*/
+Phaser.Point.subtract = function (a, b, out) {
+
+ if (typeof out === "undefined") { out = new Phaser.Point(); }
+
+ out.x = a.x - b.x;
+ out.y = a.y - b.y;
+
+ return out;
+
+};
+
+/**
+* Multiplies the coordinates of two points to create a new point.
+* @method Phaser.Point.multiply
+* @param {Phaser.Point} a - The first Point object.
+* @param {Phaser.Point} b - The second Point object.
+* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
+* @return {Phaser.Point} The new Point object.
+*/
+Phaser.Point.multiply = function (a, b, out) {
+
+ if (typeof out === "undefined") { out = new Phaser.Point(); }
+
+ out.x = a.x * b.x;
+ out.y = a.y * b.y;
+
+ return out;
+
+};
+
+/**
+* Divides the coordinates of two points to create a new point.
+* @method Phaser.Point.divide
+* @param {Phaser.Point} a - The first Point object.
+* @param {Phaser.Point} b - The second Point object.
+* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
+* @return {Phaser.Point} The new Point object.
+*/
+Phaser.Point.divide = function (a, b, out) {
+
+ if (typeof out === "undefined") { out = new Phaser.Point(); }
+
+ out.x = a.x / b.x;
+ out.y = a.y / b.y;
+
+ return out;
+
+};
+
+/**
+* Determines whether the two given Point objects are equal. They are considered equal if they have the same x and y values.
+* @method Phaser.Point.equals
+* @param {Phaser.Point} a - The first Point object.
+* @param {Phaser.Point} b - The second Point object.
+* @return {boolean} A value of true if the Points are equal, otherwise false.
+*/
+Phaser.Point.equals = function (a, b) {
+ return (a.x == b.x && a.y == b.y);
+};
+
+/**
+* Returns the distance of this Point object to the given object (can be a Circle, Point or anything with x/y properties).
+* @method Phaser.Point.distance
+* @param {object} a - The target object. Must have visible x and y properties that represent the center of the object.
+* @param {object} b - The target object. Must have visible x and y properties that represent the center of the object.
+* @param {boolean} [round] - Round the distance to the nearest integer (default false).
+* @return {number} The distance between this Point object and the destination Point object.
+*/
+Phaser.Point.distance = function (a, b, round) {
+
+ if (typeof round === "undefined") { round = false }
+
+ if (round)
+ {
+ return Phaser.Math.distanceRound(a.x, a.y, b.x, b.y);
+ }
+ else
+ {
+ return Phaser.Math.distance(a.x, a.y, b.x, b.y);
+ }
+
+};
+
+/**
+* Rotates a Point around the x/y coordinates given to the desired angle.
+* @method Phaser.Point.rotate
+* @param {Phaser.Point} a - The Point object to rotate.
+* @param {number} x - The x coordinate of the anchor point
+* @param {number} y - The y coordinate of the anchor point
+* @param {number} angle - The angle in radians (unless asDegrees is true) to rotate the Point to.
+* @param {boolean} asDegrees - Is the given rotation in radians (false) or degrees (true)?
+* @param {number} distance - An optional distance constraint between the Point and the anchor.
+* @return {Phaser.Point} The modified point object.
+*/
+Phaser.Point.rotate = function (a, x, y, angle, asDegrees, distance) {
+
+ asDegrees = asDegrees || false;
+ distance = distance || null;
+
+ if (asDegrees)
+ {
+ angle = Phaser.Math.degToRad(angle);
+ }
+
+ // Get distance from origin (cx/cy) to this point
+ if (distance === null)
+ {
+ distance = Math.sqrt(((x - a.x) * (x - a.x)) + ((y - a.y) * (y - a.y)));
+ }
+
+ return a.setTo(x + distance * Math.cos(angle), y + distance * Math.sin(angle));
+
+};
+
+// Because PIXI uses its own Point, we'll replace it with ours to avoid duplicating code or confusion.
+PIXI.Point = Phaser.Point;
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Creates a new Rectangle object with the top-left corner specified by the x and y parameters and with the specified width and height parameters. If you call this function without parameters, a Rectangle with x, y, width, and height properties set to 0 is created.
+*
+* @class Phaser.Rectangle
+* @constructor
+* @param {number} x - The x coordinate of the top-left corner of the Rectangle.
+* @param {number} y - The y coordinate of the top-left corner of the Rectangle.
+* @param {number} width - The width of the Rectangle.
+* @param {number} height - The height of the Rectangle.
+* @return {Phaser.Rectangle} This Rectangle object.
+*/
+Phaser.Rectangle = function (x, y, width, height) {
+
+ x = x || 0;
+ y = y || 0;
+ width = width || 0;
+ height = height || 0;
+
+ /**
+ * @property {number} x - The x coordinate of the top-left corner of the Rectangle.
+ */
+ this.x = x;
+
+ /**
+ * @property {number} y - The y coordinate of the top-left corner of the Rectangle.
+ */
+ this.y = y;
+
+ /**
+ * @property {number} width - The width of the Rectangle.
+ */
+ this.width = width;
+
+ /**
+ * @property {number} height - The height of the Rectangle.
+ */
+ this.height = height;
+
+};
+
+Phaser.Rectangle.prototype = {
+
+ /**
+ * Adjusts the location of the Rectangle object, as determined by its top-left corner, by the specified amounts.
+ * @method Phaser.Rectangle#offset
+ * @param {number} dx - Moves the x value of the Rectangle object by this amount.
+ * @param {number} dy - Moves the y value of the Rectangle object by this amount.
+ * @return {Phaser.Rectangle} This Rectangle object.
+ */
+ offset: function (dx, dy) {
+
+ this.x += dx;
+ this.y += dy;
+
+ return this;
+
+ },
+
+ /**
+ * Adjusts the location of the Rectangle object using a Point object as a parameter. This method is similar to the Rectangle.offset() method, except that it takes a Point object as a parameter.
+ * @method Phaser.Rectangle#offsetPoint
+ * @param {Phaser.Point} point - A Point object to use to offset this Rectangle object.
+ * @return {Phaser.Rectangle} This Rectangle object.
+ */
+ offsetPoint: function (point) {
+
+ return this.offset(point.x, point.y);
+
+ },
+
+ /**
+ * Sets the members of Rectangle to the specified values.
+ * @method Phaser.Rectangle#setTo
+ * @param {number} x - The x coordinate of the top-left corner of the Rectangle.
+ * @param {number} y - The y coordinate of the top-left corner of the Rectangle.
+ * @param {number} width - The width of the Rectangle in pixels.
+ * @param {number} height - The height of the Rectangle in pixels.
+ * @return {Phaser.Rectangle} This Rectangle object
+ */
+ setTo: function (x, y, width, height) {
+
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ return this;
+
+ },
+
+ /**
+ * Runs Math.floor() on both the x and y values of this Rectangle.
+ * @method Phaser.Rectangle#floor
+ */
+ floor: function () {
+
+ this.x = Math.floor(this.x);
+ this.y = Math.floor(this.y);
+
+ },
+
+ /**
+ * Runs Math.floor() on the x, y, width and height values of this Rectangle.
+ * @method Phaser.Rectangle#floorAll
+ */
+ floorAll: function () {
+
+ this.x = Math.floor(this.x);
+ this.y = Math.floor(this.y);
+ this.width = Math.floor(this.width);
+ this.height = Math.floor(this.height);
+
+ },
+
+ /**
+ * Copies the x, y, width and height properties from any given object to this Rectangle.
+ * @method Phaser.Rectangle#copyFrom
+ * @param {any} source - The object to copy from.
+ * @return {Phaser.Rectangle} This Rectangle object.
+ */
+ copyFrom: function (source) {
+
+ return this.setTo(source.x, source.y, source.width, source.height);
+
+ },
+
+ /**
+ * Copies the x, y, width and height properties from this Rectangle to any given object.
+ * @method Phaser.Rectangle#copyTo
+ * @param {any} source - The object to copy to.
+ * @return {object} This object.
+ */
+ copyTo: function (dest) {
+
+ dest.x = this.x;
+ dest.y = this.y;
+ dest.width = this.width;
+ dest.height = this.height;
+
+ return dest;
+
+ },
+
+ /**
+ * Increases the size of the Rectangle object by the specified amounts. The center point of the Rectangle object stays the same, and its size increases to the left and right by the dx value, and to the top and the bottom by the dy value.
+ * @method Phaser.Rectangle#inflate
+ * @param {number} dx - The amount to be added to the left side of the Rectangle.
+ * @param {number} dy - The amount to be added to the bottom side of the Rectangle.
+ * @return {Phaser.Rectangle} This Rectangle object.
+ */
+ inflate: function (dx, dy) {
+
+ return Phaser.Rectangle.inflate(this, dx, dy);
+
+ },
+
+ /**
+ * The size of the Rectangle object, expressed as a Point object with the values of the width and height properties.
+ * @method Phaser.Rectangle#size
+ * @param {Phaser.Point} [output] - Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
+ * @return {Phaser.Point} The size of the Rectangle object.
+ */
+ size: function (output) {
+
+ return Phaser.Rectangle.size(this, output);
+
+ },
+
+ /**
+ * Returns a new Rectangle object with the same values for the x, y, width, and height properties as the original Rectangle object.
+ * @method Phaser.Rectangle#clone
+ * @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
+ * @return {Phaser.Rectangle}
+ */
+ clone: function (output) {
+
+ return Phaser.Rectangle.clone(this, output);
+
+ },
+
+ /**
+ * Determines whether the specified coordinates are contained within the region defined by this Rectangle object.
+ * @method Phaser.Rectangle#contains
+ * @param {number} x - The x coordinate of the point to test.
+ * @param {number} y - The y coordinate of the point to test.
+ * @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
+ */
+ contains: function (x, y) {
+
+ return Phaser.Rectangle.contains(this, x, y);
+
+ },
+
+ /**
+ * Determines whether the first Rectangle object is fully contained within the second Rectangle object.
+ * A Rectangle object is said to contain another if the second Rectangle object falls entirely within the boundaries of the first.
+ * @method Phaser.Rectangle#containsRect
+ * @param {Phaser.Rectangle} b - The second Rectangle object.
+ * @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
+ */
+ containsRect: function (b) {
+
+ return Phaser.Rectangle.containsRect(this, b);
+
+ },
+
+ /**
+ * Determines whether the two Rectangles are equal.
+ * This method compares the x, y, width and height properties of each Rectangle.
+ * @method Phaser.Rectangle#equals
+ * @param {Phaser.Rectangle} b - The second Rectangle object.
+ * @return {boolean} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
+ */
+ equals: function (b) {
+
+ return Phaser.Rectangle.equals(this, b);
+
+ },
+
+ /**
+ * If the Rectangle object specified in the toIntersect parameter intersects with this Rectangle object, returns the area of intersection as a Rectangle object. If the Rectangles do not intersect, this method returns an empty Rectangle object with its properties set to 0.
+ * @method Phaser.Rectangle#intersection
+ * @param {Phaser.Rectangle} b - The second Rectangle object.
+ * @param {Phaser.Rectangle} out - Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
+ * @return {Phaser.Rectangle} A Rectangle object that equals the area of intersection. If the Rectangles do not intersect, this method returns an empty Rectangle object; that is, a Rectangle with its x, y, width, and height properties set to 0.
+ */
+ intersection: function (b, out) {
+
+ return Phaser.Rectangle.intersection(this, b, out);
+
+ },
+
+ /**
+ * Determines whether the two Rectangles intersect with each other.
+ * This method checks the x, y, width, and height properties of the Rectangles.
+ * @method Phaser.Rectangle#intersects
+ * @param {Phaser.Rectangle} b - The second Rectangle object.
+ * @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0.
+ * @return {boolean} A value of true if the specified object intersects with this Rectangle object; otherwise false.
+ */
+ intersects: function (b, tolerance) {
+
+ return Phaser.Rectangle.intersects(this, b, tolerance);
+
+ },
+
+ /**
+ * Determines whether the object specified intersects (overlaps) with the given values.
+ * @method Phaser.Rectangle#intersectsRaw
+ * @param {number} left - Description.
+ * @param {number} right - Description.
+ * @param {number} top - Description.
+ * @param {number} bottomt - Description.
+ * @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0
+ * @return {boolean} A value of true if the specified object intersects with the Rectangle; otherwise false.
+ */
+ intersectsRaw: function (left, right, top, bottom, tolerance) {
+
+ return Phaser.Rectangle.intersectsRaw(this, left, right, top, bottom, tolerance);
+
+ },
+
+ /**
+ * Adds two Rectangles together to create a new Rectangle object, by filling in the horizontal and vertical space between the two Rectangles.
+ * @method Phaser.Rectangle#union
+ * @param {Phaser.Rectangle} b - The second Rectangle object.
+ * @param {Phaser.Rectangle} [out] - Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
+ * @return {Phaser.Rectangle} A Rectangle object that is the union of the two Rectangles.
+ */
+ union: function (b, out) {
+
+ return Phaser.Rectangle.union(this, b, out);
+
+ },
+
+ /**
+ * Returns a string representation of this object.
+ * @method Phaser.Rectangle#toString
+ * @return {string} A string representation of the instance.
+ */
+ toString: function () {
+
+ return "[{Rectangle (x=" + this.x + " y=" + this.y + " width=" + this.width + " height=" + this.height + " empty=" + this.empty + ")}]";
+
+ },
+
+ /**
+ * @name Phaser.Rectangle#halfWidth
+ * @property {number} halfWidth - Half of the width of the Rectangle.
+ * @readonly
+ */
+ get halfWidth() {
+
+ return Math.round(this.width / 2);
+
+ },
+
+ /**
+ * @name Phaser.Rectangle#halfHeight
+ * @property {number} halfHeight - Half of the height of the Rectangle.
+ * @readonly
+ */
+ get halfHeight() {
+
+ return Math.round(this.height / 2);
+
+ },
+
+ /**
+ * The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property.
+ * @name Phaser.Rectangle#bottom
+ * @property {number} bottom - The sum of the y and height properties.
+ */
+ get bottom() {
+
+ return this.y + this.height;
+
+ },
+
+ set bottom(value) {
+
+ if (value <= this.y)
+ {
+ this.height = 0;
+ }
+ else
+ {
+ this.height = (this.y - value);
+ }
+ },
+
+ /**
+ * The location of the Rectangles bottom right corner as a Point object.
+ * @name Phaser.Rectangle#bottomRight
+ * @property {Phaser.Point} bottomRight - Gets or sets the location of the Rectangles bottom right corner as a Point object.
+ */
+ get bottomRight() {
+
+ return new Phaser.Point(this.right, this.bottom);
+ },
+
+
+ set bottomRight(value) {
+
+ this.right = value.x;
+ this.bottom = value.y;
+
+ },
+
+ /**
+ * The x coordinate of the left of the Rectangle. Changing the left property of a Rectangle object has no effect on the y and height properties. However it does affect the width property, whereas changing the x value does not affect the width property.
+ * @name Phaser.Rectangle#left
+ * @property {number} left - The x coordinate of the left of the Rectangle.
+ */
+ get left() {
+
+ return this.x;
+
+ },
+
+ set left(value) {
+
+ if (value >= this.right)
+ {
+ this.width = 0;
+ }
+ else
+ {
+ this.width = this.right - value;
+ }
+
+ this.x = value;
+ },
+
+ /**
+ * The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties, however it does affect the width property.
+ * @name Phaser.Rectangle#right
+ * @property {number} right - The sum of the x and width properties.
+ */
+ get right() {
+
+ return this.x + this.width;
+
+ },
+
+ set right(value) {
+
+ if (value <= this.x)
+ {
+ this.width = 0;
+ }
+ else
+ {
+ this.width = this.x + value;
+ }
+
+ },
+
+ /**
+ * The volume of the Rectangle derived from width * height.
+ * @name Phaser.Rectangle#volume
+ * @property {number} volume - The volume of the Rectangle derived from width * height.
+ * @readonly
+ */
+ get volume() {
+
+ return this.width * this.height;
+
+ },
+
+ /**
+ * The perimeter size of the Rectangle. This is the sum of all 4 sides.
+ * @name Phaser.Rectangle#perimeter
+ * @property {number} perimeter - The perimeter size of the Rectangle. This is the sum of all 4 sides.
+ * @readonly
+ */
+ get perimeter() {
+
+ return (this.width * 2) + (this.height * 2);
+
+ },
+
+ /**
+ * The x coordinate of the center of the Rectangle.
+ * @name Phaser.Rectangle#centerX
+ * @property {number} centerX - The x coordinate of the center of the Rectangle.
+ */
+ get centerX() {
+
+ return this.x + this.halfWidth;
+
+ },
+
+ set centerX(value) {
+
+ this.x = value - this.halfWidth;
+
+ },
+
+ /**
+ * The y coordinate of the center of the Rectangle.
+ * @name Phaser.Rectangle#centerY
+ * @property {number} centerY - The y coordinate of the center of the Rectangle.
+ */
+ get centerY() {
+
+ return this.y + this.halfHeight;
+
+ },
+
+ set centerY(value) {
+
+ this.y = value - this.halfHeight;
+
+ },
+
+ /**
+ * The y coordinate of the top of the Rectangle. Changing the top property of a Rectangle object has no effect on the x and width properties.
+ * However it does affect the height property, whereas changing the y value does not affect the height property.
+ * @name Phaser.Rectangle#top
+ * @property {number} top - The y coordinate of the top of the Rectangle.
+ */
+ get top() {
+
+ return this.y;
+
+ },
+
+ set top(value) {
+
+ if (value >= this.bottom)
+ {
+ this.height = 0;
+ this.y = value;
+ }
+ else
+ {
+ this.height = (this.bottom - value);
+ }
+
+ },
+
+ /**
+ * The location of the Rectangles top left corner as a Point object.
+ * @name Phaser.Rectangle#topLeft
+ * @property {Phaser.Point} topLeft - The location of the Rectangles top left corner as a Point object.
+ */
+ get topLeft() {
+
+ return new Phaser.Point(this.x, this.y);
+
+ },
+
+ set topLeft(value) {
+
+ this.x = value.x;
+ this.y = value.y;
+
+ },
+
+ /**
+ * Determines whether or not this Rectangle object is empty. A Rectangle object is empty if its width or height is less than or equal to 0.
+ * If set to true then all of the Rectangle properties are set to 0.
+ * @name Phaser.Rectangle#empty
+ * @property {boolean} empty - Gets or sets the Rectangles empty state.
+ */
+ get empty() {
+
+ return (!this.width || !this.height);
+
+ },
+
+ set empty(value) {
+
+ if (value === true)
+ {
+ this.setTo(0, 0, 0, 0);
+ }
+
+ }
+
+};
+
+Phaser.Rectangle.prototype.constructor = Phaser.Rectangle;
+
+/**
+* Increases the size of the Rectangle object by the specified amounts. The center point of the Rectangle object stays the same, and its size increases to the left and right by the dx value, and to the top and the bottom by the dy value.
+* @method Phaser.Rectangle.inflate
+* @param {Phaser.Rectangle} a - The Rectangle object.
+* @param {number} dx - The amount to be added to the left side of the Rectangle.
+* @param {number} dy - The amount to be added to the bottom side of the Rectangle.
+* @return {Phaser.Rectangle} This Rectangle object.
+*/
+Phaser.Rectangle.inflate = function (a, dx, dy) {
+
+ a.x -= dx;
+ a.width += 2 * dx;
+ a.y -= dy;
+ a.height += 2 * dy;
+
+ return a;
+
+};
+
+/**
+* Increases the size of the Rectangle object. This method is similar to the Rectangle.inflate() method except it takes a Point object as a parameter.
+* @method Phaser.Rectangle.inflatePoint
+* @param {Phaser.Rectangle} a - The Rectangle object.
+* @param {Phaser.Point} point - The x property of this Point object is used to increase the horizontal dimension of the Rectangle object. The y property is used to increase the vertical dimension of the Rectangle object.
+* @return {Phaser.Rectangle} The Rectangle object.
+*/
+Phaser.Rectangle.inflatePoint = function (a, point) {
+
+ return Phaser.Rectangle.inflate(a, point.x, point.y);
+
+};
+
+/**
+* The size of the Rectangle object, expressed as a Point object with the values of the width and height properties.
+* @method Phaser.Rectangle.size
+* @param {Phaser.Rectangle} a - The Rectangle object.
+* @param {Phaser.Point} [output] - Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
+* @return {Phaser.Point} The size of the Rectangle object
+*/
+Phaser.Rectangle.size = function (a, output) {
+
+ if (typeof output === "undefined")
+ {
+ output = new Phaser.Point(a.width, a.height);
+ }
+ else
+ {
+ output.setTo(a.width, a.height);
+ }
+
+ return output;
+
+};
+
+/**
+* Returns a new Rectangle object with the same values for the x, y, width, and height properties as the original Rectangle object.
+* @method Phaser.Rectangle.clone
+* @param {Phaser.Rectangle} a - The Rectangle object.
+* @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
+* @return {Phaser.Rectangle}
+*/
+Phaser.Rectangle.clone = function (a, output) {
+
+ if (typeof output === "undefined")
+ {
+ output = new Phaser.Rectangle(a.x, a.y, a.width, a.height);
+ }
+ else
+ {
+ output.setTo(a.x, a.y, a.width, a.height);
+ }
+
+ return output;
+
+};
+
+/**
+* Determines whether the specified coordinates are contained within the region defined by this Rectangle object.
+* @method Phaser.Rectangle.contains
+* @param {Phaser.Rectangle} a - The Rectangle object.
+* @param {number} x - The x coordinate of the point to test.
+* @param {number} y - The y coordinate of the point to test.
+* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
+*/
+Phaser.Rectangle.contains = function (a, x, y) {
+
+ if (a.width <= 0 || a.height <= 0)
+ {
+ return false;
+ }
+
+ return (x >= a.x && x <= a.right && y >= a.y && y <= a.bottom);
+
+};
+
+/**
+* Determines whether the specified coordinates are contained within the region defined by the given raw values.
+* @method Phaser.Rectangle.containsRaw
+* @param {number} rx - The x coordinate of the top left of the area.
+* @param {number} ry - The y coordinate of the top left of the area.
+* @param {number} rw - The width of the area.
+* @param {number} rh - The height of the area.
+* @param {number} x - The x coordinate of the point to test.
+* @param {number} y - The y coordinate of the point to test.
+* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
+*/
+Phaser.Rectangle.containsRaw = function (rx, ry, rw, rh, x, y) {
+
+ return (x >= rx && x <= (rx + rw) && y >= ry && y <= (ry + rh));
+
+};
+
+/**
+* Determines whether the specified point is contained within the rectangular region defined by this Rectangle object. This method is similar to the Rectangle.contains() method, except that it takes a Point object as a parameter.
+* @method Phaser.Rectangle.containsPoint
+* @param {Phaser.Rectangle} a - The Rectangle object.
+* @param {Phaser.Point} point - The point object being checked. Can be Point or any object with .x and .y values.
+* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
+*/
+Phaser.Rectangle.containsPoint = function (a, point) {
+
+ return Phaser.Rectangle.contains(a, point.x, point.y);
+
+};
+
+/**
+* Determines whether the first Rectangle object is fully contained within the second Rectangle object.
+* A Rectangle object is said to contain another if the second Rectangle object falls entirely within the boundaries of the first.
+* @method Phaser.Rectangle.containsRect
+* @param {Phaser.Rectangle} a - The first Rectangle object.
+* @param {Phaser.Rectangle} b - The second Rectangle object.
+* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
+*/
+Phaser.Rectangle.containsRect = function (a, b) {
+
+ // If the given rect has a larger volume than this one then it can never contain it
+ if (a.volume > b.volume)
+ {
+ return false;
+ }
+
+ return (a.x >= b.x && a.y >= b.y && a.right <= b.right && a.bottom <= b.bottom);
+
+};
+
+/**
+* Determines whether the two Rectangles are equal.
+* This method compares the x, y, width and height properties of each Rectangle.
+* @method Phaser.Rectangle.equals
+* @param {Phaser.Rectangle} a - The first Rectangle object.
+* @param {Phaser.Rectangle} b - The second Rectangle object.
+* @return {boolean} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
+*/
+Phaser.Rectangle.equals = function (a, b) {
+
+ return (a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height);
+
+};
+
+/**
+* If the Rectangle object specified in the toIntersect parameter intersects with this Rectangle object, returns the area of intersection as a Rectangle object. If the Rectangles do not intersect, this method returns an empty Rectangle object with its properties set to 0.
+* @method Phaser.Rectangle.intersection
+* @param {Phaser.Rectangle} a - The first Rectangle object.
+* @param {Phaser.Rectangle} b - The second Rectangle object.
+* @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
+* @return {Phaser.Rectangle} A Rectangle object that equals the area of intersection. If the Rectangles do not intersect, this method returns an empty Rectangle object; that is, a Rectangle with its x, y, width, and height properties set to 0.
+*/
+Phaser.Rectangle.intersection = function (a, b, output) {
+
+ if (typeof output === "undefined")
+ {
+ output = new Phaser.Rectangle();
+ }
+
+ if (Phaser.Rectangle.intersects(a, b))
+ {
+ output.x = Math.max(a.x, b.x);
+ output.y = Math.max(a.y, b.y);
+ output.width = Math.min(a.right, b.right) - output.x;
+ output.height = Math.min(a.bottom, b.bottom) - output.y;
+ }
+
+ return output;
+
+};
+
+/**
+* Determines whether the two Rectangles intersect with each other.
+* This method checks the x, y, width, and height properties of the Rectangles.
+* @method Phaser.Rectangle.intersects
+* @param {Phaser.Rectangle} a - The first Rectangle object.
+* @param {Phaser.Rectangle} b - The second Rectangle object.
+* @return {boolean} A value of true if the specified object intersects with this Rectangle object; otherwise false.
+*/
+Phaser.Rectangle.intersects = function (a, b) {
+
+ if (a.width <= 0 || a.height <= 0 || b.width <= 0 || b.height <= 0)
+ {
+ return false;
+ }
+
+ return !(a.right < b.x || a.bottom < b.y || a.x > b.right || a.y > b.bottom);
+
+};
+
+/**
+* Determines whether the object specified intersects (overlaps) with the given values.
+* @method Phaser.Rectangle.intersectsRaw
+* @param {number} left - Description.
+* @param {number} right - Description.
+* @param {number} top - Description.
+* @param {number} bottom - Description.
+* @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0
+* @return {boolean} A value of true if the specified object intersects with the Rectangle; otherwise false.
+*/
+Phaser.Rectangle.intersectsRaw = function (a, left, right, top, bottom, tolerance) {
+
+ if (typeof tolerance === "undefined") { tolerance = 0; }
+
+ return !(left > a.right + tolerance || right < a.left - tolerance || top > a.bottom + tolerance || bottom < a.top - tolerance);
+
+};
+
+/**
+* Adds two Rectangles together to create a new Rectangle object, by filling in the horizontal and vertical space between the two Rectangles.
+* @method Phaser.Rectangle.union
+* @param {Phaser.Rectangle} a - The first Rectangle object.
+* @param {Phaser.Rectangle} b - The second Rectangle object.
+* @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
+* @return {Phaser.Rectangle} A Rectangle object that is the union of the two Rectangles.
+*/
+Phaser.Rectangle.union = function (a, b, output) {
+
+ if (typeof output === "undefined")
+ {
+ output = new Phaser.Rectangle();
+ }
+
+ return output.setTo(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.max(a.right, b.right) - Math.min(a.left, b.left), Math.max(a.bottom, b.bottom) - Math.min(a.top, b.top));
+
+};
+
+// Because PIXI uses its own Rectangle, we'll replace it with ours to avoid duplicating code or confusion.
+PIXI.Rectangle = Phaser.Rectangle;
+PIXI.EmptyRectangle = new Phaser.Rectangle(0, 0, 0, 0);
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Creates a new Line object with a start and an end point.
+* @class Line
+* @classdesc Phaser - Line
+* @constructor
+* @param {number} [x1=0] - The x coordinate of the start of the line.
+* @param {number} [y1=0] - The y coordinate of the start of the line.
+* @param {number} [x2=0] - The x coordinate of the end of the line.
+* @param {number} [y2=0] - The y coordinate of the end of the line.
+* @return {Phaser.Line} This line object
+*/
+Phaser.Line = function (x1, y1, x2, y2) {
+
+ x1 = x1 || 0;
+ y1 = y1 || 0;
+ x2 = x2 || 0;
+ y2 = y2 || 0;
+
+ /**
+ * @property {Phaser.Point} start - The start point of the line.
+ */
+ this.start = new Phaser.Point(x1, y1);
+
+ /**
+ * @property {Phaser.Point} end - The end point of the line.
+ */
+ this.end = new Phaser.Point(x2, y2);
+
+};
+
+Phaser.Line.prototype = {
+
+ /**
+ * Sets the components of the Line to the specified values.
+ * @method Phaser.Line#setTo
+ * @param {number} [x1=0] - The x coordinate of the start of the line.
+ * @param {number} [y1=0] - The y coordinate of the start of the line.
+ * @param {number} [x2=0] - The x coordinate of the end of the line.
+ * @param {number} [y2=0] - The y coordinate of the end of the line.
+ * @return {Phaser.Line} This line object
+ */
+ setTo: function (x1, y1, x2, y2) {
+
+ this.start.setTo(x1, y1);
+ this.end.setTo(x2, y2);
+
+ return this;
+
+ },
+
+ /**
+ * Sets the line to match the x/y coordinates of the two given sprites.
+ * Can optionally be calculated from their center coordinates.
+ * @method Phaser.Line#fromSprite
+ * @param {Phaser.Sprite} startSprite - The coordinates of this Sprite will be set to the Line.start point.
+ * @param {Phaser.Sprite} endSprite - The coordinates of this Sprite will be set to the Line.start point.
+ * @param {boolean} [useCenter=true] - If true it will use startSprite.center.x, if false startSprite.x.
+ * @return {Phaser.Line} This line object
+ */
+ fromSprite: function (startSprite, endSprite, useCenter) {
+
+ if (typeof useCenter === 'undefined') { useCenter = true; }
+
+ if (useCenter)
+ {
+ return this.setTo(startSprite.center.x, startSprite.center.y, endSprite.center.x, endSprite.center.y);
+ }
+ else
+ {
+ return this.setTo(startSprite.x, startSprite.y, endSprite.x, endSprite.y);
+ }
+
+ },
+
+ /**
+ * Checks for intersection between this line and another Line.
+ * If asSegment is true it will check for segment intersection. If asSegment is false it will check for line intersection.
+ * Returns the intersection segment of AB and EF as a Point, or null if there is no intersection.
+ *
+ * @method Phaser.Line#intersects
+ * @param {Phaser.Line} line - The line to check against this one.
+ * @param {boolean} [asSegment=true] - If true it will check for segment intersection, otherwise full line intersection.
+ * @param {Phaser.Point} [result] - A Point object to store the result in, if not given a new one will be created.
+ * @return {Phaser.Point} The intersection segment of the two lines as a Point, or null if there is no intersection.
+ */
+ intersects: function (line, asSegment, result) {
+
+ return Phaser.Line.intersectsPoints(this.start, this.end, line.start, line.end, asSegment, result);
+
+ },
+
+ /**
+ * Tests if the given coordinates fall on this line. See pointOnSegment to test against just the line segment.
+ * @method Phaser.Line#pointOnLine
+ * @param {number} x - The line to check against this one.
+ * @param {number} y - The line to check against this one.
+ * @return {boolean} True if the point is on the line, false if not.
+ */
+ pointOnLine: function (x, y) {
+
+ return ((x - this.start.x) * (this.end.y - this.end.y) === (this.end.x - this.start.x) * (y - this.end.y));
+
+ },
+
+ /**
+ * Tests if the given coordinates fall on this line and within the segment. See pointOnLine to test against just the line.
+ * @method Phaser.Line#pointOnSegment
+ * @param {number} x - The line to check against this one.
+ * @param {number} y - The line to check against this one.
+ * @return {boolean} True if the point is on the line and segment, false if not.
+ */
+ pointOnSegment: function (x, y) {
+
+ var xMin = Math.min(this.start.x, this.end.x);
+ var xMax = Math.max(this.start.x, this.end.x);
+ var yMin = Math.min(this.start.y, this.end.y);
+ var yMax = Math.max(this.start.y, this.end.y);
+
+ return (this.pointOnLine(x, y) && (x >= xMin && x <= xMax) && (y >= yMin && y <= yMax));
+
+ }
+
+};
+
+/**
+* @name Phaser.Line#length
+* @property {number} length - Gets the length of the line segment.
+* @readonly
+*/
+Object.defineProperty(Phaser.Line.prototype, "length", {
+
+ get: function () {
+ return Math.sqrt((this.end.x - this.start.x) * (this.end.x - this.start.x) + (this.end.y - this.start.y) * (this.end.y - this.start.y));
+ }
+
+});
+
+/**
+* @name Phaser.Line#angle
+* @property {number} angle - Gets the angle of the line.
+* @readonly
+*/
+Object.defineProperty(Phaser.Line.prototype, "angle", {
+
+ get: function () {
+ return Math.atan2(this.end.x - this.start.x, this.end.y - this.start.y);
+ }
+
+});
+
+/**
+* @name Phaser.Line#slope
+* @property {number} slope - Gets the slope of the line (y/x).
+* @readonly
+*/
+Object.defineProperty(Phaser.Line.prototype, "slope", {
+
+ get: function () {
+ return (this.end.y - this.start.y) / (this.end.x - this.start.x);
+ }
+
+});
+
+/**
+* @name Phaser.Line#perpSlope
+* @property {number} perpSlope - Gets the perpendicular slope of the line (x/y).
+* @readonly
+*/
+Object.defineProperty(Phaser.Line.prototype, "perpSlope", {
+
+ get: function () {
+ return -((this.end.x - this.start.x) / (this.end.y - this.start.y));
+ }
+
+});
+
+/**
+* Checks for intersection between two lines as defined by the given start and end points.
+* If asSegment is true it will check for line segment intersection. If asSegment is false it will check for line intersection.
+* Returns the intersection segment of AB and EF as a Point, or null if there is no intersection.
+* Adapted from code by Keith Hair
+*
+* @method Phaser.Line.intersects
+* @param {Phaser.Point} a - The start of the first Line to be checked.
+* @param {Phaser.Point} b - The end of the first line to be checked.
+* @param {Phaser.Point} e - The start of the second Line to be checked.
+* @param {Phaser.Point} f - The end of the second line to be checked.
+* @param {boolean} [asSegment=true] - If true it will check for segment intersection, otherwise full line intersection.
+* @param {Phaser.Point} [result] - A Point object to store the result in, if not given a new one will be created.
+* @return {Phaser.Point} The intersection segment of the two lines as a Point, or null if there is no intersection.
+*/
+Phaser.Line.intersectsPoints = function (a, b, e, f, asSegment, result) {
+
+ if (typeof asSegment === 'undefined') { asSegment = true; }
+ if (typeof result === 'undefined') { result = new Phaser.Point(); }
+
+ var a1 = b.y - a.y;
+ var a2 = f.y - e.y;
+ var b1 = a.x - b.x;
+ var b2 = e.x - f.x;
+ var c1 = (b.x * a.y) - (a.x * b.y);
+ var c2 = (f.x * e.y) - (e.x * f.y);
+ var denom = (a1 * b2) - (a2 * b1);
+
+ if (denom === 0)
+ {
+ return null;
+ }
+
+ result.x = ((b1 * c2) - (b2 * c1)) / denom;
+ result.y = ((a2 * c1) - (a1 * c2)) / denom;
+
+ if (asSegment)
+ {
+ if (Math.pow((result.x - b.x) + (result.y - b.y), 2) > Math.pow((a.x - b.x) + (a.y - b.y), 2))
+ {
+ return null;
+ }
+
+ if (Math.pow((result.x - a.x) + (result.y - a.y), 2) > Math.pow((a.x - b.x) + (a.y - b.y), 2))
+ {
+ return null;
+ }
+
+ if (Math.pow((result.x - f.x) + (result.y - f.y), 2) > Math.pow((e.x - f.x) + (e.y - f.y), 2))
+ {
+ return null;
+ }
+
+ if (Math.pow((result.x - e.x) + (result.y - e.y), 2) > Math.pow((e.x - f.x) + (e.y - f.y), 2))
+ {
+ return null;
+ }
+ }
+
+ return result;
+
+};
+
+/**
+* Checks for intersection between two lines.
+* If asSegment is true it will check for segment intersection.
+* If asSegment is false it will check for line intersection.
+* Returns the intersection segment of AB and EF as a Point, or null if there is no intersection.
+* Adapted from code by Keith Hair
+*
+* @method Phaser.Line.intersects
+* @param {Phaser.Line} a - The first Line to be checked.
+* @param {Phaser.Line} b - The second Line to be checked.
+* @param {boolean} [asSegment=true] - If true it will check for segment intersection, otherwise full line intersection.
+* @param {Phaser.Point} [result] - A Point object to store the result in, if not given a new one will be created.
+* @return {Phaser.Point} The intersection segment of the two lines as a Point, or null if there is no intersection.
+*/
+Phaser.Line.intersects = function (a, b, asSegment, result) {
+
+ return Phaser.Line.intersectsPoints(a.start, a.end, b.start, b.end, asSegment, result);
+
+};
+
+/**
+* @author Richard Davey
+* @author Chad Engler
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Creates a Ellipse object. A curve on a plane surrounding two focal points.
+* @class Ellipse
+* @classdesc Phaser - Ellipse
+* @constructor
+* @param {number} [x=0] - The X coordinate of the upper-left corner of the framing rectangle of this ellipse.
+* @param {number} [y=0] - The Y coordinate of the upper-left corner of the framing rectangle of this ellipse.
+* @param {number} [width=0] - The overall width of this ellipse.
+* @param {number} [height=0] - The overall height of this ellipse.
+* @return {Phaser.Ellipse} This Ellipse object
+*/
+Phaser.Ellipse = function (x, y, width, height) {
+
+ this.type = Phaser.ELLIPSE;
+
+ x = x || 0;
+ y = y || 0;
+ width = width || 0;
+ height = height || 0;
+
+ /**
+ * @property {number} x - The X coordinate of the upper-left corner of the framing rectangle of this ellipse.
+ */
+ this.x = x;
+
+ /**
+ * @property {number} y - The Y coordinate of the upper-left corner of the framing rectangle of this ellipse.
+ */
+ this.y = y;
+
+ /**
+ * @property {number} width - The overall width of this ellipse.
+ */
+ this.width = width;
+
+ /**
+ * @property {number} height - The overall height of this ellipse.
+ */
+ this.height = height;
+
+};
+
+Phaser.Ellipse.prototype = {
+
+ /**
+ * Sets the members of the Ellipse to the specified values.
+ * @method Phaser.Ellipse#setTo
+ * @param {number} x - The X coordinate of the upper-left corner of the framing rectangle of this ellipse.
+ * @param {number} y - The Y coordinate of the upper-left corner of the framing rectangle of this ellipse.
+ * @param {number} width - The overall width of this ellipse.
+ * @param {number} height - The overall height of this ellipse.
+ * @return {Phaser.Ellipse} This Ellipse object.
+ */
+ setTo: function (x, y, width, height) {
+
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+
+ return this;
+
+ },
+
+ /**
+ * Copies the x, y, width and height properties from any given object to this Ellipse.
+ * @method Phaser.Ellipse#copyFrom
+ * @param {any} source - The object to copy from.
+ * @return {Phaser.Ellipse} This Ellipse object.
+ */
+ copyFrom: function (source) {
+
+ return this.setTo(source.x, source.y, source.width, source.height);
+
+ },
+
+ /**
+ * Copies the x, y and diameter properties from this Circle to any given object.
+ * @method Phaser.Ellipse#copyTo
+ * @param {any} dest - The object to copy to.
+ * @return {Object} This dest object.
+ */
+ copyTo: function(dest) {
+
+ dest.x = this.x;
+ dest.y = this.y;
+ dest.width = this.width;
+ dest.height = this.height;
+
+ return dest;
+
+ },
+
+ /**
+ * Returns a new Ellipse object with the same values for the x, y, width, and height properties as this Ellipse object.
+ * @method Phaser.Ellipse#clone
+ * @param {Phaser.Ellipse} out - Optional Ellipse object. If given the values will be set into the object, otherwise a brand new Ellipse object will be created and returned.
+ * @return {Phaser.Ellipse} The cloned Ellipse object.
+ */
+ clone: function(out) {
+
+ if (typeof out === "undefined")
+ {
+ out = new Phaser.Ellipse(this.x, this.y, this.width, this.height);
+ }
+ else
+ {
+ out.setTo(this.x, this.y, this.width, this.height);
+ }
+
+ return out;
+
+ },
+
+ /**
+ * Return true if the given x/y coordinates are within this Ellipse object.
+ * @method Phaser.Ellipse#contains
+ * @param {number} x - The X value of the coordinate to test.
+ * @param {number} y - The Y value of the coordinate to test.
+ * @return {boolean} True if the coordinates are within this ellipse, otherwise false.
+ */
+ contains: function (x, y) {
+
+ return Phaser.Ellipse.contains(this, x, y);
+
+ },
+
+ /**
+ * Returns a string representation of this object.
+ * @method Phaser.Ellipse#toString
+ * @return {string} A string representation of the instance.
+ */
+ toString: function () {
+ return "[{Phaser.Ellipse (x=" + this.x + " y=" + this.y + " width=" + this.width + " height=" + this.height + ")}]";
+ }
+
+};
+
+Phaser.Ellipse.prototype.constructor = Phaser.Ellipse;
+
+/**
+* The left coordinate of the Ellipse. The same as the X coordinate.
+* @name Phaser.Ellipse#left
+* @propety {number} left - Gets or sets the value of the leftmost point of the ellipse.
+*/
+Object.defineProperty(Phaser.Ellipse.prototype, "left", {
+
+ get: function () {
+ return this.x;
+ },
+
+ set: function (value) {
+
+ this.x = value;
+
+ }
+
+});
+
+/**
+* The x coordinate of the rightmost point of the Ellipse. Changing the right property of an Ellipse object has no effect on the x property, but does adjust the width.
+* @name Phaser.Ellipse#right
+* @property {number} right - Gets or sets the value of the rightmost point of the ellipse.
+*/
+Object.defineProperty(Phaser.Ellipse.prototype, "right", {
+
+ get: function () {
+ return this.x + this.width;
+ },
+
+ set: function (value) {
+
+ if (value < this.x)
+ {
+ this.width = 0;
+ }
+ else
+ {
+ this.width = this.x + width;
+ }
+ }
+
+});
+
+/**
+* The top of the Ellipse. The same as its y property.
+* @name Phaser.Ellipse#top
+* @property {number} top - Gets or sets the top of the ellipse.
+*/
+Object.defineProperty(Phaser.Ellipse.prototype, "top", {
+
+ get: function () {
+ return this.y;
+ },
+
+ set: function (value) {
+ this.y = value;
+ }
+
+});
+
+/**
+* The sum of the y and height properties. Changing the bottom property of an Ellipse doesn't adjust the y property, but does change the height.
+* @name Phaser.Ellipse#bottom
+* @property {number} bottom - Gets or sets the bottom of the ellipse.
+*/
+Object.defineProperty(Phaser.Ellipse.prototype, "bottom", {
+
+ get: function () {
+ return this.y + this.height;
+ },
+
+ set: function (value) {
+
+ if (value < this.y)
+ {
+ this.height = 0;
+ }
+ else
+ {
+ this.height = this.y + value;
+ }
+ }
+
+});
+
+/**
+* Determines whether or not this Ellipse object is empty. Will return a value of true if the Ellipse objects dimensions are less than or equal to 0; otherwise false.
+* If set to true it will reset all of the Ellipse objects properties to 0. An Ellipse object is empty if its width or height is less than or equal to 0.
+* @name Phaser.Ellipse#empty
+* @property {boolean} empty - Gets or sets the empty state of the ellipse.
+*/
+Object.defineProperty(Phaser.Ellipse.prototype, "empty", {
+
+ get: function () {
+ return (this.width === 0 || this.height === 0);
+ },
+
+ set: function (value) {
+
+ if (value === true)
+ {
+ this.setTo(0, 0, 0, 0);
+ }
+
+ }
+
+});
+
+/**
+* Return true if the given x/y coordinates are within the Ellipse object.
+* @method Phaser.Ellipse.contains
+* @param {Phaser.Ellipse} a - The Ellipse to be checked.
+* @param {number} x - The X value of the coordinate to test.
+* @param {number} y - The Y value of the coordinate to test.
+* @return {boolean} True if the coordinates are within this ellipse, otherwise false.
+*/
+Phaser.Ellipse.contains = function (a, x, y) {
+
+ if (a.width <= 0 || a.height <= 0)
+ {
+ return false;
+ }
+
+ // Normalize the coords to an ellipse with center 0,0 and a radius of 0.5
+ var normx = ((x - a.x) / a.width) - 0.5;
+ var normy = ((y - a.y) / a.height) - 0.5;
+
+ normx *= normx;
+ normy *= normy;
+
+ return (normx + normy < 0.25);
+
+};
+
+/**
+* Returns the framing rectangle of the ellipse as a Phaser.Rectangle object.
+*
+* @method getBounds
+* @return {Phaser.Rectangle} The framing rectangle
+*/
+Phaser.Ellipse.prototype.getBounds = function() {
+
+ return new Phaser.Rectangle(this.x, this.y, this.width, this.height);
+
+};
+
+// Because PIXI uses its own Ellipse, we'll replace it with ours to avoid duplicating code or confusion.
+PIXI.Ellipse = Phaser.Ellipse;
+
+/**
+* @author Richard Davey
+* @author Adrien Brault
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Creates a new Polygon. You have to provide a list of points.
+* This can be an array of Points that form the polygon, a flat array of numbers that will be interpreted as [x,y, x,y, ...],
+* or the arguments passed can be all the points of the polygon e.g. `new Phaser.Polygon(new Phaser.Point(), new Phaser.Point(), ...)`, or the
+* arguments passed can be flat x,y values e.g. `new Phaser.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are numbers.
+*
+* @class Phaser.Polygon
+* @classdesc The polygon represents a list of orderded points in space
+* @constructor
+* @param {Array|Array} points - The array of Points.
+*/
+Phaser.Polygon = function (points) {
+
+ /**
+ * @property {number} type - The base object type.
+ */
+ this.type = Phaser.POLYGON;
+
//if points isn't an array, use arguments as the array
- if(!(points instanceof Array))
+ if (!(points instanceof Array))
+ {
points = Array.prototype.slice.call(arguments);
+ }
//if this is a flat array of numbers, convert it to points
- if(typeof points[0] === 'number') {
+ if (typeof points[0] === 'number')
+ {
var p = [];
- for(var i = 0, il = points.length; i < il; i+=2) {
- p.push(
- new PIXI.Point(points[i], points[i + 1])
- );
+
+ for (var i = 0, len = points.length; i < len; i += 2)
+ {
+ p.push(new Phaser.Point(points[i], points[i + 1]));
}
points = p;
}
+ /**
+ * @property {array|array} points - The array of Points.
+ */
this.points = points;
+
+};
+
+Phaser.Polygon.prototype = {
+
+ /**
+ * Creates a clone of this polygon.
+ *
+ * @method Phaser.Polygon#clone
+ * @return {Phaser.Polygon} A copy of the polygon.
+ */
+ clone: function () {
+
+ var points = [];
+
+ for (var i=0; i < this.points.length; i++)
+ {
+ points.push(this.points[i].clone());
+ }
+
+ return new Phaser.Polygon(points);
+
+ },
+
+ /**
+ * Checks whether the x and y coordinates are contained within this polygon.
+ *
+ * @method Phaser.Polygon#contains
+ * @param {number} x - The X value of the coordinate to test.
+ * @param {number} y - The Y value of the coordinate to test.
+ * @return {boolean} True if the coordinates are within this polygon, otherwise false.
+ */
+ contains: function (x, y) {
+
+ var inside = false;
+
+ // use some raycasting to test hits https://github.com/substack/point-in-polygon/blob/master/index.js
+ for (var i = 0, j = this.points.length - 1; i < this.points.length; j = i++)
+ {
+ var xi = this.points[i].x;
+ var yi = this.points[i].y;
+ var xj = this.points[j].x;
+ var yj = this.points[j].y;
+
+ var intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
+
+ if (intersect)
+ {
+ inside = true;
+ }
+ }
+
+ return inside;
+
+ }
+
+};
+
+Phaser.Polygon.prototype.constructor = Phaser.Polygon;
+
+// Because PIXI uses its own Polygon, we'll replace it with ours to avoid duplicating code or confusion.
+PIXI.Polygon = Phaser.Polygon;
+
+/**
+ * @author Mat Groves http://matgroves.com/ @Doormat23
+ */
+
+PIXI.determineMatrixArrayType = function() {
+ return (typeof Float32Array !== 'undefined') ? Float32Array : Array;
+};
+
+/*
+* @class Matrix2
+* The Matrix2 class will choose the best type of array to use between
+* a regular javascript Array and a Float32Array if the latter is available
+*
+*/
+PIXI.Matrix2 = PIXI.determineMatrixArrayType();
+
+/*
+* @class Matrix
+* The Matrix class is now an object, which makes it a lot faster,
+* here is a representation of it :
+* | a | b | tx|
+* | c | c | ty|
+* | 0 | 0 | 1 |
+*
+*/
+PIXI.Matrix = function()
+{
+ this.a = 1;
+ this.b = 0;
+ this.c = 0;
+ this.d = 1;
+ this.tx = 0;
+ this.ty = 0;
};
/**
- * Creates a clone of this polygon
+ * Creates a pixi matrix object based on the array given as a parameter
*
- * @method clone
- * @return {Polygon} a copy of the polygon
+ * @method fromArray
+ * @param array {Array} The array that the matrix will be filled with
*/
-PIXI.Polygon.prototype.clone = function()
+PIXI.Matrix.prototype.fromArray = function(array)
{
- var points = [];
- for (var i=0; i y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
-
- if(intersect) inside = !inside;
+ if(transpose)
+ {
+ this.array[0] = this.a;
+ this.array[1] = this.c;
+ this.array[2] = 0;
+ this.array[3] = this.b;
+ this.array[4] = this.d;
+ this.array[5] = 0;
+ this.array[6] = this.tx;
+ this.array[7] = this.ty;
+ this.array[8] = 1;
+ }
+ else
+ {
+ this.array[0] = this.a;
+ this.array[1] = this.b;
+ this.array[2] = this.tx;
+ this.array[3] = this.c;
+ this.array[4] = this.d;
+ this.array[5] = this.ty;
+ this.array[6] = 0;
+ this.array[7] = 0;
+ this.array[8] = 1;
}
- return inside;
+ return array;//[this.a, this.b, this.tx, this.c, this.d, this.ty, 0, 0, 1];
};
-// constructor
-PIXI.Polygon.prototype.constructor = PIXI.Polygon;
-
+PIXI.identityMatrix = new PIXI.Matrix();
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
@@ -868,8 +13236,6 @@ PIXI.Polygon.prototype.constructor = PIXI.Polygon;
*/
PIXI.DisplayObject = function()
{
- this.last = this;
- this.first = this;
/**
* The coordinate of the object relative to the local coordinates of the parent.
*
@@ -962,7 +13328,7 @@ PIXI.DisplayObject = function()
this.stage = null;
/**
- * [read-only] The multiplied alpha of the displayobject
+ * [read-only] The multiplied alpha of the displayObject
*
* @property worldAlpha
* @type Number
@@ -980,6 +13346,13 @@ PIXI.DisplayObject = function()
*/
this._interactive = false;
+ /**
+ * This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true
+ *
+ * @property defaultCursor
+ * @type String
+ *
+ */
this.defaultCursor = 'pointer';
/**
@@ -990,20 +13363,10 @@ PIXI.DisplayObject = function()
* @readOnly
* @private
*/
- this.worldTransform = PIXI.mat3.create(); //mat3.identity();
+ this.worldTransform = new PIXI.Matrix();
/**
- * [read-only] Current transform of the object locally
- *
- * @property localTransform
- * @type Mat3
- * @readOnly
- * @private
- */
- this.localTransform = PIXI.mat3.create(); //mat3.identity();
-
- /**
- * [NYI] Unkown
+ * [NYI] Unknown
*
* @property color
* @type Array<>
@@ -1020,13 +13383,47 @@ PIXI.DisplayObject = function()
*/
this.dynamic = true;
- // chach that puppy!
+ // cached sin rotation and cos rotation
this._sr = 0;
this._cr = 1;
-
+ /**
+ * The area the filter is applied to
+ *
+ * @property filterArea
+ * @type Rectangle
+ */
this.filterArea = new PIXI.Rectangle(0,0,1,1);
+ /**
+ * The original, cached bounds of the object
+ *
+ * @property _bounds
+ * @type Rectangle
+ * @private
+ */
+ this._bounds = new PIXI.Rectangle(0, 0, 1, 1);
+ /**
+ * The most up-to-date bounds of the object
+ *
+ * @property _currentBounds
+ * @type Rectangle
+ * @private
+ */
+ this._currentBounds = null;
+ /**
+ * The original, cached mask of the object
+ *
+ * @property _currentBounds
+ * @type Rectangle
+ * @private
+ */
+ this._mask = null;
+
+ this._cacheAsBitmap = false;
+ this._cacheIsDirty = false;
+
+
/*
* MOUSE Callbacks
*/
@@ -1082,7 +13479,7 @@ PIXI.DisplayObject = function()
*/
/**
- * A callback that is used when the user touch's over the displayObject
+ * A callback that is used when the user touches over the displayObject
* @method touchstart
* @param interactionData {InteractionData}
*/
@@ -1137,9 +13534,30 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', {
}
});
+/**
+ * [read-only] Indicates if the sprite is globaly visible.
+ *
+ * @property worldVisible
+ * @type Boolean
+ */
+Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', {
+ get: function() {
+ var item = this;
+
+ do
+ {
+ if(!item.visible)return false;
+ item = item.parent;
+ }
+ while(item);
+
+ return true;
+ }
+});
+
/**
* Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it.
- * In PIXI a regular mask must be a PIXI.Ggraphics object. This allows for much faster masking in canvas as it utilises shape clipping.
+ * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping.
* To remove a mask, set this property to null.
*
* @property mask
@@ -1151,27 +13569,9 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', {
},
set: function(value) {
-
- if(value)
- {
- if(this._mask)
- {
- value.start = this._mask.start;
- value.end = this._mask.end;
- }
- else
- {
- this.addFilter(value);
- value.renderable = false;
- }
- }
- else
- {
- this.removeFilter(this._mask);
- this._mask.renderable = true;
- }
-
+ if(this._mask)this._mask.isMask = false;
this._mask = value;
+ if(this._mask)this._mask.isMask = true;
}
});
@@ -1190,9 +13590,6 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', {
if(value)
{
- if(this._filters)this.removeFilter(this._filters);
- this.addFilter(value);
-
// now put all the passes in one place..
var passes = [];
for (var i = 0; i < value.length; i++)
@@ -1204,171 +13601,35 @@ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', {
}
}
- value.start.filterPasses = passes;
- }
- else
- {
- if(this._filters) {
- this.removeFilter(this._filters);
- }
+ // TODO change this as it is legacy
+ this._filterBlock = {target:this, filterPasses:passes};
}
this._filters = value;
}
});
-/*
- * Adds a filter to this displayObject
- *
- * @method addFilter
- * @param mask {Graphics} the graphics object to use as a filter
- * @private
- */
-PIXI.DisplayObject.prototype.addFilter = function(data)
-{
- //if(this.filter)return;
- //this.filter = true;
-// data[0].target = this;
+Object.defineProperty(PIXI.DisplayObject.prototype, 'cacheAsBitmap', {
+ get: function() {
+ return this._cacheAsBitmap;
+ },
+ set: function(value) {
+ if(this._cacheAsBitmap === value)return;
- // insert a filter block..
- // TODO Onject pool thease bad boys..
- var start = new PIXI.FilterBlock();
- var end = new PIXI.FilterBlock();
-
- data.start = start;
- data.end = end;
-
- start.data = data;
- end.data = data;
-
- start.first = start.last = this;
- end.first = end.last = this;
-
- start.open = true;
-
- start.target = this;
-
- /*
- * insert start
- */
-
- var childFirst = start;
- var childLast = start;
- var nextObject;
- var previousObject;
-
- previousObject = this.first._iPrev;
-
- if(previousObject)
- {
- nextObject = previousObject._iNext;
- childFirst._iPrev = previousObject;
- previousObject._iNext = childFirst;
- }
- else
- {
- nextObject = this;
- }
-
- if(nextObject)
- {
- nextObject._iPrev = childLast;
- childLast._iNext = nextObject;
- }
-
- // now insert the end filter block..
-
- /*
- * insert end filter
- */
- childFirst = end;
- childLast = end;
- nextObject = null;
- previousObject = null;
-
- previousObject = this.last;
- nextObject = previousObject._iNext;
-
- if(nextObject)
- {
- nextObject._iPrev = childLast;
- childLast._iNext = nextObject;
- }
-
- childFirst._iPrev = previousObject;
- previousObject._iNext = childFirst;
-
- var updateLast = this;
-
- var prevLast = this.last;
- while(updateLast)
- {
- if(updateLast.last === prevLast)
+ if(value)
{
- updateLast.last = end;
+ //this._cacheIsDirty = true;
+ this._generateCachedSprite();
}
- updateLast = updateLast.parent;
+ else
+ {
+ this._destroyCachedSprite();
+ }
+
+ this._cacheAsBitmap = value;
}
-
- this.first = start;
-
- // if webGL...
- if(this.__renderGroup)
- {
- this.__renderGroup.addFilterBlocks(start, end);
- }
-};
-
-/*
- * Removes the filter to this displayObject
- *
- * @method removeFilter
- * @private
- */
-PIXI.DisplayObject.prototype.removeFilter = function(data)
-{
- //if(!this.filter)return;
- //this.filter = false;
- // console.log('YUOIO')
- // modify the list..
- var startBlock = data.start;
-
-
- var nextObject = startBlock._iNext;
- var previousObject = startBlock._iPrev;
-
- if(nextObject)nextObject._iPrev = previousObject;
- if(previousObject)previousObject._iNext = nextObject;
-
- this.first = startBlock._iNext;
-
- // remove the end filter
- var lastBlock = data.end;
-
- nextObject = lastBlock._iNext;
- previousObject = lastBlock._iPrev;
-
- if(nextObject)nextObject._iPrev = previousObject;
- previousObject._iNext = nextObject;
-
- // this is always true too!
- var tempLast = lastBlock._iPrev;
- // need to make sure the parents last is updated too
- var updateLast = this;
- while(updateLast.last === lastBlock)
- {
- updateLast.last = tempLast;
- updateLast = updateLast.parent;
- if(!updateLast)break;
- }
-
- // if webGL...
- if(this.__renderGroup)
- {
- this.__renderGroup.removeFilterBlocks(startBlock, lastBlock);
- }
-};
+});
/*
* Updates the object transform for rendering
@@ -1381,51 +13642,201 @@ PIXI.DisplayObject.prototype.updateTransform = function()
// TODO OPTIMIZE THIS!! with dirty
if(this.rotation !== this.rotationCache)
{
+
this.rotationCache = this.rotation;
this._sr = Math.sin(this.rotation);
this._cr = Math.cos(this.rotation);
}
- var localTransform = this.localTransform;
- var parentTransform = this.parent.worldTransform;
- var worldTransform = this.worldTransform;
- //console.log(localTransform)
- localTransform[0] = this._cr * this.scale.x;
- localTransform[1] = -this._sr * this.scale.y;
- localTransform[3] = this._sr * this.scale.x;
- localTransform[4] = this._cr * this.scale.y;
-
- // TODO --> do we even need a local matrix???
+ // var localTransform = this.localTransform//.toArray();
+ var parentTransform = this.parent.worldTransform;//.toArray();
+ var worldTransform = this.worldTransform;//.toArray();
var px = this.pivot.x;
var py = this.pivot.y;
- // Cache the matrix values (makes for huge speed increases!)
- var a00 = localTransform[0], a01 = localTransform[1], a02 = this.position.x - localTransform[0] * px - py * localTransform[1],
- a10 = localTransform[3], a11 = localTransform[4], a12 = this.position.y - localTransform[4] * py - px * localTransform[3],
+ var a00 = this._cr * this.scale.x,
+ a01 = -this._sr * this.scale.y,
+ a10 = this._sr * this.scale.x,
+ a11 = this._cr * this.scale.y,
+ a02 = this.position.x - a00 * px - py * a01,
+ a12 = this.position.y - a11 * py - px * a10,
+ b00 = parentTransform.a, b01 = parentTransform.b,
+ b10 = parentTransform.c, b11 = parentTransform.d;
- b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2],
- b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5];
+ worldTransform.a = b00 * a00 + b01 * a10;
+ worldTransform.b = b00 * a01 + b01 * a11;
+ worldTransform.tx = b00 * a02 + b01 * a12 + parentTransform.tx;
- localTransform[2] = a02;
- localTransform[5] = a12;
+ worldTransform.c = b10 * a00 + b11 * a10;
+ worldTransform.d = b10 * a01 + b11 * a11;
+ worldTransform.ty = b10 * a02 + b11 * a12 + parentTransform.ty;
- worldTransform[0] = b00 * a00 + b01 * a10;
- worldTransform[1] = b00 * a01 + b01 * a11;
- worldTransform[2] = b00 * a02 + b01 * a12 + b02;
-
- worldTransform[3] = b10 * a00 + b11 * a10;
- worldTransform[4] = b10 * a01 + b11 * a11;
- worldTransform[5] = b10 * a02 + b11 * a12 + b12;
-
- // because we are using affine transformation, we can optimise the matrix concatenation process.. wooo!
- // mat3.multiply(this.localTransform, this.parent.worldTransform, this.worldTransform);
this.worldAlpha = this.alpha * this.parent.worldAlpha;
-
- this.vcount = PIXI.visibleCount;
};
-PIXI.visibleCount = 0;
+/**
+ * Retrieves the bounds of the displayObject as a rectangle object
+ *
+ * @method getBounds
+ * @return {Rectangle} the rectangular bounding area
+ */
+PIXI.DisplayObject.prototype.getBounds = function( matrix )
+{
+ matrix = matrix;//just to get passed js hinting (and preserve inheritance)
+ return PIXI.EmptyRectangle;
+};
+
+/**
+ * Retrieves the local bounds of the displayObject as a rectangle object
+ *
+ * @method getLocalBounds
+ * @return {Rectangle} the rectangular bounding area
+ */
+PIXI.DisplayObject.prototype.getLocalBounds = function()
+{
+ return this.getBounds(PIXI.identityMatrix);///PIXI.EmptyRectangle();
+};
+
+
+/**
+ * Sets the object's stage reference, the stage this object is connected to
+ *
+ * @method setStageReference
+ * @param stage {Stage} the stage that the object will have as its current stage reference
+ */
+PIXI.DisplayObject.prototype.setStageReference = function(stage)
+{
+ this.stage = stage;
+ if(this._interactive)this.stage.dirty = true;
+};
+
+PIXI.DisplayObject.prototype.generateTexture = function(renderer)
+{
+ var bounds = this.getLocalBounds();
+
+ var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer);
+ renderTexture.render(this);
+
+ return renderTexture;
+};
+
+PIXI.DisplayObject.prototype.updateCache = function()
+{
+ this._generateCachedSprite();
+};
+
+PIXI.DisplayObject.prototype._renderCachedSprite = function(renderSession)
+{
+ if(renderSession.gl)
+ {
+ PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession);
+ }
+ else
+ {
+ PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession);
+ }
+};
+
+PIXI.DisplayObject.prototype._generateCachedSprite = function()//renderSession)
+{
+ this._cacheAsBitmap = false;
+ var bounds = this.getLocalBounds();
+
+ if(!this._cachedSprite)
+ {
+ var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0);//, renderSession.renderer);
+
+ this._cachedSprite = new PIXI.Sprite(renderTexture);
+ this._cachedSprite.worldTransform = this.worldTransform;
+ }
+ else
+ {
+ this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0);
+ }
+
+ //REMOVE filter!
+ var tempFilters = this._filters;
+ this._filters = null;
+
+ this._cachedSprite.filters = tempFilters;
+ this._cachedSprite.texture.render(this);
+
+ this._filters = tempFilters;
+
+ this._cacheAsBitmap = true;
+};
+
+/**
+* Renders the object using the WebGL renderer
+*
+* @method _renderWebGL
+* @param renderSession {RenderSession}
+* @private
+*/
+PIXI.DisplayObject.prototype._destroyCachedSprite = function()
+{
+ if(!this._cachedSprite)return;
+
+ this._cachedSprite.texture.destroy(true);
+ // console.log("DESTROY")
+ // let the gc collect the unused sprite
+ // TODO could be object pooled!
+ this._cachedSprite = null;
+};
+
+
+PIXI.DisplayObject.prototype._renderWebGL = function(renderSession)
+{
+ // OVERWRITE;
+ // this line is just here to pass jshinting :)
+ renderSession = renderSession;
+};
+
+/**
+* Renders the object using the Canvas renderer
+*
+* @method _renderCanvas
+* @param renderSession {RenderSession}
+* @private
+*/
+PIXI.DisplayObject.prototype._renderCanvas = function(renderSession)
+{
+ // OVERWRITE;
+ // this line is just here to pass jshinting :)
+ renderSession = renderSession;
+};
+
+/**
+ * The position of the displayObject on the x axis relative to the local coordinates of the parent.
+ *
+ * @property x
+ * @type Number
+ */
+Object.defineProperty(PIXI.DisplayObject.prototype, 'x', {
+ get: function() {
+ return this.position.x;
+ },
+ set: function(value) {
+ this.position.x = value;
+ }
+});
+
+/**
+ * The position of the displayObject on the y axis relative to the local coordinates of the parent.
+ *
+ * @property y
+ * @type Number
+ */
+Object.defineProperty(PIXI.DisplayObject.prototype, 'y', {
+ get: function() {
+ return this.position.y;
+ },
+ set: function(value) {
+ this.position.y = value;
+ }
+});
+
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
@@ -1444,7 +13855,7 @@ PIXI.DisplayObjectContainer = function()
PIXI.DisplayObject.call( this );
/**
- * [read-only] The of children of this container.
+ * [read-only] The array of children of this container.
*
* @property children
* @type Array
@@ -1457,6 +13868,44 @@ PIXI.DisplayObjectContainer = function()
PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype );
PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer;
+/**
+ * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
+ *
+ * @property width
+ * @type Number
+ */
+
+ /*
+Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', {
+ get: function() {
+ return this.scale.x * this.getLocalBounds().width;
+ },
+ set: function(value) {
+ this.scale.x = value / (this.getLocalBounds().width/this.scale.x);
+ this._width = value;
+ }
+});
+*/
+
+/**
+ * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set
+ *
+ * @property height
+ * @type Number
+ */
+
+/*
+Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', {
+ get: function() {
+ return this.scale.y * this.getLocalBounds().height;
+ },
+ set: function(value) {
+ this.scale.y = value / (this.getLocalBounds().height/this.scale.y);
+ this._height = value;
+ }
+});
+*/
+
/**
* Adds a child to the container.
*
@@ -1465,82 +13914,7 @@ PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer;
*/
PIXI.DisplayObjectContainer.prototype.addChild = function(child)
{
- if(child.parent && child.parent !== this)
- {
- //// COULD BE THIS???
- child.parent.removeChild(child);
- // return;
- }
-
- child.parent = this;
-
- this.children.push(child);
-
- // update the stage refference..
-
- if(this.stage)
- {
- var tmpChild = child;
- do
- {
- if(tmpChild.interactive)this.stage.dirty = true;
- tmpChild.stage = this.stage;
- tmpChild = tmpChild._iNext;
- }
- while(tmpChild);
- }
-
- // LINKED LIST //
-
- // modify the list..
- var childFirst = child.first;
- var childLast = child.last;
- var nextObject;
- var previousObject;
-
- // this could be wrong if there is a filter??
- if(this._filters || this._mask)
- {
- previousObject = this.last._iPrev;
- }
- else
- {
- previousObject = this.last;
- }
-
- nextObject = previousObject._iNext;
-
- // always true in this case
- // need to make sure the parents last is updated too
- var updateLast = this;
- var prevLast = previousObject;
-
- while(updateLast)
- {
- if(updateLast.last === prevLast)
- {
- updateLast.last = child.last;
- }
- updateLast = updateLast.parent;
- }
-
- if(nextObject)
- {
- nextObject._iPrev = childLast;
- childLast._iNext = nextObject;
- }
-
- childFirst._iPrev = previousObject;
- previousObject._iNext = childFirst;
-
- // need to remove any render groups..
- if(this.__renderGroup)
- {
- // being used by a renderTexture.. if it exists then it must be from a render texture;
- if(child.__renderGroup)child.__renderGroup.removeDisplayObjectAndChildren(child);
- // add them to the new render group..
- this.__renderGroup.addDisplayObjectAndChildren(child);
- }
+ this.addChildAt(child, this.children.length);
};
/**
@@ -1554,76 +13928,16 @@ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index)
{
if(index >= 0 && index <= this.children.length)
{
- if(child.parent !== undefined)
+ if(child.parent)
{
child.parent.removeChild(child);
}
child.parent = this;
- if(this.stage)
- {
- var tmpChild = child;
- do
- {
- if(tmpChild.interactive)this.stage.dirty = true;
- tmpChild.stage = this.stage;
- tmpChild = tmpChild._iNext;
- }
- while(tmpChild);
- }
-
- // modify the list..
- var childFirst = child.first;
- var childLast = child.last;
- var nextObject;
- var previousObject;
-
- if(index === this.children.length)
- {
- previousObject = this.last;
- var updateLast = this;
- var prevLast = this.last;
- while(updateLast)
- {
- if(updateLast.last === prevLast)
- {
- updateLast.last = child.last;
- }
- updateLast = updateLast.parent;
- }
- }
- else if(index === 0)
- {
- previousObject = this;
- }
- else
- {
- previousObject = this.children[index-1].last;
- }
-
- nextObject = previousObject._iNext;
-
- // always true in this case
- if(nextObject)
- {
- nextObject._iPrev = childLast;
- childLast._iNext = nextObject;
- }
-
- childFirst._iPrev = previousObject;
- previousObject._iNext = childFirst;
-
this.children.splice(index, 0, child);
- // need to remove any render groups..
- if(this.__renderGroup)
- {
- // being used by a renderTexture.. if it exists then it must be from a render texture;
- if(child.__renderGroup)child.__renderGroup.removeDisplayObjectAndChildren(child);
- // add them to the new render group..
- this.__renderGroup.addDisplayObjectAndChildren(child);
- }
+ if(this.stage)child.setStageReference(this.stage);
}
else
{
@@ -1652,23 +13966,13 @@ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2)
throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.');
}
- this.removeChild(child);
- this.removeChild(child2);
-
- if(index1 < index2)
- {
- this.addChildAt(child2, index1);
- this.addChildAt(child, index2);
- }
- else
- {
- this.addChildAt(child, index2);
- this.addChildAt(child2, index1);
- }
+ this.children[index1] = child2;
+ this.children[index2] = child;
+
};
/**
- * Returns the Child at the specified index
+ * Returns the child at the specified index
*
* @method getChildAt
* @param index {Number} The index to get the child from
@@ -1681,7 +13985,7 @@ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index)
}
else
{
- throw new Error('Both the supplied DisplayObjects must be a child of the caller ' + this);
+ throw new Error('The supplied DisplayObjects must be a child of the caller ' + this);
}
};
@@ -1696,53 +14000,8 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child)
var index = this.children.indexOf( child );
if ( index !== -1 )
{
- // unlink //
- // modify the list..
- var childFirst = child.first;
- var childLast = child.last;
-
- var nextObject = childLast._iNext;
- var previousObject = childFirst._iPrev;
-
- if(nextObject)nextObject._iPrev = previousObject;
- previousObject._iNext = nextObject;
-
- if(this.last === childLast)
- {
- var tempLast = childFirst._iPrev;
- // need to make sure the parents last is updated too
- var updateLast = this;
-
- while(updateLast.last === childLast)
- {
- updateLast.last = tempLast;
- updateLast = updateLast.parent;
- if(!updateLast)break;
-
- }
- }
-
- childLast._iNext = null;
- childFirst._iPrev = null;
-
// update the stage reference..
- if(this.stage)
- {
- var tmpChild = child;
- do
- {
- if(tmpChild.interactive)this.stage.dirty = true;
- tmpChild.stage = null;
- tmpChild = tmpChild._iNext;
- }
- while(tmpChild);
- }
-
- // webGL trim
- if(child.__renderGroup)
- {
- child.__renderGroup.removeDisplayObjectAndChildren(child);
- }
+ if(this.stage)child.removeStageReference();
child.parent = undefined;
this.children.splice( index, 1 );
@@ -1753,41 +14012,276 @@ PIXI.DisplayObjectContainer.prototype.removeChild = function(child)
}
};
+
+/**
+* Removes all the children
+*
+* @method removeAll
+* NOT tested yet
+*/
+/* PIXI.DisplayObjectContainer.prototype.removeAll = function()
+{
+
+
+ for(var i = 0 , j = this.children.length; i < j; i++)
+ {
+ this.removeChild(this.children[i]);
+ }
+
+};
+*/
/*
- * Updates the container's children's transform for rendering
+ * Updates the container's childrens transform for rendering
*
* @method updateTransform
* @private
*/
PIXI.DisplayObjectContainer.prototype.updateTransform = function()
{
+ //this._currentBounds = null;
+
if(!this.visible)return;
PIXI.DisplayObject.prototype.updateTransform.call( this );
+ if(this._cacheAsBitmap)return;
+
for(var i=0,j=this.children.length; i childMaxX ? maxX : childMaxX;
+ maxY = maxY > childMaxY ? maxY : childMaxY;
+ }
+
+ if(!childVisible)
+ return PIXI.EmptyRectangle;
+
+ var bounds = this._bounds;
+
+ bounds.x = minX;
+ bounds.y = minY;
+ bounds.width = maxX - minX;
+ bounds.height = maxY - minY;
+
+ // TODO: store a reference so that if this function gets called again in the render cycle we do not have to recalculate
+ //this._currentBounds = bounds;
+
+ return bounds;
+};
+
+PIXI.DisplayObjectContainer.prototype.getLocalBounds = function()
+{
+ var matrixCache = this.worldTransform;
+
+ this.worldTransform = PIXI.identityMatrix;
+
+ for(var i=0,j=this.children.length; i maxX ? x1 : maxX;
+ maxX = x2 > maxX ? x2 : maxX;
+ maxX = x3 > maxX ? x3 : maxX;
+ maxX = x4 > maxX ? x4 : maxX;
+
+ maxY = y1 > maxY ? y1 : maxY;
+ maxY = y2 > maxY ? y2 : maxY;
+ maxY = y3 > maxY ? y3 : maxY;
+ maxY = y4 > maxY ? y4 : maxY;
+
+ var bounds = this._bounds;
+
+ bounds.x = minX;
+ bounds.width = maxX - minX;
+
+ bounds.y = minY;
+ bounds.height = maxY - minY;
+
+ // store a reference so that if this function gets called again in the render cycle we do not have to recalculate
+ this._currentBounds = bounds;
+
+ return bounds;
+};
+
+/**
+* Renders the object using the WebGL renderer
+*
+* @method _renderWebGL
+* @param renderSession {RenderSession}
+* @private
+*/
+PIXI.Sprite.prototype._renderWebGL = function(renderSession)
+{
+ // if the sprite is not visible or the alpha is 0 then no need to render this element
+ if(!this.visible || this.alpha <= 0)return;
+
+ var i,j;
+
+ // do a quick check to see if this element has a mask or a filter.
+ if(this._mask || this._filters)
+ {
+ var spriteBatch = renderSession.spriteBatch;
+
+ if(this._mask)
+ {
+ spriteBatch.stop();
+ renderSession.maskManager.pushMask(this.mask, renderSession);
+ spriteBatch.start();
+ }
+
+ if(this._filters)
+ {
+ spriteBatch.flush();
+ renderSession.filterManager.pushFilter(this._filterBlock);
+ }
+
+ // add this sprite to the batch
+ spriteBatch.render(this);
+
+ // now loop through the children and make sure they get rendered
+ for(i=0,j=this.children.length; i spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if(j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j] + ' ';
+ spaceLeft = this.style.wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += words[j] + ' ';
+ }
+ }
+
+ if (i < lines.length-1)
+ {
+ result += '\n';
+ }
+ }
+ return result;
+};
+
+/**
+ * Destroys this text object
+ *
+ * @method destroy
+ * @param destroyTexture {Boolean}
+ */
+PIXI.Text.prototype.destroy = function(destroyTexture)
+{
+ if(destroyTexture)
+ {
+ this.texture.destroy();
+ }
+
+};
+
+PIXI.Text.heightCache = {};
+
+/**
+ * @author Mat Groves http://matgroves.com/ @Doormat23
+ */
+
+/**
+ * A Text Object will create a line(s) of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n'
+ * You can generate the fnt files using
+ * http://www.angelcode.com/products/bmfont/ for windows or
+ * http://www.bmglyph.com/ for mac.
+ *
+ * @class BitmapText
+ * @extends DisplayObjectContainer
+ * @constructor
+ * @param text {String} The copy that you would like the text to display
+ * @param style {Object} The style parameters
+ * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously)
+ * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text
+ */
+PIXI.BitmapText = function(text, style)
+{
+ PIXI.DisplayObjectContainer.call(this);
+
+ this._pool = [];
+
+ this.setText(text);
+ this.setStyle(style);
+ this.updateText();
+ this.dirty = false;
+};
+
+// constructor
+PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
+PIXI.BitmapText.prototype.constructor = PIXI.BitmapText;
+
+/**
+ * Set the copy for the text object
+ *
+ * @method setText
+ * @param text {String} The copy that you would like the text to display
+ */
+PIXI.BitmapText.prototype.setText = function(text)
+{
+ this.text = text || ' ';
+ this.dirty = true;
+};
+
+/**
+ * Set the style of the text
+ * style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously)
+ * [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text
+ *
+ * @method setStyle
+ * @param style {Object} The style parameters, contained as properties of an object
+ */
+PIXI.BitmapText.prototype.setStyle = function(style)
+{
+ style = style || {};
+ style.align = style.align || 'left';
+ this.style = style;
+
+ var font = style.font.split(' ');
+ this.fontName = font[font.length - 1];
+ this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size;
+
+ this.dirty = true;
+ this.tint = style.tint;
+};
+
+/**
+ * Renders text and updates it when needed
+ *
+ * @method updateText
+ * @private
+ */
+PIXI.BitmapText.prototype.updateText = function()
+{
+ var data = PIXI.BitmapText.fonts[this.fontName];
+ var pos = new PIXI.Point();
+ var prevCharCode = null;
+ var chars = [];
+ var maxLineWidth = 0;
+ var lineWidths = [];
+ var line = 0;
+ var scale = this.fontSize / data.size;
+
+
+ for(var i = 0; i < this.text.length; i++)
+ {
+ var charCode = this.text.charCodeAt(i);
+ if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i)))
+ {
+ lineWidths.push(pos.x);
+ maxLineWidth = Math.max(maxLineWidth, pos.x);
+ line++;
+
+ pos.x = 0;
+ pos.y += data.lineHeight;
+ prevCharCode = null;
+ continue;
+ }
+
+ var charData = data.chars[charCode];
+ if(!charData) continue;
+
+ if(prevCharCode && charData[prevCharCode])
+ {
+ pos.x += charData.kerning[prevCharCode];
+ }
+ chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)});
+ pos.x += charData.xAdvance;
+
+ prevCharCode = charCode;
+ }
+
+ lineWidths.push(pos.x);
+ maxLineWidth = Math.max(maxLineWidth, pos.x);
+
+ var lineAlignOffsets = [];
+ for(i = 0; i <= line; i++)
+ {
+ var alignOffset = 0;
+ if(this.style.align === 'right')
+ {
+ alignOffset = maxLineWidth - lineWidths[i];
+ }
+ else if(this.style.align === 'center')
+ {
+ alignOffset = (maxLineWidth - lineWidths[i]) / 2;
+ }
+ lineAlignOffsets.push(alignOffset);
+ }
+
+ var lenChildren = this.children.length;
+ var lenChars = chars.length;
+ var tint = this.tint || 0xFFFFFF;
+ for(i = 0; i < lenChars; i++)
+ {
+ var c = i < lenChildren ? this.children[i] : this._pool.pop(); // get old child if have. if not - take from pool.
+
+ if (c) c.setTexture(chars[i].texture); // check if got one before.
+ else c = new PIXI.Sprite(chars[i].texture); // if no create new one.
+
+ c.position.x = (chars[i].position.x + lineAlignOffsets[chars[i].line]) * scale;
+ c.position.y = chars[i].position.y * scale;
+ c.scale.x = c.scale.y = scale;
+ c.tint = tint;
+ if (!c.parent) this.addChild(c);
+ }
+
+ // remove unnecessary children.
+ // and put their into the pool.
+ while(this.children.length > lenChars)
+ {
+ var child = this.getChildAt(this.children.length - 1);
+ this._pool.push(child);
+ this.removeChild(child);
+ }
+
+
+ /**
+ * [read-only] The width of the overall text, different from fontSize,
+ * which is defined in the style object
+ *
+ * @property textWidth
+ * @type Number
+ */
+ this.textWidth = maxLineWidth * scale;
+
+ /**
+ * [read-only] The height of the overall text, different from fontSize,
+ * which is defined in the style object
+ *
+ * @property textHeight
+ * @type Number
+ */
+ this.textHeight = (pos.y + data.lineHeight) * scale;
+};
+
+/**
+ * Updates the transform of this object
+ *
+ * @method updateTransform
+ * @private
+ */
+PIXI.BitmapText.prototype.updateTransform = function()
+{
+ if(this.dirty)
+ {
+ this.updateText();
+ this.dirty = false;
+ }
+
+ PIXI.DisplayObjectContainer.prototype.updateTransform.call(this);
+};
+
+PIXI.BitmapText.fonts = {};
+
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
@@ -1978,8 +15447,15 @@ PIXI.Sprite.fromImage = function(imageId)
* @class Stage
* @extends DisplayObjectContainer
* @constructor
- * @param backgroundColor {Number} the background color of the stage, easiest way to pass this in is in hex format
+ * @param backgroundColor {Number} the background color of the stage, you have to pass this in is in hex format
* like: 0xFFFFFF for white
+ *
+ * Creating a stage is a mandatory process when you use Pixi, which is as simple as this :
+ * var stage = new PIXI.Stage(0xFFFFFF);
+ * where the parameter given is the background colour of the stage, in hex
+ * you will use this stage instance to add your sprites to it and therefore to the renderer
+ * Here is how to add a sprite to the stage :
+ * stage.addChild(sprite);
*/
PIXI.Stage = function(backgroundColor)
{
@@ -1993,7 +15469,7 @@ PIXI.Stage = function(backgroundColor)
* @readOnly
* @private
*/
- this.worldTransform = PIXI.mat3.create();
+ this.worldTransform = new PIXI.Matrix();
/**
* Whether or not the stage is interactive
@@ -2020,17 +15496,13 @@ PIXI.Stage = function(backgroundColor)
*/
this.dirty = true;
- this.__childrenAdded = [];
- this.__childrenRemoved = [];
-
- //the stage is it's own stage
+ //the stage is its own stage
this.stage = this;
//optimize hit detection a bit
this.stage.hitArea = new PIXI.Rectangle(0,0,100000, 100000);
this.setBackgroundColor(backgroundColor);
- this.worldVisible = true;
};
// constructor
@@ -2039,7 +15511,7 @@ PIXI.Stage.prototype.constructor = PIXI.Stage;
/**
* Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element.
- * This is useful for when you have other DOM elements ontop of the Canvas element.
+ * This is useful for when you have other DOM elements on top of the Canvas element.
*
* @method setInteractionDelegate
* @param domElement {DOMElement} This new domElement which will receive mouse/touch events
@@ -2058,7 +15530,6 @@ PIXI.Stage.prototype.setInteractionDelegate = function(domElement)
PIXI.Stage.prototype.updateTransform = function()
{
this.worldAlpha = 1;
- this.vcount = PIXI.visibleCount;
for(var i=0,j=this.children.length; i 1) ratio = 1;
-
- perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y);
- num = this.texture.height / 2; //(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio;
- perp.x /= perpLength;
- perp.y /= perpLength;
-
- perp.x *= num;
- perp.y *= num;
-
- verticies[index] = point.x + perp.x;
- verticies[index+1] = point.y + perp.y;
- verticies[index+2] = point.x - perp.x;
- verticies[index+3] = point.y - perp.y;
-
- lastPoint = point;
- }
-
- PIXI.DisplayObjectContainer.prototype.updateTransform.call( this );
-};
-
-PIXI.Rope.prototype.setTexture = function(texture)
-{
- // stop current texture
- this.texture = texture;
- this.updateFrame = true;
-};
-
-/**
- * @author Mat Groves http://matgroves.com/
- */
-
-/**
- * A tiling sprite is a fast way of rendering a tiling image
- *
- * @class TilingSprite
- * @extends DisplayObjectContainer
- * @constructor
- * @param texture {Texture} the texture of the tiling sprite
- * @param width {Number} the width of the tiling sprite
- * @param height {Number} the height of the tiling sprite
- */
-PIXI.TilingSprite = function(texture, width, height)
-{
- PIXI.DisplayObjectContainer.call( this );
+PIXI.EventTarget = function () {
/**
- * The texture that the sprite is using
+ * Holds all the listeners
*
- * @property texture
- * @type Texture
- */
- this.texture = texture;
-
- /**
- * The width of the tiling sprite
- *
- * @property width
- * @type Number
- */
- this.width = width;
-
- /**
- * The height of the tiling sprite
- *
- * @property height
- * @type Number
- */
- this.height = height;
-
- /**
- * The scaling of the image that is being tiled
- *
- * @property tileScale
- * @type Point
- */
- this.tileScale = new PIXI.Point(1,1);
-
- /**
- * The offset position of the image that is being tiled
- *
- * @property tilePosition
- * @type Point
- */
- this.tilePosition = new PIXI.Point(0,0);
-
- this.renderable = true;
-
- this.blendMode = PIXI.blendModes.NORMAL;
-};
-
-// constructor
-PIXI.TilingSprite.prototype = Object.create( PIXI.DisplayObjectContainer.prototype );
-PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite;
-
-/**
- * Sets the texture of the tiling sprite
- *
- * @method setTexture
- * @param texture {Texture} The PIXI texture that is displayed by the sprite
- */
-PIXI.TilingSprite.prototype.setTexture = function(texture)
-{
- //TODO SET THE TEXTURES
- //TODO VISIBILITY
-
- // stop current texture
- this.texture = texture;
- this.updateFrame = true;
-};
-
-/**
- * When the texture is updated, this event will fire to update the frame
- *
- * @method onTextureUpdate
- * @param event
- * @private
- */
-PIXI.TilingSprite.prototype.onTextureUpdate = function()
-{
- this.updateFrame = true;
-};
-
-/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
- */
-
-/**
- * This is the base class for creating a pixi.js filter. Currently only webGL supports filters.
- * If you want to make a custom filter this should be your base class.
- * @class AbstractFilter
- * @constructor
- * @param fragmentSrc
- * @param uniforms
- */
-PIXI.AbstractFilter = function(fragmentSrc, uniforms)
-{
- /**
- * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion.
- * For example the blur filter has two passes blurX and blurY.
- * @property passes
- * @type Array an array of filter objects
- * @private
- */
- this.passes = [this];
-
-
- this.dirty = true;
- this.padding = 0;
-
- /**
- @property uniforms
- @private
- */
- this.uniforms = uniforms || {};
-
- this.fragmentSrc = fragmentSrc || [];
-};
-
-/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
- */
-
-
-
-PIXI.FilterBlock = function()
-{
- this.visible = true;
- this.renderable = true;
-};
-
-/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
- */
-
-
-/**
- * The Graphics class contains a set of methods that you can use to create primitive shapes and lines.
- * It is important to know that with the webGL renderer only simple polys can be filled at this stage
- * Complex polys will not be filled. Heres an example of a complex poly: http://www.goodboydigital.com/wp-content/uploads/2013/06/complexPolygon.png
- *
- * @class Graphics
- * @extends DisplayObjectContainer
- * @constructor
- */
-PIXI.Graphics = function()
-{
- PIXI.DisplayObjectContainer.call( this );
-
- this.renderable = true;
-
- /**
- * The alpha of the fill of this graphics object
- *
- * @property fillAlpha
- * @type Number
- */
- this.fillAlpha = 1;
-
- /**
- * The width of any lines drawn
- *
- * @property lineWidth
- * @type Number
- */
- this.lineWidth = 0;
-
- /**
- * The color of any lines drawn
- *
- * @property lineColor
- * @type String
- */
- this.lineColor = "black";
-
- /**
- * Graphics data
- *
- * @property graphicsData
- * @type Array
- * @private
- */
- this.graphicsData = [];
-
- /**
- * Current path
- *
- * @property currentPath
+ * @property listeneners
* @type Object
- * @private
*/
- this.currentPath = {points:[]};
+ var listeners = {};
+
+ /**
+ * Adds a listener for a specific event
+ *
+ * @method addEventListener
+ * @param type {string} A string representing the event type to listen for.
+ * @param listener {function} The callback function that will be fired when the event occurs
+ */
+ this.addEventListener = this.on = function ( type, listener ) {
+
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+ }
+
+ };
+
+ /**
+ * Fires the event, ie pretends that the event has happened
+ *
+ * @method dispatchEvent
+ * @param event {Event} the event object
+ */
+ this.dispatchEvent = this.emit = function ( event ) {
+
+ if ( !listeners[ event.type ] || !listeners[ event.type ].length ) {
+
+ return;
+
+ }
+
+ for(var i = 0, l = listeners[ event.type ].length; i < l; i++) {
+
+ listeners[ event.type ][ i ]( event );
+
+ }
+
+ };
+
+ /**
+ * Removes the specified listener that was assigned to the specified event type
+ *
+ * @method removeEventListener
+ * @param type {string} A string representing the event type which will have its listener removed
+ * @param listener {function} The callback function that was be fired when the event occured
+ */
+ this.removeEventListener = this.off = function ( type, listener ) {
+
+ var index = listeners[ type ].indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listeners[ type ].splice( index, 1 );
+
+ }
+
+ };
+
+ /**
+ * Removes all the listeners that were active for the specified event type
+ *
+ * @method removeAllEventListeners
+ * @param type {string} A string representing the event type which will have all its listeners removed
+ */
+ this.removeAllEventListeners = function( type ) {
+ var a = listeners[type];
+ if (a)
+ a.length = 0;
+ };
};
-// constructor
-PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype );
-PIXI.Graphics.prototype.constructor = PIXI.Graphics;
+/*
+ PolyK library
+ url: http://polyk.ivank.net
+ Released under MIT licence.
+
+ Copyright (c) 2012 Ivan Kuckir
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ This is an amazing lib!
+
+ slightly modified by Mat Groves (matgroves.com);
+*/
/**
- * Specifies a line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method.
+ * Based on the Polyk library http://polyk.ivank.net released under MIT licence.
+ * This is an amazing lib!
+ * slightly modified by Mat Groves (matgroves.com);
+ * @class PolyK
*
- * @method lineStyle
- * @param lineWidth {Number} width of the line to draw, will update the object's stored style
- * @param color {Number} color of the line to draw, will update the object's stored style
- * @param alpha {Number} alpha of the line to draw, will update the object's stored style
*/
-PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha)
-{
- if (!this.currentPath.points.length) this.graphicsData.pop();
-
- this.lineWidth = lineWidth || 0;
- this.lineColor = color || 0;
- this.lineAlpha = (arguments.length < 3) ? 1 : alpha;
-
- this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
- fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY};
-
- this.graphicsData.push(this.currentPath);
-};
+PIXI.PolyK = {};
/**
- * Moves the current drawing position to (x, y).
+ * Triangulates shapes for webGL graphic fills
*
- * @method moveTo
- * @param x {Number} the X coord to move to
- * @param y {Number} the Y coord to move to
+ * @method Triangulate
+ *
*/
-PIXI.Graphics.prototype.moveTo = function(x, y)
+PIXI.PolyK.Triangulate = function(p)
{
- if (!this.currentPath.points.length) this.graphicsData.pop();
+ var sign = true;
- this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
- fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY};
+ var n = p.length >> 1;
+ if(n < 3) return [];
- this.currentPath.points.push(x, y);
+ var tgs = [];
+ var avl = [];
+ for(var i = 0; i < n; i++) avl.push(i);
- this.graphicsData.push(this.currentPath);
-};
-
-/**
- * Draws a line using the current line style from the current drawing position to (x, y);
- * the current drawing position is then set to (x, y).
- *
- * @method lineTo
- * @param x {Number} the X coord to draw to
- * @param y {Number} the Y coord to draw to
- */
-PIXI.Graphics.prototype.lineTo = function(x, y)
-{
- this.currentPath.points.push(x, y);
- this.dirty = true;
-};
-
-/**
- * Specifies a simple one-color fill that subsequent calls to other Graphics methods
- * (such as lineTo() or drawCircle()) use when drawing.
- *
- * @method beginFill
- * @param color {uint} the color of the fill
- * @param alpha {Number} the alpha
- */
-PIXI.Graphics.prototype.beginFill = function(color, alpha)
-{
- this.filling = true;
- this.fillColor = color || 0;
- this.fillAlpha = (arguments.length < 2) ? 1 : alpha;
-};
-
-/**
- * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method.
- *
- * @method endFill
- */
-PIXI.Graphics.prototype.endFill = function()
-{
- this.filling = false;
- this.fillColor = null;
- this.fillAlpha = 1;
-};
-
-/**
- * @method drawRect
- *
- * @param x {Number} The X coord of the top-left of the rectangle
- * @param y {Number} The Y coord of the top-left of the rectangle
- * @param width {Number} The width of the rectangle
- * @param height {Number} The height of the rectangle
- */
-PIXI.Graphics.prototype.drawRect = function( x, y, width, height )
-{
- if (!this.currentPath.points.length) this.graphicsData.pop();
-
- this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
- fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling,
- points:[x, y, width, height], type:PIXI.Graphics.RECT};
-
- this.graphicsData.push(this.currentPath);
- this.dirty = true;
-};
-
-/**
- * Draws a circle.
- *
- * @method drawCircle
- * @param x {Number} The X coord of the center of the circle
- * @param y {Number} The Y coord of the center of the circle
- * @param radius {Number} The radius of the circle
- */
-PIXI.Graphics.prototype.drawCircle = function( x, y, radius)
-{
- if (!this.currentPath.points.length) this.graphicsData.pop();
-
- this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
- fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling,
- points:[x, y, radius, radius], type:PIXI.Graphics.CIRC};
-
- this.graphicsData.push(this.currentPath);
- this.dirty = true;
-};
-
-/**
- * Draws an ellipse.
- *
- * @method drawEllipse
- * @param x {Number}
- * @param y {Number}
- * @param width {Number}
- * @param height {Number}
- */
-PIXI.Graphics.prototype.drawEllipse = function( x, y, width, height)
-{
- if (!this.currentPath.points.length) this.graphicsData.pop();
-
- this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
- fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling,
- points:[x, y, width, height], type:PIXI.Graphics.ELIP};
-
- this.graphicsData.push(this.currentPath);
- this.dirty = true;
-};
-
-/**
- * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings.
- *
- * @method clear
- */
-PIXI.Graphics.prototype.clear = function()
-{
- this.lineWidth = 0;
- this.filling = false;
-
- this.dirty = true;
- this.clearDirty = true;
- this.graphicsData = [];
-
- this.bounds = null; //new PIXI.Rectangle();
-};
-
-
-PIXI.Graphics.prototype.updateFilterBounds = function()
-{
- if(!this.bounds)
+ i = 0;
+ var al = n;
+ while(al > 3)
{
- var minX = Infinity;
- var maxX = -Infinity;
+ var i0 = avl[(i+0)%al];
+ var i1 = avl[(i+1)%al];
+ var i2 = avl[(i+2)%al];
- var minY = Infinity;
- var maxY = -Infinity;
+ var ax = p[2*i0], ay = p[2*i0+1];
+ var bx = p[2*i1], by = p[2*i1+1];
+ var cx = p[2*i2], cy = p[2*i2+1];
- var points, x, y;
-
- for (var i = 0; i < this.graphicsData.length; i++) {
- var data = this.graphicsData[i];
- var type = data.type;
- var lineWidth = data.lineWidth;
-
- points = data.points;
-
- if(type === PIXI.Graphics.RECT)
+ var earFound = false;
+ if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign))
+ {
+ earFound = true;
+ for(var j = 0; j < al; j++)
{
- x = points.x - lineWidth/2;
- y = points.y - lineWidth/2;
- var width = points.width + lineWidth;
- var height = points.height + lineWidth;
+ var vi = avl[j];
+ if(vi === i0 || vi === i1 || vi === i2) continue;
- minX = x < minX ? x : minX;
- maxX = x + width > maxX ? x + width : maxX;
-
- minY = y < minY ? x : minY;
- maxY = y + height > maxY ? y + height : maxY;
+ if(PIXI.PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) {
+ earFound = false;
+ break;
+ }
}
- else if(type === PIXI.Graphics.CIRC || type === PIXI.Graphics.ELIP)
+ }
+
+ if(earFound)
+ {
+ tgs.push(i0, i1, i2);
+ avl.splice((i+1)%al, 1);
+ al--;
+ i = 0;
+ }
+ else if(i++ > 3*al)
+ {
+ // need to flip flip reverse it!
+ // reset!
+ if(sign)
{
- x = points.x;
- y = points.y;
- var radius = points.radius + lineWidth/2;
+ tgs = [];
+ avl = [];
+ for(i = 0; i < n; i++) avl.push(i);
- minX = x - radius < minX ? x - radius : minX;
- maxX = x + radius > maxX ? x + radius : maxX;
+ i = 0;
+ al = n;
- minY = y - radius < minY ? y - radius : minY;
- maxY = y + radius > maxY ? y + radius : maxY;
+ sign = false;
}
else
{
- // POLY
- for (var j = 0; j < points.length; j+=2)
- {
-
- x = points[j];
- y = points[j+1];
-
- minX = x-lineWidth < minX ? x-lineWidth : minX;
- maxX = x+lineWidth > maxX ? x+lineWidth : maxX;
-
- minY = y-lineWidth < minY ? y-lineWidth : minY;
- maxY = y+lineWidth > maxY ? y+lineWidth : maxY;
- }
+ window.console.log("PIXI Warning: shape too complex to fill");
+ return [];
}
}
-
- this.bounds = new PIXI.Rectangle(minX, minY, maxX - minX, maxY - minY);
}
-// console.log(this.bounds);
-};
-// SOME TYPES:
-PIXI.Graphics.POLY = 0;
-PIXI.Graphics.RECT = 1;
-PIXI.Graphics.CIRC = 2;
-PIXI.Graphics.ELIP = 3;
+ tgs.push(avl[0], avl[1], avl[2]);
+ return tgs;
+};
/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
+ * Checks whether a point is within a triangle
+ *
+ * @method _PointInTriangle
+ * @param px {Number} x coordinate of the point to test
+ * @param py {Number} y coordinate of the point to test
+ * @param ax {Number} x coordinate of the a point of the triangle
+ * @param ay {Number} y coordinate of the a point of the triangle
+ * @param bx {Number} x coordinate of the b point of the triangle
+ * @param by {Number} y coordinate of the b point of the triangle
+ * @param cx {Number} x coordinate of the c point of the triangle
+ * @param cy {Number} y coordinate of the c point of the triangle
+ * @private
*/
+PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy)
+{
+ var v0x = cx-ax;
+ var v0y = cy-ay;
+ var v1x = bx-ax;
+ var v1y = by-ay;
+ var v2x = px-ax;
+ var v2y = py-ay;
+ var dot00 = v0x*v0x+v0y*v0y;
+ var dot01 = v0x*v1x+v0y*v1y;
+ var dot02 = v0x*v2x+v0y*v2y;
+ var dot11 = v1x*v1x+v1y*v1y;
+ var dot12 = v1x*v2x+v1y*v2y;
+
+ var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+ var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ // Check if point is in triangle
+ return (u >= 0) && (v >= 0) && (u + v < 1);
+};
/**
- * A set of functions used by the canvas renderer to draw the primitive graphics data
+ * Checks whether a shape is convex
*
- * @class CanvasGraphics
- */
-PIXI.CanvasGraphics = function()
-{
-
-};
-
-
-/*
- * Renders the graphics object
- *
- * @static
+ * @method _convex
+ *
* @private
- * @method renderGraphics
- * @param graphics {Graphics}
- * @param context {Context2D}
*/
-PIXI.CanvasGraphics.renderGraphics = function(graphics, context)
+PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign)
{
- var worldAlpha = graphics.worldAlpha;
- var color = '';
-
- for (var i = 0; i < graphics.graphicsData.length; i++)
- {
- var data = graphics.graphicsData[i];
- var points = data.points;
-
- context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6);
-
- context.lineWidth = data.lineWidth;
-
- if(data.type === PIXI.Graphics.POLY)
- {
- context.beginPath();
-
- context.moveTo(points[0], points[1]);
-
- for (var j=1; j < points.length/2; j++)
- {
- context.lineTo(points[j * 2], points[j * 2 + 1]);
- }
-
- // if the first and last point are the same close the path - much neater :)
- if(points[0] === points[points.length-2] && points[1] === points[points.length-1])
- {
- context.closePath();
- }
-
- if(data.fill)
- {
- context.globalAlpha = data.fillAlpha * worldAlpha;
- context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
- context.fill();
- }
- if(data.lineWidth)
- {
- context.globalAlpha = data.lineAlpha * worldAlpha;
- context.stroke();
- }
- }
- else if(data.type === PIXI.Graphics.RECT)
- {
-
- if(data.fillColor || data.fillColor === 0)
- {
- context.globalAlpha = data.fillAlpha * worldAlpha;
- context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
- context.fillRect(points[0], points[1], points[2], points[3]);
-
- }
- if(data.lineWidth)
- {
- context.globalAlpha = data.lineAlpha * worldAlpha;
- context.strokeRect(points[0], points[1], points[2], points[3]);
- }
-
- }
- else if(data.type === PIXI.Graphics.CIRC)
- {
- // TODO - need to be Undefined!
- context.beginPath();
- context.arc(points[0], points[1], points[2],0,2*Math.PI);
- context.closePath();
-
- if(data.fill)
- {
- context.globalAlpha = data.fillAlpha * worldAlpha;
- context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
- context.fill();
- }
- if(data.lineWidth)
- {
- context.globalAlpha = data.lineAlpha * worldAlpha;
- context.stroke();
- }
- }
- else if(data.type === PIXI.Graphics.ELIP)
- {
-
- // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
-
- var ellipseData = data.points;
-
- var w = ellipseData[2] * 2;
- var h = ellipseData[3] * 2;
-
- var x = ellipseData[0] - w/2;
- var y = ellipseData[1] - h/2;
-
- context.beginPath();
-
- var kappa = 0.5522848,
- ox = (w / 2) * kappa, // control point offset horizontal
- oy = (h / 2) * kappa, // control point offset vertical
- xe = x + w, // x-end
- ye = y + h, // y-end
- xm = x + w / 2, // x-middle
- ym = y + h / 2; // y-middle
-
- context.moveTo(x, ym);
- context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
- context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
- context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
- context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
-
- context.closePath();
-
- if(data.fill)
- {
- context.globalAlpha = data.fillAlpha * worldAlpha;
- context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
- context.fill();
- }
- if(data.lineWidth)
- {
- context.globalAlpha = data.lineAlpha * worldAlpha;
- context.stroke();
- }
- }
- }
-};
-
-/*
- * Renders a graphics mask
- *
- * @static
- * @private
- * @method renderGraphicsMask
- * @param graphics {Graphics}
- * @param context {Context2D}
- */
-PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context)
-{
- var len = graphics.graphicsData.length;
-
- if(len === 0) return;
-
- if(len > 1)
- {
- len = 1;
- window.console.log('Pixi.js warning: masks in canvas can only mask using the first path in the graphics object');
- }
-
- for (var i = 0; i < 1; i++)
- {
- var data = graphics.graphicsData[i];
- var points = data.points;
-
- if(data.type === PIXI.Graphics.POLY)
- {
- context.beginPath();
- context.moveTo(points[0], points[1]);
-
- for (var j=1; j < points.length/2; j++)
- {
- context.lineTo(points[j * 2], points[j * 2 + 1]);
- }
-
- // if the first and last point are the same close the path - much neater :)
- if(points[0] === points[points.length-2] && points[1] === points[points.length-1])
- {
- context.closePath();
- }
-
- }
- else if(data.type === PIXI.Graphics.RECT)
- {
- context.beginPath();
- context.rect(points[0], points[1], points[2], points[3]);
- context.closePath();
- }
- else if(data.type === PIXI.Graphics.CIRC)
- {
- // TODO - need to be Undefined!
- context.beginPath();
- context.arc(points[0], points[1], points[2],0,2*Math.PI);
- context.closePath();
- }
- else if(data.type === PIXI.Graphics.ELIP)
- {
-
- // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
- var ellipseData = data.points;
-
- var w = ellipseData[2] * 2;
- var h = ellipseData[3] * 2;
-
- var x = ellipseData[0] - w/2;
- var y = ellipseData[1] - h/2;
-
- context.beginPath();
-
- var kappa = 0.5522848,
- ox = (w / 2) * kappa, // control point offset horizontal
- oy = (h / 2) * kappa, // control point offset vertical
- xe = x + w, // x-end
- ye = y + h, // y-end
- xm = x + w / 2, // x-middle
- ym = y + h / 2; // y-middle
-
- context.moveTo(x, ym);
- context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
- context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
- context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
- context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
- context.closePath();
- }
- }
+ return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign;
};
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
-/**
- * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
- * Dont forget to add the view to your DOM or you will not see anything :)
- *
- * @class CanvasRenderer
- * @constructor
- * @param width=0 {Number} the width of the canvas view
- * @param height=0 {Number} the height of the canvas view
- * @param view {Canvas} the canvas to use as a view, optional
- * @param transparent=false {Boolean} the transparency of the render view, default false
- */
-PIXI.CanvasRenderer = function(width, height, view, transparent)
+// TODO Alvin and Mat
+// Should we eventually create a Utils class ?
+// Or just move this file to the pixi.js file ?
+PIXI.initDefaultShaders = function()
{
- this.transparent = transparent;
+
+ // PIXI.stripShader = new PIXI.StripShader();
+// PIXI.stripShader.init();
- /**
- * The width of the canvas view
- *
- * @property width
- * @type Number
- * @default 800
- */
- this.width = width || 800;
-
- /**
- * The height of the canvas view
- *
- * @property height
- * @type Number
- * @default 600
- */
- this.height = height || 600;
-
- /**
- * The canvas element that the everything is drawn to
- *
- * @property view
- * @type Canvas
- */
- this.view = view || document.createElement( 'canvas' );
-
- /**
- * The canvas context that the everything is drawn to
- * @property context
- * @type Canvas 2d Context
- */
- this.context = this.view.getContext( '2d' );
-
- //some filter variables
- this.smoothProperty = null;
-
- if('imageSmoothingEnabled' in this.context)
- this.smoothProperty = 'imageSmoothingEnabled';
- else if('webkitImageSmoothingEnabled' in this.context)
- this.smoothProperty = 'webkitImageSmoothingEnabled';
- else if('mozImageSmoothingEnabled' in this.context)
- this.smoothProperty = 'mozImageSmoothingEnabled';
- else if('oImageSmoothingEnabled' in this.context)
- this.smoothProperty = 'oImageSmoothingEnabled';
-
- this.scaleMode = null;
-
- this.refresh = true;
- // hack to enable some hardware acceleration!
- //this.view.style["transform"] = "translatez(0)";
-
- this.view.width = this.width;
- this.view.height = this.height;
- this.count = 0;
};
-// constructor
-PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer;
-
-/**
- * Renders the stage to its canvas view
- *
- * @method render
- * @param stage {Stage} the Stage element to be rendered
- */
-PIXI.CanvasRenderer.prototype.render = function(stage)
+PIXI.CompileVertexShader = function(gl, shaderSrc)
{
- //stage.__childrenAdded = [];
- //stage.__childrenRemoved = [];
+ return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER);
+};
- // update textures if need be
- PIXI.texturesToUpdate = [];
- PIXI.texturesToDestroy = [];
+PIXI.CompileFragmentShader = function(gl, shaderSrc)
+{
+ return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER);
+};
- PIXI.visibleCount++;
- stage.updateTransform();
+PIXI._CompileShader = function(gl, shaderSrc, shaderType)
+{
+ var src = shaderSrc.join("\n");
+ var shader = gl.createShader(shaderType);
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
- // update the background color
- if(this.view.style.backgroundColor !== stage.backgroundColorString && !this.transparent)
- this.view.style.backgroundColor = stage.backgroundColorString;
-
- this.context.setTransform(1,0,0,1,0,0);
- this.context.clearRect(0, 0, this.width, this.height);
- this.renderDisplayObject(stage);
- //as
-
- // run interaction!
- if(stage.interactive)
- {
- //need to add some events!
- if(!stage._interactiveEventsAdded)
- {
- stage._interactiveEventsAdded = true;
- stage.interactionManager.setTarget(this);
- }
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ window.console.log(gl.getShaderInfoLog(shader));
+ return null;
}
- // remove frame updates..
- if(PIXI.Texture.frameUpdates.length > 0)
- {
- PIXI.Texture.frameUpdates = [];
- }
+ return shader;
};
-/**
- * resizes the canvas view to the specified width and height
- *
- * @method resize
- * @param width {Number} the new width of the canvas view
- * @param height {Number} the new height of the canvas view
- */
-PIXI.CanvasRenderer.prototype.resize = function(width, height)
+PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc)
{
- this.width = width;
- this.height = height;
+ var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc);
+ var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc);
- this.view.width = width;
- this.view.height = height;
-};
+ var shaderProgram = gl.createProgram();
-/**
- * Renders a display object
- *
- * @method renderDisplayObject
- * @param displayObject {DisplayObject} The displayObject to render
- * @private
- */
-PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject)
-{
- // no loger recurrsive!
- var transform;
- var context = this.context;
+ gl.attachShader(shaderProgram, vertexShader);
+ gl.attachShader(shaderProgram, fragmentShader);
+ gl.linkProgram(shaderProgram);
- context.globalCompositeOperation = 'source-over';
-
- // one the display object hits this. we can break the loop
- var testObject = displayObject.last._iNext;
- displayObject = displayObject.first;
-
- do
- {
- transform = displayObject.worldTransform;
-
- if(!displayObject.visible)
- {
- displayObject = displayObject.last._iNext;
- continue;
- }
-
- if(!displayObject.renderable)
- {
- displayObject = displayObject._iNext;
- continue;
- }
-
- if(displayObject instanceof PIXI.Sprite)
- {
-
- var frame = displayObject.texture.frame;
-
- //ignore null sources
- if(frame && frame.width && frame.height && displayObject.texture.baseTexture.source)
- {
- context.globalAlpha = displayObject.worldAlpha;
-
- context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]);
-
- //if smoothingEnabled is supported and we need to change the smoothing property for this texture
- if(this.smoothProperty && this.scaleMode !== displayObject.texture.baseTexture.scaleMode) {
- this.scaleMode = displayObject.texture.baseTexture.scaleMode;
- context[this.smoothProperty] = (this.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR);
- }
-
- context.drawImage(displayObject.texture.baseTexture.source,
- frame.x,
- frame.y,
- frame.width,
- frame.height,
- (displayObject.anchor.x) * -frame.width,
- (displayObject.anchor.y) * -frame.height,
- frame.width,
- frame.height);
- }
- }
- else if(displayObject instanceof PIXI.Strip)
- {
- context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]);
- this.renderStrip(displayObject);
- }
- else if(displayObject instanceof PIXI.TilingSprite)
- {
- context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]);
- this.renderTilingSprite(displayObject);
- }
- else if(displayObject instanceof PIXI.CustomRenderable)
- {
- context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]);
- displayObject.renderCanvas(this);
- }
- else if(displayObject instanceof PIXI.Graphics)
- {
- context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]);
- PIXI.CanvasGraphics.renderGraphics(displayObject, context);
- }
- else if(displayObject instanceof PIXI.FilterBlock)
- {
- if(displayObject.data instanceof PIXI.Graphics)
- {
- var mask = displayObject.data;
-
- if(displayObject.open)
- {
- context.save();
-
- var cacheAlpha = mask.alpha;
- var maskTransform = mask.worldTransform;
-
- context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5]);
-
- mask.worldAlpha = 0.5;
-
- context.worldAlpha = 0;
-
- PIXI.CanvasGraphics.renderGraphicsMask(mask, context);
- context.clip();
-
- mask.worldAlpha = cacheAlpha;
- }
- else
- {
- context.restore();
- }
- }
- }
- //count++
- displayObject = displayObject._iNext;
- }
- while(displayObject !== testObject);
-};
-
-/**
- * Renders a flat strip
- *
- * @method renderStripFlat
- * @param strip {Strip} The Strip to render
- * @private
- */
-PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip)
-{
- var context = this.context;
- var verticies = strip.verticies;
-
- var length = verticies.length/2;
- this.count++;
-
- context.beginPath();
- for (var i=1; i < length-2; i++)
- {
- // draw some triangles!
- var index = i*2;
-
- var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4];
- var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];
-
- context.moveTo(x0, y0);
- context.lineTo(x1, y1);
- context.lineTo(x2, y2);
+ if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+ window.console.log("Could not initialise shaders");
}
- context.fillStyle = '#FF0000';
- context.fill();
- context.closePath();
-};
-
-/**
- * Renders a tiling sprite
- *
- * @method renderTilingSprite
- * @param sprite {TilingSprite} The tilingsprite to render
- * @private
- */
-PIXI.CanvasRenderer.prototype.renderTilingSprite = function(sprite)
-{
- var context = this.context;
-
- context.globalAlpha = sprite.worldAlpha;
-
- if(!sprite.__tilePattern)
- sprite.__tilePattern = context.createPattern(sprite.texture.baseTexture.source, 'repeat');
-
- context.beginPath();
-
- var tilePosition = sprite.tilePosition;
- var tileScale = sprite.tileScale;
-
- // offset
- context.scale(tileScale.x,tileScale.y);
- context.translate(tilePosition.x, tilePosition.y);
-
- context.fillStyle = sprite.__tilePattern;
- context.fillRect(-tilePosition.x,-tilePosition.y,sprite.width / tileScale.x, sprite.height / tileScale.y);
-
- context.scale(1/tileScale.x, 1/tileScale.y);
- context.translate(-tilePosition.x, -tilePosition.y);
-
- context.closePath();
-};
-
-/**
- * Renders a strip
- *
- * @method renderStrip
- * @param strip {Strip} The Strip to render
- * @private
- */
-PIXI.CanvasRenderer.prototype.renderStrip = function(strip)
-{
- var context = this.context;
-
- // draw triangles!!
- var verticies = strip.verticies;
- var uvs = strip.uvs;
-
- var length = verticies.length/2;
- this.count++;
-
- for (var i = 1; i < length-2; i++)
- {
- // draw some triangles!
- var index = i*2;
-
- var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4];
- var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];
-
- var u0 = uvs[index] * strip.texture.width, u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width;
- var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height;
-
- context.save();
- context.beginPath();
- context.moveTo(x0, y0);
- context.lineTo(x1, y1);
- context.lineTo(x2, y2);
- context.closePath();
-
- context.clip();
-
- // Compute matrix transform
- var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2;
- var deltaA = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2;
- var deltaB = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2;
- var deltaC = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2;
- var deltaD = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2;
- var deltaE = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2;
- var deltaF = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2;
-
- context.transform(deltaA / delta, deltaD / delta,
- deltaB / delta, deltaE / delta,
- deltaC / delta, deltaF / delta);
-
- context.drawImage(strip.texture.baseTexture.source, 0, 0);
- context.restore();
- }
+ return shaderProgram;
};
/**
@@ -3489,11 +15915,17 @@ PIXI.CanvasRenderer.prototype.renderStrip = function(strip)
*/
/**
-* @class PIXI.PixiShader
+* @class PixiShader
* @constructor
*/
-PIXI.PixiShader = function()
+PIXI.PixiShader = function(gl)
{
+ /**
+ * @property gl
+ * @type WebGLContext
+ */
+ this.gl = gl;
+
/**
* @property {any} program - The WebGL program.
*/
@@ -3505,28 +15937,36 @@ PIXI.PixiShader = function()
this.fragmentSrc = [
'precision lowp float;',
'varying vec2 vTextureCoord;',
- 'varying float vColor;',
+ 'varying vec4 vColor;',
'uniform sampler2D uSampler;',
'void main(void) {',
- ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;',
+ ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;',
'}'
];
+
/**
* @property {number} textureCount - A local texture counter for multi-texture shaders.
*/
this.textureCount = 0;
+
+ this.attributes = [];
+
+ this.init();
};
/**
-* @method PIXI.PixiShader#init
+* Initialises the shader
+* @method init
+*
*/
PIXI.PixiShader.prototype.init = function()
{
- var program = PIXI.compileProgram(this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc);
- var gl = PIXI.gl;
+ var gl = this.gl;
+ var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc);
+
gl.useProgram(program);
// get and store the uniforms for the shader
@@ -3537,8 +15977,24 @@ PIXI.PixiShader.prototype.init = function()
// get and store the attributes
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
- this.colorAttribute = gl.getAttribLocation(program, 'aColor');
this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
+ this.colorAttribute = gl.getAttribLocation(program, 'aColor');
+
+
+ // Begin worst hack eva //
+
+ // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters?
+ // maybe its something to do with the current state of the gl context.
+ // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel
+ // If theres any webGL people that know why could happen please help :)
+ if(this.colorAttribute === -1)
+ {
+ this.colorAttribute = 2;
+ }
+
+ this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute];
+
+ // End worst hack eva //
// add those custom shaders!
for (var key in this.uniforms)
@@ -3557,12 +16013,12 @@ PIXI.PixiShader.prototype.init = function()
* Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/
* http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf
*
-* @method PIXI.PixiShader#initUniforms
+* @method initUniforms
*/
PIXI.PixiShader.prototype.initUniforms = function()
{
this.textureCount = 1;
-
+ var gl = this.gl;
var uniform;
for (var key in this.uniforms)
@@ -3588,21 +16044,21 @@ PIXI.PixiShader.prototype.initUniforms = function()
if (type === 'mat2')
{
- uniform.glFunc = PIXI.gl.uniformMatrix2fv;
+ uniform.glFunc = gl.uniformMatrix2fv;
}
else if (type === 'mat3')
{
- uniform.glFunc = PIXI.gl.uniformMatrix3fv;
+ uniform.glFunc = gl.uniformMatrix3fv;
}
else if (type === 'mat4')
{
- uniform.glFunc = PIXI.gl.uniformMatrix4fv;
+ uniform.glFunc = gl.uniformMatrix4fv;
}
}
else
{
// GL function reference
- uniform.glFunc = PIXI.gl['uniform' + type];
+ uniform.glFunc = gl['uniform' + type];
if (type === '2f' || type === '2i')
{
@@ -3626,9 +16082,9 @@ PIXI.PixiShader.prototype.initUniforms = function()
};
/**
-* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture is has loaded)
+* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded)
*
-* @method PIXI.PixiShader#initSampler2D
+* @method initSampler2D
*/
PIXI.PixiShader.prototype.initSampler2D = function(uniform)
{
@@ -3637,8 +16093,10 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform)
return;
}
- PIXI.gl.activeTexture(PIXI.gl['TEXTURE' + this.textureCount]);
- PIXI.gl.bindTexture(PIXI.gl.TEXTURE_2D, uniform.value.baseTexture._glTexture);
+ var gl = this.gl;
+
+ gl.activeTexture(gl['TEXTURE' + this.textureCount]);
+ gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTexture);
// Extended texture data
if (uniform.textureData)
@@ -3655,19 +16113,19 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform)
// magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST
// wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT
- var magFilter = (data.magFilter) ? data.magFilter : PIXI.gl.LINEAR;
- var minFilter = (data.minFilter) ? data.minFilter : PIXI.gl.LINEAR;
- var wrapS = (data.wrapS) ? data.wrapS : PIXI.gl.CLAMP_TO_EDGE;
- var wrapT = (data.wrapT) ? data.wrapT : PIXI.gl.CLAMP_TO_EDGE;
- var format = (data.luminance) ? PIXI.gl.LUMINANCE : PIXI.gl.RGBA;
+ var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR;
+ var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR;
+ var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE;
+ var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE;
+ var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA;
if (data.repeat)
{
- wrapS = PIXI.gl.REPEAT;
- wrapT = PIXI.gl.REPEAT;
+ wrapS = gl.REPEAT;
+ wrapT = gl.REPEAT;
}
- PIXI.gl.pixelStorei(PIXI.gl.UNPACK_FLIP_Y_WEBGL, false);
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY);
if (data.width)
{
@@ -3676,21 +16134,21 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform)
var border = (data.border) ? data.border : 0;
// void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels);
- PIXI.gl.texImage2D(PIXI.gl.TEXTURE_2D, 0, format, width, height, border, format, PIXI.gl.UNSIGNED_BYTE, null);
+ gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null);
}
else
{
// void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels);
- PIXI.gl.texImage2D(PIXI.gl.TEXTURE_2D, 0, format, PIXI.gl.RGBA, PIXI.gl.UNSIGNED_BYTE, uniform.value.baseTexture.source);
+ gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source);
}
- PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_MAG_FILTER, magFilter);
- PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_MIN_FILTER, minFilter);
- PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_WRAP_S, wrapS);
- PIXI.gl.texParameteri(PIXI.gl.TEXTURE_2D, PIXI.gl.TEXTURE_WRAP_T, wrapT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT);
}
- PIXI.gl.uniform1i(uniform.uniformLocation, this.textureCount);
+ gl.uniform1i(uniform.uniformLocation, this.textureCount);
uniform._init = true;
@@ -3701,12 +16159,13 @@ PIXI.PixiShader.prototype.initSampler2D = function(uniform)
/**
* Updates the shader uniform values.
*
-* @method PIXI.PixiShader#syncUniforms
+* @method syncUniforms
*/
PIXI.PixiShader.prototype.syncUniforms = function()
{
this.textureCount = 1;
var uniform;
+ var gl = this.gl;
// This would probably be faster in an array and it would guarantee key order
for (var key in this.uniforms)
@@ -3718,32 +16177,32 @@ PIXI.PixiShader.prototype.syncUniforms = function()
{
if (uniform.glMatrix === true)
{
- uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.transpose, uniform.value);
+ uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value);
}
else
{
- uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value);
+ uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value);
}
}
else if (uniform.glValueLength === 2)
{
- uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value.x, uniform.value.y);
+ uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y);
}
else if (uniform.glValueLength === 3)
{
- uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z);
+ uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z);
}
else if (uniform.glValueLength === 4)
{
- uniform.glFunc.call(PIXI.gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w);
+ uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w);
}
else if (uniform.type === 'sampler2D')
{
if (uniform._init)
{
- PIXI.gl.activeTexture(PIXI.gl['TEXTURE' + this.textureCount]);
- PIXI.gl.bindTexture(PIXI.gl.TEXTURE_2D, uniform.value.baseTexture._glTexture);
- PIXI.gl.uniform1i(uniform.uniformLocation, this.textureCount);
+ gl.activeTexture(gl['TEXTURE' + this.textureCount]);
+ gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl));
+ gl.uniform1i(uniform.uniformLocation, this.textureCount);
this.textureCount++;
}
else
@@ -3755,85 +16214,197 @@ PIXI.PixiShader.prototype.syncUniforms = function()
};
+/**
+* Destroys the shader
+* @method destroy
+*
+*/
+PIXI.PixiShader.prototype.destroy = function()
+{
+ this.gl.deleteProgram( this.program );
+ this.uniforms = null;
+ this.gl = null;
+
+ this.attributes = null;
+};
+
+/**
+*
+* @property defaultVertexSrc
+* @type String
+*/
PIXI.PixiShader.defaultVertexSrc = [
'attribute vec2 aVertexPosition;',
'attribute vec2 aTextureCoord;',
- 'attribute float aColor;',
+ 'attribute vec2 aColor;',
'uniform vec2 projectionVector;',
'uniform vec2 offsetVector;',
- 'varying vec2 vTextureCoord;',
- 'varying float vColor;',
+ 'varying vec2 vTextureCoord;',
+ 'varying vec4 vColor;',
'const vec2 center = vec2(-1.0, 1.0);',
'void main(void) {',
' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);',
' vTextureCoord = aTextureCoord;',
- ' vColor = aColor;',
+ ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;',
+ ' vColor = vec4(color * aColor.x, aColor.x);',
'}'
];
+
+
+
+
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
+ * @author Richard Davey http://www.photonstorm.com @photonstorm
*/
-
-PIXI.PrimitiveShader = function()
+/**
+* @class PixiFastShader
+* @constructor
+* @param gl {WebGLContext} the current WebGL drawing context
+*/
+PIXI.PixiFastShader = function(gl)
{
- // the webGL program..
+
+ /**
+ * @property gl
+ * @type WebGLContext
+ */
+ this.gl = gl;
+
+ /**
+ * @property {any} program - The WebGL program.
+ */
this.program = null;
+ /**
+ * @property {array} fragmentSrc - The fragment shader.
+ */
this.fragmentSrc = [
- 'precision mediump float;',
- 'varying vec4 vColor;',
-
+ 'precision lowp float;',
+ 'varying vec2 vTextureCoord;',
+ 'varying float vColor;',
+ 'uniform sampler2D uSampler;',
'void main(void) {',
- ' gl_FragColor = vColor;',
+ ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;',
'}'
];
- this.vertexSrc = [
+ /**
+ * @property {array} vertexSrc - The vertex shader
+ */
+ this.vertexSrc = [
'attribute vec2 aVertexPosition;',
- 'attribute vec4 aColor;',
- 'uniform mat3 translationMatrix;',
+ 'attribute vec2 aPositionCoord;',
+ 'attribute vec2 aScale;',
+ 'attribute float aRotation;',
+ 'attribute vec2 aTextureCoord;',
+ 'attribute float aColor;',
+
'uniform vec2 projectionVector;',
'uniform vec2 offsetVector;',
- 'uniform float alpha;',
- 'varying vec4 vColor;',
+ 'uniform mat3 uMatrix;',
+
+ 'varying vec2 vTextureCoord;',
+ 'varying float vColor;',
+
+ 'const vec2 center = vec2(-1.0, 1.0);',
'void main(void) {',
- ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);',
- ' v -= offsetVector.xyx;',
- ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);',
- ' vColor = aColor * alpha;',
+ ' vec2 v;',
+ ' vec2 sv = aVertexPosition * aScale;',
+ ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);',
+ ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);',
+ ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;',
+ ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);',
+ ' vTextureCoord = aTextureCoord;',
+ // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;',
+ ' vColor = aColor;',
'}'
];
+
+
+ /**
+ * @property {number} textureCount - A local texture counter for multi-texture shaders.
+ */
+ this.textureCount = 0;
+
+
+ this.init();
};
-PIXI.PrimitiveShader.prototype.init = function()
+/**
+* Initialises the shader
+* @method init
+*
+*/
+PIXI.PixiFastShader.prototype.init = function()
{
- var program = PIXI.compileProgram(this.vertexSrc, this.fragmentSrc);
- var gl = PIXI.gl;
+ var gl = this.gl;
+ var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
+
gl.useProgram(program);
// get and store the uniforms for the shader
+ this.uSampler = gl.getUniformLocation(program, 'uSampler');
+
this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
+ this.dimensions = gl.getUniformLocation(program, 'dimensions');
+ this.uMatrix = gl.getUniformLocation(program, 'uMatrix');
// get and store the attributes
this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
- this.colorAttribute = gl.getAttribLocation(program, 'aColor');
+ this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord');
+
+ this.aScale = gl.getAttribLocation(program, 'aScale');
+ this.aRotation = gl.getAttribLocation(program, 'aRotation');
+
+ this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord');
+ this.colorAttribute = gl.getAttribLocation(program, 'aColor');
+
+
+
+ // Begin worst hack eva //
+
+ // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters?
+ // maybe its somthing to do with the current state of the gl context.
+ // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel
+ // If theres any webGL people that know why could happen please help :)
+ if(this.colorAttribute === -1)
+ {
+ this.colorAttribute = 2;
+ }
+
+ this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute];
+
+ // End worst hack eva //
- this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix');
- this.alpha = gl.getUniformLocation(program, 'alpha');
this.program = program;
};
+/**
+* Destroys the shader
+* @method destroy
+*
+*/
+PIXI.PixiFastShader.prototype.destroy = function()
+{
+ this.gl.deleteProgram( this.program );
+ this.uniforms = null;
+ this.gl = null;
+
+ this.attributes = null;
+};
+
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
@@ -3841,9 +16412,14 @@ PIXI.PrimitiveShader.prototype.init = function()
PIXI.StripShader = function()
{
- // the webGL program..
+ /**
+ * @property {any} program - The WebGL program.
+ */
this.program = null;
+ /**
+ * @property {array} fragmentSrc - The fragment shader.
+ */
this.fragmentSrc = [
'precision mediump float;',
'varying vec2 vTextureCoord;',
@@ -3857,14 +16433,17 @@ PIXI.StripShader = function()
'}'
];
+ /**
+ * @property {array} fragmentSrc - The fragment shader.
+ */
this.vertexSrc = [
'attribute vec2 aVertexPosition;',
'attribute vec2 aTextureCoord;',
'attribute float aColor;',
'uniform mat3 translationMatrix;',
'uniform vec2 projectionVector;',
- 'uniform vec2 offsetVector;',
'varying vec2 vTextureCoord;',
+ 'uniform vec2 offsetVector;',
'varying float vColor;',
'void main(void) {',
@@ -3877,12 +16456,17 @@ PIXI.StripShader = function()
];
};
+/**
+* Initialises the shader
+* @method init
+*
+*/
PIXI.StripShader.prototype.init = function()
{
- var program = PIXI.compileProgram(this.vertexSrc, this.fragmentSrc);
var gl = PIXI.gl;
+ var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
gl.useProgram(program);
// get and store the uniforms for the shader
@@ -3906,1070 +16490,105 @@ PIXI.StripShader.prototype.init = function()
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
-PIXI._batchs = [];
-
/**
- * @private
- */
-PIXI._getBatch = function(gl)
-{
- if(PIXI._batchs.length === 0)
- {
- return new PIXI.WebGLBatch(gl);
- }
- else
- {
- return PIXI._batchs.pop();
- }
-};
-
-/**
- * @private
- */
-PIXI._returnBatch = function(batch)
-{
- batch.clean();
- PIXI._batchs.push(batch);
-};
-
-/**
- * @private
- */
-PIXI._restoreBatchs = function(gl)
-{
- for (var i=0; i < PIXI._batchs.length; i++)
- {
- PIXI._batchs[i].restoreLostContext(gl);
- }
-};
-
-/**
- * A WebGLBatch Enables a group of sprites to be drawn using the same settings.
- * if a group of sprites all have the same baseTexture and blendMode then they can be grouped into a batch.
- * All the sprites in a batch can then be drawn in one go by the GPU which is hugely efficient. ALL sprites
- * in the webGL renderer are added to a batch even if the batch only contains one sprite. Batching is handled
- * automatically by the webGL renderer. A good tip is: the smaller the number of batchs there are, the faster
- * the webGL renderer will run.
- *
- * @class WebGLBatch
- * @constructor
- * @param gl {WebGLContext} an instance of the webGL context
- */
-PIXI.WebGLBatch = function(gl)
+* @class PrimitiveShader
+* @constructor
+* @param gl {WebGLContext} the current WebGL drawing context
+*/
+PIXI.PrimitiveShader = function(gl)
{
+ /**
+ * @property gl
+ * @type WebGLContext
+ */
this.gl = gl;
- this.size = 0;
+ /**
+ * @property {any} program - The WebGL program.
+ */
+ this.program = null;
- this.vertexBuffer = gl.createBuffer();
- this.indexBuffer = gl.createBuffer();
- this.uvBuffer = gl.createBuffer();
- this.colorBuffer = gl.createBuffer();
- this.blendMode = PIXI.blendModes.NORMAL;
- this.dynamicSize = 1;
-};
+ /**
+ * @property fragmentSrc
+ * @type Array
+ */
+ this.fragmentSrc = [
+ 'precision mediump float;',
+ 'varying vec4 vColor;',
-// constructor
-PIXI.WebGLBatch.prototype.constructor = PIXI.WebGLBatch;
+ 'void main(void) {',
+ ' gl_FragColor = vColor;',
+ '}'
+ ];
-/**
- * Cleans the batch so that is can be returned to an object pool and reused
- *
- * @method clean
- */
-PIXI.WebGLBatch.prototype.clean = function()
-{
- this.verticies = [];
- this.uvs = [];
- this.indices = [];
- this.colors = [];
- this.dynamicSize = 1;
- this.texture = null;
- this.last = null;
- this.size = 0;
- this.head = null;
- this.tail = null;
+ /**
+ * @property vertexSrc
+ * @type Array
+ */
+ this.vertexSrc = [
+ 'attribute vec2 aVertexPosition;',
+ 'attribute vec4 aColor;',
+ 'uniform mat3 translationMatrix;',
+ 'uniform vec2 projectionVector;',
+ 'uniform vec2 offsetVector;',
+ 'uniform float alpha;',
+ 'uniform vec3 tint;',
+ 'varying vec4 vColor;',
+
+ 'void main(void) {',
+ ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);',
+ ' v -= offsetVector.xyx;',
+ ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);',
+ ' vColor = aColor * vec4(tint * alpha, alpha);',
+ '}'
+ ];
+
+ this.init();
};
/**
- * Recreates the buffers in the event of a context loss
- *
- * @method restoreLostContext
- * @param gl {WebGLContext}
- */
-PIXI.WebGLBatch.prototype.restoreLostContext = function(gl)
+* Initialises the shader
+* @method init
+*
+*/
+PIXI.PrimitiveShader.prototype.init = function()
{
- this.gl = gl;
- this.vertexBuffer = gl.createBuffer();
- this.indexBuffer = gl.createBuffer();
- this.uvBuffer = gl.createBuffer();
- this.colorBuffer = gl.createBuffer();
-};
-/**
- * inits the batch's texture and blend mode based if the supplied sprite
- *
- * @method init
- * @param sprite {Sprite} the first sprite to be added to the batch. Only sprites with
- * the same base texture and blend mode will be allowed to be added to this batch
- */
-PIXI.WebGLBatch.prototype.init = function(sprite)
-{
- sprite.batch = this;
- this.dirty = true;
- this.blendMode = sprite.blendMode;
- this.texture = sprite.texture.baseTexture;
- this.head = sprite;
- this.tail = sprite;
- this.size = 1;
-
- this.growBatch();
-};
-
-/**
- * inserts a sprite before the specified sprite
- *
- * @method insertBefore
- * @param sprite {Sprite} the sprite to be added
- * @param nextSprite {nextSprite} the first sprite will be inserted before this sprite
- */
-PIXI.WebGLBatch.prototype.insertBefore = function(sprite, nextSprite)
-{
- this.size++;
-
- sprite.batch = this;
- this.dirty = true;
- var tempPrev = nextSprite.__prev;
- nextSprite.__prev = sprite;
- sprite.__next = nextSprite;
-
- if(tempPrev)
- {
- sprite.__prev = tempPrev;
- tempPrev.__next = sprite;
- }
- else
- {
- this.head = sprite;
- }
-};
-
-/**
- * inserts a sprite after the specified sprite
- *
- * @method insertAfter
- * @param sprite {Sprite} the sprite to be added
- * @param previousSprite {Sprite} the first sprite will be inserted after this sprite
- */
-PIXI.WebGLBatch.prototype.insertAfter = function(sprite, previousSprite)
-{
- this.size++;
-
- sprite.batch = this;
- this.dirty = true;
-
- var tempNext = previousSprite.__next;
- previousSprite.__next = sprite;
- sprite.__prev = previousSprite;
-
- if(tempNext)
- {
- sprite.__next = tempNext;
- tempNext.__prev = sprite;
- }
- else
- {
- this.tail = sprite;
- }
-};
-
-/**
- * removes a sprite from the batch
- *
- * @method remove
- * @param sprite {Sprite} the sprite to be removed
- */
-PIXI.WebGLBatch.prototype.remove = function(sprite)
-{
- this.size--;
-
- if(this.size === 0)
- {
- sprite.batch = null;
- sprite.__prev = null;
- sprite.__next = null;
- return;
- }
-
- if(sprite.__prev)
- {
- sprite.__prev.__next = sprite.__next;
- }
- else
- {
- this.head = sprite.__next;
- this.head.__prev = null;
- }
-
- if(sprite.__next)
- {
- sprite.__next.__prev = sprite.__prev;
- }
- else
- {
- this.tail = sprite.__prev;
- this.tail.__next = null;
- }
-
- sprite.batch = null;
- sprite.__next = null;
- sprite.__prev = null;
- this.dirty = true;
-};
-
-/**
- * Splits the batch into two with the specified sprite being the start of the new batch.
- *
- * @method split
- * @param sprite {Sprite} the sprite that indicates where the batch should be split
- * @return {WebGLBatch} the new batch
- */
-PIXI.WebGLBatch.prototype.split = function(sprite)
-{
- this.dirty = true;
-
- var batch = new PIXI.WebGLBatch(this.gl);
- batch.init(sprite);
- batch.texture = this.texture;
- batch.tail = this.tail;
-
- this.tail = sprite.__prev;
- this.tail.__next = null;
-
- sprite.__prev = null;
- // return a splite batch!
-
- // TODO this size is wrong!
- // need to recalculate :/ problem with a linked list!
- // unless it gets calculated in the "clean"?
-
- // need to loop through items as there is no way to know the length on a linked list :/
- var tempSize = 0;
- while(sprite)
- {
- tempSize++;
- sprite.batch = batch;
- sprite = sprite.__next;
- }
-
- batch.size = tempSize;
- this.size -= tempSize;
-
- return batch;
-};
-
-/**
- * Merges two batchs together
- *
- * @method merge
- * @param batch {WebGLBatch} the batch that will be merged
- */
-PIXI.WebGLBatch.prototype.merge = function(batch)
-{
- this.dirty = true;
-
- this.tail.__next = batch.head;
- batch.head.__prev = this.tail;
-
- this.size += batch.size;
-
- this.tail = batch.tail;
-
- var sprite = batch.head;
- while(sprite)
- {
- sprite.batch = this;
- sprite = sprite.__next;
- }
-};
-
-/**
- * Grows the size of the batch. As the elements in the batch cannot have a dynamic size this
- * function is used to increase the size of the batch. It also creates a little extra room so
- * that the batch does not need to be resized every time a sprite is added
- *
- * @method growBatch
- */
-PIXI.WebGLBatch.prototype.growBatch = function()
-{
- var gl = this.gl;
- if( this.size === 1)
- {
- this.dynamicSize = 1;
- }
- else
- {
- this.dynamicSize = this.size * 1.5;
- }
-
- // grow verts
- this.verticies = new Float32Array(this.dynamicSize * 8);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
- gl.bufferData(gl.ARRAY_BUFFER,this.verticies , gl.DYNAMIC_DRAW);
-
- this.uvs = new Float32Array( this.dynamicSize * 8 );
- gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.uvs , gl.DYNAMIC_DRAW);
-
- this.dirtyUVS = true;
-
- this.colors = new Float32Array( this.dynamicSize * 4 );
- gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, this.colors , gl.DYNAMIC_DRAW);
-
- this.dirtyColors = true;
-
- this.indices = new Uint16Array(this.dynamicSize * 6);
- var length = this.indices.length/6;
-
- for (var i = 0; i < length; i++)
- {
- var index2 = i * 6;
- var index3 = i * 4;
- this.indices[index2 + 0] = index3 + 0;
- this.indices[index2 + 1] = index3 + 1;
- this.indices[index2 + 2] = index3 + 2;
- this.indices[index2 + 3] = index3 + 0;
- this.indices[index2 + 4] = index3 + 2;
- this.indices[index2 + 5] = index3 + 3;
- }
-
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
-};
-
-/**
- * Refresh's all the data in the batch and sync's it with the webGL buffers
- *
- * @method refresh
- */
-PIXI.WebGLBatch.prototype.refresh = function()
-{
- if (this.dynamicSize < this.size)
- {
- this.growBatch();
- }
-
- var indexRun = 0;
- var index, colorIndex;
-
- var displayObject = this.head;
-
- while(displayObject)
- {
- index = indexRun * 8;
-
- var texture = displayObject.texture;
-
- var frame = texture.frame;
- var tw = texture.baseTexture.width;
- var th = texture.baseTexture.height;
-
- this.uvs[index + 0] = frame.x / tw;
- this.uvs[index +1] = frame.y / th;
-
- this.uvs[index +2] = (frame.x + frame.width) / tw;
- this.uvs[index +3] = frame.y / th;
-
- this.uvs[index +4] = (frame.x + frame.width) / tw;
- this.uvs[index +5] = (frame.y + frame.height) / th;
-
- this.uvs[index +6] = frame.x / tw;
- this.uvs[index +7] = (frame.y + frame.height) / th;
-
- displayObject.updateFrame = false;
-
- colorIndex = indexRun * 4;
- this.colors[colorIndex] = this.colors[colorIndex + 1] = this.colors[colorIndex + 2] = this.colors[colorIndex + 3] = displayObject.worldAlpha;
-
- displayObject = displayObject.__next;
-
- indexRun++;
- }
-
- this.dirtyUVS = true;
- this.dirtyColors = true;
-};
-
-/**
- * Updates all the relevant geometry and uploads the data to the GPU
- *
- * @method update
- */
-PIXI.WebGLBatch.prototype.update = function()
-{
- var worldTransform, width, height, aX, aY, w0, w1, h0, h1, index;
-
- var a, b, c, d, tx, ty;
-
- var indexRun = 0;
-
- var displayObject = this.head;
- var verticies = this.verticies;
- var uvs = this.uvs;
- var colors = this.colors;
-
- while(displayObject)
- {
- if(displayObject.vcount === PIXI.visibleCount)
- {
- width = displayObject.texture.frame.width;
- height = displayObject.texture.frame.height;
-
- // TODO trim??
- aX = displayObject.anchor.x;// - displayObject.texture.trim.x
- aY = displayObject.anchor.y; //- displayObject.texture.trim.y
- w0 = width * (1-aX);
- w1 = width * -aX;
-
- h0 = height * (1-aY);
- h1 = height * -aY;
-
- index = indexRun * 8;
-
- worldTransform = displayObject.worldTransform;
-
- a = worldTransform[0];
- b = worldTransform[3];
- c = worldTransform[1];
- d = worldTransform[4];
- tx = worldTransform[2];
- ty = worldTransform[5];
-
- verticies[index + 0 ] = a * w1 + c * h1 + tx;
- verticies[index + 1 ] = d * h1 + b * w1 + ty;
-
- verticies[index + 2 ] = a * w0 + c * h1 + tx;
- verticies[index + 3 ] = d * h1 + b * w0 + ty;
-
- verticies[index + 4 ] = a * w0 + c * h0 + tx;
- verticies[index + 5 ] = d * h0 + b * w0 + ty;
-
- verticies[index + 6] = a * w1 + c * h0 + tx;
- verticies[index + 7] = d * h0 + b * w1 + ty;
-
- if(displayObject.updateFrame || displayObject.texture.updateFrame)
- {
- this.dirtyUVS = true;
-
- var texture = displayObject.texture;
-
- var frame = texture.frame;
- var tw = texture.baseTexture.width;
- var th = texture.baseTexture.height;
-
- uvs[index + 0] = frame.x / tw;
- uvs[index +1] = frame.y / th;
-
- uvs[index +2] = (frame.x + frame.width) / tw;
- uvs[index +3] = frame.y / th;
-
- uvs[index +4] = (frame.x + frame.width) / tw;
- uvs[index +5] = (frame.y + frame.height) / th;
-
- uvs[index +6] = frame.x / tw;
- uvs[index +7] = (frame.y + frame.height) / th;
-
- displayObject.updateFrame = false;
- }
-
- // TODO this probably could do with some optimisation....
- if(displayObject.cacheAlpha !== displayObject.worldAlpha)
- {
- displayObject.cacheAlpha = displayObject.worldAlpha;
-
- var colorIndex = indexRun * 4;
- colors[colorIndex] = colors[colorIndex + 1] = colors[colorIndex + 2] = colors[colorIndex + 3] = displayObject.worldAlpha;
- this.dirtyColors = true;
- }
- }
- else
- {
- index = indexRun * 8;
-
- verticies[index + 0 ] = verticies[index + 1 ] = verticies[index + 2 ] = verticies[index + 3 ] = verticies[index + 4 ] = verticies[index + 5 ] = verticies[index + 6] = verticies[index + 7] = 0;
- }
-
- indexRun++;
- displayObject = displayObject.__next;
- }
-};
-
-/**
- * Draws the batch to the frame buffer
- *
- * @method render
- */
-PIXI.WebGLBatch.prototype.render = function(start, end)
-{
- start = start || 0;
-
- if(end === undefined)
- end = this.size;
-
- if(this.dirty)
- {
- this.refresh();
- this.dirty = false;
- }
-
- if (this.size === 0)return;
-
- this.update();
var gl = this.gl;
- //TODO optimize this!
+ var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc);
+ gl.useProgram(program);
- var shaderProgram = PIXI.defaultShader;
+ // get and store the uniforms for the shader
+ this.projectionVector = gl.getUniformLocation(program, 'projectionVector');
+ this.offsetVector = gl.getUniformLocation(program, 'offsetVector');
+ this.tintColor = gl.getUniformLocation(program, 'tint');
- //gl.useProgram(shaderProgram);
- // update the verts..
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
- // ok..
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.verticies);
- gl.vertexAttribPointer(shaderProgram.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
- // update the uvs
- //var isDefault = (shaderProgram == PIXI.shaderProgram)
+ // get and store the attributes
+ this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition');
+ this.colorAttribute = gl.getAttribLocation(program, 'aColor');
- gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
+ this.attributes = [this.aVertexPosition, this.colorAttribute];
- if(this.dirtyUVS)
- {
- this.dirtyUVS = false;
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvs);
- }
+ this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix');
+ this.alpha = gl.getUniformLocation(program, 'alpha');
- gl.vertexAttribPointer(shaderProgram.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
-
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, this.texture._glTexture);
-
- // update color!
- gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
-
- if(this.dirtyColors)
- {
- this.dirtyColors = false;
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.colors);
- }
-
- gl.vertexAttribPointer(shaderProgram.colorAttribute, 1, gl.FLOAT, false, 0, 0);
- // dont need to upload!
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
-
- var len = end - start;
-
- // DRAW THAT this!
- gl.drawElements(gl.TRIANGLES, len * 6, gl.UNSIGNED_SHORT, start * 2 * 6 );
+ this.program = program;
};
/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
- */
-
-
-PIXI.WebGLFilterManager = function(transparent)
+* Destroys the shader
+* @method destroy
+*
+*/
+PIXI.PrimitiveShader.prototype.destroy = function()
{
- this.transparent = transparent;
-
- this.filterStack = [];
- this.texturePool = [];
-
- this.offsetX = 0;
- this.offsetY = 0;
-
- this.initShaderBuffers();
-};
-
-// API
-
-PIXI.WebGLFilterManager.prototype.begin = function(projection, buffer)
-{
- this.width = projection.x * 2;
- this.height = -projection.y * 2;
- this.buffer = buffer;
-};
-
-PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock)
-{
- var gl = PIXI.gl;
-
- // filter program
- // OPTIMISATION - the first filter is free if its a simple color change?
- this.filterStack.push(filterBlock);
-
- var filter = filterBlock.filterPasses[0];
-
- this.offsetX += filterBlock.target.filterArea.x;
- this.offsetY += filterBlock.target.filterArea.y;
-
- var texture = this.texturePool.pop();
- if(!texture)
- {
- texture = new PIXI.FilterTexture(this.width, this.height);
- }
- else
- {
- texture.resize(this.width, this.height);
- }
-
- gl.bindTexture(gl.TEXTURE_2D, texture.texture);
-
- this.getBounds(filterBlock.target);
-
- var filterArea = filterBlock.target.filterArea;
-
- var padidng = filter.padding;
- filterArea.x -= padidng;
- filterArea.y -= padidng;
- filterArea.width += padidng * 2;
- filterArea.height += padidng * 2;
-
- // cap filter to screen size..
- if(filterArea.x < 0)filterArea.x = 0;
- if(filterArea.width > this.width)filterArea.width = this.width;
- if(filterArea.y < 0)filterArea.y = 0;
- if(filterArea.height > this.height)filterArea.height = this.height;
-
- //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
- gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);
-
- // set view port
- gl.viewport(0, 0, filterArea.width, filterArea.height);
-
- PIXI.projection.x = filterArea.width/2;
- PIXI.projection.y = -filterArea.height/2;
-
- PIXI.offset.x = -filterArea.x;
- PIXI.offset.y = -filterArea.y;
-
- // update projection
- gl.uniform2f(PIXI.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2);
- gl.uniform2f(PIXI.defaultShader.offsetVector, -filterArea.x, -filterArea.y);
- //PIXI.primitiveProgram
-
- gl.colorMask(true, true, true, true);
- gl.clearColor(0,0,0, 0);
- gl.clear(gl.COLOR_BUFFER_BIT);
-
- filterBlock._glFilterTexture = texture;
-};
-
-
-PIXI.WebGLFilterManager.prototype.popFilter = function()
-{
- var gl = PIXI.gl;
- var filterBlock = this.filterStack.pop();
- var filterArea = filterBlock.target.filterArea;
- var texture = filterBlock._glFilterTexture;
-
- if(filterBlock.filterPasses.length > 1)
- {
- gl.viewport(0, 0, filterArea.width, filterArea.height);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
-
- this.vertexArray[0] = 0;
- this.vertexArray[1] = filterArea.height;
-
- this.vertexArray[2] = filterArea.width;
- this.vertexArray[3] = filterArea.height;
-
- this.vertexArray[4] = 0;
- this.vertexArray[5] = 0;
-
- this.vertexArray[6] = filterArea.width;
- this.vertexArray[7] = 0;
-
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
- // nnow set the uvs..
- this.uvArray[2] = filterArea.width/this.width;
- this.uvArray[5] = filterArea.height/this.height;
- this.uvArray[6] = filterArea.width/this.width;
- this.uvArray[7] = filterArea.height/this.height;
-
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);
-
- var inputTexture = texture;
- var outputTexture = this.texturePool.pop();
- if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.width, this.height);
-
- // need to clear this FBO as it may have some left over elements from a prvious filter.
- gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
- gl.clear(gl.COLOR_BUFFER_BIT);
-
- gl.disable(gl.BLEND);
-
- for (var i = 0; i < filterBlock.filterPasses.length-1; i++)
- {
- var filterPass = filterBlock.filterPasses[i];
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
-
- // set texture
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);
-
- // draw texture..
- //filterPass.applyFilterPass(filterArea.width, filterArea.height);
- this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height);
-
- // swap the textures..
- var temp = inputTexture;
- inputTexture = outputTexture;
- outputTexture = temp;
- }
-
- gl.enable(gl.BLEND);
-
- texture = inputTexture;
- this.texturePool.push(outputTexture);
- }
-
- var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1];
-
- this.offsetX -= filterArea.x;
- this.offsetY -= filterArea.y;
-
-
- var sizeX = this.width;
- var sizeY = this.height;
-
- var offsetX = 0;
- var offsetY = 0;
-
- var buffer = this.buffer;
-
- // time to render the filters texture to the previous scene
- if(this.filterStack.length === 0)
- {
- gl.colorMask(true, true, true, this.transparent);
- }
- else
- {
- var currentFilter = this.filterStack[this.filterStack.length-1];
- filterArea = currentFilter.target.filterArea;
-
- sizeX = filterArea.width;
- sizeY = filterArea.height;
-
- offsetX = filterArea.x;
- offsetY = filterArea.y;
-
- buffer = currentFilter._glFilterTexture.frameBuffer;
- }
-
-
-
- // TODO need toremove thease global elements..
- PIXI.projection.x = sizeX/2;
- PIXI.projection.y = -sizeY/2;
-
- PIXI.offset.x = offsetX;
- PIXI.offset.y = offsetY;
-
- filterArea = filterBlock.target.filterArea;
-
- var x = filterArea.x-offsetX;
- var y = filterArea.y-offsetY;
-
- // update the buffers..
- // make sure to flip the y!
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
-
- this.vertexArray[0] = x;
- this.vertexArray[1] = y + filterArea.height;
-
- this.vertexArray[2] = x + filterArea.width;
- this.vertexArray[3] = y + filterArea.height;
-
- this.vertexArray[4] = x;
- this.vertexArray[5] = y;
-
- this.vertexArray[6] = x + filterArea.width;
- this.vertexArray[7] = y;
-
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
-
- this.uvArray[2] = filterArea.width/this.width;
- this.uvArray[5] = filterArea.height/this.height;
- this.uvArray[6] = filterArea.width/this.width;
- this.uvArray[7] = filterArea.height/this.height;
-
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);
-
- gl.viewport(0, 0, sizeX, sizeY);
- // bind the buffer
- gl.bindFramebuffer(gl.FRAMEBUFFER, buffer );
-
- // set texture
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, texture.texture);
-
- // apply!
- //filter.applyFilterPass(sizeX, sizeY);
- this.applyFilterPass(filter, filterArea, sizeX, sizeY);
-
- // now restore the regular shader..
- gl.useProgram(PIXI.defaultShader.program);
- gl.uniform2f(PIXI.defaultShader.projectionVector, sizeX/2, -sizeY/2);
- gl.uniform2f(PIXI.defaultShader.offsetVector, -offsetX, -offsetY);
-
- // return the texture to the pool
- this.texturePool.push(texture);
- filterBlock._glFilterTexture = null;
-};
-
-PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height)
-{
- // use program
- var gl = PIXI.gl;
- var shader = filter.shader;
-
- if(!shader)
- {
- shader = new PIXI.PixiShader();
-
- shader.fragmentSrc = filter.fragmentSrc;
- shader.uniforms = filter.uniforms;
- shader.init();
-
- filter.shader = shader;
- }
-
- // set the shader
- gl.useProgram(shader.program);
-
- gl.uniform2f(shader.projectionVector, width/2, -height/2);
- gl.uniform2f(shader.offsetVector, 0,0);
-
- if(filter.uniforms.dimensions)
- {
- filter.uniforms.dimensions.value[0] = this.width;//width;
- filter.uniforms.dimensions.value[1] = this.height;//height;
- filter.uniforms.dimensions.value[2] = this.vertexArray[0];
- filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height;
- }
-
- shader.syncUniforms();
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
- gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
- gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
-
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
-
- // draw the filter...
- gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
-};
-
-PIXI.WebGLFilterManager.prototype.initShaderBuffers = function()
-{
- var gl = PIXI.gl;
-
- // create some buffers
- this.vertexBuffer = gl.createBuffer();
- this.uvBuffer = gl.createBuffer();
- this.indexBuffer = gl.createBuffer();
-
- // bind and upload the vertexs..
- // keep a refferance to the vertexFloatData..
- this.vertexArray = new Float32Array([0.0, 0.0,
- 1.0, 0.0,
- 0.0, 1.0,
- 1.0, 1.0]);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
- gl.bufferData(
- gl.ARRAY_BUFFER,
- this.vertexArray,
- gl.STATIC_DRAW);
-
-
- // bind and upload the uv buffer
- this.uvArray = new Float32Array([0.0, 0.0,
- 1.0, 0.0,
- 0.0, 1.0,
- 1.0, 1.0]);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
- gl.bufferData(
- gl.ARRAY_BUFFER,
- this.uvArray,
- gl.STATIC_DRAW);
-
- // bind and upload the index
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
- gl.bufferData(
- gl.ELEMENT_ARRAY_BUFFER,
- new Uint16Array([0, 1, 2, 1, 3, 2]),
- gl.STATIC_DRAW);
-};
-
-PIXI.WebGLFilterManager.prototype.getBounds = function(displayObject)
-{
- // time to get the width and height of the object!
- var worldTransform, width, height, aX, aY, w0, w1, h0, h1, doTest;
- var a, b, c, d, tx, ty, x1, x2, x3, x4, y1, y2, y3, y4;
-
- var tempObject = displayObject.first;
- var testObject = displayObject.last._iNext;
-
- var maxX = -Infinity;
- var maxY = -Infinity;
-
- var minX = Infinity;
- var minY = Infinity;
-
- do
- {
- // TODO can be optimized! - what if there is no scale / rotation?
-
- if(tempObject.visible)
- {
- if(tempObject instanceof PIXI.Sprite)
- {
- width = tempObject.texture.frame.width;
- height = tempObject.texture.frame.height;
-
- // TODO trim??
- aX = tempObject.anchor.x;
- aY = tempObject.anchor.y;
- w0 = width * (1-aX);
- w1 = width * -aX;
-
- h0 = height * (1-aY);
- h1 = height * -aY;
-
- doTest = true;
- }
- else if(tempObject instanceof PIXI.Graphics)
- {
- tempObject.updateFilterBounds();
-
- var bounds = tempObject.bounds;
-
- width = bounds.width;
- height = bounds.height;
-
- w0 = bounds.x;
- w1 = bounds.x + bounds.width;
-
- h0 = bounds.y;
- h1 = bounds.y + bounds.height;
-
- doTest = true;
- }
- }
-
- if(doTest)
- {
- worldTransform = tempObject.worldTransform;
-
- a = worldTransform[0];
- b = worldTransform[3];
- c = worldTransform[1];
- d = worldTransform[4];
- tx = worldTransform[2];
- ty = worldTransform[5];
-
- x1 = a * w1 + c * h1 + tx;
- y1 = d * h1 + b * w1 + ty;
-
- x2 = a * w0 + c * h1 + tx;
- y2 = d * h1 + b * w0 + ty;
-
- x3 = a * w0 + c * h0 + tx;
- y3 = d * h0 + b * w0 + ty;
-
- x4 = a * w1 + c * h0 + tx;
- y4 = d * h0 + b * w1 + ty;
-
- minX = x1 < minX ? x1 : minX;
- minX = x2 < minX ? x2 : minX;
- minX = x3 < minX ? x3 : minX;
- minX = x4 < minX ? x4 : minX;
-
- minY = y1 < minY ? y1 : minY;
- minY = y2 < minY ? y2 : minY;
- minY = y3 < minY ? y3 : minY;
- minY = y4 < minY ? y4 : minY;
-
- maxX = x1 > maxX ? x1 : maxX;
- maxX = x2 > maxX ? x2 : maxX;
- maxX = x3 > maxX ? x3 : maxX;
- maxX = x4 > maxX ? x4 : maxX;
-
- maxY = y1 > maxY ? y1 : maxY;
- maxY = y2 > maxY ? y2 : maxY;
- maxY = y3 > maxY ? y3 : maxY;
- maxY = y4 > maxY ? y4 : maxY;
- }
-
- doTest = false;
- tempObject = tempObject._iNext;
-
- }
- while(tempObject !== testObject);
-
- displayObject.filterArea.x = minX;
- displayObject.filterArea.y = minY;
-
- displayObject.filterArea.width = maxX - minX;
- displayObject.filterArea.height = maxY - minY;
-};
-
-PIXI.FilterTexture = function(width, height)
-{
- var gl = PIXI.gl;
-
- // next time to create a frame buffer and texture
- this.frameBuffer = gl.createFramebuffer();
- this.texture = gl.createTexture();
-
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer );
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer );
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
-
- this.resize(width, height);
-};
-
-PIXI.FilterTexture.prototype.resize = function(width, height)
-{
- if(this.width === width && this.height === height) return;
-
- this.width = width;
- this.height = height;
-
- var gl = PIXI.gl;
-
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ this.gl.deleteProgram( this.program );
+ this.uniforms = null;
+ this.gl = null;
+ this.attribute = null;
};
/**
@@ -4979,7 +16598,9 @@ PIXI.FilterTexture.prototype.resize = function(width, height)
/**
* A set of functions used by the webGL renderer to draw the primitive graphics data
*
- * @class CanvasGraphics
+ * @class WebGLGraphics
+ * @private
+ * @static
*/
PIXI.WebGLGraphics = function()
{
@@ -4993,16 +16614,21 @@ PIXI.WebGLGraphics = function()
* @private
* @method renderGraphics
* @param graphics {Graphics}
- * @param projection {Object}
+ * @param renderSession {Object}
*/
-PIXI.WebGLGraphics.renderGraphics = function(graphics, projection)
+PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projection, offset)
{
- var gl = PIXI.gl;
+ var gl = renderSession.gl;
+ var projection = renderSession.projection,
+ offset = renderSession.offset,
+ shader = renderSession.shaderManager.primitiveShader;
- if(!graphics._webGL)graphics._webGL = {points:[], indices:[], lastIndex:0,
+ if(!graphics._webGL[gl.id])graphics._webGL[gl.id] = {points:[], indices:[], lastIndex:0,
buffer:gl.createBuffer(),
indexBuffer:gl.createBuffer()};
+ var webGL = graphics._webGL[gl.id];
+
if(graphics.dirty)
{
graphics.dirty = false;
@@ -5011,43 +16637,41 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection)
{
graphics.clearDirty = false;
- graphics._webGL.lastIndex = 0;
- graphics._webGL.points = [];
- graphics._webGL.indices = [];
+ webGL.lastIndex = 0;
+ webGL.points = [];
+ webGL.indices = [];
}
- PIXI.WebGLGraphics.updateGraphics(graphics);
+ PIXI.WebGLGraphics.updateGraphics(graphics, gl);
}
- PIXI.activatePrimitiveShader();
+ renderSession.shaderManager.activatePrimitiveShader();
- // This could be speeded up fo sure!
- var m = PIXI.mat3.clone(graphics.worldTransform);
+ // This could be speeded up for sure!
- PIXI.mat3.transpose(m);
-
- // set the matrix transform for the
+ // set the matrix transform
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- gl.uniformMatrix3fv(PIXI.primitiveShader.translationMatrix, false, m);
+ gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true));
- gl.uniform2f(PIXI.primitiveShader.projectionVector, projection.x, -projection.y);
- gl.uniform2f(PIXI.primitiveShader.offsetVector, -PIXI.offset.x, -PIXI.offset.y);
+ gl.uniform2f(shader.projectionVector, projection.x, -projection.y);
+ gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);
- gl.uniform1f(PIXI.primitiveShader.alpha, graphics.worldAlpha);
- gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer);
+ gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint));
- gl.vertexAttribPointer(PIXI.primitiveShader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0);
- gl.vertexAttribPointer(PIXI.primitiveShader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4);
+ gl.uniform1f(shader.alpha, graphics.worldAlpha);
+ gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer);
+
+ gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0);
+ gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4);
// set the index buffer!
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer);
+ gl.drawElements(gl.TRIANGLE_STRIP, webGL.indices.length, gl.UNSIGNED_SHORT, 0 );
- gl.drawElements(gl.TRIANGLE_STRIP, graphics._webGL.indices.length, gl.UNSIGNED_SHORT, 0 );
-
- PIXI.deactivatePrimitiveShader();
+ renderSession.shaderManager.deactivatePrimitiveShader();
// return to default shader...
// PIXI.activateShader(PIXI.defaultShader);
@@ -5059,11 +16683,14 @@ PIXI.WebGLGraphics.renderGraphics = function(graphics, projection)
* @static
* @private
* @method updateGraphics
- * @param graphics {Graphics}
+ * @param graphicsData {Graphics} The graphics object to update
+ * @param gl {WebGLContext} the current WebGL drawing context
*/
-PIXI.WebGLGraphics.updateGraphics = function(graphics)
+PIXI.WebGLGraphics.updateGraphics = function(graphics, gl)
{
- for (var i = graphics._webGL.lastIndex; i < graphics.graphicsData.length; i++)
+ var webGL = graphics._webGL[gl.id];
+
+ for (var i = webGL.lastIndex; i < graphics.graphicsData.length; i++)
{
var data = graphics.graphicsData[i];
@@ -5072,37 +16699,37 @@ PIXI.WebGLGraphics.updateGraphics = function(graphics)
if(data.fill)
{
if(data.points.length>3)
- PIXI.WebGLGraphics.buildPoly(data, graphics._webGL);
+ PIXI.WebGLGraphics.buildPoly(data, webGL);
}
if(data.lineWidth > 0)
{
- PIXI.WebGLGraphics.buildLine(data, graphics._webGL);
+ PIXI.WebGLGraphics.buildLine(data, webGL);
}
}
else if(data.type === PIXI.Graphics.RECT)
{
- PIXI.WebGLGraphics.buildRectangle(data, graphics._webGL);
+ PIXI.WebGLGraphics.buildRectangle(data, webGL);
}
else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP)
{
- PIXI.WebGLGraphics.buildCircle(data, graphics._webGL);
+ PIXI.WebGLGraphics.buildCircle(data, webGL);
}
}
- graphics._webGL.lastIndex = graphics.graphicsData.length;
+ webGL.lastIndex = graphics.graphicsData.length;
- var gl = PIXI.gl;
+
- graphics._webGL.glPoints = new Float32Array(graphics._webGL.points);
+ webGL.glPoints = new Float32Array(webGL.points);
- gl.bindBuffer(gl.ARRAY_BUFFER, graphics._webGL.buffer);
- gl.bufferData(gl.ARRAY_BUFFER, graphics._webGL.glPoints, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, webGL.buffer);
+ gl.bufferData(gl.ARRAY_BUFFER, webGL.glPoints, gl.STATIC_DRAW);
- graphics._webGL.glIndicies = new Uint16Array(graphics._webGL.indices);
+ webGL.glIndicies = new Uint16Array(webGL.indices);
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.indexBuffer);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, graphics._webGL.glIndicies, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGL.indexBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, webGL.glIndicies, gl.STATIC_DRAW);
};
/**
@@ -5111,7 +16738,7 @@ PIXI.WebGLGraphics.updateGraphics = function(graphics)
* @static
* @private
* @method buildRectangle
- * @param graphics {Graphics}
+ * @param graphicsData {Graphics} The graphics object containing all the necessary properties
* @param webGLData {Object}
*/
PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData)
@@ -5159,13 +16786,18 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData)
if(graphicsData.lineWidth)
{
+ var tempPoints = graphicsData.points;
+
graphicsData.points = [x, y,
x + width, y,
x + width, y + height,
x, y + height,
x, y];
+
PIXI.WebGLGraphics.buildLine(graphicsData, webGLData);
+
+ graphicsData.points = tempPoints;
}
};
@@ -5175,14 +16807,13 @@ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData)
* @static
* @private
* @method buildCircle
- * @param graphics {Graphics}
+ * @param graphicsData {Graphics} The graphics object to draw
* @param webGLData {Object}
*/
PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData)
{
- // --- //
+
// need to convert points to a nice regular data
- //
var rectData = graphicsData.points;
var x = rectData[0];
var y = rectData[1];
@@ -5226,6 +16857,8 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData)
if(graphicsData.lineWidth)
{
+ var tempPoints = graphicsData.points;
+
graphicsData.points = [];
for (i = 0; i < totalSegs + 1; i++)
@@ -5235,6 +16868,8 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData)
}
PIXI.WebGLGraphics.buildLine(graphicsData, webGLData);
+
+ graphicsData.points = tempPoints;
}
};
@@ -5244,7 +16879,7 @@ PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData)
* @static
* @private
* @method buildLine
- * @param graphics {Graphics}
+ * @param graphicsData {Graphics} The graphics object containing all the necessary properties
* @param webGLData {Object}
*/
PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData)
@@ -5267,7 +16902,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData)
var firstPoint = new PIXI.Point( points[0], points[1] );
var lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] );
- // if the first point is the last point - goona have issues :)
+ // if the first point is the last point - gonna have issues :)
if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y)
{
points.pop();
@@ -5454,7 +17089,7 @@ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData)
* @static
* @private
* @method buildPoly
- * @param graphics {Graphics}
+ * @param graphicsData {Graphics} The graphics object containing all the necessary properties
* @param webGLData {Object}
*/
PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData)
@@ -5501,15 +17136,11 @@ PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData)
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
-PIXI._defaultFrame = new PIXI.Rectangle(0,0,1,1);
-
-// an instance of the gl context..
-// only one at the moment :/
-PIXI.gl = null;
+PIXI.glContexts = []; // this is where we store the webGL contexts for easy access.
/**
- * the WebGLRenderer is draws the stage and all its content onto a webGL enabled canvas. This renderer
- * should be used for browsers support webGL. This Render works by automatically managing webGLBatchs.
+ * the WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer
+ * should be used for browsers that support webGL. This Render works by automatically managing webGLBatch's.
* So no need for Sprite Batch's or Sprite Cloud's
* Dont forget to add the view to your DOM or you will not see anything :)
*
@@ -5517,123 +17148,148 @@ PIXI.gl = null;
* @constructor
* @param width=0 {Number} the width of the canvas view
* @param height=0 {Number} the height of the canvas view
- * @param view {Canvas} the canvas to use as a view, optional
- * @param transparent=false {Boolean} the transparency of the render view, default false
+ * @param view {HTMLCanvasElement} the canvas to use as a view, optional
+ * @param transparent=false {Boolean} If the render view is transparent, default false
* @param antialias=false {Boolean} sets antialias (only applicable in chrome at the moment)
*
*/
PIXI.WebGLRenderer = function(width, height, view, transparent, antialias)
{
- // do a catch.. only 1 webGL renderer..
+ if(!PIXI.defaultRenderer)PIXI.defaultRenderer = this;
+ this.type = PIXI.WEBGL_RENDERER;
+
+ // do a catch.. only 1 webGL renderer..
+ /**
+ * Whether the render view is transparent
+ *
+ * @property transparent
+ * @type Boolean
+ */
this.transparent = !!transparent;
+ /**
+ * The width of the canvas view
+ *
+ * @property width
+ * @type Number
+ * @default 800
+ */
this.width = width || 800;
+
+ /**
+ * The height of the canvas view
+ *
+ * @property height
+ * @type Number
+ * @default 600
+ */
this.height = height || 600;
+ /**
+ * The canvas element that everything is drawn to
+ *
+ * @property view
+ * @type HTMLCanvasElement
+ */
this.view = view || document.createElement( 'canvas' );
this.view.width = this.width;
this.view.height = this.height;
// deal with losing context..
- var scope = this;
- this.view.addEventListener('webglcontextlost', function(event) { scope.handleContextLost(event); }, false);
- this.view.addEventListener('webglcontextrestored', function(event) { scope.handleContextRestored(event); }, false);
+ this.contextLost = this.handleContextLost.bind(this);
+ this.contextRestoredLost = this.handleContextRestored.bind(this);
+
+ this.view.addEventListener('webglcontextlost', this.contextLost, false);
+ this.view.addEventListener('webglcontextrestored', this.contextRestoredLost, false);
- this.batchs = [];
-
- var options = {
+ this.options = {
alpha: this.transparent,
antialias:!!antialias, // SPEED UP??
- premultipliedAlpha:false,
+ premultipliedAlpha:!!transparent,
stencil:true
};
//try 'experimental-webgl'
try {
- PIXI.gl = this.gl = this.view.getContext('experimental-webgl', options);
+ this.gl = this.view.getContext('experimental-webgl', this.options);
} catch (e) {
//try 'webgl'
try {
- PIXI.gl = this.gl = this.view.getContext('webgl', options);
+ this.gl = this.view.getContext('webgl', this.options);
} catch (e2) {
// fail, not able to get a context
throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this);
}
}
- PIXI.initDefaultShaders();
-
-
-
-
- // PIXI.activateDefaultShader();
-
var gl = this.gl;
+ this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++;
- gl.useProgram(PIXI.defaultShader.program);
+ PIXI.glContexts[this.glContextId] = gl;
+
+ if(!PIXI.blendModesWebGL)
+ {
+ PIXI.blendModesWebGL = [];
+
+ PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE];
+ PIXI.blendModesWebGL[PIXI.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ PIXI.blendModesWebGL[PIXI.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA];
+ }
- PIXI.WebGLRenderer.gl = gl;
- this.batch = new PIXI.WebGLBatch(gl);
+
+ this.projection = new PIXI.Point();
+ this.projection.x = this.width/2;
+ this.projection.y = -this.height/2;
+
+ this.offset = new PIXI.Point(0, 0);
+
+ this.resize(this.width, this.height);
+ this.contextLost = false;
+
+ // time to create the render managers! each one focuses on managine a state in webGL
+ this.shaderManager = new PIXI.WebGLShaderManager(gl); // deals with managing the shader programs and their attribs
+ this.spriteBatch = new PIXI.WebGLSpriteBatch(gl); // manages the rendering of sprites
+ this.maskManager = new PIXI.WebGLMaskManager(gl); // manages the masks using the stencil buffer
+ this.filterManager = new PIXI.WebGLFilterManager(gl, this.transparent); // manages the filters
+
+ this.renderSession = {};
+ this.renderSession.gl = this.gl;
+ this.renderSession.drawCount = 0;
+ this.renderSession.shaderManager = this.shaderManager;
+ this.renderSession.maskManager = this.maskManager;
+ this.renderSession.filterManager = this.filterManager;
+ this.renderSession.spriteBatch = this.spriteBatch;
+ this.renderSession.renderer = this;
+
+ gl.useProgram(this.shaderManager.defaultShader.program);
+
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.CULL_FACE);
gl.enable(gl.BLEND);
gl.colorMask(true, true, true, this.transparent);
-
- PIXI.projection = new PIXI.Point(400, 300);
- PIXI.offset = new PIXI.Point(0, 0);
-
- // TODO remove thease globals..
-
- this.resize(this.width, this.height);
- this.contextLost = false;
-
- //PIXI.pushShader(PIXI.defaultShader);
-
- this.stageRenderGroup = new PIXI.WebGLRenderGroup(this.gl, this.transparent);
- // this.stageRenderGroup. = this.transparent
};
// constructor
PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer;
-/**
- * Gets a new WebGLBatch from the pool
- *
- * @static
- * @method getBatch
- * @return {WebGLBatch}
- * @private
- */
-PIXI.WebGLRenderer.getBatch = function()
-{
- if(PIXI._batchs.length === 0)
- {
- return new PIXI.WebGLBatch(PIXI.WebGLRenderer.gl);
- }
- else
- {
- return PIXI._batchs.pop();
- }
-};
-
-/**
- * Puts a batch back into the pool
- *
- * @static
- * @method returnBatch
- * @param batch {WebGLBatch} The batch to return
- * @private
- */
-PIXI.WebGLRenderer.returnBatch = function(batch)
-{
- batch.clean();
- PIXI._batchs.push(batch);
-};
-
/**
* Renders the stage to its webGL view
*
@@ -5645,44 +17301,58 @@ PIXI.WebGLRenderer.prototype.render = function(stage)
if(this.contextLost)return;
- // if rendering a new stage clear the batchs..
+ // if rendering a new stage clear the batches..
if(this.__stage !== stage)
{
+ if(stage.interactive)stage.interactionManager.removeEvents();
+
// TODO make this work
// dont think this is needed any more?
this.__stage = stage;
- this.stageRenderGroup.setRenderable(stage);
}
- // update any textures
+ // update any textures this includes uvs and uploading them to the gpu
PIXI.WebGLRenderer.updateTextures();
// update the scene graph
- PIXI.visibleCount++;
stage.updateTransform();
+
+ // interaction
+ if(stage._interactive)
+ {
+ //need to add some events!
+ if(!stage._interactiveEventsAdded)
+ {
+ stage._interactiveEventsAdded = true;
+ stage.interactionManager.setTarget(this);
+ }
+ }
+
var gl = this.gl;
// -- Does this need to be set every frame? -- //
- gl.colorMask(true, true, true, this.transparent);
+ //gl.colorMask(true, true, true, this.transparent);
gl.viewport(0, 0, this.width, this.height);
+ // make sure we are bound to the main frame buffer
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], !this.transparent);
+ if(this.transparent)
+ {
+ gl.clearColor(0, 0, 0, 0);
+ }
+ else
+ {
+ gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1);
+ }
+
+
gl.clear(gl.COLOR_BUFFER_BIT);
- // HACK TO TEST
-
- this.stageRenderGroup.backgroundColor = stage.backgroundColorSplit;
-
- PIXI.projection.x = this.width/2;
- PIXI.projection.y = -this.height/2;
-
- this.stageRenderGroup.render(PIXI.projection);
+ this.renderDisplayObject( stage, this.projection );
// interaction
- // run interaction!
if(stage.interactive)
{
//need to add some events!
@@ -5692,17 +17362,65 @@ PIXI.WebGLRenderer.prototype.render = function(stage)
stage.interactionManager.setTarget(this);
}
}
-
- // after rendering lets confirm all frames that have been uodated..
- if(PIXI.Texture.frameUpdates.length > 0)
+ else
{
- for (var i=0; i < PIXI.Texture.frameUpdates.length; i++)
+ if(stage._interactiveEventsAdded)
{
- PIXI.Texture.frameUpdates[i].updateFrame = false;
+ stage._interactiveEventsAdded = false;
+ stage.interactionManager.setTarget(this);
}
-
- PIXI.Texture.frameUpdates = [];
}
+
+ /*
+ //can simulate context loss in Chrome like so:
+ this.view.onmousedown = function(ev) {
+ console.dir(this.gl.getSupportedExtensions());
+ var ext = (
+ gl.getExtension("WEBGL_scompressed_texture_s3tc")
+ // gl.getExtension("WEBGL_compressed_texture_s3tc") ||
+ // gl.getExtension("MOZ_WEBGL_compressed_texture_s3tc") ||
+ // gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc")
+ );
+ console.dir(ext);
+ var loseCtx = this.gl.getExtension("WEBGL_lose_context");
+ console.log("killing context");
+ loseCtx.loseContext();
+ setTimeout(function() {
+ console.log("restoring context...");
+ loseCtx.restoreContext();
+ }.bind(this), 1000);
+ }.bind(this);
+ */
+};
+
+/**
+ * Renders a display Object
+ *
+ * @method renderDIsplayObject
+ * @param displayObject {DisplayObject} The DisplayObject to render
+ * @param projection {Point} The projection
+ * @param buffer {Array} a standard WebGL buffer
+ */
+PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer)
+{
+ // reset the render session data..
+ this.renderSession.drawCount = 0;
+ this.renderSession.currentBlendMode = 9999;
+
+ this.renderSession.projection = projection;
+ this.renderSession.offset = this.offset;
+
+ // start the sprite batch
+ this.spriteBatch.begin(this.renderSession);
+
+ // start the filter manager
+ this.filterManager.begin(this.renderSession, buffer);
+
+ // render the scene!
+ displayObject._renderWebGL(this.renderSession);
+
+ // finish the sprite batch
+ this.spriteBatch.end();
};
/**
@@ -5717,58 +17435,19 @@ PIXI.WebGLRenderer.updateTextures = function()
var i = 0;
//TODO break this out into a texture manager...
- for (i = 0; i < PIXI.texturesToUpdate.length; i++)
- PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]);
+ //for (i = 0; i < PIXI.texturesToUpdate.length; i++)
+ // PIXI.WebGLRenderer.updateTexture(PIXI.texturesToUpdate[i]);
+
+
+ for (i=0; i < PIXI.Texture.frameUpdates.length; i++)
+ PIXI.WebGLRenderer.updateTextureFrame(PIXI.Texture.frameUpdates[i]);
for (i = 0; i < PIXI.texturesToDestroy.length; i++)
PIXI.WebGLRenderer.destroyTexture(PIXI.texturesToDestroy[i]);
- PIXI.texturesToUpdate = [];
- PIXI.texturesToDestroy = [];
-};
-
-/**
- * Updates a loaded webgl texture
- *
- * @static
- * @method updateTexture
- * @param texture {Texture} The texture to update
- * @private
- */
-PIXI.WebGLRenderer.updateTexture = function(texture)
-{
- //TODO break this out into a texture manager...
- var gl = PIXI.gl;
-
- if(!texture._glTexture)
- {
- texture._glTexture = gl.createTexture();
- }
-
- if(texture.hasLoaded)
- {
- gl.bindTexture(gl.TEXTURE_2D, texture._glTexture);
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
-
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR ? gl.LINEAR : gl.NEAREST);
-
- // reguler...
-
- if(!texture._powerOf2)
- {
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- }
- else
- {
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
- }
-
- gl.bindTexture(gl.TEXTURE_2D, null);
- }
+ PIXI.texturesToUpdate.length = 0;
+ PIXI.texturesToDestroy.length = 0;
+ PIXI.Texture.frameUpdates.length = 0;
};
/**
@@ -5781,13 +17460,34 @@ PIXI.WebGLRenderer.updateTexture = function(texture)
PIXI.WebGLRenderer.destroyTexture = function(texture)
{
//TODO break this out into a texture manager...
- var gl = PIXI.gl;
- if(texture._glTexture)
+ for (var i = texture._glTextures.length - 1; i >= 0; i--)
{
- texture._glTexture = gl.createTexture();
- gl.deleteTexture(gl.TEXTURE_2D, texture._glTexture);
+ var glTexture = texture._glTextures[i];
+ var gl = PIXI.glContexts[i];
+
+ if(gl && glTexture)
+ {
+ gl.deleteTexture(glTexture);
+ }
}
+
+ texture._glTextures.length = 0;
+};
+
+/**
+ *
+ * @method updateTextureFrame
+ * @param texture {Texture} The texture to update the frame from
+ * @private
+ */
+PIXI.WebGLRenderer.updateTextureFrame = function(texture)
+{
+ texture.updateFrame = false;
+
+ // now set the uvs. Figured that the uv data sits with a texture rather than a sprite.
+ // so uv data is stored on the texture itself
+ texture._updateWebGLuvs();
};
/**
@@ -5807,18 +17507,87 @@ PIXI.WebGLRenderer.prototype.resize = function(width, height)
this.gl.viewport(0, 0, this.width, this.height);
- //var projectionMatrix = this.projectionMatrix;
+ this.projection.x = this.width/2;
+ this.projection.y = -this.height/2;
+};
- PIXI.projection.x = this.width/2;
- PIXI.projection.y = -this.height/2;
+/**
+ * Creates a WebGL texture
+ *
+ * @method createWebGLTexture
+ * @param texture {Texture} the texture to render
+ * @param gl {webglContext} the WebGL context
+ * @static
+ */
+PIXI.createWebGLTexture = function(texture, gl)
+{
- //PIXI.size.x = this.width/2;
- //PIXI.size.y = -this.height/2;
-// projectionMatrix[0] = 2/this.width;
-// projectionMatrix[5] = -2/this.height;
-// projectionMatrix[12] = -1;
-// projectionMatrix[13] = 1;
+ if(texture.hasLoaded)
+ {
+ texture._glTextures[gl.id] = gl.createTexture();
+
+ gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]);
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
+
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
+
+ // reguler...
+
+ if(!texture._powerOf2)
+ {
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ }
+ else
+ {
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+ }
+
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+
+ return texture._glTextures[gl.id];
+};
+
+/**
+ * Updates a WebGL texture
+ *
+ * @method updateWebGLTexture
+ * @param texture {Texture} the texture to update
+ * @param gl {webglContext} the WebGL context
+ * @private
+ */
+PIXI.updateWebGLTexture = function(texture, gl)
+{
+ if( texture._glTextures[gl.id] )
+ {
+ gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]);
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
+
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST);
+
+ // reguler...
+
+ if(!texture._powerOf2)
+ {
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ }
+ else
+ {
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
+ }
+
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ }
+
};
/**
@@ -5843,1617 +17612,3792 @@ PIXI.WebGLRenderer.prototype.handleContextLost = function(event)
*/
PIXI.WebGLRenderer.prototype.handleContextRestored = function()
{
- this.gl = this.view.getContext('experimental-webgl', {
- alpha: true
- });
- this.initShaders();
+ //try 'experimental-webgl'
+ try {
+ this.gl = this.view.getContext('experimental-webgl', this.options);
+ } catch (e) {
+ //try 'webgl'
+ try {
+ this.gl = this.view.getContext('webgl', this.options);
+ } catch (e2) {
+ // fail, not able to get a context
+ throw new Error(' This browser does not support webGL. Try using the canvas renderer' + this);
+ }
+ }
+
+ var gl = this.gl;
+ gl.id = PIXI.WebGLRenderer.glContextId ++;
+
+
+
+ // need to set the context...
+ this.shaderManager.setContext(gl);
+ this.spriteBatch.setContext(gl);
+ this.maskManager.setContext(gl);
+ this.filterManager.setContext(gl);
+
+
+ this.renderSession.gl = this.gl;
+
+ gl.disable(gl.DEPTH_TEST);
+ gl.disable(gl.CULL_FACE);
+
+ gl.enable(gl.BLEND);
+ gl.colorMask(true, true, true, this.transparent);
+
+ this.gl.viewport(0, 0, this.width, this.height);
for(var key in PIXI.TextureCache)
{
var texture = PIXI.TextureCache[key].baseTexture;
- texture._glTexture = null;
- PIXI.WebGLRenderer.updateTexture(texture);
+ texture._glTextures = [];
}
- for (var i=0; i < this.batchs.length; i++)
- {
- this.batchs[i].restoreLostContext(this.gl);
- this.batchs[i].dirty = true;
- }
-
- PIXI._restoreBatchs(this.gl);
-
+ /**
+ * Whether the context was lost
+ * @property contextLost
+ * @type Boolean
+ */
this.contextLost = false;
+
};
+/**
+ * Removes everything from the renderer (event listeners, spritebatch, etc...)
+ *
+ * @method destroy
+ */
+PIXI.WebGLRenderer.prototype.destroy = function()
+{
+
+ // deal with losing context..
+
+ // remove listeners
+ this.view.removeEventListener('webglcontextlost', this.contextLost);
+ this.view.removeEventListener('webglcontextrestored', this.contextRestoredLost);
+
+ PIXI.glContexts[this.glContextId] = null;
+
+ this.projection = null;
+ this.offset = null;
+
+ // time to create the render managers! each one focuses on managine a state in webGL
+ this.shaderManager.destroy();
+ this.spriteBatch.destroy();
+ this.maskManager.destroy();
+ this.filterManager.destroy();
+
+ this.shaderManager = null;
+ this.spriteBatch = null;
+ this.maskManager = null;
+ this.filterManager = null;
+
+ this.gl = null;
+ //
+ this.renderSession = null;
+};
+
+
+PIXI.WebGLRenderer.glContextId = 0;
+
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
+
/**
- * A WebGLBatch Enables a group of sprites to be drawn using the same settings.
- * if a group of sprites all have the same baseTexture and blendMode then they can be
- * grouped into a batch. All the sprites in a batch can then be drawn in one go by the
- * GPU which is hugely efficient. ALL sprites in the webGL renderer are added to a batch
- * even if the batch only contains one sprite. Batching is handled automatically by the
- * webGL renderer. A good tip is: the smaller the number of batchs there are, the faster
- * the webGL renderer will run.
- *
- * @class WebGLBatch
- * @contructor
- * @param gl {WebGLContext} An instance of the webGL context
- */
-PIXI.WebGLRenderGroup = function(gl, transparent)
-{
- this.gl = gl;
- this.root;
-
- this.backgroundColor;
- this.transparent = transparent == undefined ? true : transparent;
-
- this.batchs = [];
- this.toRemove = [];
- // console.log(this.transparent)
- this.filterManager = new PIXI.WebGLFilterManager(this.transparent);
-}
-
-// constructor
-PIXI.WebGLRenderGroup.prototype.constructor = PIXI.WebGLRenderGroup;
-
-/**
- * Add a display object to the webgl renderer
- *
- * @method setRenderable
- * @param displayObject {DisplayObject}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.setRenderable = function(displayObject)
-{
- // has this changed??
- if(this.root)this.removeDisplayObjectAndChildren(this.root);
-
- displayObject.worldVisible = displayObject.visible;
-
- // soooooo //
- // to check if any batchs exist already??
-
- // TODO what if its already has an object? should remove it
- this.root = displayObject;
- this.addDisplayObjectAndChildren(displayObject);
-}
-
-/**
- * Renders the stage to its webgl view
- *
- * @method render
- * @param projection {Object}
- */
-PIXI.WebGLRenderGroup.prototype.render = function(projection, buffer)
-{
- PIXI.WebGLRenderer.updateTextures();
-
- var gl = this.gl;
- gl.uniform2f(PIXI.defaultShader.projectionVector, projection.x, projection.y);
-
- this.filterManager.begin(projection, buffer);
-
-
- gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- // will render all the elements in the group
- var renderable;
-
- for (var i=0; i < this.batchs.length; i++)
- {
-
- renderable = this.batchs[i];
- if(renderable instanceof PIXI.WebGLBatch)
- {
- this.batchs[i].render();
- continue;
- }
-
- // render special
- this.renderSpecial(renderable, projection);
- }
-
-}
-
-/**
- * Renders a specific displayObject
- *
- * @method renderSpecific
- * @param displayObject {DisplayObject}
- * @param projection {Object}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.renderSpecific = function(displayObject, projection, buffer)
-{
- PIXI.WebGLRenderer.updateTextures();
- var gl = this.gl;
-
- gl.uniform2f(PIXI.defaultShader.projectionVector, projection.x, projection.y);
-
- this.filterManager.begin(projection, buffer);
-
- // to do!
- // render part of the scene...
-
- var startIndex;
- var startBatchIndex;
-
- var endIndex;
- var endBatchIndex;
-
- /*
- * LOOK FOR THE NEXT SPRITE
- * This part looks for the closest next sprite that can go into a batch
- * it keeps looking until it finds a sprite or gets to the end of the display
- * scene graph
- */
- var nextRenderable = displayObject.first;
- while(nextRenderable._iNext)
- {
- if(nextRenderable.renderable && nextRenderable.__renderGroup)break;
- nextRenderable = nextRenderable._iNext;
- }
- var startBatch = nextRenderable.batch;
- //console.log(nextRenderable);
-
- //console.log(renderable)
- if(nextRenderable instanceof PIXI.Sprite)
- {
- startBatch = nextRenderable.batch;
-
- var head = startBatch.head;
- var next = head;
-
- // ok now we have the batch.. need to find the start index!
- if(head == nextRenderable)
- {
- startIndex = 0;
- }
- else
- {
- startIndex = 1;
-
- while(head.__next != nextRenderable)
- {
- startIndex++;
- head = head.__next;
- }
- }
- }
- else
- {
- startBatch = nextRenderable;
- }
-
- // Get the LAST renderable object
- var lastRenderable = displayObject.last;
- while(lastRenderable._iPrev)
- {
- if(lastRenderable.renderable && lastRenderable.__renderGroup)break;
- lastRenderable = lastRenderable._iNext;
- }
-
- if(lastRenderable instanceof PIXI.Sprite)
- {
- endBatch = lastRenderable.batch;
-
- var head = endBatch.head;
-
- if(head == lastRenderable)
- {
- endIndex = 0;
- }
- else
- {
- endIndex = 1;
-
- while(head.__next != lastRenderable)
- {
- endIndex++;
- head = head.__next;
- }
- }
- }
- else
- {
- endBatch = lastRenderable;
- }
-
- if(startBatch == endBatch)
- {
- if(startBatch instanceof PIXI.WebGLBatch)
- {
- startBatch.render(startIndex, endIndex+1);
- }
- else
- {
- this.renderSpecial(startBatch, projection);
- }
- return;
- }
-
- // now we have first and last!
- startBatchIndex = this.batchs.indexOf(startBatch);
- endBatchIndex = this.batchs.indexOf(endBatch);
-
- // DO the first batch
- if(startBatch instanceof PIXI.WebGLBatch)
- {
- startBatch.render(startIndex);
- }
- else
- {
- this.renderSpecial(startBatch, projection);
- }
-
- // DO the middle batchs..
- for (var i=startBatchIndex+1; i < endBatchIndex; i++)
- {
- renderable = this.batchs[i];
-
- if(renderable instanceof PIXI.WebGLBatch)
- {
- this.batchs[i].render();
- }
- else
- {
- this.renderSpecial(renderable, projection);
- }
- }
-
- // DO the last batch..
- if(endBatch instanceof PIXI.WebGLBatch)
- {
- endBatch.render(0, endIndex+1);
- }
- else
- {
- this.renderSpecial(endBatch, projection);
- }
-}
-
-/**
- * Renders a specific renderable
- *
- * @method renderSpecial
- * @param renderable {DisplayObject}
- * @param projection {Object}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.renderSpecial = function(renderable, projection)
-{
-
- var worldVisible = renderable.vcount === PIXI.visibleCount
-
-
- if(renderable instanceof PIXI.TilingSprite)
- {
- if(worldVisible)this.renderTilingSprite(renderable, projection);
- }
- else if(renderable instanceof PIXI.Strip)
- {
- if(worldVisible)this.renderStrip(renderable, projection);
- }
- else if(renderable instanceof PIXI.CustomRenderable)
- {
- if(worldVisible) renderable.renderWebGL(this, projection);
- }
- else if(renderable instanceof PIXI.Graphics)
- {
- if(worldVisible && renderable.renderable) PIXI.WebGLGraphics.renderGraphics(renderable, projection);
- }
- else if(renderable instanceof PIXI.FilterBlock)
- {
- this.handleFilterBlock(renderable, projection);
- }
-}
-
-flip = false;
-var maskStack = [];
-var maskPosition = 0;
-
-//var usedMaskStack = [];
-
-PIXI.WebGLRenderGroup.prototype.handleFilterBlock = function(filterBlock, projection)
-{
- /*
- * for now only masks are supported..
- */
- var gl = PIXI.gl;
-
- if(filterBlock.open)
- {
- if(filterBlock.data instanceof Array)
- {
- this.filterManager.pushFilter(filterBlock);
- // ok so..
-
- }
- else
- {
- maskPosition++;
-
- maskStack.push(filterBlock)
-
- gl.enable(gl.STENCIL_TEST);
-
- gl.colorMask(false, false, false, false);
-
- gl.stencilFunc(gl.ALWAYS,1,1);
- gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR);
-
- PIXI.WebGLGraphics.renderGraphics(filterBlock.data, projection);
-
- gl.colorMask(true, true, true, true);
- gl.stencilFunc(gl.NOTEQUAL,0,maskStack.length);
- gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
- }
- }
- else
- {
- if(filterBlock.data instanceof Array)
- {
- this.filterManager.popFilter();
- }
- else
- {
- var maskData = maskStack.pop(filterBlock)
-
-
- if(maskData)
- {
- gl.colorMask(false, false, false, false);
-
- gl.stencilFunc(gl.ALWAYS,1,1);
- gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR);
-
- PIXI.WebGLGraphics.renderGraphics(maskData.data, projection);
-
- gl.colorMask(true, true, true, true);
- gl.stencilFunc(gl.NOTEQUAL,0,maskStack.length);
- gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
- };
-
- gl.disable(gl.STENCIL_TEST);
- }
- }
-}
-
-/**
- * Updates a webgl texture
- *
- * @method updateTexture
- * @param displayObject {DisplayObject}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.updateTexture = function(displayObject)
-{
-
- // TODO definitely can optimse this function..
-
- this.removeObject(displayObject);
-
- /*
- * LOOK FOR THE PREVIOUS RENDERABLE
- * This part looks for the closest previous sprite that can go into a batch
- * It keeps going back until it finds a sprite or the stage
- */
- var previousRenderable = displayObject.first;
- while(previousRenderable != this.root)
- {
- previousRenderable = previousRenderable._iPrev;
- if(previousRenderable.renderable && previousRenderable.__renderGroup)break;
- }
-
- /*
- * LOOK FOR THE NEXT SPRITE
- * This part looks for the closest next sprite that can go into a batch
- * it keeps looking until it finds a sprite or gets to the end of the display
- * scene graph
- */
- var nextRenderable = displayObject.last;
- while(nextRenderable._iNext)
- {
- nextRenderable = nextRenderable._iNext;
- if(nextRenderable.renderable && nextRenderable.__renderGroup)break;
- }
-
- this.insertObject(displayObject, previousRenderable, nextRenderable);
-}
-
-/**
- * Adds filter blocks
- *
- * @method addFilterBlocks
- * @param start {FilterBlock}
- * @param end {FilterBlock}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.addFilterBlocks = function(start, end)
-{
- start.__renderGroup = this;
- end.__renderGroup = this;
- /*
- * LOOK FOR THE PREVIOUS RENDERABLE
- * This part looks for the closest previous sprite that can go into a batch
- * It keeps going back until it finds a sprite or the stage
- */
- var previousRenderable = start;
- while(previousRenderable != this.root.first)
- {
- previousRenderable = previousRenderable._iPrev;
- if(previousRenderable.renderable && previousRenderable.__renderGroup)break;
- }
- this.insertAfter(start, previousRenderable);
-
- /*
- * LOOK FOR THE NEXT SPRITE
- * This part looks for the closest next sprite that can go into a batch
- * it keeps looking until it finds a sprite or gets to the end of the display
- * scene graph
- */
- var previousRenderable2 = end;
- while(previousRenderable2 != this.root.first)
- {
- previousRenderable2 = previousRenderable2._iPrev;
- if(previousRenderable2.renderable && previousRenderable2.__renderGroup)break;
- }
- this.insertAfter(end, previousRenderable2);
-}
-
-/**
- * Remove filter blocks
- *
- * @method removeFilterBlocks
- * @param start {FilterBlock}
- * @param end {FilterBlock}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.removeFilterBlocks = function(start, end)
-{
- this.removeObject(start);
- this.removeObject(end);
-}
-
-/**
- * Adds a display object and children to the webgl context
- *
- * @method addDisplayObjectAndChildren
- * @param displayObject {DisplayObject}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.addDisplayObjectAndChildren = function(displayObject)
-{
- if(displayObject.__renderGroup)displayObject.__renderGroup.removeDisplayObjectAndChildren(displayObject);
-
- /*
- * LOOK FOR THE PREVIOUS RENDERABLE
- * This part looks for the closest previous sprite that can go into a batch
- * It keeps going back until it finds a sprite or the stage
- */
-
- var previousRenderable = displayObject.first;
- while(previousRenderable != this.root.first)
- {
- previousRenderable = previousRenderable._iPrev;
- if(previousRenderable.renderable && previousRenderable.__renderGroup)break;
- }
-
- /*
- * LOOK FOR THE NEXT SPRITE
- * This part looks for the closest next sprite that can go into a batch
- * it keeps looking until it finds a sprite or gets to the end of the display
- * scene graph
- */
- var nextRenderable = displayObject.last;
- while(nextRenderable._iNext)
- {
- nextRenderable = nextRenderable._iNext;
- if(nextRenderable.renderable && nextRenderable.__renderGroup)break;
- }
-
- // one the display object hits this. we can break the loop
-
- var tempObject = displayObject.first;
- var testObject = displayObject.last._iNext;
- do
- {
- tempObject.__renderGroup = this;
-
- if(tempObject.renderable)
- {
-
- this.insertObject(tempObject, previousRenderable, nextRenderable);
- previousRenderable = tempObject;
- }
-
- tempObject = tempObject._iNext;
- }
- while(tempObject != testObject)
-}
-
-/**
- * Removes a display object and children to the webgl context
- *
- * @method removeDisplayObjectAndChildren
- * @param displayObject {DisplayObject}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren = function(displayObject)
-{
- if(displayObject.__renderGroup != this)return;
-
-// var displayObject = displayObject.first;
- var lastObject = displayObject.last;
- do
- {
- displayObject.__renderGroup = null;
- if(displayObject.renderable)this.removeObject(displayObject);
- displayObject = displayObject._iNext;
- }
- while(displayObject)
-}
-
-/**
- * Inserts a displayObject into the linked list
- *
- * @method insertObject
- * @param displayObject {DisplayObject}
- * @param previousObject {DisplayObject}
- * @param nextObject {DisplayObject}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.insertObject = function(displayObject, previousObject, nextObject)
-{
- // while looping below THE OBJECT MAY NOT HAVE BEEN ADDED
- var previousSprite = previousObject;
- var nextSprite = nextObject;
-
- /*
- * so now we have the next renderable and the previous renderable
- *
- */
- if(displayObject instanceof PIXI.Sprite)
- {
- var previousBatch
- var nextBatch
-
- if(previousSprite instanceof PIXI.Sprite)
- {
- previousBatch = previousSprite.batch;
- if(previousBatch)
- {
- if(previousBatch.texture == displayObject.texture.baseTexture && previousBatch.blendMode == displayObject.blendMode)
- {
- previousBatch.insertAfter(displayObject, previousSprite);
- return;
- }
- }
- }
- else
- {
- // TODO reword!
- previousBatch = previousSprite;
- }
-
- if(nextSprite)
- {
- if(nextSprite instanceof PIXI.Sprite)
- {
- nextBatch = nextSprite.batch;
-
- //batch may not exist if item was added to the display list but not to the webGL
- if(nextBatch)
- {
- if(nextBatch.texture == displayObject.texture.baseTexture && nextBatch.blendMode == displayObject.blendMode)
- {
- nextBatch.insertBefore(displayObject, nextSprite);
- return;
- }
- else
- {
- if(nextBatch == previousBatch)
- {
- // THERE IS A SPLIT IN THIS BATCH! //
- var splitBatch = previousBatch.split(nextSprite);
- // COOL!
- // add it back into the array
- /*
- * OOPS!
- * seems the new sprite is in the middle of a batch
- * lets split it..
- */
- var batch = PIXI.WebGLRenderer.getBatch();
-
- var index = this.batchs.indexOf( previousBatch );
- batch.init(displayObject);
- this.batchs.splice(index+1, 0, batch, splitBatch);
-
- return;
- }
- }
- }
- }
- else
- {
- // TODO re-word!
-
- nextBatch = nextSprite;
- }
- }
-
- /*
- * looks like it does not belong to any batch!
- * but is also not intersecting one..
- * time to create anew one!
- */
-
- var batch = PIXI.WebGLRenderer.getBatch();
- batch.init(displayObject);
-
- if(previousBatch) // if this is invalid it means
- {
- var index = this.batchs.indexOf( previousBatch );
- this.batchs.splice(index+1, 0, batch);
- }
- else
- {
- this.batchs.push(batch);
- }
-
- return;
- }
- else if(displayObject instanceof PIXI.TilingSprite)
- {
-
- // add to a batch!!
- this.initTilingSprite(displayObject);
- // this.batchs.push(displayObject);
-
- }
- else if(displayObject instanceof PIXI.Strip)
- {
- // add to a batch!!
- this.initStrip(displayObject);
- // this.batchs.push(displayObject);
- }
- else if(displayObject)// instanceof PIXI.Graphics)
- {
- //displayObject.initWebGL(this);
-
- // add to a batch!!
- //this.initStrip(displayObject);
- //this.batchs.push(displayObject);
- }
-
- this.insertAfter(displayObject, previousSprite);
-
- // insert and SPLIT!
-
-}
-
-/**
- * Inserts a displayObject into the linked list
- *
- * @method insertAfter
- * @param item {DisplayObject}
- * @param displayObject {DisplayObject} The object to insert
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.insertAfter = function(item, displayObject)
-{
- if(displayObject instanceof PIXI.Sprite)
- {
- var previousBatch = displayObject.batch;
-
- if(previousBatch)
- {
- // so this object is in a batch!
-
- // is it not? need to split the batch
- if(previousBatch.tail == displayObject)
- {
- // is it tail? insert in to batchs
- var index = this.batchs.indexOf( previousBatch );
- this.batchs.splice(index+1, 0, item);
- }
- else
- {
- // TODO MODIFY ADD / REMOVE CHILD TO ACCOUNT FOR FILTERS (also get prev and next) //
-
- // THERE IS A SPLIT IN THIS BATCH! //
- var splitBatch = previousBatch.split(displayObject.__next);
-
- // COOL!
- // add it back into the array
- /*
- * OOPS!
- * seems the new sprite is in the middle of a batch
- * lets split it..
- */
- var index = this.batchs.indexOf( previousBatch );
- this.batchs.splice(index+1, 0, item, splitBatch);
- }
- }
- else
- {
- this.batchs.push(item);
- }
- }
- else
- {
- var index = this.batchs.indexOf( displayObject );
- this.batchs.splice(index+1, 0, item);
- }
-}
-
-/**
- * Removes a displayObject from the linked list
- *
- * @method removeObject
- * @param displayObject {DisplayObject} The object to remove
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.removeObject = function(displayObject)
-{
- // loop through children..
- // display object //
-
- // add a child from the render group..
- // remove it and all its children!
- //displayObject.cacheVisible = false;//displayObject.visible;
-
- /*
- * removing is a lot quicker..
- *
- */
- var batchToRemove;
-
- if(displayObject instanceof PIXI.Sprite)
- {
- // should always have a batch!
- var batch = displayObject.batch;
- if(!batch)return; // this means the display list has been altered befre rendering
-
- batch.remove(displayObject);
-
- if(batch.size==0)
- {
- batchToRemove = batch;
- }
- }
- else
- {
- batchToRemove = displayObject;
- }
-
- /*
- * Looks like there is somthing that needs removing!
- */
- if(batchToRemove)
- {
- var index = this.batchs.indexOf( batchToRemove );
- if(index == -1)return;// this means it was added then removed before rendered
-
- // ok so.. check to see if you adjacent batchs should be joined.
- // TODO may optimise?
- if(index == 0 || index == this.batchs.length-1)
- {
- // wha - eva! just get of the empty batch!
- this.batchs.splice(index, 1);
- if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove);
-
- return;
- }
-
- if(this.batchs[index-1] instanceof PIXI.WebGLBatch && this.batchs[index+1] instanceof PIXI.WebGLBatch)
- {
- if(this.batchs[index-1].texture == this.batchs[index+1].texture && this.batchs[index-1].blendMode == this.batchs[index+1].blendMode)
- {
- //console.log("MERGE")
- this.batchs[index-1].merge(this.batchs[index+1]);
-
- if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove);
- PIXI.WebGLRenderer.returnBatch(this.batchs[index+1]);
- this.batchs.splice(index, 2);
- return;
- }
- }
-
- this.batchs.splice(index, 1);
- if(batchToRemove instanceof PIXI.WebGLBatch)PIXI.WebGLRenderer.returnBatch(batchToRemove);
- }
-}
-
-
-/**
- * Initializes a tiling sprite
- *
- * @method initTilingSprite
- * @param sprite {TilingSprite} The tiling sprite to initialize
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.initTilingSprite = function(sprite)
-{
- var gl = this.gl;
-
- // make the texture tilable..
-
- sprite.verticies = new Float32Array([0, 0,
- sprite.width, 0,
- sprite.width, sprite.height,
- 0, sprite.height]);
-
- sprite.uvs = new Float32Array([0, 0,
- 1, 0,
- 1, 1,
- 0, 1]);
-
- sprite.colors = new Float32Array([1,1,1,1]);
-
- sprite.indices = new Uint16Array([0, 1, 3,2])//, 2]);
-
- sprite._vertexBuffer = gl.createBuffer();
- sprite._indexBuffer = gl.createBuffer();
- sprite._uvBuffer = gl.createBuffer();
- sprite._colorBuffer = gl.createBuffer();
-
- gl.bindBuffer(gl.ARRAY_BUFFER, sprite._vertexBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, sprite.verticies, gl.STATIC_DRAW);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, sprite._uvBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, sprite.uvs, gl.DYNAMIC_DRAW);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, sprite._colorBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, sprite.colors, gl.STATIC_DRAW);
-
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sprite._indexBuffer);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, sprite.indices, gl.STATIC_DRAW);
-
-// return ( (x > 0) && ((x & (x - 1)) == 0) );
-
- if(sprite.texture.baseTexture._glTexture)
- {
- gl.bindTexture(gl.TEXTURE_2D, sprite.texture.baseTexture._glTexture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
- sprite.texture.baseTexture._powerOf2 = true;
- }
- else
- {
- sprite.texture.baseTexture._powerOf2 = true;
- }
-}
-
-/**
- * Renders a Strip
- *
- * @method renderStrip
- * @param strip {Strip} The strip to render
- * @param projection {Object}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.renderStrip = function(strip, projection)
-{
- var gl = this.gl;
-
- PIXI.activateStripShader();
-
- var shader = PIXI.stripShader;
-
- var program = shader.program;
-
- var m = PIXI.mat3.clone(strip.worldTransform);
-
- PIXI.mat3.transpose(m);
-
-// console.log(projection)
- // set the matrix transform for the
- gl.uniformMatrix3fv(shader.translationMatrix, false, m);
- gl.uniform2f(shader.projectionVector, projection.x, projection.y);
- gl.uniform2f(shader.offsetVector, -PIXI.offset.x, -PIXI.offset.y);
-
- gl.uniform1f(shader.alpha, strip.worldAlpha);
-
- /*
- if(strip.blendMode == PIXI.blendModes.NORMAL)
- {
- gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- }
- else
- {
- gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_COLOR);
- }
- */
-
- //console.log("!!")
- if(!strip.dirty)
- {
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, strip.verticies)
- gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
-
- // update the uvs
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer);
- gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
-
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, strip.texture.baseTexture._glTexture);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._colorBuffer);
- gl.vertexAttribPointer(shader.colorAttribute, 1, gl.FLOAT, false, 0, 0);
-
- // dont need to upload!
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer);
- }
- else
- {
- strip.dirty = false;
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, strip.verticies, gl.STATIC_DRAW)
- gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
-
- // update the uvs
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, strip.uvs, gl.STATIC_DRAW)
- gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
-
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, strip.texture.baseTexture._glTexture);
- // console.log(strip.texture.baseTexture._glTexture)
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._colorBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, strip.colors, gl.STATIC_DRAW)
- gl.vertexAttribPointer(shader.colorAttribute, 1, gl.FLOAT, false, 0, 0);
-
- // dont need to upload!
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW);
-
- }
-
- gl.drawElements(gl.TRIANGLE_STRIP, strip.indices.length, gl.UNSIGNED_SHORT, 0);
-
- PIXI.deactivateStripShader();
- //gl.useProgram(PIXI.currentProgram);
-}
-
-/**
- * Renders a TilingSprite
- *
- * @method renderTilingSprite
- * @param sprite {TilingSprite} The tiling sprite to render
- * @param projectionMatrix {Object}
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.renderTilingSprite = function(sprite, projectionMatrix)
-{
- var gl = this.gl;
-
-
- var shaderProgram = PIXI.shaderProgram;
-
- var tilePosition = sprite.tilePosition;
- var tileScale = sprite.tileScale;
-
- var offsetX = tilePosition.x/sprite.texture.baseTexture.width;
- var offsetY = tilePosition.y/sprite.texture.baseTexture.height;
-
- var scaleX = (sprite.width / sprite.texture.baseTexture.width) / tileScale.x;
- var scaleY = (sprite.height / sprite.texture.baseTexture.height) / tileScale.y;
-
- sprite.uvs[0] = 0 - offsetX;
- sprite.uvs[1] = 0 - offsetY;
-
- sprite.uvs[2] = (1 * scaleX) -offsetX;
- sprite.uvs[3] = 0 - offsetY;
-
- sprite.uvs[4] = (1 *scaleX) - offsetX;
- sprite.uvs[5] = (1 *scaleY) - offsetY;
-
- sprite.uvs[6] = 0 - offsetX;
- sprite.uvs[7] = (1 *scaleY) - offsetY;
-
- gl.bindBuffer(gl.ARRAY_BUFFER, sprite._uvBuffer);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, sprite.uvs)
-
- this.renderStrip(sprite, projectionMatrix);
-}
-
-/**
- * Initializes a strip to be rendered
- *
- * @method initStrip
- * @param strip {Strip} The strip to initialize
- * @private
- */
-PIXI.WebGLRenderGroup.prototype.initStrip = function(strip)
-{
- // build the strip!
- var gl = this.gl;
- var shaderProgram = this.shaderProgram;
-
- strip._vertexBuffer = gl.createBuffer();
- strip._indexBuffer = gl.createBuffer();
- strip._uvBuffer = gl.createBuffer();
- strip._colorBuffer = gl.createBuffer();
-
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._vertexBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, strip.verticies, gl.DYNAMIC_DRAW);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._uvBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, strip.uvs, gl.STATIC_DRAW);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, strip._colorBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, strip.colors, gl.STATIC_DRAW);
-
-
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, strip._indexBuffer);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, strip.indices, gl.STATIC_DRAW);
-}
-
-
-/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
- */
-
-PIXI.initDefaultShaders = function()
-{
- PIXI.primitiveShader = new PIXI.PrimitiveShader();
- PIXI.primitiveShader.init();
-
- PIXI.stripShader = new PIXI.StripShader();
- PIXI.stripShader.init();
-
- PIXI.defaultShader = new PIXI.PixiShader();
- PIXI.defaultShader.init();
-
- var gl = PIXI.gl;
- var shaderProgram = PIXI.defaultShader.program;
-
- gl.useProgram(shaderProgram);
-
- gl.enableVertexAttribArray(PIXI.defaultShader.aVertexPosition);
- gl.enableVertexAttribArray(PIXI.defaultShader.colorAttribute);
- gl.enableVertexAttribArray(PIXI.defaultShader.aTextureCoord);
-};
-
-PIXI.activatePrimitiveShader = function()
-{
- var gl = PIXI.gl;
-
- gl.useProgram(PIXI.primitiveShader.program);
-
- gl.disableVertexAttribArray(PIXI.defaultShader.aVertexPosition);
- gl.disableVertexAttribArray(PIXI.defaultShader.colorAttribute);
- gl.disableVertexAttribArray(PIXI.defaultShader.aTextureCoord);
-
- gl.enableVertexAttribArray(PIXI.primitiveShader.aVertexPosition);
- gl.enableVertexAttribArray(PIXI.primitiveShader.colorAttribute);
-};
-
-PIXI.deactivatePrimitiveShader = function()
-{
- var gl = PIXI.gl;
-
- gl.useProgram(PIXI.defaultShader.program);
-
- gl.disableVertexAttribArray(PIXI.primitiveShader.aVertexPosition);
- gl.disableVertexAttribArray(PIXI.primitiveShader.colorAttribute);
-
- gl.enableVertexAttribArray(PIXI.defaultShader.aVertexPosition);
- gl.enableVertexAttribArray(PIXI.defaultShader.colorAttribute);
- gl.enableVertexAttribArray(PIXI.defaultShader.aTextureCoord);
-};
-
-PIXI.activateStripShader = function()
-{
- var gl = PIXI.gl;
-
- gl.useProgram(PIXI.stripShader.program);
- // gl.disableVertexAttribArray(PIXI.defaultShader.aTextureCoord);
-};
-
-PIXI.deactivateStripShader = function()
-{
- var gl = PIXI.gl;
-
- gl.useProgram(PIXI.defaultShader.program);
- //gl.enableVertexAttribArray(PIXI.defaultShader.aTextureCoord);
-};
-
-/*
-
-SHADER COMPILER HELPERS
+* @class WebGLMaskManager
+* @constructor
+* @param gl {WebGLContext} the current WebGL drawing context
+* @private
*/
-
-PIXI.CompileVertexShader = function(gl, shaderSrc)
+PIXI.WebGLMaskManager = function(gl)
{
- return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER);
+ this.maskStack = [];
+ this.maskPosition = 0;
+
+ this.setContext(gl);
};
-PIXI.CompileFragmentShader = function(gl, shaderSrc)
+/**
+* Sets the drawing context to the one given in parameter
+* @method setContext
+* @param gl {WebGLContext} the current WebGL drawing context
+*/
+PIXI.WebGLMaskManager.prototype.setContext = function(gl)
{
- return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER);
+ this.gl = gl;
};
-PIXI._CompileShader = function(gl, shaderSrc, shaderType)
+/**
+* Applies the Mask and adds it to the current filter stack
+* @method pushMask
+* @param maskData {Array}
+* @param renderSession {RenderSession}
+*/
+PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession)
{
- var src = shaderSrc.join("\n");
- var shader = gl.createShader(shaderType);
- gl.shaderSource(shader, src);
- gl.compileShader(shader);
+ var gl = this.gl;
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
- window.console.log(gl.getShaderInfoLog(shader));
- return null;
+ if(this.maskStack.length === 0)
+ {
+ gl.enable(gl.STENCIL_TEST);
+ gl.stencilFunc(gl.ALWAYS,1,1);
}
+
+ // maskData.visible = false;
- return shader;
+ this.maskStack.push(maskData);
+
+ gl.colorMask(false, false, false, true);
+ gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR);
+
+ PIXI.WebGLGraphics.renderGraphics(maskData, renderSession);
+
+ gl.colorMask(true, true, true, true);
+ gl.stencilFunc(gl.NOTEQUAL,0, this.maskStack.length);
+ gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
};
-PIXI.compileProgram = function(vertexSrc, fragmentSrc)
+/**
+* Removes the last filter from the filter stack and doesn't return it
+* @method popMask
+*
+* @param renderSession {RenderSession} an object containing all the useful parameters
+*/
+PIXI.WebGLMaskManager.prototype.popMask = function(renderSession)
{
- var gl = PIXI.gl;
- var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc);
- var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc);
+ var gl = this.gl;
- var shaderProgram = gl.createProgram();
+ var maskData = this.maskStack.pop();
- gl.attachShader(shaderProgram, vertexShader);
- gl.attachShader(shaderProgram, fragmentShader);
- gl.linkProgram(shaderProgram);
+ if(maskData)
+ {
+ gl.colorMask(false, false, false, false);
- if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
- window.console.log("Could not initialise shaders");
+ //gl.stencilFunc(gl.ALWAYS,1,1);
+ gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR);
+
+ PIXI.WebGLGraphics.renderGraphics(maskData, renderSession);
+
+ gl.colorMask(true, true, true, true);
+ gl.stencilFunc(gl.NOTEQUAL,0,this.maskStack.length);
+ gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
}
-
- return shaderProgram;
+
+ if(this.maskStack.length === 0)gl.disable(gl.STENCIL_TEST);
};
+/**
+* Destroys the mask stack
+* @method destroy
+*/
+PIXI.WebGLMaskManager.prototype.destroy = function()
+{
+ this.maskStack = null;
+ this.gl = null;
+};
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
/**
- * A Text Object will create a line(s) of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n'
- * You can generate the fnt files using
- * http://www.angelcode.com/products/bmfont/ for windows or
- * http://www.bmglyph.com/ for mac.
- *
- * @class BitmapText
- * @extends DisplayObjectContainer
- * @constructor
- * @param text {String} The copy that you would like the text to display
- * @param style {Object} The style parameters
- * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously)
- * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right')
- */
-PIXI.BitmapText = function(text, style)
+* @class WebGLShaderManager
+* @constructor
+* @param gl {WebGLContext} the current WebGL drawing context
+* @private
+*/
+PIXI.WebGLShaderManager = function(gl)
{
- PIXI.DisplayObjectContainer.call(this);
- this.setText(text);
- this.setStyle(style);
- this.updateText();
- this.dirty = false;
-};
+ this.maxAttibs = 10;
+ this.attribState = [];
+ this.tempAttribState = [];
-// constructor
-PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
-PIXI.BitmapText.prototype.constructor = PIXI.BitmapText;
-
-/**
- * Set the copy for the text object
- *
- * @method setText
- * @param text {String} The copy that you would like the text to display
- */
-PIXI.BitmapText.prototype.setText = function(text)
-{
- this.text = text || ' ';
- this.dirty = true;
-};
-
-/**
- * Set the style of the text
- *
- * @method setStyle
- * @param style {Object} The style parameters
- * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously)
- * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right')
- */
-PIXI.BitmapText.prototype.setStyle = function(style)
-{
- style = style || {};
- style.align = style.align || 'left';
- this.style = style;
-
- var font = style.font.split(' ');
- this.fontName = font[font.length - 1];
- this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size;
-
- this.dirty = true;
-};
-
-/**
- * Renders text
- *
- * @method updateText
- * @private
- */
-PIXI.BitmapText.prototype.updateText = function()
-{
- var data = PIXI.BitmapText.fonts[this.fontName];
- var pos = new PIXI.Point();
- var prevCharCode = null;
- var chars = [];
- var maxLineWidth = 0;
- var lineWidths = [];
- var line = 0;
- var scale = this.fontSize / data.size;
- for(var i = 0; i < this.text.length; i++)
- {
- var charCode = this.text.charCodeAt(i);
- if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i)))
- {
- lineWidths.push(pos.x);
- maxLineWidth = Math.max(maxLineWidth, pos.x);
- line++;
-
- pos.x = 0;
- pos.y += data.lineHeight;
- prevCharCode = null;
- continue;
- }
-
- var charData = data.chars[charCode];
- if(!charData) continue;
-
- if(prevCharCode && charData[prevCharCode])
- {
- pos.x += charData.kerning[prevCharCode];
- }
- chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)});
- pos.x += charData.xAdvance;
-
- prevCharCode = charCode;
+ for (var i = 0; i < this.maxAttibs; i++) {
+ this.attribState[i] = false;
}
- lineWidths.push(pos.x);
- maxLineWidth = Math.max(maxLineWidth, pos.x);
+ this.setContext(gl);
+ // the final one is used for the rendering strips
+ //this.stripShader = new PIXI.StripShader(gl);
+};
- var lineAlignOffsets = [];
- for(i = 0; i <= line; i++)
+
+/**
+* Initialises the context and the properties
+* @method setContext
+* @param gl {WebGLContext} the current WebGL drawing context
+* @param transparent {Boolean} Whether or not the drawing context should be transparent
+*/
+PIXI.WebGLShaderManager.prototype.setContext = function(gl)
+{
+ this.gl = gl;
+
+ // the next one is used for rendering primatives
+ this.primitiveShader = new PIXI.PrimitiveShader(gl);
+
+ // this shader is used for the default sprite rendering
+ this.defaultShader = new PIXI.PixiShader(gl);
+
+ // this shader is used for the fast sprite rendering
+ this.fastShader = new PIXI.PixiFastShader(gl);
+
+
+ this.activateShader(this.defaultShader);
+};
+
+
+/**
+* Takes the attributes given in parameters
+* @method setAttribs
+* @param attribs {Array} attribs
+*/
+PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs)
+{
+ // reset temp state
+
+ var i;
+
+ for (i = 0; i < this.tempAttribState.length; i++)
{
- var alignOffset = 0;
- if(this.style.align === 'right')
- {
- alignOffset = maxLineWidth - lineWidths[i];
- }
- else if(this.style.align === 'center')
- {
- alignOffset = (maxLineWidth - lineWidths[i]) / 2;
- }
- lineAlignOffsets.push(alignOffset);
+ this.tempAttribState[i] = false;
}
- for(i = 0; i < chars.length; i++)
+ // set the new attribs
+ for (i = 0; i < attribs.length; i++)
{
- var c = new PIXI.Sprite(chars[i].texture); //PIXI.Sprite.fromFrame(chars[i].charCode);
- c.position.x = (chars[i].position.x + lineAlignOffsets[chars[i].line]) * scale;
- c.position.y = chars[i].position.y * scale;
- c.scale.x = c.scale.y = scale;
- this.addChild(c);
+ var attribId = attribs[i];
+ this.tempAttribState[attribId] = true;
}
- this.width = maxLineWidth * scale;
- this.height = (pos.y + data.lineHeight) * scale;
-};
+ var gl = this.gl;
-/**
- * Updates the transfor of this object
- *
- * @method updateTransform
- * @private
- */
-PIXI.BitmapText.prototype.updateTransform = function()
-{
- if(this.dirty)
+ for (i = 0; i < this.attribState.length; i++)
{
- while(this.children.length > 0)
+
+ if(this.attribState[i] !== this.tempAttribState[i])
{
- this.removeChild(this.getChildAt(0));
- }
- this.updateText();
+ this.attribState[i] = this.tempAttribState[i];
- this.dirty = false;
- }
-
- PIXI.DisplayObjectContainer.prototype.updateTransform.call(this);
-};
-
-PIXI.BitmapText.fonts = {};
-
-/**
- * @author Mat Groves http://matgroves.com/ @Doormat23
- */
-
-/**
- * A Text Object will create a line(s) of text to split a line you can use '\n'
- *
- * @class Text
- * @extends Sprite
- * @constructor
- * @param text {String} The copy that you would like the text to display
- * @param [style] {Object} The style parameters
- * @param [style.font] {String} default 'bold 20pt Arial' The style and size of the font
- * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00'
- * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right')
- * @param [style.stroke] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00'
- * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke)
- * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used
- * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap
- */
-PIXI.Text = function(text, style)
-{
- this.canvas = document.createElement('canvas');
- this.context = this.canvas.getContext('2d');
- PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas));
-
- this.setText(text);
- this.setStyle(style);
-
- this.updateText();
- this.dirty = false;
-};
-
-// constructor
-PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype);
-PIXI.Text.prototype.constructor = PIXI.Text;
-
-/**
- * Set the style of the text
- *
- * @method setStyle
- * @param [style] {Object} The style parameters
- * @param [style.font='bold 20pt Arial'] {String} The style and size of the font
- * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00'
- * @param [style.align='left'] {String} An alignment of the multiline text ('left', 'center' or 'right')
- * @param [style.stroke='black'] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00'
- * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke)
- * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used
- * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap
- */
-PIXI.Text.prototype.setStyle = function(style)
-{
- style = style || {};
- style.font = style.font || 'bold 20pt Arial';
- style.fill = style.fill || 'black';
- style.align = style.align || 'left';
- style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136
- style.strokeThickness = style.strokeThickness || 0;
- style.wordWrap = style.wordWrap || false;
- style.wordWrapWidth = style.wordWrapWidth || 100;
- this.style = style;
- this.dirty = true;
-};
-
-/**
- * Set the copy for the text object. To split a line you can use '\n'
- *
- * @method setText
- * @param {String} text The copy that you would like the text to display
- */
-PIXI.Text.prototype.setText = function(text)
-{
- this.text = text.toString() || ' ';
- this.dirty = true;
-};
-
-/**
- * Renders text
- *
- * @method updateText
- * @private
- */
-PIXI.Text.prototype.updateText = function()
-{
- this.context.font = this.style.font;
-
- var outputText = this.text;
-
- // word wrap
- // preserve original text
- if(this.style.wordWrap)outputText = this.wordWrap(this.text);
-
- //split text into lines
- var lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- //calculate text width
- var lineWidths = [];
- var maxLineWidth = 0;
- for (var i = 0; i < lines.length; i++)
- {
- var lineWidth = this.context.measureText(lines[i]).width;
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
- this.canvas.width = maxLineWidth + this.style.strokeThickness;
-
- //calculate text height
- var lineHeight = this.determineFontHeight('font: ' + this.style.font + ';') + this.style.strokeThickness;
- this.canvas.height = lineHeight * lines.length;
-
- //set canvas text styles
- this.context.fillStyle = this.style.fill;
- this.context.font = this.style.font;
-
- this.context.strokeStyle = this.style.stroke;
- this.context.lineWidth = this.style.strokeThickness;
-
- this.context.textBaseline = 'top';
-
- //draw lines line by line
- for (i = 0; i < lines.length; i++)
- {
- var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight);
-
- if(this.style.align === 'right')
- {
- linePosition.x += maxLineWidth - lineWidths[i];
- }
- else if(this.style.align === 'center')
- {
- linePosition.x += (maxLineWidth - lineWidths[i]) / 2;
- }
-
- if(this.style.stroke && this.style.strokeThickness)
- {
- this.context.strokeText(lines[i], linePosition.x, linePosition.y);
- }
-
- if(this.style.fill)
- {
- this.context.fillText(lines[i], linePosition.x, linePosition.y);
- }
- }
-
- this.updateTexture();
-};
-
-/**
- * Updates texture size based on canvas size
- *
- * @method updateTexture
- * @private
- */
-PIXI.Text.prototype.updateTexture = function()
-{
- this.texture.baseTexture.width = this.canvas.width;
- this.texture.baseTexture.height = this.canvas.height;
- this.texture.frame.width = this.canvas.width;
- this.texture.frame.height = this.canvas.height;
-
- this._width = this.canvas.width;
- this._height = this.canvas.height;
-
- PIXI.texturesToUpdate.push(this.texture.baseTexture);
-};
-
-/**
- * Updates the transfor of this object
- *
- * @method updateTransform
- * @private
- */
-PIXI.Text.prototype.updateTransform = function()
-{
- if(this.dirty)
- {
- this.updateText();
- this.dirty = false;
- }
-
- PIXI.Sprite.prototype.updateTransform.call(this);
-};
-
-/*
- * http://stackoverflow.com/users/34441/ellisbben
- * great solution to the problem!
- *
- * @method determineFontHeight
- * @param fontStyle {Object}
- * @private
- */
-PIXI.Text.prototype.determineFontHeight = function(fontStyle)
-{
- // build a little reference dictionary so if the font style has been used return a
- // cached version...
- var result = PIXI.Text.heightCache[fontStyle];
-
- if(!result)
- {
- var body = document.getElementsByTagName('body')[0];
- var dummy = document.createElement('div');
- var dummyText = document.createTextNode('M');
- dummy.appendChild(dummyText);
- dummy.setAttribute('style', fontStyle + ';position:absolute;top:0;left:0');
- body.appendChild(dummy);
-
- result = dummy.offsetHeight;
- PIXI.Text.heightCache[fontStyle] = result;
-
- body.removeChild(dummy);
- }
-
- return result;
-};
-
-/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @method wordWrap
- * @param text {String}
- * @private
- */
-PIXI.Text.prototype.wordWrap = function(text)
-{
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- var result = '';
- var lines = text.split('\n');
- for (var i = 0; i < lines.length; i++)
- {
- var spaceLeft = this.style.wordWrapWidth;
- var words = lines[i].split(' ');
- for (var j = 0; j < words.length; j++)
- {
- var wordWidth = this.context.measureText(words[j]).width;
- var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
- if(wordWidthWithSpace > spaceLeft)
+ if(this.tempAttribState[i])
{
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if(j > 0)
- {
- result += '\n';
- }
- result += words[j] + ' ';
- spaceLeft = this.style.wordWrapWidth - wordWidth;
+ gl.enableVertexAttribArray(i);
}
else
{
- spaceLeft -= wordWidthWithSpace;
- result += words[j] + ' ';
+ gl.disableVertexAttribArray(i);
}
}
- result += '\n';
}
- return result;
};
/**
- * Destroys this text object
- *
- * @method destroy
- * @param destroyTexture {Boolean}
- */
-PIXI.Text.prototype.destroy = function(destroyTexture)
+* Sets-up the given shader
+*
+* @method activateShader
+* @param shader {Object} the shader that is going to be activated
+*/
+PIXI.WebGLShaderManager.prototype.activateShader = function(shader)
{
- if(destroyTexture)
+ //if(this.currentShader == shader)return;
+
+ this.currentShader = shader;
+
+ this.gl.useProgram(shader.program);
+ this.setAttribs(shader.attributes);
+
+};
+
+/**
+* Triggers the primitive shader
+* @method activatePrimitiveShader
+*/
+PIXI.WebGLShaderManager.prototype.activatePrimitiveShader = function()
+{
+ var gl = this.gl;
+
+ gl.useProgram(this.primitiveShader.program);
+
+ this.setAttribs(this.primitiveShader.attributes);
+
+};
+
+/**
+* Disable the primitive shader
+* @method deactivatePrimitiveShader
+*/
+PIXI.WebGLShaderManager.prototype.deactivatePrimitiveShader = function()
+{
+ var gl = this.gl;
+
+ gl.useProgram(this.defaultShader.program);
+
+ this.setAttribs(this.defaultShader.attributes);
+};
+
+/**
+* Destroys
+* @method destroy
+*/
+PIXI.WebGLShaderManager.prototype.destroy = function()
+{
+ this.attribState = null;
+
+ this.tempAttribState = null;
+
+ this.primitiveShader.destroy();
+
+ this.defaultShader.destroy();
+
+ this.fastShader.destroy();
+
+ this.gl = null;
+};
+
+
+/**
+ * @author Mat Groves
+ *
+ * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/
+ * for creating the original pixi version!
+ *
+ * Heavily inspired by LibGDX's WebGLSpriteBatch:
+ * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java
+ */
+
+ /**
+ *
+ * @class WebGLSpriteBatch
+ * @private
+ * @constructor
+ * @param gl {WebGLContext} the current WebGL drawing context
+ *
+ */
+PIXI.WebGLSpriteBatch = function(gl)
+{
+
+ /**
+ *
+ *
+ * @property vertSize
+ * @type Number
+ */
+ this.vertSize = 6;
+
+ /**
+ * The number of images in the SpriteBatch before it flushes
+ * @property size
+ * @type Number
+ */
+ this.size = 10000;//Math.pow(2, 16) / this.vertSize;
+
+ //the total number of floats in our batch
+ var numVerts = this.size * 4 * this.vertSize;
+ //the total number of indices in our batch
+ var numIndices = this.size * 6;
+
+ //vertex data
+
+ /**
+ * Holds the vertices
+ *
+ * @property vertices
+ * @type Float32Array
+ */
+ this.vertices = new Float32Array(numVerts);
+
+ //index data
+ /**
+ * Holds the indices
+ *
+ * @property indices
+ * @type Uint16Array
+ */
+ this.indices = new Uint16Array(numIndices);
+
+ this.lastIndexCount = 0;
+
+ for (var i=0, j=0; i < numIndices; i += 6, j += 4)
{
- this.texture.destroy();
+ this.indices[i + 0] = j + 0;
+ this.indices[i + 1] = j + 1;
+ this.indices[i + 2] = j + 2;
+ this.indices[i + 3] = j + 0;
+ this.indices[i + 4] = j + 2;
+ this.indices[i + 5] = j + 3;
}
+
+ this.drawing = false;
+ this.currentBatchSize = 0;
+ this.currentBaseTexture = null;
+
+ this.setContext(gl);
+};
+
+/**
+*
+* @method setContext
+*
+* @param gl {WebGLContext} the current WebGL drawing context
+*/
+PIXI.WebGLSpriteBatch.prototype.setContext = function(gl)
+{
+ this.gl = gl;
+
+ // create a couple of buffers
+ this.vertexBuffer = gl.createBuffer();
+ this.indexBuffer = gl.createBuffer();
+
+ // 65535 is max index, so 65535 / 6 = 10922.
+
+
+ //upload the index data
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
+
+ this.currentBlendMode = 99999;
+};
+
+/**
+*
+* @method begin
+*
+* @param renderSession {RenderSession} the RenderSession
+*/
+PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession)
+{
+ this.renderSession = renderSession;
+ this.shader = this.renderSession.shaderManager.defaultShader;
+
+ this.start();
+};
+
+/**
+*
+* @method end
+*
+*/
+PIXI.WebGLSpriteBatch.prototype.end = function()
+{
+ this.flush();
+};
+
+/**
+*
+* @method render
+*
+* @param sprite {Sprite} the sprite to render when using this spritebatch
+*/
+PIXI.WebGLSpriteBatch.prototype.render = function(sprite)
+{
+ var texture = sprite.texture;
+
+ // check texture..
+ if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size)
+ {
+ this.flush();
+ this.currentBaseTexture = texture.baseTexture;
+ }
+
+
+ // check blend mode
+ if(sprite.blendMode !== this.currentBlendMode)
+ {
+ this.setBlendMode(sprite.blendMode);
+ }
+
+ // get the uvs for the texture
+ var uvs = sprite._uvs || sprite.texture._uvs;
+ // if the uvs have not updated then no point rendering just yet!
+ if(!uvs)return;
+
+ // get the sprites current alpha
+ var alpha = sprite.worldAlpha;
+ var tint = sprite.tint;
+
+ var verticies = this.vertices;
+
+
+ // TODO trim??
+ var aX = sprite.anchor.x;
+ var aY = sprite.anchor.y;
+
+ var w0, w1, h0, h1;
+
+ if (sprite.texture.trim)
+ {
+ // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords..
+ var trim = sprite.texture.trim;
+
+ w1 = trim.x - aX * trim.width;
+ w0 = w1 + texture.frame.width;
+
+ h1 = trim.y - aY * trim.height;
+ h0 = h1 + texture.frame.height;
+
+ }
+ else
+ {
+ w0 = (texture.frame.width ) * (1-aX);
+ w1 = (texture.frame.width ) * -aX;
+
+ h0 = texture.frame.height * (1-aY);
+ h1 = texture.frame.height * -aY;
+ }
+
+ var index = this.currentBatchSize * 4 * this.vertSize;
+
+ var worldTransform = sprite.worldTransform;//.toArray();
+
+ var a = worldTransform.a;//[0];
+ var b = worldTransform.c;//[3];
+ var c = worldTransform.b;//[1];
+ var d = worldTransform.d;//[4];
+ var tx = worldTransform.tx;//[2];
+ var ty = worldTransform.ty;///[5];
+
+ // xy
+ verticies[index++] = a * w1 + c * h1 + tx;
+ verticies[index++] = d * h1 + b * w1 + ty;
+ // uv
+ verticies[index++] = uvs.x0;
+ verticies[index++] = uvs.y0;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // xy
+ verticies[index++] = a * w0 + c * h1 + tx;
+ verticies[index++] = d * h1 + b * w0 + ty;
+ // uv
+ verticies[index++] = uvs.x1;
+ verticies[index++] = uvs.y1;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // xy
+ verticies[index++] = a * w0 + c * h0 + tx;
+ verticies[index++] = d * h0 + b * w0 + ty;
+ // uv
+ verticies[index++] = uvs.x2;
+ verticies[index++] = uvs.y2;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // xy
+ verticies[index++] = a * w1 + c * h0 + tx;
+ verticies[index++] = d * h0 + b * w1 + ty;
+ // uv
+ verticies[index++] = uvs.x3;
+ verticies[index++] = uvs.y3;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // increment the batchsize
+ this.currentBatchSize++;
+
+
};
-PIXI.Text.heightCache = {};
+/**
+* Renders a tilingSprite using the spriteBatch
+* @method renderTilingSprite
+*
+* @param sprite {TilingSprite} the tilingSprite to render
+*/
+PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite)
+{
+ var texture = tilingSprite.tilingTexture;
+ if(texture.baseTexture !== this.currentBaseTexture || this.currentBatchSize >= this.size)
+ {
+ this.flush();
+ this.currentBaseTexture = texture.baseTexture;
+ }
+
+ // check blend mode
+ if(tilingSprite.blendMode !== this.currentBlendMode)
+ {
+ this.setBlendMode(tilingSprite.blendMode);
+ }
+
+ // set the textures uvs temporarily
+ // TODO create a separate texture so that we can tile part of a texture
+
+ if(!tilingSprite._uvs)tilingSprite._uvs = new PIXI.TextureUvs();
+
+ var uvs = tilingSprite._uvs;
+
+ tilingSprite.tilePosition.x %= texture.baseTexture.width * tilingSprite.tileScaleOffset.x;
+ tilingSprite.tilePosition.y %= texture.baseTexture.height * tilingSprite.tileScaleOffset.y;
+
+ var offsetX = tilingSprite.tilePosition.x/(texture.baseTexture.width*tilingSprite.tileScaleOffset.x);
+ var offsetY = tilingSprite.tilePosition.y/(texture.baseTexture.height*tilingSprite.tileScaleOffset.y);
+
+ var scaleX = (tilingSprite.width / texture.baseTexture.width) / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x);
+ var scaleY = (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y);
+
+ uvs.x0 = 0 - offsetX;
+ uvs.y0 = 0 - offsetY;
+
+ uvs.x1 = (1 * scaleX) - offsetX;
+ uvs.y1 = 0 - offsetY;
+
+ uvs.x2 = (1 * scaleX) - offsetX;
+ uvs.y2 = (1 * scaleY) - offsetY;
+
+ uvs.x3 = 0 - offsetX;
+ uvs.y3 = (1 *scaleY) - offsetY;
+
+ // get the tilingSprites current alpha
+ var alpha = tilingSprite.worldAlpha;
+ var tint = tilingSprite.tint;
+
+ var verticies = this.vertices;
+
+ var width = tilingSprite.width;
+ var height = tilingSprite.height;
+
+ // TODO trim??
+ var aX = tilingSprite.anchor.x; // - tilingSprite.texture.trim.x
+ var aY = tilingSprite.anchor.y; //- tilingSprite.texture.trim.y
+ var w0 = width * (1-aX);
+ var w1 = width * -aX;
+
+ var h0 = height * (1-aY);
+ var h1 = height * -aY;
+
+ var index = this.currentBatchSize * 4 * this.vertSize;
+
+ var worldTransform = tilingSprite.worldTransform;
+
+ var a = worldTransform.a;//[0];
+ var b = worldTransform.c;//[3];
+ var c = worldTransform.b;//[1];
+ var d = worldTransform.d;//[4];
+ var tx = worldTransform.tx;//[2];
+ var ty = worldTransform.ty;///[5];
+
+ // xy
+ verticies[index++] = a * w1 + c * h1 + tx;
+ verticies[index++] = d * h1 + b * w1 + ty;
+ // uv
+ verticies[index++] = uvs.x0;
+ verticies[index++] = uvs.y0;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // xy
+ verticies[index++] = a * w0 + c * h1 + tx;
+ verticies[index++] = d * h1 + b * w0 + ty;
+ // uv
+ verticies[index++] = uvs.x1;
+ verticies[index++] = uvs.y1;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // xy
+ verticies[index++] = a * w0 + c * h0 + tx;
+ verticies[index++] = d * h0 + b * w0 + ty;
+ // uv
+ verticies[index++] = uvs.x2;
+ verticies[index++] = uvs.y2;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // xy
+ verticies[index++] = a * w1 + c * h0 + tx;
+ verticies[index++] = d * h0 + b * w1 + ty;
+ // uv
+ verticies[index++] = uvs.x3;
+ verticies[index++] = uvs.y3;
+ // color
+ verticies[index++] = alpha;
+ verticies[index++] = tint;
+
+ // increment the batchs
+ this.currentBatchSize++;
+};
+
+
+/**
+* Renders the content and empties the current batch
+*
+* @method flush
+*
+*/
+PIXI.WebGLSpriteBatch.prototype.flush = function()
+{
+ // If the batch is length 0 then return as there is nothing to draw
+ if (this.currentBatchSize===0)return;
+
+ var gl = this.gl;
+
+ // bind the current texture
+ gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id] || PIXI.createWebGLTexture(this.currentBaseTexture, gl));
+
+ // upload the verts to the buffer
+
+ if(this.currentBatchSize > ( this.size * 0.5 ) )
+ {
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
+ }
+ else
+ {
+ var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize);
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
+ }
+
+ // var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize);
+ //gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
+
+ // now draw those suckas!
+ gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0);
+
+ // then reset the batch!
+ this.currentBatchSize = 0;
+
+ // increment the draw count
+ this.renderSession.drawCount++;
+};
+
+/**
+*
+* @method stop
+*
+*/
+PIXI.WebGLSpriteBatch.prototype.stop = function()
+{
+ this.flush();
+};
+
+/**
+*
+* @method start
+*
+*/
+PIXI.WebGLSpriteBatch.prototype.start = function()
+{
+ var gl = this.gl;
+
+ // bind the main texture
+ gl.activeTexture(gl.TEXTURE0);
+
+ // bind the buffers
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
+
+ // set the projection
+ var projection = this.renderSession.projection;
+ gl.uniform2f(this.shader.projectionVector, projection.x, projection.y);
+
+ // set the pointers
+ var stride = this.vertSize * 4;
+ gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0);
+ gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4);
+ gl.vertexAttribPointer(this.shader.colorAttribute, 2, gl.FLOAT, false, stride, 4 * 4);
+
+ // set the blend mode..
+ if(this.currentBlendMode !== PIXI.blendModes.NORMAL)
+ {
+ this.setBlendMode(PIXI.blendModes.NORMAL);
+ }
+};
+
+/**
+* Sets-up the given blendMode from WebGL's point of view
+* @method setBlendMode
+*
+* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD
+*/
+PIXI.WebGLSpriteBatch.prototype.setBlendMode = function(blendMode)
+{
+ this.flush();
+
+ this.currentBlendMode = blendMode;
+
+ var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode];
+ this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]);
+};
+
+/**
+* Destroys the SpriteBatch
+* @method destroy
+*/
+PIXI.WebGLSpriteBatch.prototype.destroy = function()
+{
+
+ this.vertices = null;
+ this.indices = null;
+
+ this.gl.deleteBuffer( this.vertexBuffer );
+ this.gl.deleteBuffer( this.indexBuffer );
+
+ this.currentBaseTexture = null;
+
+ this.gl = null;
+};
+
+
+/**
+ * @author Mat Groves
+ *
+ * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/
+ * for creating the original pixi version!
+ *
+ * Heavily inspired by LibGDX's WebGLSpriteBatch:
+ * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java
+ */
+
+PIXI.WebGLFastSpriteBatch = function(gl)
+{
+
+
+ this.vertSize = 10;
+ this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize;
+ this.size = this.maxSize;
+
+ //the total number of floats in our batch
+ var numVerts = this.size * 4 * this.vertSize;
+ //the total number of indices in our batch
+ var numIndices = this.maxSize * 6;
+
+ //vertex data
+ this.vertices = new Float32Array(numVerts);
+ //index data
+ this.indices = new Uint16Array(numIndices);
+
+ this.vertexBuffer = null;
+ this.indexBuffer = null;
+
+ this.lastIndexCount = 0;
+
+ for (var i=0, j=0; i < numIndices; i += 6, j += 4)
+ {
+ this.indices[i + 0] = j + 0;
+ this.indices[i + 1] = j + 1;
+ this.indices[i + 2] = j + 2;
+ this.indices[i + 3] = j + 0;
+ this.indices[i + 4] = j + 2;
+ this.indices[i + 5] = j + 3;
+ }
+
+ this.drawing = false;
+ this.currentBatchSize = 0;
+ this.currentBaseTexture = null;
+
+ this.currentBlendMode = 0;
+ this.renderSession = null;
+
+
+ this.shader = null;
+
+ this.matrix = null;
+
+ this.setContext(gl);
+};
+
+PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl)
+{
+ this.gl = gl;
+
+ // create a couple of buffers
+ this.vertexBuffer = gl.createBuffer();
+ this.indexBuffer = gl.createBuffer();
+
+ // 65535 is max index, so 65535 / 6 = 10922.
+
+
+ //upload the index data
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW);
+
+ this.currentBlendMode = 99999;
+};
+
+PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession)
+{
+ this.renderSession = renderSession;
+ this.shader = this.renderSession.shaderManager.fastShader;
+
+ this.matrix = spriteBatch.worldTransform.toArray(true);
+
+ this.start();
+};
+
+PIXI.WebGLFastSpriteBatch.prototype.end = function()
+{
+ this.flush();
+};
+
+
+PIXI.WebGLFastSpriteBatch.prototype.render = function(spriteBatch)
+{
+
+ var children = spriteBatch.children;
+ var sprite = children[0];
+
+ // if the uvs have not updated then no point rendering just yet!
+
+ // check texture.
+ if(!sprite.texture._uvs)return;
+
+ this.currentBaseTexture = sprite.texture.baseTexture;
+ // check blend mode
+ if(sprite.blendMode !== this.currentBlendMode)
+ {
+ this.setBlendMode(sprite.blendMode);
+ }
+
+ for(var i=0,j= children.length; i= this.size)
+ {
+ this.flush();
+ }
+};
+
+PIXI.WebGLFastSpriteBatch.prototype.flush = function()
+{
+
+ // If the batch is length 0 then return as there is nothing to draw
+ if (this.currentBatchSize===0)return;
+
+ var gl = this.gl;
+
+ // bind the current texture
+
+ if(!this.currentBaseTexture._glTextures[gl.id])PIXI.createWebGLTexture(this.currentBaseTexture, gl);
+
+ gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id]);// || PIXI.createWebGLTexture(this.currentBaseTexture, gl));
+
+ // upload the verts to the buffer
+
+
+ if(this.currentBatchSize > ( this.size * 0.5 ) )
+ {
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices);
+ }
+ else
+ {
+ var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize);
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, view);
+ }
+
+
+ // now draw those suckas!
+ gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0);
+
+ // then reset the batch!
+ this.currentBatchSize = 0;
+
+ // increment the draw count
+ this.renderSession.drawCount++;
+};
+
+
+PIXI.WebGLFastSpriteBatch.prototype.stop = function()
+{
+ this.flush();
+};
+
+PIXI.WebGLFastSpriteBatch.prototype.start = function()
+{
+ var gl = this.gl;
+
+ // bind the main texture
+ gl.activeTexture(gl.TEXTURE0);
+
+ // bind the buffers
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
+
+ // set the projection
+ var projection = this.renderSession.projection;
+ gl.uniform2f(this.shader.projectionVector, projection.x, projection.y);
+
+ // set the matrix
+ gl.uniformMatrix3fv(this.shader.uMatrix, false, this.matrix);
+
+ // set the pointers
+ var stride = this.vertSize * 4;
+
+ gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0);
+ gl.vertexAttribPointer(this.shader.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4);
+ gl.vertexAttribPointer(this.shader.aScale, 2, gl.FLOAT, false, stride, 4 * 4);
+ gl.vertexAttribPointer(this.shader.aRotation, 1, gl.FLOAT, false, stride, 6 * 4);
+ gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4);
+ gl.vertexAttribPointer(this.shader.colorAttribute, 1, gl.FLOAT, false, stride, 9 * 4);
+
+ // set the blend mode..
+ if(this.currentBlendMode !== PIXI.blendModes.NORMAL)
+ {
+ this.setBlendMode(PIXI.blendModes.NORMAL);
+ }
+};
+
+PIXI.WebGLFastSpriteBatch.prototype.setBlendMode = function(blendMode)
+{
+ this.flush();
+
+ this.currentBlendMode = blendMode;
+
+ var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode];
+ this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]);
+};
+
+
+
+/**
+ * @author Mat Groves http://matgroves.com/ @Doormat23
+ */
+
+/**
+* @class WebGLFilterManager
+* @constructor
+* @param gl {WebGLContext} the current WebGL drawing context
+* @param transparent {Boolean} Whether or not the drawing context should be transparent
+* @private
+*/
+PIXI.WebGLFilterManager = function(gl, transparent)
+{
+ this.transparent = transparent;
+
+ this.filterStack = [];
+
+ this.offsetX = 0;
+ this.offsetY = 0;
+
+ this.setContext(gl);
+};
+
+// API
+/**
+* Initialises the context and the properties
+* @method setContext
+* @param gl {WebGLContext} the current WebGL drawing context
+*/
+PIXI.WebGLFilterManager.prototype.setContext = function(gl)
+{
+ this.gl = gl;
+ this.texturePool = [];
+
+ this.initShaderBuffers();
+};
+
+/**
+*
+* @method begin
+* @param renderSession {RenderSession}
+* @param buffer {ArrayBuffer}
+*/
+PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer)
+{
+ this.renderSession = renderSession;
+ this.defaultShader = renderSession.shaderManager.defaultShader;
+
+ var projection = this.renderSession.projection;
+ // console.log(this.width)
+ this.width = projection.x * 2;
+ this.height = -projection.y * 2;
+ this.buffer = buffer;
+};
+
+/**
+* Applies the filter and adds it to the current filter stack
+* @method pushFilter
+* @param filterBlock {Object} the filter that will be pushed to the current filter stack
+*/
+PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock)
+{
+ var gl = this.gl;
+
+ var projection = this.renderSession.projection;
+ var offset = this.renderSession.offset;
+
+
+ // filter program
+ // OPTIMISATION - the first filter is free if its a simple color change?
+ this.filterStack.push(filterBlock);
+
+ var filter = filterBlock.filterPasses[0];
+
+ this.offsetX += filterBlock.target.filterArea.x;
+ this.offsetY += filterBlock.target.filterArea.y;
+
+ var texture = this.texturePool.pop();
+ if(!texture)
+ {
+ texture = new PIXI.FilterTexture(this.gl, this.width, this.height);
+ }
+ else
+ {
+ texture.resize(this.width, this.height);
+ }
+
+ gl.bindTexture(gl.TEXTURE_2D, texture.texture);
+
+ filterBlock.target.filterArea = filterBlock.target.getBounds();
+
+ var filterArea = filterBlock.target.filterArea;
+
+ var padidng = filter.padding;
+ filterArea.x -= padidng;
+ filterArea.y -= padidng;
+ filterArea.width += padidng * 2;
+ filterArea.height += padidng * 2;
+
+ // cap filter to screen size..
+ if(filterArea.x < 0)filterArea.x = 0;
+ if(filterArea.width > this.width)filterArea.width = this.width;
+ if(filterArea.y < 0)filterArea.y = 0;
+ if(filterArea.height > this.height)filterArea.height = this.height;
+
+ //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer);
+
+ // set view port
+ gl.viewport(0, 0, filterArea.width, filterArea.height);
+
+ projection.x = filterArea.width/2;
+ projection.y = -filterArea.height/2;
+
+ offset.x = -filterArea.x;
+ offset.y = -filterArea.y;
+
+ // update projection
+ gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2);
+ gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y);
+
+ gl.colorMask(true, true, true, true);
+ gl.clearColor(0,0,0, 0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ filterBlock._glFilterTexture = texture;
+
+};
+
+
+/**
+* Removes the last filter from the filter stack and doesn't return it
+* @method popFilter
+*/
+PIXI.WebGLFilterManager.prototype.popFilter = function()
+{
+ var gl = this.gl;
+ var filterBlock = this.filterStack.pop();
+ var filterArea = filterBlock.target.filterArea;
+ var texture = filterBlock._glFilterTexture;
+ var projection = this.renderSession.projection;
+ var offset = this.renderSession.offset;
+
+ if(filterBlock.filterPasses.length > 1)
+ {
+ gl.viewport(0, 0, filterArea.width, filterArea.height);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+
+ this.vertexArray[0] = 0;
+ this.vertexArray[1] = filterArea.height;
+
+ this.vertexArray[2] = filterArea.width;
+ this.vertexArray[3] = filterArea.height;
+
+ this.vertexArray[4] = 0;
+ this.vertexArray[5] = 0;
+
+ this.vertexArray[6] = filterArea.width;
+ this.vertexArray[7] = 0;
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
+ // now set the uvs..
+ this.uvArray[2] = filterArea.width/this.width;
+ this.uvArray[5] = filterArea.height/this.height;
+ this.uvArray[6] = filterArea.width/this.width;
+ this.uvArray[7] = filterArea.height/this.height;
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);
+
+ var inputTexture = texture;
+ var outputTexture = this.texturePool.pop();
+ if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height);
+ outputTexture.resize(this.width, this.height);
+
+ // need to clear this FBO as it may have some left over elements from a previous filter.
+ gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ gl.disable(gl.BLEND);
+
+ for (var i = 0; i < filterBlock.filterPasses.length-1; i++)
+ {
+ var filterPass = filterBlock.filterPasses[i];
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer );
+
+ // set texture
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture);
+
+ // draw texture..
+ //filterPass.applyFilterPass(filterArea.width, filterArea.height);
+ this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height);
+
+ // swap the textures..
+ var temp = inputTexture;
+ inputTexture = outputTexture;
+ outputTexture = temp;
+ }
+
+ gl.enable(gl.BLEND);
+
+ texture = inputTexture;
+ this.texturePool.push(outputTexture);
+ }
+
+ var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1];
+
+ this.offsetX -= filterArea.x;
+ this.offsetY -= filterArea.y;
+
+
+ var sizeX = this.width;
+ var sizeY = this.height;
+
+ var offsetX = 0;
+ var offsetY = 0;
+
+ var buffer = this.buffer;
+
+ // time to render the filters texture to the previous scene
+ if(this.filterStack.length === 0)
+ {
+ gl.colorMask(true, true, true, true);//this.transparent);
+ }
+ else
+ {
+ var currentFilter = this.filterStack[this.filterStack.length-1];
+ filterArea = currentFilter.target.filterArea;
+
+ sizeX = filterArea.width;
+ sizeY = filterArea.height;
+
+ offsetX = filterArea.x;
+ offsetY = filterArea.y;
+
+ buffer = currentFilter._glFilterTexture.frameBuffer;
+ }
+
+
+
+ // TODO need toremove thease global elements..
+ projection.x = sizeX/2;
+ projection.y = -sizeY/2;
+
+ offset.x = offsetX;
+ offset.y = offsetY;
+
+ filterArea = filterBlock.target.filterArea;
+
+ var x = filterArea.x-offsetX;
+ var y = filterArea.y-offsetY;
+
+ // update the buffers..
+ // make sure to flip the y!
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+
+ this.vertexArray[0] = x;
+ this.vertexArray[1] = y + filterArea.height;
+
+ this.vertexArray[2] = x + filterArea.width;
+ this.vertexArray[3] = y + filterArea.height;
+
+ this.vertexArray[4] = x;
+ this.vertexArray[5] = y;
+
+ this.vertexArray[6] = x + filterArea.width;
+ this.vertexArray[7] = y;
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
+
+ this.uvArray[2] = filterArea.width/this.width;
+ this.uvArray[5] = filterArea.height/this.height;
+ this.uvArray[6] = filterArea.width/this.width;
+ this.uvArray[7] = filterArea.height/this.height;
+
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray);
+
+ //console.log(this.vertexArray)
+ //console.log(this.uvArray)
+ //console.log(sizeX + " : " + sizeY)
+
+ gl.viewport(0, 0, sizeX, sizeY);
+
+ // bind the buffer
+ gl.bindFramebuffer(gl.FRAMEBUFFER, buffer );
+
+ // set the blend mode!
+ //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
+
+ // set texture
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, texture.texture);
+
+ // apply!
+ this.applyFilterPass(filter, filterArea, sizeX, sizeY);
+
+ // now restore the regular shader..
+ gl.useProgram(this.defaultShader.program);
+ gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2);
+ gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY);
+
+ // return the texture to the pool
+ this.texturePool.push(texture);
+ filterBlock._glFilterTexture = null;
+};
+
+
+/**
+* Applies the filter to the specified area
+* @method applyFilterPass
+* @param filter {AbstractFilter} the filter that needs to be applied
+* @param filterArea {texture} TODO - might need an update
+* @param width {Number} the horizontal range of the filter
+* @param height {Number} the vertical range of the filter
+*/
+PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height)
+{
+ // use program
+ var gl = this.gl;
+ var shader = filter.shaders[gl.id];
+
+ if(!shader)
+ {
+ shader = new PIXI.PixiShader(gl);
+
+ shader.fragmentSrc = filter.fragmentSrc;
+ shader.uniforms = filter.uniforms;
+ shader.init();
+
+ filter.shaders[gl.id] = shader;
+ }
+
+ // set the shader
+ gl.useProgram(shader.program);
+
+ gl.uniform2f(shader.projectionVector, width/2, -height/2);
+ gl.uniform2f(shader.offsetVector, 0,0);
+
+ if(filter.uniforms.dimensions)
+ {
+ filter.uniforms.dimensions.value[0] = this.width;//width;
+ filter.uniforms.dimensions.value[1] = this.height;//height;
+ filter.uniforms.dimensions.value[2] = this.vertexArray[0];
+ filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height;
+ }
+
+ // console.log(this.uvArray )
+ shader.syncUniforms();
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+ gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
+ gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
+ gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0);
+
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
+
+ // draw the filter...
+ gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );
+
+ this.renderSession.drawCount++;
+};
+
+/**
+* Initialises the shader buffers
+* @method initShaderBuffers
+*/
+PIXI.WebGLFilterManager.prototype.initShaderBuffers = function()
+{
+ var gl = this.gl;
+
+ // create some buffers
+ this.vertexBuffer = gl.createBuffer();
+ this.uvBuffer = gl.createBuffer();
+ this.colorBuffer = gl.createBuffer();
+ this.indexBuffer = gl.createBuffer();
+
+
+ // bind and upload the vertexs..
+ // keep a reference to the vertexFloatData..
+ this.vertexArray = new Float32Array([0.0, 0.0,
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 1.0, 1.0]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ this.vertexArray,
+ gl.STATIC_DRAW);
+
+
+ // bind and upload the uv buffer
+ this.uvArray = new Float32Array([0.0, 0.0,
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 1.0, 1.0]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ this.uvArray,
+ gl.STATIC_DRAW);
+
+ this.colorArray = new Float32Array([1.0, 0xFFFFFF,
+ 1.0, 0xFFFFFF,
+ 1.0, 0xFFFFFF,
+ 1.0, 0xFFFFFF]);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ this.colorArray,
+ gl.STATIC_DRAW);
+
+ // bind and upload the index
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
+ gl.bufferData(
+ gl.ELEMENT_ARRAY_BUFFER,
+ new Uint16Array([0, 1, 2, 1, 3, 2]),
+ gl.STATIC_DRAW);
+};
+
+/**
+* Destroys the filter and removes it from the filter stack
+* @method destroy
+*/
+PIXI.WebGLFilterManager.prototype.destroy = function()
+{
+ var gl = this.gl;
+
+ this.filterStack = null;
+
+ this.offsetX = 0;
+ this.offsetY = 0;
+
+ // destroy textures
+ for (var i = 0; i < this.texturePool.length; i++) {
+ this.texturePool.destroy();
+ }
+
+ this.texturePool = null;
+
+ //destroy buffers..
+ gl.deleteBuffer(this.vertexBuffer);
+ gl.deleteBuffer(this.uvBuffer);
+ gl.deleteBuffer(this.colorBuffer);
+ gl.deleteBuffer(this.indexBuffer);
+};
+
+/**
+ * @author Mat Groves http://matgroves.com/ @Doormat23
+ */
+
+/**
+* @class FilterTexture
+* @constructor
+* @param gl {WebGLContext} the current WebGL drawing context
+* @param width {Number} the horizontal range of the filter
+* @param height {Number} the vertical range of the filter
+* @private
+*/
+PIXI.FilterTexture = function(gl, width, height)
+{
+ /**
+ * @property gl
+ * @type WebGLContext
+ */
+ this.gl = gl;
+
+ // next time to create a frame buffer and texture
+ this.frameBuffer = gl.createFramebuffer();
+ this.texture = gl.createTexture();
+
+ gl.bindTexture(gl.TEXTURE_2D, this.texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer );
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer );
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
+
+ this.resize(width, height);
+};
+
+
+/**
+* Clears the filter texture
+* @method clear
+*/
+PIXI.FilterTexture.prototype.clear = function()
+{
+ var gl = this.gl;
+
+ gl.clearColor(0,0,0, 0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+};
+
+/**
+ * Resizes the texture to the specified width and height
+ *
+ * @method resize
+ * @param width {Number} the new width of the texture
+ * @param height {Number} the new height of the texture
+ */
+PIXI.FilterTexture.prototype.resize = function(width, height)
+{
+ if(this.width === width && this.height === height) return;
+
+ this.width = width;
+ this.height = height;
+
+ var gl = this.gl;
+
+ gl.bindTexture(gl.TEXTURE_2D, this.texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+
+};
+
+/**
+* Destroys the filter texture
+* @method destroy
+*/
+PIXI.FilterTexture.prototype.destroy = function()
+{
+ var gl = this.gl;
+ gl.deleteFramebuffer( this.frameBuffer );
+ gl.deleteTexture( this.texture );
+
+ this.frameBuffer = null;
+ this.texture = null;
+};
+
+/**
+ * @author Mat Groves
+ *
+ *
+ */
+/**
+ * A set of functions used to handle masking
+ *
+ * @class CanvasMaskManager
+ */
+PIXI.CanvasMaskManager = function()
+{
+
+};
+
+/**
+ * This method adds it to the current stack of masks
+ *
+ * @method pushMask
+ * @param maskData the maskData that will be pushed
+ * @param context {Context2D} the 2d drawing method of the canvas
+ */
+PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, context)
+{
+ context.save();
+
+ var cacheAlpha = maskData.alpha;
+ var transform = maskData.worldTransform;
+
+ context.setTransform(transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty);
+
+ PIXI.CanvasGraphics.renderGraphicsMask(maskData, context);
+
+ context.clip();
+
+ maskData.worldAlpha = cacheAlpha;
+};
+
+/**
+ * Restores the current drawing context to the state it was before the mask was applied
+ *
+ * @method popMask
+ * @param context {Context2D} the 2d drawing method of the canvas
+ */
+PIXI.CanvasMaskManager.prototype.popMask = function(context)
+{
+ context.restore();
+};
+
+/**
+ * @author Mat Groves
+ *
+ *
+ */
+
+/**
+ * @class CanvasTinter
+ * @constructor
+ * @static
+ */
+PIXI.CanvasTinter = function()
+{
+ /// this.textureCach
+};
+
+//PIXI.CanvasTinter.cachTint = true;
+
+
+/**
+ * Basically this method just needs a sprite and a color and tints the sprite
+ * with the given color
+ *
+ * @method getTintedTexture
+ * @param sprite {Sprite} the sprite to tint
+ * @param color {Number} the color to use to tint the sprite with
+ */
+PIXI.CanvasTinter.getTintedTexture = function(sprite, color)
+{
+
+ var texture = sprite.texture;
+
+ color = PIXI.CanvasTinter.roundColor(color);
+
+ var stringColor = "#" + ("00000" + ( color | 0).toString(16)).substr(-6);
+
+ texture.tintCache = texture.tintCache || {};
+
+ if(texture.tintCache[stringColor]) return texture.tintCache[stringColor];
+
+ // clone texture..
+ var canvas = PIXI.CanvasTinter.canvas || document.createElement("canvas");
+
+ //PIXI.CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
+
+
+ PIXI.CanvasTinter.tintMethod(texture, color, canvas);
+
+ if(PIXI.CanvasTinter.convertTintToImage)
+ {
+ // is this better?
+ var tintImage = new Image();
+ tintImage.src = canvas.toDataURL();
+
+ texture.tintCache[stringColor] = tintImage;
+ }
+ else
+ {
+
+ texture.tintCache[stringColor] = canvas;
+ // if we are not converting the texture to an image then we need to lose the reference to the canvas
+ PIXI.CanvasTinter.canvas = null;
+
+ }
+
+ return canvas;
+};
+
+/**
+ * Tint a texture using the "multiply" operation
+ * @method tintWithMultiply
+ * @param texture {texture} the texture to tint
+ * @param color {Number} the color to use to tint the sprite with
+ * @param canvas {HTMLCanvasElement} the current canvas
+ */
+PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas)
+{
+ var context = canvas.getContext( "2d" );
+
+ var frame = texture.frame;
+
+ canvas.width = frame.width;
+ canvas.height = frame.height;
+
+ context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6);
+
+ context.fillRect(0, 0, frame.width, frame.height);
+
+ context.globalCompositeOperation = "multiply";
+
+ context.drawImage(texture.baseTexture.source,
+ frame.x,
+ frame.y,
+ frame.width,
+ frame.height,
+ 0,
+ 0,
+ frame.width,
+ frame.height);
+
+ context.globalCompositeOperation = "destination-atop";
+
+ context.drawImage(texture.baseTexture.source,
+ frame.x,
+ frame.y,
+ frame.width,
+ frame.height,
+ 0,
+ 0,
+ frame.width,
+ frame.height);
+};
+
+/**
+ * Tint a texture using the "overlay" operation
+ * @method tintWithOverlay
+ * @param texture {texture} the texture to tint
+ * @param color {Number} the color to use to tint the sprite with
+ * @param canvas {HTMLCanvasElement} the current canvas
+ */
+PIXI.CanvasTinter.tintWithOverlay = function(texture, color, canvas)
+{
+ var context = canvas.getContext( "2d" );
+
+ var frame = texture.frame;
+
+ canvas.width = frame.width;
+ canvas.height = frame.height;
+
+
+
+ context.globalCompositeOperation = "copy";
+ context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6);
+ context.fillRect(0, 0, frame.width, frame.height);
+
+ context.globalCompositeOperation = "destination-atop";
+ context.drawImage(texture.baseTexture.source,
+ frame.x,
+ frame.y,
+ frame.width,
+ frame.height,
+ 0,
+ 0,
+ frame.width,
+ frame.height);
+
+
+ //context.globalCompositeOperation = "copy";
+
+};
+
+/**
+ * Tint a texture pixel per pixel
+ * @method tintPerPixel
+ * @param texture {texture} the texture to tint
+ * @param color {Number} the color to use to tint the sprite with
+ * @param canvas {HTMLCanvasElement} the current canvas
+ */
+PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas)
+{
+ var context = canvas.getContext( "2d" );
+
+ var frame = texture.frame;
+
+ canvas.width = frame.width;
+ canvas.height = frame.height;
+
+ context.globalCompositeOperation = "copy";
+ context.drawImage(texture.baseTexture.source,
+ frame.x,
+ frame.y,
+ frame.width,
+ frame.height,
+ 0,
+ 0,
+ frame.width,
+ frame.height);
+
+ var rgbValues = PIXI.hex2rgb(color);
+ var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2];
+
+ var pixelData = context.getImageData(0, 0, frame.width, frame.height);
+
+ var pixels = pixelData.data;
+
+ for (var i = 0; i < pixels.length; i += 4)
+ {
+ pixels[i+0] *= r;
+ pixels[i+1] *= g;
+ pixels[i+2] *= b;
+ }
+
+ context.putImageData(pixelData, 0, 0);
+};
+
+/**
+ * Rounds the specified color according to the PIXI.CanvasTinter.cacheStepsPerColorChannel
+ * @method roundColor
+ * @param color {number} the color to round, should be a hex color
+ */
+PIXI.CanvasTinter.roundColor = function(color)
+{
+ var step = PIXI.CanvasTinter.cacheStepsPerColorChannel;
+
+ var rgbValues = PIXI.hex2rgb(color);
+
+ rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step);
+ rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step);
+ rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step);
+
+ return PIXI.rgb2hex(rgbValues);
+};
+
+/**
+ *
+ * Number of steps which will be used as a cap when rounding colors
+ *
+ * @property cacheStepsPerColorChannel
+ * @type Number
+ */
+PIXI.CanvasTinter.cacheStepsPerColorChannel = 8;
+/**
+ *
+ * Number of steps which will be used as a cap when rounding colors
+ *
+ * @property convertTintToImage
+ * @type Boolean
+ */
+PIXI.CanvasTinter.convertTintToImage = false;
+
+/**
+ * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method
+ *
+ * @property canUseMultiply
+ * @type Boolean
+ */
+PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes();
+
+PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel;
+
+
+/**
+ * @author Mat Groves http://matgroves.com/ @Doormat23
+ */
+
+/**
+ * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
+ * Dont forget to add the view to your DOM or you will not see anything :)
+ *
+ * @class CanvasRenderer
+ * @constructor
+ * @param width=800 {Number} the width of the canvas view
+ * @param height=600 {Number} the height of the canvas view
+ * @param [view] {HTMLCanvasElement} the canvas to use as a view, optional
+ * @param [transparent=false] {Boolean} the transparency of the render view, default false
+ */
+PIXI.CanvasRenderer = function(width, height, view, transparent)
+{
+ PIXI.defaultRenderer = PIXI.defaultRenderer || this;
+
+ this.type = PIXI.CANVAS_RENDERER;
+
+ /**
+ * This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
+ * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color.
+ * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame.
+ * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set.
+ *
+ * @property clearBeforeRender
+ * @type Boolean
+ * @default
+ */
+ this.clearBeforeRender = true;
+
+ /**
+ * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
+ * Handy for crisp pixel art and speed on legacy devices.
+ *
+ * @property roundPixels
+ * @type Boolean
+ * @default
+ */
+ this.roundPixels = false;
+
+ /**
+ * Whether the render view is transparent
+ *
+ * @property transparent
+ * @type Boolean
+ */
+ this.transparent = !!transparent;
+
+ if(!PIXI.blendModesCanvas)
+ {
+ PIXI.blendModesCanvas = [];
+
+ if(PIXI.canUseNewCanvasBlendModes())
+ {
+ PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK???
+ PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply";
+ PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "screen";
+ PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "overlay";
+ PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "darken";
+ PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "lighten";
+ PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge";
+ PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn";
+ PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light";
+ PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light";
+ PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference";
+ PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion";
+ PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "hue";
+ PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation";
+ PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "color";
+ PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity";
+ }
+ else
+ {
+ // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough"
+ PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK???
+ PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "source-over";
+ PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over";
+ }
+ }
+
+ /**
+ * The width of the canvas view
+ *
+ * @property width
+ * @type Number
+ * @default 800
+ */
+ this.width = width || 800;
+
+ /**
+ * The height of the canvas view
+ *
+ * @property height
+ * @type Number
+ * @default 600
+ */
+ this.height = height || 600;
+
+ /**
+ * The canvas element that everything is drawn to
+ *
+ * @property view
+ * @type HTMLCanvasElement
+ */
+ this.view = view || document.createElement( "canvas" );
+
+ /**
+ * The canvas 2d context that everything is drawn with
+ * @property context
+ * @type HTMLCanvasElement 2d Context
+ */
+ this.context = this.view.getContext( "2d", { alpha: this.transparent } );
+
+ this.refresh = true;
+ // hack to enable some hardware acceleration!
+ //this.view.style["transform"] = "translatez(0)";
+
+ this.view.width = this.width;
+ this.view.height = this.height;
+ this.count = 0;
+
+ /**
+ * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer
+ * @property CanvasMaskManager
+ * @type CanvasMaskManager
+ */
+ this.maskManager = new PIXI.CanvasMaskManager();
+
+ /**
+ * The render session is just a bunch of parameter used for rendering
+ * @property renderSession
+ * @type Object
+ */
+ this.renderSession = {
+ context: this.context,
+ maskManager: this.maskManager,
+ scaleMode: null,
+ smoothProperty: null
+ };
+
+ if("imageSmoothingEnabled" in this.context)
+ this.renderSession.smoothProperty = "imageSmoothingEnabled";
+ else if("webkitImageSmoothingEnabled" in this.context)
+ this.renderSession.smoothProperty = "webkitImageSmoothingEnabled";
+ else if("mozImageSmoothingEnabled" in this.context)
+ this.renderSession.smoothProperty = "mozImageSmoothingEnabled";
+ else if("oImageSmoothingEnabled" in this.context)
+ this.renderSession.smoothProperty = "oImageSmoothingEnabled";
+};
+
+// constructor
+PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer;
+
+/**
+ * Renders the stage to its canvas view
+ *
+ * @method render
+ * @param stage {Stage} the Stage element to be rendered
+ */
+PIXI.CanvasRenderer.prototype.render = function(stage)
+{
+ // update textures if need be
+ PIXI.texturesToUpdate.length = 0;
+ PIXI.texturesToDestroy.length = 0;
+
+ stage.updateTransform();
+
+ this.context.setTransform(1,0,0,1,0,0);
+ this.context.globalAlpha = 1;
+
+ if (!this.transparent && this.clearBeforeRender)
+ {
+ this.context.fillStyle = stage.backgroundColorString;
+ this.context.fillRect(0, 0, this.width, this.height);
+ }
+ else if (this.transparent && this.clearBeforeRender)
+ {
+ this.context.clearRect(0, 0, this.width, this.height);
+ }
+
+ this.renderDisplayObject(stage);
+
+ // run interaction!
+ if(stage.interactive)
+ {
+ //need to add some events!
+ if(!stage._interactiveEventsAdded)
+ {
+ stage._interactiveEventsAdded = true;
+ stage.interactionManager.setTarget(this);
+ }
+ }
+
+ // remove frame updates..
+ if(PIXI.Texture.frameUpdates.length > 0)
+ {
+ PIXI.Texture.frameUpdates.length = 0;
+ }
+};
+
+/**
+ * Resizes the canvas view to the specified width and height
+ *
+ * @method resize
+ * @param width {Number} the new width of the canvas view
+ * @param height {Number} the new height of the canvas view
+ */
+PIXI.CanvasRenderer.prototype.resize = function(width, height)
+{
+ this.width = width;
+ this.height = height;
+
+ this.view.width = width;
+ this.view.height = height;
+};
+
+/**
+ * Renders a display object
+ *
+ * @method renderDisplayObject
+ * @param displayObject {DisplayObject} The displayObject to render
+ * @param context {Context2D} the context 2d method of the canvas
+ * @private
+ */
+PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context)
+{
+ // no longer recursive!
+ //var transform;
+ //var context = this.context;
+
+ this.renderSession.context = context || this.context;
+ displayObject._renderCanvas(this.renderSession);
+};
+
+/**
+ * Renders a flat strip
+ *
+ * @method renderStripFlat
+ * @param strip {Strip} The Strip to render
+ * @private
+ */
+PIXI.CanvasRenderer.prototype.renderStripFlat = function(strip)
+{
+ var context = this.context;
+ var verticies = strip.verticies;
+
+ var length = verticies.length/2;
+ this.count++;
+
+ context.beginPath();
+ for (var i=1; i < length-2; i++)
+ {
+ // draw some triangles!
+ var index = i*2;
+
+ var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4];
+ var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];
+
+ context.moveTo(x0, y0);
+ context.lineTo(x1, y1);
+ context.lineTo(x2, y2);
+ }
+
+ context.fillStyle = "#FF0000";
+ context.fill();
+ context.closePath();
+};
+
+/**
+ * Renders a strip
+ *
+ * @method renderStrip
+ * @param strip {Strip} The Strip to render
+ * @private
+ */
+PIXI.CanvasRenderer.prototype.renderStrip = function(strip)
+{
+ var context = this.context;
+
+ // draw triangles!!
+ var verticies = strip.verticies;
+ var uvs = strip.uvs;
+
+ var length = verticies.length/2;
+ this.count++;
+
+ for (var i = 1; i < length-2; i++)
+ {
+ // draw some triangles!
+ var index = i*2;
+
+ var x0 = verticies[index], x1 = verticies[index+2], x2 = verticies[index+4];
+ var y0 = verticies[index+1], y1 = verticies[index+3], y2 = verticies[index+5];
+
+ var u0 = uvs[index] * strip.texture.width, u1 = uvs[index+2] * strip.texture.width, u2 = uvs[index+4]* strip.texture.width;
+ var v0 = uvs[index+1]* strip.texture.height, v1 = uvs[index+3] * strip.texture.height, v2 = uvs[index+5]* strip.texture.height;
+
+ context.save();
+ context.beginPath();
+ context.moveTo(x0, y0);
+ context.lineTo(x1, y1);
+ context.lineTo(x2, y2);
+ context.closePath();
+
+ context.clip();
+
+ // Compute matrix transform
+ var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2;
+ var deltaA = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2;
+ var deltaB = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2;
+ var deltaC = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2 - v0*u1*x2 - u0*x1*v2;
+ var deltaD = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2;
+ var deltaE = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2;
+ var deltaF = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2 - v0*u1*y2 - u0*y1*v2;
+
+ context.transform(deltaA / delta, deltaD / delta,
+ deltaB / delta, deltaE / delta,
+ deltaC / delta, deltaF / delta);
+
+ context.drawImage(strip.texture.baseTexture.source, 0, 0);
+ context.restore();
+ }
+};
+
+/**
+ * Creates a Canvas element of the given size
+ *
+ * @method CanvasBuffer
+ * @param width {Number} the width for the newly created canvas
+ * @param height {Number} the height for the newly created canvas
+ * @static
+ * @private
+ */
+PIXI.CanvasBuffer = function(width, height)
+{
+ this.width = width;
+ this.height = height;
+
+ this.canvas = document.createElement( "canvas" );
+ this.context = this.canvas.getContext( "2d" );
+
+ this.canvas.width = width;
+ this.canvas.height = height;
+};
+
+/**
+ * Clears the canvas that was created by the CanvasBuffer class
+ *
+ * @method clear
+ * @private
+ */
+PIXI.CanvasBuffer.prototype.clear = function()
+{
+ this.context.clearRect(0,0, this.width, this.height);
+};
+
+/**
+ * Resizes the canvas that was created by the CanvasBuffer class to the specified width and height
+ *
+ * @method resize
+ * @param width {Number} the new width of the canvas
+ * @param height {Number} the new height of the canvas
+ * @private
+ */
+
+PIXI.CanvasBuffer.prototype.resize = function(width, height)
+{
+ this.width = this.canvas.width = width;
+ this.height = this.canvas.height = height;
+};
+
+
+/**
+ * @author Mat Groves http://matgroves.com/ @Doormat23
+ */
+
+
+/**
+ * A set of functions used by the canvas renderer to draw the primitive graphics data
+ *
+ * @class CanvasGraphics
+ */
+PIXI.CanvasGraphics = function()
+{
+
+};
+
+
+/*
+ * Renders the graphics object
+ *
+ * @static
+ * @private
+ * @method renderGraphics
+ * @param graphics {Graphics} the actual graphics object to render
+ * @param context {Context2D} the 2d drawing method of the canvas
+ */
+PIXI.CanvasGraphics.renderGraphics = function(graphics, context)
+{
+ var worldAlpha = graphics.worldAlpha;
+ var color = '';
+
+ for (var i = 0; i < graphics.graphicsData.length; i++)
+ {
+ var data = graphics.graphicsData[i];
+ var points = data.points;
+
+ context.strokeStyle = color = '#' + ('00000' + ( data.lineColor | 0).toString(16)).substr(-6);
+
+ context.lineWidth = data.lineWidth;
+
+ if(data.type === PIXI.Graphics.POLY)
+ {
+ context.beginPath();
+
+ context.moveTo(points[0], points[1]);
+
+ for (var j=1; j < points.length/2; j++)
+ {
+ context.lineTo(points[j * 2], points[j * 2 + 1]);
+ }
+
+ // if the first and last point are the same close the path - much neater :)
+ if(points[0] === points[points.length-2] && points[1] === points[points.length-1])
+ {
+ context.closePath();
+ }
+
+ if(data.fill)
+ {
+ context.globalAlpha = data.fillAlpha * worldAlpha;
+ context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
+ context.fill();
+ }
+ if(data.lineWidth)
+ {
+ context.globalAlpha = data.lineAlpha * worldAlpha;
+ context.stroke();
+ }
+ }
+ else if(data.type === PIXI.Graphics.RECT)
+ {
+
+ if(data.fillColor || data.fillColor === 0)
+ {
+ context.globalAlpha = data.fillAlpha * worldAlpha;
+ context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
+ context.fillRect(points[0], points[1], points[2], points[3]);
+
+ }
+ if(data.lineWidth)
+ {
+ context.globalAlpha = data.lineAlpha * worldAlpha;
+ context.strokeRect(points[0], points[1], points[2], points[3]);
+ }
+
+ }
+ else if(data.type === PIXI.Graphics.CIRC)
+ {
+ // TODO - need to be Undefined!
+ context.beginPath();
+ context.arc(points[0], points[1], points[2],0,2*Math.PI);
+ context.closePath();
+
+ if(data.fill)
+ {
+ context.globalAlpha = data.fillAlpha * worldAlpha;
+ context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
+ context.fill();
+ }
+ if(data.lineWidth)
+ {
+ context.globalAlpha = data.lineAlpha * worldAlpha;
+ context.stroke();
+ }
+ }
+ else if(data.type === PIXI.Graphics.ELIP)
+ {
+
+ // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+
+ var ellipseData = data.points;
+
+ var w = ellipseData[2] * 2;
+ var h = ellipseData[3] * 2;
+
+ var x = ellipseData[0] - w/2;
+ var y = ellipseData[1] - h/2;
+
+ context.beginPath();
+
+ var kappa = 0.5522848,
+ ox = (w / 2) * kappa, // control point offset horizontal
+ oy = (h / 2) * kappa, // control point offset vertical
+ xe = x + w, // x-end
+ ye = y + h, // y-end
+ xm = x + w / 2, // x-middle
+ ym = y + h / 2; // y-middle
+
+ context.moveTo(x, ym);
+ context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+ context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+ context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+ context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+
+ context.closePath();
+
+ if(data.fill)
+ {
+ context.globalAlpha = data.fillAlpha * worldAlpha;
+ context.fillStyle = color = '#' + ('00000' + ( data.fillColor | 0).toString(16)).substr(-6);
+ context.fill();
+ }
+ if(data.lineWidth)
+ {
+ context.globalAlpha = data.lineAlpha * worldAlpha;
+ context.stroke();
+ }
+ }
+ }
+};
+
+/*
+ * Renders a graphics mask
+ *
+ * @static
+ * @private
+ * @method renderGraphicsMask
+ * @param graphics {Graphics} the graphics which will be used as a mask
+ * @param context {Context2D} the context 2d method of the canvas
+ */
+PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context)
+{
+ var len = graphics.graphicsData.length;
+
+ if(len === 0) return;
+
+ if(len > 1)
+ {
+ len = 1;
+ window.console.log('Pixi.js warning: masks in canvas can only mask using the first path in the graphics object');
+ }
+
+ for (var i = 0; i < 1; i++)
+ {
+ var data = graphics.graphicsData[i];
+ var points = data.points;
+
+ if(data.type === PIXI.Graphics.POLY)
+ {
+ context.beginPath();
+ context.moveTo(points[0], points[1]);
+
+ for (var j=1; j < points.length/2; j++)
+ {
+ context.lineTo(points[j * 2], points[j * 2 + 1]);
+ }
+
+ // if the first and last point are the same close the path - much neater :)
+ if(points[0] === points[points.length-2] && points[1] === points[points.length-1])
+ {
+ context.closePath();
+ }
+
+ }
+ else if(data.type === PIXI.Graphics.RECT)
+ {
+ context.beginPath();
+ context.rect(points[0], points[1], points[2], points[3]);
+ context.closePath();
+ }
+ else if(data.type === PIXI.Graphics.CIRC)
+ {
+ // TODO - need to be Undefined!
+ context.beginPath();
+ context.arc(points[0], points[1], points[2],0,2*Math.PI);
+ context.closePath();
+ }
+ else if(data.type === PIXI.Graphics.ELIP)
+ {
+
+ // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas
+ var ellipseData = data.points;
+
+ var w = ellipseData[2] * 2;
+ var h = ellipseData[3] * 2;
+
+ var x = ellipseData[0] - w/2;
+ var y = ellipseData[1] - h/2;
+
+ context.beginPath();
+
+ var kappa = 0.5522848,
+ ox = (w / 2) * kappa, // control point offset horizontal
+ oy = (h / 2) * kappa, // control point offset vertical
+ xe = x + w, // x-end
+ ye = y + h, // y-end
+ xm = x + w / 2, // x-middle
+ ym = y + h / 2; // y-middle
+
+ context.moveTo(x, ym);
+ context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
+ context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
+ context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
+ context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
+ context.closePath();
+ }
+ }
+};
+
+/**
+ * @author Mat Groves http://matgroves.com/ @Doormat23
+ */
+
+
+/**
+ * The Graphics class contains a set of methods that you can use to create primitive shapes and lines.
+ * It is important to know that with the webGL renderer only simple polygons can be filled at this stage
+ * Complex polygons will not be filled. Heres an example of a complex polygon: http://www.goodboydigital.com/wp-content/uploads/2013/06/complexPolygon.png
+ *
+ * @class Graphics
+ * @extends DisplayObjectContainer
+ * @constructor
+ */
+PIXI.Graphics = function()
+{
+ PIXI.DisplayObjectContainer.call( this );
+
+ this.renderable = true;
+
+ /**
+ * The alpha of the fill of this graphics object
+ *
+ * @property fillAlpha
+ * @type Number
+ */
+ this.fillAlpha = 1;
+
+ /**
+ * The width of any lines drawn
+ *
+ * @property lineWidth
+ * @type Number
+ */
+ this.lineWidth = 0;
+
+ /**
+ * The color of any lines drawn
+ *
+ * @property lineColor
+ * @type String
+ */
+ this.lineColor = "black";
+
+ /**
+ * Graphics data
+ *
+ * @property graphicsData
+ * @type Array
+ * @private
+ */
+ this.graphicsData = [];
+
+
+ /**
+ * The tint applied to the graphic shape. This is a hex value
+ *
+ * @property tint
+ * @type Number
+ * @default 0xFFFFFF
+ */
+ this.tint = 0xFFFFFF;// * Math.random();
+
+ /**
+ * The blend mode to be applied to the graphic shape
+ *
+ * @property blendMode
+ * @type Number
+ * @default PIXI.blendModes.NORMAL;
+ */
+ this.blendMode = PIXI.blendModes.NORMAL;
+
+ /**
+ * Current path
+ *
+ * @property currentPath
+ * @type Object
+ * @private
+ */
+ this.currentPath = {points:[]};
+
+ /**
+ * Array containing some WebGL-related properties used by the WebGL renderer
+ *
+ * @property _webGL
+ * @type Array
+ * @private
+ */
+ this._webGL = [];
+
+ /**
+ * Whether this shape is being used as a mask
+ *
+ * @property isMask
+ * @type isMask
+ */
+ this.isMask = false;
+
+ /**
+ * The bounds of the graphic shape as rectangle object
+ *
+ * @property bounds
+ * @type Rectangle
+ */
+ this.bounds = null;
+
+ /**
+ * the bounds' padding used for bounds calculation
+ *
+ * @property bounds
+ * @type Number
+ */
+ this.boundsPadding = 10;
+};
+
+// constructor
+PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype );
+PIXI.Graphics.prototype.constructor = PIXI.Graphics;
+
+/**
+ * If cacheAsBitmap is true the graphics object will then be rendered as if it was a sprite.
+ * This is useful if your graphics element does not change often as it will speed up the rendering of the object
+ * It is also usful as the graphics object will always be antialiased because it will be rendered using canvas
+ * Not recommended if you are constanly redrawing the graphics element.
+ *
+ * @property cacheAsBitmap
+ * @default false
+ * @type Boolean
+ * @private
+ */
+Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", {
+ get: function() {
+ return this._cacheAsBitmap;
+ },
+ set: function(value) {
+ this._cacheAsBitmap = value;
+
+ if(this._cacheAsBitmap)
+ {
+ this._generateCachedSprite();
+ }
+ else
+ {
+ this.destroyCachedSprite();
+ this.dirty = true;
+ }
+
+ }
+});
+
+
+/**
+ * Specifies the line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method.
+ *
+ * @method lineStyle
+ * @param lineWidth {Number} width of the line to draw, will update the object's stored style
+ * @param color {Number} color of the line to draw, will update the object's stored style
+ * @param alpha {Number} alpha of the line to draw, will update the object's stored style
+ */
+PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha)
+{
+ if (!this.currentPath.points.length) this.graphicsData.pop();
+
+ this.lineWidth = lineWidth || 0;
+ this.lineColor = color || 0;
+ this.lineAlpha = (arguments.length < 3) ? 1 : alpha;
+
+ this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
+ fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY};
+
+ this.graphicsData.push(this.currentPath);
+
+ return this;
+};
+
+/**
+ * Moves the current drawing position to (x, y).
+ *
+ * @method moveTo
+ * @param x {Number} the X coordinate to move to
+ * @param y {Number} the Y coordinate to move to
+ */
+PIXI.Graphics.prototype.moveTo = function(x, y)
+{
+ if (!this.currentPath.points.length) this.graphicsData.pop();
+
+ this.currentPath = this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
+ fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling, points:[], type:PIXI.Graphics.POLY};
+
+ this.currentPath.points.push(x, y);
+
+ this.graphicsData.push(this.currentPath);
+
+ return this;
+};
+
+/**
+ * Draws a line using the current line style from the current drawing position to (x, y);
+ * the current drawing position is then set to (x, y).
+ *
+ * @method lineTo
+ * @param x {Number} the X coordinate to draw to
+ * @param y {Number} the Y coordinate to draw to
+ */
+PIXI.Graphics.prototype.lineTo = function(x, y)
+{
+ this.currentPath.points.push(x, y);
+ this.dirty = true;
+
+ return this;
+};
+
+/**
+ * Specifies a simple one-color fill that subsequent calls to other Graphics methods
+ * (such as lineTo() or drawCircle()) use when drawing.
+ *
+ * @method beginFill
+ * @param color {Number} the color of the fill
+ * @param alpha {Number} the alpha of the fill
+ */
+PIXI.Graphics.prototype.beginFill = function(color, alpha)
+{
+
+ this.filling = true;
+ this.fillColor = color || 0;
+ this.fillAlpha = (arguments.length < 2) ? 1 : alpha;
+
+ return this;
+};
+
+/**
+ * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method.
+ *
+ * @method endFill
+ */
+PIXI.Graphics.prototype.endFill = function()
+{
+ this.filling = false;
+ this.fillColor = null;
+ this.fillAlpha = 1;
+
+ return this;
+};
+
+/**
+ * @method drawRect
+ *
+ * @param x {Number} The X coord of the top-left of the rectangle
+ * @param y {Number} The Y coord of the top-left of the rectangle
+ * @param width {Number} The width of the rectangle
+ * @param height {Number} The height of the rectangle
+ */
+PIXI.Graphics.prototype.drawRect = function( x, y, width, height )
+{
+ if (!this.currentPath.points.length) this.graphicsData.pop();
+
+ this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
+ fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling,
+ points:[x, y, width, height], type:PIXI.Graphics.RECT};
+
+ this.graphicsData.push(this.currentPath);
+ this.dirty = true;
+
+ return this;
+};
+
+/**
+ * Draws a circle.
+ *
+ * @method drawCircle
+ * @param x {Number} The X coordinate of the center of the circle
+ * @param y {Number} The Y coordinate of the center of the circle
+ * @param radius {Number} The radius of the circle
+ */
+PIXI.Graphics.prototype.drawCircle = function( x, y, radius)
+{
+
+ if (!this.currentPath.points.length) this.graphicsData.pop();
+
+ this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
+ fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling,
+ points:[x, y, radius, radius], type:PIXI.Graphics.CIRC};
+
+ this.graphicsData.push(this.currentPath);
+ this.dirty = true;
+
+ return this;
+};
+
+/**
+ * Draws an ellipse.
+ *
+ * @method drawEllipse
+ * @param x {Number} The X coordinate of the upper-left corner of the framing rectangle of this ellipse
+ * @param y {Number} The Y coordinate of the upper-left corner of the framing rectangle of this ellipse
+ * @param width {Number} The width of the ellipse
+ * @param height {Number} The height of the ellipse
+ */
+PIXI.Graphics.prototype.drawEllipse = function( x, y, width, height)
+{
+
+ if (!this.currentPath.points.length) this.graphicsData.pop();
+
+ this.currentPath = {lineWidth:this.lineWidth, lineColor:this.lineColor, lineAlpha:this.lineAlpha,
+ fillColor:this.fillColor, fillAlpha:this.fillAlpha, fill:this.filling,
+ points:[x, y, width, height], type:PIXI.Graphics.ELIP};
+
+ this.graphicsData.push(this.currentPath);
+ this.dirty = true;
+
+ return this;
+};
+
+/**
+ * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings.
+ *
+ * @method clear
+ */
+PIXI.Graphics.prototype.clear = function()
+{
+ this.lineWidth = 0;
+ this.filling = false;
+
+ this.dirty = true;
+ this.clearDirty = true;
+ this.graphicsData = [];
+
+ this.bounds = null; //new PIXI.Rectangle();
+
+ return this;
+};
+
+/**
+ * Useful function that returns a texture of the graphics object that can then be used to create sprites
+ * This can be quite useful if your geometry is complicated and needs to be reused multiple times.
+ *
+ * @method generateTexture
+ * @return {Texture} a texture of the graphics object
+ */
+PIXI.Graphics.prototype.generateTexture = function()
+{
+ var bounds = this.getBounds();
+
+ var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height);
+ var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas);
+
+ canvasBuffer.context.translate(-bounds.x,-bounds.y);
+
+ PIXI.CanvasGraphics.renderGraphics(this, canvasBuffer.context);
+
+ return texture;
+};
+
+/**
+* Renders the object using the WebGL renderer
+*
+* @method _renderWebGL
+* @param renderSession {RenderSession}
+* @private
+*/
+PIXI.Graphics.prototype._renderWebGL = function(renderSession)
+{
+ // if the sprite is not visible or the alpha is 0 then no need to render this element
+ if(this.visible === false || this.alpha === 0 || this.isMask === true)return;
+
+ if(this._cacheAsBitmap)
+ {
+
+ if(this.dirty)
+ {
+ this._generateCachedSprite();
+ // we will also need to update the texture on the gpu too!
+ PIXI.updateWebGLTexture(this._cachedSprite.texture.baseTexture, renderSession.gl);
+
+ this.dirty = false;
+ }
+
+ PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession);
+
+ return;
+ }
+ else
+ {
+ renderSession.spriteBatch.stop();
+
+ if(this._mask)renderSession.maskManager.pushMask(this.mask, renderSession);
+ if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock);
+
+ // check blend mode
+ if(this.blendMode !== renderSession.spriteBatch.currentBlendMode)
+ {
+ renderSession.spriteBatch.currentBlendMode = this.blendMode;
+ var blendModeWebGL = PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode];
+ renderSession.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]);
+ }
+
+ PIXI.WebGLGraphics.renderGraphics(this, renderSession);
+
+ // only render if it has children!
+ if(this.children.length)
+ {
+ renderSession.spriteBatch.start();
+
+ // simple render children!
+ for(var i=0, j=this.children.length; i maxX ? x1 : maxX;
+ maxX = x2 > maxX ? x2 : maxX;
+ maxX = x3 > maxX ? x3 : maxX;
+ maxX = x4 > maxX ? x4 : maxX;
+
+ maxY = y1 > maxY ? y1 : maxY;
+ maxY = y2 > maxY ? y2 : maxY;
+ maxY = y3 > maxY ? y3 : maxY;
+ maxY = y4 > maxY ? y4 : maxY;
+
+ var bounds = this._bounds;
+
+ bounds.x = minX;
+ bounds.width = maxX - minX;
+
+ bounds.y = minY;
+ bounds.height = maxY - minY;
+
+ return bounds;
+};
+
+/**
+ * Update the bounds of the object
+ *
+ * @method updateBounds
+ */
+PIXI.Graphics.prototype.updateBounds = function()
+{
+
+ var minX = Infinity;
+ var maxX = -Infinity;
+
+ var minY = Infinity;
+ var maxY = -Infinity;
+
+ var points, x, y, w, h;
+
+ for (var i = 0; i < this.graphicsData.length; i++) {
+ var data = this.graphicsData[i];
+ var type = data.type;
+ var lineWidth = data.lineWidth;
+
+ points = data.points;
+
+ if(type === PIXI.Graphics.RECT)
+ {
+ x = points[0] - lineWidth/2;
+ y = points[1] - lineWidth/2;
+ w = points[2] + lineWidth;
+ h = points[3] + lineWidth;
+
+ minX = x < minX ? x : minX;
+ maxX = x + w > maxX ? x + w : maxX;
+
+ minY = y < minY ? x : minY;
+ maxY = y + h > maxY ? y + h : maxY;
+ }
+ else if(type === PIXI.Graphics.CIRC || type === PIXI.Graphics.ELIP)
+ {
+ x = points[0];
+ y = points[1];
+ w = points[2] + lineWidth/2;
+ h = points[3] + lineWidth/2;
+
+ minX = x - w < minX ? x - w : minX;
+ maxX = x + w > maxX ? x + w : maxX;
+
+ minY = y - h < minY ? y - h : minY;
+ maxY = y + h > maxY ? y + h : maxY;
+ }
+ else
+ {
+ // POLY
+ for (var j = 0; j < points.length; j+=2)
+ {
+
+ x = points[j];
+ y = points[j+1];
+ minX = x-lineWidth < minX ? x-lineWidth : minX;
+ maxX = x+lineWidth > maxX ? x+lineWidth : maxX;
+
+ minY = y-lineWidth < minY ? y-lineWidth : minY;
+ maxY = y+lineWidth > maxY ? y+lineWidth : maxY;
+ }
+ }
+ }
+
+ var padding = this.boundsPadding;
+ this.bounds = new PIXI.Rectangle(minX - padding, minY - padding, (maxX - minX) + padding * 2, (maxY - minY) + padding * 2);
+};
+
+
+/**
+ * Generates the cached sprite when the sprite has cacheAsBitmap = true
+ *
+ * @method _generateCachedSprite
+ * @private
+ */
+PIXI.Graphics.prototype._generateCachedSprite = function()
+{
+ var bounds = this.getLocalBounds();
+
+ if(!this._cachedSprite)
+ {
+ var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height);
+ var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas);
+
+ this._cachedSprite = new PIXI.Sprite(texture);
+ this._cachedSprite.buffer = canvasBuffer;
+
+ this._cachedSprite.worldTransform = this.worldTransform;
+ }
+ else
+ {
+ this._cachedSprite.buffer.resize(bounds.width, bounds.height);
+ }
+
+ // leverage the anchor to account for the offset of the element
+ this._cachedSprite.anchor.x = -( bounds.x / bounds.width );
+ this._cachedSprite.anchor.y = -( bounds.y / bounds.height );
+
+ // this._cachedSprite.buffer.context.save();
+ this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y);
+
+ PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context);
+ // this._cachedSprite.buffer.context.restore();
+};
+
+PIXI.Graphics.prototype.destroyCachedSprite = function()
+{
+ this._cachedSprite.texture.destroy(true);
+
+ // let the gc collect the unused sprite
+ // TODO could be object pooled!
+ this._cachedSprite = null;
+};
+
+
+// SOME TYPES:
+PIXI.Graphics.POLY = 0;
+PIXI.Graphics.RECT = 1;
+PIXI.Graphics.CIRC = 2;
+PIXI.Graphics.ELIP = 3;
+
+/**
+ * @author Mat Groves http://matgroves.com/
+ */
+
+/**
+ * A tiling sprite is a fast way of rendering a tiling image
+ *
+ * @class TilingSprite
+ * @extends DisplayObjectContainer
+ * @constructor
+ * @param texture {Texture} the texture of the tiling sprite
+ * @param width {Number} the width of the tiling sprite
+ * @param height {Number} the height of the tiling sprite
+ */
+PIXI.TilingSprite = function(texture, width, height)
+{
+ PIXI.Sprite.call( this, texture);
+
+ /**
+ * The with of the tiling sprite
+ *
+ * @property width
+ * @type Number
+ */
+ this.width = width || 100;
+ /**
+ * The height of the tiling sprite
+ *
+ * @property height
+ * @type Number
+ */
+ this.height = height || 100;
+
+ /**
+ * The scaling of the image that is being tiled
+ *
+ * @property tileScale
+ * @type Point
+ */
+ this.tileScale = new PIXI.Point(1,1);
+
+ /**
+ * A point that represents the scale of the texture object
+ *
+ * @property tileScaleOffset
+ * @type Point
+ */
+ this.tileScaleOffset = new PIXI.Point(1,1);
+
+ /**
+ * The offset position of the image that is being tiled
+ *
+ * @property tilePosition
+ * @type Point
+ */
+ this.tilePosition = new PIXI.Point(0,0);
+
+
+ /**
+ * Whether this sprite is renderable or not
+ *
+ * @property renderable
+ * @type Boolean
+ * @default true
+ */
+ this.renderable = true;
+
+ /**
+ * The tint applied to the sprite. This is a hex value
+ *
+ * @property tint
+ * @type Number
+ * @default 0xFFFFFF
+ */
+ this.tint = 0xFFFFFF;
+
+ /**
+ * The blend mode to be applied to the sprite
+ *
+ * @property blendMode
+ * @type Number
+ * @default PIXI.blendModes.NORMAL;
+ */
+ this.blendMode = PIXI.blendModes.NORMAL;
+};
+
+// constructor
+PIXI.TilingSprite.prototype = Object.create(PIXI.Sprite.prototype);
+PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite;
+
+
+/**
+ * The width of the sprite, setting this will actually modify the scale to achieve the value set
+ *
+ * @property width
+ * @type Number
+ */
+Object.defineProperty(PIXI.TilingSprite.prototype, 'width', {
+ get: function() {
+ return this._width;
+ },
+ set: function(value) {
+
+ this._width = value;
+ }
+});
+
+/**
+ * The height of the TilingSprite, setting this will actually modify the scale to achieve the value set
+ *
+ * @property height
+ * @type Number
+ */
+Object.defineProperty(PIXI.TilingSprite.prototype, 'height', {
+ get: function() {
+ return this._height;
+ },
+ set: function(value) {
+ this._height = value;
+ }
+});
+
+/**
+ * When the texture is updated, this event will be fired to update the scale and frame
+ *
+ * @method onTextureUpdate
+ * @param event
+ * @private
+ */
+PIXI.TilingSprite.prototype.onTextureUpdate = function()
+{
+ this.updateFrame = true;
+};
+
+PIXI.TilingSprite.prototype.setTexture = function(texture)
+{
+ if(this.texture === texture)return;
+
+ this.texture = texture;
+
+ this.refreshTexture = true;
+ /*
+ if(this.tilingTexture)
+ {
+ this.generateTilingTexture(true);
+ }
+*/
+
+ /*
+ // stop current texture;
+ if(this.texture.baseTexture !== texture.baseTexture)
+ {
+ this.textureChange = true;
+ this.texture = texture;
+ }
+ else
+ {
+ this.texture = texture;
+ }
+
+ this.updateFrame = true;*/
+ this.cachedTint = 0xFFFFFF;
+};
+
+/**
+* Renders the object using the WebGL renderer
+*
+* @method _renderWebGL
+* @param renderSession {RenderSession}
+* @private
+*/
+PIXI.TilingSprite.prototype._renderWebGL = function(renderSession)
+{
+
+ if(this.visible === false || this.alpha === 0)return;
+
+ var i,j;
+
+ if(this.mask || this.filters)
+ {
+ if(this.mask)
+ {
+ renderSession.spriteBatch.stop();
+ renderSession.maskManager.pushMask(this.mask, renderSession);
+ renderSession.spriteBatch.start();
+ }
+
+ if(this.filters)
+ {
+ renderSession.spriteBatch.flush();
+ renderSession.filterManager.pushFilter(this._filterBlock);
+ }
+
+ if(!this.tilingTexture || this.refreshTexture)this.generateTilingTexture(true);
+ else renderSession.spriteBatch.renderTilingSprite(this);
+
+ // simple render children!
+ for(i=0,j=this.children.length; i maxX ? x1 : maxX;
+ maxX = x2 > maxX ? x2 : maxX;
+ maxX = x3 > maxX ? x3 : maxX;
+ maxX = x4 > maxX ? x4 : maxX;
+
+ maxY = y1 > maxY ? y1 : maxY;
+ maxY = y2 > maxY ? y2 : maxY;
+ maxY = y3 > maxY ? y3 : maxY;
+ maxY = y4 > maxY ? y4 : maxY;
+
+ var bounds = this._bounds;
+
+ bounds.x = minX;
+ bounds.width = maxX - minX;
+
+ bounds.y = minY;
+ bounds.height = maxY - minY;
+
+ // store a reference so that if this function gets called again in the render cycle we do not have to recalculate
+ this._currentBounds = bounds;
+
+ return bounds;
+};
+
+/**
+*
+* @method generateTilingTexture
+*
+* @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two
+*/
+PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo)
+{
+ var texture = this.texture;
+
+ if(!texture.baseTexture.hasLoaded)return;
+
+ var baseTexture = texture.baseTexture;
+ var frame = texture.frame;
+
+ var targetWidth, targetHeight;
+
+ // check that the frame is the same size as the base texture.
+ var isFrame = frame.width !== baseTexture.width || frame.height !== baseTexture.height;
+
+ var newTextureRequired = false;
+
+ if(!forcePowerOfTwo)
+ {
+ if(isFrame)
+ {
+ if (texture.trim)
+ {
+ targetWidth = texture.trim.width;
+ targetHeight = texture.trim.height;
+ }
+ else
+ {
+ targetWidth = frame.width;
+ targetHeight = frame.height;
+ }
+
+ newTextureRequired = true;
+
+ }
+ }
+ else
+ {
+ targetWidth = PIXI.getNextPowerOfTwo(frame.width);
+ targetHeight = PIXI.getNextPowerOfTwo(frame.height);
+ if(frame.width !== targetWidth && frame.height !== targetHeight)newTextureRequired = true;
+ }
+
+ if(newTextureRequired)
+ {
+ var canvasBuffer;
+
+ if(this.tilingTexture && this.tilingTexture.isTiling)
+ {
+ canvasBuffer = this.tilingTexture.canvasBuffer;
+ canvasBuffer.resize(targetWidth, targetHeight);
+ this.tilingTexture.baseTexture.width = targetWidth;
+ this.tilingTexture.baseTexture.height = targetHeight;
+ this.tilingTexture.needsUpdate = true;
+ }
+ else
+ {
+ canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight);
+
+ this.tilingTexture = PIXI.Texture.fromCanvas(canvasBuffer.canvas);
+ this.tilingTexture.canvasBuffer = canvasBuffer;
+ this.tilingTexture.isTiling = true;
+
+ }
+
+ canvasBuffer.context.drawImage(texture.baseTexture.source,
+ frame.x,
+ frame.y,
+ frame.width,
+ frame.height,
+ 0,
+ 0,
+ targetWidth,
+ targetHeight);
+
+ this.tileScaleOffset.x = frame.width / targetWidth;
+ this.tileScaleOffset.y = frame.height / targetHeight;
+
+ }
+ else
+ {
+ //TODO - switching?
+ if(this.tilingTexture && this.tilingTexture.isTiling)
+ {
+ // destroy the tiling texture!
+ // TODO could store this somewhere?
+ this.tilingTexture.destroy(true);
+ }
+
+ this.tileScaleOffset.x = 1;
+ this.tileScaleOffset.y = 1;
+ this.tilingTexture = texture;
+ }
+ this.refreshTexture = false;
+ this.tilingTexture.baseTexture._powerOf2 = true;
+};
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
@@ -7462,6 +21406,8 @@ PIXI.BaseTextureCache = {};
PIXI.texturesToUpdate = [];
PIXI.texturesToDestroy = [];
+PIXI.BaseTextureCacheIdGenerator = 0;
+
/**
* A texture stores the information that represents an image. All textures have a base texture
*
@@ -7469,6 +21415,7 @@ PIXI.texturesToDestroy = [];
* @uses EventTarget
* @constructor
* @param source {String} the source object (image or canvas)
+ * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts
*/
PIXI.BaseTexture = function(source, scaleMode)
{
@@ -7495,10 +21442,10 @@ PIXI.BaseTexture = function(source, scaleMode)
/**
* The scale mode to apply when scaling this texture
* @property scaleMode
- * @type PIXI.BaseTexture.SCALE_MODE
- * @default PIXI.BaseTexture.SCALE_MODE.LINEAR
+ * @type PIXI.scaleModes
+ * @default PIXI.scaleModes.LINEAR
*/
- this.scaleMode = scaleMode || PIXI.BaseTexture.SCALE_MODE.DEFAULT;
+ this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT;
/**
* [read-only] Describes if the base texture has loaded or not
@@ -7517,36 +21464,15 @@ PIXI.BaseTexture = function(source, scaleMode)
*/
this.source = source;
+ //TODO will be used for futer pixi 1.5...
+ this.id = PIXI.BaseTextureCacheIdGenerator++;
+
+ // used for webGL
+ this._glTextures = [];
+
if(!source)return;
- if(this.source instanceof Image || this.source instanceof HTMLImageElement)
- {
- if(this.source.complete)
- {
- this.hasLoaded = true;
- this.width = this.source.width;
- this.height = this.source.height;
-
- PIXI.texturesToUpdate.push(this);
- }
- else
- {
-
- var scope = this;
- this.source.onload = function() {
-
- scope.hasLoaded = true;
- scope.width = scope.source.width;
- scope.height = scope.source.height;
-
- // add it to somewhere...
- PIXI.texturesToUpdate.push(scope);
- scope.dispatchEvent( { type: 'loaded', content: scope } );
- };
- //this.image.src = imageUrl;
- }
- }
- else
+ if(this.source.complete || this.source.getContext)
{
this.hasLoaded = true;
this.width = this.source.width;
@@ -7554,9 +21480,27 @@ PIXI.BaseTexture = function(source, scaleMode)
PIXI.texturesToUpdate.push(this);
}
+ else
+ {
+
+ var scope = this;
+ this.source.onload = function() {
+
+ scope.hasLoaded = true;
+ scope.width = scope.source.width;
+ scope.height = scope.source.height;
+
+ // add it to somewhere...
+ PIXI.texturesToUpdate.push(scope);
+ scope.dispatchEvent( { type: 'loaded', content: scope } );
+ };
+ }
this.imageUrl = null;
this._powerOf2 = false;
+
+
+
};
PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture;
@@ -7568,10 +21512,9 @@ PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture;
*/
PIXI.BaseTexture.prototype.destroy = function()
{
- if(this.source instanceof Image)
+ if(this.imageUrl)
{
- if (this.imageUrl in PIXI.BaseTextureCache)
- delete PIXI.BaseTextureCache[this.imageUrl];
+ delete PIXI.BaseTextureCache[this.imageUrl];
this.imageUrl = null;
this.source.src = null;
}
@@ -7580,11 +21523,11 @@ PIXI.BaseTexture.prototype.destroy = function()
};
/**
+ * Changes the source image of the texture
*
- *
- * @method destroy
+ * @method updateSourceImage
+ * @param newSrc {String} the path of the image
*/
-
PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc)
{
this.hasLoaded = false;
@@ -7594,16 +21537,20 @@ PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc)
/**
* Helper function that returns a base texture based on an image url
- * If the image is not in the base texture cache it will be created and loaded
+ * If the image is not in the base texture cache it will be created and loaded
*
* @static
* @method fromImage
* @param imageUrl {String} The image url of the texture
+ * @param crossorigin {Boolean}
+ * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts
* @return BaseTexture
*/
PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode)
{
var baseTexture = PIXI.BaseTextureCache[imageUrl];
+ crossorigin = !crossorigin;
+
if(!baseTexture)
{
// new Image() breaks tex loading in some versions of Chrome.
@@ -7622,11 +21569,26 @@ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode)
return baseTexture;
};
-PIXI.BaseTexture.SCALE_MODE = {
- DEFAULT: 0, //default to LINEAR
- LINEAR: 0,
- NEAREST: 1
+PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode)
+{
+ if(!canvas._pixiId)
+ {
+ canvas._pixiId = 'canvas_' + PIXI.TextureCacheIdGenerator++;
+ }
+
+ var baseTexture = PIXI.BaseTextureCache[canvas._pixiId];
+
+ if(!baseTexture)
+ {
+ baseTexture = new PIXI.BaseTexture(canvas, scaleMode);
+ PIXI.BaseTextureCache[canvas._pixiId] = baseTexture;
+ }
+
+ return baseTexture;
};
+
+
+
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
*/
@@ -7634,6 +21596,8 @@ PIXI.BaseTexture.SCALE_MODE = {
PIXI.TextureCache = {};
PIXI.FrameCache = {};
+PIXI.TextureCacheIdGenerator = 0;
+
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
* to the display list directly. To do this use PIXI.Sprite. If no frame is provided then the whole image is used
@@ -7658,7 +21622,7 @@ PIXI.Texture = function(baseTexture, frame)
baseTexture = baseTexture.baseTexture;
/**
- * The base texture of this texture
+ * The base texture of that this texture uses
*
* @property baseTexture
* @type BaseTexture
@@ -7677,17 +21641,18 @@ PIXI.Texture = function(baseTexture, frame)
* The trim point
*
* @property trim
- * @type Point
+ * @type Rectangle
*/
- this.trim = new PIXI.Point();
-
+ this.trim = null;
+
this.scope = this;
+ this._uvs = null;
+
if(baseTexture.hasLoaded)
{
if(this.noFrame)frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height);
- //console.log(frame)
-
+
this.setFrame(frame);
}
else
@@ -7712,9 +21677,8 @@ PIXI.Texture.prototype.onBaseTextureLoaded = function()
baseTexture.removeEventListener( 'loaded', this.onLoaded );
if(this.noFrame)this.frame = new PIXI.Rectangle(0,0, baseTexture.width, baseTexture.height);
- this.noFrame = false;
- this.width = this.frame.width;
- this.height = this.frame.height;
+
+ this.setFrame(this.frame);
this.scope.dispatchEvent( { type: 'update', content: this } );
};
@@ -7750,9 +21714,32 @@ PIXI.Texture.prototype.setFrame = function(frame)
this.updateFrame = true;
PIXI.Texture.frameUpdates.push(this);
+
+
//this.dispatchEvent( { type: 'update', content: this } );
};
+PIXI.Texture.prototype._updateWebGLuvs = function()
+{
+ if(!this._uvs)this._uvs = new PIXI.TextureUvs();
+
+ var frame = this.frame;
+ var tw = this.baseTexture.width;
+ var th = this.baseTexture.height;
+
+ this._uvs.x0 = frame.x / tw;
+ this._uvs.y0 = frame.y / th;
+
+ this._uvs.x1 = (frame.x + frame.width) / tw;
+ this._uvs.y1 = frame.y / th;
+
+ this._uvs.x2 = (frame.x + frame.width) / tw;
+ this._uvs.y2 = (frame.y + frame.height) / th;
+
+ this._uvs.x3 = frame.x / tw;
+ this._uvs.y3 = (frame.y + frame.height) / th;
+};
+
/**
* Helper function that returns a texture based on an image url
* If the image is not in the texture cache it will be created and loaded
@@ -7788,7 +21775,7 @@ PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode)
PIXI.Texture.fromFrame = function(frameId)
{
var texture = PIXI.TextureCache[frameId];
- if(!texture) throw new Error('The frameId "' + frameId + '" does not exist in the texture cache ' + this);
+ if(!texture) throw new Error('The frameId "' + frameId + '" does not exist in the texture cache ');
return texture;
};
@@ -7803,8 +21790,10 @@ PIXI.Texture.fromFrame = function(frameId)
*/
PIXI.Texture.fromCanvas = function(canvas, scaleMode)
{
- var baseTexture = new PIXI.BaseTexture(canvas, scaleMode);
- return new PIXI.Texture(baseTexture);
+ var baseTexture = PIXI.BaseTexture.fromCanvas(canvas, scaleMode);
+
+ return new PIXI.Texture( baseTexture );
+
};
@@ -7832,14 +21821,31 @@ PIXI.Texture.addTextureToCache = function(texture, id)
PIXI.Texture.removeTextureFromCache = function(id)
{
var texture = PIXI.TextureCache[id];
- PIXI.TextureCache[id] = null;
+ delete PIXI.TextureCache[id];
+ delete PIXI.BaseTextureCache[id];
return texture;
};
// this is more for webGL.. it contains updated frames..
PIXI.Texture.frameUpdates = [];
-PIXI.Texture.SCALE_MODE = PIXI.BaseTexture.SCALE_MODE;
+PIXI.TextureUvs = function()
+{
+ this.x0 = 0;
+ this.y0 = 0;
+
+ this.x1 = 0;
+ this.y1 = 0;
+
+ this.x2 = 0;
+ this.y2 = 0;
+
+ this.x3 = 0;
+ this.y4 = 0;
+
+
+};
+
/**
* @author Mat Groves http://matgroves.com/ @Doormat23
@@ -7848,7 +21854,7 @@ PIXI.Texture.SCALE_MODE = PIXI.BaseTexture.SCALE_MODE;
/**
A RenderTexture is a special texture that allows any pixi displayObject to be rendered to it.
- __Hint__: All DisplayObjects (exmpl. Sprites) that renders on RenderTexture should be preloaded.
+ __Hint__: All DisplayObjects (exmpl. Sprites) that render on RenderTexture should be preloaded.
Otherwise black rectangles will be drawn instead.
RenderTexture takes snapshot of DisplayObject passed to render method. If DisplayObject is passed to render method, position and rotation of it will be ignored. For example:
@@ -7867,118 +21873,103 @@ PIXI.Texture.SCALE_MODE = PIXI.BaseTexture.SCALE_MODE;
doc.addChild(sprite);
renderTexture.render(doc); // Renders to center of renderTexture
- @class RenderTexture
- @extends Texture
- @constructor
- @param width {Number} The width of the render texture
- @param height {Number} The height of the render texture
+ * @class RenderTexture
+ * @extends Texture
+ * @constructor
+ * @param width {Number} The width of the render texture
+ * @param height {Number} The height of the render texture
*/
-PIXI.RenderTexture = function(width, height)
+PIXI.RenderTexture = function(width, height, renderer)
{
PIXI.EventTarget.call( this );
+ /**
+ * The with of the render texture
+ *
+ * @property width
+ * @type Number
+ */
this.width = width || 100;
+ /**
+ * The height of the render texture
+ *
+ * @property height
+ * @type Number
+ */
this.height = height || 100;
- this.indetityMatrix = PIXI.mat3.create();
-
+ /**
+ * The framing rectangle of the render texture
+ *
+ * @property frame
+ * @type Rectangle
+ */
this.frame = new PIXI.Rectangle(0, 0, this.width, this.height);
- if(PIXI.gl)
+ /**
+ * The base texture object that this texture uses
+ *
+ * @property baseTexture
+ * @type BaseTexture
+ */
+ this.baseTexture = new PIXI.BaseTexture();
+ this.baseTexture.width = this.width;
+ this.baseTexture.height = this.height;
+ this.baseTexture._glTextures = [];
+
+ this.baseTexture.hasLoaded = true;
+
+ // each render texture can only belong to one renderer at the moment if its webGL
+ this.renderer = renderer || PIXI.defaultRenderer;
+
+ if(this.renderer.type === PIXI.WEBGL_RENDERER)
{
- this.initWebGL();
+ var gl = this.renderer.gl;
+
+ this.textureBuffer = new PIXI.FilterTexture(gl, this.width, this.height);
+ this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture;
+
+ this.render = this.renderWebGL;
+ this.projection = new PIXI.Point(this.width/2 , -this.height/2);
}
else
{
- this.initCanvas();
+ this.render = this.renderCanvas;
+ this.textureBuffer = new PIXI.CanvasBuffer(this.width, this.height);
+ this.baseTexture.source = this.textureBuffer.canvas;
}
+
+ PIXI.Texture.frameUpdates.push(this);
+
+
};
-PIXI.RenderTexture.prototype = Object.create( PIXI.Texture.prototype );
+PIXI.RenderTexture.prototype = Object.create(PIXI.Texture.prototype);
PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture;
-/**
- * Initializes the webgl data for this texture
- *
- * @method initWebGL
- * @private
- */
-PIXI.RenderTexture.prototype.initWebGL = function()
-{
- var gl = PIXI.gl;
- this.glFramebuffer = gl.createFramebuffer();
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer );
-
- this.glFramebuffer.width = this.width;
- this.glFramebuffer.height = this.height;
-
- this.baseTexture = new PIXI.BaseTexture();
-
- this.baseTexture.width = this.width;
- this.baseTexture.height = this.height;
-
- this.baseTexture._glTexture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture);
-
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
-
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-
- this.baseTexture.isRender = true;
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer );
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.baseTexture._glTexture, 0);
-
- // create a projection matrix..
- this.projection = new PIXI.Point(this.width/2 , -this.height/2);
-
- // set the correct render function..
- this.render = this.renderWebGL;
-};
-
-
PIXI.RenderTexture.prototype.resize = function(width, height)
{
-
this.width = width;
this.height = height;
- if(PIXI.gl)
+ this.frame.width = this.width;
+ this.frame.height = this.height;
+
+ if(this.renderer.type === PIXI.WEBGL_RENDERER)
{
this.projection.x = this.width / 2;
this.projection.y = -this.height / 2;
- var gl = PIXI.gl;
- gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture);
+ var gl = this.renderer.gl;
+ gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTextures[gl.id]);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
}
else
{
-
- this.frame.width = this.width;
- this.frame.height = this.height;
- this.renderer.resize(this.width, this.height);
+ this.textureBuffer.resize(this.width, this.height);
}
-};
-/**
- * Initializes the canvas data for this texture
- *
- * @method initCanvas
- * @private
- */
-PIXI.RenderTexture.prototype.initCanvas = function()
-{
- this.renderer = new PIXI.CanvasRenderer(this.width, this.height, null, 0);
-
- this.baseTexture = new PIXI.BaseTexture(this.renderer.view);
- this.frame = new PIXI.Rectangle(0, 0, this.width, this.height);
-
- this.render = this.renderCanvas;
+ PIXI.Texture.frameUpdates.push(this);
};
/**
@@ -7991,64 +21982,43 @@ PIXI.RenderTexture.prototype.initCanvas = function()
*/
PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear)
{
- var gl = PIXI.gl;
+ //TOOD replace position with matrix..
+ var gl = this.renderer.gl;
- // enable the alpha color mask..
gl.colorMask(true, true, true, true);
gl.viewport(0, 0, this.width, this.height);
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer );
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer );
- if(clear)
- {
- gl.clearColor(0,0,0, 0);
- gl.clear(gl.COLOR_BUFFER_BIT);
- }
+ if(clear)this.textureBuffer.clear();
// THIS WILL MESS WITH HIT TESTING!
var children = displayObject.children;
//TODO -? create a new one??? dont think so!
var originalWorldTransform = displayObject.worldTransform;
- displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix;
+ displayObject.worldTransform = PIXI.RenderTexture.tempMatrix;
// modify to flip...
- displayObject.worldTransform[4] = -1;
- displayObject.worldTransform[5] = this.projection.y * -2;
+ displayObject.worldTransform.d = -1;
+ displayObject.worldTransform.ty = this.projection.y * -2;
if(position)
{
- displayObject.worldTransform[2] = position.x;
- displayObject.worldTransform[5] -= position.y;
+ displayObject.worldTransform.tx = position.x;
+ displayObject.worldTransform.ty -= position.y;
}
- PIXI.visibleCount++;
- displayObject.vcount = PIXI.visibleCount;
-
for(var i=0,j=children.length; i> 1;
- if(n < 3) return [];
-
- var tgs = [];
- var avl = [];
- for(var i = 0; i < n; i++) avl.push(i);
-
- i = 0;
- var al = n;
- while(al > 3)
- {
- var i0 = avl[(i+0)%al];
- var i1 = avl[(i+1)%al];
- var i2 = avl[(i+2)%al];
-
- var ax = p[2*i0], ay = p[2*i0+1];
- var bx = p[2*i1], by = p[2*i1+1];
- var cx = p[2*i2], cy = p[2*i2+1];
-
- var earFound = false;
- if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign))
- {
- earFound = true;
- for(var j = 0; j < al; j++)
- {
- var vi = avl[j];
- if(vi === i0 || vi === i1 || vi === i2) continue;
-
- if(PIXI.PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) {
- earFound = false;
- break;
- }
- }
- }
-
- if(earFound)
- {
- tgs.push(i0, i1, i2);
- avl.splice((i+1)%al, 1);
- al--;
- i = 0;
- }
- else if(i++ > 3*al)
- {
- // need to flip flip reverse it!
- // reset!
- if(sign)
- {
- tgs = [];
- avl = [];
- for(i = 0; i < n; i++) avl.push(i);
-
- i = 0;
- al = n;
-
- sign = false;
- }
- else
- {
- window.console.log("PIXI Warning: shape too complex to fill");
- return [];
- }
- }
- }
-
- tgs.push(avl[0], avl[1], avl[2]);
- return tgs;
-};
-
-/**
- * Checks if a point is within a triangle
- *
- * @class _PointInTriangle
- * @namespace PolyK
- * @private
- */
-PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy)
-{
- var v0x = cx-ax;
- var v0y = cy-ay;
- var v1x = bx-ax;
- var v1y = by-ay;
- var v2x = px-ax;
- var v2y = py-ay;
-
- var dot00 = v0x*v0x+v0y*v0y;
- var dot01 = v0x*v1x+v0y*v1y;
- var dot02 = v0x*v2x+v0y*v2y;
- var dot11 = v1x*v1x+v1y*v1y;
- var dot12 = v1x*v2x+v1y*v2y;
-
- var invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
- var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
- var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
-
- // Check if point is in triangle
- return (u >= 0) && (v >= 0) && (u + v < 1);
-};
-
-/**
- * Checks if a shape is convex
- *
- * @class _convex
- * @namespace PolyK
- * @private
- */
-PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign)
-{
- return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign;
-};
/**
* @author Richard Davey
@@ -8650,6 +22397,19 @@ Phaser.Camera.prototype = {
this.view.width = width;
this.view.height = height;
+ },
+
+ /**
+ * Resets the camera back to 0,0 and un-follows any object it may have been tracking.
+ *
+ * @method Phaser.Camera#reset
+ */
+ reset: function () {
+
+ this.target = null;
+ this.view.x = 0;
+ this.view.y = 0;
+
}
};
@@ -9113,11 +22873,12 @@ Phaser.StateManager.prototype = {
},
/**
- * Start the given state
+ * Start the given State. If a State is already running then State.shutDown will be called (if it exists) before switching to the new State.
+ *
* @method Phaser.StateManager#start
* @param {string} key - The key of the state you want to start.
- * @param {boolean} [clearWorld] - clear everything in the world? (Default to true)
- * @param {boolean} [clearCache] - clear asset cache? (Default to false and ONLY available when clearWorld=true)
+ * @param {boolean} [clearWorld=true] - Clear everything in the world? This clears the World display list fully (but not the Stage, so if you've added your own objects to the Stage they will need managing directly)
+ * @param {boolean} [clearCache=false] - Clear the Game.Cache? This purges out all loaded assets. The default is false and you must have clearWorld=true if you want to clearCache as well.
*/
start: function (key, clearWorld, clearCache) {
@@ -9190,7 +22951,7 @@ Phaser.StateManager.prototype = {
},
/**
- * Checks i a given phaser state is valid.
+ * Checks if a given phaser state is valid.
* State must exist and have at least one callback function registered..
* @method Phaser.StateManager#checkState
* @param {string} key - The key of the state you want to check.
@@ -9244,6 +23005,7 @@ Phaser.StateManager.prototype = {
this.states[key].load = this.game.load;
this.states[key].math = this.game.math;
this.states[key].sound = this.game.sound;
+ this.states[key].scale = this.game.scale;
this.states[key].stage = this.game.stage;
this.states[key].time = this.game.time;
this.states[key].tweens = this.game.tweens;
@@ -10101,6 +23863,12 @@ Phaser.Filter = function (game, uniforms, fragmentSrc) {
*/
this.passes = [this];
+ /**
+ * @property {array} shaders - Array an array of shaders.
+ * @private
+ */
+ this.shaders = [];
+
/**
* @property {boolean} dirty - Internal PIXI var.
* @default
@@ -10659,6 +24427,7 @@ Phaser.PluginManager.prototype.constructor = Phaser.PluginManager;
* focus handling, game resizing, scaling and the pause, boot and orientation screens.
*
* @class Phaser.Stage
+* @extends PIXI.Stage
* @constructor
* @param {Phaser.Game} game - Game reference to the currently running game.
* @param {number} width - Width of the canvas element.
@@ -10672,53 +24441,19 @@ Phaser.Stage = function (game, width, height) {
this.game = game;
/**
- * @property {string} game - Background color of the stage (defaults to black). Set via the public backgroundColor property.
- * @private
- */
- this._backgroundColor = 'rgb(0,0,0)';
-
- /**
- * @property {Phaser.Point} offset - Get the offset values (for input and other things).
+ * @property {Phaser.Point} offset - Holds the offset coordinates of the Game.canvas from the top-left of the browser window (used by Input and other classes)
*/
this.offset = new Phaser.Point();
- /**
- * @property {HTMLCanvasElement} canvas - Reference to the newly created `canvas` element.
- */
- this.canvas = null;
-
- /**
- * @property {PIXI.Stage} _stage - The Pixi Stage which is hooked to the renderer.
- * @private
- */
- this._stage = new PIXI.Stage(0x000000, false);
- this._stage.name = '_stage_root';
- this._stage.interactive = false;
+ PIXI.Stage.call(this, 0x000000, false);
/**
- * @property {PIXI.Stage} display - The Pixi Stage which is hooked to the renderer.
+ * @property {string} name - The name of this object.
+ * @default
*/
- this.display = this._stage;
+ this.name = '_stage_root';
- /**
- * @property {number} scaleMode - The current scaleMode.
- */
- this.scaleMode = Phaser.StageScaleMode.NO_SCALE;
-
- /*
- * @property {number} fullScreenScaleMode - Scale mode to be used in fullScreen
- */
- this.fullScreenScaleMode = Phaser.StageScaleMode.NO_SCALE;
-
- /**
- * @property {Phaser.StageScaleMode} scale - The scale of the current running game.
- */
- this.scale = new Phaser.StageScaleMode(this.game, width, height);
-
- /**
- * @property {number} aspectRatio - Aspect ratio.
- */
- this.aspectRatio = width / height;
+ this.interactive = false;
/**
* @property {boolean} disableVisibilityChange - By default if the browser tab loses focus the game will pause. You can stop that behaviour by setting this property to true.
@@ -10726,6 +24461,18 @@ Phaser.Stage = function (game, width, height) {
*/
this.disableVisibilityChange = false;
+ /**
+ * @property {number|false} checkOffsetInterval - The time (in ms) between which the stage should check to see if it has moved.
+ * @default
+ */
+ this.checkOffsetInterval = 2500;
+
+ /**
+ * @property {boolean} exists - If exists is true the Stage and all children are updated, otherwise it is skipped.
+ * @default
+ */
+ this.exists = true;
+
/**
* @property {number} _nextOffsetCheck - The time to run the next offset check.
* @private
@@ -10733,10 +24480,10 @@ Phaser.Stage = function (game, width, height) {
this._nextOffsetCheck = 0;
/**
- * @property {number|false} checkOffsetInterval - The time (in ms) between which the stage should check to see if it has moved.
- * @default
+ * @property {number} _backgroundColor - Stage background color.
+ * @private
*/
- this.checkOffsetInterval = 2500;
+ this._backgroundColor;
if (game.config)
{
@@ -10744,144 +24491,229 @@ Phaser.Stage = function (game, width, height) {
}
else
{
- this.canvas = Phaser.Canvas.create(width, height);
- this.canvas.style['-webkit-full-screen'] = 'width: 100%; height: 100%';
- }
-
-};
-
-Phaser.Stage.prototype = {
-
- /**
- * Parses a Game configuration object.
- *
- * @method Phaser.Stage#parseConfig
- * @protected
- */
- parseConfig: function (config) {
-
- if (config['canvasID'])
- {
- this.canvas = Phaser.Canvas.create(this.game.width, this.game.height, config['canvasID']);
- }
- else
- {
- this.canvas = Phaser.Canvas.create(this.game.width, this.game.height);
- }
-
- if (config['canvasStyle'])
- {
- this.canvas.stlye = config['canvasStyle'];
- }
- else
- {
- this.canvas.style['-webkit-full-screen'] = 'width: 100%; height: 100%';
- }
-
- if (config['checkOffsetInterval'])
- {
- this.checkOffsetInterval = config['checkOffsetInterval'];
- }
-
- if (config['disableVisibilityChange'])
- {
- this.disableVisibilityChange = config['disableVisibilityChange'];
- }
-
- if (config['fullScreenScaleMode'])
- {
- this.fullScreenScaleMode = config['fullScreenScaleMode'];
- }
-
- if (config['scaleMode'])
- {
- this.scaleMode = config['scaleMode'];
- }
-
- if (config['backgroundColor'])
- {
- this.backgroundColor = config['backgroundColor'];
- }
-
- },
-
- /**
- * Initialises the stage and adds the event listeners.
- * @method Phaser.Stage#boot
- * @private
- */
- boot: function () {
-
- Phaser.Canvas.getOffset(this.canvas, this.offset);
-
- this.bounds = new Phaser.Rectangle(this.offset.x, this.offset.y, this.game.width, this.game.height);
-
- var _this = this;
-
- this._onChange = function (event) {
- return _this.visibilityChange(event);
- }
-
- Phaser.Canvas.setUserSelect(this.canvas, 'none');
- Phaser.Canvas.setTouchAction(this.canvas, 'none');
-
- this.backgroundColor = '#000';
-
- document.addEventListener('visibilitychange', this._onChange, false);
- document.addEventListener('webkitvisibilitychange', this._onChange, false);
- document.addEventListener('pagehide', this._onChange, false);
- document.addEventListener('pageshow', this._onChange, false);
-
- window.onblur = this._onChange;
- window.onfocus = this._onChange;
-
- },
-
- /**
- * Runs Stage processes that need periodic updates, such as the offset checks.
- * @method Phaser.Stage#update
- */
- update: function () {
-
- if (this.checkOffsetInterval !== false)
- {
- if (this.game.time.now > this._nextOffsetCheck)
- {
- Phaser.Canvas.getOffset(this.canvas, this.offset);
- this._nextOffsetCheck = this.game.time.now + this.checkOffsetInterval;
- }
-
- }
-
- },
-
- /**
- * This method is called when the document visibility is changed.
- * @method Phaser.Stage#visibilityChange
- * @param {Event} event - Its type will be used to decide whether the game should be paused or not.
- */
- visibilityChange: function (event) {
-
- if (this.disableVisibilityChange)
- {
- return;
- }
-
- if (this.game.paused === false && (event.type == 'pagehide' || event.type == 'blur' || document['hidden'] === true || document['webkitHidden'] === true))
- {
- this.game.paused = true;
- }
- else
- {
- this.game.paused = false;
- }
-
+ this.game.canvas = Phaser.Canvas.create(width, height);
+ this.game.canvas.style['-webkit-full-screen'] = 'width: 100%; height: 100%';
}
};
+Phaser.Stage.prototype = Object.create(PIXI.Stage.prototype);
Phaser.Stage.prototype.constructor = Phaser.Stage;
+/**
+* This is called automatically after the plugins preUpdate and before the State.update.
+* Most objects have preUpdate methods and it's where initial movement and positioning is done.
+*
+* @method Phaser.Stage#preUpdate
+*/
+Phaser.Stage.prototype.preUpdate = function () {
+
+ this.currentRenderOrderID = 0;
+
+ var i = this.children.length;
+
+ while (i--)
+ {
+ this.children[i].preUpdate();
+ }
+
+}
+
+/**
+* This is called automatically after the State.update, but before particles or plugins update.
+*
+* @method Phaser.Stage#update
+*/
+Phaser.Stage.prototype.update = function () {
+
+ var i = this.children.length;
+
+ while (i--)
+ {
+ this.children[i].update();
+ }
+
+}
+
+/**
+* This is called automatically before the renderer runs and after the plugins have updated.
+* In postUpdate this is where all the final physics calculatations and object positioning happens.
+* The objects are processed in the order of the display list.
+* The only exception to this is if the camera is following an object, in which case that is updated first.
+*
+* @method Phaser.Stage#postUpdate
+*/
+Phaser.Stage.prototype.postUpdate = function () {
+
+ if (this.game.world.camera.target)
+ {
+ this.game.world.camera.target.postUpdate();
+
+ this.game.world.camera.update();
+
+ var i = this.children.length;
+
+ while (i--)
+ {
+ if (this.children[i] !== this.game.world.camera.target)
+ {
+ this.children[i].postUpdate();
+ }
+ }
+ }
+ else
+ {
+ this.game.world.camera.update();
+
+ var i = this.children.length;
+
+ while (i--)
+ {
+ this.children[i].postUpdate();
+ }
+ }
+
+}
+
+/**
+* Parses a Game configuration object.
+*
+* @method Phaser.Stage#parseConfig
+* @protected
+*/
+Phaser.Stage.prototype.parseConfig = function (config) {
+
+ if (config['canvasID'])
+ {
+ this.game.canvas = Phaser.Canvas.create(this.game.width, this.game.height, config['canvasID']);
+ }
+ else
+ {
+ this.game.canvas = Phaser.Canvas.create(this.game.width, this.game.height);
+ }
+
+ if (config['canvasStyle'])
+ {
+ this.game.canvas.stlye = config['canvasStyle'];
+ }
+ else
+ {
+ this.game.canvas.style['-webkit-full-screen'] = 'width: 100%; height: 100%';
+ }
+
+ if (config['checkOffsetInterval'])
+ {
+ this.checkOffsetInterval = config['checkOffsetInterval'];
+ }
+
+ if (config['disableVisibilityChange'])
+ {
+ this.disableVisibilityChange = config['disableVisibilityChange'];
+ }
+
+ if (config['fullScreenScaleMode'])
+ {
+ this.fullScreenScaleMode = config['fullScreenScaleMode'];
+ }
+
+ if (config['scaleMode'])
+ {
+ this.scaleMode = config['scaleMode'];
+ }
+
+ if (config['backgroundColor'])
+ {
+ this.backgroundColor = config['backgroundColor'];
+ }
+
+}
+
+/**
+* Initialises the stage and adds the event listeners.
+* @method Phaser.Stage#boot
+* @private
+*/
+Phaser.Stage.prototype.boot = function () {
+
+ Phaser.Canvas.getOffset(this.game.canvas, this.offset);
+
+ this.bounds = new Phaser.Rectangle(this.offset.x, this.offset.y, this.game.width, this.game.height);
+
+ var _this = this;
+
+ this._onChange = function (event) {
+ return _this.visibilityChange(event);
+ }
+
+ Phaser.Canvas.setUserSelect(this.game.canvas, 'none');
+ Phaser.Canvas.setTouchAction(this.game.canvas, 'none');
+
+ document.addEventListener('visibilitychange', this._onChange, false);
+ document.addEventListener('webkitvisibilitychange', this._onChange, false);
+ document.addEventListener('pagehide', this._onChange, false);
+ document.addEventListener('pageshow', this._onChange, false);
+
+ window.onblur = this._onChange;
+ window.onfocus = this._onChange;
+
+}
+
+/**
+* Runs Stage processes that need periodic updates, such as the offset checks.
+* @method Phaser.Stage#update
+*/
+Phaser.Stage.prototype.update = function () {
+
+ if (this.checkOffsetInterval !== false)
+ {
+ if (this.game.time.now > this._nextOffsetCheck)
+ {
+ Phaser.Canvas.getOffset(this.game.canvas, this.offset);
+ this._nextOffsetCheck = this.game.time.now + this.checkOffsetInterval;
+ }
+
+ }
+
+}
+
+/**
+* This method is called when the document visibility is changed.
+* @method Phaser.Stage#visibilityChange
+* @param {Event} event - Its type will be used to decide whether the game should be paused or not.
+*/
+Phaser.Stage.prototype.visibilityChange = function (event) {
+
+ if (this.disableVisibilityChange)
+ {
+ return;
+ }
+
+ if (this.game.paused === false && (event.type == 'pagehide' || event.type == 'blur' || document['hidden'] === true || document['webkitHidden'] === true))
+ {
+ this.game.paused = true;
+ }
+ else
+ {
+ this.game.paused = false;
+ }
+
+}
+
+/**
+* Sets the background color for the stage.
+*
+* @name Phaser.Stage#setBackgroundColor
+* @param {number} backgroundColor - The color of the background, easiest way to pass this in is in hex format like: 0xFFFFFF for white.
+*/
+Phaser.Stage.prototype.setBackgroundColor = function(backgroundColor)
+{
+ this._backgroundColor = backgroundColor || 0x000000;
+ this.backgroundColorSplit = PIXI.hex2rgb(this.backgroundColor);
+ var hex = this._backgroundColor.toString(16);
+ hex = '000000'.substr(0, 6 - hex.length) + hex;
+ this.backgroundColorString = '#' + hex;
+}
+
/**
* @name Phaser.Stage#backgroundColor
* @property {number|string} backgroundColor - Gets and sets the background color of the stage. The color can be given as a number: 0xff0000 or a hex string: '#ff0000'
@@ -10898,21 +24730,12 @@ Object.defineProperty(Phaser.Stage.prototype, "backgroundColor", {
if (this.game.transparent === false)
{
- if (this.game.renderType == Phaser.CANVAS)
+ if (typeof color === 'string')
{
- // Set it directly, this allows us to use rgb alpha values in Canvas mode.
- this.game.canvas.style.backgroundColor = color;
- }
- else
- {
- if (typeof color === 'string')
- {
- color = Phaser.Color.hexToRGB(color);
- }
-
- this._stage.setBackgroundColor(color);
+ color = Phaser.Color.hexToRGB(color);
}
+ this.setBackgroundColor(color);
}
}
@@ -10928,21 +24751,21 @@ Object.defineProperty(Phaser.Stage.prototype, "backgroundColor", {
/**
* Phaser Group constructor.
* @class Phaser.Group
-* @classdesc A Group is a container for display objects that allows for fast pooling, recycling and collision checks.
+* @classdesc A Group is a container for display objects that allows for fast pooling and object recycling. Groups can be nested within other Groups and have their own local transforms.
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
-* @param {*} parent - The parent Group or DisplayObjectContainer that will hold this group, if any. If undefined it will use game.world.
+* @param {Phaser.Group|Phaser.Sprite} parent - The parent Group, DisplayObject or DisplayObjectContainer that this Group will be added to. If undefined or null it will use game.world.
* @param {string} [name=group] - A name for this Group. Not used internally but useful for debugging.
-* @param {boolean} [useStage=false] - Should the DisplayObjectContainer this Group creates be added to the World (default, false) or direct to the Stage (true).
+* @param {boolean} [addToStage=false] - If set to true this Group will be added directly to the Game.Stage instead of Game.World.
*/
-Phaser.Group = function (game, parent, name, useStage) {
+Phaser.Group = function (game, parent, name, addToStage) {
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
- if (typeof parent === 'undefined')
+ if (typeof parent === 'undefined' || parent === null)
{
parent = game.world;
}
@@ -10952,38 +24775,23 @@ Phaser.Group = function (game, parent, name, useStage) {
*/
this.name = name || 'group';
- if (typeof useStage === 'undefined')
- {
- useStage = false;
- }
+ PIXI.DisplayObjectContainer.call(this);
- if (useStage)
+ if (typeof addToStage === 'undefined' || addToStage === false)
{
- this._container = this.game.stage._stage;
- }
- else
- {
- this._container = new PIXI.DisplayObjectContainer();
- this._container.name = this.name;
-
if (parent)
{
- if (parent instanceof Phaser.Group)
- {
- parent._container.addChild(this._container);
- }
- else
- {
- parent.addChild(this._container);
- parent.updateTransform();
- }
+ parent.addChild(this);
}
else
{
- this.game.stage._stage.addChild(this._container);
- this.game.stage._stage.updateTransform();
+ this.game.stage.addChild(this);
}
}
+ else
+ {
+ this.game.stage.addChild(this);
+ }
/**
* @property {number} type - Internal Phaser Type value.
@@ -10998,28 +24806,23 @@ Phaser.Group = function (game, parent, name, useStage) {
this.alive = true;
/**
- * @property {boolean} exists - If exists is true the the Group is updated, otherwise it is skipped.
+ * @property {boolean} exists - If exists is true the Group is updated, otherwise it is skipped.
* @default
*/
this.exists = true;
/**
- * @property {Phaser.Group} group - The parent Group of this Group, if a child of another.
+ * @property {Phaser.Group|Phaser.Sprite} parent - The parent of this Group.
*/
- this.group = null;
-
- // Replaces the PIXI.Point with a slightly more flexible one.
- this._container.scale = new Phaser.Point(1, 1);
/**
- * @property {Phaser.Point} scale - The scane of the Group container.
+ * @property {Phaser.Point} scale - The scale of the Group container.
*/
- this.scale = this._container.scale;
+ this.scale = new Phaser.Point(1, 1);
/**
* @property {Phaser.Point} pivot - The pivot point of the Group container.
*/
- this.pivot = this._container.pivot;
/**
* The cursor is a simple way to iterate through the objects in a Group using the Group.next and Group.previous functions.
@@ -11028,8 +24831,33 @@ Phaser.Group = function (game, parent, name, useStage) {
*/
this.cursor = null;
+ this._cursorIndex = 0;
+
+ /**
+ * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view.
+ */
+ this.cameraOffset = new Phaser.Point();
+
+ /**
+ * A small internal cache:
+ * 0 = previous position.x
+ * 1 = previous position.y
+ * 2 = previous rotation
+ * 3 = renderID
+ * 4 = fresh? (0 = no, 1 = yes)
+ * 5 = outOfBoundsFired (0 = no, 1 = yes)
+ * 6 = exists (0 = no, 1 = yes)
+ * 7 = fixed to camera (0 = no, 1 = yes)
+ * @property {Int16Array} _cache
+ * @private
+ */
+ this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]);
+
};
+Phaser.Group.prototype = Object.create(PIXI.DisplayObjectContainer.prototype);
+Phaser.Group.prototype.constructor = Phaser.Group;
+
/**
* @constant
* @type {number}
@@ -11060,1324 +24888,1031 @@ Phaser.Group.SORT_ASCENDING = -1;
*/
Phaser.Group.SORT_DESCENDING = 1;
-Phaser.Group.prototype = {
+/**
+* Adds an existing object to this Group. The object can be an instance of Phaser.Sprite, Phaser.Button or any other display object.
+* The child is automatically added to the top of the Group, so renders on-top of everything else within the Group. If you need to control
+* that then see the addAt method.
+*
+* @see Phaser.Group#create
+* @see Phaser.Group#addAt
+* @method Phaser.Group#add
+* @param {*} child - An instance of Phaser.Sprite, Phaser.Button or any other display object..
+* @return {*} The child that was added to the Group.
+*/
+Phaser.Group.prototype.add = function (child) {
- /**
- * Adds an existing object to this Group. The object can be an instance of Phaser.Sprite, Phaser.Button or any other display object.
- * The child is automatically added to the top of the Group, so renders on-top of everything else within the Group. If you need to control
- * that then see the addAt method.
- *
- * @see Phaser.Group#create
- * @see Phaser.Group#addAt
- * @method Phaser.Group#add
- * @param {*} child - An instance of Phaser.Sprite, Phaser.Button or any other display object..
- * @return {*} The child that was added to the Group.
- */
- add: function (child) {
-
- if (child.group !== this)
- {
- if (child.type && child.type === Phaser.GROUP)
- {
- child.group = this;
-
- this._container.addChild(child._container);
-
- child._container.updateTransform();
- }
- else
- {
- child.group = this;
-
- this._container.addChild(child);
-
- child.updateTransform();
-
- if (child.events)
- {
- child.events.onAddedToGroup.dispatch(child, this);
- }
- }
-
- if (this.cursor === null)
- {
- this.cursor = child;
- }
- }
-
- return child;
-
- },
-
- /**
- * Adds an existing object to this Group. The object can be an instance of Phaser.Sprite, Phaser.Button or any other display object.
- * The child is added to the Group at the location specified by the index value, this allows you to control child ordering.
- *
- * @method Phaser.Group#addAt
- * @param {*} child - An instance of Phaser.Sprite, Phaser.Button or any other display object..
- * @param {number} index - The index within the Group to insert the child to.
- * @return {*} The child that was added to the Group.
- */
- addAt: function (child, index) {
-
- if (child.group !== this)
- {
- if (child.type && child.type === Phaser.GROUP)
- {
- child.group = this;
-
- this._container.addChildAt(child._container, index);
-
- child._container.updateTransform();
- }
- else
- {
- child.group = this;
-
- this._container.addChildAt(child, index);
-
- child.updateTransform();
-
- if (child.events)
- {
- child.events.onAddedToGroup.dispatch(child, this);
- }
- }
-
- if (this.cursor === null)
- {
- this.cursor = child;
- }
- }
-
- return child;
-
- },
-
- /**
- * Returns the child found at the given index within this Group.
- *
- * @method Phaser.Group#getAt
- * @param {number} index - The index to return the child from.
- * @return {*} The child that was found at the given index.
- */
- getAt: function (index) {
-
- return this._container.getChildAt(index);
-
- },
-
- /**
- * Automatically creates a new Phaser.Sprite object and adds it to the top of this Group.
- * Useful if you don't need to create the Sprite instances before-hand.
- *
- * @method Phaser.Group#create
- * @param {number} x - The x coordinate to display the newly created Sprite at. The value is in relation to the Group.x point.
- * @param {number} y - The y coordinate to display the newly created Sprite at. The value is in relation to the Group.y point.
- * @param {string} key - The Game.cache key of the image that this Sprite will use.
- * @param {number|string} [frame] - If the Sprite image contains multiple frames you can specify which one to use here.
- * @param {boolean} [exists=true] - The default exists state of the Sprite.
- * @return {Phaser.Sprite} The child that was created.
- */
- create: function (x, y, key, frame, exists) {
-
- if (typeof exists === 'undefined') { exists = true; }
-
- var child = new Phaser.Sprite(this.game, x, y, key, frame);
-
- child.group = this;
- child.exists = exists;
- child.visible = exists;
- child.alive = exists;
-
- this._container.addChild(child);
-
- child.updateTransform();
+ if (child.parent !== this)
+ {
+ this.addChild(child);
if (child.events)
{
child.events.onAddedToGroup.dispatch(child, this);
}
-
- if (this.cursor === null)
- {
- this.cursor = child;
- }
-
- return child;
-
- },
-
- /**
- * Automatically creates multiple Phaser.Sprite objects and adds them to the top of this Group.
- * Useful if you need to quickly generate a pool of identical sprites, such as bullets. By default the sprites will be set to not exist
- * and will be positioned at 0, 0 (relative to the Group.x/y)
- *
- * @method Phaser.Group#createMultiple
- * @param {number} quantity - The number of Sprites to create.
- * @param {string} key - The Game.cache key of the image that this Sprite will use.
- * @param {number|string} [frame] - If the Sprite image contains multiple frames you can specify which one to use here.
- * @param {boolean} [exists=false] - The default exists state of the Sprite.
- */
- createMultiple: function (quantity, key, frame, exists) {
-
- if (typeof exists === 'undefined') { exists = false; }
-
- for (var i = 0; i < quantity; i++)
- {
- var child = new Phaser.Sprite(this.game, 0, 0, key, frame);
-
- child.group = this;
- child.exists = exists;
- child.visible = exists;
- child.alive = exists;
-
- this._container.addChild(child);
-
- child.updateTransform();
-
- if (child.events)
- {
- child.events.onAddedToGroup.dispatch(child, this);
- }
-
- if (this.cursor === null)
- {
- this.cursor = child;
- }
-
- }
-
- },
-
- /**
- * Advances the Group cursor to the next object in the Group. If it's at the end of the Group it wraps around to the first object.
- *
- * @method Phaser.Group#next
- */
- next: function () {
-
- if (this.cursor)
- {
- // Wrap the cursor?
- if (this.cursor == this._container.last)
- {
- this.cursor = this._container._iNext;
- }
- else
- {
- this.cursor = this.cursor._iNext;
- }
- }
-
- },
-
- /**
- * Moves the Group cursor to the previous object in the Group. If it's at the start of the Group it wraps around to the last object.
- *
- * @method Phaser.Group#previous
- */
- previous: function () {
-
- if (this.cursor)
- {
- // Wrap the cursor?
- if (this.cursor == this._container._iNext)
- {
- this.cursor = this._container.last;
- }
- else
- {
- this.cursor = this.cursor._iPrev;
- }
- }
-
- },
-
- /**
- * Internal test.
- *
- * @method Phaser.Group#childTest
- */
- childTest: function (prefix, child) {
-
- var s = prefix + ' next: ';
-
- if (child._iNext)
- {
- s = s + child._iNext.name;
- }
- else
- {
- s = s + '-null-';
- }
-
- s = s + ' ' + prefix + ' prev: ';
-
- if (child._iPrev)
- {
- s = s + child._iPrev.name;
- }
- else
- {
- s = s + '-null-';
- }
-
- console.log(s);
-
- },
-
- /**
- * Internal test.
- *
- * @method Phaser.Group#swapIndex
- */
- swapIndex: function (index1, index2) {
-
- var child1 = this.getAt(index1);
- var child2 = this.getAt(index2);
-
- this.swap(child1, child2);
-
- },
-
- /**
- * Swaps the position of two children in this Group. Both children must be in this Group.
- * You cannot swap a child with itself, or swap un-parented children, doing so will return false.
- *
- * @method Phaser.Group#swap
- * @param {*} child1 - The first child to swap.
- * @param {*} child2 - The second child to swap.
- * @return {boolean} True if the swap was successful, otherwise false.
- */
- swap: function (child1, child2) {
-
- if (child1 === child2 || !child1.parent || !child2.parent || child1.group !== this || child2.group !== this)
- {
- return false;
- }
-
- // Cache the values
- var child1Prev = child1._iPrev;
- var child1Next = child1._iNext;
- var child2Prev = child2._iPrev;
- var child2Next = child2._iNext;
-
- var endNode = this._container.last._iNext;
- var currentNode = this.game.stage._stage;
-
- do
- {
- if (currentNode !== child1 && currentNode !== child2)
- {
- if (currentNode.first === child1)
- {
- currentNode.first = child2;
- }
- else if (currentNode.first === child2)
- {
- currentNode.first = child1;
- }
-
- if (currentNode.last === child1)
- {
- currentNode.last = child2;
- }
- else if (currentNode.last === child2)
- {
- currentNode.last = child1;
- }
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != endNode)
-
- if (child1._iNext == child2)
- {
- // This is a downward (A to B) neighbour swap
- child1._iNext = child2Next;
- child1._iPrev = child2;
- child2._iNext = child1;
- child2._iPrev = child1Prev;
-
- if (child1Prev) { child1Prev._iNext = child2; }
- if (child2Next) { child2Next._iPrev = child1; }
-
- if (child1.__renderGroup)
- {
- child1.__renderGroup.updateTexture(child1);
- }
-
- if (child2.__renderGroup)
- {
- child2.__renderGroup.updateTexture(child2);
- }
-
- return true;
- }
- else if (child2._iNext == child1)
- {
- // This is an upward (B to A) neighbour swap
- child1._iNext = child2;
- child1._iPrev = child2Prev;
- child2._iNext = child1Next;
- child2._iPrev = child1;
-
- if (child2Prev) { child2Prev._iNext = child1; }
- if (child1Next) { child1Next._iPrev = child2; }
-
- if (child1.__renderGroup)
- {
- child1.__renderGroup.updateTexture(child1);
- }
-
- if (child2.__renderGroup)
- {
- child2.__renderGroup.updateTexture(child2);
- }
-
- return true;
- }
- else
- {
- // Children are far apart
- child1._iNext = child2Next;
- child1._iPrev = child2Prev;
- child2._iNext = child1Next;
- child2._iPrev = child1Prev;
-
- if (child1Prev) { child1Prev._iNext = child2; }
- if (child1Next) { child1Next._iPrev = child2; }
- if (child2Prev) { child2Prev._iNext = child1; }
- if (child2Next) { child2Next._iPrev = child1; }
-
- if (child1.__renderGroup)
- {
- child1.__renderGroup.updateTexture(child1);
- }
-
- if (child2.__renderGroup)
- {
- child2.__renderGroup.updateTexture(child2);
- }
-
- return true;
- }
-
- return false;
-
- },
-
- /**
- * Brings the given child to the top of this Group so it renders above all other children.
- *
- * @method Phaser.Group#bringToTop
- * @param {*} child - The child to bring to the top of this Group.
- * @return {*} The child that was moved.
- */
- bringToTop: function (child) {
-
- if (child.group === this)
- {
- this.remove(child);
- this.add(child);
- }
-
- return child;
-
- },
-
- /**
- * Get the index position of the given child in this Group.
- *
- * @method Phaser.Group#getIndex
- * @param {*} child - The child to get the index for.
- * @return {number} The index of the child or -1 if it's not a member of this Group.
- */
- getIndex: function (child) {
-
- return this._container.children.indexOf(child);
-
- },
-
- /**
- * Replaces a child of this Group with the given newChild. The newChild cannot be a member of this Group.
- *
- * @method Phaser.Group#replace
- * @param {*} oldChild - The child in this Group that will be replaced.
- * @param {*} newChild - The child to be inserted into this group.
- */
- replace: function (oldChild, newChild) {
-
- if (!this._container.first._iNext)
- {
- return;
- }
-
- var index = this.getIndex(oldChild);
-
- if (index != -1)
- {
- if (newChild.parent !== undefined)
- {
- newChild.events.onRemovedFromGroup.dispatch(newChild, this);
- newChild.parent.removeChild(newChild);
- }
-
- this._container.removeChild(oldChild);
- this._container.addChildAt(newChild, index);
-
- newChild.events.onAddedToGroup.dispatch(newChild, this);
- newChild.updateTransform();
-
- if (this.cursor == oldChild)
- {
- this.cursor = this._container._iNext;
- }
- }
-
- },
-
- /**
- * Sets the given property to the given value on the child. The operation controls the assignment of the value.
- *
- * @method Phaser.Group#setProperty
- * @param {*} child - The child to set the property value on.
- * @param {array} key - An array of strings that make up the property that will be set.
- * @param {*} value - The value that will be set.
- * @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
- */
- setProperty: function (child, key, value, operation) {
-
- operation = operation || 0;
-
- // As ugly as this approach looks, and although it's limited to a depth of only 4, it's extremely fast.
- // Much faster than a for loop or object iteration. There are no checks, so if the key isn't valid then it'll fail
- // but as you are likely to call this from inner loops that have to perform well, I'll take that trade off.
-
- // 0 = Equals
- // 1 = Add
- // 2 = Subtract
- // 3 = Multiply
- // 4 = Divide
-
- var len = key.length;
-
- if (len == 1)
- {
- if (operation === 0) { child[key[0]] = value; }
- else if (operation == 1) { child[key[0]] += value; }
- else if (operation == 2) { child[key[0]] -= value; }
- else if (operation == 3) { child[key[0]] *= value; }
- else if (operation == 4) { child[key[0]] /= value; }
- }
- else if (len == 2)
- {
- if (operation === 0) { child[key[0]][key[1]] = value; }
- else if (operation == 1) { child[key[0]][key[1]] += value; }
- else if (operation == 2) { child[key[0]][key[1]] -= value; }
- else if (operation == 3) { child[key[0]][key[1]] *= value; }
- else if (operation == 4) { child[key[0]][key[1]] /= value; }
- }
- else if (len == 3)
- {
- if (operation === 0) { child[key[0]][key[1]][key[2]] = value; }
- else if (operation == 1) { child[key[0]][key[1]][key[2]] += value; }
- else if (operation == 2) { child[key[0]][key[1]][key[2]] -= value; }
- else if (operation == 3) { child[key[0]][key[1]][key[2]] *= value; }
- else if (operation == 4) { child[key[0]][key[1]][key[2]] /= value; }
- }
- else if (len == 4)
- {
- if (operation === 0) { child[key[0]][key[1]][key[2]][key[3]] = value; }
- else if (operation == 1) { child[key[0]][key[1]][key[2]][key[3]] += value; }
- else if (operation == 2) { child[key[0]][key[1]][key[2]][key[3]] -= value; }
- else if (operation == 3) { child[key[0]][key[1]][key[2]][key[3]] *= value; }
- else if (operation == 4) { child[key[0]][key[1]][key[2]][key[3]] /= value; }
- }
-
- // TODO - Deep property scane
-
- },
-
- /**
- * This function allows you to quickly set a property on a single child of this Group to a new value.
- * The operation parameter controls how the new value is assigned to the property, from simple replacement to addition and multiplication.
- *
- * @method Phaser.Group#set
- * @param {Phaser.Sprite} child - The child to set the property on.
- * @param {string} key - The property, as a string, to be set. For example: 'body.velocity.x'
- * @param {*} value - The value that will be set.
- * @param {boolean} [checkAlive=false] - If set then the child will only be updated if alive=true.
- * @param {boolean} [checkVisible=false] - If set then the child will only be updated if visible=true.
- * @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
- */
- set: function (child, key, value, checkAlive, checkVisible, operation) {
-
- key = key.split('.');
-
- if (typeof checkAlive === 'undefined') { checkAlive = false; }
- if (typeof checkVisible === 'undefined') { checkVisible = false; }
-
- if ((checkAlive === false || (checkAlive && child.alive)) && (checkVisible === false || (checkVisible && child.visible)))
- {
- this.setProperty(child, key, value, operation);
- }
-
- },
-
- /**
- * This function allows you to quickly set the same property across all children of this Group to a new value.
- * The operation parameter controls how the new value is assigned to the property, from simple replacement to addition and multiplication.
- *
- * @method Phaser.Group#setAll
- * @param {string} key - The property, as a string, to be set. For example: 'body.velocity.x'
- * @param {*} value - The value that will be set.
- * @param {boolean} [checkAlive=false] - If set then only children with alive=true will be updated.
- * @param {boolean} [checkVisible=false] - If set then only children with visible=true will be updated.
- * @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
- */
- setAll: function (key, value, checkAlive, checkVisible, operation) {
-
- key = key.split('.');
-
- if (typeof checkAlive === 'undefined') { checkAlive = false; }
- if (typeof checkVisible === 'undefined') { checkVisible = false; }
-
- operation = operation || 0;
-
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if ((checkAlive === false || (checkAlive && currentNode.alive)) && (checkVisible === false || (checkVisible && currentNode.visible)))
- {
- this.setProperty(currentNode, key, value, operation);
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext)
- }
-
- },
-
- /**
- * Adds the amount to the given property on all children in this Group.
- * Group.addAll('x', 10) will add 10 to the child.x value.
- *
- * @method Phaser.Group#addAll
- * @param {string} property - The property to increment, for example 'body.velocity.x' or 'angle'.
- * @param {number} amount - The amount to increment the property by. If child.x = 10 then addAll('x', 40) would make child.x = 50.
- * @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
- * @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
- */
- addAll: function (property, amount, checkAlive, checkVisible) {
-
- this.setAll(property, amount, checkAlive, checkVisible, 1);
-
- },
-
- /**
- * Subtracts the amount from the given property on all children in this Group.
- * Group.subAll('x', 10) will minus 10 from the child.x value.
- *
- * @method Phaser.Group#subAll
- * @param {string} property - The property to decrement, for example 'body.velocity.x' or 'angle'.
- * @param {number} amount - The amount to subtract from the property. If child.x = 50 then subAll('x', 40) would make child.x = 10.
- * @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
- * @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
- */
- subAll: function (property, amount, checkAlive, checkVisible) {
-
- this.setAll(property, amount, checkAlive, checkVisible, 2);
-
- },
-
- /**
- * Multiplies the given property by the amount on all children in this Group.
- * Group.multiplyAll('x', 2) will x2 the child.x value.
- *
- * @method Phaser.Group#multiplyAll
- * @param {string} property - The property to multiply, for example 'body.velocity.x' or 'angle'.
- * @param {number} amount - The amount to multiply the property by. If child.x = 10 then multiplyAll('x', 2) would make child.x = 20.
- * @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
- * @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
- */
- multiplyAll: function (property, amount, checkAlive, checkVisible) {
-
- this.setAll(property, amount, checkAlive, checkVisible, 3);
-
- },
-
- /**
- * Divides the given property by the amount on all children in this Group.
- * Group.divideAll('x', 2) will half the child.x value.
- *
- * @method Phaser.Group#divideAll
- * @param {string} property - The property to divide, for example 'body.velocity.x' or 'angle'.
- * @param {number} amount - The amount to divide the property by. If child.x = 100 then divideAll('x', 2) would make child.x = 50.
- * @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
- * @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
- */
- divideAll: function (property, amount, checkAlive, checkVisible) {
-
- this.setAll(property, amount, checkAlive, checkVisible, 4);
-
- },
-
- /**
- * Calls a function on all of the children that have exists=true in this Group.
- * After the existsValue parameter you can add as many parameters as you like, which will all be passed to the child callback.
- *
- * @method Phaser.Group#callAllExists
- * @param {function} callback - The function that exists on the children that will be called.
- * @param {boolean} existsValue - Only children with exists=existsValue will be called.
- * @param {...*} parameter - Additional parameters that will be passed to the callback.
- */
- callAllExists: function (callback, existsValue) {
-
- var args = Array.prototype.splice.call(arguments, 2);
-
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (currentNode.exists == existsValue && currentNode[callback])
- {
- currentNode[callback].apply(currentNode, args);
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext)
-
- }
-
- },
-
- /**
- * Returns a reference to a function that exists on a child of the Group based on the given callback array.
- *
- * @method Phaser.Group#callbackFromArray
- * @param {object} child - The object to inspect.
- * @param {array} callback - The array of function names.
- * @param {number} length - The size of the array (pre-calculated in callAll).
- * @protected
- */
- callbackFromArray: function (child, callback, length) {
-
- // Kinda looks like a Christmas tree
-
- if (length == 1)
- {
- if (child[callback[0]])
- {
- return child[callback[0]];
- }
- }
- else if (length == 2)
- {
- if (child[callback[0]][callback[1]])
- {
- return child[callback[0]][callback[1]];
- }
- }
- else if (length == 3)
- {
- if (child[callback[0]][callback[1]][callback[2]])
- {
- return child[callback[0]][callback[1]][callback[2]];
- }
- }
- else if (length == 4)
- {
- if (child[callback[0]][callback[1]][callback[2]][callback[3]])
- {
- return child[callback[0]][callback[1]][callback[2]][callback[3]];
- }
- }
- else
- {
- if (child[callback])
- {
- return child[callback];
- }
- }
-
- return false;
-
- },
-
- /**
- * Calls a function on all of the children regardless if they are dead or alive (see callAllExists if you need control over that)
- * After the method parameter and context you can add as many extra parameters as you like, which will all be passed to the child.
- *
- * @method Phaser.Group#callAll
- * @param {string} method - A string containing the name of the function that will be called. The function must exist on the child.
- * @param {string} [context=null] - A string containing the context under which the method will be executed. Set to null to default to the child.
- * @param {...*} parameter - Additional parameters that will be passed to the method.
- */
- callAll: function (method, context) {
-
- if (typeof method === 'undefined')
- {
- return;
- }
-
- // Extract the method into an array
- method = method.split('.');
-
- var methodLength = method.length;
-
- if (typeof context === 'undefined')
- {
- context = null;
- }
- else
- {
- // Extract the context into an array
- if (typeof context === 'string')
- {
- context = context.split('.');
- var contextLength = context.length;
- }
- }
-
- var args = Array.prototype.splice.call(arguments, 2);
- var callback = null;
- var callbackContext = null;
-
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var child = this._container.first._iNext;
-
- do
- {
- callback = this.callbackFromArray(child, method, methodLength);
-
- if (context && callback)
- {
- callbackContext = this.callbackFromArray(child, context, contextLength);
-
- if (callback)
- {
- callback.apply(callbackContext, args);
- }
- }
- else if (callback)
- {
- callback.apply(child, args);
- }
-
- child = child._iNext;
- }
- while (child != this._container.last._iNext)
-
- }
-
- },
-
- /**
- * Allows you to call your own function on each member of this Group. You must pass the callback and context in which it will run.
- * After the checkExists parameter you can add as many parameters as you like, which will all be passed to the callback along with the child.
- * For example: Group.forEach(awardBonusGold, this, true, 100, 500)
- * Note: Currently this will skip any children which are Groups themselves.
- *
- * @method Phaser.Group#forEach
- * @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
- * @param {Object} callbackContext - The context in which the function should be called (usually 'this').
- * @param {boolean} checkExists - If set only children with exists=true will be passed to the callback, otherwise all children will be passed.
- */
- forEach: function (callback, callbackContext, checkExists) {
-
- if (typeof checkExists === 'undefined')
- {
- checkExists = false;
- }
-
- var args = Array.prototype.splice.call(arguments, 3);
- args.unshift(null);
-
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (checkExists === false || (checkExists && currentNode.exists))
- {
- args[0] = currentNode;
- callback.apply(callbackContext, args);
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext);
-
- }
-
- },
-
- /**
- * Allows you to call your own function on each alive member of this Group (where child.alive=true). You must pass the callback and context in which it will run.
- * You can add as many parameters as you like, which will all be passed to the callback along with the child.
- * For example: Group.forEachAlive(causeDamage, this, 500)
- *
- * @method Phaser.Group#forEachAlive
- * @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
- * @param {Object} callbackContext - The context in which the function should be called (usually 'this').
- */
- forEachExists: function (callback, callbackContext) {
-
- var args = Array.prototype.splice.call(arguments, 2);
- args.unshift(null);
-
- this.iterate('exists', true, Phaser.Group.RETURN_TOTAL, callback, callbackContext, args);
-
- },
-
- /**
- * Allows you to call your own function on each alive member of this Group (where child.alive=true). You must pass the callback and context in which it will run.
- * You can add as many parameters as you like, which will all be passed to the callback along with the child.
- * For example: Group.forEachAlive(causeDamage, this, 500)
- *
- * @method Phaser.Group#forEachAlive
- * @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
- * @param {Object} callbackContext - The context in which the function should be called (usually 'this').
- */
- forEachAlive: function (callback, callbackContext) {
-
- var args = Array.prototype.splice.call(arguments, 2);
- args.unshift(null);
-
- this.iterate('alive', true, Phaser.Group.RETURN_TOTAL, callback, callbackContext, args);
-
- },
-
- /**
- * Allows you to call your own function on each dead member of this Group (where alive=false). You must pass the callback and context in which it will run.
- * You can add as many parameters as you like, which will all be passed to the callback along with the child.
- * For example: Group.forEachDead(bringToLife, this)
- *
- * @method Phaser.Group#forEachDead
- * @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
- * @param {Object} callbackContext - The context in which the function should be called (usually 'this').
- */
- forEachDead: function (callback, callbackContext) {
-
- var args = Array.prototype.splice.call(arguments, 2);
- args.unshift(null);
-
- this.iterate('alive', false, Phaser.Group.RETURN_TOTAL, callback, callbackContext, args);
-
- },
-
- /**
- * Call this function to sort the group according to a particular value and order.
- * For example to depth sort Sprites for Zelda-style game you might call `group.sort('y', Phaser.Group.SORT_ASCENDING)` at the bottom of your `State.update()`.
- *
- * @method Phaser.Group#sort
- * @param {string} [index='y'] - The `string` name of the property you want to sort on.
- * @param {number} [order=Phaser.Group.SORT_ASCENDING] - The `Group` constant that defines the sort order. Possible values are Phaser.Group.SORT_ASCENDING and Phaser.Group.SORT_DESCENDING.
- */
- sort: function (index, order) {
-
- if (typeof index === 'undefined') { index = 'y'; }
- if (typeof order === 'undefined') { order = Phaser.Group.SORT_ASCENDING; }
-
- var swapped;
- var temp;
-
- do {
-
- swapped = false;
-
- for (var i = 0, len = this._container.children.length - 1; i < len; i++)
- {
- if (order == Phaser.Group.SORT_ASCENDING)
- {
- if (this._container.children[i][index] > this._container.children[i + 1][index])
- {
- this.swap(this.getAt(i), this.getAt(i + 1));
- temp = this._container.children[i];
- this._container.children[i] = this._container.children[i + 1];
- this._container.children[i + 1] = temp;
- swapped = true;
- }
- }
- else
- {
- if (this._container.children[i][index] < this._container.children[i + 1][index])
- {
- this.swap(this.getAt(i), this.getAt(i + 1));
- temp = this._container.children[i];
- this._container.children[i] = this._container.children[i + 1];
- this._container.children[i + 1] = temp;
- swapped = true;
- }
- }
- }
- } while (swapped);
-
- },
-
- /**
- * Iterates over the children of the Group. When a child has a property matching key that equals the given value, it is considered as a match.
- * Matched children can be sent to the optional callback, or simply returned or counted.
- * You can add as many callback parameters as you like, which will all be passed to the callback along with the child, after the callbackContext parameter.
- *
- * @method Phaser.Group#iterate
- * @param {string} key - The child property to check, i.e. 'exists', 'alive', 'health'
- * @param {any} value - If child.key === this value it will be considered a match. Note that a strict comparison is used.
- * @param {number} returnType - How to return the data from this method. Either Phaser.Group.RETURN_NONE, Phaser.Group.RETURN_TOTAL or Phaser.Group.RETURN_CHILD.
- * @param {function} [callback=null] - Optional function that will be called on each matching child. Each child of the Group will be passed to it as its first parameter.
- * @param {Object} [callbackContext] - The context in which the function should be called (usually 'this').
- * @return {any} Returns either a numeric total (if RETURN_TOTAL was specified) or the child object.
- */
- iterate: function (key, value, returnType, callback, callbackContext, args) {
-
- if (returnType === Phaser.Group.RETURN_TOTAL && this._container.children.length === 0)
- {
- return 0;
- }
-
- if (typeof callback === 'undefined')
- {
- callback = false;
- }
-
- var total = 0;
-
- if (this._container.children.length > 0 && this._container.first._iNext)
- {
- var currentNode = this._container.first._iNext;
-
- do
- {
- if (currentNode[key] === value)
- {
- total++;
-
- if (callback)
- {
- args[0] = currentNode;
- callback.apply(callbackContext, args);
- }
-
- if (returnType === Phaser.Group.RETURN_CHILD)
- {
- return currentNode;
- }
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this._container.last._iNext);
- }
-
- if (returnType === Phaser.Group.RETURN_TOTAL)
- {
- return total;
- }
- else if (returnType === Phaser.Group.RETURN_CHILD)
- {
- return null;
- }
-
- },
-
- /**
- * Call this function to retrieve the first object with exists == (the given state) in the Group.
- *
- * @method Phaser.Group#getFirstExists
- * @param {boolean} state - True or false.
- * @return {Any} The first child, or null if none found.
- */
- getFirstExists: function (state) {
-
- if (typeof state !== 'boolean')
- {
- state = true;
- }
-
- return this.iterate('exists', state, Phaser.Group.RETURN_CHILD);
-
- },
-
- /**
- * Call this function to retrieve the first object with alive === true in the group.
- * This is handy for checking if everything has been wiped out, or choosing a squad leader, etc.
- *
- * @method Phaser.Group#getFirstAlive
- * @return {Any} The first alive child, or null if none found.
- */
- getFirstAlive: function () {
-
- return this.iterate('alive', true, Phaser.Group.RETURN_CHILD);
-
- },
-
- /**
- * Call this function to retrieve the first object with alive === false in the group.
- * This is handy for checking if everything has been wiped out, or choosing a squad leader, etc.
- *
- * @method Phaser.Group#getFirstDead
- * @return {Any} The first dead child, or null if none found.
- */
- getFirstDead: function () {
-
- return this.iterate('alive', false, Phaser.Group.RETURN_CHILD);
-
- },
-
- /**
- * Call this function to find out how many members of the group are alive.
- *
- * @method Phaser.Group#countLiving
- * @return {number} The number of children flagged as alive.
- */
- countLiving: function () {
-
- return this.iterate('alive', true, Phaser.Group.RETURN_TOTAL);
-
- },
-
- /**
- * Call this function to find out how many members of the group are dead.
- *
- * @method Phaser.Group#countDead
- * @return {number} The number of children flagged as dead.
- */
- countDead: function () {
-
- return this.iterate('alive', false, Phaser.Group.RETURN_TOTAL);
-
- },
-
- /**
- * Returns a member at random from the group.
- *
- * @method Phaser.Group#getRandom
- * @param {number} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
- * @param {number} length - Optional restriction on the number of values you want to randomly select from.
- * @return {Any} A random child of this Group.
- */
- getRandom: function (startIndex, length) {
-
- if (this._container.children.length === 0)
- {
- return null;
- }
-
- startIndex = startIndex || 0;
- length = length || this._container.children.length;
-
- return this.game.math.getRandom(this._container.children, startIndex, length);
-
- },
-
- /**
- * Removes the given child from this Group and sets its group property to null.
- *
- * @method Phaser.Group#remove
- * @param {Any} child - The child to remove.
- * @return {boolean} true if the child was removed from this Group, otherwise false.
- */
- remove: function (child) {
-
- if (child.group !== this)
- {
- return false;
- }
+ }
+
+ if (this.cursor === null)
+ {
+ this.cursor = child;
+ }
+
+ return child;
+
+}
+
+/**
+* Adds an existing object to this Group. The object can be an instance of Phaser.Sprite, Phaser.Button or any other display object.
+* The child is added to the Group at the location specified by the index value, this allows you to control child ordering.
+*
+* @method Phaser.Group#addAt
+* @param {*} child - An instance of Phaser.Sprite, Phaser.Button or any other display object..
+* @param {number} index - The index within the Group to insert the child to.
+* @return {*} The child that was added to the Group.
+*/
+Phaser.Group.prototype.addAt = function (child, index) {
+
+ if (child.parent !== this)
+ {
+ this.addChildAt(child, index);
if (child.events)
{
- child.events.onRemovedFromGroup.dispatch(child, this);
+ child.events.onAddedToGroup.dispatch(child, this);
}
+ }
- // Check it's actually in the container
- if (child.parent === this._container)
+ if (this.cursor === null)
+ {
+ this.cursor = child;
+ }
+
+ return child;
+
+}
+
+/**
+* Returns the child found at the given index within this Group.
+*
+* @method Phaser.Group#getAt
+* @param {number} index - The index to return the child from.
+* @return {*} The child that was found at the given index.
+*/
+Phaser.Group.prototype.getAt = function (index) {
+
+ return this.getChildAt(index);
+
+}
+
+/**
+* Automatically creates a new Phaser.Sprite object and adds it to the top of this Group.
+* Useful if you don't need to create the Sprite instances before-hand.
+*
+* @method Phaser.Group#create
+* @param {number} x - The x coordinate to display the newly created Sprite at. The value is in relation to the Group.x point.
+* @param {number} y - The y coordinate to display the newly created Sprite at. The value is in relation to the Group.y point.
+* @param {string} key - The Game.cache key of the image that this Sprite will use.
+* @param {number|string} [frame] - If the Sprite image contains multiple frames you can specify which one to use here.
+* @param {boolean} [exists=true] - The default exists state of the Sprite.
+* @return {Phaser.Sprite} The child that was created.
+*/
+Phaser.Group.prototype.create = function (x, y, key, frame, exists) {
+
+ if (typeof exists === 'undefined') { exists = true; }
+
+ var child = new Phaser.Sprite(this.game, x, y, key, frame);
+
+ child.exists = exists;
+ child.visible = exists;
+ child.alive = exists;
+
+ this.addChild(child);
+
+ if (child.events)
+ {
+ child.events.onAddedToGroup.dispatch(child, this);
+ }
+
+ if (this.cursor === null)
+ {
+ this.cursor = child;
+ }
+
+ return child;
+
+}
+
+/**
+* Automatically creates multiple Phaser.Sprite objects and adds them to the top of this Group.
+* Useful if you need to quickly generate a pool of identical sprites, such as bullets. By default the sprites will be set to not exist
+* and will be positioned at 0, 0 (relative to the Group.x/y)
+*
+* @method Phaser.Group#createMultiple
+* @param {number} quantity - The number of Sprites to create.
+* @param {string} key - The Game.cache key of the image that this Sprite will use.
+* @param {number|string} [frame] - If the Sprite image contains multiple frames you can specify which one to use here.
+* @param {boolean} [exists=false] - The default exists state of the Sprite.
+*/
+Phaser.Group.prototype.createMultiple = function (quantity, key, frame, exists) {
+
+ if (typeof exists === 'undefined') { exists = false; }
+
+ for (var i = 0; i < quantity; i++)
+ {
+ this.create(0, 0, key, frame, exists);
+ }
+
+}
+
+/**
+* Advances the Group cursor to the next object in the Group. If it's at the end of the Group it wraps around to the first object.
+*
+* @method Phaser.Group#next
+*/
+Phaser.Group.prototype.next = function () {
+
+ if (this.cursor)
+ {
+ // Wrap the cursor?
+ if (this._cursorIndex === this.children.length)
{
- this._container.removeChild(child);
- }
-
- if (this.cursor == child)
- {
- if (this._container._iNext)
- {
- this.cursor = this._container._iNext;
- }
- else
- {
- this.cursor = null;
- }
- }
-
- child.group = null;
-
- return true;
-
- },
-
- /**
- * Removes all children from this Group, setting all group properties to null.
- * The Group container remains on the display list.
- *
- * @method Phaser.Group#removeAll
- */
- removeAll: function () {
-
- if (this._container.children.length === 0)
- {
- return;
- }
-
- do
- {
- if (this._container.children[0].events)
- {
- this._container.children[0].events.onRemovedFromGroup.dispatch(this._container.children[0], this);
- }
- this._container.removeChild(this._container.children[0]);
- }
- while (this._container.children.length > 0);
-
- this.cursor = null;
-
- },
-
- /**
- * Removes all children from this Group whos index falls beteen the given startIndex and endIndex values.
- *
- * @method Phaser.Group#removeBetween
- * @param {number} startIndex - The index to start removing children from.
- * @param {number} endIndex - The index to stop removing children from. Must be higher than startIndex and less than the length of the Group.
- */
- removeBetween: function (startIndex, endIndex) {
-
- if (this._container.children.length === 0)
- {
- return;
- }
-
- if (startIndex > endIndex || startIndex < 0 || endIndex > this._container.children.length)
- {
- return false;
- }
-
- for (var i = startIndex; i < endIndex; i++)
- {
- var child = this._container.children[i];
- child.events.onRemovedFromGroup.dispatch(child, this);
- this._container.removeChild(child);
-
- if (this.cursor == child)
- {
- if (this._container._iNext)
- {
- this.cursor = this._container._iNext;
- }
- else
- {
- this.cursor = null;
- }
- }
- }
-
- },
-
- /**
- * Destroys this Group. Removes all children, then removes the container from the display list and nulls references.
- *
- * @method Phaser.Group#destroy
- * @param {boolean} [destroyChildren=false] - Should every child of this Group have its destroy method called?
- */
- destroy: function (destroyChildren) {
-
- if (typeof destroyChildren === 'undefined') { destroyChildren = false; }
-
- if (destroyChildren)
- {
- if (this._container.children.length > 0)
- {
- do
- {
- if (this._container.children[0].group)
- {
- this._container.children[0].destroy();
- }
- }
- while (this._container.children.length > 0);
- }
+ this._cursorIndex = 0;
}
else
{
- this.removeAll();
+ this._cursorIndex++;
}
-
- this._container.parent.removeChild(this._container);
-
- this._container = null;
-
- this.game = null;
-
- this.exists = false;
-
- this.cursor = null;
-
- },
-
- validate: function () {
-
- var testObject = this.game.stage._stage.last._iNext;
- var displayObject = this.game.stage._stage;
- var nextObject = null;
- var prevObject = null;
- var count = 0;
-
- do
- {
- if (count > 0)
- {
- // check next
- if (displayObject !== nextObject)
- {
- console.log('check next fail');
- return false;
- }
-
- // check previous
- if (displayObject._iPrev !== prevObject)
- {
- console.log('check previous fail');
- return false;
- }
- }
-
- // Set the next object
- nextObject = displayObject._iNext;
- prevObject = displayObject;
-
- displayObject = displayObject._iNext;
-
- count++;
-
- }
- while(displayObject != testObject)
-
- return true;
+ this.cursor = this.children[this._cursorIndex];
}
-};
+}
-Phaser.Group.prototype.constructor = Phaser.Group;
+/**
+* Moves the Group cursor to the previous object in the Group. If it's at the start of the Group it wraps around to the last object.
+*
+* @method Phaser.Group#previous
+*/
+Phaser.Group.prototype.previous = function () {
+
+ if (this.cursor)
+ {
+ // Wrap the cursor?
+ if (this._cursorIndex === 0)
+ {
+ this._cursorIndex = this.children.length - 1;
+ }
+ else
+ {
+ this._cursorIndex--;
+ }
+
+ this.cursor = this.children[this._cursorIndex];
+ }
+
+}
+
+/**
+* Swaps the position of two children in this Group. Both children must be in this Group.
+* You cannot swap a child with itself, or swap un-parented children, doing so will return false.
+*
+* @method Phaser.Group#swap
+* @param {*} child1 - The first child to swap.
+* @param {*} child2 - The second child to swap.
+*/
+Phaser.Group.prototype.swap = function (child1, child2) {
+
+ return this.swapChildren(child1, child2);
+
+}
+
+/**
+* Brings the given child to the top of this Group so it renders above all other children.
+*
+* @method Phaser.Group#bringToTop
+* @param {*} child - The child to bring to the top of this Group.
+* @return {*} The child that was moved.
+*/
+Phaser.Group.prototype.bringToTop = function (child) {
+
+ if (child.parent === this)
+ {
+ this.remove(child);
+ this.add(child);
+ }
+
+ return child;
+
+}
+
+/**
+* Get the index position of the given child in this Group.
+*
+* @method Phaser.Group#getIndex
+* @param {*} child - The child to get the index for.
+* @return {number} The index of the child or -1 if it's not a member of this Group.
+*/
+Phaser.Group.prototype.getIndex = function (child) {
+
+ return this.children.indexOf(child);
+
+}
+
+/**
+* Replaces a child of this Group with the given newChild. The newChild cannot be a member of this Group.
+*
+* @method Phaser.Group#replace
+* @param {*} oldChild - The child in this Group that will be replaced.
+* @param {*} newChild - The child to be inserted into this group.
+*/
+Phaser.Group.prototype.replace = function (oldChild, newChild) {
+
+ var index = this.getIndex(oldChild);
+
+ if (index !== -1)
+ {
+ if (newChild.parent !== undefined)
+ {
+ newChild.events.onRemovedFromGroup.dispatch(newChild, this);
+ newChild.parent.removeChild(newChild);
+ }
+
+ this.removeChild(oldChild);
+ this.addChildAt(newChild, index);
+
+ newChild.events.onAddedToGroup.dispatch(newChild, this);
+
+ if (this.cursor === oldChild)
+ {
+ this.cursor = newChild;
+ }
+ }
+
+}
+
+/**
+* Sets the given property to the given value on the child. The operation controls the assignment of the value.
+*
+* @method Phaser.Group#setProperty
+* @param {*} child - The child to set the property value on.
+* @param {array} key - An array of strings that make up the property that will be set.
+* @param {*} value - The value that will be set.
+* @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
+*/
+Phaser.Group.prototype.setProperty = function (child, key, value, operation) {
+
+ operation = operation || 0;
+
+ // As ugly as this approach looks, and although it's limited to a depth of only 4, it's extremely fast.
+ // Much faster than a for loop or object iteration. There are no checks, so if the key isn't valid then it'll fail
+ // but as you are likely to call this from inner loops that have to perform well, I'll take that trade off.
+
+ // 0 = Equals
+ // 1 = Add
+ // 2 = Subtract
+ // 3 = Multiply
+ // 4 = Divide
+
+ var len = key.length;
+
+ if (len == 1)
+ {
+ if (operation === 0) { child[key[0]] = value; }
+ else if (operation == 1) { child[key[0]] += value; }
+ else if (operation == 2) { child[key[0]] -= value; }
+ else if (operation == 3) { child[key[0]] *= value; }
+ else if (operation == 4) { child[key[0]] /= value; }
+ }
+ else if (len == 2)
+ {
+ if (operation === 0) { child[key[0]][key[1]] = value; }
+ else if (operation == 1) { child[key[0]][key[1]] += value; }
+ else if (operation == 2) { child[key[0]][key[1]] -= value; }
+ else if (operation == 3) { child[key[0]][key[1]] *= value; }
+ else if (operation == 4) { child[key[0]][key[1]] /= value; }
+ }
+ else if (len == 3)
+ {
+ if (operation === 0) { child[key[0]][key[1]][key[2]] = value; }
+ else if (operation == 1) { child[key[0]][key[1]][key[2]] += value; }
+ else if (operation == 2) { child[key[0]][key[1]][key[2]] -= value; }
+ else if (operation == 3) { child[key[0]][key[1]][key[2]] *= value; }
+ else if (operation == 4) { child[key[0]][key[1]][key[2]] /= value; }
+ }
+ else if (len == 4)
+ {
+ if (operation === 0) { child[key[0]][key[1]][key[2]][key[3]] = value; }
+ else if (operation == 1) { child[key[0]][key[1]][key[2]][key[3]] += value; }
+ else if (operation == 2) { child[key[0]][key[1]][key[2]][key[3]] -= value; }
+ else if (operation == 3) { child[key[0]][key[1]][key[2]][key[3]] *= value; }
+ else if (operation == 4) { child[key[0]][key[1]][key[2]][key[3]] /= value; }
+ }
+
+}
+
+/**
+* This function allows you to quickly set a property on a single child of this Group to a new value.
+* The operation parameter controls how the new value is assigned to the property, from simple replacement to addition and multiplication.
+*
+* @method Phaser.Group#set
+* @param {Phaser.Sprite} child - The child to set the property on.
+* @param {string} key - The property, as a string, to be set. For example: 'body.velocity.x'
+* @param {*} value - The value that will be set.
+* @param {boolean} [checkAlive=false] - If set then the child will only be updated if alive=true.
+* @param {boolean} [checkVisible=false] - If set then the child will only be updated if visible=true.
+* @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
+*/
+Phaser.Group.prototype.set = function (child, key, value, checkAlive, checkVisible, operation) {
+
+ key = key.split('.');
+
+ if (typeof checkAlive === 'undefined') { checkAlive = false; }
+ if (typeof checkVisible === 'undefined') { checkVisible = false; }
+
+ if ((checkAlive === false || (checkAlive && child.alive)) && (checkVisible === false || (checkVisible && child.visible)))
+ {
+ this.setProperty(child, key, value, operation);
+ }
+
+}
+
+/**
+* This function allows you to quickly set the same property across all children of this Group to a new value.
+* The operation parameter controls how the new value is assigned to the property, from simple replacement to addition and multiplication.
+*
+* @method Phaser.Group#setAll
+* @param {string} key - The property, as a string, to be set. For example: 'body.velocity.x'
+* @param {*} value - The value that will be set.
+* @param {boolean} [checkAlive=false] - If set then only children with alive=true will be updated.
+* @param {boolean} [checkVisible=false] - If set then only children with visible=true will be updated.
+* @param {number} [operation=0] - Controls how the value is assigned. A value of 0 replaces the value with the new one. A value of 1 adds it, 2 subtracts it, 3 multiplies it and 4 divides it.
+*/
+Phaser.Group.prototype.setAll = function (key, value, checkAlive, checkVisible, operation) {
+
+ key = key.split('.');
+
+ if (typeof checkAlive === 'undefined') { checkAlive = false; }
+ if (typeof checkVisible === 'undefined') { checkVisible = false; }
+
+ operation = operation || 0;
+
+ for (var i = 0, len = this.children.length; i < len; i++)
+ {
+ if ((!checkAlive || (checkAlive && this.children[i].alive)) && (!checkVisible || (checkVisible && this.children[i].visible)))
+ {
+ this.setProperty(this.children[i], key, value, operation);
+ }
+ }
+
+}
+
+/**
+* Adds the amount to the given property on all children in this Group.
+* Group.addAll('x', 10) will add 10 to the child.x value.
+*
+* @method Phaser.Group#addAll
+* @param {string} property - The property to increment, for example 'body.velocity.x' or 'angle'.
+* @param {number} amount - The amount to increment the property by. If child.x = 10 then addAll('x', 40) would make child.x = 50.
+* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
+* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
+*/
+Phaser.Group.prototype.addAll = function (property, amount, checkAlive, checkVisible) {
+
+ this.setAll(property, amount, checkAlive, checkVisible, 1);
+
+}
+
+/**
+* Subtracts the amount from the given property on all children in this Group.
+* Group.subAll('x', 10) will minus 10 from the child.x value.
+*
+* @method Phaser.Group#subAll
+* @param {string} property - The property to decrement, for example 'body.velocity.x' or 'angle'.
+* @param {number} amount - The amount to subtract from the property. If child.x = 50 then subAll('x', 40) would make child.x = 10.
+* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
+* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
+*/
+Phaser.Group.prototype.subAll = function (property, amount, checkAlive, checkVisible) {
+
+ this.setAll(property, amount, checkAlive, checkVisible, 2);
+
+}
+
+/**
+* Multiplies the given property by the amount on all children in this Group.
+* Group.multiplyAll('x', 2) will x2 the child.x value.
+*
+* @method Phaser.Group#multiplyAll
+* @param {string} property - The property to multiply, for example 'body.velocity.x' or 'angle'.
+* @param {number} amount - The amount to multiply the property by. If child.x = 10 then multiplyAll('x', 2) would make child.x = 20.
+* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
+* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
+*/
+Phaser.Group.prototype.multiplyAll = function (property, amount, checkAlive, checkVisible) {
+
+ this.setAll(property, amount, checkAlive, checkVisible, 3);
+
+}
+
+/**
+* Divides the given property by the amount on all children in this Group.
+* Group.divideAll('x', 2) will half the child.x value.
+*
+* @method Phaser.Group#divideAll
+* @param {string} property - The property to divide, for example 'body.velocity.x' or 'angle'.
+* @param {number} amount - The amount to divide the property by. If child.x = 100 then divideAll('x', 2) would make child.x = 50.
+* @param {boolean} checkAlive - If true the property will only be changed if the child is alive.
+* @param {boolean} checkVisible - If true the property will only be changed if the child is visible.
+*/
+Phaser.Group.prototype.divideAll = function (property, amount, checkAlive, checkVisible) {
+
+ this.setAll(property, amount, checkAlive, checkVisible, 4);
+
+}
+
+/**
+* Calls a function on all of the children that have exists=true in this Group.
+* After the existsValue parameter you can add as many parameters as you like, which will all be passed to the child callback.
+*
+* @method Phaser.Group#callAllExists
+* @param {function} callback - The function that exists on the children that will be called.
+* @param {boolean} existsValue - Only children with exists=existsValue will be called.
+* @param {...*} parameter - Additional parameters that will be passed to the callback.
+*/
+Phaser.Group.prototype.callAllExists = function (callback, existsValue) {
+
+ var args = Array.prototype.splice.call(arguments, 2);
+
+ for (var i = 0, len = this.children.length; i < len; i++)
+ {
+ if (this.children[i].exists === existsValue && this.children[i][callback])
+ {
+ this.children[i][callback].apply(this.children[i], args);
+ }
+ }
+
+}
+
+/**
+* Returns a reference to a function that exists on a child of the Group based on the given callback array.
+*
+* @method Phaser.Group#callbackFromArray
+* @param {object} child - The object to inspect.
+* @param {array} callback - The array of function names.
+* @param {number} length - The size of the array (pre-calculated in callAll).
+* @protected
+*/
+Phaser.Group.prototype.callbackFromArray = function (child, callback, length) {
+
+ // Kinda looks like a Christmas tree
+
+ if (length == 1)
+ {
+ if (child[callback[0]])
+ {
+ return child[callback[0]];
+ }
+ }
+ else if (length == 2)
+ {
+ if (child[callback[0]][callback[1]])
+ {
+ return child[callback[0]][callback[1]];
+ }
+ }
+ else if (length == 3)
+ {
+ if (child[callback[0]][callback[1]][callback[2]])
+ {
+ return child[callback[0]][callback[1]][callback[2]];
+ }
+ }
+ else if (length == 4)
+ {
+ if (child[callback[0]][callback[1]][callback[2]][callback[3]])
+ {
+ return child[callback[0]][callback[1]][callback[2]][callback[3]];
+ }
+ }
+ else
+ {
+ if (child[callback])
+ {
+ return child[callback];
+ }
+ }
+
+ return false;
+
+}
+
+/**
+* Calls a function on all of the children regardless if they are dead or alive (see callAllExists if you need control over that)
+* After the method parameter and context you can add as many extra parameters as you like, which will all be passed to the child.
+*
+* @method Phaser.Group#callAll
+* @param {string} method - A string containing the name of the function that will be called. The function must exist on the child.
+* @param {string} [context=null] - A string containing the context under which the method will be executed. Set to null to default to the child.
+* @param {...*} parameter - Additional parameters that will be passed to the method.
+*/
+Phaser.Group.prototype.callAll = function (method, context) {
+
+ if (typeof method === 'undefined')
+ {
+ return;
+ }
+
+ // Extract the method into an array
+ method = method.split('.');
+
+ var methodLength = method.length;
+
+ if (typeof context === 'undefined')
+ {
+ context = null;
+ }
+ else
+ {
+ // Extract the context into an array
+ if (typeof context === 'string')
+ {
+ context = context.split('.');
+ var contextLength = context.length;
+ }
+ }
+
+ var args = Array.prototype.splice.call(arguments, 2);
+ var callback = null;
+ var callbackContext = null;
+
+ for (var i = 0, len = this.children.length; i < len; i++)
+ {
+ callback = this.callbackFromArray(this.children[i], method, methodLength);
+
+ if (context && callback)
+ {
+ callbackContext = this.callbackFromArray(this.children[i], context, contextLength);
+
+ if (callback)
+ {
+ callback.apply(callbackContext, args);
+ }
+ }
+ else if (callback)
+ {
+ callback.apply(this.children[i], args);
+ }
+ }
+
+}
+
+/**
+* The core preUpdate - as called by World.
+* @method Phaser.Group#preUpdate
+* @protected
+*/
+Phaser.Group.prototype.preUpdate = function () {
+
+ if (!this.exists || !this.parent.exists)
+ {
+ this.renderOrderID = -1;
+ return false;
+ }
+
+ var i = this.children.length;
+
+ while (i--)
+ {
+ this.children[i].preUpdate();
+ }
+
+ return true;
+
+}
+
+/**
+* The core update - as called by World.
+* @method Phaser.Group#update
+* @protected
+*/
+Phaser.Group.prototype.update = function () {
+
+ var i = this.children.length;
+
+ while (i--)
+ {
+ this.children[i].update();
+ }
+
+}
+
+/**
+* The core postUpdate - as called by World.
+* @method Phaser.Group#postUpdate
+* @protected
+*/
+Phaser.Group.prototype.postUpdate = function () {
+
+ // Fixed to Camera?
+ if (this._cache[7] === 1)
+ {
+ this.x = this.game.camera.view.x + this.cameraOffset.x;
+ this.y = this.game.camera.view.y + this.cameraOffset.y;
+ }
+
+ var i = this.children.length;
+
+ while (i--)
+ {
+ this.children[i].postUpdate();
+ }
+
+}
+
+/**
+* Allows you to call your own function on each member of this Group. You must pass the callback and context in which it will run.
+* After the checkExists parameter you can add as many parameters as you like, which will all be passed to the callback along with the child.
+* For example: Group.forEach(awardBonusGold, this, true, 100, 500)
+* Note: Currently this will skip any children which are Groups themselves.
+*
+* @method Phaser.Group#forEach
+* @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
+* @param {Object} callbackContext - The context in which the function should be called (usually 'this').
+* @param {boolean} checkExists - If set only children with exists=true will be passed to the callback, otherwise all children will be passed.
+*/
+Phaser.Group.prototype.forEach = function (callback, callbackContext, checkExists) {
+
+ if (typeof checkExists === 'undefined')
+ {
+ checkExists = false;
+ }
+
+ var args = Array.prototype.splice.call(arguments, 3);
+ args.unshift(null);
+
+ for (var i = 0, len = this.children.length; i < len; i++)
+ {
+ if (!checkExists || (checkExists && this.children[i].exists))
+ {
+ args[0] = this.children[i];
+ callback.apply(callbackContext, args);
+ }
+ }
+
+}
+
+/**
+* Allows you to call your own function on each alive member of this Group (where child.alive=true). You must pass the callback and context in which it will run.
+* You can add as many parameters as you like, which will all be passed to the callback along with the child.
+* For example: Group.forEachAlive(causeDamage, this, 500)
+*
+* @method Phaser.Group#forEachAlive
+* @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
+* @param {Object} callbackContext - The context in which the function should be called (usually 'this').
+*/
+Phaser.Group.prototype.forEachExists = function (callback, callbackContext) {
+
+ var args = Array.prototype.splice.call(arguments, 2);
+ args.unshift(null);
+
+ this.iterate('exists', true, Phaser.Group.RETURN_TOTAL, callback, callbackContext, args);
+
+}
+
+/**
+* Allows you to call your own function on each alive member of this Group (where child.alive=true). You must pass the callback and context in which it will run.
+* You can add as many parameters as you like, which will all be passed to the callback along with the child.
+* For example: Group.forEachAlive(causeDamage, this, 500)
+*
+* @method Phaser.Group#forEachAlive
+* @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
+* @param {Object} callbackContext - The context in which the function should be called (usually 'this').
+*/
+Phaser.Group.prototype.forEachAlive = function (callback, callbackContext) {
+
+ var args = Array.prototype.splice.call(arguments, 2);
+ args.unshift(null);
+
+ this.iterate('alive', true, Phaser.Group.RETURN_TOTAL, callback, callbackContext, args);
+
+}
+
+/**
+* Allows you to call your own function on each dead member of this Group (where alive=false). You must pass the callback and context in which it will run.
+* You can add as many parameters as you like, which will all be passed to the callback along with the child.
+* For example: Group.forEachDead(bringToLife, this)
+*
+* @method Phaser.Group#forEachDead
+* @param {function} callback - The function that will be called. Each child of the Group will be passed to it as its first parameter.
+* @param {Object} callbackContext - The context in which the function should be called (usually 'this').
+*/
+Phaser.Group.prototype.forEachDead = function (callback, callbackContext) {
+
+ var args = Array.prototype.splice.call(arguments, 2);
+ args.unshift(null);
+
+ this.iterate('alive', false, Phaser.Group.RETURN_TOTAL, callback, callbackContext, args);
+
+}
+
+/**
+* Call this function to sort the group according to a particular value and order.
+* For example to depth sort Sprites for Zelda-style game you might call `group.sort('y', Phaser.Group.SORT_ASCENDING)` at the bottom of your `State.update()`.
+*
+* @method Phaser.Group#sort
+* @param {string} [index='y'] - The `string` name of the property you want to sort on.
+* @param {number} [order=Phaser.Group.SORT_ASCENDING] - The `Group` constant that defines the sort order. Possible values are Phaser.Group.SORT_ASCENDING and Phaser.Group.SORT_DESCENDING.
+*/
+Phaser.Group.prototype.sort = function (index, order) {
+
+ if (typeof index === 'undefined') { index = 'y'; }
+ if (typeof order === 'undefined') { order = Phaser.Group.SORT_ASCENDING; }
+
+
+}
+
+Phaser.Group.prototype.sortHandler = function (a, b) {
+
+}
+
+/**
+* Iterates over the children of the Group. When a child has a property matching key that equals the given value, it is considered as a match.
+* Matched children can be sent to the optional callback, or simply returned or counted.
+* You can add as many callback parameters as you like, which will all be passed to the callback along with the child, after the callbackContext parameter.
+*
+* @method Phaser.Group#iterate
+* @param {string} key - The child property to check, i.e. 'exists', 'alive', 'health'
+* @param {any} value - If child.key === this value it will be considered a match. Note that a strict comparison is used.
+* @param {number} returnType - How to return the data from this method. Either Phaser.Group.RETURN_NONE, Phaser.Group.RETURN_TOTAL or Phaser.Group.RETURN_CHILD.
+* @param {function} [callback=null] - Optional function that will be called on each matching child. Each child of the Group will be passed to it as its first parameter.
+* @param {Object} [callbackContext] - The context in which the function should be called (usually 'this').
+* @return {any} Returns either a numeric total (if RETURN_TOTAL was specified) or the child object.
+*/
+Phaser.Group.prototype.iterate = function (key, value, returnType, callback, callbackContext, args) {
+
+ if (returnType === Phaser.Group.RETURN_TOTAL && this.children.length === 0)
+ {
+ return 0;
+ }
+
+ if (typeof callback === 'undefined')
+ {
+ callback = false;
+ }
+
+ var total = 0;
+
+ for (var i = 0, len = this.children.length; i < len; i++)
+ {
+ if (this.children[i][key] === value)
+ {
+ total++;
+
+ if (callback)
+ {
+ args[0] = this.children[i];
+ callback.apply(callbackContext, args);
+ }
+
+ if (returnType === Phaser.Group.RETURN_CHILD)
+ {
+ return this.children[i];
+ }
+ }
+ }
+
+ if (returnType === Phaser.Group.RETURN_TOTAL)
+ {
+ return total;
+ }
+ else if (returnType === Phaser.Group.RETURN_CHILD)
+ {
+ return null;
+ }
+
+}
+
+/**
+* Call this function to retrieve the first object with exists == (the given state) in the Group.
+*
+* @method Phaser.Group#getFirstExists
+* @param {boolean} state - True or false.
+* @return {Any} The first child, or null if none found.
+*/
+Phaser.Group.prototype.getFirstExists = function (state) {
+
+ if (typeof state !== 'boolean')
+ {
+ state = true;
+ }
+
+ return this.iterate('exists', state, Phaser.Group.RETURN_CHILD);
+
+}
+
+/**
+* Call this function to retrieve the first object with alive === true in the group.
+* This is handy for checking if everything has been wiped out, or choosing a squad leader, etc.
+*
+* @method Phaser.Group#getFirstAlive
+* @return {Any} The first alive child, or null if none found.
+*/
+Phaser.Group.prototype.getFirstAlive = function () {
+
+ return this.iterate('alive', true, Phaser.Group.RETURN_CHILD);
+
+}
+
+/**
+* Call this function to retrieve the first object with alive === false in the group.
+* This is handy for checking if everything has been wiped out, or choosing a squad leader, etc.
+*
+* @method Phaser.Group#getFirstDead
+* @return {Any} The first dead child, or null if none found.
+*/
+Phaser.Group.prototype.getFirstDead = function () {
+
+ return this.iterate('alive', false, Phaser.Group.RETURN_CHILD);
+
+}
+
+/**
+* Call this function to find out how many members of the group are alive.
+*
+* @method Phaser.Group#countLiving
+* @return {number} The number of children flagged as alive.
+*/
+Phaser.Group.prototype.countLiving = function () {
+
+ return this.iterate('alive', true, Phaser.Group.RETURN_TOTAL);
+
+}
+
+/**
+* Call this function to find out how many members of the group are dead.
+*
+* @method Phaser.Group#countDead
+* @return {number} The number of children flagged as dead.
+*/
+Phaser.Group.prototype.countDead = function () {
+
+ return this.iterate('alive', false, Phaser.Group.RETURN_TOTAL);
+
+}
+
+/**
+* Returns a member at random from the group.
+*
+* @method Phaser.Group#getRandom
+* @param {number} startIndex - Optional offset off the front of the array. Default value is 0, or the beginning of the array.
+* @param {number} length - Optional restriction on the number of values you want to randomly select from.
+* @return {Any} A random child of this Group.
+*/
+Phaser.Group.prototype.getRandom = function (startIndex, length) {
+
+ if (this.children.length === 0)
+ {
+ return null;
+ }
+
+ startIndex = startIndex || 0;
+ length = length || this.children.length;
+
+ return this.game.math.getRandom(this.children, startIndex, length);
+
+}
+
+/**
+* Removes the given child from this Group and sets its group property to null.
+*
+* @method Phaser.Group#remove
+* @param {Any} child - The child to remove.
+* @return {boolean} true if the child was removed from this Group, otherwise false.
+*/
+Phaser.Group.prototype.remove = function (child) {
+
+ if (this.children.length === 0)
+ {
+ return;
+ }
+
+ if (child.events)
+ {
+ child.events.onRemovedFromGroup.dispatch(child, this);
+ }
+
+ this.removeChild(child);
+
+ if (this.cursor === child)
+ {
+ this.next();
+ }
+
+ return true;
+
+}
+
+/**
+* Removes all children from this Group, setting all group properties to null.
+* The Group container remains on the display list.
+*
+* @method Phaser.Group#removeAll
+*/
+Phaser.Group.prototype.removeAll = function () {
+
+ if (this.children.length === 0)
+ {
+ return;
+ }
+
+ do
+ {
+ if (this.children[0].events)
+ {
+ this.children[0].events.onRemovedFromGroup.dispatch(this.children[0], this);
+ }
+
+ this.removeChild(this.children[0]);
+ }
+ while (this.children.length > 0);
+
+ this.cursor = null;
+
+}
+
+/**
+* Removes all children from this Group whos index falls beteen the given startIndex and endIndex values.
+*
+* @method Phaser.Group#removeBetween
+* @param {number} startIndex - The index to start removing children from.
+* @param {number} endIndex - The index to stop removing children from. Must be higher than startIndex and less than the length of the Group.
+*/
+Phaser.Group.prototype.removeBetween = function (startIndex, endIndex) {
+
+ if (this.children.length === 0)
+ {
+ return;
+ }
+
+ if (startIndex > endIndex || startIndex < 0 || endIndex > this.children.length)
+ {
+ return false;
+ }
+
+ for (var i = startIndex; i < endIndex; i++)
+ {
+ if (this.children[i].events)
+ {
+ this.children[i].events.onRemovedFromGroup.dispatch(this.children[i], this);
+ }
+
+ this.removeChild(this.children[i]);
+
+ if (this.cursor === child)
+ {
+ this.cursor = null;
+ }
+ }
+
+}
+
+/**
+* Destroys this Group. Removes all children, then removes the container from the display list and nulls references.
+*
+* @method Phaser.Group#destroy
+* @param {boolean} [destroyChildren=false] - Should every child of this Group have its destroy method called?
+*/
+Phaser.Group.prototype.destroy = function (destroyChildren) {
+
+ if (typeof destroyChildren === 'undefined') { destroyChildren = false; }
+
+ if (destroyChildren)
+ {
+ if (this.children.length > 0)
+ {
+ do
+ {
+ if (this.children[0].group)
+ {
+ this.children[0].destroy();
+ }
+ }
+ while (this.children.length > 0);
+ }
+ }
+ else
+ {
+ this.removeAll();
+ }
+
+ this.parent.removeChild(this);
+
+ this.game = null;
+
+ this.exists = false;
+
+ this.cursor = null;
+
+}
/**
* @name Phaser.Group#total
@@ -12388,14 +25923,7 @@ Object.defineProperty(Phaser.Group.prototype, "total", {
get: function () {
- if (this._container)
- {
- return this.iterate('exists', true, Phaser.Group.RETURN_TOTAL);
- }
- else
- {
- return 0;
- }
+ return this.iterate('exists', true, Phaser.Group.RETURN_TOTAL);
}
@@ -12410,55 +25938,12 @@ Object.defineProperty(Phaser.Group.prototype, "length", {
get: function () {
- if (this._container)
- {
- return this._container.children.length;
- }
- else
- {
- return 0;
- }
+ return this.children.length;
}
});
-/**
-* The x coordinate of the Group container. You can adjust the Group container itself by modifying its coordinates.
-* This will have no impact on the x/y coordinates of its children, but it will update their worldTransform and on-screen position.
-* @name Phaser.Group#x
-* @property {number} x - The x coordinate of the Group container.
-*/
-Object.defineProperty(Phaser.Group.prototype, "x", {
-
- get: function () {
- return this._container.position.x;
- },
-
- set: function (value) {
- this._container.position.x = value;
- }
-
-});
-
-/**
-* The y coordinate of the Group container. You can adjust the Group container itself by modifying its coordinates.
-* This will have no impact on the x/y coordinates of its children, but it will update their worldTransform and on-screen position.
-* @name Phaser.Group#y
-* @property {number} y - The y coordinate of the Group container.
-*/
-Object.defineProperty(Phaser.Group.prototype, "y", {
-
- get: function () {
- return this._container.position.y;
- },
-
- set: function (value) {
- this._container.position.y = value;
- }
-
-});
-
/**
* The angle of rotation of the Group container. This will adjust the Group container itself by modifying its rotation.
* This will have no impact on the rotation value of its children, but it will update their worldTransform and on-screen position.
@@ -12468,64 +25953,78 @@ Object.defineProperty(Phaser.Group.prototype, "y", {
Object.defineProperty(Phaser.Group.prototype, "angle", {
get: function() {
- return Phaser.Math.radToDeg(this._container.rotation);
+ return Phaser.Math.radToDeg(this.rotation);
},
set: function(value) {
- this._container.rotation = Phaser.Math.degToRad(value);
+ this.rotation = Phaser.Math.degToRad(value);
}
});
+/**
+* A Group that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Group.cameraOffset.
+* Note that the cameraOffset values are in addition to any parent in the display list.
+* So if this Group was in a Group that has x: 200, then this will be added to the cameraOffset.x
+*
+* @name Phaser.Group#fixedToCamera
+* @property {boolean} fixedToCamera - Set to true to fix this Group to the Camera at its current world coordinates.
+*/
+Object.defineProperty(Phaser.Group.prototype, "fixedToCamera", {
+
+ get: function () {
+
+ return !!this._cache[7];
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ this._cache[7] = 1;
+ this.cameraOffset.set(this.x, this.y);
+ }
+ else
+ {
+ this._cache[7] = 0;
+ }
+ }
+
+});
+
+// Documentation stubs
+
+/**
+* The x coordinate of the Group container. You can adjust the Group container itself by modifying its coordinates.
+* This will have no impact on the x/y coordinates of its children, but it will update their worldTransform and on-screen position.
+* @name Phaser.Group#x
+* @property {number} x - The x coordinate of the Group container.
+*/
+
+/**
+* The y coordinate of the Group container. You can adjust the Group container itself by modifying its coordinates.
+* This will have no impact on the x/y coordinates of its children, but it will update their worldTransform and on-screen position.
+* @name Phaser.Group#y
+* @property {number} y - The y coordinate of the Group container.
+*/
+
/**
* The angle of rotation of the Group container. This will adjust the Group container itself by modifying its rotation.
* This will have no impact on the rotation value of its children, but it will update their worldTransform and on-screen position.
* @name Phaser.Group#rotation
* @property {number} rotation - The angle of rotation given in radians.
*/
-Object.defineProperty(Phaser.Group.prototype, "rotation", {
-
- get: function () {
- return this._container.rotation;
- },
-
- set: function (value) {
- this._container.rotation = value;
- }
-
-});
/**
* @name Phaser.Group#visible
* @property {boolean} visible - The visible state of the Group. Non-visible Groups and all of their children are not rendered.
*/
-Object.defineProperty(Phaser.Group.prototype, "visible", {
-
- get: function () {
- return this._container.visible;
- },
-
- set: function (value) {
- this._container.visible = value;
- }
-
-});
/**
* @name Phaser.Group#alpha
* @property {number} alpha - The alpha value of the Group container.
*/
-Object.defineProperty(Phaser.Group.prototype, "alpha", {
-
- get: function () {
- return this._container.alpha;
- },
-
- set: function (value) {
- this._container.alpha = value;
- }
-
-});
/**
* @author Richard Davey
@@ -12568,7 +26067,7 @@ Phaser.World = function (game) {
*/
this.currentRenderOrderID = 0;
-};
+}
Phaser.World.prototype = Object.create(Phaser.Group.prototype);
Phaser.World.prototype.constructor = Phaser.World;
@@ -12583,126 +26082,11 @@ Phaser.World.prototype.boot = function () {
this.camera = new Phaser.Camera(this.game, 0, 0, 0, this.game.width, this.game.height);
- this.camera.displayObject = this._container;
+ this.camera.displayObject = this;
this.game.camera = this.camera;
-}
-
-/**
-* This is called automatically after the plugins preUpdate and before the State.update.
-* Most objects have preUpdate methods and it's where initial movement, drawing and calculations are done.
-*
-* @method Phaser.World#update
-*/
-Phaser.World.prototype.preUpdate = function () {
-
- if (this.game.stage._stage.first._iNext)
- {
- var currentNode = this.game.stage._stage.first._iNext;
-
- do
- {
- // If preUpdate exists, and it returns false, skip PIXI child objects
- if (currentNode['preUpdate'] && !currentNode.preUpdate())
- {
- currentNode = currentNode.last._iNext;
- }
- else
- {
- currentNode = currentNode._iNext;
- }
-
- }
- while (currentNode != this.game.stage._stage.last._iNext)
- }
-
-}
-
-/**
-* This is called automatically after the State.update, but before particles or plugins update.
-* Most objects won't have an update method set unless explicitly given one.
-*
-* @method Phaser.World#update
-*/
-Phaser.World.prototype.update = function () {
-
- this.currentRenderOrderID = 0;
-
- if (this.game.stage._stage.first._iNext)
- {
- var currentNode = this.game.stage._stage.first._iNext;
-
- do
- {
- // If update exists, and it returns false, skip PIXI child objects
- if (currentNode['update'] && !currentNode.update())
- {
- currentNode = currentNode.last._iNext;
- }
- else
- {
- currentNode = currentNode._iNext;
- }
-
- }
- while (currentNode != this.game.stage._stage.last._iNext)
- }
-
-}
-
-/**
-* This is called automatically before the renderer runs and after the plugins have updated.
-* In postUpdate this is where all the final physics calculatations and object positioning happens.
-* The objects are processed in the order of the display list.
-* The only exception to this is if the camera is following an object, in which case that is updated first.
-*
-* @method Phaser.World#postUpdate
-*/
-Phaser.World.prototype.postUpdate = function () {
-
- if (this.camera.target && this.camera.target['postUpdate'])
- {
- this.camera.target.postUpdate();
-
- this.camera.update();
-
- if (this.game.stage._stage.first._iNext)
- {
- var currentNode = this.game.stage._stage.first._iNext;
-
- do
- {
- if (currentNode['postUpdate'] && currentNode !== this.camera.target)
- {
- currentNode.postUpdate();
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this.game.stage._stage.last._iNext)
- }
- }
- else
- {
- this.camera.update();
-
- if (this.game.stage._stage.first._iNext)
- {
- var currentNode = this.game.stage._stage.first._iNext;
-
- do
- {
- if (currentNode['postUpdate'])
- {
- currentNode.postUpdate();
- }
-
- currentNode = currentNode._iNext;
- }
- while (currentNode != this.game.stage._stage.last._iNext)
- }
- }
+ this.game.stage.addChild(this);
}
@@ -12745,8 +26129,7 @@ Phaser.World.prototype.setBounds = function (x, y, width, height) {
*/
Phaser.World.prototype.destroy = function () {
- this.camera.x = 0;
- this.camera.y = 0;
+ this.camera.reset();
this.game.input.reset(true);
@@ -12856,21 +26239,6 @@ Object.defineProperty(Phaser.World.prototype, "randomY", {
});
-/**
-* @name Phaser.World#visible
-* @property {boolean} visible - Gets or sets the visible state of the World.
-*/
-Object.defineProperty(Phaser.World.prototype, "visible", {
-
- get: function () {
- return this._container.visible;
- },
-
- set: function (value) {
- this._container.visible = value;
- }
-
-});
/**
* @author Richard Davey
@@ -12895,8 +26263,9 @@ Object.defineProperty(Phaser.World.prototype, "visible", {
* @param {object} [state=null] - The default state object. A object consisting of Phaser.State functions (preload, create, update, render) or null.
* @param {boolean} [transparent=false] - Use a transparent canvas background or not.
* @param {boolean} [antialias=true] - Anti-alias graphics.
+* @param {object} [physicsConfig=null] - A physics configuration object to pass to the Physics world on creation.
*/
-Phaser.Game = function (width, height, renderer, parent, state, transparent, antialias) {
+Phaser.Game = function (width, height, renderer, parent, state, transparent, antialias, physicsConfig) {
/**
* @property {number} id - Phaser Game ID (for when Pixi supports multiple instances).
@@ -12908,6 +26277,11 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
*/
this.config = null;
+ /**
+ * @property {object} physicsConfig - The Phaser.Physics.World configuration object.
+ */
+ this.physicsConfig = physicsConfig;
+
/**
* @property {HTMLElement} parent - The Games DOM parent.
* @default
@@ -12982,13 +26356,11 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
/**
* @property {Phaser.RequestAnimationFrame} raf - Automatically handles the core game loop via requestAnimationFrame or setTimeout
- * @default
*/
this.raf = null;
/**
* @property {Phaser.GameObjectFactory} add - Reference to the GameObject Factory.
- * @default
*/
this.add = null;
@@ -13012,91 +26384,81 @@ Phaser.Game = function (width, height, renderer, parent, state, transparent, ant
/**
* @property {Phaser.Math} math - Reference to the math helper.
- * @default
*/
this.math = null;
/**
* @property {Phaser.Net} net - Reference to the network class.
- * @default
*/
this.net = null;
+ /**
+ * @property {Phaser.StageScaleMode} scale - The game scale manager.
+ */
+ this.scale = null;
+
/**
* @property {Phaser.SoundManager} sound - Reference to the sound manager.
- * @default
*/
this.sound = null;
/**
* @property {Phaser.Stage} stage - Reference to the stage.
- * @default
*/
this.stage = null;
/**
* @property {Phaser.TimeManager} time - Reference to game clock.
- * @default
*/
this.time = null;
/**
* @property {Phaser.TweenManager} tweens - Reference to the tween manager.
- * @default
*/
this.tweens = null;
/**
* @property {Phaser.World} world - Reference to the world.
- * @default
*/
this.world = null;
/**
- * @property {Phaser.Physics.PhysicsManager} physics - Reference to the physics manager.
- * @default
+ * @property {Phaser.Physics.World} physics - Reference to the physics world.
*/
this.physics = null;
/**
* @property {Phaser.RandomDataGenerator} rnd - Instance of repeatable random data generator helper.
- * @default
*/
this.rnd = null;
/**
* @property {Phaser.Device} device - Contains device information and capabilities.
- * @default
*/
this.device = null;
/**
* @property {Phaser.Physics.PhysicsManager} camera - A handy reference to world.camera.
- * @default
*/
this.camera = null;
- /**
- * @property {HTMLCanvasElement} canvas - A handy reference to renderer.view.
- * @default
+ /**
+ * @property {HTMLCanvasElement} canvas - A handy reference to renderer.view, the canvas that the game is being rendered in to.
*/
this.canvas = null;
/**
- * @property {Context} context - A handy reference to renderer.context (only set for CANVAS games)
- * @default
+ * @property {Context} context - A handy reference to renderer.context (only set for CANVAS games, not WebGL)
*/
this.context = null;
/**
* @property {Phaser.Utils.Debug} debug - A set of useful debug utilitie.
- * @default
*/
this.debug = null;
/**
* @property {Phaser.Particles} particles - The Particle Manager.
- * @default
*/
this.particles = null;
@@ -13225,6 +26587,11 @@ Phaser.Game.prototype = {
this.antialias = config['antialias'];
}
+ if (config['physicsConfig'])
+ {
+ this.physicsConfig = config['physicsConfig'];
+ }
+
var state = null;
if (config['state'])
@@ -13309,6 +26676,7 @@ Phaser.Game.prototype = {
this.rnd = new Phaser.RandomDataGenerator([(Date.now() * Math.random()).toString()]);
this.stage = new Phaser.Stage(this, this.width, this.height);
+ this.scale = new Phaser.StageScaleMode(this, this.width, this.height);
this.setUpRenderer();
@@ -13320,7 +26688,7 @@ Phaser.Game.prototype = {
this.tweens = new Phaser.TweenManager(this);
this.input = new Phaser.Input(this);
this.sound = new Phaser.SoundManager(this);
- this.physics = new Phaser.Physics.Arcade(this);
+ this.physics = new Phaser.Physics.World(this, this.physicsConfig);
this.particles = new Phaser.Particles(this);
this.plugins = new Phaser.PluginManager(this, this);
this.net = new Phaser.Net(this);
@@ -13342,7 +26710,6 @@ Phaser.Game.prototype = {
this.raf = new Phaser.RequestAnimationFrame(this);
this.raf.start();
-
}
},
@@ -13418,9 +26785,9 @@ Phaser.Game.prototype = {
this.renderType = Phaser.CANVAS;
}
- this.renderer = new PIXI.CanvasRenderer(this.width, this.height, this.stage.canvas, this.transparent);
+ this.renderer = new PIXI.CanvasRenderer(this.width, this.height, this.canvas, this.transparent);
Phaser.Canvas.setSmoothingEnabled(this.renderer.context, this.antialias);
- this.canvas = this.renderer.view;
+ // this.canvas = this.renderer.view;
this.context = this.renderer.context;
}
else
@@ -13432,13 +26799,16 @@ Phaser.Game.prototype = {
{
// They requested WebGL, and their browser supports it
this.renderType = Phaser.WEBGL;
- this.renderer = new PIXI.WebGLRenderer(this.width, this.height, this.stage.canvas, this.transparent, this.antialias);
- this.canvas = this.renderer.view;
+ this.renderer = new PIXI.WebGLRenderer(this.width, this.height, this.canvas, this.transparent, this.antialias);
+ // this.canvas = this.renderer.view;
this.context = null;
}
- Phaser.Canvas.addToDOM(this.renderer.view, this.parent, true);
- Phaser.Canvas.setTouchAction(this.renderer.view);
+ // Phaser.Canvas.addToDOM(this.renderer.view, this.parent, true);
+ // Phaser.Canvas.setTouchAction(this.renderer.view);
+
+ Phaser.Canvas.addToDOM(this.canvas, this.parent, true);
+ Phaser.Canvas.setTouchAction(this.canvas);
},
@@ -13469,7 +26839,7 @@ Phaser.Game.prototype = {
if (this._paused)
{
- this.renderer.render(this.stage._stage);
+ this.renderer.render(this.stage);
this.plugins.render();
this.state.render();
}
@@ -13483,24 +26853,24 @@ Phaser.Game.prototype = {
}
this.plugins.preUpdate();
- this.world.preUpdate();
+ this.stage.preUpdate();
this.stage.update();
this.tweens.update();
this.sound.update();
this.input.update();
this.state.update();
- this.world.update();
+ this.physics.update();
this.particles.update();
this.plugins.update();
- this.world.postUpdate();
+ this.stage.postUpdate();
this.plugins.postUpdate();
}
if (this.renderType !== Phaser.HEADLESS)
{
- this.renderer.render(this.stage._stage);
+ this.renderer.render(this.stage);
this.plugins.render();
this.state.render();
@@ -13659,6 +27029,257 @@ Phaser.Input = function (game) {
* @property {object} moveCallbackContext - The context in which the moveCallback will be sent. Defaults to Phaser.Input but can be set to any valid JS object.
*/
this.moveCallbackContext = this;
+
+ /**
+ * @property {number} pollRate - How often should the input pointers be checked for updates? A value of 0 means every single frame (60fps); a value of 1 means every other frame (30fps) and so on.
+ * @default
+ */
+ this.pollRate = 0;
+
+ /**
+ * @property {number} _pollCounter - Internal var holding the current poll counter.
+ * @private
+ */
+ this._pollCounter = 0;
+
+ /**
+ * @property {Phaser.Point} _oldPosition - A point object representing the previous position of the Pointer.
+ * @private
+ */
+ this._oldPosition = null;
+
+ /**
+ * @property {number} _x - x coordinate of the most recent Pointer event
+ * @private
+ */
+ this._x = 0;
+
+ /**
+ * @property {number} _y - Y coordinate of the most recent Pointer event
+ * @private
+ */
+ this._y = 0;
+
+ /**
+ * You can disable all Input by setting Input.disabled = true. While set all new input related events will be ignored.
+ * If you need to disable just one type of input; for example mouse; use Input.mouse.disabled = true instead
+ * @property {boolean} disabled
+ * @default
+ */
+ this.disabled = false;
+
+ /**
+ * @property {number} multiInputOverride - Controls the expected behaviour when using a mouse and touch together on a multi-input device.
+ * @default
+ */
+ this.multiInputOverride = Phaser.Input.MOUSE_TOUCH_COMBINE;
+
+ /**
+ * @property {Phaser.Point} position - A point object representing the current position of the Pointer.
+ * @default
+ */
+ this.position = null;
+
+ /**
+ * @property {Phaser.Point} speed - A point object representing the speed of the Pointer. Only really useful in single Pointer games; otherwise see the Pointer objects directly.
+ */
+ this.speed = null;
+
+ /**
+ * A Circle object centered on the x/y screen coordinates of the Input.
+ * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything.
+ * @property {Phaser.Circle} circle
+ */
+ this.circle = null;
+
+ /**
+ * @property {Phaser.Point} scale - The scale by which all input coordinates are multiplied; calculated by the StageScaleMode. In an un-scaled game the values will be x = 1 and y = 1.
+ */
+ this.scale = null;
+
+ /**
+ * @property {number} maxPointers - The maximum number of Pointers allowed to be active at any one time. For lots of games it's useful to set this to 1.
+ * @default
+ */
+ this.maxPointers = 10;
+
+ /**
+ * @property {number} currentPointers - The current number of active Pointers.
+ * @default
+ */
+ this.currentPointers = 0;
+
+ /**
+ * @property {number} tapRate - The number of milliseconds that the Pointer has to be pressed down and then released to be considered a tap or click.
+ * @default
+ */
+ this.tapRate = 200;
+
+ /**
+ * @property {number} doubleTapRate - The number of milliseconds between taps of the same Pointer for it to be considered a double tap / click.
+ * @default
+ */
+ this.doubleTapRate = 300;
+
+ /**
+ * @property {number} holdRate - The number of milliseconds that the Pointer has to be pressed down for it to fire a onHold event.
+ * @default
+ */
+ this.holdRate = 2000;
+
+ /**
+ * @property {number} justPressedRate - The number of milliseconds below which the Pointer is considered justPressed.
+ * @default
+ */
+ this.justPressedRate = 200;
+
+ /**
+ * @property {number} justReleasedRate - The number of milliseconds below which the Pointer is considered justReleased .
+ * @default
+ */
+ this.justReleasedRate = 200;
+
+ /**
+ * Sets if the Pointer objects should record a history of x/y coordinates they have passed through.
+ * The history is cleared each time the Pointer is pressed down.
+ * The history is updated at the rate specified in Input.pollRate
+ * @property {boolean} recordPointerHistory
+ * @default
+ */
+ this.recordPointerHistory = false;
+
+ /**
+ * @property {number} recordRate - The rate in milliseconds at which the Pointer objects should update their tracking history.
+ * @default
+ */
+ this.recordRate = 100;
+
+ /**
+ * The total number of entries that can be recorded into the Pointer objects tracking history.
+ * If the Pointer is tracking one event every 100ms; then a trackLimit of 100 would store the last 10 seconds worth of history.
+ * @property {number} recordLimit
+ * @default
+ */
+ this.recordLimit = 100;
+
+ /**
+ * @property {Phaser.Pointer} pointer1 - A Pointer object.
+ */
+ this.pointer1 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer2 - A Pointer object.
+ */
+ this.pointer2 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer3 - A Pointer object.
+ */
+ this.pointer3 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer4 - A Pointer object.
+ */
+ this.pointer4 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer5 - A Pointer object.
+ */
+ this.pointer5 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer6 - A Pointer object.
+ */
+ this.pointer6 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer7 - A Pointer object.
+ */
+ this.pointer7 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer8 - A Pointer object.
+ */
+ this.pointer8 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer9 - A Pointer object.
+ */
+ this.pointer9 = null;
+
+ /**
+ * @property {Phaser.Pointer} pointer10 - A Pointer object.
+ */
+ this.pointer10 = null;
+
+ /**
+ * The most recently active Pointer object.
+ * When you've limited max pointers to 1 this will accurately be either the first finger touched or mouse.
+ * @property {Phaser.Pointer} activePointer
+ */
+ this.activePointer = null;
+
+ /**
+ * @property {Pointer} mousePointer - The mouse has its own unique Phaser.Pointer object which you can use if making a desktop specific game.
+ */
+ this.mousePointer = null;
+
+ /**
+ * @property {Phaser.Mouse} mouse - The Mouse Input manager.
+ */
+ this.mouse = null;
+
+ /**
+ * @property {Phaser.Keyboard} keyboard - The Keyboard Input manager.
+ */
+ this.keyboard = null;
+
+ /**
+ * @property {Phaser.Touch} touch - the Touch Input manager.
+ */
+ this.touch = null;
+
+ /**
+ * @property {Phaser.MSPointer} mspointer - The MSPointer Input manager.
+ */
+ this.mspointer = null;
+
+ /**
+ * @property {Phaser.Gamepad} gamepad - The Gamepad Input manager.
+ */
+ this.gamepad = null;
+
+ /**
+ * @property {Phaser.Signal} onDown - A Signal that is dispatched each time a pointer is pressed down.
+ */
+ this.onDown = null;
+
+ /**
+ * @property {Phaser.Signal} onUp - A Signal that is dispatched each time a pointer is released.
+ */
+ this.onUp = null;
+
+ /**
+ * @property {Phaser.Signal} onTap - A Signal that is dispatched each time a pointer is tapped.
+ */
+ this.onTap = null;
+
+ /**
+ * @property {Phaser.Signal} onHold - A Signal that is dispatched each time a pointer is held down.
+ */
+ this.onHold = null;
+
+ /**
+ * A linked list of interactive objects; the InputHandler components (belonging to Sprites) register themselves with this.
+ * @property {Phaser.LinkedList} interactiveItems
+ */
+ this.interactiveItems = new Phaser.LinkedList();
+
+ /**
+ * @property {Phaser.Point} _localPoint - Internal cache var.
+ * @private
+ */
+ this._localPoint = new Phaser.Point();
};
@@ -13682,300 +27303,6 @@ Phaser.Input.MOUSE_TOUCH_COMBINE = 2;
Phaser.Input.prototype = {
- /**
- * How often should the input pointers be checked for updates?
- * A value of 0 means every single frame (60fps), a value of 1 means every other frame (30fps) and so on.
- * @property {number} pollRate
- * @default
- */
- pollRate: 0,
-
- /**
- * @property {number} _pollCounter - Internal var holding the current poll counter.
- * @private
- * @default
- */
- _pollCounter: 0,
-
- /**
- * @property {Phaser.Point} _oldPosition - A point object representing the previous position of the Pointer.
- * @private
- * @default
- */
- _oldPosition: null,
-
- /**
- * @property {number} _x - x coordinate of the most recent Pointer event
- * @private
- * @default
- */
- _x: 0,
-
- /**
- * @property {number} _y - Y coordinate of the most recent Pointer event
- * @private
- * @default
- */
- _y: 0,
-
- /**
- * You can disable all Input by setting Input.disabled: true. While set all new input related events will be ignored.
- * If you need to disable just one type of input, for example mouse, use Input.mouse.disabled: true instead
- * @property {boolean} disabled
- * @default
- */
- disabled: false,
-
- /**
- * Controls the expected behaviour when using a mouse and touch together on a multi-input device.
- * @property {Description} multiInputOverride
- */
- multiInputOverride: Phaser.Input.MOUSE_TOUCH_COMBINE,
-
- /**
- * @property {Phaser.Point} position - A point object representing the current position of the Pointer.
- * @default
- */
- position: null,
-
- /**
- * A point object representing the speed of the Pointer. Only really useful in single Pointer games, otherwise see the Pointer objects directly.
- * @property {Phaser.Point} speed
- */
- speed: null,
-
- /**
- * A Circle object centered on the x/y screen coordinates of the Input.
- * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything.
- * @property {Phaser.Circle} circle
- */
- circle: null,
-
- /**
- * The scale by which all input coordinates are multiplied, calculated by the StageScaleMode.
- * In an un-scaled game the values will be x: 1 and y: 1.
- * @property {Phaser.Point} scale
- */
- scale: null,
-
- /**
- * The maximum number of Pointers allowed to be active at any one time.
- * For lots of games it's useful to set this to 1.
- * @property {number} maxPointers
- * @default
- */
- maxPointers: 10,
-
- /**
- * The current number of active Pointers.
- * @property {number} currentPointers
- * @default
- */
- currentPointers: 0,
-
- /**
- * The number of milliseconds that the Pointer has to be pressed down and then released to be considered a tap or clicke
- * @property {number} tapRate
- * @default
- */
- tapRate: 200,
-
- /**
- * The number of milliseconds between taps of the same Pointer for it to be considered a double tap / click
- * @property {number} doubleTapRate
- * @default
- */
- doubleTapRate: 300,
-
- /**
- * The number of milliseconds that the Pointer has to be pressed down for it to fire a onHold event
- * @property {number} holdRate
- * @default
- */
- holdRate: 2000,
-
- /**
- * The number of milliseconds below which the Pointer is considered justPressed
- * @property {number} justPressedRate
- * @default
- */
- justPressedRate: 200,
-
- /**
- * The number of milliseconds below which the Pointer is considered justReleased
- * @property {number} justReleasedRate
- * @default
- */
- justReleasedRate: 200,
-
- /**
- * Sets if the Pointer objects should record a history of x/y coordinates they have passed through.
- * The history is cleared each time the Pointer is pressed down.
- * The history is updated at the rate specified in Input.pollRate
- * @property {boolean} recordPointerHistory
- * @default
- */
- recordPointerHistory: false,
-
- /**
- * The rate in milliseconds at which the Pointer objects should update their tracking history
- * @property {number} recordRate
- * @default
- */
- recordRate: 100,
-
- /**
- * The total number of entries that can be recorded into the Pointer objects tracking history.
- * If the Pointer is tracking one event every 100ms, then a trackLimit of 100 would store the last 10 seconds worth of history.
- * @property {number} recordLimit
- * @default
- */
- recordLimit: 100,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer1
- */
- pointer1: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer2
- */
- pointer2: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer3
- */
- pointer3: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer4
- */
- pointer4: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer5
- */
- pointer5: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer6
- */
- pointer6: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer7
- */
- pointer7: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer8
- */
- pointer8: null,
-
- /**
- * A Pointer object
- * @property {Phaser.Pointer} pointer9
- */
- pointer9: null,
-
- /**
- * A Pointer object.
- * @property {Phaser.Pointer} pointer10
- */
- pointer10: null,
-
- /**
- * The most recently active Pointer object.
- * When you've limited max pointers to 1 this will accurately be either the first finger touched or mouse.
- * @property {Phaser.Pointer} activePointer
- * @default
- */
- activePointer: null,
-
- /**
- * The mouse has its own unique Phaser.Pointer object which you can use if making a desktop specific game.
- * @property {Pointer} mousePointer
- * @default
- */
- mousePointer: null,
-
- /**
- * The Mouse Input manager.
- * @property {Phaser.Mouse} mouse - The Mouse Input manager.
- * @default
- */
- mouse: null,
-
- /**
- * The Keyboard Input manager.
- * @property {Phaser.Keyboard} keyboard - The Keyboard Input manager.
- * @default
- */
- keyboard: null,
-
- /**
- * The Touch Input manager.
- * @property {Phaser.Touch} touch - the Touch Input manager.
- * @default
- */
- touch: null,
-
- /**
- * The MSPointer Input manager.
- * @property {Phaser.MSPointer} mspointer - The MSPointer Input manager.
- * @default
- */
- mspointer: null,
-
- /**
- * The Gamepad Input manager.
- * @property {Phaser.Gamepad} gamepad - The Gamepad Input manager.
- * @default
- */
- gamepad: null,
-
- /**
- * A Signal that is dispatched each time a pointer is pressed down.
- * @property {Phaser.Signal} onDown
- * @default
- */
- onDown: null,
-
- /**
- * A Signal that is dispatched each time a pointer is released.
- * @property {Phaser.Signal} onUp
- * @default
- */
- onUp: null,
-
- /**
- * A Signal that is dispatched each time a pointer is tapped.
- * @property {Phaser.Signal} onTap
- * @default
- */
- onTap: null,
-
- /**
- * A Signal that is dispatched each time a pointer is held down.
- * @property {Phaser.Signal} onHold
- * @default
- */
- onHold: null,
-
- /**
- * A linked list of interactive objects, the InputHandler components (belonging to Sprites) register themselves with this.
- * @property {Phaser.LinkedList} interactiveItems
- */
- interactiveItems: new Phaser.LinkedList(),
-
/**
* Starts the Input Manager running.
* @method Phaser.Input#boot
@@ -14149,7 +27476,7 @@ Phaser.Input.prototype = {
if (this.game.canvas.style.cursor !== 'none')
{
- this.game.canvas.style.cursor = 'default';
+ this.game.canvas.style.cursor = 'inherit';
}
if (hard === true)
@@ -14343,6 +27670,81 @@ Phaser.Input.prototype = {
return null;
+ },
+
+ /**
+ * This will return the local coordinates of the specified displayObject based on the given Pointer.
+ * @method Phaser.Input#getLocalPosition
+ * @param {Phaser.Sprite|Phaser.Image} displayObject - The DisplayObject to get the local coordinates for.
+ * @param {Phaser.Pointer} pointer - The Pointer to use in the check against the displayObject.
+ * @return {Phaser.Point} A point containing the coordinates of the Pointer position relative to the DisplayObject.
+ */
+ getLocalPosition: function (displayObject, pointer, output) {
+
+ if (typeof output === 'undefined') { output = new Phaser.Point(); }
+
+ var wt = displayObject.worldTransform;
+ var id = 1 / (wt.a * wt.d + wt.b * -wt.c);
+
+ return output.setTo(
+ wt.d * id * pointer.x + -wt.b * id * pointer.y + (wt.ty * wt.b - wt.tx * wt.d) * id,
+ wt.a * id * pointer.y + -wt.c * id * pointer.x + (-wt.ty * wt.a + wt.tx * wt.c) * id
+ );
+
+ },
+
+ /**
+ * Tests if the current mouse coordinates hit a sprite
+ *
+ * @method hitTest
+ * @param displayObject {DisplayObject} The displayObject to test for a hit
+ */
+ hitTest: function (displayObject, pointer, localPoint) {
+
+ if (!displayObject.worldVisible)
+ {
+ return false;
+ }
+
+ this.getLocalPosition(displayObject, pointer, this._localPoint);
+
+ localPoint.copyFrom(this._localPoint);
+
+ if (displayObject.hitArea && displayObject.hitArea.contains)
+ {
+ if (displayObject.hitArea.contains(this._localPoint.x, this._localPoint.y))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ else if (displayObject instanceof PIXI.Sprite)
+ {
+ var width = displayObject.texture.frame.width;
+ var height = displayObject.texture.frame.height;
+ var x1 = -width * displayObject.anchor.x;
+
+ if (this._localPoint.x > x1 && this._localPoint.x < x1 + width)
+ {
+ var y1 = -height * displayObject.anchor.y;
+
+ if (this._localPoint.y > y1 && this._localPoint.y < y1 + height)
+ {
+ return true;
+ }
+ }
+ }
+
+ for (var i = 0, len = displayObject.children.length; i < len; i++)
+ {
+ if (this.hitTest(displayObject.children[i], pointer, localPoint))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
};
@@ -14620,7 +28022,7 @@ Phaser.Key.prototype = {
/**
* Returns the "just released" state of the Key. Just released is considered as being true if the key was released within the duration given (default 250ms)
- * @method Phaser.Key#justPressed
+ * @method Phaser.Key#justReleased
* @param {number} [duration=250] - The duration below which the key is considered as being just released.
* @return {boolean} True if the key is just released otherwise false.
*/
@@ -15281,9 +28683,9 @@ Phaser.Mouse.prototype = {
return _this.onMouseUp(event);
};
- document.addEventListener('mousedown', this._onMouseDown, true);
- document.addEventListener('mousemove', this._onMouseMove, true);
- document.addEventListener('mouseup', this._onMouseUp, true);
+ this.game.canvas.addEventListener('mousedown', this._onMouseDown, true);
+ this.game.canvas.addEventListener('mousemove', this._onMouseMove, true);
+ this.game.canvas.addEventListener('mouseup', this._onMouseUp, true);
},
@@ -15391,7 +28793,7 @@ Phaser.Mouse.prototype = {
if (this.game.device.pointerLock)
{
- var element = this.game.stage.canvas;
+ var element = this.game.canvas;
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
@@ -15417,7 +28819,7 @@ Phaser.Mouse.prototype = {
*/
pointerLockChange: function (event) {
- var element = this.game.stage.canvas;
+ var element = this.game.canvas;
if (document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element)
{
@@ -15456,9 +28858,9 @@ Phaser.Mouse.prototype = {
*/
stop: function () {
- document.removeEventListener('mousedown', this._onMouseDown, true);
- document.removeEventListener('mousemove', this._onMouseMove, true);
- document.removeEventListener('mouseup', this._onMouseUp, true);
+ this.game.canvas.removeEventListener('mousedown', this._onMouseDown, true);
+ this.game.canvas.removeEventListener('mousemove', this._onMouseMove, true);
+ this.game.canvas.removeEventListener('mouseup', this._onMouseUp, true);
}
@@ -15623,13 +29025,13 @@ Phaser.MSPointer.prototype = {
*/
stop: function () {
- this.game.stage.canvas.removeEventListener('MSPointerDown', this._onMSPointerDown);
- this.game.stage.canvas.removeEventListener('MSPointerMove', this._onMSPointerMove);
- this.game.stage.canvas.removeEventListener('MSPointerUp', this._onMSPointerUp);
+ this.game.canvas.removeEventListener('MSPointerDown', this._onMSPointerDown);
+ this.game.canvas.removeEventListener('MSPointerMove', this._onMSPointerMove);
+ this.game.canvas.removeEventListener('MSPointerUp', this._onMSPointerUp);
- this.game.stage.canvas.removeEventListener('pointerDown', this._onMSPointerDown);
- this.game.stage.canvas.removeEventListener('pointerMove', this._onMSPointerMove);
- this.game.stage.canvas.removeEventListener('pointerUp', this._onMSPointerUp);
+ this.game.canvas.removeEventListener('pointerDown', this._onMSPointerDown);
+ this.game.canvas.removeEventListener('pointerMove', this._onMSPointerMove);
+ this.game.canvas.removeEventListener('pointerUp', this._onMSPointerUp);
}
@@ -15846,7 +29248,7 @@ Phaser.Pointer.prototype = {
}
// Fix to stop rogue browser plugins from blocking the visibility state event
- if (this.game.stage.disableVisibilityChange === false && this.game.paused && this.game.stage.scale.incorrectOrientation === false)
+ if (this.game.stage.disableVisibilityChange === false && this.game.paused && this.game.scale.incorrectOrientation === false)
{
this.game.paused = false;
return this;
@@ -15864,12 +29266,12 @@ Phaser.Pointer.prototype = {
this._holdSent = false;
// This sets the x/y and other local values
- this.move(event);
+ this.move(event, true);
// x and y are the old values here?
this.positionDown.setTo(this.x, this.y);
- if (this.game.input.multiInputOverride == Phaser.Input.MOUSE_OVERRIDES_TOUCH || this.game.input.multiInputOverride == Phaser.Input.MOUSE_TOUCH_COMBINE || (this.game.input.multiInputOverride == Phaser.Input.TOUCH_OVERRIDES_MOUSE && this.game.input.currentPointers === 0))
+ if (this.game.input.multiInputOverride === Phaser.Input.MOUSE_OVERRIDES_TOUCH || this.game.input.multiInputOverride === Phaser.Input.MOUSE_TOUCH_COMBINE || (this.game.input.multiInputOverride === Phaser.Input.TOUCH_OVERRIDES_MOUSE && this.game.input.currentPointers === 0))
{
this.game.input.x = this.x;
this.game.input.y = this.y;
@@ -15881,7 +29283,7 @@ Phaser.Pointer.prototype = {
this._stateReset = false;
this.totalTouches++;
- if (this.isMouse === false)
+ if (!this.isMouse)
{
this.game.input.currentPointers++;
}
@@ -15936,14 +29338,17 @@ Phaser.Pointer.prototype = {
* Called when the Pointer is moved.
* @method Phaser.Pointer#move
* @param {MouseEvent|PointerEvent|TouchEvent} event - The event passed up from the input handler.
+ * @param {boolean} [fromClick=false] - Was this called from the click event?
*/
- move: function (event) {
+ move: function (event, fromClick) {
if (this.game.input.pollLocked)
{
return;
}
+ if (typeof fromClick === 'undefined') { fromClick = false; }
+
if (typeof event.button !== 'undefined')
{
this.button = event.button;
@@ -16010,11 +29415,10 @@ Phaser.Pointer.prototype = {
do
{
// If the object is using pixelPerfect checks, or has a higher InputManager.PriorityID OR if the priority ID is the same as the current highest AND it has a higher renderOrderID, then set it to the top
- if (currentNode.pixelPerfect || currentNode.priorityID > this._highestInputPriorityID || (currentNode.priorityID == this._highestInputPriorityID && currentNode.sprite.renderOrderID > this._highestRenderOrderID))
+ if (currentNode.pixelPerfectClick || currentNode.pixelPerfectOver || currentNode.priorityID > this._highestInputPriorityID || (currentNode.priorityID === this._highestInputPriorityID && currentNode.sprite.renderOrderID > this._highestRenderOrderID))
{
- if (currentNode.checkPointerOver(this))
+ if ((!fromClick && currentNode.checkPointerOver(this)) || (fromClick && currentNode.checkPointerDown(this)))
{
- // console.log('HRO set', currentNode.sprite.name);
this._highestRenderOrderID = currentNode.sprite.renderOrderID;
this._highestInputPriorityID = currentNode.priorityID;
this._highestRenderObject = currentNode;
@@ -16025,7 +29429,7 @@ Phaser.Pointer.prototype = {
while (currentNode != null)
}
- if (this._highestRenderObject == null)
+ if (this._highestRenderObject === null)
{
// The pointer isn't currently over anything, check if we've got a lingering previous target
if (this.targetObject)
@@ -16037,7 +29441,7 @@ Phaser.Pointer.prototype = {
}
else
{
- if (this.targetObject == null)
+ if (this.targetObject === null)
{
// And now set the new one
// console.log('And now set the new one');
@@ -16048,7 +29452,7 @@ Phaser.Pointer.prototype = {
{
// We've got a target from the last update
// console.log("We've got a target from the last update");
- if (this.targetObject == this._highestRenderObject)
+ if (this.targetObject === this._highestRenderObject)
{
// Same target as before, so update it
// console.log("Same target as before, so update it");
@@ -16082,7 +29486,7 @@ Phaser.Pointer.prototype = {
leave: function (event) {
this.withinGame = false;
- this.move(event);
+ this.move(event, false);
},
@@ -16640,12 +30044,12 @@ Phaser.Touch.prototype = {
if (this.game.device.touch)
{
- this.game.stage.canvas.removeEventListener('touchstart', this._onTouchStart);
- this.game.stage.canvas.removeEventListener('touchmove', this._onTouchMove);
- this.game.stage.canvas.removeEventListener('touchend', this._onTouchEnd);
- this.game.stage.canvas.removeEventListener('touchenter', this._onTouchEnter);
- this.game.stage.canvas.removeEventListener('touchleave', this._onTouchLeave);
- this.game.stage.canvas.removeEventListener('touchcancel', this._onTouchCancel);
+ this.game.canvas.removeEventListener('touchstart', this._onTouchStart);
+ this.game.canvas.removeEventListener('touchmove', this._onTouchMove);
+ this.game.canvas.removeEventListener('touchend', this._onTouchEnd);
+ this.game.canvas.removeEventListener('touchenter', this._onTouchEnter);
+ this.game.canvas.removeEventListener('touchleave', this._onTouchLeave);
+ this.game.canvas.removeEventListener('touchcancel', this._onTouchCancel);
}
}
@@ -16654,1185 +30058,6 @@ Phaser.Touch.prototype = {
Phaser.Touch.prototype.constructor = Phaser.Touch;
-/**
-* @author Richard Davey
-* @copyright 2014 Photon Storm Ltd.
-* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
-*/
-
-/**
-* The Input Handler is bound to a specific Sprite and is responsible for managing all Input events on that Sprite.
-* @class Phaser.InputHandler
-* @constructor
-* @param {Phaser.Sprite} sprite - The Sprite object to which this Input Handler belongs.
-*/
-Phaser.InputHandler = function (sprite) {
-
- /**
- * @property {Phaser.Sprite} sprite - The Sprite object to which this Input Handler belongs.
- */
- this.sprite = sprite;
-
- /**
- * @property {Phaser.Game} game - A reference to the currently running game.
- */
- this.game = sprite.game;
-
- /**
- * @property {boolean} enabled - If enabled the Input Handler will process input requests and monitor pointer activity.
- * @default
- */
- this.enabled = false;
-
- /**
- * @property {number} priorityID - The PriorityID controls which Sprite receives an Input event first if they should overlap.
- * @default
- */
- this.priorityID = 0;
-
- /**
- * @property {boolean} useHandCursor - On a desktop browser you can set the 'hand' cursor to appear when moving over the Sprite.
- * @default
- */
- this.useHandCursor = false;
-
- /**
- * @property {boolean} isDragged - true if the Sprite is being currently dragged.
- * @default
- */
- this.isDragged = false;
-
- /**
- * @property {boolean} allowHorizontalDrag - Controls if the Sprite is allowed to be dragged horizontally.
- * @default
- */
- this.allowHorizontalDrag = true;
-
- /**
- * @property {boolean} allowVerticalDrag - Controls if the Sprite is allowed to be dragged vertically.
- * @default
- */
- this.allowVerticalDrag = true;
-
- /**
- * @property {boolean} bringToTop - If true when this Sprite is clicked or dragged it will automatically be bought to the top of the Group it is within.
- * @default
- */
- this.bringToTop = false;
-
- /**
- * @property {Phaser.Point} snapOffset - A Point object that contains by how far the Sprite snap is offset.
- * @default
- */
- this.snapOffset = null;
-
- /**
- * @property {boolean} snapOnDrag - When the Sprite is dragged this controls if the center of the Sprite will snap to the pointer on drag or not.
- * @default
- */
- this.snapOnDrag = false;
-
- /**
- * @property {boolean} snapOnRelease - When the Sprite is dragged this controls if the Sprite will be snapped on release.
- * @default
- */
- this.snapOnRelease = false;
-
- /**
- * @property {number} snapX - When a Sprite has snapping enabled this holds the width of the snap grid.
- * @default
- */
- this.snapX = 0;
-
- /**
- * @property {number} snapY - When a Sprite has snapping enabled this holds the height of the snap grid.
- * @default
- */
- this.snapY = 0;
-
- /**
- * @property {number} snapOffsetX - This defines the top-left X coordinate of the snap grid.
- * @default
- */
- this.snapOffsetX = 0;
-
- /**
- * @property {number} snapOffsetY - This defines the top-left Y coordinate of the snap grid..
- * @default
- */
- this.snapOffsetY = 0;
-
- /**
- * @property {number} pixelPerfect - Should we use pixel perfect hit detection? Warning: expensive. Only enable if you really need it!
- * @default
- */
- this.pixelPerfect = false;
-
- /**
- * @property {number} pixelPerfectAlpha - The alpha tolerance threshold. If the alpha value of the pixel matches or is above this value, it's considered a hit.
- * @default
- */
- this.pixelPerfectAlpha = 255;
-
- /**
- * @property {boolean} draggable - Is this sprite allowed to be dragged by the mouse? true = yes, false = no
- * @default
- */
- this.draggable = false;
-
- /**
- * @property {Phaser.Rectangle} boundsRect - A region of the game world within which the sprite is restricted during drag.
- * @default
- */
- this.boundsRect = null;
-
- /**
- * @property {Phaser.Sprite} boundsSprite - A Sprite the bounds of which this sprite is restricted during drag.
- * @default
- */
- this.boundsSprite = null;
-
- /**
- * If this object is set to consume the pointer event then it will stop all propogation from this object on.
- * For example if you had a stack of 6 sprites with the same priority IDs and one consumed the event, none of the others would receive it.
- * @property {boolean} consumePointerEvent
- * @default
- */
- this.consumePointerEvent = false;
-
- /**
- * @property {Phaser.Point} _tempPoint - Description.
- * @private
- */
- this._tempPoint = new Phaser.Point();
-
- this._pointerData = [];
-
- this._pointerData.push({
- id: 0,
- x: 0,
- y: 0,
- isDown: false,
- isUp: false,
- isOver: false,
- isOut: false,
- timeOver: 0,
- timeOut: 0,
- timeDown: 0,
- timeUp: 0,
- downDuration: 0,
- isDragged: false
- });
-
-};
-
-Phaser.InputHandler.prototype = {
-
- /**
- * Starts the Input Handler running. This is called automatically when you enable input on a Sprite, or can be called directly if you need to set a specific priority.
- * @method Phaser.InputHandler#start
- * @param {number} priority - Higher priority sprites take click priority over low-priority sprites when they are stacked on-top of each other.
- * @param {boolean} useHandCursor - If true the Sprite will show the hand cursor on mouse-over (doesn't apply to mobile browsers)
- * @return {Phaser.Sprite} The Sprite object to which the Input Handler is bound.
- */
- start: function (priority, useHandCursor) {
-
- priority = priority || 0;
- if (typeof useHandCursor == 'undefined') { useHandCursor = false; }
-
- // Turning on
- if (this.enabled === false)
- {
- // Register, etc
- this.game.input.interactiveItems.add(this);
- this.useHandCursor = useHandCursor;
- this.priorityID = priority;
-
- for (var i = 0; i < 10; i++)
- {
- this._pointerData[i] = {
- id: i,
- x: 0,
- y: 0,
- isDown: false,
- isUp: false,
- isOver: false,
- isOut: false,
- timeOver: 0,
- timeOut: 0,
- timeDown: 0,
- timeUp: 0,
- downDuration: 0,
- isDragged: false
- };
- }
-
- this.snapOffset = new Phaser.Point();
- this.enabled = true;
-
- // Create the signals the Input component will emit
- if (this.sprite.events && this.sprite.events.onInputOver == null)
- {
- this.sprite.events.onInputOver = new Phaser.Signal();
- this.sprite.events.onInputOut = new Phaser.Signal();
- this.sprite.events.onInputDown = new Phaser.Signal();
- this.sprite.events.onInputUp = new Phaser.Signal();
- this.sprite.events.onDragStart = new Phaser.Signal();
- this.sprite.events.onDragStop = new Phaser.Signal();
- }
- }
-
- return this.sprite;
-
- },
-
- /**
- * Resets the Input Handler and disables it.
- * @method Phaser.InputHandler#reset
- */
- reset: function () {
-
- this.enabled = false;
-
- for (var i = 0; i < 10; i++)
- {
- this._pointerData[i] = {
- id: i,
- x: 0,
- y: 0,
- isDown: false,
- isUp: false,
- isOver: false,
- isOut: false,
- timeOver: 0,
- timeOut: 0,
- timeDown: 0,
- timeUp: 0,
- downDuration: 0,
- isDragged: false
- };
- }
- },
-
- /**
- * Stops the Input Handler from running.
- * @method Phaser.InputHandler#stop
- */
- stop: function () {
-
- // Turning off
- if (this.enabled === false)
- {
- return;
- }
- else
- {
- // De-register, etc
- this.enabled = false;
- this.game.input.interactiveItems.remove(this);
- }
-
- },
-
- /**
- * Clean up memory.
- * @method Phaser.InputHandler#destroy
- */
- destroy: function () {
-
- if (this.enabled)
- {
- this.enabled = false;
-
- this.game.input.interactiveItems.remove(this);
-
- this.stop();
-
- this.sprite = null;
- }
- },
-
- /**
- * The x coordinate of the Input pointer, relative to the top-left of the parent Sprite.
- * This value is only set when the pointer is over this Sprite.
- * @method Phaser.InputHandler#pointerX
- * @param {Phaser.Pointer} pointer
- * @return {number} The x coordinate of the Input pointer.
- */
- pointerX: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].x;
-
- },
-
- /**
- * The y coordinate of the Input pointer, relative to the top-left of the parent Sprite
- * This value is only set when the pointer is over this Sprite.
- * @method Phaser.InputHandler#pointerY
- * @param {Phaser.Pointer} pointer
- * @return {number} The y coordinate of the Input pointer.
- */
- pointerY: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].y;
-
- },
-
- /**
- * If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true.
- * @method Phaser.InputHandler#pointerDown
- * @param {Phaser.Pointer} pointer
- * @return {boolean}
- */
- pointerDown: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].isDown;
-
- },
-
- /**
- * If the Pointer is not touching the touchscreen, or the mouse button is up, isUp is set to true
- * @method Phaser.InputHandler#pointerUp
- * @param {Phaser.Pointer} pointer
- * @return {boolean}
- */
- pointerUp: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].isUp;
-
- },
-
- /**
- * A timestamp representing when the Pointer first touched the touchscreen.
- * @method Phaser.InputHandler#pointerTimeDown
- * @param {Phaser.Pointer} pointer
- * @return {number}
- */
- pointerTimeDown: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].timeDown;
-
- },
-
- /**
- * A timestamp representing when the Pointer left the touchscreen.
- * @method Phaser.InputHandler#pointerTimeUp
- * @param {Phaser.Pointer} pointer
- * @return {number}
- */
- pointerTimeUp: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].timeUp;
-
- },
-
- /**
- * Is the Pointer over this Sprite?
- * @method Phaser.InputHandler#pointerOver
- * @param {number} [index] - The ID number of a Pointer to check. If you don't provide a number it will check all Pointers.
- * @return {boolean} True if the given pointer (if a index was given, or any pointer if not) is over this object.
- */
- pointerOver: function (index) {
-
- if (this.enabled)
- {
- if (typeof index === 'undefined')
- {
- for (var i = 0; i < 10; i++)
- {
- if (this._pointerData[i].isOver)
- {
- return true;
- }
- }
- }
- else
- {
- return this._pointerData[index].isOver;
- }
- }
-
- return false;
-
- },
-
- /**
- * Is the Pointer outside of this Sprite?
- * @method Phaser.InputHandler#pointerOut
- * @param {number} [index] - The ID number of a Pointer to check. If you don't provide a number it will check all Pointers.
- * @return {boolean} True if the given pointer (if a index was given, or any pointer if not) is out of this object.
- */
- pointerOut: function (index) {
-
- if (this.enabled)
- {
- if (typeof index === 'undefined')
- {
- for (var i = 0; i < 10; i++)
- {
- if (this._pointerData[i].isOut)
- {
- return true;
- }
- }
- }
- else
- {
- return this._pointerData[index].isOut;
- }
- }
-
- return false;
-
- },
-
- /**
- * A timestamp representing when the Pointer first touched the touchscreen.
- * @method Phaser.InputHandler#pointerTimeOver
- * @param {Phaser.Pointer} pointer
- * @return {number}
- */
- pointerTimeOver: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].timeOver;
-
- },
-
- /**
- * A timestamp representing when the Pointer left the touchscreen.
- * @method Phaser.InputHandler#pointerTimeOut
- * @param {Phaser.Pointer} pointer
- * @return {number}
- */
- pointerTimeOut: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].timeOut;
-
- },
-
- /**
- * Is this sprite being dragged by the mouse or not?
- * @method Phaser.InputHandler#pointerTimeOut
- * @param {Phaser.Pointer} pointer
- * @return {number}
- */
- pointerDragged: function (pointer) {
-
- pointer = pointer || 0;
-
- return this._pointerData[pointer].isDragged;
-
- },
-
- /**
- * Checks if the given pointer is over this Sprite.
- * @method Phaser.InputHandler#checkPointerOver
- * @param {Phaser.Pointer} pointer
- * @return {boolean}
- */
- checkPointerOver: function (pointer) {
-
- if (this.enabled === false || this.sprite.visible === false || (this.sprite.group && this.sprite.group.visible === false))
- {
- return false;
- }
-
- this.sprite.getLocalUnmodifiedPosition(this._tempPoint, pointer.x, pointer.y);
-
- if (this._tempPoint.x >= 0 && this._tempPoint.x <= this.sprite.currentFrame.width && this._tempPoint.y >= 0 && this._tempPoint.y <= this.sprite.currentFrame.height)
- {
- if (this.pixelPerfect)
- {
- return this.checkPixel(this._tempPoint.x, this._tempPoint.y);
- }
- else
- {
- return true;
- }
- }
-
- },
-
- /**
- * Runs a pixel perfect check against the given x/y coordinates of the Sprite this InputHandler is bound to.
- * It compares the alpha value of the pixel and if >= InputHandler.pixelPerfectAlpha it returns true.
- * @method Phaser.InputHandler#checkPixel
- * @param {number} x - The x coordinate to check.
- * @param {number} y - The y coordinate to check.
- * @return {boolean} true if there is the alpha of the pixel is >= InputHandler.pixelPerfectAlpha
- */
- checkPixel: function (x, y) {
-
- // Grab a pixel from our image into the hitCanvas and then test it
- if (this.sprite.texture.baseTexture.source)
- {
- this.game.input.hitContext.clearRect(0, 0, 1, 1);
-
- x += this.sprite.texture.frame.x;
- y += this.sprite.texture.frame.y;
-
- this.game.input.hitContext.drawImage(this.sprite.texture.baseTexture.source, x, y, 1, 1, 0, 0, 1, 1);
-
- var rgb = this.game.input.hitContext.getImageData(0, 0, 1, 1);
-
- if (rgb.data[3] >= this.pixelPerfectAlpha)
- {
- return true;
- }
- }
-
- return false;
-
- },
-
- /**
- * Update.
- * @method Phaser.InputHandler#update
- * @param {Phaser.Pointer} pointer
- */
- update: function (pointer) {
-
- if (this.enabled === false || this.sprite.visible === false || (this.sprite.group && this.sprite.group.visible === false))
- {
- this._pointerOutHandler(pointer);
- return false;
- }
-
- if (this.draggable && this._draggedPointerID == pointer.id)
- {
- return this.updateDrag(pointer);
- }
- else if (this._pointerData[pointer.id].isOver === true)
- {
- if (this.checkPointerOver(pointer))
- {
- this._pointerData[pointer.id].x = pointer.x - this.sprite.x;
- this._pointerData[pointer.id].y = pointer.y - this.sprite.y;
- return true;
- }
- else
- {
- this._pointerOutHandler(pointer);
- return false;
- }
- }
- },
-
- /**
- * Internal method handling the pointer over event.
- * @method Phaser.InputHandler#_pointerOverHandler
- * @private
- * @param {Phaser.Pointer} pointer
- */
- _pointerOverHandler: function (pointer) {
-
- if (this._pointerData[pointer.id].isOver === false)
- {
- this._pointerData[pointer.id].isOver = true;
- this._pointerData[pointer.id].isOut = false;
- this._pointerData[pointer.id].timeOver = this.game.time.now;
- this._pointerData[pointer.id].x = pointer.x - this.sprite.x;
- this._pointerData[pointer.id].y = pointer.y - this.sprite.y;
-
- if (this.useHandCursor && this._pointerData[pointer.id].isDragged === false)
- {
- this.game.canvas.style.cursor = "pointer";
- }
-
- this.sprite.events.onInputOver.dispatch(this.sprite, pointer);
- }
- },
-
- /**
- * Internal method handling the pointer out event.
- * @method Phaser.InputHandler#_pointerOutHandler
- * @private
- * @param {Phaser.Pointer} pointer
- */
- _pointerOutHandler: function (pointer) {
-
- this._pointerData[pointer.id].isOver = false;
- this._pointerData[pointer.id].isOut = true;
- this._pointerData[pointer.id].timeOut = this.game.time.now;
-
- if (this.useHandCursor && this._pointerData[pointer.id].isDragged === false)
- {
- this.game.canvas.style.cursor = "default";
- }
-
- if (this.sprite && this.sprite.events)
- {
- this.sprite.events.onInputOut.dispatch(this.sprite, pointer);
- }
-
- },
-
- /**
- * Internal method handling the touched event.
- * @method Phaser.InputHandler#_touchedHandler
- * @private
- * @param {Phaser.Pointer} pointer
- */
- _touchedHandler: function (pointer) {
-
- if (this._pointerData[pointer.id].isDown === false && this._pointerData[pointer.id].isOver === true)
- {
- this._pointerData[pointer.id].isDown = true;
- this._pointerData[pointer.id].isUp = false;
- this._pointerData[pointer.id].timeDown = this.game.time.now;
- this.sprite.events.onInputDown.dispatch(this.sprite, pointer);
-
- // Start drag
- if (this.draggable && this.isDragged === false)
- {
- this.startDrag(pointer);
- }
-
- if (this.bringToTop)
- {
- this.sprite.bringToTop();
- }
- }
-
- // Consume the event?
- return this.consumePointerEvent;
-
- },
-
- /**
- * Internal method handling the pointer released event.
- * @method Phaser.InputHandler#_releasedHandler
- * @private
- * @param {Phaser.Pointer} pointer
- */
- _releasedHandler: function (pointer) {
-
- // If was previously touched by this Pointer, check if still is AND still over this item
- if (this._pointerData[pointer.id].isDown && pointer.isUp)
- {
- this._pointerData[pointer.id].isDown = false;
- this._pointerData[pointer.id].isUp = true;
- this._pointerData[pointer.id].timeUp = this.game.time.now;
- this._pointerData[pointer.id].downDuration = this._pointerData[pointer.id].timeUp - this._pointerData[pointer.id].timeDown;
-
- // Only release the InputUp signal if the pointer is still over this sprite
- if (this.checkPointerOver(pointer))
- {
- // Release the inputUp signal and provide optional parameter if pointer is still over the sprite or not
- this.sprite.events.onInputUp.dispatch(this.sprite, pointer, true);
- }
- else
- {
- // Release the inputUp signal and provide optional parameter if pointer is still over the sprite or not
- this.sprite.events.onInputUp.dispatch(this.sprite, pointer, false);
-
- // Pointer outside the sprite? Reset the cursor
- if (this.useHandCursor)
- {
- this.game.canvas.style.cursor = "default";
- }
- }
-
- // Stop drag
- if (this.draggable && this.isDragged && this._draggedPointerID == pointer.id)
- {
- this.stopDrag(pointer);
- }
- }
-
- },
-
- /**
- * Updates the Pointer drag on this Sprite.
- * @method Phaser.InputHandler#updateDrag
- * @param {Phaser.Pointer} pointer
- * @return {boolean}
- */
- updateDrag: function (pointer) {
-
- if (pointer.isUp)
- {
- this.stopDrag(pointer);
- return false;
- }
-
- if (this.sprite.fixedToCamera)
- {
- if (this.allowHorizontalDrag)
- {
- this.sprite.cameraOffset.x = pointer.x + this._dragPoint.x + this.dragOffset.x;
- }
-
- if (this.allowVerticalDrag)
- {
- this.sprite.cameraOffset.y = pointer.y + this._dragPoint.y + this.dragOffset.y;
- }
-
- if (this.boundsRect)
- {
- this.checkBoundsRect();
- }
-
- if (this.boundsSprite)
- {
- this.checkBoundsSprite();
- }
-
- if (this.snapOnDrag)
- {
- this.sprite.cameraOffset.x = Math.round((this.sprite.cameraOffset.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
- this.sprite.cameraOffset.y = Math.round((this.sprite.cameraOffset.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
- }
- }
- else
- {
- if (this.allowHorizontalDrag)
- {
- this.sprite.x = pointer.x + this._dragPoint.x + this.dragOffset.x;
- }
-
- if (this.allowVerticalDrag)
- {
- this.sprite.y = pointer.y + this._dragPoint.y + this.dragOffset.y;
- }
-
- if (this.boundsRect)
- {
- this.checkBoundsRect();
- }
-
- if (this.boundsSprite)
- {
- this.checkBoundsSprite();
- }
-
- if (this.snapOnDrag)
- {
- this.sprite.x = Math.round((this.sprite.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
- this.sprite.y = Math.round((this.sprite.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
- }
- }
-
- return true;
-
- },
-
- /**
- * Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second)
- * @method Phaser.InputHandler#justOver
- * @param {Phaser.Pointer} pointer
- * @param {number} delay - The time below which the pointer is considered as just over.
- * @return {boolean}
- */
- justOver: function (pointer, delay) {
-
- pointer = pointer || 0;
- delay = delay || 500;
-
- return (this._pointerData[pointer].isOver && this.overDuration(pointer) < delay);
-
- },
-
- /**
- * Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second)
- * @method Phaser.InputHandler#justOut
- * @param {Phaser.Pointer} pointer
- * @param {number} delay - The time below which the pointer is considered as just out.
- * @return {boolean}
- */
- justOut: function (pointer, delay) {
-
- pointer = pointer || 0;
- delay = delay || 500;
-
- return (this._pointerData[pointer].isOut && (this.game.time.now - this._pointerData[pointer].timeOut < delay));
-
- },
-
- /**
- * Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second)
- * @method Phaser.InputHandler#justPressed
- * @param {Phaser.Pointer} pointer
- * @param {number} delay - The time below which the pointer is considered as just over.
- * @return {boolean}
- */
- justPressed: function (pointer, delay) {
-
- pointer = pointer || 0;
- delay = delay || 500;
-
- return (this._pointerData[pointer].isDown && this.downDuration(pointer) < delay);
-
- },
-
- /**
- * Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second)
- * @method Phaser.InputHandler#justReleased
- * @param {Phaser.Pointer} pointer
- * @param {number} delay - The time below which the pointer is considered as just out.
- * @return {boolean}
- */
- justReleased: function (pointer, delay) {
-
- pointer = pointer || 0;
- delay = delay || 500;
-
- return (this._pointerData[pointer].isUp && (this.game.time.now - this._pointerData[pointer].timeUp < delay));
-
- },
-
- /**
- * If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds.
- * @method Phaser.InputHandler#overDuration
- * @param {Phaser.Pointer} pointer
- * @return {number} The number of milliseconds the pointer has been over the Sprite, or -1 if not over.
- */
- overDuration: function (pointer) {
-
- pointer = pointer || 0;
-
- if (this._pointerData[pointer].isOver)
- {
- return this.game.time.now - this._pointerData[pointer].timeOver;
- }
-
- return -1;
-
- },
-
- /**
- * If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds.
- * @method Phaser.InputHandler#downDuration
- * @param {Phaser.Pointer} pointer
- * @return {number} The number of milliseconds the pointer has been pressed down on the Sprite, or -1 if not over.
- */
- downDuration: function (pointer) {
-
- pointer = pointer || 0;
-
- if (this._pointerData[pointer].isDown)
- {
- return this.game.time.now - this._pointerData[pointer].timeDown;
- }
-
- return -1;
-
- },
-
- /**
- * Make this Sprite draggable by the mouse. You can also optionally set mouseStartDragCallback and mouseStopDragCallback
- * @method Phaser.InputHandler#enableDrag
- * @param {boolean} [lockCenter=false] - If false the Sprite will drag from where you click it minus the dragOffset. If true it will center itself to the tip of the mouse pointer.
- * @param {boolean} [bringToTop=false] - If true the Sprite will be bought to the top of the rendering list in its current Group.
- * @param {boolean} [pixelPerfect=false] - If true it will use a pixel perfect test to see if you clicked the Sprite. False uses the bounding box.
- * @param {boolean} [alphaThreshold=255] - If using pixel perfect collision this specifies the alpha level from 0 to 255 above which a collision is processed.
- * @param {Phaser.Rectangle} [boundsRect=null] - If you want to restrict the drag of this sprite to a specific Rectangle, pass the Phaser.Rectangle here, otherwise it's free to drag anywhere.
- * @param {Phaser.Sprite} [boundsSprite=null] - If you want to restrict the drag of this sprite to within the bounding box of another sprite, pass it here.
- */
- enableDrag: function (lockCenter, bringToTop, pixelPerfect, alphaThreshold, boundsRect, boundsSprite) {
-
- if (typeof lockCenter == 'undefined') { lockCenter = false; }
- if (typeof bringToTop == 'undefined') { bringToTop = false; }
- if (typeof pixelPerfect == 'undefined') { pixelPerfect = false; }
- if (typeof alphaThreshold == 'undefined') { alphaThreshold = 255; }
- if (typeof boundsRect == 'undefined') { boundsRect = null; }
- if (typeof boundsSprite == 'undefined') { boundsSprite = null; }
-
- this._dragPoint = new Phaser.Point();
- this.draggable = true;
- this.bringToTop = bringToTop;
- this.dragOffset = new Phaser.Point();
- this.dragFromCenter = lockCenter;
-
- this.pixelPerfect = pixelPerfect;
- this.pixelPerfectAlpha = alphaThreshold;
-
- if (boundsRect)
- {
- this.boundsRect = boundsRect;
- }
-
- if (boundsSprite)
- {
- this.boundsSprite = boundsSprite;
- }
-
- },
-
- /**
- * Stops this sprite from being able to be dragged. If it is currently the target of an active drag it will be stopped immediately. Also disables any set callbacks.
- * @method Phaser.InputHandler#disableDrag
- */
- disableDrag: function () {
-
- if (this._pointerData)
- {
- for (var i = 0; i < 10; i++)
- {
- this._pointerData[i].isDragged = false;
- }
- }
-
- this.draggable = false;
- this.isDragged = false;
- this._draggedPointerID = -1;
-
- },
-
- /**
- * Called by Pointer when drag starts on this Sprite. Should not usually be called directly.
- * @method Phaser.InputHandler#startDrag
- * @param {Phaser.Pointer} pointer
- */
- startDrag: function (pointer) {
-
- this.isDragged = true;
- this._draggedPointerID = pointer.id;
- this._pointerData[pointer.id].isDragged = true;
-
- if (this.sprite.fixedToCamera)
- {
- if (this.dragFromCenter)
- {
- this.sprite.centerOn(pointer.x, pointer.y);
- this._dragPoint.setTo(this.sprite.cameraOffset.x - pointer.x, this.sprite.cameraOffset.y - pointer.y);
- }
- else
- {
- this._dragPoint.setTo(this.sprite.cameraOffset.x - pointer.x, this.sprite.cameraOffset.y - pointer.y);
- }
- }
- else
- {
- if (this.dragFromCenter)
- {
- this.sprite.centerOn(pointer.x, pointer.y);
- this._dragPoint.setTo(this.sprite.x - pointer.x, this.sprite.y - pointer.y);
- }
- else
- {
- this._dragPoint.setTo(this.sprite.x - pointer.x, this.sprite.y - pointer.y);
- }
- }
-
- this.updateDrag(pointer);
-
- if (this.bringToTop)
- {
- this.sprite.bringToTop();
- }
-
- this.sprite.events.onDragStart.dispatch(this.sprite, pointer);
-
- },
-
- /**
- * Called by Pointer when drag is stopped on this Sprite. Should not usually be called directly.
- * @method Phaser.InputHandler#stopDrag
- * @param {Phaser.Pointer} pointer
- */
- stopDrag: function (pointer) {
-
- this.isDragged = false;
- this._draggedPointerID = -1;
- this._pointerData[pointer.id].isDragged = false;
-
- if (this.snapOnRelease)
- {
- if (this.sprite.fixedToCamera)
- {
- this.sprite.cameraOffset.x = Math.round((this.sprite.cameraOffset.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
- this.sprite.cameraOffset.y = Math.round((this.sprite.cameraOffset.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
- }
- else
- {
- this.sprite.x = Math.round((this.sprite.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
- this.sprite.y = Math.round((this.sprite.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
- }
- }
-
- this.sprite.events.onDragStop.dispatch(this.sprite, pointer);
- this.sprite.events.onInputUp.dispatch(this.sprite, pointer);
-
- if (this.checkPointerOver(pointer) === false)
- {
- this._pointerOutHandler(pointer);
- }
-
- },
-
- /**
- * Restricts this sprite to drag movement only on the given axis. Note: If both are set to false the sprite will never move!
- * @method Phaser.InputHandler#setDragLock
- * @param {boolean} [allowHorizontal=true] - To enable the sprite to be dragged horizontally set to true, otherwise false.
- * @param {boolean} [allowVertical=true] - To enable the sprite to be dragged vertically set to true, otherwise false.
- */
- setDragLock: function (allowHorizontal, allowVertical) {
-
- if (typeof allowHorizontal == 'undefined') { allowHorizontal = true; }
- if (typeof allowVertical == 'undefined') { allowVertical = true; }
-
- this.allowHorizontalDrag = allowHorizontal;
- this.allowVerticalDrag = allowVertical;
-
- },
-
- /**
- * Make this Sprite snap to the given grid either during drag or when it's released.
- * For example 16x16 as the snapX and snapY would make the sprite snap to every 16 pixels.
- * @method Phaser.InputHandler#enableSnap
- * @param {number} snapX - The width of the grid cell to snap to.
- * @param {number} snapY - The height of the grid cell to snap to.
- * @param {boolean} [onDrag=true] - If true the sprite will snap to the grid while being dragged.
- * @param {boolean} [onRelease=false] - If true the sprite will snap to the grid when released.
- * @param {number} [snapOffsetX=0] - Used to offset the top-left starting point of the snap grid.
- * @param {number} [snapOffsetX=0] - Used to offset the top-left starting point of the snap grid.
- */
- enableSnap: function (snapX, snapY, onDrag, onRelease, snappOffsetX, snappOffsetY) {
-
- if (typeof onDrag == 'undefined') { onDrag = true; }
- if (typeof onRelease == 'undefined') { onRelease = false; }
- if (typeof snapOffsetX == 'undefined') { snapOffsetX = 0; }
- if (typeof snapOffsetY == 'undefined') { snapOffsetY = 0; }
-
- this.snapX = snapX;
- this.snapY = snapY;
- this.snapOffsetX = snapOffsetX;
- this.snapOffsetY = snapOffsetY;
- this.snapOnDrag = onDrag;
- this.snapOnRelease = onRelease;
-
- },
-
- /**
- * Stops the sprite from snapping to a grid during drag or release.
- * @method Phaser.InputHandler#disableSnap
- */
- disableSnap: function () {
-
- this.snapOnDrag = false;
- this.snapOnRelease = false;
-
- },
-
- /**
- * Bounds Rect check for the sprite drag
- * @method Phaser.InputHandler#checkBoundsRect
- */
- checkBoundsRect: function () {
-
- if (this.sprite.fixedToCamera)
- {
- if (this.sprite.cameraOffset.x < this.boundsRect.left)
- {
- this.sprite.cameraOffset.x = this.boundsRect.cameraOffset.x;
- }
- else if ((this.sprite.cameraOffset.x + this.sprite.width) > this.boundsRect.right)
- {
- this.sprite.cameraOffset.x = this.boundsRect.right - this.sprite.width;
- }
-
- if (this.sprite.cameraOffset.y < this.boundsRect.top)
- {
- this.sprite.cameraOffset.y = this.boundsRect.top;
- }
- else if ((this.sprite.cameraOffset.y + this.sprite.height) > this.boundsRect.bottom)
- {
- this.sprite.cameraOffset.y = this.boundsRect.bottom - this.sprite.height;
- }
- }
- else
- {
- if (this.sprite.x < this.boundsRect.left)
- {
- this.sprite.x = this.boundsRect.x;
- }
- else if ((this.sprite.x + this.sprite.width) > this.boundsRect.right)
- {
- this.sprite.x = this.boundsRect.right - this.sprite.width;
- }
-
- if (this.sprite.y < this.boundsRect.top)
- {
- this.sprite.y = this.boundsRect.top;
- }
- else if ((this.sprite.y + this.sprite.height) > this.boundsRect.bottom)
- {
- this.sprite.y = this.boundsRect.bottom - this.sprite.height;
- }
- }
-
- },
-
- /**
- * Parent Sprite Bounds check for the sprite drag.
- * @method Phaser.InputHandler#checkBoundsSprite
- */
- checkBoundsSprite: function () {
-
- if (this.sprite.fixedToCamera && this.boundsSprite.fixedToCamera)
- {
- if (this.sprite.cameraOffset.x < this.boundsSprite.camerOffset.x)
- {
- this.sprite.cameraOffset.x = this.boundsSprite.camerOffset.x;
- }
- else if ((this.sprite.cameraOffset.x + this.sprite.width) > (this.boundsSprite.camerOffset.x + this.boundsSprite.width))
- {
- this.sprite.cameraOffset.x = (this.boundsSprite.camerOffset.x + this.boundsSprite.width) - this.sprite.width;
- }
-
- if (this.sprite.cameraOffset.y < this.boundsSprite.camerOffset.y)
- {
- this.sprite.cameraOffset.y = this.boundsSprite.camerOffset.y;
- }
- else if ((this.sprite.cameraOffset.y + this.sprite.height) > (this.boundsSprite.camerOffset.y + this.boundsSprite.height))
- {
- this.sprite.cameraOffset.y = (this.boundsSprite.camerOffset.y + this.boundsSprite.height) - this.sprite.height;
- }
- }
- else
- {
- if (this.sprite.x < this.boundsSprite.x)
- {
- this.sprite.x = this.boundsSprite.x;
- }
- else if ((this.sprite.x + this.sprite.width) > (this.boundsSprite.x + this.boundsSprite.width))
- {
- this.sprite.x = (this.boundsSprite.x + this.boundsSprite.width) - this.sprite.width;
- }
-
- if (this.sprite.y < this.boundsSprite.y)
- {
- this.sprite.y = this.boundsSprite.y;
- }
- else if ((this.sprite.y + this.sprite.height) > (this.boundsSprite.y + this.boundsSprite.height))
- {
- this.sprite.y = (this.boundsSprite.y + this.boundsSprite.height) - this.sprite.height;
- }
- }
-
- }
-
-};
-
-Phaser.InputHandler.prototype.constructor = Phaser.InputHandler;
-
/**
* @author @karlmacklin
* @copyright 2014 Photon Storm Ltd.
@@ -17902,7 +30127,7 @@ Phaser.Gamepad = function (game) {
* @property {boolean} _gamepadSupportAvailable - Are gamepads supported in this browser or not?
* @private
*/
- this._gamepadSupportAvailable = !!navigator.webkitGetGamepads || !!navigator.webkitGamepads || (navigator.userAgent.indexOf('Firefox/') != -1);
+ this._gamepadSupportAvailable = !!navigator.webkitGetGamepads || !!navigator.webkitGamepads || (navigator.userAgent.indexOf('Firefox/') != -1) || !!navigator.getGamepads;
/**
* Used to check for differences between earlier polls and current state of gamepads.
@@ -18055,7 +30280,7 @@ Phaser.Gamepad.prototype = {
*/
_pollGamepads: function () {
- var rawGamepads = (navigator.webkitGetGamepads && navigator.webkitGetGamepads()) || navigator.webkitGamepads;
+ var rawGamepads = (navigator.webkitGetGamepads && navigator.webkitGetGamepads()) || navigator.webkitGamepads || navigator.getGamepads;
if (rawGamepads)
{
@@ -19164,6 +31389,1291 @@ Phaser.GamepadButton.prototype.constructor = Phaser.GamepadButton;
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
+/**
+* The Input Handler is bound to a specific Sprite and is responsible for managing all Input events on that Sprite.
+* @class Phaser.InputHandler
+* @constructor
+* @param {Phaser.Sprite} sprite - The Sprite object to which this Input Handler belongs.
+*/
+Phaser.InputHandler = function (sprite) {
+
+ /**
+ * @property {Phaser.Sprite} sprite - The Sprite object to which this Input Handler belongs.
+ */
+ this.sprite = sprite;
+
+ /**
+ * @property {Phaser.Game} game - A reference to the currently running game.
+ */
+ this.game = sprite.game;
+
+ /**
+ * @property {boolean} enabled - If enabled the Input Handler will process input requests and monitor pointer activity.
+ * @default
+ */
+ this.enabled = false;
+
+ /**
+ * @property {number} priorityID - The PriorityID controls which Sprite receives an Input event first if they should overlap.
+ * @default
+ */
+ this.priorityID = 0;
+
+ /**
+ * @property {boolean} useHandCursor - On a desktop browser you can set the 'hand' cursor to appear when moving over the Sprite.
+ * @default
+ */
+ this.useHandCursor = false;
+
+ /**
+ * @property {boolean} isDragged - true if the Sprite is being currently dragged.
+ * @default
+ */
+ this.isDragged = false;
+
+ /**
+ * @property {boolean} allowHorizontalDrag - Controls if the Sprite is allowed to be dragged horizontally.
+ * @default
+ */
+ this.allowHorizontalDrag = true;
+
+ /**
+ * @property {boolean} allowVerticalDrag - Controls if the Sprite is allowed to be dragged vertically.
+ * @default
+ */
+ this.allowVerticalDrag = true;
+
+ /**
+ * @property {boolean} bringToTop - If true when this Sprite is clicked or dragged it will automatically be bought to the top of the Group it is within.
+ * @default
+ */
+ this.bringToTop = false;
+
+ /**
+ * @property {Phaser.Point} snapOffset - A Point object that contains by how far the Sprite snap is offset.
+ * @default
+ */
+ this.snapOffset = null;
+
+ /**
+ * @property {boolean} snapOnDrag - When the Sprite is dragged this controls if the center of the Sprite will snap to the pointer on drag or not.
+ * @default
+ */
+ this.snapOnDrag = false;
+
+ /**
+ * @property {boolean} snapOnRelease - When the Sprite is dragged this controls if the Sprite will be snapped on release.
+ * @default
+ */
+ this.snapOnRelease = false;
+
+ /**
+ * @property {number} snapX - When a Sprite has snapping enabled this holds the width of the snap grid.
+ * @default
+ */
+ this.snapX = 0;
+
+ /**
+ * @property {number} snapY - When a Sprite has snapping enabled this holds the height of the snap grid.
+ * @default
+ */
+ this.snapY = 0;
+
+ /**
+ * @property {number} snapOffsetX - This defines the top-left X coordinate of the snap grid.
+ * @default
+ */
+ this.snapOffsetX = 0;
+
+ /**
+ * @property {number} snapOffsetY - This defines the top-left Y coordinate of the snap grid..
+ * @default
+ */
+ this.snapOffsetY = 0;
+
+ /**
+ * Set to true to use pixel perfect hit detection when checking if the pointer is over this Sprite.
+ * The x/y coordinates of the pointer are tested against the image in combination with the InputHandler.pixelPerfectAlpha value.
+ * Warning: This is expensive, especially on mobile (where it's not even needed!) so only enable if required. Also see the less-expensive InputHandler.pixelPerfectClick.
+ * @property {number} pixelPerfectOver - Use a pixel perfect check when testing for pointer over.
+ * @default
+ */
+ this.pixelPerfectOver = false;
+
+ /**
+ * Set to true to use pixel perfect hit detection when checking if the pointer is over this Sprite when it's clicked or touched.
+ * The x/y coordinates of the pointer are tested against the image in combination with the InputHandler.pixelPerfectAlpha value.
+ * Warning: This is expensive so only enable if you really need it.
+ * @property {number} pixelPerfectClick - Use a pixel perfect check when testing for clicks or touches on the Sprite.
+ * @default
+ */
+ this.pixelPerfectClick = false;
+
+ /**
+ * @property {number} pixelPerfectAlpha - The alpha tolerance threshold. If the alpha value of the pixel matches or is above this value, it's considered a hit.
+ * @default
+ */
+ this.pixelPerfectAlpha = 255;
+
+ /**
+ * @property {boolean} draggable - Is this sprite allowed to be dragged by the mouse? true = yes, false = no
+ * @default
+ */
+ this.draggable = false;
+
+ /**
+ * @property {Phaser.Rectangle} boundsRect - A region of the game world within which the sprite is restricted during drag.
+ * @default
+ */
+ this.boundsRect = null;
+
+ /**
+ * @property {Phaser.Sprite} boundsSprite - A Sprite the bounds of which this sprite is restricted during drag.
+ * @default
+ */
+ this.boundsSprite = null;
+
+ /**
+ * If this object is set to consume the pointer event then it will stop all propogation from this object on.
+ * For example if you had a stack of 6 sprites with the same priority IDs and one consumed the event, none of the others would receive it.
+ * @property {boolean} consumePointerEvent
+ * @default
+ */
+ this.consumePointerEvent = false;
+
+ /**
+ * @property {Phaser.Point} _tempPoint - Internal cache var.
+ * @private
+ */
+ this._tempPoint = new Phaser.Point();
+
+ /**
+ * @property {array} _pointerData - Internal cache var.
+ * @private
+ */
+ this._pointerData = [];
+
+ this._pointerData.push({
+ id: 0,
+ x: 0,
+ y: 0,
+ isDown: false,
+ isUp: false,
+ isOver: false,
+ isOut: false,
+ timeOver: 0,
+ timeOut: 0,
+ timeDown: 0,
+ timeUp: 0,
+ downDuration: 0,
+ isDragged: false
+ });
+
+};
+
+Phaser.InputHandler.prototype = {
+
+ /**
+ * Starts the Input Handler running. This is called automatically when you enable input on a Sprite, or can be called directly if you need to set a specific priority.
+ * @method Phaser.InputHandler#start
+ * @param {number} priority - Higher priority sprites take click priority over low-priority sprites when they are stacked on-top of each other.
+ * @param {boolean} useHandCursor - If true the Sprite will show the hand cursor on mouse-over (doesn't apply to mobile browsers)
+ * @return {Phaser.Sprite} The Sprite object to which the Input Handler is bound.
+ */
+ start: function (priority, useHandCursor) {
+
+ priority = priority || 0;
+ if (typeof useHandCursor == 'undefined') { useHandCursor = false; }
+
+ // Turning on
+ if (this.enabled === false)
+ {
+ // Register, etc
+ this.game.input.interactiveItems.add(this);
+ this.useHandCursor = useHandCursor;
+ this.priorityID = priority;
+
+ for (var i = 0; i < 10; i++)
+ {
+ this._pointerData[i] = {
+ id: i,
+ x: 0,
+ y: 0,
+ isDown: false,
+ isUp: false,
+ isOver: false,
+ isOut: false,
+ timeOver: 0,
+ timeOut: 0,
+ timeDown: 0,
+ timeUp: 0,
+ downDuration: 0,
+ isDragged: false
+ };
+ }
+
+ this.snapOffset = new Phaser.Point();
+ this.enabled = true;
+
+ // Create the signals the Input component will emit
+ if (this.sprite.events && this.sprite.events.onInputOver === null)
+ {
+ this.sprite.events.onInputOver = new Phaser.Signal();
+ this.sprite.events.onInputOut = new Phaser.Signal();
+ this.sprite.events.onInputDown = new Phaser.Signal();
+ this.sprite.events.onInputUp = new Phaser.Signal();
+ this.sprite.events.onDragStart = new Phaser.Signal();
+ this.sprite.events.onDragStop = new Phaser.Signal();
+ }
+ }
+
+ return this.sprite;
+
+ },
+
+ /**
+ * Resets the Input Handler and disables it.
+ * @method Phaser.InputHandler#reset
+ */
+ reset: function () {
+
+ this.enabled = false;
+
+ for (var i = 0; i < 10; i++)
+ {
+ this._pointerData[i] = {
+ id: i,
+ x: 0,
+ y: 0,
+ isDown: false,
+ isUp: false,
+ isOver: false,
+ isOut: false,
+ timeOver: 0,
+ timeOut: 0,
+ timeDown: 0,
+ timeUp: 0,
+ downDuration: 0,
+ isDragged: false
+ };
+ }
+ },
+
+ /**
+ * Stops the Input Handler from running.
+ * @method Phaser.InputHandler#stop
+ */
+ stop: function () {
+
+ // Turning off
+ if (this.enabled === false)
+ {
+ return;
+ }
+ else
+ {
+ // De-register, etc
+ this.enabled = false;
+ this.game.input.interactiveItems.remove(this);
+ }
+
+ },
+
+ /**
+ * Clean up memory.
+ * @method Phaser.InputHandler#destroy
+ */
+ destroy: function () {
+
+ if (this.enabled)
+ {
+ this.enabled = false;
+
+ this.game.input.interactiveItems.remove(this);
+
+ this._pointerData.length = 0;
+ this.boundsRect = null;
+ this.boundsSprite = null;
+ this.sprite = null;
+ }
+
+ },
+
+ /**
+ * The x coordinate of the Input pointer, relative to the top-left of the parent Sprite.
+ * This value is only set when the pointer is over this Sprite.
+ * @method Phaser.InputHandler#pointerX
+ * @param {Phaser.Pointer} pointer
+ * @return {number} The x coordinate of the Input pointer.
+ */
+ pointerX: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].x;
+
+ },
+
+ /**
+ * The y coordinate of the Input pointer, relative to the top-left of the parent Sprite
+ * This value is only set when the pointer is over this Sprite.
+ * @method Phaser.InputHandler#pointerY
+ * @param {Phaser.Pointer} pointer
+ * @return {number} The y coordinate of the Input pointer.
+ */
+ pointerY: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].y;
+
+ },
+
+ /**
+ * If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true.
+ * @method Phaser.InputHandler#pointerDown
+ * @param {Phaser.Pointer} pointer
+ * @return {boolean}
+ */
+ pointerDown: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].isDown;
+
+ },
+
+ /**
+ * If the Pointer is not touching the touchscreen, or the mouse button is up, isUp is set to true
+ * @method Phaser.InputHandler#pointerUp
+ * @param {Phaser.Pointer} pointer
+ * @return {boolean}
+ */
+ pointerUp: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].isUp;
+
+ },
+
+ /**
+ * A timestamp representing when the Pointer first touched the touchscreen.
+ * @method Phaser.InputHandler#pointerTimeDown
+ * @param {Phaser.Pointer} pointer
+ * @return {number}
+ */
+ pointerTimeDown: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].timeDown;
+
+ },
+
+ /**
+ * A timestamp representing when the Pointer left the touchscreen.
+ * @method Phaser.InputHandler#pointerTimeUp
+ * @param {Phaser.Pointer} pointer
+ * @return {number}
+ */
+ pointerTimeUp: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].timeUp;
+
+ },
+
+ /**
+ * Is the Pointer over this Sprite?
+ * @method Phaser.InputHandler#pointerOver
+ * @param {number} [index] - The ID number of a Pointer to check. If you don't provide a number it will check all Pointers.
+ * @return {boolean} True if the given pointer (if a index was given, or any pointer if not) is over this object.
+ */
+ pointerOver: function (index) {
+
+ if (this.enabled)
+ {
+ if (typeof index === 'undefined')
+ {
+ for (var i = 0; i < 10; i++)
+ {
+ if (this._pointerData[i].isOver)
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ return this._pointerData[index].isOver;
+ }
+ }
+
+ return false;
+
+ },
+
+ /**
+ * Is the Pointer outside of this Sprite?
+ * @method Phaser.InputHandler#pointerOut
+ * @param {number} [index] - The ID number of a Pointer to check. If you don't provide a number it will check all Pointers.
+ * @return {boolean} True if the given pointer (if a index was given, or any pointer if not) is out of this object.
+ */
+ pointerOut: function (index) {
+
+ if (this.enabled)
+ {
+ if (typeof index === 'undefined')
+ {
+ for (var i = 0; i < 10; i++)
+ {
+ if (this._pointerData[i].isOut)
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ return this._pointerData[index].isOut;
+ }
+ }
+
+ return false;
+
+ },
+
+ /**
+ * A timestamp representing when the Pointer first touched the touchscreen.
+ * @method Phaser.InputHandler#pointerTimeOver
+ * @param {Phaser.Pointer} pointer
+ * @return {number}
+ */
+ pointerTimeOver: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].timeOver;
+
+ },
+
+ /**
+ * A timestamp representing when the Pointer left the touchscreen.
+ * @method Phaser.InputHandler#pointerTimeOut
+ * @param {Phaser.Pointer} pointer
+ * @return {number}
+ */
+ pointerTimeOut: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].timeOut;
+
+ },
+
+ /**
+ * Is this sprite being dragged by the mouse or not?
+ * @method Phaser.InputHandler#pointerTimeOut
+ * @param {Phaser.Pointer} pointer
+ * @return {number}
+ */
+ pointerDragged: function (pointer) {
+
+ pointer = pointer || 0;
+
+ return this._pointerData[pointer].isDragged;
+
+ },
+
+ /**
+ * Checks if the given pointer is over this Sprite and can click it.
+ * @method Phaser.InputHandler#checkPointerDown
+ * @param {Phaser.Pointer} pointer
+ * @return {boolean}
+ */
+ checkPointerDown: function (pointer) {
+
+ if (this.enabled === false || this.sprite.visible === false || this.sprite.parent.visible === false)
+ {
+ return false;
+ }
+
+ // Need to pass it a temp point, in case we need it again for the pixel check
+ if (this.game.input.hitTest(this.sprite, pointer, this._tempPoint))
+ {
+ if (this.pixelPerfectClick)
+ {
+ return this.checkPixel(this._tempPoint.x, this._tempPoint.y);
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ return false;
+
+ },
+
+ /**
+ * Checks if the given pointer is over this Sprite.
+ * @method Phaser.InputHandler#checkPointerOver
+ * @param {Phaser.Pointer} pointer
+ * @return {boolean}
+ */
+ checkPointerOver: function (pointer) {
+
+ if (this.enabled === false || this.sprite.visible === false || this.sprite.parent.visible === false)
+ {
+ return false;
+ }
+
+ // Need to pass it a temp point, in case we need it again for the pixel check
+ if (this.game.input.hitTest(this.sprite, pointer, this._tempPoint))
+ {
+ if (this.pixelPerfectOver)
+ {
+ return this.checkPixel(this._tempPoint.x, this._tempPoint.y);
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ return false;
+
+ },
+
+ /**
+ * Runs a pixel perfect check against the given x/y coordinates of the Sprite this InputHandler is bound to.
+ * It compares the alpha value of the pixel and if >= InputHandler.pixelPerfectAlpha it returns true.
+ * @method Phaser.InputHandler#checkPixel
+ * @param {number} x - The x coordinate to check.
+ * @param {number} y - The y coordinate to check.
+ * @param {Phaser.Pointer} [pointer] - The pointer to get the x/y coordinate from if not passed as the first two parameters.
+ * @return {boolean} true if there is the alpha of the pixel is >= InputHandler.pixelPerfectAlpha
+ */
+ checkPixel: function (x, y, pointer) {
+
+ // Grab a pixel from our image into the hitCanvas and then test it
+ if (this.sprite.texture.baseTexture.source)
+ {
+ this.game.input.hitContext.clearRect(0, 0, 1, 1);
+
+ if (x === null && y === null)
+ {
+ // Use the pointer parameter
+ this.game.input.getLocalPosition(this.sprite, pointer, this._tempPoint);
+
+ var x = this._tempPoint.x;
+ var y = this._tempPoint.y;
+ }
+
+ if (this.sprite.anchor.x !== 0)
+ {
+ x -= -this.sprite.texture.frame.width * this.sprite.anchor.x;
+ }
+
+ if (this.sprite.anchor.y !== 0)
+ {
+ y -= -this.sprite.texture.frame.height * this.sprite.anchor.y;
+ }
+
+ x += this.sprite.texture.frame.x;
+ y += this.sprite.texture.frame.y;
+
+ this.game.input.hitContext.drawImage(this.sprite.texture.baseTexture.source, x, y, 1, 1, 0, 0, 1, 1);
+
+ var rgb = this.game.input.hitContext.getImageData(0, 0, 1, 1);
+
+ if (rgb.data[3] >= this.pixelPerfectAlpha)
+ {
+ return true;
+ }
+ }
+
+ return false;
+
+ },
+
+ /**
+ * Update.
+ * @method Phaser.InputHandler#update
+ * @protected
+ * @param {Phaser.Pointer} pointer
+ */
+ update: function (pointer) {
+
+ if (this.sprite === null)
+ {
+ // Abort. We've been destroyed.
+ return;
+ }
+
+ if (this.enabled === false || this.sprite.visible === false || (this.sprite.group && this.sprite.group.visible === false))
+ {
+ this._pointerOutHandler(pointer);
+ return false;
+ }
+
+ if (this.draggable && this._draggedPointerID == pointer.id)
+ {
+ return this.updateDrag(pointer);
+ }
+ else if (this._pointerData[pointer.id].isOver === true)
+ {
+ if (this.checkPointerOver(pointer))
+ {
+ this._pointerData[pointer.id].x = pointer.x - this.sprite.x;
+ this._pointerData[pointer.id].y = pointer.y - this.sprite.y;
+ return true;
+ }
+ else
+ {
+ this._pointerOutHandler(pointer);
+ return false;
+ }
+ }
+ },
+
+ /**
+ * Internal method handling the pointer over event.
+ * @method Phaser.InputHandler#_pointerOverHandler
+ * @private
+ * @param {Phaser.Pointer} pointer
+ */
+ _pointerOverHandler: function (pointer) {
+
+ if (this.sprite === null)
+ {
+ // Abort. We've been destroyed.
+ return;
+ }
+
+ if (this._pointerData[pointer.id].isOver === false)
+ {
+ this._pointerData[pointer.id].isOver = true;
+ this._pointerData[pointer.id].isOut = false;
+ this._pointerData[pointer.id].timeOver = this.game.time.now;
+ this._pointerData[pointer.id].x = pointer.x - this.sprite.x;
+ this._pointerData[pointer.id].y = pointer.y - this.sprite.y;
+
+ if (this.useHandCursor && this._pointerData[pointer.id].isDragged === false)
+ {
+ this.game.canvas.style.cursor = "pointer";
+ }
+
+ this.sprite.events.onInputOver.dispatch(this.sprite, pointer);
+ }
+
+ },
+
+ /**
+ * Internal method handling the pointer out event.
+ * @method Phaser.InputHandler#_pointerOutHandler
+ * @private
+ * @param {Phaser.Pointer} pointer
+ */
+ _pointerOutHandler: function (pointer) {
+
+ if (this.sprite === null)
+ {
+ // Abort. We've been destroyed.
+ return;
+ }
+
+ this._pointerData[pointer.id].isOver = false;
+ this._pointerData[pointer.id].isOut = true;
+ this._pointerData[pointer.id].timeOut = this.game.time.now;
+
+ if (this.useHandCursor && this._pointerData[pointer.id].isDragged === false)
+ {
+ this.game.canvas.style.cursor = "default";
+ }
+
+ if (this.sprite && this.sprite.events)
+ {
+ this.sprite.events.onInputOut.dispatch(this.sprite, pointer);
+ }
+
+ },
+
+ /**
+ * Internal method handling the touched event.
+ * @method Phaser.InputHandler#_touchedHandler
+ * @private
+ * @param {Phaser.Pointer} pointer
+ */
+ _touchedHandler: function (pointer) {
+
+ if (this.sprite === null)
+ {
+ // Abort. We've been destroyed.
+ return;
+ }
+
+ if (this._pointerData[pointer.id].isDown === false && this._pointerData[pointer.id].isOver === true)
+ {
+ if (this.pixelPerfectClick && !this.checkPixel(null, null, pointer))
+ {
+ return;
+ }
+
+ this._pointerData[pointer.id].isDown = true;
+ this._pointerData[pointer.id].isUp = false;
+ this._pointerData[pointer.id].timeDown = this.game.time.now;
+ this.sprite.events.onInputDown.dispatch(this.sprite, pointer);
+
+ // Start drag
+ if (this.draggable && this.isDragged === false)
+ {
+ this.startDrag(pointer);
+ }
+
+ if (this.bringToTop)
+ {
+ this.sprite.bringToTop();
+ }
+ }
+
+ // Consume the event?
+ return this.consumePointerEvent;
+
+ },
+
+ /**
+ * Internal method handling the pointer released event.
+ * @method Phaser.InputHandler#_releasedHandler
+ * @private
+ * @param {Phaser.Pointer} pointer
+ */
+ _releasedHandler: function (pointer) {
+
+ if (this.sprite === null)
+ {
+ // Abort. We've been destroyed.
+ return;
+ }
+
+ // If was previously touched by this Pointer, check if still is AND still over this item
+ if (this._pointerData[pointer.id].isDown && pointer.isUp)
+ {
+ this._pointerData[pointer.id].isDown = false;
+ this._pointerData[pointer.id].isUp = true;
+ this._pointerData[pointer.id].timeUp = this.game.time.now;
+ this._pointerData[pointer.id].downDuration = this._pointerData[pointer.id].timeUp - this._pointerData[pointer.id].timeDown;
+
+ // Only release the InputUp signal if the pointer is still over this sprite
+ if (this.checkPointerOver(pointer))
+ {
+ // Release the inputUp signal and provide optional parameter if pointer is still over the sprite or not
+ this.sprite.events.onInputUp.dispatch(this.sprite, pointer, true);
+ }
+ else
+ {
+ // Release the inputUp signal and provide optional parameter if pointer is still over the sprite or not
+ this.sprite.events.onInputUp.dispatch(this.sprite, pointer, false);
+
+ // Pointer outside the sprite? Reset the cursor
+ if (this.useHandCursor)
+ {
+ this.game.canvas.style.cursor = "default";
+ }
+ }
+
+ // Stop drag
+ if (this.draggable && this.isDragged && this._draggedPointerID == pointer.id)
+ {
+ this.stopDrag(pointer);
+ }
+ }
+
+ },
+
+ /**
+ * Updates the Pointer drag on this Sprite.
+ * @method Phaser.InputHandler#updateDrag
+ * @param {Phaser.Pointer} pointer
+ * @return {boolean}
+ */
+ updateDrag: function (pointer) {
+
+ if (pointer.isUp)
+ {
+ this.stopDrag(pointer);
+ return false;
+ }
+
+ if (this.sprite.fixedToCamera)
+ {
+ if (this.allowHorizontalDrag)
+ {
+ this.sprite.cameraOffset.x = pointer.x + this._dragPoint.x + this.dragOffset.x;
+ }
+
+ if (this.allowVerticalDrag)
+ {
+ this.sprite.cameraOffset.y = pointer.y + this._dragPoint.y + this.dragOffset.y;
+ }
+
+ if (this.boundsRect)
+ {
+ this.checkBoundsRect();
+ }
+
+ if (this.boundsSprite)
+ {
+ this.checkBoundsSprite();
+ }
+
+ if (this.snapOnDrag)
+ {
+ this.sprite.cameraOffset.x = Math.round((this.sprite.cameraOffset.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
+ this.sprite.cameraOffset.y = Math.round((this.sprite.cameraOffset.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
+ }
+ }
+ else
+ {
+ if (this.allowHorizontalDrag)
+ {
+ this.sprite.x = pointer.x + this._dragPoint.x + this.dragOffset.x;
+ }
+
+ if (this.allowVerticalDrag)
+ {
+ this.sprite.y = pointer.y + this._dragPoint.y + this.dragOffset.y;
+ }
+
+ if (this.boundsRect)
+ {
+ this.checkBoundsRect();
+ }
+
+ if (this.boundsSprite)
+ {
+ this.checkBoundsSprite();
+ }
+
+ if (this.snapOnDrag)
+ {
+ this.sprite.x = Math.round((this.sprite.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
+ this.sprite.y = Math.round((this.sprite.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
+ }
+ }
+
+ return true;
+
+ },
+
+ /**
+ * Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second)
+ * @method Phaser.InputHandler#justOver
+ * @param {Phaser.Pointer} pointer
+ * @param {number} delay - The time below which the pointer is considered as just over.
+ * @return {boolean}
+ */
+ justOver: function (pointer, delay) {
+
+ pointer = pointer || 0;
+ delay = delay || 500;
+
+ return (this._pointerData[pointer].isOver && this.overDuration(pointer) < delay);
+
+ },
+
+ /**
+ * Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second)
+ * @method Phaser.InputHandler#justOut
+ * @param {Phaser.Pointer} pointer
+ * @param {number} delay - The time below which the pointer is considered as just out.
+ * @return {boolean}
+ */
+ justOut: function (pointer, delay) {
+
+ pointer = pointer || 0;
+ delay = delay || 500;
+
+ return (this._pointerData[pointer].isOut && (this.game.time.now - this._pointerData[pointer].timeOut < delay));
+
+ },
+
+ /**
+ * Returns true if the pointer has touched or clicked on the Sprite within the specified delay time (defaults to 500ms, half a second)
+ * @method Phaser.InputHandler#justPressed
+ * @param {Phaser.Pointer} pointer
+ * @param {number} delay - The time below which the pointer is considered as just over.
+ * @return {boolean}
+ */
+ justPressed: function (pointer, delay) {
+
+ pointer = pointer || 0;
+ delay = delay || 500;
+
+ return (this._pointerData[pointer].isDown && this.downDuration(pointer) < delay);
+
+ },
+
+ /**
+ * Returns true if the pointer was touching this Sprite, but has been released within the specified delay time (defaults to 500ms, half a second)
+ * @method Phaser.InputHandler#justReleased
+ * @param {Phaser.Pointer} pointer
+ * @param {number} delay - The time below which the pointer is considered as just out.
+ * @return {boolean}
+ */
+ justReleased: function (pointer, delay) {
+
+ pointer = pointer || 0;
+ delay = delay || 500;
+
+ return (this._pointerData[pointer].isUp && (this.game.time.now - this._pointerData[pointer].timeUp < delay));
+
+ },
+
+ /**
+ * If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds.
+ * @method Phaser.InputHandler#overDuration
+ * @param {Phaser.Pointer} pointer
+ * @return {number} The number of milliseconds the pointer has been over the Sprite, or -1 if not over.
+ */
+ overDuration: function (pointer) {
+
+ pointer = pointer || 0;
+
+ if (this._pointerData[pointer].isOver)
+ {
+ return this.game.time.now - this._pointerData[pointer].timeOver;
+ }
+
+ return -1;
+
+ },
+
+ /**
+ * If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds.
+ * @method Phaser.InputHandler#downDuration
+ * @param {Phaser.Pointer} pointer
+ * @return {number} The number of milliseconds the pointer has been pressed down on the Sprite, or -1 if not over.
+ */
+ downDuration: function (pointer) {
+
+ pointer = pointer || 0;
+
+ if (this._pointerData[pointer].isDown)
+ {
+ return this.game.time.now - this._pointerData[pointer].timeDown;
+ }
+
+ return -1;
+
+ },
+
+ /**
+ * Make this Sprite draggable by the mouse. You can also optionally set mouseStartDragCallback and mouseStopDragCallback
+ * @method Phaser.InputHandler#enableDrag
+ * @param {boolean} [lockCenter=false] - If false the Sprite will drag from where you click it minus the dragOffset. If true it will center itself to the tip of the mouse pointer.
+ * @param {boolean} [bringToTop=false] - If true the Sprite will be bought to the top of the rendering list in its current Group.
+ * @param {boolean} [pixelPerfect=false] - If true it will use a pixel perfect test to see if you clicked the Sprite. False uses the bounding box.
+ * @param {boolean} [alphaThreshold=255] - If using pixel perfect collision this specifies the alpha level from 0 to 255 above which a collision is processed.
+ * @param {Phaser.Rectangle} [boundsRect=null] - If you want to restrict the drag of this sprite to a specific Rectangle, pass the Phaser.Rectangle here, otherwise it's free to drag anywhere.
+ * @param {Phaser.Sprite} [boundsSprite=null] - If you want to restrict the drag of this sprite to within the bounding box of another sprite, pass it here.
+ */
+ enableDrag: function (lockCenter, bringToTop, pixelPerfect, alphaThreshold, boundsRect, boundsSprite) {
+
+ if (typeof lockCenter == 'undefined') { lockCenter = false; }
+ if (typeof bringToTop == 'undefined') { bringToTop = false; }
+ if (typeof pixelPerfect == 'undefined') { pixelPerfect = false; }
+ if (typeof alphaThreshold == 'undefined') { alphaThreshold = 255; }
+ if (typeof boundsRect == 'undefined') { boundsRect = null; }
+ if (typeof boundsSprite == 'undefined') { boundsSprite = null; }
+
+ this._dragPoint = new Phaser.Point();
+ this.draggable = true;
+ this.bringToTop = bringToTop;
+ this.dragOffset = new Phaser.Point();
+ this.dragFromCenter = lockCenter;
+
+ this.pixelPerfect = pixelPerfect;
+ this.pixelPerfectAlpha = alphaThreshold;
+
+ if (boundsRect)
+ {
+ this.boundsRect = boundsRect;
+ }
+
+ if (boundsSprite)
+ {
+ this.boundsSprite = boundsSprite;
+ }
+
+ },
+
+ /**
+ * Stops this sprite from being able to be dragged. If it is currently the target of an active drag it will be stopped immediately. Also disables any set callbacks.
+ * @method Phaser.InputHandler#disableDrag
+ */
+ disableDrag: function () {
+
+ if (this._pointerData)
+ {
+ for (var i = 0; i < 10; i++)
+ {
+ this._pointerData[i].isDragged = false;
+ }
+ }
+
+ this.draggable = false;
+ this.isDragged = false;
+ this._draggedPointerID = -1;
+
+ },
+
+ /**
+ * Called by Pointer when drag starts on this Sprite. Should not usually be called directly.
+ * @method Phaser.InputHandler#startDrag
+ * @param {Phaser.Pointer} pointer
+ */
+ startDrag: function (pointer) {
+
+ this.isDragged = true;
+ this._draggedPointerID = pointer.id;
+ this._pointerData[pointer.id].isDragged = true;
+
+ if (this.sprite.fixedToCamera)
+ {
+ if (this.dragFromCenter)
+ {
+ this.sprite.centerOn(pointer.x, pointer.y);
+ this._dragPoint.setTo(this.sprite.cameraOffset.x - pointer.x, this.sprite.cameraOffset.y - pointer.y);
+ }
+ else
+ {
+ this._dragPoint.setTo(this.sprite.cameraOffset.x - pointer.x, this.sprite.cameraOffset.y - pointer.y);
+ }
+ }
+ else
+ {
+ if (this.dragFromCenter)
+ {
+ this.sprite.centerOn(pointer.x, pointer.y);
+ this._dragPoint.setTo(this.sprite.x - pointer.x, this.sprite.y - pointer.y);
+ }
+ else
+ {
+ this._dragPoint.setTo(this.sprite.x - pointer.x, this.sprite.y - pointer.y);
+ }
+ }
+
+ this.updateDrag(pointer);
+
+ if (this.bringToTop)
+ {
+ this.sprite.bringToTop();
+ }
+
+ this.sprite.events.onDragStart.dispatch(this.sprite, pointer);
+
+ },
+
+ /**
+ * Called by Pointer when drag is stopped on this Sprite. Should not usually be called directly.
+ * @method Phaser.InputHandler#stopDrag
+ * @param {Phaser.Pointer} pointer
+ */
+ stopDrag: function (pointer) {
+
+ this.isDragged = false;
+ this._draggedPointerID = -1;
+ this._pointerData[pointer.id].isDragged = false;
+
+ if (this.snapOnRelease)
+ {
+ if (this.sprite.fixedToCamera)
+ {
+ this.sprite.cameraOffset.x = Math.round((this.sprite.cameraOffset.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
+ this.sprite.cameraOffset.y = Math.round((this.sprite.cameraOffset.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
+ }
+ else
+ {
+ this.sprite.x = Math.round((this.sprite.x - (this.snapOffsetX % this.snapX)) / this.snapX) * this.snapX + (this.snapOffsetX % this.snapX);
+ this.sprite.y = Math.round((this.sprite.y - (this.snapOffsetY % this.snapY)) / this.snapY) * this.snapY + (this.snapOffsetY % this.snapY);
+ }
+ }
+
+ this.sprite.events.onDragStop.dispatch(this.sprite, pointer);
+ this.sprite.events.onInputUp.dispatch(this.sprite, pointer);
+
+ if (this.checkPointerOver(pointer) === false)
+ {
+ this._pointerOutHandler(pointer);
+ }
+
+ },
+
+ /**
+ * Restricts this sprite to drag movement only on the given axis. Note: If both are set to false the sprite will never move!
+ * @method Phaser.InputHandler#setDragLock
+ * @param {boolean} [allowHorizontal=true] - To enable the sprite to be dragged horizontally set to true, otherwise false.
+ * @param {boolean} [allowVertical=true] - To enable the sprite to be dragged vertically set to true, otherwise false.
+ */
+ setDragLock: function (allowHorizontal, allowVertical) {
+
+ if (typeof allowHorizontal == 'undefined') { allowHorizontal = true; }
+ if (typeof allowVertical == 'undefined') { allowVertical = true; }
+
+ this.allowHorizontalDrag = allowHorizontal;
+ this.allowVerticalDrag = allowVertical;
+
+ },
+
+ /**
+ * Make this Sprite snap to the given grid either during drag or when it's released.
+ * For example 16x16 as the snapX and snapY would make the sprite snap to every 16 pixels.
+ * @method Phaser.InputHandler#enableSnap
+ * @param {number} snapX - The width of the grid cell to snap to.
+ * @param {number} snapY - The height of the grid cell to snap to.
+ * @param {boolean} [onDrag=true] - If true the sprite will snap to the grid while being dragged.
+ * @param {boolean} [onRelease=false] - If true the sprite will snap to the grid when released.
+ * @param {number} [snapOffsetX=0] - Used to offset the top-left starting point of the snap grid.
+ * @param {number} [snapOffsetX=0] - Used to offset the top-left starting point of the snap grid.
+ */
+ enableSnap: function (snapX, snapY, onDrag, onRelease, snappOffsetX, snappOffsetY) {
+
+ if (typeof onDrag == 'undefined') { onDrag = true; }
+ if (typeof onRelease == 'undefined') { onRelease = false; }
+ if (typeof snapOffsetX == 'undefined') { snapOffsetX = 0; }
+ if (typeof snapOffsetY == 'undefined') { snapOffsetY = 0; }
+
+ this.snapX = snapX;
+ this.snapY = snapY;
+ this.snapOffsetX = snapOffsetX;
+ this.snapOffsetY = snapOffsetY;
+ this.snapOnDrag = onDrag;
+ this.snapOnRelease = onRelease;
+
+ },
+
+ /**
+ * Stops the sprite from snapping to a grid during drag or release.
+ * @method Phaser.InputHandler#disableSnap
+ */
+ disableSnap: function () {
+
+ this.snapOnDrag = false;
+ this.snapOnRelease = false;
+
+ },
+
+ /**
+ * Bounds Rect check for the sprite drag
+ * @method Phaser.InputHandler#checkBoundsRect
+ */
+ checkBoundsRect: function () {
+
+ if (this.sprite.fixedToCamera)
+ {
+ if (this.sprite.cameraOffset.x < this.boundsRect.left)
+ {
+ this.sprite.cameraOffset.x = this.boundsRect.cameraOffset.x;
+ }
+ else if ((this.sprite.cameraOffset.x + this.sprite.width) > this.boundsRect.right)
+ {
+ this.sprite.cameraOffset.x = this.boundsRect.right - this.sprite.width;
+ }
+
+ if (this.sprite.cameraOffset.y < this.boundsRect.top)
+ {
+ this.sprite.cameraOffset.y = this.boundsRect.top;
+ }
+ else if ((this.sprite.cameraOffset.y + this.sprite.height) > this.boundsRect.bottom)
+ {
+ this.sprite.cameraOffset.y = this.boundsRect.bottom - this.sprite.height;
+ }
+ }
+ else
+ {
+ if (this.sprite.x < this.boundsRect.left)
+ {
+ this.sprite.x = this.boundsRect.x;
+ }
+ else if ((this.sprite.x + this.sprite.width) > this.boundsRect.right)
+ {
+ this.sprite.x = this.boundsRect.right - this.sprite.width;
+ }
+
+ if (this.sprite.y < this.boundsRect.top)
+ {
+ this.sprite.y = this.boundsRect.top;
+ }
+ else if ((this.sprite.y + this.sprite.height) > this.boundsRect.bottom)
+ {
+ this.sprite.y = this.boundsRect.bottom - this.sprite.height;
+ }
+ }
+
+ },
+
+ /**
+ * Parent Sprite Bounds check for the sprite drag.
+ * @method Phaser.InputHandler#checkBoundsSprite
+ */
+ checkBoundsSprite: function () {
+
+ if (this.sprite.fixedToCamera && this.boundsSprite.fixedToCamera)
+ {
+ if (this.sprite.cameraOffset.x < this.boundsSprite.camerOffset.x)
+ {
+ this.sprite.cameraOffset.x = this.boundsSprite.camerOffset.x;
+ }
+ else if ((this.sprite.cameraOffset.x + this.sprite.width) > (this.boundsSprite.camerOffset.x + this.boundsSprite.width))
+ {
+ this.sprite.cameraOffset.x = (this.boundsSprite.camerOffset.x + this.boundsSprite.width) - this.sprite.width;
+ }
+
+ if (this.sprite.cameraOffset.y < this.boundsSprite.camerOffset.y)
+ {
+ this.sprite.cameraOffset.y = this.boundsSprite.camerOffset.y;
+ }
+ else if ((this.sprite.cameraOffset.y + this.sprite.height) > (this.boundsSprite.camerOffset.y + this.boundsSprite.height))
+ {
+ this.sprite.cameraOffset.y = (this.boundsSprite.camerOffset.y + this.boundsSprite.height) - this.sprite.height;
+ }
+ }
+ else
+ {
+ if (this.sprite.x < this.boundsSprite.x)
+ {
+ this.sprite.x = this.boundsSprite.x;
+ }
+ else if ((this.sprite.x + this.sprite.width) > (this.boundsSprite.x + this.boundsSprite.width))
+ {
+ this.sprite.x = (this.boundsSprite.x + this.boundsSprite.width) - this.sprite.width;
+ }
+
+ if (this.sprite.y < this.boundsSprite.y)
+ {
+ this.sprite.y = this.boundsSprite.y;
+ }
+ else if ((this.sprite.y + this.sprite.height) > (this.boundsSprite.y + this.boundsSprite.height))
+ {
+ this.sprite.y = (this.boundsSprite.y + this.boundsSprite.height) - this.sprite.height;
+ }
+ }
+
+ }
+
+};
+
+Phaser.InputHandler.prototype.constructor = Phaser.InputHandler;
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
/**
* @class Phaser.Events
*
@@ -19202,9 +32712,6 @@ Phaser.Events = function (sprite) {
this.onAnimationComplete = null;
this.onAnimationLoop = null;
- this.onBeginContact = null;
- this.onEndContact = null;
-
};
Phaser.Events.prototype = {
@@ -19282,6 +32789,26 @@ Phaser.GameObjectFactory.prototype = {
},
+ /**
+ * Create a new `Image` object. An Image is a light-weight object you can use to display anything that doesn't need physics or animation.
+ * It can still rotate, scale, crop and receive input events. This makes it perfect for logos, backgrounds, simple buttons and other non-Sprite graphics.
+ *
+ * @method Phaser.GameObjectFactory#image
+ * @param {number} x - X position of the image.
+ * @param {number} y - Y position of the image.
+ * @param {string|Phaser.RenderTexture|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
+ * @param {string|number} [frame] - If the sprite uses an image from a texture atlas or sprite sheet you can pass the frame here. Either a number for a frame ID or a string for a frame name.
+ * @param {Phaser.Group} [group] - Optional Group to add the object to. If not specified it will be added to the World group.
+ * @returns {Phaser.Sprite} the newly created sprite object.
+ */
+ image: function (x, y, key, frame, group) {
+
+ if (typeof group === 'undefined') { group = this.world; }
+
+ return group.add(new Phaser.Image(this.game, x, y, key, frame));
+
+ },
+
/**
* Create a new Sprite with specific position and sprite sheet key.
*
@@ -19301,24 +32828,6 @@ Phaser.GameObjectFactory.prototype = {
},
- /**
- * DEPRECATED - will be removed in Phaser 1.2
- * Create a new Sprite with specific position and sprite sheet key that will automatically be added as a child of the given parent.
- *
- * @method Phaser.GameObjectFactory#child
- * @param {Phaser.Group} group - The Group to add this child to.
- * @param {number} x - X position of the new sprite.
- * @param {number} y - Y position of the new sprite.
- * @param {string|RenderTexture} [key] - The image key as defined in the Game.Cache to use as the texture for this sprite OR a RenderTexture.
- * @param {string|number} [frame] - If the sprite uses an image from a texture atlas or sprite sheet you can pass the frame here. Either a number for a frame ID or a string for a frame name.
- * @returns {Phaser.Sprite} the newly created sprite object.
- */
- child: function (group, x, y, key, frame) {
-
- return group.create(x, y, key, frame);
-
- },
-
/**
* Create a tween object for a specific object. The object can be any JavaScript object or Phaser object such as Sprite.
*
@@ -19338,11 +32847,33 @@ Phaser.GameObjectFactory.prototype = {
* @method Phaser.GameObjectFactory#group
* @param {any} parent - The parent Group or DisplayObjectContainer that will hold this group, if any.
* @param {string} [name='group'] - A name for this Group. Not used internally but useful for debugging.
+ * @param {boolean} [addToStage=false] - If set to true this Group will be added directly to the Game.Stage instead of Game.World.
* @return {Phaser.Group} The newly created group.
*/
- group: function (parent, name) {
+ group: function (parent, name, addToStage) {
- return new Phaser.Group(this.game, parent, name);
+ if (typeof name === 'undefined') { name = 'group'; }
+ if (typeof addToStage === 'undefined') { addToStage = false; }
+
+ return new Phaser.Group(this.game, parent, name, addToStage);
+
+ },
+
+ /**
+ * A Group is a container for display objects that allows for fast pooling, recycling and collision checks.
+ *
+ * @method Phaser.GameObjectFactory#spriteBatch
+ * @param {any} parent - The parent Group or DisplayObjectContainer that will hold this group, if any.
+ * @param {string} [name='group'] - A name for this Group. Not used internally but useful for debugging.
+ * @param {boolean} [addToStage=false] - If set to true this Group will be added directly to the Game.Stage instead of Game.World.
+ * @return {Phaser.Group} The newly created group.
+ */
+ spriteBatch: function (parent, name, addToStage) {
+
+ if (typeof name === 'undefined') { name = 'group'; }
+ if (typeof addToStage === 'undefined') { addToStage = false; }
+
+ return new Phaser.SpriteBatch(this.game, parent, name, addToStage);
},
@@ -19382,19 +32913,20 @@ Phaser.GameObjectFactory.prototype = {
* Creates a new TileSprite object.
*
* @method Phaser.GameObjectFactory#tileSprite
- * @param {number} x - X position of the new tileSprite.
- * @param {number} y - Y position of the new tileSprite.
- * @param {number} width - the width of the tilesprite.
- * @param {number} height - the height of the tilesprite.
- * @param {string|Phaser.RenderTexture|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
+ * @param {number} x - The x coordinate (in world space) to position the TileSprite at.
+ * @param {number} y - The y coordinate (in world space) to position the TileSprite at.
+ * @param {number} width - The width of the TileSprite.
+ * @param {number} height - The height of the TileSprite.
+ * @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the TileSprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
+ * @param {string|number} frame - If this TileSprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
* @param {Phaser.Group} [group] - Optional Group to add the object to. If not specified it will be added to the World group.
* @return {Phaser.TileSprite} The newly created tileSprite object.
*/
- tileSprite: function (x, y, width, height, key, group) {
+ tileSprite: function (x, y, width, height, key, frame, group) {
if (typeof group === 'undefined') { group = this.world; }
- return group.add(new Phaser.TileSprite(this.game, x, y, width, height, key));
+ return group.add(new Phaser.TileSprite(this.game, x, y, width, height, key, frame));
},
@@ -19476,21 +33008,45 @@ Phaser.GameObjectFactory.prototype = {
},
/**
- * * Create a new BitmapText object.
+ * Create a new BitmapFont object to be used as a texture for an Image or Sprite and optionally add it to the Cache.
+ * The texture can be asssigned or one or multiple images/sprites, but note that the text the BitmapFont uses will be shared across them all,
+ * i.e. if you need each Image to have different text in it, then you need to create multiple BitmapFont objects.
+ *
+ * @method Phaser.GameObjectFactory#bitmapFont
+ * @param {string} font - The key of the image in the Game.Cache that the BitmapFont will use.
+ * @param {number} characterWidth - The width of each character in the font set.
+ * @param {number} characterHeight - The height of each character in the font set.
+ * @param {string} chars - The characters used in the font set, in display order. You can use the TEXT_SET consts for common font set arrangements.
+ * @param {number} charsPerRow - The number of characters per row in the font set.
+ * @param {number} [xSpacing=0] - If the characters in the font set have horizontal spacing between them set the required amount here.
+ * @param {number} [ySpacing=0] - If the characters in the font set have vertical spacing between them set the required amount here.
+ * @param {number} [xOffset=0] - If the font set doesn't start at the top left of the given image, specify the X coordinate offset here.
+ * @param {number} [yOffset=0] - If the font set doesn't start at the top left of the given image, specify the Y coordinate offset here.
+ * @return {Phaser.BitmapFont} The newly created BitmapFont texture which can be applied to an Image or Sprite.
+ */
+ bitmapFont: function (font, characterWidth, characterHeight, chars, charsPerRow, xSpacing, ySpacing, xOffset, yOffset) {
+
+ return new Phaser.BitmapFont(this.game, font, characterWidth, characterHeight, chars, charsPerRow, xSpacing, ySpacing, xOffset, yOffset);
+
+ },
+
+ /**
+ * Create a new BitmapText object.
*
* @method Phaser.GameObjectFactory#bitmapText
* @param {number} x - X position of the new bitmapText object.
* @param {number} y - Y position of the new bitmapText object.
- * @param {string} text - The actual text that will be written.
- * @param {object} style - The style object containing style attributes like font, font size , etc.
+ * @param {string} font - The key of the BitmapText font as stored in Game.Cache.
+ * @param {string} [text] - The actual text that will be rendered. Can be set later via BitmapText.text.
+ * @param {number} [size] - The size the font will be rendered in, in pixels.
* @param {Phaser.Group} [group] - Optional Group to add the object to. If not specified it will be added to the World group.
* @return {Phaser.BitmapText} The newly created bitmapText object.
*/
- bitmapText: function (x, y, text, style, group) {
+ bitmapText: function (x, y, font, text, size, group) {
if (typeof group === 'undefined') { group = this.world; }
- return this.world.add(new Phaser.BitmapText(this.game, x, y, text, style));
+ return group.add(new Phaser.BitmapText(this.game, x, y, font, text, size));
},
@@ -19512,32 +33068,51 @@ Phaser.GameObjectFactory.prototype = {
* A dynamic initially blank canvas to which images can be drawn.
*
* @method Phaser.GameObjectFactory#renderTexture
- * @param {string} key - Asset key for the render texture.
- * @param {number} width - the width of the render texture.
- * @param {number} height - the height of the render texture.
- * @return {Phaser.RenderTexture} The newly created renderTexture object.
+ * @param {number} [width=100] - the width of the RenderTexture.
+ * @param {number} [height=100] - the height of the RenderTexture.
+ * @param {string} [key=''] - Asset key for the RenderTexture when stored in the Cache (see addToCache parameter).
+ * @param {boolean} [addToCache=false] - Should this RenderTexture be added to the Game.Cache? If so you can retrieve it with Cache.getTexture(key)
+ * @return {Phaser.RenderTexture} The newly created RenderTexture object.
*/
- renderTexture: function (key, width, height) {
+ renderTexture: function (width, height, key, addToCache) {
- var texture = new Phaser.RenderTexture(this.game, key, width, height);
+ if (typeof key === 'undefined' || key === '') { key = this.game.rnd.uuid(); }
+ if (typeof addToCache === 'undefined') { addToCache = false; }
- this.game.cache.addRenderTexture(key, texture);
+ var texture = new Phaser.RenderTexture(this.game, width, height, key);
+
+ if (addToCache)
+ {
+ this.game.cache.addRenderTexture(key, texture);
+ }
return texture;
},
/**
- * Experimental: A BitmapData object which can be manipulated and drawn to like a traditional Canvas object and used to texture Sprites.
+ * A BitmapData object which can be manipulated and drawn to like a traditional Canvas object and used to texture Sprites.
*
* @method Phaser.GameObjectFactory#bitmapData
- * @param {number} [width=256] - The width of the BitmapData in pixels.
- * @param {number} [height=256] - The height of the BitmapData in pixels.
+ * @param {number} [width=100] - The width of the BitmapData in pixels.
+ * @param {number} [height=100] - The height of the BitmapData in pixels.
+ * @param {string} [key=''] - Asset key for the BitmapData when stored in the Cache (see addToCache parameter).
+ * @param {boolean} [addToCache=false] - Should this BitmapData be added to the Game.Cache? If so you can retrieve it with Cache.getBitmapData(key)
* @return {Phaser.BitmapData} The newly created BitmapData object.
*/
- bitmapData: function (width, height) {
+ bitmapData: function (width, height, addToCache) {
- return new Phaser.BitmapData(this.game, width, height);
+ if (typeof addToCache === 'undefined') { addToCache = false; }
+ if (typeof key === 'undefined' || key === '') { key = this.game.rnd.uuid(); }
+
+ var texture = new Phaser.BitmapData(this.game, key, width, height);
+
+ if (addToCache)
+ {
+ this.game.cache.addBitmapData(key, texture);
+ }
+
+ return texture;
},
@@ -19576,21 +33151,19 @@ Phaser.GameObjectFactory.prototype.constructor = Phaser.GameObjectFactory;
*
* @class Phaser.BitmapData
*
-* @classdesc Note: This object is still experimental and likely to change.
-*
-* A BitmapData object can be thought of as a blank canvas onto which you can perform graphics operations as you would on a normal canvas,
-* such as drawing lines, circles, arcs, fills and copying and setting blocks of pixel data. A single BitmapData can be used as the texture
-* for multiple Sprites. So if you need to dynamically create a Sprite texture then they are a good choice. It supports the EaselJS Tiny API.
+* @classdesc A BitmapData object contains a Canvas element to which you can draw anything you like via normal Canvas context operations.
+* A single BitmapData can be used as the texture one or many Images/Sprites. So if you need to dynamically create a Sprite texture then they are a good choice.
*
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
-* @param {number} [width=256] - The width of the BitmapData in pixels.
-* @param {number} [height=256] - The height of the BitmapData in pixels.
+* @param {string} key - Internal Phaser reference key for the render texture.
+* @param {number} [width=100] - The width of the BitmapData in pixels.
+* @param {number} [height=100] - The height of the BitmapData in pixels.
*/
-Phaser.BitmapData = function (game, width, height) {
+Phaser.BitmapData = function (game, key, width, height) {
- if (typeof width === 'undefined') { width = 256; }
- if (typeof height === 'undefined') { height = 256; }
+ if (typeof width === 'undefined') { width = 100; }
+ if (typeof height === 'undefined') { height = 100; }
/**
* @property {Phaser.Game} game - A reference to the currently running game.
@@ -19598,9 +33171,9 @@ Phaser.BitmapData = function (game, width, height) {
this.game = game;
/**
- * @property {string} name - The name of the BitmapData.
+ * @property {string} key - The key of the BitmapData in the Cache, if stored there.
*/
- this.name = '';
+ this.key = key;
/**
* @property {number} width - The width of the BitmapData in pixels.
@@ -19624,6 +33197,11 @@ Phaser.BitmapData = function (game, width, height) {
*/
this.context = this.canvas.getContext('2d');
+ /**
+ * @property {CanvasRenderingContext2D} ctx - A reference to BitmapData.context.
+ */
+ this.ctx = this.context;
+
/**
* @property {array} imageData - The canvas image data.
*/
@@ -19672,30 +33250,27 @@ Phaser.BitmapData = function (game, width, height) {
Phaser.BitmapData.prototype = {
/**
- * Updates the given Sprite so that it uses this BitmapData as its texture.
+ * Updates the given objects so that they use this BitmapData as their texture.
+ *
* @method Phaser.BitmapData#add
- * @param {Phaser.Sprite} sprite - The sprite to apply this texture to.
+ * @param {Phaser.Sprite|Phaser.Sprite[]|Phaser.Image|Phaser.Image[]} object - Either a single Sprite/Image or an Array of Sprites/Images.
*/
- add: function (sprite) {
+ add: function (object) {
- sprite.loadTexture(this);
-
- },
-
- /**
- * Given an array of Sprites it will update each of them so that their Textures reference this BitmapData.
- * @method Phaser.BitmapData#addTo
- * @param {Phaser.Sprite[]} sprites - An array of Sprites to apply this texture to.
- */
- addTo: function (sprites) {
-
- for (var i = 0; i < sprites.length; i++)
+ if (Array.isArray(object))
{
- if (sprites[i].texture)
+ for (var i = 0; i < object.length; i++)
{
- sprites[i].loadTexture(this);
+ if (object[i]['loadTexture'])
+ {
+ object[i].loadTexture(this);
+ }
}
}
+ else
+ {
+ object.loadTexture(this);
+ }
},
@@ -19711,6 +33286,31 @@ Phaser.BitmapData.prototype = {
},
+ /**
+ * Resizes the BitmapData.
+ * @method Phaser.BitmapData#resize
+ */
+ resize: function (width, height) {
+
+ if (width !== this.width || height !== this.height)
+ {
+ console.log('bmd resize', width, height);
+ this.width = width;
+ this.height = height;
+ this.canvas.width = width;
+ this.canvas.height = height;
+ this.textureFrame.width = width;
+ this.textureFrame.height = height;
+ this.imageData = this.context.getImageData(0, 0, width, height);
+ }
+
+ this._dirty = true;
+
+ },
+
+ /**
+ * @method Phaser.BitmapData#refreshBuffer
+ */
refreshBuffer: function () {
this.imageData = this.context.getImageData(0, 0, this.width, this.height);
@@ -19813,697 +33413,12 @@ Phaser.BitmapData.prototype = {
},
- /**
- * Adds an arc to the path which is centered at (x, y) position with radius r starting at startAngle and ending at endAngle
- * going in the given direction by anticlockwise (defaulting to clockwise).
- * @method Phaser.BitmapData#arc
- * @param {number} x - The x axis of the coordinate for the arc's center
- * @param {number} y - The y axis of the coordinate for the arc's center
- * @param {number} radius - The arc's radius
- * @param {number} startAngle - The starting point, measured from the x axis, from which it will be drawn, expressed in radians.
- * @param {number} endAngle - The end arc's angle to which it will be drawn, expressed in radians.
- * @param {boolean} [anticlockwise=true] - true draws the arc anticlockwise, otherwise in a clockwise direction.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- arc: function (x, y, radius, startAngle, endAngle, anticlockwise) {
+ copyPixels: function (source, area, destX, destY) {
- if (typeof anticlockwise === 'undefined') { anticlockwise = false; }
-
- this._dirty = true;
- this.context.arc(x, y, radius, startAngle, endAngle, anticlockwise);
- return this;
+ this.context.drawImage(source, area.x, area.y, area.width, area.height, destX, destY, area.width, area.height);
},
- /**
- * Adds an arc with the given control points and radius, connected to the previous point by a straight line.
- * @method Phaser.BitmapData#arcTo
- * @param {number} x1
- * @param {number} y1
- * @param {number} x2
- * @param {number} y2
- * @param {number} radius
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- arcTo: function (x1, y1, x2, y2, radius) {
-
- this._dirty = true;
- this.context.arcTo(x1, y1, x2, y2, radius);
- return this;
-
- },
-
- /**
- * Begins a fill with the specified color. This ends the current sub-path.
- * @method Phaser.BitmapData#beginFill
- * @param {string} color - A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to null will result in no fill.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- beginFill: function (color) {
-
- this.fillStyle(color);
-
- return this;
-
- },
-
- /**
- * Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
- * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a square to display it:
- *
- * ```myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).rect(20, 20, 120, 120);```
- *
- * @method Phaser.BitmapData#beginLinearGradientFill
- * @param {Array} colors - An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient drawing from red to blue.
- * @param {Array} ratios - An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
- * @param {number} x0 - The position of the first point defining the line that defines the gradient direction and size.
- * @param {number} y0 - The position of the first point defining the line that defines the gradient direction and size.
- * @param {number} x1 - The position of the second point defining the line that defines the gradient direction and size.
- * @param {number} y1 - The position of the second point defining the line that defines the gradient direction and size.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- beginLinearGradientFill: function (colors, ratios, x0, y0, x1, y1) {
-
- var gradient = this.createLinearGradient(x0, y0, x1, y1);
-
- for (var i = 0, len = colors.length; i < len; i++)
- {
- gradient.addColorStop(ratios[i], colors[i]);
- }
-
- this.fillStyle(gradient);
-
- return this;
-
- },
-
- /**
- * Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
- * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
- * square to display it:
- *
- * ```myGraphics.setStrokeStyle(10).beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);```
- *
- * @method Phaser.BitmapData#beginLinearGradientStroke
- * @param {Array} colors - An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient drawing from red to blue.
- * @param {Array} ratios - An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
- * @param {number} x0 - The position of the first point defining the line that defines the gradient direction and size.
- * @param {number} y0 - The position of the first point defining the line that defines the gradient direction and size.
- * @param {number} x1 - The position of the second point defining the line that defines the gradient direction and size.
- * @param {number} y1 - The position of the second point defining the line that defines the gradient direction and size.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- beginLinearGradientStroke: function (colors, ratios, x0, y0, x1, y1) {
-
- var gradient = this.createLinearGradient(x0, y0, x1, y1);
-
- for (var i = 0, len = colors.length; i < len; i++)
- {
- gradient.addColorStop(ratios[i], colors[i]);
- }
-
- this.strokeStyle(gradient);
-
- return this;
-
- },
-
- /**
- * Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to
- * blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it:
- *
- * myGraphics.setStrokeStyle(10)
- * .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50)
- * .drawRect(50, 90, 150, 110);
- *
- * @method Phaser.BitmapData#beginRadialGradientStroke
- * @param {Array} colors - An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient drawing from red to blue.
- * @param {Array} ratios - An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
- * @param {number} x0 - Center position of the inner circle that defines the gradient.
- * @param {number} y0 - Center position of the inner circle that defines the gradient.
- * @param {number} r0 - Radius of the inner circle that defines the gradient.
- * @param {number} x1 - Center position of the outer circle that defines the gradient.
- * @param {number} y1 - Center position of the outer circle that defines the gradient.
- * @param {number} r1 - Radius of the outer circle that defines the gradient.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- beginRadialGradientStroke: function (colors, ratios, x0, y0, r0, x1, y1, r1) {
-
- var gradient = this.createRadialGradient(x0, y0, r0, x1, y1, r1);
-
- for (var i = 0, len = colors.length; i < len; i++)
- {
- gradient.addColorStop(ratios[i], colors[i]);
- }
-
- this.strokeStyle(gradient);
-
- return this;
-
- },
-
- /**
- * Starts a new path by resetting the list of sub-paths. Call this method when you want to create a new path.
- * @method Phaser.BitmapData#beginPath
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- beginPath: function () {
-
- this.context.beginPath();
- return this;
-
- },
-
- /**
- * Begins a stroke with the specified color. This ends the current sub-path.
- * @method Phaser.BitmapData#beginStroke
- * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to null will result in no stroke.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- beginStroke: function (color) {
-
- this.strokeStyle(color);
- return this;
-
- },
-
- /**
- * Adds a bezier curve from the current context point to (x, y) using the control points (cp1x, cp1y) and (cp2x, cp2y).
- * @method Phaser.BitmapData#bezierCurveTo
- * @param {number} cp1x - The x axis of control point 1.
- * @param {number} cp1y - The y axis of control point 1.
- * @param {number} cp2x - The x axis of control point 2.
- * @param {number} cp2y - The y axis of control point 2.
- * @param {number} x - The x axis of the ending point.
- * @param {number} y - The y axis of the ending point.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- bezierCurveTo: function (cp1x, cp1y, cp2x, cp2y, x, y) {
-
- this._dirty = true;
- this.context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
- return this;
-
- },
-
- /**
- * Draws a circle with the specified radius at (x, y).
- * @method Phaser.BitmapData#circle
- * @param {number} x - x coordinate center point of circle.
- * @param {number} y - y coordinate center point of circle.
- * @param {number} radius - Radius of circle in radians.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- circle: function (x, y, radius) {
-
- this.arc(x, y, radius, 0, Math.PI*2);
- return this;
-
- },
-
- /**
- * Sets all pixels in the rectangle defined by starting point (x, y) and size (width, height) to transparent black.
- * @method Phaser.BitmapData#clearRect
- * @param {number} x - The x axis of the coordinate for the rectangle starting point.
- * @param {number} y - The y axis of the coordinate for the rectangle starting point.
- * @param {number} width - The rectangles width.
- * @param {number} height - The rectangles height.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- clearRect: function (x, y, width, height) {
-
- this._dirty = true;
- this.context.clearRect(x, y, width, height);
- return this;
-
- },
-
- /**
- * Creates a clipping path from the current sub-paths. Everything drawn after clip() is called appears inside the clipping path only.
- * @method Phaser.BitmapData#clip
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- clip: function () {
-
- this._dirty = true;
- this.context.clip();
- return this;
-
- },
-
- /**
- * Tries to draw a straight line from the current point to the start. If the shape has already been closed or has only one point, this function does nothing.
- * @method Phaser.BitmapData#closePath
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- closePath: function () {
-
- this._dirty = true;
- this.context.closePath();
- return this;
-
- },
-
- /**
- * Creates a linear gradient with defined by an imaginary line which implies the direction of the gradient.
- * Once the gradient is created colors can be inserted using the addColorStop method.
- * @method Phaser.BitmapData#createLinearGradient
- * @param {number} x - The x axis of the coordinate for the gradients starting point.
- * @param {number} y - The y axis of the coordinate for the gradients starting point.
- * @param {number} width - The width of the gradient.
- * @param {number} height - The height of the gradient.
- * @return {CanvasGradient} The Linear Gradient.
- */
- createLinearGradient: function (x, y, width, height) {
-
- return this.context.createLinearGradient(x, y, width, height);
-
- },
-
- // createPattern
-
- /**
- * Creates a radial gradient.
- * @method Phaser.BitmapData#createRadialGradient
- * @param {number} x0
- * @param {number} y0
- * @param {number} r0
- * @param {number} x1
- * @param {number} y1
- * @param {number} r1
- * @return {CanvasGradient} The Radial Gradient.
- */
- createRadialGradient: function (x0, y0, r0, x1, y1, r1) {
-
- return this.context.createRadialGradient(x0, y0, r0, x1, y1, r1);
-
- },
-
- // drawImage
- // drawSystemFocusRing (?)
-
- /**
- * Draws an ellipse (oval) with a specified width (w) and height (h).
- * @method Phaser.BitmapData#ellipse
- * @param {number} x - x coordinate center point of ellipse.
- * @param {number} y - y coordinate center point of ellipse.
- * @param {number} w - height (horizontal diameter) of ellipse. The horizontal radius will be half of this number.
- * @param {number} h - width (vertical diameter) of ellipse. The vertical radius will be half of this number.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- ellipse: function (x, y, w, h) {
-
- var k = 0.5522848;
- var ox = (w / 2) * k;
- var oy = (h / 2) * k;
- var xe = x + w;
- var ye = y + h;
- var xm = x + w / 2;
- var ym = y + h / 2;
-
- this.moveTo(x, ym);
- this.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
- this.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
- this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
- this.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
-
- return this;
-
- },
-
- /**
- * Fills the subpaths with the current fill style.
- * @method Phaser.BitmapData#fill
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- fill: function () {
-
- this._dirty = true;
- this.context.fill();
- return this;
-
- },
-
- /**
- * Draws a filled rectangle at (x, y) position whose size is determined by width and height.
- * @method Phaser.BitmapData#fillRect
- * @param {number} x - The x axis of the coordinate for the rectangle starting point.
- * @param {number} y - The y axis of the coordinate for the rectangle starting point.
- * @param {number} width - The rectangles width.
- * @param {number} height - The rectangles height.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- fillRect: function (x, y, width, height) {
-
- this._dirty = true;
- this.context.fillRect(x, y, width, height);
- return this;
-
- },
-
- /**
- * Sets the fill style.
- * @method Phaser.BitmapData#fillStyle
- * @param {string} color - The fill color value in CSS format: #RRGGBB or rgba(r,g,b,a)
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- fillStyle: function (color) {
-
- this.context.fillStyle = color;
- return this;
-
- },
-
- // fillText
-
- /**
- * Sets the font.
- * @method Phaser.BitmapData#font
- * @param {DOMString} font - The font to be used for any text rendering. Default value 10px sans-serif.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- font: function (font) {
-
- this.context.font = font;
- return this;
-
- },
-
- /**
- * Alpha value that is applied to shapes and images before they are composited onto the canvas. Default 1.0 (opaque).
- * @method Phaser.BitmapData#globalAlpha
- * @param {number} alpha - Alpha value that is applied to shapes and images before they are composited onto the canvas. Default 1.0 (opaque).
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- globalAlpha: function (alpha) {
-
- this.context.globalAlpha = alpha;
- return this;
-
- },
-
- /**
- * With globalAlpha applied this sets how shapes and images are drawn onto the existing bitmap. Possible values: source-atop, source-in, source-out,
- * source-over (default), destination-atop, destination-in, destination-out, destination-over, lighter, darker, copy and xor.
- * @method Phaser.BitmapData#globalCompositeOperation
- * @param {DOMString} operation - The composite operation to apply.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- globalCompositeOperation: function (operation) {
-
- this.context.globalCompositeOperation = operation;
- return this;
-
- },
-
- /**
- * Type of endings on the end of lines. Possible values: butt (default), round, square.
- * @method Phaser.BitmapData#lineCap
- * @param {DOMString} style - Possible values: butt (default), round, square
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- lineCap: function (style) {
-
- this.context.lineCap = style;
- return this;
-
- },
-
- /**
- * Specifies where to start a dasharray on a line.
- * @method Phaser.BitmapData#lineDashOffset
- * @param {number} offset - Specifies where to start a dasharray on a line.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- lineDashOffset: function (offset) {
-
- this.context.lineDashOffset = offset;
- return this;
-
- },
-
- /**
- * Defines the type of corners where two lines meet. Possible values: round, bevel, miter (default)
- * @method Phaser.BitmapData#lineJoin
- * @param {DOMString} join - Possible values: round, bevel, miter (default)
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- lineJoin: function (join) {
-
- this.context.lineJoin = join;
- return this;
-
- },
-
- /**
- * Width of lines. Default 1.0
- * @method Phaser.BitmapData#lineWidth
- * @param {number} width - Width of lines. Default 1.0
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- lineWidth: function (width) {
-
- this.context.lineWidth = width;
- return this;
-
- },
-
- /**
- * Default 10.
- * @method Phaser.BitmapData#miterLimit
- * @param {number} limit - Default 10.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- miterLimit: function (limit) {
-
- this.context.miterLimit = limit;
- return this;
-
- },
-
- // getImageData
- // getLineDash
- // isPointInPath
- // isPointInStroke
-
- /**
- * Connects the last point in the subpath to the x, y coordinates with a straight line.
- * @method Phaser.BitmapData#lineTo
- * @param {number} x - The x axis of the coordinate for the end of the line.
- * @param {number} y - The y axis of the coordinate for the end of the line.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- lineTo: function (x, y) {
-
- this._dirty = true;
- this.context.lineTo(x, y);
- return this;
-
- },
-
- // measureText
-
- /**
- * Moves the starting point of a new subpath to the (x, y) coordinates.
- * @method Phaser.BitmapData#moveTo
- * @param {number} x - The x axis of the point.
- * @param {number} y - The y axis of the point.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- moveTo: function (x, y) {
-
- this.context.moveTo(x, y);
- return this;
-
- },
-
- // putImageData
-
- /**
- * Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy).
- * @method Phaser.BitmapData#quadraticCurveTo
- * @param {Number} cpx - The x axis of the control point.
- * @param {Number} cpy - The y axis of the control point.
- * @param {Number} x - The x axis of the ending point.
- * @param {Number} y - The y axis of the ending point.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- quadraticCurveTo: function(cpx, cpy, x, y) {
-
- this._dirty = true;
- this.context.quadraticCurveTo(cpx, cpy, x, y);
- return this;
-
- },
-
- /**
- * Draws a rectangle at (x, y) position whose size is determined by width and height.
- * @method Phaser.BitmapData#rect
- * @param {number} x - The x axis of the coordinate for the rectangle starting point.
- * @param {number} y - The y axis of the coordinate for the rectangle starting point.
- * @param {number} width - The rectangles width.
- * @param {number} height - The rectangles height.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- rect: function (x, y, width, height) {
-
- this._dirty = true;
- this.context.rect(x, y, width, height);
- return this;
-
- },
-
- /**
- * Restores the drawing style state to the last element on the 'state stack' saved by save().
- * @method Phaser.BitmapData#restore
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- restore: function () {
-
- this._dirty = true;
- this.context.restore();
- return this;
-
- },
-
- /**
- * Rotates the drawing context values by r radians.
- * @method Phaser.BitmapData#rotate
- * @param {number} angle - The angle of rotation given in radians.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- rotate: function (angle) {
-
- this._dirty = true;
- this.context.rotate(angle);
- return this;
-
- },
-
- /**
- * Sets the stroke style for the current sub-path. Like all drawing methods, this can be chained, so you can define
- * the stroke style and color in a single line of code like so:
- *
- * ```myGraphics.setStrokeStyle(8,"round").beginStroke("#F00");```
- *
- * @method Phaser.BitmapData#setStrokeStyle
- * @param {number} thickness - The width of the stroke.
- * @param {string|number} [caps=0] - Indicates the type of caps to use at the end of lines. One of butt, round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with he tiny API.
- * @param {string|number} [joints=0] Specifies the type of joints that should be used where two lines meet. One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel) for use with the tiny API.
- * @param {number} [miterLimit=10] - If joints is set to "miter", then you can specify a miter limit ratio which controls at what point a mitered joint will be clipped.
- * @param {boolean} [ignoreScale=false] - If true, the stroke will be drawn at the specified thickness regardless of active transformations.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- setStrokeStyle: function (thickness, caps, joints, miterLimit, ignoreScale) {
-
- if (typeof thickness === 'undefined') { thickness = 1; }
- if (typeof caps === 'undefined') { caps = 'butt'; }
- if (typeof joints === 'undefined') { joints = 'miter'; }
- if (typeof miterLimit === 'undefined') { miterLimit = 10; }
-
- // TODO
- ignoreScale = false;
-
- this.lineWidth(thickness);
- this.lineCap(caps);
- this.lineJoin(joints);
- this.miterLimit(miterLimit);
-
- return this;
-
- },
-
- /**
- * Saves the current drawing style state using a stack so you can revert any change you make to it using restore().
- * @method Phaser.BitmapData#save
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- save: function () {
-
- this._dirty = true;
- this.context.save();
- return this;
-
- },
-
- /**
- * Scales the current drawing context.
- * @method Phaser.BitmapData#scale
- * @param {number} x
- * @param {number} y
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- scale: function (x, y) {
-
- this._dirty = true;
- this.context.scale(x, y);
- return this;
-
- },
-
- /**
- *
- * @method Phaser.BitmapData#scrollPathIntoView
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- scrollPathIntoView: function () {
-
- this._dirty = true;
- this.context.scrollPathIntoView();
- return this;
-
- },
-
- // setLineDash
- // setTransform
-
- /**
- * Strokes the subpaths with the current stroke style.
- * @method Phaser.BitmapData#stroke
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- stroke: function () {
-
- this._dirty = true;
- this.context.stroke();
- return this;
-
- },
-
- /**
- * Paints a rectangle which has a starting point at (x, y) and has a w width and an h height onto the canvas, using the current stroke style.
- * @method Phaser.BitmapData#strokeRect
- * @param {number} x - The x axis for the starting point of the rectangle.
- * @param {number} y - The y axis for the starting point of the rectangle.
- * @param {number} width - The rectangles width.
- * @param {number} height - The rectangles height.
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- strokeRect: function (x, y, width, height) {
-
- this._dirty = true;
- this.context.strokeRect(x, y, width, height);
- return this;
-
- },
-
- /**
- * Color or style to use for the lines around shapes. Default #000 (black).
- * @method Phaser.BitmapData#strokeStyle
- * @param {string} style - Color or style to use for the lines around shapes. Default #000 (black).
- * @return {Phaser.BitmapData} The BitmapData instance this method was called on.
- */
- strokeStyle: function (style) {
-
- this.context.strokeStyle = style;
- return this;
-
- },
-
- // strokeText
- // transform
- // translate
-
/**
* If the game is running in WebGL this will push the texture up to the GPU if it's dirty.
* This is called automatically if the BitmapData is being used by a Sprite, otherwise you need to remember to call it in your render function.
@@ -20528,164 +33443,6 @@ Phaser.BitmapData.prototype = {
Phaser.BitmapData.prototype.constructor = Phaser.BitmapData;
-// EaselJS Tiny API emulation
-
-/**
-* Shortcut to moveTo.
-* @method Phaser.BitmapData.prototype.mt
-*/
-Phaser.BitmapData.prototype.mt = Phaser.BitmapData.prototype.moveTo;
-
-/**
-* Shortcut to lineTo.
-* @method Phaser.BitmapData.prototype.mt
-*/
-Phaser.BitmapData.prototype.lt = Phaser.BitmapData.prototype.lineTo;
-
-/**
-* Shortcut to arcTo.
-* @method Phaser.BitmapData.prototype.at
-*/
-Phaser.BitmapData.prototype.at = Phaser.BitmapData.prototype.arcTo;
-
-/**
-* Shortcut to bezierCurveTo.
-* @method Phaser.BitmapData.prototype.bt
-*/
-Phaser.BitmapData.prototype.bt = Phaser.BitmapData.prototype.bezierCurveTo;
-
-/**
-* Shortcut to quadraticCurveTo.
-* @method Phaser.BitmapData.prototype.qt
-*/
-Phaser.BitmapData.prototype.qt = Phaser.BitmapData.prototype.quadraticCurveTo;
-
-/**
-* Shortcut to arc.
-* @method Phaser.BitmapData.prototype.a
-*/
-Phaser.BitmapData.prototype.a = Phaser.BitmapData.prototype.arc;
-
-/**
-* Shortcut to rect.
-* @method Phaser.BitmapData.prototype.r
-*/
-Phaser.BitmapData.prototype.r = Phaser.BitmapData.prototype.rect;
-
-/**
-* Shortcut to closePath.
-* @method Phaser.BitmapData.prototype.cp
-*/
-Phaser.BitmapData.prototype.cp = Phaser.BitmapData.prototype.closePath;
-
-/**
-* Shortcut to clear.
-* @method Phaser.BitmapData.prototype.c
-*/
-Phaser.BitmapData.prototype.c = Phaser.BitmapData.prototype.clear;
-
-/**
-* Shortcut to beginFill.
-* @method Phaser.BitmapData.prototype.f
-*/
-Phaser.BitmapData.prototype.f = Phaser.BitmapData.prototype.beginFill;
-
-/**
-* Shortcut to beginLinearGradientFill.
-* @method Phaser.BitmapData.prototype.lf
-*/
-Phaser.BitmapData.prototype.lf = Phaser.BitmapData.prototype.beginLinearGradientFill;
-
-/**
-* Shortcut to beginRadialGradientFill.
-* @method Phaser.BitmapData.prototype.rf
-*/
-Phaser.BitmapData.prototype.rf = Phaser.BitmapData.prototype.beginRadialGradientFill;
-
-/**
-* Shortcut to beginBitmapFill.
-* @method Phaser.BitmapData.prototype.bf
-*/
-//Phaser.BitmapData.prototype.bf = Phaser.BitmapData.prototype.beginBitmapFill;
-
-/**
-* Shortcut to endFill.
-* @method Phaser.BitmapData.prototype.ef
-*/
-Phaser.BitmapData.prototype.ef = Phaser.BitmapData.prototype.endFill;
-
-/**
-* Shortcut to setStrokeStyle.
-* @method Phaser.BitmapData.prototype.ss
-*/
-Phaser.BitmapData.prototype.ss = Phaser.BitmapData.prototype.setStrokeStyle;
-
-/**
-* Shortcut to beginStroke.
-* @method Phaser.BitmapData.prototype.s
-*/
-Phaser.BitmapData.prototype.s = Phaser.BitmapData.prototype.beginStroke;
-
-/**
-* Shortcut to beginLinearGradientStroke.
-* @method Phaser.BitmapData.prototype.ls
-*/
-Phaser.BitmapData.prototype.ls = Phaser.BitmapData.prototype.beginLinearGradientStroke;
-
-/**
-* Shortcut to beginRadialGradientStroke.
-* @method Phaser.BitmapData.prototype.rs
-*/
-Phaser.BitmapData.prototype.rs = Phaser.BitmapData.prototype.beginRadialGradientStroke;
-
-/**
-* Shortcut to beginBitmapStroke.
-* @method Phaser.BitmapData.prototype.bs
-*/
-// Phaser.BitmapData.prototype.bs = Phaser.BitmapData.prototype.beginBitmapStroke;
-
-/**
-* Shortcut to endStroke.
-* @method Phaser.BitmapData.prototype.es
-*/
-// Phaser.BitmapData.prototype.es = Phaser.BitmapData.prototype.endStroke;
-
-/**
-* Shortcut to rect.
-* @method Phaser.BitmapData.prototype.dr
-*/
-Phaser.BitmapData.prototype.dr = Phaser.BitmapData.prototype.rect;
-
-/**
-* Shortcut to drawRoundRect.
-* @method Phaser.BitmapData.prototype.rr
-*/
-// Phaser.BitmapData.prototype.rr = Phaser.BitmapData.prototype.drawRoundRect;
-
-/**
-* Shortcut to drawRoundRectComplex.
-* @method Phaser.BitmapData.prototype.rc
-*/
-// Phaser.BitmapData.prototype.rc = Phaser.BitmapData.prototype.drawRoundRectComplex;
-
-/**
-* Shortcut to drawCircle.
-* @method Phaser.BitmapData.prototype.dc
-*/
-Phaser.BitmapData.prototype.dc = Phaser.BitmapData.prototype.circle;
-
-/**
-* Shortcut to drawEllipse.
-* @method Phaser.BitmapData.prototype.de
-*/
-Phaser.BitmapData.prototype.de = Phaser.BitmapData.prototype.ellipse;
-
-/**
-* Shortcut to drawPolyStar.
-* @method Phaser.BitmapData.prototype.dp
-*/
-// Phaser.BitmapData.prototype.dp = Phaser.BitmapData.prototype.drawPolyStar;
-
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -20719,23 +33476,6 @@ Phaser.Sprite = function (game, x, y, key, frame) {
* @property {Phaser.Game} game - A reference to the currently running Game.
*/
this.game = game;
-
- /**
- * @property {boolean} exists - If exists = false then the Sprite isn't updated by the core game loop or physics subsystem at all.
- * @default
- */
- this.exists = true;
-
- /**
- * @property {boolean} alive - This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering.
- * @default
- */
- this.alive = true;
-
- /**
- * @property {Phaser.Group} group - The parent Group of this Sprite. This is usually set after Sprite instantiation by the parent.
- */
- this.group = null;
/**
* @property {string} name - The user defined name given to this Sprite.
@@ -20749,20 +33489,6 @@ Phaser.Sprite = function (game, x, y, key, frame) {
*/
this.type = Phaser.SPRITE;
- /**
- * @property {number} renderOrderID - Used by the Renderer and Input Manager to control picking order.
- * @default
- */
- this.renderOrderID = -1;
-
- /**
- * If you would like the Sprite to have a lifespan once 'born' you can set this to a positive value. Handy for particles, bullets, etc.
- * The lifespan is decremented by game.time.elapsed each update, once it reaches zero the kill() function is called.
- * @property {number} lifespan - The lifespan of the Sprite (in ms) before it will be killed.
- * @default
- */
- this.lifespan = 0;
-
/**
* @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Sprite or its components.
*/
@@ -20773,104 +33499,28 @@ Phaser.Sprite = function (game, x, y, key, frame) {
*/
this.animations = new Phaser.AnimationManager(this);
- /**
- * @property {Phaser.InputHandler} input - The Input Handler Component.
- */
- this.input = new Phaser.InputHandler(this);
-
/**
* @property {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture.
*/
this.key = key;
/**
- * @property {Phaser.Frame} currentFrame - A reference to the currently displayed frame.
+ * @property {number} _frame - Internal cache var.
+ * @private
*/
- this.currentFrame = null;
-
- if (key instanceof Phaser.RenderTexture)
- {
- PIXI.Sprite.call(this, key);
-
- this.currentFrame = this.game.cache.getTextureFrame(key.name);
- }
- else if (key instanceof Phaser.BitmapData)
- {
- PIXI.Sprite.call(this, key.texture, key.textureFrame);
-
- this.currentFrame = key.textureFrame;
- }
- else if (key instanceof PIXI.Texture)
- {
- PIXI.Sprite.call(this, key);
-
- this.currentFrame = frame;
- }
- else
- {
- if (key === null || typeof key === 'undefined')
- {
- key = '__default';
- this.key = key;
- }
- else if (typeof key === 'string' && this.game.cache.checkImageKey(key) === false)
- {
- key = '__missing';
- this.key = key;
- }
-
- PIXI.Sprite.call(this, PIXI.TextureCache[key]);
-
- if (this.game.cache.isSpriteSheet(key))
- {
- this.animations.loadFrameData(this.game.cache.getFrameData(key));
-
- if (frame !== null)
- {
- if (typeof frame === 'string')
- {
- this.frameName = frame;
- }
- else
- {
- this.frame = frame;
- }
- }
- }
- else
- {
- this.currentFrame = this.game.cache.getFrame(key);
- }
- }
+ this._frame = 0;
/**
- * The rectangular area from the texture that will be rendered.
- * @property {Phaser.Rectangle} textureRegion
+ * @property {string} _frameName - Internal cache var.
+ * @private
*/
- this.textureRegion = new Phaser.Rectangle(this.texture.frame.x, this.texture.frame.y, this.texture.frame.width, this.texture.frame.height);
+ this._frameName = '';
- /**
- * The anchor sets the origin point of the texture.
- * The default is 0,0 this means the textures origin is the top left
- * Setting than anchor to 0.5,0.5 means the textures origin is centered
- * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right
- *
- * @property {Phaser.Point} anchor - The anchor around which rotation and scaling takes place.
- */
- this.anchor = new Phaser.Point();
+ PIXI.Sprite.call(this, PIXI.TextureCache['__default']);
- /**
- * @property {number} x - The x coordinate in world space of this Sprite.
- */
- this.x = x;
-
- /**
- * @property {number} y - The y coordinate in world space of this Sprite.
- */
- this.y = y;
+ this.loadTexture(key, frame);
- this.position.x = x;
- this.position.y = y;
+ this.position.set(x, y);
/**
* @property {Phaser.Point} world - The world coordinates of this Sprite. This differs from the x/y coordinates which are relative to the Sprites container.
@@ -20880,6 +33530,7 @@ Phaser.Sprite = function (game, x, y, key, frame) {
/**
* Should this Sprite be automatically culled if out of range of the camera?
* A culled sprite has its renderable property set to 'false'.
+ * Be advised this is quite an expensive operation, as it has to calculate the bounds of the object every frame, so only enable it if you really need it.
*
* @property {boolean} autoCull - A flag indicating if the Sprite should be automatically camera culled or not.
* @default
@@ -20887,122 +33538,14 @@ Phaser.Sprite = function (game, x, y, key, frame) {
this.autoCull = false;
/**
- * @property {Phaser.Point} scale - The scale of the Sprite when rendered. By default it's set to 1 (no scale). You can modify it via scale.x or scale.y or scale.setTo(x, y). A value of 1 means no change to the scale, 0.5 means "half the size", 2 means "twice the size", etc.
+ * @property {Phaser.InputHandler|null} input - The Input Handler for this object. Needs to be enabled with image.inputEnabled = true before you can use it.
*/
- this.scale = new Phaser.Point(1, 1);
+ this.input = null;
/**
- * @property {object} _cache - A mini cache for storing all of the calculated values.
- * @private
+ * @property {Phaser.Physics.Body|null} body - The Sprites physics Body. Will be null unless physics has been enabled via `Sprite.physicsEnabled = true`.
*/
- this._cache = {
-
- fresh: true,
- dirty: false,
-
- // Transform cache
- a00: -1,
- a01: -1,
- a02: -1,
- a10: -1,
- a11: -1,
- a12: -1,
- id: -1,
-
- // Input specific transform cache
- i01: -1,
- i10: -1,
- idi: -1,
-
- // Bounds check
- left: null,
- right: null,
- top: null,
- bottom: null,
-
- // delta cache
- prevX: x,
- prevY: y,
-
- // The previous calculated position
- x: -1,
- y: -1,
-
- // The actual scale values based on the worldTransform
- scaleX: 1,
- scaleY: 1,
-
- // The width/height of the image, based on the un-modified frame size multiplied by the final calculated scale size
- width: this.currentFrame.sourceSizeW,
- height: this.currentFrame.sourceSizeH,
-
- // The actual width/height of the image if from a trimmed atlas, multiplied by the final calculated scale size
- halfWidth: Math.floor(this.currentFrame.sourceSizeW / 2),
- halfHeight: Math.floor(this.currentFrame.sourceSizeH / 2),
-
- // The width/height of the image, based on the un-modified frame size multiplied by the final calculated scale size
- calcWidth: -1,
- calcHeight: -1,
-
- // The current frame details
- // frameID: this.currentFrame.uuid, frameWidth: this.currentFrame.width, frameHeight: this.currentFrame.height,
- frameID: -1,
- frameWidth: this.currentFrame.width,
- frameHeight: this.currentFrame.height,
-
- // If this sprite visible to the camera (regardless of being set to visible or not)
- cameraVisible: true,
-
- // Crop cache
- cropX: 0,
- cropY: 0,
- cropWidth: this.currentFrame.sourceSizeW,
- cropHeight: this.currentFrame.sourceSizeH
-
- };
-
- /**
- * @property {Phaser.Point} offset - Corner point defaults. Should not typically be modified.
- */
- this.offset = new Phaser.Point();
-
- /**
- * @property {Phaser.Point} center - A Point containing the center coordinate of the Sprite. Takes rotation and scale into account.
- */
- this.center = new Phaser.Point(x + Math.floor(this._cache.width / 2), y + Math.floor(this._cache.height / 2));
-
- /**
- * @property {Phaser.Point} topLeft - A Point containing the top left coordinate of the Sprite. Takes rotation and scale into account.
- */
- this.topLeft = new Phaser.Point(x, y);
-
- /**
- * @property {Phaser.Point} topRight - A Point containing the top right coordinate of the Sprite. Takes rotation and scale into account.
- */
- this.topRight = new Phaser.Point(x + this._cache.width, y);
-
- /**
- * @property {Phaser.Point} bottomRight - A Point containing the bottom right coordinate of the Sprite. Takes rotation and scale into account.
- */
- this.bottomRight = new Phaser.Point(x + this._cache.width, y + this._cache.height);
-
- /**
- * @property {Phaser.Point} bottomLeft - A Point containing the bottom left coordinate of the Sprite. Takes rotation and scale into account.
- */
- this.bottomLeft = new Phaser.Point(x, y + this._cache.height);
-
- /**
- * This Rectangle object fully encompasses the Sprite and is updated in real-time.
- * The bounds is the full bounding area after rotation and scale have been taken into account. It should not be modified directly.
- * It's used for Camera culling and physics body alignment.
- * @property {Phaser.Rectangle} bounds
- */
- this.bounds = new Phaser.Rectangle(x, y, this._cache.width, this._cache.height);
-
- /**
- * @property {Phaser.Physics.Arcade.Body} body - By default Sprites have a Phaser.Physics Body attached to them. You can operate physics actions via this property, or null it to skip all physics updates.
- */
- this.body = new Phaser.Physics.Arcade.Body(this);
+ this.body = null;
/**
* @property {number} health - Health value. Used in combination with damage() to allow for quick killing of Sprites.
@@ -21010,54 +33553,27 @@ Phaser.Sprite = function (game, x, y, key, frame) {
this.health = 1;
/**
- * @property {boolean} inWorld - This value is set to true if the Sprite is positioned within the World, otherwise false.
- */
- this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds);
-
- /**
- * @property {number} inWorldThreshold - A threshold value applied to the inWorld check. If you don't want a Sprite to be considered "out of the world" until at least 100px away for example then set it to 100.
+ * If you would like the Sprite to have a lifespan once 'born' you can set this to a positive value. Handy for particles, bullets, etc.
+ * The lifespan is decremented by game.time.elapsed each update, once it reaches zero the kill() function is called.
+ * @property {number} lifespan - The lifespan of the Sprite (in ms) before it will be killed.
* @default
*/
- this.inWorldThreshold = 0;
+ this.lifespan = 0;
/**
- * @property {boolean} outOfBoundsKill - If true the Sprite is killed as soon as Sprite.inWorld is false.
+ * If true the Sprite checks if it is still within the world each frame, when it leaves the world it dispatches Sprite.events.onOutOfBounds
+ * and optionally kills the sprite (if Sprite.outOfBoundsKill is true). By default this is disabled because the Sprite has to calculate its
+ * bounds every frame to support it, and not all games need it. Enable it by setting the value to true.
+ * @property {boolean} checkWorldBounds
+ * @default
+ */
+ this.checkWorldBounds = false;
+
+ /**
+ * @property {boolean} outOfBoundsKill - If true Sprite.kill is called as soon as Sprite.inWorld returns false, as long as Sprite.checkWorldBounds is true.
* @default
*/
this.outOfBoundsKill = false;
-
- /**
- * @property {boolean} _outOfBoundsFired - Internal flag.
- * @private
- * @default
- */
- this._outOfBoundsFired = false;
-
- /**
- * A Sprite that is fixed to the camera ignores the position of any ancestors in the display list and uses its x/y coordinates as offsets from the top left of the camera.
- * @property {boolean} fixedToCamera - Fixes this Sprite to the Camera.
- * @default
- */
- this.fixedToCamera = false;
-
- /**
- * @property {Phaser.Point} cameraOffset - If this Sprite is fixed to the camera then use this Point to specify how far away from the Camera x/y it's rendered.
- */
- this.cameraOffset = new Phaser.Point(x, y);
-
- /**
- * You can crop the Sprites texture by modifying the crop properties. For example crop.width = 50 would set the Sprite to only render 50px wide.
- * The crop is only applied if you have set Sprite.cropEnabled to true.
- * @property {Phaser.Rectangle} crop - The crop Rectangle applied to the Sprite texture before rendering.
- * @default
- */
- this.crop = new Phaser.Rectangle(0, 0, this._cache.width, this._cache.height);
-
- /**
- * @property {boolean} cropEnabled - If true the Sprite.crop property is used to crop the texture before render. Set to false to disable.
- * @default
- */
- this.cropEnabled = false;
/**
* @property {boolean} debug - Handy flag to use with Game.enableStep
@@ -21065,50 +33581,75 @@ Phaser.Sprite = function (game, x, y, key, frame) {
*/
this.debug = false;
- this.updateCache();
- this.updateBounds();
+ /**
+ * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view.
+ */
+ this.cameraOffset = new Phaser.Point();
/**
- * @property {PIXI.Point} pivot - The pivot point of the displayObject that it rotates around.
+ * A small internal cache:
+ * 0 = previous position.x
+ * 1 = previous position.y
+ * 2 = previous rotation
+ * 3 = renderID
+ * 4 = fresh? (0 = no, 1 = yes)
+ * 5 = outOfBoundsFired (0 = no, 1 = yes)
+ * 6 = exists (0 = no, 1 = yes)
+ * 7 = fixed to camera (0 = no, 1 = yes)
+ * @property {Int16Array} _cache
+ * @private
*/
+ this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]);
+
+ /**
+ * @property {Phaser.Rectangle} _bounds - Internal cache var.
+ * @private
+ */
+ this._bounds = new Phaser.Rectangle();
};
-// Needed to keep the PIXI.Sprite constructor in the prototype chain (as the core pixi renderer uses an instanceof check sadly)
Phaser.Sprite.prototype = Object.create(PIXI.Sprite.prototype);
Phaser.Sprite.prototype.constructor = Phaser.Sprite;
/**
-* Automatically called by World.preUpdate. Handles cache updates, lifespan checks, animation updates and physics updates.
+* Automatically called by World.preUpdate.
*
* @method Phaser.Sprite#preUpdate
* @memberof Phaser.Sprite
+* @return {boolean} True if the Sprite was rendered, otherwise false.
*/
Phaser.Sprite.prototype.preUpdate = function() {
- if (this._cache.fresh)
+ if (this._cache[4] === 1)
{
- this.world.setTo(this.parent.position.x + this.x, this.parent.position.y + this.y);
- this.worldTransform[2] = this.world.x;
- this.worldTransform[5] = this.world.y;
- this._cache.fresh = false;
+ this.world.setTo(this.parent.position.x + this.position.x, this.parent.position.y + this.position.y);
+ this.worldTransform.tx = this.world.x;
+ this.worldTransform.ty = this.world.y;
+ this._cache[0] = this.world.x;
+ this._cache[1] = this.world.y;
+ this._cache[2] = this.rotation;
+ this._cache[4] = 0;
- if (this.body)
- {
- this.body.x = (this.world.x - (this.anchor.x * this.width)) + this.body.offset.x;
- this.body.y = (this.world.y - (this.anchor.y * this.height)) + this.body.offset.y;
- this.body.preX = this.body.x;
- this.body.preY = this.body.y;
- }
+ // if (this.body)
+ // {
+ // this.body.x = (this.world.x - (this.anchor.x * this.width)) + this.body.offset.x;
+ // this.body.y = (this.world.y - (this.anchor.y * this.height)) + this.body.offset.y;
+ // this.body.preX = this.body.x;
+ // this.body.preY = this.body.y;
+ // }
- return;
+ return false;
}
- if (!this.exists || (this.group && !this.group.exists))
+ this._cache[0] = this.world.x;
+ this._cache[1] = this.world.y;
+ this._cache[2] = this.rotation;
+
+ if (!this.exists || !this.parent.exists)
{
- this.renderOrderID = -1;
-
- // Skip children if not exists
+ // Reset the renderOrderID
+ this._cache[3] = -1;
return false;
}
@@ -21123,28 +33664,51 @@ Phaser.Sprite.prototype.preUpdate = function() {
}
}
- this._cache.dirty = false;
+ // Cache the bounds if we need it
+ if (this.autoCull || this.checkWorldBounds)
+ {
+ this._bounds.copyFrom(this.getBounds());
+ }
+
+ if (this.autoCull)
+ {
+ // Won't get rendered but will still get its transform updated
+ this.renderable = this.game.world.camera.screenView.intersects(this._bounds);
+ }
+
+ if (this.checkWorldBounds)
+ {
+ // The Sprite is already out of the world bounds, so let's check to see if it has come back again
+ if (this._cache[5] === 1 && this.game.world.bounds.intersects(this._bounds))
+ {
+ this._cache[5] = 0;
+ }
+ else if (this._cache[5] === 0 && !this.game.world.bounds.intersects(this._bounds))
+ {
+ // The Sprite WAS in the screen, but has now left.
+ this._cache[5] = 1;
+ this.events.onOutOfBounds.dispatch(this);
+
+ if (this.outOfBoundsKill)
+ {
+ this.kill();
+ return false;
+ }
+ }
+ }
+
+ this.world.setTo(this.game.camera.x + this.worldTransform.tx, this.game.camera.y + this.worldTransform.ty);
if (this.visible)
{
- this.renderOrderID = this.game.world.currentRenderOrderID++;
+ this._cache[3] = this.game.world.currentRenderOrderID++;
}
- this.updateCache();
-
- this.updateAnimation();
-
- this.updateCrop();
-
- // Re-run the camera visibility check
- if (this._cache.dirty || this.world.x !== this._cache.prevX || this.world.y !== this._cache.prevY)
- {
- this.updateBounds();
- }
+ this.animations.update();
if (this.body)
{
- this.body.preUpdate();
+ // this.body.preUpdate();
}
return true;
@@ -21152,220 +33716,12 @@ Phaser.Sprite.prototype.preUpdate = function() {
};
/**
-* Internal function called by preUpdate.
+* Override and use this function in your own custom objects to handle any update requirements you may have.
*
-* @method Phaser.Sprite#updateCache
+* @method Phaser.Sprite#update
* @memberof Phaser.Sprite
*/
-Phaser.Sprite.prototype.updateCache = function() {
-
- this._cache.prevX = this.world.x;
- this._cache.prevY = this.world.y;
-
- if (this.fixedToCamera)
- {
- this.x = this.game.camera.view.x + this.cameraOffset.x;
- this.y = this.game.camera.view.y + this.cameraOffset.y;
- }
-
- this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]);
-
- if (this.worldTransform[1] != this._cache.i01 || this.worldTransform[3] != this._cache.i10 || this.worldTransform[0] != this._cache.a00 || this.worldTransform[41] != this._cache.a11)
- {
- this._cache.a00 = this.worldTransform[0]; // scaleX a |a c tx|
- this._cache.a01 = this.worldTransform[1]; // skewY c |b d ty|
- this._cache.a10 = this.worldTransform[3]; // skewX b |0 0 1|
- this._cache.a11 = this.worldTransform[4]; // scaleY d
-
- this._cache.i01 = this.worldTransform[1]; // skewY c (remains non-modified for input checks)
- this._cache.i10 = this.worldTransform[3]; // skewX b (remains non-modified for input checks)
-
- this._cache.scaleX = Math.sqrt((this._cache.a00 * this._cache.a00) + (this._cache.a01 * this._cache.a01)); // round this off a bit?
- this._cache.scaleY = Math.sqrt((this._cache.a10 * this._cache.a10) + (this._cache.a11 * this._cache.a11)); // round this off a bit?
-
- this._cache.a01 *= -1;
- this._cache.a10 *= -1;
-
- this._cache.id = 1 / (this._cache.a00 * this._cache.a11 + this._cache.a01 * -this._cache.a10);
- this._cache.idi = 1 / (this._cache.a00 * this._cache.a11 + this._cache.i01 * -this._cache.i10);
-
- this._cache.dirty = true;
- }
-
- this._cache.a02 = this.worldTransform[2]; // translateX tx
- this._cache.a12 = this.worldTransform[5]; // translateY ty
-
-};
-
-/**
-* Internal function called by preUpdate.
-*
-* @method Phaser.Sprite#updateAnimation
-* @memberof Phaser.Sprite
-*/
-Phaser.Sprite.prototype.updateAnimation = function() {
-
- if (this.animations.update() || (this.currentFrame && this.currentFrame.uuid != this._cache.frameID))
- {
- this._cache.frameID = this.currentFrame.uuid;
-
- this._cache.frameWidth = this.texture.frame.width;
- this._cache.frameHeight = this.texture.frame.height;
-
- this._cache.width = this.currentFrame.width;
- this._cache.height = this.currentFrame.height;
-
- this._cache.halfWidth = Math.floor(this._cache.width / 2);
- this._cache.halfHeight = Math.floor(this._cache.height / 2);
-
- this._cache.dirty = true;
- }
-
-};
-
-/**
-* Internal function called by preUpdate.
-*
-* @method Phaser.Sprite#updateCrop
-* @memberof Phaser.Sprite
-*/
-Phaser.Sprite.prototype.updateCrop = function() {
-
- // This only runs if crop is enabled
- if (this.cropEnabled && (this.crop.width != this._cache.cropWidth || this.crop.height != this._cache.cropHeight || this.crop.x != this._cache.cropX || this.crop.y != this._cache.cropY))
- {
- this.crop.floorAll();
-
- this._cache.cropX = this.crop.x;
- this._cache.cropY = this.crop.y;
- this._cache.cropWidth = this.crop.width;
- this._cache.cropHeight = this.crop.height;
-
- this.texture.frame = this.crop;
- this.texture.width = this.crop.width;
- this.texture.height = this.crop.height;
-
- this.texture.updateFrame = true;
-
- PIXI.Texture.frameUpdates.push(this.texture);
- }
-
-};
-
-/**
-* Internal function called by preUpdate.
-*
-* @method Phaser.Sprite#updateBounds
-* @memberof Phaser.Sprite
-*/
-Phaser.Sprite.prototype.updateBounds = function() {
-
- this.offset.setTo(this._cache.a02 - (this.anchor.x * this.width), this._cache.a12 - (this.anchor.y * this.height));
-
- this.getLocalPosition(this.center, this.offset.x + (this.width / 2), this.offset.y + (this.height / 2));
- this.getLocalPosition(this.topLeft, this.offset.x, this.offset.y);
- this.getLocalPosition(this.topRight, this.offset.x + this.width, this.offset.y);
- this.getLocalPosition(this.bottomLeft, this.offset.x, this.offset.y + this.height);
- this.getLocalPosition(this.bottomRight, this.offset.x + this.width, this.offset.y + this.height);
-
- this._cache.left = Phaser.Math.min(this.topLeft.x, this.topRight.x, this.bottomLeft.x, this.bottomRight.x);
- this._cache.right = Phaser.Math.max(this.topLeft.x, this.topRight.x, this.bottomLeft.x, this.bottomRight.x);
- this._cache.top = Phaser.Math.min(this.topLeft.y, this.topRight.y, this.bottomLeft.y, this.bottomRight.y);
- this._cache.bottom = Phaser.Math.max(this.topLeft.y, this.topRight.y, this.bottomLeft.y, this.bottomRight.y);
-
- this.bounds.setTo(this._cache.left, this._cache.top, this._cache.right - this._cache.left, this._cache.bottom - this._cache.top);
-
- this.updateFrame = true;
-
- if (this.inWorld === false)
- {
- // Sprite WAS out of the screen, is it still?
- this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds, this.inWorldThreshold);
-
- if (this.inWorld)
- {
- // It's back again, reset the OOB check
- this._outOfBoundsFired = false;
- }
- }
- else
- {
- // Sprite WAS in the screen, has it now left?
- this.inWorld = Phaser.Rectangle.intersects(this.bounds, this.game.world.bounds, this.inWorldThreshold);
-
- if (this.inWorld === false)
- {
- this.events.onOutOfBounds.dispatch(this);
- this._outOfBoundsFired = true;
-
- if (this.outOfBoundsKill)
- {
- this.kill();
- }
- }
- }
-
- this._cache.cameraVisible = Phaser.Rectangle.intersects(this.game.world.camera.screenView, this.bounds, 0);
-
- if (this.autoCull)
- {
- // Won't get rendered but will still get its transform updated
- this.renderable = this._cache.cameraVisible;
- }
-
-};
-
-/**
-* Gets the local position of a coordinate relative to the Sprite, factoring in rotation and scale.
-* Mostly only used internally.
-*
-* @method Phaser.Sprite#getLocalPosition
-* @memberof Phaser.Sprite
-* @param {Phaser.Point} p - The Point object to store the results in.
-* @param {number} x - x coordinate within the Sprite to translate.
-* @param {number} y - y coordinate within the Sprite to translate.
-* @return {Phaser.Point} The translated point.
-*/
-Phaser.Sprite.prototype.getLocalPosition = function(p, x, y) {
-
- p.x = ((this._cache.a11 * this._cache.id * x + -this._cache.a01 * this._cache.id * y + (this._cache.a12 * this._cache.a01 - this._cache.a02 * this._cache.a11) * this._cache.id) * this.scale.x) + this._cache.a02;
- p.y = ((this._cache.a00 * this._cache.id * y + -this._cache.a10 * this._cache.id * x + (-this._cache.a12 * this._cache.a00 + this._cache.a02 * this._cache.a10) * this._cache.id) * this.scale.y) + this._cache.a12;
-
- return p;
-
-};
-
-/**
-* Gets the local unmodified position of a coordinate relative to the Sprite, factoring in rotation and scale.
-* Mostly only used internally by the Input Manager, but also useful for custom hit detection.
-*
-* @method Phaser.Sprite#getLocalUnmodifiedPosition
-* @memberof Phaser.Sprite
-* @param {Phaser.Point} p - The Point object to store the results in.
-* @param {number} gx - x coordinate within the Sprite to translate.
-* @param {number} gy - y coordinate within the Sprite to translate.
-* @return {Phaser.Point} The translated point.
-*/
-Phaser.Sprite.prototype.getLocalUnmodifiedPosition = function(p, gx, gy) {
-
- p.x = (this._cache.a11 * this._cache.idi * gx + -this._cache.i01 * this._cache.idi * gy + (this._cache.a12 * this._cache.i01 - this._cache.a02 * this._cache.a11) * this._cache.idi) + (this.anchor.x * this._cache.width);
- p.y = (this._cache.a00 * this._cache.idi * gy + -this._cache.i10 * this._cache.idi * gx + (-this._cache.a12 * this._cache.a00 + this._cache.a02 * this._cache.i10) * this._cache.idi) + (this.anchor.y * this._cache.height);
-
- return p;
-
-};
-
-/**
-* Resets the Sprite.crop value back to the frame dimensions.
-*
-* @method Phaser.Sprite#resetCrop
-* @memberof Phaser.Sprite
-*/
-Phaser.Sprite.prototype.resetCrop = function() {
-
- this.crop = new Phaser.Rectangle(0, 0, this._cache.width, this._cache.height);
- this.texture.setFrame(this.crop);
- this.cropEnabled = false;
+Phaser.Sprite.prototype.update = function() {
};
@@ -21388,20 +33744,13 @@ Phaser.Sprite.prototype.postUpdate = function() {
{
this.body.postUpdate();
}
+ }
- if (this.fixedToCamera)
- {
- this._cache.x = this.game.camera.view.x + this.cameraOffset.x;
- this._cache.y = this.game.camera.view.y + this.cameraOffset.y;
- }
- else
- {
- this._cache.x = this.x;
- this._cache.y = this.y;
- }
-
- this.position.x = this._cache.x;
- this.position.y = this._cache.y;
+ // Fixed to Camera?
+ if (this._cache[7] === 1)
+ {
+ this.position.x = this.game.camera.view.x + this.cameraOffset.x;
+ this.position.y = this.game.camera.view.y + this.cameraOffset.y;
}
};
@@ -21417,78 +33766,111 @@ Phaser.Sprite.prototype.postUpdate = function() {
*/
Phaser.Sprite.prototype.loadTexture = function (key, frame) {
- this.key = key;
+ frame = frame || 0;
if (key instanceof Phaser.RenderTexture)
{
- this.currentFrame = this.game.cache.getTextureFrame(key.name);
+ this.key = key.key;
+ this.setTexture(key);
+ return;
}
else if (key instanceof Phaser.BitmapData)
{
+ this.key = key.key;
this.setTexture(key.texture);
- this.currentFrame = key.textureFrame;
+ return;
}
else if (key instanceof PIXI.Texture)
{
- this.currentFrame = frame;
+ this.key = key;
+ this.setTexture(key);
+ return;
}
else
{
- if (typeof key === 'undefined' || this.game.cache.checkImageKey(key) === false)
+ if (key === null || typeof key === 'undefined')
{
- key = '__default';
- this.key = key;
+ this.key = '__default';
+ this.setTexture(PIXI.TextureCache[this.key]);
+ return;
+ }
+ else if (typeof key === 'string' && !this.game.cache.checkImageKey(key))
+ {
+ this.key = '__missing';
+ this.setTexture(PIXI.TextureCache[this.key]);
+ return;
}
if (this.game.cache.isSpriteSheet(key))
{
+ this.key = key;
+
+ // var frameData = this.game.cache.getFrameData(key);
this.animations.loadFrameData(this.game.cache.getFrameData(key));
- if (typeof frame !== 'undefined')
+ if (typeof frame === 'string')
{
- if (typeof frame === 'string')
- {
- this.frameName = frame;
- }
- else
- {
- this.frame = frame;
- }
+ this.frameName = frame;
+ }
+ else
+ {
+ this.frame = frame;
}
}
else
{
- this.currentFrame = this.game.cache.getFrame(key);
+ this.key = key;
this.setTexture(PIXI.TextureCache[key]);
+ return;
}
}
};
/**
-* Moves the sprite so its center is located on the given x and y coordinates.
-* Doesn't change the anchor point of the sprite.
-*
-* @method Phaser.Sprite#centerOn
+* Crop allows you to crop the texture used to display this Sprite.
+* Cropping takes place from the top-left of the Sprite and can be modified in real-time by providing an updated rectangle object.
+*
+* @method Phaser.Sprite#crop
* @memberof Phaser.Sprite
-* @param {number} x - The x coordinate (in world space) to position the Sprite at.
-* @param {number} y - The y coordinate (in world space) to position the Sprite at.
-* @return (Phaser.Sprite) This instance.
+* @param {Phaser.Rectangle} rect - The Rectangle to crop the Sprite to. Pass null or no parameters to clear a previously set crop rectangle.
*/
-Phaser.Sprite.prototype.centerOn = function(x, y) {
+Phaser.Sprite.prototype.crop = function(rect) {
- if (this.fixedToCamera)
+ if (typeof rect === 'undefined' || rect === null)
{
- this.cameraOffset.x = x + (this.cameraOffset.x - this.center.x);
- this.cameraOffset.y = y + (this.cameraOffset.y - this.center.y);
+ // Clear any crop that may be set
+ if (this.texture.hasOwnProperty('sourceWidth'))
+ {
+ this.texture.setFrame(new Phaser.Rectangle(0, 0, this.texture.sourceWidth, this.texture.sourceHeight));
+ }
}
else
{
- this.x = x + (this.x - this.center.x);
- this.y = y + (this.y - this.center.y);
- }
+ // Do we need to clone the PIXI.Texture object?
+ if (this.texture instanceof PIXI.Texture)
+ {
+ // Yup, let's rock it ...
+ var local = {};
- return this;
+ Phaser.Utils.extend(true, local, this.texture);
+
+ local.sourceWidth = local.width;
+ local.sourceHeight = local.height;
+ local.frame = rect;
+ local.width = rect.width;
+ local.height = rect.height;
+
+ this.texture = local;
+
+ this.texture.updateFrame = true;
+ PIXI.Texture.frameUpdates.push(this.texture);
+ }
+ else
+ {
+ this.texture.setFrame(rect);
+ }
+ }
};
@@ -21559,9 +33941,9 @@ Phaser.Sprite.prototype.destroy = function() {
this.filters = null;
}
- if (this.group)
+ if (this.parent)
{
- this.group.remove(this);
+ this.parent.remove(this);
}
if (this.input)
@@ -21569,11 +33951,6 @@ Phaser.Sprite.prototype.destroy = function() {
this.input.destroy();
}
- if (this.events)
- {
- this.events.destroy();
- }
-
if (this.animations)
{
this.animations.destroy();
@@ -21584,6 +33961,11 @@ Phaser.Sprite.prototype.destroy = function() {
this.body.destroy();
}
+ if (this.events)
+ {
+ this.events.destroy();
+ }
+
this.alive = false;
this.exists = false;
this.visible = false;
@@ -21633,11 +34015,9 @@ Phaser.Sprite.prototype.reset = function(x, y, health) {
if (typeof health === 'undefined') { health = 1; }
- this.x = x;
- this.y = y;
this.world.setTo(x, y);
- this.position.x = this.x;
- this.position.y = this.y;
+ this.position.x = x;
+ this.position.y = y;
this.alive = true;
this.exists = true;
this.visible = true;
@@ -21648,7 +34028,7 @@ Phaser.Sprite.prototype.reset = function(x, y, health) {
if (this.body)
{
- this.body.reset(false);
+ this.body.reset(x, y, false, false);
}
return this;
@@ -21663,15 +34043,18 @@ Phaser.Sprite.prototype.reset = function(x, y, health) {
* @memberof Phaser.Sprite
* @return (Phaser.Sprite) This instance.
*/
-Phaser.Sprite.prototype.bringToTop = function() {
+Phaser.Sprite.prototype.bringToTop = function(child) {
- if (this.group)
+ if (typeof child === 'undefined')
{
- this.group.bringToTop(this);
+ if (this.parent)
+ {
+ this.parent.bringToTop(this);
+ }
}
else
{
- this.game.world.bringToTop(this);
+
}
return this;
@@ -21700,48 +34083,110 @@ Phaser.Sprite.prototype.play = function (name, frameRate, loop, killOnComplete)
};
/**
-* Returns the delta x value. The difference between Sprite.x now and in the previous step.
+* Indicates the rotation of the Sprite, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
+* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90.
+* If you wish to work in radians instead of degrees use the property Sprite.rotation instead. Working in radians is also a little faster as it doesn't have to convert the angle.
+*
+* @name Phaser.Sprite#angle
+* @property {number} angle - The angle of this Sprite in degrees.
+*/
+Object.defineProperty(Phaser.Sprite.prototype, "angle", {
+
+ get: function() {
+
+ return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation));
+
+ },
+
+ set: function(value) {
+
+ this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
+
+ }
+
+});
+
+/**
+* Returns the delta x value. The difference between world.x now and in the previous step.
+*
* @name Phaser.Sprite#deltaX
* @property {number} deltaX - The delta value. Positive if the motion was to the right, negative if to the left.
* @readonly
*/
-Object.defineProperty(Phaser.Sprite.prototype, 'deltaX', {
+Object.defineProperty(Phaser.Sprite.prototype, "deltaX", {
get: function() {
- return this.world.x - this._cache.prevX;
+
+ return this.world.x - this._cache[0];
+
}
});
/**
-* Returns the delta x value. The difference between Sprite.y now and in the previous step.
+* Returns the delta y value. The difference between world.y now and in the previous step.
+*
* @name Phaser.Sprite#deltaY
* @property {number} deltaY - The delta value. Positive if the motion was downwards, negative if upwards.
* @readonly
*/
-Object.defineProperty(Phaser.Sprite.prototype, 'deltaY', {
+Object.defineProperty(Phaser.Sprite.prototype, "deltaY", {
get: function() {
- return this.world.y - this._cache.prevY;
+
+ return this.world.y - this._cache[1];
+
}
});
/**
-* Indicates the rotation of the Sprite, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
-* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90.
-* If you wish to work in radians instead of degrees use the property Sprite.rotation instead.
-* @name Phaser.Sprite#angle
-* @property {number} angle - Gets or sets the Sprites angle of rotation in degrees.
+* Returns the delta z value. The difference between rotation now and in the previous step.
+*
+* @name Phaser.Sprite#deltaZ
+* @property {number} deltaZ - The delta value.
+* @readonly
*/
-Object.defineProperty(Phaser.Sprite.prototype, 'angle', {
+Object.defineProperty(Phaser.Sprite.prototype, "deltaZ", {
get: function() {
- return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation));
- },
+
+ return this.rotation - this._cache[2];
+
+ }
+
+});
+
+/**
+* Checks if the Sprite bounds are within the game world, otherwise false if fully outside of it.
+*
+* @name Phaser.Sprite#inWorld
+* @property {boolean} inWorld - True if the Sprite bounds is within the game world, even if only partially. Otherwise false if fully outside of it.
+* @readonly
+*/
+Object.defineProperty(Phaser.Sprite.prototype, "inWorld", {
+
+ get: function() {
+
+ return this.game.world.bounds.intersects(this.getBounds());
+
+ }
+
+});
+
+/**
+* Checks if the Sprite bounds are within the game camera, otherwise false if fully outside of it.
+*
+* @name Phaser.Sprite#inCamera
+* @property {boolean} inCamera - True if the Sprite bounds is within the game camera, even if only partially. Otherwise false if fully outside of it.
+* @readonly
+*/
+Object.defineProperty(Phaser.Sprite.prototype, "inCamera", {
+
+ get: function() {
+
+ return this.game.world.camera.screenView.intersects(this.getBounds());
- set: function(value) {
- this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
}
});
@@ -21751,7 +34196,7 @@ Object.defineProperty(Phaser.Sprite.prototype, 'angle', {
* @property {number} frame - Gets or sets the current frame index and updates the Texture Cache for display.
*/
Object.defineProperty(Phaser.Sprite.prototype, "frame", {
-
+
get: function () {
return this.animations.frame;
},
@@ -21767,7 +34212,7 @@ Object.defineProperty(Phaser.Sprite.prototype, "frame", {
* @property {string} frameName - Gets or sets the current frame name and updates the Texture Cache for display.
*/
Object.defineProperty(Phaser.Sprite.prototype, "frameName", {
-
+
get: function () {
return this.animations.frameName;
},
@@ -21779,85 +34224,15 @@ Object.defineProperty(Phaser.Sprite.prototype, "frameName", {
});
/**
-* @name Phaser.Sprite#inCamera
-* @property {boolean} inCamera - Is this sprite visible to the camera or not?
+* @name Phaser.Sprite#renderOrderID
+* @property {number} renderOrderID - The render order ID, reset every frame.
* @readonly
*/
-Object.defineProperty(Phaser.Sprite.prototype, "inCamera", {
-
- get: function () {
- return this._cache.cameraVisible;
- }
-
-});
-
-/**
-* @name Phaser.Sprite#worldCenterX
-* @property {number} worldCenterX - The center of the Sprite in world coordinates.
-* @readonly
-*/
-Object.defineProperty(Phaser.Sprite.prototype, "worldCenterX", {
-
- get: function () {
- return this.game.camera.x + this.center.x;
- }
-
-});
-
-/**
-* @name Phaser.Sprite#worldCenterY
-* @property {number} worldCenterY - The center of the Sprite in world coordinates.
-* @readonly
-*/
-Object.defineProperty(Phaser.Sprite.prototype, "worldCenterY", {
-
- get: function () {
- return this.game.camera.y + this.center.y;
- }
-
-});
-
-/**
-* The width of the sprite in pixels, setting this will actually modify the scale to acheive the value desired.
-* If you wish to crop the Sprite instead see the Sprite.crop value.
-*
-* @name Phaser.Sprite#width
-* @property {number} width - The width of the Sprite in pixels.
-*/
-Object.defineProperty(Phaser.Sprite.prototype, 'width', {
+Object.defineProperty(Phaser.Sprite.prototype, "renderOrderID", {
get: function() {
- return this.scale.x * this.currentFrame.width;
- },
- set: function(value) {
-
- this.scale.x = value / this.currentFrame.width;
- this._cache.scaleX = value / this.currentFrame.width;
- this._width = value;
-
- }
-
-});
-
-/**
-* The height of the sprite in pixels, setting this will actually modify the scale to acheive the value desired.
-* If you wish to crop the Sprite instead see the Sprite.crop value.
-*
-* @name Phaser.Sprite#height
-* @property {number} height - The height of the Sprite in pixels.
-*/
-Object.defineProperty(Phaser.Sprite.prototype, 'height', {
-
- get: function() {
- return this.scale.y * this.currentFrame.height;
- },
-
- set: function(value) {
-
- this.scale.y = value / this.currentFrame.height;
- this._cache.scaleY = value / this.currentFrame.height;
- this._height = value;
+ return this._cache[3];
}
@@ -21865,16 +34240,16 @@ Object.defineProperty(Phaser.Sprite.prototype, 'height', {
/**
* By default a Sprite won't process any input events at all. By setting inputEnabled to true the Phaser.InputHandler is
-* activated for this Sprite instance and it will then start to process click/touch events and more.
+* activated for this object and it will then start to process click/touch events and more.
*
* @name Phaser.Sprite#inputEnabled
-* @property {boolean} inputEnabled - Set to true to allow this Sprite to receive input events, otherwise false.
+* @property {boolean} inputEnabled - Set to true to allow this object to receive input events.
*/
Object.defineProperty(Phaser.Sprite.prototype, "inputEnabled", {
get: function () {
- return (this.input.enabled);
+ return (this.input && this.input.enabled);
},
@@ -21882,19 +34257,138 @@ Object.defineProperty(Phaser.Sprite.prototype, "inputEnabled", {
if (value)
{
- if (this.input.enabled === false)
+ if (this.input === null)
{
+ this.input = new Phaser.InputHandler(this);
this.input.start();
}
}
else
{
- if (this.input.enabled)
+ if (this.input && this.input.enabled)
{
this.input.stop();
}
}
+ }
+});
+
+/**
+* By default Sprites won't add themselves to the physics world. By setting physicsEnabled to true a Rectangle physics body is
+* attached to this Sprite matching its placement and dimensions, and will then start to process physics world updates.
+* You can access all physics related properties via Sprite.body.
+*
+* Important: Enabling a Sprite for physics will automatically set `Sprite.anchor` to 0.5 s0 the physics body is centered on the Sprite.
+* If you need a different result then adjust or re-create the Body shape offsets manually, and/or reset the anchor after enabling physics.
+*
+* @name Phaser.Sprite#physicsEnabled
+* @property {boolean} physicsEnabled - Set to true to add this Sprite to the physics world. Set to false to destroy the body and remove it from the physics world.
+*/
+Object.defineProperty(Phaser.Sprite.prototype, "physicsEnabled", {
+
+ get: function () {
+
+ return (this.body !== null);
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ if (this.body === null)
+ {
+ this.body = new Phaser.Physics.Body(this.game, this, this.x, this.y, 1);
+ this.anchor.set(0.5);
+ }
+ }
+ else
+ {
+ if (this.body)
+ {
+ this.body.destroy();
+ }
+ }
+ }
+
+});
+
+/**
+* Sprite.exists controls if the core game loop and physics update this Sprite or not.
+* When you set Sprite.exists to false it will remove its Body from the physics world (if it has one) and also set Sprite.visible to false.
+* Setting Sprite.exists to true will re-add the Body to the physics world (if it has a body) and set Sprite.visible to true.
+*
+* @name Phaser.Sprite#exists
+* @property {boolean} exists - If the Sprite is processed by the core game update and physics.
+*/
+Object.defineProperty(Phaser.Sprite.prototype, "exists", {
+
+ get: function () {
+
+ return !!this._cache[6];
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ // exists = true
+ this._cache[6] = 1;
+
+ if (this.body)
+ {
+ this.body.addToWorld();
+ }
+
+ this.visible = true;
+ }
+ else
+ {
+ // exists = false
+ this._cache[6] = 0;
+
+ if (this.body)
+ {
+ this.body.removeFromWorld();
+ }
+
+ this.visible = false;
+
+ }
+ }
+
+});
+
+
+/**
+* An Sprite that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Sprite.cameraOffset.
+* Note that the cameraOffset values are in addition to any parent in the display list.
+* So if this Sprite was in a Group that has x: 200, then this will be added to the cameraOffset.x
+*
+* @name Phaser.Sprite#fixedToCamera
+* @property {boolean} fixedToCamera - Set to true to fix this Sprite to the Camera at its current world coordinates.
+*/
+Object.defineProperty(Phaser.Sprite.prototype, "fixedToCamera", {
+
+ get: function () {
+
+ return !!this._cache[7];
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ this._cache[7] = 1;
+ this.cameraOffset.set(this.x, this.y);
+ }
+ else
+ {
+ this._cache[7] = 0;
+ }
}
});
@@ -21906,33 +34400,735 @@ Object.defineProperty(Phaser.Sprite.prototype, "inputEnabled", {
*/
/**
-* A TileSprite is a Sprite whos texture is set to repeat and can be scrolled. As it scrolls the texture repeats (wraps) on the edges.
-* @class Phaser.Tilemap
-* @extends Phaser.Sprite
+* @class Phaser.Image
+*
+* @classdesc Create a new `Image` object. An Image is a light-weight object you can use to display anything that doesn't need physics or animation.
+* It can still rotate, scale, crop and receive input events. This makes it perfect for logos, backgrounds, simple buttons and other non-Sprite graphics.
+*
* @constructor
-* @param {Phaser.Game} game - Current game instance.
-* @param {number} x - X position of the new tileSprite.
-* @param {number} y - Y position of the new tileSprite.
-* @param {number} width - the width of the tilesprite.
-* @param {number} height - the height of the tilesprite.
-* @param {string|Phaser.RenderTexture|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
+* @param {Phaser.Game} game - A reference to the currently running game.
+* @param {number} x - The x coordinate of the Imaget. The coordinate is relative to any parent container this Image may be in.
+* @param {number} y - The y coordinate of the Image. The coordinate is relative to any parent container this Image may be in.
+* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - The texture used by the Image during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture.
+* @param {string|number} frame - If this Image is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
*/
-Phaser.TileSprite = function (game, x, y, width, height, key) {
+Phaser.Image = function (game, x, y, key, frame) {
+
+ x = x || 0;
+ y = y || 0;
+ key = key || null;
+ frame = frame || null;
+
+ /**
+ * @property {Phaser.Game} game - A reference to the currently running Game.
+ */
+ this.game = game;
+
+ /**
+ * @property {boolean} exists - If exists = false then the Image isn't updated by the core game loop.
+ * @default
+ */
+ this.exists = true;
+
+ /**
+ * @property {string} name - The user defined name given to this Image.
+ * @default
+ */
+ this.name = '';
+
+ /**
+ * @property {number} type - The const type of this object.
+ * @readonly
+ */
+ this.type = Phaser.IMAGE;
+
+ /**
+ * @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Image or its components.
+ */
+ this.events = new Phaser.Events(this);
+
+ /**
+ * @property {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Image during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture.
+ */
+ this.key = key;
+
+ /**
+ * @property {number} _frame - Internal cache var.
+ * @private
+ */
+ this._frame = 0;
+
+ /**
+ * @property {string} _frameName - Internal cache var.
+ * @private
+ */
+ this._frameName = '';
+
+ PIXI.Sprite.call(this, PIXI.TextureCache['__default']);
+
+ this.loadTexture(key, frame);
+
+ this.position.set(x, y);
+
+ /**
+ * @property {Phaser.Point} world - The world coordinates of this Image. This differs from the x/y coordinates which are relative to the Images container.
+ */
+ this.world = new Phaser.Point(x, y);
+
+ /**
+ * Should this Image be automatically culled if out of range of the camera?
+ * A culled sprite has its renderable property set to 'false'.
+ * Be advised this is quite an expensive operation, as it has to calculate the bounds of the object every frame, so only enable it if you really need it.
+ *
+ * @property {boolean} autoCull - A flag indicating if the Image should be automatically camera culled or not.
+ * @default
+ */
+ this.autoCull = false;
+
+ /**
+ * @property {Phaser.InputHandler|null} input - The Input Handler for this object. Needs to be enabled with image.inputEnabled = true before you can use it.
+ */
+ this.input = null;
+
+ /**
+ * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view.
+ */
+ this.cameraOffset = new Phaser.Point();
+
+ /**
+ * A small internal cache:
+ * 0 = previous position.x
+ * 1 = previous position.y
+ * 2 = previous rotation
+ * 3 = renderID
+ * 4 = fresh? (0 = no, 1 = yes)
+ * 5 = outOfBoundsFired (0 = no, 1 = yes)
+ * 6 = exists (0 = no, 1 = yes)
+ * 7 = fixed to camera (0 = no, 1 = yes)
+ * @property {Int16Array} _cache
+ * @private
+ */
+ this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]);
+
+};
+
+Phaser.Image.prototype = Object.create(PIXI.Sprite.prototype);
+Phaser.Image.prototype.constructor = Phaser.Image;
+
+/**
+* Automatically called by World.preUpdate.
+*
+* @method Phaser.Image#preUpdate
+* @memberof Phaser.Image
+*/
+Phaser.Image.prototype.preUpdate = function() {
+
+ this._cache[0] = this.world.x;
+ this._cache[1] = this.world.y;
+ this._cache[2] = this.rotation;
+
+ if (!this.exists || !this.parent.exists)
+ {
+ this.renderOrderID = -1;
+ return false;
+ }
+
+ if (this.autoCull)
+ {
+ // Won't get rendered but will still get its transform updated
+ this.renderable = this.game.world.camera.screenView.intersects(this.getBounds());
+ }
+
+ this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]);
+
+ if (this.visible)
+ {
+ this._cache[3] = this.game.world.currentRenderOrderID++;
+ }
+
+ return true;
+
+}
+
+/**
+* Override and use this function in your own custom objects to handle any update requirements you may have.
+*
+* @method Phaser.Image#update
+* @memberof Phaser.Image
+*/
+Phaser.Image.prototype.update = function() {
+
+}
+
+/**
+* Internal function called by the World postUpdate cycle.
+*
+* @method Phaser.Image#postUpdate
+* @memberof Phaser.Image
+*/
+Phaser.Image.prototype.postUpdate = function() {
+
+ if (this.key instanceof Phaser.BitmapData && this.key._dirty)
+ {
+ this.key.render();
+ }
+
+ // Fixed to Camera?
+ if (this._cache[7] === 1)
+ {
+ this.position.x = this.game.camera.view.x + this.cameraOffset.x;
+ this.position.y = this.game.camera.view.y + this.cameraOffset.y;
+ }
+
+}
+
+/**
+* Changes the Texture the Image is using entirely. The old texture is removed and the new one is referenced or fetched from the Cache.
+* This causes a WebGL texture update, so use sparingly or in low-intensity portions of your game.
+*
+* @method Phaser.Image#loadTexture
+* @memberof Phaser.Image
+* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Image during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture.
+* @param {string|number} frame - If this Image is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
+*/
+Phaser.Image.prototype.loadTexture = function (key, frame) {
+
+ frame = frame || 0;
+
+ if (key instanceof Phaser.RenderTexture)
+ {
+ this.key = key.key;
+ this.setTexture(key);
+ return;
+ }
+ else if (key instanceof Phaser.BitmapData)
+ {
+ this.key = key.key;
+ this.setTexture(key.texture);
+ return;
+ }
+ else if (key instanceof PIXI.Texture)
+ {
+ this.key = key;
+ this.setTexture(key);
+ return;
+ }
+ else
+ {
+ if (key === null || typeof key === 'undefined')
+ {
+ this.key = '__default';
+ this.setTexture(PIXI.TextureCache[this.key]);
+ return;
+ }
+ else if (typeof key === 'string' && !this.game.cache.checkImageKey(key))
+ {
+ this.key = '__missing';
+ this.setTexture(PIXI.TextureCache[this.key]);
+ return;
+ }
+
+ if (this.game.cache.isSpriteSheet(key))
+ {
+ this.key = key;
+
+ var frameData = this.game.cache.getFrameData(key);
+
+ if (typeof frame === 'string')
+ {
+ this._frame = 0;
+ this._frameName = frame;
+ this.setTexture(PIXI.TextureCache[frameData.getFrameByName(frame).uuid]);
+ return;
+ }
+ else
+ {
+ this._frame = frame;
+ this._frameName = '';
+ this.setTexture(PIXI.TextureCache[frameData.getFrame(frame).uuid]);
+ return;
+ }
+ }
+ else
+ {
+ this.key = key;
+ this.setTexture(PIXI.TextureCache[key]);
+ return;
+ }
+ }
+
+}
+
+/**
+* Crop allows you to crop the texture used to display this Image.
+* Cropping takes place from the top-left of the Image and can be modified in real-time by providing an updated rectangle object.
+*
+* @method Phaser.Image#crop
+* @memberof Phaser.Image
+* @param {Phaser.Rectangle} rect - The Rectangle to crop the Image to. Pass null or no parameters to clear a previously set crop rectangle.
+*/
+Phaser.Image.prototype.crop = function(rect) {
+
+ if (typeof rect === 'undefined' || rect === null)
+ {
+ // Clear any crop that may be set
+ if (this.texture.hasOwnProperty('sourceWidth'))
+ {
+ this.texture.setFrame(new Phaser.Rectangle(0, 0, this.texture.sourceWidth, this.texture.sourceHeight));
+ }
+ }
+ else
+ {
+ // Do we need to clone the PIXI.Texture object?
+ if (this.texture instanceof PIXI.Texture)
+ {
+ // Yup, let's rock it ...
+ var local = {};
+
+ Phaser.Utils.extend(true, local, this.texture);
+
+ local.sourceWidth = local.width;
+ local.sourceHeight = local.height;
+ local.frame = rect;
+ local.width = rect.width;
+ local.height = rect.height;
+
+ this.texture = local;
+
+ this.texture.updateFrame = true;
+ PIXI.Texture.frameUpdates.push(this.texture);
+ }
+ else
+ {
+ this.texture.setFrame(rect);
+ }
+ }
+
+}
+
+/**
+* Brings a 'dead' Image back to life, optionally giving it the health value specified.
+* A resurrected Image has its alive, exists and visible properties all set to true.
+* It will dispatch the onRevived event, you can listen to Image.events.onRevived for the signal.
+*
+* @method Phaser.Image#revive
+* @memberof Phaser.Image
+* @return {Phaser.Image} This instance.
+*/
+Phaser.Image.prototype.revive = function() {
+
+ this.alive = true;
+ this.exists = true;
+ this.visible = true;
+
+ if (this.events)
+ {
+ this.events.onRevived.dispatch(this);
+ }
+
+ return this;
+
+}
+
+/**
+* Kills a Image. A killed Image has its alive, exists and visible properties all set to false.
+* It will dispatch the onKilled event, you can listen to Image.events.onKilled for the signal.
+* Note that killing a Image is a way for you to quickly recycle it in a Image pool, it doesn't free it up from memory.
+* If you don't need this Image any more you should call Image.destroy instead.
+*
+* @method Phaser.Image#kill
+* @memberof Phaser.Image
+* @return {Phaser.Image} This instance.
+*/
+Phaser.Image.prototype.kill = function() {
+
+ this.alive = false;
+ this.exists = false;
+ this.visible = false;
+
+ if (this.events)
+ {
+ this.events.onKilled.dispatch(this);
+ }
+
+ return this;
+
+}
+
+/**
+* Destroys the Image. This removes it from its parent group, destroys the input, event and animation handlers if present
+* and nulls its reference to game, freeing it up for garbage collection.
+*
+* @method Phaser.Image#destroy
+* @memberof Phaser.Image
+*/
+Phaser.Image.prototype.destroy = function() {
+
+ if (this.filters)
+ {
+ this.filters = null;
+ }
+
+ if (this.parent)
+ {
+ this.parent.remove(this);
+ }
+
+ if (this.events)
+ {
+ this.events.destroy();
+ }
+
+ if (this.input)
+ {
+ this.input.destroy();
+ }
+
+ this.alive = false;
+ this.exists = false;
+ this.visible = false;
+
+ this.game = null;
+
+}
+
+/**
+* Resets the Image. This places the Image at the given x/y world coordinates and then sets alive, exists, visible and renderable all to true.
+*
+* @method Phaser.Image#reset
+* @memberof Phaser.Image
+* @param {number} x - The x coordinate (in world space) to position the Image at.
+* @param {number} y - The y coordinate (in world space) to position the Image at.
+* @return {Phaser.Image} This instance.
+*/
+Phaser.Image.prototype.reset = function(x, y) {
+
+ this.world.setTo(x, y);
+ this.position.x = x;
+ this.position.y = y;
+ this.alive = true;
+ this.exists = true;
+ this.visible = true;
+ this.renderable = true;
+
+ return this;
+
+}
+
+/**
+* Brings the Image to the top of the display list it is a child of. Images that are members of a Phaser.Group are only
+* bought to the top of that Group, not the entire display list.
+*
+* @method Phaser.Image#bringToTop
+* @memberof Phaser.Image
+* @return {Phaser.Image} This instance.
+*/
+Phaser.Image.prototype.bringToTop = function(child) {
+
+ if (typeof child === 'undefined')
+ {
+ if (this.parent)
+ {
+ this.parent.bringToTop(this);
+ }
+ }
+ else
+ {
+
+ }
+
+ return this;
+
+}
+
+/**
+* Indicates the rotation of the Image, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
+* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90.
+* If you wish to work in radians instead of degrees use the property Image.rotation instead. Working in radians is also a little faster as it doesn't have to convert the angle.
+*
+* @name Phaser.Image#angle
+* @property {number} angle - The angle of this Image in degrees.
+*/
+Object.defineProperty(Phaser.Image.prototype, "angle", {
+
+ get: function() {
+
+ return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation));
+
+ },
+
+ set: function(value) {
+
+ this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
+
+ }
+
+});
+
+/**
+* Returns the delta x value. The difference between world.x now and in the previous step.
+*
+* @name Phaser.Image#deltaX
+* @property {number} deltaX - The delta value. Positive if the motion was to the right, negative if to the left.
+* @readonly
+*/
+Object.defineProperty(Phaser.Image.prototype, "deltaX", {
+
+ get: function() {
+
+ return this.world.x - this._cache[0];
+
+ }
+
+});
+
+/**
+* Returns the delta y value. The difference between world.y now and in the previous step.
+*
+* @name Phaser.Image#deltaY
+* @property {number} deltaY - The delta value. Positive if the motion was downwards, negative if upwards.
+* @readonly
+*/
+Object.defineProperty(Phaser.Image.prototype, "deltaY", {
+
+ get: function() {
+
+ return this.world.y - this._cache[1];
+
+ }
+
+});
+
+/**
+* Returns the delta z value. The difference between rotation now and in the previous step.
+*
+* @name Phaser.Image#deltaZ
+* @property {number} deltaZ - The delta value.
+* @readonly
+*/
+Object.defineProperty(Phaser.Image.prototype, "deltaZ", {
+
+ get: function() {
+
+ return this.rotation - this._cache[2];
+
+ }
+
+});
+
+/**
+* Checks if the Image bounds are within the game world, otherwise false if fully outside of it.
+*
+* @name Phaser.Image#inWorld
+* @property {boolean} inWorld - True if the Image bounds is within the game world, even if only partially. Otherwise false if fully outside of it.
+* @readonly
+*/
+Object.defineProperty(Phaser.Image.prototype, "inWorld", {
+
+ get: function() {
+
+ return this.game.world.bounds.intersects(this.getBounds());
+
+ }
+
+});
+
+/**
+* Checks if the Image bounds are within the game camera, otherwise false if fully outside of it.
+*
+* @name Phaser.Image#inCamera
+* @property {boolean} inCamera - True if the Image bounds is within the game camera, even if only partially. Otherwise false if fully outside of it.
+* @readonly
+*/
+Object.defineProperty(Phaser.Image.prototype, "inCamera", {
+
+ get: function() {
+
+ return this.game.world.camera.screenView.intersects(this.getBounds());
+
+ }
+
+});
+
+/**
+* @name Phaser.Image#frame
+* @property {number} frame - Gets or sets the current frame index and updates the Texture for display.
+*/
+Object.defineProperty(Phaser.Image.prototype, "frame", {
+
+ get: function() {
+
+ return this._frame;
+
+ },
+
+ set: function(value) {
+
+ if (value !== this.frame && this.game.cache.isSpriteSheet(this.key))
+ {
+ var frameData = this.game.cache.getFrameData(this.key);
+
+ if (frameData && value < frameData.total && frameData.getFrame(value))
+ {
+ this.setTexture(PIXI.TextureCache[frameData.getFrame(value).uuid]);
+ this._frame = value;
+ }
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Image#frameName
+* @property {string} frameName - Gets or sets the current frame by name and updates the Texture for display.
+*/
+Object.defineProperty(Phaser.Image.prototype, "frameName", {
+
+ get: function() {
+
+ return this._frameName;
+
+ },
+
+ set: function(value) {
+
+ if (value !== this.frameName && this.game.cache.isSpriteSheet(this.key))
+ {
+ var frameData = this.game.cache.getFrameData(this.key);
+
+ if (frameData && frameData.getFrameByName(value))
+ {
+ this.setTexture(PIXI.TextureCache[frameData.getFrameByName(value).uuid]);
+ this._frameName = value;
+ }
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Image#renderOrderID
+* @property {number} renderOrderID - The render order ID, reset every frame.
+* @readonly
+*/
+Object.defineProperty(Phaser.Image.prototype, "renderOrderID", {
+
+ get: function() {
+
+ return this._cache[3];
+
+ }
+
+});
+
+/**
+* By default an Image won't process any input events at all. By setting inputEnabled to true the Phaser.InputHandler is
+* activated for this object and it will then start to process click/touch events and more.
+*
+* @name Phaser.Image#inputEnabled
+* @property {boolean} inputEnabled - Set to true to allow this object to receive input events.
+*/
+Object.defineProperty(Phaser.Image.prototype, "inputEnabled", {
+
+ get: function () {
+
+ return (this.input && this.input.enabled);
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ if (this.input === null)
+ {
+ this.input = new Phaser.InputHandler(this);
+ this.input.start();
+ }
+ }
+ else
+ {
+ if (this.input && this.input.enabled)
+ {
+ this.input.stop();
+ }
+ }
+ }
+
+});
+
+/**
+* An Image that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Image.cameraOffset.
+* Note that the cameraOffset values are in addition to any parent in the display list.
+* So if this Image was in a Group that has x: 200, then this will be added to the cameraOffset.x
+*
+* @name Phaser.Image#fixedToCamera
+* @property {boolean} fixedToCamera - Set to true to fix this Image to the Camera at its current world coordinates.
+*/
+Object.defineProperty(Phaser.Image.prototype, "fixedToCamera", {
+
+ get: function () {
+
+ return !!this._cache[7];
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ this._cache[7] = 1;
+ this.cameraOffset.set(this.x, this.y);
+ }
+ else
+ {
+ this._cache[7] = 0;
+ }
+ }
+
+});
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* A TileSprite is a Sprite that has a repeating texture. The texture can be scrolled and scaled and will automatically wrap on the edges as it does so.
+* Please note that TileSprites have no input handler or physics bodies.
+*
+* @class Phaser.TileSprite
+* @constructor
+* @param {Phaser.Game} game - A reference to the currently running game.
+* @param {number} x - The x coordinate (in world space) to position the TileSprite at.
+* @param {number} y - The y coordinate (in world space) to position the TileSprite at.
+* @param {number} width - The width of the TileSprite.
+* @param {number} height - The height of the TileSprite.
+* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the TileSprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture or PIXI.Texture.
+* @param {string|number} frame - If this TileSprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
+*/
+Phaser.TileSprite = function (game, x, y, width, height, key, frame) {
x = x || 0;
y = y || 0;
width = width || 256;
height = height || 256;
key = key || null;
-
- Phaser.Sprite.call(this, game, x, y, key);
+ frame = frame || null;
/**
- * @property {PIXI.Texture} texture - The texture that the sprite renders with.
+ * @property {Phaser.Game} game - A reference to the currently running Game.
*/
- this.texture = PIXI.TextureCache[key];
+ this.game = game;
- PIXI.TilingSprite.call(this, this.texture, width, height);
+ /**
+ * @property {string} name - The user defined name given to this Sprite.
+ * @default
+ */
+ this.name = '';
/**
* @property {number} type - The const type of this object.
@@ -21941,38 +35137,294 @@ Phaser.TileSprite = function (game, x, y, width, height, key) {
this.type = Phaser.TILESPRITE;
/**
- * @property {Phaser.Point} tileScale - The scaling of the image that is being tiled.
+ * @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Sprite or its components.
*/
- this.tileScale = new Phaser.Point(1, 1);
+ this.events = new Phaser.Events(this);
/**
- * @property {Phaser.Point} tilePosition - The offset position of the image that is being tiled.
+ * @property {Phaser.AnimationManager} animations - This manages animations of the sprite. You can modify animations through it (see Phaser.AnimationManager)
*/
- this.tilePosition = new Phaser.Point(0, 0);
+ this.animations = new Phaser.AnimationManager(this);
- this.body.width = width;
- this.body.height = height;
+ /**
+ * @property {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture.
+ */
+ this.key = key;
+
+ /**
+ * @property {number} _frame - Internal cache var.
+ * @private
+ */
+ this._frame = 0;
+
+ /**
+ * @property {string} _frameName - Internal cache var.
+ * @private
+ */
+ this._frameName = '';
+
+ /**
+ * @property {Phaser.Point} _scroll - Internal cache var.
+ * @private
+ */
+ this._scroll = new Phaser.Point();
+
+ PIXI.TilingSprite.call(this, PIXI.TextureCache['__default'], width, height);
+
+ this.loadTexture(key, frame);
+
+ this.position.set(x, y);
+
+ /**
+ * @property {Phaser.Point} world - The world coordinates of this Sprite. This differs from the x/y coordinates which are relative to the Sprites container.
+ */
+ this.world = new Phaser.Point(x, y);
+
+ /**
+ * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view.
+ */
+ this.cameraOffset = new Phaser.Point();
+
+ /**
+ * A small internal cache:
+ * 0 = previous position.x
+ * 1 = previous position.y
+ * 2 = previous rotation
+ * 3 = renderID
+ * 4 = fresh? (0 = no, 1 = yes)
+ * 5 = outOfBoundsFired (0 = no, 1 = yes)
+ * 6 = exists (0 = no, 1 = yes)
+ * 7 = fixed to camera (0 = no, 1 = yes)
+ * @property {Int16Array} _cache
+ * @private
+ */
+ this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]);
};
-Phaser.TileSprite.prototype = Phaser.Utils.extend(true, PIXI.TilingSprite.prototype, Phaser.Sprite.prototype);
+Phaser.TileSprite.prototype = Object.create(PIXI.TilingSprite.prototype);
Phaser.TileSprite.prototype.constructor = Phaser.TileSprite;
+/**
+* Automatically called by World.preUpdate.
+*
+* @method Phaser.TileSprite#preUpdate
+* @memberof Phaser.TileSprite
+*/
+Phaser.TileSprite.prototype.preUpdate = function() {
+
+ this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]);
+
+ this.animations.update();
+
+ if (this._scroll.x !== 0)
+ {
+ this.tilePosition.x += this._scroll.x * this.game.time.physicsElapsed;
+ }
+
+ if (this._scroll.y !== 0)
+ {
+ this.tilePosition.y += this._scroll.y * this.game.time.physicsElapsed;
+ }
+
+ return true;
+
+}
+
+/**
+* Override and use this function in your own custom objects to handle any update requirements you may have.
+*
+* @method Phaser.TileSprite#update
+* @memberof Phaser.TileSprite
+*/
+Phaser.TileSprite.prototype.update = function() {
+
+}
+
+/**
+* Internal function called by the World postUpdate cycle.
+*
+* @method Phaser.TileSprite#postUpdate
+* @memberof Phaser.TileSprite
+*/
+Phaser.TileSprite.prototype.postUpdate = function() {
+
+ // Fixed to Camera?
+ if (this._cache[7] === 1)
+ {
+ this.position.x = this.game.camera.view.x + this.cameraOffset.x;
+ this.position.y = this.game.camera.view.y + this.cameraOffset.y;
+ }
+
+}
+
+/**
+* Sets this TileSprite to automatically scroll in the given direction until stopped via TileSprite.stopScroll().
+* The scroll speed is specified in pixels per second.
+* A negative x value will scroll to the left. A positive x value will scroll to the right.
+* A negative y value will scroll up. A positive y value will scroll down.
+*
+* @method Phaser.TileSprite#autoScroll
+* @memberof Phaser.TileSprite
+*/
+Phaser.TileSprite.prototype.autoScroll = function(x, y) {
+
+ this._scroll.set(x, y);
+
+}
+
+/**
+* Stops an automatically scrolling TileSprite.
+*
+* @method Phaser.TileSprite#stopScroll
+* @memberof Phaser.TileSprite
+*/
+Phaser.TileSprite.prototype.stopScroll = function() {
+
+ this._scroll.set(0, 0);
+
+}
+
+/**
+* Changes the Texture the TileSprite is using entirely. The old texture is removed and the new one is referenced or fetched from the Cache.
+* This causes a WebGL texture update, so use sparingly or in low-intensity portions of your game.
+*
+* @method Phaser.TileSprite#loadTexture
+* @memberof Phaser.TileSprite
+* @param {string|Phaser.RenderTexture|Phaser.BitmapData|PIXI.Texture} key - This is the image or texture used by the Sprite during rendering. It can be a string which is a reference to the Cache entry, or an instance of a RenderTexture, BitmapData or PIXI.Texture.
+* @param {string|number} frame - If this Sprite is using part of a sprite sheet or texture atlas you can specify the exact frame to use by giving a string or numeric index.
+*/
+Phaser.TileSprite.prototype.loadTexture = function (key, frame) {
+
+ frame = frame || 0;
+
+ if (key instanceof Phaser.RenderTexture)
+ {
+ this.key = key.key;
+ this.setTexture(key);
+ return;
+ }
+ else if (key instanceof Phaser.BitmapData)
+ {
+ this.key = key.key;
+ this.setTexture(key.texture);
+ return;
+ }
+ else if (key instanceof PIXI.Texture)
+ {
+ this.key = key;
+ this.setTexture(key);
+ return;
+ }
+ else
+ {
+ if (key === null || typeof key === 'undefined')
+ {
+ this.key = '__default';
+ this.setTexture(PIXI.TextureCache[this.key]);
+ return;
+ }
+ else if (typeof key === 'string' && !this.game.cache.checkImageKey(key))
+ {
+ this.key = '__missing';
+ this.setTexture(PIXI.TextureCache[this.key]);
+ return;
+ }
+
+ if (this.game.cache.isSpriteSheet(key))
+ {
+ this.key = key;
+
+ // var frameData = this.game.cache.getFrameData(key);
+ this.animations.loadFrameData(this.game.cache.getFrameData(key));
+
+ if (typeof frame === 'string')
+ {
+ this.frameName = frame;
+ }
+ else
+ {
+ this.frame = frame;
+ }
+ }
+ else
+ {
+ this.key = key;
+ this.setTexture(PIXI.TextureCache[key]);
+ return;
+ }
+ }
+
+}
+
+/**
+* Destroys the TileSprite. This removes it from its parent group, destroys the event and animation handlers if present
+* and nulls its reference to game, freeing it up for garbage collection.
+*
+* @method Phaser.TileSprite#destroy
+* @memberof Phaser.TileSprite
+*/
+Phaser.TileSprite.prototype.destroy = function() {
+
+ if (this.filters)
+ {
+ this.filters = null;
+ }
+
+ if (this.parent)
+ {
+ this.parent.remove(this);
+ }
+
+ this.animations.destroy();
+
+ this.events.destroy();
+
+ this.exists = false;
+ this.visible = false;
+
+ this.game = null;
+
+}
+
+/**
+* Play an animation based on the given key. The animation should previously have been added via sprite.animations.add()
+* If the requested animation is already playing this request will be ignored. If you need to reset an already running animation do so directly on the Animation object itself.
+*
+* @method Phaser.TileSprite#play
+* @memberof Phaser.TileSprite
+* @param {string} name - The name of the animation to be played, e.g. "fire", "walk", "jump".
+* @param {number} [frameRate=null] - The framerate to play the animation at. The speed is given in frames per second. If not provided the previously set frameRate of the Animation is used.
+* @param {boolean} [loop=false] - Should the animation be looped after playback. If not provided the previously set loop value of the Animation is used.
+* @param {boolean} [killOnComplete=false] - If set to true when the animation completes (only happens if loop=false) the parent Sprite will be killed.
+* @return {Phaser.Animation} A reference to playing Animation instance.
+*/
+Phaser.TileSprite.prototype.play = function (name, frameRate, loop, killOnComplete) {
+
+ return this.animations.play(name, frameRate, loop, killOnComplete);
+
+}
+
/**
* Indicates the rotation of the Sprite, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90.
-* If you wish to work in radians instead of degrees use the property Sprite.rotation instead.
+* If you wish to work in radians instead of degrees use the property Sprite.rotation instead. Working in radians is also a little faster as it doesn't have to convert the angle.
+*
* @name Phaser.TileSprite#angle
-* @property {number} angle - Gets or sets the Sprites angle of rotation in degrees.
+* @property {number} angle - The angle of this Sprite in degrees.
*/
-Object.defineProperty(Phaser.TileSprite.prototype, 'angle', {
+Object.defineProperty(Phaser.TileSprite.prototype, "angle", {
get: function() {
+
return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation));
+
},
set: function(value) {
+
this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
+
}
});
@@ -21982,13 +35434,18 @@ Object.defineProperty(Phaser.TileSprite.prototype, 'angle', {
* @property {number} frame - Gets or sets the current frame index and updates the Texture Cache for display.
*/
Object.defineProperty(Phaser.TileSprite.prototype, "frame", {
-
+
get: function () {
return this.animations.frame;
},
set: function (value) {
- this.animations.frame = value;
+
+ if (value !== this.animations.frame)
+ {
+ this.animations.frame = value;
+ }
+
}
});
@@ -21998,42 +35455,35 @@ Object.defineProperty(Phaser.TileSprite.prototype, "frame", {
* @property {string} frameName - Gets or sets the current frame name and updates the Texture Cache for display.
*/
Object.defineProperty(Phaser.TileSprite.prototype, "frameName", {
-
+
get: function () {
return this.animations.frameName;
},
set: function (value) {
- this.animations.frameName = value;
+
+ if (value !== this.animations.frameName)
+ {
+ this.animations.frameName = value;
+ }
+
}
});
/**
-* @name Phaser.TileSprite#inCamera
-* @property {boolean} inCamera - Is this sprite visible to the camera or not?
-* @readonly
-*/
-Object.defineProperty(Phaser.TileSprite.prototype, "inCamera", {
-
- get: function () {
- return this._cache.cameraVisible;
- }
-
-});
-
-/**
-* By default a Sprite won't process any input events at all. By setting inputEnabled to true the Phaser.InputHandler is
-* activated for this Sprite instance and it will then start to process click/touch events and more.
+* An TileSprite that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in TileSprite.cameraOffset.
+* Note that the cameraOffset values are in addition to any parent in the display list.
+* So if this TileSprite was in a Group that has x: 200, then this will be added to the cameraOffset.x
*
-* @name Phaser.TileSprite#inputEnabled
-* @property {boolean} inputEnabled - Set to true to allow this Sprite to receive input events, otherwise false.
+* @name Phaser.TileSprite#fixedToCamera
+* @property {boolean} fixedToCamera - Set to true to fix this TileSprite to the Camera at its current world coordinates.
*/
-Object.defineProperty(Phaser.TileSprite.prototype, "inputEnabled", {
+Object.defineProperty(Phaser.TileSprite.prototype, "fixedToCamera", {
get: function () {
- return (this.input.enabled);
+ return !!this._cache[7];
},
@@ -22041,19 +35491,13 @@ Object.defineProperty(Phaser.TileSprite.prototype, "inputEnabled", {
if (value)
{
- if (this.input.enabled === false)
- {
- this.input.start();
- }
+ this._cache[7] = 1;
+ this.cameraOffset.set(this.x, this.y);
}
else
{
- if (this.input.enabled)
- {
- this.input.stop();
- }
+ this._cache[7] = 0;
}
-
}
});
@@ -22092,17 +35536,6 @@ Phaser.Text = function (game, x, y, text, style) {
*/
this.exists = true;
- /**
- * @property {boolean} alive - This is a handy little var your game can use to determine if an object is alive or not, it doesn't effect rendering.
- * @default
- */
- this.alive = true;
-
- /**
- * @property {Phaser.Group} group - The parent Group of this Text object.
- */
- this.group = null;
-
/**
* @property {string} name - The user defined name given to this object.
* @default
@@ -22116,86 +35549,73 @@ Phaser.Text = function (game, x, y, text, style) {
this.type = Phaser.TEXT;
/**
- * @property {string} _text - Internal value.
+ * @property {Phaser.Point} world - The world coordinates of this Sprite. This differs from the x/y coordinates which are relative to the Sprites container.
+ */
+ this.world = new Phaser.Point(x, y);
+
+ /**
+ * @property {string} _text - Internal cache var.
* @private
*/
this._text = text;
/**
- * @property {string} _style - Internal value.
+ * @property {string} _font - Internal cache var.
* @private
*/
- this._style = style;
+ this._font = '';
+
+ /**
+ * @property {number} _fontSize - Internal cache var.
+ * @private
+ */
+ this._fontSize = 32;
+
+ /**
+ * @property {string} _fontWeight - Internal cache var.
+ * @private
+ */
+ this._fontWeight = 'normal';
+
+ /**
+ * @property {number} lineSpacing - Additional spacing (in pixels) between each line of text if multi-line.
+ * @private
+ */
+ this._lineSpacing = 0;
+
+ /**
+ * @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Sprite or its components.
+ */
+ this.events = new Phaser.Events(this);
+
+ /**
+ * @property {Phaser.InputHandler|null} input - The Input Handler for this object. Needs to be enabled with image.inputEnabled = true before you can use it.
+ */
+ this.input = null;
+
+ /**
+ * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view.
+ */
+ this.cameraOffset = new Phaser.Point();
PIXI.Text.call(this, text, style);
- /**
- * @property {Phaser.Point} position - The position of this Text object in world space.
- */
- this.position.x = this.x = x;
- this.position.y = this.y = y;
+ this.position.set(x, y);
/**
- * The anchor sets the origin point of the texture.
- * The default is 0,0 this means the textures origin is the top left
- * Setting than anchor to 0.5,0.5 means the textures origin is centered
- * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right
- *
- * @property {Phaser.Point} anchor - The anchor around which rotation and scaling takes place.
- */
- this.anchor = new Phaser.Point();
-
- /**
- * @property {Phaser.Point} scale - The scale of the object when rendered. By default it's set to 1 (no scale). You can modify it via scale.x or scale.y or scale.setTo(x, y). A value of 1 means no change to the scale, 0.5 means "half the size", 2 means "twice the size", etc.
- */
- this.scale = new Phaser.Point(1, 1);
-
- /**
- * An object that is fixed to the camera ignores the position of any ancestors in the display list and uses its x/y coordinates as offsets from the top left of the camera.
- * @property {boolean} fixedToCamera - Fixes this object to the Camera.
- * @default
- */
- this.fixedToCamera = false;
-
- /**
- * @property {Phaser.Point} cameraOffset - If this object is fixed to the camera then use this Point to specify how far away from the Camera x/y it's rendered.
- */
- this.cameraOffset = new Phaser.Point(x, y);
-
- /**
- * @property {object} _cache - A mini cache for storing all of the calculated values.
+ * A small internal cache:
+ * 0 = previous position.x
+ * 1 = previous position.y
+ * 2 = previous rotation
+ * 3 = renderID
+ * 4 = fresh? (0 = no, 1 = yes)
+ * 5 = outOfBoundsFired (0 = no, 1 = yes)
+ * 6 = exists (0 = no, 1 = yes)
+ * 7 = fixed to camera (0 = no, 1 = yes)
+ * @property {Int16Array} _cache
* @private
*/
- this._cache = {
-
- dirty: false,
-
- // Transform cache
- a00: 1,
- a01: 0,
- a02: x,
- a10: 0,
- a11: 1,
- a12: y,
- id: 1,
-
- // The previous calculated position
- x: -1,
- y: -1,
-
- // The actual scale values based on the worldTransform
- scaleX: 1,
- scaleY: 1
-
- };
-
- this._cache.x = this.x;
- this._cache.y = this.y;
-
- /**
- * @property {boolean} renderable - A renderable object will be rendered to the context each frame.
- */
- this.renderable = true;
+ this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]);
};
@@ -22203,32 +35623,59 @@ Phaser.Text.prototype = Object.create(PIXI.Text.prototype);
Phaser.Text.prototype.constructor = Phaser.Text;
/**
-* Automatically called by World.update.
-* @method Phaser.Text.prototype.update
+* Automatically called by World.preUpdate.
+* @method Phaser.Text.prototype.preUpdate
+*/
+Phaser.Text.prototype.preUpdate = function () {
+
+ this._cache[0] = this.world.x;
+ this._cache[1] = this.world.y;
+ this._cache[2] = this.rotation;
+
+ if (!this.exists || !this.parent.exists)
+ {
+ this.renderOrderID = -1;
+ return false;
+ }
+
+ if (this.autoCull)
+ {
+ // Won't get rendered but will still get its transform updated
+ this.renderable = this.game.world.camera.screenView.intersects(this.getBounds());
+ }
+
+ this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]);
+
+ if (this.visible)
+ {
+ this._cache[3] = this.game.world.currentRenderOrderID++;
+ }
+
+ return true;
+
+}
+
+/**
+* Override and use this function in your own custom objects to handle any update requirements you may have.
+*
+* @method Phaser.Text#update
+* @memberof Phaser.Text
*/
Phaser.Text.prototype.update = function() {
- if (!this.exists)
+}
+
+/**
+* Automatically called by World.postUpdate.
+* @method Phaser.Text.prototype.postUpdate
+*/
+Phaser.Text.prototype.postUpdate = function () {
+
+ // Fixed to Camera?
+ if (this._cache[7] === 1)
{
- return;
- }
-
- if (this.fixedToCamera)
- {
- this.x = this.game.camera.view.x + this.cameraOffset.x;
- this.y = this.game.camera.view.y + this.cameraOffset.y;
- }
-
- this._cache.dirty = false;
-
- this._cache.x = this.x;
- this._cache.y = this.y;
-
- if (this.position.x != this._cache.x || this.position.y != this._cache.y)
- {
- this.position.x = this._cache.x;
- this.position.y = this._cache.y;
- this._cache.dirty = true;
+ this.position.x = this.game.camera.view.x + this.cameraOffset.x;
+ this.position.y = this.game.camera.view.y + this.cameraOffset.y;
}
}
@@ -22236,13 +35683,20 @@ Phaser.Text.prototype.update = function() {
/**
* @method Phaser.Text.prototype.destroy
*/
-Phaser.Text.prototype.destroy = function() {
+Phaser.Text.prototype.destroy = function () {
- if (this.group)
+ if (this.filters)
{
- this.group.remove(this);
+ this.filters = null;
}
+ if (this.parent)
+ {
+ this.parent.remove(this);
+ }
+
+ this.texture.destroy();
+
if (this.canvas.parentNode)
{
this.canvas.parentNode.removeChild(this.canvas);
@@ -22254,8 +35708,187 @@ Phaser.Text.prototype.destroy = function() {
}
this.exists = false;
+ this.visible = false;
- this.group = null;
+ this.game = null;
+
+}
+
+/**
+* @method Phaser.Text.prototype.setShadow
+* @param {number} [x=0] - The shadowOffsetX value in pixels. This is how far offset horizontally the shadow effect will be.
+* @param {number} [y=0] - The shadowOffsetY value in pixels. This is how far offset vertically the shadow effect will be.
+* @param {string} [color='rgba(0,0,0,0)'] - The color of the shadow, as given in CSS rgba format. Set the alpha component to 0 to disable the shadow.
+* @param {number} [blur=0] - The shadowBlur value. Make the shadow softer by applying a Gaussian blur to it. A number from 0 (no blur) up to approx. 10 (depending on scene).
+*/
+Phaser.Text.prototype.setShadow = function (x, y, color, blur) {
+
+ this.style.shadowOffsetX = x || 0;
+ this.style.shadowOffsetY = y || 0;
+ this.style.shadowColor = color || 'rgba(0,0,0,0)';
+ this.style.shadowBlur = blur || 0;
+ this.dirty = true;
+
+}
+
+/**
+* Set the style of the text by passing a single style object to it.
+*
+* @method Phaser.Text.prototype.setStyle
+* @param [style] {Object} The style parameters
+* @param [style.font='bold 20pt Arial'] {String} The style and size of the font
+* @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00'
+* @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text
+* @param [style.stroke='black'] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00'
+* @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke)
+* @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used
+* @param [style.wordWrapWidth=100] {Number} The width at which text will wrap
+*/
+Phaser.Text.prototype.setStyle = function (style) {
+
+ style = style || {};
+ style.font = style.font || 'bold 20pt Arial';
+ style.fill = style.fill || 'black';
+ style.align = style.align || 'left';
+ style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136
+ style.strokeThickness = style.strokeThickness || 0;
+ style.wordWrap = style.wordWrap || false;
+ style.wordWrapWidth = style.wordWrapWidth || 100;
+ style.shadowOffsetX = style.shadowOffsetX || 0;
+ style.shadowOffsetY = style.shadowOffsetY || 0;
+ style.shadowColor = style.shadowColor || 'rgba(0,0,0,0)';
+ style.shadowBlur = style.shadowBlur || 0;
+
+ this.style = style;
+ this.dirty = true;
+
+}
+
+/**
+* Renders text. This replaces the Pixi.Text.updateText function as we need a few extra bits in here.
+*
+* @method Phaser.Text.prototype.updateText
+* @private
+*/
+Phaser.Text.prototype.updateText = function () {
+
+ this.context.font = this.style.font;
+
+ var outputText = this.text;
+
+ // word wrap
+ // preserve original text
+ if(this.style.wordWrap)outputText = this.runWordWrap(this.text);
+
+ //split text into lines
+ var lines = outputText.split(/(?:\r\n|\r|\n)/);
+
+ //calculate text width
+ var lineWidths = [];
+ var maxLineWidth = 0;
+ for (var i = 0; i < lines.length; i++)
+ {
+ var lineWidth = this.context.measureText(lines[i]).width;
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ this.canvas.width = maxLineWidth + this.style.strokeThickness;
+
+ //calculate text height
+ var lineHeight = this.determineFontHeight('font: ' + this.style.font + ';') + this.style.strokeThickness + this._lineSpacing + this.style.shadowOffsetY;
+ this.canvas.height = lineHeight * lines.length;
+
+ if(navigator.isCocoonJS) this.context.clearRect(0,0,this.canvas.width,this.canvas.height);
+
+ //set canvas text styles
+ this.context.fillStyle = this.style.fill;
+ this.context.font = this.style.font;
+
+ this.context.strokeStyle = this.style.stroke;
+ this.context.lineWidth = this.style.strokeThickness;
+
+ this.context.shadowOffsetX = this.style.shadowOffsetX;
+ this.context.shadowOffsetY = this.style.shadowOffsetY;
+ this.context.shadowColor = this.style.shadowColor;
+ this.context.shadowBlur = this.style.shadowBlur;
+
+ this.context.textBaseline = 'top';
+
+ //draw lines line by line
+ for (i = 0; i < lines.length; i++)
+ {
+ var linePosition = new PIXI.Point(this.style.strokeThickness / 2, this.style.strokeThickness / 2 + i * lineHeight);
+
+ if(this.style.align === 'right')
+ {
+ linePosition.x += maxLineWidth - lineWidths[i];
+ }
+ else if(this.style.align === 'center')
+ {
+ linePosition.x += (maxLineWidth - lineWidths[i]) / 2;
+ }
+
+ linePosition.y += this._lineSpacing;
+
+ if(this.style.stroke && this.style.strokeThickness)
+ {
+ this.context.strokeText(lines[i], linePosition.x, linePosition.y);
+ }
+
+ if(this.style.fill)
+ {
+ this.context.fillText(lines[i], linePosition.x, linePosition.y);
+ }
+ }
+
+ this.updateTexture();
+}
+
+/**
+* Greedy wrapping algorithm that will wrap words as the line grows longer than its horizontal bounds.
+*
+* @method Phaser.Text.prototype.runWordWrap
+* @private
+*/
+Phaser.Text.prototype.runWordWrap = function (text) {
+
+ var result = '';
+ var lines = text.split('\n');
+
+ for (var i = 0; i < lines.length; i++)
+ {
+ var spaceLeft = this.style.wordWrapWidth;
+ var words = lines[i].split(' ');
+
+ for (var j = 0; j < words.length; j++)
+ {
+ var wordWidth = this.context.measureText(words[j]).width;
+ var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
+
+ if (wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j] + ' ';
+ spaceLeft = this.style.wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += words[j] + ' ';
+ }
+ }
+
+ if (i < lines.length-1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
}
@@ -22279,45 +35912,11 @@ Object.defineProperty(Phaser.Text.prototype, 'angle', {
});
/**
-* The x coordinate of this object in world space.
-* @name Phaser.Text#x
-* @property {number} x - The x coordinate of this object in world space.
+* The text string to be displayed by this Text object, taking into account the style settings.
+* @name Phaser.Text#text
+* @property {string} text - The text string to be displayed by this Text object, taking into account the style settings.
*/
-Object.defineProperty(Phaser.Text.prototype, 'x', {
-
- get: function() {
- return this.position.x;
- },
-
- set: function(value) {
- this.position.x = value;
- }
-
-});
-
-/**
-* The y coordinate of this object in world space.
-* @name Phaser.Text#y
-* @property {number} y - The y coordinate of this object in world space.
-*/
-Object.defineProperty(Phaser.Text.prototype, 'y', {
-
- get: function() {
- return this.position.y;
- },
-
- set: function(value) {
- this.position.y = value;
- }
-
-});
-
-/**
-* The string to be rendered by this Text object.
-* @name Phaser.Text#content
-* @property {string} content - The string to be rendered by this Text object.
-*/
-Object.defineProperty(Phaser.Text.prototype, 'content', {
+Object.defineProperty(Phaser.Text.prototype, 'text', {
get: function() {
return this._text;
@@ -22325,11 +35924,10 @@ Object.defineProperty(Phaser.Text.prototype, 'content', {
set: function(value) {
- // Let's not update unless needed, this way we can safely update the text in a core loop without constant re-draws
if (value !== this._text)
{
- this._text = value;
- this.setText(value);
+ this._text = value.toString() || ' ';
+ this.dirty = true;
}
}
@@ -22337,29 +35935,385 @@ Object.defineProperty(Phaser.Text.prototype, 'content', {
});
/**
-* The font the text will be rendered in.
* @name Phaser.Text#font
-* @property {string} font - The font the text will be rendered in.
+* @property {string} font - The font the text will be rendered in, i.e. 'Arial'. Must be loaded in the browser before use.
*/
Object.defineProperty(Phaser.Text.prototype, 'font', {
get: function() {
- return this._style;
+ return this._font;
},
set: function(value) {
- // Let's not update unless needed, this way we can safely update the text in a core loop without constant re-draws
- if (value !== this._style)
+ if (value !== this._font)
{
- this._style = value;
- this.setStyle(value);
+ this._font = value.trim();
+ this.style.font = this._fontWeight + ' ' + this._fontSize + "px '" + this._font + "'";
+ this.dirty = true;
}
}
});
+/**
+* @name Phaser.Text#fontSize
+* @property {number} fontSize - The size of the font in pixels.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'fontSize', {
+
+ get: function() {
+ return this._fontSize;
+ },
+
+ set: function(value) {
+
+ value = parseInt(value);
+
+ if (value !== this._fontSize)
+ {
+ this._fontSize = value;
+ this.style.font = this._fontWeight + ' ' + this._fontSize + "px '" + this._font + "'";
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#fontWeight
+* @property {number} fontWeight - The weight of the font: 'normal', 'bold', 'italic'. You can combine settings too, such as 'bold italic'.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'fontWeight', {
+
+ get: function() {
+ return this._fontWeight;
+ },
+
+ set: function(value) {
+
+ if (value !== this._fontWeight)
+ {
+ this._fontWeight = value;
+ this.style.font = this._fontWeight + ' ' + this._fontSize + "px '" + this._font + "'";
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#fill
+* @property {object} fill - A canvas fillstyle that will be used on the text eg 'red', '#00FF00'.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'fill', {
+
+ get: function() {
+ return this.style.fill;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.fill)
+ {
+ this.style.fill = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#align
+* @property {string} align - Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'align', {
+
+ get: function() {
+ return this.style.align;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.align)
+ {
+ this.style.align = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#stroke
+* @property {string} stroke - A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00'.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'stroke', {
+
+ get: function() {
+ return this.style.stroke;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.stroke)
+ {
+ this.style.stroke = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#strokeThickness
+* @property {number} strokeThickness - A number that represents the thickness of the stroke. Default is 0 (no stroke)
+*/
+Object.defineProperty(Phaser.Text.prototype, 'strokeThickness', {
+
+ get: function() {
+ return this.style.strokeThickness;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.strokeThickness)
+ {
+ this.style.strokeThickness = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#wordWrap
+* @property {boolean} wordWrap - Indicates if word wrap should be used.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'wordWrap', {
+
+ get: function() {
+ return this.style.wordWrap;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.wordWrap)
+ {
+ this.style.wordWrap = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#wordWrapWidth
+* @property {number} wordWrapWidth - The width at which text will wrap.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'wordWrapWidth', {
+
+ get: function() {
+ return this.style.wordWrapWidth;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.wordWrapWidth)
+ {
+ this.style.wordWrapWidth = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#lineSpacing
+* @property {number} lineSpacing - Additional spacing (in pixels) between each line of text if multi-line.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'lineSpacing', {
+
+ get: function() {
+ return this._lineSpacing;
+ },
+
+ set: function(value) {
+
+ if (value !== this._lineSpacing)
+ {
+ this._lineSpacing = parseFloat(value);
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#shadowOffsetX
+* @property {number} shadowOffsetX - The shadowOffsetX value in pixels. This is how far offset horizontally the shadow effect will be.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'shadowOffsetX', {
+
+ get: function() {
+ return this.style.shadowOffsetX;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.shadowOffsetX)
+ {
+ this.style.shadowOffsetX = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#shadowOffsetY
+* @property {number} shadowOffsetY - The shadowOffsetY value in pixels. This is how far offset vertically the shadow effect will be.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'shadowOffsetY', {
+
+ get: function() {
+ return this.style.shadowOffsetY;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.shadowOffsetY)
+ {
+ this.style.shadowOffsetY = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#shadowColor
+* @property {string} shadowColor - The color of the shadow, as given in CSS rgba format. Set the alpha component to 0 to disable the shadow.
+*/
+Object.defineProperty(Phaser.Text.prototype, 'shadowColor', {
+
+ get: function() {
+ return this.style.shadowColor;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.shadowColor)
+ {
+ this.style.shadowColor = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Text#shadowBlur
+* @property {number} shadowBlur - The shadowBlur value. Make the shadow softer by applying a Gaussian blur to it. A number from 0 (no blur) up to approx. 10 (depending on scene).
+*/
+Object.defineProperty(Phaser.Text.prototype, 'shadowBlur', {
+
+ get: function() {
+ return this.style.shadowBlur;
+ },
+
+ set: function(value) {
+
+ if (value !== this.style.shadowBlur)
+ {
+ this.style.shadowBlur = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* By default a Text object won't process any input events at all. By setting inputEnabled to true the Phaser.InputHandler is
+* activated for this object and it will then start to process click/touch events and more.
+*
+* @name Phaser.Text#inputEnabled
+* @property {boolean} inputEnabled - Set to true to allow this object to receive input events.
+*/
+Object.defineProperty(Phaser.Text.prototype, "inputEnabled", {
+
+ get: function () {
+
+ return (this.input && this.input.enabled);
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ if (this.input === null)
+ {
+ this.input = new Phaser.InputHandler(this);
+ this.input.start();
+ }
+ }
+ else
+ {
+ if (this.input && this.input.enabled)
+ {
+ this.input.stop();
+ }
+ }
+ }
+
+});
+
+/**
+* An Text that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Text.cameraOffset.
+* Note that the cameraOffset values are in addition to any parent in the display list.
+* So if this Text was in a Group that has x: 200, then this will be added to the cameraOffset.x
+*
+* @name Phaser.Text#fixedToCamera
+* @property {boolean} fixedToCamera - Set to true to fix this Text to the Camera at its current world coordinates.
+*/
+Object.defineProperty(Phaser.Text.prototype, "fixedToCamera", {
+
+ get: function () {
+
+ return !!this._cache[7];
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ this._cache[7] = 1;
+ this.cameraOffset.set(this.x, this.y);
+ }
+ else
+ {
+ this._cache[7] = 0;
+ }
+ }
+
+});
+
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -22374,22 +36328,24 @@ Object.defineProperty(Phaser.Text.prototype, 'font', {
* @classdesc BitmapText objects work by taking a texture file and an XML file that describes the font layout.
*
* On Windows you can use the free app BMFont: http://www.angelcode.com/products/bmfont/
-*
* On OS X we recommend Glyph Designer: http://www.71squared.com/en/glyphdesigner
+* For Web there is the great Littera: http://kvazars.com/littera/
*
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
* @param {number} x - X position of the new bitmapText object.
* @param {number} y - Y position of the new bitmapText object.
-* @param {string} text - The actual text that will be written.
-* @param {object} style - The style object containing style attributes like font, font size , etc.
+* @param {string} font - The key of the BitmapFont as stored in Game.Cache.
+* @param {string} [text=''] - The actual text that will be rendered. Can be set later via BitmapText.text.
+* @param {number} [size=32] - The size the font will be rendered in, in pixels.
*/
-Phaser.BitmapText = function (game, x, y, text, style) {
+Phaser.BitmapText = function (game, x, y, font, text, size) {
x = x || 0;
y = y || 0;
+ font = font || '';
text = text || '';
- style = style || '';
+ size = size || 32;
/**
* @property {Phaser.Game} game - A reference to the currently running Game.
@@ -22402,17 +36358,6 @@ Phaser.BitmapText = function (game, x, y, text, style) {
*/
this.exists = true;
- /**
- * @property {boolean} alive - This is a handy little var your game can use to determine if a sprite is alive or not, it doesn't effect rendering.
- * @default
- */
- this.alive = true;
-
- /**
- * @property {Phaser.Group} group - The parent Group of this BitmapText.
- */
- this.group = null;
-
/**
* @property {string} name - The user defined name given to this BitmapText.
* @default
@@ -22425,62 +36370,74 @@ Phaser.BitmapText = function (game, x, y, text, style) {
*/
this.type = Phaser.BITMAPTEXT;
- PIXI.BitmapText.call(this, text, style);
+ /**
+ * @property {Phaser.Point} world - The world coordinates of this Sprite. This differs from the x/y coordinates which are relative to the Sprites container.
+ */
+ this.world = new Phaser.Point(x, y);
/**
- * @property {number} position.x - The x position of this object.
- */
- this.position.x = x;
-
- /**
- * @property {number} position.y - The y position of this object.
- */
- this.position.y = y;
-
- /**
- * The anchor sets the origin point of the texture.
- * The default is 0,0 this means the textures origin is the top left
- * Setting than anchor to 0.5,0.5 means the textures origin is centered
- * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right
- *
- * @property {Phaser.Point} anchor - The anchor around which rotation and scaling takes place.
- */
- this.anchor = new Phaser.Point();
-
- /**
- * @property {Phaser.Point} scale - The scale of the object when rendered. By default it's set to 1 (no scale). You can modify it via scale.x or scale.y or scale.setTo(x, y). A value of 1 means no change to the scale, 0.5 means "half the size", 2 means "twice the size", etc.
- */
- this.scale = new Phaser.Point(1, 1);
-
- /**
- * @property {object} _cache - A mini cache for storing all of the calculated values.
+ * @property {string} _text - Internal cache var.
* @private
*/
- this._cache = {
+ this._text = text;
- dirty: false,
+ /**
+ * @property {string} _font - Internal cache var.
+ * @private
+ */
+ this._font = font;
- // Transform cache
- a00: 1,
- a01: 0,
- a02: x,
- a10: 0,
- a11: 1,
- a12: y,
- id: 1,
+ /**
+ * @property {number} _fontSize - Internal cache var.
+ * @private
+ */
+ this._fontSize = size;
- // The previous calculated position
- x: -1,
- y: -1,
+ /**
+ * @property {string} _align - Internal cache var.
+ * @private
+ */
+ this._align = 'left';
- // The actual scale values based on the worldTransform
- scaleX: 1,
- scaleY: 1
+ /**
+ * @property {number} _tint - Internal cache var.
+ * @private
+ */
+ this._tint = 0xFFFFFF;
- };
+ /**
+ * @property {Phaser.Events} events - The Events you can subscribe to that are dispatched when certain things happen on this Sprite or its components.
+ */
+ this.events = new Phaser.Events(this);
- this._cache.x = this.x;
- this._cache.y = this.y;
+ /**
+ * @property {Phaser.InputHandler|null} input - The Input Handler for this object. Needs to be enabled with image.inputEnabled = true before you can use it.
+ */
+ this.input = null;
+
+ /**
+ * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view.
+ */
+ this.cameraOffset = new Phaser.Point();
+
+ PIXI.BitmapText.call(this, text);
+
+ this.position.set(x, y);
+
+ /**
+ * A small internal cache:
+ * 0 = previous position.x
+ * 1 = previous position.y
+ * 2 = previous rotation
+ * 3 = renderID
+ * 4 = fresh? (0 = no, 1 = yes)
+ * 5 = outOfBoundsFired (0 = no, 1 = yes)
+ * 6 = exists (0 = no, 1 = yes)
+ * 7 = fixed to camera (0 = no, 1 = yes)
+ * @property {Int16Array} _cache
+ * @private
+ */
+ this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]);
};
@@ -22488,61 +36445,153 @@ Phaser.BitmapText.prototype = Object.create(PIXI.BitmapText.prototype);
Phaser.BitmapText.prototype.constructor = Phaser.BitmapText;
/**
-* Automatically called by World.update
-* @method Phaser.BitmapText.prototype.update
+* @method setStyle
+* @private
+*/
+Phaser.BitmapText.prototype.setStyle = function() {
+
+ this.style = { align: this._align };
+ this.fontName = this._font;
+ this.fontSize = this._fontSize;
+ this.dirty = true;
+
+};
+
+/**
+* Automatically called by World.preUpdate.
+* @method Phaser.BitmapText.prototype.preUpdate
+*/
+Phaser.BitmapText.prototype.preUpdate = function () {
+
+ this._cache[0] = this.world.x;
+ this._cache[1] = this.world.y;
+ this._cache[2] = this.rotation;
+
+ if (!this.exists || !this.parent.exists)
+ {
+ this.renderOrderID = -1;
+ return false;
+ }
+
+ if (this.autoCull)
+ {
+ // Won't get rendered but will still get its transform updated
+ this.renderable = this.game.world.camera.screenView.intersects(this.getBounds());
+ }
+
+ this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]);
+
+ if (this.visible)
+ {
+ this._cache[3] = this.game.world.currentRenderOrderID++;
+ }
+
+ return true;
+
+}
+
+/**
+* Override and use this function in your own custom objects to handle any update requirements you may have.
+*
+* @method Phaser.BitmapText#update
+* @memberof Phaser.BitmapText
*/
Phaser.BitmapText.prototype.update = function() {
- if (!this.exists)
+}
+
+/**
+* Automatically called by World.postUpdate.
+* @method Phaser.BitmapText.prototype.postUpdate
+*/
+Phaser.BitmapText.prototype.postUpdate = function () {
+
+ // Fixed to Camera?
+ if (this._cache[7] === 1)
{
- return;
+ this.position.x = this.game.camera.view.x + this.cameraOffset.x;
+ this.position.y = this.game.camera.view.y + this.cameraOffset.y;
}
- this._cache.dirty = false;
-
- this._cache.x = this.x;
- this._cache.y = this.y;
-
- if (this.position.x != this._cache.x || this.position.y != this._cache.y)
- {
- this.position.x = this._cache.x;
- this.position.y = this._cache.y;
- this._cache.dirty = true;
- }
-
- this.pivot.x = this.anchor.x * this.width;
- this.pivot.y = this.anchor.y * this.height;
-
}
/**
-* @method Phaser.Text.prototype.destroy
+* @method Phaser.BitmapText.prototype.destroy
*/
Phaser.BitmapText.prototype.destroy = function() {
- if (this.group)
+ if (this.filters)
{
- this.group.remove(this);
+ this.filters = null;
}
- if (this.canvas && this.canvas.parentNode)
+ if (this.parent)
{
- this.canvas.parentNode.removeChild(this.canvas);
- }
- else
- {
- this.canvas = null;
- this.context = null;
+ this.parent.remove(this);
}
this.exists = false;
+ this.visible = false;
- this.group = null;
+ this.game = null;
+
+ if (this.children.length > 0)
+ {
+ do
+ {
+ this.removeChild(this.children[0]);
+ }
+ while (this.children.length > 0);
+ }
}
/**
-* Indicates the rotation of the BitmapText, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
+* @name Phaser.BitmapText#align
+* @property {string} align - Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text.
+*/
+Object.defineProperty(Phaser.BitmapText.prototype, 'align', {
+
+ get: function() {
+ return this._align;
+ },
+
+ set: function(value) {
+
+ if (value !== this._align)
+ {
+ this._align = value;
+ this.setStyle();
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.BitmapText#tint
+* @property {number} tint - The tint applied to the BitmapText. This is a hex value. Set to white to disable (0xFFFFFF)
+*/
+Object.defineProperty(Phaser.BitmapText.prototype, 'tint', {
+
+ get: function() {
+ return this._tint;
+ },
+
+ set: function(value) {
+
+ if (value !== this._tint)
+ {
+ this._tint = value;
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* Indicates the rotation of the Text, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90.
* If you wish to work in radians instead of degrees use the property Sprite.rotation instead.
* @name Phaser.BitmapText#angle
@@ -22561,35 +36610,139 @@ Object.defineProperty(Phaser.BitmapText.prototype, 'angle', {
});
/**
-* The x coordinate of this object in world space.
-* @name Phaser.BitmapText#x
-* @property {number} x - The x coordinate of this object in world space.
+* @name Phaser.BitmapText#font
+* @property {string} font - The font the text will be rendered in, i.e. 'Arial'. Must be loaded in the browser before use.
*/
-Object.defineProperty(Phaser.BitmapText.prototype, 'x', {
+Object.defineProperty(Phaser.BitmapText.prototype, 'font', {
get: function() {
- return this.position.x;
+ return this._font;
},
set: function(value) {
- this.position.x = value;
+
+ if (value !== this._font)
+ {
+ this._font = value.trim();
+ this.style.font = this._fontSize + "px '" + this._font + "'";
+ this.dirty = true;
+ }
+
}
});
/**
-* The y coordinate of this object in world space.
-* @name Phaser.BitmapText#y
-* @property {number} y - The y coordinate of this object in world space.
+* @name Phaser.BitmapText#fontSize
+* @property {number} fontSize - The size of the font in pixels.
*/
-Object.defineProperty(Phaser.BitmapText.prototype, 'y', {
+Object.defineProperty(Phaser.BitmapText.prototype, 'fontSize', {
get: function() {
- return this.position.y;
+ return this._fontSize;
},
set: function(value) {
- this.position.y = value;
+
+ value = parseInt(value);
+
+ if (value !== this._fontSize)
+ {
+ this._fontSize = value;
+ this.style.font = this._fontSize + "px '" + this._font + "'";
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* The text string to be displayed by this Text object, taking into account the style settings.
+* @name Phaser.BitmapText#text
+* @property {string} text - The text string to be displayed by this Text object, taking into account the style settings.
+*/
+Object.defineProperty(Phaser.BitmapText.prototype, 'text', {
+
+ get: function() {
+ return this._text;
+ },
+
+ set: function(value) {
+
+ if (value !== this._text)
+ {
+ this._text = value.toString() || ' ';
+ this.dirty = true;
+ }
+
+ }
+
+});
+
+/**
+* By default a Text object won't process any input events at all. By setting inputEnabled to true the Phaser.InputHandler is
+* activated for this object and it will then start to process click/touch events and more.
+*
+* @name Phaser.BitmapText#inputEnabled
+* @property {boolean} inputEnabled - Set to true to allow this object to receive input events.
+*/
+Object.defineProperty(Phaser.BitmapText.prototype, "inputEnabled", {
+
+ get: function () {
+
+ return (this.input && this.input.enabled);
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ if (this.input === null)
+ {
+ this.input = new Phaser.InputHandler(this);
+ this.input.start();
+ }
+ }
+ else
+ {
+ if (this.input && this.input.enabled)
+ {
+ this.input.stop();
+ }
+ }
+ }
+
+});
+
+/**
+* An BitmapText that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in BitmapText.cameraOffset.
+* Note that the cameraOffset values are in addition to any parent in the display list.
+* So if this BitmapText was in a Group that has x: 200, then this will be added to the cameraOffset.x
+*
+* @name Phaser.BitmapText#fixedToCamera
+* @property {boolean} fixedToCamera - Set to true to fix this BitmapText to the Camera at its current world coordinates.
+*/
+Object.defineProperty(Phaser.BitmapText.prototype, "fixedToCamera", {
+
+ get: function () {
+
+ return !!this._cache[7];
+
+ },
+
+ set: function (value) {
+
+ if (value)
+ {
+ this._cache[7] = 1;
+ this.cameraOffset.set(this.x, this.y);
+ }
+ else
+ {
+ this._cache[7] = 0;
+ }
}
});
@@ -22613,6 +36766,7 @@ Object.defineProperty(Phaser.BitmapText.prototype, 'y', {
* You can set a unique texture frame and Sound for any of these states.
*
* @constructor
+* @extends Phaser.Image
*
* @param {Phaser.Game} game Current game instance.
* @param {number} [x=0] - X position of the Button.
@@ -22633,7 +36787,7 @@ Phaser.Button = function (game, x, y, key, callback, callbackContext, overFrame,
callback = callback || null;
callbackContext = callbackContext || this;
- Phaser.Sprite.call(this, game, x, y, key, outFrame);
+ Phaser.Image.call(this, game, x, y, key, outFrame);
/**
* @property {number} type - The Phaser Object Type.
@@ -22776,6 +36930,10 @@ Phaser.Button = function (game, x, y, key, callback, callbackContext, overFrame,
*/
this.forceOut = false;
+ this.inputEnabled = true;
+
+ this.input.start(0, true);
+
this.setFrames(overFrame, outFrame, downFrame, upFrame);
if (callback !== null)
@@ -22783,8 +36941,6 @@ Phaser.Button = function (game, x, y, key, callback, callbackContext, overFrame,
this.onInputUp.add(callback, callbackContext);
}
- this.input.start(0, true);
-
// Redirect the input events to here so we can handle animation updates, etc
this.events.onInputOver.add(this.onInputOverHandler, this);
this.events.onInputOut.add(this.onInputOutHandler, this);
@@ -22793,8 +36949,7 @@ Phaser.Button = function (game, x, y, key, callback, callbackContext, overFrame,
};
-Phaser.Button.prototype = Object.create(Phaser.Sprite.prototype);
-Phaser.Button.prototype = Phaser.Utils.extend(true, Phaser.Button.prototype, Phaser.Sprite.prototype, PIXI.Sprite.prototype);
+Phaser.Button.prototype = Object.create(Phaser.Image.prototype);
Phaser.Button.prototype.constructor = Phaser.Button;
/**
@@ -23248,23 +37403,124 @@ Phaser.Button.prototype.setState = function (newState) {
*/
Phaser.Graphics = function (game, x, y) {
+ x = x || 0;
+ y = y || 0;
+
+ /**
+ * @property {Phaser.Game} game - A reference to the currently running Game.
+ */
this.game = game;
+
+ /**
+ * @property {boolean} exists - If exists = false then the Text isn't updated by the core game loop.
+ * @default
+ */
+ this.exists = true;
+
+ /**
+ * @property {string} name - The user defined name given to this object.
+ * @default
+ */
+ this.name = '';
+
+ /**
+ * @property {number} type - The const type of this object.
+ * @default
+ */
+ this.type = Phaser.GRAPHICS;
+
+ /**
+ * @property {Phaser.Point} world - The world coordinates of this Sprite. This differs from the x/y coordinates which are relative to the Sprites container.
+ */
+ this.world = new Phaser.Point(x, y);
+
+ /**
+ * @property {Phaser.Point} cameraOffset - If this object is fixedToCamera then this stores the x/y offset that its drawn at, from the top-left of the camera view.
+ */
+ this.cameraOffset = new Phaser.Point();
PIXI.Graphics.call(this);
- /**
- * @property {number} type - The Phaser Object Type.
- */
- this.type = Phaser.GRAPHICS;
+ this.position.set(x, y);
- this.position.x = x;
- this.position.y = y;
+ /**
+ * A small internal cache:
+ * 0 = previous position.x
+ * 1 = previous position.y
+ * 2 = previous rotation
+ * 3 = renderID
+ * 4 = fresh? (0 = no, 1 = yes)
+ * 5 = outOfBoundsFired (0 = no, 1 = yes)
+ * 6 = exists (0 = no, 1 = yes)
+ * 7 = fixed to camera (0 = no, 1 = yes)
+ * @property {Int16Array} _cache
+ * @private
+ */
+ this._cache = new Int16Array([0, 0, 0, 0, 1, 0, 1, 0]);
};
Phaser.Graphics.prototype = Object.create(PIXI.Graphics.prototype);
Phaser.Graphics.prototype.constructor = Phaser.Graphics;
+/**
+* Automatically called by World.preUpdate.
+* @method Phaser.Graphics.prototype.preUpdate
+*/
+Phaser.Graphics.prototype.preUpdate = function () {
+
+ this._cache[0] = this.world.x;
+ this._cache[1] = this.world.y;
+ this._cache[2] = this.rotation;
+
+ if (!this.exists || !this.parent.exists)
+ {
+ this.renderOrderID = -1;
+ return false;
+ }
+
+ if (this.autoCull)
+ {
+ // Won't get rendered but will still get its transform updated
+ this.renderable = this.game.world.camera.screenView.intersects(this.getBounds());
+ }
+
+ this.world.setTo(this.game.camera.x + this.worldTransform[2], this.game.camera.y + this.worldTransform[5]);
+
+ if (this.visible)
+ {
+ this._cache[3] = this.game.world.currentRenderOrderID++;
+ }
+
+ return true;
+
+}
+
+/**
+* Override and use this function in your own custom objects to handle any update requirements you may have.
+*
+* @method Phaser.Graphics#update
+* @memberof Phaser.Graphics
+*/
+Phaser.Graphics.prototype.update = function() {
+
+}
+
+/**
+* Automatically called by World.postUpdate.
+* @method Phaser.Graphics.prototype.postUpdate
+*/
+Phaser.Graphics.prototype.postUpdate = function () {
+
+ // Fixed to Camera?
+ if (this._cache[7] === 1)
+ {
+ this.position.x = this.game.camera.view.x + this.cameraOffset.x;
+ this.position.y = this.game.camera.view.y + this.cameraOffset.y;
+ }
+
+}
+
/**
* Destroy this Graphics instance.
*
@@ -23274,17 +37530,22 @@ Phaser.Graphics.prototype.destroy = function() {
this.clear();
- if (this.group)
+ if (this.parent)
{
- this.group.remove(this);
+ this.parent.remove(this);
}
+ this.exists = false;
+ this.visible = false;
+
this.game = null;
}
/*
* Draws a {Phaser.Polygon} or a {PIXI.Polygon} filled
+*
+* @method Phaser.Sprite.prototype.drawPolygon
*/
Phaser.Graphics.prototype.drawPolygon = function (poly) {
@@ -23299,38 +37560,52 @@ Phaser.Graphics.prototype.drawPolygon = function (poly) {
}
+/**
+* Indicates the rotation of the Graphics, in degrees, from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
+* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement player.angle = 450 is the same as player.angle = 90.
+* If you wish to work in radians instead of degrees use the property Sprite.rotation instead.
+* @name Phaser.Graphics#angle
+* @property {number} angle - Gets or sets the angle of rotation in degrees.
+*/
Object.defineProperty(Phaser.Graphics.prototype, 'angle', {
get: function() {
- return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.rotation));
+ return Phaser.Math.radToDeg(this.rotation);
},
set: function(value) {
- this.rotation = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
+ this.rotation = Phaser.Math.degToRad(value);
}
});
-Object.defineProperty(Phaser.Graphics.prototype, 'x', {
+/**
+* An Graphics that is fixed to the camera uses its x/y coordinates as offsets from the top left of the camera. These are stored in Graphics.cameraOffset.
+* Note that the cameraOffset values are in addition to any parent in the display list.
+* So if this Graphics was in a Group that has x: 200, then this will be added to the cameraOffset.x
+*
+* @name Phaser.Graphics#fixedToCamera
+* @property {boolean} fixedToCamera - Set to true to fix this Graphics to the Camera at its current world coordinates.
+*/
+Object.defineProperty(Phaser.Graphics.prototype, "fixedToCamera", {
+
+ get: function () {
+
+ return !!this._cache[7];
- get: function() {
- return this.position.x;
},
- set: function(value) {
- this.position.x = value;
- }
+ set: function (value) {
-});
-
-Object.defineProperty(Phaser.Graphics.prototype, 'y', {
-
- get: function() {
- return this.position.y;
- },
-
- set: function(value) {
- this.position.y = value;
+ if (value)
+ {
+ this._cache[7] = 1;
+ this.cameraOffset.set(this.x, this.y);
+ }
+ else
+ {
+ this._cache[7] = 0;
+ }
}
});
@@ -23346,11 +37621,11 @@ Object.defineProperty(Phaser.Graphics.prototype, 'y', {
* @class Phaser.RenderTexture
* @constructor
* @param {Phaser.Game} game - Current game instance.
-* @param {string} key - Asset key for the render texture.
-* @param {number} width - the width of the render texture.
-* @param {number} height - the height of the render texture.
+* @param {string} key - Internal Phaser reference key for the render texture.
+* @param {number} [width=100] - The width of the render texture.
+* @param {number} [height=100] - The height of the render texture.
*/
-Phaser.RenderTexture = function (game, key, width, height) {
+Phaser.RenderTexture = function (game, width, height, key) {
/**
* @property {Phaser.Game} game - A reference to the currently running game.
@@ -23358,315 +37633,630 @@ Phaser.RenderTexture = function (game, key, width, height) {
this.game = game;
/**
- * @property {string} name - the name of the object.
+ * @property {string} key - The key of the RenderTexture in the Cache, if stored there.
*/
- this.name = key;
-
- PIXI.EventTarget.call(this);
-
- /**
- * @property {number} width - the width.
- */
- this.width = width || 100;
-
- /**
- * @property {number} height - the height.
- */
- this.height = height || 100;
-
- /**
- * @property {PIXI.mat3} indetityMatrix - Matrix object.
- */
- this.indetityMatrix = PIXI.mat3.create();
-
- /**
- * @property {PIXI.Rectangle} frame - The frame for this texture.
- */
- this.frame = new PIXI.Rectangle(0, 0, this.width, this.height);
+ this.key = key;
/**
* @property {number} type - Base Phaser object type.
*/
this.type = Phaser.RENDERTEXTURE;
- this._tempPoint = { x: 0, y: 0 };
-
- if (PIXI.gl)
- {
- this.initWebGL();
- }
- else
- {
- this.initCanvas();
- }
+ PIXI.RenderTexture.call(this, width, height);
};
-Phaser.RenderTexture.prototype = Object.create(PIXI.Texture.prototype);
-Phaser.RenderTexture.prototype.constructor = PIXI.RenderTexture;
+Phaser.RenderTexture.prototype = Object.create(PIXI.RenderTexture.prototype);
+Phaser.RenderTexture.prototype.constructor = Phaser.RenderTexture;
/**
-* This function will draw the display object to the texture. If the display object is a Group or has children it will
-* draw all children as well.
-*
-* @method Phaser.RenderTexture#render
-* @memberof Phaser.RenderTexture
-* @param {DisplayObject} displayObject - The display object to render this texture on.
-* @param {Phaser.Point} [position] - Where to draw the display object.
-* @param {boolean} [clear=false] - If true the texture will be cleared before the displayObject is drawn.
-* @param {boolean} [renderHidden=false] - If true displayObjects that have their visible property set to false will still be rendered.
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
-Phaser.RenderTexture.prototype.render = function(displayObject, position, clear, renderHidden) {
- if (typeof position === 'undefined') { position = false; }
- if (typeof clear === 'undefined') { clear = false; }
- if (typeof renderHidden === 'undefined') { renderHidden = false; }
+/**
+* Phaser SpriteBatch constructor.
+*
+* @classdesc The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, so use when you need a lot of sprites or particles.
+* @class Phaser.SpriteBatch
+* @extends Phaser.Group
+* @constructor
+* @param {Phaser.Game} game - A reference to the currently running game.
+* @param {Phaser.Group|Phaser.Sprite} parent - The parent Group, DisplayObject or DisplayObjectContainer that this Group will be added to. If undefined or null it will use game.world.
+* @param {string} [name=group] - A name for this Group. Not used internally but useful for debugging.
+* @param {boolean} [addToStage=false] - If set to true this Group will be added directly to the Game.Stage instead of Game.World.
+*/
+Phaser.SpriteBatch = function (game, parent, name, addToStage) {
- if (displayObject instanceof Phaser.Group)
+ PIXI.SpriteBatch.call(this);
+
+ Phaser.Group.call(this, game, parent, name, addToStage);
+
+ /**
+ * @property {number} type - Internal Phaser Type value.
+ * @protected
+ */
+ this.type = Phaser.SPRITEBATCH;
+
+};
+
+Phaser.SpriteBatch.prototype = Phaser.Utils.extend(true, Phaser.SpriteBatch.prototype, Phaser.Group.prototype, PIXI.SpriteBatch.prototype);
+
+Phaser.SpriteBatch.prototype.constructor = Phaser.SpriteBatch;
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* @class Phaser.BitmapFont
+* @extends Phaser.RenderTexture
+* @constructor
+* @param {Phaser.Game} game - Current game instance.
+* @param {string} key - The font set graphic set as stored in the Game.Cache.
+* @param {number} characterWidth - The width of each character in the font set.
+* @param {number} characterHeight - The height of each character in the font set.
+* @param {string} chars - The characters used in the font set, in display order. You can use the TEXT_SET consts for common font set arrangements.
+* @param {number} charsPerRow - The number of characters per row in the font set.
+* @param {number} [xSpacing=0] - If the characters in the font set have horizontal spacing between them set the required amount here.
+* @param {number} [ySpacing=0] - If the characters in the font set have vertical spacing between them set the required amount here.
+* @param {number} [xOffset=0] - If the font set doesn't start at the top left of the given image, specify the X coordinate offset here.
+* @param {number} [yOffset=0] - If the font set doesn't start at the top left of the given image, specify the Y coordinate offset here.
+*/
+Phaser.BitmapFont = function (game, key, characterWidth, characterHeight, chars, charsPerRow, xSpacing, ySpacing, xOffset, yOffset) {
+
+ /**
+ * @property {number} characterWidth - The width of each character in the font set.
+ */
+ this.characterWidth = characterWidth;
+
+ /**
+ * @property {number} characterHeight - The height of each character in the font set.
+ */
+ this.characterHeight = characterHeight;
+
+ /**
+ * @property {number} characterSpacingX - If the characters in the font set have horizontal spacing between them set the required amount here.
+ */
+ this.characterSpacingX = xSpacing || 0;
+
+ /**
+ * @property {number} characterSpacingY - If the characters in the font set have vertical spacing between them set the required amount here.
+ */
+ this.characterSpacingY = ySpacing || 0;
+
+ /**
+ * @property {number} characterPerRow - The number of characters per row in the font set.
+ */
+ this.characterPerRow = charsPerRow;
+
+ /**
+ * @property {number} offsetX - If the font set doesn't start at the top left of the given image, specify the X coordinate offset here.
+ */
+ this.offsetX = xOffset || 0;
+
+ /**
+ * @property {number} offsetY - If the font set doesn't start at the top left of the given image, specify the Y coordinate offset here.
+ */
+ this.offsetY = yOffset || 0;
+
+ /**
+ * @property {string} align - Alignment of the text when multiLine = true or a fixedWidth is set. Set to BitmapFont.ALIGN_LEFT (default), BitmapFont.ALIGN_RIGHT or BitmapFont.ALIGN_CENTER.
+ */
+ this.align = "left";
+
+ /**
+ * @property {boolean} multiLine - If set to true all carriage-returns in text will form new lines (see align). If false the font will only contain one single line of text (the default)
+ * @default
+ */
+ this.multiLine = false;
+
+ /**
+ * @property {boolean} autoUpperCase - Automatically convert any text to upper case. Lots of old bitmap fonts only contain upper-case characters, so the default is true.
+ * @default
+ */
+ this.autoUpperCase = true;
+
+ /**
+ * @property {number} customSpacingX - Adds horizontal spacing between each character of the font, in pixels.
+ * @default
+ */
+ this.customSpacingX = 0;
+
+ /**
+ * @property {number} customSpacingY - Adds vertical spacing between each line of multi-line text, set in pixels.
+ * @default
+ */
+ this.customSpacingY = 0;
+
+ /**
+ * If you need this BitmapFont image to have a fixed width you can set the width in this value.
+ * If text is wider than the width specified it will be cropped off.
+ * @property {number} fixedWidth
+ */
+ this.fixedWidth = 0;
+
+ /**
+ * @property {HTMLImage} fontSet - A reference to the image stored in the Game.Cache that contains the font.
+ */
+ this.fontSet = game.cache.getImage(key);
+
+ /**
+ * @property {string} _text - The text of the font image.
+ * @private
+ */
+ this._text = '';
+
+ /**
+ * @property {array} grabData - An array of rects for faster character pasting.
+ * @private
+ */
+ this.grabData = [];
+
+ // Now generate our rects for faster copying later on
+ var currentX = this.offsetX;
+ var currentY = this.offsetY;
+ var r = 0;
+ var data = new Phaser.FrameData();
+
+ for (var c = 0; c < chars.length; c++)
{
- displayObject = displayObject._container;
+ var uuid = game.rnd.uuid();
+
+ var frame = data.addFrame(new Phaser.Frame(c, currentX, currentY, this.characterWidth, this.characterHeight, '', uuid));
+
+ this.grabData[chars.charCodeAt(c)] = frame.index;
+
+ PIXI.TextureCache[uuid] = new PIXI.Texture(PIXI.BaseTextureCache[key], {
+ x: currentX,
+ y: currentY,
+ width: this.characterWidth,
+ height: this.characterHeight
+ });
+
+ r++;
+
+ if (r == this.characterPerRow)
+ {
+ r = 0;
+ currentX = this.offsetX;
+ currentY += this.characterHeight + this.characterSpacingY;
+ }
+ else
+ {
+ currentX += this.characterWidth + this.characterSpacingX;
+ }
}
- if (PIXI.gl)
+ game.cache.updateFrameData(key, data);
+
+ this.stamp = new Phaser.Image(game, 0, 0, key, 0);
+
+ Phaser.RenderTexture.call(this, game);
+
+ /**
+ * @property {number} type - Base Phaser object type.
+ */
+ this.type = Phaser.BITMAPFONT;
+
+};
+
+Phaser.BitmapFont.prototype = Object.create(Phaser.RenderTexture.prototype);
+Phaser.BitmapFont.prototype.constructor = Phaser.BitmapFont;
+
+/**
+* Align each line of multi-line text to the left.
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.ALIGN_LEFT = "left";
+
+/**
+* Align each line of multi-line text to the right.
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.ALIGN_RIGHT = "right";
+
+/**
+* Align each line of multi-line text in the center.
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.ALIGN_CENTER = "center";
+
+/**
+* Text Set 1 = !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET1 = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
+
+/**
+* Text Set 2 = !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET2 = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/**
+* Text Set 3 = ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
+
+/**
+* Text Set 4 = ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789";
+
+/**
+* Text Set 5 = ABCDEFGHIJKLMNOPQRSTUVWXYZ.,/() '!?-*:0123456789
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET5 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,/() '!?-*:0123456789";
+
+/**
+* Text Set 6 = ABCDEFGHIJKLMNOPQRSTUVWXYZ!?:;0123456789\"(),-.'
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET6 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!?:;0123456789\"(),-.' ";
+
+/**
+* Text Set 7 = AGMSY+:4BHNTZ!;5CIOU.?06DJPV,(17EKQW\")28FLRX-'39
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET7 = "AGMSY+:4BHNTZ!;5CIOU.?06DJPV,(17EKQW\")28FLRX-'39";
+
+/**
+* Text Set 8 = 0123456789 .ABCDEFGHIJKLMNOPQRSTUVWXYZ
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET8 = "0123456789 .ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/**
+* Text Set 9 = ABCDEFGHIJKLMNOPQRSTUVWXYZ()-0123456789.:,'\"?!
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET9 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ()-0123456789.:,'\"?!";
+
+/**
+* Text Set 10 = ABCDEFGHIJKLMNOPQRSTUVWXYZ
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET10 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+/**
+* Text Set 11 = ABCDEFGHIJKLMNOPQRSTUVWXYZ.,\"-+!?()':;0123456789
+* @constant
+* @type {string}
+*/
+Phaser.BitmapFont.TEXT_SET11 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,\"-+!?()':;0123456789";
+
+/**
+* If you need this FlxSprite to have a fixed width and custom alignment you can set the width here.
+* If text is wider than the width specified it will be cropped off.
+*
+* @method Phaser.BitmapFont#setFixedWidth
+* @memberof Phaser.BitmapFont
+* @param {number} width - Width in pixels of this BitmapFont. Set to zero to disable and re-enable automatic resizing.
+* @param {string} [lineAlignment='left'] - Align the text within this width. Set to BitmapFont.ALIGN_LEFT (default), BitmapFont.ALIGN_RIGHT or BitmapFont.ALIGN_CENTER.
+*/
+Phaser.BitmapFont.prototype.setFixedWidth = function (width, lineAlignment) {
+
+ if (typeof lineAlignment === 'undefined') { lineAlignment = 'left'; }
+
+ this.fixedWidth = width;
+ this.align = lineAlignment;
+
+}
+
+/**
+* A helper function that quickly sets lots of variables at once, and then updates the text.
+*
+* @method Phaser.BitmapFont#setText
+* @memberof Phaser.BitmapFont
+* @param {string} content - The text of this sprite.
+* @param {boolean} [multiLine=false] - Set to true if you want to support carriage-returns in the text and create a multi-line sprite instead of a single line.
+* @param {number} [characterSpacing=0] - To add horizontal spacing between each character specify the amount in pixels.
+* @param {number} [lineSpacing=0] - To add vertical spacing between each line of text, set the amount in pixels.
+* @param {string} [lineAlignment='left'] - Align each line of multi-line text. Set to BitmapFont.ALIGN_LEFT, BitmapFont.ALIGN_RIGHT or BitmapFont.ALIGN_CENTER.
+* @param {boolean} [allowLowerCase=false] - Lots of bitmap font sets only include upper-case characters, if yours needs to support lower case then set this to true.
+*/
+Phaser.BitmapFont.prototype.setText = function (content, multiLine, characterSpacing, lineSpacing, lineAlignment, allowLowerCase) {
+
+ this.multiLine = multiLine || false;
+ this.customSpacingX = characterSpacing || 0;
+ this.customSpacingY = lineSpacing || 0;
+ this.align = lineAlignment || 'left';
+
+ if (allowLowerCase)
{
- this.renderWebGL(displayObject, position, clear, renderHidden);
+ this.autoUpperCase = false;
}
else
{
- this.renderCanvas(displayObject, position, clear, renderHidden);
+ this.autoUpperCase = true;
+ }
+
+ if (content.length > 0)
+ {
+ this.text = content;
}
}
/**
-* This function will draw the display object to the texture at the given x/y coordinates.
-* If the display object is a Group or has children it will draw all children as well.
-*
-* @method Phaser.RenderTexture#renderXY
-* @memberof Phaser.RenderTexture
-* @param {DisplayObject} displayObject - The display object to render this texture on.
-* @param {number} x - The x coordinate to draw the display object at.
-* @param {number} y - The y coordinate to draw the display object at.
-* @param {boolean} [clear=false] - If true the texture will be cleared before the displayObject is drawn.
-* @param {boolean} [renderHidden=false] - If true displayObjects that have their visible property set to false will still be rendered.
+* Over rides the default PIXI.RenderTexture resize event as we need our baseTexture resized as well.
+*
+* @method Phaser.BitmapFont#resize
+* @memberof Phaser.BitmapFont
*/
-Phaser.RenderTexture.prototype.renderXY = function(displayObject, x, y, clear, renderHidden) {
+Phaser.BitmapFont.prototype.resize = function (width, height) {
- this._tempPoint.x = x;
- this._tempPoint.y = y;
+ this.width = width;
+ this.height = height;
- this.render(displayObject, this._tempPoint, clear, renderHidden);
-
-}
-
-/**
-* Initializes the webgl data for this texture
-*
-* @method Phaser.RenderTexture#initWebGL
-* @memberof Phaser.RenderTexture
-* @private
-*/
-Phaser.RenderTexture.prototype.initWebGL = function() {
-
- var gl = PIXI.gl;
- this.glFramebuffer = gl.createFramebuffer();
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer );
-
- this.glFramebuffer.width = this.width;
- this.glFramebuffer.height = this.height;
-
- this.baseTexture = new PIXI.BaseTexture();
+ this.frame.width = this.width;
+ this.frame.height = this.height;
this.baseTexture.width = this.width;
this.baseTexture.height = this.height;
- this.baseTexture._glTexture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture);
-
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
-
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-
- this.baseTexture.isRender = true;
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer );
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.baseTexture._glTexture, 0);
-
- // create a projection matrix..
- this.projection = new PIXI.Point(this.width/2 , -this.height/2);
-
- // set the correct render function..
- // this.render = this.renderWebGL;
-}
-
-/**
-* Resizes the RenderTexture.
-*
-* @method Phaser.RenderTexture#resize
-* @memberof Phaser.RenderTexture
-*/
-Phaser.RenderTexture.prototype.resize = function(width, height)
-{
-
- this.width = width;
- this.height = height;
-
- if(PIXI.gl)
+ if (this.renderer.type === PIXI.WEBGL_RENDERER)
{
- this.projection.x = this.width/2
- this.projection.y = -this.height/2;
-
- var gl = PIXI.gl;
- gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTexture);
+ this.projection.x = this.width / 2;
+ this.projection.y = -this.height / 2;
+
+ var gl = this.renderer.gl;
+ gl.bindTexture(gl.TEXTURE_2D, this.baseTexture._glTextures[gl.id]);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.width, this.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
}
else
{
-
- this.frame.width = this.width
- this.frame.height = this.height;
- this.renderer.resize(this.width, this.height);
+ this.textureBuffer.resize(this.width, this.height);
}
+
+ PIXI.Texture.frameUpdates.push(this);
+
}
/**
-* Initializes the canvas data for this texture
-*
-* @method Phaser.RenderTexture#initCanvas
-* @memberof Phaser.RenderTexture
-* @private
+* Updates the BitmapData of the Sprite with the text
+*
+* @method Phaser.BitmapFont#buildBitmapFontText
+* @memberof Phaser.BitmapFont
*/
-Phaser.RenderTexture.prototype.initCanvas = function()
-{
- this.renderer = new PIXI.CanvasRenderer(this.width, this.height, null, 0);
+Phaser.BitmapFont.prototype.buildBitmapFontText = function () {
- this.baseTexture = new PIXI.BaseTexture(this.renderer.view);
- this.frame = new PIXI.Rectangle(0, 0, this.width, this.height);
+ var cx = 0;
+ var cy = 0;
- // this.render = this.renderCanvas;
-}
-
-/**
-* This function will draw the display object to the texture.
-*
-* @method Phaser.RenderTexture#renderWebGL
-* @memberof Phaser.RenderTexture
-* @private
-* @param {DisplayObject} displayObject - The display object to render this texture on.
-* @param {Phaser.Point} [position] - Where to draw the display object.
-* @param {boolean} [clear=false] - If true the texture will be cleared before the displayObject is drawn.
-* @param {boolean} [renderHidden=false] - If true displayObjects that have their visible property set to false will still be rendered.
-*/
-Phaser.RenderTexture.prototype.renderWebGL = function(displayObject, position, clear, renderHidden)
-{
- var gl = PIXI.gl;
-
- // enable the alpha color mask..
- gl.colorMask(true, true, true, true);
-
- gl.viewport(0, 0, this.width, this.height);
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.glFramebuffer );
-
- if (clear)
+ if (this.multiLine)
{
- gl.clearColor(0,0,0, 0);
- gl.clear(gl.COLOR_BUFFER_BIT);
- }
-
- // THIS WILL MESS WITH HIT TESTING!
- var children = displayObject.children;
-
- //TODO -? create a new one??? dont think so!
- var originalWorldTransform = displayObject.worldTransform;
- displayObject.worldTransform = PIXI.mat3.create();//sthis.indetityMatrix;
- // modify to flip...
- displayObject.worldTransform[4] = -1;
- displayObject.worldTransform[5] = this.projection.y * -2;
-
- if (position)
- {
- displayObject.worldTransform[2] = position.x;
- displayObject.worldTransform[5] -= position.y;
- }
+ var lines = this._text.split("\n");
- PIXI.visibleCount++;
- displayObject.vcount = PIXI.visibleCount;
-
- for (var i = 0, j = children.length; i < j; i++)
- {
- children[i].updateTransform();
- }
-
- var renderGroup = displayObject.__renderGroup;
-
- if (renderGroup)
- {
- if (displayObject == renderGroup.root)
+ if (this.fixedWidth > 0)
{
- renderGroup.render(this.projection, this.glFramebuffer);
+ this.resize(fixedWidth, (lines.length * (this.characterHeight + this.customSpacingY)) - this.customSpacingY);
}
else
{
- renderGroup.renderSpecific(displayObject, this.projection, this.glFramebuffer);
+ this.resize(this.getLongestLine() * (this.characterWidth + this.customSpacingX), (lines.length * (this.characterHeight + this.customSpacingY)) - this.customSpacingY);
+ }
+
+ this.textureBuffer.clear();
+
+ // Loop through each line of text
+ for (var i = 0; i < lines.length; i++)
+ {
+ // This line of text is held in lines[i] - need to work out the alignment
+ switch (this.align)
+ {
+ case Phaser.BitmapFont.ALIGN_LEFT:
+ cx = 0;
+ break;
+
+ case Phaser.BitmapFont.ALIGN_RIGHT:
+ cx = this.width - (lines[i].length * (this.characterWidth + this.customSpacingX));
+ break;
+
+ case Phaser.BitmapFont.ALIGN_CENTER:
+ cx = (this.width / 2) - ((lines[i].length * (this.characterWidth + this.customSpacingX)) / 2);
+ cx += this.customSpacingX / 2;
+ break;
+ }
+
+ // Sanity checks
+ if (cx < 0)
+ {
+ cx = 0;
+ }
+
+ this.pasteLine(lines[i], cx, cy, this.customSpacingX);
+
+ cy += this.characterHeight + this.customSpacingY;
}
}
else
{
- if (!this.renderGroup)
+ if (this.fixedWidth > 0)
{
- this.renderGroup = new PIXI.WebGLRenderGroup(gl);
+ this.resize(fixedWidth, this.characterHeight);
+ }
+ else
+ {
+ this.resize(this._text.length * (this.characterWidth + this.customSpacingX), this.characterHeight);
}
- this.renderGroup.setRenderable(displayObject);
- this.renderGroup.render(this.projection, this.glFramebuffer);
- }
+ this.textureBuffer.clear();
+
+ switch (this.align)
+ {
+ case Phaser.BitmapFont.ALIGN_LEFT:
+ cx = 0;
+ break;
+
+ case Phaser.BitmapFont.ALIGN_RIGHT:
+ cx = this.width - (this._text.length * (this.characterWidth + this.customSpacingX));
+ break;
+
+ case Phaser.BitmapFont.ALIGN_CENTER:
+ cx = (this.width / 2) - ((this._text.length * (this.characterWidth + this.customSpacingX)) / 2);
+ cx += this.customSpacingX / 2;
+ break;
+ }
- displayObject.worldTransform = originalWorldTransform;
+ this.pasteLine(this._text, cx, 0, this.customSpacingX);
+ }
+
}
/**
- * This function will draw the display object to the texture.
- *
-* @method Phaser.RenderTexture#renderCanvas
-* @memberof Phaser.RenderTexture
-* @private
-* @param {DisplayObject} displayObject - The display object to render this texture on.
-* @param {Phaser.Point} [position] - Where to draw the display object.
-* @param {boolean} [clear=false] - If true the texture will be cleared before the displayObject is drawn.
-* @param {boolean} [renderHidden=false] - If true displayObjects that have their visible property set to false will still be rendered.
+* Internal function that takes a single line of text (2nd parameter) and pastes it into the BitmapData at the given coordinates.
+* Used by getLine and getMultiLine
+*
+* @method Phaser.BitmapFont#buildBitmapFontText
+* @memberof Phaser.BitmapFont
+* @param {string} line - The single line of text to paste.
+* @param {number} x - The x coordinate.
+* @param {number} y - The y coordinate.
+* @param {number} customSpacingX - Custom X spacing.
*/
-Phaser.RenderTexture.prototype.renderCanvas = function(displayObject, position, clear, renderHidden)
-{
- var children = displayObject.children;
+Phaser.BitmapFont.prototype.pasteLine = function (line, x, y, customSpacingX) {
- displayObject.worldTransform = PIXI.mat3.create();
-
- if (position)
+ var p = new Phaser.Point();
+
+ for (var c = 0; c < line.length; c++)
{
- displayObject.worldTransform[2] = position.x;
- displayObject.worldTransform[5] = position.y;
+ // If it's a space then there is no point copying, so leave a blank space
+ if (line.charAt(c) == " ")
+ {
+ x += this.characterWidth + this.customSpacingX;
+ }
+ else
+ {
+ // If the character doesn't exist in the font then we don't want a blank space, we just want to skip it
+ if (this.grabData[line.charCodeAt(c)] >= 0)
+ {
+ this.stamp.frame = this.grabData[line.charCodeAt(c)];
+ p.set(x, y);
+ this.render(this.stamp, p, false);
+
+ x += this.characterWidth + this.customSpacingX;
+
+ if (x > this.width)
+ {
+ break;
+ }
+ }
+ }
}
-
- for (var i = 0, j = children.length; i < j; i++)
- {
- children[i].updateTransform();
- }
-
- if (clear)
- {
- this.renderer.context.clearRect(0, 0, this.width, this.height);
- }
-
- this.renderer.renderDisplayObject(displayObject, renderHidden);
-
- this.renderer.context.setTransform(1, 0, 0, 1, 0, 0);
-
}
+
+/**
+* Works out the longest line of text in _text and returns its length
+*
+* @method Phaser.BitmapFont#getLongestLine
+* @memberof Phaser.BitmapFont
+* @return {number} The length of the longest line of text.
+*/
+Phaser.BitmapFont.prototype.getLongestLine = function () {
+
+ var longestLine = 0;
+
+ if (this._text.length > 0)
+ {
+ var lines = this._text.split("\n");
+
+ for (var i = 0; i < lines.length; i++)
+ {
+ if (lines[i].length > longestLine)
+ {
+ longestLine = lines[i].length;
+ }
+ }
+ }
+
+ return longestLine;
+}
+
+/**
+* Internal helper function that removes all unsupported characters from the _text String, leaving only characters contained in the font set.
+*
+* @method Phaser.BitmapFont#removeUnsupportedCharacters
+* @memberof Phaser.BitmapFont
+* @protected
+* @param {boolean} [stripCR=true] - Should it strip carriage returns as well?
+* @return {string} A clean version of the string.
+*/
+Phaser.BitmapFont.prototype.removeUnsupportedCharacters = function (stripCR) {
+
+ var newString = "";
+
+ for (var c = 0; c < this._text.length; c++)
+ {
+ var char = this._text[c];
+ var code = char.charCodeAt(0);
+
+ if (this.grabData[code] >= 0 || (!stripCR && char === "\n"))
+ {
+ newString = newString.concat(char);
+ }
+ }
+
+ return newString;
+}
+
+/**
+* @name Phaser.BitmapText#text
+* @property {string} text - Set this value to update the text in this sprite. Carriage returns are automatically stripped out if multiLine is false. Text is converted to upper case if autoUpperCase is true.
+*/
+Object.defineProperty(Phaser.BitmapFont.prototype, "text", {
+
+ get: function () {
+
+ return this._text;
+
+ },
+
+ set: function (value) {
+
+ var newText;
+
+ if (this.autoUpperCase)
+ {
+ newText = value.toUpperCase();
+ }
+ else
+ {
+ newText = value;
+ }
+
+ if (newText !== this._text)
+ {
+ this._text = newText;
+
+ this.removeUnsupportedCharacters(this.multiLine);
+
+ this.buildBitmapFontText();
+ }
+
+ }
+
+});
/**
* @author Richard Davey
@@ -23696,7 +38286,8 @@ Phaser.Canvas = {
width = width || 256;
height = height || 256;
- var canvas = document.createElement('canvas');
+ // var canvas = document.createElement('canvas');
+ var canvas = document.createElement(navigator.isCocoonJS ? 'screencanvas' : 'canvas');
if (typeof id === 'string')
{
@@ -23989,37 +38580,24 @@ Phaser.StageScaleMode = function (game, width, height) {
/**
* @property {number} minWidth - Minimum width the canvas should be scaled to (in pixels).
- * @default
*/
this.minWidth = null;
/**
- * @property {number} maxWidth - Maximum width the canvas should be scaled to (in pixels).
- * If null it will scale to whatever width the browser can handle.
- * @default
+ * @property {number} maxWidth - Maximum width the canvas should be scaled to (in pixels). If null it will scale to whatever width the browser can handle.
*/
this.maxWidth = null;
/**
* @property {number} minHeight - Minimum height the canvas should be scaled to (in pixels).
- * @default
*/
this.minHeight = null;
/**
- * @property {number} maxHeight - Maximum height the canvas should be scaled to (in pixels).
- * If null it will scale to whatever height the browser can handle.
- * @default
+ * @property {number} maxHeight - Maximum height the canvas should be scaled to (in pixels). If null it will scale to whatever height the browser can handle.
*/
this.maxHeight = null;
- /**
- * @property {number} _startHeight - Stage height when starting the game.
- * @default
- * @private
- */
- this._startHeight = 0;
-
/**
* @property {boolean} forceLandscape - If the game should be forced to use Landscape mode, this is set to true by Game.Stage
* @default
@@ -24054,20 +38632,6 @@ Phaser.StageScaleMode = function (game, width, height) {
*/
this.pageAlignVertically = false;
- /**
- * @property {number} _width - Cached stage width for full screen mode.
- * @default
- * @private
- */
- this._width = 0;
-
- /**
- * @property {number} _height - Cached stage height for full screen mode.
- * @default
- * @private
- */
- this._height = 0;
-
/**
* @property {number} maxIterations - The maximum number of times it will try to resize the canvas to fill the browser.
* @default
@@ -24076,7 +38640,6 @@ Phaser.StageScaleMode = function (game, width, height) {
/**
* @property {PIXI.Sprite} orientationSprite - The Sprite that is optionally displayed if the browser enters an unsupported orientation.
- * @default
*/
this.orientationSprite = null;
@@ -24105,6 +38668,11 @@ Phaser.StageScaleMode = function (game, width, height) {
*/
this.hasResized = new Phaser.Signal();
+ /**
+ * @property {number} orientation - The orientation value of the game (as defined by window.orientation if set). 90 = landscape. 0 = portrait.
+ */
+ this.orientation = 0;
+
if (window['orientation'])
{
this.orientation = window['orientation'];
@@ -24115,10 +38683,6 @@ Phaser.StageScaleMode = function (game, width, height) {
{
this.orientation = 90;
}
- else
- {
- this.orientation = 0;
- }
}
/**
@@ -24140,16 +38704,50 @@ Phaser.StageScaleMode = function (game, width, height) {
this.margin = new Phaser.Point(0, 0);
/**
- * @property {number} aspectRatio - Aspect ratio.
- * @default
+ * @property {number} aspectRatio - The aspect ratio of the scaled game.
+ * @readonly
*/
this.aspectRatio = 0;
+ /**
+ * @property {number} sourceAspectRatio - The aspect ratio (width / height) of the original game dimensions.
+ * @readonly
+ */
+ this.sourceAspectRatio = width / height;
+
/**
* @property {any} event- The native browser events from full screen API changes.
*/
this.event = null;
+ /**
+ * @property {number} scaleMode - The current scaleMode.
+ */
+ this.scaleMode = Phaser.StageScaleMode.NO_SCALE;
+
+ /*
+ * @property {number} fullScreenScaleMode - Scale mode to be used in fullScreen
+ */
+ this.fullScreenScaleMode = Phaser.StageScaleMode.NO_SCALE;
+
+ /**
+ * @property {number} _startHeight - Internal cache var. Stage height when starting the game.
+ * @private
+ */
+ this._startHeight = 0;
+
+ /**
+ * @property {number} _width - Cached stage width for full screen mode.
+ * @private
+ */
+ this._width = 0;
+
+ /**
+ * @property {number} _height - Cached stage height for full screen mode.
+ * @private
+ */
+ this._height = 0;
+
var _this = this;
window.addEventListener('orientationchange', function (event) {
@@ -24268,10 +38866,10 @@ Phaser.StageScaleMode.prototype = {
if (this.isFullScreen)
{
- if (this.game.stage.fullScreenScaleMode === Phaser.StageScaleMode.EXACT_FIT)
+ if (this.fullScreenScaleMode === Phaser.StageScaleMode.EXACT_FIT)
{
- this.game.stage.canvas.style['width'] = '100%';
- this.game.stage.canvas.style['height'] = '100%';
+ this.game.canvas.style['width'] = '100%';
+ this.game.canvas.style['height'] = '100%';
this.setMaximum();
@@ -24281,16 +38879,16 @@ Phaser.StageScaleMode.prototype = {
this.scaleFactor.x = this.game.width / this.width;
this.scaleFactor.y = this.game.height / this.height;
}
- else if (this.game.stage.fullScreenScaleMode === Phaser.StageScaleMode.SHOW_ALL)
+ else if (this.fullScreenScaleMode === Phaser.StageScaleMode.SHOW_ALL)
{
- this.game.stage.scale.setShowAll();
- this.game.stage.scale.refresh();
+ this.setShowAll();
+ this.refresh();
}
}
else
{
- this.game.stage.canvas.style['width'] = this.game.width + 'px';
- this.game.stage.canvas.style['height'] = this.game.height + 'px';
+ this.game.canvas.style['width'] = this.game.width + 'px';
+ this.game.canvas.style['height'] = this.game.height + 'px';
this.width = this._width;
this.height = this._height;
@@ -24345,7 +38943,7 @@ Phaser.StageScaleMode.prototype = {
this.game.world.visible = true;
}
- this.game.stage._stage.addChild(this.orientationSprite);
+ this.game.stage.addChild(this.orientationSprite);
}
},
@@ -24415,7 +39013,7 @@ Phaser.StageScaleMode.prototype = {
this.enterPortrait.dispatch(this.orientation, false, true);
}
- if (this.game.stage.scaleMode !== Phaser.StageScaleMode.NO_SCALE)
+ if (this.scaleMode !== Phaser.StageScaleMode.NO_SCALE)
{
this.refresh();
}
@@ -24449,7 +39047,7 @@ Phaser.StageScaleMode.prototype = {
this.enterPortrait.dispatch(this.orientation, false, true);
}
- if (this.game.stage.scaleMode !== Phaser.StageScaleMode.NO_SCALE)
+ if (this.scaleMode !== Phaser.StageScaleMode.NO_SCALE)
{
this.refresh();
}
@@ -24528,22 +39126,22 @@ Phaser.StageScaleMode.prototype = {
}
else if (!this.isFullScreen)
{
- if (this.game.stage.scaleMode == Phaser.StageScaleMode.EXACT_FIT)
+ if (this.scaleMode == Phaser.StageScaleMode.EXACT_FIT)
{
this.setExactFit();
}
- else if (this.game.stage.scaleMode == Phaser.StageScaleMode.SHOW_ALL)
+ else if (this.scaleMode == Phaser.StageScaleMode.SHOW_ALL)
{
this.setShowAll();
}
}
else
{
- if (this.game.stage.fullScreenScaleMode == Phaser.StageScaleMode.EXACT_FIT)
+ if (this.fullScreenScaleMode == Phaser.StageScaleMode.EXACT_FIT)
{
this.setExactFit();
}
- else if (this.game.stage.fullScreenScaleMode == Phaser.StageScaleMode.SHOW_ALL)
+ else if (this.fullScreenScaleMode == Phaser.StageScaleMode.SHOW_ALL)
{
this.setShowAll();
}
@@ -24923,7 +39521,7 @@ Phaser.Device = function () {
this.ie = false;
/**
- * @property {number} ieVersion - If running in Internet Explorer this will contain the major version number.
+ * @property {number} ieVersion - If running in Internet Explorer this will contain the major version number. Beyond IE10 you should use Device.trident and Device.tridentVersion.
* @default
*/
this.ieVersion = 0;
@@ -25119,7 +39717,7 @@ Phaser.Device.prototype = {
*/
_checkFeatures: function () {
- this.canvas = !!window['CanvasRenderingContext2D'];
+ this.canvas = !!window['CanvasRenderingContext2D'] || this.iscocoonJS;
try {
this.localStorage = !!localStorage.getItem;
@@ -25208,11 +39806,12 @@ Phaser.Device.prototype = {
{
this.silk = true;
}
- else if (/Trident\/(\d+\.\d+);/.test(ua))
+ else if (/Trident\/(\d+\.\d+); rv:(\d+\.\d+)/.test(ua))
{
this.ie = true;
this.trident = true;
this.tridentVersion = parseInt(RegExp.$1, 10);
+ this.ieVersion = parseInt(RegExp.$2, 10);
}
// WebApp mode in iOS
@@ -25572,256 +40171,6 @@ Phaser.RequestAnimationFrame.prototype = {
Phaser.RequestAnimationFrame.prototype.constructor = Phaser.RequestAnimationFrame;
-/* jshint noempty: false */
-
-/**
-* @author Richard Davey
-* @copyright 2014 Photon Storm Ltd.
-* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
-*/
-
-/**
-* Phaser.RandomDataGenerator constructor.
-*
-* @class Phaser.RandomDataGenerator
-* @classdesc An extremely useful repeatable random data generator. Access it via Phaser.Game.rnd
-* Based on Nonsense by Josh Faul https://github.com/jocafa/Nonsense.
-* Random number generator from http://baagoe.org/en/wiki/Better_random_numbers_for_javascript
-*
-* @constructor
-* @param {array} seeds
-*/
-Phaser.RandomDataGenerator = function (seeds) {
-
- if (typeof seeds === "undefined") { seeds = []; }
-
- /**
- * @property {number} c - Internal var.
- * @private
- */
- this.c = 1;
-
- /**
- * @property {number} s0 - Internal var.
- * @private
- */
- this.s0 = 0;
-
- /**
- * @property {number} s1 - Internal var.
- * @private
- */
- this.s1 = 0;
-
- /**
- * @property {number} s2 - Internal var.
- * @private
- */
- this.s2 = 0;
-
- this.sow(seeds);
-
-};
-
-Phaser.RandomDataGenerator.prototype = {
-
- /**
- * Private random helper.
- * @method Phaser.RandomDataGenerator#rnd
- * @private
- * @return {number}
- */
- rnd: function () {
-
- var t = 2091639 * this.s0 + this.c * 2.3283064365386963e-10; // 2^-32
-
- this.c = t | 0;
- this.s0 = this.s1;
- this.s1 = this.s2;
- this.s2 = t - this.c;
-
- return this.s2;
- },
-
- /**
- * Reset the seed of the random data generator.
- *
- * @method Phaser.RandomDataGenerator#sow
- * @param {array} seeds
- */
- sow: function (seeds) {
-
- if (typeof seeds === "undefined") { seeds = []; }
-
- this.s0 = this.hash(' ');
- this.s1 = this.hash(this.s0);
- this.s2 = this.hash(this.s1);
- this.c = 1;
-
- var seed;
-
- for (var i = 0; seed = seeds[i++]; )
- {
- this.s0 -= this.hash(seed);
- this.s0 += ~~(this.s0 < 0);
- this.s1 -= this.hash(seed);
- this.s1 += ~~(this.s1 < 0);
- this.s2 -= this.hash(seed);
- this.s2 += ~~(this.s2 < 0);
- }
-
- },
-
- /**
- * Internal method that creates a seed hash.
- * @method Phaser.RandomDataGenerator#hash
- * @param {Any} data
- * @private
- * @return {number} hashed value.
- */
- hash: function (data) {
-
- var h, i, n;
- n = 0xefc8249d;
- data = data.toString();
-
- for (i = 0; i < data.length; i++) {
- n += data.charCodeAt(i);
- h = 0.02519603282416938 * n;
- n = h >>> 0;
- h -= n;
- h *= n;
- n = h >>> 0;
- h -= n;
- n += h * 0x100000000;// 2^32
- }
-
- return (n >>> 0) * 2.3283064365386963e-10;// 2^-32
-
- },
-
- /**
- * Returns a random integer between 0 and 2^32.
- * @method Phaser.RandomDataGenerator#integer
- * @return {number} A random integer between 0 and 2^32.
- */
- integer: function() {
- return this.rnd.apply(this) * 0x100000000;// 2^32
- },
-
- /**
- * Returns a random real number between 0 and 1.
- * @method Phaser.RandomDataGenerator#frac
- * @return {number} A random real number between 0 and 1.
- */
- frac: function() {
- return this.rnd.apply(this) + (this.rnd.apply(this) * 0x200000 | 0) * 1.1102230246251565e-16;// 2^-53
- },
-
- /**
- * Returns a random real number between 0 and 2^32.
- * @method Phaser.RandomDataGenerator#real
- * @return {number} A random real number between 0 and 2^32.
- */
- real: function() {
- return this.integer() + this.frac();
- },
-
- /**
- * Returns a random integer between min and max.
- * @method Phaser.RandomDataGenerator#integerInRange
- * @param {number} min - The minimum value in the range.
- * @param {number} max - The maximum value in the range.
- * @return {number} A random number between min and max.
- */
- integerInRange: function (min, max) {
- return Math.floor(this.realInRange(min, max));
- },
-
- /**
- * Returns a random real number between min and max.
- * @method Phaser.RandomDataGenerator#realInRange
- * @param {number} min - The minimum value in the range.
- * @param {number} max - The maximum value in the range.
- * @return {number} A random number between min and max.
- */
- realInRange: function (min, max) {
-
- return this.frac() * (max - min) + min;
-
- },
-
- /**
- * Returns a random real number between -1 and 1.
- * @method Phaser.RandomDataGenerator#normal
- * @return {number} A random real number between -1 and 1.
- */
- normal: function () {
- return 1 - 2 * this.frac();
- },
-
- /**
- * Returns a valid RFC4122 version4 ID hex string from https://gist.github.com/1308368
- * @method Phaser.RandomDataGenerator#uuid
- * @return {string} A valid RFC4122 version4 ID hex string
- */
- uuid: function () {
-
- var a = '';
- var b = '';
-
- for (b = a = ''; a++ < 36; b +=~a % 5 | a * 3&4 ? (a^15 ? 8^this.frac() * (a^20 ? 16 : 4) : 4).toString(16) : '-')
- {
- }
-
- return b;
-
- },
-
- /**
- * Returns a random member of `array`.
- * @method Phaser.RandomDataGenerator#pick
- * @param {Array} ary - An Array to pick a random member of.
- * @return {any} A random member of the array.
- */
- pick: function (ary) {
- return ary[this.integerInRange(0, ary.length)];
- },
-
- /**
- * Returns a random member of `array`, favoring the earlier entries.
- * @method Phaser.RandomDataGenerator#weightedPick
- * @param {Array} ary - An Array to pick a random member of.
- * @return {any} A random member of the array.
- */
- weightedPick: function (ary) {
- return ary[~~(Math.pow(this.frac(), 2) * ary.length)];
- },
-
- /**
- * Returns a random timestamp between min and max, or between the beginning of 2000 and the end of 2020 if min and max aren't specified.
- * @method Phaser.RandomDataGenerator#timestamp
- * @param {number} min - The minimum value in the range.
- * @param {number} max - The maximum value in the range.
- * @return {number} A random timestamp between min and max.
- */
- timestamp: function (min, max) {
- return this.realInRange(min || 946684800000, max || 1577862000000);
- },
-
- /**
- * Returns a random angle between -180 and 180.
- * @method Phaser.RandomDataGenerator#angle
- * @return {number} A random number between -180 and 180.
- */
- angle: function() {
- return this.integerInRange(-180, 180);
- }
-
-};
-
-Phaser.RandomDataGenerator.prototype.constructor = Phaser.RandomDataGenerator;
-
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -26160,7 +40509,18 @@ Phaser.Math = {
* @return {number}
*/
angleBetween: function (x1, y1, x2, y2) {
- return Math.atan2(y2 - y1, x2 - x1);
+ return Math.atan2(x2 - x1, y2 - y1);
+ },
+
+ /**
+ * Find the angle of a segment from (point1.x, point1.y) -> (point2.x, point2.y).
+ * @method Phaser.Math#angleBetweenPoints
+ * @param {Phaser.Point} point1
+ * @param {Phaser.Point} point2
+ * @return {number}
+ */
+ angleBetweenPoints: function (point1, point2) {
+ return Math.atan2(point2.x - point1.x, point2.y - point1.y);
},
/**
@@ -27104,6 +41464,28 @@ Phaser.Math = {
},
+ /**
+ * Convert p2 physics value (meters) to pixel scale.
+ *
+ * @method Phaser.Math#p2px
+ * @param {number} v - The value to convert.
+ * @return {number} The scaled value.
+ */
+ p2px: function (v) {
+ return v *= -20;
+ },
+
+ /**
+ * Convert pixel value to p2 physics scale (meters).
+ *
+ * @method Phaser.Math#px2p
+ * @param {number} v - The value to convert.
+ * @return {number} The scaled value.
+ */
+ px2p: function (v) {
+ return v * -0.05;
+ },
+
/**
* Convert degrees to radians.
*
@@ -27142,6 +41524,8 @@ Phaser.Math = {
};
+/* jshint noempty: false */
+
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -27149,1919 +41533,247 @@ Phaser.Math = {
*/
/**
-* Javascript QuadTree
-* @version 1.0
-* @author Timo Hausmann
-*
-* @version 1.2, September 4th 2013
-* @author Richard Davey
-* The original code was a conversion of the Java code posted to GameDevTuts. However I've tweaked
-* it massively to add node indexing, removed lots of temp. var creation and significantly
-* increased performance as a result.
-*
-* Original version at https://github.com/timohausmann/quadtree-js/
-*/
-
-/**
-* @copyright © 2012 Timo Hausmann
-*
-* Permission is hereby granted, free of charge, to any person obtaining
-* a copy of this software and associated documentation files (the
-* "Software"), to deal in the Software without restriction, including
-* without limitation the rights to use, copy, modify, merge, publish,
-* distribute, sublicense, and/or sell copies of the Software, and to
-* permit persons to whom the Software is furnished to do so, subject to
-* the following conditions:
-*
-* The above copyright notice and this permission notice shall be
-* included in all copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-/**
-* QuadTree Constructor
+* Phaser.RandomDataGenerator constructor.
+*
+* @class Phaser.RandomDataGenerator
+* @classdesc An extremely useful repeatable random data generator. Access it via Phaser.Game.rnd
+* Based on Nonsense by Josh Faul https://github.com/jocafa/Nonsense.
+* Random number generator from http://baagoe.org/en/wiki/Better_random_numbers_for_javascript
*
-* @class Phaser.QuadTree
-* @classdesc A QuadTree implementation. The original code was a conversion of the Java code posted to GameDevTuts.
-* However I've tweaked it massively to add node indexing, removed lots of temp. var creation and significantly increased performance as a result.
-* Original version at https://github.com/timohausmann/quadtree-js/
* @constructor
-* @param {number} x - The top left coordinate of the quadtree.
-* @param {number} y - The top left coordinate of the quadtree.
-* @param {number} width - The width of the quadtree in pixels.
-* @param {number} height - The height of the quadtree in pixels.
-* @param {number} [maxObjects=10] - The maximum number of objects per node.
-* @param {number} [maxLevels=4] - The maximum number of levels to iterate to.
-* @param {number} [level=0] - Which level is this?
+* @param {array} seeds
*/
-Phaser.QuadTree = function (x, y, width, height, maxObjects, maxLevels, level) {
-
- this.maxObjects = maxObjects || 10;
- this.maxLevels = maxLevels || 4;
- this.level = level || 0;
-
- this.bounds = {
- x: Math.round(x),
- y: Math.round(y),
- width: width,
- height: height,
- subWidth: Math.floor(width / 2),
- subHeight: Math.floor(height / 2),
- right: Math.round(x) + Math.floor(width / 2),
- bottom: Math.round(y) + Math.floor(height / 2)
- };
+Phaser.RandomDataGenerator = function (seeds) {
- this.objects = [];
- this.nodes = [];
-
-};
-
-Phaser.QuadTree.prototype = {
-
- /*
- * Populates this quadtree with the members of the given Group.
- *
- * @method Phaser.QuadTree#populate
- * @param {Phaser.Group} group - The Group to add to the quadtree.
- */
- populate: function (group) {
-
- group.forEach(this.populateHandler, this, true);
-
- },
-
- /*
- * Handler for the populate method.
- *
- * @method Phaser.QuadTree#populateHandler
- * @param {Phaser.Sprite} sprite - The Sprite to check.
- */
- populateHandler: function (sprite) {
-
- if (sprite.body && sprite.body.checkCollision.none === false && sprite.alive)
- {
- this.insert(sprite.body);
- }
-
- },
-
- /*
- * Split the node into 4 subnodes
- *
- * @method Phaser.QuadTree#split
- */
- split: function () {
-
- this.level++;
-
- // top right node
- this.nodes[0] = new Phaser.QuadTree(this.bounds.right, this.bounds.y, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level);
-
- // top left node
- this.nodes[1] = new Phaser.QuadTree(this.bounds.x, this.bounds.y, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level);
-
- // bottom left node
- this.nodes[2] = new Phaser.QuadTree(this.bounds.x, this.bounds.bottom, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level);
-
- // bottom right node
- this.nodes[3] = new Phaser.QuadTree(this.bounds.right, this.bounds.bottom, this.bounds.subWidth, this.bounds.subHeight, this.maxObjects, this.maxLevels, this.level);
-
- },
-
- /*
- * Insert the object into the node. If the node exceeds the capacity, it will split and add all objects to their corresponding subnodes.
- *
- * @method Phaser.QuadTree#insert
- * @param {Phaser.Physics.Arcade.Body|object} body - The Body object to insert into the quadtree.
- */
- insert: function (body) {
-
- var i = 0;
- var index;
-
- // if we have subnodes ...
- if (this.nodes[0] != null)
- {
- index = this.getIndex(body);
-
- if (index !== -1)
- {
- this.nodes[index].insert(body);
- return;
- }
- }
-
- this.objects.push(body);
-
- if (this.objects.length > this.maxObjects && this.level < this.maxLevels)
- {
- // Split if we don't already have subnodes
- if (this.nodes[0] == null)
- {
- this.split();
- }
-
- // Add objects to subnodes
- while (i < this.objects.length)
- {
- index = this.getIndex(this.objects[i]);
-
- if (index !== -1)
- {
- // this is expensive - see what we can do about it
- this.nodes[index].insert(this.objects.splice(i, 1)[0]);
- }
- else
- {
- i++;
- }
- }
- }
-
- },
-
- /*
- * Determine which node the object belongs to.
- *
- * @method Phaser.QuadTree#getIndex
- * @param {Phaser.Rectangle|object} rect - The bounds in which to check.
- * @return {number} index - Index of the subnode (0-3), or -1 if rect cannot completely fit within a subnode and is part of the parent node.
- */
- getIndex: function (rect) {
-
- // default is that rect doesn't fit, i.e. it straddles the internal quadrants
- var index = -1;
-
- if (rect.x < this.bounds.right && rect.right < this.bounds.right)
- {
- if ((rect.y < this.bounds.bottom && rect.bottom < this.bounds.bottom))
- {
- // rect fits within the top-left quadrant of this quadtree
- index = 1;
- }
- else if ((rect.y > this.bounds.bottom))
- {
- // rect fits within the bottom-left quadrant of this quadtree
- index = 2;
- }
- }
- else if (rect.x > this.bounds.right)
- {
- // rect can completely fit within the right quadrants
- if ((rect.y < this.bounds.bottom && rect.bottom < this.bounds.bottom))
- {
- // rect fits within the top-right quadrant of this quadtree
- index = 0;
- }
- else if ((rect.y > this.bounds.bottom))
- {
- // rect fits within the bottom-right quadrant of this quadtree
- index = 3;
- }
- }
-
- return index;
-
- },
-
- /*
- * Return all objects that could collide with the given Sprite.
- *
- * @method Phaser.QuadTree#retrieve
- * @param {Phaser.Sprite} sprite - The sprite to check against.
- * @return {array} - Array with all detected objects.
- */
- retrieve: function (sprite) {
-
- var returnObjects = this.objects;
-
- sprite.body.quadTreeIndex = this.getIndex(sprite.body);
-
- // Temp store for the node IDs this sprite is in, we can use this for fast elimination later
- // sprite.body.quadTreeIDs.push(this.ID);
-
- if (this.nodes[0])
- {
- // if rect fits into a subnode ..
- if (sprite.body.quadTreeIndex !== -1)
- {
- returnObjects = returnObjects.concat(this.nodes[sprite.body.quadTreeIndex].retrieve(sprite));
- }
- else
- {
- // if rect does not fit into a subnode, check it against all subnodes (unrolled for speed)
- returnObjects = returnObjects.concat(this.nodes[0].retrieve(sprite));
- returnObjects = returnObjects.concat(this.nodes[1].retrieve(sprite));
- returnObjects = returnObjects.concat(this.nodes[2].retrieve(sprite));
- returnObjects = returnObjects.concat(this.nodes[3].retrieve(sprite));
- }
- }
-
- return returnObjects;
-
- },
-
- /*
- * Clear the quadtree.
- * @method Phaser.QuadTree#clear
- */
- clear: function () {
-
- this.objects = [];
-
- for (var i = 0, len = this.nodes.length; i < len; i++)
- {
- if (this.nodes[i])
- {
- this.nodes[i].clear();
- delete this.nodes[i];
- }
- }
- }
-
-};
-
-Phaser.QuadTree.prototype.constructor = Phaser.QuadTree;
-
-/**
-* @author Richard Davey
-* @copyright 2014 Photon Storm Ltd.
-* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
-*/
-
-/**
-* Creates a new Circle object with the center coordinate specified by the x and y parameters and the diameter specified by the diameter parameter. If you call this function without parameters, a circle with x, y, diameter and radius properties set to 0 is created.
-* @class Circle
-* @classdesc Phaser - Circle
-* @constructor
-* @param {number} [x=0] - The x coordinate of the center of the circle.
-* @param {number} [y=0] - The y coordinate of the center of the circle.
-* @param {number} [diameter=0] - The diameter of the circle.
-* @return {Phaser.Circle} This circle object
-*/
-Phaser.Circle = function (x, y, diameter) {
-
- x = x || 0;
- y = y || 0;
- diameter = diameter || 0;
+ if (typeof seeds === "undefined") { seeds = []; }
/**
- * @property {number} x - The x coordinate of the center of the circle.
- */
- this.x = x;
-
- /**
- * @property {number} y - The y coordinate of the center of the circle.
- */
- this.y = y;
-
- /**
- * @property {number} _diameter - The diameter of the circle.
+ * @property {number} c - Internal var.
* @private
*/
- this._diameter = diameter;
+ this.c = 1;
- if (diameter > 0)
- {
- /**
- * @property {number} _radius - The radius of the circle.
- * @private
- */
- this._radius = diameter * 0.5;
- }
- else
- {
- this._radius = 0;
- }
+ /**
+ * @property {number} s0 - Internal var.
+ * @private
+ */
+ this.s0 = 0;
+
+ /**
+ * @property {number} s1 - Internal var.
+ * @private
+ */
+ this.s1 = 0;
+
+ /**
+ * @property {number} s2 - Internal var.
+ * @private
+ */
+ this.s2 = 0;
+
+ this.sow(seeds);
};
-Phaser.Circle.prototype = {
+Phaser.RandomDataGenerator.prototype = {
/**
- * The circumference of the circle.
- * @method Phaser.Circle#circumference
+ * Private random helper.
+ * @method Phaser.RandomDataGenerator#rnd
+ * @private
* @return {number}
*/
- circumference: function () {
- return 2 * (Math.PI * this._radius);
+ rnd: function () {
+
+ var t = 2091639 * this.s0 + this.c * 2.3283064365386963e-10; // 2^-32
+
+ this.c = t | 0;
+ this.s0 = this.s1;
+ this.s1 = this.s2;
+ this.s2 = t - this.c;
+
+ return this.s2;
},
/**
- * Sets the members of Circle to the specified values.
- * @method Phaser.Circle#setTo
- * @param {number} x - The x coordinate of the center of the circle.
- * @param {number} y - The y coordinate of the center of the circle.
- * @param {number} diameter - The diameter of the circle in pixels.
- * @return {Circle} This circle object.
+ * Reset the seed of the random data generator.
+ *
+ * @method Phaser.RandomDataGenerator#sow
+ * @param {array} seeds
*/
- setTo: function (x, y, diameter) {
- this.x = x;
- this.y = y;
- this._diameter = diameter;
- this._radius = diameter * 0.5;
- return this;
- },
+ sow: function (seeds) {
- /**
- * Copies the x, y and diameter properties from any given object to this Circle.
- * @method Phaser.Circle#copyFrom
- * @param {any} source - The object to copy from.
- * @return {Circle} This Circle object.
- */
- copyFrom: function (source) {
- return this.setTo(source.x, source.y, source.diameter);
- },
+ if (typeof seeds === "undefined") { seeds = []; }
- /**
- * Copies the x, y and diameter properties from this Circle to any given object.
- * @method Phaser.Circle#copyTo
- * @param {any} dest - The object to copy to.
- * @return {Object} This dest object.
- */
- copyTo: function(dest) {
- dest.x = this.x;
- dest.y = this.y;
- dest.diameter = this._diameter;
- return dest;
- },
+ this.s0 = this.hash(' ');
+ this.s1 = this.hash(this.s0);
+ this.s2 = this.hash(this.s1);
+ this.c = 1;
- /**
- * Returns the distance from the center of the Circle object to the given object
- * (can be Circle, Point or anything with x/y properties)
- * @method Phaser.Circle#distance
- * @param {object} dest - The target object. Must have visible x and y properties that represent the center of the object.
- * @param {boolean} [round] - Round the distance to the nearest integer (default false).
- * @return {number} The distance between this Point object and the destination Point object.
- */
- distance: function (dest, round) {
+ var seed;
- if (typeof round === "undefined") { round = false }
-
- if (round)
+ for (var i = 0; seed = seeds[i++]; )
{
- return Phaser.Math.distanceRound(this.x, this.y, dest.x, dest.y);
+ this.s0 -= this.hash(seed);
+ this.s0 += ~~(this.s0 < 0);
+ this.s1 -= this.hash(seed);
+ this.s1 += ~~(this.s1 < 0);
+ this.s2 -= this.hash(seed);
+ this.s2 += ~~(this.s2 < 0);
}
- else
- {
- return Phaser.Math.distance(this.x, this.y, dest.x, dest.y);
- }
-
- },
-
- /**
- * Returns a new Circle object with the same values for the x, y, width, and height properties as this Circle object.
- * @method Phaser.Circle#clone
- * @param {Phaser.Circle} out - Optional Circle object. If given the values will be set into the object, otherwise a brand new Circle object will be created and returned.
- * @return {Phaser.Circle} The cloned Circle object.
- */
- clone: function(out) {
-
- if (typeof out === "undefined") { out = new Phaser.Circle(); }
-
- return out.setTo(this.x, this.y, this.diameter);
-
- },
-
- /**
- * Return true if the given x/y coordinates are within this Circle object.
- * @method Phaser.Circle#contains
- * @param {number} x - The X value of the coordinate to test.
- * @param {number} y - The Y value of the coordinate to test.
- * @return {boolean} True if the coordinates are within this circle, otherwise false.
- */
- contains: function (x, y) {
- return Phaser.Circle.contains(this, x, y);
- },
-
- /**
- * Returns a Point object containing the coordinates of a point on the circumference of the Circle based on the given angle.
- * @method Phaser.Circle#circumferencePoint
- * @param {number} angle - The angle in radians (unless asDegrees is true) to return the point from.
- * @param {boolean} asDegrees - Is the given angle in radians (false) or degrees (true)?
- * @param {Phaser.Point} [out] - An optional Point object to put the result in to. If none specified a new Point object will be created.
- * @return {Phaser.Point} The Point object holding the result.
- */
- circumferencePoint: function (angle, asDegrees, out) {
- return Phaser.Circle.circumferencePoint(this, angle, asDegrees, out);
- },
-
- /**
- * Adjusts the location of the Circle object, as determined by its center coordinate, by the specified amounts.
- * @method Phaser.Circle#offset
- * @param {number} dx - Moves the x value of the Circle object by this amount.
- * @param {number} dy - Moves the y value of the Circle object by this amount.
- * @return {Circle} This Circle object.
- */
- offset: function (dx, dy) {
- this.x += dx;
- this.y += dy;
- return this;
- },
-
- /**
- * Adjusts the location of the Circle object using a Point object as a parameter. This method is similar to the Circle.offset() method, except that it takes a Point object as a parameter.
- * @method Phaser.Circle#offsetPoint
- * @param {Point} point A Point object to use to offset this Circle object (or any valid object with exposed x and y properties).
- * @return {Circle} This Circle object.
- */
- offsetPoint: function (point) {
- return this.offset(point.x, point.y);
- },
-
- /**
- * Returns a string representation of this object.
- * @method Phaser.Circle#toString
- * @return {string} a string representation of the instance.
- */
- toString: function () {
- return "[{Phaser.Circle (x=" + this.x + " y=" + this.y + " diameter=" + this.diameter + " radius=" + this.radius + ")}]";
- }
-
-};
-
-Phaser.Circle.prototype.constructor = Phaser.Circle;
-
-/**
-* The largest distance between any two points on the circle. The same as the radius * 2.
-* @name Phaser.Circle#diameter
-* @property {number} diameter - Gets or sets the diameter of the circle.
-*/
-Object.defineProperty(Phaser.Circle.prototype, "diameter", {
-
- get: function () {
- return this._diameter;
- },
-
- set: function (value) {
- if (value > 0) {
- this._diameter = value;
- this._radius = value * 0.5;
- }
- }
-
-});
-
-/**
-* The length of a line extending from the center of the circle to any point on the circle itself. The same as half the diameter.
-* @name Phaser.Circle#radius
-* @property {number} radius - Gets or sets the radius of the circle.
-*/
-Object.defineProperty(Phaser.Circle.prototype, "radius", {
-
- get: function () {
- return this._radius;
- },
-
- set: function (value) {
- if (value > 0) {
- this._radius = value;
- this._diameter = value * 2;
- }
- }
-
-});
-
-/**
-* The x coordinate of the leftmost point of the circle. Changing the left property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
-* @name Phaser.Circle#left
-* @propety {number} left - Gets or sets the value of the leftmost point of the circle.
-*/
-Object.defineProperty(Phaser.Circle.prototype, "left", {
-
- get: function () {
- return this.x - this._radius;
- },
-
- set: function (value) {
- if (value > this.x) {
- this._radius = 0;
- this._diameter = 0;
- } else {
- this.radius = this.x - value;
- }
- }
-
-});
-
-/**
-* The x coordinate of the rightmost point of the circle. Changing the right property of a Circle object has no effect on the x and y properties. However it does affect the diameter, whereas changing the x value does not affect the diameter property.
-* @name Phaser.Circle#right
-* @property {number} right - Gets or sets the value of the rightmost point of the circle.
-*/
-Object.defineProperty(Phaser.Circle.prototype, "right", {
-
- get: function () {
- return this.x + this._radius;
- },
-
- set: function (value) {
- if (value < this.x) {
- this._radius = 0;
- this._diameter = 0;
- } else {
- this.radius = value - this.x;
- }
- }
-
-});
-
-/**
-* The sum of the y minus the radius property. Changing the top property of a Circle object has no effect on the x and y properties, but does change the diameter.
-* @name Phaser.Circle#top
-* @property {number} top - Gets or sets the top of the circle.
-*/
-Object.defineProperty(Phaser.Circle.prototype, "top", {
-
- get: function () {
- return this.y - this._radius;
- },
-
- set: function (value) {
- if (value > this.y) {
- this._radius = 0;
- this._diameter = 0;
- } else {
- this.radius = this.y - value;
- }
- }
-
-});
-
-/**
-* The sum of the y and radius properties. Changing the bottom property of a Circle object has no effect on the x and y properties, but does change the diameter.
-* @name Phaser.Circle#bottom
-* @property {number} bottom - Gets or sets the bottom of the circle.
-*/
-Object.defineProperty(Phaser.Circle.prototype, "bottom", {
-
- get: function () {
- return this.y + this._radius;
- },
-
- set: function (value) {
-
- if (value < this.y) {
- this._radius = 0;
- this._diameter = 0;
- } else {
- this.radius = value - this.y;
- }
- }
-
-});
-
-/**
-* The area of this Circle.
-* @name Phaser.Circle#area
-* @property {number} area - The area of this circle.
-* @readonly
-*/
-Object.defineProperty(Phaser.Circle.prototype, "area", {
-
- get: function () {
- if (this._radius > 0) {
- return Math.PI * this._radius * this._radius;
- } else {
- return 0;
- }
- }
-
-});
-
-/**
-* Determines whether or not this Circle object is empty. Will return a value of true if the Circle objects diameter is less than or equal to 0; otherwise false.
-* If set to true it will reset all of the Circle objects properties to 0. A Circle object is empty if its diameter is less than or equal to 0.
-* @name Phaser.Circle#empty
-* @property {boolean} empty - Gets or sets the empty state of the circle.
-*/
-Object.defineProperty(Phaser.Circle.prototype, "empty", {
-
- get: function () {
- return (this._diameter === 0);
- },
-
- set: function (value) {
-
- if (value === true)
- {
- this.setTo(0, 0, 0);
- }
-
- }
-
-});
-
-/**
-* Return true if the given x/y coordinates are within the Circle object.
-* @method Phaser.Circle.contains
-* @param {Phaser.Circle} a - The Circle to be checked.
-* @param {number} x - The X value of the coordinate to test.
-* @param {number} y - The Y value of the coordinate to test.
-* @return {boolean} True if the coordinates are within this circle, otherwise false.
-*/
-Phaser.Circle.contains = function (a, x, y) {
-
- // Check if x/y are within the bounds first
- if (x >= a.left && x <= a.right && y >= a.top && y <= a.bottom) {
-
- var dx = (a.x - x) * (a.x - x);
- var dy = (a.y - y) * (a.y - y);
-
- return (dx + dy) <= (a.radius * a.radius);
-
- }
-
- return false;
-
-};
-
-/**
-* Determines whether the two Circle objects match. This method compares the x, y and diameter properties.
-* @method Phaser.Circle.equals
-* @param {Phaser.Circle} a - The first Circle object.
-* @param {Phaser.Circle} b - The second Circle object.
-* @return {boolean} A value of true if the object has exactly the same values for the x, y and diameter properties as this Circle object; otherwise false.
-*/
-Phaser.Circle.equals = function (a, b) {
- return (a.x == b.x && a.y == b.y && a.diameter == b.diameter);
-};
-
-/**
-* Determines whether the two Circle objects intersect.
-* This method checks the radius distances between the two Circle objects to see if they intersect.
-* @method Phaser.Circle.intersects
-* @param {Phaser.Circle} a - The first Circle object.
-* @param {Phaser.Circle} b - The second Circle object.
-* @return {boolean} A value of true if the specified object intersects with this Circle object; otherwise false.
-*/
-Phaser.Circle.intersects = function (a, b) {
- return (Phaser.Math.distance(a.x, a.y, b.x, b.y) <= (a.radius + b.radius));
-};
-
-/**
-* Returns a Point object containing the coordinates of a point on the circumference of the Circle based on the given angle.
-* @method Phaser.Circle.circumferencePoint
-* @param {Phaser.Circle} a - The first Circle object.
-* @param {number} angle - The angle in radians (unless asDegrees is true) to return the point from.
-* @param {boolean} asDegrees - Is the given angle in radians (false) or degrees (true)?
-* @param {Phaser.Point} [out] - An optional Point object to put the result in to. If none specified a new Point object will be created.
-* @return {Phaser.Point} The Point object holding the result.
-*/
-Phaser.Circle.circumferencePoint = function (a, angle, asDegrees, out) {
-
- if (typeof asDegrees === "undefined") { asDegrees = false; }
- if (typeof out === "undefined") { out = new Phaser.Point(); }
-
- if (asDegrees === true) {
- angle = Phaser.Math.radToDeg(angle);
- }
-
- out.x = a.x + a.radius * Math.cos(angle);
- out.y = a.y + a.radius * Math.sin(angle);
-
- return out;
-
-};
-
-/**
-* Checks if the given Circle and Rectangle objects intersect.
-* @method Phaser.Circle.intersectsRectangle
-* @param {Phaser.Circle} c - The Circle object to test.
-* @param {Phaser.Rectangle} r - The Rectangle object to test.
-* @return {boolean} True if the two objects intersect, otherwise false.
-*/
-Phaser.Circle.intersectsRectangle = function (c, r) {
-
- var cx = Math.abs(c.x - r.x - r.halfWidth);
- var xDist = r.halfWidth + c.radius;
-
- if (cx > xDist) {
- return false;
- }
-
- var cy = Math.abs(c.y - r.y - r.halfHeight);
- var yDist = r.halfHeight + c.radius;
-
- if (cy > yDist) {
- return false;
- }
-
- if (cx <= r.halfWidth || cy <= r.halfHeight) {
- return true;
- }
-
- var xCornerDist = cx - r.halfWidth;
- var yCornerDist = cy - r.halfHeight;
- var xCornerDistSq = xCornerDist * xCornerDist;
- var yCornerDistSq = yCornerDist * yCornerDist;
- var maxCornerDistSq = c.radius * c.radius;
-
- return xCornerDistSq + yCornerDistSq <= maxCornerDistSq;
-
-};
-
-/**
-* @author Richard Davey
-* @copyright 2014 Photon Storm Ltd.
-* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
-*/
-
-/**
-* Creates a new Point. If you pass no parameters a Point is created set to (0,0).
-* @class Phaser.Point
-* @classdesc The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis.
-* @constructor
-* @param {number} x The horizontal position of this Point (default 0)
-* @param {number} y The vertical position of this Point (default 0)
-*/
-Phaser.Point = function (x, y) {
-
- x = x || 0;
- y = y || 0;
-
- /**
- * @property {number} x - The x coordinate of the point.
- */
- this.x = x;
-
- /**
- * @property {number} y - The y coordinate of the point.
- */
- this.y = y;
-
-};
-
-Phaser.Point.prototype = {
-
- /**
- * Copies the x and y properties from any given object to this Point.
- * @method Phaser.Point#copyFrom
- * @param {any} source - The object to copy from.
- * @return {Point} This Point object.
- */
- copyFrom: function (source) {
- return this.setTo(source.x, source.y);
- },
-
- /**
- * Inverts the x and y values of this Point
- * @method Phaser.Point#invert
- * @return {Point} This Point object.
- */
- invert: function () {
- return this.setTo(this.y, this.x);
- },
-
- /**
- * Sets the x and y values of this Point object to the given coordinates.
- * @method Phaser.Point#setTo
- * @param {number} x - The horizontal position of this point.
- * @param {number} y - The vertical position of this point.
- * @return {Point} This Point object. Useful for chaining method calls.
- */
- setTo: function (x, y) {
-
- this.x = x;
- this.y = y;
- return this;
},
/**
- * Adds the given x and y values to this Point.
- * @method Phaser.Point#add
- * @param {number} x - The value to add to Point.x.
- * @param {number} y - The value to add to Point.y.
- * @return {Phaser.Point} This Point object. Useful for chaining method calls.
+ * Internal method that creates a seed hash.
+ * @method Phaser.RandomDataGenerator#hash
+ * @param {Any} data
+ * @private
+ * @return {number} hashed value.
*/
- add: function (x, y) {
+ hash: function (data) {
- this.x += x;
- this.y += y;
- return this;
+ var h, i, n;
+ n = 0xefc8249d;
+ data = data.toString();
- },
-
- /**
- * Subtracts the given x and y values from this Point.
- * @method Phaser.Point#subtract
- * @param {number} x - The value to subtract from Point.x.
- * @param {number} y - The value to subtract from Point.y.
- * @return {Phaser.Point} This Point object. Useful for chaining method calls.
- */
- subtract: function (x, y) {
-
- this.x -= x;
- this.y -= y;
- return this;
-
- },
-
- /**
- * Multiplies Point.x and Point.y by the given x and y values.
- * @method Phaser.Point#multiply
- * @param {number} x - The value to multiply Point.x by.
- * @param {number} y - The value to multiply Point.x by.
- * @return {Phaser.Point} This Point object. Useful for chaining method calls.
- */
- multiply: function (x, y) {
-
- this.x *= x;
- this.y *= y;
- return this;
-
- },
-
- /**
- * Divides Point.x and Point.y by the given x and y values.
- * @method Phaser.Point#divide
- * @param {number} x - The value to divide Point.x by.
- * @param {number} y - The value to divide Point.x by.
- * @return {Phaser.Point} This Point object. Useful for chaining method calls.
- */
- divide: function (x, y) {
-
- this.x /= x;
- this.y /= y;
- return this;
-
- },
-
- /**
- * Clamps the x value of this Point to be between the given min and max.
- * @method Phaser.Point#clampX
- * @param {number} min - The minimum value to clamp this Point to.
- * @param {number} max - The maximum value to clamp this Point to.
- * @return {Phaser.Point} This Point object.
- */
- clampX: function (min, max) {
-
- this.x = Phaser.Math.clamp(this.x, min, max);
- return this;
-
- },
-
- /**
- * Clamps the y value of this Point to be between the given min and max
- * @method Phaser.Point#clampY
- * @param {number} min - The minimum value to clamp this Point to.
- * @param {number} max - The maximum value to clamp this Point to.
- * @return {Phaser.Point} This Point object.
- */
- clampY: function (min, max) {
-
- this.y = Phaser.Math.clamp(this.y, min, max);
- return this;
-
- },
-
- /**
- * Clamps this Point object values to be between the given min and max.
- * @method Phaser.Point#clamp
- * @param {number} min - The minimum value to clamp this Point to.
- * @param {number} max - The maximum value to clamp this Point to.
- * @return {Phaser.Point} This Point object.
- */
- clamp: function (min, max) {
-
- this.x = Phaser.Math.clamp(this.x, min, max);
- this.y = Phaser.Math.clamp(this.y, min, max);
- return this;
-
- },
-
- /**
- * Creates a copy of the given Point.
- * @method Phaser.Point#clone
- * @param {Phaser.Point} [output] Optional Point object. If given the values will be set into this object, otherwise a brand new Point object will be created and returned.
- * @return {Phaser.Point} The new Point object.
- */
- clone: function (output) {
-
- if (typeof output === "undefined") { output = new Phaser.Point(); }
-
- return output.setTo(this.x, this.y);
-
- },
-
- /**
- * Copies the x and y properties from this Point to any given object.
- * @method Phaser.Point#copyTo
- * @param {any} dest - The object to copy to.
- * @return {Object} The dest object.
- */
- copyTo: function(dest) {
-
- dest.x = this.x;
- dest.y = this.y;
-
- return dest;
-
- },
-
- /**
- * Returns the distance of this Point object to the given object (can be a Circle, Point or anything with x/y properties)
- * @method Phaser.Point#distance
- * @param {object} dest - The target object. Must have visible x and y properties that represent the center of the object.
- * @param {boolean} [round] - Round the distance to the nearest integer (default false).
- * @return {number} The distance between this Point object and the destination Point object.
- */
- distance: function (dest, round) {
- return Phaser.Point.distance(this, dest, round);
- },
-
- /**
- * Determines whether the given objects x/y values are equal to this Point object.
- * @method Phaser.Point#equals
- * @param {Phaser.Point} a - The first object to compare.
- * @return {boolean} A value of true if the Points are equal, otherwise false.
- */
- equals: function (a) {
- return (a.x == this.x && a.y == this.y);
- },
-
- /**
- * Rotates this Point around the x/y coordinates given to the desired angle.
- * @method Phaser.Point#rotate
- * @param {number} x - The x coordinate of the anchor point
- * @param {number} y - The y coordinate of the anchor point
- * @param {number} angle - The angle in radians (unless asDegrees is true) to rotate the Point to.
- * @param {boolean} asDegrees - Is the given rotation in radians (false) or degrees (true)?
- * @param {number} [distance] - An optional distance constraint between the Point and the anchor.
- * @return {Phaser.Point} The modified point object.
- */
- rotate: function (x, y, angle, asDegrees, distance) {
- return Phaser.Point.rotate(this, x, y, angle, asDegrees, distance);
- },
-
- /**
- * Calculates the length of the vector
- * @method Phaser.Point#getMagnitude
- * @return {number} the length of the vector
- */
- getMagnitude: function() {
- return Math.sqrt((this.x * this.x) + (this.y * this.y));
- },
-
- /**
- * Alters the length of the vector without changing the direction
- * @method Phaser.Point#getMagnitude
- * @param {number} magnitude the desired magnitude of the resulting vector
- * @return {Phaser.Point} the modified original vector
- */
- setMagnitude: function(magnitude) {
- return this.normalize().multiply(magnitude, magnitude);
- },
-
- /**
- * Alters the vector so that its length is 1, but it retains the same direction
- * @method Phaser.Point#normalize
- * @return {Phaser.Point} the modified original vector
- */
- normalize: function() {
-
- if(!this.isZero()) {
- var m = this.getMagnitude();
- this.x /= m;
- this.y /= m;
+ for (i = 0; i < data.length; i++) {
+ n += data.charCodeAt(i);
+ h = 0.02519603282416938 * n;
+ n = h >>> 0;
+ h -= n;
+ h *= n;
+ n = h >>> 0;
+ h -= n;
+ n += h * 0x100000000;// 2^32
}
- return this;
+ return (n >>> 0) * 2.3283064365386963e-10;// 2^-32
},
/**
- * Determine if this point is at 0,0
- * @method Phaser.Point#isZero
- * @return {boolean} True if this Point is 0,0, otherwise false
+ * Returns a random integer between 0 and 2^32.
+ * @method Phaser.RandomDataGenerator#integer
+ * @return {number} A random integer between 0 and 2^32.
*/
- isZero: function() {
- return (this.x === 0 && this.y === 0);
+ integer: function() {
+ return this.rnd.apply(this) * 0x100000000;// 2^32
},
/**
- * Returns a string representation of this object.
- * @method Phaser.Point#toString
- * @return {string} A string representation of the instance.
+ * Returns a random real number between 0 and 1.
+ * @method Phaser.RandomDataGenerator#frac
+ * @return {number} A random real number between 0 and 1.
*/
- toString: function () {
- return '[{Point (x=' + this.x + ' y=' + this.y + ')}]';
- }
-
-};
-
-Phaser.Point.prototype.constructor = Phaser.Point;
-
-/**
-* Adds the coordinates of two points together to create a new point.
-* @method Phaser.Point.add
-* @param {Phaser.Point} a - The first Point object.
-* @param {Phaser.Point} b - The second Point object.
-* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
-* @return {Phaser.Point} The new Point object.
-*/
-Phaser.Point.add = function (a, b, out) {
-
- if (typeof out === "undefined") { out = new Phaser.Point(); }
-
- out.x = a.x + b.x;
- out.y = a.y + b.y;
-
- return out;
-
-};
-
-/**
-* Subtracts the coordinates of two points to create a new point.
-* @method Phaser.Point.subtract
-* @param {Phaser.Point} a - The first Point object.
-* @param {Phaser.Point} b - The second Point object.
-* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
-* @return {Phaser.Point} The new Point object.
-*/
-Phaser.Point.subtract = function (a, b, out) {
-
- if (typeof out === "undefined") { out = new Phaser.Point(); }
-
- out.x = a.x - b.x;
- out.y = a.y - b.y;
-
- return out;
-
-};
-
-/**
-* Multiplies the coordinates of two points to create a new point.
-* @method Phaser.Point.multiply
-* @param {Phaser.Point} a - The first Point object.
-* @param {Phaser.Point} b - The second Point object.
-* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
-* @return {Phaser.Point} The new Point object.
-*/
-Phaser.Point.multiply = function (a, b, out) {
-
- if (typeof out === "undefined") { out = new Phaser.Point(); }
-
- out.x = a.x * b.x;
- out.y = a.y * b.y;
-
- return out;
-
-};
-
-/**
-* Divides the coordinates of two points to create a new point.
-* @method Phaser.Point.divide
-* @param {Phaser.Point} a - The first Point object.
-* @param {Phaser.Point} b - The second Point object.
-* @param {Phaser.Point} [out] - Optional Point to store the value in, if not supplied a new Point object will be created.
-* @return {Phaser.Point} The new Point object.
-*/
-Phaser.Point.divide = function (a, b, out) {
-
- if (typeof out === "undefined") { out = new Phaser.Point(); }
-
- out.x = a.x / b.x;
- out.y = a.y / b.y;
-
- return out;
-
-};
-
-/**
-* Determines whether the two given Point objects are equal. They are considered equal if they have the same x and y values.
-* @method Phaser.Point.equals
-* @param {Phaser.Point} a - The first Point object.
-* @param {Phaser.Point} b - The second Point object.
-* @return {boolean} A value of true if the Points are equal, otherwise false.
-*/
-Phaser.Point.equals = function (a, b) {
- return (a.x == b.x && a.y == b.y);
-};
-
-/**
-* Returns the distance of this Point object to the given object (can be a Circle, Point or anything with x/y properties).
-* @method Phaser.Point.distance
-* @param {object} a - The target object. Must have visible x and y properties that represent the center of the object.
-* @param {object} b - The target object. Must have visible x and y properties that represent the center of the object.
-* @param {boolean} [round] - Round the distance to the nearest integer (default false).
-* @return {number} The distance between this Point object and the destination Point object.
-*/
-Phaser.Point.distance = function (a, b, round) {
-
- if (typeof round === "undefined") { round = false }
-
- if (round)
- {
- return Phaser.Math.distanceRound(a.x, a.y, b.x, b.y);
- }
- else
- {
- return Phaser.Math.distance(a.x, a.y, b.x, b.y);
- }
-
-};
-
-/**
-* Rotates a Point around the x/y coordinates given to the desired angle.
-* @method Phaser.Point.rotate
-* @param {Phaser.Point} a - The Point object to rotate.
-* @param {number} x - The x coordinate of the anchor point
-* @param {number} y - The y coordinate of the anchor point
-* @param {number} angle - The angle in radians (unless asDegrees is true) to rotate the Point to.
-* @param {boolean} asDegrees - Is the given rotation in radians (false) or degrees (true)?
-* @param {number} distance - An optional distance constraint between the Point and the anchor.
-* @return {Phaser.Point} The modified point object.
-*/
-Phaser.Point.rotate = function (a, x, y, angle, asDegrees, distance) {
-
- asDegrees = asDegrees || false;
- distance = distance || null;
-
- if (asDegrees)
- {
- angle = Phaser.Math.degToRad(angle);
- }
-
- // Get distance from origin (cx/cy) to this point
- if (distance === null)
- {
- distance = Math.sqrt(((x - a.x) * (x - a.x)) + ((y - a.y) * (y - a.y)));
- }
-
- return a.setTo(x + distance * Math.cos(angle), y + distance * Math.sin(angle));
-
-};
-
-/**
-* @author Richard Davey
-* @copyright 2014 Photon Storm Ltd.
-* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
-*/
-
-/**
-* Creates a new Rectangle object with the top-left corner specified by the x and y parameters and with the specified width and height parameters. If you call this function without parameters, a Rectangle with x, y, width, and height properties set to 0 is created.
-*
-* @class Phaser.Rectangle
-* @constructor
-* @param {number} x - The x coordinate of the top-left corner of the Rectangle.
-* @param {number} y - The y coordinate of the top-left corner of the Rectangle.
-* @param {number} width - The width of the Rectangle.
-* @param {number} height - The height of the Rectangle.
-* @return {Rectangle} This Rectangle object.
-*/
-Phaser.Rectangle = function (x, y, width, height) {
-
- x = x || 0;
- y = y || 0;
- width = width || 0;
- height = height || 0;
-
- /**
- * @property {number} x - The x coordinate of the top-left corner of the Rectangle.
- */
- this.x = x;
-
- /**
- * @property {number} y - The y coordinate of the top-left corner of the Rectangle.
- */
- this.y = y;
-
- /**
- * @property {number} width - The width of the Rectangle.
- */
- this.width = width;
-
- /**
- * @property {number} height - The height of the Rectangle.
- */
- this.height = height;
-
-};
-
-Phaser.Rectangle.prototype = {
-
- /**
- * Adjusts the location of the Rectangle object, as determined by its top-left corner, by the specified amounts.
- * @method Phaser.Rectangle#offset
- * @param {number} dx - Moves the x value of the Rectangle object by this amount.
- * @param {number} dy - Moves the y value of the Rectangle object by this amount.
- * @return {Rectangle} This Rectangle object.
- */
- offset: function (dx, dy) {
-
- this.x += dx;
- this.y += dy;
-
- return this;
-
+ frac: function() {
+ return this.rnd.apply(this) + (this.rnd.apply(this) * 0x200000 | 0) * 1.1102230246251565e-16;// 2^-53
},
-
+
/**
- * Adjusts the location of the Rectangle object using a Point object as a parameter. This method is similar to the Rectangle.offset() method, except that it takes a Point object as a parameter.
- * @method Phaser.Rectangle#offsetPoint
- * @param {Point} point - A Point object to use to offset this Rectangle object.
- * @return {Rectangle} This Rectangle object.
+ * Returns a random real number between 0 and 2^32.
+ * @method Phaser.RandomDataGenerator#real
+ * @return {number} A random real number between 0 and 2^32.
*/
- offsetPoint: function (point) {
- return this.offset(point.x, point.y);
+ real: function() {
+ return this.integer() + this.frac();
},
-
+
/**
- * Sets the members of Rectangle to the specified values.
- * @method Phaser.Rectangle#setTo
- * @param {number} x - The x coordinate of the top-left corner of the Rectangle.
- * @param {number} y - The y coordinate of the top-left corner of the Rectangle.
- * @param {number} width - The width of the Rectangle in pixels.
- * @param {number} height - The height of the Rectangle in pixels.
- * @return {Rectangle} This Rectangle object
+ * Returns a random integer between min and max.
+ * @method Phaser.RandomDataGenerator#integerInRange
+ * @param {number} min - The minimum value in the range.
+ * @param {number} max - The maximum value in the range.
+ * @return {number} A random number between min and max.
*/
- setTo: function (x, y, width, height) {
-
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
-
- return this;
-
+ integerInRange: function (min, max) {
+ return Math.floor(this.realInRange(min, max));
},
-
+
/**
- * Runs Math.floor() on both the x and y values of this Rectangle.
- * @method Phaser.Rectangle#floor
+ * Returns a random real number between min and max.
+ * @method Phaser.RandomDataGenerator#realInRange
+ * @param {number} min - The minimum value in the range.
+ * @param {number} max - The maximum value in the range.
+ * @return {number} A random number between min and max.
*/
- floor: function () {
+ realInRange: function (min, max) {
- this.x = Math.floor(this.x);
- this.y = Math.floor(this.y);
-
- },
-
- /**
- * Runs Math.floor() on the x, y, width and height values of this Rectangle.
- * @method Phaser.Rectangle#floorAll
- */
- floorAll: function () {
-
- this.x = Math.floor(this.x);
- this.y = Math.floor(this.y);
- this.width = Math.floor(this.width);
- this.height = Math.floor(this.height);
+ return this.frac() * (max - min) + min;
},
/**
- * Copies the x, y, width and height properties from any given object to this Rectangle.
- * @method Phaser.Rectangle#copyFrom
- * @param {any} source - The object to copy from.
- * @return {Rectangle} This Rectangle object.
+ * Returns a random real number between -1 and 1.
+ * @method Phaser.RandomDataGenerator#normal
+ * @return {number} A random real number between -1 and 1.
*/
- copyFrom: function (source) {
- return this.setTo(source.x, source.y, source.width, source.height);
+ normal: function () {
+ return 1 - 2 * this.frac();
},
/**
- * Copies the x, y, width and height properties from this Rectangle to any given object.
- * @method Phaser.Rectangle#copyTo
- * @param {any} source - The object to copy to.
- * @return {object} This object.
+ * Returns a valid RFC4122 version4 ID hex string from https://gist.github.com/1308368
+ * @method Phaser.RandomDataGenerator#uuid
+ * @return {string} A valid RFC4122 version4 ID hex string
*/
- copyTo: function (dest) {
+ uuid: function () {
- dest.x = this.x;
- dest.y = this.y;
- dest.width = this.width;
- dest.height = this.height;
+ var a = '';
+ var b = '';
- return dest;
-
- },
-
- /**
- * Increases the size of the Rectangle object by the specified amounts. The center point of the Rectangle object stays the same, and its size increases to the left and right by the dx value, and to the top and the bottom by the dy value.
- * @method Phaser.Rectangle#inflate
- * @param {number} dx - The amount to be added to the left side of the Rectangle.
- * @param {number} dy - The amount to be added to the bottom side of the Rectangle.
- * @return {Phaser.Rectangle} This Rectangle object.
- */
- inflate: function (dx, dy) {
- return Phaser.Rectangle.inflate(this, dx, dy);
- },
-
- /**
- * The size of the Rectangle object, expressed as a Point object with the values of the width and height properties.
- * @method Phaser.Rectangle#size
- * @param {Phaser.Point} [output] - Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
- * @return {Phaser.Point} The size of the Rectangle object.
- */
- size: function (output) {
- return Phaser.Rectangle.size(this, output);
- },
-
- /**
- * Returns a new Rectangle object with the same values for the x, y, width, and height properties as the original Rectangle object.
- * @method Phaser.Rectangle#clone
- * @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
- * @return {Phaser.Rectangle}
- */
- clone: function (output) {
- return Phaser.Rectangle.clone(this, output);
- },
-
- /**
- * Determines whether the specified coordinates are contained within the region defined by this Rectangle object.
- * @method Phaser.Rectangle#contains
- * @param {number} x - The x coordinate of the point to test.
- * @param {number} y - The y coordinate of the point to test.
- * @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
- */
- contains: function (x, y) {
- return Phaser.Rectangle.contains(this, x, y);
- },
-
- /**
- * Determines whether the first Rectangle object is fully contained within the second Rectangle object.
- * A Rectangle object is said to contain another if the second Rectangle object falls entirely within the boundaries of the first.
- * @method Phaser.Rectangle#containsRect
- * @param {Phaser.Rectangle} b - The second Rectangle object.
- * @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
- */
- containsRect: function (b) {
- return Phaser.Rectangle.containsRect(this, b);
- },
-
- /**
- * Determines whether the two Rectangles are equal.
- * This method compares the x, y, width and height properties of each Rectangle.
- * @method Phaser.Rectangle#equals
- * @param {Phaser.Rectangle} b - The second Rectangle object.
- * @return {boolean} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
- */
- equals: function (b) {
- return Phaser.Rectangle.equals(this, b);
- },
-
- /**
- * If the Rectangle object specified in the toIntersect parameter intersects with this Rectangle object, returns the area of intersection as a Rectangle object. If the Rectangles do not intersect, this method returns an empty Rectangle object with its properties set to 0.
- * @method Phaser.Rectangle#intersection
- * @param {Phaser.Rectangle} b - The second Rectangle object.
- * @param {Phaser.Rectangle} out - Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
- * @return {Phaser.Rectangle} A Rectangle object that equals the area of intersection. If the Rectangles do not intersect, this method returns an empty Rectangle object; that is, a Rectangle with its x, y, width, and height properties set to 0.
- */
- intersection: function (b, out) {
- return Phaser.Rectangle.intersection(this, b, out);
- },
-
- /**
- * Determines whether the two Rectangles intersect with each other.
- * This method checks the x, y, width, and height properties of the Rectangles.
- * @method Phaser.Rectangle#intersects
- * @param {Phaser.Rectangle} b - The second Rectangle object.
- * @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0.
- * @return {boolean} A value of true if the specified object intersects with this Rectangle object; otherwise false.
- */
- intersects: function (b, tolerance) {
- return Phaser.Rectangle.intersects(this, b, tolerance);
- },
-
- /**
- * Determines whether the object specified intersects (overlaps) with the given values.
- * @method Phaser.Rectangle#intersectsRaw
- * @param {number} left - Description.
- * @param {number} right - Description.
- * @param {number} top - Description.
- * @param {number} bottomt - Description.
- * @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0
- * @return {boolean} A value of true if the specified object intersects with the Rectangle; otherwise false.
- */
- intersectsRaw: function (left, right, top, bottom, tolerance) {
- return Phaser.Rectangle.intersectsRaw(this, left, right, top, bottom, tolerance);
- },
-
- /**
- * Adds two Rectangles together to create a new Rectangle object, by filling in the horizontal and vertical space between the two Rectangles.
- * @method Phaser.Rectangle#union
- * @param {Phaser.Rectangle} b - The second Rectangle object.
- * @param {Phaser.Rectangle} [out] - Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
- * @return {Phaser.Rectangle} A Rectangle object that is the union of the two Rectangles.
- */
- union: function (b, out) {
- return Phaser.Rectangle.union(this, b, out);
- },
-
- /**
- * Returns a string representation of this object.
- * @method Phaser.Rectangle#toString
- * @return {string} A string representation of the instance.
- */
- toString: function () {
- return "[{Rectangle (x=" + this.x + " y=" + this.y + " width=" + this.width + " height=" + this.height + " empty=" + this.empty + ")}]";
- }
-
-};
-
-Phaser.Rectangle.prototype.constructor = Phaser.Rectangle;
-
-/**
-* @name Phaser.Rectangle#halfWidth
-* @property {number} halfWidth - Half of the width of the Rectangle.
-* @readonly
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "halfWidth", {
-
- get: function () {
- return Math.round(this.width / 2);
- }
-
-});
-
-/**
-* @name Phaser.Rectangle#halfHeight
-* @property {number} halfHeight - Half of the height of the Rectangle.
-* @readonly
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "halfHeight", {
-
- get: function () {
- return Math.round(this.height / 2);
- }
-
-});
-
-/**
-* The sum of the y and height properties. Changing the bottom property of a Rectangle object has no effect on the x, y and width properties, but does change the height property.
-* @name Phaser.Rectangle#bottom
-* @property {number} bottom - The sum of the y and height properties.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "bottom", {
-
- get: function () {
- return this.y + this.height;
- },
-
- set: function (value) {
- if (value <= this.y) {
- this.height = 0;
- } else {
- this.height = (this.y - value);
- }
- }
-
-});
-
-/**
-* The location of the Rectangles bottom right corner as a Point object.
-* @name Phaser.Rectangle#bottom
-* @property {Phaser.Point} bottomRight - Gets or sets the location of the Rectangles bottom right corner as a Point object.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "bottomRight", {
-
- get: function () {
- return new Phaser.Point(this.right, this.bottom);
- },
-
- set: function (value) {
- this.right = value.x;
- this.bottom = value.y;
- }
-
-});
-
-/**
-* The x coordinate of the left of the Rectangle. Changing the left property of a Rectangle object has no effect on the y and height properties. However it does affect the width property, whereas changing the x value does not affect the width property.
-* @name Phaser.Rectangle#left
-* @property {number} left - The x coordinate of the left of the Rectangle.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "left", {
-
- get: function () {
- return this.x;
- },
-
- set: function (value) {
- if (value >= this.right) {
- this.width = 0;
- } else {
- this.width = this.right - value;
- }
- this.x = value;
- }
-
-});
-
-/**
-* The sum of the x and width properties. Changing the right property of a Rectangle object has no effect on the x, y and height properties, however it does affect the width property.
-* @name Phaser.Rectangle#right
-* @property {number} right - The sum of the x and width properties.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "right", {
-
- get: function () {
- return this.x + this.width;
- },
-
- set: function (value) {
- if (value <= this.x) {
- this.width = 0;
- } else {
- this.width = this.x + value;
- }
- }
-
-});
-
-/**
-* The volume of the Rectangle derived from width * height.
-* @name Phaser.Rectangle#volume
-* @property {number} volume - The volume of the Rectangle derived from width * height.
-* @readonly
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "volume", {
-
- get: function () {
- return this.width * this.height;
- }
-
-});
-
-/**
-* The perimeter size of the Rectangle. This is the sum of all 4 sides.
-* @name Phaser.Rectangle#perimeter
-* @property {number} perimeter - The perimeter size of the Rectangle. This is the sum of all 4 sides.
-* @readonly
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "perimeter", {
-
- get: function () {
- return (this.width * 2) + (this.height * 2);
- }
-
-});
-
-/**
-* The x coordinate of the center of the Rectangle.
-* @name Phaser.Rectangle#centerX
-* @property {number} centerX - The x coordinate of the center of the Rectangle.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "centerX", {
-
- get: function () {
- return this.x + this.halfWidth;
- },
-
- set: function (value) {
- this.x = value - this.halfWidth;
- }
-
-});
-
-/**
-* The y coordinate of the center of the Rectangle.
-* @name Phaser.Rectangle#centerY
-* @property {number} centerY - The y coordinate of the center of the Rectangle.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "centerY", {
-
- get: function () {
- return this.y + this.halfHeight;
- },
-
- set: function (value) {
- this.y = value - this.halfHeight;
- }
-
-});
-
-/**
-* The y coordinate of the top of the Rectangle. Changing the top property of a Rectangle object has no effect on the x and width properties.
-* However it does affect the height property, whereas changing the y value does not affect the height property.
-* @name Phaser.Rectangle#top
-* @property {number} top - The y coordinate of the top of the Rectangle.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "top", {
-
- get: function () {
- return this.y;
- },
-
- set: function (value) {
- if (value >= this.bottom) {
- this.height = 0;
- this.y = value;
- } else {
- this.height = (this.bottom - value);
- }
- }
-
-});
-
-/**
-* The location of the Rectangles top left corner as a Point object.
-* @name Phaser.Rectangle#topLeft
-* @property {Phaser.Point} topLeft - The location of the Rectangles top left corner as a Point object.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "topLeft", {
-
- get: function () {
- return new Phaser.Point(this.x, this.y);
- },
-
- set: function (value) {
- this.x = value.x;
- this.y = value.y;
- }
-
-});
-
-/**
-* Determines whether or not this Rectangle object is empty. A Rectangle object is empty if its width or height is less than or equal to 0.
-* If set to true then all of the Rectangle properties are set to 0.
-* @name Phaser.Rectangle#empty
-* @property {boolean} empty - Gets or sets the Rectangles empty state.
-*/
-Object.defineProperty(Phaser.Rectangle.prototype, "empty", {
-
- get: function () {
- return (!this.width || !this.height);
- },
-
- set: function (value) {
-
- if (value === true)
+ for (b = a = ''; a++ < 36; b +=~a % 5 | a * 3&4 ? (a^15 ? 8^this.frac() * (a^20 ? 16 : 4) : 4).toString(16) : '-')
{
- this.setTo(0, 0, 0, 0);
}
-
- }
-});
+ return b;
-/**
-* Increases the size of the Rectangle object by the specified amounts. The center point of the Rectangle object stays the same, and its size increases to the left and right by the dx value, and to the top and the bottom by the dy value.
-* @method Phaser.Rectangle.inflate
-* @param {Phaser.Rectangle} a - The Rectangle object.
-* @param {number} dx - The amount to be added to the left side of the Rectangle.
-* @param {number} dy - The amount to be added to the bottom side of the Rectangle.
-* @return {Phaser.Rectangle} This Rectangle object.
-*/
-Phaser.Rectangle.inflate = function (a, dx, dy) {
- a.x -= dx;
- a.width += 2 * dx;
- a.y -= dy;
- a.height += 2 * dy;
- return a;
-};
-
-/**
-* Increases the size of the Rectangle object. This method is similar to the Rectangle.inflate() method except it takes a Point object as a parameter.
-* @method Phaser.Rectangle.inflatePoint
-* @param {Phaser.Rectangle} a - The Rectangle object.
-* @param {Phaser.Point} point - The x property of this Point object is used to increase the horizontal dimension of the Rectangle object. The y property is used to increase the vertical dimension of the Rectangle object.
-* @return {Phaser.Rectangle} The Rectangle object.
-*/
-Phaser.Rectangle.inflatePoint = function (a, point) {
- return Phaser.Rectangle.inflate(a, point.x, point.y);
-};
-
-/**
-* The size of the Rectangle object, expressed as a Point object with the values of the width and height properties.
-* @method Phaser.Rectangle.size
-* @param {Phaser.Rectangle} a - The Rectangle object.
-* @param {Phaser.Point} [output] - Optional Point object. If given the values will be set into the object, otherwise a brand new Point object will be created and returned.
-* @return {Phaser.Point} The size of the Rectangle object
-*/
-Phaser.Rectangle.size = function (a, output) {
- if (typeof output === "undefined") { output = new Phaser.Point(); }
- return output.setTo(a.width, a.height);
-};
-
-/**
-* Returns a new Rectangle object with the same values for the x, y, width, and height properties as the original Rectangle object.
-* @method Phaser.Rectangle.clone
-* @param {Phaser.Rectangle} a - The Rectangle object.
-* @param {Phaser.Rectangle} [output] - Optional Rectangle object. If given the values will be set into the object, otherwise a brand new Rectangle object will be created and returned.
-* @return {Phaser.Rectangle}
-*/
-Phaser.Rectangle.clone = function (a, output) {
- if (typeof output === "undefined") { output = new Phaser.Rectangle(); }
- return output.setTo(a.x, a.y, a.width, a.height);
-};
-
-/**
-* Determines whether the specified coordinates are contained within the region defined by this Rectangle object.
-* @method Phaser.Rectangle.contains
-* @param {Phaser.Rectangle} a - The Rectangle object.
-* @param {number} x - The x coordinate of the point to test.
-* @param {number} y - The y coordinate of the point to test.
-* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
-*/
-Phaser.Rectangle.contains = function (a, x, y) {
- return (x >= a.x && x <= a.right && y >= a.y && y <= a.bottom);
-};
-
-Phaser.Rectangle.containsRaw = function (rx, ry, rw, rh, x, y) {
- return (x >= rx && x <= (rx + rw) && y >= ry && y <= (ry + rh));
-};
-
-/**
-* Determines whether the specified point is contained within the rectangular region defined by this Rectangle object. This method is similar to the Rectangle.contains() method, except that it takes a Point object as a parameter.
-* @method Phaser.Rectangle.containsPoint
-* @param {Phaser.Rectangle} a - The Rectangle object.
-* @param {Phaser.Point} point - The point object being checked. Can be Point or any object with .x and .y values.
-* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
-*/
-Phaser.Rectangle.containsPoint = function (a, point) {
- return Phaser.Rectangle.contains(a, point.x, point.y);
-};
-
-/**
-* Determines whether the first Rectangle object is fully contained within the second Rectangle object.
-* A Rectangle object is said to contain another if the second Rectangle object falls entirely within the boundaries of the first.
-* @method Phaser.Rectangle.containsRect
-* @param {Phaser.Rectangle} a - The first Rectangle object.
-* @param {Phaser.Rectangle} b - The second Rectangle object.
-* @return {boolean} A value of true if the Rectangle object contains the specified point; otherwise false.
-*/
-Phaser.Rectangle.containsRect = function (a, b) {
-
- // If the given rect has a larger volume than this one then it can never contain it
- if (a.volume > b.volume)
- {
- return false;
- }
-
- return (a.x >= b.x && a.y >= b.y && a.right <= b.right && a.bottom <= b.bottom);
-
-};
-
-/**
-* Determines whether the two Rectangles are equal.
-* This method compares the x, y, width and height properties of each Rectangle.
-* @method Phaser.Rectangle.equals
-* @param {Phaser.Rectangle} a - The first Rectangle object.
-* @param {Phaser.Rectangle} b - The second Rectangle object.
-* @return {boolean} A value of true if the two Rectangles have exactly the same values for the x, y, width and height properties; otherwise false.
-*/
-Phaser.Rectangle.equals = function (a, b) {
- return (a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height);
-};
-
-/**
-* If the Rectangle object specified in the toIntersect parameter intersects with this Rectangle object, returns the area of intersection as a Rectangle object. If the Rectangles do not intersect, this method returns an empty Rectangle object with its properties set to 0.
-* @method Phaser.Rectangle.intersection
-* @param {Phaser.Rectangle} a - The first Rectangle object.
-* @param {Phaser.Rectangle} b - The second Rectangle object.
-* @param {Phaser.Rectangle} [out] - Optional Rectangle object. If given the intersection values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
-* @return {Phaser.Rectangle} A Rectangle object that equals the area of intersection. If the Rectangles do not intersect, this method returns an empty Rectangle object; that is, a Rectangle with its x, y, width, and height properties set to 0.
-*/
-Phaser.Rectangle.intersection = function (a, b, out) {
-
- out = out || new Phaser.Rectangle();
-
- if (Phaser.Rectangle.intersects(a, b))
- {
- out.x = Math.max(a.x, b.x);
- out.y = Math.max(a.y, b.y);
- out.width = Math.min(a.right, b.right) - out.x;
- out.height = Math.min(a.bottom, b.bottom) - out.y;
- }
-
- return out;
-
-};
-
-/**
-* Determines whether the two Rectangles intersect with each other.
-* This method checks the x, y, width, and height properties of the Rectangles.
-* @method Phaser.Rectangle.intersects
-* @param {Phaser.Rectangle} a - The first Rectangle object.
-* @param {Phaser.Rectangle} b - The second Rectangle object.
-* @return {boolean} A value of true if the specified object intersects with this Rectangle object; otherwise false.
-*/
-Phaser.Rectangle.intersects = function (a, b) {
-
- if (a.width <= 0 || a.height <= 0 || b.width <= 0 || b.height <= 0)
- {
- return false;
- }
-
- return !(a.right < b.x || a.bottom < b.y || a.x > b.right || a.y > b.bottom);
-
-};
-
-/**
-* Determines whether the object specified intersects (overlaps) with the given values.
-* @method Phaser.Rectangle.intersectsRaw
-* @param {number} left - Description.
-* @param {number} right - Description.
-* @param {number} top - Description.
-* @param {number} bottom - Description.
-* @param {number} tolerance - A tolerance value to allow for an intersection test with padding, default to 0
-* @return {boolean} A value of true if the specified object intersects with the Rectangle; otherwise false.
-*/
-Phaser.Rectangle.intersectsRaw = function (a, left, right, top, bottom, tolerance) {
-
- if (typeof tolerance === "undefined") { tolerance = 0; }
-
- return !(left > a.right + tolerance || right < a.left - tolerance || top > a.bottom + tolerance || bottom < a.top - tolerance);
-
-};
-
-/**
-* Adds two Rectangles together to create a new Rectangle object, by filling in the horizontal and vertical space between the two Rectangles.
-* @method Phaser.Rectangle.union
-* @param {Phaser.Rectangle} a - The first Rectangle object.
-* @param {Phaser.Rectangle} b - The second Rectangle object.
-* @param {Phaser.Rectangle} [out] - Optional Rectangle object. If given the new values will be set into this object, otherwise a brand new Rectangle object will be created and returned.
-* @return {Phaser.Rectangle} A Rectangle object that is the union of the two Rectangles.
-*/
-Phaser.Rectangle.union = function (a, b, out) {
-
- if (typeof out === "undefined") { out = new Phaser.Rectangle(); }
-
- return out.setTo(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.max(a.right, b.right) - Math.min(a.left, b.left), Math.max(a.bottom, b.bottom) - Math.min(a.top, b.top));
-
-};
-
-/**
-* @author Richard Davey
-* @copyright 2014 Photon Storm Ltd.
-* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
-*/
-
-/**
-* Creates a new Polygon. You have to provide a list of points.
-* This can be an array of Points that form the polygon, a flat array of numbers that will be interpreted as [x,y, x,y, ...],
-* or the arguments passed can be all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the
-* arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are numbers.
-*
-* @class Phaser.Polygon
-* @classdesc The polygon represents a list of orderded points in space
-* @constructor
-* @param {Array|Array} points - The array of Points.
-*/
-Phaser.Polygon = function (points) {
-
- PIXI.Polygon.call(this, points);
+ },
/**
- * @property {number} type - The base object type.
+ * Returns a random member of `array`.
+ * @method Phaser.RandomDataGenerator#pick
+ * @param {Array} ary - An Array to pick a random member of.
+ * @return {any} A random member of the array.
*/
- this.type = Phaser.POLYGON;
+ pick: function (ary) {
+ return ary[this.integerInRange(0, ary.length)];
+ },
+
+ /**
+ * Returns a random member of `array`, favoring the earlier entries.
+ * @method Phaser.RandomDataGenerator#weightedPick
+ * @param {Array} ary - An Array to pick a random member of.
+ * @return {any} A random member of the array.
+ */
+ weightedPick: function (ary) {
+ return ary[~~(Math.pow(this.frac(), 2) * ary.length)];
+ },
+
+ /**
+ * Returns a random timestamp between min and max, or between the beginning of 2000 and the end of 2020 if min and max aren't specified.
+ * @method Phaser.RandomDataGenerator#timestamp
+ * @param {number} min - The minimum value in the range.
+ * @param {number} max - The maximum value in the range.
+ * @return {number} A random timestamp between min and max.
+ */
+ timestamp: function (min, max) {
+ return this.realInRange(min || 946684800000, max || 1577862000000);
+ },
+
+ /**
+ * Returns a random angle between -180 and 180.
+ * @method Phaser.RandomDataGenerator#angle
+ * @return {number} A random number between -180 and 180.
+ */
+ angle: function() {
+ return this.integerInRange(-180, 180);
+ }
};
-Phaser.Polygon.prototype = Object.create(PIXI.Polygon.prototype);
-Phaser.Polygon.prototype.constructor = Phaser.Polygon;
+Phaser.RandomDataGenerator.prototype.constructor = Phaser.RandomDataGenerator;
+
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -31173,7 +43885,7 @@ Phaser.Timer.prototype = {
{
if (this.events[i] === event)
{
- this.events.splice(i, 1);
+ this.events[i].pendingDelete = true;
return true;
}
}
@@ -31236,11 +43948,26 @@ Phaser.Timer.prototype = {
this._len = this.events.length;
+ this._i = 0;
+
+ while (this._i < this._len)
+ {
+ if (this.events[this._i].pendingDelete)
+ {
+ this.events.splice(this._i, 1);
+ this._len--;
+ }
+
+ this._i++;
+ }
+
+ this._len = this.events.length;
+
if (this.running && this._now >= this.nextTick && this._len > 0)
{
this._i = 0;
- while (this._i < this._len)
+ while (this._i < this._len && this.running)
{
if (this._now >= this.events[this._i].tick)
{
@@ -31299,9 +44026,12 @@ Phaser.Timer.prototype = {
*/
pause: function () {
- this._pauseStarted = this.game.time.now;
+ if (this.running && !this.expired)
+ {
+ this._pauseStarted = this.game.time.now;
- this.paused = true;
+ this.paused = true;
+ }
},
@@ -31311,17 +44041,20 @@ Phaser.Timer.prototype = {
*/
resume: function () {
- var pauseDuration = this.game.time.now - this._pauseStarted;
-
- for (var i = 0; i < this.events.length; i++)
+ if (this.running && !this.expired)
{
- this.events[i].tick += pauseDuration;
+ var pauseDuration = this.game.time.now - this._pauseStarted;
+
+ for (var i = 0; i < this.events.length; i++)
+ {
+ this.events[i].tick += pauseDuration;
+ }
+
+ this.nextTick += pauseDuration;
+
+ this.paused = false;
}
- this.nextTick += pauseDuration;
-
- this.paused = false;
-
},
/**
@@ -31333,6 +44066,7 @@ Phaser.Timer.prototype = {
this.onComplete.removeAll();
this.running = false;
this.events = [];
+ this._i = this._len;
}
@@ -31478,6 +44212,12 @@ Phaser.TimerEvent = function (timer, delay, tick, repeatCount, loop, callback, c
*/
this.args = args;
+ /**
+ * @property {boolean} pendingDelete - A flag that controls if the TimerEvent is pending deletion.
+ * @protected
+ */
+ this.pendingDelete = false;
+
};
Phaser.TimerEvent.prototype.constructor = Phaser.TimerEvent;
@@ -31619,6 +44359,12 @@ Phaser.AnimationManager.prototype = {
this.currentFrame = this.currentAnim.currentFrame;
this.sprite.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ if (this.sprite.__tilePattern)
+ {
+ this.__tilePattern = false;
+ this.tilingTexture = false;
+ }
+
return this._anims[name];
},
@@ -31737,7 +44483,6 @@ Phaser.AnimationManager.prototype = {
if (this.currentAnim && this.currentAnim.update() === true)
{
this.currentFrame = this.currentAnim.currentFrame;
- this.sprite.currentFrame = this.currentFrame;
return true;
}
@@ -31754,7 +44499,7 @@ Phaser.AnimationManager.prototype = {
*/
getAnimation: function (name) {
- if (typeof name == 'string')
+ if (typeof name === 'string')
{
if (this._anims[name])
{
@@ -31773,9 +44518,14 @@ Phaser.AnimationManager.prototype = {
*/
refreshFrame: function () {
- this.sprite.currentFrame = this.currentFrame;
this.sprite.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ if (this.sprite.__tilePattern)
+ {
+ this.__tilePattern = false;
+ this.tilingTexture = false;
+ }
+
},
/**
@@ -31872,8 +44622,13 @@ Object.defineProperty(Phaser.AnimationManager.prototype, 'frame', {
{
this.currentFrame = this._frameData.getFrame(value);
this._frameIndex = value;
- this.sprite.currentFrame = this.currentFrame;
this.sprite.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+
+ if (this.sprite.__tilePattern)
+ {
+ this.__tilePattern = false;
+ this.tilingTexture = false;
+ }
}
}
@@ -31901,8 +44656,13 @@ Object.defineProperty(Phaser.AnimationManager.prototype, 'frameName', {
{
this.currentFrame = this._frameData.getFrameByName(value);
this._frameIndex = this.currentFrame.index;
- this.sprite.currentFrame = this.currentFrame;
this.sprite.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+
+ if (this.sprite.__tilePattern)
+ {
+ this.__tilePattern = false;
+ this.tilingTexture = false;
+ }
}
else
{
@@ -32076,6 +44836,12 @@ Phaser.Animation.prototype = {
this.currentFrame = this._frameData.getFrame(this._frames[this._frameIndex]);
this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+ if (this._parent.__tilePattern)
+ {
+ this._parent.__tilePattern = false;
+ this._parent.tilingTexture = false;
+ }
+
if (this._parent.events)
{
this._parent.events.onAnimationStart.dispatch(this._parent, this);
@@ -32173,6 +44939,12 @@ Phaser.Animation.prototype = {
if (this.currentFrame)
{
this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+
+ if (this._parent.__tilePattern)
+ {
+ this._parent.__tilePattern = false;
+ this._parent.tilingTexture = false;
+ }
}
this._parent.events.onAnimationLoop.dispatch(this._parent, this);
@@ -32189,6 +44961,12 @@ Phaser.Animation.prototype = {
if (this.currentFrame)
{
this._parent.setTexture(PIXI.TextureCache[this.currentFrame.uuid]);
+
+ if (this._parent.__tilePattern)
+ {
+ this._parent.__tilePattern = false;
+ this._parent.tilingTexture = false;
+ }
}
}
@@ -32543,6 +45321,28 @@ Phaser.Frame.prototype = {
this.spriteSourceSizeH = destHeight;
}
+ },
+
+ /**
+ * Returns a Rectangle set to the dimensions of this Frame.
+ *
+ * @method Phaser.Frame#getRect
+ * @param {Phaser.Rectangle} [out] - A rectangle to copy the frame dimensions to.
+ * @return {Phaser.Rectangle} A rectangle.
+ */
+ getRect: function (out) {
+
+ if (typeof out === 'undefined')
+ {
+ out = new Phaser.Rectangle(this.x, this.y, this.width, this.height);
+ }
+ else
+ {
+ out.setTo(this.x, this.y, this.width, this.height);
+ }
+
+ return out;
+
}
};
@@ -32611,12 +45411,12 @@ Phaser.FrameData.prototype = {
*/
getFrame: function (index) {
- if (this._frames.length > index)
+ if (index > this._frames.length)
{
- return this._frames[index];
+ index = 0;
}
- return null;
+ return this._frames[index];
},
@@ -32944,12 +45744,9 @@ Phaser.AnimationParser = {
frames[i].spriteSourceSize.h
);
- // We had to hack Pixi to get this to work :(
- PIXI.TextureCache[uuid].trimmed = true;
- PIXI.TextureCache[uuid].trim.x = frames[i].spriteSourceSize.x;
- PIXI.TextureCache[uuid].trim.y = frames[i].spriteSourceSize.y;
-
+ PIXI.TextureCache[uuid].trim = new Phaser.Rectangle(frames[i].spriteSourceSize.x, frames[i].spriteSourceSize.y, frames[i].sourceSize.w, frames[i].sourceSize.h);
}
+
}
return data;
@@ -33016,11 +45813,7 @@ Phaser.AnimationParser = {
frames[key].spriteSourceSize.h
);
- // We had to hack Pixi to get this to work :(
- PIXI.TextureCache[uuid].trimmed = true;
- PIXI.TextureCache[uuid].trim.x = frames[key].spriteSourceSize.x;
- PIXI.TextureCache[uuid].trim.y = frames[key].spriteSourceSize.y;
-
+ PIXI.TextureCache[uuid].trim = new Phaser.Rectangle(frames[key].spriteSourceSize.x, frames[key].spriteSourceSize.y, frames[key].sourceSize.w, frames[key].sourceSize.h);
}
i++;
@@ -33102,12 +45895,7 @@ Phaser.AnimationParser = {
{
newFrame.setTrim(true, width, height, frameX, frameY, frameWidth, frameHeight);
- PIXI.TextureCache[uuid].realSize = { x: frameX, y: frameY, w: frameWidth, h: frameHeight };
-
- // We had to hack Pixi to get this to work :(
- PIXI.TextureCache[uuid].trimmed = true;
- PIXI.TextureCache[uuid].trim.x = frameX;
- PIXI.TextureCache[uuid].trim.y = frameY;
+ PIXI.TextureCache[uuid].trim = new Phaser.Rectangle(frameX, frameY, width, height);
}
}
@@ -33168,6 +45956,12 @@ Phaser.Cache = function (game) {
*/
this._text = {};
+ /**
+ * @property {object} _physics - Physics data key-value container.
+ * @private
+ */
+ this._physics = {};
+
/**
* @property {object} _tilemaps - Tilemap key-value container.
* @private
@@ -33186,6 +45980,12 @@ Phaser.Cache = function (game) {
*/
this._bitmapDatas = {};
+ /**
+ * @property {object} _bitmapFont - BitmapFont key-value container.
+ * @private
+ */
+ this._bitmapFont = {};
+
this.addDefaultImage();
this.addMissingImage();
@@ -33196,10 +45996,71 @@ Phaser.Cache = function (game) {
};
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.CANVAS = 1;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.IMAGE = 2;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.TEXTURE = 3;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.SOUND = 4;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.TEXT = 5;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.PHYSICS = 6;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.TILEMAP = 7;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.BINARY = 8;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.BITMAPDATA = 9;
+
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Cache.BITMAPFONT = 10;
+
Phaser.Cache.prototype = {
/**
* Add a new canvas object in to the cache.
+ *
* @method Phaser.Cache#addCanvas
* @param {string} key - Asset key for this canvas.
* @param {HTMLCanvasElement} canvas - Canvas DOM element.
@@ -33213,6 +46074,7 @@ Phaser.Cache.prototype = {
/**
* Add a binary object in to the cache.
+ *
* @method Phaser.Cache#addBinary
* @param {string} key - Asset key for this binary data.
* @param {object} binaryData - The binary object to be addded to the cache.
@@ -33225,6 +46087,7 @@ Phaser.Cache.prototype = {
/**
* Add a BitmapData object in to the cache.
+ *
* @method Phaser.Cache#addBitmapData
* @param {string} key - Asset key for this BitmapData.
* @param {Phaser.BitmapData} bitmapData - The BitmapData object to be addded to the cache.
@@ -33253,6 +46116,19 @@ Phaser.Cache.prototype = {
},
+ /**
+ * Add a Phaser.BitmapFont in to the cache.
+ *
+ * @method Phaser.Cache#addBitmapFont
+ * @param {string} key - The unique key by which you will reference this object.
+ * @param {Phaser.BitmapFont} texture - The BitmapFont object to be stored. This can be applied to any Image/Sprite as a texture.
+ */
+ addBitmapFont: function (key, texture) {
+
+ this._bitmapFont[key] = texture;
+
+ },
+
/**
* Add a new sprite sheet in to the cache.
*
@@ -33278,7 +46154,7 @@ Phaser.Cache.prototype = {
},
/**
- * Add a new tilemap.
+ * Add a new tilemap to the Cache.
*
* @method Phaser.Cache#addTilemap
* @param {string} key - The unique key by which you will reference this object.
@@ -33293,7 +46169,7 @@ Phaser.Cache.prototype = {
},
/**
- * Add a new texture atlas.
+ * Add a new texture atlas to the Cache.
*
* @method Phaser.Cache#addTextureAtlas
* @param {string} key - The unique key by which you will reference this object.
@@ -33325,23 +46201,39 @@ Phaser.Cache.prototype = {
},
/**
- * Add a new Bitmap Font.
+ * Add a new Bitmap Font to the Cache.
*
* @method Phaser.Cache#addBitmapFont
* @param {string} key - The unique key by which you will reference this object.
* @param {string} url - URL of this font xml file.
* @param {object} data - Extra font data.
- * @param xmlData {object} Texture atlas frames data.
+ * @param {object} xmlData - Texture atlas frames data.
+ * @param {number} [xSpacing=0] - If you'd like to add additional horizontal spacing between the characters then set the pixel value here.
+ * @param {number} [ySpacing=0] - If you'd like to add additional vertical spacing between the lines then set the pixel value here.
*/
- addBitmapFont: function (key, url, data, xmlData) {
+ addBitmapFont: function (key, url, data, xmlData, xSpacing, ySpacing) {
this._images[key] = { url: url, data: data, spriteSheet: true };
PIXI.BaseTextureCache[key] = new PIXI.BaseTexture(data);
PIXI.TextureCache[key] = new PIXI.Texture(PIXI.BaseTextureCache[key]);
- Phaser.LoaderParser.bitmapFont(this.game, xmlData, key);
- // this._images[key].frameData = Phaser.AnimationParser.XMLData(this.game, xmlData, key);
+ Phaser.LoaderParser.bitmapFont(this.game, xmlData, key, xSpacing, ySpacing);
+
+ },
+
+ /**
+ * Add a new physics data object to the Cache.
+ *
+ * @method Phaser.Cache#addTilemap
+ * @param {string} key - The unique key by which you will reference this object.
+ * @param {string} url - URL of the physics json data.
+ * @param {object} JSONData - The physics data object (a JSON file).
+ * @param {number} format - The format of the physics data.
+ */
+ addPhysicsData: function (key, url, JSONData, format) {
+
+ this._physics[key] = { url: url, data: JSONData, format: format };
},
@@ -33349,6 +46241,7 @@ Phaser.Cache.prototype = {
* Adds a default image to be used in special cases such as WebGL Filters. Is mapped to the key __default.
*
* @method Phaser.Cache#addDefaultImage
+ * @protected
*/
addDefaultImage: function () {
@@ -33367,6 +46260,7 @@ Phaser.Cache.prototype = {
* Adds an image to be used when a key is wrong / missing. Is mapped to the key __missing.
*
* @method Phaser.Cache#addMissingImage
+ * @protected
*/
addMissingImage: function () {
@@ -33391,10 +46285,7 @@ Phaser.Cache.prototype = {
*/
addText: function (key, url, data) {
- this._text[key] = {
- url: url,
- data: data
- };
+ this._text[key] = { url: url, data: data };
},
@@ -33551,6 +46442,64 @@ Phaser.Cache.prototype = {
},
+ /**
+ * Get a BitmapFont object from the cache by its key.
+ *
+ * @method Phaser.Cache#getBitmapFont
+ * @param {string} key - Asset key of the BitmapFont object to retrieve from the Cache.
+ * @return {Phaser.BitmapFont} The requested BitmapFont object if found, or null if not.
+ */
+ getBitmapFont: function (key) {
+
+ if (this._bitmapFont[key])
+ {
+ return this._bitmapFont[key];
+ }
+ else
+ {
+ console.warn('Phaser.Cache.getBitmapFont: Invalid key: "' + key + '"');
+ }
+
+ },
+
+ /**
+ * Get a physics data object from the cache by its key. You can get either the entire data set or just a single object from it.
+ *
+ * @method Phaser.Cache#getPhysicsData
+ * @param {string} key - Asset key of the physics data object to retrieve from the Cache.
+ * @param {string} [object=null] - If specified it will return just the physics object that is part of the given key, if null it will return them all.
+ * @return {object} The requested physics object data if found.
+ */
+ getPhysicsData: function (key, object) {
+
+ if (typeof object === 'undefined' || object === null)
+ {
+ // Get 'em all
+ if (this._physics[key])
+ {
+ return this._physics[key].data;
+ }
+ else
+ {
+ console.warn('Phaser.Cache.getPhysicsData: Invalid key: "' + key + '"');
+ }
+ }
+ else
+ {
+ if (this._physics[key] && this._physics[key].data[object])
+ {
+ return this._physics[key].data[object][0];
+ }
+ else
+ {
+ console.warn('Phaser.Cache.getPhysicsData: Invalid key/object: "' + key + ' / ' + object + '"');
+ }
+ }
+
+ return null;
+
+ },
+
/**
* Checks if an image key exists.
*
@@ -33626,6 +46575,23 @@ Phaser.Cache.prototype = {
return null;
},
+ /**
+ * Replaces a set of frameData with a new Phaser.FrameData object.
+ *
+ * @method Phaser.Cache#updateFrameData
+ * @param {string} key - The unique key by which you will reference this object.
+ * @param {number} frameData - The new FrameData.
+ */
+ updateFrameData: function (key, frameData) {
+
+ if (this._images[key])
+ {
+ this._images[key].spriteSheet = true;
+ this._images[key].frameData = frameData;
+ }
+
+ },
+
/**
* Get a single frame out of a frameData set by key.
*
@@ -33842,14 +46808,63 @@ Phaser.Cache.prototype = {
},
/**
- * Get the cache keys from a given array of objects.
- * Normally you don't call this directly but instead use getImageKeys, getSoundKeys, etc.
+ * Gets all keys used by the Cache for the given data type.
*
* @method Phaser.Cache#getKeys
- * @param {Array} array - An array of items to return the keys for.
+ * @param {number} [type=Phaser.Cache.IMAGE] - The type of Cache keys you wish to get. Can be Cache.CANVAS, Cache.IMAGE, Cache.SOUND, etc.
* @return {Array} The array of item keys.
*/
- getKeys: function (array) {
+ getKeys: function (type) {
+
+ var array = null;
+
+ switch (type)
+ {
+ case Phaser.Cache.CANVAS:
+ array = this._canvases;
+ break;
+
+ case Phaser.Cache.IMAGE:
+ array = this._images;
+ break;
+
+ case Phaser.Cache.TEXTURE:
+ array = this._textures;
+ break;
+
+ case Phaser.Cache.SOUND:
+ array = this._sounds;
+ break;
+
+ case Phaser.Cache.TEXT:
+ array = this._text;
+ break;
+
+ case Phaser.Cache.PHYSICS:
+ array = this._physics;
+ break;
+
+ case Phaser.Cache.TILEMAP:
+ array = this._tilemaps;
+ break;
+
+ case Phaser.Cache.BINARY:
+ array = this._binary;
+ break;
+
+ case Phaser.Cache.BITMAPDATA:
+ array = this._bitmapDatas;
+ break;
+
+ case Phaser.Cache.BITMAPFONT:
+ array = this._bitmapFont;
+ break;
+ }
+
+ if (!array)
+ {
+ return;
+ }
var output = [];
@@ -33865,36 +46880,6 @@ Phaser.Cache.prototype = {
},
- /**
- * Returns an array containing all of the keys of Images in the Cache.
- *
- * @method Phaser.Cache#getImageKeys
- * @return {Array} The string based keys in the Cache.
- */
- getImageKeys: function () {
- return this.getKeys(this._images);
- },
-
- /**
- * Returns an array containing all of the keys of Sounds in the Cache.
- *
- * @method Phaser.Cache#getSoundKeys
- * @return {Array} The string based keys in the Cache.
- */
- getSoundKeys: function () {
- return this.getKeys(this._sounds);
- },
-
- /**
- * Returns an array containing all of the keys of Text Files in the Cache.
- *
- * @method Phaser.Cache#getTextKeys
- * @return {Array} The string based keys in the Cache.
- */
- getTextKeys: function () {
- return this.getKeys(this._text);
- },
-
/**
* Removes a canvas from the cache.
*
@@ -33935,6 +46920,56 @@ Phaser.Cache.prototype = {
delete this._text[key];
},
+ /**
+ * Removes a physics data file from the cache.
+ *
+ * @method Phaser.Cache#removePhysics
+ * @param {string} key - Key of the asset you want to remove.
+ */
+ removePhysics: function (key) {
+ delete this._text[key];
+ },
+
+ /**
+ * Removes a tilemap from the cache.
+ *
+ * @method Phaser.Cache#removeTilemap
+ * @param {string} key - Key of the asset you want to remove.
+ */
+ removeTilemap: function (key) {
+ delete this._text[key];
+ },
+
+ /**
+ * Removes a binary file from the cache.
+ *
+ * @method Phaser.Cache#removeBinary
+ * @param {string} key - Key of the asset you want to remove.
+ */
+ removeBinary: function (key) {
+ delete this._text[key];
+ },
+
+ /**
+ * Removes a bitmap data from the cache.
+ *
+ * @method Phaser.Cache#removeBitmapData
+ * @param {string} key - Key of the asset you want to remove.
+ */
+ removeBitmapData: function (key) {
+ delete this._text[key];
+ },
+
+ /**
+ * Removes a bitmap font from the cache.
+ *
+ * @method Phaser.Cache#removeBitmapFont
+ * @param {string} key - Key of the asset you want to remove.
+ */
+ removeBitmapFont: function (key) {
+ delete this._text[key];
+ },
+
/**
* Clears the cache. Removes every local cache object reference.
*
@@ -33961,6 +46996,37 @@ Phaser.Cache.prototype = {
{
delete this._text[item['key']];
}
+
+ for (var item in this._textures)
+ {
+ delete this._textures[item['key']];
+ }
+
+ for (var item in this._physics)
+ {
+ delete this._physics[item['key']];
+ }
+
+ for (var item in this._tilemaps)
+ {
+ delete this._tilemaps[item['key']];
+ }
+
+ for (var item in this._binary)
+ {
+ delete this._binary[item['key']];
+ }
+
+ for (var item in this._bitmapDatas)
+ {
+ delete this._bitmapDatas[item['key']];
+ }
+
+ for (var item in this._bitmapFont)
+ {
+ delete this._bitmapFont[item['key']];
+ }
+
}
};
@@ -34041,16 +47107,17 @@ Phaser.Loader = function (game) {
/**
* You can optionally link a sprite to the preloader.
- * If you do so the Sprite's width or height will be cropped based on the percentage loaded.
- * @property {Phaser.Sprite} preloadSprite
+ * If you do so the Sprites width or height will be cropped based on the percentage loaded.
+ * @property {Phaser.Sprite|Phaser.Image} preloadSprite
* @default
*/
this.preloadSprite = null;
/**
- * @property {string} crossOrigin - The crossOrigin value applied to loaded images
+ * @property {boolean|string} crossOrigin - The crossOrigin value applied to loaded images.
+ * @default
*/
- this.crossOrigin = '';
+ this.crossOrigin = false;
/**
* If you want to append a URL before the path of any asset you can set this here.
@@ -34101,6 +47168,12 @@ Phaser.Loader.TEXTURE_ATLAS_JSON_HASH = 1;
*/
Phaser.Loader.TEXTURE_ATLAS_XML_STARLING = 2;
+/**
+* @constant
+* @type {number}
+*/
+Phaser.Loader.PHYSICS_LIME_CORONA = 3;
+
Phaser.Loader.prototype = {
/**
@@ -34129,8 +47202,7 @@ Phaser.Loader.prototype = {
this.preloadSprite.crop = new Phaser.Rectangle(0, 0, sprite.width, 1);
}
- sprite.crop = this.preloadSprite.crop;
- sprite.cropEnabled = true;
+ sprite.crop(this.preloadSprite.crop);
},
@@ -34458,6 +47530,49 @@ Phaser.Loader.prototype = {
},
+ /**
+ * Add a new physics data object loading request.
+ * The data must be in Lime + Corona JSON format. Physics Editor by code'n'web exports in this format natively.
+ *
+ * @method Phaser.Loader#physics
+ * @param {string} key - Unique asset key of the physics json data.
+ * @param {string} [dataURL] - The url of the map data file (csv/json)
+ * @param {object} [jsonData] - An optional JSON data object. If given then the dataURL is ignored and this JSON object is used for physics data instead.
+ * @param {string} [format=Phaser.Physics.LIME_CORONA_JSON] - The format of the physics data.
+ * @return {Phaser.Loader} This Loader instance.
+ */
+ physics: function (key, dataURL, jsonData, format) {
+
+ if (typeof dataURL === "undefined") { dataURL = null; }
+ if (typeof jsonData === "undefined") { jsonData = null; }
+ if (typeof format === "undefined") { format = Phaser.Physics.LIME_CORONA_JSON; }
+
+ if (dataURL == null && jsonData == null)
+ {
+ console.warn('Phaser.Loader.physics - Both dataURL and jsonData are null. One must be set.');
+
+ return this;
+ }
+
+ // A map data object has been given
+ if (jsonData)
+ {
+ if (typeof jsonData === 'string')
+ {
+ jsonData = JSON.parse(jsonData);
+ }
+
+ this.game.cache.addPhysicsData(key, null, jsonData, format);
+ }
+ else
+ {
+ this.addToFileList('physics', key, dataURL, { format: format });
+ }
+
+ return this;
+
+ },
+
/**
* Add a new bitmap font loading request.
*
@@ -34466,17 +47581,21 @@ Phaser.Loader.prototype = {
* @param {string} textureURL - The url of the font image file.
* @param {string} [xmlURL] - The url of the font data file (xml/fnt)
* @param {object} [xmlData] - An optional XML data object.
+ * @param {number} [xSpacing=0] - If you'd like to add additional horizontal spacing between the characters then set the pixel value here.
+ * @param {number} [ySpacing=0] - If you'd like to add additional vertical spacing between the lines then set the pixel value here.
* @return {Phaser.Loader} This Loader instance.
*/
- bitmapFont: function (key, textureURL, xmlURL, xmlData) {
+ bitmapFont: function (key, textureURL, xmlURL, xmlData, xSpacing, ySpacing) {
if (typeof xmlURL === "undefined") { xmlURL = null; }
if (typeof xmlData === "undefined") { xmlData = null; }
+ if (typeof xSpacing === "undefined") { xSpacing = 0; }
+ if (typeof ySpacing === "undefined") { ySpacing = 0; }
// A URL to a json/xml file has been given
if (xmlURL)
{
- this.addToFileList('bitmapfont', key, textureURL, { xmlURL: xmlURL });
+ this.addToFileList('bitmapfont', key, textureURL, { xmlURL: xmlURL, xSpacing: xSpacing, ySpacing: ySpacing });
}
else
{
@@ -34509,7 +47628,7 @@ Phaser.Loader.prototype = {
}
else
{
- this.addToFileList('bitmapfont', key, textureURL, { xmlURL: null, xmlData: xml });
+ this.addToFileList('bitmapfont', key, textureURL, { xmlURL: null, xmlData: xml, xSpacing: xSpacing, ySpacing: ySpacing });
}
}
}
@@ -34742,7 +47861,10 @@ Phaser.Loader.prototype = {
file.data.onerror = function () {
return _this.fileError(_this._fileIndex);
};
- file.data.crossOrigin = this.crossOrigin;
+ if (this.crossOrigin)
+ {
+ file.data.crossOrigin = this.crossOrigin;
+ }
file.data.src = this.baseURL + file.url;
break;
@@ -34825,6 +47947,7 @@ Phaser.Loader.prototype = {
case 'text':
case 'script':
+ case 'physics':
this._xhr.open("GET", this.baseURL + file.url, true);
this._xhr.responseType = "text";
this._xhr.onload = function () {
@@ -34971,7 +48094,7 @@ Phaser.Loader.prototype = {
if (file.xmlURL == null)
{
- this.game.cache.addBitmapFont(file.key, file.url, file.data, file.xmlData);
+ this.game.cache.addBitmapFont(file.key, file.url, file.data, file.xmlData, file.xSpacing, file.ySpacing);
}
else
{
@@ -35027,6 +48150,11 @@ Phaser.Loader.prototype = {
this.game.cache.addText(file.key, file.url, file.data);
break;
+ case 'physics':
+ var data = JSON.parse(this._xhr.responseText);
+ this.game.cache.addPhysicsData(file.key, file.url, data, file.format);
+ break;
+
case 'script':
file.data = document.createElement('script');
file.data.language = 'javascript';
@@ -35174,7 +48302,7 @@ Phaser.Loader.prototype = {
if (file.type == 'bitmapfont')
{
- this.game.cache.addBitmapFont(file.key, file.url, file.data, xml);
+ this.game.cache.addBitmapFont(file.key, file.url, file.data, xml, file.xSpacing, file.ySpacing);
}
else if (file.type == 'textureatlas')
{
@@ -35213,7 +48341,7 @@ Phaser.Loader.prototype = {
this.preloadSprite.crop.height = Math.floor((this.preloadSprite.height / 100) * this.progress);
}
- this.preloadSprite.sprite.crop = this.preloadSprite.crop;
+ // this.preloadSprite.sprite.crop = this.preloadSprite.crop;
}
this.onFileComplete.dispatch(this.progress, this._fileList[previousIndex].key, success, this.totalLoadedFiles(), this._fileList.length);
@@ -35293,73 +48421,75 @@ Phaser.Loader.prototype.constructor = Phaser.Loader;
* @class Phaser.LoaderParser
*/
Phaser.LoaderParser = {
-
- /**
- * Parse frame data from an XML file.
- * @method Phaser.LoaderParser.bitmapFont
- * @param {object} xml - XML data you want to parse.
- * @return {FrameData} Generated FrameData object.
- */
- bitmapFont: function (game, xml, cacheKey) {
- // Malformed?
- if (!xml.getElementsByTagName('font'))
+ /**
+ * Parse a Bitmap Font from an XML file.
+ * @method Phaser.LoaderParser.bitmapFont
+ * @param {Phaser.Game} game - A reference to the current game.
+ * @param {object} xml - XML data you want to parse.
+ * @param {string} cacheKey - The key of the texture this font uses in the cache.
+ */
+ bitmapFont: function (game, xml, cacheKey, xSpacing, ySpacing) {
+
+ if (!xml || /MSIE 9/i.test(navigator.userAgent) || navigator.isCocoonJS)
{
- console.warn("Phaser.LoaderParser.bitmapFont: Invalid XML given, missing tag");
- return;
+ if (typeof(window.DOMParser) === 'function')
+ {
+ var domparser = new DOMParser();
+ xml = domparser.parseFromString(this.ajaxRequest.responseText, 'text/xml');
+ }
+ else
+ {
+ var div = document.createElement('div');
+ div.innerHTML = this.ajaxRequest.responseText;
+ xml = div;
+ }
}
- var texture = PIXI.TextureCache[cacheKey];
-
var data = {};
- var info = xml.getElementsByTagName("info")[0];
- var common = xml.getElementsByTagName("common")[0];
- data.font = info.attributes.getNamedItem("face").nodeValue;
- data.size = parseInt(info.attributes.getNamedItem("size").nodeValue, 10);
- data.lineHeight = parseInt(common.attributes.getNamedItem("lineHeight").nodeValue, 10);
+ var info = xml.getElementsByTagName('info')[0];
+ var common = xml.getElementsByTagName('common')[0];
+
+ data.font = info.getAttribute('face');
+ data.size = parseInt(info.getAttribute('size'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) + ySpacing;
data.chars = {};
- //parse letters
- var letters = xml.getElementsByTagName("char");
+ var letters = xml.getElementsByTagName('char');
+ var texture = PIXI.TextureCache[cacheKey];
for (var i = 0; i < letters.length; i++)
{
- var charCode = parseInt(letters[i].attributes.getNamedItem("id").nodeValue, 10);
+ var charCode = parseInt(letters[i].getAttribute('id'), 10);
- var textureRect = {
- x: parseInt(letters[i].attributes.getNamedItem("x").nodeValue, 10),
- y: parseInt(letters[i].attributes.getNamedItem("y").nodeValue, 10),
- width: parseInt(letters[i].attributes.getNamedItem("width").nodeValue, 10),
- height: parseInt(letters[i].attributes.getNamedItem("height").nodeValue, 10)
- };
-
- // Note: This means you can only have 1 BitmapFont loaded at once!
- // Need to replace this with our own handler soon.
- PIXI.TextureCache[charCode] = new PIXI.Texture(texture, textureRect);
+ var textureRect = new PIXI.Rectangle(
+ parseInt(letters[i].getAttribute('x'), 10),
+ parseInt(letters[i].getAttribute('y'), 10),
+ parseInt(letters[i].getAttribute('width'), 10),
+ parseInt(letters[i].getAttribute('height'), 10)
+ );
data.chars[charCode] = {
- xOffset: parseInt(letters[i].attributes.getNamedItem("xoffset").nodeValue, 10),
- yOffset: parseInt(letters[i].attributes.getNamedItem("yoffset").nodeValue, 10),
- xAdvance: parseInt(letters[i].attributes.getNamedItem("xadvance").nodeValue, 10),
+ xOffset: parseInt(letters[i].getAttribute('xoffset'), 10),
+ yOffset: parseInt(letters[i].getAttribute('yoffset'), 10),
+ xAdvance: parseInt(letters[i].getAttribute('xadvance'), 10) + xSpacing,
kerning: {},
- texture:new PIXI.Texture(texture, textureRect)
-
+ texture: PIXI.TextureCache[cacheKey] = new PIXI.Texture(texture, textureRect)
};
}
- //parse kernings
- var kernings = xml.getElementsByTagName("kerning");
+ var kernings = xml.getElementsByTagName('kerning');
for (i = 0; i < kernings.length; i++)
{
- var first = parseInt(kernings[i].attributes.getNamedItem("first").nodeValue, 10);
- var second = parseInt(kernings[i].attributes.getNamedItem("second").nodeValue, 10);
- var amount = parseInt(kernings[i].attributes.getNamedItem("amount").nodeValue, 10);
+ var first = parseInt(kernings[i].getAttribute('first'), 10);
+ var second = parseInt(kernings[i].getAttribute('second'), 10);
+ var amount = parseInt(kernings[i].getAttribute('amount'), 10);
data.chars[second].kerning[first] = amount;
}
- PIXI.BitmapText.fonts[data.font] = data;
+ PIXI.BitmapText.fonts[cacheKey] = data;
}
@@ -36889,65 +50019,6 @@ Phaser.Utils.Debug.prototype = {
},
- /**
- * Renders the corners and point information of the given Sprite.
- * @method Phaser.Utils.Debug#renderSpriteCorners
- * @param {Phaser.Sprite} sprite - The sprite to be rendered.
- * @param {boolean} [showText=false] - If true the x/y coordinates of each point will be rendered.
- * @param {boolean} [showBounds=false] - If true the bounds will be rendered over the top of the sprite.
- * @param {string} [color='rgb(255,0,255)'] - The color the text is rendered in.
- */
- renderSpriteCorners: function (sprite, showText, showBounds, color) {
-
- if (this.context == null)
- {
- return;
- }
-
- showText = showText || false;
- showBounds = showBounds || false;
- color = color || 'rgb(255,255,255)';
-
- this.start(0, 0, color);
-
- if (showBounds)
- {
- this.context.beginPath();
- this.context.strokeStyle = 'rgba(0, 255, 0, 0.7)';
- this.context.strokeRect(sprite.bounds.x, sprite.bounds.y, sprite.bounds.width, sprite.bounds.height);
- this.context.closePath();
- this.context.stroke();
- }
-
- this.context.beginPath();
- this.context.moveTo(sprite.topLeft.x, sprite.topLeft.y);
- this.context.lineTo(sprite.topRight.x, sprite.topRight.y);
- this.context.lineTo(sprite.bottomRight.x, sprite.bottomRight.y);
- this.context.lineTo(sprite.bottomLeft.x, sprite.bottomLeft.y);
- this.context.closePath();
- this.context.strokeStyle = 'rgba(255, 0, 255, 0.7)';
- this.context.stroke();
-
- this.renderPoint(sprite.offset);
- this.renderPoint(sprite.center);
- this.renderPoint(sprite.topLeft);
- this.renderPoint(sprite.topRight);
- this.renderPoint(sprite.bottomLeft);
- this.renderPoint(sprite.bottomRight);
-
- if (showText)
- {
- this.currentColor = color;
- this.line('x: ' + Math.floor(sprite.topLeft.x) + ' y: ' + Math.floor(sprite.topLeft.y), sprite.topLeft.x, sprite.topLeft.y);
- this.line('x: ' + Math.floor(sprite.topRight.x) + ' y: ' + Math.floor(sprite.topRight.y), sprite.topRight.x, sprite.topRight.y);
- this.line('x: ' + Math.floor(sprite.bottomLeft.x) + ' y: ' + Math.floor(sprite.bottomLeft.y), sprite.bottomLeft.x, sprite.bottomLeft.y);
- this.line('x: ' + Math.floor(sprite.bottomRight.x) + ' y: ' + Math.floor(sprite.bottomRight.y), sprite.bottomRight.x, sprite.bottomRight.y);
- }
-
- this.stop();
-
- },
-
/**
* Render Sound information, including decoded state, duration, volume and more.
* @method Phaser.Utils.Debug#renderSoundInfo
@@ -37093,32 +50164,6 @@ Phaser.Utils.Debug.prototype = {
},
- /**
- * Render Sprite Body Physics Data as text.
- * @method Phaser.Utils.Debug#renderBodyInfo
- * @param {Phaser.Sprite} sprite - The sprite to be rendered.
- * @param {number} x - X position of the debug info to be rendered.
- * @param {number} y - Y position of the debug info to be rendered.
- * @param {string} [color='rgb(255,255,255)'] - color of the debug info to be rendered. (format is css color string).
- */
- renderBodyInfo: function (sprite, x, y, color) {
-
- color = color || 'rgb(255,255,255)';
-
- this.start(x, y, color, 210);
-
- this.splitline('x: ' + sprite.body.x.toFixed(2), 'y: ' + sprite.body.y.toFixed(2), 'width: ' + sprite.width, 'height: ' + sprite.height);
- this.splitline('speed: ' + sprite.body.speed.toFixed(2), 'angle: ' + sprite.body.angle.toFixed(2), 'linear damping: ' + sprite.body.linearDamping);
- this.splitline('blocked left: ' + sprite.body.blocked.left, 'right: ' + sprite.body.blocked.right, 'up: ' + sprite.body.blocked.up, 'down: ' + sprite.body.blocked.down);
- this.splitline('touching left: ' + sprite.body.touching.left, 'right: ' + sprite.body.touching.right, 'up: ' + sprite.body.touching.up, 'down: ' + sprite.body.touching.down);
- this.splitline('gravity x: ' + sprite.body.gravity.x, 'y: ' + sprite.body.gravity.y, 'world gravity x: ' + this.game.physics.gravity.x, 'y: ' + this.game.physics.gravity.y);
- this.splitline('acceleration x: ' + sprite.body.acceleration.x.toFixed(2), 'y: ' + sprite.body.acceleration.y.toFixed(2));
- this.splitline('velocity x: ' + sprite.body.velocity.x.toFixed(2), 'y: ' + sprite.body.velocity.y.toFixed(2), 'deltaX: ' + sprite.body.deltaX().toFixed(2), 'deltaY: ' + sprite.body.deltaY().toFixed(2));
- this.splitline('bounce x: ' + sprite.body.bounce.x.toFixed(2), 'y: ' + sprite.body.bounce.y.toFixed(2));
- this.stop();
-
- },
-
/**
* Render debug information about the Input object.
* @method Phaser.Utils.Debug#renderInputInfo
@@ -37298,78 +50343,6 @@ Phaser.Utils.Debug.prototype = {
},
- /**
- * Renders just the full Sprite bounds.
- * @method Phaser.Utils.Debug#renderSpriteBounds
- * @param {Phaser.Sprite} sprite - Description.
- * @param {string} [color] - Color of the debug info to be rendered (format is css color string).
- * @param {boolean} [fill=false] - If false the bounds outline is rendered, if true the whole rectangle is rendered.
- */
- renderSpriteBody: function (sprite, color, fill) {
-
- if (this.context == null)
- {
- return;
- }
-
- color = color || 'rgb(255,0,255)';
-
- if (typeof fill === 'undefined') { fill = false; }
-
- this.start(0, 0, color);
-
- if (fill)
- {
- this.context.fillStyle = color;
- this.context.fillRect(sprite.body.left, sprite.body.top, sprite.body.width, sprite.body.height);
- }
- else
- {
- this.context.strokeStyle = color;
- this.context.strokeRect(sprite.body.left, sprite.body.top, sprite.body.width, sprite.body.height);
- this.context.stroke();
- }
-
- this.stop();
-
- },
-
- /**
- * Renders just the full Sprite bounds.
- * @method Phaser.Utils.Debug#renderSpriteBounds
- * @param {Phaser.Sprite} sprite - Description.
- * @param {string} [color] - Color of the debug info to be rendered (format is css color string).
- * @param {boolean} [fill=false] - If false the bounds outline is rendered, if true the whole rectangle is rendered.
- */
- renderSpriteBounds: function (sprite, color, fill) {
-
- if (this.context == null)
- {
- return;
- }
-
- color = color || 'rgb(255,0,255)';
-
- if (typeof fill === 'undefined') { fill = false; }
-
- this.start(0, 0, color);
-
- if (fill)
- {
- this.context.fillStyle = color;
- this.context.fillRect(sprite.bounds.x, sprite.bounds.y, sprite.bounds.width, sprite.bounds.height);
- }
- else
- {
- this.context.strokeStyle = color;
- this.context.strokeRect(sprite.bounds.x, sprite.bounds.y, sprite.bounds.width, sprite.bounds.height);
- this.context.stroke();
- }
-
- this.stop();
-
- },
-
/**
* Renders a single pixel.
* @method Phaser.Utils.Debug#renderPixel
@@ -37420,19 +50393,32 @@ Phaser.Utils.Debug.prototype = {
* @method Phaser.Utils.Debug#renderRectangle
* @param {Phaser.Rectangle} rect - The Rectangle to render.
* @param {string} [color] - Color of the debug info to be rendered (format is css color string).
+ * @param {boolean} [filled=true] - Render the rectangle as a fillRect (default, true) or a strokeRect (false)
*/
- renderRectangle: function (rect, color) {
+ renderRectangle: function (rect, color, filled) {
if (this.context == null)
{
return;
}
+ if (typeof filled === 'undefined') { filled = true; }
+
color = color || 'rgba(0,255,0,0.3)';
this.start();
- this.context.fillStyle = color;
- this.context.fillRect(rect.x, rect.y, rect.width, rect.height);
+
+ if (filled)
+ {
+ this.context.fillStyle = color;
+ this.context.fillRect(rect.x, rect.y, rect.width, rect.height);
+ }
+ else
+ {
+ this.context.strokeStyle = color;
+ this.context.strokeRect(rect.x, rect.y, rect.width, rect.height);
+ }
+
this.stop();
},
@@ -37489,9 +50475,35 @@ Phaser.Utils.Debug.prototype = {
},
+ /**
+ * Render Sprite Body Physics Data as text.
+ * @method Phaser.Utils.Debug#renderBodyInfo
+ * @param {Phaser.Sprite} sprite - The sprite to be rendered.
+ * @param {number} x - X position of the debug info to be rendered.
+ * @param {number} y - Y position of the debug info to be rendered.
+ * @param {string} [color='rgb(255,255,255)'] - color of the debug info to be rendered. (format is css color string).
+ */
+ renderBodyInfo: function (sprite, x, y, color) {
+
+ color = color || 'rgb(255,255,255)';
+
+ this.start(x, y, color, 210);
+
+ this.splitline('x: ' + sprite.body.x.toFixed(2), 'y: ' + sprite.body.y.toFixed(2), 'width: ' + sprite.width, 'height: ' + sprite.height);
+ // this.splitline('speed: ' + sprite.body.speed.toFixed(2), 'angle: ' + sprite.body.angle.toFixed(2), 'linear damping: ' + sprite.body.linearDamping);
+ // this.splitline('blocked left: ' + sprite.body.blocked.left, 'right: ' + sprite.body.blocked.right, 'up: ' + sprite.body.blocked.up, 'down: ' + sprite.body.blocked.down);
+ // this.splitline('touching left: ' + sprite.body.touching.left, 'right: ' + sprite.body.touching.right, 'up: ' + sprite.body.touching.up, 'down: ' + sprite.body.touching.down);
+ // this.splitline('gravity x: ' + sprite.body.gravity.x, 'y: ' + sprite.body.gravity.y, 'world gravity x: ' + this.game.physics.gravity.x, 'y: ' + this.game.physics.gravity.y);
+ // this.splitline('acceleration x: ' + sprite.body.acceleration.x.toFixed(2), 'y: ' + sprite.body.acceleration.y.toFixed(2));
+ // this.splitline('velocity x: ' + sprite.body.velocity.x.toFixed(2), 'y: ' + sprite.body.velocity.y.toFixed(2), 'deltaX: ' + sprite.body.deltaX().toFixed(2), 'deltaY: ' + sprite.body.deltaY().toFixed(2));
+ // this.splitline('bounce x: ' + sprite.body.bounce.x.toFixed(2), 'y: ' + sprite.body.bounce.y.toFixed(2));
+ this.stop();
+
+ },
+
/**
* @method Phaser.Utils.Debug#renderPhysicsBody
- * @param {array} body
+ * @param {Phaser.Body} body - The Phaser.Body instance to render all shapes from.
* @param {string} [color='rgb(255,255,255)'] - The color the polygon is stroked in.
*/
renderPhysicsBody: function (body, color, context) {
@@ -37503,91 +50515,118 @@ Phaser.Utils.Debug.prototype = {
color = color || 'rgb(255,255,255)';
- var x = body.x - this.game.camera.x;
- var y = body.y - this.game.camera.y;
+ this.start(0, 0, color);
- if (body.type === Phaser.Physics.Arcade.CIRCLE)
+ var shapes = body.data.shapes;
+ var shapeOffsets = body.data.shapeOffsets;
+ var shapeAngles = body.data.shapeAngles;
+
+ var i = shapes.length;
+ var x = this.game.math.p2px(body.data.position[0]) - this.game.camera.view.x;
+ var y = this.game.math.p2px(body.data.position[1]) - this.game.camera.view.y;
+ var angle = body.data.angle;
+
+ while (i--)
{
- this.start(0, 0, color);
- this.context.beginPath();
- this.context.strokeStyle = color;
- this.context.arc(x, y, body.shape.r, 0, Math.PI * 2, false);
- this.context.stroke();
- this.context.closePath();
-
- // this.context.strokeStyle = 'rgb(0,0,255)';
- // this.context.strokeRect(body.left, body.top, body.width, body.height);
-
- this.stop();
- }
- else
- {
- var points = body.polygon.points;
-
- this.start(0, 0, color);
-
- this.context.beginPath();
- this.context.moveTo(x + points[0].x, y + points[0].y);
-
- for (var i = 1; i < points.length; i++)
+ if (shapes[i] instanceof p2.Rectangle)
{
- this.context.lineTo(x + points[i].x, y + points[i].y);
+ this.renderShapeRectangle(x, y, angle, shapes[i], shapeOffsets[i], shapeAngles[i]);
}
-
- this.context.closePath();
- this.context.strokeStyle = color;
- this.context.stroke();
-
- this.context.fillStyle = 'rgb(255,0,0)';
- this.context.fillRect(x + points[0].x - 2, y + points[0].y - 2, 5, 5);
-
- for (var i = 1; i < points.length; i++)
+ else if (shapes[i] instanceof p2.Line)
{
- this.context.fillStyle = 'rgb(255,' + (i * 40) + ',0)';
- this.context.fillRect(x + points[i].x - 2, y + points[i].y - 2, 5, 5);
+ this.renderShapeLine(x, y, angle, shapes[i], shapeOffsets[i], shapeAngles[i]);
+ }
+ // else if (shapes[i] instanceof p2.Convex)
+ else
+ {
+ this.renderShapeConvex(x, y, angle, shapes[i], shapeOffsets[i], shapeAngles[i]);
}
-
- // this.context.strokeStyle = 'rgb(0,255,255)';
- // this.context.strokeRect(body.left, body.top, body.width, body.height);
-
- this.stop();
}
+ this.stop();
+
},
/**
- * @method Phaser.Utils.Debug#renderPolygon
- * @param {array} polygon
- * @param {string} [color='rgb(255,255,255)'] - The color the polygon is stroked in.
+ * @method Phaser.Utils.Debug#renderShape
+ * @param {p2.Shape} shape - The shape to render.
+ * @param {number} x - The x coordinate of the Body to translate to.
+ * @param {number} y - The y coordinate of the Body to translate to.
+ * @param {number} angle - The angle of the Body to rotate to.
*/
- renderPolygon: function (polygon, color, context) {
-
- if (this.context === null && context === null)
- {
- return;
- }
-
- color = color || 'rgb(255,255,255)';
-
- var points = polygon.points;
- var x = polygon.pos.x;
- var y = polygon.pos.y;
-
- this.start(0, 0, color);
+ renderShapeRectangle: function (x, y, bodyAngle, shape, offset, angle) {
+
+ var w = this.game.math.p2px(shape.width);
+ var h = this.game.math.p2px(shape.height);
+ var points = shape.vertices;
this.context.beginPath();
- this.context.moveTo(x + points[0].x, y + points[0].y);
+ this.context.save();
+ this.context.translate(x + this.game.math.p2px(offset[0]), y + this.game.math.p2px(offset[1]));
+ this.context.rotate(bodyAngle + angle);
+
+ this.context.moveTo(this.game.math.p2px(points[0][0]), this.game.math.p2px(points[0][1]));
for (var i = 1; i < points.length; i++)
{
- this.context.lineTo(x + points[i].x, y + points[i].y);
+ this.context.lineTo(this.game.math.p2px(points[i][0]), this.game.math.p2px(points[i][1]));
}
this.context.closePath();
- this.context.strokeStyle = color;
this.context.stroke();
+ this.context.restore();
- this.stop();
+ },
+
+ /**
+ * @method Phaser.Utils.Debug#renderShape
+ * @param {number} x - The x coordinate of the Body to translate to.
+ * @param {number} y - The y coordinate of the Body to translate to.
+ * @param {p2.Shape} shape - The shape to render.
+ * @param {number} offset -
+ * @param {number} angle -
+ */
+ renderShapeLine: function (x, y, bodyAngle, shape, offset, angle) {
+
+ this.context.beginPath();
+ this.context.save();
+ this.context.translate(x, y);
+ this.context.rotate(bodyAngle + angle);
+ this.context.lineWidth = 0.5;
+ this.context.moveTo(0, 0);
+ this.context.lineTo(this.game.math.p2px(shape.length), 0);
+ this.context.closePath();
+ this.context.stroke();
+ this.context.restore();
+
+ },
+
+ /**
+ * @method Phaser.Utils.Debug#renderShape
+ * @param {p2.Shape} shape - The shape to render.
+ * @param {number} x - The x coordinate of the Body to translate to.
+ * @param {number} y - The y coordinate of the Body to translate to.
+ * @param {number} angle - The angle of the Body to rotate to.
+ */
+ renderShapeConvex: function (x, y, bodyAngle, shape, offset, angle) {
+
+ var points = shape.vertices;
+
+ this.context.beginPath();
+ this.context.save();
+ this.context.translate(x + this.game.math.p2px(offset[0]), y + this.game.math.p2px(offset[1]));
+ this.context.rotate(bodyAngle + angle);
+
+ this.context.moveTo(this.game.math.p2px(points[0][0]), this.game.math.p2px(points[0][1]));
+
+ for (var i = 1; i < points.length; i++)
+ {
+ this.context.lineTo(this.game.math.p2px(points[i][0]), this.game.math.p2px(points[i][1]));
+ }
+
+ this.context.closePath();
+ this.context.stroke();
+ this.context.restore();
}
@@ -37947,868 +50986,6 @@ Phaser.Color = {
};
-// Version 0.2 - Copyright 2013 - Jim Riecken
-//
-// Released under the MIT License - https://github.com/jriecken/sat-js
-//
-// A simple library for determining intersections of circles and
-// polygons using the Separating Axis Theorem.
-/** @preserve SAT.js - Version 0.2 - Copyright 2013 - Jim Riecken - released under the MIT License. https://github.com/jriecken/sat-js */
-
-/*global define: false, module: false*/
-/*jshint shadow:true, sub:true, forin:true, noarg:true, noempty:true,
- eqeqeq:true, bitwise:true, strict:true, undef:true,
- curly:true, browser:true */
-
-// Create a UMD wrapper for SAT. Works in:
-//
-// - Plain browser via global SAT variable
-// - AMD loader (like require.js)
-// - Node.js
-//
-// The quoted properties all over the place are used so that the Closure Compiler
-// does not mangle the exposed API in advanced mode.
-/**
- * @param {*} root - The global scope
- * @param {Function} factory - Factory that creates SAT module
- */
-(function (root, factory) {
- "use strict";
- if (typeof define === 'function' && define['amd']) {
- define(factory);
- } else if (typeof exports === 'object') {
- module['exports'] = factory();
- } else {
- root['SAT'] = factory();
- }
-}(this, function () {
- "use strict";
-
- var SAT = {};
-
- //
- // ## Vector
- //
- // Represents a vector in two dimensions with `x` and `y` properties.
-
-
- // Create a new Vector, optionally passing in the `x` and `y` coordinates. If
- // a coordinate is not specified, it will be set to `0`
- /**
- * @param {?number=} x The x position.
- * @param {?number=} y The y position.
- * @constructor
- */
- function Vector(x, y) {
- this['x'] = x || 0;
- this['y'] = y || 0;
- }
- SAT['Vector'] = Vector;
- // Alias `Vector` as `V`
- SAT['V'] = Vector;
-
-
- // Copy the values of another Vector into this one.
- /**
- * @param {Vector} other The other Vector.
- * @return {Vector} This for chaining.
- */
- Vector.prototype['copy'] = Vector.prototype.copy = function(other) {
- this['x'] = other['x'];
- this['y'] = other['y'];
- return this;
- };
-
- // Change this vector to be perpendicular to what it was before. (Effectively
- // roatates it 90 degrees in a clockwise direction)
- /**
- * @return {Vector} This for chaining.
- */
- Vector.prototype['perp'] = Vector.prototype.perp = function() {
- var x = this['x'];
- this['x'] = this['y'];
- this['y'] = -x;
- return this;
- };
-
- // Rotate this vector (counter-clockwise) by the specified angle (in radians).
- /**
- * @param {number} angle The angle to rotate (in radians)
- * @return {Vector} This for chaining.
- */
- Vector.prototype['rotate'] = Vector.prototype.rotate = function (angle) {
- var x = this['x'];
- var y = this['y'];
- this['x'] = x * Math.cos(angle) - y * Math.sin(angle);
- this['y'] = x * Math.sin(angle) + y * Math.cos(angle);
- return this;
- };
-
- // Rotate this vector (counter-clockwise) by the specified angle (in radians) which has already been calculated into sin and cos.
- /**
- * @param {number} sin - The Math.sin(angle)
- * @param {number} cos - The Math.cos(angle)
- * @return {Vector} This for chaining.
- */
- Vector.prototype['rotatePrecalc'] = Vector.prototype.rotatePrecalc = function (sin, cos) {
- var x = this['x'];
- var y = this['y'];
- this['x'] = x * cos - y * sin;
- this['y'] = x * sin + y * cos;
- return this;
- };
-
- // Reverse this vector.
- /**
- * @return {Vector} This for chaining.
- */
- Vector.prototype['reverse'] = Vector.prototype.reverse = function() {
- this['x'] = -this['x'];
- this['y'] = -this['y'];
- return this;
- };
-
-
- // Normalize this vector. (make it have length of `1`)
- /**
- * @return {Vector} This for chaining.
- */
- Vector.prototype['normalize'] = Vector.prototype.normalize = function() {
- var d = this.len();
- if(d > 0) {
- this['x'] = this['x'] / d;
- this['y'] = this['y'] / d;
- }
- return this;
- };
-
- // Add another vector to this one.
- /**
- * @param {Vector} other The other Vector.
- * @return {Vector} This for chaining.
- */
- Vector.prototype['add'] = Vector.prototype.add = function(other) {
- this['x'] += other['x'];
- this['y'] += other['y'];
- return this;
- };
-
- // Subtract another vector from this one.
- /**
- * @param {Vector} other The other Vector.
- * @return {Vector} This for chaiing.
- */
- Vector.prototype['sub'] = Vector.prototype.sub = function(other) {
- this['x'] -= other['x'];
- this['y'] -= other['y'];
- return this;
- };
-
- // Scale this vector. An independant scaling factor can be provided
- // for each axis, or a single scaling factor that will scale both `x` and `y`.
- /**
- * @param {number} x The scaling factor in the x direction.
- * @param {?number=} y The scaling factor in the y direction. If this
- * is not specified, the x scaling factor will be used.
- * @return {Vector} This for chaining.
- */
- Vector.prototype['scale'] = Vector.prototype.scale = function(x,y) {
- this['x'] *= x;
- this['y'] *= y || x;
- return this;
- };
-
- // Project this vector on to another vector.
- /**
- * @param {Vector} other The vector to project onto.
- * @return {Vector} This for chaining.
- */
- Vector.prototype['project'] = Vector.prototype.project = function(other) {
- var amt = this.dot(other) / other.len2();
- this['x'] = amt * other['x'];
- this['y'] = amt * other['y'];
- return this;
- };
-
- // Project this vector onto a vector of unit length. This is slightly more efficient
- // than `project` when dealing with unit vectors.
- /**
- * @param {Vector} other The unit vector to project onto.
- * @return {Vector} This for chaining.
- */
- Vector.prototype['projectN'] = Vector.prototype.projectN = function(other) {
- var amt = this.dot(other);
- this['x'] = amt * other['x'];
- this['y'] = amt * other['y'];
- return this;
- };
-
- // Reflect this vector on an arbitrary axis.
- /**
- * @param {Vector} axis The vector representing the axis.
- * @return {Vector} This for chaining.
- */
- Vector.prototype['reflect'] = Vector.prototype.reflect = function(axis) {
- var x = this['x'];
- var y = this['y'];
- this.project(axis).scale(2);
- this['x'] -= x;
- this['y'] -= y;
- return this;
- };
-
- // Reflect this vector on an arbitrary axis (represented by a unit vector). This is
- // slightly more efficient than `reflect` when dealing with an axis that is a unit vector.
- /**
- * @param {Vector} axis The unit vector representing the axis.
- * @return {Vector} This for chaining.
- */
- Vector.prototype['reflectN'] = Vector.prototype.reflectN = function(axis) {
- var x = this['x'];
- var y = this['y'];
- this.projectN(axis).scale(2);
- this['x'] -= x;
- this['y'] -= y;
- return this;
- };
-
- // Get the dot product of this vector and another.
- /**
- * @param {Vector} other The vector to dot this one against.
- * @return {number} The dot product.
- */
- Vector.prototype['dot'] = Vector.prototype.dot = function(other) {
- return this['x'] * other['x'] + this['y'] * other['y'];
- };
-
- // Get the squared length of this vector.
- /**
- * @return {number} The length^2 of this vector.
- */
- Vector.prototype['len2'] = Vector.prototype.len2 = function() {
- return this.dot(this);
- };
-
- // Get the length of this vector.
- /**
- * @return {number} The length of this vector.
- */
- Vector.prototype['len'] = Vector.prototype.len = function() {
- return Math.sqrt(this.len2());
- };
-
- // ## Circle
- //
- // Represents a circle with a position and a radius.
-
- // Create a new circle, optionally passing in a position and/or radius. If no position
- // is given, the circle will be at `(0,0)`. If no radius is provided, the circle will
- // have a radius of `0`.
- /**
- * @param {Vector=} pos A vector representing the position of the center of the circle
- * @param {?number=} r The radius of the circle
- * @constructor
- */
- function Circle(pos, r) {
- this['pos'] = pos || new Vector();
- this['r'] = r || 0;
- }
- SAT['Circle'] = Circle;
-
- // ## Polygon
- //
- // Represents a *convex* polygon with any number of points (specified in counter-clockwise order)
- //
- // The edges/normals of the polygon will be calculated on creation and stored in the
- // `edges` and `normals` properties. If you change the polygon's points, you will need
- // to call `recalc` to recalculate the edges/normals.
-
- // Create a new polygon, passing in a position vector, and an array of points (represented
- // by vectors relative to the position vector). If no position is passed in, the position
- // of the polygon will be `(0,0)`.
- /**
- * @param {Vector=} pos A vector representing the origin of the polygon. (all other
- * points are relative to this one)
- * @param {Array.=} points An array of vectors representing the points in the polygon,
- * in counter-clockwise order.
- * @constructor
- */
- function Polygon(pos, points) {
- this['pos'] = pos || new Vector();
- this['points'] = points || [];
- this.recalc();
- }
- SAT['Polygon'] = Polygon;
-
- // Recalculates the edges and normals of the polygon. This **must** be called
- // if the `points` array is modified at all and the edges or normals are to be
- // accessed.
- /**
- * @return {Polygon} This for chaining.
- */
- Polygon.prototype['recalc'] = Polygon.prototype.recalc = function() {
- // The edges here are the direction of the `n`th edge of the polygon, relative to
- // the `n`th point. If you want to draw a given edge from the edge value, you must
- // first translate to the position of the starting point.
- this['edges'] = [];
- // The normals here are the direction of the normal for the `n`th edge of the polygon, relative
- // to the position of the `n`th point. If you want to draw an edge normal, you must first
- // translate to the position of the starting point.
- this['normals'] = [];
- var points = this['points'];
- var len = points.length;
- for (var i = 0; i < len; i++) {
- var p1 = points[i];
- var p2 = i < len - 1 ? points[i + 1] : points[0];
- var e = new Vector().copy(p2).sub(p1);
- var n = new Vector().copy(e).perp().normalize();
- this['edges'].push(e);
- this['normals'].push(n);
- }
- return this;
- };
-
- // Rotates this polygon counter-clockwise around the origin of *its local coordinate system* (i.e. `pos`).
- //
- // Note: You do **not** need to call `recalc` after rotation.
- /**
- * @param {number} angle The angle to rotate (in radians)
- * @return {Polygon} This for chaining.
- */
- Polygon.prototype['rotate'] = Polygon.prototype.rotate = function(angle) {
- var i;
- var points = this['points'];
- var edges = this['edges'];
- var normals = this['normals'];
- var len = points.length;
-
- // Calc it just the once, rather than 4 times per array element
- var cos = Math.cos(angle);
- var sin = Math.sin(angle);
-
- for (i = 0; i < len; i++) {
- points[i].rotatePrecalc(sin, cos);
- edges[i].rotatePrecalc(sin, cos);
- normals[i].rotatePrecalc(sin, cos);
- }
- return this;
- };
-
- // Rotates this polygon counter-clockwise around the origin of *its local coordinate system* (i.e. `pos`).
- //
- // Note: You do **not** need to call `recalc` after rotation.
- /**
- * @param {number} angle The angle to rotate (in radians)
- * @return {Polygon} This for chaining.
- */
- Polygon.prototype['scale'] = Polygon.prototype.scale = function(x, y) {
- var i;
- var points = this['points'];
- var edges = this['edges'];
- var normals = this['normals'];
- var len = points.length;
- for (i = 0; i < len; i++) {
- points[i].scale(x,y);
- edges[i].scale(x,y);
- normals[i].scale(x,y);
- }
- return this;
- };
-
- // Translates the points of this polygon by a specified amount relative to the origin of *its own coordinate
- // system* (i.e. `pos`).
- //
- // This is most useful to change the "center point" of a polygon.
- //
- // Note: You do **not** need to call `recalc` after translation.
- /**
- * @param {number} x The horizontal amount to translate.
- * @param {number} y The vertical amount to translate.
- * @return {Polygon} This for chaining.
- */
- Polygon.prototype['translate'] = Polygon.prototype.translate = function (x, y) {
- var i;
- var points = this['points'];
- var len = points.length;
- for (i = 0; i < len; i++) {
- points[i].x += x;
- points[i].y += y;
- }
- return this;
- };
-
- // ## Box
- //
- // Represents an axis-aligned box, with a width and height.
-
-
- // Create a new box, with the specified position, width, and height. If no position
- // is given, the position will be `(0,0)`. If no width or height are given, they will
- // be set to `0`.
- /**
- * @param {Vector=} pos A vector representing the top-left of the box.
- * @param {?number=} w The width of the box.
- * @param {?number=} h The height of the box.
- * @constructor
- */
- function Box(pos, w, h) {
- this['pos'] = pos || new Vector();
- this['w'] = w || 0;
- this['h'] = h || 0;
- }
- SAT['Box'] = Box;
-
- // Returns a polygon whose edges are the same as this box.
- /**
- * @return {Polygon} A new Polygon that represents this box.
- */
- Box.prototype['toPolygon'] = Box.prototype.toPolygon = function() {
- var pos = this['pos'];
- var w = this['w'];
- var h = this['h'];
- return new Polygon(new Vector(pos['x'], pos['y']), [
- new Vector(), new Vector(w, 0),
- new Vector(w,h), new Vector(0,h)
- ]);
- };
-
- // ## Response
- //
- // An object representing the result of an intersection. Contains:
- // - The two objects participating in the intersection
- // - The vector representing the minimum change necessary to extract the first object
- // from the second one (as well as a unit vector in that direction and the magnitude
- // of the overlap)
- // - Whether the first object is entirely inside the second, and vice versa.
- /**
- * @constructor
- */
- function Response() {
- this['a'] = null;
- this['b'] = null;
- this['overlapN'] = new Vector();
- this['overlapV'] = new Vector();
- this.clear();
- }
- SAT['Response'] = Response;
-
- // Set some values of the response back to their defaults. Call this between tests if
- // you are going to reuse a single Response object for multiple intersection tests (recommented
- // as it will avoid allcating extra memory)
- /**
- * @return {Response} This for chaining
- */
- Response.prototype['clear'] = Response.prototype.clear = function() {
- this['aInB'] = true;
- this['bInA'] = true;
- this['overlap'] = Number.MAX_VALUE;
- return this;
- };
-
- // ## Object Pools
-
- // A pool of `Vector` objects that are used in calculations to avoid
- // allocating memory.
- /**
- * @type {Array.}
- */
- var T_VECTORS = [];
- for (var i = 0; i < 10; i++) { T_VECTORS.push(new Vector()); }
-
- // A pool of arrays of numbers used in calculations to avoid allocating
- // memory.
- /**
- * @type {Array.>}
- */
- var T_ARRAYS = [];
- for (var i = 0; i < 5; i++) { T_ARRAYS.push([]); }
-
- // ## Helper Functions
-
- // Flattens the specified array of points onto a unit vector axis,
- // resulting in a one dimensional range of the minimum and
- // maximum value on that axis.
- /**
- * @param {Array.} points The points to flatten.
- * @param {Vector} normal The unit vector axis to flatten on.
- * @param {Array.} result An array. After calling this function,
- * result[0] will be the minimum value,
- * result[1] will be the maximum value.
- */
- function flattenPointsOn(points, normal, result) {
- var min = Number.MAX_VALUE;
- var max = -Number.MAX_VALUE;
- var len = points.length;
- for (var i = 0; i < len; i++ ) {
- // The magnitude of the projection of the point onto the normal
- var dot = points[i].dot(normal);
- if (dot < min) { min = dot; }
- if (dot > max) { max = dot; }
- }
- result[0] = min; result[1] = max;
- }
-
- // Check whether two convex polygons are separated by the specified
- // axis (must be a unit vector).
- /**
- * @param {Vector} aPos The position of the first polygon.
- * @param {Vector} bPos The position of the second polygon.
- * @param {Array.} aPoints The points in the first polygon.
- * @param {Array.} bPoints The points in the second polygon.
- * @param {Vector} axis The axis (unit sized) to test against. The points of both polygons
- * will be projected onto this axis.
- * @param {Response=} response A Response object (optional) which will be populated
- * if the axis is not a separating axis.
- * @return {boolean} true if it is a separating axis, false otherwise. If false,
- * and a response is passed in, information about how much overlap and
- * the direction of the overlap will be populated.
- */
- function isSeparatingAxis(aPos, bPos, aPoints, bPoints, axis, response) {
- var rangeA = T_ARRAYS.pop();
- var rangeB = T_ARRAYS.pop();
- // The magnitude of the offset between the two polygons
- var offsetV = T_VECTORS.pop().copy(bPos).sub(aPos);
- var projectedOffset = offsetV.dot(axis);
- // Project the polygons onto the axis.
- flattenPointsOn(aPoints, axis, rangeA);
- flattenPointsOn(bPoints, axis, rangeB);
- // Move B's range to its position relative to A.
- rangeB[0] += projectedOffset;
- rangeB[1] += projectedOffset;
- // Check if there is a gap. If there is, this is a separating axis and we can stop
- if (rangeA[0] > rangeB[1] || rangeB[0] > rangeA[1]) {
- T_VECTORS.push(offsetV);
- T_ARRAYS.push(rangeA);
- T_ARRAYS.push(rangeB);
- return true;
- }
- // This is not a separating axis. If we're calculating a response, calculate the overlap.
- if (response) {
- var overlap = 0;
- // A starts further left than B
- if (rangeA[0] < rangeB[0]) {
- response['aInB'] = false;
- // A ends before B does. We have to pull A out of B
- if (rangeA[1] < rangeB[1]) {
- overlap = rangeA[1] - rangeB[0];
- response['bInA'] = false;
- // B is fully inside A. Pick the shortest way out.
- } else {
- var option1 = rangeA[1] - rangeB[0];
- var option2 = rangeB[1] - rangeA[0];
- overlap = option1 < option2 ? option1 : -option2;
- }
- // B starts further left than A
- } else {
- response['bInA'] = false;
- // B ends before A ends. We have to push A out of B
- if (rangeA[1] > rangeB[1]) {
- overlap = rangeA[0] - rangeB[1];
- response['aInB'] = false;
- // A is fully inside B. Pick the shortest way out.
- } else {
- var option1 = rangeA[1] - rangeB[0];
- var option2 = rangeB[1] - rangeA[0];
- overlap = option1 < option2 ? option1 : -option2;
- }
- }
- // If this is the smallest amount of overlap we've seen so far, set it as the minimum overlap.
- var absOverlap = Math.abs(overlap);
- if (absOverlap < response['overlap']) {
- response['overlap'] = absOverlap;
- response['overlapN'].copy(axis);
- if (overlap < 0) {
- response['overlapN'].reverse();
- }
- }
- }
- T_VECTORS.push(offsetV);
- T_ARRAYS.push(rangeA);
- T_ARRAYS.push(rangeB);
- return false;
- }
-
- // Calculates which Vornoi region a point is on a line segment.
- // It is assumed that both the line and the point are relative to `(0,0)`
- //
- // | (0) |
- // (-1) [S]--------------[E] (1)
- // | (0) |
- /**
- * @param {Vector} line The line segment.
- * @param {Vector} point The point.
- * @return {number} LEFT_VORNOI_REGION (-1) if it is the left region,
- * MIDDLE_VORNOI_REGION (0) if it is the middle region,
- * RIGHT_VORNOI_REGION (1) if it is the right region.
- */
- function vornoiRegion(line, point) {
- var len2 = line.len2();
- var dp = point.dot(line);
- // If the point is beyond the start of the line, it is in the
- // left vornoi region.
- if (dp < 0) { return LEFT_VORNOI_REGION; }
- // If the point is beyond the end of the line, it is in the
- // right vornoi region.
- else if (dp > len2) { return RIGHT_VORNOI_REGION; }
- // Otherwise, it's in the middle one.
- else { return MIDDLE_VORNOI_REGION; }
- }
- // Constants for Vornoi regions
- /**
- * @const
- */
- var LEFT_VORNOI_REGION = -1;
- /**
- * @const
- */
- var MIDDLE_VORNOI_REGION = 0;
- /**
- * @const
- */
- var RIGHT_VORNOI_REGION = 1;
-
- // ## Collision Tests
-
- // Check if two circles collide.
- /**
- * @param {Circle} a The first circle.
- * @param {Circle} b The second circle.
- * @param {Response=} response Response object (optional) that will be populated if
- * the circles intersect.
- * @return {boolean} true if the circles intersect, false if they don't.
- */
- function testCircleCircle(a, b, response) {
- // Check if the distance between the centers of the two
- // circles is greater than their combined radius.
- var differenceV = T_VECTORS.pop().copy(b['pos']).sub(a['pos']);
- var totalRadius = a['r'] + b['r'];
- var totalRadiusSq = totalRadius * totalRadius;
- var distanceSq = differenceV.len2();
- // If the distance is bigger than the combined radius, they don't intersect.
- if (distanceSq > totalRadiusSq) {
- T_VECTORS.push(differenceV);
- return false;
- }
- // They intersect. If we're calculating a response, calculate the overlap.
- if (response) {
- var dist = Math.sqrt(distanceSq);
- response['a'] = a;
- response['b'] = b;
- response['overlap'] = totalRadius - dist;
- response['overlapN'].copy(differenceV.normalize());
- response['overlapV'].copy(differenceV).scale(response['overlap']);
- response['aInB']= a['r'] <= b['r'] && dist <= b['r'] - a['r'];
- response['bInA'] = b['r'] <= a['r'] && dist <= a['r'] - b['r'];
- }
- T_VECTORS.push(differenceV);
- return true;
- }
- SAT['testCircleCircle'] = testCircleCircle;
-
- // Check if a polygon and a circle collide.
- /**
- * @param {Polygon} polygon The polygon.
- * @param {Circle} circle The circle.
- * @param {Response=} response Response object (optional) that will be populated if
- * they interset.
- * @return {boolean} true if they intersect, false if they don't.
- */
- function testPolygonCircle(polygon, circle, response) {
- // Get the position of the circle relative to the polygon.
- var circlePos = T_VECTORS.pop().copy(circle['pos']).sub(polygon['pos']);
- var radius = circle['r'];
- var radius2 = radius * radius;
- var points = polygon['points'];
- var len = points.length;
- var edge = T_VECTORS.pop();
- var point = T_VECTORS.pop();
-
- // For each edge in the polygon:
- for (var i = 0; i < len; i++) {
- var next = i === len - 1 ? 0 : i + 1;
- var prev = i === 0 ? len - 1 : i - 1;
- var overlap = 0;
- var overlapN = null;
-
- // Get the edge.
- edge.copy(polygon['edges'][i]);
- // Calculate the center of the circle relative to the starting point of the edge.
- point.copy(circlePos).sub(points[i]);
-
- // If the distance between the center of the circle and the point
- // is bigger than the radius, the polygon is definitely not fully in
- // the circle.
- if (response && point.len2() > radius2) {
- response['aInB'] = false;
- }
-
- // Calculate which Vornoi region the center of the circle is in.
- var region = vornoiRegion(edge, point);
- // If it's the left region:
- if (region === LEFT_VORNOI_REGION) {
- // We need to make sure we're in the RIGHT_VORNOI_REGION of the previous edge.
- edge.copy(polygon['edges'][prev]);
- // Calculate the center of the circle relative the starting point of the previous edge
- var point2 = T_VECTORS.pop().copy(circlePos).sub(points[prev]);
- region = vornoiRegion(edge, point2);
- if (region === RIGHT_VORNOI_REGION) {
- // It's in the region we want. Check if the circle intersects the point.
- var dist = point.len();
- if (dist > radius) {
- // No intersection
- T_VECTORS.push(circlePos);
- T_VECTORS.push(edge);
- T_VECTORS.push(point);
- T_VECTORS.push(point2);
- return false;
- } else if (response) {
- // It intersects, calculate the overlap.
- response['bInA'] = false;
- overlapN = point.normalize();
- overlap = radius - dist;
- }
- }
- T_VECTORS.push(point2);
- // If it's the right region:
- } else if (region === RIGHT_VORNOI_REGION) {
- // We need to make sure we're in the left region on the next edge
- edge.copy(polygon['edges'][next]);
- // Calculate the center of the circle relative to the starting point of the next edge.
- point.copy(circlePos).sub(points[next]);
- region = vornoiRegion(edge, point);
- if (region === LEFT_VORNOI_REGION) {
- // It's in the region we want. Check if the circle intersects the point.
- var dist = point.len();
- if (dist > radius) {
- // No intersection
- T_VECTORS.push(circlePos);
- T_VECTORS.push(edge);
- T_VECTORS.push(point);
- return false;
- } else if (response) {
- // It intersects, calculate the overlap.
- response['bInA'] = false;
- overlapN = point.normalize();
- overlap = radius - dist;
- }
- }
- // Otherwise, it's the middle region:
- } else {
- // Need to check if the circle is intersecting the edge,
- // Change the edge into its "edge normal".
- var normal = edge.perp().normalize();
- // Find the perpendicular distance between the center of the
- // circle and the edge.
- var dist = point.dot(normal);
- var distAbs = Math.abs(dist);
- // If the circle is on the outside of the edge, there is no intersection.
- if (dist > 0 && distAbs > radius) {
- // No intersection
- T_VECTORS.push(circlePos);
- T_VECTORS.push(normal);
- T_VECTORS.push(point);
- return false;
- } else if (response) {
- // It intersects, calculate the overlap.
- overlapN = normal;
- overlap = radius - dist;
- // If the center of the circle is on the outside of the edge, or part of the
- // circle is on the outside, the circle is not fully inside the polygon.
- if (dist >= 0 || overlap < 2 * radius) {
- response['bInA'] = false;
- }
- }
- }
-
- // If this is the smallest overlap we've seen, keep it.
- // (overlapN may be null if the circle was in the wrong Vornoi region).
- if (overlapN && response && Math.abs(overlap) < Math.abs(response['overlap'])) {
- response['overlap'] = overlap;
- response['overlapN'].copy(overlapN);
- }
- }
-
- // Calculate the final overlap vector - based on the smallest overlap.
- if (response) {
- response['a'] = polygon;
- response['b'] = circle;
- response['overlapV'].copy(response['overlapN']).scale(response['overlap']);
- }
- T_VECTORS.push(circlePos);
- T_VECTORS.push(edge);
- T_VECTORS.push(point);
- return true;
- }
- SAT['testPolygonCircle'] = testPolygonCircle;
-
- // Check if a circle and a polygon collide.
- //
- // **NOTE:** This is slightly less efficient than polygonCircle as it just
- // runs polygonCircle and reverses everything at the end.
- /**
- * @param {Circle} circle The circle.
- * @param {Polygon} polygon The polygon.
- * @param {Response=} response Response object (optional) that will be populated if
- * they interset.
- * @return {boolean} true if they intersect, false if they don't.
- */
- function testCirclePolygon(circle, polygon, response) {
- // Test the polygon against the circle.
- var result = testPolygonCircle(polygon, circle, response);
- if (result && response) {
- // Swap A and B in the response.
- var a = response['a'];
- var aInB = response['aInB'];
- response['overlapN'].reverse();
- response['overlapV'].reverse();
- response['a'] = response['b'];
- response['b'] = a;
- response['aInB'] = response['bInA'];
- response['bInA'] = aInB;
- }
- return result;
- }
- SAT['testCirclePolygon'] = testCirclePolygon;
-
- // Checks whether polygons collide.
- /**
- * @param {Polygon} a The first polygon.
- * @param {Polygon} b The second polygon.
- * @param {Response=} response Response object (optional) that will be populated if
- * they interset.
- * @return {boolean} true if they intersect, false if they don't.
- */
- function testPolygonPolygon(a, b, response) {
- var aPoints = a['points'];
- var aLen = aPoints.length;
- var bPoints = b['points'];
- var bLen = bPoints.length;
- // If any of the edge normals of A is a separating axis, no intersection.
- for (var i = 0; i < aLen; i++) {
- if (isSeparatingAxis(a['pos'], b['pos'], aPoints, bPoints, a['normals'][i], response)) {
- return false;
- }
- }
- // If any of the edge normals of B is a separating axis, no intersection.
- for (var i = 0;i < bLen; i++) {
- if (isSeparatingAxis(a['pos'], b['pos'], aPoints, bPoints, b['normals'][i], response)) {
- return false;
- }
- }
- // Since none of the edge normals of A or B are a separating axis, there is an intersection
- // and we've already calculated the smallest overlap (in isSeparatingAxis). Calculate the
- // final overlap vector.
- if (response) {
- response['a'] = a;
- response['b'] = b;
- response['overlapV'].copy(response['overlapN']).scale(response['overlap']);
- }
- return true;
- }
- SAT['testPolygonPolygon'] = testPolygonPolygon;
-
- return SAT;
-}));
/**
* @author Richard Davey
* @copyright 2014 Photon Storm Ltd.
@@ -38821,247 +50998,319 @@ Phaser.Color = {
Phaser.Physics = {};
/**
-* Arcade Physics constructor.
-*
-* @class Phaser.Physics.Arcade
-* @classdesc Arcade Physics Constructor
-* @constructor
-* @param {Phaser.Game} game reference to the current game instance.
+* @const
+* @type {number}
*/
-Phaser.Physics.Arcade = function (game) {
-
+Phaser.Physics.LIME_CORONA_JSON = 0;
+
+// Add an extra properties to p2 that we need
+p2.Body.prototype.parent = null;
+p2.Spring.prototype.parent = null;
+
+/**
+* @class Phaser.Physics.World
+* @classdesc Physics World Constructor
+* @constructor
+* @param {Phaser.Game} game - Reference to the current game instance.
+* @param {object} [config] - Physics configuration object passed in from the game constructor.
+*/
+Phaser.Physics.World = function (game, config) {
+
/**
* @property {Phaser.Game} game - Local reference to game.
*/
this.game = game;
- /**
- * @property {Phaser.Point} gravity - The World gravity setting. Defaults to x: 0, y: 0, or no gravity.
- */
- this.gravity = new Phaser.Point();
+ if (typeof config === 'undefined')
+ {
+ config = { gravity: [0, 0], broadphase: new p2.SAPBroadphase() };
+ }
/**
- * @property {SAT.Box} worldLeft - The left hand side of the physics bounds.
+ * @property {p2.World} game - The p2 World in which the simulation is run.
+ * @protected
*/
- this.worldLeft = null;
+ this.world = new p2.World(config);
/**
- * @property {SAT.Box} worldRight - The right hand side of the physics bounds.
+ * @property {array} materials - A local array of all created Materials.
+ * @protected
*/
- this.worldRight = null;
+ this.materials = [];
/**
- * @property {SAT.Box} worldTop - The top side of the physics bounds.
+ * @property {Phaser.InversePointProxy} gravity - The gravity applied to all bodies each step.
*/
- this.worldTop = null;
+ this.gravity = new Phaser.Physics.InversePointProxy(this.world.gravity);
/**
- * @property {SAT.Box} worldBottom - The bottom of the physics bounds.
+ * @property {p2.Body} bounds - The bounds body contains the 4 walls that border the World. Define or disable with setBounds.
*/
- this.worldBottom = null;
+ this.bounds = null;
/**
- * @property {array} worldPolys - An array of the polygon data from the physics bounds.
- */
- this.worldPolys = [ null, null, null, null ];
-
- /**
- * @property {Phaser.QuadTree} quadTree - The world QuadTree.
- */
- this.quadTree = new Phaser.QuadTree(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels);
-
- /**
- * @property {number} maxObjects - Used by the QuadTree to set the maximum number of objects per quad.
- */
- this.maxObjects = 10;
-
- /**
- * @property {number} maxLevels - Used by the QuadTree to set the maximum number of iteration levels.
- */
- this.maxLevels = 4;
-
- /**
- * @property {Array} _mapData - Internal cache var.
+ * @property {array} _wallShapes - The wall bounds shapes.
* @private
*/
- this._mapData = [];
+ this._wallShapes = [ null, null, null, null ];
/**
- * @property {number} _mapTiles - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onBodyAdded - Dispatched when a new Body is added to the World.
*/
- this._mapTiles = 0;
+ this.onBodyAdded = new Phaser.Signal();
/**
- * @property {boolean} _result - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onBodyRemoved - Dispatched when a Body is removed from the World.
*/
- this._result = false;
+ this.onBodyRemoved = new Phaser.Signal();
/**
- * @property {number} _total - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onSpringAdded - Dispatched when a new Spring is added to the World.
*/
- this._total = 0;
+ this.onSpringAdded = new Phaser.Signal();
/**
- * @property {number} _angle - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onSpringRemoved - Dispatched when a Spring is removed from the World.
*/
- this._angle = 0;
+ this.onSpringRemoved = new Phaser.Signal();
/**
- * @property {number} _drag - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onConstraintAdded - Dispatched when a new Constraint is added to the World.
*/
- this._drag = 0;
+ this.onConstraintAdded = new Phaser.Signal();
/**
- * @property {number} _dx - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onConstraintRemoved - Dispatched when a Constraint is removed from the World.
*/
- this._dx = 0;
+ this.onConstraintRemoved = new Phaser.Signal();
/**
- * @property {number} _dy - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onContactMaterialAdded - Dispatched when a new ContactMaterial is added to the World.
*/
- this._dy = 0;
+ this.onContactMaterialAdded = new Phaser.Signal();
/**
- * @property {Phaser.Point} _p - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onContactMaterialRemoved - Dispatched when a ContactMaterial is removed from the World.
*/
- this._p = new Phaser.Point(0, 0);
+ this.onContactMaterialRemoved = new Phaser.Signal();
/**
- * @property {number} _intersection - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onPostStep - Dispatched after the World.step()
*/
- this._intersection = [0,0,0,0];
+ this.onPostStep = new Phaser.Signal();
/**
- * @property {number} _gravityX - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onPostBroadphase - Dispatched after the Broadphase has collected collision pairs in the world.
*/
- this._gravityX = 0;
+ this.onPostBroadphase = new Phaser.Signal();
/**
- * @property {number} _gravityY - Internal cache var.
- * @private
+ * @property {Phaser.Signal} onImpact - Dispatched when a first contact is created between two bodies. This event is fired after the step has been done.
*/
- this._gravityY = 0;
+ this.onImpact = new Phaser.Signal();
/**
- * @property {SAT.Response} _response - Internal cache var.
+ * @property {Phaser.Signal} onBeginContact - Dispatched when a first contact is created between two bodies. This event is fired before the step has been done.
+ */
+ this.onBeginContact = new Phaser.Signal();
+
+ /**
+ * @property {Phaser.Signal} onEndContact - Dispatched when final contact occurs between two bodies. This event is fired before the step has been done.
+ */
+ this.onEndContact = new Phaser.Signal();
+
+ // Hook into the World events
+ this.world.on("postStep", this.postStepHandler, this);
+ this.world.on("postBroadphase", this.postBroadphaseHandler, this);
+ this.world.on("impact", this.impactHandler, this);
+ this.world.on("beginContact", this.beginContactHandler, this);
+ this.world.on("endContact", this.endContactHandler, this);
+
+ /**
+ * @property {array} collisionGroups - Internal var.
+ */
+ this.collisionGroups = [];
+
+ /**
+ * @property {number} _collisionGroupID - Internal var.
* @private
*/
- this._response = new SAT.Response();
+ this._collisionGroupID = 2;
- // Set the bounds to the world as default
- this.setBoundsToWorld(true, true, true, true);
+ this.boundsCollisionGroup = new Phaser.Physics.CollisionGroup(2);
+ this.boundsCollidesWith = [];
+
+ // Group vs. Group callbacks
+
+ // this.setBoundsToWorld(true, true, true, true, false);
};
-/**
-* @constant
-* @type {number}
-*/
-Phaser.Physics.Arcade.RECT = 0;
-
-/**
-* @constant
-* @type {number}
-*/
-Phaser.Physics.Arcade.CIRCLE = 1;
-
-/**
-* @constant
-* @type {number}
-*/
-Phaser.Physics.Arcade.POLYGON = 2;
-
-Phaser.Physics.Arcade.prototype = {
+Phaser.Physics.World.prototype = {
/**
- * Checks the given Physics.Body against the Physics Bounds, if any are set, and separates them, setting the blocked flags on the Body as it does so.
+ * Handles a p2 postStep event.
*
- * @method Phaser.Physics.Arcade#checkBounds
- * @param {Phaser.Physics.Arcade.Body} The Body object to be checked.
- * @return {boolean} True if the body hit the bounds, otherwise false.
+ * @method Phaser.Physics.World#postStepHandler
+ * @private
+ * @param {object} event - The event data.
*/
- checkBounds: function (body) {
-
- if (!body.collideWorldBounds || (!this.worldLeft && !this.worldRight && !this.worldTop && !this.worldBottom))
- {
- return false;
- }
-
- this._response.clear();
-
- var test = SAT.testPolygonPolygon;
- var part = body.polygon;
- var rebounded = false;
-
- if (body.type === Phaser.Physics.Arcade.CIRCLE)
- {
- test = SAT.testPolygonCircle;
- part = body.shape;
- }
-
- if (this.worldLeft && test(this.worldPolys[0], part, this._response))
- {
- body.blocked.left = true;
- part.pos.add(this._response.overlapV);
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- rebounded = true;
- }
- else if (this.worldRight && test(this.worldPolys[1], part, this._response))
- {
- body.blocked.right = true;
- part.pos.add(this._response.overlapV);
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- rebounded = true;
- }
-
- this._response.clear();
-
- if (this.worldTop && test(this.worldPolys[2], part, this._response))
- {
- body.blocked.up = true;
- part.pos.add(this._response.overlapV);
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- rebounded = true;
- }
- else if (this.worldBottom && test(this.worldPolys[3], part, this._response))
- {
- body.blocked.down = true;
- part.pos.add(this._response.overlapV);
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- rebounded = true;
- }
-
- return rebounded;
+ postStepHandler: function (event) {
},
/**
- * Sets the bounds of the Physics world to match the Game.World.
+ * Fired after the Broadphase has collected collision pairs in the world.
+ * Inside the event handler, you can modify the pairs array as you like, to prevent collisions between objects that you don't want.
+ *
+ * @method Phaser.Physics.World#postBroadphaseHandler
+ * @private
+ * @param {object} event - The event data.
+ */
+ postBroadphaseHandler: function (event) {
+
+ // Body.id 1 is always the World bounds object
+
+ for (var i = 0; i < event.pairs.length; i += 2)
+ {
+ var a = event.pairs[i];
+ var b = event.pairs[i+1];
+
+ if (a.id !== 1 && b.id !== 1)
+ {
+ // console.log('postBroadphaseHandler', a, b);
+ }
+ }
+
+ },
+
+ /**
+ * Handles a p2 impact event.
+ *
+ * @method Phaser.Physics.World#impactHandler
+ * @private
+ * @param {object} event - The event data.
+ */
+ impactHandler: function (event) {
+
+ if (event.bodyA.parent && event.bodyB.parent)
+ {
+ // Body vs. Body callbacks
+ var a = event.bodyA.parent;
+ var b = event.bodyB.parent;
+
+ if (a._bodyCallbacks[event.bodyB.id])
+ {
+ a._bodyCallbacks[event.bodyB.id].call(a._bodyCallbackContext[event.bodyB.id], a, b, event.shapeA, event.shapeB);
+ }
+
+ if (b._bodyCallbacks[event.bodyA.id])
+ {
+ b._bodyCallbacks[event.bodyA.id].call(b._bodyCallbackContext[event.bodyA.id], b, a, event.shapeB, event.shapeA);
+ }
+
+ // Body vs. Group callbacks
+ if (a._groupCallbacks[event.shapeB.collisionGroup])
+ {
+ a._groupCallbacks[event.shapeB.collisionGroup].call(a._groupCallbackContext[event.shapeB.collisionGroup], a, b, event.shapeA, event.shapeB);
+ }
+
+ if (b._groupCallbacks[event.shapeA.collisionGroup])
+ {
+ b._groupCallbacks[event.shapeA.collisionGroup].call(b._groupCallbackContext[event.shapeA.collisionGroup], b, a, event.shapeB, event.shapeA);
+ }
+ }
+
+ },
+
+ /**
+ * Handles a p2 begin contact event.
+ *
+ * @method Phaser.Physics.World#beginContactHandler
+ * @private
+ * @param {object} event - The event data.
+ */
+ beginContactHandler: function (event) {
+
+ if (event.bodyA.id > 1 && event.bodyB.id > 1)
+ {
+ // console.log('beginContactHandler');
+ // console.log(event.bodyA.parent.sprite.key);
+ // console.log(event.bodyB.parent.sprite.key);
+ }
+
+ },
+
+ /**
+ * Handles a p2 end contact event.
+ *
+ * @method Phaser.Physics.World#endContactHandler
+ * @private
+ * @param {object} event - The event data.
+ */
+ endContactHandler: function (event) {
+
+ if (event.bodyA.id > 1 && event.bodyB.id > 1)
+ {
+ // console.log('endContactHandler');
+ // console.log(event);
+ }
+
+ },
+
+ /**
+ * Sets the bounds of the Physics world to match the Game.World dimensions.
* You can optionally set which 'walls' to create: left, right, top or bottom.
*
- * @method Phaser.Physics.Arcade#setBoundsToWorld
+ * @method Phaser.Physics#setBoundsToWorld
* @param {boolean} [left=true] - If true will create the left bounds wall.
* @param {boolean} [right=true] - If true will create the right bounds wall.
* @param {boolean} [top=true] - If true will create the top bounds wall.
* @param {boolean} [bottom=true] - If true will create the bottom bounds wall.
+ * @param {boolean} [setCollisionGroup=true] - If true the Bounds will be set to use its own Collision Group.
*/
- setBoundsToWorld: function (left, right, top, bottom) {
+ setBoundsToWorld: function (left, right, top, bottom, setCollisionGroup) {
- this.setBounds(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, left, right, top, bottom);
+ this.setBounds(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, left, right, top, bottom, setCollisionGroup);
+
+ },
+
+ /**
+ * Sets the given material against the 4 bounds of this World.
+ *
+ * @method Phaser.Physics#setWorldMaterial
+ * @param {Phaser.Physics.Material} material - The material to set.
+ * @param {boolean} [left=true] - If true will set the material on the left bounds wall.
+ * @param {boolean} [right=true] - If true will set the material on the right bounds wall.
+ * @param {boolean} [top=true] - If true will set the material on the top bounds wall.
+ * @param {boolean} [bottom=true] - If true will set the material on the bottom bounds wall.
+ */
+ setWorldMaterial: function (material, left, right, top, bottom) {
+
+ if (typeof left === 'undefined') { left = true; }
+ if (typeof right === 'undefined') { right = true; }
+ if (typeof top === 'undefined') { top = true; }
+ if (typeof bottom === 'undefined') { bottom = true; }
+
+ if (left && this._wallShapes[0])
+ {
+ this._wallShapes[0].material = material;
+ }
+
+ if (right && this._wallShapes[1])
+ {
+ this._wallShapes[1].material = material;
+ }
+
+ if (top && this._wallShapes[2])
+ {
+ this._wallShapes[2].material = material;
+ }
+
+ if (bottom && this._wallShapes[3])
+ {
+ this._wallShapes[3].material = material;
+ }
},
@@ -39069,7 +51318,7 @@ Phaser.Physics.Arcade.prototype = {
* Sets the bounds of the Physics world to match the given world pixel dimensions.
* You can optionally set which 'walls' to create: left, right, top or bottom.
*
- * @method Phaser.Physics.Arcade#setBounds
+ * @method Phaser.Physics.World#setBounds
* @param {number} x - The x coordinate of the top-left corner of the bounds.
* @param {number} y - The y coordinate of the top-left corner of the bounds.
* @param {number} width - The width of the bounds.
@@ -39078,1168 +51327,710 @@ Phaser.Physics.Arcade.prototype = {
* @param {boolean} [right=true] - If true will create the right bounds wall.
* @param {boolean} [top=true] - If true will create the top bounds wall.
* @param {boolean} [bottom=true] - If true will create the bottom bounds wall.
+ * @param {boolean} [setCollisionGroup=true] - If true the Bounds will be set to use its own Collision Group.
*/
- setBounds: function (x, y, width, height, left, right, top, bottom) {
+ setBounds: function (x, y, width, height, left, right, top, bottom, setCollisionGroup) {
if (typeof left === 'undefined') { left = true; }
if (typeof right === 'undefined') { right = true; }
if (typeof top === 'undefined') { top = true; }
if (typeof bottom === 'undefined') { bottom = true; }
+ if (typeof setCollisionGroup === 'undefined') { setCollisionGroup = true; }
- var thickness = 100;
+ var hw = (width / 2);
+ var hh = (height / 2);
+ var cx = hw + x;
+ var cy = hh + y;
- if (left)
+ if (this.bounds !== null)
{
- this.worldLeft = new SAT.Box(new SAT.Vector(x - thickness, y), thickness, height);
- this.worldPolys[0] = this.worldLeft.toPolygon();
+ this.world.removeBody(this.bounds);
+
+ var i = this.bounds.shapes.length;
+
+ while (i--)
+ {
+ var shape = this.bounds.shapes[i];
+ this.bounds.removeShape(shape);
+ }
+
+ this.bounds.position[0] = this.game.math.px2p(cx);
+ this.bounds.position[1] = this.game.math.px2p(cy);
}
else
{
- this.worldLeft = null;
- this.worldPolys[0] = null;
+ this.bounds = new p2.Body({ mass: 0, position:[this.game.math.px2p(cx), this.game.math.px2p(cy)] });
+ }
+
+ if (left)
+ {
+ this._wallShapes[0] = new p2.Plane();
+
+ if (setCollisionGroup)
+ {
+ this._wallShapes[0].collisionGroup = this.boundsCollisionGroup.mask;
+ }
+
+ this.bounds.addShape(this._wallShapes[0], [this.game.math.px2p(-hw), 0], 1.5707963267948966 );
}
if (right)
{
- this.worldRight = new SAT.Box(new SAT.Vector(x + width, y), thickness, height);
- this.worldPolys[1] = this.worldRight.toPolygon();
- }
- else
- {
- this.worldRight = null;
- this.worldPolys[1] = null;
+ this._wallShapes[1] = new p2.Plane();
+
+ if (setCollisionGroup)
+ {
+ this._wallShapes[1].collisionGroup = this.boundsCollisionGroup.mask;
+ }
+
+ this.bounds.addShape(this._wallShapes[1], [this.game.math.px2p(hw), 0], -1.5707963267948966 );
}
if (top)
{
- this.worldTop = new SAT.Box(new SAT.Vector(x, y - thickness), width, thickness);
- this.worldPolys[2] = this.worldTop.toPolygon();
- }
- else
- {
- this.worldTop = null;
- this.worldPolys[2] = null;
+ this._wallShapes[2] = new p2.Plane();
+
+ if (setCollisionGroup)
+ {
+ this._wallShapes[2].collisionGroup = this.boundsCollisionGroup.mask;
+ }
+
+ this.bounds.addShape(this._wallShapes[2], [0, this.game.math.px2p(-hh)], -3.141592653589793 );
}
if (bottom)
{
- this.worldBottom = new SAT.Box(new SAT.Vector(x, y + height), width, thickness);
- this.worldPolys[3] = this.worldBottom.toPolygon();
- }
- else
- {
- this.worldBottom = null;
- this.worldPolys[3] = null;
+ this._wallShapes[3] = new p2.Plane();
+
+ if (setCollisionGroup)
+ {
+ this._wallShapes[3].collisionGroup = this.boundsCollisionGroup.mask;
+ }
+
+ this.bounds.addShape(this._wallShapes[3], [0, this.game.math.px2p(hh)] );
}
+ this.world.addBody(this.bounds);
+
},
/**
- * Called automatically by a Physics body, it updates all motion related values on the Body.
+ * @method Phaser.Physics.World#update
+ */
+ update: function () {
+
+ this.world.step(1 / 60);
+
+ },
+
+ /**
+ * @method Phaser.Physics.World#destroy
+ */
+ destroy: function () {
+
+ this.world.clear();
+
+ this.game = null;
+
+ },
+
+ /**
+ * Add a body to the world.
*
- * @method Phaser.Physics.Arcade#updateMotion
- * @param {Phaser.Physics.Arcade.Body} The Body object to be updated.
+ * @method Phaser.Physics.World#addBody
+ * @param {Phaser.Physics.Body} body - The Body to add to the World.
+ * @return {boolean} True if the Body was added successfully, otherwise false.
*/
- updateMotion: function (body) {
+ addBody: function (body) {
- // If you're wondering why the velocity is halved and applied twice, read this: http://www.niksula.hut.fi/~hkankaan/Homepages/gravity.html
-
- // World gravity is allowed
- if (body.allowGravity)
- {
- this._gravityX = this.gravity.x + body.gravity.x;
- this._gravityY = this.gravity.y + body.gravity.y;
- }
- else
- {
- this._gravityX = body.gravity.x;
- this._gravityY = body.gravity.y;
- }
-
- // Don't apply gravity to any body that is blocked
- if ((this._gravityX < 0 && body.blocked.left) || (this._gravityX > 0 && body.blocked.right))
- {
- this._gravityX = 0;
- }
-
- if ((this._gravityY < 0 && body.blocked.up) || (this._gravityY > 0 && body.blocked.down))
- {
- this._gravityY = 0;
- }
-
- // Rotation
- if (body.allowRotation)
- {
- this._velocityDelta = body.angularAcceleration * this.game.time.physicsElapsed;
-
- if (body.angularDrag !== 0 && body.angularAcceleration === 0)
- {
- this._drag = body.angularDrag * this.game.time.physicsElapsed;
-
- if (body.angularVelocity > 0)
- {
- body.angularVelocity -= this._drag;
- }
- else if (body.angularVelocity < 0)
- {
- body.angularVelocity += this._drag;
- }
- }
-
- body.rotation += this.game.time.physicsElapsed * (body.angularVelocity + this._velocityDelta / 2);
- body.angularVelocity += this._velocityDelta;
-
- if (body.angularVelocity > body.maxAngular)
- {
- body.angularVelocity = body.maxAngular;
- }
- else if (body.angularVelocity < -body.maxAngular)
- {
- body.angularVelocity = -body.maxAngular;
- }
- }
-
- // temp = acc*dt
- // pos = pos + dt*(vel + temp/2)
- // vel = vel + temp
-
- this._p.setTo((body.acceleration.x + this._gravityX) * this.game.time.physicsElapsed, (body.acceleration.y + this._gravityY) * this.game.time.physicsElapsed);
-
- return this._p;
-
- },
-
- /**
- * Checks for overlaps between two game objects. The objects can be Sprites, Groups or Emitters.
- * You can perform Sprite vs. Sprite, Sprite vs. Group and Group vs. Group overlap checks.
- * Unlike collide the objects are NOT automatically separated or have any physics applied, they merely test for overlap results.
- * The second parameter can be an array of objects, of differing types.
- *
- * @method Phaser.Physics.Arcade#overlap
- * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter.
- * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group or Phaser.Particles.Emitter.
- * @param {function} [overlapCallback=null] - An optional callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you specified them.
- * @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then overlapCallback will only be called if processCallback returns true.
- * @param {object} [callbackContext] - The context in which to run the callbacks.
- * @returns {boolean} True if an overlap occured otherwise false.
- */
- overlap: function (object1, object2, overlapCallback, processCallback, callbackContext) {
-
- overlapCallback = overlapCallback || null;
- processCallback = processCallback || null;
- callbackContext = callbackContext || overlapCallback;
-
- this._result = false;
- this._total = 0;
-
- if (Array.isArray(object2))
- {
- for (var i = 0, len = object2.length; i < len; i++)
- {
- this.collideHandler(object1, object2[i], overlapCallback, processCallback, callbackContext, true);
- }
- }
- else
- {
- this.collideHandler(object1, object2, overlapCallback, processCallback, callbackContext, true);
- }
-
- return (this._total > 0);
-
- },
-
- /**
- * Checks for collision between two game objects. You can perform Sprite vs. Sprite, Sprite vs. Group, Group vs. Group, Sprite vs. Tilemap Layer or Group vs. Tilemap Layer collisions.
- * The second parameter can be an array of objects, of differing types.
- * The objects are also automatically separated. If you don't require separation then use ArcadePhysics.overlap instead.
- * An optional processCallback can be provided. If given this function will be called when two sprites are found to be colliding. It is called before any separation takes place,
- * giving you the chance to perform additional checks. If the function returns true then the collision and separation is carried out. If it returns false it is skipped.
- * The collideCallback is an optional function that is only called if two sprites collide. If a processCallback has been set then it needs to return true for collideCallback to be called.
- *
- * @method Phaser.Physics.Arcade#collide
- * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap.
- * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap|array} object2 - The second object or array of objects to check. Can be Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap.
- * @param {function} [collideCallback=null] - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them.
- * @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them.
- * @param {object} [callbackContext] - The context in which to run the callbacks.
- * @returns {boolean} True if a collision occured otherwise false.
- */
- collide: function (object1, object2, collideCallback, processCallback, callbackContext) {
-
- collideCallback = collideCallback || null;
- processCallback = processCallback || null;
- callbackContext = callbackContext || collideCallback;
-
- this._result = false;
- this._total = 0;
-
- if (Array.isArray(object2))
- {
- for (var i = 0, len = object2.length; i < len; i++)
- {
- this.collideHandler(object1, object2[i], collideCallback, processCallback, callbackContext, false);
- }
- }
- else
- {
- this.collideHandler(object1, object2, collideCallback, processCallback, callbackContext, false);
- }
-
- return (this._total > 0);
-
- },
-
- /**
- * Internal collision handler.
- *
- * @method Phaser.Physics.Arcade#collideHandler
- * @private
- * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object1 - The first object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter, or Phaser.Tilemap.
- * @param {Phaser.Sprite|Phaser.Group|Phaser.Particles.Emitter|Phaser.Tilemap} object2 - The second object to check. Can be an instance of Phaser.Sprite, Phaser.Group, Phaser.Particles.Emitter or Phaser.Tilemap. Can also be an array of objects to check.
- * @param {function} collideCallback - An optional callback function that is called if the objects collide. The two objects will be passed to this function in the same order in which you specified them.
- * @param {function} processCallback - A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then collision will only happen if processCallback returns true. The two objects will be passed to this function in the same order in which you specified them.
- * @param {object} callbackContext - The context in which to run the callbacks.
- * @param {boolean} overlapOnly - Just run an overlap or a full collision.
- */
- collideHandler: function (object1, object2, collideCallback, processCallback, callbackContext, overlapOnly) {
-
- // Only collide valid objects
- if (typeof object2 === 'undefined' && (object1.type === Phaser.GROUP || object1.type === Phaser.EMITTER))
- {
- this.collideGroupVsSelf(object1, collideCallback, processCallback, callbackContext, overlapOnly);
- return;
- }
-
- if (object1 && object2 && object1.exists && object2.exists)
- {
- // SPRITES
- if (object1.type == Phaser.SPRITE || object1.type == Phaser.TILESPRITE)
- {
- if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE)
- {
- this.collideSpriteVsSprite(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly);
- }
- else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER)
- {
- this.collideSpriteVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly);
- }
- else if (object2.type == Phaser.TILEMAPLAYER)
- {
- this.collideSpriteVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext);
- }
- }
- // GROUPS
- else if (object1.type == Phaser.GROUP)
- {
- if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE)
- {
- this.collideSpriteVsGroup(object2, object1, collideCallback, processCallback, callbackContext, overlapOnly);
- }
- else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER)
- {
- this.collideGroupVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly);
- }
- else if (object2.type == Phaser.TILEMAPLAYER)
- {
- this.collideGroupVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext);
- }
- }
- // TILEMAP LAYERS
- else if (object1.type == Phaser.TILEMAPLAYER)
- {
- if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE)
- {
- this.collideSpriteVsTilemapLayer(object2, object1, collideCallback, processCallback, callbackContext);
- }
- else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER)
- {
- this.collideGroupVsTilemapLayer(object2, object1, collideCallback, processCallback, callbackContext);
- }
- }
- // EMITTER
- else if (object1.type == Phaser.EMITTER)
- {
- if (object2.type == Phaser.SPRITE || object2.type == Phaser.TILESPRITE)
- {
- this.collideSpriteVsGroup(object2, object1, collideCallback, processCallback, callbackContext, overlapOnly);
- }
- else if (object2.type == Phaser.GROUP || object2.type == Phaser.EMITTER)
- {
- this.collideGroupVsGroup(object1, object2, collideCallback, processCallback, callbackContext, overlapOnly);
- }
- else if (object2.type == Phaser.TILEMAPLAYER)
- {
- this.collideGroupVsTilemapLayer(object1, object2, collideCallback, processCallback, callbackContext);
- }
- }
- }
-
- },
-
- /**
- * An internal function. Use Phaser.Physics.Arcade.collide instead.
- *
- * @method Phaser.Physics.Arcade#collideSpriteVsSprite
- * @private
- */
- collideSpriteVsSprite: function (sprite1, sprite2, collideCallback, processCallback, callbackContext, overlapOnly) {
-
- if (this.separate(sprite1.body, sprite2.body, processCallback, callbackContext, overlapOnly))
- {
- if (collideCallback)
- {
- collideCallback.call(callbackContext, sprite1, sprite2);
- }
-
- this._total++;
- }
-
- },
-
- /**
- * An internal function. Use Phaser.Physics.Arcade.collide instead.
- *
- * @method Phaser.Physics.Arcade#collideSpriteVsGroup
- * @private
- */
- collideSpriteVsGroup: function (sprite, group, collideCallback, processCallback, callbackContext, overlapOnly) {
-
- if (group.length === 0)
- {
- return;
- }
-
- // What is the sprite colliding with in the quadtree?
- this.quadTree.clear();
-
- this.quadTree = new Phaser.QuadTree(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height, this.maxObjects, this.maxLevels);
-
- this.quadTree.populate(group);
-
- this._potentials = this.quadTree.retrieve(sprite);
-
- for (var i = 0, len = this._potentials.length; i < len; i++)
- {
- // We have our potential suspects, are they in this group?
- if (this.separate(sprite.body, this._potentials[i], processCallback, callbackContext, overlapOnly))
- {
- if (collideCallback)
- {
- collideCallback.call(callbackContext, sprite, this._potentials[i].sprite);
- }
-
- this._total++;
- }
- }
-
- },
-
- /**
- * An internal function. Use Phaser.Physics.Arcade.collide instead.
- *
- * @method Phaser.Physics.Arcade#collideGroupVsSelf
- * @private
- */
- collideGroupVsSelf: function (group, collideCallback, processCallback, callbackContext, overlapOnly) {
-
- if (group.length === 0)
- {
- return;
- }
-
- var len = group._container.children.length;
-
- for (var i = 0; i < len; i++)
- {
- for (var j = i + 1; j <= len; j++)
- {
- if (group._container.children[i] && group._container.children[j] && group._container.children[i].exists && group._container.children[j].exists)
- {
- this.collideSpriteVsSprite(group._container.children[i], group._container.children[j], collideCallback, processCallback, callbackContext, overlapOnly);
- }
- }
- }
-
- },
-
- /**
- * An internal function. Use Phaser.Physics.Arcade.collide instead.
- *
- * @method Phaser.Physics.Arcade#collideGroupVsGroup
- * @private
- */
- collideGroupVsGroup: function (group1, group2, collideCallback, processCallback, callbackContext, overlapOnly) {
-
- if (group1.length === 0 || group2.length === 0)
- {
- return;
- }
-
- if (group1._container.first._iNext)
- {
- var currentNode = group1._container.first._iNext;
-
- do
- {
- if (currentNode.exists)
- {
- this.collideSpriteVsGroup(currentNode, group2, collideCallback, processCallback, callbackContext, overlapOnly);
- }
- currentNode = currentNode._iNext;
- }
- while (currentNode != group1._container.last._iNext);
- }
-
- },
-
- /**
- * An internal function. Use Phaser.Physics.Arcade.collide instead.
- *
- * @method Phaser.Physics.Arcade#collideSpriteVsTilemapLayer
- * @private
- */
- collideSpriteVsTilemapLayer: function (sprite, tilemapLayer, collideCallback, processCallback, callbackContext) {
-
- this._mapData = tilemapLayer.getTiles(sprite.body.left, sprite.body.top, sprite.body.width, sprite.body.height, true);
-
- if (this._mapData.length === 0)
- {
- return;
- }
-
- if (this._mapData.length > 1)
- {
- this.separateTiles(sprite.body, this._mapData);
- }
- else
- {
- var i = 0;
-
- if (this.separateTile(sprite.body, this._mapData[i]))
- {
- // They collided, is there a custom process callback?
- if (processCallback)
- {
- if (processCallback.call(callbackContext, sprite, this._mapData[i]))
- {
- this._total++;
-
- if (collideCallback)
- {
- collideCallback.call(callbackContext, sprite, this._mapData[i]);
- }
- }
- }
- else
- {
- this._total++;
-
- if (collideCallback)
- {
- collideCallback.call(callbackContext, sprite, this._mapData[i]);
- }
- }
- }
- }
-
- },
-
- /**
- * An internal function. Use Phaser.Physics.Arcade.collide instead.
- *
- * @method Phaser.Physics.Arcade#collideGroupVsTilemapLayer
- * @private
- */
- collideGroupVsTilemapLayer: function (group, tilemapLayer, collideCallback, processCallback, callbackContext) {
-
- if (group.length === 0)
- {
- return;
- }
-
- if (group._container.first._iNext)
- {
- var currentNode = group._container.first._iNext;
-
- do
- {
- if (currentNode.exists)
- {
- this.collideSpriteVsTilemapLayer(currentNode, tilemapLayer, collideCallback, processCallback, callbackContext);
- }
- currentNode = currentNode._iNext;
- }
- while (currentNode != group._container.last._iNext);
- }
-
- },
-
- /**
- * The core separation function to separate two physics bodies.
- * @method Phaser.Physics.Arcade#separate
- * @param {Phaser.Physics.Arcade.Body} body1 - The Body object to separate.
- * @param {Phaser.Physics.Arcade.Body} body2 - The Body object to separate.
- * @param {function} [processCallback=null] - A callback function that lets you perform additional checks against the two objects if they overlap. If this function is set then the sprites will only be collided if it returns true.
- * @param {object} [callbackContext] - The context in which to run the process callback.
- * @returns {boolean} Returns true if the bodies collided, otherwise false.
- */
- separate: function (body1, body2, processCallback, callbackContext, overlapOnly) {
-
- if (body1 === body2 || this.intersects(body1, body2) === false)
+ if (body.data.world)
{
return false;
}
-
- // They overlap. Is there a custom process callback? If it returns true then we can carry on, otherwise we should abort.
- if (processCallback && processCallback.call(callbackContext, body1.sprite, body2.sprite) === false)
- {
- return false;
- }
-
- this._response.clear();
-
- if (overlapOnly)
- {
- return body1.overlap(body2, this._response);
- }
else
{
- if (body1.overlap(body2, this._response))
- {
- return body1.separate(body2, this._response);
- }
- }
+ this.world.addBody(body.data);
- return false;
+ this.onBodyAdded.dispatch(body);
- },
-
- /**
- * Performs a rect intersection test against the two objects.
- * Objects must expose properties: width, height, left, right, top, bottom.
- * @method Phaser.Physics.Arcade#intersects
- * @param {object} a - The first object to test.
- * @param {object} b - The second object to test.
- * @returns {boolean} Returns true if the objects intersect, otherwise false.
- */
- intersects: function (a, b) {
-
- var result = false;
-
- if (a.width <= 0 || a.height <= 0 || b.width <= 0 || b.height <= 0)
- {
- result = false;
- }
-
- result = !(a.right < b.left || a.bottom < b.top || a.left > b.right || a.top > b.bottom);
-
- if (!result && a.inContact(b))
- {
- a.removeContact(b);
+ return true;
}
},
/**
- * Performs a rect intersection test against the two objects.
- * Objects must expose properties: width, height, left, right, top, bottom.
- * @method Phaser.Physics.Arcade#tileIntersects
- * @param {object} body - The Body to test.
- * @param {object} tile - The Tile to test.
- * @returns {boolean} Returns true if the objects intersect, otherwise false.
+ * Removes a body from the world.
+ *
+ * @method Phaser.Physics.World#removeBody
+ * @param {Phaser.Physics.Body} body - The Body to remove from the World.
+ * @return {Phaser.Physics.Body} The Body that was removed.
*/
- tileIntersects: function (body, tile) {
+ removeBody: function (body) {
- if (body.width <= 0 || body.height <= 0 || tile.width <= 0 || tile.height <= 0)
- {
- this._intersection[4] = 0;
- return this._intersection;
- }
+ this.world.removeBody(body.data);
- if (!(body.right < tile.x || body.bottom < tile.y || body.left > tile.right || body.top > tile.bottom))
- {
- this._intersection[0] = Math.max(body.left, tile.x); // x
- this._intersection[1] = Math.max(body.top, tile.y); // y
- this._intersection[2] = Math.min(body.right, tile.right) - this._intersection[0]; // width
- this._intersection[3] = Math.min(body.bottom, tile.bottom) - this._intersection[1]; // height
- this._intersection[4] = 1;
+ this.onBodyRemoved.dispatch(body);
- return this._intersection;
- }
-
- this._intersection[4] = 0;
-
- return this._intersection;
+ return body;
},
/**
- * The core separation function to separate a physics body and an array of tiles.
- * @method Phaser.Physics.Arcade#separateTiles
- * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate.
- * @param {array} tiles - The array of tiles to collide against.
- * @returns {boolean} Returns true if the body was separated, otherwise false.
+ * Adds a Spring to the world.
+ *
+ * @method Phaser.Physics.World#addSpring
+ * @param {Phaser.Physics.Spring} spring - The Spring to add to the World.
+ * @return {Phaser.Physics.Spring} The Spring that was added.
*/
- separateTiles: function (body, tiles) {
+ addSpring: function (spring) {
- var tile;
- var result = false;
+ this.world.addSpring(spring);
- for (var i = 0; i < tiles.length; i++)
- {
- tile = tiles[i];
+ this.onSpringAdded.dispatch(spring);
- if (this.separateTile(body, tile))
- {
- result = true;
- }
- }
-
- return result;
+ return spring;
},
/**
- * The core separation function to separate a physics body and a tile.
- * @method Phaser.Physics.Arcade#separateTile
- * @param {Phaser.Physics.Arcade.Body} body - The Body object to separate.
- * @param {Phaser.Tile} tile - The tile to collide against.
- * @returns {boolean} Returns true if the body was separated, otherwise false.
+ * Removes a Spring from the world.
+ *
+ * @method Phaser.Physics.World#removeSpring
+ * @param {Phaser.Physics.Spring} spring - The Spring to remove from the World.
+ * @return {Phaser.Physics.Spring} The Spring that was removed.
*/
- separateTile: function (body, tile) {
+ removeSpring: function (spring) {
- this._intersection = this.tileIntersects(body, tile);
+ this.world.removeSpring(spring);
- // If the intersection area is either entirely null, or has a width/height of zero, we bail out now
- if (this._intersection[4] === 0 || this._intersection[2] === 0 || this._intersection[3] === 0)
+ this.onSpringRemoved.dispatch(spring);
+
+ return spring;
+
+ },
+
+ /**
+ * Adds a Constraint to the world.
+ *
+ * @method Phaser.Physics.World#addConstraint
+ * @param {Phaser.Physics.Constraint} constraint - The Constraint to add to the World.
+ * @return {Phaser.Physics.Constraint} The Constraint that was added.
+ */
+ addConstraint: function (constraint) {
+
+ this.world.addConstraint(constraint);
+
+ this.onConstraintAdded.dispatch(constraint);
+
+ return constraint;
+
+ },
+
+ /**
+ * Removes a Constraint from the world.
+ *
+ * @method Phaser.Physics.World#removeConstraint
+ * @param {Phaser.Physics.Constraint} constraint - The Constraint to be removed from the World.
+ * @return {Phaser.Physics.Constraint} The Constraint that was removed.
+ */
+ removeConstraint: function (constraint) {
+
+ this.world.removeConstraint(constraint);
+
+ this.onConstraintRemoved.dispatch(constraint);
+
+ return constraint;
+
+ },
+
+ /**
+ * Adds a Contact Material to the world.
+ *
+ * @method Phaser.Physics.World#addContactMaterial
+ * @param {Phaser.Physics.ContactMaterial} material - The Contact Material to be added to the World.
+ * @return {Phaser.Physics.ContactMaterial} The Contact Material that was added.
+ */
+ addContactMaterial: function (material) {
+
+ this.world.addContactMaterial(material);
+
+ this.onContactMaterialAdded.dispatch(material);
+
+ return material;
+
+ },
+
+ /**
+ * Removes a Contact Material from the world.
+ *
+ * @method Phaser.Physics.World#removeContactMaterial
+ * @param {Phaser.Physics.ContactMaterial} material - The Contact Material to be removed from the World.
+ * @return {Phaser.Physics.ContactMaterial} The Contact Material that was removed.
+ */
+ removeContactMaterial: function (material) {
+
+ this.world.removeContactMaterial(material);
+
+ this.onContactMaterialRemoved.dispatch(material);
+
+ return material;
+
+ },
+
+ /**
+ * Gets a Contact Material based on the two given Materials.
+ *
+ * @method Phaser.Physics.World#getContactMaterial
+ * @param {Phaser.Physics.Material} materialA - The first Material to search for.
+ * @param {Phaser.Physics.Material} materialB - The second Material to search for.
+ * @return {Phaser.Physics.ContactMaterial|boolean} The Contact Material or false if none was found matching the Materials given.
+ */
+ getContactMaterial: function (materialA, materialB) {
+
+ return this.world.getContactMaterial(materialA, materialB);
+
+ },
+
+ /**
+ * Sets the given Material against all Shapes owned by all the Bodies in the given array.
+ *
+ * @method Phaser.Physics.World#setMaterial
+ * @param {Phaser.Physics.Material} material - The Material to be applied to the given Bodies.
+ * @param {array} bodies - An Array of Body objects that the given Material will be set on.
+ */
+ setMaterial: function (material, bodies) {
+
+ var i = bodies.length;
+
+ while (i--)
{
- return false;
+ bodies.setMaterial(material);
}
- // They overlap. Any custom callbacks?
- if (tile.tile.callback || tile.layer.callbacks[tile.tile.index])
+ },
+
+ /**
+ * Creates a Material. Materials are applied to Shapes owned by a Body and can be set with Body.setMaterial().
+ * Materials are a way to control what happens when Shapes collide. Combine unique Materials together to create Contact Materials.
+ * Contact Materials have properties such as friction and restitution that allow for fine-grained collision control between different Materials.
+ *
+ * @method Phaser.Physics.World#createMaterial
+ * @param {string} [name] - Optional name of the Material. Each Material has a unique ID but string names are handy for debugging.
+ * @param {Phaser.Physics.Body} [body] - Optional Body. If given it will assign the newly created Material to the Body shapes.
+ * @return {Phaser.Physics.Material} The Material that was created. This is also stored in Phaser.Physics.World.materials.
+ */
+ createMaterial: function (name, body) {
+
+ name = name || '';
+
+ var material = new Phaser.Physics.Material(name);
+
+ this.materials.push(material);
+
+ if (typeof body !== 'undefined')
{
- // A local callback takes priority over a global callback.
- if (tile.tile.callback && tile.tile.callback.call(tile.tile.callbackContext, body.sprite, tile) === false)
+ body.setMaterial(material);
+ }
+
+ return material;
+
+ },
+
+ /**
+ * Creates a Contact Material from the two given Materials. You can then edit the properties of the Contact Material directly.
+ *
+ * @method Phaser.Physics.World#createContactMaterial
+ * @param {Phaser.Physics.Material} [materialA] - The first Material to create the ContactMaterial from. If undefined it will create a new Material object first.
+ * @param {Phaser.Physics.Material} [materialB] - The second Material to create the ContactMaterial from. If undefined it will create a new Material object first.
+ * @param {object} [options] - Material options object.
+ * @return {Phaser.Physics.ContactMaterial} The Contact Material that was created.
+ */
+ createContactMaterial: function (materialA, materialB, options) {
+
+ if (typeof materialA === 'undefined') { materialA = this.createMaterial(); }
+ if (typeof materialB === 'undefined') { materialB = this.createMaterial(); }
+
+ var contact = new Phaser.Physics.ContactMaterial(materialA, materialB, options);
+
+ return this.addContactMaterial(contact);
+
+ },
+
+ /**
+ * Populates and returns an array of all current Bodies in the world.
+ *
+ * @method Phaser.Physics.World#getBodies
+ * @return {array} An array containing all current Bodies in the world.
+ */
+ getBodies: function () {
+
+ var output = [];
+ var i = this.world.bodies.length;
+
+ while (i--)
+ {
+ output.push(this.world.bodies[i].parent);
+ }
+
+ return output;
+
+ },
+
+ /**
+ * Populates and returns an array of all current Springs in the world.
+ *
+ * @method Phaser.Physics.World#getSprings
+ * @return {array} An array containing all current Springs in the world.
+ */
+ getSprings: function () {
+
+ var output = [];
+ var i = this.world.springs.length;
+
+ while (i--)
+ {
+ output.push(this.world.springs[i]);
+ }
+
+ return output;
+
+ },
+
+ /**
+ * Populates and returns an array of all current Constraints in the world.
+ *
+ * @method Phaser.Physics.World#getConstraints
+ * @return {array} An array containing all current Constraints in the world.
+ */
+ getConstraints: function () {
+
+ var output = [];
+ var i = this.world.constraints.length;
+
+ while (i--)
+ {
+ output.push(this.world.springs[i]);
+ }
+
+ return output;
+
+ },
+
+ /**
+ * Test if a world point overlaps bodies.
+ *
+ * @method Phaser.Physics.World#hitTest
+ * @param {Phaser.Point} worldPoint - Point to use for intersection tests.
+ * @param {Array} bodies - A list of objects to check for intersection.
+ * @param {number} precision - Used for matching against particles and lines. Adds some margin to these infinitesimal objects.
+ * @return {Array} Array of bodies that overlap the point.
+ */
+ hitTest: function (worldPoint, bodies, precision) {
+
+ },
+
+ /**
+ * Converts the current world into a JSON object.
+ *
+ * @method Phaser.Physics.World#toJSON
+ * @return {object} A JSON representation of the world.
+ */
+ toJSON: function () {
+
+ this.world.toJSON();
+
+ },
+
+ createCollisionGroup: function () {
+
+ var bitmask = Math.pow(2, this._collisionGroupID);
+
+ if (this._wallShapes[0])
+ {
+ this._wallShapes[0].collisionMask = this._wallShapes[0].collisionMask | bitmask;
+ }
+
+ if (this._wallShapes[1])
+ {
+ this._wallShapes[1].collisionMask = this._wallShapes[1].collisionMask | bitmask;
+ }
+
+ if (this._wallShapes[2])
+ {
+ this._wallShapes[2].collisionMask = this._wallShapes[2].collisionMask | bitmask;
+ }
+
+ if (this._wallShapes[3])
+ {
+ this._wallShapes[3].collisionMask = this._wallShapes[3].collisionMask | bitmask;
+ }
+
+ this._collisionGroupID++;
+
+ var group = new Phaser.Physics.CollisionGroup(bitmask);
+
+ this.collisionGroups.push(group);
+
+ return group;
+
+ },
+
+ /**
+ * @method Phaser.Physics.World.prototype.createBody
+ * @param {number} x - The x coordinate of Body.
+ * @param {number} y - The y coordinate of Body.
+ * @param {number} mass - The mass of the Body. A mass of 0 means a 'static' Body is created.
+ * @param {boolean} [addToWorld=false] - Automatically add this Body to the world? (usually false as it won't have any shapes on construction).
+ * @param {object} options - An object containing the build options:
+ * @param {boolean} [options.optimalDecomp=false] - Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
+ * @param {boolean} [options.skipSimpleCheck=false] - Set to true if you already know that the path is not intersecting itself.
+ * @param {boolean|number} [options.removeCollinearPoints=false] - Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
+ * @param {(number[]|...number)} points - An array of 2d vectors that form the convex or concave polygon.
+ * Either [[0,0], [0,1],...] or a flat array of numbers that will be interpreted as [x,y, x,y, ...],
+ * or the arguments passed can be flat x,y values e.g. `setPolygon(options, x,y, x,y, x,y, ...)` where `x` and `y` are numbers.
+ */
+ createBody: function (x, y, mass, addToWorld, options, data) {
+
+ if (typeof addToWorld === 'undefined') { addToWorld = false; }
+
+ var body = new Phaser.Physics.Body(this.game, null, x, y, mass);
+
+ if (data)
+ {
+ var result = body.addPolygon(options, data);
+
+ if (!result)
{
- // Is there a tile specific collision callback? If it returns true then we can carry on, otherwise we should abort.
- return false;
- }
- else if (tile.layer.callbacks[tile.tile.index] && tile.layer.callbacks[tile.tile.index].callback.call(tile.layer.callbacks[tile.tile.index].callbackContext, body.sprite, tile) === false)
- {
- // Is there a tile index collision callback? If it returns true then we can carry on, otherwise we should abort.
return false;
}
}
- body.overlapX = 0;
- body.overlapY = 0;
-
- var process = false;
-
- if (body.deltaX() < 0 && body.checkCollision.left && tile.tile.faceRight && !body.blocked.left)
+ if (addToWorld)
{
- // LEFT
- body.overlapX = body.left - tile.right;
-
- if (body.overlapX < 0)
- {
- process = true;
- }
- else
- {
- body.overlapX = 0;
- }
+ this.world.addBody(body.data);
}
- else if (body.deltaX() > 0 && body.checkCollision.right && tile.tile.faceLeft && !body.blocked.right)
- {
- // RIGHT
- body.overlapX = body.right - tile.x;
- if (body.overlapX > 0)
+ return body;
+
+ },
+
+ /**
+ * @method Phaser.Physics.World.prototype.createBody
+ * @param {number} x - The x coordinate of Body.
+ * @param {number} y - The y coordinate of Body.
+ * @param {number} mass - The mass of the Body. A mass of 0 means a 'static' Body is created.
+ * @param {boolean} [addToWorld=false] - Automatically add this Body to the world? (usually false as it won't have any shapes on construction).
+ * @param {object} options - An object containing the build options:
+ * @param {boolean} [options.optimalDecomp=false] - Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
+ * @param {boolean} [options.skipSimpleCheck=false] - Set to true if you already know that the path is not intersecting itself.
+ * @param {boolean|number} [options.removeCollinearPoints=false] - Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
+ * @param {(number[]|...number)} points - An array of 2d vectors that form the convex or concave polygon.
+ * Either [[0,0], [0,1],...] or a flat array of numbers that will be interpreted as [x,y, x,y, ...],
+ * or the arguments passed can be flat x,y values e.g. `setPolygon(options, x,y, x,y, x,y, ...)` where `x` and `y` are numbers.
+ */
+ createParticle: function (x, y, mass, addToWorld, options, data) {
+
+ if (typeof addToWorld === 'undefined') { addToWorld = false; }
+
+ var body = new Phaser.Physics.Body(this.game, null, x, y, mass);
+
+ if (data)
+ {
+ var result = body.addPolygon(options, data);
+
+ if (!result)
{
- process = true;
- }
- else
- {
- body.overlapX = 0;
+ return false;
}
}
- if (body.deltaY() < 0 && body.checkCollision.up && tile.tile.faceBottom && !body.blocked.up)
+ if (addToWorld)
{
- // UP
- body.overlapY = body.top - tile.bottom;
-
- if (body.overlapY < 0)
- {
- process = true;
- }
- else
- {
- body.overlapY = 0;
- }
- }
- else if (body.deltaY() > 0 && body.checkCollision.down && tile.tile.faceTop && !body.blocked.down)
- {
- // DOWN
- body.overlapY = body.bottom - tile.y;
-
- if (body.overlapY > 0)
- {
- process = true;
- }
- else
- {
- body.overlapY = 0;
- }
+ this.world.addBody(body.data);
}
- // Only separate on the smallest of the two values if it's a single tile
- if (body.overlapX !== 0 && body.overlapY !== 0)
- {
- if (Math.abs(body.overlapX) > Math.abs(body.overlapY))
- {
- body.overlapX = 0;
- }
- else
- {
- body.overlapY = 0;
- }
- }
-
- // Separate in a single sweep
- if (process)
- {
- return this.processTileSeparation(body);
- }
- else
- {
- return false;
- }
+ return body;
},
- /**
- * Internal function to process the separation of a physics body from a tile.
- * @method Phaser.Physics.Arcade#processTileSeparation
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body1 - The Body object to separate.
- * @returns {boolean} Returns true if separated, false if not.
- */
- processTileSeparation: function (body) {
- if (body.overlapX < 0)
- {
- body.x -= body.overlapX;
- body.left -= body.overlapX;
- body.right -= body.overlapX;
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- body.blocked.left = true;
- }
- else if (body.overlapX > 0)
- {
- body.x -= body.overlapX;
- body.left -= body.overlapX;
- body.right -= body.overlapX;
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- body.blocked.right = true;
- }
-
- if (body.overlapY < 0)
- {
- body.y -= body.overlapY;
- body.top -= body.overlapY;
- body.bottom -= body.overlapY;
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- body.blocked.up = true;
- }
- else if (body.overlapY > 0)
- {
- body.y -= body.overlapY;
- body.top -= body.overlapY;
- body.bottom -= body.overlapY;
- body.blocked.x = Math.floor(body.x);
- body.blocked.y = Math.floor(body.y);
- body.blocked.down = true;
- }
-
- body.reboundCheck(body.overlapX, body.overlapY, true);
-
- return true;
-
- },
-
- /**
- * Move the given display object towards the destination object at a steady velocity.
- * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
- * Timings are approximate due to the way browser timers work. Allow for a variance of +- 50ms.
- * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course.
- * Note: The display object doesn't stop moving once it reaches the destination coordinates.
- * Note: Doesn't take into account acceleration, maxVelocity or drag (if you've set drag or acceleration too high this object may not move at all)
- *
- * @method Phaser.Physics.Arcade#moveToObject
- * @param {any} displayObject - The display object to move.
- * @param {any} destination - The display object to move towards. Can be any object but must have visible x/y properties.
- * @param {number} [speed=60] - The speed it will move, in pixels per second (default is 60 pixels/sec)
- * @param {number} [maxTime=0] - Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the object will arrive at destination in the given number of ms.
- * @return {number} The angle (in radians) that the object should be visually set to in order to match its new velocity.
- */
- moveToObject: function (displayObject, destination, speed, maxTime) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- if (typeof maxTime === 'undefined') { maxTime = 0; }
-
- this._angle = Math.atan2(destination.y - displayObject.y, destination.x - displayObject.x);
-
- if (maxTime > 0)
- {
- // We know how many pixels we need to move, but how fast?
- speed = this.distanceBetween(displayObject, destination) / (maxTime / 1000);
- }
-
- displayObject.body.velocity.x = Math.cos(this._angle) * speed;
- displayObject.body.velocity.y = Math.sin(this._angle) * speed;
-
- return this._angle;
-
- },
-
- /**
- * Move the given display object towards the pointer at a steady velocity. If no pointer is given it will use Phaser.Input.activePointer.
- * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
- * Timings are approximate due to the way browser timers work. Allow for a variance of +- 50ms.
- * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course.
- * Note: The display object doesn't stop moving once it reaches the destination coordinates.
- *
- * @method Phaser.Physics.Arcade#moveToPointer
- * @param {any} displayObject - The display object to move.
- * @param {number} [speed=60] - The speed it will move, in pixels per second (default is 60 pixels/sec)
- * @param {Phaser.Pointer} [pointer] - The pointer to move towards. Defaults to Phaser.Input.activePointer.
- * @param {number} [maxTime=0] - Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the object will arrive at destination in the given number of ms.
- * @return {number} The angle (in radians) that the object should be visually set to in order to match its new velocity.
- */
- moveToPointer: function (displayObject, speed, pointer, maxTime) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- pointer = pointer || this.game.input.activePointer;
- if (typeof maxTime === 'undefined') { maxTime = 0; }
-
- this._angle = this.angleToPointer(displayObject, pointer);
-
- if (maxTime > 0)
- {
- // We know how many pixels we need to move, but how fast?
- speed = this.distanceToPointer(displayObject, pointer) / (maxTime / 1000);
- }
-
- displayObject.body.velocity.x = Math.cos(this._angle) * speed;
- displayObject.body.velocity.y = Math.sin(this._angle) * speed;
-
- return this._angle;
-
- },
-
- /**
- * Move the given display object towards the x/y coordinates at a steady velocity.
- * If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
- * Timings are approximate due to the way browser timers work. Allow for a variance of +- 50ms.
- * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course.
- * Note: The display object doesn't stop moving once it reaches the destination coordinates.
- * Note: Doesn't take into account acceleration, maxVelocity or drag (if you've set drag or acceleration too high this object may not move at all)
- *
- * @method Phaser.Physics.Arcade#moveToXY
- * @param {any} displayObject - The display object to move.
- * @param {number} x - The x coordinate to move towards.
- * @param {number} y - The y coordinate to move towards.
- * @param {number} [speed=60] - The speed it will move, in pixels per second (default is 60 pixels/sec)
- * @param {number} [maxTime=0] - Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the object will arrive at destination in the given number of ms.
- * @return {number} The angle (in radians) that the object should be visually set to in order to match its new velocity.
- */
- moveToXY: function (displayObject, x, y, speed, maxTime) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- if (typeof maxTime === 'undefined') { maxTime = 0; }
-
- this._angle = Math.atan2(y - displayObject.y, x - displayObject.x);
-
- if (maxTime > 0)
- {
- // We know how many pixels we need to move, but how fast?
- speed = this.distanceToXY(displayObject, x, y) / (maxTime / 1000);
- }
-
- displayObject.body.velocity.x = Math.cos(this._angle) * speed;
- displayObject.body.velocity.y = Math.sin(this._angle) * speed;
-
- return this._angle;
-
- },
-
- /**
- * Given the angle (in degrees) and speed calculate the velocity and return it as a Point object, or set it to the given point object.
- * One way to use this is: velocityFromAngle(angle, 200, sprite.velocity) which will set the values directly to the sprites velocity and not create a new Point object.
- *
- * @method Phaser.Physics.Arcade#velocityFromAngle
- * @param {number} angle - The angle in degrees calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative)
- * @param {number} [speed=60] - The speed it will move, in pixels per second sq.
- * @param {Phaser.Point|object} [point] - The Point object in which the x and y properties will be set to the calculated velocity.
- * @return {Phaser.Point} - A Point where point.x contains the velocity x value and point.y contains the velocity y value.
- */
- velocityFromAngle: function (angle, speed, point) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- point = point || new Phaser.Point();
-
- return point.setTo((Math.cos(this.game.math.degToRad(angle)) * speed), (Math.sin(this.game.math.degToRad(angle)) * speed));
-
- },
-
- /**
- * Given the rotation (in radians) and speed calculate the velocity and return it as a Point object, or set it to the given point object.
- * One way to use this is: velocityFromRotation(rotation, 200, sprite.velocity) which will set the values directly to the sprites velocity and not create a new Point object.
- *
- * @method Phaser.Physics.Arcade#velocityFromRotation
- * @param {number} rotation - The angle in radians.
- * @param {number} [speed=60] - The speed it will move, in pixels per second sq.
- * @param {Phaser.Point|object} [point] - The Point object in which the x and y properties will be set to the calculated velocity.
- * @return {Phaser.Point} - A Point where point.x contains the velocity x value and point.y contains the velocity y value.
- */
- velocityFromRotation: function (rotation, speed, point) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- point = point || new Phaser.Point();
-
- return point.setTo((Math.cos(rotation) * speed), (Math.sin(rotation) * speed));
-
- },
-
- /**
- * Given the rotation (in radians) and speed calculate the acceleration and return it as a Point object, or set it to the given point object.
- * One way to use this is: accelerationFromRotation(rotation, 200, sprite.acceleration) which will set the values directly to the sprites acceleration and not create a new Point object.
- *
- * @method Phaser.Physics.Arcade#accelerationFromRotation
- * @param {number} rotation - The angle in radians.
- * @param {number} [speed=60] - The speed it will move, in pixels per second sq.
- * @param {Phaser.Point|object} [point] - The Point object in which the x and y properties will be set to the calculated acceleration.
- * @return {Phaser.Point} - A Point where point.x contains the acceleration x value and point.y contains the acceleration y value.
- */
- accelerationFromRotation: function (rotation, speed, point) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- point = point || new Phaser.Point();
-
- return point.setTo((Math.cos(rotation) * speed), (Math.sin(rotation) * speed));
-
- },
-
- /**
- * Sets the acceleration.x/y property on the display object so it will move towards the target at the given speed (in pixels per second sq.)
- * You must give a maximum speed value, beyond which the display object won't go any faster.
- * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course.
- * Note: The display object doesn't stop moving once it reaches the destination coordinates.
- *
- * @method Phaser.Physics.Arcade#accelerateToObject
- * @param {any} displayObject - The display object to move.
- * @param {any} destination - The display object to move towards. Can be any object but must have visible x/y properties.
- * @param {number} [speed=60] - The speed it will accelerate in pixels per second.
- * @param {number} [xSpeedMax=500] - The maximum x velocity the display object can reach.
- * @param {number} [ySpeedMax=500] - The maximum y velocity the display object can reach.
- * @return {number} The angle (in radians) that the object should be visually set to in order to match its new trajectory.
- */
- accelerateToObject: function (displayObject, destination, speed, xSpeedMax, ySpeedMax) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- if (typeof xSpeedMax === 'undefined') { xSpeedMax = 1000; }
- if (typeof ySpeedMax === 'undefined') { ySpeedMax = 1000; }
-
- this._angle = this.angleBetween(displayObject, destination);
-
- displayObject.body.acceleration.setTo(Math.cos(this._angle) * speed, Math.sin(this._angle) * speed);
- displayObject.body.maxVelocity.setTo(xSpeedMax, ySpeedMax);
-
- return this._angle;
-
- },
-
- /**
- * Sets the acceleration.x/y property on the display object so it will move towards the target at the given speed (in pixels per second sq.)
- * You must give a maximum speed value, beyond which the display object won't go any faster.
- * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course.
- * Note: The display object doesn't stop moving once it reaches the destination coordinates.
- *
- * @method Phaser.Physics.Arcade#accelerateToPointer
- * @param {any} displayObject - The display object to move.
- * @param {Phaser.Pointer} [pointer] - The pointer to move towards. Defaults to Phaser.Input.activePointer.
- * @param {number} [speed=60] - The speed it will accelerate in pixels per second.
- * @param {number} [xSpeedMax=500] - The maximum x velocity the display object can reach.
- * @param {number} [ySpeedMax=500] - The maximum y velocity the display object can reach.
- * @return {number} The angle (in radians) that the object should be visually set to in order to match its new trajectory.
- */
- accelerateToPointer: function (displayObject, pointer, speed, xSpeedMax, ySpeedMax) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- if (typeof pointer === 'undefined') { pointer = this.game.input.activePointer; }
- if (typeof xSpeedMax === 'undefined') { xSpeedMax = 1000; }
- if (typeof ySpeedMax === 'undefined') { ySpeedMax = 1000; }
-
- this._angle = this.angleToPointer(displayObject, pointer);
-
- displayObject.body.acceleration.setTo(Math.cos(this._angle) * speed, Math.sin(this._angle) * speed);
- displayObject.body.maxVelocity.setTo(xSpeedMax, ySpeedMax);
-
- return this._angle;
-
- },
-
- /**
- * Sets the acceleration.x/y property on the display object so it will move towards the x/y coordinates at the given speed (in pixels per second sq.)
- * You must give a maximum speed value, beyond which the display object won't go any faster.
- * Note: The display object does not continuously track the target. If the target changes location during transit the display object will not modify its course.
- * Note: The display object doesn't stop moving once it reaches the destination coordinates.
- *
- * @method Phaser.Physics.Arcade#accelerateToXY
- * @param {any} displayObject - The display object to move.
- * @param {number} x - The x coordinate to accelerate towards.
- * @param {number} y - The y coordinate to accelerate towards.
- * @param {number} [speed=60] - The speed it will accelerate in pixels per second.
- * @param {number} [xSpeedMax=500] - The maximum x velocity the display object can reach.
- * @param {number} [ySpeedMax=500] - The maximum y velocity the display object can reach.
- * @return {number} The angle (in radians) that the object should be visually set to in order to match its new trajectory.
- */
- accelerateToXY: function (displayObject, x, y, speed, xSpeedMax, ySpeedMax) {
-
- if (typeof speed === 'undefined') { speed = 60; }
- if (typeof xSpeedMax === 'undefined') { xSpeedMax = 1000; }
- if (typeof ySpeedMax === 'undefined') { ySpeedMax = 1000; }
-
- this._angle = this.angleToXY(displayObject, x, y);
-
- displayObject.body.acceleration.setTo(Math.cos(this._angle) * speed, Math.sin(this._angle) * speed);
- displayObject.body.maxVelocity.setTo(xSpeedMax, ySpeedMax);
-
- return this._angle;
-
- },
-
- /**
- * Find the distance between two display objects (like Sprites).
- *
- * @method Phaser.Physics.Arcade#distanceBetween
- * @param {any} source - The Display Object to test from.
- * @param {any} target - The Display Object to test to.
- * @return {number} The distance between the source and target objects.
- */
- distanceBetween: function (source, target) {
-
- this._dx = source.x - target.x;
- this._dy = source.y - target.y;
-
- return Math.sqrt(this._dx * this._dx + this._dy * this._dy);
-
- },
-
- /**
- * Find the distance between a display object (like a Sprite) and the given x/y coordinates.
- * The calculation is made from the display objects x/y coordinate. This may be the top-left if its anchor hasn't been changed.
- * If you need to calculate from the center of a display object instead use the method distanceBetweenCenters()
- *
- * @method Phaser.Physics.Arcade#distanceToXY
- * @param {any} displayObject - The Display Object to test from.
- * @param {number} x - The x coordinate to move towards.
- * @param {number} y - The y coordinate to move towards.
- * @return {number} The distance between the object and the x/y coordinates.
- */
- distanceToXY: function (displayObject, x, y) {
-
- this._dx = displayObject.x - x;
- this._dy = displayObject.y - y;
-
- return Math.sqrt(this._dx * this._dx + this._dy * this._dy);
-
- },
-
- /**
- * Find the distance between a display object (like a Sprite) and a Pointer. If no Pointer is given the Input.activePointer is used.
- * The calculation is made from the display objects x/y coordinate. This may be the top-left if its anchor hasn't been changed.
- * If you need to calculate from the center of a display object instead use the method distanceBetweenCenters()
- *
- * @method Phaser.Physics.Arcade#distanceToPointer
- * @param {any} displayObject - The Display Object to test from.
- * @param {Phaser.Pointer} [pointer] - The Phaser.Pointer to test to. If none is given then Input.activePointer is used.
- * @return {number} The distance between the object and the Pointer.
- */
- distanceToPointer: function (displayObject, pointer) {
-
- pointer = pointer || this.game.input.activePointer;
-
- this._dx = displayObject.x - pointer.x;
- this._dy = displayObject.y - pointer.y;
-
- return Math.sqrt(this._dx * this._dx + this._dy * this._dy);
-
- },
-
- /**
- * Find the angle in radians between two display objects (like Sprites).
- *
- * @method Phaser.Physics.Arcade#angleBetween
- * @param {any} source - The Display Object to test from.
- * @param {any} target - The Display Object to test to.
- * @return {number} The angle in radians between the source and target display objects.
- */
- angleBetween: function (source, target) {
-
- this._dx = target.x - source.x;
- this._dy = target.y - source.y;
-
- return Math.atan2(this._dy, this._dx);
-
- },
-
- /**
- * Find the angle in radians between a display object (like a Sprite) and the given x/y coordinate.
- *
- * @method Phaser.Physics.Arcade#angleToXY
- * @param {any} displayObject - The Display Object to test from.
- * @param {number} x - The x coordinate to get the angle to.
- * @param {number} y - The y coordinate to get the angle to.
- * @return {number} The angle in radians between displayObject.x/y to Pointer.x/y
- */
- angleToXY: function (displayObject, x, y) {
-
- this._dx = x - displayObject.x;
- this._dy = y - displayObject.y;
-
- return Math.atan2(this._dy, this._dx);
-
- },
-
- /**
- * Find the angle in radians between a display object (like a Sprite) and a Pointer, taking their x/y and center into account.
- *
- * @method Phaser.Physics.Arcade#angleToPointer
- * @param {any} displayObject - The Display Object to test from.
- * @param {Phaser.Pointer} [pointer] - The Phaser.Pointer to test to. If none is given then Input.activePointer is used.
- * @return {number} The angle in radians between displayObject.x/y to Pointer.x/y
- */
- angleToPointer: function (displayObject, pointer) {
-
- pointer = pointer || this.game.input.activePointer;
-
- this._dx = pointer.worldX - displayObject.x;
- this._dy = pointer.worldY - displayObject.y;
-
- return Math.atan2(this._dy, this._dx);
-
- }
};
-Phaser.Physics.Arcade.prototype.constructor = Phaser.Physics.Arcade;
+/**
+* @name Phaser.Physics.World#friction
+* @property {number} friction - Friction between colliding bodies. This value is used if no matching ContactMaterial is found for a Material pair.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "friction", {
+
+ get: function () {
+
+ return this.world.defaultFriction;
+
+ },
+
+ set: function (value) {
+
+ this.world.defaultFriction = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#restituion
+* @property {number} restitution - Default coefficient of restitution between colliding bodies. This value is used if no matching ContactMaterial is found for a Material pair.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "restituion", {
+
+ get: function () {
+
+ return this.world.defaultRestitution;
+
+ },
+
+ set: function (value) {
+
+ this.world.defaultRestitution = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#applySpringForces
+* @property {boolean} applySpringForces - Enable to automatically apply spring forces each step.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "applySpringForces", {
+
+ get: function () {
+
+ return this.world.applySpringForces;
+
+ },
+
+ set: function (value) {
+
+ this.world.applySpringForces = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#applyDamping
+* @property {boolean} applyDamping - Enable to automatically apply body damping each step.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "applyDamping", {
+
+ get: function () {
+
+ return this.world.applyDamping;
+
+ },
+
+ set: function (value) {
+
+ this.world.applyDamping = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#applyGravity
+* @property {boolean} applyGravity - Enable to automatically apply gravity each step.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "applyGravity", {
+
+ get: function () {
+
+ return this.world.applyGravity;
+
+ },
+
+ set: function (value) {
+
+ this.world.applyGravity = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#solveConstraints
+* @property {boolean} solveConstraints - Enable/disable constraint solving in each step.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "solveConstraints", {
+
+ get: function () {
+
+ return this.world.solveConstraints;
+
+ },
+
+ set: function (value) {
+
+ this.world.solveConstraints = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#time
+* @property {boolean} time - The World time.
+* @readonly
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "time", {
+
+ get: function () {
+
+ return this.world.time;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#emitImpactEvent
+* @property {boolean} emitImpactEvent - Set to true if you want to the world to emit the "impact" event. Turning this off could improve performance.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "emitImpactEvent", {
+
+ get: function () {
+
+ return this.world.emitImpactEvent;
+
+ },
+
+ set: function (value) {
+
+ this.world.emitImpactEvent = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.World#enableBodySleeping
+* @property {boolean} enableBodySleeping - Enable / disable automatic body sleeping.
+*/
+Object.defineProperty(Phaser.Physics.World.prototype, "enableBodySleeping", {
+
+ get: function () {
+
+ return this.world.enableBodySleeping;
+
+ },
+
+ set: function (value) {
+
+ this.world.enableBodySleeping = value;
+
+ }
+
+});
/**
* @author Richard Davey
@@ -40248,1532 +52039,1597 @@ Phaser.Physics.Arcade.prototype.constructor = Phaser.Physics.Arcade;
*/
/**
-* The Physics Body is linked to a single Sprite and defines properties that determine how the physics body is simulated.
-* These properties affect how the body reacts to forces, what forces it generates on itself (to simulate friction), and how it reacts to collisions in the scene. In most cases, the properties are used to simulate physical effects.
-* Each body also has its own property values that determine exactly how it reacts to forces and collisions in the scene.
+* A PointProxy is an internal class that allows for direct getter/setter style property access to Arrays and TypedArrays.
*
-* @class Phaser.Physics.Arcade.Body
-* @classdesc Arcade Physics Body Constructor
+* @class Phaser.Physics.PointProxy
+* @classdesc PointProxy
* @constructor
-* @param {Phaser.Sprite} sprite - The Sprite object this physics body belongs to.
+* @param {any} destination - The object to bind to.
*/
-Phaser.Physics.Arcade.Body = function (sprite) {
+Phaser.Physics.PointProxy = function (destination) {
+
+ this.destination = destination;
+
+};
+
+Phaser.Physics.PointProxy.prototype.constructor = Phaser.Physics.PointProxy;
+
+/**
+* @name Phaser.Physics.PointProxy#x
+* @property {number} x - The x property of this PointProxy.
+*/
+Object.defineProperty(Phaser.Physics.PointProxy.prototype, "x", {
+
+ get: function () {
+
+ return this.destination[0];
+
+ },
+
+ set: function (value) {
+
+ this.destination[0] = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.PointProxy#y
+* @property {number} y - The y property of this PointProxy.
+*/
+Object.defineProperty(Phaser.Physics.PointProxy.prototype, "y", {
+
+ get: function () {
+
+ return this.destination[1];
+
+ },
+
+ set: function (value) {
+
+ this.destination[1] = value;
+
+ }
+
+});
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* A InversePointProxy is an internal class that allows for direct getter/setter style property access to Arrays and TypedArrays but inverses the values on set.
+*
+* @class Phaser.Physics.InversePointProxy
+* @classdesc InversePointProxy
+* @constructor
+* @param {any} destination - The object to bind to.
+*/
+Phaser.Physics.InversePointProxy = function (destination) {
+
+ this.destination = destination;
+
+};
+
+Phaser.Physics.InversePointProxy.prototype.constructor = Phaser.Physics.InversePointProxy;
+
+/**
+* @name Phaser.Physics.InversePointProxy#x
+* @property {number} x - The x property of this InversePointProxy.
+*/
+Object.defineProperty(Phaser.Physics.InversePointProxy.prototype, "x", {
+
+ get: function () {
+
+ return this.destination[0];
+
+ },
+
+ set: function (value) {
+
+ this.destination[0] = -value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.InversePointProxy#y
+* @property {number} y - The y property of this InversePointProxy.
+*/
+Object.defineProperty(Phaser.Physics.InversePointProxy.prototype, "y", {
+
+ get: function () {
+
+ return this.destination[1];
+
+ },
+
+ set: function (value) {
+
+ this.destination[1] = -value;
+
+ }
+
+});
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* The Physics Body is typically linked to a single Sprite and defines properties that determine how the physics body is simulated.
+* These properties affect how the body reacts to forces, what forces it generates on itself (to simulate friction), and how it reacts to collisions in the scene.
+* In most cases, the properties are used to simulate physical effects. Each body also has its own property values that determine exactly how it reacts to forces and collisions in the scene.
+* By default a single Rectangle shape is added to the Body that matches the dimensions of the parent Sprite. See addShape, removeShape, clearShapes to add extra shapes around the Body.
+*
+* @class Phaser.Physics.Body
+* @classdesc Physics Body Constructor
+* @constructor
+* @param {Phaser.Game} game - Game reference to the currently running game.
+* @param {Phaser.Sprite} [sprite] - The Sprite object this physics body belongs to.
+* @param {number} [x=0] - The x coordinate of this Body.
+* @param {number} [y=0] - The y coordinate of this Body.
+* @param {number} [mass=1] - The default mass of this Body (0 = static).
+*/
+Phaser.Physics.Body = function (game, sprite, x, y, mass) {
+
+ sprite = sprite || null;
+ x = x || 0;
+ y = y || 0;
+ if (typeof mass === 'undefined') { mass = 1; }
+
+ /**
+ * @property {Phaser.Game} game - Local reference to game.
+ */
+ this.game = game;
/**
* @property {Phaser.Sprite} sprite - Reference to the parent Sprite.
*/
this.sprite = sprite;
- /**
- * @property {Phaser.Game} game - Local reference to game.
- */
- this.game = sprite.game;
-
/**
* @property {Phaser.Point} offset - The offset of the Physics Body from the Sprite x/y position.
*/
this.offset = new Phaser.Point();
/**
- * @property {number} preX - The previous x position of the physics body.
- * @readonly
+ * @property {p2.Body} data - The p2 Body data.
+ * @protected
*/
- this.preX = sprite.world.x;
+ this.data = new p2.Body({ position:[this.px2p(x), this.px2p(y)], mass: mass });
+ this.data.parent = this;
/**
- * @property {number} preY - The previous y position of the physics body.
- * @readonly
+ * @property {Phaser.PointProxy} velocity - The velocity of the body. Set velocity.x to a negative value to move to the left, position to the right. velocity.y negative values move up, positive move down.
*/
- this.preY = sprite.world.y;
+ this.velocity = new Phaser.Physics.PointProxy(this.data.velocity);
/**
- * @property {number} preRotation - The previous rotation of the physics body.
- * @readonly
+ * @property {Phaser.PointProxy} force - The force applied to the body.
*/
- this.preRotation = sprite.angle;
+ this.force = new Phaser.Physics.PointProxy(this.data.force);
/**
- * @property {Phaser.Point} velocity - The velocity of the Body.
- */
- this.velocity = new Phaser.Point();
-
- /**
- * @property {Phaser.Point} acceleration - The acceleration in pixels per second sq. of the Body.
- */
- this.acceleration = new Phaser.Point();
-
- /**
- * @property {number} speed - The speed in pixels per second sq. of the Body.
- */
- this.speed = 0;
-
- /**
- * @property {number} angle - The angle of the Body based on its velocity in radians.
- */
- this.angle = 0;
-
- /**
- * @property {Phaser.Point} gravity - The gravity applied to the motion of the Body. This works in addition to any gravity set on the world.
+ * @property {Phaser.Point} gravity - A locally applied gravity force to the Body. Applied directly before the world step. NOTE: Not currently implemented.
*/
this.gravity = new Phaser.Point();
/**
- * @property {Phaser.Point} bounce - The elasticitiy of the Body when colliding. This property determines how much energy a body maintains during a collision, i.e. its bounciness.
- */
- this.bounce = new Phaser.Point();
-
- /**
- * @property {Phaser.Point} minVelocity - When a body rebounds off another body or a wall the minVelocity is checked. If the new velocity is lower than minVelocity the body is stopped.
- * @default
- */
- this.minVelocity = new Phaser.Point();
-
- /**
- * @property {Phaser.Point} maxVelocity - The maximum velocity that the Body can reach.
- * @default
- */
- this.maxVelocity = new Phaser.Point(1000, 1000);
-
- /**
- * @property {number} angularVelocity - The angular velocity of the Body.
- * @default
- */
- this.angularVelocity = 0;
-
- /**
- * @property {number} angularAcceleration - The angular acceleration of the Body.
- * @default
- */
- this.angularAcceleration = 0;
-
- /**
- * @property {number} angularDrag - angularDrag is used to calculate friction on the body as it rotates.
- * @default
- */
- this.angularDrag = 0;
-
- /**
- * @property {number} maxAngular - The maximum angular velocity that the Body can reach.
- * @default
- */
- this.maxAngular = 1000;
-
- /**
- * @property {number} mass - The mass property determines how forces affect the body, as well as how much momentum the body has when it is involved in a collision.
- * @default
- */
- this.mass = 1;
-
- /**
- * @property {number} linearDamping - linearDamping is used to calculate friction on the body as it moves through the world. For example, this might be used to simulate air or water friction.
- * @default
- */
- this.linearDamping = 0.0;
-
- /**
- * Set the checkCollision properties to control which directions collision is processed for this Body.
- * For example checkCollision.up = false means it won't collide when the collision happened while moving up.
- * @property {object} checkCollision - An object containing allowed collision.
- */
- this.checkCollision = { none: false, any: true, up: true, down: true, left: true, right: true };
-
- /**
- * This object is populated with boolean values when the Body collides with another.
- * touching.up = true means the collision happened to the top of this Body for example.
- * @property {object} touching - An object containing touching results.
- */
- this.touching = { none: true, up: false, down: false, left: false, right: false };
-
- /**
- * This object is populated with boolean values when the Body collides with the World bounds or a Tile.
- * For example if blocked.up is true then the Body cannot move up.
- * @property {object} blocked - An object containing on which faces this Body is blocked from moving, if any.
- */
- this.blocked = { x: 0, y: 0, up: false, down: false, left: false, right: false };
-
- /**
- * @property {number} facing - A const reference to the direction the Body is traveling or facing.
- * @default
- */
- this.facing = Phaser.NONE;
-
- /**
- * @property {boolean} rebound - A Body set to rebound will exchange velocity with another Body during collision. Set to false to allow this body to be 'pushed' rather than exchange velocity.
- * @default
- */
- this.rebound = true;
-
- /**
- * @property {boolean} immovable - An immovable Body will not receive any impacts or exchanges of velocity from other bodies.
- * @default
- */
- this.immovable = false;
-
- /**
- * @property {boolean} moves - Set to true to allow the Physics system (such as velocity) to move this Body, or false to move it manually.
- * @default
- */
- this.moves = true;
-
- /**
- * @property {number} rotation - The amount the parent Sprite is rotated.
- * @default
- */
- this.rotation = 0;
-
- /**
- * @property {boolean} allowRotation - Allow angular rotation? This will cause the Sprite to be rotated via angularVelocity, etc.
- * @default
- */
- this.allowRotation = true;
-
- /**
- * @property {boolean} allowGravity - Allow this Body to be influenced by the global Gravity value? Note: It will always be influenced by the local gravity if set.
- * @default
- */
- this.allowGravity = true;
-
- /**
- * @property {function} customSeparateCallback - If set this callback will be used for Body separation instead of the built-in one. Callback should return true if separated, otherwise false.
- * @default
- */
- this.customSeparateCallback = null;
-
- /**
- * @property {object} customSeparateContext - The context in which the customSeparateCallback is called.
- * @default
- */
- this.customSeparateContext = null;
-
- /**
- * @property {function} collideCallback - If set this callback will be fired whenever this Body is hit (on any face). It will send three parameters, the face it hit on, this Body and the Body that hit it.
- * @default
- */
- this.collideCallback = null;
-
- /**
- * @property {object} collideCallbackContext - The context in which the collideCallback is called.
- * @default
- */
- this.collideCallbackContext = null;
-
- /**
- * A Body can be set to collide against the World bounds automatically and rebound back into the World if this is set to true. Otherwise it will leave the World.
+ * A Body can be set to collide against the World bounds automatically if this is set to true. Otherwise it will leave the World.
+ * Note that this only applies if your World has bounds! The response to the collision should be managed via CollisionMaterials.
* @property {boolean} collideWorldBounds - Should the Body collide with the World bounds?
*/
- this.collideWorldBounds = false;
+ this.collideWorldBounds = true;
/**
- * @property {Phaser.Physics.Arcade.RECT|Phaser.Physics.Arcade.CIRCLE} type - The type of SAT Shape.
+ * @property {Phaser.Signal} onImpact - Dispatched when the shape/s of this Body impact with another. The event will be sent 2 parameters, this Body and the impact Body.
*/
- this.type = Phaser.Physics.Arcade.RECT;
+ this.onImpact = new Phaser.Signal();
/**
- * @property {SAT.Box|SAT.Circle|SAT.Polygon} shape - The SAT Collision shape.
- */
- this.shape = null;
-
- /**
- * @property {SAT.Polygon} polygon - The SAT Polygons, as derived from the Shape.
- */
- this.polygon = null;
-
- /**
- * @property {number} left - The left-most point of this Body.
- * @readonly
- */
- this.left = 0;
-
- /**
- * @property {number} right - The right-most point of this Body.
- * @readonly
- */
- this.right = 0;
-
- /**
- * @property {number} top - The top-most point of this Body.
- * @readonly
- */
- this.top = 0;
-
- /**
- * @property {number} bottom - The bottom-most point of this Body.
- * @readonly
- */
- this.bottom = 0;
-
- /**
- * @property {number} width - The current width of the Body, taking into account the point rotation.
- * @readonly
- */
- this.width = 0;
-
- /**
- * @property {number} height - The current height of the Body, taking into account the point rotation.
- * @readonly
- */
- this.height = 0;
-
- /**
- * @property {array} contacts - Used to store references to bodies this Body is in contact with.
- * @protected
- */
- this.contacts = [];
-
- /**
- * @property {number} overlapX - Mostly used internally to store the overlap values from Tile seperation.
- * @protected
- */
- this.overlapX = 0;
-
- /**
- * @property {number} overlapY - Mostly used internally to store the overlap values from Tile seperation.
- * @protected
- */
- this.overlapY = 0;
-
- /**
- * @property {Phaser.Point} _temp - Internal cache var.
+ * @property {array} collidesWith - Array of CollisionGroups that this Bodies shapes collide with.
* @private
*/
- this._temp = null;
+ this.collidesWith = [];
/**
- * @property {number} _dx - Internal cache var.
+ * @property {array} _bodyCallbacks - Array of Body callbacks.
* @private
*/
- this._dx = 0;
+ this._bodyCallbacks = [];
/**
- * @property {number} _dy - Internal cache var.
+ * @property {array} _bodyCallbackContext - Array of Body callback contexts.
* @private
*/
- this._dy = 0;
+ this._bodyCallbackContext = [];
/**
- * @property {number} _sx - Internal cache var.
+ * @property {array} _groupCallbacks - Array of Group callbacks.
* @private
*/
- this._sx = sprite.scale.x;
+ this._groupCallbacks = [];
/**
- * @property {number} _sy - Internal cache var.
+ * @property {array} _bodyCallbackContext - Array of Grouo callback contexts.
* @private
*/
- this._sy = sprite.scale.y;
-
- /**
- * @property {array} _distances - Internal cache var.
- * @private
- */
- this._distances = [0, 0, 0, 0];
-
- /**
- * @property {number} _vx - Internal cache var.
- * @private
- */
- this._vx = 0;
-
- /**
- * @property {number} _vy - Internal cache var.
- * @private
- */
- this._vy = 0;
+ this._groupCallbackContext = [];
// Set-up the default shape
- this.setRectangle(sprite.width, sprite.height, 0, 0);
+ if (sprite)
+ {
+ this.setRectangleFromSprite(sprite);
- // Set-up contact events
- this.sprite.events.onBeginContact = new Phaser.Signal();
- this.sprite.events.onEndContact = new Phaser.Signal();
+ this.game.physics.addBody(this);
+ }
};
-Phaser.Physics.Arcade.Body.prototype = {
+Phaser.Physics.Body.prototype = {
/**
- * Internal method that updates the Body scale in relation to the parent Sprite.
+ * Sets a callback to be fired any time this Body impacts with the given Body. The impact test is performed against body.id values.
+ * The callback will be sent 4 parameters: This body, the body that impacted, the Shape in this body and the shape in the impacting body.
*
- * @method Phaser.Physics.Arcade.Body#updateScale
- * @protected
+ * @method Phaser.Physics.Body#createBodyCallback
+ * @param {Phaser.Physics.Body} body - The Body to send impact events for.
+ * @param {function} callback - The callback to fire on impact. Set to null to clear a previously set callback.
+ * @param {object} callbackContext - The context under which the callback will fire.
*/
- updateScale: function () {
+ createBodyCallback: function (body, callback, callbackContext) {
- if (this.polygon)
+ this._bodyCallbacks[body.data.id] = callback;
+ this._bodyCallbackContext[body.data.id] = callbackContext;
+
+ },
+
+ /**
+ * Sets a callback to be fired any time this Body impacts with the given Group. The impact test is performed against shape.collisionGroup values.
+ * The callback will be sent 4 parameters: This body, the body that impacted, the Shape in this body and the shape in the impacting body.
+ * This callback will only fire if this Body has been assigned a collision group.
+ *
+ * @method Phaser.Physics.Body#createGroupCallback
+ * @param {Phaser.Physics.CollisionGroup} group - The Group to send impact events for.
+ * @param {function} callback - The callback to fire on impact. Set to null to clear a previously set callback.
+ * @param {object} callbackContext - The context under which the callback will fire.
+ */
+ createGroupCallback: function (group, callback, callbackContext) {
+
+ this._groupCallbacks[group.mask] = callback;
+ this._groupCallbackContext[group.mask] = callbackContext;
+
+ },
+
+ /**
+ * Gets the collision bitmask from the groups this body collides with.
+ *
+ * @method Phaser.Physics.Body#getCollisionMask
+ * @return {number} The bitmask.
+ */
+ getCollisionMask: function () {
+
+ var mask = 0;
+
+ if (this.collideWorldBounds)
{
- this.polygon.scale(this.sprite.scale.x / this._sx, this.sprite.scale.y / this._sy);
+ mask = this.game.physics.boundsCollisionGroup.mask;
+ }
+
+ for (var i = 0; i < this.collidesWith.length; i++)
+ {
+ mask = mask | this.collidesWith[i].mask;
+ }
+
+ return mask;
+
+ },
+
+ /**
+ * Sets the given CollisionGroup to be the collision group for all shapes in this Body, unless a shape is specified.
+ * This also resets the collisionMask.
+ *
+ * @method Phaser.Physics.Body#setCollisionGroup
+ * @param {Phaser.Physics.CollisionGroup|array} group - The Collision Group that this Bodies shapes will use.
+ * @param {p2.Shape} [shape] - An optional Shape. If not provided the collision group will be added to all Shapes in this Body.
+ */
+ setCollisionGroup: function (group, shape) {
+
+ var mask = this.getCollisionMask();
+
+ if (typeof shape === 'undefined')
+ {
+ for (var i = this.data.shapes.length - 1; i >= 0; i--)
+ {
+ this.data.shapes[i].collisionGroup = group.mask;
+ this.data.shapes[i].collisionMask = mask;
+ }
}
else
{
- this.shape.r *= Math.max(this.sprite.scale.x, this.sprite.scale.y);
- }
-
- this._sx = this.sprite.scale.x;
- this._sy = this.sprite.scale.y;
-
- },
-
- /**
- * Internal method that updates the Body position in relation to the parent Sprite.
- *
- * @method Phaser.Physics.Arcade.Body#preUpdate
- * @protected
- */
- preUpdate: function () {
-
- this.x = (this.sprite.world.x - (this.sprite.anchor.x * this.sprite.width)) + this.offset.x;
- this.y = (this.sprite.world.y - (this.sprite.anchor.y * this.sprite.height)) + this.offset.y;
-
- // This covers any motion that happens during this frame, not since the last frame
- this.preX = this.x;
- this.preY = this.y;
- this.preRotation = this.sprite.angle;
-
- this.rotation = this.preRotation;
-
- if (this.sprite.scale.x !== this._sx || this.sprite.scale.y !== this._sy)
- {
- this.updateScale();
- }
-
- this.checkBlocked();
-
- this.touching.none = true;
- this.touching.up = false;
- this.touching.down = false;
- this.touching.left = false;
- this.touching.right = false;
-
- if (this.moves)
- {
- if (this._vx !== this.velocity.x || this._vy !== this.velocity.y)
- {
- // No need to re-calc these if they haven't changed
- this._vx = this.velocity.x;
- this._vy = this.velocity.y;
- this.speed = Math.sqrt(this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y);
- this.angle = Math.atan2(this.velocity.y, this.velocity.x);
- }
-
- if (this.game.physics.checkBounds(this))
- {
- this.reboundCheck(true, true, true);
- }
-
- this.applyDamping();
-
- this.integrateVelocity();
-
- this.updateBounds();
-
- this.checkBlocked();
- }
- else
- {
- this.updateBounds();
+ shape.collisionGroup = group.mask;
+ shapes.collisionMask = mask;
}
},
/**
- * Internal method that checks and potentially resets the blocked status flags.
+ * Clears the collision data from the shapes in this Body. Optionally clears Group and/or Mask.
*
- * @method Phaser.Physics.Arcade.Body#checkBlocked
- * @protected
+ * @method Phaser.Physics.Body#clearCollision
+ * @param {boolean} [clearGroup=true] - Clear the collisionGroup value from the shape/s?
+ * @param {boolean} [clearMask=true] - Clear the collisionMask value from the shape/s?
+ * @param {p2.Shape} [shape] - An optional Shape. If not provided the collision data will be cleared from all Shapes in this Body.
*/
- checkBlocked: function () {
+ clearCollision: function (clearGroup, clearMask, shape) {
- if ((this.blocked.left || this.blocked.right) && (Math.floor(this.x) !== this.blocked.x || Math.floor(this.y) !== this.blocked.y))
+ if (typeof shape === 'undefined')
{
- this.blocked.left = false;
- this.blocked.right = false;
- }
-
- if ((this.blocked.up || this.blocked.down) && (Math.floor(this.x) !== this.blocked.x || Math.floor(this.y) !== this.blocked.y))
- {
- this.blocked.up = false;
- this.blocked.down = false;
- }
-
- },
-
- /**
- * Internal method that updates the left, right, top, bottom, width and height properties.
- *
- * @method Phaser.Physics.Arcade.Body#updateBounds
- * @protected
- */
- updateBounds: function () {
-
- if (this.type === Phaser.Physics.Arcade.CIRCLE)
- {
- this.left = this.shape.pos.x - this.shape.r;
- this.right = this.shape.pos.x + this.shape.r;
- this.top = this.shape.pos.y - this.shape.r;
- this.bottom = this.shape.pos.y + this.shape.r;
- }
- else
- {
- this.left = Phaser.Math.minProperty('x', this.polygon.points) + this.polygon.pos.x;
- this.right = Phaser.Math.maxProperty('x', this.polygon.points) + this.polygon.pos.x;
- this.top = Phaser.Math.minProperty('y', this.polygon.points) + this.polygon.pos.y;
- this.bottom = Phaser.Math.maxProperty('y', this.polygon.points) + this.polygon.pos.y;
- }
-
- this.width = this.right - this.left;
- this.height = this.bottom - this.top;
-
- },
-
- /**
- * Internal method that checks the acceleration and applies damping if not set.
- *
- * @method Phaser.Physics.Arcade.Body#applyDamping
- * @protected
- */
- applyDamping: function () {
-
- if (this.linearDamping > 0 && this.acceleration.isZero())
- {
- if (this.speed > this.linearDamping)
+ for (var i = this.data.shapes.length - 1; i >= 0; i--)
{
- this.speed -= this.linearDamping;
- }
- else
- {
- this.speed = 0;
- }
-
- // Don't bother if speed 0
- if (this.speed > 0)
- {
- this.velocity.x = Math.cos(this.angle) * this.speed;
- this.velocity.y = Math.sin(this.angle) * this.speed;
-
- this.speed = Math.sqrt(this.velocity.x * this.velocity.x + this.velocity.y * this.velocity.y);
- this.angle = Math.atan2(this.velocity.y, this.velocity.x);
- }
- }
-
- },
-
- /**
- * Check if we're below minVelocity and gravity isn't trying to drag us in the opposite direction.
- *
- * @method Phaser.Physics.Arcade.Body#reboundCheck
- * @protected
- * @param {boolean} x - Check the X axis?
- * @param {boolean} y - Check the Y axis?
- * @param {boolean} rebound - If true it will reverse the velocity on the given axis
- */
- reboundCheck: function (x, y, rebound) {
-
- if (x)
- {
- if (rebound && this.bounce.x !== 0 && (this.blocked.left || this.blocked.right || this.touching.left || this.touching.right))
- {
- // Don't rebound if they've already rebounded in this frame
- if (!(this._vx <= 0 && this.velocity.x > 0) && !(this._vx >= 0 && this.velocity.x < 0))
+ if (clearGroup)
{
- this.velocity.x *= -this.bounce.x;
- this.angle = Math.atan2(this.velocity.y, this.velocity.x);
+ this.data.shapes[i].collisionGroup = null;
+ }
+
+ if (clearMask)
+ {
+ this.data.shapes[i].collisionMask = null;
}
}
-
- if (this.bounce.x === 0 || Math.abs(this.velocity.x) < this.minVelocity.x)
+ }
+ else
+ {
+ if (clearGroup)
{
- var gx = this.getUpwardForce();
+ shapes.collisionGroup = null;
+ }
- if (((this.blocked.left || this.touching.left) && (gx < 0 || this.velocity.x < 0)) || ((this.blocked.right || this.touching.right) && (gx > 0 || this.velocity.x > 0)))
+ if (clearMask)
+ {
+ shapes.collisionMask = null;
+ }
+ }
+
+ if (clearGroup)
+ {
+ this.collidesWith.length = 0;
+ }
+
+ },
+
+ /**
+ * Adds the given CollisionGroup, or array of CollisionGroups, to the list of groups that this body will collide with and updates the collision masks.
+ *
+ * @method Phaser.Physics.Body#collides
+ * @param {Phaser.Physics.CollisionGroup|array} group - The Collision Group or Array of Collision Groups that this Bodies shapes will collide with.
+ * @param {function} [callback] - Optional callback that will be triggered when this Body impacts with the given Group.
+ * @param {object} [callbackContext] - The context under which the callback will be called.
+ * @param {p2.Shape} [shape] - An optional Shape. If not provided the collision mask will be added to all Shapes in this Body.
+ */
+ collides: function (group, callback, callbackContext, shape) {
+
+ if (Array.isArray(group))
+ {
+ for (var i = 0; i < group.length; i++)
+ {
+ if (this.collidesWith.indexOf(group[i]) === -1)
{
- this.velocity.x = 0;
+ this.collidesWith.push(group[i]);
+
+ if (callback)
+ {
+ this.createGroupCallback(group[i], callback, callbackContext);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (this.collidesWith.indexOf(group) === -1)
+ {
+ this.collidesWith.push(group);
+
+ if (callback)
+ {
+ this.createGroupCallback(group, callback, callbackContext);
}
}
}
- if (y)
+ var mask = this.getCollisionMask();
+
+ if (typeof shape === 'undefined')
{
- if (rebound && this.bounce.y !== 0 && (this.blocked.up || this.blocked.down || this.touching.up || this.touching.down))
+ for (var i = this.data.shapes.length - 1; i >= 0; i--)
{
- // Don't rebound if they've already rebounded in this frame
- if (!(this._vy <= 0 && this.velocity.y > 0) && !(this._vy >= 0 && this.velocity.y < 0))
- {
- this.velocity.y *= -this.bounce.y;
- this.angle = Math.atan2(this.velocity.y, this.velocity.x);
- }
+ this.data.shapes[i].collisionMask = mask;
}
-
- if (this.bounce.y === 0 || Math.abs(this.velocity.y) < this.minVelocity.y)
- {
- var gy = this.getDownwardForce();
-
- if (((this.blocked.up || this.touching.up) && (gy < 0 || this.velocity.y < 0)) || ((this.blocked.down || this.touching.down) && (gy > 0 || this.velocity.y > 0)))
- {
- this.velocity.y = 0;
- }
- }
- }
-
- },
-
- /**
- * Gets the total force being applied on the X axis, including gravity and velocity.
- *
- * @method Phaser.Physics.Arcade.Body#getUpwardForce
- * @return {number} The total force being applied on the X axis.
- */
- getUpwardForce: function () {
-
- if (this.allowGravity)
- {
- return this.gravity.x + this.game.physics.gravity.x + this.velocity.x;
}
else
{
- return this.gravity.x + this.velocity.x;
+ shape.collisionMask = mask;
}
},
/**
- * Gets the total force being applied on the X axis, including gravity and velocity.
+ * Moves the shape offsets so their center of mass becomes the body center of mass.
*
- * @method Phaser.Physics.Arcade.Body#getDownwardForce
- * @return {number} The total force being applied on the Y axis.
+ * @method Phaser.Physics.Body#adjustCenterOfMass
*/
- getDownwardForce: function () {
+ adjustCenterOfMass: function () {
- if (this.allowGravity)
- {
- return this.gravity.y + this.game.physics.gravity.y + this.velocity.y;
- }
- else
- {
- return this.gravity.y + this.velocity.y;
- }
+ this.data.adjustCenterOfMass();
},
/**
- * Subtracts the given Vector from this Body.
+ * Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details.
*
- * @method Phaser.Physics.Arcade.Body#sub
- * @protected
- * @param {SAT.Vector} v - The vector to substract from this Body.
+ * @method Phaser.Physics.Body#applyDamping
+ * @param {number} dt - Current time step.
*/
- sub: function (v) {
+ applyDamping: function (dt) {
- this.x -= v.x;
- this.y -= v.y;
+ this.data.applyDamping(dt);
},
/**
- * Adds the given Vector to this Body.
+ * Apply force to a world point. This could for example be a point on the RigidBody surface. Applying force this way will add to Body.force and Body.angularForce.
*
- * @method Phaser.Physics.Arcade.Body#add
- * @protected
- * @param {SAT.Vector} v - The vector to add to this Body.
+ * @method Phaser.Physics.Body#applyForce
+ * @param {number} force - The force to add.
+ * @param {number} worldX - The world x point to apply the force on.
+ * @param {number} worldY - The world y point to apply the force on.
*/
- add: function (v) {
+ applyForce: function (force, worldX, worldY) {
- this.x += v.x;
- this.y += v.y;
+ this.data.applyForce(force, [this.px2p(worldX), this.px2p(worldY)]);
},
/**
- * Separation response handler.
+ * Sets the force on the body to zero.
*
- * @method Phaser.Physics.Arcade.Body#give
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
- * @param {SAT.Response} response - The SAT Response object containing the collision data.
+ * @method Phaser.Physics.Body#setZeroForce
*/
- give: function (body, response) {
+ setZeroForce: function () {
- this.add(response.overlapV);
-
- if (this.rebound)
- {
- this.processRebound(body);
- this.reboundCheck(true, true, false);
- body.reboundCheck(true, true, false);
- }
+ this.data.setZeroForce();
},
/**
- * Separation response handler.
+ * If this Body is dynamic then this will zero its angular velocity.
*
- * @method Phaser.Physics.Arcade.Body#take
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
- * @param {SAT.Response} response - The SAT Response object containing the collision data.
+ * @method Phaser.Physics.Body#setZeroRotation
*/
- take: function (body, response) {
+ setZeroRotation: function () {
- this.sub(response.overlapV);
-
- if (this.rebound)
- {
- this.processRebound(body);
- this.reboundCheck(true, true, false);
- body.reboundCheck(true, true, false);
- }
+ this.data.angularVelocity = 0;
},
/**
- * Split the collision response evenly between the two bodies.
+ * If this Body is dynamic then this will zero its velocity on both axis.
*
- * @method Phaser.Physics.Arcade.Body#split
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
- * @param {SAT.Response} response - The SAT Response object containing the collision data.
+ * @method Phaser.Physics.Body#setZeroVelocity
*/
- split: function (body, response) {
-
- response.overlapV.scale(0.5);
- this.sub(response.overlapV);
- body.add(response.overlapV);
+ setZeroVelocity: function () {
- if (this.rebound)
- {
- this.exchange(body);
- this.reboundCheck(true, true, false);
- body.reboundCheck(true, true, false);
- }
+ this.data.velocity[0] = 0;
+ this.data.velocity[1] = 0;
},
/**
- * Exchange velocity with the given Body.
+ * Sets the Body damping and angularDamping to zero.
*
- * @method Phaser.Physics.Arcade.Body#exchange
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
+ * @method Phaser.Physics.Body#setZeroDamping
*/
- exchange: function (body) {
+ setZeroDamping: function () {
- if (this.mass === body.mass && this.speed > 0 && body.speed > 0)
- {
- // A direct velocity exchange (as they are both moving and have the same mass)
- this._dx = body.velocity.x;
- this._dy = body.velocity.y;
-
- body.velocity.x = this.velocity.x * body.bounce.x;
- body.velocity.y = this.velocity.y * body.bounce.x;
-
- this.velocity.x = this._dx * this.bounce.x;
- this.velocity.y = this._dy * this.bounce.y;
- }
- else
- {
- var nv1 = Math.sqrt((body.velocity.x * body.velocity.x * body.mass) / this.mass) * ((body.velocity.x > 0) ? 1 : -1);
- var nv2 = Math.sqrt((this.velocity.x * this.velocity.x * this.mass) / body.mass) * ((this.velocity.x > 0) ? 1 : -1);
- var average = (nv1 + nv2) * 0.5;
- nv1 -= average;
- nv2 -= average;
-
- this.velocity.x = nv1;
- body.velocity.x = nv2;
-
- nv1 = Math.sqrt((body.velocity.y * body.velocity.y * body.mass) / this.mass) * ((body.velocity.y > 0) ? 1 : -1);
- nv2 = Math.sqrt((this.velocity.y * this.velocity.y * this.mass) / body.mass) * ((this.velocity.y > 0) ? 1 : -1);
- average = (nv1 + nv2) * 0.5;
- nv1 -= average;
- nv2 -= average;
-
- this.velocity.y = nv1;
- body.velocity.y = nv2;
- }
-
- // update speed / angle?
+ this.data.damping = 0;
+ this.data.angularDamping = 0;
},
/**
- * Rebound the velocity of this Body.
+ * Transform a world point to local body frame.
*
- * @method Phaser.Physics.Arcade.Body#processRebound
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
+ * @method Phaser.Physics.Body#toLocalFrame
+ * @param {Float32Array|Array} out - The vector to store the result in.
+ * @param {Float32Array|Array} worldPoint - The input world vector.
*/
- processRebound: function (body) {
+ toLocalFrame: function (out, worldPoint) {
- // Don't rebound again if they've already rebounded in this frame
- if (!(this._vx <= 0 && this.velocity.x > 0) && !(this._vx >= 0 && this.velocity.x < 0))
- {
- this.velocity.x = body.velocity.x - this.velocity.x * this.bounce.x;
- }
-
- if (!(this._vy <= 0 && this.velocity.y > 0) && !(this._vy >= 0 && this.velocity.y < 0))
- {
- this.velocity.y = body.velocity.y - this.velocity.y * this.bounce.y;
- }
-
- this.angle = Math.atan2(this.velocity.y, this.velocity.x);
-
- this.reboundCheck(true, true, false);
+ return this.data.toLocalFrame(out, worldPoint);
},
/**
- * Checks for an overlap between this Body and the given Body.
+ * Transform a local point to world frame.
*
- * @method Phaser.Physics.Arcade.Body#overlap
- * @param {Phaser.Physics.Arcade.Body} body - The Body that is being checked against this Body.
- * @param {SAT.Response} response - SAT Response handler.
- * @return {boolean} True if the two bodies overlap, otherwise false.
+ * @method Phaser.Physics.Body#toWorldFrame
+ * @param {Array} out - The vector to store the result in.
+ * @param {Array} localPoint - The input local vector.
*/
- overlap: function (body, response) {
+ toWorldFrame: function (out, localPoint) {
- var result = false;
-
- if ((this.type === Phaser.Physics.Arcade.RECT || this.type === Phaser.Physics.Arcade.POLYGON) && (body.type === Phaser.Physics.Arcade.RECT || body.type === Phaser.Physics.Arcade.POLYGON))
- {
- result = SAT.testPolygonPolygon(this.polygon, body.polygon, response);
- }
- else if (this.type === Phaser.Physics.Arcade.CIRCLE && body.type === Phaser.Physics.Arcade.CIRCLE)
- {
- result = SAT.testCircleCircle(this.shape, body.shape, response);
- }
- else if ((this.type === Phaser.Physics.Arcade.RECT || this.type === Phaser.Physics.Arcade.POLYGON) && body.type === Phaser.Physics.Arcade.CIRCLE)
- {
- result = SAT.testPolygonCircle(this.polygon, body.shape, response);
- }
- else if (this.type === Phaser.Physics.Arcade.CIRCLE && (body.type === Phaser.Physics.Arcade.RECT || body.type === Phaser.Physics.Arcade.POLYGON))
- {
- result = SAT.testCirclePolygon(this.shape, body.polygon, response);
- }
-
- if (!result)
- {
- this.removeContact(body);
- }
-
- return result;
+ return this.data.toWorldFrame(out, localPoint);
},
/**
- * Checks if this Body is already in contact with the given Body.
+ * This will rotate the Body by the given speed to the left (counter-clockwise).
*
- * @method Phaser.Physics.Arcade.Body#inContact
- * @param {Phaser.Physics.Arcade.Body} body - The Body to be checked.
- * @return {boolean} True if the given Body is already in contact with this Body.
+ * @method Phaser.Physics.Body#rotateLeft
+ * @param {number} speed - The speed at which it should rotate.
*/
- inContact: function (body) {
+ rotateLeft: function (speed) {
- return (this.contacts.indexOf(body) != -1);
+ this.data.angularVelocity = this.px2p(speed);
},
/**
- * Adds the given Body to the contact list of this Body. Also adds this Body to the contact list of the given Body.
+ * This will rotate the Body by the given speed to the left (clockwise).
*
- * @method Phaser.Physics.Arcade.Body#addContact
- * @param {Phaser.Physics.Arcade.Body} body - The Body to be added.
- * @return {boolean} True if the given Body was added to this contact list, false if already on it.
+ * @method Phaser.Physics.Body#rotateRight
+ * @param {number} speed - The speed at which it should rotate.
*/
- addContact: function (body) {
+ rotateRight: function (speed) {
- if (this.inContact(body))
- {
- return false;
- }
-
- this.contacts.push(body);
-
- this.sprite.events.onBeginContact.dispatch(this.sprite, body.sprite, this, body);
-
- body.addContact(this);
-
- return true;
+ this.data.angularVelocity = this.px2p(-speed);
},
/**
- * Removes the given Body from the contact list of this Body. Also removes this Body from the contact list of the given Body.
+ * Moves the Body forwards based on its current angle and the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
*
- * @method Phaser.Physics.Arcade.Body#removeContact
- * @param {Phaser.Physics.Arcade.Body} body - The Body to be removed.
- * @return {boolean} True if the given Body was removed from this contact list, false if wasn't on it.
+ * @method Phaser.Physics.Body#moveForward
+ * @param {number} speed - The speed at which it should move forwards.
*/
- removeContact: function (body) {
+ moveForward: function (speed) {
- if (!this.inContact(body))
- {
- return false;
- }
+ var magnitude = this.px2p(-speed);
+ var angle = this.data.angle + Math.PI / 2;
- this.contacts.splice(this.contacts.indexOf(body), 1);
-
- this.sprite.events.onEndContact.dispatch(this.sprite, body.sprite, this, body);
-
- body.removeContact(this);
-
- return true;
+ this.data.velocity[0] = magnitude * Math.cos(angle);
+ this.data.velocity[1] = magnitude * Math.sin(angle);
},
/**
- * This separates this Body from the given Body unless a customSeparateCallback is set.
- * It assumes they have already been overlap checked and the resulting overlap is stored in the SAT response.
+ * Moves the Body backwards based on its current angle and the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
*
- * @method Phaser.Physics.Arcade.Body#separate
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body to be separated from this one.
- * @param {SAT.Response} response - SAT Response handler.
- * @return {boolean} True if the bodies were separated, false if not (for example checkCollide allows them to pass through)
+ * @method Phaser.Physics.Body#moveBackward
+ * @param {number} speed - The speed at which it should move backwards.
*/
- separate: function (body, response) {
+ moveBackward: function (speed) {
- if (this.inContact(body))
- {
- return false;
- }
+ var magnitude = this.px2p(-speed);
+ var angle = this.data.angle + Math.PI / 2;
- this._distances[0] = body.right - this.x; // Distance of B to face on left side of A
- this._distances[1] = this.right - body.x; // Distance of B to face on right side of A
- this._distances[2] = body.bottom - this.y; // Distance of B to face on bottom side of A
- this._distances[3] = this.bottom - body.y; // Distance of B to face on top side of A
-
- // If we've zero distance then check for side-slicing
- if (response.overlapN.x && (this._distances[0] === 0 || this._distances[1] === 0))
- {
- response.overlapN.x = false;
- response.overlapN.y = true;
- }
- else if (response.overlapN.y && (this._distances[2] === 0 || this._distances[3] === 0))
- {
- response.overlapN.x = true;
- response.overlapN.y = false;
- }
-
- if (this.customSeparateCallback)
- {
- return this.customSeparateCallback.call(this.customSeparateContext, this, response, this._distances);
- }
-
- var hasSeparated = false;
-
- if (response.overlapN.x)
- {
- // Which is smaller? Left or Right?
- if (this._distances[0] < this._distances[1])
- {
- hasSeparated = this.hitLeft(body, response);
- }
- else if (this._distances[1] < this._distances[0])
- {
- hasSeparated = this.hitRight(body, response);
- }
- }
- else if (response.overlapN.y)
- {
- // Which is smaller? Top or Bottom?
- if (this._distances[2] < this._distances[3])
- {
- hasSeparated = this.hitTop(body, response);
- }
- else if (this._distances[3] < this._distances[2])
- {
- hasSeparated = this.hitBottom(body, response);
- }
- }
-
- if (hasSeparated)
- {
- this.game.physics.checkBounds(this);
- this.game.physics.checkBounds(body);
- }
- else
- {
- // They can only contact like this if at least one of their sides is open, otherwise it's a separation
- // if (!this.checkCollision.up || !this.checkCollision.down || !this.checkCollision.left || !this.checkCollision.right || !body.checkCollision.up || !body.checkCollision.down || !body.checkCollision.left || !body.checkCollision.right)
- // {
- this.addContact(body);
- // }
- }
-
- return hasSeparated;
+ this.data.velocity[0] = -(magnitude * Math.cos(angle));
+ this.data.velocity[1] = -(magnitude * Math.sin(angle));
},
/**
- * Process a collision with the left face of this Body.
- * Collision and separation can be further checked by setting a collideCallback.
- * This callback will be sent 4 parameters: The face of collision, this Body, the colliding Body and the SAT Response.
- * If the callback returns true then separation, rebounds and the touching flags will all be set.
- * If it returns false this will be skipped and must be handled manually.
+ * Applies a force to the Body that causes it to 'thrust' forwards, based on its current angle and the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
*
- * @method Phaser.Physics.Arcade.Body#hitLeft
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
- * @param {SAT.Response} response - The SAT Response object containing the collision data.
+ * @method Phaser.Physics.Body#thrust
+ * @param {number} speed - The speed at which it should thrust.
*/
- hitLeft: function (body, response) {
+ thrust: function (speed) {
- if (!this.checkCollision.left || !body.checkCollision.right)
- {
- return false;
- }
+ var magnitude = this.px2p(-speed);
+ var angle = this.data.angle + Math.PI / 2;
- if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.LEFT, this, body, response))
- {
- return;
- }
-
- if (!this.moves || this.immovable || this.blocked.right || this.touching.right)
- {
- body.give(this, response);
- }
- else
- {
- if (body.immovable || body.blocked.left || body.touching.left)
- {
- // We take the full separation
- this.take(body, response);
- }
- else
- {
- // Share out the separation
- this.split(body, response);
- }
- }
-
- this.touching.left = true;
- body.touching.right = true;
+ this.data.force[0] += magnitude * Math.cos(angle);
+ this.data.force[1] += magnitude * Math.sin(angle);
},
/**
- * Process a collision with the right face of this Body.
- * Collision and separation can be further checked by setting a collideCallback.
- * This callback will be sent 4 parameters: The face of collision, this Body, the colliding Body and the SAT Response.
- * If the callback returns true then separation, rebounds and the touching flags will all be set.
- * If it returns false this will be skipped and must be handled manually.
+ * Applies a force to the Body that causes it to 'thrust' backwards (in reverse), based on its current angle and the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
*
- * @method Phaser.Physics.Arcade.Body#hitRight
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
- * @param {SAT.Response} response - The SAT Response object containing the collision data.
+ * @method Phaser.Physics.Body#rever
+ * @param {number} speed - The speed at which it should reverse.
*/
- hitRight: function (body, response) {
+ reverse: function (speed) {
- if (!this.checkCollision.right || !body.checkCollision.left)
- {
- return false;
- }
+ var magnitude = this.px2p(-speed);
+ var angle = this.data.angle + Math.PI / 2;
- if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.RIGHT, this, body))
- {
- return;
- }
-
- if (!this.moves || this.immovable || this.blocked.left || this.touching.left)
- {
- body.give(this, response);
- }
- else
- {
- if (body.immovable || body.blocked.right || body.touching.right)
- {
- // We take the full separation
- this.take(body, response);
- }
- else
- {
- // Share out the separation
- this.split(body, response);
- }
- }
-
- this.touching.right = true;
- body.touching.left = true;
+ this.data.force[0] -= magnitude * Math.cos(angle);
+ this.data.force[1] -= magnitude * Math.sin(angle);
},
/**
- * Process a collision with the top face of this Body.
- * Collision and separation can be further checked by setting a collideCallback.
- * This callback will be sent 4 parameters: The face of collision, this Body, the colliding Body and the SAT Response.
- * If the callback returns true then separation, rebounds and the touching flags will all be set.
- * If it returns false this will be skipped and must be handled manually.
+ * If this Body is dynamic then this will move it to the left by setting its x velocity to the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
*
- * @method Phaser.Physics.Arcade.Body#hitTop
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
- * @param {SAT.Response} response - The SAT Response object containing the collision data.
+ * @method Phaser.Physics.Body#moveLeft
+ * @param {number} speed - The speed at which it should move to the left, in pixels per second.
*/
- hitTop: function (body, response) {
+ moveLeft: function (speed) {
- if (!this.checkCollision.up || !body.checkCollision.down)
- {
- return false;
- }
-
- if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.UP, this, body))
- {
- return false;
- }
-
- if (!this.moves || this.immovable || this.blocked.down || this.touching.down)
- {
- body.give(this, response);
- }
- else
- {
- if (body.immovable || body.blocked.up || body.touching.up)
- {
- // We take the full separation
- this.take(body, response);
- }
- else
- {
- // Share out the separation
- this.split(body, response);
- }
- }
-
- this.touching.up = true;
- body.touching.down = true;
-
- return true;
+ this.data.velocity[0] = this.px2p(-speed);
},
/**
- * Process a collision with the bottom face of this Body.
- * Collision and separation can be further checked by setting a collideCallback.
- * This callback will be sent 4 parameters: The face of collision, this Body, the colliding Body and the SAT Response.
- * If the callback returns true then separation, rebounds and the touching flags will all be set.
- * If it returns false this will be skipped and must be handled manually.
+ * If this Body is dynamic then this will move it to the right by setting its x velocity to the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
*
- * @method Phaser.Physics.Arcade.Body#hitBottom
- * @protected
- * @param {Phaser.Physics.Arcade.Body} body - The Body that collided.
- * @param {SAT.Response} response - The SAT Response object containing the collision data.
+ * @method Phaser.Physics.Body#moveRight
+ * @param {number} speed - The speed at which it should move to the right, in pixels per second.
*/
- hitBottom: function (body, response) {
+ moveRight: function (speed) {
- if (!this.checkCollision.down || !body.checkCollision.up)
- {
- return false;
- }
-
- if (this.collideCallback && !this.collideCallback.call(this.collideCallbackContext, Phaser.DOWN, this, body))
- {
- return false;
- }
-
- if (!this.moves || this.immovable || this.blocked.up || this.touching.up)
- {
- body.give(this, response);
- }
- else
- {
- if (body.immovable || body.blocked.down || body.touching.down)
- {
- // We take the full separation
- this.take(body, response);
- }
- else
- {
- // Share out the separation
- this.split(body, response);
- }
- }
-
- this.touching.down = true;
- body.touching.up = true;
-
- return true;
+ this.data.velocity[0] = this.px2p(speed);
},
/**
- * Internal method. Integrates velocity, global gravity and the delta timer.
+ * If this Body is dynamic then this will move it up by setting its y velocity to the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
*
- * @method Phaser.Physics.Arcade.Body#integrateVelocity
- * @protected
+ * @method Phaser.Physics.Body#moveUp
+ * @param {number} speed - The speed at which it should move up, in pixels per second.
*/
- integrateVelocity: function () {
+ moveUp: function (speed) {
- this._temp = this.game.physics.updateMotion(this);
- this._dx = this.game.time.physicsElapsed * (this.velocity.x + this._temp.x / 2);
- this._dy = this.game.time.physicsElapsed * (this.velocity.y + this._temp.y / 2);
+ this.data.velocity[1] = this.px2p(-speed);
- // positive = RIGHT / DOWN
- // negative = LEFT / UP
+ },
- if ((this._dx < 0 && !this.blocked.left && !this.touching.left) || (this._dx > 0 && !this.blocked.right && !this.touching.right))
- {
- this.x += this._dx;
- this.velocity.x += this._temp.x;
- }
+ /**
+ * If this Body is dynamic then this will move it down by setting its y velocity to the given speed.
+ * The speed is represented in pixels per second. So a value of 100 would move 100 pixels in 1 second (1000ms).
+ *
+ * @method Phaser.Physics.Body#moveDown
+ * @param {number} speed - The speed at which it should move down, in pixels per second.
+ */
+ moveDown: function (speed) {
- if ((this._dy < 0 && !this.blocked.up && !this.touching.up) || (this._dy > 0 && !this.blocked.down && !this.touching.down))
- {
- this.y += this._dy;
- this.velocity.y += this._temp.y;
- }
-
- if (this.velocity.x > this.maxVelocity.x)
- {
- this.velocity.x = this.maxVelocity.x;
- }
- else if (this.velocity.x < -this.maxVelocity.x)
- {
- this.velocity.x = -this.maxVelocity.x;
- }
-
- if (this.velocity.y > this.maxVelocity.y)
- {
- this.velocity.y = this.maxVelocity.y;
- }
- else if (this.velocity.y < -this.maxVelocity.y)
- {
- this.velocity.y = -this.maxVelocity.y;
- }
+ this.data.velocity[1] = this.px2p(speed);
},
/**
* Internal method. This is called directly before the sprites are sent to the renderer and after the update function has finished.
*
- * @method Phaser.Physics.Arcade.Body#postUpdate
+ * @method Phaser.Physics.Body#preUpdate
+ * @protected
+ */
+ preUpdate: function () {
+ },
+
+ /**
+ * Internal method. This is called directly before the sprites are sent to the renderer and after the update function has finished.
+ *
+ * @method Phaser.Physics.Body#postUpdate
* @protected
*/
postUpdate: function () {
- if (this.moves)
+ this.sprite.x = this.p2px(this.data.position[0]);
+ this.sprite.y = this.p2px(this.data.position[1]);
+
+ if (!this.fixedRotation)
{
- this.game.physics.checkBounds(this);
-
- this.reboundCheck(true, true, true);
-
- this._dx = this.deltaX();
- this._dy = this.deltaY();
-
- if (this._dx < 0)
- {
- this.facing = Phaser.LEFT;
- }
- else if (this._dx > 0)
- {
- this.facing = Phaser.RIGHT;
- }
-
- if (this._dy < 0)
- {
- this.facing = Phaser.UP;
- }
- else if (this._dy > 0)
- {
- this.facing = Phaser.DOWN;
- }
-
- if (this._dx !== 0 || this._dy !== 0)
- {
- this.sprite.x += this._dx;
- this.sprite.y += this._dy;
- }
-
- if (this.allowRotation && this.deltaZ() !== 0)
- {
- this.sprite.angle += this.deltaZ();
- }
-
- if (this.sprite.scale.x !== this._sx || this.sprite.scale.y !== this._sy)
- {
- this.updateScale();
- }
+ this.sprite.rotation = this.data.angle;
}
},
/**
- * Resets the Body motion values: velocity, acceleration, angularVelocity and angularAcceleration.
- * Also resets the forces to defaults: gravity, bounce, minVelocity,maxVelocity, angularDrag, maxAngular, mass, friction and checkCollision if 'full' specified.
+ * Resets the Body force, velocity (linear and angular) and rotation. Optionally resets damping and mass.
*
- * @method Phaser.Physics.Arcade.Body#reset
- * @param {boolean} [full=false] - A full reset clears down settings you may have set, such as gravity, bounce and drag. A non-full reset just clears motion values.
+ * @method Phaser.Physics.Body#reset
+ * @param {number} x - The new x position of the Body.
+ * @param {number} y - The new x position of the Body.
+ * @param {boolean} [resetDamping=false] - Resets the linear and angular damping.
+ * @param {boolean} [resetMass=false] - Sets the Body mass back to 1.
*/
- reset: function (full) {
+ reset: function (x, y, resetDamping, resetMass) {
- if (typeof full === 'undefined') { full = false; }
+ if (typeof resetDamping === 'undefined') { resetDamping = false; }
+ if (typeof resetMass === 'undefined') { resetMass = false; }
- if (full)
+ this.setZeroForce();
+ this.setZeroVelocity();
+ this.setZeroRotation();
+
+ if (resetDamping)
{
- this.gravity.setTo(0, 0);
- this.bounce.setTo(0, 0);
- this.minVelocity.setTo(5, 5);
- this.maxVelocity.setTo(1000, 1000);
- this.angularDrag = 0;
- this.maxAngular = 1000;
- this.mass = 1;
- this.friction = 0.0;
- this.checkCollision = { none: false, any: true, up: true, down: true, left: true, right: true };
+ this.setZeroDamping();
}
- this.velocity.setTo(0, 0);
- this.acceleration.setTo(0, 0);
- this.angularVelocity = 0;
- this.angularAcceleration = 0;
- this.blocked = { x: 0, y: 0, up: false, down: false, left: false, right: false };
- this.x = (this.sprite.world.x - (this.sprite.anchor.x * this.sprite.width)) + this.offset.x;
- this.y = (this.sprite.world.y - (this.sprite.anchor.y * this.sprite.height)) + this.offset.y;
- this.preX = this.x;
- this.preY = this.y;
- this.updateBounds();
+ if (resetMass)
+ {
+ this.mass = 1;
+ }
- this.contacts.length = 0;
+ this.x = x;
+ this.y = y;
+
+ },
+
+ /**
+ * Adds this physics body to the world.
+ *
+ * @method Phaser.Physics.Body#addToWorld
+ */
+ addToWorld: function () {
+
+ if (this.data.world !== this.game.physics.world)
+ {
+ this.game.physics.addBody(this);
+ }
+
+ },
+
+ /**
+ * Removes this physics body from the world.
+ *
+ * @method Phaser.Physics.Body#removeFromWorld
+ */
+ removeFromWorld: function () {
+
+ if (this.data.world === this.game.physics.world)
+ {
+ this.game.physics.removeBody(this);
+ }
},
/**
* Destroys this Body and all references it holds to other objects.
*
- * @method Phaser.Physics.Arcade.Body#destroy
+ * @method Phaser.Physics.Body#destroy
*/
destroy: function () {
+ this.removeFromWorld();
+
+ this.clearShapes();
+
this.sprite = null;
+ /*
this.collideCallback = null;
this.collideCallbackContext = null;
-
- this.customSeparateCallback = null;
- this.customSeparateContext = null;
-
- this.contacts.length = 0;
+ */
},
/**
- * Sets this Body to use a circle of the given radius for all collision.
- * The Circle will be centered on the center of the Sprite by default, but can be adjusted via the Body.offset property and the setCircle x/y parameters.
+ * Removes all Shapes from this Body.
*
- * @method Phaser.Physics.Arcade.Body#setCircle
- * @param {number} radius - The radius of this circle (in pixels)
- * @param {number} [offsetX=0] - The x amount the circle will be offset from the Sprites center.
- * @param {number} [offsetY=0] - The y amount the circle will be offset from the Sprites center.
+ * @method Phaser.Physics.Body#clearShapes
*/
- setCircle: function (radius, offsetX, offsetY) {
+ clearShapes: function () {
- if (typeof offsetX === 'undefined') { offsetX = this.sprite._cache.halfWidth; }
- if (typeof offsetY === 'undefined') { offsetY = this.sprite._cache.halfHeight; }
-
- this.type = Phaser.Physics.Arcade.CIRCLE;
- this.shape = new SAT.Circle(new SAT.Vector(this.sprite.x, this.sprite.y), radius);
- this.polygon = null;
-
- this.offset.setTo(offsetX, offsetY);
-
- },
-
- /**
- * Sets this Body to use a rectangle for all collision.
- * If you don't specify any parameters it will be sized to match the parent Sprites current width and height (including scale factor) and centered on the sprite.
- *
- * @method Phaser.Physics.Arcade.Body#setRectangle
- * @param {number} [width] - The width of the rectangle. If not specified it will default to the width of the parent Sprite.
- * @param {number} [height] - The height of the rectangle. If not specified it will default to the height of the parent Sprite.
- * @param {number} [translateX] - The x amount the rectangle will be translated from the Sprites center.
- * @param {number} [translateY] - The y amount the rectangle will be translated from the Sprites center.
- */
- setRectangle: function (width, height, translateX, translateY) {
-
- if (typeof width === 'undefined') { width = this.sprite.width; }
- if (typeof height === 'undefined') { height = this.sprite.height; }
- if (typeof translateX === 'undefined') { translateX = -this.sprite._cache.halfWidth; }
- if (typeof translateY === 'undefined') { translateY = -this.sprite._cache.halfHeight; }
-
- this.type = Phaser.Physics.Arcade.RECT;
- this.shape = new SAT.Box(new SAT.Vector(this.sprite.world.x, this.sprite.world.y), width, height);
- this.polygon = this.shape.toPolygon();
- this.polygon.translate(translateX, translateY);
-
- this.offset.setTo(0, 0);
-
- },
-
- /**
- * Sets this Body to use a convex polygon for collision.
- * The points are specified in a counter-clockwise direction and must create a convex polygon.
- * Use Body.translate and/or Body.offset to re-position the polygon from the Sprite origin.
- *
- * @method Phaser.Physics.Arcade.Body#setPolygon
- * @param {(SAT.Vector[]|number[]|...SAT.Vector|...number)} points - This can be an array of Vectors that form the polygon,
- * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be
- * all the points of the polygon e.g. `setPolygon(new SAT.Vector(), new SAT.Vector(), ...)`, or the
- * arguments passed can be flat x,y values e.g. `setPolygon(x,y, x,y, x,y, ...)` where `x` and `y` are Numbers.
- */
- setPolygon: function (points) {
-
- this.type = Phaser.Physics.Arcade.POLYGON;
- this.shape = null;
-
- if (!Array.isArray(points))
+ for (var i = this.data.shapes.length - 1; i >= 0; i--)
{
- points = Array.prototype.slice.call(arguments);
+ var shape = this.data.shapes[i];
+ this.data.removeShape(shape);
}
- if (typeof points[0] === 'number')
- {
- var p = [];
+ },
+ /**
+ * Add a shape to the body. You can pass a local transform when adding a shape, so that the shape gets an offset and an angle relative to the body center of mass.
+ * Will automatically update the mass properties and bounding radius.
+ *
+ * @method Phaser.Physics.Body#addShape
+ * @param {*} shape - The shape to add to the body.
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Circle|p2.Rectangle|p2.Plane|p2.Line|p2.Particle} The shape that was added to the body.
+ */
+ addShape: function (shape, offsetX, offsetY, rotation) {
+
+ if (typeof offsetX === 'undefined') { offsetX = 0; }
+ if (typeof offsetY === 'undefined') { offsetY = 0; }
+ if (typeof rotation === 'undefined') { rotation = 0; }
+
+ this.data.addShape(shape, [this.px2p(offsetX), this.px2p(offsetY)], rotation);
+
+ return shape;
+
+ },
+
+ /**
+ * Adds a Circle shape to this Body. You can control the offset from the center of the body and the rotation.
+ *
+ * @method Phaser.Physics.Body#addCircle
+ * @param {number} radius - The radius of this circle (in pixels)
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Circle} The Circle shape that was added to the Body.
+ */
+ addCircle: function (radius, offsetX, offsetY, rotation) {
+
+ var shape = new p2.Circle(this.px2p(radius));
+
+ return this.addShape(shape, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Adds a Rectangle shape to this Body. You can control the offset from the center of the body and the rotation.
+ *
+ * @method Phaser.Physics.Body#addRectangle
+ * @param {number} width - The width of the rectangle in pixels.
+ * @param {number} height - The height of the rectangle in pixels.
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Rectangle} The Rectangle shape that was added to the Body.
+ */
+ addRectangle: function (width, height, offsetX, offsetY, rotation) {
+
+ var shape = new p2.Rectangle(this.px2p(width), this.px2p(height));
+
+ return this.addShape(shape, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Adds a Plane shape to this Body. The plane is facing in the Y direction. You can control the offset from the center of the body and the rotation.
+ *
+ * @method Phaser.Physics.Body#addPlane
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Plane} The Plane shape that was added to the Body.
+ */
+ addPlane: function (offsetX, offsetY, rotation) {
+
+ var shape = new p2.Plane();
+
+ return this.addShape(shape, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Adds a Particle shape to this Body. You can control the offset from the center of the body and the rotation.
+ *
+ * @method Phaser.Physics.Body#addParticle
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Particle} The Particle shape that was added to the Body.
+ */
+ addParticle: function (offsetX, offsetY, rotation) {
+
+ var shape = new p2.Particle();
+
+ return this.addShape(shape, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Adds a Line shape to this Body.
+ * The line shape is along the x direction, and stretches from [-length/2, 0] to [length/2,0].
+ * You can control the offset from the center of the body and the rotation.
+ *
+ * @method Phaser.Physics.Body#addLine
+ * @param {number} length - The length of this line (in pixels)
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Line} The Line shape that was added to the Body.
+ */
+ addLine: function (length, offsetX, offsetY, rotation) {
+
+ var shape = new p2.Line(this.px2p(length));
+
+ return this.addShape(shape, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Adds a Capsule shape to this Body.
+ * You can control the offset from the center of the body and the rotation.
+ *
+ * @method Phaser.Physics.Body#addCapsule
+ * @param {number} length - The distance between the end points in pixels.
+ * @param {number} radius - Radius of the capsule in radians.
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Capsule} The Capsule shape that was added to the Body.
+ */
+ addCapsule: function (length, radius, offsetX, offsetY, rotation) {
+
+ var shape = new p2.Capsule(this.px2p(length), radius);
+
+ return this.addShape(shape, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Reads a polygon shape path, and assembles convex shapes from that and puts them at proper offset points. The shape must be simple and without holes.
+ * This function expects the x.y values to be given in pixels. If you want to provide them at p2 world scales then call Body.data.fromPolygon directly.
+ *
+ * @method Phaser.Physics.Body#addPolygon
+ * @param {object} options - An object containing the build options:
+ * @param {boolean} [options.optimalDecomp=false] - Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
+ * @param {boolean} [options.skipSimpleCheck=false] - Set to true if you already know that the path is not intersecting itself.
+ * @param {boolean|number} [options.removeCollinearPoints=false] - Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
+ * @param {(number[]|...number)} points - An array of 2d vectors that form the convex or concave polygon.
+ * Either [[0,0], [0,1],...] or a flat array of numbers that will be interpreted as [x,y, x,y, ...],
+ * or the arguments passed can be flat x,y values e.g. `setPolygon(options, x,y, x,y, x,y, ...)` where `x` and `y` are numbers.
+ * @return {boolean} True on success, else false.
+ */
+ addPolygon: function (options, points) {
+
+ options = options || {};
+
+ points = Array.prototype.slice.call(arguments, 1);
+
+ var path = [];
+
+ // Did they pass in a single array of points?
+ if (points.length === 1 && Array.isArray(points[0]))
+ {
+ path = points[0].slice(0);
+ }
+ else if (Array.isArray(points[0]))
+ {
+ path = points[0].slice(0);
+ // for (var i = 0, len = points[0].length; i < len; i += 2)
+ // {
+ // path.push([points[0][i], points[0][i + 1]]);
+ // }
+ }
+ else if (typeof points[0] === 'number')
+ {
+ // console.log('addPolygon --- We\'ve a list of numbers');
+ // We've a list of numbers
for (var i = 0, len = points.length; i < len; i += 2)
{
- p.push(new SAT.Vector(points[i], points[i + 1]));
+ path.push([points[i], points[i + 1]]);
+ }
+ }
+
+ // console.log('addPolygon PATH pre');
+ // console.log(path[1]);
+ // console.table(path);
+
+ // top and tail
+ var idx = path.length - 1;
+
+ if ( path[idx][0] === path[0][0] && path[idx][1] === path[0][1] )
+ {
+ path.pop();
+ }
+
+ // Now process them into p2 values
+ for (var p = 0; p < path.length; p++)
+ {
+ path[p][0] = this.px2p(path[p][0]);
+ path[p][1] = this.px2p(path[p][1]);
+ }
+
+ // console.log('addPolygon PATH POST');
+ // console.log(path[1]);
+ // console.table(path);
+
+ return this.data.fromPolygon(path, options);
+
+ },
+
+ /**
+ * Remove a shape from the body. Will automatically update the mass properties and bounding radius.
+ *
+ * @method Phaser.Physics.Body#removeShape
+ * @param {p2.Circle|p2.Rectangle|p2.Plane|p2.Line|p2.Particle} shape - The shape to remove from the body.
+ * @return {boolean} True if the shape was found and removed, else false.
+ */
+ removeShape: function (shape) {
+
+ return this.data.removeShape(shape);
+
+ },
+
+ /**
+ * Clears any previously set shapes. Then creates a new Circle shape and adds it to this Body.
+ *
+ * @method Phaser.Physics.Body#setCircle
+ * @param {number} radius - The radius of this circle (in pixels)
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ */
+ setCircle: function (radius, offsetX, offsetY, rotation) {
+
+ this.clearShapes();
+
+ this.addCircle(radius, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Clears any previously set shapes. The creates a new Rectangle shape at the given size and offset, and adds it to this Body.
+ * If you wish to create a Rectangle to match the size of a Sprite or Image see Body.setRectangleFromSprite.
+ *
+ * @method Phaser.Physics.Body#setRectangle
+ * @param {number} [width=16] - The width of the rectangle in pixels.
+ * @param {number} [height=16] - The height of the rectangle in pixels.
+ * @param {number} [offsetX=0] - Local horizontal offset of the shape relative to the body center of mass.
+ * @param {number} [offsetY=0] - Local vertical offset of the shape relative to the body center of mass.
+ * @param {number} [rotation=0] - Local rotation of the shape relative to the body center of mass, specified in radians.
+ * @return {p2.Rectangle} The Rectangle shape that was added to the Body.
+ */
+ setRectangle: function (width, height, offsetX, offsetY, rotation) {
+
+ if (typeof width === 'undefined') { width = 16; }
+ if (typeof height === 'undefined') { height = 16; }
+
+ this.clearShapes();
+
+ return this.addRectangle(width, height, offsetX, offsetY, rotation);
+
+ },
+
+ /**
+ * Clears any previously set shapes.
+ * Then creates a Rectangle shape sized to match the dimensions and orientation of the Sprite given.
+ * If no Sprite is given it defaults to using the parent of this Body.
+ *
+ * @method Phaser.Physics.Body#setRectangleFromSprite
+ * @param {Phaser.Sprite|Phaser.Image} [sprite] - The Sprite on which the Rectangle will get its dimensions.
+ * @return {p2.Rectangle} The Rectangle shape that was added to the Body.
+ */
+ setRectangleFromSprite: function (sprite) {
+
+ if (typeof sprite === 'undefined') { sprite = this.sprite; }
+
+ // because Sprite.phyicsEnabled = true now sets anchor to 0.5
+ // var px = (sprite.width / 2) + (-sprite.width * sprite.anchor.x);
+ // var py = (sprite.height / 2) + (-sprite.height * sprite.anchor.y);
+
+ this.clearShapes();
+
+ return this.addRectangle(sprite.width, sprite.height, 0, 0, sprite.rotation);
+
+ },
+
+ /**
+ * Adds the given Material to all Shapes that belong to this Body.
+ * If you only wish to apply it to a specific Shape in this Body then provide that as the 2nd parameter.
+ *
+ * @method Phaser.Physics.Body#setMaterial
+ * @param {Phaser.Physics.Material} material - The Material that will be applied.
+ * @param {p2.Shape} [shape] - An optional Shape. If not provided the Material will be added to all Shapes in this Body.
+ */
+ setMaterial: function (material, shape) {
+
+ if (typeof shape === 'undefined')
+ {
+ for (var i = this.data.shapes.length - 1; i >= 0; i--)
+ {
+ this.data.shapes[i].material = material;
+ }
+ }
+ else
+ {
+ shape.material = material;
+ }
+
+ },
+
+ /**
+ * Reads the shape data from a physics data file stored in the Game.Cache and adds it as a polygon to this Body.
+ *
+ * @method Phaser.Physics.Body#loadPolygon
+ * @param {string} key - The key of the Physics Data file as stored in Game.Cache.
+ * @param {string} object - The key of the object within the Physics data file that you wish to load the shape data from.
+ * @param {object} options - An object containing the build options:
+ * @param {boolean} [options.optimalDecomp=false] - Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
+ * @param {boolean} [options.skipSimpleCheck=false] - Set to true if you already know that the path is not intersecting itself.
+ * @param {boolean|number} [options.removeCollinearPoints=false] - Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
+ * @return {boolean} True on success, else false.
+ */
+ loadPolygon: function (key, object, options) {
+
+ var data = game.cache.getPhysicsData(key, object);
+
+ if (data && data.shape)
+ {
+ var temp = [];
+
+ // We've a list of numbers
+ for (var i = 0, len = data.shape.length; i < len; i += 2)
+ {
+ temp.push([data.shape[i], data.shape[i + 1]]);
}
- points = p;
+ return this.addPolygon(options, temp);
}
- this.polygon = new SAT.Polygon(new SAT.Vector(this.sprite.center.x, this.sprite.center.y), points);
-
- this.offset.setTo(0, 0);
+ return false;
},
/**
- * Used for translating rectangle and polygon bodies from the Sprite parent. Doesn't apply to Circles.
- * See also the Body.offset property.
+ * Reads the physics data from a physics data file stored in the Game.Cache.
+ * It will add the shape data to this Body, as well as set the density (mass), friction and bounce (restitution) values.
*
- * @method Phaser.Physics.Arcade.Body#translate
- * @param {number} x - The x amount the polygon or rectangle will be translated by from the Sprite.
- * @param {number} y - The y amount the polygon or rectangle will be translated by from the Sprite.
+ * @method Phaser.Physics.Body#loadPolygon
+ * @param {string} key - The key of the Physics Data file as stored in Game.Cache.
+ * @param {string} object - The key of the object within the Physics data file that you wish to load the shape data from.
+ * @param {object} options - An object containing the build options:
+ * @param {boolean} [options.optimalDecomp=false] - Set to true if you need optimal decomposition. Warning: very slow for polygons with more than 10 vertices.
+ * @param {boolean} [options.skipSimpleCheck=false] - Set to true if you already know that the path is not intersecting itself.
+ * @param {boolean|number} [options.removeCollinearPoints=false] - Set to a number (angle threshold value) to remove collinear points, or false to keep all points.
+ * @return {boolean} True on success, else false.
*/
- translate: function (x, y) {
+ loadData: function (key, object, options) {
- if (this.polygon)
+ var data = game.cache.getPhysicsData(key, object);
+
+ if (data && data.shape)
{
- this.polygon.translate(x, y);
+ this.mass = data.density;
+ // set friction + bounce here
+ this.loadPolygon(key, object);
}
},
/**
- * Determines if this Body is 'on the floor', which means in contact with a Tile or World bounds, or other object that has set 'down' as blocked.
- *
- * @method Phaser.Physics.Arcade.Body#onFloor
- * @return {boolean} True if this Body is 'on the floor', which means in contact with a Tile or World bounds, or object that has set 'down' as blocked.
+ * Convert p2 physics value (meters) to pixel scale.
+ *
+ * @method Phaser.Math#p2px
+ * @param {number} v - The value to convert.
+ * @return {number} The scaled value.
*/
- onFloor: function () {
- return this.blocked.down;
+ p2px: function (v) {
+
+ return v *= -20;
+
},
/**
- * Determins if this Body is 'on a wall', which means horizontally in contact with a Tile or World bounds, or other object but not the ground.
- *
- * @method Phaser.Physics.Arcade.Body#onWall
- * @return {boolean} True if this Body is 'on a wall', which means horizontally in contact with a Tile or World bounds, or other object but not the ground.
+ * Convert pixel value to p2 physics scale (meters).
+ *
+ * @method Phaser.Math#px2p
+ * @param {number} v - The value to convert.
+ * @return {number} The scaled value.
*/
- onWall: function () {
- return (!this.blocked.down && (this.blocked.left || this.blocked.right));
- },
+ px2p: function (v) {
- /**
- * Returns the delta x value. The amount the Body has moved horizontally in the current step.
- *
- * @method Phaser.Physics.Arcade.Body#deltaX
- * @return {number} The delta value. Positive if the motion was to the right, negative if to the left.
- */
- deltaX: function () {
- return this.x - this.preX;
- },
+ return v * -0.05;
- /**
- * Returns the delta y value. The amount the Body has moved vertically in the current step.
- *
- * @method Phaser.Physics.Arcade.Body#deltaY
- * @return {number} The delta value. Positive if the motion was downwards, negative if upwards.
- */
- deltaY: function () {
- return this.y - this.preY;
- },
-
- /**
- * Returns the delta z value. The amount the Body has rotated in the current step.
- *
- * @method Phaser.Physics.Arcade.Body#deltaZ
- * @return {number} The delta value.
- */
- deltaZ: function () {
- return this.rotation - this.preRotation;
}
};
-Phaser.Physics.Arcade.Body.prototype.constructor = Phaser.Physics.Arcade.Body;
+Phaser.Physics.Body.prototype.constructor = Phaser.Physics.Body;
/**
-* @name Phaser.Physics.Arcade.Body#x
+* @name Phaser.Physics.Body#static
+* @property {boolean} static - Returns true if the Body is static. Setting Body.static to 'false' will make it dynamic.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "static", {
+
+ get: function () {
+
+ return (this.data.motionState === Phaser.STATIC);
+
+ },
+
+ set: function (value) {
+
+ if (value && this.data.motionState !== Phaser.STATIC)
+ {
+ this.data.motionState = Phaser.STATIC;
+ this.mass = 0;
+ }
+ else if (!value && this.data.motionState === Phaser.STATIC)
+ {
+ this.data.motionState = Phaser.DYNAMIC;
+
+ if (this.mass === 0)
+ {
+ this.mass = 1;
+ }
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#dynamic
+* @property {boolean} dynamic - Returns true if the Body is dynamic. Setting Body.dynamic to 'false' will make it static.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "dynamic", {
+
+ get: function () {
+
+ return (this.data.motionState === Phaser.DYNAMIC);
+
+ },
+
+ set: function (value) {
+
+ if (value && this.data.motionState !== Phaser.DYNAMIC)
+ {
+ this.data.motionState = Phaser.DYNAMIC;
+
+ if (this.mass === 0)
+ {
+ this.mass = 1;
+ }
+ }
+ else if (!value && this.data.motionState === Phaser.DYNAMIC)
+ {
+ this.data.motionState = Phaser.STATIC;
+ this.mass = 0;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#kinematic
+* @property {boolean} kinematic - Returns true if the Body is kinematic. Setting Body.kinematic to 'false' will make it static.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "kinematic", {
+
+ get: function () {
+
+ return (this.data.motionState === Phaser.KINEMATIC);
+
+ },
+
+ set: function (value) {
+
+ if (value && this.data.motionState !== Phaser.KINEMATIC)
+ {
+ this.data.motionState = Phaser.KINEMATIC;
+ this.mass = 4;
+ }
+ else if (!value && this.data.motionState === Phaser.KINEMATIC)
+ {
+ this.data.motionState = Phaser.STATIC;
+ this.mass = 0;
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#allowSleep
+* @property {boolean} allowSleep -
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "allowSleep", {
+
+ get: function () {
+
+ return this.data.allowSleep;
+
+ },
+
+ set: function (value) {
+
+ if (value !== this.data.allowSleep)
+ {
+ this.data.allowSleep = value;
+ }
+
+ }
+
+});
+
+/**
+* The angle of the Body in degrees from its original orientation. Values from 0 to 180 represent clockwise rotation; values from 0 to -180 represent counterclockwise rotation.
+* Values outside this range are added to or subtracted from 360 to obtain a value within the range. For example, the statement Body.angle = 450 is the same as Body.angle = 90.
+* If you wish to work in radians instead of degrees use the property Body.rotation instead. Working in radians is faster as it doesn't have to convert values.
+*
+* @name Phaser.Physics.Body#angle
+* @property {number} angle - The angle of this Body in degrees.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "angle", {
+
+ get: function() {
+
+ return Phaser.Math.wrapAngle(Phaser.Math.radToDeg(this.data.angle));
+
+ },
+
+ set: function(value) {
+
+ this.data.angle = Phaser.Math.degToRad(Phaser.Math.wrapAngle(value));
+
+ }
+
+});
+
+/**
+* Damping is specified as a value between 0 and 1, which is the proportion of velocity lost per second.
+* @name Phaser.Physics.Body#angularDamping
+* @property {number} angularDamping - The angular damping acting acting on the body.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "angularDamping", {
+
+ get: function () {
+
+ return this.data.angularDamping;
+
+ },
+
+ set: function (value) {
+
+ this.data.angularDamping = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#angularForce
+* @property {number} angularForce - The angular force acting on the body.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "angularForce", {
+
+ get: function () {
+
+ return this.data.angularForce;
+
+ },
+
+ set: function (value) {
+
+ this.data.angularForce = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#angularVelocity
+* @property {number} angularVelocity - The angular velocity of the body.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "angularVelocity", {
+
+ get: function () {
+
+ return this.data.angularVelocity;
+
+ },
+
+ set: function (value) {
+
+ this.data.angularVelocity = value;
+
+ }
+
+});
+
+/**
+* Damping is specified as a value between 0 and 1, which is the proportion of velocity lost per second.
+* @name Phaser.Physics.Body#damping
+* @property {number} damping - The linear damping acting on the body in the velocity direction.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "damping", {
+
+ get: function () {
+
+ return this.data.damping;
+
+ },
+
+ set: function (value) {
+
+ this.data.damping = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#fixedRotation
+* @property {boolean} fixedRotation -
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "fixedRotation", {
+
+ get: function () {
+
+ return this.data.fixedRotation;
+
+ },
+
+ set: function (value) {
+
+ if (value !== this.data.fixedRotation)
+ {
+ this.data.fixedRotation = value;
+ // update anything?
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#inertia
+* @property {number} inertia - The inertia of the body around the Z axis..
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "inertia", {
+
+ get: function () {
+
+ return this.data.inertia;
+
+ },
+
+ set: function (value) {
+
+ this.data.inertia = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#mass
+* @property {number} mass -
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "mass", {
+
+ get: function () {
+
+ return this.data.mass;
+
+ },
+
+ set: function (value) {
+
+ if (value !== this.data.mass)
+ {
+ this.data.mass = value;
+ this.data.updateMassProperties();
+
+ if (value === 0)
+ {
+ // this.static = true;
+ }
+ }
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#motionState
+* @property {number} motionState - The type of motion this body has. Should be one of: Body.STATIC (the body does not move), Body.DYNAMIC (body can move and respond to collisions) and Body.KINEMATIC (only moves according to its .velocity).
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "motionState", {
+
+ get: function () {
+
+ return this.data.motionState;
+
+ },
+
+ set: function (value) {
+
+ if (value !== this.data.motionState)
+ {
+ this.data.motionState = value;
+ // update?
+ }
+
+ }
+
+});
+
+/**
+* The angle of the Body in radians.
+* If you wish to work in degrees instead of radians use the Body.angle property instead. Working in radians is faster as it doesn't have to convert values.
+*
+* @name Phaser.Physics.Body#rotation
+* @property {number} rotation - The angle of this Body in radians.
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "rotation", {
+
+ get: function() {
+
+ return this.data.angle;
+
+ },
+
+ set: function(value) {
+
+ this.data.angle = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#sleepSpeedLimit
+* @property {number} sleepSpeedLimit - .
+*/
+Object.defineProperty(Phaser.Physics.Body.prototype, "sleepSpeedLimit", {
+
+ get: function () {
+
+ return this.data.sleepSpeedLimit;
+
+ },
+
+ set: function (value) {
+
+ this.data.sleepSpeedLimit = value;
+
+ }
+
+});
+
+/**
+* @name Phaser.Physics.Body#x
* @property {number} x - The x coordinate of this Body.
*/
-Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "x", {
+Object.defineProperty(Phaser.Physics.Body.prototype, "x", {
get: function () {
-
- if (this.type === Phaser.Physics.Arcade.CIRCLE)
- {
- return this.shape.pos.x;
- }
- else
- {
- return this.polygon.pos.x;
- }
+
+ return this.p2px(this.data.position[0]);
},
set: function (value) {
- if (this.type === Phaser.Physics.Arcade.CIRCLE)
- {
- this.shape.pos.x = value;
- }
- else
- {
- this.polygon.pos.x = value;
- }
+ this.data.position[0] = this.px2p(value);
}
});
/**
-* @name Phaser.Physics.Arcade.Body#y
+* @name Phaser.Physics.Body#y
* @property {number} y - The y coordinate of this Body.
*/
-Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "y", {
+Object.defineProperty(Phaser.Physics.Body.prototype, "y", {
get: function () {
-
- if (this.type === Phaser.Physics.Arcade.CIRCLE)
- {
- return this.shape.pos.y;
- }
- else
- {
- return this.polygon.pos.y;
- }
+
+ return this.p2px(this.data.position[1]);
},
set: function (value) {
- if (this.type === Phaser.Physics.Arcade.CIRCLE)
- {
- this.shape.pos.y = value;
- }
- else
- {
- this.polygon.pos.y = value;
- }
+ this.data.position[1] = this.px2p(value);
}
@@ -41785,6 +53641,188 @@ Object.defineProperty(Phaser.Physics.Arcade.Body.prototype, "y", {
* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/
+/**
+* Creates a spring, connecting two bodies.
+*
+* @class Phaser.Physics.Spring
+* @classdesc Physics Spring Constructor
+* @constructor
+* @param {Phaser.Game} game - A reference to the current game.
+* @param {p2.Body} bodyA - First connected body.
+* @param {p2.Body} bodyB - Second connected body.
+* @param {number} [restLength=1] - Rest length of the spring. A number > 0.
+* @param {number} [stiffness=100] - Stiffness of the spring. A number >= 0.
+* @param {number} [damping=1] - Damping of the spring. A number >= 0.
+* @param {Array} [worldA] - Where to hook the spring to body A, in world coordinates, i.e. [32, 32].
+* @param {Array} [worldB] - Where to hook the spring to body B, in world coordinates, i.e. [32, 32].
+* @param {Array} [localA] - Where to hook the spring to body A, in local body coordinates.
+* @param {Array} [localB] - Where to hook the spring to body B, in local body coordinates.
+*/
+Phaser.Physics.Spring = function (game, bodyA, bodyB, restLength, stiffness, damping, worldA, worldB, localA, localB) {
+
+ /**
+ * @property {Phaser.Game} game - Local reference to game.
+ */
+ this.game = game;
+
+ if (typeof restLength === 'undefined') { restLength = 1; }
+ if (typeof stiffness === 'undefined') { stiffness = 100; }
+ if (typeof damping === 'undefined') { damping = 1; }
+
+ var options = {
+ restLength: restLength,
+ stiffness: stiffness,
+ damping: damping
+ };
+
+ if (typeof worldA !== 'undefined' && worldA !== null)
+ {
+ options.worldAnchorA = [ game.math.px2p(worldA[0]), game.math.px2p(worldA[1]) ];
+ }
+
+ if (typeof worldB !== 'undefined' && worldB !== null)
+ {
+ options.worldAnchorB = [ game.math.px2p(worldB[0]), game.math.px2p(worldB[1]) ];
+ }
+
+ if (typeof localA !== 'undefined' && localA !== null)
+ {
+ options.localAnchorA = [ game.math.px2p(localA[0]), game.math.px2p(localA[1]) ];
+ }
+
+ if (typeof localB !== 'undefined' && localB !== null)
+ {
+ options.localAnchorB = [ game.math.px2p(localB[0]), game.math.px2p(localB[1]) ];
+ }
+
+ p2.Spring.call(this, bodyA, bodyB, options);
+
+}
+
+Phaser.Physics.Spring.prototype = Object.create(p2.Spring.prototype);
+Phaser.Physics.Spring.prototype.constructor = Phaser.Physics.Spring;
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* \o/ ~ "Because I'm a Material girl"
+*
+* @class Phaser.Physics.Material
+* @classdesc Physics Material Constructor
+* @constructor
+*/
+Phaser.Physics.Material = function (name) {
+
+ /**
+ * @property {string} name - The user defined name given to this Material.
+ * @default
+ */
+ this.name = name;
+
+ p2.Material.call(this);
+
+}
+
+Phaser.Physics.Material.prototype = Object.create(p2.Material.prototype);
+Phaser.Physics.Material.prototype.constructor = Phaser.Physics.Material;
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Defines a physics material
+*
+* @class Phaser.Physics.ContactMaterial
+* @classdesc Physics ContactMaterial Constructor
+* @constructor
+* @param {Phaser.Physics.Material} materialA
+* @param {Phaser.Physics.Material} materialB
+* @param {object} [options]
+*/
+Phaser.Physics.ContactMaterial = function (materialA, materialB, options) {
+
+ /**
+ * @property {number} id - The contact material identifier.
+ */
+
+ /**
+ * @property {Phaser.Physics.Material} materialA - First material participating in the contact material.
+ */
+
+ /**
+ * @property {Phaser.Physics.Material} materialB - First second participating in the contact material.
+ */
+
+ /**
+ * @property {number} [friction=0.3] - Friction to use in the contact of these two materials.
+ */
+
+ /**
+ * @property {number} [restitution=0.0] - Restitution to use in the contact of these two materials.
+ */
+
+ /**
+ * @property {number} [stiffness=1e7] - Stiffness of the resulting ContactEquation that this ContactMaterial generate.
+ */
+
+ /**
+ * @property {number} [relaxation=3] - Relaxation of the resulting ContactEquation that this ContactMaterial generate.
+ */
+
+ /**
+ * @property {number} [frictionStiffness=1e7] - Stiffness of the resulting FrictionEquation that this ContactMaterial generate.
+ */
+
+ /**
+ * @property {number} [frictionRelaxation=3] - Relaxation of the resulting FrictionEquation that this ContactMaterial generate.
+ */
+
+ /**
+ * @property {number} [surfaceVelocity=0] - Will add surface velocity to this material. If bodyA rests on top if bodyB, and the surface velocity is positive, bodyA will slide to the right.
+ */
+
+ p2.ContactMaterial.call(this, materialA, materialB, options);
+
+}
+
+Phaser.Physics.ContactMaterial.prototype = Object.create(p2.ContactMaterial.prototype);
+Phaser.Physics.ContactMaterial.prototype.constructor = Phaser.Physics.ContactMaterial;
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
+/**
+* Collision Group
+*
+* @class Phaser.Physics.CollisionGroup
+* @classdesc Physics Collision Group Constructor
+* @constructor
+*/
+Phaser.Physics.CollisionGroup = function (bitmask) {
+
+ /**
+ * @property {number} mask - The CollisionGroup bitmask.
+ */
+ this.mask = bitmask;
+
+}
+
+/**
+* @author Richard Davey
+* @copyright 2014 Photon Storm Ltd.
+* @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
+*/
+
/**
* Phaser.Particles is the Particle Manager for the game. It is called during the game update loop and in turn updates any Emitters attached to it.
*
@@ -42383,7 +54421,6 @@ Phaser.Particles.Arcade.Emitter.prototype.at = function (object) {
* The emitters alpha value.
* @name Phaser.Particles.Arcade.Emitter#alpha
* @property {number} alpha - Gets or sets the alpha value of the Emitter.
-*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "alpha", {
get: function () {
@@ -42395,12 +54432,12 @@ Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "alpha", {
}
});
+*/
/**
* The emitter visible state.
* @name Phaser.Particles.Arcade.Emitter#visible
* @property {boolean} visible - Gets or sets the Emitter visible state.
-*/
Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "visible", {
get: function () {
@@ -42412,6 +54449,7 @@ Object.defineProperty(Phaser.Particles.Arcade.Emitter.prototype, "visible", {
}
});
+*/
/**
* @name Phaser.Particles.Arcade.Emitter#x
@@ -42619,16 +54657,16 @@ Phaser.Tile = function (layer, index, x, y, width, height) {
this.collideDown = false;
/**
- * @property {function} callback - Tile collision callback.
+ * @property {function} collisionCallback - Tile collision callback.
* @default
*/
- this.callback = null;
+ this.collisionCallback = null;
/**
- * @property {object} callbackContext - The context in which the collision callback will be called.
+ * @property {object} collisionCallbackContext - The context in which the collision callback will be called.
* @default
*/
- this.callbackContext = this;
+ this.collisionCallbackContext = this;
};
@@ -42640,12 +54678,12 @@ Phaser.Tile.prototype = {
*
* @method Phaser.Tile#setCollisionCallback
* @param {function} callback - Callback function.
- * @param {object} context - Callback will be called with this context.
+ * @param {object} context - Callback will be called within this context.
*/
setCollisionCallback: function (callback, context) {
- this.collisionCallbackContext = context;
this.collisionCallback = callback;
+ this.collisionCallbackContext = context;
},
@@ -42825,14 +54863,49 @@ Phaser.Tilemap = function (game, key) {
return;
}
+ /**
+ * @property {number} width - The width of the map (in tiles).
+ */
this.width = data.width;
+
+ /**
+ * @property {number} height - The height of the map (in tiles).
+ */
this.height = data.height;
+
+ /**
+ * @property {number} tileWidth - The base width of the tiles in the map (in pixels).
+ */
this.tileWidth = data.tileWidth;
+
+ /**
+ * @property {number} tileHeight - The base height of the tiles in the map (in pixels).
+ */
this.tileHeight = data.tileHeight;
+
+ /**
+ * @property {string} orientation - The orientation of the map data (as specified in Tiled), usually 'orthogonal'.
+ */
this.orientation = data.orientation;
+
+ /**
+ * @property {number} version - The version of the map data (as specified in Tiled, usually 1).
+ */
this.version = data.version;
+
+ /**
+ * @property {object} properties - Map specific properties as specified in Tiled.
+ */
this.properties = data.properties;
+
+ /**
+ * @property {number} widthInPixels - The width of the map in pixels based on width * tileWidth.
+ */
this.widthInPixels = data.widthInPixels;
+
+ /**
+ * @property {number} heightInPixels - The height of the map in pixels based on height * tileHeight.
+ */
this.heightInPixels = data.heightInPixels;
/**
@@ -42855,6 +54928,11 @@ Phaser.Tilemap = function (game, key) {
*/
this.objects = data.objects;
+ /**
+ * @property {array} collision - An array of collision data (polylines, etc).
+ */
+ this.collision = data.collision;
+
/**
* @property {array} images - An array of Tiled Image Layers.
*/
@@ -42984,12 +55062,13 @@ Phaser.Tilemap.prototype = {
},
- // Region? Remove tile from map data?
+ /*
createFromTiles: function (layer, tileIndex, key, frame, group) {
if (typeof group === 'undefined') { group = this.game.world; }
},
+ */
/**
* Creates a Sprite for every object matching the given gid in the map data. You can optionally specify the group that the Sprite will be created in. If none is
@@ -42997,7 +55076,7 @@ Phaser.Tilemap.prototype = {
* configure Sprite properties from within the map editor. For example giving an object a property if alpha: 0.5 in the map editor will duplicate that when the
* Sprite is created. You could also give it a value like: body.velocity.x: 100 to set it moving automatically.
*
- * @method Phaser.Tileset#createFromObjects
+ * @method Phaser.Tilemap#createFromObjects
* @param {string} name - The name of the Object Group to create Sprites from.
* @param {number} gid - The layer array index value, or if a string is given the layer name, within the map data that this TilemapLayer represents.
* @param {string} key - The Game.cache key of the image that this Sprite will use.
@@ -43041,9 +55120,146 @@ Phaser.Tilemap.prototype = {
},
/**
- * Creates a new TilemapLayer object. By default TilemapLayers are fixed to the camera.
+ * Clears all physics bodies from the world for the given layer.
*
- * @method Phaser.Tileset#createLayer
+ * @method Phaser.Tilemap#clearPhysicsBodies
+ * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to this.currentLayer.
+ */
+ clearPhysicsBodies: function (layer) {
+
+ layer = this.getLayer(layer);
+
+ var i = this.layers[layer].bodies.length;
+
+ while (i--)
+ {
+ this.layers[layer].bodies[i].destroy();
+ }
+
+ },
+
+ /**
+ * Goes through all tiles in the given layer and converts those set to collide into physics bodies in the world.
+ * Only call this *after* you have specified all of the tiles you wish to collide with calls like Tilemap.setCollisionBetween, etc.
+ * Every time you call this method it will destroy any previously created bodies and remove them from the world.
+ * Therefore understand it's an expensive operation and not to be done in a core game update loop.
+ *
+ * @method Phaser.Tilemap#generateCollisionData
+ * @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to this.currentLayer.
+ * @param {boolean} [addToWorld=true] - If true it will automatically add each body to the world, otherwise it's up to you to do so.
+ * @return {array} An array of the Phaser.Physics.Body objects that have been created.
+ */
+ generateCollisionData: function (layer, addToWorld) {
+
+ layer = this.getLayer(layer);
+
+ if (typeof addToWorld === 'undefined') { addToWorld = true; }
+
+ // If the bodies array is already populated we need to nuke it
+ if (this.layers[layer].bodies.length > 0)
+ {
+ this.clearPhysicsBodies(layer);
+ }
+
+ this.layers[layer].bodies.length = [];
+
+ var width = 0;
+ var sx = 0;
+ var sy = 0;
+
+ for (var y = 0, h = this.layers[layer].height; y < h; y++)
+ {
+ width = 0;
+
+ for (var x = 0, w = this.layers[layer].width; x < w; x++)
+ {
+ var tile = this.layers[layer].data[y][x];
+
+ if (tile)
+ {
+ right = this.getTileRight(layer, x, y);
+
+ if (width === 0)
+ {
+ sx = tile.x * tile.width;
+ sy = tile.y * tile.height;
+ width = tile.width;
+ }
+
+ if (right && right.collides)
+ {
+ width += tile.width;
+ }
+ else
+ {
+ var body = this.game.physics.createBody(sx, sy, 0, false);
+
+ body.addRectangle(width, tile.height, width / 2, tile.height / 2, 0);
+
+ if (addToWorld)
+ {
+ this.game.physics.addBody(body.data);
+ }
+
+ this.layers[layer].bodies.push(body);
+
+ width = 0;
+ }
+ }
+ }
+ }
+
+ return this.layers[layer].bodies;
+
+ },
+
+ /**
+ * Converts all of the polylines inside a Tiled ObjectGroup into physics bodies that are added to the world.
+ * Note that the polylines must be created in such a way that they can withstand polygon decomposition.
+ *
+ * @method Phaser.Tilemap#createCollisionObjects
+ * @param {string} [layer] - The Tiled layer to operate on that contains the collision data.
+ * @param {boolean} [addToWorld=true] - If true it will automatically add each body to the world.
+ * @return {array} An array of the Phaser.Physics.Body objects that have been created.
+ */
+ createCollisionObjects: function (layer, addToWorld) {
+
+ if (typeof addToWorld === 'undefined') { addToWorld = true; }
+
+ var output = [];
+
+ for (var i = 0, len = this.collision[layer].length; i < len; i++)
+ {
+ // name: json.layers[i].objects[v].name,
+ // x: json.layers[i].objects[v].x,
+ // y: json.layers[i].objects[v].y,
+ // width: json.layers[i].objects[v].width,
+ // height: json.layers[i].objects[v].height,
+ // visible: json.layers[i].objects[v].visible,
+ // properties: json.layers[i].objects[v].properties,
+ // polyline: json.layers[i].objects[v].polyline
+
+ var object = this.collision[layer][i];
+
+ var body = this.game.physics.createBody(object.x, object.y, 0, addToWorld, {}, object.polyline);
+
+ if (body)
+ {
+ output.push(body);
+ }
+
+ }
+
+ return output;
+
+ },
+
+ /**
+ * Creates a new TilemapLayer object. By default TilemapLayers are fixed to the camera.
+ * The `layer` parameter is important. If you've created your map in Tiled then you can get this by looking in Tiled and looking at the Layer name.
+ * Or you can open the JSON file it exports and look at the layers[].name value. Either way it must match.
+ *
+ * @method Phaser.Tilemap#createLayer
* @param {number|string} layer - The layer array index value, or if a string is given the layer name, within the map data that this TilemapLayer represents.
* @param {number} [width] - The rendered width of the layer, should never be wider than Game.width. If not given it will be set to Game.width.
* @param {number} [height] - The rendered height of the layer, should never be wider than Game.height. If not given it will be set to Game.height.
@@ -43078,7 +55294,7 @@ Phaser.Tilemap.prototype = {
/**
* Gets the layer index based on the layers name.
*
- * @method Phaser.Tileset#getIndex
+ * @method Phaser.Tilemap#getIndex
* @protected
* @param {array} location - The local array to search.
* @param {string} name - The name of the array element to get.
@@ -43101,7 +55317,7 @@ Phaser.Tilemap.prototype = {
/**
* Gets the layer index based on its name.
*
- * @method Phaser.Tileset#getLayerIndex
+ * @method Phaser.Tilemap#getLayerIndex
* @param {string} name - The name of the layer to get.
* @return {number} The index of the layer in this tilemap, or null if not found.
*/
@@ -43114,7 +55330,7 @@ Phaser.Tilemap.prototype = {
/**
* Gets the tileset index based on its name.
*
- * @method Phaser.Tileset#getTilesetIndex
+ * @method Phaser.Tilemap#getTilesetIndex
* @param {string} name - The name of the tileset to get.
* @return {number} The index of the tileset in this tilemap, or null if not found.
*/
@@ -43127,7 +55343,7 @@ Phaser.Tilemap.prototype = {
/**
* Gets the image index based on its name.
*
- * @method Phaser.Tileset#getImageIndex
+ * @method Phaser.Tilemap#getImageIndex
* @param {string} name - The name of the image to get.
* @return {number} The index of the image in this tilemap, or null if not found.
*/
@@ -43140,7 +55356,7 @@ Phaser.Tilemap.prototype = {
/**
* Gets the object index based on its name.
*
- * @method Phaser.Tileset#getObjectIndex
+ * @method Phaser.Tilemap#getObjectIndex
* @param {string} name - The name of the object to get.
* @return {number} The index of the object in this tilemap, or null if not found.
*/
@@ -43155,7 +55371,7 @@ Phaser.Tilemap.prototype = {
* If a callback is already set for the tile index it will be replaced. Set the callback to null to remove it.
* If you want to set a callback for a tile at a specific location on the map then see setTileLocationCallback.
*
- * @method Phaser.Tileset#setTileIndexCallback
+ * @method Phaser.Tilemap#setTileIndexCallback
* @param {number|array} indexes - Either a single tile index, or an array of tile indexes to have a collision callback set for.
* @param {function} callback - The callback that will be invoked when the tile is collided with.
* @param {object} callbackContext - The context under which the callback is called.
@@ -43186,7 +55402,7 @@ Phaser.Tilemap.prototype = {
* If a callback is already set for the tile index it will be replaced. Set the callback to null to remove it.
* If you want to set a callback for a tile at a specific location on the map then see setTileLocationCallback.
*
- * @method Phaser.Tileset#setTileLocationCallback
+ * @method Phaser.Tilemap#setTileLocationCallback
* @param {number} x - X position of the top left of the area to copy (given in tiles, not pixels)
* @param {number} y - Y position of the top left of the area to copy (given in tiles, not pixels)
* @param {number} width - The width of the area to copy (given in tiles, not pixels)
@@ -43217,7 +55433,7 @@ Phaser.Tilemap.prototype = {
* Sets collision the given tile or tiles. You can pass in either a single numeric index or an array of indexes: [ 2, 3, 15, 20].
* The `collides` parameter controls if collision will be enabled (true) or disabled (false).
*
- * @method Phaser.Tileset#setCollision
+ * @method Phaser.Tilemap#setCollision
* @param {number|array} indexes - Either a single tile index, or an array of tile IDs to be checked for collision.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear collision.
* @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to this.currentLayer.
@@ -43251,7 +55467,7 @@ Phaser.Tilemap.prototype = {
* Calling this with a start value of 10 and a stop value of 14 would set collision for tiles 10, 11, 12, 13 and 14.
* The `collides` parameter controls if collision will be enabled (true) or disabled (false).
*
- * @method Phaser.Tileset#setCollisionBetween
+ * @method Phaser.Tilemap#setCollisionBetween
* @param {number} start - The first index of the tile to be set for collision.
* @param {number} stop - The last index of the tile to be set for collision.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear collision.
@@ -43282,7 +55498,7 @@ Phaser.Tilemap.prototype = {
* Sets collision on all tiles in the given layer, except for the IDs of those in the given array.
* The `collides` parameter controls if collision will be enabled (true) or disabled (false).
*
- * @method Phaser.Tileset#setCollisionByExclusion
+ * @method Phaser.Tilemap#setCollisionByExclusion
* @param {array} indexes - An array of the tile IDs to not be counted for collision.
* @param {boolean} [collides=true] - If true it will enable collision. If false it will clear collision.
* @param {number|string|Phaser.TilemapLayer} [layer] - The layer to operate on. If not given will default to this.currentLayer.
@@ -43311,7 +55527,7 @@ Phaser.Tilemap.prototype = {
* Sets collision values on a tile in the set.
* You shouldn't usually call this method directly, instead use setCollision, setCollisionBetween or setCollisionByExclusion.
*
- * @method Phaser.Tileset#setCollisionByIndex
+ * @method Phaser.Tilemap#setCollisionByIndex
* @protected
* @param {number} index - The index of the tile on the layer.
* @param {boolean} [collides=true] - If true it will enable collision on the tile. If false it will clear collision values from the tile.
@@ -43354,7 +55570,7 @@ Phaser.Tilemap.prototype = {
/**
* Gets the TilemapLayer index as used in the setCollision calls.
*
- * @method Phaser.Tileset#getLayer
+ * @method Phaser.Tilemap#getLayer
* @protected
* @param {number|string|Phaser.TilemapLayer} layer - The layer to operate on. If not given will default to this.currentLayer.
* @return {number} The TilemapLayer index.
@@ -43385,7 +55601,7 @@ Phaser.Tilemap.prototype = {
/**
* Internal function.
*
- * @method Phaser.Tileset#calculateFaces
+ * @method Phaser.Tilemap#calculateFaces
* @protected
* @param {number} layer - The index of the TilemapLayer to operate on.
*/
@@ -43442,7 +55658,7 @@ Phaser.Tilemap.prototype = {
* Gets the tile above the tile coordinates given.
* Mostly used as an internal function by calculateFaces.
*
- * @method Phaser.Tileset#getTileAbove
+ * @method Phaser.Tilemap#getTileAbove
* @param {number} layer - The local layer index to get the tile from. Can be determined by Tilemap.getLayer().
* @param {number} x - The x coordinate to get the tile from. In tiles, not pixels.
* @param {number} y - The y coordinate to get the tile from. In tiles, not pixels.
@@ -43462,7 +55678,7 @@ Phaser.Tilemap.prototype = {
* Gets the tile below the tile coordinates given.
* Mostly used as an internal function by calculateFaces.
*
- * @method Phaser.Tileset#getTileBelow
+ * @method Phaser.Tilemap#getTileBelow
* @param {number} layer - The local layer index to get the tile from. Can be determined by Tilemap.getLayer().
* @param {number} x - The x coordinate to get the tile from. In tiles, not pixels.
* @param {number} y - The y coordinate to get the tile from. In tiles, not pixels.
@@ -43482,7 +55698,7 @@ Phaser.Tilemap.prototype = {
* Gets the tile to the left of the tile coordinates given.
* Mostly used as an internal function by calculateFaces.
*
- * @method Phaser.Tileset#getTileLeft
+ * @method Phaser.Tilemap#getTileLeft
* @param {number} layer - The local layer index to get the tile from. Can be determined by Tilemap.getLayer().
* @param {number} x - The x coordinate to get the tile from. In tiles, not pixels.
* @param {number} y - The y coordinate to get the tile from. In tiles, not pixels.
@@ -43502,7 +55718,7 @@ Phaser.Tilemap.prototype = {
* Gets the tile to the right of the tile coordinates given.
* Mostly used as an internal function by calculateFaces.
*
- * @method Phaser.Tileset#getTileRight
+ * @method Phaser.Tilemap#getTileRight
* @param {number} layer - The local layer index to get the tile from. Can be determined by Tilemap.getLayer().
* @param {number} x - The x coordinate to get the tile from. In tiles, not pixels.
* @param {number} y - The y coordinate to get the tile from. In tiles, not pixels.
@@ -45001,7 +57217,7 @@ Phaser.TilemapParser = {
}
else
{
- return { layers: [], objects: [], images: [], tilesets: [] };
+ return this.getEmptyData();
}
},
@@ -45045,6 +57261,58 @@ Phaser.TilemapParser = {
},
+ /**
+ * Returns an empty map data object.
+ * @method Phaser.TilemapParser.getEmptyData
+ * @return {object} Generated map data.
+ */
+ getEmptyData: function () {
+
+ var map = {};
+
+ map.width = 0;
+ map.height = 0;
+ map.tileWidth = 0;
+ map.tileHeight = 0;
+ map.orientation = 'orthogonal';
+ map.version = '1';
+ map.properties = {};
+ map.widthInPixels = 0;
+ map.heightInPixels = 0;
+
+ var layers = [];
+
+ var layer = {
+
+ name: 'layer',
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0,
+ widthInPixels: 0,
+ heightInPixels: 0,
+ alpha: 1,
+ visible: true,
+ properties: {},
+ indexes: [],
+ callbacks: [],
+ data: []
+
+ };
+
+ layers.push(layer);
+
+ map.layers = layers;
+ map.images = [];
+ map.objects = {};
+ map.collision = {};
+ map.tilesets = [];
+ map.tiles = [];
+
+ return map;
+
+ },
+
/**
* Parses a Tiled JSON file into valid map data.
* @method Phaser.TilemapParser.parseJSON
@@ -45095,7 +57363,8 @@ Phaser.TilemapParser = {
visible: json.layers[i].visible,
properties: {},
indexes: [],
- callbacks: []
+ callbacks: [],
+ bodies: []
};
@@ -45177,8 +57446,9 @@ Phaser.TilemapParser = {
map.images = images;
- // Objects
+ // Objects & Collision Data (polylines, etc)
var objects = {};
+ var collision = {};
for (var i = 0; i < json.layers.length; i++)
{
@@ -45188,10 +57458,11 @@ Phaser.TilemapParser = {
}
objects[json.layers[i].name] = [];
+ collision[json.layers[i].name] = [];
for (var v = 0, len = json.layers[i].objects.length; v < len; v++)
{
- // For now we'll just support object tiles
+ // Object Tiles
if (json.layers[i].objects[v].gid)
{
var object = {
@@ -45207,11 +57478,37 @@ Phaser.TilemapParser = {
objects[json.layers[i].name].push(object);
}
+ else if (json.layers[i].objects[v].polyline)
+ {
+ var object = {
+
+ name: json.layers[i].objects[v].name,
+ x: json.layers[i].objects[v].x,
+ y: json.layers[i].objects[v].y,
+ width: json.layers[i].objects[v].width,
+ height: json.layers[i].objects[v].height,
+ visible: json.layers[i].objects[v].visible,
+ properties: json.layers[i].objects[v].properties
+
+ };
+
+ object.polyline = [];
+
+ // Parse the polyline into an array
+ for (var p = 0; p < json.layers[i].objects[v].polyline.length; p++)
+ {
+ object.polyline.push([ json.layers[i].objects[v].polyline[p].x, json.layers[i].objects[v].polyline[p].y ]);
+ }
+
+ collision[json.layers[i].name].push(object);
+
+ }
}
}
map.objects = objects;
+ map.collision = collision;
// Tilesets
var tilesets = [];
@@ -45445,286 +57742,5 @@ Phaser.Tileset.prototype = {
};
Phaser.Tileset.prototype.constructor = Phaser.Tileset;
-
-/**
-* We're replacing a couple of Pixi's methods here to fix or add some vital functionality:
-*
-* 1) Added support for Trimmed sprite sheets
-* 2) Skip display objects with an alpha of zero
-* 3) Avoid Style Recalculation from the incorrect bgcolor value
-* 4) Added support for Canvas unit rounding via Phaser.CANVAS_PX_ROUND boolean (disabled by default).
-*
-* Hopefully we can remove this once Pixi has been updated to support these things.
-*/
-
-/**
- * Renders the stage to its canvas view
- *
- * @method render
- * @param stage {Stage} the Stage element to be rendered
- */
-PIXI.CanvasRenderer.prototype.render = function(stage)
-{
- PIXI.texturesToUpdate.length = 0;
- PIXI.texturesToDestroy.length = 0;
-
- PIXI.visibleCount++;
- stage.updateTransform();
-
- this.context.setTransform(1, 0, 0, 1, 0, 0);
-
- if (Phaser.CANVAS_CLEAR_RECT)
- {
- this.context.clearRect(0, 0, this.width, this.height)
- }
-
- this.renderDisplayObject(stage, false);
-
- // Remove frame updates
- if (PIXI.Texture.frameUpdates.length > 0)
- {
- PIXI.Texture.frameUpdates.length = 0;
- }
-
-}
-
-// @param {boolean} [renderHidden=false] - If true displayObjects that have their visible property set to false will still be rendered.
-
-PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, renderHidden)
-{
- // Once the display object hits this we can break the loop
- var testObject = displayObject.last._iNext;
- displayObject = displayObject.first;
-
- do
- {
- if (!displayObject.visible && !renderHidden)
- {
- displayObject = displayObject.last._iNext;
- continue;
- }
-
- if (!displayObject.renderable || displayObject.alpha === 0)
- {
- displayObject = displayObject._iNext;
- continue;
- }
-
- if (displayObject instanceof PIXI.Sprite)
- {
- if (displayObject.texture.frame)
- {
- this.context.globalAlpha = displayObject.worldAlpha;
-
- if (Phaser.CANVAS_PX_ROUND)
- {
- this.context.setTransform(
- displayObject.worldTransform[0],
- displayObject.worldTransform[3],
- displayObject.worldTransform[1],
- displayObject.worldTransform[4],
- Math.floor(displayObject.worldTransform[2]),
- Math.floor(displayObject.worldTransform[5]));
- }
- else
- {
- this.context.setTransform(
- displayObject.worldTransform[0],
- displayObject.worldTransform[3],
- displayObject.worldTransform[1],
- displayObject.worldTransform[4],
- displayObject.worldTransform[2],
- displayObject.worldTransform[5]);
- }
-
- if (displayObject.texture.trimmed)
- {
- this.context.transform(1, 0, 0, 1, displayObject.texture.trim.x, displayObject.texture.trim.y);
- }
-
- //if smoothingEnabled is supported and we need to change the smoothing property for this texture
- if (this.smoothProperty && this.scaleMode !== displayObject.texture.baseTexture.scaleMode)
- {
- this.scaleMode = displayObject.texture.baseTexture.scaleMode;
- this.context[this.smoothProperty] = (this.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR);
- }
-
- this.context.drawImage(
- displayObject.texture.baseTexture.source,
- displayObject.texture.frame.x,
- displayObject.texture.frame.y,
- displayObject.texture.frame.width,
- displayObject.texture.frame.height,
- Math.floor((displayObject.anchor.x) * -displayObject.texture.frame.width),
- Math.floor((displayObject.anchor.y) * -displayObject.texture.frame.height),
- displayObject.texture.frame.width,
- displayObject.texture.frame.height);
- }
- }
- else if (displayObject instanceof PIXI.Strip)
- {
- this.context.setTransform(displayObject.worldTransform[0], displayObject.worldTransform[3], displayObject.worldTransform[1], displayObject.worldTransform[4], displayObject.worldTransform[2], displayObject.worldTransform[5])
- this.renderStrip(displayObject);
- }
- else if (displayObject instanceof PIXI.TilingSprite)
- {
- this.context.setTransform(displayObject.worldTransform[0], displayObject.worldTransform[3], displayObject.worldTransform[1], displayObject.worldTransform[4], displayObject.worldTransform[2], displayObject.worldTransform[5])
- this.renderTilingSprite(displayObject);
- }
- else if (displayObject instanceof PIXI.CustomRenderable)
- {
- displayObject.renderCanvas(this);
- }
- else if (displayObject instanceof PIXI.Graphics)
- {
- this.context.setTransform(displayObject.worldTransform[0], displayObject.worldTransform[3], displayObject.worldTransform[1], displayObject.worldTransform[4], displayObject.worldTransform[2], displayObject.worldTransform[5])
- PIXI.CanvasGraphics.renderGraphics(displayObject, this.context);
- }
- else if (displayObject instanceof PIXI.FilterBlock)
- {
- if (displayObject.open)
- {
- this.context.save();
-
- var cacheAlpha = displayObject.mask.alpha;
- var maskTransform = displayObject.mask.worldTransform;
-
- this.context.setTransform(maskTransform[0], maskTransform[3], maskTransform[1], maskTransform[4], maskTransform[2], maskTransform[5])
-
- displayObject.mask.worldAlpha = 0.5;
-
- this.context.worldAlpha = 0;
-
- PIXI.CanvasGraphics.renderGraphicsMask(displayObject.mask, this.context);
- this.context.clip();
-
- displayObject.mask.worldAlpha = cacheAlpha;
- }
- else
- {
- this.context.restore();
- }
- }
- // count++
- displayObject = displayObject._iNext;
- }
- while(displayObject != testObject)
-
-}
-
-PIXI.WebGLBatch.prototype.update = function()
-{
- // var gl = this.gl;
- // var worldTransform, width, height, aX, aY, w0, w1, h0, h1, index, index2, index3
-
- var worldTransform, width, height, aX, aY, w0, w1, h0, h1, index;
-
- var a, b, c, d, tx, ty;
-
- var indexRun = 0;
-
- var displayObject = this.head;
-
- while(displayObject)
- {
- if(displayObject.vcount === PIXI.visibleCount)
- {
- width = displayObject.texture.frame.width;
- height = displayObject.texture.frame.height;
-
- // TODO trim??
- aX = displayObject.anchor.x;// - displayObject.texture.trim.x
- aY = displayObject.anchor.y; //- displayObject.texture.trim.y
- w0 = width * (1-aX);
- w1 = width * -aX;
-
- h0 = height * (1-aY);
- h1 = height * -aY;
-
- index = indexRun * 8;
-
- worldTransform = displayObject.worldTransform;
-
- a = worldTransform[0];
- b = worldTransform[3];
- c = worldTransform[1];
- d = worldTransform[4];
- tx = worldTransform[2];
- ty = worldTransform[5];
-
- if (displayObject.texture.trimmed)
- {
- tx += displayObject.texture.trim.x;
- ty += displayObject.texture.trim.y;
- }
-
- this.verticies[index + 0 ] = a * w1 + c * h1 + tx;
- this.verticies[index + 1 ] = d * h1 + b * w1 + ty;
-
- this.verticies[index + 2 ] = a * w0 + c * h1 + tx;
- this.verticies[index + 3 ] = d * h1 + b * w0 + ty;
-
- this.verticies[index + 4 ] = a * w0 + c * h0 + tx;
- this.verticies[index + 5 ] = d * h0 + b * w0 + ty;
-
- this.verticies[index + 6] = a * w1 + c * h0 + tx;
- this.verticies[index + 7] = d * h0 + b * w1 + ty;
-
- if(displayObject.updateFrame || displayObject.texture.updateFrame)
- {
- this.dirtyUVS = true;
-
- var texture = displayObject.texture;
-
- var frame = texture.frame;
- var tw = texture.baseTexture.width;
- var th = texture.baseTexture.height;
-
- this.uvs[index + 0] = frame.x / tw;
- this.uvs[index +1] = frame.y / th;
-
- this.uvs[index +2] = (frame.x + frame.width) / tw;
- this.uvs[index +3] = frame.y / th;
-
- this.uvs[index +4] = (frame.x + frame.width) / tw;
- this.uvs[index +5] = (frame.y + frame.height) / th;
-
- this.uvs[index +6] = frame.x / tw;
- this.uvs[index +7] = (frame.y + frame.height) / th;
-
- displayObject.updateFrame = false;
- }
-
- // TODO this probably could do with some optimisation....
- if(displayObject.cacheAlpha != displayObject.worldAlpha)
- {
- displayObject.cacheAlpha = displayObject.worldAlpha;
-
- var colorIndex = indexRun * 4;
- this.colors[colorIndex] = this.colors[colorIndex + 1] = this.colors[colorIndex + 2] = this.colors[colorIndex + 3] = displayObject.worldAlpha;
- this.dirtyColors = true;
- }
- }
- else
- {
- index = indexRun * 8;
-
- this.verticies[index + 0 ] = 0;
- this.verticies[index + 1 ] = 0;
-
- this.verticies[index + 2 ] = 0;
- this.verticies[index + 3 ] = 0;
-
- this.verticies[index + 4 ] = 0;
- this.verticies[index + 5 ] = 0;
-
- this.verticies[index + 6] = 0;
- this.verticies[index + 7] = 0;
- }
-
- indexRun++;
- displayObject = displayObject.__next;
- }
-}
return Phaser;
});
\ No newline at end of file
diff --git a/build/phaser.min.js b/build/phaser.min.js
index 26cbc352..652b7ba8 100644
--- a/build/phaser.min.js
+++ b/build/phaser.min.js
@@ -1,13 +1,16 @@
-/*! Phaser v1.1.4 | (c) 2013 Photon Storm Ltd. */
-!function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.Phaser=b()}(this,function(){function a(){return b.Matrix="undefined"!=typeof Float32Array?Float32Array:Array,b.Matrix}var b=b||{},c=c||{VERSION:"1.1.4",DEV_VERSION:"1.1.4",GAMES:[],AUTO:0,CANVAS:1,WEBGL:2,HEADLESS:3,SPRITE:0,BUTTON:1,BULLET:2,GRAPHICS:3,TEXT:4,TILESPRITE:5,BITMAPTEXT:6,GROUP:7,RENDERTEXTURE:8,TILEMAP:9,TILEMAPLAYER:10,EMITTER:11,POLYGON:12,BITMAPDATA:13,CANVAS_FILTER:14,WEBGL_FILTER:15,NONE:0,LEFT:1,RIGHT:2,UP:3,DOWN:4,CANVAS_PX_ROUND:!1,CANVAS_CLEAR_RECT:!0};b.InteractionManager=function(){},c.Utils={shuffle:function(a){for(var b=a.length-1;b>0;b--){var c=Math.floor(Math.random()*(b+1)),d=a[b];a[b]=a[c],a[c]=d}return a},pad:function(a,b,c,d){if("undefined"==typeof b)var b=0;if("undefined"==typeof c)var c=" ";if("undefined"==typeof d)var d=3;var e=0;if(b+1>=a.length)switch(d){case 1:a=Array(b+1-a.length).join(c)+a;break;case 3:var f=Math.ceil((e=b-a.length)/2),g=e-f;a=Array(g+1).join(c)+a+Array(f+1).join(c);break;default:a+=Array(b+1-a.length).join(c)}return a},isPlainObject:function(a){if("object"!=typeof a||a.nodeType||a===a.window)return!1;try{if(a.constructor&&!hasOwn.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}return!0},extend:function(){var a,b,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;for("boolean"==typeof h&&(k=h,h=arguments[1]||{},i=2),j===i&&(h=this,--i);j>i;i++)if(null!=(a=arguments[i]))for(b in a)d=h[b],e=a[b],h!==e&&(k&&e&&(c.Utils.isPlainObject(e)||(f=Array.isArray(e)))?(f?(f=!1,g=d&&Array.isArray(d)?d:[]):g=d&&c.Utils.isPlainObject(d)?d:{},h[b]=c.Utils.extend(k,g,e)):void 0!==e&&(h[b]=e));return h}},b.hex2rgb=function(a){return[(255&a>>16)/255,(255&a>>8)/255,(255&a)/255]},"function"!=typeof Function.prototype.bind&&(Function.prototype.bind=function(){var a=Array.prototype.slice;return function(b){function c(){var f=e.concat(a.call(arguments));d.apply(this instanceof c?this:b,f)}var d=this,e=a.call(arguments,1);if("function"!=typeof d)throw new TypeError;return c.prototype=function f(a){return a&&(f.prototype=a),this instanceof f?void 0:new f}(d.prototype),c}}()),Array.isArray||(Array.isArray=function(a){return"[object Array]"==Object.prototype.toString.call(a)}),a(),b.mat3={},b.mat3.create=function(){var a=new b.Matrix(9);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},b.mat3.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},b.mat4={},b.mat4.create=function(){var a=new b.Matrix(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},b.mat3.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=b[0],n=b[1],o=b[2],p=b[3],q=b[4],r=b[5],s=b[6],t=b[7],u=b[8];return c[0]=m*d+n*g+o*j,c[1]=m*e+n*h+o*k,c[2]=m*f+n*i+o*l,c[3]=p*d+q*g+r*j,c[4]=p*e+q*h+r*k,c[5]=p*f+q*i+r*l,c[6]=s*d+t*g+u*j,c[7]=s*e+t*h+u*k,c[8]=s*f+t*i+u*l,c},b.mat3.clone=function(a){var c=new b.Matrix(9);return c[0]=a[0],c[1]=a[1],c[2]=a[2],c[3]=a[3],c[4]=a[4],c[5]=a[5],c[6]=a[6],c[7]=a[7],c[8]=a[8],c},b.mat3.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[5];return a[1]=a[3],a[2]=a[6],a[3]=c,a[5]=a[7],a[6]=d,a[7]=e,a}return b[0]=a[0],b[1]=a[3],b[2]=a[6],b[3]=a[1],b[4]=a[4],b[5]=a[7],b[6]=a[2],b[7]=a[5],b[8]=a[8],b},b.mat3.toMat4=function(a,c){return c||(c=b.mat4.create()),c[15]=1,c[14]=0,c[13]=0,c[12]=0,c[11]=0,c[10]=a[8],c[9]=a[7],c[8]=a[6],c[7]=0,c[6]=a[5],c[5]=a[4],c[4]=a[3],c[3]=0,c[2]=a[2],c[1]=a[1],c[0]=a[0],c},b.mat4.create=function(){var a=new b.Matrix(16);return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},b.mat4.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[3],f=a[6],g=a[7],h=a[11];return a[1]=a[4],a[2]=a[8],a[3]=a[12],a[4]=c,a[6]=a[9],a[7]=a[13],a[8]=d,a[9]=f,a[11]=a[14],a[12]=e,a[13]=g,a[14]=h,a}return b[0]=a[0],b[1]=a[4],b[2]=a[8],b[3]=a[12],b[4]=a[1],b[5]=a[5],b[6]=a[9],b[7]=a[13],b[8]=a[2],b[9]=a[6],b[10]=a[10],b[11]=a[14],b[12]=a[3],b[13]=a[7],b[14]=a[11],b[15]=a[15],b},b.mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=a[9],n=a[10],o=a[11],p=a[12],q=a[13],r=a[14],s=a[15],t=b[0],u=b[1],v=b[2],w=b[3];return c[0]=t*d+u*h+v*l+w*p,c[1]=t*e+u*i+v*m+w*q,c[2]=t*f+u*j+v*n+w*r,c[3]=t*g+u*k+v*o+w*s,t=b[4],u=b[5],v=b[6],w=b[7],c[4]=t*d+u*h+v*l+w*p,c[5]=t*e+u*i+v*m+w*q,c[6]=t*f+u*j+v*n+w*r,c[7]=t*g+u*k+v*o+w*s,t=b[8],u=b[9],v=b[10],w=b[11],c[8]=t*d+u*h+v*l+w*p,c[9]=t*e+u*i+v*m+w*q,c[10]=t*f+u*j+v*n+w*r,c[11]=t*g+u*k+v*o+w*s,t=b[12],u=b[13],v=b[14],w=b[15],c[12]=t*d+u*h+v*l+w*p,c[13]=t*e+u*i+v*m+w*q,c[14]=t*f+u*j+v*n+w*r,c[15]=t*g+u*k+v*o+w*s,c},b.Point=function(a,b){this.x=a||0,this.y=b||0},b.Point.prototype.clone=function(){return new b.Point(this.x,this.y)},b.Point.prototype.constructor=b.Point,b.Rectangle=function(a,b,c,d){this.x=a||0,this.y=b||0,this.width=c||0,this.height=d||0},b.Rectangle.prototype.clone=function(){return new b.Rectangle(this.x,this.y,this.width,this.height)},b.Rectangle.prototype.contains=function(a,b){if(this.width<=0||this.height<=0)return!1;var c=this.x;if(a>=c&&a<=c+this.width){var d=this.y;if(b>=d&&b<=d+this.height)return!0}return!1},b.Rectangle.prototype.constructor=b.Rectangle,b.Polygon=function(a){if(a instanceof Array||(a=Array.prototype.slice.call(arguments)),"number"==typeof a[0]){for(var c=[],d=0,e=a.length;e>d;d+=2)c.push(new b.Point(a[d],a[d+1]));a=c}this.points=a},b.Polygon.prototype.clone=function(){for(var a=[],c=0;cb!=i>b&&(h-f)*(b-g)/(i-g)+f>a;j&&(c=!c)}return c},b.Polygon.prototype.constructor=b.Polygon,b.DisplayObject=function(){this.last=this,this.first=this,this.position=new b.Point,this.scale=new b.Point(1,1),this.pivot=new b.Point(0,0),this.rotation=0,this.alpha=1,this.visible=!0,this.hitArea=null,this.buttonMode=!1,this.renderable=!1,this.parent=null,this.stage=null,this.worldAlpha=1,this._interactive=!1,this.defaultCursor="pointer",this.worldTransform=b.mat3.create(),this.localTransform=b.mat3.create(),this.color=[],this.dynamic=!0,this._sr=0,this._cr=1,this.filterArea=new b.Rectangle(0,0,1,1)},b.DisplayObject.prototype.constructor=b.DisplayObject,b.DisplayObject.prototype.setInteractive=function(a){this.interactive=a},Object.defineProperty(b.DisplayObject.prototype,"interactive",{get:function(){return this._interactive},set:function(a){this._interactive=a,this.stage&&(this.stage.dirty=!0)}}),Object.defineProperty(b.DisplayObject.prototype,"mask",{get:function(){return this._mask},set:function(a){a?this._mask?(a.start=this._mask.start,a.end=this._mask.end):(this.addFilter(a),a.renderable=!1):(this.removeFilter(this._mask),this._mask.renderable=!0),this._mask=a}}),Object.defineProperty(b.DisplayObject.prototype,"filters",{get:function(){return this._filters},set:function(a){if(a){this._filters&&this.removeFilter(this._filters),this.addFilter(a);for(var b=[],c=0;c=0&&b<=this.children.length))throw new Error(a+" The index "+b+" supplied is out of bounds "+this.children.length);if(void 0!==a.parent&&a.parent.removeChild(a),a.parent=this,this.stage){var c=a;do c.interactive&&(this.stage.dirty=!0),c.stage=this.stage,c=c._iNext;while(c)}var d,e,f=a.first,g=a.last;if(b===this.children.length){e=this.last;for(var h=this,i=this.last;h;)h.last===i&&(h.last=a.last),h=h.parent}else e=0===b?this:this.children[b-1].last;d=e._iNext,d&&(d._iPrev=g,g._iNext=d),f._iPrev=e,e._iNext=f,this.children.splice(b,0,a),this.__renderGroup&&(a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a),this.__renderGroup.addDisplayObjectAndChildren(a))},b.DisplayObjectContainer.prototype.swapChildren=function(a,b){if(a!==b){var c=this.children.indexOf(a),d=this.children.indexOf(b);if(0>c||0>d)throw new Error("swapChildren: Both the supplied DisplayObjects must be a child of the caller.");this.removeChild(a),this.removeChild(b),d>c?(this.addChildAt(b,c),this.addChildAt(a,d)):(this.addChildAt(a,d),this.addChildAt(b,c))}},b.DisplayObjectContainer.prototype.getChildAt=function(a){if(a>=0&&aa;a++)this.children[a].updateTransform()}},b.blendModes={},b.blendModes.NORMAL=0,b.blendModes.SCREEN=1,b.Sprite=function(a){b.DisplayObjectContainer.call(this),this.anchor=new b.Point,this.texture=a,this.blendMode=b.blendModes.NORMAL,this._width=0,this._height=0,a.baseTexture.hasLoaded?this.updateFrame=!0:(this.onTextureUpdateBind=this.onTextureUpdate.bind(this),this.texture.addEventListener("update",this.onTextureUpdateBind)),this.renderable=!0},b.Sprite.prototype=Object.create(b.DisplayObjectContainer.prototype),b.Sprite.prototype.constructor=b.Sprite,Object.defineProperty(b.Sprite.prototype,"width",{get:function(){return this.scale.x*this.texture.frame.width},set:function(a){this.scale.x=a/this.texture.frame.width,this._width=a}}),Object.defineProperty(b.Sprite.prototype,"height",{get:function(){return this.scale.y*this.texture.frame.height},set:function(a){this.scale.y=a/this.texture.frame.height,this._height=a}}),b.Sprite.prototype.setTexture=function(a){this.texture.baseTexture!==a.baseTexture?(this.textureChange=!0,this.texture=a,this.__renderGroup&&this.__renderGroup.updateTexture(this)):this.texture=a,this.updateFrame=!0},b.Sprite.prototype.onTextureUpdate=function(){this._width&&(this.scale.x=this._width/this.texture.frame.width),this._height&&(this.scale.y=this._height/this.texture.frame.height),this.updateFrame=!0},b.Sprite.fromFrame=function(a){var c=b.TextureCache[a];if(!c)throw new Error('The frameId "'+a+'" does not exist in the texture cache'+this);return new b.Sprite(c)},b.Sprite.fromImage=function(a){var c=b.Texture.fromImage(a);return new b.Sprite(c)},b.Stage=function(a){b.DisplayObjectContainer.call(this),this.worldTransform=b.mat3.create(),this.interactive=!0,this.interactionManager=new b.InteractionManager(this),this.dirty=!0,this.__childrenAdded=[],this.__childrenRemoved=[],this.stage=this,this.stage.hitArea=new b.Rectangle(0,0,1e5,1e5),this.setBackgroundColor(a),this.worldVisible=!0},b.Stage.prototype=Object.create(b.DisplayObjectContainer.prototype),b.Stage.prototype.constructor=b.Stage,b.Stage.prototype.setInteractionDelegate=function(a){this.interactionManager.setTargetDomElement(a)},b.Stage.prototype.updateTransform=function(){this.worldAlpha=1,this.vcount=b.visibleCount;for(var a=0,c=this.children.length;c>a;a++)this.children[a].updateTransform();this.dirty&&(this.dirty=!1,this.interactionManager.dirty=!0),this.interactive&&this.interactionManager.update()},b.Stage.prototype.setBackgroundColor=function(a){this.backgroundColor=a||0,this.backgroundColorSplit=b.hex2rgb(this.backgroundColor);var c=this.backgroundColor.toString(16);c="000000".substr(0,6-c.length)+c,this.backgroundColorString="#"+c},b.Stage.prototype.getMousePosition=function(){return this.interactionManager.mouse.global},b.CustomRenderable=function(){b.DisplayObject.call(this),this.renderable=!0},b.CustomRenderable.prototype=Object.create(b.DisplayObject.prototype),b.CustomRenderable.prototype.constructor=b.CustomRenderable,b.CustomRenderable.prototype.renderCanvas=function(){},b.CustomRenderable.prototype.initWebGL=function(){},b.CustomRenderable.prototype.renderWebGL=function(){},b.Strip=function(a,c,d){b.DisplayObjectContainer.call(this),this.texture=a,this.blendMode=b.blendModes.NORMAL;try{this.uvs=new Float32Array([0,1,1,1,1,0,0,1]),this.verticies=new Float32Array([0,0,0,0,0,0,0,0,0]),this.colors=new Float32Array([1,1,1,1]),this.indices=new Uint16Array([0,1,2,3])}catch(e){this.uvs=[0,1,1,1,1,0,0,1],this.verticies=[0,0,0,0,0,0,0,0,0],this.colors=[1,1,1,1],this.indices=[0,1,2,3]}this.width=c,this.height=d,a.baseTexture.hasLoaded?(this.width=this.texture.frame.width,this.height=this.texture.frame.height,this.updateFrame=!0):(this.onTextureUpdateBind=this.onTextureUpdate.bind(this),this.texture.addEventListener("update",this.onTextureUpdateBind)),this.renderable=!0},b.Strip.prototype=Object.create(b.DisplayObjectContainer.prototype),b.Strip.prototype.constructor=b.Strip,b.Strip.prototype.setTexture=function(a){this.texture=a,this.width=a.frame.width,this.height=a.frame.height,this.updateFrame=!0},b.Strip.prototype.onTextureUpdate=function(){this.updateFrame=!0},b.Rope=function(a,c){b.Strip.call(this,a),this.points=c;try{this.verticies=new Float32Array(4*c.length),this.uvs=new Float32Array(4*c.length),this.colors=new Float32Array(2*c.length),this.indices=new Uint16Array(2*c.length)}catch(d){this.verticies=new Array(4*c.length),this.uvs=new Array(4*c.length),this.colors=new Array(2*c.length),this.indices=new Array(2*c.length)}this.refresh()},b.Rope.prototype=Object.create(b.Strip.prototype),b.Rope.prototype.constructor=b.Rope,b.Rope.prototype.refresh=function(){var a=this.points;if(!(a.length<1)){var b=this.uvs,c=a[0],d=this.indices,e=this.colors;this.count-=.2,b[0]=0,b[1]=1,b[2]=0,b[3]=1,e[0]=1,e[1]=1,d[0]=0,d[1]=1;for(var f,g,h,i=a.length,j=1;i>j;j++)f=a[j],g=4*j,h=j/(i-1),j%2?(b[g]=h,b[g+1]=0,b[g+2]=h,b[g+3]=1):(b[g]=h,b[g+1]=0,b[g+2]=h,b[g+3]=1),g=2*j,e[g]=1,e[g+1]=1,g=2*j,d[g]=g,d[g+1]=g+1,c=f}},b.Rope.prototype.updateTransform=function(){var a=this.points;if(!(a.length<1)){var c,d=a[0],e={x:0,y:0};this.count-=.2;var f=this.verticies;f[0]=d.x+e.x,f[1]=d.y+e.y,f[2]=d.x-e.x,f[3]=d.y-e.y;for(var g,h,i,j,k,l=a.length,m=1;l>m;m++)g=a[m],h=4*m,c=m1&&(i=1),j=Math.sqrt(e.x*e.x+e.y*e.y),k=this.texture.height/2,e.x/=j,e.y/=j,e.x*=k,e.y*=k,f[h]=g.x+e.x,f[h+1]=g.y+e.y,f[h+2]=g.x-e.x,f[h+3]=g.y-e.y,d=g;b.DisplayObjectContainer.prototype.updateTransform.call(this)}},b.Rope.prototype.setTexture=function(a){this.texture=a,this.updateFrame=!0},b.TilingSprite=function(a,c,d){b.DisplayObjectContainer.call(this),this.texture=a,this.width=c,this.height=d,this.tileScale=new b.Point(1,1),this.tilePosition=new b.Point(0,0),this.renderable=!0,this.blendMode=b.blendModes.NORMAL},b.TilingSprite.prototype=Object.create(b.DisplayObjectContainer.prototype),b.TilingSprite.prototype.constructor=b.TilingSprite,b.TilingSprite.prototype.setTexture=function(a){this.texture=a,this.updateFrame=!0},b.TilingSprite.prototype.onTextureUpdate=function(){this.updateFrame=!0},b.AbstractFilter=function(a,b){this.passes=[this],this.dirty=!0,this.padding=0,this.uniforms=b||{},this.fragmentSrc=a||[]},b.FilterBlock=function(){this.visible=!0,this.renderable=!0},b.Graphics=function(){b.DisplayObjectContainer.call(this),this.renderable=!0,this.fillAlpha=1,this.lineWidth=0,this.lineColor="black",this.graphicsData=[],this.currentPath={points:[]}},b.Graphics.prototype=Object.create(b.DisplayObjectContainer.prototype),b.Graphics.prototype.constructor=b.Graphics,b.Graphics.prototype.lineStyle=function(a,c,d){this.currentPath.points.length||this.graphicsData.pop(),this.lineWidth=a||0,this.lineColor=c||0,this.lineAlpha=arguments.length<3?1:d,this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[],type:b.Graphics.POLY},this.graphicsData.push(this.currentPath)},b.Graphics.prototype.moveTo=function(a,c){this.currentPath.points.length||this.graphicsData.pop(),this.currentPath=this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[],type:b.Graphics.POLY},this.currentPath.points.push(a,c),this.graphicsData.push(this.currentPath)},b.Graphics.prototype.lineTo=function(a,b){this.currentPath.points.push(a,b),this.dirty=!0},b.Graphics.prototype.beginFill=function(a,b){this.filling=!0,this.fillColor=a||0,this.fillAlpha=arguments.length<2?1:b},b.Graphics.prototype.endFill=function(){this.filling=!1,this.fillColor=null,this.fillAlpha=1},b.Graphics.prototype.drawRect=function(a,c,d,e){this.currentPath.points.length||this.graphicsData.pop(),this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[a,c,d,e],type:b.Graphics.RECT},this.graphicsData.push(this.currentPath),this.dirty=!0},b.Graphics.prototype.drawCircle=function(a,c,d){this.currentPath.points.length||this.graphicsData.pop(),this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[a,c,d,d],type:b.Graphics.CIRC},this.graphicsData.push(this.currentPath),this.dirty=!0},b.Graphics.prototype.drawEllipse=function(a,c,d,e){this.currentPath.points.length||this.graphicsData.pop(),this.currentPath={lineWidth:this.lineWidth,lineColor:this.lineColor,lineAlpha:this.lineAlpha,fillColor:this.fillColor,fillAlpha:this.fillAlpha,fill:this.filling,points:[a,c,d,e],type:b.Graphics.ELIP},this.graphicsData.push(this.currentPath),this.dirty=!0},b.Graphics.prototype.clear=function(){this.lineWidth=0,this.filling=!1,this.dirty=!0,this.clearDirty=!0,this.graphicsData=[],this.bounds=null},b.Graphics.prototype.updateFilterBounds=function(){if(!this.bounds){for(var a,c,d,e=1/0,f=-1/0,g=1/0,h=-1/0,i=0;ic?c:e,f=c+m>f?c+m:f,g=g>d?c:g,h=d+n>h?d+n:h}else if(k===b.Graphics.CIRC||k===b.Graphics.ELIP){c=a.x,d=a.y;var o=a.radius+l/2;e=e>c-o?c-o:e,f=c+o>f?c+o:f,g=g>d-o?d-o:g,h=d+o>h?d+o:h}else for(var p=0;pc-l?c-l:e,f=c+l>f?c+l:f,g=g>d-l?d-l:g,h=d+l>h?d+l:h}this.bounds=new b.Rectangle(e,g,f-e,h-g)}},b.Graphics.POLY=0,b.Graphics.RECT=1,b.Graphics.CIRC=2,b.Graphics.ELIP=3,b.CanvasGraphics=function(){},b.CanvasGraphics.renderGraphics=function(a,c){for(var d=a.worldAlpha,e="",f=0;f1&&(d=1,window.console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object"));for(var e=0;1>e;e++){var f=a.graphicsData[e],g=f.points;if(f.type===b.Graphics.POLY){c.beginPath(),c.moveTo(g[0],g[1]);for(var h=1;h0&&(b.Texture.frameUpdates=[])},b.CanvasRenderer.prototype.resize=function(a,b){this.width=a,this.height=b,this.view.width=a,this.view.height=b},b.CanvasRenderer.prototype.renderDisplayObject=function(a){var c,d=this.context;d.globalCompositeOperation="source-over";var e=a.last._iNext;a=a.first;do if(c=a.worldTransform,a.visible)if(a.renderable){if(a instanceof b.Sprite){var f=a.texture.frame;f&&f.width&&f.height&&a.texture.baseTexture.source&&(d.globalAlpha=a.worldAlpha,d.setTransform(c[0],c[3],c[1],c[4],c[2],c[5]),this.smoothProperty&&this.scaleMode!==a.texture.baseTexture.scaleMode&&(this.scaleMode=a.texture.baseTexture.scaleMode,d[this.smoothProperty]=this.scaleMode===b.BaseTexture.SCALE_MODE.LINEAR),d.drawImage(a.texture.baseTexture.source,f.x,f.y,f.width,f.height,a.anchor.x*-f.width,a.anchor.y*-f.height,f.width,f.height))}else if(a instanceof b.Strip)d.setTransform(c[0],c[3],c[1],c[4],c[2],c[5]),this.renderStrip(a);else if(a instanceof b.TilingSprite)d.setTransform(c[0],c[3],c[1],c[4],c[2],c[5]),this.renderTilingSprite(a);else if(a instanceof b.CustomRenderable)d.setTransform(c[0],c[3],c[1],c[4],c[2],c[5]),a.renderCanvas(this);else if(a instanceof b.Graphics)d.setTransform(c[0],c[3],c[1],c[4],c[2],c[5]),b.CanvasGraphics.renderGraphics(a,d);else if(a instanceof b.FilterBlock&&a.data instanceof b.Graphics){var g=a.data;if(a.open){d.save();var h=g.alpha,i=g.worldTransform;d.setTransform(i[0],i[3],i[1],i[4],i[2],i[5]),g.worldAlpha=.5,d.worldAlpha=0,b.CanvasGraphics.renderGraphicsMask(g,d),d.clip(),g.worldAlpha=h}else d.restore()}a=a._iNext}else a=a._iNext;else a=a.last._iNext;while(a!==e)},b.CanvasRenderer.prototype.renderStripFlat=function(a){var b=this.context,c=a.verticies,d=c.length/2;this.count++,b.beginPath();for(var e=1;d-2>e;e++){var f=2*e,g=c[f],h=c[f+2],i=c[f+4],j=c[f+1],k=c[f+3],l=c[f+5];b.moveTo(g,j),b.lineTo(h,k),b.lineTo(i,l)}b.fillStyle="#FF0000",b.fill(),b.closePath()},b.CanvasRenderer.prototype.renderTilingSprite=function(a){var b=this.context;b.globalAlpha=a.worldAlpha,a.__tilePattern||(a.__tilePattern=b.createPattern(a.texture.baseTexture.source,"repeat")),b.beginPath();var c=a.tilePosition,d=a.tileScale;b.scale(d.x,d.y),b.translate(c.x,c.y),b.fillStyle=a.__tilePattern,b.fillRect(-c.x,-c.y,a.width/d.x,a.height/d.y),b.scale(1/d.x,1/d.y),b.translate(-c.x,-c.y),b.closePath()},b.CanvasRenderer.prototype.renderStrip=function(a){var b=this.context,c=a.verticies,d=a.uvs,e=c.length/2;this.count++;for(var f=1;e-2>f;f++){var g=2*f,h=c[g],i=c[g+2],j=c[g+4],k=c[g+1],l=c[g+3],m=c[g+5],n=d[g]*a.texture.width,o=d[g+2]*a.texture.width,p=d[g+4]*a.texture.width,q=d[g+1]*a.texture.height,r=d[g+3]*a.texture.height,s=d[g+5]*a.texture.height;b.save(),b.beginPath(),b.moveTo(h,k),b.lineTo(i,l),b.lineTo(j,m),b.closePath(),b.clip();var t=n*r+q*p+o*s-r*p-q*o-n*s,u=h*r+q*j+i*s-r*j-q*i-h*s,v=n*i+h*p+o*j-i*p-h*o-n*j,w=n*r*j+q*i*p+h*o*s-h*r*p-q*o*j-n*i*s,x=k*r+q*m+l*s-r*m-q*l-k*s,y=n*l+k*p+o*m-l*p-k*o-n*m,z=n*r*m+q*l*p+k*o*s-k*r*p-q*o*m-n*l*s;b.transform(u/t,x/t,v/t,y/t,w/t,z/t),b.drawImage(a.texture.baseTexture.source,0,0),b.restore()}},b.PixiShader=function(){this.program=null,this.fragmentSrc=["precision lowp float;","varying vec2 vTextureCoord;","varying float vColor;","uniform sampler2D uSampler;","void main(void) {"," gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor;","}"],this.textureCount=0},b.PixiShader.prototype.init=function(){var a=b.compileProgram(this.vertexSrc||b.PixiShader.defaultVertexSrc,this.fragmentSrc),c=b.gl;c.useProgram(a),this.uSampler=c.getUniformLocation(a,"uSampler"),this.projectionVector=c.getUniformLocation(a,"projectionVector"),this.offsetVector=c.getUniformLocation(a,"offsetVector"),this.dimensions=c.getUniformLocation(a,"dimensions"),this.aVertexPosition=c.getAttribLocation(a,"aVertexPosition"),this.colorAttribute=c.getAttribLocation(a,"aColor"),this.aTextureCoord=c.getAttribLocation(a,"aTextureCoord");for(var d in this.uniforms)this.uniforms[d].uniformLocation=c.getUniformLocation(a,d);this.initUniforms(),this.program=a},b.PixiShader.prototype.initUniforms=function(){this.textureCount=1;var a;for(var c in this.uniforms){a=this.uniforms[c];var d=a.type;"sampler2D"===d?(a._init=!1,null!==a.value&&this.initSampler2D(a)):"mat2"===d||"mat3"===d||"mat4"===d?(a.glMatrix=!0,a.glValueLength=1,"mat2"===d?a.glFunc=b.gl.uniformMatrix2fv:"mat3"===d?a.glFunc=b.gl.uniformMatrix3fv:"mat4"===d&&(a.glFunc=b.gl.uniformMatrix4fv)):(a.glFunc=b.gl["uniform"+d],a.glValueLength="2f"===d||"2i"===d?2:"3f"===d||"3i"===d?3:"4f"===d||"4i"===d?4:1)}},b.PixiShader.prototype.initSampler2D=function(a){if(a.value&&a.value.baseTexture&&a.value.baseTexture.hasLoaded){if(b.gl.activeTexture(b.gl["TEXTURE"+this.textureCount]),b.gl.bindTexture(b.gl.TEXTURE_2D,a.value.baseTexture._glTexture),a.textureData){var c=a.textureData,d=c.magFilter?c.magFilter:b.gl.LINEAR,e=c.minFilter?c.minFilter:b.gl.LINEAR,f=c.wrapS?c.wrapS:b.gl.CLAMP_TO_EDGE,g=c.wrapT?c.wrapT:b.gl.CLAMP_TO_EDGE,h=c.luminance?b.gl.LUMINANCE:b.gl.RGBA;if(c.repeat&&(f=b.gl.REPEAT,g=b.gl.REPEAT),b.gl.pixelStorei(b.gl.UNPACK_FLIP_Y_WEBGL,!1),c.width){var i=c.width?c.width:512,j=c.height?c.height:2,k=c.border?c.border:0;b.gl.texImage2D(b.gl.TEXTURE_2D,0,h,i,j,k,h,b.gl.UNSIGNED_BYTE,null)}else b.gl.texImage2D(b.gl.TEXTURE_2D,0,h,b.gl.RGBA,b.gl.UNSIGNED_BYTE,a.value.baseTexture.source);b.gl.texParameteri(b.gl.TEXTURE_2D,b.gl.TEXTURE_MAG_FILTER,d),b.gl.texParameteri(b.gl.TEXTURE_2D,b.gl.TEXTURE_MIN_FILTER,e),b.gl.texParameteri(b.gl.TEXTURE_2D,b.gl.TEXTURE_WRAP_S,f),b.gl.texParameteri(b.gl.TEXTURE_2D,b.gl.TEXTURE_WRAP_T,g)}b.gl.uniform1i(a.uniformLocation,this.textureCount),a._init=!0,this.textureCount++}},b.PixiShader.prototype.syncUniforms=function(){this.textureCount=1;var a;for(var c in this.uniforms)a=this.uniforms[c],1===a.glValueLength?a.glMatrix===!0?a.glFunc.call(b.gl,a.uniformLocation,a.transpose,a.value):a.glFunc.call(b.gl,a.uniformLocation,a.value):2===a.glValueLength?a.glFunc.call(b.gl,a.uniformLocation,a.value.x,a.value.y):3===a.glValueLength?a.glFunc.call(b.gl,a.uniformLocation,a.value.x,a.value.y,a.value.z):4===a.glValueLength?a.glFunc.call(b.gl,a.uniformLocation,a.value.x,a.value.y,a.value.z,a.value.w):"sampler2D"===a.type&&(a._init?(b.gl.activeTexture(b.gl["TEXTURE"+this.textureCount]),b.gl.bindTexture(b.gl.TEXTURE_2D,a.value.baseTexture._glTexture),b.gl.uniform1i(a.uniformLocation,this.textureCount),this.textureCount++):this.initSampler2D(a))},b.PixiShader.defaultVertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","varying vec2 vTextureCoord;","varying float vColor;","const vec2 center = vec2(-1.0, 1.0);","void main(void) {"," gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);"," vTextureCoord = aTextureCoord;"," vColor = aColor;","}"],b.PrimitiveShader=function(){this.program=null,this.fragmentSrc=["precision mediump float;","varying vec4 vColor;","void main(void) {"," gl_FragColor = vColor;","}"],this.vertexSrc=["attribute vec2 aVertexPosition;","attribute vec4 aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","uniform float alpha;","varying vec4 vColor;","void main(void) {"," vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);"," v -= offsetVector.xyx;"," gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);"," vColor = aColor * alpha;","}"]
-},b.PrimitiveShader.prototype.init=function(){var a=b.compileProgram(this.vertexSrc,this.fragmentSrc),c=b.gl;c.useProgram(a),this.projectionVector=c.getUniformLocation(a,"projectionVector"),this.offsetVector=c.getUniformLocation(a,"offsetVector"),this.aVertexPosition=c.getAttribLocation(a,"aVertexPosition"),this.colorAttribute=c.getAttribLocation(a,"aColor"),this.translationMatrix=c.getUniformLocation(a,"translationMatrix"),this.alpha=c.getUniformLocation(a,"alpha"),this.program=a},b.StripShader=function(){this.program=null,this.fragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","varying float vColor;","uniform float alpha;","uniform sampler2D uSampler;","void main(void) {"," gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y));"," gl_FragColor = gl_FragColor * alpha;","}"],this.vertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","varying vec2 vTextureCoord;","varying float vColor;","void main(void) {"," vec3 v = translationMatrix * vec3(aVertexPosition, 1.0);"," v -= offsetVector.xyx;"," gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / projectionVector.y + 1.0 , 0.0, 1.0);"," vTextureCoord = aTextureCoord;"," vColor = aColor;","}"]},b.StripShader.prototype.init=function(){var a=b.compileProgram(this.vertexSrc,this.fragmentSrc),c=b.gl;c.useProgram(a),this.uSampler=c.getUniformLocation(a,"uSampler"),this.projectionVector=c.getUniformLocation(a,"projectionVector"),this.offsetVector=c.getUniformLocation(a,"offsetVector"),this.colorAttribute=c.getAttribLocation(a,"aColor"),this.aVertexPosition=c.getAttribLocation(a,"aVertexPosition"),this.aTextureCoord=c.getAttribLocation(a,"aTextureCoord"),this.translationMatrix=c.getUniformLocation(a,"translationMatrix"),this.alpha=c.getUniformLocation(a,"alpha"),this.program=a},b._batchs=[],b._getBatch=function(a){return 0===b._batchs.length?new b.WebGLBatch(a):b._batchs.pop()},b._returnBatch=function(a){a.clean(),b._batchs.push(a)},b._restoreBatchs=function(a){for(var c=0;cc;c++){var d=6*c,e=4*c;this.indices[d+0]=e+0,this.indices[d+1]=e+1,this.indices[d+2]=e+2,this.indices[d+3]=e+0,this.indices[d+4]=e+2,this.indices[d+5]=e+3}a.bindBuffer(a.ELEMENT_ARRAY_BUFFER,this.indexBuffer),a.bufferData(a.ELEMENT_ARRAY_BUFFER,this.indices,a.STATIC_DRAW)},b.WebGLBatch.prototype.refresh=function(){this.dynamicSizethis.width&&(f.width=this.width),f.y<0&&(f.y=0),f.height>this.height&&(f.height=this.height),c.bindFramebuffer(c.FRAMEBUFFER,e.frameBuffer),c.viewport(0,0,f.width,f.height),b.projection.x=f.width/2,b.projection.y=-f.height/2,b.offset.x=-f.x,b.offset.y=-f.y,c.uniform2f(b.defaultShader.projectionVector,f.width/2,-f.height/2),c.uniform2f(b.defaultShader.offsetVector,-f.x,-f.y),c.colorMask(!0,!0,!0,!0),c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT),a._glFilterTexture=e},b.WebGLFilterManager.prototype.popFilter=function(){var a=b.gl,c=this.filterStack.pop(),d=c.target.filterArea,e=c._glFilterTexture;if(c.filterPasses.length>1){a.viewport(0,0,d.width,d.height),a.bindBuffer(a.ARRAY_BUFFER,this.vertexBuffer),this.vertexArray[0]=0,this.vertexArray[1]=d.height,this.vertexArray[2]=d.width,this.vertexArray[3]=d.height,this.vertexArray[4]=0,this.vertexArray[5]=0,this.vertexArray[6]=d.width,this.vertexArray[7]=0,a.bufferSubData(a.ARRAY_BUFFER,0,this.vertexArray),a.bindBuffer(a.ARRAY_BUFFER,this.uvBuffer),this.uvArray[2]=d.width/this.width,this.uvArray[5]=d.height/this.height,this.uvArray[6]=d.width/this.width,this.uvArray[7]=d.height/this.height,a.bufferSubData(a.ARRAY_BUFFER,0,this.uvArray);var f=e,g=this.texturePool.pop();g||(g=new b.FilterTexture(this.width,this.height)),a.bindFramebuffer(a.FRAMEBUFFER,g.frameBuffer),a.clear(a.COLOR_BUFFER_BIT),a.disable(a.BLEND);for(var h=0;hs?s:E,E=E>t?t:E,E=E>u?u:E,E=E>v?v:E,F=F>w?w:F,F=F>x?x:F,F=F>y?y:F,F=F>z?z:F,C=s>C?s:C,C=t>C?t:C,C=u>C?u:C,C=v>C?v:C,D=w>D?w:D,D=x>D?x:D,D=y>D?y:D,D=z>D?z:D),l=!1,A=A._iNext}while(A!==B);a.filterArea.x=E,a.filterArea.y=F,a.filterArea.width=C-E,a.filterArea.height=D-F},b.FilterTexture=function(a,c){var d=b.gl;this.frameBuffer=d.createFramebuffer(),this.texture=d.createTexture(),d.bindTexture(d.TEXTURE_2D,this.texture),d.texParameteri(d.TEXTURE_2D,d.TEXTURE_MAG_FILTER,d.LINEAR),d.texParameteri(d.TEXTURE_2D,d.TEXTURE_MIN_FILTER,d.LINEAR),d.texParameteri(d.TEXTURE_2D,d.TEXTURE_WRAP_S,d.CLAMP_TO_EDGE),d.texParameteri(d.TEXTURE_2D,d.TEXTURE_WRAP_T,d.CLAMP_TO_EDGE),d.bindFramebuffer(d.FRAMEBUFFER,this.framebuffer),d.bindFramebuffer(d.FRAMEBUFFER,this.frameBuffer),d.framebufferTexture2D(d.FRAMEBUFFER,d.COLOR_ATTACHMENT0,d.TEXTURE_2D,this.texture,0),this.resize(a,c)},b.FilterTexture.prototype.resize=function(a,c){if(this.width!==a||this.height!==c){this.width=a,this.height=c;var d=b.gl;d.bindTexture(d.TEXTURE_2D,this.texture),d.texImage2D(d.TEXTURE_2D,0,d.RGBA,a,c,0,d.RGBA,d.UNSIGNED_BYTE,null)}},b.WebGLGraphics=function(){},b.WebGLGraphics.renderGraphics=function(a,c){var d=b.gl;a._webGL||(a._webGL={points:[],indices:[],lastIndex:0,buffer:d.createBuffer(),indexBuffer:d.createBuffer()}),a.dirty&&(a.dirty=!1,a.clearDirty&&(a.clearDirty=!1,a._webGL.lastIndex=0,a._webGL.points=[],a._webGL.indices=[]),b.WebGLGraphics.updateGraphics(a)),b.activatePrimitiveShader();var e=b.mat3.clone(a.worldTransform);b.mat3.transpose(e),d.blendFunc(d.ONE,d.ONE_MINUS_SRC_ALPHA),d.uniformMatrix3fv(b.primitiveShader.translationMatrix,!1,e),d.uniform2f(b.primitiveShader.projectionVector,c.x,-c.y),d.uniform2f(b.primitiveShader.offsetVector,-b.offset.x,-b.offset.y),d.uniform1f(b.primitiveShader.alpha,a.worldAlpha),d.bindBuffer(d.ARRAY_BUFFER,a._webGL.buffer),d.vertexAttribPointer(b.primitiveShader.aVertexPosition,2,d.FLOAT,!1,24,0),d.vertexAttribPointer(b.primitiveShader.colorAttribute,4,d.FLOAT,!1,24,8),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,a._webGL.indexBuffer),d.drawElements(d.TRIANGLE_STRIP,a._webGL.indices.length,d.UNSIGNED_SHORT,0),b.deactivatePrimitiveShader()},b.WebGLGraphics.updateGraphics=function(a){for(var c=a._webGL.lastIndex;c3&&b.WebGLGraphics.buildPoly(d,a._webGL),d.lineWidth>0&&b.WebGLGraphics.buildLine(d,a._webGL)):d.type===b.Graphics.RECT?b.WebGLGraphics.buildRectangle(d,a._webGL):(d.type===b.Graphics.CIRC||d.type===b.Graphics.ELIP)&&b.WebGLGraphics.buildCircle(d,a._webGL)}a._webGL.lastIndex=a.graphicsData.length;var e=b.gl;a._webGL.glPoints=new Float32Array(a._webGL.points),e.bindBuffer(e.ARRAY_BUFFER,a._webGL.buffer),e.bufferData(e.ARRAY_BUFFER,a._webGL.glPoints,e.STATIC_DRAW),a._webGL.glIndicies=new Uint16Array(a._webGL.indices),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,a._webGL.indexBuffer),e.bufferData(e.ELEMENT_ARRAY_BUFFER,a._webGL.glIndicies,e.STATIC_DRAW)},b.WebGLGraphics.buildRectangle=function(a,c){var d=a.points,e=d[0],f=d[1],g=d[2],h=d[3];if(a.fill){var i=b.hex2rgb(a.fillColor),j=a.fillAlpha,k=i[0]*j,l=i[1]*j,m=i[2]*j,n=c.points,o=c.indices,p=n.length/6;n.push(e,f),n.push(k,l,m,j),n.push(e+g,f),n.push(k,l,m,j),n.push(e,f+h),n.push(k,l,m,j),n.push(e+g,f+h),n.push(k,l,m,j),o.push(p,p,p+1,p+2,p+3,p+3)}a.lineWidth&&(a.points=[e,f,e+g,f,e+g,f+h,e,f+h,e,f],b.WebGLGraphics.buildLine(a,c))},b.WebGLGraphics.buildCircle=function(a,c){var d=a.points,e=d[0],f=d[1],g=d[2],h=d[3],i=40,j=2*Math.PI/i,k=0;if(a.fill){var l=b.hex2rgb(a.fillColor),m=a.fillAlpha,n=l[0]*m,o=l[1]*m,p=l[2]*m,q=c.points,r=c.indices,s=q.length/6;for(r.push(s),k=0;i+1>k;k++)q.push(e,f,n,o,p,m),q.push(e+Math.sin(j*k)*g,f+Math.cos(j*k)*h,n,o,p,m),r.push(s++,s++);r.push(s-1)}if(a.lineWidth){for(a.points=[],k=0;i+1>k;k++)a.points.push(e+Math.sin(j*k)*g,f+Math.cos(j*k)*h);b.WebGLGraphics.buildLine(a,c)}},b.WebGLGraphics.buildLine=function(a,c){var d=0,e=a.points;if(0!==e.length){if(a.lineWidth%2)for(d=0;dd;d++)l=e[2*(d-1)],m=e[2*(d-1)+1],n=e[2*d],o=e[2*d+1],p=e[2*(d+1)],q=e[2*(d+1)+1],r=-(m-o),s=l-n,F=Math.sqrt(r*r+s*s),r/=F,s/=F,r*=L,s*=L,t=-(o-q),u=n-p,F=Math.sqrt(t*t+u*u),t/=F,u/=F,t*=L,u*=L,x=-s+m-(-s+o),y=-r+n-(-r+l),z=(-r+l)*(-s+o)-(-r+n)*(-s+m),A=-u+q-(-u+o),B=-t+n-(-t+p),C=(-t+p)*(-u+o)-(-t+n)*(-u+q),D=x*B-A*y,Math.abs(D)<.1?(D+=10.1,G.push(n-r,o-s,O,P,Q,N),G.push(n+r,o+s,O,P,Q,N)):(j=(y*C-B*z)/D,k=(A*z-x*C)/D,E=(j-n)*(j-n)+(k-o)+(k-o),E>19600?(v=r-t,w=s-u,F=Math.sqrt(v*v+w*w),v/=F,w/=F,v*=L,w*=L,G.push(n-v,o-w),G.push(O,P,Q,N),G.push(n+v,o+w),G.push(O,P,Q,N),G.push(n-v,o-w),G.push(O,P,Q,N),J++):(G.push(j,k),G.push(O,P,Q,N),G.push(n-(j-n),o-(k-o)),G.push(O,P,Q,N)));for(l=e[2*(I-2)],m=e[2*(I-2)+1],n=e[2*(I-1)],o=e[2*(I-1)+1],r=-(m-o),s=l-n,F=Math.sqrt(r*r+s*s),r/=F,s/=F,r*=L,s*=L,G.push(n-r,o-s),G.push(O,P,Q,N),G.push(n+r,o+s),G.push(O,P,Q,N),H.push(K),d=0;J>d;d++)H.push(K++);H.push(K-1)}},b.WebGLGraphics.buildPoly=function(a,c){var d=a.points;if(!(d.length<6)){var e=c.points,f=c.indices,g=d.length/2,h=b.hex2rgb(a.fillColor),i=a.fillAlpha,j=h[0]*i,k=h[1]*i,l=h[2]*i,m=b.PolyK.Triangulate(d),n=e.length/6,o=0;for(o=0;oo;o++)e.push(d[2*o],d[2*o+1],j,k,l,i)}},b._defaultFrame=new b.Rectangle(0,0,1,1),b.gl=null,b.WebGLRenderer=function(a,c,d,e,f){this.transparent=!!e,this.width=a||800,this.height=c||600,this.view=d||document.createElement("canvas"),this.view.width=this.width,this.view.height=this.height;var g=this;this.view.addEventListener("webglcontextlost",function(a){g.handleContextLost(a)},!1),this.view.addEventListener("webglcontextrestored",function(a){g.handleContextRestored(a)},!1),this.batchs=[];var h={alpha:this.transparent,antialias:!!f,premultipliedAlpha:!1,stencil:!0};try{b.gl=this.gl=this.view.getContext("experimental-webgl",h)}catch(i){try{b.gl=this.gl=this.view.getContext("webgl",h)}catch(j){throw new Error(" This browser does not support webGL. Try using the canvas renderer"+this)}}b.initDefaultShaders();var k=this.gl;k.useProgram(b.defaultShader.program),b.WebGLRenderer.gl=k,this.batch=new b.WebGLBatch(k),k.disable(k.DEPTH_TEST),k.disable(k.CULL_FACE),k.enable(k.BLEND),k.colorMask(!0,!0,!0,this.transparent),b.projection=new b.Point(400,300),b.offset=new b.Point(0,0),this.resize(this.width,this.height),this.contextLost=!1,this.stageRenderGroup=new b.WebGLRenderGroup(this.gl,this.transparent)},b.WebGLRenderer.prototype.constructor=b.WebGLRenderer,b.WebGLRenderer.getBatch=function(){return 0===b._batchs.length?new b.WebGLBatch(b.WebGLRenderer.gl):b._batchs.pop()},b.WebGLRenderer.returnBatch=function(a){a.clean(),b._batchs.push(a)},b.WebGLRenderer.prototype.render=function(a){if(!this.contextLost){this.__stage!==a&&(this.__stage=a,this.stageRenderGroup.setRenderable(a)),b.WebGLRenderer.updateTextures(),b.visibleCount++,a.updateTransform();var c=this.gl;if(c.colorMask(!0,!0,!0,this.transparent),c.viewport(0,0,this.width,this.height),c.bindFramebuffer(c.FRAMEBUFFER,null),c.clearColor(a.backgroundColorSplit[0],a.backgroundColorSplit[1],a.backgroundColorSplit[2],!this.transparent),c.clear(c.COLOR_BUFFER_BIT),this.stageRenderGroup.backgroundColor=a.backgroundColorSplit,b.projection.x=this.width/2,b.projection.y=-this.height/2,this.stageRenderGroup.render(b.projection),a.interactive&&(a._interactiveEventsAdded||(a._interactiveEventsAdded=!0,a.interactionManager.setTarget(this))),b.Texture.frameUpdates.length>0){for(var d=0;dn;n++)renderable=this.batchs[n],renderable instanceof b.WebGLBatch?this.batchs[n].render():this.renderSpecial(renderable,c);endBatch instanceof b.WebGLBatch?endBatch.render(0,h+1):this.renderSpecial(endBatch,c)},b.WebGLRenderGroup.prototype.renderSpecial=function(a,c){var d=a.vcount===b.visibleCount;a instanceof b.TilingSprite?d&&this.renderTilingSprite(a,c):a instanceof b.Strip?d&&this.renderStrip(a,c):a instanceof b.CustomRenderable?d&&a.renderWebGL(this,c):a instanceof b.Graphics?d&&a.renderable&&b.WebGLGraphics.renderGraphics(a,c):a instanceof b.FilterBlock&&this.handleFilterBlock(a,c)},flip=!1;var d=[],e=0;return b.WebGLRenderGroup.prototype.handleFilterBlock=function(a,c){var f=b.gl;if(a.open)a.data instanceof Array?this.filterManager.pushFilter(a):(e++,d.push(a),f.enable(f.STENCIL_TEST),f.colorMask(!1,!1,!1,!1),f.stencilFunc(f.ALWAYS,1,1),f.stencilOp(f.KEEP,f.KEEP,f.INCR),b.WebGLGraphics.renderGraphics(a.data,c),f.colorMask(!0,!0,!0,!0),f.stencilFunc(f.NOTEQUAL,0,d.length),f.stencilOp(f.KEEP,f.KEEP,f.KEEP));else if(a.data instanceof Array)this.filterManager.popFilter();else{var g=d.pop(a);g&&(f.colorMask(!1,!1,!1,!1),f.stencilFunc(f.ALWAYS,1,1),f.stencilOp(f.KEEP,f.KEEP,f.DECR),b.WebGLGraphics.renderGraphics(g.data,c),f.colorMask(!0,!0,!0,!0),f.stencilFunc(f.NOTEQUAL,0,d.length),f.stencilOp(f.KEEP,f.KEEP,f.KEEP)),f.disable(f.STENCIL_TEST)}},b.WebGLRenderGroup.prototype.updateTexture=function(a){this.removeObject(a);for(var b=a.first;b!=this.root&&(b=b._iPrev,!b.renderable||!b.__renderGroup););for(var c=a.last;c._iNext&&(c=c._iNext,!c.renderable||!c.__renderGroup););this.insertObject(a,b,c)},b.WebGLRenderGroup.prototype.addFilterBlocks=function(a,b){a.__renderGroup=this,b.__renderGroup=this;for(var c=a;c!=this.root.first&&(c=c._iPrev,!c.renderable||!c.__renderGroup););this.insertAfter(a,c);for(var d=b;d!=this.root.first&&(d=d._iPrev,!d.renderable||!d.__renderGroup););this.insertAfter(b,d)},b.WebGLRenderGroup.prototype.removeFilterBlocks=function(a,b){this.removeObject(a),this.removeObject(b)},b.WebGLRenderGroup.prototype.addDisplayObjectAndChildren=function(a){a.__renderGroup&&a.__renderGroup.removeDisplayObjectAndChildren(a);for(var b=a.first;b!=this.root.first&&(b=b._iPrev,!b.renderable||!b.__renderGroup););for(var c=a.last;c._iNext&&(c=c._iNext,!c.renderable||!c.__renderGroup););var d=a.first,e=a.last._iNext;do d.__renderGroup=this,d.renderable&&(this.insertObject(d,b,c),b=d),d=d._iNext;while(d!=e)},b.WebGLRenderGroup.prototype.removeDisplayObjectAndChildren=function(a){if(a.__renderGroup==this){a.last;do a.__renderGroup=null,a.renderable&&this.removeObject(a),a=a._iNext;while(a)}},b.WebGLRenderGroup.prototype.insertObject=function(a,c,d){var e=c,f=d;if(a instanceof b.Sprite){var g,h;if(e instanceof b.Sprite){if(g=e.batch,g&&g.texture==a.texture.baseTexture&&g.blendMode==a.blendMode)return g.insertAfter(a,e),void 0}else g=e;if(f)if(f instanceof b.Sprite){if(h=f.batch){if(h.texture==a.texture.baseTexture&&h.blendMode==a.blendMode)return h.insertBefore(a,f),void 0;if(h==g){var i=g.split(f),j=b.WebGLRenderer.getBatch(),k=this.batchs.indexOf(g);return j.init(a),this.batchs.splice(k+1,0,j,i),void 0}}}else h=f;var j=b.WebGLRenderer.getBatch();if(j.init(a),g){var k=this.batchs.indexOf(g);this.batchs.splice(k+1,0,j)}else this.batchs.push(j)}else a instanceof b.TilingSprite?this.initTilingSprite(a):a instanceof b.Strip&&this.initStrip(a),this.insertAfter(a,e)},b.WebGLRenderGroup.prototype.insertAfter=function(a,c){if(c instanceof b.Sprite){var d=c.batch;if(d)if(d.tail==c){var e=this.batchs.indexOf(d);this.batchs.splice(e+1,0,a)}else{var f=d.split(c.__next),e=this.batchs.indexOf(d);this.batchs.splice(e+1,0,a,f)}else this.batchs.push(a)}else{var e=this.batchs.indexOf(c);this.batchs.splice(e+1,0,a)}},b.WebGLRenderGroup.prototype.removeObject=function(a){var c;if(a instanceof b.Sprite){var d=a.batch;if(!d)return;d.remove(a),0==d.size&&(c=d)}else c=a;if(c){var e=this.batchs.indexOf(c);if(-1==e)return;if(0==e||e==this.batchs.length-1)return this.batchs.splice(e,1),c instanceof b.WebGLBatch&&b.WebGLRenderer.returnBatch(c),void 0;if(this.batchs[e-1]instanceof b.WebGLBatch&&this.batchs[e+1]instanceof b.WebGLBatch&&this.batchs[e-1].texture==this.batchs[e+1].texture&&this.batchs[e-1].blendMode==this.batchs[e+1].blendMode)return this.batchs[e-1].merge(this.batchs[e+1]),c instanceof b.WebGLBatch&&b.WebGLRenderer.returnBatch(c),b.WebGLRenderer.returnBatch(this.batchs[e+1]),this.batchs.splice(e,2),void 0;this.batchs.splice(e,1),c instanceof b.WebGLBatch&&b.WebGLRenderer.returnBatch(c)}},b.WebGLRenderGroup.prototype.initTilingSprite=function(a){var b=this.gl;a.verticies=new Float32Array([0,0,a.width,0,a.width,a.height,0,a.height]),a.uvs=new Float32Array([0,0,1,0,1,1,0,1]),a.colors=new Float32Array([1,1,1,1]),a.indices=new Uint16Array([0,1,3,2]),a._vertexBuffer=b.createBuffer(),a._indexBuffer=b.createBuffer(),a._uvBuffer=b.createBuffer(),a._colorBuffer=b.createBuffer(),b.bindBuffer(b.ARRAY_BUFFER,a._vertexBuffer),b.bufferData(b.ARRAY_BUFFER,a.verticies,b.STATIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._uvBuffer),b.bufferData(b.ARRAY_BUFFER,a.uvs,b.DYNAMIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._colorBuffer),b.bufferData(b.ARRAY_BUFFER,a.colors,b.STATIC_DRAW),b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,a._indexBuffer),b.bufferData(b.ELEMENT_ARRAY_BUFFER,a.indices,b.STATIC_DRAW),a.texture.baseTexture._glTexture?(b.bindTexture(b.TEXTURE_2D,a.texture.baseTexture._glTexture),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.REPEAT),b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.REPEAT),a.texture.baseTexture._powerOf2=!0):a.texture.baseTexture._powerOf2=!0},b.WebGLRenderGroup.prototype.renderStrip=function(a,c){var d=this.gl;b.activateStripShader();var e=b.stripShader;e.program;var f=b.mat3.clone(a.worldTransform);b.mat3.transpose(f),d.uniformMatrix3fv(e.translationMatrix,!1,f),d.uniform2f(e.projectionVector,c.x,c.y),d.uniform2f(e.offsetVector,-b.offset.x,-b.offset.y),d.uniform1f(e.alpha,a.worldAlpha),a.dirty?(a.dirty=!1,d.bindBuffer(d.ARRAY_BUFFER,a._vertexBuffer),d.bufferData(d.ARRAY_BUFFER,a.verticies,d.STATIC_DRAW),d.vertexAttribPointer(e.aVertexPosition,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,a._uvBuffer),d.bufferData(d.ARRAY_BUFFER,a.uvs,d.STATIC_DRAW),d.vertexAttribPointer(e.aTextureCoord,2,d.FLOAT,!1,0,0),d.activeTexture(d.TEXTURE0),d.bindTexture(d.TEXTURE_2D,a.texture.baseTexture._glTexture),d.bindBuffer(d.ARRAY_BUFFER,a._colorBuffer),d.bufferData(d.ARRAY_BUFFER,a.colors,d.STATIC_DRAW),d.vertexAttribPointer(e.colorAttribute,1,d.FLOAT,!1,0,0),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,a._indexBuffer),d.bufferData(d.ELEMENT_ARRAY_BUFFER,a.indices,d.STATIC_DRAW)):(d.bindBuffer(d.ARRAY_BUFFER,a._vertexBuffer),d.bufferSubData(d.ARRAY_BUFFER,0,a.verticies),d.vertexAttribPointer(e.aVertexPosition,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,a._uvBuffer),d.vertexAttribPointer(e.aTextureCoord,2,d.FLOAT,!1,0,0),d.activeTexture(d.TEXTURE0),d.bindTexture(d.TEXTURE_2D,a.texture.baseTexture._glTexture),d.bindBuffer(d.ARRAY_BUFFER,a._colorBuffer),d.vertexAttribPointer(e.colorAttribute,1,d.FLOAT,!1,0,0),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,a._indexBuffer)),d.drawElements(d.TRIANGLE_STRIP,a.indices.length,d.UNSIGNED_SHORT,0),b.deactivateStripShader()
-},b.WebGLRenderGroup.prototype.renderTilingSprite=function(a,c){var d=this.gl;b.shaderProgram;var e=a.tilePosition,f=a.tileScale,g=e.x/a.texture.baseTexture.width,h=e.y/a.texture.baseTexture.height,i=a.width/a.texture.baseTexture.width/f.x,j=a.height/a.texture.baseTexture.height/f.y;a.uvs[0]=0-g,a.uvs[1]=0-h,a.uvs[2]=1*i-g,a.uvs[3]=0-h,a.uvs[4]=1*i-g,a.uvs[5]=1*j-h,a.uvs[6]=0-g,a.uvs[7]=1*j-h,d.bindBuffer(d.ARRAY_BUFFER,a._uvBuffer),d.bufferSubData(d.ARRAY_BUFFER,0,a.uvs),this.renderStrip(a,c)},b.WebGLRenderGroup.prototype.initStrip=function(a){var b=this.gl;this.shaderProgram,a._vertexBuffer=b.createBuffer(),a._indexBuffer=b.createBuffer(),a._uvBuffer=b.createBuffer(),a._colorBuffer=b.createBuffer(),b.bindBuffer(b.ARRAY_BUFFER,a._vertexBuffer),b.bufferData(b.ARRAY_BUFFER,a.verticies,b.DYNAMIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._uvBuffer),b.bufferData(b.ARRAY_BUFFER,a.uvs,b.STATIC_DRAW),b.bindBuffer(b.ARRAY_BUFFER,a._colorBuffer),b.bufferData(b.ARRAY_BUFFER,a.colors,b.STATIC_DRAW),b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,a._indexBuffer),b.bufferData(b.ELEMENT_ARRAY_BUFFER,a.indices,b.STATIC_DRAW)},b.initDefaultShaders=function(){b.primitiveShader=new b.PrimitiveShader,b.primitiveShader.init(),b.stripShader=new b.StripShader,b.stripShader.init(),b.defaultShader=new b.PixiShader,b.defaultShader.init();var a=b.gl,c=b.defaultShader.program;a.useProgram(c),a.enableVertexAttribArray(b.defaultShader.aVertexPosition),a.enableVertexAttribArray(b.defaultShader.colorAttribute),a.enableVertexAttribArray(b.defaultShader.aTextureCoord)},b.activatePrimitiveShader=function(){var a=b.gl;a.useProgram(b.primitiveShader.program),a.disableVertexAttribArray(b.defaultShader.aVertexPosition),a.disableVertexAttribArray(b.defaultShader.colorAttribute),a.disableVertexAttribArray(b.defaultShader.aTextureCoord),a.enableVertexAttribArray(b.primitiveShader.aVertexPosition),a.enableVertexAttribArray(b.primitiveShader.colorAttribute)},b.deactivatePrimitiveShader=function(){var a=b.gl;a.useProgram(b.defaultShader.program),a.disableVertexAttribArray(b.primitiveShader.aVertexPosition),a.disableVertexAttribArray(b.primitiveShader.colorAttribute),a.enableVertexAttribArray(b.defaultShader.aVertexPosition),a.enableVertexAttribArray(b.defaultShader.colorAttribute),a.enableVertexAttribArray(b.defaultShader.aTextureCoord)},b.activateStripShader=function(){var a=b.gl;a.useProgram(b.stripShader.program)},b.deactivateStripShader=function(){var a=b.gl;a.useProgram(b.defaultShader.program)},b.CompileVertexShader=function(a,c){return b._CompileShader(a,c,a.VERTEX_SHADER)},b.CompileFragmentShader=function(a,c){return b._CompileShader(a,c,a.FRAGMENT_SHADER)},b._CompileShader=function(a,b,c){var d=b.join("\n"),e=a.createShader(c);return a.shaderSource(e,d),a.compileShader(e),a.getShaderParameter(e,a.COMPILE_STATUS)?e:(window.console.log(a.getShaderInfoLog(e)),null)},b.compileProgram=function(a,c){var d=b.gl,e=b.CompileFragmentShader(d,c),f=b.CompileVertexShader(d,a),g=d.createProgram();return d.attachShader(g,f),d.attachShader(g,e),d.linkProgram(g),d.getProgramParameter(g,d.LINK_STATUS)||window.console.log("Could not initialise shaders"),g},b.BitmapText=function(a,c){b.DisplayObjectContainer.call(this),this.setText(a),this.setStyle(c),this.updateText(),this.dirty=!1},b.BitmapText.prototype=Object.create(b.DisplayObjectContainer.prototype),b.BitmapText.prototype.constructor=b.BitmapText,b.BitmapText.prototype.setText=function(a){this.text=a||" ",this.dirty=!0},b.BitmapText.prototype.setStyle=function(a){a=a||{},a.align=a.align||"left",this.style=a;var c=a.font.split(" ");this.fontName=c[c.length-1],this.fontSize=c.length>=2?parseInt(c[c.length-2],10):b.BitmapText.fonts[this.fontName].size,this.dirty=!0},b.BitmapText.prototype.updateText=function(){for(var a=b.BitmapText.fonts[this.fontName],c=new b.Point,d=null,e=[],f=0,g=[],h=0,i=this.fontSize/a.size,j=0;j=j;j++){var n=0;"right"===this.style.align?n=f-g[j]:"center"===this.style.align&&(n=(f-g[j])/2),m.push(n)}for(j=0;j0;)this.removeChild(this.getChildAt(0));this.updateText(),this.dirty=!1}b.DisplayObjectContainer.prototype.updateTransform.call(this)},b.BitmapText.fonts={},b.Text=function(a,c){this.canvas=document.createElement("canvas"),this.context=this.canvas.getContext("2d"),b.Sprite.call(this,b.Texture.fromCanvas(this.canvas)),this.setText(a),this.setStyle(c),this.updateText(),this.dirty=!1},b.Text.prototype=Object.create(b.Sprite.prototype),b.Text.prototype.constructor=b.Text,b.Text.prototype.setStyle=function(a){a=a||{},a.font=a.font||"bold 20pt Arial",a.fill=a.fill||"black",a.align=a.align||"left",a.stroke=a.stroke||"black",a.strokeThickness=a.strokeThickness||0,a.wordWrap=a.wordWrap||!1,a.wordWrapWidth=a.wordWrapWidth||100,this.style=a,this.dirty=!0},b.Text.prototype.setText=function(a){this.text=a.toString()||" ",this.dirty=!0},b.Text.prototype.updateText=function(){this.context.font=this.style.font;var a=this.text;this.style.wordWrap&&(a=this.wordWrap(this.text));for(var c=a.split(/(?:\r\n|\r|\n)/),d=[],e=0,f=0;fe?(g>0&&(b+="\n"),b+=f[g]+" ",e=this.style.wordWrapWidth-h):(e-=i,b+=f[g]+" ")}b+="\n"}return b},b.Text.prototype.destroy=function(a){a&&this.texture.destroy()},b.Text.heightCache={},b.BaseTextureCache={},b.texturesToUpdate=[],b.texturesToDestroy=[],b.BaseTexture=function(a,c){if(b.EventTarget.call(this),this.width=100,this.height=100,this.scaleMode=c||b.BaseTexture.SCALE_MODE.DEFAULT,this.hasLoaded=!1,this.source=a,a){if(this.source instanceof Image||this.source instanceof HTMLImageElement)if(this.source.complete)this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,b.texturesToUpdate.push(this);else{var d=this;this.source.onload=function(){d.hasLoaded=!0,d.width=d.source.width,d.height=d.source.height,b.texturesToUpdate.push(d),d.dispatchEvent({type:"loaded",content:d})}}else this.hasLoaded=!0,this.width=this.source.width,this.height=this.source.height,b.texturesToUpdate.push(this);this.imageUrl=null,this._powerOf2=!1}},b.BaseTexture.prototype.constructor=b.BaseTexture,b.BaseTexture.prototype.destroy=function(){this.source instanceof Image&&(this.imageUrl in b.BaseTextureCache&&delete b.BaseTextureCache[this.imageUrl],this.imageUrl=null,this.source.src=null),this.source=null,b.texturesToDestroy.push(this)},b.BaseTexture.prototype.updateSourceImage=function(a){this.hasLoaded=!1,this.source.src=null,this.source.src=a},b.BaseTexture.fromImage=function(a,c,d){var e=b.BaseTextureCache[a];if(!e){var f=new Image;c&&(f.crossOrigin=""),f.src=a,e=new b.BaseTexture(f,d),e.imageUrl=a,b.BaseTextureCache[a]=e}return e},b.BaseTexture.SCALE_MODE={DEFAULT:0,LINEAR:0,NEAREST:1},b.TextureCache={},b.FrameCache={},b.Texture=function(a,c){if(b.EventTarget.call(this),c||(this.noFrame=!0,c=new b.Rectangle(0,0,1,1)),a instanceof b.Texture&&(a=a.baseTexture),this.baseTexture=a,this.frame=c,this.trim=new b.Point,this.scope=this,a.hasLoaded)this.noFrame&&(c=new b.Rectangle(0,0,a.width,a.height)),this.setFrame(c);else{var d=this;a.addEventListener("loaded",function(){d.onBaseTextureLoaded()})}},b.Texture.prototype.constructor=b.Texture,b.Texture.prototype.onBaseTextureLoaded=function(){var a=this.baseTexture;a.removeEventListener("loaded",this.onLoaded),this.noFrame&&(this.frame=new b.Rectangle(0,0,a.width,a.height)),this.noFrame=!1,this.width=this.frame.width,this.height=this.frame.height,this.scope.dispatchEvent({type:"update",content:this})},b.Texture.prototype.destroy=function(a){a&&this.baseTexture.destroy()},b.Texture.prototype.setFrame=function(a){if(this.frame=a,this.width=a.width,this.height=a.height,a.x+a.width>this.baseTexture.width||a.y+a.height>this.baseTexture.height)throw new Error("Texture Error: frame does not fit inside the base Texture dimensions "+this);this.updateFrame=!0,b.Texture.frameUpdates.push(this)},b.Texture.fromImage=function(a,c,d){var e=b.TextureCache[a];return e||(e=new b.Texture(b.BaseTexture.fromImage(a,c,d)),b.TextureCache[a]=e),e},b.Texture.fromFrame=function(a){var c=b.TextureCache[a];if(!c)throw new Error('The frameId "'+a+'" does not exist in the texture cache '+this);return c},b.Texture.fromCanvas=function(a,c){var d=new b.BaseTexture(a,c);return new b.Texture(d)},b.Texture.addTextureToCache=function(a,c){b.TextureCache[c]=a},b.Texture.removeTextureFromCache=function(a){var c=b.TextureCache[a];return b.TextureCache[a]=null,c},b.Texture.frameUpdates=[],b.Texture.SCALE_MODE=b.BaseTexture.SCALE_MODE,b.RenderTexture=function(a,c){b.EventTarget.call(this),this.width=a||100,this.height=c||100,this.indetityMatrix=b.mat3.create(),this.frame=new b.Rectangle(0,0,this.width,this.height),b.gl?this.initWebGL():this.initCanvas()},b.RenderTexture.prototype=Object.create(b.Texture.prototype),b.RenderTexture.prototype.constructor=b.RenderTexture,b.RenderTexture.prototype.initWebGL=function(){var a=b.gl;this.glFramebuffer=a.createFramebuffer(),a.bindFramebuffer(a.FRAMEBUFFER,this.glFramebuffer),this.glFramebuffer.width=this.width,this.glFramebuffer.height=this.height,this.baseTexture=new b.BaseTexture,this.baseTexture.width=this.width,this.baseTexture.height=this.height,this.baseTexture._glTexture=a.createTexture(),a.bindTexture(a.TEXTURE_2D,this.baseTexture._glTexture),a.texImage2D(a.TEXTURE_2D,0,a.RGBA,this.width,this.height,0,a.RGBA,a.UNSIGNED_BYTE,null),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE),a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE),this.baseTexture.isRender=!0,a.bindFramebuffer(a.FRAMEBUFFER,this.glFramebuffer),a.framebufferTexture2D(a.FRAMEBUFFER,a.COLOR_ATTACHMENT0,a.TEXTURE_2D,this.baseTexture._glTexture,0),this.projection=new b.Point(this.width/2,-this.height/2),this.render=this.renderWebGL},b.RenderTexture.prototype.resize=function(a,c){if(this.width=a,this.height=c,b.gl){this.projection.x=this.width/2,this.projection.y=-this.height/2;var d=b.gl;d.bindTexture(d.TEXTURE_2D,this.baseTexture._glTexture),d.texImage2D(d.TEXTURE_2D,0,d.RGBA,this.width,this.height,0,d.RGBA,d.UNSIGNED_BYTE,null)}else this.frame.width=this.width,this.frame.height=this.height,this.renderer.resize(this.width,this.height)},b.RenderTexture.prototype.initCanvas=function(){this.renderer=new b.CanvasRenderer(this.width,this.height,null,0),this.baseTexture=new b.BaseTexture(this.renderer.view),this.frame=new b.Rectangle(0,0,this.width,this.height),this.render=this.renderCanvas},b.RenderTexture.prototype.renderWebGL=function(a,c,d){var e=b.gl;e.colorMask(!0,!0,!0,!0),e.viewport(0,0,this.width,this.height),e.bindFramebuffer(e.FRAMEBUFFER,this.glFramebuffer),d&&(e.clearColor(0,0,0,0),e.clear(e.COLOR_BUFFER_BIT));var f=a.children,g=a.worldTransform;a.worldTransform=b.mat3.create(),a.worldTransform[4]=-1,a.worldTransform[5]=-2*this.projection.y,c&&(a.worldTransform[2]=c.x,a.worldTransform[5]-=c.y),b.visibleCount++,a.vcount=b.visibleCount;for(var h=0,i=f.length;i>h;h++)f[h].updateTransform();var j=a.__renderGroup;j?a===j.root?j.render(this.projection,this.glFramebuffer):j.renderSpecific(a,this.projection,this.glFramebuffer):(this.renderGroup||(this.renderGroup=new b.WebGLRenderGroup(e)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projection,this.glFramebuffer)),a.worldTransform=g},b.RenderTexture.prototype.renderCanvas=function(a,c,d){var e=a.children;a.worldTransform=b.mat3.create(),c&&(a.worldTransform[2]=c.x,a.worldTransform[5]=c.y);for(var f=0,g=e.length;g>f;f++)e[f].updateTransform();d&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a),this.renderer.context.setTransform(1,0,0,1,0,0)},b.EventTarget=function(){var a={};this.addEventListener=this.on=function(b,c){void 0===a[b]&&(a[b]=[]),-1===a[b].indexOf(c)&&a[b].push(c)},this.dispatchEvent=this.emit=function(b){if(a[b.type]&&a[b.type].length)for(var c=0,d=a[b.type].length;d>c;c++)a[b.type][c](b)},this.removeEventListener=this.off=function(b,c){var d=a[b].indexOf(c);-1!==d&&a[b].splice(d,1)},this.removeAllEventListeners=function(b){var c=a[b];c&&(c.length=0)}},b.PolyK={},b.PolyK.Triangulate=function(a){var c=!0,d=a.length>>1;if(3>d)return[];for(var e=[],f=[],g=0;d>g;g++)f.push(g);g=0;for(var h=d;h>3;){var i=f[(g+0)%h],j=f[(g+1)%h],k=f[(g+2)%h],l=a[2*i],m=a[2*i+1],n=a[2*j],o=a[2*j+1],p=a[2*k],q=a[2*k+1],r=!1;if(b.PolyK._convex(l,m,n,o,p,q,c)){r=!0;for(var s=0;h>s;s++){var t=f[s];if(t!==i&&t!==j&&t!==k&&b.PolyK._PointInTriangle(a[2*t],a[2*t+1],l,m,n,o,p,q)){r=!1;break}}}if(r)e.push(i,j,k),f.splice((g+1)%h,1),h--,g=0;else if(g++>3*h){if(!c)return window.console.log("PIXI Warning: shape too complex to fill"),[];for(e=[],f=[],g=0;d>g;g++)f.push(g);g=0,h=d,c=!1}}return e.push(f[0],f[1],f[2]),e},b.PolyK._PointInTriangle=function(a,b,c,d,e,f,g,h){var i=g-c,j=h-d,k=e-c,l=f-d,m=a-c,n=b-d,o=i*i+j*j,p=i*k+j*l,q=i*m+j*n,r=k*k+l*l,s=k*m+l*n,t=1/(o*r-p*p),u=(r*q-p*s)*t,v=(o*s-p*q)*t;return u>=0&&v>=0&&1>u+v},b.PolyK._convex=function(a,b,c,d,e,f,g){return(b-d)*(e-c)+(c-a)*(f-d)>=0===g},c.Camera=function(a,b,d,e,f,g){this.game=a,this.world=a.world,this.id=0,this.view=new c.Rectangle(d,e,f,g),this.screenView=new c.Rectangle(d,e,f,g),this.bounds=new c.Rectangle(d,e,f,g),this.deadzone=null,this.visible=!0,this.atLimit={x:!1,y:!1},this.target=null,this._edge=0,this.displayObject=null},c.Camera.FOLLOW_LOCKON=0,c.Camera.FOLLOW_PLATFORMER=1,c.Camera.FOLLOW_TOPDOWN=2,c.Camera.FOLLOW_TOPDOWN_TIGHT=3,c.Camera.prototype={follow:function(a,b){"undefined"==typeof b&&(b=c.Camera.FOLLOW_LOCKON),this.target=a;var d;switch(b){case c.Camera.FOLLOW_PLATFORMER:var e=this.width/8,f=this.height/3;this.deadzone=new c.Rectangle((this.width-e)/2,(this.height-f)/2-.25*f,e,f);break;case c.Camera.FOLLOW_TOPDOWN:d=Math.max(this.width,this.height)/4,this.deadzone=new c.Rectangle((this.width-d)/2,(this.height-d)/2,d,d);break;case c.Camera.FOLLOW_TOPDOWN_TIGHT:d=Math.max(this.width,this.height)/8,this.deadzone=new c.Rectangle((this.width-d)/2,(this.height-d)/2,d,d);break;case c.Camera.FOLLOW_LOCKON:this.deadzone=null;break;default:this.deadzone=null}},focusOn:function(a){this.setPosition(Math.round(a.x-this.view.halfWidth),Math.round(a.y-this.view.halfHeight))},focusOnXY:function(a,b){this.setPosition(Math.round(a-this.view.halfWidth),Math.round(b-this.view.halfHeight))},update:function(){this.target&&this.updateTarget(),this.bounds&&this.checkBounds(),this.displayObject.position.x=-this.view.x,this.displayObject.position.y=-this.view.y},updateTarget:function(){this.deadzone?(this._edge=this.target.x-this.deadzone.x,this.view.x>this._edge&&(this.view.x=this._edge),this._edge=this.target.x+this.target.width-this.deadzone.x-this.deadzone.width,this.view.xthis._edge&&(this.view.y=this._edge),this._edge=this.target.y+this.target.height-this.deadzone.y-this.deadzone.height,this.view.ythis.bounds.right&&(this.atLimit.x=!0,this.view.x=this.bounds.right-this.width),this.view.ythis.bounds.bottom&&(this.atLimit.y=!0,this.view.y=this.bounds.bottom-this.height),this.view.floor()},setPosition:function(a,b){this.view.x=a,this.view.y=b,this.bounds&&this.checkBounds()},setSize:function(a,b){this.view.width=a,this.view.height=b}},c.Camera.prototype.constructor=c.Camera,Object.defineProperty(c.Camera.prototype,"x",{get:function(){return this.view.x},set:function(a){this.view.x=a,this.bounds&&this.checkBounds()}}),Object.defineProperty(c.Camera.prototype,"y",{get:function(){return this.view.y},set:function(a){this.view.y=a,this.bounds&&this.checkBounds()}}),Object.defineProperty(c.Camera.prototype,"width",{get:function(){return this.view.width},set:function(a){this.view.width=a}}),Object.defineProperty(c.Camera.prototype,"height",{get:function(){return this.view.height},set:function(a){this.view.height=a}}),c.State=function(){this.game=null,this.add=null,this.camera=null,this.cache=null,this.input=null,this.load=null,this.math=null,this.sound=null,this.stage=null,this.time=null,this.tweens=null,this.world=null,this.particles=null,this.physics=null},c.State.prototype={preload:function(){},loadUpdate:function(){},loadRender:function(){},create:function(){},update:function(){},render:function(){},paused:function(){},destroy:function(){}},c.State.prototype.constructor=c.State,c.StateManager=function(a,b){this.game=a,this.states={},this._pendingState=null,"undefined"!=typeof b&&null!==b&&(this._pendingState=b),this._created=!1,this.current="",this.onInitCallback=null,this.onPreloadCallback=null,this.onCreateCallback=null,this.onUpdateCallback=null,this.onRenderCallback=null,this.onPreRenderCallback=null,this.onLoadUpdateCallback=null,this.onLoadRenderCallback=null,this.onPausedCallback=null,this.onShutDownCallback=null},c.StateManager.prototype={boot:function(){this.game.onPause.add(this.pause,this),this.game.onResume.add(this.resume,this),null!==this._pendingState&&("string"==typeof this._pendingState?this.start(this._pendingState,!1,!1):this.add("default",this._pendingState,!0))},add:function(a,b,d){"undefined"==typeof d&&(d=!1);var e;return b instanceof c.State?e=b:"object"==typeof b?(e=b,e.game=this.game):"function"==typeof b&&(e=new b(this.game)),this.states[a]=e,d&&(this.game.isBooted?this.start(a):this._pendingState=a),e},remove:function(a){this.current==a&&(this.callbackContext=null,this.onInitCallback=null,this.onShutDownCallback=null,this.onPreloadCallback=null,this.onLoadRenderCallback=null,this.onLoadUpdateCallback=null,this.onCreateCallback=null,this.onUpdateCallback=null,this.onRenderCallback=null,this.onPausedCallback=null,this.onDestroyCallback=null),delete this.states[a]},start:function(a,b,c){return"undefined"==typeof b&&(b=!0),"undefined"==typeof c&&(c=!1),this.game.isBooted===!1?(this._pendingState=a,void 0):(this.checkState(a)!==!1&&(this.current&&this.onShutDownCallback.call(this.callbackContext,this.game),b&&(this.game.tweens.removeAll(),this.game.world.destroy(),c===!0&&this.game.cache.destroy()),this.setCurrentState(a),this.onPreloadCallback?(this.game.load.reset(),this.onPreloadCallback.call(this.callbackContext,this.game),0===this.game.load.totalQueuedFiles()?this.game.loadComplete():this.game.load.start()):this.game.loadComplete()),void 0)},dummy:function(){},checkState:function(a){if(this.states[a]){var b=!1;return this.states[a].preload&&(b=!0),b===!1&&this.states[a].loadRender&&(b=!0),b===!1&&this.states[a].loadUpdate&&(b=!0),b===!1&&this.states[a].create&&(b=!0),b===!1&&this.states[a].update&&(b=!0),b===!1&&this.states[a].preRender&&(b=!0),b===!1&&this.states[a].render&&(b=!0),b===!1&&this.states[a].paused&&(b=!0),b===!1?(console.warn("Invalid Phaser State object given. Must contain at least a one of the required functions."),!1):!0}return console.warn("Phaser.StateManager - No state found with the key: "+a),!1},link:function(a){this.states[a].game=this.game,this.states[a].add=this.game.add,this.states[a].camera=this.game.camera,this.states[a].cache=this.game.cache,this.states[a].input=this.game.input,this.states[a].load=this.game.load,this.states[a].math=this.game.math,this.states[a].sound=this.game.sound,this.states[a].stage=this.game.stage,this.states[a].time=this.game.time,this.states[a].tweens=this.game.tweens,this.states[a].world=this.game.world,this.states[a].particles=this.game.particles,this.states[a].physics=this.game.physics,this.states[a].rnd=this.game.rnd},setCurrentState:function(a){this.callbackContext=this.states[a],this.link(a),this.onInitCallback=this.states[a].init||this.dummy,this.onPreloadCallback=this.states[a].preload||null,this.onLoadRenderCallback=this.states[a].loadRender||null,this.onLoadUpdateCallback=this.states[a].loadUpdate||null,this.onCreateCallback=this.states[a].create||null,this.onUpdateCallback=this.states[a].update||null,this.onPreRenderCallback=this.states[a].preRender||null,this.onRenderCallback=this.states[a].render||null,this.onPausedCallback=this.states[a].paused||null,this.onShutDownCallback=this.states[a].shutdown||this.dummy,this.current=a,this._created=!1,this.onInitCallback.call(this.callbackContext,this.game)},getCurrentState:function(){return this.states[this.current]},loadComplete:function(){this._created===!1&&this.onCreateCallback?(this._created=!0,this.onCreateCallback.call(this.callbackContext,this.game)):this._created=!0},pause:function(){this._created&&this.onPausedCallback&&this.onPausedCallback.call(this.callbackContext,this.game,!0)},resume:function(){this._created&&this.onre&&this.onPausedCallback.call(this.callbackContext,this.game,!1)},update:function(){this._created&&this.onUpdateCallback?this.onUpdateCallback.call(this.callbackContext,this.game):this.onLoadUpdateCallback&&this.onLoadUpdateCallback.call(this.callbackContext,this.game)},preRender:function(){this.onPreRenderCallback&&this.onPreRenderCallback.call(this.callbackContext,this.game)},render:function(){this._created&&this.onRenderCallback?(this.game.renderType===c.CANVAS&&(this.game.context.save(),this.game.context.setTransform(1,0,0,1,0,0)),this.onRenderCallback.call(this.callbackContext,this.game),this.game.renderType===c.CANVAS&&this.game.context.restore()):this.onLoadRenderCallback&&this.onLoadRenderCallback.call(this.callbackContext,this.game)},destroy:function(){this.callbackContext=null,this.onInitCallback=null,this.onShutDownCallback=null,this.onPreloadCallback=null,this.onLoadRenderCallback=null,this.onLoadUpdateCallback=null,this.onCreateCallback=null,this.onUpdateCallback=null,this.onRenderCallback=null,this.onPausedCallback=null,this.onDestroyCallback=null,this.game=null,this.states={},this._pendingState=null}},c.StateManager.prototype.constructor=c.StateManager,c.LinkedList=function(){this.next=null,this.prev=null,this.first=null,this.last=null,this.total=0},c.LinkedList.prototype={add:function(a){return 0===this.total&&null==this.first&&null==this.last?(this.first=a,this.last=a,this.next=a,a.prev=this,this.total++,a):(this.last.next=a,a.prev=this.last,this.last=a,this.total++,a)},remove:function(a){a==this.first?this.first=this.first.next:a==this.last&&(this.last=this.last.prev),a.prev&&(a.prev.next=a.next),a.next&&(a.next.prev=a.prev),a.next=a.prev=null,null==this.first&&(this.last=null),this.total--},callAll:function(a){if(this.first&&this.last){var b=this.first;do b&&b[a]&&b[a].call(b),b=b.next;while(b!=this.last.next)}}},c.LinkedList.prototype.constructor=c.LinkedList,c.Signal=function(){this._bindings=[],this._prevParams=null;var a=this;this.dispatch=function(){c.Signal.prototype.dispatch.apply(a,arguments)}},c.Signal.prototype={memorize:!1,_shouldPropagate:!0,active:!0,validateListener:function(a,b){if("function"!=typeof a)throw new Error("listener is a required param of {fn}() and should be a Function.".replace("{fn}",b))},_registerListener:function(a,b,d,e){var f,g=this._indexOfListener(a,d);if(-1!==g){if(f=this._bindings[g],f.isOnce()!==b)throw new Error("You cannot add"+(b?"":"Once")+"() then add"+(b?"Once":"")+"() the same listener without removing the relationship first.")}else f=new c.SignalBinding(this,a,b,d,e),this._addBinding(f);return this.memorize&&this._prevParams&&f.execute(this._prevParams),f},_addBinding:function(a){var b=this._bindings.length;do--b;while(this._bindings[b]&&a._priority<=this._bindings[b]._priority);this._bindings.splice(b+1,0,a)},_indexOfListener:function(a,b){for(var c,d=this._bindings.length;d--;)if(c=this._bindings[d],c._listener===a&&c.context===b)return d;return-1},has:function(a,b){return-1!==this._indexOfListener(a,b)},add:function(a,b,c){return this.validateListener(a,"add"),this._registerListener(a,!1,b,c)},addOnce:function(a,b,c){return this.validateListener(a,"addOnce"),this._registerListener(a,!0,b,c)},remove:function(a,b){this.validateListener(a,"remove");var c=this._indexOfListener(a,b);return-1!==c&&(this._bindings[c]._destroy(),this._bindings.splice(c,1)),a},removeAll:function(){for(var a=this._bindings.length;a--;)this._bindings[a]._destroy();this._bindings.length=0},getNumListeners:function(){return this._bindings.length},halt:function(){this._shouldPropagate=!1},dispatch:function(){if(this.active){var a,b=Array.prototype.slice.call(arguments),c=this._bindings.length;if(this.memorize&&(this._prevParams=b),c){a=this._bindings.slice(),this._shouldPropagate=!0;do c--;while(a[c]&&this._shouldPropagate&&a[c].execute(b)!==!1)}}},forget:function(){this._prevParams=null},dispose:function(){this.removeAll(),delete this._bindings,delete this._prevParams},toString:function(){return"[Phaser.Signal active:"+this.active+" numListeners:"+this.getNumListeners()+"]"}},c.Signal.prototype.constructor=c.Signal,c.SignalBinding=function(a,b,c,d,e){this._listener=b,this._isOnce=c,this.context=d,this._signal=a,this._priority=e||0},c.SignalBinding.prototype={active:!0,params:null,execute:function(a){var b,c;return this.active&&this._listener&&(c=this.params?this.params.concat(a):a,b=this._listener.apply(this.context,c),this._isOnce&&this.detach()),b},detach:function(){return this.isBound()?this._signal.remove(this._listener,this.context):null},isBound:function(){return!!this._signal&&!!this._listener},isOnce:function(){return this._isOnce},getListener:function(){return this._listener},getSignal:function(){return this._signal},_destroy:function(){delete this._signal,delete this._listener,delete this.context},toString:function(){return"[Phaser.SignalBinding isOnce:"+this._isOnce+", isBound:"+this.isBound()+", active:"+this.active+"]"}},c.SignalBinding.prototype.constructor=c.SignalBinding,c.Filter=function(a,b,d){this.game=a,this.type=c.WEBGL_FILTER,this.passes=[this],this.dirty=!0,this.padding=0,this.uniforms={time:{type:"1f",value:0},resolution:{type:"2f",value:{x:256,y:256}},mouse:{type:"2f",value:{x:0,y:0}}},this.fragmentSrc=d||[]},c.Filter.prototype={init:function(){},setResolution:function(a,b){this.uniforms.resolution.value.x=a,this.uniforms.resolution.value.y=b},update:function(a){"undefined"!=typeof a&&(a.x>0&&(this.uniforms.mouse.x=a.x.toFixed(2)),a.y>0&&(this.uniforms.mouse.y=a.y.toFixed(2))),this.uniforms.time.value=this.game.time.totalElapsedSeconds()},destroy:function(){this.game=null}},c.Filter.prototype.constructor=c.Filter,Object.defineProperty(c.Filter.prototype,"width",{get:function(){return this.uniforms.resolution.value.x},set:function(a){this.uniforms.resolution.value.x=a}}),Object.defineProperty(c.Filter.prototype,"height",{get:function(){return this.uniforms.resolution.value.y},set:function(a){this.uniforms.resolution.value.y=a}}),c.Plugin=function(a,b){"undefined"==typeof b&&(b=null),this.game=a,this.parent=b,this.active=!1,this.visible=!1,this.hasPreUpdate=!1,this.hasUpdate=!1,this.hasPostUpdate=!1,this.hasRender=!1,this.hasPostRender=!1},c.Plugin.prototype={preUpdate:function(){},update:function(){},render:function(){},postRender:function(){},destroy:function(){this.game=null,this.parent=null,this.active=!1,this.visible=!1}},c.Plugin.prototype.constructor=c.Plugin,c.PluginManager=function(a,b){this.game=a,this._parent=b,this.plugins=[],this._pluginsLength=0},c.PluginManager.prototype={add:function(a){var b=!1;return"function"==typeof a?a=new a(this.game,this._parent):(a.game=this.game,a.parent=this._parent),"function"==typeof a.preUpdate&&(a.hasPreUpdate=!0,b=!0),"function"==typeof a.update&&(a.hasUpdate=!0,b=!0),"function"==typeof a.postUpdate&&(a.hasPostUpdate=!0,b=!0),"function"==typeof a.render&&(a.hasRender=!0,b=!0),"function"==typeof a.postRender&&(a.hasPostRender=!0,b=!0),b?((a.hasPreUpdate||a.hasUpdate||a.hasPostUpdate)&&(a.active=!0),(a.hasRender||a.hasPostRender)&&(a.visible=!0),this._pluginsLength=this.plugins.push(a),"function"==typeof a.init&&a.init(),a):null},remove:function(a){if(0!==this._pluginsLength)for(this._p=0;this._pthis._nextOffsetCheck&&(c.Canvas.getOffset(this.canvas,this.offset),this._nextOffsetCheck=this.game.time.now+this.checkOffsetInterval)},visibilityChange:function(a){this.disableVisibilityChange||(this.game.paused=this.game.paused!==!1||"pagehide"!=a.type&&"blur"!=a.type&&document.hidden!==!0&&document.webkitHidden!==!0?!1:!0)}},c.Stage.prototype.constructor=c.Stage,Object.defineProperty(c.Stage.prototype,"backgroundColor",{get:function(){return this._backgroundColor},set:function(a){this._backgroundColor=a,this.game.transparent===!1&&(this.game.renderType==c.CANVAS?this.game.canvas.style.backgroundColor=a:("string"==typeof a&&(a=c.Color.hexToRGB(a)),this._stage.setBackgroundColor(a)))}}),c.Group=function(a,d,e,f){this.game=a,"undefined"==typeof d&&(d=a.world),this.name=e||"group","undefined"==typeof f&&(f=!1),f?this._container=this.game.stage._stage:(this._container=new b.DisplayObjectContainer,this._container.name=this.name,d?d instanceof c.Group?d._container.addChild(this._container):(d.addChild(this._container),d.updateTransform()):(this.game.stage._stage.addChild(this._container),this.game.stage._stage.updateTransform())),this.type=c.GROUP,this.alive=!0,this.exists=!0,this.group=null,this._container.scale=new c.Point(1,1),this.scale=this._container.scale,this.pivot=this._container.pivot,this.cursor=null},c.Group.RETURN_NONE=0,c.Group.RETURN_TOTAL=1,c.Group.RETURN_CHILD=2,c.Group.SORT_ASCENDING=-1,c.Group.SORT_DESCENDING=1,c.Group.prototype={add:function(a){return a.group!==this&&(a.type&&a.type===c.GROUP?(a.group=this,this._container.addChild(a._container),a._container.updateTransform()):(a.group=this,this._container.addChild(a),a.updateTransform(),a.events&&a.events.onAddedToGroup.dispatch(a,this)),null===this.cursor&&(this.cursor=a)),a},addAt:function(a,b){return a.group!==this&&(a.type&&a.type===c.GROUP?(a.group=this,this._container.addChildAt(a._container,b),a._container.updateTransform()):(a.group=this,this._container.addChildAt(a,b),a.updateTransform(),a.events&&a.events.onAddedToGroup.dispatch(a,this)),null===this.cursor&&(this.cursor=a)),a},getAt:function(a){return this._container.getChildAt(a)},create:function(a,b,d,e,f){"undefined"==typeof f&&(f=!0);var g=new c.Sprite(this.game,a,b,d,e);return g.group=this,g.exists=f,g.visible=f,g.alive=f,this._container.addChild(g),g.updateTransform(),g.events&&g.events.onAddedToGroup.dispatch(g,this),null===this.cursor&&(this.cursor=g),g},createMultiple:function(a,b,d,e){"undefined"==typeof e&&(e=!1);for(var f=0;a>f;f++){var g=new c.Sprite(this.game,0,0,b,d);g.group=this,g.exists=e,g.visible=e,g.alive=e,this._container.addChild(g),g.updateTransform(),g.events&&g.events.onAddedToGroup.dispatch(g,this),null===this.cursor&&(this.cursor=g)}},next:function(){this.cursor&&(this.cursor=this.cursor==this._container.last?this._container._iNext:this.cursor._iNext)},previous:function(){this.cursor&&(this.cursor=this.cursor==this._container._iNext?this._container.last:this.cursor._iPrev)},childTest:function(a,b){var c=a+" next: ";c+=b._iNext?b._iNext.name:"-null-",c=c+" "+a+" prev: ",c+=b._iPrev?b._iPrev.name:"-null-",console.log(c)},swapIndex:function(a,b){var c=this.getAt(a),d=this.getAt(b);this.swap(c,d)},swap:function(a,b){if(a===b||!a.parent||!b.parent||a.group!==this||b.group!==this)return!1;var c=a._iPrev,d=a._iNext,e=b._iPrev,f=b._iNext,g=this._container.last._iNext,h=this.game.stage._stage;do h!==a&&h!==b&&(h.first===a?h.first=b:h.first===b&&(h.first=a),h.last===a?h.last=b:h.last===b&&(h.last=a)),h=h._iNext;while(h!=g);return a._iNext==b?(a._iNext=f,a._iPrev=b,b._iNext=a,b._iPrev=c,c&&(c._iNext=b),f&&(f._iPrev=a),a.__renderGroup&&a.__renderGroup.updateTexture(a),b.__renderGroup&&b.__renderGroup.updateTexture(b),!0):b._iNext==a?(a._iNext=b,a._iPrev=e,b._iNext=d,b._iPrev=a,e&&(e._iNext=a),d&&(d._iPrev=b),a.__renderGroup&&a.__renderGroup.updateTexture(a),b.__renderGroup&&b.__renderGroup.updateTexture(b),!0):(a._iNext=f,a._iPrev=e,b._iNext=d,b._iPrev=c,c&&(c._iNext=b),d&&(d._iPrev=b),e&&(e._iNext=a),f&&(f._iPrev=a),a.__renderGroup&&a.__renderGroup.updateTexture(a),b.__renderGroup&&b.__renderGroup.updateTexture(b),!0)},bringToTop:function(a){return a.group===this&&(this.remove(a),this.add(a)),a},getIndex:function(a){return this._container.children.indexOf(a)},replace:function(a,b){if(this._container.first._iNext){var c=this.getIndex(a);-1!=c&&(void 0!==b.parent&&(b.events.onRemovedFromGroup.dispatch(b,this),b.parent.removeChild(b)),this._container.removeChild(a),this._container.addChildAt(b,c),b.events.onAddedToGroup.dispatch(b,this),b.updateTransform(),this.cursor==a&&(this.cursor=this._container._iNext))}},setProperty:function(a,b,c,d){d=d||0;var e=b.length;1==e?0===d?a[b[0]]=c:1==d?a[b[0]]+=c:2==d?a[b[0]]-=c:3==d?a[b[0]]*=c:4==d&&(a[b[0]]/=c):2==e?0===d?a[b[0]][b[1]]=c:1==d?a[b[0]][b[1]]+=c:2==d?a[b[0]][b[1]]-=c:3==d?a[b[0]][b[1]]*=c:4==d&&(a[b[0]][b[1]]/=c):3==e?0===d?a[b[0]][b[1]][b[2]]=c:1==d?a[b[0]][b[1]][b[2]]+=c:2==d?a[b[0]][b[1]][b[2]]-=c:3==d?a[b[0]][b[1]][b[2]]*=c:4==d&&(a[b[0]][b[1]][b[2]]/=c):4==e&&(0===d?a[b[0]][b[1]][b[2]][b[3]]=c:1==d?a[b[0]][b[1]][b[2]][b[3]]+=c:2==d?a[b[0]][b[1]][b[2]][b[3]]-=c:3==d?a[b[0]][b[1]][b[2]][b[3]]*=c:4==d&&(a[b[0]][b[1]][b[2]][b[3]]/=c))},set:function(a,b,c,d,e,f){b=b.split("."),"undefined"==typeof d&&(d=!1),"undefined"==typeof e&&(e=!1),(d===!1||d&&a.alive)&&(e===!1||e&&a.visible)&&this.setProperty(a,b,c,f)},setAll:function(a,b,c,d,e){if(a=a.split("."),"undefined"==typeof c&&(c=!1),"undefined"==typeof d&&(d=!1),e=e||0,this._container.children.length>0&&this._container.first._iNext){var f=this._container.first._iNext;do(c===!1||c&&f.alive)&&(d===!1||d&&f.visible)&&this.setProperty(f,a,b,e),f=f._iNext;while(f!=this._container.last._iNext)}},addAll:function(a,b,c,d){this.setAll(a,b,c,d,1)},subAll:function(a,b,c,d){this.setAll(a,b,c,d,2)},multiplyAll:function(a,b,c,d){this.setAll(a,b,c,d,3)},divideAll:function(a,b,c,d){this.setAll(a,b,c,d,4)},callAllExists:function(a,b){var c=Array.prototype.splice.call(arguments,2);if(this._container.children.length>0&&this._container.first._iNext){var d=this._container.first._iNext;do d.exists==b&&d[a]&&d[a].apply(d,c),d=d._iNext;while(d!=this._container.last._iNext)}},callbackFromArray:function(a,b,c){if(1==c){if(a[b[0]])return a[b[0]]}else if(2==c){if(a[b[0]][b[1]])return a[b[0]][b[1]]}else if(3==c){if(a[b[0]][b[1]][b[2]])return a[b[0]][b[1]][b[2]]}else if(4==c){if(a[b[0]][b[1]][b[2]][b[3]])return a[b[0]][b[1]][b[2]][b[3]]}else if(a[b])return a[b];return!1},callAll:function(a,b){if("undefined"!=typeof a){a=a.split(".");var c=a.length;if("undefined"==typeof b)b=null;else if("string"==typeof b){b=b.split(".");var d=b.length}var e=Array.prototype.splice.call(arguments,2),f=null,g=null;if(this._container.children.length>0&&this._container.first._iNext){var h=this._container.first._iNext;do f=this.callbackFromArray(h,a,c),b&&f?(g=this.callbackFromArray(h,b,d),f&&f.apply(g,e)):f&&f.apply(h,e),h=h._iNext;while(h!=this._container.last._iNext)}}},forEach:function(a,b,c){"undefined"==typeof c&&(c=!1);var d=Array.prototype.splice.call(arguments,3);if(d.unshift(null),this._container.children.length>0&&this._container.first._iNext){var e=this._container.first._iNext;do(c===!1||c&&e.exists)&&(d[0]=e,a.apply(b,d)),e=e._iNext;while(e!=this._container.last._iNext)}},forEachExists:function(a,b){var d=Array.prototype.splice.call(arguments,2);d.unshift(null),this.iterate("exists",!0,c.Group.RETURN_TOTAL,a,b,d)},forEachAlive:function(a,b){var d=Array.prototype.splice.call(arguments,2);d.unshift(null),this.iterate("alive",!0,c.Group.RETURN_TOTAL,a,b,d)},forEachDead:function(a,b){var d=Array.prototype.splice.call(arguments,2);d.unshift(null),this.iterate("alive",!1,c.Group.RETURN_TOTAL,a,b,d)},sort:function(a,b){"undefined"==typeof a&&(a="y"),"undefined"==typeof b&&(b=c.Group.SORT_ASCENDING);var d,e;do{d=!1;for(var f=0,g=this._container.children.length-1;g>f;f++)b==c.Group.SORT_ASCENDING?this._container.children[f][a]>this._container.children[f+1][a]&&(this.swap(this.getAt(f),this.getAt(f+1)),e=this._container.children[f],this._container.children[f]=this._container.children[f+1],this._container.children[f+1]=e,d=!0):this._container.children[f][a]0&&this._container.first._iNext){var i=this._container.first._iNext;do{if(i[a]===b&&(h++,e&&(g[0]=i,e.apply(f,g)),d===c.Group.RETURN_CHILD))return i;i=i._iNext}while(i!=this._container.last._iNext)}return d===c.Group.RETURN_TOTAL?h:d===c.Group.RETURN_CHILD?null:void 0},getFirstExists:function(a){return"boolean"!=typeof a&&(a=!0),this.iterate("exists",a,c.Group.RETURN_CHILD)},getFirstAlive:function(){return this.iterate("alive",!0,c.Group.RETURN_CHILD)},getFirstDead:function(){return this.iterate("alive",!1,c.Group.RETURN_CHILD)},countLiving:function(){return this.iterate("alive",!0,c.Group.RETURN_TOTAL)},countDead:function(){return this.iterate("alive",!1,c.Group.RETURN_TOTAL)},getRandom:function(a,b){return 0===this._container.children.length?null:(a=a||0,b=b||this._container.children.length,this.game.math.getRandom(this._container.children,a,b))},remove:function(a){return a.group!==this?!1:(a.events&&a.events.onRemovedFromGroup.dispatch(a,this),a.parent===this._container&&this._container.removeChild(a),this.cursor==a&&(this.cursor=this._container._iNext?this._container._iNext:null),a.group=null,!0)},removeAll:function(){if(0!==this._container.children.length){do this._container.children[0].events&&this._container.children[0].events.onRemovedFromGroup.dispatch(this._container.children[0],this),this._container.removeChild(this._container.children[0]);while(this._container.children.length>0);this.cursor=null}},removeBetween:function(a,b){if(0!==this._container.children.length){if(a>b||0>a||b>this._container.children.length)return!1;for(var c=a;b>c;c++){var d=this._container.children[c];d.events.onRemovedFromGroup.dispatch(d,this),this._container.removeChild(d),this.cursor==d&&(this.cursor=this._container._iNext?this._container._iNext:null)}}},destroy:function(a){if("undefined"==typeof a&&(a=!1),a){if(this._container.children.length>0)do this._container.children[0].group&&this._container.children[0].destroy();while(this._container.children.length>0)}else this.removeAll();this._container.parent.removeChild(this._container),this._container=null,this.game=null,this.exists=!1,this.cursor=null},validate:function(){var a=this.game.stage._stage.last._iNext,b=this.game.stage._stage,c=null,d=null,e=0;do{if(e>0){if(b!==c)return console.log("check next fail"),!1;if(b._iPrev!==d)return console.log("check previous fail"),!1}c=b._iNext,d=b,b=b._iNext,e++}while(b!=a);return!0}},c.Group.prototype.constructor=c.Group,Object.defineProperty(c.Group.prototype,"total",{get:function(){return this._container?this.iterate("exists",!0,c.Group.RETURN_TOTAL):0}}),Object.defineProperty(c.Group.prototype,"length",{get:function(){return this._container?this._container.children.length:0}}),Object.defineProperty(c.Group.prototype,"x",{get:function(){return this._container.position.x},set:function(a){this._container.position.x=a}}),Object.defineProperty(c.Group.prototype,"y",{get:function(){return this._container.position.y},set:function(a){this._container.position.y=a}}),Object.defineProperty(c.Group.prototype,"angle",{get:function(){return c.Math.radToDeg(this._container.rotation)},set:function(a){this._container.rotation=c.Math.degToRad(a)}}),Object.defineProperty(c.Group.prototype,"rotation",{get:function(){return this._container.rotation},set:function(a){this._container.rotation=a}}),Object.defineProperty(c.Group.prototype,"visible",{get:function(){return this._container.visible},set:function(a){this._container.visible=a}}),Object.defineProperty(c.Group.prototype,"alpha",{get:function(){return this._container.alpha},set:function(a){this._container.alpha=a}}),c.World=function(a){c.Group.call(this,a,null,"__world",!1),this.bounds=new c.Rectangle(0,0,a.width,a.height),this.camera=null,this.currentRenderOrderID=0},c.World.prototype=Object.create(c.Group.prototype),c.World.prototype.constructor=c.World,c.World.prototype.boot=function(){this.camera=new c.Camera(this.game,0,0,0,this.game.width,this.game.height),this.camera.displayObject=this._container,this.game.camera=this.camera},c.World.prototype.preUpdate=function(){if(this.game.stage._stage.first._iNext){var a=this.game.stage._stage.first._iNext;do a=a.preUpdate&&!a.preUpdate()?a.last._iNext:a._iNext;while(a!=this.game.stage._stage.last._iNext)}},c.World.prototype.update=function(){if(this.currentRenderOrderID=0,this.game.stage._stage.first._iNext){var a=this.game.stage._stage.first._iNext;do a=a.update&&!a.update()?a.last._iNext:a._iNext;while(a!=this.game.stage._stage.last._iNext)}},c.World.prototype.postUpdate=function(){if(this.camera.target&&this.camera.target.postUpdate){if(this.camera.target.postUpdate(),this.camera.update(),this.game.stage._stage.first._iNext){var a=this.game.stage._stage.first._iNext;do a.postUpdate&&a!==this.camera.target&&a.postUpdate(),a=a._iNext;while(a!=this.game.stage._stage.last._iNext)}}else if(this.camera.update(),this.game.stage._stage.first._iNext){var a=this.game.stage._stage.first._iNext;do a.postUpdate&&a.postUpdate(),a=a._iNext;while(a!=this.game.stage._stage.last._iNext)}},c.World.prototype.setBounds=function(a,b,c,d){c0;b--)null===this["pointer"+b]&&(a=b);return 0===a?(console.warn("You can only have 10 Pointer objects"),null):(this["pointer"+a]=new c.Pointer(this.game,a),this["pointer"+a])},update:function(){return this.pollRate>0&&this._pollCounter=b;b++)this["pointer"+b]&&this["pointer"+b].reset();this.currentPointers=0,"none"!==this.game.canvas.style.cursor&&(this.game.canvas.style.cursor="default"),a===!0&&(this.onDown.dispose(),this.onUp.dispose(),this.onTap.dispose(),this.onHold.dispose(),this.onDown=new c.Signal,this.onUp=new c.Signal,this.onTap=new c.Signal,this.onHold=new c.Signal,this.interactiveItems.callAll("reset")),this._pollCounter=0}},resetSpeed:function(a,b){this._oldPosition.setTo(a,b),this.speed.setTo(0,0)},startPointer:function(a){if(this.maxPointers<10&&this.totalActivePointers==this.maxPointers)return null;if(this.pointer1.active===!1)return this.pointer1.start(a);if(this.pointer2.active===!1)return this.pointer2.start(a);for(var b=3;10>=b;b++)if(this["pointer"+b]&&this["pointer"+b].active===!1)return this["pointer"+b].start(a);return null},updatePointer:function(a){if(this.pointer1.active&&this.pointer1.identifier==a.identifier)return this.pointer1.move(a);if(this.pointer2.active&&this.pointer2.identifier==a.identifier)return this.pointer2.move(a);for(var b=3;10>=b;b++)if(this["pointer"+b]&&this["pointer"+b].active&&this["pointer"+b].identifier==a.identifier)return this["pointer"+b].move(a);return null},stopPointer:function(a){if(this.pointer1.active&&this.pointer1.identifier==a.identifier)return this.pointer1.stop(a);if(this.pointer2.active&&this.pointer2.identifier==a.identifier)return this.pointer2.stop(a);for(var b=3;10>=b;b++)if(this["pointer"+b]&&this["pointer"+b].active&&this["pointer"+b].identifier==a.identifier)return this["pointer"+b].stop(a);return null},getPointer:function(a){if(a=a||!1,this.pointer1.active==a)return this.pointer1;if(this.pointer2.active==a)return this.pointer2;for(var b=3;10>=b;b++)if(this["pointer"+b]&&this["pointer"+b].active==a)return this["pointer"+b];return null},getPointerFromIdentifier:function(a){if(this.pointer1.identifier==a)return this.pointer1;if(this.pointer2.identifier==a)return this.pointer2;for(var b=3;10>=b;b++)if(this["pointer"+b]&&this["pointer"+b].identifier==a)return this["pointer"+b];return null}},c.Input.prototype.constructor=c.Input,Object.defineProperty(c.Input.prototype,"x",{get:function(){return this._x},set:function(a){this._x=Math.floor(a)}}),Object.defineProperty(c.Input.prototype,"y",{get:function(){return this._y},set:function(a){this._y=Math.floor(a)}}),Object.defineProperty(c.Input.prototype,"pollLocked",{get:function(){return this.pollRate>0&&this._pollCounter=a;a++)this["pointer"+a]&&this["pointer"+a].active&&this.currentPointers++;return this.currentPointers}}),Object.defineProperty(c.Input.prototype,"worldX",{get:function(){return this.game.camera.view.x+this.x}}),Object.defineProperty(c.Input.prototype,"worldY",{get:function(){return this.game.camera.view.y+this.y}}),c.Key=function(a,b){this.game=a,this.isDown=!1,this.isUp=!1,this.altKey=!1,this.ctrlKey=!1,this.shiftKey=!1,this.timeDown=0,this.duration=0,this.timeUp=0,this.repeats=0,this.keyCode=b,this.onDown=new c.Signal,this.onUp=new c.Signal},c.Key.prototype={processKeyDown:function(a){this.altKey=a.altKey,this.ctrlKey=a.ctrlKey,this.shiftKey=a.shiftKey,this.isDown?(this.duration=a.timeStamp-this.timeDown,this.repeats++):(this.isDown=!0,this.isUp=!1,this.timeDown=a.timeStamp,this.duration=0,this.repeats=0,this.onDown.dispatch(this))},processKeyUp:function(a){this.isDown=!1,this.isUp=!0,this.timeUp=a.timeStamp,this.onUp.dispatch(this)},justPressed:function(a){return"undefined"==typeof a&&(a=250),this.isDown&&this.duration=this.game.input.holdRate&&((this.game.input.multiInputOverride==c.Input.MOUSE_OVERRIDES_TOUCH||this.game.input.multiInputOverride==c.Input.MOUSE_TOUCH_COMBINE||this.game.input.multiInputOverride==c.Input.TOUCH_OVERRIDES_MOUSE&&0===this.game.input.currentPointers)&&this.game.input.onHold.dispatch(this),this._holdSent=!0),this.game.input.recordPointerHistory&&this.game.time.now>=this._nextDrop&&(this._nextDrop=this.game.time.now+this.game.input.recordRate,this._history.push({x:this.position.x,y:this.position.y}),this._history.length>this.game.input.recordLimit&&this._history.shift()))},move:function(a){if(!this.game.input.pollLocked){if("undefined"!=typeof a.button&&(this.button=a.button),this.clientX=a.clientX,this.clientY=a.clientY,this.pageX=a.pageX,this.pageY=a.pageY,this.screenX=a.screenX,this.screenY=a.screenY,this.x=(this.pageX-this.game.stage.offset.x)*this.game.input.scale.x,this.y=(this.pageY-this.game.stage.offset.y)*this.game.input.scale.y,this.position.setTo(this.x,this.y),this.circle.x=this.x,this.circle.y=this.y,(this.game.input.multiInputOverride==c.Input.MOUSE_OVERRIDES_TOUCH||this.game.input.multiInputOverride==c.Input.MOUSE_TOUCH_COMBINE||this.game.input.multiInputOverride==c.Input.TOUCH_OVERRIDES_MOUSE&&0===this.game.input.currentPointers)&&(this.game.input.activePointer=this,this.game.input.x=this.x,this.game.input.y=this.y,this.game.input.position.setTo(this.game.input.x,this.game.input.y),this.game.input.circle.x=this.game.input.x,this.game.input.circle.y=this.game.input.y),this.game.paused)return this;if(this.game.input.moveCallback&&this.game.input.moveCallback.call(this.game.input.moveCallbackContext,this,this.x,this.y),null!==this.targetObject&&this.targetObject.isDragged===!0)return this.targetObject.update(this)===!1&&(this.targetObject=null),this;if(this._highestRenderOrderID=-1,this._highestRenderObject=null,this._highestInputPriorityID=-1,this.game.input.interactiveItems.total>0){var b=this.game.input.interactiveItems.next;do(b.pixelPerfect||b.priorityID>this._highestInputPriorityID||b.priorityID==this._highestInputPriorityID&&b.sprite.renderOrderID>this._highestRenderOrderID)&&b.checkPointerOver(this)&&(this._highestRenderOrderID=b.sprite.renderOrderID,this._highestInputPriorityID=b.priorityID,this._highestRenderObject=b),b=b.next;while(null!=b)}return null==this._highestRenderObject?this.targetObject&&(this.targetObject._pointerOutHandler(this),this.targetObject=null):null==this.targetObject?(this.targetObject=this._highestRenderObject,this._highestRenderObject._pointerOverHandler(this)):this.targetObject==this._highestRenderObject?this._highestRenderObject.update(this)===!1&&(this.targetObject=null):(this.targetObject._pointerOutHandler(this),this.targetObject=this._highestRenderObject,this.targetObject._pointerOverHandler(this)),this}},leave:function(a){this.withinGame=!1,this.move(a)},stop:function(a){if(this._stateReset)return a.preventDefault(),void 0;if(this.timeUp=this.game.time.now,(this.game.input.multiInputOverride==c.Input.MOUSE_OVERRIDES_TOUCH||this.game.input.multiInputOverride==c.Input.MOUSE_TOUCH_COMBINE||this.game.input.multiInputOverride==c.Input.TOUCH_OVERRIDES_MOUSE&&0===this.game.input.currentPointers)&&(this.game.input.onUp.dispatch(this,a),this.duration>=0&&this.duration<=this.game.input.tapRate&&(this.timeUp-this.previousTapTime0&&(this.active=!1),this.withinGame=!1,this.isDown=!1,this.isUp=!0,this.isMouse===!1&&this.game.input.currentPointers--,this.game.input.interactiveItems.total>0){var b=this.game.input.interactiveItems.next;do b&&b._releasedHandler(this),b=b.next;while(null!=b)}return this.targetObject&&this.targetObject._releasedHandler(this),this.targetObject=null,this},justPressed:function(a){return a=a||this.game.input.justPressedRate,this.isDown===!0&&this.timeDown+a>this.game.time.now},justReleased:function(a){return a=a||this.game.input.justReleasedRate,this.isUp===!0&&this.timeUp+a>this.game.time.now},reset:function(){this.isMouse===!1&&(this.active=!1),this.identifier=null,this.isDown=!1,this.isUp=!0,this.totalTouches=0,this._holdSent=!1,this._history.length=0,this._stateReset=!0,this.targetObject&&this.targetObject._releasedHandler(this),this.targetObject=null}},c.Pointer.prototype.constructor=c.Pointer,Object.defineProperty(c.Pointer.prototype,"duration",{get:function(){return this.isUp?-1:this.game.time.now-this.timeDown}}),Object.defineProperty(c.Pointer.prototype,"worldX",{get:function(){return this.game.world.camera.x+this.x}}),Object.defineProperty(c.Pointer.prototype,"worldY",{get:function(){return this.game.world.camera.y+this.y}}),c.Touch=function(a){this.game=a,this.disabled=!1,this.callbackContext=this.game,this.touchStartCallback=null,this.touchMoveCallback=null,this.touchEndCallback=null,this.touchEnterCallback=null,this.touchLeaveCallback=null,this.touchCancelCallback=null,this.preventDefault=!0,this.event=null,this._onTouchStart=null,this._onTouchMove=null,this._onTouchEnd=null,this._onTouchEnter=null,this._onTouchLeave=null,this._onTouchCancel=null,this._onTouchMove=null},c.Touch.prototype={start:function(){var a=this;this.game.device.touch&&(this._onTouchStart=function(b){return a.onTouchStart(b)},this._onTouchMove=function(b){return a.onTouchMove(b)},this._onTouchEnd=function(b){return a.onTouchEnd(b)},this._onTouchEnter=function(b){return a.onTouchEnter(b)},this._onTouchLeave=function(b){return a.onTouchLeave(b)},this._onTouchCancel=function(b){return a.onTouchCancel(b)},this.game.renderer.view.addEventListener("touchstart",this._onTouchStart,!1),this.game.renderer.view.addEventListener("touchmove",this._onTouchMove,!1),this.game.renderer.view.addEventListener("touchend",this._onTouchEnd,!1),this.game.renderer.view.addEventListener("touchenter",this._onTouchEnter,!1),this.game.renderer.view.addEventListener("touchleave",this._onTouchLeave,!1),this.game.renderer.view.addEventListener("touchcancel",this._onTouchCancel,!1))},consumeDocumentTouches:function(){this._documentTouchMove=function(a){a.preventDefault()},document.addEventListener("touchmove",this._documentTouchMove,!1)},onTouchStart:function(a){if(this.event=a,this.touchStartCallback&&this.touchStartCallback.call(this.callbackContext,a),!this.game.input.disabled&&!this.disabled){this.preventDefault&&a.preventDefault();for(var b=0;bd;d++)this._pointerData[d]={id:d,x:0,y:0,isDown:!1,isUp:!1,isOver:!1,isOut:!1,timeOver:0,timeOut:0,timeDown:0,timeUp:0,downDuration:0,isDragged:!1};this.snapOffset=new c.Point,this.enabled=!0,this.sprite.events&&null==this.sprite.events.onInputOver&&(this.sprite.events.onInputOver=new c.Signal,this.sprite.events.onInputOut=new c.Signal,this.sprite.events.onInputDown=new c.Signal,this.sprite.events.onInputUp=new c.Signal,this.sprite.events.onDragStart=new c.Signal,this.sprite.events.onDragStop=new c.Signal)}return this.sprite},reset:function(){this.enabled=!1;for(var a=0;10>a;a++)this._pointerData[a]={id:a,x:0,y:0,isDown:!1,isUp:!1,isOver:!1,isOut:!1,timeOver:0,timeOut:0,timeDown:0,timeUp:0,downDuration:0,isDragged:!1}},stop:function(){this.enabled!==!1&&(this.enabled=!1,this.game.input.interactiveItems.remove(this))},destroy:function(){this.enabled&&(this.enabled=!1,this.game.input.interactiveItems.remove(this),this.stop(),this.sprite=null)},pointerX:function(a){return a=a||0,this._pointerData[a].x},pointerY:function(a){return a=a||0,this._pointerData[a].y},pointerDown:function(a){return a=a||0,this._pointerData[a].isDown},pointerUp:function(a){return a=a||0,this._pointerData[a].isUp},pointerTimeDown:function(a){return a=a||0,this._pointerData[a].timeDown},pointerTimeUp:function(a){return a=a||0,this._pointerData[a].timeUp},pointerOver:function(a){if(this.enabled){if("undefined"!=typeof a)return this._pointerData[a].isOver;for(var b=0;10>b;b++)if(this._pointerData[b].isOver)return!0}return!1},pointerOut:function(a){if(this.enabled){if("undefined"!=typeof a)return this._pointerData[a].isOut;for(var b=0;10>b;b++)if(this._pointerData[b].isOut)return!0}return!1},pointerTimeOver:function(a){return a=a||0,this._pointerData[a].timeOver},pointerTimeOut:function(a){return a=a||0,this._pointerData[a].timeOut},pointerDragged:function(a){return a=a||0,this._pointerData[a].isDragged},checkPointerOver:function(a){return this.enabled===!1||this.sprite.visible===!1||this.sprite.group&&this.sprite.group.visible===!1?!1:(this.sprite.getLocalUnmodifiedPosition(this._tempPoint,a.x,a.y),this._tempPoint.x>=0&&this._tempPoint.x<=this.sprite.currentFrame.width&&this._tempPoint.y>=0&&this._tempPoint.y<=this.sprite.currentFrame.height?this.pixelPerfect?this.checkPixel(this._tempPoint.x,this._tempPoint.y):!0:void 0)},checkPixel:function(a,b){if(this.sprite.texture.baseTexture.source){this.game.input.hitContext.clearRect(0,0,1,1),a+=this.sprite.texture.frame.x,b+=this.sprite.texture.frame.y,this.game.input.hitContext.drawImage(this.sprite.texture.baseTexture.source,a,b,1,1,0,0,1,1);var c=this.game.input.hitContext.getImageData(0,0,1,1);if(c.data[3]>=this.pixelPerfectAlpha)return!0}return!1},update:function(a){return this.enabled===!1||this.sprite.visible===!1||this.sprite.group&&this.sprite.group.visible===!1?(this._pointerOutHandler(a),!1):this.draggable&&this._draggedPointerID==a.id?this.updateDrag(a):this._pointerData[a.id].isOver===!0?this.checkPointerOver(a)?(this._pointerData[a.id].x=a.x-this.sprite.x,this._pointerData[a.id].y=a.y-this.sprite.y,!0):(this._pointerOutHandler(a),!1):void 0},_pointerOverHandler:function(a){this._pointerData[a.id].isOver===!1&&(this._pointerData[a.id].isOver=!0,this._pointerData[a.id].isOut=!1,this._pointerData[a.id].timeOver=this.game.time.now,this._pointerData[a.id].x=a.x-this.sprite.x,this._pointerData[a.id].y=a.y-this.sprite.y,this.useHandCursor&&this._pointerData[a.id].isDragged===!1&&(this.game.canvas.style.cursor="pointer"),this.sprite.events.onInputOver.dispatch(this.sprite,a))},_pointerOutHandler:function(a){this._pointerData[a.id].isOver=!1,this._pointerData[a.id].isOut=!0,this._pointerData[a.id].timeOut=this.game.time.now,this.useHandCursor&&this._pointerData[a.id].isDragged===!1&&(this.game.canvas.style.cursor="default"),this.sprite&&this.sprite.events&&this.sprite.events.onInputOut.dispatch(this.sprite,a)},_touchedHandler:function(a){return this._pointerData[a.id].isDown===!1&&this._pointerData[a.id].isOver===!0&&(this._pointerData[a.id].isDown=!0,this._pointerData[a.id].isUp=!1,this._pointerData[a.id].timeDown=this.game.time.now,this.sprite.events.onInputDown.dispatch(this.sprite,a),this.draggable&&this.isDragged===!1&&this.startDrag(a),this.bringToTop&&this.sprite.bringToTop()),this.consumePointerEvent},_releasedHandler:function(a){this._pointerData[a.id].isDown&&a.isUp&&(this._pointerData[a.id].isDown=!1,this._pointerData[a.id].isUp=!0,this._pointerData[a.id].timeUp=this.game.time.now,this._pointerData[a.id].downDuration=this._pointerData[a.id].timeUp-this._pointerData[a.id].timeDown,this.checkPointerOver(a)?this.sprite.events.onInputUp.dispatch(this.sprite,a,!0):(this.sprite.events.onInputUp.dispatch(this.sprite,a,!1),this.useHandCursor&&(this.game.canvas.style.cursor="default")),this.draggable&&this.isDragged&&this._draggedPointerID==a.id&&this.stopDrag(a))},updateDrag:function(a){return a.isUp?(this.stopDrag(a),!1):(this.sprite.fixedToCamera?(this.allowHorizontalDrag&&(this.sprite.cameraOffset.x=a.x+this._dragPoint.x+this.dragOffset.x),this.allowVerticalDrag&&(this.sprite.cameraOffset.y=a.y+this._dragPoint.y+this.dragOffset.y),this.boundsRect&&this.checkBoundsRect(),this.boundsSprite&&this.checkBoundsSprite(),this.snapOnDrag&&(this.sprite.cameraOffset.x=Math.round((this.sprite.cameraOffset.x-this.snapOffsetX%this.snapX)/this.snapX)*this.snapX+this.snapOffsetX%this.snapX,this.sprite.cameraOffset.y=Math.round((this.sprite.cameraOffset.y-this.snapOffsetY%this.snapY)/this.snapY)*this.snapY+this.snapOffsetY%this.snapY)):(this.allowHorizontalDrag&&(this.sprite.x=a.x+this._dragPoint.x+this.dragOffset.x),this.allowVerticalDrag&&(this.sprite.y=a.y+this._dragPoint.y+this.dragOffset.y),this.boundsRect&&this.checkBoundsRect(),this.boundsSprite&&this.checkBoundsSprite(),this.snapOnDrag&&(this.sprite.x=Math.round((this.sprite.x-this.snapOffsetX%this.snapX)/this.snapX)*this.snapX+this.snapOffsetX%this.snapX,this.sprite.y=Math.round((this.sprite.y-this.snapOffsetY%this.snapY)/this.snapY)*this.snapY+this.snapOffsetY%this.snapY)),!0)},justOver:function(a,b){return a=a||0,b=b||500,this._pointerData[a].isOver&&this.overDuration(a)a;a++)this._pointerData[a].isDragged=!1;this.draggable=!1,this.isDragged=!1,this._draggedPointerID=-1},startDrag:function(a){this.isDragged=!0,this._draggedPointerID=a.id,this._pointerData[a.id].isDragged=!0,this.sprite.fixedToCamera?this.dragFromCenter?(this.sprite.centerOn(a.x,a.y),this._dragPoint.setTo(this.sprite.cameraOffset.x-a.x,this.sprite.cameraOffset.y-a.y)):this._dragPoint.setTo(this.sprite.cameraOffset.x-a.x,this.sprite.cameraOffset.y-a.y):this.dragFromCenter?(this.sprite.centerOn(a.x,a.y),this._dragPoint.setTo(this.sprite.x-a.x,this.sprite.y-a.y)):this._dragPoint.setTo(this.sprite.x-a.x,this.sprite.y-a.y),this.updateDrag(a),this.bringToTop&&this.sprite.bringToTop(),this.sprite.events.onDragStart.dispatch(this.sprite,a)},stopDrag:function(a){this.isDragged=!1,this._draggedPointerID=-1,this._pointerData[a.id].isDragged=!1,this.snapOnRelease&&(this.sprite.fixedToCamera?(this.sprite.cameraOffset.x=Math.round((this.sprite.cameraOffset.x-this.snapOffsetX%this.snapX)/this.snapX)*this.snapX+this.snapOffsetX%this.snapX,this.sprite.cameraOffset.y=Math.round((this.sprite.cameraOffset.y-this.snapOffsetY%this.snapY)/this.snapY)*this.snapY+this.snapOffsetY%this.snapY):(this.sprite.x=Math.round((this.sprite.x-this.snapOffsetX%this.snapX)/this.snapX)*this.snapX+this.snapOffsetX%this.snapX,this.sprite.y=Math.round((this.sprite.y-this.snapOffsetY%this.snapY)/this.snapY)*this.snapY+this.snapOffsetY%this.snapY)),this.sprite.events.onDragStop.dispatch(this.sprite,a),this.sprite.events.onInputUp.dispatch(this.sprite,a),this.checkPointerOver(a)===!1&&this._pointerOutHandler(a)},setDragLock:function(a,b){"undefined"==typeof a&&(a=!0),"undefined"==typeof b&&(b=!0),this.allowHorizontalDrag=a,this.allowVerticalDrag=b},enableSnap:function(a,b,c,d){"undefined"==typeof c&&(c=!0),"undefined"==typeof d&&(d=!1),"undefined"==typeof snapOffsetX&&(snapOffsetX=0),"undefined"==typeof snapOffsetY&&(snapOffsetY=0),this.snapX=a,this.snapY=b,this.snapOffsetX=snapOffsetX,this.snapOffsetY=snapOffsetY,this.snapOnDrag=c,this.snapOnRelease=d},disableSnap:function(){this.snapOnDrag=!1,this.snapOnRelease=!1},checkBoundsRect:function(){this.sprite.fixedToCamera?(this.sprite.cameraOffset.xthis.boundsRect.right&&(this.sprite.cameraOffset.x=this.boundsRect.right-this.sprite.width),this.sprite.cameraOffset.ythis.boundsRect.bottom&&(this.sprite.cameraOffset.y=this.boundsRect.bottom-this.sprite.height)):(this.sprite.xthis.boundsRect.right&&(this.sprite.x=this.boundsRect.right-this.sprite.width),this.sprite.ythis.boundsRect.bottom&&(this.sprite.y=this.boundsRect.bottom-this.sprite.height))},checkBoundsSprite:function(){this.sprite.fixedToCamera&&this.boundsSprite.fixedToCamera?(this.sprite.cameraOffset.xthis.boundsSprite.camerOffset.x+this.boundsSprite.width&&(this.sprite.cameraOffset.x=this.boundsSprite.camerOffset.x+this.boundsSprite.width-this.sprite.width),this.sprite.cameraOffset.ythis.boundsSprite.camerOffset.y+this.boundsSprite.height&&(this.sprite.cameraOffset.y=this.boundsSprite.camerOffset.y+this.boundsSprite.height-this.sprite.height)):(this.sprite.xthis.boundsSprite.x+this.boundsSprite.width&&(this.sprite.x=this.boundsSprite.x+this.boundsSprite.width-this.sprite.width),this.sprite.ythis.boundsSprite.y+this.boundsSprite.height&&(this.sprite.y=this.boundsSprite.y+this.boundsSprite.height-this.sprite.height))}},c.InputHandler.prototype.constructor=c.InputHandler,c.Gamepad=function(a){this.game=a,this._gamepads=[new c.SinglePad(a,this),new c.SinglePad(a,this),new c.SinglePad(a,this),new c.SinglePad(a,this)],this._gamepadIndexMap={},this._rawPads=[],this._active=!1,this.disabled=!1,this._gamepadSupportAvailable=!!navigator.webkitGetGamepads||!!navigator.webkitGamepads||-1!=navigator.userAgent.indexOf("Firefox/"),this._prevRawGamepadTypes=[],this._prevTimestamps=[],this.callbackContext=this,this.onConnectCallback=null,this.onDisconnectCallback=null,this.onDownCallback=null,this.onUpCallback=null,this.onAxisCallback=null,this.onFloatCallback=null,this._ongamepadconnected=null,this._gamepaddisconnected=null},c.Gamepad.prototype={addCallbacks:function(a,b){"undefined"!=typeof b&&(this.onConnectCallback="function"==typeof b.onConnect?b.onConnect:this.onConnectCallback,this.onDisconnectCallback="function"==typeof b.onDisconnect?b.onDisconnect:this.onDisconnectCallback,this.onDownCallback="function"==typeof b.onDown?b.onDown:this.onDownCallback,this.onUpCallback="function"==typeof b.onUp?b.onUp:this.onUpCallback,this.onAxisCallback="function"==typeof b.onAxis?b.onAxis:this.onAxisCallback,this.onFloatCallback="function"==typeof b.onFloat?b.onFloat:this.onFloatCallback)},start:function(){this._active=!0;var a=this;this._ongamepadconnected=function(b){var c=b.gamepad;a._rawPads.push(c),a._gamepads[c.index].connect(c)},window.addEventListener("gamepadconnected",this._ongamepadconnected,!1),this._ongamepaddisconnected=function(b){var c=b.gamepad;for(var d in a._rawPads)a._rawPads[d].index===c.index&&a._rawPads.splice(d,1);a._gamepads[c.index].disconnect()},window.addEventListener("gamepaddisconnected",this._ongamepaddisconnected,!1)
-},update:function(){this._pollGamepads();for(var a=0;a0&&e>this.deadZone||0>e&&e<-this.deadZone?this.processAxisChange({axis:d,value:e}):this.processAxisChange({axis:d,value:0})}this._prevTimestamp=this._rawPad.timestamp}},connect:function(a){var b=!this._connected;this._index=a.index,this._connected=!0,this._rawPad=a,this._rawButtons=a.buttons,this._axes=a.axes,b&&this._padParent.onConnectCallback&&this._padParent.onConnectCallback.call(this._padParent.callbackContext,this._index),b&&this.onConnectCallback&&this.onConnectCallback.call(this.callbackContext)},disconnect:function(){var a=this._connected;this._connected=!1,this._rawPad=void 0,this._rawButtons=[],this._buttons=[];var b=this._index;this._index=null,a&&this._padParent.onDisconnectCallback&&this._padParent.onDisconnectCallback.call(this._padParent.callbackContext,b),a&&this.onDisconnectCallback&&this.onDisconnectCallback.call(this.callbackContext)},processAxisChange:function(a){this.game.input.disabled||this.game.input.gamepad.disabled||this._axes[a.axis]!==a.value&&(this._axes[a.axis]=a.value,this._padParent.onAxisCallback&&this._padParent.onAxisCallback.call(this._padParent.callbackContext,a,this._index),this.onAxisCallback&&this.onAxisCallback.call(this.callbackContext,a))},processButtonDown:function(a,b){this.game.input.disabled||this.game.input.gamepad.disabled||(this._padParent.onDownCallback&&this._padParent.onDownCallback.call(this._padParent.callbackContext,a,b,this._index),this.onDownCallback&&this.onDownCallback.call(this.callbackContext,a,b),this._buttons[a]&&this._buttons[a].isDown?this._buttons[a].duration=this.game.time.now-this._buttons[a].timeDown:this._buttons[a]?(this._buttons[a].isDown=!0,this._buttons[a].timeDown=this.game.time.now,this._buttons[a].duration=0,this._buttons[a].value=b):this._buttons[a]={isDown:!0,timeDown:this.game.time.now,timeUp:0,duration:0,value:b},this._hotkeys[a]&&this._hotkeys[a].processButtonDown(b))},processButtonUp:function(a,b){this.game.input.disabled||this.game.input.gamepad.disabled||(this._padParent.onUpCallback&&this._padParent.onUpCallback.call(this._padParent.callbackContext,a,b,this._index),this.onUpCallback&&this.onUpCallback.call(this.callbackContext,a,b),this._hotkeys[a]&&this._hotkeys[a].processButtonUp(b),this._buttons[a]?(this._buttons[a].isDown=!1,this._buttons[a].timeUp=this.game.time.now,this._buttons[a].value=b):this._buttons[a]={isDown:!1,timeDown:this.game.time.now,timeUp:this.game.time.now,duration:0,value:b})},processButtonFloat:function(a,b){this.game.input.disabled||this.game.input.gamepad.disabled||(this._padParent.onFloatCallback&&this._padParent.onFloatCallback.call(this._padParent.callbackContext,a,b,this._index),this.onFloatCallback&&this.onFloatCallback.call(this.callbackContext,a,b),this._buttons[a]?this._buttons[a].value=b:this._buttons[a]={value:b},this._hotkeys[a]&&this._hotkeys[a].processButtonFloat(b))},axis:function(a){return this._axes[a]?this._axes[a]:!1},isDown:function(a){return this._buttons[a]?this._buttons[a].isDown:!1},justReleased:function(a,b){return"undefined"==typeof b&&(b=250),this._buttons[a]&&this._buttons[a].isDown===!1&&this.game.time.now-this._buttons[a].timeUp=0&&a<=this.width&&b>=0&&b<=this.height&&(this.pixels[b*this.width+a]=f<<24|e<<16|d<<8|c,this.context.putImageData(this.imageData,0,0),this._dirty=!0)},setPixel:function(a,b,c,d,e){this.setPixel32(a,b,c,d,e,255)},getPixel:function(a,b){return a>=0&&a<=this.width&&b>=0&&b<=this.height?this.data32[b*this.width+a]:void 0},getPixel32:function(a,b){return a>=0&&a<=this.width&&b>=0&&b<=this.height?this.data32[b*this.width+a]:void 0},getPixels:function(a){return this.context.getImageData(a.x,a.y,a.width,a.height)},arc:function(a,b,c,d,e,f){return"undefined"==typeof f&&(f=!1),this._dirty=!0,this.context.arc(a,b,c,d,e,f),this},arcTo:function(a,b,c,d,e){return this._dirty=!0,this.context.arcTo(a,b,c,d,e),this},beginFill:function(a){return this.fillStyle(a),this},beginLinearGradientFill:function(a,b,c,d,e,f){for(var g=this.createLinearGradient(c,d,e,f),h=0,i=a.length;i>h;h++)g.addColorStop(b[h],a[h]);return this.fillStyle(g),this},beginLinearGradientStroke:function(a,b,c,d,e,f){for(var g=this.createLinearGradient(c,d,e,f),h=0,i=a.length;i>h;h++)g.addColorStop(b[h],a[h]);return this.strokeStyle(g),this},beginRadialGradientStroke:function(a,b,c,d,e,f,g,h){for(var i=this.createRadialGradient(c,d,e,f,g,h),j=0,k=a.length;k>j;j++)i.addColorStop(b[j],a[j]);return this.strokeStyle(i),this},beginPath:function(){return this.context.beginPath(),this},beginStroke:function(a){return this.strokeStyle(a),this},bezierCurveTo:function(a,b,c,d,e,f){return this._dirty=!0,this.context.bezierCurveTo(a,b,c,d,e,f),this},circle:function(a,b,c){return this.arc(a,b,c,0,2*Math.PI),this},clearRect:function(a,b,c,d){return this._dirty=!0,this.context.clearRect(a,b,c,d),this},clip:function(){return this._dirty=!0,this.context.clip(),this},closePath:function(){return this._dirty=!0,this.context.closePath(),this},createLinearGradient:function(a,b,c,d){return this.context.createLinearGradient(a,b,c,d)},createRadialGradient:function(a,b,c,d,e,f){return this.context.createRadialGradient(a,b,c,d,e,f)},ellipse:function(a,b,c,d){var e=.5522848,f=c/2*e,g=d/2*e,h=a+c,i=b+d,j=a+c/2,k=b+d/2;return this.moveTo(a,k),this.bezierCurveTo(a,k-g,j-f,b,j,b),this.bezierCurveTo(j+f,b,h,k-g,h,k),this.bezierCurveTo(h,k+g,j+f,i,j,i),this.bezierCurveTo(j-f,i,a,k+g,a,k),this},fill:function(){return this._dirty=!0,this.context.fill(),this},fillRect:function(a,b,c,d){return this._dirty=!0,this.context.fillRect(a,b,c,d),this},fillStyle:function(a){return this.context.fillStyle=a,this},font:function(a){return this.context.font=a,this},globalAlpha:function(a){return this.context.globalAlpha=a,this},globalCompositeOperation:function(a){return this.context.globalCompositeOperation=a,this},lineCap:function(a){return this.context.lineCap=a,this},lineDashOffset:function(a){return this.context.lineDashOffset=a,this},lineJoin:function(a){return this.context.lineJoin=a,this},lineWidth:function(a){return this.context.lineWidth=a,this},miterLimit:function(a){return this.context.miterLimit=a,this},lineTo:function(a,b){return this._dirty=!0,this.context.lineTo(a,b),this},moveTo:function(a,b){return this.context.moveTo(a,b),this},quadraticCurveTo:function(a,b,c,d){return this._dirty=!0,this.context.quadraticCurveTo(a,b,c,d),this},rect:function(a,b,c,d){return this._dirty=!0,this.context.rect(a,b,c,d),this},restore:function(){return this._dirty=!0,this.context.restore(),this},rotate:function(a){return this._dirty=!0,this.context.rotate(a),this},setStrokeStyle:function(a,b,c,d,e){return"undefined"==typeof a&&(a=1),"undefined"==typeof b&&(b="butt"),"undefined"==typeof c&&(c="miter"),"undefined"==typeof d&&(d=10),e=!1,this.lineWidth(a),this.lineCap(b),this.lineJoin(c),this.miterLimit(d),this},save:function(){return this._dirty=!0,this.context.save(),this},scale:function(a,b){return this._dirty=!0,this.context.scale(a,b),this},scrollPathIntoView:function(){return this._dirty=!0,this.context.scrollPathIntoView(),this},stroke:function(){return this._dirty=!0,this.context.stroke(),this},strokeRect:function(a,b,c,d){return this._dirty=!0,this.context.strokeRect(a,b,c,d),this},strokeStyle:function(a){return this.context.strokeStyle=a,this},render:function(){this._dirty&&(this.game.renderType==c.WEBGL&&b.texturesToUpdate.push(this.baseTexture),this._dirty=!1)}},c.BitmapData.prototype.constructor=c.BitmapData,c.BitmapData.prototype.mt=c.BitmapData.prototype.moveTo,c.BitmapData.prototype.lt=c.BitmapData.prototype.lineTo,c.BitmapData.prototype.at=c.BitmapData.prototype.arcTo,c.BitmapData.prototype.bt=c.BitmapData.prototype.bezierCurveTo,c.BitmapData.prototype.qt=c.BitmapData.prototype.quadraticCurveTo,c.BitmapData.prototype.a=c.BitmapData.prototype.arc,c.BitmapData.prototype.r=c.BitmapData.prototype.rect,c.BitmapData.prototype.cp=c.BitmapData.prototype.closePath,c.BitmapData.prototype.c=c.BitmapData.prototype.clear,c.BitmapData.prototype.f=c.BitmapData.prototype.beginFill,c.BitmapData.prototype.lf=c.BitmapData.prototype.beginLinearGradientFill,c.BitmapData.prototype.rf=c.BitmapData.prototype.beginRadialGradientFill,c.BitmapData.prototype.ef=c.BitmapData.prototype.endFill,c.BitmapData.prototype.ss=c.BitmapData.prototype.setStrokeStyle,c.BitmapData.prototype.s=c.BitmapData.prototype.beginStroke,c.BitmapData.prototype.ls=c.BitmapData.prototype.beginLinearGradientStroke,c.BitmapData.prototype.rs=c.BitmapData.prototype.beginRadialGradientStroke,c.BitmapData.prototype.dr=c.BitmapData.prototype.rect,c.BitmapData.prototype.dc=c.BitmapData.prototype.circle,c.BitmapData.prototype.de=c.BitmapData.prototype.ellipse,c.Sprite=function(a,d,e,f,g){d=d||0,e=e||0,f=f||null,g=g||null,this.game=a,this.exists=!0,this.alive=!0,this.group=null,this.name="",this.type=c.SPRITE,this.renderOrderID=-1,this.lifespan=0,this.events=new c.Events(this),this.animations=new c.AnimationManager(this),this.input=new c.InputHandler(this),this.key=f,this.currentFrame=null,f instanceof c.RenderTexture?(b.Sprite.call(this,f),this.currentFrame=this.game.cache.getTextureFrame(f.name)):f instanceof c.BitmapData?(b.Sprite.call(this,f.texture,f.textureFrame),this.currentFrame=f.textureFrame):f instanceof b.Texture?(b.Sprite.call(this,f),this.currentFrame=g):(null===f||"undefined"==typeof f?(f="__default",this.key=f):"string"==typeof f&&this.game.cache.checkImageKey(f)===!1&&(f="__missing",this.key=f),b.Sprite.call(this,b.TextureCache[f]),this.game.cache.isSpriteSheet(f)?(this.animations.loadFrameData(this.game.cache.getFrameData(f)),null!==g&&("string"==typeof g?this.frameName=g:this.frame=g)):this.currentFrame=this.game.cache.getFrame(f)),this.textureRegion=new c.Rectangle(this.texture.frame.x,this.texture.frame.y,this.texture.frame.width,this.texture.frame.height),this.anchor=new c.Point,this.x=d,this.y=e,this.position.x=d,this.position.y=e,this.world=new c.Point(d,e),this.autoCull=!1,this.scale=new c.Point(1,1),this._cache={fresh:!0,dirty:!1,a00:-1,a01:-1,a02:-1,a10:-1,a11:-1,a12:-1,id:-1,i01:-1,i10:-1,idi:-1,left:null,right:null,top:null,bottom:null,prevX:d,prevY:e,x:-1,y:-1,scaleX:1,scaleY:1,width:this.currentFrame.sourceSizeW,height:this.currentFrame.sourceSizeH,halfWidth:Math.floor(this.currentFrame.sourceSizeW/2),halfHeight:Math.floor(this.currentFrame.sourceSizeH/2),calcWidth:-1,calcHeight:-1,frameID:-1,frameWidth:this.currentFrame.width,frameHeight:this.currentFrame.height,cameraVisible:!0,cropX:0,cropY:0,cropWidth:this.currentFrame.sourceSizeW,cropHeight:this.currentFrame.sourceSizeH},this.offset=new c.Point,this.center=new c.Point(d+Math.floor(this._cache.width/2),e+Math.floor(this._cache.height/2)),this.topLeft=new c.Point(d,e),this.topRight=new c.Point(d+this._cache.width,e),this.bottomRight=new c.Point(d+this._cache.width,e+this._cache.height),this.bottomLeft=new c.Point(d,e+this._cache.height),this.bounds=new c.Rectangle(d,e,this._cache.width,this._cache.height),this.body=new c.Physics.Arcade.Body(this),this.health=1,this.inWorld=c.Rectangle.intersects(this.bounds,this.game.world.bounds),this.inWorldThreshold=0,this.outOfBoundsKill=!1,this._outOfBoundsFired=!1,this.fixedToCamera=!1,this.cameraOffset=new c.Point(d,e),this.crop=new c.Rectangle(0,0,this._cache.width,this._cache.height),this.cropEnabled=!1,this.debug=!1,this.updateCache(),this.updateBounds()},c.Sprite.prototype=Object.create(b.Sprite.prototype),c.Sprite.prototype.constructor=c.Sprite,c.Sprite.prototype.preUpdate=function(){return this._cache.fresh?(this.world.setTo(this.parent.position.x+this.x,this.parent.position.y+this.y),this.worldTransform[2]=this.world.x,this.worldTransform[5]=this.world.y,this._cache.fresh=!1,this.body&&(this.body.x=this.world.x-this.anchor.x*this.width+this.body.offset.x,this.body.y=this.world.y-this.anchor.y*this.height+this.body.offset.y,this.body.preX=this.body.x,this.body.preY=this.body.y),void 0):!this.exists||this.group&&!this.group.exists?(this.renderOrderID=-1,!1):this.lifespan>0&&(this.lifespan-=this.game.time.elapsed,this.lifespan<=0)?(this.kill(),!1):(this._cache.dirty=!1,this.visible&&(this.renderOrderID=this.game.world.currentRenderOrderID++),this.updateCache(),this.updateAnimation(),this.updateCrop(),(this._cache.dirty||this.world.x!==this._cache.prevX||this.world.y!==this._cache.prevY)&&this.updateBounds(),this.body&&this.body.preUpdate(),!0)},c.Sprite.prototype.updateCache=function(){this._cache.prevX=this.world.x,this._cache.prevY=this.world.y,this.fixedToCamera&&(this.x=this.game.camera.view.x+this.cameraOffset.x,this.y=this.game.camera.view.y+this.cameraOffset.y),this.world.setTo(this.game.camera.x+this.worldTransform[2],this.game.camera.y+this.worldTransform[5]),(this.worldTransform[1]!=this._cache.i01||this.worldTransform[3]!=this._cache.i10||this.worldTransform[0]!=this._cache.a00||this.worldTransform[41]!=this._cache.a11)&&(this._cache.a00=this.worldTransform[0],this._cache.a01=this.worldTransform[1],this._cache.a10=this.worldTransform[3],this._cache.a11=this.worldTransform[4],this._cache.i01=this.worldTransform[1],this._cache.i10=this.worldTransform[3],this._cache.scaleX=Math.sqrt(this._cache.a00*this._cache.a00+this._cache.a01*this._cache.a01),this._cache.scaleY=Math.sqrt(this._cache.a10*this._cache.a10+this._cache.a11*this._cache.a11),this._cache.a01*=-1,this._cache.a10*=-1,this._cache.id=1/(this._cache.a00*this._cache.a11+this._cache.a01*-this._cache.a10),this._cache.idi=1/(this._cache.a00*this._cache.a11+this._cache.i01*-this._cache.i10),this._cache.dirty=!0),this._cache.a02=this.worldTransform[2],this._cache.a12=this.worldTransform[5]},c.Sprite.prototype.updateAnimation=function(){(this.animations.update()||this.currentFrame&&this.currentFrame.uuid!=this._cache.frameID)&&(this._cache.frameID=this.currentFrame.uuid,this._cache.frameWidth=this.texture.frame.width,this._cache.frameHeight=this.texture.frame.height,this._cache.width=this.currentFrame.width,this._cache.height=this.currentFrame.height,this._cache.halfWidth=Math.floor(this._cache.width/2),this._cache.halfHeight=Math.floor(this._cache.height/2),this._cache.dirty=!0)},c.Sprite.prototype.updateCrop=function(){!this.cropEnabled||this.crop.width==this._cache.cropWidth&&this.crop.height==this._cache.cropHeight&&this.crop.x==this._cache.cropX&&this.crop.y==this._cache.cropY||(this.crop.floorAll(),this._cache.cropX=this.crop.x,this._cache.cropY=this.crop.y,this._cache.cropWidth=this.crop.width,this._cache.cropHeight=this.crop.height,this.texture.frame=this.crop,this.texture.width=this.crop.width,this.texture.height=this.crop.height,this.texture.updateFrame=!0,b.Texture.frameUpdates.push(this.texture))},c.Sprite.prototype.updateBounds=function(){this.offset.setTo(this._cache.a02-this.anchor.x*this.width,this._cache.a12-this.anchor.y*this.height),this.getLocalPosition(this.center,this.offset.x+this.width/2,this.offset.y+this.height/2),this.getLocalPosition(this.topLeft,this.offset.x,this.offset.y),this.getLocalPosition(this.topRight,this.offset.x+this.width,this.offset.y),this.getLocalPosition(this.bottomLeft,this.offset.x,this.offset.y+this.height),this.getLocalPosition(this.bottomRight,this.offset.x+this.width,this.offset.y+this.height),this._cache.left=c.Math.min(this.topLeft.x,this.topRight.x,this.bottomLeft.x,this.bottomRight.x),this._cache.right=c.Math.max(this.topLeft.x,this.topRight.x,this.bottomLeft.x,this.bottomRight.x),this._cache.top=c.Math.min(this.topLeft.y,this.topRight.y,this.bottomLeft.y,this.bottomRight.y),this._cache.bottom=c.Math.max(this.topLeft.y,this.topRight.y,this.bottomLeft.y,this.bottomRight.y),this.bounds.setTo(this._cache.left,this._cache.top,this._cache.right-this._cache.left,this._cache.bottom-this._cache.top),this.updateFrame=!0,this.inWorld===!1?(this.inWorld=c.Rectangle.intersects(this.bounds,this.game.world.bounds,this.inWorldThreshold),this.inWorld&&(this._outOfBoundsFired=!1)):(this.inWorld=c.Rectangle.intersects(this.bounds,this.game.world.bounds,this.inWorldThreshold),this.inWorld===!1&&(this.events.onOutOfBounds.dispatch(this),this._outOfBoundsFired=!0,this.outOfBoundsKill&&this.kill())),this._cache.cameraVisible=c.Rectangle.intersects(this.game.world.camera.screenView,this.bounds,0),this.autoCull&&(this.renderable=this._cache.cameraVisible)},c.Sprite.prototype.getLocalPosition=function(a,b,c){return a.x=(this._cache.a11*this._cache.id*b+-this._cache.a01*this._cache.id*c+(this._cache.a12*this._cache.a01-this._cache.a02*this._cache.a11)*this._cache.id)*this.scale.x+this._cache.a02,a.y=(this._cache.a00*this._cache.id*c+-this._cache.a10*this._cache.id*b+(-this._cache.a12*this._cache.a00+this._cache.a02*this._cache.a10)*this._cache.id)*this.scale.y+this._cache.a12,a},c.Sprite.prototype.getLocalUnmodifiedPosition=function(a,b,c){return a.x=this._cache.a11*this._cache.idi*b+-this._cache.i01*this._cache.idi*c+(this._cache.a12*this._cache.i01-this._cache.a02*this._cache.a11)*this._cache.idi+this.anchor.x*this._cache.width,a.y=this._cache.a00*this._cache.idi*c+-this._cache.i10*this._cache.idi*b+(-this._cache.a12*this._cache.a00+this._cache.a02*this._cache.i10)*this._cache.idi+this.anchor.y*this._cache.height,a},c.Sprite.prototype.resetCrop=function(){this.crop=new c.Rectangle(0,0,this._cache.width,this._cache.height),this.texture.setFrame(this.crop),this.cropEnabled=!1},c.Sprite.prototype.postUpdate=function(){this.key instanceof c.BitmapData&&this.key._dirty&&this.key.render(),this.exists&&(this.body&&this.body.postUpdate(),this.fixedToCamera?(this._cache.x=this.game.camera.view.x+this.cameraOffset.x,this._cache.y=this.game.camera.view.y+this.cameraOffset.y):(this._cache.x=this.x,this._cache.y=this.y),this.position.x=this._cache.x,this.position.y=this._cache.y)},c.Sprite.prototype.loadTexture=function(a,d){this.key=a,a instanceof c.RenderTexture?this.currentFrame=this.game.cache.getTextureFrame(a.name):a instanceof c.BitmapData?(this.setTexture(a.texture),this.currentFrame=a.textureFrame):a instanceof b.Texture?this.currentFrame=d:(("undefined"==typeof a||this.game.cache.checkImageKey(a)===!1)&&(a="__default",this.key=a),this.game.cache.isSpriteSheet(a)?(this.animations.loadFrameData(this.game.cache.getFrameData(a)),"undefined"!=typeof d&&("string"==typeof d?this.frameName=d:this.frame=d)):(this.currentFrame=this.game.cache.getFrame(a),this.setTexture(b.TextureCache[a])))},c.Sprite.prototype.centerOn=function(a,b){return this.fixedToCamera?(this.cameraOffset.x=a+(this.cameraOffset.x-this.center.x),this.cameraOffset.y=b+(this.cameraOffset.y-this.center.y)):(this.x=a+(this.x-this.center.x),this.y=b+(this.y-this.center.y)),this},c.Sprite.prototype.revive=function(a){return"undefined"==typeof a&&(a=1),this.alive=!0,this.exists=!0,this.visible=!0,this.health=a,this.events&&this.events.onRevived.dispatch(this),this},c.Sprite.prototype.kill=function(){return this.alive=!1,this.exists=!1,this.visible=!1,this.events&&this.events.onKilled.dispatch(this),this},c.Sprite.prototype.destroy=function(){this.filters&&(this.filters=null),this.group&&this.group.remove(this),this.input&&this.input.destroy(),this.events&&this.events.destroy(),this.animations&&this.animations.destroy(),this.body&&this.body.destroy(),this.alive=!1,this.exists=!1,this.visible=!1,this.game=null},c.Sprite.prototype.damage=function(a){return this.alive&&(this.health-=a,this.health<0&&this.kill()),this},c.Sprite.prototype.reset=function(a,b,c){return"undefined"==typeof c&&(c=1),this.x=a,this.y=b,this.world.setTo(a,b),this.position.x=this.x,this.position.y=this.y,this.alive=!0,this.exists=!0,this.visible=!0,this.renderable=!0,this._outOfBoundsFired=!1,this.health=c,this.body&&this.body.reset(!1),this},c.Sprite.prototype.bringToTop=function(){return this.group?this.group.bringToTop(this):this.game.world.bringToTop(this),this},c.Sprite.prototype.play=function(a,b,c,d){return this.animations?this.animations.play(a,b,c,d):void 0},Object.defineProperty(c.Sprite.prototype,"deltaX",{get:function(){return this.world.x-this._cache.prevX}}),Object.defineProperty(c.Sprite.prototype,"deltaY",{get:function(){return this.world.y-this._cache.prevY}}),Object.defineProperty(c.Sprite.prototype,"angle",{get:function(){return c.Math.wrapAngle(c.Math.radToDeg(this.rotation))},set:function(a){this.rotation=c.Math.degToRad(c.Math.wrapAngle(a))}}),Object.defineProperty(c.Sprite.prototype,"frame",{get:function(){return this.animations.frame},set:function(a){this.animations.frame=a}}),Object.defineProperty(c.Sprite.prototype,"frameName",{get:function(){return this.animations.frameName},set:function(a){this.animations.frameName=a}}),Object.defineProperty(c.Sprite.prototype,"inCamera",{get:function(){return this._cache.cameraVisible}}),Object.defineProperty(c.Sprite.prototype,"worldCenterX",{get:function(){return this.game.camera.x+this.center.x}}),Object.defineProperty(c.Sprite.prototype,"worldCenterY",{get:function(){return this.game.camera.y+this.center.y}}),Object.defineProperty(c.Sprite.prototype,"width",{get:function(){return this.scale.x*this.currentFrame.width},set:function(a){this.scale.x=a/this.currentFrame.width,this._cache.scaleX=a/this.currentFrame.width,this._width=a}}),Object.defineProperty(c.Sprite.prototype,"height",{get:function(){return this.scale.y*this.currentFrame.height},set:function(a){this.scale.y=a/this.currentFrame.height,this._cache.scaleY=a/this.currentFrame.height,this._height=a}}),Object.defineProperty(c.Sprite.prototype,"inputEnabled",{get:function(){return this.input.enabled},set:function(a){a?this.input.enabled===!1&&this.input.start():this.input.enabled&&this.input.stop()}}),c.TileSprite=function(a,d,e,f,g,h){d=d||0,e=e||0,f=f||256,g=g||256,h=h||null,c.Sprite.call(this,a,d,e,h),this.texture=b.TextureCache[h],b.TilingSprite.call(this,this.texture,f,g),this.type=c.TILESPRITE,this.tileScale=new c.Point(1,1),this.tilePosition=new c.Point(0,0),this.body.width=f,this.body.height=g
-},c.TileSprite.prototype=c.Utils.extend(!0,b.TilingSprite.prototype,c.Sprite.prototype),c.TileSprite.prototype.constructor=c.TileSprite,Object.defineProperty(c.TileSprite.prototype,"angle",{get:function(){return c.Math.wrapAngle(c.Math.radToDeg(this.rotation))},set:function(a){this.rotation=c.Math.degToRad(c.Math.wrapAngle(a))}}),Object.defineProperty(c.TileSprite.prototype,"frame",{get:function(){return this.animations.frame},set:function(a){this.animations.frame=a}}),Object.defineProperty(c.TileSprite.prototype,"frameName",{get:function(){return this.animations.frameName},set:function(a){this.animations.frameName=a}}),Object.defineProperty(c.TileSprite.prototype,"inCamera",{get:function(){return this._cache.cameraVisible}}),Object.defineProperty(c.TileSprite.prototype,"inputEnabled",{get:function(){return this.input.enabled},set:function(a){a?this.input.enabled===!1&&this.input.start():this.input.enabled&&this.input.stop()}}),c.Text=function(a,d,e,f,g){d=d||0,e=e||0,f=f||"",g=g||"",this.game=a,this.exists=!0,this.alive=!0,this.group=null,this.name="",this.type=c.TEXT,this._text=f,this._style=g,b.Text.call(this,f,g),this.position.x=this.x=d,this.position.y=this.y=e,this.anchor=new c.Point,this.scale=new c.Point(1,1),this.fixedToCamera=!1,this.cameraOffset=new c.Point(d,e),this._cache={dirty:!1,a00:1,a01:0,a02:d,a10:0,a11:1,a12:e,id:1,x:-1,y:-1,scaleX:1,scaleY:1},this._cache.x=this.x,this._cache.y=this.y,this.renderable=!0},c.Text.prototype=Object.create(b.Text.prototype),c.Text.prototype.constructor=c.Text,c.Text.prototype.update=function(){this.exists&&(this.fixedToCamera&&(this.x=this.game.camera.view.x+this.cameraOffset.x,this.y=this.game.camera.view.y+this.cameraOffset.y),this._cache.dirty=!1,this._cache.x=this.x,this._cache.y=this.y,(this.position.x!=this._cache.x||this.position.y!=this._cache.y)&&(this.position.x=this._cache.x,this.position.y=this._cache.y,this._cache.dirty=!0))},c.Text.prototype.destroy=function(){this.group&&this.group.remove(this),this.canvas.parentNode?this.canvas.parentNode.removeChild(this.canvas):(this.canvas=null,this.context=null),this.exists=!1,this.group=null},Object.defineProperty(c.Text.prototype,"angle",{get:function(){return c.Math.radToDeg(this.rotation)},set:function(a){this.rotation=c.Math.degToRad(a)}}),Object.defineProperty(c.Text.prototype,"x",{get:function(){return this.position.x},set:function(a){this.position.x=a}}),Object.defineProperty(c.Text.prototype,"y",{get:function(){return this.position.y},set:function(a){this.position.y=a}}),Object.defineProperty(c.Text.prototype,"content",{get:function(){return this._text},set:function(a){a!==this._text&&(this._text=a,this.setText(a))}}),Object.defineProperty(c.Text.prototype,"font",{get:function(){return this._style},set:function(a){a!==this._style&&(this._style=a,this.setStyle(a))}}),c.BitmapText=function(a,d,e,f,g){d=d||0,e=e||0,f=f||"",g=g||"",this.game=a,this.exists=!0,this.alive=!0,this.group=null,this.name="",this.type=c.BITMAPTEXT,b.BitmapText.call(this,f,g),this.position.x=d,this.position.y=e,this.anchor=new c.Point,this.scale=new c.Point(1,1),this._cache={dirty:!1,a00:1,a01:0,a02:d,a10:0,a11:1,a12:e,id:1,x:-1,y:-1,scaleX:1,scaleY:1},this._cache.x=this.x,this._cache.y=this.y},c.BitmapText.prototype=Object.create(b.BitmapText.prototype),c.BitmapText.prototype.constructor=c.BitmapText,c.BitmapText.prototype.update=function(){this.exists&&(this._cache.dirty=!1,this._cache.x=this.x,this._cache.y=this.y,(this.position.x!=this._cache.x||this.position.y!=this._cache.y)&&(this.position.x=this._cache.x,this.position.y=this._cache.y,this._cache.dirty=!0),this.pivot.x=this.anchor.x*this.width,this.pivot.y=this.anchor.y*this.height)},c.BitmapText.prototype.destroy=function(){this.group&&this.group.remove(this),this.canvas&&this.canvas.parentNode?this.canvas.parentNode.removeChild(this.canvas):(this.canvas=null,this.context=null),this.exists=!1,this.group=null},Object.defineProperty(c.BitmapText.prototype,"angle",{get:function(){return c.Math.radToDeg(this.rotation)},set:function(a){this.rotation=c.Math.degToRad(a)}}),Object.defineProperty(c.BitmapText.prototype,"x",{get:function(){return this.position.x},set:function(a){this.position.x=a}}),Object.defineProperty(c.BitmapText.prototype,"y",{get:function(){return this.position.y},set:function(a){this.position.y=a}}),c.Button=function(a,b,d,e,f,g,h,i,j,k){b=b||0,d=d||0,e=e||null,f=f||null,g=g||this,c.Sprite.call(this,a,b,d,e,i),this.type=c.BUTTON,this._onOverFrameName=null,this._onOutFrameName=null,this._onDownFrameName=null,this._onUpFrameName=null,this._onOverFrameID=null,this._onOutFrameID=null,this._onDownFrameID=null,this._onUpFrameID=null,this.onOverSound=null,this.onOutSound=null,this.onDownSound=null,this.onUpSound=null,this.onOverSoundMarker="",this.onOutSoundMarker="",this.onDownSoundMarker="",this.onUpSoundMarker="",this.onInputOver=new c.Signal,this.onInputOut=new c.Signal,this.onInputDown=new c.Signal,this.onInputUp=new c.Signal,this.freezeFrames=!1,this.forceOut=!1,this.setFrames(h,i,j,k),null!==f&&this.onInputUp.add(f,g),this.input.start(0,!0),this.events.onInputOver.add(this.onInputOverHandler,this),this.events.onInputOut.add(this.onInputOutHandler,this),this.events.onInputDown.add(this.onInputDownHandler,this),this.events.onInputUp.add(this.onInputUpHandler,this)},c.Button.prototype=Object.create(c.Sprite.prototype),c.Button.prototype=c.Utils.extend(!0,c.Button.prototype,c.Sprite.prototype,b.Sprite.prototype),c.Button.prototype.constructor=c.Button,c.Button.prototype.clearFrames=function(){this._onOverFrameName=null,this._onOverFrameID=null,this._onOutFrameName=null,this._onOutFrameID=null,this._onDownFrameName=null,this._onDownFrameID=null,this._onUpFrameName=null,this._onUpFrameID=null},c.Button.prototype.setFrames=function(a,b,c,d){this.clearFrames(),null!==a&&("string"==typeof a?(this._onOverFrameName=a,this.input.pointerOver()&&(this.frameName=a)):(this._onOverFrameID=a,this.input.pointerOver()&&(this.frame=a))),null!==b&&("string"==typeof b?(this._onOutFrameName=b,this.input.pointerOver()===!1&&(this.frameName=b)):(this._onOutFrameID=b,this.input.pointerOver()===!1&&(this.frame=b))),null!==c&&("string"==typeof c?(this._onDownFrameName=c,this.input.pointerDown()&&(this.frameName=c)):(this._onDownFrameID=c,this.input.pointerDown()&&(this.frame=c))),null!==d&&("string"==typeof d?(this._onUpFrameName=d,this.input.pointerUp()&&(this.frameName=d)):(this._onUpFrameID=d,this.input.pointerUp()&&(this.frame=d)))},c.Button.prototype.setSounds=function(a,b,c,d,e,f,g,h){this.setOverSound(a,b),this.setOutSound(e,f),this.setDownSound(c,d),this.setUpSound(g,h)},c.Button.prototype.setOverSound=function(a,b){this.onOverSound=null,this.onOverSoundMarker="",a instanceof c.Sound&&(this.onOverSound=a),"string"==typeof b&&(this.onOverSoundMarker=b)},c.Button.prototype.setOutSound=function(a,b){this.onOutSound=null,this.onOutSoundMarker="",a instanceof c.Sound&&(this.onOutSound=a),"string"==typeof b&&(this.onOutSoundMarker=b)},c.Button.prototype.setDownSound=function(a,b){this.onDownSound=null,this.onDownSoundMarker="",a instanceof c.Sound&&(this.onDownSound=a),"string"==typeof b&&(this.onDownSoundMarker=b)},c.Button.prototype.setUpSound=function(a,b){this.onUpSound=null,this.onUpSoundMarker="",a instanceof c.Sound&&(this.onUpSound=a),"string"==typeof b&&(this.onUpSoundMarker=b)},c.Button.prototype.onInputOverHandler=function(a,b){this.freezeFrames===!1&&this.setState(1),this.onOverSound&&this.onOverSound.play(this.onOverSoundMarker),this.onInputOver&&this.onInputOver.dispatch(this,b)},c.Button.prototype.onInputOutHandler=function(a,b){this.freezeFrames===!1&&this.setState(2),this.onOutSound&&this.onOutSound.play(this.onOutSoundMarker),this.onInputOut&&this.onInputOut.dispatch(this,b)},c.Button.prototype.onInputDownHandler=function(a,b){this.freezeFrames===!1&&this.setState(3),this.onDownSound&&this.onDownSound.play(this.onDownSoundMarker),this.onInputDown&&this.onInputDown.dispatch(this,b)},c.Button.prototype.onInputUpHandler=function(a,b,c){this.onUpSound&&this.onUpSound.play(this.onUpSoundMarker),this.onInputUp&&this.onInputUp.dispatch(this,b,c),this.freezeFrames||(this.forceOut?this.setState(2):this._onUpFrameName||this._onUpFrameID?this.setState(4):c?this.setState(1):this.setState(2))},c.Button.prototype.setState=function(a){1===a?null!=this._onOverFrameName?this.frameName=this._onOverFrameName:null!=this._onOverFrameID&&(this.frame=this._onOverFrameID):2===a?null!=this._onOutFrameName?this.frameName=this._onOutFrameName:null!=this._onOutFrameID&&(this.frame=this._onOutFrameID):3===a?null!=this._onDownFrameName?this.frameName=this._onDownFrameName:null!=this._onDownFrameID&&(this.frame=this._onDownFrameID):4===a&&(null!=this._onUpFrameName?this.frameName=this._onUpFrameName:null!=this._onUpFrameID&&(this.frame=this._onUpFrameID))},c.Graphics=function(a,d,e){this.game=a,b.Graphics.call(this),this.type=c.GRAPHICS,this.position.x=d,this.position.y=e},c.Graphics.prototype=Object.create(b.Graphics.prototype),c.Graphics.prototype.constructor=c.Graphics,c.Graphics.prototype.destroy=function(){this.clear(),this.group&&this.group.remove(this),this.game=null},c.Graphics.prototype.drawPolygon=function(a){this.moveTo(a.points[0].x,a.points[0].y);for(var b=1;bh;h++)f[h].updateTransform();var j=a.__renderGroup;j?a==j.root?j.render(this.projection,this.glFramebuffer):j.renderSpecific(a,this.projection,this.glFramebuffer):(this.renderGroup||(this.renderGroup=new b.WebGLRenderGroup(e)),this.renderGroup.setRenderable(a),this.renderGroup.render(this.projection,this.glFramebuffer)),a.worldTransform=g},c.RenderTexture.prototype.renderCanvas=function(a,c,d,e){var f=a.children;a.worldTransform=b.mat3.create(),c&&(a.worldTransform[2]=c.x,a.worldTransform[5]=c.y);for(var g=0,h=f.length;h>g;g++)f[g].updateTransform();d&&this.renderer.context.clearRect(0,0,this.width,this.height),this.renderer.renderDisplayObject(a,e),this.renderer.context.setTransform(1,0,0,1,0,0)},c.Canvas={create:function(a,b,c){a=a||256,b=b||256;var d=document.createElement("canvas");return"string"==typeof c&&(d.id=c),d.width=a,d.height=b,d.style.display="block",d},getOffset:function(a,b){b=b||new c.Point;var d=a.getBoundingClientRect(),e=a.clientTop||document.body.clientTop||0,f=a.clientLeft||document.body.clientLeft||0,g=0,h=0;return"CSS1Compat"===document.compatMode?(g=window.pageYOffset||document.documentElement.scrollTop||a.scrollTop||0,h=window.pageXOffset||document.documentElement.scrollLeft||a.scrollLeft||0):(g=window.pageYOffset||document.body.scrollTop||a.scrollTop||0,h=window.pageXOffset||document.body.scrollLeft||a.scrollLeft||0),b.x=d.left+h-f,b.y=d.top+g-e,b},getAspectRatio:function(a){return a.width/a.height},setBackgroundColor:function(a,b){return b=b||"rgb(0,0,0)",a.style.backgroundColor=b,a},setTouchAction:function(a,b){return b=b||"none",a.style.msTouchAction=b,a.style["ms-touch-action"]=b,a.style["touch-action"]=b,a},setUserSelect:function(a,b){return b=b||"none",a.style["-webkit-touch-callout"]=b,a.style["-webkit-user-select"]=b,a.style["-khtml-user-select"]=b,a.style["-moz-user-select"]=b,a.style["-ms-user-select"]=b,a.style["user-select"]=b,a.style["-webkit-tap-highlight-color"]="rgba(0, 0, 0, 0)",a},addToDOM:function(a,b,c){var d;return"undefined"==typeof c&&(c=!0),b&&("string"==typeof b?d=document.getElementById(b):"object"==typeof b&&1===b.nodeType&&(d=b)),d||(d=document.body),c&&d.style&&(d.style.overflow="hidden"),d.appendChild(a),a},setTransform:function(a,b,c,d,e,f,g){return a.setTransform(d,f,g,e,b,c),a},setSmoothingEnabled:function(a,b){return a.imageSmoothingEnabled=b,a.mozImageSmoothingEnabled=b,a.oImageSmoothingEnabled=b,a.webkitImageSmoothingEnabled=b,a.msImageSmoothingEnabled=b,a},setImageRenderingCrisp:function(a){return a.style["image-rendering"]="optimizeSpeed",a.style["image-rendering"]="crisp-edges",a.style["image-rendering"]="-moz-crisp-edges",a.style["image-rendering"]="-webkit-optimize-contrast",a.style["image-rendering"]="optimize-contrast",a.style.msInterpolationMode="nearest-neighbor",a},setImageRenderingBicubic:function(a){return a.style["image-rendering"]="auto",a.style.msInterpolationMode="bicubic",a}},c.StageScaleMode=function(a,b,d){this.game=a,this.width=b,this.height=d,this.minWidth=null,this.maxWidth=null,this.minHeight=null,this.maxHeight=null,this._startHeight=0,this.forceLandscape=!1,this.forcePortrait=!1,this.incorrectOrientation=!1,this.pageAlignHorizontally=!1,this.pageAlignVertically=!1,this._width=0,this._height=0,this.maxIterations=5,this.orientationSprite=null,this.enterLandscape=new c.Signal,this.enterPortrait=new c.Signal,this.enterIncorrectOrientation=new c.Signal,this.leaveIncorrectOrientation=new c.Signal,this.hasResized=new c.Signal,this.orientation=window.orientation?window.orientation:window.outerWidth>window.outerHeight?90:0,this.scaleFactor=new c.Point(1,1),this.scaleFactorInversed=new c.Point(1,1),this.margin=new c.Point(0,0),this.aspectRatio=0,this.event=null;var e=this;window.addEventListener("orientationchange",function(a){return e.checkOrientation(a)},!1),window.addEventListener("resize",function(a){return e.checkResize(a)},!1),document.addEventListener("webkitfullscreenchange",function(a){return e.fullScreenChange(a)},!1),document.addEventListener("mozfullscreenchange",function(a){return e.fullScreenChange(a)},!1),document.addEventListener("fullscreenchange",function(a){return e.fullScreenChange(a)},!1)},c.StageScaleMode.EXACT_FIT=0,c.StageScaleMode.NO_SCALE=1,c.StageScaleMode.SHOW_ALL=2,c.StageScaleMode.prototype={startFullScreen:function(a){if(!this.isFullScreen){"undefined"!=typeof a&&c.Canvas.setSmoothingEnabled(this.game.context,a);var b=this.game.canvas;this._width=this.width,this._height=this.height,b.requestFullScreen?b.requestFullScreen():b.mozRequestFullScreen?b.parentNode.mozRequestFullScreen():b.webkitRequestFullScreen&&b.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)}},stopFullScreen:function(){document.cancelFullScreen?document.cancelFullScreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitCancelFullScreen&&document.webkitCancelFullScreen()},fullScreenChange:function(a){this.event=a,this.isFullScreen?this.game.stage.fullScreenScaleMode===c.StageScaleMode.EXACT_FIT?(this.game.stage.canvas.style.width="100%",this.game.stage.canvas.style.height="100%",this.setMaximum(),this.game.input.scale.setTo(this.game.width/this.width,this.game.height/this.height),this.aspectRatio=this.width/this.height,this.scaleFactor.x=this.game.width/this.width,this.scaleFactor.y=this.game.height/this.height):this.game.stage.fullScreenScaleMode===c.StageScaleMode.SHOW_ALL&&(this.game.stage.scale.setShowAll(),this.game.stage.scale.refresh()):(this.game.stage.canvas.style.width=this.game.width+"px",this.game.stage.canvas.style.height=this.game.height+"px",this.width=this._width,this.height=this._height,this.game.input.scale.setTo(this.game.width/this.width,this.game.height/this.height),this.aspectRatio=this.width/this.height,this.scaleFactor.x=this.game.width/this.width,this.scaleFactor.y=this.game.height/this.height)},forceOrientation:function(a,c,d){"undefined"==typeof c&&(c=!1),this.forceLandscape=a,this.forcePortrait=c,"undefined"!=typeof d&&((null==d||this.game.cache.checkImageKey(d)===!1)&&(d="__default"),this.orientationSprite=new b.Sprite(b.TextureCache[d]),this.orientationSprite.anchor.x=.5,this.orientationSprite.anchor.y=.5,this.orientationSprite.position.x=this.game.width/2,this.orientationSprite.position.y=this.game.height/2,this.checkOrientationState(),this.incorrectOrientation?(this.orientationSprite.visible=!0,this.game.world.visible=!1):(this.orientationSprite.visible=!1,this.game.world.visible=!0),this.game.stage._stage.addChild(this.orientationSprite))},checkOrientationState:function(){this.incorrectOrientation?(this.forceLandscape&&window.innerWidth>window.innerHeight||this.forcePortrait&&window.innerHeight>window.innerWidth)&&(this.game.paused=!1,this.incorrectOrientation=!1,this.leaveIncorrectOrientation.dispatch(),this.orientationSprite&&(this.orientationSprite.visible=!1,this.game.world.visible=!0),this.refresh()):(this.forceLandscape&&window.innerWidthwindow.outerHeight?90:0,this.isLandscape?this.enterLandscape.dispatch(this.orientation,!0,!1):this.enterPortrait.dispatch(this.orientation,!1,!0),this.game.stage.scaleMode!==c.StageScaleMode.NO_SCALE&&this.refresh(),this.checkOrientationState()},refresh:function(){if(this.game.device.iPad===!1&&this.game.device.webApp===!1&&this.game.device.desktop===!1&&(this.game.device.android&&this.game.device.chrome===!1?window.scrollTo(0,1):window.scrollTo(0,0)),null==this._check&&this.maxIterations>0){this._iterations=this.maxIterations;var a=this;this._check=window.setInterval(function(){return a.setScreenSize()},10),this.setScreenSize()}},setScreenSize:function(a){"undefined"==typeof a&&(a=!1),this.game.device.iPad===!1&&this.game.device.webApp===!1&&this.game.device.desktop===!1&&(this.game.device.android&&this.game.device.chrome===!1?window.scrollTo(0,1):window.scrollTo(0,0)),this._iterations--,(a||window.innerHeight>this._startHeight||this._iterations<0)&&(document.documentElement.style.minHeight=window.innerHeight+"px",this.incorrectOrientation===!0?this.setMaximum():this.isFullScreen?this.game.stage.fullScreenScaleMode==c.StageScaleMode.EXACT_FIT?this.setExactFit():this.game.stage.fullScreenScaleMode==c.StageScaleMode.SHOW_ALL&&this.setShowAll():this.game.stage.scaleMode==c.StageScaleMode.EXACT_FIT?this.setExactFit():this.game.stage.scaleMode==c.StageScaleMode.SHOW_ALL&&this.setShowAll(),this.setSize(),clearInterval(this._check),this._check=null)},setSize:function(){this.incorrectOrientation===!1&&(this.maxWidth&&this.width>this.maxWidth&&(this.width=this.maxWidth),this.maxHeight&&this.height>this.maxHeight&&(this.height=this.maxHeight),this.minWidth&&this.widththis.maxWidth?this.maxWidth:a,this.height=this.maxHeight&&b>this.maxHeight?this.maxHeight:b}},c.StageScaleMode.prototype.constructor=c.StageScaleMode,Object.defineProperty(c.StageScaleMode.prototype,"isFullScreen",{get:function(){return document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement}}),Object.defineProperty(c.StageScaleMode.prototype,"isPortrait",{get:function(){return 0===this.orientation||180==this.orientation}}),Object.defineProperty(c.StageScaleMode.prototype,"isLandscape",{get:function(){return 90===this.orientation||-90===this.orientation}}),c.Device=function(){this.patchAndroidClearRectBug=!1,this.desktop=!1,this.iOS=!1,this.cocoonJS=!1,this.ejecta=!1,this.android=!1,this.chromeOS=!1,this.linux=!1,this.macOS=!1,this.windows=!1,this.canvas=!1,this.file=!1,this.fileSystem=!1,this.localStorage=!1,this.webGL=!1,this.worker=!1,this.touch=!1,this.mspointer=!1,this.css3D=!1,this.pointerLock=!1,this.typedArray=!1,this.vibration=!1,this.quirksMode=!1,this.arora=!1,this.chrome=!1,this.epiphany=!1,this.firefox=!1,this.ie=!1,this.ieVersion=0,this.trident=!1,this.tridentVersion=0,this.mobileSafari=!1,this.midori=!1,this.opera=!1,this.safari=!1,this.webApp=!1,this.silk=!1,this.audioData=!1,this.webAudio=!1,this.ogg=!1,this.opus=!1,this.mp3=!1,this.wav=!1,this.m4a=!1,this.webm=!1,this.iPhone=!1,this.iPhone4=!1,this.iPad=!1,this.pixelRatio=0,this.littleEndian=!1,this._checkAudio(),this._checkBrowser(),this._checkCSS3D(),this._checkDevice(),this._checkFeatures(),this._checkOS()},c.Device.prototype={_checkOS:function(){var a=navigator.userAgent;/Android/.test(a)?this.android=!0:/CrOS/.test(a)?this.chromeOS=!0:/iP[ao]d|iPhone/i.test(a)?this.iOS=!0:/Linux/.test(a)?this.linux=!0:/Mac OS/.test(a)?this.macOS=!0:/Windows/.test(a)&&(this.windows=!0),(this.windows||this.macOS||this.linux&&this.silk===!1)&&(this.desktop=!0)},_checkFeatures:function(){this.canvas=!!window.CanvasRenderingContext2D;try{this.localStorage=!!localStorage.getItem}catch(a){this.localStorage=!1}this.file=!!(window.File&&window.FileReader&&window.FileList&&window.Blob),this.fileSystem=!!window.requestFileSystem,this.webGL=function(){try{var a=document.createElement("canvas");return!!window.WebGLRenderingContext&&(a.getContext("webgl")||a.getContext("experimental-webgl"))}catch(b){return!1}}(),this.webGL=null===this.webGL||this.webGL===!1?!1:!0,this.worker=!!window.Worker,("ontouchstart"in document.documentElement||window.navigator.maxTouchPoints&&window.navigator.maxTouchPoints>1)&&(this.touch=!0),(window.navigator.msPointerEnabled||window.navigator.pointerEnabled)&&(this.mspointer=!0),this.pointerLock="pointerLockElement"in document||"mozPointerLockElement"in document||"webkitPointerLockElement"in document,this.quirksMode="CSS1Compat"===document.compatMode?!1:!0},_checkBrowser:function(){var a=navigator.userAgent;/Arora/.test(a)?this.arora=!0:/Chrome/.test(a)?this.chrome=!0:/Epiphany/.test(a)?this.epiphany=!0:/Firefox/.test(a)?this.firefox=!0:/Mobile Safari/.test(a)?this.mobileSafari=!0:/MSIE (\d+\.\d+);/.test(a)?(this.ie=!0,this.ieVersion=parseInt(RegExp.$1,10)):/Midori/.test(a)?this.midori=!0:/Opera/.test(a)?this.opera=!0:/Safari/.test(a)?this.safari=!0:/Silk/.test(a)?this.silk=!0:/Trident\/(\d+\.\d+);/.test(a)&&(this.ie=!0,this.trident=!0,this.tridentVersion=parseInt(RegExp.$1,10)),navigator.standalone&&(this.webApp=!0),navigator.isCocoonJS&&(this.cocoonJS=!0),"undefined"!=typeof window.ejecta&&(this.ejecta=!0)},_checkAudio:function(){this.audioData=!!window.Audio,this.webAudio=!(!window.webkitAudioContext&&!window.AudioContext);var a=document.createElement("audio"),b=!1;try{(b=!!a.canPlayType)&&(a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,"")&&(this.ogg=!0),a.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/,"")&&(this.opus=!0),a.canPlayType("audio/mpeg;").replace(/^no$/,"")&&(this.mp3=!0),a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,"")&&(this.wav=!0),(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;").replace(/^no$/,""))&&(this.m4a=!0),a.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,"")&&(this.webm=!0))}catch(c){}},_checkDevice:function(){this.pixelRatio=window.devicePixelRatio||1,this.iPhone=-1!=navigator.userAgent.toLowerCase().indexOf("iphone"),this.iPhone4=2==this.pixelRatio&&this.iPhone,this.iPad=-1!=navigator.userAgent.toLowerCase().indexOf("ipad"),"undefined"!=typeof Int8Array?(this.littleEndian=new Int8Array(new Int16Array([1]).buffer)[0]>0,this.typedArray=!0):(this.littleEndian=!1,this.typedArray=!1),navigator.vibrate=navigator.vibrate||navigator.webkitVibrate||navigator.mozVibrate||navigator.msVibrate,navigator.vibrate&&(this.vibration=!0)},_checkCSS3D:function(){var a,b=document.createElement("p"),c={webkitTransform:"-webkit-transform",OTransform:"-o-transform",msTransform:"-ms-transform",MozTransform:"-moz-transform",transform:"transform"};document.body.insertBefore(b,null);for(var d in c)void 0!==b.style[d]&&(b.style[d]="translate3d(1px,1px,1px)",a=window.getComputedStyle(b).getPropertyValue(c[d]));document.body.removeChild(b),this.css3D=void 0!==a&&a.length>0&&"none"!==a},canPlayAudio:function(a){return"mp3"==a&&this.mp3?!0:"ogg"==a&&(this.ogg||this.opus)?!0:"m4a"==a&&this.m4a?!0:"wav"==a&&this.wav?!0:"webm"==a&&this.webm?!0:!1},isConsoleOpen:function(){return window.console&&window.console.firebug?!0:window.console?(console.profile(),console.profileEnd(),console.clear&&console.clear(),console.profiles.length>0):!1}},c.Device.prototype.constructor=c.Device,c.RequestAnimationFrame=function(a){this.game=a,this.isRunning=!1;for(var b=["ms","moz","webkit","o"],c=0;c>>0,b-=d,b*=d,d=b>>>0,b-=d,d+=4294967296*b;return 2.3283064365386963e-10*(d>>>0)},integer:function(){return 4294967296*this.rnd.apply(this)},frac:function(){return this.rnd.apply(this)+1.1102230246251565e-16*(0|2097152*this.rnd.apply(this))},real:function(){return this.integer()+this.frac()},integerInRange:function(a,b){return Math.floor(this.realInRange(a,b))},realInRange:function(a,b){return this.frac()*(b-a)+a},normal:function(){return 1-2*this.frac()},uuid:function(){var a="",b="";for(b=a="";a++<36;b+=~a%5|4&3*a?(15^a?8^this.frac()*(20^a?16:4):4).toString(16):"-");return b},pick:function(a){return a[this.integerInRange(0,a.length)]},weightedPick:function(a){return a[~~(Math.pow(this.frac(),2)*a.length)]},timestamp:function(a,b){return this.realInRange(a||9466848e5,b||1577862e6)},angle:function(){return this.integerInRange(-180,180)}},c.RandomDataGenerator.prototype.constructor=c.RandomDataGenerator,c.Math={PI2:2*Math.PI,fuzzyEqual:function(a,b,c){return"undefined"==typeof c&&(c=1e-4),Math.abs(a-b)a},fuzzyGreaterThan:function(a,b,c){return"undefined"==typeof c&&(c=1e-4),a>b-c},fuzzyCeil:function(a,b){return"undefined"==typeof b&&(b=1e-4),Math.ceil(a-b)},fuzzyFloor:function(a,b){return"undefined"==typeof b&&(b=1e-4),Math.floor(a+b)},average:function(){for(var a=[],b=0;b0?Math.floor(a):Math.ceil(a)},shear:function(a){return a%1},snapTo:function(a,b,c){return"undefined"==typeof c&&(c=0),0===b?a:(a-=c,a=b*Math.round(a/b),c+a)},snapToFloor:function(a,b,c){return"undefined"==typeof c&&(c=0),0===b?a:(a-=c,a=b*Math.floor(a/b),c+a)},snapToCeil:function(a,b,c){return"undefined"==typeof c&&(c=0),0===b?a:(a-=c,a=b*Math.ceil(a/b),c+a)},snapToInArray:function(a,b,c){if("undefined"==typeof c&&(c=!0),c&&b.sort(),a=f-a?f:e},roundTo:function(a,b,c){"undefined"==typeof b&&(b=0),"undefined"==typeof c&&(c=10);var d=Math.pow(c,-b);return Math.round(a*d)/d},floorTo:function(a,b,c){"undefined"==typeof b&&(b=0),"undefined"==typeof c&&(c=10);var d=Math.pow(c,-b);return Math.floor(a*d)/d},ceilTo:function(a,b,c){"undefined"==typeof b&&(b=0),"undefined"==typeof c&&(c=10);var d=Math.pow(c,-b);return Math.ceil(a*d)/d},interpolateFloat:function(a,b,c){return(b-a)*c+a},angleBetween:function(a,b,c,d){return Math.atan2(d-b,c-a)},reverseAngle:function(a){return this.normalizeAngle(a+Math.PI,!0)},normalizeAngle:function(a){return a%=2*Math.PI,a>=0?a:a+2*Math.PI},normalizeLatitude:function(a){return Math.max(-90,Math.min(90,a))},normalizeLongitude:function(a){return 180==a%360?180:(a%=360,-180>a?a+360:a>180?a-360:a)},nearestAngleBetween:function(a,b,c){"undefined"==typeof c&&(c=!0);var d=c?Math.PI:180;return a=this.normalizeAngle(a,c),b=this.normalizeAngle(b,c),-d/2>a&&b>d/2&&(a+=2*d),-d/2>b&&a>d/2&&(b+=2*d),b-a},interpolateAngles:function(a,b,c,d,e){return"undefined"==typeof d&&(d=!0),"undefined"==typeof e&&(e=null),a=this.normalizeAngle(a,d),b=this.normalizeAngleToAnother(b,a,d),"function"==typeof e?e(c,a,b-a,1):this.interpolateFloat(a,b,c)},chanceRoll:function(a){return"undefined"==typeof a&&(a=50),0>=a?!1:a>=100?!0:100*Math.random()>=a?!1:!0},numberArray:function(a,b){for(var c=[],d=a;b>=d;d++)c.push(d);return c},maxAdd:function(a,b,c){return a+=b,a>c&&(a=c),a},minSub:function(a,b,c){return a-=b,c>a&&(a=c),a},wrap:function(a,b,c){var d=c-b;if(0>=d)return 0;var e=(a-b)%d;return 0>e&&(e+=d),e+b},wrapValue:function(a,b,c){var d;return a=Math.abs(a),b=Math.abs(b),c=Math.abs(c),d=(a+b)%c},randomSign:function(){return Math.random()>.5?1:-1},isOdd:function(a){return 1&a},isEven:function(a){return 1&a?!1:!0},max:function(){for(var a=1,b=0,c=arguments.length;c>a;a++)arguments[b]b;b++)a[b]b;b++)a[b]>a[c]&&(c=b);return a[c]},minProperty:function(a){if(2===arguments.length&&"object"==typeof arguments[1])var b=arguments[1];else var b=arguments.slice(1);for(var c=1,d=0,e=b.length;e>c;c++)b[c][a]c;c++)b[c][a]>b[d][a]&&(d=c);return b[d][a]},wrapAngle:function(a){return this.wrap(a,-180,180)},angleLimit:function(a,b,c){var d=a;return a>c?d=c:b>a&&(d=b),d},linearInterpolation:function(a,b){var c=a.length-1,d=c*b,e=Math.floor(d);return 0>b?this.linear(a[0],a[1],d):b>1?this.linear(a[c],a[c-1],c-d):this.linear(a[e],a[e+1>c?c:e+1],d-e)},bezierInterpolation:function(a,b){for(var c=0,d=a.length-1,e=0;d>=e;e++)c+=Math.pow(1-b,d-e)*Math.pow(b,e)*a[e]*this.bernstein(d,e);return c},catmullRomInterpolation:function(a,b){var c=a.length-1,d=c*b,e=Math.floor(d);return a[0]===a[c]?(0>b&&(e=Math.floor(d=c*(1+b))),this.catmullRom(a[(e-1+c)%c],a[e],a[(e+1)%c],a[(e+2)%c],d-e)):0>b?a[0]-(this.catmullRom(a[0],a[0],a[1],a[1],-d)-a[0]):b>1?a[c]-(this.catmullRom(a[c],a[c],a[c-1],a[c-1],d-c)-a[c]):this.catmullRom(a[e?e-1:0],a[e],a[e+1>c?c:e+1],a[e+2>c?c:e+2],d-e)},linear:function(a,b,c){return(b-a)*c+a},bernstein:function(a,b){return this.factorial(a)/this.factorial(b)/this.factorial(a-b)},catmullRom:function(a,b,c,d,e){var f=.5*(c-a),g=.5*(d-b),h=e*e,i=e*h;return(2*b-2*c+f+g)*i+(-3*b+3*c-2*f-g)*h+f*e+b},difference:function(a,b){return Math.abs(a-b)},getRandom:function(a,b,c){if("undefined"==typeof b&&(b=0),"undefined"==typeof c&&(c=0),null!=a){var d=c;if((0===d||d>a.length-b)&&(d=a.length-b),d>0)return a[b+Math.floor(Math.random()*d)]}return null},floor:function(a){var b=0|a;return a>0?b:b!=a?b-1:b},ceil:function(a){var b=0|a;return a>0?b!=a?b+1:b:b},sinCosGenerator:function(a,b,c,d){"undefined"==typeof b&&(b=1),"undefined"==typeof c&&(c=1),"undefined"==typeof d&&(d=1);for(var e=b,f=c,g=d*Math.PI/a,h=[],i=[],j=0;a>j;j++)f-=e*g,e+=f*g,h[j]=f,i[j]=e;return{sin:i,cos:h,length:a}},shift:function(a){var b=a.shift();return a.push(b),b},shuffleArray:function(a){for(var b=a.length-1;b>0;b--){var c=Math.floor(Math.random()*(b+1)),d=a[b];a[b]=a[c],a[c]=d}return a},distance:function(a,b,c,d){var e=a-c,f=b-d;return Math.sqrt(e*e+f*f)},distancePow:function(a,b,c,d,e){return"undefined"==typeof e&&(e=2),Math.sqrt(Math.pow(c-a,e)+Math.pow(d-b,e))},distanceRounded:function(a,b,d,e){return Math.round(c.Math.distance(a,b,d,e))},clamp:function(a,b,c){return b>a?b:a>c?c:a},clampBottom:function(a,b){return b>a?b:a},within:function(a,b,c){return Math.abs(a-b)<=c},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},smoothstep:function(a,b,c){return b>=a?0:a>=c?1:(a=(a-b)/(c-b),a*a*(3-2*a))},smootherstep:function(a,b,c){return b>=a?0:a>=c?1:(a=(a-b)/(c-b),a*a*a*(a*(6*a-15)+10))},sign:function(a){return 0>a?-1:a>0?1:0},degToRad:function(){var a=Math.PI/180;return function(b){return b*a}}(),radToDeg:function(){var a=180/Math.PI;return function(b){return b*a}}()},c.QuadTree=function(a,b,c,d,e,f,g){this.maxObjects=e||10,this.maxLevels=f||4,this.level=g||0,this.bounds={x:Math.round(a),y:Math.round(b),width:c,height:d,subWidth:Math.floor(c/2),subHeight:Math.floor(d/2),right:Math.round(a)+Math.floor(c/2),bottom:Math.round(b)+Math.floor(d/2)},this.objects=[],this.nodes=[]},c.QuadTree.prototype={populate:function(a){a.forEach(this.populateHandler,this,!0)},populateHandler:function(a){a.body&&a.body.checkCollision.none===!1&&a.alive&&this.insert(a.body)},split:function(){this.level++,this.nodes[0]=new c.QuadTree(this.bounds.right,this.bounds.y,this.bounds.subWidth,this.bounds.subHeight,this.maxObjects,this.maxLevels,this.level),this.nodes[1]=new c.QuadTree(this.bounds.x,this.bounds.y,this.bounds.subWidth,this.bounds.subHeight,this.maxObjects,this.maxLevels,this.level),this.nodes[2]=new c.QuadTree(this.bounds.x,this.bounds.bottom,this.bounds.subWidth,this.bounds.subHeight,this.maxObjects,this.maxLevels,this.level),this.nodes[3]=new c.QuadTree(this.bounds.right,this.bounds.bottom,this.bounds.subWidth,this.bounds.subHeight,this.maxObjects,this.maxLevels,this.level)},insert:function(a){var b,c=0;if(null!=this.nodes[0]&&(b=this.getIndex(a),-1!==b))return this.nodes[b].insert(a),void 0;if(this.objects.push(a),this.objects.length>this.maxObjects&&this.levelthis.bounds.bottom&&(b=2):a.x>this.bounds.right&&(a.ythis.bounds.bottom&&(b=3)),b},retrieve:function(a){var b=this.objects;return a.body.quadTreeIndex=this.getIndex(a.body),this.nodes[0]&&(-1!==a.body.quadTreeIndex?b=b.concat(this.nodes[a.body.quadTreeIndex].retrieve(a)):(b=b.concat(this.nodes[0].retrieve(a)),b=b.concat(this.nodes[1].retrieve(a)),b=b.concat(this.nodes[2].retrieve(a)),b=b.concat(this.nodes[3].retrieve(a)))),b},clear:function(){this.objects=[];for(var a=0,b=this.nodes.length;b>a;a++)this.nodes[a]&&(this.nodes[a].clear(),delete this.nodes[a])}},c.QuadTree.prototype.constructor=c.QuadTree,c.Circle=function(a,b,c){a=a||0,b=b||0,c=c||0,this.x=a,this.y=b,this._diameter=c,this._radius=c>0?.5*c:0},c.Circle.prototype={circumference:function(){return 2*Math.PI*this._radius},setTo:function(a,b,c){return this.x=a,this.y=b,this._diameter=c,this._radius=.5*c,this},copyFrom:function(a){return this.setTo(a.x,a.y,a.diameter)},copyTo:function(a){return a.x=this.x,a.y=this.y,a.diameter=this._diameter,a},distance:function(a,b){return"undefined"==typeof b&&(b=!1),b?c.Math.distanceRound(this.x,this.y,a.x,a.y):c.Math.distance(this.x,this.y,a.x,a.y)},clone:function(a){return"undefined"==typeof a&&(a=new c.Circle),a.setTo(this.x,this.y,this.diameter)},contains:function(a,b){return c.Circle.contains(this,a,b)},circumferencePoint:function(a,b,d){return c.Circle.circumferencePoint(this,a,b,d)},offset:function(a,b){return this.x+=a,this.y+=b,this},offsetPoint:function(a){return this.offset(a.x,a.y)},toString:function(){return"[{Phaser.Circle (x="+this.x+" y="+this.y+" diameter="+this.diameter+" radius="+this.radius+")}]"}},c.Circle.prototype.constructor=c.Circle,Object.defineProperty(c.Circle.prototype,"diameter",{get:function(){return this._diameter},set:function(a){a>0&&(this._diameter=a,this._radius=.5*a)}}),Object.defineProperty(c.Circle.prototype,"radius",{get:function(){return this._radius},set:function(a){a>0&&(this._radius=a,this._diameter=2*a)}}),Object.defineProperty(c.Circle.prototype,"left",{get:function(){return this.x-this._radius},set:function(a){a>this.x?(this._radius=0,this._diameter=0):this.radius=this.x-a}}),Object.defineProperty(c.Circle.prototype,"right",{get:function(){return this.x+this._radius},set:function(a){athis.y?(this._radius=0,this._diameter=0):this.radius=this.y-a}}),Object.defineProperty(c.Circle.prototype,"bottom",{get:function(){return this.y+this._radius},set:function(a){a0?Math.PI*this._radius*this._radius:0}}),Object.defineProperty(c.Circle.prototype,"empty",{get:function(){return 0===this._diameter},set:function(a){a===!0&&this.setTo(0,0,0)}}),c.Circle.contains=function(a,b,c){if(b>=a.left&&b<=a.right&&c>=a.top&&c<=a.bottom){var d=(a.x-b)*(a.x-b),e=(a.y-c)*(a.y-c);return d+e<=a.radius*a.radius}return!1},c.Circle.equals=function(a,b){return a.x==b.x&&a.y==b.y&&a.diameter==b.diameter},c.Circle.intersects=function(a,b){return c.Math.distance(a.x,a.y,b.x,b.y)<=a.radius+b.radius},c.Circle.circumferencePoint=function(a,b,d,e){return"undefined"==typeof d&&(d=!1),"undefined"==typeof e&&(e=new c.Point),d===!0&&(b=c.Math.radToDeg(b)),e.x=a.x+a.radius*Math.cos(b),e.y=a.y+a.radius*Math.sin(b),e},c.Circle.intersectsRectangle=function(a,b){var c=Math.abs(a.x-b.x-b.halfWidth),d=b.halfWidth+a.radius;if(c>d)return!1;var e=Math.abs(a.y-b.y-b.halfHeight),f=b.halfHeight+a.radius;if(e>f)return!1;if(c<=b.halfWidth||e<=b.halfHeight)return!0;var g=c-b.halfWidth,h=e-b.halfHeight,i=g*g,j=h*h,k=a.radius*a.radius;return k>=i+j},c.Point=function(a,b){a=a||0,b=b||0,this.x=a,this.y=b},c.Point.prototype={copyFrom:function(a){return this.setTo(a.x,a.y)},invert:function(){return this.setTo(this.y,this.x)},setTo:function(a,b){return this.x=a,this.y=b,this},add:function(a,b){return this.x+=a,this.y+=b,this},subtract:function(a,b){return this.x-=a,this.y-=b,this},multiply:function(a,b){return this.x*=a,this.y*=b,this},divide:function(a,b){return this.x/=a,this.y/=b,this},clampX:function(a,b){return this.x=c.Math.clamp(this.x,a,b),this},clampY:function(a,b){return this.y=c.Math.clamp(this.y,a,b),this},clamp:function(a,b){return this.x=c.Math.clamp(this.x,a,b),this.y=c.Math.clamp(this.y,a,b),this},clone:function(a){return"undefined"==typeof a&&(a=new c.Point),a.setTo(this.x,this.y)},copyTo:function(a){return a.x=this.x,a.y=this.y,a},distance:function(a,b){return c.Point.distance(this,a,b)},equals:function(a){return a.x==this.x&&a.y==this.y},rotate:function(a,b,d,e,f){return c.Point.rotate(this,a,b,d,e,f)},getMagnitude:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},setMagnitude:function(a){return this.normalize().multiply(a,a)},normalize:function(){if(!this.isZero()){var a=this.getMagnitude();this.x/=a,this.y/=a}return this},isZero:function(){return 0===this.x&&0===this.y},toString:function(){return"[{Point (x="+this.x+" y="+this.y+")}]"}},c.Point.prototype.constructor=c.Point,c.Point.add=function(a,b,d){return"undefined"==typeof d&&(d=new c.Point),d.x=a.x+b.x,d.y=a.y+b.y,d},c.Point.subtract=function(a,b,d){return"undefined"==typeof d&&(d=new c.Point),d.x=a.x-b.x,d.y=a.y-b.y,d},c.Point.multiply=function(a,b,d){return"undefined"==typeof d&&(d=new c.Point),d.x=a.x*b.x,d.y=a.y*b.y,d},c.Point.divide=function(a,b,d){return"undefined"==typeof d&&(d=new c.Point),d.x=a.x/b.x,d.y=a.y/b.y,d},c.Point.equals=function(a,b){return a.x==b.x&&a.y==b.y},c.Point.distance=function(a,b,d){return"undefined"==typeof d&&(d=!1),d?c.Math.distanceRound(a.x,a.y,b.x,b.y):c.Math.distance(a.x,a.y,b.x,b.y)},c.Point.rotate=function(a,b,d,e,f,g){return f=f||!1,g=g||null,f&&(e=c.Math.degToRad(e)),null===g&&(g=Math.sqrt((b-a.x)*(b-a.x)+(d-a.y)*(d-a.y))),a.setTo(b+g*Math.cos(e),d+g*Math.sin(e))},c.Rectangle=function(a,b,c,d){a=a||0,b=b||0,c=c||0,d=d||0,this.x=a,this.y=b,this.width=c,this.height=d},c.Rectangle.prototype={offset:function(a,b){return this.x+=a,this.y+=b,this},offsetPoint:function(a){return this.offset(a.x,a.y)},setTo:function(a,b,c,d){return this.x=a,this.y=b,this.width=c,this.height=d,this},floor:function(){this.x=Math.floor(this.x),this.y=Math.floor(this.y)},floorAll:function(){this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.width=Math.floor(this.width),this.height=Math.floor(this.height)},copyFrom:function(a){return this.setTo(a.x,a.y,a.width,a.height)},copyTo:function(a){return a.x=this.x,a.y=this.y,a.width=this.width,a.height=this.height,a},inflate:function(a,b){return c.Rectangle.inflate(this,a,b)},size:function(a){return c.Rectangle.size(this,a)},clone:function(a){return c.Rectangle.clone(this,a)},contains:function(a,b){return c.Rectangle.contains(this,a,b)},containsRect:function(a){return c.Rectangle.containsRect(this,a)},equals:function(a){return c.Rectangle.equals(this,a)},intersection:function(a,b){return c.Rectangle.intersection(this,a,b)},intersects:function(a,b){return c.Rectangle.intersects(this,a,b)},intersectsRaw:function(a,b,d,e,f){return c.Rectangle.intersectsRaw(this,a,b,d,e,f)},union:function(a,b){return c.Rectangle.union(this,a,b)},toString:function(){return"[{Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+" empty="+this.empty+")}]"}},c.Rectangle.prototype.constructor=c.Rectangle,Object.defineProperty(c.Rectangle.prototype,"halfWidth",{get:function(){return Math.round(this.width/2)}}),Object.defineProperty(c.Rectangle.prototype,"halfHeight",{get:function(){return Math.round(this.height/2)}}),Object.defineProperty(c.Rectangle.prototype,"bottom",{get:function(){return this.y+this.height},set:function(a){this.height=a<=this.y?0:this.y-a}}),Object.defineProperty(c.Rectangle.prototype,"bottomRight",{get:function(){return new c.Point(this.right,this.bottom)},set:function(a){this.right=a.x,this.bottom=a.y}}),Object.defineProperty(c.Rectangle.prototype,"left",{get:function(){return this.x},set:function(a){this.width=a>=this.right?0:this.right-a,this.x=a}}),Object.defineProperty(c.Rectangle.prototype,"right",{get:function(){return this.x+this.width},set:function(a){this.width=a<=this.x?0:this.x+a}}),Object.defineProperty(c.Rectangle.prototype,"volume",{get:function(){return this.width*this.height}}),Object.defineProperty(c.Rectangle.prototype,"perimeter",{get:function(){return 2*this.width+2*this.height}}),Object.defineProperty(c.Rectangle.prototype,"centerX",{get:function(){return this.x+this.halfWidth},set:function(a){this.x=a-this.halfWidth}}),Object.defineProperty(c.Rectangle.prototype,"centerY",{get:function(){return this.y+this.halfHeight},set:function(a){this.y=a-this.halfHeight}}),Object.defineProperty(c.Rectangle.prototype,"top",{get:function(){return this.y},set:function(a){a>=this.bottom?(this.height=0,this.y=a):this.height=this.bottom-a}}),Object.defineProperty(c.Rectangle.prototype,"topLeft",{get:function(){return new c.Point(this.x,this.y)},set:function(a){this.x=a.x,this.y=a.y}}),Object.defineProperty(c.Rectangle.prototype,"empty",{get:function(){return!this.width||!this.height},set:function(a){a===!0&&this.setTo(0,0,0,0)}}),c.Rectangle.inflate=function(a,b,c){return a.x-=b,a.width+=2*b,a.y-=c,a.height+=2*c,a},c.Rectangle.inflatePoint=function(a,b){return c.Rectangle.inflate(a,b.x,b.y)},c.Rectangle.size=function(a,b){return"undefined"==typeof b&&(b=new c.Point),b.setTo(a.width,a.height)},c.Rectangle.clone=function(a,b){return"undefined"==typeof b&&(b=new c.Rectangle),b.setTo(a.x,a.y,a.width,a.height)},c.Rectangle.contains=function(a,b,c){return b>=a.x&&b<=a.right&&c>=a.y&&c<=a.bottom},c.Rectangle.containsRaw=function(a,b,c,d,e,f){return e>=a&&a+c>=e&&f>=b&&b+d>=f},c.Rectangle.containsPoint=function(a,b){return c.Rectangle.contains(a,b.x,b.y)},c.Rectangle.containsRect=function(a,b){return a.volume>b.volume?!1:a.x>=b.x&&a.y>=b.y&&a.right<=b.right&&a.bottom<=b.bottom},c.Rectangle.equals=function(a,b){return a.x==b.x&&a.y==b.y&&a.width==b.width&&a.height==b.height},c.Rectangle.intersection=function(a,b,d){return d=d||new c.Rectangle,c.Rectangle.intersects(a,b)&&(d.x=Math.max(a.x,b.x),d.y=Math.max(a.y,b.y),d.width=Math.min(a.right,b.right)-d.x,d.height=Math.min(a.bottom,b.bottom)-d.y),d},c.Rectangle.intersects=function(a,b){return a.width<=0||a.height<=0||b.width<=0||b.height<=0?!1:!(a.rightb.right||a.y>b.bottom)},c.Rectangle.intersectsRaw=function(a,b,c,d,e,f){return"undefined"==typeof f&&(f=0),!(b>a.right+f||ca.bottom+f||e1){if(a&&a==this.decodeURI(e[0]))return this.decodeURI(e[1]);b[this.decodeURI(e[0])]=this.decodeURI(e[1])}}return b},decodeURI:function(a){return decodeURIComponent(a.replace(/\+/g," "))}},c.Net.prototype.constructor=c.Net,c.TweenManager=function(a){this.game=a,this._tweens=[],this._add=[],this.game.onPause.add(this.pauseAll,this),this.game.onResume.add(this.resumeAll,this)},c.TweenManager.prototype={getAll:function(){return this._tweens},removeAll:function(){for(var a=0;aa;)this._tweens[a].update(this.game.time.now)?a++:(this._tweens.splice(a,1),b--);return this._add.length>0&&(this._tweens=this._tweens.concat(this._add),this._add.length=0),!0},isTweening:function(a){return this._tweens.some(function(b){return b._object===a})},pauseAll:function(){for(var a=this._tweens.length-1;a>=0;a--)this._tweens[a].pause()},resumeAll:function(){for(var a=this._tweens.length-1;a>=0;a--)this._tweens[a].resume()}},c.TweenManager.prototype.constructor=c.TweenManager,c.Tween=function(a,b){this._object=a,this.game=b,this._manager=this.game.tweens,this._valuesStart={},this._valuesEnd={},this._valuesStartRepeat={},this._duration=1e3,this._repeat=0,this._yoyo=!1,this._reversed=!1,this._delayTime=0,this._startTime=null,this._easingFunction=c.Easing.Linear.None,this._interpolationFunction=c.Math.linearInterpolation,this._chainedTweens=[],this._onStartCallbackFired=!1,this._onUpdateCallback=null,this._onUpdateCallbackContext=null,this._pausedTime=0,this.pendingDelete=!1;for(var d in a)this._valuesStart[d]=parseFloat(a[d],10);this.onStart=new c.Signal,this.onLoop=new c.Signal,this.onComplete=new c.Signal,this.isRunning=!1},c.Tween.prototype={to:function(a,b,c,d,e,f,g){b=b||1e3,c=c||null,d=d||!1,e=e||0,f=f||0,g=g||!1;var h;return this._parent?(h=this._manager.create(this._object),this._lastChild.chain(h),this._lastChild=h):(h=this,this._parent=this,this._lastChild=this),h._repeat=f,h._duration=b,h._valuesEnd=a,null!==c&&(h._easingFunction=c),e>0&&(h._delayTime=e),h._yoyo=g,d?this.start():this},start:function(){if(null!==this.game&&null!==this._object){this._manager.add(this),this.isRunning=!0,this._onStartCallbackFired=!1,this._startTime=this.game.time.now+this._delayTime;for(var a in this._valuesEnd){if(this._valuesEnd[a]instanceof Array){if(0===this._valuesEnd[a].length)continue;this._valuesEnd[a]=[this._object[a]].concat(this._valuesEnd[a])}this._valuesStart[a]=this._object[a],this._valuesStart[a]instanceof Array==!1&&(this._valuesStart[a]*=1),this._valuesStartRepeat[a]=this._valuesStart[a]||0}return this}},stop:function(){return this.isRunning=!1,this._onUpdateCallback=null,this._manager.remove(this),this},delay:function(a){return this._delayTime=a,this},repeat:function(a){return this._repeat=a,this},yoyo:function(a){return this._yoyo=a,this},easing:function(a){return this._easingFunction=a,this},interpolation:function(a){return this._interpolationFunction=a,this},chain:function(){return this._chainedTweens=arguments,this},loop:function(){return this._lastChild.chain(this),this},onUpdateCallback:function(a,b){return this._onUpdateCallback=a,this._onUpdateCallbackContext=b,this},pause:function(){this._paused=!0,this._pausedTime=this.game.time.now},resume:function(){this._paused=!1,this._startTime+=this.game.time.now-this._pausedTime},update:function(a){if(this.pendingDelete)return!1;if(this._paused||a1?1:c;var d=this._easingFunction(c);for(b in this._valuesEnd){var e=this._valuesStart[b]||0,f=this._valuesEnd[b];f instanceof Array?this._object[b]=this._interpolationFunction(f,d):("string"==typeof f&&(f=e+parseFloat(f,10)),"number"==typeof f&&(this._object[b]=e+(f-e)*d))}if(null!==this._onUpdateCallback&&this._onUpdateCallback.call(this._onUpdateCallbackContext,this,d),1==c){if(this._repeat>0){isFinite(this._repeat)&&this._repeat--;for(b in this._valuesStartRepeat){if("string"==typeof this._valuesEnd[b]&&(this._valuesStartRepeat[b]=this._valuesStartRepeat[b]+parseFloat(this._valuesEnd[b],10)),this._yoyo){var g=this._valuesStartRepeat[b];this._valuesStartRepeat[b]=this._valuesEnd[b],this._valuesEnd[b]=g,this._reversed=!this._reversed}this._valuesStart[b]=this._valuesStartRepeat[b]}return this._startTime=a+this._delayTime,this.onLoop.dispatch(this._object),!0}this.isRunning=!1,this.onComplete.dispatch(this._object);for(var h=0,i=this._chainedTweens.length;i>h;h++)this._chainedTweens[h].start(a);return!1}return!0}},c.Tween.prototype.constructor=c.Tween,c.Easing={Linear:{None:function(a){return a}},Quadratic:{In:function(a){return a*a},Out:function(a){return a*(2-a)},InOut:function(a){return(a*=2)<1?.5*a*a:-.5*(--a*(a-2)-1)}},Cubic:{In:function(a){return a*a*a},Out:function(a){return--a*a*a+1},InOut:function(a){return(a*=2)<1?.5*a*a*a:.5*((a-=2)*a*a+2)}},Quartic:{In:function(a){return a*a*a*a},Out:function(a){return 1- --a*a*a*a},InOut:function(a){return(a*=2)<1?.5*a*a*a*a:-.5*((a-=2)*a*a*a-2)}},Quintic:{In:function(a){return a*a*a*a*a},Out:function(a){return--a*a*a*a*a+1},InOut:function(a){return(a*=2)<1?.5*a*a*a*a*a:.5*((a-=2)*a*a*a*a+2)}},Sinusoidal:{In:function(a){return 1-Math.cos(a*Math.PI/2)},Out:function(a){return Math.sin(a*Math.PI/2)},InOut:function(a){return.5*(1-Math.cos(Math.PI*a))}},Exponential:{In:function(a){return 0===a?0:Math.pow(1024,a-1)},Out:function(a){return 1===a?1:1-Math.pow(2,-10*a)},InOut:function(a){return 0===a?0:1===a?1:(a*=2)<1?.5*Math.pow(1024,a-1):.5*(-Math.pow(2,-10*(a-1))+2)}},Circular:{In:function(a){return 1-Math.sqrt(1-a*a)},Out:function(a){return Math.sqrt(1- --a*a)},InOut:function(a){return(a*=2)<1?-.5*(Math.sqrt(1-a*a)-1):.5*(Math.sqrt(1-(a-=2)*a)+1)}},Elastic:{In:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),-(c*Math.pow(2,10*(a-=1))*Math.sin((a-b)*2*Math.PI/d)))},Out:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),c*Math.pow(2,-10*a)*Math.sin((a-b)*2*Math.PI/d)+1)},InOut:function(a){var b,c=.1,d=.4;return 0===a?0:1===a?1:(!c||1>c?(c=1,b=d/4):b=d*Math.asin(1/c)/(2*Math.PI),(a*=2)<1?-.5*c*Math.pow(2,10*(a-=1))*Math.sin((a-b)*2*Math.PI/d):.5*c*Math.pow(2,-10*(a-=1))*Math.sin((a-b)*2*Math.PI/d)+1)}},Back:{In:function(a){var b=1.70158;return a*a*((b+1)*a-b)},Out:function(a){var b=1.70158;return--a*a*((b+1)*a+b)+1},InOut:function(a){var b=2.5949095;return(a*=2)<1?.5*a*a*((b+1)*a-b):.5*((a-=2)*a*((b+1)*a+b)+2)}},Bounce:{In:function(a){return 1-c.Easing.Bounce.Out(1-a)},Out:function(a){return 1/2.75>a?7.5625*a*a:2/2.75>a?7.5625*(a-=1.5/2.75)*a+.75:2.5/2.75>a?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375},InOut:function(a){return.5>a?.5*c.Easing.Bounce.In(2*a):.5*c.Easing.Bounce.Out(2*a-1)+.5}}},c.Time=function(a){this.game=a,this.time=0,this.now=0,this.elapsed=0,this.pausedTime=0,this.fps=0,this.fpsMin=1e3,this.fpsMax=0,this.msMin=1e3,this.msMax=0,this.physicsElapsed=0,this.frames=0,this.pauseDuration=0,this.timeToCall=0,this.lastTime=0,this.events=new c.Timer(this.game,!1),this._started=0,this._timeLastSecond=0,this._pauseStarted=0,this._justResumed=!1,this._timers=[],this._len=0,this._i=0,this.game.onPause.add(this.gamePaused,this),this.game.onResume.add(this.gameResumed,this)},c.Time.prototype={boot:function(){this.events.start()},create:function(a){"undefined"==typeof a&&(a=!0);var b=new c.Timer(this.game,a);return this._timers.push(b),b},removeAll:function(){for(var a=0;athis._timeLastSecond+1e3&&(this.fps=Math.round(1e3*this.frames/(this.now-this._timeLastSecond)),this.fpsMin=this.game.math.min(this.fpsMin,this.fps),this.fpsMax=this.game.math.max(this.fpsMax,this.fps),this._timeLastSecond=this.now,this.frames=0),this.time=this.now,this.lastTime=a+this.timeToCall,this.physicsElapsed=1*(this.elapsed/1e3),this.physicsElapsed>.05&&(this.physicsElapsed=.05),this.game.paused)this.pausedTime=this.now-this._pauseStarted;else for(this.events.update(this.now),this._i=0,this._len=this._timers.length;this._i0&&(this.events.sort(this.sortHandler),this.nextTick=this.events[0].tick)},sortHandler:function(a,b){return a.tickb.tick?1:0},update:function(a){if(this.paused)return!0;if(this._now=a-this._started,this._len=this.events.length,this.running&&this._now>=this.nextTick&&this._len>0){for(this._i=0;this._i=this.events[this._i].tick;)this.events[this._i].loop===!0?(this.events[this._i].tick+=this.events[this._i].delay-(this._now-this.events[this._i].tick),this.events[this._i].callback.apply(this.events[this._i].callbackContext,this.events[this._i].args)):this.events[this._i].repeatCount>0?(this.events[this._i].repeatCount--,this.events[this._i].tick+=this.events[this._i].delay-(this._now-this.events[this._i].tick),this.events[this._i].callback.apply(this.events[this._i].callbackContext,this.events[this._i].args)):(this.events[this._i].callback.apply(this.events[this._i].callbackContext,this.events[this._i].args),this.events.splice(this._i,1),this._len--),this._i++;this.events.length>0?this.order():(this.expired=!0,this.onComplete.dispatch(this))}return this.expired&&this.autoDestroy?!1:!0},pause:function(){this._pauseStarted=this.game.time.now,this.paused=!0},resume:function(){for(var a=this.game.time.now-this._pauseStarted,b=0;bthis._now?this.nextTick-this._now:0}}),Object.defineProperty(c.Timer.prototype,"length",{get:function(){return this.events.length}}),Object.defineProperty(c.Timer.prototype,"ms",{get:function(){return this._now}}),Object.defineProperty(c.Timer.prototype,"seconds",{get:function(){return.001*this._now}}),c.Timer.prototype.constructor=c.Timer,c.TimerEvent=function(a,b,c,d,e,f,g,h){this.timer=a,this.delay=b,this.tick=c,this.repeatCount=d-1,this.loop=e,this.callback=f,this.callbackContext=g,this.args=h},c.TimerEvent.prototype.constructor=c.TimerEvent,c.AnimationManager=function(a){this.sprite=a,this.game=a.game,this.currentFrame=null,this.updateIfVisible=!0,this.isLoaded=!1,this._frameData=null,this._anims={},this._outputFrames=[]},c.AnimationManager.prototype={loadFrameData:function(a){this._frameData=a,this.frame=0,this.isLoaded=!0},add:function(a,d,e,f,g){return null==this._frameData?(console.warn("No FrameData available for Phaser.Animation "+a),void 0):(e=e||60,"undefined"==typeof f&&(f=!1),"undefined"==typeof g&&(g=d&&"number"==typeof d[0]?!0:!1),null==this.sprite.events.onAnimationStart&&(this.sprite.events.onAnimationStart=new c.Signal,this.sprite.events.onAnimationComplete=new c.Signal,this.sprite.events.onAnimationLoop=new c.Signal),this._outputFrames.length=0,this._frameData.getFrameIndexes(d,g,this._outputFrames),this._anims[a]=new c.Animation(this.game,this.sprite,a,this._frameData,this._outputFrames,e,f),this.currentAnim=this._anims[a],this.currentFrame=this.currentAnim.currentFrame,this.sprite.setTexture(b.TextureCache[this.currentFrame.uuid]),this._anims[a])},validateFrames:function(a,b){"undefined"==typeof b&&(b=!0);for(var c=0;cthis._frameData.total)return!1}else if(this._frameData.checkFrameName(a[c])===!1)return!1;return!0},play:function(a,b,c,d){if(this._anims[a]){if(this.currentAnim!=this._anims[a])return this.currentAnim=this._anims[a],this.currentAnim.paused=!1,this.currentAnim.play(b,c,d);if(this.currentAnim.isPlaying===!1)return this.currentAnim.paused=!1,this.currentAnim.play(b,c,d)}},stop:function(a,b){"undefined"==typeof b&&(b=!1),"string"==typeof a?this._anims[a]&&(this.currentAnim=this._anims[a],this.currentAnim.stop(b)):this.currentAnim&&this.currentAnim.stop(b)},update:function(){return this.updateIfVisible&&this.sprite.visible===!1?!1:this.currentAnim&&this.currentAnim.update()===!0?(this.currentFrame=this.currentAnim.currentFrame,this.sprite.currentFrame=this.currentFrame,!0):!1},getAnimation:function(a){return"string"==typeof a&&this._anims[a]?this._anims[a]:null},refreshFrame:function(){this.sprite.currentFrame=this.currentFrame,this.sprite.setTexture(b.TextureCache[this.currentFrame.uuid])},destroy:function(){this._anims={},this._frameData=null,this._frameIndex=0,this.currentAnim=null,this.currentFrame=null}},c.AnimationManager.prototype.constructor=c.AnimationManager,Object.defineProperty(c.AnimationManager.prototype,"frameData",{get:function(){return this._frameData}}),Object.defineProperty(c.AnimationManager.prototype,"frameTotal",{get:function(){return this._frameData?this._frameData.total:-1}}),Object.defineProperty(c.AnimationManager.prototype,"paused",{get:function(){return this.currentAnim.isPaused},set:function(a){this.currentAnim.paused=a}}),Object.defineProperty(c.AnimationManager.prototype,"frame",{get:function(){return this.currentFrame?this._frameIndex:void 0},set:function(a){"number"==typeof a&&this._frameData&&null!==this._frameData.getFrame(a)&&(this.currentFrame=this._frameData.getFrame(a),this._frameIndex=a,this.sprite.currentFrame=this.currentFrame,this.sprite.setTexture(b.TextureCache[this.currentFrame.uuid]))}}),Object.defineProperty(c.AnimationManager.prototype,"frameName",{get:function(){return this.currentFrame?this.currentFrame.name:void 0},set:function(a){"string"==typeof a&&this._frameData&&null!==this._frameData.getFrameByName(a)?(this.currentFrame=this._frameData.getFrameByName(a),this._frameIndex=this.currentFrame.index,this.sprite.currentFrame=this.currentFrame,this.sprite.setTexture(b.TextureCache[this.currentFrame.uuid])):console.warn("Cannot set frameName: "+a)}}),c.Animation=function(a,b,c,d,e,f,g){this.game=a,this._parent=b,this._frameData=d,this.name=c,this._frames=[],this._frames=this._frames.concat(e),this.delay=1e3/f,this.looped=g,this.killOnComplete=!1,this.isFinished=!1,this.isPlaying=!1,this.isPaused=!1,this._pauseStartTime=0,this._frameIndex=0,this._frameDiff=0,this._frameSkip=1,this.currentFrame=this._frameData.getFrame(this._frames[this._frameIndex])},c.Animation.prototype={play:function(a,c,d){return"number"==typeof a&&(this.delay=1e3/a),"boolean"==typeof c&&(this.looped=c),"undefined"!=typeof d&&(this.killOnComplete=d),this.isPlaying=!0,this.isFinished=!1,this.paused=!1,this._timeLastFrame=this.game.time.now,this._timeNextFrame=this.game.time.now+this.delay,this._frameIndex=0,this.currentFrame=this._frameData.getFrame(this._frames[this._frameIndex]),this._parent.setTexture(b.TextureCache[this.currentFrame.uuid]),this._parent.events&&this._parent.events.onAnimationStart.dispatch(this._parent,this),this},restart:function(){this.isPlaying=!0,this.isFinished=!1,this.paused=!1,this._timeLastFrame=this.game.time.now,this._timeNextFrame=this.game.time.now+this.delay,this._frameIndex=0,this.currentFrame=this._frameData.getFrame(this._frames[this._frameIndex])},stop:function(a){"undefined"==typeof a&&(a=!1),this.isPlaying=!1,this.isFinished=!0,this.paused=!1,a&&(this.currentFrame=this._frameData.getFrame(this._frames[0]))},update:function(){return this.isPaused?!1:this.isPlaying===!0&&this.game.time.now>=this._timeNextFrame?(this._frameSkip=1,this._frameDiff=this.game.time.now-this._timeNextFrame,this._timeLastFrame=this.game.time.now,this._frameDiff>this.delay&&(this._frameSkip=Math.floor(this._frameDiff/this.delay),this._frameDiff-=this._frameSkip*this.delay),this._timeNextFrame=this.game.time.now+(this.delay-this._frameDiff),this._frameIndex+=this._frameSkip,this._frameIndex>=this._frames.length?this.looped?(this._frameIndex%=this._frames.length,this.currentFrame=this._frameData.getFrame(this._frames[this._frameIndex]),this.currentFrame&&this._parent.setTexture(b.TextureCache[this.currentFrame.uuid]),this._parent.events.onAnimationLoop.dispatch(this._parent,this)):this.onComplete():(this.currentFrame=this._frameData.getFrame(this._frames[this._frameIndex]),this.currentFrame&&this._parent.setTexture(b.TextureCache[this.currentFrame.uuid])),!0):!1},destroy:function(){this.game=null,this._parent=null,this._frames=null,this._frameData=null,this.currentFrame=null,this.isPlaying=!1},onComplete:function(){this.isPlaying=!1,this.isFinished=!0,this.paused=!1,this._parent.events&&this._parent.events.onAnimationComplete.dispatch(this._parent,this),this.killOnComplete&&this._parent.kill()}},c.Animation.prototype.constructor=c.Animation,Object.defineProperty(c.Animation.prototype,"paused",{get:function(){return this.isPaused},set:function(a){this.isPaused=a,a?this._pauseStartTime=this.game.time.now:this.isPlaying&&(this._timeNextFrame=this.game.time.now+this.delay)}}),Object.defineProperty(c.Animation.prototype,"frameTotal",{get:function(){return this._frames.length}}),Object.defineProperty(c.Animation.prototype,"frame",{get:function(){return null!==this.currentFrame?this.currentFrame.index:this._frameIndex},set:function(a){this.currentFrame=this._frameData.getFrame(a),null!==this.currentFrame&&(this._frameIndex=a,this._parent.setTexture(b.TextureCache[this.currentFrame.uuid]))}}),c.Animation.generateFrameNames=function(a,b,d,e,f){"undefined"==typeof e&&(e="");var g=[],h="";if(d>b)for(var i=b;d>=i;i++)h="number"==typeof f?c.Utils.pad(i.toString(),f,"0",1):i.toString(),h=a+h+e,g.push(h);else for(var i=b;i>=d;i--)h="number"==typeof f?c.Utils.pad(i.toString(),f,"0",1):i.toString(),h=a+h+e,g.push(h);return g},c.Frame=function(a,b,d,e,f,g,h){this.index=a,this.x=b,this.y=d,this.width=e,this.height=f,this.name=g,this.uuid=h,this.centerX=Math.floor(e/2),this.centerY=Math.floor(f/2),this.distance=c.Math.distance(0,0,e,f),this.rotated=!1,this.rotationDirection="cw",this.trimmed=!1,this.sourceSizeW=e,this.sourceSizeH=f,this.spriteSourceSizeX=0,this.spriteSourceSizeY=0,this.spriteSourceSizeW=0,this.spriteSourceSizeH=0},c.Frame.prototype={setTrim:function(a,b,c,d,e,f,g){this.trimmed=a,a&&(this.width=b,this.height=c,this.sourceSizeW=b,this.sourceSizeH=c,this.centerX=Math.floor(b/2),this.centerY=Math.floor(c/2),this.spriteSourceSizeX=d,this.spriteSourceSizeY=e,this.spriteSourceSizeW=f,this.spriteSourceSizeH=g)}},c.Frame.prototype.constructor=c.Frame,c.FrameData=function(){this._frames=[],this._frameNames=[]},c.FrameData.prototype={addFrame:function(a){return a.index=this._frames.length,this._frames.push(a),""!==a.name&&(this._frameNames[a.name]=a.index),a},getFrame:function(a){return this._frames.length>a?this._frames[a]:null},getFrameByName:function(a){return"number"==typeof this._frameNames[a]?this._frames[this._frameNames[a]]:null},checkFrameName:function(a){return null==this._frameNames[a]?!1:!0},getFrameRange:function(a,b,c){"undefined"==typeof c&&(c=[]);for(var d=a;b>=d;d++)c.push(this._frames[d]);return c},getFrames:function(a,b,c){if("undefined"==typeof b&&(b=!0),"undefined"==typeof c&&(c=[]),"undefined"==typeof a||0===a.length)for(var d=0;dd;d++)b?c.push(this.getFrame(a[d])):c.push(this.getFrameByName(a[d]));return c},getFrameIndexes:function(a,b,c){if("undefined"==typeof b&&(b=!0),"undefined"==typeof c&&(c=[]),"undefined"==typeof a||0===a.length)for(var d=0,e=this._frames.length;e>d;d++)c.push(this._frames[d].index);else for(var d=0,e=a.length;e>d;d++)b?c.push(a[d]):this.getFrameByName(a[d])&&c.push(this.getFrameByName(a[d]).index);return c}},c.FrameData.prototype.constructor=c.FrameData,Object.defineProperty(c.FrameData.prototype,"total",{get:function(){return this._frames.length}}),c.AnimationParser={spriteSheet:function(a,d,e,f,g,h,i){var j=a.cache.getImage(d);if(null==j)return null;var k=j.width,l=j.height;0>=e&&(e=Math.floor(-k/Math.min(-1,e))),0>=f&&(f=Math.floor(-l/Math.min(-1,f)));var m=Math.round(k/e),n=Math.round(l/f),o=m*n;if(-1!==g&&(o=g),0===k||0===l||e>k||f>l||0===o)return console.warn("Phaser.AnimationParser.spriteSheet: width/height zero or width/height < given frameWidth/frameHeight"),null;for(var p=new c.FrameData,q=h,r=h,s=0;o>s;s++){var t=a.rnd.uuid();p.addFrame(new c.Frame(s,q,r,e,f,"",t)),b.TextureCache[t]=new b.Texture(b.BaseTextureCache[d],{x:q,y:r,width:e,height:f}),q+=e+i,q===k&&(q=h,r+=f+i)}return p},JSONData:function(a,d,e){if(!d.frames)return console.warn("Phaser.AnimationParser.JSONData: Invalid Texture Atlas JSON given, missing 'frames' array"),console.log(d),void 0;for(var f,g=new c.FrameData,h=d.frames,i=0;i tag"),void 0;for(var f,g,h,i,j,k,l,m,n,o,p,q,r=new c.FrameData,s=d.getElementsByTagName("SubTexture"),t=0;t0)for(var c=0;c0)for(var c=0;c0?(this._fileIndex=0,this._progressChunk=100/this._fileList.length,this.loadFile()):(this.progress=100,this.progressFloat=100,this.hasLoaded=!0,this.onLoadComplete.dispatch()))},loadFile:function(){if(!this._fileList[this._fileIndex])return console.warn("Phaser.Loader loadFile invalid index "+this._fileIndex),void 0;var a=this._fileList[this._fileIndex],b=this;switch(a.type){case"image":case"spritesheet":case"textureatlas":case"bitmapfont":a.data=new Image,a.data.name=a.key,a.data.onload=function(){return b.fileComplete(b._fileIndex)},a.data.onerror=function(){return b.fileError(b._fileIndex)},a.data.crossOrigin=this.crossOrigin,a.data.src=this.baseURL+a.url;break;case"audio":a.url=this.getAudioURL(a.url),null!==a.url?this.game.sound.usingWebAudio?(this._xhr.open("GET",this.baseURL+a.url,!0),this._xhr.responseType="arraybuffer",this._xhr.onload=function(){return b.fileComplete(b._fileIndex)},this._xhr.onerror=function(){return b.fileError(b._fileIndex)},this._xhr.send()):this.game.sound.usingAudioTag&&(this.game.sound.touchLocked?(a.data=new Audio,a.data.name=a.key,a.data.preload="auto",a.data.src=this.baseURL+a.url,this.fileComplete(this._fileIndex)):(a.data=new Audio,a.data.name=a.key,a.data.onerror=function(){return b.fileError(b._fileIndex)},a.data.preload="auto",a.data.src=this.baseURL+a.url,a.data.addEventListener("canplaythrough",c.GAMES[this.game.id].load.fileComplete(this._fileIndex),!1),a.data.load())):this.fileError(this._fileIndex);break;case"tilemap":if(this._xhr.open("GET",this.baseURL+a.url,!0),this._xhr.responseType="text",a.format===c.Tilemap.TILED_JSON)this._xhr.onload=function(){return b.jsonLoadComplete(b._fileIndex)};else{if(a.format!==c.Tilemap.CSV)throw new Error("Phaser.Loader. Invalid Tilemap format: "+a.format);this._xhr.onload=function(){return b.csvLoadComplete(b._fileIndex)}}this._xhr.onerror=function(){return b.dataLoadError(b._fileIndex)},this._xhr.send();break;case"text":case"script":this._xhr.open("GET",this.baseURL+a.url,!0),this._xhr.responseType="text",this._xhr.onload=function(){return b.fileComplete(b._fileIndex)},this._xhr.onerror=function(){return b.fileError(b._fileIndex)},this._xhr.send();break;case"binary":this._xhr.open("GET",this.baseURL+a.url,!0),this._xhr.responseType="arraybuffer",this._xhr.onload=function(){return b.fileComplete(b._fileIndex)},this._xhr.onerror=function(){return b.fileError(b._fileIndex)},this._xhr.send()}},getAudioURL:function(a){var b;"string"==typeof a&&(a=[a]);for(var c=0;c100&&(this.progress=100),null!==this.preloadSprite&&(0===this.preloadSprite.direction?this.preloadSprite.crop.width=Math.floor(this.preloadSprite.width/100*this.progress):this.preloadSprite.crop.height=Math.floor(this.preloadSprite.height/100*this.progress),this.preloadSprite.sprite.crop=this.preloadSprite.crop),this.onFileComplete.dispatch(this.progress,this._fileList[a].key,b,this.totalLoadedFiles(),this._fileList.length),this.totalQueuedFiles()>0?(this._fileIndex++,this.loadFile()):(this.hasLoaded=!0,this.isLoading=!1,this.removeAll(),this.onLoadComplete.dispatch())},totalLoadedFiles:function(){for(var a=0,b=0;b tag"),void 0;var e=b.TextureCache[d],f={},g=c.getElementsByTagName("info")[0],h=c.getElementsByTagName("common")[0];f.font=g.attributes.getNamedItem("face").nodeValue,f.size=parseInt(g.attributes.getNamedItem("size").nodeValue,10),f.lineHeight=parseInt(h.attributes.getNamedItem("lineHeight").nodeValue,10),f.chars={};for(var i=c.getElementsByTagName("char"),j=0;j=this.durationMS&&(this.usingWebAudio?this.loop?(this.onLoop.dispatch(this),""===this.currentMarker?(this.currentTime=0,this.startTime=this.game.time.now):this.play(this.currentMarker,0,this.volume,!0,!0)):this.stop():this.loop?(this.onLoop.dispatch(this),this.play(this.currentMarker,0,this.volume,!0,!0)):this.stop()))},play:function(a,b,c,d,e){if(a=a||"",b=b||0,"undefined"==typeof c&&(c=this._volume),"undefined"==typeof d&&(d=!1),"undefined"==typeof e&&(e=!0),this.isPlaying!==!0||e!==!1||this.override!==!1){if(this.isPlaying&&this.override&&(this.usingWebAudio?"undefined"==typeof this._sound.stop?this._sound.noteOff(0):this._sound.stop(0):this.usingAudioTag&&(this._sound.pause(),this._sound.currentTime=0)),this.currentMarker=a,""!==a){if(!this.markers[a])return console.warn("Phaser.Sound.play: audio marker "+a+" doesn't exist"),void 0;this.position=this.markers[a].start,this.volume=this.markers[a].volume,this.loop=this.markers[a].loop,this.duration=this.markers[a].duration,this.durationMS=this.markers[a].durationMS,this._tempMarker=a,this._tempPosition=this.position,this._tempVolume=this.volume,this._tempLoop=this.loop}else this.position=b,this.volume=c,this.loop=d,this.duration=0,this.durationMS=0,this._tempMarker=a,this._tempPosition=b,this._tempVolume=c,this._tempLoop=d;this.usingWebAudio?this.game.cache.isSoundDecoded(this.key)?(null==this._buffer&&(this._buffer=this.game.cache.getSoundData(this.key)),this._sound=this.context.createBufferSource(),this._sound.buffer=this._buffer,this.externalNode?this._sound.connect(this.externalNode.input):this._sound.connect(this.gainNode),this.totalDuration=this._sound.buffer.duration,0===this.duration&&(this.duration=this.totalDuration,this.durationMS=1e3*this.totalDuration),this.loop&&""===a&&(this._sound.loop=!0),"undefined"==typeof this._sound.start?this._sound.noteGrainOn(0,this.position,this.duration):this._sound.start(0,this.position,this.duration),this.isPlaying=!0,this.startTime=this.game.time.now,this.currentTime=0,this.stopTime=this.startTime+this.durationMS,this.onPlay.dispatch(this)):(this.pendingPlayback=!0,this.game.cache.getSound(this.key)&&this.game.cache.getSound(this.key).isDecoding===!1&&this.game.sound.decode(this.key,this)):this.game.cache.getSound(this.key)&&this.game.cache.getSound(this.key).locked?(this.game.cache.reloadSound(this.key),this.pendingPlayback=!0):this._sound&&(this.game.device.cocoonJS||4===this._sound.readyState)?(this._sound.play(),this.totalDuration=this._sound.duration,0===this.duration&&(this.duration=this.totalDuration,this.durationMS=1e3*this.totalDuration),this._sound.currentTime=this.position,this._sound.muted=this._muted,this._sound.volume=this._muted?0:this._volume,this.isPlaying=!0,this.startTime=this.game.time.now,this.currentTime=0,this.stopTime=this.startTime+this.durationMS,this.onPlay.dispatch(this)):this.pendingPlayback=!0}},restart:function(a,b,c,d){a=a||"",b=b||0,c=c||1,"undefined"==typeof d&&(d=!1),this.play(a,b,c,d,!0)},pause:function(){this.isPlaying&&this._sound&&(this.stop(),this.isPlaying=!1,this.paused=!0,this.pausedPosition=this.currentTime,this.pausedTime=this.game.time.now,this.onPause.dispatch(this))},resume:function(){if(this.paused&&this._sound){if(this.usingWebAudio){var a=this.position+this.pausedPosition/1e3;this._sound=this.context.createBufferSource(),this._sound.buffer=this._buffer,this.externalNode?this._sound.connect(this.externalNode.input):this._sound.connect(this.gainNode),this.loop&&(this._sound.loop=!0),"undefined"==typeof this._sound.start?this._sound.noteGrainOn(0,a,this.duration):this._sound.start(0,a,this.duration)}else this._sound.play();this.isPlaying=!0,this.paused=!1,this.startTime+=this.game.time.now-this.pausedTime,this.onResume.dispatch(this)}},stop:function(){this.isPlaying&&this._sound&&(this.usingWebAudio?"undefined"==typeof this._sound.stop?this._sound.noteOff(0):this._sound.stop(0):this.usingAudioTag&&(this._sound.pause(),this._sound.currentTime=0)),this.isPlaying=!1;var a=this.currentMarker;this.currentMarker="",this.onStop.dispatch(this,a)}},c.Sound.prototype.constructor=c.Sound,Object.defineProperty(c.Sound.prototype,"isDecoding",{get:function(){return this.game.cache.getSound(this.key).isDecoding}}),Object.defineProperty(c.Sound.prototype,"isDecoded",{get:function(){return this.game.cache.isSoundDecoded(this.key)}}),Object.defineProperty(c.Sound.prototype,"mute",{get:function(){return this._muted},set:function(a){a=a||null,a?(this._muted=!0,this.usingWebAudio?(this._muteVolume=this.gainNode.gain.value,this.gainNode.gain.value=0):this.usingAudioTag&&this._sound&&(this._muteVolume=this._sound.volume,this._sound.volume=0)):(this._muted=!1,this.usingWebAudio?this.gainNode.gain.value=this._muteVolume:this.usingAudioTag&&this._sound&&(this._sound.volume=this._muteVolume)),this.onMute.dispatch(this)}}),Object.defineProperty(c.Sound.prototype,"volume",{get:function(){return this._volume},set:function(a){this.usingWebAudio?(this._volume=a,this.gainNode.gain.value=a):this.usingAudioTag&&this._sound&&a>=0&&1>=a&&(this._volume=a,this._sound.volume=a)}}),c.SoundManager=function(a){this.game=a,this.onSoundDecode=new c.Signal,this._muted=!1,this._unlockSource=null,this._volume=1,this._sounds=[],this.context=null,this.usingWebAudio=!0,this.usingAudioTag=!1,this.noAudio=!1,this.connectToMaster=!0,this.touchLocked=!1,this.channels=32},c.SoundManager.prototype={boot:function(){if(this.game.device.iOS&&this.game.device.webAudio===!1&&(this.channels=1),this.game.device.iOS||window.PhaserGlobal&&window.PhaserGlobal.fakeiOSTouchLock?(this.game.input.touch.callbackContext=this,this.game.input.touch.touchStartCallback=this.unlock,this.game.input.mouse.callbackContext=this,this.game.input.mouse.mouseDownCallback=this.unlock,this.touchLocked=!0):this.touchLocked=!1,window.PhaserGlobal){if(window.PhaserGlobal.disableAudio===!0)return this.usingWebAudio=!1,this.noAudio=!0,void 0;if(window.PhaserGlobal.disableWebAudio===!0)return this.usingWebAudio=!1,this.usingAudioTag=!0,this.noAudio=!1,void 0}window.AudioContext?this.context=new window.AudioContext:window.webkitAudioContext?this.context=new window.webkitAudioContext:window.Audio?(this.usingWebAudio=!1,this.usingAudioTag=!0):(this.usingWebAudio=!1,this.noAudio=!0),null!==this.context&&(this.masterGain="undefined"==typeof this.context.createGain?this.context.createGainNode():this.context.createGain(),this.masterGain.gain.value=1,this.masterGain.connect(this.context.destination))},unlock:function(){if(this.touchLocked!==!1)if(this.game.device.webAudio===!1||window.PhaserGlobal&&window.PhaserGlobal.disableWebAudio===!0)this.touchLocked=!1,this._unlockSource=null,this.game.input.touch.callbackContext=null,this.game.input.touch.touchStartCallback=null,this.game.input.mouse.callbackContext=null,this.game.input.mouse.mouseDownCallback=null;else{var a=this.context.createBuffer(1,1,22050);this._unlockSource=this.context.createBufferSource(),this._unlockSource.buffer=a,this._unlockSource.connect(this.context.destination),this._unlockSource.noteOn(0)}},stopAll:function(){for(var a=0;a255)return c.Color.getColor(255,255,255);if(a>b)return c.Color.getColor(255,255,255);var e=a+Math.round(Math.random()*(b-a)),f=a+Math.round(Math.random()*(b-a)),g=a+Math.round(Math.random()*(b-a));return c.Color.getColor32(d,e,f,g)},getRGB:function(a){return{alpha:a>>>24,red:255&a>>16,green:255&a>>8,blue:255&a}},getWebRGB:function(a){var b=(a>>>24)/255,c=255&a>>16,d=255&a>>8,e=255&a;return"rgba("+c.toString()+","+d.toString()+","+e.toString()+","+b.toString()+")"},getAlpha:function(a){return a>>>24},getAlphaFloat:function(a){return(a>>>24)/255},getRed:function(a){return 255&a>>16},getGreen:function(a){return 255&a>>8},getBlue:function(a){return 255&a}},function(a,b){"use strict";"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.SAT=b()}(this,function(){"use strict";function a(a,b){this.x=a||0,this.y=b||0}function b(b,c){this.pos=b||new a,this.r=c||0}function c(b,c){this.pos=b||new a,this.points=c||[],this.recalc()}function d(b,c,d){this.pos=b||new a,this.w=c||0,this.h=d||0}function e(){this.a=null,this.b=null,this.overlapN=new a,this.overlapV=new a,this.clear()}function f(a,b,c){for(var d=Number.MAX_VALUE,e=-Number.MAX_VALUE,f=a.length,g=0;f>g;g++){var h=a[g].dot(b);d>h&&(d=h),h>e&&(e=h)}c[0]=d,c[1]=e}function g(a,b,c,d,e,g){var h=p.pop(),i=p.pop(),j=n.pop().copy(b).sub(a),k=j.dot(e);if(f(c,e,h),f(d,e,i),i[0]+=k,i[1]+=k,h[0]>i[1]||i[0]>h[1])return n.push(j),p.push(h),p.push(i),!0;if(g){var l=0;if(h[0]m?m:-o}else if(g.bInA=!1,h[1]>i[1])l=h[0]-i[1],g.aInB=!1;else{var m=h[1]-i[0],o=i[1]-h[0];l=o>m?m:-o}var q=Math.abs(l);ql&&g.overlapN.reverse())}return n.push(j),p.push(h),p.push(i),!1}function h(a,b){var c=a.len2(),d=b.dot(a);return 0>d?q:d>c?s:r}function i(a,b,c){var d=n.pop().copy(b.pos).sub(a.pos),e=a.r+b.r,f=e*e,g=d.len2();if(g>f)return n.push(d),!1;if(c){var h=Math.sqrt(g);c.a=a,c.b=b,c.overlap=e-h,c.overlapN.copy(d.normalize()),c.overlapV.copy(d).scale(c.overlap),c.aInB=a.r<=b.r&&h<=b.r-a.r,c.bInA=b.r<=a.r&&h<=a.r-b.r}return n.push(d),!0}function j(a,b,c){for(var d=n.pop().copy(b.pos).sub(a.pos),e=b.r,f=e*e,g=a.points,i=g.length,j=n.pop(),k=n.pop(),l=0;i>l;l++){var m=l===i-1?0:l+1,o=0===l?i-1:l-1,p=0,r=null;j.copy(a.edges[l]),k.copy(d).sub(g[l]),c&&k.len2()>f&&(c.aInB=!1);var t=h(j,k);if(t===q){j.copy(a.edges[o]);var u=n.pop().copy(d).sub(g[o]);if(t=h(j,u),t===s){var v=k.len();if(v>e)return n.push(d),n.push(j),n.push(k),n.push(u),!1;c&&(c.bInA=!1,r=k.normalize(),p=e-v)}n.push(u)}else if(t===s){if(j.copy(a.edges[m]),k.copy(d).sub(g[m]),t=h(j,k),t===q){var v=k.len();if(v>e)return n.push(d),n.push(j),n.push(k),!1;c&&(c.bInA=!1,r=k.normalize(),p=e-v)}}else{var w=j.perp().normalize(),v=k.dot(w),x=Math.abs(v);if(v>0&&x>e)return n.push(d),n.push(w),n.push(k),!1;c&&(r=w,p=e-v,(v>=0||2*e>p)&&(c.bInA=!1))}r&&c&&Math.abs(p)i;i++)if(g(a.pos,b.pos,d,f,a.normals[i],c))return!1;for(var i=0;h>i;i++)if(g(a.pos,b.pos,d,f,b.normals[i],c))return!1;return c&&(c.a=a,c.b=b,c.overlapV.copy(c.overlapN).scale(c.overlap)),!0}var m={};m.Vector=a,m.V=a,a.prototype.copy=a.prototype.copy=function(a){return this.x=a.x,this.y=a.y,this},a.prototype.perp=a.prototype.perp=function(){var a=this.x;return this.x=this.y,this.y=-a,this},a.prototype.rotate=a.prototype.rotate=function(a){var b=this.x,c=this.y;return this.x=b*Math.cos(a)-c*Math.sin(a),this.y=b*Math.sin(a)+c*Math.cos(a),this},a.prototype.rotatePrecalc=a.prototype.rotatePrecalc=function(a,b){var c=this.x,d=this.y;return this.x=c*b-d*a,this.y=c*a+d*b,this},a.prototype.reverse=a.prototype.reverse=function(){return this.x=-this.x,this.y=-this.y,this},a.prototype.normalize=a.prototype.normalize=function(){var a=this.len();return a>0&&(this.x=this.x/a,this.y=this.y/a),this},a.prototype.add=a.prototype.add=function(a){return this.x+=a.x,this.y+=a.y,this},a.prototype.sub=a.prototype.sub=function(a){return this.x-=a.x,this.y-=a.y,this},a.prototype.scale=a.prototype.scale=function(a,b){return this.x*=a,this.y*=b||a,this},a.prototype.project=a.prototype.project=function(a){var b=this.dot(a)/a.len2();return this.x=b*a.x,this.y=b*a.y,this},a.prototype.projectN=a.prototype.projectN=function(a){var b=this.dot(a);return this.x=b*a.x,this.y=b*a.y,this},a.prototype.reflect=a.prototype.reflect=function(a){var b=this.x,c=this.y;return this.project(a).scale(2),this.x-=b,this.y-=c,this},a.prototype.reflectN=a.prototype.reflectN=function(a){var b=this.x,c=this.y;return this.projectN(a).scale(2),this.x-=b,this.y-=c,this},a.prototype.dot=a.prototype.dot=function(a){return this.x*a.x+this.y*a.y},a.prototype.len2=a.prototype.len2=function(){return this.dot(this)
-},a.prototype.len=a.prototype.len=function(){return Math.sqrt(this.len2())},m.Circle=b,m.Polygon=c,c.prototype.recalc=c.prototype.recalc=function(){this.edges=[],this.normals=[];for(var b=this.points,c=b.length,d=0;c>d;d++){var e=b[d],f=c-1>d?b[d+1]:b[0],g=(new a).copy(f).sub(e),h=(new a).copy(g).perp().normalize();this.edges.push(g),this.normals.push(h)}return this},c.prototype.rotate=c.prototype.rotate=function(a){var b,c=this.points,d=this.edges,e=this.normals,f=c.length,g=Math.cos(a),h=Math.sin(a);for(b=0;f>b;b++)c[b].rotatePrecalc(h,g),d[b].rotatePrecalc(h,g),e[b].rotatePrecalc(h,g);return this},c.prototype.scale=c.prototype.scale=function(a,b){var c,d=this.points,e=this.edges,f=this.normals,g=d.length;for(c=0;g>c;c++)d[c].scale(a,b),e[c].scale(a,b),f[c].scale(a,b);return this},c.prototype.translate=c.prototype.translate=function(a,b){var c,d=this.points,e=d.length;for(c=0;e>c;c++)d[c].x+=a,d[c].y+=b;return this},m.Box=d,d.prototype.toPolygon=d.prototype.toPolygon=function(){var b=this.pos,d=this.w,e=this.h;return new c(new a(b.x,b.y),[new a,new a(d,0),new a(d,e),new a(0,e)])},m.Response=e,e.prototype.clear=e.prototype.clear=function(){return this.aInB=!0,this.bInA=!0,this.overlap=Number.MAX_VALUE,this};for(var n=[],o=0;10>o;o++)n.push(new a);for(var p=[],o=0;5>o;o++)p.push([]);var q=-1,r=0,s=1;return m.testCircleCircle=i,m.testPolygonCircle=j,m.testCirclePolygon=k,m.testPolygonPolygon=l,m}),c.Physics={},c.Physics.Arcade=function(a){this.game=a,this.gravity=new c.Point,this.worldLeft=null,this.worldRight=null,this.worldTop=null,this.worldBottom=null,this.worldPolys=[null,null,null,null],this.quadTree=new c.QuadTree(this.game.world.bounds.x,this.game.world.bounds.y,this.game.world.bounds.width,this.game.world.bounds.height,this.maxObjects,this.maxLevels),this.maxObjects=10,this.maxLevels=4,this._mapData=[],this._mapTiles=0,this._result=!1,this._total=0,this._angle=0,this._drag=0,this._dx=0,this._dy=0,this._p=new c.Point(0,0),this._intersection=[0,0,0,0],this._gravityX=0,this._gravityY=0,this._response=new SAT.Response,this.setBoundsToWorld(!0,!0,!0,!0)},c.Physics.Arcade.RECT=0,c.Physics.Arcade.CIRCLE=1,c.Physics.Arcade.POLYGON=2,c.Physics.Arcade.prototype={checkBounds:function(a){if(!a.collideWorldBounds||!this.worldLeft&&!this.worldRight&&!this.worldTop&&!this.worldBottom)return!1;this._response.clear();var b=SAT.testPolygonPolygon,d=a.polygon,e=!1;return a.type===c.Physics.Arcade.CIRCLE&&(b=SAT.testPolygonCircle,d=a.shape),this.worldLeft&&b(this.worldPolys[0],d,this._response)?(a.blocked.left=!0,d.pos.add(this._response.overlapV),a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),e=!0):this.worldRight&&b(this.worldPolys[1],d,this._response)&&(a.blocked.right=!0,d.pos.add(this._response.overlapV),a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),e=!0),this._response.clear(),this.worldTop&&b(this.worldPolys[2],d,this._response)?(a.blocked.up=!0,d.pos.add(this._response.overlapV),a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),e=!0):this.worldBottom&&b(this.worldPolys[3],d,this._response)&&(a.blocked.down=!0,d.pos.add(this._response.overlapV),a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),e=!0),e},setBoundsToWorld:function(a,b,c,d){this.setBounds(this.game.world.bounds.x,this.game.world.bounds.y,this.game.world.bounds.width,this.game.world.bounds.height,a,b,c,d)},setBounds:function(a,b,c,d,e,f,g,h){"undefined"==typeof e&&(e=!0),"undefined"==typeof f&&(f=!0),"undefined"==typeof g&&(g=!0),"undefined"==typeof h&&(h=!0);var i=100;e?(this.worldLeft=new SAT.Box(new SAT.Vector(a-i,b),i,d),this.worldPolys[0]=this.worldLeft.toPolygon()):(this.worldLeft=null,this.worldPolys[0]=null),f?(this.worldRight=new SAT.Box(new SAT.Vector(a+c,b),i,d),this.worldPolys[1]=this.worldRight.toPolygon()):(this.worldRight=null,this.worldPolys[1]=null),g?(this.worldTop=new SAT.Box(new SAT.Vector(a,b-i),c,i),this.worldPolys[2]=this.worldTop.toPolygon()):(this.worldTop=null,this.worldPolys[2]=null),h?(this.worldBottom=new SAT.Box(new SAT.Vector(a,b+d),c,i),this.worldPolys[3]=this.worldBottom.toPolygon()):(this.worldBottom=null,this.worldPolys[3]=null)},updateMotion:function(a){return a.allowGravity?(this._gravityX=this.gravity.x+a.gravity.x,this._gravityY=this.gravity.y+a.gravity.y):(this._gravityX=a.gravity.x,this._gravityY=a.gravity.y),(this._gravityX<0&&a.blocked.left||this._gravityX>0&&a.blocked.right)&&(this._gravityX=0),(this._gravityY<0&&a.blocked.up||this._gravityY>0&&a.blocked.down)&&(this._gravityY=0),a.allowRotation&&(this._velocityDelta=a.angularAcceleration*this.game.time.physicsElapsed,0!==a.angularDrag&&0===a.angularAcceleration&&(this._drag=a.angularDrag*this.game.time.physicsElapsed,a.angularVelocity>0?a.angularVelocity-=this._drag:a.angularVelocity<0&&(a.angularVelocity+=this._drag)),a.rotation+=this.game.time.physicsElapsed*(a.angularVelocity+this._velocityDelta/2),a.angularVelocity+=this._velocityDelta,a.angularVelocity>a.maxAngular?a.angularVelocity=a.maxAngular:a.angularVelocity<-a.maxAngular&&(a.angularVelocity=-a.maxAngular)),this._p.setTo((a.acceleration.x+this._gravityX)*this.game.time.physicsElapsed,(a.acceleration.y+this._gravityY)*this.game.time.physicsElapsed),this._p},overlap:function(a,b,c,d,e){if(c=c||null,d=d||null,e=e||c,this._result=!1,this._total=0,Array.isArray(b))for(var f=0,g=b.length;g>f;f++)this.collideHandler(a,b[f],c,d,e,!0);else this.collideHandler(a,b,c,d,e,!0);return this._total>0},collide:function(a,b,c,d,e){if(c=c||null,d=d||null,e=e||c,this._result=!1,this._total=0,Array.isArray(b))for(var f=0,g=b.length;g>f;f++)this.collideHandler(a,b[f],c,d,e,!1);else this.collideHandler(a,b,c,d,e,!1);return this._total>0},collideHandler:function(a,b,d,e,f,g){return"undefined"!=typeof b||a.type!==c.GROUP&&a.type!==c.EMITTER?(a&&b&&a.exists&&b.exists&&(a.type==c.SPRITE||a.type==c.TILESPRITE?b.type==c.SPRITE||b.type==c.TILESPRITE?this.collideSpriteVsSprite(a,b,d,e,f,g):b.type==c.GROUP||b.type==c.EMITTER?this.collideSpriteVsGroup(a,b,d,e,f,g):b.type==c.TILEMAPLAYER&&this.collideSpriteVsTilemapLayer(a,b,d,e,f):a.type==c.GROUP?b.type==c.SPRITE||b.type==c.TILESPRITE?this.collideSpriteVsGroup(b,a,d,e,f,g):b.type==c.GROUP||b.type==c.EMITTER?this.collideGroupVsGroup(a,b,d,e,f,g):b.type==c.TILEMAPLAYER&&this.collideGroupVsTilemapLayer(a,b,d,e,f):a.type==c.TILEMAPLAYER?b.type==c.SPRITE||b.type==c.TILESPRITE?this.collideSpriteVsTilemapLayer(b,a,d,e,f):(b.type==c.GROUP||b.type==c.EMITTER)&&this.collideGroupVsTilemapLayer(b,a,d,e,f):a.type==c.EMITTER&&(b.type==c.SPRITE||b.type==c.TILESPRITE?this.collideSpriteVsGroup(b,a,d,e,f,g):b.type==c.GROUP||b.type==c.EMITTER?this.collideGroupVsGroup(a,b,d,e,f,g):b.type==c.TILEMAPLAYER&&this.collideGroupVsTilemapLayer(a,b,d,e,f))),void 0):(this.collideGroupVsSelf(a,d,e,f,g),void 0)},collideSpriteVsSprite:function(a,b,c,d,e,f){this.separate(a.body,b.body,d,e,f)&&(c&&c.call(e,a,b),this._total++)},collideSpriteVsGroup:function(a,b,d,e,f,g){if(0!==b.length){this.quadTree.clear(),this.quadTree=new c.QuadTree(this.game.world.bounds.x,this.game.world.bounds.y,this.game.world.bounds.width,this.game.world.bounds.height,this.maxObjects,this.maxLevels),this.quadTree.populate(b),this._potentials=this.quadTree.retrieve(a);for(var h=0,i=this._potentials.length;i>h;h++)this.separate(a.body,this._potentials[h],e,f,g)&&(d&&d.call(f,a,this._potentials[h].sprite),this._total++)}},collideGroupVsSelf:function(a,b,c,d,e){if(0!==a.length)for(var f=a._container.children.length,g=0;f>g;g++)for(var h=g+1;f>=h;h++)a._container.children[g]&&a._container.children[h]&&a._container.children[g].exists&&a._container.children[h].exists&&this.collideSpriteVsSprite(a._container.children[g],a._container.children[h],b,c,d,e)},collideGroupVsGroup:function(a,b,c,d,e,f){if(0!==a.length&&0!==b.length&&a._container.first._iNext){var g=a._container.first._iNext;do g.exists&&this.collideSpriteVsGroup(g,b,c,d,e,f),g=g._iNext;while(g!=a._container.last._iNext)}},collideSpriteVsTilemapLayer:function(a,b,c,d,e){if(this._mapData=b.getTiles(a.body.left,a.body.top,a.body.width,a.body.height,!0),0!==this._mapData.length)if(this._mapData.length>1)this.separateTiles(a.body,this._mapData);else{var f=0;this.separateTile(a.body,this._mapData[f])&&(d?d.call(e,a,this._mapData[f])&&(this._total++,c&&c.call(e,a,this._mapData[f])):(this._total++,c&&c.call(e,a,this._mapData[f])))}},collideGroupVsTilemapLayer:function(a,b,c,d,e){if(0!==a.length&&a._container.first._iNext){var f=a._container.first._iNext;do f.exists&&this.collideSpriteVsTilemapLayer(f,b,c,d,e),f=f._iNext;while(f!=a._container.last._iNext)}},separate:function(a,b,c,d,e){return a===b||this.intersects(a,b)===!1?!1:c&&c.call(d,a.sprite,b.sprite)===!1?!1:(this._response.clear(),e?a.overlap(b,this._response):a.overlap(b,this._response)?a.separate(b,this._response):!1)},intersects:function(a,b){var c=!1;(a.width<=0||a.height<=0||b.width<=0||b.height<=0)&&(c=!1),c=!(a.rightb.right||a.top>b.bottom),!c&&a.inContact(b)&&a.removeContact(b)},tileIntersects:function(a,b){return a.width<=0||a.height<=0||b.width<=0||b.height<=0?(this._intersection[4]=0,this._intersection):a.rightb.right||a.top>b.bottom?(this._intersection[4]=0,this._intersection):(this._intersection[0]=Math.max(a.left,b.x),this._intersection[1]=Math.max(a.top,b.y),this._intersection[2]=Math.min(a.right,b.right)-this._intersection[0],this._intersection[3]=Math.min(a.bottom,b.bottom)-this._intersection[1],this._intersection[4]=1,this._intersection)},separateTiles:function(a,b){for(var c,d=!1,e=0;e0&&a.checkCollision.right&&b.tile.faceLeft&&!a.blocked.right&&(a.overlapX=a.right-b.x,a.overlapX>0?c=!0:a.overlapX=0),a.deltaY()<0&&a.checkCollision.up&&b.tile.faceBottom&&!a.blocked.up?(a.overlapY=a.top-b.bottom,a.overlapY<0?c=!0:a.overlapY=0):a.deltaY()>0&&a.checkCollision.down&&b.tile.faceTop&&!a.blocked.down&&(a.overlapY=a.bottom-b.y,a.overlapY>0?c=!0:a.overlapY=0),0!==a.overlapX&&0!==a.overlapY&&(Math.abs(a.overlapX)>Math.abs(a.overlapY)?a.overlapX=0:a.overlapY=0),c?this.processTileSeparation(a):!1},processTileSeparation:function(a){return a.overlapX<0?(a.x-=a.overlapX,a.left-=a.overlapX,a.right-=a.overlapX,a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),a.blocked.left=!0):a.overlapX>0&&(a.x-=a.overlapX,a.left-=a.overlapX,a.right-=a.overlapX,a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),a.blocked.right=!0),a.overlapY<0?(a.y-=a.overlapY,a.top-=a.overlapY,a.bottom-=a.overlapY,a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),a.blocked.up=!0):a.overlapY>0&&(a.y-=a.overlapY,a.top-=a.overlapY,a.bottom-=a.overlapY,a.blocked.x=Math.floor(a.x),a.blocked.y=Math.floor(a.y),a.blocked.down=!0),a.reboundCheck(a.overlapX,a.overlapY,!0),!0},moveToObject:function(a,b,c,d){return"undefined"==typeof c&&(c=60),"undefined"==typeof d&&(d=0),this._angle=Math.atan2(b.y-a.y,b.x-a.x),d>0&&(c=this.distanceBetween(a,b)/(d/1e3)),a.body.velocity.x=Math.cos(this._angle)*c,a.body.velocity.y=Math.sin(this._angle)*c,this._angle},moveToPointer:function(a,b,c,d){return"undefined"==typeof b&&(b=60),c=c||this.game.input.activePointer,"undefined"==typeof d&&(d=0),this._angle=this.angleToPointer(a,c),d>0&&(b=this.distanceToPointer(a,c)/(d/1e3)),a.body.velocity.x=Math.cos(this._angle)*b,a.body.velocity.y=Math.sin(this._angle)*b,this._angle},moveToXY:function(a,b,c,d,e){return"undefined"==typeof d&&(d=60),"undefined"==typeof e&&(e=0),this._angle=Math.atan2(c-a.y,b-a.x),e>0&&(d=this.distanceToXY(a,b,c)/(e/1e3)),a.body.velocity.x=Math.cos(this._angle)*d,a.body.velocity.y=Math.sin(this._angle)*d,this._angle},velocityFromAngle:function(a,b,d){return"undefined"==typeof b&&(b=60),d=d||new c.Point,d.setTo(Math.cos(this.game.math.degToRad(a))*b,Math.sin(this.game.math.degToRad(a))*b)},velocityFromRotation:function(a,b,d){return"undefined"==typeof b&&(b=60),d=d||new c.Point,d.setTo(Math.cos(a)*b,Math.sin(a)*b)},accelerationFromRotation:function(a,b,d){return"undefined"==typeof b&&(b=60),d=d||new c.Point,d.setTo(Math.cos(a)*b,Math.sin(a)*b)},accelerateToObject:function(a,b,c,d,e){return"undefined"==typeof c&&(c=60),"undefined"==typeof d&&(d=1e3),"undefined"==typeof e&&(e=1e3),this._angle=this.angleBetween(a,b),a.body.acceleration.setTo(Math.cos(this._angle)*c,Math.sin(this._angle)*c),a.body.maxVelocity.setTo(d,e),this._angle},accelerateToPointer:function(a,b,c,d,e){return"undefined"==typeof c&&(c=60),"undefined"==typeof b&&(b=this.game.input.activePointer),"undefined"==typeof d&&(d=1e3),"undefined"==typeof e&&(e=1e3),this._angle=this.angleToPointer(a,b),a.body.acceleration.setTo(Math.cos(this._angle)*c,Math.sin(this._angle)*c),a.body.maxVelocity.setTo(d,e),this._angle},accelerateToXY:function(a,b,c,d,e,f){return"undefined"==typeof d&&(d=60),"undefined"==typeof e&&(e=1e3),"undefined"==typeof f&&(f=1e3),this._angle=this.angleToXY(a,b,c),a.body.acceleration.setTo(Math.cos(this._angle)*d,Math.sin(this._angle)*d),a.body.maxVelocity.setTo(e,f),this._angle},distanceBetween:function(a,b){return this._dx=a.x-b.x,this._dy=a.y-b.y,Math.sqrt(this._dx*this._dx+this._dy*this._dy)},distanceToXY:function(a,b,c){return this._dx=a.x-b,this._dy=a.y-c,Math.sqrt(this._dx*this._dx+this._dy*this._dy)},distanceToPointer:function(a,b){return b=b||this.game.input.activePointer,this._dx=a.x-b.x,this._dy=a.y-b.y,Math.sqrt(this._dx*this._dx+this._dy*this._dy)},angleBetween:function(a,b){return this._dx=b.x-a.x,this._dy=b.y-a.y,Math.atan2(this._dy,this._dx)},angleToXY:function(a,b,c){return this._dx=b-a.x,this._dy=c-a.y,Math.atan2(this._dy,this._dx)},angleToPointer:function(a,b){return b=b||this.game.input.activePointer,this._dx=b.worldX-a.x,this._dy=b.worldY-a.y,Math.atan2(this._dy,this._dx)}},c.Physics.Arcade.prototype.constructor=c.Physics.Arcade,c.Physics.Arcade.Body=function(a){this.sprite=a,this.game=a.game,this.offset=new c.Point,this.preX=a.world.x,this.preY=a.world.y,this.preRotation=a.angle,this.velocity=new c.Point,this.acceleration=new c.Point,this.speed=0,this.angle=0,this.gravity=new c.Point,this.bounce=new c.Point,this.minVelocity=new c.Point,this.maxVelocity=new c.Point(1e3,1e3),this.angularVelocity=0,this.angularAcceleration=0,this.angularDrag=0,this.maxAngular=1e3,this.mass=1,this.linearDamping=0,this.checkCollision={none:!1,any:!0,up:!0,down:!0,left:!0,right:!0},this.touching={none:!0,up:!1,down:!1,left:!1,right:!1},this.blocked={x:0,y:0,up:!1,down:!1,left:!1,right:!1},this.facing=c.NONE,this.rebound=!0,this.immovable=!1,this.moves=!0,this.rotation=0,this.allowRotation=!0,this.allowGravity=!0,this.customSeparateCallback=null,this.customSeparateContext=null,this.collideCallback=null,this.collideCallbackContext=null,this.collideWorldBounds=!1,this.type=c.Physics.Arcade.RECT,this.shape=null,this.polygon=null,this.left=0,this.right=0,this.top=0,this.bottom=0,this.width=0,this.height=0,this.contacts=[],this.overlapX=0,this.overlapY=0,this._temp=null,this._dx=0,this._dy=0,this._sx=a.scale.x,this._sy=a.scale.y,this._distances=[0,0,0,0],this._vx=0,this._vy=0,this.setRectangle(a.width,a.height,0,0),this.sprite.events.onBeginContact=new c.Signal,this.sprite.events.onEndContact=new c.Signal},c.Physics.Arcade.Body.prototype={updateScale:function(){this.polygon?this.polygon.scale(this.sprite.scale.x/this._sx,this.sprite.scale.y/this._sy):this.shape.r*=Math.max(this.sprite.scale.x,this.sprite.scale.y),this._sx=this.sprite.scale.x,this._sy=this.sprite.scale.y},preUpdate:function(){this.x=this.sprite.world.x-this.sprite.anchor.x*this.sprite.width+this.offset.x,this.y=this.sprite.world.y-this.sprite.anchor.y*this.sprite.height+this.offset.y,this.preX=this.x,this.preY=this.y,this.preRotation=this.sprite.angle,this.rotation=this.preRotation,(this.sprite.scale.x!==this._sx||this.sprite.scale.y!==this._sy)&&this.updateScale(),this.checkBlocked(),this.touching.none=!0,this.touching.up=!1,this.touching.down=!1,this.touching.left=!1,this.touching.right=!1,this.moves?((this._vx!==this.velocity.x||this._vy!==this.velocity.y)&&(this._vx=this.velocity.x,this._vy=this.velocity.y,this.speed=Math.sqrt(this.velocity.x*this.velocity.x+this.velocity.y*this.velocity.y),this.angle=Math.atan2(this.velocity.y,this.velocity.x)),this.game.physics.checkBounds(this)&&this.reboundCheck(!0,!0,!0),this.applyDamping(),this.integrateVelocity(),this.updateBounds(),this.checkBlocked()):this.updateBounds()},checkBlocked:function(){!this.blocked.left&&!this.blocked.right||Math.floor(this.x)===this.blocked.x&&Math.floor(this.y)===this.blocked.y||(this.blocked.left=!1,this.blocked.right=!1),!this.blocked.up&&!this.blocked.down||Math.floor(this.x)===this.blocked.x&&Math.floor(this.y)===this.blocked.y||(this.blocked.up=!1,this.blocked.down=!1)},updateBounds:function(){this.type===c.Physics.Arcade.CIRCLE?(this.left=this.shape.pos.x-this.shape.r,this.right=this.shape.pos.x+this.shape.r,this.top=this.shape.pos.y-this.shape.r,this.bottom=this.shape.pos.y+this.shape.r):(this.left=c.Math.minProperty("x",this.polygon.points)+this.polygon.pos.x,this.right=c.Math.maxProperty("x",this.polygon.points)+this.polygon.pos.x,this.top=c.Math.minProperty("y",this.polygon.points)+this.polygon.pos.y,this.bottom=c.Math.maxProperty("y",this.polygon.points)+this.polygon.pos.y),this.width=this.right-this.left,this.height=this.bottom-this.top},applyDamping:function(){this.linearDamping>0&&this.acceleration.isZero()&&(this.speed>this.linearDamping?this.speed-=this.linearDamping:this.speed=0,this.speed>0&&(this.velocity.x=Math.cos(this.angle)*this.speed,this.velocity.y=Math.sin(this.angle)*this.speed,this.speed=Math.sqrt(this.velocity.x*this.velocity.x+this.velocity.y*this.velocity.y),this.angle=Math.atan2(this.velocity.y,this.velocity.x)))},reboundCheck:function(a,b,c){if(a&&(c&&0!==this.bounce.x&&(this.blocked.left||this.blocked.right||this.touching.left||this.touching.right)&&(this._vx<=0&&this.velocity.x>0||this._vx>=0&&this.velocity.x<0||(this.velocity.x*=-this.bounce.x,this.angle=Math.atan2(this.velocity.y,this.velocity.x))),0===this.bounce.x||Math.abs(this.velocity.x)d||this.velocity.x<0)||(this.blocked.right||this.touching.right)&&(d>0||this.velocity.x>0))&&(this.velocity.x=0)}if(b&&(c&&0!==this.bounce.y&&(this.blocked.up||this.blocked.down||this.touching.up||this.touching.down)&&(this._vy<=0&&this.velocity.y>0||this._vy>=0&&this.velocity.y<0||(this.velocity.y*=-this.bounce.y,this.angle=Math.atan2(this.velocity.y,this.velocity.x))),0===this.bounce.y||Math.abs(this.velocity.y)e||this.velocity.y<0)||(this.blocked.down||this.touching.down)&&(e>0||this.velocity.y>0))&&(this.velocity.y=0)}},getUpwardForce:function(){return this.allowGravity?this.gravity.x+this.game.physics.gravity.x+this.velocity.x:this.gravity.x+this.velocity.x},getDownwardForce:function(){return this.allowGravity?this.gravity.y+this.game.physics.gravity.y+this.velocity.y:this.gravity.y+this.velocity.y},sub:function(a){this.x-=a.x,this.y-=a.y},add:function(a){this.x+=a.x,this.y+=a.y},give:function(a,b){this.add(b.overlapV),this.rebound&&(this.processRebound(a),this.reboundCheck(!0,!0,!1),a.reboundCheck(!0,!0,!1))},take:function(a,b){this.sub(b.overlapV),this.rebound&&(this.processRebound(a),this.reboundCheck(!0,!0,!1),a.reboundCheck(!0,!0,!1))},split:function(a,b){b.overlapV.scale(.5),this.sub(b.overlapV),a.add(b.overlapV),this.rebound&&(this.exchange(a),this.reboundCheck(!0,!0,!1),a.reboundCheck(!0,!0,!1))},exchange:function(a){if(this.mass===a.mass&&this.speed>0&&a.speed>0)this._dx=a.velocity.x,this._dy=a.velocity.y,a.velocity.x=this.velocity.x*a.bounce.x,a.velocity.y=this.velocity.y*a.bounce.x,this.velocity.x=this._dx*this.bounce.x,this.velocity.y=this._dy*this.bounce.y;else{var b=Math.sqrt(a.velocity.x*a.velocity.x*a.mass/this.mass)*(a.velocity.x>0?1:-1),c=Math.sqrt(this.velocity.x*this.velocity.x*this.mass/a.mass)*(this.velocity.x>0?1:-1),d=.5*(b+c);b-=d,c-=d,this.velocity.x=b,a.velocity.x=c,b=Math.sqrt(a.velocity.y*a.velocity.y*a.mass/this.mass)*(a.velocity.y>0?1:-1),c=Math.sqrt(this.velocity.y*this.velocity.y*this.mass/a.mass)*(this.velocity.y>0?1:-1),d=.5*(b+c),b-=d,c-=d,this.velocity.y=b,a.velocity.y=c}},processRebound:function(a){this._vx<=0&&this.velocity.x>0||this._vx>=0&&this.velocity.x<0||(this.velocity.x=a.velocity.x-this.velocity.x*this.bounce.x),this._vy<=0&&this.velocity.y>0||this._vy>=0&&this.velocity.y<0||(this.velocity.y=a.velocity.y-this.velocity.y*this.bounce.y),this.angle=Math.atan2(this.velocity.y,this.velocity.x),this.reboundCheck(!0,!0,!1)},overlap:function(a,b){var d=!1;return this.type!==c.Physics.Arcade.RECT&&this.type!==c.Physics.Arcade.POLYGON||a.type!==c.Physics.Arcade.RECT&&a.type!==c.Physics.Arcade.POLYGON?this.type===c.Physics.Arcade.CIRCLE&&a.type===c.Physics.Arcade.CIRCLE?d=SAT.testCircleCircle(this.shape,a.shape,b):this.type!==c.Physics.Arcade.RECT&&this.type!==c.Physics.Arcade.POLYGON||a.type!==c.Physics.Arcade.CIRCLE?this.type!==c.Physics.Arcade.CIRCLE||a.type!==c.Physics.Arcade.RECT&&a.type!==c.Physics.Arcade.POLYGON||(d=SAT.testCirclePolygon(this.shape,a.polygon,b)):d=SAT.testPolygonCircle(this.polygon,a.shape,b):d=SAT.testPolygonPolygon(this.polygon,a.polygon,b),d||this.removeContact(a),d},inContact:function(a){return-1!=this.contacts.indexOf(a)},addContact:function(a){return this.inContact(a)?!1:(this.contacts.push(a),this.sprite.events.onBeginContact.dispatch(this.sprite,a.sprite,this,a),a.addContact(this),!0)},removeContact:function(a){return this.inContact(a)?(this.contacts.splice(this.contacts.indexOf(a),1),this.sprite.events.onEndContact.dispatch(this.sprite,a.sprite,this,a),a.removeContact(this),!0):!1},separate:function(a,b){if(this.inContact(a))return!1;if(this._distances[0]=a.right-this.x,this._distances[1]=this.right-a.x,this._distances[2]=a.bottom-this.y,this._distances[3]=this.bottom-a.y,!b.overlapN.x||0!==this._distances[0]&&0!==this._distances[1]?!b.overlapN.y||0!==this._distances[2]&&0!==this._distances[3]||(b.overlapN.x=!0,b.overlapN.y=!1):(b.overlapN.x=!1,b.overlapN.y=!0),this.customSeparateCallback)return this.customSeparateCallback.call(this.customSeparateContext,this,b,this._distances);var c=!1;return b.overlapN.x?this._distances[0]0&&!this.blocked.right&&!this.touching.right)&&(this.x+=this._dx,this.velocity.x+=this._temp.x),(this._dy<0&&!this.blocked.up&&!this.touching.up||this._dy>0&&!this.blocked.down&&!this.touching.down)&&(this.y+=this._dy,this.velocity.y+=this._temp.y),this.velocity.x>this.maxVelocity.x?this.velocity.x=this.maxVelocity.x:this.velocity.x<-this.maxVelocity.x&&(this.velocity.x=-this.maxVelocity.x),this.velocity.y>this.maxVelocity.y?this.velocity.y=this.maxVelocity.y:this.velocity.y<-this.maxVelocity.y&&(this.velocity.y=-this.maxVelocity.y)},postUpdate:function(){this.moves&&(this.game.physics.checkBounds(this),this.reboundCheck(!0,!0,!0),this._dx=this.deltaX(),this._dy=this.deltaY(),this._dx<0?this.facing=c.LEFT:this._dx>0&&(this.facing=c.RIGHT),this._dy<0?this.facing=c.UP:this._dy>0&&(this.facing=c.DOWN),(0!==this._dx||0!==this._dy)&&(this.sprite.x+=this._dx,this.sprite.y+=this._dy),this.allowRotation&&0!==this.deltaZ()&&(this.sprite.angle+=this.deltaZ()),(this.sprite.scale.x!==this._sx||this.sprite.scale.y!==this._sy)&&this.updateScale())},reset:function(a){"undefined"==typeof a&&(a=!1),a&&(this.gravity.setTo(0,0),this.bounce.setTo(0,0),this.minVelocity.setTo(5,5),this.maxVelocity.setTo(1e3,1e3),this.angularDrag=0,this.maxAngular=1e3,this.mass=1,this.friction=0,this.checkCollision={none:!1,any:!0,up:!0,down:!0,left:!0,right:!0}),this.velocity.setTo(0,0),this.acceleration.setTo(0,0),this.angularVelocity=0,this.angularAcceleration=0,this.blocked={x:0,y:0,up:!1,down:!1,left:!1,right:!1},this.x=this.sprite.world.x-this.sprite.anchor.x*this.sprite.width+this.offset.x,this.y=this.sprite.world.y-this.sprite.anchor.y*this.sprite.height+this.offset.y,this.preX=this.x,this.preY=this.y,this.updateBounds(),this.contacts.length=0},destroy:function(){this.sprite=null,this.collideCallback=null,this.collideCallbackContext=null,this.customSeparateCallback=null,this.customSeparateContext=null,this.contacts.length=0},setCircle:function(a,b,d){"undefined"==typeof b&&(b=this.sprite._cache.halfWidth),"undefined"==typeof d&&(d=this.sprite._cache.halfHeight),this.type=c.Physics.Arcade.CIRCLE,this.shape=new SAT.Circle(new SAT.Vector(this.sprite.x,this.sprite.y),a),this.polygon=null,this.offset.setTo(b,d)},setRectangle:function(a,b,d,e){"undefined"==typeof a&&(a=this.sprite.width),"undefined"==typeof b&&(b=this.sprite.height),"undefined"==typeof d&&(d=-this.sprite._cache.halfWidth),"undefined"==typeof e&&(e=-this.sprite._cache.halfHeight),this.type=c.Physics.Arcade.RECT,this.shape=new SAT.Box(new SAT.Vector(this.sprite.world.x,this.sprite.world.y),a,b),this.polygon=this.shape.toPolygon(),this.polygon.translate(d,e),this.offset.setTo(0,0)},setPolygon:function(a){if(this.type=c.Physics.Arcade.POLYGON,this.shape=null,Array.isArray(a)||(a=Array.prototype.slice.call(arguments)),"number"==typeof a[0]){for(var b=[],d=0,e=a.length;e>d;d+=2)b.push(new SAT.Vector(a[d],a[d+1]));a=b}this.polygon=new SAT.Polygon(new SAT.Vector(this.sprite.center.x,this.sprite.center.y),a),this.offset.setTo(0,0)},translate:function(a,b){this.polygon&&this.polygon.translate(a,b)},onFloor:function(){return this.blocked.down},onWall:function(){return!this.blocked.down&&(this.blocked.left||this.blocked.right)},deltaX:function(){return this.x-this.preX},deltaY:function(){return this.y-this.preY},deltaZ:function(){return this.rotation-this.preRotation}},c.Physics.Arcade.Body.prototype.constructor=c.Physics.Arcade.Body,Object.defineProperty(c.Physics.Arcade.Body.prototype,"x",{get:function(){return this.type===c.Physics.Arcade.CIRCLE?this.shape.pos.x:this.polygon.pos.x},set:function(a){this.type===c.Physics.Arcade.CIRCLE?this.shape.pos.x=a:this.polygon.pos.x=a}}),Object.defineProperty(c.Physics.Arcade.Body.prototype,"y",{get:function(){return this.type===c.Physics.Arcade.CIRCLE?this.shape.pos.y:this.polygon.pos.y},set:function(a){this.type===c.Physics.Arcade.CIRCLE?this.shape.pos.y=a:this.polygon.pos.y=a}}),c.Particles=function(a){this.game=a,this.emitters={},this.ID=0},c.Particles.prototype={add:function(a){return this.emitters[a.name]=a,a},remove:function(a){delete this.emitters[a.name]},update:function(){for(var a in this.emitters)this.emitters[a].exists&&this.emitters[a].update()}},c.Particles.prototype.constructor=c.Particles,c.Particles.Arcade={},c.Particles.Arcade.Emitter=function(a,b,d,e){this.maxParticles=e||50,c.Group.call(this,a),this.name="emitter"+this.game.particles.ID++,this.type=c.EMITTER,this.x=0,this.y=0,this.width=1,this.height=1,this.minParticleSpeed=new c.Point(-100,-100),this.maxParticleSpeed=new c.Point(100,100),this.minParticleScale=1,this.maxParticleScale=1,this.minRotation=-360,this.maxRotation=360,this.gravity=100,this.particleClass=null,this.particleFriction=0,this.angularDrag=0,this.frequency=100,this.lifespan=2e3,this.bounce=new c.Point,this._quantity=0,this._timer=0,this._counter=0,this._explode=!0,this.on=!1,this.exists=!0,this.emitX=b,this.emitY=d},c.Particles.Arcade.Emitter.prototype=Object.create(c.Group.prototype),c.Particles.Arcade.Emitter.prototype.constructor=c.Particles.Arcade.Emitter,c.Particles.Arcade.Emitter.prototype.update=function(){if(this.on)if(this._explode){this._counter=0;do this.emitParticle(),this._counter++;while(this._counter=this._timer&&(this.emitParticle(),this._counter++,this._quantity>0&&this._counter>=this._quantity&&(this.on=!1),this._timer=this.game.time.now+this.frequency)},c.Particles.Arcade.Emitter.prototype.makeParticles=function(a,b,d,e,f){"undefined"==typeof b&&(b=0),"undefined"==typeof d&&(d=this.maxParticles),"undefined"==typeof e&&(e=!1),"undefined"==typeof f&&(f=!1);for(var g,h=0,i=a,j=b;d>h;)null===this.particleClass&&("object"==typeof a&&(i=this.game.rnd.pick(a)),"object"==typeof b&&(j=this.game.rnd.pick(b)),g=new c.Sprite(this.game,0,0,i,j)),e?(g.body.checkCollision.any=!0,g.body.checkCollision.none=!1):g.body.checkCollision.none=!0,g.body.collideWorldBounds=f,g.exists=!1,g.visible=!1,g.anchor.setTo(.5,.5),this.add(g),h++;return this},c.Particles.Arcade.Emitter.prototype.kill=function(){this.on=!1,this.alive=!1,this.exists=!1},c.Particles.Arcade.Emitter.prototype.revive=function(){this.alive=!0,this.exists=!0},c.Particles.Arcade.Emitter.prototype.start=function(a,b,c,d){"undefined"==typeof a&&(a=!0),"undefined"==typeof b&&(b=0),"undefined"==typeof c&&(c=250),"undefined"==typeof d&&(d=0),this.revive(),this.visible=!0,this.on=!0,this._explode=a,this.lifespan=b,this.frequency=c,a?this._quantity=d:this._quantity+=d,this._counter=0,this._timer=this.game.time.now+c},c.Particles.Arcade.Emitter.prototype.emitParticle=function(){var a=this.getFirstExists(!1);if(null!=a){if(this.width>1||this.height>1?a.reset(this.game.rnd.integerInRange(this.left,this.right),this.game.rnd.integerInRange(this.top,this.bottom)):a.reset(this.emitX,this.emitY),a.lifespan=this.lifespan,a.body.bounce.setTo(this.bounce.x,this.bounce.y),a.body.velocity.x=this.minParticleSpeed.x!=this.maxParticleSpeed.x?this.game.rnd.integerInRange(this.minParticleSpeed.x,this.maxParticleSpeed.x):this.minParticleSpeed.x,a.body.velocity.y=this.minParticleSpeed.y!=this.maxParticleSpeed.y?this.game.rnd.integerInRange(this.minParticleSpeed.y,this.maxParticleSpeed.y):this.minParticleSpeed.y,a.body.gravity.y=this.gravity,a.body.angularVelocity=this.minRotation!=this.maxRotation?this.game.rnd.integerInRange(this.minRotation,this.maxRotation):this.minRotation,1!==this.minParticleScale||1!==this.maxParticleScale){var b=this.game.rnd.realInRange(this.minParticleScale,this.maxParticleScale);
-a.scale.setTo(b,b)}a.body.friction=this.particleFriction,a.body.angularDrag=this.angularDrag}},c.Particles.Arcade.Emitter.prototype.setSize=function(a,b){this.width=a,this.height=b},c.Particles.Arcade.Emitter.prototype.setXSpeed=function(a,b){a=a||0,b=b||0,this.minParticleSpeed.x=a,this.maxParticleSpeed.x=b},c.Particles.Arcade.Emitter.prototype.setYSpeed=function(a,b){a=a||0,b=b||0,this.minParticleSpeed.y=a,this.maxParticleSpeed.y=b},c.Particles.Arcade.Emitter.prototype.setRotation=function(a,b){a=a||0,b=b||0,this.minRotation=a,this.maxRotation=b},c.Particles.Arcade.Emitter.prototype.at=function(a){a.center&&(this.emitX=a.center.x,this.emitY=a.center.y)},Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"alpha",{get:function(){return this._container.alpha},set:function(a){this._container.alpha=a}}),Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"visible",{get:function(){return this._container.visible},set:function(a){this._container.visible=a}}),Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"x",{get:function(){return this.emitX},set:function(a){this.emitX=a}}),Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"y",{get:function(){return this.emitY},set:function(a){this.emitY=a}}),Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"left",{get:function(){return Math.floor(this.x-this.width/2)}}),Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"right",{get:function(){return Math.floor(this.x+this.width/2)}}),Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"top",{get:function(){return Math.floor(this.y-this.height/2)}}),Object.defineProperty(c.Particles.Arcade.Emitter.prototype,"bottom",{get:function(){return Math.floor(this.y+this.height/2)}}),c.Tile=function(a,b,c,d,e,f){this.layer=a,this.index=b,this.x=c,this.y=d,this.width=e,this.height=f,this.alpha=1,this.properties={},this.scanned=!1,this.faceTop=!1,this.faceBottom=!1,this.faceLeft=!1,this.faceRight=!1,this.collides=!1,this.collideNone=!0,this.collideLeft=!1,this.collideRight=!1,this.collideUp=!1,this.collideDown=!1,this.callback=null,this.callbackContext=this},c.Tile.prototype={setCollisionCallback:function(a,b){this.collisionCallbackContext=b,this.collisionCallback=a},destroy:function(){this.collisionCallback=null,this.collisionCallbackContext=null,this.properties=null},setCollision:function(a,b,c,d){this.collideLeft=a,this.collideRight=b,this.collideUp=c,this.collideDown=d,this.collideNone=a||b||c||d?!1:!0},resetCollision:function(){this.collideNone=!0,this.collideLeft=!1,this.collideRight=!1,this.collideUp=!1,this.collideDown=!1},copy:function(a){this.index=a.index,this.alpha=a.alpha,this.properties=a.properties,this.collides=a.collides,this.collideNone=a.collideNone,this.collideUp=a.collideUp,this.collideDown=a.collideDown,this.collideLeft=a.collideLeft,this.collideRight=a.collideRight,this.collisionCallback=a.collisionCallback,this.collisionCallbackContext=a.collisionCallbackContext}},c.Tile.prototype.constructor=c.Tile,Object.defineProperty(c.Tile.prototype,"canCollide",{get:function(){return this.collides||this.collisionCallback||this.layer.callbacks[this.index]}}),Object.defineProperty(c.Tile.prototype,"left",{get:function(){return this.x}}),Object.defineProperty(c.Tile.prototype,"right",{get:function(){return this.x+this.width}}),Object.defineProperty(c.Tile.prototype,"top",{get:function(){return this.y}}),Object.defineProperty(c.Tile.prototype,"bottom",{get:function(){return this.y+this.height}}),c.Tilemap=function(a,b){this.game=a,this.key=b;var d=c.TilemapParser.parse(this.game,b);null!==d&&(this.width=d.width,this.height=d.height,this.tileWidth=d.tileWidth,this.tileHeight=d.tileHeight,this.orientation=d.orientation,this.version=d.version,this.properties=d.properties,this.widthInPixels=d.widthInPixels,this.heightInPixels=d.heightInPixels,this.layers=d.layers,this.tilesets=d.tilesets,this.tiles=d.tiles,this.objects=d.objects,this.images=d.images,this.currentLayer=0,this.debugMap=[],this._results=[],this._tempA=0,this._tempB=0)},c.Tilemap.CSV=0,c.Tilemap.TILED_JSON=1,c.Tilemap.prototype={create:function(a,b,d){for(var e=[],f=0;d>f;f++){e[f]=[];for(var g=0;b>g;g++)e[f][g]=0}this.layers.push({name:a,width:b,height:d,alpha:1,visible:!0,tileMargin:0,tileSpacing:0,format:c.Tilemap.CSV,data:e,indexes:[],dirty:!0}),this.currentLayer=this.layers.length-1},addTilesetImage:function(a,b){if("undefined"==typeof b){if("string"!=typeof a)return!1;b=a}return"string"==typeof a&&(a=this.getTilesetIndex(a)),this.tilesets[a]?(this.tilesets[a].image=this.game.cache.getImage(b),!0):!1},createFromTiles:function(a,b,c,d,e){"undefined"==typeof e&&(e=this.game.world)},createFromObjects:function(a,b,c,d,e,f,g){if("undefined"==typeof e&&(e=!0),"undefined"==typeof f&&(f=!0),"undefined"==typeof g&&(g=this.game.world),!this.objects[a])return console.warn("Tilemap.createFromObjects: Invalid objectgroup name given: "+a),void 0;for(var h,i=0,j=this.objects[a].length;j>i;i++)if(this.objects[a][i].gid===b){h=g.create(this.objects[a][i].x,this.objects[a][i].y,c,d,e),h.anchor.setTo(0,1),h.name=this.objects[a][i].name,h.visible=this.objects[a][i].visible,h.autoCull=f;for(property in this.objects[a][i].properties)g.set(h,property,this.objects[a][i].properties[property],!1,!1,0)}},createLayer:function(a,b,d,e){"undefined"==typeof b&&(b=this.game.width),"undefined"==typeof d&&(d=this.game.height),"undefined"==typeof e&&(e=this.game.world);var f=a;return"string"==typeof a&&(f=this.getLayerIndex(a)),null===f||f>this.layers.length?(console.warn("Tilemap.createLayer: Invalid layer ID given: "+f),void 0):e.add(new c.TilemapLayer(this.game,this,f,b,d))},getIndex:function(a,b){for(var c=0;ce;e++)this.layers[d].callbacks[a[e]]={callback:b,callbackContext:c}},setTileLocationCallback:function(a,b,c,d,e,f,g){if(g=this.getLayer(g),this.copy(a,b,c,d,g),!(this._results.length<2))for(var h=1;hd;d++)this.setCollisionByIndex(a[d],b,c,!1);this.calculateFaces(c)},setCollisionBetween:function(a,b,c,d){if("undefined"==typeof c&&(c=!0),d=this.getLayer(d),!(a>b)){for(var e=a;b>=e;e++)this.setCollisionByIndex(e,c,d,!1);this.calculateFaces(d)}},setCollisionByExclusion:function(a,b,c){"undefined"==typeof b&&(b=!0),c=this.getLayer(c);for(var d=0,e=this.tiles.length;e>d;d++)-1===a.indexOf(d)&&this.setCollisionByIndex(d,b,c,!1);this.calculateFaces(c)},setCollisionByIndex:function(a,b,c,d){"undefined"==typeof b&&(b=!0),"undefined"==typeof c&&(c=this.currentLayer),"undefined"==typeof d&&(d=!0);for(var e=0;ef;f++)for(var h=0,i=this.layers[a].width;i>h;h++){var j=this.layers[a].data[f][h];j&&(b=this.getTileAbove(a,h,f),c=this.getTileBelow(a,h,f),d=this.getTileLeft(a,h,f),e=this.getTileRight(a,h,f),b&&b.collides&&(j.faceTop=!1),c&&c.collides&&(j.faceBottom=!1),d&&d.collides&&(j.faceLeft=!1),e&&e.collides&&(j.faceRight=!1))}},getTileAbove:function(a,b,c){return c>0?this.layers[a].data[c-1][b]:null},getTileBelow:function(a,b,c){return c0?this.layers[a].data[c][b-1]:null},getTileRight:function(a,b,c){return b=0&&b=0&&d=0&&a=0&&ba&&(a=0),0>b&&(b=0),c>this.layers[e].width&&(c=this.layers[e].width),d>this.layers[e].height&&(d=this.layers[e].height),this._results.length=0,this._results.push({x:a,y:b,width:c,height:d,layer:e});for(var f=b;b+d>f;f++)for(var g=a;a+c>g;g++)this._results.push(this.layers[e].data[f][g]);return this._results},paste:function(a,b,c,d){if("undefined"==typeof a&&(a=0),"undefined"==typeof b&&(b=0),d=this.getLayer(d),c&&!(c.length<2)){for(var e=c[1].x-a,f=c[1].y-b,g=1;g1?this.debugMap[this.layers[this.currentLayer].data[c][d]]?b.push("background: "+this.debugMap[this.layers[this.currentLayer].data[c][d]]):b.push("background: #ffffff"):b.push("background: rgb(0, 0, 0)");a+="\n"}b[0]=a,console.log.apply(console,b)},destroy:function(){this.removeAllLayers(),this.data=[],this.game=null}},c.Tilemap.prototype.constructor=c.Tilemap,c.TilemapLayer=function(a,d,e,f,g){this.game=a,this.map=d,this.index=e,this.layer=d.layers[e],this.canvas=c.Canvas.create(f,g),this.context=this.canvas.getContext("2d"),this.baseTexture=new b.BaseTexture(this.canvas),this.texture=new b.Texture(this.baseTexture),this.textureFrame=new c.Frame(0,0,0,f,g,"tilemapLayer",a.rnd.uuid()),c.Sprite.call(this,this.game,0,0,this.texture,this.textureFrame),this.name="",this.type=c.TILEMAPLAYER,this.fixedToCamera=!0,this.cameraOffset=new c.Point(0,0),this.tileColor="rgb(255, 255, 255)",this.debug=!1,this.debugAlpha=.5,this.debugColor="rgba(0, 255, 0, 1)",this.debugFill=!1,this.debugFillColor="rgba(0, 255, 0, 0.2)",this.debugCallbackColor="rgba(255, 0, 0, 1)",this.scrollFactorX=1,this.scrollFactorY=1,this.dirty=!0,this._cw=d.tileWidth,this._ch=d.tileHeight,this._ga=1,this._dx=0,this._dy=0,this._dw=0,this._dh=0,this._tx=0,this._ty=0,this._tw=0,this._th=0,this._tl=0,this._maxX=0,this._maxY=0,this._startX=0,this._startY=0,this._results=[],this._x=0,this._y=0,this._prevX=0,this._prevY=0,this.updateMax()},c.TilemapLayer.prototype=Object.create(c.Sprite.prototype),c.TilemapLayer.prototype=c.Utils.extend(!0,c.TilemapLayer.prototype,c.Sprite.prototype,b.Sprite.prototype),c.TilemapLayer.prototype.constructor=c.TilemapLayer,c.TilemapLayer.prototype.postUpdate=function(){c.Sprite.prototype.postUpdate.call(this),this.scrollX=this.game.camera.x*this.scrollFactorX,this.scrollY=this.game.camera.y*this.scrollFactorY,this.render()},c.TilemapLayer.prototype.resizeWorld=function(){this.game.world.setBounds(0,0,this.layer.widthInPixels,this.layer.heightInPixels)},c.TilemapLayer.prototype._fixX=function(a){return 0>a&&(a=0),1===this.scrollFactorX?a:this._x+(a-this._x/this.scrollFactorX)},c.TilemapLayer.prototype._unfixX=function(a){return 1===this.scrollFactorX?a:this._x/this.scrollFactorX+(a-this._x)},c.TilemapLayer.prototype._fixY=function(a){return 0>a&&(a=0),1===this.scrollFactorY?a:this._y+(a-this._y/this.scrollFactorY)},c.TilemapLayer.prototype._unfixY=function(a){return 1===this.scrollFactorY?a:this._y/this.scrollFactorY+(a-this._y)},c.TilemapLayer.prototype.getTileX=function(a){return this.game.math.snapToFloor(this._fixX(a),this.map.tileWidth)/this.map.tileWidth},c.TilemapLayer.prototype.getTileY=function(a){return this.game.math.snapToFloor(this._fixY(a),this.map.tileHeight)/this.map.tileHeight},c.TilemapLayer.prototype.getTileXY=function(a,b,c){return c.x=this.getTileX(a),c.y=this.getTileY(b),c},c.TilemapLayer.prototype.getTiles=function(a,b,c,d,e){"undefined"==typeof e&&(e=!1),a=this._fixX(a),b=this._fixY(b),c>this.layer.widthInPixels&&(c=this.layer.widthInPixels),d>this.layer.heightInPixels&&(d=this.layer.heightInPixels),this._tx=this.game.math.snapToFloor(a,this._cw)/this._cw,this._ty=this.game.math.snapToFloor(b,this._ch)/this._ch,this._tw=(this.game.math.snapToCeil(c,this._cw)+this._cw)/this._cw,this._th=(this.game.math.snapToCeil(d,this._ch)+this._ch)/this._ch,this._results.length=0;for(var f=this._ty;fthis.layer.width&&(this._maxX=this.layer.width),this._maxY>this.layer.height&&(this._maxY=this.layer.height)),this.dirty=!0},c.TilemapLayer.prototype.render=function(){if(this.layer.dirty&&(this.dirty=!0),this.dirty&&this.visible){this._prevX=this._dx,this._prevY=this._dy,this._dx=-(this._x-this._startX*this.map.tileWidth),this._dy=-(this._y-this._startY*this.map.tileHeight),this._tx=this._dx,this._ty=this._dy,this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this.context.fillStyle=this.tileColor;var a,d;this.debug&&(this.context.globalAlpha=this.debugAlpha);for(var e=this._startY,f=this._startY+this._maxY;f>e;e++){this._column=this.layer.data[e];for(var g=this._startX,h=this._startX+this._maxX;h>g;g++)this._column[g]&&(a=this._column[g],this.map.tiles[a.index]&&(d=this.map.tilesets[this.map.tiles[a.index][2]],d.image?(this.debug===!1&&a.alpha!==this.context.globalAlpha&&(this.context.globalAlpha=a.alpha),d.tileWidth!==this.map.tileWidth||d.tileHeight!==this.map.tileHeight?this.context.drawImage(this.map.tilesets[this.map.tiles[a.index][2]].image,this.map.tiles[a.index][0],this.map.tiles[a.index][1],d.tileWidth,d.tileHeight,Math.floor(this._tx),Math.floor(this._ty)-(d.tileHeight-this.map.tileHeight),d.tileWidth,d.tileHeight):this.context.drawImage(this.map.tilesets[this.map.tiles[a.index][2]].image,this.map.tiles[a.index][0],this.map.tiles[a.index][1],this.map.tileWidth,this.map.tileHeight,Math.floor(this._tx),Math.floor(this._ty),this.map.tileWidth,this.map.tileHeight),a.debug&&(this.context.fillStyle="rgba(0, 255, 0, 0.4)",this.context.fillRect(Math.floor(this._tx),Math.floor(this._ty),this.map.tileWidth,this.map.tileHeight))):this.context.fillRect(Math.floor(this._tx),Math.floor(this._ty),this.map.tileWidth,this.map.tileHeight))),this._tx+=this.map.tileWidth;this._tx=this._dx,this._ty+=this.map.tileHeight}return this.debug&&(this.context.globalAlpha=1,this.renderDebug()),this.game.renderType===c.WEBGL&&b.texturesToUpdate.push(this.baseTexture),this.dirty=!1,this.layer.dirty=!1,!0}},c.TilemapLayer.prototype.renderDebug=function(){this._tx=this._dx,this._ty=this._dy,this.context.strokeStyle=this.debugColor,this.context.fillStyle=this.debugFillColor;for(var a=this._startY,b=this._startY+this._maxY;b>a;a++){this._column=this.layer.data[a];for(var c=this._startX,d=this._startX+this._maxX;d>c;c++){var e=this._column[c];e&&(e.faceTop||e.faceBottom||e.faceLeft||e.faceRight)&&(this._tx=Math.floor(this._tx),this.debugFill&&this.context.fillRect(this._tx,this._ty,this._cw,this._ch),this.context.beginPath(),e.faceTop&&(this.context.moveTo(this._tx,this._ty),this.context.lineTo(this._tx+this._cw,this._ty)),e.faceBottom&&(this.context.moveTo(this._tx,this._ty+this._ch),this.context.lineTo(this._tx+this._cw,this._ty+this._ch)),e.faceLeft&&(this.context.moveTo(this._tx,this._ty),this.context.lineTo(this._tx,this._ty+this._ch)),e.faceRight&&(this.context.moveTo(this._tx+this._cw,this._ty),this.context.lineTo(this._tx+this._cw,this._ty+this._ch)),this.context.stroke()),e&&(e.collisionCallback||e.layer.callbacks[e.index])&&(this.context.fillStyle=this.debugCallbackColor,this.context.fillRect(this._tx,this._ty,this._cw,this._ch),this.context.fillStyle=this.debugFillColor),this._tx+=this.map.tileWidth}this._tx=this._dx,this._ty+=this.map.tileHeight}},Object.defineProperty(c.TilemapLayer.prototype,"scrollX",{get:function(){return this._x},set:function(a){a!==this._x&&a>=0&&this.layer.widthInPixels>this.width&&(this._x=a,this._x>this.layer.widthInPixels-this.width&&(this._x=this.layer.widthInPixels-this.width),this._startX=this.game.math.floor(this._x/this.map.tileWidth),this._startX<0&&(this._startX=0),this._startX+this._maxX>this.layer.width&&(this._startX=this.layer.width-this._maxX),this.dirty=!0)}}),Object.defineProperty(c.TilemapLayer.prototype,"scrollY",{get:function(){return this._y},set:function(a){a!==this._y&&a>=0&&this.layer.heightInPixels>this.height&&(this._y=a,this._y>this.layer.heightInPixels-this.height&&(this._y=this.layer.heightInPixels-this.height),this._startY=this.game.math.floor(this._y/this.map.tileHeight),this._startY<0&&(this._startY=0),this._startY+this._maxY>this.layer.height&&(this._startY=this.layer.height-this._maxY),this.dirty=!0)}}),Object.defineProperty(c.TilemapLayer.prototype,"collisionWidth",{get:function(){return this._cw},set:function(a){this._cw=a,this.dirty=!0}}),Object.defineProperty(c.TilemapLayer.prototype,"collisionHeight",{get:function(){return this._ch},set:function(a){this._ch=a,this.dirty=!0}}),c.TilemapParser={tileset:function(a,b,d,e,f,g,h,i,j){var k=a.cache.getTilesetImage(b);if(null===k)return console.warn("Phaser.TilemapParser.tileSet: Invalid image key given"),null;var l=k.width,m=k.height;return-1===h&&(h=Math.round(l/d)),-1===i&&(i=Math.round(m/e)),-1===j&&(j=h*i),0===l||0===m||d>l||e>m||0===j?(console.warn("Phaser.TilemapParser.tileSet: width/height zero or width/height < given tileWidth/tileHeight"),null):new c.Tileset(k,b,d,e,f,g,h,i,j)},parse:function(a,b){var d=a.cache.getTilemapData(b);return d?d.format===c.Tilemap.CSV?this.parseCSV(d.data):d.format===c.Tilemap.TILED_JSON?this.parseTiledJSON(d.data):void 0:{layers:[],objects:[],images:[],tilesets:[]}},parseCSV:function(a){a=a.trim();for(var b=[],c=a.split("\n"),d=c.length,e=0,f=0;fj;j++)a.layers[e].data[j]>0?h.push(new c.Tile(f,a.layers[e].data[j],g,i.length,a.tilewidth,a.tileheight)):h.push(null),g++,g===a.layers[e].width&&(i.push(h),g=0,h=[]);f.data=i,d.push(f)}b.layers=d;for(var l=[],e=0;eo;o++)if(a.layers[e].objects[o].gid){var p={gid:a.layers[e].objects[o].gid,name:a.layers[e].objects[o].name,x:a.layers[e].objects[o].x,y:a.layers[e].objects[o].y,visible:a.layers[e].objects[o].visible,properties:a.layers[e].objects[o].properties};n[a.layers[e].name].push(p)}}b.objects=n;for(var q=[],e=0;e0&&(b.Texture.frameUpdates.length=0)},b.CanvasRenderer.prototype.renderDisplayObject=function(a,d){var e=a.last._iNext;a=a.first;do if(a.visible||d)if(a.renderable&&0!==a.alpha){if(a instanceof b.Sprite)a.texture.frame&&(this.context.globalAlpha=a.worldAlpha,c.CANVAS_PX_ROUND?this.context.setTransform(a.worldTransform[0],a.worldTransform[3],a.worldTransform[1],a.worldTransform[4],Math.floor(a.worldTransform[2]),Math.floor(a.worldTransform[5])):this.context.setTransform(a.worldTransform[0],a.worldTransform[3],a.worldTransform[1],a.worldTransform[4],a.worldTransform[2],a.worldTransform[5]),a.texture.trimmed&&this.context.transform(1,0,0,1,a.texture.trim.x,a.texture.trim.y),this.smoothProperty&&this.scaleMode!==a.texture.baseTexture.scaleMode&&(this.scaleMode=a.texture.baseTexture.scaleMode,this.context[this.smoothProperty]=this.scaleMode===b.BaseTexture.SCALE_MODE.LINEAR),this.context.drawImage(a.texture.baseTexture.source,a.texture.frame.x,a.texture.frame.y,a.texture.frame.width,a.texture.frame.height,Math.floor(a.anchor.x*-a.texture.frame.width),Math.floor(a.anchor.y*-a.texture.frame.height),a.texture.frame.width,a.texture.frame.height));else if(a instanceof b.Strip)this.context.setTransform(a.worldTransform[0],a.worldTransform[3],a.worldTransform[1],a.worldTransform[4],a.worldTransform[2],a.worldTransform[5]),this.renderStrip(a);else if(a instanceof b.TilingSprite)this.context.setTransform(a.worldTransform[0],a.worldTransform[3],a.worldTransform[1],a.worldTransform[4],a.worldTransform[2],a.worldTransform[5]),this.renderTilingSprite(a);else if(a instanceof b.CustomRenderable)a.renderCanvas(this);else if(a instanceof b.Graphics)this.context.setTransform(a.worldTransform[0],a.worldTransform[3],a.worldTransform[1],a.worldTransform[4],a.worldTransform[2],a.worldTransform[5]),b.CanvasGraphics.renderGraphics(a,this.context);else if(a instanceof b.FilterBlock)if(a.open){this.context.save();var f=a.mask.alpha,g=a.mask.worldTransform;this.context.setTransform(g[0],g[3],g[1],g[4],g[2],g[5]),a.mask.worldAlpha=.5,this.context.worldAlpha=0,b.CanvasGraphics.renderGraphicsMask(a.mask,this.context),this.context.clip(),a.mask.worldAlpha=f}else this.context.restore();a=a._iNext}else a=a._iNext;else a=a.last._iNext;while(a!=e)},b.WebGLBatch.prototype.update=function(){for(var a,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r=0,s=this.head;s;){if(s.vcount===b.visibleCount){if(c=s.texture.frame.width,d=s.texture.frame.height,e=s.anchor.x,f=s.anchor.y,g=c*(1-e),h=c*-e,i=d*(1-f),j=d*-f,k=8*r,a=s.worldTransform,l=a[0],m=a[3],n=a[1],o=a[4],p=a[2],q=a[5],s.texture.trimmed&&(p+=s.texture.trim.x,q+=s.texture.trim.y),this.verticies[k+0]=l*h+n*j+p,this.verticies[k+1]=o*j+m*h+q,this.verticies[k+2]=l*g+n*j+p,this.verticies[k+3]=o*j+m*g+q,this.verticies[k+4]=l*g+n*i+p,this.verticies[k+5]=o*i+m*g+q,this.verticies[k+6]=l*h+n*i+p,this.verticies[k+7]=o*i+m*h+q,s.updateFrame||s.texture.updateFrame){this.dirtyUVS=!0;var t=s.texture,u=t.frame,v=t.baseTexture.width,w=t.baseTexture.height;this.uvs[k+0]=u.x/v,this.uvs[k+1]=u.y/w,this.uvs[k+2]=(u.x+u.width)/v,this.uvs[k+3]=u.y/w,this.uvs[k+4]=(u.x+u.width)/v,this.uvs[k+5]=(u.y+u.height)/w,this.uvs[k+6]=u.x/v,this.uvs[k+7]=(u.y+u.height)/w,s.updateFrame=!1}if(s.cacheAlpha!=s.worldAlpha){s.cacheAlpha=s.worldAlpha;var x=4*r;this.colors[x]=this.colors[x+1]=this.colors[x+2]=this.colors[x+3]=s.worldAlpha,this.dirtyColors=!0}}else k=8*r,this.verticies[k+0]=0,this.verticies[k+1]=0,this.verticies[k+2]=0,this.verticies[k+3]=0,this.verticies[k+4]=0,this.verticies[k+5]=0,this.verticies[k+6]=0,this.verticies[k+7]=0;r++,s=s.__next}},c});
\ No newline at end of file
+/*! Phaser v1.2 | (c) 2014 Photon Storm Ltd. */
+!function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b():a.Phaser=b()}(this,function(){!function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.p2=a():"undefined"!=typeof global?self.p2=a():"undefined"!=typeof self&&(self.p2=a())}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g0&&(e=1/Math.sqrt(e),a[0]=b[0]*e,a[1]=b[1]*e),a},d.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]},d.cross=function(a,b,c){var d=b[0]*c[1]-b[1]*c[0];return a[0]=a[1]=0,a[2]=d,a},d.lerp=function(a,b,c,d){var e=b[0],f=b[1];return a[0]=e+d*(c[0]-e),a[1]=f+d*(c[1]-f),a},d.transformMat2=function(a,b,c){var d=b[0],e=b[1];return a[0]=d*c[0]+e*c[1],a[1]=d*c[2]+e*c[3],a},d.forEach=function(){var a=new Float32Array(2);return function(b,c,d,e,f,g){var h,i;for(c||(c=2),d||(d=0),i=e?Math.min(e*c+d,b.length):b.length,h=d;i>h;h+=c)a[0]=b[h],a[1]=b[h+1],f(a,a,g),b[h]=a[0],b[h+1]=a[1];return b}}(),d.str=function(a){return"vec2("+a[0]+", "+a[1]+")"},"undefined"!=typeof c&&(c.vec2=d)},{}],3:[function(a,b){function c(){}var d=a("./Scalar");b.exports=c,c.lineInt=function(a,b,c){c=c||0;var e,f,g,h,i,j,k,l=[0,0];return e=a[1][1]-a[0][1],f=a[0][0]-a[1][0],g=e*a[0][0]+f*a[0][1],h=b[1][1]-b[0][1],i=b[0][0]-b[1][0],j=h*b[0][0]+i*b[0][1],k=e*i-h*f,d.eq(k,0,c)||(l[0]=(i*g-f*j)/k,l[1]=(e*j-h*g)/k),l},c.segmentsIntersect=function(a,b,c,d){var e=b[0]-a[0],f=b[1]-a[1],g=d[0]-c[0],h=d[1]-c[1];if(0==g*f-h*e)return!1;var i=(e*(c[1]-a[1])+f*(a[0]-c[0]))/(g*f-h*e),j=(g*(a[1]-c[1])+h*(c[0]-a[0]))/(h*e-g*f);return i>=0&&1>=i&&j>=0&&1>=j}},{"./Scalar":6}],4:[function(a,b){function c(){}b.exports=c,c.area=function(a,b,c){return(b[0]-a[0])*(c[1]-a[1])-(c[0]-a[0])*(b[1]-a[1])},c.left=function(a,b,d){return c.area(a,b,d)>0},c.leftOn=function(a,b,d){return c.area(a,b,d)>=0},c.right=function(a,b,d){return c.area(a,b,d)<0},c.rightOn=function(a,b,d){return c.area(a,b,d)<=0};var d=[],e=[];c.collinear=function(a,b,f,g){if(g){var h=d,i=e;h[0]=b[0]-a[0],h[1]=b[1]-a[1],i[0]=f[0]-b[0],i[1]=f[1]-b[1];var j=h[0]*i[0]+h[1]*i[1],k=Math.sqrt(h[0]*h[0]+h[1]*h[1]),l=Math.sqrt(i[0]*i[0]+i[1]*i[1]),m=Math.acos(j/(k*l));return g>m}return 0==c.area(a,b,f)},c.sqdist=function(a,b){var c=b[0]-a[0],d=b[1]-a[1];return c*c+d*d}},{}],5:[function(a,b){function c(){this.vertices=[]}function d(a,b,c,d,e){e=e||0;var f=b[1]-a[1],h=a[0]-b[0],i=f*a[0]+h*a[1],j=d[1]-c[1],k=c[0]-d[0],l=j*c[0]+k*c[1],m=f*k-j*h;return g.eq(m,0,e)?[0,0]:[(k*i-h*l)/m,(f*l-j*i)/m]}var e=a("./Line"),f=a("./Point"),g=a("./Scalar");b.exports=c,c.prototype.at=function(a){var b=this.vertices,c=b.length;return b[0>a?a%c+c:a%c]},c.prototype.first=function(){return this.vertices[0]},c.prototype.last=function(){return this.vertices[this.vertices.length-1]},c.prototype.clear=function(){this.vertices.length=0},c.prototype.append=function(a,b,c){if("undefined"==typeof b)throw new Error("From is not given!");if("undefined"==typeof c)throw new Error("To is not given!");if(b>c-1)throw new Error("lol1");if(c>a.vertices.length)throw new Error("lol2");if(0>b)throw new Error("lol3");for(var d=b;c>d;d++)this.vertices.push(a.vertices[d])},c.prototype.makeCCW=function(){for(var a=0,b=this.vertices,c=1;cb[a][0])&&(a=c);f.left(this.at(a-1),this.at(a),this.at(a+1))||this.reverse()},c.prototype.reverse=function(){for(var a=[],b=0,c=this.vertices.length;b!==c;b++)a.push(this.vertices.pop());this.vertices=a},c.prototype.isReflex=function(a){return f.right(this.at(a-1),this.at(a),this.at(a+1))};var h=[],i=[];c.prototype.canSee=function(a,b){var c,d,g=h,j=i;if(f.leftOn(this.at(a+1),this.at(a),this.at(b))&&f.rightOn(this.at(a-1),this.at(a),this.at(b)))return!1;d=f.sqdist(this.at(a),this.at(b));for(var k=0;k!==this.vertices.length;++k)if((k+1)%this.vertices.length!==a&&k!==a&&f.leftOn(this.at(a),this.at(b),this.at(k+1))&&f.rightOn(this.at(a),this.at(b),this.at(k))&&(g[0]=this.at(a),g[1]=this.at(b),j[0]=this.at(k),j[1]=this.at(k+1),c=e.lineInt(g,j),f.sqdist(this.at(a),c)a)for(var f=a;b>=f;f++)e.vertices.push(this.vertices[f]);else{for(var f=0;b>=f;f++)e.vertices.push(this.vertices[f]);for(var f=a;f0?this.slice(a):[this]},c.prototype.slice=function(a){if(0==a.length)return[this];if(a instanceof Array&&a.length&&a[0]instanceof Array&&2==a[0].length&&a[0][0]instanceof Array){for(var b=[this],c=0;cc;c++)if(e.segmentsIntersect(a[b],a[b+1],a[c],a[c+1]))return!1;for(var b=1;bh)return console.warn("quickDecomp: max level ("+h+") reached."),a;for(var x=0;xo&&(n=o,k=l,r=y))),f.left(v.at(x+1),v.at(x),v.at(y+1))&&f.rightOn(v.at(x+1),v.at(x),v.at(y))&&(l=d(v.at(x+1),v.at(x),v.at(y),v.at(y+1)),f.left(v.at(x-1),v.at(x),l)&&(o=f.sqdist(v.vertices[x],l),m>o&&(m=o,j=l,q=y)));if(r==(q+1)%this.vertices.length)l[0]=(k[0]+j[0])/2,l[1]=(k[1]+j[1])/2,e.push(l),q>x?(t.append(v,x,q+1),t.vertices.push(l),u.vertices.push(l),0!=r&&u.append(v,r,v.vertices.length),u.append(v,0,x+1)):(0!=x&&t.append(v,x,v.vertices.length),t.append(v,0,q+1),t.vertices.push(l),u.vertices.push(l),u.append(v,r,x+1));else{if(r>q&&(q+=this.vertices.length),p=Number.MAX_VALUE,r>q)return a;for(var y=r;q>=y;++y)f.leftOn(v.at(x-1),v.at(x),v.at(y))&&f.rightOn(v.at(x+1),v.at(x),v.at(y))&&(o=f.sqdist(v.at(x),v.at(y)),p>o&&(p=o,s=y%this.vertices.length));s>x?(t.append(v,x,s+1),0!=s&&u.append(v,s,w.length),u.append(v,0,x+1)):(0!=x&&t.append(v,x,w.length),t.append(v,0,s+1),u.append(v,s,x+1))}return t.vertices.length3&&c>=0;--c)f.collinear(this.at(c-1),this.at(c),this.at(c+1),a)&&(this.vertices.splice(c%this.vertices.length,1),c--,b++);return b}},{"./Line":3,"./Point":4,"./Scalar":6}],6:[function(a,b){function c(){}b.exports=c,c.eq=function(a,b,c){return c=c||0,Math.abs(a-b)