UNPKG

p2s

Version:

A JavaScript 2D physics engine.

2,027 lines (1,686 loc) 351 kB
/** * @license * pixi.js - v1.4.2 * Copyright (c) 2012, Mat Groves * http://goodboydigital.com/ * * Compiled: 2014-01-15 * * pixi.js is licensed under the MIT License. * http://www.opensource.org/licenses/mit-license.php */ /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ (function(){ var root = this; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @module PIXI */ var PIXI = PIXI || {}; PIXI.WEBGL_RENDERER = 0; PIXI.CANVAS_RENDERER = 1; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. * * @class Point * @constructor * @param x {Number} position of the point * @param y {Number} position of the point */ PIXI.Point = function(x, y) { /** * @property x * @type Number * @default 0 */ this.x = x || 0; /** * @property y * @type Number * @default 0 */ this.y = y || 0; }; /** * Creates a clone of this point * * @method clone * @return {Point} a copy of the point */ PIXI.Point.prototype.clone = function() { return new PIXI.Point(this.x, this.y); }; // constructor PIXI.Point.prototype.constructor = PIXI.Point; /** * @author Mat Groves http://matgroves.com/ */ /** * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height. * * @class Rectangle * @constructor * @param x {Number} The X coord of the upper-left corner of the rectangle * @param y {Number} The Y coord of the upper-left corner of the rectangle * @param width {Number} The overall width of this rectangle * @param height {Number} The overall height of this rectangle */ PIXI.Rectangle = function(x, y, width, height) { /** * @property x * @type Number * @default 0 */ this.x = x || 0; /** * @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; }; /** * Creates a clone of this Rectangle * * @method clone * @return {Rectangle} a copy of the rectangle */ PIXI.Rectangle.prototype.clone = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); }; /** * Checks if the x, and y coords passed to this function are contained within this Rectangle * * @method contains * @param x {Number} The X coord of the point to test * @param y {Number} The Y coord of the point to test * @return {Boolean} if the x/y coords are within this Rectangle */ PIXI.Rectangle.prototype.contains = function(x, y) { if(this.width <= 0 || this.height <= 0) return false; 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 PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; /** * @author Adrien Brault <adrien.brault@gmail.com> */ /** * @class Polygon * @constructor * @param points* {Array<Point>|Array<Number>|Point...|Number...} This can be an array of Points that form the polygon, * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are * Numbers. */ PIXI.Polygon = function(points) { //if points isn't an array, use arguments as the array if(!(points instanceof Array)) points = Array.prototype.slice.call(arguments); //if this is a flat array of numbers, convert it to points if(typeof points[0] === 'number') { var p = []; for(var i = 0, il = points.length; i < il; i+=2) { p.push( new PIXI.Point(points[i], points[i + 1]) ); } points = p; } this.points = points; }; /** * Creates a clone of this polygon * * @method clone * @return {Polygon} a copy of the polygon */ PIXI.Polygon.prototype.clone = function() { var points = []; for (var i=0; i<this.points.length; i++) { points.push(this.points[i].clone()); } return new PIXI.Polygon(points); }; /** * Checks if the x, and y coords passed to this function are contained within this polygon * * @method contains * @param x {Number} The X coord of the point to test * @param y {Number} The Y coord of the point to test * @return {Boolean} if the x/y coords are within this polygon */ PIXI.Polygon.prototype.contains = function(x, y) { var inside = false; // use some raycasting to test hits // https://github.com/substack/point-in-polygon/blob/master/index.js for(var i = 0, j = this.points.length - 1; i < this.points.length; j = i++) { var xi = this.points[i].x, yi = this.points[i].y, xj = this.points[j].x, yj = this.points[j].y, intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if(intersect) inside = !inside; } return inside; }; // constructor PIXI.Polygon.prototype.constructor = PIXI.Polygon; /** * @author Chad Engler <chad@pantherdev.com> */ /** * The Circle object can be used to specify a hit area for displayobjects * * @class Circle * @constructor * @param x {Number} The X coord of the upper-left corner of the framing rectangle of this circle * @param y {Number} The Y coord of the upper-left corner of the framing rectangle of this circle * @param radius {Number} The radius of the circle */ PIXI.Circle = function(x, y, radius) { /** * @property x * @type Number * @default 0 */ this.x = x || 0; /** * @property y * @type Number * @default 0 */ this.y = y || 0; /** * @property radius * @type Number * @default 0 */ this.radius = radius || 0; }; /** * Creates a clone of this Circle instance * * @method clone * @return {Circle} a copy of the polygon */ PIXI.Circle.prototype.clone = function() { return new PIXI.Circle(this.x, this.y, this.radius); }; /** * Checks if the x, and y coords passed to this function are contained within this circle * * @method contains * @param x {Number} The X coord of the point to test * @param y {Number} The Y coord of the point to test * @return {Boolean} if the x/y coords are within this polygon */ PIXI.Circle.prototype.contains = function(x, y) { if(this.radius <= 0) return false; var dx = (this.x - x), dy = (this.y - y), r2 = this.radius * this.radius; dx *= dx; dy *= dy; return (dx + dy <= r2); }; // constructor PIXI.Circle.prototype.constructor = PIXI.Circle; /** * @author Chad Engler <chad@pantherdev.com> */ /** * The Ellipse object can be used to specify a hit area for displayobjects * * @class Ellipse * @constructor * @param x {Number} The X coord of the upper-left corner of the framing rectangle of this ellipse * @param y {Number} The Y coord of the upper-left corner of the framing rectangle of this ellipse * @param width {Number} The overall width of this ellipse * @param height {Number} The overall height of this ellipse */ PIXI.Ellipse = function(x, y, width, height) { /** * @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; }; /** * Creates a clone of this Ellipse instance * * @method clone * @return {Ellipse} a copy of the ellipse */ PIXI.Ellipse.prototype.clone = function() { return new PIXI.Ellipse(this.x, this.y, this.width, this.height); }; /** * Checks if the x, and y coords passed to this function are contained within this ellipse * * @method contains * @param x {Number} The X coord of the point to test * @param y {Number} The Y coord of the point to test * @return {Boolean} if the x/y coords are within this ellipse */ PIXI.Ellipse.prototype.contains = function(x, y) { if(this.width <= 0 || this.height <= 0) return false; //normalize the coords to an ellipse with center 0,0 //and a radius of 0.5 var normx = ((x - this.x) / this.width) - 0.5, normy = ((y - this.y) / this.height) - 0.5; normx *= normx; normy *= normy; return (normx + normy < 0.25); }; PIXI.Ellipse.prototype.getBounds = function() { return new PIXI.Rectangle(this.x, this.y, this.width, this.height); }; // constructor PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; /* * A lighter version of the rad gl-matrix created by Brandon Jones, Colin MacKenzie IV * you both rock! */ function determineMatrixArrayType() { PIXI.Matrix = (typeof Float32Array !== 'undefined') ? Float32Array : Array; return PIXI.Matrix; } determineMatrixArrayType(); PIXI.mat3 = {}; PIXI.mat3.create = function() { var matrix = new PIXI.Matrix(9); matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 0; matrix[4] = 1; matrix[5] = 0; matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; return matrix; }; PIXI.mat3.identity = function(matrix) { matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 0; matrix[4] = 1; matrix[5] = 0; matrix[6] = 0; matrix[7] = 0; matrix[8] = 1; return matrix; }; PIXI.mat4 = {}; PIXI.mat4.create = function() { var matrix = new PIXI.Matrix(16); matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 0; matrix[4] = 0; matrix[5] = 1; matrix[6] = 0; matrix[7] = 0; matrix[8] = 0; matrix[9] = 0; matrix[10] = 1; matrix[11] = 0; matrix[12] = 0; matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; return matrix; }; PIXI.mat3.multiply = function (mat, mat2, dest) { if (!dest) { dest = mat; } // Cache the matrix values (makes for huge speed increases!) var a00 = mat[0], a01 = mat[1], a02 = mat[2], a10 = mat[3], a11 = mat[4], a12 = mat[5], a20 = mat[6], a21 = mat[7], a22 = mat[8], b00 = mat2[0], b01 = mat2[1], b02 = mat2[2], b10 = mat2[3], b11 = mat2[4], b12 = mat2[5], b20 = mat2[6], b21 = mat2[7], b22 = mat2[8]; dest[0] = b00 * a00 + b01 * a10 + b02 * a20; dest[1] = b00 * a01 + b01 * a11 + b02 * a21; dest[2] = b00 * a02 + b01 * a12 + b02 * a22; dest[3] = b10 * a00 + b11 * a10 + b12 * a20; dest[4] = b10 * a01 + b11 * a11 + b12 * a21; dest[5] = b10 * a02 + b11 * a12 + b12 * a22; dest[6] = b20 * a00 + b21 * a10 + b22 * a20; dest[7] = b20 * a01 + b21 * a11 + b22 * a21; dest[8] = b20 * a02 + b21 * a12 + b22 * a22; return dest; }; PIXI.mat3.clone = function(mat) { var matrix = new PIXI.Matrix(9); matrix[0] = mat[0]; matrix[1] = mat[1]; matrix[2] = mat[2]; matrix[3] = mat[3]; matrix[4] = mat[4]; matrix[5] = mat[5]; matrix[6] = mat[6]; matrix[7] = mat[7]; matrix[8] = mat[8]; return matrix; }; PIXI.mat3.transpose = function (mat, dest) { // If we are transposing ourselves we can skip a few steps but have to cache some values if (!dest || mat === dest) { var a01 = mat[1], a02 = mat[2], a12 = mat[5]; mat[1] = mat[3]; mat[2] = mat[6]; mat[3] = a01; mat[5] = mat[7]; mat[6] = a02; mat[7] = a12; return mat; } dest[0] = mat[0]; dest[1] = mat[3]; dest[2] = mat[6]; dest[3] = mat[1]; dest[4] = mat[4]; dest[5] = mat[7]; dest[6] = mat[2]; dest[7] = mat[5]; dest[8] = mat[8]; return dest; }; PIXI.mat3.toMat4 = function (mat, dest) { if (!dest) { dest = PIXI.mat4.create(); } dest[15] = 1; dest[14] = 0; dest[13] = 0; dest[12] = 0; dest[11] = 0; dest[10] = mat[8]; dest[9] = mat[7]; dest[8] = mat[6]; dest[7] = 0; dest[6] = mat[5]; dest[5] = mat[4]; dest[4] = mat[3]; dest[3] = 0; dest[2] = mat[2]; dest[1] = mat[1]; dest[0] = mat[0]; return dest; }; ///// PIXI.mat4.create = function() { var matrix = new PIXI.Matrix(16); matrix[0] = 1; matrix[1] = 0; matrix[2] = 0; matrix[3] = 0; matrix[4] = 0; matrix[5] = 1; matrix[6] = 0; matrix[7] = 0; matrix[8] = 0; matrix[9] = 0; matrix[10] = 1; matrix[11] = 0; matrix[12] = 0; matrix[13] = 0; matrix[14] = 0; matrix[15] = 1; return matrix; }; PIXI.mat4.transpose = function (mat, dest) { // If we are transposing ourselves we can skip a few steps but have to cache some values if (!dest || mat === dest) { var a01 = mat[1], a02 = mat[2], a03 = mat[3], a12 = mat[6], a13 = mat[7], a23 = mat[11]; mat[1] = mat[4]; mat[2] = mat[8]; mat[3] = mat[12]; mat[4] = a01; mat[6] = mat[9]; mat[7] = mat[13]; mat[8] = a02; mat[9] = a12; mat[11] = mat[14]; mat[12] = a03; mat[13] = a13; mat[14] = a23; return mat; } dest[0] = mat[0]; dest[1] = mat[4]; dest[2] = mat[8]; dest[3] = mat[12]; dest[4] = mat[1]; dest[5] = mat[5]; dest[6] = mat[9]; dest[7] = mat[13]; dest[8] = mat[2]; dest[9] = mat[6]; dest[10] = mat[10]; dest[11] = mat[14]; dest[12] = mat[3]; dest[13] = mat[7]; dest[14] = mat[11]; dest[15] = mat[15]; return dest; }; PIXI.mat4.multiply = function (mat, mat2, dest) { if (!dest) { dest = mat; } // Cache the matrix values (makes for huge speed increases!) var a00 = mat[ 0], a01 = mat[ 1], a02 = mat[ 2], a03 = mat[3]; var a10 = mat[ 4], a11 = mat[ 5], a12 = mat[ 6], a13 = mat[7]; var a20 = mat[ 8], a21 = mat[ 9], a22 = mat[10], a23 = mat[11]; var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; // Cache only the current line of the second matrix var b0 = mat2[0], b1 = mat2[1], b2 = mat2[2], b3 = mat2[3]; dest[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; dest[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; dest[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; dest[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; b0 = mat2[4]; b1 = mat2[5]; b2 = mat2[6]; b3 = mat2[7]; dest[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; dest[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; dest[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; dest[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; b0 = mat2[8]; b1 = mat2[9]; b2 = mat2[10]; b3 = mat2[11]; dest[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; dest[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; dest[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; dest[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; b0 = mat2[12]; b1 = mat2[13]; b2 = mat2[14]; b3 = mat2[15]; dest[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; dest[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; dest[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; dest[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; return dest; }; PIXI.identityMatrix = PIXI.mat3.create(); /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * The base class for all objects that are rendered on the screen. * * @class DisplayObject * @constructor */ PIXI.DisplayObject = function() { this.last = this; this.first = this; /** * The coordinate of the object relative to the local coordinates of the parent. * * @property position * @type Point */ this.position = new PIXI.Point(); /** * The scale factor of the object. * * @property scale * @type Point */ this.scale = new PIXI.Point(1,1);//{x:1, y:1}; /** * 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; /** * This is used to indicate if the displayObject should display a mouse hand cursor on rollover * * @property buttonMode * @type Boolean */ this.buttonMode = false; /** * 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] Whether or not the object is interactive, do not toggle directly! use the `interactive` property * * @property _interactive * @type Boolean * @readOnly * @private */ this._interactive = false; /** * This is the curser that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true * * @property defaultCursor * @type String * */ this.defaultCursor = 'pointer'; /** * [read-only] Current transform of the object based on world (parent) factors * * @property worldTransform * @type Mat3 * @readOnly * @private */ this.worldTransform = PIXI.mat3.create(); //mat3.identity(); /** * [read-only] Current transform of the object locally * * @property localTransform * @type Mat3 * @readOnly * @private */ this.localTransform = PIXI.mat3.create(); //mat3.identity(); /** * [NYI] Unkown * * @property color * @type Array<> * @private */ this.color = []; /** * [NYI] Holds whether or not this object is dynamic, for rendering optimization * * @property dynamic * @type Boolean * @private */ this.dynamic = true; // chach that puppy! this._sr = 0; this._cr = 1; this.filterArea = new PIXI.Rectangle(0,0,1,1); /** * * * */ this._bounds = new PIXI.Rectangle(0, 0, 1, 1); this._currentBounds = null; this._mask = null; /* * MOUSE Callbacks */ /** * A callback that is used when the users clicks on the displayObject with their mouse * @method click * @param interactionData {InteractionData} */ /** * A callback that is used when the user clicks the mouse down over the sprite * @method mousedown * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the mouse that was over the displayObject * for this callback to be fired the mouse must have been pressed down over the displayObject * @method mouseup * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the mouse that was over the displayObject but is no longer over the displayObject * for this callback to be fired, The touch must have started over the displayObject * @method mouseupoutside * @param interactionData {InteractionData} */ /** * A callback that is used when the users mouse rolls over the displayObject * @method mouseover * @param interactionData {InteractionData} */ /** * A callback that is used when the users mouse leaves the displayObject * @method mouseout * @param interactionData {InteractionData} */ /* * TOUCH Callbacks */ /** * A callback that is used when the users taps on the sprite with their finger * basically a touch version of click * @method tap * @param interactionData {InteractionData} */ /** * A callback that is used when the user touch's over the displayObject * @method touchstart * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases a touch over the displayObject * @method touchend * @param interactionData {InteractionData} */ /** * A callback that is used when the user releases the touch that was over the displayObject * for this callback to be fired, The touch must have started over the sprite * @method touchendoutside * @param interactionData {InteractionData} */ }; // constructor PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; /** * [Deprecated] Indicates if the sprite will have touch and mouse interactivity. It is false by default * Instead of using this function you can now simply set the interactive property to true or false * * @method setInteractive * @param interactive {Boolean} * @deprecated Simply set the `interactive` property directly */ PIXI.DisplayObject.prototype.setInteractive = function(interactive) { this.interactive = interactive; }; /** * Indicates if the sprite will have touch and mouse interactivity. It is false by default * * @property interactive * @type Boolean * @default false */ Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { get: function() { return this._interactive; }, set: function(value) { this._interactive = value; // TODO more to be done here.. // need to sort out a re-crawl! if(this.stage)this.stage.dirty = true; } }); /** * [read-only] Indicates if the sprite is globaly visible. * * @property worldVisible * @type Boolean */ Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', { get: function() { var item = this; do { if(!item.visible)return false; item = item.parent; } while(item); return true; } }); /** * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it. * In PIXI a regular mask must be a PIXI.Ggraphics object. This allows for much faster masking in canvas as it utilises shape clipping. * 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 An array of filters */ 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; } }); /* * Updates the object transform for rendering * * @method updateTransform * @private */ PIXI.DisplayObject.prototype.updateTransform = function() { // TODO OPTIMIZE THIS!! with dirty if(this.rotation !== this.rotationCache) { this.rotationCache = this.rotation; this._sr = Math.sin(this.rotation); this._cr = Math.cos(this.rotation); } var localTransform = this.localTransform; var parentTransform = this.parent.worldTransform; var worldTransform = this.worldTransform; //console.log(localTransform) localTransform[0] = this._cr * this.scale.x; localTransform[1] = -this._sr * this.scale.y; localTransform[3] = this._sr * this.scale.x; localTransform[4] = this._cr * this.scale.y; // TODO --> do we even need a local matrix??? var px = this.pivot.x; var py = this.pivot.y; // Cache the matrix values (makes for huge speed increases!) var a00 = localTransform[0], a01 = localTransform[1], a02 = this.position.x - localTransform[0] * px - py * localTransform[1], a10 = localTransform[3], a11 = localTransform[4], a12 = this.position.y - localTransform[4] * py - px * localTransform[3], b00 = parentTransform[0], b01 = parentTransform[1], b02 = parentTransform[2], b10 = parentTransform[3], b11 = parentTransform[4], b12 = parentTransform[5]; localTransform[2] = a02; localTransform[5] = a12; worldTransform[0] = b00 * a00 + b01 * a10; worldTransform[1] = b00 * a01 + b01 * a11; worldTransform[2] = b00 * a02 + b01 * a12 + b02; worldTransform[3] = b10 * a00 + b11 * a10; worldTransform[4] = b10 * a01 + b11 * a11; worldTransform[5] = b10 * a02 + b11 * a12 + b12; // because we are using affine transformation, we can optimise the matrix concatenation process.. wooo! // mat3.multiply(this.localTransform, this.parent.worldTransform, this.worldTransform); this.worldAlpha = this.alpha * this.parent.worldAlpha; }; PIXI.DisplayObject.prototype.getBounds = function() { return PIXI.EmptyRectangle; }; PIXI.DisplayObject.prototype.getLocalBounds = function() { var matrixCache = this.worldTransform; this.worldTransform = PIXI.identityMatrix; this.updateTransform(); var bounds = this.getBounds(); this.worldTransform = matrixCache; return bounds; }; PIXI.DisplayObject.prototype.setStageReference = function(stage) { this.stage = stage; if(this._interactive)this.stage.dirty = true; }; PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) { // OVERWRITE; // this line is just here to pass jshinting :) renderSession = renderSession; }; PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) { // OVERWRITE; // this line is just here to pass jshinting :) renderSession = renderSession; }; PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); /** * @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 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 acheive the value set * * @property width * @type Number */ /* Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { get: function() { return this.scale.x * this.getLocalBounds().width; }, set: function(value) { this.scale.x = value / (this.getLocalBounds().width/this.scale.x); this._width = value; } }); */ /** * The height of the displayObjectContainer, setting this will actually modify the scale to acheive the value set * * @property height * @type Number */ /* Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { get: function() { return this.scale.y * this.getLocalBounds().height; }, set: function(value) { this.scale.y = value / (this.getLocalBounds().height/this.scale.y); this._height = value; } }); */ /** * Adds a child to the container. * * @method addChild * @param child {DisplayObject} The DisplayObject to add to the container */ PIXI.DisplayObjectContainer.prototype.addChild = function(child) { if(child.parent && child !== this) { //// COULD BE THIS??? child.parent.removeChild(child); // return; } child.parent = this; this.children.push(child); // update the stage refference.. if(this.stage)child.setStageReference(this.stage); }; /** * 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 */ 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); } else { throw new Error(child + ' The index '+ index +' supplied is out of bounds ' + this.children.length); } }; /** * [NYI] Swaps the depth of 2 displayObjects * * @method swapChildren * @param child {DisplayObject} * @param child2 {DisplayObject} * @private */ PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) { if(child === child2) { return; } var index1 = this.children.indexOf(child); var index2 = this.children.indexOf(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 Child at the specified index * * @method getChildAt * @param index {Number} The index to get the child from */ PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) { if(index >= 0 && index < this.children.length) { return this.children[index]; } else { throw new Error('The supplied DisplayObjects must be a child of the caller ' + this); } }; /** * Removes a child from the container. * * @method removeChild * @param child {DisplayObject} The DisplayObject to remove */ PIXI.DisplayObjectContainer.prototype.removeChild = function(child) { var index = this.children.indexOf( child ); if ( index !== -1 ) { // update the stage reference.. if(this.stage)child.removeStageReference(); child.parent = undefined; this.children.splice( index, 1 ); } else { throw new Error(child + ' The supplied DisplayObject must be a child of the caller ' + this); } }; /* * Updates the container's children's transform for rendering * * @method updateTransform * @private */ PIXI.DisplayObjectContainer.prototype.updateTransform = function() { //this._currentBounds = null; if(!this.visible)return; PIXI.DisplayObject.prototype.updateTransform.call( this ); for(var i=0,j=this.children.length; i<j; i++) { this.children[i].updateTransform(); } }; 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; for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; if(!child.visible)continue; childBounds = this.children[i].getBounds(); minX = minX < childBounds.x ? minX : childBounds.x; minY = minY < childBounds.y ? minY : childBounds.y; childMaxX = childBounds.width + childBounds.x; childMaxY = childBounds.height + childBounds.y; maxX = maxX > childMaxX ? maxX : childMaxX; maxY = maxY > childMaxY ? maxY : childMaxY; } var bounds = this._bounds; bounds.x = minX; bounds.y = minY; bounds.width = maxX - minX; bounds.height = maxY - minY; // TODO: store a refferance so that if this function gets called again in the render cycle we do not have to recacalculate //this._currentBounds = bounds; return bounds; }; PIXI.DisplayObjectContainer.prototype.setStageReference = function(stage) { this.stage = stage; if(this._interactive)this.stage.dirty = true; for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; child.setStageReference(stage); } }; PIXI.DisplayObjectContainer.prototype.removeStageReference = function() { for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; child.removeStageReference(); } if(this._interactive)this.stage.dirty = true; this.stage = null; }; PIXI.DisplayObjectContainer.prototype._renderWebGL = function(renderSession) { if(this.visible === false || this.alpha === 0)return; var i,j; if(this._mask || this._filters) { if(this._mask) { renderSession.spriteBatch.stop(); renderSession.maskManager.pushMask(this.mask, renderSession); renderSession.spriteBatch.start(); } if(this._filters) { renderSession.spriteBatch.flush(); renderSession.filterManager.pushFilter(this._filterBlock); } // simple render children! for(i=0,j=this.children.length; i<j; i++) { this.children[i]._renderWebGL(renderSession); } renderSession.spriteBatch.stop(); if(this._filters)renderSession.filterManager.popFilter(); if(this._mask)renderSession.maskManager.popMask(renderSession); renderSession.spriteBatch.start(); } else { // simple render children! for(i=0,j=this.children.length; i<j; i++) { this.children[i]._renderWebGL(renderSession); } } }; PIXI.DisplayObjectContainer.prototype._renderCanvas = function(renderSession) { if(this.visible === false || this.alpha === 0)return; if(this._mask) { renderSession.maskManager.pushMask(this._mask, renderSession.context); } for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; child._renderCanvas(renderSession); } if(this._mask) { renderSession.maskManager.popMask(renderSession.context); } }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ PIXI.blendModes = {}; PIXI.blendModes.NORMAL = 0; PIXI.blendModes.ADD = 1; PIXI.blendModes.MULTIPLY = 2; PIXI.blendModes.SCREEN = 3; /** * The SPrite object is the base for all textured objects that are rendered to the screen * * @class Sprite™ * @extends DisplayObjectContainer * @constructor * @param texture {Texture} The texture for this sprite * @type String */ PIXI.Sprite = function(texture) { PIXI.DisplayObjectContainer.call( this ); /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the textures origin is the top left * Setting than anchor to 0.5,0.5 means the textures origin is centered * Setting the anchor to 1,1 would mean the textures origin points will be the bottom right * * @property anchor * @type Point */ this.anchor = new PIXI.Point(); /** * The texture that the sprite is using * * @property texture * @type Texture */ this.texture = texture; /** * The width of the sprite (this is initially set by the texture) * * @property _width * @type Number * @private */ this._width = 0; /** * The height of the sprite (this is initially set by the texture) * * @property _height * @type Number * @private */ this._height = 0; /** * The tint applied to the sprite. This is a hex value * * @property tint * @type Number * @default 0xFFFFFF */ this.tint = 0xFFFFFF;// * Math.random(); /** * The blend mode to be applied to the sprite * * @property blendMode * @type Number * @default PIXI.blendModes.NORMAL; */ this.blendMode = PIXI.blendModes.NORMAL; if(texture.baseTexture.hasLoaded) { this.onTextureUpdate(); } else { this.onTextureUpdateBind = this.onTextureUpdate.bind(this); this.texture.addEventListener( 'update', this.onTextureUpdateBind ); } this.renderable = true; }; // constructor PIXI.Sprite.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); PIXI.Sprite.prototype.constructor = PIXI.Sprite; /** * The width of the sprite, setting this will actually modify the scale to acheive the value set * * @property width * @type Number */ Object.defineProperty(PIXI.Sprite.prototype, 'width', { get: function() { return this.scale.x * this.texture.frame.width; }, set: function(value) { this.scale.x = value / this.texture.frame.width; this._width = value; } }); /** * The height of the sprite, setting this will actually modify the scale to acheive the value set * * @property height * @type Number */ Object.defineProperty(PIXI.Sprite.prototype, 'height', { get: function() { return this.scale.y * this.texture.frame.height; }, set: function(value) { this.scale.y = value / this.texture.frame.height; this._height = value; } }); /** * Sets the texture of the sprite * * @method setTexture * @param texture {Texture} The PIXI texture that is displayed by the sprite */ PIXI.Sprite.prototype.setTexture = function(texture) { // stop current texture; if(this.texture.baseTexture !== texture.baseTexture) { this.textureChange = true; this.texture = texture; } else { this.texture = texture; } this.cachedTint = 0xFFFFFF; this.updateFrame = true; }; /** * When the texture is updated, this event will fire to update the scale and frame * * @method onTextureUpdate * @param event * @private */ PIXI.Sprite.prototype.onTextureUpdate = function() { // so if _width is 0 then width was not set.. if(this._width)this.scale.x = this._width / this.texture.frame.width; if(this._height)this.scale.y = this._height / this.texture.frame.height; this.updateFrame = true; }; PIXI.Sprite.prototype.getBounds = function() { var width = this.texture.frame.width; var height = this.texture.frame.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[0]; var b = worldTransform[3]; var c = worldTransform[1]; var d = worldTransform[4]; var tx = worldTransform[2]; var ty = worldTransform[5]; 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 refferance so that if this function gets called again in the render cycle we do not have to recacalculate this._currentBounds = bounds; return bounds; }; PIXI.Sprite.prototype._renderWebGL = function(renderSession) { // if the sprite is not visible or the alpha is 0 then no need to render this element if(this.visible === false || this.alpha === 0)return; var i,j; // do a quick check to see if this element has a mask or a filter. if(this._mask || this._filters) { var spriteBatch = renderSession.spriteBatch; if(this._mask) { spriteBatch.stop(); renderSession.maskManager.pushMask(this.mask, renderSession); spriteBatch.start(); } if(this._filters) { spriteBatch.flush(); renderSession.filterManager.pushFilter(this._filterBlock); } // add this sprite to the batch spriteBatch.render(this); // now loop through the children and make sure they get rendered for(i=0,j=this.children.length; i<j; 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._filters)renderSession.filterManager.popFilter(); if(this._mask)renderSession.maskManager.popMask(renderSession); spriteBatch.start(); } else { renderSession.spriteBatch.render(this); // simple render children! for(i=0,j=this.children.length; i<j; i++) { this.children[i]._renderWebGL(renderSession); } } //TODO check culling }; PIXI.Sprite.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)return; if(this._mask) { renderSession.maskManager.pushMask(this._mask, renderSession.context); } var frame = this.texture.frame; var context = renderSession.context; var texture = this.texture; //ignore null sources if(frame && frame.width && frame.height && texture.baseTexture.source) { context.globalAlpha = this.worldAlpha; var transform = this.worldTransform; // alow for trimming context.setTransform(transform[0], transform[3], transform[1], transform[4], transform[2], transform[5]); // check blend mode if(this.blendMode !== renderSession.currentBlendMode) { renderSession.currentBlendMode = this.blendMode; context.globalCompositeOperation = PIXI.blendModesCanvas[renderSession.currentBlendMode]; } //if smoothingEnabled is supported and we need to change the smoothing property for this texture // if(this.smoothProperty && this.scaleMode !== displayObject.texture.baseTexture.scaleMode) { // this.scaleMode = displayObject.texture.baseTexture.scaleMode; // context[this.smoothProperty] = (this.scaleMode === PIXI.BaseTexture.SCALE_MODE.LINEAR); //} if(this.tint !== 0xFFFFFF) { if(this.cachedTint !== this.tint) { // no point tinting an image that has not loaded yet! if(!texture.baseTexture.hasLoaded)return; this.cachedTint = this.tint; //TODO clean up cacheing - how to clean up the caches? this.tintedTexture = PIXI.CanvasTinter.getTintedTexture(this, this.tint); } context.drawImage(this.tintedTexture, 0, 0, frame.width, frame.height, (this.anchor.x) * -frame.width, (this.anchor.y) * -frame.height, frame.width, frame.height); } else { if(texture.trimmed) { var trim = texture.trim; context.drawImage(this.texture.baseTexture.source, frame.x, frame.y, frame.width, frame.height, trim.x - this.anchor.x * trim.realWidth, trim.y - this.anchor.y * trim.realHeight, frame.width, frame.height); } else { context.drawImage(this.texture.baseTexture.source, frame.x, frame.y, frame.width, frame.height, (this.anchor.x) * -frame.width, (this.anchor.y) * -frame.height, frame.width, frame.height); } } } // OVERWRITE for(var i=0,j=this.children.length; i<j; i++) { var child = this.children[i]; child._renderCanvas(renderSession); } if(this._mask) { renderSession.maskManager.popMask(renderSession.context); } }; // 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) { var texture = PIXI.Texture.fromImage(imageId); return new PIXI.Sprite(texture); }; /** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * A MovieClip is a simple way to display an animation depicted by a list of textures. * * @class MovieClip * @extends Sprite * @constructor * @param textures {Array<Texture>} an array of {Texture} objects that make up the animation */ PIXI.MovieClip = function(textures) { PIXI.Sprite.call(this, textures[0]); /** * The array of textures that make up the animation * * @property textures * @type Array */ this.textures = textures; /** * The speed that the MovieClip will play at. Higher is faster, lower is slower * * @property animationSpeed * @type Number * @default 1 */ this.animationSpeed = 1; /** * Whether or not the movie clip repeats after playing. * * @property loop * @type Boolean * @default true */ this.loop = true; /** * Function to call when a MovieClip finishes playing * * @property onComplete * @type Function */ this.onComplete = null; /** * [read-only] The index MovieClips current frame (this may not have to be a whole number) * * @property currentFrame * @type Number * @default 0 * @readOnly */ this.currentFrame = 0; /**