diff --git a/packages/entity/traits/listed.trait.js b/packages/entity/traits/listed.trait.js index cd41e51..85c1801 100644 --- a/packages/entity/traits/listed.trait.js +++ b/packages/entity/traits/listed.trait.js @@ -31,8 +31,7 @@ export class Listed extends Trait { if (!list) { return; } - const quadTree = list.quadTree; - if (!('visibleBoundingBox' in this.entity)) { + if (!this.entity.is('visible')) { return; } const aabb = this.entity.visibleBoundingBox; @@ -45,6 +44,7 @@ export class Listed extends Trait { const points = Rectangle.toPoints(expandedAabb); this.quadTreeNodes = points.map((point) => [...point, this.entity]); // Add points to quad tree. + const quadTree = list.quadTree; for (const node of this.quadTreeNodes) { quadTree.add(node); } @@ -84,11 +84,7 @@ export class Listed extends Trait { listeners() { return { - visibleBoundingBoxesUpdated: () => { - this.resetQuadTreeNodes(); - }, - - positionChanged: () => { + visibleBoundingBoxChanged: () => { this.resetQuadTreeNodes(); }, diff --git a/packages/graphics/traits/pictured.trait.js b/packages/graphics/traits/pictured.trait.js index 99f21a4..39d6d74 100644 --- a/packages/graphics/traits/pictured.trait.js +++ b/packages/graphics/traits/pictured.trait.js @@ -11,7 +11,7 @@ const decorate = compose( }), ); -class PicturedBase extends Trait { +export class Pictured extends decorate(Trait) { static defaultParams() { return { @@ -28,8 +28,6 @@ class PicturedBase extends Trait { initialize() { this._images = this.params.get('images').toJS(); this.sprites = undefined; - // Bounding box update. - this.entity.emit('visibleBoundingBoxesUpdated'); } destroy() { @@ -61,10 +59,11 @@ class PicturedBase extends Trait { return; } // Load all images. + const imagePromises = []; this.sprites = {}; for (const key in this._images) { const {uri} = this._images[key]; - Image.load(uri).then((image) => { + const imagePromise = Image.load(uri).then((image) => { const sprite = this.sprites[key] = new Sprite(image); // Calculate any offset. sprite.position = this.viewPositionFor(key); @@ -74,7 +73,12 @@ class PicturedBase extends Trait { this.showImage(key); } }); + imagePromises.push(imagePromise); } + Promise.all(imagePromises).then((images) => { + // Bounding box update. + this.entity.updateVisibleBoundingBox(); + }); } offsetFor(key) { @@ -133,7 +137,7 @@ class PicturedBase extends Trait { return { currentImageChanged: (oldKey) => { // Bounding box update. - this.entity.emit('visibleBoundingBoxesUpdated'); + this.entity.updateVisibleBoundingBox(); // Only client/graphics. if (!this.sprites) { return; @@ -155,5 +159,3 @@ class PicturedBase extends Trait { } } - -export class Pictured extends decorate(PicturedBase) {} diff --git a/packages/graphics/traits/visible.trait.js b/packages/graphics/traits/visible.trait.js index fdc83bc..a1d2d0f 100644 --- a/packages/graphics/traits/visible.trait.js +++ b/packages/graphics/traits/visible.trait.js @@ -1,6 +1,7 @@ import {compose} from '@avocado/core'; import {StateProperty, Trait} from '@avocado/entity'; import {Rectangle, Vector} from '@avocado/math'; +import {Property} from '@avocado/mixins'; import {Container} from '../container'; import {hasGraphics} from '../has-graphics'; @@ -9,6 +10,13 @@ const decorate = compose( StateProperty('isVisible', { track: true, }), + Property('visibleBoundingBox', { + default: [0, 0, 0, 0], + emit: function (...args) { + this.entity.emit(...args); + }, + track: true, + }) ); export class Visible extends decorate(Trait) { @@ -30,8 +38,8 @@ export class Visible extends decorate(Trait) { this._container = new Container(); this._container.isVisible = this.entity.isVisible; } + this.scheduledBoundingBoxUpdate = true; this.trackPosition = this.params.get('trackPosition'); - this._visibleBoundingBox = [0, 0, 0, 0]; } destroy() { @@ -44,27 +52,6 @@ export class Visible extends decorate(Trait) { return this._container; } - get visibleBoundingBox() { - if (Rectangle.isNull(this._visibleBoundingBox)) { - // Collect all bounding boxes. - const visibleBoundingBoxes = this.entity.invokeHookFlat( - 'visibleBoundingBoxes' - ); - if (0 === visibleBoundingBoxes.length) { - return [0, 0, 0, 0]; - } - let unifiedBoundingBox = [0, 0, 0, 0]; - for (const visibleBoundingBox of visibleBoundingBoxes) { - unifiedBoundingBox = Rectangle.united( - unifiedBoundingBox, - visibleBoundingBox, - ); - } - this._visibleBoundingBox = unifiedBoundingBox; - } - return this._visibleBoundingBox; - } - shouldSynchronizePosition() { return this._container && this.trackPosition; } @@ -84,9 +71,9 @@ export class Visible extends decorate(Trait) { this.entity.container.visible = this.entity.isVisible; }, - visibleBoundingBoxesUpdated: () => { - this._visibleBoundingBox = [0, 0, 0, 0]; - } + positionChanged: () => { + this.scheduledBoundingBoxUpdate = true; + }, }; if (this.shouldSynchronizePosition()) { @@ -103,10 +90,40 @@ export class Visible extends decorate(Trait) { return listeners; } + methods() { + return { + + updateVisibleBoundingBox: () => { + this.scheduledBoundingBoxUpdate = true; + }, + + } + } + tick(elapsed) { if (this.shouldSynchronizePosition()) { this.synchronizePosition(); } + if (this.scheduledBoundingBoxUpdate) { + // Collect all bounding boxes. + const visibleBoundingBoxes = this.entity.invokeHookFlat( + 'visibleBoundingBoxes' + ); + if (0 === visibleBoundingBoxes.length) { + this.visibleBoundingBox = [0, 0, 0, 0]; + } + else { + let unifiedBoundingBox = [0, 0, 0, 0]; + for (const visibleBoundingBox of visibleBoundingBoxes) { + unifiedBoundingBox = Rectangle.united( + unifiedBoundingBox, + visibleBoundingBox, + ); + } + this.visibleBoundingBox = unifiedBoundingBox; + this.scheduledBoundingBoxUpdate = false; + } + } } } diff --git a/packages/timing/traits/animated.trait.js b/packages/timing/traits/animated.trait.js index 76b0163..a69387a 100644 --- a/packages/timing/traits/animated.trait.js +++ b/packages/timing/traits/animated.trait.js @@ -81,7 +81,7 @@ class AnimatedBase extends Trait { animation.direction = this.entity.direction; }); // Bounding box update. - this.entity.emit('visibleBoundingBoxesUpdated'); + this.entity.updateVisibleBoundingBox(); }); } @@ -167,7 +167,7 @@ class AnimatedBase extends Trait { oldAnimation.reset(); } // Bounding box update. - this.entity.emit('visibleBoundingBoxesUpdated'); + this.entity.updateVisibleBoundingBox(); // Only client/graphics. if (!this.animationViews) { return;