mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 11:05:56 +00:00
solidcraft-smlib: Brep::draft, and SMLIB_REV at the fork's smlib_draft
Flat faces tilted by an angle about the line where each meets a neutral plane, leaning in along the pull, and the faces around them extended or trimmed to meet them (tangency-labs/smlib@e1ac3c3, SMLib's local operation). A face that isn't flat or isn't parallel to the pull is Error::Unsupported. The test drafts a cube side by +-5 degrees, removing or adding the analytic wedge to 1e-6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
be3fc102da38f7537436618dcd01966f02329675
|
||||
e1ac3c31ae1f6c6dae6bd9e0bdfd19952cecff44
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The operations block of `capi/include/smlib.h` (`smlib_boolean`, `smlib_fillet`,
|
||||
//! `smlib_chamfer`, `smlib_blend`, `smlib_fillet_variable`, `smlib_chamfer_sides`, `smlib_shell`, `smlib_offset_faces`,
|
||||
//! `smlib_thicken`, `smlib_brep_solid_count`, `smlib_imprint`; the fork's `capi/src/smlib_ops.cpp`), declared by
|
||||
//! `smlib_thicken`, `smlib_draft`, `smlib_brep_solid_count`, `smlib_imprint`; the fork's `capi/src/smlib_ops.cpp`), declared by
|
||||
//! hand. Part of the FFI boundary, like its parent: called only through
|
||||
//! [`crate::ops`].
|
||||
|
||||
@@ -120,6 +120,18 @@ unsafe extern "C" {
|
||||
out_failed_point: *mut i64,
|
||||
) -> i32;
|
||||
fn smlib_thicken(brep: *const RawBrep, thickness: f64, symmetric: i32, out: *mut *mut RawBrep) -> i32;
|
||||
fn smlib_draft(
|
||||
brep: *const RawBrep,
|
||||
points: *const f64,
|
||||
n_points: i64,
|
||||
tolerance: f64,
|
||||
neutral_origin: *const f64,
|
||||
neutral_normal: *const f64,
|
||||
pull: *const f64,
|
||||
angle: f64,
|
||||
out: *mut *mut RawBrep,
|
||||
out_failed_point: *mut i64,
|
||||
) -> i32;
|
||||
}
|
||||
|
||||
#[cfg(feature = "native")]
|
||||
@@ -169,6 +181,33 @@ pub fn thicken(_: &Handle, _: f64, _: bool) -> Result<Handle, i32> {
|
||||
Err(NOT_BUILT)
|
||||
}
|
||||
|
||||
/// `smlib_draft`: the faces under `points` tilted by `angle` radians about their lines on the
|
||||
/// neutral plane (`origin`, `normal`), leaning in along `pull`.
|
||||
#[cfg(feature = "native")]
|
||||
pub fn draft(
|
||||
b: &Handle,
|
||||
points: &[[f64; 3]],
|
||||
tolerance: f64,
|
||||
origin: &[f64; 3],
|
||||
normal: &[f64; 3],
|
||||
pull: &[f64; 3],
|
||||
angle: f64,
|
||||
) -> Result<Handle, PickError> {
|
||||
let n = i64::try_from(points.len()).map_err(|_| (ERR_ARG, None))?;
|
||||
let flat = points.as_ptr().cast::<f64>();
|
||||
let mut failed = -1i64;
|
||||
new_brep(|out| unsafe { smlib_draft(b.raw(), flat, n, tolerance, origin.as_ptr(), normal.as_ptr(), pull.as_ptr(), angle, out, &mut failed) })
|
||||
.map_err(|s| {
|
||||
let at = (s == ERR_PICK_NONE || s == ERR_PICK_AMBIGUOUS).then(|| usize::try_from(failed).ok()).flatten();
|
||||
(s, at)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "native"))]
|
||||
pub fn draft(_: &Handle, _: &[[f64; 3]], _: f64, _: &[f64; 3], _: &[f64; 3], _: &[f64; 3], _: f64) -> Result<Handle, PickError> {
|
||||
Err((NOT_BUILT, None))
|
||||
}
|
||||
|
||||
/// `smlib_blend`: `sizes` holds one size per point (the caller checks the lengths match).
|
||||
#[cfg(feature = "native")]
|
||||
pub fn blend(kind: i32, b: &Handle, points: &[[f64; 3]], sizes: &[f64], tolerance: f64) -> Result<Handle, PickError> {
|
||||
|
||||
@@ -170,6 +170,27 @@ impl Brep {
|
||||
ffi::ops::thicken(&self.0, thickness, symmetric).map(Brep).map_err(error)
|
||||
}
|
||||
|
||||
/// The flat faces nearest to `faces` (each within `tolerance`) tilted by `angle` radians
|
||||
/// (0 < |angle| < 89.94 degrees) about the line where each meets the neutral plane through
|
||||
/// `neutral_origin` with normal `neutral_normal`, a positive angle leaning them in going along
|
||||
/// `pull`; the faces around them extended or trimmed to meet them. A face that isn't flat, or
|
||||
/// isn't parallel to `pull`, is [`Error::Unsupported`].
|
||||
pub fn draft(
|
||||
&self,
|
||||
faces: &[[f64; 3]],
|
||||
tolerance: f64,
|
||||
neutral_origin: [f64; 3],
|
||||
neutral_normal: [f64; 3],
|
||||
pull: [f64; 3],
|
||||
angle: f64,
|
||||
) -> Result<Brep> {
|
||||
init()?;
|
||||
if faces.is_empty() || faces.len() > MAX_POINTS {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
ffi::ops::draft(&self.0, faces, tolerance, &neutral_origin, &neutral_normal, &pull, angle).map(Brep).map_err(pick_error)
|
||||
}
|
||||
|
||||
fn pick_op(&self, kind: ffi::ops::Pick, points: &[[f64; 3]], tolerance: f64, size: f64, allow_none: bool) -> Result<Brep> {
|
||||
init()?;
|
||||
if points.len() > MAX_POINTS || (points.is_empty() && !allow_none) {
|
||||
|
||||
@@ -336,3 +336,21 @@ fn imprint_cuts_faces_where_a_tool_meets_them() {
|
||||
let far = Brep::make_box([50.0, 0.0, 0.0], [10.0, 10.0, 10.0]).unwrap();
|
||||
assert!(cube.imprint(&far).unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn draft_tilts_a_side_about_its_line_on_the_neutral_plane() {
|
||||
let c = cube(20.0);
|
||||
let deg = std::f64::consts::PI / 180.0;
|
||||
let wedge = 0.5 * 20.0 * 20.0 * (5.0 * deg).tan() * 20.0;
|
||||
let side = [[20.0, 10.0, 10.0]];
|
||||
let up = [0.0, 0.0, 1.0];
|
||||
let leaned = c.draft(&side, 0.1, [0.0; 3], up, up, 5.0 * deg).unwrap();
|
||||
assert!((leaned.volume(ACC).unwrap() - (8000.0 - wedge)).abs() < 1e-6);
|
||||
let out = c.draft(&side, 0.1, [0.0; 3], up, up, -5.0 * deg).unwrap();
|
||||
assert!((out.volume(ACC).unwrap() - (8000.0 + wedge)).abs() < 1e-6);
|
||||
// The top isn't parallel to the pull.
|
||||
assert_eq!(c.draft(&[[10.0, 10.0, 20.0]], 0.1, [0.0; 3], up, up, 5.0 * deg).unwrap_err(), Error::Unsupported);
|
||||
assert_eq!(c.draft(&side, 0.1, [0.0; 3], up, up, 0.0).unwrap_err(), Error::Argument);
|
||||
assert_eq!(c.draft(&[], 0.1, [0.0; 3], up, up, 5.0 * deg).unwrap_err(), Error::Argument);
|
||||
assert_untouched(&c, 8000.0, [6, 12, 8]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user