forked from 1b-agent/opencadstudio-upstream
Add kernel booleans: union, subtract and intersect for Solid3D
SolidOperation::Boolean runs synchronously, surfaces the kernel's refusal with a plain reason and leaves the operands untouched, consumes them on success unless keep_operands, and records one undo step. The lifecycle test uses planar boxes with exact volumes and covers chaining, refusals, both formats and undo; an ignored test shows a curved refusal reaching the script. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
69047549e7
commit
5a39052397
8 files changed
+320
-1
No files matched your search
@@ -146,6 +146,26 @@ class _Solids:
|
||||
handle = profile.handle if isinstance(profile, _Entity) else int(profile)
|
||||
return self._document.entities[ocs.solid_region(handle, layer, bool(delete_source))]
|
||||
|
||||
def _boolean(self, operation, first, second, layer, keep_operands):
|
||||
a = first.handle if isinstance(first, _Entity) else int(first)
|
||||
b = second.handle if isinstance(second, _Entity) else int(second)
|
||||
return self._document.entities[ocs.solid_boolean(a, b, operation, layer, bool(keep_operands))]
|
||||
|
||||
def union(self, first, second, layer=None, keep_operands=False):
|
||||
"""Join two solids into a new one. The operands are consumed unless
|
||||
`keep_operands`. The kernel may refuse (the error says why) and a
|
||||
refusal changes nothing. Curved operands can take a second or two,
|
||||
and a whole script has a 30 second budget."""
|
||||
return self._boolean("union", first, second, layer, keep_operands)
|
||||
|
||||
def subtract(self, first, second, layer=None, keep_operands=False):
|
||||
"""Remove `second` from `first`, giving a new solid."""
|
||||
return self._boolean("subtract", first, second, layer, keep_operands)
|
||||
|
||||
def intersect(self, first, second, layer=None, keep_operands=False):
|
||||
"""Keep only the volume both solids share, as a new solid."""
|
||||
return self._boolean("intersect", first, second, layer, keep_operands)
|
||||
|
||||
def surface(self, profile, layer=None, delete_source=False):
|
||||
"""Make a plane surface from one closed planar profile."""
|
||||
handle = profile.handle if isinstance(profile, _Entity) else int(profile)
|
||||
|
||||
@@ -475,6 +475,40 @@ mod ocs {
|
||||
.map_err(|error| vm.new_runtime_error(format!("ocs.solid_region: {error}")))
|
||||
}
|
||||
|
||||
/// Combine two solids ("union", "subtract" or "intersect").
|
||||
#[pyfunction]
|
||||
fn solid_boolean(
|
||||
first: u64,
|
||||
second: u64,
|
||||
operation: String,
|
||||
layer: Option<String>,
|
||||
keep_operands: bool,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<u64> {
|
||||
use ocs_plugin_api::host::{SolidBoolean, SolidOperation};
|
||||
let operation = match operation.as_str() {
|
||||
"union" => SolidBoolean::Union,
|
||||
"subtract" => SolidBoolean::Subtract,
|
||||
"intersect" => SolidBoolean::Intersect,
|
||||
other => {
|
||||
return Err(vm.new_value_error(format!("ocs.solid_boolean: unknown operation {other:?}")))
|
||||
}
|
||||
};
|
||||
let result = host_ctx::with_host(|host| {
|
||||
host.solid_operation(SolidOperation::Boolean {
|
||||
first: Handle::new(first),
|
||||
second: Handle::new(second),
|
||||
operation,
|
||||
layer,
|
||||
keep_operands,
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| vm.new_runtime_error("ocs: not running inside a PY_ command".to_owned()))?;
|
||||
result
|
||||
.map(|handle| handle.value())
|
||||
.map_err(|error| vm.new_runtime_error(format!("ocs.solid_boolean: {error}")))
|
||||
}
|
||||
|
||||
/// Build a plane surface from a closed planar profile entity.
|
||||
#[pyfunction]
|
||||
fn solid_surface(source: u64, layer: Option<String>, delete_source: bool, vm: &VirtualMachine) -> PyResult<u64> {
|
||||
|
||||
Reference in new issue
Block a user