@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.
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/editor/tools/v2/TransformControlsGizmo.js +1 -1
- package/editor/view/node-graph/NodeGraphEditorView.js +2 -2
- package/package.json +1 -1
- package/src/core/assert.d.ts +0 -2
- package/src/core/assert.d.ts.map +1 -1
- package/src/core/assert.js +0 -6
- package/src/core/color/Color.d.ts +0 -5
- package/src/core/color/Color.d.ts.map +1 -1
- package/src/core/color/Color.js +1 -7
- package/src/core/geom/2d/hash-grid/SpatialHashGrid.js +386 -386
- package/src/core/geom/2d/line/line_segment_compute_line_segment_intersection_2d.js +1 -1
- package/src/core/geom/2d/quad-tree-binary/QuadTree.js +714 -714
- package/src/core/geom/3d/triangle/computeTriangleRayIntersection.js +160 -160
- package/src/core/geom/3d/triangle/computeTriangleRayIntersectionBarycentric.js +96 -96
- package/src/core/geom/packing/max-rect/MaxRectanglesPacker.js +1 -1
- package/src/core/geom/packing/max-rect/findBestContainer.js +4 -4
- package/src/core/geom/packing/max-rect/packOneBox.js +2 -2
- package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts +23 -0
- package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.d.ts.map +1 -0
- package/src/core/geom/vec3/v3_rigid_align_paired_unit_vectors.js +96 -0
- package/src/core/graph/layout/box/BoxLayouter.js +7 -7
- package/src/core/graph/layout/box/position_box_next_to_box.js +6 -6
- package/src/core/math/computeWholeDivisorLow.js +33 -33
- package/src/core/math/linalg/eigen/eigen_values_find_spectral_gap.d.ts.map +1 -0
- package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts +10 -0
- package/src/core/math/linalg/eigen/matrix_eigenvalues_in_place.d.ts.map +1 -0
- package/src/core/{graph → math/linalg}/eigen/matrix_eigenvalues_in_place.js +8 -7
- package/src/core/math/linalg/eigen/matrix_householder_in_place.d.ts.map +1 -0
- package/src/core/{graph → math/linalg}/eigen/matrix_householder_in_place.js +11 -5
- package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts +15 -0
- package/src/core/math/linalg/eigen/matrix_qr_in_place.d.ts.map +1 -0
- package/src/core/{graph → math/linalg}/eigen/matrix_qr_in_place.js +8 -2
- package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts +17 -0
- package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.d.ts.map +1 -0
- package/src/core/math/linalg/eigen/matrix_top_eigenvector_power_iteration.js +107 -0
- package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts +19 -0
- package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.d.ts.map +1 -0
- package/src/core/math/linalg/polynomial_complex_roots_aberth_ehrlich.js +161 -0
- package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts +15 -0
- package/src/core/math/linalg/polynomial_real_roots_in_interval.d.ts.map +1 -0
- package/src/core/math/linalg/polynomial_real_roots_in_interval.js +200 -0
- package/src/core/math/solveCubic.d.ts +15 -0
- package/src/core/math/solveCubic.d.ts.map +1 -0
- package/src/core/math/solveCubic.js +82 -0
- package/src/core/math/spline/spline3_hermite_bounds_t.d.ts +23 -0
- package/src/core/math/spline/spline3_hermite_bounds_t.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_bounds_t.js +109 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +25 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +44 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts +16 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_1d.js +120 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +11 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +451 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +12 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +339 -0
- package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts +15 -0
- package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_intersects_spline3_hermite.js +21 -0
- package/src/core/math/spline/spline3_hermite_to_monomial.d.ts +24 -0
- package/src/core/math/spline/spline3_hermite_to_monomial.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_to_monomial.js +37 -0
- package/src/core/math/spline/v3_computeCatmullRomSplineUniformDistance.js +1 -1
- package/src/core/model/node-graph/visual/NodeGraphVisualData.js +1 -1
- package/src/core/model/reactive/model/util/createRandomReactiveExpression.js +185 -185
- package/src/core/process/delay.js +16 -16
- package/src/engine/animation/async/TimeSeries.js +300 -300
- package/src/engine/animation/curve/AnimationCurve.d.ts +3 -2
- package/src/engine/animation/curve/AnimationCurve.d.ts.map +1 -1
- package/src/engine/animation/curve/AnimationCurve.js +3 -2
- package/src/engine/animation/curve/draw/position_canvas_to_curve.js +2 -2
- package/src/engine/animation/curve/draw/position_curve_to_canvas.js +2 -2
- package/src/engine/ecs/fow/shader/FogOfWarRenderer.js +145 -145
- package/src/engine/ecs/gui/position/ViewportPositionSystem.js +2 -2
- package/src/engine/ecs/parent/entity_node_compute_bounding_box.js +1 -1
- package/src/engine/ecs/transform/Transform.d.ts +0 -10
- package/src/engine/ecs/transform/Transform.d.ts.map +1 -1
- package/src/engine/ecs/transform/Transform.js +0 -12
- package/src/engine/graphics/composit/CompositLayer.js +254 -254
- package/src/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +1 -1
- package/src/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +2 -2
- package/src/engine/graphics/material/optimization/MaterialOptimizationContext.js +3 -3
- package/src/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +201 -201
- package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +1 -1
- package/src/engine/graphics/render/buffer/slot/parameter/ProgramValueSlotParameterSet.js +2 -2
- package/src/engine/graphics/render/forward_plus/LightManager.js +1226 -1226
- package/src/engine/graphics/render/forward_plus/model/PointLight.js +1 -1
- package/src/engine/graphics/sh3/lpv/lpv_obtain_storage_cached_volume.js +1 -1
- package/src/engine/graphics/sh3/path_tracer/texture/sample_material.js +2 -2
- package/src/engine/graphics/texture/atlas/TextureAtlasDebugger.js +1 -1
- package/src/engine/graphics/texture/sampler/HarmonicDiffusionGrid.js +145 -145
- package/src/engine/graphics/texture/sampler/serialization/TextureBinaryBufferSerializer.js +2 -2
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts +2 -6
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.d.ts.map +1 -1
- package/src/engine/intelligence/behavior/ecs/BehaviorComponent.js +0 -10
- package/src/engine/intelligence/mcts/MonteCarlo.js +275 -275
- package/src/engine/navigation/ecs/path_following/PathFollower.js +222 -222
- package/src/generation/grid/GridData.js +220 -220
- package/src/generation/grid/generation/GridTaskDensityMarkerDistribution.js +385 -385
- package/src/view/elements/image/SvgImageView.js +1 -1
- package/src/view/elements/windrose/WindRoseDiagram.js +369 -369
- package/src/view/minimap/gl/MinimapFogOfWar.js +3 -3
- package/src/view/util/DomSizeObserver.js +1 -1
- package/src/core/binary/clz32.d.ts +0 -6
- package/src/core/binary/clz32.d.ts.map +0 -1
- package/src/core/binary/clz32.js +0 -5
- package/src/core/binary/type/dataTypeFromTypedArray.d.ts +0 -8
- package/src/core/binary/type/dataTypeFromTypedArray.d.ts.map +0 -1
- package/src/core/binary/type/dataTypeFromTypedArray.js +0 -11
- package/src/core/collection/array/computeHashIntegerArray.d.ts +0 -1
- package/src/core/collection/array/computeHashIntegerArray.d.ts.map +0 -1
- package/src/core/collection/array/computeHashIntegerArray.js +0 -7
- package/src/core/collection/array/typed/typedArrayToDataType.d.ts +0 -6
- package/src/core/collection/array/typed/typedArrayToDataType.d.ts.map +0 -1
- package/src/core/collection/array/typed/typedArrayToDataType.js +0 -6
- package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts +0 -6
- package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.d.ts.map +0 -1
- package/src/core/geom/3d/mat4/MATRIX_4_IDENTITY.js +0 -7
- package/src/core/graph/eigen/eigen_values_find_spectral_gap.d.ts.map +0 -1
- package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts +0 -8
- package/src/core/graph/eigen/matrix_eigenvalues_in_place.d.ts.map +0 -1
- package/src/core/graph/eigen/matrix_householder_in_place.d.ts.map +0 -1
- package/src/core/graph/eigen/matrix_qr_in_place.d.ts +0 -9
- package/src/core/graph/eigen/matrix_qr_in_place.d.ts.map +0 -1
- package/src/core/math/spline/cubicCurve.d.ts +0 -6
- package/src/core/math/spline/cubicCurve.d.ts.map +0 -1
- package/src/core/math/spline/cubicCurve.js +0 -6
- package/src/core/math/spline/spline_bezier2.d.ts +0 -6
- package/src/core/math/spline/spline_bezier2.d.ts.map +0 -1
- package/src/core/math/spline/spline_bezier2.js +0 -6
- package/src/core/math/spline/spline_bezier3.d.ts +0 -6
- package/src/core/math/spline/spline_bezier3.d.ts.map +0 -1
- package/src/core/math/spline/spline_bezier3.js +0 -6
- package/src/core/math/spline/spline_bezier3_bounds.d.ts +0 -6
- package/src/core/math/spline/spline_bezier3_bounds.d.ts.map +0 -1
- package/src/core/math/spline/spline_bezier3_bounds.js +0 -6
- package/src/core/math/spline/spline_hermite3.d.ts +0 -6
- package/src/core/math/spline/spline_hermite3.d.ts.map +0 -1
- package/src/core/math/spline/spline_hermite3.js +0 -6
- package/src/core/math/spline/spline_hermite3_bounds.d.ts +0 -6
- package/src/core/math/spline/spline_hermite3_bounds.d.ts.map +0 -1
- package/src/core/math/spline/spline_hermite3_bounds.js +0 -6
- package/src/core/math/spline/spline_hermite3_to_bezier.d.ts +0 -2
- package/src/core/math/spline/spline_hermite3_to_bezier.d.ts.map +0 -1
- package/src/core/math/spline/spline_hermite3_to_bezier.js +0 -6
- package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts +0 -37
- package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.d.ts.map +0 -1
- package/src/engine/intelligence/behavior/decorator/RepeatUntilFailureBehavior.js +0 -70
- /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.d.ts +0 -0
- /package/src/core/{graph → math/linalg}/eigen/eigen_values_find_spectral_gap.js +0 -0
- /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
|
+
}
|