@driftengine/physics 3.61.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 (136) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +503 -0
  4. package/dist/bodies.d.ts +121 -0
  5. package/dist/bodies.js +298 -0
  6. package/dist/buoyancy.d.ts +46 -0
  7. package/dist/buoyancy.js +45 -0
  8. package/dist/cloth.d.ts +226 -0
  9. package/dist/cloth.js +587 -0
  10. package/dist/collide/aabb.d.ts +10 -0
  11. package/dist/collide/aabb.js +11 -0
  12. package/dist/collide/body.d.ts +202 -0
  13. package/dist/collide/body.js +142 -0
  14. package/dist/collide/eject.d.ts +39 -0
  15. package/dist/collide/eject.js +142 -0
  16. package/dist/collide/index.d.ts +18 -0
  17. package/dist/collide/index.js +5 -0
  18. package/dist/collide/scratch.d.ts +8 -0
  19. package/dist/collide/scratch.js +9 -0
  20. package/dist/collide/segment.d.ts +12 -0
  21. package/dist/collide/segment.js +109 -0
  22. package/dist/collide/sweep.d.ts +3 -0
  23. package/dist/collide/sweep.js +1036 -0
  24. package/dist/colliderSet.d.ts +239 -0
  25. package/dist/colliderSet.js +546 -0
  26. package/dist/compoundContact.d.ts +70 -0
  27. package/dist/compoundContact.js +221 -0
  28. package/dist/controller.d.ts +351 -0
  29. package/dist/controller.js +641 -0
  30. package/dist/cylinder.d.ts +78 -0
  31. package/dist/cylinder.js +294 -0
  32. package/dist/cylinderContact.d.ts +7 -0
  33. package/dist/cylinderContact.js +704 -0
  34. package/dist/decompose.d.ts +105 -0
  35. package/dist/decompose.js +979 -0
  36. package/dist/events.d.ts +43 -0
  37. package/dist/events.js +95 -0
  38. package/dist/executor.d.ts +147 -0
  39. package/dist/executor.js +173 -0
  40. package/dist/faces.d.ts +26 -0
  41. package/dist/faces.js +34 -0
  42. package/dist/fingerprint.d.ts +61 -0
  43. package/dist/fingerprint.js +139 -0
  44. package/dist/groundMotor.d.ts +34 -0
  45. package/dist/groundMotor.js +41 -0
  46. package/dist/heightfieldShape.d.ts +128 -0
  47. package/dist/heightfieldShape.js +328 -0
  48. package/dist/index.d.ts +73 -0
  49. package/dist/index.js +47 -0
  50. package/dist/island.d.ts +55 -0
  51. package/dist/island.js +227 -0
  52. package/dist/islandSolve.d.ts +70 -0
  53. package/dist/islandSolve.js +171 -0
  54. package/dist/islandWorker.d.ts +18 -0
  55. package/dist/islandWorker.js +125 -0
  56. package/dist/joints.d.ts +180 -0
  57. package/dist/joints.js +1094 -0
  58. package/dist/manifold.d.ts +89 -0
  59. package/dist/manifold.js +608 -0
  60. package/dist/mass.d.ts +53 -0
  61. package/dist/mass.js +353 -0
  62. package/dist/meshContact.d.ts +57 -0
  63. package/dist/meshContact.js +393 -0
  64. package/dist/meshShape.d.ts +76 -0
  65. package/dist/meshShape.js +175 -0
  66. package/dist/pairs.d.ts +62 -0
  67. package/dist/pairs.js +154 -0
  68. package/dist/query.d.ts +71 -0
  69. package/dist/query.js +683 -0
  70. package/dist/ragdoll.d.ts +135 -0
  71. package/dist/ragdoll.js +489 -0
  72. package/dist/shape.d.ts +177 -0
  73. package/dist/shape.js +533 -0
  74. package/dist/solver.d.ts +136 -0
  75. package/dist/solver.js +601 -0
  76. package/dist/stage.d.ts +167 -0
  77. package/dist/stage.js +350 -0
  78. package/dist/stallEscape.d.ts +141 -0
  79. package/dist/stallEscape.js +254 -0
  80. package/dist/sweptStep.d.ts +173 -0
  81. package/dist/sweptStep.js +472 -0
  82. package/dist/tree.d.ts +61 -0
  83. package/dist/tree.js +348 -0
  84. package/dist/vehicle.d.ts +154 -0
  85. package/dist/vehicle.js +343 -0
  86. package/dist/workerPool.d.ts +107 -0
  87. package/dist/workerPool.js +239 -0
  88. package/dist/workers.d.ts +37 -0
  89. package/dist/workers.js +35 -0
  90. package/dist/world.d.ts +298 -0
  91. package/dist/world.js +712 -0
  92. package/package.json +55 -0
  93. package/src/bodies.ts +369 -0
  94. package/src/buoyancy.ts +78 -0
  95. package/src/cloth.ts +734 -0
  96. package/src/collide/aabb.ts +28 -0
  97. package/src/collide/body.ts +336 -0
  98. package/src/collide/eject.ts +156 -0
  99. package/src/collide/index.ts +26 -0
  100. package/src/collide/scratch.ts +10 -0
  101. package/src/collide/segment.ts +127 -0
  102. package/src/collide/sweep.ts +1132 -0
  103. package/src/colliderSet.ts +637 -0
  104. package/src/compoundContact.ts +284 -0
  105. package/src/controller.ts +817 -0
  106. package/src/cylinder.ts +340 -0
  107. package/src/cylinderContact.ts +825 -0
  108. package/src/decompose.ts +1198 -0
  109. package/src/events.ts +108 -0
  110. package/src/executor.ts +224 -0
  111. package/src/faces.ts +37 -0
  112. package/src/fingerprint.ts +146 -0
  113. package/src/groundMotor.ts +53 -0
  114. package/src/heightfieldShape.ts +395 -0
  115. package/src/index.ts +164 -0
  116. package/src/island.ts +219 -0
  117. package/src/islandSolve.ts +218 -0
  118. package/src/islandWorker.ts +141 -0
  119. package/src/joints.ts +1367 -0
  120. package/src/manifold.ts +798 -0
  121. package/src/mass.ts +424 -0
  122. package/src/meshContact.ts +443 -0
  123. package/src/meshShape.ts +261 -0
  124. package/src/pairs.ts +169 -0
  125. package/src/query.ts +844 -0
  126. package/src/ragdoll.ts +662 -0
  127. package/src/shape.ts +656 -0
  128. package/src/solver.ts +873 -0
  129. package/src/stage.ts +406 -0
  130. package/src/stallEscape.ts +362 -0
  131. package/src/sweptStep.ts +609 -0
  132. package/src/tree.ts +366 -0
  133. package/src/vehicle.ts +489 -0
  134. package/src/workerPool.ts +283 -0
  135. package/src/workers.ts +38 -0
  136. package/src/world.ts +961 -0
@@ -0,0 +1,62 @@
1
+ import type { BodySet } from './bodies.ts';
2
+ import { DynamicTree } from './tree.ts';
3
+ /**
4
+ * Candidate body pairs, ordered by body index and never by tree shape.
5
+ *
6
+ * **This is the file that makes the broadphase safe to change.** A dynamic tree's shape depends on
7
+ * the order proxies were inserted in, and its traversal order depends on its shape — so a pair list
8
+ * taken straight from the tree carries insertion history in it. Sequential impulses are
9
+ * order-dependent, so that history would reach the simulation's *result*, and a scene rebuilt in a
10
+ * different order would diverge while looking identical.
11
+ *
12
+ * So every pair is packed into one 32-bit key as `min * 2^16 + max`, radix-sorted, and nothing
13
+ * downstream ever learns there was a tree. **What this costs** is a two-pass sort over preallocated
14
+ * scratch each tick. **What would make it wrong** is more than 65,536 bodies, which the key cannot
15
+ * express; `MAX_BODIES` names that and the builder refuses past it rather than aliasing two pairs
16
+ * onto one key.
17
+ */
18
+ /** The key packs two indices into 32 bits, so neither may reach this. */
19
+ export declare const MAX_BODIES: number;
20
+ export declare class PairSet {
21
+ /** Packed `(min, max)` keys, sorted ascending. Read `count` of them. */
22
+ keys: Uint32Array;
23
+ count: number;
24
+ private scratch;
25
+ private histogram;
26
+ private hits;
27
+ private queryBox;
28
+ constructor(capacity?: number);
29
+ /**
30
+ * Collect every candidate pair among the tree's proxies, sorted.
31
+ *
32
+ * `leafOf` maps a body index to its tree leaf, because a query answers with proxy ids and the
33
+ * tree needs the leaf to read a body's stored bounds back.
34
+ */
35
+ build(bodies: BodySet, tree: DynamicTree, leafOf: Int32Array, ignored?: ReadonlySet<number> | null): number;
36
+ private push;
37
+ /**
38
+ * Four passes of an 8-bit radix sort, which is stable and touches no comparator.
39
+ *
40
+ * A comparison sort would also be deterministic, and this is here for the hot path rather than
41
+ * for correctness: the pair list is rebuilt every tick and is the one array whose length grows
42
+ * with the square of local density.
43
+ */
44
+ private sort;
45
+ }
46
+ /** The 32-layer mask, both ways. A pair layersInteract only if each side admits the other. */
47
+ export declare function layersInteract(bodies: BodySet, a: number, b: number): boolean;
48
+ /**
49
+ * The key a pair of bodies is stored under, in either order.
50
+ *
51
+ * **Two bodies that must never collide cannot always be said with layers.** The mask is a property
52
+ * of each body on its own, so it can express "this kind does not meet that kind" and cannot express
53
+ * "these two in particular" — and a ragdoll is entirely the second thing: every bone overlaps the
54
+ * one it hangs from, because that is what having a radius means, while the same two *kinds* of bone
55
+ * elsewhere in the body must still collide. A consumer that tried it with layers spent a bit per
56
+ * bone and most of the 32 on one doll.
57
+ */
58
+ export declare function pairKey(a: number, b: number): number;
59
+ /** Unpack a key's lower body index. */
60
+ export declare function pairA(key: number): number;
61
+ /** Unpack a key's higher body index. */
62
+ export declare function pairB(key: number): number;
package/dist/pairs.js ADDED
@@ -0,0 +1,154 @@
1
+ import { BODY_STATIC } from './bodies.js';
2
+ import { DynamicTree } from './tree.js';
3
+ /**
4
+ * Candidate body pairs, ordered by body index and never by tree shape.
5
+ *
6
+ * **This is the file that makes the broadphase safe to change.** A dynamic tree's shape depends on
7
+ * the order proxies were inserted in, and its traversal order depends on its shape — so a pair list
8
+ * taken straight from the tree carries insertion history in it. Sequential impulses are
9
+ * order-dependent, so that history would reach the simulation's *result*, and a scene rebuilt in a
10
+ * different order would diverge while looking identical.
11
+ *
12
+ * So every pair is packed into one 32-bit key as `min * 2^16 + max`, radix-sorted, and nothing
13
+ * downstream ever learns there was a tree. **What this costs** is a two-pass sort over preallocated
14
+ * scratch each tick. **What would make it wrong** is more than 65,536 bodies, which the key cannot
15
+ * express; `MAX_BODIES` names that and the builder refuses past it rather than aliasing two pairs
16
+ * onto one key.
17
+ */
18
+ /** The key packs two indices into 32 bits, so neither may reach this. */
19
+ export const MAX_BODIES = 1 << 16;
20
+ export class PairSet {
21
+ /** Packed `(min, max)` keys, sorted ascending. Read `count` of them. */
22
+ keys;
23
+ count = 0;
24
+ scratch;
25
+ histogram = new Uint32Array(256);
26
+ hits;
27
+ queryBox = { minX: 0, minY: 0, minZ: 0, maxX: 0, maxY: 0, maxZ: 0 };
28
+ constructor(capacity = 256) {
29
+ this.keys = new Uint32Array(capacity);
30
+ this.scratch = new Uint32Array(capacity);
31
+ this.hits = new Int32Array(256);
32
+ }
33
+ /**
34
+ * Collect every candidate pair among the tree's proxies, sorted.
35
+ *
36
+ * `leafOf` maps a body index to its tree leaf, because a query answers with proxy ids and the
37
+ * tree needs the leaf to read a body's stored bounds back.
38
+ */
39
+ build(bodies, tree, leafOf, ignored = null) {
40
+ this.count = 0;
41
+ if (bodies.count >= MAX_BODIES) {
42
+ throw new Error(`PairSet: ${bodies.count} bodies exceeds the ${MAX_BODIES} a pair key holds`);
43
+ }
44
+ for (let a = 0; a < bodies.count; a++) {
45
+ const leaf = leafOf[a] ?? -1;
46
+ if (leaf < 0)
47
+ continue;
48
+ const at = leaf * 6;
49
+ this.queryBox.minX = tree.bounds[at] ?? 0;
50
+ this.queryBox.minY = tree.bounds[at + 1] ?? 0;
51
+ this.queryBox.minZ = tree.bounds[at + 2] ?? 0;
52
+ this.queryBox.maxX = tree.bounds[at + 3] ?? 0;
53
+ this.queryBox.maxY = tree.bounds[at + 4] ?? 0;
54
+ this.queryBox.maxZ = tree.bounds[at + 5] ?? 0;
55
+ let found = tree.query(this.queryBox, this.hits);
56
+ while (found === this.hits.length) {
57
+ // The query truncated, so it cannot be trusted to have reported everything.
58
+ this.hits = new Int32Array(this.hits.length * 2);
59
+ found = tree.query(this.queryBox, this.hits);
60
+ }
61
+ for (let k = 0; k < found; k++) {
62
+ const b = this.hits[k] ?? 0;
63
+ // Each unordered pair once, and never a body against itself.
64
+ if (b <= a)
65
+ continue;
66
+ if (!layersInteract(bodies, a, b))
67
+ continue;
68
+ /* `b > a` by the test above, so this is already the order `pairKey` writes. */
69
+ const key = a * MAX_BODIES + b;
70
+ /* The size test rather than the lookup: a world that never excluded a pair pays one
71
+ integer compare per candidate, which is what keeps this out of everybody else's way. */
72
+ if (ignored !== null && ignored.size !== 0 && ignored.has(key))
73
+ continue;
74
+ this.push(key);
75
+ }
76
+ }
77
+ this.sort();
78
+ return this.count;
79
+ }
80
+ push(key) {
81
+ if (this.count === this.keys.length) {
82
+ const wider = new Uint32Array(this.keys.length * 2);
83
+ wider.set(this.keys);
84
+ this.keys = wider;
85
+ this.scratch = new Uint32Array(wider.length);
86
+ }
87
+ this.keys[this.count++] = key;
88
+ }
89
+ /**
90
+ * Four passes of an 8-bit radix sort, which is stable and touches no comparator.
91
+ *
92
+ * A comparison sort would also be deterministic, and this is here for the hot path rather than
93
+ * for correctness: the pair list is rebuilt every tick and is the one array whose length grows
94
+ * with the square of local density.
95
+ */
96
+ sort() {
97
+ let from = this.keys;
98
+ let into = this.scratch;
99
+ for (let shift = 0; shift < 32; shift += 8) {
100
+ this.histogram.fill(0);
101
+ for (let i = 0; i < this.count; i++) {
102
+ this.histogram[((from[i] ?? 0) >>> shift) & 255]++;
103
+ }
104
+ let sum = 0;
105
+ for (let d = 0; d < 256; d++) {
106
+ const c = this.histogram[d] ?? 0;
107
+ this.histogram[d] = sum;
108
+ sum += c;
109
+ }
110
+ for (let i = 0; i < this.count; i++) {
111
+ const key = from[i] ?? 0;
112
+ const d = (key >>> shift) & 255;
113
+ into[this.histogram[d]++] = key;
114
+ }
115
+ const swap = from;
116
+ from = into;
117
+ into = swap;
118
+ }
119
+ // Four passes is even, so the result is back where it started.
120
+ this.keys = from;
121
+ this.scratch = into;
122
+ }
123
+ }
124
+ /** The 32-layer mask, both ways. A pair layersInteract only if each side admits the other. */
125
+ export function layersInteract(bodies, a, b) {
126
+ if (bodies.type[a] === BODY_STATIC && bodies.type[b] === BODY_STATIC)
127
+ return false;
128
+ const la = bodies.layer[a] ?? 0;
129
+ const lb = bodies.layer[b] ?? 0;
130
+ const ma = bodies.mask[a] ?? 0;
131
+ const mb = bodies.mask[b] ?? 0;
132
+ return (la & mb) !== 0 && (lb & ma) !== 0;
133
+ }
134
+ /**
135
+ * The key a pair of bodies is stored under, in either order.
136
+ *
137
+ * **Two bodies that must never collide cannot always be said with layers.** The mask is a property
138
+ * of each body on its own, so it can express "this kind does not meet that kind" and cannot express
139
+ * "these two in particular" — and a ragdoll is entirely the second thing: every bone overlaps the
140
+ * one it hangs from, because that is what having a radius means, while the same two *kinds* of bone
141
+ * elsewhere in the body must still collide. A consumer that tried it with layers spent a bit per
142
+ * bone and most of the 32 on one doll.
143
+ */
144
+ export function pairKey(a, b) {
145
+ return a < b ? a * MAX_BODIES + b : b * MAX_BODIES + a;
146
+ }
147
+ /** Unpack a key's lower body index. */
148
+ export function pairA(key) {
149
+ return Math.floor(key / MAX_BODIES);
150
+ }
151
+ /** Unpack a key's higher body index. */
152
+ export function pairB(key) {
153
+ return key % MAX_BODIES;
154
+ }
@@ -0,0 +1,71 @@
1
+ import type { BodySet } from './bodies.ts';
2
+ import type { ShapePose } from './manifold.ts';
3
+ import type { ConvexShape } from './shape.ts';
4
+ import { DynamicTree } from './tree.ts';
5
+ /**
6
+ * Asking the world questions: rays, shape sweeps and overlaps.
7
+ *
8
+ * **Nothing allocates.** Every result fills a caller-owned object, and a multi-hit query fills a
9
+ * caller-supplied array to its length and returns a count — so a query in a tick costs no garbage.
10
+ *
11
+ * **Ties break by body index**, which is the same rule the pair list follows and is here for the
12
+ * same reason: an answer decided by which proxy the tree reached first is an answer decided by
13
+ * insertion history. **No test pins it**, and that is worth saying rather than leaving somebody to
14
+ * look: two distinct bodies can only sit at the identical ray distance if they are coincident, and
15
+ * the tree has never been observed to enumerate coincident proxies out of index order — so a bare
16
+ * `<=` passes every variation tried. One comparison buys the guarantee by construction instead.
17
+ *
18
+ * A shapecast is conservative advancement over the same distance query the manifold code uses,
19
+ * capped at a fixed iteration count. **What that gives up** is exactness at a grazing angle, where
20
+ * advancement converges slowly and the cap stops it early — the hit is then reported slightly late.
21
+ * **What would make it wrong** is a consumer relying on a sweep for precise placement rather than
22
+ * for a first touch; the answer then is a root-find on the separation, which costs more per query.
23
+ */
24
+ /** What a ray or a sweep found. Caller-owned; a query fills it and never allocates one. */
25
+ export interface RayHit {
26
+ body: number;
27
+ /** Fraction along the ray or sweep, in [0, 1]. */
28
+ fraction: number;
29
+ x: number;
30
+ y: number;
31
+ z: number;
32
+ nx: number;
33
+ ny: number;
34
+ nz: number;
35
+ }
36
+ export declare function createRayHit(): RayHit;
37
+ /** Which bodies a query is allowed to see. */
38
+ export interface QueryFilter {
39
+ /** The querier's own layer, tested against each body's mask. */
40
+ layer?: number;
41
+ /** Which layers the querier wants, tested against each body's layer. */
42
+ mask?: number;
43
+ /** A body to ignore, usually the one asking. */
44
+ ignore?: number;
45
+ }
46
+ /**
47
+ * The nearest body a ray meets, or `false` if it meets none.
48
+ *
49
+ * A ray starting *inside* a body reports that body at fraction zero, with the normal of the face it
50
+ * is nearest to leaving through. Reporting nothing instead is the tempting alternative and is worse:
51
+ * a line-of-sight test would then say a wall it started inside was not there.
52
+ */
53
+ export declare function raycastWorld(bodies: BodySet, tree: DynamicTree, ox: number, oy: number, oz: number, dx: number, dy: number, dz: number, maxDistance: number, out: RayHit, filter?: QueryFilter): boolean;
54
+ /**
55
+ * Every body overlapping a shape at a pose. Fills `out` to its length and returns how many.
56
+ *
57
+ * A return equal to `out.length` means the buffer filled, and the caller has been told about that
58
+ * many rather than about all of them. Returning the true total instead would invite a loop over
59
+ * entries that were never written.
60
+ */
61
+ export declare function overlapWorld(bodies: BodySet, tree: DynamicTree, shape: ConvexShape, pose: ShapePose, out: Int32Array, filter?: QueryFilter): number;
62
+ /**
63
+ * Sweep a shape from one point to another and report the first body it touches.
64
+ *
65
+ * Conservative advancement: step by the current separation, which cannot overshoot a contact, and
66
+ * stop after a fixed number of steps. A fixed count rather than a tolerance, because §3a forbids
67
+ * iterating to one.
68
+ */
69
+ export declare function shapecastWorld(bodies: BodySet, tree: DynamicTree, shape: ConvexShape, pose: ShapePose, dx: number, dy: number, dz: number, out: RayHit, filter?: QueryFilter): boolean;
70
+ /** Whether a body is one a query should treat as scenery. */
71
+ export declare function isStaticBody(bodies: BodySet, i: number): boolean;