pub fn predictor_corrector_step(
surf_a: &NurbSurface3D,
surf_b: &NurbSurface3D,
point: Vector3,
dir: Vector3,
u_a: Scalar,
v_a: Scalar,
u_b: Scalar,
v_b: Scalar,
step_size: Scalar,
) -> GeopResult<(Vector3, Scalar, Scalar, Scalar, Scalar)>Expand description
One predictor-corrector step from point (with known (u_a, v_a) /
(u_b, v_b) on surf_a/surf_b) along dir: predicts point + dir * step_size, then solves for the point that lies on both surfaces at
once by damped Newton, returning the union of the two surfaces’
enclosures of it plus each surface’s own resulting (u, v).
The corrector solves one system in the four unknowns (u_a, v_a, u_b, v_b) rather than projecting onto each surface separately. Separate
projections each enforce their own surface’s constraint while ignoring
the other’s, so they land a full prediction-error apart and stay there
no matter how good the predictor is — and that error is not small: the
marching point inherits the start vertex’s own interval width (a vertex
found by curve_surface_intersect is located only to within
min_subdivision_size), which sharpen turns into a concrete offset.
Three of the four residuals are S_a(u_a, v_a) - S_b(u_b, v_b) = 0.
Those alone are underdetermined by exactly one degree of freedom — the
intersection curve is a curve, so sliding along it satisfies them
equally well — and that free direction is what the damper has to pin
down. It pins it biased to the last direction: the fourth residual
holds the solution on the plane through the prediction with normal
dir, so the corrector may move freely onto the curve but not drift
along it, and the step stays the one the predictor asked for.
A step that would leave a patch is not rejected: project clamps into
the domain, so such a step lands exactly on the patch boundary — which
is precisely where an intersection curve ends, and therefore where the
vertex it must connect to lives. Refusing it instead stops the march a
full step_size short of that vertex, which is how a trace ends up
never reaching anything. Deciding whether a direction is viable at all
is face_contains’ job on the first step (see a trace’s), not
something for every step to re-litigate via raw domain bounds.