mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-04 02:55:54 +00:00
solidcraft-smlib: Brep::split_faces and Brep::split_body on the fork's split shims
split_faces cuts faces (SMLib face order) where ssi::Surface tools meet them and says per face how many faces it became; split_body cuts a solid in two by a surface, the part behind it and the part in front. SMLIB_REV moves to the fork commit that adds smlib_split_faces and smlib_split_body. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
This commit is contained in:
@@ -191,6 +191,7 @@ pub mod flat;
|
||||
pub mod history;
|
||||
pub mod measure;
|
||||
pub mod ops;
|
||||
pub mod split;
|
||||
pub mod ssi;
|
||||
pub mod sweeps;
|
||||
pub mod trim;
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
//! The split block of `capi/include/smlib.h` (`smlib_split_faces`, `smlib_split_body`; the
|
||||
//! fork's `capi/src/smlib_split.cpp`), declared by hand. Part of the FFI boundary, like its
|
||||
//! parent: called only through [`crate::split`].
|
||||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use super::Handle;
|
||||
#[cfg(not(feature = "native"))]
|
||||
use super::NOT_BUILT;
|
||||
use super::ssi::SurfaceHandle;
|
||||
#[cfg(feature = "native")]
|
||||
use super::{ERR_ARG, ERR_EMPTY, OK, RawBrep, new_brep, ssi::RawSurface};
|
||||
|
||||
/// `SMLIB_SPLIT_MAX_FACES`, `SMLIB_SPLIT_MAX_TOOLS`.
|
||||
pub const MAX_FACES: usize = 100_000;
|
||||
pub const MAX_TOOLS: usize = 4096;
|
||||
|
||||
#[cfg(feature = "native")]
|
||||
unsafe extern "C" {
|
||||
fn smlib_split_faces(
|
||||
body: *const RawBrep,
|
||||
faces: *const i64,
|
||||
n_faces: i64,
|
||||
tools: *const *const RawSurface,
|
||||
n_tools: i64,
|
||||
out: *mut *mut RawBrep,
|
||||
made: *mut i64,
|
||||
) -> i32;
|
||||
fn smlib_split_body(body: *const RawBrep, tool: *const RawSurface, back: *mut *mut RawBrep, front: *mut *mut RawBrep) -> i32;
|
||||
}
|
||||
|
||||
/// `body` with `faces` cut by `tools` in turn, and per face how many faces it became besides
|
||||
/// itself.
|
||||
#[cfg(feature = "native")]
|
||||
pub fn split_faces(body: &Handle, faces: &[i64], tools: &[&SurfaceHandle]) -> Result<(Handle, Vec<i64>), i32> {
|
||||
if faces.is_empty() || faces.len() > MAX_FACES || tools.is_empty() || tools.len() > MAX_TOOLS {
|
||||
return Err(ERR_ARG);
|
||||
}
|
||||
let raw: Vec<*const RawSurface> = tools.iter().map(|t| t.raw()).collect();
|
||||
let mut made = vec![0i64; faces.len()];
|
||||
let n = i64::try_from(faces.len()).map_err(|_| ERR_ARG)?;
|
||||
let m = i64::try_from(raw.len()).map_err(|_| ERR_ARG)?;
|
||||
// SAFETY: the arrays outlive the call and hold `n` and `m` entries; the handles are live.
|
||||
let h = new_brep(|out| unsafe { smlib_split_faces(body.raw(), faces.as_ptr(), n, raw.as_ptr(), m, out, made.as_mut_ptr()) })?;
|
||||
Ok((h, made))
|
||||
}
|
||||
|
||||
/// The parts of `body` behind and in front of `tool`.
|
||||
#[cfg(feature = "native")]
|
||||
pub fn split_body(body: &Handle, tool: &SurfaceHandle) -> Result<(Handle, Handle), i32> {
|
||||
let mut back: *mut RawBrep = std::ptr::null_mut();
|
||||
let mut front: *mut RawBrep = std::ptr::null_mut();
|
||||
// SAFETY: the handles are live; the out-pointers are written only on success.
|
||||
let s = unsafe { smlib_split_body(body.raw(), tool.raw(), &mut back, &mut front) };
|
||||
let (b, f) = (std::ptr::NonNull::new(back), std::ptr::NonNull::new(front));
|
||||
match (s, b, f) {
|
||||
(OK, Some(b), Some(f)) => Ok((Handle(b), Handle(f))),
|
||||
(OK, b, f) => {
|
||||
// Free whichever came back alone.
|
||||
drop(b.map(Handle));
|
||||
drop(f.map(Handle));
|
||||
Err(ERR_EMPTY)
|
||||
}
|
||||
(s, _, _) => Err(s),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "native"))]
|
||||
pub fn split_faces(_: &Handle, _: &[i64], _: &[&SurfaceHandle]) -> Result<(Handle, Vec<i64>), i32> {
|
||||
Err(NOT_BUILT)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "native"))]
|
||||
pub fn split_body(_: &Handle, _: &SurfaceHandle) -> Result<(Handle, Handle), i32> {
|
||||
Err(NOT_BUILT)
|
||||
}
|
||||
@@ -110,6 +110,13 @@ unsafe impl Sync for SurfaceHandle {}
|
||||
unsafe impl Send for ResultHandle {}
|
||||
unsafe impl Sync for ResultHandle {}
|
||||
|
||||
impl SurfaceHandle {
|
||||
#[cfg_attr(not(feature = "native"), allow(dead_code))]
|
||||
pub(crate) fn raw(&self) -> *const RawSurface {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SurfaceHandle {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(feature = "native")]
|
||||
|
||||
@@ -23,6 +23,7 @@ pub mod history;
|
||||
pub mod import;
|
||||
mod measure;
|
||||
mod ops;
|
||||
mod split;
|
||||
pub mod ssi;
|
||||
pub mod sweeps;
|
||||
pub mod trim;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//! Split Face and Split Body (`capi/include/smlib.h`, "Split"): faces cut where tool surfaces
|
||||
//! meet them, and a solid cut in two by a surface. Tools are [`crate::ssi::Surface`]s, each
|
||||
//! taken over its whole domain.
|
||||
|
||||
use crate::ssi::Surface;
|
||||
use crate::{Brep, Error, Result, error, ffi, init};
|
||||
|
||||
impl Brep {
|
||||
/// `self` with faces `faces` (indices in SMLib's face order, distinct) cut where each of
|
||||
/// `tools` meets them, the tools in turn, each cutting the pieces the earlier ones left; and
|
||||
/// for each face how many faces it became besides itself (0: no tool split it). `None` when
|
||||
/// no face is split. Tagged operands ([`Brep::tagged`]) give the pieces' history.
|
||||
pub fn split_faces(&self, faces: &[usize], tools: &[&Surface]) -> Result<Option<(Brep, Vec<usize>)>> {
|
||||
init()?;
|
||||
if faces.is_empty() || faces.len() > ffi::split::MAX_FACES || tools.is_empty() || tools.len() > ffi::split::MAX_TOOLS {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let faces: Vec<i64> = faces.iter().map(|f| i64::try_from(*f).map_err(|_| Error::Argument)).collect::<Result<_>>()?;
|
||||
let handles: Vec<&ffi::ssi::SurfaceHandle> = tools.iter().map(|t| t.handle()).collect();
|
||||
match ffi::split::split_faces(&self.0, &faces, &handles) {
|
||||
Ok((h, made)) => Ok(Some((Brep(h), made.into_iter().map(|m| usize::try_from(m).unwrap_or(0)).collect()))),
|
||||
Err(ffi::ops::ERR_NO_CUT) => Ok(None),
|
||||
Err(s) => Err(error(s)),
|
||||
}
|
||||
}
|
||||
|
||||
/// The manifold solid `self` cut in two by `tool` (whose domain must reach past it): the
|
||||
/// part behind the surface (its normal points away) and the part in front, each closed by
|
||||
/// the tool's piece inside the body and each possibly several solids. A tool that misses
|
||||
/// leaves both parts the whole body, with no face from the tool (tag `self` to tell); `None`
|
||||
/// when a side is left empty.
|
||||
pub fn split_body(&self, tool: &Surface) -> Result<Option<(Brep, Brep)>> {
|
||||
init()?;
|
||||
match ffi::split::split_body(&self.0, tool.handle()) {
|
||||
Ok((b, f)) => Ok(Some((Brep(b), Brep(f)))),
|
||||
Err(ffi::ops::ERR_NO_CUT) => Ok(None),
|
||||
Err(s) => Err(error(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,10 @@ fn int(n: usize) -> Result<i32> {
|
||||
}
|
||||
|
||||
impl Surface {
|
||||
pub(crate) fn handle(&self) -> &ffi::ssi::SurfaceHandle {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// `origin + u*x + v*y` (x, y the axes made unit), for u in `u`, v in `v`.
|
||||
pub fn plane(origin: [f64; 3], xaxis: [f64; 3], yaxis: [f64; 3], u: [f64; 2], v: [f64; 2]) -> Result<Surface> {
|
||||
init()?;
|
||||
|
||||
Reference in New Issue
Block a user