import { utils, Transform, Rectangle, RAD_TO_DEG, DEG_TO_RAD } from "@pixi/core"; import { Bounds } from "./Bounds.mjs"; class DisplayObject extends utils.EventEmitter { constructor() { super(), this.tempDisplayObjectParent = null, this.transform = new Transform(), this.alpha = 1, this.visible = !0, this.renderable = !0, this.cullable = !1, this.cullArea = null, this.parent = null, this.worldAlpha = 1, this._lastSortedIndex = 0, this._zIndex = 0, this.filterArea = null, this.filters = null, this._enabledFilters = null, this._bounds = new Bounds(), this._localBounds = null, this._boundsID = 0, this._boundsRect = null, this._localBoundsRect = null, this._mask = null, this._maskRefCount = 0, this._destroyed = !1, this.isSprite = !1, this.isMask = !1; } /** * Mixes all enumerable properties and methods from a source object to DisplayObject. * @param source - The source of properties and methods to mix in. */ static mixin(source) { const keys = Object.keys(source); for (let i = 0; i < keys.length; ++i) { const propertyName = keys[i]; Object.defineProperty( DisplayObject.prototype, propertyName, Object.getOwnPropertyDescriptor(source, propertyName) ); } } /** * Fired when this DisplayObject is added to a Container. * @instance * @event added * @param {PIXI.Container} container - The container added to. */ /** * Fired when this DisplayObject is removed from a Container. * @instance * @event removed * @param {PIXI.Container} container - The container removed from. */ /** * Fired when this DisplayObject is destroyed. This event is emitted once * destroy is finished. * @instance * @event destroyed */ /** Readonly flag for destroyed display objects. */ get destroyed() { return this._destroyed; } /** Recursively updates transform of all objects from the root to this one internal function for toLocal() */ _recursivePostUpdateTransform() { this.parent ? (this.parent._recursivePostUpdateTransform(), this.transform.updateTransform(this.parent.transform)) : this.transform.updateTransform(this._tempDisplayObjectParent.transform); } /** Updates the object transform for rendering. TODO - Optimization pass! */ updateTransform() { this._boundsID++, this.transform.updateTransform(this.parent.transform), this.worldAlpha = this.alpha * this.parent.worldAlpha; } /** * Calculates and returns the (world) bounds of the display object as a [Rectangle]{@link PIXI.Rectangle}. * * This method is expensive on containers with a large subtree (like the stage). This is because the bounds * of a container depend on its children's bounds, which recursively causes all bounds in the subtree to * be recalculated. The upside, however, is that calling `getBounds` once on a container will indeed update * the bounds of all children (the whole subtree, in fact). This side effect should be exploited by using * `displayObject._bounds.getRectangle()` when traversing through all the bounds in a scene graph. Otherwise, * calling `getBounds` on each object in a subtree will cause the total cost to increase quadratically as * its height increases. * * The transforms of all objects in a container's **subtree** and of all **ancestors** are updated. * The world bounds of all display objects in a container's **subtree** will also be recalculated. * * The `_bounds` object stores the last calculation of the bounds. You can use to entirely skip bounds * calculation if needed. * * ```js * const lastCalculatedBounds = displayObject._bounds.getRectangle(optionalRect); * ``` * * Do know that usage of `getLocalBounds` can corrupt the `_bounds` of children (the whole subtree, actually). This * is a known issue that has not been solved. See [getLocalBounds]{@link PIXI.DisplayObject#getLocalBounds} for more * details. * * `getBounds` should be called with `skipUpdate` equal to `true` in a render() call. This is because the transforms * are guaranteed to be update-to-date. In fact, recalculating inside a render() call may cause corruption in certain * cases. * @param skipUpdate - Setting to `true` will stop the transforms of the scene graph from * being updated. This means the calculation returned MAY be out of date BUT will give you a * nice performance boost. * @param rect - Optional rectangle to store the result of the bounds calculation. * @returns - The minimum axis-aligned rectangle in world space that fits around this object. */ getBounds(skipUpdate, rect) { return skipUpdate || (this.parent ? (this._recursivePostUpdateTransform(), this.updateTransform()) : (this.parent = this._tempDisplayObjectParent, this.updateTransform(), this.parent = null)), this._bounds.updateID !== this._boundsID && (this.calculateBounds(), this._bounds.updateID = this._boundsID), rect || (this._boundsRect || (this._boundsRect = new Rectangle()), rect = this._boundsRect), this._bounds.getRectangle(rect); } /** * Retrieves the local bounds of the displayObject as a rectangle object. * @param rect - Optional rectangle to store the result of the bounds calculation. * @returns - The rectangular bounding area. */ getLocalBounds(rect) { rect || (this._localBoundsRect || (this._localBoundsRect = new Rectangle()), rect = this._localBoundsRect), this._localBounds || (this._localBounds = new Bounds()); const transformRef = this.transform, parentRef = this.parent; this.parent = null, this._tempDisplayObjectParent.worldAlpha = parentRef?.worldAlpha ?? 1, this.transform = this._tempDisplayObjectParent.transform; const worldBounds = this._bounds, worldBoundsID = this._boundsID; this._bounds = this._localBounds; const bounds = this.getBounds(!1, rect); return this.parent = parentRef, this.transform = transformRef, this._bounds = worldBounds, this._bounds.updateID += this._boundsID - worldBoundsID, bounds; } /** * Calculates the global position of the display object. * @param position - The world origin to calculate from. * @param point - A Point object in which to store the value, optional * (otherwise will create a new Point). * @param skipUpdate - Should we skip the update transform. * @returns - A point object representing the position of this object. */ toGlobal(position, point, skipUpdate = !1) { return skipUpdate || (this._recursivePostUpdateTransform(), this.parent ? this.displayObjectUpdateTransform() : (this.parent = this._tempDisplayObjectParent, this.displayObjectUpdateTransform(), this.parent = null)), this.worldTransform.apply(position, point); } /** * Calculates the local position of the display object relative to another point. * @param position - The world origin to calculate from. * @param from - The DisplayObject to calculate the global position from. * @param point - A Point object in which to store the value, optional * (otherwise will create a new Point). * @param skipUpdate - Should we skip the update transform * @returns - A point object representing the position of this object */ toLocal(position, from, point, skipUpdate) { return from && (position = from.toGlobal(position, point, skipUpdate)), skipUpdate || (this._recursivePostUpdateTransform(), this.parent ? this.displayObjectUpdateTransform() : (this.parent = this._tempDisplayObjectParent, this.displayObjectUpdateTransform(), this.parent = null)), this.worldTransform.applyInverse(position, point); } /** * Set the parent Container of this DisplayObject. * @param container - The Container to add this DisplayObject to. * @returns - The Container that this DisplayObject was added to. */ setParent(container) { if (!container || !container.addChild) throw new Error("setParent: Argument must be a Container"); return container.addChild(this), container; } /** Remove the DisplayObject from its parent Container. If the DisplayObject has no parent, do nothing. */ removeFromParent() { this.parent?.removeChild(this); } /** * Convenience function to set the position, scale, skew and pivot at once. * @param x - The X position * @param y - The Y position * @param scaleX - The X scale value * @param scaleY - The Y scale value * @param rotation - The rotation * @param skewX - The X skew value * @param skewY - The Y skew value * @param pivotX - The X pivot value * @param pivotY - The Y pivot value * @returns - The DisplayObject instance */ setTransform(x = 0, y = 0, scaleX = 1, scaleY = 1, rotation = 0, skewX = 0, skewY = 0, pivotX = 0, pivotY = 0) { return this.position.x = x, this.position.y = y, this.scale.x = scaleX || 1, this.scale.y = scaleY || 1, this.rotation = rotation, this.skew.x = skewX, this.skew.y = skewY, this.pivot.x = pivotX, this.pivot.y = pivotY, this; } /** * Base destroy method for generic display objects. This will automatically * remove the display object from its parent Container as well as remove * all current event listeners and internal references. Do not use a DisplayObject * after calling `destroy()`. * @param _options */ destroy(_options) { this.removeFromParent(), this._destroyed = !0, this.transform = null, this.parent = null, this._bounds = null, this.mask = null, this.cullArea = null, this.filters = null, this.filterArea = null, this.hitArea = null, this.eventMode = "auto", this.interactiveChildren = !1, this.emit("destroyed"), this.removeAllListeners(); } /** * @protected * @member {PIXI.Container} */ get _tempDisplayObjectParent() { return this.tempDisplayObjectParent === null && (this.tempDisplayObjectParent = new TemporaryDisplayObject()), this.tempDisplayObjectParent; } /** * Used in Renderer, cacheAsBitmap and other places where you call an `updateTransform` on root. * * ```js * const cacheParent = elem.enableTempParent(); * elem.updateTransform(); * elem.disableTempParent(cacheParent); * ``` * @returns - Current parent */ enableTempParent() { const myParent = this.parent; return this.parent = this._tempDisplayObjectParent, myParent; } /** * Pair method for `enableTempParent` * @param cacheParent - Actual parent of element */ disableTempParent(cacheParent) { this.parent = cacheParent; } /** * The position of the displayObject on the x axis relative to the local coordinates of the parent. * An alias to position.x */ get x() { return this.position.x; } set x(value) { this.transform.position.x = value; } /** * The position of the displayObject on the y axis relative to the local coordinates of the parent. * An alias to position.y */ get y() { return this.position.y; } set y(value) { this.transform.position.y = value; } /** * Current transform of the object based on world (parent) factors. * @readonly */ get worldTransform() { return this.transform.worldTransform; } /** * Current transform of the object based on local factors: position, scale, other stuff. * @readonly */ get localTransform() { return this.transform.localTransform; } /** * The coordinate of the object relative to the local coordinates of the parent. * @since 4.0.0 */ get position() { return this.transform.position; } set position(value) { this.transform.position.copyFrom(value); } /** * The scale factors of this object along the local coordinate axes. * * The default scale is (1, 1). * @since 4.0.0 */ get scale() { return this.transform.scale; } set scale(value) { this.transform.scale.copyFrom(value); } /** * The center of rotation, scaling, and skewing for this display object in its local space. The `position` * is the projection of `pivot` in the parent's local space. * * By default, the pivot is the origin (0, 0). * @since 4.0.0 */ get pivot() { return this.transform.pivot; } set pivot(value) { this.transform.pivot.copyFrom(value); } /** * The skew factor for the object in radians. * @since 4.0.0 */ get skew() { return this.transform.skew; } set skew(value) { this.transform.skew.copyFrom(value); } /** * The rotation of the object in radians. * 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees. */ get rotation() { return this.transform.rotation; } set rotation(value) { this.transform.rotation = value; } /** * The angle of the object in degrees. * 'rotation' and 'angle' have the same effect on a display object; rotation is in radians, angle is in degrees. */ get angle() { return this.transform.rotation * RAD_TO_DEG; } set angle(value) { this.transform.rotation = value * DEG_TO_RAD; } /** * The zIndex of the displayObject. * * If a container has the sortableChildren property set to true, children will be automatically * sorted by zIndex value; a higher value will mean it will be moved towards the end of the array, * and thus rendered on top of other display objects within the same container. * @see PIXI.Container#sortableChildren */ get zIndex() { return this._zIndex; } set zIndex(value) { this._zIndex !== value && (this._zIndex = value, this.parent && (this.parent.sortDirty = !0)); } /** * Indicates if the object is globally visible. * @readonly */ get worldVisible() { let item = this; do { if (!item.visible) return !1; item = item.parent; } while (item); return !0; } /** * Sets a mask for the displayObject. A mask is an object that limits the visibility of an * object to the shape of the mask applied to it. In PixiJS a regular mask must be a * {@link PIXI.Graphics} or a {@link PIXI.Sprite} object. This allows for much faster masking in canvas as it * utilities shape clipping. Furthermore, a mask of an object must be in the subtree of its parent. * Otherwise, `getLocalBounds` may calculate incorrect bounds, which makes the container's width and height wrong. * To remove a mask, set this property to `null`. * * For sprite mask both alpha and red channel are used. Black mask is the same as transparent mask. * @example * import { Graphics, Sprite } from 'pixi.js'; * * const graphics = new Graphics(); * graphics.beginFill(0xFF3300); * graphics.drawRect(50, 250, 100, 100); * graphics.endFill(); * * const sprite = new Sprite(texture); * sprite.mask = graphics; * @todo At the moment, CanvasRenderer doesn't support Sprite as mask. */ get mask() { return this._mask; } set mask(value) { if (this._mask !== value) { if (this._mask) { const maskObject = this._mask.isMaskData ? this._mask.maskObject : this._mask; maskObject && (maskObject._maskRefCount--, maskObject._maskRefCount === 0 && (maskObject.renderable = !0, maskObject.isMask = !1)); } if (this._mask = value, this._mask) { const maskObject = this._mask.isMaskData ? this._mask.maskObject : this._mask; maskObject && (maskObject._maskRefCount === 0 && (maskObject.renderable = !1, maskObject.isMask = !0), maskObject._maskRefCount++); } } } } class TemporaryDisplayObject extends DisplayObject { constructor() { super(...arguments), this.sortDirty = null; } } DisplayObject.prototype.displayObjectUpdateTransform = DisplayObject.prototype.updateTransform; export { DisplayObject, TemporaryDisplayObject }; //# sourceMappingURL=DisplayObject.mjs.map