feat(selector): 增加离线候选遍历与严格回放验证 Demo

- 新增 selector_candidate_demo,移除 provenance intent 后枚举候选 selector
- 对候选分支执行有界重建与严格 STEP 比较
- 仅在候选遍历完整且唯一 strict 通过时生成 selector 映射记录
- 增加 selector 候选搜索、预算限制和记录生成的测试
- 保持生产 selector resolver 不受 Demo 逻辑影响
- 更新 CADFS 能力台账,记录 IMPRINT 派生 profile 的 lineage selector 缺口
This commit is contained in:
2026-09-10 15:12:57 +08:00
parent d8fc2a9207
commit 994d06aaea
36 changed files with 9268 additions and 753 deletions
+260 -62
View File
@@ -10,7 +10,7 @@ from cadfs_to_cdsl.rebuild import rebuild_candidate
class IntegrationTests(unittest.TestCase):
def test_known_rp_roundtrip_00000173(self):
def test_known_rp_source_preserves_prefix_when_selector_query_is_deferred(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0000/00000173.txt"; gold = root / "step_abc/0000/00000173.step"
if not feature.exists() or not gold.exists(): self.skipTest("CADFS sample is not installed")
@@ -18,11 +18,9 @@ class IntegrationTests(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp:
rebuilt = Path(tmp) / "rebuild.step"
result = rebuild_candidate(cdsl, rebuilt)
self.assertEqual(result["status"], "rebuilt")
comparison = compare_steps(gold, rebuilt)
self.assertFalse(comparison["strict"]["passed"])
self.assertTrue(comparison["rp"]["passed"])
self.assertEqual(comparison["decision"], "approximate_pass")
self.assertEqual(result["status"], "rebuild_failed")
self.assertEqual(result["error"]["message"], "f_F2: selector_query_unsupported during incremental replay")
self.assertEqual(result["last_executable_prefix"]["last_feature_id"], "f_F1")
def test_standard_tapped_through_counterbore_00002243(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -43,9 +41,30 @@ class IntegrationTests(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp:
rebuilt = Path(tmp) / "rebuild.step"
self.assertEqual(rebuild_candidate(result.cdsl, rebuilt)["status"], "rebuilt")
comparison = compare_steps(root / "step_abc/0000/00002243.step", rebuilt)
self.assertTrue(comparison["strict"]["passed"])
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F4: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F3")
def test_direct_sketch_vertex_holes_preserve_scoped_body_00406667(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0040/00406667.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00406667"), {}).cdsl
holes = [item for item in candidate["features"] if item["atomic_id"] == "hole_wizard"]
self.assertEqual([item["id"] for item in holes], ["f_F3", "f_F5"])
self.assertEqual([item["params"]["scope_feature_id"] for item in holes], ["f_F1", "f_F1"])
self.assertEqual([item["params"]["positions"] for item in holes], [
[{"mm": [0.0, 0.0, 0.0]}], [{"mm": [0.0, 26.69, 0.0]}],
])
with tempfile.TemporaryDirectory() as tmp:
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
self.assertEqual(outcome["status"], "rebuilt")
self.assertEqual(
[item["feature_id"] for item in outcome["result"]["feature_results"]],
["f_F1", "f_F3", "f_F5"],
)
def test_rectilinear_fillet_arcs_00129362_preserve_recovered_sketch_geometry(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -75,9 +94,15 @@ class IntegrationTests(unittest.TestCase):
fillet = next(item for item in result.cdsl["features"] if item["id"] == "f_F3")
self.assertEqual(fillet["atomic_id"], "fillet")
self.assertEqual(fillet["selectors"][0]["kind"], "face")
self.assertNotIn("geometry", fillet["selectors"][0])
self.assertEqual(fillet["selectors"][0]["selector_intent"]["query_family"], "SWEPT_FACE")
self.assertEqual(
fillet["selectors"][0]["geometry"],
{"axis_origin_mm": [0.0, 0.0, 0.0], "axis_direction": [0.0, -1.0, 0.0], "radius_mm": 45.66},
fillet["selectors"][0]["selector_intent"]["source_entity"],
{"sketch_id": "F0", "entity_id": "E0"},
)
self.assertEqual(
fillet["selectors"][0]["selector_intent"]["derivation_policy"],
{"allowed": ["boundary"], "multiplicity": "one"},
)
chamfer = next(item for item in result.cdsl["features"] if item["id"] == "f_F5")
@@ -101,18 +126,183 @@ class IntegrationTests(unittest.TestCase):
self.assertNotIn("result_mode", next(item for item in result.cdsl["features"] if item["id"] == "f_F7")["params"])
self.assertEqual(next(item for item in result.cdsl["features"] if item["id"] == "f_F9")["params"]["result_mode"], "new_body")
from build123d import import_step
with tempfile.TemporaryDirectory() as tmp:
rebuilt = Path(tmp) / "rebuild.step"
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertEqual(outcome["status"], "rebuilt")
runtime_box = outcome["result"]["bbox_mm"]
imported_box = import_step(str(rebuilt)).bounding_box()
actual = [imported_box.min.X, imported_box.min.Y, imported_box.min.Z, imported_box.max.X, imported_box.max.Y, imported_box.max.Z]
expected = runtime_box["min"] + runtime_box["max"]
for value, target in zip(actual, expected): self.assertAlmostEqual(value, target, places=5)
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F5: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F4")
def test_inward_shells_00789939_lower_and_rebuild(self):
def test_swept_face_extent_continues_through_primary_cuts_00925274(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0092/00925274.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00925274"), {}).cdsl
with tempfile.TemporaryDirectory() as tmp:
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
self.assertEqual(outcome["status"], "rebuilt")
result = outcome["result"]
self.assertEqual(
[item["feature_id"] for item in result["feature_results"]],
["f_F1", "f_F3", "f_F4", "f_F6", "f_F7", "f_F9", "f_F10", "f_F12", "f_F13", "f_F15", "f_F17", "f_F18_plane", "f_F18"],
)
f9_resolution = next(
item for item in result["selector_resolution"] if item["feature_id"] == "f_F9"
)
self.assertEqual(f9_resolution["status"], "resolved")
self.assertEqual(f9_resolution["resolution_mode"], "kernel_lineage")
self.assertEqual(f9_resolution["evidence"]["result_records"], ["body:f_F6:face:0"])
self.assertEqual(
[item["feature_id"] for item in f9_resolution["evidence"]["relations"]],
["f_F1", "f_F3", "f_F6"],
)
def test_direct_hole_prism_swept_edge_resolves_00007264(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0000/00007264.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(feature.read_text(), "00007264"), {})
fillet = next(item for item in result.cdsl["features"] if item["id"] == "f_F2")
self.assertEqual(fillet["atomic_id"], "fillet")
self.assertTrue(all(
selector["selector_intent"]["query_family"] == "SWEPT_EDGE"
for selector in fillet["selectors"]
))
with tempfile.TemporaryDirectory() as tmp:
outcome = rebuild_candidate(result.cdsl, Path(tmp) / "rebuild.step")
self.assertEqual(outcome["status"], "rebuilt")
binding = next(item for item in outcome["selector_binding"] if item["feature_id"] == "f_F2")
self.assertEqual(len(binding["resolved"]), 4)
vertical_edges = [
relation
for delta in outcome["result"]["topology_deltas"]
if not any(str(snapshot_id).startswith("transient:") for snapshot_id in delta["output_snapshot_ids"])
for relation in delta["relations"]
if relation["source_kind"] == "vertex"
and relation["result_kind"] == "edge"
and relation["source_record_ids"]
]
self.assertEqual(
len({(tuple(item["source_record_ids"]), tuple(item["result_record_ids"])) for item in vertical_edges}),
4,
)
self.assertTrue(all(
relation["coverage"] == "complete" and relation["lineage_status"] == "proven"
for relation in vertical_edges
))
def test_annular_swept_face_selector_00974931(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0097/00974931.txt"
gold = root / "step_abc/0097/00974931.step"
if not feature.exists() or not gold.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00974931"), {}).cdsl
fillet = next(item for item in candidate["features"] if item["id"] == "f_F2")
selector = fillet["selectors"][0]
self.assertEqual(fillet["atomic_id"], "fillet")
self.assertNotIn("geometry", selector)
self.assertNotIn("stable_id", selector)
self.assertEqual(selector["selector_intent"]["query_family"], "SWEPT_FACE")
self.assertEqual(
selector["selector_intent"]["source_entity"],
{"sketch_id": "F0", "entity_id": "E0"},
)
with tempfile.TemporaryDirectory() as tmp:
rebuilt = Path(tmp) / "rebuild.step"
outcome = rebuild_candidate(candidate, rebuilt)
self.assertEqual(outcome["status"], "rebuild_failed")
f2_resolution = next(
item for item in outcome["last_executable_prefix"]["result"]["selector_resolution"]
if item["feature_id"] == "f_F2"
)
self.assertEqual(f2_resolution["status"], "resolved")
self.assertEqual(f2_resolution["resolution_mode"], "kernel_lineage")
self.assertEqual(f2_resolution["evidence"]["semantic_anchor"], {
"type": "source_entity", "sketch_id": "F0", "entity_id": "E0",
})
self.assertEqual(
[item["feature_id"] for item in outcome["last_executable_prefix"]["result"]["feature_results"]],
["f_F1", "f_F2"],
)
self.assertEqual(outcome["error"]["message"], "f_F3: selector_query_unsupported during incremental replay")
def test_direct_prism_cap_edge_lineage_00021014(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0002/00021014.txt"
gold = root / "step_abc/0002/00021014.step"
if not feature.exists() or not gold.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00021014"), {}).cdsl
fillet = next(item for item in candidate["features"] if item["id"] == "f_F2")
cap_edges = [
selector for selector in fillet["selectors"]
if (selector.get("selector_intent") or {}).get("query_family") == "CAP_EDGE"
]
self.assertEqual(len(cap_edges), 9)
self.assertTrue(all("stable_id" not in selector and "geometry" not in selector for selector in cap_edges))
self.assertEqual(
{selector["selector_intent"]["lineage_role"] for selector in cap_edges},
{"extrude.start", "extrude.end"},
)
with tempfile.TemporaryDirectory() as tmp:
rebuilt = Path(tmp) / "rebuild.step"
outcome = rebuild_candidate(candidate, rebuilt)
self.assertEqual(outcome["status"], "rebuilt")
comparison = compare_steps(gold, rebuilt)
cap_resolutions = [
item for item in outcome["result"]["selector_resolution"]
if item.get("feature_id") == "f_F2"
and item.get("evidence", {}).get("semantic_anchor", {}).get("type") == "source_entity"
]
self.assertEqual(len(cap_resolutions), 10)
direct_caps = [
item for item in cap_resolutions
if item["evidence"]["relations"][0].get("output_role") in {"extrude.start", "extrude.end"}
]
self.assertEqual(len(direct_caps), 9)
self.assertTrue(all(item["resolution_mode"] == "kernel_lineage" for item in direct_caps))
self.assertTrue(comparison["strict"]["passed"])
def test_direct_mixed_hole_cap_edge_lineage_00735367(self):
"""A solver-split circular hole restores one proven source cap edge."""
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0073/00735367.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00735367"), {}).cdsl
fillet = next(item for item in candidate["features"] if item["id"] == "f_F2")
selector = fillet["selectors"][0]
self.assertEqual(selector["selector_intent"]["query_family"], "CAP_EDGE")
self.assertEqual(selector["selector_intent"]["source_entity"], {"sketch_id": "F0", "entity_id": "E1"})
self.assertEqual(selector["selector_intent"]["lineage_role"], "extrude.end")
self.assertNotIn("geometry", selector)
self.assertNotIn("stable_id", selector)
with tempfile.TemporaryDirectory() as tmp:
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F4: selector_query_unsupported during incremental replay")
prefix = outcome["last_executable_prefix"]["result"]
self.assertEqual([item["feature_id"] for item in prefix["feature_results"]], ["f_F1", "f_F2", "f_F3"])
resolution = next(item for item in prefix["selector_resolution"] if item["feature_id"] == "f_F2")
self.assertEqual(resolution["status"], "resolved")
self.assertEqual(resolution["resolution_mode"], "kernel_lineage")
self.assertEqual(resolution["evidence"]["semantic_anchor"], {
"type": "source_entity", "sketch_id": "F0", "entity_id": "E1",
})
self.assertEqual(resolution["evidence"]["relations"][0]["output_role"], "extrude.end")
self.assertTrue(resolution["evidence"]["relations"][0]["source_record_ids"])
def test_inward_shells_00789939_preserve_prefix_when_face_query_is_deferred(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0078/00789939.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
@@ -128,8 +318,10 @@ class IntegrationTests(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp:
rebuilt = Path(tmp) / "rebuild.step"
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertEqual(outcome["status"], "rebuilt")
self.assertEqual(outcome["result"]["solid_count"], 2)
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F6: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F5")
self.assertEqual(outcome["last_executable_prefix"]["result"]["solid_count"], 2)
def test_outward_cap_shells_lower_and_rebuild_prefixes(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -155,7 +347,10 @@ class IntegrationTests(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp:
outcome = rebuild_candidate(prefix, Path(tmp) / "outward-shell.step")
self.assertEqual(outcome["status"], "rebuilt")
self.assertEqual(outcome["result"]["solid_count"], 1)
self.assertEqual(
[item["feature_id"] for item in outcome["result"]["feature_results"]],
["f_F1", "f_F2"],
)
def test_sweep_00542223_preserves_its_open_bspline_path(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -173,6 +368,42 @@ class IntegrationTests(unittest.TestCase):
self.assertNotIn("F2", [item.get("feature_id") for item in result.diagnostics])
self.assertNotIn("F5", [item.get("feature_id") for item in result.diagnostics])
def test_direct_sketch_wire_qbodytype_path_lowers_and_rebuilds_00896761(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0089/00896761.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00896761"), {}).cdsl
sweep = next(item for item in candidate["features"] if item["id"] == "f_F2")
segment = sweep["params"]["path"]["segment"]
self.assertEqual(sweep["atomic_id"], "sweep_add")
self.assertEqual(segment["type"], "bspline")
self.assertEqual(segment["source_entity_id"], "E2")
self.assertEqual(segment["points"], [[0.0, 0.0], [87.21, 282.41]])
self.assertEqual(segment["start_tangent"], [0.0, 936.12])
self.assertEqual(segment["end_tangent"], [168.9, 48.72])
from engine.cdsl_engine.runtime import analyze_cdsl
self.assertTrue(analyze_cdsl(candidate).runtime_eligible)
with tempfile.TemporaryDirectory() as tmp:
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
self.assertEqual(outcome["status"], "rebuilt")
self.assertEqual([item["feature_id"] for item in outcome["result"]["feature_results"]], ["f_F2"])
def test_multi_entity_sketch_wire_qbodytype_path_remains_deferred_00786708(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0078/00786708.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(feature.read_text(), "00786708"), {})
self.assertIn({
"code": "unsupported_engine_capability", "feature_id": "F2", "operation": "sweep", "capability": "sweep_path_query",
"message": "current CDSL sweep requires one direct sketch line or B-spline path",
}, result.diagnostics)
self.assertNotIn("f_F2", {item["id"] for item in result.cdsl["features"]})
later_sweep = next(item for item in result.cdsl["features"] if item["id"] == "f_F5")
self.assertEqual(later_sweep["params"]["path"]["segment"]["source_entity_id"], "E5")
def test_circular_pattern_00542223_preserves_all_sweep_arms(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0054/00542223.txt"
@@ -193,7 +424,7 @@ class IntegrationTests(unittest.TestCase):
self.assertAlmostEqual(bbox["max"][0], 52.14101625137762)
self.assertAlmostEqual(bbox["max"][1], 37.5000001000001)
def test_fused_body_circular_copy_faces_bind_and_shell_00542223(self):
def test_fused_body_circular_copy_faces_preserve_shell_prefix_00542223(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0054/00542223.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
@@ -202,53 +433,20 @@ class IntegrationTests(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp:
outcome = rebuild_candidate(candidate, Path(tmp) / "rebuild.step")
self.assertEqual(outcome["status"], "rebuilt")
result = outcome["result"]
self.assertIn("f_F7", [item["feature_id"] for item in result["feature_results"]])
shell_selectors = [
item for item in result["selector_resolution"]
if item["feature_id"] == "f_F7"
]
self.assertEqual(
{item["selector"]["owner_feature_id"] for item in shell_selectors},
{"f_F5", "f_F6.c1.f_F5", "f_F6.c2.f_F5", "f_F1"},
)
self.assertTrue(all(item["status"] == "resolved" for item in shell_selectors))
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F7: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F6")
def test_face_chamfer_ignores_periodic_seams_00111611(self):
def test_face_chamfer_source_query_is_not_geometry_bound_00111611(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0011/00111611.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
from copy import deepcopy
from engine.cdsl_engine.capabilities import CapabilityAnalyzer, sketch_ids_required_by_contract
from engine.cdsl_engine.runtime import EXECUTORS, ExecutionSession, _execute_node, _selector_edges
from engine.cdsl_engine.sketch_solver import CORE_SHAPE_GENERATORS, resolve_required_sketches
from cadfs_to_cdsl.selector_binding import bind_candidate_selectors
candidate = lower_model(parse_featurescript(feature.read_text(), "00111611"), {}).cdsl
bound, _ = bind_candidate_selectors(candidate)
resolved = resolve_required_sketches(
deepcopy(bound), sketch_ids_required_by_contract(bound), errors={},
)
analysis = CapabilityAnalyzer(
atomic_ids=EXECUTORS, profile_types=CORE_SHAPE_GENERATORS,
).analyze(resolved)
session = ExecutionSession(
sketches={str(item["id"]): item for item in resolved["geometry"]["sketches"]},
nodes={node.feature_id: node for node in analysis.plan},
)
for node in analysis.plan:
if node.feature_id == "f_F5":
edges = _selector_edges(node, session, tangent_propagation=True)
break
_execute_node(node, session)
else:
self.fail("F5 chamfer was not planned")
self.assertEqual(len(edges), 8)
self.assertTrue(all(str(edge.geom_type).endswith("CIRCLE") for edge in edges))
with self.assertRaisesRegex(ValueError, "f_F5: selector_query_unsupported after prefix rebuild"):
bind_candidate_selectors(candidate)
def test_circular_remove_pattern_replays_cut_sources_00159804(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
+719 -55
View File
@@ -6,7 +6,12 @@ from pathlib import Path
from cadfs_to_cdsl.compare import compare_steps
from cadfs_to_cdsl.featurescript_parser import parse_featurescript
from cadfs_to_cdsl.ir import Call
from cadfs_to_cdsl.lowering import _arc, _contours, _global, _number, _record_single_body_successor, lower_model
from cadfs_to_cdsl.lowering import (
UnsupportedCapability, _arc, _contours, _curve_endpoint_tangent,
_direct_hole_location, _direct_line_angle_axis, _direct_sketch_wire_path,
_direct_boolean_intersection_selector, _direct_primary_cut_intersection_selector, _global, _number,
_record_single_body_successor, lower_model,
)
from cadfs_to_cdsl.pipeline import compare_one, convert_one, rebuild_one
from cadfs_to_cdsl.rebuild import rebuild_candidate
from cadfs_to_cdsl.dataset import Sample
@@ -14,7 +19,92 @@ from cadfs_to_cdsl.dataset import scan_dataset
from cadfs_to_cdsl.tests.test_parser import SOURCE, TRANSFORM_SOURCE
LINE_ANGLE_TWO_ENTITY_SOURCE = '''FeatureScript 1511;
import(path : "onshape/std/geometry.fs", version : "1511.0");
const mm = millimeter;
const VERTEX = EntityType.VERTEX;
const EDGE = EntityType.EDGE;
const FACE = EntityType.FACE;
function v(x, y){return vector(x, y);}
annotation { "Feature Type Name" : "Feature" }
export const myFeature = defineFeature(function(context is Context, id is Id, definition is map)
precondition{}
{
{ var Q0; Q0=qCreatedBy(makeId("Top.planeOp"),FACE); var sketch=newSketch(context,id+"F0",{"sketchPlane":qUnion([Q0])}); skLineSegment(sketch,"E0",{"start":v(0,0)*mm,"end":v(10,0)*mm}); skLineSegment(sketch,"E1",{"start":v(0,0)*mm,"end":v(0,10)*mm}); skLineSegment(sketch,"E2",{"start":v(0,5)*mm,"end":v(10,5)*mm}); skPoint(sketch,"P0",{"position":v(0,10)*mm}); skPoint(sketch,"P1",{"position":v(10,0)*mm}); skSolve(sketch); }
{ var Q0; Q0=sQuery(id+"F0.wireOp",EDGE,"E0"); var Q1; Q1=sQuery(id+"F0.wireOp",EDGE,"E1"); cPlane(context,id+"F1",{"entities":qUnion([Q0,Q1]),"cplaneType":CPlaneType.LINE_ANGLE,"angle":0*degree}); }
{ var Q0; Q0=sQuery(id+"F0.wireOp",EDGE,"E0"); var Q1; Q1=sQuery(id+"F0.wireOp",EDGE,"E2"); cPlane(context,id+"F2",{"entities":qUnion([Q0,Q1]),"cplaneType":CPlaneType.LINE_ANGLE,"angle":0*degree}); }
{ var Q0; Q0=sQuery(id+"F0.wireOp",EDGE,"E0"); var Q1; Q1=sQuery(id+"F0.wireOp",VERTEX,"P0"); cPlane(context,id+"F3",{"entities":qUnion([Q0,Q1]),"cplaneType":CPlaneType.LINE_ANGLE,"angle":0*degree}); }
{ var Q0; Q0=sQuery(id+"F0.wireOp",VERTEX,"P0"); var Q1; Q1=sQuery(id+"F0.wireOp",EDGE,"E0"); cPlane(context,id+"F4",{"entities":qUnion([Q0,Q1]),"cplaneType":CPlaneType.LINE_ANGLE,"angle":90*degree,"oppositeDirection":true}); }
{ var Q0; Q0=sQuery(id+"F0.wireOp",EDGE,"E0"); var Q1; Q1=sQuery(id+"F0.wireOp",VERTEX,"P1"); cPlane(context,id+"F5",{"entities":qUnion([Q0,Q1]),"cplaneType":CPlaneType.LINE_ANGLE,"angle":0*degree}); }
{ var Q0; Q0=sQuery(id+"F0.wireOp",EDGE,"E0"); var Q1; Q1=qCreatedBy(id+"F1.planeOp",FACE); cPlane(context,id+"F6",{"entities":qUnion([Q0,Q1]),"cplaneType":CPlaneType.LINE_ANGLE,"angle":0*degree}); }
}'''
class LoweringTests(unittest.TestCase):
def test_direct_sketch_wire_path_accepts_only_one_nonconstruction_line_or_bspline(self):
plane = {"origin_mm": [0.0, 0.0, 0.0], "x_dir": [1.0, 0.0, 0.0], "normal": [0.0, 0.0, 1.0]}
line = {"type": "line", "start": [0.0, 0.0], "end": [10.0, 0.0], "source_entity_id": "E0"}
query = Call("qUnion", [[
Call("qConstructionFilter", [
Call("qBodyType", [
Call("qCreatedBy", [Call("__binary__", ["id", "+", "F0"]), "EDGE"]),
"BodyType.WIRE",
]),
"ConstructionObject.NO",
]),
]])
sketches = {"F0": {"workplane": plane}}
entities = {"F0": {"E0": line, "axis": {"type": "line", "start": [0.0, 0.0], "end": [0.0, 5.0], "construction": True}}}
self.assertEqual(
_direct_sketch_wire_path(
query, sketches, entities, featurescript_version="1511",
standard_library="onshape/std/geometry.fs", standard_library_version="1511.0",
),
("F0", "E0", line, sketches["F0"]),
)
self.assertIsNone(_direct_sketch_wire_path(
query, sketches, entities, featurescript_version="1512",
standard_library="onshape/std/geometry.fs", standard_library_version="1511.0",
))
self.assertIsNone(_direct_sketch_wire_path(
query, sketches, entities, featurescript_version="1511",
standard_library="onshape/std/geometry.fs", standard_library_version="1512.0",
))
self.assertIsNone(_direct_sketch_wire_path(
query, sketches, {"F0": {"E0": line, "E1": {**line, "source_entity_id": "E1"}}},
featurescript_version="1511", standard_library="onshape/std/geometry.fs", standard_library_version="1511.0",
))
self.assertIsNone(_direct_sketch_wire_path(
Call("qBodyType", [Call("qCreatedBy", ["F0", "EDGE"]), "BodyType.WIRE"]),
sketches, entities, featurescript_version="1511",
standard_library="onshape/std/geometry.fs", standard_library_version="1511.0",
))
self.assertIsNone(_direct_sketch_wire_path(
query.args[0][0], sketches, entities, featurescript_version="1511",
standard_library="onshape/std/geometry.fs", standard_library_version="1511.0",
))
def test_direct_hole_location_accepts_only_original_sketch_vertices(self):
plane = {"origin_mm": [0.0, 0.0, 0.0], "x_dir": [1.0, 0.0, 0.0], "normal": [0.0, 0.0, 1.0]}
sketches = {"F2": {"workplane": plane}}
entities = {"F2": {
"P0": {"type": "point", "point": [1.0, 2.0]},
"C0": {"type": "circle", "center": [3.0, 4.0], "radius_mm": 1.0},
"L0": {"type": "line", "start": [5.0, 6.0], "end": [7.0, 8.0]},
"A0": {"type": "arc", "start": [9.0, 10.0], "end": [11.0, 12.0]},
}}
def vertex(token: str) -> Call:
return Call("sQuery", ["F2.wireOp", "VERTEX", token])
self.assertEqual(_direct_hole_location(vertex("P0"), sketches, entities), ([1.0, 2.0, 0.0], plane))
self.assertEqual(_direct_hole_location(vertex("C0.center"), sketches, entities), ([3.0, 4.0, 0.0], plane))
self.assertEqual(_direct_hole_location(vertex("L0.end"), sketches, entities), ([7.0, 8.0, 0.0], plane))
self.assertEqual(_direct_hole_location(vertex("A0.start"), sketches, entities), ([9.0, 10.0, 0.0], plane))
self.assertIsNone(_direct_hole_location(Call("qAdjacent", [vertex("C0.center")]), sketches, entities))
self.assertIsNone(_direct_hole_location(vertex("C0.center.copy"), sketches, entities))
def test_strict_comparison_status_is_not_labeled_approximate(self):
with tempfile.TemporaryDirectory() as tmp:
output = Path(tmp); directory = output / "samples" / "strict-status"; directory.mkdir(parents=True)
@@ -53,7 +143,7 @@ class LoweringTests(unittest.TestCase):
self.assertAlmostEqual(arc["center"][1], 0.5)
self.assertAlmostEqual(arc["radius_mm"], math.hypot(2.5, 0.5))
def test_direct_extrude_and_two_section_loft_swept_edges_bind_for_fillet(self):
def test_direct_extrude_lineage_succeeds_and_unregistered_loft_query_preserves_prefix(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
cases = {
"00000715": ("f_F2", 2, True),
@@ -67,14 +157,37 @@ class LoweringTests(unittest.TestCase):
fillet = next(item for item in result.cdsl["features"] if item["id"] == fillet_id)
self.assertEqual(len(fillet["selectors"]), selector_count)
self.assertTrue(all(item["owner_feature_id"] == fillet["depends_on"][0] for item in fillet["selectors"]))
self.assertTrue(all("bbox_mm" in item["geometry"] for item in fillet["selectors"]))
self.assertEqual(["curve_type" in item["geometry"] for item in fillet["selectors"]], [line_type] * selector_count)
if sample_id == "00000715":
self.assertTrue(all("geometry" not in item for item in fillet["selectors"]))
self.assertTrue(all(
item["selector_intent"]["query_family"] == "SWEPT_EDGE"
and item["selector_intent"]["derivation_policy"] == {"allowed": ["boundary"], "multiplicity": "one"}
and len(item["selector_intent"]["source_entities"]) == 2
for item in fillet["selectors"]
))
else:
self.assertTrue(all("bbox_mm" in item["geometry"] for item in fillet["selectors"]))
self.assertEqual(["curve_type" in item["geometry"] for item in fillet["selectors"]], [line_type] * selector_count)
self.assertTrue(all(
item["selector_intent"]["query_family"] == "SWEPT_EDGE"
and item["selector_intent"]["evidence"] == "feature_script_query"
and item["selector_intent"]["derivation_policy"] == {"allowed": ["continuation"], "multiplicity": "none"}
and item["selector_intent"]["source_query"]["featurescript_version"] == "1793"
for item in fillet["selectors"]
))
with tempfile.TemporaryDirectory() as directory:
rebuilt = Path(directory) / "rebuild.step"
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertEqual(outcome["status"], "rebuilt")
comparison = compare_steps(root / "step_abc" / sample_id[:4] / f"{sample_id}.step", rebuilt)
self.assertTrue(comparison["strict"]["passed"])
if sample_id == "00005267":
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F4: selector_query_unsupported during incremental replay")
self.assertTrue(rebuilt.exists())
self.assertEqual(outcome["last_executable_prefix"]["failed_feature_id"], "f_F4")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F3")
else:
self.assertEqual(outcome["status"], "rebuilt")
comparison = compare_steps(root / "step_abc" / sample_id[:4] / f"{sample_id}.step", rebuilt)
self.assertTrue(comparison["strict"]["passed"])
def test_swept_edge_requires_one_direct_shared_source_endpoint(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -93,7 +206,7 @@ class LoweringTests(unittest.TestCase):
"message": "swept edge source endpoint provenance is unsupported",
}])
def test_direct_full_revolve_swept_circle_edges_bind_for_dressups(self):
def test_full_revolve_swept_circle_edges_remain_deferred_with_prefixes(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
for sample_id, dressup_id, selector_count in (
("00048326", "f_F3", 2),
@@ -109,14 +222,17 @@ class LoweringTests(unittest.TestCase):
selector["geometry"].get("curve_type") == "circle"
and len(selector["geometry"].get("circle_center_mm") or []) == 3
and selector["geometry"].get("radius_mm", 0) > 0
and selector["selector_intent"]["query_family"] == "SWEPT_EDGE"
and selector["selector_intent"]["evidence"] == "feature_script_query"
and selector["selector_intent"]["derivation_policy"]["multiplicity"] == "none"
for selector in dressup["selectors"]
))
with tempfile.TemporaryDirectory() as directory:
rebuilt = Path(directory) / "rebuild.step"
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertEqual(outcome["status"], "rebuilt")
comparison = compare_steps(root / "step_abc" / sample_id[:4] / f"{sample_id}.step", rebuilt)
self.assertTrue(comparison["strict"]["passed"])
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], f"{dressup_id}: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["failed_feature_id"], dressup_id)
def test_full_revolve_swept_circle_requires_one_direct_shared_source_endpoint(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -135,7 +251,7 @@ class LoweringTests(unittest.TestCase):
"message": "swept edge source endpoint provenance is unsupported",
}, result.diagnostics)
def test_equivalent_imprint_profile_retains_direct_full_revolve_swept_edges(self):
def test_equivalent_imprint_profile_preserves_but_does_not_bind_full_revolve_queries(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
source = root / "featurescript_rp/0011/00112257.txt"
result = lower_model(parse_featurescript(source.read_text(), "00112257"), {})
@@ -144,17 +260,16 @@ class LoweringTests(unittest.TestCase):
self.assertEqual(len(fillet["selectors"]), 5)
self.assertTrue(all(
selector["geometry"].get("curve_type") == "circle"
and selector["selector_intent"]["query_family"] == "SWEPT_EDGE"
and selector["selector_intent"]["derivation_policy"]["multiplicity"] == "none"
for selector in fillet["selectors"]
))
from cadfs_to_cdsl.selector_binding import bind_candidate_selectors
prefix = dict(result.cdsl)
prefix["features"] = result.cdsl["features"][:2]
bound, evidence = bind_candidate_selectors(prefix)
selectors = next(item for item in bound["features"] if item["id"] == "f_F2")["selectors"]
self.assertEqual(len(evidence), 1)
self.assertEqual(len(selectors), 5)
self.assertTrue(all(selector.get("snapshot_id") for selector in selectors))
with self.assertRaisesRegex(ValueError, "f_F2: selector_query_unsupported after prefix rebuild"):
bind_candidate_selectors(prefix)
def test_changed_imprint_profile_does_not_inherit_full_revolve_source_edges(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -689,6 +804,30 @@ class LoweringTests(unittest.TestCase):
self.assertEqual(end["offset_mm"], 3.0)
self.assertEqual(end["reference"]["kind"], "body")
self.assertEqual(end["reference"]["owner_feature_id"], "f_F1")
self.assertEqual(end["reference"]["source"], "runtime_snapshot")
self.assertNotIn("stable_id", end["reference"])
self.assertEqual(end["reference"]["selector_intent"]["query_family"], "SWEPT_BODY")
self.assertEqual(end["reference"]["selector_intent"]["evidence"], "active_body_member")
self.assertEqual(
end["reference"]["selector_intent"]["derivation_policy"],
{"allowed": ["boundary"], "multiplicity": "one"},
)
fillet = next(item for item in result.cdsl["features"] if item["id"] == "f_F4")
self.assertEqual(len(fillet["selectors"]), 1)
self.assertEqual(fillet["selectors"][0]["selector_intent"]["query_family"], "CAP_EDGE")
self.assertEqual(
fillet["selectors"][0]["selector_intent"]["derivation_policy"],
{"allowed": ["boundary", "continuation"], "multiplicity": "one"},
)
from engine.cdsl_engine.semantic_validation import validate_semantic_cdsl
mixed_evidence = deepcopy(result.cdsl)
mixed_reference = next(
item for item in mixed_evidence["features"] if item["id"] == "f_F3"
)["params"]["end_condition"]["reference"]
mixed_reference["stable_id"] = "body:f_F1"
with self.assertRaisesRegex(ValueError, "invalid SWEPT_BODY member contract"):
validate_semantic_cdsl(mixed_evidence)
def test_up_to_surface_extrude_captures_the_cap_face(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -700,9 +839,24 @@ class LoweringTests(unittest.TestCase):
self.assertEqual(end["type"], "up_to_surface")
self.assertEqual(end["reference"]["kind"], "face")
self.assertEqual(end["reference"]["owner_feature_id"], "f_F1")
self.assertEqual(end["reference"]["geometry"]["plane_offset_mm"], 0.0)
self.assertEqual(end["reference"]["output_role"], "extrude.start")
self.assertEqual(end["reference"]["source"], "runtime_snapshot")
self.assertNotIn("geometry", end["reference"])
self.assertEqual(end["reference"]["selector_intent"]["query_family"], "CAP_FACE")
self.assertEqual(end["reference"]["selector_intent"]["evidence"], "operation_role")
swept = next(item for item in result.cdsl["features"] if item["id"] == "f_F6")
self.assertEqual(swept["params"]["end_condition"]["reference"]["geometry"]["radius_mm"], 24.3)
swept_reference = swept["params"]["end_condition"]["reference"]
self.assertEqual(swept_reference["selector_intent"]["query_family"], "SWEPT_FACE")
self.assertEqual(
swept_reference["selector_intent"]["derivation_policy"],
{"allowed": ["boundary", "continuation"], "multiplicity": "one"},
)
self.assertEqual(swept_reference["selector_intent"]["evidence"], "kernel_history")
self.assertEqual(swept_reference["selector_intent"]["source_entity"], {"sketch_id": "F0", "entity_id": "E0"})
self.assertEqual(swept_reference["source"], "runtime_snapshot")
self.assertNotIn("stable_id", swept_reference)
self.assertNotIn("binding_feature_id", swept_reference)
self.assertNotIn("geometry", swept_reference)
mirror = next(item for item in result.cdsl["features"] if item["id"] == "f_F18")
self.assertTrue(mirror["params"]["mirror_current_body"])
@@ -756,6 +910,160 @@ class LoweringTests(unittest.TestCase):
self.assertAlmostEqual(plane["origin_mm"][0], -12.15)
self.assertAlmostEqual(plane["origin_mm"][1], -24.3 * math.sqrt(3) / 2)
def test_line_angle_direct_circle_uses_the_featurescript_circle_axis(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0003/00030209.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(feature.read_text(), "00030209"), {})
self.assertEqual(result.status, "converted_partial")
planes = {
item["id"]: item["params"]["plane"]
for item in result.cdsl["features"]
if item["atomic_id"] == "reference_plane"
}
expected_centers = {
"f_F1": [45.78, 0.0, 26.2],
"f_F2": [13.9, 0.0, 26.5],
"f_F4": [-17.08, 0.0, 23.46],
"f_F5": [-44.4, 0.0, 19.8],
}
self.assertEqual(set(planes), set(expected_centers))
for feature_id, center in expected_centers.items():
with self.subTest(feature_id=feature_id):
plane = planes[feature_id]
self.assertEqual(plane["origin_mm"], center)
self.assertEqual(plane["x_dir"], [0.0, -1.0, 0.0])
self.assertAlmostEqual(plane["normal"][0], 1.0)
self.assertAlmostEqual(plane["normal"][1], 0.0)
self.assertAlmostEqual(plane["normal"][2], 0.0)
self.assertFalse(any(
diagnostic.get("operation") == "cPlane"
for diagnostic in result.diagnostics
))
opposite_source = feature.read_text().replace(
'"angle" : 90 * degree, "width"',
'"angle" : 90 * degree, "oppositeDirection" : true, "width"',
1,
)
opposite = lower_model(parse_featurescript(opposite_source, "line-angle-circle-opposite"), {})
opposite_plane = next(
item for item in opposite.cdsl["features"] if item["id"] == "f_F1"
)["params"]["plane"]
self.assertAlmostEqual(opposite_plane["normal"][0], -1.0)
self.assertAlmostEqual(opposite_plane["normal"][1], 0.0)
self.assertAlmostEqual(opposite_plane["normal"][2], 0.0)
def test_line_angle_two_entities_follow_axis_plane_order_and_swap_rules(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
cases = {
# F8 selects Right plane first, then the direct F7 line. The
# standard library must swap them before calculating the frame.
"00298026": ("f_F8", [0.0, -1.0, 0.0], [0.43837114678907757, 0.0, -0.898794046299167]),
# F1 supplies the direct line first and the default plane second.
"00586156": ("f_F1", [0.0, 0.0, 1.0], [-math.sqrt(0.5), -math.sqrt(0.5), 0.0]),
}
for sample_id, (feature_id, expected_x, expected_normal) in cases.items():
with self.subTest(sample_id=sample_id):
source = root / "featurescript_rp" / sample_id[:4] / f"{sample_id}.txt"
result = lower_model(parse_featurescript(source.read_text(), sample_id), {})
plane = next(item for item in result.cdsl["features"] if item["id"] == feature_id)["params"]["plane"]
self.assertEqual(plane["x_dir"], expected_x)
for actual, expected in zip(plane["normal"], expected_normal):
self.assertAlmostEqual(actual, expected)
self.assertFalse(any(
diagnostic.get("operation") == "cPlane"
and diagnostic.get("feature_id") == feature_id.removeprefix("f_")
for diagnostic in result.diagnostics
))
def test_line_angle_two_direct_entities_cover_axis_point_and_degenerate_contracts(self):
result = lower_model(parse_featurescript(LINE_ANGLE_TWO_ENTITY_SOURCE, "line-angle-two-entities"), {})
self.assertEqual(result.status, "converted_partial")
planes = {
item["id"]: item["params"]["plane"]
for item in result.cdsl["features"]
if item["atomic_id"] == "reference_plane"
}
# Nonparallel axes, parallel offset axes, and an axis plus a direct
# point each use the exact source direction, rather than a sketch
# workplane frame.
for feature_id in ("f_F1", "f_F2", "f_F3"):
self.assertEqual(planes[feature_id]["x_dir"], [1.0, 0.0, 0.0])
self.assertEqual(planes[feature_id]["normal"], [0.0, 0.0, 1.0])
# A point selected before its axis triggers FeatureScript's swap. The
# signed angle also follows oppositeDirection.
self.assertEqual(planes["f_F4"]["x_dir"], [1.0, 0.0, 0.0])
self.assertAlmostEqual(planes["f_F4"]["normal"][0], 0.0)
self.assertAlmostEqual(planes["f_F4"]["normal"][1], 1.0)
self.assertAlmostEqual(planes["f_F4"]["normal"][2], 0.0)
# A cPlane output is represented as qCreatedBy(...planeOp, FACE),
# not a sketch source. It is accepted only because F1 is an existing
# reference-plane frame, and it supplies the true zero-angle side.
self.assertEqual(planes["f_F6"]["x_dir"], [1.0, 0.0, 0.0])
self.assertEqual(planes["f_F6"]["normal"], [0.0, 0.0, -1.0])
self.assertIn({
"code": "feature_deferred",
"feature_id": "F5",
"operation": "cPlane",
"message": "line-angle reference selection is degenerate",
}, result.diagnostics)
def test_line_angle_direct_axis_refuses_a_topology_derived_query(self):
direct = Call("sQuery", ["F0.wireOp", "EDGE", "E0"], 1)
derived = Call("makeQuery", [
"F1.opExtrude", "CAP_EDGE", "EDGE",
{"derivedFrom": direct, "isStart": False},
], 2)
sketch_by_source = {"F0": {"workplane": {
"origin_mm": [0.0, 0.0, 0.0], "x_dir": [1.0, 0.0, 0.0], "normal": [0.0, 0.0, 1.0],
}}}
entity_by_sketch = {"F0": {"E0": {
"type": "line", "start": [0.0, 0.0], "end": [10.0, 0.0], "source_entity_id": "E0",
}}}
self.assertIsNotNone(_direct_line_angle_axis(direct, sketch_by_source, entity_by_sketch))
self.assertIsNone(_direct_line_angle_axis(derived, sketch_by_source, entity_by_sketch))
def test_line_angle_direct_axis_refuses_a_query_wrapper_and_keeps_the_prefix(self):
direct = Call("sQuery", ["F0.wireOp", "EDGE", "E0"], 1)
wrapped = Call("qAdjacent", [direct, "VERTEX", "EDGE"], 2)
sketch_by_source = {"F0": {"workplane": {
"origin_mm": [0.0, 0.0, 0.0], "x_dir": [1.0, 0.0, 0.0], "normal": [0.0, 0.0, 1.0],
}}}
entity_by_sketch = {"F0": {"E0": {
"type": "line", "start": [0.0, 0.0], "end": [10.0, 0.0], "source_entity_id": "E0",
}}}
self.assertIsNone(_direct_line_angle_axis(wrapped, sketch_by_source, entity_by_sketch))
model = parse_featurescript(LINE_ANGLE_TWO_ENTITY_SOURCE, "line-angle-wrapped-axis")
plane = next(item for item in model.features if item.feature_id == "F1")
plane.params["entities"].args[0][0] = wrapped
result = lower_model(model, {})
self.assertIn({
"code": "feature_deferred",
"feature_id": "F1",
"operation": "cPlane",
"message": "line-angle reference selection is unsupported",
}, result.diagnostics)
self.assertIn("f_F2", {item["id"] for item in result.cdsl["features"]})
def test_line_angle_rejects_a_circle_center_vertex_as_an_axis(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0003/00030209.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
source = feature.read_text().replace(
'Q0=sQuery(id+"F0.wireOp",EDGE,"E0");',
'Q0=sQuery(id+"F0.wireOp",VERTEX,"E0.center");',
1,
)
result = lower_model(parse_featurescript(source, "line-angle-circle-center"), {})
self.assertIn({
"code": "feature_deferred",
"feature_id": "F1",
"operation": "cPlane",
"message": "line-angle reference selection is unsupported",
}, result.diagnostics)
def test_open_nonconstruction_geometry_is_preserved_as_reference(self):
source = SOURCE.replace('skSolve(sketch);', 'skLineSegment(sketch, "open", {"start":v(0, 0) * mm, "end":v(20, 0) * mm}); skSolve(sketch);', 1)
result = lower_model(parse_featurescript(source, "open-profile"), {})
@@ -931,7 +1239,7 @@ class LoweringTests(unittest.TestCase):
[("outer", 25.5), ("inner", 19.0)],
)
def test_mixed_extrude_rebuilds_surface_limited_chamfer_history(self):
def test_mixed_extrude_preserves_prefix_when_cap_edge_chamfer_query_is_deferred(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0071/00710855.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
@@ -941,10 +1249,10 @@ class LoweringTests(unittest.TestCase):
from cadfs_to_cdsl.rebuild import rebuild_candidate
rebuilt = rebuild_candidate(result.cdsl, Path(tmp) / "rebuilt.step")
self.assertEqual(rebuilt["status"], "rebuilt")
chamfer = next(item for item in rebuilt["result"]["feature_results"] if item["feature_id"] == "f_F10")
self.assertIn("chamfer_surface_limited", [item["code"] for item in chamfer["diagnostics"]])
self.assertAlmostEqual(rebuilt["result"]["volume_mm3"], 72577.33528261917, places=6)
self.assertEqual(rebuilt["status"], "rebuild_failed")
self.assertEqual(rebuilt["error"]["message"], "f_F10: selector_query_unsupported during incremental replay")
self.assertEqual(rebuilt["last_executable_prefix"]["failed_feature_id"], "f_F10")
self.assertEqual(rebuilt["last_executable_prefix"]["last_feature_id"], "f_F9")
def test_fit_spline_loft_lowers_to_executable_loft_add(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -1048,7 +1356,7 @@ class LoweringTests(unittest.TestCase):
"kind": "face", "owner_feature_id": "f_F3", "output_role": "extrude.start",
"source": "runtime_snapshot", "confidence": 1.0,
})
self.assertEqual(selector["selector_intent_version"], "1.0")
self.assertNotIn("selector_intent_version", selector)
self.assertEqual(selector["selector_intent"]["query_family"], "CAP_FACE")
from engine.cdsl_engine.runtime import rebuild_cdsl
@@ -1130,21 +1438,19 @@ class LoweringTests(unittest.TestCase):
})
# F9 rotates F4 only after F5/F7 have consumed it. It cannot be baked
# into the earlier F4 sketch; the unsupported body continuation stays
# explicit and F1--F7 remains an executable prefix.
# into the earlier F4 sketch. The CAP_EDGE geometry is preserved for
# diagnostics, but no source-qualified cap-edge lineage exists for
# this two-sided profile contract.
prefix = deepcopy(result.cdsl)
prefix["features"] = [
item for item in prefix["features"]
if item["id"] in {"f_F1", "f_F2", "f_F4", "f_F5", "f_F7"}
]
from engine.cdsl_engine.runtime import rebuild_cdsl
with tempfile.TemporaryDirectory() as directory:
rebuilt = rebuild_cdsl(prefix, Path(directory) / "two-sided-cap-prefix.step")
self.assertFalse(rebuilt["runtime_diagnostics"])
self.assertEqual(
[item["feature_id"] for item in rebuilt["feature_results"]],
["f_F1", "f_F2", "f_F4", "f_F5", "f_F7"],
)
rebuilt = rebuild_candidate(prefix, Path(directory) / "two-sided-cap-prefix.step")
self.assertEqual(rebuilt["status"], "rebuild_failed")
self.assertEqual(rebuilt["error"]["message"], "f_F7: selector_query_unsupported during incremental replay")
self.assertEqual(rebuilt["last_executable_prefix"]["last_feature_id"], "f_F5")
from engine.cdsl_engine.runtime import analyze_cdsl
f9 = next(item for item in analyze_cdsl(result.cdsl).as_dict()["feature_results"] if item["feature_id"] == "f_F9")
@@ -1338,6 +1644,41 @@ class LoweringTests(unittest.TestCase):
["E14", "E14"],
)
def test_direct_imprint_profile_preserves_selector_intent_and_f1_checkpoint(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0035/00354246.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(feature.read_text(), "00354246"), {})
self.assertEqual(result.status, "converted_complete")
features = {item["id"]: item for item in result.cdsl["features"]}
sketches = {item["id"]: item for item in result.cdsl["geometry"]["sketches"]}
profile = sketches[features["f_F1"]["sketch_id"]]["profile"]
self.assertEqual(profile["type"], "planar_imprint")
self.assertEqual(profile["selections"], [
{"source_entity_id": "E6.MirrorCS", "face_side": -1.0},
{"source_entity_id": "E0.MirrorCS", "face_side": 1.0},
])
selectors = features["f_F2"]["selectors"]
self.assertEqual(len(selectors), 7)
self.assertTrue(all(
selector["selector_intent"]["query_family"] == "SWEPT_FACE"
and selector["selector_intent"]["derivation_policy"] == {
"allowed": ["continuation"], "multiplicity": "none",
}
for selector in selectors
))
self.assertTrue(any("geometry" not in selector for selector in selectors))
with tempfile.TemporaryDirectory() as directory:
rebuilt = Path(directory) / "rebuild.step"
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F2: selector_query_unsupported during incremental replay")
self.assertTrue(rebuilt.exists())
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F1")
def test_direct_translation_transform_updates_the_source_revolve(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0011/00111611.txt"
@@ -1512,6 +1853,121 @@ class LoweringTests(unittest.TestCase):
diagnostics = [item for item in result.diagnostics if item.get("operation") == "cPlane"]
self.assertFalse(diagnostics)
def test_curve_point_plane_uses_an_explicit_bspline_endpoint_tangent(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0082/00827798.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(feature.read_text(), "00827798"), {})
plane = next(item for item in result.cdsl["features"] if item["id"] == "f_F1")
self.assertEqual(plane["atomic_id"], "reference_plane")
self.assertFalse(any(
item.get("operation") == "cPlane" and item.get("feature_id") == "F1"
for item in result.diagnostics
))
expected = [107.86, 0.0, -176.04]
magnitude = math.sqrt(sum(value * value for value in expected))
for actual, value in zip(plane["params"]["plane"]["normal"], expected):
self.assertAlmostEqual(actual, value / magnitude)
second_feature = root / "featurescript_rp/0084/00845891.txt"
if second_feature.exists():
second = lower_model(parse_featurescript(second_feature.read_text(), "00845891"), {})
self.assertEqual(
next(item for item in second.cdsl["features"] if item["id"] == "f_F3")["atomic_id"],
"reference_plane",
)
self.assertFalse(any(
item.get("operation") == "cPlane" and item.get("feature_id") == "F3"
for item in second.diagnostics
))
without_end_derivative = feature.read_text().replace(
', "endDerivative": vector(107.86, -176.04) * mm', "", 1,
)
rejected = lower_model(parse_featurescript(without_end_derivative, "00827798-no-end-derivative"), {})
self.assertIn({
"code": "unsupported_engine_capability",
"capability": "reference_plane:curve_point",
"feature_id": "F1",
"operation": "cPlane",
"message": "CURVE_POINT B-spline endpoint has no exported tangent",
}, rejected.diagnostics)
def test_curve_endpoint_tangent_uses_arc_orientation_without_a_chord_fallback(self):
arc = {
"type": "arc", "start": [1.0, 0.0], "end": [0.0, 1.0],
"center": [0.0, 0.0], "clockwise": False,
}
self.assertEqual(_curve_endpoint_tangent(arc, "start"), [0.0, 1.0])
self.assertEqual(_curve_endpoint_tangent(arc, "end"), [-1.0, 0.0])
with self.assertRaisesRegex(UnsupportedCapability, "direct source-curve endpoint"):
_curve_endpoint_tangent(arc, "internal")
def test_curve_point_plane_uses_exact_bspline_interpolation_vertex_tangents(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
cases = {
"00456498": ("f_F1", "F1", [141.23, 0.0, -13.99]),
"00812557": ("f_F2", "F2", [0.0, 191.15, -139.74]),
}
for sample_id, (cdsl_feature_id, source_feature_id, expected) in cases.items():
with self.subTest(sample_id=sample_id):
source = root / "featurescript_rp" / sample_id[:4] / f"{sample_id}.txt"
if not source.exists():
self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(source.read_text(), sample_id), {})
plane = next(item for item in result.cdsl["features"] if item["id"] == cdsl_feature_id)
self.assertEqual(plane["atomic_id"], "reference_plane")
magnitude = math.sqrt(sum(value * value for value in expected))
for actual, value in zip(plane["params"]["plane"]["normal"], expected):
self.assertAlmostEqual(actual, value / magnitude, places=8)
self.assertFalse(any(
item.get("operation") == "cPlane" and item.get("feature_id") == source_feature_id
for item in result.diagnostics
))
no_derivatives = (root / "featurescript_rp/0045/00456498.txt").read_text().replace(
', "startDerivative": vector(0, 64.52) * mm, "endDerivative": vector(141.23, -13.99) * mm', "", 1,
)
rejected = lower_model(parse_featurescript(no_derivatives, "00456498-no-derivatives"), {})
self.assertIn({
"code": "unsupported_engine_capability",
"capability": "reference_plane:curve_point",
"feature_id": "F1",
"operation": "cPlane",
"message": "CURVE_POINT B-spline interpolation point has no complete source interpolation data",
}, rejected.diagnostics)
derived = (root / "featurescript_rp/0045/00456498.txt").read_text().replace(
'"E0.2.internal"', '"E0.2.endSnap0"', 1,
)
rejected_derived = lower_model(parse_featurescript(derived, "00456498-derived-vertex"), {})
self.assertIn({
"code": "unsupported_engine_capability",
"capability": "reference_plane:curve_point",
"feature_id": "F1",
"operation": "cPlane",
"message": "CURVE_POINT point must name a direct source-curve endpoint or B-spline interpolation vertex",
}, rejected_derived.diagnostics)
def test_curve_point_plane_uses_a_direct_arc_endpoint_tangent(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0005/00057273.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
source = feature.read_text().replace(
'EDGE,"E1"', 'EDGE,"E0"', 1,
).replace(
'VERTEX,"E1.end"', 'VERTEX,"E0.end"', 1,
)
result = lower_model(parse_featurescript(source, "00057273-arc-curve-point"), {})
plane = next(item for item in result.cdsl["features"] if item["id"] == "f_F1")
self.assertEqual(plane["atomic_id"], "reference_plane")
self.assertEqual(plane["params"]["plane"]["origin_mm"], [-24.6, 0.0, 0.0])
self.assertEqual(plane["params"]["plane"]["normal"], [0.0, -1.0, 0.0])
self.assertFalse(any(
item.get("operation") == "cPlane" and item.get("feature_id") == "F1"
for item in result.diagnostics
))
def test_offset_reference_plane_honors_opposite_direction_without_reversing_its_frame(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0077/00777619.txt"
@@ -1571,7 +2027,7 @@ class LoweringTests(unittest.TestCase):
))
self.assertNotIn("F8", {item.get("feature_id") for item in result.diagnostics})
def test_shell_lowers_and_executes_direct_linear_extrude_swept_faces(self):
def test_shell_resolves_direct_prism_swept_faces_through_kernel_lineage(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0059/00594348.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
@@ -1582,18 +2038,105 @@ class LoweringTests(unittest.TestCase):
shell = result.cdsl["features"][shell_index]
self.assertEqual(shell["atomic_id"], "shell")
self.assertEqual([item["owner_feature_id"] for item in shell["selectors"]], ["f_F1", "f_F1"])
self.assertTrue(all("normal" in item["geometry"] for item in shell["selectors"]))
prefix = deepcopy(result.cdsl)
prefix["features"] = prefix["features"][:shell_index + 1]
self.assertTrue(all("geometry" not in item and "stable_id" not in item for item in shell["selectors"]))
self.assertTrue(all(item["selector_intent"]["query_family"] == "SWEPT_FACE" for item in shell["selectors"]))
self.assertTrue(all(item["selector_intent"]["evidence"] == "kernel_history" for item in shell["selectors"]))
self.assertTrue(all(
item["selector_intent"]["derivation_policy"] == {"allowed": ["boundary"], "multiplicity": "one"}
for item in shell["selectors"]
))
with tempfile.TemporaryDirectory() as directory:
outcome = rebuild_candidate(prefix, Path(directory) / "swept-face-shell.step")
outcome = rebuild_candidate(result.cdsl, Path(directory) / "swept-face-shell.step")
self.assertEqual(outcome["status"], "rebuilt")
self.assertEqual(
[item["status"] for item in outcome["result"]["feature_results"]],
["executed", "executed"],
resolutions = [
item for item in outcome["result"]["selector_resolution"]
if item["feature_id"] == "f_F2"
]
self.assertEqual(len(resolutions), 2)
self.assertTrue(all(item["resolution_mode"] == "kernel_lineage" for item in resolutions))
self.assertTrue(all(
item["evidence"]["relations"][0]["source_kind"] == "edge"
and item["evidence"]["relations"][0]["result_kind"] == "face"
for item in resolutions
))
def test_shell_resolves_immediate_direct_prism_cap_faces_through_output_roles(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
cases = (("00212904", "extrude.start"), ("00789939", "extrude.end"))
for sample_id, output_role in cases:
with self.subTest(sample_id=sample_id):
feature = root / "featurescript_rp" / sample_id[:4] / f"{sample_id}.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(feature.read_text(encoding="utf-8"), sample_id), {})
self.assertEqual(result.status, "converted_complete")
shell_index = next(index for index, item in enumerate(result.cdsl["features"]) if item["id"] == "f_F2")
shell = result.cdsl["features"][shell_index]
self.assertEqual(shell["atomic_id"], "shell")
self.assertEqual(len(shell["selectors"]), 1)
selector = shell["selectors"][0]
self.assertEqual(selector["owner_feature_id"], "f_F1")
self.assertEqual(selector["output_role"], output_role)
self.assertEqual(selector["selector_intent"]["query_family"], "CAP_FACE")
self.assertEqual(selector["selector_intent"]["evidence"], "operation_role")
self.assertNotIn("stable_id", selector)
self.assertNotIn("binding_feature_id", selector)
self.assertNotIn("geometry", selector)
prefix = deepcopy(result.cdsl)
prefix["features"] = prefix["features"][:shell_index + 1]
with tempfile.TemporaryDirectory() as directory:
outcome = rebuild_candidate(prefix, Path(directory) / f"{sample_id}-cap-face-shell.step")
self.assertEqual(outcome["status"], "rebuilt")
resolution = next(
item for item in outcome["result"]["selector_resolution"]
if item["feature_id"] == "f_F2"
)
self.assertEqual(resolution["status"], "resolved")
self.assertEqual(resolution["resolution_mode"], "operation_role")
self.assertEqual(resolution["selected"]["output_roles"], [output_role])
def test_shell_direct_prism_swept_face_rejects_unregistered_source_version(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0059/00594348.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
source = feature.read_text(encoding="utf-8").replace("FeatureScript 1511;", "FeatureScript 1793;", 1)
result = lower_model(parse_featurescript(source, "00594348-version-1793"), {})
shell = next(item for item in result.cdsl["features"] if item["id"] == "f_F2")
self.assertTrue(all(
item["selector_intent"]["evidence"] == "feature_script_query"
and item["selector_intent"]["derivation_policy"]["multiplicity"] == "none"
for item in shell["selectors"]
))
with tempfile.TemporaryDirectory() as directory:
outcome = rebuild_candidate(result.cdsl, Path(directory) / "swept-face-shell-1793.step")
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F2: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F1")
def test_shell_direct_prism_swept_face_rejects_unregistered_standard_library_revision(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0059/00594348.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
source = feature.read_text(encoding="utf-8").replace(
'import(path : "onshape/std/geometry.fs", version : "1511.0");',
'import(path : "onshape/std/geometry.fs", version : "1512.0");',
1,
)
self.assertFalse(outcome["result"].get("diagnostics", []))
result = lower_model(parse_featurescript(source, "00594348-library-1512"), {})
shell = next(item for item in result.cdsl["features"] if item["id"] == "f_F2")
self.assertTrue(all(
item["selector_intent"]["source_query"]["featurescript_version"] == "1511"
and item["selector_intent"]["source_query"]["standard_library"] == "onshape/std/geometry.fs"
and item["selector_intent"]["source_query"]["standard_library_version"] == "1512.0"
for item in shell["selectors"]
))
with tempfile.TemporaryDirectory() as directory:
outcome = rebuild_candidate(result.cdsl, Path(directory) / "swept-face-shell-library-1512.step")
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F2: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F1")
def test_shell_offset_face_requires_true_dependency_cap_evidence(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -1623,13 +2166,14 @@ class LoweringTests(unittest.TestCase):
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertTrue(rebuilt.exists())
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["type"], "RuntimeExecutionError")
self.assertEqual(outcome["error"]["type"], "ValueError")
self.assertEqual(outcome["error"]["message"], "f_F5: selector_query_unsupported during incremental replay")
self.assertIn("bound_cdsl", outcome)
self.assertEqual(outcome["last_executable_prefix"]["failed_feature_id"], "f_F5")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F4")
self.assertEqual(outcome["last_executable_prefix"]["result"]["solid_count"], 1)
def test_pattern_copy_selector_binding_rebuilds_the_full_history(self):
def test_pattern_copy_intersection_query_preserves_its_prefix_without_instance_geometry_binding(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0042/00423838.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
@@ -1639,14 +2183,15 @@ class LoweringTests(unittest.TestCase):
rebuilt = Path(directory) / "selector-prefix.step"
outcome = rebuild_candidate(result.cdsl, rebuilt)
self.assertTrue(rebuilt.exists())
self.assertEqual(outcome["status"], "rebuilt")
resolved = [
item for item in outcome["result"]["selector_resolution"]
if item["feature_id"] == "f_F7"
and item["selector"].get("owner_feature_id") == "f_F4.c4.f_F1"
]
self.assertEqual(len(resolved), 2)
self.assertTrue(all(item["status"] == "resolved" for item in resolved))
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F7: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F6")
reference = next(item for item in result.cdsl["features"] if item["id"] == "f_F7")["params"]["end_condition"]["reference"]
self.assertEqual(reference["selector_intent"]["query_family"], "INTERSECT")
self.assertEqual(
[item["selector_intent"]["query_family"] for item in reference["intersection_of"][:2]],
["COPY", "COPY"],
)
def test_shell_rejects_swept_faces_without_direct_linear_extrude_ownership(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
@@ -1698,6 +2243,10 @@ class LoweringTests(unittest.TestCase):
def test_cap_face_selector_retains_versioned_provenance_intent(self):
source = SOURCE.replace(
"FeatureScript 1511;",
'FeatureScript 1511;\nimport(path : "onshape/std/geometry.fs", version : "1511.0");',
1,
).replace(
'\n});\n',
'\n extrude(context, id + "F2", {"entities":makeQuery(id + "F1.opExtrude", "CAP_FACE", FACE, {"isStart":false}), "depth":10 * mm});\n});\n',
)
@@ -1705,12 +2254,127 @@ class LoweringTests(unittest.TestCase):
self.assertEqual(result.status, "converted_complete")
selector = next(item for item in result.cdsl["features"] if item["id"] == "f_F2")["selectors"][0]
self.assertEqual(selector["selector_intent"]["query_family"], "CAP_FACE")
self.assertEqual(selector["selector_intent_version"], "1.0")
self.assertNotIn("selector_intent_version", selector)
self.assertEqual(selector["selector_intent"]["source_query"]["featurescript_version"], "1511")
self.assertEqual(selector["selector_intent"]["source_query"]["standard_library"], "onshape/std/geometry.fs")
self.assertEqual(selector["selector_intent"]["source_query"]["standard_library_version"], "1511.0")
self.assertEqual(selector["selector_intent"]["evidence"], "operation_role")
from engine.cdsl_engine.semantic_validation import validate_semantic_cdsl
self.assertTrue(validate_semantic_cdsl(result.cdsl)["future_rebuild_ready"])
def test_direct_boolean_intersection_lowering_keeps_two_source_contract(self):
base = lower_model(parse_featurescript(SOURCE, "intersection-intent"), {}).cdsl
sketch = base["geometry"]["sketches"][0]
direct = {**base["features"][0], "id": "f_F2", "name": "F2"}
feature_by_id = {
"f_F1": base["features"][0],
"f_F2": direct,
"f_F3": {
"id": "f_F3", "atomic_id": "boolean_bodies",
"params": {
"operation": "subtract", "target_feature_ids": ["f_F1"],
"tool_feature_ids": ["f_F2"], "keep_tools": False,
},
},
}
cap = Call("makeQuery", ["F1.opExtrude", "CAP_FACE", "EntityType.FACE", {"isStart": False}])
source_edge = Call("sQuery", ["F0.wireOp", "EntityType.EDGE", "E0"])
swept = Call("makeQuery", [
"F2.opExtrude", "SWEPT_FACE", "EntityType.FACE",
{"disambiguationData": [Call("OSD", [[source_edge]])]},
])
query = Call("makeQuery", [
"F3.boolean.opBoolean", "INTERSECT", "EntityType.EDGE", {"derivedFrom": [cap, swept]},
])
selector = _direct_boolean_intersection_selector(
query,
feature_by_id=feature_by_id,
feature_frames={"F2": {"profile_source": "F0"}},
sketch_by_source={"F0": sketch},
sketches_by_id={sketch["id"]: sketch},
entity_by_sketch={"F0": {"E0": {"type": "circle", "center": [0, 0], "radius_mm": 9.53}}},
featurescript_version="1511",
)
self.assertIsNotNone(selector)
self.assertNotIn("stable_id", selector)
intent = selector["selector_intent"]
self.assertEqual(intent["derivation_policy"], {"allowed": ["intersection"], "multiplicity": "one"})
self.assertEqual(intent["intersection_sources"], [
{"query_family": "CAP_FACE", "owner_feature_id": "f_F1", "output_role": "extrude.end"},
{"query_family": "SWEPT_FACE", "owner_feature_id": "f_F2", "source_entity": {"sketch_id": "F0", "entity_id": "E0"}},
])
def test_direct_primary_cut_intersection_requires_no_unverified_disambiguation(self):
base = lower_model(parse_featurescript(SOURCE, "primary-intersection-intent"), {}).cdsl
target = base["features"][0]
target_sketch = base["geometry"]["sketches"][0]
tool_sketch = deepcopy(target_sketch)
tool_sketch.update({"id": "sketch_F2", "source_sketch_id": "F2"})
if tool_sketch["profile"].get("type") == "circle":
tool_sketch["profile"]["source_entity_id"] = "E1"
else:
for contour in tool_sketch["profile"]["contours"]:
for segment in contour["segments"]:
segment["source_entity_id"] = "E1"
tool = {
"id": "f_F3", "atomic_id": "extrude_cut_blind", "sketch_id": "sketch_F2",
"params": {"distance_mm": 10, "end_condition": {"type": "blind"}},
}
cap = Call("makeQuery", ["F1.opExtrude", "CAP_FACE", "EntityType.FACE", {"isStart": False}])
tool_edge = Call("sQuery", ["F2.wireOp", "EntityType.EDGE", "E1"])
swept = Call("makeQuery", [
"F3.opExtrude", "SWEPT_FACE", "EntityType.FACE",
{"disambiguationData": [Call("OSD", [[tool_edge]])]},
])
query = Call("makeQuery", [
"F3.boolean.opBoolean", "INTERSECT", "EntityType.EDGE", {"derivedFrom": [cap, swept]},
])
kwargs = {
"feature_by_id": {"f_F1": target, "f_F3": tool},
"feature_frames": {"F3": {"profile_source": "F2"}},
"sketch_by_source": {"F2": tool_sketch},
"sketches_by_id": {tool_sketch["id"]: tool_sketch, target_sketch["id"]: target_sketch},
"entity_by_sketch": {"F2": {"E1": {"type": "circle", "center": [0, 0], "radius_mm": 9.53}}},
"previous": ["f_F1", "f_F3"],
"featurescript_version": "1511",
}
selector = _direct_primary_cut_intersection_selector(query, **kwargs)
self.assertIsNotNone(selector)
self.assertEqual(selector["owner_feature_id"], "f_F3")
self.assertEqual(selector["selector_intent"]["intersection_sources"][1], {
"query_family": "SWEPT_FACE", "owner_feature_id": "f_F3",
"source_entity": {"sketch_id": "F2", "entity_id": "E1"},
})
deferred = Call("makeQuery", [
"F3.boolean.opBoolean", "INTERSECT", "EntityType.EDGE",
{"derivedFrom": [cap, swept], "disambiguationData": [Call("OD", [0.0])]},
])
self.assertIsNone(_direct_primary_cut_intersection_selector(deferred, **kwargs))
def test_deferred_primary_intersect_keeps_the_convertible_f3_checkpoint(self):
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0002/00020311.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
result = lower_model(parse_featurescript(feature.read_text(), "00020311"), {})
f4 = next(item for item in result.cdsl["features"] if item["id"] == "f_F4")
self.assertTrue(all(
selector["kind"] == "edge"
and selector["selector_intent"]["query_family"] == "INTERSECT"
and selector["selector_intent"]["derivation_policy"]["multiplicity"] == "none"
for selector in f4["selectors"]
))
from engine.cdsl_engine.semantic_validation import validate_semantic_cdsl
self.assertTrue(validate_semantic_cdsl(result.cdsl)["future_rebuild_ready"])
with tempfile.TemporaryDirectory() as directory:
outcome = rebuild_candidate(result.cdsl, Path(directory) / "00020311.step")
self.assertTrue((Path(directory) / "00020311.step").exists())
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["message"], "f_F4: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F3")
def test_conversion_writes_status_and_sidecars(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp); source = root / "00000173.txt"; source.write_text(SOURCE)
+6
View File
@@ -58,10 +58,16 @@ class ParserTests(unittest.TestCase):
def test_source_version_and_standard_library_are_retained(self):
source = '''FeatureScript 1511;
import(path : "onshape/std/geometry.fs", version : "1511.0");
import(version : "1511.0", path : "onshape/std/common.fs");
export const f = defineFeature(function(context, id, definition) {});'''
model = parse_featurescript(source, "versioned")
self.assertEqual(model.featurescript_version, "1511")
self.assertEqual(model.standard_library, "onshape/std/geometry.fs")
self.assertEqual(model.standard_library_version, "1511.0")
self.assertEqual(model.standard_library_imports, [
{"path": "onshape/std/geometry.fs", "version": "1511.0"},
{"path": "onshape/std/common.fs", "version": "1511.0"},
])
def test_block_scoped_query_aliases_do_not_use_the_last_assignment(self):
source = r'''
+39 -29
View File
@@ -138,27 +138,45 @@ class SelectorBindingTests(unittest.TestCase):
self.assertEqual(len(binding["resolved"]), 1)
self.assertEqual(binding["resolved"][0]["output_roles"], ["extrude.end"])
def test_shell_offset_face_role_binds_only_its_true_dependency_source(self) -> None:
def test_rebuild_candidate_uses_one_incremental_replay(self) -> None:
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0001/00016195.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00016195"), {})
# The production path must bind and export from one live session. If
# it called the old compatibility binder, this patched entry point
# would fail while rebuilding selector-bearing prefixes.
with tempfile.TemporaryDirectory() as directory:
with patch(
"engine.cdsl_engine.runtime.rebuild_cdsl",
side_effect=AssertionError("production rebuild invoked legacy prefix replay"),
):
outcome = rebuild_candidate(candidate.cdsl, Path(directory) / "00016195.step")
self.assertEqual(outcome["status"], "rebuilt")
self.assertEqual(
[item["feature_id"] for item in outcome["result"]["feature_results"]],
[item["id"] for item in candidate.cdsl["features"]],
)
self.assertTrue(outcome["selector_binding"])
def test_shell_offset_face_preserves_true_dependency_but_rejects_unproven_cap_selector(self) -> None:
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0010/00107631.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00107631"), {})
self.assertEqual(candidate.status, "converted_complete")
# F2 retains F1's end-cap provenance, but F3 also selects the distinct
# start-cap plane. Its direct owner has no geometry-qualified active
# candidate, so binding must consider the unique active F2 face without
# weakening the exact qualified OFFSET_FACE role.
# F2 consumes its immediate direct CAP_FACE output role. F3 retains
# its exact OFFSET_FACE true-dependency intent, but its separate
# start-cap removal selector is deferred and must not rebind
# geometrically.
prefix = deepcopy(candidate.cdsl)
prefix["features"] = prefix["features"][:3]
bound, evidence = bind_candidate_selectors(prefix)
first, offset = bound["features"][-1]["selectors"]
self.assertEqual(first["owner_feature_id"], "f_F2")
self.assertEqual(first["stable_id"], "body:f_F2:face:1")
self.assertEqual(first["snapshot_id"], "body:f_F2:face:1")
self.assertEqual(first["source"], "runtime_snapshot")
self.assertEqual(first["confidence"], 1.0)
first, offset = prefix["features"][-1]["selectors"]
self.assertEqual(first["selector_intent"]["query_family"], "CAP_FACE")
self.assertEqual(first["selector_intent"]["derivation_policy"]["multiplicity"], "none")
self.assertEqual({key: offset[key] for key in (
"kind", "owner_feature_id", "output_role", "output_role_source", "source", "confidence",
)}, {
@@ -168,33 +186,25 @@ class SelectorBindingTests(unittest.TestCase):
})
self.assertEqual(offset["selector_intent"]["query_family"], "OFFSET_FACE")
self.assertEqual(offset["selector_intent"]["disambiguation"]["type"], "true_dependency")
binding = next(item for item in evidence if item["feature_id"] == "f_F3")
self.assertEqual(binding["resolved"][0]["snapshot_id"], "body:f_F2:face:1")
self.assertEqual(binding["resolved"][1]["record_id"], "body:f_F2:face:5")
with self.assertRaisesRegex(ValueError, "f_F3: selector_query_unsupported after prefix rebuild"):
bind_candidate_selectors(prefix)
# Binding must not hide the OCC feasibility boundary. The requested
# second shell currently produces an invalid shape, so preserve the
# F2 prefix instead of changing thickness or removal faces.
with tempfile.TemporaryDirectory() as directory:
outcome = rebuild_candidate(candidate.cdsl, Path(directory) / "00107631.step")
self.assertEqual(outcome["status"], "rebuild_failed")
self.assertEqual(outcome["error"]["type"], "RuntimeExecutionError")
self.assertEqual(outcome["error"]["message"], "OCC shell operation produced an invalid shape")
self.assertEqual(outcome["error"]["message"], "f_F3: selector_query_unsupported during incremental replay")
self.assertEqual(outcome["last_executable_prefix"]["failed_feature_id"], "f_F3")
self.assertEqual(outcome["last_executable_prefix"]["last_feature_id"], "f_F2")
def test_intersection_vertex_binds_a_pattern_copy_with_exact_instance_owner_evidence(self) -> None:
def test_intersection_vertex_preserves_outer_copy_provenance_without_geometry_binding(self) -> None:
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0042/00423838.txt"
if not feature.exists(): self.skipTest("CADFS sample is not installed")
candidate = lower_model(parse_featurescript(feature.read_text(), "00423838"), {})
reference = next(item for item in candidate.cdsl["features"] if item["id"] == "f_F7")["params"]["end_condition"]["reference"]
self.assertTrue(all(item["owner_match_required"] for item in reference["intersection_of"][:2]))
bound, evidence = bind_candidate_selectors(candidate.cdsl)
reference = next(item for item in bound["features"] if item["id"] == "f_F7")["params"]["end_condition"]["reference"]
self.assertEqual(reference["selector_intent"]["query_family"], "INTERSECT")
copy_components = reference["intersection_of"][:2]
self.assertTrue(all(item["owner_feature_id"] == "f_F4.c4.f_F1" for item in copy_components))
self.assertTrue(all(item.get("snapshot_id") for item in copy_components))
binding = next(item for item in evidence if item["feature_id"] == "f_F7")
self.assertGreaterEqual(len(binding["resolved"]), 2)
self.assertEqual([item["selector_intent"]["query_family"] for item in copy_components], ["COPY", "COPY"])
with self.assertRaisesRegex(ValueError, "f_F7: selector_query_unsupported after prefix rebuild"):
bind_candidate_selectors(candidate.cdsl)
@@ -0,0 +1,71 @@
from __future__ import annotations
import tempfile
import unittest
from pathlib import Path
from cadfs_to_cdsl.dataset import Sample
from cadfs_to_cdsl.reports import read_json
from cadfs_to_cdsl.selector_candidate_demo import _search_candidate_records, run_geometry_probe, strip_provenance_intents
def _contains_selector_intent(value: object) -> bool:
if isinstance(value, dict):
return "selector_intent" in value or any(_contains_selector_intent(child) for child in value.values())
if isinstance(value, list):
return any(_contains_selector_intent(child) for child in value)
return False
class SelectorCandidateDemoTests(unittest.TestCase):
def test_search_candidates_excludes_records_below_normal_resolution_threshold(self) -> None:
resolution = type("Resolution", (), {
"candidates": (
{"record_id": "low", "score": 0.5},
{"record_id": "high-b", "score": 1.0},
{"record_id": "high-a", "score": 1.0},
{"record_id": "zero", "score": 0.0},
),
})()
candidates, count = _search_candidate_records(resolution, maximum=8)
self.assertEqual(count, 2)
self.assertEqual([item["record_id"] for item in candidates], ["high-a", "high-b"])
def test_strip_provenance_intents_removes_nested_selector_metadata(self) -> None:
value = {
"selector_intent": {"version": "1.0"},
"params": {"reference": {"selector_intent_version": "1.0"}},
}
self.assertEqual(strip_provenance_intents(value), 2)
self.assertEqual(value, {"params": {"reference": {}}})
def test_geometry_probe_keeps_heuristic_success_separate_from_provenance(self) -> None:
root = Path(__file__).parents[2] / "data/cadfs-sample/CADFS_test"
feature = root / "featurescript_rp/0000/00002243.txt"
step = root / "step_abc/0000/00002243.step"
if not feature.exists() or not step.exists():
self.skipTest("CADFS sample is not installed")
sample = Sample("00002243", {"featurescript": str(feature), "step": str(step)})
with tempfile.TemporaryDirectory() as temporary:
output = Path(temporary)
report = run_geometry_probe(sample, output)
record = read_json(output / "samples" / sample.sample_id / "selector-record.json")
compiled = read_json(output / "samples" / sample.sample_id / "selector_record.compiled.cdsl.json")
self.assertEqual(report["provenance"]["rebuild_status"], "rebuild_failed")
self.assertEqual(report["geometry_probe"]["rebuild_status"], "rebuilt")
self.assertEqual(report["geometry_probe"]["comparison"], {
"decision": "strict_pass",
"strict_passed": True,
"rp_passed": True,
})
self.assertEqual(report["strategy"]["classification"], "heuristic_only")
self.assertEqual(report["selector_record"]["status"], "strict_replayed")
self.assertEqual(report["selector_record"]["record_count"], 2)
self.assertEqual(record["verification"], {
"geometry_probe_strict": True,
"compiled_cdsl_strict": True,
"compiled_cdsl": report["selector_record"]["compiled_cdsl"],
})
self.assertEqual([entry["mapping"] for entry in record["records"]], ["one_to_one", "one_to_one"])
self.assertTrue(all(entry["source_selector"]["selector_intent"]["query_family"] == "CAP_EDGE" for entry in record["records"]))
self.assertFalse(_contains_selector_intent(compiled))