* Add a one-surface fill of closed 3D loops to geom
A closed loop of curves that does not lie in a plane has no exact surface;
the usual answer is one smooth face fitted over it. fill_loop does that:
- the loop's best-fit plane (Newell normal), with a frame chosen from the
loop itself so the result does not depend on where the loop starts, its
direction, or where the part sits
- the loop seen along that normal must be simple and must not fold or run
along the normal; otherwise it is refused ("not supported yet"), never
filled with wrong geometry
- one non-rational bicubic B-spline height field over the loop's box,
fitted by least squares to the loop's samples (The NURBS Book 9.4.1,
9.4.3, eq. 9.62/9.65) with a thin-plate smoothness term
lambda*A*int(h_uu^2 + 2 h_uv^2 + h_vv^2), A one grid cell's area, so the
fit is the same at any scale; only planes are reproduced exactly
- span counts per direction (long narrow loops fit), refined until the
loop is within tolerance, else refused with the deviation reached
bspline: bisected span search, basis functions on a stack array, one
clamping helper shared by curves and surfaces, one binomial helper.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J53sR6gEaVt56soFDpTck8
* Patch fills non-planar loops of edges with one smooth face (#250)
surface.patch with edges refused any loop that is not planar ("not
supported yet: patching a loop that is not planar"). It now fills it:
- planar loops still get one exact planar face, but planarity is certified
conservatively: lines by their ends, B-spline/NURBS edges by their control
points, intersection curves by their samples lying on a support plane
within tolerance; a loop that can't be certified is filled instead
- other loops get one face from geom's fill_loop, bounded by the body's own
chained edges, whole (no split): its history is one generated face whose
edges are the body's, and it stitches back into a closed solid
- intersection-curve edges are sampled by a seeded, guarded double
projection shared by the planarity test and the fill (plane_fit)
- the face tolerance is 10x the planarity tolerance (documented: later
operations take the face to contain its edges within it)
- edge picking runs inside the guard; the geom->truck conversion is shared
with the T-spline sheets (spline_conv); the Newell plane fit is shared with
STEP import
Tests: 3- to 8-sided, concave (dart, arrow, plus), flat-corner, two-edge and
imported rims fill as one face and stitch closed (volumes against an
independent integral); finite mesh normals, same result for any pick order
or placement; non-height-field loops and too-fine detail are refused.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J53sR6gEaVt56soFDpTck8
* Test the Patch dialog filling a non-planar loop of picked edges
The Patch dialog's only test used a sketch profile. Picking the four
curved rim edges of a box cut by a sideways cylinder now makes one
surface.patch command whose result is a single smooth face over the rim.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J53sR6gEaVt56soFDpTck8
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
37 lines
1.2 KiB
Rust
37 lines
1.2 KiB
Rust
//! Geometry foundation for SolidCraft: 2D/3D vectors, sketch planes, axis-aligned boxes,
|
|
//! triangle meshes with their measures (volume, area, centroid), and freeform surface maths
|
|
//! (B-spline basis and NURBS surfaces in [`bspline`], one-surface fills of closed loops in
|
|
//! [`fill`], T-splines in [`tspline`]). Units are millimetres.
|
|
//!
|
|
//! This crate has no knowledge of sketches, documents or the B-rep kernel; every other crate
|
|
//! builds on it.
|
|
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
|
#![forbid(unsafe_code)]
|
|
|
|
pub mod bspline;
|
|
pub mod env;
|
|
pub mod fill;
|
|
mod mesh;
|
|
mod plane;
|
|
mod profile;
|
|
pub mod tspline;
|
|
mod vec;
|
|
|
|
pub use mesh::{Aabb3, InsideIndex, Mesh, MeshMeasure};
|
|
pub use plane::Plane;
|
|
pub use profile::{Loop2, MAX_BEZIER_DEGREE, Region2, Seg2, bezier};
|
|
pub use vec::{Vec2, Vec3};
|
|
|
|
/// Geometric tolerance for coincidence tests (mm).
|
|
pub const EPS: f64 = 1e-9;
|
|
/// Linear tolerance used for "same point" decisions in modelling (mm).
|
|
pub const LINEAR_TOL: f64 = 1e-6;
|
|
|
|
/// `x` if finite, else `fallback`.
|
|
pub fn finite_or(x: f64, fallback: f64) -> f64 {
|
|
if x.is_finite() { x } else { fallback }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|