解决合并冲突

This commit is contained in:
2026-09-08 13:58:58 +08:00
28 changed files with 5529 additions and 336 deletions
@@ -5,6 +5,7 @@ import unittest
from pathlib import Path
from engine.cdsl_engine.runtime import rebuild_cdsl
from engine.cdsl_engine.sketch_solver import resolve_profile
def _spline_profile(sketch_id: str, z: float) -> dict:
@@ -28,6 +29,22 @@ def _spline_profile(sketch_id: str, z: float) -> dict:
class LoftGeometryTests(unittest.TestCase):
def test_periodic_bspline_supports_centripetal_parameterization(self):
sketch = {
"id": "profile",
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "analytic_contours", "contours": [{
"role": "outer", "closed": True, "segments": [{
"type": "bspline", "periodic": True, "parameterization": "centripetal",
"start": [0, 0], "end": [0, 0],
"points": [[0, 0], [16, 0], [16, 9], [0, 9], [0, 0]],
}],
}]},
}
resolved = resolve_profile(sketch)
parameters = resolved["contour_regions_mm"][0]["outer"][0]["parameters"]
self.assertEqual(parameters, [0.0, 4.0, 7.0, 11.0, 14.0])
def test_open_bspline_segment_can_close_with_other_contour_edges(self):
cdsl = {
"schema": "cad.cdsl.llm.v1",
@@ -58,6 +75,32 @@ class LoftGeometryTests(unittest.TestCase):
rebuilt = rebuild_cdsl(cdsl, Path(tmp) / "open-bspline-contour.step")
self.assertGreater(rebuilt["volume_mm3"], 1.0)
def test_reversed_bspline_transports_the_parameter_domain(self):
sketch = {
"id": "profile",
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "analytic_contours", "contours": [{
"role": "outer", "closed": True,
"segments": [
{"type": "line", "start": [0, 0], "end": [0, 4]},
{
"type": "bspline", "start": [3, 0], "end": [0, 4],
"points": [[3, 0], [3, 2], [1, 4], [0, 4]],
"parameters": [0, 2, 4, 6],
"start_tangent": [2, 0], "end_tangent": [0, -3],
},
{"type": "line", "start": [3, 0], "end": [0, 0]},
],
}]},
}
resolved = resolve_profile(sketch)
spline = next(edge for edge in resolved["contour_regions_mm"][0]["outer"] if edge["type"] == "bspline")
self.assertEqual(spline["points_mm"], [[0.0, 4.0, 0.0], [1.0, 4.0, 0.0], [3.0, 2.0, 0.0], [3.0, 0.0, 0.0]])
self.assertEqual(spline["parameters"], [0.0, 2.0, 4.0, 6.0])
self.assertEqual(spline["start_tangent_mm"], [0.0, 3.0, 0.0])
self.assertEqual(spline["end_tangent_mm"], [-2.0, 0.0, 0.0])
def test_bspline_loft_and_mirror_replay_produce_step(self):
cdsl = {
"schema": "cad.cdsl.llm.v1",
@@ -2,6 +2,7 @@ from __future__ import annotations
import hashlib
import json
import math
import sys
import tempfile
import unittest
@@ -129,6 +130,133 @@ class EngineRuntimeFoundationTests(unittest.TestCase):
self.assertEqual(len(sketch["contour_regions_mm"]), 2)
self.assertTrue(all(not region["holes"] for region in sketch["contour_regions_mm"]))
def test_analytic_circle_contours_preserve_single_circular_wire_edges(self) -> None:
from cdsl_engine.build123d_adapter import Build123dGeometryAdapter
cdsl = {
"schema": "cad.cdsl.llm.v1",
"geometry": {"sketches": [{
"id": "sketch", "workplane": _workplane(),
"profile": {"type": "analytic_contours", "contours": [
{"role": "outer", "closed": True, "segments": [
{"type": "circle", "center": [-10, 0], "radius_mm": 2},
]},
{"role": "outer", "closed": True, "segments": [
{"type": "circle", "center": [10, 0], "radius_mm": 2},
]},
]},
}]},
"features": [],
}
sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0]
faces = Build123dGeometryAdapter().faces_for_sketch(sketch)
self.assertEqual(len(faces), 2)
self.assertTrue(all(len(face.outer_wire().edges()) == 1 for face in faces))
def test_analytic_ellipse_preserves_its_workplane_orientation_and_volume(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = self._base_block()
cdsl["geometry"]["sketches"][0] = {
"id": "ellipse",
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [0, 0, 1]},
"profile": {"type": "analytic_contours", "contours": [{
"role": "outer", "closed": True, "segments": [{
"type": "ellipse", "center": [0, 0], "major_radius_mm": 5,
"minor_radius_mm": 2, "major_axis": [3 / 5, 4 / 5],
}],
}]},
}
cdsl["features"][0]["sketch_id"] = "ellipse"
cdsl["features"][0]["params"] = {"distance_mm": 4}
sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0]
edge = sketch["contour_regions_mm"][0]["outer"][0]
self.assertEqual(edge["major_axis_mm"], [-0.8, 0.6, 0.0])
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "ellipse.step")
self.assertEqual(result["solid_count"], 1)
self.assertAlmostEqual(result["volume_mm3"], math.pi * 5 * 2 * 4, places=5)
def test_bspline_profile_preserves_endpoint_tangents(self) -> None:
from cdsl_engine.build123d_adapter import Build123dGeometryAdapter
cdsl = {
"schema": "cad.cdsl.llm.v1",
"geometry": {"sketches": [{
"id": "spline", "workplane": _workplane(),
"profile": {"type": "analytic_contours", "contours": [{
"role": "outer", "closed": True, "segments": [
{"type": "line", "start": [0, 0], "end": [4, 0]},
{"type": "bspline", "start": [4, 0], "end": [0, 0],
"points": [[4, 0], [4, 3], [0, 0]],
"start_tangent": [0, 5], "end_tangent": [-5, 0]},
],
}]},
}]},
"features": [],
}
sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0]
spline = next(edge for edge in sketch["contour_regions_mm"][0]["outer"] if edge["type"] == "bspline")
self.assertEqual(spline["start_tangent_mm"], [0.0, 5.0, 0.0])
self.assertEqual(spline["end_tangent_mm"], [-5.0, 0.0, 0.0])
edge = Build123dGeometryAdapter()._wire([spline]).edges()[0]
self.assertAlmostEqual(edge.tangent_at(0).Y, 1.0, places=6)
self.assertAlmostEqual(edge.tangent_at(1).X, -1.0, places=6)
def test_drafted_ellipse_exports_as_a_solid(self) -> None:
from build123d import import_step
from cdsl_engine.runtime import rebuild_cdsl
cdsl = self._base_block()
cdsl["geometry"]["sketches"].append({
"id": "ellipse", "workplane": {"origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "normal": [0, 0, 1]},
"profile": {"type": "analytic_contours", "contours": [{
"role": "outer", "closed": True, "segments": [{
"type": "ellipse", "center": [0, 0], "major_radius_mm": 3,
"minor_radius_mm": 2, "major_axis": [1, 0],
}],
}]},
})
cdsl["features"].append({
"id": "drafted_ellipse", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"],
"params": {"distance_mm": 4, "draft": {"angle_deg": 1, "pull_direction": False}}, "sketch_id": "ellipse",
})
with tempfile.TemporaryDirectory() as directory:
output = Path(directory) / "drafted-ellipse.step"
result = rebuild_cdsl(cdsl, output)
exported = import_step(str(output))
self.assertEqual(result["solid_count"], 1)
self.assertEqual(len(exported.solids()), 1)
self.assertGreater(float(exported.volume), 1000.0)
def test_boolean_bodies_subtracts_explicit_new_body_sources(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "boolean-bodies",
"meta": {"unit": "mm"},
"geometry": {"sketches": [
{"id": "outer", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 10}},
{"id": "inner", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 4}},
]},
"features": [
{"id": "outer_body", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "outer", "params": {"distance_mm": 10, "result_mode": "new_body"}},
{"id": "inner_body", "atomic_id": "extrude_add_blind", "depends_on": ["outer_body"], "sketch_id": "inner", "params": {"distance_mm": 10, "result_mode": "new_body"}},
{"id": "cut", "atomic_id": "boolean_bodies", "depends_on": ["outer_body", "inner_body"], "params": {"operation": "subtract", "target_feature_ids": ["outer_body"], "tool_feature_ids": ["inner_body"], "keep_tools": False}},
],
}
with tempfile.TemporaryDirectory() as tmp:
rebuilt = rebuild_cdsl(cdsl, Path(tmp) / "boolean.step")
self.assertEqual(rebuilt["solid_count"], 1)
self.assertAlmostEqual(rebuilt["volume_mm3"], math.pi * (10 ** 2 - 4 ** 2) * 10)
self.assertEqual([item["feature_id"] for item in rebuilt["feature_results"]], ["outer_body", "inner_body", "cut"])
def test_deferred_reference_is_currently_executable_without_a_sketch(self) -> None:
cdsl = {
"schema": "cad.cdsl.llm.v1",
@@ -598,6 +726,81 @@ class EngineRuntimeFoundationTests(unittest.TestCase):
self.assertAlmostEqual(result["volume_mm3"], 200 - 4 * 3.141592653589793, places=5)
self.assertIn("mirror", [item["feature_id"] for item in result["feature_results"]])
def test_circular_pattern_fuses_face_sharing_new_body_instances(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "circular-fuse",
"meta": {"unit": "mm"},
"geometry": {"sketches": [{
"id": "wedge", "workplane": _workplane(),
"profile": {"type": "polygon", "vertices": [[0, 0], [10, 0], [0, 10]]},
}]},
"features": [
{"id": "wedge_add", "atomic_id": "extrude_add_blind", "depends_on": [],
"params": {"distance_mm": 10, "result_mode": "new_body"}, "sketch_id": "wedge"},
{"id": "wedge_pattern", "atomic_id": "pattern_circular", "depends_on": ["wedge_add"], "params": {
"source_feature_ids": ["wedge_add"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]},
"pattern_count": 4, "sweep_angle_deg": 360,
}},
],
}
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "circular-fuse.step")
self.assertEqual(result["solid_count"], 1)
self.assertAlmostEqual(result["volume_mm3"], 2000.0, places=5)
def test_circular_pattern_skips_explicitly_deleted_copy_instances(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "circular-delete-copy",
"meta": {"unit": "mm"},
"geometry": {"sketches": [{
"id": "wedge", "workplane": _workplane(),
"profile": {"type": "polygon", "vertices": [[0, 0], [10, 0], [0, 10]]},
}]},
"features": [
{"id": "wedge_add", "atomic_id": "extrude_add_blind", "depends_on": [],
"params": {"distance_mm": 10, "result_mode": "new_body"}, "sketch_id": "wedge"},
{"id": "wedge_pattern", "atomic_id": "pattern_circular", "depends_on": ["wedge_add"], "params": {
"source_feature_ids": ["wedge_add"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]},
"pattern_count": 4, "sweep_angle_deg": 360, "excluded_instance_indices": [2],
}},
],
}
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "circular-delete-copy.step")
self.assertAlmostEqual(result["volume_mm3"], 1500.0, places=5)
def test_mirror_pattern_can_union_the_active_body(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "mirror-active-body",
"meta": {"unit": "mm"},
"geometry": {"sketches": [
{"id": "base", "workplane": _workplane(), "profile": _rectangle([-5, -5], [5, 5])},
{"id": "half_cut", "workplane": _workplane(), "profile": _rectangle([-5, 0], [5, 5])},
{"id": "hole", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, -2], "radius_mm": 1}},
]},
"features": [
{"id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]}}, "execution_status": "supported"},
{"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": ["mirror_plane"], "params": {"distance_mm": 2}, "sketch_id": "base", "execution_status": "supported"},
{"id": "half_remove", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 2}, "sketch_id": "half_cut", "execution_status": "supported"},
{"id": "hole_remove", "atomic_id": "extrude_cut_blind", "depends_on": ["half_remove"], "params": {"distance_mm": 2}, "sketch_id": "hole", "execution_status": "supported"},
{
"id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["hole_remove", "mirror_plane"],
"params": {"source_feature_ids": ["base_add"], "mirror_current_body": True, "mirror_plane": {"kind": "plane", "owner_feature_id": "mirror_plane"}},
"selectors": [{"kind": "plane", "owner_feature_id": "mirror_plane"}], "execution_status": "supported",
},
],
}
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "mirror-active-body.step")
self.assertAlmostEqual(result["volume_mm3"], 200 - 4 * 3.141592653589793, places=5)
self.assertEqual(result["solid_count"], 1)
def test_mirrored_local_circle_preserves_its_reflected_world_position(self) -> None:
from cdsl_engine.build123d_adapter import Build123dGeometryAdapter
from cdsl_engine.runtime import _mirrored_sketch
@@ -622,6 +825,120 @@ class EngineRuntimeFoundationTests(unittest.TestCase):
self.assertAlmostEqual(center.Y, 3.0)
self.assertAlmostEqual(center.Z, 0.0)
def test_circle_profile_retains_one_topological_circle_edge(self) -> None:
from cdsl_engine.build123d_adapter import Build123dGeometryAdapter
cdsl = {
"schema": "cad.cdsl.llm.v1",
"geometry": {"sketches": [{
"id": "circle", "workplane": _workplane(),
"profile": {"type": "circle", "center": [2, 3], "radius_mm": 1},
}]},
"features": [],
}
sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0]
self.assertNotIn("contour_edges_mm", sketch)
face = Build123dGeometryAdapter().faces_for_sketch(sketch)[0]
self.assertEqual(len(face.outer_wire().edges()), 1)
def test_new_body_mode_preserves_an_overlapping_result_body(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = self._base_block()
cdsl["geometry"]["sketches"].append({
"id": "overlap", "workplane": _workplane(),
"profile": _rectangle([-2, -5], [8, 5]),
})
cdsl["features"].append({
"id": "second_body", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"],
"params": {"distance_mm": 10, "result_mode": "new_body"}, "sketch_id": "overlap",
})
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "two-bodies.step")
self.assertEqual(result["solid_count"], 2)
def test_sweep_add_builds_a_solid_from_a_closed_profile_and_bspline_path(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
profile_plane = {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]}
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "sweep-add",
"meta": {"unit": "mm"},
"geometry": {"sketches": [{
"id": "profile", "workplane": profile_plane,
"profile": {"type": "circle", "radius_mm": 2},
}]},
"features": [{
"id": "sweep", "atomic_id": "sweep_add", "depends_on": [], "sketch_id": "profile",
"params": {"path": {
"workplane": _workplane(),
"segment": {
"type": "bspline", "start": [0, 0], "end": [0, 20],
"points": [[0, 0], [0, 10], [0, 20]],
"start_tangent": [0, 10], "end_tangent": [0, 10],
},
}},
}],
}
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "sweep.step")
self.assertEqual(result["solid_count"], 1)
self.assertAlmostEqual(result["volume_mm3"], 80 * math.pi, delta=1e-4)
def test_circular_pattern_rotates_a_sweep_profile_and_path(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "sweep-pattern",
"meta": {"unit": "mm"},
"geometry": {"sketches": [{
"id": "profile", "workplane": {"origin_mm": [10, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, -1]},
"profile": {"type": "circle", "radius_mm": 1},
}]},
"features": [
{"id": "sweep", "atomic_id": "sweep_add", "depends_on": [], "sketch_id": "profile", "params": {"path": {
"workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]},
"segment": {"type": "line", "start": [10, 0], "end": [10, 10]},
}}},
{"id": "pattern", "atomic_id": "pattern_circular", "depends_on": ["sweep"], "params": {
"source_feature_ids": ["sweep"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]},
"pattern_count": 3, "sweep_angle_deg": 360,
}},
],
}
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "sweep-pattern.step")
self.assertEqual(result["solid_count"], 3)
self.assertAlmostEqual(result["volume_mm3"], 30 * math.pi, delta=1e-4)
def test_shell_removes_a_selected_cap_and_offsets_inward(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
base = self._base_block()
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
baseline = rebuild_cdsl(base, root / "baseline.step")
top = next(
item for item in baseline["topology_records"]
if item["kind"] == "face"
and item["feature_id"] == "base_add"
and item["geometry"].get("surface_type") == "plane"
and item["geometry"].get("normal", [0, 0, 0])[2] > 0.9
)
shelled = deepcopy(base)
shelled["features"].append({
"id": "shell", "atomic_id": "shell", "depends_on": ["base_add"],
"params": {"thickness_mm": 1, "inward": True},
"selectors": [{
"kind": "face", "stable_id": top["record_id"], "snapshot_id": top["record_id"],
"source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add",
"geometry": top["geometry"],
}],
})
result = rebuild_cdsl(shelled, root / "shell.step")
self.assertEqual(result["solid_count"], 1)
self.assertAlmostEqual(result["volume_mm3"], 424.0, places=5)
def test_fillet_and_hole_wizard_use_resolved_face_edge_selectors(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
@@ -983,6 +1300,131 @@ class EngineRuntimeFoundationTests(unittest.TestCase):
self.assertLess(result["volume_mm3"], 1000.0)
self.assertIn("revolve_cut", [item["feature_id"] for item in result["feature_results"]])
def test_revolve_surface_preserves_the_active_solid_and_registers_a_shell(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = self._base_block()
cdsl["geometry"]["sketches"].append({
"id": "surface_profile", "workplane": _workplane(),
"profile": _rectangle([2, 1], [4, 2]),
})
cdsl["features"].append({
"id": "surface_revolve", "atomic_id": "revolve_surface", "depends_on": ["base_add"],
"sketch_id": "surface_profile",
"params": {"angle_deg": 360, "axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]}},
})
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "surface-revolve.step")
surface = next(item for item in result["feature_results"] if item["feature_id"] == "surface_revolve")
self.assertNotIn("body_id", surface)
self.assertEqual(surface["surface_id"], "surface:surface_revolve")
self.assertEqual(result["solid_count"], 1)
self.assertAlmostEqual(result["volume_mm3"], 1000.0)
faces = [
item for item in result["topology_records"]
if item["feature_id"] == "surface_revolve" and item["kind"] == "face"
]
self.assertEqual(len(faces), 4)
def test_extrude_surface_preserves_the_active_solid_and_registers_a_shell(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = self._base_block()
cdsl["geometry"]["sketches"].append({
"id": "surface_profile", "workplane": _workplane(),
"profile": {
"type": "analytic_contours",
"contours": [{
"role": "unknown", "closed": True,
"segments": [{"type": "circle", "center": [5, 5], "radius_mm": 2}],
}],
},
})
cdsl["features"].append({
"id": "surface_extrude", "atomic_id": "extrude_surface", "depends_on": ["base_add"],
"sketch_id": "surface_profile", "params": {"distance_mm": 10},
})
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "surface-extrude.step")
surface = next(item for item in result["feature_results"] if item["feature_id"] == "surface_extrude")
self.assertNotIn("body_id", surface)
self.assertEqual(surface["surface_id"], "surface:surface_extrude")
self.assertEqual(result["solid_count"], 1)
self.assertAlmostEqual(result["volume_mm3"], 1000.0)
faces = [
item for item in result["topology_records"]
if item["feature_id"] == "surface_extrude" and item["kind"] == "face"
]
self.assertEqual(len(faces), 1)
def test_extrude_surface_exports_a_surface_only_step(self) -> None:
from build123d import import_step
from cdsl_engine.runtime import rebuild_cdsl
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "surface-only",
"meta": {"unit": "mm"},
"geometry": {"sketches": [{
"id": "surface_profile", "workplane": _workplane(),
"profile": {
"type": "analytic_contours",
"contours": [{
"role": "unknown", "closed": True,
"segments": [{"type": "circle", "center": [5, 5], "radius_mm": 2}],
}],
},
}]},
"features": [{
"id": "surface_extrude", "atomic_id": "extrude_surface", "depends_on": [],
"sketch_id": "surface_profile", "params": {"distance_mm": 10},
}],
}
with tempfile.TemporaryDirectory() as directory:
out_step = Path(directory) / "surface-only.step"
result = rebuild_cdsl(cdsl, out_step)
rebuilt = import_step(str(out_step))
self.assertEqual(result["solid_count"], 0)
self.assertEqual(result["surface_count"], 1)
self.assertEqual(result["surface_face_count"], 1)
self.assertAlmostEqual(result["volume_mm3"], 0.0)
self.assertAlmostEqual(result["surface_area_mm2"], 40 * math.pi)
self.assertEqual(len(rebuilt.solids()), 0)
self.assertEqual(len(rebuilt.faces()), 1)
def test_surface_limited_chamfer_requires_an_explicit_shell_boundary(self) -> None:
from build123d import Plane, Solid
from cdsl_engine.build123d_adapter import Build123dGeometryAdapter
adapter = Build123dGeometryAdapter()
base = Solid.make_cylinder(19, 20, Plane(origin=(0, 0, -10))).cut(
Solid.make_cylinder(16, 20, Plane(origin=(0, 0, -10))),
)
lower = Solid.make_cylinder(28.5, 20, Plane(origin=(0, 0, -20))).cut(
Solid.make_cylinder(16, 20, Plane(origin=(0, 0, -20))),
)
upper = Solid.make_cylinder(25.5, 25, Plane(origin=(0, 0, 10))).cut(
Solid.make_cylinder(17.5, 25, Plane(origin=(0, 0, 10))),
)
body = adapter.fuse(adapter.fuse(base, lower), upper)
selected = [
edge for edge in body.edges()
if str(edge.geom_type).split(".")[-1].lower() == "circle"
and abs(float(edge.radius) - 28.5) <= 1e-6
and abs(edge.arc_center.Z) <= 1e-6
]
self.assertEqual(len(selected), 1)
with self.assertRaisesRegex(ValueError, "explicit surface support"):
adapter.surface_limited_chamfer(body, 10, selected, [])
wire = adapter._circle_wire([0, 0], 19, PlaneSpec.from_mapping(_workplane()))
support = adapter.extrude_surface([wire], [0, 0, 10])
rebuilt = adapter.surface_limited_chamfer(body, 10, selected, [support])
self.assertLess(rebuilt.volume, body.volume)
cones = [face for face in rebuilt.faces() if str(face.geom_type).split(".")[-1].lower() == "cone"]
self.assertEqual(len(cones), 1)
def test_unowned_revolve_feature_selector_is_preflight_blocked(self) -> None:
from cdsl_engine.runtime import analyze_cdsl
@@ -1057,6 +1499,53 @@ class EngineRuntimeFoundationTests(unittest.TestCase):
result = rebuild_cdsl(cdsl, root / f"{name}.step")
self.assertAlmostEqual(result["volume_mm3"], 1000 - expected_depth * 3.141592653589793, places=5)
def test_up_to_vertex_intersection_requires_one_shared_current_body_vertex(self) -> None:
from cdsl_engine.runtime import RuntimeExecutionError, rebuild_cdsl
base = self._base_block()
base["geometry"]["sketches"].append({
"id": "cut", "workplane": _workplane(),
"profile": {"type": "circle", "center": [0, 0], "radius_mm": 1},
})
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
baseline = rebuild_cdsl(base, root / "intersection-baseline.step")
faces = [item for item in baseline["topology_records"] if item["kind"] == "face"]
top = next(item for item in faces if item["geometry"]["normal"][2] > 0.9)
right = next(item for item in faces if item["geometry"]["normal"][0] > 0.9)
back = next(item for item in faces if item["geometry"]["normal"][1] > 0.9)
def face_selector(face: dict) -> dict:
return {
"kind": "face", "owner_feature_id": "base_add",
"stable_id": f"intersection-{face['record_id']}",
"source": "runtime_snapshot", "confidence": 1.0,
"geometry": face["geometry"],
}
cdsl = deepcopy(base)
cdsl["features"].append({
"id": "cut_to_intersection", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"],
"params": {"distance_mm": 0, "end_condition": {
"type": "up_to_vertex", "reference": {
"kind": "vertex", "owner_feature_id": "base_add",
"stable_id": "top-right-back", "source": "runtime_snapshot", "confidence": 1.0,
"intersection_of": [face_selector(top), face_selector(right), face_selector(back)],
},
}},
"sketch_id": "cut",
})
result = rebuild_cdsl(cdsl, root / "intersection.step")
self.assertAlmostEqual(result["volume_mm3"], 1000 - 10 * 3.141592653589793, places=5)
ambiguous = deepcopy(cdsl)
ambiguous["features"][-1]["params"]["end_condition"]["reference"]["intersection_of"] = [
face_selector(right), face_selector(back),
]
with self.assertRaises(RuntimeExecutionError) as error:
rebuild_cdsl(ambiguous, root / "ambiguous-intersection.step")
self.assertEqual(error.exception.diagnostic.code, "intersection_vertex_unresolved")
def test_through_next_trims_a_partially_overlapping_profile_to_the_next_body_face(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
@@ -1073,6 +1562,71 @@ class EngineRuntimeFoundationTests(unittest.TestCase):
result = rebuild_cdsl(base, Path(directory) / "partial-through-next.step")
self.assertAlmostEqual(result["volume_mm3"], 1000 + 24, places=5)
def test_through_next_keeps_leading_material_before_a_cylindrical_body(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "leading-through-next",
"meta": {"unit": "mm"},
"geometry": {"sketches": [
{"id": "base", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 10}},
{"id": "lead", "workplane": {
"origin_mm": [0, -50, 25], "x_dir": [1, 0, 0], "normal": [0, 1, 0],
}, "profile": {"type": "circle", "radius_mm": 2}},
]},
"features": [
{"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": [],
"params": {"distance_mm": 50}, "sketch_id": "base"},
{"id": "lead_add", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"],
"params": {"distance_mm": 0, "end_condition": {"type": "through_next"}, "result_mode": "new_body"},
"sketch_id": "lead"},
],
}
with tempfile.TemporaryDirectory() as directory:
result = rebuild_cdsl(cdsl, Path(directory) / "leading-through-next.step")
self.assertEqual(result["solid_count"], 2)
self.assertAlmostEqual(result["bbox_mm"]["min"][1], -50.0, places=5)
def test_up_to_surface_from_a_cylindrical_wall_uses_the_next_body_face(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl
base = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "wall-hole",
"meta": {"unit": "mm"},
"geometry": {"sketches": [
{"id": "tube", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 10}},
{"id": "tube_bore", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 7}},
{"id": "wall_hole", "workplane": {
"origin_mm": [10, 0, 5], "x_dir": [0, 1, 0], "normal": [-1, 0, 0],
}, "profile": {"type": "circle", "radius_mm": 1}},
]},
"features": [
{"id": "tube_add", "atomic_id": "extrude_add_blind", "depends_on": [],
"params": {"distance_mm": 10}, "sketch_id": "tube"},
{"id": "tube_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["tube_add"],
"params": {"distance_mm": 10}, "sketch_id": "tube_bore"},
],
}
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
baseline = rebuild_cdsl(base, root / "tube.step")
outer_face = next(
item for item in baseline["topology_records"]
if item["kind"] == "face" and item["geometry"]["surface_type"] == "cylinder"
and abs(item["geometry"]["radius_mm"] - 10) <= 1e-6
)
cdsl = deepcopy(base)
cdsl["features"].append({
"id": "wall_hole_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["tube_cut"],
"params": {"distance_mm": 0, "end_condition": {"type": "up_to_surface", "reference": {
"kind": "face", "owner_feature_id": "tube_add", "geometry": outer_face["geometry"],
}}},
"sketch_id": "wall_hole",
})
result = rebuild_cdsl(cdsl, root / "wall-hole.step")
self.assertLess(result["volume_mm3"], baseline["volume_mm3"] - 8)
self.assertGreater(result["volume_mm3"], baseline["volume_mm3"] - 12)
def test_up_to_body_extent_uses_a_uniquely_resolved_body_record(self) -> None:
from cdsl_engine.runtime import rebuild_cdsl