doc: a curve projected onto a solid's face is SMLib's projection of the curve

A sketch curve linked onto a face (LinkSource::OnSurface) was projected onto
the face's display triangles, so the wire was a polyline on the mesh. On a
solid it is now kernel::project_onto_face of the curve's exact segments, along
the sketch normal turned toward the face: the wire's exact curves are the
B-splines SMLib made, pieces meeting end to end one wire, the points sampled
from them for display. A mesh body, which has only its triangles, is still
projected onto them.

(not built: base in progress)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
This commit is contained in:
jyviko
2026-10-22 07:20:43 -04:00
parent 7b5d732e38
commit 0ef5b910f9
+45 -2
View File
@@ -208,8 +208,18 @@ fn resolve_uncached(
}
let name = st.bodies.get(bi).map(|b| b.name.clone()).unwrap_or_else(|| body.clone());
source = LinkSource::OnSurface { sketch: *sketch, curve: curve.clone(), body: name, at: q };
wires = crate::project3d::onto_face(&mesh, f, &pts, plane.normal());
exact = vec![Vec::new(); wires.len()];
match st.bodies.get(bi).filter(|b| !b.body.is_mesh()) {
// A solid's face: SMLib's projection of the sketch curve itself.
Some(target) => {
let segs = ss.sketch.exact_segs(ci).unwrap_or_else(|| ss.sketch.segs(ci));
(wires, exact) = projected_wires(target, q, ss, &segs, plane.normal(), size)?;
}
// A mesh body has only its triangles: the curve projected onto them.
None => {
wires = crate::project3d::onto_face(&mesh, f, &pts, plane.normal());
exact = vec![Vec::new(); wires.len()];
}
}
Vec::new()
}
LinkSource::Intersection { a, b } => {
@@ -334,6 +344,39 @@ fn edge_curves(body: Option<&crate::ModelBody>, e: usize) -> Vec<WireCurve> {
.unwrap_or_default()
}
/// The sketch segments `segs` of `ss` projected along the sketch normal `normal` (turned toward
/// the face) onto the face of `target` at `at`: SMLib's projection, the pieces that meet end to
/// end joined into one wire each, every wire its points (for display) and its exact curves.
fn projected_wires(
target: &crate::ModelBody,
at: Vec3,
ss: &crate::eval::SolvedSketch,
segs: &[Seg2],
normal: Vec3,
size: f64,
) -> Result<(Vec<Vec<Vec3>>, Vec<Vec<WireCurve>>)> {
let from = segs.first().map(|s| ss.plane.to_world(s.start())).unwrap_or(at);
let toward = if (at - from).dot(normal) < 0.0 { normal * -1.0 } else { normal };
let curves = crate::kernel::project_onto_face(&target.body, at, &ss.plane, segs, toward)?;
let tol = size * 1e-6;
let (mut wires, mut exact): (Vec<Vec<Vec3>>, Vec<Vec<WireCurve>>) = (Vec::new(), Vec::new());
for (curve, interval) in curves {
let pts = crate::kernel::PathPiece::Segs(crate::kernel::curve_path(&curve, interval)?).points();
let (Some(start), Some(_)) = (pts.first().copied(), pts.last()) else { continue };
match (wires.last_mut(), exact.last_mut()) {
(Some(w), Some(e)) if w.last().is_some_and(|end| end.dist(start) <= tol) => {
w.extend(pts.into_iter().skip(1));
e.push(WireCurve { curve, interval });
}
_ => {
wires.push(pts);
exact.push(vec![WireCurve { curve, interval }]);
}
}
}
Ok((wires, exact))
}
/// Curve `ci` of the solved sketch `ss` as its exact pieces in space, in the order its polyline
/// runs; none where a piece has no record curve.
fn sketch_curves(ss: &crate::eval::SolvedSketch, ci: usize) -> Vec<WireCurve> {