Skip to main content

brep_kernel/geometry/
pcurve.rs

1use crate::{
2    interpolate_curve, project_point_to_surface, project_point_to_surface_seeded, AnalyticSurface,
3    DiagnosticSeverity, KernelDiagnostics, KernelStage, KnotVector, NurbsCurve, NurbsSurface, Vec3,
4    Vec4, KNOT_IDENTITY_TOL,
5};
6
7const EPSILON: f64 = 1e-12;
8const LINEAR_TOLERANCE: f64 = 1e-7;
9
10fn surface_domains(surface: &NurbsSurface) -> Result<([f64; 2], [f64; 2]), String> {
11    Ok((
12        KnotVector::new(surface.knots_u.clone(), surface.degree_u)?.domain(),
13        KnotVector::new(surface.knots_v.clone(), surface.degree_v)?.domain(),
14    ))
15}
16
17/// The carrier's closed directions, as [`NurbsSurface::closed_directions`]
18/// reads them (cached on the surface, seam tolerance 1e-6, and a sliver patch
19/// whose rows coincide without the surface travelling between them is OPEN).
20/// Until 2026-09-26 this was a second reading with its own tolerance; the
21/// unwrap in [`build_interpolant`] and the wrap in `evaluate_extended` must
22/// agree on what a seam is, or a trim across a sliver reads a period wide.
23fn surface_closedness(surface: &NurbsSurface) -> Result<(bool, bool), String> {
24    surface.closed_directions()
25}
26
27fn invert_checked(surface: &NurbsSurface, point: Vec3) -> Result<([f64; 2], f64), String> {
28    if surface.is_affine()? {
29        let ([u0, _], [v0, _]) = surface_domains(surface)?;
30        let (origin, du, dv) = surface.deriv1(u0, v0)?;
31        let delta = point.sub(origin);
32        let uu = du.dot(du);
33        let uv = du.dot(dv);
34        let vv = dv.dot(dv);
35        let along_u = delta.dot(du);
36        let along_v = delta.dot(dv);
37        let determinant = uu * vv - uv * uv;
38        if determinant.abs() > EPSILON {
39            let parameter = [
40                u0 + (along_u * vv - along_v * uv) / determinant,
41                v0 + (along_v * uu - along_u * uv) / determinant,
42            ];
43            // The standoff is a MEASUREMENT here as on every other carrier:
44            // until 2026-09-26 this returned 0.0 for "affine, not measured",
45            // and a curve 1e-2 off its plane read as lying on it.
46            let foot = origin.add(du.scale(parameter[0] - u0)).add(dv.scale(parameter[1] - v0));
47            return Ok((parameter, point.sub(foot).length()));
48        }
49    }
50    let projection = project_point_to_surface(surface, point)?;
51    Ok(([projection.u, projection.v], projection.distance))
52}
53
54/// Whether an end station at a cone's apex takes its nearest regular
55/// station's u (`true`), as in `build_interpolant`, or the projector's
56/// arbitrary u there as before 2026-09-27. `BREP_PCURVE_CONE_APEX_U=0`
57/// restores the projector's u, for a same-binary A/B.
58fn cone_apex_u() -> bool {
59    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
60    *ON.get_or_init(|| std::env::var_os("BREP_PCURVE_CONE_APEX_U").is_none_or(|value| value != "0"))
61}
62
63/// Whether a round with more candidates than the cap has room for keeps its
64/// WORST probes (`true`), or the first in span order as before 2026-09-27.
65/// `BREP_PCURVE_WORST_FIRST=0` restores span order, for a same-binary A/B.
66fn worst_first() -> bool {
67    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
68    *ON.get_or_init(|| std::env::var_os("BREP_PCURVE_WORST_FIRST").is_none_or(|value| value != "0"))
69}
70
71/// `BREP_PCURVE_C0_JOIN=0` builds one C2 interpolant through the stations as
72/// before 2026-10-03 (the C0 joins at the curve's corners off), for a
73/// same-binary A/B. Read once per process.
74fn c0_joins() -> bool {
75    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
76    *ON.get_or_init(|| std::env::var_os("BREP_PCURVE_C0_JOIN").is_none_or(|value| value != "0"))
77}
78
79fn seeded_inserts() -> bool {
80    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
81    *ON.get_or_init(|| std::env::var_os("BREP_PCURVE_SEEDED_INSERTS").is_none_or(|value| value != "0"))
82}
83
84/// [`invert_checked`] for a station inserted between two whose feet are known:
85/// Newton from the pcurve's own `(u, v)` at that fraction, which already lies
86/// between the neighbours' feet on their branch, and the global search only
87/// when that local foot stands off further than the neighbours do (plus the
88/// refinement floor) — a local minimum the curve does not sit at. The global
89/// search was 30 s of a 54 s `abc_00000011` import (732k calls, 41 µs each,
90/// measured 2026-09-26). Planes and analytic carriers keep their closed forms.
91/// `BREP_PCURVE_SEEDED_INSERTS=0` restores the global search at every insert,
92/// for a same-binary A/B.
93fn invert_near(
94    surface: &NurbsSurface,
95    point: Vec3,
96    seed: [f64; 2],
97    neighbour_gap: f64,
98) -> Result<([f64; 2], f64), String> {
99    if !seeded_inserts() || surface.is_affine()? || surface.analytic().is_some() {
100        return invert_checked(surface, point);
101    }
102    let seeded = project_point_to_surface_seeded(surface, point, seed[0], seed[1])?;
103    if seeded.distance <= neighbour_gap + PCURVE_REFINEMENT_TOLERANCE {
104        return Ok(([seeded.u, seeded.v], seeded.distance));
105    }
106    invert_checked(surface, point)
107}
108
109fn unwrap_periodic(values: &mut [f64], minimum: f64, maximum: f64) {
110    let period = maximum - minimum;
111    for index in 1..values.len() {
112        while values[index] - values[index - 1] > period / 2.0 {
113            values[index] -= period;
114        }
115        while values[index] - values[index - 1] < -period / 2.0 {
116            values[index] += period;
117        }
118    }
119    if values.len() > 1 {
120        while values[0] - values[1] > period / 2.0 {
121            values[0] -= period;
122        }
123        while values[0] - values[1] < -period / 2.0 {
124            values[0] += period;
125        }
126    }
127    // Recenter the whole (now continuous) sequence into the domain by the
128    // whole-period shift that leaves the LEAST parameter outside [minimum,
129    // maximum]. A single middle/mean sample is NOT representative of a curve
130    // that bulges out to touch — or straddle — the periodic seam: when that
131    // sample is a boundary-kissing point (the at-seam projection unwraps a hair
132    // past the boundary), a single-sample test misfires and shoves the entire
133    // curve a full period out of range (ABC helmet 00000011/12 side channels:
134    // a touching edge's parameter run snapped a whole period off its true
135    // interior, tearing the loop open in parameter space).
136    if values.len() > 1 {
137        let excursion = |shift: f64| -> f64 {
138            values
139                .iter()
140                .map(|value| {
141                    let v = value + shift;
142                    (minimum - v).max(0.0) + (v - maximum).max(0.0)
143                })
144                .sum::<f64>()
145        };
146        let mean = values.iter().copied().sum::<f64>() / values.len() as f64;
147        let center = 0.5 * (minimum + maximum);
148        let base_k = ((center - mean) / period).round() as i64;
149        let mut best_shift = 0.0;
150        let mut best_excursion = f64::INFINITY;
151        for k in (base_k - 1)..=(base_k + 1) {
152            let shift = k as f64 * period;
153            let value = excursion(shift);
154            if value < best_excursion {
155                best_excursion = value;
156                best_shift = shift;
157            }
158        }
159        if best_shift != 0.0 {
160            for value in values.iter_mut() {
161                *value += best_shift;
162            }
163        }
164    }
165}
166
167/// The interpolant through `raw_parameters`, and what building it CLAMPED:
168/// the largest parameter distance a sample was pulled back into an open
169/// direction's domain, and the 3D distance between the extended surface at
170/// the raw parameter and the clamped foot. Both zero when nothing was pulled.
171/// A closed direction wraps and is never clamped.
172fn build_interpolant(
173    surface: &NurbsSurface,
174    raw_parameters: &[[f64; 2]],
175    curve_parameters: &[f64],
176) -> Result<(NurbsCurve, f64, f64), String> {
177    build_interpolant_joined(surface, raw_parameters, curve_parameters, &[])
178}
179
180/// [`build_interpolant`] with C0 JOINS: `joins` are values of
181/// `curve_parameters` at which the edge curve itself has a corner (a knot of
182/// multiplicity ≥ its degree, `range_breaks`). One cubic through all the
183/// stations is C2 everywhere, so across a corner it overshoots on both sides
184/// by a first-order amount however many stations are inserted: `abc_00000011`
185/// face #585 edge 179 (79 such knots on a 241-point curve) read 2.055e-4 off
186/// at 153 stations with the file's curve 2.1e-7 off the carrier (measured
187/// 2026-10-03). The stations have been planted at those knots since
188/// 2026-09-26; what was missing was the interpolant honouring them. Each
189/// piece between joins is interpolated on its own and the pieces are
190/// concatenated at knots of multiplicity 3 — the join `blend/track_fit.rs`
191/// makes for a blend track. A piece shorter than four stations cannot carry
192/// a cubic, so its join is dropped and it merges with its neighbour.
193fn build_interpolant_joined(
194    surface: &NurbsSurface,
195    raw_parameters: &[[f64; 2]],
196    curve_parameters: &[f64],
197    joins: &[f64],
198) -> Result<(NurbsCurve, f64, f64), String> {
199    let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
200    let (closed_u, closed_v) = surface_closedness(surface)?;
201    let mut parameters = raw_parameters.to_vec();
202    // A cone's apex has no longitude either: every u there is the same point,
203    // and the projector answers one arbitrarily (0, which unwraps to 1). A trim
204    // that ENDS at the apex then turns sideways in its last span —
205    // `abc 00008080` face #163 edge 476, a generatrix at u = 0.80255 whose
206    // apex station read u = 1.0 (2026-09-27). An apex station at either end of
207    // the run takes the u of its nearest regular station, so a generatrix's
208    // pcurve is the straight u = const it is. Interior apex crossings are left
209    // to the branch logic below. `BREP_PCURVE_CONE_APEX_U=0` switches this off,
210    // for a same-binary A/B.
211    if cone_apex_u() && matches!(surface.analytic(), Some(AnalyticSurface::RuledRevolution { .. })) && parameters.len() >= 2 {
212        let speeds = parameters
213            .iter()
214            .map(|parameter| surface.deriv1(parameter[0], parameter[1]).map(|(_, su, _)| su.length()))
215            .collect::<Result<Vec<_>, _>>()?;
216        let reference = speeds.iter().copied().fold(0.0_f64, f64::max);
217        let singular = |index: usize| speeds[index] <= reference * 1e-2;
218        if let (Some(first), Some(last)) = (
219            (0..parameters.len()).find(|&index| !singular(index)),
220            (0..parameters.len()).rfind(|&index| !singular(index)),
221        ) {
222            for index in 0..first {
223                parameters[index][0] = parameters[first][0];
224            }
225            for index in last + 1..parameters.len() {
226                parameters[index][0] = parameters[last][0];
227            }
228        }
229    }
230    // Longitude is undefined at a sphere pole: the surface's u tangent
231    // vanishes there, and closest-point inversion is free to return any u.
232    // Letting that arbitrary value participate in periodic unwrapping can move
233    // the first real meridian by a whole period (e.g. 0.75 -> -0.25). Analytic
234    // carriers are subsequently clamped, collapsing that meridian onto u=0.
235    //
236    // Keep this deliberately sphere- and singularity-gated. Split the u values
237    // into maximal non-pole runs, unwrap each run independently, and leave the
238    // pole samples untouched. The existing interpolant degree stays unchanged;
239    // lowering it here would also change endpoint-tangent decisions made later
240    // while stitching the face loop.
241    let sphere_u_singular = if matches!(surface.analytic(), Some(AnalyticSurface::Sphere { .. })) {
242        let speeds = parameters
243            .iter()
244            .map(|parameter| {
245                surface
246                    .deriv1(parameter[0], parameter[1])
247                    .map(|(_, su, _)| su.length())
248            })
249            .collect::<Result<Vec<_>, _>>()?;
250        let reference = speeds.iter().copied().fold(0.0_f64, f64::max);
251        speeds
252            .into_iter()
253            // Uniform edge stations need not hit the pole exactly (ABC 5360
254            // bottoms out at v=5.5e-4). In this small conditioning band,
255            // longitude is already numerically arbitrary and must not connect
256            // the two meridian runs across the pole.
257            .map(|speed| speed <= reference * 1e-2)
258            .collect::<Vec<_>>()
259    } else {
260        vec![false; parameters.len()]
261    };
262    // Do not perturb ordinary pole-touching trims. The special path is needed
263    // only when a pole band is bracketed by nonsingular samples and its raw
264    // longitude changes by at least half a period: a true through-pole branch
265    // reset whose two meridian runs must be unwrapped independently.
266    // Prefix/suffix pole samples on normal sphere caps retain the legacy
267    // interpolation byte-for-byte. Include the exactly-half-period case so
268    // floating-point noise cannot decide which entire run is lifted (ABC 5603).
269    let has_interior_pole_crossing = sphere_u_singular
270        .iter()
271        .position(|singular| !singular)
272        .zip(sphere_u_singular.iter().rposition(|singular| !singular))
273        .is_some_and(|(first, last)| sphere_u_singular[first..=last].iter().any(|value| *value));
274    let period = (u1 - u0).abs();
275    let interior_pole_branch_reset = closed_u
276        && has_interior_pole_crossing
277        && parameters.windows(2).zip(sphere_u_singular.windows(2)).any(
278            |(parameter_pair, singular_pair)| {
279                (singular_pair[0] || singular_pair[1])
280                    && (parameter_pair[1][0] - parameter_pair[0][0]).abs()
281                        >= 0.5 * period - 1e-12 * period.max(1.0)
282            },
283        );
284    if closed_u {
285        let mut values = parameters.iter().map(|value| value[0]).collect::<Vec<_>>();
286        if interior_pole_branch_reset {
287            let mut start = 0;
288            while start < values.len() {
289                while start < values.len() && sphere_u_singular[start] {
290                    start += 1;
291                }
292                let mut end = start;
293                while end < values.len() && !sphere_u_singular[end] {
294                    end += 1;
295                }
296                unwrap_periodic(&mut values[start..end], u0, u1);
297                start = end;
298            }
299        } else {
300            unwrap_periodic(&mut values, u0, u1);
301        }
302        for (parameter, value) in parameters.iter_mut().zip(values) {
303            parameter[0] = value;
304        }
305    }
306    if closed_v {
307        let mut values = parameters.iter().map(|value| value[1]).collect::<Vec<_>>();
308        unwrap_periodic(&mut values, v0, v1);
309        for (parameter, value) in parameters.iter_mut().zip(values) {
310            parameter[1] = value;
311        }
312    }
313    // A closed direction WRAPS: an edge that straddles the seam has no single
314    // in-domain parameter run, so keep the unwrapped (possibly slightly
315    // out-of-domain) values and let the periodic evaluator wrap them — clamping
316    // them onto the seam boundary would collapse the straddling geometry (and,
317    // via the interpolant, spike neighbouring stations). This applies to
318    // analytic carriers too: a refitted cylinder can have stepped rims that
319    // cross its seam. evaluate_extended wraps every closed direction.
320    // Open directions must stay in-domain; their extension is a ruling.
321    //
322    // The clamp is MEASURED, not silent: a sample pulled back into the domain
323    // is reported as the parameter distance it moved and the 3D distance
324    // between the extended surface at its raw parameter and the clamped foot
325    // ([`PcurveFitReport::clamped_excursion`]). Until 2026-09-30 a trim that
326    // ran past its chart came back folded onto the chart's boundary with
327    // nothing in the report to say so; on the rib-base document that was
328    // 3.03e-2 of volume on a body `validate()` passes.
329    let mut excursion = 0.0_f64;
330    let mut distance = 0.0_f64;
331    let mut points = Vec::with_capacity(parameters.len());
332    for parameter in parameters {
333        let u = if closed_u {
334            parameter[0]
335        } else {
336            parameter[0].clamp(u0, u1)
337        };
338        let v = if closed_v {
339            parameter[1]
340        } else {
341            parameter[1].clamp(v0, v1)
342        };
343        let pulled = ((parameter[0] - u).powi(2) + (parameter[1] - v).powi(2)).sqrt();
344        if pulled > 0.0 {
345            excursion = excursion.max(pulled);
346            let extended = surface.evaluate_extended(parameter[0], parameter[1])?;
347            distance = distance.max(extended.sub(surface.evaluate(u, v)?).length());
348        }
349        points.push(Vec3::new(u, v, 0.0));
350    }
351    let degree = 3usize.min(points.len() - 1);
352    let mut bounds: Vec<usize> = joins
353        .iter()
354        .filter_map(|join| curve_parameters.iter().position(|parameter| parameter == join))
355        .filter(|&index| index > 0 && index + 1 < curve_parameters.len())
356        .collect();
357    bounds.sort_unstable();
358    bounds.dedup();
359    if degree < 3 || bounds.is_empty() || !c0_joins() {
360        let curve = interpolate_curve(&points, degree, curve_parameters)?;
361        return Ok((curve, excursion, distance));
362    }
363    // Every piece needs four stations for a cubic: drop the join that
364    // closes a shorter piece, from the left, until all pieces carry one.
365    let mut pieces = vec![0usize];
366    for bound in bounds {
367        if bound + 1 - pieces[pieces.len() - 1] >= 4 {
368            pieces.push(bound);
369        }
370    }
371    if curve_parameters.len() - pieces[pieces.len() - 1] < 4 {
372        pieces.pop();
373    }
374    pieces.push(curve_parameters.len() - 1);
375    if pieces.len() < 3 {
376        let curve = interpolate_curve(&points, degree, curve_parameters)?;
377        return Ok((curve, excursion, distance));
378    }
379    let mut knots: Vec<f64> = Vec::with_capacity(curve_parameters.len() + 4);
380    let mut control: Vec<Vec4> = Vec::with_capacity(curve_parameters.len());
381    let last_piece = pieces.len() - 2;
382    for piece in 0..=last_piece {
383        let (from, to) = (pieces[piece], pieces[piece + 1]);
384        let fitted = interpolate_curve(&points[from..=to], 3, &curve_parameters[from..=to])?;
385        if piece == 0 {
386            knots.extend(fitted.knots[..4].iter().copied());
387        }
388        knots.extend(fitted.knots[4..fitted.knots.len() - 4].iter().copied());
389        let multiplicity = if piece == last_piece { 4 } else { 3 };
390        knots.extend(std::iter::repeat(curve_parameters[to]).take(multiplicity));
391        control.extend(fitted.control_points.iter().skip(usize::from(piece > 0)).copied());
392    }
393    let curve = NurbsCurve::new(3, knots, control)?;
394    Ok((curve, excursion, distance))
395}
396
397/// The 3D residual a pcurve's image must reach before `build_pcurve_on_surface`
398/// accepts it, and the sample ceiling it refines up to.
399///
400/// A trimmed face's boundary is only ever as accurate as these two numbers, and
401/// every mass property is an integral over that boundary — so this is the floor
402/// under area, volume and centroid for every curved trim in the kernel.
403///
404/// These were `min(1e-3, 1e-4 * scale)` and 160, where `scale` is
405/// `1 + ||curve midpoint||` — the DISTANCE FROM THE WORLD ORIGIN of the curve's
406/// midpoint, not the part's size. That expression pinned to an absolute 1e-3
407/// once the midpoint sat more than about nine units from the origin, so on an
408/// ordinary part the bar was 1e-3 and tracked nothing.
409///
410/// Measured on `offset-shell-box-bore-dished-face`, whose dished wall is a
411/// closed-form identity at 2468.431782123 (three independent routes agree). The
412/// floor is one of TWO errors there; the other is the refitted sphere carrier
413/// that `sphere_offset_surface` now builds in closed form, and before both moved
414/// they partly CANCELLED — which is why either alone reads as a regression:
415///
416/// ```text
417/// floor     Box_O.S2            abs err     relative   gate total  worst case
418/// 1e-3   2468.442270355      +1.049e-02     4.25e-06        (old)
419/// 1e-6   2468.431785429      +3.306e-06     1.34e-09       181.4 s     31.2 s
420/// 1e-7   2468.431782170      +4.746e-08     1.92e-11       218.5 s     32.1 s
421/// 1e-9   2468.431782103      -2.037e-08    -8.25e-12       305.5 s     46.8 s
422/// ```
423///
424/// 1e-7 is chosen: 70x the accuracy of 1e-6 for +20% gate time, where 1e-9 costs
425/// a further +40% for a 2.3x residual no oracle in this corpus could resolve.
426/// All four cells are one session on one binary, so the times are comparable to
427/// each other. Every case carrying a closed-form oracle moves TOWARD it and none
428/// away (`PushFaceTest` 339x, `PushFaceTest2` 275x); no case changes class at
429/// any floor.
430///
431/// A flat ABSOLUTE is what was measured, not what was designed. This codebase
432/// otherwise keys identity bands to part size (`KernelTolerances::heal_band`,
433/// the `1e-11*(1+scale)` blend family), and on a 3000 mm part 1e-7 is 3e-11
434/// relative — refinement will exhaust `MAX_PCURVE_SAMPLES` and return whatever
435/// it reached. Until 2026-09-12 it did so SILENTLY; now the fit's
436/// [`PcurveFitReport`] carries the residual actually achieved and a
437/// [`PcurveFitExit::SampleCeiling`] exit, and the owning operation's
438/// diagnostics count it (`pcurve.unmet_floor`). That is far better than the
439/// 1e-3 it replaces but it is not a size-aware bar; deriving one from the
440/// surface's own extent is separate, unmeasured work. Being absolute does remove this gate's dependence on
441/// distance from the origin as a side effect — a real translation-VARIANCE
442/// defect, the same anti-pattern `tolerance::merge_scale` records being removed
443/// elsewhere. `drop_tolerance` and `endpoint_tolerance` below still carry it.
444pub const PCURVE_REFINEMENT_TOLERANCE: f64 = 1e-7;
445
446/// Sample ceiling for a fit's FIRST refinement pass. 160 could not reach
447/// [`PCURVE_REFINEMENT_TOLERANCE`] on a curved trim of any length; 600 is the
448/// value every cell of the table above was measured at.
449///
450/// It BINDS. On `boolean_fuzz_corpus/25_cube_pierce_solid` the fitter stopped
451/// here 1.65e-3 of volume short of the converged answer (2000 and 4000 samples
452/// agree exactly). Raising it globally to 2000 was costed and rejected: +23%
453/// case-gate time, concentrated in three offset/fillet cases, and one case
454/// (`inbox-20260910-offset-shell-collapsed-fillet`) driven 26x outside its
455/// volume band. So it is not raised here: a fit that stops at this ceiling
456/// with a residual still worse than its edge curve's own distance from the
457/// surface re-enters refinement with [`PCURVE_RAISED_SAMPLES`]
458/// ([`refine_pcurve`]); one whose residual is already that gap stops here and
459/// says so ([`PcurveFitReport::off_surface`]), because no sample count can
460/// close it.
461const MAX_PCURVE_SAMPLES: usize = 600;
462
463/// Sample ceiling for the ONE raised pass a fit may take past
464/// [`MAX_PCURVE_SAMPLES`]. 2000 is the ceiling the convergence sweep measured
465/// on `25_cube_pierce_solid`: its 2000 and 4000 cells agree to every printed
466/// digit because refinement there stops itself near 1540 samples, so the cap
467/// is not what binds.
468const PCURVE_RAISED_SAMPLES: usize = 2000;
469
470/// What a pcurve fit ACHIEVED, returned beside the curve so a ceiling exit can
471/// never be mistaken for a met floor.
472///
473/// [`fit_pcurve_on_surface`] asks for [`PCURVE_REFINEMENT_TOLERANCE`] and stops
474/// at [`MAX_PCURVE_SAMPLES`] — or, where that ceiling bound and the floor is
475/// reachable, at [`PCURVE_RAISED_SAMPLES`]. Until 2026-09-12 a trim that ran
476/// out of samples came back exactly like one that met the floor; on
477/// `boolean_fuzz_corpus/25_cube_pierce_solid` that silence hid a 1.65e-3 volume
478/// error. Now every fit says which it was: `residual` is measured on the curve
479/// that is actually returned and `exit` names why refinement stopped.
480///
481/// This is a MEASUREMENT taken at a construction, not a band.
482#[derive(Clone, Copy, Debug, PartialEq)]
483pub struct PcurveFitReport {
484    /// Largest 3D distance between the returned pcurve's image on the surface
485    /// and the edge curve. On a [`PcurveFitExit::Converged`] exit whose final
486    /// round probed every span this is that round's maximum — the refinement's
487    /// own acceptance test. On every other exit, including a converged round
488    /// that skipped spans narrower than 1e-3 of the parameter range, it is a
489    /// sweep over EVERY span of the returned curve.
490    pub residual: f64,
491    /// The residual the refinement was asked to reach.
492    pub floor: f64,
493    /// Interpolation samples in the returned curve.
494    pub samples: usize,
495    /// Why refinement stopped.
496    pub exit: PcurveFitExit,
497    /// Largest distance from the edge curve to the surface at every station
498    /// the fit inverted — the part of `residual` no pcurve can remove, since a
499    /// pcurve's image lies ON the surface. Zero for an affine carrier. A value
500    /// above `floor` says the floor is unreachable for this input; a ceiling
501    /// exit is retried only while `residual` exceeds it by more than the floor,
502    /// i.e. while the returned curve is worse than its input.
503    pub off_surface: f64,
504    /// Whether the fit re-entered refinement with [`PCURVE_RAISED_SAMPLES`]
505    /// after [`MAX_PCURVE_SAMPLES`] bound with the floor unmet.
506    pub raised: bool,
507    /// Largest parameter distance a station's inversion was pulled back into
508    /// an OPEN direction's domain when the returned curve was interpolated: a
509    /// trim asked for outside its chart is folded onto the chart's boundary,
510    /// the edge stays right and the face becomes the wrong region on a body
511    /// `validate()` passes (3.03e-2 of volume on the rib-base document). Zero
512    /// when nothing was pulled. A REPORT, not a bar: nothing here refuses on
513    /// it. Its coverage is the clamp in `build_interpolant`, which is the only
514    /// place a raw out-of-domain parameter reaches today — an affine carrier's
515    /// inversion is unclamped, so an affine plane fitted through the station
516    /// lane reads here. It does NOT see a clamp the projector applied before
517    /// the fit: the analytic `Plane` closed form (a non-affine net) clamps to
518    /// its domain, `RuledRevolution` clamps its axial parameter to `[0, 1]`
519    /// and the general Newton clamps through `fit_parameter`; those stations
520    /// arrive in-domain and read as `off_surface` instead. The exact affine
521    /// lanes (`fit_pcurve_on_surface` on an affine carrier,
522    /// `affine_pcurve_on_range`) never clamp and read zero by construction.
523    pub clamped_excursion: f64,
524    /// The 3D distance the clamp corresponds to: between the extended surface
525    /// at the raw parameter and the clamped foot, at the station of the
526    /// largest such distance. Zero when nothing was pulled.
527    pub clamped_distance: f64,
528}
529
530impl PcurveFitReport {
531    /// Whether the returned curve reaches the floor it was asked for.
532    pub fn met_floor(&self) -> bool {
533        self.residual <= self.floor
534    }
535
536    /// Whether the returned curve is on the ACCEPTANCE BAR: the floor it was
537    /// asked for, plus the stations' own standoff from the carrier.
538    ///
539    /// A pcurve's image lies on the carrier, so at a station standing `g` off
540    /// it no image can come closer than `g`; a fit whose residual is within
541    /// the floor of `off_surface` is as good as the input allows, and one over
542    /// it is worse than its input — the same condition [`refine_pcurve`] uses
543    /// to decide that a raise can still buy accuracy. This is the bar every
544    /// station builder refuses over ([`build_pcurve_on_surface_stations`]).
545    /// It is blind to sideways error quadratically (a station tracked `d`
546    /// aside of the projection of a point `g` off the surface reads only
547    /// `d²/(2g)` further away), which is stated rather than hidden: on the
548    /// import corpus the stations that stand off their carriers are the
549    /// FILE's curves, and the bar there is the file's own number.
550    pub fn on_bar(&self) -> bool {
551        self.residual <= self.floor + self.off_surface
552    }
553}
554
555/// Why a pcurve refinement stopped. Only [`PcurveFitExit::Converged`] means the
556/// floor was met; every other variant is a BUDGET exit, and the report's
557/// residual was measured on the returned curve rather than assumed.
558#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
559pub enum PcurveFitExit {
560    /// A probe round found every station within the floor. An affine carrier
561    /// maps exactly and reports this with a zero residual.
562    Converged,
563    /// The sample ceiling bound before the floor was met: [`MAX_PCURVE_SAMPLES`]
564    /// when the raise was declined (the residual is already explained by
565    /// [`PcurveFitReport::off_surface`]), [`PCURVE_RAISED_SAMPLES`] when it was
566    /// taken and bound too.
567    SampleCeiling,
568    /// Every refinement round was spent and the last one still inserted
569    /// samples, so the curve it built was never re-checked by the loop.
570    RoundBudget,
571    /// A round found stations over the floor but could improve none of them:
572    /// the edge point could not be inverted inside the drop band, or (in the
573    /// raised pass) the pcurve already tracks the surface's nearest point to
574    /// within the floor. Either way the edge curve itself sits off the surface
575    /// there and no pcurve can close that gap; the residual reports how far.
576    /// The range lane's end-span challenger (its station fitter's TRACKING
577    /// mode) also exits here when a round's probes all track their feet
578    /// within the floor while the deviation residual, which includes the
579    /// edge's standoff, stays over it.
580    Stalled,
581    /// The last probe round found every span it PROBED within the floor, but
582    /// skipped spans narrower than the loop's own resolution floor — 1e-3 of
583    /// the parameter range in [`refine_pcurve`], [`SPAN_FLOOR`] (1e-6) in
584    /// [`fit_pcurve_on_surface_stations`] — and a sweep over those found the
585    /// floor unmet.
586    /// Until 2026-09-12 this exit was reported as `Converged` with the probed
587    /// spans' maximum: on `25_cube_pierce_solid` a 1539-sample fit with 1537
588    /// narrow spans read 7e-8 that way while every span read 1.9e-4.
589    SpanFloor,
590}
591
592/// A fitted pcurve with what the fit achieved.
593#[derive(Clone, Debug)]
594pub struct PcurveFit {
595    pub curve: NurbsCurve,
596    pub report: PcurveFitReport,
597}
598
599/// Per-operation tally of pcurve fits and how many missed their floor.
600///
601/// [`build_pcurve_on_surface`] and its marched twin are called from some forty
602/// sites, most of which have no diagnostics record to write into. Rather than
603/// thread one through every layer, an operation that returns
604/// [`KernelDiagnostics`] opens a [`PcurveFitScope`]; every fit made on that
605/// thread while the scope is open is tallied here, and closing the scope hands
606/// the tally back for [`PcurveFitLedger::report_into`]. This is the shape the
607/// boolean's `CONFORMANCE_REPAIRS` counter already uses. With no scope open,
608/// recording is a no-op.
609///
610/// Scopes nest: CLOSING a child folds it into its parent, so an offset shell
611/// sees the fits of the booleans it ran. A scope that is DROPPED without being
612/// closed is discarded, not folded — a boolean attempt that refused (and may be
613/// retried under perturbation) must not leave its fits on the caller's tally.
614///
615/// Thread-local by construction: a fit made on a rayon worker (the `parallel`
616/// feature's STEP body import) is not seen by a scope on the calling thread. No
617/// diagnostics-returning operation builds pcurves off-thread today.
618#[derive(Clone, Copy, Debug, Default, PartialEq)]
619pub struct PcurveFitLedger {
620    /// Fits recorded, exact affine ones included.
621    pub fits: u64,
622    /// Fits whose returned curve did not reach its floor.
623    pub unmet: u64,
624    /// Unmet fits that stopped at a sample ceiling.
625    pub sample_ceiling: u64,
626    /// Unmet fits that spent every refinement round.
627    pub round_budget: u64,
628    /// Unmet fits that could insert nothing.
629    pub stalled: u64,
630    /// Unmet fits whose remaining over-floor spans were all below the loop's
631    /// 1e-3 span floor.
632    pub span_floor: u64,
633    /// Largest residual among the unmet fits; zero when there are none.
634    pub worst_unmet_residual: f64,
635    /// Fits that re-entered refinement with [`PCURVE_RAISED_SAMPLES`], met or
636    /// not.
637    pub raised: u64,
638    /// Unmet ceiling exits that were NOT raised because the residual is
639    /// already within the floor of the edge curve's own gap from the surface.
640    pub raise_declined: u64,
641    /// Largest [`PcurveFitReport::off_surface`] over every fit.
642    pub worst_off_surface: f64,
643    /// Fits whose interpolant was clamped into an open direction's domain
644    /// ([`PcurveFitReport::clamped_excursion`] above zero), met or not.
645    pub clamped: u64,
646    /// Largest [`PcurveFitReport::clamped_excursion`] over every fit.
647    pub worst_clamped_excursion: f64,
648    /// Largest [`PcurveFitReport::clamped_distance`] over every fit.
649    pub worst_clamped_distance: f64,
650}
651
652impl PcurveFitLedger {
653    fn record(&mut self, report: &PcurveFitReport) {
654        self.fits += 1;
655        if report.raised {
656            self.raised += 1;
657        }
658        self.worst_off_surface = self.worst_off_surface.max(report.off_surface);
659        if report.clamped_excursion > 0.0 {
660            self.clamped += 1;
661            self.worst_clamped_excursion = self.worst_clamped_excursion.max(report.clamped_excursion);
662            self.worst_clamped_distance = self.worst_clamped_distance.max(report.clamped_distance);
663        }
664        if report.met_floor() {
665            return;
666        }
667        self.unmet += 1;
668        match report.exit {
669            PcurveFitExit::Converged => {}
670            PcurveFitExit::SampleCeiling => {
671                self.sample_ceiling += 1;
672                if !report.raised {
673                    self.raise_declined += 1;
674                }
675            }
676            PcurveFitExit::RoundBudget => self.round_budget += 1,
677            PcurveFitExit::Stalled => self.stalled += 1,
678            PcurveFitExit::SpanFloor => self.span_floor += 1,
679        }
680        self.worst_unmet_residual = self.worst_unmet_residual.max(report.residual);
681    }
682
683    fn fold(&mut self, child: &PcurveFitLedger) {
684        self.fits += child.fits;
685        self.unmet += child.unmet;
686        self.sample_ceiling += child.sample_ceiling;
687        self.round_budget += child.round_budget;
688        self.stalled += child.stalled;
689        self.span_floor += child.span_floor;
690        self.worst_unmet_residual = self.worst_unmet_residual.max(child.worst_unmet_residual);
691        self.raised += child.raised;
692        self.raise_declined += child.raise_declined;
693        self.worst_off_surface = self.worst_off_surface.max(child.worst_off_surface);
694        self.clamped += child.clamped;
695        self.worst_clamped_excursion = self.worst_clamped_excursion.max(child.worst_clamped_excursion);
696        self.worst_clamped_distance = self.worst_clamped_distance.max(child.worst_clamped_distance);
697    }
698
699    /// Write the tally into an operation's diagnostics: the `pcurve.*`
700    /// counters and measurements always, plus one `pcurve.unmet_floor` event at
701    /// [`DiagnosticSeverity::Degraded`] when any fit missed its floor. Degraded,
702    /// not Error: the result is still the kernel's best construction and stays
703    /// shippable, but it no longer claims the accuracy its floor states.
704    pub fn report_into(&self, diagnostics: &mut KernelDiagnostics) {
705        diagnostics.count_n("pcurve.fits", self.fits);
706        diagnostics.count_n("pcurve.unmet_floor", self.unmet);
707        diagnostics.count_n("pcurve.exit.sample_ceiling", self.sample_ceiling);
708        diagnostics.count_n("pcurve.exit.round_budget", self.round_budget);
709        diagnostics.count_n("pcurve.exit.stalled", self.stalled);
710        diagnostics.count_n("pcurve.exit.span_floor", self.span_floor);
711        diagnostics.count_n("pcurve.budget_raised", self.raised);
712        diagnostics.count_n("pcurve.budget_raise_declined", self.raise_declined);
713        diagnostics.measure_max("pcurve.worst_unmet_residual", self.worst_unmet_residual);
714        diagnostics.measure_max("pcurve.worst_off_surface", self.worst_off_surface);
715        diagnostics.count_n("pcurve.clamped", self.clamped);
716        diagnostics.measure_max("pcurve.worst_clamped_excursion", self.worst_clamped_excursion);
717        diagnostics.measure_max("pcurve.worst_clamped_distance", self.worst_clamped_distance);
718        if self.unmet > 0 {
719            diagnostics.event(
720                DiagnosticSeverity::Degraded,
721                KernelStage::Refine,
722                "pcurve.unmet_floor",
723                format!(
724                    "{} of {} pcurve fits did not reach the {:.0e} floor \
725                     (sample ceiling {}, round budget {}, stalled {}, span floor {}); \
726                     worst residual {:.3e}; budget raised on {}, declined on {}; \
727                     edge curves sit off their surfaces by up to {:.3e}{}",
728                    self.unmet,
729                    self.fits,
730                    PCURVE_REFINEMENT_TOLERANCE,
731                    self.sample_ceiling,
732                    self.round_budget,
733                    self.stalled,
734                    self.span_floor,
735                    self.worst_unmet_residual,
736                    self.raised,
737                    self.raise_declined,
738                    self.worst_off_surface,
739                    if self.clamped > 0 {
740                        format!(
741                            "; {} fit(s) clamped into their chart by up to {:.3e} in parameter, {:.3e} in 3D",
742                            self.clamped, self.worst_clamped_excursion, self.worst_clamped_distance
743                        )
744                    } else {
745                        String::new()
746                    },
747                ),
748            );
749        }
750    }
751}
752
753thread_local! {
754    /// The open [`FootMemo`] on this thread, if any ([`FootMemoScope`]).
755    static FOOT_MEMO: std::cell::RefCell<Option<FootMemo>> = const { std::cell::RefCell::new(None) };
756}
757
758/// Exact-input memo of the common reader's projections on ONE surface: the
759/// global foot of an edge point and the foot seeded at a (u, v), keyed by the
760/// bit patterns of their inputs. Both projectors are pure functions of
761/// (surface, point[, seed]), so a hit returns the value a fresh call would:
762/// the readings, decisions and fits are bit-identical with or without it.
763/// It exists for cost: within one coedge the same edge points are read by
764/// the trigger read, every tracking-mode verification round, the
765/// challenger's final read and the importer's keep read, each of which
766/// projected them again (`importTestWorking`, 2026-10-05: about 10k probes
767/// read 3-5 times on each of nine off-carrier refits, 3-5 s apiece).
768struct FootMemo {
769    surface: *const NurbsSurface,
770    global: rustc_hash::FxHashMap<[u64; 3], Option<crate::SurfaceProjection>>,
771    seeded: rustc_hash::FxHashMap<[u64; 5], Option<crate::SurfaceProjection>>,
772}
773
774/// Opens a [`FootMemo`] for `surface` on this thread until dropped. Opening
775/// one where a memo for the SAME surface is already open reuses it (an inner
776/// scope, e.g. the range challenger inside an importer coedge); a memo for
777/// another surface is set aside and restored on drop. The borrow of `surface`
778/// keeps it alive and unmodified for the scope, so the pointer key cannot be
779/// reused by another surface while the memo answers for it.
780pub(crate) struct FootMemoScope<'a> {
781    previous: Option<Option<FootMemo>>,
782    _surface: std::marker::PhantomData<&'a NurbsSurface>,
783    /// Not `Send`: the scope must close on the thread that opened it.
784    _thread_bound: std::marker::PhantomData<*const ()>,
785}
786
787impl<'a> FootMemoScope<'a> {
788    pub(crate) fn open(surface: &'a NurbsSurface) -> Self {
789        let key = surface as *const NurbsSurface;
790        let previous = FOOT_MEMO.with(|memo| {
791            let mut memo = memo.borrow_mut();
792            if memo.as_ref().is_some_and(|open| std::ptr::eq(open.surface, key)) {
793                None
794            } else {
795                Some(memo.replace(FootMemo { surface: key, global: Default::default(), seeded: Default::default() }))
796            }
797        });
798        Self { previous, _surface: std::marker::PhantomData, _thread_bound: std::marker::PhantomData }
799    }
800}
801
802impl Drop for FootMemoScope<'_> {
803    fn drop(&mut self) {
804        if let Some(previous) = self.previous.take() {
805            FOOT_MEMO.with(|memo| *memo.borrow_mut() = previous);
806        }
807    }
808}
809
810fn point_bits(point: Vec3) -> [u64; 3] {
811    [point.x.to_bits(), point.y.to_bits(), point.z.to_bits()]
812}
813
814/// [`project_point_to_surface`] as `.ok()`, through the open memo for this
815/// surface when there is one. `#[track_caller]`: the projector still sees the
816/// reader's own call site. Under a `BREP_PROJECTION_GLOBAL_SITES`/`_EXCEPT`
817/// filter the projector's answer depends on that site, so the memo is not
818/// consulted then.
819#[track_caller]
820fn memo_global_foot(surface: &NurbsSurface, point: Vec3) -> Option<crate::SurfaceProjection> {
821    if crate::projection::global_site_filter_active() {
822        return project_point_to_surface(surface, point).ok();
823    }
824    let key = point_bits(point);
825    let hit = FOOT_MEMO.with(|memo| {
826        memo.borrow().as_ref().filter(|open| std::ptr::eq(open.surface, surface)).map(|open| open.global.get(&key).copied())
827    });
828    match hit {
829        Some(Some(foot)) => foot,
830        Some(None) => {
831            let foot = project_point_to_surface(surface, point).ok();
832            FOOT_MEMO.with(|memo| {
833                if let Some(open) = memo.borrow_mut().as_mut().filter(|open| std::ptr::eq(open.surface, surface)) {
834                    open.global.insert(key, foot);
835                }
836            });
837            foot
838        }
839        None => project_point_to_surface(surface, point).ok(),
840    }
841}
842
843/// [`project_point_to_surface_seeded`] as `.ok()`, through the open memo for
844/// this surface when there is one.
845fn memo_seeded_foot(surface: &NurbsSurface, point: Vec3, seed_u: f64, seed_v: f64) -> Option<crate::SurfaceProjection> {
846    let [x, y, z] = point_bits(point);
847    let key = [x, y, z, seed_u.to_bits(), seed_v.to_bits()];
848    let hit = FOOT_MEMO.with(|memo| {
849        memo.borrow().as_ref().filter(|open| std::ptr::eq(open.surface, surface)).map(|open| open.seeded.get(&key).copied())
850    });
851    match hit {
852        Some(Some(foot)) => foot,
853        Some(None) => {
854            let foot = project_point_to_surface_seeded(surface, point, seed_u, seed_v).ok();
855            FOOT_MEMO.with(|memo| {
856                if let Some(open) = memo.borrow_mut().as_mut().filter(|open| std::ptr::eq(open.surface, surface)) {
857                    open.seeded.insert(key, foot);
858                }
859            });
860            foot
861        }
862        None => project_point_to_surface_seeded(surface, point, seed_u, seed_v).ok(),
863    }
864}
865
866thread_local! {
867    /// The open [`PcurveFitScope`]s on this thread, innermost last.
868    static PCURVE_FIT_SCOPES: std::cell::RefCell<Vec<PcurveFitLedger>> =
869        const { std::cell::RefCell::new(Vec::new()) };
870}
871
872/// `BREP_DEBUG_PCURVE_FIT` only: a process-wide id per [`refine_pcurve`]
873/// call, so its first-pass and candidate `PCURVE-TRACK` lines pair up even
874/// when fits run on several threads.
875static PCURVE_TRACK_CALL: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
876
877/// An open tally of the pcurve fits made on this thread — see
878/// [`PcurveFitLedger`]. Open it where an operation starts, [`close`] it where
879/// the operation returns its diagnostics; a `?` that unwinds past it drops the
880/// tally.
881///
882/// [`close`]: PcurveFitScope::close
883pub struct PcurveFitScope {
884    /// Not `Send`: the scope must close on the thread that opened it.
885    _thread_bound: std::marker::PhantomData<*const ()>,
886}
887
888impl PcurveFitScope {
889    pub fn open() -> Self {
890        PCURVE_FIT_SCOPES.with(|scopes| scopes.borrow_mut().push(PcurveFitLedger::default()));
891        Self {
892            _thread_bound: std::marker::PhantomData,
893        }
894    }
895
896    /// Take the tally, folding it into the enclosing scope if there is one.
897    pub fn close(self) -> PcurveFitLedger {
898        let ledger = PCURVE_FIT_SCOPES.with(|scopes| {
899            let mut scopes = scopes.borrow_mut();
900            let ledger = scopes.pop().unwrap_or_default();
901            if let Some(parent) = scopes.last_mut() {
902                parent.fold(&ledger);
903            }
904            ledger
905        });
906        std::mem::forget(self);
907        ledger
908    }
909}
910
911impl Drop for PcurveFitScope {
912    fn drop(&mut self) {
913        PCURVE_FIT_SCOPES.with(|scopes| {
914            scopes.borrow_mut().pop();
915        });
916    }
917}
918
919pub(crate) fn record_fit(report: &PcurveFitReport) {
920    // Per-fit trace for the convergence probes (`trim_floor_sweep_probe` and
921    // its offset-shell sibling): one line per fit with the whole report, the
922    // way `BREP_DEBUG_PCURVE` traces an endpoint failure.
923    if std::env::var_os("BREP_DEBUG_PCURVE_FIT").is_some() {
924        eprintln!("PCURVE-FIT {report:?}");
925    }
926    PCURVE_FIT_SCOPES.with(|scopes| {
927        if let Some(open) = scopes.borrow_mut().last_mut() {
928            open.record(report);
929        }
930    });
931}
932
933/// Largest 3D distance between `pcurve`'s image and `curve` at the
934/// quarter-points of every span of `parameters` — the stations refinement
935/// probes, over EVERY span. Evaluations only; nothing is inverted. The sweep
936/// measures, it does not decide.
937fn probe_residual(
938    surface: &NurbsSurface,
939    curve: &NurbsCurve,
940    pcurve: &NurbsCurve,
941    parameters: &[f64],
942    domain: [f64; 2],
943    _u_domain: [f64; 2],
944    _v_domain: [f64; 2],
945) -> Result<f64, String> {
946    probe_residual_at(surface, curve, pcurve, parameters, domain, &[0.25, 0.5, 0.75])
947}
948
949/// Extra interior points per sample span, at `j/17` for `j = 1..=16`, that
950/// [`tracking_acceptance_residual`] reads IN ADDITION to [`probe_residual`]'s
951/// quarter, mid and three-quarter points (nineteen fractions per span in
952/// all), so it can never read below the three-point maximum on the same
953/// curve. The three quarter-points under-read the sixteen-point maximum by
954/// 3.3–6.5 % on the constructed-cylinder union's imprint pcurves, and sixteen
955/// sit within 0.4 % of sixty-four there (2026-10-04); the Converged path keeps
956/// its own read.
957const TRACKING_ACCEPTANCE_POINTS: usize = 16;
958
959/// The same-parameter image-vs-edge distance of `pcurve` at `locals` (span
960/// fractions) inside every span of `parameters`: the largest, or an error if
961/// any is not finite.
962fn probe_residual_at(
963    surface: &NurbsSurface,
964    curve: &NurbsCurve,
965    pcurve: &NurbsCurve,
966    parameters: &[f64],
967    [t0, t1]: [f64; 2],
968    locals: &[f64],
969) -> Result<f64, String> {
970    let mut worst = 0.0_f64;
971    for span in parameters.windows(2) {
972        for &local in locals {
973            let fraction = span[0] + (span[1] - span[0]) * local;
974            let parameter = pcurve.evaluate(fraction)?;
975            let on_surface =
976                surface.evaluate_extended(parameter.x, parameter.y)?;
977            let on_curve = curve.evaluate(t0 + (t1 - t0) * fraction)?;
978            // `f64::max` drops a NaN operand, so a non-finite distance would
979            // leave `worst` reading the finite stations only. A residual that
980            // can accept a curve must have measured every station: fail
981            // closed instead.
982            let distance = on_surface.sub(on_curve).length();
983            if !distance.is_finite() {
984                return Err(format!(
985                    "probe_residual: non-finite image distance {distance} at fraction {fraction}"
986                ));
987            }
988            worst = worst.max(distance);
989        }
990    }
991    Ok(worst)
992}
993
994/// The probe fractions a native read takes for the given pcurves: both
995/// domain ENDPOINTS, and the quarter points plus 16 interior points of every
996/// span between each curve's OWN distinct knots (the installed interpolant's
997/// spans, not the construction stations). Merged over all the curves, sorted
998/// and deduplicated, so a standing fit and a candidate are read at the SAME
999/// fractions.
1000pub(crate) fn native_probe_fractions(curves: &[&NurbsCurve]) -> Result<Vec<f64>, String> {
1001    let locals: Vec<f64> = [0.25, 0.5, 0.75]
1002        .into_iter()
1003        .chain((1..=TRACKING_ACCEPTANCE_POINTS).map(|index| index as f64 / (TRACKING_ACCEPTANCE_POINTS + 1) as f64))
1004        .collect();
1005    let mut fractions = Vec::new();
1006    for curve in curves {
1007        let [q0, q1] = curve.domain()?;
1008        fractions.push(q0);
1009        fractions.push(q1);
1010        let mut knots: Vec<f64> = curve.knots.iter().copied().filter(|k| *k >= q0 && *k <= q1).collect();
1011        knots.dedup();
1012        for span in knots.windows(2).filter(|w| w[1] > w[0]) {
1013            for &local in &locals {
1014                fractions.push(span[0] + (span[1] - span[0]) * local);
1015            }
1016        }
1017    }
1018    fractions.sort_by(f64::total_cmp);
1019    fractions.dedup();
1020    Ok(fractions)
1021}
1022
1023/// One pcurve read at given probe fractions against the edge curve (whose
1024/// parameter is t0 + (t1 - t0) x fraction): the image's distance from the
1025/// edge point's FOOT on the carrier (its TRACKING, the part a pcurve can
1026/// improve; the foot is seeded at the pcurve's own uv, closed form on an
1027/// analytic carrier), the image's distance from the edge point itself (the
1028/// same-parameter DEVIATION), and the edge point's distance from the carrier
1029/// (its INHERITED STANDOFF). Maxima over the probes; fails closed on any
1030/// non-finite point or distance.
1031#[derive(Clone, Copy, Debug, PartialEq)]
1032struct NativeRead {
1033    tracking: f64,
1034    deviation: f64,
1035    standoff: f64,
1036}
1037
1038fn native_read(surface: &NurbsSurface, curve: &NurbsCurve, pcurve: &NurbsCurve, [t0, t1]: [f64; 2], fractions: &[f64]) -> Result<NativeRead, String> {
1039    let mut read = NativeRead { tracking: 0.0, deviation: 0.0, standoff: 0.0 };
1040    for &fraction in fractions {
1041        let parameter = pcurve.evaluate(fraction)?;
1042        let image = surface.evaluate_extended(parameter.x, parameter.y)?;
1043        let on_curve = curve.evaluate(t0 + (t1 - t0) * fraction)?;
1044        let foot = crate::projection::project_point_to_surface_from_seed(surface, on_curve, [parameter.x, parameter.y])?;
1045        let tracking = image.sub(foot.point).length();
1046        let deviation = image.sub(on_curve).length();
1047        if !(tracking.is_finite() && deviation.is_finite() && foot.distance.is_finite()) {
1048            return Err(format!("native read: non-finite reading at fraction {fraction}"));
1049        }
1050        read.tracking = read.tracking.max(tracking);
1051        read.deviation = read.deviation.max(deviation);
1052        read.standoff = read.standoff.max(foot.distance);
1053    }
1054    Ok(read)
1055}
1056
1057/// Whether an off-carrier tracking candidate replaces the standing fit, both
1058/// read at the SAME probes ([`native_probe_fractions`] of both curves): its
1059/// tracking finite, within the unchanged floor and strictly lower, AND its
1060/// same-parameter deviation no worse (the reported total, inherited standoff
1061/// included, must not grow), AND its own refinement fields finite.
1062fn standoff_candidate_accepted(standing: NativeRead, candidate: NativeRead, candidate_fields_finite: bool, floor: f64) -> bool {
1063    candidate_fields_finite
1064        && candidate.tracking.is_finite()
1065        && candidate.deviation.is_finite()
1066        && candidate.tracking <= floor
1067        && candidate.tracking < standing.tracking
1068        && candidate.deviation <= standing.deviation
1069}
1070
1071/// [`probe_residual_at`] at [`probe_residual`]'s three quarter-points plus
1072/// [`TRACKING_ACCEPTANCE_POINTS`] evenly spaced interior points of every span
1073/// (nineteen per span): the maximum of the old three-point reading and the
1074/// sixteen-point one. It is the reading a budget-exit tracking candidate is
1075/// accepted on, and the residual it is then reported with.
1076fn tracking_acceptance_residual(
1077    surface: &NurbsSurface,
1078    curve: &NurbsCurve,
1079    pcurve: &NurbsCurve,
1080    parameters: &[f64],
1081    domain: [f64; 2],
1082) -> Result<f64, String> {
1083    let locals: Vec<f64> = [0.25, 0.5, 0.75]
1084        .into_iter()
1085        .chain((1..=TRACKING_ACCEPTANCE_POINTS).map(|index| index as f64 / (TRACKING_ACCEPTANCE_POINTS + 1) as f64))
1086        .collect();
1087    probe_residual_at(surface, curve, pcurve, parameters, domain, &locals)
1088}
1089
1090/// Whether a projection-tracking candidate replaces the fit standing in
1091/// [`refine_pcurve`], given the `reading` it is judged on (see
1092/// [`judge_tracking_candidate`]). `standing_dense` says the standing fit's
1093/// reading is itself a nineteen-point one: a budget-exit candidate was taken.
1094///
1095/// While the standing reading is the fitter's own, a `Converged` candidate is
1096/// taken exactly as before (within the floor, better). A BUDGET exit
1097/// (`SampleCeiling`, `RoundBudget`, `Stalled`, `SpanFloor`) is taken only to
1098/// repair a standing fit that MISSES the floor, and only when its
1099/// nineteen-point reading is finite, within the unchanged floor and strictly
1100/// better. Once the standing reading is a nineteen-point one, every later
1101/// candidate is compared nineteen against nineteen: a Converged one must read
1102/// finite, within the floor and strictly better on the same stencil, and a
1103/// budget exit is never taken (the standing fit is then within the floor).
1104/// The exit label says how the loop stopped, not whether the curve is good: on
1105/// the 15° constructed-cylinder union all six imprint pcurves that shipped
1106/// 1.3e-7..7.0e-6 off had a tracking candidate reading 1.4e-8..9.95e-8 at
1107/// three points per span, discarded for its label (2026-10-04). A standing
1108/// fit already within the floor is never replaced by a budget exit, so every
1109/// in-floor fit is unchanged; a taken candidate keeps its own exit, samples
1110/// and raise, and reports the reading it was taken on.
1111fn tracking_candidate_accepted(standing: f64, standing_dense: bool, floor: f64, exit: PcurveFitExit, reading: f64) -> bool {
1112    if exit == PcurveFitExit::Converged && !standing_dense {
1113        return reading <= floor && reading < standing;
1114    }
1115    if exit != PcurveFitExit::Converged && !(standing > floor) {
1116        return false;
1117    }
1118    reading.is_finite() && reading <= floor && reading < standing
1119}
1120
1121/// The reading a tracking candidate is judged on, and whether it replaces the
1122/// standing fit: `own` (the candidate's own residual) for a Converged
1123/// candidate while the standing reading is the fitter's own; otherwise
1124/// `dense` ([`tracking_acceptance_residual`]'s nineteen points per span), read
1125/// only when the candidate could be taken. An error from `dense` declines.
1126/// [`refine_pcurve`] decides and traces through this one function.
1127fn judge_tracking_candidate(
1128    standing: f64,
1129    standing_dense: bool,
1130    floor: f64,
1131    exit: PcurveFitExit,
1132    own: f64,
1133    dense: impl FnOnce() -> Result<f64, String>,
1134) -> (Result<Option<f64>, String>, bool) {
1135    let reading = if exit == PcurveFitExit::Converged && !standing_dense {
1136        Ok(Some(own))
1137    } else if exit == PcurveFitExit::Converged || standing > floor {
1138        dense().map(Some)
1139    } else {
1140        Ok(None)
1141    };
1142    let accepted = matches!(
1143        reading,
1144        Ok(Some(value)) if tracking_candidate_accepted(standing, standing_dense, floor, exit, value)
1145    );
1146    (reading, accepted)
1147}
1148
1149/// What one call to [`refine_pcurve`] achieved, beside the curve it left in
1150/// place.
1151struct Refinement {
1152    exit: PcurveFitExit,
1153    residual: f64,
1154    off_surface: f64,
1155    raised: bool,
1156    /// [`PcurveFitReport::clamped_excursion`] and
1157    /// [`PcurveFitReport::clamped_distance`] of the curve left in place.
1158    clamped: [f64; 2],
1159}
1160
1161/// Adaptive 3D-residual refinement of an interpolant, with the PER-TRIM sample
1162/// budget. Shared by [`fit_pcurve_on_surface`] (global inversion) and
1163/// [`fit_pcurve_on_surface_marched`] (seeded projection); `insert` is the one
1164/// step that differs — given a probe station's fraction, the current pcurve's
1165/// parameter there and the edge point, it returns the surface parameter to
1166/// interpolate through (or `None` to decline the station) and the distance
1167/// from the edge point to the surface at that station, its GAP.
1168///
1169/// **First pass.** Four rounds at [`MAX_PCURVE_SAMPLES`], each probing the
1170/// quarter-points of every span wider than 1e-3 of the parameter range and
1171/// inserting the stations over `floor`. Byte-for-byte the pre-2026-09-12
1172/// loop; every exit is classified ([`PcurveFitExit`]) and the residual is
1173/// measured on the RETURNED curve — over every span, unless the final round
1174/// probed every span, in which case that round's maximum already is.
1175///
1176/// **The raise.** A [`PcurveFitExit::SampleCeiling`] exit re-enters the four
1177/// rounds ONCE with [`PCURVE_RAISED_SAMPLES`], continuing from the samples it
1178/// has, when the residual exceeds `off_surface` by more than the floor — the
1179/// returned curve is worse than the edge curve's own distance from the
1180/// surface, so more samples CAN improve it. Measured on `25_cube_pierce_solid`
1181/// (2026-09-12): the edge curves there sit off their surfaces by up to 1.9e-4,
1182/// four of the eight ceiling exits already read their gap to four digits, and
1183/// raising those spent 900 inversions each for no change in the swept
1184/// residual. The raised pass therefore inserts only where the pcurve's surface
1185/// point is further than the floor from the PROJECTION of the edge point — its
1186/// tracking error, which the inversion just computed: it stops when the pcurve
1187/// tracks the surface's nearest point to the floor, which is the best any
1188/// pcurve can do, and reports [`PcurveFitExit::Stalled`] with the deviation it
1189/// actually reached when that is still over the floor. The limit of that pass
1190/// as the floor tightens is the projected edge curve itself, so a volume built
1191/// on it can be shown to converge; the deviation test alone cannot say that on
1192/// off-surface input. Grow-once, capped, deterministic.
1193#[allow(clippy::too_many_arguments)]
1194fn refine_pcurve(
1195    surface: &NurbsSurface,
1196    curve: &NurbsCurve,
1197    domain: [f64; 2],
1198    u_domain: [f64; 2],
1199    v_domain: [f64; 2],
1200    floor: f64,
1201    joins: &[f64],
1202    parameters: &mut Vec<f64>,
1203    raw: &mut Vec<[f64; 2]>,
1204    pcurve: &mut NurbsCurve,
1205    off_surface: f64,
1206    clamped: [f64; 2],
1207    mut insert: impl FnMut(f64, Vec3, Vec3) -> Result<(Option<[f64; 2]>, f64), String>,
1208) -> Result<Refinement, String> {
1209    // Trace only (`BREP_DEBUG_PCURVE_FIT`): reads values the passes already
1210    // computed and decides nothing.
1211    let trace = std::env::var_os("BREP_DEBUG_PCURVE_FIT").is_some();
1212    let call = if trace {
1213        PCURVE_TRACK_CALL.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1
1214    } else {
1215        0
1216    };
1217    let mut best = refine_pcurve_pass(surface, curve, domain, u_domain, v_domain,
1218        floor, joins, parameters, raw, pcurve, off_surface, clamped, false, 1.0, false, &mut insert)?;
1219    if trace {
1220        eprintln!(
1221            "PCURVE-TRACK call={call} first-pass exit={:?} residual={:e} floor={floor:e} off_surface={:e} samples={} raised={} tracking={}",
1222            best.exit,
1223            best.residual,
1224            best.off_surface,
1225            parameters.len(),
1226            best.raised,
1227            if best.off_surface <= floor { "eligible" } else { "standoff-tracking" }
1228        );
1229    }
1230    if best.off_surface > floor {
1231        // The edge curve itself stands off the carrier by more than the floor
1232        // (its INHERITED standoff), so no pcurve can bring the image within the
1233        // floor of the edge point. What a pcurve CAN improve is its tracking
1234        // error: the image's distance from the edge point's foot. Measured
1235        // 2026-10-04 (a2b401d4d, 15-degree crossing, turn 295): a first pass
1236        // ended RoundBudget at 6.726e-6 over a 1.316e-7 standoff and every
1237        // tracking pass was skipped. No candidate refinement when the ACTUAL
1238        // tracking request is met: the first pass's own tracking, read on its
1239        // native spans and endpoints (a diagnostic read; it adds no station),
1240        // within the floor ([`standoff_tracking_rungs`]). A total residual
1241        // under standoff + floor does not prove that: a tangential slip t over
1242        // a standoff g reads sqrt(g^2 + t^2) < g + t.
1243        // `BREP_PCURVE_STANDOFF_TRACKING=0`: diagnostic A/B hatch, not a
1244        // policy. It restores the pre-lane answer (35c8610a1's parent: an
1245        // off-carrier first pass is returned as it stands) for a same-binary
1246        // attribution of a volume or census delta to this lane. Default (unset
1247        // or any other value) is unchanged. It does not touch the in-floor
1248        // tracking rungs or their raise rule, nor the range lane's end-span
1249        // challenger.
1250        if !standoff_tracking_enabled() {
1251            return Ok(best);
1252        }
1253        return standoff_tracking_rungs(surface, curve, domain, u_domain, v_domain, floor, joins,
1254            parameters, raw, pcurve, best, trace, call, &mut insert);
1255    }
1256    // Each target starts from the same baseline station set: retaining the
1257    // coarser target's inserts can exhaust the cap before the finer target
1258    // places its own error-directed stations. Keep the best rung
1259    // [`tracking_candidate_accepted`] takes.
1260    let baseline_parameters = parameters.clone();
1261    let baseline_raw = raw.clone();
1262    let baseline_curve = pcurve.clone();
1263    let baseline_off_surface = best.off_surface;
1264    let baseline_clamped = best.clamped;
1265    // Whether the first pass MISSED the floor: only then may a tracking
1266    // candidate that reaches the sample ceiling still missing its request take
1267    // the existing one-time raise (2026-10-04, 20-degree crossing, turn 295:
1268    // a 1.964e-6 first pass, a candidate at 600 samples reading 1.068e-7,
1269    // declined; the raise needed residual > off_surface + floor). A first pass
1270    // within the floor keeps exactly the work it did before.
1271    // (On this path the edge stands within the floor of the carrier, so a
1272    // first pass over the floor is a tracking miss, not inherited standoff.)
1273    let standing_misses = best.residual > floor;
1274    Ok(tracking_rungs(
1275        best,
1276        floor,
1277        trace,
1278        call,
1279        true,
1280        |fraction| {
1281            let mut refined_parameters = baseline_parameters.clone();
1282            let mut refined_raw = baseline_raw.clone();
1283            let mut refined_curve = baseline_curve.clone();
1284            let candidate = refine_pcurve_pass(surface, curve, domain, u_domain, v_domain,
1285                floor, joins, &mut refined_parameters, &mut refined_raw, &mut refined_curve,
1286                baseline_off_surface, baseline_clamped, true, fraction, standing_misses, &mut insert);
1287            (candidate, (refined_parameters, refined_raw, refined_curve))
1288        },
1289        |state: &RungState| tracking_acceptance_residual(surface, curve, &state.2, &state.0, domain),
1290        |state: &RungState| state.0.len(),
1291        |(refined_parameters, refined_raw, refined_curve): RungState| {
1292            *parameters = refined_parameters;
1293            *raw = refined_raw;
1294            *pcurve = refined_curve;
1295        },
1296    ))
1297}
1298
1299/// A tracking candidate's station parameters, raw surface parameters and
1300/// pcurve, as [`refine_pcurve`] hands them to [`tracking_rungs`].
1301type RungState = (Vec<f64>, Vec<[f64; 2]>, NurbsCurve);
1302
1303/// The tracking rungs of [`refine_pcurve`] after an eligible first pass: for
1304/// each tracking fraction, skip it if the standing fit is already that far
1305/// inside the floor, otherwise run the candidate pass (`run_pass`), judge it
1306/// ([`judge_tracking_candidate`], reading `dense` only when needed), trace
1307/// it, and either take it (`adopt` its state; it becomes the standing fit) or
1308/// stop. `samples` is the candidate's station count, for the trace. Separated
1309/// so the accepted-budget state can be driven with scripted passes;
1310/// `thread_dense` is `true` in production and `false` only to show, in a
1311/// test, what dropping the nineteen-point standing flag would take.
1312#[allow(clippy::too_many_arguments)]
1313fn tracking_rungs<S>(
1314    mut best: Refinement,
1315    floor: f64,
1316    trace: bool,
1317    call: u64,
1318    thread_dense: bool,
1319    mut run_pass: impl FnMut(f64) -> (Result<Refinement, String>, S),
1320    mut dense: impl FnMut(&S) -> Result<f64, String>,
1321    samples: impl Fn(&S) -> usize,
1322    mut adopt: impl FnMut(S),
1323) -> Refinement {
1324    // Whether the standing reading is a nineteen-point one (a budget-exit
1325    // candidate was taken); every later candidate is then judged on it.
1326    let mut standing_dense = false;
1327    for fraction in [0.1, 0.01] {
1328        if best.residual <= floor * fraction {
1329            if trace {
1330                eprintln!(
1331                    "PCURVE-TRACK call={call} candidate fraction={fraction} skipped best_residual={:e} <= {:e}",
1332                    best.residual,
1333                    floor * fraction
1334                );
1335            }
1336            continue;
1337        }
1338        let (candidate, state) = run_pass(fraction);
1339        // The reading the candidate is judged on and the decision (see
1340        // judge_tracking_candidate); an unreadable read declines like a
1341        // failed pass.
1342        let judged_dense = matches!(&candidate, Ok(seen) if seen.exit != PcurveFitExit::Converged || standing_dense);
1343        let (effective, accepted) = match &candidate {
1344            Ok(seen) => judge_tracking_candidate(best.residual, standing_dense, floor, seen.exit, seen.residual, || {
1345                dense(&state)
1346            }),
1347            Err(_) => (Ok(None), false),
1348        };
1349        if trace {
1350            let judged_on = match &effective {
1351                Ok(Some(reading)) => format!("{reading:e}"),
1352                Ok(None) => "-".to_string(),
1353                Err(message) => format!("error:{message:?}"),
1354            };
1355            match &candidate {
1356                Ok(seen) => eprintln!(
1357                    "PCURVE-TRACK call={call} candidate fraction={fraction} exit={:?} residual={:e} judged_on={judged_on} standing_dense={standing_dense} floor={floor:e} off_surface={:e} samples={} raised={} residual_meets_floor={} decision={}",
1358                    seen.exit,
1359                    seen.residual,
1360                    seen.off_surface,
1361                    samples(&state),
1362                    seen.raised,
1363                    seen.residual <= floor,
1364                    if accepted { "accept" } else { "decline-stop" }
1365                ),
1366                Err(message) => eprintln!(
1367                    "PCURVE-TRACK call={call} candidate fraction={fraction} error={message:?} decision=decline-stop"
1368                ),
1369            }
1370        }
1371        match candidate {
1372            Ok(mut candidate) if accepted => {
1373                // Report the reading it was taken on (its own, if Converged).
1374                if let Ok(Some(reading)) = effective {
1375                    candidate.residual = reading;
1376                }
1377                // From here on the standing reading is a nineteen-point one.
1378                if thread_dense {
1379                    standing_dense |= judged_dense;
1380                }
1381                adopt(state);
1382                best = candidate;
1383            }
1384            _ => break,
1385        }
1386    }
1387    best
1388}
1389
1390/// Whether [`refine_pcurve`] takes [`standoff_tracking_rungs`] for an
1391/// off-carrier first pass: true unless `BREP_PCURVE_STANDOFF_TRACKING=0`
1392/// (diagnostic A/B hatch, read once per process).
1393fn standoff_tracking_enabled() -> bool {
1394    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1395    *ON.get_or_init(|| std::env::var_os("BREP_PCURVE_STANDOFF_TRACKING").is_none_or(|value| value != "0"))
1396}
1397
1398/// The tracking rungs for an edge curve that stands OFF its carrier by more
1399/// than the floor ([`refine_pcurve`]). The first pass is read natively
1400/// ([`native_read`] on its own knot spans and endpoints); when its tracking is
1401/// within the floor, or the read fails, nothing more is done. Otherwise each
1402/// candidate pass (fractions 0.1 then 0.01 of the floor, from the same
1403/// baseline stations, taking the one-time raise when its own tracking target
1404/// is cut short by the ceiling) is read with the standing fit at the SAME
1405/// probes (both curves' native spans and endpoints) and taken only per
1406/// [`standoff_candidate_accepted`]: tracking finite, within the unchanged
1407/// floor and strictly lower, same-parameter deviation no worse, refinement
1408/// fields finite. A taken fit is REPORTED with its same-probe deviation
1409/// (inherited standoff included, so still over the floor) and with an
1410/// `off_surface` that is the larger of its station reading and the probes'
1411/// standoff. Any error, or a candidate that does not qualify, stops the loop
1412/// and keeps the standing curve and report.
1413#[allow(clippy::too_many_arguments)]
1414fn standoff_tracking_rungs(
1415    surface: &NurbsSurface,
1416    curve: &NurbsCurve,
1417    domain: [f64; 2],
1418    u_domain: [f64; 2],
1419    v_domain: [f64; 2],
1420    floor: f64,
1421    joins: &[f64],
1422    parameters: &mut Vec<f64>,
1423    raw: &mut Vec<[f64; 2]>,
1424    pcurve: &mut NurbsCurve,
1425    mut best: Refinement,
1426    trace: bool,
1427    call: u64,
1428    insert: &mut impl FnMut(f64, Vec3, Vec3) -> Result<(Option<[f64; 2]>, f64), String>,
1429) -> Result<Refinement, String> {
1430    let first = native_probe_fractions(&[&*pcurve]).and_then(|probes| native_read(surface, curve, pcurve, domain, &probes));
1431    if trace {
1432        eprintln!("PCURVE-TRACK call={call} standoff-tracking first-pass native={first:?} floor={floor:e}");
1433    }
1434    let Ok(first) = first else { return Ok(best) };
1435    if first.tracking <= floor {
1436        return Ok(best);
1437    }
1438    let baseline_parameters = parameters.clone();
1439    let baseline_raw = raw.clone();
1440    let baseline_curve = pcurve.clone();
1441    for fraction in [0.1, 0.01] {
1442        let mut refined_parameters = baseline_parameters.clone();
1443        let mut refined_raw = baseline_raw.clone();
1444        let mut refined_curve = baseline_curve.clone();
1445        let candidate = refine_pcurve_pass(surface, curve, domain, u_domain, v_domain, floor, joins,
1446            &mut refined_parameters, &mut refined_raw, &mut refined_curve, best.off_surface, best.clamped,
1447            true, fraction, true, &mut *insert);
1448        let reads = candidate.as_ref().ok().map(|_| {
1449            native_probe_fractions(&[&*pcurve, &refined_curve]).and_then(|probes| {
1450                Ok((native_read(surface, curve, pcurve, domain, &probes)?, native_read(surface, curve, &refined_curve, domain, &probes)?))
1451            })
1452        });
1453        let fields_finite = matches!(&candidate, Ok(seen) if seen.residual.is_finite() && seen.off_surface.is_finite());
1454        let decision = match &reads {
1455            Some(Ok((standing, read))) => standoff_rung_decision(*standing, *read, fields_finite, floor),
1456            _ => RungDecision::Stop,
1457        };
1458        if trace {
1459            eprintln!(
1460                "PCURVE-TRACK call={call} standoff-tracking fraction={fraction} candidate={} reads={reads:?} decision={}",
1461                match &candidate { Ok(seen) => format!("{:?}/{:e}/raised={}", seen.exit, seen.residual, seen.raised), Err(message) => format!("error:{message:?}") },
1462                match decision { RungDecision::Accept => "accept", RungDecision::Continue => "decline-continue", RungDecision::Stop => "decline-stop" }
1463            );
1464        }
1465        let (Ok(mut taken), Some(Ok((_, read))), RungDecision::Accept) = (candidate, reads, decision) else {
1466            if decision == RungDecision::Continue {
1467                continue;
1468            }
1469            break;
1470        };
1471        taken.residual = read.deviation;
1472        taken.off_surface = taken.off_surface.max(read.standoff);
1473        *parameters = refined_parameters;
1474        *raw = refined_raw;
1475        *pcurve = refined_curve;
1476        best = taken;
1477        if read.tracking <= floor * 0.01 {
1478            break;
1479        }
1480    }
1481    Ok(best)
1482}
1483
1484/// What one off-carrier tracking rung does with its candidate
1485/// ([`standoff_tracking_rungs`]).
1486#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1487enum RungDecision {
1488    /// Taken ([`standoff_candidate_accepted`]).
1489    Accept,
1490    /// Not taken, but it read finite and LOWER tracking than the standing fit
1491    /// while over the floor: the finer fraction is still tried from the same
1492    /// baseline (fixture 25 on e90c30c94: one use read 2.33e-7 at 0.1 and
1493    /// stopped while its partner was taken at 9.0e-9).
1494    Continue,
1495    /// Not taken and not improving (or unreadable): the rungs end.
1496    Stop,
1497}
1498
1499fn standoff_rung_decision(standing: NativeRead, candidate: NativeRead, fields_finite: bool, floor: f64) -> RungDecision {
1500    if standoff_candidate_accepted(standing, candidate, fields_finite, floor) {
1501        RungDecision::Accept
1502    } else if candidate.tracking.is_finite() && candidate.tracking < standing.tracking {
1503        RungDecision::Continue
1504    } else {
1505        RungDecision::Stop
1506    }
1507}
1508
1509/// The one-time raise decision of [`refine_pcurve_pass`]: a ceiling exit not
1510/// yet raised, when the returned curve is worse than the edge's own gap by
1511/// more than the floor (the original rule), or when a tracking pass for an
1512/// unmet first pass returned a curve whose own tracking still misses the
1513/// pass's target (`unmet_tracking`, read on the returned curve). Nothing else
1514/// raises.
1515fn takes_raise(exit: PcurveFitExit, raised: bool, residual: f64, off_surface: f64, floor: f64, unmet_tracking: bool) -> bool {
1516    exit == PcurveFitExit::SampleCeiling && !raised && (residual > off_surface + floor || unmet_tracking)
1517}
1518
1519fn refine_pcurve_pass(
1520    surface: &NurbsSurface,
1521    curve: &NurbsCurve,
1522    [t0, t1]: [f64; 2],
1523    [u0, u1]: [f64; 2],
1524    [v0, v1]: [f64; 2],
1525    floor: f64,
1526    joins: &[f64],
1527    parameters: &mut Vec<f64>,
1528    raw: &mut Vec<[f64; 2]>,
1529    pcurve: &mut NurbsCurve,
1530    mut off_surface: f64,
1531    mut clamped: [f64; 2],
1532    track_projection: bool,
1533    tracking_fraction: f64,
1534    raise_unmet_tracking: bool,
1535    mut insert: impl FnMut(f64, Vec3, Vec3) -> Result<(Option<[f64; 2]>, f64), String>,
1536) -> Result<Refinement, String> {
1537    let tracking_floor = if track_projection { floor * tracking_fraction } else { floor };
1538    let mut max_samples = MAX_PCURVE_SAMPLES;
1539    let mut raised = false;
1540    loop {
1541        // Every way out of this loop is CLASSIFIED (`PcurveFitExit`); a budget
1542        // exit returns the curve it built, but says so. Falling off the end
1543        // after a fourth round of inserts is the round-budget exit.
1544        let mut exit = PcurveFitExit::RoundBudget;
1545        let mut residual = None;
1546        for _ in 0..4 {
1547            if parameters.len() >= max_samples {
1548                exit = PcurveFitExit::SampleCeiling;
1549                break;
1550            }
1551            let mut inserts = Vec::new();
1552            let mut round_max = 0.0_f64;
1553            let mut truncated = false;
1554            let mut skipped_narrow = false;
1555            for index in 0..parameters.len() - 1 {
1556                if parameters.len() + inserts.len() >= max_samples {
1557                    truncated = true;
1558                    break;
1559                }
1560                let start = parameters[index];
1561                let end = parameters[index + 1];
1562                // Keep construction's existing first-pass policy. Extended
1563                // residual probes must not silently change its station ladder;
1564                // projection tracking retains its established finer floor.
1565                if end - start < if track_projection { SPAN_FLOOR } else { 1e-3 } {
1566                    skipped_narrow = true;
1567                    continue;
1568                }
1569                for local_fraction in [0.25, 0.5, 0.75] {
1570                    if parameters.len() + inserts.len() >= max_samples {
1571                        truncated = true;
1572                        break;
1573                    }
1574                    let fraction = start + (end - start) * local_fraction;
1575                    let parameter = pcurve.evaluate(fraction)?;
1576                    let on_surface =
1577                        surface.evaluate_extended(parameter.x, parameter.y)?;
1578                    let on_curve = curve.evaluate(t0 + (t1 - t0) * fraction)?;
1579                    let deviation = on_surface.sub(on_curve).length();
1580                    // A non-finite distance must not reach `f64::max` (which
1581                    // drops NaN) or the classification below, where a round
1582                    // with an unreadable station could still exit Converged on
1583                    // its readable ones. Fail closed: a first pass's error
1584                    // goes to its caller, a tracking candidate's declines it.
1585                    if !deviation.is_finite() {
1586                        return Err(format!(
1587                            "refine_pcurve_pass: non-finite station distance {deviation} at fraction {fraction}"
1588                        ));
1589                    }
1590                    round_max = round_max.max(deviation);
1591                    if deviation <= tracking_floor {
1592                        continue;
1593                    }
1594                    let (surface_parameter, gap) = insert(fraction, parameter, on_curve)?;
1595                    off_surface = off_surface.max(gap);
1596                    if let Some(surface_parameter) = surface_parameter {
1597                        // The raised pass stops where the pcurve TRACKS the
1598                        // edge curve's projection to the floor — the best any
1599                        // pcurve can do at this station. Not `deviation - gap`:
1600                        // a surface point at tangential distance d from the
1601                        // projection of a point g off the surface is only
1602                        // d^2/(2g) further from it, so the deviation test is
1603                        // quadratically blind to sideways error on off-surface
1604                        // input (6e-6 of slop at g = 1.9e-4, floor 1e-7).
1605                        if raised || track_projection {
1606                            let projected =
1607                                surface.evaluate(surface_parameter[0], surface_parameter[1])?;
1608                            if on_surface.sub(projected).length() <= tracking_floor {
1609                                continue;
1610                            }
1611                        }
1612                        inserts.push((index + 1, fraction, surface_parameter));
1613                    }
1614                }
1615            }
1616            if inserts.is_empty() {
1617                // Nothing to insert: every probed station is within the floor
1618                // (converged — unless a narrow span was skipped, in which case
1619                // the sweep below decides), or the ones over it could not be
1620                // improved (stalled — the edge curve is off the surface there).
1621                exit = if truncated {
1622                    PcurveFitExit::SampleCeiling
1623                } else if round_max > floor {
1624                    PcurveFitExit::Stalled
1625                } else if skipped_narrow {
1626                    PcurveFitExit::SpanFloor
1627                } else {
1628                    residual = Some(round_max);
1629                    PcurveFitExit::Converged
1630                };
1631                break;
1632            }
1633            for (at, fraction, surface_parameter) in inserts.into_iter().rev() {
1634                parameters.insert(at, fraction);
1635                raw.insert(at, surface_parameter);
1636            }
1637            let (rebuilt, excursion, distance) = build_interpolant_joined(surface, raw, parameters, joins)?;
1638            *pcurve = rebuilt;
1639            clamped = [excursion, distance];
1640            if truncated {
1641                exit = PcurveFitExit::SampleCeiling;
1642                break;
1643            }
1644        }
1645        let residual = match residual {
1646            Some(measured) => measured,
1647            // A budget exit never re-checked the curve it is returning, and a
1648            // converged round that skipped narrow spans never checked those:
1649            // measure every span.
1650            None => probe_residual(
1651                surface,
1652                curve,
1653                pcurve,
1654                parameters,
1655                [t0, t1],
1656                [u0, u1],
1657                [v0, v1],
1658            )?,
1659        };
1660        if exit == PcurveFitExit::SpanFloor && residual <= floor {
1661            exit = PcurveFitExit::Converged;
1662        }
1663        // The per-trim budget decision. Deterministic, grow-once, capped (I3):
1664        // the ceiling bound and the returned curve is worse than the edge
1665        // curve's own gap from the surface by more than the floor, so samples
1666        // can still buy accuracy. A curve already at its gap stops here —
1667        // reported, not retried.
1668        // A TRACKING pass for a standing fit that missed the floor (and only
1669        // then) also takes the same one-time raise when it hits the ceiling
1670        // still over the floor: its request is the projection's tracking, and
1671        // the ceiling cut it short. The raised maximum itself is unchanged.
1672        // The added raise serves a TRACKING pass for an unmet first pass whose
1673        // RETURNED curve still misses the pass's own target: its tracking (the
1674        // image against the projected foot, read natively on its own knot spans
1675        // and endpoints) over the tracking floor (floor x fraction). Reaching
1676        // the ceiling alone is exhaustion, not a miss, and the inherited
1677        // standoff never triggers it. Read only at a ceiling exit not yet
1678        // raised; an unreadable read does not raise.
1679        let unmet_tracking = track_projection
1680            && raise_unmet_tracking
1681            && exit == PcurveFitExit::SampleCeiling
1682            && !raised
1683            && native_probe_fractions(&[&*pcurve])
1684                .and_then(|probes| native_read(surface, curve, pcurve, [t0, t1], &probes))
1685                .is_ok_and(|read| read.tracking > tracking_floor);
1686        if takes_raise(exit, raised, residual, off_surface, floor, unmet_tracking) {
1687            raised = true;
1688            max_samples = PCURVE_RAISED_SAMPLES;
1689            continue;
1690        }
1691        return Ok(Refinement {
1692            exit,
1693            residual,
1694            off_surface,
1695            raised,
1696            clamped,
1697        });
1698    }
1699}
1700
1701/// The exact inverse of an affine carrier's parameterization.
1702///
1703/// An affine patch (2x2 net, degree 1 both ways, equal weights) maps `uv` to 3D
1704/// by an affine map, so the inverse — a 3D point to the `uv` of its orthogonal
1705/// projection — is affine too. An affine map commutes with the rational basis:
1706/// sending a curve's control points through it and keeping the weights
1707/// produces the EXACT image of that curve in parameter space, not a fit of it.
1708struct AffineInversion {
1709    origin: Vec3,
1710    du: Vec3,
1711    dv: Vec3,
1712    u0: f64,
1713    v0: f64,
1714    uu: f64,
1715    uv: f64,
1716    vv: f64,
1717    determinant: f64,
1718}
1719
1720impl AffineInversion {
1721    /// `None` when `surface` is not affine, or when its two directions are
1722    /// parallel enough that the inverse is not determined. The caller decides
1723    /// what that means: a refusal on the whole-curve lane, the sampled lane on
1724    /// the range lane.
1725    fn of(surface: &NurbsSurface) -> Result<Option<Self>, String> {
1726        if !surface.is_affine()? {
1727            return Ok(None);
1728        }
1729        let ([u0, _], [v0, _]) = surface_domains(surface)?;
1730        let (origin, du, dv) = surface.deriv1(u0, v0)?;
1731        let uu = du.dot(du);
1732        let uv = du.dot(dv);
1733        let vv = dv.dot(dv);
1734        let determinant = uu * vv - uv * uv;
1735        if determinant.abs() <= 1e-18 {
1736            return Ok(None);
1737        }
1738        Ok(Some(Self {
1739            origin,
1740            du,
1741            dv,
1742            u0,
1743            v0,
1744            uu,
1745            uv,
1746            vv,
1747            determinant,
1748        }))
1749    }
1750
1751    fn uv_of(&self, point: Vec3) -> [f64; 2] {
1752        let delta = point.sub(self.origin);
1753        let along_u = delta.dot(self.du);
1754        let along_v = delta.dot(self.dv);
1755        [
1756            self.u0 + (along_u * self.vv - along_v * self.uv) / self.determinant,
1757            self.v0 + (along_v * self.uu - along_u * self.uv) / self.determinant,
1758        ]
1759    }
1760
1761    /// The exact image of `curve` in this carrier's parameter space, with the
1762    /// curve's own degree, knots and weights.
1763    fn image_of(&self, curve: &NurbsCurve) -> Result<NurbsCurve, String> {
1764        let control_points = curve
1765            .control_points
1766            .iter()
1767            .map(|control| {
1768                let [u, v] = self.uv_of(control.point()?);
1769                Ok(Vec4::from_point(Vec3::new(u, v, 0.0), control.w))
1770            })
1771            .collect::<Result<Vec<_>, String>>()?;
1772        NurbsCurve::new(curve.degree, curve.knots.clone(), control_points)
1773    }
1774}
1775
1776/// Build a parameter-space curve for a 3D curve lying on a surface, and say
1777/// what the fit achieved.
1778///
1779/// This mirrors the reference imprint implementation: affine patches map
1780/// homogeneous control points exactly; general patches use sampled inversion,
1781/// periodic seam unwrapping, and adaptive 3D residual refinement. The report
1782/// beside the curve carries the residual the RETURNED curve reaches and why
1783/// refinement stopped ([`PcurveFitReport`]); the curve itself is what
1784/// [`build_pcurve_on_surface`] has always returned.
1785pub fn fit_pcurve_on_surface(
1786    surface: &NurbsSurface,
1787    curve: &NurbsCurve,
1788) -> Result<PcurveFit, String> {
1789    let [t0, t1] = curve.domain()?;
1790    if surface.is_affine()? {
1791        let Some(inversion) = AffineInversion::of(surface)? else {
1792            return Err("build_pcurve_on_surface: singular affine parameterization".into());
1793        };
1794        let pcurve = inversion.image_of(curve)?;
1795        return Ok(PcurveFit {
1796            report: PcurveFitReport {
1797                residual: 0.0,
1798                floor: PCURVE_REFINEMENT_TOLERANCE,
1799                samples: pcurve.control_points.len(),
1800                exit: PcurveFitExit::Converged,
1801                off_surface: 0.0,
1802                raised: false,
1803                clamped_excursion: 0.0,
1804                clamped_distance: 0.0,
1805            },
1806            curve: pcurve,
1807        });
1808    }
1809
1810    let scale = 1.0 + curve.evaluate((t0 + t1) / 2.0)?.length();
1811    let drop_tolerance = 1e-4 * scale;
1812    let endpoint_tolerance = 1e-3 * scale;
1813    let refinement_tolerance = PCURVE_REFINEMENT_TOLERANCE;
1814    let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
1815    let invert =
1816        |fraction: f64| invert_checked(surface, curve.evaluate(t0 + (t1 - t0) * fraction)?);
1817
1818    let mut parameters = Vec::with_capacity(25);
1819    let mut raw_surface_parameters = Vec::with_capacity(25);
1820    // The edge curve's own distance from the surface at every station inverted,
1821    // accepted or not: the floor below which no pcurve can bring the residual.
1822    let mut off_surface = 0.0_f64;
1823    // The edge's own C0 knots must remain corners of its parameter image.
1824    // Plant those stations before inversion; refinement retains their joins.
1825    let corners = range_breaks(curve, t0, t1, true);
1826    let mut stations: Vec<f64> = (0..=24).map(|index| index as f64 / 24.0).collect();
1827    // Replace nearby uniform seeds, never the native corner itself. Keep
1828    // endpoints and other native corners even when their spans are short.
1829    stations.retain(|station| {
1830        *station == 0.0 || *station == 1.0 || corners.contains(station)
1831            || !corners.iter().any(|corner| (*station - corner).abs() <= SPAN_FLOOR)
1832    });
1833    for &corner in &corners {
1834        let at = stations.partition_point(|value| *value < corner);
1835        if stations.get(at) != Some(&corner) {
1836            stations.insert(at, corner);
1837        }
1838    }
1839    let joins = corners;
1840    for (index, &fraction) in stations.iter().enumerate() {
1841        let (parameter, distance) = invert(fraction)?;
1842        off_surface = off_surface.max(distance);
1843        if distance > drop_tolerance && index != 0 && index != stations.len() - 1 {
1844            continue;
1845        }
1846        if distance > endpoint_tolerance {
1847            if std::env::var("BREP_DEBUG_PCURVE").is_ok() {
1848                let p3 = curve.evaluate(t0 + (t1 - t0) * fraction).ok();
1849                let p0 = curve.evaluate(t0).ok();
1850                let p1 = curve.evaluate(t1).ok();
1851                eprintln!(
1852                    "PCURVE-FAIL idx={index} frac={fraction} dist={distance} endpt_tol={endpoint_tolerance} scale={scale}\n  curve3D@frac={p3:?}\n  curve3D@t0={p0:?} curve3D@t1={p1:?}\n  surf_domain=u[{u0},{u1}] v[{v0},{v1}] invpar={parameter:?}\n  surf@invpar={:?}",
1853                    surface.evaluate(parameter[0], parameter[1]).ok()
1854                );
1855            }
1856            return Err(format!(
1857                "build_pcurve_on_surface: endpoint projection failed (distance={distance})"
1858            ));
1859        }
1860        parameters.push(fraction);
1861        raw_surface_parameters.push(parameter);
1862    }
1863
1864    let (mut pcurve, excursion, distance) =
1865        build_interpolant_joined(surface, &raw_surface_parameters, &parameters, &joins)?;
1866    let refinement = refine_pcurve(
1867        surface,
1868        curve,
1869        [t0, t1],
1870        [u0, u1],
1871        [v0, v1],
1872        refinement_tolerance,
1873        &joins,
1874        &mut parameters,
1875        &mut raw_surface_parameters,
1876        &mut pcurve,
1877        off_surface,
1878        [excursion, distance],
1879        |fraction, _parameter, _on_curve| {
1880            let (surface_parameter, distance) = invert(fraction)?;
1881            Ok((
1882                (distance <= drop_tolerance).then_some(surface_parameter),
1883                distance,
1884            ))
1885        },
1886    )?;
1887    Ok(PcurveFit {
1888        curve: pcurve,
1889        report: PcurveFitReport {
1890            residual: refinement.residual,
1891            floor: refinement_tolerance,
1892            samples: parameters.len(),
1893            exit: refinement.exit,
1894            off_surface: refinement.off_surface,
1895            raised: refinement.raised,
1896            clamped_excursion: refinement.clamped[0],
1897            clamped_distance: refinement.clamped[1],
1898        },
1899    })
1900}
1901
1902/// [`fit_pcurve_on_surface`] for the callers that only need the curve. The
1903/// report is not dropped: it is tallied on the open [`PcurveFitScope`], if any,
1904/// so the operation that owns this fit can say in its diagnostics whether every
1905/// trim reached the floor.
1906pub fn build_pcurve_on_surface(
1907    surface: &NurbsSurface,
1908    curve: &NurbsCurve,
1909) -> Result<NurbsCurve, String> {
1910    let fit = fit_pcurve_on_surface(surface, curve)?;
1911    record_fit(&fit.report);
1912    Ok(fit.curve)
1913}
1914
1915/// SECTION-PCURVE SEEDED MARCH (t222: cone × ABC 00000327, non-integral genus).
1916///
1917/// `build_pcurve_on_surface` inverts every sample by GLOBAL closest point. Where
1918/// the carrier surface is COMPRESSED / self-overlapping along the row the
1919/// section rides, the global search aliases interior samples onto a DISTANT
1920/// preimage sheet, folding the pcurve out past the trim boundary and re-crossing
1921/// it. The arrangement then splits the shared section at that phantom crossing,
1922/// while the mate face (whose carrier is not folded there) keeps the section
1923/// whole — so the two operands' fragments disagree and the section strands
1924/// one-use (non-integral genus). Ground truth for t222: 00000327 face 496's
1925/// v≈0.7875 row maps u∈[0.55,0.81] into a ~0.18mm neighbourhood of the section
1926/// endpoint A; the global pcurve for piece 9 folded out to u=0.806 and back,
1927/// re-crossing trim edge 349 at v≈0.784, 0.003 below the corner vertex A.
1928///
1929/// This marches the interior inversions with each seeded from the PREVIOUS
1930/// accepted parameters, so a marched sample
1931/// adopts the nearby branch instead of the momentarily-closest distant sheet.
1932/// The two ENDPOINT samples keep the deterministic global inversion (they are
1933/// the piece's shared junction vertices — same reasoning as
1934/// `repair_branch_jumps`). Additive + fail-soft: the marched chain is used ONLY
1935/// when it CLOSES onto the global far endpoint (its last interior sample is
1936/// parameter-adjacent to it, measured against the chain's own median step);
1937/// otherwise the plain global build is returned byte-for-byte. A single-preimage
1938/// carrier (the cone side of the same section) marches to the same samples the
1939/// global search already had. Use the analytic-aware seeded projector for both
1940/// the march and inserted stations: raw Newton stops at 1e-7 even on an exact
1941/// cylinder, introducing avoidable trim error. General folded carriers retain
1942/// their local seeded Newton path; analytic carriers take their closed form.
1943///
1944/// Escape hatch: `BREP_SECTION_PCURVE_MARCH=0`.
1945pub fn fit_pcurve_on_surface_marched(
1946    surface: &NurbsSurface,
1947    curve: &NurbsCurve,
1948) -> Result<PcurveFit, String> {
1949    let global = fit_pcurve_on_surface(surface, curve)?;
1950    if std::env::var("BREP_SECTION_PCURVE_MARCH").as_deref() == Ok("0") || surface.is_affine()? {
1951        return Ok(global);
1952    }
1953    let [t0, t1] = curve.domain()?;
1954    // A closed (ring) section has no distinct endpoints to anchor the march;
1955    // leave it to the existing path (closed rings are handled elsewhere).
1956    if curve.evaluate(t0)?.sub(curve.evaluate(t1)?).length() <= 1e-6 {
1957        return Ok(global);
1958    }
1959    const SAMPLES: usize = 24;
1960    let fractions: Vec<f64> = (0..=SAMPLES).map(|i| i as f64 / SAMPLES as f64).collect();
1961    let edge: Vec<Vec3> = fractions
1962        .iter()
1963        .map(|fraction| curve.evaluate(t0 + (t1 - t0) * fraction))
1964        .collect::<Result<Vec<_>, _>>()?;
1965    let scale = 1.0 + curve.evaluate((t0 + t1) / 2.0)?.length();
1966    let endpoint_tolerance = 1e-3 * scale;
1967    let (first, first_distance) = invert_checked(surface, edge[0])?;
1968    let (last, last_distance) = invert_checked(surface, edge[SAMPLES])?;
1969    if first_distance > endpoint_tolerance || last_distance > endpoint_tolerance {
1970        return Ok(global);
1971    }
1972    // The edge curve's distance from the surface at every station this chain
1973    // inverts — on the MARCHED branch, which is the one being fitted.
1974    let mut off_surface = first_distance.max(last_distance);
1975    let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
1976    let u_span = (u1 - u0).abs().max(EPSILON);
1977    let v_span = (v1 - v0).abs().max(EPSILON);
1978    let (closed_u, closed_v) = surface_closedness(surface)?;
1979    let norm_step = |a: [f64; 2], b: [f64; 2]| -> f64 {
1980        let mut du = a[0] - b[0];
1981        if closed_u {
1982            while du > 0.5 * u_span {
1983                du -= u_span;
1984            }
1985            while du < -0.5 * u_span {
1986                du += u_span;
1987            }
1988        }
1989        let mut dv = a[1] - b[1];
1990        if closed_v {
1991            while dv > 0.5 * v_span {
1992                dv -= v_span;
1993            }
1994            while dv < -0.5 * v_span {
1995                dv += v_span;
1996            }
1997        }
1998        ((du / u_span).powi(2) + (dv / v_span).powi(2)).sqrt()
1999    };
2000    // Forward seeded march over the interior samples.
2001    let mut raw = vec![first];
2002    let mut previous = first;
2003    for index in 1..SAMPLES {
2004        let seeded =
2005            crate::projection::project_point_to_surface_from_seed(surface, edge[index], previous)?;
2006        let (_, global_distance) = invert_checked(surface, edge[index])?;
2007        // The seeded footpoint must still sit on the surface — as tight as the
2008        // fit band or the global answer already had it. If it fell off (the
2009        // seed led Newton into a valley), abandon the march and keep global.
2010        if seeded.distance > 4.0 * global_distance + endpoint_tolerance {
2011            return Ok(global);
2012        }
2013        off_surface = off_surface.max(seeded.distance);
2014        previous = [seeded.u, seeded.v];
2015        raw.push(previous);
2016    }
2017    raw.push(last);
2018    // CLOSURE GATE: the last marched interior sample must be parameter-adjacent
2019    // to the global far endpoint — the march stayed on ONE branch the whole way.
2020    // Measure the endpoint gap against the chain's OWN median step so a genuine
2021    // long edge is not rejected while a sheet-hop (which leaves a big gap to the
2022    // endpoint) is.
2023    let mut steps: Vec<f64> = (1..raw.len())
2024        .map(|i| norm_step(raw[i], raw[i - 1]))
2025        .collect();
2026    let endpoint_gap = steps.pop().unwrap_or(0.0);
2027    steps.sort_by(f64::total_cmp);
2028    let median = steps
2029        .get(steps.len() / 2)
2030        .copied()
2031        .unwrap_or(0.0)
2032        .max(EPSILON);
2033    if endpoint_gap > (4.0 * median).max(0.05) {
2034        return Ok(global);
2035    }
2036    // Build the interpolant from the marched samples, then refine — re-projecting
2037    // each insert SEEDED from the interpolant (already on the marched branch), so
2038    // a mid-refinement global inversion cannot re-alias onto the far sheet.
2039    let mut parameters = fractions;
2040    let (mut pcurve, excursion, distance) = build_interpolant(surface, &raw, &parameters)?;
2041    let refinement_tolerance = PCURVE_REFINEMENT_TOLERANCE;
2042    // Same classified exits and budget ladder as `fit_pcurve_on_surface`. A
2043    // seeded insert always lands, so this loop cannot stall.
2044    let refinement = refine_pcurve(
2045        surface,
2046        curve,
2047        [t0, t1],
2048        [u0, u1],
2049        [v0, v1],
2050        refinement_tolerance,
2051        &[],
2052        &mut parameters,
2053        &mut raw,
2054        &mut pcurve,
2055        off_surface,
2056        [excursion, distance],
2057        |_fraction, seed, on_curve| {
2058            let seeded = crate::projection::project_point_to_surface_from_seed(surface, on_curve, [seed.x, seed.y])?;
2059            Ok((Some([seeded.u, seeded.v]), seeded.distance))
2060        },
2061    )?;
2062    Ok(PcurveFit {
2063        curve: pcurve,
2064        report: PcurveFitReport {
2065            residual: refinement.residual,
2066            floor: refinement_tolerance,
2067            samples: parameters.len(),
2068            exit: refinement.exit,
2069            off_surface: refinement.off_surface,
2070            raised: refinement.raised,
2071            clamped_excursion: refinement.clamped[0],
2072            clamped_distance: refinement.clamped[1],
2073        },
2074    })
2075}
2076
2077/// [`fit_pcurve_on_surface_marched`] for the callers that only need the curve;
2078/// the report is tallied on the open [`PcurveFitScope`] exactly as
2079/// [`build_pcurve_on_surface`] does.
2080pub fn build_pcurve_on_surface_marched(
2081    surface: &NurbsSurface,
2082    curve: &NurbsCurve,
2083) -> Result<NurbsCurve, String> {
2084    let fit = fit_pcurve_on_surface_marched(surface, curve)?;
2085    record_fit(&fit.report);
2086    Ok(fit.curve)
2087}
2088
2089/// Build a pcurve for a represented subrange of a larger edge curve.
2090///
2091/// On an affine carrier the answer is exact and is taken as such
2092/// ([`affine_pcurve_on_range`]). Otherwise this samples only the represented
2093/// interval, which is essential when off-interval control points do not lie on
2094/// the target carrier — the exact lane restricts the net by knot insertion
2095/// first, so those control points are removed rather than ignored.
2096pub fn fit_pcurve_on_surface_range(
2097    surface: &NurbsSurface,
2098    curve: &NurbsCurve,
2099    edge_start: f64,
2100    edge_end: f64,
2101    forward: bool,
2102    tolerance: f64,
2103) -> Result<PcurveFit, String> {
2104    if let Some(exact) =
2105        affine_pcurve_on_range(surface, curve, edge_start, edge_end, forward, tolerance)?
2106    {
2107        return Ok(exact);
2108    }
2109    fit_pcurve_on_surface_range_dense(
2110        surface, curve, edge_start, edge_end, forward, tolerance, 64, 3, 513,
2111    )
2112}
2113
2114/// [`fit_pcurve_on_surface_range`] for the callers that only need the curve:
2115/// the fit is ACCEPTED or REFUSED on its bar ([`accept_fit`]), never returned
2116/// off it.
2117pub fn build_pcurve_on_surface_range(
2118    surface: &NurbsSurface,
2119    curve: &NurbsCurve,
2120    edge_start: f64,
2121    edge_end: f64,
2122    forward: bool,
2123    tolerance: f64,
2124) -> Result<NurbsCurve, String> {
2125    accept_fit(fit_pcurve_on_surface_range(
2126        surface, curve, edge_start, edge_end, forward, tolerance,
2127    )?)
2128}
2129
2130/// `BREP_DEBUG_STATION_FIT`: trace every station fit round by round, and the
2131/// stations around the worst span of a fit off its bar. Read once per process.
2132fn station_fit_tracing() -> bool {
2133    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2134    *ON.get_or_init(|| std::env::var_os("BREP_DEBUG_STATION_FIT").is_some())
2135}
2136
2137/// Narrowest span, as a fraction of the parameter range, that
2138/// [`fit_pcurve_on_surface_stations`] subdivides. `refine_pcurve` floors at
2139/// 1e-3 from 25 base samples; this fitter starts from 64 and quarters, so a
2140/// floor of 1e-3 bound on its first pass and refused 67 of the 57-document
2141/// corpus's bodies (measured 2026-09-26). 1e-6 admits every halving a real
2142/// trim needs and stops the runaway that fits stations 4e-7 apart.
2143const SPAN_FLOOR: f64 = 1e-6;
2144
2145/// Rounds the raised pass of [`fit_pcurve_on_surface_stations`] may spend:
2146/// enough halvings for any span the sample cap can resolve.
2147const RAISED_ROUNDS: usize = 40;
2148
2149/// The refusal every station builder returns for a fit off its bar.
2150pub const PCURVE_OFF_BAR: &str = "pcurve fit refused: the trim misses its stations by";
2151
2152/// The one acceptance decision behind [`build_pcurve_on_surface_range`],
2153/// [`build_pcurve_on_surface_range_dense`] and
2154/// [`build_pcurve_on_surface_stations`]: a fit on its bar
2155/// ([`PcurveFitReport::on_bar`]) is the curve, a fit off it is a refusal that
2156/// names the residual, the bar, what was asked, how far the stations stand off
2157/// the carrier and why refinement stopped.
2158///
2159/// Until 2026-09-26 these builders returned whatever their last round built
2160/// without measuring it (the 2026-09-18 census: 4564 of 54556 imported pcurves
2161/// over the ask they were built to, 2072 of them at a station the refinement
2162/// itself had tested and given up on). Nothing here widens a band: the bar is
2163/// the caller's own ask plus what the input makes unreachable.
2164fn accept_fit(fit: PcurveFit) -> Result<NurbsCurve, String> {
2165    let report = fit.report;
2166    if report.on_bar() {
2167        return Ok(fit.curve);
2168    }
2169    Err(off_bar_message(&report))
2170}
2171
2172fn off_bar_message(report: &PcurveFitReport) -> String {
2173    format!(
2174        "{PCURVE_OFF_BAR} {:.3e} out of sample against a bar of {:.3e} (asked {:.1e}, the \
2175         stations stand {:.3e} off the carrier; {:?} at {} samples{}{})",
2176        report.residual,
2177        report.floor + report.off_surface,
2178        report.floor,
2179        report.off_surface,
2180        report.exit,
2181        report.samples,
2182        if report.raised { ", after the raise" } else { "" },
2183        if report.clamped_excursion > 0.0 {
2184            format!(
2185                "; clamped into the chart by {:.3e} in parameter, {:.3e} in 3D",
2186                report.clamped_excursion, report.clamped_distance
2187            )
2188        } else {
2189            String::new()
2190        },
2191    )
2192}
2193
2194/// The class slug of a pcurve fit refused off its bar
2195/// ([`crate::RefusalClass::NonConvergence`]): minted by
2196/// [`build_pcurve_on_surface_range_classed`] and read by
2197/// [`is_pcurve_off_bar`], so a dispatcher decides on the class, never on the
2198/// message text.
2199pub(crate) const PCURVE_OFF_BAR_WHAT: &str = "pcurve_fit_off_bar";
2200
2201/// Whether `refusal` is a pcurve fit that could not reach its bar.
2202pub(crate) fn is_pcurve_off_bar(refusal: &crate::KernelRefusal) -> bool {
2203    matches!(&refusal.class, crate::RefusalClass::NonConvergence { what } if what == PCURVE_OFF_BAR_WHAT)
2204}
2205
2206/// [`build_pcurve_on_surface_range`] with a typed refusal: a fit off its bar
2207/// is `NonConvergence { what: PCURVE_OFF_BAR_WHAT }` at `stage`, with the same
2208/// message; any other failure of the fit is `Internal` under `what`.
2209pub(crate) fn build_pcurve_on_surface_range_classed(
2210    surface: &NurbsSurface,
2211    curve: &NurbsCurve,
2212    edge_start: f64,
2213    edge_end: f64,
2214    forward: bool,
2215    tolerance: f64,
2216    stage: KernelStage,
2217    what: &'static str,
2218) -> Result<NurbsCurve, crate::KernelRefusal> {
2219    use crate::OrRefuse;
2220    let fit = fit_pcurve_on_surface_range(surface, curve, edge_start, edge_end, forward, tolerance)
2221        .or_refuse(stage, what)?;
2222    accept_fit_classed(fit, stage)
2223}
2224
2225/// [`accept_fit`] with the refusal typed: `NonConvergence { what:
2226/// PCURVE_OFF_BAR_WHAT }` at `stage`, the message unchanged.
2227pub(crate) fn accept_fit_classed(fit: PcurveFit, stage: KernelStage) -> Result<NurbsCurve, crate::KernelRefusal> {
2228    if fit.report.on_bar() {
2229        return Ok(fit.curve);
2230    }
2231    Err(crate::KernelRefusal {
2232        class: crate::RefusalClass::NonConvergence { what: PCURVE_OFF_BAR_WHAT.into() },
2233        stage,
2234        message: off_bar_message(&fit.report),
2235    })
2236}
2237
2238/// The EXACT pcurve for a coedge's subrange of `curve` on an affine carrier,
2239/// or `None` when there is no such answer and the sampled lane must run.
2240///
2241/// [`fit_pcurve_on_surface`] has always mapped a WHOLE curve's control net
2242/// exactly on an affine carrier. A coedge's trim is that same curve restricted
2243/// to `[edge_start, edge_end]` and oriented by `forward`, and both operations
2244/// are exact on a NURBS: [`NurbsCurve::split`] subdivides by knot insertion,
2245/// [`NurbsCurve::reversed`] mirrors the net, and rescaling the knots to
2246/// `[0, 1]` is affine in the edge's own parameter — which is precisely the
2247/// fraction convention the sampled lane uses, so the pcurve's correspondence
2248/// with the edge is the same one either lane produces.
2249///
2250/// Sampling where an exact answer exists leaves a real error: measured
2251/// 2026-09-17, a booleaned band rim's two planar trims sat 2.379e-4 and
2252/// 2.395e-4 off their edges, where this lane lands 1.4e-13 off.
2253///
2254/// The image is verified against the caller's own `tolerance` before it is
2255/// returned. The exact image of a curve that does NOT lie on this carrier is
2256/// its orthogonal projection, which is not that coedge's trim, so every such
2257/// case falls through to the sampled lane and keeps the refusals that lane
2258/// makes today.
2259fn affine_pcurve_on_range(
2260    surface: &NurbsSurface,
2261    curve: &NurbsCurve,
2262    edge_start: f64,
2263    edge_end: f64,
2264    forward: bool,
2265    tolerance: f64,
2266) -> Result<Option<PcurveFit>, String> {
2267    // Opt-out for A/B bisection of this lane against the sampled one, in the
2268    // shape of the two hatches beside it (`BREP_NO_PCURVE_REPAIR`,
2269    // `BREP_SECTION_PCURVE_MARCH`). It is what showed that the boolean's
2270    // planar residuals do NOT come through here: the same binary with this set
2271    // and unset read identically to every digit on 21_band_rim.
2272    if std::env::var("BREP_AFFINE_RANGE_PCURVE").as_deref() == Ok("0") {
2273        return Ok(None);
2274    }
2275    let Some(inversion) = AffineInversion::of(surface)? else {
2276        return Ok(None);
2277    };
2278    if !(edge_start.is_finite() && edge_end.is_finite() && edge_start < edge_end) {
2279        // The sampled lane owns this refusal; it is not this lane's to reword.
2280        return Ok(None);
2281    }
2282    let [t0, t1] = curve.domain()?;
2283    let start = edge_start.max(t0);
2284    let end = edge_end.min(t1);
2285    // A range narrower than the knot identity tolerance cannot be split onto
2286    // its own endpoints at all.
2287    if end - start <= 2.0 * KNOT_IDENTITY_TOL {
2288        return Ok(None);
2289    }
2290    let mut restricted = curve.clone();
2291    if end < t1 - KNOT_IDENTITY_TOL {
2292        restricted = restricted.split(end)?.0;
2293    }
2294    if start > t0 + KNOT_IDENTITY_TOL {
2295        restricted = restricted.split(start)?.1;
2296    }
2297    let mut pcurve = inversion.image_of(&restricted)?;
2298    if !forward {
2299        pcurve = pcurve.reversed()?;
2300    }
2301    let first = pcurve.knots[0];
2302    let last = pcurve.knots[pcurve.knots.len() - 1];
2303    if !(last - first > 0.0) {
2304        return Ok(None);
2305    }
2306    let knots = pcurve
2307        .knots
2308        .iter()
2309        .map(|knot| (knot - first) / (last - first))
2310        .collect::<Vec<_>>();
2311    let pcurve = NurbsCurve::new(pcurve.degree, knots, pcurve.control_points.clone())?;
2312
2313    // Verify against the range the CALLER asked for, not the one the
2314    // restriction produced: `split` snaps onto a nearby knot, so a snap that
2315    // moved the correspondence reads here as a deviation. Sampling at
2316    // fractions that are not the stations any lane interpolates through keeps
2317    // this a check on the whole trim rather than on its endpoints.
2318    let scale = 1.0 + curve.evaluate((start + end) / 2.0)?.length();
2319    let bar = tolerance.max(1e-11 * scale);
2320    let mut residual = 0.0_f64;
2321    for index in 0..=32 {
2322        let fraction = index as f64 / 32.0;
2323        let uv = pcurve.evaluate(fraction)?;
2324        // `evaluate` CLAMPS out-of-domain uv. A planar carrier's domain is a
2325        // bounding rectangle that a booleaned trim can leave, and an affine
2326        // patch's extension is the same plane, so read it unclamped.
2327        let on_carrier = surface.evaluate_extended(uv.x, uv.y)?;
2328        let edge_fraction = if forward { fraction } else { 1.0 - fraction };
2329        let on_edge = curve.evaluate(start + (end - start) * edge_fraction)?;
2330        residual = residual.max(on_carrier.sub(on_edge).length());
2331        if residual > bar {
2332            return Ok(None);
2333        }
2334    }
2335    let samples = pcurve.control_points.len();
2336    Ok(Some(PcurveFit {
2337        curve: pcurve,
2338        report: PcurveFitReport {
2339            residual,
2340            // The floor this lane VERIFIED against: the caller's ask, or the
2341            // floating-point floor of an exact map on a part this size. An
2342            // exact image is on its bar by construction; a caller asking for
2343            // less than the map's own precision is not refused for it.
2344            floor: bar,
2345            samples,
2346            exit: PcurveFitExit::Converged,
2347            off_surface: 0.0,
2348            raised: false,
2349            clamped_excursion: 0.0,
2350            clamped_distance: 0.0,
2351        },
2352    }))
2353}
2354
2355fn align_collapsed_endpoints(surface: &NurbsSurface, raw: &mut [[f64; 2]]) -> Result<(), String> {
2356    let anchor = surface.control_points[0][0].point()?;
2357    let mut extent = 0.0_f64;
2358    for control in surface.control_points.iter().flatten() {
2359        extent = extent.max(control.point()?.sub(anchor).length());
2360    }
2361    // This is a geometric identity check, independent of the fitting allowance
2362    // and of world position. Positive rational weights keep the whole iso-curve
2363    // inside the convex hull of its Euclidean controls. Cap the size coupling
2364    // at the linear tolerance so a large patch cannot erase a thin finite row.
2365    let pole_band = (1e-10 * (1.0 + extent)).min(LINEAR_TOLERANCE);
2366    for (endpoint, neighbor) in [(0, 1), (raw.len() - 1, raw.len() - 2)] {
2367        let point = surface.evaluate(raw[endpoint][0], raw[endpoint][1])?;
2368        for axis in 0..2 {
2369            let mut candidate = raw[endpoint];
2370            candidate[axis] = raw[neighbor][axis];
2371            if candidate == raw[endpoint]
2372                || surface
2373                    .evaluate(candidate[0], candidate[1])?
2374                    .sub(point)
2375                    .length()
2376                    > pole_band
2377            {
2378                continue;
2379            }
2380            let row = if axis == 0 {
2381                surface.iso_curve_v(raw[endpoint][1])?
2382            } else {
2383                surface.iso_curve_u(raw[endpoint][0])?
2384            };
2385            let mut collapsed = true;
2386            for control in &row.control_points {
2387                if control.point()?.sub(point).length() > pole_band {
2388                    collapsed = false;
2389                    break;
2390                }
2391            }
2392            if collapsed {
2393                raw[endpoint] = candidate;
2394            }
2395        }
2396    }
2397    Ok(())
2398}
2399
2400/// Re-seat any raw inversion sample that BRANCH-JUMPED — snapped to a distant
2401/// fold of a self-overlapping general carrier — back onto the branch traced by
2402/// its neighbours.
2403///
2404/// Each entry in `raw` is an INDEPENDENT global closest-point inversion of the
2405/// corresponding 3D edge sample. Every one is geometrically valid (on the
2406/// surface, at the edge), but they need not be CONTIGUOUS: where a rational
2407/// B-spline surface folds back over the small trimmed patch it carries, the
2408/// momentarily-closest fold flips from one sample to the next, so the raw
2409/// polygon zig-zags across the whole domain and self-crosses — and the region
2410/// its interpolant bounds no longer covers the true face (its surface flux can
2411/// exceed its own area). We anchor on the LONGEST run of mutually-continuous
2412/// samples — the fold the trimmed patch actually lies on, since a small face
2413/// lives on ONE fold and the jumped samples are the minority the global search
2414/// snapped elsewhere — then walk outward in both directions, re-seeding Newton
2415/// from the last good parameters and adopting the continuous footpoint whenever
2416/// it is geometrically as valid as the global one. Anchoring on consensus (not
2417/// blindly on sample 0, which can itself be an outlier that would drag the
2418/// whole edge onto the wrong branch and tear the loop open at its endpoint)
2419/// leaves continuous nonsingular fits unchanged. Certified collapsed endpoint
2420/// rows first adopt their neighbour's branch; a bad seed in the subsequent
2421/// jump repair cannot make a sample worse than the global search already had it.
2422fn repair_branch_jumps(
2423    surface: &NurbsSurface,
2424    edge_points: &[Vec3],
2425    raw: &mut [[f64; 2]],
2426    tolerance: f64,
2427) -> Result<(), String> {
2428    if raw.len() < 3 {
2429        return Ok(());
2430    }
2431    // Affine carriers invert linearly and exactly — no folds, nothing to chase.
2432    // Checked first so the common planar/affine face never touches the env.
2433    if surface.is_affine()? {
2434        return Ok(());
2435    }
2436    // Opt-out for A/B bisection of a STEP-import regression against this repair.
2437    if std::env::var("BREP_NO_PCURVE_REPAIR").is_ok() {
2438        return Ok(());
2439    }
2440    // A pole has no unique coordinate along its collapsed row. Continuing the
2441    // adjacent sample's branch is safe only when the ENTIRE row is collapsed;
2442    // coincident points across an ordinary seam or fold are not enough.
2443    align_collapsed_endpoints(surface, raw)?;
2444    let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
2445    let u_span = (u1 - u0).abs().max(EPSILON);
2446    let v_span = (v1 - v0).abs().max(EPSILON);
2447    let (closed_u, closed_v) = surface_closedness(surface)?;
2448    // Normalised, seam-aware step between two parameter samples. Wrapping the
2449    // closed directions keeps a legitimate seam crossing SMALL, so the seam /
2450    // periodic-unwrap machinery elsewhere is never disturbed by this repair.
2451    let norm_step = |a: [f64; 2], b: [f64; 2]| -> f64 {
2452        let mut du = a[0] - b[0];
2453        if closed_u {
2454            while du > 0.5 * u_span {
2455                du -= u_span;
2456            }
2457            while du < -0.5 * u_span {
2458                du += u_span;
2459            }
2460        }
2461        let mut dv = a[1] - b[1];
2462        if closed_v {
2463            while dv > 0.5 * v_span {
2464                dv -= v_span;
2465            }
2466            while dv < -0.5 * v_span {
2467                dv += v_span;
2468            }
2469        }
2470        ((du / u_span).powi(2) + (dv / v_span).powi(2)).sqrt()
2471    };
2472    // A jump crosses a large fraction of the WHOLE domain — orders above the
2473    // per-sample motion of any real (even domain-spanning) edge, which advances
2474    // ~1/N of its traversal between consecutive stations.
2475    const JUMP_THRESHOLD: f64 = 0.2;
2476    let count = raw.len();
2477
2478    // Locate the longest maximal run of consecutive continuous samples — the
2479    // branch to anchor on. If nothing jumps, this spans [0, count-1] and both
2480    // re-seat passes below are empty, leaving `raw` untouched.
2481    let (mut best_start, mut best_len, mut run_start) = (0usize, 1usize, 0usize);
2482    for index in 1..count {
2483        if norm_step(raw[index], raw[index - 1]) > JUMP_THRESHOLD {
2484            if index - run_start > best_len {
2485                best_len = index - run_start;
2486                best_start = run_start;
2487            }
2488            run_start = index;
2489        }
2490    }
2491    if count - run_start > best_len {
2492        best_len = count - run_start;
2493        best_start = run_start;
2494    }
2495    let (spine_lo, spine_hi) = (best_start, best_start + best_len - 1);
2496
2497    // Adopt the continuous footpoint only when it (a) still sits on the surface
2498    // — as tight as the fit band or the global answer — and (b) genuinely
2499    // closes the jump rather than trading it for another. Returns the parameter
2500    // to carry forward as the next seed (the repaired one, or the untouched
2501    // global when no repair applies).
2502    let reseat = |edge_point: Vec3,
2503                  previous: [f64; 2],
2504                  global: [f64; 2]|
2505     -> Result<[f64; 2], String> {
2506        let global_step = norm_step(global, previous);
2507        if global_step <= JUMP_THRESHOLD {
2508            return Ok(global);
2509        }
2510        let seeded =
2511            project_point_to_surface_seeded(surface, edge_point, previous[0], previous[1])?;
2512        let candidate = [seeded.u, seeded.v];
2513        let global_residual = surface
2514            .evaluate(global[0], global[1])?
2515            .sub(edge_point)
2516            .length();
2517        let on_surface = seeded.distance <= 4.0 * global_residual + tolerance.max(1e-12);
2518        if on_surface && norm_step(candidate, previous) < 0.5 * global_step {
2519            if std::env::var("BREP_DEBUG_PCURVE").is_ok() {
2520                eprintln!(
2521                    "pcurve repair: jump {global_step:.4} ({global:?}) -> {:.4} ({candidate:?}) res {:.2e}->{:.2e}",
2522                    norm_step(candidate, previous),
2523                    global_residual,
2524                    seeded.distance
2525                );
2526            }
2527            Ok(candidate)
2528        } else {
2529            Ok(global)
2530        }
2531    };
2532
2533    // Walk forward off the spine's high end, then backward off its low end,
2534    // chaining each repaired sample as the next seed so continuity propagates.
2535    //
2536    // Apart from the certified collapsed rows above, the two ENDPOINT samples
2537    // (fraction 0 and 1) are NEVER moved: they are the
2538    // edge's shared loop vertices. The global inversion is deterministic, so the
2539    // two coedges meeting at a vertex land it at the SAME parameters even when
2540    // that vertex sits on a fold reachable from two branches — moving one side
2541    // to a different branch tears the loop open there (`synthesize_pole_edge`
2542    // then rejects the non-collapsed gap). A vertex's genuine fold transition
2543    // (an edge whose interior rides u≈0.08 but whose endpoint must meet its
2544    // neighbour at u≈0.95) is exactly this case and must be preserved, not
2545    // "continuity-repaired" back onto the interior branch.
2546    let mut previous = raw[spine_hi];
2547    for index in (spine_hi + 1)..count.saturating_sub(1) {
2548        raw[index] = reseat(edge_points[index], previous, raw[index])?;
2549        previous = raw[index];
2550    }
2551    let mut previous = raw[spine_lo];
2552    for index in (1..spine_lo).rev() {
2553        raw[index] = reseat(edge_points[index], previous, raw[index])?;
2554        previous = raw[index];
2555    }
2556    Ok(())
2557}
2558
2559/// Range fitter with explicit sampling knobs, reporting what the fit achieved.
2560/// [`fit_pcurve_on_surface_range`] keeps the long-standing (base 64, 3
2561/// refinement rounds, 513 cap) budget; a fit that binds at that cap with the
2562/// bar unmet grows once to [`PCURVE_RAISED_SAMPLES`]
2563/// ([`fit_pcurve_on_surface_stations`]).
2564///
2565/// NOTE (2026-09-03): this comment used to say "STEP import retries failed fits
2566/// with a denser budget". No caller passes these knobs any more — that retry
2567/// was removed and the sentence outlived it. The knobs are kept because they
2568/// are the honest way to ASK whether a fit had headroom left, which is what
2569/// `examples/pcurve_residual_probe.rs` uses them for: on every ABC corpus
2570/// coedge that failed `validate`'s pcurve check, budgets up to (512, 8, 8193)
2571/// with a 1000x tighter target reproduced the coarse fit's deviation to six
2572/// decimals, because that deviation is the edge-vs-surface closest-point
2573/// residual and a curve ON the surface cannot beat it.
2574#[allow(clippy::too_many_arguments)]
2575pub fn fit_pcurve_on_surface_range_dense(
2576    surface: &NurbsSurface,
2577    curve: &NurbsCurve,
2578    edge_start: f64,
2579    edge_end: f64,
2580    forward: bool,
2581    tolerance: f64,
2582    base_samples: usize,
2583    refinement_rounds: usize,
2584    parameter_cap: usize,
2585) -> Result<PcurveFit, String> {
2586    if !(edge_start.is_finite() && edge_end.is_finite() && edge_start < edge_end) {
2587        return Err("build_pcurve_on_surface_range: invalid edge interval".into());
2588    }
2589    let evaluate_edge = |fraction: f64| {
2590        let edge_fraction = if forward { fraction } else { 1.0 - fraction };
2591        curve.evaluate(edge_start + (edge_end - edge_start) * edge_fraction)
2592    };
2593    let breaks = range_breaks(curve, edge_start, edge_end, forward);
2594    let ask = tolerance.max(PCURVE_REFINEMENT_TOLERANCE);
2595    let fit = fit_station_pcurve(
2596        surface,
2597        &evaluate_edge,
2598        &breaks,
2599        &breaks,
2600        ask,
2601        base_samples,
2602        refinement_rounds,
2603        parameter_cap,
2604        SPAN_FLOOR,
2605    )?;
2606    end_span_challenger(surface, curve, &evaluate_edge, edge_start, edge_end, forward, &breaks, ask, base_samples, refinement_rounds, parameter_cap, fit)
2607}
2608
2609/// The edge curve's OWN distinct interior knots inside `[edge_start,
2610/// edge_end]`, mapped through the trim's range and sense to coedge
2611/// fractions, that fall inside `pcurve`'s FIRST or LAST span (between the
2612/// domain end and its nearest interior knot), sorted.
2613fn native_end_knot_fractions(curve: &NurbsCurve, edge_start: f64, edge_end: f64, forward: bool, pcurve: &NurbsCurve) -> Result<Vec<f64>, String> {
2614    let [q0, q1] = pcurve.domain()?;
2615    let mut interior: Vec<f64> = pcurve.knots.iter().copied().filter(|k| *k > q0 && *k < q1).collect();
2616    interior.dedup();
2617    let (Some(&first), Some(&last)) = (interior.first(), interior.last()) else { return Ok(Vec::new()) };
2618    let (low, high) = (edge_start.min(edge_end), edge_start.max(edge_end));
2619    let span = edge_end - edge_start;
2620    let mut fractions: Vec<f64> = curve
2621        .knots
2622        .iter()
2623        .copied()
2624        .filter(|k| *k > low && *k < high)
2625        .map(|k| {
2626            let along = (k - edge_start) / span;
2627            if forward { along } else { 1.0 - along }
2628        })
2629        .filter(|f| f.is_finite() && ((*f > q0 && *f < first) || (*f > last && *f < q1)))
2630        .collect();
2631    fractions.sort_by(f64::total_cmp);
2632    fractions.dedup();
2633    Ok(fractions)
2634}
2635
2636/// The admission rule for a foot read against an edge point: finite distance,
2637/// finite point, finite (u, v) inside the native domain. Shared by
2638/// [`common_station_read`] and the station fitter's tracking mode.
2639fn foot_admitted(foot: &crate::SurfaceProjection, [u0, u1]: [f64; 2], [v0, v1]: [f64; 2]) -> bool {
2640    let finite = |p: Vec3| p.x.is_finite() && p.y.is_finite() && p.z.is_finite();
2641    foot.distance.is_finite() && finite(foot.point) && foot.u.is_finite() && foot.v.is_finite() && (u0..=u1).contains(&foot.u) && (v0..=v1).contains(&foot.v)
2642}
2643
2644/// The tracking mode's verification probes: the current pcurve's native probes
2645/// ([`native_probe_fractions`]: endpoints, quarters and 16 interior points of
2646/// each knot span), its EXACT knots (the final common read includes them, and
2647/// the native probes do not), and the caller's fixed fractions; finite, in
2648/// [0, 1], sorted, deduplicated.
2649fn verification_fractions(pcurve: &NurbsCurve, verify: &[f64]) -> Result<Vec<f64>, String> {
2650    let mut fractions = native_probe_fractions(&[pcurve])?;
2651    fractions.extend(pcurve.knots.iter().copied());
2652    fractions.extend(verify.iter().copied());
2653    fractions.retain(|f| f.is_finite() && (0.0..=1.0).contains(f));
2654    fractions.sort_by(f64::total_cmp);
2655    fractions.dedup();
2656    Ok(fractions)
2657}
2658
2659/// The off-carrier candidate admission, at the same probes and shared feet
2660/// (`[tracking, deviation, excess]` per curve, [`common_station_read_with_excess`]):
2661/// every reading finite, the candidate's tracking strictly lower, and EITHER
2662/// its maximum deviation no worse than the legacy's (the original rule) OR the
2663/// candidate meeting the ORIGINAL floors outright, tracking and excess both
2664/// within `PCURVE_REFINEMENT_TOLERANCE` (1e-7).
2665///
2666/// Why the second branch: for fits whose images follow the shared admitted
2667/// feet, the maximum deviation can be dominated by the edge's standoff.
2668/// Their measured maxima can then differ by rounding while tracking improves.
2669/// This does not assert that the admitted foot is a certified global nearest
2670/// point or that an arbitrary pcurve has the same maximum deviation.
2671/// next5 fixture 25 (BREP_DEBUG_STATION_FIT): every tracking candidate reached
2672/// the floor (9.52e-8 .. 9.94e-8 tracking) and was declined on a deviation
2673/// 1.2e-14 .. 6.7e-13 above the legacy's at 2.1e-5 .. 9.0e-4 standoffs, while
2674/// the legacy kept 5.9e-7 .. 8.5e-5 tracking. A candidate within the original
2675/// floors on both readings is what those controls require; it is not a
2676/// standoff allowance and no bar moves.
2677pub(crate) fn floor_admits(old: [f64; 3], new: [f64; 3]) -> bool {
2678    let floor = PCURVE_REFINEMENT_TOLERANCE;
2679    old.iter().chain(new.iter()).all(|v| v.is_finite())
2680        && new[0] < old[0]
2681        && (new[1] <= old[1] || (new[0] <= floor && new[2] <= floor))
2682}
2683
2684/// The common reader's foot: the NEAREST admitted ([`foot_admitted`]) of the
2685/// global projection, the projection seeded at the reference (legacy) uv and
2686/// the one seeded at the candidate (image) uv, in THAT order, so that between
2687/// admitted feet at equal distance the first in this order is taken
2688/// (`min_by` keeps the first minimum). [`common_station_read`] and the station
2689/// fitter's tracking mode both take their foot here, so an equal-distance tie
2690/// resolves to the same foot in both.
2691fn common_foot(
2692    global: Option<crate::SurfaceProjection>,
2693    reference: Option<crate::SurfaceProjection>,
2694    candidate: Option<crate::SurfaceProjection>,
2695    domain_u: [f64; 2],
2696    domain_v: [f64; 2],
2697) -> Option<crate::SurfaceProjection> {
2698    [global, reference, candidate]
2699        .into_iter()
2700        .flatten()
2701        .filter(|f| foot_admitted(f, domain_u, domain_v))
2702        .min_by(|a, b| a.distance.total_cmp(&b.distance))
2703}
2704
2705/// The probes a RANGE trim is read at by the common reader: `curves`'
2706/// native spans (endpoints, quarters and 16 interior points of each knot
2707/// span) and exact knots, plus the edge curve's own knots CLIPPED to
2708/// `[t0, t1]` (with the range ends) and the same 19 points on every clipped
2709/// span, mapped through the coedge's sense to fractions; finite, in [0, 1],
2710/// sorted, deduplicated.
2711pub(crate) fn range_use_probes(curves: &[&NurbsCurve], edge_curve: &NurbsCurve, t0: f64, t1: f64, forward: bool) -> Result<Vec<f64>, String> {
2712    let mut probes = native_probe_fractions(curves)?;
2713    for curve in curves {
2714        probes.extend(curve.knots.iter().copied());
2715    }
2716    let span = t1 - t0;
2717    if span.is_finite() && span != 0.0 {
2718        let (low, high) = (t0.min(t1), t0.max(t1));
2719        let mut knots: Vec<f64> = edge_curve.knots.iter().copied().filter(|k| *k >= low && *k <= high).collect();
2720        knots.push(low);
2721        knots.push(high);
2722        knots.sort_by(f64::total_cmp);
2723        knots.dedup();
2724        let locals: Vec<f64> = [0.25, 0.5, 0.75]
2725            .into_iter()
2726            .chain((1..=TRACKING_ACCEPTANCE_POINTS).map(|index| index as f64 / (TRACKING_ACCEPTANCE_POINTS + 1) as f64))
2727            .collect();
2728        let mut ts = knots.clone();
2729        for pair in knots.windows(2).filter(|w| w[1] > w[0]) {
2730            for &local in &locals {
2731                ts.push(pair[0] + (pair[1] - pair[0]) * local);
2732            }
2733        }
2734        for t in ts {
2735            let along = (t - t0) / span;
2736            probes.push(if forward { along } else { 1.0 - along });
2737        }
2738    }
2739    probes.retain(|f| f.is_finite() && (0.0..=1.0).contains(f));
2740    probes.sort_by(f64::total_cmp);
2741    probes.dedup();
2742    Ok(probes)
2743}
2744
2745/// [`common_station_read`] for two RANGE trims of one coedge (`old`, `new`,
2746/// both on the coedge fraction domain [0, 1]) against the edge curve over
2747/// `[t0, t1]` in the coedge's sense, at [`range_use_probes`] of both: the same
2748/// probes and one shared admitted foot per probe, finite or an error. A trim
2749/// not on [0, 1] is an error.
2750// Unused by the import lane since the tracking trigger and keep were withdrawn
2751// for this merge; kept, with its controls, for their re-proposal.
2752#[cfg_attr(not(test), allow(dead_code))]
2753pub(crate) fn range_common_read(
2754    surface: &NurbsSurface,
2755    edge_curve: &NurbsCurve,
2756    t0: f64,
2757    t1: f64,
2758    forward: bool,
2759    old: &NurbsCurve,
2760    new: &NurbsCurve,
2761) -> Result<((f64, f64), (f64, f64)), String> {
2762    let (o, n) = range_common_read_with_excess(surface, edge_curve, t0, t1, forward, old, new)?;
2763    Ok(((o[0], o[1]), (n[0], n[1])))
2764}
2765
2766/// [`range_common_read`] with each trim's maximum pointwise excess over the
2767/// shared admitted foot's own distance: `[tracking, deviation, excess]` per
2768/// trim ([`common_station_read_with_excess`]).
2769// Unused by the import lane since the tracking trigger and keep were withdrawn
2770// for this merge; kept, with its controls, for their re-proposal.
2771#[cfg_attr(not(test), allow(dead_code))]
2772pub(crate) fn range_common_read_with_excess(
2773    surface: &NurbsSurface,
2774    edge_curve: &NurbsCurve,
2775    t0: f64,
2776    t1: f64,
2777    forward: bool,
2778    old: &NurbsCurve,
2779    new: &NurbsCurve,
2780) -> Result<([f64; 3], [f64; 3]), String> {
2781    for curve in [old, new] {
2782        if !matches!(curve.domain(), Ok([a, b]) if a == 0.0 && b == 1.0) {
2783            return Err("range common read: a trim not on [0, 1]".into());
2784        }
2785    }
2786    let probes = range_use_probes(&[old, new], edge_curve, t0, t1, forward)?;
2787    let evaluate = |fraction: f64| -> Result<Vec3, String> {
2788        let along = if forward { fraction } else { 1.0 - fraction };
2789        edge_curve.evaluate(t0 + (t1 - t0) * along)
2790    };
2791    common_station_read_with_excess(surface, &evaluate, old, new, &probes)
2792}
2793
2794/// The legacy and candidate pcurves read against the edge at the SAME probes
2795/// and against the SAME foot per probe: at each fraction, the edge point's
2796/// foot is the NEAREST admitted one among the global projection and the
2797/// projections seeded at the legacy and the candidate image's (u, v). A foot
2798/// is admitted with a finite distance and point and a finite (u, v) inside the
2799/// native domain. Both images are then measured against that one shared foot
2800/// (TRACKING) and against the edge point (same-parameter DEVIATION). The
2801/// shared foot is the nearest of these three, not a certified global nearest.
2802/// Any non-finite image, point or distance, or a probe with no admitted foot,
2803/// is an error (the caller keeps the legacy fit). Returns
2804/// ((legacy tracking, legacy deviation), (candidate tracking, candidate
2805/// deviation)), maxima over the probes.
2806pub(crate) fn common_station_read(
2807    surface: &NurbsSurface,
2808    evaluate_edge: &dyn Fn(f64) -> Result<Vec3, String>,
2809    legacy: &NurbsCurve,
2810    candidate: &NurbsCurve,
2811    probes: &[f64],
2812) -> Result<((f64, f64), (f64, f64)), String> {
2813    let (old, new) = common_station_read_with_excess(surface, evaluate_edge, legacy, candidate, probes)?;
2814    Ok(((old[0], old[1]), (new[0], new[1])))
2815}
2816
2817/// [`common_station_read`] that also returns, per curve, the maximum
2818/// pointwise EXCESS: the same-parameter deviation minus the shared admitted
2819/// foot's own distance at that probe (the part over the edge's standoff
2820/// there, which the every-use controls bar at the original 1e-7). Returns
2821/// `[tracking, deviation, excess]` for the legacy and the candidate.
2822pub(crate) fn common_station_read_with_excess(
2823    surface: &NurbsSurface,
2824    evaluate_edge: &dyn Fn(f64) -> Result<Vec3, String>,
2825    legacy: &NurbsCurve,
2826    candidate: &NurbsCurve,
2827    probes: &[f64],
2828) -> Result<([f64; 3], [f64; 3]), String> {
2829    let (domain_u, domain_v) = surface_domains(surface)?;
2830    let finite = |p: Vec3| p.x.is_finite() && p.y.is_finite() && p.z.is_finite();
2831    // [tracking, deviation, excess] per curve; the excess (deviation minus
2832    // the shared foot's distance, per probe) starts at -inf, so a read with no
2833    // probe is not finite and admits nothing.
2834    let mut read = ([0.0_f64, 0.0, f64::NEG_INFINITY], [0.0_f64, 0.0, f64::NEG_INFINITY]);
2835    for &fraction in probes {
2836        let point = evaluate_edge(fraction)?;
2837        let (old_uv, new_uv) = (legacy.evaluate(fraction)?, candidate.evaluate(fraction)?);
2838        let (old_image, new_image) = (surface.evaluate_extended(old_uv.x, old_uv.y)?, surface.evaluate_extended(new_uv.x, new_uv.y)?);
2839        if !(finite(point) && finite(old_image) && finite(new_image)) {
2840            return Err(format!("common station read: non-finite point at fraction {fraction}"));
2841        }
2842        // Reading ONE curve against itself (`legacy` and `candidate` the same
2843        // object, as a trigger's read is): the candidate-seeded projection is
2844        // the reference-seeded one on identical inputs, so it is computed once
2845        // (same result, same tie order).
2846        let reference_foot = memo_seeded_foot(surface, point, old_uv.x, old_uv.y);
2847        let candidate_foot = if std::ptr::eq(legacy, candidate) {
2848            reference_foot
2849        } else {
2850            memo_seeded_foot(surface, point, new_uv.x, new_uv.y)
2851        };
2852        let foot = common_foot(
2853            memo_global_foot(surface, point),
2854            reference_foot,
2855            candidate_foot,
2856            domain_u,
2857            domain_v,
2858        )
2859        .ok_or_else(|| format!("common station read: no admitted foot at fraction {fraction}"))?;
2860        let values = [
2861            old_image.sub(foot.point).length(),
2862            old_image.sub(point).length(),
2863            new_image.sub(foot.point).length(),
2864            new_image.sub(point).length(),
2865        ];
2866        if values.iter().any(|v| !v.is_finite()) {
2867            return Err(format!("common station read: non-finite distance at fraction {fraction}"));
2868        }
2869        if !foot.distance.is_finite() {
2870            return Err(format!("common station read: non-finite foot distance at fraction {fraction}"));
2871        }
2872        read.0[0] = read.0[0].max(values[0]);
2873        read.0[1] = read.0[1].max(values[1]);
2874        read.0[2] = read.0[2].max(values[1] - foot.distance);
2875        read.1[0] = read.1[0].max(values[2]);
2876        read.1[1] = read.1[1].max(values[3]);
2877        read.1[2] = read.1[2].max(values[3] - foot.distance);
2878    }
2879    Ok(read)
2880}
2881
2882/// The interior probe fractions of the edge curve's own FIRST and LAST native
2883/// knot spans inside `[edge_start, edge_end]` (quarter points and 16 interior
2884/// points of each), mapped through the trim's range and sense to coedge
2885/// fractions in [0, 1].
2886fn edge_end_span_fractions(curve: &NurbsCurve, edge_start: f64, edge_end: f64, forward: bool) -> Vec<f64> {
2887    let (low, high) = (edge_start.min(edge_end), edge_start.max(edge_end));
2888    let mut knots: Vec<f64> = curve.knots.iter().copied().filter(|k| *k > low && *k < high).collect();
2889    knots.dedup();
2890    let spans = match (knots.first(), knots.last()) {
2891        (Some(&first), Some(&last)) => vec![(low, first), (last, high)],
2892        _ => vec![(low, high)],
2893    };
2894    let span = edge_end - edge_start;
2895    let mut out = Vec::new();
2896    for (a, b) in spans {
2897        if !(b > a) {
2898            continue;
2899        }
2900        for local in [0.25, 0.5, 0.75].into_iter().chain((1..=TRACKING_ACCEPTANCE_POINTS).map(|index| index as f64 / (TRACKING_ACCEPTANCE_POINTS + 1) as f64)) {
2901            let along = (a + (b - a) * local - edge_start) / span;
2902            let fraction = if forward { along } else { 1.0 - along };
2903            if fraction.is_finite() && (0.0..=1.0).contains(&fraction) {
2904                out.push(fraction);
2905            }
2906        }
2907    }
2908    out.sort_by(f64::total_cmp);
2909    out.dedup();
2910    out
2911}
2912
2913/// At most this many of the legacy pcurve's worst spans are seeded.
2914const WORST_SPANS: usize = 4;
2915/// Interior stations seeded in each of them.
2916const WORST_SPAN_SEEDS: usize = 7;
2917
2918/// The seed fractions of [`end_span_challenger`]'s worst-span source: the
2919/// legacy pcurve's own spans (between its distinct knots) whose same-parameter
2920/// deviation, read at the quarter points and 16 interior points, and at any of
2921/// `extra_probes` (the edge's own end-span interior points mapped to coedge
2922/// fractions) that fall inside the span, exceeds
2923/// `bar`, worst first, at most [`WORST_SPANS`] of them, each seeded with
2924/// [`WORST_SPAN_SEEDS`] evenly spaced interior fractions. Fails closed on any
2925/// non-finite reading.
2926fn legacy_worst_span_seeds(
2927    evaluate_edge: &dyn Fn(f64) -> Result<Vec3, String>,
2928    surface: &NurbsSurface,
2929    pcurve: &NurbsCurve,
2930    bar: f64,
2931    extra_probes: &[f64],
2932) -> Result<Vec<f64>, String> {
2933    let [q0, q1] = pcurve.domain()?;
2934    let mut knots: Vec<f64> = pcurve.knots.iter().copied().filter(|k| *k >= q0 && *k <= q1).collect();
2935    knots.dedup();
2936    let locals: Vec<f64> = [0.25, 0.5, 0.75]
2937        .into_iter()
2938        .chain((1..=TRACKING_ACCEPTANCE_POINTS).map(|index| index as f64 / (TRACKING_ACCEPTANCE_POINTS + 1) as f64))
2939        .collect();
2940    let mut over: Vec<(f64, f64, f64)> = Vec::new();
2941    for span in knots.windows(2).filter(|w| w[1] > w[0]) {
2942        let mut worst = 0.0_f64;
2943        let inside = extra_probes.iter().copied().filter(|f| *f > span[0] && *f < span[1]);
2944        for fraction in locals.iter().map(|local| span[0] + (span[1] - span[0]) * local).chain(inside) {
2945            let uv = pcurve.evaluate(fraction)?;
2946            let deviation = surface.evaluate_extended(uv.x, uv.y)?.sub(evaluate_edge(fraction)?).length();
2947            if !deviation.is_finite() {
2948                return Err(format!("worst-span read: non-finite deviation at fraction {fraction}"));
2949            }
2950            worst = worst.max(deviation);
2951        }
2952        if worst > bar {
2953            over.push((worst, span[0], span[1]));
2954        }
2955    }
2956    over.sort_by(|a, b| b.0.total_cmp(&a.0).then(a.1.total_cmp(&b.1)));
2957    over.truncate(WORST_SPANS);
2958    Ok(over
2959        .into_iter()
2960        .flat_map(|(_, lo, hi)| (1..=WORST_SPAN_SEEDS).map(move |index| lo + (hi - lo) * index as f64 / (WORST_SPAN_SEEDS + 1) as f64))
2961        .collect())
2962}
2963
2964/// Why the range lane's [`end_span_challenger`] runs.
2965#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2966enum ChallengerTrigger {
2967    /// The legacy residual misses `ask + standoff` (the original trigger).
2968    Deviation,
2969    /// The legacy residual is within `ask + standoff`, the edge stands off its
2970    /// carrier by more than the ask, and the legacy fit's COMMON tracking
2971    /// (`common_station_read` at the fixed probes) misses the ask.
2972    Tracking,
2973}
2974
2975/// The challenger's trigger. The original rule reads the same-parameter
2976/// DEVIATION against `ask + standoff`; over a standoff `g` a tangential slip
2977/// `d` adds only about d²/(2g) to it, so a slip up to sqrt(ask² + 2·ask·g)
2978/// never triggers (about 1.3e-5 at g = 9e-4, the fixture-25 inherited edges).
2979/// When the deviation rule does not fire and the edge stands off its carrier
2980/// by more than the ask (the same gate `refine_pcurve` takes its
2981/// standoff-tracking lane on), the legacy fit's common tracking is read
2982/// (`tracking`, called only then): over the ask, the challenger runs on
2983/// tracking. An unread tracking (None or non-finite) never triggers.
2984fn challenger_trigger(residual: f64, off_surface: f64, ask: f64, tracking: impl FnOnce() -> Option<f64>) -> Option<ChallengerTrigger> {
2985    if residual > ask + off_surface {
2986        return Some(ChallengerTrigger::Deviation);
2987    }
2988    if !(off_surface > ask) {
2989        return None;
2990    }
2991    match tracking() {
2992        Some(value) if value.is_finite() && value > ask => Some(ChallengerTrigger::Tracking),
2993        _ => None,
2994    }
2995}
2996
2997/// The END-SPAN CHALLENGER of the range lane (2026-10-05, the four original
2998/// fitter rows of `abc_00000011/12` edges 7 and 243). On e90c30c94 their worst
2999/// excess (4.58e-6 and 5.14e-6) sat in the installed pcurve's LAST span, at the
3000/// edge's start, where the edge curve's own first knot span is only 4.5e-6
3001/// (e7) and 1.2e-4 (e243) wide while the fitter's end span was 1.4e-4 wide at
3002/// its cap. It runs when [`challenger_trigger`] fires: the legacy fit misses
3003/// its bar (residual over the ask plus its standoff), or, on an edge standing
3004/// off its carrier by more than the ask, its common tracking misses the ask.
3005/// It refits with the edge's own interior knots that
3006/// fall inside the legacy pcurve's first or last span as additional stations
3007/// (not joins; corners stay the legacy ones), through the same fitter with the
3008/// unchanged ask, SPAN_FLOOR, cap and rounds. The candidate replaces the legacy
3009/// fit only when both, read at COMMON probes (both pcurves' own knot spans,
3010/// knots and endpoints, plus the edge's own knots and knot spans mapped to
3011/// fractions) against one SHARED foot per probe ([`common_station_read`]),
3012/// read finite, the candidate's tracking is strictly lower and its
3013/// same-parameter deviation no worse. Otherwise, or on any error or unreadable read, the legacy fit
3014/// stands exactly.
3015#[allow(clippy::too_many_arguments)]
3016fn end_span_challenger(
3017    surface: &NurbsSurface,
3018    curve: &NurbsCurve,
3019    evaluate_edge: &dyn Fn(f64) -> Result<Vec3, String>,
3020    edge_start: f64,
3021    edge_end: f64,
3022    forward: bool,
3023    breaks: &[f64],
3024    ask: f64,
3025    base_samples: usize,
3026    refinement_rounds: usize,
3027    parameter_cap: usize,
3028    legacy: PcurveFit,
3029) -> Result<PcurveFit, String> {
3030    // On the bar by deviation: nothing is read (the original zero-work
3031    // path). The TRACKING trigger (538dd700c: an off-carrier edge on its
3032    // deviation bar challenged on its common tracking) is withdrawn from this
3033    // merge (Claude master's ruling, 2026-10-05): bisected as the first commit
3034    // that moved step_import's a_trim_off_its_bar_is_accepted_and_named, and
3035    // the four original fitter rows do not need it (they enter on the
3036    // deviation trigger).
3037    if !(legacy.report.residual > ask + legacy.report.off_surface) {
3038        return Ok(legacy);
3039    }
3040    // The trigger read, the candidate's tracking verification and the final
3041    // read project the same edge points again: memoized, same answers.
3042    let _feet = FootMemoScope::open(surface);
3043    let Ok(end_knots) = native_end_knot_fractions(curve, edge_start, edge_end, forward, &legacy.curve) else { return Ok(legacy) };
3044    // The legacy fit's own WORST native spans (2026-10-05, root's boundary
3045    // audit of the four rows): the e90 witnesses read a 4.58e-6 / 5.14e-6
3046    // pointwise excess at q 0.99996 inside the closed native domain, in the
3047    // legacy pcurve's last span, where no edge knot falls. Each span between the
3048    // legacy pcurve's own distinct knots is read at its 19 probes; the spans
3049    // whose same-parameter deviation exceeds the ask plus the legacy standoff,
3050    // worst first and at most WORST_SPANS of them, each get WORST_SPAN_SEEDS
3051    // evenly spaced interior stations. Unreadable reads decline.
3052    // The edge's OWN first and last native knot spans, read at their quarter
3053    // points and 16 interior points, mapped through the trim's range and sense
3054    // to coedge fractions (e7's first edge span [0, 4.496e-6] of a
3055    // 0.018661-long trim maps reversed to q in [0.99976, 1], overlapping the
3056    // legacy pcurve's last span [0.99986, 1]).
3057    let edge_end_probes = edge_end_span_fractions(curve, edge_start, edge_end, forward);
3058    // The FIXED part of the common probe set, known before the candidate is
3059    // fitted: the legacy pcurve's own spans, endpoints and knots, and the
3060    // edge's own end-span probes, knot-span quarters and knots (every probe
3061    // that could have TRIGGERED the challenger among them). The candidate fit
3062    // verifies against these as well as its own spans, so it stops on the
3063    // reader that judges it.
3064    let Ok(mut fixed) = native_probe_fractions(&[&legacy.curve]) else { return Ok(legacy) };
3065    fixed.extend(edge_end_probes.iter().copied());
3066    let (low, high) = (edge_start.min(edge_end), edge_start.max(edge_end));
3067    let mut edge_knots: Vec<f64> = curve.knots.iter().copied().filter(|k| *k >= low && *k <= high).collect();
3068    edge_knots.push(low);
3069    edge_knots.push(high);
3070    edge_knots.sort_by(f64::total_cmp);
3071    edge_knots.dedup();
3072    let to_fraction = |t: f64| {
3073        let along = (t - edge_start) / (edge_end - edge_start);
3074        if forward { along } else { 1.0 - along }
3075    };
3076    for span in edge_knots.windows(2).filter(|w| w[1] > w[0]) {
3077        for local in [0.25, 0.5, 0.75] {
3078            fixed.push(to_fraction(span[0] + (span[1] - span[0]) * local));
3079        }
3080    }
3081    fixed.extend(edge_knots.iter().map(|&t| to_fraction(t)));
3082    fixed.extend(end_knots.iter().copied());
3083    fixed.extend(legacy.curve.knots.iter().copied());
3084    fixed.retain(|f| f.is_finite() && (0.0..=1.0).contains(f));
3085    fixed.sort_by(f64::total_cmp);
3086    fixed.dedup();
3087    let trigger = challenger_trigger(legacy.report.residual, legacy.report.off_surface, ask, || {
3088        common_station_read(surface, evaluate_edge, &legacy.curve, &legacy.curve, &fixed).ok().map(|((tracking, _), _)| tracking)
3089    });
3090    let Some(trigger) = trigger else { return Ok(legacy) };
3091    let Ok(worst_seeds) = legacy_worst_span_seeds(evaluate_edge, surface, &legacy.curve, ask + legacy.report.off_surface, &edge_end_probes) else { return Ok(legacy) };
3092    // The original guard holds for the deviation trigger only: a tracking
3093    // trigger needs no added station (the tracking mode and its verification
3094    // read the fixed probes).
3095    if trigger == ChallengerTrigger::Deviation && end_knots.is_empty() && worst_seeds.is_empty() {
3096        return Ok(legacy);
3097    }
3098    let mut stations: Vec<f64> = breaks.iter().copied().chain(end_knots.iter().copied()).chain(worst_seeds.iter().copied()).collect();
3099    stations.sort_by(f64::total_cmp);
3100    stations.dedup();
3101    // The fitter's cap is max(requested cap, initial station count): the added
3102    // stations must not raise it. Decline when the merged initial count
3103    // (base stations plus every break and end knot) could exceed the
3104    // requested cap.
3105    let initial = base_samples.max(1).checked_add(1).and_then(|count| count.checked_add(stations.len()));
3106    if initial.is_none_or(|count| count > parameter_cap) {
3107        return Ok(legacy);
3108    }
3109    let Ok(candidate) = fit_station_pcurve_mode(surface, evaluate_edge, &stations, breaks, ask, base_samples, refinement_rounds, parameter_cap, SPAN_FLOOR, true, Some(&legacy.curve), &fixed) else {
3110        return Ok(legacy);
3111    };
3112    if !(candidate.report.residual.is_finite() && candidate.report.off_surface.is_finite() && candidate.report.clamped_excursion.is_finite() && candidate.report.clamped_distance.is_finite()) {
3113        return Ok(legacy);
3114    }
3115    // Common probes: the fixed set plus the candidate's own knot spans,
3116    // endpoints and knots (the same set as both curves' native probes plus
3117    // the edge's, as before).
3118    let Ok(mut probes) = native_probe_fractions(&[&candidate.curve]) else { return Ok(legacy) };
3119    probes.extend(fixed.iter().copied());
3120    probes.extend(candidate.curve.knots.iter().copied());
3121    probes.retain(|f| f.is_finite() && (0.0..=1.0).contains(f));
3122    probes.sort_by(f64::total_cmp);
3123    probes.dedup();
3124    let readings = common_station_read_with_excess(surface, evaluate_edge, &legacy.curve, &candidate.curve, &probes);
3125    let accepted = matches!(&readings, Ok((old, new)) if floor_admits(*old, *new));
3126    if station_fit_tracing() {
3127        eprintln!(
3128            "STATION-FIT end-span challenger: trigger={trigger:?} end_knots={} worst_span_seeds={} edge_end_probes={} legacy={:?}/{:e} candidate={:?}/{:e} common={readings:?} decision={}",
3129            end_knots.len(), worst_seeds.len(), edge_end_probes.len(), legacy.report.exit, legacy.report.residual, candidate.report.exit, candidate.report.residual,
3130            if accepted { "candidate" } else { "legacy" }
3131        );
3132    }
3133    Ok(if accepted { candidate } else { legacy })
3134}
3135
3136
3137/// [`fit_pcurve_on_surface_range_dense`] for the callers that only need the
3138/// curve, accepted or refused on its bar ([`accept_fit`]).
3139#[allow(clippy::too_many_arguments)]
3140pub fn build_pcurve_on_surface_range_dense(
3141    surface: &NurbsSurface,
3142    curve: &NurbsCurve,
3143    edge_start: f64,
3144    edge_end: f64,
3145    forward: bool,
3146    tolerance: f64,
3147    base_samples: usize,
3148    refinement_rounds: usize,
3149    parameter_cap: usize,
3150) -> Result<NurbsCurve, String> {
3151    accept_fit(fit_pcurve_on_surface_range_dense(
3152        surface,
3153        curve,
3154        edge_start,
3155        edge_end,
3156        forward,
3157        tolerance,
3158        base_samples,
3159        refinement_rounds,
3160        parameter_cap,
3161    )?)
3162}
3163
3164/// The fractions of `[edge_start, edge_end]`, walked in the coedge's sense, at
3165/// which `curve` is not C2: interior knots of multiplicity at least its
3166/// degree. A rational-quadratic arc past a quarter turn is two segments joined
3167/// at a double knot, and an interpolant through uniform samples across that
3168/// joint cannot represent the second-derivative jump: on the 2026-09-18 import
3169/// census 1511 of the 2979 arc trims blamed on the fitter had their worst
3170/// miss at fraction 0.49–0.51, the mid double knot, exactly as the fillet's
3171/// corner patch had measured (`blend/track_fit.rs::curve_breaks`, which this
3172/// mirrors for a range).
3173pub(crate) fn range_breaks(
3174    curve: &NurbsCurve,
3175    edge_start: f64,
3176    edge_end: f64,
3177    forward: bool,
3178) -> Vec<f64> {
3179    let mut fractions = Vec::new();
3180    let span = edge_end - edge_start;
3181    if !(span.abs() > 0.0) {
3182        return fractions;
3183    }
3184    let (low, high) = (edge_start.min(edge_end), edge_start.max(edge_end));
3185    let mut index = 0;
3186    while index < curve.knots.len() {
3187        let value = curve.knots[index];
3188        let run = curve.knots[index..]
3189            .iter()
3190            .take_while(|knot| **knot == value)
3191            .count();
3192        if value > low && value < high && run >= curve.degree.max(1) {
3193            let along = (value - edge_start) / span;
3194            fractions.push(if forward { along } else { 1.0 - along });
3195        }
3196        index += run;
3197    }
3198    fractions.sort_by(f64::total_cmp);
3199    fractions
3200}
3201
3202/// The body of [`fit_pcurve_on_surface_range_dense`], parameterized by the
3203/// 3D station the pcurve must pass through at each coedge fraction instead of
3204/// by an edge curve and a range.
3205///
3206/// The station supplier is the whole difference between deriving a pcurve and
3207/// reading one. `fit_pcurve_on_surface_range_dense` supplies points off the
3208/// edge's own 3D curve, so the answer is "the nearest point of the carrier",
3209/// which is ambiguous exactly where a trim is interesting — a seam branch, a
3210/// near-tangential approach, a pole. The STEP importer's supplied-pcurve lane
3211/// supplies points off the VENDOR's stated trim instead, and the ambiguity is
3212/// gone because the vendor resolved it.
3213///
3214/// `stations(fraction)` must be continuous in `fraction` over `[0, 1]` and land
3215/// on (or very near) `surface`; `breaks` are the fractions at which it is not
3216/// C2 ([`range_breaks`]), each of which becomes a station so no kink sits
3217/// between two nodes. Everything else — inversion, branch repair, refinement
3218/// and the measurement — is shared with the derived lane by construction.
3219///
3220/// The SURFACE's knot-line crossings are deliberately not stations here,
3221/// although `blend/track_fit.rs` makes them so. Measured 2026-09-26 over the
3222/// 57-document import corpus: with each crossing bisected on the fraction and
3223/// projected seeded from the left station, 34 bodies refused (19 with a foot
3224/// that misses its global distance rejected) against 15 without the pass —
3225/// every extra refusal a fit stalled around a crossing station, on
3226/// `AssemblyExample-Assembly` and `abc_00000014` among others. The raised
3227/// pass resolves the O(h²) tail across a crossing on its own.
3228///
3229/// # What the fit is measured against, and when it stops
3230///
3231/// The stations' own standoff from the carrier is read FIRST, from the base
3232/// inversions, and the refinement target is the ask plus that standoff
3233/// ([`PcurveFitReport::on_bar`]): a station standing `g` off the surface
3234/// cannot be imaged closer than `g`, so chasing it to the ask alone spends the
3235/// whole budget for nothing (the 2026-09-18 census: 2052 imported pcurves at
3236/// the 513 cap, 1958 of them on a curve that is not on its carrier).
3237///
3238/// Every probe and the final measurement read the carrier through
3239/// `evaluate_extended`: a pcurve riding a periodic seam legitimately carries
3240/// parameters past the domain, and the clamped `evaluate` this loop used until
3241/// 2026-09-26 read every such station as a gross miss — an insert per round
3242/// until the cap, on a trim that was right.
3243///
3244/// The RETURNED curve is measured out of sample — at the quarter points of
3245/// every span between its nodes, where an interpolant is exact at the nodes
3246/// and extremal between — unless its last round probed every span and found
3247/// nothing to insert, in which case that round's maximum already is that
3248/// measurement. A fit that binds at `parameter_cap` or spends its rounds with
3249/// the bar still unmet grows ONCE to [`PCURVE_RAISED_SAMPLES`]
3250/// (deterministic). The report says which exit it took and what it reached;
3251/// the `build_*` wrappers refuse over the bar, this function never does.
3252#[allow(clippy::too_many_arguments)]
3253pub fn fit_pcurve_on_surface_stations(
3254    surface: &NurbsSurface,
3255    stations: &dyn Fn(f64) -> Result<Vec3, String>,
3256    breaks: &[f64],
3257    tolerance: f64,
3258    base_samples: usize,
3259    refinement_rounds: usize,
3260    parameter_cap: usize,
3261) -> Result<PcurveFit, String> {
3262    fit_station_pcurve(
3263        surface, stations, breaks, &[], tolerance.max(PCURVE_REFINEMENT_TOLERANCE),
3264        base_samples, refinement_rounds, parameter_cap, SPAN_FLOOR,
3265    )
3266}
3267
3268/// Precision construction for stations on recognized analytic carriers. Unlike
3269/// general projection, their inverse maps reach floating point accuracy. This
3270/// is an explicit construction contract, not a change to general fit policy.
3271pub(crate) fn analytic_station_pcurve(
3272    surface: &NurbsSurface,
3273    stations: &dyn Fn(f64) -> Result<Vec3, String>,
3274    tolerance: f64,
3275) -> Result<NurbsCurve, String> {
3276    if surface.analytic().is_none() {
3277        return Err("analytic pcurve requires an analytic carrier".into());
3278    }
3279    let mut breaks = Vec::new();
3280    for _ in 0..4 {
3281        let fit = fit_station_pcurve(surface, stations, &breaks, &[], tolerance, 64, 64, 8192, 1e-10)?;
3282        if fit.report.residual > tolerance || fit.report.off_surface > tolerance {
3283            return Err(format!(
3284                "analytic pcurve construction missed its contract: {:?}",
3285                fit.report
3286            ));
3287        }
3288        let mut failed = Vec::new();
3289        // Independent Gauss stations in every returned knot span, rather than
3290        // re-reading the interpolation nodes or the fitter's quarter stations.
3291        for pair in fit.curve.knots.windows(2) {
3292            if pair[1] <= pair[0] {
3293                continue;
3294            }
3295            for local in [
3296                0.06943184420297371,
3297                0.33000947820757187,
3298                0.6699905217924281,
3299                0.9305681557970262,
3300            ] {
3301                let t = pair[0] + (pair[1] - pair[0]) * local;
3302                let uv = fit.curve.evaluate(t)?;
3303                if surface
3304                    .evaluate_extended(uv.x, uv.y)?
3305                    .sub(stations(t)?)
3306                    .length()
3307                    > tolerance
3308                {
3309                    failed.push(t);
3310                }
3311            }
3312        }
3313        if failed.is_empty() {
3314            return Ok(fit.curve);
3315        }
3316        if breaks.len() + failed.len() > 8192 {
3317            break;
3318        }
3319        breaks.extend(failed);
3320        breaks.sort_by(f64::total_cmp);
3321        breaks.dedup();
3322    }
3323    Err("analytic pcurve failed independent knot-span witnesses".into())
3324}
3325
3326#[allow(clippy::too_many_arguments)]
3327fn fit_station_pcurve(
3328    surface: &NurbsSurface,
3329    stations: &dyn Fn(f64) -> Result<Vec3, String>,
3330    breaks: &[f64],
3331    corners: &[f64],
3332    tolerance: f64,
3333    base_samples: usize,
3334    refinement_rounds: usize,
3335    parameter_cap: usize,
3336    span_floor: f64,
3337) -> Result<PcurveFit, String> {
3338    fit_station_pcurve_mode(surface, stations, breaks, corners, tolerance, base_samples, refinement_rounds, parameter_cap, span_floor, false, None, &[])
3339}
3340
3341/// [`fit_station_pcurve`] with an optional TRACKING mode, taken only by the
3342/// range lane's [`end_span_challenger`]; every other caller keeps the legacy
3343/// mode byte for byte. The legacy fitter probes, ranks and stops on the
3344/// image's same-parameter DEVIATION from the edge point. On an edge standing
3345/// off its carrier that deviation never falls under the ask, so every probe is
3346/// a candidate, the cap goes to the probes with the largest STANDOFF, and a
3347/// tangential slip of `d` adds only about d²/(2g) to a deviation over a
3348/// standoff `g`: the four original rows' challenger met its standoff (4.670e-6)
3349/// and kept a common tracking of 1.434e-6 (root's e24 run, 2026-10-05). In
3350/// tracking mode each probe reads its TRACKING, the image against the edge
3351/// point's foot (the nearer admitted one of the global projection and the
3352/// projection seeded at the image's uv: finite, inside the native domain, as
3353/// [`common_station_read`] admits). A probe is a candidate only over the ask,
3354/// the cap goes to the worst TRACKING first, and the one raise is taken when
3355/// the last round's tracking still misses the ask. The reported residual
3356/// stays the same-parameter deviation (standoff included). A probe with no
3357/// admitted foot is an error, which the challenger turns into the exact legacy
3358/// fit.
3359#[allow(clippy::too_many_arguments)]
3360fn fit_station_pcurve_mode(
3361    surface: &NurbsSurface,
3362    stations: &dyn Fn(f64) -> Result<Vec3, String>,
3363    breaks: &[f64],
3364    corners: &[f64],
3365    tolerance: f64,
3366    base_samples: usize,
3367    refinement_rounds: usize,
3368    parameter_cap: usize,
3369    span_floor: f64,
3370    track_projection: bool,
3371    reference: Option<&NurbsCurve>,
3372    verify: &[f64],
3373) -> Result<PcurveFit, String> {
3374    // Read only in tracking mode: the legacy mode makes no extra call.
3375    let domains = if track_projection { Some(surface_domains(surface)?) } else { None };
3376    // The tracking reading at one fraction, by the SAME foot rule as
3377    // [`common_station_read`], the reader the challenger accepts on: the
3378    // nearest admitted foot among the global projection and the projections
3379    // seeded at this image's uv and at the reference (legacy) pcurve's uv at
3380    // the same fraction. Returns the tracking and that foot (uv, distance),
3381    // which is where a tracking insert is placed.
3382    let tracking_of = |represented: Vec3, edge_point: Vec3, uv: Vec3, fraction: f64| -> Result<(f64, [f64; 2], f64), String> {
3383        let (domain_u, domain_v) = domains.ok_or_else(|| "station tracking: legacy mode".to_string())?;
3384        let finite = |p: Vec3| p.x.is_finite() && p.y.is_finite() && p.z.is_finite();
3385        let reference_seed = match reference {
3386            Some(curve) => curve.evaluate(fraction).ok().and_then(|seed| memo_seeded_foot(surface, edge_point, seed.x, seed.y)),
3387            None => None,
3388        };
3389        // The common reader's order (global, reference, candidate), so an
3390        // equal-distance tie takes the same foot here as there.
3391        let foot = common_foot(
3392            memo_global_foot(surface, edge_point),
3393            reference_seed,
3394            memo_seeded_foot(surface, edge_point, uv.x, uv.y),
3395            domain_u,
3396            domain_v,
3397        )
3398        .ok_or_else(|| "station tracking: no admitted foot".to_string())?;
3399        let tracking = represented.sub(foot.point).length();
3400        if !(finite(represented) && tracking.is_finite()) {
3401            return Err("station tracking: non-finite reading".into());
3402        }
3403        Ok((tracking, [foot.u, foot.v], foot.distance))
3404    };
3405    // The last round's tracking missed the ask (tracking mode only).
3406    let mut tracking_unmet = false;
3407    let evaluate_edge = stations;
3408    let base_samples = base_samples.max(1);
3409    // The general caller floors its ask: its projector places a foot
3410    // to about `LINEAR_TOLERANCE` (1e-7), so a caller asking for 1e-9 asks for
3411    // more than any station can deliver. Chased to such an ask the fit spends
3412    // its cap and reads its own noise — the direct-edit heals asked 1e-9 on
3413    // stations 9.9e-8 off their carrier and were refused by 8.5e-9
3414    // (2026-09-26). The floor is the ask's floor; the report says what was
3415    // reached against it.
3416
3417    let mut parameters = (0..=base_samples)
3418        .map(|index| index as f64 / base_samples as f64)
3419        .collect::<Vec<_>>();
3420    // A break within the span floor of a station IS that station: two nodes
3421    // closer than that make the interpolant swing between them, and over a
3422    // stretch that short the kink moves the fit by nothing a bar can read.
3423    // `abc_00000011` face #549's curve carries a triple knot 4e-7 before its
3424    // end; as a station it put the cubic 1.594e-2 off between it and the last
3425    // one (measured 2026-09-26), where the file's curve sits 1e-7 off.
3426    // A break that is a CORNER of the curve (`corners`, a subset of
3427    // `breaks`: the range lane passes its curve's knots of multiplicity >=
3428    // degree as both) is a C0 JOIN of the interpolant
3429    // (`build_interpolant_joined`): one cubic through the corner overshoots
3430    // on both sides whatever is inserted. A break that is only a station
3431    // (an analytic witness, a caller's own marker) joins nothing.
3432    let mut joins = Vec::with_capacity(corners.len());
3433    for &fraction in breaks {
3434        if !(fraction > 0.0 && fraction < 1.0) {
3435            continue;
3436        }
3437        let corner = corners.contains(&fraction);
3438        let at = parameters.partition_point(|parameter| *parameter < fraction);
3439        let near_left = at > 0 && fraction - parameters[at - 1] <= span_floor;
3440        let near_right = at < parameters.len() && parameters[at] - fraction <= span_floor;
3441        if near_left || near_right {
3442            // The break IS that station (a corner at a base station, e.g.
3443            // the midpoint of a two-piece curve at 32/64): no insert, but the
3444            // station is still the join.
3445            if corner {
3446                joins.push(if near_right { parameters[at] } else { parameters[at - 1] });
3447            }
3448            continue;
3449        }
3450        parameters.insert(at, fraction);
3451        if corner {
3452            joins.push(fraction);
3453        }
3454    }
3455    // The 3D edge point behind each raw inversion sample, kept parallel so the
3456    // continuity repair can re-seed a jumped station from its own footpoint.
3457    let mut edge_points = parameters
3458        .iter()
3459        .map(|fraction| evaluate_edge(*fraction))
3460        .collect::<Result<Vec<_>, _>>()?;
3461    // The stations' standoff from the carrier: the part of every residual no
3462    // pcurve can remove, known before a single probe is spent.
3463    let mut off_surface = 0.0_f64;
3464    let mut raw = Vec::with_capacity(edge_points.len());
3465    let mut gaps = Vec::with_capacity(edge_points.len());
3466    for point in &edge_points {
3467        let (parameter, gap) = invert_checked(surface, *point)?;
3468        off_surface = off_surface.max(gap);
3469        raw.push(parameter);
3470        gaps.push(gap);
3471    }
3472    repair_branch_jumps(surface, &edge_points, &mut raw, tolerance)?;
3473    let (mut pcurve, mut excursion, mut distance) =
3474        build_interpolant_joined(surface, &raw, &parameters, &joins)?;
3475
3476    let mut cap = parameter_cap.max(parameters.len());
3477    let mut rounds = refinement_rounds;
3478    let mut raised = false;
3479    loop {
3480        let mut exit = PcurveFitExit::RoundBudget;
3481        let mut converged_max = None;
3482        // This pass's last round probed every span (none narrow, none
3483        // truncated) and every probe tracked within the ask (tracking mode).
3484        let mut tracking_met = false;
3485        for _ in 0..rounds {
3486            if parameters.len() >= cap {
3487                exit = PcurveFitExit::SampleCeiling;
3488                break;
3489            }
3490            // The target is the ASK, bare: a station standing off the carrier
3491            // is chased to the foot it projects to, as the fitter has always
3492            // done. A target of ask + standoff (one landing, 2026-09-26) let
3493            // such a fit stop at its base stations with the interpolant
3494            // wandering between them, folded a molex wall's mesh and put a
3495            // trim of abc_00000023 2.2e-5 off its own parameter; the bar
3496            // below still reads what it reached.
3497            let target = tolerance;
3498            let mut candidates = Vec::new();
3499            let mut round_max = 0.0_f64;
3500            let mut truncated = false;
3501            let mut skipped_narrow = false;
3502            let worst_first = worst_first();
3503            'spans: for index in 0..parameters.len() - 1 {
3504                // The loop's own resolution floor: a span narrower than this
3505                // is not subdivided. Without it the
3506                // raised pass inserted into a vanishing span at a trim's end
3507                // until the cap — `abc_00000011` face #549's last stations sat
3508                // 4e-7 apart with their feet clamped at the patch edge, and the
3509                // cubic through them swung 1.594e-2 off between them (measured
3510                // 2026-09-26). A miss that survives only inside such a span is
3511                // reported as [`PcurveFitExit::SpanFloor`].
3512                if parameters[index + 1] - parameters[index] < span_floor {
3513                    skipped_narrow = true;
3514                    continue;
3515                }
3516                for local in [0.25, 0.5, 0.75] {
3517                    if !worst_first && parameters.len() + candidates.len() >= cap {
3518                        truncated = true;
3519                        break 'spans;
3520                    }
3521                    let fraction =
3522                        parameters[index] + (parameters[index + 1] - parameters[index]) * local;
3523                    let uv = pcurve.evaluate(fraction)?;
3524                    let represented = surface.evaluate_extended(uv.x, uv.y)?;
3525                    let edge_point = evaluate_edge(fraction)?;
3526                    let deviation = represented.sub(edge_point).length();
3527                    // The probe's reading: its deviation (legacy), or its
3528                    // tracking against the admitted foot (tracking mode).
3529                    let (reading, foot) = if track_projection {
3530                        let (tracking, foot_uv, foot_gap) = tracking_of(represented, edge_point, uv, fraction)?;
3531                        (tracking, Some((foot_uv, foot_gap)))
3532                    } else {
3533                        (deviation, None)
3534                    };
3535                    round_max = round_max.max(reading);
3536                    if reading <= target {
3537                        continue;
3538                    }
3539                    candidates.push((index, fraction, edge_point, uv, reading, foot));
3540                }
3541            }
3542            // Tracking mode's VERIFICATION, before it may stop: a round whose
3543            // quarter probes all track within the ask is read again at the
3544            // probes the challenger's acceptance reads, the current pcurve's
3545            // own knot spans (endpoints, quarters and 16 interior points: the
3546            // interpolant's knots are averages of three stations, not the
3547            // stations) and the caller's fixed fractions (the legacy pcurve's
3548            // spans and the edge's own). A probe over the ask becomes a
3549            // candidate at its own fraction, placed at its own foot. One
3550            // within the span floor of a station cannot be inserted: it is
3551            // counted, and the fit may not report its tracking as met. On e24
3552            // the four rows stopped on their quarter probes and read 1.144e-7 /
3553            // 1.590e-7 at the common probes (a4c84871 runtime, 2026-10-05).
3554            let mut verify_blocked = false;
3555            if track_projection && candidates.is_empty() && !truncated && !skipped_narrow {
3556                for fraction in verification_fractions(&pcurve, verify)? {
3557                    let index = parameters.partition_point(|parameter| *parameter <= fraction).saturating_sub(1).min(parameters.len() - 2);
3558                    let uv = pcurve.evaluate(fraction)?;
3559                    let represented = surface.evaluate_extended(uv.x, uv.y)?;
3560                    let edge_point = evaluate_edge(fraction)?;
3561                    let (tracking, foot_uv, foot_gap) = tracking_of(represented, edge_point, uv, fraction)?;
3562                    round_max = round_max.max(tracking);
3563                    if tracking <= target {
3564                        continue;
3565                    }
3566                    if fraction - parameters[index] <= span_floor || parameters[index + 1] - fraction <= span_floor {
3567                        verify_blocked = true;
3568                        continue;
3569                    }
3570                    // Two over-ask probes closer than the span floor in one
3571                    // span: keep the worse, so no near-duplicate stations.
3572                    if let Some(last) = candidates.last_mut() {
3573                        if last.0 == index && fraction - last.1 <= span_floor {
3574                            if tracking > last.4 {
3575                                *last = (index, fraction, edge_point, uv, tracking, Some((foot_uv, foot_gap)));
3576                            }
3577                            continue;
3578                        }
3579                    }
3580                    candidates.push((index, fraction, edge_point, uv, tracking, Some((foot_uv, foot_gap))));
3581                }
3582            }
3583            tracking_unmet = track_projection && round_max > target;
3584            // A round with more candidates than the cap has room for spends
3585            // that room on its WORST probes, kept in curve order, rather than
3586            // on the first spans in order: truncating in span order left the
3587            // late spans of an off-carrier ring at their base spacing and its
3588            // image 1.523e-6 off its feet; worst first reaches 5.84e-8 on the
3589            // same 513 samples. An even stride along the curve reached 2.87e-7
3590            // there but diluted the inserts a local miss needs and lost fits
3591            // over the corpus (a 1.3e-5 trim of `abc_00000011` fell back to its
3592            // 4.4e-4 size-ask fit), measured 2026-09-27.
3593            let room = cap.saturating_sub(parameters.len());
3594            if candidates.len() > room {
3595                truncated = true;
3596                let mut order: Vec<usize> = (0..candidates.len()).collect();
3597                order.sort_by(|&a, &b| candidates[b].4.total_cmp(&candidates[a].4).then(a.cmp(&b)));
3598                order.truncate(room);
3599                order.sort_unstable();
3600                candidates = order.into_iter().map(|index| candidates[index]).collect();
3601            }
3602            let mut inserts = Vec::with_capacity(candidates.len());
3603            for (index, fraction, edge_point, uv, _, foot) in candidates {
3604                // A tracking candidate is placed at the very foot it was read
3605                // against, so the node's image IS that foot; the legacy
3606                // inversion is unchanged.
3607                let (parameter, gap) = match foot {
3608                    Some(placed) => placed,
3609                    None => invert_near(surface, edge_point, [uv.x, uv.y], gaps[index].max(gaps[index + 1]))?,
3610                };
3611                inserts.push((index + 1, fraction, edge_point, parameter, gap));
3612            }
3613            if station_fit_tracing() {
3614                eprintln!(
3615                    "STATION-FIT round: samples={} target={target:.3e} round_max={round_max:.3e} inserts={} truncated={truncated}{}",
3616                    parameters.len(),
3617                    inserts.len(),
3618                    // Tracking mode's round_max is a TRACKING reading.
3619                    if track_projection { " mode=tracking" } else { "" }
3620                );
3621            }
3622            if inserts.is_empty() {
3623                exit = if truncated {
3624                    PcurveFitExit::SampleCeiling
3625                } else if skipped_narrow {
3626                    // Every probed span is within the target; the narrow ones
3627                    // were not probed, and the sweep below measures them.
3628                    PcurveFitExit::SpanFloor
3629                } else if track_projection {
3630                    // Every probe TRACKS within the ask, but round_max is a
3631                    // tracking reading, not the deviation the report carries.
3632                    // The exit is Stalled (no probe can be improved; the
3633                    // residual measured below is the edge's own standoff),
3634                    // and it becomes Converged below only if that measured
3635                    // deviation itself meets the floor. A verification probe
3636                    // over the ask that could not be inserted leaves the
3637                    // tracking UNMET.
3638                    tracking_met = !verify_blocked;
3639                    PcurveFitExit::Stalled
3640                } else {
3641                    converged_max = Some(round_max);
3642                    PcurveFitExit::Converged
3643                };
3644                break;
3645            }
3646            for (index, fraction, edge_point, parameter, gap) in inserts.into_iter().rev() {
3647                parameters.insert(index, fraction);
3648                edge_points.insert(index, edge_point);
3649                raw.insert(index, parameter);
3650                gaps.insert(index, gap);
3651                off_surface = off_surface.max(gap);
3652            }
3653            // Inserts are independent global inversions too — re-run the repair
3654            // so a fold-flip introduced mid-refinement cannot poison the next
3655            // round's deviation interpolant.
3656            repair_branch_jumps(surface, &edge_points, &mut raw, tolerance)?;
3657            let rebuilt = build_interpolant_joined(surface, &raw, &parameters, &joins)?;
3658            pcurve = rebuilt.0;
3659            excursion = rebuilt.1;
3660            distance = rebuilt.2;
3661            if truncated {
3662                exit = PcurveFitExit::SampleCeiling;
3663                break;
3664            }
3665        }
3666        let residual = match converged_max {
3667            Some(measured) => measured,
3668            None => {
3669                station_residual(surface, evaluate_edge, &pcurve, &parameters, tolerance, &mut off_surface)?
3670            }
3671        };
3672        if station_fit_tracing() {
3673            eprintln!(
3674                "STATION-FIT pass raised={raised} exit={exit:?} samples={} residual={residual:.3e} off_surface={off_surface:.3e}",
3675                parameters.len()
3676            );
3677            if residual > tolerance + off_surface && (exit == PcurveFitExit::Converged || raised) {
3678                // Where the miss sits, and the uv track around it: the trace a
3679                // refusal needs to be attributed to a fold, a seam or the file.
3680                let (mut worst, mut at) = (0.0f64, 0usize);
3681                for (index, span) in parameters.windows(2).enumerate() {
3682                    let fraction = 0.5 * (span[0] + span[1]);
3683                    let uv = pcurve.evaluate(fraction)?;
3684                    let deviation = surface.evaluate_extended(uv.x, uv.y)?.sub(evaluate_edge(fraction)?).length();
3685                    if deviation > worst {
3686                        worst = deviation;
3687                        at = index;
3688                    }
3689                }
3690                let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
3691                let (closed_u, closed_v) = surface_closedness(surface)?;
3692                eprintln!(
3693                    "STATION-FIT OFF BAR: worst {worst:.3e} between stations {at} and {}; surface degree {}x{} net {}x{} domain u[{u0},{u1}] v[{v0},{v1}] closed ({closed_u},{closed_v}) analytic {:?}",
3694                    at + 1,
3695                    surface.degree_u,
3696                    surface.degree_v,
3697                    surface.control_points.len(),
3698                    surface.control_points[0].len(),
3699                    surface.analytic().map(|_| "yes").unwrap_or("no")
3700                );
3701                {
3702                    let fraction = 0.5 * (parameters[at] + parameters[at + 1]);
3703                    let uv = pcurve.evaluate(fraction)?;
3704                    let image = surface.evaluate_extended(uv.x, uv.y)?;
3705                    let station = evaluate_edge(fraction)?;
3706                    let foot = project_point_to_surface(surface, station)?;
3707                    let seeded = project_point_to_surface_seeded(surface, station, raw[at][0], raw[at][1])?;
3708                    eprintln!(
3709                        "    at the worst midpoint: deviation {:.4e}, station gap {:.4e} (seeded {:.4e}), image-to-foot {:.4e} (to seeded foot {:.4e}), foot uv ({:.7}, {:.7}) seeded ({:.7}, {:.7}) image uv ({:.7}, {:.7})",
3710                        image.sub(station).length(), foot.distance, seeded.distance, image.sub(foot.point).length(), image.sub(seeded.point).length(), foot.u, foot.v, seeded.u, seeded.v, uv.x, uv.y
3711                    );
3712                }
3713                let lo = at.saturating_sub(3);
3714                let hi = (at + 4).min(parameters.len() - 1);
3715                for index in lo..=hi {
3716                    eprintln!(
3717                        "    station {index}: fraction {:.6} uv ({:.6}, {:.6}) point ({:.4}, {:.4}, {:.4})",
3718                        parameters[index], raw[index][0], raw[index][1], edge_points[index].x, edge_points[index].y, edge_points[index].z
3719                    );
3720                }
3721            }
3722        }
3723        // Legacy only: a SpanFloor exit whose measured DEVIATION is on the
3724        // bar is promoted. In tracking mode the deviation sits at the standoff
3725        // whatever the unprobed narrow spans' TRACKING is, so SpanFloor stays
3726        // SpanFloor (unread); a tracking-mode fit is Converged only when every
3727        // span was probed within the ask AND the measured deviation meets the
3728        // floor (the exit's documented meaning, never the standoff bar).
3729        if !track_projection && exit == PcurveFitExit::SpanFloor && residual <= tolerance + off_surface {
3730            exit = PcurveFitExit::Converged;
3731        }
3732        if tracking_met && residual <= tolerance {
3733            exit = PcurveFitExit::Converged;
3734        }
3735        if exit != PcurveFitExit::Converged
3736            && !raised
3737            && (residual > tolerance + off_surface || tracking_unmet)
3738            && cap < PCURVE_RAISED_SAMPLES
3739        {
3740            raised = true;
3741            cap = PCURVE_RAISED_SAMPLES;
3742            // The raised pass is bounded by SAMPLES, not rounds: a kink
3743            // converges O(h²), a round per halving, and the three rounds of
3744            // the first pass are what left the 2026-09-18 census's fits over
3745            // their ask with budget to spare (RoundBudget at 158–400 of 2000
3746            // samples, measured 2026-09-26). Forty rounds cover every halving
3747            // the cap admits; a fit that is not converging stalls out first.
3748            rounds = RAISED_ROUNDS;
3749            continue;
3750        }
3751        return Ok(PcurveFit {
3752            curve: pcurve,
3753            report: PcurveFitReport {
3754                residual,
3755                floor: tolerance,
3756                samples: parameters.len(),
3757                exit,
3758                off_surface,
3759                raised,
3760                clamped_excursion: excursion,
3761                clamped_distance: distance,
3762            },
3763        });
3764    }
3765}
3766
3767/// [`fit_pcurve_on_surface_stations`] for the callers that only need the
3768/// curve, accepted or refused on its bar ([`accept_fit`]).
3769pub fn build_pcurve_on_surface_stations(
3770    surface: &NurbsSurface,
3771    stations: &dyn Fn(f64) -> Result<Vec3, String>,
3772    breaks: &[f64],
3773    tolerance: f64,
3774    base_samples: usize,
3775    refinement_rounds: usize,
3776    parameter_cap: usize,
3777) -> Result<NurbsCurve, String> {
3778    accept_fit(fit_pcurve_on_surface_stations(
3779        surface,
3780        stations,
3781        breaks,
3782        tolerance,
3783        base_samples,
3784        refinement_rounds,
3785        parameter_cap,
3786    )?)
3787}
3788
3789/// The largest 3D distance between `pcurve`'s image and its stations at the
3790/// quarter points of every span of `parameters` — out of sample by
3791/// construction, since the interpolant is exact at its nodes. Unclamped
3792/// evaluations; a station read over the running bar is inverted so that
3793/// `off_surface` covers the standoff AT the stations the residual is taken at,
3794/// not only at the nodes (a curve lifted off its plane by a bump stands
3795/// further off between two nodes than at either).
3796fn station_residual(
3797    surface: &NurbsSurface,
3798    stations: &dyn Fn(f64) -> Result<Vec3, String>,
3799    pcurve: &NurbsCurve,
3800    parameters: &[f64],
3801    tolerance: f64,
3802    off_surface: &mut f64,
3803) -> Result<f64, String> {
3804    let mut worst = 0.0_f64;
3805    let mut read = |fraction: f64| -> Result<(), String> {
3806        let uv = pcurve.evaluate(fraction)?;
3807        let represented = surface.evaluate_extended(uv.x, uv.y)?;
3808        let station = stations(fraction)?;
3809        let deviation = represented.sub(station).length();
3810        worst = worst.max(deviation);
3811        if deviation > tolerance + *off_surface {
3812            *off_surface = off_surface.max(invert_checked(surface, station)?.1);
3813        }
3814        Ok(())
3815    };
3816    for span in parameters.windows(2) {
3817        for local in [0.25, 0.5, 0.75] {
3818            read(span[0] + (span[1] - span[0]) * local)?;
3819        }
3820    }
3821    Ok(worst)
3822}
3823