diff --git a/docs/FreeCAD 3D自动布线设计方案.md b/docs/FreeCAD 3D自动布线设计方案.md index 928aa3a..f759dff 100644 --- a/docs/FreeCAD 3D自动布线设计方案.md +++ b/docs/FreeCAD 3D自动布线设计方案.md @@ -336,6 +336,14 @@ src/Mod/FreeCADExchange/InitGui.py `WiringCutOut` 不是按开孔实体外轮廓布线,而是按开孔包围盒最薄方向生成一条虚拟穿线路径,并在穿孔方向两端做小距离外扩。这样安装板、隔板或线槽侧壁上的孔可以接到孔两侧附近的线槽中心线或支撑面网格,避免路径只停留在板厚范围内而无法连通网络。 +过线孔源对象可通过下面的 FreeCAD 属性调整外扩距离: + +```text +QetWiringCutOutBridgeExtensionMm = 20.0 +``` + +该属性表示在穿孔方向每一侧额外延长多少毫米。默认值用于常见线槽中心线与板面/孔位有少量间距的情况;如果现场模型中线槽中心离开孔更近或更远,可以在 FreeCAD 属性面板里按对象单独调整。 + 自动生成的 carrier 会随源对象生命周期刷新:源对象仍有效时更新几何;安装板尺寸变化时同步增删 `RoutingRange` 网格线;源对象被删除或不再满足线槽/支撑面规则时,下一次生成布线路径网络会删除对应自动 carrier,并撤销该源对象的穿越/支撑面障碍模式。用户手工创建、没有源对象元数据的 carrier 不会被这一步自动删除。 ### 5.3 布线连接功能 @@ -410,7 +418,7 @@ tests/python/freecad_exchange_auto_routing_test.py 22. 线槽端部会生成 `WireDuctOpenEnd` 横向路径,穿线孔/过线孔会生成 `WiringCutOut` carrier。 23. 导线会保存 routing track;网络检查会生成 `RoutingPathNetwork` 诊断对象。 24. 自动生成的线槽、过线孔和支撑面 carrier 会在源对象移动、缩放、删除或失效后刷新/清理。 -25. `WiringCutOut` 会在穿孔方向外扩虚拟路径,用于桥接开孔两侧附近的线槽或支撑面网络。 +25. `WiringCutOut` 会在穿孔方向外扩虚拟路径,用于桥接开孔两侧附近的线槽或支撑面网络,并支持通过 `QetWiringCutOutBridgeExtensionMm` 按对象调整外扩距离。 已完成 FreeCAD smoke: diff --git a/src/Mod/FreeCADExchange/RoutingNetwork.py b/src/Mod/FreeCADExchange/RoutingNetwork.py index f09f234..2bb141f 100644 --- a/src/Mod/FreeCADExchange/RoutingNetwork.py +++ b/src/Mod/FreeCADExchange/RoutingNetwork.py @@ -429,6 +429,30 @@ def _ensure_integer_property(obj, prop_name, description, value): setattr(obj, prop_name, 0) +def _ensure_float_property(obj, prop_name, description, value): + if prop_name not in getattr(obj, "PropertiesList", []): + obj.addProperty( + "App::PropertyFloat", + prop_name, + PROPERTY_GROUP, + description, + ) + try: + setattr(obj, prop_name, float(value)) + except Exception: + setattr(obj, prop_name, 0.0) + + +def _wiring_cut_out_bridge_extension_value(source, default=DEFAULT_WIRING_CUT_OUT_BRIDGE_EXTENSION): + try: + value = float(getattr(source, "QetWiringCutOutBridgeExtensionMm", default) or 0.0) + except Exception: + value = float(default or 0.0) + if value < 0.0: + return 0.0 + return value + + def _set_route_carrier_semantics(obj, project_uuid="", kind=ROUTE_CARRIER_KIND, capacity=1): TerminalObjects.ensure_string_property( obj, @@ -522,7 +546,7 @@ def _set_support_surface_source_semantics(source): ) -def _set_wiring_cut_out_source_semantics(source): +def _set_wiring_cut_out_source_semantics(source, bridge_extension=DEFAULT_WIRING_CUT_OUT_BRIDGE_EXTENSION): if source is None: return TerminalObjects.ensure_string_property( @@ -539,6 +563,12 @@ def _set_wiring_cut_out_source_semantics(source): "How routing connection collision checks should treat this object", WIRE_DUCT_OBSTACLE_MODE, ) + _ensure_float_property( + source, + "QetWiringCutOutBridgeExtensionMm", + "How far the generated wiring cut-out carrier extends beyond each side of the opening", + _wiring_cut_out_bridge_extension_value(source, default=bridge_extension), + ) def _style_route_carrier(carrier, kind): @@ -1632,11 +1662,11 @@ def _mark_support_surface_source(source, carriers): pass -def _mark_wiring_cut_out_source(source, carrier): +def _mark_wiring_cut_out_source(source, carrier, bridge_extension=DEFAULT_WIRING_CUT_OUT_BRIDGE_EXTENSION): if source is None or carrier is None: return try: - _set_wiring_cut_out_source_semantics(source) + _set_wiring_cut_out_source_semantics(source, bridge_extension=bridge_extension) TerminalObjects.ensure_string_property( source, "QetRouteCarrierName", @@ -1903,7 +1933,8 @@ def create_wiring_cut_out_carriers_from_document( bbox = _bound_box_from_object(source) if bbox is None: continue - points = _wiring_cut_out_points_from_bbox(bbox, bridge_extension=bridge_extension) + source_bridge_extension = _wiring_cut_out_bridge_extension_value(source, default=bridge_extension) + points = _wiring_cut_out_points_from_bbox(bbox, bridge_extension=source_bridge_extension) if len(points) < 2: continue live_carrier = _live_source_carrier(doc, source) @@ -1914,7 +1945,7 @@ def create_wiring_cut_out_carriers_from_document( project_uuid=project_uuid, kind=ROUTE_CARRIER_KIND_WIRING_CUT_OUT, ): - _mark_wiring_cut_out_source(source, live_carrier) + _mark_wiring_cut_out_source(source, live_carrier, bridge_extension=source_bridge_extension) try: doc.recompute() except Exception: @@ -1928,7 +1959,7 @@ def create_wiring_cut_out_carriers_from_document( project_uuid=project_uuid, kind=ROUTE_CARRIER_KIND_WIRING_CUT_OUT, ) - _mark_wiring_cut_out_source(source, carrier) + _mark_wiring_cut_out_source(source, carrier, bridge_extension=source_bridge_extension) created.append(carrier) return created diff --git a/tests/python/freecad_exchange_auto_routing_test.py b/tests/python/freecad_exchange_auto_routing_test.py index 317ffe7..a52709b 100644 --- a/tests/python/freecad_exchange_auto_routing_test.py +++ b/tests/python/freecad_exchange_auto_routing_test.py @@ -1297,6 +1297,31 @@ class AutoRoutingTest(unittest.TestCase): self.assertEqual(1, len(cut_out_carriers)) 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_source_bridge_extension_controls_generated_path_length(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") + cut_out = doc.addObject("Part::Feature", "WiringCutoutA") + cut_out.Label = "过线孔A" + cut_out.Shape = FakeShape(FakeBoundBox(45, 55, -2, 2, 15, 25)) + cut_out.QetWiringCutOutBridgeExtensionMm = 8.0 + + auto_routing_panel.AutoRoutingController().generate_routing_paths() + cut_out_carriers = [ + carrier + for carrier in routing_network.collect_route_carriers(doc) + if getattr(carrier, "QetRouteCarrierKind", "") == "WiringCutOut" + ] + + self.assertEqual(1, len(cut_out_carriers)) + self.assertIn("QetWiringCutOutBridgeExtensionMm", cut_out.PropertiesList) + self.assertEqual(8.0, cut_out.QetWiringCutOutBridgeExtensionMm) + self.assertEqual([(50.0, -10.0, 20.0), (50.0, 10.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()