feat: integrate SimpleCADAPI 2.0.2 CAD workflows
This commit is contained in:
@@ -21,6 +21,16 @@ import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
if str(SCRIPT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from designir_codec import (
|
||||
DesignIRCodecError,
|
||||
read_designir,
|
||||
write_designir,
|
||||
)
|
||||
|
||||
from build123d import (
|
||||
Align,
|
||||
Box,
|
||||
@@ -113,8 +123,8 @@ class DesignIRError(ValueError):
|
||||
|
||||
def read_json(path: Path) -> dict[str, Any]:
|
||||
try:
|
||||
value = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
value = read_designir(path)
|
||||
except (OSError, json.JSONDecodeError, DesignIRCodecError) as exc:
|
||||
raise DesignIRError(f"Cannot read JSON {path}: {exc}") from exc
|
||||
if not isinstance(value, dict):
|
||||
raise DesignIRError("DesignIR must be a JSON object")
|
||||
@@ -122,6 +132,9 @@ def read_json(path: Path) -> dict[str, Any]:
|
||||
|
||||
|
||||
def write_json(path: Path, value: dict[str, Any]) -> None:
|
||||
if path.name.endswith(".designir.json") and "surface_layer" in value:
|
||||
write_designir(path, value)
|
||||
return
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
@@ -587,13 +600,26 @@ def build_simplecad_shape(payload: dict[str, Any]) -> tuple[Any, str]:
|
||||
raise DesignIRError("SimpleCADAPI backend is unavailable") from exc
|
||||
|
||||
values = parameter_values(payload)
|
||||
shape: Any = None
|
||||
z_min: float | None = None
|
||||
z_max: float | None = None
|
||||
with scad.GraphSession() as session:
|
||||
|
||||
def semantic_tag(value: object, *, prefix: str = "feature") -> str:
|
||||
token = re.sub(r"[^a-z0-9]+", ".", str(value).lower()).strip(".")
|
||||
return f"{prefix}.{token or 'unnamed'}"
|
||||
|
||||
graph_id = re.sub(
|
||||
r"[^a-zA-Z0-9_.-]+",
|
||||
"_",
|
||||
str(payload.get("model_id") or payload.get("family") or "designir_model"),
|
||||
).strip("._-") or "designir_model"
|
||||
|
||||
@scad.model(graph_id=graph_id)
|
||||
def compile_model() -> Any:
|
||||
shape: Any = None
|
||||
z_min: float | None = None
|
||||
z_max: float | None = None
|
||||
for feature in payload["features"]:
|
||||
operation = feature["operation"]
|
||||
axis = feature_axis(payload, feature)
|
||||
feature_tag = semantic_tag(feature["id"])
|
||||
if operation in {"extrude_circle", "add_cylinder"}:
|
||||
radius = resolve_value(
|
||||
feature["radius"], values, f"{feature['id']}.radius"
|
||||
@@ -612,11 +638,13 @@ def build_simplecad_shape(payload: dict[str, Any]) -> tuple[Any, str]:
|
||||
height=height,
|
||||
bottom_face_center=point,
|
||||
axis=tuple(axis),
|
||||
tag_prefix=feature_tag,
|
||||
result_tag=f"{feature_tag}.primitive",
|
||||
)
|
||||
shape = (
|
||||
tool
|
||||
if shape is None
|
||||
else scad.union_rsolid([shape, tool], clean=True)
|
||||
else scad.union_rsolid(shape, tool, clean=True)
|
||||
)
|
||||
z_min = point[2] if z_min is None else min(z_min, point[2])
|
||||
z_max = (
|
||||
@@ -649,11 +677,13 @@ def build_simplecad_shape(payload: dict[str, Any]) -> tuple[Any, str]:
|
||||
height=depth,
|
||||
depth=height,
|
||||
bottom_face_center=point,
|
||||
tag_prefix=feature_tag,
|
||||
result_tag=f"{feature_tag}.primitive",
|
||||
)
|
||||
shape = (
|
||||
tool
|
||||
if shape is None
|
||||
else scad.union_rsolid([shape, tool], clean=True)
|
||||
else scad.union_rsolid(shape, tool, clean=True)
|
||||
)
|
||||
z_min = point[2] if z_min is None else min(z_min, point[2])
|
||||
z_max = (
|
||||
@@ -709,17 +739,24 @@ def build_simplecad_shape(payload: dict[str, Any]) -> tuple[Any, str]:
|
||||
height=cutter_height,
|
||||
bottom_face_center=(x, y, z_min - 1.0),
|
||||
axis=(0.0, 0.0, 1.0),
|
||||
tag_prefix=f"{feature_tag}.tool.item{index + 1}",
|
||||
result_tag=f"{feature_tag}.tool.item{index + 1}.solid",
|
||||
)
|
||||
for x, y in centers
|
||||
for index, (x, y) in enumerate(centers)
|
||||
]
|
||||
shape = scad.cut_rsolid(
|
||||
shape, cutters, skip_non_intersecting=False
|
||||
shape, *cutters, skip_non_intersecting=False
|
||||
)
|
||||
shape = scad.apply_tag(shape=shape, tag=f"{feature_tag}.result")
|
||||
print(
|
||||
f"simplecad grounding {feature['id']}: volume={shape.get_volume():.6f}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
model_json = scad.export_model_json(session=session, indent=2)
|
||||
return scad.capture_result(value=shape)
|
||||
|
||||
result = compile_model()
|
||||
shape = result.value
|
||||
model_json = result.model_json
|
||||
if shape is None or shape.get_volume() <= 0:
|
||||
raise DesignIRError("DesignIR did not produce a valid SimpleCAD solid")
|
||||
return shape, model_json
|
||||
|
||||
Reference in New Issue
Block a user