UNPKG

@pixi-essentials/cull

Version:

Optimized recursive display-object culling for scene graphs

263 lines (221 loc) 7.04 kB
/* eslint-disable */ /*! * @pixi-essentials/cull - v1.1.0 * Compiled Sun, 05 Mar 2023 03:07:48 UTC * * @pixi-essentials/cull is licensed under the MIT License. * http://www.opensource.org/licenses/mit-license * * Copyright 2019-2020, Shukant Pal <shukantpal@outlook.com>, All Rights Reserved */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var math = require('@pixi/math'); const tempRect = new math.Rectangle(); /** * The culling options for {@code Cull}. * * @ignore * @public */ /** * Provides a simple, configurable mechanism for culling a subtree of your scene graph. * * If your scene graph is not static, culling needs to be done before rendering. You * can run it on the `prerender` event fired by the renderer. * * @public */ class Cull { /** * @param options * @param [options.recursive] - whether culling should be recursive * @param [options.toggle='renderable'] - which property of display-object was be set to indicate * its culling state. It should be one of `renderable`, `visible`. */ constructor(options = {}) { this._recursive = typeof options.recursive === 'boolean' ? options.recursive : true; this._toggle = options.toggle || 'visible'; this._targetList = new Set(); } /** * Adds a display-object to the culling list * * @param target - the display-object to be culled * @return this */ add(target) { this._targetList.add(target); return this; } /** * Adds all the display-objects to the culling list * * @param targets - the display-objects to be culled * @return this */ addAll(targets) { for (let i = 0, j = targets.length; i < j; i++) { this._targetList.add(targets[i]); } return this; } /** * Removes the display-object from the culling list * * @param target - the display-object to be removed * @return this */ remove(target) { this._targetList.delete(target); return this; } /** * Removes all the passed display-objects from the culling list * * @param targets - the display-objects to be removed * @return this */ removeAll(targets) { for (let i = 0, j = targets.length; i < j; i++) { this._targetList.delete(targets[i]); } return this; } /** * Clears the culling list * * @return this */ clear() { this._targetList.clear(); return this; } /** * @param rect - the rectangle outside of which display-objects should be culled * @param skipUpdate - whether to skip unculling, transform update, bounds calculation. It is * highly recommended you enable this by calling _this.uncull()_ and _root.getBounds(false)_ manually * before your render loop. * @return this */ cull(rect, skipUpdate = false) { if (!skipUpdate) { this.uncull(); } this._targetList.forEach((target) => { if (!skipUpdate) { // Update transforms, bounds of display-objects in this target's subtree target.getBounds(false, tempRect); } if (this._recursive) { this.cullRecursive(rect, target, skipUpdate); } else { // NOTE: If skipUpdate is false, then tempRect already contains the bounds of the target if (skipUpdate) { target._bounds.getRectangle(rect); } target[this._toggle] = tempRect.right > rect.left && tempRect.left < rect.right && tempRect.bottom > rect.top && tempRect.top < rect.bottom; } }); return this; } /** * Sets all display-objects to the unculled state. * * This happens regardless of whether the culling toggle was set by {@code this.cull} or manually. This * is why it is recommended to one of `visible` or `renderable` for normal use and the other for culling. * * @return this */ uncull() { this._targetList.forEach((target) => { if (this._recursive) { this.uncullRecursive(target); } else { target[this._toggle] = false; } }); return this; } /** * Recursively culls the subtree of {@code displayObject}. * * @param rect - the visiblity rectangle * @param displayObject - the root of the subtree to cull * @param skipUpdate - whether to skip bounds calculation. However, transforms are expected to be updated by the caller. */ cullRecursive(rect, displayObject, skipUpdate) { // NOTE: getBounds can skipUpdate because updateTransform is invoked before culling. const bounds = skipUpdate ? displayObject._bounds.getRectangle(tempRect) : displayObject.getBounds(true, tempRect); displayObject[this._toggle] = bounds.right > rect.left && bounds.left < rect.right && bounds.bottom > rect.top && bounds.top < rect.bottom; const fullyVisible = bounds.left >= rect.left && bounds.top >= rect.top && bounds.right <= rect.right && bounds.bottom <= rect.bottom; // Only cull children if this display-object is *not* fully-visible. It is expected that the bounds // of children lie inside of its own. Hence, further culling is only required if the display-object // intersects with the boundaries of "rect". Otherwise, if the object is fully outside/inside the // screen, the children don't need to be evaluated as they are presumed to be unculled. if (!fullyVisible && displayObject[this._toggle] && (displayObject ).children && (displayObject ).children.length) { const children = (displayObject ).children; for (let i = 0, j = children.length; i < j; i++) { this.cullRecursive(rect, children[i]); } } } /** * Recursively unculls the subtree of {@code displayObject}. * * @param displayObject */ uncullRecursive(displayObject) { displayObject[this._toggle] = true; if ((displayObject ).children && (displayObject ).children.length) { const children = (displayObject ).children; for (let i = 0, j = children.length; i < j; i++) { this.uncullRecursive(children[i]); } } } } exports.Cull = Cull; //# sourceMappingURL=cull.js.map