forked from 1b-agent/opencadstudio-upstream
Complete Region: create from a profile, plus tuple and int friendliness
RegionFromProfile reuses the REGION command's kernel calls; doc.solids.region exposes it with layer and delete_source options. The lifecycle test builds regions from real circle and polyline entities and covers refusals, a move, both formats and undo. create_entity now accepts coordinate tuples for point properties and every generated float setter accepts Python ints. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
04cd9e462d
commit
c731d3a9a4
9 files changed
+233
-7
No files matched your search
@@ -406,6 +406,10 @@ fn leaf_to_py(registry: &TypeRegistry, type_id: &str, item_expr: &str) -> String
|
||||
|
||||
/// `value_expr` must already be an owned `PyObjectRef` expression. Result: a `PyResult<T>` expression.
|
||||
fn leaf_from_py(registry: &TypeRegistry, type_id: &str, value_expr: &str) -> String {
|
||||
if type_id == "f64" {
|
||||
// RustPython's f64 conversion rejects ints; scripts write `radius=5`.
|
||||
return format!("py_number_to_f64({value_expr}, vm)");
|
||||
}
|
||||
if type_id == "u64" {
|
||||
// serde reports Rust `usize` as `u64`; the inferred cast serves both.
|
||||
return format!("{value_expr}.try_into_value::<u64>(vm).map(|n| n as _)");
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
"""Thin Python view over OCS host API v7 entity transactions and coverage."""
|
||||
|
||||
def _coordinates(value, width):
|
||||
return dict(zip("xyz"[:width], (float(axis) for axis in value)))
|
||||
|
||||
|
||||
def _coerce_points(kind, properties):
|
||||
"""Let create_entity take (x, y, z) tuples for point properties, as
|
||||
transaction edits already do, by using the property types in the schema."""
|
||||
schema = ocs.entity_coverage(kind)
|
||||
types = {row["name"]: row for row in schema["properties"]} if schema else {}
|
||||
result = {}
|
||||
for name, value in properties.items():
|
||||
row = types.get(name)
|
||||
if row is not None and row["type"] in ("Vector2", "Vector3"):
|
||||
width = 2 if row["type"] == "Vector2" else 3
|
||||
if row["sequence"]:
|
||||
value = [_coordinates(item, width) if isinstance(item, (tuple, list)) else item for item in value]
|
||||
elif isinstance(value, (tuple, list)):
|
||||
value = _coordinates(value, width)
|
||||
result[name] = value
|
||||
return result
|
||||
|
||||
|
||||
class _Entity:
|
||||
def __init__(self, document, handle):
|
||||
object.__setattr__(self, "_document", document)
|
||||
@@ -117,6 +139,13 @@ class _Solids:
|
||||
def pyramid(self, center=(0, 0, 0), radius=1, height=1, sides=4, layer=None):
|
||||
return self._create("pyramid", tuple(center) + (radius, height, sides), layer)
|
||||
|
||||
def region(self, profile, layer=None, delete_source=False):
|
||||
"""Make a planar region from one closed planar profile (a circle,
|
||||
ellipse, closed polyline or spline). Keeps the profile unless
|
||||
`delete_source` is true, and uses the profile's layer by default."""
|
||||
handle = profile.handle if isinstance(profile, _Entity) else int(profile)
|
||||
return self._document.entities[ocs.solid_region(handle, layer, bool(delete_source))]
|
||||
|
||||
def transform(self, entity, matrix):
|
||||
"""Apply a column-major 4x4 rigid transform (16 numbers) in place."""
|
||||
handle = entity.handle if isinstance(entity, _Entity) else int(entity)
|
||||
@@ -151,7 +180,7 @@ class _Document:
|
||||
"""Create one mapped entity and return its live document descriptor."""
|
||||
if "kind" in properties or "handle" in properties:
|
||||
raise ValueError("kind and handle are managed by create_entity")
|
||||
handle = ocs.add(dict(kind=kind, **properties))
|
||||
handle = ocs.add(dict(kind=kind, **_coerce_points(kind, properties)))
|
||||
return self.entities[handle]
|
||||
|
||||
def delete_entity(self, entity):
|
||||
|
||||
@@ -458,6 +458,23 @@ mod ocs {
|
||||
.map_err(|error| vm.new_runtime_error(format!("ocs.solid_create: {error}")))
|
||||
}
|
||||
|
||||
/// Build a planar region from a closed planar profile entity.
|
||||
#[pyfunction]
|
||||
fn solid_region(source: u64, layer: Option<String>, delete_source: bool, vm: &VirtualMachine) -> PyResult<u64> {
|
||||
use ocs_plugin_api::host::SolidOperation;
|
||||
let result = host_ctx::with_host(|host| {
|
||||
host.solid_operation(SolidOperation::RegionFromProfile {
|
||||
source: Handle::new(source),
|
||||
layer,
|
||||
delete_source,
|
||||
})
|
||||
})
|
||||
.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_region: {error}")))
|
||||
}
|
||||
|
||||
/// Apply a column-major 4x4 rigid transform to a solid, body or region.
|
||||
#[pyfunction]
|
||||
fn solid_transform(handle: u64, matrix: Vec<f64>, vm: &VirtualMachine) -> PyResult<u64> {
|
||||
|
||||
Reference in new issue
Block a user