smlib: Brep::blend_setback, blends with corner setbacks (smlib_blend_setback)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-26 11:14:20 -04:00
parent 2e1c1097ae
commit c2361c000f
2 changed files with 88 additions and 1 deletions
+63 -1
View File
@@ -1,5 +1,5 @@
//! The operations block of `capi/include/smlib.h` (`smlib_boolean`, `smlib_boolean_union_all`, `smlib_boolean2d`, `smlib_fillet`,
//! `smlib_chamfer`, `smlib_blend`, `smlib_fillet_variable`, `smlib_chamfer_sides`, `smlib_shell`, `smlib_offset_faces`,
//! `smlib_chamfer`, `smlib_blend`, `smlib_blend_setback`, `smlib_fillet_variable`, `smlib_chamfer_sides`, `smlib_shell`, `smlib_offset_faces`,
//! `smlib_thicken`, `smlib_draft`, `smlib_merge_faces`, `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`].
@@ -81,6 +81,20 @@ unsafe extern "C" {
out: *mut *mut RawBrep,
out_failed_point: *mut i64,
) -> i32;
fn smlib_blend_setback(
brep: *const RawBrep,
kind: i32,
points: *const f64,
sizes: *const f64,
n_points: i64,
corners: *const f64,
setbacks: *const f64,
n_corners: i64,
tolerance: f64,
out: *mut *mut RawBrep,
out_failed_point: *mut i64,
out_failed_corner: *mut i64,
) -> i32;
fn smlib_fillet_variable(
brep: *const RawBrep,
point: *const f64,
@@ -265,6 +279,54 @@ pub fn blend(kind: i32, b: &Handle, points: &[[f64; 3]], sizes: &[f64], toleranc
})
}
/// `smlib_blend_setback`: `sizes` holds one size per point and `setbacks` one distance per
/// corner (the caller checks the lengths match). A failed pick names its point; a corner that
/// picks no vertex or several comes back with no index.
#[cfg(feature = "native")]
pub fn blend_setback(
kind: i32,
b: &Handle,
points: &[[f64; 3]],
sizes: &[f64],
corners: &[[f64; 3]],
setbacks: &[f64],
tolerance: f64,
) -> Result<Handle, PickError> {
let n = i64::try_from(points.len()).map_err(|_| (ERR_ARG, None))?;
let nc = i64::try_from(corners.len()).map_err(|_| (ERR_ARG, None))?;
if sizes.len() != points.len() || setbacks.len() != corners.len() {
return Err((ERR_ARG, None));
}
let flat = points.as_ptr().cast::<f64>();
let flat_corners = corners.as_ptr().cast::<f64>();
let (mut failed, mut failed_corner) = (-1i64, -1i64);
new_brep(|out| unsafe {
smlib_blend_setback(
b.raw(),
kind,
flat,
sizes.as_ptr(),
n,
flat_corners,
setbacks.as_ptr(),
nc,
tolerance,
out,
&mut failed,
&mut failed_corner,
)
})
.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 blend_setback(_: i32, _: &Handle, _: &[[f64; 3]], _: &[f64], _: &[[f64; 3]], _: &[f64], _: f64) -> Result<Handle, PickError> {
Err((NOT_BUILT, None))
}
/// `smlib_fillet_variable`.
#[cfg(feature = "native")]
pub fn fillet_variable(
+25
View File
@@ -150,6 +150,31 @@ impl Brep {
ffi::ops::blend(kind, &self.0, edges, sizes, tolerance).map(Brep).map_err(pick_error)
}
/// [`Brep::blend`] with corner setbacks: each of `corners` picks the vertex nearest it
/// (within `tolerance`), a corner where every edge is blended, and that corner's patch starts
/// `setbacks[j]` along each of its edges from the vertex (one distance per corner, as SMLib
/// keeps it). A corner SMLib builds no setback on (some edge there not blended, a chamfer) is
/// [`Error::Unsupported`]; a setback as long as an edge there, [`Error::NoRoom`].
pub fn blend_setback(&self, kind: Blend, edges: &[[f64; 3]], sizes: &[f64], corners: &[[f64; 3]], setbacks: &[f64], tolerance: f64) -> Result<Brep> {
init()?;
if edges.len() > MAX_POINTS
|| edges.is_empty()
|| sizes.len() != edges.len()
|| corners.is_empty()
|| corners.len() > MAX_POINTS
|| setbacks.len() != corners.len()
{
return Err(Error::Argument);
}
let kind = match kind {
Blend::Round => ffi::ops::BLEND_ROUND,
Blend::Curvature => ffi::ops::BLEND_CURVATURE,
Blend::Chord => ffi::ops::BLEND_CHORD,
Blend::Chamfer => ffi::ops::BLEND_CHAMFER,
};
ffi::ops::blend_setback(kind, &self.0, edges, sizes, corners, setbacks, tolerance).map(Brep).map_err(pick_error)
}
/// The one edge nearest to `edge` (within `tolerance`) rounded with a radius
/// that varies along it: a clamped B-spline of `degree` (1 to 3) with `knots` and control
/// `radii` over the position along the edge, 0 at its end nearest `start` and 1 at the other