@woosh/meep-engine 2.146.0 → 2.148.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.
- package/package.json +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +4 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +48 -52
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +23 -21
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +41 -406
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +5 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +400 -395
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +0 -11
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +8 -6
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +552 -551
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts +8 -3
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/LedgeGrab.js +213 -199
- package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Mantle.js +195 -188
- package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/WallRun.js +183 -175
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts +9 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensors.js +87 -77
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +8 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +229 -196
- package/src/engine/ecs/EntityManager.d.ts +34 -11
- package/src/engine/ecs/EntityManager.d.ts.map +1 -1
- package/src/engine/ecs/EntityManager.js +71 -42
- package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/Interpoland.d.ts +48 -0
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -0
- package/src/engine/interpolation/Interpoland.js +49 -0
- package/src/engine/interpolation/Interpolated.d.ts +101 -0
- package/src/engine/interpolation/Interpolated.d.ts.map +1 -0
- package/src/engine/interpolation/Interpolated.js +149 -0
- package/src/engine/{network/sim → interpolation}/InterpolationLog.d.ts +1 -1
- package/src/engine/interpolation/InterpolationLog.d.ts.map +1 -0
- package/src/engine/{network/sim → interpolation}/InterpolationLog.js +2 -2
- package/src/engine/interpolation/InterpolationSystem.d.ts +116 -0
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -0
- package/src/engine/interpolation/InterpolationSystem.js +233 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts +17 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +35 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -0
- package/src/engine/interpolation/pose_interpoland.d.ts +18 -0
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -0
- package/src/engine/interpolation/pose_interpoland.js +27 -0
- package/src/engine/network/NetworkSession.d.ts +2 -2
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +2 -2
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.js +1 -1
- package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -0
- package/src/engine/physics/PLAN.md +10 -9
- package/src/engine/physics/body/SolverBodyState.d.ts +142 -0
- package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -0
- package/src/engine/physics/body/SolverBodyState.js +251 -0
- package/src/engine/physics/broadphase/generate_pairs.d.ts +2 -1
- package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
- package/src/engine/physics/broadphase/generate_pairs.js +5 -3
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +691 -673
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +82 -15
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +387 -87
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +23 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +116 -77
- package/src/engine/physics/integration/integrate_position.d.ts +11 -1
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +97 -79
- package/src/engine/physics/integration/integrate_velocity.d.ts +12 -3
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +201 -160
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +750 -665
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_triangle_contact.js +19 -46
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts +16 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.js +49 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +52 -4
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +7 -4
- package/src/engine/physics/solver/solve_contacts.d.ts +2 -2
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +1341 -1173
- package/src/engine/network/sim/BinaryInterpolationAdapter.d.ts.map +0 -1
- package/src/engine/network/sim/InterpolationLog.d.ts.map +0 -1
- /package/src/engine/{network/sim → interpolation}/BinaryInterpolationAdapter.d.ts +0 -0
- /package/src/engine/{network/sim → interpolation}/BinaryInterpolationAdapter.js +0 -0
|
@@ -1,395 +1,400 @@
|
|
|
1
|
-
import { cubic_residual_times_derivative_accumulate } from "../linalg/cubic_residual_times_derivative_accumulate.js";
|
|
2
|
-
import { polynomial_cubic_horner_eval } from "../linalg/polynomial_cubic_horner_eval.js";
|
|
3
|
-
import { polynomial_real_roots_in_interval } from "../linalg/polynomial_real_roots_in_interval.js";
|
|
4
|
-
import { spline3_hermite_to_monomial } from "./spline3_hermite_to_monomial.js";
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
interior critical points by 2D Newton from a
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
the
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
for (let
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* @
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
1
|
+
import { cubic_residual_times_derivative_accumulate } from "../linalg/cubic_residual_times_derivative_accumulate.js";
|
|
2
|
+
import { polynomial_cubic_horner_eval } from "../linalg/polynomial_cubic_horner_eval.js";
|
|
3
|
+
import { polynomial_real_roots_in_interval } from "../linalg/polynomial_real_roots_in_interval.js";
|
|
4
|
+
import { spline3_hermite_to_monomial } from "./spline3_hermite_to_monomial.js";
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Dimension-agnostic curve-pair closest-approach / intersection enumerator
|
|
8
|
+
(dim ≥ 2). This is the path the dispatcher uses for all dim ≥ 2: the former
|
|
9
|
+
bespoke 2D Bezout specialization was removed because it enumerated interior
|
|
10
|
+
*intersections* plus boundary minima only and so missed interior
|
|
11
|
+
non-intersection closest approaches. Closest approach is the global minimum of
|
|
12
|
+
|
|
13
|
+
Φ(s, t) = Σ_d (A_d(s) − B_d(t))²
|
|
14
|
+
|
|
15
|
+
over the closed unit square [0, 1]². It can occur at:
|
|
16
|
+
1. an interior critical point where ∇Φ = 0
|
|
17
|
+
2. a boundary edge minimum (one parameter pinned at 0 or 1)
|
|
18
|
+
3. a corner
|
|
19
|
+
|
|
20
|
+
Interior critical points are roots of the bivariate gradient system
|
|
21
|
+
|
|
22
|
+
F(s, t) = ½ ∂Φ/∂s = Σ_d (A_d(s) − B_d(t)) A_d′(s) (bidegree (5, 3))
|
|
23
|
+
G(s, t) = ½ ∂Φ/∂t = −Σ_d (A_d(s) − B_d(t)) B_d′(t) (bidegree (3, 5))
|
|
24
|
+
|
|
25
|
+
This module finds those interior critical points by 2D Newton started from a
|
|
26
|
+
GRID_SIDE × GRID_SIDE grid in [0, 1]² — quadratically convergent once close,
|
|
27
|
+
but reliant on the grid being dense enough to cover the basins of attraction
|
|
28
|
+
of all real critical points (9×9 = 81 starts is dense enough for the
|
|
29
|
+
geometric configurations seen in practice). A fully analytical alternative
|
|
30
|
+
eliminates t through the 8×8 Sylvester resultant of F and G, yielding a
|
|
31
|
+
univariate of degree ≤ 34 in s; it is exact and basin-independent but
|
|
32
|
+
genuinely large code with delicate degree-34 root-finding numerics, so it is
|
|
33
|
+
left as a possible future drop-in behind this same public API rather than
|
|
34
|
+
implemented here.
|
|
35
|
+
|
|
36
|
+
Boundary edges and corners are handled exactly: each edge reduces to a
|
|
37
|
+
quintic-derivative root-finding (degree 5), and corners fall out as the
|
|
38
|
+
endpoints tested inside each edge solve.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const MAX_DIM = 16;
|
|
42
|
+
|
|
43
|
+
// Per-dim monomial coefficient buffers, one each for A and B. Length 4*MAX_DIM.
|
|
44
|
+
const _a_mono = new Float64Array(4 * MAX_DIM);
|
|
45
|
+
const _b_mono = new Float64Array(4 * MAX_DIM);
|
|
46
|
+
|
|
47
|
+
const _candidate_st = new Float64Array(2);
|
|
48
|
+
const _edge_fixed_point = new Float64Array(MAX_DIM);
|
|
49
|
+
|
|
50
|
+
const NEWTON_MAX_ITER = 32;
|
|
51
|
+
const NEWTON_GRADIENT_TOLERANCE = 1e-13;
|
|
52
|
+
const NEWTON_STEP_TOLERANCE = 1e-15;
|
|
53
|
+
|
|
54
|
+
const GRID_SIDE = 9; // 9x9 = 81 starting points
|
|
55
|
+
|
|
56
|
+
const EDGE_QUINTIC_LEN = 6;
|
|
57
|
+
const _edge_quintic = new Float64Array(EDGE_QUINTIC_LEN);
|
|
58
|
+
const _edge_roots = new Float64Array(5);
|
|
59
|
+
|
|
60
|
+
const CRITICAL_POINT_DEDUPE_TOLERANCE = 1e-7;
|
|
61
|
+
const _seen_s = new Float64Array(GRID_SIDE * GRID_SIDE + 8);
|
|
62
|
+
const _seen_t = new Float64Array(GRID_SIDE * GRID_SIDE + 8);
|
|
63
|
+
|
|
64
|
+
// The gradient system (F, G) = (½ ∂Φ/∂s, ½ ∂Φ/∂t) and the 2×2 Jacobian
|
|
65
|
+
// of that gradient are inlined directly into `newton_2d`'s hot loop — see
|
|
66
|
+
// the module preamble for the algebraic definitions of F, G, and J.
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 2D Newton on (F, G) from a starting (s, t). Writes the converged (s, t) into
|
|
70
|
+
* `out` and returns true iff it converged inside [0, 1]² with small gradient.
|
|
71
|
+
*/
|
|
72
|
+
// Tolerance for declaring the 2x2 Jacobian singular during the inlined
|
|
73
|
+
// Newton solve. Matches `256 * FLT_EPSILON_64` used by
|
|
74
|
+
// solve_linear_system_GEPP_2x2 to keep behaviour identical on edge cases.
|
|
75
|
+
const NEWTON_SINGULAR_TOLERANCE = 256 * 1.1102230246251565e-16;
|
|
76
|
+
|
|
77
|
+
function newton_2d(dim, s_init, t_init, out) {
|
|
78
|
+
let s = s_init;
|
|
79
|
+
let t = t_init;
|
|
80
|
+
|
|
81
|
+
// F, G at the current (s, t) — promoted out of the heap-resident
|
|
82
|
+
// `_newton_FG` scratch into iteration-local doubles.
|
|
83
|
+
let F = 0, G = 0;
|
|
84
|
+
let fg_valid_at_current_st = false;
|
|
85
|
+
|
|
86
|
+
for (let iter = 0; iter < NEWTON_MAX_ITER; iter++) {
|
|
87
|
+
// ── fused F/G + Jacobian over all dimensions ──
|
|
88
|
+
// A single per-iteration sweep over dims computes the gradient
|
|
89
|
+
// (F, G) and the symmetric Hessian (Jss, J_cross, Jtt) from one
|
|
90
|
+
// load of each cubic's 4 coefficients. Eagerly computing the
|
|
91
|
+
// Hessian costs us one extra second-derivative + 3 accumulations
|
|
92
|
+
// per dim on the iteration that ends up converging — cheap insurance
|
|
93
|
+
// for skipping a duplicate cubic-eval sweep on every non-converging
|
|
94
|
+
// iteration. J_st = J_ts so the symmetric cross term is computed
|
|
95
|
+
// once into J_cross.
|
|
96
|
+
F = 0;
|
|
97
|
+
G = 0;
|
|
98
|
+
let Jss = 0, J_cross = 0, Jtt = 0;
|
|
99
|
+
|
|
100
|
+
for (let d = 0; d < dim; d++) {
|
|
101
|
+
const base = 4 * d;
|
|
102
|
+
const a0 = _a_mono[base];
|
|
103
|
+
const a1 = _a_mono[base + 1];
|
|
104
|
+
const a2 = _a_mono[base + 2];
|
|
105
|
+
const a3 = _a_mono[base + 3];
|
|
106
|
+
const b0 = _b_mono[base];
|
|
107
|
+
const b1 = _b_mono[base + 1];
|
|
108
|
+
const b2 = _b_mono[base + 2];
|
|
109
|
+
const b3 = _b_mono[base + 3];
|
|
110
|
+
|
|
111
|
+
const A_v = a0 + s * (a1 + s * (a2 + s * a3));
|
|
112
|
+
const A_p = a1 + s * (2 * a2 + s * 3 * a3);
|
|
113
|
+
const A_pp = 2 * a2 + 6 * a3 * s;
|
|
114
|
+
const B_v = b0 + t * (b1 + t * (b2 + t * b3));
|
|
115
|
+
const B_p = b1 + t * (2 * b2 + t * 3 * b3);
|
|
116
|
+
const B_pp = 2 * b2 + 6 * b3 * t;
|
|
117
|
+
|
|
118
|
+
const diff = A_v - B_v;
|
|
119
|
+
F += diff * A_p;
|
|
120
|
+
G -= diff * B_p;
|
|
121
|
+
Jss += A_p * A_p + diff * A_pp;
|
|
122
|
+
J_cross += -A_p * B_p;
|
|
123
|
+
Jtt += B_p * B_p - diff * B_pp;
|
|
124
|
+
}
|
|
125
|
+
fg_valid_at_current_st = true;
|
|
126
|
+
|
|
127
|
+
if (Math.abs(F) < NEWTON_GRADIENT_TOLERANCE && Math.abs(G) < NEWTON_GRADIENT_TOLERANCE) {
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ── inline solve_linear_system_GEPP_2x2 on column-major J ──
|
|
132
|
+
// J = [a00, a10; a01, a11] = [Jss, J_cross; J_cross, Jtt]; rhs = [F, G]
|
|
133
|
+
// Partial pivoting on column 0, then forward eliminate, then back-sub.
|
|
134
|
+
// The "exact zero" special cases handled by the full solver are skipped:
|
|
135
|
+
// for a smooth residual Newton, exact zeros essentially never arise
|
|
136
|
+
// (random + analytic inputs), and a singular Jacobian gets rejected
|
|
137
|
+
// below the same way the full solver would.
|
|
138
|
+
let a00 = Jss, a10 = J_cross, a01 = J_cross, a11 = Jtt;
|
|
139
|
+
let b0 = F, b1 = G;
|
|
140
|
+
if (Math.abs(a10) > Math.abs(a00)) {
|
|
141
|
+
// Row swap
|
|
142
|
+
let tmp = a00; a00 = a10; a10 = tmp;
|
|
143
|
+
tmp = a01; a01 = a11; a11 = tmp;
|
|
144
|
+
tmp = b0; b0 = b1; b1 = tmp;
|
|
145
|
+
}
|
|
146
|
+
if (a00 === 0) {
|
|
147
|
+
return false; // singular column 0
|
|
148
|
+
}
|
|
149
|
+
const f = -a10 / a00;
|
|
150
|
+
a11 += a01 * f;
|
|
151
|
+
b1 += b0 * f;
|
|
152
|
+
if (Math.abs(a11) < NEWTON_SINGULAR_TOLERANCE) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const dt = b1 / a11;
|
|
156
|
+
const ds = (b0 - a01 * dt) / a00;
|
|
157
|
+
if (!Number.isFinite(ds) || !Number.isFinite(dt)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
s -= ds;
|
|
162
|
+
t -= dt;
|
|
163
|
+
|
|
164
|
+
// (s, t) has just changed → cached F, G no longer match.
|
|
165
|
+
fg_valid_at_current_st = false;
|
|
166
|
+
|
|
167
|
+
// Early bail: if (s, t) has wandered well outside [0, 1] the start is
|
|
168
|
+
// diverging or homed in on a critical point of the gradient system
|
|
169
|
+
// that lies far from the parameter square. Newton won't reel it back.
|
|
170
|
+
if (s < -0.5 || s > 1.5 || t < -0.5 || t > 1.5) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (Math.abs(ds) < NEWTON_STEP_TOLERANCE && Math.abs(dt) < NEWTON_STEP_TOLERANCE) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (s < -1e-9 || s > 1 + 1e-9) return false;
|
|
180
|
+
if (t < -1e-9 || t > 1 + 1e-9) return false;
|
|
181
|
+
|
|
182
|
+
// Validate gradient at the final (s, t). When `fg_valid_at_current_st`
|
|
183
|
+
// is true we exited the loop via the gradient-tolerance break and the
|
|
184
|
+
// validation already passed (tolerance 1e-13 < 1e-6); otherwise we
|
|
185
|
+
// exited via the step-tolerance break or maxed out — re-evaluate to
|
|
186
|
+
// confirm.
|
|
187
|
+
if (!fg_valid_at_current_st) {
|
|
188
|
+
F = 0;
|
|
189
|
+
G = 0;
|
|
190
|
+
for (let d = 0; d < dim; d++) {
|
|
191
|
+
const base = 4 * d;
|
|
192
|
+
const a0 = _a_mono[base];
|
|
193
|
+
const a1 = _a_mono[base + 1];
|
|
194
|
+
const a2 = _a_mono[base + 2];
|
|
195
|
+
const a3 = _a_mono[base + 3];
|
|
196
|
+
const b0 = _b_mono[base];
|
|
197
|
+
const b1 = _b_mono[base + 1];
|
|
198
|
+
const b2 = _b_mono[base + 2];
|
|
199
|
+
const b3 = _b_mono[base + 3];
|
|
200
|
+
|
|
201
|
+
const A_v = a0 + s * (a1 + s * (a2 + s * a3));
|
|
202
|
+
const A_p = a1 + s * (2 * a2 + s * 3 * a3);
|
|
203
|
+
const B_v = b0 + t * (b1 + t * (b2 + t * b3));
|
|
204
|
+
const B_p = b1 + t * (2 * b2 + t * 3 * b3);
|
|
205
|
+
|
|
206
|
+
const diff = A_v - B_v;
|
|
207
|
+
F += diff * A_p;
|
|
208
|
+
G -= diff * B_p;
|
|
209
|
+
}
|
|
210
|
+
if (Math.abs(F) > 1e-6 || Math.abs(G) > 1e-6) return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
out[0] = s < 0 ? 0 : (s > 1 ? 1 : s);
|
|
214
|
+
out[1] = t < 0 ? 0 : (t > 1 ? 1 : t);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── boundary edge: nearest point on (varying-axis) curve to a fixed N-D point ──
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Minimise ‖(curve_mono...)(t) − point[]‖² over t ∈ [0, 1], where the curve
|
|
222
|
+
* is `dim`-dimensional with monomial coefficients packed as
|
|
223
|
+
* `[α0_0, α1_0, α2_0, α3_0, α0_1, α1_1, ..., α3_{dim-1}]`.
|
|
224
|
+
*
|
|
225
|
+
* The squared distance is a degree-6 polynomial in t; its derivative is a
|
|
226
|
+
* quintic. We hand the quintic to the polynomial root finder, then test the
|
|
227
|
+
* roots plus the endpoints t = 0 and t = 1.
|
|
228
|
+
*
|
|
229
|
+
* Writes `[t_min, dist²]` into `out` (length ≥ 2).
|
|
230
|
+
*/
|
|
231
|
+
function nearest_t_to_fixed_point(curve_mono, dim, point, point_offset, out) {
|
|
232
|
+
for (let i = 0; i < EDGE_QUINTIC_LEN; i++){
|
|
233
|
+
_edge_quintic[i] = 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (let d = 0; d < dim; d++) {
|
|
237
|
+
cubic_residual_times_derivative_accumulate(
|
|
238
|
+
_edge_quintic, curve_mono, 4 * d, point[point_offset + d]
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Aberth iteration cap of 32 is plenty for the degree-5 quintic that
|
|
243
|
+
// comes out of `(p(t) - offset)·p'(t)` on a smooth Hermite cubic — the
|
|
244
|
+
// routine's own convergence check still fires early in the common case.
|
|
245
|
+
// Default cap (80) is way more than needed and is the worst-case work
|
|
246
|
+
// budget; lowering it is a flat ~10-20% saving on degenerate inputs.
|
|
247
|
+
const root_count = polynomial_real_roots_in_interval(
|
|
248
|
+
_edge_quintic, 5, 0, 1, _edge_roots, 0,
|
|
249
|
+
32
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
let best_t = 0;
|
|
253
|
+
let best_d2 = Number.POSITIVE_INFINITY;
|
|
254
|
+
|
|
255
|
+
for (let i = -2; i < root_count; i++) {
|
|
256
|
+
|
|
257
|
+
const t = i === -2 ? 0 : (i === -1 ? 1 : _edge_roots[i]);
|
|
258
|
+
let d2 = 0;
|
|
259
|
+
|
|
260
|
+
for (let d = 0; d < dim; d++) {
|
|
261
|
+
const base = 4 * d;
|
|
262
|
+
const v = polynomial_cubic_horner_eval(curve_mono, base, t);
|
|
263
|
+
const dv = v - point[point_offset + d];
|
|
264
|
+
d2 += dv * dv;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (d2 < best_d2) {
|
|
268
|
+
best_d2 = d2;
|
|
269
|
+
best_t = t;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
out[0] = best_t;
|
|
275
|
+
out[1] = best_d2;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ── public ────────────────────────────────────────────────────────────────
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Upper bound on the number of (s, t) pairs the ND variant can return:
|
|
282
|
+
* one per starting point of the GRID_SIDE × GRID_SIDE grid Newton, plus the
|
|
283
|
+
* four boundary edges, plus a small slack. The corresponding result-buffer
|
|
284
|
+
* size in floats is `2 * ND_MAX_ROOTS`.
|
|
285
|
+
*
|
|
286
|
+
* @type {number}
|
|
287
|
+
*/
|
|
288
|
+
export const ND_MAX_ROOTS = GRID_SIDE * GRID_SIDE + 8;
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Critical-point enumerator for the dimension-agnostic Hermite curve-pair
|
|
292
|
+
* intersection / closest-approach problem (dim ≥ 2; the dispatcher routes all
|
|
293
|
+
* dim ≥ 2 here). Writes (s, t) pairs sequentially into `result` starting at
|
|
294
|
+
* `result_offset` and returns the count.
|
|
295
|
+
*
|
|
296
|
+
* What is reported (deduplicated within
|
|
297
|
+
* CRITICAL_POINT_DEDUPE_TOLERANCE = 1e-7 in each parameter):
|
|
298
|
+
* - Interior critical points of squared distance found by 2D Newton
|
|
299
|
+
* started from a GRID_SIDE × GRID_SIDE grid in [0,1]² and accepted
|
|
300
|
+
* when they converge inside [0,1]² with small gradient.
|
|
301
|
+
* - Four boundary closest-approach pairs, one per edge of [0,1]², each
|
|
302
|
+
* coming from a quintic root-find of (curve − fixed point)·curve′.
|
|
303
|
+
*
|
|
304
|
+
* Caller is responsible for evaluating both curves at each (s, t) and
|
|
305
|
+
* picking whichever pair(s) they care about (closest, within threshold,
|
|
306
|
+
* etc.).
|
|
307
|
+
*
|
|
308
|
+
* Required buffer size:
|
|
309
|
+
* `result.length >= result_offset + 2 * ND_MAX_ROOTS` floats (currently 178).
|
|
310
|
+
*
|
|
311
|
+
* @param {Float64Array|number[]} a length 4*dim
|
|
312
|
+
* @param {Float64Array|number[]} b length 4*dim
|
|
313
|
+
* @param {number} dim ≥ 2
|
|
314
|
+
* @param {Float64Array|number[]} result length >= result_offset + 2 * ND_MAX_ROOTS
|
|
315
|
+
* @param {number} result_offset
|
|
316
|
+
* @returns {number} number of (s, t) pairs written
|
|
317
|
+
*/
|
|
318
|
+
export function spline3_hermite_intersection_spline3_hermite_nd(
|
|
319
|
+
a, b, dim,
|
|
320
|
+
result, result_offset
|
|
321
|
+
) {
|
|
322
|
+
if (dim > MAX_DIM) {
|
|
323
|
+
throw new Error(`dim=${dim} exceeds MAX_DIM=${MAX_DIM}`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Pack monomial coefficients per-axis into the module buffers.
|
|
327
|
+
for (let d = 0; d < dim; d++) {
|
|
328
|
+
const off = 4 * d;
|
|
329
|
+
spline3_hermite_to_monomial(_a_mono, off, 1, a[off], a[off + 1], a[off + 2], a[off + 3]);
|
|
330
|
+
spline3_hermite_to_monomial(_b_mono, off, 1, b[off], b[off + 1], b[off + 2], b[off + 3]);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// We dedupe near-identical critical points discovered from different grid
|
|
334
|
+
// starts. _seen_s / _seen_t double as the "candidates written so far" and
|
|
335
|
+
// are mirrored into the caller's `result` buffer as we go.
|
|
336
|
+
let count = 0;
|
|
337
|
+
let write = result_offset;
|
|
338
|
+
|
|
339
|
+
const try_candidate = (s, t) => {
|
|
340
|
+
for (let i = 0; i < count; i++) {
|
|
341
|
+
if (Math.abs(_seen_s[i] - s) < CRITICAL_POINT_DEDUPE_TOLERANCE
|
|
342
|
+
&& Math.abs(_seen_t[i] - t) < CRITICAL_POINT_DEDUPE_TOLERANCE) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
_seen_s[count] = s;
|
|
347
|
+
_seen_t[count] = t;
|
|
348
|
+
result[write++] = s;
|
|
349
|
+
result[write++] = t;
|
|
350
|
+
count++;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// Interior critical points via Newton from a grid.
|
|
354
|
+
const step = 1 / (GRID_SIDE - 1);
|
|
355
|
+
for (let i = 0; i < GRID_SIDE; i++) {
|
|
356
|
+
const s_init = i * step;
|
|
357
|
+
for (let j = 0; j < GRID_SIDE; j++) {
|
|
358
|
+
const t_init = j * step;
|
|
359
|
+
if (newton_2d(dim, s_init, t_init, _candidate_st)) {
|
|
360
|
+
try_candidate(_candidate_st[0], _candidate_st[1]);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Boundary edges. Each edge problem reduces to "nearest point on the
|
|
366
|
+
// *other* curve to a fixed point on the boundary curve".
|
|
367
|
+
const tmp_pt = _edge_fixed_point;
|
|
368
|
+
|
|
369
|
+
// Edge s = 0: fixed point is A(0); minimise over t on B.
|
|
370
|
+
for (let d = 0; d < dim; d++){
|
|
371
|
+
tmp_pt[d] = _a_mono[4 * d]; // α0
|
|
372
|
+
}
|
|
373
|
+
nearest_t_to_fixed_point(_b_mono, dim, tmp_pt, 0, _candidate_st);
|
|
374
|
+
try_candidate(0, _candidate_st[0]);
|
|
375
|
+
|
|
376
|
+
// Edge s = 1: A(1) = α0 + α1 + α2 + α3.
|
|
377
|
+
for (let d = 0; d < dim; d++) {
|
|
378
|
+
const base = 4 * d;
|
|
379
|
+
tmp_pt[d] = _a_mono[base] + _a_mono[base + 1] + _a_mono[base + 2] + _a_mono[base + 3];
|
|
380
|
+
}
|
|
381
|
+
nearest_t_to_fixed_point(_b_mono, dim, tmp_pt, 0, _candidate_st);
|
|
382
|
+
try_candidate(1, _candidate_st[0]);
|
|
383
|
+
|
|
384
|
+
// Edge t = 0: fixed point is B(0); minimise over s on A.
|
|
385
|
+
for (let d = 0; d < dim; d++){
|
|
386
|
+
tmp_pt[d] = _b_mono[4 * d];
|
|
387
|
+
}
|
|
388
|
+
nearest_t_to_fixed_point(_a_mono, dim, tmp_pt, 0, _candidate_st);
|
|
389
|
+
try_candidate(_candidate_st[0], 0);
|
|
390
|
+
|
|
391
|
+
// Edge t = 1.
|
|
392
|
+
for (let d = 0; d < dim; d++) {
|
|
393
|
+
const base = 4 * d;
|
|
394
|
+
tmp_pt[d] = _b_mono[base] + _b_mono[base + 1] + _b_mono[base + 2] + _b_mono[base + 3];
|
|
395
|
+
}
|
|
396
|
+
nearest_t_to_fixed_point(_a_mono, dim, tmp_pt, 0, _candidate_st);
|
|
397
|
+
try_candidate(_candidate_st[0], 1);
|
|
398
|
+
|
|
399
|
+
return count;
|
|
400
|
+
}
|