@vworlds/vecs-physics 1.0.25 → 1.0.27

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.
Files changed (84) hide show
  1. package/adapter/{state.d.ts → engine.d.ts} +13 -10
  2. package/adapter/{state.js → engine.js} +23 -30
  3. package/adapter/engine.js.map +1 -0
  4. package/adapter/queries.d.ts +68 -0
  5. package/adapter/queries.js +145 -0
  6. package/adapter/queries.js.map +1 -0
  7. package/adapter/runtime.d.ts +4 -0
  8. package/adapter/runtime.js +20 -0
  9. package/adapter/runtime.js.map +1 -1
  10. package/components/body.js +5 -2
  11. package/components/body.js.map +1 -1
  12. package/components/geometry.d.ts +21 -6
  13. package/components/geometry.js +47 -9
  14. package/components/geometry.js.map +1 -1
  15. package/components/material.d.ts +5 -0
  16. package/components/material.js +5 -0
  17. package/components/material.js.map +1 -1
  18. package/index.d.ts +4 -2
  19. package/index.js +5 -3
  20. package/index.js.map +1 -1
  21. package/module.js +32 -19
  22. package/module.js.map +1 -1
  23. package/package.json +2 -2
  24. package/systems/chain_lifecycle.d.ts +6 -0
  25. package/systems/chain_lifecycle.js +102 -0
  26. package/systems/chain_lifecycle.js.map +1 -0
  27. package/systems/debug.d.ts +4 -5
  28. package/systems/debug.js +26 -24
  29. package/systems/debug.js.map +1 -1
  30. package/systems/event_opt_in_sync.d.ts +3 -3
  31. package/systems/event_opt_in_sync.js +53 -25
  32. package/systems/event_opt_in_sync.js.map +1 -1
  33. package/systems/events.d.ts +3 -3
  34. package/systems/events.js +36 -34
  35. package/systems/events.js.map +1 -1
  36. package/systems/forces.d.ts +3 -3
  37. package/systems/forces.js +30 -21
  38. package/systems/forces.js.map +1 -1
  39. package/systems/impulse_accumulate.d.ts +4 -4
  40. package/systems/impulse_accumulate.js +32 -28
  41. package/systems/impulse_accumulate.js.map +1 -1
  42. package/systems/impulse_zero.d.ts +3 -3
  43. package/systems/impulse_zero.js +6 -4
  44. package/systems/impulse_zero.js.map +1 -1
  45. package/systems/index.d.ts +3 -4
  46. package/systems/index.js +42 -48
  47. package/systems/index.js.map +1 -1
  48. package/systems/kinematic.d.ts +3 -3
  49. package/systems/kinematic.js +33 -33
  50. package/systems/kinematic.js.map +1 -1
  51. package/systems/lifecycle.d.ts +3 -3
  52. package/systems/lifecycle.js +58 -36
  53. package/systems/lifecycle.js.map +1 -1
  54. package/systems/mass_recompute.d.ts +3 -3
  55. package/systems/mass_recompute.js +21 -19
  56. package/systems/mass_recompute.js.map +1 -1
  57. package/systems/material_filter_sync.d.ts +3 -3
  58. package/systems/material_filter_sync.js +64 -29
  59. package/systems/material_filter_sync.js.map +1 -1
  60. package/systems/pose_sync_in.d.ts +3 -3
  61. package/systems/pose_sync_in.js +47 -20
  62. package/systems/pose_sync_in.js.map +1 -1
  63. package/systems/pose_sync_out.d.ts +3 -3
  64. package/systems/pose_sync_out.js +8 -6
  65. package/systems/pose_sync_out.js.map +1 -1
  66. package/systems/shape_lifecycle.d.ts +31 -4
  67. package/systems/shape_lifecycle.js +93 -145
  68. package/systems/shape_lifecycle.js.map +1 -1
  69. package/systems/shape_pose_sync.d.ts +3 -3
  70. package/systems/shape_pose_sync.js +35 -10
  71. package/systems/shape_pose_sync.js.map +1 -1
  72. package/systems/shape_predicates.d.ts +12 -0
  73. package/systems/shape_predicates.js +5 -0
  74. package/systems/shape_predicates.js.map +1 -0
  75. package/systems/step.d.ts +3 -3
  76. package/systems/step.js +7 -5
  77. package/systems/step.js.map +1 -1
  78. package/util/resolve_body.d.ts +2 -2
  79. package/util/resolve_body.js +5 -9
  80. package/util/resolve_body.js.map +1 -1
  81. package/util/warn.d.ts +1 -1
  82. package/util/warn.js +2 -1
  83. package/util/warn.js.map +1 -1
  84. package/adapter/state.js.map +0 -1
@@ -1,5 +1,6 @@
1
- import { ArrayMap, type EID, type Entity, type World } from "@vworlds/vecs";
1
+ import { ArrayMap, type EID, type World } from "@vworlds/vecs";
2
2
  import { type WarnSink, WarnDedupe } from "../util/warn.js";
3
+ import { type PhysicsQueries } from "./queries.js";
3
4
  import { type Box2DModule } from "./runtime.js";
4
5
  export type EntityId = EID;
5
6
  export type b2WorldId = ReturnType<Box2DModule["b2CreateWorld"]>;
@@ -7,6 +8,11 @@ export type b2BodyId = ReturnType<Box2DModule["b2CreateBody"]>;
7
8
  export type b2ShapeId = ReturnType<Box2DModule["b2CreateCircleShape"]>;
8
9
  export type b2ChainId = ReturnType<Box2DModule["b2CreateChain"]>;
9
10
  export type b2Vec2 = ReturnType<Box2DModule["b2Body_GetPosition"]>;
11
+ export type b2QueryFilter = Parameters<Box2DModule["b2World_OverlapAABB"]>[2];
12
+ export type b2AABB = Parameters<Box2DModule["b2World_OverlapAABB"]>[1];
13
+ /** The transient result object passed to a `b2World_CastRay` collector callback
14
+ * (the binding types the callback parameter as `any`). */
15
+ export type b2RayCallbackResult = InstanceType<Box2DModule["b2RayCallbackResult"]>;
10
16
  /** Fully-resolved engine configuration: every `PhysicsOptions` field with its
11
17
  * default applied. Built by `PhysicsModule`; the source of truth for tunables
12
18
  * the engine reads at runtime. */
@@ -33,10 +39,10 @@ export interface ResolvedPhysicsOptions {
33
39
  export declare class PhysicsSystemTag {
34
40
  }
35
41
  export declare function registerInternalComponents(world: World): void;
36
- export declare function attachPhysics(world: World, state: PhysicsState): void;
37
- export declare function getPhysics(world: World): PhysicsState;
38
- export declare function hasPhysics(world: World): boolean;
39
- export declare class PhysicsState {
42
+ export declare function attachPhysicsEngine(world: World, engine: PhysicsEngine): void;
43
+ export declare function getPhysicsEngine(world: World): PhysicsEngine;
44
+ export declare function hasPhysicsEngine(world: World): boolean;
45
+ export declare class PhysicsEngine {
40
46
  worldId: b2WorldId;
41
47
  readonly box2d: Box2DModule;
42
48
  readonly gravityX: number;
@@ -53,12 +59,8 @@ export declare class PhysicsState {
53
59
  readonly maximumLinearSpeed: number;
54
60
  readonly enableSleep: boolean;
55
61
  readonly enableContinuous: boolean;
56
- readonly bodyByEntityWasm: Map<number, import("node_modules/box2d3-wasm/build/dist/es/deluxe/Box2D.deluxe.js").b2BodyId>;
57
62
  readonly entityByBodyIndex: ArrayMap<number>;
58
- readonly shapeByEntityWasm: Map<number, import("node_modules/box2d3-wasm/build/dist/es/deluxe/Box2D.deluxe.js").b2ShapeId>;
59
63
  readonly entityByShapeIndex: ArrayMap<number>;
60
- readonly chainByEntity: Map<number, import("node_modules/box2d3-wasm/build/dist/es/deluxe/Box2D.deluxe.js").b2ChainId>;
61
- readonly shapeEntityByEid: Map<number, Entity>;
62
64
  readonly dirtyMassBodies: Set<number>;
63
65
  readonly prevTouchedContact: Set<number>;
64
66
  readonly prevTouchedSensor: Set<number>;
@@ -73,6 +75,7 @@ export declare class PhysicsState {
73
75
  value: number;
74
76
  wake: boolean;
75
77
  }>;
76
- readonly scratchVec2: b2Vec2;
78
+ queries?: PhysicsQueries;
79
+ constructor();
77
80
  constructor(box2d: Box2DModule, options: ResolvedPhysicsOptions, warnSink?: WarnSink);
78
81
  }
@@ -8,48 +8,41 @@ import { WarnDedupe } from "../util/warn.js";
8
8
  export class PhysicsSystemTag {
9
9
  }
10
10
  export function registerInternalComponents(world) {
11
- world.component(PhysicsRoot);
11
+ world.component(PhysicsEngine);
12
12
  world.component(PhysicsSystemTag);
13
13
  }
14
- export function attachPhysics(world, state) {
15
- world.component(PhysicsRoot).add(Singleton);
16
- world.set(PhysicsRoot, { state });
14
+ export function attachPhysicsEngine(world, engine) {
15
+ world.component(PhysicsEngine).add(Singleton);
16
+ world.component(PhysicsEngine).attach(PhysicsEngine, engine);
17
17
  }
18
- export function getPhysics(world) {
19
- const root = world.get(PhysicsRoot);
20
- if (!root) {
21
- throw new Error("world.module(PhysicsModule) must be called before reading physics state");
18
+ export function getPhysicsEngine(world) {
19
+ const engine = world.get(PhysicsEngine);
20
+ if (!engine) {
21
+ throw new Error("world.module(PhysicsModule) must be called before reading physics engine");
22
22
  }
23
- return root.state;
23
+ return engine;
24
24
  }
25
- export function hasPhysics(world) {
26
- if (!world._classRegistry.has(PhysicsRoot)) {
25
+ export function hasPhysicsEngine(world) {
26
+ if (!world._classRegistry.has(PhysicsEngine)) {
27
27
  return false;
28
28
  }
29
- const root = world.get(PhysicsRoot);
30
- return root?.state !== undefined;
29
+ return world.get(PhysicsEngine) !== undefined;
31
30
  }
32
- export class PhysicsState {
31
+ export class PhysicsEngine {
33
32
  constructor(box2d, options, warnSink) {
34
- // Entity WASM body/shape IDs plus index-keyed reverse ArrayMaps.
33
+ // Forward handles live on components: Body._bodyId, SingleShape._shapeId, and Chain._chainId.
34
+ // Index-keyed reverse ArrayMaps resolve Box2D event IDs back to entities.
35
35
  // Emscripten returns fresh wrapper objects for b2*Id fields on events, so
36
36
  // Map<b2*Id, EntityId> misses by object identity. Public index1 slot 0 is
37
37
  // unused/null-aligned by Box2D, so ArrayMap slot 0 stays empty too. We do not
38
38
  // check id generations in hot lookups: correctness comes from clearing each
39
39
  // slot on Box2D destruction before Box2D can reuse that index1.
40
- this.bodyByEntityWasm = new Map();
41
40
  this.entityByBodyIndex = new ArrayMap();
42
- this.shapeByEntityWasm = new Map();
43
41
  this.entityByShapeIndex = new ArrayMap();
44
- this.chainByEntity = new Map();
45
- // Shape entity object map: shape eid → Entity object.
46
- // Used by events.ts to resolve entities for teardown events even after
47
- // the entity has been removed from the world lookup table.
48
- this.shapeEntityByEid = new Map();
49
42
  // Bodies whose mass needs recomputing this step (populated by shape lifecycle).
50
43
  this.dirtyMassBodies = new Set();
51
44
  // Per-step event tracking sets for PhysicsEvents (M7-8 should-fix: moved from
52
- // closure-local to state so they survive future pause/resume cycles).
45
+ // closure-local to engine so they survive future pause/resume cycles).
53
46
  // Populated by PhysicsEvents at the end of each step; persist into the next
54
47
  // step to detect which entities stopped receiving events.
55
48
  this.prevTouchedContact = new Set();
@@ -60,10 +53,14 @@ export class PhysicsState {
60
53
  // Cleared by PhysicsImpulseZero at the end of each post phase.
61
54
  this.impulseEntitiesThisStep = new Set();
62
55
  // Pending impulses remain wrapper-keyed by invariant: hooks resolve bodyId
63
- // from bodyByEntityWasm, then get/set with that same stored wrapper, and the
64
- // pre-step drain iterates stored keys instead of looking up event wrappers.
56
+ // off the `Body` component via resolveBodyId, then get/set with that same
57
+ // stored wrapper, and the pre-step drain iterates stored keys instead of
58
+ // looking up event wrappers.
65
59
  this.pendingLinearImpulse = new Map();
66
60
  this.pendingAngularImpulse = new Map();
61
+ if (box2d === undefined || options === undefined) {
62
+ return;
63
+ }
67
64
  this.box2d = box2d;
68
65
  this.gravityX = options.gravityX;
69
66
  this.gravityY = options.gravityY;
@@ -79,10 +76,6 @@ export class PhysicsState {
79
76
  this.maximumLinearSpeed = options.maximumLinearSpeed;
80
77
  this.enableSleep = options.enableSleep;
81
78
  this.enableContinuous = options.enableContinuous;
82
- this.scratchVec2 = new box2d.b2Vec2();
83
79
  }
84
80
  }
85
- // Internal singleton state holder. Never exported from the package public API.
86
- class PhysicsRoot {
87
- }
88
- //# sourceMappingURL=state.js.map
81
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../../src/adapter/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { type Entity, type World } from "@vworlds/vecs";
2
+ import { type Vec2 } from "../components/geometry.js";
3
+ /** Category/mask filter applied to a query. Unlike a shape's own filter
4
+ * (default `categoryBits = 1`), a query's default `categoryBits` is
5
+ * `0xffffffff` so the query is not itself filtered out. */
6
+ export interface QueryFilter {
7
+ /** Bits this query "is". Default `0xffffffff` (matches every category). */
8
+ categoryBits?: number;
9
+ /** Bits this query "sees". Default `0xffffffff` (sees every category). */
10
+ maskBits?: number;
11
+ }
12
+ /** A single ray-cast hit, resolved to a Vecs shape entity with plain-data
13
+ * geometry — never a Box2D handle. */
14
+ export interface RayHit {
15
+ /** The shape entity that was hit. */
16
+ entity: Entity;
17
+ /** World-space hit point. */
18
+ point: Vec2;
19
+ /** World-space unit surface normal at the hit point. */
20
+ normal: Vec2;
21
+ /** Fraction along `from` → `to`, in `[0, 1]`. */
22
+ fraction: number;
23
+ }
24
+ /**
25
+ * Stable, world-scoped facade for synchronous read-only physics queries: ray
26
+ * casts and spatial overlaps. Obtained via {@link physics}. Never mutates the
27
+ * world, never creates or destroys entities, and never wakes bodies.
28
+ */
29
+ export interface PhysicsQueries {
30
+ /** Casts a ray from `from` to `to` and returns the closest hit, or `undefined`
31
+ * if the ray hits nothing (or only hits shapes whose entity has since been
32
+ * destroyed / removed). */
33
+ rayCastClosest(query: {
34
+ from: Vec2;
35
+ to: Vec2;
36
+ filter?: QueryFilter;
37
+ }): RayHit | undefined;
38
+ /** Casts a ray from `from` to `to` and returns every hit along the way. */
39
+ rayCastAll(query: {
40
+ from: Vec2;
41
+ to: Vec2;
42
+ filter?: QueryFilter;
43
+ }): RayHit[];
44
+ /** Returns every shape entity whose fat AABB overlaps `[min, max]`. This is
45
+ * a broad-phase query: it can over-report relative to exact shape
46
+ * overlap. */
47
+ overlapAABB(query: {
48
+ min: Vec2;
49
+ max: Vec2;
50
+ filter?: QueryFilter;
51
+ }): Entity[];
52
+ /** Returns every shape entity that precisely overlaps a circle centered at
53
+ * `center` with the given `radius`. */
54
+ overlapCircle(query: {
55
+ center: Vec2;
56
+ radius: number;
57
+ filter?: QueryFilter;
58
+ }): Entity[];
59
+ }
60
+ /**
61
+ * Returns the {@link PhysicsQueries} facade for `world`.
62
+ *
63
+ * Throws if `world.module(PhysicsModule)` has not been called (the error
64
+ * message comes from `getPhysicsEngine`). The returned facade is stable: repeated
65
+ * calls for the same world return the same instance, so it never
66
+ * re-allocates.
67
+ */
68
+ export declare function physics(world: World): PhysicsQueries;
@@ -0,0 +1,145 @@
1
+ // M8-3 — Query foundation: `physics(world)` accessor + `PhysicsQueries` facade.
2
+ //
3
+ // The ONE narrow imperative entry point vecs-physics exposes: world-scoped,
4
+ // read-only queries against the live Box2D world. Results are always Vecs
5
+ // entities (shape entities) and plain data — never Box2D handles.
6
+ //
7
+ // box2d3-wasm imports are confined to `src/adapter/`; this file is part of
8
+ // that boundary. Scratch allocations (b2Vec2, b2QueryFilter, b2AABB) reuse
9
+ // global pools allocated at module load. Transient allocations like b2RayResult
10
+ // are freed via explicit `finally { .delete() }`.
11
+ import { scr_b2AABB, scr_b2QueryFilter, scr_b2Vec2 } from "./runtime.js";
12
+ import { getPhysicsEngine, } from "./engine.js";
13
+ /**
14
+ * Returns the {@link PhysicsQueries} facade for `world`.
15
+ *
16
+ * Throws if `world.module(PhysicsModule)` has not been called (the error
17
+ * message comes from `getPhysicsEngine`). The returned facade is stable: repeated
18
+ * calls for the same world return the same instance, so it never
19
+ * re-allocates.
20
+ */
21
+ export function physics(world) {
22
+ const engine = getPhysicsEngine(world);
23
+ return (engine.queries ?? (engine.queries = new PhysicsQueriesImpl(world, engine)));
24
+ }
25
+ class PhysicsQueriesImpl {
26
+ constructor(world, engine) {
27
+ this.world = world;
28
+ this.engine = engine;
29
+ }
30
+ rayCastClosest(query) {
31
+ const { box2d, worldId } = this.engine;
32
+ const origin = scr_b2Vec2[0];
33
+ origin.Set(query.from.x, query.from.y);
34
+ const translation = scr_b2Vec2[1];
35
+ translation.Set(query.to.x - query.from.x, query.to.y - query.from.y);
36
+ const result = box2d.b2World_CastRayClosest(worldId, origin, translation, to_b2QueryFilter(query.filter));
37
+ try {
38
+ if (!result.hit) {
39
+ return undefined;
40
+ }
41
+ const entity = resolveShapeEntity(this.engine, this.world, result.shapeId);
42
+ if (entity === undefined) {
43
+ return undefined;
44
+ }
45
+ return {
46
+ entity,
47
+ point: { x: result.point.x, y: result.point.y },
48
+ normal: { x: result.normal.x, y: result.normal.y },
49
+ fraction: result.fraction,
50
+ };
51
+ }
52
+ finally {
53
+ result.delete();
54
+ }
55
+ }
56
+ rayCastAll(query) {
57
+ const { box2d, worldId } = this.engine;
58
+ const origin = scr_b2Vec2[0];
59
+ origin.Set(query.from.x, query.from.y);
60
+ const translation = scr_b2Vec2[1];
61
+ translation.Set(query.to.x - query.from.x, query.to.y - query.from.y);
62
+ const hits = [];
63
+ box2d.b2World_CastRay(worldId, origin, translation, to_b2QueryFilter(query.filter), (result) => {
64
+ const entity = resolveShapeEntity(this.engine, this.world, result.shapeId);
65
+ if (entity !== undefined) {
66
+ hits.push({
67
+ entity,
68
+ point: { x: result.point.x, y: result.point.y },
69
+ normal: { x: result.normal.x, y: result.normal.y },
70
+ fraction: result.fraction,
71
+ });
72
+ }
73
+ return 1;
74
+ });
75
+ return hits;
76
+ }
77
+ overlapAABB(query) {
78
+ const { box2d, worldId } = this.engine;
79
+ const aabb = scr_b2AABB;
80
+ aabb.lowerBound.Set(query.min.x, query.min.y);
81
+ aabb.upperBound.Set(query.max.x, query.max.y);
82
+ const entities = [];
83
+ const seen = new Set();
84
+ box2d.b2World_OverlapAABB(worldId, aabb, to_b2QueryFilter(query.filter), (result) => {
85
+ const entity = resolveShapeEntity(this.engine, this.world, result.shapeId);
86
+ if (entity !== undefined && !seen.has(entity.eid)) {
87
+ seen.add(entity.eid);
88
+ entities.push(entity);
89
+ }
90
+ return true;
91
+ });
92
+ return entities;
93
+ }
94
+ overlapCircle(query) {
95
+ const { box2d, worldId } = this.engine;
96
+ const centerVec = scr_b2Vec2[0];
97
+ centerVec.Set(query.center.x, query.center.y);
98
+ const proxy = box2d.b2MakeProxy(centerVec, 1, query.radius);
99
+ try {
100
+ const entities = [];
101
+ const seen = new Set();
102
+ box2d.b2World_OverlapShape(worldId, proxy, to_b2QueryFilter(query.filter), (result) => {
103
+ const entity = resolveShapeEntity(this.engine, this.world, result.shapeId);
104
+ if (entity !== undefined && !seen.has(entity.eid)) {
105
+ seen.add(entity.eid);
106
+ entities.push(entity);
107
+ }
108
+ return true;
109
+ });
110
+ return entities;
111
+ }
112
+ finally {
113
+ proxy.delete();
114
+ }
115
+ }
116
+ }
117
+ /** Maps a {@link QueryFilter} (default `0xffffffff`/`0xffffffff` — "sees and
118
+ * is seen by everything") and returns a scratch `b2QueryFilter`. `b2QueryFilter` is not
119
+ * `b2DefaultQueryFilter` here: the default-constructed filter is all zeros,
120
+ * which would match nothing.
121
+ * to_b2QueryFilter cannot be called again until the returned b2QueryFilter is no longer in use
122
+ * */
123
+ function to_b2QueryFilter(filter) {
124
+ const target = scr_b2QueryFilter[0];
125
+ target.categoryBits = filter?.categoryBits ?? 0xffffffff;
126
+ target.maskBits = filter?.maskBits ?? 0xffffffff;
127
+ return target;
128
+ }
129
+ /** Resolves a Box2D shape hit back to its Vecs shape entity, mirroring the
130
+ * `PhysicsEvents` shape→entity resolution (`src/systems/events.ts`):
131
+ * `entityByShapeIndex.get(shapeId.index1)` → `EID` → live `Entity`. Returns
132
+ * `undefined` (drop silently) if the slot is empty or the entity has been
133
+ * destroyed — the same rule as `PhysicsEvents`. */
134
+ function resolveShapeEntity(engine, world, shapeId) {
135
+ const eid = engine.entityByShapeIndex.get(shapeId.index1);
136
+ if (eid === undefined) {
137
+ return undefined;
138
+ }
139
+ const entity = world.getEntity(eid);
140
+ if (entity === undefined || entity.destroyed) {
141
+ return undefined;
142
+ }
143
+ return entity;
144
+ }
145
+ //# sourceMappingURL=queries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../../src/adapter/queries.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,8 @@
1
1
  import Box2DFactory from "box2d3-wasm";
2
+ import type { b2AABB, b2QueryFilter, b2Vec2 } from "./engine.js";
2
3
  export type Box2DModule = Awaited<ReturnType<typeof Box2DFactory>>;
4
+ export declare const scr_b2Vec2: b2Vec2[];
5
+ export declare const scr_b2QueryFilter: b2QueryFilter[];
6
+ export declare let scr_b2AABB: b2AABB | null;
3
7
  export declare function preloadPhysics(): Promise<void>;
4
8
  export declare function getBox2D(): Box2DModule;
@@ -2,6 +2,11 @@ import { cpus } from "node:os";
2
2
  import Box2DFactory from "box2d3-wasm";
3
3
  let modulePromise = null;
4
4
  let module = null;
5
+ // Global scratch pools for common Box2D structs. Allocated once at module load,
6
+ // reused across queries and other non-reentrant operations.
7
+ export const scr_b2Vec2 = [];
8
+ export const scr_b2QueryFilter = [];
9
+ export let scr_b2AABB = null;
5
10
  export async function preloadPhysics() {
6
11
  var _a;
7
12
  if (modulePromise) {
@@ -14,10 +19,25 @@ export async function preloadPhysics() {
14
19
  (_a = global.navigator).hardwareConcurrency ?? (_a.hardwareConcurrency = cpus().length);
15
20
  modulePromise = Box2DFactory().then((loadedModule) => {
16
21
  module = loadedModule;
22
+ initializeScratchPools(loadedModule);
17
23
  return loadedModule;
18
24
  });
19
25
  await modulePromise;
20
26
  }
27
+ function initializeScratchPools(box2d) {
28
+ // b2Vec2: ray casts need 2 simultaneous (origin + translation), polygon/chain creation
29
+ // can reuse serially. Allocate 3 for safety margin.
30
+ for (let i = 0; i < 3; i++) {
31
+ scr_b2Vec2.push(new box2d.b2Vec2());
32
+ }
33
+ // b2QueryFilter: queries and material_filter_sync each need at most 1 simultaneously.
34
+ // Allocate 2 for safety.
35
+ for (let i = 0; i < 2; i++) {
36
+ scr_b2QueryFilter.push(new box2d.b2QueryFilter());
37
+ }
38
+ // b2AABB: overlapAABB needs exactly 1.
39
+ scr_b2AABB = new box2d.b2AABB();
40
+ }
21
41
  export function getBox2D() {
22
42
  if (!module) {
23
43
  throw new Error("Box2D module not loaded. Call `await preloadPhysics()` before `world.module(PhysicsModule)`.");
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../../src/adapter/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,YAAY,MAAM,aAAa,CAAC;AAIvC,IAAI,aAAa,GAAgC,IAAI,CAAC;AACtD,IAAI,MAAM,GAAuB,IAAI,CAAC;AAEtC,MAAM,CAAC,KAAK,UAAU,cAAc;;IAClC,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,aAAa,CAAC;QACpB,OAAO;IACT,CAAC;IAED,8EAA8E;IAC9E,MAAM,MAAM,GAAG,UAA2D,CAAC;IAC3E,MAAM,CAAC,SAAS,KAAhB,MAAM,CAAC,SAAS,GAAK,EAAE,EAAC;IACxB,MAAC,MAAM,CAAC,SAA8C,EAAC,mBAAmB,QAAnB,mBAAmB,GAAK,IAAI,EAAE,CAAC,MAAM,EAAC;IAE7F,aAAa,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE;QACnD,MAAM,GAAG,YAAY,CAAC;QACtB,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,MAAM,aAAa,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../../src/adapter/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,YAAY,MAAM,aAAa,CAAC;AAMvC,IAAI,aAAa,GAAgC,IAAI,CAAC;AACtD,IAAI,MAAM,GAAuB,IAAI,CAAC;AAEtC,gFAAgF;AAChF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,UAAU,GAAa,EAAE,CAAC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAAoB,EAAE,CAAC;AACrD,MAAM,CAAC,IAAI,UAAU,GAAkB,IAAI,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,cAAc;;IAClC,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,aAAa,CAAC;QACpB,OAAO;IACT,CAAC;IAED,8EAA8E;IAC9E,MAAM,MAAM,GAAG,UAA2D,CAAC;IAC3E,MAAM,CAAC,SAAS,KAAhB,MAAM,CAAC,SAAS,GAAK,EAAE,EAAC;IACxB,MAAC,MAAM,CAAC,SAA8C,EAAC,mBAAmB,QAAnB,mBAAmB,GAAK,IAAI,EAAE,CAAC,MAAM,EAAC;IAE7F,aAAa,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE;QACnD,MAAM,GAAG,YAAY,CAAC;QACtB,sBAAsB,CAAC,YAAY,CAAC,CAAC;QACrC,OAAO,YAAY,CAAC;IACtB,CAAC,CAAC,CAAC;IACH,MAAM,aAAa,CAAC;AACtB,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAkB;IAChD,uFAAuF;IACvF,oDAAoD;IACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,sFAAsF;IACtF,yBAAyB;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,iBAAiB,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,uCAAuC;IACvC,UAAU,GAAG,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,10 +1,11 @@
1
1
  // Body-level components: the entity-marking `Body` plus the canonical pose and
2
- // velocity state. Defaults match Box2D's `b2DefaultBodyDef()` (see
2
+ // velocity data. Defaults match Box2D's `b2DefaultBodyDef()` (see
3
3
  // `box2d/src/types.c`). Public API — see `docs/components.md`.
4
4
  //
5
5
  // These are plain data classes: every field is initialised on the class body so
6
6
  // `new C()` yields a valid, default-shaped instance (the Vecs zero-arg
7
- // contract). No imports from systems/ or adapter/ components are pure data.
7
+ // contract). No imports from systems/ or adapter/ beyond type-only `b2*Id`
8
+ // handle types — components are pure data.
8
9
  /** Simulation behaviour of a {@link Body}. */
9
10
  export var BodyType;
10
11
  (function (BodyType) {
@@ -22,6 +23,8 @@ export class Body {
22
23
  this.bullet = false;
23
24
  this.enabled = true;
24
25
  this.allowFastRotation = false;
26
+ /** @internal Engine-owned box2d handle; set by the lifecycle system, undefined until then. */
27
+ this._bodyId = undefined;
25
28
  }
26
29
  }
27
30
  /** Canonical world-space body position. Teleports when written by user code. */
@@ -1 +1 @@
1
- {"version":3,"file":"body.js","sourceRoot":"","sources":["../../../../src/components/body.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,+DAA+D;AAC/D,EAAE;AACF,gFAAgF;AAChF,uEAAuE;AACvE,8EAA8E;AAE9E,8CAA8C;AAC9C,MAAM,CAAN,IAAY,QAOX;AAPD,WAAY,QAAQ;IAClB,gCAAgC;IAChC,6BAAiB,CAAA;IACjB,2EAA2E;IAC3E,mCAAuB,CAAA;IACvB,2DAA2D;IAC3D,+BAAmB,CAAA;AACrB,CAAC,EAPW,QAAQ,KAAR,QAAQ,QAOnB;AAED,kFAAkF;AAClF,MAAM,OAAO,IAAI;IAAjB;QACE,SAAI,GAAG,QAAQ,CAAC,MAAM,CAAC;QACvB,WAAM,GAAG,KAAK,CAAC;QACf,YAAO,GAAG,IAAI,CAAC;QACf,sBAAiB,GAAG,KAAK,CAAC;IAC5B,CAAC;CAAA;AAED,gFAAgF;AAChF,MAAM,OAAO,QAAQ;IAArB;QACE,MAAC,GAAG,CAAC,CAAC;QACN,MAAC,GAAG,CAAC,CAAC;IACR,CAAC;CAAA;AAED,8EAA8E;AAC9E,MAAM,OAAO,QAAQ;IAArB;QACE,UAAK,GAAG,CAAC,CAAC;IACZ,CAAC;CAAA;AAED,gFAAgF;AAChF,MAAM,OAAO,cAAc;IAA3B;QACE,MAAC,GAAG,CAAC,CAAC;QACN,MAAC,GAAG,CAAC,CAAC;IACR,CAAC;CAAA;AAED,mDAAmD;AACnD,MAAM,OAAO,eAAe;IAA5B;QACE,UAAK,GAAG,CAAC,CAAC;IACZ,CAAC;CAAA"}
1
+ {"version":3,"file":"body.js","sourceRoot":"","sources":["../../../../src/components/body.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kEAAkE;AAClE,+DAA+D;AAC/D,EAAE;AACF,gFAAgF;AAChF,uEAAuE;AACvE,2EAA2E;AAC3E,2CAA2C;AAI3C,8CAA8C;AAC9C,MAAM,CAAN,IAAY,QAOX;AAPD,WAAY,QAAQ;IAClB,gCAAgC;IAChC,6BAAiB,CAAA;IACjB,2EAA2E;IAC3E,mCAAuB,CAAA;IACvB,2DAA2D;IAC3D,+BAAmB,CAAA;AACrB,CAAC,EAPW,QAAQ,KAAR,QAAQ,QAOnB;AAED,kFAAkF;AAClF,MAAM,OAAO,IAAI;IAAjB;QACE,SAAI,GAAG,QAAQ,CAAC,MAAM,CAAC;QACvB,WAAM,GAAG,KAAK,CAAC;QACf,YAAO,GAAG,IAAI,CAAC;QACf,sBAAiB,GAAG,KAAK,CAAC;QAC1B,8FAA8F;QAC9F,YAAO,GAAyB,SAAS,CAAC;IAC5C,CAAC;CAAA;AAED,gFAAgF;AAChF,MAAM,OAAO,QAAQ;IAArB;QACE,MAAC,GAAG,CAAC,CAAC;QACN,MAAC,GAAG,CAAC,CAAC;IACR,CAAC;CAAA;AAED,8EAA8E;AAC9E,MAAM,OAAO,QAAQ;IAArB;QACE,UAAK,GAAG,CAAC,CAAC;IACZ,CAAC;CAAA;AAED,gFAAgF;AAChF,MAAM,OAAO,cAAc;IAA3B;QACE,MAAC,GAAG,CAAC,CAAC;QACN,MAAC,GAAG,CAAC,CAAC;IACR,CAAC;CAAA;AAED,mDAAmD;AACnD,MAAM,OAAO,eAAe;IAA5B;QACE,UAAK,GAAG,CAAC,CAAC;IACZ,CAAC;CAAA"}
@@ -3,6 +3,21 @@ export type Vec2 = {
3
3
  x: number;
4
4
  y: number;
5
5
  };
6
+ /**
7
+ * Empty umbrella marker for the six geometry components. Every shape entity carries
8
+ * exactly one concrete geometry (`Box`, `Circle`, `Capsule`, `Segment`,
9
+ * `Polygon`, or `Chain`); each implements `Shape` so queries and systems can
10
+ * match any geometry without enumerating every concrete type.
11
+ */
12
+ export declare abstract class Shape {
13
+ }
14
+ /**
15
+ * Internal marker for geometries backed by one Box2D `b2ShapeId`. Chains are
16
+ * intentionally excluded because Box2D creates them through `b2ChainId` and
17
+ * exposes their collision segments separately.
18
+ */
19
+ export declare abstract class SingleShape extends Shape {
20
+ }
6
21
  /**
7
22
  * Axis-aligned box in local shape space, stored as half extents (`hx`/`hy`) to
8
23
  * match Box2D's internal representation. The `width`/`height` accessors are
@@ -12,7 +27,7 @@ export type Vec2 = {
12
27
  * still go through `entity.set(Box, …)` or `entity.modified(Box)` for physics
13
28
  * to notice. See PLAN → Box accessors.
14
29
  */
15
- export declare class Box {
30
+ export declare class Box extends SingleShape {
16
31
  hx: number;
17
32
  hy: number;
18
33
  get width(): number;
@@ -21,11 +36,11 @@ export declare class Box {
21
36
  set height(value: number);
22
37
  }
23
38
  /** Circle geometry. */
24
- export declare class Circle {
39
+ export declare class Circle extends SingleShape {
25
40
  radius: number;
26
41
  }
27
42
  /** Capsule geometry: two local endpoints joined by a radius. */
28
- export declare class Capsule {
43
+ export declare class Capsule extends SingleShape {
29
44
  x1: number;
30
45
  y1: number;
31
46
  x2: number;
@@ -33,7 +48,7 @@ export declare class Capsule {
33
48
  radius: number;
34
49
  }
35
50
  /** Line-segment geometry. */
36
- export declare class Segment {
51
+ export declare class Segment extends SingleShape {
37
52
  x1: number;
38
53
  y1: number;
39
54
  x2: number;
@@ -44,12 +59,12 @@ export declare class Segment {
44
59
  * Convexity and vertex count are validated on shape creation, not on every
45
60
  * update. See PLAN → Vec2 in Polygon / Chain.
46
61
  */
47
- export declare class Polygon {
62
+ export declare class Polygon extends SingleShape {
48
63
  vertices: Vec2[];
49
64
  radius: number;
50
65
  }
51
66
  /** Chain geometry for static terrain. Needs at least four points. */
52
- export declare class Chain {
67
+ export declare class Chain extends Shape {
53
68
  points: Vec2[];
54
69
  loop: boolean;
55
70
  }
@@ -1,6 +1,34 @@
1
- // Geometry components. Exactly one per shape entity; `PhysicsModule` marks them
2
- // mutually exclusive so adding one removes any previous geometry. Defaults match
3
- // the documented shapes. Public API see `docs/components.md`.
1
+ // Geometry components. Exactly one per shape entity; every concrete geometry
2
+ // implements the `Shape` umbrella so `PhysicsModule` enforces mutual exclusivity
3
+ // (setting one evicts any previous geometry). Single Box2D shapes also implement
4
+ // the internal `SingleShape` tier; `Chain` is separate because Box2D exposes it
5
+ // through `b2ChainId` plus underlying segment shapes.
6
+ // Public API — see `docs/components.md`.
7
+ //
8
+ // These are plain data classes: every field is initialised on the class body so
9
+ // `new C()` yields a valid, default-shaped instance (the Vecs zero-arg
10
+ // contract). No imports from systems/ or adapter/ beyond type-only `b2*Id`
11
+ // handle types — components are pure data.
12
+ /**
13
+ * Empty umbrella marker for the six geometry components. Every shape entity carries
14
+ * exactly one concrete geometry (`Box`, `Circle`, `Capsule`, `Segment`,
15
+ * `Polygon`, or `Chain`); each implements `Shape` so queries and systems can
16
+ * match any geometry without enumerating every concrete type.
17
+ */
18
+ export class Shape {
19
+ }
20
+ /**
21
+ * Internal marker for geometries backed by one Box2D `b2ShapeId`. Chains are
22
+ * intentionally excluded because Box2D creates them through `b2ChainId` and
23
+ * exposes their collision segments separately.
24
+ */
25
+ export class SingleShape extends Shape {
26
+ constructor() {
27
+ super(...arguments);
28
+ /** @internal Engine-owned Box2D shape handle. Set by the single-shape lifecycle system. */
29
+ this._shapeId = undefined;
30
+ }
31
+ }
4
32
  /**
5
33
  * Axis-aligned box in local shape space, stored as half extents (`hx`/`hy`) to
6
34
  * match Box2D's internal representation. The `width`/`height` accessors are
@@ -10,8 +38,9 @@
10
38
  * still go through `entity.set(Box, …)` or `entity.modified(Box)` for physics
11
39
  * to notice. See PLAN → Box accessors.
12
40
  */
13
- export class Box {
41
+ export class Box extends SingleShape {
14
42
  constructor() {
43
+ super(...arguments);
15
44
  this.hx = 0.5;
16
45
  this.hy = 0.5;
17
46
  }
@@ -29,14 +58,16 @@ export class Box {
29
58
  }
30
59
  }
31
60
  /** Circle geometry. */
32
- export class Circle {
61
+ export class Circle extends SingleShape {
33
62
  constructor() {
63
+ super(...arguments);
34
64
  this.radius = 0.5;
35
65
  }
36
66
  }
37
67
  /** Capsule geometry: two local endpoints joined by a radius. */
38
- export class Capsule {
68
+ export class Capsule extends SingleShape {
39
69
  constructor() {
70
+ super(...arguments);
40
71
  this.x1 = -0.5;
41
72
  this.y1 = 0;
42
73
  this.x2 = 0.5;
@@ -45,8 +76,9 @@ export class Capsule {
45
76
  }
46
77
  }
47
78
  /** Line-segment geometry. */
48
- export class Segment {
79
+ export class Segment extends SingleShape {
49
80
  constructor() {
81
+ super(...arguments);
50
82
  this.x1 = -0.5;
51
83
  this.y1 = 0;
52
84
  this.x2 = 0.5;
@@ -58,15 +90,21 @@ export class Segment {
58
90
  * Convexity and vertex count are validated on shape creation, not on every
59
91
  * update. See PLAN → Vec2 in Polygon / Chain.
60
92
  */
61
- export class Polygon {
93
+ export class Polygon extends SingleShape {
62
94
  constructor() {
95
+ super(...arguments);
63
96
  this.vertices = [];
64
97
  this.radius = 0;
65
98
  }
66
99
  }
67
100
  /** Chain geometry for static terrain. Needs at least four points. */
68
- export class Chain {
101
+ export class Chain extends Shape {
69
102
  constructor() {
103
+ super(...arguments);
104
+ /** @internal Engine-owned Box2D chain handle. Set by the chain lifecycle system. */
105
+ this._chainId = undefined;
106
+ /** @internal Public shape indices of the chain's generated segment shapes. */
107
+ this._segmentIndices = [];
70
108
  this.points = [];
71
109
  this.loop = false;
72
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../../../src/components/geometry.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,gEAAgE;AAKhE;;;;;;;;GAQG;AACH,MAAM,OAAO,GAAG;IAAhB;QACE,OAAE,GAAG,GAAG,CAAC;QACT,OAAE,GAAG,GAAG,CAAC;IAiBX,CAAC;IAfC,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,KAAK,CAAC,KAAa;QACrB,IAAI,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,MAAM,CAAC,KAAa;QACtB,IAAI,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC;IACtB,CAAC;CACF;AAED,uBAAuB;AACvB,MAAM,OAAO,MAAM;IAAnB;QACE,WAAM,GAAG,GAAG,CAAC;IACf,CAAC;CAAA;AAED,gEAAgE;AAChE,MAAM,OAAO,OAAO;IAApB;QACE,OAAE,GAAG,CAAC,GAAG,CAAC;QACV,OAAE,GAAG,CAAC,CAAC;QACP,OAAE,GAAG,GAAG,CAAC;QACT,OAAE,GAAG,CAAC,CAAC;QACP,WAAM,GAAG,IAAI,CAAC;IAChB,CAAC;CAAA;AAED,6BAA6B;AAC7B,MAAM,OAAO,OAAO;IAApB;QACE,OAAE,GAAG,CAAC,GAAG,CAAC;QACV,OAAE,GAAG,CAAC,CAAC;QACP,OAAE,GAAG,GAAG,CAAC;QACT,OAAE,GAAG,CAAC,CAAC;IACT,CAAC;CAAA;AAED;;;;GAIG;AACH,MAAM,OAAO,OAAO;IAApB;QACE,aAAQ,GAAW,EAAE,CAAC;QACtB,WAAM,GAAG,CAAC,CAAC;IACb,CAAC;CAAA;AAED,qEAAqE;AACrE,MAAM,OAAO,KAAK;IAAlB;QACE,WAAM,GAAW,EAAE,CAAC;QACpB,SAAI,GAAG,KAAK,CAAC;IACf,CAAC;CAAA"}
1
+ {"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../../../src/components/geometry.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,iFAAiF;AACjF,iFAAiF;AACjF,gFAAgF;AAChF,sDAAsD;AACtD,yCAAyC;AACzC,EAAE;AACF,gFAAgF;AAChF,uEAAuE;AACvE,2EAA2E;AAC3E,2CAA2C;AAO3C;;;;;GAKG;AACH,MAAM,OAAgB,KAAK;CAAG;AAE9B;;;;GAIG;AACH,MAAM,OAAgB,WAAY,SAAQ,KAAK;IAA/C;;QACE,2FAA2F;QAC3F,aAAQ,GAA0B,SAAS,CAAC;IAC9C,CAAC;CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,GAAI,SAAQ,WAAW;IAApC;;QACE,OAAE,GAAG,GAAG,CAAC;QACT,OAAE,GAAG,GAAG,CAAC;IAiBX,CAAC;IAfC,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,KAAK,CAAC,KAAa;QACrB,IAAI,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,MAAM,CAAC,KAAa;QACtB,IAAI,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC,CAAC;IACtB,CAAC;CACF;AAED,uBAAuB;AACvB,MAAM,OAAO,MAAO,SAAQ,WAAW;IAAvC;;QACE,WAAM,GAAG,GAAG,CAAC;IACf,CAAC;CAAA;AAED,gEAAgE;AAChE,MAAM,OAAO,OAAQ,SAAQ,WAAW;IAAxC;;QACE,OAAE,GAAG,CAAC,GAAG,CAAC;QACV,OAAE,GAAG,CAAC,CAAC;QACP,OAAE,GAAG,GAAG,CAAC;QACT,OAAE,GAAG,CAAC,CAAC;QACP,WAAM,GAAG,IAAI,CAAC;IAChB,CAAC;CAAA;AAED,6BAA6B;AAC7B,MAAM,OAAO,OAAQ,SAAQ,WAAW;IAAxC;;QACE,OAAE,GAAG,CAAC,GAAG,CAAC;QACV,OAAE,GAAG,CAAC,CAAC;QACP,OAAE,GAAG,GAAG,CAAC;QACT,OAAE,GAAG,CAAC,CAAC;IACT,CAAC;CAAA;AAED;;;;GAIG;AACH,MAAM,OAAO,OAAQ,SAAQ,WAAW;IAAxC;;QACE,aAAQ,GAAW,EAAE,CAAC;QACtB,WAAM,GAAG,CAAC,CAAC;IACb,CAAC;CAAA;AAED,qEAAqE;AACrE,MAAM,OAAO,KAAM,SAAQ,KAAK;IAAhC;;QACE,oFAAoF;QACpF,aAAQ,GAA0B,SAAS,CAAC;QAC5C,8EAA8E;QAC9E,oBAAe,GAAa,EAAE,CAAC;QAC/B,WAAM,GAAW,EAAE,CAAC;QACpB,SAAI,GAAG,KAAK,CAAC;IACf,CAAC;CAAA"}