@newkrok/nape-js 3.5.1 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms-full.txt ADDED
@@ -0,0 +1,1620 @@
1
+ # @newkrok/nape-js — Complete API Reference
2
+
3
+ > Modern TypeScript 2D physics engine for the web. Fully typed, tree-shakeable, dual ESM/CJS exports. Originally ported from the Haxe Nape engine by Luca Deltodesco.
4
+
5
+ - **npm**: `npm install @newkrok/nape-js`
6
+ - **GitHub**: https://github.com/NewKrok/nape-js
7
+ - **API docs**: https://newkrok.github.io/nape-js/api/index.html
8
+ - **Demos**: https://newkrok.github.io/nape-js/examples.html
9
+ - **License**: MIT
10
+ - **Version**: 3.5.1
11
+
12
+ ---
13
+
14
+ ## Quick Start
15
+
16
+ ```typescript
17
+ import { Space, Body, BodyType, Vec2, Circle, Polygon } from "@newkrok/nape-js";
18
+
19
+ // Create a physics world with downward gravity
20
+ const space = new Space(new Vec2(0, 600));
21
+
22
+ // Static floor
23
+ const floor = new Body(BodyType.STATIC, new Vec2(400, 550));
24
+ floor.shapes.add(new Polygon(Polygon.box(800, 20)));
25
+ floor.space = space;
26
+
27
+ // Dynamic box
28
+ const box = new Body(BodyType.DYNAMIC, new Vec2(400, 100));
29
+ box.shapes.add(new Polygon(Polygon.box(40, 40)));
30
+ box.space = space;
31
+
32
+ // Dynamic circle
33
+ const ball = new Body(BodyType.DYNAMIC, new Vec2(420, 50));
34
+ ball.shapes.add(new Circle(20));
35
+ ball.space = space;
36
+
37
+ // Game loop — step 60 fps
38
+ function update() {
39
+ space.step(1 / 60);
40
+ for (const body of space.bodies) {
41
+ console.log(`x=${body.position.x.toFixed(1)} y=${body.position.y.toFixed(1)}`);
42
+ }
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Key Concepts
49
+
50
+ ### Architecture
51
+
52
+ All public classes are exported from the main entry point `@newkrok/nape-js`. The engine uses internal `ZPP_*` classes for computation, wrapped by typed public API classes. Users only interact with the public API.
53
+
54
+ ### Object Pooling
55
+
56
+ `Vec2`, `Vec3`, `AABB`, and other frequently-allocated types use internal object pools. Use `Vec2.get()` and `dispose()` for explicit pool management. Weak Vec2s (`Vec2.weak()`) auto-dispose after a single API call.
57
+
58
+ ### Lists
59
+
60
+ All collection properties (e.g., `body.shapes`, `space.bodies`) return `NapeList<T>` which supports ES6 iteration (`for...of`), indexed access (`.at(i)`), and standard mutation (`add()`, `remove()`, `push()`, `pop()`).
61
+
62
+ ### Simulation Loop
63
+
64
+ Call `space.step(deltaTime)` once per frame. The engine handles broadphase collision detection, narrow-phase contact generation, constraint solving, and callback dispatch.
65
+
66
+ ### Callback System
67
+
68
+ Attach listeners to `space.listeners` to receive events. Use `CbType` tags on bodies/shapes/constraints to filter which interactions trigger callbacks.
69
+
70
+ ```typescript
71
+ import { InteractionListener, CbEvent, InteractionType, CbType } from "@newkrok/nape-js";
72
+
73
+ const listener = new InteractionListener(
74
+ CbEvent.BEGIN,
75
+ InteractionType.COLLISION,
76
+ CbType.ANY_BODY,
77
+ CbType.ANY_BODY,
78
+ (cb) => {
79
+ console.log("Collision between", cb.int1, "and", cb.int2);
80
+ }
81
+ );
82
+ space.listeners.add(listener);
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Geometry Types
88
+
89
+ ### Vec2
90
+
91
+ 2D vector used for positions, velocities, forces, and other 2D quantities. Supports object pooling via `Vec2.get()` / `dispose()`, weak references that auto-dispose after a single use, and immutability guards.
92
+
93
+ #### Constructor
94
+
95
+ ```typescript
96
+ new Vec2(x?: number, y?: number)
97
+ ```
98
+
99
+ Creates a Vec2 with the given components. Defaults to (0, 0). Throws if components are NaN.
100
+
101
+ #### Static Factories
102
+
103
+ ```typescript
104
+ Vec2.get(x?: number, y?: number, weak?: boolean): Vec2
105
+ ```
106
+ Allocate a Vec2 from the public object pool. If `weak` is true, the vector auto-disposes after a single API call.
107
+
108
+ ```typescript
109
+ Vec2.weak(x?: number, y?: number): Vec2
110
+ ```
111
+ Allocate a weak Vec2 (auto-disposes after a single use).
112
+
113
+ ```typescript
114
+ Vec2.fromPolar(length: number, angle: number, weak?: boolean): Vec2
115
+ ```
116
+ Create a Vec2 from polar coordinates (length and angle in radians). Returns `(length * cos(angle), length * sin(angle))`.
117
+
118
+ #### Static Methods
119
+
120
+ ```typescript
121
+ Vec2.distance(a: Vec2, b: Vec2): number
122
+ ```
123
+ Euclidean distance between two Vec2s.
124
+
125
+ ```typescript
126
+ Vec2.dsq(a: Vec2, b: Vec2): number
127
+ ```
128
+ Squared Euclidean distance (avoids sqrt).
129
+
130
+ #### Properties
131
+
132
+ | Property | Type | Description |
133
+ |----------|------|-------------|
134
+ | `x` | `number` | The x component. Throws if NaN or immutable. |
135
+ | `y` | `number` | The y component. Throws if NaN or immutable. |
136
+ | `length` | `number` | Magnitude. Setting scales the vector to the given magnitude (throws for zero vectors). |
137
+ | `angle` | `number` | Angle in radians from +x axis. Setting preserves magnitude. |
138
+
139
+ #### Instance Methods
140
+
141
+ | Method | Returns | Description |
142
+ |--------|---------|-------------|
143
+ | `set(vector: Vec2)` | `this` | Copy another Vec2's components into this vector in-place. |
144
+ | `setxy(x: number, y: number)` | `this` | Set both components at once in-place. |
145
+ | `copy(weak?: boolean)` | `Vec2` | Return a new Vec2 with the same components. |
146
+ | `add(vector: Vec2, weak?: boolean)` | `Vec2` | Return `this + other`. |
147
+ | `addMul(vector: Vec2, scalar: number, weak?: boolean)` | `Vec2` | Return `this + other * scalar`. |
148
+ | `sub(vector: Vec2, weak?: boolean)` | `Vec2` | Return `this - other`. |
149
+ | `mul(scalar: number, weak?: boolean)` | `Vec2` | Return `this * scalar`. |
150
+ | `addeq(vector: Vec2)` | `this` | In-place `this += other`. |
151
+ | `subeq(vector: Vec2)` | `this` | In-place `this -= other`. |
152
+ | `muleq(scalar: number)` | `this` | In-place `this *= scalar`. |
153
+ | `dot(vector: Vec2)` | `number` | Dot product. |
154
+ | `cross(vector: Vec2)` | `number` | 2D cross product (scalar). |
155
+ | `lsq()` | `number` | Squared magnitude (avoids sqrt). |
156
+ | `perp(weak?: boolean)` | `Vec2` | Perpendicular vector rotated 90° CCW: `(-y, x)`. |
157
+ | `rotate(angle: number)` | `this` | Rotate by angle (radians) in-place. |
158
+ | `normalise()` | `this` | Normalise to unit length in-place. Throws for zero vectors. |
159
+ | `unit(weak?: boolean)` | `Vec2` | Return a normalised copy. |
160
+ | `reflect(vec: Vec2, weak?: boolean)` | `Vec2` | Reflect `vec` about this vector as a normal axis. |
161
+ | `dispose()` | `void` | Return this Vec2 to the pool. Throws if immutable or in use. |
162
+ | `toString()` | `string` | String in the form `{ x: ... y: ... }`. |
163
+
164
+ ---
165
+
166
+ ### Vec3
167
+
168
+ 3D vector used for constraint impulses.
169
+
170
+ ```typescript
171
+ new Vec3(x?: number, y?: number, z?: number)
172
+ ```
173
+
174
+ #### Static Factories
175
+
176
+ ```typescript
177
+ Vec3.get(x?: number, y?: number, z?: number): Vec3
178
+ ```
179
+
180
+ #### Properties
181
+
182
+ | Property | Type | Description |
183
+ |----------|------|-------------|
184
+ | `x` | `number` | X component |
185
+ | `y` | `number` | Y component |
186
+ | `z` | `number` | Z component |
187
+ | `length` | `number` | Magnitude |
188
+
189
+ #### Methods
190
+
191
+ | Method | Returns | Description |
192
+ |--------|---------|-------------|
193
+ | `lsq()` | `number` | Squared magnitude |
194
+ | `set(v: Vec3)` | `this` | Copy from another Vec3 |
195
+ | `setxyz(x, y, z)` | `this` | Set all components |
196
+ | `xy(weak?: boolean)` | `Vec2` | Extract x, y as a Vec2 |
197
+ | `dispose()` | `void` | Return to pool |
198
+
199
+ ---
200
+
201
+ ### AABB
202
+
203
+ Axis-aligned bounding box defined by min/max corners or x/y/width/height.
204
+
205
+ ```typescript
206
+ new AABB(x?: number, y?: number, width?: number, height?: number)
207
+ ```
208
+
209
+ All values default to 0. Width and height must be >= 0.
210
+
211
+ #### Properties
212
+
213
+ | Property | Type | Description |
214
+ |----------|------|-------------|
215
+ | `x` | `number` | Left edge (minx). Setting shifts box horizontally. |
216
+ | `y` | `number` | Top edge (miny). Setting shifts box vertically. |
217
+ | `width` | `number` | Width (maxx - minx). Must be >= 0. |
218
+ | `height` | `number` | Height (maxy - miny). Must be >= 0. |
219
+ | `min` | `Vec2` | Top-left corner as live Vec2 (mutating updates AABB). |
220
+ | `max` | `Vec2` | Bottom-right corner as live Vec2 (mutating updates AABB). |
221
+
222
+ #### Methods
223
+
224
+ | Method | Returns | Description |
225
+ |--------|---------|-------------|
226
+ | `copy()` | `AABB` | Return a new AABB with the same bounds. |
227
+
228
+ ---
229
+
230
+ ### Ray
231
+
232
+ Ray for raycasting queries with origin, direction, and optional maximum distance.
233
+
234
+ ```typescript
235
+ new Ray(origin: Vec2, direction: Vec2)
236
+ ```
237
+
238
+ #### Static Factories
239
+
240
+ ```typescript
241
+ Ray.fromSegment(start: Vec2, end: Vec2): Ray
242
+ ```
243
+ Create a ray from a line segment.
244
+
245
+ #### Properties
246
+
247
+ | Property | Type | Description |
248
+ |----------|------|-------------|
249
+ | `origin` | `Vec2` | World-space start point |
250
+ | `direction` | `Vec2` | Direction vector |
251
+ | `maxDistance` | `number` | Maximum cast distance (default: infinity) |
252
+
253
+ #### Methods
254
+
255
+ | Method | Returns | Description |
256
+ |--------|---------|-------------|
257
+ | `aabb()` | `AABB` | Bounding box of the ray |
258
+ | `at(t: number, weak?: boolean)` | `Vec2` | Point along ray at parameter t |
259
+ | `copy()` | `Ray` | Create a copy |
260
+
261
+ ---
262
+
263
+ ### Mat23
264
+
265
+ 2×3 affine transformation matrix for 2D transforms.
266
+
267
+ ```typescript
268
+ new Mat23(a?: number, b?: number, c?: number, d?: number, tx?: number, ty?: number)
269
+ ```
270
+
271
+ Represents: `| a b tx |`
272
+ `| c d ty |`
273
+
274
+ #### Static Factories
275
+
276
+ ```typescript
277
+ Mat23.rotation(angle: number): Mat23
278
+ Mat23.translation(tx: number, ty: number): Mat23
279
+ Mat23.scale(sx: number, sy: number): Mat23
280
+ ```
281
+
282
+ #### Properties
283
+
284
+ | Property | Type | Description |
285
+ |----------|------|-------------|
286
+ | `a`, `b`, `c`, `d` | `number` | Rotation/scale components |
287
+ | `tx`, `ty` | `number` | Translation components |
288
+ | `determinant` | `number` | Matrix determinant |
289
+
290
+ #### Methods
291
+
292
+ | Method | Returns | Description |
293
+ |--------|---------|-------------|
294
+ | `copy()` | `Mat23` | Copy this matrix |
295
+ | `set(m: Mat23)` | `this` | Copy from another matrix |
296
+ | `setAs(a, b, c, d, tx, ty)` | `this` | Set all components |
297
+ | `reset()` | `this` | Set to identity |
298
+ | `singular()` | `boolean` | True if non-invertible |
299
+ | `inverse()` | `Mat23` | Return inverse matrix |
300
+ | `transpose()` | `Mat23` | Return transposed matrix |
301
+ | `concat(m: Mat23)` | `Mat23` | Return this * m |
302
+ | `transform(v: Vec2, weak?)` | `Vec2` | Transform a point |
303
+ | `inverseTransform(v: Vec2, weak?)` | `Vec2` | Inverse-transform a point |
304
+ | `orthogonal()` | `boolean` | Test orthogonality |
305
+ | `equiorthogonal()` | `boolean` | Test equi-orthogonality |
306
+ | `orthogonalise()` | `this` | Make orthogonal in-place |
307
+ | `equiorthogonalise()` | `this` | Make equi-orthogonal in-place |
308
+
309
+ ---
310
+
311
+ ### MatMN
312
+
313
+ Variable M×N matrix for constraint math.
314
+
315
+ ```typescript
316
+ new MatMN(rows: number, cols: number)
317
+ ```
318
+
319
+ #### Methods
320
+
321
+ | Method | Returns | Description |
322
+ |--------|---------|-------------|
323
+ | `x(row, col)` | `number` | Get element |
324
+ | `setx(row, col, value)` | `void` | Set element |
325
+ | `transpose()` | `MatMN` | Return transposed matrix |
326
+ | `mul(m: MatMN)` | `MatMN` | Return this * m |
327
+
328
+ ---
329
+
330
+ ### Geom
331
+
332
+ Static geometry utility class.
333
+
334
+ ```typescript
335
+ Geom.distanceBody(body1: Body, body2: Body, out1?: Vec2, out2?: Vec2): number
336
+ Geom.distance(shape1: Shape, shape2: Shape, out1?: Vec2, out2?: Vec2): number
337
+ Geom.intersectsBody(body1: Body, body2: Body): boolean
338
+ Geom.intersects(shape1: Shape, shape2: Shape): boolean
339
+ Geom.contains(shape1: Shape, shape2: Shape): boolean
340
+ ```
341
+
342
+ ---
343
+
344
+ ### GeomPoly
345
+
346
+ Polygon data container for geometry operations (decomposition, winding, containment).
347
+
348
+ ```typescript
349
+ new GeomPoly(vertices?: Array<Vec2> | Vec2List | GeomPoly)
350
+ ```
351
+
352
+ #### Properties and Methods
353
+
354
+ | Member | Type | Description |
355
+ |--------|------|-------------|
356
+ | `empty()` | `boolean` | True if no vertices |
357
+ | `size()` | `number` | Vertex count |
358
+ | `area()` | `number` | Signed area |
359
+ | `winding()` | `Winding` | CLOCKWISE or COUNTER_CLOCKWISE |
360
+ | `contains(point: Vec2)` | `boolean` | Point-in-polygon test |
361
+ | `isClockwise()` | `boolean` | Clockwise winding |
362
+ | `isConvex()` | `boolean` | Convexity test |
363
+ | `isSimple()` | `boolean` | Simple polygon test |
364
+ | `simplify(tolerance)` | `GeomPoly` | Simplify polygon |
365
+ | `convexDecomposition()` | `GeomPolyList` | Decompose into convex parts |
366
+ | `triangularDecomposition()` | `GeomPolyList` | Triangulate |
367
+ | `inflate(distance)` | `GeomPoly` | Offset polygon |
368
+ | `transform(matrix: Mat23)` | `GeomPoly` | Apply transformation |
369
+
370
+ ---
371
+
372
+ ### MarchingSquares
373
+
374
+ Isosurface extraction from scalar fields.
375
+
376
+ ```typescript
377
+ MarchingSquares.run(
378
+ iso: (x: number, y: number) => number,
379
+ bounds: AABB,
380
+ cellsize: Vec2,
381
+ quality?: number,
382
+ subgrid?: Vec2,
383
+ combine?: boolean,
384
+ output?: GeomPolyList
385
+ ): GeomPolyList
386
+ ```
387
+
388
+ ---
389
+
390
+ ## Physics Types
391
+
392
+ ### Body
393
+
394
+ Rigid body in the physics world. Can be DYNAMIC, STATIC, or KINEMATIC.
395
+
396
+ ```typescript
397
+ new Body(type?: BodyType, position?: Vec2)
398
+ ```
399
+
400
+ #### Properties
401
+
402
+ | Property | Type | R/W | Description |
403
+ |----------|------|-----|-------------|
404
+ | `type` | `BodyType` | R/W | DYNAMIC, STATIC, or KINEMATIC |
405
+ | `position` | `Vec2` | R/W | World-space position (live Vec2) |
406
+ | `rotation` | `number` | R/W | Rotation in radians |
407
+ | `velocity` | `Vec2` | R/W | Linear velocity (live Vec2) |
408
+ | `angularVel` | `number` | R/W | Angular velocity (rad/s) |
409
+ | `force` | `Vec2` | R/W | Accumulated force (live Vec2) |
410
+ | `torque` | `number` | R/W | Accumulated torque |
411
+ | `mass` | `number` | R/W | Total mass |
412
+ | `inertia` | `number` | R/W | Rotational inertia |
413
+ | `massMode` | `MassMode` | R/W | DEFAULT or FIXED |
414
+ | `inertiaMode` | `InertiaMode` | R/W | DEFAULT or FIXED |
415
+ | `gravMassMode` | `GravMassMode` | R/W | DEFAULT, FIXED, or SCALED |
416
+ | `gravMass` | `number` | R/W | Gravity mass (when gravMassMode is FIXED) |
417
+ | `gravMassScale` | `number` | R/W | Gravity mass scale (when gravMassMode is SCALED) |
418
+ | `shapes` | `NapeList<Shape>` | R | Attached shapes |
419
+ | `constraints` | `NapeList<Constraint>` | R | Connected constraints |
420
+ | `arbiters` | `NapeList<Arbiter>` | R | Active interaction arbiters |
421
+ | `compound` | `Compound \| null` | R/W | Parent compound |
422
+ | `space` | `Space \| null` | R/W | Parent space (set to add body to world) |
423
+ | `isSleeping` | `boolean` | R | True if body is asleep |
424
+ | `isStatic` | `boolean` | R | True if type is STATIC |
425
+ | `isDynamic` | `boolean` | R | True if type is DYNAMIC |
426
+ | `isKinematic` | `boolean` | R | True if type is KINEMATIC |
427
+ | `worldCOM` | `Vec2` | R | World-space center of mass (live Vec2) |
428
+ | `localCOM` | `Vec2` | R/W | Local-space center of mass |
429
+ | `kinematicVel` | `Vec2` | R/W | Kinematic velocity (live Vec2) |
430
+ | `kinAngVel` | `number` | R/W | Kinematic angular velocity |
431
+ | `surfaceVel` | `Vec2` | R/W | Surface velocity (for conveyor belt effects) |
432
+ | `cbTypes` | `NapeList<CbType>` | R | Callback type tags |
433
+ | `group` | `InteractionGroup \| null` | R/W | Interaction group |
434
+ | `userData` | `Record<string, unknown>` | R | User data object |
435
+ | `allowMovement` | `boolean` | R/W | If false, body cannot translate |
436
+ | `allowRotation` | `boolean` | R/W | If false, body cannot rotate |
437
+ | `bounds` | `AABB` | R | World-space axis-aligned bounding box |
438
+ | `id` | `number` | R | Unique body identifier |
439
+
440
+ #### Methods
441
+
442
+ | Method | Returns | Description |
443
+ |--------|---------|-------------|
444
+ | `applyImpulse(impulse: Vec2, pos?: Vec2)` | `void` | Apply a linear impulse at a world point |
445
+ | `applyAngularImpulse(impulse: number)` | `void` | Apply an angular impulse |
446
+ | `setVelocityFromTarget(target: Vec2, targetRotation: number, dt: number)` | `void` | Set velocity to reach target position/rotation in dt seconds |
447
+ | `rotate(centre: Vec2, angle: number)` | `Body` | Rotate body around a world point |
448
+ | `translate(displacement: Vec2)` | `Body` | Translate body by displacement |
449
+ | `localPointToWorld(point: Vec2, weak?: boolean)` | `Vec2` | Convert local point to world coordinates |
450
+ | `worldPointToLocal(point: Vec2, weak?: boolean)` | `Vec2` | Convert world point to local coordinates |
451
+ | `localVectorToWorld(vector: Vec2, weak?: boolean)` | `Vec2` | Convert local vector to world coordinates |
452
+ | `worldVectorToLocal(vector: Vec2, weak?: boolean)` | `Vec2` | Convert world vector to local coordinates |
453
+ | `contains(point: Vec2)` | `boolean` | Test if any shape contains the point |
454
+ | `copy()` | `Body` | Deep copy the body (shapes, constraints copied) |
455
+ | `validateShapes()` | `void` | Validate all attached shapes |
456
+ | `wake()` | `void` | Wake a sleeping body |
457
+
458
+ ---
459
+
460
+ ### BodyType (Enum)
461
+
462
+ | Value | Description |
463
+ |-------|-------------|
464
+ | `BodyType.STATIC` | Immovable body, infinite mass |
465
+ | `BodyType.DYNAMIC` | Fully simulated body affected by forces and collisions |
466
+ | `BodyType.KINEMATIC` | Moved by code only (not affected by forces), but interacts with dynamic bodies |
467
+
468
+ ---
469
+
470
+ ### Material
471
+
472
+ Physical material properties applied to shapes.
473
+
474
+ ```typescript
475
+ new Material(
476
+ elasticity?: number, // Bounciness (0 = no bounce, 1 = perfect bounce). Default: 0.0
477
+ dynamicFriction?: number, // Kinetic friction coefficient. Default: 1.0
478
+ staticFriction?: number, // Static friction coefficient. Default: 2.0
479
+ density?: number, // Mass per unit area. Default: 1.0
480
+ rollingFriction?: number // Rolling friction coefficient. Default: 0.001
481
+ )
482
+ ```
483
+
484
+ #### Properties
485
+
486
+ | Property | Type | Description |
487
+ |----------|------|-------------|
488
+ | `elasticity` | `number` | Bounciness. Combined per-pair: `max(e1, e2)` if either > 0, else `e1 * e2`. |
489
+ | `dynamicFriction` | `number` | Kinetic friction. Combined: `sqrt(f1 * f2)`. Must be >= 0. |
490
+ | `staticFriction` | `number` | Static friction. Combined: `sqrt(f1 * f2)`. Must be >= 0. |
491
+ | `density` | `number` | Mass per area. Must be > 0. Internally stored as `value / 1000`. |
492
+ | `rollingFriction` | `number` | Rolling friction. Combined: `sqrt(f1 * f2)`. Must be >= 0. |
493
+ | `userData` | `Record<string, unknown>` | User data. |
494
+
495
+ #### Static Presets
496
+
497
+ ```typescript
498
+ Material.wood() // elasticity=0.4, dynamicFriction=0.2, staticFriction=0.4, density=0.5, rollingFriction=0.001
499
+ Material.steel() // elasticity=0.2, dynamicFriction=0.57, staticFriction=0.74, density=7.8, rollingFriction=0.001
500
+ Material.ice() // elasticity=0.3, dynamicFriction=0.03, staticFriction=0.1, density=0.9, rollingFriction=0.0001
501
+ Material.rubber() // elasticity=0.8, dynamicFriction=1.0, staticFriction=1.0, density=1.5, rollingFriction=0.01
502
+ Material.glass() // elasticity=0.4, dynamicFriction=0.94, staticFriction=0.94, density=2.6, rollingFriction=0.002
503
+ Material.sand() // elasticity=0.05, dynamicFriction=0.6, staticFriction=0.8, density=1.6, rollingFriction=0.5
504
+ ```
505
+
506
+ #### Methods
507
+
508
+ | Method | Returns | Description |
509
+ |--------|---------|-------------|
510
+ | `copy()` | `Material` | Deep copy |
511
+
512
+ ---
513
+
514
+ ### FluidProperties
515
+
516
+ Fluid simulation parameters for sensor-enabled shapes.
517
+
518
+ ```typescript
519
+ new FluidProperties(density?: number, viscosity?: number)
520
+ ```
521
+
522
+ | Property | Type | Description |
523
+ |----------|------|-------------|
524
+ | `density` | `number` | Fluid density (buoyancy). Default: 2.0 |
525
+ | `viscosity` | `number` | Fluid viscosity (drag). Default: 3.0 |
526
+ | `gravity` | `Vec2` | Override gravity inside fluid (live Vec2) |
527
+ | `shapes` | `NapeList<Shape>` | Shapes using these properties |
528
+ | `userData` | `Record<string, unknown>` | User data |
529
+
530
+ ---
531
+
532
+ ### Compound
533
+
534
+ Hierarchical grouping of bodies, constraints, and sub-compounds.
535
+
536
+ ```typescript
537
+ new Compound()
538
+ ```
539
+
540
+ | Property | Type | Description |
541
+ |----------|------|-------------|
542
+ | `bodies` | `NapeList<Body>` | Child bodies |
543
+ | `constraints` | `NapeList<Constraint>` | Child constraints |
544
+ | `compounds` | `NapeList<Compound>` | Child compounds |
545
+ | `compound` | `Compound \| null` | Parent compound |
546
+ | `space` | `Space \| null` | Parent space |
547
+ | `cbTypes` | `NapeList<CbType>` | Callback type tags |
548
+ | `userData` | `Record<string, unknown>` | User data |
549
+
550
+ | Method | Returns | Description |
551
+ |--------|---------|-------------|
552
+ | `copy()` | `Compound` | Deep copy |
553
+ | `breakApart()` | `void` | Flatten: move all children to parent space/compound |
554
+ | `visitBodies(fn)` | `void` | Call fn for every body (recursive) |
555
+ | `visitConstraints(fn)` | `void` | Call fn for every constraint (recursive) |
556
+ | `visitCompounds(fn)` | `void` | Call fn for every sub-compound (recursive) |
557
+ | `COM(weak?)` | `Vec2` | Center of mass of all bodies |
558
+ | `translate(displacement: Vec2)` | `void` | Translate all bodies |
559
+ | `rotate(centre: Vec2, angle: number)` | `void` | Rotate all bodies around point |
560
+
561
+ ---
562
+
563
+ ### Interactor (Base Class)
564
+
565
+ Base class for Body, Shape, Compound. Provides interaction tagging.
566
+
567
+ | Property | Type | Description |
568
+ |----------|------|-------------|
569
+ | `id` | `number` | Unique identifier |
570
+ | `userData` | `Record<string, unknown>` | User data |
571
+ | `group` | `InteractionGroup \| null` | Interaction group |
572
+ | `cbTypes` | `NapeList<CbType>` | Callback type tags |
573
+
574
+ | Method | Returns | Description |
575
+ |--------|---------|-------------|
576
+ | `isShape()` | `boolean` | Type check |
577
+ | `isBody()` | `boolean` | Type check |
578
+ | `isCompound()` | `boolean` | Type check |
579
+ | `castBody` | `Body \| null` | Cast to Body |
580
+ | `castShape` | `Shape \| null` | Cast to Shape |
581
+ | `castCompound` | `Compound \| null` | Cast to Compound |
582
+
583
+ ---
584
+
585
+ ### MassMode / InertiaMode / GravMassMode (Enums)
586
+
587
+ **MassMode**: `DEFAULT` (auto-calculated from shapes) | `FIXED` (user-set)
588
+
589
+ **InertiaMode**: `DEFAULT` (auto-calculated) | `FIXED` (user-set)
590
+
591
+ **GravMassMode**: `DEFAULT` (use mass) | `FIXED` (user-set gravMass) | `SCALED` (mass * gravMassScale)
592
+
593
+ ---
594
+
595
+ ## Shape Types
596
+
597
+ ### Shape (Base Class)
598
+
599
+ Base class for Circle and Polygon. Cannot be instantiated directly.
600
+
601
+ | Property | Type | R/W | Description |
602
+ |----------|------|-----|-------------|
603
+ | `type` | `ShapeType` | R | CIRCLE or POLYGON |
604
+ | `body` | `Body \| null` | R/W | Parent body (set to attach) |
605
+ | `worldCOM` | `Vec2` | R | World-space center of mass |
606
+ | `localCOM` | `Vec2` | R/W | Local-space center of mass |
607
+ | `area` | `number` | R | Shape area |
608
+ | `inertia` | `number` | R | Rotational inertia |
609
+ | `angDrag` | `number` | R | Angular drag coefficient |
610
+ | `bounds` | `AABB` | R | World-space bounding box |
611
+ | `material` | `Material` | R/W | Physical material |
612
+ | `filter` | `InteractionFilter` | R/W | Collision/sensor/fluid filter |
613
+ | `fluidProperties` | `FluidProperties` | R/W | Fluid properties |
614
+ | `fluidEnabled` | `boolean` | R/W | Enable fluid interaction |
615
+ | `sensorEnabled` | `boolean` | R/W | Enable sensor mode (detects overlap without physical response) |
616
+ | `cbTypes` | `NapeList<CbType>` | R | Callback type tags |
617
+ | `userData` | `Record<string, unknown>` | R | User data |
618
+
619
+ | Method | Returns | Description |
620
+ |--------|---------|-------------|
621
+ | `isCircle()` | `boolean` | Type check |
622
+ | `isPolygon()` | `boolean` | Type check |
623
+ | `castCircle` | `Circle \| null` | Cast to Circle |
624
+ | `castPolygon` | `Polygon \| null` | Cast to Polygon |
625
+ | `contains(point: Vec2)` | `boolean` | Point containment test |
626
+ | `translate(displacement: Vec2)` | `Shape` | Translate localCOM |
627
+ | `scale(sx: number, sy: number)` | `Shape` | Scale shape |
628
+ | `rotate(angle: number)` | `Shape` | Rotate shape |
629
+ | `transform(matrix: Mat23)` | `Shape` | Apply matrix transform |
630
+ | `copy()` | `Shape` | Deep copy |
631
+
632
+ ---
633
+
634
+ ### Circle
635
+
636
+ Circular collision shape.
637
+
638
+ ```typescript
639
+ new Circle(
640
+ radius?: number, // Default: 50
641
+ localCOM?: Vec2, // Local offset from body center
642
+ material?: Material, // Physical material
643
+ filter?: InteractionFilter // Interaction filter
644
+ )
645
+ ```
646
+
647
+ | Property | Type | Description |
648
+ |----------|------|-------------|
649
+ | `radius` | `number` | Circle radius. Must be > 0. |
650
+
651
+ All Shape properties are inherited.
652
+
653
+ ---
654
+
655
+ ### Polygon
656
+
657
+ Convex polygon collision shape.
658
+
659
+ ```typescript
660
+ new Polygon(
661
+ localVerts?: Array<Vec2> | Vec2List | GeomPoly,
662
+ material?: Material,
663
+ filter?: InteractionFilter
664
+ )
665
+ ```
666
+
667
+ #### Static Factories
668
+
669
+ ```typescript
670
+ Polygon.box(width: number, height: number, weak?: boolean): Array<Vec2>
671
+ ```
672
+ Create vertices for an axis-aligned rectangle centered at origin.
673
+
674
+ ```typescript
675
+ Polygon.rect(x: number, y: number, width: number, height: number, weak?: boolean): Array<Vec2>
676
+ ```
677
+ Create vertices for a rectangle at (x, y).
678
+
679
+ ```typescript
680
+ Polygon.regular(xRadius: number, yRadius: number, edgeCount: number, angleOffset?: number, weak?: boolean): Array<Vec2>
681
+ ```
682
+ Create vertices for a regular polygon.
683
+
684
+ #### Properties
685
+
686
+ | Property | Type | Description |
687
+ |----------|------|-------------|
688
+ | `localVerts` | `Vec2List` | Vertices in local coordinates |
689
+ | `worldVerts` | `Vec2List` | Vertices in world coordinates (read-only) |
690
+ | `edges` | `EdgeList` | Polygon edges (read-only) |
691
+
692
+ #### Methods
693
+
694
+ | Method | Returns | Description |
695
+ |--------|---------|-------------|
696
+ | `validity()` | `ValidationResult` | Check if polygon is valid (VALID, DEGENERATE, CONCAVE, SELF_INTERSECTING) |
697
+
698
+ ---
699
+
700
+ ### Edge (Read-Only)
701
+
702
+ Polygon edge data. Obtained from `polygon.edges`.
703
+
704
+ | Property | Type | Description |
705
+ |----------|------|-------------|
706
+ | `polygon` | `Polygon` | Parent polygon |
707
+ | `localNormal` | `Vec2` | Edge normal in local space |
708
+ | `worldNormal` | `Vec2` | Edge normal in world space |
709
+ | `length` | `number` | Edge length |
710
+ | `localProjection` | `number` | Normal projection in local space |
711
+ | `worldProjection` | `number` | Normal projection in world space |
712
+ | `localVertex1`, `localVertex2` | `Vec2` | Endpoints in local space |
713
+ | `worldVertex1`, `worldVertex2` | `Vec2` | Endpoints in world space |
714
+
715
+ ---
716
+
717
+ ### ShapeType (Enum)
718
+
719
+ | Value | Description |
720
+ |-------|-------------|
721
+ | `ShapeType.CIRCLE` | Circular shape |
722
+ | `ShapeType.POLYGON` | Polygon shape |
723
+
724
+ ---
725
+
726
+ ### ValidationResult (Enum)
727
+
728
+ | Value | Description |
729
+ |-------|-------------|
730
+ | `ValidationResult.VALID` | Polygon is valid |
731
+ | `ValidationResult.DEGENERATE` | Polygon has degenerate geometry (colinear points, zero area) |
732
+ | `ValidationResult.CONCAVE` | Polygon is concave (must be convex) |
733
+ | `ValidationResult.SELF_INTERSECTING` | Polygon edges cross |
734
+
735
+ ---
736
+
737
+ ## Constraint Types
738
+
739
+ ### Constraint (Base Class)
740
+
741
+ Base class for all joints. Cannot be instantiated directly.
742
+
743
+ #### Properties
744
+
745
+ | Property | Type | R/W | Description |
746
+ |----------|------|-----|-------------|
747
+ | `active` | `boolean` | R/W | Enable/disable constraint |
748
+ | `stiff` | `boolean` | R/W | If true, hard constraint. If false, spring-like. |
749
+ | `frequency` | `number` | R/W | Spring frequency in Hz (when stiff=false). Must be > 0. |
750
+ | `damping` | `number` | R/W | Damping ratio (when stiff=false). 0=no damping, 1=critical. Must be >= 0. |
751
+ | `maxForce` | `number` | R/W | Maximum constraint force. Default: infinity. |
752
+ | `maxError` | `number` | R/W | Maximum positional error before breaking. Default: infinity. |
753
+ | `breakUnderForce` | `boolean` | R/W | Break constraint when maxForce exceeded. |
754
+ | `breakUnderError` | `boolean` | R/W | Break constraint when maxError exceeded. |
755
+ | `removeOnBreak` | `boolean` | R/W | Remove from space when broken. |
756
+ | `isSleeping` | `boolean` | R | True if constraint is asleep |
757
+ | `space` | `Space \| null` | R/W | Parent space (set to add to world) |
758
+ | `compound` | `Compound \| null` | R/W | Parent compound |
759
+ | `cbTypes` | `NapeList<CbType>` | R | Callback type tags |
760
+ | `userData` | `Record<string, unknown>` | R | User data |
761
+ | `debugDraw` | `boolean` | R/W | Enable debug drawing |
762
+
763
+ #### Methods
764
+
765
+ | Method | Returns | Description |
766
+ |--------|---------|-------------|
767
+ | `impulse()` | `MatMN` | Current constraint impulse |
768
+ | `bodyImpulse(body: Body)` | `Vec3` | Impulse on a specific body |
769
+ | `visitBodies(fn: (body: Body) => void)` | `void` | Iterate connected bodies |
770
+ | `copy()` | `Constraint` | Deep copy |
771
+
772
+ ---
773
+
774
+ ### PivotJoint
775
+
776
+ Pin two bodies at a shared anchor point (hinge/pin joint).
777
+
778
+ ```typescript
779
+ new PivotJoint(body1?: Body, body2?: Body, anchor1?: Vec2, anchor2?: Vec2)
780
+ ```
781
+
782
+ Constrains two anchor points to remain coincident. 2 degrees of freedom.
783
+
784
+ | Property | Type | Description |
785
+ |----------|------|-------------|
786
+ | `body1` | `Body \| null` | First body (null = world) |
787
+ | `body2` | `Body \| null` | Second body (null = world) |
788
+ | `anchor1` | `Vec2` | Anchor point in body1 local coords |
789
+ | `anchor2` | `Vec2` | Anchor point in body2 local coords |
790
+
791
+ ---
792
+
793
+ ### DistanceJoint
794
+
795
+ Constrain distance between two anchor points.
796
+
797
+ ```typescript
798
+ new DistanceJoint(
799
+ body1?: Body, body2?: Body,
800
+ anchor1?: Vec2, anchor2?: Vec2,
801
+ jointMin?: number, jointMax?: number
802
+ )
803
+ ```
804
+
805
+ Enforces: `jointMin <= distance(anchor1, anchor2) <= jointMax`
806
+
807
+ | Property | Type | Description |
808
+ |----------|------|-------------|
809
+ | `body1` | `Body \| null` | First body |
810
+ | `body2` | `Body \| null` | Second body |
811
+ | `anchor1` | `Vec2` | Anchor on body1 |
812
+ | `anchor2` | `Vec2` | Anchor on body2 |
813
+ | `jointMin` | `number` | Minimum allowed distance |
814
+ | `jointMax` | `number` | Maximum allowed distance |
815
+
816
+ | Method | Returns | Description |
817
+ |--------|---------|-------------|
818
+ | `isSlack()` | `boolean` | True if constraint is not active (distance within limits) |
819
+
820
+ ---
821
+
822
+ ### AngleJoint
823
+
824
+ Constrain relative rotation angle between two bodies.
825
+
826
+ ```typescript
827
+ new AngleJoint(
828
+ body1?: Body, body2?: Body,
829
+ jointMin?: number, jointMax?: number,
830
+ ratio?: number
831
+ )
832
+ ```
833
+
834
+ Enforces: `jointMin <= body2.rotation - ratio * body1.rotation <= jointMax`
835
+
836
+ | Property | Type | Description |
837
+ |----------|------|-------------|
838
+ | `body1` | `Body \| null` | First body |
839
+ | `body2` | `Body \| null` | Second body |
840
+ | `jointMin` | `number` | Minimum relative angle (radians) |
841
+ | `jointMax` | `number` | Maximum relative angle (radians) |
842
+ | `ratio` | `number` | Gear ratio (default 1.0) |
843
+
844
+ | Method | Returns | Description |
845
+ |--------|---------|-------------|
846
+ | `isSlack()` | `boolean` | True if constraint is not active |
847
+
848
+ ---
849
+
850
+ ### WeldJoint
851
+
852
+ Fix relative position and rotation (rigid weld).
853
+
854
+ ```typescript
855
+ new WeldJoint(
856
+ body1?: Body, body2?: Body,
857
+ anchor1?: Vec2, anchor2?: Vec2,
858
+ phase?: number
859
+ )
860
+ ```
861
+
862
+ | Property | Type | Description |
863
+ |----------|------|-------------|
864
+ | `body1` | `Body \| null` | First body |
865
+ | `body2` | `Body \| null` | Second body |
866
+ | `anchor1` | `Vec2` | Anchor on body1 |
867
+ | `anchor2` | `Vec2` | Anchor on body2 |
868
+ | `phase` | `number` | Target relative angle offset (radians, default 0) |
869
+
870
+ ---
871
+
872
+ ### MotorJoint
873
+
874
+ Drive relative angular velocity to a target rate.
875
+
876
+ ```typescript
877
+ new MotorJoint(body1?: Body, body2?: Body, rate?: number, ratio?: number)
878
+ ```
879
+
880
+ | Property | Type | Description |
881
+ |----------|------|-------------|
882
+ | `body1` | `Body \| null` | First body |
883
+ | `body2` | `Body \| null` | Second body |
884
+ | `rate` | `number` | Target angular velocity (rad/s, default 0) |
885
+ | `ratio` | `number` | Gear ratio (default 1.0) |
886
+
887
+ ---
888
+
889
+ ### LineJoint
890
+
891
+ Constrain body2's anchor to slide along a line through body1's anchor.
892
+
893
+ ```typescript
894
+ new LineJoint(
895
+ body1?: Body, body2?: Body,
896
+ anchor1?: Vec2, anchor2?: Vec2,
897
+ direction?: Vec2,
898
+ jointMin?: number, jointMax?: number
899
+ )
900
+ ```
901
+
902
+ | Property | Type | Description |
903
+ |----------|------|-------------|
904
+ | `body1` | `Body \| null` | Body defining the line |
905
+ | `body2` | `Body \| null` | Body constrained to the line |
906
+ | `anchor1` | `Vec2` | Line origin on body1 |
907
+ | `anchor2` | `Vec2` | Anchor on body2 |
908
+ | `direction` | `Vec2` | Line direction (local to body1) |
909
+ | `jointMin` | `number` | Minimum displacement along line |
910
+ | `jointMax` | `number` | Maximum displacement along line |
911
+
912
+ ---
913
+
914
+ ### PulleyJoint
915
+
916
+ Constrain weighted sum of two distances (rope/pulley system).
917
+
918
+ ```typescript
919
+ new PulleyJoint(
920
+ body1?: Body, body2?: Body, body3?: Body, body4?: Body,
921
+ anchor1?: Vec2, anchor2?: Vec2, anchor3?: Vec2, anchor4?: Vec2,
922
+ jointMin?: number, jointMax?: number,
923
+ ratio?: number
924
+ )
925
+ ```
926
+
927
+ Enforces: `jointMin <= dist(body1.anchor1, body2.anchor2) + ratio * dist(body3.anchor3, body4.anchor4) <= jointMax`
928
+
929
+ | Property | Type | Description |
930
+ |----------|------|-------------|
931
+ | `body1` - `body4` | `Body \| null` | Four bodies (null = world) |
932
+ | `anchor1` - `anchor4` | `Vec2` | Four anchor points |
933
+ | `jointMin` | `number` | Minimum combined distance |
934
+ | `jointMax` | `number` | Maximum combined distance |
935
+ | `ratio` | `number` | Weight ratio for second distance (default 1.0) |
936
+
937
+ | Method | Returns | Description |
938
+ |--------|---------|-------------|
939
+ | `isSlack()` | `boolean` | True if constraint is not active |
940
+
941
+ ---
942
+
943
+ ## Callbacks & Events
944
+
945
+ ### CbType
946
+
947
+ Callback type tag. Attach to bodies, shapes, or constraints to control which events trigger listeners.
948
+
949
+ ```typescript
950
+ new CbType()
951
+ ```
952
+
953
+ #### Static Singletons
954
+
955
+ | Singleton | Description |
956
+ |-----------|-------------|
957
+ | `CbType.ANY_BODY` | Matches all bodies |
958
+ | `CbType.ANY_SHAPE` | Matches all shapes |
959
+ | `CbType.ANY_COMPOUND` | Matches all compounds |
960
+ | `CbType.ANY_CONSTRAINT` | Matches all constraints (must be manually added to constraints) |
961
+
962
+ #### Properties
963
+
964
+ | Property | Type | Description |
965
+ |----------|------|-------------|
966
+ | `id` | `number` | Unique identifier |
967
+ | `userData` | `Record<string, unknown>` | User data |
968
+
969
+ #### Methods
970
+
971
+ | Method | Returns | Description |
972
+ |--------|---------|-------------|
973
+ | `including(types...)` | `OptionType` | Create filter matching this AND given types |
974
+ | `excluding(types...)` | `OptionType` | Create filter matching this but NOT given types |
975
+
976
+ ---
977
+
978
+ ### CbEvent (Enum)
979
+
980
+ | Value | Description | Used with |
981
+ |-------|-------------|-----------|
982
+ | `CbEvent.BEGIN` | Interaction just started | InteractionListener |
983
+ | `CbEvent.ONGOING` | Interaction continues | InteractionListener |
984
+ | `CbEvent.END` | Interaction ended | InteractionListener |
985
+ | `CbEvent.WAKE` | Body woke up | BodyListener |
986
+ | `CbEvent.SLEEP` | Body fell asleep | BodyListener |
987
+ | `CbEvent.BREAK` | Constraint broke | ConstraintListener |
988
+ | `CbEvent.PRE` | Pre-collision phase | PreListener (internal) |
989
+
990
+ ---
991
+
992
+ ### InteractionType (Enum)
993
+
994
+ | Value | Description |
995
+ |-------|-------------|
996
+ | `InteractionType.COLLISION` | Physical collision |
997
+ | `InteractionType.SENSOR` | Overlap detection only (no physics response) |
998
+ | `InteractionType.FLUID` | Fluid interaction (buoyancy, drag) |
999
+ | `InteractionType.ANY` | Match any interaction type |
1000
+
1001
+ ---
1002
+
1003
+ ### InteractionListener
1004
+
1005
+ Listen for collision, sensor, or fluid interaction events.
1006
+
1007
+ ```typescript
1008
+ new InteractionListener(
1009
+ event: CbEvent, // BEGIN, ONGOING, or END
1010
+ interactionType: InteractionType, // COLLISION, SENSOR, FLUID, or ANY
1011
+ options1: CbType | OptionType, // Filter for first interactor
1012
+ options2: CbType | OptionType, // Filter for second interactor
1013
+ handler: (cb: InteractionCallback) => void,
1014
+ precedence?: number // Lower runs first (default 0)
1015
+ )
1016
+ ```
1017
+
1018
+ #### Properties
1019
+
1020
+ | Property | Type | Description |
1021
+ |----------|------|-------------|
1022
+ | `options1` | `OptionType` | Filter for first interactor |
1023
+ | `options2` | `OptionType` | Filter for second interactor |
1024
+ | `handler` | `Function` | Callback function |
1025
+ | `interactionType` | `InteractionType` | Event interaction type |
1026
+ | `allowSleepingCallbacks` | `boolean` | Fire callbacks even for sleeping interactions |
1027
+
1028
+ #### Example
1029
+
1030
+ ```typescript
1031
+ const ct = new CbType();
1032
+ ball.cbTypes.add(ct);
1033
+
1034
+ space.listeners.add(new InteractionListener(
1035
+ CbEvent.BEGIN,
1036
+ InteractionType.COLLISION,
1037
+ ct,
1038
+ CbType.ANY_BODY,
1039
+ (cb) => {
1040
+ console.log("Ball hit something!");
1041
+ const arb = cb.arbiters.at(0).collisionArbiter;
1042
+ console.log("Normal:", arb.normal.x, arb.normal.y);
1043
+ }
1044
+ ));
1045
+ ```
1046
+
1047
+ ---
1048
+
1049
+ ### BodyListener
1050
+
1051
+ Listen for body wake/sleep events.
1052
+
1053
+ ```typescript
1054
+ new BodyListener(
1055
+ event: CbEvent, // WAKE or SLEEP
1056
+ options: CbType | OptionType, // Filter
1057
+ handler: (cb: BodyCallback) => void,
1058
+ precedence?: number
1059
+ )
1060
+ ```
1061
+
1062
+ ---
1063
+
1064
+ ### ConstraintListener
1065
+
1066
+ Listen for constraint events (BREAK, WAKE, SLEEP).
1067
+
1068
+ ```typescript
1069
+ new ConstraintListener(
1070
+ event: CbEvent, // WAKE, SLEEP, or BREAK
1071
+ options: CbType | OptionType, // Filter
1072
+ handler: (cb: ConstraintCallback) => void,
1073
+ precedence?: number
1074
+ )
1075
+ ```
1076
+
1077
+ ---
1078
+
1079
+ ### PreListener
1080
+
1081
+ Pre-collision handler for filtering or modifying collisions before the solver runs.
1082
+
1083
+ ```typescript
1084
+ new PreListener(
1085
+ interactionType: InteractionType,
1086
+ options1: CbType | OptionType,
1087
+ options2: CbType | OptionType,
1088
+ handler: (cb: PreCallback) => PreFlag | null,
1089
+ precedence?: number,
1090
+ pure?: boolean // If true, result is cached (default false)
1091
+ )
1092
+ ```
1093
+
1094
+ Return `PreFlag.ACCEPT`, `PreFlag.IGNORE`, `PreFlag.ACCEPT_ONCE`, or `PreFlag.IGNORE_ONCE` to control collision response.
1095
+
1096
+ ---
1097
+
1098
+ ### InteractionCallback
1099
+
1100
+ Received by InteractionListener handlers.
1101
+
1102
+ | Property | Type | Description |
1103
+ |----------|------|-------------|
1104
+ | `int1` | `Interactor` | First interactor (body, shape, or compound) |
1105
+ | `int2` | `Interactor` | Second interactor |
1106
+ | `arbiters` | `ArbiterList` | All arbiters for this interaction |
1107
+
1108
+ ---
1109
+
1110
+ ### BodyCallback
1111
+
1112
+ Received by BodyListener handlers.
1113
+
1114
+ | Property | Type | Description |
1115
+ |----------|------|-------------|
1116
+ | `body` | `Body` | The body that woke or slept |
1117
+
1118
+ ---
1119
+
1120
+ ### ConstraintCallback
1121
+
1122
+ Received by ConstraintListener handlers.
1123
+
1124
+ | Property | Type | Description |
1125
+ |----------|------|-------------|
1126
+ | `constraint` | `Constraint` | The constraint that broke |
1127
+
1128
+ ---
1129
+
1130
+ ### PreCallback
1131
+
1132
+ Received by PreListener handlers.
1133
+
1134
+ | Property | Type | Description |
1135
+ |----------|------|-------------|
1136
+ | `arbiter` | `Arbiter` | The arbiter (mutable — modify collision properties here) |
1137
+ | `int1` | `Interactor` | First interactor |
1138
+ | `int2` | `Interactor` | Second interactor |
1139
+ | `swapped` | `boolean` | True if int1/int2 are swapped relative to listener definition |
1140
+
1141
+ ---
1142
+
1143
+ ### PreFlag (Enum)
1144
+
1145
+ | Value | Description |
1146
+ |-------|-------------|
1147
+ | `PreFlag.ACCEPT` | Allow collision (permanent) |
1148
+ | `PreFlag.IGNORE` | Ignore collision (permanent) |
1149
+ | `PreFlag.ACCEPT_ONCE` | Allow collision (re-evaluate next step) |
1150
+ | `PreFlag.IGNORE_ONCE` | Ignore collision (re-evaluate next step) |
1151
+
1152
+ ---
1153
+
1154
+ ## Collision & Dynamics
1155
+
1156
+ ### Arbiter (Base Class)
1157
+
1158
+ Interaction data between two shapes. Pooled — do not hold references after `space.step()`.
1159
+
1160
+ | Property | Type | Description |
1161
+ |----------|------|-------------|
1162
+ | `type` | `ArbiterType` | COLLISION, SENSOR, or FLUID |
1163
+ | `shape1` | `Shape` | First shape |
1164
+ | `shape2` | `Shape` | Second shape |
1165
+ | `body1` | `Body` | First body |
1166
+ | `body2` | `Body` | Second body |
1167
+ | `isSleeping` | `boolean` | True if interaction is sleeping |
1168
+
1169
+ | Method | Returns | Description |
1170
+ |--------|---------|-------------|
1171
+ | `isCollisionArbiter()` | `boolean` | Type check |
1172
+ | `isFluidArbiter()` | `boolean` | Type check |
1173
+ | `isSensorArbiter()` | `boolean` | Type check |
1174
+ | `collisionArbiter` | `CollisionArbiter` | Cast (throws if wrong type) |
1175
+ | `fluidArbiter` | `FluidArbiter` | Cast (throws if wrong type) |
1176
+ | `totalImpulse(body?, freshOnly?)` | `Vec3` | Total impulse (x, y, angular) |
1177
+
1178
+ ---
1179
+
1180
+ ### CollisionArbiter
1181
+
1182
+ Collision-specific arbiter with contact points and impulse data.
1183
+
1184
+ | Property | Type | R/W | Description |
1185
+ |----------|------|-----|-------------|
1186
+ | `contacts` | `ContactList` | R | Active contact points |
1187
+ | `normal` | `Vec2` | R | Collision normal (shape1 → shape2) |
1188
+ | `radius` | `number` | R | Sum of shape radii |
1189
+ | `referenceEdge1` | `Edge \| null` | R | Reference edge on shape1 |
1190
+ | `referenceEdge2` | `Edge \| null` | R | Reference edge on shape2 |
1191
+ | `elasticity` | `number` | R/W* | Combined elasticity (* mutable in PreListener) |
1192
+ | `dynamicFriction` | `number` | R/W* | Combined dynamic friction |
1193
+ | `staticFriction` | `number` | R/W* | Combined static friction |
1194
+ | `rollingFriction` | `number` | R/W* | Combined rolling friction |
1195
+
1196
+ | Method | Returns | Description |
1197
+ |--------|---------|-------------|
1198
+ | `normalImpulse(body?, freshOnly?)` | `Vec3` | Normal impulse |
1199
+ | `tangentImpulse(body?, freshOnly?)` | `Vec3` | Tangent (friction) impulse |
1200
+ | `rollingImpulse(body?, freshOnly?)` | `number` | Rolling friction impulse |
1201
+ | `totalImpulse(body?, freshOnly?)` | `Vec3` | Total impulse |
1202
+ | `firstVertex()` | `boolean` | True if contact is at first vertex |
1203
+ | `secondVertex()` | `boolean` | True if contact is at second vertex |
1204
+
1205
+ ---
1206
+
1207
+ ### FluidArbiter
1208
+
1209
+ Fluid-specific arbiter with buoyancy and drag data.
1210
+
1211
+ | Property | Type | R/W | Description |
1212
+ |----------|------|-----|-------------|
1213
+ | `position` | `Vec2` | R/W* | Center of overlap (* mutable in PreListener) |
1214
+ | `overlap` | `number` | R/W* | Overlap area in pixels² |
1215
+
1216
+ | Method | Returns | Description |
1217
+ |--------|---------|-------------|
1218
+ | `buoyancyImpulse(body?)` | `Vec3` | Buoyancy impulse |
1219
+ | `dragImpulse(body?)` | `Vec3` | Drag impulse |
1220
+ | `totalImpulse(body?)` | `Vec3` | Total fluid impulse |
1221
+
1222
+ ---
1223
+
1224
+ ### Contact
1225
+
1226
+ Single contact point between two colliding shapes.
1227
+
1228
+ | Property | Type | Description |
1229
+ |----------|------|-------------|
1230
+ | `arbiter` | `CollisionArbiter` | Parent arbiter |
1231
+ | `position` | `Vec2` | World-space contact point |
1232
+ | `penetration` | `number` | Overlap depth (positive = overlapping) |
1233
+ | `fresh` | `boolean` | True if newly created this step |
1234
+ | `friction` | `number` | Friction at this contact |
1235
+
1236
+ | Method | Returns | Description |
1237
+ |--------|---------|-------------|
1238
+ | `normalImpulse(body?)` | `Vec3` | Normal impulse at this contact |
1239
+ | `tangentImpulse(body?)` | `Vec3` | Tangent impulse at this contact |
1240
+ | `rollingImpulse(body?)` | `number` | Rolling impulse at this contact |
1241
+ | `totalImpulse(body?)` | `Vec3` | Total impulse at this contact |
1242
+
1243
+ ---
1244
+
1245
+ ### InteractionFilter
1246
+
1247
+ Bitmask-based filtering for collision, sensor, and fluid interactions.
1248
+
1249
+ ```typescript
1250
+ new InteractionFilter(
1251
+ collisionGroup?: number, // Default: 1
1252
+ collisionMask?: number, // Default: -1 (all bits set)
1253
+ sensorGroup?: number, // Default: 1
1254
+ sensorMask?: number, // Default: -1
1255
+ fluidGroup?: number, // Default: 1
1256
+ fluidMask?: number // Default: -1
1257
+ )
1258
+ ```
1259
+
1260
+ Two shapes interact when: `(filter1.mask & filter2.group) != 0 && (filter2.mask & filter1.group) != 0`
1261
+
1262
+ | Property | Type | Description |
1263
+ |----------|------|-------------|
1264
+ | `collisionGroup` | `number` | Collision group bits |
1265
+ | `collisionMask` | `number` | Collision mask bits |
1266
+ | `sensorGroup` | `number` | Sensor group bits |
1267
+ | `sensorMask` | `number` | Sensor mask bits |
1268
+ | `fluidGroup` | `number` | Fluid group bits |
1269
+ | `fluidMask` | `number` | Fluid mask bits |
1270
+
1271
+ | Method | Returns | Description |
1272
+ |--------|---------|-------------|
1273
+ | `shouldCollide(other: InteractionFilter)` | `boolean` | Test collision interaction |
1274
+ | `shouldSense(other: InteractionFilter)` | `boolean` | Test sensor interaction |
1275
+ | `shouldFlow(other: InteractionFilter)` | `boolean` | Test fluid interaction |
1276
+ | `copy()` | `InteractionFilter` | Deep copy |
1277
+
1278
+ ---
1279
+
1280
+ ### InteractionGroup
1281
+
1282
+ Hierarchical grouping for interaction control.
1283
+
1284
+ ```typescript
1285
+ new InteractionGroup(ignore?: boolean)
1286
+ ```
1287
+
1288
+ When two interactors share a common group ancestor with `ignore=true`, they don't interact.
1289
+
1290
+ | Property | Type | Description |
1291
+ |----------|------|-------------|
1292
+ | `group` | `InteractionGroup \| null` | Parent group |
1293
+ | `ignore` | `boolean` | If true, members don't interact with each other |
1294
+
1295
+ ---
1296
+
1297
+ ### ArbiterType (Enum)
1298
+
1299
+ | Value | Description |
1300
+ |-------|-------------|
1301
+ | `ArbiterType.COLLISION` | Physical collision |
1302
+ | `ArbiterType.SENSOR` | Sensor overlap |
1303
+ | `ArbiterType.FLUID` | Fluid interaction |
1304
+
1305
+ ---
1306
+
1307
+ ## Space
1308
+
1309
+ ### Space
1310
+
1311
+ Physics world. Create with gravity, add bodies/constraints, call `step(dt)` to simulate.
1312
+
1313
+ ```typescript
1314
+ new Space(gravity?: Vec2, broadphase?: Broadphase)
1315
+ ```
1316
+
1317
+ #### Properties
1318
+
1319
+ | Property | Type | R/W | Description |
1320
+ |----------|------|-----|-------------|
1321
+ | `gravity` | `Vec2` | R/W | World gravity (live Vec2). Default: (0, 0). |
1322
+ | `broadphase` | `Broadphase` | R | SWEEP_AND_PRUNE or DYNAMIC_AABB_TREE |
1323
+ | `sortContacts` | `boolean` | R/W | Sort contacts for determinism. Default: true. |
1324
+ | `worldLinearDrag` | `number` | R/W | Global linear drag coefficient |
1325
+ | `worldAngularDrag` | `number` | R/W | Global angular drag coefficient |
1326
+ | `bodies` | `NapeList<Body>` | R | All bodies |
1327
+ | `liveBodies` | `NapeList<Body>` | R | Awake bodies |
1328
+ | `compounds` | `NapeList<Compound>` | R | All compounds |
1329
+ | `constraints` | `NapeList<Constraint>` | R | All constraints |
1330
+ | `liveConstraints` | `NapeList<Constraint>` | R | Awake constraints |
1331
+ | `world` | `Body` | R | Static world body (immovable anchor) |
1332
+ | `arbiters` | `ArbiterList` | R | Active arbiters |
1333
+ | `listeners` | `ListenerList` | R | Event listeners |
1334
+ | `timeStamp` | `number` | R | Number of step() calls |
1335
+ | `elapsedTime` | `number` | R | Cumulative simulated time |
1336
+ | `userData` | `Record<string, unknown>` | R | User data |
1337
+
1338
+ #### Simulation
1339
+
1340
+ ```typescript
1341
+ step(deltaTime: number, velocityIterations?: number, positionIterations?: number): void
1342
+ ```
1343
+ Advance simulation by `deltaTime` seconds. Iterations default to 10 each.
1344
+
1345
+ ```typescript
1346
+ clear(): void
1347
+ ```
1348
+ Remove all bodies, constraints, compounds. Cannot be called during step().
1349
+
1350
+ #### Visitors
1351
+
1352
+ ```typescript
1353
+ visitBodies(fn: (body: Body) => void): void // All bodies including in compounds
1354
+ visitConstraints(fn: (c: Constraint) => void): void
1355
+ visitCompounds(fn: (c: Compound) => void): void
1356
+ ```
1357
+
1358
+ #### Spatial Queries
1359
+
1360
+ ```typescript
1361
+ // Point queries
1362
+ shapesUnderPoint(point: Vec2, filter?: InteractionFilter): ShapeList
1363
+ bodiesUnderPoint(point: Vec2, filter?: InteractionFilter): BodyList
1364
+
1365
+ // AABB queries
1366
+ shapesInAABB(aabb: AABB, containment?: boolean, strict?: boolean, filter?: InteractionFilter): ShapeList
1367
+ bodiesInAABB(aabb: AABB, containment?: boolean, strict?: boolean, filter?: InteractionFilter): BodyList
1368
+
1369
+ // Circle queries
1370
+ shapesInCircle(position: Vec2, radius: number, containment?: boolean, filter?: InteractionFilter): ShapeList
1371
+ bodiesInCircle(position: Vec2, radius: number, containment?: boolean, filter?: InteractionFilter): BodyList
1372
+
1373
+ // Shape queries
1374
+ shapesInShape(shape: Shape, containment?: boolean, filter?: InteractionFilter): ShapeList
1375
+ bodiesInShape(shape: Shape, containment?: boolean, filter?: InteractionFilter): BodyList
1376
+
1377
+ // Body queries (union of all shapes)
1378
+ shapesInBody(body: Body, filter?: InteractionFilter): ShapeList
1379
+ bodiesInBody(body: Body, filter?: InteractionFilter): BodyList
1380
+ ```
1381
+
1382
+ #### Raycasting
1383
+
1384
+ ```typescript
1385
+ rayCast(ray: Ray, inner?: boolean, filter?: InteractionFilter): RayResult | null
1386
+ rayMultiCast(ray: Ray, inner?: boolean, filter?: InteractionFilter): RayResultList
1387
+ ```
1388
+
1389
+ #### Convex Sweep
1390
+
1391
+ ```typescript
1392
+ convexCast(shape: Shape, deltaTime: number, liveSweep?: boolean, filter?: InteractionFilter): RayResult | null
1393
+ convexMultiCast(shape: Shape, deltaTime: number, liveSweep?: boolean, filter?: InteractionFilter): RayResultList
1394
+ ```
1395
+
1396
+ #### Interaction Type Query
1397
+
1398
+ ```typescript
1399
+ interactionType(shape1: Shape, shape2: Shape): InteractionType | null
1400
+ ```
1401
+
1402
+ ---
1403
+
1404
+ ### Broadphase (Enum)
1405
+
1406
+ | Value | Description |
1407
+ |-------|-------------|
1408
+ | `Broadphase.SWEEP_AND_PRUNE` | Good for many objects with little movement |
1409
+ | `Broadphase.DYNAMIC_AABB_TREE` | Good for dynamic scenes with varied object sizes |
1410
+
1411
+ ---
1412
+
1413
+ ## Collections
1414
+
1415
+ ### NapeList\<T\>
1416
+
1417
+ Generic iterable list wrapping internal Haxe lists. Supports ES6 iteration.
1418
+
1419
+ | Property | Type | Description |
1420
+ |----------|------|-------------|
1421
+ | `length` | `number` | Number of elements |
1422
+ | `empty` | `boolean` | True if length is 0 |
1423
+
1424
+ | Method | Returns | Description |
1425
+ |--------|---------|-------------|
1426
+ | `at(index: number)` | `T` | Get element by index |
1427
+ | `push(item: T)` | `boolean` | Add to end |
1428
+ | `pop()` | `T` | Remove from end |
1429
+ | `unshift(item: T)` | `boolean` | Add to beginning |
1430
+ | `shift()` | `T` | Remove from beginning |
1431
+ | `add(item: T)` | `boolean` | Add item |
1432
+ | `remove(item: T)` | `boolean` | Remove item |
1433
+ | `has(item: T)` | `boolean` | Check if contains item |
1434
+ | `clear()` | `void` | Remove all items |
1435
+ | `forEach(fn: (item: T) => void)` | `void` | Iterate all items |
1436
+ | `toArray()` | `T[]` | Convert to JavaScript array |
1437
+ | `[Symbol.iterator]()` | `Iterator<T>` | ES6 iteration support |
1438
+
1439
+ #### Usage
1440
+
1441
+ ```typescript
1442
+ // Iterate with for...of
1443
+ for (const body of space.bodies) {
1444
+ console.log(body.position.x, body.position.y);
1445
+ }
1446
+
1447
+ // Indexed access
1448
+ const first = space.bodies.at(0);
1449
+
1450
+ // Add/remove
1451
+ body.shapes.add(new Circle(20));
1452
+ body.shapes.remove(oldShape);
1453
+
1454
+ // Convert to array
1455
+ const bodyArray = space.bodies.toArray();
1456
+ ```
1457
+
1458
+ ---
1459
+
1460
+ ## Common Patterns
1461
+
1462
+ ### Adding Bodies to Space
1463
+
1464
+ ```typescript
1465
+ const body = new Body(BodyType.DYNAMIC, new Vec2(100, 100));
1466
+ body.shapes.add(new Circle(25));
1467
+ body.space = space; // This adds the body to the space
1468
+ ```
1469
+
1470
+ ### Removing Bodies
1471
+
1472
+ ```typescript
1473
+ body.space = null; // Removes from space
1474
+ ```
1475
+
1476
+ ### Applying Forces
1477
+
1478
+ ```typescript
1479
+ // Continuous force (applied per step)
1480
+ body.force.setxy(100, 0);
1481
+
1482
+ // One-time impulse
1483
+ body.applyImpulse(new Vec2(500, 0));
1484
+
1485
+ // Impulse at a point (creates torque)
1486
+ body.applyImpulse(new Vec2(0, -100), new Vec2(body.position.x + 10, body.position.y));
1487
+ ```
1488
+
1489
+ ### Sensors
1490
+
1491
+ ```typescript
1492
+ const sensor = new Circle(50);
1493
+ sensor.sensorEnabled = true;
1494
+ body.shapes.add(sensor);
1495
+
1496
+ space.listeners.add(new InteractionListener(
1497
+ CbEvent.BEGIN, InteractionType.SENSOR,
1498
+ CbType.ANY_BODY, CbType.ANY_BODY,
1499
+ (cb) => console.log("Sensor triggered!")
1500
+ ));
1501
+ ```
1502
+
1503
+ ### Fluids
1504
+
1505
+ ```typescript
1506
+ const water = new Body(BodyType.STATIC, new Vec2(400, 400));
1507
+ const waterShape = new Polygon(Polygon.box(800, 200));
1508
+ waterShape.fluidEnabled = true;
1509
+ waterShape.fluidProperties = new FluidProperties(2.0, 3.0);
1510
+ waterShape.sensorEnabled = true;
1511
+ water.shapes.add(waterShape);
1512
+ water.space = space;
1513
+ ```
1514
+
1515
+ ### Collision Layers
1516
+
1517
+ ```typescript
1518
+ const LAYER_PLAYER = 1;
1519
+ const LAYER_ENEMY = 2;
1520
+ const LAYER_BULLET = 4;
1521
+
1522
+ // Player collides with enemy and bullet
1523
+ playerShape.filter = new InteractionFilter(LAYER_PLAYER, LAYER_ENEMY | LAYER_BULLET);
1524
+
1525
+ // Enemy collides with player and bullet
1526
+ enemyShape.filter = new InteractionFilter(LAYER_ENEMY, LAYER_PLAYER | LAYER_BULLET);
1527
+
1528
+ // Bullets collide with player and enemy (not each other)
1529
+ bulletShape.filter = new InteractionFilter(LAYER_BULLET, LAYER_PLAYER | LAYER_ENEMY);
1530
+ ```
1531
+
1532
+ ### Spring Constraints
1533
+
1534
+ ```typescript
1535
+ const spring = new DistanceJoint(body1, body2,
1536
+ new Vec2(0, 0), new Vec2(0, 0),
1537
+ 50, 100 // min/max distance
1538
+ );
1539
+ spring.stiff = false; // Make it springy
1540
+ spring.frequency = 2.0; // 2 Hz oscillation
1541
+ spring.damping = 0.5; // Half critical damping
1542
+ spring.space = space;
1543
+ ```
1544
+
1545
+ ### Raycasting
1546
+
1547
+ ```typescript
1548
+ const ray = new Ray(new Vec2(0, 300), new Vec2(1, 0));
1549
+ ray.maxDistance = 800;
1550
+
1551
+ const result = space.rayCast(ray);
1552
+ if (result) {
1553
+ console.log("Hit shape:", result.shape);
1554
+ console.log("Hit point:", ray.at(result.distance));
1555
+ console.log("Normal:", result.normal);
1556
+ }
1557
+ ```
1558
+
1559
+ ### One-Way Platforms
1560
+
1561
+ ```typescript
1562
+ space.listeners.add(new PreListener(
1563
+ InteractionType.COLLISION,
1564
+ platformType, playerType,
1565
+ (cb) => {
1566
+ const arb = cb.arbiter.collisionArbiter;
1567
+ // Only collide if player is above platform (normal points up)
1568
+ if (arb.normal.y < 0) return PreFlag.ACCEPT;
1569
+ return PreFlag.IGNORE;
1570
+ }
1571
+ ));
1572
+ ```
1573
+
1574
+ ---
1575
+
1576
+ ## Exports
1577
+
1578
+ All classes are available from the main entry point:
1579
+
1580
+ ```typescript
1581
+ import {
1582
+ // Geometry
1583
+ Vec2, Vec3, Mat23, MatMN, AABB, Ray,
1584
+ ConvexResult, RayResult, Geom, GeomPoly,
1585
+ Winding, MarchingSquares,
1586
+
1587
+ // Physics
1588
+ Body, BodyType, Compound, Interactor,
1589
+ Material, FluidProperties,
1590
+ MassMode, InertiaMode, GravMassMode,
1591
+
1592
+ // Shapes
1593
+ Shape, Circle, Polygon, Edge,
1594
+ ShapeType, ValidationResult,
1595
+
1596
+ // Space
1597
+ Space, Broadphase,
1598
+
1599
+ // Dynamics
1600
+ Arbiter, CollisionArbiter, FluidArbiter,
1601
+ Contact, ArbiterType,
1602
+ InteractionFilter, InteractionGroup,
1603
+
1604
+ // Callbacks
1605
+ CbEvent, CbType, Listener,
1606
+ BodyListener, InteractionListener,
1607
+ ConstraintListener, PreListener,
1608
+ Callback, BodyCallback,
1609
+ InteractionCallback, ConstraintCallback, PreCallback,
1610
+ InteractionType, ListenerType, PreFlag, OptionType,
1611
+
1612
+ // Constraints
1613
+ Constraint, PivotJoint, DistanceJoint,
1614
+ AngleJoint, WeldJoint, MotorJoint,
1615
+ LineJoint, PulleyJoint,
1616
+
1617
+ // Utilities
1618
+ NapeList, VERSION
1619
+ } from "@newkrok/nape-js";
1620
+ ```