import {Composite, Bodies, Body as MatterBody, Bounds, Vertices} from 'matter-js'; import {Vector} from '@avocado/math'; import { ShapeList, CircleShape, PolygonShape, RectangleShape, } from '@avocado/physics'; import {AbstractBody} from '../abstract/body'; // Trick matter into not needing decomp for our convex polygons. const g = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : undefined; if (g) { g.decomp = require('poly-decomp'); } const SCALE = 1; 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); const [body, position, origin] = this.constructor.bodyFromShape(shape); this.matterBody = body; this.origin = origin; this.position = position; 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, ].map((p) => p * SCALE); } applyForce(force) { force = Vector.scale(force, 1 / SCALE); const [x, y] = force; MatterBody.applyForce(this.matterBody, this.matterBody.position, {x, y}); } applyImpulse(impulse, elapsed) { impulse = Vector.scale(impulse, 1 / SCALE); impulse = Vector.scale(impulse, elapsed); const [x, y] = impulse; MatterBody.translate(this.matterBody, {x, y}); } static bodyFromShape(shape) { const shapePosition = Vector.scale(shape.position, 1 / SCALE); if (shape instanceof ShapeList) { const children = []; for (const child of shape) { const [body, position, origin] = this.bodyFromShape(child); const [x, y] = Vector.add(Vector.scale(position, 1 / SCALE), origin); MatterBody.setPosition(body, {x, y}); children.push(body); } const body = MatterBody.create({parts: children}); const {x, y} = body.position; return [body, shapePosition, Vector.scale([x, y], SCALE)]; } else if (shape instanceof RectangleShape) { const [width, height] = Vector.scale(shape.size, 1 / SCALE); const body = Bodies.rectangle(0, 0, width, height); return [body, shapePosition, [0, 0]]; } else if (shape instanceof CircleShape) { const body = Bodies.circle(0, 0, shape.radius / SCALE); return [body, shapePosition, [0, 0]]; } else if (shape instanceof PolygonShape) { const vectors = []; for (let vertice of shape) { const[x, y] = Vector.scale(vertice, 1 / SCALE) vectors.push({x, y}); } const {x, y} = Vertices.centre(vectors); const body = Bodies.fromVertices(0, 0, vectors); return [body, shapePosition, [x, y]]; } } get position() { const {x, y} = this.matterBody.position; return Vector.scale(Vector.sub([x, y], this.origin), SCALE); } set position(position) { position = Vector.scale(position, 1 / SCALE); position = Vector.add(position, this.origin); if (Vector.equalsClose(this.position, position)) { return; } const [x, y] = position; MatterBody.setPosition(this.matterBody, {x, y}); } 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(Vector.scale([x, y], SCALE)); } return vertices; } }