feat(cadfs): 补齐核心建模能力并建立代表性回归

- 新增 loft、双向切除、through-all/up-to-next 等 CADFS lowering 与 engine 支持
- 支持多种 reference plane、B-spline profile 和 circular pattern replay
- 保留 transform 历史,并烘焙安全的单源平移/旋转变换
- 改进 selector 绑定、拓扑快照和 pattern 变换处理
- 建立 17 个代表样本的转换、重建与比较回归工具链
- 补充 schema、author guidance、运行时和几何回归测试
This commit is contained in:
2026-09-07 18:21:07 +08:00
parent 4ad890b7b4
commit 038d38ed98
31 changed files with 2498 additions and 85 deletions
@@ -316,6 +316,55 @@ class PatternTransformContractTests(unittest.TestCase):
self.assertAlmostEqual(rebuilt["volume_mm3"], 1000 + 72 + 24, places=5)
def test_circular_pattern_rotates_analytic_arc_normals_with_the_sketch(self) -> None:
"""倾斜圆草图的环形阵列必须保持每条圆弧处于旋转后的工作平面。
``analytic_contours`` 在运行时会预先展开为世界坐标的圆弧边。环形阵列
不能只旋转边端点和圆心:Build123d 依据 edge.normal 计算三点圆弧中点,
法向遗留在源草图平面会让中点偏离 wire 平面,最终报
``Cannot build face(s): wires not planar``。该 fixture 使用绕 Z 轴的
垂直圆草图,保证每个实例都实际变换该法向。
"""
cdsl = {
"schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part",
"part_id": "circular-arc-normal", "meta": {"unit": "mm"},
"geometry": {"sketches": [
{
"id": "base", "workplane": _workplane(origin=[0, 0, 0], normal=[0, 0, 1]),
"profile": {"type": "circle", "center": [0, 0], "radius_mm": 10},
},
{
"id": "boss", "workplane": _workplane(origin=[0, -10, 0], normal=[0, 1, 0]),
"profile": {"type": "analytic_contours", "contours": [{
"role": "outer", "closed": True,
"segments": [{"type": "circle", "center": [0, -5], "radius_mm": 1}],
}]},
},
]},
"features": [
{
"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": [],
"sketch_id": "base", "params": {"distance_mm": 10},
},
{
"id": "boss_add", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"],
"sketch_id": "boss", "params": {"distance_mm": 2},
},
{
"id": "repeat", "atomic_id": "pattern_circular", "depends_on": ["boss_add"],
"params": {
"source_feature_ids": ["boss_add"],
"axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]},
"pattern_count": 4, "sweep_angle_deg": 360,
},
},
],
}
with tempfile.TemporaryDirectory() as directory:
rebuilt = rebuild_cdsl(cdsl, Path(directory) / "circular-arc-normal.step")
self.assertGreater(rebuilt["volume_mm3"], math.pi * 1000)
self.assertEqual(rebuilt["solid_count"], 1)
if __name__ == "__main__":
unittest.main()