@ignifx/physics-2d 0.9.0 → 0.10.0
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.d.ts +25 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -20
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/skills/physics-2d/SKILL.md +36 -15
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/runtime/runtime-key.ts","../src/components/rigidbody.ts","../src/components/character-controller.ts","../src/errors.ts","../src/material.ts","../src/components/collider.ts","../src/components/colliders.ts","../src/components/tilemap-collider.ts","../src/lite/rapier/module.ts","../src/lite/rapier/world.ts","../src/runtime/host.ts","../src/lite/rapier/character.ts","../src/runtime/runtime.ts","../src/runtime/systems.ts","../src/service.ts","../src/settings.ts","../src/extension.ts","../src/schemas.ts"],"sourcesContent":["import { createServiceKey } from \"@ignifx/core\";\nimport type { Physics2DHost } from \"./host.js\";\nimport type { ServiceNameKey } from \"@ignifx/core\";\n\n/**\n * The key the extension registers its runtime holder under, so that `Rigidbody2D`, the 2D\n * colliders, and `CharacterController2D` can reach the runtime through `this.app.services` without\n * a module-level singleton (`CONSTITUTION.md` §3.6: no globals, two apps in one process).\n *\n * The **holder** is registered rather than the runtime itself, because everything an extension\n * contributes is declared in `register` (`docs/architecture/04-extensions.md` §1) and the runtime\n * does not exist until `onStart`.\n *\n * @internal\n */\nexport const Physics2DHostKey: ServiceNameKey<Physics2DHost> =\n createServiceKey<Physics2DHost>(\"@ignifx/physics-2d:host\");\n","import { bool, Component, createDefaults, defineSchema, enumOf, f32, Vec2 } from \"@ignifx/core\";\nimport { Physics2DHostKey } from \"../runtime/runtime-key.js\";\nimport type { RapierBody } from \"../lite/rapier/world.js\";\nimport type { Physics2DRuntime } from \"../runtime/runtime.js\";\nimport type { ComponentHooks, MutableVec2, Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * A nonzero mass rescales collider densities while preserving their inertia distribution.\n * Zero keeps the default density of 1 kg/m². Rotation has one freeze flag; damping is per body.\n * Automatic collision events follow callbacks as components join or leave the entity.\n */\n\n/**\n * How a 2D body moves.\n *\n * @public\n */\nexport const BODY_TYPES_2D = [\"dynamic\", \"kinematic\", \"static\"] as const;\n\n/**\n * The union of {@link BODY_TYPES_2D}.\n *\n * @public\n */\nexport type BodyType2D = (typeof BODY_TYPES_2D)[number];\n\n/**\n * Whether a body's display pose is interpolated between fixed steps.\n *\n * @public\n */\nexport const INTERPOLATION_MODES_2D = [\"none\", \"interpolate\"] as const;\n\n/**\n * The union of {@link INTERPOLATION_MODES_2D}.\n *\n * @public\n */\nexport type InterpolationMode2D = (typeof INTERPOLATION_MODES_2D)[number];\n\n/**\n * Whether collision callbacks are delivered for this body.\n *\n * @public\n */\nexport const COLLISION_EVENT_MODES_2D = [\"auto\", \"on\", \"off\"] as const;\n\n/**\n * The union of {@link COLLISION_EVENT_MODES_2D}.\n *\n * @public\n */\nexport type CollisionEventMode2D = (typeof COLLISION_EVENT_MODES_2D)[number];\n\n/**\n * The Rapier objects a `Rigidbody2D` owns. Unstable escape hatch\n * (`docs/architecture/00-overview.md` §3).\n *\n * @public\n */\nexport interface Rigidbody2DRapierHandles {\n /** The Rapier body, or `null` before the first fixed step has built it. */\n readonly body: RapierBody | null;\n}\n\n/**\n * Makes an entity's 2D colliders a Rapier body.\n *\n * @example\n * ```ts\n * const crate = world.createEntity(\"Crate\");\n * crate.transform.position2D = new Vec2(0, 5);\n * crate.addComponent(BoxCollider2D, { size: { x: 1, y: 1 } });\n * crate.addComponent(Rigidbody2D, { mass: 2 });\n * ```\n *\n * @public\n */\nexport class Rigidbody2D extends Component implements ComponentHooks {\n /** The namespaced registration id. */\n static typeId = \"ignifx/Rigidbody2D\";\n\n /** One body per entity. */\n static allowMultiple = false;\n\n /** The serialized field declarations (ADR-0004). */\n static schema: Schema = rigidbody2DSchema();\n\n declare bodyType: BodyType2D;\n\n declare mass: number;\n\n declare gravityScale: number;\n\n declare linearDamping: number;\n\n declare angularDamping: number;\n\n declare freezeRotation: boolean;\n\n declare interpolation: InterpolationMode2D;\n\n declare collisionEvents: CollisionEventMode2D;\n\n /** Applies the schema defaults, exactly as `Component.define` would. */\n constructor() {\n super();\n Object.assign(this, createDefaults(Rigidbody2D.schema));\n }\n\n /** Marks the entity's body for a rebuild at the start of the next fixed step. */\n onAttach(): void {\n this.rebuild();\n }\n\n /** Marks the entity's body for a rebuild, which turns it back into an implicit static body. */\n onDetach(): void {\n this.rebuild();\n }\n\n /**\n * Rebuilds the body at the start of the next fixed step. Call it after changing `bodyType`,\n * `mass`, `freezeRotation`, damping, or the entity's scale.\n */\n rebuild(): void {\n this.#runtime()?.markDirty(this.entity);\n }\n\n /**\n * The Rapier handles this component owns.\n *\n * @returns The body, or `null` before the first fixed step built it.\n */\n get rapier(): Rigidbody2DRapierHandles {\n return { body: this.#runtime()?.bodyHandleOf(this.entity) ?? null };\n }\n\n /**\n * The body's linear velocity in metres per second.\n *\n * @returns A freshly allocated vector; use {@link Rigidbody2D.linearVelocityToRef} in hot code.\n */\n get linearVelocity(): Vec2 {\n const out = new Vec2();\n this.linearVelocityToRef(out);\n return out;\n }\n\n /**\n * Replaces the body's linear velocity.\n *\n * @param value - Metres per second, world space.\n */\n set linearVelocity(value: Vec2Like) {\n this.#runtime()?.setLinearVelocity(this.entity, value);\n }\n\n /**\n * Reads the linear velocity without allocating.\n *\n * @param out - The vector to write.\n * @returns `out`.\n */\n linearVelocityToRef(out: MutableVec2): MutableVec2 {\n this.#runtime()?.readLinear(this.entity, out);\n return out;\n }\n\n /**\n * The body's angular velocity.\n *\n * @returns Degrees per second, counter-clockwise — the same unit as `Transform.rotation2D`.\n */\n get angularVelocity(): number {\n return this.#runtime()?.readAngular(this.entity) ?? 0;\n }\n\n /**\n * Replaces the body's angular velocity.\n *\n * @param value - Degrees per second, counter-clockwise.\n */\n set angularVelocity(value: number) {\n this.#runtime()?.setAngularVelocity(this.entity, value);\n }\n\n /**\n * The mass Rapier computed for the body, in kilograms.\n *\n * @returns The mass, or `0` before the body exists.\n */\n get computedMass(): number {\n return this.#runtime()?.massOf(this.entity) ?? 0;\n }\n\n /**\n * Applies a force for one fixed step. Call it from `fixedUpdate`.\n *\n * @param force - Newtons, world space.\n * @param point - Where to apply it; defaults to the centre of mass.\n *\n * @example\n * ```ts\n * fixedUpdate(): void {\n * this.body.addForce({ x: 0, y: 20 });\n * }\n * ```\n */\n addForce(force: Vec2Like, point?: Vec2Like): void {\n this.#runtime()?.addForce(this.entity, force, point ?? null);\n }\n\n /**\n * Applies an instantaneous impulse.\n *\n * @param impulse - Newton-seconds, world space.\n * @param point - Where to apply it; defaults to the centre of mass.\n */\n addImpulse(impulse: Vec2Like, point?: Vec2Like): void {\n this.#runtime()?.addImpulse(this.entity, impulse, point ?? null);\n }\n\n /**\n * Applies a torque for one fixed step.\n *\n * @param torque - Newton-metres, positive counter-clockwise.\n */\n addTorque(torque: number): void {\n this.#runtime()?.addTorque(this.entity, torque);\n }\n\n /**\n * Moves the body without integrating a velocity, and resets the interpolation history so the\n * display pose does not slide across the gap.\n *\n * @param position - The new world position, in metres.\n * @param rotation - The new rotation in degrees counter-clockwise; defaults to the current one.\n */\n teleport(position: Vec2Like, rotation?: number): void {\n this.#runtime()?.teleport(this.entity, position, rotation ?? this.transform.rotation2D);\n }\n\n /**\n * The runtime, or `null` when the extension is not registered on this app.\n *\n * @returns The runtime.\n */\n #runtime(): Physics2DRuntime | null {\n return this.app.services.tryGet(Physics2DHostKey)?.runtime ?? null;\n }\n}\n\n/**\n * Builds the `Rigidbody2D` schema.\n *\n * @returns The schema.\n */\nfunction rigidbody2DSchema(): Schema {\n return defineSchema({\n bodyType: enumOf(BODY_TYPES_2D, \"dynamic\"),\n mass: f32(1, { min: 0 }),\n gravityScale: f32(1),\n linearDamping: f32(0, { min: 0 }),\n angularDamping: f32(0.05, { min: 0 }),\n freezeRotation: bool(false),\n interpolation: enumOf(INTERPOLATION_MODES_2D, \"interpolate\"),\n collisionEvents: enumOf(COLLISION_EVENT_MODES_2D, \"auto\"),\n });\n}\n","import { bool, Component, createDefaults, defineSchema, enumOf, f32, Signal, Vec2, vec2 } from \"@ignifx/core\";\nimport { Physics2DHostKey } from \"../runtime/runtime-key.js\";\nimport { INTERPOLATION_MODES_2D } from \"./rigidbody.js\";\nimport type { CharacterCollision2D } from \"../events.js\";\nimport type { InterpolationMode2D } from \"./rigidbody.js\";\nimport type { Physics2DRuntime } from \"../runtime/runtime.js\";\nimport type { ComponentHooks, Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Sum `move` displacements during `fixedUpdate`; game scripts supply gravity.\n * After the step the controller owns world X/Y, while Z and rotation remain user-controlled.\n * Box shapes support taller autosteps than capsules; see ADR-0006's validation results.\n */\n\n/**\n * The collision shape a 2D character controller uses.\n *\n * @public\n */\nexport const CHARACTER_SHAPES_2D = [\"capsule\", \"box\"] as const;\n\n/**\n * The union of {@link CHARACTER_SHAPES_2D}.\n *\n * @public\n */\nexport type CharacterShape2D = (typeof CHARACTER_SHAPES_2D)[number];\n\n/**\n * A kinematic character that walks, slides, climbs slopes, and steps up.\n *\n * @example\n * ```ts\n * class Walk extends Script implements ScriptCallbacks {\n * static typeId = \"mygame/Walk\";\n * fixedUpdate(dt: number): void {\n * const controller = this.entity.requireComponent(CharacterController2D);\n * controller.move({ x: 4 * dt, y: -9.81 * dt });\n * }\n * }\n * ```\n *\n * @public\n */\nexport class CharacterController2D extends Component implements ComponentHooks {\n /** The namespaced registration id. */\n static typeId = \"ignifx/CharacterController2D\";\n\n /** One controller per entity. */\n static allowMultiple = false;\n\n /** The serialized field declarations (ADR-0004). */\n static schema: Schema = characterSchema();\n\n declare shape: CharacterShape2D;\n\n declare radius: number;\n\n declare height: number;\n\n declare offset: Vec2Like;\n\n declare slopeLimit: number;\n\n declare stepOffset: number;\n\n declare snapToGround: number;\n\n declare skinWidth: number;\n\n declare onOneWayPlatforms: boolean;\n\n declare pushBodies: boolean;\n\n declare interpolation: InterpolationMode2D;\n\n /** The displacement requested so far this fixed step. */\n readonly #pendingMove = new Vec2();\n\n /** The movement Rapier resolved for the last step, which is what `velocity` reports. */\n readonly #resolvedMove = new Vec2();\n\n /** The averaged normal of the supporting surface, written by the step system. */\n readonly #groundNormal = new Vec2(0, 1);\n\n /** Whether the last step ended on walkable ground. */\n #grounded = false;\n\n /** The fixed step the last move was resolved over, for `velocity`. */\n #lastDelta = 0;\n\n /** Emitted for every obstacle the character hit; created on first access. */\n #onCollided: Signal<CharacterCollision2D> | null = null;\n\n /** Applies the schema defaults. */\n constructor() {\n super();\n Object.assign(this, createDefaults(CharacterController2D.schema));\n }\n\n /** Creates the Rapier controller at the start of the next fixed step. */\n onAttach(): void {\n this.rebuild();\n }\n\n /** Releases the Rapier controller and its collider. */\n onDetach(): void {\n this.#runtime()?.releaseController(this);\n }\n\n /** Rebuilds the capsule or box at the start of the next fixed step. */\n rebuild(): void {\n this.#runtime()?.markControllerDirty(this);\n }\n\n /**\n * Requests a displacement for this fixed step. Displacements accumulate until the step runs.\n *\n * @param displacement - The world-space displacement to attempt, in metres.\n */\n move(displacement: Vec2Like): void {\n this.#pendingMove.set(this.#pendingMove.x + displacement.x, this.#pendingMove.y + displacement.y);\n }\n\n /**\n * How fast the character actually moved over the last fixed step, after sliding and blocking.\n *\n * @returns A freshly allocated vector in metres per second.\n */\n get velocity(): Vec2 {\n if (this.#lastDelta <= 0) {\n return new Vec2();\n }\n return new Vec2(this.#resolvedMove.x / this.#lastDelta, this.#resolvedMove.y / this.#lastDelta);\n }\n\n /**\n * Whether the character ended the last step on walkable ground.\n *\n * @returns Rapier's `computedGrounded` from the last move.\n */\n get isGrounded(): boolean {\n return this.#grounded;\n }\n\n /**\n * The most upward-facing normal of the obstacles the last move touched.\n *\n * @returns A live view; copy it if you keep it.\n */\n get groundNormal(): Vec2 {\n return this.#groundNormal;\n }\n\n /**\n * Emitted once per obstacle the character hit during a step.\n *\n * @remarks\n * Trigger colliders are not obstacles — the character walks straight through them — so a sensor\n * never appears here. Listen for `onTriggerEnter`/`onTriggerExit` on the entity's scripts instead.\n *\n * @returns The signal, created on first access.\n */\n get onCollided(): Signal<CharacterCollision2D> {\n this.#onCollided ??= new Signal<CharacterCollision2D>();\n return this.#onCollided;\n }\n\n /**\n * Teleports the character, clearing any pending motion and the interpolation history.\n *\n * @param position - The new world position of the entity, in metres.\n */\n teleport(position: Vec2Like): void {\n this.#pendingMove.set(0, 0);\n this.#runtime()?.teleportController(this, position);\n }\n\n /**\n * The displacement requested so far this step, consumed by the step system.\n *\n * @returns A live view of the accumulator.\n *\n * @internal\n */\n takePendingMove(): Vec2 {\n return this.#pendingMove;\n }\n\n /**\n * Records the outcome of a step.\n *\n * @param movement - What Rapier resolved.\n * @param grounded - Whether the character ended on walkable ground.\n * @param normal - The most upward-facing contact normal, or `null` when there was none.\n * @param deltaSeconds - The fixed step the move covered.\n *\n * @internal\n */\n applyStepResult(movement: Vec2Like, grounded: boolean, normal: Vec2Like | null, deltaSeconds: number): void {\n this.#resolvedMove.set(movement.x, movement.y);\n this.#grounded = grounded;\n this.#lastDelta = deltaSeconds;\n if (normal !== null) {\n this.#groundNormal.set(normal.x, normal.y);\n }\n this.#pendingMove.set(0, 0);\n }\n\n /**\n * Emits `onCollided` if anything is listening.\n *\n * @param collision - The obstacle to report.\n *\n * @internal\n */\n reportHit(collision: CharacterCollision2D): void {\n this.#onCollided?.emit(collision);\n }\n\n /**\n * The runtime, or `null` when the extension is not registered on this app.\n *\n * @returns The runtime.\n */\n #runtime(): Physics2DRuntime | null {\n return this.app.services.tryGet(Physics2DHostKey)?.runtime ?? null;\n }\n}\n\n/**\n * Builds the `CharacterController2D` schema.\n *\n * @returns The schema.\n */\nfunction characterSchema(): Schema {\n return defineSchema({\n shape: enumOf(CHARACTER_SHAPES_2D, \"capsule\"),\n radius: f32(0.25, { min: 0 }),\n height: f32(1, { min: 0 }),\n offset: vec2(),\n slopeLimit: f32(45, { min: 0, max: 90 }),\n stepOffset: f32(0, { min: 0 }),\n snapToGround: f32(0, { min: 0 }),\n skinWidth: f32(0.01, { min: 0 }),\n onOneWayPlatforms: bool(true),\n pushBodies: bool(false),\n interpolation: enumOf(INTERPOLATION_MODES_2D, \"interpolate\"),\n });\n}\n","import { IgnifxError } from \"@ignifx/core\";\nimport type { ErrorContext } from \"@ignifx/core\";\n\n/**\n * The diagnostic codes `@ignifx/physics-2d` owns. 2D physics shares the `11` subsystem range with\n * the 2D toolkit (`docs/architecture/15-devtools-and-diagnostics.md` §1,\n * `docs/architecture/11-2d-toolkit.md` §8), so the range is split: `@ignifx/2d` owns\n * `IGX-1101`–`IGX-1149` and this package owns `IGX-1150` upwards.\n *\n * `IGX-1101` is the one exception. `11-2d-toolkit.md` §8 names it by number — \"registering both\n * throws `IGX-1101`\" — and only this package can detect the condition, so this package registers\n * it. An app's error-code registry refuses a duplicate registration\n * (`packages/core/src/errors/error-code-registry.ts`), so `@ignifx/2d` must not declare it.\n */\n\n/**\n * Every diagnostic code `@ignifx/physics-2d` can throw or report.\n *\n * @example\n * ```ts\n * throw physics2DError(Physics2DErrorCode.queryBeforeStep, \"raycast() ran before the first step.\", {\n * context: { query: \"raycast\" },\n * });\n * ```\n *\n * @public\n */\nexport const Physics2DErrorCode = {\n /** Both `physics()` and `physics2d()` are registered on one world (`11-2d-toolkit.md` §8). */\n bothPhysicsExtensions: \"IGX-1101\",\n /** The Rapier WebAssembly module could not be instantiated. */\n rapierUnavailable: \"IGX-1150\",\n /** An entity with 2D colliders but no `Rigidbody2D` moved after its static body was placed. */\n movedStaticBody: \"IGX-1151\",\n /** A collider's layer index is outside the sixteen Rapier's interaction groups can express. */\n layerOutOfRange: \"IGX-1152\",\n /** A query ran before the first completed fixed step, so Rapier has no broadphase yet. */\n queryBeforeStep: \"IGX-1153\",\n /** A `.physicsmaterial.json` file is not an `ignifx.physicsmaterial` document this build reads. */\n invalidMaterialFile: \"IGX-1154\",\n /** The `physics2d.collisionMatrix` setting names a layer the project does not declare. */\n unknownLayer: \"IGX-1155\",\n /** A collider's geometry is degenerate: too few points, or a hull Rapier refused to build. */\n colliderGeometryInvalid: \"IGX-1156\",\n /** A 2D body was built for an entity that has a parent, whose pose is not world space. */\n bodyOnChildEntity: \"IGX-1157\",\n} as const;\n\n/**\n * The union of the codes the `Physics2DErrorCode` table declares.\n *\n * @public\n */\nexport type Physics2DErrorCode = (typeof Physics2DErrorCode)[keyof typeof Physics2DErrorCode];\n\n/**\n * The one-line message template of every code, as `ExtensionContext.registerErrorCodes` wants it.\n *\n * @public\n */\nexport const PHYSICS_2D_ERROR_MESSAGES: Readonly<Record<string, string>> = Object.freeze({\n \"IGX-1101\": \"A world uses either physics() or physics2d(), and {extension} found the other already there.\",\n \"IGX-1150\": \"The Rapier 2D WebAssembly module could not be instantiated.\",\n \"IGX-1151\": \"{entity} has 2D colliders but no Rigidbody2D, and it moved after its body was placed.\",\n \"IGX-1152\": \"{entity} is on layer {layer}, which is outside the sixteen layers Rapier 2D can filter by.\",\n \"IGX-1153\": \"{query} needs at least one completed fixed step before Rapier can answer it.\",\n \"IGX-1154\": \"{file} is not an ignifx.physicsmaterial document this build can read.\",\n \"IGX-1155\": \"physics2d.collisionMatrix names the layer {layer}, which the project does not declare.\",\n \"IGX-1156\": \"{entity} has a 2D collider whose geometry Rapier cannot build a shape from.\",\n \"IGX-1157\": \"{entity} carries a 2D physics body but is not a root entity; Rapier writes its world pose.\",\n});\n\n/**\n * Options accepted by {@link physics2DError}.\n *\n * @public\n */\nexport interface Physics2DErrorOptions {\n /** Identifiers that locate the failure. */\n readonly context?: ErrorContext;\n /** One sentence telling the developer what to do about it. */\n readonly hint?: string;\n /** The failure being wrapped, when there is one. */\n readonly cause?: unknown;\n}\n\n/**\n * Builds an `IgnifxError` carrying one of this package's codes.\n *\n * @param code - The code from the `Physics2DErrorCode` table.\n * @param message - The actionable development sentence.\n * @param options - Context identifiers, a remedy hint, and the wrapped cause.\n * @returns The error to throw or to reject with.\n *\n * @example\n * ```ts\n * throw physics2DError(Physics2DErrorCode.unknownLayer, \"physics2d.collisionMatrix names Enemy.\", {\n * context: { layer: \"Enemy\" },\n * });\n * ```\n *\n * @public\n */\nexport function physics2DError(\n code: Physics2DErrorCode,\n message: string,\n options?: Physics2DErrorOptions,\n): IgnifxError {\n return new IgnifxError(code, message, {\n ...(options?.context === undefined ? {} : { context: options.context }),\n ...(options?.hint === undefined ? {} : { hint: options.hint }),\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n });\n}\n","import { Physics2DErrorCode, physics2DError } from \"./errors.js\";\nimport type { Physics2DMaterialValues } from \"./settings.js\";\nimport type { AssetLoader, JsonObject, JsonValue, LoaderContext } from \"@ignifx/core\";\n\n/**\n * Read the shared `.physicsmaterial.json` format, ignoring `staticFriction` because Rapier has one\n * friction coefficient. Combine rules stay on colliders. Only one physics extension runs per world,\n * so the 2D and 3D loaders do not compete.\n */\n\n/**\n * The asset type name `.physicsmaterial.json` addresses resolve to.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_ASSET_TYPE = \"physicsmaterial\";\n\n/**\n * The address suffix that selects the loader.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_FILE_EXTENSION = \".physicsmaterial.json\";\n\n/**\n * The `format` string every physics-material document declares.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_FILE_FORMAT = \"ignifx.physicsmaterial\";\n\n/**\n * The file format version; `1` before ignifx 1.0.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_FORMAT_VERSION = 1;\n\n/**\n * A loaded 2D surface.\n *\n * @example\n * ```ts\n * const ice = await app.assets.load<PhysicsMaterial2D>(\"materials/ice.physicsmaterial.json\");\n * floor.addComponent(BoxCollider2D, { size: { x: 10, y: 1 }, material: ice });\n * ```\n *\n * @public\n */\nexport class PhysicsMaterial2D implements Physics2DMaterialValues {\n /** The asset type token, so `asset(PhysicsMaterial2D)` fields resolve. */\n static assetType: string = PHYSICS_MATERIAL_2D_ASSET_TYPE;\n\n /** A human-readable name, used in diagnostics. */\n readonly name: string;\n\n /** The friction coefficient. */\n readonly friction: number;\n\n /** How much of the approach speed is returned, `0` to `1`. */\n readonly restitution: number;\n\n /**\n * Wraps parsed values.\n *\n * @param name - A human-readable name.\n * @param values - Friction and restitution.\n */\n constructor(name: string, values: Physics2DMaterialValues) {\n this.name = name;\n this.friction = values.friction;\n this.restitution = values.restitution;\n }\n\n /**\n * Builds a material in code, filling in the fields the caller omitted.\n *\n * @param name - A human-readable name.\n * @param values - Any subset of the two coefficients.\n * @returns The material.\n *\n * @example\n * ```ts\n * const bouncy = PhysicsMaterial2D.fromValues(\"bouncy\", { restitution: 0.9 });\n * ```\n */\n static fromValues(name: string, values: Partial<Physics2DMaterialValues>): PhysicsMaterial2D {\n return new PhysicsMaterial2D(name, {\n friction: values.friction ?? 0.6,\n restitution: values.restitution ?? 0,\n });\n }\n}\n\n/**\n * Builds the loader for `.physicsmaterial.json` files.\n *\n * @returns The loader, ready for `ctx.registerAssetLoader`.\n *\n * @public\n */\nexport function createPhysicsMaterial2DLoader(): AssetLoader<PhysicsMaterial2D> {\n return {\n type: PHYSICS_MATERIAL_2D_ASSET_TYPE,\n extensions: [PHYSICS_MATERIAL_2D_FILE_EXTENSION],\n load: async (ctx: LoaderContext): Promise<PhysicsMaterial2D> => {\n const document = await ctx.fetchJson<JsonValue>();\n return parsePhysicsMaterial2D(ctx.address, document);\n },\n };\n}\n\n/**\n * Parses one `ignifx.physicsmaterial` document into a 2D surface.\n *\n * @param address - The address it came from, for the diagnostic.\n * @param document - The parsed JSON.\n * @returns The material.\n * @throws IgnifxError with code `IGX-1154` when the document is not one this build can read.\n *\n * @public\n */\nexport function parsePhysicsMaterial2D(address: string, document: JsonValue): PhysicsMaterial2D {\n const object = asObject(document);\n if (object === null || object[\"format\"] !== PHYSICS_MATERIAL_2D_FILE_FORMAT) {\n throw physics2DError(\n Physics2DErrorCode.invalidMaterialFile,\n `${address} is not an ${PHYSICS_MATERIAL_2D_FILE_FORMAT}.`,\n {\n context: { file: address },\n hint: `Add \"format\": \"${PHYSICS_MATERIAL_2D_FILE_FORMAT}\" to the document.`,\n },\n );\n }\n const version = numberAt(object, \"formatVersion\", PHYSICS_MATERIAL_2D_FORMAT_VERSION);\n if (version > PHYSICS_MATERIAL_2D_FORMAT_VERSION) {\n throw physics2DError(Physics2DErrorCode.invalidMaterialFile, `${address} is version ${String(version)}.`, {\n context: { file: address, version },\n hint: \"Upgrade @ignifx/physics-2d, or re-save the material from this version of the editor.\",\n });\n }\n const name = typeof object[\"name\"] === \"string\" ? object[\"name\"] : address;\n return new PhysicsMaterial2D(name, {\n friction: numberAt(object, \"friction\", 0.6),\n restitution: numberAt(object, \"restitution\", 0),\n });\n}\n\n/**\n * Views a JSON value as an object.\n *\n * @param value - The parsed JSON.\n * @returns The object, or `null` when the value is not one.\n */\nfunction asObject(value: JsonValue): JsonObject | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return null;\n }\n // The guard above is exactly `JsonObject`.\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion\n return value as JsonObject;\n}\n\n/**\n * Reads a numeric property, tolerating its absence.\n *\n * @param object - The document.\n * @param key - The property name.\n * @param fallback - What to use when the property is missing or not a number.\n * @returns The value.\n */\nfunction numberAt(object: JsonObject, key: string, fallback: number): number {\n const value = object[key];\n return typeof value === \"number\" && Number.isFinite(value) ? value : fallback;\n}\n","import { asset, bool, Component, createDefaults, enumOf, f32, optional, record, str, vec2 } from \"@ignifx/core\";\nimport { PhysicsMaterial2D } from \"../material.js\";\nimport { Physics2DHostKey } from \"../runtime/runtime-key.js\";\nimport type { RapierShape2D } from \"../lite/rapier/world.js\";\nimport type { Physics2DMaterialValues } from \"../settings.js\";\nimport type { AssetHandle, ComponentHooks, Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Build shapes from local metres multiplied by entity scale; call `rebuild` after changing scale.\n * Material assets take precedence over inline values, then project defaults. Friction and restitution\n * combine rules belong to each collider. A tilemap collider can produce several backend shapes.\n */\n\n/**\n * How two surfaces' coefficients are combined when they touch, mirroring Rapier's\n * `CoefficientCombineRule`.\n *\n * @public\n */\nexport const COMBINE_RULES = [\"average\", \"min\", \"multiply\", \"max\"] as const;\n\n/**\n * The union of {@link COMBINE_RULES}.\n *\n * @public\n */\nexport type CombineRule = (typeof COMBINE_RULES)[number];\n\n/**\n * One Rapier shape a collider stands for, positioned in the body's local frame.\n *\n * @internal\n */\nexport interface CollectedShape2D {\n /** The geometry. */\n readonly shape: RapierShape2D;\n /** Where it sits relative to the entity origin, in metres. */\n readonly offset: Vec2Like;\n /** How it is turned about that point, in radians counter-clockwise. */\n readonly rotation: number;\n /** Whether it is solid only when a character controller comes down onto it. */\n readonly oneWay: boolean;\n}\n\n/**\n * The fields every 2D collider declares.\n *\n * @returns The shared field declarations, ready to spread into a collider's own schema.\n *\n * @public\n */\nexport function collider2DFields(): Schema {\n return {\n offset: vec2(),\n isTrigger: bool(false),\n material: asset(PhysicsMaterial2D),\n inlineMaterial: optional(\n record({\n friction: f32(0.6, { min: 0 }),\n restitution: f32(0, { min: 0, max: 1 }),\n }),\n ),\n frictionCombine: enumOf(COMBINE_RULES, \"average\"),\n restitutionCombine: enumOf(COMBINE_RULES, \"average\"),\n oneWay: bool(false),\n layerOverride: str(\"\"),\n };\n}\n\n/**\n * The base class of every 2D collider.\n *\n * @public\n */\nexport abstract class Collider2D extends Component implements ComponentHooks {\n /** Several colliders on one entity make one compound body. */\n static allowMultiple = true;\n\n /** The shape's offset from the entity origin, in local metres. */\n declare offset: Vec2Like;\n\n /** When `true` the shape reports overlaps and resolves no contacts. */\n declare isTrigger: boolean;\n\n /** A `.physicsmaterial.json` reference; wins over {@link Collider2D.inlineMaterial}. */\n declare material: AssetHandle<PhysicsMaterial2D> | null;\n\n /** An inline surface, used when {@link Collider2D.material} is `null`. */\n declare inlineMaterial: Physics2DMaterialValues | null;\n\n /** How this surface's friction combines with the one it touches. */\n declare frictionCombine: CombineRule;\n\n /** How this surface's restitution combines with the one it touches. */\n declare restitutionCombine: CombineRule;\n\n /**\n * Whether this is a one-way platform: a `CharacterController2D` with\n * `onOneWayPlatforms` passes up through it and lands on it coming down. Rigid bodies are\n * unaffected — one-way support is a character-controller feature in the MVP.\n */\n declare oneWay: boolean;\n\n /** The name of the layer this collider filters as, or `\"\"` to use `entity.layer`. */\n declare layerOverride: string;\n\n /** Applies the shared defaults. A concrete collider calls `super()` and then applies its own. */\n constructor() {\n super();\n Object.assign(this, createDefaults(collider2DFields()));\n }\n\n /** Marks the entity's body for a rebuild at the start of the next fixed step. */\n onAttach(): void {\n this.rebuild();\n }\n\n /** Marks the entity's body for a rebuild, which removes this collider from it. */\n onDetach(): void {\n this.rebuild();\n }\n\n /**\n * Rebuilds the entity's body and shapes at the start of the next fixed step. Call it after\n * changing a size, an offset, `isTrigger`, or the entity's scale.\n *\n * @example\n * ```ts\n * box.size = { x: 2, y: 2 };\n * box.rebuild();\n * ```\n */\n rebuild(): void {\n this.app.services.tryGet(Physics2DHostKey)?.runtime?.markDirty(this.entity);\n }\n\n /**\n * Resolves the surface this collider presents to Rapier.\n *\n * @param fallback - The world's `physics2d.defaultMaterial`.\n * @returns The asset's values, the inline values, or the fallback.\n */\n resolveMaterial(fallback: Physics2DMaterialValues): Physics2DMaterialValues {\n return this.material?.value ?? this.inlineMaterial ?? fallback;\n }\n\n /**\n * Appends the Rapier shapes this collider stands for.\n *\n * @param scale - The entity's lossy scale in the 2D plane.\n * @param out - The list to append to.\n *\n * @internal\n */\n abstract collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void;\n}\n","import { array, createDefaults, defineSchema, enumOf, f32, vec2 } from \"@ignifx/core\";\nimport { Physics2DErrorCode, physics2DError } from \"../errors.js\";\nimport { Collider2D, collider2DFields } from \"./collider.js\";\nimport type { CollectedShape2D } from \"./collider.js\";\nimport type { Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Polygon colliders use convex hulls; represent concave geometry with multiple colliders.\n * Edge colliders are thin, open polylines for static contours: fast bodies can tunnel through them,\n * and bodies starting inside are not pushed out.\n */\n\n/** A quarter turn in radians, which is how a horizontal capsule is expressed. */\nconst QUARTER_TURN = Math.PI / 2;\n\n/**\n * The axis a 2D capsule stands along.\n *\n * @public\n */\nexport const CAPSULE_DIRECTIONS_2D = [\"x\", \"y\"] as const;\n\n/**\n * The union of {@link CAPSULE_DIRECTIONS_2D}.\n *\n * @public\n */\nexport type CapsuleDirection2D = (typeof CAPSULE_DIRECTIONS_2D)[number];\n\n/**\n * An axis-aligned box collider, sized in local metres.\n *\n * @example\n * ```ts\n * const floor = world.createEntity(\"Floor\");\n * floor.addComponent(BoxCollider2D, { size: { x: 20, y: 1 } });\n * ```\n *\n * @public\n */\nexport class BoxCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/BoxCollider2D\";\n\n /** The serialized field declarations (ADR-0004). */\n static schema: Schema = boxSchema();\n\n declare size: Vec2Like;\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(BoxCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n out.push({\n shape: {\n kind: \"box\",\n halfWidth: (this.size.x * abs(scale.x)) / 2,\n halfHeight: (this.size.y * abs(scale.y)) / 2,\n },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * A circle collider. Non-uniform scale is not representable as a circle, so the larger scale axis\n * wins — the same rule Unity applies.\n *\n * @public\n */\nexport class CircleCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/CircleCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = circleSchema();\n\n declare radius: number;\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(CircleCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n out.push({\n shape: { kind: \"circle\", radius: this.radius * Math.max(abs(scale.x), abs(scale.y)) },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * A capsule collider: a rectangle with semicircular caps, standing along X or Y.\n *\n * @remarks\n * `height` is the **total** tip-to-tip height, so a capsule shorter than `2 * radius` degenerates to\n * a circle of that radius rather than inverting. Rapier's capsule always stands along Y, so an\n * `x` capsule is expressed by swapping the axes of the half-extents — which means an `x` capsule\n * and a rotated `y` capsule are the same shape.\n *\n * @public\n */\nexport class CapsuleCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/CapsuleCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = capsuleSchema();\n\n declare radius: number;\n\n declare height: number;\n\n declare direction: CapsuleDirection2D;\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(CapsuleCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n const alongY = this.direction === \"y\";\n const radialScale = alongY ? abs(scale.x) : abs(scale.y);\n const axisScale = alongY ? abs(scale.y) : abs(scale.x);\n const radius = this.radius * radialScale;\n const half = Math.max(0, (this.height * axisScale) / 2 - radius);\n out.push({\n shape: { kind: \"capsule\", halfHeight: half, radius },\n offset: scaled(this.offset, scale),\n // Rapier's 2D capsule always stands along Y (`geometry/shape.d.ts`, `Capsule`), so a\n // horizontal one is the same shape turned a quarter turn on the collider itself.\n rotation: alongY ? 0 : QUARTER_TURN,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * A convex polygon collider, wound in either direction, in local metres.\n *\n * @remarks\n * Rapier builds the **convex hull** of the points, so a concave outline is silently rounded out.\n * Model a concave shape as several `PolygonCollider2D`s on one entity, or as an\n * {@link EdgeCollider2D} when it is an open contour.\n *\n * @public\n */\nexport class PolygonCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/PolygonCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = pointsSchema();\n\n declare points: Vec2Like[];\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(PolygonCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n * @throws IgnifxError with code `IGX-1156` when fewer than three points are given.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n if (this.points.length < 3) {\n throw degenerate(this.entity.name, \"PolygonCollider2D\", \"at least three points\");\n }\n out.push({\n shape: { kind: \"polygon\", points: packPoints(this.points, scale) },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * An open chain of line segments — a platformer's ground contour.\n *\n * @public\n */\nexport class EdgeCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/EdgeCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = pointsSchema();\n\n declare points: Vec2Like[];\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(EdgeCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n * @throws IgnifxError with code `IGX-1156` when fewer than two points are given.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n if (this.points.length < 2) {\n throw degenerate(this.entity.name, \"EdgeCollider2D\", \"at least two points\");\n }\n out.push({\n shape: { kind: \"polyline\", points: packPoints(this.points, scale) },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * Builds the `BoxCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction boxSchema(): Schema {\n return defineSchema({ ...collider2DFields(), size: vec2({ x: 1, y: 1 }) });\n}\n\n/**\n * Builds the `CircleCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction circleSchema(): Schema {\n return defineSchema({ ...collider2DFields(), radius: f32(0.5, { min: 0 }) });\n}\n\n/**\n * Builds the `CapsuleCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction capsuleSchema(): Schema {\n return defineSchema({\n ...collider2DFields(),\n radius: f32(0.25, { min: 0 }),\n height: f32(1, { min: 0 }),\n direction: enumOf(CAPSULE_DIRECTIONS_2D, \"y\"),\n });\n}\n\n/**\n * Builds the schema the two point-list colliders share.\n *\n * @returns The schema.\n */\nfunction pointsSchema(): Schema {\n return defineSchema({ ...collider2DFields(), points: array(vec2()) });\n}\n\n/**\n * The absolute value of a scale component; a negative scale mirrors geometry, so the shape uses the\n * magnitude.\n *\n * @param value - The scale component.\n * @returns Its magnitude.\n */\nfunction abs(value: number): number {\n return Math.abs(value);\n}\n\n/**\n * Scales a local offset into shape space.\n *\n * @param offset - The authored offset.\n * @param scale - The lossy 2D scale.\n * @returns A fresh vector; shape construction is not a per-frame path.\n */\nfunction scaled(offset: Vec2Like, scale: Vec2Like): Vec2Like {\n return { x: offset.x * scale.x, y: offset.y * scale.y };\n}\n\n/**\n * Packs a point list into the interleaved `Float32Array` Rapier's shape constructors take.\n *\n * @param points - The authored points.\n * @param scale - The lossy 2D scale.\n * @returns A fresh buffer.\n */\nfunction packPoints(points: readonly Vec2Like[], scale: Vec2Like): Float32Array {\n const packed = new Float32Array(points.length * 2);\n for (let index = 0; index < points.length; index += 1) {\n const point = points[index];\n if (point === undefined) {\n continue;\n }\n packed[index * 2] = point.x * scale.x;\n packed[index * 2 + 1] = point.y * scale.y;\n }\n return packed;\n}\n\n/**\n * Builds the \"that geometry is degenerate\" failure.\n *\n * @param entity - The entity name.\n * @param component - Which collider complained.\n * @param requirement - What it needed.\n * @returns The error to throw.\n */\nfunction degenerate(entity: string, component: string, requirement: string): ReturnType<typeof physics2DError> {\n return physics2DError(Physics2DErrorCode.colliderGeometryInvalid, `${component} on ${entity} needs ${requirement}.`, {\n context: { entity, component },\n hint: `Give the collider ${requirement}.`,\n });\n}\n","import { createDefaults, defineSchema } from \"@ignifx/core\";\nimport { Collider2D, collider2DFields } from \"./collider.js\";\nimport type { CollectedShape2D } from \"./collider.js\";\nimport type { TilemapCollisionData } from \"@ignifx/2d\";\nimport type { Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Read tile collision data through the optional 2D toolkit's type and rebuild when its version changes.\n * Closed polylines preserve concave tile outlines that convex hulls would fill in. They are thin\n * surfaces, so a body starting inside the map is not pushed out.\n */\n\n/**\n * The collision surface of a tilemap, as one static body's worth of Rapier shapes.\n *\n * @example\n * ```ts\n * const map = world.createEntity(\"Map\");\n * const collider = map.addComponent(TilemapCollider2D);\n * collider.collisionData = tilemap.collisionData;\n * ```\n *\n * @public\n */\nexport class TilemapCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/TilemapCollider2D\";\n\n /** The serialized field declarations; the geometry itself comes from the tilemap asset. */\n static schema: Schema = tilemapSchema();\n\n /** The merged chunk geometry, or `null` until a tilemap supplies it. */\n #data: TilemapCollisionData | null = null;\n\n /** The `version` of the data the current shapes were built from. */\n #builtVersion = -1;\n\n /** Applies the shared defaults. */\n constructor() {\n super();\n Object.assign(this, createDefaults(TilemapCollider2D.schema));\n }\n\n /**\n * The merged chunk geometry this collider builds shapes from.\n *\n * @returns The data, or `null` when none has been supplied.\n */\n get collisionData(): TilemapCollisionData | null {\n return this.#data;\n }\n\n /**\n * Replaces the tilemap geometry and schedules a rebuild.\n *\n * @param value - The merged chunk geometry, or `null` to drop the shapes.\n */\n set collisionData(value: TilemapCollisionData | null) {\n this.#data = value;\n this.#builtVersion = -1;\n this.rebuild();\n }\n\n /**\n * Whether the supplied data has changed since the shapes were built. The runtime polls this once\n * per fixed step, which is how a tile edit reaches physics without a cross-package subscription.\n *\n * @returns `true` when a rebuild is due.\n *\n * @internal\n */\n needsRebuild(): boolean {\n return (this.#data?.version ?? -1) !== this.#builtVersion;\n }\n\n /**\n * Appends one shape per merged outline and one per one-way edge.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n const data = this.#data;\n this.#builtVersion = data?.version ?? -1;\n if (data === null) {\n return;\n }\n const offset = { x: this.offset.x * scale.x, y: this.offset.y * scale.y };\n for (const chunk of data.chunks) {\n for (const polygon of chunk.polygons) {\n if (polygon.length < 3) {\n continue;\n }\n out.push({\n shape: { kind: \"polyline\", points: closedLoop(polygon, scale) },\n offset,\n rotation: 0,\n oneWay: false,\n });\n }\n for (const edge of chunk.oneWayEdges) {\n out.push({\n shape: { kind: \"polyline\", points: segment(edge[0], edge[1], scale) },\n offset,\n rotation: 0,\n oneWay: true,\n });\n }\n }\n }\n}\n\n/**\n * Builds the `TilemapCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction tilemapSchema(): Schema {\n return defineSchema({ ...collider2DFields() });\n}\n\n/**\n * Packs an outline as a closed chain of vertices.\n *\n * @param points - The outline, wound counter-clockwise.\n * @param scale - The entity's lossy 2D scale.\n * @returns A fresh buffer whose last vertex repeats the first.\n */\nfunction closedLoop(points: readonly Vec2Like[], scale: Vec2Like): Float32Array {\n const packed = new Float32Array((points.length + 1) * 2);\n for (let index = 0; index < points.length; index += 1) {\n const point = points[index];\n if (point === undefined) {\n continue;\n }\n packed[index * 2] = point.x * scale.x;\n packed[index * 2 + 1] = point.y * scale.y;\n }\n packed[points.length * 2] = packed[0] ?? 0;\n packed[points.length * 2 + 1] = packed[1] ?? 0;\n return packed;\n}\n\n/**\n * Packs one one-way edge as a two-vertex chain.\n *\n * @param from - The edge's start.\n * @param to - The edge's end.\n * @param scale - The entity's lossy 2D scale.\n * @returns A fresh buffer.\n */\nfunction segment(from: Vec2Like, to: Vec2Like, scale: Vec2Like): Float32Array {\n return new Float32Array([from.x * scale.x, from.y * scale.y, to.x * scale.x, to.y * scale.y]);\n}\n","import { init } from \"@dimforge/rapier2d-compat\";\n\n/**\n * Initialise Rapier's embedded WebAssembly before making backend calls.\n * The compat build includes the binary, so both Node and browser apps need no separate asset.\n * Rapier classes are imported statically by the adapters; delaying `init` does not split that bundle.\n *\n * @internal\n */\n\n/**\n * Instantiates Rapier's WebAssembly module.\n *\n * @remarks\n * Rapier's own `init()` is idempotent and caches the instantiated module in the package's module\n * scope, so the second and later apps in one process pay nothing.\n *\n * @returns A promise that settles once Rapier is usable.\n *\n * @internal\n */\nexport async function loadRapier(): Promise<void> {\n await init();\n}\n","import {\n ActiveCollisionTypes,\n ActiveEvents,\n Ball,\n Capsule,\n CoefficientCombineRule,\n ColliderDesc,\n ConvexPolygon,\n Cuboid,\n EventQueue,\n Polyline,\n QueryFilterFlags,\n Ray,\n RigidBodyDesc,\n World,\n} from \"@dimforge/rapier2d-compat\";\nimport type { Collider, RigidBody, Shape } from \"@dimforge/rapier2d-compat\";\nimport type { MutableVec2, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Keep Rapier calls inside the adapter (ADR-0006).\n * Queries need a built broadphase. Prime after collider-set changes only: a zero-length step can\n * still affect a running simulation. Interaction groups support sixteen layers; event-enabled\n * colliders include non-dynamic pairs so characters can enter static sensors.\n */\n\n/**\n * Rapier's physics world.\n *\n * @internal\n */\nexport type RapierWorld = World;\n\n/**\n * One Rapier rigid body.\n *\n * @internal\n */\nexport type RapierBody = RigidBody;\n\n/**\n * One Rapier collider.\n *\n * @internal\n */\nexport type RapierCollider = Collider;\n\n/**\n * Rapier's collision-event collector.\n *\n * @internal\n */\nexport type RapierEventQueue = EventQueue;\n\n/**\n * How a body moves, in the three flavours ignifx exposes.\n *\n * @internal\n */\nexport const BodyMotion2D = {\n /** Simulated by the solver. */\n dynamic: \"dynamic\",\n /** Moved by the game, pushes dynamic bodies, ignores forces. */\n kinematic: \"kinematic\",\n /** Never moves. */\n static: \"static\",\n} as const;\n\n/**\n * The union of {@link BodyMotion2D}.\n *\n * @internal\n */\nexport type BodyMotion2D = (typeof BodyMotion2D)[keyof typeof BodyMotion2D];\n\n/**\n * How two surfaces' friction or restitution coefficients are combined, mirroring Rapier's\n * `CoefficientCombineRule` (`dynamics/coefficient_combine_rule.d.ts`).\n *\n * @internal\n */\nexport const CombineRule2D = {\n /** The arithmetic mean; Rapier's default. */\n average: \"average\",\n /** The smaller of the two. */\n min: \"min\",\n /** The product. */\n multiply: \"multiply\",\n /** The larger of the two. */\n max: \"max\",\n} as const;\n\n/**\n * The union of {@link CombineRule2D}.\n *\n * @internal\n */\nexport type CombineRule2D = (typeof CombineRule2D)[keyof typeof CombineRule2D];\n\n/**\n * A collision shape, described in metres in the body's local frame.\n *\n * @internal\n */\nexport type RapierShape2D =\n | { readonly kind: \"box\"; readonly halfWidth: number; readonly halfHeight: number }\n | { readonly kind: \"circle\"; readonly radius: number }\n | { readonly kind: \"capsule\"; readonly halfHeight: number; readonly radius: number }\n | { readonly kind: \"polygon\"; readonly points: Float32Array }\n | { readonly kind: \"polyline\"; readonly points: Float32Array };\n\n/** What {@link createCollider} needs beyond the geometry. */\nexport interface ColliderOptions2D {\n /** The shape's offset from the body origin, in metres. */\n readonly offset: Vec2Like;\n /** The shape's rotation about the body origin, in radians. */\n readonly rotation: number;\n /** Whether the shape reports overlaps instead of resolving contacts. */\n readonly sensor: boolean;\n /** The packed membership/filter word from {@link interactionGroups}. */\n readonly groups: number;\n /** The dynamic friction coefficient. */\n readonly friction: number;\n /** How much approach speed is returned, `0` to `1`. */\n readonly restitution: number;\n /** How two frictions are combined. */\n readonly frictionCombine: CombineRule2D;\n /** How two restitutions are combined. */\n readonly restitutionCombine: CombineRule2D;\n /** Whether this collider raises collision and intersection events. */\n readonly events: boolean;\n}\n\n/** What {@link createBody} needs. */\nexport interface BodyOptions2D {\n /** How the body moves. */\n readonly motion: BodyMotion2D;\n /** The world position in metres. */\n readonly position: Vec2Like;\n /** The world rotation in radians, counter-clockwise about +Z. */\n readonly rotation: number;\n /** Multiplies world gravity for this body. */\n readonly gravityScale: number;\n /** Linear damping per second. */\n readonly linearDamping: number;\n /** Angular damping per second. */\n readonly angularDamping: number;\n /** Whether rotation is locked. */\n readonly freezeRotation: boolean;\n /** Extra mass added on top of the colliders' own, in kilograms; `0` adds none. */\n readonly additionalMass: number;\n /** Whether the body starts asleep. */\n readonly startAsleep: boolean;\n}\n\n/** One drained collision or intersection event. */\nexport interface CollisionEvent2D {\n /** The first collider's handle. */\n readonly first: number;\n /** The second collider's handle. */\n readonly second: number;\n /** `true` for an enter, `false` for an exit. */\n readonly started: boolean;\n}\n\n/** One contact between two colliders, written into a caller-owned record. */\nexport interface ContactData2D {\n /** Whether the pair has at least one contact this step. */\n touching: boolean;\n /** The world-space contact point. */\n readonly point: MutableVec2;\n /** The world-space contact normal, pointing away from the first collider. */\n readonly normal: MutableVec2;\n /** The magnitude of the impulse the solver applied. */\n impulse: number;\n}\n\n/** What {@link castRay} writes. */\nexport interface RayResult2D {\n /** Whether anything was hit. */\n hit: boolean;\n /** The collider that was hit, or `null`. */\n collider: RapierCollider | null;\n /** The distance from the origin along the (normalised) direction. */\n distance: number;\n}\n\n/** What {@link castShape} writes. */\nexport interface ShapeCastResult2D {\n /** Whether anything was hit. */\n hit: boolean;\n /** The collider that was hit, or `null`. */\n collider: RapierCollider | null;\n /** The distance travelled before contact. */\n distance: number;\n}\n\n/** Radians per full turn, for the degree conversions the components use. */\nconst TAU = Math.PI * 2;\n\n/** How wide one half of an `InteractionGroups` word is. */\nconst GROUP_BITS = 16;\n\n/** Every bit of one half. */\nconst GROUP_MASK = 0xff_ff;\n\n/**\n * The number of layers Rapier's 16-bit interaction groups can express\n * (`geometry/interaction_groups.d.ts`).\n *\n * @internal\n */\nexport const RAPIER_LAYER_LIMIT: number = GROUP_BITS;\n\n/**\n * Packs a membership mask and a collide mask into Rapier's single `InteractionGroups` word.\n *\n * @param membership - Which layers the collider belongs to, in the low 16 bits.\n * @param filter - Which layers it may collide with, in the low 16 bits.\n * @returns The packed word.\n *\n * @internal\n */\nexport function interactionGroups(membership: number, filter: number): number {\n return ((membership & GROUP_MASK) << GROUP_BITS) | (filter & GROUP_MASK);\n}\n\n/**\n * Creates the physics world.\n *\n * @param gravity - The acceleration in metres per second squared.\n * @returns The world.\n *\n * @internal\n */\nexport function createWorld(gravity: Vec2Like): RapierWorld {\n return new World({ x: gravity.x, y: gravity.y });\n}\n\n/**\n * Releases the world and every WebAssembly object it owns.\n *\n * @param world - The world.\n *\n * @internal\n */\nexport function destroyWorld(world: RapierWorld): void {\n world.free();\n}\n\n/**\n * Replaces world gravity.\n *\n * @param world - The world.\n * @param gravity - The new acceleration.\n *\n * @internal\n */\nexport function setGravity(world: RapierWorld, gravity: Vec2Like): void {\n world.gravity.x = gravity.x;\n world.gravity.y = gravity.y;\n}\n\n/**\n * Sets the length of the next step.\n *\n * @param world - The world.\n * @param seconds - The fixed step in seconds.\n *\n * @internal\n */\nexport function setTimestep(world: RapierWorld, seconds: number): void {\n world.timestep = seconds;\n}\n\n/**\n * Sets how many iterations the constraint solver runs.\n *\n * @param world - The world.\n * @param iterations - The iteration count; Rapier's default is 4.\n *\n * @internal\n */\nexport function setSolverIterations(world: RapierWorld, iterations: number): void {\n world.numSolverIterations = iterations;\n}\n\n/**\n * Advances the simulation by one timestep, collecting events.\n *\n * @param world - The world.\n * @param queue - The event collector.\n *\n * @internal\n */\nexport function stepWorld(world: RapierWorld, queue: RapierEventQueue): void {\n world.step(queue);\n}\n\n/**\n * Rebuilds the broadphase without advancing time, by running a zero-length step.\n *\n * @remarks\n * Rapier only refreshes its acceleration structure inside `World.step`, so a collider created since\n * the last step is invisible to queries and to the character controller (S6.2). The queue is passed\n * so an enter event raised by the priming step is still delivered.\n *\n * @param world - The world.\n * @param queue - The event collector.\n *\n * @internal\n */\nexport function primeWorld(world: RapierWorld, queue: RapierEventQueue): void {\n const timestep = world.timestep;\n world.timestep = 0;\n world.step(queue);\n world.timestep = timestep;\n}\n\n/**\n * Creates the event collector.\n *\n * @remarks\n * `autoDrain: false` — the collector must survive both the priming step and the real step of one\n * fixed update, and the runtime drains it once at the end of that update.\n *\n * @returns The queue.\n *\n * @internal\n */\nexport function createEventQueue(): RapierEventQueue {\n return new EventQueue(false);\n}\n\n/**\n * Releases the event collector.\n *\n * @param queue - The queue.\n *\n * @internal\n */\nexport function destroyEventQueue(queue: RapierEventQueue): void {\n queue.free();\n}\n\n/**\n * Hands every buffered collision and intersection event to a sink and clears the buffer.\n *\n * @param queue - The queue.\n * @param sink - Called once per event with the two collider handles and the enter/exit flag.\n *\n * @internal\n */\nexport function drainCollisions(queue: RapierEventQueue, sink: (event: CollisionEvent2D) => void): void {\n queue.drainCollisionEvents((first: number, second: number, started: boolean): void => {\n sink({ first, second, started });\n });\n}\n\n/**\n * Creates a rigid body.\n *\n * @param world - The world.\n * @param options - Motion, pose, damping, gravity scale, and mass.\n * @returns The body.\n *\n * @internal\n */\nexport function createBody(world: RapierWorld, options: BodyOptions2D): RapierBody {\n const description = descriptionFor(options.motion)\n .setTranslation(options.position.x, options.position.y)\n .setRotation(options.rotation)\n .setGravityScale(options.gravityScale)\n .setLinearDamping(options.linearDamping)\n .setAngularDamping(options.angularDamping)\n .setSleeping(options.startAsleep);\n if (options.freezeRotation) {\n description.lockRotations();\n }\n if (options.additionalMass > 0) {\n description.setAdditionalMass(options.additionalMass);\n }\n return world.createRigidBody(description);\n}\n\n/**\n * Removes a body and every collider attached to it.\n *\n * @param world - The world.\n * @param body - The body.\n *\n * @internal\n */\nexport function destroyBody(world: RapierWorld, body: RapierBody): void {\n world.removeRigidBody(body);\n}\n\n/**\n * Reads a body's world pose without allocating.\n *\n * @param body - The body.\n * @param out - The vector the translation is written into.\n * @returns The rotation in radians.\n *\n * @internal\n */\nexport function readBodyPose(body: RapierBody, out: MutableVec2): number {\n body.translation(out);\n return body.rotation();\n}\n\n/**\n * Moves a body immediately, as a teleport.\n *\n * @param body - The body.\n * @param position - The new world position.\n * @param rotation - The new rotation in radians.\n *\n * @internal\n */\nexport function setBodyPose(body: RapierBody, position: Vec2Like, rotation: number): void {\n body.setTranslation({ x: position.x, y: position.y }, true);\n body.setRotation(rotation, true);\n}\n\n/**\n * Asks Rapier to interpolate a kinematic body to a pose over the next step, which is what makes it\n * push dynamic bodies instead of tunnelling through them.\n *\n * @param body - The body.\n * @param position - The pose to reach.\n * @param rotation - The rotation to reach, in radians.\n *\n * @internal\n */\nexport function setNextKinematicPose(body: RapierBody, position: Vec2Like, rotation: number): void {\n body.setNextKinematicTranslation({ x: position.x, y: position.y });\n body.setNextKinematicRotation(rotation);\n}\n\n/**\n * Reads a body's linear velocity without allocating.\n *\n * @param body - The body.\n * @param out - The vector to write.\n *\n * @internal\n */\nexport function readLinearVelocity(body: RapierBody, out: MutableVec2): void {\n body.linvel(out);\n}\n\n/**\n * Replaces a body's linear velocity.\n *\n * @param body - The body.\n * @param velocity - Metres per second.\n *\n * @internal\n */\nexport function writeLinearVelocity(body: RapierBody, velocity: Vec2Like): void {\n body.setLinvel({ x: velocity.x, y: velocity.y }, true);\n}\n\n/**\n * Reads a body's angular velocity.\n *\n * @param body - The body.\n * @returns Radians per second, counter-clockwise.\n *\n * @internal\n */\nexport function readAngularVelocity(body: RapierBody): number {\n return body.angvel();\n}\n\n/**\n * Replaces a body's angular velocity.\n *\n * @param body - The body.\n * @param velocity - Radians per second.\n *\n * @internal\n */\nexport function writeAngularVelocity(body: RapierBody, velocity: number): void {\n body.setAngvel(velocity, true);\n}\n\n/**\n * Adds a force that lasts for one step.\n *\n * @param body - The body.\n * @param force - Newtons.\n * @param point - Where to apply it, or `null` for the centre of mass.\n *\n * @internal\n */\nexport function addForce(body: RapierBody, force: Vec2Like, point: Vec2Like | null): void {\n if (point === null) {\n body.addForce({ x: force.x, y: force.y }, true);\n return;\n }\n body.addForceAtPoint({ x: force.x, y: force.y }, { x: point.x, y: point.y }, true);\n}\n\n/**\n * Applies an instantaneous impulse.\n *\n * @param body - The body.\n * @param impulse - Newton-seconds.\n * @param point - Where to apply it, or `null` for the centre of mass.\n *\n * @internal\n */\nexport function applyImpulse(body: RapierBody, impulse: Vec2Like, point: Vec2Like | null): void {\n if (point === null) {\n body.applyImpulse({ x: impulse.x, y: impulse.y }, true);\n return;\n }\n body.applyImpulseAtPoint({ x: impulse.x, y: impulse.y }, { x: point.x, y: point.y }, true);\n}\n\n/**\n * Adds a torque that lasts for one step.\n *\n * @param body - The body.\n * @param torque - Newton-metres, positive counter-clockwise.\n *\n * @internal\n */\nexport function addTorque(body: RapierBody, torque: number): void {\n body.addTorque(torque, true);\n}\n\n/**\n * Whether a body is asleep, which is what `activeBodies` counts the complement of.\n *\n * @param body - The body.\n * @returns `true` when Rapier has put it to sleep.\n *\n * @internal\n */\nexport function isBodySleeping(body: RapierBody): boolean {\n return body.isSleeping();\n}\n\n/**\n * The mass Rapier computed for a body from its colliders and any additional mass.\n *\n * @param body - The body.\n * @returns Kilograms.\n *\n * @internal\n */\nexport function bodyMass(body: RapierBody): number {\n return body.mass();\n}\n\n/**\n * Sets how many kilograms a square metre of a collider weighs. Rapier derives the body's mass and\n * angular inertia from this and the collider's area.\n *\n * @param collider - The collider.\n * @param density - Kilograms per square metre.\n *\n * @internal\n */\nexport function setColliderDensity(collider: RapierCollider, density: number): void {\n collider.setDensity(density);\n}\n\n/**\n * Recomputes a body's mass and inertia from its colliders, after their densities changed.\n *\n * @param body - The body.\n *\n * @internal\n */\nexport function recomputeMass(body: RapierBody): void {\n body.recomputeMassPropertiesFromColliders();\n}\n\n/**\n * Creates a collider and attaches it to a body.\n *\n * @param world - The world.\n * @param body - The body it belongs to.\n * @param shape - The geometry, in body-local metres.\n * @param options - Offset, sensor flag, filter word, surface, and event flags.\n * @returns The collider, or `null` when Rapier refuses the geometry — a convex hull of collinear or\n * duplicated points, say, which throws inside its WebAssembly rather than returning nothing.\n *\n * @internal\n */\nexport function createCollider(\n world: RapierWorld,\n body: RapierBody,\n shape: RapierShape2D,\n options: ColliderOptions2D,\n): RapierCollider | null {\n const description = describeShape(shape);\n if (description === null) {\n return null;\n }\n description\n .setTranslation(options.offset.x, options.offset.y)\n .setRotation(options.rotation)\n .setSensor(options.sensor)\n .setCollisionGroups(options.groups)\n .setFriction(options.friction)\n .setRestitution(options.restitution)\n .setFrictionCombineRule(combineRule(options.frictionCombine))\n .setRestitutionCombineRule(combineRule(options.restitutionCombine));\n if (options.events) {\n description\n .setActiveEvents(ActiveEvents.COLLISION_EVENTS)\n // `DEFAULT` covers dynamic-versus-anything only, so a kinematic character would never report\n // entering a static sensor (measured, S6.2).\n .setActiveCollisionTypes(ActiveCollisionTypes.ALL);\n }\n try {\n return world.createCollider(description, body);\n } catch {\n // A degenerate hull — collinear or duplicate points — throws inside Rapier's WebAssembly rather\n // than returning `null` from `ColliderDesc.convexHull`, so the refusal is caught here and\n // reported as `IGX-1156` by the caller.\n return null;\n }\n}\n\n/**\n * Turns a collider's collision and intersection events on or off without rebuilding it.\n *\n * @param collider - The collider.\n * @param enabled - Whether it reports events.\n *\n * @internal\n */\nexport function setColliderEvents(collider: RapierCollider, enabled: boolean): void {\n collider.setActiveEvents(enabled ? ActiveEvents.COLLISION_EVENTS : ActiveEvents.NONE);\n collider.setActiveCollisionTypes(enabled ? ActiveCollisionTypes.ALL : ActiveCollisionTypes.DEFAULT);\n}\n\n/**\n * Reads a collider's world position without allocating.\n *\n * @param collider - The collider.\n * @param out - The vector to write.\n *\n * @internal\n */\nexport function readColliderTranslation(collider: RapierCollider, out: MutableVec2): void {\n collider.translation(out);\n}\n\n/**\n * Replaces a collider's filter word after a layer change.\n *\n * @param collider - The collider.\n * @param groups - The packed membership/filter word.\n *\n * @internal\n */\nexport function setColliderGroups(collider: RapierCollider, groups: number): void {\n collider.setCollisionGroups(groups);\n}\n\n/**\n * Removes one collider.\n *\n * @param world - The world.\n * @param collider - The collider.\n *\n * @internal\n */\nexport function destroyCollider(world: RapierWorld, collider: RapierCollider): void {\n world.removeCollider(collider, true);\n}\n\n/**\n * The stable integer Rapier identifies a collider by, which is what its event queue reports.\n *\n * @param collider - The collider.\n * @returns The handle.\n *\n * @internal\n */\nexport function colliderHandle(collider: RapierCollider): number {\n return collider.handle;\n}\n\n/**\n * Whether a collider is a sensor, which is how a trigger event is told from a contact.\n *\n * @param collider - The collider.\n * @returns `true` for a sensor.\n *\n * @internal\n */\nexport function isColliderSensor(collider: RapierCollider): boolean {\n return collider.isSensor();\n}\n\n/**\n * Reads the first contact of a collider pair.\n *\n * @param world - The world.\n * @param first - One collider.\n * @param second - The other.\n * @param out - The record to write.\n *\n * @internal\n */\nexport function readContact(\n world: RapierWorld,\n first: RapierCollider,\n second: RapierCollider,\n out: ContactData2D,\n): void {\n out.touching = false;\n out.impulse = 0;\n world.contactPair(first, second, (manifold, flipped: boolean): void => {\n const normal = manifold.normal();\n const sign = flipped ? -1 : 1;\n out.normal.set(normal.x * sign, normal.y * sign);\n const point = manifold.numSolverContacts() > 0 ? manifold.solverContactPoint(0) : null;\n if (point !== null) {\n out.point.set(point.x, point.y);\n }\n out.impulse = manifold.numContacts() > 0 ? manifold.contactImpulse(0) : 0;\n out.touching = true;\n });\n}\n\n/**\n * Casts a ray and writes the nearest hit.\n *\n * @param world - The world.\n * @param origin - The world-space origin.\n * @param direction - A unit direction.\n * @param maxDistance - How far to travel.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param outPoint - Receives the contact point.\n * @param outNormal - Receives the contact normal.\n * @param out - Receives the hit flag, collider, and distance.\n *\n * @internal\n */\nexport function castRay(\n world: RapierWorld,\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number,\n groups: number,\n hitSensors: boolean,\n outPoint: MutableVec2,\n outNormal: MutableVec2,\n out: RayResult2D,\n): void {\n out.hit = false;\n out.collider = null;\n out.distance = 0;\n const ray = new Ray({ x: origin.x, y: origin.y }, { x: direction.x, y: direction.y });\n const hit = world.castRayAndGetNormal(ray, maxDistance, true, sensorFlags(hitSensors), groups);\n if (hit === null) {\n return;\n }\n out.hit = true;\n out.collider = hit.collider;\n out.distance = hit.timeOfImpact;\n outPoint.set(origin.x + direction.x * hit.timeOfImpact, origin.y + direction.y * hit.timeOfImpact);\n outNormal.set(hit.normal.x, hit.normal.y);\n}\n\n/**\n * Casts a ray and hands every hit to a sink, nearest first is **not** guaranteed — the caller sorts.\n *\n * @param world - The world.\n * @param origin - The world-space origin.\n * @param direction - A unit direction.\n * @param maxDistance - How far to travel.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param sink - Called once per hit with the collider, distance, and normal.\n *\n * @internal\n */\nexport function castRayAll(\n world: RapierWorld,\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number,\n groups: number,\n hitSensors: boolean,\n sink: (collider: RapierCollider, distance: number, normalX: number, normalY: number) => void,\n): void {\n const ray = new Ray({ x: origin.x, y: origin.y }, { x: direction.x, y: direction.y });\n world.intersectionsWithRay(\n ray,\n maxDistance,\n true,\n (intersection): boolean => {\n sink(intersection.collider, intersection.timeOfImpact, intersection.normal.x, intersection.normal.y);\n return true;\n },\n sensorFlags(hitSensors),\n groups,\n );\n}\n\n/**\n * Lists every collider a positioned shape overlaps.\n *\n * @param world - The world.\n * @param shape - The query geometry.\n * @param position - Its world position.\n * @param rotation - Its world rotation in radians.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param sink - Called once per overlapping collider.\n *\n * @internal\n */\nexport function overlapShape(\n world: RapierWorld,\n shape: RapierShape2D,\n position: Vec2Like,\n rotation: number,\n groups: number,\n hitSensors: boolean,\n sink: (collider: RapierCollider) => void,\n): void {\n const built = buildShape(shape);\n if (built === null) {\n return;\n }\n world.intersectionsWithShape(\n { x: position.x, y: position.y },\n rotation,\n built,\n (collider): boolean => {\n sink(collider);\n return true;\n },\n sensorFlags(hitSensors),\n groups,\n );\n}\n\n/**\n * Sweeps a shape and writes the first contact.\n *\n * @param world - The world.\n * @param shape - The query geometry.\n * @param position - Where the sweep starts.\n * @param rotation - The shape's rotation in radians.\n * @param direction - A unit sweep direction.\n * @param maxDistance - How far to sweep.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param outPoint - Receives the contact point.\n * @param outNormal - Receives the contact normal.\n * @param out - Receives the hit flag, collider, and distance.\n *\n * @internal\n */\nexport function castShape(\n world: RapierWorld,\n shape: RapierShape2D,\n position: Vec2Like,\n rotation: number,\n direction: Vec2Like,\n maxDistance: number,\n groups: number,\n hitSensors: boolean,\n outPoint: MutableVec2,\n outNormal: MutableVec2,\n out: ShapeCastResult2D,\n): void {\n out.hit = false;\n out.collider = null;\n out.distance = 0;\n const built = buildShape(shape);\n if (built === null) {\n return;\n }\n const hit = world.castShape(\n { x: position.x, y: position.y },\n rotation,\n { x: direction.x, y: direction.y },\n built,\n 0,\n maxDistance,\n true,\n sensorFlags(hitSensors),\n groups,\n );\n if (hit === null) {\n return;\n }\n out.hit = true;\n out.collider = hit.collider;\n out.distance = hit.time_of_impact;\n // `witness1` and `normal1` are expressed in the **swept shape's** local frame (`geometry/toi.d.ts`),\n // so they are rotated by the query rotation and offset by where the shape stood at impact.\n const cos = Math.cos(rotation);\n const sin = Math.sin(rotation);\n const originX = position.x + direction.x * hit.time_of_impact;\n const originY = position.y + direction.y * hit.time_of_impact;\n outPoint.set(\n originX + hit.witness1.x * cos - hit.witness1.y * sin,\n originY + hit.witness1.x * sin + hit.witness1.y * cos,\n );\n outNormal.set(hit.normal1.x * cos - hit.normal1.y * sin, hit.normal1.x * sin + hit.normal1.y * cos);\n}\n\n/**\n * Turns degrees counter-clockwise into the radians Rapier stores.\n *\n * @param degrees - The angle in degrees.\n * @returns The angle in radians.\n *\n * @internal\n */\nexport function toRadians(degrees: number): number {\n return (degrees * TAU) / 360;\n}\n\n/**\n * Turns Rapier's radians into the degrees `Transform.rotation2D` uses.\n *\n * @param radians - The angle in radians.\n * @returns The angle in degrees.\n *\n * @internal\n */\nexport function toDegrees(radians: number): number {\n return (radians * 360) / TAU;\n}\n\n/**\n * Builds the Rapier `Shape` a description stands for, for the query paths.\n *\n * @param shape - The description.\n * @returns The shape, or `null` when the geometry is degenerate.\n */\nfunction buildShape(shape: RapierShape2D): Shape | null {\n if (shape.kind === \"box\") {\n return new Cuboid(shape.halfWidth, shape.halfHeight);\n }\n if (shape.kind === \"circle\") {\n return new Ball(shape.radius);\n }\n if (shape.kind === \"capsule\") {\n return new Capsule(shape.halfHeight, shape.radius);\n }\n if (shape.kind === \"polyline\") {\n return new Polyline(shape.points);\n }\n return new ConvexPolygon(shape.points, false);\n}\n\n/**\n * Builds the collider description a shape stands for.\n *\n * @param shape - The description.\n * @returns The description, or `null` when Rapier refuses the geometry.\n */\nfunction describeShape(shape: RapierShape2D): ColliderDesc | null {\n if (shape.kind === \"box\") {\n return ColliderDesc.cuboid(shape.halfWidth, shape.halfHeight);\n }\n if (shape.kind === \"circle\") {\n return ColliderDesc.ball(shape.radius);\n }\n if (shape.kind === \"capsule\") {\n return ColliderDesc.capsule(shape.halfHeight, shape.radius);\n }\n if (shape.kind === \"polyline\") {\n return ColliderDesc.polyline(shape.points);\n }\n return ColliderDesc.convexHull(shape.points);\n}\n\n/**\n * The body description a motion type starts from.\n *\n * @param motion - The motion type.\n * @returns A fresh description.\n */\nfunction descriptionFor(motion: BodyMotion2D): RigidBodyDesc {\n if (motion === BodyMotion2D.dynamic) {\n return RigidBodyDesc.dynamic();\n }\n // Position-based rather than velocity-based: ignifx moves kinematic bodies by writing a\n // transform, and `setNextKinematicTranslation` is what turns that into a swept motion.\n return motion === BodyMotion2D.kinematic ? RigidBodyDesc.kinematicPositionBased() : RigidBodyDesc.fixed();\n}\n\n/**\n * Rapier's combine rule for one of ours.\n *\n * @param rule - The ignifx name.\n * @returns Rapier's value.\n */\nfunction combineRule(rule: CombineRule2D): CoefficientCombineRule {\n if (rule === CombineRule2D.min) {\n return CoefficientCombineRule.Min;\n }\n if (rule === CombineRule2D.multiply) {\n return CoefficientCombineRule.Multiply;\n }\n return rule === CombineRule2D.max ? CoefficientCombineRule.Max : CoefficientCombineRule.Average;\n}\n\n/**\n * The query filter flags that express \"sensors count\" or \"sensors do not\".\n *\n * @param hitSensors - Whether sensors count.\n * @returns The flags.\n */\nfunction sensorFlags(hitSensors: boolean): QueryFilterFlags | undefined {\n return hitSensors ? undefined : QueryFilterFlags.EXCLUDE_SENSORS;\n}\n","import type { Physics2DRuntime } from \"./runtime.js\";\n\n/**\n * The one mutable cell between `register` and `onStart`.\n *\n * `Extension.register` runs **before** `createApp` builds the world\n * (`packages/core/src/app/app.ts`, `initialize`), and the Rapier world needs the world's layer\n * table and Rapier's WebAssembly module, which is loaded asynchronously. So the extension declares\n * its systems and its service in `register` — which is where `docs/architecture/04-extensions.md`\n * §1 says everything is declared — and they all read the runtime through this holder, which\n * `onStart` fills in.\n *\n * @internal\n */\nexport class Physics2DHost {\n /** The runtime, or `null` until `onStart` has built it. */\n runtime: Physics2DRuntime | null = null;\n}\n","import { QueryFilterFlags } from \"@dimforge/rapier2d-compat\";\nimport type { RapierCollider, RapierWorld } from \"./world.js\";\nimport type { KinematicCharacterController } from \"@dimforge/rapier2d-compat\";\nimport type { MutableVec2, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Rapier's kinematic character controller (`control/character_controller.d.ts`), wrapped so the\n * runtime never imports the backend.\n *\n * ## Facts measured against `@dimforge/rapier2d-compat@0.20.0` (spike S6.2)\n *\n * - **Slopes work as documented.** `setMaxSlopeClimbAngle(radians)` blocks a slope steeper than the\n * limit and lets a shallower one through, and `setMinSlopeSlideAngle` decides when the character\n * slides back down.\n * - **Snap-to-ground works.** Descending a 25° ramp at 4 m/s, `enableSnapToGround(0.5)` kept the\n * character grounded on every one of 200 steps; without it, 66 of them were airborne.\n * - **One-way platforms are the `filterPredicate` of `computeColliderMovement`.** Returning `false`\n * for a platform makes the character pass straight through it; the predicate is called once per\n * candidate collider per step.\n * - **Autostep needs a box.** With a box character, `enableAutostep(0.35, …)` clears a 0.3 m step\n * and refuses a 0.5 m one. With a capsule of radius 0.2 the same call clears 0.15 m but not\n * 0.2 m — a Rapier limitation, recorded in ADR-0006's validation section and in the skill.\n * - **The broadphase must exist first.** Before the world has stepped, `computeColliderMovement`\n * sees no obstacles at all and reports the requested motion unchanged.\n * - **A sensor is an obstacle unless `filterFlags` says otherwise** (measured 2026-09-08). With\n * `filterFlags` left `undefined`, a kinematic box driven by the controller into a static sensor\n * ball stops dead at the sensor's surface and no intersection event is ever raised, because the\n * character never overlaps it. Passing {@link QueryFilterFlags.EXCLUDE_SENSORS} lets the\n * character walk through and `drainCollisionEvents` then reports the started/stopped pair. Every\n * move in {@link computeControllerMovement} therefore passes it: a trigger volume is a region of\n * space, never a wall.\n *\n * @internal\n */\nexport type RapierController = KinematicCharacterController;\n\n/**\n * The obstacle filter every controller move uses. Sensors describe regions, not geometry, so they\n * are excluded from collide-and-slide; the pair is still reported by the world step, which is where\n * `onTriggerEnter`/`onTriggerExit` come from.\n */\nconst OBSTACLE_FLAGS: QueryFilterFlags = QueryFilterFlags.EXCLUDE_SENSORS;\n\n/** How a controller is configured, in ignifx units. */\nexport interface ControllerTuning2D {\n /** The steepest slope the character can walk up, in radians from \"up\". */\n readonly slopeLimit: number;\n /** The shallowest slope the character slides down, in radians from \"up\". */\n readonly slideLimit: number;\n /** The tallest step the character climbs automatically, in metres; `0` disables autostep. */\n readonly stepOffset: number;\n /** How much clear floor must follow a step, in metres. */\n readonly stepMinWidth: number;\n /** How far below the feet the ground is snapped to, in metres; `0` disables snapping. */\n readonly snapToGround: number;\n /** Whether the character pushes dynamic bodies out of the way. */\n readonly pushBodies: boolean;\n}\n\n/** One obstacle the character hit during a move. */\nexport interface ControllerHit2D {\n /** The collider that was hit, or `null` when Rapier no longer tracks it. */\n collider: RapierCollider | null;\n /** The world-space contact point. */\n readonly point: MutableVec2;\n /** The world-space outward normal on the obstacle. */\n readonly normal: MutableVec2;\n}\n\n/**\n * Creates a character controller.\n *\n * @param world - The world.\n * @param skinWidth - The gap Rapier keeps between the character and its surroundings, in metres.\n * @returns The controller.\n *\n * @internal\n */\nexport function createController(world: RapierWorld, skinWidth: number): RapierController {\n const controller = world.createCharacterController(skinWidth);\n // ignifx 2D is +Y up (ADR-0011); Rapier's default up in 2D is the same, but saying so keeps the\n // controller correct if a project ever flips the axis.\n controller.setUp({ x: 0, y: 1 });\n return controller;\n}\n\n/**\n * Releases a controller.\n *\n * @param world - The world.\n * @param controller - The controller.\n *\n * @internal\n */\nexport function destroyController(world: RapierWorld, controller: RapierController): void {\n world.removeCharacterController(controller);\n}\n\n/**\n * Applies the component's settings to a controller.\n *\n * @param controller - The controller.\n * @param tuning - The slope, step, snap, and pushing settings.\n *\n * @internal\n */\nexport function tuneController(controller: RapierController, tuning: ControllerTuning2D): void {\n controller.setMaxSlopeClimbAngle(tuning.slopeLimit);\n controller.setMinSlopeSlideAngle(tuning.slideLimit);\n controller.setApplyImpulsesToDynamicBodies(tuning.pushBodies);\n if (tuning.stepOffset > 0) {\n controller.enableAutostep(tuning.stepOffset, tuning.stepMinWidth, true);\n } else {\n controller.disableAutostep();\n }\n if (tuning.snapToGround > 0) {\n controller.enableSnapToGround(tuning.snapToGround);\n } else {\n controller.disableSnapToGround();\n }\n}\n\n/**\n * Sets the gap kept between the character and its surroundings.\n *\n * @param controller - The controller.\n * @param skinWidth - The gap in metres.\n *\n * @internal\n */\nexport function setControllerSkinWidth(controller: RapierController, skinWidth: number): void {\n controller.setOffset(skinWidth);\n}\n\n/**\n * Runs one collide-and-slide move.\n *\n * @remarks\n * Sensors are excluded from the obstacle set ({@link OBSTACLE_FLAGS}), so a character walks into and\n * out of a trigger volume at full speed and the world step raises the intersection events.\n *\n * @param controller - The controller.\n * @param collider - The character's own collider.\n * @param desired - The displacement to attempt, in metres.\n * @param groups - The packed filter word obstacles must pass.\n * @param filter - An extra per-collider predicate — how one-way platforms are done — or `null`.\n * @param out - Receives the movement Rapier resolved.\n * @returns `true` when the character ended the move on walkable ground.\n *\n * @internal\n */\nexport function computeControllerMovement(\n controller: RapierController,\n collider: RapierCollider,\n desired: Vec2Like,\n groups: number,\n filter: ((collider: RapierCollider) => boolean) | null,\n out: MutableVec2,\n): boolean {\n if (filter === null) {\n controller.computeColliderMovement(collider, { x: desired.x, y: desired.y }, OBSTACLE_FLAGS, groups);\n } else {\n controller.computeColliderMovement(collider, { x: desired.x, y: desired.y }, OBSTACLE_FLAGS, groups, filter);\n }\n controller.computedMovement(out);\n return controller.computedGrounded();\n}\n\n/**\n * How many obstacles the last move hit.\n *\n * @param controller - The controller.\n * @returns The count.\n *\n * @internal\n */\nexport function controllerHitCount(controller: RapierController): number {\n return controller.numComputedCollisions();\n}\n\n/**\n * Reads one obstacle of the last move.\n *\n * @param controller - The controller.\n * @param index - Which obstacle.\n * @param out - The record to write.\n * @returns `true` when the obstacle exists.\n *\n * @internal\n */\nexport function readControllerHit(controller: RapierController, index: number, out: ControllerHit2D): boolean {\n const hit = controller.computedCollision(index);\n if (hit === null) {\n return false;\n }\n out.collider = hit.collider;\n out.point.set(hit.witness1.x, hit.witness1.y);\n out.normal.set(hit.normal1.x, hit.normal1.y);\n return true;\n}\n","import { lerp, Vec2, Vec3 } from \"@ignifx/core\";\nimport { CharacterController2D } from \"../components/character-controller.js\";\nimport { Collider2D } from \"../components/collider.js\";\nimport { Rigidbody2D } from \"../components/rigidbody.js\";\nimport { TilemapCollider2D } from \"../components/tilemap-collider.js\";\nimport { Physics2DErrorCode, physics2DError } from \"../errors.js\";\nimport {\n computeControllerMovement,\n controllerHitCount,\n createController,\n destroyController,\n readControllerHit,\n tuneController,\n} from \"../lite/rapier/character.js\";\nimport {\n addForce,\n addTorque,\n applyImpulse,\n BodyMotion2D,\n bodyMass,\n colliderHandle,\n createBody,\n createCollider,\n createEventQueue,\n destroyBody,\n destroyEventQueue,\n destroyWorld,\n drainCollisions,\n interactionGroups,\n isColliderSensor,\n primeWorld,\n RAPIER_LAYER_LIMIT,\n readAngularVelocity,\n readBodyPose,\n readColliderTranslation,\n readContact,\n readLinearVelocity,\n recomputeMass,\n setBodyPose,\n setColliderDensity,\n setColliderEvents,\n setGravity,\n setNextKinematicPose,\n setSolverIterations,\n setTimestep,\n stepWorld,\n toDegrees,\n toRadians,\n writeAngularVelocity,\n writeLinearVelocity,\n} from \"../lite/rapier/world.js\";\nimport type { BodyRecord2D } from \"./body-record.js\";\nimport type { CollectedShape2D } from \"../components/collider.js\";\nimport type { ContactPoint2D } from \"../events.js\";\nimport type { ControllerHit2D, RapierController } from \"../lite/rapier/character.js\";\nimport type { ContactData2D, RapierCollider, RapierEventQueue, RapierWorld } from \"../lite/rapier/world.js\";\nimport type { Physics2DMaterialValues, Physics2DSettings } from \"../settings.js\";\nimport type {\n Component,\n DiagnosticsGroup,\n Entity,\n ExtensionContext,\n MutableVec2,\n PhysicsCallbackName,\n Vec2Like,\n} from \"@ignifx/core\";\n\n/**\n * The per-app 2D physics runtime: the Rapier world, the bodies, the character controllers, the\n * event drain, and the three things the systems ask it to do each frame — restore, step,\n * interpolate (`docs/architecture/11-2d-toolkit.md` §8, `09-physics.md` §1).\n *\n * Everything here is `@internal`; the public surface is `Physics2DService`, `Rigidbody2D`, the 2D\n * colliders, and `CharacterController2D`.\n */\n\n/** What the extension hands the runtime. */\nexport interface Physics2DRuntimeOptions {\n /** The extension registration surface; the physics kernel hooks live on it. */\n readonly ctx: ExtensionContext;\n /** The resolved `physics2d` settings section. */\n readonly settings: Physics2DSettings;\n /** The Rapier world. */\n readonly world: RapierWorld;\n /** The `physics2d` diagnostics group. */\n readonly counters: DiagnosticsGroup;\n}\n\n/**\n * The counters `09-physics.md` §9 and `11-2d-toolkit.md` §8 name for 2D.\n *\n * @public\n */\nexport const PHYSICS_2D_DIAGNOSTICS_COUNTERS: readonly string[] = Object.freeze([\n \"bodies\",\n \"colliders\",\n \"stepsThisFrame\",\n \"collisionEvents\",\n \"triggerEvents\",\n \"stepMs\",\n]);\n\n/**\n * The diagnostics group name.\n *\n * @public\n */\nexport const PHYSICS_2D_DIAGNOSTICS_GROUP = \"physics2d\";\n\n/** One pair of colliders Rapier currently reports as touching. */\ninterface PairState {\n /** The first collider's handle. */\n readonly first: number;\n /** The second collider's handle. */\n readonly second: number;\n /** Whether either collider is a sensor, which makes the pair a trigger rather than a contact. */\n readonly sensor: boolean;\n /** Whether the pair started this step, so it gets `Enter` rather than `Stay`. */\n entered: boolean;\n}\n\n/** The pooled `TriggerEvent2D`, whose fields the dispatcher rewrites for each delivery. */\ninterface MutableTrigger {\n /** The other participant. */\n other: Entity | null;\n /** The other participant's collider. */\n otherCollider: Collider2D | null;\n /** The entity being called. */\n self: Entity;\n /** This entity's collider. */\n selfCollider: Collider2D | null;\n}\n\n/** The pooled `Collision2D`. */\ninterface MutableCollision {\n /** The other participant. */\n other: Entity | null;\n /** The other participant's collider. */\n otherCollider: Collider2D | null;\n /** The entity being called. */\n self: Entity;\n /** This entity's collider. */\n selfCollider: Collider2D | null;\n /** The one-element pooled contact list. */\n readonly contacts: readonly ContactPoint2D[];\n /** The relative velocity of the two bodies. */\n readonly relativeVelocity: Vec2Like;\n}\n\n/** The pooled `CharacterCollision2D`. */\ninterface MutableCharacterCollision {\n /** The entity that was hit. */\n other: Entity | null;\n /** The collider that was hit. */\n otherCollider: Collider2D | null;\n /** The contact point. */\n readonly point: Vec2Like;\n /** The contact normal. */\n readonly normal: Vec2Like;\n}\n\n/** One collider that is solid only from above, with its world top cached for the move filter. */\ninterface OneWayPlatform {\n /** The Rapier collider. */\n readonly collider: RapierCollider;\n /** How far its top sits above the collider's own origin, in metres. */\n readonly halfHeight: number;\n /** Its world-space top, refreshed once per fixed step. */\n top: number;\n}\n\n/** Which ignifx collider a Rapier collider belongs to. */\ninterface ColliderBinding {\n /** The record that owns it. */\n readonly record: BodyRecord2D;\n /** The authored collider, or `null` for a character controller's own shape. */\n readonly owner: Collider2D | null;\n /** The Rapier collider itself, which the event and contact paths need. */\n readonly collider: RapierCollider;\n}\n\n/** How far an implicit static body may move before the diagnostic considers it moved. */\nconst STATIC_MOVE_EPSILON = 1e-6;\n\n/** How much clear floor Rapier requires past an autostep, in metres. */\nconst AUTOSTEP_MIN_WIDTH = 0.05;\n\n/** Every layer bit Rapier can express. */\nconst ALL_LAYER_BITS = 0xff_ff;\n\n/**\n * The per-app 2D physics runtime.\n *\n * @internal\n */\nexport class Physics2DRuntime {\n readonly #ctx: ExtensionContext;\n\n readonly #settings: Physics2DSettings;\n\n readonly #world: RapierWorld;\n\n readonly #counters: DiagnosticsGroup;\n\n readonly #queue: RapierEventQueue;\n\n readonly #records: BodyRecord2D[] = [];\n\n readonly #byEntity = new Map<Entity, BodyRecord2D>();\n\n readonly #byCollider = new Map<number, ColliderBinding>();\n\n readonly #controllers = new Map<number, RapierController>();\n\n readonly #oneWay = new Map<number, OneWayPlatform>();\n\n readonly #dirty = new Set<Entity>();\n\n readonly #collideMasks = new Int32Array(RAPIER_LAYER_LIMIT).fill(ALL_LAYER_BITS);\n\n readonly #pairs = new Map<string, PairState>();\n\n readonly #endedPairs: PairState[] = [];\n\n readonly #startedPairs: PairState[] = [];\n\n readonly #contact: ContactData2D = { touching: false, point: new Vec2(), normal: new Vec2(), impulse: 0 };\n\n readonly #contactPoint: { point: Vec2; normal: Vec2; impulse: number } = {\n point: new Vec2(),\n normal: new Vec2(),\n impulse: 0,\n };\n\n readonly #contacts: ContactPoint2D[] = [];\n\n readonly #controllerHit: ControllerHit2D = { collider: null, point: new Vec2(), normal: new Vec2() };\n\n #trigger: MutableTrigger | null = null;\n\n #collision: MutableCollision | null = null;\n\n #characterCollision: MutableCharacterCollision | null = null;\n\n readonly #scratchPose = new Vec3();\n\n readonly #scratchVector = new Vec2();\n\n readonly #relativeVelocity = new Vec2();\n\n readonly #movement = new Vec2();\n\n readonly #groundNormal = new Vec2();\n\n readonly #filterScratch = new Vec2();\n\n /** The vertical component of the displacement the filtered move is attempting. */\n #filterDesiredY = 0;\n\n /** The bottom of the filtered character, in metres. */\n #filterFeet = 0;\n\n #colliderSetChanged = true;\n\n #stepped = false;\n\n #disposed = false;\n\n #stepsThisFrame = 0;\n\n #stepMs = 0;\n\n #collisionEventCount = 0;\n\n #triggerEventCount = 0;\n\n #colliderCount = 0;\n\n /**\n * Builds the runtime and applies the world settings.\n *\n * @param options - The context, settings, world, and counters.\n * @throws IgnifxError with code `IGX-1155` when the collision matrix names an unknown layer.\n */\n constructor(options: Physics2DRuntimeOptions) {\n this.#ctx = options.ctx;\n this.#settings = options.settings;\n this.#world = options.world;\n this.#counters = options.counters;\n this.#queue = createEventQueue();\n this.#contacts.push(this.#contactPoint);\n setGravity(this.#world, this.#settings.gravity);\n if (this.#settings.velocityIterations > 0) {\n setSolverIterations(this.#world, this.#settings.velocityIterations);\n }\n this.#buildCollideMasks();\n }\n\n /**\n * The Rapier world, for the query and escape-hatch paths.\n *\n * @returns The world handle.\n */\n get world(): RapierWorld {\n return this.#world;\n }\n\n /**\n * Whether at least one fixed step has run, which is what Rapier's broadphase needs before a query\n * can answer (`09-physics.md` §5, spike S6.2).\n *\n * @returns `true` once a step has completed.\n */\n get hasStepped(): boolean {\n return this.#stepped;\n }\n\n /**\n * Every body, in creation order.\n *\n * @returns The records.\n */\n get records(): readonly BodyRecord2D[] {\n return this.#records;\n }\n\n /**\n * The resolved settings.\n *\n * @returns The `physics2d` section.\n */\n get settings(): Physics2DSettings {\n return this.#settings;\n }\n\n /**\n * Replaces world gravity.\n *\n * @param gravity - The new acceleration vector.\n */\n setGravity(gravity: Vec2Like): void {\n setGravity(this.#world, gravity);\n }\n\n /**\n * Marks an entity's body for a rebuild at the start of the next fixed step.\n *\n * @param entity - The entity whose colliders, `Rigidbody2D`, or controller changed.\n */\n markDirty(entity: Entity): void {\n if (!this.#disposed && !entity.isDestroyed) {\n this.#dirty.add(entity);\n }\n }\n\n /**\n * Marks a character controller's entity for a rebuild.\n *\n * @param component - The controller component.\n */\n markControllerDirty(component: CharacterController2D): void {\n this.markDirty(component.entity);\n }\n\n /**\n * Drops a character controller's body, which is what detaching the component does.\n *\n * @param component - The controller component.\n */\n releaseController(component: CharacterController2D): void {\n this.markDirty(component.entity);\n }\n\n /**\n * The Rapier body of an entity, for the escape hatch.\n *\n * @param entity - The entity.\n * @returns The body handle, or `null`.\n */\n bodyHandleOf(entity: Entity): BodyRecord2D[\"body\"] | null {\n return this.#byEntity.get(entity)?.body ?? null;\n }\n\n /**\n * The record an entity owns.\n *\n * @param entity - The entity.\n * @returns The record, or `null`.\n */\n recordOf(entity: Entity): BodyRecord2D | null {\n return this.#byEntity.get(entity) ?? null;\n }\n\n /**\n * The entity and collider a Rapier collider belongs to, which is how a query result or an event\n * becomes an ignifx object.\n *\n * @param collider - The Rapier collider, or `null`.\n * @returns The binding, or `null`.\n */\n bindingOf(collider: RapierCollider | null): ColliderBinding | null {\n return collider === null ? null : (this.#byCollider.get(colliderHandle(collider)) ?? null);\n }\n\n /**\n * The mass Rapier computed for an entity's body.\n *\n * @param entity - The entity.\n * @returns Kilograms, or `0` when there is no body.\n */\n massOf(entity: Entity): number {\n const record = this.#byEntity.get(entity);\n return record === undefined ? 0 : bodyMass(record.body);\n }\n\n /**\n * Reads a body's linear velocity.\n *\n * @param entity - The entity.\n * @param out - The vector to write; left untouched when there is no body.\n */\n readLinear(entity: Entity, out: MutableVec2): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n readLinearVelocity(record.body, out);\n }\n }\n\n /**\n * Reads a body's angular velocity.\n *\n * @param entity - The entity.\n * @returns Degrees per second, counter-clockwise.\n */\n readAngular(entity: Entity): number {\n const record = this.#byEntity.get(entity);\n return record === undefined ? 0 : toDegrees(readAngularVelocity(record.body));\n }\n\n /**\n * Sets a body's linear velocity.\n *\n * @param entity - The entity.\n * @param velocity - Metres per second.\n */\n setLinearVelocity(entity: Entity, velocity: Vec2Like): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n writeLinearVelocity(record.body, velocity);\n }\n }\n\n /**\n * Sets a body's angular velocity.\n *\n * @param entity - The entity.\n * @param velocity - Degrees per second.\n */\n setAngularVelocity(entity: Entity, velocity: number): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n writeAngularVelocity(record.body, toRadians(velocity));\n }\n }\n\n /**\n * Applies a force for one fixed step.\n *\n * @param entity - The entity.\n * @param force - Newtons.\n * @param point - The application point, or `null` for the centre of mass.\n */\n addForce(entity: Entity, force: Vec2Like, point: Vec2Like | null): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n addForce(record.body, force, point);\n }\n }\n\n /**\n * Applies an impulse.\n *\n * @param entity - The entity.\n * @param impulse - Newton-seconds.\n * @param point - The application point, or `null` for the centre of mass.\n */\n addImpulse(entity: Entity, impulse: Vec2Like, point: Vec2Like | null): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n applyImpulse(record.body, impulse, point);\n }\n }\n\n /**\n * Applies a torque for one fixed step.\n *\n * @param entity - The entity.\n * @param torque - Newton-metres, positive counter-clockwise.\n */\n addTorque(entity: Entity, torque: number): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n addTorque(record.body, torque);\n }\n }\n\n /**\n * Teleports a body and resets its interpolation history.\n *\n * @param entity - The entity.\n * @param position - The new world position.\n * @param rotationDegrees - The new rotation in degrees counter-clockwise.\n */\n teleport(entity: Entity, position: Vec2Like, rotationDegrees: number): void {\n const record = this.#byEntity.get(entity);\n this.#writeTransform(entity, position, rotationDegrees);\n if (record === undefined) {\n return;\n }\n const radians = toRadians(rotationDegrees);\n setBodyPose(record.body, position, radians);\n record.currentPosition.set(position.x, position.y);\n record.previousPosition.set(position.x, position.y);\n record.currentRotation = radians;\n record.previousRotation = radians;\n this.#colliderSetChanged = true;\n }\n\n /**\n * Teleports a character controller.\n *\n * @param component - The controller component.\n * @param position - The new world position.\n */\n teleportController(component: CharacterController2D, position: Vec2Like): void {\n this.teleport(component.entity, position, component.transform.rotation2D);\n }\n\n /**\n * Restores the authoritative pose of every interpolated body, undoing the display pose the\n * `Update` system wrote at the top of the previous frame's `Update` (`09-physics.md` §1).\n */\n restorePoses(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record !== undefined && record.interpolate) {\n this.#writeTransform(record.entity, record.currentPosition, toDegrees(record.currentRotation));\n }\n }\n }\n\n /**\n * Runs one fixed step: reconcile, push kinematic poses, move controllers, step Rapier, snapshot,\n * dispatch events.\n *\n * @param fixedDeltaSeconds - The fixed step in seconds.\n */\n step(fixedDeltaSeconds: number): void {\n this.#syncLayers();\n this.#pollTilemaps();\n this.#reconcile();\n this.#pushKinematicPoses();\n setTimestep(this.#world, fixedDeltaSeconds);\n const started = now();\n if (this.#colliderSetChanged || !this.#stepped) {\n // Rapier only refreshes its broadphase inside `step`, so a collider created since the last\n // one is invisible to the character controller and to queries (S6.2). A zero-length step\n // rebuilds it; it is skipped whenever the collider set is unchanged, because it perturbs a\n // running simulation.\n primeWorld(this.#world, this.#queue);\n this.#colliderSetChanged = false;\n }\n this.#refreshOneWayPlatforms();\n this.#moveControllers(fixedDeltaSeconds);\n this.#capturePreStepVelocities();\n stepWorld(this.#world, this.#queue);\n this.#stepMs += now() - started;\n this.#stepsThisFrame += 1;\n this.#stepped = true;\n this.#snapshot();\n this.#dispatchEvents();\n }\n\n /**\n * Writes the interpolated display pose of every interpolated body.\n *\n * @param alpha - `time.fixedStepAlpha`, in `[0, 1)`.\n */\n interpolate(alpha: number): void {\n this.#publishCounters();\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record === undefined || !record.interpolate) {\n continue;\n }\n this.#scratchVector.set(\n lerp(record.previousPosition.x, record.currentPosition.x, alpha),\n lerp(record.previousPosition.y, record.currentPosition.y, alpha),\n );\n const rotation = record.previousRotation + shortestArc(record.previousRotation, record.currentRotation) * alpha;\n this.#writeTransform(record.entity, this.#scratchVector, toDegrees(rotation));\n }\n }\n\n /** Tears every body down and releases the Rapier world. */\n dispose(): void {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n for (let index = this.#records.length - 1; index >= 0; index -= 1) {\n const record = this.#records[index];\n if (record !== undefined) {\n this.#teardown(record);\n }\n }\n this.#records.length = 0;\n this.#byEntity.clear();\n this.#byCollider.clear();\n this.#controllers.clear();\n this.#pairs.clear();\n destroyEventQueue(this.#queue);\n destroyWorld(this.#world);\n }\n\n /**\n * Publishes the frame's counters and zeroes the accumulators. The top of `Update`, where the\n * display pose is written, is the first point in a frame at which every fixed step of that frame\n * is done.\n */\n #publishCounters(): void {\n this.#counters.set(this.#counters.index(\"bodies\"), this.#records.length);\n this.#counters.set(this.#counters.index(\"colliders\"), this.#colliderCount);\n this.#counters.set(this.#counters.index(\"stepsThisFrame\"), this.#stepsThisFrame);\n this.#counters.set(this.#counters.index(\"collisionEvents\"), this.#collisionEventCount);\n this.#counters.set(this.#counters.index(\"triggerEvents\"), this.#triggerEventCount);\n this.#counters.set(this.#counters.index(\"stepMs\"), this.#stepMs);\n this.#stepsThisFrame = 0;\n this.#collisionEventCount = 0;\n this.#triggerEventCount = 0;\n this.#stepMs = 0;\n }\n\n /**\n * Builds the per-layer collide masks from `physics2d.collisionMatrix` (`09-physics.md` §3).\n *\n * @throws IgnifxError with code `IGX-1155` when a name is not a declared layer, and reports\n * `IGX-1152` for a layer Rapier's sixteen interaction groups cannot express.\n */\n #buildCollideMasks(): void {\n for (const [name, others] of Object.entries(this.#settings.collisionMatrix)) {\n const index = this.#layerIndex(name);\n let mask = 0;\n for (const other of others) {\n mask |= 1 << this.#layerIndex(other);\n }\n this.#collideMasks[index] = mask;\n }\n }\n\n /**\n * Resolves a layer name to its index, refusing an undeclared one.\n *\n * @param name - The layer name.\n * @returns The index, clamped into Rapier's sixteen.\n * @throws IgnifxError with code `IGX-1155` when the project does not declare the layer.\n */\n #layerIndex(name: string): number {\n const index = this.#ctx.app.world.layers.indexOf(name);\n if (index < 0) {\n throw physics2DError(Physics2DErrorCode.unknownLayer, `${name} is not a layer this project declares.`, {\n context: { layer: name },\n hint: \"Add the layer to the layers settings section, or fix the name in physics2d.collisionMatrix.\",\n });\n }\n return this.#clampLayer(index, name);\n }\n\n /**\n * Reports a layer Rapier cannot express and falls back to layer 0.\n *\n * @param layer - The layer index.\n * @param subject - What the diagnostic names.\n * @returns The layer to use.\n */\n #clampLayer(layer: number, subject: string): number {\n if (layer < RAPIER_LAYER_LIMIT) {\n return layer;\n }\n this.#ctx.log.warn(\n `${Physics2DErrorCode.layerOutOfRange}: {entity} is on layer {layer}, which is outside the sixteen layers Rapier 2D can filter by.`,\n subject,\n String(layer),\n );\n return 0;\n }\n\n /** Marks every entity whose layer changed since its body was built. */\n #syncLayers(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record !== undefined && record.entity.layer !== record.layer) {\n this.markDirty(record.entity);\n }\n }\n }\n\n /** Marks every entity whose tilemap collision data changed since its shapes were built. */\n #pollTilemaps(): void {\n const colliders = this.#ctx.app.world.components(TilemapCollider2D);\n for (let index = 0; index < colliders.length; index += 1) {\n const collider = colliders[index];\n if (collider !== undefined && collider.needsRebuild()) {\n this.markDirty(collider.entity);\n }\n }\n }\n\n /** Rebuilds every body marked dirty. */\n #reconcile(): void {\n if (this.#dirty.size === 0) {\n return;\n }\n const pending = [...this.#dirty];\n this.#dirty.clear();\n for (const entity of pending) {\n this.#rebuildEntity(entity);\n }\n this.#colliderSetChanged = true;\n }\n\n /**\n * Tears an entity's body down and builds a new one from its current components.\n *\n * @param entity - The entity to rebuild.\n */\n #rebuildEntity(entity: Entity): void {\n const existing = this.#byEntity.get(entity);\n let carriedLinear: Vec2 | null = null;\n let carriedAngular = 0;\n if (existing !== undefined) {\n if (existing.motion === BodyMotion2D.dynamic) {\n carriedLinear = new Vec2();\n readLinearVelocity(existing.body, carriedLinear);\n carriedAngular = readAngularVelocity(existing.body);\n }\n this.#removeRecord(entity);\n }\n if (entity.isDestroyed || !entity.activeInHierarchy) {\n return;\n }\n const controller = entity.getComponent(CharacterController2D);\n const colliders = collidersOf(entity);\n if (colliders.length === 0 && controller === null) {\n return;\n }\n if (entity.parent !== null) {\n this.#ctx.log.warn(\n `${Physics2DErrorCode.bodyOnChildEntity}: {entity} carries a 2D physics body but is not a root entity; Rapier writes its world pose.`,\n entity.name,\n );\n }\n const record = this.#build(entity, entity.getComponent(Rigidbody2D), controller, colliders);\n if (record === null) {\n return;\n }\n this.#records.push(record);\n if (record.motion === BodyMotion2D.dynamic && carriedLinear !== null) {\n writeLinearVelocity(record.body, carriedLinear);\n writeAngularVelocity(record.body, carriedAngular);\n }\n }\n\n /**\n * Builds one body from an entity's colliders and controller.\n *\n * @param entity - The entity.\n * @param rigidbody - Its `Rigidbody2D`, or `null`.\n * @param controller - Its `CharacterController2D`, or `null`.\n * @param colliders - Its colliders, in component order.\n * @returns The new record, or `null` when nothing produced a shape — a `TilemapCollider2D` whose\n * tilemap has not published its geometry yet, say, which must not leave an empty body behind.\n */\n #build(\n entity: Entity,\n rigidbody: Rigidbody2D | null,\n controller: CharacterController2D | null,\n colliders: readonly Collider2D[],\n ): BodyRecord2D | null {\n const transform = entity.transform;\n transform.positionToRef(this.#scratchPose);\n const rotation = toRadians(transform.rotation2D);\n const layer = this.#clampLayer(entity.layer, entity.name);\n const motion = motionOf(rigidbody, controller);\n const body = createBody(this.#world, {\n motion,\n position: { x: this.#scratchPose.x, y: this.#scratchPose.y },\n rotation,\n gravityScale: rigidbody?.gravityScale ?? 1,\n linearDamping: rigidbody?.linearDamping ?? 0,\n angularDamping: rigidbody?.angularDamping ?? 0,\n freezeRotation: rigidbody?.freezeRotation ?? controller !== null,\n additionalMass: 0,\n startAsleep: false,\n });\n const record: BodyRecord2D = {\n entity,\n rigidbody,\n controller,\n body,\n colliders: [],\n owners: [],\n controllerCollider: null,\n motion,\n interpolate: this.#shouldInterpolate(rigidbody, controller, motion),\n layer,\n collisionEvents: false,\n previousPosition: new Vec2(this.#scratchPose.x, this.#scratchPose.y),\n previousRotation: rotation,\n currentPosition: new Vec2(this.#scratchPose.x, this.#scratchPose.y),\n currentRotation: rotation,\n preStepVelocity: new Vec2(),\n staticVersion: transform.worldMatrixVersion,\n reportedMove: false,\n disconnects: [],\n };\n this.#byEntity.set(entity, record);\n this.#attachColliders(record, colliders, layer);\n if (controller !== null) {\n this.#attachController(record, controller, layer);\n }\n if (record.colliders.length === 0) {\n this.#byEntity.delete(entity);\n destroyBody(this.#world, body);\n return null;\n }\n this.#applyMass(record);\n this.#applyCollisionEvents(record);\n this.#watchEntity(record);\n return record;\n }\n\n /**\n * Builds and registers every Rapier collider an entity's authored colliders stand for.\n *\n * @param record - The record being built.\n * @param colliders - The authored colliders.\n * @param layer - The entity's clamped layer.\n */\n #attachColliders(record: BodyRecord2D, colliders: readonly Collider2D[], layer: number): void {\n const defaults = this.#settings.defaultMaterial;\n const shapes: CollectedShape2D[] = [];\n for (const collider of colliders) {\n shapes.length = 0;\n collider.collectShapes(record.entity.transform.lossyScale, shapes);\n const effective = collider.layerOverride === \"\" ? layer : this.#layerIndex(collider.layerOverride);\n const material = collider.resolveMaterial(defaults);\n for (const collected of shapes) {\n this.#addCollider(record, collider, collected, effective, material);\n }\n }\n }\n\n /**\n * Creates one Rapier collider and binds it back to its author.\n *\n * @param record - The record it belongs to.\n * @param owner - The authored collider, or `null` for a controller shape.\n * @param collected - The geometry and its placement.\n * @param layer - The layer to filter as.\n * @param material - The surface.\n * @returns The created collider, or `null` when Rapier refused the geometry.\n */\n #addCollider(\n record: BodyRecord2D,\n owner: Collider2D | null,\n collected: CollectedShape2D,\n layer: number,\n material: Physics2DMaterialValues,\n ): RapierCollider | null {\n const collider = createCollider(this.#world, record.body, collected.shape, {\n offset: collected.offset,\n rotation: collected.rotation,\n sensor: owner?.isTrigger === true,\n groups: interactionGroups(1 << layer, this.#collideMasks[layer] ?? ALL_LAYER_BITS),\n friction: material.friction,\n restitution: material.restitution,\n frictionCombine: owner?.frictionCombine ?? \"average\",\n restitutionCombine: owner?.restitutionCombine ?? \"average\",\n events: owner?.isTrigger === true,\n });\n if (collider === null) {\n this.#ctx.log.warn(\n `${Physics2DErrorCode.colliderGeometryInvalid}: {entity} has a 2D collider whose geometry Rapier cannot build a shape from.`,\n record.entity.name,\n );\n return null;\n }\n const handle = colliderHandle(collider);\n record.colliders.push(collider);\n record.owners.push(owner);\n this.#byCollider.set(handle, { record, owner, collider });\n this.#colliderCount += 1;\n if (collected.oneWay) {\n this.#oneWay.set(handle, { collider, halfHeight: topOf(collected.shape) + collected.offset.y, top: 0 });\n }\n return collider;\n }\n\n /**\n * Builds a character controller's own shape and Rapier controller.\n *\n * @param record - The record being built.\n * @param controller - The component.\n * @param layer - The entity's clamped layer.\n */\n #attachController(record: BodyRecord2D, controller: CharacterController2D, layer: number): void {\n const half = Math.max(0, controller.height / 2 - controller.radius);\n const shape: CollectedShape2D = {\n shape:\n controller.shape === \"box\"\n ? {\n kind: \"box\",\n halfWidth: controller.radius,\n halfHeight: Math.max(controller.radius, controller.height / 2),\n }\n : { kind: \"capsule\", halfHeight: half, radius: controller.radius },\n offset: controller.offset,\n rotation: 0,\n oneWay: false,\n };\n const collider = this.#addCollider(record, null, shape, layer, this.#settings.defaultMaterial);\n record.controllerCollider = collider;\n if (collider === null) {\n return;\n }\n const rapierController = createController(this.#world, Math.max(controller.skinWidth, 1e-3));\n tuneController(rapierController, {\n slopeLimit: toRadians(controller.slopeLimit),\n slideLimit: toRadians(controller.slopeLimit),\n stepOffset: controller.stepOffset,\n stepMinWidth: AUTOSTEP_MIN_WIDTH,\n snapToGround: controller.snapToGround,\n pushBodies: controller.pushBodies,\n });\n this.#controllers.set(colliderHandle(collider), rapierController);\n }\n\n /**\n * Scales the colliders' densities so the body weighs exactly `Rigidbody2D.mass`.\n *\n * @param record - The record being built.\n */\n #applyMass(record: BodyRecord2D): void {\n const wanted = record.rigidbody?.mass ?? 0;\n if (record.motion !== BodyMotion2D.dynamic || wanted <= 0) {\n return;\n }\n const current = bodyMass(record.body);\n if (current <= 0) {\n return;\n }\n const density = wanted / current;\n for (const collider of record.colliders) {\n setColliderDensity(collider, density);\n }\n recomputeMass(record.body);\n }\n\n /**\n * Keeps a record in step with its entity: component changes rebuild, destruction removes.\n *\n * @param record - The record to watch.\n */\n #watchEntity(record: BodyRecord2D): void {\n const entity = record.entity;\n record.disconnects.push(\n entity.onComponentAdded.connect((component: Component): void => {\n this.#onComponentChanged(record, component);\n }),\n entity.onComponentRemoved.connect((component: Component): void => {\n this.#onComponentChanged(record, component);\n }),\n entity.onDestroyed.connect((): void => {\n this.#removeRecord(entity);\n }),\n );\n }\n\n /**\n * Recomputes `collisionEvents` when a component joins or leaves the entity.\n *\n * @param record - The body record.\n * @param component - The component that changed.\n */\n #onComponentChanged(record: BodyRecord2D, component: Component): void {\n if (\n component instanceof Collider2D ||\n component instanceof Rigidbody2D ||\n component instanceof CharacterController2D\n ) {\n this.markDirty(record.entity);\n return;\n }\n this.#applyCollisionEvents(record);\n }\n\n /**\n * Pushes the effective `collisionEvents` value to every collider of a body.\n *\n * @param record - The body record.\n */\n #applyCollisionEvents(record: BodyRecord2D): void {\n const mode = record.rigidbody?.collisionEvents ?? \"auto\";\n const enabled = mode === \"on\" ? true : mode === \"off\" ? false : this.#entityWantsEvents(record.entity);\n if (enabled === record.collisionEvents) {\n return;\n }\n record.collisionEvents = enabled;\n for (let index = 0; index < record.colliders.length; index += 1) {\n const collider = record.colliders[index];\n const owner = record.owners[index];\n if (collider !== undefined) {\n // A sensor always reports: a trigger volume nobody listens to is free, and a trigger volume\n // whose *other* side listens must still raise the pair.\n setColliderEvents(collider, enabled || owner?.isTrigger === true);\n }\n }\n }\n\n /**\n * Whether any script on an entity implements a physics callback.\n *\n * @param entity - The entity.\n * @returns `true` when at least one does.\n */\n #entityWantsEvents(entity: Entity): boolean {\n return (\n this.#ctx.entityImplements(entity, \"onCollisionEnter\") ||\n this.#ctx.entityImplements(entity, \"onCollisionStay\") ||\n this.#ctx.entityImplements(entity, \"onCollisionExit\") ||\n this.#ctx.entityImplements(entity, \"onTriggerEnter\") ||\n this.#ctx.entityImplements(entity, \"onTriggerExit\")\n );\n }\n\n /**\n * Drops an entity's body immediately.\n *\n * @param entity - The entity whose body goes away.\n */\n #removeRecord(entity: Entity): void {\n const record = this.#byEntity.get(entity);\n if (record === undefined) {\n return;\n }\n this.#teardown(record);\n const index = this.#records.indexOf(record);\n if (index >= 0) {\n this.#records.splice(index, 1);\n }\n this.#byEntity.delete(entity);\n this.#dirty.delete(entity);\n this.#colliderSetChanged = true;\n }\n\n /**\n * Releases one record's Rapier resources, signal connections, and index entries.\n *\n * @param record - The record to tear down.\n */\n #teardown(record: BodyRecord2D): void {\n for (const disconnect of record.disconnects) {\n disconnect();\n }\n record.disconnects = [];\n for (const collider of record.colliders) {\n const handle = colliderHandle(collider);\n const controller = this.#controllers.get(handle);\n if (controller !== undefined) {\n destroyController(this.#world, controller);\n this.#controllers.delete(handle);\n }\n this.#byCollider.delete(handle);\n this.#oneWay.delete(handle);\n this.#dropPairs(handle);\n this.#colliderCount -= 1;\n }\n record.colliders = [];\n record.owners = [];\n record.controllerCollider = null;\n // Removing the body removes its colliders with it (`pipeline/world.d.ts`, `removeRigidBody`).\n destroyBody(this.#world, record.body);\n }\n\n /**\n * Forgets every touching pair a departing collider took part in.\n *\n * @param handle - The collider handle.\n */\n #dropPairs(handle: number): void {\n for (const [key, pair] of this.#pairs) {\n if (pair.first === handle || pair.second === handle) {\n this.#pairs.delete(key);\n }\n }\n }\n\n /** Pushes every kinematic body's authored transform into Rapier before the step. */\n #pushKinematicPoses(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record === undefined || record.motion !== BodyMotion2D.kinematic || record.controller !== null) {\n continue;\n }\n const transform = record.entity.transform;\n transform.positionToRef(this.#scratchPose);\n setNextKinematicPose(\n record.body,\n this.#scratchVector.set(this.#scratchPose.x, this.#scratchPose.y),\n toRadians(transform.rotation2D),\n );\n }\n }\n\n /** Caches every one-way platform's world top, so the move filter needs no Rapier call. */\n #refreshOneWayPlatforms(): void {\n for (const platform of this.#oneWay.values()) {\n readColliderTranslation(platform.collider, this.#filterScratch);\n platform.top = this.#filterScratch.y + platform.halfHeight;\n }\n }\n\n /**\n * Remembers every event-reporting body's velocity before the solver runs, so a collision callback\n * can report the speed the two bodies met at rather than the one they left with.\n */\n #capturePreStepVelocities(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record !== undefined && record.collisionEvents) {\n readLinearVelocity(record.body, record.preStepVelocity);\n }\n }\n }\n\n /**\n * Hands every character controller its accumulated displacement and turns the resolved movement\n * into a kinematic target.\n *\n * @param fixedDeltaSeconds - The fixed step.\n */\n #moveControllers(fixedDeltaSeconds: number): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n const component = record?.controller ?? null;\n const collider = record?.controllerCollider ?? null;\n if (record === undefined || component === null || collider === null) {\n continue;\n }\n const rapierController = this.#controllers.get(colliderHandle(collider));\n if (rapierController === undefined) {\n continue;\n }\n const desired = component.takePendingMove();\n this.#filterDesiredY = desired.y;\n this.#filterFeet = record.currentPosition.y + component.offset.y - halfHeightOf(component);\n const grounded = computeControllerMovement(\n rapierController,\n collider,\n desired,\n interactionGroups(1 << record.layer, this.#collideMasks[record.layer] ?? ALL_LAYER_BITS),\n component.onOneWayPlatforms ? this.#oneWayFilter : null,\n this.#movement,\n );\n const normal = this.#collectControllerHits(rapierController, component);\n component.applyStepResult(this.#movement, grounded, normal, fixedDeltaSeconds);\n setNextKinematicPose(\n record.body,\n this.#scratchVector.set(\n record.currentPosition.x + this.#movement.x,\n record.currentPosition.y + this.#movement.y,\n ),\n record.currentRotation,\n );\n }\n }\n\n /**\n * The one-way platform predicate, bound once so the per-step move allocates nothing.\n *\n * Rapier runs it from inside WebAssembly, so it reads only plain JavaScript state:\n * {@link Physics2DRuntime.#refreshOneWayPlatforms} caches every platform's world top *before* the\n * move, because calling back into Rapier (`collider.translation()`) from inside the callback is a\n * re-entrant WebAssembly call and the obstacle is then never actually excluded (measured, S6.2).\n *\n * @param collider - The obstacle Rapier is considering.\n * @returns `false` when the obstacle should be ignored this step.\n */\n readonly #oneWayFilter = (collider: RapierCollider): boolean => {\n const platform = this.#oneWay.get(colliderHandle(collider));\n if (platform === undefined) {\n return true;\n }\n // Solid only when the character is coming down and its feet already cleared the platform's top.\n return this.#filterDesiredY <= 0 && this.#filterFeet >= platform.top - ONE_WAY_TOLERANCE;\n };\n\n /**\n * Reports every obstacle a controller hit and returns the most upward-facing normal.\n *\n * @remarks\n * Sensors are filtered out of the move (`lite/rapier/character.ts`), so a trigger volume never\n * shows up here and never contributes a ground normal.\n *\n * @param rapierController - Rapier's controller.\n * @param component - The component.\n * @returns The ground normal, or `null` when nothing was hit.\n */\n #collectControllerHits(rapierController: RapierController, component: CharacterController2D): Vec2Like | null {\n const count = controllerHitCount(rapierController);\n let best: Vec2Like | null = null;\n let bestY = -2;\n for (let index = 0; index < count; index += 1) {\n if (!readControllerHit(rapierController, index, this.#controllerHit)) {\n continue;\n }\n const normal = this.#controllerHit.normal;\n if (normal.y > bestY) {\n bestY = normal.y;\n this.#groundNormal.set(normal.x, normal.y);\n best = this.#groundNormal;\n }\n const binding = this.bindingOf(this.#controllerHit.collider);\n const event: MutableCharacterCollision = (this.#characterCollision ??= {\n other: null,\n otherCollider: null,\n point: this.#controllerHit.point,\n normal: this.#controllerHit.normal,\n });\n event.other = binding?.record.entity ?? null;\n event.otherCollider = binding?.owner ?? null;\n component.reportHit(event);\n }\n return best;\n }\n\n /** Snapshots every body's authoritative pose and writes it onto the entity. */\n #snapshot(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record === undefined) {\n continue;\n }\n record.previousPosition.copyFrom(record.currentPosition);\n record.previousRotation = record.currentRotation;\n record.currentRotation = readBodyPose(record.body, record.currentPosition);\n if (record.motion === BodyMotion2D.static && record.rigidbody === null && record.controller === null) {\n this.#checkStaticMove(record);\n continue;\n }\n this.#writeTransform(record.entity, record.currentPosition, toDegrees(record.currentRotation));\n }\n }\n\n /**\n * Reports `IGX-1151` once for an implicit static body whose transform moved.\n *\n * @param record - The body record.\n */\n #checkStaticMove(record: BodyRecord2D): void {\n const version = record.entity.transform.worldMatrixVersion;\n if (version === record.staticVersion || record.reportedMove) {\n return;\n }\n record.entity.transform.positionToRef(this.#scratchPose);\n if (\n Math.abs(this.#scratchPose.x - record.currentPosition.x) < STATIC_MOVE_EPSILON &&\n Math.abs(this.#scratchPose.y - record.currentPosition.y) < STATIC_MOVE_EPSILON\n ) {\n record.staticVersion = version;\n return;\n }\n record.staticVersion = version;\n record.reportedMove = true;\n this.#ctx.log.warn(\n `${Physics2DErrorCode.movedStaticBody}: {entity} has 2D colliders but no Rigidbody2D, and it moved after its body was placed.`,\n record.entity.name,\n );\n }\n\n /**\n * Writes a 2D pose onto an entity without disturbing its Z.\n *\n * @param entity - The entity.\n * @param position - The world position in metres.\n * @param rotationDegrees - The rotation in degrees counter-clockwise.\n */\n #writeTransform(entity: Entity, position: Vec2Like, rotationDegrees: number): void {\n const transform = entity.transform;\n transform.positionToRef(this.#scratchPose);\n this.#scratchPose.set(position.x, position.y, this.#scratchPose.z);\n transform.position = this.#scratchPose;\n transform.rotation2D = rotationDegrees;\n }\n\n /** Drains Rapier's event queue and delivers enter, stay, and exit to both participants. */\n #dispatchEvents(): void {\n this.#startedPairs.length = 0;\n this.#endedPairs.length = 0;\n drainCollisions(this.#queue, (event): void => {\n const key = pairKey(event.first, event.second);\n if (!event.started) {\n const existing = this.#pairs.get(key);\n if (existing !== undefined) {\n this.#pairs.delete(key);\n this.#endedPairs.push(existing);\n }\n return;\n }\n const first = this.#byCollider.get(event.first);\n const second = this.#byCollider.get(event.second);\n if (first === undefined || second === undefined) {\n return;\n }\n const pair: PairState = {\n first: event.first,\n second: event.second,\n sensor: isColliderSensor(first.collider) || isColliderSensor(second.collider),\n entered: true,\n };\n this.#pairs.set(key, pair);\n this.#startedPairs.push(pair);\n });\n for (const pair of this.#endedPairs) {\n this.#deliverPair(pair, \"exit\");\n }\n for (const pair of this.#startedPairs) {\n this.#deliverPair(pair, \"enter\");\n }\n for (const pair of this.#pairs.values()) {\n if (pair.entered) {\n pair.entered = false;\n continue;\n }\n this.#deliverPair(pair, \"stay\");\n }\n }\n\n /**\n * Delivers one pair's event to both participants.\n *\n * @param pair - The touching pair.\n * @param phase - Which callback to deliver.\n */\n #deliverPair(pair: PairState, phase: \"enter\" | \"stay\" | \"exit\"): void {\n const first = this.#byCollider.get(pair.first);\n const second = this.#byCollider.get(pair.second);\n if (first === undefined || second === undefined) {\n return;\n }\n if (pair.sensor) {\n if (phase === \"stay\") {\n return;\n }\n this.#triggerEventCount += 1;\n const kind: PhysicsCallbackName = phase === \"enter\" ? \"onTriggerEnter\" : \"onTriggerExit\";\n this.#deliverTrigger(first, second, kind);\n this.#deliverTrigger(second, first, kind);\n return;\n }\n this.#collisionEventCount += 1;\n const kind: PhysicsCallbackName =\n phase === \"enter\" ? \"onCollisionEnter\" : phase === \"exit\" ? \"onCollisionExit\" : \"onCollisionStay\";\n this.#readContacts(pair, phase);\n this.#deliverCollision(first, second, kind);\n this.#deliverCollision(second, first, kind);\n }\n\n /**\n * Fills the pooled contact point for a collision event.\n *\n * @param pair - The touching pair.\n * @param phase - Which callback is being delivered; an exit has no contact left to read.\n */\n #readContacts(pair: PairState, phase: \"enter\" | \"stay\" | \"exit\"): void {\n this.#contactPoint.impulse = 0;\n if (phase === \"exit\") {\n return;\n }\n const first = this.#byCollider.get(pair.first);\n const second = this.#byCollider.get(pair.second);\n if (first === undefined || second === undefined) {\n return;\n }\n readContact(this.#world, first.collider, second.collider, this.#contact);\n if (!this.#contact.touching) {\n return;\n }\n this.#contactPoint.point.copyFrom(this.#contact.point);\n this.#contactPoint.normal.copyFrom(this.#contact.normal);\n this.#contactPoint.impulse = this.#contact.impulse;\n }\n\n /**\n * Delivers one trigger callback to one entity.\n *\n * @param target - The binding whose scripts receive it.\n * @param other - The other participant.\n * @param kind - Which callback to deliver.\n */\n #deliverTrigger(target: ColliderBinding, other: ColliderBinding, kind: PhysicsCallbackName): void {\n if (target.record.entity.isDestroyed) {\n return;\n }\n const event: MutableTrigger = (this.#trigger ??= {\n other: null,\n otherCollider: null,\n self: target.record.entity,\n selfCollider: null,\n });\n event.self = target.record.entity;\n event.selfCollider = target.owner;\n event.other = other.record.entity;\n event.otherCollider = other.owner;\n this.#ctx.dispatchScriptCallback(target.record.entity, kind, event);\n }\n\n /**\n * Delivers one collision callback to one entity.\n *\n * @param target - The binding whose scripts receive it.\n * @param other - The other participant.\n * @param kind - Which callback to deliver.\n */\n #deliverCollision(target: ColliderBinding, other: ColliderBinding, kind: PhysicsCallbackName): void {\n if (target.record.entity.isDestroyed || !target.record.collisionEvents) {\n return;\n }\n this.#relativeVelocity.set(\n target.record.preStepVelocity.x - other.record.preStepVelocity.x,\n target.record.preStepVelocity.y - other.record.preStepVelocity.y,\n );\n const collision: MutableCollision = (this.#collision ??= {\n other: null,\n otherCollider: null,\n self: target.record.entity,\n selfCollider: null,\n contacts: this.#contacts,\n relativeVelocity: this.#relativeVelocity,\n });\n collision.self = target.record.entity;\n collision.selfCollider = target.owner;\n collision.other = other.record.entity;\n collision.otherCollider = other.owner;\n this.#ctx.dispatchScriptCallback(target.record.entity, kind, collision);\n }\n\n /**\n * Whether a body's display pose is interpolated.\n *\n * @param rigidbody - The `Rigidbody2D`, or `null`.\n * @param controller - The `CharacterController2D`, or `null`.\n * @param motion - The motion type.\n * @returns `true` when interpolation applies.\n */\n #shouldInterpolate(\n rigidbody: Rigidbody2D | null,\n controller: CharacterController2D | null,\n motion: BodyMotion2D,\n ): boolean {\n if (!this.#settings.interpolation || motion === BodyMotion2D.static) {\n return false;\n }\n if (controller !== null) {\n return controller.interpolation === \"interpolate\";\n }\n return rigidbody !== null && rigidbody.interpolation === \"interpolate\";\n }\n}\n\n/** How far below a one-way platform's top the character's feet may be and still land on it. */\nconst ONE_WAY_TOLERANCE = 0.02;\n\n/**\n * The colliders that make up an entity's body: every enabled `Collider2D` on the entity itself.\n *\n * @param entity - The entity.\n * @returns The colliders, in component order.\n */\nfunction collidersOf(entity: Entity): readonly Collider2D[] {\n const found: Collider2D[] = [];\n const components = entity.components;\n for (let index = 0; index < components.length; index += 1) {\n const component = components[index];\n if (component instanceof Collider2D && component.enabled && !component.isDestroyed) {\n found.push(component);\n }\n }\n return found;\n}\n\n/**\n * The motion type a body takes.\n *\n * @param rigidbody - The component, or `null`.\n * @param controller - The character controller, or `null`.\n * @returns The motion type; a controller is always kinematic.\n */\nfunction motionOf(rigidbody: Rigidbody2D | null, controller: CharacterController2D | null): BodyMotion2D {\n if (controller !== null) {\n return BodyMotion2D.kinematic;\n }\n if (rigidbody === null || rigidbody.bodyType === \"static\") {\n return BodyMotion2D.static;\n }\n return rigidbody.bodyType === \"kinematic\" ? BodyMotion2D.kinematic : BodyMotion2D.dynamic;\n}\n\n/**\n * How far a shape reaches above its own origin, which is what a one-way platform's top is measured\n * from.\n *\n * @param shape - The geometry.\n * @returns The half-height in metres.\n */\nfunction topOf(shape: CollectedShape2D[\"shape\"]): number {\n if (shape.kind === \"box\") {\n return shape.halfHeight;\n }\n if (shape.kind === \"circle\") {\n return shape.radius;\n }\n if (shape.kind === \"capsule\") {\n return shape.halfHeight + shape.radius;\n }\n let top = 0;\n for (let index = 1; index < shape.points.length; index += 2) {\n top = Math.max(top, shape.points[index] ?? 0);\n }\n return top;\n}\n\n/**\n * Half a character controller's total height, including its caps.\n *\n * @param component - The controller.\n * @returns The half-height in metres.\n */\nfunction halfHeightOf(component: CharacterController2D): number {\n return Math.max(component.radius, component.height / 2);\n}\n\n/**\n * The map key of one collider pair, order-independent.\n *\n * @param first - One handle.\n * @param second - The other.\n * @returns The key.\n */\nfunction pairKey(first: number, second: number): string {\n return first < second ? `${String(first)}:${String(second)}` : `${String(second)}:${String(first)}`;\n}\n\n/**\n * The shortest signed angular difference between two angles, so interpolation never spins the long\n * way round.\n *\n * @param from - The start angle in radians.\n * @param to - The end angle in radians.\n * @returns The signed difference in `(-π, π]`.\n */\nfunction shortestArc(from: number, to: number): number {\n const twoPi = Math.PI * 2;\n const delta = ((((to - from + Math.PI) % twoPi) + twoPi) % twoPi) - Math.PI;\n return delta;\n}\n\n/**\n * A monotonic millisecond reading for the `stepMs` counter. It never feeds the simulation, so it\n * does not break the \"no wall-clock reads\" rule of `09-physics.md` §8.\n *\n * @returns Milliseconds since the process started.\n */\nfunction now(): number {\n return performance.now();\n}\n","import type { Physics2DHost } from \"./host.js\";\nimport type { System, SystemContext } from \"@ignifx/core\";\n\n/**\n * The three systems `docs/architecture/01-lifecycle-and-time.md` §3 and `09-physics.md` §1 put\n * physics in the frame with — identical in 2D, except that Rapier is stepped directly and there is\n * no Babylon Lite simulation scene:\n *\n * ```\n * Systems(FixedUpdate, -100) restore the authoritative pose of interpolated bodies\n * scripts.fixedUpdate(dt) forces, velocities, CharacterController2D.move()\n * Systems(FixedUpdate, 100) step Rapier, snapshot poses, dispatch collision and trigger events\n * ── the fixed loop ends; time.fixedStepAlpha is final ──\n * Systems(Update, -900) write lerp(prev, cur, time.fixedStepAlpha)\n * scripts.update / lateUpdate, animation, rendering — all read the display pose\n * ```\n *\n * **Why the display pose is written at the top of `Update` and not in `PreRender`** (2026-09-08).\n * It used to be written in `PreRender`, after `lateUpdate`. `Camera2DFollow` and every hand-written\n * follow camera run in `update`/`lateUpdate`, so they framed the character where the last fixed step\n * left it while the renderer drew it at `lerp(prev, cur, alpha)`: up to one fixed step of relative\n * judder every frame, which a pixel-perfect camera turns into a visibly shaking sprite because the\n * two poses are quantised independently. Writing the pose before `scripts.update` means scripts,\n * animation, camera rigs and the render sync all see the pose the frame presents, which is Unity's\n * model. The simulation is unaffected: the restore system at `FixedUpdate −100` runs before\n * `scripts.fixedUpdate`, so `fixedUpdate` and Rapier still see the authoritative pose.\n *\n * Everything here is `@internal`.\n */\n\n/** The `FixedUpdate` order the restore system runs at. */\nexport const PHYSICS_2D_RESTORE_ORDER = -100;\n\n/** The `FixedUpdate` order the step system runs at. */\nexport const PHYSICS_2D_STEP_ORDER = 100;\n\n/**\n * The `Update` order the interpolation system runs at. Negative, so it lands in\n * `Systems(Update, order < 0)` — after the fixed loop and lifecycle flush B, before\n * `scripts.update` — and low enough that a game's own `Update` systems can still be ordered in\n * front of it if they must see the authoritative pose.\n */\nexport const PHYSICS_2D_INTERPOLATE_ORDER = -900;\n\n/**\n * Undoes the display pose the interpolation system wrote at the top of the previous `Update`, so\n * `fixedUpdate` and Rapier both see the authoritative pose.\n *\n * @internal\n */\nexport class Physics2DRestoreSystem implements System {\n /** The name diagnostics and error reports use. */\n readonly name = \"physics2d-restore\";\n\n readonly #host: Physics2DHost;\n\n /**\n * Binds the system to the runtime holder.\n *\n * @param host - The holder the runtime lands in.\n */\n constructor(host: Physics2DHost) {\n this.#host = host;\n }\n\n /** Restores every interpolated body's authoritative pose. */\n update(): void {\n this.#host.runtime?.restorePoses();\n }\n}\n\n/**\n * Steps Rapier once, snapshots the poses, and dispatches the step's events.\n *\n * @internal\n */\nexport class Physics2DStepSystem implements System {\n /** The name diagnostics and error reports use. */\n readonly name = \"physics2d-step\";\n\n readonly #host: Physics2DHost;\n\n /**\n * Binds the system to the runtime holder.\n *\n * @param host - The holder the runtime lands in.\n */\n constructor(host: Physics2DHost) {\n this.#host = host;\n }\n\n /**\n * Runs one fixed step.\n *\n * @param ctx - The world, clock, phase, and fixed delta.\n */\n update(ctx: SystemContext): void {\n this.#host.runtime?.step(ctx.dt);\n }\n}\n\n/**\n * Writes the interpolated display pose once per frame.\n *\n * @internal\n */\nexport class Physics2DInterpolationSystem implements System {\n /** The name diagnostics and error reports use. */\n readonly name = \"physics2d-interpolate\";\n\n readonly #host: Physics2DHost;\n\n /**\n * Binds the system to the runtime holder.\n *\n * @param host - The holder the runtime lands in.\n */\n constructor(host: Physics2DHost) {\n this.#host = host;\n }\n\n /**\n * Writes `lerp(prev, cur, alpha)` into every interpolated body's transform.\n *\n * @param ctx - The world, clock, phase, and frame delta.\n */\n update(ctx: SystemContext): void {\n this.#host.runtime?.interpolate(ctx.time.fixedStepAlpha);\n }\n}\n","import { Vec2 } from \"@ignifx/core\";\nimport { Physics2DErrorCode, physics2DError } from \"./errors.js\";\nimport { castRay, castRayAll, castShape, interactionGroups, overlapShape, toRadians } from \"./lite/rapier/world.js\";\nimport type { RapierShape2D, RapierWorld, RayResult2D, ShapeCastResult2D } from \"./lite/rapier/world.js\";\nimport type { QueryOptions2D, RaycastHit2D, ShapeCastHit2D } from \"./queries.js\";\nimport type { Physics2DHost } from \"./runtime/host.js\";\nimport type { Physics2DRuntime } from \"./runtime/runtime.js\";\nimport type { Physics2DSettings } from \"./settings.js\";\nimport type { Entity, Vec2Like } from \"@ignifx/core\";\n\n/**\n * `app.physics2d` (`docs/architecture/11-2d-toolkit.md` §8, `09-physics.md` §5): gravity, the\n * queries, and the Rapier escape hatch.\n *\n * Every query is shape-accurate and reports the exact collider it hit, because Rapier's query\n * pipeline returns the `Collider` (`pipeline/world.d.ts`). The bounds-index approximations\n * `09-physics.md` §5 documents for Havok have no counterpart here.\n */\n\n/** How far a ray travels when the caller names no distance, in metres. */\nconst DEFAULT_RAY_DISTANCE = 1e4;\n\n/** Every layer bit Rapier can express. */\nconst ALL_LAYERS = 0xff_ff;\n\n/**\n * The Rapier objects the 2D physics extension owns. Unstable escape hatch\n * (`docs/architecture/00-overview.md` §3).\n *\n * @public\n */\nexport interface Physics2DRapierHandles {\n /** Rapier's `World`. */\n readonly world: RapierWorld;\n}\n\n/**\n * The service behind `app.physics2d`.\n *\n * @example\n * ```ts\n * const hit = app.physics2d.raycast({ x: 0, y: 5 }, { x: 0, y: -1 }, 20);\n * if (hit !== null) {\n * app.log.info(`ray hit ${hit.entity.name} at ${String(hit.point.y)}`);\n * }\n * ```\n *\n * @public\n */\nexport class Physics2DService {\n readonly #host: Physics2DHost;\n\n readonly #gravity = new Vec2();\n\n readonly #point = new Vec2();\n\n readonly #normal = new Vec2();\n\n readonly #rayResult: RayResult2D = { hit: false, collider: null, distance: 0 };\n\n readonly #sweepResult: ShapeCastResult2D = { hit: false, collider: null, distance: 0 };\n\n readonly #hits: RaycastHit2D[] = [];\n\n readonly #overlaps: Entity[] = [];\n\n /**\n * Wraps the holder the extension fills in from `onStart`.\n *\n * @param host - The holder the runtime lands in.\n * @param settings - The resolved `physics2d` settings section.\n *\n * @internal\n */\n constructor(host: Physics2DHost, settings: Physics2DSettings) {\n this.#host = host;\n this.#gravity.set(settings.gravity.x, settings.gravity.y);\n }\n\n /**\n * World gravity in metres per second squared.\n *\n * @returns A live view; writing to it does nothing, assign the property instead.\n */\n get gravity(): Vec2 {\n return this.#gravity;\n }\n\n /**\n * Replaces world gravity, which every body feels from the next fixed step.\n *\n * @param value - The new acceleration vector.\n */\n set gravity(value: Vec2Like) {\n this.#gravity.set(value.x, value.y);\n this.#rt().setGravity(this.#gravity);\n }\n\n /**\n * The Rapier handles.\n *\n * @returns The world.\n */\n get rapier(): Physics2DRapierHandles {\n return { world: this.#rt().world };\n }\n\n /**\n * Casts a ray and returns the first entity it hits.\n *\n * @param origin - The world-space origin, in metres.\n * @param direction - The direction; it is normalised for you.\n * @param maxDistance - How far to travel; defaults to 10 km.\n * @param options - Layer mask and trigger behaviour.\n * @returns The hit, or `null` when the ray clears everything.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n raycast(\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number = DEFAULT_RAY_DISTANCE,\n options?: QueryOptions2D,\n ): RaycastHit2D | null {\n this.#requireStep(\"raycast\");\n const unit = normalize(direction);\n if (unit === null) {\n return null;\n }\n castRay(\n this.#rt().world,\n origin,\n unit,\n maxDistance,\n groupsOf(options),\n options?.hitTriggers === true,\n this.#point,\n this.#normal,\n this.#rayResult,\n );\n return this.#toHit(this.#rayResult.collider, this.#rayResult.distance, this.#point, this.#normal);\n }\n\n /**\n * Casts a ray and returns every entity along it, nearest first.\n *\n * @param origin - The world-space origin.\n * @param direction - The direction; it is normalised for you.\n * @param maxDistance - How far to travel; defaults to 10 km.\n * @param options - Layer mask and trigger behaviour.\n * @returns The hits, in increasing distance. The array is reused between calls.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n raycastAll(\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number = DEFAULT_RAY_DISTANCE,\n options?: QueryOptions2D,\n ): readonly RaycastHit2D[] {\n this.#requireStep(\"raycastAll\");\n this.#hits.length = 0;\n const unit = normalize(direction);\n if (unit === null) {\n return this.#hits;\n }\n const runtime = this.#rt();\n castRayAll(\n runtime.world,\n origin,\n unit,\n maxDistance,\n groupsOf(options),\n options?.hitTriggers === true,\n (collider, distance, normalX, normalY): void => {\n const binding = runtime.bindingOf(collider);\n if (binding === null) {\n return;\n }\n this.#hits.push({\n entity: binding.record.entity,\n collider: binding.owner,\n point: new Vec2(origin.x + unit.x * distance, origin.y + unit.y * distance),\n normal: new Vec2(normalX, normalY),\n distance,\n });\n },\n );\n this.#hits.sort(byDistance);\n return this.#hits;\n }\n\n /**\n * Lists the entities a circle overlaps.\n *\n * @param centre - The circle's world position.\n * @param radius - Its radius in metres.\n * @param options - Layer mask and trigger behaviour.\n * @returns The overlapping entities, in Rapier's order. The array is reused between calls.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n overlapCircle(centre: Vec2Like, radius: number, options?: QueryOptions2D): readonly Entity[] {\n return this.#overlap({ kind: \"circle\", radius }, centre, 0, options, \"overlapCircle\");\n }\n\n /**\n * Lists the entities a box overlaps.\n *\n * @param centre - The box's world position.\n * @param size - Its full width and height in metres.\n * @param rotation - Its rotation in degrees counter-clockwise; defaults to `0`.\n * @param options - Layer mask and trigger behaviour.\n * @returns The overlapping entities. The array is reused between calls.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n overlapBox(centre: Vec2Like, size: Vec2Like, rotation: number = 0, options?: QueryOptions2D): readonly Entity[] {\n return this.#overlap(\n { kind: \"box\", halfWidth: size.x / 2, halfHeight: size.y / 2 },\n centre,\n toRadians(rotation),\n options,\n \"overlapBox\",\n );\n }\n\n /**\n * Sweeps a circle and returns the first contact.\n *\n * @param centre - Where the sweep starts.\n * @param radius - The circle's radius in metres.\n * @param direction - The sweep direction; it is normalised for you.\n * @param maxDistance - How far to sweep.\n * @param options - Layer mask and trigger behaviour.\n * @returns The hit, or `null`.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n shapeCast(\n centre: Vec2Like,\n radius: number,\n direction: Vec2Like,\n maxDistance: number,\n options?: QueryOptions2D,\n ): ShapeCastHit2D | null {\n this.#requireStep(\"shapeCast\");\n const unit = normalize(direction);\n if (unit === null) {\n return null;\n }\n castShape(\n this.#rt().world,\n { kind: \"circle\", radius },\n centre,\n 0,\n unit,\n maxDistance,\n groupsOf(options),\n options?.hitTriggers === true,\n this.#point,\n this.#normal,\n this.#sweepResult,\n );\n const binding = this.#rt().bindingOf(this.#sweepResult.collider);\n if (!this.#sweepResult.hit || binding === null) {\n return null;\n }\n return {\n entity: binding.record.entity,\n collider: binding.owner,\n point: this.#point.clone(),\n normal: this.#normal.clone(),\n distance: this.#sweepResult.distance,\n fraction: maxDistance > 0 ? this.#sweepResult.distance / maxDistance : 0,\n };\n }\n\n /**\n * Runs one overlap query.\n *\n * @param shape - The query geometry.\n * @param centre - Its world position.\n * @param rotation - Its rotation in radians.\n * @param options - Layer mask and trigger behaviour.\n * @param name - The query name, for the diagnostic.\n * @returns The overlapping entities.\n */\n #overlap(\n shape: RapierShape2D,\n centre: Vec2Like,\n rotation: number,\n options: QueryOptions2D | undefined,\n name: string,\n ): readonly Entity[] {\n this.#requireStep(name);\n this.#overlaps.length = 0;\n const runtime = this.#rt();\n overlapShape(\n runtime.world,\n shape,\n centre,\n rotation,\n groupsOf(options),\n options?.hitTriggers === true,\n (collider): void => {\n const entity = runtime.bindingOf(collider)?.record.entity ?? null;\n if (entity !== null && !this.#overlaps.includes(entity)) {\n this.#overlaps.push(entity);\n }\n },\n );\n return this.#overlaps;\n }\n\n /**\n * Turns a Rapier collider and a distance into a public hit.\n *\n * @param collider - The collider that was hit, or `null`.\n * @param distance - How far away it was.\n * @param point - The contact point.\n * @param normal - The contact normal.\n * @returns The hit, or `null`.\n */\n #toHit(collider: RayResult2D[\"collider\"], distance: number, point: Vec2, normal: Vec2): RaycastHit2D | null {\n const binding = this.#rt().bindingOf(collider);\n if (binding === null) {\n return null;\n }\n return {\n entity: binding.record.entity,\n collider: binding.owner,\n point: point.clone(),\n normal: normal.clone(),\n distance,\n };\n }\n\n /**\n * The runtime, once `onStart` has built it.\n *\n * @returns The runtime.\n * @throws IgnifxError with code `IGX-1150` when Rapier has not finished loading.\n */\n #rt(): Physics2DRuntime {\n const runtime = this.#host.runtime;\n if (runtime === null) {\n throw physics2DError(Physics2DErrorCode.rapierUnavailable, \"The 2D physics world does not exist yet.\", {\n hint: \"app.physics2d is usable from the first frame onwards; await app.start() first.\",\n });\n }\n return runtime;\n }\n\n /**\n * Refuses a query that ran before Rapier built its broadphase (spike S6.2).\n *\n * @param query - The query name, for the diagnostic.\n * @throws IgnifxError with code `IGX-1153` when no step has completed.\n */\n #requireStep(query: string): void {\n if (this.#rt().hasStepped) {\n return;\n }\n throw physics2DError(Physics2DErrorCode.queryBeforeStep, `${query}() ran before the first fixed step.`, {\n context: { query },\n hint: \"Step the app at least once (app.step(1 / 60)) before querying; Rapier builds its broadphase in the first step.\",\n });\n }\n}\n\n/**\n * The packed interaction word a query filters with: it belongs to every layer and collides with the\n * layers the caller named.\n *\n * @param options - The query options.\n * @returns The packed word.\n */\nfunction groupsOf(options: QueryOptions2D | undefined): number {\n const mask = options?.layerMask?.bits ?? ALL_LAYERS;\n return interactionGroups(ALL_LAYERS, mask & ALL_LAYERS);\n}\n\n/**\n * Normalises a direction.\n *\n * @param direction - The direction.\n * @returns A fresh unit vector, or `null` when the direction is zero.\n */\nfunction normalize(direction: Vec2Like): Vec2Like | null {\n const length = Math.hypot(direction.x, direction.y);\n return length === 0 ? null : { x: direction.x / length, y: direction.y / length };\n}\n\n/**\n * Orders two hits by distance.\n *\n * @param first - One hit.\n * @param second - The other.\n * @returns A negative number when `first` is nearer.\n */\nfunction byDistance(first: RaycastHit2D, second: RaycastHit2D): number {\n return first.distance - second.distance;\n}\n","import { array, bool, defineSchema, f32, map, record, str, u32, vec2 } from \"@ignifx/core\";\nimport type { Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * The `physics2d` project settings section (`docs/architecture/11-2d-toolkit.md` §8,\n * `09-physics.md` §6). It mirrors the 3D `physics` section with a 2D gravity vector and Rapier's\n * solver knob in place of Havok's world velocity clamps, which Rapier has no equivalent of.\n *\n * `fixedDeltaTime` is **not** here: the fixed step is shared with the core `time` section and the\n * extension reads it from `app.time.fixedDeltaTime` on every step.\n */\n\n/**\n * The section name as it appears in `ignifx.config.ts`.\n *\n * @public\n */\nexport const PHYSICS_2D_SETTINGS_SECTION = \"physics2d\";\n\n/**\n * A surface, either as the `physics2d.defaultMaterial` setting or inline on a collider.\n *\n * @public\n */\nexport interface Physics2DMaterialValues {\n /** The friction coefficient. */\n readonly friction: number;\n /** How much of the approach speed is returned, `0` to `1`. */\n readonly restitution: number;\n}\n\n/**\n * The resolved `physics2d` settings section.\n *\n * @example\n * ```ts\n * // ignifx.config.ts\n * export default {\n * layers: { layers: [\"Default\", \"Player\", \"Enemy\"] },\n * physics2d: {\n * gravity: { x: 0, y: -9.81 },\n * collisionMatrix: { Player: [\"Default\", \"Enemy\"], Enemy: [\"Default\"] },\n * },\n * };\n * ```\n *\n * @public\n */\nexport interface Physics2DSettings {\n /** World gravity in metres per second squared; +Y is up (`11-2d-toolkit.md` §3). */\n readonly gravity: Vec2Like;\n /**\n * Which layers each layer collides with. A layer the map does not mention collides with\n * everything, which is what makes the default project need no matrix at all.\n */\n readonly collisionMatrix: Readonly<Record<string, readonly string[]>>;\n /** The surface a collider with no material of its own uses. */\n readonly defaultMaterial: Physics2DMaterialValues;\n /** How many iterations Rapier's constraint solver runs; `0` leaves Rapier's own default (4). */\n readonly velocityIterations: number;\n /** Whether dynamic bodies and character controllers interpolate between fixed steps. */\n readonly interpolation: boolean;\n}\n\n/** Standard gravity in the 2D plane. */\nconst DEFAULT_GRAVITY: Vec2Like = Object.freeze({ x: 0, y: -9.81 });\n\n/** The default surface: ordinary friction, no bounce — the same numbers the 3D section uses. */\nconst DEFAULT_MATERIAL: Physics2DMaterialValues = Object.freeze({ friction: 0.6, restitution: 0 });\n\n/** An empty matrix, which means \"everything collides with everything\". */\nconst DEFAULT_COLLISION_MATRIX: Readonly<Record<string, readonly string[]>> = Object.freeze({});\n\n/**\n * Builds the schema the `physics2d` section is validated against.\n *\n * @remarks\n * It is a function, not a module-level constant: every field kind is a function call, and module\n * scope holds declarations and immutable constants only (`CONSTITUTION.md` §3.5).\n *\n * @returns The schema.\n *\n * @public\n */\nexport function physics2DSettingsSchema(): Schema {\n return defineSchema({\n gravity: vec2(DEFAULT_GRAVITY),\n collisionMatrix: map(array(str())),\n defaultMaterial: record({\n friction: f32(DEFAULT_MATERIAL.friction, { min: 0 }),\n restitution: f32(DEFAULT_MATERIAL.restitution, { min: 0, max: 1 }),\n }),\n velocityIterations: u32(0),\n interpolation: bool(true),\n });\n}\n\n/**\n * The values used when a project omits the `physics2d` section.\n *\n * @returns A fresh defaults object.\n *\n * @public\n */\nexport function defaultPhysics2DSettings(): Physics2DSettings {\n return {\n gravity: DEFAULT_GRAVITY,\n collisionMatrix: DEFAULT_COLLISION_MATRIX,\n defaultMaterial: DEFAULT_MATERIAL,\n velocityIterations: 0,\n interpolation: true,\n };\n}\n","import { defineExtension, Phase } from \"@ignifx/core\";\nimport { CharacterController2D } from \"./components/character-controller.js\";\nimport {\n BoxCollider2D,\n CapsuleCollider2D,\n CircleCollider2D,\n EdgeCollider2D,\n PolygonCollider2D,\n} from \"./components/colliders.js\";\nimport { Rigidbody2D } from \"./components/rigidbody.js\";\nimport { TilemapCollider2D } from \"./components/tilemap-collider.js\";\nimport { PHYSICS_2D_ERROR_MESSAGES, Physics2DErrorCode, physics2DError } from \"./errors.js\";\nimport { loadRapier } from \"./lite/rapier/module.js\";\nimport { createWorld } from \"./lite/rapier/world.js\";\nimport { createPhysicsMaterial2DLoader } from \"./material.js\";\nimport { Physics2DHost } from \"./runtime/host.js\";\nimport { Physics2DHostKey } from \"./runtime/runtime-key.js\";\nimport { PHYSICS_2D_DIAGNOSTICS_COUNTERS, PHYSICS_2D_DIAGNOSTICS_GROUP, Physics2DRuntime } from \"./runtime/runtime.js\";\nimport {\n PHYSICS_2D_INTERPOLATE_ORDER,\n PHYSICS_2D_RESTORE_ORDER,\n PHYSICS_2D_STEP_ORDER,\n Physics2DInterpolationSystem,\n Physics2DRestoreSystem,\n Physics2DStepSystem,\n} from \"./runtime/systems.js\";\nimport { Physics2DService } from \"./service.js\";\nimport { defaultPhysics2DSettings, PHYSICS_2D_SETTINGS_SECTION, physics2DSettingsSchema } from \"./settings.js\";\nimport type { Collider2D } from \"./components/collider.js\";\nimport type { Physics2DSettings } from \"./settings.js\";\nimport type { App, ComponentType, ConcreteComponentType, Extension, ExtensionContext } from \"@ignifx/core\";\n\n/**\n * The 2D toolkit is optional; physics only needs its tile-collision data type.\n * Create the Rapier world in `onStart`, once the world's layer table exists and WASM is ready.\n * Reject a world already using 3D physics with `IGX-1101`.\n */\n\n/**\n * What `physics2d()` accepts.\n *\n * @public\n */\nexport interface Physics2DOptions {\n /**\n * Replaces the step that instantiates Rapier's WebAssembly module. The default awaits\n * `RAPIER.init()`, which decodes the base64 payload the `-compat` build inlines; a host that has\n * already preloaded the module, or a test that wants to observe the failure path, supplies its\n * own.\n */\n readonly initialize?: () => Promise<void>;\n}\n\ndeclare module \"@ignifx/core\" {\n interface App {\n /** 2D physics: gravity, queries, and the Rapier escape hatch. */\n readonly physics2d: Physics2DService;\n }\n}\n\n/**\n * Builds the 2D physics extension.\n *\n * @param options - Optionally, an already-instantiated Rapier module.\n * @returns The extension descriptor.\n *\n * @example\n * ```ts\n * const app = await createApp({ headless: true, extensions: [physics2d()] });\n * ```\n *\n * @public\n */\nexport const physics2d: (options?: Physics2DOptions) => Extension = defineExtension<Physics2DOptions>(\n // `defineExtension` calls the factory with `undefined` when a game writes `physics2d()`, so the\n // default parameter is load-bearing even though the declared parameter type is not nullish.\n // oxlint-disable-next-line typescript/no-useless-default-assignment\n (options = {}) => {\n const host = new Physics2DHost();\n let context: ExtensionContext | null = null;\n\n return {\n name: \"@ignifx/physics-2d\",\n version: \"0.0.0\",\n engine: \">=0.0.0 <1.0.0\",\n requires: [],\n optional: [\"@ignifx/2d\", \"@ignifx/devtools\"],\n\n register(ctx: ExtensionContext): void {\n context = ctx;\n ctx.registerErrorCodes(PHYSICS_2D_ERROR_MESSAGES);\n ctx.registerSettings<Physics2DSettings>(\n PHYSICS_2D_SETTINGS_SECTION,\n physics2DSettingsSchema(),\n defaultPhysics2DSettings(),\n );\n ctx.registerComponents(PHYSICS_2D_COMPONENTS);\n ctx.registerAssetLoader(createPhysicsMaterial2DLoader());\n const settings = ctx.settings<Physics2DSettings>(PHYSICS_2D_SETTINGS_SECTION);\n const service = new Physics2DService(host, settings);\n ctx.registerService(Physics2DHostKey, host);\n ctx.registerService(Physics2DService, service);\n ctx.defineAppProperty(\"physics2d\", (): unknown => service);\n ctx.registerSystem(new Physics2DRestoreSystem(host), {\n phase: Phase.FixedUpdate,\n order: PHYSICS_2D_RESTORE_ORDER,\n });\n ctx.registerSystem(new Physics2DStepSystem(host), { phase: Phase.FixedUpdate, order: PHYSICS_2D_STEP_ORDER });\n // `Update`, not `PreRender`: camera rigs and scripts must read the same pose the renderer\n // draws (`runtime/systems.ts`, `docs/architecture/01-lifecycle-and-time.md` §3).\n ctx.registerSystem(new Physics2DInterpolationSystem(host), {\n phase: Phase.Update,\n order: PHYSICS_2D_INTERPOLATE_ORDER,\n });\n },\n\n async onStart(app: App): Promise<void> {\n const ctx = context;\n if (ctx === null) {\n return;\n }\n if (app.world.lite.simulationScene !== null) {\n throw physics2DError(\n Physics2DErrorCode.bothPhysicsExtensions,\n \"A world uses either physics() or physics2d(), and this one registered both.\",\n {\n context: { extension: \"@ignifx/physics-2d\" },\n hint: \"Remove physics() from a 2D game, or physics2d() from a 3D one.\",\n },\n );\n }\n const settings = ctx.settings<Physics2DSettings>(PHYSICS_2D_SETTINGS_SECTION);\n const initialize = options.initialize ?? defaultInitialize;\n try {\n await initialize();\n } catch (cause: unknown) {\n throw physics2DError(\n Physics2DErrorCode.rapierUnavailable,\n \"The Rapier 2D WebAssembly module could not be instantiated.\",\n { cause, hint: \"Reinstall @dimforge/rapier2d-compat; its WebAssembly is inlined and needs no serving.\" },\n );\n }\n const world = createWorld(settings.gravity);\n const counters = app.diagnostics.registerGroup(PHYSICS_2D_DIAGNOSTICS_GROUP, PHYSICS_2D_DIAGNOSTICS_COUNTERS);\n host.runtime = new Physics2DRuntime({ ctx, settings, world, counters });\n seedExistingBodies(app, host);\n },\n\n dispose(): void {\n host.runtime?.dispose();\n host.runtime = null;\n context = null;\n },\n };\n },\n);\n\n/**\n * Instantiates Rapier's WebAssembly module, which is what `Physics2DOptions.initialize` replaces.\n *\n * @returns A promise that settles once Rapier is usable.\n */\nasync function defaultInitialize(): Promise<void> {\n await loadRapier();\n}\n\n/** Every component this extension registers, in a stable order. */\nconst PHYSICS_2D_COMPONENTS: readonly ConcreteComponentType[] = Object.freeze([\n Rigidbody2D,\n BoxCollider2D,\n CircleCollider2D,\n CapsuleCollider2D,\n PolygonCollider2D,\n EdgeCollider2D,\n TilemapCollider2D,\n CharacterController2D,\n]);\n\n/** The collider classes the seed pass walks, in registration order. */\nconst COLLIDER_2D_TYPES: readonly ComponentType<Collider2D>[] = Object.freeze([\n BoxCollider2D,\n CircleCollider2D,\n CapsuleCollider2D,\n PolygonCollider2D,\n EdgeCollider2D,\n TilemapCollider2D,\n]);\n\n/**\n * Marks every collider and controller that already exists when the runtime is built.\n *\n * @remarks\n * A component attached before `onStart` — from a scene the `assets.preload` list pulled in, say —\n * called `markDirty` while the runtime was still `null`. This is the catch-up pass that makes those\n * bodies exist, and it walks the component store in registration order so body creation order stays\n * deterministic (`09-physics.md` §8).\n *\n * @param app - The app being started.\n * @param host - The holder the runtime landed in.\n */\nfunction seedExistingBodies(app: App, host: Physics2DHost): void {\n const runtime = host.runtime;\n if (runtime === null) {\n return;\n }\n for (const type of COLLIDER_2D_TYPES) {\n const components = app.world.components(type);\n for (let index = 0; index < components.length; index += 1) {\n const collider = components[index];\n if (collider !== undefined) {\n runtime.markDirty(collider.entity);\n }\n }\n }\n const controllers = app.world.components(CharacterController2D);\n for (let index = 0; index < controllers.length; index += 1) {\n const controller = controllers[index];\n if (controller !== undefined) {\n runtime.markDirty(controller.entity);\n }\n }\n}\n","import { describeSchema } from \"@ignifx/core\";\nimport { CharacterController2D } from \"./components/character-controller.js\";\nimport {\n BoxCollider2D,\n CapsuleCollider2D,\n CircleCollider2D,\n EdgeCollider2D,\n PolygonCollider2D,\n} from \"./components/colliders.js\";\nimport { Rigidbody2D } from \"./components/rigidbody.js\";\nimport { TilemapCollider2D } from \"./components/tilemap-collider.js\";\nimport type { SchemaDescription } from \"@ignifx/core\";\n\n/**\n * The documentation harness's view of everything `@ignifx/physics-2d` declares\n * (`docs/architecture/16-docs-harness-and-skill.md` §3, `scripts/README.md`).\n *\n * The `ignifx.physicsmaterial` **file format** is deliberately absent: `@ignifx/physics` already\n * documents it and this package reads the very same document (`material.ts`), so describing it\n * twice would render the same table on the formats page twice — the harness keeps the first\n * declaration and drops the rest (`scripts/lib/schema-source.ts`).\n */\n\n/**\n * Describes every component this package declares.\n *\n * @returns The records, keyed by namespaced type id.\n *\n * @example\n * ```ts\n * const schemas = describeSchemas();\n * schemas[\"ignifx/Rigidbody2D\"].fields[\"mass\"].default; // 1\n * ```\n *\n * @public\n */\nexport function describeSchemas(): Readonly<Record<string, SchemaDescription>> {\n return {\n \"ignifx/Rigidbody2D\": describeSchema(\"ignifx/Rigidbody2D\", Rigidbody2D.schema, {\n description: \"Makes an entity's 2D colliders a dynamic, kinematic, or static Rapier body.\",\n }),\n \"ignifx/BoxCollider2D\": describeSchema(\"ignifx/BoxCollider2D\", BoxCollider2D.schema, {\n description: \"An axis-aligned box, sized in local metres and scaled by the entity.\",\n }),\n \"ignifx/CircleCollider2D\": describeSchema(\"ignifx/CircleCollider2D\", CircleCollider2D.schema, {\n description: \"A circle; the larger scale axis wins.\",\n }),\n \"ignifx/CapsuleCollider2D\": describeSchema(\"ignifx/CapsuleCollider2D\", CapsuleCollider2D.schema, {\n description: \"A capsule standing along X or Y.\",\n }),\n \"ignifx/PolygonCollider2D\": describeSchema(\"ignifx/PolygonCollider2D\", PolygonCollider2D.schema, {\n description: \"The convex hull of a point list, in local metres.\",\n }),\n \"ignifx/EdgeCollider2D\": describeSchema(\"ignifx/EdgeCollider2D\", EdgeCollider2D.schema, {\n description: \"An open chain of line segments — a platformer's ground contour.\",\n }),\n \"ignifx/TilemapCollider2D\": describeSchema(\"ignifx/TilemapCollider2D\", TilemapCollider2D.schema, {\n description: \"The merged collision surface of a Tilemap, rebuilt when its tiles change.\",\n }),\n \"ignifx/CharacterController2D\": describeSchema(\"ignifx/CharacterController2D\", CharacterController2D.schema, {\n description: \"A kinematic character with collide-and-slide, slopes, autostep, and snap-to-ground.\",\n }),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/runtime/runtime-key.ts","../src/components/rigidbody.ts","../src/components/character-controller.ts","../src/errors.ts","../src/material.ts","../src/components/collider.ts","../src/components/colliders.ts","../src/components/tilemap-collider.ts","../src/lite/rapier/module.ts","../src/lite/rapier/world.ts","../src/runtime/host.ts","../src/lite/rapier/character.ts","../src/runtime/runtime.ts","../src/runtime/systems.ts","../src/service.ts","../src/settings.ts","../src/extension.ts","../src/schemas.ts"],"sourcesContent":["import { createServiceKey } from \"@ignifx/core\";\nimport type { Physics2DHost } from \"./host.js\";\nimport type { ServiceNameKey } from \"@ignifx/core\";\n\n/**\n * The key the extension registers its runtime holder under, so that `Rigidbody2D`, the 2D\n * colliders, and `CharacterController2D` can reach the runtime through `this.app.services` without\n * a module-level singleton (`CONSTITUTION.md` §3.6: no globals, two apps in one process).\n *\n * The **holder** is registered rather than the runtime itself, because everything an extension\n * contributes is declared in `register` (`docs/architecture/04-extensions.md` §1) and the runtime\n * does not exist until `onStart`.\n *\n * @internal\n */\nexport const Physics2DHostKey: ServiceNameKey<Physics2DHost> =\n createServiceKey<Physics2DHost>(\"@ignifx/physics-2d:host\");\n","import { bool, Component, createDefaults, defineSchema, enumOf, f32, Vec2 } from \"@ignifx/core\";\nimport { Physics2DHostKey } from \"../runtime/runtime-key.js\";\nimport type { RapierBody } from \"../lite/rapier/world.js\";\nimport type { Physics2DRuntime } from \"../runtime/runtime.js\";\nimport type { ComponentHooks, MutableVec2, Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * A nonzero mass rescales collider densities while preserving their inertia distribution.\n * Zero keeps the default density of 1 kg/m². Rotation has one freeze flag; damping is per body.\n * Automatic collision events follow callbacks as components join or leave the entity.\n */\n\n/**\n * How a 2D body moves.\n *\n * @public\n */\nexport const BODY_TYPES_2D = [\"dynamic\", \"kinematic\", \"static\"] as const;\n\n/**\n * The union of {@link BODY_TYPES_2D}.\n *\n * @public\n */\nexport type BodyType2D = (typeof BODY_TYPES_2D)[number];\n\n/**\n * Whether a body's display pose is interpolated between fixed steps.\n *\n * @public\n */\nexport const INTERPOLATION_MODES_2D = [\"none\", \"interpolate\"] as const;\n\n/**\n * The union of {@link INTERPOLATION_MODES_2D}.\n *\n * @public\n */\nexport type InterpolationMode2D = (typeof INTERPOLATION_MODES_2D)[number];\n\n/**\n * Whether collision callbacks are delivered for this body.\n *\n * @public\n */\nexport const COLLISION_EVENT_MODES_2D = [\"auto\", \"on\", \"off\"] as const;\n\n/**\n * The union of {@link COLLISION_EVENT_MODES_2D}.\n *\n * @public\n */\nexport type CollisionEventMode2D = (typeof COLLISION_EVENT_MODES_2D)[number];\n\n/**\n * The Rapier objects a `Rigidbody2D` owns. Unstable escape hatch\n * (`docs/architecture/00-overview.md` §3).\n *\n * @public\n */\nexport interface Rigidbody2DRapierHandles {\n /** The Rapier body, or `null` before the first fixed step has built it. */\n readonly body: RapierBody | null;\n}\n\n/**\n * Makes an entity's 2D colliders a Rapier body.\n *\n * @example\n * ```ts\n * const crate = world.createEntity(\"Crate\");\n * crate.transform.position2D = new Vec2(0, 5);\n * crate.addComponent(BoxCollider2D, { size: { x: 1, y: 1 } });\n * crate.addComponent(Rigidbody2D, { mass: 2 });\n * ```\n *\n * @public\n */\nexport class Rigidbody2D extends Component implements ComponentHooks {\n /** The namespaced registration id. */\n static typeId = \"ignifx/Rigidbody2D\";\n\n /** One body per entity. */\n static allowMultiple = false;\n\n /** The serialized field declarations (ADR-0004). */\n static schema: Schema = rigidbody2DSchema();\n\n declare bodyType: BodyType2D;\n\n declare mass: number;\n\n declare gravityScale: number;\n\n declare linearDamping: number;\n\n declare angularDamping: number;\n\n declare freezeRotation: boolean;\n\n declare interpolation: InterpolationMode2D;\n\n declare collisionEvents: CollisionEventMode2D;\n\n /** Applies the schema defaults, exactly as `Component.define` would. */\n constructor() {\n super();\n Object.assign(this, createDefaults(Rigidbody2D.schema));\n }\n\n /** Marks the entity's body for a rebuild at the start of the next fixed step. */\n onAttach(): void {\n this.rebuild();\n }\n\n /** Marks the entity's body for a rebuild, which turns it back into an implicit static body. */\n onDetach(): void {\n this.rebuild();\n }\n\n /**\n * Rebuilds the body at the start of the next fixed step. Call it after changing `bodyType`,\n * `mass`, `freezeRotation`, damping, or the entity's scale.\n */\n rebuild(): void {\n this.#runtime()?.markDirty(this.entity);\n }\n\n /**\n * The Rapier handles this component owns.\n *\n * @returns The body, or `null` before the first fixed step built it.\n */\n get rapier(): Rigidbody2DRapierHandles {\n return { body: this.#runtime()?.bodyHandleOf(this.entity) ?? null };\n }\n\n /**\n * The body's linear velocity in metres per second.\n *\n * @returns A freshly allocated vector; use {@link Rigidbody2D.linearVelocityToRef} in hot code.\n */\n get linearVelocity(): Vec2 {\n const out = new Vec2();\n this.linearVelocityToRef(out);\n return out;\n }\n\n /**\n * Replaces the body's linear velocity.\n *\n * @param value - Metres per second, world space.\n */\n set linearVelocity(value: Vec2Like) {\n this.#runtime()?.setLinearVelocity(this.entity, value);\n }\n\n /**\n * Reads the linear velocity without allocating.\n *\n * @param out - The vector to write.\n * @returns `out`.\n */\n linearVelocityToRef(out: MutableVec2): MutableVec2 {\n this.#runtime()?.readLinear(this.entity, out);\n return out;\n }\n\n /**\n * The body's angular velocity.\n *\n * @returns Degrees per second, counter-clockwise — the same unit as `Transform.rotation2D`.\n */\n get angularVelocity(): number {\n return this.#runtime()?.readAngular(this.entity) ?? 0;\n }\n\n /**\n * Replaces the body's angular velocity.\n *\n * @param value - Degrees per second, counter-clockwise.\n */\n set angularVelocity(value: number) {\n this.#runtime()?.setAngularVelocity(this.entity, value);\n }\n\n /**\n * The mass Rapier computed for the body, in kilograms.\n *\n * @returns The mass, or `0` before the body exists.\n */\n get computedMass(): number {\n return this.#runtime()?.massOf(this.entity) ?? 0;\n }\n\n /**\n * Applies a force for one fixed step. Call it from `fixedUpdate`.\n *\n * @param force - Newtons, world space.\n * @param point - Where to apply it; defaults to the centre of mass.\n *\n * @example\n * ```ts\n * fixedUpdate(): void {\n * this.body.addForce({ x: 0, y: 20 });\n * }\n * ```\n */\n addForce(force: Vec2Like, point?: Vec2Like): void {\n this.#runtime()?.addForce(this.entity, force, point ?? null);\n }\n\n /**\n * Applies an instantaneous impulse.\n *\n * @param impulse - Newton-seconds, world space.\n * @param point - Where to apply it; defaults to the centre of mass.\n */\n addImpulse(impulse: Vec2Like, point?: Vec2Like): void {\n this.#runtime()?.addImpulse(this.entity, impulse, point ?? null);\n }\n\n /**\n * Applies a torque for one fixed step.\n *\n * @param torque - Newton-metres, positive counter-clockwise.\n */\n addTorque(torque: number): void {\n this.#runtime()?.addTorque(this.entity, torque);\n }\n\n /**\n * Moves the body without integrating a velocity, and resets the interpolation history so the\n * display pose does not slide across the gap.\n *\n * @param position - The new world position, in metres.\n * @param rotation - The new rotation in degrees counter-clockwise; defaults to the current one.\n */\n teleport(position: Vec2Like, rotation?: number): void {\n this.#runtime()?.teleport(this.entity, position, rotation ?? this.transform.rotation2D);\n }\n\n /**\n * The runtime, or `null` when the extension is not registered on this app.\n *\n * @returns The runtime.\n */\n #runtime(): Physics2DRuntime | null {\n return this.app.services.tryGet(Physics2DHostKey)?.runtime ?? null;\n }\n}\n\n/**\n * Builds the `Rigidbody2D` schema.\n *\n * @returns The schema.\n */\nfunction rigidbody2DSchema(): Schema {\n return defineSchema({\n bodyType: enumOf(BODY_TYPES_2D, \"dynamic\"),\n mass: f32(1, { min: 0 }),\n gravityScale: f32(1),\n linearDamping: f32(0, { min: 0 }),\n angularDamping: f32(0.05, { min: 0 }),\n freezeRotation: bool(false),\n interpolation: enumOf(INTERPOLATION_MODES_2D, \"interpolate\"),\n collisionEvents: enumOf(COLLISION_EVENT_MODES_2D, \"auto\"),\n });\n}\n","import { bool, Component, createDefaults, defineSchema, enumOf, f32, Signal, Vec2, vec2 } from \"@ignifx/core\";\nimport { Physics2DHostKey } from \"../runtime/runtime-key.js\";\nimport { INTERPOLATION_MODES_2D } from \"./rigidbody.js\";\nimport type { CharacterCollision2D } from \"../events.js\";\nimport type { InterpolationMode2D } from \"./rigidbody.js\";\nimport type { Physics2DRuntime } from \"../runtime/runtime.js\";\nimport type { ComponentHooks, Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Sum `move` displacements during `fixedUpdate`; game scripts supply gravity.\n * After the step the controller owns world X/Y, while Z and rotation remain user-controlled.\n * Box shapes support taller autosteps than capsules; see ADR-0006's validation results.\n */\n\n/**\n * The collision shape a 2D character controller uses.\n *\n * @public\n */\nexport const CHARACTER_SHAPES_2D = [\"capsule\", \"box\"] as const;\n\n/**\n * The union of {@link CHARACTER_SHAPES_2D}.\n *\n * @public\n */\nexport type CharacterShape2D = (typeof CHARACTER_SHAPES_2D)[number];\n\n/**\n * A kinematic character that walks, slides, climbs slopes, and steps up.\n *\n * @example\n * ```ts\n * class Walk extends Script implements ScriptCallbacks {\n * static typeId = \"mygame/Walk\";\n * fixedUpdate(dt: number): void {\n * const controller = this.entity.requireComponent(CharacterController2D);\n * controller.move({ x: 4 * dt, y: -9.81 * dt });\n * }\n * }\n * ```\n *\n * @public\n */\nexport class CharacterController2D extends Component implements ComponentHooks {\n /** The namespaced registration id. */\n static typeId = \"ignifx/CharacterController2D\";\n\n /** One controller per entity. */\n static allowMultiple = false;\n\n /** The serialized field declarations (ADR-0004). */\n static schema: Schema = characterSchema();\n\n declare shape: CharacterShape2D;\n\n declare radius: number;\n\n declare height: number;\n\n declare offset: Vec2Like;\n\n declare slopeLimit: number;\n\n declare stepOffset: number;\n\n declare snapToGround: number;\n\n declare skinWidth: number;\n\n declare onOneWayPlatforms: boolean;\n\n declare pushBodies: boolean;\n\n declare interpolation: InterpolationMode2D;\n\n /** The displacement requested so far this fixed step. */\n readonly #pendingMove = new Vec2();\n\n /** The movement Rapier resolved for the last step, which is what `velocity` reports. */\n readonly #resolvedMove = new Vec2();\n\n /** The averaged normal of the supporting surface, written by the step system. */\n readonly #groundNormal = new Vec2(0, 1);\n\n /** Whether the last step ended on walkable ground. */\n #grounded = false;\n\n /** The fixed step the last move was resolved over, for `velocity`. */\n #lastDelta = 0;\n\n /** Emitted for every obstacle the character hit; created on first access. */\n #onCollided: Signal<CharacterCollision2D> | null = null;\n\n /** Applies the schema defaults. */\n constructor() {\n super();\n Object.assign(this, createDefaults(CharacterController2D.schema));\n }\n\n /** Creates the Rapier controller at the start of the next fixed step. */\n onAttach(): void {\n this.rebuild();\n }\n\n /** Releases the Rapier controller and its collider. */\n onDetach(): void {\n this.#runtime()?.releaseController(this);\n }\n\n /** Rebuilds the capsule or box at the start of the next fixed step. */\n rebuild(): void {\n this.#runtime()?.markControllerDirty(this);\n }\n\n /**\n * Requests a displacement for this fixed step. Displacements accumulate until the step runs.\n *\n * @param displacement - The world-space displacement to attempt, in metres.\n */\n move(displacement: Vec2Like): void {\n this.#pendingMove.set(this.#pendingMove.x + displacement.x, this.#pendingMove.y + displacement.y);\n }\n\n /**\n * How fast the character actually moved over the last fixed step, after sliding and blocking.\n *\n * @returns A freshly allocated vector in metres per second.\n */\n get velocity(): Vec2 {\n if (this.#lastDelta <= 0) {\n return new Vec2();\n }\n return new Vec2(this.#resolvedMove.x / this.#lastDelta, this.#resolvedMove.y / this.#lastDelta);\n }\n\n /**\n * Whether the character ended the last step on walkable ground.\n *\n * @returns Rapier's `computedGrounded` from the last move.\n */\n get isGrounded(): boolean {\n return this.#grounded;\n }\n\n /**\n * The most upward-facing normal of the obstacles the last move touched.\n *\n * @returns A live view; copy it if you keep it.\n */\n get groundNormal(): Vec2 {\n return this.#groundNormal;\n }\n\n /**\n * Emitted once per obstacle the character hit during a step.\n *\n * @remarks\n * Trigger colliders are not obstacles — the character walks straight through them — so a sensor\n * never appears here. Listen for `onTriggerEnter`/`onTriggerExit` on the entity's scripts instead.\n *\n * @returns The signal, created on first access.\n */\n get onCollided(): Signal<CharacterCollision2D> {\n this.#onCollided ??= new Signal<CharacterCollision2D>();\n return this.#onCollided;\n }\n\n /**\n * Teleports the character, clearing any pending motion and the interpolation history.\n *\n * @param position - The new world position of the entity, in metres.\n */\n teleport(position: Vec2Like): void {\n this.#pendingMove.set(0, 0);\n this.#runtime()?.teleportController(this, position);\n }\n\n /**\n * The displacement requested so far this step, consumed by the step system.\n *\n * @returns A live view of the accumulator.\n *\n * @internal\n */\n takePendingMove(): Vec2 {\n return this.#pendingMove;\n }\n\n /**\n * Records the outcome of a step.\n *\n * @param movement - What Rapier resolved.\n * @param grounded - Whether the character ended on walkable ground.\n * @param normal - The most upward-facing contact normal, or `null` when there was none.\n * @param deltaSeconds - The fixed step the move covered.\n *\n * @internal\n */\n applyStepResult(movement: Vec2Like, grounded: boolean, normal: Vec2Like | null, deltaSeconds: number): void {\n this.#resolvedMove.set(movement.x, movement.y);\n this.#grounded = grounded;\n this.#lastDelta = deltaSeconds;\n if (normal !== null) {\n this.#groundNormal.set(normal.x, normal.y);\n }\n this.#pendingMove.set(0, 0);\n }\n\n /**\n * Emits `onCollided` if anything is listening.\n *\n * @param collision - The obstacle to report.\n *\n * @internal\n */\n reportHit(collision: CharacterCollision2D): void {\n this.#onCollided?.emit(collision);\n }\n\n /**\n * The runtime, or `null` when the extension is not registered on this app.\n *\n * @returns The runtime.\n */\n #runtime(): Physics2DRuntime | null {\n return this.app.services.tryGet(Physics2DHostKey)?.runtime ?? null;\n }\n}\n\n/**\n * Builds the `CharacterController2D` schema.\n *\n * @returns The schema.\n */\nfunction characterSchema(): Schema {\n return defineSchema({\n shape: enumOf(CHARACTER_SHAPES_2D, \"capsule\"),\n radius: f32(0.25, { min: 0 }),\n height: f32(1, { min: 0 }),\n offset: vec2(),\n slopeLimit: f32(45, { min: 0, max: 90 }),\n stepOffset: f32(0, { min: 0 }),\n snapToGround: f32(0, { min: 0 }),\n skinWidth: f32(0.01, { min: 0 }),\n onOneWayPlatforms: bool(true),\n pushBodies: bool(false),\n interpolation: enumOf(INTERPOLATION_MODES_2D, \"interpolate\"),\n });\n}\n","import { IgnifxError } from \"@ignifx/core\";\nimport type { ErrorContext } from \"@ignifx/core\";\n\n/**\n * The diagnostic codes `@ignifx/physics-2d` owns. 2D physics shares the `11` subsystem range with\n * the 2D toolkit (`docs/architecture/15-devtools-and-diagnostics.md` §1,\n * `docs/architecture/11-2d-toolkit.md` §8), so the range is split: `@ignifx/2d` owns\n * `IGX-1101`–`IGX-1149` and this package owns `IGX-1150` upwards.\n *\n * `IGX-1101` is the one exception. `11-2d-toolkit.md` §8 names it by number — \"registering both\n * throws `IGX-1101`\" — and only this package can detect the condition, so this package registers\n * it. An app's error-code registry refuses a duplicate registration\n * (`packages/core/src/errors/error-code-registry.ts`), so `@ignifx/2d` must not declare it.\n */\n\n/**\n * Every diagnostic code `@ignifx/physics-2d` can throw or report.\n *\n * @example\n * ```ts\n * throw physics2DError(Physics2DErrorCode.queryBeforeStep, \"raycast() ran before the first step.\", {\n * context: { query: \"raycast\" },\n * });\n * ```\n *\n * @public\n */\nexport const Physics2DErrorCode = {\n /** Both `physics()` and `physics2d()` are registered on one world (`11-2d-toolkit.md` §8). */\n bothPhysicsExtensions: \"IGX-1101\",\n /** The Rapier WebAssembly module could not be instantiated. */\n rapierUnavailable: \"IGX-1150\",\n /** An entity with 2D colliders but no `Rigidbody2D` moved after its static body was placed. */\n movedStaticBody: \"IGX-1151\",\n /** A collider's layer index is outside the sixteen Rapier's interaction groups can express. */\n layerOutOfRange: \"IGX-1152\",\n /** A query ran before the first completed fixed step, so Rapier has no broadphase yet. */\n queryBeforeStep: \"IGX-1153\",\n /** A `.physicsmaterial.json` file is not an `ignifx.physicsmaterial` document this build reads. */\n invalidMaterialFile: \"IGX-1154\",\n /** The `physics2d.collisionMatrix` setting names a layer the project does not declare. */\n unknownLayer: \"IGX-1155\",\n /** A collider's geometry is degenerate: too few points, or a hull Rapier refused to build. */\n colliderGeometryInvalid: \"IGX-1156\",\n /** A 2D body was built for an entity that has a parent, whose pose is not world space. */\n bodyOnChildEntity: \"IGX-1157\",\n} as const;\n\n/**\n * The union of the codes the `Physics2DErrorCode` table declares.\n *\n * @public\n */\nexport type Physics2DErrorCode = (typeof Physics2DErrorCode)[keyof typeof Physics2DErrorCode];\n\n/**\n * The one-line message template of every code, as `ExtensionContext.registerErrorCodes` wants it.\n *\n * @public\n */\nexport const PHYSICS_2D_ERROR_MESSAGES: Readonly<Record<string, string>> = Object.freeze({\n \"IGX-1101\": \"A world uses either physics() or physics2d(), and {extension} found the other already there.\",\n \"IGX-1150\": \"The Rapier 2D WebAssembly module could not be instantiated.\",\n \"IGX-1151\": \"{entity} has 2D colliders but no Rigidbody2D, and it moved after its body was placed.\",\n \"IGX-1152\": \"{entity} is on layer {layer}, which is outside the sixteen layers Rapier 2D can filter by.\",\n \"IGX-1153\": \"{query} needs at least one completed fixed step before Rapier can answer it.\",\n \"IGX-1154\": \"{file} is not an ignifx.physicsmaterial document this build can read.\",\n \"IGX-1155\": \"physics2d.collisionMatrix names the layer {layer}, which the project does not declare.\",\n \"IGX-1156\": \"{entity} has a 2D collider whose geometry Rapier cannot build a shape from.\",\n \"IGX-1157\": \"{entity} carries a 2D physics body but is not a root entity; Rapier writes its world pose.\",\n});\n\n/**\n * Options accepted by {@link physics2DError}.\n *\n * @public\n */\nexport interface Physics2DErrorOptions {\n /** Identifiers that locate the failure. */\n readonly context?: ErrorContext;\n /** One sentence telling the developer what to do about it. */\n readonly hint?: string;\n /** The failure being wrapped, when there is one. */\n readonly cause?: unknown;\n}\n\n/**\n * Builds an `IgnifxError` carrying one of this package's codes.\n *\n * @param code - The code from the `Physics2DErrorCode` table.\n * @param message - The actionable development sentence.\n * @param options - Context identifiers, a remedy hint, and the wrapped cause.\n * @returns The error to throw or to reject with.\n *\n * @example\n * ```ts\n * throw physics2DError(Physics2DErrorCode.unknownLayer, \"physics2d.collisionMatrix names Enemy.\", {\n * context: { layer: \"Enemy\" },\n * });\n * ```\n *\n * @public\n */\nexport function physics2DError(\n code: Physics2DErrorCode,\n message: string,\n options?: Physics2DErrorOptions,\n): IgnifxError {\n return new IgnifxError(code, message, {\n ...(options?.context === undefined ? {} : { context: options.context }),\n ...(options?.hint === undefined ? {} : { hint: options.hint }),\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n });\n}\n","import { Physics2DErrorCode, physics2DError } from \"./errors.js\";\nimport type { Physics2DMaterialValues } from \"./settings.js\";\nimport type { AssetLoader, JsonObject, JsonValue, LoaderContext } from \"@ignifx/core\";\n\n/**\n * Read the shared `.physicsmaterial.json` format, ignoring `staticFriction` because Rapier has one\n * friction coefficient. Combine rules stay on colliders. Only one physics extension runs per world,\n * so the 2D and 3D loaders do not compete.\n */\n\n/**\n * The asset type name `.physicsmaterial.json` addresses resolve to.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_ASSET_TYPE = \"physicsmaterial\";\n\n/**\n * The address suffix that selects the loader.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_FILE_EXTENSION = \".physicsmaterial.json\";\n\n/**\n * The `format` string every physics-material document declares.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_FILE_FORMAT = \"ignifx.physicsmaterial\";\n\n/**\n * The file format version; `1` before ignifx 1.0.\n *\n * @public\n */\nexport const PHYSICS_MATERIAL_2D_FORMAT_VERSION = 1;\n\n/**\n * A loaded 2D surface.\n *\n * @example\n * ```ts\n * const ice = await app.assets.load<PhysicsMaterial2D>(\"materials/ice.physicsmaterial.json\");\n * floor.addComponent(BoxCollider2D, { size: { x: 10, y: 1 }, material: ice });\n * ```\n *\n * @public\n */\nexport class PhysicsMaterial2D implements Physics2DMaterialValues {\n /** The asset type token, so `asset(PhysicsMaterial2D)` fields resolve. */\n static assetType: string = PHYSICS_MATERIAL_2D_ASSET_TYPE;\n\n /** A human-readable name, used in diagnostics. */\n readonly name: string;\n\n /** The friction coefficient. */\n readonly friction: number;\n\n /** How much of the approach speed is returned, `0` to `1`. */\n readonly restitution: number;\n\n /**\n * Wraps parsed values.\n *\n * @param name - A human-readable name.\n * @param values - Friction and restitution.\n */\n constructor(name: string, values: Physics2DMaterialValues) {\n this.name = name;\n this.friction = values.friction;\n this.restitution = values.restitution;\n }\n\n /**\n * Builds a material in code, filling in the fields the caller omitted.\n *\n * @param name - A human-readable name.\n * @param values - Any subset of the two coefficients.\n * @returns The material.\n *\n * @example\n * ```ts\n * const bouncy = PhysicsMaterial2D.fromValues(\"bouncy\", { restitution: 0.9 });\n * ```\n */\n static fromValues(name: string, values: Partial<Physics2DMaterialValues>): PhysicsMaterial2D {\n return new PhysicsMaterial2D(name, {\n friction: values.friction ?? 0.6,\n restitution: values.restitution ?? 0,\n });\n }\n}\n\n/**\n * Builds the loader for `.physicsmaterial.json` files.\n *\n * @returns The loader, ready for `ctx.registerAssetLoader`.\n *\n * @public\n */\nexport function createPhysicsMaterial2DLoader(): AssetLoader<PhysicsMaterial2D> {\n return {\n type: PHYSICS_MATERIAL_2D_ASSET_TYPE,\n extensions: [PHYSICS_MATERIAL_2D_FILE_EXTENSION],\n load: async (ctx: LoaderContext): Promise<PhysicsMaterial2D> => {\n const document = await ctx.fetchJson<JsonValue>();\n return parsePhysicsMaterial2D(ctx.address, document);\n },\n };\n}\n\n/**\n * Parses one `ignifx.physicsmaterial` document into a 2D surface.\n *\n * @param address - The address it came from, for the diagnostic.\n * @param document - The parsed JSON.\n * @returns The material.\n * @throws IgnifxError with code `IGX-1154` when the document is not one this build can read.\n *\n * @public\n */\nexport function parsePhysicsMaterial2D(address: string, document: JsonValue): PhysicsMaterial2D {\n const object = asObject(document);\n if (object === null || object[\"format\"] !== PHYSICS_MATERIAL_2D_FILE_FORMAT) {\n throw physics2DError(\n Physics2DErrorCode.invalidMaterialFile,\n `${address} is not an ${PHYSICS_MATERIAL_2D_FILE_FORMAT}.`,\n {\n context: { file: address },\n hint: `Add \"format\": \"${PHYSICS_MATERIAL_2D_FILE_FORMAT}\" to the document.`,\n },\n );\n }\n const version = numberAt(object, \"formatVersion\", PHYSICS_MATERIAL_2D_FORMAT_VERSION);\n if (version > PHYSICS_MATERIAL_2D_FORMAT_VERSION) {\n throw physics2DError(Physics2DErrorCode.invalidMaterialFile, `${address} is version ${String(version)}.`, {\n context: { file: address, version },\n hint: \"Upgrade @ignifx/physics-2d, or re-save the material from this version of the editor.\",\n });\n }\n const name = typeof object[\"name\"] === \"string\" ? object[\"name\"] : address;\n return new PhysicsMaterial2D(name, {\n friction: numberAt(object, \"friction\", 0.6),\n restitution: numberAt(object, \"restitution\", 0),\n });\n}\n\n/**\n * Views a JSON value as an object.\n *\n * @param value - The parsed JSON.\n * @returns The object, or `null` when the value is not one.\n */\nfunction asObject(value: JsonValue): JsonObject | null {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return null;\n }\n // The guard above is exactly `JsonObject`.\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion\n return value as JsonObject;\n}\n\n/**\n * Reads a numeric property, tolerating its absence.\n *\n * @param object - The document.\n * @param key - The property name.\n * @param fallback - What to use when the property is missing or not a number.\n * @returns The value.\n */\nfunction numberAt(object: JsonObject, key: string, fallback: number): number {\n const value = object[key];\n return typeof value === \"number\" && Number.isFinite(value) ? value : fallback;\n}\n","import { asset, bool, Component, createDefaults, enumOf, f32, optional, record, str, vec2 } from \"@ignifx/core\";\nimport { PhysicsMaterial2D } from \"../material.js\";\nimport { Physics2DHostKey } from \"../runtime/runtime-key.js\";\nimport type { RapierShape2D } from \"../lite/rapier/world.js\";\nimport type { Physics2DMaterialValues } from \"../settings.js\";\nimport type { AssetHandle, ComponentHooks, Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Build shapes from local metres multiplied by entity scale; call `rebuild` after changing scale.\n * Material assets take precedence over inline values, then project defaults. Friction and restitution\n * combine rules belong to each collider. A tilemap collider can produce several backend shapes.\n */\n\n/**\n * How two surfaces' coefficients are combined when they touch, mirroring Rapier's\n * `CoefficientCombineRule`.\n *\n * @public\n */\nexport const COMBINE_RULES = [\"average\", \"min\", \"multiply\", \"max\"] as const;\n\n/**\n * The union of {@link COMBINE_RULES}.\n *\n * @public\n */\nexport type CombineRule = (typeof COMBINE_RULES)[number];\n\n/**\n * One Rapier shape a collider stands for, positioned in the body's local frame.\n *\n * @internal\n */\nexport interface CollectedShape2D {\n /** The geometry. */\n readonly shape: RapierShape2D;\n /** Where it sits relative to the entity origin, in metres. */\n readonly offset: Vec2Like;\n /** How it is turned about that point, in radians counter-clockwise. */\n readonly rotation: number;\n /** Whether it is solid only when a character controller comes down onto it. */\n readonly oneWay: boolean;\n}\n\n/**\n * The fields every 2D collider declares.\n *\n * @returns The shared field declarations, ready to spread into a collider's own schema.\n *\n * @public\n */\nexport function collider2DFields(): Schema {\n return {\n offset: vec2(),\n isTrigger: bool(false),\n material: asset(PhysicsMaterial2D),\n inlineMaterial: optional(\n record({\n friction: f32(0.6, { min: 0 }),\n restitution: f32(0, { min: 0, max: 1 }),\n }),\n ),\n frictionCombine: enumOf(COMBINE_RULES, \"average\"),\n restitutionCombine: enumOf(COMBINE_RULES, \"average\"),\n oneWay: bool(false),\n layerOverride: str(\"\"),\n };\n}\n\n/**\n * The base class of every 2D collider.\n *\n * @public\n */\nexport abstract class Collider2D extends Component implements ComponentHooks {\n /** Several colliders on one entity make one compound body. */\n static allowMultiple = true;\n\n /** The shape's offset from the entity origin, in local metres. */\n declare offset: Vec2Like;\n\n /** When `true` the shape reports overlaps and resolves no contacts. */\n declare isTrigger: boolean;\n\n /** A `.physicsmaterial.json` reference; wins over {@link Collider2D.inlineMaterial}. */\n declare material: AssetHandle<PhysicsMaterial2D> | null;\n\n /** An inline surface, used when {@link Collider2D.material} is `null`. */\n declare inlineMaterial: Physics2DMaterialValues | null;\n\n /** How this surface's friction combines with the one it touches. */\n declare frictionCombine: CombineRule;\n\n /** How this surface's restitution combines with the one it touches. */\n declare restitutionCombine: CombineRule;\n\n /**\n * Whether this is a one-way platform: a `CharacterController2D` with\n * `onOneWayPlatforms` passes up through it and lands on it coming down. Rigid bodies are\n * unaffected — one-way support is a character-controller feature in the MVP.\n */\n declare oneWay: boolean;\n\n /** The name of the layer this collider filters as, or `\"\"` to use `entity.layer`. */\n declare layerOverride: string;\n\n /** Applies the shared defaults. A concrete collider calls `super()` and then applies its own. */\n constructor() {\n super();\n Object.assign(this, createDefaults(collider2DFields()));\n }\n\n /** Marks the entity's body for a rebuild at the start of the next fixed step. */\n onAttach(): void {\n this.rebuild();\n }\n\n /** Marks the entity's body for a rebuild, which removes this collider from it. */\n onDetach(): void {\n this.rebuild();\n }\n\n /**\n * Rebuilds the entity's body and shapes at the start of the next fixed step. Call it after\n * changing a size, an offset, `isTrigger`, or the entity's scale.\n *\n * @example\n * ```ts\n * box.size = { x: 2, y: 2 };\n * box.rebuild();\n * ```\n */\n rebuild(): void {\n this.app.services.tryGet(Physics2DHostKey)?.runtime?.markDirty(this.entity);\n }\n\n /**\n * Resolves the surface this collider presents to Rapier.\n *\n * @param fallback - The world's `physics2d.defaultMaterial`.\n * @returns The asset's values, the inline values, or the fallback.\n */\n resolveMaterial(fallback: Physics2DMaterialValues): Physics2DMaterialValues {\n return this.material?.value ?? this.inlineMaterial ?? fallback;\n }\n\n /**\n * Appends the Rapier shapes this collider stands for.\n *\n * @param scale - The entity's lossy scale in the 2D plane.\n * @param out - The list to append to.\n *\n * @internal\n */\n abstract collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void;\n}\n","import { array, createDefaults, defineSchema, enumOf, f32, vec2 } from \"@ignifx/core\";\nimport { Physics2DErrorCode, physics2DError } from \"../errors.js\";\nimport { Collider2D, collider2DFields } from \"./collider.js\";\nimport type { CollectedShape2D } from \"./collider.js\";\nimport type { Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Polygon colliders use convex hulls; represent concave geometry with multiple colliders.\n * Edge colliders are thin, open polylines for static contours: fast bodies can tunnel through them,\n * and bodies starting inside are not pushed out.\n */\n\n/** A quarter turn in radians, which is how a horizontal capsule is expressed. */\nconst QUARTER_TURN = Math.PI / 2;\n\n/**\n * The axis a 2D capsule stands along.\n *\n * @public\n */\nexport const CAPSULE_DIRECTIONS_2D = [\"x\", \"y\"] as const;\n\n/**\n * The union of {@link CAPSULE_DIRECTIONS_2D}.\n *\n * @public\n */\nexport type CapsuleDirection2D = (typeof CAPSULE_DIRECTIONS_2D)[number];\n\n/**\n * An axis-aligned box collider, sized in local metres.\n *\n * @example\n * ```ts\n * const floor = world.createEntity(\"Floor\");\n * floor.addComponent(BoxCollider2D, { size: { x: 20, y: 1 } });\n * ```\n *\n * @public\n */\nexport class BoxCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/BoxCollider2D\";\n\n /** The serialized field declarations (ADR-0004). */\n static schema: Schema = boxSchema();\n\n declare size: Vec2Like;\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(BoxCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n out.push({\n shape: {\n kind: \"box\",\n halfWidth: (this.size.x * abs(scale.x)) / 2,\n halfHeight: (this.size.y * abs(scale.y)) / 2,\n },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * A circle collider. Non-uniform scale is not representable as a circle, so the larger scale axis\n * wins — the same rule Unity applies.\n *\n * @public\n */\nexport class CircleCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/CircleCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = circleSchema();\n\n declare radius: number;\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(CircleCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n out.push({\n shape: { kind: \"circle\", radius: this.radius * Math.max(abs(scale.x), abs(scale.y)) },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * A capsule collider: a rectangle with semicircular caps, standing along X or Y.\n *\n * @remarks\n * `height` is the **total** tip-to-tip height, so a capsule shorter than `2 * radius` degenerates to\n * a circle of that radius rather than inverting. Rapier's capsule always stands along Y, so an\n * `x` capsule is expressed by swapping the axes of the half-extents — which means an `x` capsule\n * and a rotated `y` capsule are the same shape.\n *\n * @public\n */\nexport class CapsuleCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/CapsuleCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = capsuleSchema();\n\n declare radius: number;\n\n declare height: number;\n\n declare direction: CapsuleDirection2D;\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(CapsuleCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n const alongY = this.direction === \"y\";\n const radialScale = alongY ? abs(scale.x) : abs(scale.y);\n const axisScale = alongY ? abs(scale.y) : abs(scale.x);\n const radius = this.radius * radialScale;\n const half = Math.max(0, (this.height * axisScale) / 2 - radius);\n out.push({\n shape: { kind: \"capsule\", halfHeight: half, radius },\n offset: scaled(this.offset, scale),\n // Rapier's 2D capsule always stands along Y (`geometry/shape.d.ts`, `Capsule`), so a\n // horizontal one is the same shape turned a quarter turn on the collider itself.\n rotation: alongY ? 0 : QUARTER_TURN,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * A convex polygon collider, wound in either direction, in local metres.\n *\n * @remarks\n * Rapier builds the **convex hull** of the points, so a concave outline is silently rounded out.\n * Model a concave shape as several `PolygonCollider2D`s on one entity, or as an\n * {@link EdgeCollider2D} when it is an open contour.\n *\n * @public\n */\nexport class PolygonCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/PolygonCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = pointsSchema();\n\n declare points: Vec2Like[];\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(PolygonCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n * @throws IgnifxError with code `IGX-1156` when fewer than three points are given.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n if (this.points.length < 3) {\n throw degenerate(this.entity.name, \"PolygonCollider2D\", \"at least three points\");\n }\n out.push({\n shape: { kind: \"polygon\", points: packPoints(this.points, scale) },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * An open chain of line segments — a platformer's ground contour.\n *\n * @public\n */\nexport class EdgeCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/EdgeCollider2D\";\n\n /** The serialized field declarations. */\n static schema: Schema = pointsSchema();\n\n declare points: Vec2Like[];\n\n /** Applies this collider's defaults on top of the shared ones. */\n constructor() {\n super();\n Object.assign(this, createDefaults(EdgeCollider2D.schema));\n }\n\n /**\n * Appends this collider's Rapier shape.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n * @throws IgnifxError with code `IGX-1156` when fewer than two points are given.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n if (this.points.length < 2) {\n throw degenerate(this.entity.name, \"EdgeCollider2D\", \"at least two points\");\n }\n out.push({\n shape: { kind: \"polyline\", points: packPoints(this.points, scale) },\n offset: scaled(this.offset, scale),\n rotation: 0,\n oneWay: this.oneWay,\n });\n }\n}\n\n/**\n * Builds the `BoxCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction boxSchema(): Schema {\n return defineSchema({ ...collider2DFields(), size: vec2({ x: 1, y: 1 }) });\n}\n\n/**\n * Builds the `CircleCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction circleSchema(): Schema {\n return defineSchema({ ...collider2DFields(), radius: f32(0.5, { min: 0 }) });\n}\n\n/**\n * Builds the `CapsuleCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction capsuleSchema(): Schema {\n return defineSchema({\n ...collider2DFields(),\n radius: f32(0.25, { min: 0 }),\n height: f32(1, { min: 0 }),\n direction: enumOf(CAPSULE_DIRECTIONS_2D, \"y\"),\n });\n}\n\n/**\n * Builds the schema the two point-list colliders share.\n *\n * @returns The schema.\n */\nfunction pointsSchema(): Schema {\n return defineSchema({ ...collider2DFields(), points: array(vec2()) });\n}\n\n/**\n * The absolute value of a scale component; a negative scale mirrors geometry, so the shape uses the\n * magnitude.\n *\n * @param value - The scale component.\n * @returns Its magnitude.\n */\nfunction abs(value: number): number {\n return Math.abs(value);\n}\n\n/**\n * Scales a local offset into shape space.\n *\n * @param offset - The authored offset.\n * @param scale - The lossy 2D scale.\n * @returns A fresh vector; shape construction is not a per-frame path.\n */\nfunction scaled(offset: Vec2Like, scale: Vec2Like): Vec2Like {\n return { x: offset.x * scale.x, y: offset.y * scale.y };\n}\n\n/**\n * Packs a point list into the interleaved `Float32Array` Rapier's shape constructors take.\n *\n * @param points - The authored points.\n * @param scale - The lossy 2D scale.\n * @returns A fresh buffer.\n */\nfunction packPoints(points: readonly Vec2Like[], scale: Vec2Like): Float32Array {\n const packed = new Float32Array(points.length * 2);\n for (let index = 0; index < points.length; index += 1) {\n const point = points[index];\n if (point === undefined) {\n continue;\n }\n packed[index * 2] = point.x * scale.x;\n packed[index * 2 + 1] = point.y * scale.y;\n }\n return packed;\n}\n\n/**\n * Builds the \"that geometry is degenerate\" failure.\n *\n * @param entity - The entity name.\n * @param component - Which collider complained.\n * @param requirement - What it needed.\n * @returns The error to throw.\n */\nfunction degenerate(entity: string, component: string, requirement: string): ReturnType<typeof physics2DError> {\n return physics2DError(Physics2DErrorCode.colliderGeometryInvalid, `${component} on ${entity} needs ${requirement}.`, {\n context: { entity, component },\n hint: `Give the collider ${requirement}.`,\n });\n}\n","import { createDefaults, defineSchema } from \"@ignifx/core\";\nimport { Collider2D, collider2DFields } from \"./collider.js\";\nimport type { CollectedShape2D } from \"./collider.js\";\nimport type { Tilemap, TilemapCollisionData } from \"@ignifx/2d\";\nimport type { Component, Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Read tile collision data through the optional 2D toolkit's type and rebuild when it changes.\n * The sibling `Tilemap` is found by its registered type id, because `@ignifx/2d` is an optional\n * peer and must not be imported as a value. Closed polylines preserve concave tile outlines that\n * convex hulls would fill in. They are thin surfaces, so a body starting inside the map is not\n * pushed out.\n */\n\n/** The type id `@ignifx/2d` registers `Tilemap` under. */\nconst TILEMAP_TYPE_ID = \"ignifx/Tilemap\";\n\n/**\n * The collision surface of a tilemap, as one static body's worth of Rapier shapes.\n *\n * @remarks\n * The collider reads the `Tilemap` on its own entity, so a scene file that holds both components\n * has tile collision as soon as it loads. It follows that tilemap: a tile edit, a new map, or a\n * tilemap added later all reach physics at the next fixed step. Assign\n * {@link TilemapCollider2D.collisionData} only to use other geometry, such as data you built in\n * code or a tilemap on another entity.\n *\n * @example\n * ```ts\n * const level = world.createEntity(\"Level\");\n * level.addComponent(Tilemap).map = app.assets.load<TilemapAsset>(\"level.tilemap.json\");\n * level.addComponent(TilemapCollider2D); // reads the Tilemap beside it\n * ```\n *\n * @public\n */\nexport class TilemapCollider2D extends Collider2D {\n /** The namespaced registration id. */\n static typeId = \"ignifx/TilemapCollider2D\";\n\n /** The serialized field declarations; the geometry itself comes from the tilemap asset. */\n static schema: Schema = tilemapSchema();\n\n /** Geometry that code assigned, which takes the place of the sibling tilemap's. */\n #override: TilemapCollisionData | null = null;\n\n /** The data the current shapes were built from. */\n #builtData: TilemapCollisionData | null = null;\n\n /** The `version` of that data when the shapes were built. */\n #builtVersion = -1;\n\n /** Applies the shared defaults. */\n constructor() {\n super();\n Object.assign(this, createDefaults(TilemapCollider2D.schema));\n }\n\n /**\n * The merged chunk geometry this collider builds shapes from: the assigned data, or else the\n * `Tilemap` on the same entity.\n *\n * @remarks\n * Reading it works in an edit-mode app too, where no body is built (ADR-0028), so an editor can\n * draw the outlines.\n *\n * @returns The data, or `null` when nothing was assigned and the entity has no `Tilemap`.\n */\n get collisionData(): TilemapCollisionData | null {\n return this.#override ?? siblingCollisionData(this);\n }\n\n /**\n * Uses other geometry instead of the sibling tilemap's, and schedules a rebuild.\n *\n * @param value - The merged chunk geometry, or `null` to go back to the `Tilemap` on the same\n * entity (no shapes when there is none).\n */\n set collisionData(value: TilemapCollisionData | null) {\n this.#override = value;\n this.#builtData = null;\n this.#builtVersion = -1;\n this.rebuild();\n }\n\n /**\n * Whether the supplied data has changed since the shapes were built. The runtime polls this once\n * per fixed step, which is how a tile edit reaches physics without a cross-package subscription.\n *\n * @remarks\n * A disabled collider, or one on an inactive entity, builds no shapes, so it answers `false`\n * rather than asking for the same empty rebuild every step. Enabling it again finds the data\n * changed and rebuilds.\n *\n * @returns `true` when a rebuild is due.\n *\n * @internal\n */\n needsRebuild(): boolean {\n if (!this.isEnabledInHierarchy) {\n return false;\n }\n const data = this.collisionData;\n return data !== this.#builtData || (data?.version ?? -1) !== this.#builtVersion;\n }\n\n /**\n * Appends one shape per merged outline and one per one-way edge.\n *\n * @param scale - The entity's lossy 2D scale.\n * @param out - The list to append to.\n *\n * @internal\n */\n collectShapes(scale: Vec2Like, out: CollectedShape2D[]): void {\n const data = this.collisionData;\n this.#builtData = data;\n this.#builtVersion = data?.version ?? -1;\n if (data === null) {\n return;\n }\n const offset = { x: this.offset.x * scale.x, y: this.offset.y * scale.y };\n for (const chunk of data.chunks) {\n for (const polygon of chunk.polygons) {\n if (polygon.length < 3) {\n continue;\n }\n out.push({\n shape: { kind: \"polyline\", points: closedLoop(polygon, scale) },\n offset,\n rotation: 0,\n oneWay: false,\n });\n }\n for (const edge of chunk.oneWayEdges) {\n out.push({\n shape: { kind: \"polyline\", points: segment(edge[0], edge[1], scale) },\n offset,\n rotation: 0,\n oneWay: true,\n });\n }\n }\n }\n}\n\n/**\n * Reads the collision data of the `Tilemap` on a collider's entity.\n *\n * @param collider - The collider.\n * @returns The tilemap's merged geometry, or `null` when the entity has no `Tilemap` or the 2D\n * toolkit is not registered.\n */\nfunction siblingCollisionData(collider: TilemapCollider2D): TilemapCollisionData | null {\n const type = collider.world.registry.get(TILEMAP_TYPE_ID);\n const tilemap = type === null ? null : collider.entity.getComponent(type);\n return tilemap !== null && isTilemap(tilemap) ? tilemap.collisionData : null;\n}\n\n/**\n * Whether a component registered as `ignifx/Tilemap` has the shape this package reads.\n *\n * @param component - The component.\n * @returns `true` when it carries `collisionData`.\n */\nfunction isTilemap(component: Component): component is Tilemap {\n return \"collisionData\" in component;\n}\n\n/**\n * Builds the `TilemapCollider2D` schema.\n *\n * @returns The schema.\n */\nfunction tilemapSchema(): Schema {\n return defineSchema({ ...collider2DFields() });\n}\n\n/**\n * Packs an outline as a closed chain of vertices.\n *\n * @param points - The outline, wound counter-clockwise.\n * @param scale - The entity's lossy 2D scale.\n * @returns A fresh buffer whose last vertex repeats the first.\n */\nfunction closedLoop(points: readonly Vec2Like[], scale: Vec2Like): Float32Array {\n const packed = new Float32Array((points.length + 1) * 2);\n for (let index = 0; index < points.length; index += 1) {\n const point = points[index];\n if (point === undefined) {\n continue;\n }\n packed[index * 2] = point.x * scale.x;\n packed[index * 2 + 1] = point.y * scale.y;\n }\n packed[points.length * 2] = packed[0] ?? 0;\n packed[points.length * 2 + 1] = packed[1] ?? 0;\n return packed;\n}\n\n/**\n * Packs one one-way edge as a two-vertex chain.\n *\n * @param from - The edge's start.\n * @param to - The edge's end.\n * @param scale - The entity's lossy 2D scale.\n * @returns A fresh buffer.\n */\nfunction segment(from: Vec2Like, to: Vec2Like, scale: Vec2Like): Float32Array {\n return new Float32Array([from.x * scale.x, from.y * scale.y, to.x * scale.x, to.y * scale.y]);\n}\n","import { init } from \"@dimforge/rapier2d-compat\";\n\n/**\n * Initialise Rapier's embedded WebAssembly before making backend calls.\n * The compat build includes the binary, so both Node and browser apps need no separate asset.\n * Rapier classes are imported statically by the adapters; delaying `init` does not split that bundle.\n *\n * @internal\n */\n\n/**\n * Instantiates Rapier's WebAssembly module.\n *\n * @remarks\n * Rapier's own `init()` is idempotent and caches the instantiated module in the package's module\n * scope, so the second and later apps in one process pay nothing.\n *\n * @returns A promise that settles once Rapier is usable.\n *\n * @internal\n */\nexport async function loadRapier(): Promise<void> {\n await init();\n}\n","import {\n ActiveCollisionTypes,\n ActiveEvents,\n Ball,\n Capsule,\n CoefficientCombineRule,\n ColliderDesc,\n ConvexPolygon,\n Cuboid,\n EventQueue,\n Polyline,\n QueryFilterFlags,\n Ray,\n RigidBodyDesc,\n World,\n} from \"@dimforge/rapier2d-compat\";\nimport type { Collider, RigidBody, Shape } from \"@dimforge/rapier2d-compat\";\nimport type { MutableVec2, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Keep Rapier calls inside the adapter (ADR-0006).\n * Queries need a built broadphase. Prime after collider-set changes only: a zero-length step can\n * still affect a running simulation. Interaction groups support sixteen layers; event-enabled\n * colliders include non-dynamic pairs so characters can enter static sensors.\n */\n\n/**\n * Rapier's physics world.\n *\n * @internal\n */\nexport type RapierWorld = World;\n\n/**\n * One Rapier rigid body.\n *\n * @internal\n */\nexport type RapierBody = RigidBody;\n\n/**\n * One Rapier collider.\n *\n * @internal\n */\nexport type RapierCollider = Collider;\n\n/**\n * Rapier's collision-event collector.\n *\n * @internal\n */\nexport type RapierEventQueue = EventQueue;\n\n/**\n * How a body moves, in the three flavours ignifx exposes.\n *\n * @internal\n */\nexport const BodyMotion2D = {\n /** Simulated by the solver. */\n dynamic: \"dynamic\",\n /** Moved by the game, pushes dynamic bodies, ignores forces. */\n kinematic: \"kinematic\",\n /** Never moves. */\n static: \"static\",\n} as const;\n\n/**\n * The union of {@link BodyMotion2D}.\n *\n * @internal\n */\nexport type BodyMotion2D = (typeof BodyMotion2D)[keyof typeof BodyMotion2D];\n\n/**\n * How two surfaces' friction or restitution coefficients are combined, mirroring Rapier's\n * `CoefficientCombineRule` (`dynamics/coefficient_combine_rule.d.ts`).\n *\n * @internal\n */\nexport const CombineRule2D = {\n /** The arithmetic mean; Rapier's default. */\n average: \"average\",\n /** The smaller of the two. */\n min: \"min\",\n /** The product. */\n multiply: \"multiply\",\n /** The larger of the two. */\n max: \"max\",\n} as const;\n\n/**\n * The union of {@link CombineRule2D}.\n *\n * @internal\n */\nexport type CombineRule2D = (typeof CombineRule2D)[keyof typeof CombineRule2D];\n\n/**\n * A collision shape, described in metres in the body's local frame.\n *\n * @internal\n */\nexport type RapierShape2D =\n | { readonly kind: \"box\"; readonly halfWidth: number; readonly halfHeight: number }\n | { readonly kind: \"circle\"; readonly radius: number }\n | { readonly kind: \"capsule\"; readonly halfHeight: number; readonly radius: number }\n | { readonly kind: \"polygon\"; readonly points: Float32Array }\n | { readonly kind: \"polyline\"; readonly points: Float32Array };\n\n/** What {@link createCollider} needs beyond the geometry. */\nexport interface ColliderOptions2D {\n /** The shape's offset from the body origin, in metres. */\n readonly offset: Vec2Like;\n /** The shape's rotation about the body origin, in radians. */\n readonly rotation: number;\n /** Whether the shape reports overlaps instead of resolving contacts. */\n readonly sensor: boolean;\n /** The packed membership/filter word from {@link interactionGroups}. */\n readonly groups: number;\n /** The dynamic friction coefficient. */\n readonly friction: number;\n /** How much approach speed is returned, `0` to `1`. */\n readonly restitution: number;\n /** How two frictions are combined. */\n readonly frictionCombine: CombineRule2D;\n /** How two restitutions are combined. */\n readonly restitutionCombine: CombineRule2D;\n /** Whether this collider raises collision and intersection events. */\n readonly events: boolean;\n}\n\n/** What {@link createBody} needs. */\nexport interface BodyOptions2D {\n /** How the body moves. */\n readonly motion: BodyMotion2D;\n /** The world position in metres. */\n readonly position: Vec2Like;\n /** The world rotation in radians, counter-clockwise about +Z. */\n readonly rotation: number;\n /** Multiplies world gravity for this body. */\n readonly gravityScale: number;\n /** Linear damping per second. */\n readonly linearDamping: number;\n /** Angular damping per second. */\n readonly angularDamping: number;\n /** Whether rotation is locked. */\n readonly freezeRotation: boolean;\n /** Extra mass added on top of the colliders' own, in kilograms; `0` adds none. */\n readonly additionalMass: number;\n /** Whether the body starts asleep. */\n readonly startAsleep: boolean;\n}\n\n/** One drained collision or intersection event. */\nexport interface CollisionEvent2D {\n /** The first collider's handle. */\n readonly first: number;\n /** The second collider's handle. */\n readonly second: number;\n /** `true` for an enter, `false` for an exit. */\n readonly started: boolean;\n}\n\n/** One contact between two colliders, written into a caller-owned record. */\nexport interface ContactData2D {\n /** Whether the pair has at least one contact this step. */\n touching: boolean;\n /** The world-space contact point. */\n readonly point: MutableVec2;\n /** The world-space contact normal, pointing away from the first collider. */\n readonly normal: MutableVec2;\n /** The magnitude of the impulse the solver applied. */\n impulse: number;\n}\n\n/** What {@link castRay} writes. */\nexport interface RayResult2D {\n /** Whether anything was hit. */\n hit: boolean;\n /** The collider that was hit, or `null`. */\n collider: RapierCollider | null;\n /** The distance from the origin along the (normalised) direction. */\n distance: number;\n}\n\n/** What {@link castShape} writes. */\nexport interface ShapeCastResult2D {\n /** Whether anything was hit. */\n hit: boolean;\n /** The collider that was hit, or `null`. */\n collider: RapierCollider | null;\n /** The distance travelled before contact. */\n distance: number;\n}\n\n/** Radians per full turn, for the degree conversions the components use. */\nconst TAU = Math.PI * 2;\n\n/** How wide one half of an `InteractionGroups` word is. */\nconst GROUP_BITS = 16;\n\n/** Every bit of one half. */\nconst GROUP_MASK = 0xff_ff;\n\n/**\n * The number of layers Rapier's 16-bit interaction groups can express\n * (`geometry/interaction_groups.d.ts`).\n *\n * @internal\n */\nexport const RAPIER_LAYER_LIMIT: number = GROUP_BITS;\n\n/**\n * Packs a membership mask and a collide mask into Rapier's single `InteractionGroups` word.\n *\n * @param membership - Which layers the collider belongs to, in the low 16 bits.\n * @param filter - Which layers it may collide with, in the low 16 bits.\n * @returns The packed word.\n *\n * @internal\n */\nexport function interactionGroups(membership: number, filter: number): number {\n return ((membership & GROUP_MASK) << GROUP_BITS) | (filter & GROUP_MASK);\n}\n\n/**\n * Creates the physics world.\n *\n * @param gravity - The acceleration in metres per second squared.\n * @returns The world.\n *\n * @internal\n */\nexport function createWorld(gravity: Vec2Like): RapierWorld {\n return new World({ x: gravity.x, y: gravity.y });\n}\n\n/**\n * Releases the world and every WebAssembly object it owns.\n *\n * @param world - The world.\n *\n * @internal\n */\nexport function destroyWorld(world: RapierWorld): void {\n world.free();\n}\n\n/**\n * Replaces world gravity.\n *\n * @param world - The world.\n * @param gravity - The new acceleration.\n *\n * @internal\n */\nexport function setGravity(world: RapierWorld, gravity: Vec2Like): void {\n world.gravity.x = gravity.x;\n world.gravity.y = gravity.y;\n}\n\n/**\n * Sets the length of the next step.\n *\n * @param world - The world.\n * @param seconds - The fixed step in seconds.\n *\n * @internal\n */\nexport function setTimestep(world: RapierWorld, seconds: number): void {\n world.timestep = seconds;\n}\n\n/**\n * Sets how many iterations the constraint solver runs.\n *\n * @param world - The world.\n * @param iterations - The iteration count; Rapier's default is 4.\n *\n * @internal\n */\nexport function setSolverIterations(world: RapierWorld, iterations: number): void {\n world.numSolverIterations = iterations;\n}\n\n/**\n * Advances the simulation by one timestep, collecting events.\n *\n * @param world - The world.\n * @param queue - The event collector.\n *\n * @internal\n */\nexport function stepWorld(world: RapierWorld, queue: RapierEventQueue): void {\n world.step(queue);\n}\n\n/**\n * Rebuilds the broadphase without advancing time, by running a zero-length step.\n *\n * @remarks\n * Rapier only refreshes its acceleration structure inside `World.step`, so a collider created since\n * the last step is invisible to queries and to the character controller (S6.2). The queue is passed\n * so an enter event raised by the priming step is still delivered.\n *\n * @param world - The world.\n * @param queue - The event collector.\n *\n * @internal\n */\nexport function primeWorld(world: RapierWorld, queue: RapierEventQueue): void {\n const timestep = world.timestep;\n world.timestep = 0;\n world.step(queue);\n world.timestep = timestep;\n}\n\n/**\n * Creates the event collector.\n *\n * @remarks\n * `autoDrain: false` — the collector must survive both the priming step and the real step of one\n * fixed update, and the runtime drains it once at the end of that update.\n *\n * @returns The queue.\n *\n * @internal\n */\nexport function createEventQueue(): RapierEventQueue {\n return new EventQueue(false);\n}\n\n/**\n * Releases the event collector.\n *\n * @param queue - The queue.\n *\n * @internal\n */\nexport function destroyEventQueue(queue: RapierEventQueue): void {\n queue.free();\n}\n\n/**\n * Hands every buffered collision and intersection event to a sink and clears the buffer.\n *\n * @param queue - The queue.\n * @param sink - Called once per event with the two collider handles and the enter/exit flag.\n *\n * @internal\n */\nexport function drainCollisions(queue: RapierEventQueue, sink: (event: CollisionEvent2D) => void): void {\n queue.drainCollisionEvents((first: number, second: number, started: boolean): void => {\n sink({ first, second, started });\n });\n}\n\n/**\n * Creates a rigid body.\n *\n * @param world - The world.\n * @param options - Motion, pose, damping, gravity scale, and mass.\n * @returns The body.\n *\n * @internal\n */\nexport function createBody(world: RapierWorld, options: BodyOptions2D): RapierBody {\n const description = descriptionFor(options.motion)\n .setTranslation(options.position.x, options.position.y)\n .setRotation(options.rotation)\n .setGravityScale(options.gravityScale)\n .setLinearDamping(options.linearDamping)\n .setAngularDamping(options.angularDamping)\n .setSleeping(options.startAsleep);\n if (options.freezeRotation) {\n description.lockRotations();\n }\n if (options.additionalMass > 0) {\n description.setAdditionalMass(options.additionalMass);\n }\n return world.createRigidBody(description);\n}\n\n/**\n * Removes a body and every collider attached to it.\n *\n * @param world - The world.\n * @param body - The body.\n *\n * @internal\n */\nexport function destroyBody(world: RapierWorld, body: RapierBody): void {\n world.removeRigidBody(body);\n}\n\n/**\n * Reads a body's world pose without allocating.\n *\n * @param body - The body.\n * @param out - The vector the translation is written into.\n * @returns The rotation in radians.\n *\n * @internal\n */\nexport function readBodyPose(body: RapierBody, out: MutableVec2): number {\n body.translation(out);\n return body.rotation();\n}\n\n/**\n * Moves a body immediately, as a teleport.\n *\n * @param body - The body.\n * @param position - The new world position.\n * @param rotation - The new rotation in radians.\n *\n * @internal\n */\nexport function setBodyPose(body: RapierBody, position: Vec2Like, rotation: number): void {\n body.setTranslation({ x: position.x, y: position.y }, true);\n body.setRotation(rotation, true);\n}\n\n/**\n * Asks Rapier to interpolate a kinematic body to a pose over the next step, which is what makes it\n * push dynamic bodies instead of tunnelling through them.\n *\n * @param body - The body.\n * @param position - The pose to reach.\n * @param rotation - The rotation to reach, in radians.\n *\n * @internal\n */\nexport function setNextKinematicPose(body: RapierBody, position: Vec2Like, rotation: number): void {\n body.setNextKinematicTranslation({ x: position.x, y: position.y });\n body.setNextKinematicRotation(rotation);\n}\n\n/**\n * Reads a body's linear velocity without allocating.\n *\n * @param body - The body.\n * @param out - The vector to write.\n *\n * @internal\n */\nexport function readLinearVelocity(body: RapierBody, out: MutableVec2): void {\n body.linvel(out);\n}\n\n/**\n * Replaces a body's linear velocity.\n *\n * @param body - The body.\n * @param velocity - Metres per second.\n *\n * @internal\n */\nexport function writeLinearVelocity(body: RapierBody, velocity: Vec2Like): void {\n body.setLinvel({ x: velocity.x, y: velocity.y }, true);\n}\n\n/**\n * Reads a body's angular velocity.\n *\n * @param body - The body.\n * @returns Radians per second, counter-clockwise.\n *\n * @internal\n */\nexport function readAngularVelocity(body: RapierBody): number {\n return body.angvel();\n}\n\n/**\n * Replaces a body's angular velocity.\n *\n * @param body - The body.\n * @param velocity - Radians per second.\n *\n * @internal\n */\nexport function writeAngularVelocity(body: RapierBody, velocity: number): void {\n body.setAngvel(velocity, true);\n}\n\n/**\n * Adds a force that lasts for one step.\n *\n * @param body - The body.\n * @param force - Newtons.\n * @param point - Where to apply it, or `null` for the centre of mass.\n *\n * @internal\n */\nexport function addForce(body: RapierBody, force: Vec2Like, point: Vec2Like | null): void {\n if (point === null) {\n body.addForce({ x: force.x, y: force.y }, true);\n return;\n }\n body.addForceAtPoint({ x: force.x, y: force.y }, { x: point.x, y: point.y }, true);\n}\n\n/**\n * Applies an instantaneous impulse.\n *\n * @param body - The body.\n * @param impulse - Newton-seconds.\n * @param point - Where to apply it, or `null` for the centre of mass.\n *\n * @internal\n */\nexport function applyImpulse(body: RapierBody, impulse: Vec2Like, point: Vec2Like | null): void {\n if (point === null) {\n body.applyImpulse({ x: impulse.x, y: impulse.y }, true);\n return;\n }\n body.applyImpulseAtPoint({ x: impulse.x, y: impulse.y }, { x: point.x, y: point.y }, true);\n}\n\n/**\n * Adds a torque that lasts for one step.\n *\n * @param body - The body.\n * @param torque - Newton-metres, positive counter-clockwise.\n *\n * @internal\n */\nexport function addTorque(body: RapierBody, torque: number): void {\n body.addTorque(torque, true);\n}\n\n/**\n * Whether a body is asleep, which is what `activeBodies` counts the complement of.\n *\n * @param body - The body.\n * @returns `true` when Rapier has put it to sleep.\n *\n * @internal\n */\nexport function isBodySleeping(body: RapierBody): boolean {\n return body.isSleeping();\n}\n\n/**\n * The mass Rapier computed for a body from its colliders and any additional mass.\n *\n * @param body - The body.\n * @returns Kilograms.\n *\n * @internal\n */\nexport function bodyMass(body: RapierBody): number {\n return body.mass();\n}\n\n/**\n * Sets how many kilograms a square metre of a collider weighs. Rapier derives the body's mass and\n * angular inertia from this and the collider's area.\n *\n * @param collider - The collider.\n * @param density - Kilograms per square metre.\n *\n * @internal\n */\nexport function setColliderDensity(collider: RapierCollider, density: number): void {\n collider.setDensity(density);\n}\n\n/**\n * Recomputes a body's mass and inertia from its colliders, after their densities changed.\n *\n * @param body - The body.\n *\n * @internal\n */\nexport function recomputeMass(body: RapierBody): void {\n body.recomputeMassPropertiesFromColliders();\n}\n\n/**\n * Creates a collider and attaches it to a body.\n *\n * @param world - The world.\n * @param body - The body it belongs to.\n * @param shape - The geometry, in body-local metres.\n * @param options - Offset, sensor flag, filter word, surface, and event flags.\n * @returns The collider, or `null` when Rapier refuses the geometry — a convex hull of collinear or\n * duplicated points, say, which throws inside its WebAssembly rather than returning nothing.\n *\n * @internal\n */\nexport function createCollider(\n world: RapierWorld,\n body: RapierBody,\n shape: RapierShape2D,\n options: ColliderOptions2D,\n): RapierCollider | null {\n const description = describeShape(shape);\n if (description === null) {\n return null;\n }\n description\n .setTranslation(options.offset.x, options.offset.y)\n .setRotation(options.rotation)\n .setSensor(options.sensor)\n .setCollisionGroups(options.groups)\n .setFriction(options.friction)\n .setRestitution(options.restitution)\n .setFrictionCombineRule(combineRule(options.frictionCombine))\n .setRestitutionCombineRule(combineRule(options.restitutionCombine));\n if (options.events) {\n description\n .setActiveEvents(ActiveEvents.COLLISION_EVENTS)\n // `DEFAULT` covers dynamic-versus-anything only, so a kinematic character would never report\n // entering a static sensor (measured, S6.2).\n .setActiveCollisionTypes(ActiveCollisionTypes.ALL);\n }\n try {\n return world.createCollider(description, body);\n } catch {\n // A degenerate hull — collinear or duplicate points — throws inside Rapier's WebAssembly rather\n // than returning `null` from `ColliderDesc.convexHull`, so the refusal is caught here and\n // reported as `IGX-1156` by the caller.\n return null;\n }\n}\n\n/**\n * Turns a collider's collision and intersection events on or off without rebuilding it.\n *\n * @param collider - The collider.\n * @param enabled - Whether it reports events.\n *\n * @internal\n */\nexport function setColliderEvents(collider: RapierCollider, enabled: boolean): void {\n collider.setActiveEvents(enabled ? ActiveEvents.COLLISION_EVENTS : ActiveEvents.NONE);\n collider.setActiveCollisionTypes(enabled ? ActiveCollisionTypes.ALL : ActiveCollisionTypes.DEFAULT);\n}\n\n/**\n * Reads a collider's world position without allocating.\n *\n * @param collider - The collider.\n * @param out - The vector to write.\n *\n * @internal\n */\nexport function readColliderTranslation(collider: RapierCollider, out: MutableVec2): void {\n collider.translation(out);\n}\n\n/**\n * Replaces a collider's filter word after a layer change.\n *\n * @param collider - The collider.\n * @param groups - The packed membership/filter word.\n *\n * @internal\n */\nexport function setColliderGroups(collider: RapierCollider, groups: number): void {\n collider.setCollisionGroups(groups);\n}\n\n/**\n * Removes one collider.\n *\n * @param world - The world.\n * @param collider - The collider.\n *\n * @internal\n */\nexport function destroyCollider(world: RapierWorld, collider: RapierCollider): void {\n world.removeCollider(collider, true);\n}\n\n/**\n * The stable integer Rapier identifies a collider by, which is what its event queue reports.\n *\n * @param collider - The collider.\n * @returns The handle.\n *\n * @internal\n */\nexport function colliderHandle(collider: RapierCollider): number {\n return collider.handle;\n}\n\n/**\n * Whether a collider is a sensor, which is how a trigger event is told from a contact.\n *\n * @param collider - The collider.\n * @returns `true` for a sensor.\n *\n * @internal\n */\nexport function isColliderSensor(collider: RapierCollider): boolean {\n return collider.isSensor();\n}\n\n/**\n * Reads the first contact of a collider pair.\n *\n * @param world - The world.\n * @param first - One collider.\n * @param second - The other.\n * @param out - The record to write.\n *\n * @internal\n */\nexport function readContact(\n world: RapierWorld,\n first: RapierCollider,\n second: RapierCollider,\n out: ContactData2D,\n): void {\n out.touching = false;\n out.impulse = 0;\n world.contactPair(first, second, (manifold, flipped: boolean): void => {\n const normal = manifold.normal();\n const sign = flipped ? -1 : 1;\n out.normal.set(normal.x * sign, normal.y * sign);\n const point = manifold.numSolverContacts() > 0 ? manifold.solverContactPoint(0) : null;\n if (point !== null) {\n out.point.set(point.x, point.y);\n }\n out.impulse = manifold.numContacts() > 0 ? manifold.contactImpulse(0) : 0;\n out.touching = true;\n });\n}\n\n/**\n * Casts a ray and writes the nearest hit.\n *\n * @param world - The world.\n * @param origin - The world-space origin.\n * @param direction - A unit direction.\n * @param maxDistance - How far to travel.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param outPoint - Receives the contact point.\n * @param outNormal - Receives the contact normal.\n * @param out - Receives the hit flag, collider, and distance.\n *\n * @internal\n */\nexport function castRay(\n world: RapierWorld,\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number,\n groups: number,\n hitSensors: boolean,\n outPoint: MutableVec2,\n outNormal: MutableVec2,\n out: RayResult2D,\n): void {\n out.hit = false;\n out.collider = null;\n out.distance = 0;\n const ray = new Ray({ x: origin.x, y: origin.y }, { x: direction.x, y: direction.y });\n const hit = world.castRayAndGetNormal(ray, maxDistance, true, sensorFlags(hitSensors), groups);\n if (hit === null) {\n return;\n }\n out.hit = true;\n out.collider = hit.collider;\n out.distance = hit.timeOfImpact;\n outPoint.set(origin.x + direction.x * hit.timeOfImpact, origin.y + direction.y * hit.timeOfImpact);\n outNormal.set(hit.normal.x, hit.normal.y);\n}\n\n/**\n * Casts a ray and hands every hit to a sink, nearest first is **not** guaranteed — the caller sorts.\n *\n * @param world - The world.\n * @param origin - The world-space origin.\n * @param direction - A unit direction.\n * @param maxDistance - How far to travel.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param sink - Called once per hit with the collider, distance, and normal.\n *\n * @internal\n */\nexport function castRayAll(\n world: RapierWorld,\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number,\n groups: number,\n hitSensors: boolean,\n sink: (collider: RapierCollider, distance: number, normalX: number, normalY: number) => void,\n): void {\n const ray = new Ray({ x: origin.x, y: origin.y }, { x: direction.x, y: direction.y });\n world.intersectionsWithRay(\n ray,\n maxDistance,\n true,\n (intersection): boolean => {\n sink(intersection.collider, intersection.timeOfImpact, intersection.normal.x, intersection.normal.y);\n return true;\n },\n sensorFlags(hitSensors),\n groups,\n );\n}\n\n/**\n * Lists every collider a positioned shape overlaps.\n *\n * @param world - The world.\n * @param shape - The query geometry.\n * @param position - Its world position.\n * @param rotation - Its world rotation in radians.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param sink - Called once per overlapping collider.\n *\n * @internal\n */\nexport function overlapShape(\n world: RapierWorld,\n shape: RapierShape2D,\n position: Vec2Like,\n rotation: number,\n groups: number,\n hitSensors: boolean,\n sink: (collider: RapierCollider) => void,\n): void {\n const built = buildShape(shape);\n if (built === null) {\n return;\n }\n world.intersectionsWithShape(\n { x: position.x, y: position.y },\n rotation,\n built,\n (collider): boolean => {\n sink(collider);\n return true;\n },\n sensorFlags(hitSensors),\n groups,\n );\n}\n\n/**\n * Sweeps a shape and writes the first contact.\n *\n * @param world - The world.\n * @param shape - The query geometry.\n * @param position - Where the sweep starts.\n * @param rotation - The shape's rotation in radians.\n * @param direction - A unit sweep direction.\n * @param maxDistance - How far to sweep.\n * @param groups - The packed filter word.\n * @param hitSensors - Whether sensors count.\n * @param outPoint - Receives the contact point.\n * @param outNormal - Receives the contact normal.\n * @param out - Receives the hit flag, collider, and distance.\n *\n * @internal\n */\nexport function castShape(\n world: RapierWorld,\n shape: RapierShape2D,\n position: Vec2Like,\n rotation: number,\n direction: Vec2Like,\n maxDistance: number,\n groups: number,\n hitSensors: boolean,\n outPoint: MutableVec2,\n outNormal: MutableVec2,\n out: ShapeCastResult2D,\n): void {\n out.hit = false;\n out.collider = null;\n out.distance = 0;\n const built = buildShape(shape);\n if (built === null) {\n return;\n }\n const hit = world.castShape(\n { x: position.x, y: position.y },\n rotation,\n { x: direction.x, y: direction.y },\n built,\n 0,\n maxDistance,\n true,\n sensorFlags(hitSensors),\n groups,\n );\n if (hit === null) {\n return;\n }\n out.hit = true;\n out.collider = hit.collider;\n out.distance = hit.time_of_impact;\n // `witness1` and `normal1` are expressed in the **swept shape's** local frame (`geometry/toi.d.ts`),\n // so they are rotated by the query rotation and offset by where the shape stood at impact.\n const cos = Math.cos(rotation);\n const sin = Math.sin(rotation);\n const originX = position.x + direction.x * hit.time_of_impact;\n const originY = position.y + direction.y * hit.time_of_impact;\n outPoint.set(\n originX + hit.witness1.x * cos - hit.witness1.y * sin,\n originY + hit.witness1.x * sin + hit.witness1.y * cos,\n );\n outNormal.set(hit.normal1.x * cos - hit.normal1.y * sin, hit.normal1.x * sin + hit.normal1.y * cos);\n}\n\n/**\n * Turns degrees counter-clockwise into the radians Rapier stores.\n *\n * @param degrees - The angle in degrees.\n * @returns The angle in radians.\n *\n * @internal\n */\nexport function toRadians(degrees: number): number {\n return (degrees * TAU) / 360;\n}\n\n/**\n * Turns Rapier's radians into the degrees `Transform.rotation2D` uses.\n *\n * @param radians - The angle in radians.\n * @returns The angle in degrees.\n *\n * @internal\n */\nexport function toDegrees(radians: number): number {\n return (radians * 360) / TAU;\n}\n\n/**\n * Builds the Rapier `Shape` a description stands for, for the query paths.\n *\n * @param shape - The description.\n * @returns The shape, or `null` when the geometry is degenerate.\n */\nfunction buildShape(shape: RapierShape2D): Shape | null {\n if (shape.kind === \"box\") {\n return new Cuboid(shape.halfWidth, shape.halfHeight);\n }\n if (shape.kind === \"circle\") {\n return new Ball(shape.radius);\n }\n if (shape.kind === \"capsule\") {\n return new Capsule(shape.halfHeight, shape.radius);\n }\n if (shape.kind === \"polyline\") {\n return new Polyline(shape.points);\n }\n return new ConvexPolygon(shape.points, false);\n}\n\n/**\n * Builds the collider description a shape stands for.\n *\n * @param shape - The description.\n * @returns The description, or `null` when Rapier refuses the geometry.\n */\nfunction describeShape(shape: RapierShape2D): ColliderDesc | null {\n if (shape.kind === \"box\") {\n return ColliderDesc.cuboid(shape.halfWidth, shape.halfHeight);\n }\n if (shape.kind === \"circle\") {\n return ColliderDesc.ball(shape.radius);\n }\n if (shape.kind === \"capsule\") {\n return ColliderDesc.capsule(shape.halfHeight, shape.radius);\n }\n if (shape.kind === \"polyline\") {\n return ColliderDesc.polyline(shape.points);\n }\n return ColliderDesc.convexHull(shape.points);\n}\n\n/**\n * The body description a motion type starts from.\n *\n * @param motion - The motion type.\n * @returns A fresh description.\n */\nfunction descriptionFor(motion: BodyMotion2D): RigidBodyDesc {\n if (motion === BodyMotion2D.dynamic) {\n return RigidBodyDesc.dynamic();\n }\n // Position-based rather than velocity-based: ignifx moves kinematic bodies by writing a\n // transform, and `setNextKinematicTranslation` is what turns that into a swept motion.\n return motion === BodyMotion2D.kinematic ? RigidBodyDesc.kinematicPositionBased() : RigidBodyDesc.fixed();\n}\n\n/**\n * Rapier's combine rule for one of ours.\n *\n * @param rule - The ignifx name.\n * @returns Rapier's value.\n */\nfunction combineRule(rule: CombineRule2D): CoefficientCombineRule {\n if (rule === CombineRule2D.min) {\n return CoefficientCombineRule.Min;\n }\n if (rule === CombineRule2D.multiply) {\n return CoefficientCombineRule.Multiply;\n }\n return rule === CombineRule2D.max ? CoefficientCombineRule.Max : CoefficientCombineRule.Average;\n}\n\n/**\n * The query filter flags that express \"sensors count\" or \"sensors do not\".\n *\n * @param hitSensors - Whether sensors count.\n * @returns The flags.\n */\nfunction sensorFlags(hitSensors: boolean): QueryFilterFlags | undefined {\n return hitSensors ? undefined : QueryFilterFlags.EXCLUDE_SENSORS;\n}\n","import type { Physics2DRuntime } from \"./runtime.js\";\n\n/**\n * The one mutable cell between `register` and `onStart`.\n *\n * `Extension.register` runs **before** `createApp` builds the world\n * (`packages/core/src/app/app.ts`, `initialize`), and the Rapier world needs the world's layer\n * table and Rapier's WebAssembly module, which is loaded asynchronously. So the extension declares\n * its systems and its service in `register` — which is where `docs/architecture/04-extensions.md`\n * §1 says everything is declared — and they all read the runtime through this holder, which\n * `onStart` fills in.\n *\n * @internal\n */\nexport class Physics2DHost {\n /** The runtime, or `null` until `onStart` has built it. */\n runtime: Physics2DRuntime | null = null;\n}\n","import { QueryFilterFlags } from \"@dimforge/rapier2d-compat\";\nimport type { RapierCollider, RapierWorld } from \"./world.js\";\nimport type { KinematicCharacterController } from \"@dimforge/rapier2d-compat\";\nimport type { MutableVec2, Vec2Like } from \"@ignifx/core\";\n\n/**\n * Rapier's kinematic character controller (`control/character_controller.d.ts`), wrapped so the\n * runtime never imports the backend.\n *\n * ## Facts measured against `@dimforge/rapier2d-compat@0.20.0` (spike S6.2)\n *\n * - **Slopes work as documented.** `setMaxSlopeClimbAngle(radians)` blocks a slope steeper than the\n * limit and lets a shallower one through, and `setMinSlopeSlideAngle` decides when the character\n * slides back down.\n * - **Snap-to-ground works.** Descending a 25° ramp at 4 m/s, `enableSnapToGround(0.5)` kept the\n * character grounded on every one of 200 steps; without it, 66 of them were airborne.\n * - **One-way platforms are the `filterPredicate` of `computeColliderMovement`.** Returning `false`\n * for a platform makes the character pass straight through it; the predicate is called once per\n * candidate collider per step.\n * - **Autostep needs a box.** With a box character, `enableAutostep(0.35, …)` clears a 0.3 m step\n * and refuses a 0.5 m one. With a capsule of radius 0.2 the same call clears 0.15 m but not\n * 0.2 m — a Rapier limitation, recorded in ADR-0006's validation section and in the skill.\n * - **The broadphase must exist first.** Before the world has stepped, `computeColliderMovement`\n * sees no obstacles at all and reports the requested motion unchanged.\n * - **A sensor is an obstacle unless `filterFlags` says otherwise** (measured 2026-09-08). With\n * `filterFlags` left `undefined`, a kinematic box driven by the controller into a static sensor\n * ball stops dead at the sensor's surface and no intersection event is ever raised, because the\n * character never overlaps it. Passing {@link QueryFilterFlags.EXCLUDE_SENSORS} lets the\n * character walk through and `drainCollisionEvents` then reports the started/stopped pair. Every\n * move in {@link computeControllerMovement} therefore passes it: a trigger volume is a region of\n * space, never a wall.\n *\n * @internal\n */\nexport type RapierController = KinematicCharacterController;\n\n/**\n * The obstacle filter every controller move uses. Sensors describe regions, not geometry, so they\n * are excluded from collide-and-slide; the pair is still reported by the world step, which is where\n * `onTriggerEnter`/`onTriggerExit` come from.\n */\nconst OBSTACLE_FLAGS: QueryFilterFlags = QueryFilterFlags.EXCLUDE_SENSORS;\n\n/** How a controller is configured, in ignifx units. */\nexport interface ControllerTuning2D {\n /** The steepest slope the character can walk up, in radians from \"up\". */\n readonly slopeLimit: number;\n /** The shallowest slope the character slides down, in radians from \"up\". */\n readonly slideLimit: number;\n /** The tallest step the character climbs automatically, in metres; `0` disables autostep. */\n readonly stepOffset: number;\n /** How much clear floor must follow a step, in metres. */\n readonly stepMinWidth: number;\n /** How far below the feet the ground is snapped to, in metres; `0` disables snapping. */\n readonly snapToGround: number;\n /** Whether the character pushes dynamic bodies out of the way. */\n readonly pushBodies: boolean;\n}\n\n/** One obstacle the character hit during a move. */\nexport interface ControllerHit2D {\n /** The collider that was hit, or `null` when Rapier no longer tracks it. */\n collider: RapierCollider | null;\n /** The world-space contact point. */\n readonly point: MutableVec2;\n /** The world-space outward normal on the obstacle. */\n readonly normal: MutableVec2;\n}\n\n/**\n * Creates a character controller.\n *\n * @param world - The world.\n * @param skinWidth - The gap Rapier keeps between the character and its surroundings, in metres.\n * @returns The controller.\n *\n * @internal\n */\nexport function createController(world: RapierWorld, skinWidth: number): RapierController {\n const controller = world.createCharacterController(skinWidth);\n // ignifx 2D is +Y up (ADR-0011); Rapier's default up in 2D is the same, but saying so keeps the\n // controller correct if a project ever flips the axis.\n controller.setUp({ x: 0, y: 1 });\n return controller;\n}\n\n/**\n * Releases a controller.\n *\n * @param world - The world.\n * @param controller - The controller.\n *\n * @internal\n */\nexport function destroyController(world: RapierWorld, controller: RapierController): void {\n world.removeCharacterController(controller);\n}\n\n/**\n * Applies the component's settings to a controller.\n *\n * @param controller - The controller.\n * @param tuning - The slope, step, snap, and pushing settings.\n *\n * @internal\n */\nexport function tuneController(controller: RapierController, tuning: ControllerTuning2D): void {\n controller.setMaxSlopeClimbAngle(tuning.slopeLimit);\n controller.setMinSlopeSlideAngle(tuning.slideLimit);\n controller.setApplyImpulsesToDynamicBodies(tuning.pushBodies);\n if (tuning.stepOffset > 0) {\n controller.enableAutostep(tuning.stepOffset, tuning.stepMinWidth, true);\n } else {\n controller.disableAutostep();\n }\n if (tuning.snapToGround > 0) {\n controller.enableSnapToGround(tuning.snapToGround);\n } else {\n controller.disableSnapToGround();\n }\n}\n\n/**\n * Sets the gap kept between the character and its surroundings.\n *\n * @param controller - The controller.\n * @param skinWidth - The gap in metres.\n *\n * @internal\n */\nexport function setControllerSkinWidth(controller: RapierController, skinWidth: number): void {\n controller.setOffset(skinWidth);\n}\n\n/**\n * Runs one collide-and-slide move.\n *\n * @remarks\n * Sensors are excluded from the obstacle set ({@link OBSTACLE_FLAGS}), so a character walks into and\n * out of a trigger volume at full speed and the world step raises the intersection events.\n *\n * @param controller - The controller.\n * @param collider - The character's own collider.\n * @param desired - The displacement to attempt, in metres.\n * @param groups - The packed filter word obstacles must pass.\n * @param filter - An extra per-collider predicate — how one-way platforms are done — or `null`.\n * @param out - Receives the movement Rapier resolved.\n * @returns `true` when the character ended the move on walkable ground.\n *\n * @internal\n */\nexport function computeControllerMovement(\n controller: RapierController,\n collider: RapierCollider,\n desired: Vec2Like,\n groups: number,\n filter: ((collider: RapierCollider) => boolean) | null,\n out: MutableVec2,\n): boolean {\n if (filter === null) {\n controller.computeColliderMovement(collider, { x: desired.x, y: desired.y }, OBSTACLE_FLAGS, groups);\n } else {\n controller.computeColliderMovement(collider, { x: desired.x, y: desired.y }, OBSTACLE_FLAGS, groups, filter);\n }\n controller.computedMovement(out);\n return controller.computedGrounded();\n}\n\n/**\n * How many obstacles the last move hit.\n *\n * @param controller - The controller.\n * @returns The count.\n *\n * @internal\n */\nexport function controllerHitCount(controller: RapierController): number {\n return controller.numComputedCollisions();\n}\n\n/**\n * Reads one obstacle of the last move.\n *\n * @param controller - The controller.\n * @param index - Which obstacle.\n * @param out - The record to write.\n * @returns `true` when the obstacle exists.\n *\n * @internal\n */\nexport function readControllerHit(controller: RapierController, index: number, out: ControllerHit2D): boolean {\n const hit = controller.computedCollision(index);\n if (hit === null) {\n return false;\n }\n out.collider = hit.collider;\n out.point.set(hit.witness1.x, hit.witness1.y);\n out.normal.set(hit.normal1.x, hit.normal1.y);\n return true;\n}\n","import { lerp, Vec2, Vec3 } from \"@ignifx/core\";\nimport { CharacterController2D } from \"../components/character-controller.js\";\nimport { Collider2D } from \"../components/collider.js\";\nimport { Rigidbody2D } from \"../components/rigidbody.js\";\nimport { TilemapCollider2D } from \"../components/tilemap-collider.js\";\nimport { Physics2DErrorCode, physics2DError } from \"../errors.js\";\nimport {\n computeControllerMovement,\n controllerHitCount,\n createController,\n destroyController,\n readControllerHit,\n tuneController,\n} from \"../lite/rapier/character.js\";\nimport {\n addForce,\n addTorque,\n applyImpulse,\n BodyMotion2D,\n bodyMass,\n colliderHandle,\n createBody,\n createCollider,\n createEventQueue,\n destroyBody,\n destroyEventQueue,\n destroyWorld,\n drainCollisions,\n interactionGroups,\n isColliderSensor,\n primeWorld,\n RAPIER_LAYER_LIMIT,\n readAngularVelocity,\n readBodyPose,\n readColliderTranslation,\n readContact,\n readLinearVelocity,\n recomputeMass,\n setBodyPose,\n setColliderDensity,\n setColliderEvents,\n setGravity,\n setNextKinematicPose,\n setSolverIterations,\n setTimestep,\n stepWorld,\n toDegrees,\n toRadians,\n writeAngularVelocity,\n writeLinearVelocity,\n} from \"../lite/rapier/world.js\";\nimport type { BodyRecord2D } from \"./body-record.js\";\nimport type { CollectedShape2D } from \"../components/collider.js\";\nimport type { ContactPoint2D } from \"../events.js\";\nimport type { ControllerHit2D, RapierController } from \"../lite/rapier/character.js\";\nimport type { ContactData2D, RapierCollider, RapierEventQueue, RapierWorld } from \"../lite/rapier/world.js\";\nimport type { Physics2DMaterialValues, Physics2DSettings } from \"../settings.js\";\nimport type {\n Component,\n DiagnosticsGroup,\n Entity,\n ExtensionContext,\n MutableVec2,\n PhysicsCallbackName,\n Vec2Like,\n} from \"@ignifx/core\";\n\n/**\n * The per-app 2D physics runtime: the Rapier world, the bodies, the character controllers, the\n * event drain, and the three things the systems ask it to do each frame — restore, step,\n * interpolate (`docs/architecture/11-2d-toolkit.md` §8, `09-physics.md` §1).\n *\n * Everything here is `@internal`; the public surface is `Physics2DService`, `Rigidbody2D`, the 2D\n * colliders, and `CharacterController2D`.\n */\n\n/** What the extension hands the runtime. */\nexport interface Physics2DRuntimeOptions {\n /** The extension registration surface; the physics kernel hooks live on it. */\n readonly ctx: ExtensionContext;\n /** The resolved `physics2d` settings section. */\n readonly settings: Physics2DSettings;\n /** The Rapier world. */\n readonly world: RapierWorld;\n /** The `physics2d` diagnostics group. */\n readonly counters: DiagnosticsGroup;\n}\n\n/**\n * The counters `09-physics.md` §9 and `11-2d-toolkit.md` §8 name for 2D.\n *\n * @public\n */\nexport const PHYSICS_2D_DIAGNOSTICS_COUNTERS: readonly string[] = Object.freeze([\n \"bodies\",\n \"colliders\",\n \"stepsThisFrame\",\n \"collisionEvents\",\n \"triggerEvents\",\n \"stepMs\",\n]);\n\n/**\n * The diagnostics group name.\n *\n * @public\n */\nexport const PHYSICS_2D_DIAGNOSTICS_GROUP = \"physics2d\";\n\n/** One pair of colliders Rapier currently reports as touching. */\ninterface PairState {\n /** The first collider's handle. */\n readonly first: number;\n /** The second collider's handle. */\n readonly second: number;\n /** Whether either collider is a sensor, which makes the pair a trigger rather than a contact. */\n readonly sensor: boolean;\n /** Whether the pair started this step, so it gets `Enter` rather than `Stay`. */\n entered: boolean;\n}\n\n/** The pooled `TriggerEvent2D`, whose fields the dispatcher rewrites for each delivery. */\ninterface MutableTrigger {\n /** The other participant. */\n other: Entity | null;\n /** The other participant's collider. */\n otherCollider: Collider2D | null;\n /** The entity being called. */\n self: Entity;\n /** This entity's collider. */\n selfCollider: Collider2D | null;\n}\n\n/** The pooled `Collision2D`. */\ninterface MutableCollision {\n /** The other participant. */\n other: Entity | null;\n /** The other participant's collider. */\n otherCollider: Collider2D | null;\n /** The entity being called. */\n self: Entity;\n /** This entity's collider. */\n selfCollider: Collider2D | null;\n /** The one-element pooled contact list. */\n readonly contacts: readonly ContactPoint2D[];\n /** The relative velocity of the two bodies. */\n readonly relativeVelocity: Vec2Like;\n}\n\n/** The pooled `CharacterCollision2D`. */\ninterface MutableCharacterCollision {\n /** The entity that was hit. */\n other: Entity | null;\n /** The collider that was hit. */\n otherCollider: Collider2D | null;\n /** The contact point. */\n readonly point: Vec2Like;\n /** The contact normal. */\n readonly normal: Vec2Like;\n}\n\n/** One collider that is solid only from above, with its world top cached for the move filter. */\ninterface OneWayPlatform {\n /** The Rapier collider. */\n readonly collider: RapierCollider;\n /** How far its top sits above the collider's own origin, in metres. */\n readonly halfHeight: number;\n /** Its world-space top, refreshed once per fixed step. */\n top: number;\n}\n\n/** Which ignifx collider a Rapier collider belongs to. */\ninterface ColliderBinding {\n /** The record that owns it. */\n readonly record: BodyRecord2D;\n /** The authored collider, or `null` for a character controller's own shape. */\n readonly owner: Collider2D | null;\n /** The Rapier collider itself, which the event and contact paths need. */\n readonly collider: RapierCollider;\n}\n\n/** How far an implicit static body may move before the diagnostic considers it moved. */\nconst STATIC_MOVE_EPSILON = 1e-6;\n\n/** How much clear floor Rapier requires past an autostep, in metres. */\nconst AUTOSTEP_MIN_WIDTH = 0.05;\n\n/** Every layer bit Rapier can express. */\nconst ALL_LAYER_BITS = 0xff_ff;\n\n/**\n * The per-app 2D physics runtime.\n *\n * @internal\n */\nexport class Physics2DRuntime {\n readonly #ctx: ExtensionContext;\n\n readonly #settings: Physics2DSettings;\n\n readonly #world: RapierWorld;\n\n readonly #counters: DiagnosticsGroup;\n\n readonly #queue: RapierEventQueue;\n\n readonly #records: BodyRecord2D[] = [];\n\n readonly #byEntity = new Map<Entity, BodyRecord2D>();\n\n readonly #byCollider = new Map<number, ColliderBinding>();\n\n readonly #controllers = new Map<number, RapierController>();\n\n readonly #oneWay = new Map<number, OneWayPlatform>();\n\n readonly #dirty = new Set<Entity>();\n\n readonly #collideMasks = new Int32Array(RAPIER_LAYER_LIMIT).fill(ALL_LAYER_BITS);\n\n readonly #pairs = new Map<string, PairState>();\n\n readonly #endedPairs: PairState[] = [];\n\n readonly #startedPairs: PairState[] = [];\n\n readonly #contact: ContactData2D = { touching: false, point: new Vec2(), normal: new Vec2(), impulse: 0 };\n\n readonly #contactPoint: { point: Vec2; normal: Vec2; impulse: number } = {\n point: new Vec2(),\n normal: new Vec2(),\n impulse: 0,\n };\n\n readonly #contacts: ContactPoint2D[] = [];\n\n readonly #controllerHit: ControllerHit2D = { collider: null, point: new Vec2(), normal: new Vec2() };\n\n #trigger: MutableTrigger | null = null;\n\n #collision: MutableCollision | null = null;\n\n #characterCollision: MutableCharacterCollision | null = null;\n\n readonly #scratchPose = new Vec3();\n\n readonly #scratchVector = new Vec2();\n\n readonly #relativeVelocity = new Vec2();\n\n readonly #movement = new Vec2();\n\n readonly #groundNormal = new Vec2();\n\n readonly #filterScratch = new Vec2();\n\n /** The vertical component of the displacement the filtered move is attempting. */\n #filterDesiredY = 0;\n\n /** The bottom of the filtered character, in metres. */\n #filterFeet = 0;\n\n #colliderSetChanged = true;\n\n #stepped = false;\n\n #disposed = false;\n\n #stepsThisFrame = 0;\n\n #stepMs = 0;\n\n #collisionEventCount = 0;\n\n #triggerEventCount = 0;\n\n #colliderCount = 0;\n\n /**\n * Builds the runtime and applies the world settings.\n *\n * @param options - The context, settings, world, and counters.\n * @throws IgnifxError with code `IGX-1155` when the collision matrix names an unknown layer.\n */\n constructor(options: Physics2DRuntimeOptions) {\n this.#ctx = options.ctx;\n this.#settings = options.settings;\n this.#world = options.world;\n this.#counters = options.counters;\n this.#queue = createEventQueue();\n this.#contacts.push(this.#contactPoint);\n setGravity(this.#world, this.#settings.gravity);\n if (this.#settings.velocityIterations > 0) {\n setSolverIterations(this.#world, this.#settings.velocityIterations);\n }\n this.#buildCollideMasks();\n }\n\n /**\n * The Rapier world, for the query and escape-hatch paths.\n *\n * @returns The world handle.\n */\n get world(): RapierWorld {\n return this.#world;\n }\n\n /**\n * Whether at least one fixed step has run, which is what Rapier's broadphase needs before a query\n * can answer (`09-physics.md` §5, spike S6.2).\n *\n * @returns `true` once a step has completed.\n */\n get hasStepped(): boolean {\n return this.#stepped;\n }\n\n /**\n * Every body, in creation order.\n *\n * @returns The records.\n */\n get records(): readonly BodyRecord2D[] {\n return this.#records;\n }\n\n /**\n * The resolved settings.\n *\n * @returns The `physics2d` section.\n */\n get settings(): Physics2DSettings {\n return this.#settings;\n }\n\n /**\n * Replaces world gravity.\n *\n * @param gravity - The new acceleration vector.\n */\n setGravity(gravity: Vec2Like): void {\n setGravity(this.#world, gravity);\n }\n\n /**\n * Marks an entity's body for a rebuild at the start of the next fixed step.\n *\n * @param entity - The entity whose colliders, `Rigidbody2D`, or controller changed.\n */\n markDirty(entity: Entity): void {\n if (!this.#disposed && !entity.isDestroyed) {\n this.#dirty.add(entity);\n }\n }\n\n /**\n * Marks a character controller's entity for a rebuild.\n *\n * @param component - The controller component.\n */\n markControllerDirty(component: CharacterController2D): void {\n this.markDirty(component.entity);\n }\n\n /**\n * Drops a character controller's body, which is what detaching the component does.\n *\n * @param component - The controller component.\n */\n releaseController(component: CharacterController2D): void {\n this.markDirty(component.entity);\n }\n\n /**\n * The Rapier body of an entity, for the escape hatch.\n *\n * @param entity - The entity.\n * @returns The body handle, or `null`.\n */\n bodyHandleOf(entity: Entity): BodyRecord2D[\"body\"] | null {\n return this.#byEntity.get(entity)?.body ?? null;\n }\n\n /**\n * The record an entity owns.\n *\n * @param entity - The entity.\n * @returns The record, or `null`.\n */\n recordOf(entity: Entity): BodyRecord2D | null {\n return this.#byEntity.get(entity) ?? null;\n }\n\n /**\n * The entity and collider a Rapier collider belongs to, which is how a query result or an event\n * becomes an ignifx object.\n *\n * @param collider - The Rapier collider, or `null`.\n * @returns The binding, or `null`.\n */\n bindingOf(collider: RapierCollider | null): ColliderBinding | null {\n return collider === null ? null : (this.#byCollider.get(colliderHandle(collider)) ?? null);\n }\n\n /**\n * The mass Rapier computed for an entity's body.\n *\n * @param entity - The entity.\n * @returns Kilograms, or `0` when there is no body.\n */\n massOf(entity: Entity): number {\n const record = this.#byEntity.get(entity);\n return record === undefined ? 0 : bodyMass(record.body);\n }\n\n /**\n * Reads a body's linear velocity.\n *\n * @param entity - The entity.\n * @param out - The vector to write; left untouched when there is no body.\n */\n readLinear(entity: Entity, out: MutableVec2): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n readLinearVelocity(record.body, out);\n }\n }\n\n /**\n * Reads a body's angular velocity.\n *\n * @param entity - The entity.\n * @returns Degrees per second, counter-clockwise.\n */\n readAngular(entity: Entity): number {\n const record = this.#byEntity.get(entity);\n return record === undefined ? 0 : toDegrees(readAngularVelocity(record.body));\n }\n\n /**\n * Sets a body's linear velocity.\n *\n * @param entity - The entity.\n * @param velocity - Metres per second.\n */\n setLinearVelocity(entity: Entity, velocity: Vec2Like): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n writeLinearVelocity(record.body, velocity);\n }\n }\n\n /**\n * Sets a body's angular velocity.\n *\n * @param entity - The entity.\n * @param velocity - Degrees per second.\n */\n setAngularVelocity(entity: Entity, velocity: number): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n writeAngularVelocity(record.body, toRadians(velocity));\n }\n }\n\n /**\n * Applies a force for one fixed step.\n *\n * @param entity - The entity.\n * @param force - Newtons.\n * @param point - The application point, or `null` for the centre of mass.\n */\n addForce(entity: Entity, force: Vec2Like, point: Vec2Like | null): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n addForce(record.body, force, point);\n }\n }\n\n /**\n * Applies an impulse.\n *\n * @param entity - The entity.\n * @param impulse - Newton-seconds.\n * @param point - The application point, or `null` for the centre of mass.\n */\n addImpulse(entity: Entity, impulse: Vec2Like, point: Vec2Like | null): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n applyImpulse(record.body, impulse, point);\n }\n }\n\n /**\n * Applies a torque for one fixed step.\n *\n * @param entity - The entity.\n * @param torque - Newton-metres, positive counter-clockwise.\n */\n addTorque(entity: Entity, torque: number): void {\n const record = this.#byEntity.get(entity);\n if (record !== undefined) {\n addTorque(record.body, torque);\n }\n }\n\n /**\n * Teleports a body and resets its interpolation history.\n *\n * @param entity - The entity.\n * @param position - The new world position.\n * @param rotationDegrees - The new rotation in degrees counter-clockwise.\n */\n teleport(entity: Entity, position: Vec2Like, rotationDegrees: number): void {\n const record = this.#byEntity.get(entity);\n this.#writeTransform(entity, position, rotationDegrees);\n if (record === undefined) {\n return;\n }\n const radians = toRadians(rotationDegrees);\n setBodyPose(record.body, position, radians);\n record.currentPosition.set(position.x, position.y);\n record.previousPosition.set(position.x, position.y);\n record.currentRotation = radians;\n record.previousRotation = radians;\n this.#colliderSetChanged = true;\n }\n\n /**\n * Teleports a character controller.\n *\n * @param component - The controller component.\n * @param position - The new world position.\n */\n teleportController(component: CharacterController2D, position: Vec2Like): void {\n this.teleport(component.entity, position, component.transform.rotation2D);\n }\n\n /**\n * Restores the authoritative pose of every interpolated body, undoing the display pose the\n * `Update` system wrote at the top of the previous frame's `Update` (`09-physics.md` §1).\n */\n restorePoses(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record !== undefined && record.interpolate) {\n this.#writeTransform(record.entity, record.currentPosition, toDegrees(record.currentRotation));\n }\n }\n }\n\n /**\n * Runs one fixed step: reconcile, push kinematic poses, move controllers, step Rapier, snapshot,\n * dispatch events.\n *\n * @param fixedDeltaSeconds - The fixed step in seconds.\n */\n step(fixedDeltaSeconds: number): void {\n this.#syncLayers();\n this.#pollTilemaps();\n this.#reconcile();\n this.#pushKinematicPoses();\n setTimestep(this.#world, fixedDeltaSeconds);\n const started = now();\n if (this.#colliderSetChanged || !this.#stepped) {\n // Rapier only refreshes its broadphase inside `step`, so a collider created since the last\n // one is invisible to the character controller and to queries (S6.2). A zero-length step\n // rebuilds it; it is skipped whenever the collider set is unchanged, because it perturbs a\n // running simulation.\n primeWorld(this.#world, this.#queue);\n this.#colliderSetChanged = false;\n }\n this.#refreshOneWayPlatforms();\n this.#moveControllers(fixedDeltaSeconds);\n this.#capturePreStepVelocities();\n stepWorld(this.#world, this.#queue);\n this.#stepMs += now() - started;\n this.#stepsThisFrame += 1;\n this.#stepped = true;\n this.#snapshot();\n this.#dispatchEvents();\n }\n\n /**\n * Writes the interpolated display pose of every interpolated body.\n *\n * @param alpha - `time.fixedStepAlpha`, in `[0, 1)`.\n */\n interpolate(alpha: number): void {\n this.#publishCounters();\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record === undefined || !record.interpolate) {\n continue;\n }\n this.#scratchVector.set(\n lerp(record.previousPosition.x, record.currentPosition.x, alpha),\n lerp(record.previousPosition.y, record.currentPosition.y, alpha),\n );\n const rotation = record.previousRotation + shortestArc(record.previousRotation, record.currentRotation) * alpha;\n this.#writeTransform(record.entity, this.#scratchVector, toDegrees(rotation));\n }\n }\n\n /** Tears every body down and releases the Rapier world. */\n dispose(): void {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n for (let index = this.#records.length - 1; index >= 0; index -= 1) {\n const record = this.#records[index];\n if (record !== undefined) {\n this.#teardown(record);\n }\n }\n this.#records.length = 0;\n this.#byEntity.clear();\n this.#byCollider.clear();\n this.#controllers.clear();\n this.#pairs.clear();\n destroyEventQueue(this.#queue);\n destroyWorld(this.#world);\n }\n\n /**\n * Publishes the frame's counters and zeroes the accumulators. The top of `Update`, where the\n * display pose is written, is the first point in a frame at which every fixed step of that frame\n * is done.\n */\n #publishCounters(): void {\n this.#counters.set(this.#counters.index(\"bodies\"), this.#records.length);\n this.#counters.set(this.#counters.index(\"colliders\"), this.#colliderCount);\n this.#counters.set(this.#counters.index(\"stepsThisFrame\"), this.#stepsThisFrame);\n this.#counters.set(this.#counters.index(\"collisionEvents\"), this.#collisionEventCount);\n this.#counters.set(this.#counters.index(\"triggerEvents\"), this.#triggerEventCount);\n this.#counters.set(this.#counters.index(\"stepMs\"), this.#stepMs);\n this.#stepsThisFrame = 0;\n this.#collisionEventCount = 0;\n this.#triggerEventCount = 0;\n this.#stepMs = 0;\n }\n\n /**\n * Builds the per-layer collide masks from `physics2d.collisionMatrix` (`09-physics.md` §3).\n *\n * @throws IgnifxError with code `IGX-1155` when a name is not a declared layer, and reports\n * `IGX-1152` for a layer Rapier's sixteen interaction groups cannot express.\n */\n #buildCollideMasks(): void {\n for (const [name, others] of Object.entries(this.#settings.collisionMatrix)) {\n const index = this.#layerIndex(name);\n let mask = 0;\n for (const other of others) {\n mask |= 1 << this.#layerIndex(other);\n }\n this.#collideMasks[index] = mask;\n }\n }\n\n /**\n * Resolves a layer name to its index, refusing an undeclared one.\n *\n * @param name - The layer name.\n * @returns The index, clamped into Rapier's sixteen.\n * @throws IgnifxError with code `IGX-1155` when the project does not declare the layer.\n */\n #layerIndex(name: string): number {\n const index = this.#ctx.app.world.layers.indexOf(name);\n if (index < 0) {\n throw physics2DError(Physics2DErrorCode.unknownLayer, `${name} is not a layer this project declares.`, {\n context: { layer: name },\n hint: \"Add the layer to the layers settings section, or fix the name in physics2d.collisionMatrix.\",\n });\n }\n return this.#clampLayer(index, name);\n }\n\n /**\n * Reports a layer Rapier cannot express and falls back to layer 0.\n *\n * @param layer - The layer index.\n * @param subject - What the diagnostic names.\n * @returns The layer to use.\n */\n #clampLayer(layer: number, subject: string): number {\n if (layer < RAPIER_LAYER_LIMIT) {\n return layer;\n }\n this.#ctx.log.warn(\n `${Physics2DErrorCode.layerOutOfRange}: {entity} is on layer {layer}, which is outside the sixteen layers Rapier 2D can filter by.`,\n subject,\n String(layer),\n );\n return 0;\n }\n\n /** Marks every entity whose layer changed since its body was built. */\n #syncLayers(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record !== undefined && record.entity.layer !== record.layer) {\n this.markDirty(record.entity);\n }\n }\n }\n\n /** Marks every entity whose tilemap collision data changed since its shapes were built. */\n #pollTilemaps(): void {\n const colliders = this.#ctx.app.world.components(TilemapCollider2D);\n for (let index = 0; index < colliders.length; index += 1) {\n const collider = colliders[index];\n if (collider !== undefined && collider.needsRebuild()) {\n this.markDirty(collider.entity);\n }\n }\n }\n\n /** Rebuilds every body marked dirty. */\n #reconcile(): void {\n if (this.#dirty.size === 0) {\n return;\n }\n const pending = [...this.#dirty];\n this.#dirty.clear();\n for (const entity of pending) {\n this.#rebuildEntity(entity);\n }\n this.#colliderSetChanged = true;\n }\n\n /**\n * Tears an entity's body down and builds a new one from its current components.\n *\n * @param entity - The entity to rebuild.\n */\n #rebuildEntity(entity: Entity): void {\n const existing = this.#byEntity.get(entity);\n let carriedLinear: Vec2 | null = null;\n let carriedAngular = 0;\n if (existing !== undefined) {\n if (existing.motion === BodyMotion2D.dynamic) {\n carriedLinear = new Vec2();\n readLinearVelocity(existing.body, carriedLinear);\n carriedAngular = readAngularVelocity(existing.body);\n }\n this.#removeRecord(entity);\n }\n if (entity.isDestroyed || !entity.activeInHierarchy) {\n return;\n }\n const controller = entity.getComponent(CharacterController2D);\n const colliders = collidersOf(entity);\n if (colliders.length === 0 && controller === null) {\n return;\n }\n if (entity.parent !== null) {\n this.#ctx.log.warn(\n `${Physics2DErrorCode.bodyOnChildEntity}: {entity} carries a 2D physics body but is not a root entity; Rapier writes its world pose.`,\n entity.name,\n );\n }\n const record = this.#build(entity, entity.getComponent(Rigidbody2D), controller, colliders);\n if (record === null) {\n return;\n }\n this.#records.push(record);\n if (record.motion === BodyMotion2D.dynamic && carriedLinear !== null) {\n writeLinearVelocity(record.body, carriedLinear);\n writeAngularVelocity(record.body, carriedAngular);\n }\n }\n\n /**\n * Builds one body from an entity's colliders and controller.\n *\n * @param entity - The entity.\n * @param rigidbody - Its `Rigidbody2D`, or `null`.\n * @param controller - Its `CharacterController2D`, or `null`.\n * @param colliders - Its colliders, in component order.\n * @returns The new record, or `null` when nothing produced a shape — a `TilemapCollider2D` whose\n * tilemap has not published its geometry yet, say, which must not leave an empty body behind.\n */\n #build(\n entity: Entity,\n rigidbody: Rigidbody2D | null,\n controller: CharacterController2D | null,\n colliders: readonly Collider2D[],\n ): BodyRecord2D | null {\n const transform = entity.transform;\n transform.positionToRef(this.#scratchPose);\n const rotation = toRadians(transform.rotation2D);\n const layer = this.#clampLayer(entity.layer, entity.name);\n const motion = motionOf(rigidbody, controller);\n const body = createBody(this.#world, {\n motion,\n position: { x: this.#scratchPose.x, y: this.#scratchPose.y },\n rotation,\n gravityScale: rigidbody?.gravityScale ?? 1,\n linearDamping: rigidbody?.linearDamping ?? 0,\n angularDamping: rigidbody?.angularDamping ?? 0,\n freezeRotation: rigidbody?.freezeRotation ?? controller !== null,\n additionalMass: 0,\n startAsleep: false,\n });\n const record: BodyRecord2D = {\n entity,\n rigidbody,\n controller,\n body,\n colliders: [],\n owners: [],\n controllerCollider: null,\n motion,\n interpolate: this.#shouldInterpolate(rigidbody, controller, motion),\n layer,\n collisionEvents: false,\n previousPosition: new Vec2(this.#scratchPose.x, this.#scratchPose.y),\n previousRotation: rotation,\n currentPosition: new Vec2(this.#scratchPose.x, this.#scratchPose.y),\n currentRotation: rotation,\n preStepVelocity: new Vec2(),\n staticVersion: transform.worldMatrixVersion,\n reportedMove: false,\n disconnects: [],\n };\n this.#byEntity.set(entity, record);\n this.#attachColliders(record, colliders, layer);\n if (controller !== null) {\n this.#attachController(record, controller, layer);\n }\n if (record.colliders.length === 0) {\n this.#byEntity.delete(entity);\n destroyBody(this.#world, body);\n return null;\n }\n this.#applyMass(record);\n this.#applyCollisionEvents(record);\n this.#watchEntity(record);\n return record;\n }\n\n /**\n * Builds and registers every Rapier collider an entity's authored colliders stand for.\n *\n * @param record - The record being built.\n * @param colliders - The authored colliders.\n * @param layer - The entity's clamped layer.\n */\n #attachColliders(record: BodyRecord2D, colliders: readonly Collider2D[], layer: number): void {\n const defaults = this.#settings.defaultMaterial;\n const shapes: CollectedShape2D[] = [];\n for (const collider of colliders) {\n shapes.length = 0;\n collider.collectShapes(record.entity.transform.lossyScale, shapes);\n const effective = collider.layerOverride === \"\" ? layer : this.#layerIndex(collider.layerOverride);\n const material = collider.resolveMaterial(defaults);\n for (const collected of shapes) {\n this.#addCollider(record, collider, collected, effective, material);\n }\n }\n }\n\n /**\n * Creates one Rapier collider and binds it back to its author.\n *\n * @param record - The record it belongs to.\n * @param owner - The authored collider, or `null` for a controller shape.\n * @param collected - The geometry and its placement.\n * @param layer - The layer to filter as.\n * @param material - The surface.\n * @returns The created collider, or `null` when Rapier refused the geometry.\n */\n #addCollider(\n record: BodyRecord2D,\n owner: Collider2D | null,\n collected: CollectedShape2D,\n layer: number,\n material: Physics2DMaterialValues,\n ): RapierCollider | null {\n const collider = createCollider(this.#world, record.body, collected.shape, {\n offset: collected.offset,\n rotation: collected.rotation,\n sensor: owner?.isTrigger === true,\n groups: interactionGroups(1 << layer, this.#collideMasks[layer] ?? ALL_LAYER_BITS),\n friction: material.friction,\n restitution: material.restitution,\n frictionCombine: owner?.frictionCombine ?? \"average\",\n restitutionCombine: owner?.restitutionCombine ?? \"average\",\n events: owner?.isTrigger === true,\n });\n if (collider === null) {\n this.#ctx.log.warn(\n `${Physics2DErrorCode.colliderGeometryInvalid}: {entity} has a 2D collider whose geometry Rapier cannot build a shape from.`,\n record.entity.name,\n );\n return null;\n }\n const handle = colliderHandle(collider);\n record.colliders.push(collider);\n record.owners.push(owner);\n this.#byCollider.set(handle, { record, owner, collider });\n this.#colliderCount += 1;\n if (collected.oneWay) {\n this.#oneWay.set(handle, { collider, halfHeight: topOf(collected.shape) + collected.offset.y, top: 0 });\n }\n return collider;\n }\n\n /**\n * Builds a character controller's own shape and Rapier controller.\n *\n * @param record - The record being built.\n * @param controller - The component.\n * @param layer - The entity's clamped layer.\n */\n #attachController(record: BodyRecord2D, controller: CharacterController2D, layer: number): void {\n const half = Math.max(0, controller.height / 2 - controller.radius);\n const shape: CollectedShape2D = {\n shape:\n controller.shape === \"box\"\n ? {\n kind: \"box\",\n halfWidth: controller.radius,\n halfHeight: Math.max(controller.radius, controller.height / 2),\n }\n : { kind: \"capsule\", halfHeight: half, radius: controller.radius },\n offset: controller.offset,\n rotation: 0,\n oneWay: false,\n };\n const collider = this.#addCollider(record, null, shape, layer, this.#settings.defaultMaterial);\n record.controllerCollider = collider;\n if (collider === null) {\n return;\n }\n const rapierController = createController(this.#world, Math.max(controller.skinWidth, 1e-3));\n tuneController(rapierController, {\n slopeLimit: toRadians(controller.slopeLimit),\n slideLimit: toRadians(controller.slopeLimit),\n stepOffset: controller.stepOffset,\n stepMinWidth: AUTOSTEP_MIN_WIDTH,\n snapToGround: controller.snapToGround,\n pushBodies: controller.pushBodies,\n });\n this.#controllers.set(colliderHandle(collider), rapierController);\n }\n\n /**\n * Scales the colliders' densities so the body weighs exactly `Rigidbody2D.mass`.\n *\n * @param record - The record being built.\n */\n #applyMass(record: BodyRecord2D): void {\n const wanted = record.rigidbody?.mass ?? 0;\n if (record.motion !== BodyMotion2D.dynamic || wanted <= 0) {\n return;\n }\n const current = bodyMass(record.body);\n if (current <= 0) {\n return;\n }\n const density = wanted / current;\n for (const collider of record.colliders) {\n setColliderDensity(collider, density);\n }\n recomputeMass(record.body);\n }\n\n /**\n * Keeps a record in step with its entity: component changes rebuild, destruction removes.\n *\n * @param record - The record to watch.\n */\n #watchEntity(record: BodyRecord2D): void {\n const entity = record.entity;\n record.disconnects.push(\n entity.onComponentAdded.connect((component: Component): void => {\n this.#onComponentChanged(record, component);\n }),\n entity.onComponentRemoved.connect((component: Component): void => {\n this.#onComponentChanged(record, component);\n }),\n entity.onDestroyed.connect((): void => {\n this.#removeRecord(entity);\n }),\n );\n }\n\n /**\n * Recomputes `collisionEvents` when a component joins or leaves the entity.\n *\n * @param record - The body record.\n * @param component - The component that changed.\n */\n #onComponentChanged(record: BodyRecord2D, component: Component): void {\n if (\n component instanceof Collider2D ||\n component instanceof Rigidbody2D ||\n component instanceof CharacterController2D\n ) {\n this.markDirty(record.entity);\n return;\n }\n this.#applyCollisionEvents(record);\n }\n\n /**\n * Pushes the effective `collisionEvents` value to every collider of a body.\n *\n * @param record - The body record.\n */\n #applyCollisionEvents(record: BodyRecord2D): void {\n const mode = record.rigidbody?.collisionEvents ?? \"auto\";\n const enabled = mode === \"on\" ? true : mode === \"off\" ? false : this.#entityWantsEvents(record.entity);\n if (enabled === record.collisionEvents) {\n return;\n }\n record.collisionEvents = enabled;\n for (let index = 0; index < record.colliders.length; index += 1) {\n const collider = record.colliders[index];\n const owner = record.owners[index];\n if (collider !== undefined) {\n // A sensor always reports: a trigger volume nobody listens to is free, and a trigger volume\n // whose *other* side listens must still raise the pair.\n setColliderEvents(collider, enabled || owner?.isTrigger === true);\n }\n }\n }\n\n /**\n * Whether any script on an entity implements a physics callback.\n *\n * @param entity - The entity.\n * @returns `true` when at least one does.\n */\n #entityWantsEvents(entity: Entity): boolean {\n return (\n this.#ctx.entityImplements(entity, \"onCollisionEnter\") ||\n this.#ctx.entityImplements(entity, \"onCollisionStay\") ||\n this.#ctx.entityImplements(entity, \"onCollisionExit\") ||\n this.#ctx.entityImplements(entity, \"onTriggerEnter\") ||\n this.#ctx.entityImplements(entity, \"onTriggerExit\")\n );\n }\n\n /**\n * Drops an entity's body immediately.\n *\n * @param entity - The entity whose body goes away.\n */\n #removeRecord(entity: Entity): void {\n const record = this.#byEntity.get(entity);\n if (record === undefined) {\n return;\n }\n this.#teardown(record);\n const index = this.#records.indexOf(record);\n if (index >= 0) {\n this.#records.splice(index, 1);\n }\n this.#byEntity.delete(entity);\n this.#dirty.delete(entity);\n this.#colliderSetChanged = true;\n }\n\n /**\n * Releases one record's Rapier resources, signal connections, and index entries.\n *\n * @param record - The record to tear down.\n */\n #teardown(record: BodyRecord2D): void {\n for (const disconnect of record.disconnects) {\n disconnect();\n }\n record.disconnects = [];\n for (const collider of record.colliders) {\n const handle = colliderHandle(collider);\n const controller = this.#controllers.get(handle);\n if (controller !== undefined) {\n destroyController(this.#world, controller);\n this.#controllers.delete(handle);\n }\n this.#byCollider.delete(handle);\n this.#oneWay.delete(handle);\n this.#dropPairs(handle);\n this.#colliderCount -= 1;\n }\n record.colliders = [];\n record.owners = [];\n record.controllerCollider = null;\n // Removing the body removes its colliders with it (`pipeline/world.d.ts`, `removeRigidBody`).\n destroyBody(this.#world, record.body);\n }\n\n /**\n * Forgets every touching pair a departing collider took part in.\n *\n * @param handle - The collider handle.\n */\n #dropPairs(handle: number): void {\n for (const [key, pair] of this.#pairs) {\n if (pair.first === handle || pair.second === handle) {\n this.#pairs.delete(key);\n }\n }\n }\n\n /** Pushes every kinematic body's authored transform into Rapier before the step. */\n #pushKinematicPoses(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record === undefined || record.motion !== BodyMotion2D.kinematic || record.controller !== null) {\n continue;\n }\n const transform = record.entity.transform;\n transform.positionToRef(this.#scratchPose);\n setNextKinematicPose(\n record.body,\n this.#scratchVector.set(this.#scratchPose.x, this.#scratchPose.y),\n toRadians(transform.rotation2D),\n );\n }\n }\n\n /** Caches every one-way platform's world top, so the move filter needs no Rapier call. */\n #refreshOneWayPlatforms(): void {\n for (const platform of this.#oneWay.values()) {\n readColliderTranslation(platform.collider, this.#filterScratch);\n platform.top = this.#filterScratch.y + platform.halfHeight;\n }\n }\n\n /**\n * Remembers every event-reporting body's velocity before the solver runs, so a collision callback\n * can report the speed the two bodies met at rather than the one they left with.\n */\n #capturePreStepVelocities(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record !== undefined && record.collisionEvents) {\n readLinearVelocity(record.body, record.preStepVelocity);\n }\n }\n }\n\n /**\n * Hands every character controller its accumulated displacement and turns the resolved movement\n * into a kinematic target.\n *\n * @param fixedDeltaSeconds - The fixed step.\n */\n #moveControllers(fixedDeltaSeconds: number): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n const component = record?.controller ?? null;\n const collider = record?.controllerCollider ?? null;\n if (record === undefined || component === null || collider === null) {\n continue;\n }\n const rapierController = this.#controllers.get(colliderHandle(collider));\n if (rapierController === undefined) {\n continue;\n }\n const desired = component.takePendingMove();\n this.#filterDesiredY = desired.y;\n this.#filterFeet = record.currentPosition.y + component.offset.y - halfHeightOf(component);\n const grounded = computeControllerMovement(\n rapierController,\n collider,\n desired,\n interactionGroups(1 << record.layer, this.#collideMasks[record.layer] ?? ALL_LAYER_BITS),\n component.onOneWayPlatforms ? this.#oneWayFilter : null,\n this.#movement,\n );\n const normal = this.#collectControllerHits(rapierController, component);\n component.applyStepResult(this.#movement, grounded, normal, fixedDeltaSeconds);\n setNextKinematicPose(\n record.body,\n this.#scratchVector.set(\n record.currentPosition.x + this.#movement.x,\n record.currentPosition.y + this.#movement.y,\n ),\n record.currentRotation,\n );\n }\n }\n\n /**\n * The one-way platform predicate, bound once so the per-step move allocates nothing.\n *\n * Rapier runs it from inside WebAssembly, so it reads only plain JavaScript state:\n * {@link Physics2DRuntime.#refreshOneWayPlatforms} caches every platform's world top *before* the\n * move, because calling back into Rapier (`collider.translation()`) from inside the callback is a\n * re-entrant WebAssembly call and the obstacle is then never actually excluded (measured, S6.2).\n *\n * @param collider - The obstacle Rapier is considering.\n * @returns `false` when the obstacle should be ignored this step.\n */\n readonly #oneWayFilter = (collider: RapierCollider): boolean => {\n const platform = this.#oneWay.get(colliderHandle(collider));\n if (platform === undefined) {\n return true;\n }\n // Solid only when the character is coming down and its feet already cleared the platform's top.\n return this.#filterDesiredY <= 0 && this.#filterFeet >= platform.top - ONE_WAY_TOLERANCE;\n };\n\n /**\n * Reports every obstacle a controller hit and returns the most upward-facing normal.\n *\n * @remarks\n * Sensors are filtered out of the move (`lite/rapier/character.ts`), so a trigger volume never\n * shows up here and never contributes a ground normal.\n *\n * @param rapierController - Rapier's controller.\n * @param component - The component.\n * @returns The ground normal, or `null` when nothing was hit.\n */\n #collectControllerHits(rapierController: RapierController, component: CharacterController2D): Vec2Like | null {\n const count = controllerHitCount(rapierController);\n let best: Vec2Like | null = null;\n let bestY = -2;\n for (let index = 0; index < count; index += 1) {\n if (!readControllerHit(rapierController, index, this.#controllerHit)) {\n continue;\n }\n const normal = this.#controllerHit.normal;\n if (normal.y > bestY) {\n bestY = normal.y;\n this.#groundNormal.set(normal.x, normal.y);\n best = this.#groundNormal;\n }\n const binding = this.bindingOf(this.#controllerHit.collider);\n const event: MutableCharacterCollision = (this.#characterCollision ??= {\n other: null,\n otherCollider: null,\n point: this.#controllerHit.point,\n normal: this.#controllerHit.normal,\n });\n event.other = binding?.record.entity ?? null;\n event.otherCollider = binding?.owner ?? null;\n component.reportHit(event);\n }\n return best;\n }\n\n /** Snapshots every body's authoritative pose and writes it onto the entity. */\n #snapshot(): void {\n for (let index = 0; index < this.#records.length; index += 1) {\n const record = this.#records[index];\n if (record === undefined) {\n continue;\n }\n record.previousPosition.copyFrom(record.currentPosition);\n record.previousRotation = record.currentRotation;\n record.currentRotation = readBodyPose(record.body, record.currentPosition);\n if (record.motion === BodyMotion2D.static && record.rigidbody === null && record.controller === null) {\n this.#checkStaticMove(record);\n continue;\n }\n this.#writeTransform(record.entity, record.currentPosition, toDegrees(record.currentRotation));\n }\n }\n\n /**\n * Reports `IGX-1151` once for an implicit static body whose transform moved.\n *\n * @param record - The body record.\n */\n #checkStaticMove(record: BodyRecord2D): void {\n const version = record.entity.transform.worldMatrixVersion;\n if (version === record.staticVersion || record.reportedMove) {\n return;\n }\n record.entity.transform.positionToRef(this.#scratchPose);\n if (\n Math.abs(this.#scratchPose.x - record.currentPosition.x) < STATIC_MOVE_EPSILON &&\n Math.abs(this.#scratchPose.y - record.currentPosition.y) < STATIC_MOVE_EPSILON\n ) {\n record.staticVersion = version;\n return;\n }\n record.staticVersion = version;\n record.reportedMove = true;\n this.#ctx.log.warn(\n `${Physics2DErrorCode.movedStaticBody}: {entity} has 2D colliders but no Rigidbody2D, and it moved after its body was placed.`,\n record.entity.name,\n );\n }\n\n /**\n * Writes a 2D pose onto an entity without disturbing its Z.\n *\n * @param entity - The entity.\n * @param position - The world position in metres.\n * @param rotationDegrees - The rotation in degrees counter-clockwise.\n */\n #writeTransform(entity: Entity, position: Vec2Like, rotationDegrees: number): void {\n const transform = entity.transform;\n transform.positionToRef(this.#scratchPose);\n this.#scratchPose.set(position.x, position.y, this.#scratchPose.z);\n transform.position = this.#scratchPose;\n transform.rotation2D = rotationDegrees;\n }\n\n /** Drains Rapier's event queue and delivers enter, stay, and exit to both participants. */\n #dispatchEvents(): void {\n this.#startedPairs.length = 0;\n this.#endedPairs.length = 0;\n drainCollisions(this.#queue, (event): void => {\n const key = pairKey(event.first, event.second);\n if (!event.started) {\n const existing = this.#pairs.get(key);\n if (existing !== undefined) {\n this.#pairs.delete(key);\n this.#endedPairs.push(existing);\n }\n return;\n }\n const first = this.#byCollider.get(event.first);\n const second = this.#byCollider.get(event.second);\n if (first === undefined || second === undefined) {\n return;\n }\n const pair: PairState = {\n first: event.first,\n second: event.second,\n sensor: isColliderSensor(first.collider) || isColliderSensor(second.collider),\n entered: true,\n };\n this.#pairs.set(key, pair);\n this.#startedPairs.push(pair);\n });\n for (const pair of this.#endedPairs) {\n this.#deliverPair(pair, \"exit\");\n }\n for (const pair of this.#startedPairs) {\n this.#deliverPair(pair, \"enter\");\n }\n for (const pair of this.#pairs.values()) {\n if (pair.entered) {\n pair.entered = false;\n continue;\n }\n this.#deliverPair(pair, \"stay\");\n }\n }\n\n /**\n * Delivers one pair's event to both participants.\n *\n * @param pair - The touching pair.\n * @param phase - Which callback to deliver.\n */\n #deliverPair(pair: PairState, phase: \"enter\" | \"stay\" | \"exit\"): void {\n const first = this.#byCollider.get(pair.first);\n const second = this.#byCollider.get(pair.second);\n if (first === undefined || second === undefined) {\n return;\n }\n if (pair.sensor) {\n if (phase === \"stay\") {\n return;\n }\n this.#triggerEventCount += 1;\n const kind: PhysicsCallbackName = phase === \"enter\" ? \"onTriggerEnter\" : \"onTriggerExit\";\n this.#deliverTrigger(first, second, kind);\n this.#deliverTrigger(second, first, kind);\n return;\n }\n this.#collisionEventCount += 1;\n const kind: PhysicsCallbackName =\n phase === \"enter\" ? \"onCollisionEnter\" : phase === \"exit\" ? \"onCollisionExit\" : \"onCollisionStay\";\n this.#readContacts(pair, phase);\n this.#deliverCollision(first, second, kind);\n this.#deliverCollision(second, first, kind);\n }\n\n /**\n * Fills the pooled contact point for a collision event.\n *\n * @param pair - The touching pair.\n * @param phase - Which callback is being delivered; an exit has no contact left to read.\n */\n #readContacts(pair: PairState, phase: \"enter\" | \"stay\" | \"exit\"): void {\n this.#contactPoint.impulse = 0;\n if (phase === \"exit\") {\n return;\n }\n const first = this.#byCollider.get(pair.first);\n const second = this.#byCollider.get(pair.second);\n if (first === undefined || second === undefined) {\n return;\n }\n readContact(this.#world, first.collider, second.collider, this.#contact);\n if (!this.#contact.touching) {\n return;\n }\n this.#contactPoint.point.copyFrom(this.#contact.point);\n this.#contactPoint.normal.copyFrom(this.#contact.normal);\n this.#contactPoint.impulse = this.#contact.impulse;\n }\n\n /**\n * Delivers one trigger callback to one entity.\n *\n * @param target - The binding whose scripts receive it.\n * @param other - The other participant.\n * @param kind - Which callback to deliver.\n */\n #deliverTrigger(target: ColliderBinding, other: ColliderBinding, kind: PhysicsCallbackName): void {\n if (target.record.entity.isDestroyed) {\n return;\n }\n const event: MutableTrigger = (this.#trigger ??= {\n other: null,\n otherCollider: null,\n self: target.record.entity,\n selfCollider: null,\n });\n event.self = target.record.entity;\n event.selfCollider = target.owner;\n event.other = other.record.entity;\n event.otherCollider = other.owner;\n this.#ctx.dispatchScriptCallback(target.record.entity, kind, event);\n }\n\n /**\n * Delivers one collision callback to one entity.\n *\n * @param target - The binding whose scripts receive it.\n * @param other - The other participant.\n * @param kind - Which callback to deliver.\n */\n #deliverCollision(target: ColliderBinding, other: ColliderBinding, kind: PhysicsCallbackName): void {\n if (target.record.entity.isDestroyed || !target.record.collisionEvents) {\n return;\n }\n this.#relativeVelocity.set(\n target.record.preStepVelocity.x - other.record.preStepVelocity.x,\n target.record.preStepVelocity.y - other.record.preStepVelocity.y,\n );\n const collision: MutableCollision = (this.#collision ??= {\n other: null,\n otherCollider: null,\n self: target.record.entity,\n selfCollider: null,\n contacts: this.#contacts,\n relativeVelocity: this.#relativeVelocity,\n });\n collision.self = target.record.entity;\n collision.selfCollider = target.owner;\n collision.other = other.record.entity;\n collision.otherCollider = other.owner;\n this.#ctx.dispatchScriptCallback(target.record.entity, kind, collision);\n }\n\n /**\n * Whether a body's display pose is interpolated.\n *\n * @param rigidbody - The `Rigidbody2D`, or `null`.\n * @param controller - The `CharacterController2D`, or `null`.\n * @param motion - The motion type.\n * @returns `true` when interpolation applies.\n */\n #shouldInterpolate(\n rigidbody: Rigidbody2D | null,\n controller: CharacterController2D | null,\n motion: BodyMotion2D,\n ): boolean {\n if (!this.#settings.interpolation || motion === BodyMotion2D.static) {\n return false;\n }\n if (controller !== null) {\n return controller.interpolation === \"interpolate\";\n }\n return rigidbody !== null && rigidbody.interpolation === \"interpolate\";\n }\n}\n\n/** How far below a one-way platform's top the character's feet may be and still land on it. */\nconst ONE_WAY_TOLERANCE = 0.02;\n\n/**\n * The colliders that make up an entity's body: every enabled `Collider2D` on the entity itself.\n *\n * @param entity - The entity.\n * @returns The colliders, in component order.\n */\nfunction collidersOf(entity: Entity): readonly Collider2D[] {\n const found: Collider2D[] = [];\n const components = entity.components;\n for (let index = 0; index < components.length; index += 1) {\n const component = components[index];\n if (component instanceof Collider2D && component.enabled && !component.isDestroyed) {\n found.push(component);\n }\n }\n return found;\n}\n\n/**\n * The motion type a body takes.\n *\n * @param rigidbody - The component, or `null`.\n * @param controller - The character controller, or `null`.\n * @returns The motion type; a controller is always kinematic.\n */\nfunction motionOf(rigidbody: Rigidbody2D | null, controller: CharacterController2D | null): BodyMotion2D {\n if (controller !== null) {\n return BodyMotion2D.kinematic;\n }\n if (rigidbody === null || rigidbody.bodyType === \"static\") {\n return BodyMotion2D.static;\n }\n return rigidbody.bodyType === \"kinematic\" ? BodyMotion2D.kinematic : BodyMotion2D.dynamic;\n}\n\n/**\n * How far a shape reaches above its own origin, which is what a one-way platform's top is measured\n * from.\n *\n * @param shape - The geometry.\n * @returns The half-height in metres.\n */\nfunction topOf(shape: CollectedShape2D[\"shape\"]): number {\n if (shape.kind === \"box\") {\n return shape.halfHeight;\n }\n if (shape.kind === \"circle\") {\n return shape.radius;\n }\n if (shape.kind === \"capsule\") {\n return shape.halfHeight + shape.radius;\n }\n let top = 0;\n for (let index = 1; index < shape.points.length; index += 2) {\n top = Math.max(top, shape.points[index] ?? 0);\n }\n return top;\n}\n\n/**\n * Half a character controller's total height, including its caps.\n *\n * @param component - The controller.\n * @returns The half-height in metres.\n */\nfunction halfHeightOf(component: CharacterController2D): number {\n return Math.max(component.radius, component.height / 2);\n}\n\n/**\n * The map key of one collider pair, order-independent.\n *\n * @param first - One handle.\n * @param second - The other.\n * @returns The key.\n */\nfunction pairKey(first: number, second: number): string {\n return first < second ? `${String(first)}:${String(second)}` : `${String(second)}:${String(first)}`;\n}\n\n/**\n * The shortest signed angular difference between two angles, so interpolation never spins the long\n * way round.\n *\n * @param from - The start angle in radians.\n * @param to - The end angle in radians.\n * @returns The signed difference in `(-π, π]`.\n */\nfunction shortestArc(from: number, to: number): number {\n const twoPi = Math.PI * 2;\n const delta = ((((to - from + Math.PI) % twoPi) + twoPi) % twoPi) - Math.PI;\n return delta;\n}\n\n/**\n * A monotonic millisecond reading for the `stepMs` counter. It never feeds the simulation, so it\n * does not break the \"no wall-clock reads\" rule of `09-physics.md` §8.\n *\n * @returns Milliseconds since the process started.\n */\nfunction now(): number {\n return performance.now();\n}\n","import type { Physics2DHost } from \"./host.js\";\nimport type { System, SystemContext } from \"@ignifx/core\";\n\n/**\n * The three systems `docs/architecture/01-lifecycle-and-time.md` §3 and `09-physics.md` §1 put\n * physics in the frame with — identical in 2D, except that Rapier is stepped directly and there is\n * no Babylon Lite simulation scene:\n *\n * ```\n * Systems(FixedUpdate, -100) restore the authoritative pose of interpolated bodies\n * scripts.fixedUpdate(dt) forces, velocities, CharacterController2D.move()\n * Systems(FixedUpdate, 100) step Rapier, snapshot poses, dispatch collision and trigger events\n * ── the fixed loop ends; time.fixedStepAlpha is final ──\n * Systems(Update, -900) write lerp(prev, cur, time.fixedStepAlpha)\n * scripts.update / lateUpdate, animation, rendering — all read the display pose\n * ```\n *\n * **Why the display pose is written at the top of `Update` and not in `PreRender`** (2026-09-08).\n * It used to be written in `PreRender`, after `lateUpdate`. `Camera2DFollow` and every hand-written\n * follow camera run in `update`/`lateUpdate`, so they framed the character where the last fixed step\n * left it while the renderer drew it at `lerp(prev, cur, alpha)`: up to one fixed step of relative\n * judder every frame, which a pixel-perfect camera turns into a visibly shaking sprite because the\n * two poses are quantised independently. Writing the pose before `scripts.update` means scripts,\n * animation, camera rigs and the render sync all see the pose the frame presents, which is Unity's\n * model. The simulation is unaffected: the restore system at `FixedUpdate −100` runs before\n * `scripts.fixedUpdate`, so `fixedUpdate` and Rapier still see the authoritative pose.\n *\n * Everything here is `@internal`.\n */\n\n/** The `FixedUpdate` order the restore system runs at. */\nexport const PHYSICS_2D_RESTORE_ORDER = -100;\n\n/** The `FixedUpdate` order the step system runs at. */\nexport const PHYSICS_2D_STEP_ORDER = 100;\n\n/**\n * The `Update` order the interpolation system runs at. Negative, so it lands in\n * `Systems(Update, order < 0)` — after the fixed loop and lifecycle flush B, before\n * `scripts.update` — and low enough that a game's own `Update` systems can still be ordered in\n * front of it if they must see the authoritative pose.\n */\nexport const PHYSICS_2D_INTERPOLATE_ORDER = -900;\n\n/**\n * Undoes the display pose the interpolation system wrote at the top of the previous `Update`, so\n * `fixedUpdate` and Rapier both see the authoritative pose.\n *\n * @internal\n */\nexport class Physics2DRestoreSystem implements System {\n /** The name diagnostics and error reports use. */\n readonly name = \"physics2d-restore\";\n\n readonly #host: Physics2DHost;\n\n /**\n * Binds the system to the runtime holder.\n *\n * @param host - The holder the runtime lands in.\n */\n constructor(host: Physics2DHost) {\n this.#host = host;\n }\n\n /** Restores every interpolated body's authoritative pose. */\n update(): void {\n this.#host.runtime?.restorePoses();\n }\n}\n\n/**\n * Steps Rapier once, snapshots the poses, and dispatches the step's events.\n *\n * @internal\n */\nexport class Physics2DStepSystem implements System {\n /** The name diagnostics and error reports use. */\n readonly name = \"physics2d-step\";\n\n readonly #host: Physics2DHost;\n\n /**\n * Binds the system to the runtime holder.\n *\n * @param host - The holder the runtime lands in.\n */\n constructor(host: Physics2DHost) {\n this.#host = host;\n }\n\n /**\n * Runs one fixed step.\n *\n * @param ctx - The world, clock, phase, and fixed delta.\n */\n update(ctx: SystemContext): void {\n this.#host.runtime?.step(ctx.dt);\n }\n}\n\n/**\n * Writes the interpolated display pose once per frame.\n *\n * @internal\n */\nexport class Physics2DInterpolationSystem implements System {\n /** The name diagnostics and error reports use. */\n readonly name = \"physics2d-interpolate\";\n\n readonly #host: Physics2DHost;\n\n /**\n * Binds the system to the runtime holder.\n *\n * @param host - The holder the runtime lands in.\n */\n constructor(host: Physics2DHost) {\n this.#host = host;\n }\n\n /**\n * Writes `lerp(prev, cur, alpha)` into every interpolated body's transform.\n *\n * @param ctx - The world, clock, phase, and frame delta.\n */\n update(ctx: SystemContext): void {\n this.#host.runtime?.interpolate(ctx.time.fixedStepAlpha);\n }\n}\n","import { Vec2 } from \"@ignifx/core\";\nimport { Physics2DErrorCode, physics2DError } from \"./errors.js\";\nimport { castRay, castRayAll, castShape, interactionGroups, overlapShape, toRadians } from \"./lite/rapier/world.js\";\nimport type { RapierShape2D, RapierWorld, RayResult2D, ShapeCastResult2D } from \"./lite/rapier/world.js\";\nimport type { QueryOptions2D, RaycastHit2D, ShapeCastHit2D } from \"./queries.js\";\nimport type { Physics2DHost } from \"./runtime/host.js\";\nimport type { Physics2DRuntime } from \"./runtime/runtime.js\";\nimport type { Physics2DSettings } from \"./settings.js\";\nimport type { Entity, Vec2Like } from \"@ignifx/core\";\n\n/**\n * `app.physics2d` (`docs/architecture/11-2d-toolkit.md` §8, `09-physics.md` §5): gravity, the\n * queries, and the Rapier escape hatch.\n *\n * Every query is shape-accurate and reports the exact collider it hit, because Rapier's query\n * pipeline returns the `Collider` (`pipeline/world.d.ts`). The bounds-index approximations\n * `09-physics.md` §5 documents for Havok have no counterpart here.\n */\n\n/** How far a ray travels when the caller names no distance, in metres. */\nconst DEFAULT_RAY_DISTANCE = 1e4;\n\n/** Every layer bit Rapier can express. */\nconst ALL_LAYERS = 0xff_ff;\n\n/**\n * The Rapier objects the 2D physics extension owns. Unstable escape hatch\n * (`docs/architecture/00-overview.md` §3).\n *\n * @public\n */\nexport interface Physics2DRapierHandles {\n /** Rapier's `World`. */\n readonly world: RapierWorld;\n}\n\n/**\n * The service behind `app.physics2d`.\n *\n * @example\n * ```ts\n * const hit = app.physics2d.raycast({ x: 0, y: 5 }, { x: 0, y: -1 }, 20);\n * if (hit !== null) {\n * app.log.info(`ray hit ${hit.entity.name} at ${String(hit.point.y)}`);\n * }\n * ```\n *\n * @public\n */\nexport class Physics2DService {\n readonly #host: Physics2DHost;\n\n readonly #gravity = new Vec2();\n\n readonly #point = new Vec2();\n\n readonly #normal = new Vec2();\n\n readonly #rayResult: RayResult2D = { hit: false, collider: null, distance: 0 };\n\n readonly #sweepResult: ShapeCastResult2D = { hit: false, collider: null, distance: 0 };\n\n readonly #hits: RaycastHit2D[] = [];\n\n readonly #overlaps: Entity[] = [];\n\n /**\n * Wraps the holder the extension fills in from `onStart`.\n *\n * @param host - The holder the runtime lands in.\n * @param settings - The resolved `physics2d` settings section.\n *\n * @internal\n */\n constructor(host: Physics2DHost, settings: Physics2DSettings) {\n this.#host = host;\n this.#gravity.set(settings.gravity.x, settings.gravity.y);\n }\n\n /**\n * World gravity in metres per second squared.\n *\n * @returns A live view; writing to it does nothing, assign the property instead.\n */\n get gravity(): Vec2 {\n return this.#gravity;\n }\n\n /**\n * Replaces world gravity, which every body feels from the next fixed step.\n *\n * @param value - The new acceleration vector.\n */\n set gravity(value: Vec2Like) {\n this.#gravity.set(value.x, value.y);\n this.#rt().setGravity(this.#gravity);\n }\n\n /**\n * The Rapier handles.\n *\n * @returns The world.\n */\n get rapier(): Physics2DRapierHandles {\n return { world: this.#rt().world };\n }\n\n /**\n * Casts a ray and returns the first entity it hits.\n *\n * @param origin - The world-space origin, in metres.\n * @param direction - The direction; it is normalised for you.\n * @param maxDistance - How far to travel; defaults to 10 km.\n * @param options - Layer mask and trigger behaviour.\n * @returns The hit, or `null` when the ray clears everything.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n raycast(\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number = DEFAULT_RAY_DISTANCE,\n options?: QueryOptions2D,\n ): RaycastHit2D | null {\n this.#requireStep(\"raycast\");\n const unit = normalize(direction);\n if (unit === null) {\n return null;\n }\n castRay(\n this.#rt().world,\n origin,\n unit,\n maxDistance,\n groupsOf(options),\n options?.hitTriggers === true,\n this.#point,\n this.#normal,\n this.#rayResult,\n );\n return this.#toHit(this.#rayResult.collider, this.#rayResult.distance, this.#point, this.#normal);\n }\n\n /**\n * Casts a ray and returns every entity along it, nearest first.\n *\n * @param origin - The world-space origin.\n * @param direction - The direction; it is normalised for you.\n * @param maxDistance - How far to travel; defaults to 10 km.\n * @param options - Layer mask and trigger behaviour.\n * @returns The hits, in increasing distance. The array is reused between calls.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n raycastAll(\n origin: Vec2Like,\n direction: Vec2Like,\n maxDistance: number = DEFAULT_RAY_DISTANCE,\n options?: QueryOptions2D,\n ): readonly RaycastHit2D[] {\n this.#requireStep(\"raycastAll\");\n this.#hits.length = 0;\n const unit = normalize(direction);\n if (unit === null) {\n return this.#hits;\n }\n const runtime = this.#rt();\n castRayAll(\n runtime.world,\n origin,\n unit,\n maxDistance,\n groupsOf(options),\n options?.hitTriggers === true,\n (collider, distance, normalX, normalY): void => {\n const binding = runtime.bindingOf(collider);\n if (binding === null) {\n return;\n }\n this.#hits.push({\n entity: binding.record.entity,\n collider: binding.owner,\n point: new Vec2(origin.x + unit.x * distance, origin.y + unit.y * distance),\n normal: new Vec2(normalX, normalY),\n distance,\n });\n },\n );\n this.#hits.sort(byDistance);\n return this.#hits;\n }\n\n /**\n * Lists the entities a circle overlaps.\n *\n * @param centre - The circle's world position.\n * @param radius - Its radius in metres.\n * @param options - Layer mask and trigger behaviour.\n * @returns The overlapping entities, in Rapier's order. The array is reused between calls.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n overlapCircle(centre: Vec2Like, radius: number, options?: QueryOptions2D): readonly Entity[] {\n return this.#overlap({ kind: \"circle\", radius }, centre, 0, options, \"overlapCircle\");\n }\n\n /**\n * Lists the entities a box overlaps.\n *\n * @param centre - The box's world position.\n * @param size - Its full width and height in metres.\n * @param rotation - Its rotation in degrees counter-clockwise; defaults to `0`.\n * @param options - Layer mask and trigger behaviour.\n * @returns The overlapping entities. The array is reused between calls.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n overlapBox(centre: Vec2Like, size: Vec2Like, rotation: number = 0, options?: QueryOptions2D): readonly Entity[] {\n return this.#overlap(\n { kind: \"box\", halfWidth: size.x / 2, halfHeight: size.y / 2 },\n centre,\n toRadians(rotation),\n options,\n \"overlapBox\",\n );\n }\n\n /**\n * Sweeps a circle and returns the first contact.\n *\n * @param centre - Where the sweep starts.\n * @param radius - The circle's radius in metres.\n * @param direction - The sweep direction; it is normalised for you.\n * @param maxDistance - How far to sweep.\n * @param options - Layer mask and trigger behaviour.\n * @returns The hit, or `null`.\n * @throws IgnifxError with code `IGX-1153` when no fixed step has run yet.\n */\n shapeCast(\n centre: Vec2Like,\n radius: number,\n direction: Vec2Like,\n maxDistance: number,\n options?: QueryOptions2D,\n ): ShapeCastHit2D | null {\n this.#requireStep(\"shapeCast\");\n const unit = normalize(direction);\n if (unit === null) {\n return null;\n }\n castShape(\n this.#rt().world,\n { kind: \"circle\", radius },\n centre,\n 0,\n unit,\n maxDistance,\n groupsOf(options),\n options?.hitTriggers === true,\n this.#point,\n this.#normal,\n this.#sweepResult,\n );\n const binding = this.#rt().bindingOf(this.#sweepResult.collider);\n if (!this.#sweepResult.hit || binding === null) {\n return null;\n }\n return {\n entity: binding.record.entity,\n collider: binding.owner,\n point: this.#point.clone(),\n normal: this.#normal.clone(),\n distance: this.#sweepResult.distance,\n fraction: maxDistance > 0 ? this.#sweepResult.distance / maxDistance : 0,\n };\n }\n\n /**\n * Runs one overlap query.\n *\n * @param shape - The query geometry.\n * @param centre - Its world position.\n * @param rotation - Its rotation in radians.\n * @param options - Layer mask and trigger behaviour.\n * @param name - The query name, for the diagnostic.\n * @returns The overlapping entities.\n */\n #overlap(\n shape: RapierShape2D,\n centre: Vec2Like,\n rotation: number,\n options: QueryOptions2D | undefined,\n name: string,\n ): readonly Entity[] {\n this.#requireStep(name);\n this.#overlaps.length = 0;\n const runtime = this.#rt();\n overlapShape(\n runtime.world,\n shape,\n centre,\n rotation,\n groupsOf(options),\n options?.hitTriggers === true,\n (collider): void => {\n const entity = runtime.bindingOf(collider)?.record.entity ?? null;\n if (entity !== null && !this.#overlaps.includes(entity)) {\n this.#overlaps.push(entity);\n }\n },\n );\n return this.#overlaps;\n }\n\n /**\n * Turns a Rapier collider and a distance into a public hit.\n *\n * @param collider - The collider that was hit, or `null`.\n * @param distance - How far away it was.\n * @param point - The contact point.\n * @param normal - The contact normal.\n * @returns The hit, or `null`.\n */\n #toHit(collider: RayResult2D[\"collider\"], distance: number, point: Vec2, normal: Vec2): RaycastHit2D | null {\n const binding = this.#rt().bindingOf(collider);\n if (binding === null) {\n return null;\n }\n return {\n entity: binding.record.entity,\n collider: binding.owner,\n point: point.clone(),\n normal: normal.clone(),\n distance,\n };\n }\n\n /**\n * The runtime, once `onStart` has built it.\n *\n * @returns The runtime.\n * @throws IgnifxError with code `IGX-1150` when Rapier has not finished loading.\n */\n #rt(): Physics2DRuntime {\n const runtime = this.#host.runtime;\n if (runtime === null) {\n throw physics2DError(Physics2DErrorCode.rapierUnavailable, \"The 2D physics world does not exist yet.\", {\n hint: \"app.physics2d is usable from the first frame onwards; await app.start() first.\",\n });\n }\n return runtime;\n }\n\n /**\n * Refuses a query that ran before Rapier built its broadphase (spike S6.2).\n *\n * @param query - The query name, for the diagnostic.\n * @throws IgnifxError with code `IGX-1153` when no step has completed.\n */\n #requireStep(query: string): void {\n if (this.#rt().hasStepped) {\n return;\n }\n throw physics2DError(Physics2DErrorCode.queryBeforeStep, `${query}() ran before the first fixed step.`, {\n context: { query },\n hint: \"Step the app at least once (app.step(1 / 60)) before querying; Rapier builds its broadphase in the first step.\",\n });\n }\n}\n\n/**\n * The packed interaction word a query filters with: it belongs to every layer and collides with the\n * layers the caller named.\n *\n * @param options - The query options.\n * @returns The packed word.\n */\nfunction groupsOf(options: QueryOptions2D | undefined): number {\n const mask = options?.layerMask?.bits ?? ALL_LAYERS;\n return interactionGroups(ALL_LAYERS, mask & ALL_LAYERS);\n}\n\n/**\n * Normalises a direction.\n *\n * @param direction - The direction.\n * @returns A fresh unit vector, or `null` when the direction is zero.\n */\nfunction normalize(direction: Vec2Like): Vec2Like | null {\n const length = Math.hypot(direction.x, direction.y);\n return length === 0 ? null : { x: direction.x / length, y: direction.y / length };\n}\n\n/**\n * Orders two hits by distance.\n *\n * @param first - One hit.\n * @param second - The other.\n * @returns A negative number when `first` is nearer.\n */\nfunction byDistance(first: RaycastHit2D, second: RaycastHit2D): number {\n return first.distance - second.distance;\n}\n","import { array, bool, defineSchema, f32, map, record, str, u32, vec2 } from \"@ignifx/core\";\nimport type { Schema, Vec2Like } from \"@ignifx/core\";\n\n/**\n * The `physics2d` project settings section (`docs/architecture/11-2d-toolkit.md` §8,\n * `09-physics.md` §6). It mirrors the 3D `physics` section with a 2D gravity vector and Rapier's\n * solver knob in place of Havok's world velocity clamps, which Rapier has no equivalent of.\n *\n * `fixedDeltaTime` is **not** here: the fixed step is shared with the core `time` section and the\n * extension reads it from `app.time.fixedDeltaTime` on every step.\n */\n\n/**\n * The section name as it appears in `ignifx.config.ts`.\n *\n * @public\n */\nexport const PHYSICS_2D_SETTINGS_SECTION = \"physics2d\";\n\n/**\n * A surface, either as the `physics2d.defaultMaterial` setting or inline on a collider.\n *\n * @public\n */\nexport interface Physics2DMaterialValues {\n /** The friction coefficient. */\n readonly friction: number;\n /** How much of the approach speed is returned, `0` to `1`. */\n readonly restitution: number;\n}\n\n/**\n * The resolved `physics2d` settings section.\n *\n * @example\n * ```ts\n * // ignifx.config.ts\n * export default {\n * layers: { layers: [\"Default\", \"Player\", \"Enemy\"] },\n * physics2d: {\n * gravity: { x: 0, y: -9.81 },\n * collisionMatrix: { Player: [\"Default\", \"Enemy\"], Enemy: [\"Default\"] },\n * },\n * };\n * ```\n *\n * @public\n */\nexport interface Physics2DSettings {\n /** World gravity in metres per second squared; +Y is up (`11-2d-toolkit.md` §3). */\n readonly gravity: Vec2Like;\n /**\n * Which layers each layer collides with. A layer the map does not mention collides with\n * everything, which is what makes the default project need no matrix at all.\n */\n readonly collisionMatrix: Readonly<Record<string, readonly string[]>>;\n /** The surface a collider with no material of its own uses. */\n readonly defaultMaterial: Physics2DMaterialValues;\n /** How many iterations Rapier's constraint solver runs; `0` leaves Rapier's own default (4). */\n readonly velocityIterations: number;\n /** Whether dynamic bodies and character controllers interpolate between fixed steps. */\n readonly interpolation: boolean;\n}\n\n/** Standard gravity in the 2D plane. */\nconst DEFAULT_GRAVITY: Vec2Like = Object.freeze({ x: 0, y: -9.81 });\n\n/** The default surface: ordinary friction, no bounce — the same numbers the 3D section uses. */\nconst DEFAULT_MATERIAL: Physics2DMaterialValues = Object.freeze({ friction: 0.6, restitution: 0 });\n\n/** An empty matrix, which means \"everything collides with everything\". */\nconst DEFAULT_COLLISION_MATRIX: Readonly<Record<string, readonly string[]>> = Object.freeze({});\n\n/**\n * Builds the schema the `physics2d` section is validated against.\n *\n * @remarks\n * It is a function, not a module-level constant: every field kind is a function call, and module\n * scope holds declarations and immutable constants only (`CONSTITUTION.md` §3.5).\n *\n * @returns The schema.\n *\n * @public\n */\nexport function physics2DSettingsSchema(): Schema {\n return defineSchema({\n gravity: vec2(DEFAULT_GRAVITY),\n collisionMatrix: map(array(str())),\n defaultMaterial: record({\n friction: f32(DEFAULT_MATERIAL.friction, { min: 0 }),\n restitution: f32(DEFAULT_MATERIAL.restitution, { min: 0, max: 1 }),\n }),\n velocityIterations: u32(0),\n interpolation: bool(true),\n });\n}\n\n/**\n * The values used when a project omits the `physics2d` section.\n *\n * @returns A fresh defaults object.\n *\n * @public\n */\nexport function defaultPhysics2DSettings(): Physics2DSettings {\n return {\n gravity: DEFAULT_GRAVITY,\n collisionMatrix: DEFAULT_COLLISION_MATRIX,\n defaultMaterial: DEFAULT_MATERIAL,\n velocityIterations: 0,\n interpolation: true,\n };\n}\n","import { defineExtension, Phase } from \"@ignifx/core\";\nimport { CharacterController2D } from \"./components/character-controller.js\";\nimport {\n BoxCollider2D,\n CapsuleCollider2D,\n CircleCollider2D,\n EdgeCollider2D,\n PolygonCollider2D,\n} from \"./components/colliders.js\";\nimport { Rigidbody2D } from \"./components/rigidbody.js\";\nimport { TilemapCollider2D } from \"./components/tilemap-collider.js\";\nimport { PHYSICS_2D_ERROR_MESSAGES, Physics2DErrorCode, physics2DError } from \"./errors.js\";\nimport { loadRapier } from \"./lite/rapier/module.js\";\nimport { createWorld } from \"./lite/rapier/world.js\";\nimport { createPhysicsMaterial2DLoader } from \"./material.js\";\nimport { Physics2DHost } from \"./runtime/host.js\";\nimport { Physics2DHostKey } from \"./runtime/runtime-key.js\";\nimport { PHYSICS_2D_DIAGNOSTICS_COUNTERS, PHYSICS_2D_DIAGNOSTICS_GROUP, Physics2DRuntime } from \"./runtime/runtime.js\";\nimport {\n PHYSICS_2D_INTERPOLATE_ORDER,\n PHYSICS_2D_RESTORE_ORDER,\n PHYSICS_2D_STEP_ORDER,\n Physics2DInterpolationSystem,\n Physics2DRestoreSystem,\n Physics2DStepSystem,\n} from \"./runtime/systems.js\";\nimport { Physics2DService } from \"./service.js\";\nimport { defaultPhysics2DSettings, PHYSICS_2D_SETTINGS_SECTION, physics2DSettingsSchema } from \"./settings.js\";\nimport type { Collider2D } from \"./components/collider.js\";\nimport type { Physics2DSettings } from \"./settings.js\";\nimport type { App, ComponentType, ConcreteComponentType, Extension, ExtensionContext } from \"@ignifx/core\";\n\n/**\n * The 2D toolkit is optional; physics only needs its tile-collision data type.\n * Create the Rapier world in `onStart`, once the world's layer table exists and WASM is ready.\n * Reject a world already using 3D physics with `IGX-1101`.\n */\n\n/**\n * What `physics2d()` accepts.\n *\n * @public\n */\nexport interface Physics2DOptions {\n /**\n * Replaces the step that instantiates Rapier's WebAssembly module. The default awaits\n * `RAPIER.init()`, which decodes the base64 payload the `-compat` build inlines; a host that has\n * already preloaded the module, or a test that wants to observe the failure path, supplies its\n * own.\n */\n readonly initialize?: () => Promise<void>;\n}\n\ndeclare module \"@ignifx/core\" {\n interface App {\n /** 2D physics: gravity, queries, and the Rapier escape hatch. */\n readonly physics2d: Physics2DService;\n }\n}\n\n/**\n * Builds the 2D physics extension.\n *\n * @param options - Optionally, an already-instantiated Rapier module.\n * @returns The extension descriptor.\n *\n * @example\n * ```ts\n * const app = await createApp({ headless: true, extensions: [physics2d()] });\n * ```\n *\n * @public\n */\nexport const physics2d: (options?: Physics2DOptions) => Extension = defineExtension<Physics2DOptions>(\n // `defineExtension` calls the factory with `undefined` when a game writes `physics2d()`, so the\n // default parameter is load-bearing even though the declared parameter type is not nullish.\n // oxlint-disable-next-line typescript/no-useless-default-assignment\n (options = {}) => {\n const host = new Physics2DHost();\n let context: ExtensionContext | null = null;\n\n return {\n name: \"@ignifx/physics-2d\",\n version: \"0.0.0\",\n engine: \">=0.0.0 <1.0.0\",\n requires: [],\n optional: [\"@ignifx/2d\", \"@ignifx/devtools\"],\n\n register(ctx: ExtensionContext): void {\n context = ctx;\n ctx.registerErrorCodes(PHYSICS_2D_ERROR_MESSAGES);\n ctx.registerSettings<Physics2DSettings>(\n PHYSICS_2D_SETTINGS_SECTION,\n physics2DSettingsSchema(),\n defaultPhysics2DSettings(),\n );\n ctx.registerComponents(PHYSICS_2D_COMPONENTS);\n ctx.registerAssetLoader(createPhysicsMaterial2DLoader());\n const settings = ctx.settings<Physics2DSettings>(PHYSICS_2D_SETTINGS_SECTION);\n const service = new Physics2DService(host, settings);\n ctx.registerService(Physics2DHostKey, host);\n ctx.registerService(Physics2DService, service);\n ctx.defineAppProperty(\"physics2d\", (): unknown => service);\n // None of the three runs in edit mode: an editor's scene view never simulates, and the\n // interpolation would overwrite a transform the editor just moved (ADR-0028).\n ctx.registerSystem(new Physics2DRestoreSystem(host), {\n phase: Phase.FixedUpdate,\n order: PHYSICS_2D_RESTORE_ORDER,\n editMode: \"skip\",\n });\n ctx.registerSystem(new Physics2DStepSystem(host), {\n phase: Phase.FixedUpdate,\n order: PHYSICS_2D_STEP_ORDER,\n editMode: \"skip\",\n });\n // `Update`, not `PreRender`: camera rigs and scripts must read the same pose the renderer\n // draws (`runtime/systems.ts`, `docs/architecture/01-lifecycle-and-time.md` §3).\n ctx.registerSystem(new Physics2DInterpolationSystem(host), {\n phase: Phase.Update,\n order: PHYSICS_2D_INTERPOLATE_ORDER,\n editMode: \"skip\",\n });\n },\n\n async onStart(app: App): Promise<void> {\n const ctx = context;\n if (ctx === null) {\n return;\n }\n if (app.world.lite.simulationScene !== null) {\n throw physics2DError(\n Physics2DErrorCode.bothPhysicsExtensions,\n \"A world uses either physics() or physics2d(), and this one registered both.\",\n {\n context: { extension: \"@ignifx/physics-2d\" },\n hint: \"Remove physics() from a 2D game, or physics2d() from a 3D one.\",\n },\n );\n }\n const settings = ctx.settings<Physics2DSettings>(PHYSICS_2D_SETTINGS_SECTION);\n const initialize = options.initialize ?? defaultInitialize;\n try {\n await initialize();\n } catch (cause: unknown) {\n throw physics2DError(\n Physics2DErrorCode.rapierUnavailable,\n \"The Rapier 2D WebAssembly module could not be instantiated.\",\n { cause, hint: \"Reinstall @dimforge/rapier2d-compat; its WebAssembly is inlined and needs no serving.\" },\n );\n }\n const world = createWorld(settings.gravity);\n const counters = app.diagnostics.registerGroup(PHYSICS_2D_DIAGNOSTICS_GROUP, PHYSICS_2D_DIAGNOSTICS_COUNTERS);\n host.runtime = new Physics2DRuntime({ ctx, settings, world, counters });\n seedExistingBodies(app, host);\n },\n\n dispose(): void {\n host.runtime?.dispose();\n host.runtime = null;\n context = null;\n },\n };\n },\n);\n\n/**\n * Instantiates Rapier's WebAssembly module, which is what `Physics2DOptions.initialize` replaces.\n *\n * @returns A promise that settles once Rapier is usable.\n */\nasync function defaultInitialize(): Promise<void> {\n await loadRapier();\n}\n\n/** Every component this extension registers, in a stable order. */\nconst PHYSICS_2D_COMPONENTS: readonly ConcreteComponentType[] = Object.freeze([\n Rigidbody2D,\n BoxCollider2D,\n CircleCollider2D,\n CapsuleCollider2D,\n PolygonCollider2D,\n EdgeCollider2D,\n TilemapCollider2D,\n CharacterController2D,\n]);\n\n/** The collider classes the seed pass walks, in registration order. */\nconst COLLIDER_2D_TYPES: readonly ComponentType<Collider2D>[] = Object.freeze([\n BoxCollider2D,\n CircleCollider2D,\n CapsuleCollider2D,\n PolygonCollider2D,\n EdgeCollider2D,\n TilemapCollider2D,\n]);\n\n/**\n * Marks every collider and controller that already exists when the runtime is built.\n *\n * @remarks\n * A component attached before `onStart` — from a scene the `assets.preload` list pulled in, say —\n * called `markDirty` while the runtime was still `null`. This is the catch-up pass that makes those\n * bodies exist, and it walks the component store in registration order so body creation order stays\n * deterministic (`09-physics.md` §8).\n *\n * @param app - The app being started.\n * @param host - The holder the runtime landed in.\n */\nfunction seedExistingBodies(app: App, host: Physics2DHost): void {\n const runtime = host.runtime;\n if (runtime === null) {\n return;\n }\n for (const type of COLLIDER_2D_TYPES) {\n const components = app.world.components(type);\n for (let index = 0; index < components.length; index += 1) {\n const collider = components[index];\n if (collider !== undefined) {\n runtime.markDirty(collider.entity);\n }\n }\n }\n const controllers = app.world.components(CharacterController2D);\n for (let index = 0; index < controllers.length; index += 1) {\n const controller = controllers[index];\n if (controller !== undefined) {\n runtime.markDirty(controller.entity);\n }\n }\n}\n","import { describeSchema } from \"@ignifx/core\";\nimport { CharacterController2D } from \"./components/character-controller.js\";\nimport {\n BoxCollider2D,\n CapsuleCollider2D,\n CircleCollider2D,\n EdgeCollider2D,\n PolygonCollider2D,\n} from \"./components/colliders.js\";\nimport { Rigidbody2D } from \"./components/rigidbody.js\";\nimport { TilemapCollider2D } from \"./components/tilemap-collider.js\";\nimport type { SchemaDescription } from \"@ignifx/core\";\n\n/**\n * The documentation harness's view of everything `@ignifx/physics-2d` declares\n * (`docs/architecture/16-docs-harness-and-skill.md` §3, `scripts/README.md`).\n *\n * The `ignifx.physicsmaterial` **file format** is deliberately absent: `@ignifx/physics` already\n * documents it and this package reads the very same document (`material.ts`), so describing it\n * twice would render the same table on the formats page twice — the harness keeps the first\n * declaration and drops the rest (`scripts/lib/schema-source.ts`).\n */\n\n/**\n * Describes every component this package declares.\n *\n * @returns The records, keyed by namespaced type id.\n *\n * @example\n * ```ts\n * const schemas = describeSchemas();\n * schemas[\"ignifx/Rigidbody2D\"].fields[\"mass\"].default; // 1\n * ```\n *\n * @public\n */\nexport function describeSchemas(): Readonly<Record<string, SchemaDescription>> {\n return {\n \"ignifx/Rigidbody2D\": describeSchema(\"ignifx/Rigidbody2D\", Rigidbody2D.schema, {\n description: \"Makes an entity's 2D colliders a dynamic, kinematic, or static Rapier body.\",\n }),\n \"ignifx/BoxCollider2D\": describeSchema(\"ignifx/BoxCollider2D\", BoxCollider2D.schema, {\n description: \"An axis-aligned box, sized in local metres and scaled by the entity.\",\n }),\n \"ignifx/CircleCollider2D\": describeSchema(\"ignifx/CircleCollider2D\", CircleCollider2D.schema, {\n description: \"A circle; the larger scale axis wins.\",\n }),\n \"ignifx/CapsuleCollider2D\": describeSchema(\"ignifx/CapsuleCollider2D\", CapsuleCollider2D.schema, {\n description: \"A capsule standing along X or Y.\",\n }),\n \"ignifx/PolygonCollider2D\": describeSchema(\"ignifx/PolygonCollider2D\", PolygonCollider2D.schema, {\n description: \"The convex hull of a point list, in local metres.\",\n }),\n \"ignifx/EdgeCollider2D\": describeSchema(\"ignifx/EdgeCollider2D\", EdgeCollider2D.schema, {\n description: \"An open chain of line segments — a platformer's ground contour.\",\n }),\n \"ignifx/TilemapCollider2D\": describeSchema(\"ignifx/TilemapCollider2D\", TilemapCollider2D.schema, {\n description: \"The merged collision surface of a Tilemap, rebuilt when its tiles change.\",\n }),\n \"ignifx/CharacterController2D\": describeSchema(\"ignifx/CharacterController2D\", CharacterController2D.schema, {\n description: \"A kinematic character with collide-and-slide, slopes, autostep, and snap-to-ground.\",\n }),\n };\n}\n"],"mappings":"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"}
|