diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index c1e674c..1274c82 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -1,7 +1,7 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'); +var Container = require('../display/Container'); /** - * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, + * The SpriteBatch class is a really fast version of the Container built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). * Any other functionality like tinting, masking, etc will not work on sprites in this batch. @@ -27,10 +27,10 @@ //TODO RENAME to PARTICLE CONTAINER? function SpriteBatch() { - DisplayObjectContainer.call(this); + Container.call(this); } -SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype); +SpriteBatch.prototype = Object.create(Container.prototype); SpriteBatch.prototype.constructor = SpriteBatch; module.exports = SpriteBatch; @@ -43,7 +43,7 @@ { // TODO don't need to! this.displayObjectUpdateTransform(); - // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); + // PIXI.Container.prototype.updateTransform.call( this ); }; /** diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index c1e674c..1274c82 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -1,7 +1,7 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'); +var Container = require('../display/Container'); /** - * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, + * The SpriteBatch class is a really fast version of the Container built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). * Any other functionality like tinting, masking, etc will not work on sprites in this batch. @@ -27,10 +27,10 @@ //TODO RENAME to PARTICLE CONTAINER? function SpriteBatch() { - DisplayObjectContainer.call(this); + Container.call(this); } -SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype); +SpriteBatch.prototype = Object.create(Container.prototype); SpriteBatch.prototype.constructor = SpriteBatch; module.exports = SpriteBatch; @@ -43,7 +43,7 @@ { // TODO don't need to! this.displayObjectUpdateTransform(); - // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); + // PIXI.Container.prototype.updateTransform.call( this ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index a19f95e..2fe7eb2 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -27,10 +27,10 @@ * ``` * * 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: + * position a Container should be used: * * ```js - * var doc = new DisplayObjectContainer(); + * var doc = new Container(); * * doc.addChild(sprite); * diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index c1e674c..1274c82 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -1,7 +1,7 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'); +var Container = require('../display/Container'); /** - * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, + * The SpriteBatch class is a really fast version of the Container built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). * Any other functionality like tinting, masking, etc will not work on sprites in this batch. @@ -27,10 +27,10 @@ //TODO RENAME to PARTICLE CONTAINER? function SpriteBatch() { - DisplayObjectContainer.call(this); + Container.call(this); } -SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype); +SpriteBatch.prototype = Object.create(Container.prototype); SpriteBatch.prototype.constructor = SpriteBatch; module.exports = SpriteBatch; @@ -43,7 +43,7 @@ { // TODO don't need to! this.displayObjectUpdateTransform(); - // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); + // PIXI.Container.prototype.updateTransform.call( this ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index a19f95e..2fe7eb2 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -27,10 +27,10 @@ * ``` * * 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: + * position a Container should be used: * * ```js - * var doc = new DisplayObjectContainer(); + * var doc = new Container(); * * doc.addChild(sprite); * diff --git a/src/extras/MovieClip.js b/src/extras/MovieClip.js index 6b61ea9..95d3a2b 100644 --- a/src/extras/MovieClip.js +++ b/src/extras/MovieClip.js @@ -133,7 +133,7 @@ */ MovieClip.prototype.updateTransform = function () { - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); if (!this.playing) { diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index c1e674c..1274c82 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -1,7 +1,7 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'); +var Container = require('../display/Container'); /** - * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, + * The SpriteBatch class is a really fast version of the Container built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). * Any other functionality like tinting, masking, etc will not work on sprites in this batch. @@ -27,10 +27,10 @@ //TODO RENAME to PARTICLE CONTAINER? function SpriteBatch() { - DisplayObjectContainer.call(this); + Container.call(this); } -SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype); +SpriteBatch.prototype = Object.create(Container.prototype); SpriteBatch.prototype.constructor = SpriteBatch; module.exports = SpriteBatch; @@ -43,7 +43,7 @@ { // TODO don't need to! this.displayObjectUpdateTransform(); - // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); + // PIXI.Container.prototype.updateTransform.call( this ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index a19f95e..2fe7eb2 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -27,10 +27,10 @@ * ``` * * 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: + * position a Container should be used: * * ```js - * var doc = new DisplayObjectContainer(); + * var doc = new Container(); * * doc.addChild(sprite); * diff --git a/src/extras/MovieClip.js b/src/extras/MovieClip.js index 6b61ea9..95d3a2b 100644 --- a/src/extras/MovieClip.js +++ b/src/extras/MovieClip.js @@ -133,7 +133,7 @@ */ MovieClip.prototype.updateTransform = function () { - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); if (!this.playing) { diff --git a/src/extras/Rope.js b/src/extras/Rope.js index 9c87bc0..4dbeb6c 100644 --- a/src/extras/Rope.js +++ b/src/extras/Rope.js @@ -161,7 +161,7 @@ lastPoint = point; } - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); }; /** diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index c1e674c..1274c82 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -1,7 +1,7 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'); +var Container = require('../display/Container'); /** - * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, + * The SpriteBatch class is a really fast version of the Container built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). * Any other functionality like tinting, masking, etc will not work on sprites in this batch. @@ -27,10 +27,10 @@ //TODO RENAME to PARTICLE CONTAINER? function SpriteBatch() { - DisplayObjectContainer.call(this); + Container.call(this); } -SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype); +SpriteBatch.prototype = Object.create(Container.prototype); SpriteBatch.prototype.constructor = SpriteBatch; module.exports = SpriteBatch; @@ -43,7 +43,7 @@ { // TODO don't need to! this.displayObjectUpdateTransform(); - // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); + // PIXI.Container.prototype.updateTransform.call( this ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index a19f95e..2fe7eb2 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -27,10 +27,10 @@ * ``` * * 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: + * position a Container should be used: * * ```js - * var doc = new DisplayObjectContainer(); + * var doc = new Container(); * * doc.addChild(sprite); * diff --git a/src/extras/MovieClip.js b/src/extras/MovieClip.js index 6b61ea9..95d3a2b 100644 --- a/src/extras/MovieClip.js +++ b/src/extras/MovieClip.js @@ -133,7 +133,7 @@ */ MovieClip.prototype.updateTransform = function () { - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); if (!this.playing) { diff --git a/src/extras/Rope.js b/src/extras/Rope.js index 9c87bc0..4dbeb6c 100644 --- a/src/extras/Rope.js +++ b/src/extras/Rope.js @@ -161,7 +161,7 @@ lastPoint = point; } - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); }; /** diff --git a/src/extras/Strip.js b/src/extras/Strip.js index 6bf78cb..113ef8f 100644 --- a/src/extras/Strip.js +++ b/src/extras/Strip.js @@ -3,7 +3,7 @@ /** * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture to use * @param width {number} the width @@ -12,7 +12,7 @@ */ function Strip(texture) { - core.DisplayObjectContainer.call(this); + core.Container.call(this); /** * The texture of the strip @@ -62,7 +62,7 @@ } // constructor -Strip.prototype = Object.create(core.DisplayObjectContainer.prototype); +Strip.prototype = Object.create(core.Container.prototype); Strip.prototype.constructor = Strip; module.exports = Strip; diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index c1e674c..1274c82 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -1,7 +1,7 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'); +var Container = require('../display/Container'); /** - * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, + * The SpriteBatch class is a really fast version of the Container built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). * Any other functionality like tinting, masking, etc will not work on sprites in this batch. @@ -27,10 +27,10 @@ //TODO RENAME to PARTICLE CONTAINER? function SpriteBatch() { - DisplayObjectContainer.call(this); + Container.call(this); } -SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype); +SpriteBatch.prototype = Object.create(Container.prototype); SpriteBatch.prototype.constructor = SpriteBatch; module.exports = SpriteBatch; @@ -43,7 +43,7 @@ { // TODO don't need to! this.displayObjectUpdateTransform(); - // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); + // PIXI.Container.prototype.updateTransform.call( this ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index a19f95e..2fe7eb2 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -27,10 +27,10 @@ * ``` * * 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: + * position a Container should be used: * * ```js - * var doc = new DisplayObjectContainer(); + * var doc = new Container(); * * doc.addChild(sprite); * diff --git a/src/extras/MovieClip.js b/src/extras/MovieClip.js index 6b61ea9..95d3a2b 100644 --- a/src/extras/MovieClip.js +++ b/src/extras/MovieClip.js @@ -133,7 +133,7 @@ */ MovieClip.prototype.updateTransform = function () { - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); if (!this.playing) { diff --git a/src/extras/Rope.js b/src/extras/Rope.js index 9c87bc0..4dbeb6c 100644 --- a/src/extras/Rope.js +++ b/src/extras/Rope.js @@ -161,7 +161,7 @@ lastPoint = point; } - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); }; /** diff --git a/src/extras/Strip.js b/src/extras/Strip.js index 6bf78cb..113ef8f 100644 --- a/src/extras/Strip.js +++ b/src/extras/Strip.js @@ -3,7 +3,7 @@ /** * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture to use * @param width {number} the width @@ -12,7 +12,7 @@ */ function Strip(texture) { - core.DisplayObjectContainer.call(this); + core.Container.call(this); /** * The texture of the strip @@ -62,7 +62,7 @@ } // constructor -Strip.prototype = Object.create(core.DisplayObjectContainer.prototype); +Strip.prototype = Object.create(core.Container.prototype); Strip.prototype.constructor = Strip; module.exports = Strip; diff --git a/src/spine/Spine.js b/src/spine/Spine.js index ce425cc..79a4ab9 100644 --- a/src/spine/Spine.js +++ b/src/spine/Spine.js @@ -11,13 +11,13 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param url {string} The url of the spine anim file to be used */ function Spine(url) { - core.DisplayObjectContainer.call(this); + core.Container.call(this); this.spineData = core.utils.AnimCache[url]; @@ -38,7 +38,7 @@ { var slot = this.skeleton.drawOrder[i]; var attachment = slot.attachment; - var slotContainer = new core.DisplayObjectContainer(); + var slotContainer = new core.Container(); this.slotContainers.push(slotContainer); this.addChild(slotContainer); @@ -67,7 +67,7 @@ this.autoUpdate = true; } -Spine.prototype = Object.create(core.DisplayObjectContainer.prototype); +Spine.prototype = Object.create(core.Container.prototype); Spine.prototype.constructor = Spine; module.exports = Spine; @@ -91,7 +91,7 @@ set: function (value) { - this.updateTransform = value ? Spine.prototype.autoUpdateTransform : core.DisplayObjectContainer.prototype.updateTransform; + this.updateTransform = value ? Spine.prototype.autoUpdateTransform : core.Container.prototype.updateTransform; } } }); @@ -211,7 +211,7 @@ this.update(timeDelta); - core.DisplayObjectContainer.prototype.updateTransform.call(this); + core.Container.prototype.updateTransform.call(this); }; /** diff --git a/src/core/display/Container.js b/src/core/display/Container.js new file mode 100644 index 0000000..d2f35ff --- /dev/null +++ b/src/core/display/Container.js @@ -0,0 +1,669 @@ +var math = require('../math'), + DisplayObject = require('./DisplayObject'), + RenderTexture = require('../textures/RenderTexture'), + // Sprite = require('./Sprite'), + _tempMatrix = new math.Matrix(); + +/** + * A Container represents a collection of display objects. + * It is the base class of all display objects that act as a container for other objects. + * + * @class + * @extends DisplayObject + * @namespace PIXI + */ +function Container() +{ + DisplayObject.call(this); + + /** + * The array of children of this container. + * + * @member {DisplayObject[]} + * @readonly + */ + this.children = []; + + /** + * Cached internal flag. + * + * @member {boolean} + * @private + */ + this._cacheAsBitmap = false; + + this._cachedSprite = null; +} + +// constructor +Container.prototype = Object.create(DisplayObject.prototype); +Container.prototype.constructor = Container; +module.exports = Container; + +Object.defineProperties(Container.prototype, { + /** + * The width of the Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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 Container, setting this will actually modify the scale to achieve the value set + * + * @member {number} + * @memberof Container# + */ + 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; + } + }, + + /** + * 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' + * + * @member {boolean} + * @memberof DisplayObject# + */ + cacheAsBitmap: { + get: function () + { + return this._cacheAsBitmap; + }, + set: function (value) + { + if (this._cacheAsBitmap === value) + { + return; + } + + if (value) + { + this._generateCachedSprite(); + } + else + { + this._destroyCachedSprite(); + } + + this._cacheAsBitmap = value; + } + } +}); + +/** + * Adds a child to the container. + * + * @param child {DisplayObject} The DisplayObject to add to the container + * @return {DisplayObject} The child that was added. + */ +Container.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 + * + * @param child {DisplayObject} The child to add + * @param index {Number} The index to place the child in + * @return {DisplayObject} The child that was added. + */ +Container.prototype.addChildAt = function (child, index) +{ + // prevent adding self as child + if (child === this) + { + return; + } + + if (index >= 0 && index <= this.children.length) + { + if (child.parent) + { + child.parent.removeChild(child); + } + + child.parent = this; + + this.children.splice(index, 0, child); + + 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. + * + * @param child {DisplayObject} + * @param child2 {DisplayObject} + */ +Container.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 + * + * @param child {DisplayObject} The DisplayObject instance to identify + * @return {Number} The index position of the child display object to identify + */ +Container.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 + * + * @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 + */ +Container.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 + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child at the given index, if any. + */ +Container.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. + * + * @param child {DisplayObject} The DisplayObject to remove + * @return {DisplayObject} The child that was removed. + */ +Container.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. + * + * @param index {Number} The index to get the child from + * @return {DisplayObject} The child that was removed. + */ +Container.prototype.removeChildAt = function (index) +{ + var child = this.getChildAt(index); + + child.parent = null; + this.children.splice(index, 1); + + return child; +}; + +/** + * Removes all children from this container that are within the begin and end indexes. + * + * @param beginIndex {Number} The beginning position. Default value is 0. + * @param endIndex {Number} The ending position. Default value is size of the container. + */ +Container.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) + { + removed[i].parent = null; + } + + return removed; + } + else if (range === 0 && this.children.length === 0) + { + return []; + } + else + { + throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); + } +}; + +/** + * Generates and updates the cached sprite for this object. + * + */ +Container.prototype.updateCachedSprite = function () +{ + this._generateCachedSprite(); +}; + +/** + * 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. + * + * @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 + */ +Container.prototype.generateTexture = function (resolution, scaleMode, renderer) +{ + var bounds = this.getLocalBounds(); + + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + renderTexture.render(this, _tempMatrix); + + return renderTexture; +}; + +/* + * Updates the transform on all children of this container for rendering + * + * @private + */ +Container.prototype.updateTransform = function () +{ + if (!this.visible) + { + return; + } + + this.displayObjectUpdateTransform(); + + if (this._cacheAsBitmap) + { + return; + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } +}; + +// performance increase to avoid using call.. (10x faster) +Container.prototype.ContainerUpdateTransform = Container.prototype.updateTransform; + +/** + * Retrieves the bounds of the Container as a rectangle. The bounds calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getBounds = function () +{ + if (this.children.length === 0) + { + return math.Rectangle.EMPTY; + } + + // 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 < j; ++i) + { + var child = this.children[i]; + + if (!child.visible) + { + continue; + } + + childVisible = true; + + 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; + } + + if (!childVisible) + { + return math.Rectangle.EMPTY; + } + + this._bounds.x = minX; + this._bounds.y = minY; + this._bounds.width = maxX - minX; + this._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 this._bounds; +}; + +Container.prototype.getBounds + +/** + * Retrieves the non-global local bounds of the Container as a rectangle. + * The calculation takes all visible children into consideration. + * + * @return {Rectangle} The rectangular bounding area + */ +Container.prototype.getLocalBounds = function () +{ + var matrixCache = this.worldTransform; + + this.worldTransform = math.Matrix.IDENTITY; + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].updateTransform(); + } + + this.worldTransform = matrixCache; + + return this.getBounds(); +}; + +/** + * Renders the object using the WebGL renderer + * + * TODO - Optimization pass! + * + * @param renderer {WebGLRenderer} The renderer + */ +Container.prototype.renderWebGL = function (renderer) +{ + // if the object is not visible or the alpha is 0 then no need to render this element + if (this.isMask || !this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + var i, j; + + // do a quick check to see if this element has a mask or a filter. + if (this._mask || this._filters) + { + renderer.currentRenderer.flush(); + + // push filter first as we need to ensure the stencil buffer is correct for any masking + if (this._filters) + { + renderer.filterManager.pushFilter(this, this._filters); + } + + if (this._mask) + { + renderer.maskManager.pushMask(this, this._mask); + } + + renderer.currentRenderer.start(); + + + // add this object to the batch, only rendered if it has a texture. + this._renderWebGL(renderer); + + // 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(renderer); + } + + renderer.currentRenderer.flush(); + + if (this._mask) + { + renderer.maskManager.popMask(this, this._mask); + } + + if (this._filters) + { + renderer.filterManager.popFilter(); + + } + renderer.currentRenderer.start(); + + } + else + { + + this._renderWebGL(renderer); + + // simple render children! + for (i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderWebGL(renderer); + } + + } +}; + +Container.prototype._renderWebGL = function (/* renderer */) +{ + // this is where content itself gets renderd.. +}; + +/** + * Renders the object using the Canvas renderer + * + * @param renderer {CanvasRenderer} The renderer + */ +Container.prototype.renderCanvas = function (renderer) +{ + if (!this.visible || this.alpha <= 0) + { + return; + } + + if (this._cacheAsBitmap) + { + this._renderCachedSprite(renderer); + return; + } + + if (this._mask) + { + renderer.maskManager.pushMask(this._mask, renderer); + } + + for (var i = 0, j = this.children.length; i < j; ++i) + { + this.children[i].renderCanvas(renderer); + } + + if (this._mask) + { + renderer.maskManager.popMask(renderer); + } +}; + +/** + * Internal method. + * + * @param renderer {WebGLRenderer|CanvasRenderer} The renderer + * @private + */ +Container.prototype._renderCachedSprite = function (renderer) +{ + this._cachedSprite.worldAlpha = this.worldAlpha; + + if (renderer.gl) + { + this._cachedSprite.renderWebGL(renderer); + } + else + { + this._cachedSprite.renderCanvas(renderer); + } +}; + +/** + * Internal method. + * + * @private + */ +Container.prototype._generateCachedSprite = function () +{/* + var bounds = this.getLocalBounds(); + + if (!this._cachedSprite) + { + // TODO - RenderTexture now *requires* a renderer instance, so this is like broken + // because `renderer` isn't actually in scope here :P + var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); + + this._cachedSprite = new Sprite(renderTexture); + this._cachedSprite.worldTransform = this.worldTransform; + } + else + { + this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); + } + + var tempFilters = this._filters; + this._filters = null; + + this._cachedSprite.filters = tempFilters; + + _tempMatrix.tx = -bounds.x; + _tempMatrix.ty = -bounds.y; + + this._cachedSprite.texture.render(this, _tempMatrix, true); + + this._cachedSprite.anchor.x = -(bounds.x / bounds.width); + this._cachedSprite.anchor.y = -(bounds.y / bounds.height); + + this._filters = tempFilters;*/ +}; + +/** + * Destroys the cached sprite. + * + * @private + */ +Container.prototype._destroyCachedSprite = function () +{ + if (!this._cachedSprite) + { + return; + } + + // TODO: Pool this sprite + this._cachedSprite.destroy(true, true); + this._cachedSprite = null; +}; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index 3ffd6d5..b53e0af 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -63,7 +63,7 @@ /** * The display object container that contains this display object. * - * @member {DisplayObjectContainer} + * @member {Container} * @readOnly */ this.parent = null; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js deleted file mode 100644 index ad76d00..0000000 --- a/src/core/display/DisplayObjectContainer.js +++ /dev/null @@ -1,667 +0,0 @@ -var math = require('../math'), - DisplayObject = require('./DisplayObject'), - RenderTexture = require('../textures/RenderTexture'), - // Sprite = require('./Sprite'), - _tempMatrix = new math.Matrix(); - -/** - * 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 - * @extends DisplayObject - * @namespace PIXI - */ -function DisplayObjectContainer() -{ - DisplayObject.call(this); - - /** - * The array of children of this container. - * - * @member {DisplayObject[]} - * @readonly - */ - this.children = []; - - /** - * Cached internal flag. - * - * @member {boolean} - * @private - */ - this._cacheAsBitmap = false; - - this._cachedSprite = null; -} - -// constructor -DisplayObjectContainer.prototype = Object.create(DisplayObject.prototype); -DisplayObjectContainer.prototype.constructor = DisplayObjectContainer; -module.exports = DisplayObjectContainer; - -Object.defineProperties(DisplayObjectContainer.prototype, { - /** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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 - * - * @member {number} - * @memberof DisplayObjectContainer# - */ - 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; - } - }, - - /** - * 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' - * - * @member {boolean} - * @memberof DisplayObject# - */ - cacheAsBitmap: { - get: function () - { - return this._cacheAsBitmap; - }, - set: function (value) - { - if (this._cacheAsBitmap === value) - { - return; - } - - if (value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } - } -}); - -/** - * Adds a child to the container. - * - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -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 - * - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -DisplayObjectContainer.prototype.addChildAt = function (child, index) -{ - // prevent adding self as child - if (child === this) - { - return; - } - - if (index >= 0 && index <= this.children.length) - { - if (child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - 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. - * - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -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 - * - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -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 - * - * @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 - */ -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 - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -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. - * - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -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. - * - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -DisplayObjectContainer.prototype.removeChildAt = function (index) -{ - var child = this.getChildAt(index); - - child.parent = null; - this.children.splice(index, 1); - - return child; -}; - -/** - * Removes all children from this container that are within the begin and end indexes. - * - * @param beginIndex {Number} The beginning position. Default value is 0. - * @param endIndex {Number} The ending position. Default value is size of the container. - */ -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) - { - removed[i].parent = null; - } - - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new RangeError('removeChildren: numeric values are outside the acceptable range.'); - } -}; - -/** - * Generates and updates the cached sprite for this object. - * - */ -DisplayObjectContainer.prototype.updateCachedSprite = function () -{ - this._generateCachedSprite(); -}; - -/** - * 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. - * - * @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 - */ -DisplayObjectContainer.prototype.generateTexture = function (resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - renderTexture.render(this, _tempMatrix); - - return renderTexture; -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @private - */ -DisplayObjectContainer.prototype.updateTransform = function () -{ - if (!this.visible) - { - return; - } - - this.displayObjectUpdateTransform(); - - if (this._cacheAsBitmap) - { - return; - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } -}; - -// performance increase to avoid using call.. (10x faster) -DisplayObjectContainer.prototype.displayObjectContainerUpdateTransform = DisplayObjectContainer.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObjectContainer as a rectangle. The bounds calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getBounds = function () -{ - if (this.children.length === 0) - { - return math.Rectangle.EMPTY; - } - - // 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 < j; ++i) - { - var child = this.children[i]; - - if (!child.visible) - { - continue; - } - - childVisible = true; - - 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; - } - - if (!childVisible) - { - return math.Rectangle.EMPTY; - } - - this._bounds.x = minX; - this._bounds.y = minY; - this._bounds.width = maxX - minX; - this._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 this._bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. - * The calculation takes all visible children into consideration. - * - * @return {Rectangle} The rectangular bounding area - */ -DisplayObjectContainer.prototype.getLocalBounds = function () -{ - var matrixCache = this.worldTransform; - - this.worldTransform = math.Matrix.IDENTITY; - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].updateTransform(); - } - - this.worldTransform = matrixCache; - - return this.getBounds(); -}; - -/** - * Renders the object using the WebGL renderer - * - * TODO - Optimization pass! - * - * @param renderer {WebGLRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderWebGL = function (renderer) -{ - // if the object is not visible or the alpha is 0 then no need to render this element - if (this.isMask || !this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - var i, j; - - // do a quick check to see if this element has a mask or a filter. - if (this._mask || this._filters) - { - renderer.currentRenderer.flush(); - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if (this._filters) - { - renderer.filterManager.pushFilter(this, this._filters); - } - - if (this._mask) - { - renderer.maskManager.pushMask(this, this._mask); - } - - renderer.currentRenderer.start(); - - - // add this object to the batch, only rendered if it has a texture. - this._renderWebGL(renderer); - - // 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(renderer); - } - - renderer.currentRenderer.flush(); - - if (this._mask) - { - renderer.maskManager.popMask(this, this._mask); - } - - if (this._filters) - { - renderer.filterManager.popFilter(); - - } - renderer.currentRenderer.start(); - - } - else - { - - this._renderWebGL(renderer); - - // simple render children! - for (i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderWebGL(renderer); - } - - } -}; - -DisplayObjectContainer.prototype._renderWebGL = function (/* renderer */) -{ - // this is where content itself gets renderd.. -}; - -/** - * Renders the object using the Canvas renderer - * - * @param renderer {CanvasRenderer} The renderer - */ -DisplayObjectContainer.prototype.renderCanvas = function (renderer) -{ - if (!this.visible || this.alpha <= 0) - { - return; - } - - if (this._cacheAsBitmap) - { - this._renderCachedSprite(renderer); - return; - } - - if (this._mask) - { - renderer.maskManager.pushMask(this._mask, renderer); - } - - for (var i = 0, j = this.children.length; i < j; ++i) - { - this.children[i].renderCanvas(renderer); - } - - if (this._mask) - { - renderer.maskManager.popMask(renderer); - } -}; - -/** - * Internal method. - * - * @param renderer {WebGLRenderer|CanvasRenderer} The renderer - * @private - */ -DisplayObjectContainer.prototype._renderCachedSprite = function (renderer) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if (renderer.gl) - { - this._cachedSprite.renderWebGL(renderer); - } - else - { - this._cachedSprite.renderCanvas(renderer); - } -}; - -/** - * Internal method. - * - * @private - */ -DisplayObjectContainer.prototype._generateCachedSprite = function () -{/* - var bounds = this.getLocalBounds(); - - if (!this._cachedSprite) - { - // TODO - RenderTexture now *requires* a renderer instance, so this is like broken - // because `renderer` isn't actually in scope here :P - var renderTexture = new RenderTexture(renderer, bounds.width | 0, bounds.height | 0); - - this._cachedSprite = new Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - _tempMatrix.tx = -bounds.x; - _tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, _tempMatrix, true); - - this._cachedSprite.anchor.x = -(bounds.x / bounds.width); - this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - - this._filters = tempFilters;*/ -}; - -/** - * Destroys the cached sprite. - * - * @private - */ -DisplayObjectContainer.prototype._destroyCachedSprite = function () -{ - if (!this._cachedSprite) - { - return; - } - - // TODO: Pool this sprite - this._cachedSprite.destroy(true, true); - this._cachedSprite = null; -}; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index ccd4a91..baeb1de 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1,4 +1,4 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'), +var Container = require('../display/Container'), Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), CanvasBuffer = require('../renderers/canvas/utils/CanvasBuffer'), @@ -13,12 +13,12 @@ * rectangles to the display, and color and fill them. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI */ function Graphics() { - DisplayObjectContainer.call(this); + Container.call(this); this.renderable = true; @@ -137,7 +137,7 @@ } // constructor -Graphics.prototype = Object.create(DisplayObjectContainer.prototype); +Graphics.prototype = Object.create(Container.prototype); Graphics.prototype.constructor = Graphics; module.exports = Graphics; diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js index 4fc777d..61e2211 100644 --- a/src/core/graphics/index.js +++ b/src/core/graphics/index.js @@ -17,9 +17,9 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + Stage: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), diff --git a/src/core/index.js b/src/core/index.js index 67b8d4e..be4b1b3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -17,9 +17,11 @@ // display DisplayObject: require('./display/DisplayObject'), - DisplayObjectContainer: require('./display/DisplayObjectContainer'), + Container: require('./display/Container'), - Stage: require('./display/DisplayObjectContainer'), + // legacy.. + Stage: require('./display/Container'), + DisplayObjectContainer: require('./display/Container'), Sprite: require('./sprites/Sprite'), SpriteBatch: require('./sprites/SpriteBatch'), @@ -44,7 +46,7 @@ // renderers - webgl WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), - ShaderManager: require('./renderers/webgl/managers/ShaderManager'), + ShaderManager: require('./renderers/webgl/managers/ShaderManager'), Shader: require('./renderers/webgl/shaders/Shader'), /** diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 249e448..37761b5 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -1,6 +1,6 @@ var math = require('../math'), Texture = require('../textures/Texture'), - DisplayObjectContainer = require('../display/DisplayObjectContainer'), + Container = require('../display/Container'), CanvasTinter = require('../renderers/canvas/utils/CanvasTinter'), utils = require('../utils'), CONST = require('../const'); @@ -15,13 +15,13 @@ * ``` * * @class Sprite - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture for this sprite */ function Sprite(texture) { - DisplayObjectContainer.call(this); + Container.call(this); /** @@ -89,7 +89,7 @@ Sprite.prototype.destroy = function (destroyTexture, destroyBaseTexture) { - DisplayObjectContainer.prototype.destroy.call(this); + Container.prototype.destroy.call(this); this.anchor = null; @@ -103,7 +103,7 @@ }; // constructor -Sprite.prototype = Object.create(DisplayObjectContainer.prototype); +Sprite.prototype = Object.create(Container.prototype); Sprite.prototype.constructor = Sprite; module.exports = Sprite; @@ -300,9 +300,15 @@ bounds.y = minY; bounds.height = maxY - minY; + if(this.children.length) + { + + } + // 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; }; diff --git a/src/core/sprites/SpriteBatch.js b/src/core/sprites/SpriteBatch.js index c1e674c..1274c82 100644 --- a/src/core/sprites/SpriteBatch.js +++ b/src/core/sprites/SpriteBatch.js @@ -1,7 +1,7 @@ -var DisplayObjectContainer = require('../display/DisplayObjectContainer'); +var Container = require('../display/Container'); /** - * The SpriteBatch class is a really fast version of the DisplayObjectContainer built solely for speed, + * The SpriteBatch class is a really fast version of the Container built solely for speed, * so use when you need a lot of sprites or particles. The tradeoff of the SpriteBatch is that advanced * functionality will not work. SpriteBatch implements only the basic object transform (position, scale, rotation). * Any other functionality like tinting, masking, etc will not work on sprites in this batch. @@ -27,10 +27,10 @@ //TODO RENAME to PARTICLE CONTAINER? function SpriteBatch() { - DisplayObjectContainer.call(this); + Container.call(this); } -SpriteBatch.prototype = Object.create(DisplayObjectContainer.prototype); +SpriteBatch.prototype = Object.create(Container.prototype); SpriteBatch.prototype.constructor = SpriteBatch; module.exports = SpriteBatch; @@ -43,7 +43,7 @@ { // TODO don't need to! this.displayObjectUpdateTransform(); - // PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); + // PIXI.Container.prototype.updateTransform.call( this ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index a19f95e..2fe7eb2 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -27,10 +27,10 @@ * ``` * * 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: + * position a Container should be used: * * ```js - * var doc = new DisplayObjectContainer(); + * var doc = new Container(); * * doc.addChild(sprite); * diff --git a/src/extras/MovieClip.js b/src/extras/MovieClip.js index 6b61ea9..95d3a2b 100644 --- a/src/extras/MovieClip.js +++ b/src/extras/MovieClip.js @@ -133,7 +133,7 @@ */ MovieClip.prototype.updateTransform = function () { - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); if (!this.playing) { diff --git a/src/extras/Rope.js b/src/extras/Rope.js index 9c87bc0..4dbeb6c 100644 --- a/src/extras/Rope.js +++ b/src/extras/Rope.js @@ -161,7 +161,7 @@ lastPoint = point; } - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); }; /** diff --git a/src/extras/Strip.js b/src/extras/Strip.js index 6bf78cb..113ef8f 100644 --- a/src/extras/Strip.js +++ b/src/extras/Strip.js @@ -3,7 +3,7 @@ /** * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param texture {Texture} The texture to use * @param width {number} the width @@ -12,7 +12,7 @@ */ function Strip(texture) { - core.DisplayObjectContainer.call(this); + core.Container.call(this); /** * The texture of the strip @@ -62,7 +62,7 @@ } // constructor -Strip.prototype = Object.create(core.DisplayObjectContainer.prototype); +Strip.prototype = Object.create(core.Container.prototype); Strip.prototype.constructor = Strip; module.exports = Strip; diff --git a/src/spine/Spine.js b/src/spine/Spine.js index ce425cc..79a4ab9 100644 --- a/src/spine/Spine.js +++ b/src/spine/Spine.js @@ -11,13 +11,13 @@ * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param url {string} The url of the spine anim file to be used */ function Spine(url) { - core.DisplayObjectContainer.call(this); + core.Container.call(this); this.spineData = core.utils.AnimCache[url]; @@ -38,7 +38,7 @@ { var slot = this.skeleton.drawOrder[i]; var attachment = slot.attachment; - var slotContainer = new core.DisplayObjectContainer(); + var slotContainer = new core.Container(); this.slotContainers.push(slotContainer); this.addChild(slotContainer); @@ -67,7 +67,7 @@ this.autoUpdate = true; } -Spine.prototype = Object.create(core.DisplayObjectContainer.prototype); +Spine.prototype = Object.create(core.Container.prototype); Spine.prototype.constructor = Spine; module.exports = Spine; @@ -91,7 +91,7 @@ set: function (value) { - this.updateTransform = value ? Spine.prototype.autoUpdateTransform : core.DisplayObjectContainer.prototype.updateTransform; + this.updateTransform = value ? Spine.prototype.autoUpdateTransform : core.Container.prototype.updateTransform; } } }); @@ -211,7 +211,7 @@ this.update(timeDelta); - core.DisplayObjectContainer.prototype.updateTransform.call(this); + core.Container.prototype.updateTransform.call(this); }; /** diff --git a/src/text/BitmapText.js b/src/text/BitmapText.js index 77df45b..8c86354 100644 --- a/src/text/BitmapText.js +++ b/src/text/BitmapText.js @@ -8,7 +8,7 @@ * http://www.bmglyph.com/ for mac. * * @class - * @extends DisplayObjectContainer + * @extends Container * @namespace PIXI * @param text {string} The copy that you would like the text to display * @param style {object} The style parameters @@ -17,7 +17,7 @@ */ function BitmapText(text, style) { - core.DisplayObjectContainer.call(this); + core.Container.call(this); /** * The width of the overall text, different from fontSize, @@ -78,7 +78,7 @@ } // constructor -BitmapText.prototype = Object.create(core.DisplayObjectContainer.prototype); +BitmapText.prototype = Object.create(core.Container.prototype); BitmapText.prototype.constructor = BitmapText; module.exports = BitmapText; @@ -292,7 +292,7 @@ this.dirty = false; } - this.displayObjectContainerUpdateTransform(); + this.ContainerUpdateTransform(); }; BitmapText.fonts = {};