@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,78 @@
1
+ import type { ShapePose } from './manifold.ts';
2
+ import type { ConvexShape } from './shape.ts';
3
+ /**
4
+ * A cylinder resolved into world space, and the three questions contact generation asks of it.
5
+ *
6
+ * **This module is the whole of what `sideRadius` means geometrically**, kept apart from
7
+ * `cylinderContact.ts` so the arithmetic can be read and tested without a manifold in the way. A
8
+ * cylinder is the Minkowski sum of a segment and a disc perpendicular to it: its support along a
9
+ * direction `d` is the segment's support plus `radius` times the part of `d` that is perpendicular
10
+ * to the axis, which is one square root and no trigonometry.
11
+ *
12
+ * The three questions:
13
+ *
14
+ * - **How far does it reach along `d`** — `cylinderSupport`, which is what a separating-axis test
15
+ * needs and what makes the axis list exact for a curved surface it never enumerates.
16
+ * - **What is touching along `d`** — `cylinderFeature`, which answers with a cap disc, a side
17
+ * segment or a single rim point, because those are the only three a cylinder has.
18
+ * - **Where is the nearest surface point to `p`** — `closestOnCylinder`, which is exact in all
19
+ * four regions and is how a sphere, a capsule or a cloth particle meets one.
20
+ *
21
+ * **No `Math.cos` anywhere**, which is not a style choice: `scripts/determinism.test.mjs` refuses
22
+ * every function ECMAScript declines to specify precisely, and a cap disc generated by trigonometry
23
+ * per tick would be a different set of points on two engines. The eight directions come from a
24
+ * literal table instead, and everything else is arithmetic and `Math.sqrt`.
25
+ */
26
+ /** A cylinder in world space: centre, unit axis, half length, radius, and a spin-tracking basis. */
27
+ export declare const CYLINDER_FLOATS = 11;
28
+ /** How many points a cap disc contributes. Eight holds a disc flat without reducing to a square. */
29
+ export declare const CAP_POINTS = 8;
30
+ /** Whether a shape is a cylinder, which is the one question every dispatch above asks. */
31
+ export declare function isCylinder(shape: ConvexShape): boolean;
32
+ /**
33
+ * The pose a shape already in its own coordinates has.
34
+ *
35
+ * Exported because two callers work in a shape's local frame and would otherwise each declare
36
+ * their own identity quaternion — `readCylinder` costs nothing extra for one, since a rotation by
37
+ * the identity is the arithmetic it was going to do anyway.
38
+ */
39
+ export declare const LOCAL_POSE: ShapePose;
40
+ /**
41
+ * Resolve a cylinder shape and its pose into `out`: centre, axis, half length, radius, basis.
42
+ *
43
+ * **The basis is the part worth explaining.** A cap disc's eight points have to be *the same eight
44
+ * points* from one tick to the next or their accumulated impulses are handed to the wrong contact,
45
+ * and a basis derived from the world axis alone stands still while the body spins about it. So it
46
+ * comes from the body's own local x, rotated by the pose and orthogonalised against the axis: it
47
+ * turns with the body, which is what a feature id has to do.
48
+ *
49
+ * A shape whose axis *is* local x — a cylinder baked into world space along that direction by a
50
+ * collider — falls back to local z, which cannot also be parallel to it.
51
+ */
52
+ export declare function readCylinder(shape: ConvexShape, pose: ShapePose, out: Float64Array): void;
53
+ /** How far the cylinder reaches along a unit direction: the highest `p · n` over its surface. */
54
+ export declare function cylinderSupport(cyl: Float64Array, nx: number, ny: number, nz: number): number;
55
+ /** The lowest `p · n` over its surface, which is the support in the opposite direction. */
56
+ export declare function cylinderMin(cyl: Float64Array, nx: number, ny: number, nz: number): number;
57
+ /**
58
+ * The feature touching along unit `n`, written xyz-packed into `out` with an index per point.
59
+ *
60
+ * Returns how many points were written, and **the count is the kind**: eight is a cap disc, two is
61
+ * the side line, one is a rim point. Nothing else can touch, which is why no separate tag is
62
+ * carried and why the caller reads the count instead.
63
+ *
64
+ * The indices are what a feature id is built from, and they are stable while the cylinder keeps its
65
+ * orientation: a cap point is its position around the basis, a side point is which end it is.
66
+ */
67
+ export declare function cylinderFeature(cyl: Float64Array, nx: number, ny: number, nz: number, out: Float64Array, ids: Int32Array): number;
68
+ /**
69
+ * The nearest point on a cylinder's surface to `p`, written as
70
+ * `[x, y, z, nx, ny, nz, signedDistance]` — the point, the outward direction there, and how far
71
+ * `p` sits from it. Negative distance means `p` is inside, and the direction is then the cheapest
72
+ * way out.
73
+ *
74
+ * Four regions, and each of them is exact: beside the side, past a cap, past a rim, or inside. The
75
+ * inside case picks the nearer of the side and the cap, which is what "cheapest way out" means and
76
+ * what keeps a deeply-buried particle from being pushed the long way through the solid.
77
+ */
78
+ export declare function closestOnCylinder(cyl: Float64Array, px: number, py: number, pz: number, out: Float64Array): void;
@@ -0,0 +1,294 @@
1
+ /**
2
+ * A cylinder resolved into world space, and the three questions contact generation asks of it.
3
+ *
4
+ * **This module is the whole of what `sideRadius` means geometrically**, kept apart from
5
+ * `cylinderContact.ts` so the arithmetic can be read and tested without a manifold in the way. A
6
+ * cylinder is the Minkowski sum of a segment and a disc perpendicular to it: its support along a
7
+ * direction `d` is the segment's support plus `radius` times the part of `d` that is perpendicular
8
+ * to the axis, which is one square root and no trigonometry.
9
+ *
10
+ * The three questions:
11
+ *
12
+ * - **How far does it reach along `d`** — `cylinderSupport`, which is what a separating-axis test
13
+ * needs and what makes the axis list exact for a curved surface it never enumerates.
14
+ * - **What is touching along `d`** — `cylinderFeature`, which answers with a cap disc, a side
15
+ * segment or a single rim point, because those are the only three a cylinder has.
16
+ * - **Where is the nearest surface point to `p`** — `closestOnCylinder`, which is exact in all
17
+ * four regions and is how a sphere, a capsule or a cloth particle meets one.
18
+ *
19
+ * **No `Math.cos` anywhere**, which is not a style choice: `scripts/determinism.test.mjs` refuses
20
+ * every function ECMAScript declines to specify precisely, and a cap disc generated by trigonometry
21
+ * per tick would be a different set of points on two engines. The eight directions come from a
22
+ * literal table instead, and everything else is arithmetic and `Math.sqrt`.
23
+ */
24
+ /** A cylinder in world space: centre, unit axis, half length, radius, and a spin-tracking basis. */
25
+ export const CYLINDER_FLOATS = 11;
26
+ /** How many points a cap disc contributes. Eight holds a disc flat without reducing to a square. */
27
+ export const CAP_POINTS = 8;
28
+ /**
29
+ * The eight unit directions of a cap disc, as literals.
30
+ *
31
+ * `Math.cos(k * Math.PI / 4)` is what these are, evaluated once by hand rather than per tick —
32
+ * `0.7071067811865476` is the double nearest √2/2. Written out because the determinism gate bans
33
+ * the call and because eight pairs are shorter than the loop that would derive them.
34
+ */
35
+ const CIRCLE = new Float64Array([
36
+ 1, 0, 0.7071067811865476, 0.7071067811865476, 0, 1, -0.7071067811865476, 0.7071067811865476, -1,
37
+ 0, -0.7071067811865476, -0.7071067811865476, 0, -1, 0.7071067811865476, -0.7071067811865476,
38
+ ]);
39
+ /**
40
+ * How parallel a direction must be to the axis before the whole cap is the touching feature, and
41
+ * how perpendicular before the whole side line is.
42
+ *
43
+ * Three degrees each way. **Generous on purpose**: a cylinder tilted by half a degree touches a
44
+ * plane at exactly one rim point, geometrically, and reporting that single point is what makes a
45
+ * resting wheel jitter. Two points get their separations measured against the reference plane
46
+ * individually, so the tilt is still resolved correctly — the constraint sees one contact deeper
47
+ * than the other, which is the truth — and the pair holds the shape steady meanwhile. Box-against-
48
+ * box clipping makes the same trade for the same reason.
49
+ */
50
+ const CAP_DOT = 0.99862953;
51
+ const SIDE_DOT = 0.05233596;
52
+ /** Whether a shape is a cylinder, which is the one question every dispatch above asks. */
53
+ export function isCylinder(shape) {
54
+ return shape.sideRadius > 0;
55
+ }
56
+ /**
57
+ * The pose a shape already in its own coordinates has.
58
+ *
59
+ * Exported because two callers work in a shape's local frame and would otherwise each declare
60
+ * their own identity quaternion — `readCylinder` costs nothing extra for one, since a rotation by
61
+ * the identity is the arithmetic it was going to do anyway.
62
+ */
63
+ export const LOCAL_POSE = { x: 0, y: 0, z: 0, qx: 0, qy: 0, qz: 0, qw: 1 };
64
+ /**
65
+ * Resolve a cylinder shape and its pose into `out`: centre, axis, half length, radius, basis.
66
+ *
67
+ * **The basis is the part worth explaining.** A cap disc's eight points have to be *the same eight
68
+ * points* from one tick to the next or their accumulated impulses are handed to the wrong contact,
69
+ * and a basis derived from the world axis alone stands still while the body spins about it. So it
70
+ * comes from the body's own local x, rotated by the pose and orthogonalised against the axis: it
71
+ * turns with the body, which is what a feature id has to do.
72
+ *
73
+ * A shape whose axis *is* local x — a cylinder baked into world space along that direction by a
74
+ * collider — falls back to local z, which cannot also be parallel to it.
75
+ */
76
+ export function readCylinder(shape, pose, out) {
77
+ rotate(pose, shape.vertices[0] ?? 0, shape.vertices[1] ?? 0, shape.vertices[2] ?? 0, END, 0);
78
+ rotate(pose, shape.vertices[3] ?? 0, shape.vertices[4] ?? 0, shape.vertices[5] ?? 0, END, 3);
79
+ out[0] = (END[0] + END[3]) / 2 + pose.x;
80
+ out[1] = (END[1] + END[4]) / 2 + pose.y;
81
+ out[2] = (END[2] + END[5]) / 2 + pose.z;
82
+ const dx = END[3] - END[0];
83
+ const dy = END[4] - END[1];
84
+ const dz = END[5] - END[2];
85
+ const length = Math.sqrt(dx * dx + dy * dy + dz * dz);
86
+ const inv = length > 1e-9 ? 1 / length : 0;
87
+ out[3] = length > 1e-9 ? dx * inv : 0;
88
+ out[4] = length > 1e-9 ? dy * inv : 1;
89
+ out[5] = length > 1e-9 ? dz * inv : 0;
90
+ out[6] = length / 2;
91
+ out[7] = shape.sideRadius;
92
+ rotate(pose, 1, 0, 0, END, 6);
93
+ let bx = END[6];
94
+ let by = END[7];
95
+ let bz = END[8];
96
+ let along = bx * out[3] + by * out[4] + bz * out[5];
97
+ if (Math.abs(along) > 0.9) {
98
+ rotate(pose, 0, 0, 1, END, 6);
99
+ bx = END[6];
100
+ by = END[7];
101
+ bz = END[8];
102
+ along = bx * out[3] + by * out[4] + bz * out[5];
103
+ }
104
+ bx -= out[3] * along;
105
+ by -= out[4] * along;
106
+ bz -= out[5] * along;
107
+ const blen = Math.sqrt(bx * bx + by * by + bz * bz) || 1;
108
+ out[8] = bx / blen;
109
+ out[9] = by / blen;
110
+ out[10] = bz / blen;
111
+ }
112
+ /** Scratch for the two endpoints and the candidate basis, so `readCylinder` allocates nothing. */
113
+ const END = new Float64Array(9);
114
+ /** How far the cylinder reaches along a unit direction: the highest `p · n` over its surface. */
115
+ export function cylinderSupport(cyl, nx, ny, nz) {
116
+ const along = (cyl[3] ?? 0) * nx + (cyl[4] ?? 0) * ny + (cyl[5] ?? 0) * nz;
117
+ const across = Math.sqrt(Math.max(0, 1 - along * along));
118
+ return ((cyl[0] ?? 0) * nx +
119
+ (cyl[1] ?? 0) * ny +
120
+ (cyl[2] ?? 0) * nz +
121
+ (cyl[6] ?? 0) * Math.abs(along) +
122
+ (cyl[7] ?? 0) * across);
123
+ }
124
+ /** The lowest `p · n` over its surface, which is the support in the opposite direction. */
125
+ export function cylinderMin(cyl, nx, ny, nz) {
126
+ const along = (cyl[3] ?? 0) * nx + (cyl[4] ?? 0) * ny + (cyl[5] ?? 0) * nz;
127
+ const across = Math.sqrt(Math.max(0, 1 - along * along));
128
+ return ((cyl[0] ?? 0) * nx +
129
+ (cyl[1] ?? 0) * ny +
130
+ (cyl[2] ?? 0) * nz -
131
+ (cyl[6] ?? 0) * Math.abs(along) -
132
+ (cyl[7] ?? 0) * across);
133
+ }
134
+ /**
135
+ * The feature touching along unit `n`, written xyz-packed into `out` with an index per point.
136
+ *
137
+ * Returns how many points were written, and **the count is the kind**: eight is a cap disc, two is
138
+ * the side line, one is a rim point. Nothing else can touch, which is why no separate tag is
139
+ * carried and why the caller reads the count instead.
140
+ *
141
+ * The indices are what a feature id is built from, and they are stable while the cylinder keeps its
142
+ * orientation: a cap point is its position around the basis, a side point is which end it is.
143
+ */
144
+ export function cylinderFeature(cyl, nx, ny, nz, out, ids) {
145
+ const cx = cyl[0] ?? 0;
146
+ const cy = cyl[1] ?? 0;
147
+ const cz = cyl[2] ?? 0;
148
+ const ax = cyl[3] ?? 0;
149
+ const ay = cyl[4] ?? 0;
150
+ const az = cyl[5] ?? 0;
151
+ const h = cyl[6] ?? 0;
152
+ const r = cyl[7] ?? 0;
153
+ const along = ax * nx + ay * ny + az * nz;
154
+ if (Math.abs(along) >= CAP_DOT) {
155
+ const side = along >= 0 ? 1 : -1;
156
+ const ux = cyl[8] ?? 0;
157
+ const uy = cyl[9] ?? 0;
158
+ const uz = cyl[10] ?? 0;
159
+ // The third basis vector, so the eight directions sweep the cap plane rather than a line.
160
+ const vx = ay * uz - az * uy;
161
+ const vy = az * ux - ax * uz;
162
+ const vz = ax * uy - ay * ux;
163
+ const ox = cx + ax * h * side;
164
+ const oy = cy + ay * h * side;
165
+ const oz = cz + az * h * side;
166
+ for (let i = 0; i < CAP_POINTS; i++) {
167
+ const c = CIRCLE[i * 2] ?? 0;
168
+ const s = CIRCLE[i * 2 + 1] ?? 0;
169
+ out[i * 3] = ox + (ux * c + vx * s) * r;
170
+ out[i * 3 + 1] = oy + (uy * c + vy * s) * r;
171
+ out[i * 3 + 2] = oz + (uz * c + vz * s) * r;
172
+ ids[i] = (side > 0 ? 0x10 : 0x20) | i;
173
+ }
174
+ return CAP_POINTS;
175
+ }
176
+ // The radial part of `n`, which is where the curved side reaches furthest.
177
+ let wx = nx - ax * along;
178
+ let wy = ny - ay * along;
179
+ let wz = nz - az * along;
180
+ const wlen = Math.sqrt(wx * wx + wy * wy + wz * wz) || 1;
181
+ wx /= wlen;
182
+ wy /= wlen;
183
+ wz /= wlen;
184
+ if (Math.abs(along) <= SIDE_DOT) {
185
+ out[0] = cx - ax * h + wx * r;
186
+ out[1] = cy - ay * h + wy * r;
187
+ out[2] = cz - az * h + wz * r;
188
+ out[3] = cx + ax * h + wx * r;
189
+ out[4] = cy + ay * h + wy * r;
190
+ out[5] = cz + az * h + wz * r;
191
+ ids[0] = 0x30;
192
+ ids[1] = 0x31;
193
+ return 2;
194
+ }
195
+ const side = along >= 0 ? 1 : -1;
196
+ out[0] = cx + ax * h * side + wx * r;
197
+ out[1] = cy + ay * h * side + wy * r;
198
+ out[2] = cz + az * h * side + wz * r;
199
+ ids[0] = side > 0 ? 0x40 : 0x41;
200
+ return 1;
201
+ }
202
+ /**
203
+ * The nearest point on a cylinder's surface to `p`, written as
204
+ * `[x, y, z, nx, ny, nz, signedDistance]` — the point, the outward direction there, and how far
205
+ * `p` sits from it. Negative distance means `p` is inside, and the direction is then the cheapest
206
+ * way out.
207
+ *
208
+ * Four regions, and each of them is exact: beside the side, past a cap, past a rim, or inside. The
209
+ * inside case picks the nearer of the side and the cap, which is what "cheapest way out" means and
210
+ * what keeps a deeply-buried particle from being pushed the long way through the solid.
211
+ */
212
+ export function closestOnCylinder(cyl, px, py, pz, out) {
213
+ const cx = cyl[0] ?? 0;
214
+ const cy = cyl[1] ?? 0;
215
+ const cz = cyl[2] ?? 0;
216
+ const ax = cyl[3] ?? 0;
217
+ const ay = cyl[4] ?? 0;
218
+ const az = cyl[5] ?? 0;
219
+ const h = cyl[6] ?? 0;
220
+ const r = cyl[7] ?? 0;
221
+ const dx = px - cx;
222
+ const dy = py - cy;
223
+ const dz = pz - cz;
224
+ const t = dx * ax + dy * ay + dz * az;
225
+ let wx = dx - ax * t;
226
+ let wy = dy - ay * t;
227
+ let wz = dz - az * t;
228
+ const rho = Math.sqrt(wx * wx + wy * wy + wz * wz);
229
+ if (rho > 1e-9) {
230
+ wx /= rho;
231
+ wy /= rho;
232
+ wz /= rho;
233
+ }
234
+ else {
235
+ // On the axis: any radial direction is as good as another, and the basis is the stable one.
236
+ wx = cyl[8] ?? 1;
237
+ wy = cyl[9] ?? 0;
238
+ wz = cyl[10] ?? 0;
239
+ }
240
+ if (rho <= r && Math.abs(t) <= h) {
241
+ const toSide = r - rho;
242
+ const toCap = h - Math.abs(t);
243
+ if (toSide <= toCap) {
244
+ out[0] = cx + ax * t + wx * r;
245
+ out[1] = cy + ay * t + wy * r;
246
+ out[2] = cz + az * t + wz * r;
247
+ out[3] = wx;
248
+ out[4] = wy;
249
+ out[5] = wz;
250
+ out[6] = -toSide;
251
+ return;
252
+ }
253
+ const side = t >= 0 ? 1 : -1;
254
+ out[0] = cx + ax * h * side + wx * rho;
255
+ out[1] = cy + ay * h * side + wy * rho;
256
+ out[2] = cz + az * h * side + wz * rho;
257
+ out[3] = ax * side;
258
+ out[4] = ay * side;
259
+ out[5] = az * side;
260
+ out[6] = -toCap;
261
+ return;
262
+ }
263
+ const tc = t < -h ? -h : t > h ? h : t;
264
+ const rc = rho < r ? rho : r;
265
+ const qx = cx + ax * tc + wx * rc;
266
+ const qy = cy + ay * tc + wy * rc;
267
+ const qz = cz + az * tc + wz * rc;
268
+ const ex = px - qx;
269
+ const ey = py - qy;
270
+ const ez = pz - qz;
271
+ const d = Math.sqrt(ex * ex + ey * ey + ez * ez);
272
+ out[0] = qx;
273
+ out[1] = qy;
274
+ out[2] = qz;
275
+ if (d > 1e-9) {
276
+ out[3] = ex / d;
277
+ out[4] = ey / d;
278
+ out[5] = ez / d;
279
+ }
280
+ else {
281
+ out[3] = wx;
282
+ out[4] = wy;
283
+ out[5] = wz;
284
+ }
285
+ out[6] = d;
286
+ }
287
+ function rotate(p, x, y, z, out, at) {
288
+ const tx = 2 * (p.qy * z - p.qz * y);
289
+ const ty = 2 * (p.qz * x - p.qx * z);
290
+ const tz = 2 * (p.qx * y - p.qy * x);
291
+ out[at] = x + p.qw * tx + (p.qy * tz - p.qz * ty);
292
+ out[at + 1] = y + p.qw * ty + (p.qz * tx - p.qx * tz);
293
+ out[at + 2] = z + p.qw * tz + (p.qx * ty - p.qy * tx);
294
+ }
@@ -0,0 +1,7 @@
1
+ import type { Manifold, ShapePose } from './manifold.ts';
2
+ import type { ConvexShape } from './shape.ts';
3
+ /**
4
+ * Fill `out` for a pair where at least one shape is a cylinder. Returns whether they are within
5
+ * `margin` of touching, exactly as `collideShapes` does — this is that function's fourth case.
6
+ */
7
+ export declare function collideCylinder(shapeA: ConvexShape, poseA: ShapePose, shapeB: ConvexShape, poseB: ShapePose, margin: number, out: Manifold): boolean;