@datamoc/mw_games 0.36.1 → 0.37.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +6 -2
  3. package/api-surface.json +234 -0
  4. package/dist/assets/paths.d.ts.map +1 -1
  5. package/dist/assets/paths.js +3 -0
  6. package/dist/assets/paths.js.map +1 -1
  7. package/dist/audio/Playable.d.ts.map +1 -1
  8. package/dist/audio/Playable.js +14 -1
  9. package/dist/audio/Playable.js.map +1 -1
  10. package/dist/audio/Synth.d.ts +14 -0
  11. package/dist/audio/Synth.d.ts.map +1 -1
  12. package/dist/audio/Synth.js +16 -0
  13. package/dist/audio/Synth.js.map +1 -1
  14. package/dist/audio/index.d.ts +1 -1
  15. package/dist/audio/index.d.ts.map +1 -1
  16. package/dist/audio/index.js +1 -1
  17. package/dist/audio/index.js.map +1 -1
  18. package/dist/core/FixedStepper.d.ts +38 -0
  19. package/dist/core/FixedStepper.d.ts.map +1 -0
  20. package/dist/core/FixedStepper.js +56 -0
  21. package/dist/core/FixedStepper.js.map +1 -0
  22. package/dist/core/Input.d.ts +61 -0
  23. package/dist/core/Input.d.ts.map +1 -1
  24. package/dist/core/Input.js +101 -0
  25. package/dist/core/Input.js.map +1 -1
  26. package/dist/core/StoredValue.d.ts +11 -2
  27. package/dist/core/StoredValue.d.ts.map +1 -1
  28. package/dist/core/StoredValue.js +11 -2
  29. package/dist/core/StoredValue.js.map +1 -1
  30. package/dist/core/index.d.ts +3 -1
  31. package/dist/core/index.d.ts.map +1 -1
  32. package/dist/core/index.js +3 -1
  33. package/dist/core/index.js.map +1 -1
  34. package/dist/mw_games.global.js +121 -121
  35. package/dist/physics/PhysicsWorld.d.ts +123 -0
  36. package/dist/physics/PhysicsWorld.d.ts.map +1 -0
  37. package/dist/physics/PhysicsWorld.js +137 -0
  38. package/dist/physics/PhysicsWorld.js.map +1 -0
  39. package/dist/physics/XpbdWorld.d.ts +139 -0
  40. package/dist/physics/XpbdWorld.d.ts.map +1 -0
  41. package/dist/physics/XpbdWorld.js +207 -0
  42. package/dist/physics/XpbdWorld.js.map +1 -0
  43. package/dist/physics/index.d.ts +3 -0
  44. package/dist/physics/index.d.ts.map +1 -0
  45. package/dist/physics/index.js +2 -0
  46. package/dist/physics/index.js.map +1 -0
  47. package/dist/physics/xpbd.d.ts +3 -0
  48. package/dist/physics/xpbd.d.ts.map +1 -0
  49. package/dist/physics/xpbd.js +2 -0
  50. package/dist/physics/xpbd.js.map +1 -0
  51. package/dist/two-d/render/Camera.d.ts +6 -0
  52. package/dist/two-d/render/Camera.d.ts.map +1 -1
  53. package/dist/two-d/render/Camera.js +9 -0
  54. package/dist/two-d/render/Camera.js.map +1 -1
  55. package/dist/two-d/render/Particles.d.ts +10 -1
  56. package/dist/two-d/render/Particles.d.ts.map +1 -1
  57. package/dist/two-d/render/Particles.js +14 -7
  58. package/dist/two-d/render/Particles.js.map +1 -1
  59. package/dist/two-d/render/Shape2D.d.ts +7 -0
  60. package/dist/two-d/render/Shape2D.d.ts.map +1 -1
  61. package/dist/two-d/render/Shape2D.js +18 -0
  62. package/dist/two-d/render/Shape2D.js.map +1 -1
  63. package/dist/two-d/render/SpriteSheet.d.ts +5 -0
  64. package/dist/two-d/render/SpriteSheet.d.ts.map +1 -1
  65. package/dist/two-d/render/SpriteSheet.js +15 -0
  66. package/dist/two-d/render/SpriteSheet.js.map +1 -1
  67. package/dist/two-d/ui/Button.d.ts +7 -0
  68. package/dist/two-d/ui/Button.d.ts.map +1 -1
  69. package/dist/two-d/ui/Button.js +16 -2
  70. package/dist/two-d/ui/Button.js.map +1 -1
  71. package/dist/two-d/ui/Dropdown.d.ts +3 -0
  72. package/dist/two-d/ui/Dropdown.d.ts.map +1 -1
  73. package/dist/two-d/ui/Dropdown.js +3 -0
  74. package/dist/two-d/ui/Dropdown.js.map +1 -1
  75. package/dist/two-d/ui/DropdownView.d.ts +40 -0
  76. package/dist/two-d/ui/DropdownView.d.ts.map +1 -0
  77. package/dist/two-d/ui/DropdownView.js +75 -0
  78. package/dist/two-d/ui/DropdownView.js.map +1 -0
  79. package/dist/two-d/ui/Label.d.ts +2 -0
  80. package/dist/two-d/ui/Label.d.ts.map +1 -1
  81. package/dist/two-d/ui/Label.js +7 -0
  82. package/dist/two-d/ui/Label.js.map +1 -1
  83. package/dist/two-d/ui/Layout.d.ts +34 -0
  84. package/dist/two-d/ui/Layout.d.ts.map +1 -1
  85. package/dist/two-d/ui/Layout.js +47 -0
  86. package/dist/two-d/ui/Layout.js.map +1 -1
  87. package/dist/two-d/ui/Window.d.ts +7 -0
  88. package/dist/two-d/ui/Window.d.ts.map +1 -1
  89. package/dist/two-d/ui/Window.js +13 -0
  90. package/dist/two-d/ui/Window.js.map +1 -1
  91. package/dist/two-d/ui/index.d.ts +4 -2
  92. package/dist/two-d/ui/index.d.ts.map +1 -1
  93. package/dist/two-d/ui/index.js +2 -1
  94. package/dist/two-d/ui/index.js.map +1 -1
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/package.json +21 -2
  98. package/sbom.cdx.json +58 -6
  99. package/tools/browser-driver.d.mts +14 -0
  100. package/tools/browser-driver.mjs +62 -0
  101. package/tools/build-tablebases.mjs +6 -2
  102. package/tools/classic-html.mjs +9 -1
  103. package/tools/compile-openings.mjs +6 -2
  104. package/tools/compile-resources.d.mts +5 -0
  105. package/tools/compile-resources.mjs +45 -4
  106. package/tools/compress-dist.mjs +15 -3
  107. package/tools/emit-page.mjs +5 -1
  108. package/tools/init.d.mts +5 -0
  109. package/tools/init.mjs +145 -0
  110. package/tools/play-uci.mjs +6 -3
  111. package/tools/sbom.mjs +5 -1
  112. package/tools/single-file.mjs +9 -1
  113. package/tools/vite.mjs +5 -0
  114. package/tools/webp-convert.mjs +9 -1
@@ -0,0 +1,123 @@
1
+ import Matter from 'matter-js';
2
+ import { Signal } from '../core/Signal.ts';
3
+ export interface PhysicsOptions {
4
+ /** gravity strength per axis; y grows downward. { x: 0, y: 9.8 } (the default) matches matter-js's own default pull */
5
+ gravity?: {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ /** the fixed simulation step in seconds; 1/60 when omitted */
10
+ step?: number;
11
+ /** steps one `update` may run before it drops the backlog; 5 when omitted */
12
+ maxSubsteps?: number;
13
+ }
14
+ export interface BodyOptions {
15
+ /** an immovable body: ground, anchors */
16
+ isStatic?: boolean;
17
+ /** 0 to 1, how much of its speed a bounce keeps; 0 when omitted */
18
+ restitution?: number;
19
+ friction?: number;
20
+ /** mass per unit area; matter-js's default when omitted */
21
+ density?: number;
22
+ angle?: number;
23
+ /** a name for collision callbacks and lookups */
24
+ label?: string;
25
+ }
26
+ export interface ConstraintOptions {
27
+ /** 0 to 1, how rigidly the ends are held: 1 is a rod, a small value a spring; 1 when omitted */
28
+ stiffness?: number;
29
+ damping?: number;
30
+ /** rest length; the distance between the attachment points when omitted */
31
+ length?: number;
32
+ /** attachment points in each body's local space; the body centre when omitted */
33
+ pointA?: {
34
+ x: number;
35
+ y: number;
36
+ };
37
+ pointB?: {
38
+ x: number;
39
+ y: number;
40
+ };
41
+ /**
42
+ * Makes the joint breakable: once its two ends are further apart than the rest length times
43
+ * `1 + maxStretch`, the constraint is removed and `onBreak` fires. 0.2 lets a beam stretch 20%
44
+ * before it snaps. For a zero-length joint (a pin) it is the absolute distance instead.
45
+ * The strain is read after each step's solve, so a fully rigid joint (`stiffness` 1) is pulled
46
+ * back before it is measured under a light load: use a stiffness below 1, as a real beam has.
47
+ * Unbreakable when omitted.
48
+ */
49
+ maxStretch?: number;
50
+ }
51
+ export type PhysicsBody = Matter.Body;
52
+ export type PhysicsConstraint = Matter.Constraint;
53
+ /**
54
+ * Rigid bodies and constraints through matter-js, stepped on a fixed clock.
55
+ *
56
+ * The adapter is the framework side of using matter-js in a mwg game: it owns the `Engine`,
57
+ * steps it with a `FixedStepper` so the simulation is the same at any display refresh rate,
58
+ * and reports contacts through a `Signal`, while leaving the bodies themselves as plain
59
+ * matter-js objects a game reads `position` and `angle` from to place its sprites. It is
60
+ * renderer-free, so a game's headless tests can run the same world.
61
+ *
62
+ * `matter-js` is an optional peer dependency (`npm install matter-js`), imported only by this
63
+ * module (`@datamoc/mw_games/physics`), so a game that never simulates physics never bundles it.
64
+ * Constraints are springs and rods. Give one a `maxStretch` and it breaks when pulled that far
65
+ * past its rest length (`onBreak`); a game with a different stress rule reads `stretchOf` itself
66
+ * and calls `remove`.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { PhysicsWorld } from '@datamoc/mw_games/physics';
71
+ *
72
+ * const world = new PhysicsWorld();
73
+ * world.addBox(0, 300, 800, 20, { isStatic: true });
74
+ * const crate = world.addBox(100, 0, 40, 40, { restitution: 0.3 });
75
+ * world.update(1 / 30); // two fixed steps
76
+ * console.log(crate.position.y > 0); // true: it has started to fall
77
+ * ```
78
+ */
79
+ export declare class PhysicsWorld {
80
+ readonly engine: Matter.Engine;
81
+ /** fires once per new contact between two bodies, with the matter-js bodies of the pair */
82
+ readonly onContact: Signal<{
83
+ a: PhysicsBody;
84
+ b: PhysicsBody;
85
+ }>;
86
+ /** fires when a breakable constraint passes its `maxStretch`; it is already removed from the world */
87
+ readonly onBreak: Signal<{
88
+ constraint: PhysicsConstraint;
89
+ stretch: number;
90
+ }>;
91
+ private readonly stepper;
92
+ private readonly breakable;
93
+ constructor(options?: PhysicsOptions);
94
+ get bodies(): readonly PhysicsBody[];
95
+ get constraints(): readonly PhysicsConstraint[];
96
+ /** how far into the next fixed step the clock is, 0 to 1, for interpolated drawing */
97
+ get alpha(): number;
98
+ /** a box centred on (x, y) */
99
+ addBox(x: number, y: number, width: number, height: number, options?: BodyOptions): PhysicsBody;
100
+ addCircle(x: number, y: number, radius: number, options?: BodyOptions): PhysicsBody;
101
+ /** joins two bodies, or a body to a fixed point in the world when `b` is a point */
102
+ addConstraint(a: PhysicsBody, b: PhysicsBody | {
103
+ x: number;
104
+ y: number;
105
+ }, options?: ConstraintOptions): PhysicsConstraint;
106
+ /** removes a body, or a constraint, from the simulation */
107
+ remove(item: PhysicsBody | PhysicsConstraint): void;
108
+ /**
109
+ * How far a constraint is stretched past its rest length, as a fraction of it (0.1 is 10%
110
+ * longer); for a zero-length joint, the distance between its ends. Negative when compressed.
111
+ */
112
+ stretchOf(constraint: PhysicsConstraint): number;
113
+ /**
114
+ * Advances the simulation by `dt` seconds of real time, running as many fixed steps as that
115
+ * covers; call it from `Scene.update(dt)`.
116
+ *
117
+ * @returns how many fixed steps ran
118
+ */
119
+ update(dt: number): number;
120
+ private breakOverstretched;
121
+ private add;
122
+ }
123
+ //# sourceMappingURL=PhysicsWorld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhysicsWorld.d.ts","sourceRoot":"","sources":["../../src/physics/PhysicsWorld.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ import Matter from 'matter-js';
2
+ import { FixedStepper } from '../core/FixedStepper.js';
3
+ import { Signal } from '../core/Signal.js';
4
+ /**
5
+ * Rigid bodies and constraints through matter-js, stepped on a fixed clock.
6
+ *
7
+ * The adapter is the framework side of using matter-js in a mwg game: it owns the `Engine`,
8
+ * steps it with a `FixedStepper` so the simulation is the same at any display refresh rate,
9
+ * and reports contacts through a `Signal`, while leaving the bodies themselves as plain
10
+ * matter-js objects a game reads `position` and `angle` from to place its sprites. It is
11
+ * renderer-free, so a game's headless tests can run the same world.
12
+ *
13
+ * `matter-js` is an optional peer dependency (`npm install matter-js`), imported only by this
14
+ * module (`@datamoc/mw_games/physics`), so a game that never simulates physics never bundles it.
15
+ * Constraints are springs and rods. Give one a `maxStretch` and it breaks when pulled that far
16
+ * past its rest length (`onBreak`); a game with a different stress rule reads `stretchOf` itself
17
+ * and calls `remove`.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { PhysicsWorld } from '@datamoc/mw_games/physics';
22
+ *
23
+ * const world = new PhysicsWorld();
24
+ * world.addBox(0, 300, 800, 20, { isStatic: true });
25
+ * const crate = world.addBox(100, 0, 40, 40, { restitution: 0.3 });
26
+ * world.update(1 / 30); // two fixed steps
27
+ * console.log(crate.position.y > 0); // true: it has started to fall
28
+ * ```
29
+ */
30
+ export class PhysicsWorld {
31
+ constructor(options = {}) {
32
+ /** fires once per new contact between two bodies, with the matter-js bodies of the pair */
33
+ this.onContact = new Signal();
34
+ /** fires when a breakable constraint passes its `maxStretch`; it is already removed from the world */
35
+ this.onBreak = new Signal();
36
+ this.breakable = new Map();
37
+ this.engine = Matter.Engine.create();
38
+ const gravity = options.gravity ?? { x: 0, y: 9.8 };
39
+ //matter's gravity is a direction times `scale`; 9.8 maps to its own default scale of 0.001
40
+ this.engine.gravity.x = Math.sign(gravity.x);
41
+ this.engine.gravity.y = Math.sign(gravity.y);
42
+ this.engine.gravity.scale = Math.max(Math.abs(gravity.x), Math.abs(gravity.y)) * 0.0001;
43
+ this.stepper = new FixedStepper(options.step ?? 1 / 60, options.maxSubsteps ?? 5);
44
+ Matter.Events.on(this.engine, 'collisionStart', (event) => {
45
+ for (const pair of event.pairs)
46
+ this.onContact.dispatch({ a: pair.bodyA, b: pair.bodyB });
47
+ });
48
+ }
49
+ get bodies() {
50
+ return Matter.Composite.allBodies(this.engine.world);
51
+ }
52
+ get constraints() {
53
+ return Matter.Composite.allConstraints(this.engine.world);
54
+ }
55
+ /** how far into the next fixed step the clock is, 0 to 1, for interpolated drawing */
56
+ get alpha() {
57
+ return this.stepper.alpha;
58
+ }
59
+ /** a box centred on (x, y) */
60
+ addBox(x, y, width, height, options = {}) {
61
+ return this.add(Matter.Bodies.rectangle(x, y, width, height, options));
62
+ }
63
+ addCircle(x, y, radius, options = {}) {
64
+ return this.add(Matter.Bodies.circle(x, y, radius, options));
65
+ }
66
+ /** joins two bodies, or a body to a fixed point in the world when `b` is a point */
67
+ addConstraint(a, b, options = {}) {
68
+ const isBody = (value) => 'position' in value;
69
+ const constraint = Matter.Constraint.create({
70
+ bodyA: a,
71
+ ...(isBody(b) ? { bodyB: b } : { pointB: b }),
72
+ ...(options.pointA ? { pointA: options.pointA } : {}),
73
+ ...(isBody(b) && options.pointB ? { pointB: options.pointB } : {}),
74
+ stiffness: options.stiffness ?? 1,
75
+ damping: options.damping ?? 0,
76
+ ...(options.length !== undefined ? { length: options.length } : {}),
77
+ });
78
+ Matter.Composite.add(this.engine.world, constraint);
79
+ if (options.maxStretch !== undefined)
80
+ this.breakable.set(constraint, options.maxStretch);
81
+ return constraint;
82
+ }
83
+ /** removes a body, or a constraint, from the simulation */
84
+ remove(item) {
85
+ Matter.Composite.remove(this.engine.world, item);
86
+ this.breakable.delete(item);
87
+ }
88
+ /**
89
+ * How far a constraint is stretched past its rest length, as a fraction of it (0.1 is 10%
90
+ * longer); for a zero-length joint, the distance between its ends. Negative when compressed.
91
+ */
92
+ stretchOf(constraint) {
93
+ const a = endPoint(constraint.bodyA, constraint.pointA);
94
+ const b = endPoint(constraint.bodyB, constraint.pointB);
95
+ const distance = Math.hypot(a.x - b.x, a.y - b.y);
96
+ return constraint.length > 0 ? distance / constraint.length - 1 : distance;
97
+ }
98
+ /**
99
+ * Advances the simulation by `dt` seconds of real time, running as many fixed steps as that
100
+ * covers; call it from `Scene.update(dt)`.
101
+ *
102
+ * @returns how many fixed steps ran
103
+ */
104
+ update(dt) {
105
+ return this.stepper.advance(dt, (step) => {
106
+ Matter.Engine.update(this.engine, step * 1000);
107
+ this.breakOverstretched();
108
+ });
109
+ }
110
+ breakOverstretched() {
111
+ //collected first: removing while iterating the map would skip entries
112
+ const snapped = [];
113
+ for (const [constraint, limit] of this.breakable) {
114
+ const stretch = this.stretchOf(constraint);
115
+ if (stretch > limit)
116
+ snapped.push([constraint, stretch]);
117
+ }
118
+ for (const [constraint, stretch] of snapped) {
119
+ this.remove(constraint);
120
+ this.onBreak.dispatch({ constraint, stretch });
121
+ }
122
+ }
123
+ add(body) {
124
+ Matter.Composite.add(this.engine.world, body);
125
+ return body;
126
+ }
127
+ }
128
+ /** a constraint end in world space: the body's attachment point turned and moved with it, or the fixed point itself */
129
+ function endPoint(body, point) {
130
+ const local = point ?? { x: 0, y: 0 };
131
+ if (!body)
132
+ return local;
133
+ const cos = Math.cos(body.angle);
134
+ const sin = Math.sin(body.angle);
135
+ return { x: body.position.x + local.x * cos - local.y * sin, y: body.position.y + local.x * sin + local.y * cos };
136
+ }
137
+ //# sourceMappingURL=PhysicsWorld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhysicsWorld.js","sourceRoot":"","sources":["../../src/physics/PhysicsWorld.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA+C3C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,OAAO,YAAY;IASxB,YAAY,OAAO,GAAmB,EAAE;QAPxC,2FAA2F;QAClF,cAAS,GAAG,IAAI,MAAM,EAAsC,CAAC;QACtE,sGAAsG;QAC7F,YAAO,GAAG,IAAI,MAAM,EAAsD,CAAC;QAEnE,cAAS,GAAG,IAAI,GAAG,EAA6B,CAAC;QAGjE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;QACpD,2FAA2F;QAC3F,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;QACxF,IAAI,CAAC,OAAO,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;QAClF,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,KAA4C,EAAE,EAAE;YAChG,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK;gBAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3F,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,MAAM;QACT,OAAO,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,WAAW;QACd,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC;IAED,sFAAsF;IACtF,IAAI,KAAK;QACR,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;IAC3B,CAAC;IAED,8BAA8B;IAC9B,MAAM,CAAC,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,MAAc,EAAE,OAAO,GAAgB,EAAE;QACpF,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,SAAS,CAAC,CAAS,EAAE,CAAS,EAAE,MAAc,EAAE,OAAO,GAAgB,EAAE;QACxE,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,oFAAoF;IACpF,aAAa,CACZ,CAAc,EACd,CAAyC,EACzC,OAAO,GAAsB,EAAE;QAE/B,MAAM,MAAM,GAAG,CAAC,KAA6C,EAAwB,EAAE,CAAC,UAAU,IAAI,KAAK,CAAC;QAC5G,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YAC3C,KAAK,EAAE,CAAC;YACR,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YAC7C,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,CAAC;YACjC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC;YAC7B,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnE,CAAC,CAAC;QACH,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACpD,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS;YAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;QACzF,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,2DAA2D;IAC3D,MAAM,CAAC,IAAqC;QAC3C,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAyB,CAAC,CAAC;IAClD,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,UAA6B;QACtC,MAAM,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC5E,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,EAAU;QAChB,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;YACxC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;YAC/C,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;IACJ,CAAC;IAEO,kBAAkB;QACzB,sEAAsE;QACtE,MAAM,OAAO,GAAkC,EAAE,CAAC;QAClD,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YAC3C,IAAI,OAAO,GAAG,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,KAAK,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC;YAC7C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC;IACF,CAAC;IAEO,GAAG,CAAC,IAAiB;QAC5B,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAED,uHAAuH;AACvH,SAAS,QAAQ,CAAC,IAAwB,EAAE,KAA2C;IACtF,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IACtC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC;AACnH,CAAC"}
@@ -0,0 +1,139 @@
1
+ import { Signal } from '../core/Signal.ts';
2
+ export interface XpbdOptions {
3
+ /** downward acceleration in units per second squared; 520 when omitted (a 1-unit-per-pixel scale) */
4
+ gravity?: number;
5
+ /** substeps per `step`; 16 when omitted. More is stiffer and steadier, and costs linearly */
6
+ substeps?: number;
7
+ /** constraint iterations per substep; 2 when omitted */
8
+ iterations?: number;
9
+ /** the frame time one `step` advances, in seconds; 1/60 when omitted */
10
+ step?: number;
11
+ /** velocity damping per second (exponential); 2.5 when omitted. Raise it while a structure settles */
12
+ damping?: number;
13
+ /** time constant of the load smoothing used for breaking, in seconds; 0.06 when omitted */
14
+ smoothing?: number;
15
+ /** how much of a node's sideways motion the ground removes while it touches, 0 to 1; 0.5 when omitted */
16
+ groundFriction?: number;
17
+ }
18
+ /** the ground a node cannot sink below: a height (y grows downward), or a height at each x */
19
+ export type XpbdGround = number | ((x: number) => number);
20
+ export interface XpbdNodeSpec {
21
+ x: number;
22
+ y: number;
23
+ /** an anchor that never moves */
24
+ fixed?: boolean;
25
+ /** point mass; 1 when omitted. Link `mass` is added on top */
26
+ mass?: number;
27
+ }
28
+ export interface XpbdLinkSpec {
29
+ a: number;
30
+ b: number;
31
+ /** spring stiffness: force per unit of stretch ratio. Higher is more rigid */
32
+ stiffness: number;
33
+ /** rest length; the current distance between the nodes when omitted */
34
+ rest?: number;
35
+ /** mass of the link itself, shared evenly between its two nodes */
36
+ mass?: number;
37
+ /** a cable: pulls when stretched and goes slack, without pushing, when compressed */
38
+ tensionOnly?: boolean;
39
+ /** the load it carries in compression before breaking; also in tension unless `tensionStrength` is set. Unbreakable when omitted */
40
+ strength?: number;
41
+ /** the load it carries in tension before breaking, when that differs from `strength` */
42
+ tensionStrength?: number;
43
+ }
44
+ export interface XpbdNode {
45
+ x: number;
46
+ y: number;
47
+ fixed: boolean;
48
+ vx: number;
49
+ vy: number;
50
+ /** inverse mass, 0 for a fixed node */
51
+ w: number;
52
+ }
53
+ export interface XpbdLink {
54
+ a: number;
55
+ b: number;
56
+ stiffness: number;
57
+ rest: number;
58
+ tensionOnly: boolean;
59
+ strength: number | undefined;
60
+ tensionStrength: number | undefined;
61
+ broken: boolean;
62
+ /** the force it carried over the last step, positive in tension, negative in compression */
63
+ force: number;
64
+ /** smoothed load as a fraction of what it can carry; it breaks above 1 */
65
+ ratio: number;
66
+ }
67
+ /**
68
+ * A deterministic structure solver: point masses joined by springs, solved with extended
69
+ * position-based dynamics (XPBD), with the force in every link read back so a link can break
70
+ * at a real load rather than at a stretch.
71
+ *
72
+ * The method (Macklin, Muller and Chentanaput's XPBD) moves each node freely for a substep,
73
+ * then corrects positions so every link returns to its rest length, with a compliance of
74
+ * `rest / stiffness / dt^2`: a link behaves as a spring of the stated stiffness whatever the
75
+ * step or iteration count, and the correction's Lagrange multiplier divided by `dt^2` is the
76
+ * force it carried. Steps are fixed and nothing is random, so the same structure and the same
77
+ * calls give the same result on every machine, which is what replaying or validating a
78
+ * player's bridge needs.
79
+ *
80
+ * It is the engine of Pontifex (a bridge-building game), generalised: the vehicle, the water
81
+ * and the levels stay in the game, and what is here is only nodes, links, forces and breaking.
82
+ * Unlike `PhysicsWorld` it has no rigid bodies or collisions and no `matter-js` dependency.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * import { XpbdWorld } from '@datamoc/mw_games/physics/xpbd';
87
+ *
88
+ * const world = new XpbdWorld({ gravity: 520 });
89
+ * const wall = world.addNode({ x: 0, y: 0, fixed: true });
90
+ * const weight = world.addNode({ x: 40, y: 0, mass: 1 });
91
+ * const rope = world.addLink({ a: wall, b: weight, stiffness: 2e4, strength: 900 });
92
+ * world.breaking = true;
93
+ * for (let i = 0; i < 120; i++) world.step();
94
+ * console.log(world.links[rope].broken, world.nodes[weight].y > 0);
95
+ * ```
96
+ */
97
+ export declare class XpbdWorld {
98
+ readonly nodes: XpbdNode[];
99
+ readonly links: XpbdLink[];
100
+ /** fires once for a link that passed its strength, which has already stopped carrying load */
101
+ readonly onBreak: Signal<{
102
+ link: number;
103
+ x: number;
104
+ y: number;
105
+ }>;
106
+ /** links break only while this is true, so a structure can settle under its own weight first */
107
+ breaking: boolean;
108
+ /** the highest smoothed load ratio across the links at the last step, for a strain gauge */
109
+ peakRatio: number;
110
+ gravity: number;
111
+ damping: number;
112
+ /** nodes are held on or above this; null for none. Water, a gorge or a cliff face is the game's own business */
113
+ ground: XpbdGround | null;
114
+ groundFriction: number;
115
+ private readonly frame;
116
+ private readonly substeps;
117
+ private readonly iterations;
118
+ private readonly smoothing;
119
+ private readonly forces;
120
+ private readonly masses;
121
+ private readonly lambda;
122
+ private readonly accumulated;
123
+ constructor(options?: XpbdOptions);
124
+ /** adds a node and returns its index */
125
+ addNode(spec: XpbdNodeSpec): number;
126
+ /** adds a link between two existing nodes and returns its index */
127
+ addLink(spec: XpbdLinkSpec): number;
128
+ /** an external force on a node for the next `step` only (a wheel's weight, wind) */
129
+ applyForce(node: number, fx: number, fy: number): void;
130
+ /** advances one frame: the substeps, then the link loads and any breaking */
131
+ step(): void;
132
+ private refreshInverseMass;
133
+ private substep;
134
+ /** lifts any node that sank below the ground back onto it, and takes some of its sideways motion with it */
135
+ private collideWithGround;
136
+ /** averages each link's force over the substeps, smooths the load ratio and breaks what passed its strength */
137
+ private settleLoads;
138
+ }
139
+ //# sourceMappingURL=XpbdWorld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XpbdWorld.d.ts","sourceRoot":"","sources":["../../src/physics/XpbdWorld.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,207 @@
1
+ import { Signal } from '../core/Signal.js';
2
+ /**
3
+ * A deterministic structure solver: point masses joined by springs, solved with extended
4
+ * position-based dynamics (XPBD), with the force in every link read back so a link can break
5
+ * at a real load rather than at a stretch.
6
+ *
7
+ * The method (Macklin, Muller and Chentanaput's XPBD) moves each node freely for a substep,
8
+ * then corrects positions so every link returns to its rest length, with a compliance of
9
+ * `rest / stiffness / dt^2`: a link behaves as a spring of the stated stiffness whatever the
10
+ * step or iteration count, and the correction's Lagrange multiplier divided by `dt^2` is the
11
+ * force it carried. Steps are fixed and nothing is random, so the same structure and the same
12
+ * calls give the same result on every machine, which is what replaying or validating a
13
+ * player's bridge needs.
14
+ *
15
+ * It is the engine of Pontifex (a bridge-building game), generalised: the vehicle, the water
16
+ * and the levels stay in the game, and what is here is only nodes, links, forces and breaking.
17
+ * Unlike `PhysicsWorld` it has no rigid bodies or collisions and no `matter-js` dependency.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { XpbdWorld } from '@datamoc/mw_games/physics/xpbd';
22
+ *
23
+ * const world = new XpbdWorld({ gravity: 520 });
24
+ * const wall = world.addNode({ x: 0, y: 0, fixed: true });
25
+ * const weight = world.addNode({ x: 40, y: 0, mass: 1 });
26
+ * const rope = world.addLink({ a: wall, b: weight, stiffness: 2e4, strength: 900 });
27
+ * world.breaking = true;
28
+ * for (let i = 0; i < 120; i++) world.step();
29
+ * console.log(world.links[rope].broken, world.nodes[weight].y > 0);
30
+ * ```
31
+ */
32
+ export class XpbdWorld {
33
+ constructor(options = {}) {
34
+ this.nodes = [];
35
+ this.links = [];
36
+ /** fires once for a link that passed its strength, which has already stopped carrying load */
37
+ this.onBreak = new Signal();
38
+ /** links break only while this is true, so a structure can settle under its own weight first */
39
+ this.breaking = false;
40
+ /** the highest smoothed load ratio across the links at the last step, for a strain gauge */
41
+ this.peakRatio = 0;
42
+ /** nodes are held on or above this; null for none. Water, a gorge or a cliff face is the game's own business */
43
+ this.ground = null;
44
+ this.forces = [];
45
+ this.masses = [];
46
+ this.lambda = [];
47
+ this.accumulated = [];
48
+ this.gravity = options.gravity ?? 520;
49
+ this.damping = options.damping ?? 2.5;
50
+ this.groundFriction = options.groundFriction ?? 0.5;
51
+ this.frame = options.step ?? 1 / 60;
52
+ this.substeps = Math.max(1, Math.floor(options.substeps ?? 16));
53
+ this.iterations = Math.max(1, Math.floor(options.iterations ?? 2));
54
+ this.smoothing = options.smoothing ?? 0.06;
55
+ }
56
+ /** adds a node and returns its index */
57
+ addNode(spec) {
58
+ const mass = spec.mass ?? 1;
59
+ if (!(mass > 0) && !spec.fixed)
60
+ throw new Error('a moving node needs a positive mass');
61
+ this.nodes.push({ x: spec.x, y: spec.y, fixed: spec.fixed ?? false, vx: 0, vy: 0, w: 0 });
62
+ this.masses.push(mass);
63
+ this.forces.push({ x: 0, y: 0 });
64
+ this.refreshInverseMass();
65
+ return this.nodes.length - 1;
66
+ }
67
+ /** adds a link between two existing nodes and returns its index */
68
+ addLink(spec) {
69
+ const a = this.nodes[spec.a];
70
+ const b = this.nodes[spec.b];
71
+ if (!a || !b)
72
+ throw new Error('a link needs two existing nodes');
73
+ const rest = spec.rest ?? Math.hypot(a.x - b.x, a.y - b.y);
74
+ if (!(rest > 0))
75
+ throw new Error('a link needs a positive rest length');
76
+ if (!(spec.stiffness > 0))
77
+ throw new Error('a link needs a positive stiffness');
78
+ const share = (spec.mass ?? 0) / 2;
79
+ this.masses[spec.a] += share;
80
+ this.masses[spec.b] += share;
81
+ this.links.push({
82
+ a: spec.a,
83
+ b: spec.b,
84
+ stiffness: spec.stiffness,
85
+ rest,
86
+ tensionOnly: spec.tensionOnly ?? false,
87
+ strength: spec.strength,
88
+ tensionStrength: spec.tensionStrength,
89
+ broken: false,
90
+ force: 0,
91
+ ratio: 0,
92
+ });
93
+ this.lambda.push(0);
94
+ this.accumulated.push(0);
95
+ this.refreshInverseMass();
96
+ return this.links.length - 1;
97
+ }
98
+ /** an external force on a node for the next `step` only (a wheel's weight, wind) */
99
+ applyForce(node, fx, fy) {
100
+ this.forces[node].x += fx;
101
+ this.forces[node].y += fy;
102
+ }
103
+ /** advances one frame: the substeps, then the link loads and any breaking */
104
+ step() {
105
+ const dt = this.frame / this.substeps;
106
+ this.accumulated.fill(0);
107
+ for (let s = 0; s < this.substeps; s++)
108
+ this.substep(dt);
109
+ for (const force of this.forces) {
110
+ force.x = 0;
111
+ force.y = 0;
112
+ }
113
+ this.settleLoads();
114
+ }
115
+ refreshInverseMass() {
116
+ this.nodes.forEach((node, i) => {
117
+ node.w = node.fixed ? 0 : 1 / this.masses[i];
118
+ });
119
+ }
120
+ substep(dt) {
121
+ const damp = Math.exp(-this.damping * dt);
122
+ const previous = this.nodes.map((node) => ({ x: node.x, y: node.y }));
123
+ this.nodes.forEach((node, i) => {
124
+ if (node.fixed)
125
+ return;
126
+ const force = this.forces[i];
127
+ node.vx = (node.vx + force.x * node.w * dt) * damp;
128
+ node.vy = (node.vy + (force.y * node.w + this.gravity) * dt) * damp;
129
+ node.x += node.vx * dt;
130
+ node.y += node.vy * dt;
131
+ });
132
+ this.lambda.fill(0);
133
+ for (let iteration = 0; iteration < this.iterations; iteration++) {
134
+ this.links.forEach((link, i) => {
135
+ if (link.broken)
136
+ return;
137
+ const p = this.nodes[link.a];
138
+ const q = this.nodes[link.b];
139
+ const w = p.w + q.w;
140
+ if (w === 0)
141
+ return;
142
+ const dx = q.x - p.x;
143
+ const dy = q.y - p.y;
144
+ const length = Math.hypot(dx, dy) || 1e-9;
145
+ const c = length - link.rest;
146
+ if (link.tensionOnly && c < 0 && this.lambda[i] >= 0)
147
+ return;
148
+ const alpha = link.rest / link.stiffness / (dt * dt);
149
+ const delta = (-c - alpha * this.lambda[i]) / (w + alpha);
150
+ this.lambda[i] += delta;
151
+ const nx = dx / length;
152
+ const ny = dy / length;
153
+ p.x -= p.w * delta * nx;
154
+ p.y -= p.w * delta * ny;
155
+ q.x += q.w * delta * nx;
156
+ q.y += q.w * delta * ny;
157
+ });
158
+ }
159
+ this.collideWithGround(previous);
160
+ this.nodes.forEach((node, i) => {
161
+ if (node.fixed)
162
+ return;
163
+ node.vx = (node.x - previous[i].x) / dt;
164
+ node.vy = (node.y - previous[i].y) / dt;
165
+ });
166
+ this.links.forEach((_, i) => {
167
+ this.accumulated[i] += -this.lambda[i] / (dt * dt);
168
+ });
169
+ }
170
+ /** lifts any node that sank below the ground back onto it, and takes some of its sideways motion with it */
171
+ collideWithGround(previous) {
172
+ const ground = this.ground;
173
+ if (ground === null)
174
+ return;
175
+ this.nodes.forEach((node, i) => {
176
+ if (node.fixed)
177
+ return;
178
+ const surface = typeof ground === 'number' ? ground : ground(node.x);
179
+ if (node.y <= surface)
180
+ return;
181
+ node.y = surface;
182
+ node.x -= (node.x - previous[i].x) * this.groundFriction;
183
+ });
184
+ }
185
+ /** averages each link's force over the substeps, smooths the load ratio and breaks what passed its strength */
186
+ settleLoads() {
187
+ const blend = 1 - Math.exp(-this.frame / this.smoothing);
188
+ let peak = 0;
189
+ this.links.forEach((link, i) => {
190
+ if (link.broken)
191
+ return;
192
+ link.force = this.accumulated[i] / this.substeps;
193
+ const limit = link.force > 0 ? (link.tensionStrength ?? link.strength) : link.strength;
194
+ const instant = limit === undefined ? 0 : Math.abs(link.force) / limit;
195
+ link.ratio += (instant - link.ratio) * blend;
196
+ peak = Math.max(peak, link.ratio);
197
+ if (this.breaking && link.ratio > 1) {
198
+ link.broken = true;
199
+ const p = this.nodes[link.a];
200
+ const q = this.nodes[link.b];
201
+ this.onBreak.dispatch({ link: i, x: (p.x + q.x) / 2, y: (p.y + q.y) / 2 });
202
+ }
203
+ });
204
+ this.peakRatio = peak;
205
+ }
206
+ }
207
+ //# sourceMappingURL=XpbdWorld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XpbdWorld.js","sourceRoot":"","sources":["../../src/physics/XpbdWorld.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { PhysicsWorld } from './PhysicsWorld.ts';
2
+ export type { BodyOptions, ConstraintOptions, PhysicsBody, PhysicsConstraint, PhysicsOptions } from './PhysicsWorld.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/physics/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { PhysicsWorld } from './PhysicsWorld.js';
2
+ //# sourceMappingURL=index.js.map