mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 03:51:08 +00:00
kernel: face sections along a direction as SMLib's exact curves
face_section_along cuts a face through a point by the plane that holds the face's exact normal there and a direction (Brep::face_section): the iso curve "along a direction" of a link, as record curves on their intervals, the B-splines SMLib made. The projection and the section share the picking reach, the curve tolerance, the error words and the conversion to record curves. (Intersection links take the base's meeting_edges, SMLib's imprint.) lib.rs (coordinator): export point_path::face_section_along. (not built: base in progress; no error in these files) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
This commit is contained in:
@@ -171,6 +171,27 @@ pub fn project_onto_face(
|
||||
crate::smlib_sweeps::project_onto_face(body, face_at, plane, segs, direction)
|
||||
}
|
||||
|
||||
/// The face `face` of `body` through `at` cut along `along` by the plane through `at` that holds
|
||||
/// the face's normal there (its exact normal from the record) and `along`: SMLib's section curves
|
||||
/// on that face, as record curves on their intervals.
|
||||
pub fn face_section_along(body: &Body, face: usize, at: Vec3, along: Vec3) -> Result<Vec<(brep::Curve, [f64; 2])>> {
|
||||
use solidcraft_geom::brep::eval::SurfaceEval;
|
||||
let f = body.record().faces.get(face).ok_or_else(|| KernelError::Invalid(format!("the body has no face {face}")))?;
|
||||
let eval = SurfaceEval::new(&f.surface).map_err(|e| KernelError::Failed(format!("face section: {e}")))?;
|
||||
let [[u0, u1], [v0, v1]] = f.domain;
|
||||
let size = body.size().max(1e-9);
|
||||
let uv = eval
|
||||
.project(at, Some([0.5 * (u0 + u1), 0.5 * (v0 + v1)]), size * 1e-3 + 1e-6)
|
||||
.ok_or_else(|| KernelError::Invalid("face section: the point is not on the face".into()))?;
|
||||
let (_, du, dv) = eval.derivs(uv[0], uv[1]).ok_or_else(|| KernelError::Failed("face section: the surface does not evaluate there".into()))?;
|
||||
let normal = du
|
||||
.cross(dv)
|
||||
.cross(along)
|
||||
.normalized()
|
||||
.ok_or_else(|| KernelError::Invalid("face section: the direction runs along the face's normal".into()))?;
|
||||
crate::smlib_sweeps::face_section(body, at, at, normal)
|
||||
}
|
||||
|
||||
/// Samples per knot span (and at least this many per pcurve) where the iso curves look for the
|
||||
/// crossings of a face's loops and classify the face's inside.
|
||||
const ISO_SAMPLES: usize = 64;
|
||||
|
||||
@@ -72,7 +72,7 @@ pub(crate) struct Draft {
|
||||
|
||||
#[cfg(feature = "smlib")]
|
||||
pub(crate) use imp::{
|
||||
body_from_record, curve, extrude, loft, loft_rails, loft_to_point, path_curve, project_onto_face, revolve, screw, sphere, sweep,
|
||||
body_from_record, curve, extrude, face_section, loft, loft_rails, loft_to_point, path_curve, project_onto_face, revolve, screw, sphere, sweep,
|
||||
sweep_controlled,
|
||||
};
|
||||
|
||||
@@ -95,6 +95,9 @@ mod stubs {
|
||||
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 face_section(_: &Body, _: Vec3, _: Vec3, _: 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())
|
||||
}
|
||||
@@ -122,7 +125,7 @@ mod stubs {
|
||||
}
|
||||
#[cfg(not(feature = "smlib"))]
|
||||
pub(crate) use stubs::{
|
||||
body_from_record, extrude, loft, loft_rails, loft_to_point, project_onto_face, revolve, screw, sphere, sweep, sweep_controlled,
|
||||
body_from_record, extrude, face_section, loft, loft_rails, loft_to_point, project_onto_face, revolve, screw, sphere, sweep, sweep_controlled,
|
||||
};
|
||||
|
||||
/// Refuses what no SMLib sweep does, before any work.
|
||||
@@ -226,19 +229,51 @@ mod imp {
|
||||
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),
|
||||
})?;
|
||||
let out = body
|
||||
.smlib()?
|
||||
.project_curves(p(face_at), pick_reach(body), &c, p(direction), curve_tolerance(body))
|
||||
.map_err(|e| curves_error("curve projection", e))?;
|
||||
record_curves(out, "curve projection")
|
||||
}
|
||||
|
||||
/// The face of `body` at `face_at` cut by the plane through `plane_point` square to `normal`,
|
||||
/// SMLib's section curves on that face, as record curves on their intervals.
|
||||
pub(crate) fn face_section(body: &Body, face_at: Vec3, plane_point: Vec3, normal: Vec3) -> Result<Vec<(solidcraft_geom::brep::Curve, [f64; 2])>> {
|
||||
let out = body
|
||||
.smlib()?
|
||||
.face_section(p(face_at), pick_reach(body), p(plane_point), p(normal), curve_tolerance(body))
|
||||
.map_err(|e| curves_error("face section", e))?;
|
||||
record_curves(out, "face section")
|
||||
}
|
||||
|
||||
/// How far a point may be from the face it picks: the kernel's picking reach.
|
||||
fn pick_reach(body: &Body) -> f64 {
|
||||
body.size().max(1e-9) * 1e-3 + 1e-6
|
||||
}
|
||||
|
||||
/// How close a curve SMLib makes on a body's faces keeps to the true one: a millionth of the
|
||||
/// body's size.
|
||||
fn curve_tolerance(body: &Body) -> f64 {
|
||||
(body.size() * 1e-6).max(1e-6)
|
||||
}
|
||||
|
||||
fn curves_error(what: &str, e: solidcraft_smlib::Error) -> KernelError {
|
||||
match e {
|
||||
solidcraft_smlib::Error::Empty => KernelError::Invalid(format!("{what}: no curve on the face")),
|
||||
solidcraft_smlib::Error::NoMatch { .. } => KernelError::Invalid(format!("{what}: no face of the body at the point")),
|
||||
e => failed(what, e),
|
||||
}
|
||||
}
|
||||
|
||||
/// SMLib's NURBS curves as record curves (rational splines) on their intervals.
|
||||
fn record_curves(out: Vec<(Curve, [f64; 2])>, what: &str) -> Result<Vec<(solidcraft_geom::brep::Curve, [f64; 2])>> {
|
||||
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")),
|
||||
_ => Err(failed(what, "a curve SMLib made is not a B-spline")),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user