UNPKG

@rpgjs/physic

Version:

A deterministic 2D top-down physics library for RPG, sandbox and MMO games

147 lines (146 loc) 4.56 kB
import { AABB } from "./index4.js"; import { createCollider } from "./index18.js"; import { raycastCollider } from "./index20.js"; class QuadNode { constructor(bounds) { this.items = []; this.children = null; this.bounds = bounds; } } class Quadtree { constructor(worldBounds, capacity = 8, maxDepth = 8) { this.entityMap = /* @__PURE__ */ new Map(); this.root = new QuadNode(worldBounds); this.capacity = capacity; this.maxDepth = maxDepth; } clear() { this.root = new QuadNode(this.root.bounds); this.entityMap.clear(); } insert(entity) { const collider = createCollider(entity); if (!collider) return; const bounds = collider.getBounds(); const item = { entity, bounds }; this.entityMap.set(entity, item); this.insertItem(this.root, item, 0); } remove(entity) { if (!this.entityMap.has(entity)) return; this.entityMap.delete(entity); const items = Array.from(this.entityMap.values()); this.clear(); for (const it of items) this.insertItem(this.root, it, 0); } update(entity) { const it = this.entityMap.get(entity); const collider = createCollider(entity); if (!collider) return; const newBounds = collider.getBounds(); if (!it) { this.entityMap.set(entity, { entity, bounds: newBounds }); this.insertItem(this.root, { entity, bounds: newBounds }, 0); return; } const b = it.bounds; if (b.minX !== newBounds.minX || b.minY !== newBounds.minY || b.maxX !== newBounds.maxX || b.maxY !== newBounds.maxY) { it.bounds = newBounds; const items = Array.from(this.entityMap.values()); this.clear(); for (const item of items) this.insertItem(this.root, item, 0); } } query(entity) { const collider = createCollider(entity); if (!collider) return /* @__PURE__ */ new Set(); const bounds = collider.getBounds(); const results = /* @__PURE__ */ new Set(); this.queryBounds(this.root, bounds, results); results.delete(entity); return results; } queryAABB(bounds) { const results = /* @__PURE__ */ new Set(); this.queryBounds(this.root, bounds, results); return results; } raycast(ray, mask, filter) { const origin = ray.origin; const end = ray.getPoint(ray.length); const bounds = new AABB( Math.min(origin.x, end.x), Math.min(origin.y, end.y), Math.max(origin.x, end.x), Math.max(origin.y, end.y) ); const candidates = this.queryAABB(bounds); let closestHit = null; for (const entity of candidates) { if (mask !== void 0 && (entity.collisionCategory & mask) === 0) continue; if (filter && !filter(entity)) continue; const collider = createCollider(entity); if (collider) { const hit = raycastCollider(collider, ray.origin, ray.direction, ray.length); if (hit) { if (!closestHit || hit.distance < closestHit.distance) { closestHit = hit; } } } } return closestHit; } insertItem(node, item, depth) { if (!node.children && (node.items.length < this.capacity || depth >= this.maxDepth)) { node.items.push(item); return; } if (!node.children) this.subdivide(node); const child = this.findChild(node, item.bounds); if (child) { this.insertItem(child, item, depth + 1); } else { node.items.push(item); } } queryBounds(node, bounds, out) { if (!node.bounds.intersects(bounds)) return; for (const it of node.items) { if (it.bounds.intersects(bounds)) out.add(it.entity); } if (node.children) { for (const c of node.children) this.queryBounds(c, bounds, out); } } subdivide(node) { const b = node.bounds; const midX = (b.minX + b.maxX) * 0.5; const midY = (b.minY + b.maxY) * 0.5; node.children = [ new QuadNode(new AABB(b.minX, b.minY, midX, midY)), // SW new QuadNode(new AABB(midX, b.minY, b.maxX, midY)), // SE new QuadNode(new AABB(b.minX, midY, midX, b.maxY)), // NW new QuadNode(new AABB(midX, midY, b.maxX, b.maxY)) // NE ]; } findChild(node, bounds) { if (!node.children) return null; for (const c of node.children) { if (containsAABB(c.bounds, bounds)) return c; } return null; } } function containsAABB(outer, inner) { return outer.minX <= inner.minX && outer.maxX >= inner.maxX && outer.minY <= inner.minY && outer.maxY >= inner.maxY; } export { Quadtree }; //# sourceMappingURL=index15.js.map