import {Composite, Bodies, Body as MatterBody, Vertices} from 'matter-js'; import {Vector} from '@avocado/math'; import {ShapeList, PolygonShape, RectangleShape} from '@avocado/physics'; import {AbstractBody} from '../abstract/body'; export class Body extends AbstractBody { static collisionCategory(group) { if (!('filterCategoryBit' in this)) { this.filterCategoryBit = 0; } if (!('filterCategories' in this)) { this.filterCategories = {}; } if (!this.filterCategories[group]) { this.filterCategories[group] = 1 << this.filterCategoryBit; this.filterCategoryBit += 1; } return this.filterCategories[group]; } static lookupBody(matterBody) { if (this.bodies) { return this.bodies.get(matterBody); } } static associateBody(matterBody, avocadoBody) { if (!this.bodies) { this.bodies = new Map(); } this.bodies.set(matterBody, avocadoBody); } constructor(shape) { super(shape); [this.origin, this.matterBody] = this.constructor.bodyFromShape(shape); this.constructor.associateBody(this.matterBody, this); } get aabb() { const bounds = this.matterBody.bounds; return [ bounds.min.x, bounds.min.y, bounds.max.x - bounds.min.x, bounds.max.y - bounds.min.y, ]; } applyForce(force) { MatterBody.applyForce( this.matterBody, this.matterBody.position, { x: force[0], y: force[1], } ); } applyImpulse(impulse, elapsed) { impulse = Vector.scale(impulse, elapsed); MatterBody.translate(this.matterBody, { x: impulse[0], y: impulse[1], }); } static bodyFromShape(shape) { if (shape instanceof RectangleShape) { const body = Bodies.rectangle( 0, 0, shape.width, shape.height, ); return [shape.position, body]; } else if (shape instanceof PolygonShape) { const vectors = []; for (const vertice of shape) { vectors.push({ x: vertice[0], y: vertice[1], }); } const centre = Vertices.centre(vectors); const body = Bodies.fromVertices( 0, 0, vectors, ); const origin = Vector.add( shape.position, Vector.scale([centre.x, centre.y], 1), ); return [origin, body]; } } get position() { return [ this.matterBody.position.x, this.matterBody.position.y, ]; } set position(position) { position = Vector.add(position, this.origin); if (Vector.equalsClose(this.position, position)) { return; } MatterBody.setPosition(this.matterBody, { x: position[0], y: position[1], }); } setCollision(category, mask, group = 0) { MatterBody.set(this.matterBody, 'collisionFilter', { category, group, mask, }); } setCollisionForEntity(entity) { if (entity.is('collider')) { const ctor = this.constructor; const category = ctor.collisionCategory(entity.collisionGroup); let mask = 0; for (const group of entity.collidesWithGroups) { mask = mask | ctor.collisionCategory(group); } this.setCollision(category, mask); } else { this.setCollision(0, 0, -1); } } get static() { return this.matterBody.isStatic; } set static(isStatic) { MatterBody.setStatic(this.matterBody, isStatic); } get vertices() { const vertices = []; for (const {x, y} of this.matterBody.vertices) { vertices.push([x, y]); } return vertices; } }