1use crate::{
2 interpolate_curve, project_point_to_surface, project_point_to_surface_seeded, AnalyticSurface,
3 KnotVector, NurbsCurve, NurbsSurface, Vec3, Vec4,
4};
5
6const EPSILON: f64 = 1e-12;
7const LINEAR_TOLERANCE: f64 = 1e-7;
8
9fn surface_domains(surface: &NurbsSurface) -> Result<([f64; 2], [f64; 2]), String> {
10 Ok((
11 KnotVector::new(surface.knots_u.clone(), surface.degree_u)?.domain(),
12 KnotVector::new(surface.knots_v.clone(), surface.degree_v)?.domain(),
13 ))
14}
15
16fn surface_closedness(surface: &NurbsSurface) -> Result<(bool, bool), String> {
17 let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
18 let mut closed_u = true;
19 let mut closed_v = true;
20 for fraction in [0.19, 0.52, 0.87] {
21 let v = v0 + (v1 - v0) * fraction;
22 if surface
23 .evaluate(u0, v)?
24 .sub(surface.evaluate(u1, v)?)
25 .length()
26 > LINEAR_TOLERANCE * 10.0
27 {
28 closed_u = false;
29 }
30 let u = u0 + (u1 - u0) * fraction;
31 if surface
32 .evaluate(u, v0)?
33 .sub(surface.evaluate(u, v1)?)
34 .length()
35 > LINEAR_TOLERANCE * 10.0
36 {
37 closed_v = false;
38 }
39 }
40 Ok((closed_u, closed_v))
41}
42
43fn invert_checked(surface: &NurbsSurface, point: Vec3) -> Result<([f64; 2], f64), String> {
44 if surface.is_affine()? {
45 let ([u0, _], [v0, _]) = surface_domains(surface)?;
46 let (origin, du, dv) = surface.deriv1(u0, v0)?;
47 let delta = point.sub(origin);
48 let uu = du.dot(du);
49 let uv = du.dot(dv);
50 let vv = dv.dot(dv);
51 let along_u = delta.dot(du);
52 let along_v = delta.dot(dv);
53 let determinant = uu * vv - uv * uv;
54 if determinant.abs() > EPSILON {
55 return Ok((
56 [
57 u0 + (along_u * vv - along_v * uv) / determinant,
58 v0 + (along_v * uu - along_u * uv) / determinant,
59 ],
60 0.0,
61 ));
62 }
63 }
64 let projection = project_point_to_surface(surface, point)?;
65 Ok(([projection.u, projection.v], projection.distance))
66}
67
68fn unwrap_periodic(values: &mut [f64], minimum: f64, maximum: f64) {
69 let period = maximum - minimum;
70 for index in 1..values.len() {
71 while values[index] - values[index - 1] > period / 2.0 {
72 values[index] -= period;
73 }
74 while values[index] - values[index - 1] < -period / 2.0 {
75 values[index] += period;
76 }
77 }
78 if values.len() > 1 {
79 while values[0] - values[1] > period / 2.0 {
80 values[0] -= period;
81 }
82 while values[0] - values[1] < -period / 2.0 {
83 values[0] += period;
84 }
85 }
86 if values.len() > 1 {
96 let excursion = |shift: f64| -> f64 {
97 values
98 .iter()
99 .map(|value| {
100 let v = value + shift;
101 (minimum - v).max(0.0) + (v - maximum).max(0.0)
102 })
103 .sum::<f64>()
104 };
105 let mean = values.iter().copied().sum::<f64>() / values.len() as f64;
106 let center = 0.5 * (minimum + maximum);
107 let base_k = ((center - mean) / period).round() as i64;
108 let mut best_shift = 0.0;
109 let mut best_excursion = f64::INFINITY;
110 for k in (base_k - 1)..=(base_k + 1) {
111 let shift = k as f64 * period;
112 let value = excursion(shift);
113 if value < best_excursion {
114 best_excursion = value;
115 best_shift = shift;
116 }
117 }
118 if best_shift != 0.0 {
119 for value in values.iter_mut() {
120 *value += best_shift;
121 }
122 }
123 }
124}
125
126fn build_interpolant(
127 surface: &NurbsSurface,
128 raw_parameters: &[[f64; 2]],
129 curve_parameters: &[f64],
130) -> Result<NurbsCurve, String> {
131 let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
132 let (closed_u, closed_v) = surface_closedness(surface)?;
133 let mut parameters = raw_parameters.to_vec();
134 let sphere_u_singular = if matches!(surface.analytic(), Some(AnalyticSurface::Sphere { .. })) {
146 let speeds = parameters
147 .iter()
148 .map(|parameter| {
149 surface
150 .deriv1(parameter[0], parameter[1])
151 .map(|(_, su, _)| su.length())
152 })
153 .collect::<Result<Vec<_>, _>>()?;
154 let reference = speeds.iter().copied().fold(0.0_f64, f64::max);
155 speeds
156 .into_iter()
157 .map(|speed| speed <= reference * 1e-2)
162 .collect::<Vec<_>>()
163 } else {
164 vec![false; parameters.len()]
165 };
166 let has_interior_pole_crossing = sphere_u_singular
174 .iter()
175 .position(|singular| !singular)
176 .zip(sphere_u_singular.iter().rposition(|singular| !singular))
177 .is_some_and(|(first, last)| sphere_u_singular[first..=last].iter().any(|value| *value));
178 let period = (u1 - u0).abs();
179 let interior_pole_branch_reset = closed_u
180 && has_interior_pole_crossing
181 && parameters.windows(2).zip(sphere_u_singular.windows(2)).any(
182 |(parameter_pair, singular_pair)| {
183 (singular_pair[0] || singular_pair[1])
184 && (parameter_pair[1][0] - parameter_pair[0][0]).abs()
185 >= 0.5 * period - 1e-12 * period.max(1.0)
186 },
187 );
188 if closed_u {
189 let mut values = parameters.iter().map(|value| value[0]).collect::<Vec<_>>();
190 if interior_pole_branch_reset {
191 let mut start = 0;
192 while start < values.len() {
193 while start < values.len() && sphere_u_singular[start] {
194 start += 1;
195 }
196 let mut end = start;
197 while end < values.len() && !sphere_u_singular[end] {
198 end += 1;
199 }
200 unwrap_periodic(&mut values[start..end], u0, u1);
201 start = end;
202 }
203 } else {
204 unwrap_periodic(&mut values, u0, u1);
205 }
206 for (parameter, value) in parameters.iter_mut().zip(values) {
207 parameter[0] = value;
208 }
209 }
210 if closed_v {
211 let mut values = parameters.iter().map(|value| value[1]).collect::<Vec<_>>();
212 unwrap_periodic(&mut values, v0, v1);
213 for (parameter, value) in parameters.iter_mut().zip(values) {
214 parameter[1] = value;
215 }
216 }
217 let wrap = surface.analytic().is_none();
227 let points = parameters
228 .into_iter()
229 .map(|parameter| {
230 let u = if closed_u && wrap {
231 parameter[0]
232 } else {
233 parameter[0].clamp(u0, u1)
234 };
235 let v = if closed_v && wrap {
236 parameter[1]
237 } else {
238 parameter[1].clamp(v0, v1)
239 };
240 Vec3::new(u, v, 0.0)
241 })
242 .collect::<Vec<_>>();
243 interpolate_curve(&points, 3usize.min(points.len() - 1), curve_parameters)
244}
245
246pub fn build_pcurve_on_surface(
252 surface: &NurbsSurface,
253 curve: &NurbsCurve,
254) -> Result<NurbsCurve, String> {
255 let [t0, t1] = curve.domain()?;
256 if surface.is_affine()? {
257 let ([u0, _], [v0, _]) = surface_domains(surface)?;
258 let (origin, du, dv) = surface.deriv1(u0, v0)?;
259 let uu = du.dot(du);
260 let uv = du.dot(dv);
261 let vv = dv.dot(dv);
262 let determinant = uu * vv - uv * uv;
263 if determinant.abs() <= 1e-18 {
264 return Err("build_pcurve_on_surface: singular affine parameterization".into());
265 }
266 let control_points = curve
267 .control_points
268 .iter()
269 .map(|control| {
270 let delta = control.point()?.sub(origin);
271 let along_u = delta.dot(du);
272 let along_v = delta.dot(dv);
273 Ok(Vec4::from_point(
274 Vec3::new(
275 u0 + (along_u * vv - along_v * uv) / determinant,
276 v0 + (along_v * uu - along_u * uv) / determinant,
277 0.0,
278 ),
279 control.w,
280 ))
281 })
282 .collect::<Result<Vec<_>, String>>()?;
283 return NurbsCurve::new(curve.degree, curve.knots.clone(), control_points);
284 }
285
286 let scale = 1.0 + curve.evaluate((t0 + t1) / 2.0)?.length();
287 let drop_tolerance = 1e-4 * scale;
288 let endpoint_tolerance = 1e-3 * scale;
289 let refinement_tolerance = 1e-3f64.min(1e-4 * scale);
290 let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
291 let invert =
292 |fraction: f64| invert_checked(surface, curve.evaluate(t0 + (t1 - t0) * fraction)?);
293
294 let mut parameters = Vec::with_capacity(25);
295 let mut raw_surface_parameters = Vec::with_capacity(25);
296 for index in 0..=24 {
297 let fraction = index as f64 / 24.0;
298 let (parameter, distance) = invert(fraction)?;
299 if distance > drop_tolerance && index != 0 && index != 24 {
300 continue;
301 }
302 if distance > endpoint_tolerance {
303 if std::env::var("BREP_DEBUG_PCURVE").is_ok() {
304 let p3 = curve.evaluate(t0 + (t1 - t0) * fraction).ok();
305 let p0 = curve.evaluate(t0).ok();
306 let p1 = curve.evaluate(t1).ok();
307 eprintln!(
308 "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={:?}",
309 surface.evaluate(parameter[0], parameter[1]).ok()
310 );
311 }
312 return Err(format!(
313 "build_pcurve_on_surface: endpoint projection failed (distance={distance})"
314 ));
315 }
316 parameters.push(fraction);
317 raw_surface_parameters.push(parameter);
318 }
319
320 let mut pcurve = build_interpolant(surface, &raw_surface_parameters, ¶meters)?;
321 const MAX_SAMPLES: usize = 160;
322 for _ in 0..4 {
323 if parameters.len() >= MAX_SAMPLES {
324 break;
325 }
326 let mut inserts = Vec::new();
327 for index in 0..parameters.len() - 1 {
328 if parameters.len() + inserts.len() >= MAX_SAMPLES {
329 break;
330 }
331 let start = parameters[index];
332 let end = parameters[index + 1];
333 if end - start < 1e-3 {
334 continue;
335 }
336 for local_fraction in [0.25, 0.5, 0.75] {
337 if parameters.len() + inserts.len() >= MAX_SAMPLES {
338 break;
339 }
340 let fraction = start + (end - start) * local_fraction;
341 let parameter = pcurve.evaluate(fraction)?;
342 let on_surface =
343 surface.evaluate(parameter.x.clamp(u0, u1), parameter.y.clamp(v0, v1))?;
344 let on_curve = curve.evaluate(t0 + (t1 - t0) * fraction)?;
345 if on_surface.sub(on_curve).length() <= refinement_tolerance {
346 continue;
347 }
348 let (surface_parameter, distance) = invert(fraction)?;
349 if distance <= drop_tolerance {
350 inserts.push((index + 1, fraction, surface_parameter));
351 }
352 }
353 }
354 if inserts.is_empty() {
355 break;
356 }
357 for (at, fraction, surface_parameter) in inserts.into_iter().rev() {
358 parameters.insert(at, fraction);
359 raw_surface_parameters.insert(at, surface_parameter);
360 }
361 pcurve = build_interpolant(surface, &raw_surface_parameters, ¶meters)?;
362 }
363 Ok(pcurve)
364}
365
366pub fn build_pcurve_on_surface_marched(
395 surface: &NurbsSurface,
396 curve: &NurbsCurve,
397) -> Result<NurbsCurve, String> {
398 let global = build_pcurve_on_surface(surface, curve)?;
399 if std::env::var("BREP_SECTION_PCURVE_MARCH").as_deref() == Ok("0") || surface.is_affine()? {
400 return Ok(global);
401 }
402 let [t0, t1] = curve.domain()?;
403 if curve.evaluate(t0)?.sub(curve.evaluate(t1)?).length() <= 1e-6 {
406 return Ok(global);
407 }
408 const SAMPLES: usize = 24;
409 let fractions: Vec<f64> = (0..=SAMPLES).map(|i| i as f64 / SAMPLES as f64).collect();
410 let edge: Vec<Vec3> = fractions
411 .iter()
412 .map(|fraction| curve.evaluate(t0 + (t1 - t0) * fraction))
413 .collect::<Result<Vec<_>, _>>()?;
414 let scale = 1.0 + curve.evaluate((t0 + t1) / 2.0)?.length();
415 let endpoint_tolerance = 1e-3 * scale;
416 let (first, first_distance) = invert_checked(surface, edge[0])?;
417 let (last, last_distance) = invert_checked(surface, edge[SAMPLES])?;
418 if first_distance > endpoint_tolerance || last_distance > endpoint_tolerance {
419 return Ok(global);
420 }
421 let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
422 let u_span = (u1 - u0).abs().max(EPSILON);
423 let v_span = (v1 - v0).abs().max(EPSILON);
424 let (closed_u, closed_v) = surface_closedness(surface)?;
425 let norm_step = |a: [f64; 2], b: [f64; 2]| -> f64 {
426 let mut du = a[0] - b[0];
427 if closed_u {
428 while du > 0.5 * u_span {
429 du -= u_span;
430 }
431 while du < -0.5 * u_span {
432 du += u_span;
433 }
434 }
435 let mut dv = a[1] - b[1];
436 if closed_v {
437 while dv > 0.5 * v_span {
438 dv -= v_span;
439 }
440 while dv < -0.5 * v_span {
441 dv += v_span;
442 }
443 }
444 ((du / u_span).powi(2) + (dv / v_span).powi(2)).sqrt()
445 };
446 let mut raw = vec![first];
448 let mut previous = first;
449 for index in 1..SAMPLES {
450 let seeded =
451 project_point_to_surface_seeded(surface, edge[index], previous[0], previous[1])?;
452 let (_, global_distance) = invert_checked(surface, edge[index])?;
453 if seeded.distance > 4.0 * global_distance + endpoint_tolerance {
457 return Ok(global);
458 }
459 previous = [seeded.u, seeded.v];
460 raw.push(previous);
461 }
462 raw.push(last);
463 let mut steps: Vec<f64> = (1..raw.len())
469 .map(|i| norm_step(raw[i], raw[i - 1]))
470 .collect();
471 let endpoint_gap = steps.pop().unwrap_or(0.0);
472 steps.sort_by(f64::total_cmp);
473 let median = steps
474 .get(steps.len() / 2)
475 .copied()
476 .unwrap_or(0.0)
477 .max(EPSILON);
478 if endpoint_gap > (4.0 * median).max(0.05) {
479 return Ok(global);
480 }
481 let mut parameters = fractions;
485 let mut pcurve = build_interpolant(surface, &raw, ¶meters)?;
486 let refinement_tolerance = 1e-3f64.min(1e-4 * scale);
487 const MAX_SAMPLES: usize = 160;
488 for _ in 0..4 {
489 if parameters.len() >= MAX_SAMPLES {
490 break;
491 }
492 let mut inserts = Vec::new();
493 for index in 0..parameters.len() - 1 {
494 if parameters.len() + inserts.len() >= MAX_SAMPLES {
495 break;
496 }
497 if parameters[index + 1] - parameters[index] < 1e-3 {
498 continue;
499 }
500 for local in [0.25, 0.5, 0.75] {
501 if parameters.len() + inserts.len() >= MAX_SAMPLES {
502 break;
503 }
504 let fraction =
505 parameters[index] + (parameters[index + 1] - parameters[index]) * local;
506 let seed = pcurve.evaluate(fraction)?;
507 let on_curve = curve.evaluate(t0 + (t1 - t0) * fraction)?;
508 let on_surface = surface.evaluate(seed.x.clamp(u0, u1), seed.y.clamp(v0, v1))?;
509 if on_surface.sub(on_curve).length() <= refinement_tolerance {
510 continue;
511 }
512 let seeded = project_point_to_surface_seeded(surface, on_curve, seed.x, seed.y)?;
513 inserts.push((index + 1, fraction, [seeded.u, seeded.v]));
514 }
515 }
516 if inserts.is_empty() {
517 break;
518 }
519 for (at, fraction, uv) in inserts.into_iter().rev() {
520 parameters.insert(at, fraction);
521 raw.insert(at, uv);
522 }
523 pcurve = build_interpolant(surface, &raw, ¶meters)?;
524 }
525 Ok(pcurve)
526}
527
528pub fn build_pcurve_on_surface_range(
533 surface: &NurbsSurface,
534 curve: &NurbsCurve,
535 edge_start: f64,
536 edge_end: f64,
537 forward: bool,
538 tolerance: f64,
539) -> Result<NurbsCurve, String> {
540 build_pcurve_on_surface_range_dense(
541 surface, curve, edge_start, edge_end, forward, tolerance, 64, 3, 513,
542 )
543}
544
545fn align_collapsed_endpoints(surface: &NurbsSurface, raw: &mut [[f64; 2]]) -> Result<(), String> {
546 let anchor = surface.control_points[0][0].point()?;
547 let mut extent = 0.0_f64;
548 for control in surface.control_points.iter().flatten() {
549 extent = extent.max(control.point()?.sub(anchor).length());
550 }
551 let pole_band = (1e-10 * (1.0 + extent)).min(LINEAR_TOLERANCE);
556 for (endpoint, neighbor) in [(0, 1), (raw.len() - 1, raw.len() - 2)] {
557 let point = surface.evaluate(raw[endpoint][0], raw[endpoint][1])?;
558 for axis in 0..2 {
559 let mut candidate = raw[endpoint];
560 candidate[axis] = raw[neighbor][axis];
561 if candidate == raw[endpoint]
562 || surface
563 .evaluate(candidate[0], candidate[1])?
564 .sub(point)
565 .length()
566 > pole_band
567 {
568 continue;
569 }
570 let row = if axis == 0 {
571 surface.iso_curve_v(raw[endpoint][1])?
572 } else {
573 surface.iso_curve_u(raw[endpoint][0])?
574 };
575 let mut collapsed = true;
576 for control in &row.control_points {
577 if control.point()?.sub(point).length() > pole_band {
578 collapsed = false;
579 break;
580 }
581 }
582 if collapsed {
583 raw[endpoint] = candidate;
584 }
585 }
586 }
587 Ok(())
588}
589
590fn repair_branch_jumps(
613 surface: &NurbsSurface,
614 edge_points: &[Vec3],
615 raw: &mut [[f64; 2]],
616 tolerance: f64,
617) -> Result<(), String> {
618 if raw.len() < 3 {
619 return Ok(());
620 }
621 if surface.is_affine()? {
624 return Ok(());
625 }
626 if std::env::var("BREP_NO_PCURVE_REPAIR").is_ok() {
628 return Ok(());
629 }
630 align_collapsed_endpoints(surface, raw)?;
634 let ([u0, u1], [v0, v1]) = surface_domains(surface)?;
635 let u_span = (u1 - u0).abs().max(EPSILON);
636 let v_span = (v1 - v0).abs().max(EPSILON);
637 let (closed_u, closed_v) = surface_closedness(surface)?;
638 let norm_step = |a: [f64; 2], b: [f64; 2]| -> f64 {
642 let mut du = a[0] - b[0];
643 if closed_u {
644 while du > 0.5 * u_span {
645 du -= u_span;
646 }
647 while du < -0.5 * u_span {
648 du += u_span;
649 }
650 }
651 let mut dv = a[1] - b[1];
652 if closed_v {
653 while dv > 0.5 * v_span {
654 dv -= v_span;
655 }
656 while dv < -0.5 * v_span {
657 dv += v_span;
658 }
659 }
660 ((du / u_span).powi(2) + (dv / v_span).powi(2)).sqrt()
661 };
662 const JUMP_THRESHOLD: f64 = 0.2;
666 let count = raw.len();
667
668 let (mut best_start, mut best_len, mut run_start) = (0usize, 1usize, 0usize);
672 for index in 1..count {
673 if norm_step(raw[index], raw[index - 1]) > JUMP_THRESHOLD {
674 if index - run_start > best_len {
675 best_len = index - run_start;
676 best_start = run_start;
677 }
678 run_start = index;
679 }
680 }
681 if count - run_start > best_len {
682 best_len = count - run_start;
683 best_start = run_start;
684 }
685 let (spine_lo, spine_hi) = (best_start, best_start + best_len - 1);
686
687 let reseat = |edge_point: Vec3,
693 previous: [f64; 2],
694 global: [f64; 2]|
695 -> Result<[f64; 2], String> {
696 let global_step = norm_step(global, previous);
697 if global_step <= JUMP_THRESHOLD {
698 return Ok(global);
699 }
700 let seeded =
701 project_point_to_surface_seeded(surface, edge_point, previous[0], previous[1])?;
702 let candidate = [seeded.u, seeded.v];
703 let global_residual = surface
704 .evaluate(global[0], global[1])?
705 .sub(edge_point)
706 .length();
707 let on_surface = seeded.distance <= 4.0 * global_residual + tolerance.max(1e-12);
708 if on_surface && norm_step(candidate, previous) < 0.5 * global_step {
709 if std::env::var("BREP_DEBUG_PCURVE").is_ok() {
710 eprintln!(
711 "pcurve repair: jump {global_step:.4} ({global:?}) -> {:.4} ({candidate:?}) res {:.2e}->{:.2e}",
712 norm_step(candidate, previous),
713 global_residual,
714 seeded.distance
715 );
716 }
717 Ok(candidate)
718 } else {
719 Ok(global)
720 }
721 };
722
723 let mut previous = raw[spine_hi];
737 for index in (spine_hi + 1)..count.saturating_sub(1) {
738 raw[index] = reseat(edge_points[index], previous, raw[index])?;
739 previous = raw[index];
740 }
741 let mut previous = raw[spine_lo];
742 for index in (1..spine_lo).rev() {
743 raw[index] = reseat(edge_points[index], previous, raw[index])?;
744 previous = raw[index];
745 }
746 Ok(())
747}
748
749#[allow(clippy::too_many_arguments)]
762pub fn build_pcurve_on_surface_range_dense(
763 surface: &NurbsSurface,
764 curve: &NurbsCurve,
765 edge_start: f64,
766 edge_end: f64,
767 forward: bool,
768 tolerance: f64,
769 base_samples: usize,
770 refinement_rounds: usize,
771 parameter_cap: usize,
772) -> Result<NurbsCurve, String> {
773 if !(edge_start.is_finite() && edge_end.is_finite() && edge_start < edge_end) {
774 return Err("build_pcurve_on_surface_range: invalid edge interval".into());
775 }
776 let evaluate_edge = |fraction: f64| {
777 let edge_fraction = if forward { fraction } else { 1.0 - fraction };
778 curve.evaluate(edge_start + (edge_end - edge_start) * edge_fraction)
779 };
780 let mut parameters = (0..=base_samples)
781 .map(|index| index as f64 / base_samples as f64)
782 .collect::<Vec<_>>();
783 let mut edge_points = parameters
786 .iter()
787 .map(|fraction| evaluate_edge(*fraction))
788 .collect::<Result<Vec<_>, _>>()?;
789 let mut raw = edge_points
790 .iter()
791 .map(|point| invert_checked(surface, *point).map(|value| value.0))
792 .collect::<Result<Vec<_>, _>>()?;
793 repair_branch_jumps(surface, &edge_points, &mut raw, tolerance)?;
794 let mut pcurve = build_interpolant(surface, &raw, ¶meters)?;
795 for _ in 0..refinement_rounds {
796 let mut inserts = Vec::new();
797 for index in 0..parameters.len() - 1 {
798 if parameters.len() + inserts.len() >= parameter_cap {
799 break;
800 }
801 for local in [0.25, 0.5, 0.75] {
802 let fraction =
803 parameters[index] + (parameters[index + 1] - parameters[index]) * local;
804 let uv = pcurve.evaluate(fraction)?;
805 let represented = surface.evaluate(uv.x, uv.y)?;
806 let edge_point = evaluate_edge(fraction)?;
807 if represented.sub(edge_point).length() <= tolerance {
808 continue;
809 }
810 inserts.push((
811 index + 1,
812 fraction,
813 edge_point,
814 invert_checked(surface, edge_point)?.0,
815 ));
816 }
817 }
818 if inserts.is_empty() {
819 break;
820 }
821 for (index, fraction, edge_point, value) in inserts.into_iter().rev() {
822 parameters.insert(index, fraction);
823 edge_points.insert(index, edge_point);
824 raw.insert(index, value);
825 }
826 repair_branch_jumps(surface, &edge_points, &mut raw, tolerance)?;
830 pcurve = build_interpolant(surface, &raw, ¶meters)?;
831 }
832 Ok(pcurve)
833}
834
835