136 lines
3.3 KiB
JavaScript
136 lines
3.3 KiB
JavaScript
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();
|
|
}),
|
|
}
|
|
}
|
|
|
|
} |