kernel: project_onto_face, sketch curves projected onto a face as SMLib's curves

project_onto_face projects sketch segments along a direction onto the face of a
body at a point through Brep::project_curves (smlib_project_curves), each
projected piece the B-spline SMLib made, returned as a record curve
(RationalSpline) on its interval, within a millionth of the body's size.

lib.rs (coordinator): export point_path::project_onto_face.
(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:11 -04:00
parent 453d6395e6
commit 7b5d732e38
2 changed files with 53 additions and 2 deletions
+16
View File
@@ -155,6 +155,22 @@ pub fn edge_path(body: &Body, edge: usize) -> Result<Vec<PathSeg>> {
curve_path(&e.curve, e.interval)
}
/// The sketch segments `segs` on `plane` projected along `direction` (pointing from them toward
/// the face) onto the face of `body` at `face_at`, as the record curves SMLib's projection makes
/// (one per piece of the projection that stays on the face), each on its interval.
pub fn project_onto_face(
body: &Body,
face_at: Vec3,
plane: &Plane,
segs: &[solidcraft_geom::Seg2],
direction: Vec3,
) -> Result<Vec<(brep::Curve, [f64; 2])>> {
if !(face_at.is_finite() && direction.is_finite() && direction.len() > 1e-12) || segs.is_empty() || segs.len() > 4096 {
return Err(KernelError::Invalid("curve projection: a point, a direction and 1…4096 curves".into()));
}
crate::smlib_sweeps::project_onto_face(body, face_at, plane, segs, direction)
}
/// Most control points a record curve given to [`curve_path`] may have.
const MAX_CURVE_POINTS: usize = 100_000;
+37 -2
View File
@@ -72,7 +72,8 @@ pub(crate) struct Draft {
#[cfg(feature = "smlib")]
pub(crate) use imp::{
body_from_record, curve, extrude, loft, loft_rails, loft_to_point, path_curve, revolve, screw, sphere, sweep, sweep_controlled,
body_from_record, curve, extrude, loft, loft_rails, loft_to_point, path_curve, project_onto_face, revolve, screw, sphere, sweep,
sweep_controlled,
};
/// The error of an SMLib operation in a build without SMLib.
@@ -91,6 +92,9 @@ mod stubs {
pub(crate) fn sphere(_: Vec3, _: f64) -> Result<Body> {
Err(not_built())
}
pub(crate) fn project_onto_face(_: &Body, _: Vec3, _: &Plane, _: &[Seg2], _: Vec3) -> Result<Vec<(solidcraft_geom::brep::Curve, [f64; 2])>> {
Err(not_built())
}
pub(crate) fn extrude(_: &Plane, _: &[Piece], _: Option<&Region2>, _: f64, _: f64, _: f64, _: Option<Draft>) -> Result<Body> {
Err(not_built())
}
@@ -117,7 +121,9 @@ mod stubs {
}
}
#[cfg(not(feature = "smlib"))]
pub(crate) use stubs::{body_from_record, extrude, loft, loft_rails, loft_to_point, revolve, screw, sphere, sweep, sweep_controlled};
pub(crate) use stubs::{
body_from_record, extrude, loft, loft_rails, loft_to_point, project_onto_face, revolve, screw, sphere, sweep, sweep_controlled,
};
/// Refuses what no SMLib sweep does, before any work.
pub(crate) fn refuse(what: &str) -> KernelError {
@@ -208,6 +214,35 @@ mod imp {
body_of(&brep, what)
}
/// The sketch segments `segs` on `plane` projected along `direction` onto the face of
/// `body` at `face_at`: SMLib's projection (smlib_project_curves), each projected curve the
/// B-spline SMLib made, as a record curve on its interval, within a millionth of the body's
/// size of the true projection.
pub(crate) fn project_onto_face(
body: &Body,
face_at: Vec3,
plane: &Plane,
segs: &[Seg2],
direction: Vec3,
) -> Result<Vec<(solidcraft_geom::brep::Curve, [f64; 2])>> {
let c = segs.iter().map(|s| curve(plane, s)).collect::<Result<Vec<_>>>()?;
let size = body.size().max(1e-9);
let out = body.smlib()?.project_curves(p(face_at), size * 1e-3 + 1e-6, &c, p(direction), (size * 1e-6).max(1e-6)).map_err(|e| match e {
solidcraft_smlib::Error::Empty => KernelError::Invalid("the curve projects to nothing on the face".into()),
solidcraft_smlib::Error::NoMatch { .. } => KernelError::Invalid("no face of the body at the projection's point".into()),
e => failed("curve projection", e),
})?;
out.into_iter()
.map(|(c, interval)| match c {
Curve::Nurbs { points, knots, .. } => {
let homogeneous = points.iter().map(|q| [q[0] * q[3], q[1] * q[3], q[2] * q[3], q[3]]).collect();
Ok((solidcraft_geom::brep::Curve::RationalSpline { knots, homogeneous }, interval))
}
_ => Err(failed("curve projection", "a projected curve is not a B-spline")),
})
.collect()
}
/// SMLib's sphere about `center`, each face swept from the arc (segment 0) of the half disc
/// a sphere revolves.
pub(crate) fn sphere(center: Vec3, radius: f64) -> Result<Body> {