From 8388f9d15ca4360de648ac8bc730c08fe0e5f47b Mon Sep 17 00:00:00 2001 From: Zhaowenlong Date: Mon, 1 Jun 2026 14:37:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=89=A9=E5=B1=95=E8=BF=87=E7=BA=BF?= =?UTF-8?q?=E5=AD=94=E5=B8=83=E7=BA=BF=E6=A1=A5=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/FreeCAD 3D自动布线设计方案.md | 3 ++ src/Mod/FreeCADExchange/RoutingNetwork.py | 14 ++++++-- .../freecad_exchange_auto_routing_test.py | 35 ++++++++++++++++++- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/docs/FreeCAD 3D自动布线设计方案.md b/docs/FreeCAD 3D自动布线设计方案.md index 41459a8..928aa3a 100644 --- a/docs/FreeCAD 3D自动布线设计方案.md +++ b/docs/FreeCAD 3D自动布线设计方案.md @@ -334,6 +334,8 @@ src/Mod/FreeCADExchange/InitGui.py 生成线槽 carrier 时,系统除了 `WireDuct` 中心路径,还会在线槽两端生成 `WireDuctOpenEnd` 横向路径;对象名或标签包含 `Wiring Cut-Out`、`wire cutout`、`穿线孔`、`过线孔` 等语义时,会生成 `WiringCutOut` 穿线路径载体。 +`WiringCutOut` 不是按开孔实体外轮廓布线,而是按开孔包围盒最薄方向生成一条虚拟穿线路径,并在穿孔方向两端做小距离外扩。这样安装板、隔板或线槽侧壁上的孔可以接到孔两侧附近的线槽中心线或支撑面网格,避免路径只停留在板厚范围内而无法连通网络。 + 自动生成的 carrier 会随源对象生命周期刷新:源对象仍有效时更新几何;安装板尺寸变化时同步增删 `RoutingRange` 网格线;源对象被删除或不再满足线槽/支撑面规则时,下一次生成布线路径网络会删除对应自动 carrier,并撤销该源对象的穿越/支撑面障碍模式。用户手工创建、没有源对象元数据的 carrier 不会被这一步自动删除。 ### 5.3 布线连接功能 @@ -408,6 +410,7 @@ tests/python/freecad_exchange_auto_routing_test.py 22. 线槽端部会生成 `WireDuctOpenEnd` 横向路径,穿线孔/过线孔会生成 `WiringCutOut` carrier。 23. 导线会保存 routing track;网络检查会生成 `RoutingPathNetwork` 诊断对象。 24. 自动生成的线槽、过线孔和支撑面 carrier 会在源对象移动、缩放、删除或失效后刷新/清理。 +25. `WiringCutOut` 会在穿孔方向外扩虚拟路径,用于桥接开孔两侧附近的线槽或支撑面网络。 已完成 FreeCAD smoke: diff --git a/src/Mod/FreeCADExchange/RoutingNetwork.py b/src/Mod/FreeCADExchange/RoutingNetwork.py index 5194266..f09f234 100644 --- a/src/Mod/FreeCADExchange/RoutingNetwork.py +++ b/src/Mod/FreeCADExchange/RoutingNetwork.py @@ -38,6 +38,7 @@ DEFAULT_ROUTE_PATH_FACE_OFFSET = 2.0 DEFAULT_AUTO_WIRE_DUCT_MIN_ASPECT = 2.5 DEFAULT_TERMINAL_ACCESS_MAX_DISTANCE = 1000.0 DEFAULT_ADJOINING_DUCT_TOLERANCE = 5.0 +DEFAULT_WIRING_CUT_OUT_BRIDGE_EXTENSION = 20.0 WIRE_DUCT_OBSTACLE_MODE = "PassThrough" SUPPORT_SURFACE_OBSTACLE_MODE = "SupportSurface" WIRE_DUCT_NAME_KEYWORDS = ( @@ -1466,7 +1467,7 @@ def _sync_wire_duct_source_carriers(doc, source, spec, project_uuid="", capacity return True -def _wiring_cut_out_points_from_bbox(bbox): +def _wiring_cut_out_points_from_bbox(bbox, bridge_extension=0.0): extents = _bbox_extents(bbox) if not extents: return [] @@ -1482,6 +1483,9 @@ def _wiring_cut_out_points_from_bbox(bbox): fallback = max(other_extents or [DEFAULT_WIRE_DUCT_OPEN_END_MIN_LENGTH]) low = _axis_value(center, through_axis) - fallback * 0.5 high = _axis_value(center, through_axis) + fallback * 0.5 + extension = max(float(bridge_extension or 0.0), 0.0) + low -= extension + high += extension start = _set_axis(center, through_axis, low) end = _set_axis(center, through_axis, high) if _distance(start, end) <= DEFAULT_NODE_TOLERANCE: @@ -1887,7 +1891,11 @@ def create_wire_duct_carriers_from_document( return created -def create_wiring_cut_out_carriers_from_document(doc, project_uuid=""): +def create_wiring_cut_out_carriers_from_document( + doc, + project_uuid="", + bridge_extension=DEFAULT_WIRING_CUT_OUT_BRIDGE_EXTENSION, +): """Create pass-through route carriers for wiring cut-out objects.""" cleanup_invalid_source_carriers(doc) created = [] @@ -1895,7 +1903,7 @@ def create_wiring_cut_out_carriers_from_document(doc, project_uuid=""): bbox = _bound_box_from_object(source) if bbox is None: continue - points = _wiring_cut_out_points_from_bbox(bbox) + points = _wiring_cut_out_points_from_bbox(bbox, bridge_extension=bridge_extension) if len(points) < 2: continue live_carrier = _live_source_carrier(doc, source) diff --git a/tests/python/freecad_exchange_auto_routing_test.py b/tests/python/freecad_exchange_auto_routing_test.py index 1be017e..317ffe7 100644 --- a/tests/python/freecad_exchange_auto_routing_test.py +++ b/tests/python/freecad_exchange_auto_routing_test.py @@ -1295,7 +1295,40 @@ class AutoRoutingTest(unittest.TestCase): self.assertEqual(1, first["wiring_cut_out_carriers"]) self.assertEqual(0, second["wiring_cut_out_carriers"]) self.assertEqual(1, len(cut_out_carriers)) - self.assertEqual([(70.0, -2.0, 20.0), (70.0, 2.0, 20.0)], [(p.x, p.y, p.z) for p in cut_out_carriers[0].Points]) + self.assertEqual([(70.0, -22.0, 20.0), (70.0, 22.0, 20.0)], [(p.x, p.y, p.z) for p in cut_out_carriers[0].Points]) + + def test_wiring_cut_out_bridges_nearby_ducts_on_both_sides_of_panel(self): + _install_fake_freecad() + terminal_objects, _wiring_objects, routing_network, auto_routing = _reload_modules() + auto_routing_panel = importlib.import_module("AutoRoutingPanel") + app = sys.modules["FreeCAD"] + doc = FakeDocument() + app.ActiveDocument = doc + terminal_objects.ensure_root_group(doc, "project-1") + start = _terminal(doc, terminal_objects, "TerminalStart", "terminal-start", app.Vector(0, -20, 0)) + end = _terminal(doc, terminal_objects, "TerminalEnd", "terminal-end", app.Vector(100, 20, 0)) + routing_network.create_route_carrier( + doc, + [app.Vector(0, -20, 20), app.Vector(50, -20, 20)], + project_uuid="project-1", + kind="WireDuct", + ) + routing_network.create_route_carrier( + doc, + [app.Vector(50, 20, 20), app.Vector(100, 20, 20)], + project_uuid="project-1", + kind="WireDuct", + ) + cut_out = doc.addObject("Part::Feature", "WiringCutoutA") + cut_out.Label = "过线孔A" + cut_out.Shape = FakeShape(FakeBoundBox(45, 55, -2, 2, 15, 25)) + + auto_routing_panel.AutoRoutingController().generate_routing_paths() + result = auto_routing.route_eplan_connection_between_terminals(doc, start, end) + + self.assertEqual("Routed", result["route_status"]) + self.assertIn("WiringCutOut", result["route_track"]["carrier_kinds"]) + self.assertEqual(0, result["collision_count"]) def test_check_routing_path_network_writes_diagnostic_for_unconnected_terminal(self): _install_fake_freecad()