/** * @author Richard Davey * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} * * @overview * * Phaser - http://phaser.io * * v2.4.0 "Katar" - Built: Fri Jul 10 2015 17:25:54 * * By Richard Davey http://www.photonstorm.com @photonstorm * * Phaser is a fun, free and fast 2D game framework for making HTML5 games * for desktop and mobile web browsers, supporting Canvas and WebGL rendering. * * Phaser uses Pixi.js for rendering, created by Mat Groves http://matgroves.com @Doormat23 * Phaser uses p2.js for full-body physics, created by Stefan Hedman https://github.com/schteppe/p2.js @schteppe * Phaser contains a port of N+ Physics, converted by Richard Davey, original by http://www.metanetsoftware.com * * Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from which both Phaser * and my love of framework development originate. * * Follow development at http://phaser.io and on our forum * * "If you want your children to be intelligent, read them fairy tales." * "If you want them to be more intelligent, read them more fairy tales." * -- Albert Einstein */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ (function(){ var root = this; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The [pixi.js](http://www.pixijs.com/) module/namespace. * * @module PIXI */ /** * Namespace-class for [pixi.js](http://www.pixijs.com/). * * Contains assorted static properties and enumerations. * * @class PIXI * @static */ var PIXI = PIXI || {}; /** * @property {Number} WEBGL_RENDERER * @protected * @static */ PIXI.WEBGL_RENDERER = 0; /** * @property {Number} CANVAS_RENDERER * @protected * @static */ PIXI.CANVAS_RENDERER = 1; /** * Version of pixi that is loaded. * @property {String} VERSION * @static */ PIXI.VERSION = "v2.2.8"; // used to create uids for various pixi objects.. PIXI._UID = 0; if (typeof(Float32Array) != 'undefined') { PIXI.Float32Array = Float32Array; PIXI.Uint16Array = Uint16Array; // Uint32Array and ArrayBuffer only used by WebGL renderer // We can suppose that if WebGL is supported then typed arrays are supported too // as they predate WebGL support for all browsers: // see typed arrays support: http://caniuse.com/#search=TypedArrays // see WebGL support: http://caniuse.com/#search=WebGL PIXI.Uint32Array = Uint32Array; PIXI.ArrayBuffer = ArrayBuffer; } else { PIXI.Float32Array = Array; PIXI.Uint16Array = Array; } /** * @property {Number} PI_2 * @static */ PIXI.PI_2 = Math.PI * 2; /** * @property {Number} RAD_TO_DEG * @static */ PIXI.RAD_TO_DEG = 180 / Math.PI; /** * @property {Number} DEG_TO_RAD * @static */ PIXI.DEG_TO_RAD = Math.PI / 180; /** * @property {String} RETINA_PREFIX * @protected * @static */ PIXI.RETINA_PREFIX = "@2x"; /** * The default render options if none are supplied to * {{#crossLink "WebGLRenderer"}}{{/crossLink}} or {{#crossLink "CanvasRenderer"}}{{/crossLink}}. * * @property {Object} defaultRenderOptions * @property {Object} defaultRenderOptions.view=null * @property {Boolean} defaultRenderOptions.transparent=false * @property {Boolean} defaultRenderOptions.antialias=false * @property {Boolean} defaultRenderOptions.preserveDrawingBuffer=false * @property {Number} defaultRenderOptions.resolution=1 * @property {Boolean} defaultRenderOptions.clearBeforeRender=true * @property {Boolean} defaultRenderOptions.autoResize=false * @static */ PIXI.defaultRenderOptions = { view: null, transparent: false, antialias: false, preserveDrawingBuffer: false, resolution: 1, clearBeforeRender: true, autoResize: false }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The base class for all objects that are rendered on the screen. * This is an abstract class and should not be used on its own rather it should be extended. * * @class DisplayObject * @constructor */ PIXI.DisplayObject = function() { /** * The coordinate of the object relative to the local coordinates of the parent. * * @property position * @type Point */ this.position = new PIXI.Point(0, 0); /** * The scale factor of the object. * * @property scale * @type Point */ this.scale = new PIXI.Point(1, 1); /** * The transform callback is an optional callback that if set will be called at the end of the updateTransform method and sent two parameters: * This Display Objects worldTransform matrix and its parents transform matrix. Both are PIXI.Matrix object types. * The matrix are passed by reference and can be modified directly without needing to return them. * This ability allows you to check any of the matrix values and perform actions such as clamping scale or limiting rotation, regardless of the parent transforms. * * @property transformCallback * @type Function */ this.transformCallback = null; /** * The context under which the transformCallback is invoked. * * @property transformCallbackContext * @type Object */ this.transformCallbackContext = null; /** * The pivot point of the displayObject that it rotates around * * @property pivot * @type Point */ this.pivot = new PIXI.Point(0, 0); /** * The rotation of the object in radians. * * @property rotation * @type Number */ this.rotation = 0; /** * The opacity of the object. * * @property alpha * @type Number */ this.alpha = 1; /** * The visibility of the object. * * @property visible * @type Boolean */ this.visible = true; /** * This is the defined area that will pick up mouse / touch events. It is null by default. * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children) * * @property hitArea * @type Rectangle|Circle|Ellipse|Polygon */ this.hitArea = null; /** * Can this object be rendered * * @property renderable * @type Boolean */ this.renderable = false; /** * [read-only] The display object container that contains this display object. * * @property parent * @type DisplayObjectContainer * @readOnly */ this.parent = null; /** * [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage. * * @property stage * @type Stage * @readOnly */ this.stage = null; /** * [read-only] The multiplied alpha of the displayObject * * @property worldAlpha * @type Number * @readOnly */ this.worldAlpha = 1; /** * [read-only] Current transform of the object based on world (parent) factors * * @property worldTransform * @type Matrix * @readOnly * @private */ this.worldTransform = new PIXI.Matrix(); /** * The position of the Display Object based on the world transform. * This value is updated at the end of updateTransform and takes all parent transforms into account. * * @property worldPosition * @type Point * @readOnly */ this.worldPosition = new PIXI.Point(0, 0); /** * The scale of the Display Object based on the world transform. * This value is updated at the end of updateTransform and takes all parent transforms into account. * * @property worldScale * @type Point * @readOnly */ this.worldScale = new PIXI.Point(1, 1); /** * The rotation of the Display Object, in radians, based on the world transform. * This value is updated at the end of updateTransform and takes all parent transforms into account. * * @property worldRotation * @type Number * @readOnly */ this.worldRotation = 0; /** * cached sin rotation and cos rotation * * @property _sr * @type Number * @private */ this._sr = 0; /** * cached sin rotation and cos rotation * * @property _cr * @type Number * @private */ this._cr = 1; /** * The area the filter is applied to like the hitArea this is used as more of an optimisation * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle * * @property filterArea * @type Rectangle */ this.filterArea = null; /** * 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 _mask * @type Rectangle * @private */ this._mask = null; /** * Cached internal flag. * * @property _cacheAsBitmap * @type Boolean * @private */ this._cacheAsBitmap = false; /** * Cached internal flag. * * @property _cacheIsDirty * @type Boolean * @private */ this._cacheIsDirty = false; }; // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; /** * Destroy this DisplayObject. * Removes all references to transformCallbacks, its parent, the stage, filters, bounds, mask and cached Sprites. * * @method destroy */ PIXI.DisplayObject.prototype.destroy = function() { if (this.children) { var i = this.children.length; while (i--) { this.children[i].destroy(); } this.children = []; } this.transformCallback = null; this.transformCallbackContext = null; this.hitArea = null; this.parent = null; this.stage = null; this.worldTransform = null; this.filterArea = null; this._bounds = null; this._currentBounds = null; this._mask = null; // In case Pixi is still going to try and render it even though destroyed this.renderable = false; this._destroyCachedSprite(); }; /** * [read-only] Indicates if the sprite is globally 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.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 * @type Graphics */ Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { get: function() { return this._mask; }, set: function(value) { if (this._mask) this._mask.isMask = false; this._mask = value; if (this._mask) this._mask.isMask = true; } }); /** * Sets the filters for the displayObject. * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer. * To remove filters simply set this property to 'null' * @property filters * @type Array(Filter) */ Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { get: function() { return this._filters; }, set: function(value) { if (value) { // now put all the passes in one place.. var passes = []; for (var i = 0; i < value.length; i++) { var filterPasses = value[i].passes; for (var j = 0; j < filterPasses.length; j++) { passes.push(filterPasses[j]); } } // TODO change this as it is legacy this._filterBlock = { target: this, filterPasses: passes }; } this._filters = value; } }); /** * Set if this display object is cached as a bitmap. * This basically takes a snap shot of the display object as it is at that moment. It can provide a performance benefit for complex static displayObjects. * To remove simply set this property to 'null' * @property cacheAsBitmap * @type Boolean */ Object.defineProperty(PIXI.DisplayObject.prototype, 'cacheAsBitmap', { get: function() { return this._cacheAsBitmap; }, set: function(value) { if (this._cacheAsBitmap === value) return; if (value) { this._generateCachedSprite(); } else { this._destroyCachedSprite(); } this._cacheAsBitmap = value; } }); /* * Updates the object transform for rendering. * * If the object has no parent, and no parent parameter is provided, it will default to Phaser.Game.World as the parent. * If that is unavailable the transform fails to take place. * * The `parent` parameter has priority over the actual parent. Use it as a parent override. * Setting it does **not** change the actual parent of this DisplayObject, it just uses the parent for the transform update. * * @method updateTransform * @param {DisplayObject} [parent] - Optional parent to parent this DisplayObject transform from. */ PIXI.DisplayObject.prototype.updateTransform = function(parent) { if (!parent && !this.parent && !this.game) { return; } var p = this.parent; if (parent) { p = parent; } else if (!this.parent) { p = this.game.world; } // create some matrix refs for easy access var pt = p.worldTransform; var wt = this.worldTransform; // temporary matrix variables var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. if (this.rotation % PIXI.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes if (this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. a = this._cr * this.scale.x; b = this._sr * this.scale.x; c = -this._sr * this.scale.y; d = this._cr * this.scale.y; tx = this.position.x; ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) { tx -= this.pivot.x * a + this.pivot.y * c; ty -= this.pivot.x * b + this.pivot.y * d; } // concat the parent matrix with the objects transform. wt.a = a * pt.a + b * pt.c; wt.b = a * pt.b + b * pt.d; wt.c = c * pt.a + d * pt.c; wt.d = c * pt.b + d * pt.d; wt.tx = tx * pt.a + ty * pt.c + pt.tx; wt.ty = tx * pt.b + ty * pt.d + pt.ty; } else { // lets do the fast version as we know there is no rotation.. a = this.scale.x; d = this.scale.y; tx = this.position.x - this.pivot.x * a; ty = this.position.y - this.pivot.y * d; wt.a = a * pt.a; wt.b = a * pt.b; wt.c = d * pt.c; wt.d = d * pt.d; wt.tx = tx * pt.a + ty * pt.c + pt.tx; wt.ty = tx * pt.b + ty * pt.d + pt.ty; } // multiply the alphas.. this.worldAlpha = this.alpha * p.worldAlpha; this.worldPosition.set(wt.tx, wt.ty); this.worldScale.set(Math.sqrt(wt.a * wt.a + wt.b * wt.b), Math.sqrt(wt.c * wt.c + wt.d * wt.d)); this.worldRotation = Math.atan2(-wt.c, wt.d); // Custom callback? if (this.transformCallback) { this.transformCallback.call(this.transformCallbackContext, wt, pt); } }; // performance increase to avoid using call.. (10x faster) PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform; /** * Retrieves the bounds of the displayObject as a rectangle object * * @method getBounds * @param matrix {Matrix} * @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; }; /** * Empty, to be overridden by classes that require it. * * @method preUpdate */ PIXI.DisplayObject.prototype.preUpdate = function() { }; /** * Useful function that returns a texture of the displayObject object that can then be used to create sprites * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. * * @method generateTexture * @param resolution {Number} The resolution of the texture being generated * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used to generate the texture. * @return {Texture} a texture of the graphics object */ PIXI.DisplayObject.prototype.generateTexture = function(resolution, scaleMode, renderer) { var bounds = this.getLocalBounds(); var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); PIXI.DisplayObject._tempMatrix.tx = -bounds.x; PIXI.DisplayObject._tempMatrix.ty = -bounds.y; renderTexture.render(this, PIXI.DisplayObject._tempMatrix); return renderTexture; }; /** * Generates and updates the cached sprite for this object. * * @method updateCache */ PIXI.DisplayObject.prototype.updateCache = function() { this._generateCachedSprite(); }; /** * Calculates the global position of the display object * * @method toGlobal * @param position {Point} The world origin to calculate from * @return {Point} A point object representing the position of this object */ PIXI.DisplayObject.prototype.toGlobal = function(position) { // don't need to u[date the lot this.displayObjectUpdateTransform(); return this.worldTransform.apply(position); }; /** * Calculates the local position of the display object relative to another point * * @method toLocal * @param position {Point} The world origin to calculate from * @param [from] {DisplayObject} The DisplayObject to calculate the global position from * @return {Point} A point object representing the position of this object */ PIXI.DisplayObject.prototype.toLocal = function(position, from) { if (from) { position = from.toGlobal(position); } // don't need to u[date the lot this.displayObjectUpdateTransform(); return this.worldTransform.applyInverse(position); }; /** * Internal method. * * @method _renderCachedSprite * @param renderSession {Object} The render session * @private */ PIXI.DisplayObject.prototype._renderCachedSprite = function(renderSession) { this._cachedSprite.worldAlpha = this.worldAlpha; if (renderSession.gl) { PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); } else { PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); } }; /** * Internal method. * * @method _generateCachedSprite * @private */ PIXI.DisplayObject.prototype._generateCachedSprite = function() { 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; PIXI.DisplayObject._tempMatrix.tx = -bounds.x; PIXI.DisplayObject._tempMatrix.ty = -bounds.y; this._cachedSprite.texture.render(this, PIXI.DisplayObject._tempMatrix, true); this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); this._filters = tempFilters; this._cacheAsBitmap = true; }; /** * Destroys the cached sprite. * * @method _destroyCachedSprite * @private */ PIXI.DisplayObject.prototype._destroyCachedSprite = function() { if (!this._cachedSprite) return; this._cachedSprite.texture.destroy(true); // TODO could be object pooled! this._cachedSprite = null; }; /** * Renders the object using the WebGL renderer * * @method _renderWebGL * @param renderSession {RenderSession} * @private */ 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 */ /** * A DisplayObjectContainer represents a collection of display objects. * It is the base class of all display objects that act as a container for other objects. * * @class DisplayObjectContainer * @extends DisplayObject * @constructor */ PIXI.DisplayObjectContainer = function() { PIXI.DisplayObject.call(this); /** * [read-only] The array of children of this container. * * @property children * @type Array(DisplayObject) * @readOnly */ this.children = []; }; // constructor 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) { var width = this.getLocalBounds().width; if (width !== 0) { this.scale.x = value / width; } else { this.scale.x = 1; } 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) { var height = this.getLocalBounds().height; if (height !== 0) { this.scale.y = value / height; } else { this.scale.y = 1; } this._height = value; } }); /** * Adds a child to the container. * * @method addChild * @param child {DisplayObject} The DisplayObject to add to the container * @return {DisplayObject} The child that was added. */ PIXI.DisplayObjectContainer.prototype.addChild = function(child) { return this.addChildAt(child, this.children.length); }; /** * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown * * @method addChildAt * @param child {DisplayObject} The child to add * @param index {Number} The index to place the child in * @return {DisplayObject} The child that was added. */ PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) { if(index >= 0 && index <= this.children.length) { if(child.parent) { child.parent.removeChild(child); } child.parent = this; this.children.splice(index, 0, child); if(this.stage)child.setStageReference(this.stage); return child; } else { throw new Error(child + 'addChildAt: The index '+ index +' supplied is out of bounds ' + this.children.length); } }; /** * Swaps the position of 2 Display Objects within this container. * * @method swapChildren * @param child {DisplayObject} * @param child2 {DisplayObject} */ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) { if(child === child2) { return; } var index1 = this.getChildIndex(child); var index2 = this.getChildIndex(child2); if(index1 < 0 || index2 < 0) { throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); } this.children[index1] = child2; this.children[index2] = child; }; /** * Returns the index position of a child DisplayObject instance * * @method getChildIndex * @param child {DisplayObject} The DisplayObject instance to identify * @return {Number} The index position of the child display object to identify */ PIXI.DisplayObjectContainer.prototype.getChildIndex = function(child) { var index = this.children.indexOf(child); if (index === -1) { throw new Error('The supplied DisplayObject must be a child of the caller'); } return index; }; /** * Changes the position of an existing child in the display object container * * @method setChildIndex * @param child {DisplayObject} The child DisplayObject instance for which you want to change the index number * @param index {Number} The resulting index number for the child display object */ PIXI.DisplayObjectContainer.prototype.setChildIndex = function(child, index) { if (index < 0 || index >= this.children.length) { throw new Error('The supplied index is out of bounds'); } var currentIndex = this.getChildIndex(child); this.children.splice(currentIndex, 1); //remove from old position this.children.splice(index, 0, child); //add at new position }; /** * Returns the child at the specified index * * @method getChildAt * @param index {Number} The index to get the child from * @return {DisplayObject} The child at the given index, if any. */ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) { if (index < 0 || index >= this.children.length) { throw new Error('getChildAt: Supplied index '+ index +' does not exist in the child list, or the supplied DisplayObject must be a child of the caller'); } return this.children[index]; }; /** * Removes a child from the container. * * @method removeChild * @param child {DisplayObject} The DisplayObject to remove * @return {DisplayObject} The child that was removed. */ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { var index = this.children.indexOf( child ); if(index === -1)return; return this.removeChildAt( index ); }; /** * Removes a child from the specified index position. * * @method removeChildAt * @param index {Number} The index to get the child from * @return {DisplayObject} The child that was removed. */ PIXI.DisplayObjectContainer.prototype.removeChildAt = function(index) { var child = this.getChildAt( index ); if(this.stage) child.removeStageReference(); child.parent = undefined; this.children.splice( index, 1 ); return child; }; /** * Removes all children from this container that are within the begin and end indexes. * * @method removeChildren * @param beginIndex {Number} The beginning position. Default value is 0. * @param endIndex {Number} The ending position. Default value is size of the container. */ PIXI.DisplayObjectContainer.prototype.removeChildren = function(beginIndex, endIndex) { var begin = beginIndex || 0; var end = typeof endIndex === 'number' ? endIndex : this.children.length; var range = end - begin; if (range > 0 && range <= end) { var removed = this.children.splice(begin, range); for (var i = 0; i < removed.length; i++) { var child = removed[i]; if(this.stage) child.removeStageReference(); child.parent = undefined; } return removed; } else if (range === 0 && this.children.length === 0) { return []; } else { throw new Error( 'removeChildren: Range Error, numeric values are outside the acceptable range' ); } }; /* * Updates the transform on all children of this container for rendering * * @method updateTransform * @private */ PIXI.DisplayObjectContainer.prototype.updateTransform = function() { if (!this.visible) { return; } this.displayObjectUpdateTransform(); if (this._cacheAsBitmap) { return; } for (var i = 0; i < this.children.length; i++) { this.children[i].updateTransform(); } }; // performance increase to avoid using call.. (10x faster) PIXI.DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = PIXI.DisplayObjectContainer.prototype.updateTransform; /** * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. * * @method getBounds * @return {Rectangle} The rectangular bounding area */ PIXI.DisplayObjectContainer.prototype.getBounds = function() { if(this.children.length === 0)return PIXI.EmptyRectangle; // TODO the bounds have already been calculated this render session so return what we have var minX = Infinity; var minY = Infinity; var maxX = -Infinity; var maxY = -Infinity; var childBounds; var childMaxX; var childMaxY; var childVisible = false; 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; }; /** * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. The calculation takes all visible children into consideration. * * @method getLocalBounds * @return {Rectangle} The rectangular bounding area */ 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} * @param {Matrix} [matrix] - Optional matrix. If provided the Display Object will be rendered using this matrix, otherwise it will use its worldTransform. * @private */ PIXI.Sprite.prototype._renderWebGL = function(renderSession, matrix) { // if the sprite is not visible or the alpha is 0 then no need to render this element if (!this.visible || this.alpha <= 0 || !this.renderable) return; // They provided an alternative rendering matrix, so use it var wt = this.worldTransform; if (matrix) { wt = matrix; } // A quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { var spriteBatch = renderSession.spriteBatch; // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { spriteBatch.flush(); renderSession.filterManager.pushFilter(this._filterBlock); } if (this._mask) { spriteBatch.stop(); renderSession.maskManager.pushMask(this.mask, renderSession); spriteBatch.start(); } // add this sprite to the batch spriteBatch.render(this); // now loop through the children and make sure they get rendered for (var i = 0; i < this.children.length; i++) { this.children[i]._renderWebGL(renderSession); } // time to stop the sprite batch as either a mask element or a filter draw will happen next spriteBatch.stop(); if (this._mask) renderSession.maskManager.popMask(this._mask, renderSession); if (this._filters) renderSession.filterManager.popFilter(); spriteBatch.start(); } else { renderSession.spriteBatch.render(this); // Render children! for (var i = 0; i < this.children.length; i++) { this.children[i]._renderWebGL(renderSession, wt); } } }; /** * Renders the object using the Canvas renderer * * @method _renderCanvas * @param renderSession {RenderSession} * @param {Matrix} [matrix] - Optional matrix. If provided the Display Object will be rendered using this matrix, otherwise it will use its worldTransform. * @private */ PIXI.Sprite.prototype._renderCanvas = function(renderSession, matrix) { // 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.renderable === false || this.texture.crop.width <= 0 || this.texture.crop.height <= 0) { return; } var wt = this.worldTransform; // If they provided an alternative rendering matrix then use it if (matrix) { wt = matrix; } if (this.blendMode !== renderSession.currentBlendMode) { renderSession.currentBlendMode = this.blendMode; renderSession.context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; } if (this._mask) { renderSession.maskManager.pushMask(this._mask, renderSession); } // Ignore null sources if (this.texture.valid) { var resolution = this.texture.baseTexture.resolution / renderSession.resolution; renderSession.context.globalAlpha = this.worldAlpha; // If smoothingEnabled is supported and we need to change the smoothing property for this texture if (renderSession.smoothProperty && renderSession.scaleMode !== this.texture.baseTexture.scaleMode) { renderSession.scaleMode = this.texture.baseTexture.scaleMode; renderSession.context[renderSession.smoothProperty] = (renderSession.scaleMode === PIXI.scaleModes.LINEAR); } // If the texture is trimmed we offset by the trim x/y, otherwise we use the frame dimensions var dx = (this.texture.trim) ? this.texture.trim.x - this.anchor.x * this.texture.trim.width : this.anchor.x * -this.texture.frame.width; var dy = (this.texture.trim) ? this.texture.trim.y - this.anchor.y * this.texture.trim.height : this.anchor.y * -this.texture.frame.height; // Allow for pixel rounding if (renderSession.roundPixels) { renderSession.context.setTransform(wt.a, wt.b, wt.c, wt.d, (wt.tx * renderSession.resolution) | 0, (wt.ty * renderSession.resolution) | 0); dx = dx | 0; dy = dy | 0; } else { renderSession.context.setTransform(wt.a, wt.b, wt.c, wt.d, wt.tx * renderSession.resolution, wt.ty * renderSession.resolution); } var cw = this.texture.crop.width; var ch = this.texture.crop.height; dx /= resolution; dy /= resolution; if (this.tint !== 0xFFFFFF) { if (this.texture.requiresReTint || this.cachedTint !== this.tint) { this.tintedTexture = PIXI.CanvasTinter.getTintedTexture(this, this.tint); this.cachedTint = this.tint; } renderSession.context.drawImage(this.tintedTexture, 0, 0, cw, ch, dx, dy, cw / resolution, ch / resolution); } else { var cx = this.texture.crop.x; var cy = this.texture.crop.y; renderSession.context.drawImage(this.texture.baseTexture.source, cx, cy, cw, ch, dx, dy, cw / resolution, ch / resolution); } } for (var i = 0; i < this.children.length; i++) { this.children[i]._renderCanvas(renderSession); } if (this._mask) { renderSession.maskManager.popMask(renderSession); } }; // some helper functions.. /** * * Helper function that creates a sprite that will contain a texture from the TextureCache based on the frameId * The frame ids are created when a Texture packer file has been loaded * * @method fromFrame * @static * @param frameId {String} The frame Id of the texture in the cache * @return {Sprite} A new Sprite using a texture from the texture cache matching the frameId */ PIXI.Sprite.fromFrame = function(frameId) { var texture = PIXI.TextureCache[frameId]; if (!texture) throw new Error('The frameId "' + frameId + '" does not exist in the texture cache' + this); return new PIXI.Sprite(texture); }; /** * * Helper function that creates a sprite that will contain a texture based on an image url * If the image is not in the texture cache it will be loaded * * @method fromImage * @static * @param imageId {String} The image url of the texture * @return {Sprite} A new Sprite using a texture from the texture cache matching the image id */ PIXI.Sprite.fromImage = function(imageId, crossorigin, scaleMode) { var texture = PIXI.Texture.fromImage(imageId, crossorigin, scaleMode); return new PIXI.Sprite(texture); }; /** * @author Mat Groves http://matgroves.com/ */ /** * 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. * And it's extremely easy to use : var container = new PIXI.SpriteBatch(); stage.addChild(container); for(var i = 0; i < 100; i++) { var sprite = new PIXI.Sprite.fromImage("myImage.png"); container.addChild(sprite); } * And here you have a hundred sprites that will be renderer at the speed of light * * @class SpriteBatch * @constructor * @param texture {Texture} */ PIXI.SpriteBatch = function(texture) { PIXI.DisplayObjectContainer.call( this); this.textureThing = texture; this.ready = false; }; PIXI.SpriteBatch.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.SpriteBatch.prototype.constructor = PIXI.SpriteBatch; /* * Initialises the spriteBatch * * @method initWebGL * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.SpriteBatch.prototype.initWebGL = function(gl) { // TODO only one needed for the whole engine really? this.fastSpriteBatch = new PIXI.WebGLFastSpriteBatch(gl); this.ready = true; }; /* * Updates the object transform for rendering * * @method updateTransform * @private */ PIXI.SpriteBatch.prototype.updateTransform = function() { // TODO don't need to! this.displayObjectUpdateTransform(); // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); }; /** * Renders the object using the WebGL renderer * * @method _renderWebGL * @param renderSession {RenderSession} * @private */ PIXI.SpriteBatch.prototype._renderWebGL = function(renderSession) { if (!this.visible || this.alpha <= 0 || !this.children.length) return; if (!this.ready) { this.initWebGL(renderSession.gl); } if (this.fastSpriteBatch.gl !== renderSession.gl) { this.fastSpriteBatch.setContext(renderSession.gl); } renderSession.spriteBatch.stop(); renderSession.shaderManager.setShader(renderSession.shaderManager.fastShader); this.fastSpriteBatch.begin(this, renderSession); this.fastSpriteBatch.render(this); renderSession.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * * @method _renderCanvas * @param renderSession {RenderSession} * @private */ PIXI.SpriteBatch.prototype._renderCanvas = function(renderSession) { if (!this.visible || this.alpha <= 0 || !this.children.length) return; var context = renderSession.context; context.globalAlpha = this.worldAlpha; this.displayObjectUpdateTransform(); var transform = this.worldTransform; var isRotated = true; for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (!child.visible) continue; var texture = child.texture; var frame = texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; if (child.rotation % (Math.PI * 2) === 0) { if (isRotated) { context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); isRotated = false; } // this is the fastest way to optimise! - if rotation is 0 then we can avoid any kind of setTransform call context.drawImage(texture.baseTexture.source, frame.x, frame.y, frame.width, frame.height, ((child.anchor.x) * (-frame.width * child.scale.x) + child.position.x + 0.5) | 0, ((child.anchor.y) * (-frame.height * child.scale.y) + child.position.y + 0.5) | 0, frame.width * child.scale.x, frame.height * child.scale.y); } else { if (!isRotated) isRotated = true; child.displayObjectUpdateTransform(); var childTransform = child.worldTransform; // allow for trimming if (renderSession.roundPixels) { context.setTransform(childTransform.a, childTransform.b, childTransform.c, childTransform.d, childTransform.tx | 0, childTransform.ty | 0); } else { context.setTransform(childTransform.a, childTransform.b, childTransform.c, childTransform.d, childTransform.tx, childTransform.ty); } context.drawImage(texture.baseTexture.source, frame.x, frame.y, frame.width, frame.height, ((child.anchor.x) * (-frame.width) + 0.5) | 0, ((child.anchor.y) * (-frame.height) + 0.5) | 0, frame.width, frame.height); } } }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * A Stage represents the root of the display tree. Everything connected to the stage is rendered * * @class Stage * @extends DisplayObjectContainer * @constructor * @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) { PIXI.DisplayObjectContainer.call( this ); /** * [read-only] Current transform of the object based on world (parent) factors * * @property worldTransform * @type Matrix * @readOnly * @private */ this.worldTransform = new PIXI.Matrix(); //the stage is its own stage this.stage = this; this.setBackgroundColor(backgroundColor); }; // constructor PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Stage.prototype.constructor = PIXI.Stage; /* * Updates the object transform for rendering * * @method updateTransform * @private */ PIXI.Stage.prototype.updateTransform = function() { this.worldAlpha = 1; for (var i = 0; i < this.children.length; i++) { this.children[i].updateTransform(); } }; /** * Sets the background color for the stage * * @method setBackgroundColor * @param backgroundColor {Number} the color of the background, easiest way to pass this in is in hex format * like: 0xFFFFFF for white */ PIXI.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; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * 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]; }; /** * 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 for drawImage * * @method canUseNewCanvasBlendModes * @return {Boolean} whether they are supported */ PIXI.canUseNewCanvasBlendModes = function() { if (typeof document === 'undefined') return false; var pngHead = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABAQMAAADD8p2OAAAAA1BMVEX/'; var pngEnd = 'AAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=='; var magenta = new Image(); magenta.src = pngHead + 'AP804Oa6' + pngEnd; var yellow = new Image(); yellow.src = pngHead + '/wCKxvRF' + pngEnd; var canvas = document.createElement('canvas'); canvas.width = 6; canvas.height = 1; var context = canvas.getContext('2d'); context.globalCompositeOperation = 'multiply'; context.drawImage(magenta, 0, 0); context.drawImage(yellow, 2, 0); if (!context.getImageData(2,0,1,1)) { return false; } var data = context.getImageData(2,0,1,1).data; return (data[0] === 255 && data[1] === 0 && data[2] === 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; } }; /** * checks if the given width and height make a power of two texture * @method isPowerOfTwo * @param width {Number} * @param height {Number} * @return {Boolean} */ PIXI.isPowerOfTwo = function(width, height) { return (width > 0 && (width & (width - 1)) === 0 && height > 0 && (height & (height - 1)) === 0); }; /* 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); */ /** * 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 */ PIXI.PolyK = {}; /** * Triangulates shapes for webGL graphic fills. * * @method Triangulate */ PIXI.PolyK.Triangulate = function(p) { var sign = true; var n = p.length >> 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 null; } } } tgs.push(avl[0], avl[1], avl[2]); return tgs; }; /** * 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 * @return {Boolean} */ 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 whether a shape is convex * * @method _convex * @private * @return {Boolean} */ PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign) { return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @method initDefaultShaders * @static * @private */ PIXI.initDefaultShaders = function() { }; /** * @method CompileVertexShader * @static * @param gl {WebGLContext} the current WebGL drawing context * @param shaderSrc {Array} * @return {Any} */ PIXI.CompileVertexShader = function(gl, shaderSrc) { return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); }; /** * @method CompileFragmentShader * @static * @param gl {WebGLContext} the current WebGL drawing context * @param shaderSrc {Array} * @return {Any} */ PIXI.CompileFragmentShader = function(gl, shaderSrc) { return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); }; /** * @method _CompileShader * @static * @private * @param gl {WebGLContext} the current WebGL drawing context * @param shaderSrc {Array} * @param shaderType {Number} * @return {Any} */ PIXI._CompileShader = function(gl, shaderSrc, shaderType) { var src = shaderSrc.join("\n"); var shader = gl.createShader(shaderType); gl.shaderSource(shader, src); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { window.console.log(gl.getShaderInfoLog(shader)); return null; } return shader; }; /** * @method compileProgram * @static * @param gl {WebGLContext} the current WebGL drawing context * @param vertexSrc {Array} * @param fragmentSrc {Array} * @return {Any} */ PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) { var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc); var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { window.console.log("Could not initialise shaders"); } return shaderProgram; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 * @author Richard Davey http://www.photonstorm.com @photonstorm */ /** * @class PixiShader * @constructor * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.PixiShader = function(gl) { /** * @property _UID * @type Number * @private */ this._UID = PIXI._UID++; /** * @property gl * @type WebGLContext */ this.gl = gl; /** * The WebGL program. * @property program * @type Any */ this.program = null; /** * The fragment shader. * @property fragmentSrc * @type Array */ this.fragmentSrc = [ 'precision lowp float;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', 'uniform sampler2D uSampler;', 'void main(void) {', ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', '}' ]; /** * A local texture counter for multi-texture shaders. * @property textureCount * @type Number */ this.textureCount = 0; /** * A local flag * @property firstRun * @type Boolean * @private */ this.firstRun = true; /** * A dirty flag * @property dirty * @type Boolean */ this.dirty = true; /** * Uniform attributes cache. * @property attributes * @type Array * @private */ this.attributes = []; this.init(); }; PIXI.PixiShader.prototype.constructor = PIXI.PixiShader; /** * Initialises the shader. * * @method init */ PIXI.PixiShader.prototype.init = function() { 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 this.uSampler = gl.getUniformLocation(program, 'uSampler'); this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); this.dimensions = gl.getUniformLocation(program, 'dimensions'); // get and store the attributes this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); 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. // I'm 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) { // get the uniform locations.. this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); } this.initUniforms(); this.program = program; }; /** * Initialises the shader uniform values. * * 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 initUniforms */ PIXI.PixiShader.prototype.initUniforms = function() { this.textureCount = 1; var gl = this.gl; var uniform; for (var key in this.uniforms) { uniform = this.uniforms[key]; var type = uniform.type; if (type === 'sampler2D') { uniform._init = false; if (uniform.value !== null) { this.initSampler2D(uniform); } } else if (type === 'mat2' || type === 'mat3' || type === 'mat4') { // These require special handling uniform.glMatrix = true; uniform.glValueLength = 1; if (type === 'mat2') { uniform.glFunc = gl.uniformMatrix2fv; } else if (type === 'mat3') { uniform.glFunc = gl.uniformMatrix3fv; } else if (type === 'mat4') { uniform.glFunc = gl.uniformMatrix4fv; } } else { // GL function reference uniform.glFunc = gl['uniform' + type]; if (type === '2f' || type === '2i') { uniform.glValueLength = 2; } else if (type === '3f' || type === '3i') { uniform.glValueLength = 3; } else if (type === '4f' || type === '4i') { uniform.glValueLength = 4; } else { uniform.glValueLength = 1; } } } }; /** * Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) * * @method initSampler2D */ PIXI.PixiShader.prototype.initSampler2D = function(uniform) { if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) { return; } var gl = this.gl; gl.activeTexture(gl['TEXTURE' + this.textureCount]); gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); // Extended texture data if (uniform.textureData) { var data = uniform.textureData; // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); // GLTextureLinear = mag/min linear, wrap clamp // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat // GLTextureNearest = mag/min nearest, wrap clamp // AudioTexture = whatever + luminance + width 512, height 2, border 0 // KeyTexture = whatever + luminance + width 256, height 2, border 0 // 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 : 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 = gl.REPEAT; wrapT = gl.REPEAT; } gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); if (data.width) { var width = (data.width) ? data.width : 512; var height = (data.height) ? data.height : 2; 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); 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); gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); } 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); } gl.uniform1i(uniform.uniformLocation, this.textureCount); uniform._init = true; this.textureCount++; }; /** * Updates the shader uniform values. * * @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) { uniform = this.uniforms[key]; if (uniform.glValueLength === 1) { if (uniform.glMatrix === true) { uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); } else { uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); } } else if (uniform.glValueLength === 2) { uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); } else if (uniform.glValueLength === 3) { uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); } else if (uniform.glValueLength === 4) { 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) { gl.activeTexture(gl['TEXTURE' + this.textureCount]); if(uniform.value.baseTexture._dirty[gl.id]) { PIXI.instances[gl.id].updateTexture(uniform.value.baseTexture); } else { // bind the current texture gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); } // 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 { this.initSampler2D(uniform); } } } }; /** * Destroys the shader. * * @method destroy */ PIXI.PixiShader.prototype.destroy = function() { this.gl.deleteProgram( this.program ); this.uniforms = null; this.gl = null; this.attributes = null; }; /** * The Default Vertex shader source. * * @property defaultVertexSrc * @type String */ PIXI.PixiShader.defaultVertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', '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 = vec4(aColor.rgb * aColor.a, aColor.a);', '}' ]; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class PixiFastShader * @constructor * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.PixiFastShader = function(gl) { /** * @property _UID * @type Number * @private */ this._UID = PIXI._UID++; /** * @property gl * @type WebGLContext */ this.gl = gl; /** * The WebGL program. * @property program * @type Any */ this.program = null; /** * The fragment shader. * @property fragmentSrc * @type Array */ this.fragmentSrc = [ 'precision lowp float;', 'varying vec2 vTextureCoord;', 'varying float vColor;', 'uniform sampler2D uSampler;', 'void main(void) {', ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', '}' ]; /** * The vertex shader. * @property vertexSrc * @type Array */ this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aPositionCoord;', 'attribute vec2 aScale;', 'attribute float aRotation;', 'attribute vec2 aTextureCoord;', 'attribute float aColor;', 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', 'uniform mat3 uMatrix;', 'varying vec2 vTextureCoord;', 'varying float vColor;', 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void) {', ' 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;', '}' ]; /** * A local texture counter for multi-texture shaders. * @property textureCount * @type Number */ this.textureCount = 0; this.init(); }; PIXI.PixiFastShader.prototype.constructor = PIXI.PixiFastShader; /** * Initialises the shader. * * @method init */ PIXI.PixiFastShader.prototype.init = function() { 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.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.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 */ /** * @class StripShader * @constructor * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.StripShader = function(gl) { /** * @property _UID * @type Number * @private */ this._UID = PIXI._UID++; /** * @property gl * @type WebGLContext */ this.gl = gl; /** * The WebGL program. * @property program * @type Any */ this.program = null; /** * The fragment shader. * @property fragmentSrc * @type Array */ 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)) * alpha;', // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', '}' ]; /** * The vertex shader. * @property vertexSrc * @type Array */ this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'uniform mat3 translationMatrix;', 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', // 'uniform float alpha;', // 'uniform vec3 tint;', 'varying vec2 vTextureCoord;', // '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);', ' vTextureCoord = aTextureCoord;', // ' vColor = aColor * vec4(tint * alpha, alpha);', '}' ]; this.init(); }; PIXI.StripShader.prototype.constructor = PIXI.StripShader; /** * Initialises the shader. * * @method init */ PIXI.StripShader.prototype.init = function() { 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.colorAttribute = gl.getAttribLocation(program, 'aColor'); //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); // get and store the attributes this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); this.attributes = [this.aVertexPosition, this.aTextureCoord]; this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); this.alpha = gl.getUniformLocation(program, 'alpha'); this.program = program; }; /** * Destroys the shader. * * @method destroy */ PIXI.StripShader.prototype.destroy = function() { this.gl.deleteProgram( this.program ); this.uniforms = null; this.gl = null; this.attribute = null; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class PrimitiveShader * @constructor * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.PrimitiveShader = function(gl) { /** * @property _UID * @type Number * @private */ this._UID = PIXI._UID++; /** * @property gl * @type WebGLContext */ this.gl = gl; /** * The WebGL program. * @property program * @type Any */ this.program = null; /** * The fragment shader. * @property fragmentSrc * @type Array */ this.fragmentSrc = [ 'precision mediump float;', 'varying vec4 vColor;', 'void main(void) {', ' gl_FragColor = vColor;', '}' ]; /** * The vertex shader. * @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 float flipY;', '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 * -flipY) + flipY , 0.0, 1.0);', ' vColor = aColor * vec4(tint * alpha, alpha);', '}' ]; this.init(); }; PIXI.PrimitiveShader.prototype.constructor = PIXI.PrimitiveShader; /** * Initialises the shader. * * @method init */ PIXI.PrimitiveShader.prototype.init = function() { 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.projectionVector = gl.getUniformLocation(program, 'projectionVector'); this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); this.tintColor = gl.getUniformLocation(program, 'tint'); this.flipY = gl.getUniformLocation(program, 'flipY'); // get and store the attributes this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); this.colorAttribute = gl.getAttribLocation(program, 'aColor'); this.attributes = [this.aVertexPosition, this.colorAttribute]; this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); this.alpha = gl.getUniformLocation(program, 'alpha'); this.program = program; }; /** * Destroys the shader. * * @method destroy */ PIXI.PrimitiveShader.prototype.destroy = function() { this.gl.deleteProgram( this.program ); this.uniforms = null; this.gl = null; this.attributes = null; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class ComplexPrimitiveShader * @constructor * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.ComplexPrimitiveShader = function(gl) { /** * @property _UID * @type Number * @private */ this._UID = PIXI._UID++; /** * @property gl * @type WebGLContext */ this.gl = gl; /** * The WebGL program. * @property program * @type Any */ this.program = null; /** * The fragment shader. * @property fragmentSrc * @type Array */ this.fragmentSrc = [ 'precision mediump float;', 'varying vec4 vColor;', 'void main(void) {', ' gl_FragColor = vColor;', '}' ]; /** * The vertex shader. * @property vertexSrc * @type Array */ this.vertexSrc = [ 'attribute vec2 aVertexPosition;', //'attribute vec4 aColor;', 'uniform mat3 translationMatrix;', 'uniform vec2 projectionVector;', 'uniform vec2 offsetVector;', 'uniform vec3 tint;', 'uniform float alpha;', 'uniform vec3 color;', 'uniform float flipY;', '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 * -flipY) + flipY , 0.0, 1.0);', ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', '}' ]; this.init(); }; PIXI.ComplexPrimitiveShader.prototype.constructor = PIXI.ComplexPrimitiveShader; /** * Initialises the shader. * * @method init */ PIXI.ComplexPrimitiveShader.prototype.init = function() { 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.projectionVector = gl.getUniformLocation(program, 'projectionVector'); this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); this.tintColor = gl.getUniformLocation(program, 'tint'); this.color = gl.getUniformLocation(program, 'color'); this.flipY = gl.getUniformLocation(program, 'flipY'); // get and store the attributes this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); this.attributes = [this.aVertexPosition, this.colorAttribute]; this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); this.alpha = gl.getUniformLocation(program, 'alpha'); this.program = program; }; /** * Destroys the shader. * * @method destroy */ PIXI.ComplexPrimitiveShader.prototype.destroy = function() { this.gl.deleteProgram( this.program ); this.uniforms = null; this.gl = null; this.attribute = null; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * A set of functions used by the webGL renderer to draw the primitive graphics data * * @class WebGLGraphics * @private * @static */ PIXI.WebGLGraphics = function() { }; /** * Renders the graphics object * * @static * @private * @method renderGraphics * @param graphics {Graphics} * @param renderSession {Object} */ PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projection, offset) { var gl = renderSession.gl; var projection = renderSession.projection, offset = renderSession.offset, shader = renderSession.shaderManager.primitiveShader, webGLData; if(graphics.dirty) { PIXI.WebGLGraphics.updateGraphics(graphics, gl); } var webGL = graphics._webGL[gl.id]; // This could be speeded up for sure! for (var i = 0; i < webGL.data.length; i++) { if(webGL.data[i].mode === 1) { webGLData = webGL.data[i]; renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); // render quad.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); } else { webGLData = webGL.data[i]; renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); shader = renderSession.shaderManager.primitiveShader; gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, 1); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); gl.uniform1f(shader.alpha, graphics.worldAlpha); gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.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, webGLData.indexBuffer); gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); } } }; /** * Updates the graphics object * * @static * @private * @method updateGraphics * @param graphicsData {Graphics} The graphics object to update * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLGraphics.updateGraphics = function(graphics, gl) { // get the contexts graphics object var webGL = graphics._webGL[gl.id]; // if the graphics object does not exist in the webGL context time to create it! if(!webGL)webGL = graphics._webGL[gl.id] = {lastIndex:0, data:[], gl:gl}; // flag the graphics as not dirty as we are about to update it... graphics.dirty = false; var i; // if the user cleared the graphics object we will need to clear every object if(graphics.clearDirty) { graphics.clearDirty = false; // lop through and return all the webGLDatas to the object pool so than can be reused later on for (i = 0; i < webGL.data.length; i++) { var graphicsData = webGL.data[i]; graphicsData.reset(); PIXI.WebGLGraphics.graphicsDataPool.push( graphicsData ); } // clear the array and reset the index.. webGL.data = []; webGL.lastIndex = 0; } var webGLData; // loop through the graphics datas and construct each one.. // if the object is a complex fill then the new stencil buffer technique will be used // other wise graphics objects will be pushed into a batch.. for (i = webGL.lastIndex; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; if(data.type === PIXI.Graphics.POLY) { // need to add the points the the graphics object.. data.points = data.shape.points.slice(); if(data.shape.closed) { // close the poly if the value is true! if(data.points[0] !== data.points[data.points.length-2] || data.points[1] !== data.points[data.points.length-1]) { data.points.push(data.points[0], data.points[1]); } } // MAKE SURE WE HAVE THE CORRECT TYPE.. if(data.fill) { if(data.points.length >= 6) { if(data.points.length < 6 * 2) { webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); var canDrawUsingSimple = PIXI.WebGLGraphics.buildPoly(data, webGLData); // console.log(canDrawUsingSimple); if(!canDrawUsingSimple) { // console.log("<>>>") webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); } } else { webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); } } } if(data.lineWidth > 0) { webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); PIXI.WebGLGraphics.buildLine(data, webGLData); } } else { webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); if(data.type === PIXI.Graphics.RECT) { PIXI.WebGLGraphics.buildRectangle(data, webGLData); } else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) { PIXI.WebGLGraphics.buildCircle(data, webGLData); } else if(data.type === PIXI.Graphics.RREC) { PIXI.WebGLGraphics.buildRoundedRectangle(data, webGLData); } } webGL.lastIndex++; } // upload all the dirty data... for (i = 0; i < webGL.data.length; i++) { webGLData = webGL.data[i]; if(webGLData.dirty)webGLData.upload(); } }; /** * @static * @private * @method switchMode * @param webGL {WebGLContext} * @param type {Number} */ PIXI.WebGLGraphics.switchMode = function(webGL, type) { var webGLData; if(!webGL.data.length) { webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); webGLData.mode = type; webGL.data.push(webGLData); } else { webGLData = webGL.data[webGL.data.length-1]; if(webGLData.mode !== type || type === 1) { webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); webGLData.mode = type; webGL.data.push(webGLData); } } webGLData.dirty = true; return webGLData; }; /** * Builds a rectangle to draw * * @static * @private * @method buildRectangle * @param graphicsData {Graphics} The graphics object containing all the necessary properties * @param webGLData {Object} */ PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) { // --- // // need to convert points to a nice regular data // var rectData = graphicsData.shape; var x = rectData.x; var y = rectData.y; var width = rectData.width; var height = rectData.height; if(graphicsData.fill) { var color = PIXI.hex2rgb(graphicsData.fillColor); var alpha = graphicsData.fillAlpha; var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; var verts = webGLData.points; var indices = webGLData.indices; var vertPos = verts.length/6; // start verts.push(x, y); verts.push(r, g, b, alpha); verts.push(x + width, y); verts.push(r, g, b, alpha); verts.push(x , y + height); verts.push(r, g, b, alpha); verts.push(x + width, y + height); verts.push(r, g, b, alpha); // insert 2 dead triangles.. indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3); } 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; } }; /** * Builds a rounded rectangle to draw * * @static * @private * @method buildRoundedRectangle * @param graphicsData {Graphics} The graphics object containing all the necessary properties * @param webGLData {Object} */ PIXI.WebGLGraphics.buildRoundedRectangle = function(graphicsData, webGLData) { var rrectData = graphicsData.shape; var x = rrectData.x; var y = rrectData.y; var width = rrectData.width; var height = rrectData.height; var radius = rrectData.radius; var recPoints = []; recPoints.push(x, y + radius); recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x, y + height - radius, x, y + height, x + radius, y + height)); recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width - radius, y + height, x + width, y + height, x + width, y + height - radius)); recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width, y + radius, x + width, y, x + width - radius, y)); recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + radius, y, x, y, x, y + radius)); if (graphicsData.fill) { var color = PIXI.hex2rgb(graphicsData.fillColor); var alpha = graphicsData.fillAlpha; var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; var verts = webGLData.points; var indices = webGLData.indices; var vecPos = verts.length/6; var triangles = PIXI.PolyK.Triangulate(recPoints); // var i = 0; for (i = 0; i < triangles.length; i+=3) { indices.push(triangles[i] + vecPos); indices.push(triangles[i] + vecPos); indices.push(triangles[i+1] + vecPos); indices.push(triangles[i+2] + vecPos); indices.push(triangles[i+2] + vecPos); } for (i = 0; i < recPoints.length; i++) { verts.push(recPoints[i], recPoints[++i], r, g, b, alpha); } } if (graphicsData.lineWidth) { var tempPoints = graphicsData.points; graphicsData.points = recPoints; PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); graphicsData.points = tempPoints; } }; /** * Calculate the points for a quadratic bezier curve. (helper function..) * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c * * @static * @private * @method quadraticBezierCurve * @param fromX {Number} Origin point x * @param fromY {Number} Origin point x * @param cpX {Number} Control point x * @param cpY {Number} Control point y * @param toX {Number} Destination point x * @param toY {Number} Destination point y * @return {Array(Number)} */ PIXI.WebGLGraphics.quadraticBezierCurve = function(fromX, fromY, cpX, cpY, toX, toY) { var xa, ya, xb, yb, x, y, n = 20, points = []; function getPt(n1 , n2, perc) { var diff = n2 - n1; return n1 + ( diff * perc ); } var j = 0; for (var i = 0; i <= n; i++ ) { j = i / n; // The Green Line xa = getPt( fromX , cpX , j ); ya = getPt( fromY , cpY , j ); xb = getPt( cpX , toX , j ); yb = getPt( cpY , toY , j ); // The Black Dot x = getPt( xa , xb , j ); y = getPt( ya , yb , j ); points.push(x, y); } return points; }; /** * Builds a circle to draw * * @static * @private * @method buildCircle * @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 circleData = graphicsData.shape; var x = circleData.x; var y = circleData.y; var width; var height; // TODO - bit hacky?? if(graphicsData.type === PIXI.Graphics.CIRC) { width = circleData.radius; height = circleData.radius; } else { width = circleData.width; height = circleData.height; } var totalSegs = 40; var seg = (Math.PI * 2) / totalSegs ; var i = 0; if(graphicsData.fill) { var color = PIXI.hex2rgb(graphicsData.fillColor); var alpha = graphicsData.fillAlpha; var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; var verts = webGLData.points; var indices = webGLData.indices; var vecPos = verts.length/6; indices.push(vecPos); for (i = 0; i < totalSegs + 1 ; i++) { verts.push(x,y, r, g, b, alpha); verts.push(x + Math.sin(seg * i) * width, y + Math.cos(seg * i) * height, r, g, b, alpha); indices.push(vecPos++, vecPos++); } indices.push(vecPos-1); } if(graphicsData.lineWidth) { var tempPoints = graphicsData.points; graphicsData.points = []; for (i = 0; i < totalSegs + 1; i++) { graphicsData.points.push(x + Math.sin(seg * i) * width, y + Math.cos(seg * i) * height); } PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); graphicsData.points = tempPoints; } }; /** * Builds a line to draw * * @static * @private * @method buildLine * @param graphicsData {Graphics} The graphics object containing all the necessary properties * @param webGLData {Object} */ PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) { // TODO OPTIMISE! var i = 0; var points = graphicsData.points; if(points.length === 0)return; // if the line width is an odd number add 0.5 to align to a whole pixel if(graphicsData.lineWidth%2) { for (i = 0; i < points.length; i++) { points[i] += 0.5; } } // get first and last point.. figure out the middle! 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 - gonna have issues :) if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) { // need to clone as we are going to slightly modify the shape.. points = points.slice(); points.pop(); points.pop(); lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; points.unshift(midPointX, midPointY); points.push(midPointX, midPointY); } var verts = webGLData.points; var indices = webGLData.indices; var length = points.length / 2; var indexCount = points.length; var indexStart = verts.length/6; // DRAW the Line var width = graphicsData.lineWidth / 2; // sort color var color = PIXI.hex2rgb(graphicsData.lineColor); var alpha = graphicsData.lineAlpha; var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; var px, py, p1x, p1y, p2x, p2y, p3x, p3y; var perpx, perpy, perp2x, perp2y, perp3x, perp3y; var a1, b1, c1, a2, b2, c2; var denom, pdist, dist; p1x = points[0]; p1y = points[1]; p2x = points[2]; p2y = points[3]; perpx = -(p1y - p2y); perpy = p1x - p2x; dist = Math.sqrt(perpx*perpx + perpy*perpy); perpx /= dist; perpy /= dist; perpx *= width; perpy *= width; // start verts.push(p1x - perpx , p1y - perpy, r, g, b, alpha); verts.push(p1x + perpx , p1y + perpy, r, g, b, alpha); for (i = 1; i < length-1; i++) { p1x = points[(i-1)*2]; p1y = points[(i-1)*2 + 1]; p2x = points[(i)*2]; p2y = points[(i)*2 + 1]; p3x = points[(i+1)*2]; p3y = points[(i+1)*2 + 1]; perpx = -(p1y - p2y); perpy = p1x - p2x; dist = Math.sqrt(perpx*perpx + perpy*perpy); perpx /= dist; perpy /= dist; perpx *= width; perpy *= width; perp2x = -(p2y - p3y); perp2y = p2x - p3x; dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); perp2x /= dist; perp2y /= dist; perp2x *= width; perp2y *= width; a1 = (-perpy + p1y) - (-perpy + p2y); b1 = (-perpx + p2x) - (-perpx + p1x); c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); a2 = (-perp2y + p3y) - (-perp2y + p2y); b2 = (-perp2x + p2x) - (-perp2x + p3x); c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); denom = a1*b2 - a2*b1; if(Math.abs(denom) < 0.1 ) { denom+=10.1; verts.push(p2x - perpx , p2y - perpy, r, g, b, alpha); verts.push(p2x + perpx , p2y + perpy, r, g, b, alpha); continue; } px = (b1*c2 - b2*c1)/denom; py = (a2*c1 - a1*c2)/denom; pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); if(pdist > 140 * 140) { perp3x = perpx - perp2x; perp3y = perpy - perp2y; dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); perp3x /= dist; perp3y /= dist; perp3x *= width; perp3y *= width; verts.push(p2x - perp3x, p2y -perp3y); verts.push(r, g, b, alpha); verts.push(p2x + perp3x, p2y +perp3y); verts.push(r, g, b, alpha); verts.push(p2x - perp3x, p2y -perp3y); verts.push(r, g, b, alpha); indexCount++; } else { verts.push(px , py); verts.push(r, g, b, alpha); verts.push(p2x - (px-p2x), p2y - (py - p2y)); verts.push(r, g, b, alpha); } } p1x = points[(length-2)*2]; p1y = points[(length-2)*2 + 1]; p2x = points[(length-1)*2]; p2y = points[(length-1)*2 + 1]; perpx = -(p1y - p2y); perpy = p1x - p2x; dist = Math.sqrt(perpx*perpx + perpy*perpy); perpx /= dist; perpy /= dist; perpx *= width; perpy *= width; verts.push(p2x - perpx , p2y - perpy); verts.push(r, g, b, alpha); verts.push(p2x + perpx , p2y + perpy); verts.push(r, g, b, alpha); indices.push(indexStart); for (i = 0; i < indexCount; i++) { indices.push(indexStart++); } indices.push(indexStart-1); }; /** * Builds a complex polygon to draw * * @static * @private * @method buildComplexPoly * @param graphicsData {Graphics} The graphics object containing all the necessary properties * @param webGLData {Object} */ PIXI.WebGLGraphics.buildComplexPoly = function(graphicsData, webGLData) { //TODO - no need to copy this as it gets turned into a FLoat32Array anyways.. var points = graphicsData.points.slice(); if(points.length < 6)return; // get first and last point.. figure out the middle! var indices = webGLData.indices; webGLData.points = points; webGLData.alpha = graphicsData.fillAlpha; webGLData.color = PIXI.hex2rgb(graphicsData.fillColor); /* calclate the bounds.. */ var minX = Infinity; var maxX = -Infinity; var minY = Infinity; var maxY = -Infinity; var x,y; // get size.. for (var i = 0; i < points.length; i+=2) { x = points[i]; y = points[i+1]; minX = x < minX ? x : minX; maxX = x > maxX ? x : maxX; minY = y < minY ? y : minY; maxY = y > maxY ? y : maxY; } // add a quad to the end cos there is no point making another buffer! points.push(minX, minY, maxX, minY, maxX, maxY, minX, maxY); // push a quad onto the end.. //TODO - this aint needed! var length = points.length / 2; for (i = 0; i < length; i++) { indices.push( i ); } }; /** * Builds a polygon to draw * * @static * @private * @method buildPoly * @param graphicsData {Graphics} The graphics object containing all the necessary properties * @param webGLData {Object} */ PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) { var points = graphicsData.points; if(points.length < 6)return; // get first and last point.. figure out the middle! var verts = webGLData.points; var indices = webGLData.indices; var length = points.length / 2; // sort color var color = PIXI.hex2rgb(graphicsData.fillColor); var alpha = graphicsData.fillAlpha; var r = color[0] * alpha; var g = color[1] * alpha; var b = color[2] * alpha; var triangles = PIXI.PolyK.Triangulate(points); if(!triangles)return false; var vertPos = verts.length / 6; var i = 0; for (i = 0; i < triangles.length; i+=3) { indices.push(triangles[i] + vertPos); indices.push(triangles[i] + vertPos); indices.push(triangles[i+1] + vertPos); indices.push(triangles[i+2] +vertPos); indices.push(triangles[i+2] + vertPos); } for (i = 0; i < length; i++) { verts.push(points[i * 2], points[i * 2 + 1], r, g, b, alpha); } return true; }; PIXI.WebGLGraphics.graphicsDataPool = []; /** * @class WebGLGraphicsData * @private * @static */ PIXI.WebGLGraphicsData = function(gl) { this.gl = gl; //TODO does this need to be split before uploding?? this.color = [0,0,0]; // color split! this.points = []; this.indices = []; this.buffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); this.mode = 1; this.alpha = 1; this.dirty = true; }; /** * @method reset */ PIXI.WebGLGraphicsData.prototype.reset = function() { this.points = []; this.indices = []; }; /** * @method upload */ PIXI.WebGLGraphicsData.prototype.upload = function() { var gl = this.gl; // this.lastIndex = graphics.graphicsData.length; this.glPoints = new PIXI.Float32Array(this.points); gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); gl.bufferData(gl.ARRAY_BUFFER, this.glPoints, gl.STATIC_DRAW); this.glIndicies = new PIXI.Uint16Array(this.indices); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.glIndicies, gl.STATIC_DRAW); this.dirty = false; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ PIXI.glContexts = []; // this is where we store the webGL contexts for easy access. PIXI.instances = []; /** * 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 webGLBatchs. * So no need for Sprite Batches or Sprite Clouds. * Don't forget to add the view to your DOM or you will not see anything :) * * @class WebGLRenderer * @constructor * @param [width=0] {Number} the width of the canvas view * @param [height=0] {Number} the height of the canvas view * @param [options] {Object} The optional renderer parameters * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional * @param [options.transparent=false] {Boolean} If the render view is transparent, default false * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 */ PIXI.WebGLRenderer = function(width, height, options) { if(options) { for (var i in PIXI.defaultRenderOptions) { if (typeof options[i] === 'undefined') options[i] = PIXI.defaultRenderOptions[i]; } } else { options = PIXI.defaultRenderOptions; } if(!PIXI.defaultRenderer) { PIXI.defaultRenderer = this; } /** * @property type * @type Number */ this.type = PIXI.WEBGL_RENDERER; /** * The resolution of the renderer * * @property resolution * @type Number * @default 1 */ this.resolution = options.resolution; // do a catch.. only 1 webGL renderer.. /** * Whether the render view is transparent * * @property transparent * @type Boolean */ this.transparent = options.transparent; /** * Whether the render view should be resized automatically * * @property autoResize * @type Boolean */ this.autoResize = options.autoResize || false; /** * The value of the preserveDrawingBuffer flag affects whether or not the contents of the stencil buffer is retained after rendering. * * @property preserveDrawingBuffer * @type Boolean */ this.preserveDrawingBuffer = options.preserveDrawingBuffer; /** * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). * If the Stage is transparent, Pixi will clear to the target Stage's background color. * 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 = options.clearBeforeRender; /** * 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 = options.view || document.createElement( 'canvas' ); // deal with losing context.. /** * @property contextLostBound * @type Function */ this.contextLostBound = this.handleContextLost.bind(this); /** * @property contextRestoredBound * @type Function */ this.contextRestoredBound = this.handleContextRestored.bind(this); this.view.addEventListener('webglcontextlost', this.contextLostBound, false); this.view.addEventListener('webglcontextrestored', this.contextRestoredBound, false); /** * @property _contextOptions * @type Object * @private */ this._contextOptions = { alpha: this.transparent, antialias: options.antialias, // SPEED UP?? premultipliedAlpha:this.transparent && this.transparent !== 'notMultiplied', stencil:true, preserveDrawingBuffer: options.preserveDrawingBuffer }; /** * @property projection * @type Point */ this.projection = new PIXI.Point(); /** * @property offset * @type Point */ this.offset = new PIXI.Point(0, 0); // time to create the render managers! each one focuses on managing a state in webGL /** * Deals with managing the shader programs and their attribs * @property shaderManager * @type WebGLShaderManager */ this.shaderManager = new PIXI.WebGLShaderManager(); /** * Manages the rendering of sprites * @property spriteBatch * @type WebGLSpriteBatch */ this.spriteBatch = new PIXI.WebGLSpriteBatch(); /** * Manages the masks using the stencil buffer * @property maskManager * @type WebGLMaskManager */ this.maskManager = new PIXI.WebGLMaskManager(); /** * Manages the filters * @property filterManager * @type WebGLFilterManager */ this.filterManager = new PIXI.WebGLFilterManager(); /** * Manages the stencil buffer * @property stencilManager * @type WebGLStencilManager */ this.stencilManager = new PIXI.WebGLStencilManager(); /** * Manages the blendModes * @property blendModeManager * @type WebGLBlendModeManager */ this.blendModeManager = new PIXI.WebGLBlendModeManager(); /** * TODO remove * @property renderSession * @type Object */ 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.blendModeManager = this.blendModeManager; this.renderSession.spriteBatch = this.spriteBatch; this.renderSession.stencilManager = this.stencilManager; this.renderSession.renderer = this; this.renderSession.resolution = this.resolution; // time init the context.. this.initContext(); // map some webGL blend modes.. this.mapBlendModes(); }; // constructor PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; /** * @method initContext */ PIXI.WebGLRenderer.prototype.initContext = function() { var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); this.gl = gl; if (!gl) { // fail, not able to get a context throw new Error('This browser does not support webGL. Try using the canvas renderer'); } this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId++; PIXI.glContexts[this.glContextId] = gl; PIXI.instances[this.glContextId] = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); gl.disable(gl.CULL_FACE); gl.enable(gl.BLEND); // need to set the context for all the managers... this.shaderManager.setContext(gl); this.spriteBatch.setContext(gl); this.maskManager.setContext(gl); this.filterManager.setContext(gl); this.blendModeManager.setContext(gl); this.stencilManager.setContext(gl); this.renderSession.gl = this.gl; // now resize and we are good to go! this.resize(this.width, this.height); }; /** * Renders the stage to its webGL view * * @method render * @param stage {Stage} the Stage element to be rendered */ PIXI.WebGLRenderer.prototype.render = function(stage) { // no point rendering if our context has been blown up! if (this.contextLost) return; // if rendering a new stage clear the batches.. if (this.__stage !== stage) { // TODO make this work // dont think this is needed any more? this.__stage = stage; } // update the scene graph stage.updateTransform(); var gl = this.gl; // -- Does this need to be set every frame? -- // gl.viewport(0, 0, this.width, this.height); // make sure we are bound to the main frame buffer gl.bindFramebuffer(gl.FRAMEBUFFER, null); if (this.clearBeforeRender) { 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); } this.renderDisplayObject( stage, this.projection ); }; /** * 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, matrix) { this.renderSession.blendModeManager.setBlendMode(PIXI.blendModes.NORMAL); // reset the render session data.. this.renderSession.drawCount = 0; // make sure to flip the Y if using a render texture.. this.renderSession.flipY = buffer ? -1 : 1; // set the default projection this.renderSession.projection = projection; //set the default offset 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, matrix); // finish the sprite batch this.spriteBatch.end(); }; /** * Resizes the webGL view to the specified width and height. * * @method resize * @param width {Number} the new width of the webGL view * @param height {Number} the new height of the webGL view */ PIXI.WebGLRenderer.prototype.resize = function(width, height) { this.width = width * this.resolution; this.height = height * this.resolution; this.view.width = this.width; this.view.height = this.height; if (this.autoResize) { this.view.style.width = this.width / this.resolution + 'px'; this.view.style.height = this.height / this.resolution + 'px'; } this.gl.viewport(0, 0, this.width, this.height); this.projection.x = this.width / 2 / this.resolution; this.projection.y = -this.height / 2 / this.resolution; }; /** * Updates and Creates a WebGL texture for the renderers context. * * @method updateTexture * @param texture {Texture} the texture to update */ PIXI.WebGLRenderer.prototype.updateTexture = function(texture) { if(!texture.hasLoaded )return; var gl = this.gl; if(!texture._glTextures[gl.id])texture._glTextures[gl.id] = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha); 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); if(texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height)) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); gl.generateMipmap(gl.TEXTURE_2D); } else { 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); } texture._dirty[gl.id] = false; return texture._glTextures[gl.id]; }; /** * Handles a lost webgl context * * @method handleContextLost * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextLost = function(event) { event.preventDefault(); this.contextLost = true; }; /** * Handles a restored webgl context * * @method handleContextRestored * @param event {Event} * @private */ PIXI.WebGLRenderer.prototype.handleContextRestored = function() { this.initContext(); // empty all the ol gl textures as they are useless now for(var key in PIXI.TextureCache) { var texture = PIXI.TextureCache[key].baseTexture; texture._glTextures = []; } this.contextLost = false; }; /** * Removes everything from the renderer (event listeners, spritebatch, etc...) * * @method destroy */ PIXI.WebGLRenderer.prototype.destroy = function() { // remove listeners this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); PIXI.glContexts[this.glContextId] = null; this.projection = null; this.offset = null; 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.instances[this.glContextId] = null; PIXI.WebGLRenderer.glContextId--; }; /** * Maps Pixi blend modes to WebGL blend modes. * * @method mapBlendModes */ PIXI.WebGLRenderer.prototype.mapBlendModes = function() { var gl = this.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.glContextId = 0; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class WebGLBlendModeManager * @constructor * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLBlendModeManager = function() { /** * @property currentBlendMode * @type Number */ this.currentBlendMode = 99999; }; PIXI.WebGLBlendModeManager.prototype.constructor = PIXI.WebGLBlendModeManager; /** * Sets the WebGL Context. * * @method setContext * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLBlendModeManager.prototype.setContext = function(gl) { this.gl = gl; }; /** * 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.WebGLBlendModeManager.prototype.setBlendMode = function(blendMode) { if(this.currentBlendMode === blendMode)return false; this.currentBlendMode = blendMode; var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); return true; }; /** * Destroys this object. * * @method destroy */ PIXI.WebGLBlendModeManager.prototype.destroy = function() { this.gl = null; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class WebGLMaskManager * @constructor * @private */ PIXI.WebGLMaskManager = function() { }; PIXI.WebGLMaskManager.prototype.constructor = PIXI.WebGLMaskManager; /** * 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) { this.gl = gl; }; /** * Applies the Mask and adds it to the current filter stack. * * @method pushMask * @param maskData {Array} * @param renderSession {Object} */ PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession) { var gl = renderSession.gl; if(maskData.dirty) { PIXI.WebGLGraphics.updateGraphics(maskData, gl); } if(!maskData._webGL[gl.id].data.length)return; renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); }; /** * Removes the last filter from the filter stack and doesn't return it. * * @method popMask * @param maskData {Array} * @param renderSession {Object} an object containing all the useful parameters */ PIXI.WebGLMaskManager.prototype.popMask = function(maskData, renderSession) { var gl = this.gl; renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); }; /** * Destroys the mask stack. * * @method destroy */ PIXI.WebGLMaskManager.prototype.destroy = function() { this.gl = null; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class WebGLStencilManager * @constructor * @private */ PIXI.WebGLStencilManager = function() { this.stencilStack = []; this.reverse = true; this.count = 0; }; /** * Sets the drawing context to the one given in parameter. * * @method setContext * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLStencilManager.prototype.setContext = function(gl) { this.gl = gl; }; /** * Applies the Mask and adds it to the current filter stack. * * @method pushMask * @param graphics {Graphics} * @param webGLData {Array} * @param renderSession {Object} */ PIXI.WebGLStencilManager.prototype.pushStencil = function(graphics, webGLData, renderSession) { var gl = this.gl; this.bindGraphics(graphics, webGLData, renderSession); if(this.stencilStack.length === 0) { gl.enable(gl.STENCIL_TEST); gl.clear(gl.STENCIL_BUFFER_BIT); this.reverse = true; this.count = 0; } this.stencilStack.push(webGLData); var level = this.count; gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,0,0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); // draw the triangle strip! if(webGLData.mode === 1) { gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); if(this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } // draw a quad to increment.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); if(this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); } this.reverse = !this.reverse; } else { if(!this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); if(!this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); } } gl.colorMask(true, true, true, true); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); this.count++; }; /** * TODO this does not belong here! * * @method bindGraphics * @param graphics {Graphics} * @param webGLData {Array} * @param renderSession {Object} */ PIXI.WebGLStencilManager.prototype.bindGraphics = function(graphics, webGLData, renderSession) { //if(this._currentGraphics === graphics)return; this._currentGraphics = graphics; var gl = this.gl; // bind the graphics object.. var projection = renderSession.projection, offset = renderSession.offset, shader;// = renderSession.shaderManager.primitiveShader; if(webGLData.mode === 1) { shader = renderSession.shaderManager.complexPrimitiveShader; renderSession.shaderManager.setShader( shader ); gl.uniform1f(shader.flipY, renderSession.flipY); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); gl.uniform3fv(shader.color, webGLData.color); gl.uniform1f(shader.alpha, graphics.worldAlpha * webGLData.alpha); gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 2, 0); // now do the rest.. // set the index buffer! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); } else { //renderSession.shaderManager.activatePrimitiveShader(); shader = renderSession.shaderManager.primitiveShader; renderSession.shaderManager.setShader( shader ); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform1f(shader.flipY, renderSession.flipY); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); gl.uniform1f(shader.alpha, graphics.worldAlpha); gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.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, webGLData.indexBuffer); } }; /** * @method popStencil * @param graphics {Graphics} * @param webGLData {Array} * @param renderSession {Object} */ PIXI.WebGLStencilManager.prototype.popStencil = function(graphics, webGLData, renderSession) { var gl = this.gl; this.stencilStack.pop(); this.count--; if(this.stencilStack.length === 0) { // the stack is empty! gl.disable(gl.STENCIL_TEST); } else { var level = this.count; this.bindGraphics(graphics, webGLData, renderSession); gl.colorMask(false, false, false, false); if(webGLData.mode === 1) { this.reverse = !this.reverse; if(this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } // draw a quad to increment.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); gl.stencilFunc(gl.ALWAYS,0,0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); // draw the triangle strip! gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); if(!this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); } } else { // console.log("<<>>") if(!this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); if(!this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); } } gl.colorMask(true, true, true, true); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } }; /** * Destroys the mask stack. * * @method destroy */ PIXI.WebGLStencilManager.prototype.destroy = function() { this.stencilStack = null; this.gl = null; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class WebGLShaderManager * @constructor * @private */ PIXI.WebGLShaderManager = function() { /** * @property maxAttibs * @type Number */ this.maxAttibs = 10; /** * @property attribState * @type Array */ this.attribState = []; /** * @property tempAttribState * @type Array */ this.tempAttribState = []; for (var i = 0; i < this.maxAttibs; i++) { this.attribState[i] = false; } /** * @property stack * @type Array */ this.stack = []; }; PIXI.WebGLShaderManager.prototype.constructor = PIXI.WebGLShaderManager; /** * Initialises the context and the properties. * * @method setContext * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLShaderManager.prototype.setContext = function(gl) { this.gl = gl; // the next one is used for rendering primitives this.primitiveShader = new PIXI.PrimitiveShader(gl); // the next one is used for rendering triangle strips this.complexPrimitiveShader = new PIXI.ComplexPrimitiveShader(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); // the next one is used for rendering triangle strips this.stripShader = new PIXI.StripShader(gl); this.setShader(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++) { this.tempAttribState[i] = false; } // set the new attribs for (i = 0; i < attribs.length; i++) { var attribId = attribs[i]; this.tempAttribState[attribId] = true; } var gl = this.gl; for (i = 0; i < this.attribState.length; i++) { if(this.attribState[i] !== this.tempAttribState[i]) { this.attribState[i] = this.tempAttribState[i]; if(this.tempAttribState[i]) { gl.enableVertexAttribArray(i); } else { gl.disableVertexAttribArray(i); } } } }; /** * Sets the current shader. * * @method setShader * @param shader {Any} */ PIXI.WebGLShaderManager.prototype.setShader = function(shader) { if(this._currentId === shader._UID)return false; this._currentId = shader._UID; this.currentShader = shader; this.gl.useProgram(shader.program); this.setAttribs(shader.attributes); return true; }; /** * Destroys this object. * * @method destroy */ PIXI.WebGLShaderManager.prototype.destroy = function() { this.attribState = null; this.tempAttribState = null; this.primitiveShader.destroy(); this.complexPrimitiveShader.destroy(); this.defaultShader.destroy(); this.fastShader.destroy(); this.stripShader.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! * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer * * 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 */ PIXI.WebGLSpriteBatch = function() { /** * @property vertSize * @type Number */ this.vertSize = 5; /** * The number of images in the SpriteBatch before it flushes * @property size * @type Number */ this.size = 2000;//Math.pow(2, 16) / this.vertSize; //the total number of bytes in our batch var numVerts = this.size * 4 * 4 * this.vertSize; //the total number of indices in our batch var numIndices = this.size * 6; /** * Holds the vertices * * @property vertices * @type ArrayBuffer */ this.vertices = new PIXI.ArrayBuffer(numVerts); /** * View on the vertices as a Float32Array * * @property positions * @type Float32Array */ this.positions = new PIXI.Float32Array(this.vertices); /** * View on the vertices as a Uint32Array * * @property colors * @type Uint32Array */ this.colors = new PIXI.Uint32Array(this.vertices); /** * Holds the indices * * @property indices * @type Uint16Array */ this.indices = new PIXI.Uint16Array(numIndices); /** * @property lastIndexCount * @type Number */ 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; } /** * @property drawing * @type Boolean */ this.drawing = false; /** * @property currentBatchSize * @type Number */ this.currentBatchSize = 0; /** * @property currentBaseTexture * @type BaseTexture */ this.currentBaseTexture = null; /** * @property dirty * @type Boolean */ this.dirty = true; /** * @property textures * @type Array */ this.textures = []; /** * @property blendModes * @type Array */ this.blendModes = []; /** * @property shaders * @type Array */ this.shaders = []; /** * @property sprites * @type Array */ this.sprites = []; /** * @property defaultShader * @type AbstractFilter */ this.defaultShader = new PIXI.AbstractFilter([ 'precision lowp float;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', 'uniform sampler2D uSampler;', 'void main(void) {', ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', '}' ]); }; /** * @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; var shader = new PIXI.PixiShader(gl); shader.fragmentSrc = this.defaultShader.fragmentSrc; shader.uniforms = {}; shader.init(); this.defaultShader.shaders[gl.id] = shader; }; /** * @method begin * @param renderSession {Object} The RenderSession object */ 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 * @param {Matrix} [matrix] - Optional matrix. If provided the Display Object will be rendered using this matrix, otherwise it will use its worldTransform. */ PIXI.WebGLSpriteBatch.prototype.render = function(sprite, matrix) { var texture = sprite.texture; // They provided an alternative rendering matrix, so use it var wt = sprite.worldTransform; if (matrix) { wt = matrix; } // check texture.. if (this.currentBatchSize >= this.size) { this.flush(); this.currentBaseTexture = texture.baseTexture; } // get the uvs for the texture var uvs = texture._uvs; // if the uvs have not updated then no point rendering just yet! if (!uvs) { return; } var aX = sprite.anchor.x; var aY = sprite.anchor.y; var w0, w1, h0, h1; if (texture.trim) { // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords. var trim = texture.trim; w1 = trim.x - aX * trim.width; w0 = w1 + texture.crop.width; h1 = trim.y - aY * trim.height; h0 = h1 + texture.crop.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 i = this.currentBatchSize * 4 * this.vertSize; var resolution = texture.baseTexture.resolution; var a = wt.a / resolution; var b = wt.b / resolution; var c = wt.c / resolution; var d = wt.d / resolution; var tx = wt.tx; var ty = wt.ty; var colors = this.colors; var positions = this.positions; if (this.renderSession.roundPixels) { // xy positions[i] = a * w1 + c * h1 + tx | 0; positions[i+1] = d * h1 + b * w1 + ty | 0; // xy positions[i+5] = a * w0 + c * h1 + tx | 0; positions[i+6] = d * h1 + b * w0 + ty | 0; // xy positions[i+10] = a * w0 + c * h0 + tx | 0; positions[i+11] = d * h0 + b * w0 + ty | 0; // xy positions[i+15] = a * w1 + c * h0 + tx | 0; positions[i+16] = d * h0 + b * w1 + ty | 0; } else { // xy positions[i] = a * w1 + c * h1 + tx; positions[i+1] = d * h1 + b * w1 + ty; // xy positions[i+5] = a * w0 + c * h1 + tx; positions[i+6] = d * h1 + b * w0 + ty; // xy positions[i+10] = a * w0 + c * h0 + tx; positions[i+11] = d * h0 + b * w0 + ty; // xy positions[i+15] = a * w1 + c * h0 + tx; positions[i+16] = d * h0 + b * w1 + ty; } // uv positions[i+2] = uvs.x0; positions[i+3] = uvs.y0; // uv positions[i+7] = uvs.x1; positions[i+8] = uvs.y1; // uv positions[i+12] = uvs.x2; positions[i+13] = uvs.y2; // uv positions[i+17] = uvs.x3; positions[i+18] = uvs.y3; // color and alpha var tint = sprite.tint; colors[i+4] = colors[i+9] = colors[i+14] = colors[i+19] = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); // increment the batchsize this.sprites[this.currentBatchSize++] = sprite; }; /** * Renders a TilingSprite using the spriteBatch. * * @method renderTilingSprite * @param sprite {TilingSprite} the sprite to render */ PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(sprite) { var texture = sprite.tilingTexture; // check texture.. if (this.currentBatchSize >= this.size) { this.flush(); this.currentBaseTexture = texture.baseTexture; } // set the textures uvs temporarily if (!sprite._uvs) { sprite._uvs = new PIXI.TextureUvs(); } var uvs = sprite._uvs; var w = texture.baseTexture.width; var h = texture.baseTexture.height; // var w = sprite._frame.sourceSizeW; // var h = sprite._frame.sourceSizeH; // w = 16; // h = 16; sprite.tilePosition.x %= w * sprite.tileScaleOffset.x; sprite.tilePosition.y %= h * sprite.tileScaleOffset.y; var offsetX = sprite.tilePosition.x / (w * sprite.tileScaleOffset.x); var offsetY = sprite.tilePosition.y / (h * sprite.tileScaleOffset.y); var scaleX = (sprite.width / w) / (sprite.tileScale.x * sprite.tileScaleOffset.x); var scaleY = (sprite.height / h) / (sprite.tileScale.y * sprite.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 sprites current alpha and tint and combine them into a single color var tint = sprite.tint; var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); var positions = this.positions; var colors = this.colors; var width = sprite.width; var height = sprite.height; // TODO trim?? var aX = sprite.anchor.x; var aY = sprite.anchor.y; var w0 = width * (1-aX); var w1 = width * -aX; var h0 = height * (1-aY); var h1 = height * -aY; var i = this.currentBatchSize * 4 * this.vertSize; var resolution = texture.baseTexture.resolution; var wt = sprite.worldTransform; var a = wt.a / resolution; var b = wt.b / resolution; var c = wt.c / resolution; var d = wt.d / resolution; var tx = wt.tx; var ty = wt.ty; // xy positions[i++] = a * w1 + c * h1 + tx; positions[i++] = d * h1 + b * w1 + ty; // uv positions[i++] = uvs.x0; positions[i++] = uvs.y0; // color colors[i++] = color; // xy positions[i++] = (a * w0 + c * h1 + tx); positions[i++] = d * h1 + b * w0 + ty; // uv positions[i++] = uvs.x1; positions[i++] = uvs.y1; // color colors[i++] = color; // xy positions[i++] = a * w0 + c * h0 + tx; positions[i++] = d * h0 + b * w0 + ty; // uv positions[i++] = uvs.x2; positions[i++] = uvs.y2; // color colors[i++] = color; // xy positions[i++] = a * w1 + c * h0 + tx; positions[i++] = d * h0 + b * w1 + ty; // uv positions[i++] = uvs.x3; positions[i++] = uvs.y3; // color colors[i++] = color; // increment the batchsize this.sprites[this.currentBatchSize++] = sprite; }; /** * 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; var shader; if (this.dirty) { this.dirty = false; // 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); shader = this.defaultShader.shaders[gl.id]; // this is the same for each shader? var stride = this.vertSize * 4; gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); // color attributes will be interpreted as unsigned bytes and normalized gl.vertexAttribPointer(shader.colorAttribute, 4, gl.UNSIGNED_BYTE, true, stride, 4 * 4); } // 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.positions.subarray(0, this.currentBatchSize * 4 * this.vertSize); gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); } var nextTexture, nextBlendMode, nextShader; var batchSize = 0; var start = 0; var currentBaseTexture = null; var currentBlendMode = this.renderSession.blendModeManager.currentBlendMode; var currentShader = null; var blendSwap = false; var shaderSwap = false; var sprite; for (var i = 0, j = this.currentBatchSize; i < j; i++) { sprite = this.sprites[i]; if (sprite.tilingTexture) { nextTexture = sprite.tilingTexture.baseTexture; } else { nextTexture = sprite.texture.baseTexture; } nextBlendMode = sprite.blendMode; nextShader = sprite.shader || this.defaultShader; blendSwap = currentBlendMode !== nextBlendMode; shaderSwap = currentShader !== nextShader; // should I use _UIDS??? if (currentBaseTexture !== nextTexture || blendSwap || shaderSwap) { this.renderBatch(currentBaseTexture, batchSize, start); start = i; batchSize = 0; currentBaseTexture = nextTexture; if (blendSwap) { currentBlendMode = nextBlendMode; this.renderSession.blendModeManager.setBlendMode(currentBlendMode); } if (shaderSwap) { currentShader = nextShader; shader = currentShader.shaders[gl.id]; if (!shader) { shader = new PIXI.PixiShader(gl); shader.fragmentSrc = currentShader.fragmentSrc; shader.uniforms = currentShader.uniforms; shader.init(); currentShader.shaders[gl.id] = shader; } // set shader function??? this.renderSession.shaderManager.setShader(shader); if (shader.dirty) { shader.syncUniforms(); } // both these only need to be set if they are changing.. // set the projection var projection = this.renderSession.projection; gl.uniform2f(shader.projectionVector, projection.x, projection.y); // TODO - this is temporary! var offsetVector = this.renderSession.offset; gl.uniform2f(shader.offsetVector, offsetVector.x, offsetVector.y); // set the pointers } } batchSize++; } this.renderBatch(currentBaseTexture, batchSize, start); // then reset the batch! this.currentBatchSize = 0; }; /** * @method renderBatch * @param texture {Texture} * @param size {Number} * @param startIndex {Number} */ PIXI.WebGLSpriteBatch.prototype.renderBatch = function(texture, size, startIndex) { if (size === 0) { return; } var gl = this.gl; // check if a texture is dirty.. if (texture._dirty[gl.id]) { this.renderSession.renderer.updateTexture(texture); } else { // bind the current texture gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); } // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); // increment the draw count this.renderSession.drawCount++; }; /** * @method stop */ PIXI.WebGLSpriteBatch.prototype.stop = function() { this.flush(); this.dirty = true; }; /** * @method start */ PIXI.WebGLSpriteBatch.prototype.start = function() { this.dirty = true; }; /** * 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 */ /** * @class WebGLFastSpriteBatch * @constructor */ PIXI.WebGLFastSpriteBatch = function(gl) { /** * @property vertSize * @type Number */ this.vertSize = 10; /** * @property maxSize * @type Number */ this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize; /** * @property size * @type Number */ 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 * @property vertices * @type Float32Array */ this.vertices = new PIXI.Float32Array(numVerts); /** * Index data * @property indices * @type Uint16Array */ this.indices = new PIXI.Uint16Array(numIndices); /** * @property vertexBuffer * @type Object */ this.vertexBuffer = null; /** * @property indexBuffer * @type Object */ this.indexBuffer = null; /** * @property lastIndexCount * @type Number */ 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; } /** * @property drawing * @type Boolean */ this.drawing = false; /** * @property currentBatchSize * @type Number */ this.currentBatchSize = 0; /** * @property currentBaseTexture * @type BaseTexture */ this.currentBaseTexture = null; /** * @property currentBlendMode * @type Number */ this.currentBlendMode = 0; /** * @property renderSession * @type Object */ this.renderSession = null; /** * @property shader * @type Object */ this.shader = null; /** * @property matrix * @type Matrix */ this.matrix = null; this.setContext(gl); }; PIXI.WebGLFastSpriteBatch.prototype.constructor = PIXI.WebGLFastSpriteBatch; /** * Sets the WebGL Context. * * @method setContext * @param gl {WebGLContext} the current WebGL drawing context */ 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); }; /** * @method begin * @param spriteBatch {WebGLSpriteBatch} * @param renderSession {Object} */ PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession) { this.renderSession = renderSession; this.shader = this.renderSession.shaderManager.fastShader; this.matrix = spriteBatch.worldTransform.toArray(true); this.start(); }; /** * @method end */ PIXI.WebGLFastSpriteBatch.prototype.end = function() { this.flush(); }; /** * @method render * @param spriteBatch {WebGLSpriteBatch} */ 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.renderSession.blendModeManager.currentBlendMode) { this.flush(); this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); } for(var i=0,j= children.length; i= this.size) { this.flush(); } }; /** * @method 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])this.renderSession.renderer.updateTexture(this.currentBaseTexture, gl); gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id]); // 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++; }; /** * @method stop */ PIXI.WebGLFastSpriteBatch.prototype.stop = function() { this.flush(); }; /** * @method start */ 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); }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class WebGLFilterManager * @constructor */ PIXI.WebGLFilterManager = function() { /** * @property filterStack * @type Array */ this.filterStack = []; /** * @property offsetX * @type Number */ this.offsetX = 0; /** * @property offsetY * @type Number */ this.offsetY = 0; }; PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager; /** * 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; 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; filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); // 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._filterArea.x; this.offsetY += filterBlock._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); var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; var padding = filter.padding; filterArea.x -= padding; filterArea.y -= padding; filterArea.width += padding * 2; filterArea.height += padding * 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 // now restore the regular shader.. // this.renderSession.shaderManager.setShader(this.defaultShader); //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._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._filterArea; sizeX = filterArea.width; sizeY = filterArea.height; offsetX = filterArea.x; offsetY = filterArea.y; buffer = currentFilter._glFilterTexture.frameBuffer; } // TODO need to remove these global elements.. projection.x = sizeX/2; projection.y = -sizeY/2; offset.x = offsetX; offset.y = offsetY; filterArea = filterBlock._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 * this.renderSession.resolution, sizeY * this.renderSession.resolution); // 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.. should happen automatically now.. // this.renderSession.shaderManager.setShader(this.defaultShader); // 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 this.renderSession.shaderManager.setShader(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.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 PIXI.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 PIXI.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 PIXI.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[i].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 * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values */ PIXI.FilterTexture = function(gl, width, height, scaleMode) { /** * @property gl * @type WebGLContext */ this.gl = gl; // next time to create a frame buffer and texture /** * @property frameBuffer * @type Any */ this.frameBuffer = gl.createFramebuffer(); /** * @property texture * @type Any */ this.texture = gl.createTexture(); /** * @property scaleMode * @type Number */ scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); 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); // required for masking a mask?? this.renderBuffer = gl.createRenderbuffer(); gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer); this.resize(width, height); }; PIXI.FilterTexture.prototype.constructor = PIXI.FilterTexture; /** * 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); // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); }; /** * 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 http://matgroves.com/ @Doormat23 */ /** * Creates a Canvas element of the given size. * * @class CanvasBuffer * @constructor * @param width {Number} the width for the newly created canvas * @param height {Number} the height for the newly created canvas */ PIXI.CanvasBuffer = function(width, height) { /** * The width of the Canvas in pixels. * * @property width * @type Number */ this.width = width; /** * The height of the Canvas in pixels. * * @property height * @type Number */ this.height = height; /** * The Canvas object that belongs to this CanvasBuffer. * * @property canvas * @type HTMLCanvasElement */ this.canvas = document.createElement("canvas"); /** * A CanvasRenderingContext2D object representing a two-dimensional rendering context. * * @property context * @type CanvasRenderingContext2D */ this.context = this.canvas.getContext("2d"); this.canvas.width = width; this.canvas.height = height; }; PIXI.CanvasBuffer.prototype.constructor = PIXI.CanvasBuffer; /** * Clears the canvas that was created by the CanvasBuffer class. * * @method clear * @private */ PIXI.CanvasBuffer.prototype.clear = function() { this.context.setTransform(1, 0, 0, 1, 0, 0); this.context.clearRect(0,0, this.width, this.height); }; /** * Resizes the canvas 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 */ 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 to handle masking. * * @class CanvasMaskManager * @constructor */ PIXI.CanvasMaskManager = function() { }; PIXI.CanvasMaskManager.prototype.constructor = PIXI.CanvasMaskManager; /** * This method adds it to the current stack of masks. * * @method pushMask * @param maskData {Object} the maskData that will be pushed * @param renderSession {Object} The renderSession whose context will be used for this mask manager. */ PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, renderSession) { var context = renderSession.context; context.save(); var cacheAlpha = maskData.alpha; var transform = maskData.worldTransform; var resolution = renderSession.resolution; context.setTransform(transform.a * resolution, transform.b * resolution, transform.c * resolution, transform.d * resolution, transform.tx * resolution, transform.ty * resolution); 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 renderSession {Object} The renderSession whose context will be used for this mask manager. */ PIXI.CanvasMaskManager.prototype.popMask = function(renderSession) { renderSession.context.restore(); }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * Utility methods for Sprite/Texture tinting. * * @class CanvasTinter * @static */ PIXI.CanvasTinter = function() {}; /** * Basically this method just needs a sprite and a color and tints the sprite with the given color. * * @method getTintedTexture * @static * @param sprite {Sprite} the sprite to tint * @param color {Number} the color to use to tint the sprite with * @return {HTMLCanvasElement} The tinted canvas */ PIXI.CanvasTinter.getTintedTexture = function(sprite, color) { var canvas = sprite.tintedTexture || document.createElement("canvas"); PIXI.CanvasTinter.tintMethod(sprite.texture, color, canvas); return canvas; }; /** * Tint a texture using the "multiply" operation. * * @method tintWithMultiply * @static * @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 crop = texture.crop; if (canvas.width !== crop.width || canvas.height !== crop.height) { canvas.width = crop.width; canvas.height = crop.height; } context.clearRect(0, 0, crop.width, crop.height); context.fillStyle = "#" + ("00000" + (color | 0).toString(16)).substr(-6); context.fillRect(0, 0, crop.width, crop.height); context.globalCompositeOperation = "multiply"; context.drawImage(texture.baseTexture.source, crop.x, crop.y, crop.width, crop.height, 0, 0, crop.width, crop.height); context.globalCompositeOperation = "destination-atop"; context.drawImage(texture.baseTexture.source, crop.x, crop.y, crop.width, crop.height, 0, 0, crop.width, crop.height); }; /** * Tint a texture pixel per pixel. * * @method tintPerPixel * @static * @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 crop = texture.crop; canvas.width = crop.width; canvas.height = crop.height; context.globalCompositeOperation = "copy"; context.drawImage(texture.baseTexture.source, crop.x, crop.y, crop.width, crop.height, 0, 0, crop.width, crop.height); var rgbValues = PIXI.hex2rgb(color); var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2]; var pixelData = context.getImageData(0, 0, crop.width, crop.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; if (!PIXI.CanvasTinter.canHandleAlpha) { var alpha = pixels[i + 3]; pixels[i + 0] /= 255 / alpha; pixels[i + 1] /= 255 / alpha; pixels[i + 2] /= 255 / alpha; } } context.putImageData(pixelData, 0, 0); }; /** * Checks if the browser correctly supports putImageData alpha channels. * * @method checkInverseAlpha * @static */ PIXI.CanvasTinter.checkInverseAlpha = function() { var canvas = new PIXI.CanvasBuffer(2, 1); canvas.context.fillStyle = "rgba(10, 20, 30, 0.5)"; // Draw a single pixel canvas.context.fillRect(0, 0, 1, 1); // Get the color values var s1 = canvas.context.getImageData(0, 0, 1, 1); if (s1 === null) { return false; } // Plot them to x2 canvas.context.putImageData(s1, 1, 0); // Get those values var s2 = canvas.context.getImageData(1, 0, 1, 1); // Compare and return return (s2.data[0] === s1.data[0] && s2.data[1] === s1.data[1] && s2.data[2] === s1.data[2] && s2.data[3] === s1.data[3]); }; /** * If the browser isn't capable of handling tinting with alpha this will be false. * This property is only applicable if using tintWithPerPixel. * * @property canHandleAlpha * @type Boolean * @static */ PIXI.CanvasTinter.canHandleAlpha = PIXI.CanvasTinter.checkInverseAlpha(); /** * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. * * @property canUseMultiply * @type Boolean * @static */ PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes(); /** * The tinting method that will be used. * * @method tintMethod * @static */ 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. * Don't forget to add the CanvasRenderer.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 [options] {Object} The optional renderer parameters * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional * @param [options.transparent=false] {Boolean} If the render view is transparent, default false * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 * @param [options.clearBeforeRender=true] {Boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ PIXI.CanvasRenderer = function(width, height, options) { if (options) { for (var i in PIXI.defaultRenderOptions) { if (typeof options[i] === "undefined") options[i] = PIXI.defaultRenderOptions[i]; } } else { options = PIXI.defaultRenderOptions; } if (!PIXI.defaultRenderer) { PIXI.defaultRenderer = this; } /** * The renderer type. * * @property type * @type Number */ this.type = PIXI.CANVAS_RENDERER; /** * The resolution of the canvas. * * @property resolution * @type Number */ this.resolution = options.resolution; /** * 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 = options.clearBeforeRender; /** * Whether the render view is transparent * * @property transparent * @type Boolean */ this.transparent = options.transparent; /** * Whether the render view should be resized automatically * * @property autoResize * @type Boolean */ this.autoResize = options.autoResize || false; /** * 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; this.width *= this.resolution; this.height *= this.resolution; /** * The canvas element that everything is drawn to. * * @property view * @type HTMLCanvasElement */ this.view = options.view || document.createElement( "canvas" ); /** * The canvas 2d context that everything is drawn with * @property context * @type CanvasRenderingContext2D */ this.context = this.view.getContext( "2d", { alpha: this.transparent } ); /** * Boolean flag controlling canvas refresh. * * @property refresh * @type Boolean */ this.refresh = true; this.view.width = this.width * this.resolution; this.view.height = this.height * this.resolution; /** * Internal var. * * @property count * @type Number */ 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 true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. * Handy for crisp pixel art and speed on legacy devices. */ roundPixels: false }; this.mapBlendModes(); this.resize(width, height); 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"; else if ("msImageSmoothingEnabled" in this.context) this.renderSession.smoothProperty = "msImageSmoothingEnabled"; }; // constructor PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; /** * Renders the Stage to this canvas view * * @method render * @param stage {Stage} the Stage element to be rendered */ PIXI.CanvasRenderer.prototype.render = function(stage) { stage.updateTransform(); this.context.setTransform(1,0,0,1,0,0); this.context.globalAlpha = 1; this.renderSession.currentBlendMode = PIXI.blendModes.NORMAL; this.context.globalCompositeOperation = PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = "black"; this.context.clear(); } if (this.clearBeforeRender) { if (this.transparent) { this.context.clearRect(0, 0, this.width, this.height); } else { this.context.fillStyle = stage.backgroundColorString; this.context.fillRect(0, 0, this.width , this.height); } } this.renderDisplayObject(stage); }; /** * Removes everything from the renderer and optionally removes the Canvas DOM element. * * @method destroy * @param [removeView=true] {boolean} Removes the Canvas element from the DOM. */ PIXI.CanvasRenderer.prototype.destroy = function(removeView) { if (typeof removeView === "undefined") { removeView = true; } if (removeView && this.view.parent) { this.view.parent.removeChild(this.view); } this.view = null; this.context = null; this.maskManager = null; this.renderSession = null; }; /** * 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.resolution; this.height = height * this.resolution; this.view.width = this.width; this.view.height = this.height; if (this.autoResize) { this.view.style.width = this.width / this.resolution + "px"; this.view.style.height = this.height / this.resolution + "px"; } }; /** * Renders a display object * * @method renderDisplayObject * @param displayObject {DisplayObject} The displayObject to render * @param context {CanvasRenderingContext2D} the context 2d method of the canvas * @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering. * @private */ PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context, matrix) { this.renderSession.context = context || this.context; this.renderSession.resolution = this.resolution; displayObject._renderCanvas(this.renderSession, matrix); }; /** * Maps Pixi blend modes to canvas blend modes. * * @method mapBlendModes * @private */ PIXI.CanvasRenderer.prototype.mapBlendModes = function() { 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"; } } }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * A set of functions used by the canvas renderer to draw the primitive graphics data. * * @class CanvasGraphics * @static */ PIXI.CanvasGraphics = function() { }; /* * Renders a PIXI.Graphics object to a canvas. * * @method renderGraphics * @static * @param graphics {Graphics} the actual graphics object to render * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas */ PIXI.CanvasGraphics.renderGraphics = function(graphics, context) { var worldAlpha = graphics.worldAlpha; if (graphics.dirty) { this.updateGraphicsTint(graphics); graphics.dirty = false; } for (var i = 0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var shape = data.shape; var fillColor = data._fillTint; var lineColor = data._lineTint; context.lineWidth = data.lineWidth; if (data.type === PIXI.Graphics.POLY) { context.beginPath(); var points = shape.points; 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 (shape.closed) { context.lineTo(points[0], points[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 = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fill(); } if (data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); context.stroke(); } } else if (data.type === PIXI.Graphics.RECT) { if (data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fillRect(shape.x, shape.y, shape.width, shape.height); } if (data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); context.strokeRect(shape.x, shape.y, shape.width, shape.height); } } else if (data.type === PIXI.Graphics.CIRC) { // TODO - need to be Undefined! context.beginPath(); context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); context.closePath(); if (data.fill) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fill(); } if (data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); 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 w = shape.width * 2; var h = shape.height * 2; var x = shape.x - w/2; var y = shape.y - 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 = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fill(); } if (data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); context.stroke(); } } else if (data.type === PIXI.Graphics.RREC) { var rx = shape.x; var ry = shape.y; var width = shape.width; var height = shape.height; var radius = shape.radius; var maxRadius = Math.min(width, height) / 2 | 0; radius = radius > maxRadius ? maxRadius : radius; context.beginPath(); context.moveTo(rx, ry + radius); context.lineTo(rx, ry + height - radius); context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); context.lineTo(rx + width - radius, ry + height); context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); context.lineTo(rx + width, ry + radius); context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); context.lineTo(rx + radius, ry); context.quadraticCurveTo(rx, ry, rx, ry + radius); context.closePath(); if (data.fillColor || data.fillColor === 0) { context.globalAlpha = data.fillAlpha * worldAlpha; context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); context.fill(); } if (data.lineWidth) { context.globalAlpha = data.lineAlpha * worldAlpha; context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); context.stroke(); } } } }; /* * Renders a graphics mask * * @static * @private * @method renderGraphicsMask * @param graphics {Graphics} the graphics which will be used as a mask * @param context {CanvasRenderingContext2D} the context 2d method of the canvas */ PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) { var len = graphics.graphicsData.length; if (len === 0) { return; } context.beginPath(); for (var i = 0; i < len; i++) { var data = graphics.graphicsData[i]; var shape = data.shape; if (data.type === PIXI.Graphics.POLY) { var points = shape.points; 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.rect(shape.x, shape.y, shape.width, shape.height); context.closePath(); } else if (data.type === PIXI.Graphics.CIRC) { // TODO - need to be Undefined! context.arc(shape.x, shape.y, shape.radius, 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 w = shape.width * 2; var h = shape.height * 2; var x = shape.x - w/2; var y = shape.y - h/2; 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(); } else if (data.type === PIXI.Graphics.RREC) { var rx = shape.x; var ry = shape.y; var width = shape.width; var height = shape.height; var radius = shape.radius; var maxRadius = Math.min(width, height) / 2 | 0; radius = radius > maxRadius ? maxRadius : radius; context.moveTo(rx, ry + radius); context.lineTo(rx, ry + height - radius); context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); context.lineTo(rx + width - radius, ry + height); context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); context.lineTo(rx + width, ry + radius); context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); context.lineTo(rx + radius, ry); context.quadraticCurveTo(rx, ry, rx, ry + radius); context.closePath(); } } }; PIXI.CanvasGraphics.updateGraphicsTint = function(graphics) { if (graphics.tint === 0xFFFFFF) { return; } var tintR = (graphics.tint >> 16 & 0xFF) / 255; var tintG = (graphics.tint >> 8 & 0xFF) / 255; var tintB = (graphics.tint & 0xFF)/ 255; for (var i = 0; i < graphics.graphicsData.length; i++) { var data = graphics.graphicsData[i]; var fillColor = data.fillColor | 0; var lineColor = data.lineColor | 0; /* var colorR = (fillColor >> 16 & 0xFF) / 255; var colorG = (fillColor >> 8 & 0xFF) / 255; var colorB = (fillColor & 0xFF) / 255; colorR *= tintR; colorG *= tintG; colorB *= tintB; fillColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); colorR = (lineColor >> 16 & 0xFF) / 255; colorG = (lineColor >> 8 & 0xFF) / 255; colorB = (lineColor & 0xFF) / 255; colorR *= tintR; colorG *= tintG; colorB *= tintB; lineColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); */ data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); } }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The Graphics class contains methods used to draw primitive shapes such as lines, circles and rectangles to the display, and color and fill them. * * @class Graphics * @extends DisplayObjectContainer * @constructor */ PIXI.Graphics = function() { PIXI.DisplayObjectContainer.call(this); this.renderable = true; /** * The alpha value used when filling the Graphics object. * * @property fillAlpha * @type Number */ this.fillAlpha = 1; /** * The width (thickness) of any lines drawn. * * @property lineWidth * @type Number */ this.lineWidth = 0; /** * The color of any lines drawn. * * @property lineColor * @type String * @default 0 */ this.lineColor = 0; /** * Graphics data * * @property graphicsData * @type Array * @private */ this.graphicsData = []; /** * The tint applied to the graphic shape. This is a hex value. Apply a value of 0xFFFFFF to reset the tint. * * @property tint * @type Number * @default 0xFFFFFF */ this.tint = 0xFFFFFF; /** * The blend mode to be applied to the graphic shape. Apply a value of PIXI.blendModes.NORMAL to reset the blend mode. * * @property blendMode * @type Number * @default PIXI.blendModes.NORMAL; */ this.blendMode = PIXI.blendModes.NORMAL; /** * Current path * * @property currentPath * @type Object * @private */ this.currentPath = null; /** * 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 Boolean */ this.isMask = false; /** * The bounds' padding used for bounds calculation. * * @property boundsPadding * @type Number */ this.boundsPadding = 0; this._localBounds = new PIXI.Rectangle(0,0,1,1); /** * Used to detect if the graphics object has changed. If this is set to true then the graphics object will be recalculated. * * @property dirty * @type Boolean * @private */ this.dirty = true; /** * Used to detect if the webgl graphics object has changed. If this is set to true then the graphics object will be recalculated. * * @property webGLDirty * @type Boolean * @private */ this.webGLDirty = false; /** * Used to detect if the cached sprite object needs to be updated. * * @property cachedSpriteDirty * @type Boolean * @private */ this.cachedSpriteDirty = false; }; // constructor PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Graphics.prototype.constructor = PIXI.Graphics; /** * 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 objects stored style * @param color {Number} color of the line to draw, will update the objects stored style * @param alpha {Number} alpha of the line to draw, will update the objects stored style * @return {Graphics} */ PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) { this.lineWidth = lineWidth || 0; this.lineColor = color || 0; this.lineAlpha = (alpha === undefined) ? 1 : alpha; if (this.currentPath) { if (this.currentPath.shape.points.length) { // halfway through a line? start a new one! this.drawShape(new PIXI.Polygon(this.currentPath.shape.points.slice(-2))); } else { // otherwise its empty so lets just set the line properties this.currentPath.lineWidth = this.lineWidth; this.currentPath.lineColor = this.lineColor; this.currentPath.lineAlpha = this.lineAlpha; } } 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 * @return {Graphics} */ PIXI.Graphics.prototype.moveTo = function(x, y) { this.drawShape(new PIXI.Polygon([x, y])); 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 * @return {Graphics} */ PIXI.Graphics.prototype.lineTo = function(x, y) { if (!this.currentPath) { this.moveTo(0, 0); } this.currentPath.shape.points.push(x, y); this.dirty = true; return this; }; /** * Calculate the points for a quadratic bezier curve and then draws it. * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c * * @method quadraticCurveTo * @param cpX {Number} Control point x * @param cpY {Number} Control point y * @param toX {Number} Destination point x * @param toY {Number} Destination point y * @return {Graphics} */ PIXI.Graphics.prototype.quadraticCurveTo = function(cpX, cpY, toX, toY) { if (this.currentPath) { if (this.currentPath.shape.points.length === 0) { this.currentPath.shape.points = [0, 0]; } } else { this.moveTo(0,0); } var xa, ya, n = 20, points = this.currentPath.shape.points; if (points.length === 0) { this.moveTo(0, 0); } var fromX = points[points.length - 2]; var fromY = points[points.length - 1]; var j = 0; for (var i = 1; i <= n; ++i) { j = i / n; xa = fromX + ( (cpX - fromX) * j ); ya = fromY + ( (cpY - fromY) * j ); points.push( xa + ( ((cpX + ( (toX - cpX) * j )) - xa) * j ), ya + ( ((cpY + ( (toY - cpY) * j )) - ya) * j ) ); } this.dirty = true; return this; }; /** * Calculate the points for a bezier curve and then draws it. * * @method bezierCurveTo * @param cpX {Number} Control point x * @param cpY {Number} Control point y * @param cpX2 {Number} Second Control point x * @param cpY2 {Number} Second Control point y * @param toX {Number} Destination point x * @param toY {Number} Destination point y * @return {Graphics} */ PIXI.Graphics.prototype.bezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) { if (this.currentPath) { if (this.currentPath.shape.points.length === 0) { this.currentPath.shape.points = [0, 0]; } } else { this.moveTo(0,0); } var n = 20, dt, dt2, dt3, t2, t3, points = this.currentPath.shape.points; var fromX = points[points.length-2]; var fromY = points[points.length-1]; var j = 0; for (var i = 1; i <= n; ++i) { j = i / n; dt = (1 - j); dt2 = dt * dt; dt3 = dt2 * dt; t2 = j * j; t3 = t2 * j; points.push( dt3 * fromX + 3 * dt2 * j * cpX + 3 * dt * t2 * cpX2 + t3 * toX, dt3 * fromY + 3 * dt2 * j * cpY + 3 * dt * t2 * cpY2 + t3 * toY); } this.dirty = true; return this; }; /* * The arcTo() method creates an arc/curve between two tangents on the canvas. * * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google! * * @method arcTo * @param x1 {Number} The x-coordinate of the beginning of the arc * @param y1 {Number} The y-coordinate of the beginning of the arc * @param x2 {Number} The x-coordinate of the end of the arc * @param y2 {Number} The y-coordinate of the end of the arc * @param radius {Number} The radius of the arc * @return {Graphics} */ PIXI.Graphics.prototype.arcTo = function(x1, y1, x2, y2, radius) { if (this.currentPath) { if (this.currentPath.shape.points.length === 0) { this.currentPath.shape.points.push(x1, y1); } } else { this.moveTo(x1, y1); } var points = this.currentPath.shape.points, fromX = points[points.length-2], fromY = points[points.length-1], a1 = fromY - y1, b1 = fromX - x1, a2 = y2 - y1, b2 = x2 - x1, mm = Math.abs(a1 * b2 - b1 * a2); if (mm < 1.0e-8 || radius === 0) { if (points[points.length-2] !== x1 || points[points.length-1] !== y1) { points.push(x1, y1); } } else { var dd = a1 * a1 + b1 * b1, cc = a2 * a2 + b2 * b2, tt = a1 * a2 + b1 * b2, k1 = radius * Math.sqrt(dd) / mm, k2 = radius * Math.sqrt(cc) / mm, j1 = k1 * tt / dd, j2 = k2 * tt / cc, cx = k1 * b2 + k2 * b1, cy = k1 * a2 + k2 * a1, px = b1 * (k2 + j1), py = a1 * (k2 + j1), qx = b2 * (k1 + j2), qy = a2 * (k1 + j2), startAngle = Math.atan2(py - cy, px - cx), endAngle = Math.atan2(qy - cy, qx - cx); this.arc(cx + x1, cy + y1, radius, startAngle, endAngle, b1 * a2 > b2 * a1); } this.dirty = true; return this; }; /** * The arc method creates an arc/curve (used to create circles, or parts of circles). * * @method arc * @param cx {Number} The x-coordinate of the center of the circle * @param cy {Number} The y-coordinate of the center of the circle * @param radius {Number} The radius of the circle * @param startAngle {Number} The starting angle, in radians (0 is at the 3 o'clock position of the arc's circle) * @param endAngle {Number} The ending angle, in radians * @param anticlockwise {Boolean} Optional. Specifies whether the drawing should be counterclockwise or clockwise. False is default, and indicates clockwise, while true indicates counter-clockwise. * @return {Graphics} */ PIXI.Graphics.prototype.arc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) { // If we do this we can never draw a full circle if (startAngle === endAngle) { return this; } if (typeof anticlockwise === 'undefined') { anticlockwise = false; } if (!anticlockwise && endAngle <= startAngle) { endAngle += Math.PI * 2; } else if (anticlockwise && startAngle <= endAngle) { startAngle += Math.PI * 2; } var sweep = anticlockwise ? (startAngle - endAngle) * -1 : (endAngle - startAngle); var segs = Math.ceil(Math.abs(sweep) / (Math.PI * 2)) * 40; // Sweep check - moved here because we don't want to do the moveTo below if the arc fails if (sweep === 0) { return this; } var startX = cx + Math.cos(startAngle) * radius; var startY = cy + Math.sin(startAngle) * radius; if (anticlockwise && this.filling) { this.moveTo(cx, cy); } else { this.moveTo(startX, startY); } // currentPath will always exist after calling a moveTo var points = this.currentPath.shape.points; var theta = sweep / (segs * 2); var theta2 = theta * 2; var cTheta = Math.cos(theta); var sTheta = Math.sin(theta); var segMinus = segs - 1; var remainder = (segMinus % 1) / segMinus; for (var i = 0; i <= segMinus; i++) { var real = i + remainder * i; var angle = ((theta) + startAngle + (theta2 * real)); var c = Math.cos(angle); var s = -Math.sin(angle); points.push(( (cTheta * c) + (sTheta * s) ) * radius + cx, ( (cTheta * -s) + (sTheta * c) ) * radius + cy); } 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 * @return {Graphics} */ PIXI.Graphics.prototype.beginFill = function(color, alpha) { this.filling = true; this.fillColor = color || 0; this.fillAlpha = (alpha === undefined) ? 1 : alpha; if (this.currentPath) { if (this.currentPath.shape.points.length <= 2) { this.currentPath.fill = this.filling; this.currentPath.fillColor = this.fillColor; this.currentPath.fillAlpha = this.fillAlpha; } } return this; }; /** * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method. * * @method endFill * @return {Graphics} */ 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 * @return {Graphics} */ PIXI.Graphics.prototype.drawRect = function(x, y, width, height) { this.drawShape(new PIXI.Rectangle(x, y, width, height)); return this; }; /** * @method drawRoundedRect * @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 * @param radius {Number} Radius of the rectangle corners. In WebGL this must be a value between 0 and 9. */ PIXI.Graphics.prototype.drawRoundedRect = function(x, y, width, height, radius) { this.drawShape(new PIXI.RoundedRectangle(x, y, width, height, radius)); 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 diameter {Number} The diameter of the circle * @return {Graphics} */ PIXI.Graphics.prototype.drawCircle = function(x, y, diameter) { this.drawShape(new PIXI.Circle(x, y, diameter)); return this; }; /** * Draws an ellipse. * * @method drawEllipse * @param x {Number} The X coordinate of the center of the ellipse * @param y {Number} The Y coordinate of the center of the ellipse * @param width {Number} The half width of the ellipse * @param height {Number} The half height of the ellipse * @return {Graphics} */ PIXI.Graphics.prototype.drawEllipse = function(x, y, width, height) { this.drawShape(new PIXI.Ellipse(x, y, width, height)); return this; }; /** * Draws a polygon using the given path. * * @method drawPolygon * @param path {Array|Phaser.Polygon} The path data used to construct the polygon. Can either be an array of points or a Phaser.Polygon object. * @return {Graphics} */ PIXI.Graphics.prototype.drawPolygon = function(path) { if (path instanceof Phaser.Polygon || path instanceof PIXI.Polygon) { path = path.points; } // prevents an argument assignment deopt // see section 3.1: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments var points = path; if (!Array.isArray(points)) { // prevents an argument leak deopt // see section 3.2: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments points = new Array(arguments.length); for (var i = 0; i < points.length; ++i) { points[i] = arguments[i]; } } this.drawShape(new Phaser.Polygon(points)); return this; }; /** * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings. * * @method clear * @return {Graphics} */ PIXI.Graphics.prototype.clear = function() { this.lineWidth = 0; this.filling = false; this.dirty = true; this.clearDirty = true; this.graphicsData = []; 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 * @param resolution {Number} The resolution of the texture being generated * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts * @return {Texture} a texture of the graphics object */ PIXI.Graphics.prototype.generateTexture = function(resolution, scaleMode) { resolution = resolution || 1; var bounds = this.getBounds(); var canvasBuffer = new PIXI.CanvasBuffer(bounds.width * resolution, bounds.height * resolution); var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas, scaleMode); texture.baseTexture.resolution = resolution; canvasBuffer.context.scale(resolution, resolution); 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.cachedSpriteDirty) { this._generateCachedSprite(); // we will also need to update the texture on the gpu too! this.updateCachedSpriteTexture(); this.cachedSpriteDirty = false; this.dirty = false; } this._cachedSprite.worldAlpha = this.worldAlpha; PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); return; } else { renderSession.spriteBatch.stop(); renderSession.blendModeManager.setBlendMode(this.blendMode); 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]); } // check if the webgl graphic needs to be updated if (this.webGLDirty) { this.dirty = true; this.webGLDirty = false; } 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; i < this.children.length; i++) { this.children[i]._renderWebGL(renderSession); } renderSession.spriteBatch.stop(); } if (this._filters) renderSession.filterManager.popFilter(); if (this._mask) renderSession.maskManager.popMask(this.mask, renderSession); renderSession.drawCount++; renderSession.spriteBatch.start(); } }; /** * Renders the object using the Canvas renderer * * @method _renderCanvas * @param renderSession {RenderSession} * @private */ PIXI.Graphics.prototype._renderCanvas = 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 the tint has changed, set the graphics object to dirty. if (this._prevTint !== this.tint) { this.dirty = true; this._prevTint = this.tint; } if (this._cacheAsBitmap) { if (this.dirty || this.cachedSpriteDirty) { this._generateCachedSprite(); // we will also need to update the texture this.updateCachedSpriteTexture(); this.cachedSpriteDirty = false; this.dirty = false; } this._cachedSprite.alpha = this.alpha; PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); return; } else { var context = renderSession.context; var transform = this.worldTransform; if (this.blendMode !== renderSession.currentBlendMode) { renderSession.currentBlendMode = this.blendMode; context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; } if (this._mask) { renderSession.maskManager.pushMask(this._mask, renderSession); } var resolution = renderSession.resolution; context.setTransform(transform.a * resolution, transform.b * resolution, transform.c * resolution, transform.d * resolution, transform.tx * resolution, transform.ty * resolution); PIXI.CanvasGraphics.renderGraphics(this, context); // simple render children! for (var i = 0; i < this.children.length; i++) { this.children[i]._renderCanvas(renderSession); } if (this._mask) { renderSession.maskManager.popMask(renderSession); } } }; /** * Retrieves the bounds of the graphic shape as a rectangle object * * @method getBounds * @return {Rectangle} the rectangular bounding area */ PIXI.Graphics.prototype.getBounds = function(matrix) { if(!this._currentBounds) { // return an empty object if the item is a mask! if (!this.renderable) { return PIXI.EmptyRectangle; } if (this.dirty) { this.updateLocalBounds(); this.webGLDirty = true; this.cachedSpriteDirty = true; this.dirty = false; } var bounds = this._localBounds; var w0 = bounds.x; var w1 = bounds.width + bounds.x; var h0 = bounds.y; var h1 = bounds.height + bounds.y; var worldTransform = matrix || this.worldTransform; var a = worldTransform.a; var b = worldTransform.b; var c = worldTransform.c; var d = worldTransform.d; var tx = worldTransform.tx; var ty = worldTransform.ty; var x1 = a * w1 + c * h1 + tx; var y1 = d * h1 + b * w1 + ty; var x2 = a * w0 + c * h1 + tx; var y2 = d * h1 + b * w0 + ty; var x3 = a * w0 + c * h0 + tx; var y3 = d * h0 + b * w0 + ty; var x4 = a * w1 + c * h0 + tx; var y4 = d * h0 + b * w1 + ty; var maxX = x1; var maxY = y1; var minX = x1; var minY = y1; minX = x2 < minX ? x2 : minX; minX = x3 < minX ? x3 : minX; minX = x4 < minX ? x4 : minX; minY = y2 < minY ? y2 : minY; minY = y3 < minY ? y3 : minY; minY = y4 < minY ? y4 : minY; maxX = x2 > maxX ? x2 : maxX; maxX = x3 > maxX ? x3 : maxX; maxX = x4 > maxX ? x4 : maxX; maxY = y2 > maxY ? y2 : maxY; maxY = y3 > maxY ? y3 : maxY; maxY = y4 > maxY ? y4 : maxY; this._bounds.x = minX; this._bounds.width = maxX - minX; this._bounds.y = minY; this._bounds.height = maxY - minY; this._currentBounds = this._bounds; } return this._currentBounds; }; /** * Tests if a point is inside this graphics object * * @param point {Point} the point to test * @return {boolean} the result of the test */ PIXI.Graphics.prototype.containsPoint = function( point ) { this.worldTransform.applyInverse(point, tempPoint); var graphicsData = this.graphicsData; for (var i = 0; i < graphicsData.length; i++) { var data = graphicsData[i]; if (!data.fill) { continue; } // only deal with fills.. if (data.shape) { if ( data.shape.contains( tempPoint.x, tempPoint.y ) ) { return true; } } } return false; }; /** * Update the bounds of the object * * @method updateLocalBounds */ PIXI.Graphics.prototype.updateLocalBounds = function() { var minX = Infinity; var maxX = -Infinity; var minY = Infinity; var maxY = -Infinity; if (this.graphicsData.length) { var shape, 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; shape = data.shape; if (type === PIXI.Graphics.RECT || type === PIXI.Graphics.RREC) { x = shape.x - lineWidth / 2; y = shape.y - lineWidth / 2; w = shape.width + lineWidth; h = shape.height + lineWidth; minX = x < minX ? x : minX; maxX = x + w > maxX ? x + w : maxX; minY = y < minY ? y : minY; maxY = y + h > maxY ? y + h : maxY; } else if (type === PIXI.Graphics.CIRC) { x = shape.x; y = shape.y; w = shape.radius + lineWidth / 2; h = shape.radius + 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 if (type === PIXI.Graphics.ELIP) { x = shape.x; y = shape.y; w = shape.width + lineWidth / 2; h = shape.height + 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 - assumes points are sequential, not Point objects points = shape.points; for (var j = 0; j < points.length; j++) { if (points[j] instanceof Phaser.Point) { x = points[j].x; y = points[j].y; } else { x = points[j]; y = points[j + 1]; if (j < points.length - 1) { j++; } } 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; } } } } else { minX = 0; maxX = 0; minY = 0; maxY = 0; } var padding = this.boundsPadding; this._localBounds.x = minX - padding; this._localBounds.width = (maxX - minX) + padding * 2; this._localBounds.y = minY - padding; this._localBounds.height = (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); // make sure we set the alpha of the graphics to 1 for the render.. this.worldAlpha = 1; // now render the graphic.. PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context); this._cachedSprite.alpha = this.alpha; }; /** * Updates texture size based on canvas size * * @method updateCachedSpriteTexture * @private */ PIXI.Graphics.prototype.updateCachedSpriteTexture = function() { var cachedSprite = this._cachedSprite; var texture = cachedSprite.texture; var canvas = cachedSprite.buffer.canvas; texture.baseTexture.width = canvas.width; texture.baseTexture.height = canvas.height; texture.crop.width = texture.frame.width = canvas.width; texture.crop.height = texture.frame.height = canvas.height; cachedSprite._width = canvas.width; cachedSprite._height = canvas.height; // update the dirty base textures texture.baseTexture.dirty(); }; /** * Destroys a previous cached sprite. * * @method destroyCachedSprite */ PIXI.Graphics.prototype.destroyCachedSprite = function() { this._cachedSprite.texture.destroy(true); this._cachedSprite = null; }; /** * Draws the given shape to this Graphics object. Can be any of Circle, Rectangle, Ellipse, Line or Polygon. * * @method drawShape * @param {Circle|Rectangle|Ellipse|Line|Polygon} shape The Shape object to draw. * @return {GraphicsData} The generated GraphicsData object. */ PIXI.Graphics.prototype.drawShape = function(shape) { if (this.currentPath) { // check current path! if (this.currentPath.shape.points.length <= 2) { this.graphicsData.pop(); } } this.currentPath = null; // Handle mixed-type polygons if (shape instanceof Phaser.Polygon) { shape = shape.clone(); shape.flatten(); } var data = new PIXI.GraphicsData(this.lineWidth, this.lineColor, this.lineAlpha, this.fillColor, this.fillAlpha, this.filling, shape); this.graphicsData.push(data); if (data.type === PIXI.Graphics.POLY) { data.shape.closed = this.filling; this.currentPath = data; } this.dirty = true; return data; }; /** * When cacheAsBitmap is set to true the graphics object will 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 in exchange for taking up texture memory. * It is also useful if you need the graphics object to be anti-aliased, because it will be rendered using canvas. * This is not recommended if you are constantly redrawing the graphics element. * * @property cacheAsBitmap * @type Boolean * @default false * @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; } } }); /** * A GraphicsData object. * * @class GraphicsData * @constructor PIXI.GraphicsData = function(lineWidth, lineColor, lineAlpha, fillColor, fillAlpha, fill, shape) { this.lineWidth = lineWidth; this.lineColor = lineColor; this.lineAlpha = lineAlpha; this._lineTint = lineColor; this.fillColor = fillColor; this.fillAlpha = fillAlpha; this._fillTint = fillColor; this.fill = fill; this.shape = shape; this.type = shape.type; }; */ /** * A GraphicsData object. * * @class * @memberof PIXI * @param lineWidth {number} the width of the line to draw * @param lineColor {number} the color of the line to draw * @param lineAlpha {number} the alpha of the line to draw * @param fillColor {number} the color of the fill * @param fillAlpha {number} the alpha of the fill * @param fill {boolean} whether or not the shape is filled with a colour * @param shape {Circle|Rectangle|Ellipse|Line|Polygon} The shape object to draw. */ PIXI.GraphicsData = function(lineWidth, lineColor, lineAlpha, fillColor, fillAlpha, fill, shape) { /* * @member {number} the width of the line to draw */ this.lineWidth = lineWidth; /* * @member {number} the color of the line to draw */ this.lineColor = lineColor; /* * @member {number} the alpha of the line to draw */ this.lineAlpha = lineAlpha; /* * @member {number} cached tint of the line to draw */ this._lineTint = lineColor; /* * @member {number} the color of the fill */ this.fillColor = fillColor; /* * @member {number} the alpha of the fill */ this.fillAlpha = fillAlpha; /* * @member {number} cached tint of the fill */ this._fillTint = fillColor; /* * @member {boolean} whether or not the shape is filled with a color */ this.fill = fill; /* * @member {Circle|Rectangle|Ellipse|Line|Polygon} The shape object to draw. */ this.shape = shape; /* * @member {number} The type of the shape, see the Const.Shapes file for all the existing types, */ this.type = shape.type; }; PIXI.GraphicsData.prototype.constructor = PIXI.GraphicsData; /** * Creates a new GraphicsData object with the same values as this one. * * @return {GraphicsData} */ PIXI.GraphicsData.prototype.clone = function() { return new GraphicsData( this.lineWidth, this.lineColor, this.lineAlpha, this.fillColor, this.fillAlpha, this.fill, this.shape ); }; /** * @author Mat Groves http://matgroves.com/ */ /** * * @class Strip * @extends DisplayObjectContainer * @constructor * @param texture {Texture} The texture to use * @param width {Number} the width * @param height {Number} the height * */ PIXI.Strip = function(texture) { PIXI.DisplayObjectContainer.call( this ); /** * The texture of the strip * * @property texture * @type Texture */ this.texture = texture; // set up the main bits.. this.uvs = new PIXI.Float32Array([0, 1, 1, 1, 1, 0, 0, 1]); this.vertices = new PIXI.Float32Array([0, 0, 100, 0, 100, 100, 0, 100]); this.colors = new PIXI.Float32Array([1, 1, 1, 1]); this.indices = new PIXI.Uint16Array([0, 1, 2, 3]); /** * Whether the strip is dirty or not * * @property dirty * @type Boolean */ this.dirty = true; /** * The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode. * * @property blendMode * @type Number * @default PIXI.blendModes.NORMAL; */ this.blendMode = PIXI.blendModes.NORMAL; /** * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. * * @property canvasPadding * @type Number */ this.canvasPadding = 0; this.drawMode = PIXI.Strip.DrawModes.TRIANGLE_STRIP; }; // constructor PIXI.Strip.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); PIXI.Strip.prototype.constructor = PIXI.Strip; PIXI.Strip.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; // render triangle strip.. renderSession.spriteBatch.stop(); // init! init! if(!this._vertexBuffer)this._initWebGL(renderSession); renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader); this._renderStrip(renderSession); ///renderSession.shaderManager.activateDefaultShader(); renderSession.spriteBatch.start(); //TODO check culling }; PIXI.Strip.prototype._initWebGL = function(renderSession) { // build the strip! var gl = renderSession.gl; this._vertexBuffer = gl.createBuffer(); this._indexBuffer = gl.createBuffer(); this._uvBuffer = gl.createBuffer(); this._colorBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); }; PIXI.Strip.prototype._renderStrip = function(renderSession) { var gl = renderSession.gl; var projection = renderSession.projection, offset = renderSession.offset, shader = renderSession.shaderManager.stripShader; var drawMode = this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP ? gl.TRIANGLE_STRIP : gl.TRIANGLES; // gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); renderSession.blendModeManager.setBlendMode(this.blendMode); // set uniforms gl.uniformMatrix3fv(shader.translationMatrix, false, this.worldTransform.toArray(true)); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); gl.uniform1f(shader.alpha, this.worldAlpha); if(!this.dirty) { gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); // update the uvs gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); gl.activeTexture(gl.TEXTURE0); // check if a texture is dirty.. if(this.texture.baseTexture._dirty[gl.id]) { renderSession.renderer.updateTexture(this.texture.baseTexture); } else { // bind the current texture gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); } // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); } else { this.dirty = false; gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW); gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); // update the uvs gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); gl.activeTexture(gl.TEXTURE0); // check if a texture is dirty.. if(this.texture.baseTexture._dirty[gl.id]) { renderSession.renderer.updateTexture(this.texture.baseTexture); } else { gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); } // dont need to upload! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); } //console.log(gl.TRIANGLE_STRIP) // // gl.drawElements(drawMode, this.indices.length, gl.UNSIGNED_SHORT, 0); }; PIXI.Strip.prototype._renderCanvas = function(renderSession) { var context = renderSession.context; var transform = this.worldTransform; if (renderSession.roundPixels) { context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx | 0, transform.ty | 0); } else { context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); } if (this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP) { this._renderCanvasTriangleStrip(context); } else { this._renderCanvasTriangles(context); } }; PIXI.Strip.prototype._renderCanvasTriangleStrip = function(context) { // draw triangles!! var vertices = this.vertices; var uvs = this.uvs; var length = vertices.length / 2; this.count++; for (var i = 0; i < length - 2; i++) { // draw some triangles! var index = i * 2; this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); } }; PIXI.Strip.prototype._renderCanvasTriangles = function(context) { // draw triangles!! var vertices = this.vertices; var uvs = this.uvs; var indices = this.indices; var length = indices.length; this.count++; for (var i = 0; i < length; i += 3) { // draw some triangles! var index0 = indices[i] * 2, index1 = indices[i + 1] * 2, index2 = indices[i + 2] * 2; this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); } }; PIXI.Strip.prototype._renderCanvasDrawTriangle = function(context, vertices, uvs, index0, index1, index2) { var textureSource = this.texture.baseTexture.source; var textureWidth = this.texture.width; var textureHeight = this.texture.height; var x0 = vertices[index0], x1 = vertices[index1], x2 = vertices[index2]; var y0 = vertices[index0 + 1], y1 = vertices[index1 + 1], y2 = vertices[index2 + 1]; var u0 = uvs[index0] * textureWidth, u1 = uvs[index1] * textureWidth, u2 = uvs[index2] * textureWidth; var v0 = uvs[index0 + 1] * textureHeight, v1 = uvs[index1 + 1] * textureHeight, v2 = uvs[index2 + 1] * textureHeight; if (this.canvasPadding > 0) { var paddingX = this.canvasPadding / this.worldTransform.a; var paddingY = this.canvasPadding / this.worldTransform.d; var centerX = (x0 + x1 + x2) / 3; var centerY = (y0 + y1 + y2) / 3; var normX = x0 - centerX; var normY = y0 - centerY; var dist = Math.sqrt(normX * normX + normY * normY); x0 = centerX + (normX / dist) * (dist + paddingX); y0 = centerY + (normY / dist) * (dist + paddingY); // normX = x1 - centerX; normY = y1 - centerY; dist = Math.sqrt(normX * normX + normY * normY); x1 = centerX + (normX / dist) * (dist + paddingX); y1 = centerY + (normY / dist) * (dist + paddingY); normX = x2 - centerX; normY = y2 - centerY; dist = Math.sqrt(normX * normX + normY * normY); x2 = centerX + (normX / dist) * (dist + paddingX); y2 = centerY + (normY / dist) * (dist + paddingY); } 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(textureSource, 0, 0); context.restore(); }; /** * Renders a flat strip * * @method renderStripFlat * @param strip {Strip} The Strip to render * @private */ PIXI.Strip.prototype.renderStripFlat = function(strip) { var context = this.context; var vertices = strip.vertices; var length = vertices.length/2; this.count++; context.beginPath(); for (var i=1; i < length-2; i++) { // draw some triangles! var index = i*2; var x0 = vertices[index], x1 = vertices[index+2], x2 = vertices[index+4]; var y0 = vertices[index+1], y1 = vertices[index+3], y2 = vertices[index+5]; context.moveTo(x0, y0); context.lineTo(x1, y1); context.lineTo(x2, y2); } context.fillStyle = '#FF0000'; context.fill(); context.closePath(); }; /* PIXI.Strip.prototype.setTexture = function(texture) { //TODO SET THE TEXTURES //TODO VISIBILITY // stop current texture this.texture = texture; this.width = texture.frame.width; this.height = texture.frame.height; this.updateFrame = true; }; */ /** * When the texture is updated, this event will fire to update the scale and frame * * @method onTextureUpdate * @param event * @private */ PIXI.Strip.prototype.onTextureUpdate = function() { this.updateFrame = true; }; /** * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. * * @method getBounds * @param matrix {Matrix} the transformation matrix of the sprite * @return {Rectangle} the framing rectangle */ PIXI.Strip.prototype.getBounds = function(matrix) { var worldTransform = matrix || this.worldTransform; var a = worldTransform.a; var b = worldTransform.b; var c = worldTransform.c; var d = worldTransform.d; var tx = worldTransform.tx; var ty = worldTransform.ty; var maxX = -Infinity; var maxY = -Infinity; var minX = Infinity; var minY = Infinity; var vertices = this.vertices; for (var i = 0, n = vertices.length; i < n; i += 2) { var rawX = vertices[i], rawY = vertices[i + 1]; var x = (a * rawX) + (c * rawY) + tx; var y = (d * rawY) + (b * rawX) + ty; minX = x < minX ? x : minX; minY = y < minY ? y : minY; maxX = x > maxX ? x : maxX; maxY = y > maxY ? y : maxY; } if (minX === -Infinity || maxY === Infinity) { return PIXI.EmptyRectangle; } 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; }; /** * Different drawing buffer modes supported * * @property * @type {{TRIANGLE_STRIP: number, TRIANGLES: number}} * @static */ PIXI.Strip.DrawModes = { TRIANGLE_STRIP: 0, TRIANGLES: 1 }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 * @copyright Mat Groves, Rovanion Luckey */ /** * * @class Rope * @constructor * @extends Strip * @param {Texture} texture - The texture to use on the rope. * @param {Array} points - An array of {PIXI.Point}. * */ PIXI.Rope = function(texture, points) { PIXI.Strip.call( this, texture ); this.points = points; this.vertices = new PIXI.Float32Array(points.length * 4); this.uvs = new PIXI.Float32Array(points.length * 4); this.colors = new PIXI.Float32Array(points.length * 2); this.indices = new PIXI.Uint16Array(points.length * 2); this.refresh(); }; // constructor PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); PIXI.Rope.prototype.constructor = PIXI.Rope; /* * Refreshes * * @method refresh */ PIXI.Rope.prototype.refresh = function() { var points = this.points; if(points.length < 1) return; var uvs = this.uvs; var lastPoint = points[0]; var indices = this.indices; var colors = this.colors; this.count-=0.2; uvs[0] = 0; uvs[1] = 0; uvs[2] = 0; uvs[3] = 1; colors[0] = 1; colors[1] = 1; indices[0] = 0; indices[1] = 1; var total = points.length, point, index, amount; for (var i = 1; i < total; i++) { point = points[i]; index = i * 4; // time to do some smart drawing! amount = i / (total-1); if(i%2) { uvs[index] = amount; uvs[index+1] = 0; uvs[index+2] = amount; uvs[index+3] = 1; } else { uvs[index] = amount; uvs[index+1] = 0; uvs[index+2] = amount; uvs[index+3] = 1; } index = i * 2; colors[index] = 1; colors[index+1] = 1; index = i * 2; indices[index] = index; indices[index + 1] = index + 1; lastPoint = point; } }; /* * Updates the object transform for rendering * * @method updateTransform * @private */ PIXI.Rope.prototype.updateTransform = function() { var points = this.points; if(points.length < 1)return; var lastPoint = points[0]; var nextPoint; var perp = {x:0, y:0}; this.count-=0.2; var vertices = this.vertices; var total = points.length, point, index, ratio, perpLength, num; for (var i = 0; i < total; i++) { point = points[i]; index = i * 4; if(i < points.length-1) { nextPoint = points[i+1]; } else { nextPoint = point; } perp.y = -(nextPoint.x - lastPoint.x); perp.x = nextPoint.y - lastPoint.y; ratio = (1 - (i / (total-1))) * 10; if(ratio > 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; vertices[index] = point.x + perp.x; vertices[index+1] = point.y + perp.y; vertices[index+2] = point.x - perp.x; vertices[index+3] = point.y - perp.y; lastPoint = point; } PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); }; /* * Sets the texture that the Rope will use * * @method setTexture * @param texture {Texture} the texture that will be used */ 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 Sprite * @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 || 128; /** * The height of the tiling sprite * * @property height * @type Number */ this._height = height || 128; /** * 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(); /** * 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; /** * If enabled a green rectangle will be drawn behind the generated tiling texture, allowing you to visually * debug the texture being used. * * @property textureDebug * @type Boolean */ this.textureDebug = false; /** * The blend mode to be applied to the sprite * * @property blendMode * @type Number * @default PIXI.blendModes.NORMAL; */ this.blendMode = PIXI.blendModes.NORMAL; /** * The CanvasBuffer object that the tiled texture is drawn to. * * @property canvasBuffer * @type PIXI.CanvasBuffer */ this.canvasBuffer = null; /** * An internal Texture object that holds the tiling texture that was generated from TilingSprite.texture. * * @property tilingTexture * @type PIXI.Texture */ this.tilingTexture = null; /** * The Context fill pattern that is used to draw the TilingSprite in Canvas mode only (will be null in WebGL). * * @property tilePattern * @type PIXI.Texture */ this.tilePattern = null; /** * If true the TilingSprite will run generateTexture on its **next** render pass. * This is set by the likes of Phaser.LoadTexture.setFrame. * * @property refreshTexture * @type Boolean * @default true */ this.refreshTexture = true; this.frameWidth = 0; this.frameHeight = 0; }; PIXI.TilingSprite.prototype = Object.create(PIXI.Sprite.prototype); PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite; PIXI.TilingSprite.prototype.setTexture = function(texture) { if (this.texture !== texture) { this.texture = texture; this.refreshTexture = 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; } 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.refreshTexture) { this.generateTilingTexture(true); if (this.tilingTexture) { if (this.tilingTexture.needsUpdate) { renderSession.renderer.updateTexture(this.tilingTexture.baseTexture); this.tilingTexture.needsUpdate = false; } } else { return; } } renderSession.spriteBatch.renderTilingSprite(this); for (var i = 0; i < this.children.length; i++) { this.children[i]._renderWebGL(renderSession); } renderSession.spriteBatch.stop(); if (this._filters) { renderSession.filterManager.popFilter(); } if (this._mask) { renderSession.maskManager.popMask(this._mask, renderSession); } renderSession.spriteBatch.start(); }; /** * Renders the object using the Canvas renderer * * @method _renderCanvas * @param renderSession {RenderSession} * @private */ PIXI.TilingSprite.prototype._renderCanvas = function(renderSession) { if (this.visible === false || this.alpha === 0) { return; } var context = renderSession.context; if (this._mask) { renderSession.maskManager.pushMask(this._mask, renderSession); } context.globalAlpha = this.worldAlpha; var wt = this.worldTransform; var resolution = renderSession.resolution; context.setTransform(wt.a * resolution, wt.b * resolution, wt.c * resolution, wt.d * resolution, wt.tx * resolution, wt.ty * resolution); if (this.refreshTexture) { this.generateTilingTexture(false); if (this.tilingTexture) { this.tilePattern = context.createPattern(this.tilingTexture.baseTexture.source, 'repeat'); } else { return; } } var sessionBlendMode = renderSession.currentBlendMode; // Check blend mode if (this.blendMode !== renderSession.currentBlendMode) { renderSession.currentBlendMode = this.blendMode; context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; } var tilePosition = this.tilePosition; var tileScale = this.tileScale; tilePosition.x %= this.tilingTexture.baseTexture.width; tilePosition.y %= this.tilingTexture.baseTexture.height; // Translate context.scale(tileScale.x, tileScale.y); context.translate(tilePosition.x + (this.anchor.x * -this._width), tilePosition.y + (this.anchor.y * -this._height)); context.fillStyle = this.tilePattern; var tx = -tilePosition.x; var ty = -tilePosition.y; var tw = this._width / tileScale.x; var th = this._height / tileScale.y; // Allow for pixel rounding if (renderSession.roundPixels) { tx | 0; ty | 0; tw | 0; th | 0; } context.fillRect(tx, ty, tw, th); // Translate back again context.scale(1 / tileScale.x, 1 / tileScale.y); context.translate(-tilePosition.x + (this.anchor.x * this._width), -tilePosition.y + (this.anchor.y * this._height)); if (this._mask) { renderSession.maskManager.popMask(renderSession); } for (var i = 0; i < this.children.length; i++) { this.children[i]._renderCanvas(renderSession); } // Reset blend mode if (sessionBlendMode !== this.blendMode) { renderSession.currentBlendMode = sessionBlendMode; context.globalCompositeOperation = PIXI.blendModesCanvas[sessionBlendMode]; } }; /** * When the texture is updated, this event will fire to update the scale and frame * * @method onTextureUpdate * @param event * @private */ PIXI.TilingSprite.prototype.onTextureUpdate = function() { // overriding the sprite version of this! }; /** * * @method generateTilingTexture * * @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two */ PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo) { if (!this.texture.baseTexture.hasLoaded) { return; } var texture = this.texture; var frame = texture.frame; var targetWidth = this._frame.sourceSizeW; var targetHeight = this._frame.sourceSizeH; var dx = 0; var dy = 0; if (this._frame.trimmed) { dx = this._frame.spriteSourceSizeX; dy = this._frame.spriteSourceSizeY; } if (forcePowerOfTwo) { targetWidth = PIXI.getNextPowerOfTwo(targetWidth); targetHeight = PIXI.getNextPowerOfTwo(targetHeight); } if (this.canvasBuffer) { this.canvasBuffer.resize(targetWidth, targetHeight); this.tilingTexture.baseTexture.width = targetWidth; this.tilingTexture.baseTexture.height = targetHeight; this.tilingTexture.needsUpdate = true; } else { this.canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight); this.tilingTexture = PIXI.Texture.fromCanvas(this.canvasBuffer.canvas); this.tilingTexture.isTiling = true; this.tilingTexture.needsUpdate = true; } if (this.textureDebug) { this.canvasBuffer.context.strokeStyle = '#00ff00'; this.canvasBuffer.context.strokeRect(0, 0, targetWidth, targetHeight); } this.canvasBuffer.context.drawImage(texture.baseTexture.source, texture.crop.x, texture.crop.y, texture.crop.width, texture.crop.height, dx, dy, texture.crop.width, texture.crop.height); this.tileScaleOffset.x = frame.width / targetWidth; this.tileScaleOffset.y = frame.height / targetHeight; this.refreshTexture = false; this.tilingTexture.baseTexture._powerOf2 = true; }; /** * Returns the framing rectangle of the sprite as a PIXI.Rectangle object * * @method getBounds * @return {Rectangle} the framing rectangle */ PIXI.TilingSprite.prototype.getBounds = function() { var width = this._width; var height = this._height; var w0 = width * (1-this.anchor.x); var w1 = width * -this.anchor.x; var h0 = height * (1-this.anchor.y); var h1 = height * -this.anchor.y; var worldTransform = this.worldTransform; var a = worldTransform.a; var b = worldTransform.b; var c = worldTransform.c; var d = worldTransform.d; var tx = worldTransform.tx; var ty = worldTransform.ty; var x1 = a * w1 + c * h1 + tx; var y1 = d * h1 + b * w1 + ty; var x2 = a * w0 + c * h1 + tx; var y2 = d * h1 + b * w0 + ty; var x3 = a * w0 + c * h0 + tx; var y3 = d * h0 + b * w0 + ty; var x4 = a * w1 + c * h0 + tx; var y4 = d * h0 + b * w1 + ty; var maxX = -Infinity; var maxY = -Infinity; var minX = Infinity; var minY = Infinity; 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; 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; }; PIXI.TilingSprite.prototype.destroy = function () { PIXI.Sprite.prototype.destroy.call(this); this.tileScale = null; this.tileScaleOffset = null; this.tilePosition = null; if (this.tilingTexture) { this.tilingTexture.destroy(true); this.tilingTexture = null; } }; /** * 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; } }); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ PIXI.BaseTextureCache = {}; PIXI.BaseTextureCacheIdGenerator = 0; /** * A texture stores the information that represents an image. All textures have a base texture. * * @class BaseTexture * @uses EventTarget * @constructor * @param source {String} the source object (image or canvas) * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values */ PIXI.BaseTexture = function(source, scaleMode) { /** * The Resolution of the texture. * * @property resolution * @type Number */ this.resolution = 1; /** * [read-only] The width of the base texture set when the image has loaded * * @property width * @type Number * @readOnly */ this.width = 100; /** * [read-only] The height of the base texture set when the image has loaded * * @property height * @type Number * @readOnly */ this.height = 100; /** * The scale mode to apply when scaling this texture * * @property scaleMode * @type {Number} * @default PIXI.scaleModes.LINEAR */ this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; /** * [read-only] Set to true once the base texture has loaded * * @property hasLoaded * @type Boolean * @readOnly */ this.hasLoaded = false; /** * The image source that is used to create the texture. * * @property source * @type Image */ this.source = source; this._UID = PIXI._UID++; /** * Controls if RGB channels should be pre-multiplied by Alpha (WebGL only) * * @property premultipliedAlpha * @type Boolean * @default true */ this.premultipliedAlpha = true; // used for webGL /** * @property _glTextures * @type Array * @private */ this._glTextures = []; /** * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work * * @property mipmap * @type {Boolean} */ this.mipmap = false; /** * @property _dirty * @type Array * @private */ this._dirty = [true, true, true, true]; if (!source) { return; } if ((this.source.complete || this.source.getContext) && this.source.width && this.source.height) { this.hasLoaded = true; this.width = this.source.naturalWidth || this.source.width; this.height = this.source.naturalHeight || this.source.height; this.dirty(); } /** * @property imageUrl * @type String */ this.imageUrl = null; /** * @property _powerOf2 * @type Boolean * @private */ this._powerOf2 = false; }; PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; /** * Forces this BaseTexture to be set as loaded, with the given width and height. * Then calls BaseTexture.dirty. * Important for when you don't want to modify the source object by forcing in `complete` or dimension properties it may not have. * * @method forceLoaded * @param {number} width - The new width to force the BaseTexture to be. * @param {number} height - The new height to force the BaseTexture to be. */ PIXI.BaseTexture.prototype.forceLoaded = function(width, height) { this.hasLoaded = true; this.width = width; this.height = height; this.dirty(); }; /** * Destroys this base texture * * @method destroy */ PIXI.BaseTexture.prototype.destroy = function() { if (this.imageUrl) { delete PIXI.BaseTextureCache[this.imageUrl]; delete PIXI.TextureCache[this.imageUrl]; this.imageUrl = null; if (!navigator.isCocoonJS) this.source.src = ''; } else if (this.source && this.source._pixiId) { delete PIXI.BaseTextureCache[this.source._pixiId]; } this.source = null; this.unloadFromGPU(); }; /** * Changes the source image of the texture * * @method updateSourceImage * @param newSrc {String} the path of the image */ PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) { this.hasLoaded = false; this.source.src = null; this.source.src = newSrc; }; /** * Sets all glTextures to be dirty. * * @method dirty */ PIXI.BaseTexture.prototype.dirty = function() { for (var i = 0; i < this._glTextures.length; i++) { this._dirty[i] = true; } }; /** * Removes the base texture from the GPU, useful for managing resources on the GPU. * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. * * @method unloadFromGPU */ PIXI.BaseTexture.prototype.unloadFromGPU = function() { this.dirty(); // delete the webGL textures if any. for (var i = this._glTextures.length - 1; i >= 0; i--) { var glTexture = this._glTextures[i]; var gl = PIXI.glContexts[i]; if(gl && glTexture) { gl.deleteTexture(glTexture); } } this._glTextures.length = 0; this.dirty(); }; /** * Helper function that creates a base texture from the given image url. * 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} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @return BaseTexture */ PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) { var baseTexture = PIXI.BaseTextureCache[imageUrl]; if(crossorigin === undefined && imageUrl.indexOf('data:') === -1) crossorigin = true; if(!baseTexture) { // new Image() breaks tex loading in some versions of Chrome. // See https://code.google.com/p/chromium/issues/detail?id=238071 var image = new Image();//document.createElement('img'); if (crossorigin) { image.crossOrigin = ''; } image.src = imageUrl; baseTexture = new PIXI.BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; PIXI.BaseTextureCache[imageUrl] = baseTexture; // if there is an @2x at the end of the url we are going to assume its a highres image if( imageUrl.indexOf(PIXI.RETINA_PREFIX + '.') !== -1) { baseTexture.resolution = 2; } } return baseTexture; }; /** * Helper function that creates a base texture from the given canvas element. * * @static * @method fromCanvas * @param canvas {Canvas} The canvas element source of the texture * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @return BaseTexture */ 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 */ PIXI.TextureCache = {}; PIXI.FrameCache = {}; /** * TextureSilentFail is a boolean that defaults to `false`. * If `true` then `PIXI.Texture.setFrame` will no longer throw an error if the texture dimensions are incorrect. * Instead `Texture.valid` will be set to `false` (#1556) * * @type {boolean} */ PIXI.TextureSilentFail = false; 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. Instead use it as the texture for a PIXI.Sprite. If no frame is provided then the whole image is used. * * @class Texture * @uses EventTarget * @constructor * @param baseTexture {BaseTexture} The base texture source to create the texture from * @param frame {Rectangle} The rectangle frame of the texture to show * @param [crop] {Rectangle} The area of original texture * @param [trim] {Rectangle} Trimmed texture rectangle */ PIXI.Texture = function(baseTexture, frame, crop, trim) { /** * Does this Texture have any frame data assigned to it? * * @property noFrame * @type Boolean */ this.noFrame = false; if (!frame) { this.noFrame = true; frame = new PIXI.Rectangle(0,0,1,1); } if (baseTexture instanceof PIXI.Texture) { baseTexture = baseTexture.baseTexture; } /** * The base texture that this texture uses. * * @property baseTexture * @type BaseTexture */ this.baseTexture = baseTexture; /** * The frame specifies the region of the base texture that this texture uses * * @property frame * @type Rectangle */ this.frame = frame; /** * The texture trim data. * * @property trim * @type Rectangle */ this.trim = trim; /** * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. * * @property valid * @type Boolean */ this.valid = false; /** * Is this a tiling texture? As used by the likes of a TilingSprite. * * @property isTiling * @type Boolean */ this.isTiling = false; /** * This will let a renderer know that a texture has been updated (used mainly for webGL uv updates) * * @property requiresUpdate * @type Boolean */ this.requiresUpdate = false; /** * This will let a renderer know that a tinted parent has updated its texture. * * @property requiresReTint * @type Boolean */ this.requiresReTint = false; /** * The WebGL UV data cache. * * @property _uvs * @type Object * @private */ this._uvs = null; /** * The width of the Texture in pixels. * * @property width * @type Number */ this.width = 0; /** * The height of the Texture in pixels. * * @property height * @type Number */ this.height = 0; /** * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) * * @property crop * @type Rectangle */ this.crop = crop || new PIXI.Rectangle(0, 0, 1, 1); if (baseTexture.hasLoaded) { if (this.noFrame) frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height); this.setFrame(frame); } }; PIXI.Texture.prototype.constructor = PIXI.Texture; /** * Called when the base texture is loaded * * @method onBaseTextureLoaded * @private */ PIXI.Texture.prototype.onBaseTextureLoaded = function() { var baseTexture = this.baseTexture; if (this.noFrame) { this.frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height); } this.setFrame(this.frame); }; /** * Destroys this texture * * @method destroy * @param destroyBase {Boolean} Whether to destroy the base texture as well */ PIXI.Texture.prototype.destroy = function(destroyBase) { if (destroyBase) this.baseTexture.destroy(); this.valid = false; }; /** * Specifies the region of the baseTexture that this texture will use. * * @method setFrame * @param frame {Rectangle} The frame of the texture to set it to */ PIXI.Texture.prototype.setFrame = function(frame) { this.noFrame = false; this.frame = frame; this.width = frame.width; this.height = frame.height; this.crop.x = frame.x; this.crop.y = frame.y; this.crop.width = frame.width; this.crop.height = frame.height; if (!this.trim && (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)) { if (!PIXI.TextureSilentFail) { throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); } this.valid = false; return; } this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; if (this.trim) { this.width = this.trim.width; this.height = this.trim.height; this.frame.width = this.trim.width; this.frame.height = this.trim.height; } if (this.valid) this._updateUvs(); }; /** * Updates the internal WebGL UV cache. * * @method _updateUvs * @private */ PIXI.Texture.prototype._updateUvs = function() { if(!this._uvs)this._uvs = new PIXI.TextureUvs(); var frame = this.crop; 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 creates a Texture object from the given image url. * If the image is not in the texture cache it will be created and loaded. * * @static * @method fromImage * @param imageUrl {String} The image url of the texture * @param crossorigin {Boolean} Whether requests should be treated as crossorigin * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @return Texture */ PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode) { var texture = PIXI.TextureCache[imageUrl]; if(!texture) { texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin, scaleMode)); PIXI.TextureCache[imageUrl] = texture; } return texture; }; /** * Helper function that returns a Texture objected based on the given frame id. * If the frame id is not in the texture cache an error will be thrown. * * @static * @method fromFrame * @param frameId {String} The frame id of the texture * @return Texture */ 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 '); return texture; }; /** * Helper function that creates a new a Texture based on the given canvas element. * * @static * @method fromCanvas * @param canvas {Canvas} The canvas element source of the texture * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @return Texture */ PIXI.Texture.fromCanvas = function(canvas, scaleMode) { var baseTexture = PIXI.BaseTexture.fromCanvas(canvas, scaleMode); return new PIXI.Texture( baseTexture ); }; /** * Adds a texture to the global PIXI.TextureCache. This cache is shared across the whole PIXI object. * * @static * @method addTextureToCache * @param texture {Texture} The Texture to add to the cache. * @param id {String} The id that the texture will be stored against. */ PIXI.Texture.addTextureToCache = function(texture, id) { PIXI.TextureCache[id] = texture; }; /** * Remove a texture from the global PIXI.TextureCache. * * @static * @method removeTextureFromCache * @param id {String} The id of the texture to be removed * @return {Texture} The texture that was removed */ PIXI.Texture.removeTextureFromCache = function(id) { var texture = PIXI.TextureCache[id]; delete PIXI.TextureCache[id]; delete PIXI.BaseTextureCache[id]; return texture; }; 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.y3 = 0; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * A RenderTexture is a special texture that allows any Pixi display object to be rendered to it. * * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded otherwise black rectangles will be drawn instead. * * A RenderTexture takes a snapshot of any Display Object given to its render method. The position and rotation of the given Display Objects is ignored. For example: * * var renderTexture = new PIXI.RenderTexture(800, 600); * var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); * sprite.position.x = 800/2; * sprite.position.y = 600/2; * sprite.anchor.x = 0.5; * sprite.anchor.y = 0.5; * renderTexture.render(sprite); * * The Sprite in this case will be rendered to a position of 0,0. To render this sprite at its actual position a DisplayObjectContainer should be used: * * var doc = new PIXI.DisplayObjectContainer(); * 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 * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values * @param resolution {Number} The resolution of the texture being generated */ PIXI.RenderTexture = function(width, height, renderer, scaleMode, resolution) { /** * 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; /** * The Resolution of the texture. * * @property resolution * @type Number */ this.resolution = resolution || 1; /** * The framing rectangle of the render texture * * @property frame * @type Rectangle */ this.frame = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); /** * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) * * @property crop * @type Rectangle */ this.crop = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); /** * The base texture object that this texture uses * * @property baseTexture * @type BaseTexture */ this.baseTexture = new PIXI.BaseTexture(); this.baseTexture.width = this.width * this.resolution; this.baseTexture.height = this.height * this.resolution; this.baseTexture._glTextures = []; this.baseTexture.resolution = this.resolution; this.baseTexture.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; this.baseTexture.hasLoaded = true; PIXI.Texture.call(this, this.baseTexture, new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution) ); /** * The renderer this RenderTexture uses. A RenderTexture can only belong to one renderer at the moment if its webGL. * * @property renderer * @type CanvasRenderer|WebGLRenderer */ this.renderer = renderer || PIXI.defaultRenderer; if (this.renderer.type === PIXI.WEBGL_RENDERER) { var gl = this.renderer.gl; this.baseTexture._dirty[gl.id] = false; this.textureBuffer = new PIXI.FilterTexture(gl, this.width, this.height, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; this.render = this.renderWebGL; this.projection = new PIXI.Point(this.width * 0.5, -this.height * 0.5); } else { this.render = this.renderCanvas; this.textureBuffer = new PIXI.CanvasBuffer(this.width * this.resolution, this.height * this.resolution); this.baseTexture.source = this.textureBuffer.canvas; } /** * @property valid * @type Boolean */ this.valid = true; this.tempMatrix = new Phaser.Matrix(); this._updateUvs(); }; PIXI.RenderTexture.prototype = Object.create(PIXI.Texture.prototype); PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture; /** * Resizes the RenderTexture. * * @method resize * @param width {Number} The width to resize to. * @param height {Number} The height to resize to. * @param updateBase {Boolean} Should the baseTexture.width and height values be resized as well? */ PIXI.RenderTexture.prototype.resize = function(width, height, updateBase) { if (width === this.width && height === this.height)return; this.valid = (width > 0 && height > 0); this.width = width; this.height = height; this.frame.width = this.crop.width = width * this.resolution; this.frame.height = this.crop.height = height * this.resolution; if (updateBase) { this.baseTexture.width = this.width * this.resolution; this.baseTexture.height = this.height * this.resolution; } if (this.renderer.type === PIXI.WEBGL_RENDERER) { this.projection.x = this.width / 2; this.projection.y = -this.height / 2; } if(!this.valid)return; this.textureBuffer.resize(this.width, this.height); }; /** * Clears the RenderTexture. * * @method clear */ PIXI.RenderTexture.prototype.clear = function() { if (!this.valid) { return; } if (this.renderer.type === PIXI.WEBGL_RENDERER) { this.renderer.gl.bindFramebuffer(this.renderer.gl.FRAMEBUFFER, this.textureBuffer.frameBuffer); } this.textureBuffer.clear(); }; /** * This function will draw the display object to the texture. * * @method renderWebGL * @param displayObject {DisplayObject} The display object to render this texture on * @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering. * @param [clear] {Boolean} If true the texture will be cleared before the displayObject is drawn * @private */ PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, matrix, clear) { if (!this.valid || displayObject.alpha === 0) { return; } // Frame buffers come in upside down so we need to flip the matrix matrix.translate(0, this.projection.y * 2); matrix.scale(1, -1); // Time to update all the children of the displayObject with the new matrix. for (var i = 0; i < displayObject.children.length; i++) { displayObject.children[i].updateTransform(); } // Time for the webGL fun stuff! var gl = this.renderer.gl; gl.viewport(0, 0, this.width * this.resolution, this.height * this.resolution); gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer ); if (clear) { this.textureBuffer.clear(); } this.renderer.spriteBatch.dirty = true; this.renderer.renderDisplayObject(displayObject, this.projection, this.textureBuffer.frameBuffer, matrix); this.renderer.spriteBatch.dirty = true; }; /** * This function will draw the display object to the texture. * * @method renderCanvas * @param displayObject {DisplayObject} The display object to render this texture on * @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering. * @param [clear] {Boolean} If true the texture will be cleared before the displayObject is drawn * @private */ PIXI.RenderTexture.prototype.renderCanvas = function(displayObject, matrix, clear) { if (!this.valid || displayObject.alpha === 0) { return; } // Time to update all the children of the displayObject with the new matrix (what new matrix? there isn't one!) for (var i = 0; i < displayObject.children.length; i++) { displayObject.children[i].updateTransform(); } if (clear) { this.textureBuffer.clear(); } var realResolution = this.renderer.resolution; this.renderer.resolution = this.resolution; this.renderer.renderDisplayObject(displayObject, this.textureBuffer.context, matrix); this.renderer.resolution = realResolution; }; /** * Will return a HTML Image of the texture * * @method getImage * @return {Image} */ PIXI.RenderTexture.prototype.getImage = function() { var image = new Image(); image.src = this.getBase64(); return image; }; /** * Will return a base64 encoded string of this texture. It works by calling RenderTexture.getCanvas and then running toDataURL on that. * * @method getBase64 * @return {String} A base64 encoded string of the texture. */ PIXI.RenderTexture.prototype.getBase64 = function() { return this.getCanvas().toDataURL(); }; /** * Creates a Canvas element, renders this RenderTexture to it and then returns it. * * @method getCanvas * @return {HTMLCanvasElement} A Canvas element with the texture rendered on. */ PIXI.RenderTexture.prototype.getCanvas = function() { if (this.renderer.type === PIXI.WEBGL_RENDERER) { var gl = this.renderer.gl; var width = this.textureBuffer.width; var height = this.textureBuffer.height; var webGLPixels = new Uint8Array(4 * width * height); gl.bindFramebuffer(gl.FRAMEBUFFER, this.textureBuffer.frameBuffer); gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, webGLPixels); gl.bindFramebuffer(gl.FRAMEBUFFER, null); var tempCanvas = new PIXI.CanvasBuffer(width, height); var canvasData = tempCanvas.context.getImageData(0, 0, width, height); canvasData.data.set(webGLPixels); tempCanvas.context.putImageData(canvasData, 0, 0); return tempCanvas.canvas; } else { return this.textureBuffer.canvas; } }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * This is the base class for creating a PIXI 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 {Array} The fragment source in an array of strings. * @param uniforms {Object} An object containing the uniforms for this filter. */ 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(Filter) * @private */ this.passes = [this]; /** * @property shaders * @type Array(Shader) * @private */ this.shaders = []; /** * @property dirty * @type Boolean */ this.dirty = true; /** * @property padding * @type Number */ this.padding = 0; /** * @property uniforms * @type object * @private */ this.uniforms = uniforms || {}; /** * @property fragmentSrc * @type Array * @private */ this.fragmentSrc = fragmentSrc || []; }; PIXI.AbstractFilter.prototype.constructor = PIXI.AbstractFilter; /** * Syncs the uniforms between the class object and the shaders. * * @method syncUniforms */ PIXI.AbstractFilter.prototype.syncUniforms = function() { for(var i=0,j=this.shaders.length; i * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ (function(){ var root = this; /* global Phaser:true */ /** * @author Richard Davey * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * @namespace Phaser */ var Phaser = Phaser || { VERSION: '2.4.0-dev', GAMES: [], AUTO: 0, CANVAS: 1, WEBGL: 2, HEADLESS: 3, NONE: 0, LEFT: 1, RIGHT: 2, UP: 3, DOWN: 4, SPRITE: 0, BUTTON: 1, IMAGE: 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, ELLIPSE: 16, SPRITEBATCH: 17, RETROFONT: 18, POINTER: 19, ROPE: 20, CIRCLE: 21, RECTANGLE: 22, LINE: 23, MATRIX: 24, POINT: 25, ROUNDEDRECTANGLE: 26, CREATURE: 27, VIDEO: 28, /** * Various blend modes supported by Pixi. * IMPORTANT: The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes. * * @property {Object} blendModes * @property {Number} blendModes.NORMAL * @property {Number} blendModes.ADD * @property {Number} blendModes.MULTIPLY * @property {Number} blendModes.SCREEN * @property {Number} blendModes.OVERLAY * @property {Number} blendModes.DARKEN * @property {Number} blendModes.LIGHTEN * @property {Number} blendModes.COLOR_DODGE * @property {Number} blendModes.COLOR_BURN * @property {Number} blendModes.HARD_LIGHT * @property {Number} blendModes.SOFT_LIGHT * @property {Number} blendModes.DIFFERENCE * @property {Number} blendModes.EXCLUSION * @property {Number} blendModes.HUE * @property {Number} blendModes.SATURATION * @property {Number} blendModes.COLOR * @property {Number} blendModes.LUMINOSITY * @static */ 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 that are supported by Pixi. * * The DEFAULT scale mode affects the default scaling mode of future operations. * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability. * * @property {Object} scaleModes * @property {Number} scaleModes.DEFAULT=LINEAR * @property {Number} scaleModes.LINEAR Smooth scaling * @property {Number} scaleModes.NEAREST Pixelating scaling * @static */ scaleModes: { DEFAULT:0, LINEAR:0, NEAREST:1 } }; /** * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ // ES6 Math.trunc - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc if (!Math.trunc) { Math.trunc = function trunc(x) { return x < 0 ? Math.ceil(x) : Math.floor(x); }; } /** * A polyfill for Function.prototype.bind */ if (!Function.prototype.bind) { /* jshint freeze: false */ Function.prototype.bind = (function () { var slice = Array.prototype.slice; return function (thisArg) { var target = this, boundArgs = slice.call(arguments, 1); if (typeof target !== 'function') { throw new TypeError(); } function bound() { var args = boundArgs.concat(slice.call(arguments)); target.apply(this instanceof bound ? this : thisArg, args); } bound.prototype = (function F(proto) { if (proto) { F.prototype = proto; } if (!(this instanceof F)) { /* jshint supernew: true */ return new F; } })(target.prototype); return bound; }; })(); } /** * A polyfill for Array.isArray */ if (!Array.isArray) { Array.isArray = function (arg) { return Object.prototype.toString.call(arg) == '[object Array]'; }; } /** * A polyfill for Array.forEach * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach */ if (!Array.prototype.forEach) { Array.prototype.forEach = function(fun /*, thisArg */) { "use strict"; if (this === void 0 || this === null) { throw new TypeError(); } var t = Object(this); var len = t.length >>> 0; if (typeof fun !== "function") { throw new TypeError(); } var thisArg = arguments.length >= 2 ? arguments[1] : void 0; for (var i = 0; i < len; i++) { if (i in t) { fun.call(thisArg, t[i], i, t); } } }; } /** * Low-budget Float32Array knock-off, suitable for use with P2.js in IE9 * Source: http://www.html5gamedevs.com/topic/5988-phaser-12-ie9/ * Cameron Foale (http://www.kibibu.com) */ if (typeof window.Uint32Array !== "function" && typeof window.Uint32Array !== "object") { var CheapArray = function(type) { var proto = new Array(); // jshint ignore:line window[type] = function(arg) { if (typeof(arg) === "number") { Array.call(this, arg); this.length = arg; for (var i = 0; i < this.length; i++) { this[i] = 0; } } else { Array.call(this, arg.length); this.length = arg.length; for (var i = 0; i < this.length; i++) { this[i] = arg[i]; } } }; window[type].prototype = proto; window[type].constructor = window[type]; }; CheapArray('Uint32Array'); // jshint ignore:line CheapArray('Int16Array'); // jshint ignore:line } /** * Also fix for the absent console in IE9 */ if (!window.console) { window.console = {}; window.console.log = window.console.assert = function(){}; window.console.warn = window.console.assert = function(){}; } /** * @author Richard Davey * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * @class Phaser.Utils * @static */ Phaser.Utils = { /** * Gets an objects property by string. * * @method Phaser.Utils.getProperty * @param {object} obj - The object to traverse. * @param {string} prop - The property whose value will be returned. * @return {*} the value of the property or null if property isn't found . */ getProperty: function(obj, prop) { var parts = prop.split('.'), last = parts.pop(), l = parts.length, i = 1, current = parts[0]; while (i < l && (obj = obj[current])) { current = parts[i]; i++; } if (obj) { return obj[last]; } else { return null; } }, /** * Sets an objects property by string. * * @method Phaser.Utils.setProperty * @param {object} obj - The object to traverse * @param {string} prop - The property whose value will be changed * @return {object} The object on which the property was set. */ setProperty: function(obj, prop, value) { var parts = prop.split('.'), last = parts.pop(), l = parts.length, i = 1, current = parts[0]; while (i < l && (obj = obj[current])) { current = parts[i]; i++; } if (obj) { obj[last] = value; } return obj; }, /** * Generate a random bool result based on the chance value. * * Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance * of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed. * * @method Phaser.Math#chanceRoll * @param {number} chance - The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%). * @return {boolean} True if the roll passed, or false otherwise. */ chanceRoll: function (chance) { if (typeof chance === 'undefined') { chance = 50; } return chance > 0 && (Math.random() * 100 <= chance); }, /** * Choose between one of two values randomly. * * @method Phaser.Utils#randomChoice * @param {any} choice1 * @param {any} choice2 * @return {any} The randomly selected choice */ randomChoice: function (choice1, choice2) { return (Math.random() < 0.5) ? choice1 : choice2; }, /** * Get a unit dimension from a string. * * @method Phaser.Utils.parseDimension * @param {string|number} size - The size to parse. * @param {number} dimension - The window dimension to check. * @return {number} The parsed dimension. */ parseDimension: function (size, dimension) { var f = 0; var px = 0; if (typeof size === 'string') { // %? if (size.substr(-1) === '%') { f = parseInt(size, 10) / 100; if (dimension === 0) { px = window.innerWidth * f; } else { px = window.innerHeight * f; } } else { px = parseInt(size, 10); } } else { px = size; } return px; }, /** * JavaScript string pad http://www.webtoolkit.info/. * * @method Phaser.Utils.pad * @param {string} str - The target string. * @param {integer} [len=0] - The number of characters to be added. * @param {string} [pad=" "] - The string to pad it out with (defaults to a space). * @param {integer} [dir=3] The direction dir = 1 (left), 2 (right), 3 (both). * @return {string} The padded string */ pad: function (str, len, pad, dir) { if (typeof(len) === "undefined") { var len = 0; } if (typeof(pad) === "undefined") { var pad = ' '; } if (typeof(dir) === "undefined") { var dir = 3; } var padlen = 0; if (len + 1 >= str.length) { switch (dir) { case 1: str = new Array(len + 1 - str.length).join(pad) + str; break; case 3: var right = Math.ceil((padlen = len - str.length) / 2); var left = padlen - right; str = new Array(left+1).join(pad) + str + new Array(right+1).join(pad); break; default: str = str + new Array(len + 1 - str.length).join(pad); break; } } return str; }, /** * This is a slightly modified version of jQuery.isPlainObject. * A plain object is an object whose internal class property is [object Object]. * @method Phaser.Utils.isPlainObject * @param {object} obj - The object to inspect. * @return {boolean} - true if the object is plain, otherwise false. */ isPlainObject: function (obj) { // Not plain objects: // - Any object or value whose internal [[Class]] property is not "[object Object]" // - DOM nodes // - window if (typeof(obj) !== "object" || obj.nodeType || obj === obj.window) { return false; } // Support: Firefox <20 // The try/catch suppresses exceptions thrown when attempting to access // the "constructor" property of certain host objects, ie. |window.location| // https://bugzilla.mozilla.org/show_bug.cgi?id=814622 try { if (obj.constructor && !({}).hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) { return false; } } catch (e) { return false; } // If the function hasn't returned already, we're confident that // |obj| is a plain object, created by {} or constructed with new Object return true; }, /** * This is a slightly modified version of http://api.jquery.com/jQuery.extend/ * * @method Phaser.Utils.extend * @param {boolean} deep - Perform a deep copy? * @param {object} target - The target object to copy to. * @return {object} The extended object. */ extend: function () { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if (typeof target === "boolean") { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // extend Phaser if only one argument is passed if (length === i) { target = this; --i; } for (; i < length; i++) { // Only deal with non-null/undefined values if ((options = arguments[i]) != null) { // Extend the base object for (name in options) { src = target[name]; copy = options[name]; // Prevent never-ending loop if (target === copy) { continue; } // Recurse if we're merging plain objects or arrays if (deep && copy && (Phaser.Utils.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && Array.isArray(src) ? src : []; } else { clone = src && Phaser.Utils.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[name] = Phaser.Utils.extend(deep, clone, copy); // Don't bring in undefined values } else if (copy !== undefined) { target[name] = copy; } } } } // Return the modified object return target; }, /** * Mixes in an existing mixin object with the target. * * Values in the mixin that have either `get` or `set` functions are created as properties via `defineProperty` * _except_ if they also define a `clone` method - if a clone method is defined that is called instead and * the result is assigned directly. * * @method Phaser.Utils.mixinPrototype * @param {object} target - The target object to receive the new functions. * @param {object} mixin - The object to copy the functions from. * @param {boolean} [replace=false] - If the target object already has a matching function should it be overwritten or not? */ mixinPrototype: function (target, mixin, replace) { if (typeof replace === 'undefined') { replace = false; } var mixinKeys = Object.keys(mixin); for (var i = 0; i < mixinKeys.length; i++) { var key = mixinKeys[i]; var value = mixin[key]; if (!replace && (key in target)) { // Not overwriting existing property continue; } else { if (value && (typeof value.get === 'function' || typeof value.set === 'function')) { // Special case for classes like Phaser.Point which has a 'set' function! if (typeof value.clone === 'function') { target[key] = value.clone(); } else { Object.defineProperty(target, key, value); } } else { target[key] = value; } } } }, /** * Mixes the source object into the destination object, returning the newly modified destination object. * Based on original code by @mudcube * * @method Phaser.Utils.mixin * @param {object} from - The object to copy (the source object). * @param {object} to - The object to copy to (the destination object). * @return {object} The modified destination object. */ mixin: function (from, to) { if (!from || typeof (from) !== "object") { return to; } for (var key in from) { var o = from[key]; if (o.childNodes || o.cloneNode) { continue; } var type = typeof (from[key]); if (!from[key] || type !== "object") { to[key] = from[key]; } else { // Clone sub-object if (typeof (to[key]) === type) { to[key] = Phaser.Utils.mixin(from[key], to[key]); } else { to[key] = Phaser.Utils.mixin(from[key], new o.constructor()); } } } return to; } }; /** * @author Richard Davey * @copyright 2015 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 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. */ Phaser.Circle = function (x, y, diameter) { x = x || 0; y = y || 0; diameter = diameter || 0; /** * @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. * @private */ this._diameter = diameter; /** * @property {number} _radius - The radius of the circle. * @private */ this._radius = 0; if (diameter > 0) { this._radius = diameter * 0.5; } /** * @property {number} type - The const type of this object. * @readonly */ this.type = Phaser.CIRCLE; }; Phaser.Circle.prototype = { /** * The circumference of the circle. * * @method Phaser.Circle#circumference * @return {number} The circumference of the circle. */ circumference: function () { return 2 * (Math.PI * this._radius); }, /** * Returns a uniformly distributed random point from anywhere within this Circle. * * @method Phaser.Circle#random * @param {Phaser.Point|object} [out] - A Phaser.Point, or any object with public x/y properties, that the values will be set in. * If no object is provided a new Phaser.Point object will be created. In high performance areas avoid this by re-using an existing object. * @return {Phaser.Point} An object containing the random point in its `x` and `y` properties. */ random: function (out) { if (typeof out === 'undefined') { out = new Phaser.Point(); } var t = 2 * Math.PI * Math.random(); var u = Math.random() + Math.random(); var r = (u > 1) ? 2 - u : u; var x = r * Math.cos(t); var y = r * Math.sin(t); out.x = this.x + (x * this.radius); out.y = this.y + (y * this.radius); return out; }, /** * Returns the framing rectangle of the circle as a Phaser.Rectangle object. * * @method Phaser.Circle#getBounds * @return {Phaser.Rectangle} The bounds of the Circle. */ getBounds: function () { return new Phaser.Rectangle(this.x - this.radius, this.y - this.radius, this.diameter, this.diameter); }, /** * 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. * @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; }, /** * 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); }, /** * 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; }, /** * 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=false] - Round the distance to the nearest integer. * @return {number} The distance between this Point object and the destination Point object. */ distance: function (dest, round) { var distance = Phaser.Math.distance(this.x, this.y, dest.x, dest.y); return round ? Math.round(distance) : distance; }, /** * 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} output - 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 (output) { if (typeof output === "undefined" || output === null) { output = new Phaser.Circle(this.x, this.y, this.diameter); } else { output.setTo(this.x, this.y, this.diameter); } return output; }, /** * 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=false] - 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 (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; } }; /** * 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=false] - 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.degToRad(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 * @author Chad Engler * @copyright 2015 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 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. */ Phaser.Ellipse = 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 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; /** * @property {number} type - The const type of this object. * @readonly */ this.type = Phaser.ELLIPSE; }; 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; }, /** * Returns the framing rectangle of the ellipse as a Phaser.Rectangle object. * * @method Phaser.Ellipse#getBounds * @return {Phaser.Rectangle} The bounds of the Ellipse. */ getBounds: function () { return new Phaser.Rectangle(this.x - this.width, this.y - this.height, this.width, this.height); }, /** * 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, width and height properties from this Ellipse 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} output - 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(output) { if (typeof output === "undefined" || output === null) { output = new Phaser.Ellipse(this.x, this.y, this.width, this.height); } else { output.setTo(this.x, this.y, this.width, this.height); } return output; }, /** * 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 uniformly distributed random point from anywhere within this Ellipse. * * @method Phaser.Ellipse#random * @param {Phaser.Point|object} [out] - A Phaser.Point, or any object with public x/y properties, that the values will be set in. * If no object is provided a new Phaser.Point object will be created. In high performance areas avoid this by re-using an existing object. * @return {Phaser.Point} An object containing the random point in its `x` and `y` properties. */ random: function (out) { if (typeof out === 'undefined') { out = new Phaser.Point(); } var p = Math.random() * Math.PI * 2; var r = Math.random(); out.x = Math.sqrt(r) * Math.cos(p); out.y = Math.sqrt(r) * Math.sin(p); out.x = this.x + (out.x * this.width / 2.0); out.y = this.y + (out.y * this.height / 2.0); return out; }, /** * 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 = value - this.x; } } }); /** * 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 = value - this.y; } } }); /** * 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); }; // 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 * @copyright 2015 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 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. */ 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); /** * @property {number} type - The const type of this object. * @readonly */ this.type = Phaser.LINE; }; 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=false] - If true it will use startSprite.center.x, if false startSprite.x. Note that Sprites don't have a center property by default, so only enable if you've over-ridden your Sprite with a custom class. * @return {Phaser.Line} This line object */ fromSprite: function (startSprite, endSprite, useCenter) { if (typeof useCenter === 'undefined') { useCenter = false; } if (useCenter) { return this.setTo(startSprite.center.x, startSprite.center.y, endSprite.center.x, endSprite.center.y); } return this.setTo(startSprite.x, startSprite.y, endSprite.x, endSprite.y); }, /** * Sets this line to start at the given `x` and `y` coordinates and for the segment to extend at `angle` for the given `length`. * * @method Phaser.Line#fromAngle * @param {number} x - The x coordinate of the start of the line. * @param {number} y - The y coordinate of the start of the line. * @param {number} angle - The angle of the line in radians. * @param {number} length - The length of the line in pixels. * @return {Phaser.Line} This line object */ fromAngle: function (x, y, angle, length) { this.start.setTo(x, y); this.end.setTo(x + (Math.cos(angle) * length), y + (Math.sin(angle) * length)); return this; }, /** * Rotates the line by the amount specified in `angle`. * * Rotation takes place from the center of the line. * * If you wish to rotate from either end see Line.start.rotate or Line.end.rotate. * * @method Phaser.Line#rotate * @param {number} angle - The angle in radians (unless asDegrees is true) to rotate the line by. * @param {boolean} [asDegrees=false] - Is the given angle in radians (false) or degrees (true)? * @return {Phaser.Line} This line object */ rotate: function (angle, asDegrees) { var x = this.start.x; var y = this.start.y; this.start.rotate(this.end.x, this.end.y, angle, asDegrees, this.length); this.end.rotate(x, y, angle, asDegrees, this.length); return this; }, /** * 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); }, /** * Returns the reflected angle between two lines. * This is the outgoing angle based on the angle of this line and the normalAngle of the given line. * * @method Phaser.Line#reflect * @param {Phaser.Line} line - The line to reflect off this line. * @return {number} The reflected angle in radians. */ reflect: function (line) { return Phaser.Line.reflect(this, line); }, /** * 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.start.y) === (this.end.x - this.start.x) * (y - this.start.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)); }, /** * Picks a random point from anywhere on the Line segment and returns it. * * @method Phaser.Line#random * @param {Phaser.Point|object} [out] - A Phaser.Point, or any object with public x/y properties, that the values will be set in. * If no object is provided a new Phaser.Point object will be created. In high performance areas avoid this by re-using an object. * @return {Phaser.Point} An object containing the random point in its `x` and `y` properties. */ random: function (out) { if (typeof out === 'undefined') { out = new Phaser.Point(); } var t = Math.random(); out.x = this.start.x + t * (this.end.x - this.start.x); out.y = this.start.y + t * (this.end.y - this.start.y); return out; }, /** * Using Bresenham's line algorithm this will return an array of all coordinates on this line. * The start and end points are rounded before this runs as the algorithm works on integers. * * @method Phaser.Line#coordinatesOnLine * @param {number} [stepRate=1] - How many steps will we return? 1 = every coordinate on the line, 2 = every other coordinate, etc. * @param {array} [results] - The array to store the results in. If not provided a new one will be generated. * @return {array} An array of coordinates. */ coordinatesOnLine: function (stepRate, results) { if (typeof stepRate === 'undefined') { stepRate = 1; } if (typeof results === 'undefined') { results = []; } var x1 = Math.round(this.start.x); var y1 = Math.round(this.start.y); var x2 = Math.round(this.end.x); var y2 = Math.round(this.end.y); var dx = Math.abs(x2 - x1); var dy = Math.abs(y2 - y1); var sx = (x1 < x2) ? 1 : -1; var sy = (y1 < y2) ? 1 : -1; var err = dx - dy; results.push([x1, y1]); var i = 1; while (!((x1 == x2) && (y1 == y2))) { var e2 = err << 1; if (e2 > -dy) { err -= dy; x1 += sx; } if (e2 < dx) { err += dx; y1 += sy; } if (i % stepRate === 0) { results.push([x1, y1]); } i++; } return results; }, /** * Returns a new Line object with the same values for the start and end properties as this Line object. * @method Phaser.Line#clone * @param {Phaser.Line} output - Optional Line object. If given the values will be set into the object, otherwise a brand new Line object will be created and returned. * @return {Phaser.Line} The cloned Line object. */ clone: function (output) { if (typeof output === "undefined" || output === null) { output = new Phaser.Line(this.start.x, this.start.y, this.end.x, this.end.y); } else { output.setTo(this.start.x, this.start.y, this.end.x, this.end.y); } return output; } }; /** * @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 in radians. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "angle", { get: function () { return Math.atan2(this.end.y - this.start.y, this.end.x - this.start.x); } }); /** * @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)); } }); /** * @name Phaser.Line#x * @property {number} x - Gets the x coordinate of the top left of the bounds around this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "x", { get: function () { return Math.min(this.start.x, this.end.x); } }); /** * @name Phaser.Line#y * @property {number} y - Gets the y coordinate of the top left of the bounds around this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "y", { get: function () { return Math.min(this.start.y, this.end.y); } }); /** * @name Phaser.Line#left * @property {number} left - Gets the left-most point of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "left", { get: function () { return Math.min(this.start.x, this.end.x); } }); /** * @name Phaser.Line#right * @property {number} right - Gets the right-most point of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "right", { get: function () { return Math.max(this.start.x, this.end.x); } }); /** * @name Phaser.Line#top * @property {number} top - Gets the top-most point of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "top", { get: function () { return Math.min(this.start.y, this.end.y); } }); /** * @name Phaser.Line#bottom * @property {number} bottom - Gets the bottom-most point of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "bottom", { get: function () { return Math.max(this.start.y, this.end.y); } }); /** * @name Phaser.Line#width * @property {number} width - Gets the width of this bounds of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "width", { get: function () { return Math.abs(this.start.x - this.end.x); } }); /** * @name Phaser.Line#height * @property {number} height - Gets the height of this bounds of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "height", { get: function () { return Math.abs(this.start.y - this.end.y); } }); /** * @name Phaser.Line#normalX * @property {number} normalX - Gets the x component of the left-hand normal of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "normalX", { get: function () { return Math.cos(this.angle - 1.5707963267948966); } }); /** * @name Phaser.Line#normalY * @property {number} normalY - Gets the y component of the left-hand normal of this line. * @readonly */ Object.defineProperty(Phaser.Line.prototype, "normalY", { get: function () { return Math.sin(this.angle - 1.5707963267948966); } }); /** * @name Phaser.Line#normalAngle * @property {number} normalAngle - Gets the angle in radians of the normal of this line (line.angle - 90 degrees.) * @readonly */ Object.defineProperty(Phaser.Line.prototype, "normalAngle", { get: function () { return Phaser.Math.wrap(this.angle - 1.5707963267948966, -Math.PI, Math.PI); } }); /** * 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.intersectsPoints * @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|object} [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) { var uc = ((f.y - e.y) * (b.x - a.x) - (f.x - e.x) * (b.y - a.y)); var ua = (((f.x - e.x) * (a.y - e.y)) - (f.y - e.y) * (a.x - e.x)) / uc; var ub = (((b.x - a.x) * (a.y - e.y)) - ((b.y - a.y) * (a.x - e.x))) / uc; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { return result; } else { 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); }; /** * Returns the reflected angle between two lines. * This is the outgoing angle based on the angle of Line 1 and the normalAngle of Line 2. * * @method Phaser.Line.reflect * @param {Phaser.Line} a - The base line. * @param {Phaser.Line} b - The line to be reflected from the base line. * @return {number} The reflected angle in radians. */ Phaser.Line.reflect = function (a, b) { return 2 * b.normalAngle - 3.141592653589793 - a.angle; }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 * @author Richard Davey * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * The Matrix is a 3x3 matrix mostly used for display transforms within the renderer. * * It is represented like so: * * | a | b | tx | * | c | d | ty | * | 0 | 0 | 1 | * * @class Phaser.Matrix * @constructor * @param {number} [a=1] * @param {number} [b=0] * @param {number} [c=0] * @param {number} [d=1] * @param {number} [tx=0] * @param {number} [ty=0] */ Phaser.Matrix = function (a, b, c, d, tx, ty) { a = a || 1; b = b || 0; c = c || 0; d = d || 1; tx = tx || 0; ty = ty || 0; /** * @property {number} a * @default 1 */ this.a = a; /** * @property {number} b * @default 0 */ this.b = b; /** * @property {number} c * @default 0 */ this.c = c; /** * @property {number} d * @default 1 */ this.d = d; /** * @property {number} tx * @default 0 */ this.tx = tx; /** * @property {number} ty * @default 0 */ this.ty = ty; /** * @property {number} type - The const type of this object. * @readonly */ this.type = Phaser.MATRIX; }; Phaser.Matrix.prototype = { /** * Sets the values of this Matrix to the values in the given array. * * The Array elements should be set as follows: * * a = array[0] * b = array[1] * c = array[3] * d = array[4] * tx = array[2] * ty = array[5] * * @method Phaser.Matrix#fromArray * @param {Array} array - The array to copy from. * @return {Phaser.Matrix} This Matrix object. */ fromArray: function (array) { return this.setTo(array[0], array[1], array[3], array[4], array[2], array[5]); }, /** * Sets the values of this Matrix to the given values. * * @method Phaser.Matrix#setTo * @param {number} a * @param {number} b * @param {number} c * @param {number} d * @param {number} tx * @param {number} ty * @return {Phaser.Matrix} This Matrix object. */ setTo: function (a, b, c, d, tx, ty) { this.a = a; this.b = b; this.c = c; this.d = d; this.tx = tx; this.ty = ty; return this; }, /** * Creates a new Matrix object based on the values of this Matrix. * If you provide the output parameter the values of this Matrix will be copied over to it. * If the output parameter is blank a new Matrix object will be created. * * @method Phaser.Matrix#clone * @param {Phaser.Matrix} [output] - If provided the values of this Matrix will be copied to it, otherwise a new Matrix object is created. * @return {Phaser.Matrix} A clone of this Matrix. */ clone: function (output) { if (typeof output === "undefined" || output === null) { output = new Phaser.Matrix(this.a, this.b, this.c, this.d, this.tx, this.ty); } else { output.a = this.a; output.b = this.b; output.c = this.c; output.d = this.d; output.tx = this.tx; output.ty = this.ty; } return output; }, /** * Copies the properties from this Matrix to the given Matrix. * * @method Phaser.Matrix#copyTo * @param {Phaser.Matrix} matrix - The Matrix to copy from. * @return {Phaser.Matrix} The destination Matrix object. */ copyTo: function (matrix) { matrix.copyFrom(this); return matrix; }, /** * Copies the properties from the given Matrix into this Matrix. * * @method Phaser.Matrix#copyFrom * @param {Phaser.Matrix} matrix - The Matrix to copy from. * @return {Phaser.Matrix} This Matrix object. */ copyFrom: function (matrix) { this.a = matrix.a; this.b = matrix.b; this.c = matrix.c; this.d = matrix.d; this.tx = matrix.tx; this.ty = matrix.ty; return this; }, /** * Creates a Float32 Array with values populated from this Matrix object. * * @method Phaser.Matrix#toArray * @param {boolean} [transpose=false] - Whether the values in the array are transposed or not. * @param {PIXI.Float32Array} [array] - If provided the values will be set into this array, otherwise a new Float32Array is created. * @return {PIXI.Float32Array} The newly created array which contains the matrix. */ toArray: function (transpose, array) { if (typeof array === 'undefined') { array = new PIXI.Float32Array(9); } if (transpose) { array[0] = this.a; array[1] = this.b; array[2] = 0; array[3] = this.c; array[4] = this.d; array[5] = 0; array[6] = this.tx; array[7] = this.ty; array[8] = 1; } else { array[0] = this.a; array[1] = this.c; array[2] = this.tx; array[3] = this.b; array[4] = this.d; array[5] = this.ty; array[6] = 0; array[7] = 0; array[8] = 1; } return array; }, /** * Get a new position with the current transformation applied. * * Can be used to go from a childs coordinate space to the world coordinate space (e.g. rendering) * * @method Phaser.Matrix#apply * @param {Phaser.Point} pos - The origin Point. * @param {Phaser.Point} [newPos] - The point that the new position is assigned to. This can be same as input point. * @return {Phaser.Point} The new point, transformed through this matrix. */ apply: function (pos, newPos) { if (typeof newPos === 'undefined') { newPos = new Phaser.Point(); } newPos.x = this.a * pos.x + this.c * pos.y + this.tx; newPos.y = this.b * pos.x + this.d * pos.y + this.ty; return newPos; }, /** * Get a new position with the inverse of the current transformation applied. * * Can be used to go from the world coordinate space to a childs coordinate space. (e.g. input) * * @method Phaser.Matrix#applyInverse * @param {Phaser.Point} pos - The origin Point. * @param {Phaser.Point} [newPos] - The point that the new position is assigned to. This can be same as input point. * @return {Phaser.Point} The new point, inverse transformed through this matrix. */ applyInverse: function (pos, newPos) { if (typeof newPos === 'undefined') { newPos = new Phaser.Point(); } var id = 1 / (this.a * this.d + this.c * -this.b); var x = pos.x; var y = pos.y; newPos.x = this.d * id * x + -this.c * id * y + (this.ty * this.c - this.tx * this.d) * id; newPos.y = this.a * id * y + -this.b * id * x + (-this.ty * this.a + this.tx * this.b) * id; return newPos; }, /** * Translates the matrix on the x and y. * This is the same as Matrix.tx += x. * * @method Phaser.Matrix#translate * @param {number} x - The x value to translate on. * @param {number} y - The y value to translate on. * @return {Phaser.Matrix} This Matrix object. */ translate: function (x, y) { this.tx += x; this.ty += y; return this; }, /** * Applies a scale transformation to this matrix. * * @method Phaser.Matrix#scale * @param {number} x - The amount to scale horizontally. * @param {number} y - The amount to scale vertically. * @return {Phaser.Matrix} This Matrix object. */ scale: function (x, y) { this.a *= x; this.d *= y; this.c *= x; this.b *= y; this.tx *= x; this.ty *= y; return this; }, /** * Applies a rotation transformation to this matrix. * * @method Phaser.Matrix#rotate * @param {number} angle - The angle to rotate by, given in radians. * @return {Phaser.Matrix} This Matrix object. */ rotate: function (angle) { var cos = Math.cos(angle); var sin = Math.sin(angle); var a1 = this.a; var c1 = this.c; var tx1 = this.tx; this.a = a1 * cos-this.b * sin; this.b = a1 * sin+this.b * cos; this.c = c1 * cos-this.d * sin; this.d = c1 * sin+this.d * cos; this.tx = tx1 * cos - this.ty * sin; this.ty = tx1 * sin + this.ty * cos; return this; }, /** * Appends the given Matrix to this Matrix. * * @method Phaser.Matrix#append * @param {Phaser.Matrix} matrix - The matrix to append to this one. * @return {Phaser.Matrix} This Matrix object. */ append: function (matrix) { var a1 = this.a; var b1 = this.b; var c1 = this.c; var d1 = this.d; this.a = matrix.a * a1 + matrix.b * c1; this.b = matrix.a * b1 + matrix.b * d1; this.c = matrix.c * a1 + matrix.d * c1; this.d = matrix.c * b1 + matrix.d * d1; this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx; this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty; return this; }, /** * Resets this Matrix to an identity (default) matrix. * * @method identity * @return {Phaser.Matrix} This Matrix object. */ identity: function () { return this.setTo(1, 0, 0, 1, 0, 0); } }; Phaser.identityMatrix = new Phaser.Matrix(); // Because PIXI uses its own type, we'll replace it with ours to avoid duplicating code or confusion. PIXI.Matrix = Phaser.Matrix; PIXI.identityMatrix = Phaser.identityMatrix; /** * @author Richard Davey * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * A Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. * The following code creates a point at (0,0): * `var myPoint = new Phaser.Point();` * You can also use them as 2D Vectors and you'll find different vector related methods in this class. * * @class Phaser.Point * @constructor * @param {number} [x=0] - The horizontal position of this Point. * @param {number} [y=0] - The vertical position of this Point. */ Phaser.Point = function (x, y) { x = x || 0; y = y || 0; /** * @property {number} x - The x value of the point. */ this.x = x; /** * @property {number} y - The y value of the point. */ this.y = y; /** * @property {number} type - The const type of this object. * @readonly */ this.type = Phaser.POINT; }; 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 {Phaser.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 {Phaser.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 values. * If you omit the `y` value then the `x` value will be applied to both, for example: * `Point.setTo(2)` is the same as `Point.setTo(2, 2)` * * @method Phaser.Point#setTo * @param {number} x - The horizontal value of this point. * @param {number} [y] - The vertical value of this point. If not given the x value will be used in its place. * @return {Phaser.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 values. * If you omit the `y` value then the `x` value will be applied to both, for example: * `Point.set(2)` is the same as `Point.set(2, 2)` * * @method Phaser.Point#set * @param {number} x - The horizontal value of this point. * @param {number} [y] - The vertical value of this point. If not given the x value will be used in its place. * @return {Phaser.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. Sometimes known as `Scale`. * * @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 === null) { 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|any} a - The object to compare with this Point. * @return {boolean} A value of true if the x and y points are equal, otherwise false. */ equals: function (a) { return (a.x === this.x && a.y === this.y); }, /** * Returns the angle between this Point object and another object with public x and y properties. * * @method Phaser.Point#angle * @param {Phaser.Point|any} a - The object to get the angle from this Point to. * @param {boolean} [asDegrees=false] - Is the given angle in radians (false) or degrees (true)? * @return {number} The angle between the two objects. */ angle: function (a, asDegrees) { if (typeof asDegrees === 'undefined') { asDegrees = false; } if (asDegrees) { return Phaser.Math.radToDeg(Math.atan2(a.y - this.y, a.x - this.x)); } else { return Math.atan2(a.y - this.y, a.x - this.x); } }, /** * 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=false] - Is the given angle 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 Point object. * * @method Phaser.Point#getMagnitude * @return {number} The length of the Point. */ getMagnitude: function () { return Math.sqrt((this.x * this.x) + (this.y * this.y)); }, /** * Calculates the length squared of the Point object. * * @method Phaser.Point#getMagnitudeSq * @return {number} The length ^ 2 of the Point. */ getMagnitudeSq: function () { return (this.x * this.x) + (this.y * this.y); }, /** * Alters the length of the Point without changing the direction. * * @method Phaser.Point#setMagnitude * @param {number} magnitude - The desired magnitude of the resulting Point. * @return {Phaser.Point} This Point object. */ setMagnitude: function (magnitude) { return this.normalize().multiply(magnitude, magnitude); }, /** * Alters the Point object so that its length is 1, but it retains the same direction. * * @method Phaser.Point#normalize * @return {Phaser.Point} This Point object. */ 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); }, /** * The dot product of this and another Point object. * * @method Phaser.Point#dot * @param {Phaser.Point} a - The Point object to get the dot product combined with this Point. * @return {number} The result. */ dot: function (a) { return ((this.x * a.x) + (this.y * a.y)); }, /** * The cross product of this and another Point object. * * @method Phaser.Point#cross * @param {Phaser.Point} a - The Point object to get the cross product combined with this Point. * @return {number} The result. */ cross: function (a) { return ((this.x * a.y) - (this.y * a.x)); }, /** * Make this Point perpendicular (90 degrees rotation) * * @method Phaser.Point#perp * @return {Phaser.Point} This Point object. */ perp: function () { return this.setTo(-this.y, this.x); }, /** * Make this Point perpendicular (-90 degrees rotation) * * @method Phaser.Point#rperp * @return {Phaser.Point} This Point object. */ rperp: function () { return this.setTo(this.y, -this.x); }, /** * Right-hand normalize (make unit length) this Point. * * @method Phaser.Point#normalRightHand * @return {Phaser.Point} This Point object. */ normalRightHand: function () { return this.setTo(this.y * -1, this.x); }, /** * Math.floor() both the x and y properties of this Point. * * @method Phaser.Point#floor * @return {Phaser.Point} This Point object. */ floor: function () { return this.setTo(Math.floor(this.x), Math.floor(this.y)); }, /** * Math.ceil() both the x and y properties of this Point. * * @method Phaser.Point#ceil * @return {Phaser.Point} This Point object. */ ceil: function () { return this.setTo(Math.ceil(this.x), Math.ceil(this.y)); }, /** * 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 angle between two Point objects. * * @method Phaser.Point.angle * @param {Phaser.Point} a - The first Point object. * @param {Phaser.Point} b - The second Point object. * @return {number} The angle between the two Points. */ Phaser.Point.angle = function (a, b) { // return Math.atan2(a.x * b.y - a.y * b.x, a.x * b.x + a.y * b.y); return Math.atan2(a.y - b.y, a.x - b.x); }; /** * Creates a negative Point. * * @method Phaser.Point.negative * @param {Phaser.Point} a - The first 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.negative = function (a, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } return out.setTo(-a.x, -a.y); }; /** * Adds two 2D Points together and multiplies the result by the given scalar. * * @method Phaser.Point.multiplyAdd * @param {Phaser.Point} a - The first Point object. * @param {Phaser.Point} b - The second Point object. * @param {number} s - The scaling value. * @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.multiplyAdd = function (a, b, s, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } return out.setTo(a.x + b.x * s, a.y + b.y * s); }; /** * Interpolates the two given Points, based on the `f` value (between 0 and 1) and returns a new Point. * * @method Phaser.Point.interpolate * @param {Phaser.Point} a - The first Point object. * @param {Phaser.Point} b - The second Point object. * @param {number} f - The level of interpolation between the two points. Indicates where the new point will be, along the line between pt1 and pt2. If f=1, pt1 is returned; if f=0, pt2 is returned. * @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.interpolate = function (a, b, f, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } return out.setTo(a.x + (b.x - a.x) * f, a.y + (b.y - a.y) * f); }; /** * Return a perpendicular vector (90 degrees rotation) * * @method Phaser.Point.perp * @param {Phaser.Point} a - The 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.perp = function (a, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } return out.setTo(-a.y, a.x); }; /** * Return a perpendicular vector (-90 degrees rotation) * * @method Phaser.Point.rperp * @param {Phaser.Point} a - The 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.rperp = function (a, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } return out.setTo(a.y, -a.x); }; /** * Returns the euclidian 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=false] - Round the distance to the nearest integer. * @return {number} The distance between this Point object and the destination Point object. */ Phaser.Point.distance = function (a, b, round) { var distance = Phaser.Math.distance(a.x, a.y, b.x, b.y); return round ? Math.round(distance) : distance; }; /** * Project two Points onto another Point. * * @method Phaser.Point.project * @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.project = function (a, b, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } var amt = a.dot(b) / b.getMagnitudeSq(); if (amt !== 0) { out.setTo(amt * b.x, amt * b.y); } return out; }; /** * Project two Points onto a Point of unit length. * * @method Phaser.Point.projectUnit * @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.projectUnit = function (a, b, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } var amt = a.dot(b); if (amt !== 0) { out.setTo(amt * b.x, amt * b.y); } return out; }; /** * Right-hand normalize (make unit length) a Point. * * @method Phaser.Point.normalRightHand * @param {Phaser.Point} a - The 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.normalRightHand = function (a, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } return out.setTo(a.y * -1, a.x); }; /** * Normalize (make unit length) a Point. * * @method Phaser.Point.normalize * @param {Phaser.Point} a - The 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.normalize = function (a, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } var m = a.getMagnitude(); if (m !== 0) { out.setTo(a.x / m, a.y / m); } return out; }; /** * Rotates a Point object, or any object with exposed x/y properties, around the given coordinates by * the angle specified. If the angle between the point and coordinates was 45 deg and the angle argument * is 45 deg then the resulting angle will be 90 deg, as the angle argument is added to the current angle. * * The distance allows you to specify a distance constraint for the rotation between the point and the * coordinates. If none is given the distance between the two is calculated and used. * * @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 by. * @param {boolean} [asDegrees=false] - Is the given angle 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) { if (typeof asDegrees === 'undefined') { asDegrees = false; } if (typeof distance === 'undefined') { distance = null; } if (asDegrees) { angle = Phaser.Math.degToRad(angle); } if (distance === null) { // Get distance from origin (cx/cy) to this point distance = Math.sqrt(((x - a.x) * (x - a.x)) + ((y - a.y) * (y - a.y))); } var t = angle + Math.atan2(a.y - y, a.x - x); a.x = x + distance * Math.cos(t); a.y = y + distance * Math.sin(t); return a; }; /** * Calculates centroid (or midpoint) from an array of points. If only one point is provided, that point is returned. * * @method Phaser.Point.centroid * @param {Phaser.Point[]} points - The array of one or more points. * @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.centroid = function (points, out) { if (typeof out === "undefined") { out = new Phaser.Point(); } if (Object.prototype.toString.call(points) !== '[object Array]') { throw new Error("Phaser.Point. Parameter 'points' must be an array"); } var pointslength = points.length; if (pointslength < 1) { throw new Error("Phaser.Point. Parameter 'points' array must not be empty"); } if (pointslength === 1) { out.copyFrom(points[0]); return out; } for (var i = 0; i < pointslength; i++) { Phaser.Point.add(out, points[i], out); } out.divide(pointslength, pointslength); return out; }; /** * Parses an object for x and/or y properties and returns a new Phaser.Point with matching values. * If the object doesn't contain those properties a Point with x/y of zero will be returned. * * @method Phaser.Point.parse * @static * @param {object} obj - The object to parse. * @param {string} [xProp='x'] - The property used to set the Point.x value. * @param {string} [yProp='y'] - The property used to set the Point.y value. * @return {Phaser.Point} The new Point object. */ Phaser.Point.parse = function(obj, xProp, yProp) { xProp = xProp || 'x'; yProp = yProp || 'y'; var point = new Phaser.Point(); if (obj[xProp]) { point.x = parseInt(obj[xProp], 10); } if (obj[yProp]) { point.y = parseInt(obj[yProp], 10); } return point; }; // 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 * @author Adrien Brault * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * Creates a new Polygon. * * The points can be set from a variety of formats: * * - An array of Point objects: `[new Phaser.Point(x1, y1), ...]` * - An array of objects with public x/y properties: `[obj1, obj2, ...]` * - An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]` * - As separate Point arguments: `setTo(new Phaser.Point(x1, y1), ...)` * - As separate objects with public x/y properties arguments: `setTo(obj1, obj2, ...)` * - As separate arguments representing point coordinates: `setTo(x1,y1, x2,y2, ...)` * * @class Phaser.Polygon * @constructor * @param {Phaser.Point[]|number[]|...Phaser.Point|...number} points - The points to set. */ Phaser.Polygon = function () { /** * @property {number} area - The area of this Polygon. */ this.area = 0; /** * @property {array} _points - An array of Points that make up this Polygon. * @private */ this._points = []; if (arguments.length > 0) { this.setTo.apply(this, arguments); } /** * @property {boolean} closed - Is the Polygon closed or not? */ this.closed = true; /** * @property {number} type - The base object type. */ this.type = Phaser.POLYGON; }; Phaser.Polygon.prototype = { /** * Export the points as an array of flat numbers, following the sequence [ x,y, x,y, x,y ] * * @method Phaser.Polygon#toNumberArray * @param {array} [output] - The array to append the points to. If not specified a new array will be created. * @return {array} The flattened array. */ toNumberArray: function (output) { if (typeof output === 'undefined') { output = []; } for (var i = 0; i < this._points.length; i++) { if (typeof this._points[i] === 'number') { output.push(this._points[i]); output.push(this._points[i + 1]); i++; } else { output.push(this._points[i].x); output.push(this._points[i].y); } } return output; }, /** * Flattens this Polygon so the points are a sequence of numbers. Any Point objects found are removed and replaced with two numbers. * * @method Phaser.Polygon#flatten * @return {Phaser.Polygon} This Polygon object */ flatten: function () { this._points = this.toNumberArray(); return this; }, /** * Creates a copy of the given Polygon. * This is a deep clone, the resulting copy contains new Phaser.Point objects * * @method Phaser.Polygon#clone * @param {Phaser.Polygon} [output=(new Polygon)] - The polygon to update. If not specified a new polygon will be created. * @return {Phaser.Polygon} The cloned (`output`) polygon object. */ clone: function (output) { var points = this._points.slice(); if (typeof output === "undefined" || output === null) { output = new Phaser.Polygon(points); } else { output.setTo(points); } return output; }, /** * 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) { // Adapted from http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html by Jonas Raoni Soares Silva var length = this._points.length; var inside = false; for (var i = -1, j = length - 1; ++i < length; j = i) { var ix = this._points[i].x; var iy = this._points[i].y; var jx = this._points[j].x; var jy = this._points[j].y; if (((iy <= y && y < jy) || (jy <= y && y < iy)) && (x < (jx - ix) * (y - iy) / (jy - iy) + ix)) { inside = !inside; } } return inside; }, /** * Sets this Polygon to the given points. * * The points can be set from a variety of formats: * * - An array of Point objects: `[new Phaser.Point(x1, y1), ...]` * - An array of objects with public x/y properties: `[obj1, obj2, ...]` * - An array of paired numbers that represent point coordinates: `[x1,y1, x2,y2, ...]` * - As separate Point arguments: `setTo(new Phaser.Point(x1, y1), ...)` * - As separate objects with public x/y properties arguments: `setTo(obj1, obj2, ...)` * - As separate arguments representing point coordinates: `setTo(x1,y1, x2,y2, ...)` * * `setTo` may also be called without any arguments to remove all points. * * @method Phaser.Polygon#setTo * @param {Phaser.Point[]|number[]|...Phaser.Point|...number} points - The points to set. * @return {Phaser.Polygon} This Polygon object */ setTo: function (points) { this.area = 0; this._points = []; if (arguments.length > 0) { // If points isn't an array, use arguments as the array if (!Array.isArray(points)) { points = Array.prototype.slice.call(arguments); } var y0 = Number.MAX_VALUE; // Allows for mixed-type arguments for (var i = 0, len = points.length; i < len; i++) { if (typeof points[i] === 'number') { var p = new PIXI.Point(points[i], points[i + 1]); i++; } else { var p = new PIXI.Point(points[i].x, points[i].y); } this._points.push(p); // Lowest boundary if (p.y < y0) { y0 = p.y; } } this.calculateArea(y0); } return this; }, /** * Calcuates the area of the Polygon. This is available in the property Polygon.area * * @method Phaser.Polygon#calculateArea * @private * @param {number} y0 - The lowest boundary * @return {number} The area of the Polygon. */ calculateArea: function (y0) { var p1; var p2; var avgHeight; var width; for (var i = 0, len = this._points.length; i < len; i++) { p1 = this._points[i]; if (i === len - 1) { p2 = this._points[0]; } else { p2 = this._points[i + 1]; } avgHeight = ((p1.y - y0) + (p2.y - y0)) / 2; width = p1.x - p2.x; this.area += avgHeight * width; } return this.area; } }; Phaser.Polygon.prototype.constructor = Phaser.Polygon; /** * Sets and modifies the points of this polygon. * * See {@link Phaser.Polygon#setTo setTo} for the different kinds of arrays formats that can be assigned. * * @name Phaser.Polygon#points * @property {Phaser.Point[]} points - The array of vertex points. * @deprecated Use `setTo`. */ Object.defineProperty(Phaser.Polygon.prototype, 'points', { get: function() { return this._points; }, set: function(points) { if (points != null) { this.setTo(points); } else { // Clear the points this.setTo(); } } }); // Because PIXI uses its own type, we'll replace it with ours to avoid duplicating code or confusion. PIXI.Polygon = Phaser.Polygon; /** * @author Richard Davey * @copyright 2015 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. Should always be either zero or a positive value. * @param {number} height - The height of the Rectangle. Should always be either zero or a positive value. */ 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 value should never be set to a negative. */ this.width = width; /** * @property {number} height - The height of the Rectangle. This value should never be set to a negative. */ this.height = height; /** * @property {number} type - The const type of this object. * @readonly */ this.type = Phaser.RECTANGLE; }; 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. Should always be either zero or a positive value. * @param {number} height - The height of the Rectangle. Should always be either zero or a positive value. * @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; }, /** * Scales the width and height of this Rectangle by the given amounts. * * @method Phaser.Rectangle#scale * @param {number} x - The amount to scale the width of the Rectangle by. A value of 0.5 would reduce by half, a value of 2 would double the width, etc. * @param {number} [y] - The amount to scale the height of the Rectangle by. A value of 0.5 would reduce by half, a value of 2 would double the height, etc. * @return {Phaser.Rectangle} This Rectangle object */ scale: function (x, y) { if (typeof y === 'undefined') { y = x; } this.width *= x; this.height *= y; return this; }, /** * Centers this Rectangle so that the center coordinates match the given x and y values. * * @method Phaser.Rectangle#centerOn * @param {number} x - The x coordinate to place the center of the Rectangle at. * @param {number} y - The y coordinate to place the center of the Rectangle at. * @return {Phaser.Rectangle} This Rectangle object */ centerOn: function (x, y) { this.centerX = x; this.centerY = y; 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); }, /** * Runs Math.ceil() on both the x and y values of this Rectangle. * @method Phaser.Rectangle#ceil */ ceil: function () { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); }, /** * Runs Math.ceil() on the x, y, width and height values of this Rectangle. * @method Phaser.Rectangle#ceilAll */ ceilAll: function () { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); this.width = Math.ceil(this.width); this.height = Math.ceil(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); }, /** * Resize the Rectangle by providing a new width and height. * The x and y positions remain unchanged. * * @method Phaser.Rectangle#resize * @param {number} width - The width of the Rectangle. Should always be either zero or a positive value. * @param {number} height - The height of the Rectangle. Should always be either zero or a positive value. * @return {Phaser.Rectangle} This Rectangle object */ resize: function (width, height) { this.width = width; this.height = height; return this; }, /** * 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(b, this); }, /** * 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 this Rectangle and another given Rectangle intersect with each other. * This method checks the x, y, width, and height properties of the two Rectangles. * * @method Phaser.Rectangle#intersects * @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. */ intersects: function (b) { return Phaser.Rectangle.intersects(this, b); }, /** * Determines whether the coordinates given intersects (overlaps) with this Rectangle. * * @method Phaser.Rectangle#intersectsRaw * @param {number} left - The x coordinate of the left of the area. * @param {number} right - The right coordinate of the area. * @param {number} top - The y coordinate of the area. * @param {number} bottom - The bottom coordinate of the area. * @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 uniformly distributed random point from anywhere within this Rectangle. * * @method Phaser.Rectangle#random * @param {Phaser.Point|object} [out] - A Phaser.Point, or any object with public x/y properties, that the values will be set in. * If no object is provided a new Phaser.Point object will be created. In high performance areas avoid this by re-using an existing object. * @return {Phaser.Point} An object containing the random point in its `x` and `y` properties. */ random: function (out) { if (typeof out === 'undefined') { out = new Phaser.Point(); } out.x = this.randomX; out.y = this.randomY; return 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 */ 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 = value - this.y; } } }); /** * The location of the Rectangles bottom left corner as a Point object. * @name Phaser.Rectangle#bottomLeft * @property {Phaser.Point} bottomLeft - Gets or sets the location of the Rectangles bottom left corner as a Point object. */ Object.defineProperty(Phaser.Rectangle.prototype, "bottomLeft", { get: function () { return new Phaser.Point(this.x, this.bottom); }, set: function (value) { this.x = value.x; this.bottom = value.y; } }); /** * 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. */ 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 = value - this.x; } } }); /** * 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; } }); /** * A random value between the left and right values (inclusive) of the Rectangle. * * @name Phaser.Rectangle#randomX * @property {number} randomX - A random value between the left and right values (inclusive) of the Rectangle. */ Object.defineProperty(Phaser.Rectangle.prototype, "randomX", { get: function () { return this.x + (Math.random() * this.width); } }); /** * A random value between the top and bottom values (inclusive) of the Rectangle. * * @name Phaser.Rectangle#randomY * @property {number} randomY - A random value between the top and bottom values (inclusive) of the Rectangle. */ Object.defineProperty(Phaser.Rectangle.prototype, "randomY", { get: function () { return this.y + (Math.random() * this.height); } }); /** * 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; } }); /** * The location of the Rectangles top right corner as a Point object. * @name Phaser.Rectangle#topRight * @property {Phaser.Point} topRight - The location of the Rectangles top left corner as a Point object. */ Object.defineProperty(Phaser.Rectangle.prototype, "topRight", { get: function () { return new Phaser.Point(this.x + this.width, this.y); }, set: function (value) { this.right = 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) { 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 === null) { 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 === null) { 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); }; /** * Determines if the two objects (either Rectangles or Rectangle-like) have the same width and height values under strict equality. * @method Phaser.Rectangle.sameDimensions * @param {Rectangle-like} a - The first Rectangle object. * @param {Rectangle-like} b - The second Rectangle object. * @return {boolean} True if the object have equivalent values for the width and height properties. */ Phaser.Rectangle.sameDimensions = function (a, b) { return (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 - The x coordinate of the left of the area. * @param {number} right - The right coordinate of the area. * @param {number} top - The y coordinate of the area. * @param {number} bottom - The bottom coordinate of the area. * @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)); }; /** * Calculates the Axis Aligned Bounding Box (or aabb) from an array of points. * * @method Phaser.Rectangle#aabb * @param {Phaser.Point[]} points - The array of one or more points. * @param {Phaser.Rectangle} [out] - Optional Rectangle to store the value in, if not supplied a new Rectangle object will be created. * @return {Phaser.Rectangle} The new Rectangle object. * @static */ Phaser.Rectangle.aabb = function(points, out) { if (typeof out === "undefined") { out = new Phaser.Rectangle(); } var xMax = Number.MIN_VALUE, xMin = Number.MAX_VALUE, yMax = Number.MIN_VALUE, yMin = Number.MAX_VALUE; points.forEach(function(point) { if (point.x > xMax) { xMax = point.x; } if (point.x < xMin) { xMin = point.x; } if (point.y > yMax) { yMax = point.y; } if (point.y < yMin) { yMin = point.y; } }); out.setTo(xMin, yMin, xMax - xMin, yMax - yMin); return out; }; // 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 Mat Groves http://matgroves.com/ */ /** * The Rounded Rectangle object is an area defined by its position and has nice rounded corners, as indicated by its top-left corner point (x, y) and by its width and its height. * * @class RoundedRectangle * @constructor * @param x {Number} The X coordinate of the upper-left corner of the rounded rectangle * @param y {Number} The Y coordinate of the upper-left corner of the rounded rectangle * @param width {Number} The overall width of this rounded rectangle * @param height {Number} The overall height of this rounded rectangle * @param radius {Number} Controls the radius of the rounded corners */ Phaser.RoundedRectangle = function(x, y, width, height, radius) { /** * @property x * @type Number * @default 0 */ this.x = x || 0; /** * @property y * @type Number * @default 0 */ this.y = y || 0; /** * @property width * @type Number * @default 0 */ this.width = width || 0; /** * @property height * @type Number * @default 0 */ this.height = height || 0; /** * @property radius * @type Number * @default 20 */ this.radius = radius || 20; /** * @property {number} type - The const type of this object. * @readonly */ this.type = Phaser.ROUNDEDRECTANGLE; }; /** * Creates a clone of this Rounded Rectangle * * @method clone * @return {RoundedRectangle} a copy of the rounded rectangle */ Phaser.RoundedRectangle.prototype.clone = function() { return new Phaser.RoundedRectangle(this.x, this.y, this.width, this.height, this.radius); }; /** * Checks whether the x and y coordinates given are contained within this Rounded Rectangle * * @method contains * @param x {Number} The X coordinate of the point to test * @param y {Number} The Y coordinate of the point to test * @return {Boolean} Whether the x/y coordinates are within this Rounded Rectangle */ Phaser.RoundedRectangle.prototype.contains = function(x, y) { if (this.width <= 0 || this.height <= 0) { return false; } var x1 = this.x; if (x >= x1 && x <= x1 + this.width) { var y1 = this.y; if (y >= y1 && y <= y1 + this.height) { return true; } } return false; }; // constructor Phaser.RoundedRectangle.prototype.constructor = Phaser.RoundedRectangle; // Because PIXI uses its own type, we'll replace it with ours to avoid duplicating code or confusion. PIXI.RoundedRectangle = Phaser.RoundedRectangle; /** * @author Richard Davey * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * A Camera is your view into the game world. It has a position and size and renders only those objects within its field of view. * The game automatically creates a single Stage sized camera on boot. Move the camera around the world with Phaser.Camera.x/y * * @class Phaser.Camera * @constructor * @param {Phaser.Game} game - Game reference to the currently running game. * @param {number} id - Not being used at the moment, will be when Phaser supports multiple camera * @param {number} x - Position of the camera on the X axis * @param {number} y - Position of the camera on the Y axis * @param {number} width - The width of the view rectangle * @param {number} height - The height of the view rectangle */ Phaser.Camera = function (game, id, x, y, width, height) { /** * @property {Phaser.Game} game - A reference to the currently running Game. */ this.game = game; /** * @property {Phaser.World} world - A reference to the game world. */ this.world = game.world; /** * @property {number} id - Reserved for future multiple camera set-ups. * @default */ this.id = 0; /** * Camera view. * The view into the world we wish to render (by default the game dimensions). * The x/y values are in world coordinates, not screen coordinates, the width/height is how many pixels to render. * Sprites outside of this view are not rendered if Sprite.autoCull is set to `true`. Otherwise they are always rendered. * @property {Phaser.Rectangle} view */ this.view = new Phaser.Rectangle(x, y, width, height); /** * The Camera is bound to this Rectangle and cannot move outside of it. By default it is enabled and set to the size of the World. * The Rectangle can be located anywhere in the world and updated as often as you like. If you don't wish the Camera to be bound * at all then set this to null. The values can be anything and are in World coordinates, with 0,0 being the top-left of the world. * * @property {Phaser.Rectangle} bounds - The Rectangle in which the Camera is bounded. Set to null to allow for movement anywhere. */ this.bounds = new Phaser.Rectangle(x, y, width, height); /** * @property {Phaser.Rectangle} deadzone - Moving inside this Rectangle will not cause the camera to move. */ this.deadzone = null; /** * @property {boolean} visible - Whether this camera is visible or not. * @default */ this.visible = true; /** * @property {boolean} roundPx - If a Camera has roundPx set to `true` it will call `view.floor` as part of its update loop, keeping its boundary to integer values. Set this to `false` to disable this from happening. * @default */ this.roundPx = true; /** * @property {boolean} atLimit - Whether this camera is flush with the World Bounds or not. */ this.atLimit = { x: false, y: false }; /** * @property {Phaser.Sprite} target - If the camera is tracking a Sprite, this is a reference to it, otherwise null. * @default */ this.target = null; /** * @property {PIXI.DisplayObject} displayObject - The display object to which all game objects are added. Set by World.boot */ this.displayObject = null; /** * @property {Phaser.Point} scale - The scale of the display object to which all game objects are added. Set by World.boot */ this.scale = null; /** * @property {number} totalInView - The total number of Sprites with `autoCull` set to `true` that are visible by this Camera. * @readonly */ this.totalInView = 0; /** * @property {Phaser.Point} _targetPosition - Internal point used to calculate target position * @private */ this._targetPosition = new Phaser.Point(); /** * @property {number} edge - Edge property. * @private * @default */ this._edge = 0; /** * @property {Phaser.Point} position - Current position of the camera in world. * @private * @default */ this._position = new Phaser.Point(); }; /** * @constant * @type {number} */ Phaser.Camera.FOLLOW_LOCKON = 0; /** * @constant * @type {number} */ Phaser.Camera.FOLLOW_PLATFORMER = 1; /** * @constant * @type {number} */ Phaser.Camera.FOLLOW_TOPDOWN = 2; /** * @constant * @type {number} */ Phaser.Camera.FOLLOW_TOPDOWN_TIGHT = 3; Phaser.Camera.prototype = { preUpdate: function () { this.totalInView = 0; }, /** * Tell the camera which sprite to follow. * * If you find you're getting a slight "jitter" effect when following a Sprite it's probably to do with sub-pixel rendering of the Sprite position. * This can be disabled by setting `game.renderer.renderSession.roundPixels = true` to force full pixel rendering. * * @method Phaser.Camera#follow * @param {Phaser.Sprite|Phaser.Image|Phaser.Text} target - The object you want the camera to track. Set to null to not follow anything. * @param {number} [style] - Leverage one of the existing "deadzone" presets. If you use a custom deadzone, ignore this parameter and manually specify the deadzone after calling follow(). */ follow: function (target, style) { if (typeof style === "undefined") { style = Phaser.Camera.FOLLOW_LOCKON; } this.target = target; var helper; switch (style) { case Phaser.Camera.FOLLOW_PLATFORMER: var w = this.width / 8; var h = this.height / 3; this.deadzone = new Phaser.Rectangle((this.width - w) / 2, (this.height - h) / 2 - h * 0.25, w, h); break; case Phaser.Camera.FOLLOW_TOPDOWN: helper = Math.max(this.width, this.height) / 4; this.deadzone = new Phaser.Rectangle((this.width - helper) / 2, (this.height - helper) / 2, helper, helper); break; case Phaser.Camera.FOLLOW_TOPDOWN_TIGHT: helper = Math.max(this.width, this.height) / 8; this.deadzone = new Phaser.Rectangle((this.width - helper) / 2, (this.height - helper) / 2, helper, helper); break; case Phaser.Camera.FOLLOW_LOCKON: this.deadzone = null; break; default: this.deadzone = null; break; } }, /** * Sets the Camera follow target to null, stopping it from following an object if it's doing so. * * @method Phaser.Camera#unfollow */ unfollow: function () { this.target = null; }, /** * Move the camera focus on a display object instantly. * @method Phaser.Camera#focusOn * @param {any} displayObject - The display object to focus the camera on. Must have visible x/y properties. */ focusOn: function (displayObject) { this.setPosition(Math.round(displayObject.x - this.view.halfWidth), Math.round(displayObject.y - this.view.halfHeight)); }, /** * Move the camera focus on a location instantly. * @method Phaser.Camera#focusOnXY * @param {number} x - X position. * @param {number} y - Y position. */ focusOnXY: function (x, y) { this.setPosition(Math.round(x - this.view.halfWidth), Math.round(y - this.view.halfHeight)); }, /** * Update focusing and scrolling. * @method Phaser.Camera#update */ update: function () { if (this.target) { this.updateTarget(); } if (this.bounds) { this.checkBounds(); } if (this.roundPx) { this.view.floor(); } this.displayObject.position.x = -this.view.x; this.displayObject.position.y = -this.view.y; }, /** * Internal method * @method Phaser.Camera#updateTarget * @private */ updateTarget: function () { this._targetPosition.copyFrom(this.target); if (this.target.parent) { this._targetPosition.multiply(this.target.parent.worldTransform.a, this.target.parent.worldTransform.d); } if (this.deadzone) { this._edge = this._targetPosition.x - this.view.x; if (this._edge < this.deadzone.left) { this.view.x = this._targetPosition.x - this.deadzone.left; } else if (this._edge > this.deadzone.right) { this.view.x = this._targetPosition.x - this.deadzone.right; } this._edge = this._targetPosition.y - this.view.y; if (this._edge < this.deadzone.top) { this.view.y = this._targetPosition.y - this.deadzone.top; } else if (this._edge > this.deadzone.bottom) { this.view.y = this._targetPosition.y - this.deadzone.bottom; } } else { this.view.x = this._targetPosition.x - this.view.halfWidth; this.view.y = this._targetPosition.y - this.view.halfHeight; } }, /** * Update the Camera bounds to match the game world. * @method Phaser.Camera#setBoundsToWorld */ setBoundsToWorld: function () { if (this.bounds) { this.bounds.setTo(this.game.world.bounds.x, this.game.world.bounds.y, this.game.world.bounds.width, this.game.world.bounds.height); } }, /** * Method called to ensure the camera doesn't venture outside of the game world. * @method Phaser.Camera#checkBounds */ checkBounds: function () { this.atLimit.x = false; this.atLimit.y = false; // Make sure we didn't go outside the cameras bounds if (this.view.x <= this.bounds.x) { this.atLimit.x = true; this.view.x = this.bounds.x; } if (this.view.right >= this.bounds.right) { this.atLimit.x = true; this.view.x = this.bounds.right - this.width; } if (this.view.y <= this.bounds.top) { this.atLimit.y = true; this.view.y = this.bounds.top; } if (this.view.bottom >= this.bounds.bottom) { this.atLimit.y = true; this.view.y = this.bounds.bottom - this.height; } }, /** * A helper function to set both the X and Y properties of the camera at once * without having to use game.camera.x and game.camera.y. * * @method Phaser.Camera#setPosition * @param {number} x - X position. * @param {number} y - Y position. */ setPosition: function (x, y) { this.view.x = x; this.view.y = y; if (this.bounds) { this.checkBounds(); } }, /** * Sets the size of the view rectangle given the width and height in parameters. * * @method Phaser.Camera#setSize * @param {number} width - The desired width. * @param {number} height - The desired height. */ setSize: function (width, height) { 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; } }; Phaser.Camera.prototype.constructor = Phaser.Camera; /** * The Cameras x coordinate. This value is automatically clamped if it falls outside of the World bounds. * @name Phaser.Camera#x * @property {number} x - Gets or sets the cameras x position. */ Object.defineProperty(Phaser.Camera.prototype, "x", { get: function () { return this.view.x; }, set: function (value) { this.view.x = value; if (this.bounds) { this.checkBounds(); } } }); /** * The Cameras y coordinate. This value is automatically clamped if it falls outside of the World bounds. * @name Phaser.Camera#y * @property {number} y - Gets or sets the cameras y position. */ Object.defineProperty(Phaser.Camera.prototype, "y", { get: function () { return this.view.y; }, set: function (value) { this.view.y = value; if (this.bounds) { this.checkBounds(); } } }); /** * The Cameras position. This value is automatically clamped if it falls outside of the World bounds. * @name Phaser.Camera#position * @property {Phaser.Point} position - Gets or sets the cameras xy position using Phaser.Point object. */ Object.defineProperty(Phaser.Camera.prototype, "position", { get: function () { this._position.set(this.view.centerX, this.view.centerY); return this._position; }, set: function (value) { if (typeof value.x !== "undefined") { this.view.x = value.x; } if (typeof value.y !== "undefined") { this.view.y = value.y; } if (this.bounds) { this.checkBounds(); } } }); /** * The Cameras width. By default this is the same as the Game size and should not be adjusted for now. * @name Phaser.Camera#width * @property {number} width - Gets or sets the cameras width. */ Object.defineProperty(Phaser.Camera.prototype, "width", { get: function () { return this.view.width; }, set: function (value) { this.view.width = value; } }); /** * The Cameras height. By default this is the same as the Game size and should not be adjusted for now. * @name Phaser.Camera#height * @property {number} height - Gets or sets the cameras height. */ Object.defineProperty(Phaser.Camera.prototype, "height", { get: function () { return this.view.height; }, set: function (value) { this.view.height = value; } }); /** * @author Richard Davey * @copyright 2015 Photon Storm Ltd. * @license {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License} */ /** * * TODO: Gradient generator * TODO: Look at sfxr for audio gen * TODO: Dither support * TODO: Sprite Sheet generator * * @class Phaser.Create * @constructor * @param {Phaser.Game} game - Game reference to the currently running game. */ Phaser.Create = function (game) { /** * @property {Phaser.Game} game - A reference to the currently running Game. */ this.game = game; this.bmd = game.make.bitmapData(); this.canvas = this.bmd.canvas; this.ctx = this.bmd.context; // http://androidarts.com/palette/16pal.htm // { 0: '#000', 1: '#', 2: '#', 3: '#', 4: '#', 5: '#', 6: '#', 7: '#', 8: '#', 9: '#', A: '#', B: '#', C: '#', D: '#', E: '#', F: '#' } this.palettes = [ { 0: '#000', 1: '#9D9D9D', 2: '#FFF', 3: '#BE2633', 4: '#E06F8B', 5: '#493C2B', 6: '#A46422', 7: '#EB8931', 8: '#F7E26B', 9: '#2F484E', A: '#44891A', B: '#A3CE27', C: '#1B2632', D: '#005784', E: '#31A2F2', F: '#B2DCEF' }, { 0: '#000', 1: '#191028', 2: '#46af45', 3: '#a1d685', 4: '#453e78', 5: '#7664fe', 6: '#833129', 7: '#9ec2e8', 8: '#dc534b', 9: '#e18d79', A: '#d6b97b', B: '#e9d8a1', C: '#216c4b', D: '#d365c8', E: '#afaab9', F: '#f5f4eb' }, { 0: '#000', 1: '#2234d1', 2: '#0c7e45', 3: '#44aacc', 4: '#8a3622', 5: '#5c2e78', 6: '#aa5c3d', 7: '#b5b5b5', 8: '#5e606e', 9: '#4c81fb', A: '#6cd947', B: '#7be2f9', C: '#eb8a60', D: '#e23d69', E: '#ffd93f', F: '#fff' }, { 0: '#000', 1: '#fff', 2: '#8b4131', 3: '#7bbdc5', 4: '#8b41ac', 5: '#6aac41', 6: '#3931a4', 7: '#d5de73', 8: '#945a20', 9: '#5a4100', A: '#bd736a', B: '#525252', C: '#838383', D: '#acee8b', E: '#7b73de', F: '#acacac' }, { 0: '#000', 1: '#191028', 2: '#46af45', 3: '#a1d685', 4: '#453e78', 5: '#7664fe', 6: '#833129', 7: '#9ec2e8', 8: '#dc534b', 9: '#e18d79', A: '#d6b97b', B: '#e9d8a1', C: '#216c4b', D: '#d365c8', E: '#afaab9', F: '#fff' } ]; }; Phaser.Create.PALETTE_ARNE = 0; Phaser.Create.PALETTE_JMP = 1; Phaser.Create.PALETTE_CGA = 2; Phaser.Create.PALETTE_C64 = 3; Phaser.Create.PALETTE_JAPANESE_MACHINE = 4; Phaser.Create.prototype = { texture: function (key, data, pixelWidth, pixelHeight, palette) { if (typeof pixelWidth === 'undefined') { pixelWidth = 8; } if (typeof pixelHeight === 'undefined') { pixelHeight = pixelWidth; } if (typeof palette === 'undefined') { palette = 0; } var w = data[0].length * pixelWidth; var h = data.length * pixelHeight; this.bmd.resize(w, h); this.bmd.clear(); // Draw it for (var y = 0; y < data.length; y++) { var row = data[y]; for (var x = 0; x < row.length; x++) { var d = row[x]; if (d !== '.' && d !== ' ') { this.ctx.fillStyle = this.palettes[palette][d]; this.ctx.fillRect(x * pixelWidth, y * pixelHeight, pixelWidth, pixelHeight); } } } return this.bmd.generateTexture(key); }, grid: function (key, width, height, cellWidth, cellHeight, color) { this.bmd.resize(width, height); this.ctx.fillStyle = color; for (var y = 0; y < height; y += cellHeight) { this.ctx.fillRect(0, y, width, 1); } for (var x = 0; x < width; x += cellWidth) { this.ctx.fillRect(x, 0, 1, height); } return this.bmd.generateTexture(key); } }; Phaser.Create.prototype.constructor = Phaser.Create; /* * RIFFWAVE.js v0.03 - Audio encoder for HTML5