import {Vector} from '@avocado/math'; import {Shape} from './shape'; import {shapeFromJSON} from './index'; export class ShapeList extends Shape { constructor() { super(); this._shapes = []; this.on('shapesChanged', this.onShapesChanged, this); this.on('originChanged', this.onOriginChanged, this); this.on('rotationChanged', this.onRotationChanged, this); this.on('scaleChanged', this.onScaleChanged, this); } destroy() { super.destroy(); this.off('shapesChanged', this.onShapesChanged); this.off('originChanged', this.onOriginChanged); this.off('rotationChanged', this.onRotationChanged); this.off('scaleChanged', this.onScaleChanged); for (let i = 0; i < this._shapes.length; i++) { const shape = this._shapes[i]; shape.destroy(); } } *[Symbol.iterator]() { for (const shape of this._shapes) { yield shape; } } get aabb() { if (0 === this._shapes.length) { return [0, 0, 0, 0]; } const min = [Infinity, Infinity]; const max = [-Infinity, -Infinity]; for (const shape of this._shapes) { const aabb = shape.aabb; min[0] = aabb[0] < min[0] ? aabb[0] : min[0]; min[1] = aabb[1] < min[1] ? aabb[1] : min[1]; max[0] = aabb[0] > max[0] ? aabb[0] : max[0]; max[1] = aabb[1] > max[1] ? aabb[1] : max[1]; } return Rectangle.translated( [min[0], min[1], max[0] - min[0], max[1] - min[1]], this.position ); } addShape(shape) { const oldShapes = [...this._shapes]; this._shapes.push(shape); this.emit('shapesChanged', oldShapes, this._shapes); } fromJSON(json) { super.fromJSON(json); if (json.shapes) { const shapes = []; json.shapes.forEach((shape) => { shapes.push(shapeFromJSON(shape)); }); const oldShapes = [...this._shapes]; this._shapes = shapes; this.emit('shapesChanged', oldShapes, this._shapes); } return this; } intersects(list) { if (0 === this._shapes.length) { return false; } if (!Rectangle.intersects(this.aabb, list.aabb)) { return false; } // TODO: Quadtrees? for (const shape of this._shapes) { for (const otherShape of list._shapes) { if (Rectangle.intersects( Rectangle.translated(shape.aabb, this.position), Rectangle.translated(otherShape.aabb, otherShape.position) )) { return true; } } } return false; } onChildAabbChanged() { this.emit('aabbChanged'); } onOriginChanged(oldOrigin) { this._shapes.forEach((shape) => { shape.emit('parentOriginChanged', oldOrigin, this.origin); }); } onRotationChanged(oldRotation) { this._shapes.forEach((shape) => { shape.emit('parentRotationChanged', oldRotation, this.rotation); }); } onScaleChanged(oldScale) { this._shapes.forEach((shape) => { shape.emit('parentScaleChanged', oldScale, this.scale); }); } onShapesChanged(oldShapes) { oldShapes.forEach((shape) => { shape.off('aabbChanged'); }); this._shapes.forEach((shape) => { shape.on('aabbChanged', this.onChildAabbChanged, this); }); } toJSON() { return { ...super.toJSON(), type: 'list', shapes: this._shapes.map((shape) => { return shape.toJSON(); }), } } }