@daneren2005/shared-memory-physics 1.9.1 → 1.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/constants.ts","../src/components/transform-component-constants.ts","../src/components/polygon-component-constants.ts","../src/components/polygon-component.ts","../src/components/transform-component.ts","../src/components/velocity-component-constants.ts","../src/components/velocity-component.ts","../src/components/dynamics-component-constants.ts","../src/components/dynamics-component.ts","../src/components/body-component-constants.ts","../src/components/body-component.ts","../src/components/bounciness-component-constants.ts","../src/components/bounciness-component.ts","../src/components/interpolation-component-constants.ts","../src/components/interpolation-component.ts","../src/components/navigation-component-constants.ts","../src/pathfinding/path-search.ts","../src/components/navigation-component.ts","../src/components/registry.ts","../src/math/shapes.ts","../src/systems/spatial-bounds.ts","../src/world.ts","../src/math/polygons.ts","../src/actions/dynamics-commands.ts","../src/actions/find-dynamics-command.ts","../src/actions/find-dynamics-command-offset.ts","../src/systems/collision.ts","../src/systems/spatial-index.ts","../src/actions/get-spatial-map.ts","../src/actions/update-spatial-map.ts","../src/actions/set-entity-position.ts","../src/actions/add-physical-world-data.ts","../src/actions/snap-entity.ts","../src/actions/start-spawn-interpolation.ts","../src/actions/integrate-dynamics.ts","../src/systems/bounce.ts","../src/actions/combine-dynamics-commands.ts","../src/actions/queue-dynamics-vector.ts","../src/actions/queue-velocity-assignment.ts","../src/systems/physics-update.ts","../src/systems/interpolation-channels.ts","../src/systems/physics-system.ts","../src/pathfinding/nav-grid.ts","../src/pathfinding/min-heap.ts","../src/pathfinding/line-of-sight.ts","../src/pathfinding/smooth-path.ts","../src/pathfinding/grid-a-star.ts","../src/pathfinding/follow-path.ts","../src/pathfinding/path-blockers.ts","../src/pathfinding/navigation-state.ts","../src/pathfinding/pathfinding-update.ts","../src/pathfinding/pathfinding-system.ts","../src/systems/interpolation-update.ts","../src/systems/interpolation-system.ts"],"sourcesContent":["// One event per completed run, carrying the ids of moved entities.\nexport const POSITION_UPDATED_EVENT = 'position-updated';\n\nexport const COLLIDABLE_QUERY = 'collidable';\nexport const DEFAULT_PHYSICS_STEP_MS = 50;\n\n// Backlog beyond this many steps is eased away instead of leaving a persistent render delay.\nexport const BACKLOG_TARGET_STEPS = 1.5;\n// Extra fraction of each frame's render budget available for catching up.\nexport const BACKLOG_CATCHUP_RATE = 0.5;\n","export const TRANSFORM_X_INDEX = 0;\nexport const TRANSFORM_Y_INDEX = 1;\nexport const TRANSFORM_WIDTH_INDEX = 2;\nexport const TRANSFORM_HEIGHT_INDEX = 3;\nexport const TRANSFORM_ANGLE_INDEX = 4;\nexport const TRANSFORM_SIZE = 5;\n","export const MAX_POLYGON_VERTICES = 16;\nexport const POLYGON_VERTEX_COUNT_INDEX = 0;\nexport const POLYGON_VERTICES_INDEX = 1;\nexport const POLYGON_SIZE = POLYGON_VERTICES_INDEX + MAX_POLYGON_VERTICES * 2;\n","import { MAX_POLYGON_VERTICES, POLYGON_VERTEX_COUNT_INDEX, POLYGON_SIZE } from './polygon-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\nexport type PolygonVertex = readonly [x: number, y: number];\n\nexport interface PolygonConfig {\n\tvertices?: ReadonlyArray<PolygonVertex>\n\twidth?: number\n\theight?: number\n}\n\nexport interface PolygonComponent {\n\tindex: number\n\treadonly block: Float32Array\n\treadonly vertexCount: number\n}\n\nexport interface PreparedPolygon {\n\twidth: number\n\theight: number\n\tvertices: Array<number>\n}\n\nclass PolygonComponentImpl extends Component<Float32Array> implements PolygonComponent {\n\tget vertexCount() {\n\t\treturn this.block[POLYGON_VERTEX_COUNT_INDEX];\n\t}\n}\n\nexport const polygonDefinition: ComponentDefinition<PolygonComponent, Float32Array, PolygonConfig> = {\n\ttype: Float32Array,\n\tsize: POLYGON_SIZE,\n\tloadProperties: ['vertices'],\n\ttoBlock(config) {\n\t\tconst prepared = preparePolygon(config.vertices, config.width, config.height);\n\n\t\treturn [prepared.vertices.length / 2, ...prepared.vertices];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new PolygonComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\n// With a width and height, the vertices are offsets from the entity's centre that must fit inside that box, so the\n// outline can sit tighter than the box a game draws (and sizes) the entity by. Without, the vertices' bounds are the box.\nexport function preparePolygon(vertices: ReadonlyArray<PolygonVertex> | undefined, boxWidth?: number, boxHeight?: number): PreparedPolygon {\n\tif(!vertices || vertices.length < 3 || vertices.length > MAX_POLYGON_VERTICES) {\n\t\tthrow new Error(`A polygon needs between 3 and ${MAX_POLYGON_VERTICES} vertices`);\n\t}\n\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\tfor(const [x, y] of vertices) {\n\t\tif(!Number.isFinite(x) || !Number.isFinite(y)) {\n\t\t\tthrow new Error('Polygon vertices must be finite numbers');\n\t\t}\n\t\tminX = Math.min(minX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxX = Math.max(maxX, x);\n\t\tmaxY = Math.max(maxY, y);\n\t}\n\n\tconst width = maxX - minX;\n\tconst height = maxY - minY;\n\tif(width <= 0 || height <= 0) {\n\t\tthrow new Error('A polygon must have nonzero width and height');\n\t}\n\n\tlet winding = 0;\n\tfor(let i = 0; i < vertices.length; i++) {\n\t\tconst previous = vertices[(i + vertices.length - 1) % vertices.length];\n\t\tconst current = vertices[i];\n\t\tconst next = vertices[(i + 1) % vertices.length];\n\t\tconst turnCross = (current[0] - previous[0]) * (next[1] - current[1])\n\t\t\t- (current[1] - previous[1]) * (next[0] - current[0]);\n\t\tif(turnCross === 0) {\n\t\t\tthrow new Error('A polygon cannot contain duplicate or collinear adjacent vertices');\n\t\t}\n\t\tconst direction = Math.sign(turnCross);\n\t\tif(winding === 0) {\n\t\t\twinding = direction;\n\t\t} else if(direction !== winding) {\n\t\t\tthrow new Error('Polygon vertices must describe a convex polygon in boundary order');\n\t\t}\n\t}\n\tfor(let i = 0; i < vertices.length; i++) {\n\t\tconst aStart = vertices[i];\n\t\tconst aEnd = vertices[(i + 1) % vertices.length];\n\t\tfor(let j = i + 1; j < vertices.length; j++) {\n\t\t\tif(j === i || j === i + 1 || i === 0 && j === vertices.length - 1) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst bStart = vertices[j];\n\t\t\tconst bEnd = vertices[(j + 1) % vertices.length];\n\t\t\tif(segmentsIntersect(aStart, aEnd, bStart, bEnd)) {\n\t\t\t\tthrow new Error('Polygon edges cannot cross or share non-adjacent vertices');\n\t\t\t}\n\t\t}\n\t}\n\n\tif((boxWidth === undefined) !== (boxHeight === undefined)) {\n\t\tthrow new Error('A sized polygon needs both a width and a height');\n\t}\n\tconst sized = boxWidth !== undefined && boxHeight !== undefined;\n\tif(sized) {\n\t\tif(!(boxWidth > 0) || !(boxHeight > 0)) {\n\t\t\tthrow new Error('A sized polygon needs a positive width and height');\n\t\t} else if(minX < -boxWidth / 2 || maxX > boxWidth / 2 || minY < -boxHeight / 2 || maxY > boxHeight / 2) {\n\t\t\t// The broadphase indexes the entity by its box, so an outline poking out of it would miss contacts.\n\t\t\tthrow new Error('Polygon vertices must fit inside the width and height they are given with');\n\t\t}\n\t}\n\n\tconst outputWidth = sized ? boxWidth : width;\n\tconst outputHeight = sized ? boxHeight : height;\n\tconst centreX = sized ? 0 : (minX + maxX) / 2;\n\tconst centreY = sized ? 0 : (minY + maxY) / 2;\n\tconst normalized: Array<number> = [];\n\tconst ordered = winding > 0 ? vertices : vertices.toReversed();\n\tfor(const [x, y] of ordered) {\n\t\tnormalized.push((x - centreX) / outputWidth, (y - centreY) / outputHeight);\n\t}\n\n\treturn { width: outputWidth, height: outputHeight, vertices: normalized };\n}\n\nfunction segmentsIntersect(aStart: PolygonVertex, aEnd: PolygonVertex, bStart: PolygonVertex, bEnd: PolygonVertex): boolean {\n\tconst aToStart = cross(aStart, aEnd, bStart);\n\tconst aToEnd = cross(aStart, aEnd, bEnd);\n\tconst bToStart = cross(bStart, bEnd, aStart);\n\tconst bToEnd = cross(bStart, bEnd, aEnd);\n\n\treturn aToStart * aToEnd <= 0 && bToStart * bToEnd <= 0;\n}\n\nfunction cross(start: PolygonVertex, end: PolygonVertex, point: PolygonVertex): number {\n\treturn (end[0] - start[0]) * (point[1] - start[1]) - (end[1] - start[1]) * (point[0] - start[0]);\n}\n\nexport default polygonDefinition;\n","import {\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_ANGLE_INDEX,\n\tTRANSFORM_SIZE,\n} from './transform-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\nimport { preparePolygon, type PolygonVertex } from './polygon-component';\n\n// Where an entity is, how big it is, and which way it faces: a position, its box, and the box's rotation.\nexport interface TransformComponent {\n\tindex: number\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n\tangle: number\n}\n\n// The defining half of a transform, from the entity template: the size that gives an entity a transform at all\n// (a config with no size does not load one). `width`/`height` are the unrotated box and required, since a\n// zero-area box never overlaps anything. `angle` is radians counter-clockwise from +x, the game's to set. A\n// round entity may give a `radius` instead, loaded as a width and height of its diameter; both at once throws.\nexport interface TransformConfig {\n\twidth: number\n\theight: number\n\tangle?: number\n\tradius?: number\n\t// A convex outline; alone its bounds become width/height, alongside them it must fit inside that box.\n\tvertices?: ReadonlyArray<PolygonVertex>\n}\n// The live runtime half: the physics system moves the position and can turn the box every run, so both round-trip\n// through `save`. Size comes back from the template. `x`/`y` are the centre of the box, not a corner; `angle` is\n// radians counter-clockwise from +x.\nexport interface TransformSerialization {\n\tx: number\n\ty: number\n\tangle?: number\n}\n\nclass TransformComponentImpl extends Component<Float32Array> implements TransformComponent {\n\tget x() {\n\t\treturn this.block[TRANSFORM_X_INDEX];\n\t}\n\tset x(value: number) {\n\t\tthis.block[TRANSFORM_X_INDEX] = value;\n\t}\n\tget y() {\n\t\treturn this.block[TRANSFORM_Y_INDEX];\n\t}\n\tset y(value: number) {\n\t\tthis.block[TRANSFORM_Y_INDEX] = value;\n\t}\n\tget width() {\n\t\treturn this.block[TRANSFORM_WIDTH_INDEX];\n\t}\n\tset width(value: number) {\n\t\tthis.block[TRANSFORM_WIDTH_INDEX] = value;\n\t}\n\tget height() {\n\t\treturn this.block[TRANSFORM_HEIGHT_INDEX];\n\t}\n\tset height(value: number) {\n\t\tthis.block[TRANSFORM_HEIGHT_INDEX] = value;\n\t}\n\tget angle() {\n\t\treturn this.block[TRANSFORM_ANGLE_INDEX];\n\t}\n\tset angle(value: number) {\n\t\tthis.block[TRANSFORM_ANGLE_INDEX] = value;\n\t}\n}\n\nexport const transformDefinition: ComponentDefinition<TransformComponent, Float32Array, TransformConfig, TransformSerialization> = {\n\ttype: Float32Array,\n\tsize: TRANSFORM_SIZE,\n\t// A size marks a config as having a transform. x/y are not listed: they come off a save, not the template.\n\tloadProperties: ['width', 'height', 'radius', 'vertices'],\n\ttoBlock(config) {\n\t\t// A radius is the same size said differently; turn it into width/height so nothing downstream cares. Both\n\t\t// at once cannot be honoured, so it throws rather than picking a winner.\n\t\tconst radius = config.radius;\n\t\tconst vertices = config.vertices;\n\t\tlet width = config.width;\n\t\tlet height = config.height;\n\t\tif(vertices !== undefined) {\n\t\t\tif(radius !== undefined) {\n\t\t\t\tthrow new Error('A transform takes polygon vertices or a radius, not both');\n\t\t\t}\n\n\t\t\tconst prepared = preparePolygon(vertices, width, height);\n\t\t\twidth = prepared.width;\n\t\t\theight = prepared.height;\n\t\t}\n\t\tif(radius !== undefined) {\n\t\t\tif(width !== undefined || height !== undefined) {\n\t\t\t\tthrow new Error('A transform takes either a radius or a width and height, not both');\n\t\t\t}\n\n\t\t\twidth = height = radius * 2;\n\t\t}\n\n\t\treturn [config.x, config.y, width, height, config.angle ?? 0];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new TransformComponentImpl(memory.getBlock(index), index);\n\t},\n\tsave(component) {\n\t\tconst saved: TransformSerialization = {\n\t\t\tx: component.x,\n\t\t\ty: component.y,\n\t\t};\n\t\tconst angle = component.angle;\n\t\tif(angle !== 0) {\n\t\t\tsaved.angle = angle;\n\t\t}\n\n\t\treturn saved;\n\t},\n};\n\nexport default transformDefinition;\n","export const VELOCITY_X_INDEX = 0;\nexport const VELOCITY_Y_INDEX = 1;\nexport const VELOCITY_DAMPING_INDEX = 2;\nexport const VELOCITY_SIZE = 3;\n","import {\n\tVELOCITY_X_INDEX,\n\tVELOCITY_Y_INDEX,\n\tVELOCITY_DAMPING_INDEX,\n\tVELOCITY_SIZE,\n} from './velocity-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\n// How fast an entity moves, in world units per second per axis. An entity with a transform and a velocity is\n// moved by the physics system every run. `damping` bleeds speed off each step so a mover coasts to a stop.\nexport interface VelocityComponent {\n\tindex: number\n\tvelocityX: number\n\tvelocityY: number\n\tdamping: number\n}\n\n// Named per-axis, not `x`/`y`, because configs are flat and `x`/`y` belong to the transform. Either axis alone\n// gives an entity a velocity, so both are optional and a missing one starts at 0.\nexport interface VelocityConfig {\n\tvelocityX?: number\n\tvelocityY?: number\n\t// Linear damping per second: the fraction of speed shed each second, applied as 1/(1+damping*dt) so it stays\n\t// stable at any step and never reverses. 0 (default) coasts forever; higher values stop sooner.\n\tdamping?: number\n}\n// Both spawn config and live state (a game's systems apply forces), so it round-trips through `save`.\nexport type VelocitySerialization = VelocityConfig;\n\nclass VelocityComponentImpl extends Component<Float32Array> implements VelocityComponent {\n\tget velocityX() {\n\t\treturn this.block[VELOCITY_X_INDEX];\n\t}\n\tset velocityX(value: number) {\n\t\tthis.block[VELOCITY_X_INDEX] = value;\n\t}\n\tget velocityY() {\n\t\treturn this.block[VELOCITY_Y_INDEX];\n\t}\n\tset velocityY(value: number) {\n\t\tthis.block[VELOCITY_Y_INDEX] = value;\n\t}\n\tget damping() {\n\t\treturn this.block[VELOCITY_DAMPING_INDEX];\n\t}\n\tset damping(value: number) {\n\t\tassertDamping(value);\n\t\tthis.block[VELOCITY_DAMPING_INDEX] = value;\n\t}\n}\n\nexport const velocityDefinition: ComponentDefinition<VelocityComponent, Float32Array, VelocityConfig, VelocitySerialization> = {\n\ttype: Float32Array,\n\tsize: VELOCITY_SIZE,\n\tloadProperties: ['velocityX', 'velocityY', 'damping'],\n\ttoBlock(config) {\n\t\tconst damping = config.damping ?? 0;\n\t\tassertDamping(damping);\n\n\t\treturn [config.velocityX ?? 0, config.velocityY ?? 0, damping];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new VelocityComponentImpl(memory.getBlock(index), index);\n\t},\n\tsave(component) {\n\t\treturn {\n\t\t\tvelocityX: component.velocityX,\n\t\t\tvelocityY: component.velocityY,\n\t\t\tdamping: component.damping,\n\t\t};\n\t},\n};\n\nfunction assertDamping(value: number): void {\n\tif(!Number.isFinite(value) || value < 0) {\n\t\tthrow new Error('Damping must be a finite number of at least zero');\n\t}\n}\n\nexport default velocityDefinition;\n","export const DYNAMICS_ACCELERATION_X_INDEX = 0;\nexport const DYNAMICS_ACCELERATION_Y_INDEX = 1;\nexport const DYNAMICS_INVERSE_MASS_INDEX = 2;\nexport const DYNAMICS_SIZE = 3;\n","import {\n\tDYNAMICS_ACCELERATION_X_INDEX,\n\tDYNAMICS_ACCELERATION_Y_INDEX,\n\tDYNAMICS_INVERSE_MASS_INDEX,\n\tDYNAMICS_SIZE,\n} from './dynamics-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\nexport interface DynamicsComponent {\n\tindex: number\n\taccelerationX: number\n\taccelerationY: number\n\tmass: number\n}\n\nexport interface DynamicsConfig {\n\taccelerationX?: number\n\taccelerationY?: number\n\tmass?: number\n}\n\nexport type DynamicsSerialization = DynamicsConfig;\n\nclass DynamicsComponentImpl extends Component<Float32Array> implements DynamicsComponent {\n\tget accelerationX() {\n\t\treturn this.block[DYNAMICS_ACCELERATION_X_INDEX];\n\t}\n\tset accelerationX(value: number) {\n\t\tassertFinite(value, 'Acceleration');\n\t\tthis.block[DYNAMICS_ACCELERATION_X_INDEX] = value;\n\t}\n\tget accelerationY() {\n\t\treturn this.block[DYNAMICS_ACCELERATION_Y_INDEX];\n\t}\n\tset accelerationY(value: number) {\n\t\tassertFinite(value, 'Acceleration');\n\t\tthis.block[DYNAMICS_ACCELERATION_Y_INDEX] = value;\n\t}\n\tget mass() {\n\t\treturn 1 / this.block[DYNAMICS_INVERSE_MASS_INDEX];\n\t}\n\tset mass(value: number) {\n\t\tassertMass(value);\n\t\tthis.block[DYNAMICS_INVERSE_MASS_INDEX] = 1 / value;\n\t}\n}\n\nexport const dynamicsDefinition: ComponentDefinition<DynamicsComponent, Float32Array, DynamicsConfig, DynamicsSerialization> = {\n\ttype: Float32Array,\n\tsize: DYNAMICS_SIZE,\n\tloadProperties: ['accelerationX', 'accelerationY', 'mass'],\n\ttoBlock(config) {\n\t\tconst accelerationX = config.accelerationX ?? 0;\n\t\tconst accelerationY = config.accelerationY ?? 0;\n\t\tconst mass = config.mass ?? 1;\n\t\tassertFinite(accelerationX, 'Acceleration');\n\t\tassertFinite(accelerationY, 'Acceleration');\n\t\tassertMass(mass);\n\n\t\treturn [accelerationX, accelerationY, 1 / mass];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new DynamicsComponentImpl(memory.getBlock(index), index);\n\t},\n\tsave(component) {\n\t\treturn {\n\t\t\taccelerationX: component.accelerationX,\n\t\t\taccelerationY: component.accelerationY,\n\t\t\tmass: component.mass,\n\t\t};\n\t},\n};\n\nfunction assertMass(value: number): void {\n\tif(!Number.isFinite(value) || value <= 0) {\n\t\tthrow new Error('Mass must be a finite number greater than zero');\n\t}\n}\n\nfunction assertFinite(value: number, name: string): void {\n\tif(!Number.isFinite(value)) {\n\t\tthrow new Error(`${name} must be a finite number`);\n\t}\n}\n\nexport default dynamicsDefinition;\n","export const SHAPE_RECTANGLE = 1;\nexport const SHAPE_CIRCLE = 2;\nexport const SHAPE_CAPSULE = 3;\nexport const SHAPE_POLYGON = 4;\nexport const DEFAULT_COLLIDE_CATEGORY = 1;\nexport const DEFAULT_COLLIDE_MASK = 0xFFFFFFFF;\nexport const BODY_FLAGS_INDEX = 0;\nexport const BODY_CATEGORY_INDEX = 1;\nexport const BODY_MASK_INDEX = 2;\nexport const BODY_SIZE = 3;\n// Shape occupies the low three bits; boolean flags occupy the remaining bits.\nexport const BODY_SHAPE_MASK = 0b111;\nexport const BODY_SENSOR_FLAG = 0b1000;\nexport const BODY_CCD_FLAG = 0b10000;\n// Runtime-only flag: defer removal until the final interpolation segment has played.\nexport const BODY_DYING_FLAG = 0b100000;\nexport const BODY_BLOCKS_PATH_FLAG = 0b1000000;\n","import {\n\tSHAPE_RECTANGLE,\n\tSHAPE_CIRCLE,\n\tSHAPE_CAPSULE,\n\tSHAPE_POLYGON,\n\tDEFAULT_COLLIDE_CATEGORY,\n\tDEFAULT_COLLIDE_MASK,\n\tBODY_FLAGS_INDEX,\n\tBODY_CATEGORY_INDEX,\n\tBODY_MASK_INDEX,\n\tBODY_SIZE,\n\tBODY_SHAPE_MASK,\n\tBODY_SENSOR_FLAG,\n\tBODY_CCD_FLAG,\n\tBODY_DYING_FLAG,\n\tBODY_BLOCKS_PATH_FLAG,\n} from './body-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\nimport type { PolygonVertex } from './polygon-component';\n\n// What an entity collides as and what it collides with. A body is loaded for anything with a size, so the way\n// to opt out is `collideCategory: 0`, not leaving the component off. Categories are the game's to name; the\n// library only reads them as bits.\nexport interface BodyComponent {\n\tindex: number\n\tshape: number\n\tcollideCategory: number\n\tcollideMask: number\n\t// A sensor takes part in detection - a mover overlapping it still gets its onCollision - but stops nothing:\n\t// nothing is swept short against it or bounces off it. Only about the response; it obeys canCollide unchanged.\n\tsensor: boolean\n\t// When set, this body's own move is tested along its whole path this run rather than only where it ends.\n\tcontinuousCollisionDetection: boolean\n\t// A hint for pathfinding to route around this body. Physics never reads it; it assumes the body never moves.\n\tblocksPath: boolean\n}\n\nconst KNOWN_SHAPES = [SHAPE_RECTANGLE, SHAPE_CIRCLE, SHAPE_CAPSULE, SHAPE_POLYGON];\n\n// Whether two bodies collide: each side's mask must accept the other's category. Symmetric by design, so the\n// outcome does not depend on which entity moved first; a one-sided response is done by checking `other` inside\n// onCollision. `collideCategory: 0` collides with nothing. `&` is signed, so compare against 0, not > 0.\nexport function canCollide(categoryA: number, maskA: number, categoryB: number, maskB: number): boolean {\n\treturn (maskA & categoryB) !== 0 && (maskB & categoryA) !== 0;\n}\n\n// A size loads a body, as it loads a transform: anything with a place in the world is collidable by default,\n// and the collide properties narrow that. All defining config from the entity template, so there is no `save`.\nexport interface BodyConfig {\n\t// Not read by the loader, only listed in `loadProperties`: they mark a config as having a place in the world.\n\twidth?: number\n\theight?: number\n\n\t// One of the SHAPE_ constants. Defaults to a circle for a `radius` config and a rectangle otherwise, so only\n\t// capsules name their shape.\n\tshape?: number\n\t// Not read here - the transform turns it into width/height - but a `radius` config decides the default shape.\n\tradius?: number\n\tvertices?: ReadonlyArray<PolygonVertex>\n\t// The bits this entity collides as, and the bits it collides with. See canCollide.\n\tcollideCategory?: number\n\tcollideMask?: number\n\t// Makes the body a sensor: found and reported, but never blocking or bounced off. Defaults to solid.\n\tsensor?: boolean\n\tcontinuousCollisionDetection?: boolean\n\t// Marks the body as something pathfinding routes around. Defaults to false.\n\tblocksPath?: boolean\n}\n\n// Readers off a raw body block, exported so the broadphase, bounce and game systems unpack the flags the same way.\nexport function bodyShape(body: Uint32Array): number {\n\treturn body[BODY_FLAGS_INDEX] & BODY_SHAPE_MASK;\n}\nexport function isSensor(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_SENSOR_FLAG) !== 0;\n}\nexport function isContinuous(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_CCD_FLAG) !== 0;\n}\nexport function blocksPath(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_BLOCKS_PATH_FLAG) !== 0;\n}\nexport function isDying(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_DYING_FLAG) !== 0;\n}\n// Sets the dying flag. One-way for the entity's remaining life - nothing clears it, since a dying entity is gone\n// a run later.\nexport function markDying(body: Uint32Array): void {\n\tbody[BODY_FLAGS_INDEX] |= BODY_DYING_FLAG;\n}\n\nclass BodyComponentImpl extends Component<Uint32Array> implements BodyComponent {\n\tget shape() {\n\t\treturn this.block[BODY_FLAGS_INDEX] & BODY_SHAPE_MASK;\n\t}\n\tset shape(value: number) {\n\t\tthis.block[BODY_FLAGS_INDEX] = (this.block[BODY_FLAGS_INDEX] & ~BODY_SHAPE_MASK) | (value & BODY_SHAPE_MASK);\n\t}\n\tget collideCategory() {\n\t\treturn this.block[BODY_CATEGORY_INDEX];\n\t}\n\tset collideCategory(value: number) {\n\t\tthis.block[BODY_CATEGORY_INDEX] = value;\n\t}\n\tget collideMask() {\n\t\treturn this.block[BODY_MASK_INDEX];\n\t}\n\tset collideMask(value: number) {\n\t\tthis.block[BODY_MASK_INDEX] = value;\n\t}\n\tget sensor() {\n\t\treturn (this.block[BODY_FLAGS_INDEX] & BODY_SENSOR_FLAG) !== 0;\n\t}\n\tset sensor(value: boolean) {\n\t\tthis.block[BODY_FLAGS_INDEX] = value ? (this.block[BODY_FLAGS_INDEX] | BODY_SENSOR_FLAG) : (this.block[BODY_FLAGS_INDEX] & ~BODY_SENSOR_FLAG);\n\t}\n\tget continuousCollisionDetection() {\n\t\treturn (this.block[BODY_FLAGS_INDEX] & BODY_CCD_FLAG) !== 0;\n\t}\n\tset continuousCollisionDetection(value: boolean) {\n\t\tthis.block[BODY_FLAGS_INDEX] = value ? (this.block[BODY_FLAGS_INDEX] | BODY_CCD_FLAG) : (this.block[BODY_FLAGS_INDEX] & ~BODY_CCD_FLAG);\n\t}\n\tget blocksPath() {\n\t\treturn (this.block[BODY_FLAGS_INDEX] & BODY_BLOCKS_PATH_FLAG) !== 0;\n\t}\n\tset blocksPath(value: boolean) {\n\t\tthis.block[BODY_FLAGS_INDEX] = value ? (this.block[BODY_FLAGS_INDEX] | BODY_BLOCKS_PATH_FLAG) : (this.block[BODY_FLAGS_INDEX] & ~BODY_BLOCKS_PATH_FLAG);\n\t}\n}\n\nexport const bodyDefinition: ComponentDefinition<BodyComponent, Uint32Array, BodyConfig> = {\n\ttype: Uint32Array,\n\tsize: BODY_SIZE,\n\tloadProperties: ['width', 'height', 'radius', 'vertices', 'shape', 'collideCategory', 'collideMask', 'sensor', 'continuousCollisionDetection', 'blocksPath'],\n\ttoBlock(config) {\n\t\treturn [\n\t\t\ttoFlags(config),\n\t\t\tconfig.collideCategory ?? DEFAULT_COLLIDE_CATEGORY,\n\t\t\tconfig.collideMask ?? DEFAULT_COLLIDE_MASK,\n\t\t];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new BodyComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\n// Packs the config's shape and boolean flags into the single flags word stored at BODY_FLAGS_INDEX.\nfunction toFlags(config: BodyConfig): number {\n\tlet flags = toShape(config);\n\tif(config.sensor) {\n\t\tflags |= BODY_SENSOR_FLAG;\n\t}\n\tif(config.continuousCollisionDetection) {\n\t\tflags |= BODY_CCD_FLAG;\n\t}\n\tif(config.blocksPath) {\n\t\tflags |= BODY_BLOCKS_PATH_FLAG;\n\t}\n\n\treturn flags;\n}\n\n// An unknown shape throws rather than defaulting: colliding with the wrong outline is harder to spot than a\n// failed load. A config that names no shape is read off its size - `radius` is a circle, anything else a rectangle.\nfunction toShape(config: BodyConfig): number {\n\tconst shape = config.shape;\n\tif(shape === undefined) {\n\t\treturn config.vertices !== undefined ? SHAPE_POLYGON : config.radius !== undefined ? SHAPE_CIRCLE : SHAPE_RECTANGLE;\n\t} else if(!KNOWN_SHAPES.includes(shape)) {\n\t\tthrow new Error(`Unknown body shape: ${shape}`);\n\t}\n\tif(shape === SHAPE_POLYGON && config.vertices === undefined) {\n\t\tthrow new Error('A polygon body requires vertices');\n\t}\n\n\treturn shape;\n}\n\nexport default bodyDefinition;\n","export const BOUNCINESS_INDEX = 0;\nexport const BOUNCINESS_SIZE = 1;\n","import { BOUNCINESS_INDEX, BOUNCINESS_SIZE } from './bounciness-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\n// How much speed an entity keeps when it bounces, as a fraction of the velocity that pointed into what it hit:\n// 1 is a perfect bounce, 0.5 loses half on each, 0 does not turn around at all. See the bounce in\n// createPhysicsUpdate. Only bouncing entities carry this; one without it is still stopped by what it runs into\n// (the sweep does that for everything), it just does not rebound.\nexport interface BouncinessComponent {\n\tindex: number\n\tbounciness: number\n}\n\n// A `bounciness` config loads the component. Defining config from the entity template, so there is no `save`.\nexport interface BouncinessConfig {\n\tbounciness?: number\n}\n\nclass BouncinessComponentImpl extends Component<Float32Array> implements BouncinessComponent {\n\tget bounciness() {\n\t\treturn this.block[BOUNCINESS_INDEX];\n\t}\n\tset bounciness(value: number) {\n\t\tthis.block[BOUNCINESS_INDEX] = value;\n\t}\n}\n\nexport const bouncinessDefinition: ComponentDefinition<BouncinessComponent, Float32Array, BouncinessConfig> = {\n\ttype: Float32Array,\n\tsize: BOUNCINESS_SIZE,\n\tloadProperties: ['bounciness'],\n\ttoBlock(config) {\n\t\treturn [config.bounciness ?? 0];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new BouncinessComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\nexport default bouncinessDefinition;\n","export const INTERPOLATION_X_INDEX = 0;\nexport const INTERPOLATION_Y_INDEX = 1;\nexport const INTERPOLATION_SYNCED_TICK_INDEX = 2;\nexport const INTERPOLATION_DURATION_INDEX = 3;\n// Published last; NaN marks an in-progress transform and timing update.\nexport const INTERPOLATION_TICK_INDEX = 4;\n// Cumulative time preserves segments replaced before the renderer observes them.\nexport const INTERPOLATION_TOTAL_DURATION_INDEX = 5;\nexport const INTERPOLATION_SYNCED_DURATION_INDEX = 6;\nexport const INTERPOLATION_REMAINING_DURATION_INDEX = 7;\n// Consumer-owned target used while physics publishes the next segment.\nexport const INTERPOLATION_TARGET_X_INDEX = 8;\nexport const INTERPOLATION_TARGET_Y_INDEX = 9;\nexport const INTERPOLATION_SYNCED_STEP_DURATION_INDEX = 10;\n// Channel 0 is ungated; other channels wait for their PhysicsSystem to commit.\nexport const INTERPOLATION_CHANNEL_INDEX = 11;\n// Previous publication remains readable until the current run commits.\nexport const INTERPOLATION_PREVIOUS_X_INDEX = 12;\nexport const INTERPOLATION_PREVIOUS_Y_INDEX = 13;\nexport const INTERPOLATION_PREVIOUS_DURATION_INDEX = 14;\nexport const INTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX = 15;\n// Previous-slot stamp follows the same NaN-while-writing protocol.\nexport const INTERPOLATION_PREVIOUS_TICK_INDEX = 16;\nexport const INTERPOLATION_SIZE = 17;\n","import {\n\tINTERPOLATION_X_INDEX,\n\tINTERPOLATION_Y_INDEX,\n\tINTERPOLATION_SYNCED_TICK_INDEX,\n\tINTERPOLATION_DURATION_INDEX,\n\tINTERPOLATION_TICK_INDEX,\n\tINTERPOLATION_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_SYNCED_DURATION_INDEX,\n\tINTERPOLATION_REMAINING_DURATION_INDEX,\n\tINTERPOLATION_TARGET_X_INDEX,\n\tINTERPOLATION_TARGET_Y_INDEX,\n\tINTERPOLATION_SYNCED_STEP_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_X_INDEX,\n\tINTERPOLATION_PREVIOUS_Y_INDEX,\n\tINTERPOLATION_SIZE,\n} from './interpolation-component-constants';\nimport { loadFloat32, storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\n// Where an entity should be drawn, which diverges from where it is once physics runs less often than the screen\n// refreshes: a 50ms step against a 16ms frame updates the transform one frame in three, so drawing straight off\n// it stutters. This block holds a position that changes every frame instead.\n//\n// Filled by InterpolationSystem by advancing the render position toward its last coherent physics target.\n// The renderer consumes published simulation time without extrapolating past that target. Nothing in the\n// simulation reads it back - collision, the sweep and the spatial index all use the transform - which also keeps\n// it safe under lockstep multiplayer, where different frame rates would otherwise read different numbers.\nexport interface InterpolationComponent {\n\tindex: number\n\t// The render position, rewritten every frame by InterpolationSystem. A game reads it, and writes it only\n\t// through `snapEntity` after a teleport.\n\tx: number\n\ty: number\n\t// Publication timing and consumer-owned render bookkeeping.\n\tsyncedTick: number\n\tduration: number\n\ttick: number\n\ttotalDuration: number\n\tsyncedDuration: number\n\tremainingDuration: number\n\ttargetX: number\n\ttargetY: number\n\tsyncedStepDuration: number\n\tpreviousX: number\n\tpreviousY: number\n}\n\n// `interpolate` gives an entity this component, opt-in rather than automatic because it is 68 bytes and a\n// per-frame visit for something a game may never draw. `x`/`y` come off the same config the transform loads\n// from, so a render position starts where the entity spawned.\nexport interface InterpolationConfig {\n\tinterpolate?: boolean\n\tx?: number\n\ty?: number\n}\n\nclass InterpolationComponentImpl extends Component<Float32Array> implements InterpolationComponent {\n\tget x() {\n\t\treturn this.block[INTERPOLATION_X_INDEX];\n\t}\n\tset x(value: number) {\n\t\tthis.block[INTERPOLATION_X_INDEX] = value;\n\t}\n\tget y() {\n\t\treturn this.block[INTERPOLATION_Y_INDEX];\n\t}\n\tset y(value: number) {\n\t\tthis.block[INTERPOLATION_Y_INDEX] = value;\n\t}\n\tget syncedTick() {\n\t\treturn this.block[INTERPOLATION_SYNCED_TICK_INDEX];\n\t}\n\tset syncedTick(value: number) {\n\t\tthis.block[INTERPOLATION_SYNCED_TICK_INDEX] = value;\n\t}\n\tget duration() {\n\t\treturn this.block[INTERPOLATION_DURATION_INDEX];\n\t}\n\tset duration(value: number) {\n\t\tthis.block[INTERPOLATION_DURATION_INDEX] = value;\n\t}\n\t// The stamp everything else is published under, so it is read and written atomically like the physics update\n\t// does: a release store paired with the acquire load the interpolation update reads it through.\n\tget tick() {\n\t\treturn loadFloat32(this.block, INTERPOLATION_TICK_INDEX);\n\t}\n\tset tick(value: number) {\n\t\tstoreFloat32(this.block, INTERPOLATION_TICK_INDEX, value);\n\t}\n\tget totalDuration() {\n\t\treturn this.block[INTERPOLATION_TOTAL_DURATION_INDEX];\n\t}\n\tset totalDuration(value: number) {\n\t\tthis.block[INTERPOLATION_TOTAL_DURATION_INDEX] = value;\n\t}\n\tget syncedDuration() {\n\t\treturn this.block[INTERPOLATION_SYNCED_DURATION_INDEX];\n\t}\n\tset syncedDuration(value: number) {\n\t\tthis.block[INTERPOLATION_SYNCED_DURATION_INDEX] = value;\n\t}\n\tget targetX() {\n\t\treturn this.block[INTERPOLATION_TARGET_X_INDEX];\n\t}\n\tset targetX(value: number) {\n\t\tthis.block[INTERPOLATION_TARGET_X_INDEX] = value;\n\t}\n\tget targetY() {\n\t\treturn this.block[INTERPOLATION_TARGET_Y_INDEX];\n\t}\n\tset targetY(value: number) {\n\t\tthis.block[INTERPOLATION_TARGET_Y_INDEX] = value;\n\t}\n\tget syncedStepDuration() {\n\t\treturn this.block[INTERPOLATION_SYNCED_STEP_DURATION_INDEX];\n\t}\n\tset syncedStepDuration(value: number) {\n\t\tthis.block[INTERPOLATION_SYNCED_STEP_DURATION_INDEX] = value;\n\t}\n\tget previousX() {\n\t\treturn this.block[INTERPOLATION_PREVIOUS_X_INDEX];\n\t}\n\tset previousX(value: number) {\n\t\tthis.block[INTERPOLATION_PREVIOUS_X_INDEX] = value;\n\t}\n\tget previousY() {\n\t\treturn this.block[INTERPOLATION_PREVIOUS_Y_INDEX];\n\t}\n\tset previousY(value: number) {\n\t\tthis.block[INTERPOLATION_PREVIOUS_Y_INDEX] = value;\n\t}\n\tget remainingDuration() {\n\t\treturn this.block[INTERPOLATION_REMAINING_DURATION_INDEX];\n\t}\n\tset remainingDuration(value: number) {\n\t\tthis.block[INTERPOLATION_REMAINING_DURATION_INDEX] = value;\n\t}\n}\n\nexport const interpolationDefinition: ComponentDefinition<InterpolationComponent, Float32Array, InterpolationConfig> = {\n\ttype: Float32Array,\n\tsize: INTERPOLATION_SIZE,\n\tloadProperties: ['interpolate'],\n\ttoBlock(config) {\n\t\tconst x = config.x ?? 0;\n\t\tconst y = config.y ?? 0;\n\t\t// Seed both the render position and cached target at spawn; duration 0 means no published step yet.\n\t\treturn [x, y, 0, 0, 0, 0, 0, 0, x, y, 0, 0, x, y, 0, 0, 0];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new InterpolationComponentImpl(memory.getBlock(index), index);\n\t},\n\t// No `save`: everything here is derived from the transform and rebuilt on the next step, and `interpolate`\n\t// is defining config from the entity template.\n};\n\nexport default interpolationDefinition;\n","// A path is pending while the request and result ids differ. Requesters bump the request id; the pathfinding update\n// writes the waypoints and outcome first and the result id last, so a settled path is never read half written.\nexport const NAVIGATION_REQUEST_INDEX = 0;\nexport const NAVIGATION_RESULT_INDEX = 1;\nexport const NAVIGATION_OUTCOME_INDEX = 2;\n// Written only by the follower once a path settles; reset with each new path.\nexport const NAVIGATION_WAYPOINT_INDEX = 3;\nexport const NAVIGATION_WAYPOINT_COUNT_INDEX = 4;\n// Explicit flag because (0, 0) is a valid goal. Only active movers are searched for or rechecked.\nexport const NAVIGATION_ACTIVE_INDEX = 5;\n// Goal and waypoints are Float32 bit patterns in the Int32 block, read with loadFloat32.\nexport const NAVIGATION_GOAL_X_INDEX = 6;\nexport const NAVIGATION_GOAL_Y_INDEX = 7;\nexport const NAVIGATION_WAYPOINTS_INDEX = 8;\n// A longer path is cut short and marked Incomplete, so the follower asks again from its end.\nexport const NAVIGATION_MAX_WAYPOINTS = 32;\nexport const NAVIGATION_SIZE = NAVIGATION_WAYPOINTS_INDEX + NAVIGATION_MAX_WAYPOINTS * 2;\n","import type NavGrid from './nav-grid';\n\nexport interface Point {\n\tx: number\n\ty: number\n}\n\n// Positions are the mover's centre in world units; its size decides which gaps it fits through.\nexport interface PathRequest {\n\tstartX: number\n\tstartY: number\n\tgoalX: number\n\tgoalY: number\n\tagentWidth: number\n\tagentHeight: number\n}\n\nexport const PathStatus = {\n\t// Ends on the goal.\n\tFound: 1,\n\t// Ran out of search budget; heads toward the goal and should be searched again from its end.\n\tIncomplete: 2,\n\t// No way to the goal. Any points lead to the reachable spot closest to it.\n\tUnreachable: 3,\n} as const;\nexport type PathStatusValue = typeof PathStatus[keyof typeof PathStatus];\n\n// `points` excludes the start: they are the waypoints to walk, in order.\nexport interface PathResult {\n\tstatus: PathStatusValue\n\tpoints: Array<Point>\n\titerations: number\n}\n\n// The seam for swapping algorithms: anything that can answer a request against a grid.\nexport interface PathSearch {\n\tfindPath(grid: NavGrid, request: PathRequest): PathResult\n}","import { loadFloat32, storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\nimport {\n\tNAVIGATION_ACTIVE_INDEX,\n\tNAVIGATION_GOAL_X_INDEX,\n\tNAVIGATION_GOAL_Y_INDEX,\n\tNAVIGATION_MAX_WAYPOINTS,\n\tNAVIGATION_OUTCOME_INDEX,\n\tNAVIGATION_REQUEST_INDEX,\n\tNAVIGATION_RESULT_INDEX,\n\tNAVIGATION_SIZE,\n\tNAVIGATION_WAYPOINT_COUNT_INDEX,\n\tNAVIGATION_WAYPOINT_INDEX,\n\tNAVIGATION_WAYPOINTS_INDEX,\n} from './navigation-component-constants';\nimport { PathStatus, type Point } from '../pathfinding/path-search';\n\n// A goal to walk to and the path PathfindingSystem found there. Who follows the path (sets velocity along it) is up to\n// the game; followPath does the bookkeeping.\nexport interface NavigationComponent {\n\tindex: number\n\treadonly active: boolean\n\treadonly goalX: number\n\treadonly goalY: number\n\treadonly pending: boolean\n\treadonly outcome: number\n\treadonly waypointIndex: number\n\treadonly waypoints: Array<Point>\n\tnavigateTo(x: number, y: number): void\n\tstop(): void\n}\n\n// A capability tag. Goals and paths are transient: whoever set a goal re-issues it after a load.\nexport interface NavigationConfig {\n\tnavigate: boolean\n}\n\nclass NavigationComponentImpl extends Component<Int32Array> implements NavigationComponent {\n\tget active() {\n\t\treturn this.block[NAVIGATION_ACTIVE_INDEX] !== 0;\n\t}\n\tget goalX() {\n\t\treturn loadFloat32(this.block, NAVIGATION_GOAL_X_INDEX);\n\t}\n\tget goalY() {\n\t\treturn loadFloat32(this.block, NAVIGATION_GOAL_Y_INDEX);\n\t}\n\tget pending() {\n\t\treturn isPathPending(this.block);\n\t}\n\tget outcome() {\n\t\treturn this.block[NAVIGATION_OUTCOME_INDEX];\n\t}\n\tget waypointIndex() {\n\t\treturn this.block[NAVIGATION_WAYPOINT_INDEX];\n\t}\n\tget waypoints() {\n\t\treturn Array.from({ length: this.block[NAVIGATION_WAYPOINT_COUNT_INDEX] }, (_, i) => waypoint(this.block, i));\n\t}\n\tnavigateTo(x: number, y: number) {\n\t\tnavigateTo(this.block, x, y);\n\t}\n\tstop() {\n\t\tstopNavigating(this.block);\n\t}\n}\n\nexport const navigationDefinition: ComponentDefinition<NavigationComponent, Int32Array, NavigationConfig> = {\n\ttype: Int32Array,\n\tsize: NAVIGATION_SIZE,\n\tloadProperties: ['navigate'],\n\ttoBlock: () => Array.from({ length: NAVIGATION_SIZE }, () => 0),\n\tattach(entity, memory, index) {\n\t\treturn new NavigationComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\n// Sets a new goal and asks for a path to it.\nexport function navigateTo(navigation: Int32Array, x: number, y: number): void {\n\tsetNavigationGoal(navigation, x, y);\n\tnavigation[NAVIGATION_ACTIVE_INDEX] = 1;\n\trequestPath(navigation);\n}\n\n// Nudges the goal without searching again: the path already found still leads near it, and its last leg heads here.\nexport function setNavigationGoal(navigation: Int32Array, x: number, y: number): void {\n\tstoreFloat32(navigation, NAVIGATION_GOAL_X_INDEX, x);\n\tstoreFloat32(navigation, NAVIGATION_GOAL_Y_INDEX, y);\n}\n\nexport function stopNavigating(navigation: Int32Array): void {\n\tnavigation[NAVIGATION_ACTIVE_INDEX] = 0;\n}\n\nexport function isNavigating(navigation: Int32Array): boolean {\n\treturn navigation[NAVIGATION_ACTIVE_INDEX] !== 0;\n}\n\n// Searches again for the current goal.\nexport function requestPath(navigation: Int32Array): void {\n\tAtomics.add(navigation, NAVIGATION_REQUEST_INDEX, 1);\n}\n\nexport function isPathPending(navigation: Int32Array): boolean {\n\treturn Atomics.load(navigation, NAVIGATION_REQUEST_INDEX) !== Atomics.load(navigation, NAVIGATION_RESULT_INDEX);\n}\n\nexport function isUnreachable(navigation: Int32Array): boolean {\n\treturn !isPathPending(navigation) && navigation[NAVIGATION_OUTCOME_INDEX] === PathStatus.Unreachable;\n}\n\nexport function navigationGoal(navigation: Int32Array): Point {\n\treturn { x: loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX), y: loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX) };\n}\n\nexport function waypoint(navigation: Int32Array, i: number): Point {\n\treturn { x: waypointX(navigation, i), y: waypointY(navigation, i) };\n}\n\nexport function waypointX(navigation: Int32Array, i: number): number {\n\treturn loadFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2);\n}\n\nexport function waypointY(navigation: Int32Array, i: number): number {\n\treturn loadFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2 + 1);\n}\n\n// Leaves the result id alone: the writer stores it last, once the path is in place.\nexport function writePath(navigation: Int32Array, status: number, points: ReadonlyArray<Point>): void {\n\tconst truncated = points.length > NAVIGATION_MAX_WAYPOINTS;\n\tconst count = truncated ? NAVIGATION_MAX_WAYPOINTS : points.length;\n\tfor(let i = 0; i < count; i++) {\n\t\tstoreFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2, points[i].x);\n\t\tstoreFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2 + 1, points[i].y);\n\t}\n\tnavigation[NAVIGATION_WAYPOINT_COUNT_INDEX] = count;\n\tnavigation[NAVIGATION_WAYPOINT_INDEX] = 0;\n\tnavigation[NAVIGATION_OUTCOME_INDEX] = truncated ? PathStatus.Incomplete : status;\n}\n\nexport default navigationDefinition;\n","import { bodyDefinition, type BodyComponent } from './body-component';\nimport { bouncinessDefinition, type BouncinessComponent } from './bounciness-component';\nimport { dynamicsDefinition, type DynamicsComponent } from './dynamics-component';\nimport { interpolationDefinition, type InterpolationComponent } from './interpolation-component';\nimport { navigationDefinition, type NavigationComponent } from './navigation-component';\nimport { polygonDefinition, type PolygonComponent } from './polygon-component';\nimport { transformDefinition, type TransformComponent } from './transform-component';\nimport { velocityDefinition, type VelocityComponent } from './velocity-component';\n\n// The component definitions this library supplies. A game spreads them into its own registry:\n//\n// const registry = { ...physicsRegistry, health: healthDefinition };\n//\n// The keys here are what the physics systems query on, so they must stay as the registry keys.\nexport const physicsRegistry = {\n\ttransform: transformDefinition,\n\tvelocity: velocityDefinition,\n\tbody: bodyDefinition,\n\tpolygon: polygonDefinition,\n\tbounciness: bouncinessDefinition,\n\tdynamics: dynamicsDefinition,\n\tinterpolation: interpolationDefinition,\n};\n\n// Kept out of physicsRegistry so only games with pathfinding register it. Spread in alongside it:\n//\n// const registry = { ...physicsRegistry, ...navigationRegistry };\nexport const navigationRegistry = {\n\tnavigation: navigationDefinition,\n};\n\n// The slice of a game's component map the physics systems need. A type alias, not an interface, so it satisfies\n// the ECS's `ComponentMap` index signature; systems only require the game's map extends this.\nexport type PhysicsComponents = {\n\ttransform: TransformComponent\n\tvelocity: VelocityComponent\n\tbody: BodyComponent\n\tpolygon: PolygonComponent\n\tbounciness: BouncinessComponent\n\tdynamics: DynamicsComponent\n\tinterpolation: InterpolationComponent\n};\n\n// The strictly smaller slice InterpolationSystem needs: it never looks at velocity or body. Split out so the\n// interpolation update declares exactly what it touches.\nexport type InterpolationComponents = {\n\ttransform: TransformComponent\n\tinterpolation: InterpolationComponent\n};\n\n// The slice PathfindingSystem needs.\nexport type NavigationComponents = {\n\tnavigation: NavigationComponent\n\ttransform: TransformComponent\n\tbody: BodyComponent\n};\n\n// The same components as their backing arrays, the form an update sees on the worker thread. `body`, `bounciness`,\n// `dynamics` and `interpolation` are optional because an entity may lack them; the collidable query requires `body`, so\n// CollisionComponents narrows it back to guaranteed there. A game widens this for extra callback components:\n//\n// type GameUpdateComponents = PhysicsUpdateComponents & { health?: Float32Array };\nexport type PhysicsUpdateComponents = {\n\ttransform: Float32Array\n\tvelocity: Float32Array\n\tbody?: Uint32Array\n\tpolygon?: Float32Array\n\tbounciness?: Float32Array\n\tdynamics?: Float32Array\n\tinterpolation?: Float32Array\n\t// For checking if entity is dead during run\n\tentity?: Uint32Array\n};\n\n// The blocks the interpolation update works on. Both required - the query asks for both - so no per-entity guard.\nexport type InterpolationUpdateComponents = {\n\ttransform: Float32Array\n\tinterpolation: Float32Array\n};\n","// Pure geometry for the shapes a transform plus a body describe, centred on (x, y) and turned `angle` radians\n// counter-clockwise. Loose number arguments rather than objects so the collision sweep can call per pair\n// without allocating.\n//\n// The three shapes are one idea: an oriented core grown by a radius.\n// rectangle - a width x height box, radius 0\n// circle - a point, radius width / 2\n// capsule - a segment along its longer side, radius half the shorter side\n// A circle is a capsule whose segment has no length, so there are only two core kinds - box and segment - and\n// three pair tests rather than one per shape combination. See shapesOverlap.\n\nimport { SHAPE_CAPSULE, SHAPE_CIRCLE, SHAPE_RECTANGLE } from '../components/body-component-constants';\n\nexport interface Vector {\n\tx: number\n\ty: number\n}\n\n// Scratch, reused rather than allocated: every user is a single unbroken pass, never re-entered part way through.\nconst GAP: Vector = { x: 0, y: 0 };\nconst CANDIDATE: Vector = { x: 0, y: 0 };\n\nexport function capsuleHalfLength(width: number, height: number): number {\n\treturn Math.abs(width - height) / 2;\n}\n\n// The angle a capsule's core segment lies at: its facing, or a quarter turn off it when it is taller than wide.\nexport function capsuleCoreAngle(width: number, height: number, angle: number): number {\n\treturn height > width ? angle + Math.PI / 2 : angle;\n}\n\nexport function shapeRadius(shape: number, width: number, height: number): number {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width / 2;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn Math.min(width, height) / 2;\n\t}\n\n\treturn 0;\n}\n\n// A shape with no area never overlaps anything, which keeps a sizeless entity out of collisions. Which size\n// matters is per shape: a circle is nothing without its width, a capsule with no thickness is a bare line.\nexport function shapeIsEmpty(shape: number, width: number, height: number): boolean {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width <= 0;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn width <= 0 || height <= 0;\n\t}\n\n\treturn width <= 0 || height <= 0;\n}\n\n// Half the axis-aligned box that fully contains a shape at any rotation - what the broadphase indexes it under,\n// so it must hold the whole shape. Asked per shape because a circle is its width alone: one whose height was\n// hand-written smaller is taller than the width/height rectangle a capsule or radius-loaded circle fits in.\nexport function shapeHalfWidth(shape: number, width: number, height: number, angle: number): number {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width / 2;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn Math.abs(Math.cos(capsuleCoreAngle(width, height, angle))) * capsuleHalfLength(width, height) + Math.min(width, height) / 2;\n\t}\n\n\treturn boundsHalfWidth(width, height, angle);\n}\nexport function shapeHalfHeight(shape: number, width: number, height: number, angle: number): number {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width / 2;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn Math.abs(Math.sin(capsuleCoreAngle(width, height, angle))) * capsuleHalfLength(width, height) + Math.min(width, height) / 2;\n\t}\n\n\treturn boundsHalfHeight(width, height, angle);\n}\n\n// Whether two shapes of any kind overlap. Every pair is one of three cases: box+box (separating axis),\n// round+round (segment gap under the summed radii), or box+round (segment gap under the one radius). Touching\n// exactly counts as not overlapping throughout - the comparisons are strict.\nexport function shapesOverlap(\n\taShape: number, aX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\tif(shapeIsEmpty(aShape, aWidth, aHeight) || shapeIsEmpty(bShape, bWidth, bHeight)) {\n\t\treturn false;\n\t}\n\n\tconst aIsRound = aShape !== SHAPE_RECTANGLE;\n\tconst bIsRound = bShape !== SHAPE_RECTANGLE;\n\n\tif(!aIsRound && !bIsRound) {\n\t\treturn orientedBoxesOverlap(aX, aY, aWidth, aHeight, aAngle, bX, bY, bWidth, bHeight, bAngle);\n\t} else if(aIsRound && !bIsRound) {\n\t\t// The mixed case is written once with the box first and reached from the other side by swapping.\n\t\treturn shapesOverlap(\n\t\t\tbShape, bX, bY, bWidth, bHeight, bAngle,\n\t\t\taShape, aX, aY, aWidth, aHeight, aAngle,\n\t\t);\n\t}\n\n\tconst bHalf = coreHalfLength(bShape, bWidth, bHeight);\n\tconst bCoreAngle = capsuleCoreAngle(bWidth, bHeight, bAngle);\n\tconst bCos = Math.cos(bCoreAngle) * bHalf;\n\tconst bSin = Math.sin(bCoreAngle) * bHalf;\n\tconst bRadius = shapeRadius(bShape, bWidth, bHeight);\n\n\tif(!aIsRound) {\n\t\treturn segmentBoxDistanceSquared(\n\t\t\tbX - bCos, bY - bSin, bX + bCos, bY + bSin,\n\t\t\taX, aY, aWidth, aHeight, aAngle,\n\t\t) < bRadius * bRadius;\n\t}\n\n\tconst aHalf = coreHalfLength(aShape, aWidth, aHeight);\n\tconst aCoreAngle = capsuleCoreAngle(aWidth, aHeight, aAngle);\n\tconst aCos = Math.cos(aCoreAngle) * aHalf;\n\tconst aSin = Math.sin(aCoreAngle) * aHalf;\n\tconst reach = shapeRadius(aShape, aWidth, aHeight) + bRadius;\n\n\treturn segmentSegmentDistanceSquared(\n\t\taX - aCos, aY - aSin, aX + aCos, aY + aSin,\n\t\tbX - bCos, bY - bSin, bX + bCos, bY + bSin,\n\t) < reach * reach;\n}\n\n// How far a shape's core reaches from its centre: a capsule's segment, and nothing for a circle or rectangle.\nfunction coreHalfLength(shape: number, width: number, height: number): number {\n\treturn shape === SHAPE_CAPSULE ? capsuleHalfLength(width, height) : 0;\n}\n\n// The unit direction a is pushed off b at their contact, written into `out`; false when the two are on top of\n// each other and there is no such direction. Split the same three ways as shapesOverlap and off the same\n// geometry, so the normal is the face that was actually hit rather than a guess from the bounding boxes: a pair\n// meeting at an angle reflects along the contact, not along whichever world axis happens to be nearest.\nexport function contactNormal(\n\taShape: number, aX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tconst aIsRound = aShape !== SHAPE_RECTANGLE;\n\tconst bIsRound = bShape !== SHAPE_RECTANGLE;\n\n\tif(!aIsRound && !bIsRound) {\n\t\treturn boxesContactNormal(aX, aY, aWidth, aHeight, aAngle, bX, bY, bWidth, bHeight, bAngle, out);\n\t} else if(aIsRound && bIsRound) {\n\t\treturn roundsContactNormal(aShape, aX, aY, aWidth, aHeight, aAngle, bShape, bX, bY, bWidth, bHeight, bAngle, out);\n\t} else if(aIsRound) {\n\t\treturn boxRoundContactNormal(bX, bY, bWidth, bHeight, bAngle, aShape, aX, aY, aWidth, aHeight, aAngle, out);\n\t}\n\n\t// The mixed case is written once with the box second and reached from the other side by turning it round.\n\tif(!boxRoundContactNormal(aX, aY, aWidth, aHeight, aAngle, bShape, bX, bY, bWidth, bHeight, bAngle, out)) {\n\t\treturn false;\n\t}\n\n\tout.x = -out.x;\n\tout.y = -out.y;\n\n\treturn true;\n}\n\n// Two boxes part along whichever of their four side directions they run into each other least far - the face\n// that was hit. The same axes and reaches the separating-axis test uses, kept as depths rather than a verdict.\nfunction boxesContactNormal(\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tconst aCos = Math.cos(aAngle);\n\tconst aSin = Math.sin(aAngle);\n\tconst bCos = Math.cos(bAngle);\n\tconst bSin = Math.sin(bAngle);\n\tconst aHalfWidth = aWidth / 2;\n\tconst aHalfHeight = aHeight / 2;\n\tconst bHalfWidth = bWidth / 2;\n\tconst bHalfHeight = bHeight / 2;\n\tconst offsetX = aX - bX;\n\tconst offsetY = aY - bY;\n\n\tlet best = boxOverlapAlong(aCos, aSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tout.x = aCos;\n\tout.y = aSin;\n\n\tlet depth = boxOverlapAlong(-aSin, aCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tif(depth < best) {\n\t\tbest = depth;\n\t\tout.x = -aSin;\n\t\tout.y = aCos;\n\t}\n\n\tdepth = boxOverlapAlong(bCos, bSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tif(depth < best) {\n\t\tbest = depth;\n\t\tout.x = bCos;\n\t\tout.y = bSin;\n\t}\n\n\tdepth = boxOverlapAlong(-bSin, bCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tif(depth < best) {\n\t\tout.x = -bSin;\n\t\tout.y = bCos;\n\t}\n\n\tpointAwayFrom(offsetX, offsetY, out);\n\n\treturn true;\n}\n\n// Two rounds meet where their cores come closest - the line between those points, grown by nothing since both\n// radii lie along it. Two circles reduce to the line between their centres.\nfunction roundsContactNormal(\n\taShape: number, aX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tconst aHalf = coreHalfLength(aShape, aWidth, aHeight);\n\tconst aCoreAngle = capsuleCoreAngle(aWidth, aHeight, aAngle);\n\tconst aCos = Math.cos(aCoreAngle) * aHalf;\n\tconst aSin = Math.sin(aCoreAngle) * aHalf;\n\tconst bHalf = coreHalfLength(bShape, bWidth, bHeight);\n\tconst bCoreAngle = capsuleCoreAngle(bWidth, bHeight, bAngle);\n\tconst bCos = Math.cos(bCoreAngle) * bHalf;\n\tconst bSin = Math.sin(bCoreAngle) * bHalf;\n\n\tconst distance = Math.sqrt(segmentSegmentDistanceSquared(\n\t\taX - aCos, aY - aSin, aX + aCos, aY + aSin,\n\t\tbX - bCos, bY - bSin, bX + bCos, bY + bSin,\n\t\tGAP,\n\t));\n\tif(distance > 0) {\n\t\tout.x = GAP.x / distance;\n\t\tout.y = GAP.y / distance;\n\n\t\treturn true;\n\t}\n\n\t// The cores cross, so they have no gap to part along; the centre line is the best left.\n\treturn normalize(aX - bX, aY - bY, out);\n}\n\n// A round shape leaves a box along the gap between its core and the box - the box's face for a flat hit, its\n// corner for one off the end - which is the direction the round side is pushed. Not the box: the caller turns\n// it round for that.\nfunction boxRoundContactNormal(\n\tboxX: number, boxY: number, boxWidth: number, boxHeight: number, boxAngle: number,\n\troundShape: number, roundX: number, roundY: number, roundWidth: number, roundHeight: number, roundAngle: number,\n\tout: Vector,\n): boolean {\n\tconst half = coreHalfLength(roundShape, roundWidth, roundHeight);\n\tconst coreAngle = capsuleCoreAngle(roundWidth, roundHeight, roundAngle);\n\tconst halfX = Math.cos(coreAngle) * half;\n\tconst halfY = Math.sin(coreAngle) * half;\n\n\tconst distance = Math.sqrt(segmentBoxDistanceSquared(\n\t\troundX - halfX, roundY - halfY, roundX + halfX, roundY + halfY,\n\t\tboxX, boxY, boxWidth, boxHeight, boxAngle,\n\t\tGAP,\n\t));\n\tif(distance > 0) {\n\t\tout.x = GAP.x / distance;\n\t\tout.y = GAP.y / distance;\n\n\t\treturn true;\n\t}\n\n\t// The core runs through the box, leaving no gap to point along. Fall back to the shallowest way out, in the\n\t// box's frame where it is axis aligned: its own two sides, or square across the core.\n\tconst cos = Math.cos(boxAngle);\n\tconst sin = Math.sin(boxAngle);\n\tconst offsetX = roundX - boxX;\n\tconst offsetY = roundY - boxY;\n\tconst localX = offsetX * cos + offsetY * sin;\n\tconst localY = -offsetX * sin + offsetY * cos;\n\tconst localHalfX = halfX * cos + halfY * sin;\n\tconst localHalfY = -halfX * sin + halfY * cos;\n\tconst radius = shapeRadius(roundShape, roundWidth, roundHeight);\n\tconst halfWidth = boxWidth / 2;\n\tconst halfHeight = boxHeight / 2;\n\n\tlet best = roundOverlapAlong(1, 0, localX, localY, localHalfX, localHalfY, radius, halfWidth, halfHeight);\n\tout.x = 1;\n\tout.y = 0;\n\n\tlet depth = roundOverlapAlong(0, 1, localX, localY, localHalfX, localHalfY, radius, halfWidth, halfHeight);\n\tif(depth < best) {\n\t\tbest = depth;\n\t\tout.x = 0;\n\t\tout.y = 1;\n\t}\n\n\tif(normalize(-localHalfY, localHalfX, CANDIDATE)) {\n\t\tdepth = roundOverlapAlong(CANDIDATE.x, CANDIDATE.y, localX, localY, localHalfX, localHalfY, radius, halfWidth, halfHeight);\n\t\tif(depth < best) {\n\t\t\tout.x = CANDIDATE.x;\n\t\t\tout.y = CANDIDATE.y;\n\t\t}\n\t}\n\n\tpointAwayFrom(localX, localY, out);\n\n\t// Back out of the box's frame.\n\tconst axisX = out.x;\n\tconst axisY = out.y;\n\tout.x = axisX * cos - axisY * sin;\n\tout.y = axisX * sin + axisY * cos;\n\n\treturn true;\n}\n\n// How far an axis-aligned box at the origin and a segment grown by `radius` run into each other along a unit\n// axis. The round side reaches its core's shadow plus the radius, which is the same in every direction.\nfunction roundOverlapAlong(\n\taxisX: number, axisY: number,\n\tcentreX: number, centreY: number,\n\thalfX: number, halfY: number, radius: number,\n\thalfWidth: number, halfHeight: number,\n): number {\n\tconst boxReach = halfWidth * Math.abs(axisX) + halfHeight * Math.abs(axisY);\n\tconst roundReach = Math.abs(halfX * axisX + halfY * axisY) + radius;\n\n\treturn boxReach + roundReach - Math.abs(centreX * axisX + centreY * axisY);\n}\n\n// Turns an axis the way that leads away from the other shape. A tie means the centres sit on the axis' line, and\n// either side is as good.\nfunction pointAwayFrom(offsetX: number, offsetY: number, out: Vector): void {\n\tif(offsetX * out.x + offsetY * out.y < 0) {\n\t\tout.x = -out.x;\n\t\tout.y = -out.y;\n\t}\n}\n\nfunction normalize(x: number, y: number, out: Vector): boolean {\n\tconst length = Math.sqrt(x * x + y * y);\n\tif(length === 0) {\n\t\treturn false;\n\t}\n\n\tout.x = x / length;\n\tout.y = y / length;\n\n\treturn true;\n}\n\n// Half the axis-aligned box containing a rotated rectangle: each side contributes however much now points along\n// the axis. width / 2 at angle 0, height / 2 at a quarter turn.\nexport function boundsHalfWidth(width: number, height: number, angle: number): number {\n\treturn (Math.abs(Math.cos(angle)) * width + Math.abs(Math.sin(angle)) * height) / 2;\n}\nexport function boundsHalfHeight(width: number, height: number, angle: number): number {\n\treturn (Math.abs(Math.sin(angle)) * width + Math.abs(Math.cos(angle)) * height) / 2;\n}\n\n// Whether two rotated boxes overlap, by the separating axis theorem: for two boxes only four axes can separate\n// them - the two each box's sides face along. Touching exactly and a zero-area box both count as not\n// overlapping, which keeps a sizeless entity out of collisions.\nexport function orientedBoxesOverlap(\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\tif(aWidth <= 0 || aHeight <= 0 || bWidth <= 0 || bHeight <= 0) {\n\t\treturn false;\n\t}\n\n\tconst aCos = Math.cos(aAngle);\n\tconst aSin = Math.sin(aAngle);\n\tconst bCos = Math.cos(bAngle);\n\tconst bSin = Math.sin(bAngle);\n\n\tconst aHalfWidth = aWidth / 2;\n\tconst aHalfHeight = aHeight / 2;\n\tconst bHalfWidth = bWidth / 2;\n\tconst bHalfHeight = bHeight / 2;\n\n\tconst offsetX = bX - aX;\n\tconst offsetY = bY - aY;\n\n\t// a's two axes, then b's; each box's sides face along (cos, sin) and (-sin, cos) of its angle.\n\treturn boxOverlapAlong(aCos, aSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0\n\t\t&& boxOverlapAlong(-aSin, aCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0\n\t\t&& boxOverlapAlong(bCos, bSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0\n\t\t&& boxOverlapAlong(-bSin, bCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0;\n}\n\n// How far the boxes' shadows on the axis run into each other; zero or less means the axis separates them, so\n// touching exactly does not overlap. The axes are always a box's own unit-vector sides, so the dot products are\n// true distances and nothing needs normalising. The depth is what picks the contact face in contactNormal.\nfunction boxOverlapAlong(\n\taxisX: number, axisY: number,\n\toffsetX: number, offsetY: number,\n\taHalfWidth: number, aHalfHeight: number, aCos: number, aSin: number,\n\tbHalfWidth: number, bHalfHeight: number, bCos: number, bSin: number,\n): number {\n\tconst aReach = aHalfWidth * Math.abs(aCos * axisX + aSin * axisY) + aHalfHeight * Math.abs(-aSin * axisX + aCos * axisY);\n\tconst bReach = bHalfWidth * Math.abs(bCos * axisX + bSin * axisY) + bHalfHeight * Math.abs(-bSin * axisX + bCos * axisY);\n\n\treturn aReach + bReach - Math.abs(offsetX * axisX + offsetY * axisY);\n}\n\n// Squared distance between two segments - squared so callers can compare against a squared radius without a\n// sqrt. Settles every round pair (a circle is a segment whose ends coincide). Solves the closest points on the\n// two infinite lines then clamps back into the segments; the parallel case pins one end and clamps the rest.\n// `out`, if given, is filled with the vector from b's closest point to a's - the direction a is pushed off b.\nexport function segmentSegmentDistanceSquared(\n\taX1: number, aY1: number, aX2: number, aY2: number,\n\tbX1: number, bY1: number, bX2: number, bY2: number,\n\tout?: Vector,\n): number {\n\tconst aDeltaX = aX2 - aX1;\n\tconst aDeltaY = aY2 - aY1;\n\tconst bDeltaX = bX2 - bX1;\n\tconst bDeltaY = bY2 - bY1;\n\tconst offsetX = aX1 - bX1;\n\tconst offsetY = aY1 - bY1;\n\n\tconst aLength = aDeltaX * aDeltaX + aDeltaY * aDeltaY;\n\tconst bLength = bDeltaX * bDeltaX + bDeltaY * bDeltaY;\n\tconst bOffset = bDeltaX * offsetX + bDeltaY * offsetY;\n\n\tlet aParam = 0;\n\tlet bParam = 0;\n\tif(aLength <= 0 && bLength <= 0) {\n\t\tif(out) {\n\t\t\tout.x = offsetX;\n\t\t\tout.y = offsetY;\n\t\t}\n\n\t\treturn offsetX * offsetX + offsetY * offsetY;\n\t} else if(aLength <= 0) {\n\t\tbParam = clamp(bOffset / bLength);\n\t} else {\n\t\tconst aOffset = aDeltaX * offsetX + aDeltaY * offsetY;\n\t\tif(bLength <= 0) {\n\t\t\taParam = clamp(-aOffset / aLength);\n\t\t} else {\n\t\t\tconst along = aDeltaX * bDeltaX + aDeltaY * bDeltaY;\n\t\t\t// Zero when the segments are parallel; pinning aParam at 0 then picks any point, all equally close.\n\t\t\tconst denominator = aLength * bLength - along * along;\n\t\t\tif(denominator !== 0) {\n\t\t\t\taParam = clamp((along * bOffset - aOffset * bLength) / denominator);\n\t\t\t}\n\n\t\t\tbParam = (along * aParam + bOffset) / bLength;\n\t\t\t// Clamping bParam moves b's closest point, so aParam has to be solved again.\n\t\t\tif(bParam < 0) {\n\t\t\t\tbParam = 0;\n\t\t\t\taParam = clamp(-aOffset / aLength);\n\t\t\t} else if(bParam > 1) {\n\t\t\t\tbParam = 1;\n\t\t\t\taParam = clamp((along - aOffset) / aLength);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst gapX = (aX1 + aDeltaX * aParam) - (bX1 + bDeltaX * bParam);\n\tconst gapY = (aY1 + aDeltaY * aParam) - (bY1 + bDeltaY * bParam);\n\tif(out) {\n\t\tout.x = gapX;\n\t\tout.y = gapY;\n\t}\n\n\treturn gapX * gapX + gapY * gapY;\n}\n\n// Squared distance between a segment and a rotated box, measured in the box's frame where it is axis aligned.\n// Two disjoint convex shapes have their closest pair at a corner, so once the crossing case is ruled out the\n// answer is the nearest of each segment end to the box and each box corner to the segment.\n// `out`, if given, is filled with the world vector from the box's closest point to the segment's - the direction\n// the segment is pushed off the box. It is (0, 0) when the segment runs through the box, which has no such\n// direction; contactNormal falls back to a penetration axis there.\nexport function segmentBoxDistanceSquared(\n\tx1: number, y1: number, x2: number, y2: number,\n\tboxX: number, boxY: number, boxWidth: number, boxHeight: number, boxAngle: number,\n\tout?: Vector,\n): number {\n\tconst cos = Math.cos(boxAngle);\n\tconst sin = Math.sin(boxAngle);\n\tconst halfWidth = boxWidth / 2;\n\tconst halfHeight = boxHeight / 2;\n\n\tconst firstOffsetX = x1 - boxX;\n\tconst firstOffsetY = y1 - boxY;\n\tconst secondOffsetX = x2 - boxX;\n\tconst secondOffsetY = y2 - boxY;\n\tconst localX1 = firstOffsetX * cos + firstOffsetY * sin;\n\tconst localY1 = -firstOffsetX * sin + firstOffsetY * cos;\n\tconst localX2 = secondOffsetX * cos + secondOffsetY * sin;\n\tconst localY2 = -secondOffsetX * sin + secondOffsetY * cos;\n\n\t// A segment through the box has both ends outside and every corner off, so the nearest-pair search below\n\t// would report a real distance for two shapes already through each other; catch it first.\n\tif(!segmentMissesBox(localX1, localY1, localX2, localY2, halfWidth, halfHeight)) {\n\t\tif(out) {\n\t\t\tout.x = 0;\n\t\t\tout.y = 0;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\tif(!out) {\n\t\treturn Math.min(\n\t\t\tpointBoxDistanceSquared(localX1, localY1, halfWidth, halfHeight),\n\t\t\tpointBoxDistanceSquared(localX2, localY2, halfWidth, halfHeight),\n\t\t\tpointSegmentDistanceSquared(-halfWidth, -halfHeight, localX1, localY1, localX2, localY2),\n\t\t\tpointSegmentDistanceSquared(halfWidth, -halfHeight, localX1, localY1, localX2, localY2),\n\t\t\tpointSegmentDistanceSquared(halfWidth, halfHeight, localX1, localY1, localX2, localY2),\n\t\t\tpointSegmentDistanceSquared(-halfWidth, halfHeight, localX1, localY1, localX2, localY2),\n\t\t);\n\t}\n\n\t// The same six candidates, keeping the gap of the winner. The corner ones measure box-to-segment, the other\n\t// way round from the endpoint ones, so they are negated to leave every gap pointing off the box.\n\tlet best = pointBoxDistanceSquared(localX1, localY1, halfWidth, halfHeight, out);\n\tbest = keepCloser(best, pointBoxDistanceSquared(localX2, localY2, halfWidth, halfHeight, CANDIDATE), CANDIDATE, out, false);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(-halfWidth, -halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(halfWidth, -halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(halfWidth, halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(-halfWidth, halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\n\t// Back out of the box's frame, the gap having been measured inside it.\n\tconst localGapX = out.x;\n\tconst localGapY = out.y;\n\tout.x = localGapX * cos - localGapY * sin;\n\tout.y = localGapX * sin + localGapY * cos;\n\n\treturn best;\n}\n\n// Takes the candidate over what `out` holds when it is closer, flipping it when it was measured box-to-segment.\nfunction keepCloser(best: number, candidate: number, gap: Vector, out: Vector, flip: boolean): number {\n\tif(candidate >= best) {\n\t\treturn best;\n\t}\n\n\tout.x = flip ? -gap.x : gap.x;\n\tout.y = flip ? -gap.y : gap.y;\n\n\treturn candidate;\n}\n\n// `out`, if given, is filled with the vector from the segment's closest point to (x, y).\nexport function pointSegmentDistanceSquared(x: number, y: number, x1: number, y1: number, x2: number, y2: number, out?: Vector): number {\n\tconst deltaX = x2 - x1;\n\tconst deltaY = y2 - y1;\n\tconst length = deltaX * deltaX + deltaY * deltaY;\n\n\tconst along = length > 0 ? clamp(((x - x1) * deltaX + (y - y1) * deltaY) / length) : 0;\n\tconst gapX = x - (x1 + deltaX * along);\n\tconst gapY = y - (y1 + deltaY * along);\n\tif(out) {\n\t\tout.x = gapX;\n\t\tout.y = gapY;\n\t}\n\n\treturn gapX * gapX + gapY * gapY;\n}\n\n// Whether an origin-centred axis-aligned box and a segment fail to reach, by separating axis. Touching counts\n// as reaching. For a zero-length segment the normal is (0, 0), which never separates and leaves the two box\n// axes to decide whether the point is inside.\nfunction segmentMissesBox(x1: number, y1: number, x2: number, y2: number, halfWidth: number, halfHeight: number): boolean {\n\tif(Math.max(x1, x2) < -halfWidth || Math.min(x1, x2) > halfWidth) {\n\t\treturn true;\n\t} else if(Math.max(y1, y2) < -halfHeight || Math.min(y1, y2) > halfHeight) {\n\t\treturn true;\n\t}\n\n\tconst normalX = -(y2 - y1);\n\tconst normalY = x2 - x1;\n\n\treturn Math.abs(normalX * x1 + normalY * y1) > halfWidth * Math.abs(normalX) + halfHeight * Math.abs(normalY);\n}\n\n// Squared distance from a point to an origin-centred axis-aligned box; 0 inside it. `out`, if given, is filled\n// with the vector from the box's closest point to (x, y).\nfunction pointBoxDistanceSquared(x: number, y: number, halfWidth: number, halfHeight: number, out?: Vector): number {\n\tconst gapX = Math.abs(x) - halfWidth;\n\tconst gapY = Math.abs(y) - halfHeight;\n\tconst outsideX = gapX > 0 ? gapX : 0;\n\tconst outsideY = gapY > 0 ? gapY : 0;\n\tif(out) {\n\t\tout.x = x < 0 ? -outsideX : outsideX;\n\t\tout.y = y < 0 ? -outsideY : outsideY;\n\t}\n\n\treturn outsideX * outsideX + outsideY * outsideY;\n}\n\nfunction clamp(value: number): number {\n\tif(value < 0) {\n\t\treturn 0;\n\t} else if(value > 1) {\n\t\treturn 1;\n\t}\n\n\treturn value;\n}\n","import { bodyShape } from '../components/body-component';\nimport { SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport {\n\tTRANSFORM_ANGLE_INDEX,\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n} from '../components/transform-component-constants';\nimport { shapeHalfHeight, shapeHalfWidth } from '../math/shapes';\n\nexport interface SpatialBounds {\n\tminX: number\n\tminY: number\n\tmaxX: number\n\tmaxY: number\n}\n\nexport interface SpatialBlockComponents {\n\ttransform: Float32Array\n\tbody?: Uint32Array\n}\n\nexport function spatialBounds(components: SpatialBlockComponents): SpatialBounds {\n\tconst transform = components.transform;\n\tconst shape = components.body ? bodyShape(components.body) : SHAPE_RECTANGLE;\n\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\tconst x = transform[TRANSFORM_X_INDEX];\n\tconst y = transform[TRANSFORM_Y_INDEX];\n\n\treturn {\n\t\tminX: x - halfWidth,\n\t\tminY: y - halfHeight,\n\t\tmaxX: x + halfWidth,\n\t\tmaxY: y + halfHeight,\n\t};\n}\n\nexport function boundsOverlap(a: SpatialBounds, minX: number, minY: number, maxX: number, maxY: number): boolean {\n\treturn a.minX <= maxX && a.maxX >= minX && a.minY <= maxY && a.maxY >= minY;\n}\n\nexport function querySize(min: number, max: number): number {\n\treturn max > min ? max - min : Math.max(1, Math.abs(min)) * Number.EPSILON;\n}\n","import SharedSpatialMap from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport type { SharedSpatialMapConfig, SharedSpatialMapMemory } from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport { BaseWorld } from '@daneren2005/shared-memory-ecs';\nimport type {\n\tBaseEntity,\n\tComponentDefinitionMap,\n\tComponentsOf,\n\tEntityWorkerSystemWorld,\n\tEntityConfigOf,\n\tWorldOptions,\n} from '@daneren2005/shared-memory-ecs';\nimport { boundsOverlap, querySize, spatialBounds } from './systems/spatial-bounds';\n\nexport interface PhysicalWorldData {\n\tspatialMapMemory?: SharedSpatialMapMemory\n}\n\nexport interface PhysicalSystemWorld extends EntityWorkerSystemWorld, PhysicalWorldData {\n\tspatialMap?: SharedSpatialMap\n}\n\nexport interface PhysicalWorldSource {\n\tspatialMap: SharedSpatialMap\n}\n\nexport type PhysicalWorldOptions<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> = WorldOptions<ComponentsOf<R>, EntityConfigOf<R>, E> & {\n\tspatial?: SharedSpatialMapConfig\n};\n\nexport type PhysicalWorldEntity<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> = E;\nexport type PhysicalWorldFilter<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> = (entity: PhysicalWorldEntity<R, E>) => boolean;\n\ninterface SpatialAccessorComponents {\n\ttransform?: { block?: Float32Array }\n\tbody?: { block?: Uint32Array }\n}\n\nexport default class PhysicalWorld<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> extends BaseWorld<R, ComponentsOf<R>, EntityConfigOf<R>, E> implements PhysicalWorldSource {\n\treadonly spatialMap: SharedSpatialMap;\n\tprivate spatialIds = new Set<number>();\n\n\tconstructor(registry: R, options: PhysicalWorldOptions<R, E> = {}) {\n\t\tconst { spatial, ...worldOptions } = options;\n\t\tsuper(registry, worldOptions);\n\n\t\t// Float64 because eids only increase: Float32 caps lifetime-created entities at 2^24, Float64 covers the uint32 eid range\n\t\tthis.spatialMap = new SharedSpatialMap(this.heap, { type: Float64Array, ...spatial });\n\t\tthis.on('entity-added', (entity: PhysicalWorldEntity<R, E>) => {\n\t\t\tthis.updateSpatialEntity(entity);\n\t\t\tentity.on('component-added', (name: string) => {\n\t\t\t\tif(name === 'transform' || name === 'body') {\n\t\t\t\t\tthis.updateSpatialEntity(entity);\n\t\t\t\t}\n\t\t\t});\n\t\t\tentity.on('component-removed', (name: string) => {\n\t\t\t\tif(name === 'transform' || name === 'body') {\n\t\t\t\t\tthis.updateSpatialEntity(entity);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\tthis.on('entity-removed', (entity: PhysicalWorldEntity<R, E>) => {\n\t\t\tthis.spatialIds.delete(entity.eid);\n\t\t\tthis.spatialMap.remove(entity.eid);\n\t\t});\n\t}\n\n\tupdateSpatialEntity(entityOrId: PhysicalWorldEntity<R, E> | number): void {\n\t\tconst entity = typeof entityOrId === 'number' ? this.getEntityByEid(entityOrId) : entityOrId;\n\t\tif(!entity) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst components = entity.components as typeof entity.components & SpatialAccessorComponents;\n\t\tconst transform = components.transform?.block;\n\t\tif(!transform) {\n\t\t\tthis.spatialIds.delete(entity.eid);\n\t\t\tthis.spatialMap.remove(entity.eid);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = spatialBounds({ transform, body: components.body?.block });\n\t\tthis.spatialIds.add(entity.eid);\n\t\tthis.spatialMap.update(entity.eid, bounds.minX, bounds.minY, bounds.maxX - bounds.minX, bounds.maxY - bounds.minY);\n\t}\n\n\tsearchSpatial(minX: number, minY: number, maxX: number, maxY: number, filter?: PhysicalWorldFilter<R, E>): Array<PhysicalWorldEntity<R, E>> {\n\t\tif(maxX < minX || maxY < minY) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst found: Array<PhysicalWorldEntity<R, E>> = [];\n\t\tconst ids = Number.isFinite(minX) && Number.isFinite(minY) && Number.isFinite(maxX) && Number.isFinite(maxY)\n\t\t\t? this.spatialMap.search(minX, minY, querySize(minX, maxX), querySize(minY, maxY))\n\t\t\t: this.spatialIds;\n\t\tfor(const id of ids) {\n\t\t\tconst entity = this.getEntityByEid(id);\n\t\t\tif(!entity || filter && !filter(entity)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst components = entity.components as typeof entity.components & SpatialAccessorComponents;\n\t\t\tconst transform = components.transform?.block;\n\t\t\tif(transform && boundsOverlap(spatialBounds({ transform, body: components.body?.block }), minX, minY, maxX, maxY)) {\n\t\t\t\tfound.push(entity);\n\t\t\t}\n\t\t}\n\n\t\treturn found;\n\t}\n\n\tsearchSpatialAround(x: number, y: number, reachX: number, reachY: number, filter?: PhysicalWorldFilter<R, E>): Array<PhysicalWorldEntity<R, E>> {\n\t\treturn this.searchSpatial(x - reachX, y - reachY, x + reachX, y + reachY, filter);\n\t}\n\n\tfindNearestSpatial(x: number, y: number, maxDistance = Infinity, filter?: PhysicalWorldFilter<R, E>): PhysicalWorldEntity<R, E> | undefined {\n\t\treturn this.findNearbySpatial(x, y, 1, maxDistance, filter)[0];\n\t}\n\n\tfindNearbySpatial(x: number, y: number, maxResults: number, maxDistance = Infinity, filter?: PhysicalWorldFilter<R, E>): Array<PhysicalWorldEntity<R, E>> {\n\t\tconst ids = this.spatialMap.neighbors(x, y, maxResults, maxDistance, id => {\n\t\t\tconst entity = this.getEntityByEid(id);\n\t\t\treturn !!entity && (!filter || filter(entity));\n\t\t});\n\n\t\treturn ids.flatMap(id => {\n\t\t\tconst entity = this.getEntityByEid(id);\n\t\t\treturn entity ? [entity] : [];\n\t\t});\n\t}\n}\n","import { SHAPE_CAPSULE, SHAPE_CIRCLE, SHAPE_POLYGON, SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { POLYGON_VERTEX_COUNT_INDEX, POLYGON_VERTICES_INDEX } from '../components/polygon-component-constants';\nimport { capsuleCoreAngle, capsuleHalfLength } from './shapes';\nimport type { Vector } from './shapes';\n\ninterface Projection {\n\tmin: number\n\tmax: number\n}\n\ninterface AxisResult {\n\toverlap: boolean\n\tdepth: number\n\tx: number\n\ty: number\n}\n\nconst PROJECTION_A: Projection = { min: 0, max: 0 };\nconst PROJECTION_B: Projection = { min: 0, max: 0 };\nconst AXIS: AxisResult = { overlap: true, depth: Infinity, x: 0, y: 0 };\n\nexport function polygonShapesOverlap(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\treturn testPolygonAxes(\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tfalse,\n\t);\n}\n\nexport function polygonContactNormal(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tAXIS.depth = Infinity;\n\tif(!testPolygonAxes(\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\ttrue,\n\t) || AXIS.depth === Infinity) {\n\t\treturn false;\n\t}\n\n\tout.x = AXIS.x;\n\tout.y = AXIS.y;\n\n\treturn true;\n}\n\nfunction testPolygonAxes(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tif(aShape === SHAPE_POLYGON) {\n\t\tif(!aPolygon || !testEdges(\n\t\t\taPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif(bShape === SHAPE_POLYGON) {\n\t\tif(!bPolygon || !testEdges(\n\t\t\tbPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif(aShape === SHAPE_RECTANGLE && !testRectangleAxes(\n\t\taAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\tif(bShape === SHAPE_RECTANGLE && !testRectangleAxes(\n\t\tbAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\n\tif(isRound(aShape) && bPolygon && !testRoundVertexAxes(\n\t\taShape, aX, aY, aWidth, aHeight, aAngle,\n\t\tbPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\tif(isRound(bShape) && aPolygon && !testRoundVertexAxes(\n\t\tbShape, bX, bY, bWidth, bHeight, bAngle,\n\t\taPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nfunction testEdges(\n\tpolygon: Float32Array, x: number, y: number, width: number, height: number, angle: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst count = polygon[POLYGON_VERTEX_COUNT_INDEX];\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\tfor(let i = 0; i < count; i++) {\n\t\tconst next = (i + 1) % count;\n\t\tconst currentX = worldVertexX(polygon, i, x, width, height, cos, sin);\n\t\tconst currentY = worldVertexY(polygon, i, y, width, height, cos, sin);\n\t\tconst nextX = worldVertexX(polygon, next, x, width, height, cos, sin);\n\t\tconst nextY = worldVertexY(polygon, next, y, width, height, cos, sin);\n\t\tif(!testAxis(\n\t\t\tcurrentY - nextY, nextX - currentX,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nfunction testRectangleAxes(\n\tangle: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\n\treturn testAxis(\n\t\tcos, sin,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t) && testAxis(\n\t\t-sin, cos,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t);\n}\n\nfunction testRoundVertexAxes(\n\troundShape: number, roundX: number, roundY: number, roundWidth: number, roundHeight: number, roundAngle: number,\n\tpolygon: Float32Array, polygonX: number, polygonY: number, polygonWidth: number, polygonHeight: number, polygonAngle: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst half = roundShape === SHAPE_CAPSULE ? capsuleHalfLength(roundWidth, roundHeight) : 0;\n\tconst coreAngle = capsuleCoreAngle(roundWidth, roundHeight, roundAngle);\n\tconst alongX = Math.cos(coreAngle) * half;\n\tconst alongY = Math.sin(coreAngle) * half;\n\tconst startX = roundX - alongX;\n\tconst startY = roundY - alongY;\n\tconst endX = roundX + alongX;\n\tconst endY = roundY + alongY;\n\tconst count = polygon[POLYGON_VERTEX_COUNT_INDEX];\n\tconst cos = Math.cos(polygonAngle);\n\tconst sin = Math.sin(polygonAngle);\n\tfor(let i = 0; i < count; i++) {\n\t\tconst vertexX = worldVertexX(polygon, i, polygonX, polygonWidth, polygonHeight, cos, sin);\n\t\tconst vertexY = worldVertexY(polygon, i, polygonY, polygonWidth, polygonHeight, cos, sin);\n\t\tconst fraction = closestFraction(startX, startY, endX, endY, vertexX, vertexY);\n\t\tconst coreX = startX + (endX - startX) * fraction;\n\t\tconst coreY = startY + (endY - startY) * fraction;\n\t\tif(!testAxis(\n\t\t\tvertexX - coreX, vertexY - coreY,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nfunction testAxis(\n\taxisX: number, axisY: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst length = Math.hypot(axisX, axisY);\n\tif(length === 0) {\n\t\treturn true;\n\t}\n\taxisX /= length;\n\taxisY /= length;\n\n\tprojectShape(aShape, aPolygon, aX, aY, aWidth, aHeight, aAngle, axisX, axisY, PROJECTION_A);\n\tprojectShape(bShape, bPolygon, bX, bY, bWidth, bHeight, bAngle, axisX, axisY, PROJECTION_B);\n\tconst negativeDepth = PROJECTION_A.max - PROJECTION_B.min;\n\tconst positiveDepth = PROJECTION_B.max - PROJECTION_A.min;\n\tif(!keepBest && (negativeDepth <= 0 || positiveDepth <= 0)) {\n\t\treturn false;\n\t}\n\n\tif(keepBest) {\n\t\tlet depth: number;\n\t\tlet direction: number;\n\t\tif(negativeDepth <= 0) {\n\t\t\tdepth = -negativeDepth;\n\t\t\tdirection = -1;\n\t\t} else if(positiveDepth <= 0) {\n\t\t\tdepth = -positiveDepth;\n\t\t\tdirection = 1;\n\t\t} else if(negativeDepth < positiveDepth) {\n\t\t\tdepth = negativeDepth;\n\t\t\tdirection = -1;\n\t\t} else {\n\t\t\tdepth = positiveDepth;\n\t\t\tdirection = 1;\n\t\t}\n\t\tif(depth < AXIS.depth) {\n\t\t\tAXIS.depth = depth;\n\t\t\tAXIS.x = axisX * direction;\n\t\t\tAXIS.y = axisY * direction;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nfunction projectShape(\n\tshape: number, polygon: Float32Array | undefined,\n\tx: number, y: number, width: number, height: number, angle: number,\n\taxisX: number, axisY: number, out: Projection,\n): void {\n\tconst centre = x * axisX + y * axisY;\n\tif(shape === SHAPE_CIRCLE) {\n\t\tconst radius = width / 2;\n\t\tout.min = centre - radius;\n\t\tout.max = centre + radius;\n\t\treturn;\n\t}\n\n\tif(shape === SHAPE_CAPSULE) {\n\t\tconst half = capsuleHalfLength(width, height);\n\t\tconst coreAngle = capsuleCoreAngle(width, height, angle);\n\t\tconst reach = Math.abs(axisX * Math.cos(coreAngle) + axisY * Math.sin(coreAngle)) * half + Math.min(width, height) / 2;\n\t\tout.min = centre - reach;\n\t\tout.max = centre + reach;\n\t\treturn;\n\t}\n\n\tif(shape === SHAPE_RECTANGLE) {\n\t\tconst cos = Math.cos(angle);\n\t\tconst sin = Math.sin(angle);\n\t\tconst reach = Math.abs(axisX * cos + axisY * sin) * width / 2\n\t\t\t+ Math.abs(axisX * -sin + axisY * cos) * height / 2;\n\t\tout.min = centre - reach;\n\t\tout.max = centre + reach;\n\t\treturn;\n\t}\n\n\tif(!polygon) {\n\t\tout.min = Infinity;\n\t\tout.max = -Infinity;\n\t\treturn;\n\t}\n\n\tconst count = polygon[POLYGON_VERTEX_COUNT_INDEX];\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\tout.min = Infinity;\n\tout.max = -Infinity;\n\tfor(let i = 0; i < count; i++) {\n\t\tconst vertexX = worldVertexX(polygon, i, x, width, height, cos, sin);\n\t\tconst vertexY = worldVertexY(polygon, i, y, width, height, cos, sin);\n\t\tconst projection = vertexX * axisX + vertexY * axisY;\n\t\tout.min = Math.min(out.min, projection);\n\t\tout.max = Math.max(out.max, projection);\n\t}\n}\n\nfunction worldVertexX(polygon: Float32Array, index: number, x: number, width: number, height: number, cos: number, sin: number): number {\n\tconst offset = POLYGON_VERTICES_INDEX + index * 2;\n\treturn x + polygon[offset] * width * cos - polygon[offset + 1] * height * sin;\n}\n\nfunction worldVertexY(polygon: Float32Array, index: number, y: number, width: number, height: number, cos: number, sin: number): number {\n\tconst offset = POLYGON_VERTICES_INDEX + index * 2;\n\treturn y + polygon[offset] * width * sin + polygon[offset + 1] * height * cos;\n}\n\nfunction closestFraction(startX: number, startY: number, endX: number, endY: number, x: number, y: number): number {\n\tconst segmentX = endX - startX;\n\tconst segmentY = endY - startY;\n\tconst lengthSquared = segmentX * segmentX + segmentY * segmentY;\n\tif(lengthSquared === 0) {\n\t\treturn 0;\n\t}\n\n\treturn Math.min(1, Math.max(0, ((x - startX) * segmentX + (y - startY) * segmentY) / lengthSquared));\n}\n\nfunction isRound(shape: number): boolean {\n\treturn shape === SHAPE_CIRCLE || shape === SHAPE_CAPSULE;\n}\n","export interface DynamicsCommand {\n\tentityId: number\n\tforceX: number\n\tforceY: number\n\timpulseX: number\n\timpulseY: number\n\tvelocityX?: number\n\tvelocityY?: number\n}\n\nexport interface VelocityAssignment {\n\tvelocityX?: number\n\tvelocityY?: number\n}\n\nexport interface DynamicsCommandQueue {\n\tqueueForce(entityId: number, forceX: number, forceY: number): void\n\tqueueImpulse(entityId: number, impulseX: number, impulseY: number): void\n\tqueueVelocity(entityId: number, velocity: VelocityAssignment): void\n}\n\nexport type PendingDynamicsCommands = Map<number, Omit<DynamicsCommand, 'entityId'>>;\n\nexport type DynamicsCommandBuffer = Float64Array;\nexport type DynamicsCommands = DynamicsCommandBuffer | ReadonlyArray<DynamicsCommand>;\n\nexport const DYNAMICS_COMMAND_STRIDE = 8;\nexport const DYNAMICS_COMMAND_ENTITY_ID_OFFSET = 0;\nexport const DYNAMICS_COMMAND_FORCE_X_OFFSET = 1;\nexport const DYNAMICS_COMMAND_FORCE_Y_OFFSET = 2;\nexport const DYNAMICS_COMMAND_IMPULSE_X_OFFSET = 3;\nexport const DYNAMICS_COMMAND_IMPULSE_Y_OFFSET = 4;\nexport const DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET = 5;\nexport const DYNAMICS_COMMAND_VELOCITY_X_OFFSET = 6;\nexport const DYNAMICS_COMMAND_VELOCITY_Y_OFFSET = 7;\nexport const DYNAMICS_COMMAND_VELOCITY_X_FLAG = 1 << 0;\nexport const DYNAMICS_COMMAND_VELOCITY_Y_FLAG = 1 << 1;\n\nexport interface DynamicsWorld {\n\telapsedTime: number\n\tdynamicsCommands?: DynamicsCommands\n}\n\nexport function emptyCommand(): Omit<DynamicsCommand, 'entityId'> {\n\treturn { forceX: 0, forceY: 0, impulseX: 0, impulseY: 0 };\n}\n\n","import type { DynamicsCommand } from './dynamics-commands';\n\nexport function findDynamicsCommand(commands: ReadonlyArray<DynamicsCommand> | undefined, entityId: number): DynamicsCommand | undefined {\n\tif(!commands) {\n\t\treturn undefined;\n\t}\n\n\tlet low = 0;\n\tlet high = commands.length - 1;\n\twhile(low <= high) {\n\t\tconst middle = (low + high) >>> 1;\n\t\tconst command = commands[middle];\n\t\tif(command.entityId === entityId) {\n\t\t\treturn command;\n\t\t} else if(command.entityId < entityId) {\n\t\t\tlow = middle + 1;\n\t\t} else {\n\t\t\thigh = middle - 1;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n","import {\n\tDYNAMICS_COMMAND_STRIDE,\n\tDYNAMICS_COMMAND_ENTITY_ID_OFFSET,\n} from './dynamics-commands';\nimport type { DynamicsCommandBuffer } from './dynamics-commands';\n\nexport function findDynamicsCommandOffset(commands: DynamicsCommandBuffer, entityId: number): number {\n\tlet low = 0;\n\tlet high = commands.length / DYNAMICS_COMMAND_STRIDE - 1;\n\twhile(low <= high) {\n\t\tconst middle = (low + high) >>> 1;\n\t\tconst offset = middle * DYNAMICS_COMMAND_STRIDE;\n\t\tconst commandEntityId = commands[offset + DYNAMICS_COMMAND_ENTITY_ID_OFFSET];\n\t\tif(commandEntityId === entityId) {\n\t\t\treturn offset;\n\t\t} else if(commandEntityId < entityId) {\n\t\t\tlow = middle + 1;\n\t\t} else {\n\t\t\thigh = middle - 1;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n","import Flatbush from 'flatbush';\nimport { DEAD_INDEX } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentMap, EntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents } from '@daneren2005/shared-memory-ecs';\nimport type { PhysicsUpdateComponents } from '../components/registry';\nimport { bodyShape, isContinuous, isDying, isSensor } from '../components/body-component';\nimport { BODY_CATEGORY_INDEX, BODY_MASK_INDEX, SHAPE_CAPSULE, SHAPE_POLYGON, SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { TRANSFORM_ANGLE_INDEX, TRANSFORM_HEIGHT_INDEX, TRANSFORM_WIDTH_INDEX, TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\nimport { DYNAMICS_ACCELERATION_X_INDEX, DYNAMICS_ACCELERATION_Y_INDEX, DYNAMICS_INVERSE_MASS_INDEX } from '../components/dynamics-component-constants';\nimport { contactNormal as shapeContactNormal, shapeHalfHeight, shapeHalfWidth, shapeIsEmpty, shapeRadius, shapesOverlap } from '../math/shapes';\nimport type { Vector } from '../math/shapes';\nimport { polygonContactNormal, polygonShapesOverlap } from '../math/polygons';\nimport {\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n} from '../actions/dynamics-commands';\nimport type { DynamicsCommands } from '../actions/dynamics-commands';\nimport { findDynamicsCommand } from '../actions/find-dynamics-command';\nimport { findDynamicsCommandOffset } from '../actions/find-dynamics-command-offset';\n\n// How close to contact a sweep settles for, in world units: below anything a game would draw, yet reached by\n// the halving below in a handful of steps.\nconst CONTACT_TOLERANCE = 1e-4;\n// Cap on the halving so a huge move cannot spawn unbounded overlap tests; reaches the tolerance for any move\n// up to ~1600 units.\nconst MAX_REFINEMENTS = 24;\n\n// Blocks handed to a collision callback for the entity run into. `transform` and `body` are guaranteed by the\n// collidable query; `velocity` and anything in `optional` are present only if that entity has them.\nexport type CollisionComponents<T extends PhysicsUpdateComponents> = Partial<T> & {\n\ttransform: Float32Array\n\tbody: Uint32Array\n\tpolygon?: Float32Array\n\tentity?: Uint32Array\n};\n\n// The entity doing the running into: the one being updated, so everything the system asked for is present.\nexport interface MovingEntity<T extends PhysicsUpdateComponents> {\n\tentityId: number\n\tcomponents: T\n}\n\n// The entity on the other side of a collision, which may be something the system never moves.\nexport interface CollisionEntity<T extends PhysicsUpdateComponents> {\n\tentityId: number\n\tcomponents: CollisionComponents<T>\n}\n\n// Geometry at the first touch. The normal always points from `other` towards `self`, so negating it gives the\n// direction for the other side. Exact coincident centres can have no unique direction and report (0, 0).\nexport interface CollisionContact {\n\tnormalX: number\n\tnormalY: number\n}\n\n// What a game runs when one entity moves into another. Runs on the physics thread, so it is a plain function\n// over the raw blocks and reaches the main thread only through `callbacks`. Called once per pair per run, right\n// after the mover is put down: `self` is whichever side reached the contact first, `other` is the other. Because\n// the other side gets no call of its own, a callback has to decide for both. A velocity-less entity is never\n// `self` but is found as `other`. `contact` is oriented from `other` towards `self` at first touch.\nexport type CollisionFunction<C extends ComponentMap, T extends PhysicsUpdateComponents & EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld> = (\n\tworld: W,\n\tself: MovingEntity<T>,\n\tother: CollisionEntity<T>,\n\tqueries: EntityQueryComponents<C>,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n\tcontact: CollisionContact,\n) => void;\n\n// How much of a move an entity may make and what stopped it. `fraction` is how far along it got (1 clear, 0\n// already pressed against something). `blocking` is what it came to rest against - the overlap test there will\n// not report it, since the two are touching; more than one entry means it wedged between them at once.\nexport interface SweepResult<T extends PhysicsUpdateComponents> {\n\tfraction: number\n\tblocking: Array<CollisionEntity<T>>\n}\n\n// How a move came out, as the displacement to apply rather than a fraction - a move that dropped an axis is no\n// longer a scaling of the original. `blocking` is empty for a move that got where it was going.\nexport interface MoveResult<T extends PhysicsUpdateComponents> {\n\tmoveX: number\n\tmoveY: number\n\tblocking: Array<CollisionEntity<T>>\n}\n\n// One shared empty array for the sweeps that find nothing, rather than one per entity per run.\nconst NOTHING_BLOCKING: Array<never> = [];\n\n// The minimal read of the searching side a shape test needs: a body to be measured as, at a transform. Looser\n// than MovingEntity - no component map - so the geometry helpers below serve both the sweep and `contactPoint`,\n// which death interpolation calls with whichever side of a collision was fatal.\ninterface ContactSource {\n\tentityId: number\n\tcomponents: { transform: Float32Array, body?: Uint32Array, polygon?: Float32Array }\n}\n// The other side of a shape test: only ever a body at a transform is looked at, so this is all that is required.\n// CollisionEntity satisfies it, as does anything else carrying the two blocks.\ninterface ContactTarget {\n\tcomponents: { transform: Float32Array, body: Uint32Array, polygon?: Float32Array }\n}\n\n// Packs a pair of entity ids into one key. Ids must stay under this for the key to be unique, which a pool an\n// ECS can address leaves untouchable, and it keeps the product inside the safe integer range.\nconst MAX_PAIR_ID = 2 ** 26;\n\n// A per-axis sweep that hit nothing: the whole axis, clear. Held once rather than rebuilt per clear axis.\nconst CLEAR = { fraction: 1, blocking: NOTHING_BLOCKING };\n\n// The searching entity's blocks read out once, so a sweep testing the same pair repeatedly does not re-read\n// them. x/y are where it started the move.\ninterface Searcher {\n\tentityId: number\n\tshape: number\n\tpolygon?: Float32Array\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n\tangle: number\n\thalfWidth: number\n\thalfHeight: number\n\tcategory: number\n\tmask: number\n\t// A sensor's move is never swept short: it passes through rather than resting against. Read once here.\n\tsensor: boolean\n\tccd: boolean\n}\n\n// One category's broadphase: every entity that collides as that category, plus an R-tree over them. A searcher\n// only visits buckets its mask accepts. Bucketed on the whole category value, so an entity that collides as two\n// things at once still lives in exactly one bucket and is never found twice by one search.\ninterface CategoryBucket<T extends PhysicsUpdateComponents> {\n\tcategory: number\n\tentries: Array<CollisionEntity<T>>\n\tindex: Flatbush\n}\n\n// An R-tree over every collidable entity, built once at the top of a run and then asked, per entity as it\n// moves, what it might have hit.\n//\n// Everything is indexed where it stood at the start of the run - the only moment all entities agree on. To stay\n// a superset of what can collide, each box is grown by how far that entity could travel this run, both ways: a\n// callback may turn an entity around before its own move, so a box grown only forwards would point wrong.\n//\n// Split by collide category so most of what an entity cannot hit is ruled out a subtree at a time. An empty\n// category has no bucket, so Flatbush is never asked to index zero items.\nexport default class CollisionBroadphase<T extends PhysicsUpdateComponents> {\n\tprivate buckets: Array<CategoryBucket<T>> = [];\n\t// Whether anything in this run can bounce. Bounces are resolved for both sides of a contact, so an entity with\n\t// no bounciness of its own still has to look at what it ran into - but only in a world where that can matter.\n\treadonly hasBounciness: boolean = false;\n\t// The pairs already resolved this run. Lives here rather than in the update because the tree is what a run is\n\t// scoped to: preRun builds a new one, so the set is empty again for the next run with nothing to clear.\n\tprivate resolved = new Set<number>();\n\n\tconstructor(\n\t\tentities: Array<{ entityId: number, components: EntityUpdateComponents }>,\n\t\tseconds: number,\n\t\tdynamicsCommands?: DynamicsCommands,\n\t) {\n\t\t// Collected per category first because Flatbush needs its item count up front, which is only known once\n\t\t// everything that cannot collide has been dropped.\n\t\tconst pending = new Map<number, { entries: Array<CollisionEntity<T>>, bounds: Array<number> }>();\n\t\tconst flatCommands = dynamicsCommands instanceof Float64Array ? dynamicsCommands : undefined;\n\t\tconst recordCommands = dynamicsCommands && !(dynamicsCommands instanceof Float64Array) ? dynamicsCommands : undefined;\n\n\t\tfor(const entity of entities) {\n\t\t\t// The ECS types blocks generically as ComponentTypedArray; narrow to the concrete arrays once here so\n\t\t\t// neither the search nor the game's callback needs a cast.\n\t\t\tconst components = entity.components as CollisionComponents<T>;\n\t\t\tconst transform = components.transform;\n\t\t\tconst body = components.body;\n\t\t\tif(!transform || !body) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No mask can name category 0, so a category-0 entity can never be the other half of a collision;\n\t\t\t// dropping it here is exact and is what makes `collideCategory: 0` opt out entirely.\n\t\t\tconst category = body[BODY_CATEGORY_INDEX];\n\t\t\tif(category === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst shape = bodyShape(body);\n\t\t\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\t\t\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\t\t\t// A shape with no area can never overlap anything; dropping it keeps a sizeless entity out of the tree.\n\t\t\tif(shapeIsEmpty(shape, width, height)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\t\t\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\t\t\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\n\t\t\tif(components.bounciness) {\n\t\t\t\tthis.hasBounciness = true;\n\t\t\t}\n\n\t\t\tconst velocity = components.velocity;\n\t\t\tconst dynamics = components.dynamics;\n\t\t\tconst command = findDynamicsCommand(recordCommands, entity.entityId);\n\t\t\tconst commandOffset = flatCommands ? findDynamicsCommandOffset(flatCommands, entity.entityId) : -1;\n\t\t\tconst inverseMass = dynamics?.[DYNAMICS_INVERSE_MASS_INDEX] ?? 1;\n\t\t\tconst forceX = commandOffset < 0 ? command?.forceX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_X_OFFSET];\n\t\t\tconst forceY = commandOffset < 0 ? command?.forceY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_Y_OFFSET];\n\t\t\tconst impulseX = commandOffset < 0 ? command?.impulseX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET];\n\t\t\tconst impulseY = commandOffset < 0 ? command?.impulseY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET];\n\t\t\tconst velocityMask = commandOffset < 0 ? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET];\n\t\t\tconst assignedVelocityX = commandOffset < 0\n\t\t\t\t? command?.velocityX\n\t\t\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_X_FLAG\n\t\t\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET]\n\t\t\t\t\t: undefined;\n\t\t\tconst assignedVelocityY = commandOffset < 0\n\t\t\t\t? command?.velocityY\n\t\t\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_Y_FLAG\n\t\t\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET]\n\t\t\t\t\t: undefined;\n\t\t\tconst velocityX = velocity?.[VELOCITY_X_INDEX] ?? 0;\n\t\t\tconst velocityY = velocity?.[VELOCITY_Y_INDEX] ?? 0;\n\t\t\tconst integratedVelocityX = velocityX\n\t\t\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_X_INDEX] ?? 0) * seconds\n\t\t\t\t+ forceX * inverseMass * seconds\n\t\t\t\t+ impulseX * inverseMass;\n\t\t\tconst integratedVelocityY = velocityY\n\t\t\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_Y_INDEX] ?? 0) * seconds\n\t\t\t\t+ forceY * inverseMass * seconds\n\t\t\t\t+ impulseY * inverseMass;\n\t\t\tconst endVelocityX = assignedVelocityX ?? integratedVelocityX;\n\t\t\tconst endVelocityY = assignedVelocityY ?? integratedVelocityY;\n\t\t\tconst travelX = Math.max(Math.abs(velocityX), Math.abs(endVelocityX)) * seconds;\n\t\t\tconst travelY = Math.max(Math.abs(velocityY), Math.abs(endVelocityY)) * seconds;\n\n\t\t\tconst x = transform[TRANSFORM_X_INDEX];\n\t\t\tconst y = transform[TRANSFORM_Y_INDEX];\n\n\t\t\tlet bucket = pending.get(category);\n\t\t\tif(!bucket) {\n\t\t\t\tbucket = { entries: [], bounds: [] };\n\t\t\t\tpending.set(category, bucket);\n\t\t\t}\n\n\t\t\tbucket.entries.push({ entityId: entity.entityId, components });\n\t\t\tbucket.bounds.push(\n\t\t\t\tx - halfWidth - travelX,\n\t\t\t\ty - halfHeight - travelY,\n\t\t\t\tx + halfWidth + travelX,\n\t\t\t\ty + halfHeight + travelY,\n\t\t\t);\n\t\t}\n\n\t\tfor(const [category, { entries, bounds }] of pending) {\n\t\t\tconst index = new Flatbush(entries.length);\n\t\t\tfor(let i = 0; i < entries.length; i++) {\n\t\t\t\tindex.add(bounds[i * 4], bounds[i * 4 + 1], bounds[i * 4 + 2], bounds[i * 4 + 3]);\n\t\t\t}\n\t\t\tindex.finish();\n\n\t\t\tthis.buckets.push({ category, entries, index });\n\t\t}\n\t}\n\n\t// Claims a contact for the caller: true the first time this run sees the pair, false every time after. Both\n\t// sides find the same contact - one sweeping into it, the other overlapping it on its own turn - and it is\n\t// resolved once, by whichever got there first, so nothing is bounced or reported twice.\n\tclaimContact(a: number, b: number): boolean {\n\t\tconst key = a < b ? a * MAX_PAIR_ID + b : b * MAX_PAIR_ID + a;\n\t\tif(this.resolved.has(key)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.resolved.add(key);\n\n\t\treturn true;\n\t}\n\n\t// Runs `handle` for everything `self` really overlaps, having just moved. The tree narrows the field, then\n\t// each candidate gets the full rotated box test against where both entities are now - which for one that has\n\t// not moved yet is still its start. That evens out: the pair is seen again from the other side once it moves.\n\t//\n\t// `moveX`/`moveY` is the move this entity just took, so a continuous-collision body can be tested along the way\n\tforEachOverlapping(self: MovingEntity<T>, handle: (other: CollisionEntity<T>) => void, moveX = 0, moveY = 0): void {\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { x, y, halfWidth, halfHeight } = searcher;\n\n\t\tif(searcher.ccd && (moveX !== 0 || moveY !== 0)) {\n\t\t\tconst startX = x - moveX;\n\t\t\tconst startY = y - moveY;\n\t\t\tconst minX = Math.min(startX, x) - halfWidth;\n\t\t\tconst minY = Math.min(startY, y) - halfHeight;\n\t\t\tconst maxX = Math.max(startX, x) + halfWidth;\n\t\t\tconst maxY = Math.max(startY, y) + halfHeight;\n\t\t\tthis.forEachCandidate(searcher, minX, minY, maxX, maxY, other => {\n\t\t\t\tif(sweptOverlaps(searcher, startX, startY, moveX, moveY, other)) {\n\t\t\t\t\thandle(other);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Searched ungrown at where this entity ended up: every tree box already allows for its own entity's move.\n\t\tthis.forEachCandidate(searcher, x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight, other => {\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\thandle(other);\n\t\t\t}\n\t\t});\n\t}\n\n\t// Where `self`'s centre sits the moment it first touches `other`, along the move it just took (moveX, moveY).\n\t// Used by death interpolation to end a dying entity's last segment exactly on the thing it hit, rather than\n\t// wherever its step happened to land - which for a fast continuous body is a stride past the target. `self`'s\n\t// transform is read after the move, so the start it sweeps from is reconstructed as (end - move). Falls back to\n\t// the current position for an entity that cannot collide or did not move.\n\tcontactPoint(self: ContactSource, other: ContactTarget, moveX: number, moveY: number): { x: number, y: number } {\n\t\tconst transform = self.components.transform;\n\t\tconst endX = transform[TRANSFORM_X_INDEX];\n\t\tconst endY = transform[TRANSFORM_Y_INDEX];\n\t\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher || distance === 0) {\n\t\t\treturn { x: endX, y: endY };\n\t\t}\n\n\t\tsearcher.x = endX - moveX;\n\t\tsearcher.y = endY - moveY;\n\t\tconst fraction = this.entryFraction(searcher, other, moveX, moveY, distance);\n\n\t\treturn { x: searcher.x + moveX * fraction, y: searcher.y + moveY * fraction };\n\t}\n\n\t// The direction `self` leaves `other` at first touch. For a swept sensor this is evaluated where its path\n\t// entered the target, not at the far end of the move after it may already have passed through.\n\tcontactNormal(self: ContactSource, other: ContactTarget, moveX: number, moveY: number, out: Vector): boolean {\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst point = moveX === 0 && moveY === 0\n\t\t\t? { x: searcher.x, y: searcher.y }\n\t\t\t: this.contactPoint(self, other, moveX, moveY);\n\n\t\treturn contactNormalAt(searcher, point.x, point.y, other, out);\n\t}\n\n\t// How much of the move `self` may take before it runs into something. The whole move is checked in one pass\n\t// for what it lands on, and only then is the resting place refined - so the answer is the earliest contact\n\t// of the lot, not whichever candidate came up first.\n\tsweep(self: MovingEntity<T>, moveX: number, moveY: number): SweepResult<T> {\n\t\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher || distance === 0) {\n\t\t\treturn { fraction: 1, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\tif(searcher.ccd) {\n\t\t\treturn this.sweepContinuous(searcher, moveX, moveY, distance);\n\t\t}\n\n\t\tconst candidates = this.gatherCandidates(searcher, moveX, moveY);\n\t\tif(candidates.length === 0) {\n\t\t\treturn { fraction: 1, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\treturn this.refine(searcher, candidates, moveX, moveY, distance);\n\t}\n\n\t// Resolves a move as the displacement to write. With `slide` off it is `sweep` in other clothes: the whole\n\t// move, or the swept-short part. With `slide` on, a blocked diagonal is resolved one axis at a time so an\n\t// entity clipping a corner keeps running along the wall instead of sticking to it. Sliding is the caller's to\n\t// ask for: it is wrong for a bouncing entity, which turns around rather than skating along, so bounces pass\n\t// `slide` off.\n\tresolveMove(self: MovingEntity<T>, moveX: number, moveY: number, slide: boolean): MoveResult<T> {\n\t\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher || distance === 0) {\n\t\t\treturn { moveX, moveY, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\t// A continuous body comes straight to rest at its first contact, never sliding: sliding is for a steered\n\t\t// character clipping a corner at walking pace, not the fast thing ccd is switched on for, and the swept\n\t\t// path is a single decision over the whole move rather than one refined per axis.\n\t\tif(searcher.ccd) {\n\t\t\tconst rest = this.sweepContinuous(searcher, moveX, moveY, distance);\n\n\t\t\treturn { moveX: moveX * rest.fraction, moveY: moveY * rest.fraction, blocking: rest.blocking };\n\t\t}\n\n\t\tconst candidates = this.gatherCandidates(searcher, moveX, moveY);\n\t\tif(candidates.length === 0) {\n\t\t\treturn { moveX, moveY, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\t// Blocked diagonal, asked to slide: resolve each axis on its own to find which one was actually blocked.\n\t\tif(slide && moveX !== 0 && moveY !== 0) {\n\t\t\tconst xCandidates = this.gatherCandidates(searcher, moveX, 0);\n\t\t\tconst yCandidates = this.gatherCandidates(searcher, 0, moveY);\n\n\t\t\t// Corner clip: each axis alone is clear, so only the diagonal was caught. Slide the full length of the\n\t\t\t// dominant axis and leave the other - advancing it would walk back into the corner.\n\t\t\tif(xCandidates.length === 0 && yCandidates.length === 0) {\n\t\t\t\treturn Math.abs(moveX) >= Math.abs(moveY)\n\t\t\t\t\t? { moveX, moveY: 0, blocking: NOTHING_BLOCKING }\n\t\t\t\t\t: { moveX: 0, moveY, blocking: NOTHING_BLOCKING };\n\t\t\t}\n\n\t\t\t// At least one axis is blocked. Take each as far as it goes - a blocked one refined up to its edge, not\n\t\t\t// dropped to nothing - so the entity ends hard against the obstacle.\n\t\t\tconst x = xCandidates.length === 0 ? CLEAR : this.refine(searcher, xCandidates, moveX, 0, Math.abs(moveX));\n\t\t\tconst y = yCandidates.length === 0 ? CLEAR : this.refine(searcher, yCandidates, 0, moveY, Math.abs(moveY));\n\n\t\t\tconst slidX = moveX * x.fraction;\n\t\t\tconst slidY = moveY * y.fraction;\n\n\t\t\t// The axes were swept apart; their combination can still poke into a convex (round) obstacle neither axis\n\t\t\t// alone reached - sliding the full length of one axis carries the entity into the curve the other axis\n\t\t\t// stopped short of. Left overlapping, next run's start-overlap escape hatch would let it pass straight\n\t\t\t// through, so the combined slide is checked against where it actually lands and refined to contact if it\n\t\t\t// overlaps. A box slide ends touching an axis-aligned face and finds nothing here.\n\t\t\tif(slidX !== 0 && slidY !== 0) {\n\t\t\t\tconst slidCandidates = this.gatherCandidates(searcher, slidX, slidY);\n\t\t\t\tif(slidCandidates.length > 0) {\n\t\t\t\t\tconst rest = this.refine(searcher, slidCandidates, slidX, slidY, Math.sqrt(slidX * slidX + slidY * slidY));\n\n\t\t\t\t\treturn { moveX: slidX * rest.fraction, moveY: slidY * rest.fraction, blocking: rest.blocking };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { moveX: slidX, moveY: slidY, blocking: mergeBlocking(x.blocking, y.blocking) };\n\t\t}\n\n\t\t// Not sliding, or a straight move: come to rest where it stops.\n\t\tconst rest = this.refine(searcher, candidates, moveX, moveY, distance);\n\n\t\treturn { moveX: moveX * rest.fraction, moveY: moveY * rest.fraction, blocking: rest.blocking };\n\t}\n\n\t// The cheap half of a sweep: everything the move would land on, tested only where it ends. That keeps a clear\n\t// move - nearly every move - to one shape test per candidate. The trade is that a move long enough to carry\n\t// an entity clean past something is not stopped by it, which a fixed `deltaBetweenRuns` rules out.\n\tprivate gatherCandidates(searcher: Searcher, moveX: number, moveY: number): Array<CollisionEntity<T>> {\n\t\tconst { x, y, halfWidth, halfHeight } = searcher;\n\n\t\t// A sensor's move is blocked by nothing; it is still reported by forEachOverlapping, but never gathered.\n\t\tif(searcher.sensor) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst endX = x + moveX;\n\t\tconst endY = y + moveY;\n\n\t\tconst candidates: Array<CollisionEntity<T>> = [];\n\t\tthis.forEachCandidate(searcher, endX - halfWidth, endY - halfHeight, endX + halfWidth, endY + halfHeight, other => {\n\t\t\tif(!overlapsAt(searcher, endX, endY, other)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// A sensor blocks nothing: a solid mover passes through it, left to the overlap callback.\n\t\t\tif(isSensor(other.components.body)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Already inside it before the move began: it is not what this move ran into, and blocking on it would\n\t\t\t// pin the entity inside with no way out. Let the move through and leave the pair to the overlap callback.\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcandidates.push(other);\n\t\t});\n\n\t\treturn candidates;\n\t}\n\n\t// The expensive half: the move is known to stop short, and this finds where by halving between a `clear`\n\t// fraction and a `blocked` one. Contact is halved in on rather than solved - three shapes at any rotation\n\t// have no single first-touch formula - and the result is always taken from a position that tested clear.\n\tprivate refine(searcher: Searcher, candidates: Array<CollisionEntity<T>>, moveX: number, moveY: number, distance: number): SweepResult<T> {\n\t\tconst { x, y } = searcher;\n\t\tlet clear = 0;\n\t\tlet blocked = 1;\n\t\tconst tolerance = CONTACT_TOLERANCE / distance;\n\t\tfor(let i = 0; i < MAX_REFINEMENTS && blocked - clear > tolerance; i++) {\n\t\t\tconst middle = (clear + blocked) / 2;\n\t\t\tif(this.blockedAt(searcher, candidates, moveX, moveY, middle)) {\n\t\t\t\tblocked = middle;\n\t\t\t} else {\n\t\t\t\tclear = middle;\n\t\t\t}\n\t\t}\n\n\t\t// Whatever is in the way at the first blocked position: the entity rests a hair short of it.\n\t\tconst blocking: Array<CollisionEntity<T>> = [];\n\t\tconst blockedX = x + moveX * blocked;\n\t\tconst blockedY = y + moveY * blocked;\n\t\tfor(const other of candidates) {\n\t\t\tif(overlapsAt(searcher, blockedX, blockedY, other)) {\n\t\t\t\tblocking.push(other);\n\t\t\t}\n\t\t}\n\n\t\t// An entity already against something is left exactly where it is, rather than crawling a sub-tolerance\n\t\t// step forward every run and reporting a position change for no visible move.\n\t\treturn { fraction: clear * distance > CONTACT_TOLERANCE ? clear : 0, blocking };\n\t}\n\n\t// The continuous counterpart to gatherCandidates: everything the move sweeps *across*, not only what it ends\n\t// on, so a step long enough to fly clean past something still gathers it. Costs the swept-shape test per\n\t// candidate, which is why it is a body's own opt-in rather than the default.\n\tprivate gatherContinuousCandidates(searcher: Searcher, moveX: number, moveY: number): Array<CollisionEntity<T>> {\n\t\t// A sensor is stopped by nothing; its overlaps are still reported by forEachOverlapping, never gathered here.\n\t\tif(searcher.sensor) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst { x, y, halfWidth, halfHeight } = searcher;\n\t\tconst endX = x + moveX;\n\t\tconst endY = y + moveY;\n\n\t\tconst candidates: Array<CollisionEntity<T>> = [];\n\t\tthis.forEachCandidate(searcher, Math.min(x, endX) - halfWidth, Math.min(y, endY) - halfHeight, Math.max(x, endX) + halfWidth, Math.max(y, endY) + halfHeight, other => {\n\t\t\t// A sensor blocks nothing: a solid mover passes through it, left to the overlap callback.\n\t\t\tif(isSensor(other.components.body)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Already inside it before the move began: not what this move ran into, and blocking on it would pin the\n\t\t\t// entity there. Let the move through and leave the pair to the overlap callback.\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif(sweptOverlaps(searcher, x, y, moveX, moveY, other)) {\n\t\t\t\tcandidates.push(other);\n\t\t\t}\n\t\t});\n\n\t\treturn candidates;\n\t}\n\n\t// A continuous move's resting place: the earliest contact of everything it swept across. Each candidate's own\n\t// entry fraction is found and the whole move stops at the smallest of them - the near obstacle, not whichever\n\t// the tree happened to turn up first.\n\tprivate sweepContinuous(searcher: Searcher, moveX: number, moveY: number, distance: number): SweepResult<T> {\n\t\tconst candidates = this.gatherContinuousCandidates(searcher, moveX, moveY);\n\t\tif(candidates.length === 0) {\n\t\t\treturn { fraction: 1, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\tlet earliest = 1;\n\t\tlet blocking: Array<CollisionEntity<T>> = [];\n\t\tconst tolerance = CONTACT_TOLERANCE / distance;\n\t\tfor(const other of candidates) {\n\t\t\tconst fraction = this.entryFraction(searcher, other, moveX, moveY, distance);\n\t\t\tif(fraction < earliest - tolerance) {\n\t\t\t\tearliest = fraction;\n\t\t\t\tblocking = [other];\n\t\t\t} else if(fraction <= earliest + tolerance) {\n\t\t\t\tblocking.push(other);\n\t\t\t}\n\t\t}\n\n\t\t// An entity already against something is left exactly where it is rather than creeping a sub-tolerance step\n\t\t// forward every run, same as refine.\n\t\treturn { fraction: earliest * distance > CONTACT_TOLERANCE ? earliest : 0, blocking };\n\t}\n\n\t// The fraction of its move at which `searcher` first touches `other`, halved in the way refine does. What refine\n\t// cannot assume here is that the far end is blocked: a continuous move may pass clean through, clear at both\n\t// ends, so the blocked bracket is seeded from where the two centres pass closest - which for convex shapes that\n\t// really cross lies inside the overlap - rather than from the end of the move.\n\tprivate entryFraction(searcher: Searcher, other: ContactTarget, moveX: number, moveY: number, distance: number): number {\n\t\tlet blocked: number;\n\t\tif(this.blockedAtOne(searcher, other, moveX, moveY, 1)) {\n\t\t\tblocked = 1;\n\t\t} else {\n\t\t\tconst transform = other.components.transform;\n\t\t\tconst toX = transform[TRANSFORM_X_INDEX] - searcher.x;\n\t\t\tconst toY = transform[TRANSFORM_Y_INDEX] - searcher.y;\n\t\t\tconst closest = clamp01((toX * moveX + toY * moveY) / (distance * distance));\n\t\t\t// The swept shape crossed the candidate but the centres' closest pass is not itself an overlap (a\n\t\t\t// glancing hit off the centre line): stop there rather than search on. It rests a hair early at worst,\n\t\t\t// which never tunnels.\n\t\t\tif(!this.blockedAtOne(searcher, other, moveX, moveY, closest)) {\n\t\t\t\treturn closest;\n\t\t\t}\n\n\t\t\tblocked = closest;\n\t\t}\n\n\t\tlet clear = 0;\n\t\tconst tolerance = CONTACT_TOLERANCE / distance;\n\t\tfor(let i = 0; i < MAX_REFINEMENTS && blocked - clear > tolerance; i++) {\n\t\t\tconst middle = (clear + blocked) / 2;\n\t\t\tif(this.blockedAtOne(searcher, other, moveX, moveY, middle)) {\n\t\t\t\tblocked = middle;\n\t\t\t} else {\n\t\t\t\tclear = middle;\n\t\t\t}\n\t\t}\n\n\t\treturn clear;\n\t}\n\n\t// blockedAt for a single candidate, so the continuous refinement halves without allocating a one-item array per\n\t// step. The position is rounded to float32 for the same reason blockedAt is - the resting place must stay clear\n\t// once stored to the shared transform.\n\tprivate blockedAtOne(searcher: Searcher, other: ContactTarget, moveX: number, moveY: number, fraction: number): boolean {\n\t\treturn overlapsAt(searcher, Math.fround(searcher.x + moveX * fraction), Math.fround(searcher.y + moveY * fraction), other);\n\t}\n\n\t// Whether `self` would be inside any of `candidates` that far along its move. The position is rounded to\n\t// float32 - what `move` actually stores into the shared transform - so the resting place this settles on is\n\t// still clear once stored, not a hair inside. Otherwise a float32 nudge into overlap would trip the\n\t// start-overlap escape hatch next run and let the entity pass straight through what it came to rest against.\n\tprivate blockedAt(searcher: Searcher, candidates: Array<CollisionEntity<T>>, moveX: number, moveY: number, fraction: number): boolean {\n\t\tconst x = Math.fround(searcher.x + moveX * fraction);\n\t\tconst y = Math.fround(searcher.y + moveY * fraction);\n\t\tfor(const other of candidates) {\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t// Runs `handle` for every collidable entity in the box that `searcher` may collide with: the tree search and\n\t// the category masks, applied before any shape test so an impossible pair is settled by two ANDs.\n\tprivate forEachCandidate(searcher: Searcher, minX: number, minY: number, maxX: number, maxY: number, handle: (other: CollisionEntity<T>) => void): void {\n\t\tfor(const bucket of this.buckets) {\n\t\t\t// Half of canCollide, settled once per bucket: all entries share this category.\n\t\t\tif((searcher.mask & bucket.category) === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor(const found of bucket.index.search(minX, minY, maxX, maxY)) {\n\t\t\t\tconst other = bucket.entries[found];\n\t\t\t\tif(other.entityId === searcher.entityId) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// The other half of canCollide, per candidate: each entity carries its own mask.\n\t\t\t\tif((other.components.body[BODY_MASK_INDEX] & searcher.category) === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Already killed this run, or by another worker sharing this block.\n\t\t\t\tif(other.components.entity?.[DEAD_INDEX] === 1) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Struck what will kill it and playing out its final segment onto the impact point: it is a step from\n\t\t\t\t// removal and must not be run into again, on this run or the next, or it would resolve a second contact\n\t\t\t\t// and be killed twice. Skipped here rather than left out of the tree so it stays a candidate for nothing\n\t\t\t\t// - the same way a dead entity is filtered at query time, not at build.\n\t\t\t\tif(isDying(other.components.body)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\thandle(other);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction contactNormalAt(searcher: Searcher, x: number, y: number, other: ContactTarget, out: Vector): boolean {\n\tconst transform = other.components.transform;\n\tconst otherShape = bodyShape(other.components.body);\n\tconst otherX = transform[TRANSFORM_X_INDEX];\n\tconst otherY = transform[TRANSFORM_Y_INDEX];\n\tconst otherWidth = transform[TRANSFORM_WIDTH_INDEX];\n\tconst otherHeight = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst otherAngle = transform[TRANSFORM_ANGLE_INDEX];\n\tif(searcher.shape === SHAPE_POLYGON || otherShape === SHAPE_POLYGON) {\n\t\treturn polygonContactNormal(\n\t\t\tsearcher.shape, searcher.polygon,\n\t\t\tx, y, searcher.width, searcher.height, searcher.angle,\n\t\t\totherShape, other.components.polygon,\n\t\t\totherX, otherY, otherWidth, otherHeight, otherAngle,\n\t\t\tout,\n\t\t);\n\t}\n\n\treturn shapeContactNormal(\n\t\tsearcher.shape, x, y, searcher.width, searcher.height, searcher.angle,\n\t\totherShape, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t\tout,\n\t);\n}\n\n// Merges what stopped each axis of a slide, without repeating an entity that stopped both. Either side empty is\n// the common case, so it is handed straight back rather than copied.\nfunction mergeBlocking<T extends PhysicsUpdateComponents>(a: Array<CollisionEntity<T>>, b: Array<CollisionEntity<T>>): Array<CollisionEntity<T>> {\n\tif(a.length === 0) {\n\t\treturn b;\n\t}\n\tif(b.length === 0) {\n\t\treturn a;\n\t}\n\n\tconst merged = [...a];\n\tfor(const other of b) {\n\t\tif(!merged.some(entry => entry.entityId === other.entityId)) {\n\t\t\tmerged.push(other);\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// Reads what a searching entity is measured by, or undefined for one that cannot collide.\nfunction toSearcher(self: ContactSource): Searcher | undefined {\n\t// No body means no collisions, though the system can still move it - the move query does not require a body.\n\tconst body = self.components.body;\n\tif(!body) {\n\t\treturn undefined;\n\t}\n\n\t// Mask 0 runs into nothing, so no bucket is worth searching. This only stops it finding a collision; something\n\t// whose mask accepts it can still find this one, an asymmetry the symmetric canCollide rule then rules out.\n\tconst mask = body[BODY_MASK_INDEX];\n\tif(mask === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst transform = self.components.transform;\n\tconst shape = bodyShape(body);\n\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\tif(shapeIsEmpty(shape, width, height)) {\n\t\treturn undefined;\n\t}\n\n\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\tconst polygon = self.components.polygon;\n\tif(shape === SHAPE_POLYGON && !polygon) {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tentityId: self.entityId,\n\t\tshape,\n\t\tpolygon,\n\t\tx: transform[TRANSFORM_X_INDEX],\n\t\ty: transform[TRANSFORM_Y_INDEX],\n\t\twidth,\n\t\theight,\n\t\tangle,\n\t\thalfWidth: shapeHalfWidth(shape, width, height, angle),\n\t\thalfHeight: shapeHalfHeight(shape, width, height, angle),\n\t\tcategory: body[BODY_CATEGORY_INDEX],\n\t\tmask,\n\t\tsensor: isSensor(body),\n\t\tccd: isContinuous(body),\n\t};\n}\n\n// Whether `searcher`, swept from (startX, startY) along (moveX, moveY), crosses `other` anywhere along the way -\n// the single extra test a continuous-collision body pays per candidate. Each shape sweeps to one covering shape:\n// rectangle - an oriented box holding its start, its end, and everything between, since a box that does not turn\n// as it moves just slides in its own frame and the region it covers is itself grown by that slide.\n// circle - a capsule down the centre path, which is exactly a moving circle's swept region.\n// capsule - that same path capsule, plus its own destination shape for the far end of the pill the straight\n// path capsule falls short of.\n// Every shape's region is a superset of the true swept area, so a contact is only ever reported early, never\n// missed - which is the whole point of turning ccd on.\nfunction sweptOverlaps(searcher: Searcher, startX: number, startY: number, moveX: number, moveY: number, other: ContactTarget): boolean {\n\tconst endX = startX + moveX;\n\tconst endY = startY + moveY;\n\tconst transform = other.components.transform;\n\tconst otherShape = bodyShape(other.components.body);\n\tconst otherX = transform[TRANSFORM_X_INDEX];\n\tconst otherY = transform[TRANSFORM_Y_INDEX];\n\tconst otherWidth = transform[TRANSFORM_WIDTH_INDEX];\n\tconst otherHeight = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst otherAngle = transform[TRANSFORM_ANGLE_INDEX];\n\n\tif(searcher.shape === SHAPE_RECTANGLE || searcher.shape === SHAPE_POLYGON) {\n\t\t// The move measured in the box's own frame, where the box is axis aligned and merely slides by it. Growing\n\t\t// each side by that slide over-covers the two corners the slid hexagon leaves open, which reports early, not\n\t\t// short.\n\t\tconst cos = Math.cos(searcher.angle);\n\t\tconst sin = Math.sin(searcher.angle);\n\t\tconst localX = moveX * cos + moveY * sin;\n\t\tconst localY = -moveX * sin + moveY * cos;\n\n\t\treturn overlapShapes(\n\t\t\tSHAPE_RECTANGLE, undefined, (startX + endX) / 2, (startY + endY) / 2,\n\t\t\tsearcher.width + Math.abs(localX), searcher.height + Math.abs(localY), searcher.angle,\n\t\t\totherShape, other.components.polygon, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t\t);\n\t}\n\n\t// A capsule of the mover's own radius laid down the centre path: width is the path length plus a cap at each\n\t// end, height the full thickness. For a circle that is the exact swept region; for a capsule the destination\n\t// test below adds back the length of the pill it does not cover.\n\tconst radius = shapeRadius(searcher.shape, searcher.width, searcher.height);\n\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\tconst pathAngle = distance > 0 ? Math.atan2(moveY, moveX) : searcher.angle;\n\tconst alongPath = overlapShapes(\n\t\tSHAPE_CAPSULE, undefined, (startX + endX) / 2, (startY + endY) / 2, distance + radius * 2, radius * 2, pathAngle,\n\t\totherShape, other.components.polygon, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t);\n\tif(alongPath) {\n\t\treturn true;\n\t}\n\n\treturn overlapShapes(\n\t\tsearcher.shape, searcher.polygon, endX, endY, searcher.width, searcher.height, searcher.angle,\n\t\totherShape, other.components.polygon, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t);\n}\n\n// [0, 1] clamp for the closest-approach fraction; the shapes helper keeps its own copy unexported.\nfunction clamp01(value: number): number {\n\tif(value < 0) {\n\t\treturn 0;\n\t} else if(value > 1) {\n\t\treturn 1;\n\t}\n\n\treturn value;\n}\n\n// Whether the searcher, put down at (x, y) rather than its own block's position, overlaps `other`. Taking the\n// position as arguments lets a sweep try a whole path without writing any of it.\nfunction overlapsAt(searcher: Searcher, x: number, y: number, other: ContactTarget): boolean {\n\tconst transform = other.components.transform;\n\n\treturn overlapShapes(\n\t\tsearcher.shape, searcher.polygon, x, y, searcher.width, searcher.height, searcher.angle,\n\t\tbodyShape(other.components.body), other.components.polygon,\n\t\ttransform[TRANSFORM_X_INDEX], transform[TRANSFORM_Y_INDEX],\n\t\ttransform[TRANSFORM_WIDTH_INDEX], transform[TRANSFORM_HEIGHT_INDEX], transform[TRANSFORM_ANGLE_INDEX],\n\t);\n}\n\nfunction overlapShapes(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\tif(aShape !== SHAPE_POLYGON && bShape !== SHAPE_POLYGON) {\n\t\treturn shapesOverlap(\n\t\t\taShape, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bX, bY, bWidth, bHeight, bAngle,\n\t\t);\n\t}\n\n\treturn polygonShapesOverlap(\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t);\n}\n","import Flatbush from 'flatbush';\nimport type { EntityUpdateComponents } from '@daneren2005/shared-memory-ecs';\nimport { bodyShape } from '../components/body-component';\nimport { SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { TRANSFORM_ANGLE_INDEX, TRANSFORM_HEIGHT_INDEX, TRANSFORM_WIDTH_INDEX, TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { shapeHalfHeight, shapeHalfWidth } from '../math/shapes';\n\n// The blocks the index reads off each entity. Only the transform is needed; the body refines the box to the\n// entity's actual shape. Anything else a game queried travels along untouched for a filter to read.\nexport type SpatialComponents = {\n\ttransform: Float32Array\n\tbody?: Uint32Array\n};\n\n// One entity as the index hands it back: its id plus whatever blocks came with it.\nexport interface SpatialEntity<T extends SpatialComponents = SpatialComponents> {\n\tentityId: number\n\tcomponents: T\n}\n\n// Which found entities are wanted. Run per candidate, so it is the place for what the index cannot know - the\n// searcher's own id, which team, whether it is dead - and a reject is as if it were never in the box.\nexport type SpatialFilter<T extends SpatialComponents = SpatialComponents> = (entity: SpatialEntity<T>) => boolean;\n\n// An R-tree over a set of entities, for questions about where things are rather than what is hitting what: who\n// is in this area, and what is nearest a point. The same machinery as the collision broadphase minus everything\n// collision specific, so answers are in terms of each entity's axis-aligned box and a `filter` picks the ones\n// that matter. It is a snapshot - nothing reads the blocks after the constructor - so build one per run.\n//\n// const index = new SpatialIndex(queries.collidable);\n// const nearest = index.findNearest(x, y, 150, other => other.entityId !== entityId);\nexport default class SpatialIndex<T extends SpatialComponents = SpatialComponents> {\n\tprivate entries: Array<SpatialEntity<T>> = [];\n\t// Left off for an empty index: Flatbush cannot hold zero items, and there is no answer to give anyway.\n\tprivate index: Flatbush | undefined;\n\n\t// `entities` is anything shaped like a query result, so a system's own `queries.whatever` goes straight in.\n\t// Entities without a transform are skipped - something with no place in the world cannot be found by a search\n\t// of it.\n\tconstructor(entities: Iterable<{ entityId: number, components: EntityUpdateComponents }>) {\n\t\t// Collected up front because Flatbush needs its item count before any are added, only known once everything\n\t\t// without a transform is dropped.\n\t\tconst bounds: Array<number> = [];\n\n\t\tfor(const entity of entities) {\n\t\t\t// The ECS types blocks generically as ComponentTypedArray; narrow to the concrete arrays once here.\n\t\t\tconst components = entity.components as T;\n\t\t\tconst transform = components.transform;\n\t\t\tif(!transform) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No body means the transform's width and height are a plain rectangle, keeping the index usable for\n\t\t\t// anything with a place in the world, not only things that collide.\n\t\t\tconst body = components.body;\n\t\t\tconst shape = body ? bodyShape(body) : SHAPE_RECTANGLE;\n\t\t\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\t\t\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\t\t\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\t\t\tconst x = transform[TRANSFORM_X_INDEX];\n\t\t\tconst y = transform[TRANSFORM_Y_INDEX];\n\n\t\t\t// Unlike the collision broadphase, a zero-area entity is kept: it still has a position to be found at.\n\t\t\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\t\t\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\n\t\t\tthis.entries.push({ entityId: entity.entityId, components });\n\t\t\tbounds.push(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight);\n\t\t}\n\n\t\tif(this.entries.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst index = new Flatbush(this.entries.length);\n\t\tfor(let i = 0; i < this.entries.length; i++) {\n\t\t\tindex.add(bounds[i * 4], bounds[i * 4 + 1], bounds[i * 4 + 2], bounds[i * 4 + 3]);\n\t\t}\n\t\tindex.finish();\n\n\t\tthis.index = index;\n\t}\n\n\t// How many entities are in the index, which is not how many were handed to it: transform-less ones are out.\n\tget size(): number {\n\t\treturn this.entries.length;\n\t}\n\n\t// Everything whose box reaches into the one given, in no particular order. Touching counts, so an exact\n\t// edge question is the caller's to settle on the results.\n\tsearch(minX: number, minY: number, maxX: number, maxY: number, filter?: SpatialFilter<T>): Array<SpatialEntity<T>> {\n\t\tconst index = this.index;\n\t\tif(!index) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst entries = this.entries;\n\t\t// Filtering inside the search keeps a reject from ever reaching the results array.\n\t\tconst found = filter ? index.search(minX, minY, maxX, maxY, i => filter(entries[i]))\n\t\t\t: index.search(minX, minY, maxX, maxY);\n\n\t\treturn found.map(i => entries[i]);\n\t}\n\n\t// `search` from the middle out: everything within `reachX` / `reachY` of (x, y), the form most searches take.\n\tsearchAround(x: number, y: number, reachX: number, reachY: number, filter?: SpatialFilter<T>): Array<SpatialEntity<T>> {\n\t\treturn this.search(x - reachX, y - reachY, x + reachX, y + reachY, filter);\n\t}\n\n\t// The closest entity to (x, y) that `filter` accepts, or undefined when none is within `maxDistance`. A walk\n\t// of the tree in distance order rather than a search-then-sort, so it never measures the far side of the\n\t// world. Distance is to the entity's box, not its centre, so a big thing is as near as its nearest edge.\n\tfindNearest(x: number, y: number, maxDistance = Infinity, filter?: SpatialFilter<T>): SpatialEntity<T> | undefined {\n\t\treturn this.findNearby(x, y, 1, maxDistance, filter)[0];\n\t}\n\n\t// The `maxResults` closest entities to (x, y), nearest first, on the same terms as findNearest.\n\tfindNearby(x: number, y: number, maxResults: number, maxDistance = Infinity, filter?: SpatialFilter<T>): Array<SpatialEntity<T>> {\n\t\tconst index = this.index;\n\t\tif(!index) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst entries = this.entries;\n\t\t// Handed to Flatbush rather than applied afterwards, so `maxResults` counts wanted entities: filtering after\n\t\t// would let a few rejects use up the allowance and hide a valid target behind them.\n\t\tconst found = filter ? index.neighbors(x, y, maxResults, maxDistance, i => filter(entries[i]))\n\t\t\t: index.neighbors(x, y, maxResults, maxDistance);\n\n\t\treturn found.map(i => entries[i]);\n\t}\n}\n","import SharedSpatialMap from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport type { PhysicalSystemWorld } from '../world';\n\nexport function getSpatialMap(world: PhysicalSystemWorld): SharedSpatialMap {\n\tif(world.spatialMap) {\n\t\treturn world.spatialMap;\n\t}\n\tif(!world.heap || !world.spatialMapMemory) {\n\t\tthrow new Error('Spatial system world is missing its shared MemoryHeap or spatial map');\n\t}\n\n\tworld.spatialMap = new SharedSpatialMap(world.heap, world.spatialMapMemory);\n\n\treturn world.spatialMap;\n}\n\n","import type SharedSpatialMap from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport { spatialBounds, type SpatialBlockComponents } from '../systems/spatial-bounds';\nimport type { PhysicalSystemWorld } from '../world';\nimport { getSpatialMap } from './get-spatial-map';\n\nexport function updateSpatialMap(world: PhysicalSystemWorld, entityId: number, components: SpatialBlockComponents): void {\n\tif(!world.spatialMap && (!world.heap || !world.spatialMapMemory)) {\n\t\treturn;\n\t}\n\n\tconst spatialMap: SharedSpatialMap = getSpatialMap(world);\n\tconst bounds = spatialBounds(components);\n\tspatialMap.update(entityId, bounds.minX, bounds.minY, bounds.maxX - bounds.minX, bounds.maxY - bounds.minY);\n}\n\n","import type { EntityUpdateComponents } from '@daneren2005/shared-memory-ecs/worker';\nimport { storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport type { Vector } from '../math/shapes';\nimport type { PhysicalSystemWorld } from '../world';\nimport { updateSpatialMap } from './update-spatial-map';\n\nexport function setEntityPosition(\n\tworld: PhysicalSystemWorld, entity: { entityId: number, components: EntityUpdateComponents }, position: Vector,\n): void {\n\tconst transform = entity.components.transform;\n\tif(!(transform instanceof Float32Array)) {\n\t\treturn;\n\t}\n\tstoreFloat32(transform, TRANSFORM_X_INDEX, position.x);\n\tstoreFloat32(transform, TRANSFORM_Y_INDEX, position.y);\n\tconst body = entity.components.body;\n\tupdateSpatialMap(world, entity.entityId, { transform, ...(body instanceof Uint32Array ? { body } : {}) });\n}\n","import type { PhysicalWorldSource, PhysicalWorldData } from '../world';\n\nexport function addPhysicalWorldData(source: PhysicalWorldSource, target: PhysicalWorldData): void {\n\ttarget.spatialMapMemory = source.spatialMap.getSharedMemory();\n}\n\n","import type { TransformComponent } from '../components/transform-component';\nimport type { InterpolationComponent } from '../components/interpolation-component';\n\nexport interface SnappableEntity {\n\tcomponents: {\n\t\ttransform?: TransformComponent\n\t\tinterpolation?: InterpolationComponent\n\t}\n}\n\n// Reset interpolation after a teleport so it does not render as a long move.\nexport function snapEntity(entity: SnappableEntity): void {\n\tconst { transform, interpolation } = entity.components;\n\tif(!transform || !interpolation) {\n\t\treturn;\n\t}\n\n\tinterpolation.targetX = interpolation.x = transform.x;\n\tinterpolation.targetY = interpolation.y = transform.y;\n\t// A gated reader may still draw from the previous slot until the in-flight run commits.\n\tinterpolation.previousX = transform.x;\n\tinterpolation.previousY = transform.y;\n\tinterpolation.remainingDuration = 0;\n\tinterpolation.syncedStepDuration = 0;\n\tinterpolation.syncedDuration = interpolation.totalDuration;\n}\n\n","import type { TransformComponent } from '../components/transform-component';\nimport type { VelocityComponent } from '../components/velocity-component';\nimport type { InterpolationComponent } from '../components/interpolation-component';\n\nexport interface SpawnableEntity {\n\tcomponents: {\n\t\ttransform?: TransformComponent\n\t\tvelocity?: VelocityComponent\n\t\tinterpolation?: InterpolationComponent\n\t}\n}\n\nexport interface SpawnInterpolationOptions {\n\tstepMs: number\n\t// Elapsed fraction of the current step, clamped to 0..1.\n\tstepFraction: number\n\t// Last physics tick so the first real step is recognized as a new publication.\n\ttick: number\n}\n\n// Jump the transform ahead without a sweep; the render starts at spawn and catches up by the next step.\nexport function startSpawnInterpolation(entity: SpawnableEntity, options: SpawnInterpolationOptions): void {\n\tconst { transform, velocity, interpolation } = entity.components;\n\tif(!transform || !velocity || !interpolation) {\n\t\treturn;\n\t}\n\n\tconst fraction = options.stepFraction < 0 ? 0 : options.stepFraction > 1 ? 1 : options.stepFraction;\n\tconst remaining = 1 - fraction;\n\tconst seconds = options.stepMs / 1000;\n\tconst spawnX = transform.x;\n\tconst spawnY = transform.y;\n\tconst jumpX = velocity.velocityX * seconds * remaining;\n\tconst jumpY = velocity.velocityY * seconds * remaining;\n\n\ttransform.x = spawnX + jumpX;\n\ttransform.y = spawnY + jumpY;\n\tinterpolation.x = spawnX;\n\tinterpolation.y = spawnY;\n\tinterpolation.duration = options.stepMs * remaining;\n\tinterpolation.syncedTick = options.tick;\n\tinterpolation.totalDuration += interpolation.duration;\n\tinterpolation.syncedDuration = interpolation.totalDuration;\n\tinterpolation.remainingDuration = interpolation.duration;\n\tinterpolation.targetX = transform.x;\n\tinterpolation.targetY = transform.y;\n\tinterpolation.syncedStepDuration = interpolation.duration;\n\t// Publish the stamp last so readers see the completed target and timing.\n\tinterpolation.tick = options.tick;\n}\n\n","import {\n\tDYNAMICS_ACCELERATION_X_INDEX,\n\tDYNAMICS_ACCELERATION_Y_INDEX,\n\tDYNAMICS_INVERSE_MASS_INDEX,\n} from '../components/dynamics-component-constants';\nimport { VELOCITY_DAMPING_INDEX, VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\n\nimport {\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n} from './dynamics-commands';\nimport type { DynamicsWorld } from './dynamics-commands';\nimport { findDynamicsCommand } from './find-dynamics-command';\nimport { findDynamicsCommandOffset } from './find-dynamics-command-offset';\n\n// Semi-implicit Euler: acceleration changes this step's velocity before that velocity is turned into movement.\nexport function integrateDynamics(world: DynamicsWorld, entityId: number, velocity: Float32Array, dynamics?: Float32Array): void {\n\tconst commands = world.dynamicsCommands;\n\tconst flatCommands = commands instanceof Float64Array ? commands : undefined;\n\tconst recordCommands = commands && !(commands instanceof Float64Array) ? commands : undefined;\n\tconst command = findDynamicsCommand(recordCommands, entityId);\n\tconst commandOffset = flatCommands ? findDynamicsCommandOffset(flatCommands, entityId) : -1;\n\tconst damping = velocity[VELOCITY_DAMPING_INDEX];\n\tif(!dynamics && !command && commandOffset < 0 && damping === 0) {\n\t\treturn;\n\t}\n\n\tconst seconds = world.elapsedTime / 1000;\n\tconst inverseMass = dynamics?.[DYNAMICS_INVERSE_MASS_INDEX] ?? 1;\n\tconst forceX = commandOffset < 0 ? command?.forceX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_X_OFFSET];\n\tconst forceY = commandOffset < 0 ? command?.forceY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_Y_OFFSET];\n\tconst impulseX = commandOffset < 0 ? command?.impulseX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET];\n\tconst impulseY = commandOffset < 0 ? command?.impulseY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET];\n\tconst velocityMask = commandOffset < 0 ? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET];\n\tconst assignedVelocityX = commandOffset < 0\n\t\t? command?.velocityX\n\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_X_FLAG\n\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET]\n\t\t\t: undefined;\n\tconst assignedVelocityY = commandOffset < 0\n\t\t? command?.velocityY\n\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_Y_FLAG\n\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET]\n\t\t\t: undefined;\n\t// Implicit damping: 1/(1+damping*dt) shed off the integrated velocity, stable at any step and never reversing.\n\t// An explicit velocity assignment is authoritative for the step, so it bypasses damping until it coasts again.\n\tconst dampingFactor = damping > 0 ? 1 / (1 + damping * seconds) : 1;\n\tconst integratedVelocityX = (velocity[VELOCITY_X_INDEX]\n\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_X_INDEX] ?? 0) * seconds\n\t\t+ forceX * inverseMass * seconds\n\t\t+ impulseX * inverseMass) * dampingFactor;\n\tconst integratedVelocityY = (velocity[VELOCITY_Y_INDEX]\n\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_Y_INDEX] ?? 0) * seconds\n\t\t+ forceY * inverseMass * seconds\n\t\t+ impulseY * inverseMass) * dampingFactor;\n\tvelocity[VELOCITY_X_INDEX] = assignedVelocityX ?? integratedVelocityX;\n\tvelocity[VELOCITY_Y_INDEX] = assignedVelocityY ?? integratedVelocityY;\n}\n\nexport default integrateDynamics;\n","import { bodyShape, isSensor } from '../components/body-component';\nimport { SHAPE_POLYGON, SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { BOUNCINESS_INDEX } from '../components/bounciness-component-constants';\nimport { TRANSFORM_ANGLE_INDEX, TRANSFORM_HEIGHT_INDEX, TRANSFORM_WIDTH_INDEX, TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\nimport { contactNormal } from '../math/shapes';\nimport { polygonContactNormal } from '../math/polygons';\nimport type { Vector } from '../math/shapes';\n\ninterface BounceEntity {\n\tcomponents: {\n\t\ttransform: Float32Array\n\t\tvelocity?: Float32Array\n\t\tbounciness?: Float32Array\n\t\tbody?: Uint32Array\n\t\tpolygon?: Float32Array\n\t}\n}\n\n// Scratch, reused rather than allocated: a run is one unbroken pass, never re-entered part way through.\nconst NORMAL: Vector = { x: 0, y: 0 };\n\n// Applies each side's configured velocity response. Bounciness takes precedence; with stopNonBouncing enabled,\n// a side without it instead loses only the velocity component pointing into the surface.\nexport function bouncePair(self: BounceEntity, other: BounceEntity, stopNonBouncing = false): void {\n\tif(!canRespond(self, stopNonBouncing) && !canRespond(other, stopNonBouncing)) {\n\t\treturn;\n\t}\n\n\tif(isSurface(self, other) && collisionNormal(self, other, NORMAL)) {\n\t\trespond(self, NORMAL.x, NORMAL.y, stopNonBouncing);\n\t\trespond(other, -NORMAL.x, -NORMAL.y, stopNonBouncing);\n\t}\n}\n\n// Turns `self` around off `other` and writes the new heading into its velocity block. `bounciness` is how much\n// of the speed into the surface comes back: 1 reflects perfectly, 0 cancels it (slide along the surface) - see\n// BouncinessComponent. Only `self` is touched.\nexport function bounce(self: BounceEntity, other: BounceEntity): void {\n\tif(!canBounce(self) || !isSurface(self, other)) {\n\t\treturn;\n\t}\n\n\tif(collisionNormal(self, other, NORMAL)) {\n\t\treflect(self, NORMAL.x, NORMAL.y);\n\t}\n}\n\n// Mirrors an entity's velocity about a surface it is leaving along `normal`, a unit vector pointing away from it.\nfunction reflect(entity: BounceEntity, normalX: number, normalY: number): void {\n\tconst bounciness = entity.components.bounciness;\n\tconst velocity = entity.components.velocity;\n\tif(!bounciness || !velocity) {\n\t\treturn;\n\t}\n\n\tconst velocityX = velocity[VELOCITY_X_INDEX];\n\tconst velocityY = velocity[VELOCITY_Y_INDEX];\n\n\t// How much of the velocity points into what it hit. Zero or more means it is already on its way out, and\n\t// reflecting again while the two still overlap would turn it straight back in.\n\tconst into = velocityX * normalX + velocityY * normalY;\n\tif(into >= 0) {\n\t\treturn;\n\t}\n\n\t// Reverse the normal component and scale by bounciness, leaving the surface component alone. Scale is 2 at\n\t// bounciness 1 (mirror reflection) and 1 at 0 (removes the normal component, sliding along the surface).\n\tconst scale = (1 + bounciness[BOUNCINESS_INDEX]) * into;\n\tvelocity[VELOCITY_X_INDEX] = velocityX - scale * normalX;\n\tvelocity[VELOCITY_Y_INDEX] = velocityY - scale * normalY;\n}\n\nfunction respond(entity: BounceEntity, normalX: number, normalY: number, stopNonBouncing: boolean): void {\n\tif(entity.components.bounciness) {\n\t\treflect(entity, normalX, normalY);\n\t\treturn;\n\t}\n\n\tconst velocity = entity.components.velocity;\n\tif(!stopNonBouncing || !velocity) {\n\t\treturn;\n\t}\n\n\tconst velocityX = velocity[VELOCITY_X_INDEX];\n\tconst velocityY = velocity[VELOCITY_Y_INDEX];\n\tconst into = velocityX * normalX + velocityY * normalY;\n\tif(into < 0) {\n\t\tvelocity[VELOCITY_X_INDEX] = velocityX - into * normalX;\n\t\tvelocity[VELOCITY_Y_INDEX] = velocityY - into * normalY;\n\t}\n}\n\nfunction canRespond(entity: BounceEntity, stopNonBouncing: boolean): boolean {\n\treturn entity.components.velocity !== undefined && (stopNonBouncing || entity.components.bounciness !== undefined);\n}\n\nfunction canBounce(entity: BounceEntity): boolean {\n\treturn entity.components.bounciness !== undefined && entity.components.velocity !== undefined;\n}\n\n// A sensor is felt only through onCollision, never as a surface, so neither side bounces. The pair is still\n// reported - this decides the bounce, not the collision.\nfunction isSurface(self: BounceEntity, other: BounceEntity): boolean {\n\treturn !isSensorBody(self.components.body) && !isSensorBody(other.components.body);\n}\n\n// Which way `self` is pushed off `other`, as a unit vector, or false when the two are exactly on top of each\n// other and there is no such direction. Taken off the shapes themselves, at the angle they are turned to, so a\n// contact that is not square to the world still reflects the speed that was closing on it.\nfunction collisionNormal(self: BounceEntity, other: BounceEntity, out: Vector): boolean {\n\tconst selfTransform = self.components.transform;\n\tconst otherTransform = other.components.transform;\n\n\tconst selfShape = shapeOf(self.components.body);\n\tconst otherShape = shapeOf(other.components.body);\n\tif(selfShape === SHAPE_POLYGON || otherShape === SHAPE_POLYGON) {\n\t\treturn polygonContactNormal(\n\t\t\tselfShape, self.components.polygon,\n\t\t\tselfTransform[TRANSFORM_X_INDEX], selfTransform[TRANSFORM_Y_INDEX],\n\t\t\tselfTransform[TRANSFORM_WIDTH_INDEX], selfTransform[TRANSFORM_HEIGHT_INDEX], selfTransform[TRANSFORM_ANGLE_INDEX],\n\t\t\totherShape, other.components.polygon,\n\t\t\totherTransform[TRANSFORM_X_INDEX], otherTransform[TRANSFORM_Y_INDEX],\n\t\t\totherTransform[TRANSFORM_WIDTH_INDEX], otherTransform[TRANSFORM_HEIGHT_INDEX], otherTransform[TRANSFORM_ANGLE_INDEX],\n\t\t\tout,\n\t\t);\n\t}\n\n\treturn contactNormal(\n\t\tselfShape,\n\t\tselfTransform[TRANSFORM_X_INDEX], selfTransform[TRANSFORM_Y_INDEX],\n\t\tselfTransform[TRANSFORM_WIDTH_INDEX], selfTransform[TRANSFORM_HEIGHT_INDEX], selfTransform[TRANSFORM_ANGLE_INDEX],\n\t\totherShape,\n\t\totherTransform[TRANSFORM_X_INDEX], otherTransform[TRANSFORM_Y_INDEX],\n\t\totherTransform[TRANSFORM_WIDTH_INDEX], otherTransform[TRANSFORM_HEIGHT_INDEX], otherTransform[TRANSFORM_ANGLE_INDEX],\n\t\tout,\n\t);\n}\n\n// A size-only entity with no body collides as a rectangle.\nfunction shapeOf(body: Uint32Array | undefined): number {\n\treturn body ? bodyShape(body) : SHAPE_RECTANGLE;\n}\n\n// A missing body is a solid, not a sensor.\nfunction isSensorBody(body: Uint32Array | undefined): boolean {\n\treturn body !== undefined && isSensor(body);\n}\n\nexport default bounce;\n","import {\n\tDYNAMICS_COMMAND_STRIDE,\n\tDYNAMICS_COMMAND_ENTITY_ID_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n\temptyCommand,\n} from './dynamics-commands';\nimport type { DynamicsCommand, DynamicsCommands } from './dynamics-commands';\n\n// Commands raised by the previous physics run happened before main-thread commands attached to this one.\n// Forces and impulses accumulate; a newer per-axis velocity assignment replaces an older one.\nexport function combineDynamicsCommands(\n\tqueued: ReadonlyMap<number, Omit<DynamicsCommand, 'entityId'>>,\n\tincoming: DynamicsCommands | undefined,\n): DynamicsCommands | undefined {\n\tif(queued.size === 0) {\n\t\treturn incoming;\n\t}\n\n\tconst combined = new Map<number, Omit<DynamicsCommand, 'entityId'>>();\n\tfor(const [entityId, command] of queued) {\n\t\tcombined.set(entityId, { ...command });\n\t}\n\tforEachDynamicsCommand(incoming, (entityId, command) => {\n\t\tconst current = combined.get(entityId) ?? emptyCommand();\n\t\tcombined.set(entityId, {\n\t\t\tforceX: current.forceX + command.forceX,\n\t\t\tforceY: current.forceY + command.forceY,\n\t\t\timpulseX: current.impulseX + command.impulseX,\n\t\t\timpulseY: current.impulseY + command.impulseY,\n\t\t\tvelocityX: command.velocityX ?? current.velocityX,\n\t\t\tvelocityY: command.velocityY ?? current.velocityY,\n\t\t});\n\t});\n\n\treturn Array.from(combined, ([entityId, command]) => ({ entityId, ...command }))\n\t\t.sort((left, right) => left.entityId - right.entityId);\n}\n\nfunction forEachDynamicsCommand(\n\tcommands: DynamicsCommands | undefined,\n\thandle: (entityId: number, command: Omit<DynamicsCommand, 'entityId'>) => void,\n): void {\n\tif(!commands) {\n\t\treturn;\n\t}\n\tif(!(commands instanceof Float64Array)) {\n\t\tfor(const { entityId, ...command } of commands) {\n\t\t\thandle(entityId, command);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tfor(let offset = 0; offset < commands.length; offset += DYNAMICS_COMMAND_STRIDE) {\n\t\tconst velocityMask = commands[offset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET];\n\t\thandle(commands[offset + DYNAMICS_COMMAND_ENTITY_ID_OFFSET], {\n\t\t\tforceX: commands[offset + DYNAMICS_COMMAND_FORCE_X_OFFSET],\n\t\t\tforceY: commands[offset + DYNAMICS_COMMAND_FORCE_Y_OFFSET],\n\t\t\timpulseX: commands[offset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET],\n\t\t\timpulseY: commands[offset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET],\n\t\t\tvelocityX: velocityMask & DYNAMICS_COMMAND_VELOCITY_X_FLAG\n\t\t\t\t? commands[offset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET]\n\t\t\t\t: undefined,\n\t\t\tvelocityY: velocityMask & DYNAMICS_COMMAND_VELOCITY_Y_FLAG\n\t\t\t\t? commands[offset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET]\n\t\t\t\t: undefined,\n\t\t});\n\t}\n}\n\n","import { emptyCommand } from './dynamics-commands';\nimport type { PendingDynamicsCommands } from './dynamics-commands';\n\nexport function queueDynamicsVector(\n\tcommands: PendingDynamicsCommands,\n\tentityId: number,\n\tforceX: number,\n\tforceY: number,\n\timpulseX: number,\n\timpulseY: number,\n): void {\n\tassertFiniteVector(forceX, forceY, 'Force');\n\tassertFiniteVector(impulseX, impulseY, 'Impulse');\n\tif(forceX === 0 && forceY === 0 && impulseX === 0 && impulseY === 0) {\n\t\treturn;\n\t}\n\n\tconst current = commands.get(entityId) ?? emptyCommand();\n\tconst next = {\n\t\t...current,\n\t\tforceX: current.forceX + forceX,\n\t\tforceY: current.forceY + forceY,\n\t\timpulseX: current.impulseX + impulseX,\n\t\timpulseY: current.impulseY + impulseY,\n\t};\n\tassertFiniteVector(next.forceX, next.forceY, 'Accumulated force');\n\tassertFiniteVector(next.impulseX, next.impulseY, 'Accumulated impulse');\n\tcommands.set(entityId, next);\n}\n\nfunction assertFiniteVector(x: number, y: number, name: string): void {\n\tif(!Number.isFinite(x) || !Number.isFinite(y)) {\n\t\tthrow new Error(`${name} must contain finite numbers`);\n\t}\n}\n\n","import { emptyCommand } from './dynamics-commands';\nimport type { PendingDynamicsCommands, VelocityAssignment } from './dynamics-commands';\n\nexport function queueVelocityAssignment(\n\tcommands: PendingDynamicsCommands,\n\tentityId: number,\n\tvelocity: VelocityAssignment,\n): void {\n\tassertFiniteAssignment(velocity);\n\tif(velocity.velocityX === undefined && velocity.velocityY === undefined) {\n\t\treturn;\n\t}\n\n\tcommands.set(entityId, { ...(commands.get(entityId) ?? emptyCommand()), ...velocity });\n}\n\nfunction assertFiniteAssignment(velocity: VelocityAssignment): void {\n\tif((velocity.velocityX !== undefined && !Number.isFinite(velocity.velocityX))\n\t\t|| (velocity.velocityY !== undefined && !Number.isFinite(velocity.velocityY))) {\n\t\tthrow new Error('Velocity assignment must contain finite numbers');\n\t}\n}\n\n","import { addAtomicFloat32 } from '@daneren2005/shared-memory-objects/utils/atomic-math';\nimport { loadFloat32, storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { DEAD_INDEX } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentMap, EntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents, EntityUpdateFunction } from '@daneren2005/shared-memory-ecs';\nimport type { PhysicsComponents, PhysicsUpdateComponents } from '../components/registry';\nimport {\n\tINTERPOLATION_CHANNEL_INDEX,\n\tINTERPOLATION_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_TICK_INDEX,\n\tINTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_X_INDEX,\n\tINTERPOLATION_PREVIOUS_Y_INDEX,\n\tINTERPOLATION_TICK_INDEX,\n\tINTERPOLATION_TOTAL_DURATION_INDEX,\n} from '../components/interpolation-component-constants';\nimport { isDying, markDying } from '../components/body-component';\nimport { TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\nimport CollisionBroadphase, { type CollisionContact, type CollisionEntity, type CollisionFunction, type MovingEntity } from './collision';\nimport { COLLIDABLE_QUERY, POSITION_UPDATED_EVENT } from '../constants';\nimport type { Vector } from '../math/shapes';\nimport { bouncePair } from './bounce';\nimport { combineDynamicsCommands } from '../actions/combine-dynamics-commands';\nimport { integrateDynamics } from '../actions/integrate-dynamics';\nimport { queueDynamicsVector } from '../actions/queue-dynamics-vector';\nimport { queueVelocityAssignment } from '../actions/queue-velocity-assignment';\nimport type { DynamicsCommandQueue, DynamicsWorld, PendingDynamicsCommands } from '../actions/dynamics-commands';\nimport { spatialBounds, type SpatialBlockComponents } from './spatial-bounds';\nimport type { PhysicalSystemWorld } from '../world';\nimport { updateSpatialMap } from '../actions/update-spatial-map';\n\n// The per-run data object every physics update is handed: the base one plus the step counter the interpolation\n// component is stamped with. PhysicsSystem fills `tick` from addDataToWorld, so a subclass adding data must\n// call `super.addDataToWorld(world)` or nothing gets a tick and interpolation stops noticing new steps.\nexport interface PhysicsWorld extends EntityWorkerSystemWorld, PhysicalSystemWorld, DynamicsWorld {\n\t// Which physics step this is, bumped per run. Only compared for equality - a publication stamp, not a clock.\n\ttick: number\n\t// Stamped onto each publication so InterpolationSystem can hold it back until this system's run commits.\n\tinterpolationChannel?: number\n\t// Whether to report this run's moves. Set by PhysicsSystem from whether anything is listening, since the cost\n\t// is paid in the worker (an id per moved entity, cloned across the boundary). Undefined means report.\n\treportMoves?: boolean\n\t// For a grouped update (see `group` on createPhysicsUpdate)\n\tskipGroup?: number\n\t// Identifies one PhysicsSystem to a shared update function, keeping its callback commands isolated.\n\tcommandQueueId?: number\n\tdieAtImpact?(dying: DeathInterpolationEntity, other: DeathInterpolationEntity): void\n}\n\n// The world seen inside createPhysicsUpdate callbacks. Commands raised here are retained by the active backend\n// and integrated at the start of its next run, so callback behavior does not depend on entity update order.\nexport interface PhysicsCallbackWorld extends PhysicsWorld, DynamicsCommandQueue {}\n\n// What `dieAtImpact` reads off each side. Everything is optional and guarded at runtime so both a MovingEntity\n// (the mover, whose body is only optional in the type) and a CollisionEntity (the thing hit, whose velocity is)\n// can be handed to it either way round - which side carries the fatal category is the game's to sort out.\nexport interface DeathInterpolationEntity {\n\tentityId: number\n\tcomponents: {\n\t\ttransform?: Float32Array\n\t\tvelocity?: Float32Array\n\t\tbody?: Uint32Array\n\t}\n}\n\nconst CONTACT_NORMAL: Vector = { x: 0, y: 0 };\n\n// What a game asks createPhysicsUpdate for on top of plain movement.\nexport interface PhysicsUpdateOptions<\n\tC extends ComponentMap,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> {\n\t// Run for each entity that moved into another, straight after its own move. Optional: an update with no\n\t// callback still sweeps and comes to rest against what is in the way, it just decides nothing about it.\n\tonCollision?: CollisionFunction<C, T, W & PhysicsCallbackWorld>\n\t// Extra components to hand the update, and so the callback, beyond transform and velocity. List a health\n\t// component here to take damage on collision. Optional per entity: one without it is still moved and collided.\n\toptional?: Array<keyof C & string>\n\t// Splits the run into isolated groups by a numeric id read off a component block, so one system can simulate many independent worlds at once\n\tgroup?: PhysicsGroupConfig<C>\n\t// For solid contacts, remove the velocity component pointing into the surface from entities without a\n\t// bounciness component. Off by default to preserve the existing sweep-only response.\n\tstopVelocityOnContact?: boolean\n}\n\n// Which component block carries an entity's group id, and where in it. The block travels with both movers and\n// collidables (PhysicsSystem adds it to their optionals), so grouping holds on both sides of a collision.\nexport interface PhysicsGroupConfig<C extends ComponentMap> {\n\tcomponent: keyof C & string\n\tindex: number\n}\n\n// What PhysicsSystem needs to know about an update to set up its components and queries. Stamped on by\n// createPhysicsUpdate so a game declares its collision setup once, in the module both backends import.\nexport interface PhysicsUpdateMetadata<C extends ComponentMap> {\n\t// Whether the update needs the collidable query. Always true for createPhysicsUpdate, which always sweeps;\n\t// the bare physicsUpdate carries no metadata and never asks for it.\n\tcollision: boolean\n\t// The update integrates the optional dynamics block before movement.\n\tdynamics: boolean\n\toptional: Array<keyof C & string>\n\t// Set when the update groups its run; PhysicsSystem reads it to send the group block with movers and collidables.\n\tgroup?: PhysicsGroupConfig<C>\n}\n\nexport type PhysicsUpdateFunction<\n\tC extends ComponentMap,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> = EntityUpdateFunction<C, T, W> & {\n\tphysics: PhysicsUpdateMetadata<C>\n};\n\n// Builds the update a game runs physics through when it wants its entities to notice each other. The result is\n// a plain EntityUpdateFunction that goes to both backends: createEntitySystemWorker in the worker file, and\n// PhysicsSystem's `updateFunction` for the in-process fallback.\n//\n// export const gamePhysicsUpdate = createPhysicsUpdate<Components, GameUpdateComponents>({\n// optional: ['health'],\n// onCollision(world, a, b, queries, callbacks, contact) { ... },\n// });\n//\n// Every update built here sweeps, so an entity comes to rest against what is in its way. The callback is on top\n// and optional; movement that walks through everything is the bare physicsUpdate below. The callback is baked\n// in rather than passed to PhysicsSystem because functions do not survive postMessage to the worker.\nexport function createPhysicsUpdate<\n\t// Constrained to a map holding the physics components because `T` is the game's components narrowed to\n\t// transform and velocity; every game already spreads physicsRegistry into its own, as PhysicsSystem requires.\n\tC extends ComponentMap & PhysicsComponents = PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C> = PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n>(options: PhysicsUpdateOptions<C, T, W> = {}): PhysicsUpdateFunction<C, T, W> {\n\tconst { onCollision, optional = [], group, stopVelocityOnContact = false } = options;\n\n\t// Built by preRun, read by each entity update after it. Safe as closure variables because a run is one\n\t// unbroken pass - preRun, then every entity - never re-entered part way through. Ungrouped runs use the single\n\t// `broadphase`; a grouped run keeps one per group and each entity sweeps against its own.\n\tlet broadphase: CollisionBroadphase<T> | undefined;\n\tlet broadphaseByGroup: Map<number, CollisionBroadphase<T>> | undefined;\n\tconst callbackCommandsBySystem = new Map<number, PendingDynamicsCommands>();\n\n\tconst update: PhysicsUpdateFunction<C, T, W> = Object.assign(\n\t\t(world: W, entityId: number, components: T, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>) => {\n\t\t\t// Killed earlier this run\n\t\t\tif(components.entity?.[DEAD_INDEX] === 1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Struck something fatal on an earlier run and marked to die a step later, once its final segment onto the\n\t\t\t// impact point had a step to render (see dieAtImpact). That step is now, so this is where it actually dies -\n\t\t\t// before it moves or sweeps again, so it neither drifts on nor resolves a second contact.\n\t\t\tif(components.body !== undefined && isDying(components.body)) {\n\t\t\t\tcallbacks.entityDied(entityId);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// A grouped entity in the skipped group is left entirely alone - another system owns it this run.\n\t\t\tconst groupId = group ? readGroup<C>(components, group) : 0;\n\t\t\tif(group && world.skipGroup === groupId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst self: MovingEntity<T> = { entityId, components };\n\t\t\tconst interpolation = components.interpolation;\n\t\t\tconst seconds = world.elapsedTime / 1000;\n\t\t\tintegrateDynamics(world, entityId, components.velocity, components.dynamics);\n\t\t\tconst moveX = components.velocity[VELOCITY_X_INDEX] * seconds;\n\t\t\tconst moveY = components.velocity[VELOCITY_Y_INDEX] * seconds;\n\n\t\t\t// Taken before anything is written, so the segment a renderer blends along runs from this step's start\n\t\t\t// to its end.\n\t\t\tstartInterpolationStep(interpolation, components.transform, world);\n\n\t\t\t// No tree means either this update was called by hand without preRun, or a grouped entity whose group\n\t\t\t// holds nothing collidable: nothing to sweep against, plain move. Bound locally so it stays narrowed\n\t\t\t// inside the overlap callback below.\n\t\t\tconst tree = group ? broadphaseByGroup?.get(groupId) : broadphase;\n\t\t\tif(!tree) {\n\t\t\t\tmove(entityId, components.transform, moveX, moveY, callbacks, world.reportMoves);\n\t\t\t\tupdateSpatialMap(world, entityId, components);\n\t\t\t\tfinishInterpolationStep(interpolation, world.tick);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Native bounce turns an entity around on contact without a game callback; the bounciness component\n\t\t\t// decides whether and how much. Read before the move because it also decides how the move resolves.\n\t\t\tconst bouncing = components.bounciness !== undefined;\n\n\t\t\t// Resolved off the blocks and only written once known, so an entity is never inside another. The move\n\t\t\t// resolves with or without a callback - coming to rest is what physics does about a collision,\n\t\t\t// onCollision is what the game does. A non-bouncing entity slides along a single axis off a corner; a\n\t\t\t// bouncing one does not, since it is about to turn around off the face it hit.\n\t\t\tconst moved = tree.resolveMove(self, moveX, moveY, !bouncing);\n\t\t\tmove(entityId, components.transform, moved.moveX, moved.moveY, callbacks, world.reportMoves);\n\t\t\tupdateSpatialMap(world, entityId, components);\n\t\t\t// Before any callback, so the stamp covers exactly the pair physics produced. A callback that writes\n\t\t\t// the transform is a game move, blended towards next frame like a teleport.\n\t\t\tfinishInterpolationStep(interpolation, world.tick);\n\n\t\t\t// A non-bouncing entity still has to look, since what it ran into may bounce off it.\n\t\t\tif(!tree.hasBounciness && !stopVelocityOnContact && !onCollision) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handled after the entity is put down, so both bounce and callback act on where it ended up; the\n\t\t\t// flipped velocity is applied by the next run. A contact is resolved once per run, by whichever side\n\t\t\t// reached it first - claimContact is what makes the second side's turn a no-op.\n\t\t\ttree.forEachOverlapping(self, other => {\n\t\t\t\tif(tree.claimContact(entityId, other.entityId)) {\n\t\t\t\t\tresolveContact(world as W & PhysicsCallbackWorld, tree, self, other, queries, callbacks, moved.moveX, moved.moveY, stopVelocityOnContact, onCollision);\n\t\t\t\t}\n\t\t\t}, moved.moveX, moved.moveY);\n\n\t\t\t// Then what the move was stopped against, which the overlap check cannot find: the entity rests\n\t\t\t// touching it, not through it. The two lists never share an entry.\n\t\t\tfor(const other of moved.blocking) {\n\t\t\t\tif(tree.claimContact(entityId, other.entityId)) {\n\t\t\t\t\tresolveContact(world as W & PhysicsCallbackWorld, tree, self, other, queries, callbacks, moved.moveX, moved.moveY, stopVelocityOnContact, onCollision);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tphysics: {\n\t\t\t\t// The sweep needs the collidable query even with no callback, so this is not `onCollision !== undefined`.\n\t\t\t\tcollision: true,\n\t\t\t\tdynamics: true,\n\t\t\t\toptional,\n\t\t\t\tgroup,\n\t\t\t},\n\t\t},\n\t);\n\n\t// preRun sees every entity at once, so it is the only place the tree can be built from one consistent moment,\n\t// before any of this run's movement. Ungrouped builds one tree over everything; grouped buckets the collidables\n\t// by group id and builds a tree per group, so an entity only ever sweeps against its own group - the skipped\n\t// group gets none, since nothing in it will be stepped.\n\tupdate.preRun = (world, entities, queries) => {\n\t\tprepareCallbackCommands(world, callbackCommandsBySystem);\n\t\t// Bound to the tree it strikes against and hung on the world so a collision callback can reach it. The tree\n\t\t// is picked when it is called, not now, since the callback fires after the trees below are built; a dying\n\t\t// entity and the thing it hit are always in the same group, so the dying side's group picks the right one.\n\t\tworld.dieAtImpact = (dying, other) => {\n\t\t\tconst tree = group ? broadphaseByGroup?.get(readGroup<C>(dying.components, group)) : broadphase;\n\t\t\tapplyDeathInterpolation<T>(world, tree, dying, other);\n\t\t};\n\n\t\tconst seconds = world.elapsedTime / 1000;\n\t\tconst collidables = queries[COLLIDABLE_QUERY] ?? [];\n\t\tif(!group) {\n\t\t\tbroadphase = new CollisionBroadphase<T>(collidables, seconds, world.dynamicsCommands);\n\t\t\treturn;\n\t\t}\n\n\t\tconst byGroup = new Map<number, typeof collidables>();\n\t\tfor(const entity of collidables) {\n\t\t\tconst groupId = readGroup<C>(entity.components, group);\n\t\t\tif(world.skipGroup === groupId) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet list = byGroup.get(groupId);\n\t\t\tif(!list) {\n\t\t\t\tlist = [];\n\t\t\t\tbyGroup.set(groupId, list);\n\t\t\t}\n\t\t\tlist.push(entity);\n\t\t}\n\n\t\tbroadphaseByGroup = new Map();\n\t\tfor(const [groupId, list] of byGroup) {\n\t\t\tbroadphaseByGroup.set(groupId, new CollisionBroadphase<T>(list, seconds, world.dynamicsCommands));\n\t\t}\n\t};\n\n\treturn update;\n}\n\nfunction prepareCallbackCommands(\n\tworld: PhysicsWorld,\n\tcommandsBySystem: Map<number, PendingDynamicsCommands>,\n): void {\n\tconst commandWorld = world as PhysicsWorld & Partial<DynamicsCommandQueue>;\n\tconst commandQueueId = world.commandQueueId ?? 0;\n\tconst deferred = commandsBySystem.get(commandQueueId);\n\tif(deferred) {\n\t\tworld.dynamicsCommands = combineDynamicsCommands(deferred, world.dynamicsCommands);\n\t\tcommandsBySystem.delete(commandQueueId);\n\t}\n\tconst commands = (): PendingDynamicsCommands => {\n\t\tlet pending = commandsBySystem.get(commandQueueId);\n\t\tif(!pending) {\n\t\t\tpending = new Map();\n\t\t\tcommandsBySystem.set(commandQueueId, pending);\n\t\t}\n\n\t\treturn pending;\n\t};\n\tcommandWorld.queueForce = (entityId, forceX, forceY) => {\n\t\tqueueDynamicsVector(commands(), entityId, forceX, forceY, 0, 0);\n\t};\n\tcommandWorld.queueImpulse = (entityId, impulseX, impulseY) => {\n\t\tqueueDynamicsVector(commands(), entityId, 0, 0, impulseX, impulseY);\n\t};\n\tcommandWorld.queueVelocity = (entityId, velocity) => {\n\t\tqueueVelocityAssignment(commands(), entityId, velocity);\n\t};\n}\n\n// Reads an entity's group id off the configured block, defaulting to group 0 when the entity lacks it.\nfunction readGroup<C extends ComponentMap>(components: EntityUpdateComponents<C>, group: PhysicsGroupConfig<C>): number {\n\tconst block = components[group.component];\n\n\treturn block ? block[group.index] : 0;\n}\n\n// Everything a contact means, applied once for the pair: both sides turned around by their own bounciness, then\n// the one callback. `self` is whichever side reached the contact first, so a game acting on only one of the two\n// would leave the other untouched half the time - the callback has to decide for both.\nfunction resolveContact<\n\tC extends ComponentMap,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsCallbackWorld,\n>(\n\tworld: W,\n\ttree: CollisionBroadphase<T>,\n\tself: MovingEntity<T>,\n\tother: CollisionEntity<T>,\n\tqueries: EntityQueryComponents<C>,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n\tmoveX: number,\n\tmoveY: number,\n\tstopVelocityOnContact: boolean,\n\tonCollision?: CollisionFunction<C, T, W>,\n): void {\n\tbouncePair(self, other, stopVelocityOnContact);\n\tif(!onCollision) {\n\t\treturn;\n\t}\n\n\tconst selfBounds = spatialBounds(self.components);\n\tconst otherBounds = spatialBounds(other.components);\n\tCONTACT_NORMAL.x = 0;\n\tCONTACT_NORMAL.y = 0;\n\ttree.contactNormal(self, other, moveX, moveY, CONTACT_NORMAL);\n\tconst contact: CollisionContact = { normalX: CONTACT_NORMAL.x, normalY: CONTACT_NORMAL.y };\n\tonCollision(world, self, other, queries, callbacks, contact);\n\tupdateSpatialMapIfBoundsChanged(world, self.entityId, self.components, selfBounds);\n\tupdateSpatialMapIfBoundsChanged(world, other.entityId, other.components, otherBounds);\n}\n\nfunction updateSpatialMapIfBoundsChanged(\n\tworld: PhysicalSystemWorld,\n\tentityId: number,\n\tcomponents: SpatialBlockComponents,\n\tbefore: ReturnType<typeof spatialBounds>,\n): void {\n\tconst after = spatialBounds(components);\n\tif(after.minX !== before.minX || after.minY !== before.minY || after.maxX !== before.maxX || after.maxY !== before.maxY) {\n\t\tupdateSpatialMap(world, entityId, components);\n\t}\n}\n\n// Backs `dieAtImpact`. Rather than remove `dying` on the spot - where a fast continuous mover has already stepped\n// a stride past what it hit, and its render, a step behind, has it short of the target - this ends its current\n// interpolation segment on the exact point it struck `other` and marks it to die one run later. That extra run is\n// the step the render needs to play the segment out, so the entity is drawn reaching the impact and only then\n// vanishes. Position only: the game still raises whatever hit/score event it wants, on the spot. A no-op tail for\n// an entity missing the blocks or that did not move leaves it marked dying, so it is still cleaned up next run.\nfunction applyDeathInterpolation<T extends PhysicsUpdateComponents>(\n\tworld: PhysicsWorld,\n\ttree: CollisionBroadphase<T> | undefined,\n\tdying: DeathInterpolationEntity,\n\tother: DeathInterpolationEntity,\n): void {\n\tconst body = dying.components.body;\n\t// Needs a body to carry the flag; already dying means a second contact this run, which is nothing to redo.\n\tif(!body || isDying(body)) {\n\t\treturn;\n\t}\n\tmarkDying(body);\n\n\tconst transform = dying.components.transform;\n\tconst velocity = dying.components.velocity;\n\tconst otherTransform = other.components.transform;\n\tconst otherBody = other.components.body;\n\tif(!tree || !transform || !velocity || !otherTransform || !otherBody) {\n\t\treturn;\n\t}\n\n\tconst seconds = world.elapsedTime / 1000;\n\tconst moveX = velocity[VELOCITY_X_INDEX] * seconds;\n\tconst moveY = velocity[VELOCITY_Y_INDEX] * seconds;\n\tif(moveX === 0 && moveY === 0) {\n\t\treturn;\n\t}\n\n\tconst contact = tree.contactPoint(\n\t\t{ entityId: dying.entityId, components: { transform, body } },\n\t\t{ components: { transform: otherTransform, body: otherBody } },\n\t\tmoveX,\n\t\tmoveY,\n\t);\n\t// Retarget this run's segment: `prev` is still the step's start, so moving the transform back to the impact\n\t// point makes the render play start -> impact instead of start -> overshoot. The tick was already stamped for\n\t// this run and the interpolation reads the transform live, so the shortened end is picked up with no reseeding.\n\ttransform[TRANSFORM_X_INDEX] = contact.x;\n\ttransform[TRANSFORM_Y_INDEX] = contact.y;\n}\n\n// Movement only, and the only way to get movement with no collision detection: walks an entity straight through\n// anything in its way, for something that has no business noticing the world (a particle, a camera). For\n// entities stopped by each other, build the update with createPhysicsUpdate above, which sweeps around this move.\nexport function physicsUpdate<\n\tC extends ComponentMap & PhysicsComponents = PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C> = PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n>(world: W, entityId: number, components: T, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>): void {\n\t// elapsedTime is ms, velocity is units per second.\n\tconst seconds = world.elapsedTime / 1000;\n\tconst interpolation = components.interpolation;\n\tintegrateDynamics(world, entityId, components.velocity, components.dynamics);\n\n\tstartInterpolationStep(interpolation, components.transform, world);\n\tmove(\n\t\tentityId,\n\t\tcomponents.transform,\n\t\tcomponents.velocity[VELOCITY_X_INDEX] * seconds,\n\t\tcomponents.velocity[VELOCITY_Y_INDEX] * seconds,\n\t\tcallbacks,\n\t\tworld.reportMoves,\n\t);\n\tupdateSpatialMap(world, entityId, components);\n\tfinishInterpolationStep(interpolation, world.tick);\n}\n\n// Preserve the current publication in the previous slot, then publish simulated time under the dirty marker\n// before moving the transform. The previous slot is complete before the current tick reads NaN, so a reader that\n// finds the current one torn or uncommitted always has a coherent step to fall back on.\nfunction startInterpolationStep(interpolation: Float32Array | undefined, transform: Float32Array, world: PhysicsWorld): void {\n\tif(!interpolation) {\n\t\treturn;\n\t}\n\n\tstoreFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX, Number.NaN);\n\tinterpolation[INTERPOLATION_PREVIOUS_X_INDEX] = transform[TRANSFORM_X_INDEX];\n\tinterpolation[INTERPOLATION_PREVIOUS_Y_INDEX] = transform[TRANSFORM_Y_INDEX];\n\tinterpolation[INTERPOLATION_PREVIOUS_DURATION_INDEX] = interpolation[INTERPOLATION_DURATION_INDEX];\n\tinterpolation[INTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX] = interpolation[INTERPOLATION_TOTAL_DURATION_INDEX];\n\tstoreFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX, loadFloat32(interpolation, INTERPOLATION_TICK_INDEX));\n\n\t// Invalidate readers before changing timing or position.\n\tstoreFloat32(interpolation, INTERPOLATION_TICK_INDEX, Number.NaN);\n\tinterpolation[INTERPOLATION_CHANNEL_INDEX] = world.interpolationChannel ?? 0;\n\tinterpolation[INTERPOLATION_DURATION_INDEX] = world.elapsedTime;\n\tinterpolation[INTERPOLATION_TOTAL_DURATION_INDEX] += world.elapsedTime;\n}\n\n// Stationary entities also publish time; release the matching transform and timing together.\nfunction finishInterpolationStep(interpolation: Float32Array | undefined, tick: number): void {\n\tif(!interpolation) {\n\t\treturn;\n\t}\n\n\tstoreFloat32(interpolation, INTERPOLATION_TICK_INDEX, tick);\n}\n\n// Applies an already-decided move and reports where the entity ended up. The one place the position is written,\n// reached only once the final position is known. Applied with addAtomicFloat32 rather than `+=` because another\n// thread may be adding to the same shared position at once and a plain read-modify-write would drop one; the\n// `max` cap is unwanted, so Infinity leaves a plain atomic add. A move that went nowhere is not reported, which\n// keeps a world of still entities quiet. See PhysicsSystem for `reportMoves`.\nfunction move(entityId: number, transform: Float32Array, moveX: number, moveY: number, callbacks: PositionCallbacks, reportMoves?: boolean): void {\n\tif(moveX === 0 && moveY === 0) {\n\t\treturn;\n\t}\n\n\tif(moveX !== 0) {\n\t\taddAtomicFloat32(transform, TRANSFORM_X_INDEX, moveX, Infinity);\n\t}\n\tif(moveY !== 0) {\n\t\taddAtomicFloat32(transform, TRANSFORM_Y_INDEX, moveY, Infinity);\n\t}\n\n\tif(reportMoves === false) {\n\t\treturn;\n\t}\n\n\t// Just the id: the main thread reads the position off the shared block, so no copy is worth sending.\n\tcallbacks.emitSystemEvent(POSITION_UPDATED_EVENT, entityId);\n}\n\n// The one callback move reaches for, narrowed to the event physics reports.\ninterface PositionCallbacks {\n\temitSystemEvent(event: typeof POSITION_UPDATED_EVENT, entityId: number): void\n}\n\nexport default physicsUpdate;\n","// Main-thread registry of each world's PhysicsSystems, so an InterpolationSystem can snapshot every run's commit\n// once per frame. A worker publishes entity by entity while the main thread renders, so without one snapshot some\n// entities of a run would be drawn at its new step and others at the old one.\nexport interface InterpolationCommitSource {\n\t// The last tick whose run has finished publishing every entity.\n\treadonly committedTick: number\n}\n\nconst channelsByWorld = new WeakMap<object, Array<InterpolationCommitSource | undefined>>();\n\n// Channels start at 1 so a zeroed block reads as \"no channel\" and stays ungated.\nexport function registerInterpolationChannel(world: object, source: InterpolationCommitSource): number {\n\tlet channels = channelsByWorld.get(world);\n\tif(!channels) {\n\t\tchannels = [undefined];\n\t\tchannelsByWorld.set(world, channels);\n\t}\n\tchannels.push(source);\n\n\treturn channels.length - 1;\n}\n\nexport function unregisterInterpolationChannel(world: object, channel: number): void {\n\tconst channels = channelsByWorld.get(world);\n\tif(channels && channel > 0 && channel < channels.length) {\n\t\tchannels[channel] = undefined;\n\t}\n}\n\n// Indexed by channel. An unregistered slot is NaN, which leaves its entities ungated.\nexport function snapshotCommittedTicks(world: object): Array<number> | undefined {\n\tconst channels = channelsByWorld.get(world);\n\tif(!channels) {\n\t\treturn undefined;\n\t}\n\n\treturn channels.map(source => source ? source.committedTick : Number.NaN);\n}\n","import { EntityWorkerSystem } from '@daneren2005/shared-memory-ecs';\nimport type { BaseEntity, BaseWorld, ComponentDefinitionMap, ComponentMap, EntityWorkerSystemQuery, EntityUpdateComponents, EntityUpdateFunction, SystemConfig } from '@daneren2005/shared-memory-ecs';\nimport { startSpawnInterpolation, type SpawnableEntity } from '../actions/start-spawn-interpolation';\nimport type { PhysicsComponents, PhysicsUpdateComponents } from '../components/registry';\nimport { physicsUpdate, type PhysicsUpdateMetadata, type PhysicsWorld } from './physics-update';\nimport { COLLIDABLE_QUERY, DEFAULT_PHYSICS_STEP_MS, POSITION_UPDATED_EVENT } from '../constants';\nimport type { PhysicalWorldSource } from '../world';\nimport { addPhysicalWorldData } from '../actions/add-physical-world-data';\nimport { registerInterpolationChannel, unregisterInterpolationChannel } from './interpolation-channels';\nimport {\n\tDYNAMICS_COMMAND_ENTITY_ID_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_STRIDE,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n} from '../actions/dynamics-commands';\nimport type { DynamicsCommandBuffer, PendingDynamicsCommands, VelocityAssignment } from '../actions/dynamics-commands';\nimport { queueDynamicsVector } from '../actions/queue-dynamics-vector';\nimport { queueVelocityAssignment } from '../actions/queue-velocity-assignment';\n\nlet nextCommandQueueId = 1;\n\nexport type { VelocityAssignment } from '../actions/dynamics-commands';\n\nexport interface PhysicsSystemConfig<\n\tC extends ComponentMap & PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> extends Partial<SystemConfig> {\n\t// Worker entry point that runs the update off the main thread. Bundlers need the `new Worker(new URL(...))`\n\t// call written out in the game's own source, so the game supplies the file:\n\t//\n\t// getWorker: () => new Worker(new URL('./physics.worker.ts', import.meta.url), { type: 'module' })\n\t//\n\t// Left off, the simulation runs in-process on the main thread instead.\n\tgetWorker?: () => Worker\n\t// Forces the in-process worker even with a getWorker. Defaults to true when no getWorker was given.\n\tforceMainThread?: boolean\n\t// The update to run, defaulting to plain movement that walks through anything in its way. Build one with\n\t// createPhysicsUpdate for collisions and pass the same function the worker file hands to createEntitySystemWorker\n\t// so both backends match; it stamps `optional` and the collidable query on, so they need not be repeated here.\n\tupdateFunction?: EntityUpdateFunction<C, T, W> & { physics?: PhysicsUpdateMetadata<C> }\n\t// Extra components to send with each moving entity, for an update built by hand. Overrides what the update\n\t// function declared.\n\toptional?: Array<keyof C & string>\n\t// Whether to gather the collidable query for the update's collision detection. Defaults to what the update\n\t// declared: on for createPhysicsUpdate, off for the bare physicsUpdate.\n\tcollision?: boolean\n\t// Whether each run reports what moved through POSITION_UPDATED_EVENT. Defaults to whether anything is\n\t// listening, checked per run. Worth a setting because the worker pays for it - an id per moved entity, cloned\n\t// back across the boundary - so force it `true` only for a listener this system cannot see.\n\treportMoves?: boolean\n\t// Narrows which entities this system moves to a subset, for spreading one simulation across several workers:\n\t// give each the same update but a shard filter (`entity => entity.eid % workers === n`). The collidable query\n\t// and any extra `queries` are gathered whole regardless, so every shard sweeps against the whole world.\n\tfilter?: (entity: BaseEntity<C>) => boolean\n\t// Restricts this system to one group of the world - a solar system, a level - so it never sees, moves, or\n\t// collides against anything outside it. Unlike `filter` (which shards movers but still sweeps the whole world),\n\t// `scope` narrows the collidable set too, so an out-of-scope body never enters this system's broadphase and two\n\t// overlapping groups cannot collide across the boundary. Give each group its own PhysicsSystem with its own\n\t// `scope` (`entity => entity.components.group?.groupId === thisId`). Combined with `filter` an entity must pass\n\t// both to be moved. Extra `queries` are left whole - scope them in the caller if a group-local read is wanted.\n\tscope?: (entity: BaseEntity<C>) => boolean\n\t// Extra queries sent to the worker alongside the collidable one, for an update that reads more of the world\n\t// than the entity it moves (e.g. flocking). Read by name off `queries`, gathered whole regardless of `filter`,\n\t// and merged with the collidable query rather than replacing it.\n\tqueries?: { [key: string]: EntityWorkerSystemQuery<C> }\n\t// For a grouped update (see `group` on createPhysicsUpdate)\n\tskipGroup?: number\n}\n\n// Moves every entity with a transform and a velocity, and (when the update detects collisions) reports overlaps\n// to the game's callback. Generic over `C` the game's component map, `T` the blocks its update touches, and `W`\n// the per-run data object.\nexport default class PhysicsSystem<\n\tC extends ComponentMap & PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C> = PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> extends EntityWorkerSystem<C, T, W> {\n\tprivate spatialWorld: PhysicalWorldSource;\n\tprivate pendingDynamicsCommands: PendingDynamicsCommands = new Map();\n\tprivate readonly commandQueueId = nextCommandQueueId++;\n\t// Bumped per run and stamped onto every interpolated entity so a renderer can tell a blended position from one\n\t// physics just replaced. Only has to change; a Float32 holds integers to 2^24, over nine days at a 50ms step.\n\ttick = 0;\n\tprivate readonly interpolationChannel: number;\n\n\t// Undefined means \"whenever something is listening\", worked out per run by addDataToWorld.\n\treportMoves: boolean | undefined;\n\n\t// The group left untouched each run, sent to the worker per run via addDataToWorld\n\tskipGroup: number | undefined;\n\n\tconstructor(world: BaseWorld<ComponentDefinitionMap, C> & PhysicalWorldSource, options: PhysicsSystemConfig<C, T, W> = {}) {\n\t\tconst updateFunction: EntityUpdateFunction<C, T, W> & { physics?: PhysicsUpdateMetadata<C> } = options.updateFunction ?? physicsUpdate;\n\t\tconst optional = options.optional ?? updateFunction.physics?.optional ?? [];\n\t\tconst collision = options.collision ?? updateFunction.physics?.collision ?? false;\n\t\tconst integratesDynamics = updateFunction === physicsUpdate || updateFunction.physics?.dynamics === true;\n\t\t// A moving entity reads its own body (collide category/mask) and bounciness before it searches, so both\n\t\t// travel with movers too - but only on the collision path, or they are blocks per mover for nothing.\n\t\t// Interpolation goes along unconditionally, since the update must publish into it and nothing flags which\n\t\t// entities have one. All are free for entities that lack them: the ECS only sends a block it actually holds.\n\t\tconst group = updateFunction.physics?.group;\n\n\t\tconst extraOptional: Array<keyof C & string> = [];\n\t\tif(collision && !optional.includes('body')) {\n\t\t\textraOptional.push('body');\n\t\t}\n\t\tif(collision && !optional.includes('bounciness')) {\n\t\t\textraOptional.push('bounciness');\n\t\t}\n\t\tif(collision && !optional.includes('polygon')) {\n\t\t\textraOptional.push('polygon');\n\t\t}\n\t\tif(!optional.includes('interpolation')) {\n\t\t\textraOptional.push('interpolation');\n\t\t}\n\t\tif(integratesDynamics && !optional.includes('dynamics')) {\n\t\t\textraOptional.push('dynamics');\n\t\t}\n\t\tif(collision) {\n\t\t\textraOptional.push('entity');\n\t\t}\n\t\t// The group block travels with every mover so the update can read its group id and honour skipGroup even\n\t\t// where nothing collides.\n\t\tif(group && !optional.includes(group.component)) {\n\t\t\textraOptional.push(group.component);\n\t\t}\n\t\tconst movingOptional = extraOptional.length ? [...extraOptional, ...optional] : optional;\n\n\t\t// Collision is between anything with a transform and a body, not only movers, so a ship can hit a station.\n\t\t// Gathered only when something will read it, since it ships a block per entity in the world. Merged with\n\t\t// the game's own `queries`, and neither is narrowed by the shard `filter`.\n\t\t// `bounciness` has to be on this side too, not just the movers: a contact is resolved once and turns both\n\t\t// sides around by their own bounciness, and the broadphase decides from these blocks whether anything in the\n\t\t// run can bounce at all. Left off, hasBounciness is never true and nothing ever bounces.\n\t\tconst collidableOptional = optional.includes('bounciness') ? [...optional] : ['bounciness', ...optional];\n\t\tif(!collidableOptional.includes('polygon')) {\n\t\t\tcollidableOptional.push('polygon');\n\t\t}\n\t\tif(integratesDynamics && !collidableOptional.includes('dynamics')) {\n\t\t\tcollidableOptional.push('dynamics');\n\t\t}\n\t\t// The group block on the collidable side too, so preRun can bucket the broadphase by group.\n\t\tif(group && !collidableOptional.includes(group.component)) {\n\t\t\tcollidableOptional.push(group.component);\n\t\t}\n\t\tconst collidableQuery = collision ? {\n\t\t\t[COLLIDABLE_QUERY]: {\n\t\t\t\trequired: ['transform', 'body'] as Array<keyof C>,\n\t\t\t\toptional: ['velocity', 'entity', ...collidableOptional] as Array<keyof C>,\n\t\t\t\t// Scope narrows the collidable set as well as the movers, so an out-of-scope body never enters the\n\t\t\t\t// broadphase; the shard `filter` deliberately does not, since every shard sweeps the whole group.\n\t\t\t\tfilter: options.scope,\n\t\t\t},\n\t\t} : undefined;\n\t\tconst queries = collidableQuery || options.queries ? { ...collidableQuery, ...options.queries } : undefined;\n\n\t\t// A moving entity must be in scope and in this shard: scope bounds the group, filter shards it across workers.\n\t\tconst moverFilter = combineFilters(options.scope, options.filter);\n\n\t\tsuper(world, {\n\t\t\tname: options.name ?? 'PhysicsSystem',\n\t\t\tdeltaBetweenRuns: options.deltaBetweenRuns ?? DEFAULT_PHYSICS_STEP_MS,\n\t\t\tfirstRun: options.firstRun,\n\n\t\t\t// Movement needs both; an entity with only one is not moved, though it can still be collided with.\n\t\t\trequired: ['transform', 'velocity'],\n\t\t\toptional: movingOptional,\n\t\t\tfilter: moverFilter,\n\t\t\tupdateFunction,\n\t\t\tqueries,\n\n\t\t\tforceMainThread: options.forceMainThread ?? !options.getWorker,\n\t\t\t// Only reached when a getWorker was supplied: without one forceMainThread defaults to true.\n\t\t\tgetWorker: options.getWorker ?? (() => {\n\t\t\t\tthrow new Error('PhysicsSystem cannot start a worker: no getWorker was supplied');\n\t\t\t}),\n\t\t});\n\n\t\tthis.spatialWorld = world;\n\t\tthis.reportMoves = options.reportMoves;\n\t\tthis.skipGroup = options.skipGroup;\n\t\tthis.interpolationChannel = registerInterpolationChannel(world, this);\n\t}\n\n\t// A worker run is still publishing until its reply lands, so its tick is not committed until then.\n\tget committedTick(): number {\n\t\treturn this.isCurrentlyRunning() ? this.tick - 1 : this.tick;\n\t}\n\n\tdestroy(): void {\n\t\tunregisterInterpolationChannel(this.world, this.interpolationChannel);\n\t\tsuper.destroy();\n\t}\n\n\t// Seeds a just-spawned, already-moving entity's interpolation so a renderer draws it leaving its spawn point\n\t// from the next frame rather than parked there until this system's first step reaches it.\n\tstartInterpolation(entity: SpawnableEntity): void {\n\t\tconst step = this.deltaBetweenRuns;\n\t\tif(step <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tstartSpawnInterpolation(entity, {\n\t\t\tstepMs: step,\n\t\t\tstepFraction: this.currentDelta / step,\n\t\t\ttick: this.tick,\n\t\t});\n\t}\n\n\tqueueForce(entity: BaseEntity<C> | number, forceX: number, forceY: number): void {\n\t\tthis.queueDynamics(entity, forceX, forceY, 0, 0);\n\t}\n\n\tqueueImpulse(entity: BaseEntity<C> | number, impulseX: number, impulseY: number): void {\n\t\tthis.queueDynamics(entity, 0, 0, impulseX, impulseY);\n\t}\n\n\tqueueVelocity(entity: BaseEntity<C> | number, velocity: VelocityAssignment): void {\n\t\tconst entityId = typeof entity === 'number' ? entity : entity.eid;\n\t\tqueueVelocityAssignment(this.pendingDynamicsCommands, entityId, velocity);\n\t}\n\n\t// Stamps the run with its step number and whether to report what moved. A subclass overriding this to attach\n\t// its own data must call `super.addDataToWorld(world)`, or `tick` stays undefined and every interpolated\n\t// entity looks to a renderer like it has never had a physics step.\n\taddDataToWorld(world: W): void {\n\t\tworld.tick = ++this.tick;\n\t\tworld.interpolationChannel = this.interpolationChannel;\n\t\taddPhysicalWorldData(this.spatialWorld, world);\n\t\t// Asked per run so a game can attach and drop the listener freely.\n\t\tworld.reportMoves = this.reportMoves ?? this.listenerCount(POSITION_UPDATED_EVENT) > 0;\n\t\t// Sent every run so a game can flip which group is handed off (a jump) between runs with no rebuild.\n\t\tworld.skipGroup = this.skipGroup;\n\t\tworld.commandQueueId = this.commandQueueId;\n\t\tworld.dynamicsCommands = this.snapshotDynamicsCommands();\n\t}\n\n\tprivate queueDynamics(\n\t\tentity: BaseEntity<C> | number,\n\t\tforceX: number,\n\t\tforceY: number,\n\t\timpulseX: number,\n\t\timpulseY: number,\n\t): void {\n\t\tconst entityId = typeof entity === 'number' ? entity : entity.eid;\n\t\tqueueDynamicsVector(this.pendingDynamicsCommands, entityId, forceX, forceY, impulseX, impulseY);\n\t}\n\n\tprivate snapshotDynamicsCommands(): DynamicsCommandBuffer | undefined {\n\t\tif(this.pendingDynamicsCommands.size === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst pending = this.pendingDynamicsCommands;\n\t\tthis.pendingDynamicsCommands = new Map();\n\n\t\tconst entityIds = Array.from(pending.keys()).sort((a, b) => a - b);\n\t\tconst snapshot = new Float64Array(entityIds.length * DYNAMICS_COMMAND_STRIDE);\n\t\tfor(let index = 0; index < entityIds.length; index++) {\n\t\t\tconst entityId = entityIds[index];\n\t\t\tconst command = pending.get(entityId)!;\n\t\t\tconst offset = index * DYNAMICS_COMMAND_STRIDE;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_ENTITY_ID_OFFSET] = entityId;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_FORCE_X_OFFSET] = command.forceX;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_FORCE_Y_OFFSET] = command.forceY;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET] = command.impulseX;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET] = command.impulseY;\n\t\t\tconst velocityMask = (command.velocityX === undefined ? 0 : DYNAMICS_COMMAND_VELOCITY_X_FLAG)\n\t\t\t\t| (command.velocityY === undefined ? 0 : DYNAMICS_COMMAND_VELOCITY_Y_FLAG);\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET] = velocityMask;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET] = command.velocityX ?? 0;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET] = command.velocityY ?? 0;\n\t\t}\n\n\t\treturn snapshot;\n\t}\n}\n\n// Ands two optional entity filters. Either alone is handed straight back; only both present pays for a closure.\nfunction combineFilters<C extends ComponentMap & PhysicsComponents>(\n\ta: ((entity: BaseEntity<C>) => boolean) | undefined,\n\tb: ((entity: BaseEntity<C>) => boolean) | undefined,\n): ((entity: BaseEntity<C>) => boolean) | undefined {\n\tif(!a) {\n\t\treturn b;\n\t}\n\tif(!b) {\n\t\treturn a;\n\t}\n\n\treturn entity => a(entity) && b(entity);\n}\n","// World-space axis-aligned box. Edges are exclusive, so a blocker ending exactly on a tile edge stays out of the next tile.\nexport interface Rect {\n\tminX: number\n\tminY: number\n\tmaxX: number\n\tmaxY: number\n}\n\nexport interface TileRange {\n\tfromX: number\n\tfromY: number\n\ttoX: number\n\ttoY: number\n}\n\nconst NO_REGION = 0;\n\n// Walkability and terrain cost per square tile, with (0, 0) the map's top-left corner. Blockers are counted rather\n// than flagged so overlapping blockers can be removed in any order.\nexport default class NavGrid {\n\treadonly tilesWide: number;\n\treadonly tilesHigh: number;\n\treadonly cellSize: number;\n\tprivate blockers: Uint16Array;\n\tprivate speeds: Float32Array;\n\t// Only ever raised, so the search heuristic stays admissible after a fast road is repaved as mud.\n\tprivate fastestSpeed = 1;\n\t// 4-connected open areas, rebuilt lazily after blockers change. A diagonal step needs both orthogonal tiles open,\n\t// so it never joins two regions.\n\tprivate regions: Int32Array;\n\tprivate regionsDirty = true;\n\n\tconstructor(tilesWide: number, tilesHigh: number, cellSize: number) {\n\t\tif(!Number.isInteger(tilesWide) || !Number.isInteger(tilesHigh) || tilesWide <= 0 || tilesHigh <= 0) {\n\t\t\tthrow new Error(`NavGrid needs a positive whole number of tiles, got ${tilesWide}x${tilesHigh}`);\n\t\t}\n\t\tif(!(cellSize > 0)) {\n\t\t\tthrow new Error(`NavGrid needs a positive cell size, got ${cellSize}`);\n\t\t}\n\t\tthis.tilesWide = tilesWide;\n\t\tthis.tilesHigh = tilesHigh;\n\t\tthis.cellSize = cellSize;\n\t\tthis.blockers = new Uint16Array(tilesWide * tilesHigh);\n\t\tthis.speeds = new Float32Array(tilesWide * tilesHigh).fill(1);\n\t\tthis.regions = new Int32Array(tilesWide * tilesHigh);\n\t}\n\n\tclone(): NavGrid {\n\t\tconst copy = new NavGrid(this.tilesWide, this.tilesHigh, this.cellSize);\n\t\tcopy.blockers.set(this.blockers);\n\t\tcopy.speeds.set(this.speeds);\n\t\tcopy.fastestSpeed = this.fastestSpeed;\n\t\treturn copy;\n\t}\n\n\tget tileCount(): number {\n\t\treturn this.tilesWide * this.tilesHigh;\n\t}\n\n\tget maxSpeed(): number {\n\t\treturn this.fastestSpeed;\n\t}\n\n\tinBounds(tileX: number, tileY: number): boolean {\n\t\treturn tileX >= 0 && tileY >= 0 && tileX < this.tilesWide && tileY < this.tilesHigh;\n\t}\n\n\tindex(tileX: number, tileY: number): number {\n\t\treturn tileY * this.tilesWide + tileX;\n\t}\n\n\ttileOf(world: number): number {\n\t\treturn Math.floor(world / this.cellSize);\n\t}\n\n\ttileCenter(tile: number): number {\n\t\treturn (tile + 0.5) * this.cellSize;\n\t}\n\n\t// Off the map counts as blocked.\n\tisBlocked(tileX: number, tileY: number): boolean {\n\t\treturn !this.inBounds(tileX, tileY) || this.blockers[this.index(tileX, tileY)] > 0;\n\t}\n\n\tisAreaClear(tileX: number, tileY: number, tilesWide: number, tilesHigh: number): boolean {\n\t\tif(!this.inBounds(tileX, tileY) || !this.inBounds(tileX + tilesWide - 1, tileY + tilesHigh - 1)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor(let y = tileY; y < tileY + tilesHigh; y++) {\n\t\t\tfor(let x = tileX; x < tileX + tilesWide; x++) {\n\t\t\t\tif(this.blockers[this.index(x, y)] > 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tblockerCount(tileX: number, tileY: number): number {\n\t\treturn this.inBounds(tileX, tileY) ? this.blockers[this.index(tileX, tileY)] : 0;\n\t}\n\n\t// The tiles a rect has area in, clamped to the map; empty (toX < fromX) for a rect with no area or off the map.\n\ttilesIn(rect: Rect): TileRange {\n\t\tif(!(rect.maxX > rect.minX) || !(rect.maxY > rect.minY)) {\n\t\t\treturn { fromX: 0, fromY: 0, toX: -1, toY: -1 };\n\t\t}\n\t\treturn {\n\t\t\tfromX: Math.max(0, Math.floor(rect.minX / this.cellSize)),\n\t\t\tfromY: Math.max(0, Math.floor(rect.minY / this.cellSize)),\n\t\t\ttoX: Math.min(this.tilesWide - 1, Math.ceil(rect.maxX / this.cellSize) - 1),\n\t\t\ttoY: Math.min(this.tilesHigh - 1, Math.ceil(rect.maxY / this.cellSize) - 1),\n\t\t};\n\t}\n\n\taddBlocker(rect: Rect): void {\n\t\tconst range = this.tilesIn(rect);\n\t\tfor(let y = range.fromY; y <= range.toY; y++) {\n\t\t\tfor(let x = range.fromX; x <= range.toX; x++) {\n\t\t\t\tconst i = this.index(x, y);\n\t\t\t\tif(this.blockers[i] < 0xFFFF) {\n\t\t\t\t\tthis.blockers[i]++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.regionsDirty = true;\n\t}\n\n\t// Must be given the same rect that was added.\n\tremoveBlocker(rect: Rect): void {\n\t\tconst range = this.tilesIn(rect);\n\t\tfor(let y = range.fromY; y <= range.toY; y++) {\n\t\t\tfor(let x = range.fromX; x <= range.toX; x++) {\n\t\t\t\tconst i = this.index(x, y);\n\t\t\t\tif(this.blockers[i] > 0) {\n\t\t\t\t\tthis.blockers[i]--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.regionsDirty = true;\n\t}\n\n\t// A multiplier on walking speed: 1.5 for a road, 0.5 for mud.\n\tsetTerrainSpeed(tileX: number, tileY: number, speed: number): void {\n\t\tif(!(speed > 0) || !Number.isFinite(speed)) {\n\t\t\tthrow new Error(`Terrain speed must be a positive finite number, got ${speed}`);\n\t\t}\n\t\tif(!this.inBounds(tileX, tileY)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.speeds[this.index(tileX, tileY)] = speed;\n\t\tthis.fastestSpeed = Math.max(this.fastestSpeed, speed);\n\t}\n\n\tterrainSpeed(tileX: number, tileY: number): number {\n\t\treturn this.inBounds(tileX, tileY) ? this.speeds[this.index(tileX, tileY)] : 1;\n\t}\n\n\t// Time-weighted cost of crossing a tile edge to edge.\n\tcost(tileX: number, tileY: number): number {\n\t\treturn this.cellSize / this.speeds[this.index(tileX, tileY)];\n\t}\n\n\t// 0 for a blocked or off-map tile.\n\tregion(tileX: number, tileY: number): number {\n\t\tif(!this.inBounds(tileX, tileY)) {\n\t\t\treturn NO_REGION;\n\t\t}\n\t\tif(this.regionsDirty) {\n\t\t\tthis.rebuildRegions();\n\t\t}\n\t\treturn this.regions[this.index(tileX, tileY)];\n\t}\n\n\tprivate rebuildRegions(): void {\n\t\tconst regions = this.regions;\n\t\tregions.fill(NO_REGION);\n\t\tconst queue = new Int32Array(this.tileCount);\n\t\tlet next = NO_REGION;\n\t\tfor(let start = 0; start < regions.length; start++) {\n\t\t\tif(regions[start] !== NO_REGION || this.blockers[start] > 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tnext++;\n\t\t\tregions[start] = next;\n\t\t\tlet head = 0;\n\t\t\tlet tail = 0;\n\t\t\tqueue[tail++] = start;\n\t\t\twhile(head < tail) {\n\t\t\t\tconst i = queue[head++];\n\t\t\t\tconst x = i % this.tilesWide;\n\t\t\t\tconst y = (i - x) / this.tilesWide;\n\t\t\t\ttail = this.flood(x - 1, y, next, queue, tail);\n\t\t\t\ttail = this.flood(x + 1, y, next, queue, tail);\n\t\t\t\ttail = this.flood(x, y - 1, next, queue, tail);\n\t\t\t\ttail = this.flood(x, y + 1, next, queue, tail);\n\t\t\t}\n\t\t}\n\t\tthis.regionsDirty = false;\n\t}\n\n\tprivate flood(x: number, y: number, region: number, queue: Int32Array, tail: number): number {\n\t\tif(!this.inBounds(x, y)) {\n\t\t\treturn tail;\n\t\t}\n\t\tconst i = this.index(x, y);\n\t\tif(this.regions[i] !== NO_REGION || this.blockers[i] > 0) {\n\t\t\treturn tail;\n\t\t}\n\t\tthis.regions[i] = region;\n\t\tqueue[tail] = i;\n\t\treturn tail + 1;\n\t}\n}","// Binary min-heap of integer ids ordered by a primary then secondary key. Keys are captured on push, so an id pushed\n// again with a better key simply sits beside its stale entry; the caller skips stale pops.\nexport default class MinHeap {\n\tprivate ids: Int32Array;\n\tprivate primary: Float64Array;\n\tprivate secondary: Float64Array;\n\tprivate count = 0;\n\n\tconstructor(capacity = 64) {\n\t\tthis.ids = new Int32Array(capacity);\n\t\tthis.primary = new Float64Array(capacity);\n\t\tthis.secondary = new Float64Array(capacity);\n\t}\n\n\tget size(): number {\n\t\treturn this.count;\n\t}\n\n\tclear(): void {\n\t\tthis.count = 0;\n\t}\n\n\tpush(id: number, primary: number, secondary = 0): void {\n\t\tif(this.count === this.ids.length) {\n\t\t\tthis.grow();\n\t\t}\n\t\tlet i = this.count++;\n\t\twhile(i > 0) {\n\t\t\tconst parent = (i - 1) >> 1;\n\t\t\tif(!this.less(primary, secondary, this.primary[parent], this.secondary[parent])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.set(i, this.ids[parent], this.primary[parent], this.secondary[parent]);\n\t\t\ti = parent;\n\t\t}\n\t\tthis.set(i, id, primary, secondary);\n\t}\n\n\t// -1 when empty.\n\tpop(): number {\n\t\tif(this.count === 0) {\n\t\t\treturn -1;\n\t\t}\n\t\tconst top = this.ids[0];\n\t\tconst last = --this.count;\n\t\tif(last > 0) {\n\t\t\tconst id = this.ids[last];\n\t\t\tconst primary = this.primary[last];\n\t\t\tconst secondary = this.secondary[last];\n\t\t\tlet i = 0;\n\t\t\tfor(;;) {\n\t\t\t\tconst left = i * 2 + 1;\n\t\t\t\tif(left >= last) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst right = left + 1;\n\t\t\t\tconst child = right < last && this.less(this.primary[right], this.secondary[right], this.primary[left], this.secondary[left]) ? right : left;\n\t\t\t\tif(!this.less(this.primary[child], this.secondary[child], primary, secondary)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tthis.set(i, this.ids[child], this.primary[child], this.secondary[child]);\n\t\t\t\ti = child;\n\t\t\t}\n\t\t\tthis.set(i, id, primary, secondary);\n\t\t}\n\t\treturn top;\n\t}\n\n\tprivate less(primaryA: number, secondaryA: number, primaryB: number, secondaryB: number): boolean {\n\t\treturn primaryA < primaryB || (primaryA === primaryB && secondaryA < secondaryB);\n\t}\n\n\tprivate set(i: number, id: number, primary: number, secondary: number): void {\n\t\tthis.ids[i] = id;\n\t\tthis.primary[i] = primary;\n\t\tthis.secondary[i] = secondary;\n\t}\n\n\tprivate grow(): void {\n\t\tconst capacity = this.ids.length * 2;\n\t\tconst ids = new Int32Array(capacity);\n\t\tconst primary = new Float64Array(capacity);\n\t\tconst secondary = new Float64Array(capacity);\n\t\tids.set(this.ids);\n\t\tprimary.set(this.primary);\n\t\tsecondary.set(this.secondary);\n\t\tthis.ids = ids;\n\t\tthis.primary = primary;\n\t\tthis.secondary = secondary;\n\t}\n}","import type NavGrid from './nav-grid';\n\n// Slowest terrain (cost per world unit, i.e. 1 / speed) any tile touched by a box of half-size (halfWidth,\n// halfHeight) sliding from (x0, y0) to (x1, y1), or Infinity if it touches a blocked or off-map tile. Touching a\n// tile's edge does not count, matching physics, where touching is not overlapping.\nexport function sweepCost(grid: NavGrid, x0: number, y0: number, x1: number, y1: number, halfWidth: number, halfHeight: number): number {\n\tconst cell = grid.cellSize;\n\tconst dx = x1 - x0;\n\tconst dy = y1 - y0;\n\tconst reachY = halfHeight + cell / 2;\n\tconst fromRow = Math.floor((Math.min(y0, y1) - halfHeight) / cell);\n\tconst toRow = Math.ceil((Math.max(y0, y1) + halfHeight) / cell) - 1;\n\tlet worst = 0;\n\tfor(let row = fromRow; row <= toRow; row++) {\n\t\t// When during the slide the box overlaps this row: an open interval of t.\n\t\tconst centerY = (row + 0.5) * cell;\n\t\tlet tLow: number;\n\t\tlet tHigh: number;\n\t\tif(dy === 0) {\n\t\t\tif(Math.abs(y0 - centerY) >= reachY) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttLow = -Infinity;\n\t\t\ttHigh = Infinity;\n\t\t} else {\n\t\t\tconst a = (centerY - reachY - y0) / dy;\n\t\t\tconst b = (centerY + reachY - y0) / dy;\n\t\t\ttLow = Math.min(a, b);\n\t\t\ttHigh = Math.max(a, b);\n\t\t}\n\t\tconst tFrom = Math.max(tLow, 0);\n\t\tconst tTo = Math.min(tHigh, 1);\n\t\tif(tFrom > tTo || (tFrom === tTo && !(tLow < tFrom && tTo < tHigh))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst xa = x0 + dx * tFrom;\n\t\tconst xb = x0 + dx * tTo;\n\t\tconst fromColumn = Math.floor((Math.min(xa, xb) - halfWidth) / cell);\n\t\tconst toColumn = Math.ceil((Math.max(xa, xb) + halfWidth) / cell) - 1;\n\t\tfor(let column = fromColumn; column <= toColumn; column++) {\n\t\t\tif(grid.isBlocked(column, row)) {\n\t\t\t\treturn Infinity;\n\t\t\t}\n\t\t\tworst = Math.max(worst, 1 / grid.terrainSpeed(column, row));\n\t\t}\n\t}\n\treturn worst;\n}","import { sweepCost } from './line-of-sight';\nimport type NavGrid from './nav-grid';\nimport type { Point } from './path-search';\n\n// Turns checked ahead of each kept point; bounds smoothing to O(turns x LOOKAHEAD) sweeps on a long staircase.\nconst LOOKAHEAD = 24;\n\n// Terrain-aware string pulling over a walkable polyline (start first). A corner is only cut when the straight line is\n// clear with `clearance` to spare and, costing it at the slowest terrain it touches, is no dearer than the stretch it\n// replaces, so a path that detoured around mud is not pulled back through it.\nexport function smoothPath(grid: NavGrid, points: ReadonlyArray<Point>, halfWidth: number, halfHeight: number, clearance: number): Array<Point> {\n\tconst polyline = dropDuplicates(points);\n\tif(polyline.length <= 2) {\n\t\treturn polyline;\n\t}\n\n\t// Costed without the clearance: the unsmoothed path legitimately brushes walls.\n\tconst travelled = new Float64Array(polyline.length);\n\tfor(let i = 1; i < polyline.length; i++) {\n\t\ttravelled[i] = travelled[i - 1] + segmentCost(grid, polyline[i - 1], polyline[i], halfWidth, halfHeight);\n\t}\n\n\tconst turns = [0];\n\tfor(let i = 1; i < polyline.length - 1; i++) {\n\t\tif(!isCollinear(polyline[i - 1], polyline[i], polyline[i + 1])) {\n\t\t\tturns.push(i);\n\t\t}\n\t}\n\tturns.push(polyline.length - 1);\n\n\tconst result = [polyline[0]];\n\tlet from = 0;\n\twhile(from < turns.length - 1) {\n\t\tlet to = from + 1;\n\t\t// Visibility is not monotonic (a shortcut can fail while a longer one succeeds), so look the whole window over.\n\t\tconst last = Math.min(turns.length - 1, from + LOOKAHEAD);\n\t\tfor(let candidate = from + 2; candidate <= last; candidate++) {\n\t\t\tconst a = polyline[turns[from]];\n\t\t\tconst b = polyline[turns[candidate]];\n\t\t\tconst perUnit = sweepCost(grid, a.x, a.y, b.x, b.y, halfWidth + clearance, halfHeight + clearance);\n\t\t\tconst straight = Math.hypot(b.x - a.x, b.y - a.y) * perUnit;\n\t\t\t// Tolerance for float noise; any real saving or loss is whole world units.\n\t\t\tif(Number.isFinite(straight) && straight <= travelled[turns[candidate]] - travelled[turns[from]] + 1e-6) {\n\t\t\t\tto = candidate;\n\t\t\t}\n\t\t}\n\t\tresult.push(polyline[turns[to]]);\n\t\tfrom = to;\n\t}\n\treturn result;\n}\n\nfunction segmentCost(grid: NavGrid, a: Point, b: Point, halfWidth: number, halfHeight: number): number {\n\tconst length = Math.hypot(b.x - a.x, b.y - a.y);\n\tconst perUnit = sweepCost(grid, a.x, a.y, b.x, b.y, halfWidth, halfHeight);\n\t// Legs off a blocked start still need a price; the tile walked into is the best guess.\n\treturn length * (Number.isFinite(perUnit) ? perUnit : 1 / grid.terrainSpeed(grid.tileOf(b.x), grid.tileOf(b.y)));\n}\n\nfunction isCollinear(a: Point, b: Point, c: Point): boolean {\n\treturn Math.abs((b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x)) < 1e-9 &&\n\t\t(b.x - a.x) * (c.x - b.x) + (b.y - a.y) * (c.y - b.y) > 0;\n}\n\nfunction dropDuplicates(points: ReadonlyArray<Point>): Array<Point> {\n\tconst result: Array<Point> = [];\n\tfor(const point of points) {\n\t\tconst last = result[result.length - 1];\n\t\tif(!last || last.x !== point.x || last.y !== point.y) {\n\t\t\tresult.push(point);\n\t\t}\n\t}\n\treturn result;\n}","import MinHeap from './min-heap';\nimport type NavGrid from './nav-grid';\nimport { PathStatus, type PathRequest, type PathResult, type PathSearch, type PathStatusValue, type Point } from './path-search';\nimport { smoothPath } from './smooth-path';\nimport { sweepCost } from './line-of-sight';\n\nexport interface GridAStarOptions {\n\t// 8-way movement. Diagonals never squeeze between two blocked tiles. Defaults to 4-way.\n\tdiagonal?: boolean\n\t// Tiles expanded before giving up with an Incomplete path.\n\tmaxIterations?: number\n\tsmooth?: boolean\n\t// Extra room a smoothed shortcut must leave around blockers and the map edge. Defaults to 0: physics slides a mover\n\t// off a corner it grazes.\n\tclearance?: number\n\t// How far (in tiles) a blocked start or goal is moved to find open ground.\n\trelocateRadius?: number\n}\n\nconst DEFAULT_MAX_ITERATIONS = 50_000;\nconst DEFAULT_CLEARANCE = 0;\nconst DEFAULT_RELOCATE_RADIUS = 16;\nconst ORTHOGONAL = [[-1, 0], [1, 0], [0, -1], [0, 1]] as const;\nconst DIAGONAL = [[-1, -1], [1, -1], [-1, 1], [1, 1]] as const;\n\ninterface Anchor {\n\tx: number\n\ty: number\n}\n\n// A* over tiles. A mover is placed by the top-left tile of its footprint (its \"anchor\"), so a mover bigger than a tile\n// only visits anchors where its whole footprint is open. Scratch arrays are reused across searches and stamped rather\n// than cleared, so a search only pays for the tiles it touches.\nexport default class GridAStar implements PathSearch {\n\tprivate readonly diagonal: boolean;\n\tprivate readonly maxIterations: number;\n\tprivate readonly smooth: boolean;\n\tprivate readonly clearance: number;\n\tprivate readonly relocateRadius: number;\n\tprivate readonly heap = new MinHeap(256);\n\tprivate cost = new Float64Array(0);\n\tprivate parent = new Int32Array(0);\n\tprivate seen = new Uint32Array(0);\n\tprivate closed = new Uint32Array(0);\n\tprivate stamp = 0;\n\n\tconstructor(options: GridAStarOptions = {}) {\n\t\tthis.diagonal = options.diagonal ?? false;\n\t\tthis.maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n\t\tthis.smooth = options.smooth ?? true;\n\t\tthis.clearance = options.clearance ?? DEFAULT_CLEARANCE;\n\t\tthis.relocateRadius = options.relocateRadius ?? DEFAULT_RELOCATE_RADIUS;\n\t}\n\n\tfindPath(grid: NavGrid, request: PathRequest): PathResult {\n\t\tconst cell = grid.cellSize;\n\t\tconst tilesWide = Math.max(1, Math.ceil(request.agentWidth / cell - 1e-9));\n\t\tconst tilesHigh = Math.max(1, Math.ceil(request.agentHeight / cell - 1e-9));\n\t\tif(tilesWide > grid.tilesWide || tilesHigh > grid.tilesHigh) {\n\t\t\treturn { status: PathStatus.Unreachable, points: [], iterations: 0 };\n\t\t}\n\t\tconst anchorOf = (x: number, y: number): Anchor => ({\n\t\t\tx: Math.round((x - tilesWide * cell / 2) / cell),\n\t\t\ty: Math.round((y - tilesHigh * cell / 2) / cell),\n\t\t});\n\t\tconst onMap = (anchor: Anchor): Anchor => ({\n\t\t\tx: clamp(anchor.x, 0, grid.tilesWide - tilesWide),\n\t\t\ty: clamp(anchor.y, 0, grid.tilesHigh - tilesHigh),\n\t\t});\n\t\tconst centerOf = (anchor: Anchor): Point => ({ x: (anchor.x + tilesWide / 2) * cell, y: (anchor.y + tilesHigh / 2) * cell });\n\n\t\tconst start = this.relocate(grid, onMap(anchorOf(request.startX, request.startY)), tilesWide, tilesHigh, request.startX, request.startY, request.goalX, request.goalY);\n\t\t// Off the map counts as moved, so an off-map goal is never walked to.\n\t\tconst requestedGoal = anchorOf(request.goalX, request.goalY);\n\t\tconst goal = this.relocate(grid, onMap(requestedGoal), tilesWide, tilesHigh, request.goalX, request.goalY, request.startX, request.startY);\n\t\tif(!start || !goal || grid.region(start.x, start.y) !== grid.region(goal.x, goal.y)) {\n\t\t\treturn { status: PathStatus.Unreachable, points: [], iterations: 0 };\n\t\t}\n\t\tconst goalMoved = goal.x !== requestedGoal.x || goal.y !== requestedGoal.y;\n\n\t\tconst search = this.search(grid, start, goal, tilesWide, tilesHigh);\n\t\tconst polyline: Array<Point> = [{ x: request.startX, y: request.startY }];\n\t\tfor(const index of search.tiles) {\n\t\t\tpolyline.push(centerOf({ x: index % grid.tilesWide, y: Math.floor(index / grid.tilesWide) }));\n\t\t}\n\n\t\tlet status: PathStatusValue = search.status;\n\t\tif(status === PathStatus.Found) {\n\t\t\tconst destination = centerOf(goal);\n\t\t\tif(goalMoved || !Number.isFinite(sweepCost(grid, destination.x, destination.y, request.goalX, request.goalY,\n\t\t\t\trequest.agentWidth / 2, request.agentHeight / 2))) {\n\t\t\t\tstatus = PathStatus.Unreachable;\n\t\t\t} else {\n\t\t\t\tpolyline.push({ x: request.goalX, y: request.goalY });\n\t\t\t}\n\t\t}\n\n\t\tconst halfWidth = request.agentWidth / 2;\n\t\tconst halfHeight = request.agentHeight / 2;\n\t\tconst points = this.smooth ? smoothPath(grid, polyline, halfWidth, halfHeight, this.clearance) : withoutRepeats(polyline);\n\t\treturn { status, points: points.slice(1), iterations: search.iterations };\n\t}\n\n\t// The open anchor nearest `toward` within relocateRadius rings of `anchor`, ties going to the one nearer `prefer`.\n\tprivate relocate(grid: NavGrid, anchor: Anchor, tilesWide: number, tilesHigh: number, towardX: number, towardY: number, preferX: number, preferY: number): Anchor | undefined {\n\t\tif(grid.isAreaClear(anchor.x, anchor.y, tilesWide, tilesHigh)) {\n\t\t\treturn anchor;\n\t\t}\n\t\tconst cell = grid.cellSize;\n\t\tfor(let radius = 1; radius <= this.relocateRadius; radius++) {\n\t\t\tlet best: Anchor | undefined;\n\t\t\tlet bestDistance = Infinity;\n\t\t\tlet bestPreference = Infinity;\n\t\t\tfor(let y = anchor.y - radius; y <= anchor.y + radius; y++) {\n\t\t\t\tconst edge = y === anchor.y - radius || y === anchor.y + radius;\n\t\t\t\tfor(let x = anchor.x - radius; x <= anchor.x + radius; x += edge ? 1 : radius * 2) {\n\t\t\t\t\tif(!grid.isAreaClear(x, y, tilesWide, tilesHigh)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst centerX = (x + tilesWide / 2) * cell;\n\t\t\t\t\tconst centerY = (y + tilesHigh / 2) * cell;\n\t\t\t\t\tconst distance = Math.hypot(centerX - towardX, centerY - towardY);\n\t\t\t\t\tconst preference = Math.hypot(centerX - preferX, centerY - preferY);\n\t\t\t\t\tif(distance < bestDistance || (distance === bestDistance && preference < bestPreference)) {\n\t\t\t\t\t\tbest = { x, y };\n\t\t\t\t\t\tbestDistance = distance;\n\t\t\t\t\t\tbestPreference = preference;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(best) {\n\t\t\t\treturn best;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate search(grid: NavGrid, start: Anchor, goal: Anchor, tilesWide: number, tilesHigh: number): { status: PathStatusValue, tiles: Array<number>, iterations: number } {\n\t\tthis.prepare(grid.tileCount);\n\t\tconst stamp = this.stamp;\n\t\tconst { cost, parent, seen, closed, heap } = this;\n\t\tconst width = grid.tilesWide;\n\t\tconst startIndex = grid.index(start.x, start.y);\n\t\tconst goalIndex = grid.index(goal.x, goal.y);\n\t\t// The cheapest a tile can ever be crossed, so the estimate never overshoots once roads exist.\n\t\tconst cheapestTile = grid.cellSize / grid.maxSpeed;\n\t\tconst estimate = (x: number, y: number) => {\n\t\t\tconst dx = Math.abs(x - goal.x);\n\t\t\tconst dy = Math.abs(y - goal.y);\n\t\t\tconst tiles = this.diagonal ? Math.max(dx, dy) + (Math.SQRT2 - 1) * Math.min(dx, dy) : dx + dy;\n\t\t\treturn tiles * cheapestTile;\n\t\t};\n\n\t\theap.clear();\n\t\tseen[startIndex] = stamp;\n\t\tcost[startIndex] = 0;\n\t\tparent[startIndex] = -1;\n\t\theap.push(startIndex, estimate(start.x, start.y), estimate(start.x, start.y));\n\t\tlet closest = startIndex;\n\t\tlet closestEstimate = estimate(start.x, start.y);\n\t\tlet iterations = 0;\n\n\t\twhile(heap.size > 0) {\n\t\t\tconst current = heap.pop();\n\t\t\tif(closed[current] === stamp) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(current === goalIndex) {\n\t\t\t\treturn { status: PathStatus.Found, tiles: this.trace(current), iterations };\n\t\t\t}\n\t\t\tif(iterations >= this.maxIterations) {\n\t\t\t\treturn { status: PathStatus.Incomplete, tiles: this.trace(closest), iterations };\n\t\t\t}\n\t\t\tclosed[current] = stamp;\n\t\t\titerations++;\n\n\t\t\tconst x = current % width;\n\t\t\tconst y = (current - x) / width;\n\t\t\tconst remaining = estimate(x, y);\n\t\t\tif(remaining < closestEstimate || (remaining === closestEstimate && cost[current] < cost[closest])) {\n\t\t\t\tclosest = current;\n\t\t\t\tclosestEstimate = remaining;\n\t\t\t}\n\n\t\t\tfor(const [dx, dy] of ORTHOGONAL) {\n\t\t\t\tthis.relax(grid, current, x + dx, y + dy, 1, tilesWide, tilesHigh, estimate);\n\t\t\t}\n\t\t\tif(this.diagonal) {\n\t\t\t\tfor(const [dx, dy] of DIAGONAL) {\n\t\t\t\t\tif(grid.isAreaClear(x + dx, y, tilesWide, tilesHigh) && grid.isAreaClear(x, y + dy, tilesWide, tilesHigh)) {\n\t\t\t\t\t\tthis.relax(grid, current, x + dx, y + dy, Math.SQRT2, tilesWide, tilesHigh, estimate);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { status: PathStatus.Unreachable, tiles: this.trace(closest), iterations };\n\t}\n\n\tprivate relax(grid: NavGrid, from: number, x: number, y: number, distance: number, tilesWide: number, tilesHigh: number, estimate: (x: number, y: number) => number): void {\n\t\tif(!grid.isAreaClear(x, y, tilesWide, tilesHigh)) {\n\t\t\treturn;\n\t\t}\n\t\tconst index = grid.index(x, y);\n\t\tif(this.closed[index] === this.stamp) {\n\t\t\treturn;\n\t\t}\n\t\tconst next = this.cost[from] + distance * footprintCost(grid, x, y, tilesWide, tilesHigh);\n\t\tif(this.seen[index] === this.stamp && next >= this.cost[index]) {\n\t\t\treturn;\n\t\t}\n\t\tthis.seen[index] = this.stamp;\n\t\tthis.cost[index] = next;\n\t\tthis.parent[index] = from;\n\t\tconst remaining = estimate(x, y);\n\t\t// Ties go to the node nearer the goal, which keeps equal-cost searches narrow.\n\t\tthis.heap.push(index, next + remaining, remaining);\n\t}\n\n\tprivate trace(index: number): Array<number> {\n\t\tlet length = 0;\n\t\tfor(let i = index; i !== -1; i = this.parent[i]) {\n\t\t\tlength++;\n\t\t}\n\t\tconst tiles = Array.from({ length }, () => 0);\n\t\tfor(let i = index, at = length - 1; i !== -1; i = this.parent[i], at--) {\n\t\t\ttiles[at] = i;\n\t\t}\n\t\treturn tiles;\n\t}\n\n\tprivate prepare(tileCount: number): void {\n\t\tif(this.cost.length !== tileCount) {\n\t\t\tthis.cost = new Float64Array(tileCount);\n\t\t\tthis.parent = new Int32Array(tileCount);\n\t\t\tthis.seen = new Uint32Array(tileCount);\n\t\t\tthis.closed = new Uint32Array(tileCount);\n\t\t\tthis.stamp = 0;\n\t\t}\n\t\tthis.stamp++;\n\t\t// Wrapped after ~4 billion searches: stale stamps could then collide, so start clean.\n\t\tif(this.stamp === 0xFFFFFFFF) {\n\t\t\tthis.seen.fill(0);\n\t\t\tthis.closed.fill(0);\n\t\t\tthis.stamp = 1;\n\t\t}\n\t}\n}\n\n// Mean cost of the tiles a footprint covers, so a big mover half on a road is half as quick as one fully on it.\nfunction footprintCost(grid: NavGrid, x: number, y: number, tilesWide: number, tilesHigh: number): number {\n\tif(tilesWide === 1 && tilesHigh === 1) {\n\t\treturn grid.cost(x, y);\n\t}\n\tlet total = 0;\n\tfor(let ty = y; ty < y + tilesHigh; ty++) {\n\t\tfor(let tx = x; tx < x + tilesWide; tx++) {\n\t\t\ttotal += grid.cost(tx, ty);\n\t\t}\n\t}\n\treturn total / (tilesWide * tilesHigh);\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n\treturn Math.min(max, Math.max(min, value));\n}\n\nfunction withoutRepeats(points: Array<Point>): Array<Point> {\n\treturn points.filter((point, i) => i === 0 || point.x !== points[i - 1].x || point.y !== points[i - 1].y);\n}","import { loadFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport {\n\tNAVIGATION_GOAL_X_INDEX,\n\tNAVIGATION_GOAL_Y_INDEX,\n\tNAVIGATION_OUTCOME_INDEX,\n\tNAVIGATION_WAYPOINT_COUNT_INDEX,\n\tNAVIGATION_WAYPOINT_INDEX,\n} from '../components/navigation-component-constants';\nimport { isPathPending, requestPath, waypointX, waypointY } from '../components/navigation-component';\nimport { PathStatus, type Point } from './path-search';\n\nexport const FollowStep = {\n\t// No path to follow yet, including just after asking again from the end of an incomplete one.\n\tWaiting: 0,\n\t// `target` is the next waypoint.\n\tWaypoint: 1,\n\t// Past the last waypoint of a found path; `target` is the goal.\n\tGoal: 2,\n\t// At the closest reachable spot to an unreachable goal; whoever set it decides what to do next.\n\tStuck: 3,\n} as const;\nexport type FollowStepValue = typeof FollowStep[keyof typeof FollowStep];\n\n// Advances past waypoints within `arriveDistance` of (x, y) and says where to head next. Writes into `target` rather\n// than returning a point so a follower running every step does not allocate.\nexport function followPath(navigation: Int32Array, x: number, y: number, arriveDistance: number, target: Point): FollowStepValue {\n\tif(isPathPending(navigation)) {\n\t\treturn FollowStep.Waiting;\n\t}\n\n\tconst count = navigation[NAVIGATION_WAYPOINT_COUNT_INDEX];\n\tlet index = navigation[NAVIGATION_WAYPOINT_INDEX];\n\twhile(index < count && Math.hypot(waypointX(navigation, index) - x, waypointY(navigation, index) - y) <= arriveDistance) {\n\t\tindex++;\n\t}\n\tnavigation[NAVIGATION_WAYPOINT_INDEX] = index;\n\n\tif(index < count) {\n\t\ttarget.x = waypointX(navigation, index);\n\t\ttarget.y = waypointY(navigation, index);\n\t\treturn FollowStep.Waypoint;\n\t}\n\tconst outcome = navigation[NAVIGATION_OUTCOME_INDEX];\n\tif(outcome === PathStatus.Incomplete) {\n\t\trequestPath(navigation);\n\t\treturn FollowStep.Waiting;\n\t}\n\tif(outcome === PathStatus.Found) {\n\t\ttarget.x = loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX);\n\t\ttarget.y = loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX);\n\t\treturn FollowStep.Goal;\n\t}\n\treturn FollowStep.Stuck;\n}\n","import type { BaseEntity, ComponentMap, EntityWorkerSystemQuery } from '@daneren2005/shared-memory-ecs';\nimport { BODY_FLAGS_INDEX, BODY_SHAPE_MASK } from '../components/body-component-constants';\nimport type { BodyComponent } from '../components/body-component';\nimport {\n\tTRANSFORM_ANGLE_INDEX,\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n} from '../components/transform-component-constants';\nimport type { TransformComponent } from '../components/transform-component';\nimport { shapeHalfHeight, shapeHalfWidth } from '../math/shapes';\nimport type NavGrid from './nav-grid';\nimport type { Rect } from './nav-grid';\n\nexport const PATH_BLOCKERS_QUERY = 'pathBlockers';\n// Bodies in a gate category, which only stop movers whose collide mask takes that category in.\nexport const PATH_GATES_QUERY = 'pathGates';\n\nexport type PathBlockerComponents = {\n\ttransform: TransformComponent\n\tbody: BodyComponent\n};\n\n// Structural so a delta from any game's component map fits.\nexport interface PathBlockerDelta {\n\tadded: ReadonlyArray<{ entityId: number, components: { transform?: unknown, body?: unknown } }>\n\tremoved: ReadonlyArray<number>\n}\n\nexport interface PathBlockerQueryOptions<C extends ComponentMap> {\n\tgateCategories?: number\n\t// Narrows both queries, e.g. `not: ['construction']` so a building site blocks nothing until it is finished.\n\tnot?: Array<keyof C>\n\tfilter?: (entity: BaseEntity<C>) => boolean\n}\n\n// The blocker and gate queries PathfindingSystem builds its grids from, for another system wanting the same view.\nexport function pathBlockerQueries<C extends ComponentMap & PathBlockerComponents>(options: PathBlockerQueryOptions<C> = {}): Record<string, EntityWorkerSystemQuery<C>> {\n\tconst { gateCategories = 0, not, filter } = options;\n\tconst blockers: EntityWorkerSystemQuery<C> = {\n\t\trequired: ['transform', 'body'],\n\t\tnot,\n\t\tfilter: entity => {\n\t\t\tconst body = entity.components.body;\n\t\t\treturn !!body && body.blocksPath && !body.sensor && (!filter || filter(entity));\n\t\t},\n\t};\n\tif(!gateCategories) {\n\t\treturn { [PATH_BLOCKERS_QUERY]: blockers };\n\t}\n\tconst gates: EntityWorkerSystemQuery<C> = {\n\t\trequired: ['transform', 'body'],\n\t\tnot,\n\t\tfilter: entity => {\n\t\t\tconst body = entity.components.body;\n\t\t\treturn !!body && !body.blocksPath && !body.sensor && (body.collideCategory & gateCategories) !== 0 && (!filter || filter(entity));\n\t\t},\n\t};\n\treturn { [PATH_BLOCKERS_QUERY]: blockers, [PATH_GATES_QUERY]: gates };\n}\n\n// The axis-aligned box a body covers. A body turned by a right angle is snapped to it so float noise in the angle does\n// not grow the box into the next tile.\nexport function blockerRect(transform: Float32Array, body: Uint32Array): Rect {\n\tconst shape = body[BODY_FLAGS_INDEX] & BODY_SHAPE_MASK;\n\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst angle = snappedRightAngle(transform[TRANSFORM_ANGLE_INDEX]);\n\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\tconst x = transform[TRANSFORM_X_INDEX];\n\tconst y = transform[TRANSFORM_Y_INDEX];\n\treturn { minX: x - halfWidth, minY: y - halfHeight, maxX: x + halfWidth, maxY: y + halfHeight };\n}\n\n// Blockers are assumed never to move, so each is rasterised once when it joins and cleared by the same rect when it\n// leaves. Returns whether any were added.\nexport function applyRectChanges(rects: Map<number, Rect>, grids: Iterable<NavGrid>, delta: PathBlockerDelta): boolean {\n\tconst targets = [...grids];\n\tfor(const entityId of delta.removed) {\n\t\tconst rect = rects.get(entityId);\n\t\tif(rect) {\n\t\t\ttargets.forEach(grid => grid.removeBlocker(rect));\n\t\t\trects.delete(entityId);\n\t\t}\n\t}\n\tfor(const { entityId, components } of delta.added) {\n\t\t// `added` also re-sends an existing member whose components changed.\n\t\tconst previous = rects.get(entityId);\n\t\tif(previous) {\n\t\t\ttargets.forEach(grid => grid.removeBlocker(previous));\n\t\t}\n\t\tconst { transform, body } = components;\n\t\tif(!(transform instanceof Float32Array) || !(body instanceof Uint32Array)) {\n\t\t\trects.delete(entityId);\n\t\t\tcontinue;\n\t\t}\n\t\tconst rect = blockerRect(transform, body);\n\t\ttargets.forEach(grid => grid.addBlocker(rect));\n\t\trects.set(entityId, rect);\n\t}\n\treturn delta.added.length > 0;\n}\n\nfunction snappedRightAngle(angle: number): number {\n\tconst snapped = Math.round(angle / (Math.PI / 2)) * Math.PI / 2;\n\treturn Math.abs(angle - snapped) < 1e-5 ? snapped : angle;\n}\n","import { BODY_CATEGORY_INDEX } from '../components/body-component-constants';\nimport NavGrid, { type Rect } from './nav-grid';\nimport { applyRectChanges, blockerRect, type PathBlockerDelta } from './path-blockers';\n\ninterface Gate {\n\trect: Rect\n\tcategory: number\n}\n\n// Worker-local grids the pathfinding update searches, rebuilt from the blocker and gate queries on every load.\nexport interface NavigationState {\n\t// Blockers only: what a mover whose mask takes in none of the gate categories walks through.\n\tgrid: NavGrid\n\tblockers: Map<number, Rect>\n\tgateCategories: number\n\tgates: Map<number, Gate>\n\t// Keyed by the gate categories a mover's mask takes in. Cloned from `grid` on first use, so a game with gates no\n\t// mover collides with never pays for a second grid.\n\tgatedGrids: Map<number, NavGrid>\n}\n\nexport function createNavigationState(tilesWide: number, tilesHigh: number, cellSize: number, gateCategories = 0): NavigationState {\n\treturn {\n\t\tgrid: new NavGrid(tilesWide, tilesHigh, cellSize),\n\t\tblockers: new Map(),\n\t\tgateCategories,\n\t\tgates: new Map(),\n\t\tgatedGrids: new Map(),\n\t};\n}\n\nexport function navigationGridFor(state: NavigationState, collideMask: number): NavGrid {\n\tconst key = collideMask & state.gateCategories;\n\tif(!key) {\n\t\treturn state.grid;\n\t}\n\tlet grid = state.gatedGrids.get(key);\n\tif(!grid) {\n\t\tgrid = state.grid.clone();\n\t\tfor(const gate of state.gates.values()) {\n\t\t\tif(gate.category & key) {\n\t\t\t\tgrid.addBlocker(gate.rect);\n\t\t\t}\n\t\t}\n\t\tstate.gatedGrids.set(key, grid);\n\t}\n\treturn grid;\n}\n\n// Everything blocking a mover with this mask: the blockers plus the gates it collides with.\nexport function blockingRects(state: NavigationState, collideMask: number): Array<Rect> {\n\tconst key = collideMask & state.gateCategories;\n\tconst rects = [...state.blockers.values()];\n\tif(key) {\n\t\tfor(const gate of state.gates.values()) {\n\t\t\tif(gate.category & key) {\n\t\t\t\trects.push(gate.rect);\n\t\t\t}\n\t\t}\n\t}\n\treturn rects;\n}\n\n// A multiplier on walking speed (1.5 for a road, 0.5 for mud), applied to every grid.\nexport function setNavigationTerrainSpeed(state: NavigationState, tileX: number, tileY: number, speed: number): void {\n\tstate.grid.setTerrainSpeed(tileX, tileY, speed);\n\tfor(const grid of state.gatedGrids.values()) {\n\t\tgrid.setTerrainSpeed(tileX, tileY, speed);\n\t}\n}\n\n// Returns whether any blockers were added, so paths already handed out can be rechecked.\nexport function applyPathBlockerChanges(state: NavigationState, delta: PathBlockerDelta): boolean {\n\treturn applyRectChanges(state.blockers, [state.grid, ...state.gatedGrids.values()], delta);\n}\n\nexport function applyPathGateChanges(state: NavigationState, delta: PathBlockerDelta): boolean {\n\tfor(const entityId of delta.removed) {\n\t\tconst gate = state.gates.get(entityId);\n\t\tif(gate) {\n\t\t\tforGatedGrids(state, gate.category, grid => grid.removeBlocker(gate.rect));\n\t\t\tstate.gates.delete(entityId);\n\t\t}\n\t}\n\tfor(const { entityId, components } of delta.added) {\n\t\tconst previous = state.gates.get(entityId);\n\t\tif(previous) {\n\t\t\tforGatedGrids(state, previous.category, grid => grid.removeBlocker(previous.rect));\n\t\t}\n\t\tconst { transform, body } = components;\n\t\tif(!(transform instanceof Float32Array) || !(body instanceof Uint32Array)) {\n\t\t\tstate.gates.delete(entityId);\n\t\t\tcontinue;\n\t\t}\n\t\tconst gate = { rect: blockerRect(transform, body), category: body[BODY_CATEGORY_INDEX] & state.gateCategories };\n\t\tforGatedGrids(state, gate.category, grid => grid.addBlocker(gate.rect));\n\t\tstate.gates.set(entityId, gate);\n\t}\n\treturn delta.added.length > 0;\n}\n\nfunction forGatedGrids(state: NavigationState, category: number, apply: (grid: NavGrid) => void): void {\n\tfor(const [key, grid] of state.gatedGrids) {\n\t\tif(key & category) {\n\t\t\tapply(grid);\n\t\t}\n\t}\n}\n","import { loadFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport type {\n\tComponentMap,\n\tEntityQueryChangedFunction,\n\tEntityQueryComponents,\n\tEntityUpdateComponents,\n\tEntityUpdateFunction,\n\tEntityUpdatePreRunFunction,\n\tEntityWorkerSystemWorld,\n} from '@daneren2005/shared-memory-ecs';\nimport { BODY_MASK_INDEX } from '../components/body-component-constants';\nimport {\n\tNAVIGATION_ACTIVE_INDEX,\n\tNAVIGATION_GOAL_X_INDEX,\n\tNAVIGATION_GOAL_Y_INDEX,\n\tNAVIGATION_OUTCOME_INDEX,\n\tNAVIGATION_REQUEST_INDEX,\n\tNAVIGATION_RESULT_INDEX,\n\tNAVIGATION_WAYPOINT_COUNT_INDEX,\n\tNAVIGATION_WAYPOINT_INDEX,\n} from '../components/navigation-component-constants';\nimport { waypointX, waypointY, writePath } from '../components/navigation-component';\nimport {\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n} from '../components/transform-component-constants';\nimport GridAStar from './grid-a-star';\nimport { sweepCost } from './line-of-sight';\nimport type NavGrid from './nav-grid';\nimport type { Rect } from './nav-grid';\nimport {\n\tapplyPathBlockerChanges,\n\tapplyPathGateChanges,\n\tblockingRects,\n\tcreateNavigationState,\n\tnavigationGridFor,\n\ttype NavigationState,\n} from './navigation-state';\nimport { PATH_BLOCKERS_QUERY, PATH_GATES_QUERY } from './path-blockers';\nimport { PathStatus, type PathRequest, type PathSearch } from './path-search';\n\n// A* tiles expanded per run before the rest stay pending for the next. Counted in work rather than wall-clock time so\n// a simulation plays out the same however loaded the machine is. The first search of a run always goes ahead.\nexport const DEFAULT_PATH_SEARCH_ITERATION_BUDGET = 16_000;\n\nexport type PathfindingUpdateComponents = {\n\tnavigation: Int32Array\n\ttransform: Float32Array\n\t// Movers whose mask takes in a gate category path around those gates.\n\tbody?: Uint32Array\n};\n\nexport interface PathfindingInitData {\n\ttilesWide: number\n\ttilesHigh: number\n\tcellSize: number\n}\n\nexport interface PathfindingWorld extends EntityWorkerSystemWorld {\n\tnavigation: NavigationState\n\t// Per run: set when a blocker was added, so paths already handed out are rechecked.\n\tblockersAdded?: boolean\n\tsearchIterations?: number\n\t// Overrides the update's iterationBudget for this run; PathfindingSystem sets it from its own `iterationBudget`.\n\titerationBudget?: number\n}\n\nexport interface PathfindingUpdateOptions<\n\tC extends ComponentMap,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld,\n\tD extends PathfindingInitData,\n> {\n\t// Defaults to 8-way GridAStar.\n\tsearch?: PathSearch\n\titerationBudget?: number\n\t// Collide categories that stop only movers whose mask takes them in, such as a door animals cannot open.\n\tgateCategories?: number\n\t// Runs once the grids are built from what was loaded; what it returns joins the world. Feed terrain speeds in here\n\t// with setNavigationTerrainSpeed.\n\tinit?: (navigation: NavigationState, data: D | undefined, queries: EntityQueryComponents<C>) => Partial<W> | void\n\t// Handed every query change after the update's own, for the game's extra queries.\n\tqueryChanged?: EntityQueryChangedFunction<C, W>\n\tpreRun?: EntityUpdatePreRunFunction<C, T, W>\n}\n\n// Read by PathfindingSystem on the main thread to build matching gate queries.\nexport interface PathfindingUpdateMetadata {\n\tgateCategories: number\n}\n\nexport type PathfindingUpdateFunction<\n\tC extends ComponentMap,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld,\n\tD extends PathfindingInitData,\n> = EntityUpdateFunction<C, T, W, D> & { pathfinding: PathfindingUpdateMetadata };\n\n// Builds the update both backends run: the worker file hands it to createEntitySystemWorker and PathfindingSystem takes\n// the same function as its `updateFunction`. Hooks are baked in because functions do not survive postMessage.\nexport function createPathfindingUpdate<\n\tC extends ComponentMap = ComponentMap,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C> = PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld = PathfindingWorld,\n\tD extends PathfindingInitData = PathfindingInitData,\n>(options: PathfindingUpdateOptions<C, T, W, D> = {}): PathfindingUpdateFunction<C, T, W, D> {\n\tconst search = options.search ?? new GridAStar({ diagonal: true });\n\tconst budget = options.iterationBudget ?? DEFAULT_PATH_SEARCH_ITERATION_BUDGET;\n\tconst gateCategories = options.gateCategories ?? 0;\n\n\tconst update: EntityUpdateFunction<C, T, W, D> = (world, entityId, components) => {\n\t\tconst { navigation, transform, body } = components;\n\t\tif(!navigation[NAVIGATION_ACTIVE_INDEX]) {\n\t\t\treturn;\n\t\t}\n\t\tconst mask = body ? body[BODY_MASK_INDEX] : 0;\n\t\tconst grid = navigationGridFor(world.navigation, mask);\n\n\t\tlet requestId = Atomics.load(navigation, NAVIGATION_REQUEST_INDEX);\n\t\tif(requestId === Atomics.load(navigation, NAVIGATION_RESULT_INDEX)) {\n\t\t\tif(!world.blockersAdded || isPathClear(grid, navigation, transform)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trequestId = Atomics.add(navigation, NAVIGATION_REQUEST_INDEX, 1) + 1;\n\t\t}\n\t\tif((world.searchIterations ?? 0) >= (world.iterationBudget ?? budget)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst request: PathRequest = {\n\t\t\tstartX: transform[TRANSFORM_X_INDEX],\n\t\t\tstartY: transform[TRANSFORM_Y_INDEX],\n\t\t\tgoalX: loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX),\n\t\t\tgoalY: loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX),\n\t\t\tagentWidth: transform[TRANSFORM_WIDTH_INDEX],\n\t\t\tagentHeight: transform[TRANSFORM_HEIGHT_INDEX],\n\t\t};\n\t\t// Physics lets a mover walk out of a body it is already inside (a bed finished around a sleeper), so that body\n\t\t// mustn't count here. Left in, the search would relocate the start to open ground, maybe through a wall.\n\t\tconst embedded = embeddedRects(world.navigation, grid, mask, request);\n\t\tembedded.forEach(rect => grid.removeBlocker(rect));\n\t\tconst result = search.findPath(grid, request);\n\t\tembedded.forEach(rect => grid.addBlocker(rect));\n\t\tworld.searchIterations = (world.searchIterations ?? 0) + result.iterations;\n\t\t// A found path's last point is the goal, which the follower reads off the navigation block instead.\n\t\tconst points = result.status === PathStatus.Found ? result.points.slice(0, -1) : result.points;\n\t\t// Stuck against the same dead end it was already at: searching again would only land here once more.\n\t\tconst status = result.status === PathStatus.Incomplete && points.length === 0 ? PathStatus.Unreachable : result.status;\n\t\twritePath(navigation, status, points);\n\t\tAtomics.store(navigation, NAVIGATION_RESULT_INDEX, requestId);\n\t};\n\n\tupdate.init = (_world, data, _entities, queries) => {\n\t\tconst navigation = createNavigationState(data?.tilesWide ?? 1, data?.tilesHigh ?? 1, data?.cellSize ?? 1, gateCategories);\n\t\tapplyPathBlockerChanges(navigation, { added: queries[PATH_BLOCKERS_QUERY] ?? [], removed: [] });\n\t\tapplyPathGateChanges(navigation, { added: queries[PATH_GATES_QUERY] ?? [], removed: [] });\n\t\tconst extension = options.init?.(navigation, data, queries);\n\t\t// Cast because a W could in theory narrow `navigation`; the update always builds the base NavigationState.\n\t\treturn { ...extension, navigation } as Partial<W>;\n\t};\n\n\tupdate.queryChanged = (world, queryName, delta, callbacks) => {\n\t\tif(queryName === PATH_BLOCKERS_QUERY) {\n\t\t\tworld.blockersAdded = applyPathBlockerChanges(world.navigation, delta) || !!world.blockersAdded;\n\t\t} else if(queryName === PATH_GATES_QUERY) {\n\t\t\tworld.blockersAdded = applyPathGateChanges(world.navigation, delta) || !!world.blockersAdded;\n\t\t}\n\t\toptions.queryChanged?.(world, queryName, delta, callbacks);\n\t};\n\n\tupdate.preRun = (world, entities, queries, callbacks) => {\n\t\tworld.searchIterations = 0;\n\t\toptions.preRun?.(world, entities, queries, callbacks);\n\t};\n\n\treturn Object.assign(update, { pathfinding: { gateCategories } });\n}\n\nexport const pathfindingUpdate = createPathfindingUpdate();\n\nfunction embeddedRects(state: NavigationState, grid: NavGrid, mask: number, request: PathRequest): Array<Rect> {\n\tconst halfWidth = request.agentWidth / 2;\n\tconst halfHeight = request.agentHeight / 2;\n\tconst footprint = {\n\t\tminX: request.startX - halfWidth, minY: request.startY - halfHeight, maxX: request.startX + halfWidth, maxY: request.startY + halfHeight,\n\t};\n\tconst tiles = grid.tilesIn(footprint);\n\tlet clear = true;\n\tfor(let y = tiles.fromY; y <= tiles.toY && clear; y++) {\n\t\tfor(let x = tiles.fromX; x <= tiles.toX && clear; x++) {\n\t\t\tclear = grid.blockerCount(x, y) === 0;\n\t\t}\n\t}\n\tif(clear) {\n\t\treturn [];\n\t}\n\treturn blockingRects(state, mask)\n\t\t.filter(rect => rect.minX < footprint.maxX && rect.maxX > footprint.minX && rect.minY < footprint.maxY && rect.maxY > footprint.minY);\n}\n\n// Whether the rest of a settled path is still walkable, from where the mover stands.\nfunction isPathClear(grid: NavGrid, navigation: Int32Array, transform: Float32Array): boolean {\n\tconst halfWidth = transform[TRANSFORM_WIDTH_INDEX] / 2;\n\tconst halfHeight = transform[TRANSFORM_HEIGHT_INDEX] / 2;\n\tlet x = transform[TRANSFORM_X_INDEX];\n\tlet y = transform[TRANSFORM_Y_INDEX];\n\tconst count = navigation[NAVIGATION_WAYPOINT_COUNT_INDEX];\n\tfor(let i = navigation[NAVIGATION_WAYPOINT_INDEX]; i <= count; i++) {\n\t\tlet nextX: number;\n\t\tlet nextY: number;\n\t\tif(i < count) {\n\t\t\tnextX = waypointX(navigation, i);\n\t\t\tnextY = waypointY(navigation, i);\n\t\t} else if(navigation[NAVIGATION_OUTCOME_INDEX] === PathStatus.Found) {\n\t\t\tnextX = loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX);\n\t\t\tnextY = loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX);\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t\tif(!Number.isFinite(sweepCost(grid, x, y, nextX, nextY, halfWidth, halfHeight))) {\n\t\t\treturn false;\n\t\t}\n\t\tx = nextX;\n\t\ty = nextY;\n\t}\n\treturn true;\n}\n","import { EntityWorkerSystem } from '@daneren2005/shared-memory-ecs';\nimport type { BaseEntity, BaseWorld, ComponentDefinitionMap, ComponentMap, EntityUpdateComponents, EntityWorkerSystemQuery, SystemConfig } from '@daneren2005/shared-memory-ecs';\nimport { DEFAULT_PHYSICS_STEP_MS } from '../constants';\nimport type { NavigationComponents } from '../components/registry';\nimport { pathBlockerQueries } from './path-blockers';\nimport {\n\tcreatePathfindingUpdate,\n\ttype PathfindingInitData,\n\ttype PathfindingUpdateComponents,\n\ttype PathfindingUpdateFunction,\n\ttype PathfindingWorld,\n} from './pathfinding-update';\n\nexport interface PathfindingSystemConfig<\n\tC extends ComponentMap & NavigationComponents,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld,\n\tD extends PathfindingInitData,\n> extends Partial<SystemConfig> {\n\t// The grid's size, read when the system loads (so after a save's bounds are applied).\n\tgetInitData: () => D\n\t// Worker entry point, written out in the game's own source so bundlers see it:\n\t//\n\t// getWorker: () => new Worker(new URL('./pathfinding.worker.ts', import.meta.url), { type: 'module' })\n\t//\n\t// Left off, searches run in-process on the main thread instead.\n\tgetWorker?: () => Worker\n\tforceMainThread?: boolean\n\t// Built with createPathfindingUpdate; the worker file must hand createEntitySystemWorker the same function.\n\tupdateFunction?: PathfindingUpdateFunction<C, T, W, D>\n\t// Narrows what counts as a blocker or gate, e.g. `not: ['construction']`. Other systems reading the same view can\n\t// build these queries with pathBlockerQueries.\n\tblockers?: {\n\t\tnot?: Array<keyof C>\n\t\tfilter?: (entity: BaseEntity<C>) => boolean\n\t}\n\t// Extra queries for the update's hooks, such as terrain to feed setNavigationTerrainSpeed.\n\tqueries?: { [key: string]: EntityWorkerSystemQuery<C> }\n\t// Extra components sent with each mover.\n\toptional?: Array<keyof C & string>\n\t// A* tiles expanded per run, overriding the update's own budget. Also a live field on the system.\n\titerationBudget?: number\n}\n\n// Finds paths for movers whose navigation request is pending, against grids its update keeps from the blocker queries.\nexport default class PathfindingSystem<\n\tC extends ComponentMap & NavigationComponents,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C> = PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld = PathfindingWorld,\n\tD extends PathfindingInitData = PathfindingInitData,\n> extends EntityWorkerSystem<C, T, W, D> {\n\t// Sent with every run, so a change takes effect on the next one. Undefined leaves the update's own budget.\n\titerationBudget: number | undefined;\n\n\tconstructor(world: BaseWorld<ComponentDefinitionMap, C>, options: PathfindingSystemConfig<C, T, W, D>) {\n\t\tconst updateFunction: PathfindingUpdateFunction<C, T, W, D> = options.updateFunction ?? createPathfindingUpdate<C, T, W, D>();\n\t\tconst optional = options.optional ?? [];\n\t\tsuper(world, {\n\t\t\tname: options.name ?? 'PathfindingSystem',\n\t\t\tdeltaBetweenRuns: options.deltaBetweenRuns ?? DEFAULT_PHYSICS_STEP_MS,\n\t\t\tfirstRun: options.firstRun,\n\t\t\trequired: ['navigation', 'transform'],\n\t\t\toptional: optional.includes('body') ? optional : ['body', ...optional],\n\t\t\tqueries: {\n\t\t\t\t...pathBlockerQueries<C>({ ...options.blockers, gateCategories: updateFunction.pathfinding.gateCategories }),\n\t\t\t\t...options.queries,\n\t\t\t},\n\t\t\tupdateFunction,\n\t\t\tgetInitData: options.getInitData,\n\t\t\tforceMainThread: options.forceMainThread ?? !options.getWorker,\n\t\t\tgetWorker: options.getWorker ?? (() => {\n\t\t\t\tthrow new Error('PathfindingSystem cannot start a worker: no getWorker was supplied');\n\t\t\t}),\n\t\t});\n\t\tthis.iterationBudget = options.iterationBudget;\n\t}\n\n\t// A subclass overriding this must call super.addDataToWorld(world), or the budget stops reaching the update.\n\taddDataToWorld(world: W): void {\n\t\tworld.iterationBudget = this.iterationBudget;\n\t}\n\n\tprivate loaded = false;\n\n\t// The update builds its grids in its `init` hook, which the ECS only calls from `world.load`. A world filled with\n\t// loadEntity and then init()ed never loads, so load here; it consumes the pending deltas, so nothing is added twice.\n\tasync init(): Promise<void> {\n\t\tawait super.init();\n\t\tif(!this.loaded) {\n\t\t\tawait this.finishLoading();\n\t\t}\n\t}\n\n\tfinishLoading(): Promise<void> {\n\t\tthis.loaded = true;\n\t\treturn super.finishLoading();\n\t}\n}\n","import { loadFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { BACKLOG_CATCHUP_RATE, BACKLOG_TARGET_STEPS } from '../constants';\nimport type { EntityUpdateFunction, EntityWorkerSystemWorld } from '@daneren2005/shared-memory-ecs';\nimport type { InterpolationComponents, InterpolationUpdateComponents } from '../components/registry';\nimport {\n\tINTERPOLATION_CHANNEL_INDEX,\n\tINTERPOLATION_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_TICK_INDEX,\n\tINTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_X_INDEX,\n\tINTERPOLATION_PREVIOUS_Y_INDEX,\n\tINTERPOLATION_TARGET_X_INDEX,\n\tINTERPOLATION_TARGET_Y_INDEX,\n\tINTERPOLATION_SYNCED_STEP_DURATION_INDEX,\n\tINTERPOLATION_REMAINING_DURATION_INDEX,\n\tINTERPOLATION_SYNCED_DURATION_INDEX,\n\tINTERPOLATION_SYNCED_TICK_INDEX,\n\tINTERPOLATION_TICK_INDEX,\n\tINTERPOLATION_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_X_INDEX,\n\tINTERPOLATION_Y_INDEX,\n} from '../components/interpolation-component-constants';\nimport { TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\n\nexport interface InterpolationWorld extends EntityWorkerSystemWorld {\n\t// Committed physics tick per interpolation channel, snapshotted once per run by InterpolationSystem.\n\tcommittedTicks?: Array<number>\n}\n\n// Consume published simulation time while advancing toward the last coherent target.\nexport const interpolationUpdate: EntityUpdateFunction<InterpolationComponents, InterpolationUpdateComponents, InterpolationWorld> = (world, entityId, components) => {\n\tconst interpolation = components.interpolation;\n\tconst transform = components.transform;\n\n\t// Accept the transform and timing only if physics completed one consistent publication.\n\tconst before = loadFloat32(interpolation, INTERPOLATION_TICK_INDEX);\n\tlet duration = interpolation[INTERPOLATION_DURATION_INDEX];\n\tlet totalDuration = interpolation[INTERPOLATION_TOTAL_DURATION_INDEX];\n\tlet x = transform[TRANSFORM_X_INDEX];\n\tlet y = transform[TRANSFORM_Y_INDEX];\n\tlet after = loadFloat32(interpolation, INTERPOLATION_TICK_INDEX);\n\tlet coherent = before === after;\n\n\t// A torn or not-yet-committed publication falls back to the one it replaced, so every entity of a physics run\n\t// renders the same step even while the worker is part way through publishing it.\n\tconst committedTick = world.committedTicks?.[interpolation[INTERPOLATION_CHANNEL_INDEX]];\n\tif(committedTick !== undefined && !Number.isNaN(committedTick) && !(coherent && after <= committedTick)) {\n\t\tconst previousBefore = loadFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX);\n\t\tduration = interpolation[INTERPOLATION_PREVIOUS_DURATION_INDEX];\n\t\ttotalDuration = interpolation[INTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX];\n\t\tx = interpolation[INTERPOLATION_PREVIOUS_X_INDEX];\n\t\ty = interpolation[INTERPOLATION_PREVIOUS_Y_INDEX];\n\t\tafter = loadFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX);\n\t\tcoherent = previousBefore === after;\n\t}\n\n\tif(!coherent) {\n\t\t// Keep consuming the last coherent segment; dropping this frame makes moving ships stutter relative to a camera.\n\t\tduration = interpolation[INTERPOLATION_SYNCED_STEP_DURATION_INDEX];\n\t\tif(duration <= 0 || interpolation[INTERPOLATION_REMAINING_DURATION_INDEX] <= 0) {\n\t\t\treturn;\n\t\t}\n\t\tx = interpolation[INTERPOLATION_TARGET_X_INDEX];\n\t\ty = interpolation[INTERPOLATION_TARGET_Y_INDEX];\n\t\ttotalDuration = interpolation[INTERPOLATION_SYNCED_DURATION_INDEX];\n\t\tafter = interpolation[INTERPOLATION_SYNCED_TICK_INDEX];\n\t} else {\n\t\tinterpolation[INTERPOLATION_TARGET_X_INDEX] = x;\n\t\tinterpolation[INTERPOLATION_TARGET_Y_INDEX] = y;\n\t\tinterpolation[INTERPOLATION_SYNCED_STEP_DURATION_INDEX] = duration;\n\t}\n\n\t// No physics segment has been published yet.\n\tif(duration <= 0) {\n\t\tinterpolation[INTERPOLATION_SYNCED_TICK_INDEX] = after;\n\t\tinterpolation[INTERPOLATION_SYNCED_DURATION_INDEX] = totalDuration;\n\t\tinterpolation[INTERPOLATION_REMAINING_DURATION_INDEX] = 0;\n\t\tinterpolation[INTERPOLATION_X_INDEX] = x;\n\t\tinterpolation[INTERPOLATION_Y_INDEX] = y;\n\n\t\treturn;\n\t}\n\n\tlet remainingDuration = interpolation[INTERPOLATION_REMAINING_DURATION_INDEX];\n\tif(after !== interpolation[INTERPOLATION_SYNCED_TICK_INDEX]) {\n\t\tconst syncedDuration = interpolation[INTERPOLATION_SYNCED_DURATION_INDEX];\n\t\tconst publishedDuration = totalDuration >= syncedDuration ? totalDuration - syncedDuration : duration;\n\t\tremainingDuration += publishedDuration;\n\t\tinterpolation[INTERPOLATION_SYNCED_DURATION_INDEX] = totalDuration;\n\t\tinterpolation[INTERPOLATION_SYNCED_TICK_INDEX] = after;\n\t}\n\n\tif(remainingDuration <= 0) {\n\t\tinterpolation[INTERPOLATION_REMAINING_DURATION_INDEX] = 0;\n\t\tinterpolation[INTERPOLATION_X_INDEX] = x;\n\t\tinterpolation[INTERPOLATION_Y_INDEX] = y;\n\t\treturn;\n\t}\n\n\tlet frameDuration = Math.min(Math.max(world.elapsedTime, 0), remainingDuration);\n\t// Ease off a persistent backlog. Once physics publishes every frame (high timeScale, a step lands per frame)\n\t// the budget added per frame matches the budget spent, so a backlog banked by a one-off slow run would never\n\t// drain on its own - the render would trail the simulation by it forever. Spend a little extra on the part of\n\t// the backlog beyond one step of latency, bounded so the render eases back rather than jumping across it. Below\n\t// the target this never fires, leaving normal one-step-latency pacing exactly as it was.\n\tconst targetBacklog = duration * BACKLOG_TARGET_STEPS;\n\tconst backlogAfterFrame = remainingDuration - frameDuration;\n\tif(backlogAfterFrame > targetBacklog) {\n\t\tframeDuration += Math.min(backlogAfterFrame - targetBacklog, frameDuration * BACKLOG_CATCHUP_RATE);\n\t}\n\tconst frameProgress = frameDuration / remainingDuration;\n\tconst renderX = interpolation[INTERPOLATION_X_INDEX];\n\tconst renderY = interpolation[INTERPOLATION_Y_INDEX];\n\tinterpolation[INTERPOLATION_X_INDEX] = renderX + (x - renderX) * frameProgress;\n\tinterpolation[INTERPOLATION_Y_INDEX] = renderY + (y - renderY) * frameProgress;\n\tinterpolation[INTERPOLATION_REMAINING_DURATION_INDEX] = remainingDuration - frameDuration;\n};\n\nexport default interpolationUpdate;\n","import { EntityWorkerSystem } from '@daneren2005/shared-memory-ecs';\nimport type { BaseWorld, ComponentDefinitionMap, ComponentMap, EntityUpdateComponents, SystemConfig } from '@daneren2005/shared-memory-ecs';\nimport type { InterpolationComponents, InterpolationUpdateComponents } from '../components/registry';\nimport { interpolationUpdate, type InterpolationWorld } from './interpolation-update';\nimport { snapshotCommittedTicks } from './interpolation-channels';\n\nexport interface InterpolationSystemConfig extends Partial<SystemConfig> {\n\t// Defaulted the opposite way to PhysicsSystem - main thread unless asked otherwise - since a render position\n\t// read a frame after it was computed is a frame stale, the latency this system exists to cut.\n\tforceMainThread?: boolean\n\tgetWorker?: () => Worker\n}\n\n// Fills in a render position for every entity with an `interpolation` component, once per frame, by blending\n// between the two positions physics published either side of its last step.\n//\n// Its only link to PhysicsSystem is a per-run snapshot of each system's committed tick (interpolation-channels.ts);\n// everything else it needs is in the block, published by whichever run wrote it. Pacing off the physics system would pace off when a run was posted, which on a worker\n// is not when its results arrive; see interpolation-update.ts. So it takes no configuration and follows a step\n// the game retunes mid-flight. Ordering after the physics system is a preference worth one frame of latency;\n// pause and `timeScale` need no code, since BaseWorld#runUpdate already skips and scales for free.\nexport default class InterpolationSystem<\n\tC extends ComponentMap & InterpolationComponents,\n> extends EntityWorkerSystem<C, InterpolationUpdateComponents & EntityUpdateComponents<C>, InterpolationWorld> {\n\tconstructor(world: BaseWorld<ComponentDefinitionMap, C>, options: InterpolationSystemConfig = {}) {\n\t\tsuper(world, {\n\t\t\tname: options.name ?? 'InterpolationSystem',\n\t\t\t// Every frame, which is the whole point.\n\t\t\tdeltaBetweenRuns: 0,\n\t\t\tfirstRun: options.firstRun,\n\n\t\t\t// No velocity is read - the blend is between two positions the simulation produced - so a velocity-less\n\t\t\t// entity is in this system on the same terms as a ship.\n\t\t\trequired: ['transform', 'interpolation'],\n\t\t\tupdateFunction: interpolationUpdate,\n\n\t\t\tforceMainThread: options.forceMainThread ?? !options.getWorker,\n\t\t\tgetWorker: options.getWorker ?? (() => {\n\t\t\t\tthrow new Error('InterpolationSystem cannot start a worker: no getWorker was supplied');\n\t\t\t}),\n\t\t});\n\t}\n\n\t// One snapshot per run, so a physics run that commits mid-pass cannot split entities across two steps.\n\taddDataToWorld(world: InterpolationWorld): void {\n\t\tworld.committedTicks = snapshotCommittedTicks(this.world);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["clamp","shapeContactNormal","SharedSpatialMap","Flatbush","POSITION_UPDATED_EVENT","COLLIDABLE_QUERY","DEFAULT_PHYSICS_STEP_MS","BACKLOG_TARGET_STEPS","BACKLOG_CATCHUP_RATE","TRANSFORM_X_INDEX","TRANSFORM_Y_INDEX","TRANSFORM_WIDTH_INDEX","TRANSFORM_HEIGHT_INDEX","TRANSFORM_ANGLE_INDEX","TRANSFORM_SIZE","MAX_POLYGON_VERTICES","POLYGON_VERTEX_COUNT_INDEX","POLYGON_VERTICES_INDEX","POLYGON_SIZE","PolygonComponentImpl","Component","polygonDefinition","prepared","preparePolygon","config","memory","index","vertices","minX","minY","maxX","maxY","x","y","width","height","winding","i","previous","current","next","turnCross","direction","aStart","aEnd","j","bStart","bEnd","segmentsIntersect","boxWidth","boxHeight","sized","outputWidth","outputHeight","centreX","centreY","normalized","ordered","aToStart","cross","aToEnd","bToStart","bToEnd","end","start","point","TransformComponentImpl","value","transformDefinition","radius","saved","component","angle","VELOCITY_X_INDEX","VELOCITY_Y_INDEX","VELOCITY_DAMPING_INDEX","VELOCITY_SIZE","VelocityComponentImpl","assertDamping","velocityDefinition","damping","DYNAMICS_ACCELERATION_X_INDEX","DYNAMICS_ACCELERATION_Y_INDEX","DYNAMICS_INVERSE_MASS_INDEX","DYNAMICS_SIZE","DynamicsComponentImpl","assertFinite","assertMass","dynamicsDefinition","accelerationX","accelerationY","mass","name","SHAPE_RECTANGLE","SHAPE_CIRCLE","SHAPE_CAPSULE","SHAPE_POLYGON","DEFAULT_COLLIDE_CATEGORY","DEFAULT_COLLIDE_MASK","BODY_FLAGS_INDEX","BODY_CATEGORY_INDEX","BODY_MASK_INDEX","BODY_SIZE","BODY_SHAPE_MASK","BODY_SENSOR_FLAG","BODY_CCD_FLAG","BODY_DYING_FLAG","BODY_BLOCKS_PATH_FLAG","KNOWN_SHAPES","canCollide","maskA","categoryB","maskB","categoryA","bodyShape","body","isSensor","isContinuous","blocksPath","isDying","markDying","BodyComponentImpl","bodyDefinition","toFlags","flags","toShape","shape","BOUNCINESS_INDEX","BOUNCINESS_SIZE","BouncinessComponentImpl","bouncinessDefinition","INTERPOLATION_X_INDEX","INTERPOLATION_Y_INDEX","INTERPOLATION_SYNCED_TICK_INDEX","INTERPOLATION_DURATION_INDEX","INTERPOLATION_TICK_INDEX","INTERPOLATION_TOTAL_DURATION_INDEX","INTERPOLATION_SYNCED_DURATION_INDEX","INTERPOLATION_REMAINING_DURATION_INDEX","INTERPOLATION_TARGET_X_INDEX","INTERPOLATION_TARGET_Y_INDEX","INTERPOLATION_SYNCED_STEP_DURATION_INDEX","INTERPOLATION_CHANNEL_INDEX","INTERPOLATION_PREVIOUS_X_INDEX","INTERPOLATION_PREVIOUS_Y_INDEX","INTERPOLATION_PREVIOUS_DURATION_INDEX","INTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX","INTERPOLATION_PREVIOUS_TICK_INDEX","INTERPOLATION_SIZE","InterpolationComponentImpl","loadFloat32","interpolationDefinition","NAVIGATION_REQUEST_INDEX","NAVIGATION_RESULT_INDEX","NAVIGATION_OUTCOME_INDEX","NAVIGATION_WAYPOINT_INDEX","NAVIGATION_WAYPOINT_COUNT_INDEX","NAVIGATION_ACTIVE_INDEX","NAVIGATION_GOAL_X_INDEX","NAVIGATION_GOAL_Y_INDEX","NAVIGATION_WAYPOINTS_INDEX","NAVIGATION_MAX_WAYPOINTS","NAVIGATION_SIZE","PathStatus","NavigationComponentImpl","isPathPending","waypoint","navigationDefinition","navigateTo","setNavigationGoal","navigation","requestPath","storeFloat32","stopNavigating","isNavigating","isUnreachable","navigationGoal","waypointX","waypointY","writePath","truncated","points","count","status","physicsRegistry","navigationRegistry","GAP","CANDIDATE","capsuleHalfLength","capsuleCoreAngle","shapeRadius","shapeIsEmpty","shapeHalfWidth","boundsHalfWidth","shapeHalfHeight","boundsHalfHeight","shapesOverlap","aShape","aWidth","aHeight","bShape","bWidth","bHeight","aIsRound","bIsRound","orientedBoxesOverlap","aX","aY","aAngle","bX","bY","bAngle","bHalf","coreHalfLength","bCoreAngle","bCos","bSin","bRadius","segmentBoxDistanceSquared","aHalf","aCoreAngle","aCos","aSin","reach","segmentSegmentDistanceSquared","contactNormal","boxesContactNormal","out","roundsContactNormal","boxRoundContactNormal","aHalfWidth","aHalfHeight","bHalfWidth","bHalfHeight","offsetX","offsetY","best","boxOverlapAlong","depth","pointAwayFrom","distance","normalize","half","roundShape","roundWidth","roundHeight","coreAngle","roundAngle","halfX","halfY","roundX","roundY","boxX","boxY","boxAngle","cos","sin","localX","localY","localHalfX","localHalfY","halfWidth","halfHeight","roundOverlapAlong","axisX","axisY","length","aDeltaX","aX2","aX1","aDeltaY","aY2","aY1","bDeltaX","bX2","bX1","bDeltaY","bY2","bY1","aLength","bLength","bOffset","aParam","bParam","clamp$1","aOffset","along","denominator","gapX","gapY","firstOffsetX","x1","firstOffsetY","y1","secondOffsetX","x2","secondOffsetY","y2","localX1","localY1","localX2","localY2","segmentMissesBox","pointBoxDistanceSquared","pointSegmentDistanceSquared","keepCloser","localGapX","localGapY","candidate","flip","gap","deltaX","deltaY","normalX","normalY","outsideX","outsideY","spatialBounds","transform","components","boundsOverlap","a","querySize","max","min","PhysicalWorld","BaseWorld","worldOptions","options","registry","spatial","entity","entityOrId","bounds","inserts","found","ids","id","filter","reachX","reachY","maxDistance","maxResults","PROJECTION_A","PROJECTION_B","AXIS","polygonShapesOverlap","testPolygonAxes","aPolygon","bPolygon","polygonContactNormal","testEdges","keepBest","testRectangleAxes","isRound","testRoundVertexAxes","polygon","currentX","worldVertexX","currentY","worldVertexY","nextX","testAxis","alongX","alongY","startX","startY","endX","endY","polygonAngle","vertexX","polygonX","polygonWidth","polygonHeight","vertexY","polygonY","fraction","closestFraction","coreX","coreY","projectShape","negativeDepth","positiveDepth","centre","projection","offset","segmentX","segmentY","lengthSquared","emptyCommand","findDynamicsCommand","commands","low","high","middle","command","entityId","findDynamicsCommandOffset","commandEntityId","CONTACT_TOLERANCE","MAX_REFINEMENTS","NOTHING_BLOCKING","MAX_PAIR_ID","CLEAR","CollisionBroadphase","pending","flatCommands","dynamicsCommands","recordCommands","entities","category","velocity","dynamics","commandOffset","inverseMass","forceX","forceY","impulseX","impulseY","velocityMask","assignedVelocityX","assignedVelocityY","velocityX","velocityY","integratedVelocityX","seconds","integratedVelocityY","endVelocityX","endVelocityY","travelX","travelY","bucket","entries","key","b","searcher","toSearcher","self","moveX","moveY","sweptOverlaps","other","handle","overlapsAt","contactNormalAt","candidates","rest","slide","xCandidates","yCandidates","slidX","slidY","slidCandidates","mergeBlocking","clear","blocked","tolerance","blocking","blockedX","blockedY","earliest","toX","toY","closest","clamp01","DEAD_INDEX","otherShape","otherX","otherY","otherWidth","otherHeight","otherAngle","merged","entry","mask","overlapShapes","pathAngle","SpatialIndex","getSpatialMap","world","updateSpatialMap","spatialMap","setEntityPosition","position","addPhysicalWorldData","source","snapEntity","interpolation","startSpawnInterpolation","remaining","spawnX","spawnY","jumpX","jumpY","integrateDynamics","dampingFactor","NORMAL","bouncePair","canRespond","stopNonBouncing","isSurface","collisionNormal","respond","reflect","bounciness","into","scale","isSensorBody","selfTransform","otherTransform","selfShape","shapeOf","combineDynamicsCommands","queued","incoming","combined","forEachDynamicsCommand","left","right","queueDynamicsVector","assertFiniteVector","queueVelocityAssignment","assertFiniteAssignment","CONTACT_NORMAL","createPhysicsUpdate","broadphase","broadphaseByGroup","callbackCommandsBySystem","update","groupId","group","readGroup","tree","move","callbacks","finishInterpolationStep","bouncing","moved","stopVelocityOnContact","onCollision","resolveContact","queries","prepareCallbackCommands","dying","collidables","\"collidable\"","byGroup","list","commandWorld","commandQueueId","deferred","commandsBySystem","block","selfBounds","otherBounds","updateSpatialMapIfBoundsChanged","after","before","applyDeathInterpolation","otherBody","contact","physicsUpdate","startInterpolationStep","tick","addAtomicFloat32","reportMoves","channelsByWorld","registerInterpolationChannel","channels","unregisterInterpolationChannel","channel","snapshotCommittedTicks","nextCommandQueueId","PhysicsSystem","EntityWorkerSystem","updateFunction","optional","collision","integratesDynamics","extraOptional","movingOptional","collidableOptional","collidableQuery","moverFilter","combineFilters","step","entityIds","snapshot","NO_REGION","NavGrid","tilesWide","tilesHigh","cellSize","copy","tileX","tileY","tile","rect","range","speed","regions","queue","head","tail","region","MinHeap","capacity","parent","primary","secondary","top","last","child","primaryA","primaryB","secondaryA","secondaryB","sweepCost","cell","grid","dx","x0","dy","y0","fromRow","toRow","worst","row","centerY","tLow","tHigh","tFrom","tTo","xa","xb","fromColumn","toColumn","column","LOOKAHEAD","smoothPath","polyline","dropDuplicates","travelled","segmentCost","turns","isCollinear","result","from","to","perUnit","clearance","straight","c","DEFAULT_MAX_ITERATIONS","DEFAULT_CLEARANCE","DEFAULT_RELOCATE_RADIUS","ORTHOGONAL","DIAGONAL","GridAStar","request","anchorOf","onMap","clamp","anchor","centerOf","requestedGoal","goal","goalMoved","search","destination","withoutRepeats","bestDistance","bestPreference","edge","centerX","towardX","towardY","preference","preferX","preferY","stamp","startIndex","goalIndex","cheapestTile","estimate","heap","closestEstimate","iterations","closed","cost","footprintCost","tiles","at","tileCount","total","ty","tx","FollowStep","followPath","arriveDistance","outcome","PATH_BLOCKERS_QUERY","PATH_GATES_QUERY","pathBlockerQueries","blockers","gateCategories","gates","blockerRect","snappedRightAngle","applyRectChanges","targets","grids","delta","rects","snapped","createNavigationState","navigationGridFor","collideMask","state","gate","blockingRects","setNavigationTerrainSpeed","applyPathBlockerChanges","applyPathGateChanges","forGatedGrids","apply","DEFAULT_PATH_SEARCH_ITERATION_BUDGET","createPathfindingUpdate","budget","requestId","isPathClear","embedded","embeddedRects","data","\"pathBlockers\"","\"pathGates\"","queryName","pathfindingUpdate","footprint","nextY","PathfindingSystem","interpolationUpdate","duration","totalDuration","coherent","committedTick","previousBefore","remainingDuration","syncedDuration","publishedDuration","frameDuration","targetBacklog","backlogAfterFrame","frameProgress","renderX","renderY","InterpolationSystem"],"sources":["../src/constants.ts","../src/components/transform-component-constants.ts","../src/components/polygon-component-constants.ts","../src/components/polygon-component.ts","../src/components/transform-component.ts","../src/components/velocity-component-constants.ts","../src/components/velocity-component.ts","../src/components/dynamics-component-constants.ts","../src/components/dynamics-component.ts","../src/components/body-component-constants.ts","../src/components/body-component.ts","../src/components/bounciness-component-constants.ts","../src/components/bounciness-component.ts","../src/components/interpolation-component-constants.ts","../src/components/interpolation-component.ts","../src/components/navigation-component-constants.ts","../src/pathfinding/path-search.ts","../src/components/navigation-component.ts","../src/components/registry.ts","../src/math/shapes.ts","../src/systems/spatial-bounds.ts","../src/world.ts","../src/math/polygons.ts","../src/actions/dynamics-commands.ts","../src/actions/find-dynamics-command.ts","../src/actions/find-dynamics-command-offset.ts","../src/systems/collision.ts","../src/systems/spatial-index.ts","../src/actions/get-spatial-map.ts","../src/actions/update-spatial-map.ts","../src/actions/set-entity-position.ts","../src/actions/add-physical-world-data.ts","../src/actions/snap-entity.ts","../src/actions/start-spawn-interpolation.ts","../src/actions/integrate-dynamics.ts","../src/systems/bounce.ts","../src/actions/combine-dynamics-commands.ts","../src/actions/queue-dynamics-vector.ts","../src/actions/queue-velocity-assignment.ts","../src/systems/physics-update.ts","../src/systems/interpolation-channels.ts","../src/systems/physics-system.ts","../src/pathfinding/nav-grid.ts","../src/pathfinding/min-heap.ts","../src/pathfinding/line-of-sight.ts","../src/pathfinding/smooth-path.ts","../src/pathfinding/grid-a-star.ts","../src/pathfinding/follow-path.ts","../src/pathfinding/path-blockers.ts","../src/pathfinding/navigation-state.ts","../src/pathfinding/pathfinding-update.ts","../src/pathfinding/pathfinding-system.ts","../src/systems/interpolation-update.ts","../src/systems/interpolation-system.ts"],"sourcesContent":["// One event per completed run, carrying the ids of moved entities.\nexport const POSITION_UPDATED_EVENT = 'position-updated';\n\nexport const COLLIDABLE_QUERY = 'collidable';\nexport const DEFAULT_PHYSICS_STEP_MS = 50;\n\n// Backlog beyond this many steps is eased away instead of leaving a persistent render delay.\nexport const BACKLOG_TARGET_STEPS = 1.5;\n// Extra fraction of each frame's render budget available for catching up.\nexport const BACKLOG_CATCHUP_RATE = 0.5;\n","export const TRANSFORM_X_INDEX = 0;\nexport const TRANSFORM_Y_INDEX = 1;\nexport const TRANSFORM_WIDTH_INDEX = 2;\nexport const TRANSFORM_HEIGHT_INDEX = 3;\nexport const TRANSFORM_ANGLE_INDEX = 4;\nexport const TRANSFORM_SIZE = 5;\n","export const MAX_POLYGON_VERTICES = 16;\nexport const POLYGON_VERTEX_COUNT_INDEX = 0;\nexport const POLYGON_VERTICES_INDEX = 1;\nexport const POLYGON_SIZE = POLYGON_VERTICES_INDEX + MAX_POLYGON_VERTICES * 2;\n","import { MAX_POLYGON_VERTICES, POLYGON_VERTEX_COUNT_INDEX, POLYGON_SIZE } from './polygon-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\nexport type PolygonVertex = readonly [x: number, y: number];\n\nexport interface PolygonConfig {\n\tvertices?: ReadonlyArray<PolygonVertex>\n\twidth?: number\n\theight?: number\n}\n\nexport interface PolygonComponent {\n\tindex: number\n\treadonly block: Float32Array\n\treadonly vertexCount: number\n}\n\nexport interface PreparedPolygon {\n\twidth: number\n\theight: number\n\tvertices: Array<number>\n}\n\nclass PolygonComponentImpl extends Component<Float32Array> implements PolygonComponent {\n\tget vertexCount() {\n\t\treturn this.block[POLYGON_VERTEX_COUNT_INDEX];\n\t}\n}\n\nexport const polygonDefinition: ComponentDefinition<PolygonComponent, Float32Array, PolygonConfig> = {\n\ttype: Float32Array,\n\tsize: POLYGON_SIZE,\n\tloadProperties: ['vertices'],\n\ttoBlock(config) {\n\t\tconst prepared = preparePolygon(config.vertices, config.width, config.height);\n\n\t\treturn [prepared.vertices.length / 2, ...prepared.vertices];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new PolygonComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\n// With a width and height, the vertices are offsets from the entity's centre that must fit inside that box, so the\n// outline can sit tighter than the box a game draws (and sizes) the entity by. Without, the vertices' bounds are the box.\nexport function preparePolygon(vertices: ReadonlyArray<PolygonVertex> | undefined, boxWidth?: number, boxHeight?: number): PreparedPolygon {\n\tif(!vertices || vertices.length < 3 || vertices.length > MAX_POLYGON_VERTICES) {\n\t\tthrow new Error(`A polygon needs between 3 and ${MAX_POLYGON_VERTICES} vertices`);\n\t}\n\n\tlet minX = Infinity;\n\tlet minY = Infinity;\n\tlet maxX = -Infinity;\n\tlet maxY = -Infinity;\n\tfor(const [x, y] of vertices) {\n\t\tif(!Number.isFinite(x) || !Number.isFinite(y)) {\n\t\t\tthrow new Error('Polygon vertices must be finite numbers');\n\t\t}\n\t\tminX = Math.min(minX, x);\n\t\tminY = Math.min(minY, y);\n\t\tmaxX = Math.max(maxX, x);\n\t\tmaxY = Math.max(maxY, y);\n\t}\n\n\tconst width = maxX - minX;\n\tconst height = maxY - minY;\n\tif(width <= 0 || height <= 0) {\n\t\tthrow new Error('A polygon must have nonzero width and height');\n\t}\n\n\tlet winding = 0;\n\tfor(let i = 0; i < vertices.length; i++) {\n\t\tconst previous = vertices[(i + vertices.length - 1) % vertices.length];\n\t\tconst current = vertices[i];\n\t\tconst next = vertices[(i + 1) % vertices.length];\n\t\tconst turnCross = (current[0] - previous[0]) * (next[1] - current[1])\n\t\t\t- (current[1] - previous[1]) * (next[0] - current[0]);\n\t\tif(turnCross === 0) {\n\t\t\tthrow new Error('A polygon cannot contain duplicate or collinear adjacent vertices');\n\t\t}\n\t\tconst direction = Math.sign(turnCross);\n\t\tif(winding === 0) {\n\t\t\twinding = direction;\n\t\t} else if(direction !== winding) {\n\t\t\tthrow new Error('Polygon vertices must describe a convex polygon in boundary order');\n\t\t}\n\t}\n\tfor(let i = 0; i < vertices.length; i++) {\n\t\tconst aStart = vertices[i];\n\t\tconst aEnd = vertices[(i + 1) % vertices.length];\n\t\tfor(let j = i + 1; j < vertices.length; j++) {\n\t\t\tif(j === i || j === i + 1 || i === 0 && j === vertices.length - 1) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst bStart = vertices[j];\n\t\t\tconst bEnd = vertices[(j + 1) % vertices.length];\n\t\t\tif(segmentsIntersect(aStart, aEnd, bStart, bEnd)) {\n\t\t\t\tthrow new Error('Polygon edges cannot cross or share non-adjacent vertices');\n\t\t\t}\n\t\t}\n\t}\n\n\tif((boxWidth === undefined) !== (boxHeight === undefined)) {\n\t\tthrow new Error('A sized polygon needs both a width and a height');\n\t}\n\tconst sized = boxWidth !== undefined && boxHeight !== undefined;\n\tif(sized) {\n\t\tif(!(boxWidth > 0) || !(boxHeight > 0)) {\n\t\t\tthrow new Error('A sized polygon needs a positive width and height');\n\t\t} else if(minX < -boxWidth / 2 || maxX > boxWidth / 2 || minY < -boxHeight / 2 || maxY > boxHeight / 2) {\n\t\t\t// The broadphase indexes the entity by its box, so an outline poking out of it would miss contacts.\n\t\t\tthrow new Error('Polygon vertices must fit inside the width and height they are given with');\n\t\t}\n\t}\n\n\tconst outputWidth = sized ? boxWidth : width;\n\tconst outputHeight = sized ? boxHeight : height;\n\tconst centreX = sized ? 0 : (minX + maxX) / 2;\n\tconst centreY = sized ? 0 : (minY + maxY) / 2;\n\tconst normalized: Array<number> = [];\n\tconst ordered = winding > 0 ? vertices : vertices.toReversed();\n\tfor(const [x, y] of ordered) {\n\t\tnormalized.push((x - centreX) / outputWidth, (y - centreY) / outputHeight);\n\t}\n\n\treturn { width: outputWidth, height: outputHeight, vertices: normalized };\n}\n\nfunction segmentsIntersect(aStart: PolygonVertex, aEnd: PolygonVertex, bStart: PolygonVertex, bEnd: PolygonVertex): boolean {\n\tconst aToStart = cross(aStart, aEnd, bStart);\n\tconst aToEnd = cross(aStart, aEnd, bEnd);\n\tconst bToStart = cross(bStart, bEnd, aStart);\n\tconst bToEnd = cross(bStart, bEnd, aEnd);\n\n\treturn aToStart * aToEnd <= 0 && bToStart * bToEnd <= 0;\n}\n\nfunction cross(start: PolygonVertex, end: PolygonVertex, point: PolygonVertex): number {\n\treturn (end[0] - start[0]) * (point[1] - start[1]) - (end[1] - start[1]) * (point[0] - start[0]);\n}\n\nexport default polygonDefinition;\n","import {\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_ANGLE_INDEX,\n\tTRANSFORM_SIZE,\n} from './transform-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\nimport { preparePolygon, type PolygonVertex } from './polygon-component';\n\n// Where an entity is, how big it is, and which way it faces: a position, its box, and the box's rotation.\nexport interface TransformComponent {\n\tindex: number\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n\tangle: number\n}\n\n// The defining half of a transform, from the entity template: the size that gives an entity a transform at all\n// (a config with no size does not load one). `width`/`height` are the unrotated box and required, since a\n// zero-area box never overlaps anything. `angle` is radians counter-clockwise from +x, the game's to set. A\n// round entity may give a `radius` instead, loaded as a width and height of its diameter; both at once throws.\nexport interface TransformConfig {\n\twidth: number\n\theight: number\n\tangle?: number\n\tradius?: number\n\t// A convex outline; alone its bounds become width/height, alongside them it must fit inside that box.\n\tvertices?: ReadonlyArray<PolygonVertex>\n}\n// The live runtime half: the physics system moves the position and can turn the box every run, so both round-trip\n// through `save`. Size comes back from the template. `x`/`y` are the centre of the box, not a corner; `angle` is\n// radians counter-clockwise from +x.\nexport interface TransformSerialization {\n\tx: number\n\ty: number\n\tangle?: number\n}\n\nclass TransformComponentImpl extends Component<Float32Array> implements TransformComponent {\n\tget x() {\n\t\treturn this.block[TRANSFORM_X_INDEX];\n\t}\n\tset x(value: number) {\n\t\tthis.block[TRANSFORM_X_INDEX] = value;\n\t}\n\tget y() {\n\t\treturn this.block[TRANSFORM_Y_INDEX];\n\t}\n\tset y(value: number) {\n\t\tthis.block[TRANSFORM_Y_INDEX] = value;\n\t}\n\tget width() {\n\t\treturn this.block[TRANSFORM_WIDTH_INDEX];\n\t}\n\tset width(value: number) {\n\t\tthis.block[TRANSFORM_WIDTH_INDEX] = value;\n\t}\n\tget height() {\n\t\treturn this.block[TRANSFORM_HEIGHT_INDEX];\n\t}\n\tset height(value: number) {\n\t\tthis.block[TRANSFORM_HEIGHT_INDEX] = value;\n\t}\n\tget angle() {\n\t\treturn this.block[TRANSFORM_ANGLE_INDEX];\n\t}\n\tset angle(value: number) {\n\t\tthis.block[TRANSFORM_ANGLE_INDEX] = value;\n\t}\n}\n\nexport const transformDefinition: ComponentDefinition<TransformComponent, Float32Array, TransformConfig, TransformSerialization> = {\n\ttype: Float32Array,\n\tsize: TRANSFORM_SIZE,\n\t// A size marks a config as having a transform. x/y are not listed: they come off a save, not the template.\n\tloadProperties: ['width', 'height', 'radius', 'vertices'],\n\ttoBlock(config) {\n\t\t// A radius is the same size said differently; turn it into width/height so nothing downstream cares. Both\n\t\t// at once cannot be honoured, so it throws rather than picking a winner.\n\t\tconst radius = config.radius;\n\t\tconst vertices = config.vertices;\n\t\tlet width = config.width;\n\t\tlet height = config.height;\n\t\tif(vertices !== undefined) {\n\t\t\tif(radius !== undefined) {\n\t\t\t\tthrow new Error('A transform takes polygon vertices or a radius, not both');\n\t\t\t}\n\n\t\t\tconst prepared = preparePolygon(vertices, width, height);\n\t\t\twidth = prepared.width;\n\t\t\theight = prepared.height;\n\t\t}\n\t\tif(radius !== undefined) {\n\t\t\tif(width !== undefined || height !== undefined) {\n\t\t\t\tthrow new Error('A transform takes either a radius or a width and height, not both');\n\t\t\t}\n\n\t\t\twidth = height = radius * 2;\n\t\t}\n\n\t\treturn [config.x, config.y, width, height, config.angle ?? 0];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new TransformComponentImpl(memory.getBlock(index), index);\n\t},\n\tsave(component) {\n\t\tconst saved: TransformSerialization = {\n\t\t\tx: component.x,\n\t\t\ty: component.y,\n\t\t};\n\t\tconst angle = component.angle;\n\t\tif(angle !== 0) {\n\t\t\tsaved.angle = angle;\n\t\t}\n\n\t\treturn saved;\n\t},\n};\n\nexport default transformDefinition;\n","export const VELOCITY_X_INDEX = 0;\nexport const VELOCITY_Y_INDEX = 1;\nexport const VELOCITY_DAMPING_INDEX = 2;\nexport const VELOCITY_SIZE = 3;\n","import {\n\tVELOCITY_X_INDEX,\n\tVELOCITY_Y_INDEX,\n\tVELOCITY_DAMPING_INDEX,\n\tVELOCITY_SIZE,\n} from './velocity-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\n// How fast an entity moves, in world units per second per axis. An entity with a transform and a velocity is\n// moved by the physics system every run. `damping` bleeds speed off each step so a mover coasts to a stop.\nexport interface VelocityComponent {\n\tindex: number\n\tvelocityX: number\n\tvelocityY: number\n\tdamping: number\n}\n\n// Named per-axis, not `x`/`y`, because configs are flat and `x`/`y` belong to the transform. Either axis alone\n// gives an entity a velocity, so both are optional and a missing one starts at 0.\nexport interface VelocityConfig {\n\tvelocityX?: number\n\tvelocityY?: number\n\t// Linear damping per second: the fraction of speed shed each second, applied as 1/(1+damping*dt) so it stays\n\t// stable at any step and never reverses. 0 (default) coasts forever; higher values stop sooner.\n\tdamping?: number\n}\n// Both spawn config and live state (a game's systems apply forces), so it round-trips through `save`.\nexport type VelocitySerialization = VelocityConfig;\n\nclass VelocityComponentImpl extends Component<Float32Array> implements VelocityComponent {\n\tget velocityX() {\n\t\treturn this.block[VELOCITY_X_INDEX];\n\t}\n\tset velocityX(value: number) {\n\t\tthis.block[VELOCITY_X_INDEX] = value;\n\t}\n\tget velocityY() {\n\t\treturn this.block[VELOCITY_Y_INDEX];\n\t}\n\tset velocityY(value: number) {\n\t\tthis.block[VELOCITY_Y_INDEX] = value;\n\t}\n\tget damping() {\n\t\treturn this.block[VELOCITY_DAMPING_INDEX];\n\t}\n\tset damping(value: number) {\n\t\tassertDamping(value);\n\t\tthis.block[VELOCITY_DAMPING_INDEX] = value;\n\t}\n}\n\nexport const velocityDefinition: ComponentDefinition<VelocityComponent, Float32Array, VelocityConfig, VelocitySerialization> = {\n\ttype: Float32Array,\n\tsize: VELOCITY_SIZE,\n\tloadProperties: ['velocityX', 'velocityY', 'damping'],\n\ttoBlock(config) {\n\t\tconst damping = config.damping ?? 0;\n\t\tassertDamping(damping);\n\n\t\treturn [config.velocityX ?? 0, config.velocityY ?? 0, damping];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new VelocityComponentImpl(memory.getBlock(index), index);\n\t},\n\tsave(component) {\n\t\treturn {\n\t\t\tvelocityX: component.velocityX,\n\t\t\tvelocityY: component.velocityY,\n\t\t\tdamping: component.damping,\n\t\t};\n\t},\n};\n\nfunction assertDamping(value: number): void {\n\tif(!Number.isFinite(value) || value < 0) {\n\t\tthrow new Error('Damping must be a finite number of at least zero');\n\t}\n}\n\nexport default velocityDefinition;\n","export const DYNAMICS_ACCELERATION_X_INDEX = 0;\nexport const DYNAMICS_ACCELERATION_Y_INDEX = 1;\nexport const DYNAMICS_INVERSE_MASS_INDEX = 2;\nexport const DYNAMICS_SIZE = 3;\n","import {\n\tDYNAMICS_ACCELERATION_X_INDEX,\n\tDYNAMICS_ACCELERATION_Y_INDEX,\n\tDYNAMICS_INVERSE_MASS_INDEX,\n\tDYNAMICS_SIZE,\n} from './dynamics-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\nexport interface DynamicsComponent {\n\tindex: number\n\taccelerationX: number\n\taccelerationY: number\n\tmass: number\n}\n\nexport interface DynamicsConfig {\n\taccelerationX?: number\n\taccelerationY?: number\n\tmass?: number\n}\n\nexport type DynamicsSerialization = DynamicsConfig;\n\nclass DynamicsComponentImpl extends Component<Float32Array> implements DynamicsComponent {\n\tget accelerationX() {\n\t\treturn this.block[DYNAMICS_ACCELERATION_X_INDEX];\n\t}\n\tset accelerationX(value: number) {\n\t\tassertFinite(value, 'Acceleration');\n\t\tthis.block[DYNAMICS_ACCELERATION_X_INDEX] = value;\n\t}\n\tget accelerationY() {\n\t\treturn this.block[DYNAMICS_ACCELERATION_Y_INDEX];\n\t}\n\tset accelerationY(value: number) {\n\t\tassertFinite(value, 'Acceleration');\n\t\tthis.block[DYNAMICS_ACCELERATION_Y_INDEX] = value;\n\t}\n\tget mass() {\n\t\treturn 1 / this.block[DYNAMICS_INVERSE_MASS_INDEX];\n\t}\n\tset mass(value: number) {\n\t\tassertMass(value);\n\t\tthis.block[DYNAMICS_INVERSE_MASS_INDEX] = 1 / value;\n\t}\n}\n\nexport const dynamicsDefinition: ComponentDefinition<DynamicsComponent, Float32Array, DynamicsConfig, DynamicsSerialization> = {\n\ttype: Float32Array,\n\tsize: DYNAMICS_SIZE,\n\tloadProperties: ['accelerationX', 'accelerationY', 'mass'],\n\ttoBlock(config) {\n\t\tconst accelerationX = config.accelerationX ?? 0;\n\t\tconst accelerationY = config.accelerationY ?? 0;\n\t\tconst mass = config.mass ?? 1;\n\t\tassertFinite(accelerationX, 'Acceleration');\n\t\tassertFinite(accelerationY, 'Acceleration');\n\t\tassertMass(mass);\n\n\t\treturn [accelerationX, accelerationY, 1 / mass];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new DynamicsComponentImpl(memory.getBlock(index), index);\n\t},\n\tsave(component) {\n\t\treturn {\n\t\t\taccelerationX: component.accelerationX,\n\t\t\taccelerationY: component.accelerationY,\n\t\t\tmass: component.mass,\n\t\t};\n\t},\n};\n\nfunction assertMass(value: number): void {\n\tif(!Number.isFinite(value) || value <= 0) {\n\t\tthrow new Error('Mass must be a finite number greater than zero');\n\t}\n}\n\nfunction assertFinite(value: number, name: string): void {\n\tif(!Number.isFinite(value)) {\n\t\tthrow new Error(`${name} must be a finite number`);\n\t}\n}\n\nexport default dynamicsDefinition;\n","export const SHAPE_RECTANGLE = 1;\nexport const SHAPE_CIRCLE = 2;\nexport const SHAPE_CAPSULE = 3;\nexport const SHAPE_POLYGON = 4;\nexport const DEFAULT_COLLIDE_CATEGORY = 1;\nexport const DEFAULT_COLLIDE_MASK = 0xFFFFFFFF;\nexport const BODY_FLAGS_INDEX = 0;\nexport const BODY_CATEGORY_INDEX = 1;\nexport const BODY_MASK_INDEX = 2;\nexport const BODY_SIZE = 3;\n// Shape occupies the low three bits; boolean flags occupy the remaining bits.\nexport const BODY_SHAPE_MASK = 0b111;\nexport const BODY_SENSOR_FLAG = 0b1000;\nexport const BODY_CCD_FLAG = 0b10000;\n// Runtime-only flag: defer removal until the final interpolation segment has played.\nexport const BODY_DYING_FLAG = 0b100000;\nexport const BODY_BLOCKS_PATH_FLAG = 0b1000000;\n","import {\n\tSHAPE_RECTANGLE,\n\tSHAPE_CIRCLE,\n\tSHAPE_CAPSULE,\n\tSHAPE_POLYGON,\n\tDEFAULT_COLLIDE_CATEGORY,\n\tDEFAULT_COLLIDE_MASK,\n\tBODY_FLAGS_INDEX,\n\tBODY_CATEGORY_INDEX,\n\tBODY_MASK_INDEX,\n\tBODY_SIZE,\n\tBODY_SHAPE_MASK,\n\tBODY_SENSOR_FLAG,\n\tBODY_CCD_FLAG,\n\tBODY_DYING_FLAG,\n\tBODY_BLOCKS_PATH_FLAG,\n} from './body-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\nimport type { PolygonVertex } from './polygon-component';\n\n// What an entity collides as and what it collides with. A body is loaded for anything with a size, so the way\n// to opt out is `collideCategory: 0`, not leaving the component off. Categories are the game's to name; the\n// library only reads them as bits.\nexport interface BodyComponent {\n\tindex: number\n\tshape: number\n\tcollideCategory: number\n\tcollideMask: number\n\t// A sensor takes part in detection - a mover overlapping it still gets its onCollision - but stops nothing:\n\t// nothing is swept short against it or bounces off it. Only about the response; it obeys canCollide unchanged.\n\tsensor: boolean\n\t// When set, this body's own move is tested along its whole path this run rather than only where it ends.\n\tcontinuousCollisionDetection: boolean\n\t// A hint for pathfinding to route around this body. Physics never reads it; it assumes the body never moves.\n\tblocksPath: boolean\n}\n\nconst KNOWN_SHAPES = [SHAPE_RECTANGLE, SHAPE_CIRCLE, SHAPE_CAPSULE, SHAPE_POLYGON];\n\n// Whether two bodies collide: each side's mask must accept the other's category. Symmetric by design, so the\n// outcome does not depend on which entity moved first; a one-sided response is done by checking `other` inside\n// onCollision. `collideCategory: 0` collides with nothing. `&` is signed, so compare against 0, not > 0.\nexport function canCollide(categoryA: number, maskA: number, categoryB: number, maskB: number): boolean {\n\treturn (maskA & categoryB) !== 0 && (maskB & categoryA) !== 0;\n}\n\n// A size loads a body, as it loads a transform: anything with a place in the world is collidable by default,\n// and the collide properties narrow that. All defining config from the entity template, so there is no `save`.\nexport interface BodyConfig {\n\t// Not read by the loader, only listed in `loadProperties`: they mark a config as having a place in the world.\n\twidth?: number\n\theight?: number\n\n\t// One of the SHAPE_ constants. Defaults to a circle for a `radius` config and a rectangle otherwise, so only\n\t// capsules name their shape.\n\tshape?: number\n\t// Not read here - the transform turns it into width/height - but a `radius` config decides the default shape.\n\tradius?: number\n\tvertices?: ReadonlyArray<PolygonVertex>\n\t// The bits this entity collides as, and the bits it collides with. See canCollide.\n\tcollideCategory?: number\n\tcollideMask?: number\n\t// Makes the body a sensor: found and reported, but never blocking or bounced off. Defaults to solid.\n\tsensor?: boolean\n\tcontinuousCollisionDetection?: boolean\n\t// Marks the body as something pathfinding routes around. Defaults to false.\n\tblocksPath?: boolean\n}\n\n// Readers off a raw body block, exported so the broadphase, bounce and game systems unpack the flags the same way.\nexport function bodyShape(body: Uint32Array): number {\n\treturn body[BODY_FLAGS_INDEX] & BODY_SHAPE_MASK;\n}\nexport function isSensor(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_SENSOR_FLAG) !== 0;\n}\nexport function isContinuous(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_CCD_FLAG) !== 0;\n}\nexport function blocksPath(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_BLOCKS_PATH_FLAG) !== 0;\n}\nexport function isDying(body: Uint32Array): boolean {\n\treturn (body[BODY_FLAGS_INDEX] & BODY_DYING_FLAG) !== 0;\n}\n// Sets the dying flag. One-way for the entity's remaining life - nothing clears it, since a dying entity is gone\n// a run later.\nexport function markDying(body: Uint32Array): void {\n\tbody[BODY_FLAGS_INDEX] |= BODY_DYING_FLAG;\n}\n\nclass BodyComponentImpl extends Component<Uint32Array> implements BodyComponent {\n\tget shape() {\n\t\treturn this.block[BODY_FLAGS_INDEX] & BODY_SHAPE_MASK;\n\t}\n\tset shape(value: number) {\n\t\tthis.block[BODY_FLAGS_INDEX] = (this.block[BODY_FLAGS_INDEX] & ~BODY_SHAPE_MASK) | (value & BODY_SHAPE_MASK);\n\t}\n\tget collideCategory() {\n\t\treturn this.block[BODY_CATEGORY_INDEX];\n\t}\n\tset collideCategory(value: number) {\n\t\tthis.block[BODY_CATEGORY_INDEX] = value;\n\t}\n\tget collideMask() {\n\t\treturn this.block[BODY_MASK_INDEX];\n\t}\n\tset collideMask(value: number) {\n\t\tthis.block[BODY_MASK_INDEX] = value;\n\t}\n\tget sensor() {\n\t\treturn (this.block[BODY_FLAGS_INDEX] & BODY_SENSOR_FLAG) !== 0;\n\t}\n\tset sensor(value: boolean) {\n\t\tthis.block[BODY_FLAGS_INDEX] = value ? (this.block[BODY_FLAGS_INDEX] | BODY_SENSOR_FLAG) : (this.block[BODY_FLAGS_INDEX] & ~BODY_SENSOR_FLAG);\n\t}\n\tget continuousCollisionDetection() {\n\t\treturn (this.block[BODY_FLAGS_INDEX] & BODY_CCD_FLAG) !== 0;\n\t}\n\tset continuousCollisionDetection(value: boolean) {\n\t\tthis.block[BODY_FLAGS_INDEX] = value ? (this.block[BODY_FLAGS_INDEX] | BODY_CCD_FLAG) : (this.block[BODY_FLAGS_INDEX] & ~BODY_CCD_FLAG);\n\t}\n\tget blocksPath() {\n\t\treturn (this.block[BODY_FLAGS_INDEX] & BODY_BLOCKS_PATH_FLAG) !== 0;\n\t}\n\tset blocksPath(value: boolean) {\n\t\tthis.block[BODY_FLAGS_INDEX] = value ? (this.block[BODY_FLAGS_INDEX] | BODY_BLOCKS_PATH_FLAG) : (this.block[BODY_FLAGS_INDEX] & ~BODY_BLOCKS_PATH_FLAG);\n\t}\n}\n\nexport const bodyDefinition: ComponentDefinition<BodyComponent, Uint32Array, BodyConfig> = {\n\ttype: Uint32Array,\n\tsize: BODY_SIZE,\n\tloadProperties: ['width', 'height', 'radius', 'vertices', 'shape', 'collideCategory', 'collideMask', 'sensor', 'continuousCollisionDetection', 'blocksPath'],\n\ttoBlock(config) {\n\t\treturn [\n\t\t\ttoFlags(config),\n\t\t\tconfig.collideCategory ?? DEFAULT_COLLIDE_CATEGORY,\n\t\t\tconfig.collideMask ?? DEFAULT_COLLIDE_MASK,\n\t\t];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new BodyComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\n// Packs the config's shape and boolean flags into the single flags word stored at BODY_FLAGS_INDEX.\nfunction toFlags(config: BodyConfig): number {\n\tlet flags = toShape(config);\n\tif(config.sensor) {\n\t\tflags |= BODY_SENSOR_FLAG;\n\t}\n\tif(config.continuousCollisionDetection) {\n\t\tflags |= BODY_CCD_FLAG;\n\t}\n\tif(config.blocksPath) {\n\t\tflags |= BODY_BLOCKS_PATH_FLAG;\n\t}\n\n\treturn flags;\n}\n\n// An unknown shape throws rather than defaulting: colliding with the wrong outline is harder to spot than a\n// failed load. A config that names no shape is read off its size - `radius` is a circle, anything else a rectangle.\nfunction toShape(config: BodyConfig): number {\n\tconst shape = config.shape;\n\tif(shape === undefined) {\n\t\treturn config.vertices !== undefined ? SHAPE_POLYGON : config.radius !== undefined ? SHAPE_CIRCLE : SHAPE_RECTANGLE;\n\t} else if(!KNOWN_SHAPES.includes(shape)) {\n\t\tthrow new Error(`Unknown body shape: ${shape}`);\n\t}\n\tif(shape === SHAPE_POLYGON && config.vertices === undefined) {\n\t\tthrow new Error('A polygon body requires vertices');\n\t}\n\n\treturn shape;\n}\n\nexport default bodyDefinition;\n","export const BOUNCINESS_INDEX = 0;\nexport const BOUNCINESS_SIZE = 1;\n","import { BOUNCINESS_INDEX, BOUNCINESS_SIZE } from './bounciness-component-constants';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\n// How much speed an entity keeps when it bounces, as a fraction of the velocity that pointed into what it hit:\n// 1 is a perfect bounce, 0.5 loses half on each, 0 does not turn around at all. See the bounce in\n// createPhysicsUpdate. Only bouncing entities carry this; one without it is still stopped by what it runs into\n// (the sweep does that for everything), it just does not rebound.\nexport interface BouncinessComponent {\n\tindex: number\n\tbounciness: number\n}\n\n// A `bounciness` config loads the component. Defining config from the entity template, so there is no `save`.\nexport interface BouncinessConfig {\n\tbounciness?: number\n}\n\nclass BouncinessComponentImpl extends Component<Float32Array> implements BouncinessComponent {\n\tget bounciness() {\n\t\treturn this.block[BOUNCINESS_INDEX];\n\t}\n\tset bounciness(value: number) {\n\t\tthis.block[BOUNCINESS_INDEX] = value;\n\t}\n}\n\nexport const bouncinessDefinition: ComponentDefinition<BouncinessComponent, Float32Array, BouncinessConfig> = {\n\ttype: Float32Array,\n\tsize: BOUNCINESS_SIZE,\n\tloadProperties: ['bounciness'],\n\ttoBlock(config) {\n\t\treturn [config.bounciness ?? 0];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new BouncinessComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\nexport default bouncinessDefinition;\n","export const INTERPOLATION_X_INDEX = 0;\nexport const INTERPOLATION_Y_INDEX = 1;\nexport const INTERPOLATION_SYNCED_TICK_INDEX = 2;\nexport const INTERPOLATION_DURATION_INDEX = 3;\n// Published last; NaN marks an in-progress transform and timing update.\nexport const INTERPOLATION_TICK_INDEX = 4;\n// Cumulative time preserves segments replaced before the renderer observes them.\nexport const INTERPOLATION_TOTAL_DURATION_INDEX = 5;\nexport const INTERPOLATION_SYNCED_DURATION_INDEX = 6;\nexport const INTERPOLATION_REMAINING_DURATION_INDEX = 7;\n// Consumer-owned target used while physics publishes the next segment.\nexport const INTERPOLATION_TARGET_X_INDEX = 8;\nexport const INTERPOLATION_TARGET_Y_INDEX = 9;\nexport const INTERPOLATION_SYNCED_STEP_DURATION_INDEX = 10;\n// Channel 0 is ungated; other channels wait for their PhysicsSystem to commit.\nexport const INTERPOLATION_CHANNEL_INDEX = 11;\n// Previous publication remains readable until the current run commits.\nexport const INTERPOLATION_PREVIOUS_X_INDEX = 12;\nexport const INTERPOLATION_PREVIOUS_Y_INDEX = 13;\nexport const INTERPOLATION_PREVIOUS_DURATION_INDEX = 14;\nexport const INTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX = 15;\n// Previous-slot stamp follows the same NaN-while-writing protocol.\nexport const INTERPOLATION_PREVIOUS_TICK_INDEX = 16;\nexport const INTERPOLATION_SIZE = 17;\n","import {\n\tINTERPOLATION_X_INDEX,\n\tINTERPOLATION_Y_INDEX,\n\tINTERPOLATION_SYNCED_TICK_INDEX,\n\tINTERPOLATION_DURATION_INDEX,\n\tINTERPOLATION_TICK_INDEX,\n\tINTERPOLATION_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_SYNCED_DURATION_INDEX,\n\tINTERPOLATION_REMAINING_DURATION_INDEX,\n\tINTERPOLATION_TARGET_X_INDEX,\n\tINTERPOLATION_TARGET_Y_INDEX,\n\tINTERPOLATION_SYNCED_STEP_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_X_INDEX,\n\tINTERPOLATION_PREVIOUS_Y_INDEX,\n\tINTERPOLATION_SIZE,\n} from './interpolation-component-constants';\nimport { loadFloat32, storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\n\n// Where an entity should be drawn, which diverges from where it is once physics runs less often than the screen\n// refreshes: a 50ms step against a 16ms frame updates the transform one frame in three, so drawing straight off\n// it stutters. This block holds a position that changes every frame instead.\n//\n// Filled by InterpolationSystem by advancing the render position toward its last coherent physics target.\n// The renderer consumes published simulation time without extrapolating past that target. Nothing in the\n// simulation reads it back - collision, the sweep and the spatial index all use the transform - which also keeps\n// it safe under lockstep multiplayer, where different frame rates would otherwise read different numbers.\nexport interface InterpolationComponent {\n\tindex: number\n\t// The render position, rewritten every frame by InterpolationSystem. A game reads it, and writes it only\n\t// through `snapEntity` after a teleport.\n\tx: number\n\ty: number\n\t// Publication timing and consumer-owned render bookkeeping.\n\tsyncedTick: number\n\tduration: number\n\ttick: number\n\ttotalDuration: number\n\tsyncedDuration: number\n\tremainingDuration: number\n\ttargetX: number\n\ttargetY: number\n\tsyncedStepDuration: number\n\tpreviousX: number\n\tpreviousY: number\n}\n\n// `interpolate` gives an entity this component, opt-in rather than automatic because it is 68 bytes and a\n// per-frame visit for something a game may never draw. `x`/`y` come off the same config the transform loads\n// from, so a render position starts where the entity spawned.\nexport interface InterpolationConfig {\n\tinterpolate?: boolean\n\tx?: number\n\ty?: number\n}\n\nclass InterpolationComponentImpl extends Component<Float32Array> implements InterpolationComponent {\n\tget x() {\n\t\treturn this.block[INTERPOLATION_X_INDEX];\n\t}\n\tset x(value: number) {\n\t\tthis.block[INTERPOLATION_X_INDEX] = value;\n\t}\n\tget y() {\n\t\treturn this.block[INTERPOLATION_Y_INDEX];\n\t}\n\tset y(value: number) {\n\t\tthis.block[INTERPOLATION_Y_INDEX] = value;\n\t}\n\tget syncedTick() {\n\t\treturn this.block[INTERPOLATION_SYNCED_TICK_INDEX];\n\t}\n\tset syncedTick(value: number) {\n\t\tthis.block[INTERPOLATION_SYNCED_TICK_INDEX] = value;\n\t}\n\tget duration() {\n\t\treturn this.block[INTERPOLATION_DURATION_INDEX];\n\t}\n\tset duration(value: number) {\n\t\tthis.block[INTERPOLATION_DURATION_INDEX] = value;\n\t}\n\t// The stamp everything else is published under, so it is read and written atomically like the physics update\n\t// does: a release store paired with the acquire load the interpolation update reads it through.\n\tget tick() {\n\t\treturn loadFloat32(this.block, INTERPOLATION_TICK_INDEX);\n\t}\n\tset tick(value: number) {\n\t\tstoreFloat32(this.block, INTERPOLATION_TICK_INDEX, value);\n\t}\n\tget totalDuration() {\n\t\treturn this.block[INTERPOLATION_TOTAL_DURATION_INDEX];\n\t}\n\tset totalDuration(value: number) {\n\t\tthis.block[INTERPOLATION_TOTAL_DURATION_INDEX] = value;\n\t}\n\tget syncedDuration() {\n\t\treturn this.block[INTERPOLATION_SYNCED_DURATION_INDEX];\n\t}\n\tset syncedDuration(value: number) {\n\t\tthis.block[INTERPOLATION_SYNCED_DURATION_INDEX] = value;\n\t}\n\tget targetX() {\n\t\treturn this.block[INTERPOLATION_TARGET_X_INDEX];\n\t}\n\tset targetX(value: number) {\n\t\tthis.block[INTERPOLATION_TARGET_X_INDEX] = value;\n\t}\n\tget targetY() {\n\t\treturn this.block[INTERPOLATION_TARGET_Y_INDEX];\n\t}\n\tset targetY(value: number) {\n\t\tthis.block[INTERPOLATION_TARGET_Y_INDEX] = value;\n\t}\n\tget syncedStepDuration() {\n\t\treturn this.block[INTERPOLATION_SYNCED_STEP_DURATION_INDEX];\n\t}\n\tset syncedStepDuration(value: number) {\n\t\tthis.block[INTERPOLATION_SYNCED_STEP_DURATION_INDEX] = value;\n\t}\n\tget previousX() {\n\t\treturn this.block[INTERPOLATION_PREVIOUS_X_INDEX];\n\t}\n\tset previousX(value: number) {\n\t\tthis.block[INTERPOLATION_PREVIOUS_X_INDEX] = value;\n\t}\n\tget previousY() {\n\t\treturn this.block[INTERPOLATION_PREVIOUS_Y_INDEX];\n\t}\n\tset previousY(value: number) {\n\t\tthis.block[INTERPOLATION_PREVIOUS_Y_INDEX] = value;\n\t}\n\tget remainingDuration() {\n\t\treturn this.block[INTERPOLATION_REMAINING_DURATION_INDEX];\n\t}\n\tset remainingDuration(value: number) {\n\t\tthis.block[INTERPOLATION_REMAINING_DURATION_INDEX] = value;\n\t}\n}\n\nexport const interpolationDefinition: ComponentDefinition<InterpolationComponent, Float32Array, InterpolationConfig> = {\n\ttype: Float32Array,\n\tsize: INTERPOLATION_SIZE,\n\tloadProperties: ['interpolate'],\n\ttoBlock(config) {\n\t\tconst x = config.x ?? 0;\n\t\tconst y = config.y ?? 0;\n\t\t// Seed both the render position and cached target at spawn; duration 0 means no published step yet.\n\t\treturn [x, y, 0, 0, 0, 0, 0, 0, x, y, 0, 0, x, y, 0, 0, 0];\n\t},\n\tattach(entity, memory, index) {\n\t\treturn new InterpolationComponentImpl(memory.getBlock(index), index);\n\t},\n\t// No `save`: everything here is derived from the transform and rebuilt on the next step, and `interpolate`\n\t// is defining config from the entity template.\n};\n\nexport default interpolationDefinition;\n","// A path is pending while the request and result ids differ. Requesters bump the request id; the pathfinding update\n// writes the waypoints and outcome first and the result id last, so a settled path is never read half written.\nexport const NAVIGATION_REQUEST_INDEX = 0;\nexport const NAVIGATION_RESULT_INDEX = 1;\nexport const NAVIGATION_OUTCOME_INDEX = 2;\n// Written only by the follower once a path settles; reset with each new path.\nexport const NAVIGATION_WAYPOINT_INDEX = 3;\nexport const NAVIGATION_WAYPOINT_COUNT_INDEX = 4;\n// Explicit flag because (0, 0) is a valid goal. Only active movers are searched for or rechecked.\nexport const NAVIGATION_ACTIVE_INDEX = 5;\n// Goal and waypoints are Float32 bit patterns in the Int32 block, read with loadFloat32.\nexport const NAVIGATION_GOAL_X_INDEX = 6;\nexport const NAVIGATION_GOAL_Y_INDEX = 7;\nexport const NAVIGATION_WAYPOINTS_INDEX = 8;\n// A longer path is cut short and marked Incomplete, so the follower asks again from its end.\nexport const NAVIGATION_MAX_WAYPOINTS = 32;\nexport const NAVIGATION_SIZE = NAVIGATION_WAYPOINTS_INDEX + NAVIGATION_MAX_WAYPOINTS * 2;\n","import type NavGrid from './nav-grid';\n\nexport interface Point {\n\tx: number\n\ty: number\n}\n\n// Positions are the mover's centre in world units; its size decides which gaps it fits through.\nexport interface PathRequest {\n\tstartX: number\n\tstartY: number\n\tgoalX: number\n\tgoalY: number\n\tagentWidth: number\n\tagentHeight: number\n}\n\nexport const PathStatus = {\n\t// Ends on the goal.\n\tFound: 1,\n\t// Ran out of search budget; heads toward the goal and should be searched again from its end.\n\tIncomplete: 2,\n\t// No way to the goal. Any points lead to the reachable spot closest to it.\n\tUnreachable: 3,\n} as const;\nexport type PathStatusValue = typeof PathStatus[keyof typeof PathStatus];\n\n// `points` excludes the start: they are the waypoints to walk, in order.\nexport interface PathResult {\n\tstatus: PathStatusValue\n\tpoints: Array<Point>\n\titerations: number\n}\n\n// The seam for swapping algorithms: anything that can answer a request against a grid.\nexport interface PathSearch {\n\tfindPath(grid: NavGrid, request: PathRequest): PathResult\n}","import { loadFloat32, storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { Component } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentDefinition } from '@daneren2005/shared-memory-ecs';\nimport {\n\tNAVIGATION_ACTIVE_INDEX,\n\tNAVIGATION_GOAL_X_INDEX,\n\tNAVIGATION_GOAL_Y_INDEX,\n\tNAVIGATION_MAX_WAYPOINTS,\n\tNAVIGATION_OUTCOME_INDEX,\n\tNAVIGATION_REQUEST_INDEX,\n\tNAVIGATION_RESULT_INDEX,\n\tNAVIGATION_SIZE,\n\tNAVIGATION_WAYPOINT_COUNT_INDEX,\n\tNAVIGATION_WAYPOINT_INDEX,\n\tNAVIGATION_WAYPOINTS_INDEX,\n} from './navigation-component-constants';\nimport { PathStatus, type Point } from '../pathfinding/path-search';\n\n// A goal to walk to and the path PathfindingSystem found there. Who follows the path (sets velocity along it) is up to\n// the game; followPath does the bookkeeping.\nexport interface NavigationComponent {\n\tindex: number\n\treadonly active: boolean\n\treadonly goalX: number\n\treadonly goalY: number\n\treadonly pending: boolean\n\treadonly outcome: number\n\treadonly waypointIndex: number\n\treadonly waypoints: Array<Point>\n\tnavigateTo(x: number, y: number): void\n\tstop(): void\n}\n\n// A capability tag. Goals and paths are transient: whoever set a goal re-issues it after a load.\nexport interface NavigationConfig {\n\tnavigate: boolean\n}\n\nclass NavigationComponentImpl extends Component<Int32Array> implements NavigationComponent {\n\tget active() {\n\t\treturn this.block[NAVIGATION_ACTIVE_INDEX] !== 0;\n\t}\n\tget goalX() {\n\t\treturn loadFloat32(this.block, NAVIGATION_GOAL_X_INDEX);\n\t}\n\tget goalY() {\n\t\treturn loadFloat32(this.block, NAVIGATION_GOAL_Y_INDEX);\n\t}\n\tget pending() {\n\t\treturn isPathPending(this.block);\n\t}\n\tget outcome() {\n\t\treturn this.block[NAVIGATION_OUTCOME_INDEX];\n\t}\n\tget waypointIndex() {\n\t\treturn this.block[NAVIGATION_WAYPOINT_INDEX];\n\t}\n\tget waypoints() {\n\t\treturn Array.from({ length: this.block[NAVIGATION_WAYPOINT_COUNT_INDEX] }, (_, i) => waypoint(this.block, i));\n\t}\n\tnavigateTo(x: number, y: number) {\n\t\tnavigateTo(this.block, x, y);\n\t}\n\tstop() {\n\t\tstopNavigating(this.block);\n\t}\n}\n\nexport const navigationDefinition: ComponentDefinition<NavigationComponent, Int32Array, NavigationConfig> = {\n\ttype: Int32Array,\n\tsize: NAVIGATION_SIZE,\n\tloadProperties: ['navigate'],\n\ttoBlock: () => Array.from({ length: NAVIGATION_SIZE }, () => 0),\n\tattach(entity, memory, index) {\n\t\treturn new NavigationComponentImpl(memory.getBlock(index), index);\n\t},\n};\n\n// Sets a new goal and asks for a path to it.\nexport function navigateTo(navigation: Int32Array, x: number, y: number): void {\n\tsetNavigationGoal(navigation, x, y);\n\tnavigation[NAVIGATION_ACTIVE_INDEX] = 1;\n\trequestPath(navigation);\n}\n\n// Nudges the goal without searching again: the path already found still leads near it, and its last leg heads here.\nexport function setNavigationGoal(navigation: Int32Array, x: number, y: number): void {\n\tstoreFloat32(navigation, NAVIGATION_GOAL_X_INDEX, x);\n\tstoreFloat32(navigation, NAVIGATION_GOAL_Y_INDEX, y);\n}\n\nexport function stopNavigating(navigation: Int32Array): void {\n\tnavigation[NAVIGATION_ACTIVE_INDEX] = 0;\n}\n\nexport function isNavigating(navigation: Int32Array): boolean {\n\treturn navigation[NAVIGATION_ACTIVE_INDEX] !== 0;\n}\n\n// Searches again for the current goal.\nexport function requestPath(navigation: Int32Array): void {\n\tAtomics.add(navigation, NAVIGATION_REQUEST_INDEX, 1);\n}\n\nexport function isPathPending(navigation: Int32Array): boolean {\n\treturn Atomics.load(navigation, NAVIGATION_REQUEST_INDEX) !== Atomics.load(navigation, NAVIGATION_RESULT_INDEX);\n}\n\nexport function isUnreachable(navigation: Int32Array): boolean {\n\treturn !isPathPending(navigation) && navigation[NAVIGATION_OUTCOME_INDEX] === PathStatus.Unreachable;\n}\n\nexport function navigationGoal(navigation: Int32Array): Point {\n\treturn { x: loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX), y: loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX) };\n}\n\nexport function waypoint(navigation: Int32Array, i: number): Point {\n\treturn { x: waypointX(navigation, i), y: waypointY(navigation, i) };\n}\n\nexport function waypointX(navigation: Int32Array, i: number): number {\n\treturn loadFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2);\n}\n\nexport function waypointY(navigation: Int32Array, i: number): number {\n\treturn loadFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2 + 1);\n}\n\n// Leaves the result id alone: the writer stores it last, once the path is in place.\nexport function writePath(navigation: Int32Array, status: number, points: ReadonlyArray<Point>): void {\n\tconst truncated = points.length > NAVIGATION_MAX_WAYPOINTS;\n\tconst count = truncated ? NAVIGATION_MAX_WAYPOINTS : points.length;\n\tfor(let i = 0; i < count; i++) {\n\t\tstoreFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2, points[i].x);\n\t\tstoreFloat32(navigation, NAVIGATION_WAYPOINTS_INDEX + i * 2 + 1, points[i].y);\n\t}\n\tnavigation[NAVIGATION_WAYPOINT_COUNT_INDEX] = count;\n\tnavigation[NAVIGATION_WAYPOINT_INDEX] = 0;\n\tnavigation[NAVIGATION_OUTCOME_INDEX] = truncated ? PathStatus.Incomplete : status;\n}\n\nexport default navigationDefinition;\n","import { bodyDefinition, type BodyComponent } from './body-component';\nimport { bouncinessDefinition, type BouncinessComponent } from './bounciness-component';\nimport { dynamicsDefinition, type DynamicsComponent } from './dynamics-component';\nimport { interpolationDefinition, type InterpolationComponent } from './interpolation-component';\nimport { navigationDefinition, type NavigationComponent } from './navigation-component';\nimport { polygonDefinition, type PolygonComponent } from './polygon-component';\nimport { transformDefinition, type TransformComponent } from './transform-component';\nimport { velocityDefinition, type VelocityComponent } from './velocity-component';\n\n// The component definitions this library supplies. A game spreads them into its own registry:\n//\n// const registry = { ...physicsRegistry, health: healthDefinition };\n//\n// The keys here are what the physics systems query on, so they must stay as the registry keys.\nexport const physicsRegistry = {\n\ttransform: transformDefinition,\n\tvelocity: velocityDefinition,\n\tbody: bodyDefinition,\n\tpolygon: polygonDefinition,\n\tbounciness: bouncinessDefinition,\n\tdynamics: dynamicsDefinition,\n\tinterpolation: interpolationDefinition,\n};\n\n// Kept out of physicsRegistry so only games with pathfinding register it. Spread in alongside it:\n//\n// const registry = { ...physicsRegistry, ...navigationRegistry };\nexport const navigationRegistry = {\n\tnavigation: navigationDefinition,\n};\n\n// The slice of a game's component map the physics systems need. A type alias, not an interface, so it satisfies\n// the ECS's `ComponentMap` index signature; systems only require the game's map extends this.\nexport type PhysicsComponents = {\n\ttransform: TransformComponent\n\tvelocity: VelocityComponent\n\tbody: BodyComponent\n\tpolygon: PolygonComponent\n\tbounciness: BouncinessComponent\n\tdynamics: DynamicsComponent\n\tinterpolation: InterpolationComponent\n};\n\n// The strictly smaller slice InterpolationSystem needs: it never looks at velocity or body. Split out so the\n// interpolation update declares exactly what it touches.\nexport type InterpolationComponents = {\n\ttransform: TransformComponent\n\tinterpolation: InterpolationComponent\n};\n\n// The slice PathfindingSystem needs.\nexport type NavigationComponents = {\n\tnavigation: NavigationComponent\n\ttransform: TransformComponent\n\tbody: BodyComponent\n};\n\n// The same components as their backing arrays, the form an update sees on the worker thread. `body`, `bounciness`,\n// `dynamics` and `interpolation` are optional because an entity may lack them; the collidable query requires `body`, so\n// CollisionComponents narrows it back to guaranteed there. A game widens this for extra callback components:\n//\n// type GameUpdateComponents = PhysicsUpdateComponents & { health?: Float32Array };\nexport type PhysicsUpdateComponents = {\n\ttransform: Float32Array\n\tvelocity: Float32Array\n\tbody?: Uint32Array\n\tpolygon?: Float32Array\n\tbounciness?: Float32Array\n\tdynamics?: Float32Array\n\tinterpolation?: Float32Array\n\t// For checking if entity is dead during run\n\tentity?: Uint32Array\n};\n\n// The blocks the interpolation update works on. Both required - the query asks for both - so no per-entity guard.\nexport type InterpolationUpdateComponents = {\n\ttransform: Float32Array\n\tinterpolation: Float32Array\n};\n","// Pure geometry for the shapes a transform plus a body describe, centred on (x, y) and turned `angle` radians\n// counter-clockwise. Loose number arguments rather than objects so the collision sweep can call per pair\n// without allocating.\n//\n// The three shapes are one idea: an oriented core grown by a radius.\n// rectangle - a width x height box, radius 0\n// circle - a point, radius width / 2\n// capsule - a segment along its longer side, radius half the shorter side\n// A circle is a capsule whose segment has no length, so there are only two core kinds - box and segment - and\n// three pair tests rather than one per shape combination. See shapesOverlap.\n\nimport { SHAPE_CAPSULE, SHAPE_CIRCLE, SHAPE_RECTANGLE } from '../components/body-component-constants';\n\nexport interface Vector {\n\tx: number\n\ty: number\n}\n\n// Scratch, reused rather than allocated: every user is a single unbroken pass, never re-entered part way through.\nconst GAP: Vector = { x: 0, y: 0 };\nconst CANDIDATE: Vector = { x: 0, y: 0 };\n\nexport function capsuleHalfLength(width: number, height: number): number {\n\treturn Math.abs(width - height) / 2;\n}\n\n// The angle a capsule's core segment lies at: its facing, or a quarter turn off it when it is taller than wide.\nexport function capsuleCoreAngle(width: number, height: number, angle: number): number {\n\treturn height > width ? angle + Math.PI / 2 : angle;\n}\n\nexport function shapeRadius(shape: number, width: number, height: number): number {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width / 2;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn Math.min(width, height) / 2;\n\t}\n\n\treturn 0;\n}\n\n// A shape with no area never overlaps anything, which keeps a sizeless entity out of collisions. Which size\n// matters is per shape: a circle is nothing without its width, a capsule with no thickness is a bare line.\nexport function shapeIsEmpty(shape: number, width: number, height: number): boolean {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width <= 0;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn width <= 0 || height <= 0;\n\t}\n\n\treturn width <= 0 || height <= 0;\n}\n\n// Half the axis-aligned box that fully contains a shape at any rotation - what the broadphase indexes it under,\n// so it must hold the whole shape. Asked per shape because a circle is its width alone: one whose height was\n// hand-written smaller is taller than the width/height rectangle a capsule or radius-loaded circle fits in.\nexport function shapeHalfWidth(shape: number, width: number, height: number, angle: number): number {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width / 2;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn Math.abs(Math.cos(capsuleCoreAngle(width, height, angle))) * capsuleHalfLength(width, height) + Math.min(width, height) / 2;\n\t}\n\n\treturn boundsHalfWidth(width, height, angle);\n}\nexport function shapeHalfHeight(shape: number, width: number, height: number, angle: number): number {\n\tif(shape === SHAPE_CIRCLE) {\n\t\treturn width / 2;\n\t} else if(shape === SHAPE_CAPSULE) {\n\t\treturn Math.abs(Math.sin(capsuleCoreAngle(width, height, angle))) * capsuleHalfLength(width, height) + Math.min(width, height) / 2;\n\t}\n\n\treturn boundsHalfHeight(width, height, angle);\n}\n\n// Whether two shapes of any kind overlap. Every pair is one of three cases: box+box (separating axis),\n// round+round (segment gap under the summed radii), or box+round (segment gap under the one radius). Touching\n// exactly counts as not overlapping throughout - the comparisons are strict.\nexport function shapesOverlap(\n\taShape: number, aX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\tif(shapeIsEmpty(aShape, aWidth, aHeight) || shapeIsEmpty(bShape, bWidth, bHeight)) {\n\t\treturn false;\n\t}\n\n\tconst aIsRound = aShape !== SHAPE_RECTANGLE;\n\tconst bIsRound = bShape !== SHAPE_RECTANGLE;\n\n\tif(!aIsRound && !bIsRound) {\n\t\treturn orientedBoxesOverlap(aX, aY, aWidth, aHeight, aAngle, bX, bY, bWidth, bHeight, bAngle);\n\t} else if(aIsRound && !bIsRound) {\n\t\t// The mixed case is written once with the box first and reached from the other side by swapping.\n\t\treturn shapesOverlap(\n\t\t\tbShape, bX, bY, bWidth, bHeight, bAngle,\n\t\t\taShape, aX, aY, aWidth, aHeight, aAngle,\n\t\t);\n\t}\n\n\tconst bHalf = coreHalfLength(bShape, bWidth, bHeight);\n\tconst bCoreAngle = capsuleCoreAngle(bWidth, bHeight, bAngle);\n\tconst bCos = Math.cos(bCoreAngle) * bHalf;\n\tconst bSin = Math.sin(bCoreAngle) * bHalf;\n\tconst bRadius = shapeRadius(bShape, bWidth, bHeight);\n\n\tif(!aIsRound) {\n\t\treturn segmentBoxDistanceSquared(\n\t\t\tbX - bCos, bY - bSin, bX + bCos, bY + bSin,\n\t\t\taX, aY, aWidth, aHeight, aAngle,\n\t\t) < bRadius * bRadius;\n\t}\n\n\tconst aHalf = coreHalfLength(aShape, aWidth, aHeight);\n\tconst aCoreAngle = capsuleCoreAngle(aWidth, aHeight, aAngle);\n\tconst aCos = Math.cos(aCoreAngle) * aHalf;\n\tconst aSin = Math.sin(aCoreAngle) * aHalf;\n\tconst reach = shapeRadius(aShape, aWidth, aHeight) + bRadius;\n\n\treturn segmentSegmentDistanceSquared(\n\t\taX - aCos, aY - aSin, aX + aCos, aY + aSin,\n\t\tbX - bCos, bY - bSin, bX + bCos, bY + bSin,\n\t) < reach * reach;\n}\n\n// How far a shape's core reaches from its centre: a capsule's segment, and nothing for a circle or rectangle.\nfunction coreHalfLength(shape: number, width: number, height: number): number {\n\treturn shape === SHAPE_CAPSULE ? capsuleHalfLength(width, height) : 0;\n}\n\n// The unit direction a is pushed off b at their contact, written into `out`; false when the two are on top of\n// each other and there is no such direction. Split the same three ways as shapesOverlap and off the same\n// geometry, so the normal is the face that was actually hit rather than a guess from the bounding boxes: a pair\n// meeting at an angle reflects along the contact, not along whichever world axis happens to be nearest.\nexport function contactNormal(\n\taShape: number, aX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tconst aIsRound = aShape !== SHAPE_RECTANGLE;\n\tconst bIsRound = bShape !== SHAPE_RECTANGLE;\n\n\tif(!aIsRound && !bIsRound) {\n\t\treturn boxesContactNormal(aX, aY, aWidth, aHeight, aAngle, bX, bY, bWidth, bHeight, bAngle, out);\n\t} else if(aIsRound && bIsRound) {\n\t\treturn roundsContactNormal(aShape, aX, aY, aWidth, aHeight, aAngle, bShape, bX, bY, bWidth, bHeight, bAngle, out);\n\t} else if(aIsRound) {\n\t\treturn boxRoundContactNormal(bX, bY, bWidth, bHeight, bAngle, aShape, aX, aY, aWidth, aHeight, aAngle, out);\n\t}\n\n\t// The mixed case is written once with the box second and reached from the other side by turning it round.\n\tif(!boxRoundContactNormal(aX, aY, aWidth, aHeight, aAngle, bShape, bX, bY, bWidth, bHeight, bAngle, out)) {\n\t\treturn false;\n\t}\n\n\tout.x = -out.x;\n\tout.y = -out.y;\n\n\treturn true;\n}\n\n// Two boxes part along whichever of their four side directions they run into each other least far - the face\n// that was hit. The same axes and reaches the separating-axis test uses, kept as depths rather than a verdict.\nfunction boxesContactNormal(\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tconst aCos = Math.cos(aAngle);\n\tconst aSin = Math.sin(aAngle);\n\tconst bCos = Math.cos(bAngle);\n\tconst bSin = Math.sin(bAngle);\n\tconst aHalfWidth = aWidth / 2;\n\tconst aHalfHeight = aHeight / 2;\n\tconst bHalfWidth = bWidth / 2;\n\tconst bHalfHeight = bHeight / 2;\n\tconst offsetX = aX - bX;\n\tconst offsetY = aY - bY;\n\n\tlet best = boxOverlapAlong(aCos, aSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tout.x = aCos;\n\tout.y = aSin;\n\n\tlet depth = boxOverlapAlong(-aSin, aCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tif(depth < best) {\n\t\tbest = depth;\n\t\tout.x = -aSin;\n\t\tout.y = aCos;\n\t}\n\n\tdepth = boxOverlapAlong(bCos, bSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tif(depth < best) {\n\t\tbest = depth;\n\t\tout.x = bCos;\n\t\tout.y = bSin;\n\t}\n\n\tdepth = boxOverlapAlong(-bSin, bCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin);\n\tif(depth < best) {\n\t\tout.x = -bSin;\n\t\tout.y = bCos;\n\t}\n\n\tpointAwayFrom(offsetX, offsetY, out);\n\n\treturn true;\n}\n\n// Two rounds meet where their cores come closest - the line between those points, grown by nothing since both\n// radii lie along it. Two circles reduce to the line between their centres.\nfunction roundsContactNormal(\n\taShape: number, aX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tconst aHalf = coreHalfLength(aShape, aWidth, aHeight);\n\tconst aCoreAngle = capsuleCoreAngle(aWidth, aHeight, aAngle);\n\tconst aCos = Math.cos(aCoreAngle) * aHalf;\n\tconst aSin = Math.sin(aCoreAngle) * aHalf;\n\tconst bHalf = coreHalfLength(bShape, bWidth, bHeight);\n\tconst bCoreAngle = capsuleCoreAngle(bWidth, bHeight, bAngle);\n\tconst bCos = Math.cos(bCoreAngle) * bHalf;\n\tconst bSin = Math.sin(bCoreAngle) * bHalf;\n\n\tconst distance = Math.sqrt(segmentSegmentDistanceSquared(\n\t\taX - aCos, aY - aSin, aX + aCos, aY + aSin,\n\t\tbX - bCos, bY - bSin, bX + bCos, bY + bSin,\n\t\tGAP,\n\t));\n\tif(distance > 0) {\n\t\tout.x = GAP.x / distance;\n\t\tout.y = GAP.y / distance;\n\n\t\treturn true;\n\t}\n\n\t// The cores cross, so they have no gap to part along; the centre line is the best left.\n\treturn normalize(aX - bX, aY - bY, out);\n}\n\n// A round shape leaves a box along the gap between its core and the box - the box's face for a flat hit, its\n// corner for one off the end - which is the direction the round side is pushed. Not the box: the caller turns\n// it round for that.\nfunction boxRoundContactNormal(\n\tboxX: number, boxY: number, boxWidth: number, boxHeight: number, boxAngle: number,\n\troundShape: number, roundX: number, roundY: number, roundWidth: number, roundHeight: number, roundAngle: number,\n\tout: Vector,\n): boolean {\n\tconst half = coreHalfLength(roundShape, roundWidth, roundHeight);\n\tconst coreAngle = capsuleCoreAngle(roundWidth, roundHeight, roundAngle);\n\tconst halfX = Math.cos(coreAngle) * half;\n\tconst halfY = Math.sin(coreAngle) * half;\n\n\tconst distance = Math.sqrt(segmentBoxDistanceSquared(\n\t\troundX - halfX, roundY - halfY, roundX + halfX, roundY + halfY,\n\t\tboxX, boxY, boxWidth, boxHeight, boxAngle,\n\t\tGAP,\n\t));\n\tif(distance > 0) {\n\t\tout.x = GAP.x / distance;\n\t\tout.y = GAP.y / distance;\n\n\t\treturn true;\n\t}\n\n\t// The core runs through the box, leaving no gap to point along. Fall back to the shallowest way out, in the\n\t// box's frame where it is axis aligned: its own two sides, or square across the core.\n\tconst cos = Math.cos(boxAngle);\n\tconst sin = Math.sin(boxAngle);\n\tconst offsetX = roundX - boxX;\n\tconst offsetY = roundY - boxY;\n\tconst localX = offsetX * cos + offsetY * sin;\n\tconst localY = -offsetX * sin + offsetY * cos;\n\tconst localHalfX = halfX * cos + halfY * sin;\n\tconst localHalfY = -halfX * sin + halfY * cos;\n\tconst radius = shapeRadius(roundShape, roundWidth, roundHeight);\n\tconst halfWidth = boxWidth / 2;\n\tconst halfHeight = boxHeight / 2;\n\n\tlet best = roundOverlapAlong(1, 0, localX, localY, localHalfX, localHalfY, radius, halfWidth, halfHeight);\n\tout.x = 1;\n\tout.y = 0;\n\n\tlet depth = roundOverlapAlong(0, 1, localX, localY, localHalfX, localHalfY, radius, halfWidth, halfHeight);\n\tif(depth < best) {\n\t\tbest = depth;\n\t\tout.x = 0;\n\t\tout.y = 1;\n\t}\n\n\tif(normalize(-localHalfY, localHalfX, CANDIDATE)) {\n\t\tdepth = roundOverlapAlong(CANDIDATE.x, CANDIDATE.y, localX, localY, localHalfX, localHalfY, radius, halfWidth, halfHeight);\n\t\tif(depth < best) {\n\t\t\tout.x = CANDIDATE.x;\n\t\t\tout.y = CANDIDATE.y;\n\t\t}\n\t}\n\n\tpointAwayFrom(localX, localY, out);\n\n\t// Back out of the box's frame.\n\tconst axisX = out.x;\n\tconst axisY = out.y;\n\tout.x = axisX * cos - axisY * sin;\n\tout.y = axisX * sin + axisY * cos;\n\n\treturn true;\n}\n\n// How far an axis-aligned box at the origin and a segment grown by `radius` run into each other along a unit\n// axis. The round side reaches its core's shadow plus the radius, which is the same in every direction.\nfunction roundOverlapAlong(\n\taxisX: number, axisY: number,\n\tcentreX: number, centreY: number,\n\thalfX: number, halfY: number, radius: number,\n\thalfWidth: number, halfHeight: number,\n): number {\n\tconst boxReach = halfWidth * Math.abs(axisX) + halfHeight * Math.abs(axisY);\n\tconst roundReach = Math.abs(halfX * axisX + halfY * axisY) + radius;\n\n\treturn boxReach + roundReach - Math.abs(centreX * axisX + centreY * axisY);\n}\n\n// Turns an axis the way that leads away from the other shape. A tie means the centres sit on the axis' line, and\n// either side is as good.\nfunction pointAwayFrom(offsetX: number, offsetY: number, out: Vector): void {\n\tif(offsetX * out.x + offsetY * out.y < 0) {\n\t\tout.x = -out.x;\n\t\tout.y = -out.y;\n\t}\n}\n\nfunction normalize(x: number, y: number, out: Vector): boolean {\n\tconst length = Math.sqrt(x * x + y * y);\n\tif(length === 0) {\n\t\treturn false;\n\t}\n\n\tout.x = x / length;\n\tout.y = y / length;\n\n\treturn true;\n}\n\n// Half the axis-aligned box containing a rotated rectangle: each side contributes however much now points along\n// the axis. width / 2 at angle 0, height / 2 at a quarter turn.\nexport function boundsHalfWidth(width: number, height: number, angle: number): number {\n\treturn (Math.abs(Math.cos(angle)) * width + Math.abs(Math.sin(angle)) * height) / 2;\n}\nexport function boundsHalfHeight(width: number, height: number, angle: number): number {\n\treturn (Math.abs(Math.sin(angle)) * width + Math.abs(Math.cos(angle)) * height) / 2;\n}\n\n// Whether two rotated boxes overlap, by the separating axis theorem: for two boxes only four axes can separate\n// them - the two each box's sides face along. Touching exactly and a zero-area box both count as not\n// overlapping, which keeps a sizeless entity out of collisions.\nexport function orientedBoxesOverlap(\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\tif(aWidth <= 0 || aHeight <= 0 || bWidth <= 0 || bHeight <= 0) {\n\t\treturn false;\n\t}\n\n\tconst aCos = Math.cos(aAngle);\n\tconst aSin = Math.sin(aAngle);\n\tconst bCos = Math.cos(bAngle);\n\tconst bSin = Math.sin(bAngle);\n\n\tconst aHalfWidth = aWidth / 2;\n\tconst aHalfHeight = aHeight / 2;\n\tconst bHalfWidth = bWidth / 2;\n\tconst bHalfHeight = bHeight / 2;\n\n\tconst offsetX = bX - aX;\n\tconst offsetY = bY - aY;\n\n\t// a's two axes, then b's; each box's sides face along (cos, sin) and (-sin, cos) of its angle.\n\treturn boxOverlapAlong(aCos, aSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0\n\t\t&& boxOverlapAlong(-aSin, aCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0\n\t\t&& boxOverlapAlong(bCos, bSin, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0\n\t\t&& boxOverlapAlong(-bSin, bCos, offsetX, offsetY, aHalfWidth, aHalfHeight, aCos, aSin, bHalfWidth, bHalfHeight, bCos, bSin) > 0;\n}\n\n// How far the boxes' shadows on the axis run into each other; zero or less means the axis separates them, so\n// touching exactly does not overlap. The axes are always a box's own unit-vector sides, so the dot products are\n// true distances and nothing needs normalising. The depth is what picks the contact face in contactNormal.\nfunction boxOverlapAlong(\n\taxisX: number, axisY: number,\n\toffsetX: number, offsetY: number,\n\taHalfWidth: number, aHalfHeight: number, aCos: number, aSin: number,\n\tbHalfWidth: number, bHalfHeight: number, bCos: number, bSin: number,\n): number {\n\tconst aReach = aHalfWidth * Math.abs(aCos * axisX + aSin * axisY) + aHalfHeight * Math.abs(-aSin * axisX + aCos * axisY);\n\tconst bReach = bHalfWidth * Math.abs(bCos * axisX + bSin * axisY) + bHalfHeight * Math.abs(-bSin * axisX + bCos * axisY);\n\n\treturn aReach + bReach - Math.abs(offsetX * axisX + offsetY * axisY);\n}\n\n// Squared distance between two segments - squared so callers can compare against a squared radius without a\n// sqrt. Settles every round pair (a circle is a segment whose ends coincide). Solves the closest points on the\n// two infinite lines then clamps back into the segments; the parallel case pins one end and clamps the rest.\n// `out`, if given, is filled with the vector from b's closest point to a's - the direction a is pushed off b.\nexport function segmentSegmentDistanceSquared(\n\taX1: number, aY1: number, aX2: number, aY2: number,\n\tbX1: number, bY1: number, bX2: number, bY2: number,\n\tout?: Vector,\n): number {\n\tconst aDeltaX = aX2 - aX1;\n\tconst aDeltaY = aY2 - aY1;\n\tconst bDeltaX = bX2 - bX1;\n\tconst bDeltaY = bY2 - bY1;\n\tconst offsetX = aX1 - bX1;\n\tconst offsetY = aY1 - bY1;\n\n\tconst aLength = aDeltaX * aDeltaX + aDeltaY * aDeltaY;\n\tconst bLength = bDeltaX * bDeltaX + bDeltaY * bDeltaY;\n\tconst bOffset = bDeltaX * offsetX + bDeltaY * offsetY;\n\n\tlet aParam = 0;\n\tlet bParam = 0;\n\tif(aLength <= 0 && bLength <= 0) {\n\t\tif(out) {\n\t\t\tout.x = offsetX;\n\t\t\tout.y = offsetY;\n\t\t}\n\n\t\treturn offsetX * offsetX + offsetY * offsetY;\n\t} else if(aLength <= 0) {\n\t\tbParam = clamp(bOffset / bLength);\n\t} else {\n\t\tconst aOffset = aDeltaX * offsetX + aDeltaY * offsetY;\n\t\tif(bLength <= 0) {\n\t\t\taParam = clamp(-aOffset / aLength);\n\t\t} else {\n\t\t\tconst along = aDeltaX * bDeltaX + aDeltaY * bDeltaY;\n\t\t\t// Zero when the segments are parallel; pinning aParam at 0 then picks any point, all equally close.\n\t\t\tconst denominator = aLength * bLength - along * along;\n\t\t\tif(denominator !== 0) {\n\t\t\t\taParam = clamp((along * bOffset - aOffset * bLength) / denominator);\n\t\t\t}\n\n\t\t\tbParam = (along * aParam + bOffset) / bLength;\n\t\t\t// Clamping bParam moves b's closest point, so aParam has to be solved again.\n\t\t\tif(bParam < 0) {\n\t\t\t\tbParam = 0;\n\t\t\t\taParam = clamp(-aOffset / aLength);\n\t\t\t} else if(bParam > 1) {\n\t\t\t\tbParam = 1;\n\t\t\t\taParam = clamp((along - aOffset) / aLength);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst gapX = (aX1 + aDeltaX * aParam) - (bX1 + bDeltaX * bParam);\n\tconst gapY = (aY1 + aDeltaY * aParam) - (bY1 + bDeltaY * bParam);\n\tif(out) {\n\t\tout.x = gapX;\n\t\tout.y = gapY;\n\t}\n\n\treturn gapX * gapX + gapY * gapY;\n}\n\n// Squared distance between a segment and a rotated box, measured in the box's frame where it is axis aligned.\n// Two disjoint convex shapes have their closest pair at a corner, so once the crossing case is ruled out the\n// answer is the nearest of each segment end to the box and each box corner to the segment.\n// `out`, if given, is filled with the world vector from the box's closest point to the segment's - the direction\n// the segment is pushed off the box. It is (0, 0) when the segment runs through the box, which has no such\n// direction; contactNormal falls back to a penetration axis there.\nexport function segmentBoxDistanceSquared(\n\tx1: number, y1: number, x2: number, y2: number,\n\tboxX: number, boxY: number, boxWidth: number, boxHeight: number, boxAngle: number,\n\tout?: Vector,\n): number {\n\tconst cos = Math.cos(boxAngle);\n\tconst sin = Math.sin(boxAngle);\n\tconst halfWidth = boxWidth / 2;\n\tconst halfHeight = boxHeight / 2;\n\n\tconst firstOffsetX = x1 - boxX;\n\tconst firstOffsetY = y1 - boxY;\n\tconst secondOffsetX = x2 - boxX;\n\tconst secondOffsetY = y2 - boxY;\n\tconst localX1 = firstOffsetX * cos + firstOffsetY * sin;\n\tconst localY1 = -firstOffsetX * sin + firstOffsetY * cos;\n\tconst localX2 = secondOffsetX * cos + secondOffsetY * sin;\n\tconst localY2 = -secondOffsetX * sin + secondOffsetY * cos;\n\n\t// A segment through the box has both ends outside and every corner off, so the nearest-pair search below\n\t// would report a real distance for two shapes already through each other; catch it first.\n\tif(!segmentMissesBox(localX1, localY1, localX2, localY2, halfWidth, halfHeight)) {\n\t\tif(out) {\n\t\t\tout.x = 0;\n\t\t\tout.y = 0;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\tif(!out) {\n\t\treturn Math.min(\n\t\t\tpointBoxDistanceSquared(localX1, localY1, halfWidth, halfHeight),\n\t\t\tpointBoxDistanceSquared(localX2, localY2, halfWidth, halfHeight),\n\t\t\tpointSegmentDistanceSquared(-halfWidth, -halfHeight, localX1, localY1, localX2, localY2),\n\t\t\tpointSegmentDistanceSquared(halfWidth, -halfHeight, localX1, localY1, localX2, localY2),\n\t\t\tpointSegmentDistanceSquared(halfWidth, halfHeight, localX1, localY1, localX2, localY2),\n\t\t\tpointSegmentDistanceSquared(-halfWidth, halfHeight, localX1, localY1, localX2, localY2),\n\t\t);\n\t}\n\n\t// The same six candidates, keeping the gap of the winner. The corner ones measure box-to-segment, the other\n\t// way round from the endpoint ones, so they are negated to leave every gap pointing off the box.\n\tlet best = pointBoxDistanceSquared(localX1, localY1, halfWidth, halfHeight, out);\n\tbest = keepCloser(best, pointBoxDistanceSquared(localX2, localY2, halfWidth, halfHeight, CANDIDATE), CANDIDATE, out, false);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(-halfWidth, -halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(halfWidth, -halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(halfWidth, halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\tbest = keepCloser(best, pointSegmentDistanceSquared(-halfWidth, halfHeight, localX1, localY1, localX2, localY2, CANDIDATE), CANDIDATE, out, true);\n\n\t// Back out of the box's frame, the gap having been measured inside it.\n\tconst localGapX = out.x;\n\tconst localGapY = out.y;\n\tout.x = localGapX * cos - localGapY * sin;\n\tout.y = localGapX * sin + localGapY * cos;\n\n\treturn best;\n}\n\n// Takes the candidate over what `out` holds when it is closer, flipping it when it was measured box-to-segment.\nfunction keepCloser(best: number, candidate: number, gap: Vector, out: Vector, flip: boolean): number {\n\tif(candidate >= best) {\n\t\treturn best;\n\t}\n\n\tout.x = flip ? -gap.x : gap.x;\n\tout.y = flip ? -gap.y : gap.y;\n\n\treturn candidate;\n}\n\n// `out`, if given, is filled with the vector from the segment's closest point to (x, y).\nexport function pointSegmentDistanceSquared(x: number, y: number, x1: number, y1: number, x2: number, y2: number, out?: Vector): number {\n\tconst deltaX = x2 - x1;\n\tconst deltaY = y2 - y1;\n\tconst length = deltaX * deltaX + deltaY * deltaY;\n\n\tconst along = length > 0 ? clamp(((x - x1) * deltaX + (y - y1) * deltaY) / length) : 0;\n\tconst gapX = x - (x1 + deltaX * along);\n\tconst gapY = y - (y1 + deltaY * along);\n\tif(out) {\n\t\tout.x = gapX;\n\t\tout.y = gapY;\n\t}\n\n\treturn gapX * gapX + gapY * gapY;\n}\n\n// Whether an origin-centred axis-aligned box and a segment fail to reach, by separating axis. Touching counts\n// as reaching. For a zero-length segment the normal is (0, 0), which never separates and leaves the two box\n// axes to decide whether the point is inside.\nfunction segmentMissesBox(x1: number, y1: number, x2: number, y2: number, halfWidth: number, halfHeight: number): boolean {\n\tif(Math.max(x1, x2) < -halfWidth || Math.min(x1, x2) > halfWidth) {\n\t\treturn true;\n\t} else if(Math.max(y1, y2) < -halfHeight || Math.min(y1, y2) > halfHeight) {\n\t\treturn true;\n\t}\n\n\tconst normalX = -(y2 - y1);\n\tconst normalY = x2 - x1;\n\n\treturn Math.abs(normalX * x1 + normalY * y1) > halfWidth * Math.abs(normalX) + halfHeight * Math.abs(normalY);\n}\n\n// Squared distance from a point to an origin-centred axis-aligned box; 0 inside it. `out`, if given, is filled\n// with the vector from the box's closest point to (x, y).\nfunction pointBoxDistanceSquared(x: number, y: number, halfWidth: number, halfHeight: number, out?: Vector): number {\n\tconst gapX = Math.abs(x) - halfWidth;\n\tconst gapY = Math.abs(y) - halfHeight;\n\tconst outsideX = gapX > 0 ? gapX : 0;\n\tconst outsideY = gapY > 0 ? gapY : 0;\n\tif(out) {\n\t\tout.x = x < 0 ? -outsideX : outsideX;\n\t\tout.y = y < 0 ? -outsideY : outsideY;\n\t}\n\n\treturn outsideX * outsideX + outsideY * outsideY;\n}\n\nfunction clamp(value: number): number {\n\tif(value < 0) {\n\t\treturn 0;\n\t} else if(value > 1) {\n\t\treturn 1;\n\t}\n\n\treturn value;\n}\n","import { bodyShape } from '../components/body-component';\nimport { SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport {\n\tTRANSFORM_ANGLE_INDEX,\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n} from '../components/transform-component-constants';\nimport { shapeHalfHeight, shapeHalfWidth } from '../math/shapes';\n\nexport interface SpatialBounds {\n\tminX: number\n\tminY: number\n\tmaxX: number\n\tmaxY: number\n}\n\nexport interface SpatialBlockComponents {\n\ttransform: Float32Array\n\tbody?: Uint32Array\n}\n\nexport function spatialBounds(components: SpatialBlockComponents): SpatialBounds {\n\tconst transform = components.transform;\n\tconst shape = components.body ? bodyShape(components.body) : SHAPE_RECTANGLE;\n\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\tconst x = transform[TRANSFORM_X_INDEX];\n\tconst y = transform[TRANSFORM_Y_INDEX];\n\n\treturn {\n\t\tminX: x - halfWidth,\n\t\tminY: y - halfHeight,\n\t\tmaxX: x + halfWidth,\n\t\tmaxY: y + halfHeight,\n\t};\n}\n\nexport function boundsOverlap(a: SpatialBounds, minX: number, minY: number, maxX: number, maxY: number): boolean {\n\treturn a.minX <= maxX && a.maxX >= minX && a.minY <= maxY && a.maxY >= minY;\n}\n\nexport function querySize(min: number, max: number): number {\n\treturn max > min ? max - min : Math.max(1, Math.abs(min)) * Number.EPSILON;\n}\n","import SharedSpatialMap from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport type { SharedSpatialMapConfig, SharedSpatialMapMemory } from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport type { SpatialEntity } from '@daneren2005/shared-memory-objects/spatial/spatial-entity';\nimport { BaseWorld } from '@daneren2005/shared-memory-ecs';\nimport type {\n\tBaseEntity,\n\tComponentDefinitionMap,\n\tComponentsOf,\n\tCompressedWorldConfig,\n\tEntityWorkerSystemWorld,\n\tEntityConfigOf,\n\tWorldConfig,\n\tWorldLoadOptions,\n\tWorldOptions,\n} from '@daneren2005/shared-memory-ecs';\nimport { boundsOverlap, querySize, spatialBounds } from './systems/spatial-bounds';\n\nexport interface PhysicalWorldData {\n\tspatialMapMemory?: SharedSpatialMapMemory\n}\n\nexport interface PhysicalSystemWorld extends EntityWorkerSystemWorld, PhysicalWorldData {\n\tspatialMap?: SharedSpatialMap\n}\n\nexport interface PhysicalWorldSource {\n\tspatialMap: SharedSpatialMap\n}\n\nexport type PhysicalWorldOptions<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> = WorldOptions<ComponentsOf<R>, EntityConfigOf<R>, E> & {\n\tspatial?: SharedSpatialMapConfig\n};\n\nexport type PhysicalWorldEntity<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> = E;\nexport type PhysicalWorldFilter<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> = (entity: PhysicalWorldEntity<R, E>) => boolean;\n\ninterface SpatialAccessorComponents {\n\ttransform?: { block?: Float32Array }\n\tbody?: { block?: Uint32Array }\n}\n\nexport default class PhysicalWorld<\n\tR extends ComponentDefinitionMap,\n\tE extends BaseEntity<ComponentsOf<R>, EntityConfigOf<R>> = BaseEntity<ComponentsOf<R>, EntityConfigOf<R>>,\n> extends BaseWorld<R, ComponentsOf<R>, EntityConfigOf<R>, E> implements PhysicalWorldSource {\n\treadonly spatialMap: SharedSpatialMap;\n\tprivate spatialIds = new Set<number>();\n\t// Bulk-loaded entities whose spatial entry is still to be written, by eid.\n\tprivate pendingSpatial = new Map<number, PhysicalWorldEntity<R, E>>();\n\n\tconstructor(registry: R, options: PhysicalWorldOptions<R, E> = {}) {\n\t\tconst { spatial, ...worldOptions } = options;\n\t\tsuper(registry, worldOptions);\n\n\t\t// Float64 because eids only increase: Float32 caps lifetime-created entities at 2^24, Float64 covers the uint32 eid range\n\t\tthis.spatialMap = new SharedSpatialMap(this.heap, { type: Float64Array, ...spatial });\n\t\tthis.on('entity-added', (entity: PhysicalWorldEntity<R, E>) => {\n\t\t\tthis.updateSpatialEntity(entity);\n\t\t\tentity.on('component-added', (name: string) => {\n\t\t\t\tif(name === 'transform' || name === 'body') {\n\t\t\t\t\tthis.updateSpatialEntity(entity);\n\t\t\t\t}\n\t\t\t});\n\t\t\tentity.on('component-removed', (name: string) => {\n\t\t\t\tif(name === 'transform' || name === 'body') {\n\t\t\t\t\tthis.updateSpatialEntity(entity);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\tthis.on('entity-removed', (entity: PhysicalWorldEntity<R, E>) => {\n\t\t\tthis.pendingSpatial.delete(entity.eid);\n\t\t\tthis.spatialIds.delete(entity.eid);\n\t\t\tthis.spatialMap.remove(entity.eid);\n\t\t});\n\t}\n\n\t// Inserts the entities a bulk load queued before anything can read the map: worker systems only run after this.\n\tload(config: WorldConfig<EntityConfigOf<R>> | CompressedWorldConfig<EntityConfigOf<R>>, options: WorldLoadOptions = {}): Promise<void> {\n\t\tthis.pendingSpatial.clear();\n\t\treturn super.load(config, {\n\t\t\t...options,\n\t\t\tonEntitiesReady: () => {\n\t\t\t\tthis.flushPendingSpatial();\n\t\t\t\toptions.onEntitiesReady?.();\n\t\t\t},\n\t\t});\n\t}\n\n\tupdateSpatialEntity(entityOrId: PhysicalWorldEntity<R, E> | number): void {\n\t\tconst entity = typeof entityOrId === 'number' ? this.getEntityByEid(entityOrId) : entityOrId;\n\t\tif(!entity) {\n\t\t\treturn;\n\t\t}\n\t\tif(this.isBulkLoading) {\n\t\t\tthis.pendingSpatial.set(entity.eid, entity);\n\t\t\treturn;\n\t\t}\n\t\tthis.flushPendingSpatial();\n\n\t\tconst components = entity.components as typeof entity.components & SpatialAccessorComponents;\n\t\tconst transform = components.transform?.block;\n\t\tif(!transform) {\n\t\t\tthis.spatialIds.delete(entity.eid);\n\t\t\tthis.spatialMap.remove(entity.eid);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bounds = spatialBounds({ transform, body: components.body?.block });\n\t\tthis.spatialIds.add(entity.eid);\n\t\tthis.spatialMap.update(entity.eid, bounds.minX, bounds.minY, bounds.maxX - bounds.minX, bounds.maxY - bounds.minY);\n\t}\n\n\t// Inserting one by one during a load regrows and rehashes the buckets as the map fills; bulkInsert sizes them once.\n\tprivate flushPendingSpatial() {\n\t\tif(this.pendingSpatial.size === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst inserts: Array<SpatialEntity> = [];\n\t\tfor(const entity of this.pendingSpatial.values()) {\n\t\t\tconst components = entity.components as typeof entity.components & SpatialAccessorComponents;\n\t\t\tconst transform = components.transform?.block;\n\t\t\tif(!transform) {\n\t\t\t\tthis.spatialIds.delete(entity.eid);\n\t\t\t\tthis.spatialMap.remove(entity.eid);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst bounds = spatialBounds({ transform, body: components.body?.block });\n\t\t\tthis.spatialIds.add(entity.eid);\n\t\t\tinserts.push({ id: entity.eid, x: bounds.minX, y: bounds.minY, width: bounds.maxX - bounds.minX, height: bounds.maxY - bounds.minY });\n\t\t}\n\t\tthis.pendingSpatial.clear();\n\t\tthis.spatialMap.bulkInsert(inserts);\n\t}\n\n\tsearchSpatial(minX: number, minY: number, maxX: number, maxY: number, filter?: PhysicalWorldFilter<R, E>): Array<PhysicalWorldEntity<R, E>> {\n\t\tif(maxX < minX || maxY < minY) {\n\t\t\treturn [];\n\t\t}\n\t\tthis.flushPendingSpatial();\n\n\t\tconst found: Array<PhysicalWorldEntity<R, E>> = [];\n\t\tconst ids = Number.isFinite(minX) && Number.isFinite(minY) && Number.isFinite(maxX) && Number.isFinite(maxY)\n\t\t\t? this.spatialMap.search(minX, minY, querySize(minX, maxX), querySize(minY, maxY))\n\t\t\t: this.spatialIds;\n\t\tfor(const id of ids) {\n\t\t\tconst entity = this.getEntityByEid(id);\n\t\t\tif(!entity || filter && !filter(entity)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst components = entity.components as typeof entity.components & SpatialAccessorComponents;\n\t\t\tconst transform = components.transform?.block;\n\t\t\tif(transform && boundsOverlap(spatialBounds({ transform, body: components.body?.block }), minX, minY, maxX, maxY)) {\n\t\t\t\tfound.push(entity);\n\t\t\t}\n\t\t}\n\n\t\treturn found;\n\t}\n\n\tsearchSpatialAround(x: number, y: number, reachX: number, reachY: number, filter?: PhysicalWorldFilter<R, E>): Array<PhysicalWorldEntity<R, E>> {\n\t\treturn this.searchSpatial(x - reachX, y - reachY, x + reachX, y + reachY, filter);\n\t}\n\n\tfindNearestSpatial(x: number, y: number, maxDistance = Infinity, filter?: PhysicalWorldFilter<R, E>): PhysicalWorldEntity<R, E> | undefined {\n\t\treturn this.findNearbySpatial(x, y, 1, maxDistance, filter)[0];\n\t}\n\n\tfindNearbySpatial(x: number, y: number, maxResults: number, maxDistance = Infinity, filter?: PhysicalWorldFilter<R, E>): Array<PhysicalWorldEntity<R, E>> {\n\t\tthis.flushPendingSpatial();\n\t\tconst ids = this.spatialMap.neighbors(x, y, maxResults, maxDistance, id => {\n\t\t\tconst entity = this.getEntityByEid(id);\n\t\t\treturn !!entity && (!filter || filter(entity));\n\t\t});\n\n\t\treturn ids.flatMap(id => {\n\t\t\tconst entity = this.getEntityByEid(id);\n\t\t\treturn entity ? [entity] : [];\n\t\t});\n\t}\n}\n","import { SHAPE_CAPSULE, SHAPE_CIRCLE, SHAPE_POLYGON, SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { POLYGON_VERTEX_COUNT_INDEX, POLYGON_VERTICES_INDEX } from '../components/polygon-component-constants';\nimport { capsuleCoreAngle, capsuleHalfLength } from './shapes';\nimport type { Vector } from './shapes';\n\ninterface Projection {\n\tmin: number\n\tmax: number\n}\n\ninterface AxisResult {\n\toverlap: boolean\n\tdepth: number\n\tx: number\n\ty: number\n}\n\nconst PROJECTION_A: Projection = { min: 0, max: 0 };\nconst PROJECTION_B: Projection = { min: 0, max: 0 };\nconst AXIS: AxisResult = { overlap: true, depth: Infinity, x: 0, y: 0 };\n\nexport function polygonShapesOverlap(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\treturn testPolygonAxes(\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tfalse,\n\t);\n}\n\nexport function polygonContactNormal(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tout: Vector,\n): boolean {\n\tAXIS.depth = Infinity;\n\tif(!testPolygonAxes(\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\ttrue,\n\t) || AXIS.depth === Infinity) {\n\t\treturn false;\n\t}\n\n\tout.x = AXIS.x;\n\tout.y = AXIS.y;\n\n\treturn true;\n}\n\nfunction testPolygonAxes(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tif(aShape === SHAPE_POLYGON) {\n\t\tif(!aPolygon || !testEdges(\n\t\t\taPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif(bShape === SHAPE_POLYGON) {\n\t\tif(!bPolygon || !testEdges(\n\t\t\tbPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif(aShape === SHAPE_RECTANGLE && !testRectangleAxes(\n\t\taAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\tif(bShape === SHAPE_RECTANGLE && !testRectangleAxes(\n\t\tbAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\n\tif(isRound(aShape) && bPolygon && !testRoundVertexAxes(\n\t\taShape, aX, aY, aWidth, aHeight, aAngle,\n\t\tbPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\tif(isRound(bShape) && aPolygon && !testRoundVertexAxes(\n\t\tbShape, bX, bY, bWidth, bHeight, bAngle,\n\t\taPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t)) {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nfunction testEdges(\n\tpolygon: Float32Array, x: number, y: number, width: number, height: number, angle: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst count = polygon[POLYGON_VERTEX_COUNT_INDEX];\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\tfor(let i = 0; i < count; i++) {\n\t\tconst next = (i + 1) % count;\n\t\tconst currentX = worldVertexX(polygon, i, x, width, height, cos, sin);\n\t\tconst currentY = worldVertexY(polygon, i, y, width, height, cos, sin);\n\t\tconst nextX = worldVertexX(polygon, next, x, width, height, cos, sin);\n\t\tconst nextY = worldVertexY(polygon, next, y, width, height, cos, sin);\n\t\tif(!testAxis(\n\t\t\tcurrentY - nextY, nextX - currentX,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nfunction testRectangleAxes(\n\tangle: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\n\treturn testAxis(\n\t\tcos, sin,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t) && testAxis(\n\t\t-sin, cos,\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\tkeepBest,\n\t);\n}\n\nfunction testRoundVertexAxes(\n\troundShape: number, roundX: number, roundY: number, roundWidth: number, roundHeight: number, roundAngle: number,\n\tpolygon: Float32Array, polygonX: number, polygonY: number, polygonWidth: number, polygonHeight: number, polygonAngle: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst half = roundShape === SHAPE_CAPSULE ? capsuleHalfLength(roundWidth, roundHeight) : 0;\n\tconst coreAngle = capsuleCoreAngle(roundWidth, roundHeight, roundAngle);\n\tconst alongX = Math.cos(coreAngle) * half;\n\tconst alongY = Math.sin(coreAngle) * half;\n\tconst startX = roundX - alongX;\n\tconst startY = roundY - alongY;\n\tconst endX = roundX + alongX;\n\tconst endY = roundY + alongY;\n\tconst count = polygon[POLYGON_VERTEX_COUNT_INDEX];\n\tconst cos = Math.cos(polygonAngle);\n\tconst sin = Math.sin(polygonAngle);\n\tfor(let i = 0; i < count; i++) {\n\t\tconst vertexX = worldVertexX(polygon, i, polygonX, polygonWidth, polygonHeight, cos, sin);\n\t\tconst vertexY = worldVertexY(polygon, i, polygonY, polygonWidth, polygonHeight, cos, sin);\n\t\tconst fraction = closestFraction(startX, startY, endX, endY, vertexX, vertexY);\n\t\tconst coreX = startX + (endX - startX) * fraction;\n\t\tconst coreY = startY + (endY - startY) * fraction;\n\t\tif(!testAxis(\n\t\t\tvertexX - coreX, vertexY - coreY,\n\t\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t\t\tkeepBest,\n\t\t)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nfunction testAxis(\n\taxisX: number, axisY: number,\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n\tkeepBest: boolean,\n): boolean {\n\tconst length = Math.hypot(axisX, axisY);\n\tif(length === 0) {\n\t\treturn true;\n\t}\n\taxisX /= length;\n\taxisY /= length;\n\n\tprojectShape(aShape, aPolygon, aX, aY, aWidth, aHeight, aAngle, axisX, axisY, PROJECTION_A);\n\tprojectShape(bShape, bPolygon, bX, bY, bWidth, bHeight, bAngle, axisX, axisY, PROJECTION_B);\n\tconst negativeDepth = PROJECTION_A.max - PROJECTION_B.min;\n\tconst positiveDepth = PROJECTION_B.max - PROJECTION_A.min;\n\tif(!keepBest && (negativeDepth <= 0 || positiveDepth <= 0)) {\n\t\treturn false;\n\t}\n\n\tif(keepBest) {\n\t\tlet depth: number;\n\t\tlet direction: number;\n\t\tif(negativeDepth <= 0) {\n\t\t\tdepth = -negativeDepth;\n\t\t\tdirection = -1;\n\t\t} else if(positiveDepth <= 0) {\n\t\t\tdepth = -positiveDepth;\n\t\t\tdirection = 1;\n\t\t} else if(negativeDepth < positiveDepth) {\n\t\t\tdepth = negativeDepth;\n\t\t\tdirection = -1;\n\t\t} else {\n\t\t\tdepth = positiveDepth;\n\t\t\tdirection = 1;\n\t\t}\n\t\tif(depth < AXIS.depth) {\n\t\t\tAXIS.depth = depth;\n\t\t\tAXIS.x = axisX * direction;\n\t\t\tAXIS.y = axisY * direction;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nfunction projectShape(\n\tshape: number, polygon: Float32Array | undefined,\n\tx: number, y: number, width: number, height: number, angle: number,\n\taxisX: number, axisY: number, out: Projection,\n): void {\n\tconst centre = x * axisX + y * axisY;\n\tif(shape === SHAPE_CIRCLE) {\n\t\tconst radius = width / 2;\n\t\tout.min = centre - radius;\n\t\tout.max = centre + radius;\n\t\treturn;\n\t}\n\n\tif(shape === SHAPE_CAPSULE) {\n\t\tconst half = capsuleHalfLength(width, height);\n\t\tconst coreAngle = capsuleCoreAngle(width, height, angle);\n\t\tconst reach = Math.abs(axisX * Math.cos(coreAngle) + axisY * Math.sin(coreAngle)) * half + Math.min(width, height) / 2;\n\t\tout.min = centre - reach;\n\t\tout.max = centre + reach;\n\t\treturn;\n\t}\n\n\tif(shape === SHAPE_RECTANGLE) {\n\t\tconst cos = Math.cos(angle);\n\t\tconst sin = Math.sin(angle);\n\t\tconst reach = Math.abs(axisX * cos + axisY * sin) * width / 2\n\t\t\t+ Math.abs(axisX * -sin + axisY * cos) * height / 2;\n\t\tout.min = centre - reach;\n\t\tout.max = centre + reach;\n\t\treturn;\n\t}\n\n\tif(!polygon) {\n\t\tout.min = Infinity;\n\t\tout.max = -Infinity;\n\t\treturn;\n\t}\n\n\tconst count = polygon[POLYGON_VERTEX_COUNT_INDEX];\n\tconst cos = Math.cos(angle);\n\tconst sin = Math.sin(angle);\n\tout.min = Infinity;\n\tout.max = -Infinity;\n\tfor(let i = 0; i < count; i++) {\n\t\tconst vertexX = worldVertexX(polygon, i, x, width, height, cos, sin);\n\t\tconst vertexY = worldVertexY(polygon, i, y, width, height, cos, sin);\n\t\tconst projection = vertexX * axisX + vertexY * axisY;\n\t\tout.min = Math.min(out.min, projection);\n\t\tout.max = Math.max(out.max, projection);\n\t}\n}\n\nfunction worldVertexX(polygon: Float32Array, index: number, x: number, width: number, height: number, cos: number, sin: number): number {\n\tconst offset = POLYGON_VERTICES_INDEX + index * 2;\n\treturn x + polygon[offset] * width * cos - polygon[offset + 1] * height * sin;\n}\n\nfunction worldVertexY(polygon: Float32Array, index: number, y: number, width: number, height: number, cos: number, sin: number): number {\n\tconst offset = POLYGON_VERTICES_INDEX + index * 2;\n\treturn y + polygon[offset] * width * sin + polygon[offset + 1] * height * cos;\n}\n\nfunction closestFraction(startX: number, startY: number, endX: number, endY: number, x: number, y: number): number {\n\tconst segmentX = endX - startX;\n\tconst segmentY = endY - startY;\n\tconst lengthSquared = segmentX * segmentX + segmentY * segmentY;\n\tif(lengthSquared === 0) {\n\t\treturn 0;\n\t}\n\n\treturn Math.min(1, Math.max(0, ((x - startX) * segmentX + (y - startY) * segmentY) / lengthSquared));\n}\n\nfunction isRound(shape: number): boolean {\n\treturn shape === SHAPE_CIRCLE || shape === SHAPE_CAPSULE;\n}\n","export interface DynamicsCommand {\n\tentityId: number\n\tforceX: number\n\tforceY: number\n\timpulseX: number\n\timpulseY: number\n\tvelocityX?: number\n\tvelocityY?: number\n}\n\nexport interface VelocityAssignment {\n\tvelocityX?: number\n\tvelocityY?: number\n}\n\nexport interface DynamicsCommandQueue {\n\tqueueForce(entityId: number, forceX: number, forceY: number): void\n\tqueueImpulse(entityId: number, impulseX: number, impulseY: number): void\n\tqueueVelocity(entityId: number, velocity: VelocityAssignment): void\n}\n\nexport type PendingDynamicsCommands = Map<number, Omit<DynamicsCommand, 'entityId'>>;\n\nexport type DynamicsCommandBuffer = Float64Array;\nexport type DynamicsCommands = DynamicsCommandBuffer | ReadonlyArray<DynamicsCommand>;\n\nexport const DYNAMICS_COMMAND_STRIDE = 8;\nexport const DYNAMICS_COMMAND_ENTITY_ID_OFFSET = 0;\nexport const DYNAMICS_COMMAND_FORCE_X_OFFSET = 1;\nexport const DYNAMICS_COMMAND_FORCE_Y_OFFSET = 2;\nexport const DYNAMICS_COMMAND_IMPULSE_X_OFFSET = 3;\nexport const DYNAMICS_COMMAND_IMPULSE_Y_OFFSET = 4;\nexport const DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET = 5;\nexport const DYNAMICS_COMMAND_VELOCITY_X_OFFSET = 6;\nexport const DYNAMICS_COMMAND_VELOCITY_Y_OFFSET = 7;\nexport const DYNAMICS_COMMAND_VELOCITY_X_FLAG = 1 << 0;\nexport const DYNAMICS_COMMAND_VELOCITY_Y_FLAG = 1 << 1;\n\nexport interface DynamicsWorld {\n\telapsedTime: number\n\tdynamicsCommands?: DynamicsCommands\n}\n\nexport function emptyCommand(): Omit<DynamicsCommand, 'entityId'> {\n\treturn { forceX: 0, forceY: 0, impulseX: 0, impulseY: 0 };\n}\n\n","import type { DynamicsCommand } from './dynamics-commands';\n\nexport function findDynamicsCommand(commands: ReadonlyArray<DynamicsCommand> | undefined, entityId: number): DynamicsCommand | undefined {\n\tif(!commands) {\n\t\treturn undefined;\n\t}\n\n\tlet low = 0;\n\tlet high = commands.length - 1;\n\twhile(low <= high) {\n\t\tconst middle = (low + high) >>> 1;\n\t\tconst command = commands[middle];\n\t\tif(command.entityId === entityId) {\n\t\t\treturn command;\n\t\t} else if(command.entityId < entityId) {\n\t\t\tlow = middle + 1;\n\t\t} else {\n\t\t\thigh = middle - 1;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n","import {\n\tDYNAMICS_COMMAND_STRIDE,\n\tDYNAMICS_COMMAND_ENTITY_ID_OFFSET,\n} from './dynamics-commands';\nimport type { DynamicsCommandBuffer } from './dynamics-commands';\n\nexport function findDynamicsCommandOffset(commands: DynamicsCommandBuffer, entityId: number): number {\n\tlet low = 0;\n\tlet high = commands.length / DYNAMICS_COMMAND_STRIDE - 1;\n\twhile(low <= high) {\n\t\tconst middle = (low + high) >>> 1;\n\t\tconst offset = middle * DYNAMICS_COMMAND_STRIDE;\n\t\tconst commandEntityId = commands[offset + DYNAMICS_COMMAND_ENTITY_ID_OFFSET];\n\t\tif(commandEntityId === entityId) {\n\t\t\treturn offset;\n\t\t} else if(commandEntityId < entityId) {\n\t\t\tlow = middle + 1;\n\t\t} else {\n\t\t\thigh = middle - 1;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n","import Flatbush from 'flatbush';\nimport { DEAD_INDEX } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentMap, EntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents } from '@daneren2005/shared-memory-ecs';\nimport type { PhysicsUpdateComponents } from '../components/registry';\nimport { bodyShape, isContinuous, isDying, isSensor } from '../components/body-component';\nimport { BODY_CATEGORY_INDEX, BODY_MASK_INDEX, SHAPE_CAPSULE, SHAPE_POLYGON, SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { TRANSFORM_ANGLE_INDEX, TRANSFORM_HEIGHT_INDEX, TRANSFORM_WIDTH_INDEX, TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\nimport { DYNAMICS_ACCELERATION_X_INDEX, DYNAMICS_ACCELERATION_Y_INDEX, DYNAMICS_INVERSE_MASS_INDEX } from '../components/dynamics-component-constants';\nimport { contactNormal as shapeContactNormal, shapeHalfHeight, shapeHalfWidth, shapeIsEmpty, shapeRadius, shapesOverlap } from '../math/shapes';\nimport type { Vector } from '../math/shapes';\nimport { polygonContactNormal, polygonShapesOverlap } from '../math/polygons';\nimport {\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n} from '../actions/dynamics-commands';\nimport type { DynamicsCommands } from '../actions/dynamics-commands';\nimport { findDynamicsCommand } from '../actions/find-dynamics-command';\nimport { findDynamicsCommandOffset } from '../actions/find-dynamics-command-offset';\n\n// How close to contact a sweep settles for, in world units: below anything a game would draw, yet reached by\n// the halving below in a handful of steps.\nconst CONTACT_TOLERANCE = 1e-4;\n// Cap on the halving so a huge move cannot spawn unbounded overlap tests; reaches the tolerance for any move\n// up to ~1600 units.\nconst MAX_REFINEMENTS = 24;\n\n// Blocks handed to a collision callback for the entity run into. `transform` and `body` are guaranteed by the\n// collidable query; `velocity` and anything in `optional` are present only if that entity has them.\nexport type CollisionComponents<T extends PhysicsUpdateComponents> = Partial<T> & {\n\ttransform: Float32Array\n\tbody: Uint32Array\n\tpolygon?: Float32Array\n\tentity?: Uint32Array\n};\n\n// The entity doing the running into: the one being updated, so everything the system asked for is present.\nexport interface MovingEntity<T extends PhysicsUpdateComponents> {\n\tentityId: number\n\tcomponents: T\n}\n\n// The entity on the other side of a collision, which may be something the system never moves.\nexport interface CollisionEntity<T extends PhysicsUpdateComponents> {\n\tentityId: number\n\tcomponents: CollisionComponents<T>\n}\n\n// Geometry at the first touch. The normal always points from `other` towards `self`, so negating it gives the\n// direction for the other side. Exact coincident centres can have no unique direction and report (0, 0).\nexport interface CollisionContact {\n\tnormalX: number\n\tnormalY: number\n}\n\n// What a game runs when one entity moves into another. Runs on the physics thread, so it is a plain function\n// over the raw blocks and reaches the main thread only through `callbacks`. Called once per pair per run, right\n// after the mover is put down: `self` is whichever side reached the contact first, `other` is the other. Because\n// the other side gets no call of its own, a callback has to decide for both. A velocity-less entity is never\n// `self` but is found as `other`. `contact` is oriented from `other` towards `self` at first touch.\nexport type CollisionFunction<C extends ComponentMap, T extends PhysicsUpdateComponents & EntityUpdateComponents<C>, W extends EntityWorkerSystemWorld = EntityWorkerSystemWorld> = (\n\tworld: W,\n\tself: MovingEntity<T>,\n\tother: CollisionEntity<T>,\n\tqueries: EntityQueryComponents<C>,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n\tcontact: CollisionContact,\n) => void;\n\n// How much of a move an entity may make and what stopped it. `fraction` is how far along it got (1 clear, 0\n// already pressed against something). `blocking` is what it came to rest against - the overlap test there will\n// not report it, since the two are touching; more than one entry means it wedged between them at once.\nexport interface SweepResult<T extends PhysicsUpdateComponents> {\n\tfraction: number\n\tblocking: Array<CollisionEntity<T>>\n}\n\n// How a move came out, as the displacement to apply rather than a fraction - a move that dropped an axis is no\n// longer a scaling of the original. `blocking` is empty for a move that got where it was going.\nexport interface MoveResult<T extends PhysicsUpdateComponents> {\n\tmoveX: number\n\tmoveY: number\n\tblocking: Array<CollisionEntity<T>>\n}\n\n// One shared empty array for the sweeps that find nothing, rather than one per entity per run.\nconst NOTHING_BLOCKING: Array<never> = [];\n\n// The minimal read of the searching side a shape test needs: a body to be measured as, at a transform. Looser\n// than MovingEntity - no component map - so the geometry helpers below serve both the sweep and `contactPoint`,\n// which death interpolation calls with whichever side of a collision was fatal.\ninterface ContactSource {\n\tentityId: number\n\tcomponents: { transform: Float32Array, body?: Uint32Array, polygon?: Float32Array }\n}\n// The other side of a shape test: only ever a body at a transform is looked at, so this is all that is required.\n// CollisionEntity satisfies it, as does anything else carrying the two blocks.\ninterface ContactTarget {\n\tcomponents: { transform: Float32Array, body: Uint32Array, polygon?: Float32Array }\n}\n\n// Packs a pair of entity ids into one key. Ids must stay under this for the key to be unique, which a pool an\n// ECS can address leaves untouchable, and it keeps the product inside the safe integer range.\nconst MAX_PAIR_ID = 2 ** 26;\n\n// A per-axis sweep that hit nothing: the whole axis, clear. Held once rather than rebuilt per clear axis.\nconst CLEAR = { fraction: 1, blocking: NOTHING_BLOCKING };\n\n// The searching entity's blocks read out once, so a sweep testing the same pair repeatedly does not re-read\n// them. x/y are where it started the move.\ninterface Searcher {\n\tentityId: number\n\tshape: number\n\tpolygon?: Float32Array\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n\tangle: number\n\thalfWidth: number\n\thalfHeight: number\n\tcategory: number\n\tmask: number\n\t// A sensor's move is never swept short: it passes through rather than resting against. Read once here.\n\tsensor: boolean\n\tccd: boolean\n}\n\n// One category's broadphase: every entity that collides as that category, plus an R-tree over them. A searcher\n// only visits buckets its mask accepts. Bucketed on the whole category value, so an entity that collides as two\n// things at once still lives in exactly one bucket and is never found twice by one search.\ninterface CategoryBucket<T extends PhysicsUpdateComponents> {\n\tcategory: number\n\tentries: Array<CollisionEntity<T>>\n\tindex: Flatbush\n}\n\n// An R-tree over every collidable entity, built once at the top of a run and then asked, per entity as it\n// moves, what it might have hit.\n//\n// Everything is indexed where it stood at the start of the run - the only moment all entities agree on. To stay\n// a superset of what can collide, each box is grown by how far that entity could travel this run, both ways: a\n// callback may turn an entity around before its own move, so a box grown only forwards would point wrong.\n//\n// Split by collide category so most of what an entity cannot hit is ruled out a subtree at a time. An empty\n// category has no bucket, so Flatbush is never asked to index zero items.\nexport default class CollisionBroadphase<T extends PhysicsUpdateComponents> {\n\tprivate buckets: Array<CategoryBucket<T>> = [];\n\t// Whether anything in this run can bounce. Bounces are resolved for both sides of a contact, so an entity with\n\t// no bounciness of its own still has to look at what it ran into - but only in a world where that can matter.\n\treadonly hasBounciness: boolean = false;\n\t// The pairs already resolved this run. Lives here rather than in the update because the tree is what a run is\n\t// scoped to: preRun builds a new one, so the set is empty again for the next run with nothing to clear.\n\tprivate resolved = new Set<number>();\n\n\tconstructor(\n\t\tentities: Array<{ entityId: number, components: EntityUpdateComponents }>,\n\t\tseconds: number,\n\t\tdynamicsCommands?: DynamicsCommands,\n\t) {\n\t\t// Collected per category first because Flatbush needs its item count up front, which is only known once\n\t\t// everything that cannot collide has been dropped.\n\t\tconst pending = new Map<number, { entries: Array<CollisionEntity<T>>, bounds: Array<number> }>();\n\t\tconst flatCommands = dynamicsCommands instanceof Float64Array ? dynamicsCommands : undefined;\n\t\tconst recordCommands = dynamicsCommands && !(dynamicsCommands instanceof Float64Array) ? dynamicsCommands : undefined;\n\n\t\tfor(const entity of entities) {\n\t\t\t// The ECS types blocks generically as ComponentTypedArray; narrow to the concrete arrays once here so\n\t\t\t// neither the search nor the game's callback needs a cast.\n\t\t\tconst components = entity.components as CollisionComponents<T>;\n\t\t\tconst transform = components.transform;\n\t\t\tconst body = components.body;\n\t\t\tif(!transform || !body) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No mask can name category 0, so a category-0 entity can never be the other half of a collision;\n\t\t\t// dropping it here is exact and is what makes `collideCategory: 0` opt out entirely.\n\t\t\tconst category = body[BODY_CATEGORY_INDEX];\n\t\t\tif(category === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst shape = bodyShape(body);\n\t\t\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\t\t\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\t\t\t// A shape with no area can never overlap anything; dropping it keeps a sizeless entity out of the tree.\n\t\t\tif(shapeIsEmpty(shape, width, height)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\t\t\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\t\t\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\n\t\t\tif(components.bounciness) {\n\t\t\t\tthis.hasBounciness = true;\n\t\t\t}\n\n\t\t\tconst velocity = components.velocity;\n\t\t\tconst dynamics = components.dynamics;\n\t\t\tconst command = findDynamicsCommand(recordCommands, entity.entityId);\n\t\t\tconst commandOffset = flatCommands ? findDynamicsCommandOffset(flatCommands, entity.entityId) : -1;\n\t\t\tconst inverseMass = dynamics?.[DYNAMICS_INVERSE_MASS_INDEX] ?? 1;\n\t\t\tconst forceX = commandOffset < 0 ? command?.forceX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_X_OFFSET];\n\t\t\tconst forceY = commandOffset < 0 ? command?.forceY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_Y_OFFSET];\n\t\t\tconst impulseX = commandOffset < 0 ? command?.impulseX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET];\n\t\t\tconst impulseY = commandOffset < 0 ? command?.impulseY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET];\n\t\t\tconst velocityMask = commandOffset < 0 ? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET];\n\t\t\tconst assignedVelocityX = commandOffset < 0\n\t\t\t\t? command?.velocityX\n\t\t\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_X_FLAG\n\t\t\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET]\n\t\t\t\t\t: undefined;\n\t\t\tconst assignedVelocityY = commandOffset < 0\n\t\t\t\t? command?.velocityY\n\t\t\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_Y_FLAG\n\t\t\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET]\n\t\t\t\t\t: undefined;\n\t\t\tconst velocityX = velocity?.[VELOCITY_X_INDEX] ?? 0;\n\t\t\tconst velocityY = velocity?.[VELOCITY_Y_INDEX] ?? 0;\n\t\t\tconst integratedVelocityX = velocityX\n\t\t\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_X_INDEX] ?? 0) * seconds\n\t\t\t\t+ forceX * inverseMass * seconds\n\t\t\t\t+ impulseX * inverseMass;\n\t\t\tconst integratedVelocityY = velocityY\n\t\t\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_Y_INDEX] ?? 0) * seconds\n\t\t\t\t+ forceY * inverseMass * seconds\n\t\t\t\t+ impulseY * inverseMass;\n\t\t\tconst endVelocityX = assignedVelocityX ?? integratedVelocityX;\n\t\t\tconst endVelocityY = assignedVelocityY ?? integratedVelocityY;\n\t\t\tconst travelX = Math.max(Math.abs(velocityX), Math.abs(endVelocityX)) * seconds;\n\t\t\tconst travelY = Math.max(Math.abs(velocityY), Math.abs(endVelocityY)) * seconds;\n\n\t\t\tconst x = transform[TRANSFORM_X_INDEX];\n\t\t\tconst y = transform[TRANSFORM_Y_INDEX];\n\n\t\t\tlet bucket = pending.get(category);\n\t\t\tif(!bucket) {\n\t\t\t\tbucket = { entries: [], bounds: [] };\n\t\t\t\tpending.set(category, bucket);\n\t\t\t}\n\n\t\t\tbucket.entries.push({ entityId: entity.entityId, components });\n\t\t\tbucket.bounds.push(\n\t\t\t\tx - halfWidth - travelX,\n\t\t\t\ty - halfHeight - travelY,\n\t\t\t\tx + halfWidth + travelX,\n\t\t\t\ty + halfHeight + travelY,\n\t\t\t);\n\t\t}\n\n\t\tfor(const [category, { entries, bounds }] of pending) {\n\t\t\tconst index = new Flatbush(entries.length);\n\t\t\tfor(let i = 0; i < entries.length; i++) {\n\t\t\t\tindex.add(bounds[i * 4], bounds[i * 4 + 1], bounds[i * 4 + 2], bounds[i * 4 + 3]);\n\t\t\t}\n\t\t\tindex.finish();\n\n\t\t\tthis.buckets.push({ category, entries, index });\n\t\t}\n\t}\n\n\t// Claims a contact for the caller: true the first time this run sees the pair, false every time after. Both\n\t// sides find the same contact - one sweeping into it, the other overlapping it on its own turn - and it is\n\t// resolved once, by whichever got there first, so nothing is bounced or reported twice.\n\tclaimContact(a: number, b: number): boolean {\n\t\tconst key = a < b ? a * MAX_PAIR_ID + b : b * MAX_PAIR_ID + a;\n\t\tif(this.resolved.has(key)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.resolved.add(key);\n\n\t\treturn true;\n\t}\n\n\t// Runs `handle` for everything `self` really overlaps, having just moved. The tree narrows the field, then\n\t// each candidate gets the full rotated box test against where both entities are now - which for one that has\n\t// not moved yet is still its start. That evens out: the pair is seen again from the other side once it moves.\n\t//\n\t// `moveX`/`moveY` is the move this entity just took, so a continuous-collision body can be tested along the way\n\tforEachOverlapping(self: MovingEntity<T>, handle: (other: CollisionEntity<T>) => void, moveX = 0, moveY = 0): void {\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { x, y, halfWidth, halfHeight } = searcher;\n\n\t\tif(searcher.ccd && (moveX !== 0 || moveY !== 0)) {\n\t\t\tconst startX = x - moveX;\n\t\t\tconst startY = y - moveY;\n\t\t\tconst minX = Math.min(startX, x) - halfWidth;\n\t\t\tconst minY = Math.min(startY, y) - halfHeight;\n\t\t\tconst maxX = Math.max(startX, x) + halfWidth;\n\t\t\tconst maxY = Math.max(startY, y) + halfHeight;\n\t\t\tthis.forEachCandidate(searcher, minX, minY, maxX, maxY, other => {\n\t\t\t\tif(sweptOverlaps(searcher, startX, startY, moveX, moveY, other)) {\n\t\t\t\t\thandle(other);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Searched ungrown at where this entity ended up: every tree box already allows for its own entity's move.\n\t\tthis.forEachCandidate(searcher, x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight, other => {\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\thandle(other);\n\t\t\t}\n\t\t});\n\t}\n\n\t// Where `self`'s centre sits the moment it first touches `other`, along the move it just took (moveX, moveY).\n\t// Used by death interpolation to end a dying entity's last segment exactly on the thing it hit, rather than\n\t// wherever its step happened to land - which for a fast continuous body is a stride past the target. `self`'s\n\t// transform is read after the move, so the start it sweeps from is reconstructed as (end - move). Falls back to\n\t// the current position for an entity that cannot collide or did not move.\n\tcontactPoint(self: ContactSource, other: ContactTarget, moveX: number, moveY: number): { x: number, y: number } {\n\t\tconst transform = self.components.transform;\n\t\tconst endX = transform[TRANSFORM_X_INDEX];\n\t\tconst endY = transform[TRANSFORM_Y_INDEX];\n\t\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher || distance === 0) {\n\t\t\treturn { x: endX, y: endY };\n\t\t}\n\n\t\tsearcher.x = endX - moveX;\n\t\tsearcher.y = endY - moveY;\n\t\tconst fraction = this.entryFraction(searcher, other, moveX, moveY, distance);\n\n\t\treturn { x: searcher.x + moveX * fraction, y: searcher.y + moveY * fraction };\n\t}\n\n\t// The direction `self` leaves `other` at first touch. For a swept sensor this is evaluated where its path\n\t// entered the target, not at the far end of the move after it may already have passed through.\n\tcontactNormal(self: ContactSource, other: ContactTarget, moveX: number, moveY: number, out: Vector): boolean {\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst point = moveX === 0 && moveY === 0\n\t\t\t? { x: searcher.x, y: searcher.y }\n\t\t\t: this.contactPoint(self, other, moveX, moveY);\n\n\t\treturn contactNormalAt(searcher, point.x, point.y, other, out);\n\t}\n\n\t// How much of the move `self` may take before it runs into something. The whole move is checked in one pass\n\t// for what it lands on, and only then is the resting place refined - so the answer is the earliest contact\n\t// of the lot, not whichever candidate came up first.\n\tsweep(self: MovingEntity<T>, moveX: number, moveY: number): SweepResult<T> {\n\t\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher || distance === 0) {\n\t\t\treturn { fraction: 1, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\tif(searcher.ccd) {\n\t\t\treturn this.sweepContinuous(searcher, moveX, moveY, distance);\n\t\t}\n\n\t\tconst candidates = this.gatherCandidates(searcher, moveX, moveY);\n\t\tif(candidates.length === 0) {\n\t\t\treturn { fraction: 1, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\treturn this.refine(searcher, candidates, moveX, moveY, distance);\n\t}\n\n\t// Resolves a move as the displacement to write. With `slide` off it is `sweep` in other clothes: the whole\n\t// move, or the swept-short part. With `slide` on, a blocked diagonal is resolved one axis at a time so an\n\t// entity clipping a corner keeps running along the wall instead of sticking to it. Sliding is the caller's to\n\t// ask for: it is wrong for a bouncing entity, which turns around rather than skating along, so bounces pass\n\t// `slide` off.\n\tresolveMove(self: MovingEntity<T>, moveX: number, moveY: number, slide: boolean): MoveResult<T> {\n\t\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\t\tconst searcher = toSearcher(self);\n\t\tif(!searcher || distance === 0) {\n\t\t\treturn { moveX, moveY, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\t// A continuous body comes straight to rest at its first contact, never sliding: sliding is for a steered\n\t\t// character clipping a corner at walking pace, not the fast thing ccd is switched on for, and the swept\n\t\t// path is a single decision over the whole move rather than one refined per axis.\n\t\tif(searcher.ccd) {\n\t\t\tconst rest = this.sweepContinuous(searcher, moveX, moveY, distance);\n\n\t\t\treturn { moveX: moveX * rest.fraction, moveY: moveY * rest.fraction, blocking: rest.blocking };\n\t\t}\n\n\t\tconst candidates = this.gatherCandidates(searcher, moveX, moveY);\n\t\tif(candidates.length === 0) {\n\t\t\treturn { moveX, moveY, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\t// Blocked diagonal, asked to slide: resolve each axis on its own to find which one was actually blocked.\n\t\tif(slide && moveX !== 0 && moveY !== 0) {\n\t\t\tconst xCandidates = this.gatherCandidates(searcher, moveX, 0);\n\t\t\tconst yCandidates = this.gatherCandidates(searcher, 0, moveY);\n\n\t\t\t// Corner clip: each axis alone is clear, so only the diagonal was caught. Slide the full length of the\n\t\t\t// dominant axis and leave the other - advancing it would walk back into the corner.\n\t\t\tif(xCandidates.length === 0 && yCandidates.length === 0) {\n\t\t\t\treturn Math.abs(moveX) >= Math.abs(moveY)\n\t\t\t\t\t? { moveX, moveY: 0, blocking: NOTHING_BLOCKING }\n\t\t\t\t\t: { moveX: 0, moveY, blocking: NOTHING_BLOCKING };\n\t\t\t}\n\n\t\t\t// At least one axis is blocked. Take each as far as it goes - a blocked one refined up to its edge, not\n\t\t\t// dropped to nothing - so the entity ends hard against the obstacle.\n\t\t\tconst x = xCandidates.length === 0 ? CLEAR : this.refine(searcher, xCandidates, moveX, 0, Math.abs(moveX));\n\t\t\tconst y = yCandidates.length === 0 ? CLEAR : this.refine(searcher, yCandidates, 0, moveY, Math.abs(moveY));\n\n\t\t\tconst slidX = moveX * x.fraction;\n\t\t\tconst slidY = moveY * y.fraction;\n\n\t\t\t// The axes were swept apart; their combination can still poke into a convex (round) obstacle neither axis\n\t\t\t// alone reached - sliding the full length of one axis carries the entity into the curve the other axis\n\t\t\t// stopped short of. Left overlapping, next run's start-overlap escape hatch would let it pass straight\n\t\t\t// through, so the combined slide is checked against where it actually lands and refined to contact if it\n\t\t\t// overlaps. A box slide ends touching an axis-aligned face and finds nothing here.\n\t\t\tif(slidX !== 0 && slidY !== 0) {\n\t\t\t\tconst slidCandidates = this.gatherCandidates(searcher, slidX, slidY);\n\t\t\t\tif(slidCandidates.length > 0) {\n\t\t\t\t\tconst rest = this.refine(searcher, slidCandidates, slidX, slidY, Math.sqrt(slidX * slidX + slidY * slidY));\n\n\t\t\t\t\treturn { moveX: slidX * rest.fraction, moveY: slidY * rest.fraction, blocking: rest.blocking };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { moveX: slidX, moveY: slidY, blocking: mergeBlocking(x.blocking, y.blocking) };\n\t\t}\n\n\t\t// Not sliding, or a straight move: come to rest where it stops.\n\t\tconst rest = this.refine(searcher, candidates, moveX, moveY, distance);\n\n\t\treturn { moveX: moveX * rest.fraction, moveY: moveY * rest.fraction, blocking: rest.blocking };\n\t}\n\n\t// The cheap half of a sweep: everything the move would land on, tested only where it ends. That keeps a clear\n\t// move - nearly every move - to one shape test per candidate. The trade is that a move long enough to carry\n\t// an entity clean past something is not stopped by it, which a fixed `deltaBetweenRuns` rules out.\n\tprivate gatherCandidates(searcher: Searcher, moveX: number, moveY: number): Array<CollisionEntity<T>> {\n\t\tconst { x, y, halfWidth, halfHeight } = searcher;\n\n\t\t// A sensor's move is blocked by nothing; it is still reported by forEachOverlapping, but never gathered.\n\t\tif(searcher.sensor) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst endX = x + moveX;\n\t\tconst endY = y + moveY;\n\n\t\tconst candidates: Array<CollisionEntity<T>> = [];\n\t\tthis.forEachCandidate(searcher, endX - halfWidth, endY - halfHeight, endX + halfWidth, endY + halfHeight, other => {\n\t\t\tif(!overlapsAt(searcher, endX, endY, other)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// A sensor blocks nothing: a solid mover passes through it, left to the overlap callback.\n\t\t\tif(isSensor(other.components.body)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Already inside it before the move began: it is not what this move ran into, and blocking on it would\n\t\t\t// pin the entity inside with no way out. Let the move through and leave the pair to the overlap callback.\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcandidates.push(other);\n\t\t});\n\n\t\treturn candidates;\n\t}\n\n\t// The expensive half: the move is known to stop short, and this finds where by halving between a `clear`\n\t// fraction and a `blocked` one. Contact is halved in on rather than solved - three shapes at any rotation\n\t// have no single first-touch formula - and the result is always taken from a position that tested clear.\n\tprivate refine(searcher: Searcher, candidates: Array<CollisionEntity<T>>, moveX: number, moveY: number, distance: number): SweepResult<T> {\n\t\tconst { x, y } = searcher;\n\t\tlet clear = 0;\n\t\tlet blocked = 1;\n\t\tconst tolerance = CONTACT_TOLERANCE / distance;\n\t\tfor(let i = 0; i < MAX_REFINEMENTS && blocked - clear > tolerance; i++) {\n\t\t\tconst middle = (clear + blocked) / 2;\n\t\t\tif(this.blockedAt(searcher, candidates, moveX, moveY, middle)) {\n\t\t\t\tblocked = middle;\n\t\t\t} else {\n\t\t\t\tclear = middle;\n\t\t\t}\n\t\t}\n\n\t\t// Whatever is in the way at the first blocked position: the entity rests a hair short of it.\n\t\tconst blocking: Array<CollisionEntity<T>> = [];\n\t\tconst blockedX = x + moveX * blocked;\n\t\tconst blockedY = y + moveY * blocked;\n\t\tfor(const other of candidates) {\n\t\t\tif(overlapsAt(searcher, blockedX, blockedY, other)) {\n\t\t\t\tblocking.push(other);\n\t\t\t}\n\t\t}\n\n\t\t// An entity already against something is left exactly where it is, rather than crawling a sub-tolerance\n\t\t// step forward every run and reporting a position change for no visible move.\n\t\treturn { fraction: clear * distance > CONTACT_TOLERANCE ? clear : 0, blocking };\n\t}\n\n\t// The continuous counterpart to gatherCandidates: everything the move sweeps *across*, not only what it ends\n\t// on, so a step long enough to fly clean past something still gathers it. Costs the swept-shape test per\n\t// candidate, which is why it is a body's own opt-in rather than the default.\n\tprivate gatherContinuousCandidates(searcher: Searcher, moveX: number, moveY: number): Array<CollisionEntity<T>> {\n\t\t// A sensor is stopped by nothing; its overlaps are still reported by forEachOverlapping, never gathered here.\n\t\tif(searcher.sensor) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst { x, y, halfWidth, halfHeight } = searcher;\n\t\tconst endX = x + moveX;\n\t\tconst endY = y + moveY;\n\n\t\tconst candidates: Array<CollisionEntity<T>> = [];\n\t\tthis.forEachCandidate(searcher, Math.min(x, endX) - halfWidth, Math.min(y, endY) - halfHeight, Math.max(x, endX) + halfWidth, Math.max(y, endY) + halfHeight, other => {\n\t\t\t// A sensor blocks nothing: a solid mover passes through it, left to the overlap callback.\n\t\t\tif(isSensor(other.components.body)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Already inside it before the move began: not what this move ran into, and blocking on it would pin the\n\t\t\t// entity there. Let the move through and leave the pair to the overlap callback.\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif(sweptOverlaps(searcher, x, y, moveX, moveY, other)) {\n\t\t\t\tcandidates.push(other);\n\t\t\t}\n\t\t});\n\n\t\treturn candidates;\n\t}\n\n\t// A continuous move's resting place: the earliest contact of everything it swept across. Each candidate's own\n\t// entry fraction is found and the whole move stops at the smallest of them - the near obstacle, not whichever\n\t// the tree happened to turn up first.\n\tprivate sweepContinuous(searcher: Searcher, moveX: number, moveY: number, distance: number): SweepResult<T> {\n\t\tconst candidates = this.gatherContinuousCandidates(searcher, moveX, moveY);\n\t\tif(candidates.length === 0) {\n\t\t\treturn { fraction: 1, blocking: NOTHING_BLOCKING };\n\t\t}\n\n\t\tlet earliest = 1;\n\t\tlet blocking: Array<CollisionEntity<T>> = [];\n\t\tconst tolerance = CONTACT_TOLERANCE / distance;\n\t\tfor(const other of candidates) {\n\t\t\tconst fraction = this.entryFraction(searcher, other, moveX, moveY, distance);\n\t\t\tif(fraction < earliest - tolerance) {\n\t\t\t\tearliest = fraction;\n\t\t\t\tblocking = [other];\n\t\t\t} else if(fraction <= earliest + tolerance) {\n\t\t\t\tblocking.push(other);\n\t\t\t}\n\t\t}\n\n\t\t// An entity already against something is left exactly where it is rather than creeping a sub-tolerance step\n\t\t// forward every run, same as refine.\n\t\treturn { fraction: earliest * distance > CONTACT_TOLERANCE ? earliest : 0, blocking };\n\t}\n\n\t// The fraction of its move at which `searcher` first touches `other`, halved in the way refine does. What refine\n\t// cannot assume here is that the far end is blocked: a continuous move may pass clean through, clear at both\n\t// ends, so the blocked bracket is seeded from where the two centres pass closest - which for convex shapes that\n\t// really cross lies inside the overlap - rather than from the end of the move.\n\tprivate entryFraction(searcher: Searcher, other: ContactTarget, moveX: number, moveY: number, distance: number): number {\n\t\tlet blocked: number;\n\t\tif(this.blockedAtOne(searcher, other, moveX, moveY, 1)) {\n\t\t\tblocked = 1;\n\t\t} else {\n\t\t\tconst transform = other.components.transform;\n\t\t\tconst toX = transform[TRANSFORM_X_INDEX] - searcher.x;\n\t\t\tconst toY = transform[TRANSFORM_Y_INDEX] - searcher.y;\n\t\t\tconst closest = clamp01((toX * moveX + toY * moveY) / (distance * distance));\n\t\t\t// The swept shape crossed the candidate but the centres' closest pass is not itself an overlap (a\n\t\t\t// glancing hit off the centre line): stop there rather than search on. It rests a hair early at worst,\n\t\t\t// which never tunnels.\n\t\t\tif(!this.blockedAtOne(searcher, other, moveX, moveY, closest)) {\n\t\t\t\treturn closest;\n\t\t\t}\n\n\t\t\tblocked = closest;\n\t\t}\n\n\t\tlet clear = 0;\n\t\tconst tolerance = CONTACT_TOLERANCE / distance;\n\t\tfor(let i = 0; i < MAX_REFINEMENTS && blocked - clear > tolerance; i++) {\n\t\t\tconst middle = (clear + blocked) / 2;\n\t\t\tif(this.blockedAtOne(searcher, other, moveX, moveY, middle)) {\n\t\t\t\tblocked = middle;\n\t\t\t} else {\n\t\t\t\tclear = middle;\n\t\t\t}\n\t\t}\n\n\t\treturn clear;\n\t}\n\n\t// blockedAt for a single candidate, so the continuous refinement halves without allocating a one-item array per\n\t// step. The position is rounded to float32 for the same reason blockedAt is - the resting place must stay clear\n\t// once stored to the shared transform.\n\tprivate blockedAtOne(searcher: Searcher, other: ContactTarget, moveX: number, moveY: number, fraction: number): boolean {\n\t\treturn overlapsAt(searcher, Math.fround(searcher.x + moveX * fraction), Math.fround(searcher.y + moveY * fraction), other);\n\t}\n\n\t// Whether `self` would be inside any of `candidates` that far along its move. The position is rounded to\n\t// float32 - what `move` actually stores into the shared transform - so the resting place this settles on is\n\t// still clear once stored, not a hair inside. Otherwise a float32 nudge into overlap would trip the\n\t// start-overlap escape hatch next run and let the entity pass straight through what it came to rest against.\n\tprivate blockedAt(searcher: Searcher, candidates: Array<CollisionEntity<T>>, moveX: number, moveY: number, fraction: number): boolean {\n\t\tconst x = Math.fround(searcher.x + moveX * fraction);\n\t\tconst y = Math.fround(searcher.y + moveY * fraction);\n\t\tfor(const other of candidates) {\n\t\t\tif(overlapsAt(searcher, x, y, other)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t// Runs `handle` for every collidable entity in the box that `searcher` may collide with: the tree search and\n\t// the category masks, applied before any shape test so an impossible pair is settled by two ANDs.\n\tprivate forEachCandidate(searcher: Searcher, minX: number, minY: number, maxX: number, maxY: number, handle: (other: CollisionEntity<T>) => void): void {\n\t\tfor(const bucket of this.buckets) {\n\t\t\t// Half of canCollide, settled once per bucket: all entries share this category.\n\t\t\tif((searcher.mask & bucket.category) === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor(const found of bucket.index.search(minX, minY, maxX, maxY)) {\n\t\t\t\tconst other = bucket.entries[found];\n\t\t\t\tif(other.entityId === searcher.entityId) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// The other half of canCollide, per candidate: each entity carries its own mask.\n\t\t\t\tif((other.components.body[BODY_MASK_INDEX] & searcher.category) === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Already killed this run, or by another worker sharing this block.\n\t\t\t\tif(other.components.entity?.[DEAD_INDEX] === 1) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Struck what will kill it and playing out its final segment onto the impact point: it is a step from\n\t\t\t\t// removal and must not be run into again, on this run or the next, or it would resolve a second contact\n\t\t\t\t// and be killed twice. Skipped here rather than left out of the tree so it stays a candidate for nothing\n\t\t\t\t// - the same way a dead entity is filtered at query time, not at build.\n\t\t\t\tif(isDying(other.components.body)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\thandle(other);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction contactNormalAt(searcher: Searcher, x: number, y: number, other: ContactTarget, out: Vector): boolean {\n\tconst transform = other.components.transform;\n\tconst otherShape = bodyShape(other.components.body);\n\tconst otherX = transform[TRANSFORM_X_INDEX];\n\tconst otherY = transform[TRANSFORM_Y_INDEX];\n\tconst otherWidth = transform[TRANSFORM_WIDTH_INDEX];\n\tconst otherHeight = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst otherAngle = transform[TRANSFORM_ANGLE_INDEX];\n\tif(searcher.shape === SHAPE_POLYGON || otherShape === SHAPE_POLYGON) {\n\t\treturn polygonContactNormal(\n\t\t\tsearcher.shape, searcher.polygon,\n\t\t\tx, y, searcher.width, searcher.height, searcher.angle,\n\t\t\totherShape, other.components.polygon,\n\t\t\totherX, otherY, otherWidth, otherHeight, otherAngle,\n\t\t\tout,\n\t\t);\n\t}\n\n\treturn shapeContactNormal(\n\t\tsearcher.shape, x, y, searcher.width, searcher.height, searcher.angle,\n\t\totherShape, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t\tout,\n\t);\n}\n\n// Merges what stopped each axis of a slide, without repeating an entity that stopped both. Either side empty is\n// the common case, so it is handed straight back rather than copied.\nfunction mergeBlocking<T extends PhysicsUpdateComponents>(a: Array<CollisionEntity<T>>, b: Array<CollisionEntity<T>>): Array<CollisionEntity<T>> {\n\tif(a.length === 0) {\n\t\treturn b;\n\t}\n\tif(b.length === 0) {\n\t\treturn a;\n\t}\n\n\tconst merged = [...a];\n\tfor(const other of b) {\n\t\tif(!merged.some(entry => entry.entityId === other.entityId)) {\n\t\t\tmerged.push(other);\n\t\t}\n\t}\n\n\treturn merged;\n}\n\n// Reads what a searching entity is measured by, or undefined for one that cannot collide.\nfunction toSearcher(self: ContactSource): Searcher | undefined {\n\t// No body means no collisions, though the system can still move it - the move query does not require a body.\n\tconst body = self.components.body;\n\tif(!body) {\n\t\treturn undefined;\n\t}\n\n\t// Mask 0 runs into nothing, so no bucket is worth searching. This only stops it finding a collision; something\n\t// whose mask accepts it can still find this one, an asymmetry the symmetric canCollide rule then rules out.\n\tconst mask = body[BODY_MASK_INDEX];\n\tif(mask === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst transform = self.components.transform;\n\tconst shape = bodyShape(body);\n\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\tif(shapeIsEmpty(shape, width, height)) {\n\t\treturn undefined;\n\t}\n\n\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\tconst polygon = self.components.polygon;\n\tif(shape === SHAPE_POLYGON && !polygon) {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tentityId: self.entityId,\n\t\tshape,\n\t\tpolygon,\n\t\tx: transform[TRANSFORM_X_INDEX],\n\t\ty: transform[TRANSFORM_Y_INDEX],\n\t\twidth,\n\t\theight,\n\t\tangle,\n\t\thalfWidth: shapeHalfWidth(shape, width, height, angle),\n\t\thalfHeight: shapeHalfHeight(shape, width, height, angle),\n\t\tcategory: body[BODY_CATEGORY_INDEX],\n\t\tmask,\n\t\tsensor: isSensor(body),\n\t\tccd: isContinuous(body),\n\t};\n}\n\n// Whether `searcher`, swept from (startX, startY) along (moveX, moveY), crosses `other` anywhere along the way -\n// the single extra test a continuous-collision body pays per candidate. Each shape sweeps to one covering shape:\n// rectangle - an oriented box holding its start, its end, and everything between, since a box that does not turn\n// as it moves just slides in its own frame and the region it covers is itself grown by that slide.\n// circle - a capsule down the centre path, which is exactly a moving circle's swept region.\n// capsule - that same path capsule, plus its own destination shape for the far end of the pill the straight\n// path capsule falls short of.\n// Every shape's region is a superset of the true swept area, so a contact is only ever reported early, never\n// missed - which is the whole point of turning ccd on.\nfunction sweptOverlaps(searcher: Searcher, startX: number, startY: number, moveX: number, moveY: number, other: ContactTarget): boolean {\n\tconst endX = startX + moveX;\n\tconst endY = startY + moveY;\n\tconst transform = other.components.transform;\n\tconst otherShape = bodyShape(other.components.body);\n\tconst otherX = transform[TRANSFORM_X_INDEX];\n\tconst otherY = transform[TRANSFORM_Y_INDEX];\n\tconst otherWidth = transform[TRANSFORM_WIDTH_INDEX];\n\tconst otherHeight = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst otherAngle = transform[TRANSFORM_ANGLE_INDEX];\n\n\tif(searcher.shape === SHAPE_RECTANGLE || searcher.shape === SHAPE_POLYGON) {\n\t\t// The move measured in the box's own frame, where the box is axis aligned and merely slides by it. Growing\n\t\t// each side by that slide over-covers the two corners the slid hexagon leaves open, which reports early, not\n\t\t// short.\n\t\tconst cos = Math.cos(searcher.angle);\n\t\tconst sin = Math.sin(searcher.angle);\n\t\tconst localX = moveX * cos + moveY * sin;\n\t\tconst localY = -moveX * sin + moveY * cos;\n\n\t\treturn overlapShapes(\n\t\t\tSHAPE_RECTANGLE, undefined, (startX + endX) / 2, (startY + endY) / 2,\n\t\t\tsearcher.width + Math.abs(localX), searcher.height + Math.abs(localY), searcher.angle,\n\t\t\totherShape, other.components.polygon, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t\t);\n\t}\n\n\t// A capsule of the mover's own radius laid down the centre path: width is the path length plus a cap at each\n\t// end, height the full thickness. For a circle that is the exact swept region; for a capsule the destination\n\t// test below adds back the length of the pill it does not cover.\n\tconst radius = shapeRadius(searcher.shape, searcher.width, searcher.height);\n\tconst distance = Math.sqrt(moveX * moveX + moveY * moveY);\n\tconst pathAngle = distance > 0 ? Math.atan2(moveY, moveX) : searcher.angle;\n\tconst alongPath = overlapShapes(\n\t\tSHAPE_CAPSULE, undefined, (startX + endX) / 2, (startY + endY) / 2, distance + radius * 2, radius * 2, pathAngle,\n\t\totherShape, other.components.polygon, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t);\n\tif(alongPath) {\n\t\treturn true;\n\t}\n\n\treturn overlapShapes(\n\t\tsearcher.shape, searcher.polygon, endX, endY, searcher.width, searcher.height, searcher.angle,\n\t\totherShape, other.components.polygon, otherX, otherY, otherWidth, otherHeight, otherAngle,\n\t);\n}\n\n// [0, 1] clamp for the closest-approach fraction; the shapes helper keeps its own copy unexported.\nfunction clamp01(value: number): number {\n\tif(value < 0) {\n\t\treturn 0;\n\t} else if(value > 1) {\n\t\treturn 1;\n\t}\n\n\treturn value;\n}\n\n// Whether the searcher, put down at (x, y) rather than its own block's position, overlaps `other`. Taking the\n// position as arguments lets a sweep try a whole path without writing any of it.\nfunction overlapsAt(searcher: Searcher, x: number, y: number, other: ContactTarget): boolean {\n\tconst transform = other.components.transform;\n\n\treturn overlapShapes(\n\t\tsearcher.shape, searcher.polygon, x, y, searcher.width, searcher.height, searcher.angle,\n\t\tbodyShape(other.components.body), other.components.polygon,\n\t\ttransform[TRANSFORM_X_INDEX], transform[TRANSFORM_Y_INDEX],\n\t\ttransform[TRANSFORM_WIDTH_INDEX], transform[TRANSFORM_HEIGHT_INDEX], transform[TRANSFORM_ANGLE_INDEX],\n\t);\n}\n\nfunction overlapShapes(\n\taShape: number, aPolygon: Float32Array | undefined,\n\taX: number, aY: number, aWidth: number, aHeight: number, aAngle: number,\n\tbShape: number, bPolygon: Float32Array | undefined,\n\tbX: number, bY: number, bWidth: number, bHeight: number, bAngle: number,\n): boolean {\n\tif(aShape !== SHAPE_POLYGON && bShape !== SHAPE_POLYGON) {\n\t\treturn shapesOverlap(\n\t\t\taShape, aX, aY, aWidth, aHeight, aAngle,\n\t\t\tbShape, bX, bY, bWidth, bHeight, bAngle,\n\t\t);\n\t}\n\n\treturn polygonShapesOverlap(\n\t\taShape, aPolygon, aX, aY, aWidth, aHeight, aAngle,\n\t\tbShape, bPolygon, bX, bY, bWidth, bHeight, bAngle,\n\t);\n}\n","import Flatbush from 'flatbush';\nimport type { EntityUpdateComponents } from '@daneren2005/shared-memory-ecs';\nimport { bodyShape } from '../components/body-component';\nimport { SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { TRANSFORM_ANGLE_INDEX, TRANSFORM_HEIGHT_INDEX, TRANSFORM_WIDTH_INDEX, TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { shapeHalfHeight, shapeHalfWidth } from '../math/shapes';\n\n// The blocks the index reads off each entity. Only the transform is needed; the body refines the box to the\n// entity's actual shape. Anything else a game queried travels along untouched for a filter to read.\nexport type SpatialComponents = {\n\ttransform: Float32Array\n\tbody?: Uint32Array\n};\n\n// One entity as the index hands it back: its id plus whatever blocks came with it.\nexport interface SpatialEntity<T extends SpatialComponents = SpatialComponents> {\n\tentityId: number\n\tcomponents: T\n}\n\n// Which found entities are wanted. Run per candidate, so it is the place for what the index cannot know - the\n// searcher's own id, which team, whether it is dead - and a reject is as if it were never in the box.\nexport type SpatialFilter<T extends SpatialComponents = SpatialComponents> = (entity: SpatialEntity<T>) => boolean;\n\n// An R-tree over a set of entities, for questions about where things are rather than what is hitting what: who\n// is in this area, and what is nearest a point. The same machinery as the collision broadphase minus everything\n// collision specific, so answers are in terms of each entity's axis-aligned box and a `filter` picks the ones\n// that matter. It is a snapshot - nothing reads the blocks after the constructor - so build one per run.\n//\n// const index = new SpatialIndex(queries.collidable);\n// const nearest = index.findNearest(x, y, 150, other => other.entityId !== entityId);\nexport default class SpatialIndex<T extends SpatialComponents = SpatialComponents> {\n\tprivate entries: Array<SpatialEntity<T>> = [];\n\t// Left off for an empty index: Flatbush cannot hold zero items, and there is no answer to give anyway.\n\tprivate index: Flatbush | undefined;\n\n\t// `entities` is anything shaped like a query result, so a system's own `queries.whatever` goes straight in.\n\t// Entities without a transform are skipped - something with no place in the world cannot be found by a search\n\t// of it.\n\tconstructor(entities: Iterable<{ entityId: number, components: EntityUpdateComponents }>) {\n\t\t// Collected up front because Flatbush needs its item count before any are added, only known once everything\n\t\t// without a transform is dropped.\n\t\tconst bounds: Array<number> = [];\n\n\t\tfor(const entity of entities) {\n\t\t\t// The ECS types blocks generically as ComponentTypedArray; narrow to the concrete arrays once here.\n\t\t\tconst components = entity.components as T;\n\t\t\tconst transform = components.transform;\n\t\t\tif(!transform) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No body means the transform's width and height are a plain rectangle, keeping the index usable for\n\t\t\t// anything with a place in the world, not only things that collide.\n\t\t\tconst body = components.body;\n\t\t\tconst shape = body ? bodyShape(body) : SHAPE_RECTANGLE;\n\t\t\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\t\t\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\t\t\tconst angle = transform[TRANSFORM_ANGLE_INDEX];\n\t\t\tconst x = transform[TRANSFORM_X_INDEX];\n\t\t\tconst y = transform[TRANSFORM_Y_INDEX];\n\n\t\t\t// Unlike the collision broadphase, a zero-area entity is kept: it still has a position to be found at.\n\t\t\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\t\t\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\n\t\t\tthis.entries.push({ entityId: entity.entityId, components });\n\t\t\tbounds.push(x - halfWidth, y - halfHeight, x + halfWidth, y + halfHeight);\n\t\t}\n\n\t\tif(this.entries.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst index = new Flatbush(this.entries.length);\n\t\tfor(let i = 0; i < this.entries.length; i++) {\n\t\t\tindex.add(bounds[i * 4], bounds[i * 4 + 1], bounds[i * 4 + 2], bounds[i * 4 + 3]);\n\t\t}\n\t\tindex.finish();\n\n\t\tthis.index = index;\n\t}\n\n\t// How many entities are in the index, which is not how many were handed to it: transform-less ones are out.\n\tget size(): number {\n\t\treturn this.entries.length;\n\t}\n\n\t// Everything whose box reaches into the one given, in no particular order. Touching counts, so an exact\n\t// edge question is the caller's to settle on the results.\n\tsearch(minX: number, minY: number, maxX: number, maxY: number, filter?: SpatialFilter<T>): Array<SpatialEntity<T>> {\n\t\tconst index = this.index;\n\t\tif(!index) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst entries = this.entries;\n\t\t// Filtering inside the search keeps a reject from ever reaching the results array.\n\t\tconst found = filter ? index.search(minX, minY, maxX, maxY, i => filter(entries[i]))\n\t\t\t: index.search(minX, minY, maxX, maxY);\n\n\t\treturn found.map(i => entries[i]);\n\t}\n\n\t// `search` from the middle out: everything within `reachX` / `reachY` of (x, y), the form most searches take.\n\tsearchAround(x: number, y: number, reachX: number, reachY: number, filter?: SpatialFilter<T>): Array<SpatialEntity<T>> {\n\t\treturn this.search(x - reachX, y - reachY, x + reachX, y + reachY, filter);\n\t}\n\n\t// The closest entity to (x, y) that `filter` accepts, or undefined when none is within `maxDistance`. A walk\n\t// of the tree in distance order rather than a search-then-sort, so it never measures the far side of the\n\t// world. Distance is to the entity's box, not its centre, so a big thing is as near as its nearest edge.\n\tfindNearest(x: number, y: number, maxDistance = Infinity, filter?: SpatialFilter<T>): SpatialEntity<T> | undefined {\n\t\treturn this.findNearby(x, y, 1, maxDistance, filter)[0];\n\t}\n\n\t// The `maxResults` closest entities to (x, y), nearest first, on the same terms as findNearest.\n\tfindNearby(x: number, y: number, maxResults: number, maxDistance = Infinity, filter?: SpatialFilter<T>): Array<SpatialEntity<T>> {\n\t\tconst index = this.index;\n\t\tif(!index) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst entries = this.entries;\n\t\t// Handed to Flatbush rather than applied afterwards, so `maxResults` counts wanted entities: filtering after\n\t\t// would let a few rejects use up the allowance and hide a valid target behind them.\n\t\tconst found = filter ? index.neighbors(x, y, maxResults, maxDistance, i => filter(entries[i]))\n\t\t\t: index.neighbors(x, y, maxResults, maxDistance);\n\n\t\treturn found.map(i => entries[i]);\n\t}\n}\n","import SharedSpatialMap from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport type { PhysicalSystemWorld } from '../world';\n\nexport function getSpatialMap(world: PhysicalSystemWorld): SharedSpatialMap {\n\tif(world.spatialMap) {\n\t\treturn world.spatialMap;\n\t}\n\tif(!world.heap || !world.spatialMapMemory) {\n\t\tthrow new Error('Spatial system world is missing its shared MemoryHeap or spatial map');\n\t}\n\n\tworld.spatialMap = new SharedSpatialMap(world.heap, world.spatialMapMemory);\n\n\treturn world.spatialMap;\n}\n\n","import type SharedSpatialMap from '@daneren2005/shared-memory-objects/spatial/shared-spatial-map';\nimport { spatialBounds, type SpatialBlockComponents } from '../systems/spatial-bounds';\nimport type { PhysicalSystemWorld } from '../world';\nimport { getSpatialMap } from './get-spatial-map';\n\nexport function updateSpatialMap(world: PhysicalSystemWorld, entityId: number, components: SpatialBlockComponents): void {\n\tif(!world.spatialMap && (!world.heap || !world.spatialMapMemory)) {\n\t\treturn;\n\t}\n\n\tconst spatialMap: SharedSpatialMap = getSpatialMap(world);\n\tconst bounds = spatialBounds(components);\n\tspatialMap.update(entityId, bounds.minX, bounds.minY, bounds.maxX - bounds.minX, bounds.maxY - bounds.minY);\n}\n\n","import type { EntityUpdateComponents } from '@daneren2005/shared-memory-ecs/worker';\nimport { storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport type { Vector } from '../math/shapes';\nimport type { PhysicalSystemWorld } from '../world';\nimport { updateSpatialMap } from './update-spatial-map';\n\nexport function setEntityPosition(\n\tworld: PhysicalSystemWorld, entity: { entityId: number, components: EntityUpdateComponents }, position: Vector,\n): void {\n\tconst transform = entity.components.transform;\n\tif(!(transform instanceof Float32Array)) {\n\t\treturn;\n\t}\n\tstoreFloat32(transform, TRANSFORM_X_INDEX, position.x);\n\tstoreFloat32(transform, TRANSFORM_Y_INDEX, position.y);\n\tconst body = entity.components.body;\n\tupdateSpatialMap(world, entity.entityId, { transform, ...(body instanceof Uint32Array ? { body } : {}) });\n}\n","import type { PhysicalWorldSource, PhysicalWorldData } from '../world';\n\nexport function addPhysicalWorldData(source: PhysicalWorldSource, target: PhysicalWorldData): void {\n\ttarget.spatialMapMemory = source.spatialMap.getSharedMemory();\n}\n\n","import type { TransformComponent } from '../components/transform-component';\nimport type { InterpolationComponent } from '../components/interpolation-component';\n\nexport interface SnappableEntity {\n\tcomponents: {\n\t\ttransform?: TransformComponent\n\t\tinterpolation?: InterpolationComponent\n\t}\n}\n\n// Reset interpolation after a teleport so it does not render as a long move.\nexport function snapEntity(entity: SnappableEntity): void {\n\tconst { transform, interpolation } = entity.components;\n\tif(!transform || !interpolation) {\n\t\treturn;\n\t}\n\n\tinterpolation.targetX = interpolation.x = transform.x;\n\tinterpolation.targetY = interpolation.y = transform.y;\n\t// A gated reader may still draw from the previous slot until the in-flight run commits.\n\tinterpolation.previousX = transform.x;\n\tinterpolation.previousY = transform.y;\n\tinterpolation.remainingDuration = 0;\n\tinterpolation.syncedStepDuration = 0;\n\tinterpolation.syncedDuration = interpolation.totalDuration;\n}\n\n","import type { TransformComponent } from '../components/transform-component';\nimport type { VelocityComponent } from '../components/velocity-component';\nimport type { InterpolationComponent } from '../components/interpolation-component';\n\nexport interface SpawnableEntity {\n\tcomponents: {\n\t\ttransform?: TransformComponent\n\t\tvelocity?: VelocityComponent\n\t\tinterpolation?: InterpolationComponent\n\t}\n}\n\nexport interface SpawnInterpolationOptions {\n\tstepMs: number\n\t// Elapsed fraction of the current step, clamped to 0..1.\n\tstepFraction: number\n\t// Last physics tick so the first real step is recognized as a new publication.\n\ttick: number\n}\n\n// Jump the transform ahead without a sweep; the render starts at spawn and catches up by the next step.\nexport function startSpawnInterpolation(entity: SpawnableEntity, options: SpawnInterpolationOptions): void {\n\tconst { transform, velocity, interpolation } = entity.components;\n\tif(!transform || !velocity || !interpolation) {\n\t\treturn;\n\t}\n\n\tconst fraction = options.stepFraction < 0 ? 0 : options.stepFraction > 1 ? 1 : options.stepFraction;\n\tconst remaining = 1 - fraction;\n\tconst seconds = options.stepMs / 1000;\n\tconst spawnX = transform.x;\n\tconst spawnY = transform.y;\n\tconst jumpX = velocity.velocityX * seconds * remaining;\n\tconst jumpY = velocity.velocityY * seconds * remaining;\n\n\ttransform.x = spawnX + jumpX;\n\ttransform.y = spawnY + jumpY;\n\tinterpolation.x = spawnX;\n\tinterpolation.y = spawnY;\n\tinterpolation.duration = options.stepMs * remaining;\n\tinterpolation.syncedTick = options.tick;\n\tinterpolation.totalDuration += interpolation.duration;\n\tinterpolation.syncedDuration = interpolation.totalDuration;\n\tinterpolation.remainingDuration = interpolation.duration;\n\tinterpolation.targetX = transform.x;\n\tinterpolation.targetY = transform.y;\n\tinterpolation.syncedStepDuration = interpolation.duration;\n\t// Publish the stamp last so readers see the completed target and timing.\n\tinterpolation.tick = options.tick;\n}\n\n","import {\n\tDYNAMICS_ACCELERATION_X_INDEX,\n\tDYNAMICS_ACCELERATION_Y_INDEX,\n\tDYNAMICS_INVERSE_MASS_INDEX,\n} from '../components/dynamics-component-constants';\nimport { VELOCITY_DAMPING_INDEX, VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\n\nimport {\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n} from './dynamics-commands';\nimport type { DynamicsWorld } from './dynamics-commands';\nimport { findDynamicsCommand } from './find-dynamics-command';\nimport { findDynamicsCommandOffset } from './find-dynamics-command-offset';\n\n// Semi-implicit Euler: acceleration changes this step's velocity before that velocity is turned into movement.\nexport function integrateDynamics(world: DynamicsWorld, entityId: number, velocity: Float32Array, dynamics?: Float32Array): void {\n\tconst commands = world.dynamicsCommands;\n\tconst flatCommands = commands instanceof Float64Array ? commands : undefined;\n\tconst recordCommands = commands && !(commands instanceof Float64Array) ? commands : undefined;\n\tconst command = findDynamicsCommand(recordCommands, entityId);\n\tconst commandOffset = flatCommands ? findDynamicsCommandOffset(flatCommands, entityId) : -1;\n\tconst damping = velocity[VELOCITY_DAMPING_INDEX];\n\tif(!dynamics && !command && commandOffset < 0 && damping === 0) {\n\t\treturn;\n\t}\n\n\tconst seconds = world.elapsedTime / 1000;\n\tconst inverseMass = dynamics?.[DYNAMICS_INVERSE_MASS_INDEX] ?? 1;\n\tconst forceX = commandOffset < 0 ? command?.forceX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_X_OFFSET];\n\tconst forceY = commandOffset < 0 ? command?.forceY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_FORCE_Y_OFFSET];\n\tconst impulseX = commandOffset < 0 ? command?.impulseX ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET];\n\tconst impulseY = commandOffset < 0 ? command?.impulseY ?? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET];\n\tconst velocityMask = commandOffset < 0 ? 0 : flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET];\n\tconst assignedVelocityX = commandOffset < 0\n\t\t? command?.velocityX\n\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_X_FLAG\n\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET]\n\t\t\t: undefined;\n\tconst assignedVelocityY = commandOffset < 0\n\t\t? command?.velocityY\n\t\t: velocityMask & DYNAMICS_COMMAND_VELOCITY_Y_FLAG\n\t\t\t? flatCommands![commandOffset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET]\n\t\t\t: undefined;\n\t// Implicit damping: 1/(1+damping*dt) shed off the integrated velocity, stable at any step and never reversing.\n\t// An explicit velocity assignment is authoritative for the step, so it bypasses damping until it coasts again.\n\tconst dampingFactor = damping > 0 ? 1 / (1 + damping * seconds) : 1;\n\tconst integratedVelocityX = (velocity[VELOCITY_X_INDEX]\n\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_X_INDEX] ?? 0) * seconds\n\t\t+ forceX * inverseMass * seconds\n\t\t+ impulseX * inverseMass) * dampingFactor;\n\tconst integratedVelocityY = (velocity[VELOCITY_Y_INDEX]\n\t\t+ (dynamics?.[DYNAMICS_ACCELERATION_Y_INDEX] ?? 0) * seconds\n\t\t+ forceY * inverseMass * seconds\n\t\t+ impulseY * inverseMass) * dampingFactor;\n\tvelocity[VELOCITY_X_INDEX] = assignedVelocityX ?? integratedVelocityX;\n\tvelocity[VELOCITY_Y_INDEX] = assignedVelocityY ?? integratedVelocityY;\n}\n\nexport default integrateDynamics;\n","import { bodyShape, isSensor } from '../components/body-component';\nimport { SHAPE_POLYGON, SHAPE_RECTANGLE } from '../components/body-component-constants';\nimport { BOUNCINESS_INDEX } from '../components/bounciness-component-constants';\nimport { TRANSFORM_ANGLE_INDEX, TRANSFORM_HEIGHT_INDEX, TRANSFORM_WIDTH_INDEX, TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\nimport { contactNormal } from '../math/shapes';\nimport { polygonContactNormal } from '../math/polygons';\nimport type { Vector } from '../math/shapes';\n\ninterface BounceEntity {\n\tcomponents: {\n\t\ttransform: Float32Array\n\t\tvelocity?: Float32Array\n\t\tbounciness?: Float32Array\n\t\tbody?: Uint32Array\n\t\tpolygon?: Float32Array\n\t}\n}\n\n// Scratch, reused rather than allocated: a run is one unbroken pass, never re-entered part way through.\nconst NORMAL: Vector = { x: 0, y: 0 };\n\n// Applies each side's configured velocity response. Bounciness takes precedence; with stopNonBouncing enabled,\n// a side without it instead loses only the velocity component pointing into the surface.\nexport function bouncePair(self: BounceEntity, other: BounceEntity, stopNonBouncing = false): void {\n\tif(!canRespond(self, stopNonBouncing) && !canRespond(other, stopNonBouncing)) {\n\t\treturn;\n\t}\n\n\tif(isSurface(self, other) && collisionNormal(self, other, NORMAL)) {\n\t\trespond(self, NORMAL.x, NORMAL.y, stopNonBouncing);\n\t\trespond(other, -NORMAL.x, -NORMAL.y, stopNonBouncing);\n\t}\n}\n\n// Turns `self` around off `other` and writes the new heading into its velocity block. `bounciness` is how much\n// of the speed into the surface comes back: 1 reflects perfectly, 0 cancels it (slide along the surface) - see\n// BouncinessComponent. Only `self` is touched.\nexport function bounce(self: BounceEntity, other: BounceEntity): void {\n\tif(!canBounce(self) || !isSurface(self, other)) {\n\t\treturn;\n\t}\n\n\tif(collisionNormal(self, other, NORMAL)) {\n\t\treflect(self, NORMAL.x, NORMAL.y);\n\t}\n}\n\n// Mirrors an entity's velocity about a surface it is leaving along `normal`, a unit vector pointing away from it.\nfunction reflect(entity: BounceEntity, normalX: number, normalY: number): void {\n\tconst bounciness = entity.components.bounciness;\n\tconst velocity = entity.components.velocity;\n\tif(!bounciness || !velocity) {\n\t\treturn;\n\t}\n\n\tconst velocityX = velocity[VELOCITY_X_INDEX];\n\tconst velocityY = velocity[VELOCITY_Y_INDEX];\n\n\t// How much of the velocity points into what it hit. Zero or more means it is already on its way out, and\n\t// reflecting again while the two still overlap would turn it straight back in.\n\tconst into = velocityX * normalX + velocityY * normalY;\n\tif(into >= 0) {\n\t\treturn;\n\t}\n\n\t// Reverse the normal component and scale by bounciness, leaving the surface component alone. Scale is 2 at\n\t// bounciness 1 (mirror reflection) and 1 at 0 (removes the normal component, sliding along the surface).\n\tconst scale = (1 + bounciness[BOUNCINESS_INDEX]) * into;\n\tvelocity[VELOCITY_X_INDEX] = velocityX - scale * normalX;\n\tvelocity[VELOCITY_Y_INDEX] = velocityY - scale * normalY;\n}\n\nfunction respond(entity: BounceEntity, normalX: number, normalY: number, stopNonBouncing: boolean): void {\n\tif(entity.components.bounciness) {\n\t\treflect(entity, normalX, normalY);\n\t\treturn;\n\t}\n\n\tconst velocity = entity.components.velocity;\n\tif(!stopNonBouncing || !velocity) {\n\t\treturn;\n\t}\n\n\tconst velocityX = velocity[VELOCITY_X_INDEX];\n\tconst velocityY = velocity[VELOCITY_Y_INDEX];\n\tconst into = velocityX * normalX + velocityY * normalY;\n\tif(into < 0) {\n\t\tvelocity[VELOCITY_X_INDEX] = velocityX - into * normalX;\n\t\tvelocity[VELOCITY_Y_INDEX] = velocityY - into * normalY;\n\t}\n}\n\nfunction canRespond(entity: BounceEntity, stopNonBouncing: boolean): boolean {\n\treturn entity.components.velocity !== undefined && (stopNonBouncing || entity.components.bounciness !== undefined);\n}\n\nfunction canBounce(entity: BounceEntity): boolean {\n\treturn entity.components.bounciness !== undefined && entity.components.velocity !== undefined;\n}\n\n// A sensor is felt only through onCollision, never as a surface, so neither side bounces. The pair is still\n// reported - this decides the bounce, not the collision.\nfunction isSurface(self: BounceEntity, other: BounceEntity): boolean {\n\treturn !isSensorBody(self.components.body) && !isSensorBody(other.components.body);\n}\n\n// Which way `self` is pushed off `other`, as a unit vector, or false when the two are exactly on top of each\n// other and there is no such direction. Taken off the shapes themselves, at the angle they are turned to, so a\n// contact that is not square to the world still reflects the speed that was closing on it.\nfunction collisionNormal(self: BounceEntity, other: BounceEntity, out: Vector): boolean {\n\tconst selfTransform = self.components.transform;\n\tconst otherTransform = other.components.transform;\n\n\tconst selfShape = shapeOf(self.components.body);\n\tconst otherShape = shapeOf(other.components.body);\n\tif(selfShape === SHAPE_POLYGON || otherShape === SHAPE_POLYGON) {\n\t\treturn polygonContactNormal(\n\t\t\tselfShape, self.components.polygon,\n\t\t\tselfTransform[TRANSFORM_X_INDEX], selfTransform[TRANSFORM_Y_INDEX],\n\t\t\tselfTransform[TRANSFORM_WIDTH_INDEX], selfTransform[TRANSFORM_HEIGHT_INDEX], selfTransform[TRANSFORM_ANGLE_INDEX],\n\t\t\totherShape, other.components.polygon,\n\t\t\totherTransform[TRANSFORM_X_INDEX], otherTransform[TRANSFORM_Y_INDEX],\n\t\t\totherTransform[TRANSFORM_WIDTH_INDEX], otherTransform[TRANSFORM_HEIGHT_INDEX], otherTransform[TRANSFORM_ANGLE_INDEX],\n\t\t\tout,\n\t\t);\n\t}\n\n\treturn contactNormal(\n\t\tselfShape,\n\t\tselfTransform[TRANSFORM_X_INDEX], selfTransform[TRANSFORM_Y_INDEX],\n\t\tselfTransform[TRANSFORM_WIDTH_INDEX], selfTransform[TRANSFORM_HEIGHT_INDEX], selfTransform[TRANSFORM_ANGLE_INDEX],\n\t\totherShape,\n\t\totherTransform[TRANSFORM_X_INDEX], otherTransform[TRANSFORM_Y_INDEX],\n\t\totherTransform[TRANSFORM_WIDTH_INDEX], otherTransform[TRANSFORM_HEIGHT_INDEX], otherTransform[TRANSFORM_ANGLE_INDEX],\n\t\tout,\n\t);\n}\n\n// A size-only entity with no body collides as a rectangle.\nfunction shapeOf(body: Uint32Array | undefined): number {\n\treturn body ? bodyShape(body) : SHAPE_RECTANGLE;\n}\n\n// A missing body is a solid, not a sensor.\nfunction isSensorBody(body: Uint32Array | undefined): boolean {\n\treturn body !== undefined && isSensor(body);\n}\n\nexport default bounce;\n","import {\n\tDYNAMICS_COMMAND_STRIDE,\n\tDYNAMICS_COMMAND_ENTITY_ID_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n\temptyCommand,\n} from './dynamics-commands';\nimport type { DynamicsCommand, DynamicsCommands } from './dynamics-commands';\n\n// Commands raised by the previous physics run happened before main-thread commands attached to this one.\n// Forces and impulses accumulate; a newer per-axis velocity assignment replaces an older one.\nexport function combineDynamicsCommands(\n\tqueued: ReadonlyMap<number, Omit<DynamicsCommand, 'entityId'>>,\n\tincoming: DynamicsCommands | undefined,\n): DynamicsCommands | undefined {\n\tif(queued.size === 0) {\n\t\treturn incoming;\n\t}\n\n\tconst combined = new Map<number, Omit<DynamicsCommand, 'entityId'>>();\n\tfor(const [entityId, command] of queued) {\n\t\tcombined.set(entityId, { ...command });\n\t}\n\tforEachDynamicsCommand(incoming, (entityId, command) => {\n\t\tconst current = combined.get(entityId) ?? emptyCommand();\n\t\tcombined.set(entityId, {\n\t\t\tforceX: current.forceX + command.forceX,\n\t\t\tforceY: current.forceY + command.forceY,\n\t\t\timpulseX: current.impulseX + command.impulseX,\n\t\t\timpulseY: current.impulseY + command.impulseY,\n\t\t\tvelocityX: command.velocityX ?? current.velocityX,\n\t\t\tvelocityY: command.velocityY ?? current.velocityY,\n\t\t});\n\t});\n\n\treturn Array.from(combined, ([entityId, command]) => ({ entityId, ...command }))\n\t\t.sort((left, right) => left.entityId - right.entityId);\n}\n\nfunction forEachDynamicsCommand(\n\tcommands: DynamicsCommands | undefined,\n\thandle: (entityId: number, command: Omit<DynamicsCommand, 'entityId'>) => void,\n): void {\n\tif(!commands) {\n\t\treturn;\n\t}\n\tif(!(commands instanceof Float64Array)) {\n\t\tfor(const { entityId, ...command } of commands) {\n\t\t\thandle(entityId, command);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tfor(let offset = 0; offset < commands.length; offset += DYNAMICS_COMMAND_STRIDE) {\n\t\tconst velocityMask = commands[offset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET];\n\t\thandle(commands[offset + DYNAMICS_COMMAND_ENTITY_ID_OFFSET], {\n\t\t\tforceX: commands[offset + DYNAMICS_COMMAND_FORCE_X_OFFSET],\n\t\t\tforceY: commands[offset + DYNAMICS_COMMAND_FORCE_Y_OFFSET],\n\t\t\timpulseX: commands[offset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET],\n\t\t\timpulseY: commands[offset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET],\n\t\t\tvelocityX: velocityMask & DYNAMICS_COMMAND_VELOCITY_X_FLAG\n\t\t\t\t? commands[offset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET]\n\t\t\t\t: undefined,\n\t\t\tvelocityY: velocityMask & DYNAMICS_COMMAND_VELOCITY_Y_FLAG\n\t\t\t\t? commands[offset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET]\n\t\t\t\t: undefined,\n\t\t});\n\t}\n}\n\n","import { emptyCommand } from './dynamics-commands';\nimport type { PendingDynamicsCommands } from './dynamics-commands';\n\nexport function queueDynamicsVector(\n\tcommands: PendingDynamicsCommands,\n\tentityId: number,\n\tforceX: number,\n\tforceY: number,\n\timpulseX: number,\n\timpulseY: number,\n): void {\n\tassertFiniteVector(forceX, forceY, 'Force');\n\tassertFiniteVector(impulseX, impulseY, 'Impulse');\n\tif(forceX === 0 && forceY === 0 && impulseX === 0 && impulseY === 0) {\n\t\treturn;\n\t}\n\n\tconst current = commands.get(entityId) ?? emptyCommand();\n\tconst next = {\n\t\t...current,\n\t\tforceX: current.forceX + forceX,\n\t\tforceY: current.forceY + forceY,\n\t\timpulseX: current.impulseX + impulseX,\n\t\timpulseY: current.impulseY + impulseY,\n\t};\n\tassertFiniteVector(next.forceX, next.forceY, 'Accumulated force');\n\tassertFiniteVector(next.impulseX, next.impulseY, 'Accumulated impulse');\n\tcommands.set(entityId, next);\n}\n\nfunction assertFiniteVector(x: number, y: number, name: string): void {\n\tif(!Number.isFinite(x) || !Number.isFinite(y)) {\n\t\tthrow new Error(`${name} must contain finite numbers`);\n\t}\n}\n\n","import { emptyCommand } from './dynamics-commands';\nimport type { PendingDynamicsCommands, VelocityAssignment } from './dynamics-commands';\n\nexport function queueVelocityAssignment(\n\tcommands: PendingDynamicsCommands,\n\tentityId: number,\n\tvelocity: VelocityAssignment,\n): void {\n\tassertFiniteAssignment(velocity);\n\tif(velocity.velocityX === undefined && velocity.velocityY === undefined) {\n\t\treturn;\n\t}\n\n\tcommands.set(entityId, { ...(commands.get(entityId) ?? emptyCommand()), ...velocity });\n}\n\nfunction assertFiniteAssignment(velocity: VelocityAssignment): void {\n\tif((velocity.velocityX !== undefined && !Number.isFinite(velocity.velocityX))\n\t\t|| (velocity.velocityY !== undefined && !Number.isFinite(velocity.velocityY))) {\n\t\tthrow new Error('Velocity assignment must contain finite numbers');\n\t}\n}\n\n","import { addAtomicFloat32 } from '@daneren2005/shared-memory-objects/utils/atomic-math';\nimport { loadFloat32, storeFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { DEAD_INDEX } from '@daneren2005/shared-memory-ecs';\nimport type { ComponentMap, EntityWorkerSystemCallbacks, EntityWorkerSystemWorld, EntityQueryComponents, EntityUpdateComponents, EntityUpdateFunction } from '@daneren2005/shared-memory-ecs';\nimport type { PhysicsComponents, PhysicsUpdateComponents } from '../components/registry';\nimport {\n\tINTERPOLATION_CHANNEL_INDEX,\n\tINTERPOLATION_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_TICK_INDEX,\n\tINTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_X_INDEX,\n\tINTERPOLATION_PREVIOUS_Y_INDEX,\n\tINTERPOLATION_TICK_INDEX,\n\tINTERPOLATION_TOTAL_DURATION_INDEX,\n} from '../components/interpolation-component-constants';\nimport { isDying, markDying } from '../components/body-component';\nimport { TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\nimport { VELOCITY_X_INDEX, VELOCITY_Y_INDEX } from '../components/velocity-component-constants';\nimport CollisionBroadphase, { type CollisionContact, type CollisionEntity, type CollisionFunction, type MovingEntity } from './collision';\nimport { COLLIDABLE_QUERY, POSITION_UPDATED_EVENT } from '../constants';\nimport type { Vector } from '../math/shapes';\nimport { bouncePair } from './bounce';\nimport { combineDynamicsCommands } from '../actions/combine-dynamics-commands';\nimport { integrateDynamics } from '../actions/integrate-dynamics';\nimport { queueDynamicsVector } from '../actions/queue-dynamics-vector';\nimport { queueVelocityAssignment } from '../actions/queue-velocity-assignment';\nimport type { DynamicsCommandQueue, DynamicsWorld, PendingDynamicsCommands } from '../actions/dynamics-commands';\nimport { spatialBounds, type SpatialBlockComponents } from './spatial-bounds';\nimport type { PhysicalSystemWorld } from '../world';\nimport { updateSpatialMap } from '../actions/update-spatial-map';\n\n// The per-run data object every physics update is handed: the base one plus the step counter the interpolation\n// component is stamped with. PhysicsSystem fills `tick` from addDataToWorld, so a subclass adding data must\n// call `super.addDataToWorld(world)` or nothing gets a tick and interpolation stops noticing new steps.\nexport interface PhysicsWorld extends EntityWorkerSystemWorld, PhysicalSystemWorld, DynamicsWorld {\n\t// Which physics step this is, bumped per run. Only compared for equality - a publication stamp, not a clock.\n\ttick: number\n\t// Stamped onto each publication so InterpolationSystem can hold it back until this system's run commits.\n\tinterpolationChannel?: number\n\t// Whether to report this run's moves. Set by PhysicsSystem from whether anything is listening, since the cost\n\t// is paid in the worker (an id per moved entity, cloned across the boundary). Undefined means report.\n\treportMoves?: boolean\n\t// For a grouped update (see `group` on createPhysicsUpdate)\n\tskipGroup?: number\n\t// Identifies one PhysicsSystem to a shared update function, keeping its callback commands isolated.\n\tcommandQueueId?: number\n\tdieAtImpact?(dying: DeathInterpolationEntity, other: DeathInterpolationEntity): void\n}\n\n// The world seen inside createPhysicsUpdate callbacks. Commands raised here are retained by the active backend\n// and integrated at the start of its next run, so callback behavior does not depend on entity update order.\nexport interface PhysicsCallbackWorld extends PhysicsWorld, DynamicsCommandQueue {}\n\n// What `dieAtImpact` reads off each side. Everything is optional and guarded at runtime so both a MovingEntity\n// (the mover, whose body is only optional in the type) and a CollisionEntity (the thing hit, whose velocity is)\n// can be handed to it either way round - which side carries the fatal category is the game's to sort out.\nexport interface DeathInterpolationEntity {\n\tentityId: number\n\tcomponents: {\n\t\ttransform?: Float32Array\n\t\tvelocity?: Float32Array\n\t\tbody?: Uint32Array\n\t}\n}\n\nconst CONTACT_NORMAL: Vector = { x: 0, y: 0 };\n\n// What a game asks createPhysicsUpdate for on top of plain movement.\nexport interface PhysicsUpdateOptions<\n\tC extends ComponentMap,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> {\n\t// Run for each entity that moved into another, straight after its own move. Optional: an update with no\n\t// callback still sweeps and comes to rest against what is in the way, it just decides nothing about it.\n\tonCollision?: CollisionFunction<C, T, W & PhysicsCallbackWorld>\n\t// Extra components to hand the update, and so the callback, beyond transform and velocity. List a health\n\t// component here to take damage on collision. Optional per entity: one without it is still moved and collided.\n\toptional?: Array<keyof C & string>\n\t// Splits the run into isolated groups by a numeric id read off a component block, so one system can simulate many independent worlds at once\n\tgroup?: PhysicsGroupConfig<C>\n\t// For solid contacts, remove the velocity component pointing into the surface from entities without a\n\t// bounciness component. Off by default to preserve the existing sweep-only response.\n\tstopVelocityOnContact?: boolean\n}\n\n// Which component block carries an entity's group id, and where in it. The block travels with both movers and\n// collidables (PhysicsSystem adds it to their optionals), so grouping holds on both sides of a collision.\nexport interface PhysicsGroupConfig<C extends ComponentMap> {\n\tcomponent: keyof C & string\n\tindex: number\n}\n\n// What PhysicsSystem needs to know about an update to set up its components and queries. Stamped on by\n// createPhysicsUpdate so a game declares its collision setup once, in the module both backends import.\nexport interface PhysicsUpdateMetadata<C extends ComponentMap> {\n\t// Whether the update needs the collidable query. Always true for createPhysicsUpdate, which always sweeps;\n\t// the bare physicsUpdate carries no metadata and never asks for it.\n\tcollision: boolean\n\t// The update integrates the optional dynamics block before movement.\n\tdynamics: boolean\n\toptional: Array<keyof C & string>\n\t// Set when the update groups its run; PhysicsSystem reads it to send the group block with movers and collidables.\n\tgroup?: PhysicsGroupConfig<C>\n}\n\nexport type PhysicsUpdateFunction<\n\tC extends ComponentMap,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> = EntityUpdateFunction<C, T, W> & {\n\tphysics: PhysicsUpdateMetadata<C>\n};\n\n// Builds the update a game runs physics through when it wants its entities to notice each other. The result is\n// a plain EntityUpdateFunction that goes to both backends: createEntitySystemWorker in the worker file, and\n// PhysicsSystem's `updateFunction` for the in-process fallback.\n//\n// export const gamePhysicsUpdate = createPhysicsUpdate<Components, GameUpdateComponents>({\n// optional: ['health'],\n// onCollision(world, a, b, queries, callbacks, contact) { ... },\n// });\n//\n// Every update built here sweeps, so an entity comes to rest against what is in its way. The callback is on top\n// and optional; movement that walks through everything is the bare physicsUpdate below. The callback is baked\n// in rather than passed to PhysicsSystem because functions do not survive postMessage to the worker.\nexport function createPhysicsUpdate<\n\t// Constrained to a map holding the physics components because `T` is the game's components narrowed to\n\t// transform and velocity; every game already spreads physicsRegistry into its own, as PhysicsSystem requires.\n\tC extends ComponentMap & PhysicsComponents = PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C> = PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n>(options: PhysicsUpdateOptions<C, T, W> = {}): PhysicsUpdateFunction<C, T, W> {\n\tconst { onCollision, optional = [], group, stopVelocityOnContact = false } = options;\n\n\t// Built by preRun, read by each entity update after it. Safe as closure variables because a run is one\n\t// unbroken pass - preRun, then every entity - never re-entered part way through. Ungrouped runs use the single\n\t// `broadphase`; a grouped run keeps one per group and each entity sweeps against its own.\n\tlet broadphase: CollisionBroadphase<T> | undefined;\n\tlet broadphaseByGroup: Map<number, CollisionBroadphase<T>> | undefined;\n\tconst callbackCommandsBySystem = new Map<number, PendingDynamicsCommands>();\n\n\tconst update: PhysicsUpdateFunction<C, T, W> = Object.assign(\n\t\t(world: W, entityId: number, components: T, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>) => {\n\t\t\t// Killed earlier this run\n\t\t\tif(components.entity?.[DEAD_INDEX] === 1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Struck something fatal on an earlier run and marked to die a step later, once its final segment onto the\n\t\t\t// impact point had a step to render (see dieAtImpact). That step is now, so this is where it actually dies -\n\t\t\t// before it moves or sweeps again, so it neither drifts on nor resolves a second contact.\n\t\t\tif(components.body !== undefined && isDying(components.body)) {\n\t\t\t\tcallbacks.entityDied(entityId);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// A grouped entity in the skipped group is left entirely alone - another system owns it this run.\n\t\t\tconst groupId = group ? readGroup<C>(components, group) : 0;\n\t\t\tif(group && world.skipGroup === groupId) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst self: MovingEntity<T> = { entityId, components };\n\t\t\tconst interpolation = components.interpolation;\n\t\t\tconst seconds = world.elapsedTime / 1000;\n\t\t\tintegrateDynamics(world, entityId, components.velocity, components.dynamics);\n\t\t\tconst moveX = components.velocity[VELOCITY_X_INDEX] * seconds;\n\t\t\tconst moveY = components.velocity[VELOCITY_Y_INDEX] * seconds;\n\n\t\t\t// Taken before anything is written, so the segment a renderer blends along runs from this step's start\n\t\t\t// to its end.\n\t\t\tstartInterpolationStep(interpolation, components.transform, world);\n\n\t\t\t// No tree means either this update was called by hand without preRun, or a grouped entity whose group\n\t\t\t// holds nothing collidable: nothing to sweep against, plain move. Bound locally so it stays narrowed\n\t\t\t// inside the overlap callback below.\n\t\t\tconst tree = group ? broadphaseByGroup?.get(groupId) : broadphase;\n\t\t\tif(!tree) {\n\t\t\t\tmove(entityId, components.transform, moveX, moveY, callbacks, world.reportMoves);\n\t\t\t\tupdateSpatialMap(world, entityId, components);\n\t\t\t\tfinishInterpolationStep(interpolation, world.tick);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Native bounce turns an entity around on contact without a game callback; the bounciness component\n\t\t\t// decides whether and how much. Read before the move because it also decides how the move resolves.\n\t\t\tconst bouncing = components.bounciness !== undefined;\n\n\t\t\t// Resolved off the blocks and only written once known, so an entity is never inside another. The move\n\t\t\t// resolves with or without a callback - coming to rest is what physics does about a collision,\n\t\t\t// onCollision is what the game does. A non-bouncing entity slides along a single axis off a corner; a\n\t\t\t// bouncing one does not, since it is about to turn around off the face it hit.\n\t\t\tconst moved = tree.resolveMove(self, moveX, moveY, !bouncing);\n\t\t\tmove(entityId, components.transform, moved.moveX, moved.moveY, callbacks, world.reportMoves);\n\t\t\tupdateSpatialMap(world, entityId, components);\n\t\t\t// Before any callback, so the stamp covers exactly the pair physics produced. A callback that writes\n\t\t\t// the transform is a game move, blended towards next frame like a teleport.\n\t\t\tfinishInterpolationStep(interpolation, world.tick);\n\n\t\t\t// A non-bouncing entity still has to look, since what it ran into may bounce off it.\n\t\t\tif(!tree.hasBounciness && !stopVelocityOnContact && !onCollision) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Handled after the entity is put down, so both bounce and callback act on where it ended up; the\n\t\t\t// flipped velocity is applied by the next run. A contact is resolved once per run, by whichever side\n\t\t\t// reached it first - claimContact is what makes the second side's turn a no-op.\n\t\t\ttree.forEachOverlapping(self, other => {\n\t\t\t\tif(tree.claimContact(entityId, other.entityId)) {\n\t\t\t\t\tresolveContact(world as W & PhysicsCallbackWorld, tree, self, other, queries, callbacks, moved.moveX, moved.moveY, stopVelocityOnContact, onCollision);\n\t\t\t\t}\n\t\t\t}, moved.moveX, moved.moveY);\n\n\t\t\t// Then what the move was stopped against, which the overlap check cannot find: the entity rests\n\t\t\t// touching it, not through it. The two lists never share an entry.\n\t\t\tfor(const other of moved.blocking) {\n\t\t\t\tif(tree.claimContact(entityId, other.entityId)) {\n\t\t\t\t\tresolveContact(world as W & PhysicsCallbackWorld, tree, self, other, queries, callbacks, moved.moveX, moved.moveY, stopVelocityOnContact, onCollision);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\tphysics: {\n\t\t\t\t// The sweep needs the collidable query even with no callback, so this is not `onCollision !== undefined`.\n\t\t\t\tcollision: true,\n\t\t\t\tdynamics: true,\n\t\t\t\toptional,\n\t\t\t\tgroup,\n\t\t\t},\n\t\t},\n\t);\n\n\t// preRun sees every entity at once, so it is the only place the tree can be built from one consistent moment,\n\t// before any of this run's movement. Ungrouped builds one tree over everything; grouped buckets the collidables\n\t// by group id and builds a tree per group, so an entity only ever sweeps against its own group - the skipped\n\t// group gets none, since nothing in it will be stepped.\n\tupdate.preRun = (world, entities, queries) => {\n\t\tprepareCallbackCommands(world, callbackCommandsBySystem);\n\t\t// Bound to the tree it strikes against and hung on the world so a collision callback can reach it. The tree\n\t\t// is picked when it is called, not now, since the callback fires after the trees below are built; a dying\n\t\t// entity and the thing it hit are always in the same group, so the dying side's group picks the right one.\n\t\tworld.dieAtImpact = (dying, other) => {\n\t\t\tconst tree = group ? broadphaseByGroup?.get(readGroup<C>(dying.components, group)) : broadphase;\n\t\t\tapplyDeathInterpolation<T>(world, tree, dying, other);\n\t\t};\n\n\t\tconst seconds = world.elapsedTime / 1000;\n\t\tconst collidables = queries[COLLIDABLE_QUERY] ?? [];\n\t\tif(!group) {\n\t\t\tbroadphase = new CollisionBroadphase<T>(collidables, seconds, world.dynamicsCommands);\n\t\t\treturn;\n\t\t}\n\n\t\tconst byGroup = new Map<number, typeof collidables>();\n\t\tfor(const entity of collidables) {\n\t\t\tconst groupId = readGroup<C>(entity.components, group);\n\t\t\tif(world.skipGroup === groupId) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet list = byGroup.get(groupId);\n\t\t\tif(!list) {\n\t\t\t\tlist = [];\n\t\t\t\tbyGroup.set(groupId, list);\n\t\t\t}\n\t\t\tlist.push(entity);\n\t\t}\n\n\t\tbroadphaseByGroup = new Map();\n\t\tfor(const [groupId, list] of byGroup) {\n\t\t\tbroadphaseByGroup.set(groupId, new CollisionBroadphase<T>(list, seconds, world.dynamicsCommands));\n\t\t}\n\t};\n\n\treturn update;\n}\n\nfunction prepareCallbackCommands(\n\tworld: PhysicsWorld,\n\tcommandsBySystem: Map<number, PendingDynamicsCommands>,\n): void {\n\tconst commandWorld = world as PhysicsWorld & Partial<DynamicsCommandQueue>;\n\tconst commandQueueId = world.commandQueueId ?? 0;\n\tconst deferred = commandsBySystem.get(commandQueueId);\n\tif(deferred) {\n\t\tworld.dynamicsCommands = combineDynamicsCommands(deferred, world.dynamicsCommands);\n\t\tcommandsBySystem.delete(commandQueueId);\n\t}\n\tconst commands = (): PendingDynamicsCommands => {\n\t\tlet pending = commandsBySystem.get(commandQueueId);\n\t\tif(!pending) {\n\t\t\tpending = new Map();\n\t\t\tcommandsBySystem.set(commandQueueId, pending);\n\t\t}\n\n\t\treturn pending;\n\t};\n\tcommandWorld.queueForce = (entityId, forceX, forceY) => {\n\t\tqueueDynamicsVector(commands(), entityId, forceX, forceY, 0, 0);\n\t};\n\tcommandWorld.queueImpulse = (entityId, impulseX, impulseY) => {\n\t\tqueueDynamicsVector(commands(), entityId, 0, 0, impulseX, impulseY);\n\t};\n\tcommandWorld.queueVelocity = (entityId, velocity) => {\n\t\tqueueVelocityAssignment(commands(), entityId, velocity);\n\t};\n}\n\n// Reads an entity's group id off the configured block, defaulting to group 0 when the entity lacks it.\nfunction readGroup<C extends ComponentMap>(components: EntityUpdateComponents<C>, group: PhysicsGroupConfig<C>): number {\n\tconst block = components[group.component];\n\n\treturn block ? block[group.index] : 0;\n}\n\n// Everything a contact means, applied once for the pair: both sides turned around by their own bounciness, then\n// the one callback. `self` is whichever side reached the contact first, so a game acting on only one of the two\n// would leave the other untouched half the time - the callback has to decide for both.\nfunction resolveContact<\n\tC extends ComponentMap,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsCallbackWorld,\n>(\n\tworld: W,\n\ttree: CollisionBroadphase<T>,\n\tself: MovingEntity<T>,\n\tother: CollisionEntity<T>,\n\tqueries: EntityQueryComponents<C>,\n\tcallbacks: EntityWorkerSystemCallbacks<C>,\n\tmoveX: number,\n\tmoveY: number,\n\tstopVelocityOnContact: boolean,\n\tonCollision?: CollisionFunction<C, T, W>,\n): void {\n\tbouncePair(self, other, stopVelocityOnContact);\n\tif(!onCollision) {\n\t\treturn;\n\t}\n\n\tconst selfBounds = spatialBounds(self.components);\n\tconst otherBounds = spatialBounds(other.components);\n\tCONTACT_NORMAL.x = 0;\n\tCONTACT_NORMAL.y = 0;\n\ttree.contactNormal(self, other, moveX, moveY, CONTACT_NORMAL);\n\tconst contact: CollisionContact = { normalX: CONTACT_NORMAL.x, normalY: CONTACT_NORMAL.y };\n\tonCollision(world, self, other, queries, callbacks, contact);\n\tupdateSpatialMapIfBoundsChanged(world, self.entityId, self.components, selfBounds);\n\tupdateSpatialMapIfBoundsChanged(world, other.entityId, other.components, otherBounds);\n}\n\nfunction updateSpatialMapIfBoundsChanged(\n\tworld: PhysicalSystemWorld,\n\tentityId: number,\n\tcomponents: SpatialBlockComponents,\n\tbefore: ReturnType<typeof spatialBounds>,\n): void {\n\tconst after = spatialBounds(components);\n\tif(after.minX !== before.minX || after.minY !== before.minY || after.maxX !== before.maxX || after.maxY !== before.maxY) {\n\t\tupdateSpatialMap(world, entityId, components);\n\t}\n}\n\n// Backs `dieAtImpact`. Rather than remove `dying` on the spot - where a fast continuous mover has already stepped\n// a stride past what it hit, and its render, a step behind, has it short of the target - this ends its current\n// interpolation segment on the exact point it struck `other` and marks it to die one run later. That extra run is\n// the step the render needs to play the segment out, so the entity is drawn reaching the impact and only then\n// vanishes. Position only: the game still raises whatever hit/score event it wants, on the spot. A no-op tail for\n// an entity missing the blocks or that did not move leaves it marked dying, so it is still cleaned up next run.\nfunction applyDeathInterpolation<T extends PhysicsUpdateComponents>(\n\tworld: PhysicsWorld,\n\ttree: CollisionBroadphase<T> | undefined,\n\tdying: DeathInterpolationEntity,\n\tother: DeathInterpolationEntity,\n): void {\n\tconst body = dying.components.body;\n\t// Needs a body to carry the flag; already dying means a second contact this run, which is nothing to redo.\n\tif(!body || isDying(body)) {\n\t\treturn;\n\t}\n\tmarkDying(body);\n\n\tconst transform = dying.components.transform;\n\tconst velocity = dying.components.velocity;\n\tconst otherTransform = other.components.transform;\n\tconst otherBody = other.components.body;\n\tif(!tree || !transform || !velocity || !otherTransform || !otherBody) {\n\t\treturn;\n\t}\n\n\tconst seconds = world.elapsedTime / 1000;\n\tconst moveX = velocity[VELOCITY_X_INDEX] * seconds;\n\tconst moveY = velocity[VELOCITY_Y_INDEX] * seconds;\n\tif(moveX === 0 && moveY === 0) {\n\t\treturn;\n\t}\n\n\tconst contact = tree.contactPoint(\n\t\t{ entityId: dying.entityId, components: { transform, body } },\n\t\t{ components: { transform: otherTransform, body: otherBody } },\n\t\tmoveX,\n\t\tmoveY,\n\t);\n\t// Retarget this run's segment: `prev` is still the step's start, so moving the transform back to the impact\n\t// point makes the render play start -> impact instead of start -> overshoot. The tick was already stamped for\n\t// this run and the interpolation reads the transform live, so the shortened end is picked up with no reseeding.\n\ttransform[TRANSFORM_X_INDEX] = contact.x;\n\ttransform[TRANSFORM_Y_INDEX] = contact.y;\n}\n\n// Movement only, and the only way to get movement with no collision detection: walks an entity straight through\n// anything in its way, for something that has no business noticing the world (a particle, a camera). For\n// entities stopped by each other, build the update with createPhysicsUpdate above, which sweeps around this move.\nexport function physicsUpdate<\n\tC extends ComponentMap & PhysicsComponents = PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C> = PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n>(world: W, entityId: number, components: T, queries: EntityQueryComponents<C>, callbacks: EntityWorkerSystemCallbacks<C>): void {\n\t// elapsedTime is ms, velocity is units per second.\n\tconst seconds = world.elapsedTime / 1000;\n\tconst interpolation = components.interpolation;\n\tintegrateDynamics(world, entityId, components.velocity, components.dynamics);\n\n\tstartInterpolationStep(interpolation, components.transform, world);\n\tmove(\n\t\tentityId,\n\t\tcomponents.transform,\n\t\tcomponents.velocity[VELOCITY_X_INDEX] * seconds,\n\t\tcomponents.velocity[VELOCITY_Y_INDEX] * seconds,\n\t\tcallbacks,\n\t\tworld.reportMoves,\n\t);\n\tupdateSpatialMap(world, entityId, components);\n\tfinishInterpolationStep(interpolation, world.tick);\n}\n\n// Preserve the current publication in the previous slot, then publish simulated time under the dirty marker\n// before moving the transform. The previous slot is complete before the current tick reads NaN, so a reader that\n// finds the current one torn or uncommitted always has a coherent step to fall back on.\nfunction startInterpolationStep(interpolation: Float32Array | undefined, transform: Float32Array, world: PhysicsWorld): void {\n\tif(!interpolation) {\n\t\treturn;\n\t}\n\n\tstoreFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX, Number.NaN);\n\tinterpolation[INTERPOLATION_PREVIOUS_X_INDEX] = transform[TRANSFORM_X_INDEX];\n\tinterpolation[INTERPOLATION_PREVIOUS_Y_INDEX] = transform[TRANSFORM_Y_INDEX];\n\tinterpolation[INTERPOLATION_PREVIOUS_DURATION_INDEX] = interpolation[INTERPOLATION_DURATION_INDEX];\n\tinterpolation[INTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX] = interpolation[INTERPOLATION_TOTAL_DURATION_INDEX];\n\tstoreFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX, loadFloat32(interpolation, INTERPOLATION_TICK_INDEX));\n\n\t// Invalidate readers before changing timing or position.\n\tstoreFloat32(interpolation, INTERPOLATION_TICK_INDEX, Number.NaN);\n\tinterpolation[INTERPOLATION_CHANNEL_INDEX] = world.interpolationChannel ?? 0;\n\tinterpolation[INTERPOLATION_DURATION_INDEX] = world.elapsedTime;\n\tinterpolation[INTERPOLATION_TOTAL_DURATION_INDEX] += world.elapsedTime;\n}\n\n// Stationary entities also publish time; release the matching transform and timing together.\nfunction finishInterpolationStep(interpolation: Float32Array | undefined, tick: number): void {\n\tif(!interpolation) {\n\t\treturn;\n\t}\n\n\tstoreFloat32(interpolation, INTERPOLATION_TICK_INDEX, tick);\n}\n\n// Applies an already-decided move and reports where the entity ended up. The one place the position is written,\n// reached only once the final position is known. Applied with addAtomicFloat32 rather than `+=` because another\n// thread may be adding to the same shared position at once and a plain read-modify-write would drop one; the\n// `max` cap is unwanted, so Infinity leaves a plain atomic add. A move that went nowhere is not reported, which\n// keeps a world of still entities quiet. See PhysicsSystem for `reportMoves`.\nfunction move(entityId: number, transform: Float32Array, moveX: number, moveY: number, callbacks: PositionCallbacks, reportMoves?: boolean): void {\n\tif(moveX === 0 && moveY === 0) {\n\t\treturn;\n\t}\n\n\tif(moveX !== 0) {\n\t\taddAtomicFloat32(transform, TRANSFORM_X_INDEX, moveX, Infinity);\n\t}\n\tif(moveY !== 0) {\n\t\taddAtomicFloat32(transform, TRANSFORM_Y_INDEX, moveY, Infinity);\n\t}\n\n\tif(reportMoves === false) {\n\t\treturn;\n\t}\n\n\t// Just the id: the main thread reads the position off the shared block, so no copy is worth sending.\n\tcallbacks.emitSystemEvent(POSITION_UPDATED_EVENT, entityId);\n}\n\n// The one callback move reaches for, narrowed to the event physics reports.\ninterface PositionCallbacks {\n\temitSystemEvent(event: typeof POSITION_UPDATED_EVENT, entityId: number): void\n}\n\nexport default physicsUpdate;\n","// Main-thread registry of each world's PhysicsSystems, so an InterpolationSystem can snapshot every run's commit\n// once per frame. A worker publishes entity by entity while the main thread renders, so without one snapshot some\n// entities of a run would be drawn at its new step and others at the old one.\nexport interface InterpolationCommitSource {\n\t// The last tick whose run has finished publishing every entity.\n\treadonly committedTick: number\n}\n\nconst channelsByWorld = new WeakMap<object, Array<InterpolationCommitSource | undefined>>();\n\n// Channels start at 1 so a zeroed block reads as \"no channel\" and stays ungated.\nexport function registerInterpolationChannel(world: object, source: InterpolationCommitSource): number {\n\tlet channels = channelsByWorld.get(world);\n\tif(!channels) {\n\t\tchannels = [undefined];\n\t\tchannelsByWorld.set(world, channels);\n\t}\n\tchannels.push(source);\n\n\treturn channels.length - 1;\n}\n\nexport function unregisterInterpolationChannel(world: object, channel: number): void {\n\tconst channels = channelsByWorld.get(world);\n\tif(channels && channel > 0 && channel < channels.length) {\n\t\tchannels[channel] = undefined;\n\t}\n}\n\n// Indexed by channel. An unregistered slot is NaN, which leaves its entities ungated.\nexport function snapshotCommittedTicks(world: object): Array<number> | undefined {\n\tconst channels = channelsByWorld.get(world);\n\tif(!channels) {\n\t\treturn undefined;\n\t}\n\n\treturn channels.map(source => source ? source.committedTick : Number.NaN);\n}\n","import { EntityWorkerSystem } from '@daneren2005/shared-memory-ecs';\nimport type { BaseEntity, BaseWorld, ComponentDefinitionMap, ComponentMap, EntityWorkerSystemQuery, EntityUpdateComponents, EntityUpdateFunction, SystemConfig } from '@daneren2005/shared-memory-ecs';\nimport { startSpawnInterpolation, type SpawnableEntity } from '../actions/start-spawn-interpolation';\nimport type { PhysicsComponents, PhysicsUpdateComponents } from '../components/registry';\nimport { physicsUpdate, type PhysicsUpdateMetadata, type PhysicsWorld } from './physics-update';\nimport { COLLIDABLE_QUERY, DEFAULT_PHYSICS_STEP_MS, POSITION_UPDATED_EVENT } from '../constants';\nimport type { PhysicalWorldSource } from '../world';\nimport { addPhysicalWorldData } from '../actions/add-physical-world-data';\nimport { registerInterpolationChannel, unregisterInterpolationChannel } from './interpolation-channels';\nimport {\n\tDYNAMICS_COMMAND_ENTITY_ID_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_X_OFFSET,\n\tDYNAMICS_COMMAND_FORCE_Y_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_X_OFFSET,\n\tDYNAMICS_COMMAND_IMPULSE_Y_OFFSET,\n\tDYNAMICS_COMMAND_STRIDE,\n\tDYNAMICS_COMMAND_VELOCITY_MASK_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_X_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_X_OFFSET,\n\tDYNAMICS_COMMAND_VELOCITY_Y_FLAG,\n\tDYNAMICS_COMMAND_VELOCITY_Y_OFFSET,\n} from '../actions/dynamics-commands';\nimport type { DynamicsCommandBuffer, PendingDynamicsCommands, VelocityAssignment } from '../actions/dynamics-commands';\nimport { queueDynamicsVector } from '../actions/queue-dynamics-vector';\nimport { queueVelocityAssignment } from '../actions/queue-velocity-assignment';\n\nlet nextCommandQueueId = 1;\n\nexport type { VelocityAssignment } from '../actions/dynamics-commands';\n\nexport interface PhysicsSystemConfig<\n\tC extends ComponentMap & PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> extends Partial<SystemConfig> {\n\t// Worker entry point that runs the update off the main thread. Bundlers need the `new Worker(new URL(...))`\n\t// call written out in the game's own source, so the game supplies the file:\n\t//\n\t// getWorker: () => new Worker(new URL('./physics.worker.ts', import.meta.url), { type: 'module' })\n\t//\n\t// Left off, the simulation runs in-process on the main thread instead.\n\tgetWorker?: () => Worker\n\t// Forces the in-process worker even with a getWorker. Defaults to true when no getWorker was given.\n\tforceMainThread?: boolean\n\t// The update to run, defaulting to plain movement that walks through anything in its way. Build one with\n\t// createPhysicsUpdate for collisions and pass the same function the worker file hands to createEntitySystemWorker\n\t// so both backends match; it stamps `optional` and the collidable query on, so they need not be repeated here.\n\tupdateFunction?: EntityUpdateFunction<C, T, W> & { physics?: PhysicsUpdateMetadata<C> }\n\t// Extra components to send with each moving entity, for an update built by hand. Overrides what the update\n\t// function declared.\n\toptional?: Array<keyof C & string>\n\t// Whether to gather the collidable query for the update's collision detection. Defaults to what the update\n\t// declared: on for createPhysicsUpdate, off for the bare physicsUpdate.\n\tcollision?: boolean\n\t// Whether each run reports what moved through POSITION_UPDATED_EVENT. Defaults to whether anything is\n\t// listening, checked per run. Worth a setting because the worker pays for it - an id per moved entity, cloned\n\t// back across the boundary - so force it `true` only for a listener this system cannot see.\n\treportMoves?: boolean\n\t// Narrows which entities this system moves to a subset, for spreading one simulation across several workers:\n\t// give each the same update but a shard filter (`entity => entity.eid % workers === n`). The collidable query\n\t// and any extra `queries` are gathered whole regardless, so every shard sweeps against the whole world.\n\tfilter?: (entity: BaseEntity<C>) => boolean\n\t// Restricts this system to one group of the world - a solar system, a level - so it never sees, moves, or\n\t// collides against anything outside it. Unlike `filter` (which shards movers but still sweeps the whole world),\n\t// `scope` narrows the collidable set too, so an out-of-scope body never enters this system's broadphase and two\n\t// overlapping groups cannot collide across the boundary. Give each group its own PhysicsSystem with its own\n\t// `scope` (`entity => entity.components.group?.groupId === thisId`). Combined with `filter` an entity must pass\n\t// both to be moved. Extra `queries` are left whole - scope them in the caller if a group-local read is wanted.\n\tscope?: (entity: BaseEntity<C>) => boolean\n\t// Extra queries sent to the worker alongside the collidable one, for an update that reads more of the world\n\t// than the entity it moves (e.g. flocking). Read by name off `queries`, gathered whole regardless of `filter`,\n\t// and merged with the collidable query rather than replacing it.\n\tqueries?: { [key: string]: EntityWorkerSystemQuery<C> }\n\t// For a grouped update (see `group` on createPhysicsUpdate)\n\tskipGroup?: number\n}\n\n// Moves every entity with a transform and a velocity, and (when the update detects collisions) reports overlaps\n// to the game's callback. Generic over `C` the game's component map, `T` the blocks its update touches, and `W`\n// the per-run data object.\nexport default class PhysicsSystem<\n\tC extends ComponentMap & PhysicsComponents,\n\tT extends PhysicsUpdateComponents & EntityUpdateComponents<C> = PhysicsUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PhysicsWorld = PhysicsWorld,\n> extends EntityWorkerSystem<C, T, W> {\n\tprivate spatialWorld: PhysicalWorldSource;\n\tprivate pendingDynamicsCommands: PendingDynamicsCommands = new Map();\n\tprivate readonly commandQueueId = nextCommandQueueId++;\n\t// Bumped per run and stamped onto every interpolated entity so a renderer can tell a blended position from one\n\t// physics just replaced. Only has to change; a Float32 holds integers to 2^24, over nine days at a 50ms step.\n\ttick = 0;\n\tprivate readonly interpolationChannel: number;\n\n\t// Undefined means \"whenever something is listening\", worked out per run by addDataToWorld.\n\treportMoves: boolean | undefined;\n\n\t// The group left untouched each run, sent to the worker per run via addDataToWorld\n\tskipGroup: number | undefined;\n\n\tconstructor(world: BaseWorld<ComponentDefinitionMap, C> & PhysicalWorldSource, options: PhysicsSystemConfig<C, T, W> = {}) {\n\t\tconst updateFunction: EntityUpdateFunction<C, T, W> & { physics?: PhysicsUpdateMetadata<C> } = options.updateFunction ?? physicsUpdate;\n\t\tconst optional = options.optional ?? updateFunction.physics?.optional ?? [];\n\t\tconst collision = options.collision ?? updateFunction.physics?.collision ?? false;\n\t\tconst integratesDynamics = updateFunction === physicsUpdate || updateFunction.physics?.dynamics === true;\n\t\t// A moving entity reads its own body (collide category/mask) and bounciness before it searches, so both\n\t\t// travel with movers too - but only on the collision path, or they are blocks per mover for nothing.\n\t\t// Interpolation goes along unconditionally, since the update must publish into it and nothing flags which\n\t\t// entities have one. All are free for entities that lack them: the ECS only sends a block it actually holds.\n\t\tconst group = updateFunction.physics?.group;\n\n\t\tconst extraOptional: Array<keyof C & string> = [];\n\t\tif(collision && !optional.includes('body')) {\n\t\t\textraOptional.push('body');\n\t\t}\n\t\tif(collision && !optional.includes('bounciness')) {\n\t\t\textraOptional.push('bounciness');\n\t\t}\n\t\tif(collision && !optional.includes('polygon')) {\n\t\t\textraOptional.push('polygon');\n\t\t}\n\t\tif(!optional.includes('interpolation')) {\n\t\t\textraOptional.push('interpolation');\n\t\t}\n\t\tif(integratesDynamics && !optional.includes('dynamics')) {\n\t\t\textraOptional.push('dynamics');\n\t\t}\n\t\tif(collision) {\n\t\t\textraOptional.push('entity');\n\t\t}\n\t\t// The group block travels with every mover so the update can read its group id and honour skipGroup even\n\t\t// where nothing collides.\n\t\tif(group && !optional.includes(group.component)) {\n\t\t\textraOptional.push(group.component);\n\t\t}\n\t\tconst movingOptional = extraOptional.length ? [...extraOptional, ...optional] : optional;\n\n\t\t// Collision is between anything with a transform and a body, not only movers, so a ship can hit a station.\n\t\t// Gathered only when something will read it, since it ships a block per entity in the world. Merged with\n\t\t// the game's own `queries`, and neither is narrowed by the shard `filter`.\n\t\t// `bounciness` has to be on this side too, not just the movers: a contact is resolved once and turns both\n\t\t// sides around by their own bounciness, and the broadphase decides from these blocks whether anything in the\n\t\t// run can bounce at all. Left off, hasBounciness is never true and nothing ever bounces.\n\t\tconst collidableOptional = optional.includes('bounciness') ? [...optional] : ['bounciness', ...optional];\n\t\tif(!collidableOptional.includes('polygon')) {\n\t\t\tcollidableOptional.push('polygon');\n\t\t}\n\t\tif(integratesDynamics && !collidableOptional.includes('dynamics')) {\n\t\t\tcollidableOptional.push('dynamics');\n\t\t}\n\t\t// The group block on the collidable side too, so preRun can bucket the broadphase by group.\n\t\tif(group && !collidableOptional.includes(group.component)) {\n\t\t\tcollidableOptional.push(group.component);\n\t\t}\n\t\tconst collidableQuery = collision ? {\n\t\t\t[COLLIDABLE_QUERY]: {\n\t\t\t\trequired: ['transform', 'body'] as Array<keyof C>,\n\t\t\t\toptional: ['velocity', 'entity', ...collidableOptional] as Array<keyof C>,\n\t\t\t\t// Scope narrows the collidable set as well as the movers, so an out-of-scope body never enters the\n\t\t\t\t// broadphase; the shard `filter` deliberately does not, since every shard sweeps the whole group.\n\t\t\t\tfilter: options.scope,\n\t\t\t},\n\t\t} : undefined;\n\t\tconst queries = collidableQuery || options.queries ? { ...collidableQuery, ...options.queries } : undefined;\n\n\t\t// A moving entity must be in scope and in this shard: scope bounds the group, filter shards it across workers.\n\t\tconst moverFilter = combineFilters(options.scope, options.filter);\n\n\t\tsuper(world, {\n\t\t\tname: options.name ?? 'PhysicsSystem',\n\t\t\tdeltaBetweenRuns: options.deltaBetweenRuns ?? DEFAULT_PHYSICS_STEP_MS,\n\t\t\tfirstRun: options.firstRun,\n\n\t\t\t// Movement needs both; an entity with only one is not moved, though it can still be collided with.\n\t\t\trequired: ['transform', 'velocity'],\n\t\t\toptional: movingOptional,\n\t\t\tfilter: moverFilter,\n\t\t\tupdateFunction,\n\t\t\tqueries,\n\n\t\t\tforceMainThread: options.forceMainThread ?? !options.getWorker,\n\t\t\t// Only reached when a getWorker was supplied: without one forceMainThread defaults to true.\n\t\t\tgetWorker: options.getWorker ?? (() => {\n\t\t\t\tthrow new Error('PhysicsSystem cannot start a worker: no getWorker was supplied');\n\t\t\t}),\n\t\t});\n\n\t\tthis.spatialWorld = world;\n\t\tthis.reportMoves = options.reportMoves;\n\t\tthis.skipGroup = options.skipGroup;\n\t\tthis.interpolationChannel = registerInterpolationChannel(world, this);\n\t}\n\n\t// A worker run is still publishing until its reply lands, so its tick is not committed until then.\n\tget committedTick(): number {\n\t\treturn this.isCurrentlyRunning() ? this.tick - 1 : this.tick;\n\t}\n\n\tdestroy(): void {\n\t\tunregisterInterpolationChannel(this.world, this.interpolationChannel);\n\t\tsuper.destroy();\n\t}\n\n\t// Seeds a just-spawned, already-moving entity's interpolation so a renderer draws it leaving its spawn point\n\t// from the next frame rather than parked there until this system's first step reaches it.\n\tstartInterpolation(entity: SpawnableEntity): void {\n\t\tconst step = this.deltaBetweenRuns;\n\t\tif(step <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tstartSpawnInterpolation(entity, {\n\t\t\tstepMs: step,\n\t\t\tstepFraction: this.currentDelta / step,\n\t\t\ttick: this.tick,\n\t\t});\n\t}\n\n\tqueueForce(entity: BaseEntity<C> | number, forceX: number, forceY: number): void {\n\t\tthis.queueDynamics(entity, forceX, forceY, 0, 0);\n\t}\n\n\tqueueImpulse(entity: BaseEntity<C> | number, impulseX: number, impulseY: number): void {\n\t\tthis.queueDynamics(entity, 0, 0, impulseX, impulseY);\n\t}\n\n\tqueueVelocity(entity: BaseEntity<C> | number, velocity: VelocityAssignment): void {\n\t\tconst entityId = typeof entity === 'number' ? entity : entity.eid;\n\t\tqueueVelocityAssignment(this.pendingDynamicsCommands, entityId, velocity);\n\t}\n\n\t// Stamps the run with its step number and whether to report what moved. A subclass overriding this to attach\n\t// its own data must call `super.addDataToWorld(world)`, or `tick` stays undefined and every interpolated\n\t// entity looks to a renderer like it has never had a physics step.\n\taddDataToWorld(world: W): void {\n\t\tworld.tick = ++this.tick;\n\t\tworld.interpolationChannel = this.interpolationChannel;\n\t\taddPhysicalWorldData(this.spatialWorld, world);\n\t\t// Asked per run so a game can attach and drop the listener freely.\n\t\tworld.reportMoves = this.reportMoves ?? this.listenerCount(POSITION_UPDATED_EVENT) > 0;\n\t\t// Sent every run so a game can flip which group is handed off (a jump) between runs with no rebuild.\n\t\tworld.skipGroup = this.skipGroup;\n\t\tworld.commandQueueId = this.commandQueueId;\n\t\tworld.dynamicsCommands = this.snapshotDynamicsCommands();\n\t}\n\n\tprivate queueDynamics(\n\t\tentity: BaseEntity<C> | number,\n\t\tforceX: number,\n\t\tforceY: number,\n\t\timpulseX: number,\n\t\timpulseY: number,\n\t): void {\n\t\tconst entityId = typeof entity === 'number' ? entity : entity.eid;\n\t\tqueueDynamicsVector(this.pendingDynamicsCommands, entityId, forceX, forceY, impulseX, impulseY);\n\t}\n\n\tprivate snapshotDynamicsCommands(): DynamicsCommandBuffer | undefined {\n\t\tif(this.pendingDynamicsCommands.size === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst pending = this.pendingDynamicsCommands;\n\t\tthis.pendingDynamicsCommands = new Map();\n\n\t\tconst entityIds = Array.from(pending.keys()).sort((a, b) => a - b);\n\t\tconst snapshot = new Float64Array(entityIds.length * DYNAMICS_COMMAND_STRIDE);\n\t\tfor(let index = 0; index < entityIds.length; index++) {\n\t\t\tconst entityId = entityIds[index];\n\t\t\tconst command = pending.get(entityId)!;\n\t\t\tconst offset = index * DYNAMICS_COMMAND_STRIDE;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_ENTITY_ID_OFFSET] = entityId;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_FORCE_X_OFFSET] = command.forceX;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_FORCE_Y_OFFSET] = command.forceY;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_IMPULSE_X_OFFSET] = command.impulseX;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_IMPULSE_Y_OFFSET] = command.impulseY;\n\t\t\tconst velocityMask = (command.velocityX === undefined ? 0 : DYNAMICS_COMMAND_VELOCITY_X_FLAG)\n\t\t\t\t| (command.velocityY === undefined ? 0 : DYNAMICS_COMMAND_VELOCITY_Y_FLAG);\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_VELOCITY_MASK_OFFSET] = velocityMask;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_VELOCITY_X_OFFSET] = command.velocityX ?? 0;\n\t\t\tsnapshot[offset + DYNAMICS_COMMAND_VELOCITY_Y_OFFSET] = command.velocityY ?? 0;\n\t\t}\n\n\t\treturn snapshot;\n\t}\n}\n\n// Ands two optional entity filters. Either alone is handed straight back; only both present pays for a closure.\nfunction combineFilters<C extends ComponentMap & PhysicsComponents>(\n\ta: ((entity: BaseEntity<C>) => boolean) | undefined,\n\tb: ((entity: BaseEntity<C>) => boolean) | undefined,\n): ((entity: BaseEntity<C>) => boolean) | undefined {\n\tif(!a) {\n\t\treturn b;\n\t}\n\tif(!b) {\n\t\treturn a;\n\t}\n\n\treturn entity => a(entity) && b(entity);\n}\n","// World-space axis-aligned box. Edges are exclusive, so a blocker ending exactly on a tile edge stays out of the next tile.\nexport interface Rect {\n\tminX: number\n\tminY: number\n\tmaxX: number\n\tmaxY: number\n}\n\nexport interface TileRange {\n\tfromX: number\n\tfromY: number\n\ttoX: number\n\ttoY: number\n}\n\nconst NO_REGION = 0;\n\n// Walkability and terrain cost per square tile, with (0, 0) the map's top-left corner. Blockers are counted rather\n// than flagged so overlapping blockers can be removed in any order.\nexport default class NavGrid {\n\treadonly tilesWide: number;\n\treadonly tilesHigh: number;\n\treadonly cellSize: number;\n\tprivate blockers: Uint16Array;\n\tprivate speeds: Float32Array;\n\t// Only ever raised, so the search heuristic stays admissible after a fast road is repaved as mud.\n\tprivate fastestSpeed = 1;\n\t// 4-connected open areas, rebuilt lazily after blockers change. A diagonal step needs both orthogonal tiles open,\n\t// so it never joins two regions.\n\tprivate regions: Int32Array;\n\tprivate regionsDirty = true;\n\n\tconstructor(tilesWide: number, tilesHigh: number, cellSize: number) {\n\t\tif(!Number.isInteger(tilesWide) || !Number.isInteger(tilesHigh) || tilesWide <= 0 || tilesHigh <= 0) {\n\t\t\tthrow new Error(`NavGrid needs a positive whole number of tiles, got ${tilesWide}x${tilesHigh}`);\n\t\t}\n\t\tif(!(cellSize > 0)) {\n\t\t\tthrow new Error(`NavGrid needs a positive cell size, got ${cellSize}`);\n\t\t}\n\t\tthis.tilesWide = tilesWide;\n\t\tthis.tilesHigh = tilesHigh;\n\t\tthis.cellSize = cellSize;\n\t\tthis.blockers = new Uint16Array(tilesWide * tilesHigh);\n\t\tthis.speeds = new Float32Array(tilesWide * tilesHigh).fill(1);\n\t\tthis.regions = new Int32Array(tilesWide * tilesHigh);\n\t}\n\n\tclone(): NavGrid {\n\t\tconst copy = new NavGrid(this.tilesWide, this.tilesHigh, this.cellSize);\n\t\tcopy.blockers.set(this.blockers);\n\t\tcopy.speeds.set(this.speeds);\n\t\tcopy.fastestSpeed = this.fastestSpeed;\n\t\treturn copy;\n\t}\n\n\tget tileCount(): number {\n\t\treturn this.tilesWide * this.tilesHigh;\n\t}\n\n\tget maxSpeed(): number {\n\t\treturn this.fastestSpeed;\n\t}\n\n\tinBounds(tileX: number, tileY: number): boolean {\n\t\treturn tileX >= 0 && tileY >= 0 && tileX < this.tilesWide && tileY < this.tilesHigh;\n\t}\n\n\tindex(tileX: number, tileY: number): number {\n\t\treturn tileY * this.tilesWide + tileX;\n\t}\n\n\ttileOf(world: number): number {\n\t\treturn Math.floor(world / this.cellSize);\n\t}\n\n\ttileCenter(tile: number): number {\n\t\treturn (tile + 0.5) * this.cellSize;\n\t}\n\n\t// Off the map counts as blocked.\n\tisBlocked(tileX: number, tileY: number): boolean {\n\t\treturn !this.inBounds(tileX, tileY) || this.blockers[this.index(tileX, tileY)] > 0;\n\t}\n\n\tisAreaClear(tileX: number, tileY: number, tilesWide: number, tilesHigh: number): boolean {\n\t\tif(!this.inBounds(tileX, tileY) || !this.inBounds(tileX + tilesWide - 1, tileY + tilesHigh - 1)) {\n\t\t\treturn false;\n\t\t}\n\t\tfor(let y = tileY; y < tileY + tilesHigh; y++) {\n\t\t\tfor(let x = tileX; x < tileX + tilesWide; x++) {\n\t\t\t\tif(this.blockers[this.index(x, y)] > 0) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tblockerCount(tileX: number, tileY: number): number {\n\t\treturn this.inBounds(tileX, tileY) ? this.blockers[this.index(tileX, tileY)] : 0;\n\t}\n\n\t// The tiles a rect has area in, clamped to the map; empty (toX < fromX) for a rect with no area or off the map.\n\ttilesIn(rect: Rect): TileRange {\n\t\tif(!(rect.maxX > rect.minX) || !(rect.maxY > rect.minY)) {\n\t\t\treturn { fromX: 0, fromY: 0, toX: -1, toY: -1 };\n\t\t}\n\t\treturn {\n\t\t\tfromX: Math.max(0, Math.floor(rect.minX / this.cellSize)),\n\t\t\tfromY: Math.max(0, Math.floor(rect.minY / this.cellSize)),\n\t\t\ttoX: Math.min(this.tilesWide - 1, Math.ceil(rect.maxX / this.cellSize) - 1),\n\t\t\ttoY: Math.min(this.tilesHigh - 1, Math.ceil(rect.maxY / this.cellSize) - 1),\n\t\t};\n\t}\n\n\taddBlocker(rect: Rect): void {\n\t\tconst range = this.tilesIn(rect);\n\t\tfor(let y = range.fromY; y <= range.toY; y++) {\n\t\t\tfor(let x = range.fromX; x <= range.toX; x++) {\n\t\t\t\tconst i = this.index(x, y);\n\t\t\t\tif(this.blockers[i] < 0xFFFF) {\n\t\t\t\t\tthis.blockers[i]++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.regionsDirty = true;\n\t}\n\n\t// Must be given the same rect that was added.\n\tremoveBlocker(rect: Rect): void {\n\t\tconst range = this.tilesIn(rect);\n\t\tfor(let y = range.fromY; y <= range.toY; y++) {\n\t\t\tfor(let x = range.fromX; x <= range.toX; x++) {\n\t\t\t\tconst i = this.index(x, y);\n\t\t\t\tif(this.blockers[i] > 0) {\n\t\t\t\t\tthis.blockers[i]--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.regionsDirty = true;\n\t}\n\n\t// A multiplier on walking speed: 1.5 for a road, 0.5 for mud.\n\tsetTerrainSpeed(tileX: number, tileY: number, speed: number): void {\n\t\tif(!(speed > 0) || !Number.isFinite(speed)) {\n\t\t\tthrow new Error(`Terrain speed must be a positive finite number, got ${speed}`);\n\t\t}\n\t\tif(!this.inBounds(tileX, tileY)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.speeds[this.index(tileX, tileY)] = speed;\n\t\tthis.fastestSpeed = Math.max(this.fastestSpeed, speed);\n\t}\n\n\tterrainSpeed(tileX: number, tileY: number): number {\n\t\treturn this.inBounds(tileX, tileY) ? this.speeds[this.index(tileX, tileY)] : 1;\n\t}\n\n\t// Time-weighted cost of crossing a tile edge to edge.\n\tcost(tileX: number, tileY: number): number {\n\t\treturn this.cellSize / this.speeds[this.index(tileX, tileY)];\n\t}\n\n\t// 0 for a blocked or off-map tile.\n\tregion(tileX: number, tileY: number): number {\n\t\tif(!this.inBounds(tileX, tileY)) {\n\t\t\treturn NO_REGION;\n\t\t}\n\t\tif(this.regionsDirty) {\n\t\t\tthis.rebuildRegions();\n\t\t}\n\t\treturn this.regions[this.index(tileX, tileY)];\n\t}\n\n\tprivate rebuildRegions(): void {\n\t\tconst regions = this.regions;\n\t\tregions.fill(NO_REGION);\n\t\tconst queue = new Int32Array(this.tileCount);\n\t\tlet next = NO_REGION;\n\t\tfor(let start = 0; start < regions.length; start++) {\n\t\t\tif(regions[start] !== NO_REGION || this.blockers[start] > 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tnext++;\n\t\t\tregions[start] = next;\n\t\t\tlet head = 0;\n\t\t\tlet tail = 0;\n\t\t\tqueue[tail++] = start;\n\t\t\twhile(head < tail) {\n\t\t\t\tconst i = queue[head++];\n\t\t\t\tconst x = i % this.tilesWide;\n\t\t\t\tconst y = (i - x) / this.tilesWide;\n\t\t\t\ttail = this.flood(x - 1, y, next, queue, tail);\n\t\t\t\ttail = this.flood(x + 1, y, next, queue, tail);\n\t\t\t\ttail = this.flood(x, y - 1, next, queue, tail);\n\t\t\t\ttail = this.flood(x, y + 1, next, queue, tail);\n\t\t\t}\n\t\t}\n\t\tthis.regionsDirty = false;\n\t}\n\n\tprivate flood(x: number, y: number, region: number, queue: Int32Array, tail: number): number {\n\t\tif(!this.inBounds(x, y)) {\n\t\t\treturn tail;\n\t\t}\n\t\tconst i = this.index(x, y);\n\t\tif(this.regions[i] !== NO_REGION || this.blockers[i] > 0) {\n\t\t\treturn tail;\n\t\t}\n\t\tthis.regions[i] = region;\n\t\tqueue[tail] = i;\n\t\treturn tail + 1;\n\t}\n}","// Binary min-heap of integer ids ordered by a primary then secondary key. Keys are captured on push, so an id pushed\n// again with a better key simply sits beside its stale entry; the caller skips stale pops.\nexport default class MinHeap {\n\tprivate ids: Int32Array;\n\tprivate primary: Float64Array;\n\tprivate secondary: Float64Array;\n\tprivate count = 0;\n\n\tconstructor(capacity = 64) {\n\t\tthis.ids = new Int32Array(capacity);\n\t\tthis.primary = new Float64Array(capacity);\n\t\tthis.secondary = new Float64Array(capacity);\n\t}\n\n\tget size(): number {\n\t\treturn this.count;\n\t}\n\n\tclear(): void {\n\t\tthis.count = 0;\n\t}\n\n\tpush(id: number, primary: number, secondary = 0): void {\n\t\tif(this.count === this.ids.length) {\n\t\t\tthis.grow();\n\t\t}\n\t\tlet i = this.count++;\n\t\twhile(i > 0) {\n\t\t\tconst parent = (i - 1) >> 1;\n\t\t\tif(!this.less(primary, secondary, this.primary[parent], this.secondary[parent])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.set(i, this.ids[parent], this.primary[parent], this.secondary[parent]);\n\t\t\ti = parent;\n\t\t}\n\t\tthis.set(i, id, primary, secondary);\n\t}\n\n\t// -1 when empty.\n\tpop(): number {\n\t\tif(this.count === 0) {\n\t\t\treturn -1;\n\t\t}\n\t\tconst top = this.ids[0];\n\t\tconst last = --this.count;\n\t\tif(last > 0) {\n\t\t\tconst id = this.ids[last];\n\t\t\tconst primary = this.primary[last];\n\t\t\tconst secondary = this.secondary[last];\n\t\t\tlet i = 0;\n\t\t\tfor(;;) {\n\t\t\t\tconst left = i * 2 + 1;\n\t\t\t\tif(left >= last) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tconst right = left + 1;\n\t\t\t\tconst child = right < last && this.less(this.primary[right], this.secondary[right], this.primary[left], this.secondary[left]) ? right : left;\n\t\t\t\tif(!this.less(this.primary[child], this.secondary[child], primary, secondary)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tthis.set(i, this.ids[child], this.primary[child], this.secondary[child]);\n\t\t\t\ti = child;\n\t\t\t}\n\t\t\tthis.set(i, id, primary, secondary);\n\t\t}\n\t\treturn top;\n\t}\n\n\tprivate less(primaryA: number, secondaryA: number, primaryB: number, secondaryB: number): boolean {\n\t\treturn primaryA < primaryB || (primaryA === primaryB && secondaryA < secondaryB);\n\t}\n\n\tprivate set(i: number, id: number, primary: number, secondary: number): void {\n\t\tthis.ids[i] = id;\n\t\tthis.primary[i] = primary;\n\t\tthis.secondary[i] = secondary;\n\t}\n\n\tprivate grow(): void {\n\t\tconst capacity = this.ids.length * 2;\n\t\tconst ids = new Int32Array(capacity);\n\t\tconst primary = new Float64Array(capacity);\n\t\tconst secondary = new Float64Array(capacity);\n\t\tids.set(this.ids);\n\t\tprimary.set(this.primary);\n\t\tsecondary.set(this.secondary);\n\t\tthis.ids = ids;\n\t\tthis.primary = primary;\n\t\tthis.secondary = secondary;\n\t}\n}","import type NavGrid from './nav-grid';\n\n// Slowest terrain (cost per world unit, i.e. 1 / speed) any tile touched by a box of half-size (halfWidth,\n// halfHeight) sliding from (x0, y0) to (x1, y1), or Infinity if it touches a blocked or off-map tile. Touching a\n// tile's edge does not count, matching physics, where touching is not overlapping.\nexport function sweepCost(grid: NavGrid, x0: number, y0: number, x1: number, y1: number, halfWidth: number, halfHeight: number): number {\n\tconst cell = grid.cellSize;\n\tconst dx = x1 - x0;\n\tconst dy = y1 - y0;\n\tconst reachY = halfHeight + cell / 2;\n\tconst fromRow = Math.floor((Math.min(y0, y1) - halfHeight) / cell);\n\tconst toRow = Math.ceil((Math.max(y0, y1) + halfHeight) / cell) - 1;\n\tlet worst = 0;\n\tfor(let row = fromRow; row <= toRow; row++) {\n\t\t// When during the slide the box overlaps this row: an open interval of t.\n\t\tconst centerY = (row + 0.5) * cell;\n\t\tlet tLow: number;\n\t\tlet tHigh: number;\n\t\tif(dy === 0) {\n\t\t\tif(Math.abs(y0 - centerY) >= reachY) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttLow = -Infinity;\n\t\t\ttHigh = Infinity;\n\t\t} else {\n\t\t\tconst a = (centerY - reachY - y0) / dy;\n\t\t\tconst b = (centerY + reachY - y0) / dy;\n\t\t\ttLow = Math.min(a, b);\n\t\t\ttHigh = Math.max(a, b);\n\t\t}\n\t\tconst tFrom = Math.max(tLow, 0);\n\t\tconst tTo = Math.min(tHigh, 1);\n\t\tif(tFrom > tTo || (tFrom === tTo && !(tLow < tFrom && tTo < tHigh))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst xa = x0 + dx * tFrom;\n\t\tconst xb = x0 + dx * tTo;\n\t\tconst fromColumn = Math.floor((Math.min(xa, xb) - halfWidth) / cell);\n\t\tconst toColumn = Math.ceil((Math.max(xa, xb) + halfWidth) / cell) - 1;\n\t\tfor(let column = fromColumn; column <= toColumn; column++) {\n\t\t\tif(grid.isBlocked(column, row)) {\n\t\t\t\treturn Infinity;\n\t\t\t}\n\t\t\tworst = Math.max(worst, 1 / grid.terrainSpeed(column, row));\n\t\t}\n\t}\n\treturn worst;\n}","import { sweepCost } from './line-of-sight';\nimport type NavGrid from './nav-grid';\nimport type { Point } from './path-search';\n\n// Turns checked ahead of each kept point; bounds smoothing to O(turns x LOOKAHEAD) sweeps on a long staircase.\nconst LOOKAHEAD = 24;\n\n// Terrain-aware string pulling over a walkable polyline (start first). A corner is only cut when the straight line is\n// clear with `clearance` to spare and, costing it at the slowest terrain it touches, is no dearer than the stretch it\n// replaces, so a path that detoured around mud is not pulled back through it.\nexport function smoothPath(grid: NavGrid, points: ReadonlyArray<Point>, halfWidth: number, halfHeight: number, clearance: number): Array<Point> {\n\tconst polyline = dropDuplicates(points);\n\tif(polyline.length <= 2) {\n\t\treturn polyline;\n\t}\n\n\t// Costed without the clearance: the unsmoothed path legitimately brushes walls.\n\tconst travelled = new Float64Array(polyline.length);\n\tfor(let i = 1; i < polyline.length; i++) {\n\t\ttravelled[i] = travelled[i - 1] + segmentCost(grid, polyline[i - 1], polyline[i], halfWidth, halfHeight);\n\t}\n\n\tconst turns = [0];\n\tfor(let i = 1; i < polyline.length - 1; i++) {\n\t\tif(!isCollinear(polyline[i - 1], polyline[i], polyline[i + 1])) {\n\t\t\tturns.push(i);\n\t\t}\n\t}\n\tturns.push(polyline.length - 1);\n\n\tconst result = [polyline[0]];\n\tlet from = 0;\n\twhile(from < turns.length - 1) {\n\t\tlet to = from + 1;\n\t\t// Visibility is not monotonic (a shortcut can fail while a longer one succeeds), so look the whole window over.\n\t\tconst last = Math.min(turns.length - 1, from + LOOKAHEAD);\n\t\tfor(let candidate = from + 2; candidate <= last; candidate++) {\n\t\t\tconst a = polyline[turns[from]];\n\t\t\tconst b = polyline[turns[candidate]];\n\t\t\tconst perUnit = sweepCost(grid, a.x, a.y, b.x, b.y, halfWidth + clearance, halfHeight + clearance);\n\t\t\tconst straight = Math.hypot(b.x - a.x, b.y - a.y) * perUnit;\n\t\t\t// Tolerance for float noise; any real saving or loss is whole world units.\n\t\t\tif(Number.isFinite(straight) && straight <= travelled[turns[candidate]] - travelled[turns[from]] + 1e-6) {\n\t\t\t\tto = candidate;\n\t\t\t}\n\t\t}\n\t\tresult.push(polyline[turns[to]]);\n\t\tfrom = to;\n\t}\n\treturn result;\n}\n\nfunction segmentCost(grid: NavGrid, a: Point, b: Point, halfWidth: number, halfHeight: number): number {\n\tconst length = Math.hypot(b.x - a.x, b.y - a.y);\n\tconst perUnit = sweepCost(grid, a.x, a.y, b.x, b.y, halfWidth, halfHeight);\n\t// Legs off a blocked start still need a price; the tile walked into is the best guess.\n\treturn length * (Number.isFinite(perUnit) ? perUnit : 1 / grid.terrainSpeed(grid.tileOf(b.x), grid.tileOf(b.y)));\n}\n\nfunction isCollinear(a: Point, b: Point, c: Point): boolean {\n\treturn Math.abs((b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x)) < 1e-9 &&\n\t\t(b.x - a.x) * (c.x - b.x) + (b.y - a.y) * (c.y - b.y) > 0;\n}\n\nfunction dropDuplicates(points: ReadonlyArray<Point>): Array<Point> {\n\tconst result: Array<Point> = [];\n\tfor(const point of points) {\n\t\tconst last = result[result.length - 1];\n\t\tif(!last || last.x !== point.x || last.y !== point.y) {\n\t\t\tresult.push(point);\n\t\t}\n\t}\n\treturn result;\n}","import MinHeap from './min-heap';\nimport type NavGrid from './nav-grid';\nimport { PathStatus, type PathRequest, type PathResult, type PathSearch, type PathStatusValue, type Point } from './path-search';\nimport { smoothPath } from './smooth-path';\nimport { sweepCost } from './line-of-sight';\n\nexport interface GridAStarOptions {\n\t// 8-way movement. Diagonals never squeeze between two blocked tiles. Defaults to 4-way.\n\tdiagonal?: boolean\n\t// Tiles expanded before giving up with an Incomplete path.\n\tmaxIterations?: number\n\tsmooth?: boolean\n\t// Extra room a smoothed shortcut must leave around blockers and the map edge. Defaults to 0: physics slides a mover\n\t// off a corner it grazes.\n\tclearance?: number\n\t// How far (in tiles) a blocked start or goal is moved to find open ground.\n\trelocateRadius?: number\n}\n\nconst DEFAULT_MAX_ITERATIONS = 50_000;\nconst DEFAULT_CLEARANCE = 0;\nconst DEFAULT_RELOCATE_RADIUS = 16;\nconst ORTHOGONAL = [[-1, 0], [1, 0], [0, -1], [0, 1]] as const;\nconst DIAGONAL = [[-1, -1], [1, -1], [-1, 1], [1, 1]] as const;\n\ninterface Anchor {\n\tx: number\n\ty: number\n}\n\n// A* over tiles. A mover is placed by the top-left tile of its footprint (its \"anchor\"), so a mover bigger than a tile\n// only visits anchors where its whole footprint is open. Scratch arrays are reused across searches and stamped rather\n// than cleared, so a search only pays for the tiles it touches.\nexport default class GridAStar implements PathSearch {\n\tprivate readonly diagonal: boolean;\n\tprivate readonly maxIterations: number;\n\tprivate readonly smooth: boolean;\n\tprivate readonly clearance: number;\n\tprivate readonly relocateRadius: number;\n\tprivate readonly heap = new MinHeap(256);\n\tprivate cost = new Float64Array(0);\n\tprivate parent = new Int32Array(0);\n\tprivate seen = new Uint32Array(0);\n\tprivate closed = new Uint32Array(0);\n\tprivate stamp = 0;\n\n\tconstructor(options: GridAStarOptions = {}) {\n\t\tthis.diagonal = options.diagonal ?? false;\n\t\tthis.maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n\t\tthis.smooth = options.smooth ?? true;\n\t\tthis.clearance = options.clearance ?? DEFAULT_CLEARANCE;\n\t\tthis.relocateRadius = options.relocateRadius ?? DEFAULT_RELOCATE_RADIUS;\n\t}\n\n\tfindPath(grid: NavGrid, request: PathRequest): PathResult {\n\t\tconst cell = grid.cellSize;\n\t\tconst tilesWide = Math.max(1, Math.ceil(request.agentWidth / cell - 1e-9));\n\t\tconst tilesHigh = Math.max(1, Math.ceil(request.agentHeight / cell - 1e-9));\n\t\tif(tilesWide > grid.tilesWide || tilesHigh > grid.tilesHigh) {\n\t\t\treturn { status: PathStatus.Unreachable, points: [], iterations: 0 };\n\t\t}\n\t\tconst anchorOf = (x: number, y: number): Anchor => ({\n\t\t\tx: Math.round((x - tilesWide * cell / 2) / cell),\n\t\t\ty: Math.round((y - tilesHigh * cell / 2) / cell),\n\t\t});\n\t\tconst onMap = (anchor: Anchor): Anchor => ({\n\t\t\tx: clamp(anchor.x, 0, grid.tilesWide - tilesWide),\n\t\t\ty: clamp(anchor.y, 0, grid.tilesHigh - tilesHigh),\n\t\t});\n\t\tconst centerOf = (anchor: Anchor): Point => ({ x: (anchor.x + tilesWide / 2) * cell, y: (anchor.y + tilesHigh / 2) * cell });\n\n\t\tconst start = this.relocate(grid, onMap(anchorOf(request.startX, request.startY)), tilesWide, tilesHigh, request.startX, request.startY, request.goalX, request.goalY);\n\t\t// Off the map counts as moved, so an off-map goal is never walked to.\n\t\tconst requestedGoal = anchorOf(request.goalX, request.goalY);\n\t\tconst goal = this.relocate(grid, onMap(requestedGoal), tilesWide, tilesHigh, request.goalX, request.goalY, request.startX, request.startY);\n\t\tif(!start || !goal || grid.region(start.x, start.y) !== grid.region(goal.x, goal.y)) {\n\t\t\treturn { status: PathStatus.Unreachable, points: [], iterations: 0 };\n\t\t}\n\t\tconst goalMoved = goal.x !== requestedGoal.x || goal.y !== requestedGoal.y;\n\n\t\tconst search = this.search(grid, start, goal, tilesWide, tilesHigh);\n\t\tconst polyline: Array<Point> = [{ x: request.startX, y: request.startY }];\n\t\tfor(const index of search.tiles) {\n\t\t\tpolyline.push(centerOf({ x: index % grid.tilesWide, y: Math.floor(index / grid.tilesWide) }));\n\t\t}\n\n\t\tlet status: PathStatusValue = search.status;\n\t\tif(status === PathStatus.Found) {\n\t\t\tconst destination = centerOf(goal);\n\t\t\tif(goalMoved || !Number.isFinite(sweepCost(grid, destination.x, destination.y, request.goalX, request.goalY,\n\t\t\t\trequest.agentWidth / 2, request.agentHeight / 2))) {\n\t\t\t\tstatus = PathStatus.Unreachable;\n\t\t\t} else {\n\t\t\t\tpolyline.push({ x: request.goalX, y: request.goalY });\n\t\t\t}\n\t\t}\n\n\t\tconst halfWidth = request.agentWidth / 2;\n\t\tconst halfHeight = request.agentHeight / 2;\n\t\tconst points = this.smooth ? smoothPath(grid, polyline, halfWidth, halfHeight, this.clearance) : withoutRepeats(polyline);\n\t\treturn { status, points: points.slice(1), iterations: search.iterations };\n\t}\n\n\t// The open anchor nearest `toward` within relocateRadius rings of `anchor`, ties going to the one nearer `prefer`.\n\tprivate relocate(grid: NavGrid, anchor: Anchor, tilesWide: number, tilesHigh: number, towardX: number, towardY: number, preferX: number, preferY: number): Anchor | undefined {\n\t\tif(grid.isAreaClear(anchor.x, anchor.y, tilesWide, tilesHigh)) {\n\t\t\treturn anchor;\n\t\t}\n\t\tconst cell = grid.cellSize;\n\t\tfor(let radius = 1; radius <= this.relocateRadius; radius++) {\n\t\t\tlet best: Anchor | undefined;\n\t\t\tlet bestDistance = Infinity;\n\t\t\tlet bestPreference = Infinity;\n\t\t\tfor(let y = anchor.y - radius; y <= anchor.y + radius; y++) {\n\t\t\t\tconst edge = y === anchor.y - radius || y === anchor.y + radius;\n\t\t\t\tfor(let x = anchor.x - radius; x <= anchor.x + radius; x += edge ? 1 : radius * 2) {\n\t\t\t\t\tif(!grid.isAreaClear(x, y, tilesWide, tilesHigh)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst centerX = (x + tilesWide / 2) * cell;\n\t\t\t\t\tconst centerY = (y + tilesHigh / 2) * cell;\n\t\t\t\t\tconst distance = Math.hypot(centerX - towardX, centerY - towardY);\n\t\t\t\t\tconst preference = Math.hypot(centerX - preferX, centerY - preferY);\n\t\t\t\t\tif(distance < bestDistance || (distance === bestDistance && preference < bestPreference)) {\n\t\t\t\t\t\tbest = { x, y };\n\t\t\t\t\t\tbestDistance = distance;\n\t\t\t\t\t\tbestPreference = preference;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(best) {\n\t\t\t\treturn best;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate search(grid: NavGrid, start: Anchor, goal: Anchor, tilesWide: number, tilesHigh: number): { status: PathStatusValue, tiles: Array<number>, iterations: number } {\n\t\tthis.prepare(grid.tileCount);\n\t\tconst stamp = this.stamp;\n\t\tconst { cost, parent, seen, closed, heap } = this;\n\t\tconst width = grid.tilesWide;\n\t\tconst startIndex = grid.index(start.x, start.y);\n\t\tconst goalIndex = grid.index(goal.x, goal.y);\n\t\t// The cheapest a tile can ever be crossed, so the estimate never overshoots once roads exist.\n\t\tconst cheapestTile = grid.cellSize / grid.maxSpeed;\n\t\tconst estimate = (x: number, y: number) => {\n\t\t\tconst dx = Math.abs(x - goal.x);\n\t\t\tconst dy = Math.abs(y - goal.y);\n\t\t\tconst tiles = this.diagonal ? Math.max(dx, dy) + (Math.SQRT2 - 1) * Math.min(dx, dy) : dx + dy;\n\t\t\treturn tiles * cheapestTile;\n\t\t};\n\n\t\theap.clear();\n\t\tseen[startIndex] = stamp;\n\t\tcost[startIndex] = 0;\n\t\tparent[startIndex] = -1;\n\t\theap.push(startIndex, estimate(start.x, start.y), estimate(start.x, start.y));\n\t\tlet closest = startIndex;\n\t\tlet closestEstimate = estimate(start.x, start.y);\n\t\tlet iterations = 0;\n\n\t\twhile(heap.size > 0) {\n\t\t\tconst current = heap.pop();\n\t\t\tif(closed[current] === stamp) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(current === goalIndex) {\n\t\t\t\treturn { status: PathStatus.Found, tiles: this.trace(current), iterations };\n\t\t\t}\n\t\t\tif(iterations >= this.maxIterations) {\n\t\t\t\treturn { status: PathStatus.Incomplete, tiles: this.trace(closest), iterations };\n\t\t\t}\n\t\t\tclosed[current] = stamp;\n\t\t\titerations++;\n\n\t\t\tconst x = current % width;\n\t\t\tconst y = (current - x) / width;\n\t\t\tconst remaining = estimate(x, y);\n\t\t\tif(remaining < closestEstimate || (remaining === closestEstimate && cost[current] < cost[closest])) {\n\t\t\t\tclosest = current;\n\t\t\t\tclosestEstimate = remaining;\n\t\t\t}\n\n\t\t\tfor(const [dx, dy] of ORTHOGONAL) {\n\t\t\t\tthis.relax(grid, current, x + dx, y + dy, 1, tilesWide, tilesHigh, estimate);\n\t\t\t}\n\t\t\tif(this.diagonal) {\n\t\t\t\tfor(const [dx, dy] of DIAGONAL) {\n\t\t\t\t\tif(grid.isAreaClear(x + dx, y, tilesWide, tilesHigh) && grid.isAreaClear(x, y + dy, tilesWide, tilesHigh)) {\n\t\t\t\t\t\tthis.relax(grid, current, x + dx, y + dy, Math.SQRT2, tilesWide, tilesHigh, estimate);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { status: PathStatus.Unreachable, tiles: this.trace(closest), iterations };\n\t}\n\n\tprivate relax(grid: NavGrid, from: number, x: number, y: number, distance: number, tilesWide: number, tilesHigh: number, estimate: (x: number, y: number) => number): void {\n\t\tif(!grid.isAreaClear(x, y, tilesWide, tilesHigh)) {\n\t\t\treturn;\n\t\t}\n\t\tconst index = grid.index(x, y);\n\t\tif(this.closed[index] === this.stamp) {\n\t\t\treturn;\n\t\t}\n\t\tconst next = this.cost[from] + distance * footprintCost(grid, x, y, tilesWide, tilesHigh);\n\t\tif(this.seen[index] === this.stamp && next >= this.cost[index]) {\n\t\t\treturn;\n\t\t}\n\t\tthis.seen[index] = this.stamp;\n\t\tthis.cost[index] = next;\n\t\tthis.parent[index] = from;\n\t\tconst remaining = estimate(x, y);\n\t\t// Ties go to the node nearer the goal, which keeps equal-cost searches narrow.\n\t\tthis.heap.push(index, next + remaining, remaining);\n\t}\n\n\tprivate trace(index: number): Array<number> {\n\t\tlet length = 0;\n\t\tfor(let i = index; i !== -1; i = this.parent[i]) {\n\t\t\tlength++;\n\t\t}\n\t\tconst tiles = Array.from({ length }, () => 0);\n\t\tfor(let i = index, at = length - 1; i !== -1; i = this.parent[i], at--) {\n\t\t\ttiles[at] = i;\n\t\t}\n\t\treturn tiles;\n\t}\n\n\tprivate prepare(tileCount: number): void {\n\t\tif(this.cost.length !== tileCount) {\n\t\t\tthis.cost = new Float64Array(tileCount);\n\t\t\tthis.parent = new Int32Array(tileCount);\n\t\t\tthis.seen = new Uint32Array(tileCount);\n\t\t\tthis.closed = new Uint32Array(tileCount);\n\t\t\tthis.stamp = 0;\n\t\t}\n\t\tthis.stamp++;\n\t\t// Wrapped after ~4 billion searches: stale stamps could then collide, so start clean.\n\t\tif(this.stamp === 0xFFFFFFFF) {\n\t\t\tthis.seen.fill(0);\n\t\t\tthis.closed.fill(0);\n\t\t\tthis.stamp = 1;\n\t\t}\n\t}\n}\n\n// Mean cost of the tiles a footprint covers, so a big mover half on a road is half as quick as one fully on it.\nfunction footprintCost(grid: NavGrid, x: number, y: number, tilesWide: number, tilesHigh: number): number {\n\tif(tilesWide === 1 && tilesHigh === 1) {\n\t\treturn grid.cost(x, y);\n\t}\n\tlet total = 0;\n\tfor(let ty = y; ty < y + tilesHigh; ty++) {\n\t\tfor(let tx = x; tx < x + tilesWide; tx++) {\n\t\t\ttotal += grid.cost(tx, ty);\n\t\t}\n\t}\n\treturn total / (tilesWide * tilesHigh);\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n\treturn Math.min(max, Math.max(min, value));\n}\n\nfunction withoutRepeats(points: Array<Point>): Array<Point> {\n\treturn points.filter((point, i) => i === 0 || point.x !== points[i - 1].x || point.y !== points[i - 1].y);\n}","import { loadFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport {\n\tNAVIGATION_GOAL_X_INDEX,\n\tNAVIGATION_GOAL_Y_INDEX,\n\tNAVIGATION_OUTCOME_INDEX,\n\tNAVIGATION_WAYPOINT_COUNT_INDEX,\n\tNAVIGATION_WAYPOINT_INDEX,\n} from '../components/navigation-component-constants';\nimport { isPathPending, requestPath, waypointX, waypointY } from '../components/navigation-component';\nimport { PathStatus, type Point } from './path-search';\n\nexport const FollowStep = {\n\t// No path to follow yet, including just after asking again from the end of an incomplete one.\n\tWaiting: 0,\n\t// `target` is the next waypoint.\n\tWaypoint: 1,\n\t// Past the last waypoint of a found path; `target` is the goal.\n\tGoal: 2,\n\t// At the closest reachable spot to an unreachable goal; whoever set it decides what to do next.\n\tStuck: 3,\n} as const;\nexport type FollowStepValue = typeof FollowStep[keyof typeof FollowStep];\n\n// Advances past waypoints within `arriveDistance` of (x, y) and says where to head next. Writes into `target` rather\n// than returning a point so a follower running every step does not allocate.\nexport function followPath(navigation: Int32Array, x: number, y: number, arriveDistance: number, target: Point): FollowStepValue {\n\tif(isPathPending(navigation)) {\n\t\treturn FollowStep.Waiting;\n\t}\n\n\tconst count = navigation[NAVIGATION_WAYPOINT_COUNT_INDEX];\n\tlet index = navigation[NAVIGATION_WAYPOINT_INDEX];\n\twhile(index < count && Math.hypot(waypointX(navigation, index) - x, waypointY(navigation, index) - y) <= arriveDistance) {\n\t\tindex++;\n\t}\n\tnavigation[NAVIGATION_WAYPOINT_INDEX] = index;\n\n\tif(index < count) {\n\t\ttarget.x = waypointX(navigation, index);\n\t\ttarget.y = waypointY(navigation, index);\n\t\treturn FollowStep.Waypoint;\n\t}\n\tconst outcome = navigation[NAVIGATION_OUTCOME_INDEX];\n\tif(outcome === PathStatus.Incomplete) {\n\t\trequestPath(navigation);\n\t\treturn FollowStep.Waiting;\n\t}\n\tif(outcome === PathStatus.Found) {\n\t\ttarget.x = loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX);\n\t\ttarget.y = loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX);\n\t\treturn FollowStep.Goal;\n\t}\n\treturn FollowStep.Stuck;\n}\n","import type { BaseEntity, ComponentMap, EntityWorkerSystemQuery } from '@daneren2005/shared-memory-ecs';\nimport { BODY_FLAGS_INDEX, BODY_SHAPE_MASK } from '../components/body-component-constants';\nimport type { BodyComponent } from '../components/body-component';\nimport {\n\tTRANSFORM_ANGLE_INDEX,\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n} from '../components/transform-component-constants';\nimport type { TransformComponent } from '../components/transform-component';\nimport { shapeHalfHeight, shapeHalfWidth } from '../math/shapes';\nimport type NavGrid from './nav-grid';\nimport type { Rect } from './nav-grid';\n\nexport const PATH_BLOCKERS_QUERY = 'pathBlockers';\n// Bodies in a gate category, which only stop movers whose collide mask takes that category in.\nexport const PATH_GATES_QUERY = 'pathGates';\n\nexport type PathBlockerComponents = {\n\ttransform: TransformComponent\n\tbody: BodyComponent\n};\n\n// Structural so a delta from any game's component map fits.\nexport interface PathBlockerDelta {\n\tadded: ReadonlyArray<{ entityId: number, components: { transform?: unknown, body?: unknown } }>\n\tremoved: ReadonlyArray<number>\n}\n\nexport interface PathBlockerQueryOptions<C extends ComponentMap> {\n\tgateCategories?: number\n\t// Narrows both queries, e.g. `not: ['construction']` so a building site blocks nothing until it is finished.\n\tnot?: Array<keyof C>\n\tfilter?: (entity: BaseEntity<C>) => boolean\n}\n\n// The blocker and gate queries PathfindingSystem builds its grids from, for another system wanting the same view.\nexport function pathBlockerQueries<C extends ComponentMap & PathBlockerComponents>(options: PathBlockerQueryOptions<C> = {}): Record<string, EntityWorkerSystemQuery<C>> {\n\tconst { gateCategories = 0, not, filter } = options;\n\tconst blockers: EntityWorkerSystemQuery<C> = {\n\t\trequired: ['transform', 'body'],\n\t\tnot,\n\t\tfilter: entity => {\n\t\t\tconst body = entity.components.body;\n\t\t\treturn !!body && body.blocksPath && !body.sensor && (!filter || filter(entity));\n\t\t},\n\t};\n\tif(!gateCategories) {\n\t\treturn { [PATH_BLOCKERS_QUERY]: blockers };\n\t}\n\tconst gates: EntityWorkerSystemQuery<C> = {\n\t\trequired: ['transform', 'body'],\n\t\tnot,\n\t\tfilter: entity => {\n\t\t\tconst body = entity.components.body;\n\t\t\treturn !!body && !body.blocksPath && !body.sensor && (body.collideCategory & gateCategories) !== 0 && (!filter || filter(entity));\n\t\t},\n\t};\n\treturn { [PATH_BLOCKERS_QUERY]: blockers, [PATH_GATES_QUERY]: gates };\n}\n\n// The axis-aligned box a body covers. A body turned by a right angle is snapped to it so float noise in the angle does\n// not grow the box into the next tile.\nexport function blockerRect(transform: Float32Array, body: Uint32Array): Rect {\n\tconst shape = body[BODY_FLAGS_INDEX] & BODY_SHAPE_MASK;\n\tconst width = transform[TRANSFORM_WIDTH_INDEX];\n\tconst height = transform[TRANSFORM_HEIGHT_INDEX];\n\tconst angle = snappedRightAngle(transform[TRANSFORM_ANGLE_INDEX]);\n\tconst halfWidth = shapeHalfWidth(shape, width, height, angle);\n\tconst halfHeight = shapeHalfHeight(shape, width, height, angle);\n\tconst x = transform[TRANSFORM_X_INDEX];\n\tconst y = transform[TRANSFORM_Y_INDEX];\n\treturn { minX: x - halfWidth, minY: y - halfHeight, maxX: x + halfWidth, maxY: y + halfHeight };\n}\n\n// Blockers are assumed never to move, so each is rasterised once when it joins and cleared by the same rect when it\n// leaves. Returns whether any were added.\nexport function applyRectChanges(rects: Map<number, Rect>, grids: Iterable<NavGrid>, delta: PathBlockerDelta): boolean {\n\tconst targets = [...grids];\n\tfor(const entityId of delta.removed) {\n\t\tconst rect = rects.get(entityId);\n\t\tif(rect) {\n\t\t\ttargets.forEach(grid => grid.removeBlocker(rect));\n\t\t\trects.delete(entityId);\n\t\t}\n\t}\n\tfor(const { entityId, components } of delta.added) {\n\t\t// `added` also re-sends an existing member whose components changed.\n\t\tconst previous = rects.get(entityId);\n\t\tif(previous) {\n\t\t\ttargets.forEach(grid => grid.removeBlocker(previous));\n\t\t}\n\t\tconst { transform, body } = components;\n\t\tif(!(transform instanceof Float32Array) || !(body instanceof Uint32Array)) {\n\t\t\trects.delete(entityId);\n\t\t\tcontinue;\n\t\t}\n\t\tconst rect = blockerRect(transform, body);\n\t\ttargets.forEach(grid => grid.addBlocker(rect));\n\t\trects.set(entityId, rect);\n\t}\n\treturn delta.added.length > 0;\n}\n\nfunction snappedRightAngle(angle: number): number {\n\tconst snapped = Math.round(angle / (Math.PI / 2)) * Math.PI / 2;\n\treturn Math.abs(angle - snapped) < 1e-5 ? snapped : angle;\n}\n","import { BODY_CATEGORY_INDEX } from '../components/body-component-constants';\nimport NavGrid, { type Rect } from './nav-grid';\nimport { applyRectChanges, blockerRect, type PathBlockerDelta } from './path-blockers';\n\ninterface Gate {\n\trect: Rect\n\tcategory: number\n}\n\n// Worker-local grids the pathfinding update searches, rebuilt from the blocker and gate queries on every load.\nexport interface NavigationState {\n\t// Blockers only: what a mover whose mask takes in none of the gate categories walks through.\n\tgrid: NavGrid\n\tblockers: Map<number, Rect>\n\tgateCategories: number\n\tgates: Map<number, Gate>\n\t// Keyed by the gate categories a mover's mask takes in. Cloned from `grid` on first use, so a game with gates no\n\t// mover collides with never pays for a second grid.\n\tgatedGrids: Map<number, NavGrid>\n}\n\nexport function createNavigationState(tilesWide: number, tilesHigh: number, cellSize: number, gateCategories = 0): NavigationState {\n\treturn {\n\t\tgrid: new NavGrid(tilesWide, tilesHigh, cellSize),\n\t\tblockers: new Map(),\n\t\tgateCategories,\n\t\tgates: new Map(),\n\t\tgatedGrids: new Map(),\n\t};\n}\n\nexport function navigationGridFor(state: NavigationState, collideMask: number): NavGrid {\n\tconst key = collideMask & state.gateCategories;\n\tif(!key) {\n\t\treturn state.grid;\n\t}\n\tlet grid = state.gatedGrids.get(key);\n\tif(!grid) {\n\t\tgrid = state.grid.clone();\n\t\tfor(const gate of state.gates.values()) {\n\t\t\tif(gate.category & key) {\n\t\t\t\tgrid.addBlocker(gate.rect);\n\t\t\t}\n\t\t}\n\t\tstate.gatedGrids.set(key, grid);\n\t}\n\treturn grid;\n}\n\n// Everything blocking a mover with this mask: the blockers plus the gates it collides with.\nexport function blockingRects(state: NavigationState, collideMask: number): Array<Rect> {\n\tconst key = collideMask & state.gateCategories;\n\tconst rects = [...state.blockers.values()];\n\tif(key) {\n\t\tfor(const gate of state.gates.values()) {\n\t\t\tif(gate.category & key) {\n\t\t\t\trects.push(gate.rect);\n\t\t\t}\n\t\t}\n\t}\n\treturn rects;\n}\n\n// A multiplier on walking speed (1.5 for a road, 0.5 for mud), applied to every grid.\nexport function setNavigationTerrainSpeed(state: NavigationState, tileX: number, tileY: number, speed: number): void {\n\tstate.grid.setTerrainSpeed(tileX, tileY, speed);\n\tfor(const grid of state.gatedGrids.values()) {\n\t\tgrid.setTerrainSpeed(tileX, tileY, speed);\n\t}\n}\n\n// Returns whether any blockers were added, so paths already handed out can be rechecked.\nexport function applyPathBlockerChanges(state: NavigationState, delta: PathBlockerDelta): boolean {\n\treturn applyRectChanges(state.blockers, [state.grid, ...state.gatedGrids.values()], delta);\n}\n\nexport function applyPathGateChanges(state: NavigationState, delta: PathBlockerDelta): boolean {\n\tfor(const entityId of delta.removed) {\n\t\tconst gate = state.gates.get(entityId);\n\t\tif(gate) {\n\t\t\tforGatedGrids(state, gate.category, grid => grid.removeBlocker(gate.rect));\n\t\t\tstate.gates.delete(entityId);\n\t\t}\n\t}\n\tfor(const { entityId, components } of delta.added) {\n\t\tconst previous = state.gates.get(entityId);\n\t\tif(previous) {\n\t\t\tforGatedGrids(state, previous.category, grid => grid.removeBlocker(previous.rect));\n\t\t}\n\t\tconst { transform, body } = components;\n\t\tif(!(transform instanceof Float32Array) || !(body instanceof Uint32Array)) {\n\t\t\tstate.gates.delete(entityId);\n\t\t\tcontinue;\n\t\t}\n\t\tconst gate = { rect: blockerRect(transform, body), category: body[BODY_CATEGORY_INDEX] & state.gateCategories };\n\t\tforGatedGrids(state, gate.category, grid => grid.addBlocker(gate.rect));\n\t\tstate.gates.set(entityId, gate);\n\t}\n\treturn delta.added.length > 0;\n}\n\nfunction forGatedGrids(state: NavigationState, category: number, apply: (grid: NavGrid) => void): void {\n\tfor(const [key, grid] of state.gatedGrids) {\n\t\tif(key & category) {\n\t\t\tapply(grid);\n\t\t}\n\t}\n}\n","import { loadFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport type {\n\tComponentMap,\n\tEntityQueryChangedFunction,\n\tEntityQueryComponents,\n\tEntityUpdateComponents,\n\tEntityUpdateFunction,\n\tEntityUpdatePreRunFunction,\n\tEntityWorkerSystemWorld,\n} from '@daneren2005/shared-memory-ecs';\nimport { BODY_MASK_INDEX } from '../components/body-component-constants';\nimport {\n\tNAVIGATION_ACTIVE_INDEX,\n\tNAVIGATION_GOAL_X_INDEX,\n\tNAVIGATION_GOAL_Y_INDEX,\n\tNAVIGATION_OUTCOME_INDEX,\n\tNAVIGATION_REQUEST_INDEX,\n\tNAVIGATION_RESULT_INDEX,\n\tNAVIGATION_WAYPOINT_COUNT_INDEX,\n\tNAVIGATION_WAYPOINT_INDEX,\n} from '../components/navigation-component-constants';\nimport { waypointX, waypointY, writePath } from '../components/navigation-component';\nimport {\n\tTRANSFORM_HEIGHT_INDEX,\n\tTRANSFORM_WIDTH_INDEX,\n\tTRANSFORM_X_INDEX,\n\tTRANSFORM_Y_INDEX,\n} from '../components/transform-component-constants';\nimport GridAStar from './grid-a-star';\nimport { sweepCost } from './line-of-sight';\nimport type NavGrid from './nav-grid';\nimport type { Rect } from './nav-grid';\nimport {\n\tapplyPathBlockerChanges,\n\tapplyPathGateChanges,\n\tblockingRects,\n\tcreateNavigationState,\n\tnavigationGridFor,\n\ttype NavigationState,\n} from './navigation-state';\nimport { PATH_BLOCKERS_QUERY, PATH_GATES_QUERY } from './path-blockers';\nimport { PathStatus, type PathRequest, type PathSearch } from './path-search';\n\n// A* tiles expanded per run before the rest stay pending for the next. Counted in work rather than wall-clock time so\n// a simulation plays out the same however loaded the machine is. The first search of a run always goes ahead.\nexport const DEFAULT_PATH_SEARCH_ITERATION_BUDGET = 16_000;\n\nexport type PathfindingUpdateComponents = {\n\tnavigation: Int32Array\n\ttransform: Float32Array\n\t// Movers whose mask takes in a gate category path around those gates.\n\tbody?: Uint32Array\n};\n\nexport interface PathfindingInitData {\n\ttilesWide: number\n\ttilesHigh: number\n\tcellSize: number\n}\n\nexport interface PathfindingWorld extends EntityWorkerSystemWorld {\n\tnavigation: NavigationState\n\t// Per run: set when a blocker was added, so paths already handed out are rechecked.\n\tblockersAdded?: boolean\n\tsearchIterations?: number\n\t// Overrides the update's iterationBudget for this run; PathfindingSystem sets it from its own `iterationBudget`.\n\titerationBudget?: number\n}\n\nexport interface PathfindingUpdateOptions<\n\tC extends ComponentMap,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld,\n\tD extends PathfindingInitData,\n> {\n\t// Defaults to 8-way GridAStar.\n\tsearch?: PathSearch\n\titerationBudget?: number\n\t// Collide categories that stop only movers whose mask takes them in, such as a door animals cannot open.\n\tgateCategories?: number\n\t// Runs once the grids are built from what was loaded; what it returns joins the world. Feed terrain speeds in here\n\t// with setNavigationTerrainSpeed.\n\tinit?: (navigation: NavigationState, data: D | undefined, queries: EntityQueryComponents<C>) => Partial<W> | void\n\t// Handed every query change after the update's own, for the game's extra queries.\n\tqueryChanged?: EntityQueryChangedFunction<C, W>\n\tpreRun?: EntityUpdatePreRunFunction<C, T, W>\n}\n\n// Read by PathfindingSystem on the main thread to build matching gate queries.\nexport interface PathfindingUpdateMetadata {\n\tgateCategories: number\n}\n\nexport type PathfindingUpdateFunction<\n\tC extends ComponentMap,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld,\n\tD extends PathfindingInitData,\n> = EntityUpdateFunction<C, T, W, D> & { pathfinding: PathfindingUpdateMetadata };\n\n// Builds the update both backends run: the worker file hands it to createEntitySystemWorker and PathfindingSystem takes\n// the same function as its `updateFunction`. Hooks are baked in because functions do not survive postMessage.\nexport function createPathfindingUpdate<\n\tC extends ComponentMap = ComponentMap,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C> = PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld = PathfindingWorld,\n\tD extends PathfindingInitData = PathfindingInitData,\n>(options: PathfindingUpdateOptions<C, T, W, D> = {}): PathfindingUpdateFunction<C, T, W, D> {\n\tconst search = options.search ?? new GridAStar({ diagonal: true });\n\tconst budget = options.iterationBudget ?? DEFAULT_PATH_SEARCH_ITERATION_BUDGET;\n\tconst gateCategories = options.gateCategories ?? 0;\n\n\tconst update: EntityUpdateFunction<C, T, W, D> = (world, entityId, components) => {\n\t\tconst { navigation, transform, body } = components;\n\t\tif(!navigation[NAVIGATION_ACTIVE_INDEX]) {\n\t\t\treturn;\n\t\t}\n\t\tconst mask = body ? body[BODY_MASK_INDEX] : 0;\n\t\tconst grid = navigationGridFor(world.navigation, mask);\n\n\t\tlet requestId = Atomics.load(navigation, NAVIGATION_REQUEST_INDEX);\n\t\tif(requestId === Atomics.load(navigation, NAVIGATION_RESULT_INDEX)) {\n\t\t\tif(!world.blockersAdded || isPathClear(grid, navigation, transform)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trequestId = Atomics.add(navigation, NAVIGATION_REQUEST_INDEX, 1) + 1;\n\t\t}\n\t\tif((world.searchIterations ?? 0) >= (world.iterationBudget ?? budget)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst request: PathRequest = {\n\t\t\tstartX: transform[TRANSFORM_X_INDEX],\n\t\t\tstartY: transform[TRANSFORM_Y_INDEX],\n\t\t\tgoalX: loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX),\n\t\t\tgoalY: loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX),\n\t\t\tagentWidth: transform[TRANSFORM_WIDTH_INDEX],\n\t\t\tagentHeight: transform[TRANSFORM_HEIGHT_INDEX],\n\t\t};\n\t\t// Physics lets a mover walk out of a body it is already inside (a bed finished around a sleeper), so that body\n\t\t// mustn't count here. Left in, the search would relocate the start to open ground, maybe through a wall.\n\t\tconst embedded = embeddedRects(world.navigation, grid, mask, request);\n\t\tembedded.forEach(rect => grid.removeBlocker(rect));\n\t\tconst result = search.findPath(grid, request);\n\t\tembedded.forEach(rect => grid.addBlocker(rect));\n\t\tworld.searchIterations = (world.searchIterations ?? 0) + result.iterations;\n\t\t// A found path's last point is the goal, which the follower reads off the navigation block instead.\n\t\tconst points = result.status === PathStatus.Found ? result.points.slice(0, -1) : result.points;\n\t\t// Stuck against the same dead end it was already at: searching again would only land here once more.\n\t\tconst status = result.status === PathStatus.Incomplete && points.length === 0 ? PathStatus.Unreachable : result.status;\n\t\twritePath(navigation, status, points);\n\t\tAtomics.store(navigation, NAVIGATION_RESULT_INDEX, requestId);\n\t};\n\n\tupdate.init = (_world, data, _entities, queries) => {\n\t\tconst navigation = createNavigationState(data?.tilesWide ?? 1, data?.tilesHigh ?? 1, data?.cellSize ?? 1, gateCategories);\n\t\tapplyPathBlockerChanges(navigation, { added: queries[PATH_BLOCKERS_QUERY] ?? [], removed: [] });\n\t\tapplyPathGateChanges(navigation, { added: queries[PATH_GATES_QUERY] ?? [], removed: [] });\n\t\tconst extension = options.init?.(navigation, data, queries);\n\t\t// Cast because a W could in theory narrow `navigation`; the update always builds the base NavigationState.\n\t\treturn { ...extension, navigation } as Partial<W>;\n\t};\n\n\tupdate.queryChanged = (world, queryName, delta, callbacks) => {\n\t\tif(queryName === PATH_BLOCKERS_QUERY) {\n\t\t\tworld.blockersAdded = applyPathBlockerChanges(world.navigation, delta) || !!world.blockersAdded;\n\t\t} else if(queryName === PATH_GATES_QUERY) {\n\t\t\tworld.blockersAdded = applyPathGateChanges(world.navigation, delta) || !!world.blockersAdded;\n\t\t}\n\t\toptions.queryChanged?.(world, queryName, delta, callbacks);\n\t};\n\n\tupdate.preRun = (world, entities, queries, callbacks) => {\n\t\tworld.searchIterations = 0;\n\t\toptions.preRun?.(world, entities, queries, callbacks);\n\t};\n\n\treturn Object.assign(update, { pathfinding: { gateCategories } });\n}\n\nexport const pathfindingUpdate = createPathfindingUpdate();\n\nfunction embeddedRects(state: NavigationState, grid: NavGrid, mask: number, request: PathRequest): Array<Rect> {\n\tconst halfWidth = request.agentWidth / 2;\n\tconst halfHeight = request.agentHeight / 2;\n\tconst footprint = {\n\t\tminX: request.startX - halfWidth, minY: request.startY - halfHeight, maxX: request.startX + halfWidth, maxY: request.startY + halfHeight,\n\t};\n\tconst tiles = grid.tilesIn(footprint);\n\tlet clear = true;\n\tfor(let y = tiles.fromY; y <= tiles.toY && clear; y++) {\n\t\tfor(let x = tiles.fromX; x <= tiles.toX && clear; x++) {\n\t\t\tclear = grid.blockerCount(x, y) === 0;\n\t\t}\n\t}\n\tif(clear) {\n\t\treturn [];\n\t}\n\treturn blockingRects(state, mask)\n\t\t.filter(rect => rect.minX < footprint.maxX && rect.maxX > footprint.minX && rect.minY < footprint.maxY && rect.maxY > footprint.minY);\n}\n\n// Whether the rest of a settled path is still walkable, from where the mover stands.\nfunction isPathClear(grid: NavGrid, navigation: Int32Array, transform: Float32Array): boolean {\n\tconst halfWidth = transform[TRANSFORM_WIDTH_INDEX] / 2;\n\tconst halfHeight = transform[TRANSFORM_HEIGHT_INDEX] / 2;\n\tlet x = transform[TRANSFORM_X_INDEX];\n\tlet y = transform[TRANSFORM_Y_INDEX];\n\tconst count = navigation[NAVIGATION_WAYPOINT_COUNT_INDEX];\n\tfor(let i = navigation[NAVIGATION_WAYPOINT_INDEX]; i <= count; i++) {\n\t\tlet nextX: number;\n\t\tlet nextY: number;\n\t\tif(i < count) {\n\t\t\tnextX = waypointX(navigation, i);\n\t\t\tnextY = waypointY(navigation, i);\n\t\t} else if(navigation[NAVIGATION_OUTCOME_INDEX] === PathStatus.Found) {\n\t\t\tnextX = loadFloat32(navigation, NAVIGATION_GOAL_X_INDEX);\n\t\t\tnextY = loadFloat32(navigation, NAVIGATION_GOAL_Y_INDEX);\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t\tif(!Number.isFinite(sweepCost(grid, x, y, nextX, nextY, halfWidth, halfHeight))) {\n\t\t\treturn false;\n\t\t}\n\t\tx = nextX;\n\t\ty = nextY;\n\t}\n\treturn true;\n}\n","import { EntityWorkerSystem } from '@daneren2005/shared-memory-ecs';\nimport type { BaseEntity, BaseWorld, ComponentDefinitionMap, ComponentMap, EntityUpdateComponents, EntityWorkerSystemQuery, SystemConfig } from '@daneren2005/shared-memory-ecs';\nimport { DEFAULT_PHYSICS_STEP_MS } from '../constants';\nimport type { NavigationComponents } from '../components/registry';\nimport { pathBlockerQueries } from './path-blockers';\nimport {\n\tcreatePathfindingUpdate,\n\ttype PathfindingInitData,\n\ttype PathfindingUpdateComponents,\n\ttype PathfindingUpdateFunction,\n\ttype PathfindingWorld,\n} from './pathfinding-update';\n\nexport interface PathfindingSystemConfig<\n\tC extends ComponentMap & NavigationComponents,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld,\n\tD extends PathfindingInitData,\n> extends Partial<SystemConfig> {\n\t// The grid's size, read when the system loads (so after a save's bounds are applied).\n\tgetInitData: () => D\n\t// Worker entry point, written out in the game's own source so bundlers see it:\n\t//\n\t// getWorker: () => new Worker(new URL('./pathfinding.worker.ts', import.meta.url), { type: 'module' })\n\t//\n\t// Left off, searches run in-process on the main thread instead.\n\tgetWorker?: () => Worker\n\tforceMainThread?: boolean\n\t// Built with createPathfindingUpdate; the worker file must hand createEntitySystemWorker the same function.\n\tupdateFunction?: PathfindingUpdateFunction<C, T, W, D>\n\t// Narrows what counts as a blocker or gate, e.g. `not: ['construction']`. Other systems reading the same view can\n\t// build these queries with pathBlockerQueries.\n\tblockers?: {\n\t\tnot?: Array<keyof C>\n\t\tfilter?: (entity: BaseEntity<C>) => boolean\n\t}\n\t// Extra queries for the update's hooks, such as terrain to feed setNavigationTerrainSpeed.\n\tqueries?: { [key: string]: EntityWorkerSystemQuery<C> }\n\t// Extra components sent with each mover.\n\toptional?: Array<keyof C & string>\n\t// A* tiles expanded per run, overriding the update's own budget. Also a live field on the system.\n\titerationBudget?: number\n}\n\n// Finds paths for movers whose navigation request is pending, against grids its update keeps from the blocker queries.\nexport default class PathfindingSystem<\n\tC extends ComponentMap & NavigationComponents,\n\tT extends PathfindingUpdateComponents & EntityUpdateComponents<C> = PathfindingUpdateComponents & EntityUpdateComponents<C>,\n\tW extends PathfindingWorld = PathfindingWorld,\n\tD extends PathfindingInitData = PathfindingInitData,\n> extends EntityWorkerSystem<C, T, W, D> {\n\t// Sent with every run, so a change takes effect on the next one. Undefined leaves the update's own budget.\n\titerationBudget: number | undefined;\n\n\tconstructor(world: BaseWorld<ComponentDefinitionMap, C>, options: PathfindingSystemConfig<C, T, W, D>) {\n\t\tconst updateFunction: PathfindingUpdateFunction<C, T, W, D> = options.updateFunction ?? createPathfindingUpdate<C, T, W, D>();\n\t\tconst optional = options.optional ?? [];\n\t\tsuper(world, {\n\t\t\tname: options.name ?? 'PathfindingSystem',\n\t\t\tdeltaBetweenRuns: options.deltaBetweenRuns ?? DEFAULT_PHYSICS_STEP_MS,\n\t\t\tfirstRun: options.firstRun,\n\t\t\trequired: ['navigation', 'transform'],\n\t\t\toptional: optional.includes('body') ? optional : ['body', ...optional],\n\t\t\tqueries: {\n\t\t\t\t...pathBlockerQueries<C>({ ...options.blockers, gateCategories: updateFunction.pathfinding.gateCategories }),\n\t\t\t\t...options.queries,\n\t\t\t},\n\t\t\tupdateFunction,\n\t\t\tgetInitData: options.getInitData,\n\t\t\tforceMainThread: options.forceMainThread ?? !options.getWorker,\n\t\t\tgetWorker: options.getWorker ?? (() => {\n\t\t\t\tthrow new Error('PathfindingSystem cannot start a worker: no getWorker was supplied');\n\t\t\t}),\n\t\t});\n\t\tthis.iterationBudget = options.iterationBudget;\n\t}\n\n\t// A subclass overriding this must call super.addDataToWorld(world), or the budget stops reaching the update.\n\taddDataToWorld(world: W): void {\n\t\tworld.iterationBudget = this.iterationBudget;\n\t}\n\n\tprivate loaded = false;\n\n\t// The update builds its grids in its `init` hook, which the ECS only calls from `world.load`. A world filled with\n\t// loadEntity and then init()ed never loads, so load here; it consumes the pending deltas, so nothing is added twice.\n\tasync init(): Promise<void> {\n\t\tawait super.init();\n\t\tif(!this.loaded) {\n\t\t\tawait this.finishLoading();\n\t\t}\n\t}\n\n\tfinishLoading(): Promise<void> {\n\t\tthis.loaded = true;\n\t\treturn super.finishLoading();\n\t}\n}\n","import { loadFloat32 } from '@daneren2005/shared-memory-objects/utils/float32-atomics';\nimport { BACKLOG_CATCHUP_RATE, BACKLOG_TARGET_STEPS } from '../constants';\nimport type { EntityUpdateFunction, EntityWorkerSystemWorld } from '@daneren2005/shared-memory-ecs';\nimport type { InterpolationComponents, InterpolationUpdateComponents } from '../components/registry';\nimport {\n\tINTERPOLATION_CHANNEL_INDEX,\n\tINTERPOLATION_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_TICK_INDEX,\n\tINTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_PREVIOUS_X_INDEX,\n\tINTERPOLATION_PREVIOUS_Y_INDEX,\n\tINTERPOLATION_TARGET_X_INDEX,\n\tINTERPOLATION_TARGET_Y_INDEX,\n\tINTERPOLATION_SYNCED_STEP_DURATION_INDEX,\n\tINTERPOLATION_REMAINING_DURATION_INDEX,\n\tINTERPOLATION_SYNCED_DURATION_INDEX,\n\tINTERPOLATION_SYNCED_TICK_INDEX,\n\tINTERPOLATION_TICK_INDEX,\n\tINTERPOLATION_TOTAL_DURATION_INDEX,\n\tINTERPOLATION_X_INDEX,\n\tINTERPOLATION_Y_INDEX,\n} from '../components/interpolation-component-constants';\nimport { TRANSFORM_X_INDEX, TRANSFORM_Y_INDEX } from '../components/transform-component-constants';\n\nexport interface InterpolationWorld extends EntityWorkerSystemWorld {\n\t// Committed physics tick per interpolation channel, snapshotted once per run by InterpolationSystem.\n\tcommittedTicks?: Array<number>\n}\n\n// Consume published simulation time while advancing toward the last coherent target.\nexport const interpolationUpdate: EntityUpdateFunction<InterpolationComponents, InterpolationUpdateComponents, InterpolationWorld> = (world, entityId, components) => {\n\tconst interpolation = components.interpolation;\n\tconst transform = components.transform;\n\n\t// Accept the transform and timing only if physics completed one consistent publication.\n\tconst before = loadFloat32(interpolation, INTERPOLATION_TICK_INDEX);\n\tlet duration = interpolation[INTERPOLATION_DURATION_INDEX];\n\tlet totalDuration = interpolation[INTERPOLATION_TOTAL_DURATION_INDEX];\n\tlet x = transform[TRANSFORM_X_INDEX];\n\tlet y = transform[TRANSFORM_Y_INDEX];\n\tlet after = loadFloat32(interpolation, INTERPOLATION_TICK_INDEX);\n\tlet coherent = before === after;\n\n\t// A torn or not-yet-committed publication falls back to the one it replaced, so every entity of a physics run\n\t// renders the same step even while the worker is part way through publishing it.\n\tconst committedTick = world.committedTicks?.[interpolation[INTERPOLATION_CHANNEL_INDEX]];\n\tif(committedTick !== undefined && !Number.isNaN(committedTick) && !(coherent && after <= committedTick)) {\n\t\tconst previousBefore = loadFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX);\n\t\tduration = interpolation[INTERPOLATION_PREVIOUS_DURATION_INDEX];\n\t\ttotalDuration = interpolation[INTERPOLATION_PREVIOUS_TOTAL_DURATION_INDEX];\n\t\tx = interpolation[INTERPOLATION_PREVIOUS_X_INDEX];\n\t\ty = interpolation[INTERPOLATION_PREVIOUS_Y_INDEX];\n\t\tafter = loadFloat32(interpolation, INTERPOLATION_PREVIOUS_TICK_INDEX);\n\t\tcoherent = previousBefore === after;\n\t}\n\n\tif(!coherent) {\n\t\t// Keep consuming the last coherent segment; dropping this frame makes moving ships stutter relative to a camera.\n\t\tduration = interpolation[INTERPOLATION_SYNCED_STEP_DURATION_INDEX];\n\t\tif(duration <= 0 || interpolation[INTERPOLATION_REMAINING_DURATION_INDEX] <= 0) {\n\t\t\treturn;\n\t\t}\n\t\tx = interpolation[INTERPOLATION_TARGET_X_INDEX];\n\t\ty = interpolation[INTERPOLATION_TARGET_Y_INDEX];\n\t\ttotalDuration = interpolation[INTERPOLATION_SYNCED_DURATION_INDEX];\n\t\tafter = interpolation[INTERPOLATION_SYNCED_TICK_INDEX];\n\t} else {\n\t\tinterpolation[INTERPOLATION_TARGET_X_INDEX] = x;\n\t\tinterpolation[INTERPOLATION_TARGET_Y_INDEX] = y;\n\t\tinterpolation[INTERPOLATION_SYNCED_STEP_DURATION_INDEX] = duration;\n\t}\n\n\t// No physics segment has been published yet.\n\tif(duration <= 0) {\n\t\tinterpolation[INTERPOLATION_SYNCED_TICK_INDEX] = after;\n\t\tinterpolation[INTERPOLATION_SYNCED_DURATION_INDEX] = totalDuration;\n\t\tinterpolation[INTERPOLATION_REMAINING_DURATION_INDEX] = 0;\n\t\tinterpolation[INTERPOLATION_X_INDEX] = x;\n\t\tinterpolation[INTERPOLATION_Y_INDEX] = y;\n\n\t\treturn;\n\t}\n\n\tlet remainingDuration = interpolation[INTERPOLATION_REMAINING_DURATION_INDEX];\n\tif(after !== interpolation[INTERPOLATION_SYNCED_TICK_INDEX]) {\n\t\tconst syncedDuration = interpolation[INTERPOLATION_SYNCED_DURATION_INDEX];\n\t\tconst publishedDuration = totalDuration >= syncedDuration ? totalDuration - syncedDuration : duration;\n\t\tremainingDuration += publishedDuration;\n\t\tinterpolation[INTERPOLATION_SYNCED_DURATION_INDEX] = totalDuration;\n\t\tinterpolation[INTERPOLATION_SYNCED_TICK_INDEX] = after;\n\t}\n\n\tif(remainingDuration <= 0) {\n\t\tinterpolation[INTERPOLATION_REMAINING_DURATION_INDEX] = 0;\n\t\tinterpolation[INTERPOLATION_X_INDEX] = x;\n\t\tinterpolation[INTERPOLATION_Y_INDEX] = y;\n\t\treturn;\n\t}\n\n\tlet frameDuration = Math.min(Math.max(world.elapsedTime, 0), remainingDuration);\n\t// Ease off a persistent backlog. Once physics publishes every frame (high timeScale, a step lands per frame)\n\t// the budget added per frame matches the budget spent, so a backlog banked by a one-off slow run would never\n\t// drain on its own - the render would trail the simulation by it forever. Spend a little extra on the part of\n\t// the backlog beyond one step of latency, bounded so the render eases back rather than jumping across it. Below\n\t// the target this never fires, leaving normal one-step-latency pacing exactly as it was.\n\tconst targetBacklog = duration * BACKLOG_TARGET_STEPS;\n\tconst backlogAfterFrame = remainingDuration - frameDuration;\n\tif(backlogAfterFrame > targetBacklog) {\n\t\tframeDuration += Math.min(backlogAfterFrame - targetBacklog, frameDuration * BACKLOG_CATCHUP_RATE);\n\t}\n\tconst frameProgress = frameDuration / remainingDuration;\n\tconst renderX = interpolation[INTERPOLATION_X_INDEX];\n\tconst renderY = interpolation[INTERPOLATION_Y_INDEX];\n\tinterpolation[INTERPOLATION_X_INDEX] = renderX + (x - renderX) * frameProgress;\n\tinterpolation[INTERPOLATION_Y_INDEX] = renderY + (y - renderY) * frameProgress;\n\tinterpolation[INTERPOLATION_REMAINING_DURATION_INDEX] = remainingDuration - frameDuration;\n};\n\nexport default interpolationUpdate;\n","import { EntityWorkerSystem } from '@daneren2005/shared-memory-ecs';\nimport type { BaseWorld, ComponentDefinitionMap, ComponentMap, EntityUpdateComponents, SystemConfig } from '@daneren2005/shared-memory-ecs';\nimport type { InterpolationComponents, InterpolationUpdateComponents } from '../components/registry';\nimport { interpolationUpdate, type InterpolationWorld } from './interpolation-update';\nimport { snapshotCommittedTicks } from './interpolation-channels';\n\nexport interface InterpolationSystemConfig extends Partial<SystemConfig> {\n\t// Defaulted the opposite way to PhysicsSystem - main thread unless asked otherwise - since a render position\n\t// read a frame after it was computed is a frame stale, the latency this system exists to cut.\n\tforceMainThread?: boolean\n\tgetWorker?: () => Worker\n}\n\n// Fills in a render position for every entity with an `interpolation` component, once per frame, by blending\n// between the two positions physics published either side of its last step.\n//\n// Its only link to PhysicsSystem is a per-run snapshot of each system's committed tick (interpolation-channels.ts);\n// everything else it needs is in the block, published by whichever run wrote it. Pacing off the physics system would pace off when a run was posted, which on a worker\n// is not when its results arrive; see interpolation-update.ts. So it takes no configuration and follows a step\n// the game retunes mid-flight. Ordering after the physics system is a preference worth one frame of latency;\n// pause and `timeScale` need no code, since BaseWorld#runUpdate already skips and scales for free.\nexport default class InterpolationSystem<\n\tC extends ComponentMap & InterpolationComponents,\n> extends EntityWorkerSystem<C, InterpolationUpdateComponents & EntityUpdateComponents<C>, InterpolationWorld> {\n\tconstructor(world: BaseWorld<ComponentDefinitionMap, C>, options: InterpolationSystemConfig = {}) {\n\t\tsuper(world, {\n\t\t\tname: options.name ?? 'InterpolationSystem',\n\t\t\t// Every frame, which is the whole point.\n\t\t\tdeltaBetweenRuns: 0,\n\t\t\tfirstRun: options.firstRun,\n\n\t\t\t// No velocity is read - the blend is between two positions the simulation produced - so a velocity-less\n\t\t\t// entity is in this system on the same terms as a ship.\n\t\t\trequired: ['transform', 'interpolation'],\n\t\t\tupdateFunction: interpolationUpdate,\n\n\t\t\tforceMainThread: options.forceMainThread ?? !options.getWorker,\n\t\t\tgetWorker: options.getWorker ?? (() => {\n\t\t\t\tthrow new Error('InterpolationSystem cannot start a worker: no getWorker was supplied');\n\t\t\t}),\n\t\t});\n\t}\n\n\t// One snapshot per run, so a physics run that commits mid-pass cannot split entities across two steps.\n\taddDataToWorld(world: InterpolationWorld): void {\n\t\tworld.committedTicks = snapshotCommittedTicks(this.world);\n\t}\n}\n"],"mappings":"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"}