@codexo/exojs-physics 0.13.0 → 0.15.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 (76) hide show
  1. package/README.md +17 -10
  2. package/dist/esm/Collider.d.ts +17 -6
  3. package/dist/esm/Collider.js +28 -6
  4. package/dist/esm/Collider.js.map +1 -1
  5. package/dist/esm/ContactGraph.d.ts +49 -3
  6. package/dist/esm/ContactGraph.js +132 -44
  7. package/dist/esm/ContactGraph.js.map +1 -1
  8. package/dist/esm/PhysicsBody.d.ts +145 -15
  9. package/dist/esm/PhysicsBody.js +282 -21
  10. package/dist/esm/PhysicsBody.js.map +1 -1
  11. package/dist/esm/PhysicsWorld.d.ts +177 -39
  12. package/dist/esm/PhysicsWorld.js +412 -35
  13. package/dist/esm/PhysicsWorld.js.map +1 -1
  14. package/dist/esm/backend/NativePhysicsBackend.d.ts +5 -0
  15. package/dist/esm/backend/NativePhysicsBackend.js +14 -0
  16. package/dist/esm/backend/NativePhysicsBackend.js.map +1 -1
  17. package/dist/esm/backend/PhysicsBackend.d.ts +9 -1
  18. package/dist/esm/binding/BindingRegistry.d.ts +1 -2
  19. package/dist/esm/binding/BindingRegistry.js +2 -2
  20. package/dist/esm/binding/BindingRegistry.js.map +1 -1
  21. package/dist/esm/binding/PhysicsBinding.d.ts +7 -18
  22. package/dist/esm/binding/PhysicsBinding.js +9 -8
  23. package/dist/esm/binding/PhysicsBinding.js.map +1 -1
  24. package/dist/esm/broadphase/SweepAndPrune.d.ts +1 -0
  25. package/dist/esm/broadphase/SweepAndPrune.js +32 -3
  26. package/dist/esm/broadphase/SweepAndPrune.js.map +1 -1
  27. package/dist/esm/collision/CollisionProxy.d.ts +2 -2
  28. package/dist/esm/collision/narrowphase.js +91 -38
  29. package/dist/esm/collision/narrowphase.js.map +1 -1
  30. package/dist/esm/debug/PhysicsDebugDraw.d.ts +8 -1
  31. package/dist/esm/debug/PhysicsDebugDraw.js +26 -2
  32. package/dist/esm/debug/PhysicsDebugDraw.js.map +1 -1
  33. package/dist/esm/index.js +7 -0
  34. package/dist/esm/index.js.map +1 -1
  35. package/dist/esm/joints/DistanceJoint.d.ts +71 -0
  36. package/dist/esm/joints/DistanceJoint.js +176 -0
  37. package/dist/esm/joints/DistanceJoint.js.map +1 -0
  38. package/dist/esm/joints/Joint.d.ts +25 -0
  39. package/dist/esm/joints/Joint.js +24 -0
  40. package/dist/esm/joints/Joint.js.map +1 -0
  41. package/dist/esm/joints/MouseJoint.d.ts +57 -0
  42. package/dist/esm/joints/MouseJoint.js +137 -0
  43. package/dist/esm/joints/MouseJoint.js.map +1 -0
  44. package/dist/esm/joints/PrismaticJoint.d.ts +85 -0
  45. package/dist/esm/joints/PrismaticJoint.js +241 -0
  46. package/dist/esm/joints/PrismaticJoint.js.map +1 -0
  47. package/dist/esm/joints/RevoluteJoint.d.ts +81 -0
  48. package/dist/esm/joints/RevoluteJoint.js +217 -0
  49. package/dist/esm/joints/RevoluteJoint.js.map +1 -0
  50. package/dist/esm/joints/WeldJoint.d.ts +61 -0
  51. package/dist/esm/joints/WeldJoint.js +159 -0
  52. package/dist/esm/joints/WeldJoint.js.map +1 -0
  53. package/dist/esm/joints/WheelJoint.d.ts +92 -0
  54. package/dist/esm/joints/WheelJoint.js +256 -0
  55. package/dist/esm/joints/WheelJoint.js.map +1 -0
  56. package/dist/esm/math.js +15 -1
  57. package/dist/esm/math.js.map +1 -1
  58. package/dist/esm/physicsBuildInfo.js +2 -2
  59. package/dist/esm/public.d.ts +9 -2
  60. package/dist/esm/query/QueryEngine.d.ts +2 -2
  61. package/dist/esm/query/QueryEngine.js +13 -4
  62. package/dist/esm/query/QueryEngine.js.map +1 -1
  63. package/dist/esm/shapes/AnyShape.d.ts +9 -0
  64. package/dist/esm/shapes/CircleShape.d.ts +1 -2
  65. package/dist/esm/shapes/CircleShape.js.map +1 -1
  66. package/dist/esm/shapes/PolygonShape.d.ts +1 -2
  67. package/dist/esm/shapes/PolygonShape.js +45 -17
  68. package/dist/esm/shapes/PolygonShape.js.map +1 -1
  69. package/dist/esm/shapes/index.d.ts +1 -0
  70. package/dist/esm/solver/ContactSolver.d.ts +87 -0
  71. package/dist/esm/solver/ContactSolver.js +490 -0
  72. package/dist/esm/solver/ContactSolver.js.map +1 -0
  73. package/dist/esm/sort.d.ts +17 -0
  74. package/dist/esm/sort.js +54 -0
  75. package/dist/esm/sort.js.map +1 -0
  76. package/package.json +3 -3
@@ -10,14 +10,18 @@ export interface BodyOptions {
10
10
  position?: VectorLike;
11
11
  /** Initial rotation in radians. Default `0`. */
12
12
  angle?: number;
13
- /** Linear damping (applied once dynamics ship). Default `0`. */
13
+ /** Linear damping applied to the velocity each sub-step. Default `0`. */
14
14
  linearDamping?: number;
15
- /** Angular damping (applied once dynamics ship). Default `0`. */
15
+ /** Angular damping applied to the angular velocity each sub-step. Default `0`. */
16
16
  angularDamping?: number;
17
- /** Per-body multiplier on world gravity (applied once dynamics ship). Default `1`. */
17
+ /** Per-body multiplier on world gravity (e.g. `0` to ignore gravity). Default `1`. */
18
18
  gravityScale?: number;
19
19
  /** When `true`, the body never rotates under contacts (infinite rotational inertia). Default `false`. */
20
20
  fixedRotation?: boolean;
21
+ /** When `true`, the body is swept against static geometry each step (CCD) so it cannot tunnel through thin walls. Default `false`. */
22
+ isBullet?: boolean;
23
+ /** Colliders to attach up-front. Each may be a {@link Collider} instance or its {@link ColliderOptions}. */
24
+ colliders?: Array<Collider | ColliderOptions>;
21
25
  }
22
26
  /**
23
27
  * Internal hook the world hands to each body so colliders can be id-allocated
@@ -29,22 +33,25 @@ export interface BodyOwner {
29
33
  }
30
34
  /**
31
35
  * A rigid body: a world transform plus mass properties aggregated from its
32
- * colliders. In this collision/query release a body holds and reports its
33
- * transform and mass but is not integrated it moves only via
34
- * {@link setTransform} (game-driven / kinematic). Forces, impulses and gravity
35
- * integration arrive with the dynamics solver.
36
+ * colliders. Dynamic bodies integrate under gravity, accumulated forces/torque
37
+ * and contact impulses each fixed sub-step; static bodies never move; kinematic
38
+ * bodies move by their velocity only and stay immovable under contacts. Drive a
39
+ * body with {@link applyForce}, {@link applyTorque} and {@link applyImpulse}, or
40
+ * set {@link linearVelocityX}/{@link linearVelocityY}/{@link angularVelocity}
41
+ * directly; reposition it (teleport, no velocity) with {@link setTransform}.
36
42
  */
37
43
  export declare class PhysicsBody {
38
- readonly id: number;
39
44
  readonly type: BodyType;
40
- /** Linear damping (inert until dynamics ship). */
45
+ /** Linear damping applied to the velocity each sub-step. */
41
46
  linearDamping: number;
42
- /** Angular damping (inert until dynamics ship). */
47
+ /** Angular damping applied to the angular velocity each sub-step. */
43
48
  angularDamping: number;
44
- /** Per-body gravity multiplier (inert until dynamics ship). */
49
+ /** Per-body multiplier on world gravity (e.g. `0` to ignore gravity). */
45
50
  gravityScale: number;
46
51
  /** When `true`, rotational inertia is treated as infinite. */
47
52
  fixedRotation: boolean;
53
+ /** When `true`, the body is swept against static geometry each step (CCD) so it cannot tunnel through thin walls. */
54
+ isBullet: boolean;
48
55
  /** Total mass (0 for static/kinematic). */
49
56
  mass: number;
50
57
  /** Inverse mass (`0` = immovable). */
@@ -53,14 +60,50 @@ export declare class PhysicsBody {
53
60
  inertia: number;
54
61
  /** Inverse rotational inertia (`0` = no angular response). */
55
62
  invInertia: number;
56
- private readonly _owner;
63
+ /** Linear velocity X in px/s. */
64
+ linearVelocityX: number;
65
+ /** Linear velocity Y in px/s. */
66
+ linearVelocityY: number;
67
+ /** Angular velocity in rad/s. */
68
+ angularVelocity: number;
69
+ /** When `false`, this body is never put to sleep. Default `true`. */
70
+ allowSleep: boolean;
71
+ /** @internal — Delta position X accumulated across the frame's sub-steps by the TGS integrator; written into the transform once per frame by {@link _finalizePosition}. */
72
+ _deltaPosX: number;
73
+ /** @internal — Delta position Y accumulated across the frame's sub-steps by the TGS integrator. */
74
+ _deltaPosY: number;
75
+ /** @internal — Delta rotation (radians) accumulated across the frame's sub-steps by the TGS integrator. */
76
+ _deltaAngle: number;
77
+ /** @internal — `cos(_deltaAngle)`, cached so the solver can rotate the contact anchors by the live sub-step rotation without a per-contact trig call. */
78
+ _deltaCos: number;
79
+ /** @internal — `sin(_deltaAngle)`, cached alongside {@link _deltaCos}. */
80
+ _deltaSin: number;
81
+ private _forceX;
82
+ private _forceY;
83
+ private _torque;
84
+ /** @internal — seconds the body has stayed below the sleep thresholds (frozen while asleep). Read by the world's island pass. */
85
+ _sleepTime: number;
86
+ /** @internal — dense union-find index assigned by the world's island pass each step. */
87
+ _islandIndex: number;
88
+ /** @internal — world-space centre of mass at the start of the current fixed step (CCD swept-test origin). */
89
+ _ccdPrevX: number;
90
+ /** @internal — see {@link _ccdPrevX}. */
91
+ _ccdPrevY: number;
92
+ private _sleeping;
93
+ private _id;
94
+ private _owner;
95
+ private _attached;
57
96
  private readonly _transform;
58
97
  private readonly _colliders;
59
98
  private _comX;
60
99
  private _comY;
61
100
  private _massReady;
62
101
  private _destroyed;
63
- constructor(owner: BodyOwner, id: number, options?: BodyOptions);
102
+ constructor(options?: BodyOptions);
103
+ /** Stable id, assigned when the body joins a world via `world.add()`; `-1` until then. */
104
+ get id(): number;
105
+ /** `true` once the body has been added to a world (guards against double-add). */
106
+ get attached(): boolean;
64
107
  /** World X. */
65
108
  get x(): number;
66
109
  /** World Y. */
@@ -85,8 +128,17 @@ export declare class PhysicsBody {
85
128
  get isMassReady(): boolean;
86
129
  /** `true` after the owning world has destroyed this body. */
87
130
  get destroyed(): boolean;
88
- /** Attach a new collider, recomputing mass and synchronising world geometry. */
89
- createCollider(options: ColliderOptions): Collider;
131
+ /** `true` when the body is asleep — skipped by the integrator and solver until woken. */
132
+ get isSleeping(): boolean;
133
+ /**
134
+ * Attach a collider to this body. Accepts a {@link Collider} instance or its
135
+ * {@link ColliderOptions} (a convenience that constructs the collider for you).
136
+ * When the body is already in a world, the collider is id-allocated, registered,
137
+ * the mass model is recomputed and the world geometry synchronised immediately;
138
+ * on a free-standing body the collider is simply held until `world.add()`.
139
+ * Returns the collider.
140
+ */
141
+ addCollider(collider: Collider | ColliderOptions): Collider;
90
142
  /**
91
143
  * Set the body's world transform. Resets the body's cached collider geometry
92
144
  * immediately so subsequent queries see the new placement. Returns `this`.
@@ -94,8 +146,86 @@ export declare class PhysicsBody {
94
146
  setTransform(position: VectorLike, angle?: number): this;
95
147
  /** Refresh every collider's world geometry from the current transform. */
96
148
  synchronizeColliders(): void;
149
+ /** World-space centre of mass X (body origin + rotated local centre of mass). */
150
+ get worldCenterOfMassX(): number;
151
+ /** World-space centre of mass Y. */
152
+ get worldCenterOfMassY(): number;
153
+ /**
154
+ * Accumulate a world-space force at the centre of mass; integrated on the next
155
+ * sub-step and then cleared. No-op on static/kinematic bodies. Returns `this`.
156
+ */
157
+ applyForce(forceX: number, forceY: number): this;
158
+ /**
159
+ * Accumulate a torque; integrated on the next sub-step and then cleared. No-op
160
+ * on static/kinematic bodies (and fixed-rotation bodies). Returns `this`.
161
+ */
162
+ applyTorque(torque: number): this;
163
+ /**
164
+ * Apply an instantaneous world-space impulse at `(pointX, pointY)` (world space;
165
+ * defaults to the centre of mass), changing velocity immediately. No-op on
166
+ * static/kinematic bodies (infinite mass). Returns `this`.
167
+ */
168
+ applyImpulse(impulseX: number, impulseY: number, pointX?: number, pointY?: number): this;
169
+ /**
170
+ * @internal — integrate velocity from gravity, accumulated forces/torque and
171
+ * damping over the sub-step `h`. No-op for static/kinematic (infinite mass
172
+ * keeps their velocity solver-driven only). Called once per TGS sub-step, so
173
+ * gravity/forces are applied with `h` each sub-step (`N·h = dt`, same total
174
+ * impulse, but the small-step position error scales with `h²`). The
175
+ * force/torque accumulators are **not** cleared here — they are consumed by
176
+ * every sub-step and cleared once per frame by {@link _finalizePosition}.
177
+ */
178
+ _integrateVelocity(h: number, gravityX: number, gravityY: number): void;
179
+ /**
180
+ * @internal — accumulate this sub-step's velocity into the per-frame delta
181
+ * position/rotation (TGS sub-stepping). The transform itself is **not** moved
182
+ * here; {@link _finalizePosition} writes the accumulated delta once per frame.
183
+ * Tracking the delta (rather than moving the transform each sub-step) lets the
184
+ * solver recompute contact separation from it without re-running detection or
185
+ * re-syncing collider geometry per sub-step. Static bodies never move.
186
+ */
187
+ _integratePosition(h: number): void;
188
+ /**
189
+ * @internal — advance the sleep timer over one fixed step `dt`. A body below
190
+ * both velocity thresholds accumulates time; a too-fast body, or one that opts
191
+ * out via {@link allowSleep}, resets it. The sleep/wake decision is made per
192
+ * island by the world (so a stack sleeps as a unit) from {@link _sleepTime}.
193
+ */
194
+ _accumulateSleepTime(dt: number, linearThreshold: number, angularThreshold: number): void;
195
+ /** @internal — set the sleep state. Sleeping zeroes the velocity; waking resets the sleep timer. */
196
+ _setSleeping(sleeping: boolean): void;
197
+ /**
198
+ * Wake the body if it is asleep, resetting its sleep timer. The rest of its
199
+ * island wakes with it on the next step (a contact to an awake body keeps the
200
+ * whole island awake). Returns `this`.
201
+ */
202
+ wake(): this;
203
+ /**
204
+ * @internal — apply the frame's accumulated delta position/rotation to the
205
+ * transform (rotating about the centre of mass), re-sync collider geometry and
206
+ * clear the force/torque accumulators. Called once per frame after the
207
+ * sub-step loop. Static bodies never move; the force clear still runs (forces
208
+ * are a no-op on infinite mass but the accumulator is reset for consistency).
209
+ */
210
+ _finalizePosition(): void;
211
+ /** Clear the per-frame TGS delta accumulators (position, rotation and its cached cos/sin). */
212
+ private _resetDelta;
97
213
  /** Internal: detach a collider (called by the world during destruction). */
98
214
  _removeCollider(collider: Collider): void;
215
+ /**
216
+ * @internal — join `owner`'s world with the allocated `id`. Allocates ids for
217
+ * and registers every held collider, then aggregates the mass model and
218
+ * synchronises world geometry. Called once by {@link PhysicsWorld.add}.
219
+ *
220
+ * Ordering matters and mirrors the per-collider create path exactly: ids and
221
+ * the body link go on first, then a single mass recompute over the full
222
+ * collider set (so the centre of mass / inertia aggregate is correct), then
223
+ * `synchronize` over every collider (the cached world geometry the broad/narrow
224
+ * phase reads), then world registration last. Recomputing mass before all
225
+ * colliders are linked, or syncing before the mass model exists, would feed the
226
+ * solver a stale CoM and produce subtle integration bugs.
227
+ */
228
+ _attachToWorld(owner: BodyOwner, id: number): void;
99
229
  /** Internal: mark destroyed (called by the world). */
100
230
  _markDestroyed(): void;
101
231
  /** Recompute mass, centre of mass and rotational inertia from the colliders. */
@@ -4,22 +4,25 @@ import { createTransform, setTransform, applyTransform } from './math.js';
4
4
 
5
5
  /**
6
6
  * A rigid body: a world transform plus mass properties aggregated from its
7
- * colliders. In this collision/query release a body holds and reports its
8
- * transform and mass but is not integrated it moves only via
9
- * {@link setTransform} (game-driven / kinematic). Forces, impulses and gravity
10
- * integration arrive with the dynamics solver.
7
+ * colliders. Dynamic bodies integrate under gravity, accumulated forces/torque
8
+ * and contact impulses each fixed sub-step; static bodies never move; kinematic
9
+ * bodies move by their velocity only and stay immovable under contacts. Drive a
10
+ * body with {@link applyForce}, {@link applyTorque} and {@link applyImpulse}, or
11
+ * set {@link linearVelocityX}/{@link linearVelocityY}/{@link angularVelocity}
12
+ * directly; reposition it (teleport, no velocity) with {@link setTransform}.
11
13
  */
12
14
  class PhysicsBody {
13
- id;
14
15
  type;
15
- /** Linear damping (inert until dynamics ship). */
16
+ /** Linear damping applied to the velocity each sub-step. */
16
17
  linearDamping;
17
- /** Angular damping (inert until dynamics ship). */
18
+ /** Angular damping applied to the angular velocity each sub-step. */
18
19
  angularDamping;
19
- /** Per-body gravity multiplier (inert until dynamics ship). */
20
+ /** Per-body multiplier on world gravity (e.g. `0` to ignore gravity). */
20
21
  gravityScale;
21
22
  /** When `true`, rotational inertia is treated as infinite. */
22
23
  fixedRotation;
24
+ /** When `true`, the body is swept against static geometry each step (CCD) so it cannot tunnel through thin walls. */
25
+ isBullet;
23
26
  /** Total mass (0 for static/kinematic). */
24
27
  mass = 0;
25
28
  /** Inverse mass (`0` = immovable). */
@@ -28,22 +31,69 @@ class PhysicsBody {
28
31
  inertia = 0;
29
32
  /** Inverse rotational inertia (`0` = no angular response). */
30
33
  invInertia = 0;
31
- _owner;
34
+ /** Linear velocity X in px/s. */
35
+ linearVelocityX = 0;
36
+ /** Linear velocity Y in px/s. */
37
+ linearVelocityY = 0;
38
+ /** Angular velocity in rad/s. */
39
+ angularVelocity = 0;
40
+ /** When `false`, this body is never put to sleep. Default `true`. */
41
+ allowSleep = true;
42
+ /** @internal — Delta position X accumulated across the frame's sub-steps by the TGS integrator; written into the transform once per frame by {@link _finalizePosition}. */
43
+ _deltaPosX = 0;
44
+ /** @internal — Delta position Y accumulated across the frame's sub-steps by the TGS integrator. */
45
+ _deltaPosY = 0;
46
+ /** @internal — Delta rotation (radians) accumulated across the frame's sub-steps by the TGS integrator. */
47
+ _deltaAngle = 0;
48
+ /** @internal — `cos(_deltaAngle)`, cached so the solver can rotate the contact anchors by the live sub-step rotation without a per-contact trig call. */
49
+ _deltaCos = 1;
50
+ /** @internal — `sin(_deltaAngle)`, cached alongside {@link _deltaCos}. */
51
+ _deltaSin = 0;
52
+ _forceX = 0;
53
+ _forceY = 0;
54
+ _torque = 0;
55
+ /** @internal — seconds the body has stayed below the sleep thresholds (frozen while asleep). Read by the world's island pass. */
56
+ _sleepTime = 0;
57
+ /** @internal — dense union-find index assigned by the world's island pass each step. */
58
+ _islandIndex = 0;
59
+ /** @internal — world-space centre of mass at the start of the current fixed step (CCD swept-test origin). */
60
+ _ccdPrevX = 0;
61
+ /** @internal — see {@link _ccdPrevX}. */
62
+ _ccdPrevY = 0;
63
+ _sleeping = false;
64
+ _id = -1;
65
+ _owner = null;
66
+ _attached = false;
32
67
  _transform;
33
68
  _colliders = [];
34
69
  _comX = 0;
35
70
  _comY = 0;
36
71
  _massReady = false;
37
72
  _destroyed = false;
38
- constructor(owner, id, options = {}) {
39
- this.id = id;
73
+ constructor(options = {}) {
40
74
  this.type = options.type ?? 'dynamic';
41
- this._owner = owner;
42
75
  this._transform = createTransform(options.position?.x ?? 0, options.position?.y ?? 0, options.angle ?? 0);
43
76
  this.linearDamping = options.linearDamping ?? 0;
44
77
  this.angularDamping = options.angularDamping ?? 0;
45
78
  this.gravityScale = options.gravityScale ?? 1;
46
79
  this.fixedRotation = options.fixedRotation ?? false;
80
+ this.isBullet = options.isBullet ?? false;
81
+ // Build up-front colliders now (no id/world registration until the body
82
+ // joins a world via `world.add()`). They sit in `_colliders` unsynchronised;
83
+ // `_attachToWorld` assigns ids, registers them and computes the mass model.
84
+ if (options.colliders) {
85
+ for (const entry of options.colliders) {
86
+ this._colliders.push(entry instanceof Collider ? entry : new Collider(entry));
87
+ }
88
+ }
89
+ }
90
+ /** Stable id, assigned when the body joins a world via `world.add()`; `-1` until then. */
91
+ get id() {
92
+ return this._id;
93
+ }
94
+ /** `true` once the body has been added to a world (guards against double-add). */
95
+ get attached() {
96
+ return this._attached;
47
97
  }
48
98
  /** World X. */
49
99
  get x() {
@@ -89,19 +139,36 @@ class PhysicsBody {
89
139
  get destroyed() {
90
140
  return this._destroyed;
91
141
  }
92
- /** Attach a new collider, recomputing mass and synchronising world geometry. */
93
- createCollider(options) {
142
+ /** `true` when the body is asleep — skipped by the integrator and solver until woken. */
143
+ get isSleeping() {
144
+ return this._sleeping;
145
+ }
146
+ /**
147
+ * Attach a collider to this body. Accepts a {@link Collider} instance or its
148
+ * {@link ColliderOptions} (a convenience that constructs the collider for you).
149
+ * When the body is already in a world, the collider is id-allocated, registered,
150
+ * the mass model is recomputed and the world geometry synchronised immediately;
151
+ * on a free-standing body the collider is simply held until `world.add()`.
152
+ * Returns the collider.
153
+ */
154
+ addCollider(collider) {
94
155
  if (this._destroyed) {
95
- throw new Error('PhysicsBody: cannot create a collider on a destroyed body.');
156
+ throw new Error('PhysicsBody: cannot add a collider to a destroyed body.');
96
157
  }
97
158
  // Collider imports PhysicsBody type-only (erased), so this value import is
98
159
  // one-directional — no runtime cycle.
99
- const collider = new Collider(this._owner._allocateColliderId(), this, options);
100
- this._colliders.push(collider);
101
- this._recomputeMass();
102
- collider.synchronize(this._transform);
103
- this._owner._registerCollider(collider);
104
- return collider;
160
+ const instance = collider instanceof Collider ? collider : new Collider(collider);
161
+ this._colliders.push(instance);
162
+ // Before the body joins a world there is no owner to allocate ids / register
163
+ // with; `_attachToWorld` does that (and the mass/sync pass) for every held
164
+ // collider. Once attached, mirror the world's create order exactly.
165
+ if (this._attached && this._owner) {
166
+ instance._attach(this, this._owner._allocateColliderId());
167
+ this._recomputeMass();
168
+ instance.synchronize(this._transform);
169
+ this._owner._registerCollider(instance);
170
+ }
171
+ return instance;
105
172
  }
106
173
  /**
107
174
  * Set the body's world transform. Resets the body's cached collider geometry
@@ -111,6 +178,7 @@ class PhysicsBody {
111
178
  if (this._destroyed) {
112
179
  throw new Error('PhysicsBody: cannot move a destroyed body.');
113
180
  }
181
+ this.wake();
114
182
  setTransform(this._transform, position.x, position.y, angle);
115
183
  for (const collider of this._colliders) {
116
184
  collider.synchronize(this._transform);
@@ -123,6 +191,168 @@ class PhysicsBody {
123
191
  collider.synchronize(this._transform);
124
192
  }
125
193
  }
194
+ /** World-space centre of mass X (body origin + rotated local centre of mass). */
195
+ get worldCenterOfMassX() {
196
+ return this._transform.x + this._transform.cos * this._comX - this._transform.sin * this._comY;
197
+ }
198
+ /** World-space centre of mass Y. */
199
+ get worldCenterOfMassY() {
200
+ return this._transform.y + this._transform.sin * this._comX + this._transform.cos * this._comY;
201
+ }
202
+ /**
203
+ * Accumulate a world-space force at the centre of mass; integrated on the next
204
+ * sub-step and then cleared. No-op on static/kinematic bodies. Returns `this`.
205
+ */
206
+ applyForce(forceX, forceY) {
207
+ this.wake();
208
+ this._forceX += forceX;
209
+ this._forceY += forceY;
210
+ return this;
211
+ }
212
+ /**
213
+ * Accumulate a torque; integrated on the next sub-step and then cleared. No-op
214
+ * on static/kinematic bodies (and fixed-rotation bodies). Returns `this`.
215
+ */
216
+ applyTorque(torque) {
217
+ this.wake();
218
+ this._torque += torque;
219
+ return this;
220
+ }
221
+ /**
222
+ * Apply an instantaneous world-space impulse at `(pointX, pointY)` (world space;
223
+ * defaults to the centre of mass), changing velocity immediately. No-op on
224
+ * static/kinematic bodies (infinite mass). Returns `this`.
225
+ */
226
+ applyImpulse(impulseX, impulseY, pointX, pointY) {
227
+ if (this.invMass === 0) {
228
+ return this;
229
+ }
230
+ this.wake();
231
+ this.linearVelocityX += impulseX * this.invMass;
232
+ this.linearVelocityY += impulseY * this.invMass;
233
+ if (pointX !== undefined && pointY !== undefined && this.invInertia !== 0) {
234
+ const rx = pointX - this.worldCenterOfMassX;
235
+ const ry = pointY - this.worldCenterOfMassY;
236
+ this.angularVelocity += (rx * impulseY - ry * impulseX) * this.invInertia;
237
+ }
238
+ return this;
239
+ }
240
+ /**
241
+ * @internal — integrate velocity from gravity, accumulated forces/torque and
242
+ * damping over the sub-step `h`. No-op for static/kinematic (infinite mass
243
+ * keeps their velocity solver-driven only). Called once per TGS sub-step, so
244
+ * gravity/forces are applied with `h` each sub-step (`N·h = dt`, same total
245
+ * impulse, but the small-step position error scales with `h²`). The
246
+ * force/torque accumulators are **not** cleared here — they are consumed by
247
+ * every sub-step and cleared once per frame by {@link _finalizePosition}.
248
+ */
249
+ _integrateVelocity(h, gravityX, gravityY) {
250
+ if (this.invMass === 0 || this._sleeping) {
251
+ return;
252
+ }
253
+ this.linearVelocityX += (gravityX * this.gravityScale + this._forceX * this.invMass) * h;
254
+ this.linearVelocityY += (gravityY * this.gravityScale + this._forceY * this.invMass) * h;
255
+ this.angularVelocity += this._torque * this.invInertia * h;
256
+ // Exponential damping (no-op when damping is 0).
257
+ this.linearVelocityX /= 1 + h * this.linearDamping;
258
+ this.linearVelocityY /= 1 + h * this.linearDamping;
259
+ this.angularVelocity /= 1 + h * this.angularDamping;
260
+ }
261
+ /**
262
+ * @internal — accumulate this sub-step's velocity into the per-frame delta
263
+ * position/rotation (TGS sub-stepping). The transform itself is **not** moved
264
+ * here; {@link _finalizePosition} writes the accumulated delta once per frame.
265
+ * Tracking the delta (rather than moving the transform each sub-step) lets the
266
+ * solver recompute contact separation from it without re-running detection or
267
+ * re-syncing collider geometry per sub-step. Static bodies never move.
268
+ */
269
+ _integratePosition(h) {
270
+ if (this.type === 'static' || this._sleeping) {
271
+ return;
272
+ }
273
+ this._deltaPosX += this.linearVelocityX * h;
274
+ this._deltaPosY += this.linearVelocityY * h;
275
+ this._deltaAngle += this.angularVelocity * h;
276
+ // Cache the rotation so the solver can rotate the contact anchors by the
277
+ // live sub-step rotation (TGS): without it the anchors stay frozen at frame
278
+ // start and a tilting tower's restoring torque is mis-computed, so the tilt
279
+ // grows instead of being corrected. Only non-zero rotation pays the trig.
280
+ if (this._deltaAngle !== 0) {
281
+ this._deltaCos = Math.cos(this._deltaAngle);
282
+ this._deltaSin = Math.sin(this._deltaAngle);
283
+ }
284
+ }
285
+ /**
286
+ * @internal — advance the sleep timer over one fixed step `dt`. A body below
287
+ * both velocity thresholds accumulates time; a too-fast body, or one that opts
288
+ * out via {@link allowSleep}, resets it. The sleep/wake decision is made per
289
+ * island by the world (so a stack sleeps as a unit) from {@link _sleepTime}.
290
+ */
291
+ _accumulateSleepTime(dt, linearThreshold, angularThreshold) {
292
+ const tooFast = this.linearVelocityX * this.linearVelocityX + this.linearVelocityY * this.linearVelocityY > linearThreshold * linearThreshold ||
293
+ Math.abs(this.angularVelocity) > angularThreshold;
294
+ this._sleepTime = !this.allowSleep || tooFast ? 0 : this._sleepTime + dt;
295
+ }
296
+ /** @internal — set the sleep state. Sleeping zeroes the velocity; waking resets the sleep timer. */
297
+ _setSleeping(sleeping) {
298
+ if (this._sleeping === sleeping) {
299
+ return;
300
+ }
301
+ this._sleeping = sleeping;
302
+ if (sleeping) {
303
+ this.linearVelocityX = 0;
304
+ this.linearVelocityY = 0;
305
+ this.angularVelocity = 0;
306
+ }
307
+ else {
308
+ this._sleepTime = 0;
309
+ }
310
+ }
311
+ /**
312
+ * Wake the body if it is asleep, resetting its sleep timer. The rest of its
313
+ * island wakes with it on the next step (a contact to an awake body keeps the
314
+ * whole island awake). Returns `this`.
315
+ */
316
+ wake() {
317
+ this._setSleeping(false);
318
+ this._sleepTime = 0;
319
+ return this;
320
+ }
321
+ /**
322
+ * @internal — apply the frame's accumulated delta position/rotation to the
323
+ * transform (rotating about the centre of mass), re-sync collider geometry and
324
+ * clear the force/torque accumulators. Called once per frame after the
325
+ * sub-step loop. Static bodies never move; the force clear still runs (forces
326
+ * are a no-op on infinite mass but the accumulator is reset for consistency).
327
+ */
328
+ _finalizePosition() {
329
+ this._forceX = 0;
330
+ this._forceY = 0;
331
+ this._torque = 0;
332
+ if (this.type === 'static' || (this._deltaPosX === 0 && this._deltaPosY === 0 && this._deltaAngle === 0)) {
333
+ this._resetDelta();
334
+ return;
335
+ }
336
+ const newComX = this.worldCenterOfMassX + this._deltaPosX;
337
+ const newComY = this.worldCenterOfMassY + this._deltaPosY;
338
+ const newAngle = this._transform.angle + this._deltaAngle;
339
+ const cos = Math.cos(newAngle);
340
+ const sin = Math.sin(newAngle);
341
+ // origin = newCoM − R(newAngle)·comLocal (so rotation pivots about the CoM).
342
+ setTransform(this._transform, newComX - (cos * this._comX - sin * this._comY), newComY - (sin * this._comX + cos * this._comY), newAngle);
343
+ this._resetDelta();
344
+ for (const collider of this._colliders) {
345
+ collider.synchronize(this._transform);
346
+ }
347
+ }
348
+ /** Clear the per-frame TGS delta accumulators (position, rotation and its cached cos/sin). */
349
+ _resetDelta() {
350
+ this._deltaPosX = 0;
351
+ this._deltaPosY = 0;
352
+ this._deltaAngle = 0;
353
+ this._deltaCos = 1;
354
+ this._deltaSin = 0;
355
+ }
126
356
  /** Internal: detach a collider (called by the world during destruction). */
127
357
  _removeCollider(collider) {
128
358
  const index = this._colliders.indexOf(collider);
@@ -131,6 +361,37 @@ class PhysicsBody {
131
361
  this._recomputeMass();
132
362
  }
133
363
  }
364
+ /**
365
+ * @internal — join `owner`'s world with the allocated `id`. Allocates ids for
366
+ * and registers every held collider, then aggregates the mass model and
367
+ * synchronises world geometry. Called once by {@link PhysicsWorld.add}.
368
+ *
369
+ * Ordering matters and mirrors the per-collider create path exactly: ids and
370
+ * the body link go on first, then a single mass recompute over the full
371
+ * collider set (so the centre of mass / inertia aggregate is correct), then
372
+ * `synchronize` over every collider (the cached world geometry the broad/narrow
373
+ * phase reads), then world registration last. Recomputing mass before all
374
+ * colliders are linked, or syncing before the mass model exists, would feed the
375
+ * solver a stale CoM and produce subtle integration bugs.
376
+ */
377
+ _attachToWorld(owner, id) {
378
+ if (this._destroyed) {
379
+ throw new Error('PhysicsBody: cannot add a destroyed body to a world.');
380
+ }
381
+ this._owner = owner;
382
+ this._id = id;
383
+ this._attached = true;
384
+ for (const collider of this._colliders) {
385
+ collider._attach(this, owner._allocateColliderId());
386
+ }
387
+ this._recomputeMass();
388
+ for (const collider of this._colliders) {
389
+ collider.synchronize(this._transform);
390
+ }
391
+ for (const collider of this._colliders) {
392
+ owner._registerCollider(collider);
393
+ }
394
+ }
134
395
  /** Internal: mark destroyed (called by the world). */
135
396
  _markDestroyed() {
136
397
  this._destroyed = true;
@@ -1 +1 @@
1
- {"version":3,"file":"PhysicsBody.js","sources":["../../../src/PhysicsBody.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PhysicsBody.js","sources":["../../../src/PhysicsBody.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}