From e72e581f853aa12aceab90de81fd9db2a81fdb3a Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 1 Sep 2025 15:40:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E4=BA=86=E4=BC=A0=E7=BB=9F?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/PathPlanningManager.cs | 628 +++++++++--------- src/PathPlanning/AutoPathFinder.cs | 349 +--------- src/PathPlanning/GridMapGenerator.cs | 958 +-------------------------- 3 files changed, 361 insertions(+), 1574 deletions(-) diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs index f27e2a7..8ce7214 100644 --- a/src/Core/PathPlanningManager.cs +++ b/src/Core/PathPlanningManager.cs @@ -23,7 +23,7 @@ namespace NavisworksTransport private readonly CategoryAttributeManager _categoryManager; private readonly VisibilityManager _visibilityManager; private readonly UIStateManager _uiStateManager; - + private CoordinateConverter _coordinateConverter; private PathPointRenderPlugin _renderPlugin; private List _selectedChannels; @@ -36,31 +36,31 @@ namespace NavisworksTransport private PathPointType _currentPointType = PathPointType.WayPoint; private PathRoute _editingRoute = null; private PathHistoryManager _historyManager; - + // ToolPlugin 集成 private bool _isToolPluginActive = false; - + // 自动路径规划模式标志 private bool _isInAutoPathMode = false; - + // 路径点3D标记管理 private List _pathPointMarkers; - + // 预览点管理 private PathPoint _previewPoint = null; private bool _isPreviewMode = false; private int _previewInsertIndex = -1; // 保存预览点应该插入的索引位置 - + // 修改路径点管理 private int _editingPointIndex = -1; // 正在修改的路径点索引 private PathPoint _originalPoint = null; // 修改前的原始路径点 private PathPoint _editingPreviewPoint = null; // 修改时的预览路径点 - + /// /// 获取当前是否在预览模式 /// public bool IsPreviewMode => _isPreviewMode; - + // 静态引用,用于处理ToolPlugin事件 private static PathPlanningManager _activePathManager; @@ -80,7 +80,7 @@ namespace NavisworksTransport _categoryManager = categoryManager ?? throw new ArgumentNullException(nameof(categoryManager)); _visibilityManager = visibilityManager ?? throw new ArgumentNullException(nameof(visibilityManager)); _uiStateManager = uiStateManager ?? UIStateManager.Instance; - + InitializeManager(); } @@ -93,7 +93,7 @@ namespace NavisworksTransport _categoryManager = new CategoryAttributeManager(); _visibilityManager = new VisibilityManager(); _uiStateManager = UIStateManager.Instance; - + InitializeManager(); } @@ -102,7 +102,7 @@ namespace NavisworksTransport // 设置静态引用,确保所有地方都使用同一个实例 _activePathManager = this; LogManager.WriteLog($"[路径管理] 设置_activePathManager静态引用, ManagerId: {_managerId}"); - + // 获取已注册的圆形渲染插件实例 try { @@ -123,7 +123,7 @@ namespace NavisworksTransport LogManager.WriteLog($"[路径管理] 异常堆栈: {ex.StackTrace}"); _renderPlugin = null; } - + _selectedChannels = new List(); _routes = new List(); _currentRoute = new PathRoute("默认路径"); @@ -145,14 +145,14 @@ namespace NavisworksTransport public PathEditState PathEditState { get { return _pathEditState; } - private set - { + private set + { var previousState = _pathEditState; _pathEditState = value; - + // 触发状态变更事件 RaiseEditStateChanged(previousState, value, _editingRoute); - + LogManager.Info($"路径编辑状态变更: {previousState} -> {value}"); } } @@ -171,11 +171,11 @@ namespace NavisworksTransport public PathRoute CurrentRoute { get { return _currentRoute; } - private set - { + private set + { var previousRoute = _currentRoute; _currentRoute = value; - + // 触发当前路径变更事件 RaiseCurrentRouteChanged(previousRoute, value); } @@ -222,28 +222,28 @@ namespace NavisworksTransport // 保留原有事件以保持向后兼容 [Obsolete("请使用新的强类型事件接口")] public event EventHandler> ChannelsSelected; - + [Obsolete("请使用新的强类型事件接口")] public event EventHandler CurrentRouteChanged_Legacy; - + [Obsolete("请使用新的强类型事件接口")] public event EventHandler RouteGenerated_Legacy; - + [Obsolete("请使用新的强类型事件接口")] public event EventHandler StatusChanged_Legacy; - + [Obsolete("请使用新的强类型事件接口")] public event EventHandler ErrorOccurred_Legacy; [Obsolete("请使用新的强类型事件接口")] public event EventHandler PathEditStateChanged; - + [Obsolete("请使用新的强类型事件接口")] public event EventHandler PathPointAddedIn3D; - + [Obsolete("请使用新的强类型事件接口")] public event EventHandler PathPointRemovedFrom3D; - + [Obsolete("请使用新的强类型事件接口")] public event EventHandler PathPointsListUpdated_Legacy; @@ -256,11 +256,11 @@ namespace NavisworksTransport try { var eventArgs = new PathPlanningStatusChangedEventArgs(statusMessage, statusType, additionalData, _managerId); - + // 使用UIStateManager安全地触发事件 if (_uiStateManager != null) { - _uiStateManager.QueueUIUpdate(() => + _uiStateManager.QueueUIUpdate(() => { StatusChanged?.Invoke(this, eventArgs); // 向后兼容的事件 @@ -286,11 +286,11 @@ namespace NavisworksTransport try { var eventArgs = new PathPlanningErrorOccurredEventArgs(errorMessage, exception, errorLevel, _managerId); - + // 使用UIStateManager安全地触发事件 if (_uiStateManager != null) { - _uiStateManager.QueueUIUpdate(() => + _uiStateManager.QueueUIUpdate(() => { ErrorOccurred?.Invoke(this, eventArgs); // 向后兼容的事件 @@ -320,10 +320,10 @@ namespace NavisworksTransport try { var eventArgs = new PathEditStateChangedEventArgs(previousState, newState, editingRoute, _managerId); - + if (_uiStateManager != null) { - _uiStateManager.QueueUIUpdate(() => + _uiStateManager.QueueUIUpdate(() => { EditStateChanged?.Invoke(this, eventArgs); // 向后兼容的事件 @@ -347,10 +347,10 @@ namespace NavisworksTransport try { var eventArgs = new ChannelSelectionChangedEventArgs(selectedChannels, selectionMethod, _managerId); - + if (_uiStateManager != null) { - _uiStateManager.QueueUIUpdate(() => + _uiStateManager.QueueUIUpdate(() => { ChannelSelectionChanged?.Invoke(this, eventArgs); // 向后兼容的事件 @@ -374,13 +374,13 @@ namespace NavisworksTransport try { var eventArgs = new PathPointOperationEventArgs(operationType, pathPoint, route, _managerId); - + if (_uiStateManager != null) { - _uiStateManager.QueueUIUpdate(() => + _uiStateManager.QueueUIUpdate(() => { PathPointOperation?.Invoke(this, eventArgs); - + // 向后兼容的事件 if (operationType == PathPointOperationType.Added) { @@ -395,7 +395,7 @@ namespace NavisworksTransport else { PathPointOperation?.Invoke(this, eventArgs); - + if (operationType == PathPointOperationType.Added) { PathPointAddedIn3D?.Invoke(this, pathPoint); @@ -417,15 +417,15 @@ namespace NavisworksTransport try { LogManager.Info($"*** RaiseRouteGenerated被调用: {route?.Name}, Method: {generationMethod}, GridSize: {gridSize} ***"); - + var eventArgs = new RouteGeneratedEventArgs(route, generationMethod, gridSize, _managerId); - + // 检查是否有订阅者 if (RouteGenerated != null) { var invocationList = RouteGenerated.GetInvocationList(); LogManager.Info($"*** RouteGenerated事件有 {invocationList.Length} 个订阅者 ***"); - + for (int i = 0; i < invocationList.Length; i++) { var handler = invocationList[i]; @@ -436,12 +436,12 @@ namespace NavisworksTransport { LogManager.Warning("*** RouteGenerated事件没有订阅者!!! ***"); } - + // 测试强制同步处理:使用高优先级QueueUIUpdate代替立即执行 if (_uiStateManager != null) { LogManager.Info("*** 使用高优先级强制同步处理RouteGenerated事件 ***"); - _uiStateManager.QueueUIUpdateWithForcedSync(() => + _uiStateManager.QueueUIUpdateWithForcedSync(() => { LogManager.Info($"*** 强制同步队列中执行RouteGenerated事件: {route?.Name} ***"); RouteGenerated?.Invoke(this, eventArgs); @@ -469,10 +469,10 @@ namespace NavisworksTransport try { var eventArgs = new CurrentRouteChangedEventArgs(previousRoute, newRoute, _managerId); - + if (_uiStateManager != null) { - _uiStateManager.QueueUIUpdate(() => + _uiStateManager.QueueUIUpdate(() => { CurrentRouteChanged?.Invoke(this, eventArgs); // 向后兼容的事件 @@ -496,10 +496,10 @@ namespace NavisworksTransport try { var eventArgs = new PathPointsListUpdatedEventArgs(route, updateReason, _managerId); - + if (_uiStateManager != null) { - _uiStateManager.QueueUIUpdate(() => + _uiStateManager.QueueUIUpdate(() => { PathPointsListUpdated?.Invoke(this, eventArgs); // 向后兼容的事件 @@ -529,10 +529,10 @@ namespace NavisworksTransport { PathEditState = PathEditState.Viewing; EditingRoute = null; - + // 智能管理ToolPlugin状态 ManageToolPluginForEditState(); - + RaiseStatusChanged("已切换到查看状态", PathPlanningStatusType.Info); } @@ -615,14 +615,14 @@ namespace NavisworksTransport LogManager.Info($"已从3D中移除路径点标记: {point.Name}"); } } - + // 重新渲染当前路径以反映更改 if (_renderPlugin != null && CurrentRoute != null) { _renderPlugin.RenderPath(CurrentRoute); LogManager.Info($"已更新路径可视化: {point.Name}"); } - + // 触发路径点移除事件 RaisePathPointOperation(PathPointOperationType.Removed, point, CurrentRoute); } @@ -639,25 +639,25 @@ namespace NavisworksTransport /// /// 点击类型 public void StartClickTool(PathPointType pointType) -{ - try - { - CurrentPointType = pointType; - - // 检查是否在自动路径模式 - 如果是,则不订阅PathPlanningManager的事件 - bool shouldSubscribeToEvents = !IsInAutoPathMode; - LogManager.Info($"StartClickTool - 自动路径模式: {IsInAutoPathMode}, 订阅事件: {shouldSubscribeToEvents}"); - - ActivateToolPlugin(shouldSubscribeToEvents); - PathEditState = PathEditState.AddingPoints; - LogManager.Info($"点击工具已启动,类型: {pointType},事件订阅: {shouldSubscribeToEvents}"); - } - catch (Exception ex) - { - LogManager.Error("启动点击工具失败", ex); - RaiseErrorOccurred(ex.Message, ex); - } -} + { + try + { + CurrentPointType = pointType; + + // 检查是否在自动路径模式 - 如果是,则不订阅PathPlanningManager的事件 + bool shouldSubscribeToEvents = !IsInAutoPathMode; + LogManager.Info($"StartClickTool - 自动路径模式: {IsInAutoPathMode}, 订阅事件: {shouldSubscribeToEvents}"); + + ActivateToolPlugin(shouldSubscribeToEvents); + PathEditState = PathEditState.AddingPoints; + LogManager.Info($"点击工具已启动,类型: {pointType},事件订阅: {shouldSubscribeToEvents}"); + } + catch (Exception ex) + { + LogManager.Error("启动点击工具失败", ex); + RaiseErrorOccurred(ex.Message, ex); + } + } /// /// 停止点击工具 @@ -667,10 +667,10 @@ namespace NavisworksTransport try { LogManager.WriteLog("[事件清理] ===== 开始执行StopClickTool - 完整事件订阅清理 ====="); - + // 1. 停用ToolPlugin并清理事件订阅 DeactivateToolPlugin(); - + // 2. 额外的事件订阅清理 - 确保所有可能的重复订阅都被清除 LogManager.WriteLog("[事件清理] 执行额外的事件订阅清理"); try @@ -679,7 +679,7 @@ namespace NavisworksTransport var mouseClickedEvent = typeof(PathClickToolPlugin).GetEvent("MouseClicked"); if (mouseClickedEvent != null) { - var eventField = typeof(PathClickToolPlugin).GetField("MouseClicked", + var eventField = typeof(PathClickToolPlugin).GetField("MouseClicked", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); if (eventField != null) { @@ -688,7 +688,7 @@ namespace NavisworksTransport { var invocationList = currentDelegate.GetInvocationList(); LogManager.WriteLog($"[事件清理] 发现 {invocationList.Length} 个事件订阅者"); - + foreach (var handler in invocationList) { if (handler.Target == this && handler.Method.Name == "OnToolPluginMouseClicked") @@ -705,7 +705,7 @@ namespace NavisworksTransport { LogManager.WriteLog($"[事件清理] 额外清理过程异常: {cleanupEx.Message}"); } - + // 注意:移除了PathEditState设置,StopClickTool只管理工具插件状态,不修改业务逻辑状态 LogManager.WriteLog("[事件清理] ===== StopClickTool执行完成,所有事件订阅已清理 ====="); } @@ -725,7 +725,7 @@ namespace NavisworksTransport { LogManager.Info("禁用PathPlanningManager鼠标处理,进入自动路径规划模式"); IsInAutoPathMode = true; - + // 取消PathPlanningManager的鼠标事件订阅 PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked; LogManager.Info("已取消PathPlanningManager的鼠标事件订阅"); @@ -745,7 +745,7 @@ namespace NavisworksTransport { LogManager.Info("启用PathPlanningManager鼠标处理,退出自动路径规划模式"); IsInAutoPathMode = false; - + // 重新订阅PathPlanningManager的鼠标事件 PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked; // 先取消避免重复 PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked; @@ -780,7 +780,7 @@ namespace NavisworksTransport LogManager.Info($"起点坐标: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})"); LogManager.Info($"终点坐标: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})"); LogManager.Info($"车辆尺寸: {vehicleSize}m, 安全间隙: {safetyMargin}m, 车辆高度: {vehicleHeight}m"); - + RaiseStatusChanged("正在进行自动路径规划...", PathPlanningStatusType.Info); // 1. 获取模型边界 @@ -802,30 +802,30 @@ namespace NavisworksTransport RaiseStatusChanged("正在生成网格地图...", PathPlanningStatusType.Info); var gridMapGenerator = new GridMapGenerator(); GridMap gridMap = null; - + try { - LogManager.Info($"开始调用GridMapGenerator.GenerateFromBIM(新版本2.5D模式)"); + LogManager.Info($"开始调用GridMapGenerator.GenerateFromBIM(2.5D模式)"); var performanceStart = System.Diagnostics.Stopwatch.StartNew(); - + // 获取当前文档 var document = Application.ActiveDocument; LogManager.Info($"网格生成参数 - 边界: {bounds.Min.X:F2},{bounds.Min.Y:F2} -> {bounds.Max.X:F2},{bounds.Max.Y:F2}"); LogManager.Info($"网格生成参数 - 网格大小: {gridSize}m, 车辆大小: {vehicleSize}m, 安全边距: {safetyMargin}m"); LogManager.Info($"网格生成参数 - 起点: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})"); LogManager.Info($"网格生成参数 - 终点: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})"); - + try { - // 首先尝试使用新的2.5D通道检测模式 + // 使用2.5D通道检测模式 LogManager.Info("尝试使用ChannelBased2_5D模式生成网格"); gridMap = gridMapGenerator.GenerateFromBIM( - document, - bounds, - gridSize, - vehicleSize, - safetyMargin, - startPoint.Position, + document, + bounds, + gridSize, + vehicleSize, + safetyMargin, + startPoint.Position, endPoint.Position, GridGenerationMode.ChannelBased2_5D, // 新增:使用2.5D通道检测模式 vehicleHeight // 传递车辆高度参数 @@ -834,22 +834,9 @@ namespace NavisworksTransport } catch (Exception modeEx) { - LogManager.Warning($"ChannelBased2_5D模式失败,回退到默认模式: {modeEx.Message}"); - // 回退到传统的边界框模式 - gridMap = gridMapGenerator.GenerateFromBIM( - document, - bounds, - gridSize, - vehicleSize, - safetyMargin, - startPoint.Position, - endPoint.Position, - GridGenerationMode.BoundingBox, // 回退到传统模式 - vehicleHeight // 传递车辆高度参数(虽然BoundingBox模式不会使用) - ); - LogManager.Info("✅ 回退到BoundingBox模式网格生成成功"); + LogManager.Warning($"ChannelBased2_5D模式失败,{modeEx.Message}"); } - + performanceStart.Stop(); LogManager.Info($"GridMapGenerator.GenerateFromBIM 调用完成,耗时: {performanceStart.ElapsedMilliseconds}ms"); LogManager.Info($"生成的网格统计: {gridMap?.GetStatistics() ?? "NULL"}"); @@ -866,22 +853,49 @@ namespace NavisworksTransport LogManager.Info("开始获取通道数据用于精确高度计算..."); var channelItems = GetChannelItemsForHeightCalculation(); LogManager.Info($"获取通道数据完成: {channelItems?.Count() ?? 0} 个通道项"); - - // 4. 执行A*路径查找 + + // 4. 创建ChannelCoverage对象(关键修复) + ChannelCoverage channelCoverage = null; + if (channelItems != null && channelItems.Any()) + { + LogManager.Info($"创建ChannelCoverage对象,包含 {channelItems.Count()} 个通道项"); + channelCoverage = new ChannelCoverage + { + GridMap = gridMap, + ChannelItems = channelItems.ToList(), + TotalBounds = bounds + }; + LogManager.Info($"ChannelCoverage创建完成: {channelCoverage.GetStatistics()}"); + } + else + { + LogManager.Info("未找到通道数据,将使用传统边界框模式进行路径规划"); + } + + // 5. 执行A*路径查找 RaiseStatusChanged("正在计算最优路径...", PathPlanningStatusType.Info); LogManager.Info("=== 开始执行A*路径查找 ==="); - + AutoPathFinder pathFinder = null; List pathPoints = null; - + try { pathFinder = new AutoPathFinder(); LogManager.Info("AutoPathFinder实例创建成功"); - + LogManager.Info("开始调用FindPath方法..."); - // 向后兼容:传统模式下不传递通道覆盖数据 - pathPoints = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap); + // 🔥 关键修复:传递ChannelCoverage数据和车辆高度 + if (channelCoverage != null) + { + LogManager.Info($"使用2.5D模式进行路径查找,车辆高度: {vehicleHeight}m"); + pathPoints = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, vehicleHeight); + } + else + { + LogManager.Info("使用传统边界框模式进行路径查找"); + pathPoints = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap); + } LogManager.Info("FindPath方法调用完成"); } catch (Exception ex) @@ -897,30 +911,30 @@ namespace NavisworksTransport LogManager.Info($"A*算法找到路径,包含 {pathPoints.Count} 个点"); - // 5. 创建PathRoute对象 + // 6. 创建PathRoute对象 var routeName = $"自动路径_{DateTime.Now:HHmmss}"; var autoRoute = CreateAutoPathRoute(pathPoints, routeName); - // 6. 添加到路径集合 + // 7. 添加到路径集合 if (!_routes.Contains(autoRoute)) { _routes.Add(autoRoute); } CurrentRoute = autoRoute; - // 7. 自动绘制路径可视化 + // 8. 自动绘制路径可视化 DrawRouteVisualization(autoRoute, isAutoPath: true); - // 8. 强制设置路径编辑状态为查看状态(修复状态问题) + // 9. 强制设置路径编辑状态为查看状态(修复状态问题) // 在所有事件触发之前设置状态,确保不会被后续操作重置 LogManager.Info("自动路径规划完成,强制设置状态为Viewing"); PathEditState = PathEditState.Viewing; EditingRoute = null; // 确保没有编辑中的路径 - - // 9. 触发事件 + + // 10. 触发事件 RaiseRouteGenerated(autoRoute, RouteGenerationMethod.AutoPlanning, gridSize); - - // 10. 最终确保状态正确(防御性编程) + + // 11. 最终确保状态正确(防御性编程) // 使用延迟执行确保在所有事件处理完成后状态仍然正确 if (_uiStateManager != null) { @@ -935,8 +949,12 @@ namespace NavisworksTransport } }, UIUpdatePriority.Normal, forceSync: false); } - - RaiseStatusChanged($"自动路径规划完成: {routeName},使用网格大小 {gridSize:F2}米", PathPlanningStatusType.Success); + + var statusMessage = channelCoverage != null + ? $"自动路径规划完成(2.5D模式): {routeName},使用网格大小 {gridSize:F2}米" + : $"自动路径规划完成(边界框模式): {routeName},使用网格大小 {gridSize:F2}米"; + + RaiseStatusChanged(statusMessage, PathPlanningStatusType.Success); LogManager.Info($"自动路径规划成功完成: 路径长度 {autoRoute.TotalLength:F2}米,包含 {autoRoute.Points.Count} 个点,使用网格大小 {gridSize:F2}米"); return Task.FromResult(autoRoute); @@ -977,10 +995,10 @@ namespace NavisworksTransport PathEditState = PathEditState.Editing; EditingRoute = route ?? throw new ArgumentNullException(nameof(route)); CurrentRoute = route; - + // 智能管理ToolPlugin状态 ManageToolPluginForEditState(); - + RaiseStatusChanged($"已切换到编辑状态,正在编辑路径: {route.Name}", PathPlanningStatusType.Info); } @@ -1022,7 +1040,7 @@ namespace NavisworksTransport { // 计算组合边界 CalculateCombinedBounds(); - + // 触发通道选择变更事件 RaiseChannelSelectionChanged(_selectedChannels, useCurrentSelection ? "手动选择" : "类别筛选"); } @@ -1188,15 +1206,15 @@ namespace NavisworksTransport { // 设置为创建状态 PathEditState = PathEditState.Creating; - + // 创建新路径 var newRoute = new PathRoute(routeName ?? $"路径{DateTime.Now:yyyyMMdd_HHmmss}"); _editingRoute = newRoute; CurrentRoute = newRoute; - + // 自动选择所有可通行的物流模型 AutoSelectLogisticsChannels(); - + // 检查是否有可通行的物流模型 if (_selectedChannels == null || _selectedChannels.Count == 0) { @@ -1205,12 +1223,12 @@ namespace NavisworksTransport SwitchToViewingState(); return null; } - + // 智能管理ToolPlugin状态 ManageToolPluginForEditState(); - + RaiseStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中可通行的物流模型上点击设置路径点", PathPlanningStatusType.Info); - + return newRoute; } catch (Exception ex) @@ -1233,7 +1251,7 @@ namespace NavisworksTransport RaiseErrorOccurred("当前不在编辑状态,无法完成编辑"); return false; } - + try { // 自动设置最后一个点为终点 @@ -1252,44 +1270,44 @@ namespace NavisworksTransport LogManager.Info($"已自动设置最后一个点为终点: {lastPoint.Name}"); } } - + // 如果是创建模式,将当前路径添加到路径集合 if (_pathEditState == PathEditState.Creating && CurrentRoute != null) { if (!_routes.Contains(CurrentRoute)) { _routes.Add(CurrentRoute); - + // 添加历史记录 var historyEntry = new PathHistoryEntry( - CurrentRoute.Id, - PathHistoryOperationType.Created, - CurrentRoute, + CurrentRoute.Id, + PathHistoryOperationType.Created, + CurrentRoute, $"创建新路径: {CurrentRoute.Name}"); _historyManager.AddHistoryEntry(historyEntry); } } - + // 如果是编辑模式,保存编辑历史 if (_pathEditState == PathEditState.Editing && EditingRoute != null) { var historyEntry = new PathHistoryEntry( - EditingRoute.Id, - PathHistoryOperationType.Edited, - EditingRoute, + EditingRoute.Id, + PathHistoryOperationType.Edited, + EditingRoute, $"编辑路径: {EditingRoute.Name}"); _historyManager.AddHistoryEntry(historyEntry); } - + // 切换回查看状态 SwitchToViewingState(); - + // 触发路径点列表更新事件 RaisePathPointsListUpdated(CurrentRoute, "编辑完成"); - + // 触发路径生成事件 RaiseRouteGenerated(CurrentRoute, RouteGenerationMethod.Manual); - + RaiseStatusChanged("路径编辑已完成", PathPlanningStatusType.Success); return true; } @@ -1327,16 +1345,16 @@ namespace NavisworksTransport Position = originalPoint.Position, Type = originalPoint.Type }; - + // 切换到修改路径点状态 PathEditState = PathEditState.EditingPoint; - + // 激活ToolPlugin以接收3D点击 ActivateToolPlugin(); - + RaiseStatusChanged($"开始修改路径点: {_originalPoint.Name}", PathPlanningStatusType.Info); LogManager.Info($"开始修改路径点,索引: {pointIndex}, 名称: {_originalPoint.Name}"); - + return true; } catch (Exception ex) @@ -1369,7 +1387,7 @@ namespace NavisworksTransport // 更新预览可视化 UpdateEditingPreviewVisualization(); - + LogManager.Info($"设置修改预览点: ({position.X:F3}, {position.Y:F3}, {position.Z:F3})"); } catch (Exception ex) @@ -1386,8 +1404,8 @@ namespace NavisworksTransport { try { - if (PathEditState != PathEditState.EditingPoint || - _editingPointIndex == -1 || + if (PathEditState != PathEditState.EditingPoint || + _editingPointIndex == -1 || _editingPreviewPoint == null || CurrentRoute == null) { @@ -1398,7 +1416,7 @@ namespace NavisworksTransport // 更新路径点位置 var pointToUpdate = CurrentRoute.Points[_editingPointIndex]; pointToUpdate.Position = _editingPreviewPoint.Position; - + // 添加历史记录 var historyEntry = new PathHistoryEntry( CurrentRoute.Id, @@ -1415,7 +1433,7 @@ namespace NavisworksTransport // 切换回查看状态 PathEditState = PathEditState.Viewing; - + // 停用ToolPlugin DeactivateToolPlugin(); @@ -1424,7 +1442,7 @@ namespace NavisworksTransport RaiseStatusChanged($"路径点 {pointToUpdate.Name} 修改完成", PathPlanningStatusType.Success); LogManager.Info($"路径点修改完成: {pointToUpdate.Name}"); - + return true; } catch (Exception ex) @@ -1452,7 +1470,7 @@ namespace NavisworksTransport // 切换回查看状态 PathEditState = PathEditState.Viewing; - + // 停用ToolPlugin DeactivateToolPlugin(); @@ -1461,7 +1479,7 @@ namespace NavisworksTransport RaiseStatusChanged("已取消路径点修改", PathPlanningStatusType.Info); LogManager.Info("已取消路径点修改"); - + return true; } catch (Exception ex) @@ -1520,7 +1538,7 @@ namespace NavisworksTransport // 清理预览渲染,恢复原始路径渲染 _renderPlugin.ClearPreview(); - + if (CurrentRoute != null) { _renderPlugin.RenderPath(CurrentRoute); @@ -1545,7 +1563,7 @@ namespace NavisworksTransport RaiseErrorOccurred("当前不在编辑状态,无法取消编辑"); return false; } - + try { // 如果是创建模式,清理当前路径 @@ -1554,10 +1572,10 @@ namespace NavisworksTransport CurrentRoute.Points.Clear(); LogManager.Info("已清理新建路径的临时数据"); } - + // 切换回查看状态 SwitchToViewingState(); - + RaiseStatusChanged("路径编辑已取消", PathPlanningStatusType.Info); return true; } @@ -1582,7 +1600,7 @@ namespace NavisworksTransport RaiseErrorOccurred("不在编辑状态,无法添加路径点"); return null; } - + // 如果没有当前路径,则无法添加 if (CurrentRoute == null) { @@ -1601,8 +1619,8 @@ namespace NavisworksTransport else { // 自动判断类型:第一个点为起点,其余为路径点 - finalPointType = (CurrentRoute.Points.Count == 0) - ? PathPointType.StartPoint + finalPointType = (CurrentRoute.Points.Count == 0) + ? PathPointType.StartPoint : PathPointType.WayPoint; } @@ -1627,10 +1645,10 @@ namespace NavisworksTransport { LogManager.Error($"绘制手工路径3D可视化失败: {renderEx.Message}"); } - + // 触发路径点添加事件 RaisePathPointOperation(PathPointOperationType.Added, pathPoint, CurrentRoute); - + // 触发路径列表更新事件 RaisePathPointsListUpdated(CurrentRoute, "添加路径点"); @@ -1657,7 +1675,7 @@ namespace NavisworksTransport RaiseErrorOccurred("不在编辑状态,无法设置预览点"); return null; } - + // 如果没有当前路径,则无法设置预览点 if (CurrentRoute == null) { @@ -1676,8 +1694,8 @@ namespace NavisworksTransport else { // 自动判断类型:第一个点为起点,其余为路径点 - finalPointType = (CurrentRoute.Points.Count == 0) - ? PathPointType.StartPoint + finalPointType = (CurrentRoute.Points.Count == 0) + ? PathPointType.StartPoint : PathPointType.WayPoint; } @@ -1710,7 +1728,7 @@ namespace NavisworksTransport { DrawPreviewPointVisualization(_previewPoint); LogManager.Info($"预览点3D可视化已更新: {_previewPoint.Name}"); - + // 如果路径中有足够的点,绘制预览连线 if (CurrentRoute.Points.Count >= 2) { @@ -1787,10 +1805,10 @@ namespace NavisworksTransport { LogManager.Error($"绘制手工路径3D可视化失败: {renderEx.Message}"); } - + // 触发路径点添加事件 RaisePathPointOperation(PathPointOperationType.Added, confirmPoint, CurrentRoute); - + // 触发路径列表更新事件 RaisePathPointsListUpdated(CurrentRoute, "确认添加预览点"); @@ -1811,7 +1829,7 @@ namespace NavisworksTransport if (_isPreviewMode && _previewPoint != null) { LogManager.Info($"清除预览点: {_previewPoint.Name}"); - + // 清除预览点可视化 try { @@ -1838,7 +1856,7 @@ namespace NavisworksTransport { // 先清除之前的预览点 _renderPlugin.ClearPreviewPoint(); - + // 绘制新的灰色预览点 _renderPlugin.RenderPreviewPoint(previewPoint); LogManager.Info($"预览点可视化已绘制: {previewPoint.Name}"); @@ -1880,7 +1898,7 @@ namespace NavisworksTransport { var stats = $"通道数量: {_selectedChannels.Count}\n"; stats += $"路径数量: {_routes.Count}\n"; - + if (_currentRoute != null) { stats += $"当前路径: {_currentRoute.Name}\n"; @@ -2065,9 +2083,9 @@ namespace NavisworksTransport { var conversionFactor = GetUnitsToMetersConversionFactor(); var units = Application.ActiveDocument.Units; - + var allBounds = new List(); - + foreach (var channel in _selectedChannels) { try @@ -2100,7 +2118,7 @@ namespace NavisworksTransport ); _combinedChannelBounds = new ChannelBounds(combinedBoundingBox); - + RaiseStatusChanged($"已计算通道边界: {_combinedChannelBounds.MinPoint.X:F2},{_combinedChannelBounds.MinPoint.Y:F2} - {_combinedChannelBounds.MaxPoint.X:F2},{_combinedChannelBounds.MaxPoint.Y:F2}", PathPlanningStatusType.Info); } } @@ -2152,36 +2170,36 @@ namespace NavisworksTransport { var document = Application.ActiveDocument; if (document?.Models == null) return; - + _selectedChannels.Clear(); - + LogManager.Info("[通道自动选择] 开始搜索可通行的物流模型"); - + // 使用Search API代替手动遍历,提高效率 var allLogisticsItems = SearchLogisticsItemsUsingSearchAPI(); LogManager.Info($"[通道自动选择] Search API找到 {allLogisticsItems.Count} 个具有物流属性的模型项"); - + // 筛选出可通行的物流模型 var modelCollection = new ModelItemCollection(); modelCollection.AddRange(allLogisticsItems); var traversableItems = CategoryAttributeManager.FilterTraversableItems(modelCollection); LogManager.Info($"[通道自动选择] 筛选出 {traversableItems.Count} 个可通行的物流模型"); - + // 将可通行的物流模型添加到_selectedChannels foreach (ModelItem item in traversableItems) { _selectedChannels.Add(item); LogManager.Info($"[通道自动选择] 添加可通行模型: '{item.DisplayName}'"); } - + if (_selectedChannels.Count > 0) { // 计算组合边界 CalculateCombinedBounds(); - + // 触发通道选择变更事件 RaiseChannelSelectionChanged(_selectedChannels, "自动选择"); - + LogManager.Info($"[通道自动选择] ✅ 自动选择了 {_selectedChannels.Count} 个可通行的物流模型"); } else @@ -2232,86 +2250,86 @@ namespace NavisworksTransport /// /// 是否订阅鼠标事件 private bool ActivateToolPlugin(bool subscribeToEvents = true) -{ - // 如果已经激活,检查是否需要订阅事件 - if (_isToolPluginActive) - { - LogManager.WriteLog("[ToolPlugin] ToolPlugin已激活,检查事件订阅状态"); - if (subscribeToEvents) { - // 确保事件订阅(避免重复订阅) - PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked; - PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked; - LogManager.WriteLog("[ToolPlugin] 已确保PathPlanningManager事件订阅"); + // 如果已经激活,检查是否需要订阅事件 + if (_isToolPluginActive) + { + LogManager.WriteLog("[ToolPlugin] ToolPlugin已激活,检查事件订阅状态"); + if (subscribeToEvents) + { + // 确保事件订阅(避免重复订阅) + PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked; + PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked; + LogManager.WriteLog("[ToolPlugin] 已确保PathPlanningManager事件订阅"); + } + return true; + } + + try + { + LogManager.WriteLog("[ToolPlugin] ===== 开始激活ToolPlugin ====="); + LogManager.WriteLog($"[ToolPlugin] 订阅事件: {subscribeToEvents}"); + LogManager.WriteLog($"[ToolPlugin] 当前应用程序状态: {Application.IsAutomated}"); + LogManager.WriteLog($"[ToolPlugin] 当前文档状态: {Application.ActiveDocument?.Title ?? "NULL"}"); + + // 1. 加载插件程序集 + LogManager.WriteLog("[ToolPlugin] 步骤1: 加载插件程序集"); + var assemblyPath = PathClickToolPlugin.AssemblyPath; + LogManager.WriteLog($"[ToolPlugin] 程序集路径: {assemblyPath}"); + LogManager.WriteLog($"[ToolPlugin] 程序集文件是否存在: {System.IO.File.Exists(assemblyPath)}"); + + Application.Plugins.AddPluginAssembly(assemblyPath); + LogManager.WriteLog("[ToolPlugin] ✓ 程序集加载完成"); + + // 2. 查找插件 + LogManager.WriteLog("[ToolPlugin] 步骤2: 查找插件"); + ToolPluginRecord toolPluginRecord = (ToolPluginRecord)Application.Plugins.FindPlugin("PathClickTool.NavisworksTransport"); + + if (toolPluginRecord == null) + { + LogManager.WriteLog("[ToolPlugin] ✗ 错误: 无法找到PathClickTool插件"); + return false; + } + LogManager.WriteLog("[ToolPlugin] ✓ 插件查找成功"); + + // 3. 加载插件 + LogManager.WriteLog("[ToolPlugin] 步骤3: 加载插件"); + var loadedPlugin = toolPluginRecord.LoadPlugin(); + if (loadedPlugin == null) + { + LogManager.WriteLog("[ToolPlugin] ✗ 错误: 插件加载失败"); + return false; + } + LogManager.WriteLog("[ToolPlugin] ✓ 插件加载成功"); + + // 4. 设置为活动工具 + LogManager.WriteLog("[ToolPlugin] 步骤4: 设置为活动工具"); + Application.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin); + LogManager.WriteLog("[ToolPlugin] ✓ 工具设置成功"); + + // 5. 根据参数决定是否订阅点击事件 + if (subscribeToEvents) + { + LogManager.WriteLog("[ToolPlugin] 步骤5: 订阅点击事件"); + PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked; + LogManager.WriteLog("[ToolPlugin] ✓ PathPlanningManager事件订阅成功"); + } + else + { + LogManager.WriteLog("[ToolPlugin] 步骤5: 跳过事件订阅(由外部组件管理)"); + } + + _isToolPluginActive = true; + LogManager.WriteLog("[ToolPlugin] ===== ToolPlugin激活完成 ====="); + return true; + } + catch (Exception ex) + { + LogManager.WriteLog($"[ToolPlugin] 激活异常: {ex.Message}"); + LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}"); + return false; + } } - return true; - } - - try - { - LogManager.WriteLog("[ToolPlugin] ===== 开始激活ToolPlugin ====="); - LogManager.WriteLog($"[ToolPlugin] 订阅事件: {subscribeToEvents}"); - LogManager.WriteLog($"[ToolPlugin] 当前应用程序状态: {Application.IsAutomated}"); - LogManager.WriteLog($"[ToolPlugin] 当前文档状态: {Application.ActiveDocument?.Title ?? "NULL"}"); - - // 1. 加载插件程序集 - LogManager.WriteLog("[ToolPlugin] 步骤1: 加载插件程序集"); - var assemblyPath = PathClickToolPlugin.AssemblyPath; - LogManager.WriteLog($"[ToolPlugin] 程序集路径: {assemblyPath}"); - LogManager.WriteLog($"[ToolPlugin] 程序集文件是否存在: {System.IO.File.Exists(assemblyPath)}"); - - Application.Plugins.AddPluginAssembly(assemblyPath); - LogManager.WriteLog("[ToolPlugin] ✓ 程序集加载完成"); - - // 2. 查找插件 - LogManager.WriteLog("[ToolPlugin] 步骤2: 查找插件"); - ToolPluginRecord toolPluginRecord = (ToolPluginRecord)Application.Plugins.FindPlugin("PathClickTool.NavisworksTransport"); - - if (toolPluginRecord == null) - { - LogManager.WriteLog("[ToolPlugin] ✗ 错误: 无法找到PathClickTool插件"); - return false; - } - LogManager.WriteLog("[ToolPlugin] ✓ 插件查找成功"); - - // 3. 加载插件 - LogManager.WriteLog("[ToolPlugin] 步骤3: 加载插件"); - var loadedPlugin = toolPluginRecord.LoadPlugin(); - if (loadedPlugin == null) - { - LogManager.WriteLog("[ToolPlugin] ✗ 错误: 插件加载失败"); - return false; - } - LogManager.WriteLog("[ToolPlugin] ✓ 插件加载成功"); - - // 4. 设置为活动工具 - LogManager.WriteLog("[ToolPlugin] 步骤4: 设置为活动工具"); - Application.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin); - LogManager.WriteLog("[ToolPlugin] ✓ 工具设置成功"); - - // 5. 根据参数决定是否订阅点击事件 - if (subscribeToEvents) - { - LogManager.WriteLog("[ToolPlugin] 步骤5: 订阅点击事件"); - PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked; - LogManager.WriteLog("[ToolPlugin] ✓ PathPlanningManager事件订阅成功"); - } - else - { - LogManager.WriteLog("[ToolPlugin] 步骤5: 跳过事件订阅(由外部组件管理)"); - } - - _isToolPluginActive = true; - LogManager.WriteLog("[ToolPlugin] ===== ToolPlugin激活完成 ====="); - return true; - } - catch (Exception ex) - { - LogManager.WriteLog($"[ToolPlugin] 激活异常: {ex.Message}"); - LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}"); - return false; - } -} /// /// 处理ToolPlugin的鼠标点击事件 @@ -2349,7 +2367,7 @@ namespace NavisworksTransport { // 检查当前选中的通道状态 LogManager.WriteLog($"[手动编辑] 当前_selectedChannels状态: {(_selectedChannels == null ? "NULL" : $"包含{_selectedChannels.Count}个项目")}"); - + // 如果没有选中的通道,尝试实时搜索 if (_selectedChannels == null || _selectedChannels.Count == 0) { @@ -2360,9 +2378,9 @@ namespace NavisworksTransport // 检查是否在可通行的物流模型内并处理点击 if (_selectedChannels != null && _selectedChannels.Any()) { - bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) || + bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) || IsItemChildOfSelectedChannels(pickResult.ModelItem); - + LogManager.WriteLog($"[手动编辑] 在可通行的物流模型内: {isInTraversableLogisticsModel}"); if (isInTraversableLogisticsModel) @@ -2373,7 +2391,7 @@ namespace NavisworksTransport // 添加路径点模式 - 使用预览点 LogManager.WriteLog("[手动编辑] 设置预览点位置"); var previewPoint = SetPreviewPoint(pickResult.Point); - + if (previewPoint != null) { LogManager.WriteLog($"[手动编辑] ✓ 预览点已设置: {previewPoint.Name}"); @@ -2395,7 +2413,7 @@ namespace NavisworksTransport // 其他编辑模式 - 保持原有逻辑 LogManager.WriteLog("[手动编辑] 调用AddPathPointIn3D添加路径点"); var pathPoint = AddPathPointIn3D(pickResult.Point); - + if (pathPoint != null) { LogManager.WriteLog($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}"); @@ -2432,7 +2450,7 @@ namespace NavisworksTransport // 使用Search API实时搜索 var logisticsItems = SearchLogisticsItemsUsingSearchAPI(); LogManager.WriteLog($"[搜索通道] 实时搜索找到 {logisticsItems.Count} 个物流模型"); - + if (logisticsItems.Count > 0) { // 筛选出可通行的物流模型 @@ -2440,11 +2458,11 @@ namespace NavisworksTransport modelCollection.AddRange(logisticsItems); var traversableItems = CategoryAttributeManager.FilterTraversableItems(modelCollection); LogManager.WriteLog($"[搜索通道] 筛选出 {traversableItems.Count} 个可通行的物流模型"); - + // 临时设置为选中通道 if (_selectedChannels == null) _selectedChannels = new List(); _selectedChannels.Clear(); - + foreach (ModelItem item in traversableItems) { _selectedChannels.Add(item); @@ -2466,7 +2484,7 @@ namespace NavisworksTransport private List SearchLogisticsItemsUsingSearchAPI() { var logisticsItems = new List(); - + try { var document = Application.ActiveDocument; @@ -2477,28 +2495,28 @@ namespace NavisworksTransport } LogManager.Info("[Search API] 开始使用Search API搜索具有物流属性的模型项"); - + // 创建搜索对象 using (var search = new Search()) { // 选择所有项目作为搜索范围 search.Selection.SelectAll(); - + // 创建搜索条件:查找具有物流属性的项 var searchConditions = search.SearchConditions; searchConditions.Clear(); - + // 搜索条件:有物流类别的项目 var hasLogisticsCategoryCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS); searchConditions.Add(hasLogisticsCategoryCondition); - + // 执行搜索 var stopwatch = System.Diagnostics.Stopwatch.StartNew(); ModelItemCollection searchResults = search.FindAll(document, false); stopwatch.Stop(); - + LogManager.Info($"[Search API] 搜索完成,找到 {searchResults?.Count ?? 0} 个具有物流属性的项,耗时: {stopwatch.ElapsedMilliseconds}ms"); - + if (searchResults != null) { foreach (ModelItem item in searchResults) @@ -2507,7 +2525,7 @@ namespace NavisworksTransport } } } - + return logisticsItems; } catch (Exception ex) @@ -2526,7 +2544,7 @@ namespace NavisworksTransport try { var units = Application.ActiveDocument.Units; - + switch (units) { case Units.Millimeters: @@ -2594,7 +2612,7 @@ namespace NavisworksTransport _renderPlugin.ClearAllPaths(); _renderPlugin = null; } - + // 停用ToolPlugin if (_isToolPluginActive) { @@ -2616,16 +2634,16 @@ namespace NavisworksTransport LogManager.WriteLog("[ToolPlugin] ToolPlugin未激活,无需停用"); return true; } - + try { LogManager.WriteLog("[ToolPlugin] ===== 开始停用ToolPlugin ====="); - + // 1. 取消事件订阅 LogManager.WriteLog("[ToolPlugin] 步骤1: 取消事件订阅"); PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked; LogManager.WriteLog("[ToolPlugin] ✓ 事件订阅已取消"); - + // 2. 重置为无活动工具状态 LogManager.WriteLog("[ToolPlugin] 步骤2: 重置为默认工具"); try @@ -2638,7 +2656,7 @@ namespace NavisworksTransport { LogManager.WriteLog($"[ToolPlugin] 重置工具状态失败: {ex.Message}"); } - + _isToolPluginActive = false; LogManager.WriteLog("[ToolPlugin] ===== ToolPlugin停用完成 ====="); return true; @@ -2725,7 +2743,7 @@ namespace NavisworksTransport private BoundingBox3D CalculateChannelsBounds(List channels) { var allBounds = new List(); - + foreach (var channel in channels) { var bounds = channel.BoundingBox(); @@ -2734,16 +2752,16 @@ namespace NavisworksTransport allBounds.Add(bounds); } } - + if (!allBounds.Any()) return null; - + var minX = allBounds.Min(b => b.Min.X); var minY = allBounds.Min(b => b.Min.Y); var minZ = allBounds.Min(b => b.Min.Z); var maxX = allBounds.Max(b => b.Max.X); var maxY = allBounds.Max(b => b.Max.Y); var maxZ = allBounds.Max(b => b.Max.Z); - + return new BoundingBox3D( new Point3D(minX, minY, minZ), new Point3D(maxX, maxY, maxZ) @@ -2760,7 +2778,7 @@ namespace NavisworksTransport var width = bounds.Max.X - bounds.Min.X; var height = bounds.Max.Y - bounds.Min.Y; var maxDimension = Math.Max(width, height); - + // 基于模型大小智能选择网格大小 if (maxDimension > 1000) return 5.0; // 大型模型:5米 if (maxDimension > 500) return 2.0; // 中型模型:2米 @@ -2779,7 +2797,7 @@ namespace NavisworksTransport private IEnumerable GetChannelItemsForHeightCalculation() { var channelItems = new List(); - + try { // 如果用户已选择通道,优先使用用户选择的通道 @@ -2813,7 +2831,7 @@ namespace NavisworksTransport try { var route = new PathRoute(routeName); - + for (int i = 0; i < pathPoints.Count; i++) { var point = pathPoints[i]; @@ -2825,10 +2843,10 @@ namespace NavisworksTransport }; route.Points.Add(pathPoint); } - + // 计算路径长度 route.RecalculateLength(); - + LogManager.Info($"创建自动路径: {routeName}, 点数: {pathPoints.Count}, 长度: {route.TotalLength:F2}米"); return route; } @@ -2878,7 +2896,7 @@ namespace NavisworksTransport // 使用新的统一路径渲染API renderPlugin.RenderPath(route); - + LogManager.Info($"已渲染路径: {route.Name},包含 {route.Points.Count} 个路径点"); } catch (Exception ex) @@ -2975,7 +2993,7 @@ namespace NavisworksTransport // 对路径点进行排序(按添加顺序,即列表中的索引) var sortedPoints = pathPoints.ToList(); - + double minDistance = double.MaxValue; (PathPoint prevPoint, PathPoint nextPoint, int insertIndex)? nearestSegment = null; @@ -2984,10 +3002,10 @@ namespace NavisworksTransport { var currentPoint = sortedPoints[i]; var nextPoint = sortedPoints[i + 1]; - + // 计算预览点到线段的距离 var distance = CalculatePointToLineSegmentDistance(previewPosition, currentPoint.Position, nextPoint.Position); - + if (distance < minDistance) { minDistance = distance; @@ -3010,32 +3028,32 @@ namespace NavisworksTransport { // 线段向量 var lineVector = new Point3D(lineEnd.X - lineStart.X, lineEnd.Y - lineStart.Y, lineEnd.Z - lineStart.Z); - + // 点到线段起点的向量 var pointVector = new Point3D(point.X - lineStart.X, point.Y - lineStart.Y, point.Z - lineStart.Z); - + // 计算线段长度的平方 var lineLengthSquared = lineVector.X * lineVector.X + lineVector.Y * lineVector.Y + lineVector.Z * lineVector.Z; - + if (lineLengthSquared == 0) { // 线段退化为点,返回点到点的距离 return CalculateDistance(point, lineStart); } - + // 计算投影参数t var t = (pointVector.X * lineVector.X + pointVector.Y * lineVector.Y + pointVector.Z * lineVector.Z) / lineLengthSquared; - + // 将t限制在[0,1]范围内 t = Math.Max(0, Math.Min(1, t)); - + // 计算线段上最近点 var closestPoint = new Point3D( lineStart.X + t * lineVector.X, lineStart.Y + t * lineVector.Y, lineStart.Z + t * lineVector.Z ); - + // 返回点到最近点的距离 return CalculateDistance(point, closestPoint); } diff --git a/src/PathPlanning/AutoPathFinder.cs b/src/PathPlanning/AutoPathFinder.cs index 635ef83..2ad726d 100644 --- a/src/PathPlanning/AutoPathFinder.cs +++ b/src/PathPlanning/AutoPathFinder.cs @@ -37,7 +37,7 @@ namespace NavisworksTransport.PathPlanning /// 通道覆盖数据(可选,用于2.5D路径规划) /// 车辆高度(用于高度约束检查) /// 路径点列表(世界坐标) - public List FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage = null, double vehicleHeight = 3.0) + public List FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight) { LogManager.Info($"开始A*路径查找: 起点({start?.X:F2}, {start?.Y:F2}, {start?.Z:F2}), 终点({end?.X:F2}, {end?.Y:F2}, {end?.Z:F2})"); @@ -48,9 +48,7 @@ namespace NavisworksTransport.PathPlanning return FindPath2_5D(start, end, gridMap, channelCoverage, vehicleHeight); } - // 传统模式路径规划 - LogManager.Info("[传统路径规划] 使用边界框模式"); - return FindPathCore(start, end, gridMap); + return null; } /// @@ -68,6 +66,10 @@ namespace NavisworksTransport.PathPlanning { LogManager.Info("[2.5D路径规划] 开始执行"); + // 保存原始起点和终点,用于最终路径修正 + var originalStart = start; + var originalEnd = end; + // 1. 检查起点和终点的高度约束 if (!IsPointPassableAtHeight(start, gridMap, vehicleHeight)) { @@ -83,142 +85,39 @@ namespace NavisworksTransport.PathPlanning var astarGrid = ConvertToAStarGridWith2_5D(gridMap, channelCoverage, vehicleHeight); // 3. 执行A*算法 - var path = ExecuteAStarAlgorithm(start, end, gridMap, astarGrid); + var rawPath = ExecuteAStarAlgorithm(start, end, gridMap, astarGrid); - // 4. 对路径进行高度插值,利用2.5D高度区间信息 - if (path != null && path.Any()) + if (rawPath == null || !rawPath.Any()) { - var enhancedPath = InterpolatePathHeights2_5D(path, gridMap, vehicleHeight); - LogManager.Info($"[2.5D路径规划] 路径生成完成,包含 {enhancedPath.Count} 个路径点"); - return enhancedPath; + LogManager.Warning("[2.5D路径规划] 未找到有效路径"); + return new List(); } - LogManager.Warning("[2.5D路径规划] 未找到有效路径"); - return new List(); + LogManager.Info($"[2.5D路径规划] A*算法找到原始路径,包含 {rawPath.Count} 个点"); + + // 4. 路径优化(添加缺失的步骤) + var optimizedPath = OptimizePath(rawPath, gridMap); + LogManager.Info($"[2.5D路径规划] 路径优化完成,从 {rawPath.Count} 个点优化到 {optimizedPath.Count} 个点"); + + // 5. 对路径进行高度插值,利用2.5D高度区间信息 + var enhancedPath = InterpolatePathHeights2_5D(optimizedPath, gridMap, vehicleHeight); + + // 6. 精确高度修正 + var heightCorrectedPath = ApplyPreciseHeightCorrection(enhancedPath, gridMap); + + // 7. 替换起点和终点为原始用户指定的坐标(与FindPathCore保持一致) + var finalPath = CorrectStartEndPoints(heightCorrectedPath, originalStart, originalEnd); + + LogManager.Info($"[2.5D路径规划] 路径生成完成,最终包含 {finalPath.Count} 个路径点"); + LogManager.Info($"[2.5D路径规划] 起点坐标修正: 网格转换({heightCorrectedPath[0].X:F2}, {heightCorrectedPath[0].Y:F2}) -> 原始坐标({originalStart.X:F2}, {originalStart.Y:F2})"); + LogManager.Info($"[2.5D路径规划] 终点坐标修正: 网格转换({heightCorrectedPath[heightCorrectedPath.Count-1].X:F2}, {heightCorrectedPath[heightCorrectedPath.Count-1].Y:F2}) -> 原始坐标({originalEnd.X:F2}, {originalEnd.Y:F2})"); + + return finalPath; } catch (Exception ex) { LogManager.Error($"[2.5D路径规划] 路径查找失败: {ex.Message}"); - LogManager.Info("[2.5D路径规划] 回退到传统模式"); - return FindPathCore(start, end, gridMap); - } - } - - /// - /// 查找路径(传统方法,保持向后兼容) - /// - /// 起点(世界坐标) - /// 终点(世界坐标) - /// 网格地图 - /// 路径点列表(世界坐标) - private List FindPathCore(Point3D start, Point3D end, GridMap gridMap) - { - try - { - // 验证输入参数 - ValidateInputs(start, end, gridMap); - - // 保存原始起点和终点,用于最终路径修正 - var originalStart = start; - var originalEnd = end; - - // 设置路径规划的起点和终点,用于Z坐标插值 - gridMap.PlanningStartPoint = start; - gridMap.PlanningEndPoint = end; - gridMap.HasPlanningPoints = true; - - // 转换起点和终点为网格坐标 - var startGrid = gridMap.WorldToGrid(start); - var endGrid = gridMap.WorldToGrid(end); - - // 验证起点和终点是否在有效范围内 - if (!gridMap.IsValidGridPosition(startGrid)) - { - throw new AutoPathPlanningException($"起点({start.X:F2}, {start.Y:F2})超出网格范围"); - } - - if (!gridMap.IsValidGridPosition(endGrid)) - { - throw new AutoPathPlanningException($"终点({end.X:F2}, {end.Y:F2})超出网格范围"); - } - - // 🔥 移除权宜性措施:直接验证起点和终点是否可通行 - if (!gridMap.IsWalkable(startGrid)) - { - throw new AutoPathPlanningException($"起点({start.X:F2}, {start.Y:F2})位于障碍物上,请检查起点位置或确认是否在通道内"); - } - - if (!gridMap.IsWalkable(endGrid)) - { - throw new AutoPathPlanningException($"终点({end.X:F2}, {end.Y:F2})位于障碍物上,请检查起点位置或确认是否在通道内"); - } - - // 转换为RoyT.AStar网格格式并执行A*算法 - LogManager.Info("执行A*算法查找路径..."); - var astarGrid = ConvertToAStarGrid(gridMap); - var startPos = new GridPosition(startGrid.X, startGrid.Y); - var endPos = new GridPosition(endGrid.X, endGrid.Y); - - var pathFinder = new PathFinder(); - var astarPath = pathFinder.FindPath(startPos, endPos, astarGrid); - - if (astarPath == null || astarPath.Edges.Count == 0) - { - throw new AutoPathPlanningException("未找到可行路径"); - } - - LogManager.Info($"A*算法找到路径,包含 {astarPath.Edges.Count + 1} 个网格点"); - - // 转换为世界坐标并优化 - var worldPath = ConvertPathToWorldCoordinates(astarPath, gridMap); - var optimizedPath = OptimizePath(worldPath, gridMap); - var heightCorrectedPath = ApplyPreciseHeightCorrection(optimizedPath, gridMap); - - // 🔥 关键修复:替换起点和终点为原始用户指定的坐标 - var correctedPath = CorrectStartEndPoints(heightCorrectedPath, originalStart, originalEnd); - - LogManager.Info($"路径查找完成,最终包含 {correctedPath.Count} 个点"); - LogManager.Info($"起点坐标修正: 网格转换({heightCorrectedPath[0].X:F2}, {heightCorrectedPath[0].Y:F2}) -> 原始坐标({originalStart.X:F2}, {originalStart.Y:F2})"); - LogManager.Info($"终点坐标修正: 网格转换({heightCorrectedPath[heightCorrectedPath.Count-1].X:F2}, {heightCorrectedPath[heightCorrectedPath.Count-1].Y:F2}) -> 原始坐标({originalEnd.X:F2}, {originalEnd.Y:F2})"); - - return correctedPath; - } - catch (AutoPathPlanningException) - { - throw; // 重新抛出已知异常 - } - catch (Exception ex) - { - LogManager.Error($"A*路径查找时发生错误: {ex.Message}"); - throw new AutoPathPlanningException($"路径查找失败: {ex.Message}", ex); - } - } - - /// - /// 验证输入参数 - /// - /// 起点 - /// 终点 - /// 网格地图 - private void ValidateInputs(Point3D start, Point3D end, GridMap gridMap) - { - if (start == null) - throw new ArgumentNullException(nameof(start), "起点不能为空"); - - if (end == null) - throw new ArgumentNullException(nameof(end), "终点不能为空"); - - if (gridMap == null) - throw new ArgumentNullException(nameof(gridMap), "网格地图不能为空"); - - // 检查起点和终点是否相同 - double distance = Math.Sqrt( - Math.Pow(end.X - start.X, 2) + - Math.Pow(end.Y - start.Y, 2)); - - if (distance < 0.1) // 10cm以内认为是同一点 - { - throw new AutoPathPlanningException("起点和终点距离过近"); + return null; } } @@ -295,60 +194,6 @@ namespace NavisworksTransport.PathPlanning } } - /// - /// 将GridMap转换为RoyT.AStar的Grid格式(传统模式) - /// - /// 网格地图 - /// A*算法网格 - private Grid ConvertToAStarGrid(GridMap gridMap) - { - try - { - LogManager.Info($"[A*转换] 开始转换网格格式: {gridMap.Width}x{gridMap.Height}"); - - // ⚠️ 关键修复:gridMap.CellSize 是模型单位,需要转换为米 - // RoyT.AStar 的 cellSize 必须使用米为单位,因为它的 Position 就是米坐标 - LogManager.Info($"[A*转换] 开始单位转换..."); - double cellSizeInMeters = UnitsConverter.ConvertToMeters(gridMap.CellSize); - LogManager.Info($"[A*转换] 单位转换完成: {gridMap.CellSize:F2}模型单位 -> {cellSizeInMeters:F2}米"); - - LogManager.Info($"[A*转换] 创建网格参数..."); - var gridSize = new GridSize(gridMap.Width, gridMap.Height); - var cellSize = new Size(Distance.FromMeters((float)cellSizeInMeters), Distance.FromMeters((float)cellSizeInMeters)); - var traversalVelocity = Velocity.FromKilometersPerHour(5); // 5 km/h 的遍历速度 - - LogManager.Info($"[A*转换] A*网格参数: 尺寸={gridMap.Width}x{gridMap.Height}, 模型单位={gridMap.CellSize:F2}, 米单位={cellSizeInMeters:F2}, 速度=5km/h"); - - LogManager.Info($"[A*转换] 调用Grid.CreateGridWithLateralConnections..."); - var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, traversalVelocity); - LogManager.Info($"[A*转换] A*网格创建完成"); - - int blockedCells = 0; - - // 标记不可通行的单元格 - for (int x = 0; x < gridMap.Width; x++) - { - for (int y = 0; y < gridMap.Height; y++) - { - if (!gridMap.Cells[x, y].IsWalkable) - { - var gridPos = new GridPosition(x, y); - grid.DisconnectNode(gridPos); - blockedCells++; - } - } - } - - LogManager.Info($"网格转换完成,阻塞单元格: {blockedCells}个"); - return grid; - } - catch (Exception ex) - { - LogManager.Error($"转换网格格式时发生错误: {ex.Message}"); - throw new AutoPathPlanningException($"网格转换失败: {ex.Message}", ex); - } - } - /// /// 将A*路径转换为世界坐标 /// @@ -524,138 +369,6 @@ namespace NavisworksTransport.PathPlanning return true; } - /// - /// 查找多个备选路径(支持精确高度计算) - /// - /// 起点 - /// 终点 - /// 网格地图 - /// 最大备选路径数 - /// 通道模型项集合(可选,用于精确高度计算) - /// 备选路径列表 - public List> FindAlternativePaths(Point3D start, Point3D end, GridMap gridMap, int maxAlternatives = 3, IEnumerable channelItems = null) - { - // 如果提供了通道数据,设置到网格地图中启用精确高度计算 - if (channelItems != null) - { - gridMap.SetChannelItems(channelItems); - LogManager.Info($"[备选路径] 已设置通道数据,启用精确高度计算: {channelItems.Count()} 个通道"); - } - - return FindAlternativePathsCore(start, end, gridMap, maxAlternatives); - } - - /// - /// 查找多个备选路径(核心实现) - /// - /// 起点 - /// 终点 - /// 网格地图 - /// 最大备选路径数 - /// 备选路径列表 - private List> FindAlternativePathsCore(Point3D start, Point3D end, GridMap gridMap, int maxAlternatives = 3) - { - var alternatives = new List>(); - - try - { - // 第一条路径:标准最短路径 - var primaryPath = FindPathCore(start, end, gridMap); - alternatives.Add(primaryPath); - - // 为了找到替代路径,我们可以临时阻塞主路径的部分节点 - // 这是一个简化的实现,实际应用中可能需要更复杂的算法 - LogManager.Info($"找到主路径,尝试寻找 {maxAlternatives - 1} 条备选路径"); - - for (int i = 1; i < maxAlternatives; i++) - { - try - { - var alternativePath = FindAlternativePathByBlocking(start, end, gridMap, primaryPath, i); - if (alternativePath != null && alternativePath.Count > 0) - { - alternatives.Add(alternativePath); - } - } - catch - { - break; // 无法找到更多备选路径 - } - } - - LogManager.Info($"共找到 {alternatives.Count} 条路径"); - } - catch (Exception ex) - { - LogManager.Error($"查找备选路径时发生错误: {ex.Message}"); - } - - return alternatives; - } - - /// - /// 通过阻塞主路径的部分节点来寻找备选路径 - /// - /// 起点 - /// 终点 - /// 网格地图 - /// 主路径 - /// 阻塞策略 - /// 备选路径 - private List FindAlternativePathByBlocking(Point3D start, Point3D end, - GridMap gridMap, List primaryPath, int blockingStrategy) - { - if (primaryPath.Count < 3) - return null; - - // 创建临时网格地图副本 - var tempGridMap = CloneGridMap(gridMap); - - // 根据策略阻塞主路径的部分节点 - int blockStart = primaryPath.Count / (blockingStrategy + 1); - int blockEnd = Math.Min(blockStart + primaryPath.Count / 4, primaryPath.Count - 1); - - for (int i = blockStart; i < blockEnd; i++) - { - var gridPos = tempGridMap.WorldToGrid(primaryPath[i]); - if (tempGridMap.IsValidGridPosition(gridPos)) - { - tempGridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Obstacle); - } - } - - // 在修改后的网格上寻找路径 - return FindPathCore(start, end, tempGridMap); - } - - /// - /// 克隆网格地图 - /// - /// 原始网格地图 - /// 克隆的网格地图 - private GridMap CloneGridMap(GridMap original) - { - var clone = new GridMap(original.Bounds, original.CellSize); - - // 复制网格单元格数据 - for (int x = 0; x < original.Width; x++) - { - for (int y = 0; y < original.Height; y++) - { - clone.Cells[x, y] = original.Cells[x, y]; - } - } - - // 复制高度计算相关设置 - clone.PlanningStartPoint = original.PlanningStartPoint; - clone.PlanningEndPoint = original.PlanningEndPoint; - clone.HasPlanningPoints = original.HasPlanningPoints; - clone.ChannelItems = original.ChannelItems; - clone.EnablePreciseHeightCalculation = original.EnablePreciseHeightCalculation; - - return clone; - } - /// /// 计算路径总长度 /// diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs index ef60e8c..abd51d1 100644 --- a/src/PathPlanning/GridMapGenerator.cs +++ b/src/PathPlanning/GridMapGenerator.cs @@ -11,12 +11,7 @@ namespace NavisworksTransport.PathPlanning /// 网格生成模式 /// public enum GridGenerationMode - { - /// - /// 边界框模式 - 传统的基于整个边界框的网格生成 - /// - BoundingBox, - + { /// /// 通道基础2.5D模式 - 使用通道优先策略和垂直扫描的高性能模式 /// @@ -49,16 +44,6 @@ namespace NavisworksTransport.PathPlanning LogManager.Info($"[网格生成器] 初始化完成,延迟创建垂直扫描器"); } - /// - /// 根据当前模型单位计算合适的空间哈希桶大小 - /// - /// 适合当前模型单位的空间哈希桶大小 - private double CalculateSpatialHashSize() - { - // 目标:10米在当前模型单位下的等价值 - return 10.0 * GetMetersToModelUnitsConversionFactor(); - } - /// /// 从BIM模型生成网格地图 /// @@ -69,7 +54,7 @@ namespace NavisworksTransport.PathPlanning /// 安全间隙(额外的膨胀间距,米) /// 规划起点(用于确定楼层) /// 规划终点(用于确定楼层) - /// 网格生成模式,默认为边界框模式以保持向后兼容 + /// 网格生成模式,默认为2.5D模式 /// 车辆高度(用于垂直通行检查) /// 生成的网格地图 public GridMap GenerateFromBIM(Document document, BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, GridGenerationMode mode, double vehicleHeight) @@ -84,9 +69,8 @@ namespace NavisworksTransport.PathPlanning case GridGenerationMode.ChannelBased2_5D: return GenerateChannelBased2_5D(document, bounds, cellSize, vehicleRadius, safetyMargin, planningStartPoint, planningEndPoint, vehicleHeight); - case GridGenerationMode.BoundingBox: default: - return GenerateBoundingBoxMode(document, bounds, cellSize, vehicleRadius, safetyMargin, planningStartPoint, planningEndPoint); + return GenerateChannelBased2_5D(document, bounds, cellSize, vehicleRadius, safetyMargin, planningStartPoint, planningEndPoint, vehicleHeight); } } catch (Exception ex) @@ -95,123 +79,9 @@ namespace NavisworksTransport.PathPlanning throw new AutoPathPlanningException($"网格地图生成失败: {ex.Message}", ex); } } - - /// - /// 传统边界框模式的网格生成(原GenerateFromBIM方法内容) - /// - private GridMap GenerateBoundingBoxMode(Document document, BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint) - { - try - { - // 获取单位转换系数,将米转换为模型单位 - double metersToModelUnits = GetMetersToModelUnitsConversionFactor(); - double cellSizeInModelUnits = cellSize * metersToModelUnits; - double vehicleRadiusInModelUnits = vehicleRadius * metersToModelUnits; - double safetyMarginInModelUnits = safetyMargin * metersToModelUnits; - double totalInflationRadius = vehicleRadiusInModelUnits + safetyMarginInModelUnits; - - LogManager.Info($"开始生成网格地图: 边界={bounds}"); - LogManager.Info($"单元格大小: {cellSize}m = {cellSizeInModelUnits:F2}模型单位"); - LogManager.Info($"车辆半径: {vehicleRadius}m = {vehicleRadiusInModelUnits:F2}模型单位"); - LogManager.Info($"安全间隙: {safetyMargin}m = {safetyMarginInModelUnits:F2}模型单位"); - LogManager.Info($"总膨胀半径: {totalInflationRadius:F2}模型单位"); - LogManager.Info($"模型单位: {Application.ActiveDocument.Units}, 转换系数: {metersToModelUnits}"); - - // 计算网格尺寸 - double boundsWidth = bounds.Max.X - bounds.Min.X; - double boundsHeight = bounds.Max.Y - bounds.Min.Y; - int gridWidth = (int)Math.Ceiling(boundsWidth / cellSizeInModelUnits); - int gridHeight = (int)Math.Ceiling(boundsHeight / cellSizeInModelUnits); - int totalCells = gridWidth * gridHeight; - - LogManager.Info($"预计网格尺寸: {gridWidth}x{gridHeight} = {totalCells}个单元格"); - - // 检查网格大小限制,防止内存溢出 - const int maxCells = 1000000; // 最大100万个单元格 - if (totalCells > maxCells) - { - // 自动调整单元格大小 - double scaleFactor = Math.Sqrt((double)totalCells / maxCells); - cellSizeInModelUnits *= scaleFactor; - LogManager.Warning($"网格过大,自动调整单元格大小: {cellSize}m -> {cellSizeInModelUnits / metersToModelUnits:F2}m"); - - // 重新计算网格尺寸 - gridWidth = (int)Math.Ceiling(boundsWidth / cellSizeInModelUnits); - gridHeight = (int)Math.Ceiling(boundsHeight / cellSizeInModelUnits); - totalCells = gridWidth * gridHeight; - LogManager.Info($"调整后网格尺寸: {gridWidth}x{gridHeight} = {totalCells}个单元格"); - } - - // 步骤1: 获取所有模型项(暂时注释楼层过滤功能) - LogManager.Info("[步骤1] 获取所有模型项(楼层过滤已暂时禁用)..."); - var allModelItems = new List(); - foreach (var model in document.Models) - { - if (model.RootItem != null) - { - CollectAllItemsRecursively(model.RootItem, allModelItems); - } - } - LogManager.Info($"[步骤1] 获取到 {allModelItems.Count} 个模型项"); - - // TODO: 楼层过滤功能已暂时注释,需要时可重新启用 - // var floorFilteredItems = GetFloorFilteredModelItems(document, planningStartPoint, planningEndPoint); - var modelItems = allModelItems; // 直接使用所有模型项,不进行楼层过滤 - - // 步骤2: 基于工作区域重新计算边界(使用所有模型项) - LogManager.Info("[步骤2] 重新计算路径规划工作区域..."); - var workingBounds = CalculateWorkingBounds(modelItems, bounds, planningStartPoint, planningEndPoint); - LogManager.Info($"[步骤2] 工作区域边界: {workingBounds}"); - - // 步骤3: 基于工作区域创建网格地图 - LogManager.Info("[步骤3] 创建网格地图..."); - var gridMap = new GridMap(workingBounds, cellSizeInModelUnits); - LogManager.Info($"[步骤3] 创建网格地图: {gridMap.Width}x{gridMap.Height} = {gridMap.Width * gridMap.Height}个单元格"); - - // 步骤4: 使用所有模型项进行网格化(楼层过滤已禁用) - LogManager.Info($"[步骤4] 使用 {modelItems.Count} 个模型项进行网格化(楼层过滤已禁用)"); - - // 识别障碍物 - LogManager.Info("开始识别障碍物..."); - List obstacles = null; - try - { - obstacles = GetObstacleItems(modelItems); - LogManager.Info($"识别出 {obstacles.Count} 个障碍物"); - } - catch (Exception ex) - { - LogManager.Error($"识别障碍物时发生严重错误: {ex.Message}"); - LogManager.Error($"异常类型: {ex.GetType().Name}"); - LogManager.Error($"堆栈跟踪: {ex.StackTrace}"); - throw new AutoPathPlanningException($"障碍物识别失败: {ex.Message}", ex); - } - - // 标记障碍物单元格 - MarkObstacleCells(gridMap, obstacles); - - // 应用车辆尺寸膨胀(使用模型单位,包含安全间隙) - if (totalInflationRadius > 0) - { - ApplyVehicleInflation(gridMap, totalInflationRadius); - LogManager.Info($"已应用车辆膨胀,车辆半径={vehicleRadius}m + 安全间隙={safetyMargin}m = 总膨胀半径{totalInflationRadius:F2}模型单位"); - } - - // 识别和标记特殊区域(门、通道等) - MarkSpecialAreas(gridMap, modelItems); - - LogManager.Info($"网格地图生成完成: {gridMap.GetStatistics()}"); - return gridMap; - } - catch (Exception ex) - { - LogManager.Error($"生成网格地图时发生错误: {ex.Message}"); - throw new AutoPathPlanningException($"网格地图生成失败: {ex.Message}", ex); - } - } /// - /// 通道基础2.5D模式的网格生成(高性能模式) + /// 通道基础2.5D模式的网格生成 /// 使用通道优先策略和垂直扫描处理器 /// private GridMap GenerateChannelBased2_5D(Document document, BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, double vehicleHeight) @@ -221,7 +91,7 @@ namespace NavisworksTransport.PathPlanning LogManager.Info("[通道2.5D模式] 开始生成网格地图"); // 重要:将米制网格大小转换为模型单位 - double metersToModelUnitsConversionFactor = GetMetersToModelUnitsConversionFactor(); + double metersToModelUnitsConversionFactor = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units); double cellSizeInModelUnits = cellSize * metersToModelUnitsConversionFactor; LogManager.Info($"[通道2.5D模式] 网格大小单位转换: {cellSize}米 → {cellSizeInModelUnits:F2}模型单位 (转换系数: {metersToModelUnitsConversionFactor:F2})"); @@ -229,12 +99,6 @@ namespace NavisworksTransport.PathPlanning LogManager.Info("[通道2.5D模式] 步骤1: 构建通道覆盖网格"); var channelCoverage = _channelBuilder.BuildChannelCoverage(cellSizeInModelUnits, document); - if (channelCoverage?.GridMap == null) - { - LogManager.Warning("[通道2.5D模式] 通道构建失败,回退到边界框模式"); - return GenerateBoundingBoxMode(document, bounds, cellSizeInModelUnits, vehicleRadius, safetyMargin, planningStartPoint, planningEndPoint); - } - LogManager.Info($"[通道2.5D模式] 通道覆盖构建完成: {channelCoverage.GetStatistics()}"); LogManager.Info($"[阶段1完成] 通道覆盖网格统计: {channelCoverage.GridMap.GetStatistics()}"); @@ -279,7 +143,7 @@ namespace NavisworksTransport.PathPlanning if (vehicleRadius > 0 || safetyMargin > 0) { LogManager.Info("[通道2.5D模式] 步骤6: 应用车辆膨胀"); - double metersToModelUnits = GetMetersToModelUnitsConversionFactor(); + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units); double totalInflationRadius = (vehicleRadius + safetyMargin) * metersToModelUnits; ApplyVehicleInflation(channelCoverage.GridMap, totalInflationRadius); LogManager.Info($"[通道2.5D模式] 车辆膨胀完成,膨胀半径: {totalInflationRadius:F2}模型单位"); @@ -300,11 +164,7 @@ namespace NavisworksTransport.PathPlanning catch (Exception ex) { LogManager.Error($"[通道2.5D模式] 生成失败: {ex.Message}"); - LogManager.Warning("[通道2.5D模式] 回退到边界框模式"); - // 注意:catch块中也需要使用转换后的单位,但这里需要重新计算因为cellSizeInModelUnits变量作用域问题 - double metersToModelUnitsConversionFactor = GetMetersToModelUnitsConversionFactor(); - double cellSizeInModelUnits = cellSize * metersToModelUnitsConversionFactor; - return GenerateBoundingBoxMode(document, bounds, cellSizeInModelUnits, vehicleRadius, safetyMargin, planningStartPoint, planningEndPoint); + return null; } } @@ -529,145 +389,6 @@ namespace NavisworksTransport.PathPlanning } } - /// - /// 进行楼层过滤,获取起终点所在楼层的所有模型项 - /// - /// Navisworks文档 - /// 规划起点 - /// 规划终点 - /// 楼层过滤后的模型项列表 - private List GetFloorFilteredModelItems(Document document, Point3D planningStartPoint, Point3D planningEndPoint) - { - try - { - LogManager.Info("[楼层过滤] 开始获取文档中的所有模型项..."); - - // 获取文档中的所有模型项 - var allModelItems = new List(); - foreach (var model in document.Models) - { - if (model.RootItem != null) - { - CollectAllItemsRecursively(model.RootItem, allModelItems); - } - } - LogManager.Info($"[楼层过滤] 文档中共找到 {allModelItems.Count} 个模型项"); - - // 如果没有规划点,返回所有项目 - if (planningStartPoint.Equals(default(Point3D)) || planningEndPoint.Equals(default(Point3D))) - { - LogManager.Info("[楼层过滤] 未提供规划起终点,跳过楼层过滤"); - return allModelItems; - } - - // 进行楼层感知过滤 - var floorFilteredItems = ApplyFloorAwareFilteringWithSearchAPI(allModelItems, planningStartPoint, planningEndPoint, document); - LogManager.Info($"[楼层过滤] 楼层过滤后剩余 {floorFilteredItems.Count} 个模型项"); - - return floorFilteredItems; - } - catch (Exception ex) - { - LogManager.Error($"[楼层过滤] 楼层过滤失败: {ex.Message}"); - return new List(); - } - } - - /// - /// 基于楼层过滤结果计算路径规划工作区域边界 - /// - /// 楼层过滤后的模型项 - /// 原始边界(通道边界或模型边界) - /// 规划起点 - /// 规划终点 - /// 工作区域边界 - private BoundingBox3D CalculateWorkingBounds(List floorFilteredItems, BoundingBox3D originalBounds, Point3D planningStartPoint, Point3D planningEndPoint) - { - try - { - if (floorFilteredItems.Count == 0) - { - LogManager.Warning("[工作区域] 没有楼层过滤项,使用原始边界"); - return originalBounds; - } - - // 计算楼层模型项的实际边界 - double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue; - double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue; - int validBoundsCount = 0; - - foreach (var item in floorFilteredItems) - { - try - { - var itemBounds = item.BoundingBox(); - if (itemBounds != null) - { - validBoundsCount++; - minX = Math.Min(minX, itemBounds.Min.X); - minY = Math.Min(minY, itemBounds.Min.Y); - minZ = Math.Min(minZ, itemBounds.Min.Z); - maxX = Math.Max(maxX, itemBounds.Max.X); - maxY = Math.Max(maxY, itemBounds.Max.Y); - maxZ = Math.Max(maxZ, itemBounds.Max.Z); - } - } - catch - { - // 忽略单个项目的边界计算错误 - } - } - - if (validBoundsCount == 0) - { - LogManager.Warning("[工作区域] 无有效边界的模型项,使用原始边界"); - return originalBounds; - } - - // 确保起终点都在工作区域内 - if (!planningStartPoint.Equals(default(Point3D))) - { - minX = Math.Min(minX, planningStartPoint.X); - minY = Math.Min(minY, planningStartPoint.Y); - minZ = Math.Min(minZ, planningStartPoint.Z); - maxX = Math.Max(maxX, planningStartPoint.X); - maxY = Math.Max(maxY, planningStartPoint.Y); - maxZ = Math.Max(maxZ, planningStartPoint.Z); - } - - if (!planningEndPoint.Equals(default(Point3D))) - { - minX = Math.Min(minX, planningEndPoint.X); - minY = Math.Min(minY, planningEndPoint.Y); - minZ = Math.Min(minZ, planningEndPoint.Z); - maxX = Math.Max(maxX, planningEndPoint.X); - maxY = Math.Max(maxY, planningEndPoint.Y); - maxZ = Math.Max(maxZ, planningEndPoint.Z); - } - - // 添加一些缓冲区(10%的扩展) - double bufferX = (maxX - minX) * 0.1; - double bufferY = (maxY - minY) * 0.1; - double bufferZ = (maxZ - minZ) * 0.1; - - var workingBounds = new BoundingBox3D( - new Point3D(minX - bufferX, minY - bufferY, minZ - bufferZ), - new Point3D(maxX + bufferX, maxY + bufferY, maxZ + bufferZ) - ); - - LogManager.Info($"[工作区域] 原始边界: {originalBounds}"); - LogManager.Info($"[工作区域] 楼层边界: Min({minX:F1}, {minY:F1}, {minZ:F1}), Max({maxX:F1}, {maxY:F1}, {maxZ:F1})"); - LogManager.Info($"[工作区域] 工作区域: {workingBounds}"); - - return workingBounds; - } - catch (Exception ex) - { - LogManager.Error($"[工作区域] 计算工作区域失败: {ex.Message},使用原始边界"); - return originalBounds; - } - } - /// /// 递归收集所有模型项 /// @@ -711,53 +432,6 @@ namespace NavisworksTransport.PathPlanning } } - - /// - /// 获取指定边界内的所有模型项(带楼层过滤) - /// - /// Navisworks文档 - /// 边界框 - /// 规划起点 - /// 规划终点 - /// 过滤后的模型项列表 - private List GetModelItemsInBoundsWithFloorFiltering(Document document, BoundingBox3D bounds, Point3D planningStartPoint, Point3D planningEndPoint) - { - var allModelItems = new List(); - - try - { - // 遍历所有模型的根项 - foreach (var model in document.Models) - { - if (model.RootItem != null) - { - // 递归查找边界内的模型项 - CollectItemsInBounds(model.RootItem, bounds, allModelItems); - } - } - - LogManager.Info($"边界内共找到 {allModelItems.Count} 个模型项,开始楼层过滤..."); - - // 如果没有规划点,则不进行楼层过滤 - if (planningStartPoint.Equals(default(Point3D)) || planningEndPoint.Equals(default(Point3D))) - { - LogManager.Info("[楼层过滤] 未提供规划起终点,跳过楼层过滤"); - return allModelItems; - } - - // 进行楼层感知过滤(使用Search API优化) - var filteredItems = ApplyFloorAwareFilteringWithSearchAPI(allModelItems, planningStartPoint, planningEndPoint, document); - LogManager.Info($"[楼层过滤] 过滤后剩余 {filteredItems.Count} 个模型项"); - - return filteredItems; - } - catch (Exception ex) - { - LogManager.Warning($"获取模型项时发生错误: {ex.Message}"); - return allModelItems; - } - } - /// /// 递归收集边界内的模型项 /// @@ -833,51 +507,6 @@ namespace NavisworksTransport.PathPlanning return obstacles; } - /// - /// 获取米转换为模型单位的转换系数 - /// - private double GetMetersToModelUnitsConversionFactor() - { - try - { - var units = Application.ActiveDocument.Units; - - switch (units) - { - case Units.Millimeters: - return 1000.0; // 1米 = 1000毫米 - case Units.Centimeters: - return 100.0; // 1米 = 100厘米 - case Units.Meters: - return 1.0; // 1米 = 1米 - case Units.Inches: - return 39.37; // 1米 = 39.37英寸 - case Units.Feet: - return 3.281; // 1米 = 3.281英尺 - case Units.Kilometers: - return 0.001; // 1米 = 0.001公里 - case Units.Micrometers: - return 1000000.0; // 1米 = 1000000微米 - case Units.Microinches: - return 39370078.74; // 1米 = 39370078.74微英寸 - case Units.Mils: - return 39370.08; // 1米 = 39370.08密尔 - case Units.Yards: - return 1.094; // 1米 = 1.094码 - case Units.Miles: - return 0.000621; // 1米 = 0.000621英里 - default: - LogManager.Warning($"未知单位类型: {units},使用默认米单位"); - return 1.0; - } - } - catch (Exception ex) - { - LogManager.Error($"获取单位转换系数失败: {ex.Message}"); - return 1.0; - } - } - /// /// 判断模型项是否为障碍物 /// 逻辑:只标记明确的结构障碍物(墙、柱等),默认空间为可通行 @@ -1005,126 +634,6 @@ namespace NavisworksTransport.PathPlanning return false; } - /// - /// 在网格地图中标记障碍物单元格 - /// - /// 网格地图 - /// 障碍物列表 - private void MarkObstacleCells(GridMap gridMap, List obstacles) - { - int totalMarkedCells = 0; - int newlyMarkedCells = 0; - int skippedLargeObstacles = 0; - - foreach (var obstacle in obstacles) - { - try - { - var boundingBox = obstacle.BoundingBox(); - if (boundingBox == null) continue; - - // 将障碍物包围盒投影到网格 - var minGrid = gridMap.WorldToGrid(boundingBox.Min); - var maxGrid = gridMap.WorldToGrid(boundingBox.Max); - - // 计算要标记的单元格数量 - int cellsToMark = (maxGrid.X - minGrid.X + 1) * (maxGrid.Y - minGrid.Y + 1); - - // 🔥 智能分层过滤策略:根据障碍物类型和重要性决定处理方式 - if (cellsToMark > 500) // 大型障碍物需要特殊处理 - { - // 检查是否为重要结构障碍物 - bool isStructuralObstacle = IsStructuralObstacle(obstacle); - - if (isStructuralObstacle && cellsToMark <= 3000) // 重要结构且不超过网格的1/3 - { - LogManager.Info($"[障碍物标记] 保留重要结构障碍物: {obstacle.DisplayName}, 标记{cellsToMark}个单元格"); - // 继续处理重要结构障碍物 - } - else if (cellsToMark > 3000) // 超大型障碍物使用边界标记 - { - LogManager.Info($"[障碍物标记] 大型障碍物使用边界标记: {obstacle.DisplayName}, 原需{cellsToMark}个单元格"); - MarkLargeObstacleBoundary(gridMap, minGrid, maxGrid, ref newlyMarkedCells, ref totalMarkedCells); - continue; - } - else // 非重要且较大的障碍物跳过 - { - LogManager.Warning($"[障碍物标记] 跳过非重要大型障碍物: {obstacle.DisplayName}, 需标记{cellsToMark}个单元格"); - skippedLargeObstacles++; - continue; - } - } - - // 标记相交的网格单元格为障碍物 - for (int x = Math.Max(0, minGrid.X); x <= Math.Min(gridMap.Width - 1, maxGrid.X); x++) - { - for (int y = Math.Max(0, minGrid.Y); y <= Math.Min(gridMap.Height - 1, maxGrid.Y); y++) - { - var gridPos = new Point2D(x, y); - - // 只有当前单元格还可通行时才标记为障碍物 - if (gridMap.IsWalkable(gridPos)) - { - gridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Obstacle); - newlyMarkedCells++; - } - totalMarkedCells++; - } - } - } - catch (Exception ex) - { - LogManager.Warning($"标记障碍物 {obstacle.DisplayName} 时发生错误: {ex.Message}"); - } - } - - LogManager.Info($"[障碍物标记] 处理完成: 新标记{newlyMarkedCells}个, 总计数{totalMarkedCells}个, 跳过大型{skippedLargeObstacles}个"); - } - - /// - /// 判断是否为重要的结构障碍物(墙体、柱子、梁等) - /// - private bool IsStructuralObstacle(ModelItem item) - { - try - { - var displayName = item.DisplayName?.ToLower() ?? ""; - var className = item.ClassName?.ToLower() ?? ""; - - // 重要结构元素关键词 - string[] structuralKeywords = { - "wall", "墙", "墙体", "隔墙", - "column", "柱", "柱子", "支柱", "pillar", - "beam", "梁", "横梁", "主梁", - "structure", "结构", "structural", - "partition", "隔断", - "foundation", "基础", "承重" - }; - - bool hasStructuralKeyword = structuralKeywords.Any(keyword => - displayName.Contains(keyword) || className.Contains(keyword)); - - if (hasStructuralKeyword) - { - return true; - } - - // 检查是否有明确的结构物流属性 - var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item); - if (logisticsType == CategoryAttributeManager.LogisticsElementType.障碍物) - { - return true; // 明确标记为障碍物的都视为重要 - } - - return false; - } - catch (Exception ex) - { - LogManager.Warning($"判断结构障碍物时发生错误: {ex.Message}"); - return false; - } - } - /// /// 标记大型障碍物的边界轮廓,而不是完全填充 /// @@ -1414,180 +923,6 @@ namespace NavisworksTransport.PathPlanning stopwatch.Stop(); LogManager.Info($"[高效膨胀] 车辆膨胀完成: 膨胀半径={inflationRadius}格, 新增障碍物={inflatedCells}个, 跳过={skippedCells}个, 总耗时={stopwatch.ElapsedMilliseconds}ms"); } - - /// - /// 标记特殊区域(门、通道等) - /// 注意:通道不应该覆盖障碍物,只用于高度计算和路径成本优化 - /// - /// 网格地图 - /// 模型项列表 - private void MarkSpecialAreas(GridMap gridMap, List modelItems) - { - LogManager.Info($"[特殊区域标记] 跳过通道区域标记 - 通道仅用于高度计算,不覆盖障碍物"); - LogManager.Info($"[特殊区域标记] 保持所有障碍物标记完整,确保A*算法能正确避障"); - - // 通道的作用: - // 1. 路径规划时提供精确的表面高度 - // 2. 在GridMap.GetChannelItems()中被AutoPathFinder使用 - // 3. 不需要在这里修改网格标记 - - // 如果将来需要门等特殊区域,可以在这里添加,但通道不需要 - } - - /// - /// 在指定包围盒内标记网格单元格 - /// - /// 网格地图 - /// 包围盒 - /// 是否可通行 - /// 通行成本 - /// 单元格类型 - /// 被覆盖的障碍物单元格数量 - private int MarkCellsInBoundingBox(GridMap gridMap, BoundingBox3D boundingBox, - bool isWalkable, double cost, ElementType cellType) - { - var minGrid = gridMap.WorldToGrid(boundingBox.Min); - var maxGrid = gridMap.WorldToGrid(boundingBox.Max); - - int overriddenCells = 0; - int totalCells = (maxGrid.X - minGrid.X + 1) * (maxGrid.Y - minGrid.Y + 1); - int processedCells = 0; - - for (int x = Math.Max(0, minGrid.X); x <= Math.Min(gridMap.Width - 1, maxGrid.X); x++) - { - for (int y = Math.Max(0, minGrid.Y); y <= Math.Min(gridMap.Height - 1, maxGrid.Y); y++) - { - var gridPos = new Point2D(x, y); - - // 只有在以下情况才覆盖现有标记: - // 1. 当前是空白区域(OpenSpace) - // 2. 当前是低优先级的障碍物,而新标记是可通行区域 - var currentCell = gridMap.Cells[gridPos.X, gridPos.Y]; - - bool shouldOverride = false; - - if (currentCell.CellType == ElementType.OpenSpace) - { - // 空白区域可以被标记为特殊区域 - shouldOverride = true; - } - else if (!currentCell.IsWalkable && isWalkable && - (cellType == ElementType.Channel || cellType == ElementType.Door)) - { - // 障碍物可以被通道和门覆盖(但要谨慎) - shouldOverride = true; - overriddenCells++; - } - else if (currentCell.IsWalkable && isWalkable) - { - // 可通行区域之间可以相互覆盖(更新类型和成本) - shouldOverride = true; - } - - if (shouldOverride) - { - gridMap.SetCell(gridPos, isWalkable, cost, cellType); - processedCells++; - } - } - } - - if (overriddenCells > 0) - { - LogManager.Warning($"[特殊区域标记] {cellType}覆盖了{overriddenCells}/{totalCells}个障碍物单元格,处理了{processedCells}个单元格"); - } - - return overriddenCells; - } - - /// - /// 应用楼层感知过滤,只保留起终点所在楼层的模型项(使用Search API优化) - /// - /// 所有模型项 - /// 起点 - /// 终点 - /// Navisworks文档 - /// 过滤后的模型项列表 - private List ApplyFloorAwareFilteringWithSearchAPI(List allItems, Point3D startPoint, Point3D endPoint, Document document) - { - try - { - LogManager.Info("[楼层过滤] 开始基于物流属性的复杂楼层过滤"); - LogManager.Info($"[楼层过滤] 起点: ({startPoint.X:F1}, {startPoint.Y:F1}, {startPoint.Z:F1})"); - LogManager.Info($"[楼层过滤] 终点: ({endPoint.X:F1}, {endPoint.Y:F1}, {endPoint.Z:F1})"); - LogManager.Info($"[楼层过滤] 输入模型项数量: {allItems.Count}"); - - // 步骤1:获取通道楼层属性值 - string channelFloorValue = GetChannelFloorValue(document); - - if (string.IsNullOrEmpty(channelFloorValue)) - { - LogManager.Warning("[楼层过滤] 未找到通道楼层属性,应用简化楼层过滤"); - return ApplySimplifiedFloorFiltering(allItems, startPoint, endPoint); - } - - LogManager.Info($"[楼层过滤] 检测到通道楼层属性值: '{channelFloorValue}'"); - - // 步骤2:使用Search API获取该楼层的所有项目 - var floorItems = GetItemsInTargetFloorUsingSearchAPI(document, channelFloorValue); - - if (floorItems.Count == 0) - { - LogManager.Warning($"[楼层过滤] 未找到楼层 '{channelFloorValue}' 的项目,应用简化楼层过滤"); - return ApplySimplifiedFloorFiltering(allItems, startPoint, endPoint); - } - - LogManager.Info($"[楼层过滤] Search API找到 {floorItems.Count} 个属于楼层 '{channelFloorValue}' 的项目"); - - // 步骤3:将楼层项目转换为哈希集合以提高查找效率 - var floorItemSet = new HashSet(floorItems); - - // 步骤4:从输入的所有项目中筛选出属于目标楼层的项目 - var filteredItems = new List(); - int filteredCount = 0; - - foreach (var item in allItems) - { - try - { - // 检查项目是否属于目标楼层 - if (floorItemSet.Contains(item)) - { - filteredItems.Add(item); - filteredCount++; - } - } - catch (Exception ex) - { - LogManager.Warning($"[楼层过滤] 检查项目时出错: {ex.Message}"); - // 出错时保留项目 - filteredItems.Add(item); - } - } - - LogManager.Info($"[楼层过滤] 楼层过滤完成: {allItems.Count} -> {filteredItems.Count} (匹配楼层: {filteredCount})"); - - // 步骤5:验证过滤结果的合理性 - if (filteredItems.Count == 0) - { - LogManager.Warning("[楼层过滤] 过滤后无剩余项目,使用原始项目列表"); - return allItems; - } - - double filterRatio = (double)filteredItems.Count / allItems.Count; - if (filterRatio < 0.1) // 如果过滤掉了90%以上的项目,可能有问题 - { - LogManager.Warning($"[楼层过滤] 过滤比例过低 ({filterRatio:P1}),可能存在问题"); - } - - return filteredItems; - } - catch (Exception ex) - { - LogManager.Error($"[楼层过滤] 楼层感知过滤失败: {ex.Message},应用简化楼层过滤"); - return ApplySimplifiedFloorFiltering(allItems, startPoint, endPoint); - } - } /// /// 搜索所有物流通道模型 @@ -1656,226 +991,6 @@ namespace NavisworksTransport.PathPlanning return channelModels; } } - - /// - /// 获取通道所在的楼层值 - /// - /// Navisworks文档 - /// 通道所在楼层的值 - private string GetChannelFloorValue(Document document) - { - try - { - LogManager.Info("[楼层属性] 开始获取通道楼层属性"); - - // 使用新的搜索方法获取通道模型 - var channelModels = SearchChannelModels(document); - - if (channelModels.Count == 0) - { - LogManager.Warning("[楼层属性] 未找到任何通道模型"); - return null; - } - - // 遍历所有通道,获取第一个有楼层属性的通道 - foreach (var channelItem in channelModels) - { - LogManager.Info($"[楼层属性] 分析通道 '{channelItem.DisplayName}' 的楼层属性"); - - foreach (PropertyCategory category in channelItem.PropertyCategories) - { - LogManager.Info($"[楼层属性] 检查属性类别: '{category.DisplayName}'"); - foreach (DataProperty property in category.Properties) - { - LogManager.Info($"[楼层属性] 检查属性: '{property.DisplayName}' = '{property.Value}'"); - - if (property.DisplayName == "层") - { - string floorValue = property.Value?.ToDisplayString()?.Trim(); - if (!string.IsNullOrEmpty(floorValue)) - { - LogManager.Info($"[楼层属性] ✅ 通道 '{channelItem.DisplayName}' 楼层属性: '{floorValue}'"); - return floorValue; - } - } - } - } - - LogManager.Warning($"[楼层属性] 通道 '{channelItem.DisplayName}' 没有'层'属性"); - } - - LogManager.Warning("[楼层属性] 所有通道都没有楼层属性"); - return null; - } - catch (Exception ex) - { - LogManager.Error($"[楼层属性] 获取通道楼层值失败: {ex.Message}"); - return null; - } - } - - /// - /// 使用Search API获取具有楼层属性的项目 - /// - /// Navisworks文档 - /// 目标楼层值 - /// 属于目标楼层的项目列表 - private List GetItemsInTargetFloorUsingSearchAPI(Document document, string targetFloorValue) - { - var floorItems = new List(); - - try - { - LogManager.Info($"[Search API] 使用正确的类别名称搜索楼层 '{targetFloorValue}'"); - - // 使用Search API,楼层属性"层"位于"项目"类别中 - using (var search = new Search()) - { - // 🔥 关键修复:设置搜索范围为全选 - search.Selection.SelectAll(); - LogManager.Info("[Search API] ✅ 已设置搜索范围为全选 (SelectAll)"); - - var searchConditions = search.SearchConditions; - searchConditions.Clear(); - - // 正确的搜索条件:在"项目"类别中查找"层"属性 - var hasFloorProperty = SearchCondition.HasPropertyByDisplayName("项目", "层"); - var floorValueCondition = hasFloorProperty.EqualValue(new VariantData(targetFloorValue)); - searchConditions.Add(floorValueCondition); - - LogManager.Info($"[Search API] 搜索条件:在'项目'类别中查找'层'属性等于'{targetFloorValue}'"); - - ModelItemCollection searchResults = search.FindAll(document, false); - LogManager.Info($"[Search API] 找到 {searchResults.Count} 个具有楼层 '{targetFloorValue}' 属性的层级节点"); - - // 从每个层级节点收集第一层子项目 - foreach (ModelItem layerNode in searchResults) - { - // 只收集第一层子项目,不递归到所有后代 - LogManager.Info($"[Search API] 开始收集层级节点 '{layerNode.DisplayName}' 的第一层子项目"); - int childCount = 0; - foreach (ModelItem child in layerNode.Children) - { - try - { - // 检查是否为通道 - bool isChannel = false; - try - { - if (HasLogisticsProperty(child)) - { - var logisticsType = CategoryAttributeManager.GetLogisticsElementType(child); - if (logisticsType == CategoryAttributeManager.LogisticsElementType.通道) - { - isChannel = true; - } - } - } - catch { } - - // 对于通道,无论是否有几何体都要包含 - if (child.HasGeometry || isChannel) - { - floorItems.Add(child); - childCount++; - } - else - { - // 如果第一层子项目没有几何体,继续查找其子项目(限制深度为1) - foreach (ModelItem grandchild in child.Children) - { - if (grandchild.HasGeometry) - { - floorItems.Add(grandchild); - childCount++; - } - } - } - } - catch (Exception childEx) - { - LogManager.Warning($"[Search API] 处理第一层子项目 '{child?.DisplayName}' 时出错: {childEx.Message}"); - } - } - LogManager.Info($"[Search API] 层级节点 '{layerNode.DisplayName}' 第一层子项目收集完成,共收集 {childCount} 个"); - } - } - - LogManager.Info($"[Search API] Search API找到 {floorItems.Count} 个属于楼层 '{targetFloorValue}' 的项目"); - } - catch (Exception ex) - { - LogManager.Warning($"[Search API] Search API搜索楼层 '{targetFloorValue}' 失败: {ex.Message},回退到手动遍历"); - - // 如果Search API失败,回退到手动遍历 - var allModelItems = new List(); - foreach (var model in document.Models) - { - if (model.RootItem != null) - { - CollectAllItemsRecursively(model.RootItem, allModelItems); - } - } - - LogManager.Info($"[Search API] 开始在 {allModelItems.Count} 个模型项中手动查找楼层 '{targetFloorValue}'"); - - // 遍历所有模型项,查找具有目标楼层属性的项目 - int foundCount = 0; - foreach (var item in allModelItems) - { - try - { - // 检查该项目是否属于目标楼层 - if (IsItemInTargetFloor(item, targetFloorValue)) - { - floorItems.Add(item); - foundCount++; - } - } - catch (Exception itemEx) - { - LogManager.Warning($"[Search API] 检查模型项楼层属性时出错: {itemEx.Message}"); - } - } - - LogManager.Info($"[Search API] 手动遍历找到 {foundCount} 个属于楼层 '{targetFloorValue}' 的项目"); - } - - return floorItems; - } - - /// - /// 检查模型项是否属于目标楼层 - /// - /// 模型项 - /// 目标楼层值 - /// 是否属于目标楼层 - private bool IsItemInTargetFloor(ModelItem item, string targetFloorValue) - { - try - { - foreach (PropertyCategory category in item.PropertyCategories) - { - foreach (DataProperty property in category.Properties) - { - if (property.DisplayName == "层") - { - string floorValue = property.Value?.ToDisplayString()?.Trim(); - if (!string.IsNullOrEmpty(floorValue) && floorValue == targetFloorValue) - { - return true; - } - } - } - } - } - catch (Exception ex) - { - LogManager.Warning($"[Search API] 检查楼层属性时出错: {ex.Message}"); - } - - return false; - } /// /// 递归收集节点的所有后代 @@ -1893,64 +1008,5 @@ namespace NavisworksTransport.PathPlanning CollectAllDescendants(child, result); } } - - /// - /// 应用简化的楼层过滤(基于高程) - /// - /// 所有模型项 - /// 起点 - /// 终点 - /// 过滤后的模型项 - private List ApplySimplifiedFloorFiltering(List allItems, Point3D startPoint, Point3D endPoint) - { - try - { - // 计算起终点的平均高程 - double avgElevation = (startPoint.Z + endPoint.Z) / 2.0; - const double elevationTolerance = 10.0; // 10米高程容差 - - LogManager.Info($"[简化楼层过滤] 使用平均高程 {avgElevation:F1},容差±{elevationTolerance}m"); - - var filteredItems = new List(); - - foreach (var item in allItems) - { - try - { - var boundingBox = item.BoundingBox(); - if (boundingBox != null) - { - // 检查项目的高程范围是否与起终点重叠 - double itemMinZ = boundingBox.Min.Z; - double itemMaxZ = boundingBox.Max.Z; - - // 如果项目的高程范围与平均高程有重叠,则保留 - if (itemMaxZ >= avgElevation - elevationTolerance && itemMinZ <= avgElevation + elevationTolerance) - { - filteredItems.Add(item); - } - } - else - { - // 无包围盒的项目默认保留 - filteredItems.Add(item); - } - } - catch - { - // 出错时保留项目 - filteredItems.Add(item); - } - } - - LogManager.Info($"[简化楼层过滤] 过滤结果: {allItems.Count} -> {filteredItems.Count}"); - return filteredItems; - } - catch (Exception ex) - { - LogManager.Error($"[简化楼层过滤] 过滤失败: {ex.Message}"); - return allItems; - } - } } } \ No newline at end of file