@woosh/meep-engine 2.134.4 → 2.135.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 (156) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/editor/tools/v2/TransformControlsGizmo.js +1 -1
  4. package/editor/view/node-graph/NodeGraphEditorView.js +2 -2
  5. package/package.json +1 -1
  6. package/src/core/assert.d.ts +0 -2
  7. package/src/core/assert.d.ts.map +1 -1
  8. package/src/core/assert.js +0 -6
  9. package/src/core/color/Color.d.ts +0 -5
  10. package/src/core/color/Color.d.ts.map +1 -1
  11. package/src/core/color/Color.js +1 -7
  12. package/src/core/geom/2d/hash-grid/SpatialHashGrid.js +386 -386
  13. package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +1 -1
  14. package/src/core/geom/2d/quad-tree-binary/QuadTree.js +714 -714
  15. package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +160 -160
  16. package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +96 -96
  17. package/src/core/geom/packing/max-rect/MaxRectanglesPacker.js +1 -1
  18. package/src/core/geom/packing/max-rect/findBestContainer.js +4 -4
  19. package/src/core/geom/packing/max-rect/packOneBox.js +2 -2
  20. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts +23 -0
  21. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts.map +1 -0
  22. package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.js +96 -0
  23. package/src/core/graph/layout/box/BoxLayouter.js +7 -7
  24. package/src/core/graph/layout/box/position_box_next_to_box.js +6 -6
  25. package/src/core/math/computeWholeDivisorLow.js +33 -33
  26. package/src/core/math/linalg/eigen/eigen_values_find_spectral_gap.d.ts.map +1 -0
  27. package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts +10 -0
  28. package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts.map +1 -0
  29. package/src/core/{graph → math/linalg}/eigen/matrix_eigenvalues_in_place.js +8 -7
  30. package/src/core/math/linalg/eigen/matrix_householder_in_place.d.ts.map +1 -0
  31. package/src/core/{graph → math/linalg}/eigen/matrix_householder_in_place.js +11 -5
  32. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts +15 -0
  33. package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts.map +1 -0
  34. package/src/core/{graph → math/linalg}/eigen/matrix_qr_in_place.js +8 -2
  35. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts +17 -0
  36. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts.map +1 -0
  37. package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.js +107 -0
  38. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts +19 -0
  39. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts.map +1 -0
  40. package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +161 -0
  41. package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts +15 -0
  42. package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts.map +1 -0
  43. package/src/core/math/linalg/polynomial_real_roots_in_interval.js +200 -0
  44. package/src/core/math/solveCubic.d.ts +15 -0
  45. package/src/core/math/solveCubic.d.ts.map +1 -0
  46. package/src/core/math/solveCubic.js +82 -0
  47. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts +23 -0
  48. package/src/core/math/spline/spline3_hermite_bounds_t.d.ts.map +1 -0
  49. package/src/core/math/spline/spline3_hermite_bounds_t.js +109 -0
  50. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +25 -0
  51. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -0
  52. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +44 -0
  53. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts +16 -0
  54. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts.map +1 -0
  55. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +120 -0
  56. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +11 -0
  57. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -0
  58. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +451 -0
  59. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +12 -0
  60. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -0
  61. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +339 -0
  62. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts +15 -0
  63. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts.map +1 -0
  64. package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +21 -0
  65. package/src/core/math/spline/spline3_hermite_to_monomial.d.ts +24 -0
  66. package/src/core/math/spline/spline3_hermite_to_monomial.d.ts.map +1 -0
  67. package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -0
  68. package/src/core/math/spline/v3_computeCatmullRomSplineUniformDistance.js +1 -1
  69. package/src/core/model/node-graph/visual/NodeGraphVisualData.js +1 -1
  70. package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
  71. package/src/core/process/delay.js +16 -16
  72. package/src/engine/animation/async/TimeSeries.js +300 -300
  73. package/src/engine/animation/curve/AnimationCurve.d.ts +3 -2
  74. package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
  75. package/src/engine/animation/curve/AnimationCurve.js +3 -2
  76. package/src/engine/animation/curve/draw/position_canvas_to_curve.js +2 -2
  77. package/src/engine/animation/curve/draw/position_curve_to_canvas.js +2 -2
  78. package/src/engine/ecs/fow/shader/FogOfWarRenderer.js +145 -145
  79. package/src/engine/ecs/gui/position/ViewportPositionSystem.js +2 -2
  80. package/src/engine/ecs/parent/entity_node_compute_bounding_box.js +1 -1
  81. package/src/engine/ecs/transform/Transform.d.ts +0 -10
  82. package/src/engine/ecs/transform/Transform.d.ts.map +1 -1
  83. package/src/engine/ecs/transform/Transform.js +0 -12
  84. package/src/engine/graphics/composit/CompositLayer.js +254 -254
  85. package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +1 -1
  86. package/src/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +2 -2
  87. package/src/engine/graphics/material/optimization/MaterialOptimizationContext.js +3 -3
  88. package/src/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +201 -201
  89. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +1 -1
  90. package/src/engine/graphics/render/buffer/slot/parameter/ProgramValueSlotParameterSet.js +2 -2
  91. package/src/engine/graphics/render/forward_plus/LightManager.js +1226 -1226
  92. package/src/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
  93. package/src/engine/graphics/sh3/lpv/lpv_obtain_storage_cached_volume.js +1 -1
  94. package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +2 -2
  95. package/src/engine/graphics/texture/atlas/TextureAtlasDebugger.js +1 -1
  96. package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
  97. package/src/engine/graphics/texture/sampler/serialization/TextureBinaryBufferSerializer.js +2 -2
  98. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts +2 -6
  99. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts.map +1 -1
  100. package/src/engine/intelligence/behavior/ecs/BehaviorComponent.js +0 -10
  101. package/src/engine/intelligence/mcts/MonteCarlo.js +275 -275
  102. package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
  103. package/src/generation/grid/GridData.js +220 -220
  104. package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +385 -385
  105. package/src/view/elements/image/SvgImageView.js +1 -1
  106. package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
  107. package/src/view/minimap/gl/MinimapFogOfWar.js +3 -3
  108. package/src/view/util/DomSizeObserver.js +1 -1
  109. package/src/core/binary/clz32.d.ts +0 -6
  110. package/src/core/binary/clz32.d.ts.map +0 -1
  111. package/src/core/binary/clz32.js +0 -5
  112. package/src/core/binary/type/dataTypeFromTypedArray.d.ts +0 -8
  113. package/src/core/binary/type/dataTypeFromTypedArray.d.ts.map +0 -1
  114. package/src/core/binary/type/dataTypeFromTypedArray.js +0 -11
  115. package/src/core/collection/array/computeHashIntegerArray.d.ts +0 -1
  116. package/src/core/collection/array/computeHashIntegerArray.d.ts.map +0 -1
  117. package/src/core/collection/array/computeHashIntegerArray.js +0 -7
  118. package/src/core/collection/array/typed/typedArrayToDataType.d.ts +0 -6
  119. package/src/core/collection/array/typed/typedArrayToDataType.d.ts.map +0 -1
  120. package/src/core/collection/array/typed/typedArrayToDataType.js +0 -6
  121. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts +0 -6
  122. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts.map +0 -1
  123. package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.js +0 -7
  124. package/src/core/graph/eigen/eigen_values_find_spectral_gap.d.ts.map +0 -1
  125. package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts +0 -8
  126. package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts.map +0 -1
  127. package/src/core/graph/eigen/matrix_householder_in_place.d.ts.map +0 -1
  128. package/src/core/graph/eigen/matrix_qr_in_place.d.ts +0 -9
  129. package/src/core/graph/eigen/matrix_qr_in_place.d.ts.map +0 -1
  130. package/src/core/math/spline/cubicCurve.d.ts +0 -6
  131. package/src/core/math/spline/cubicCurve.d.ts.map +0 -1
  132. package/src/core/math/spline/cubicCurve.js +0 -6
  133. package/src/core/math/spline/spline_bezier2.d.ts +0 -6
  134. package/src/core/math/spline/spline_bezier2.d.ts.map +0 -1
  135. package/src/core/math/spline/spline_bezier2.js +0 -6
  136. package/src/core/math/spline/spline_bezier3.d.ts +0 -6
  137. package/src/core/math/spline/spline_bezier3.d.ts.map +0 -1
  138. package/src/core/math/spline/spline_bezier3.js +0 -6
  139. package/src/core/math/spline/spline_bezier3_bounds.d.ts +0 -6
  140. package/src/core/math/spline/spline_bezier3_bounds.d.ts.map +0 -1
  141. package/src/core/math/spline/spline_bezier3_bounds.js +0 -6
  142. package/src/core/math/spline/spline_hermite3.d.ts +0 -6
  143. package/src/core/math/spline/spline_hermite3.d.ts.map +0 -1
  144. package/src/core/math/spline/spline_hermite3.js +0 -6
  145. package/src/core/math/spline/spline_hermite3_bounds.d.ts +0 -6
  146. package/src/core/math/spline/spline_hermite3_bounds.d.ts.map +0 -1
  147. package/src/core/math/spline/spline_hermite3_bounds.js +0 -6
  148. package/src/core/math/spline/spline_hermite3_to_bezier.d.ts +0 -2
  149. package/src/core/math/spline/spline_hermite3_to_bezier.d.ts.map +0 -1
  150. package/src/core/math/spline/spline_hermite3_to_bezier.js +0 -6
  151. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts +0 -37
  152. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts.map +0 -1
  153. package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.js +0 -70
  154. /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.d.ts +0 -0
  155. /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.js +0 -0
  156. /package/src/core/{graph → math/linalg}/eigen/matrix_householder_in_place.d.ts +0 -0
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Find all real roots of a univariate polynomial that lie inside the closed
3
+ * interval [lo, hi]. Roots are written to `out[out_offset ..]` in ascending
4
+ * order; the count of roots written is returned.
5
+ *
6
+ * @param {Float64Array|number[]} coeffs ascending power order: coeffs[i] is the coefficient of x^i
7
+ * @param {number} degree polynomial degree (coeffs must have at least `degree + 1` entries)
8
+ * @param {number} lo
9
+ * @param {number} hi
10
+ * @param {Float64Array|number[]} out
11
+ * @param {number} out_offset
12
+ * @returns {number} number of roots written
13
+ */
14
+ export function polynomial_real_roots_in_interval(coeffs: Float64Array | number[], degree: number, lo: number, hi: number, out: Float64Array | number[], out_offset: number): number;
15
+ //# sourceMappingURL=polynomial_real_roots_in_interval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polynomial_real_roots_in_interval.d.ts","sourceRoot":"","sources":["../../../../../src/core/math/linalg/polynomial_real_roots_in_interval.js"],"names":[],"mappings":"AAsGA;;;;;;;;;;;;GAYG;AACH,0DARW,YAAY,GAAC,MAAM,EAAE,UACrB,MAAM,MACN,MAAM,MACN,MAAM,OACN,YAAY,GAAC,MAAM,EAAE,cACrB,MAAM,GACJ,MAAM,CAsFlB"}
@@ -0,0 +1,200 @@
1
+ import { assert } from "../../assert.js";
2
+ import { solveQuadratic } from "../solveQuadratic.js";
3
+ import { polynomial_complex_roots_aberth_ehrlich } from "./polynomial_complex_roots_aberth_ehrlich.js";
4
+
5
+ /*
6
+ Find all real roots of a univariate polynomial that lie inside a closed interval [lo, hi].
7
+ Coefficient layout: coeffs[i] is the coefficient of x^i, so
8
+
9
+ p(x) = coeffs[0] + coeffs[1]*x + coeffs[2]*x^2 + ... + coeffs[degree]*x^degree
10
+
11
+ Approach:
12
+ 1. Trim leading zeros (treat the polynomial at its effective degree).
13
+ 2. Closed-form for degrees 1 and 2.
14
+ 3. For degree ≥ 3, isolate all complex roots via
15
+ polynomial_complex_roots_aberth_ehrlich, then keep the ones whose
16
+ imaginary part is below a scale-relative threshold and whose real part
17
+ lies in [lo, hi]. Each survivor is Newton-polished against the real
18
+ polynomial as a sanity / precision pass.
19
+ */
20
+
21
+ const MAX_SUPPORTED_DEGREE = 64;
22
+
23
+ const SCRATCH_COEFF = new Float64Array(MAX_SUPPORTED_DEGREE + 1);
24
+ const SCRATCH_ROOTS_RE = new Float64Array(MAX_SUPPORTED_DEGREE);
25
+ const SCRATCH_ROOTS_IM = new Float64Array(MAX_SUPPORTED_DEGREE);
26
+ const SCRATCH_OUT_REAL = new Float64Array(MAX_SUPPORTED_DEGREE);
27
+ const SCRATCH_QUADRATIC = new Float64Array(2);
28
+ const SCRATCH_EVAL = new Float64Array(2);
29
+
30
+ const NEWTON_MAX_ITERATIONS = 32;
31
+
32
+ // Imaginary part below this fraction of root scale is treated as real.
33
+ const IMAG_PART_REAL_THRESHOLD = 1e-8;
34
+
35
+ const NEWTON_RELATIVE_RESIDUAL_TOLERANCE = 1e-10;
36
+
37
+ const DEDUPE_RELATIVE_TOLERANCE = 1e-7;
38
+
39
+ function poly_eval_with_derivative(coeffs, degree, x, out) {
40
+ let p = coeffs[degree];
41
+ let dp = 0;
42
+
43
+ for (let i = degree - 1; i >= 0; i--) {
44
+ dp = dp * x + p;
45
+ p = p * x + coeffs[i];
46
+ }
47
+
48
+ out[0] = p;
49
+ out[1] = dp;
50
+ }
51
+
52
+ function max_abs_coefficient(coeffs, degree) {
53
+ let m = 0;
54
+ for (let i = 0; i <= degree; i++) {
55
+ const a = Math.abs(coeffs[i]);
56
+ if (a > m) m = a;
57
+ }
58
+ return m;
59
+ }
60
+
61
+ function root_scale_estimate(coeffs, degree) {
62
+ const lead = Math.abs(coeffs[degree]);
63
+ let m = 0;
64
+ for (let i = 0; i < degree; i++) {
65
+ const a = Math.abs(coeffs[i]);
66
+ if (a > m) m = a;
67
+ }
68
+ return 1 + m / lead;
69
+ }
70
+
71
+ function newton_polish_real(coeffs, degree, x_init) {
72
+ let x = x_init;
73
+
74
+ for (let i = 0; i < NEWTON_MAX_ITERATIONS; i++) {
75
+ poly_eval_with_derivative(coeffs, degree, x, SCRATCH_EVAL);
76
+
77
+ const p = SCRATCH_EVAL[0];
78
+ const dp = SCRATCH_EVAL[1];
79
+
80
+ if (dp === 0) break;
81
+
82
+ const step = p / dp;
83
+ x -= step;
84
+
85
+ if (Math.abs(step) <= Math.abs(x) * 1e-15) break;
86
+ }
87
+
88
+ return x;
89
+ }
90
+
91
+ function insert_sorted_unique(roots, count, value, dedupe_tolerance) {
92
+ let i = 0;
93
+ while (i < count && roots[i] < value) i++;
94
+
95
+ if (i > 0 && Math.abs(roots[i - 1] - value) <= dedupe_tolerance) return count;
96
+ if (i < count && Math.abs(roots[i] - value) <= dedupe_tolerance) return count;
97
+
98
+ for (let j = count; j > i; j--) roots[j] = roots[j - 1];
99
+ roots[i] = value;
100
+ return count + 1;
101
+ }
102
+
103
+ /**
104
+ * Find all real roots of a univariate polynomial that lie inside the closed
105
+ * interval [lo, hi]. Roots are written to `out[out_offset ..]` in ascending
106
+ * order; the count of roots written is returned.
107
+ *
108
+ * @param {Float64Array|number[]} coeffs ascending power order: coeffs[i] is the coefficient of x^i
109
+ * @param {number} degree polynomial degree (coeffs must have at least `degree + 1` entries)
110
+ * @param {number} lo
111
+ * @param {number} hi
112
+ * @param {Float64Array|number[]} out
113
+ * @param {number} out_offset
114
+ * @returns {number} number of roots written
115
+ */
116
+ export function polynomial_real_roots_in_interval(
117
+ coeffs,
118
+ degree,
119
+ lo, hi,
120
+ out, out_offset
121
+ ) {
122
+ assert.isNonNegativeInteger(degree, 'degree');
123
+ assert.isNonNegativeInteger(out_offset, 'out_offset');
124
+ assert.greaterThanOrEqual(MAX_SUPPORTED_DEGREE, degree, 'degree exceeds MAX_SUPPORTED_DEGREE');
125
+
126
+ if (degree === 0) return 0;
127
+
128
+ const coeff_norm = max_abs_coefficient(coeffs, degree);
129
+ if (coeff_norm === 0) return 0;
130
+
131
+ let effective_degree = degree;
132
+ const leading_zero_threshold = coeff_norm * 1e-14;
133
+ while (effective_degree > 0 && Math.abs(coeffs[effective_degree]) <= leading_zero_threshold) {
134
+ effective_degree--;
135
+ }
136
+ if (effective_degree === 0) return 0;
137
+
138
+ if (effective_degree === 1) {
139
+ const root = -coeffs[0] / coeffs[1];
140
+ if (root >= lo && root <= hi) {
141
+ out[out_offset] = root;
142
+ return 1;
143
+ }
144
+ return 0;
145
+ }
146
+
147
+ if (effective_degree === 2) {
148
+ const root_count = solveQuadratic(SCRATCH_QUADRATIC, 0, coeffs[2], coeffs[1], coeffs[0]);
149
+ let written = 0;
150
+ for (let i = 0; i < root_count; i++) {
151
+ const r = SCRATCH_QUADRATIC[i];
152
+ if (r >= lo && r <= hi) {
153
+ if (written === 0 || Math.abs(out[out_offset + written - 1] - r) > 0) {
154
+ out[out_offset + written] = r;
155
+ written++;
156
+ }
157
+ }
158
+ }
159
+ return written;
160
+ }
161
+
162
+ for (let i = 0; i <= effective_degree; i++) SCRATCH_COEFF[i] = coeffs[i];
163
+
164
+ polynomial_complex_roots_aberth_ehrlich(
165
+ SCRATCH_COEFF, effective_degree,
166
+ SCRATCH_ROOTS_RE, SCRATCH_ROOTS_IM, 0
167
+ );
168
+
169
+ const root_scale = root_scale_estimate(SCRATCH_COEFF, effective_degree);
170
+ const imag_threshold = IMAG_PART_REAL_THRESHOLD * Math.max(1, root_scale);
171
+ const residual_tolerance = NEWTON_RELATIVE_RESIDUAL_TOLERANCE * coeff_norm;
172
+ const dedupe_tolerance = DEDUPE_RELATIVE_TOLERANCE * Math.max(1, Math.max(Math.abs(lo), Math.abs(hi)));
173
+
174
+ let unique_count = 0;
175
+
176
+ for (let i = 0; i < effective_degree; i++) {
177
+ const re = SCRATCH_ROOTS_RE[i];
178
+ const im = SCRATCH_ROOTS_IM[i];
179
+
180
+ if (!Number.isFinite(re) || !Number.isFinite(im)) continue;
181
+
182
+ if (Math.abs(im) > imag_threshold) continue;
183
+
184
+ const polished = newton_polish_real(SCRATCH_COEFF, effective_degree, re);
185
+ if (!Number.isFinite(polished)) continue;
186
+
187
+ poly_eval_with_derivative(SCRATCH_COEFF, effective_degree, polished, SCRATCH_EVAL);
188
+ if (Math.abs(SCRATCH_EVAL[0]) > residual_tolerance) continue;
189
+
190
+ if (polished < lo || polished > hi) continue;
191
+
192
+ unique_count = insert_sorted_unique(SCRATCH_OUT_REAL, unique_count, polished, dedupe_tolerance);
193
+ }
194
+
195
+ for (let i = 0; i < unique_count; i++) {
196
+ out[out_offset + i] = SCRATCH_OUT_REAL[i];
197
+ }
198
+
199
+ return unique_count;
200
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Return real solutions for a cubic polynomial: ax³ + bx² + cx + d
3
+ * Repeated roots are written once each — multiplicity is not duplicated in the output.
4
+ * Imaginary roots are not provided.
5
+ *
6
+ * @param {number[]|Float32Array|Float64Array} result solutions are written here
7
+ * @param {number} result_offset offset into result array where solutions are written to
8
+ * @param {number} a
9
+ * @param {number} b
10
+ * @param {number} c
11
+ * @param {number} d
12
+ * @returns {number} number of real roots found (0, 1, 2, or 3)
13
+ */
14
+ export function solveCubic(result: number[] | Float32Array | Float64Array, result_offset: number, a: number, b: number, c: number, d: number): number;
15
+ //# sourceMappingURL=solveCubic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solveCubic.d.ts","sourceRoot":"","sources":["../../../../src/core/math/solveCubic.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;;GAYG;AACH,mCARW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,iBAClC,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,KACN,MAAM,GACJ,MAAM,CA+DlB"}
@@ -0,0 +1,82 @@
1
+ import { assert } from "../assert.js";
2
+ import { EPSILON } from "./EPSILON.js";
3
+ import { solveQuadratic } from "./solveQuadratic.js";
4
+
5
+ const ONE_THIRD = 1.0 / 3.0;
6
+ const TWO_THIRDS_PI = 2.0 * Math.PI / 3.0;
7
+
8
+ /**
9
+ * Return real solutions for a cubic polynomial: ax³ + bx² + cx + d
10
+ * Repeated roots are written once each — multiplicity is not duplicated in the output.
11
+ * Imaginary roots are not provided.
12
+ *
13
+ * @param {number[]|Float32Array|Float64Array} result solutions are written here
14
+ * @param {number} result_offset offset into result array where solutions are written to
15
+ * @param {number} a
16
+ * @param {number} b
17
+ * @param {number} c
18
+ * @param {number} d
19
+ * @returns {number} number of real roots found (0, 1, 2, or 3)
20
+ */
21
+ export function solveCubic(result, result_offset, a, b, c, d) {
22
+ assert.isNumber(a, 'a');
23
+ assert.isNumber(b, 'b');
24
+ assert.isNumber(c, 'c');
25
+ assert.isNumber(d, 'd');
26
+
27
+ if (Math.abs(a) < EPSILON) {
28
+ // degrade to quadratic
29
+ return solveQuadratic(result, result_offset, b, c, d);
30
+ }
31
+
32
+ // normalize: x³ + B·x² + C·x + D = 0
33
+ const inv_a = 1 / a;
34
+ const B = b * inv_a;
35
+ const C = c * inv_a;
36
+ const D = d * inv_a;
37
+
38
+ // depress via x = y - B/3 → y³ + p·y + q = 0
39
+ const B_third = B * ONE_THIRD;
40
+ const p = C - B * B_third;
41
+ const q = 2 * B_third * B_third * B_third - C * B_third + D;
42
+
43
+ if (Math.abs(p) < EPSILON && Math.abs(q) < EPSILON) {
44
+ // triple root at y = 0
45
+ result[result_offset] = -B_third;
46
+ return 1;
47
+ }
48
+
49
+ // Cardano's discriminant criterion. ratio = (q/2)² + (p/3)³.
50
+ // ratio > 0 → one real root, two complex
51
+ // ratio = 0 → repeated real roots (single + double, or triple covered above)
52
+ // ratio < 0 → three distinct real roots
53
+ const half_q = q * 0.5;
54
+ const third_p = p * ONE_THIRD;
55
+ const ratio = half_q * half_q + third_p * third_p * third_p;
56
+
57
+ if (ratio > EPSILON) {
58
+ const sqrt_ratio = Math.sqrt(ratio);
59
+ const u = Math.cbrt(-half_q + sqrt_ratio);
60
+ const v = Math.cbrt(-half_q - sqrt_ratio);
61
+
62
+ result[result_offset] = u + v - B_third;
63
+ return 1;
64
+ }
65
+
66
+ if (ratio < -EPSILON) {
67
+ // p must be negative for ratio < 0; sqrt(-p/3) is real
68
+ const m = 2 * Math.sqrt(-third_p);
69
+ const theta_third = ONE_THIRD * Math.acos(3 * q / (p * m));
70
+
71
+ result[result_offset] = m * Math.cos(theta_third) - B_third;
72
+ result[result_offset + 1] = m * Math.cos(theta_third - TWO_THIRDS_PI) - B_third;
73
+ result[result_offset + 2] = m * Math.cos(theta_third - 2 * TWO_THIRDS_PI) - B_third;
74
+ return 3;
75
+ }
76
+
77
+ // ratio ≈ 0: simple + double root
78
+ const u = Math.cbrt(-half_q);
79
+ result[result_offset] = 2 * u - B_third;
80
+ result[result_offset + 1] = -u - B_third;
81
+ return 2;
82
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Compute the parameter values `t` at which a cubic Hermite spline reaches its
3
+ * minimum and maximum on `[0, 1]`. `t_at_min` is written to
4
+ * `result[result_offset]`, `t_at_max` to `result[result_offset + result_stride]`.
5
+ * Both values lie in `[0, 1]`.
6
+ *
7
+ * Companion to {@link spline3_hermite_bounds}, which writes the values; this
8
+ * one writes the parameters at which those values are reached. Either function
9
+ * does its own critical-point search — call whichever you actually need.
10
+ *
11
+ * @param {number[]|Float32Array|Float64Array} result
12
+ * @param {number} result_offset
13
+ * @param {number} result_stride
14
+ * @param {number} p0
15
+ * @param {number} p1
16
+ * @param {number} m0
17
+ * @param {number} m1
18
+ *
19
+ * @author Alex Goldring
20
+ * @copyright Company Named Limited (c) 2025
21
+ */
22
+ export function spline3_hermite_bounds_t(result: number[] | Float32Array | Float64Array, result_offset: number, result_stride: number, p0: number, p1: number, m0: number, m1: number): void;
23
+ //# sourceMappingURL=spline3_hermite_bounds_t.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spline3_hermite_bounds_t.d.ts","sourceRoot":"","sources":["../../../../../src/core/math/spline/spline3_hermite_bounds_t.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,iDAXW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,iBAClC,MAAM,iBACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,MACN,MAAM,QAyFhB"}
@@ -0,0 +1,109 @@
1
+ import { assert } from "../../assert.js";
2
+ import { spline3_hermite } from "./spline3_hermite.js";
3
+
4
+ /**
5
+ * Compute the parameter values `t` at which a cubic Hermite spline reaches its
6
+ * minimum and maximum on `[0, 1]`. `t_at_min` is written to
7
+ * `result[result_offset]`, `t_at_max` to `result[result_offset + result_stride]`.
8
+ * Both values lie in `[0, 1]`.
9
+ *
10
+ * Companion to {@link spline3_hermite_bounds}, which writes the values; this
11
+ * one writes the parameters at which those values are reached. Either function
12
+ * does its own critical-point search — call whichever you actually need.
13
+ *
14
+ * @param {number[]|Float32Array|Float64Array} result
15
+ * @param {number} result_offset
16
+ * @param {number} result_stride
17
+ * @param {number} p0
18
+ * @param {number} p1
19
+ * @param {number} m0
20
+ * @param {number} m1
21
+ *
22
+ * @author Alex Goldring
23
+ * @copyright Company Named Limited (c) 2025
24
+ */
25
+ export function spline3_hermite_bounds_t(
26
+ result,
27
+ result_offset,
28
+ result_stride,
29
+ p0, p1, m0, m1
30
+ ) {
31
+ assert.greaterThan(result_stride, 0, 'result_stride must be greater than 0');
32
+ assert.isInteger(result_stride, 'result_stride');
33
+
34
+ // Derivative of the Hermite polynomial:
35
+ // 3 t² (m0 + m1 + 2 p0 - 2 p1) - 2 t (2 m0 + m1 + 3 p0 - 3 p1) + m0
36
+ const a = 3 * (m0 + m1 + 2 * p0 - 2 * p1);
37
+ const b = -2 * (2 * m0 + m1 + 3 * p0 - 3 * p1);
38
+ const c = m0;
39
+
40
+ let min, max, t_at_min, t_at_max;
41
+ if (p0 <= p1) {
42
+ min = p0; t_at_min = 0;
43
+ max = p1; t_at_max = 1;
44
+ } else {
45
+ min = p1; t_at_min = 1;
46
+ max = p0; t_at_max = 0;
47
+ }
48
+
49
+ if (Math.abs(a) < 1e-12) {
50
+
51
+ if (Math.abs(b) >= 1e-12) {
52
+ const t = -c / b;
53
+
54
+ if (0 < t && t < 1) {
55
+ const value = spline3_hermite(t, p0, p1, m0, m1);
56
+
57
+ if (value < min) {
58
+ min = value;
59
+ t_at_min = t;
60
+ }
61
+ if (value > max) {
62
+ max = value;
63
+ t_at_max = t;
64
+ }
65
+ }
66
+ }
67
+
68
+ } else {
69
+
70
+ const b2ac = b * b - 4 * c * a;
71
+
72
+ if (b2ac >= 0) {
73
+ const sqrtb2ac = Math.sqrt(b2ac);
74
+
75
+ const t1 = (-b + sqrtb2ac) / (2 * a);
76
+
77
+ if (0 < t1 && t1 < 1) {
78
+ const value = spline3_hermite(t1, p0, p1, m0, m1);
79
+
80
+ if (value < min) {
81
+ min = value;
82
+ t_at_min = t1;
83
+ }
84
+ if (value > max) {
85
+ max = value;
86
+ t_at_max = t1;
87
+ }
88
+ }
89
+
90
+ const t2 = (-b - sqrtb2ac) / (2 * a);
91
+
92
+ if (0 < t2 && t2 < 1) {
93
+ const value = spline3_hermite(t2, p0, p1, m0, m1);
94
+
95
+ if (value < min) {
96
+ min = value;
97
+ t_at_min = t2;
98
+ }
99
+ if (value > max) {
100
+ max = value;
101
+ t_at_max = t2;
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ result[result_offset] = t_at_min;
108
+ result[result_offset + result_stride] = t_at_max;
109
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Closest approach between two cubic Hermite curves over the full [0,1]²
3
+ * parameter square. Writes `[s, t]` of the closest pair to
4
+ * `result[result_offset], result[result_offset + 1]` and returns the squared
5
+ * distance at that pair.
6
+ *
7
+ * Coefficient layout for `a` and `b`: per-dimension grouped quads,
8
+ * `[p0_0, p1_0, m0_0, m1_0, p0_1, p1_1, m0_1, m1_1, ..., m1_{dim-1}]`,
9
+ * length `4 * dim`. dim ≥ 1.
10
+ *
11
+ * Internally dispatched by dimension because the algebra of the closest-
12
+ * approach problem is genuinely different per dim:
13
+ * - 1D: range overlap (a single equation in two unknowns).
14
+ * - 2D: direct (3,3)/(3,3) Bezout resultant of degree ≤ 9.
15
+ * - ND (≥ 3): gradient resultant of distance², degree ≤ 34.
16
+ *
17
+ * @param {Float64Array|number[]} a length 4*dim
18
+ * @param {Float64Array|number[]} b length 4*dim
19
+ * @param {number} dim
20
+ * @param {Float64Array|number[]} result
21
+ * @param {number} result_offset
22
+ * @returns {number} squared distance at closest approach
23
+ */
24
+ export function spline3_hermite_intersection_spline3_hermite(a: Float64Array | number[], b: Float64Array | number[], dim: number, result: Float64Array | number[], result_offset: number): number;
25
+ //# sourceMappingURL=spline3_hermite_intersection_spline3_hermite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spline3_hermite_intersection_spline3_hermite.d.ts","sourceRoot":"","sources":["../../../../../src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,gEAPW,YAAY,GAAC,MAAM,EAAE,KACrB,YAAY,GAAC,MAAM,EAAE,OACrB,MAAM,UACN,YAAY,GAAC,MAAM,EAAE,iBACrB,MAAM,GACJ,MAAM,CAiBlB"}
@@ -0,0 +1,44 @@
1
+ import { assert } from "../../assert.js";
2
+ import { spline3_hermite_intersection_spline3_hermite_1d } from "./spline3_hermite_intersection_spline3_hermite_1d.js";
3
+ import { spline3_hermite_intersection_spline3_hermite_2d } from "./spline3_hermite_intersection_spline3_hermite_2d.js";
4
+ import { spline3_hermite_intersection_spline3_hermite_nd } from "./spline3_hermite_intersection_spline3_hermite_nd.js";
5
+
6
+ /**
7
+ * Closest approach between two cubic Hermite curves over the full [0,1]²
8
+ * parameter square. Writes `[s, t]` of the closest pair to
9
+ * `result[result_offset], result[result_offset + 1]` and returns the squared
10
+ * distance at that pair.
11
+ *
12
+ * Coefficient layout for `a` and `b`: per-dimension grouped quads,
13
+ * `[p0_0, p1_0, m0_0, m1_0, p0_1, p1_1, m0_1, m1_1, ..., m1_{dim-1}]`,
14
+ * length `4 * dim`. dim ≥ 1.
15
+ *
16
+ * Internally dispatched by dimension because the algebra of the closest-
17
+ * approach problem is genuinely different per dim:
18
+ * - 1D: range overlap (a single equation in two unknowns).
19
+ * - 2D: direct (3,3)/(3,3) Bezout resultant of degree ≤ 9.
20
+ * - ND (≥ 3): gradient resultant of distance², degree ≤ 34.
21
+ *
22
+ * @param {Float64Array|number[]} a length 4*dim
23
+ * @param {Float64Array|number[]} b length 4*dim
24
+ * @param {number} dim
25
+ * @param {Float64Array|number[]} result
26
+ * @param {number} result_offset
27
+ * @returns {number} squared distance at closest approach
28
+ */
29
+ export function spline3_hermite_intersection_spline3_hermite(
30
+ a, b, dim,
31
+ result, result_offset
32
+ ) {
33
+ assert.greaterThanOrEqual(dim, 1, 'dim');
34
+
35
+ if (dim === 1) {
36
+ return spline3_hermite_intersection_spline3_hermite_1d(a, b, result, result_offset);
37
+ }
38
+
39
+ if (dim === 2) {
40
+ return spline3_hermite_intersection_spline3_hermite_2d(a, b, result, result_offset);
41
+ }
42
+
43
+ return spline3_hermite_intersection_spline3_hermite_nd(a, b, dim, result, result_offset);
44
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Closest approach of two 1D cubic Hermite curves. Inputs are flat 4-element
3
+ * arrays `[p0, p1, m0, m1]` for each curve (the dim=1 slice of the full
4
+ * intersection API).
5
+ *
6
+ * Writes `[s, t]` to `result[result_offset], result[result_offset + 1]` and
7
+ * returns the squared distance at the closest approach.
8
+ *
9
+ * @param {Float64Array|number[]} a length 4
10
+ * @param {Float64Array|number[]} b length 4
11
+ * @param {Float64Array|number[]} result length >= result_offset + 2
12
+ * @param {number} result_offset
13
+ * @returns {number}
14
+ */
15
+ export function spline3_hermite_intersection_spline3_hermite_1d(a: Float64Array | number[], b: Float64Array | number[], result: Float64Array | number[], result_offset: number): number;
16
+ //# sourceMappingURL=spline3_hermite_intersection_spline3_hermite_1d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spline3_hermite_intersection_spline3_hermite_1d.d.ts","sourceRoot":"","sources":["../../../../../src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js"],"names":[],"mappings":"AA2CA;;;;;;;;;;;;;GAaG;AACH,mEANW,YAAY,GAAC,MAAM,EAAE,KACrB,YAAY,GAAC,MAAM,EAAE,UACrB,YAAY,GAAC,MAAM,EAAE,iBACrB,MAAM,GACJ,MAAM,CAgElB"}
@@ -0,0 +1,120 @@
1
+ import { solveCubic } from "../solveCubic.js";
2
+ import { spline3_hermite_bounds } from "./spline3_hermite_bounds.js";
3
+ import { spline3_hermite_bounds_t } from "./spline3_hermite_bounds_t.js";
4
+
5
+ /*
6
+ 1D specialization: A and B are scalar functions on [0, 1]. The "intersection"
7
+ is anywhere their value ranges coincide.
8
+
9
+ Two regimes:
10
+ - Ranges overlap: any (s, t) with A(s) = v = B(t) for some v in the overlap
11
+ is a true intersection. We pick v = midpoint of the overlap, then solve
12
+ each cubic A(s) = v and B(t) = v for the first root in [0, 1].
13
+ - Ranges disjoint: the closest pair sits at the inner extrema — A's max if A
14
+ is below B (or A's min if A is above B), paired with the corresponding
15
+ extremum of B. The parameter at each extremum comes from
16
+ `spline3_hermite_bounds_t`.
17
+ */
18
+
19
+ const _bounds_value = new Float64Array(4); // [a_lo, a_hi, b_lo, b_hi]
20
+ const _bounds_t = new Float64Array(4); // [a_t_min, a_t_max, b_t_min, b_t_max]
21
+ const _cubic_roots = new Float64Array(3);
22
+
23
+ function first_cubic_root_in_unit(p0, p1, m0, m1, target) {
24
+ // A(s) - target = (2 p0 - 2 p1 + m0 + m1) s³
25
+ // + (-3 p0 + 3 p1 - 2 m0 - m1) s²
26
+ // + m0 s
27
+ // + (p0 - target)
28
+ const a3 = 2 * p0 - 2 * p1 + m0 + m1;
29
+ const a2 = -3 * p0 + 3 * p1 - 2 * m0 - m1;
30
+ const a1 = m0;
31
+ const a0 = p0 - target;
32
+
33
+ const count = solveCubic(_cubic_roots, 0, a3, a2, a1, a0);
34
+ let best_s = -1;
35
+ for (let i = 0; i < count; i++) {
36
+ const r = _cubic_roots[i];
37
+ if (r >= 0 && r <= 1) {
38
+ if (best_s < 0 || r < best_s) best_s = r;
39
+ }
40
+ }
41
+ return best_s;
42
+ }
43
+
44
+ /**
45
+ * Closest approach of two 1D cubic Hermite curves. Inputs are flat 4-element
46
+ * arrays `[p0, p1, m0, m1]` for each curve (the dim=1 slice of the full
47
+ * intersection API).
48
+ *
49
+ * Writes `[s, t]` to `result[result_offset], result[result_offset + 1]` and
50
+ * returns the squared distance at the closest approach.
51
+ *
52
+ * @param {Float64Array|number[]} a length 4
53
+ * @param {Float64Array|number[]} b length 4
54
+ * @param {Float64Array|number[]} result length >= result_offset + 2
55
+ * @param {number} result_offset
56
+ * @returns {number}
57
+ */
58
+ export function spline3_hermite_intersection_spline3_hermite_1d(
59
+ a, b,
60
+ result, result_offset
61
+ ) {
62
+ const a_p0 = a[0], a_p1 = a[1], a_m0 = a[2], a_m1 = a[3];
63
+ const b_p0 = b[0], b_p1 = b[1], b_m0 = b[2], b_m1 = b[3];
64
+
65
+ spline3_hermite_bounds(_bounds_value, 0, 1, a_p0, a_p1, a_m0, a_m1);
66
+ const a_lo = _bounds_value[0];
67
+ const a_hi = _bounds_value[1];
68
+
69
+ spline3_hermite_bounds(_bounds_value, 2, 1, b_p0, b_p1, b_m0, b_m1);
70
+ const b_lo = _bounds_value[2];
71
+ const b_hi = _bounds_value[3];
72
+
73
+ const overlap_lo = a_lo > b_lo ? a_lo : b_lo;
74
+ const overlap_hi = a_hi < b_hi ? a_hi : b_hi;
75
+
76
+ if (overlap_lo <= overlap_hi) {
77
+ // Ranges overlap → exact intersection at distance 0.
78
+ const target = 0.5 * (overlap_lo + overlap_hi);
79
+
80
+ let s = first_cubic_root_in_unit(a_p0, a_p1, a_m0, a_m1, target);
81
+ let t = first_cubic_root_in_unit(b_p0, b_p1, b_m0, b_m1, target);
82
+
83
+ // The cubic root finder shouldn't miss given that target lies inside
84
+ // both ranges, but guard against pathological numerics by falling back
85
+ // to the relevant extremum parameter.
86
+ if (s < 0 || t < 0) {
87
+ spline3_hermite_bounds_t(_bounds_t, 0, 1, a_p0, a_p1, a_m0, a_m1);
88
+ spline3_hermite_bounds_t(_bounds_t, 2, 1, b_p0, b_p1, b_m0, b_m1);
89
+ if (s < 0) s = (target === a_lo) ? _bounds_t[0] : _bounds_t[1];
90
+ if (t < 0) t = (target === b_lo) ? _bounds_t[2] : _bounds_t[3];
91
+ }
92
+
93
+ result[result_offset] = s;
94
+ result[result_offset + 1] = t;
95
+ return 0;
96
+ }
97
+
98
+ // Ranges disjoint. Recover the t parameters at the inner extrema.
99
+ spline3_hermite_bounds_t(_bounds_t, 0, 1, a_p0, a_p1, a_m0, a_m1);
100
+ spline3_hermite_bounds_t(_bounds_t, 2, 1, b_p0, b_p1, b_m0, b_m1);
101
+ const a_t_min = _bounds_t[0];
102
+ const a_t_max = _bounds_t[1];
103
+ const b_t_min = _bounds_t[2];
104
+ const b_t_max = _bounds_t[3];
105
+
106
+ let s, t, gap;
107
+ if (a_hi < b_lo) {
108
+ s = a_t_max;
109
+ t = b_t_min;
110
+ gap = b_lo - a_hi;
111
+ } else {
112
+ s = a_t_min;
113
+ t = b_t_max;
114
+ gap = a_lo - b_hi;
115
+ }
116
+
117
+ result[result_offset] = s;
118
+ result[result_offset + 1] = t;
119
+ return gap * gap;
120
+ }