using System; using System.Collections.Generic; using System.Linq; using System.IO; using Autodesk.Navisworks.Api; namespace NavisworksTransport { /// /// 路径规划管理器 /// 负责路径规划的核心业务逻辑 /// public class PathPlanningManager { private CategoryAttributeManager _categoryManager; private VisibilityManager _visibilityManager; private CoordinateConverter _coordinateConverter; private NavigationMapWindow _mapWindow; private List _selectedChannels; private List _routes; private PathRoute _currentRoute; private ChannelBounds _combinedChannelBounds; // 新增:3D交互模式相关 private bool _isPathEditMode = false; private PathPointType _currentPointType = PathPointType.WayPoint; // 静态标志,用于跨实例跟踪3D编辑模式状态 private static bool _globalIsPathEditMode = false; // 日志文件路径 private static string _logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "NavisworksTransport_Debug.log"); /// /// 是否处于路径编辑模式 /// public bool IsPathEditMode { get { return _isPathEditMode || _globalIsPathEditMode; } } /// /// 全局3D编辑模式状态(静态属性) /// public static bool GlobalIsPathEditMode { get { return _globalIsPathEditMode; } } /// /// 当前设置的路径点类型 /// public PathPointType CurrentPointType { get { return _currentPointType; } set { _currentPointType = value; } } // 新增事件 public event EventHandler PathEditModeChanged; public event EventHandler PathPointAddedIn3D; /// /// 当前选中的通道集合 /// public List SelectedChannels { get { return _selectedChannels; } } /// /// 所有路径集合 /// public List Routes { get { return _routes; } } /// /// 当前活动路径 /// public PathRoute CurrentRoute { get { return _currentRoute; } set { _currentRoute = value; if (_mapWindow != null) { _mapWindow.CurrentRoute = _currentRoute; } CurrentRouteChanged?.Invoke(this, _currentRoute); } } /// /// 组合通道边界 /// public ChannelBounds CombinedChannelBounds { get { return _combinedChannelBounds; } } // 事件定义 public event EventHandler> ChannelsSelected; public event EventHandler CurrentRouteChanged; public event EventHandler RouteGenerated; public event EventHandler StatusChanged; public event EventHandler ErrorOccurred; /// /// 构造函数 /// /// 类别属性管理器 /// 可见性管理器 public PathPlanningManager(CategoryAttributeManager categoryManager, VisibilityManager visibilityManager) { _categoryManager = categoryManager ?? throw new ArgumentNullException(nameof(categoryManager)); _visibilityManager = visibilityManager ?? throw new ArgumentNullException(nameof(visibilityManager)); _selectedChannels = new List(); _routes = new List(); _currentRoute = new PathRoute("默认路径"); } /// /// 无参构造函数(为向后兼容而保留) /// public PathPlanningManager() { _categoryManager = new CategoryAttributeManager(); _visibilityManager = new VisibilityManager(); _selectedChannels = new List(); _routes = new List(); _currentRoute = new PathRoute("默认路径"); } /// /// 选择通道模型 /// /// 是否使用当前选择的模型 /// 选择的通道数量 public int SelectChannels(bool useCurrentSelection = true) { try { _selectedChannels.Clear(); if (useCurrentSelection) { // 使用当前选择的模型 var currentSelection = Application.ActiveDocument.CurrentSelection.SelectedItems; if (currentSelection.Any()) { _selectedChannels.AddRange(currentSelection); OnStatusChanged($"已选择 {_selectedChannels.Count} 个模型作为通道"); } else { OnStatusChanged("未选择任何模型,请先选择通道模型"); return 0; } } else { // 通过类别属性筛选通道 var channelItems = FilterChannelsByCategory(); _selectedChannels.AddRange(channelItems); OnStatusChanged($"通过类别筛选到 {_selectedChannels.Count} 个通道"); } if (_selectedChannels.Any()) { // 计算组合边界 CalculateCombinedBounds(); // 触发事件 ChannelsSelected?.Invoke(this, _selectedChannels); } return _selectedChannels.Count; } catch (Exception ex) { OnErrorOccurred($"选择通道时发生错误: {ex.Message}"); return 0; } } /// /// 通过类别筛选通道模型 /// /// 通道模型集合 private List FilterChannelsByCategory() { var channelItems = new List(); try { // 获取所有模型项 var allItems = new List(); foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems) { CollectAllItems(rootItem, allItems); } foreach (var item in allItems) { // 检查是否有物流类别属性 if (CategoryAttributeManager.HasLogisticsAttributes(item)) { // 通过FilterByLogisticsType方法检查是否为通道类型 var singleItemCollection = new ModelItemCollection(); singleItemCollection.Add(item); var filteredItems = CategoryAttributeManager.FilterByLogisticsType(singleItemCollection, CategoryAttributeManager.LogisticsElementType.通道); if (filteredItems.Count > 0) { channelItems.Add(item); } } } } catch (Exception ex) { OnErrorOccurred($"筛选通道类别时发生错误: {ex.Message}"); } return channelItems; } /// /// 计算组合通道边界 /// private void CalculateCombinedBounds() { if (!_selectedChannels.Any()) { _combinedChannelBounds = null; return; } try { // 获取单位转换系数 var conversionFactor = GetUnitsToMetersConversionFactor(); var units = Application.ActiveDocument.Units; OnStatusChanged($"检测到文档单位: {units},转换系数: {conversionFactor}"); // 初始化边界值 var allBounds = new List(); foreach (var channel in _selectedChannels) { try { var originalBoundingBox = channel.BoundingBox(); if (originalBoundingBox != null) { // 转换包围盒到米单位 var boundingBoxInMeters = ConvertBoundingBoxToMeters(originalBoundingBox, conversionFactor); allBounds.Add(boundingBoxInMeters); } } catch { // 忽略无法获取边界的模型 continue; } } if (allBounds.Any()) { // 计算组合边界 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); // 检查边界尺寸是否合理 var width = maxX - minX; var length = maxY - minY; var height = maxZ - minZ; var maxDimension = Math.Max(width, Math.Max(length, height)); if (maxDimension > 1000) // 超过1公里,可能是坐标系统问题 { OnStatusChanged($"警告:通道边界过大({maxDimension:F2}m),将限制到合理范围"); // 计算中心点 var centerX = (minX + maxX) / 2; var centerY = (minY + maxY) / 2; var centerZ = (minZ + maxZ) / 2; // 限制到合理范围(100米) var limitedSize = 100; var halfSize = limitedSize / 2; minX = centerX - halfSize; maxX = centerX + halfSize; minY = centerY - halfSize; maxY = centerY + halfSize; minZ = centerZ - halfSize; maxZ = centerZ + halfSize; } var combinedBoundingBox = new BoundingBox3D( new Point3D(minX, minY, minZ), new Point3D(maxX, maxY, maxZ) ); _combinedChannelBounds = new ChannelBounds(combinedBoundingBox); OnStatusChanged($"已计算通道边界: {_combinedChannelBounds.MinPoint.X:F2},{_combinedChannelBounds.MinPoint.Y:F2} - {_combinedChannelBounds.MaxPoint.X:F2},{_combinedChannelBounds.MaxPoint.Y:F2}"); } } catch (Exception ex) { OnErrorOccurred($"计算通道边界时发生错误: {ex.Message}"); } } /// /// 显示导航地图窗口 /// /// 地图宽度 /// 地图高度 /// 是否成功显示 public bool ShowNavigationMap(double mapWidth = 800, double mapHeight = 600) { try { if (_mapWindow != null && !_mapWindow.IsDisposed) { _mapWindow.BringToFront(); return true; } // 确保有选中的通道 if (_selectedChannels.Count == 0) { var selectionResult = ShowChannelSelectionDialog(); if (!selectionResult.Success || selectionResult.SelectedChannels.Count == 0) { OnStatusChanged("未选择通道,无法显示导航地图"); return false; } // 更新选中的通道 _selectedChannels.Clear(); _selectedChannels.AddRange(selectionResult.SelectedChannels); CalculateCombinedBounds(); } // 创建坐标转换器 if (_coordinateConverter == null) { _coordinateConverter = new CoordinateConverter(_combinedChannelBounds, mapWidth, mapHeight); } // 创建并显示导航地图窗口 _mapWindow = new NavigationMapWindow(_coordinateConverter, _selectedChannels); _mapWindow.CurrentRoute = _currentRoute; SetupMapWindowEvents(); _mapWindow.Show(); OnStatusChanged("导航地图已打开"); return true; } catch (Exception ex) { OnErrorOccurred($"显示导航地图失败: {ex.Message}"); return false; } } /// /// 显示路径规划界面(MainPlugin兼容方法) /// /// 是否成功显示 public bool ShowPathPlanningInterface() { return ShowNavigationMap(); } /// /// 添加路径到管理器(MainPlugin兼容方法) /// /// 要添加的路径 /// 是否成功添加 public bool AddRoute(PathRoute route) { try { if (route == null) { OnErrorOccurred("无法添加空路径"); return false; } // 检查是否已存在同名路径 var existingRoute = _routes.FirstOrDefault(r => r.Name == route.Name); if (existingRoute != null) { // 如果存在同名路径,生成唯一名称 int counter = 1; string originalName = route.Name; while (_routes.Any(r => r.Name == route.Name)) { route.Name = $"{originalName}_{counter}"; counter++; } } _routes.Add(route); OnStatusChanged($"已添加路径: {route.Name}"); // 如果当前没有活动路径,设置此路径为活动路径 if (_currentRoute == null || _currentRoute.Points.Count == 0) { CurrentRoute = route; } // 触发路径生成事件 RouteGenerated?.Invoke(this, route); return true; } catch (Exception ex) { OnErrorOccurred($"添加路径失败: {ex.Message}"); return false; } } /// /// 设置地图窗口事件处理器 /// private void SetupMapWindowEvents() { if (_mapWindow != null) { _mapWindow.PathGenerated += MapWindow_PathGenerated; _mapWindow.PointSelected += MapWindow_PointSelected; _mapWindow.PointAdded += MapWindow_PointAdded; _mapWindow.PointRemoved += MapWindow_PointRemoved; _mapWindow.FormClosed += MapWindow_FormClosed; } } /// /// 创建新路径 /// /// 路径名称 /// 新创建的路径 public PathRoute CreateNewRoute(string routeName = null) { if (string.IsNullOrEmpty(routeName)) { routeName = $"路径_{_routes.Count + 1}"; } var newRoute = new PathRoute(routeName); _routes.Add(newRoute); CurrentRoute = newRoute; OnStatusChanged($"已创建新路径: {routeName}"); return newRoute; } /// /// 删除路径 /// /// 要删除的路径 /// 是否成功删除 public bool DeleteRoute(PathRoute route) { if (route == null) return false; try { bool removed = _routes.Remove(route); if (removed) { if (_currentRoute == route) { _currentRoute = _routes.FirstOrDefault() ?? new PathRoute("默认路径"); CurrentRouteChanged?.Invoke(this, _currentRoute); } OnStatusChanged($"已删除路径: {route.Name}"); } return removed; } catch (Exception ex) { OnErrorOccurred($"删除路径时发生错误: {ex.Message}"); return false; } } /// /// 生成路径 /// /// 要生成的路径,为null时使用当前路径 /// 是否成功生成 public bool GeneratePath(PathRoute route = null) { route = route ?? _currentRoute; if (route == null) return false; try { // 验证路径有效性 if (!route.IsValid()) { OnErrorOccurred("路径无效:必须包含至少一个起点和一个终点"); return false; } // 更新路径关联的通道ID route.AssociatedChannelIds.Clear(); foreach (var channel in _selectedChannels) { // 这里可以添加更复杂的逻辑来获取模型ID route.AssociatedChannelIds.Add(channel.InstanceGuid.ToString()); } // 计算预估时间(简单实现) CalculateEstimatedTime(route); OnStatusChanged($"路径生成成功: {route.Name}, 长度: {route.TotalLength:F2}米, 预估时间: {route.EstimatedTime:F1}秒"); RouteGenerated?.Invoke(this, route); return true; } catch (Exception ex) { OnErrorOccurred($"生成路径时发生错误: {ex.Message}"); return false; } } /// /// 计算预估时间 /// /// 路径 private void CalculateEstimatedTime(PathRoute route) { // 简单的时间估算:假设平均速度1米/秒 const double averageSpeed = 1.0; // 米/秒 route.EstimatedTime = route.TotalLength / averageSpeed; } /// /// 隐藏非通道元素 /// /// 是否成功 public bool HideNonChannelElements() { try { if (!_selectedChannels.Any()) { OnErrorOccurred("请先选择通道模型"); return false; } // 获取所有非通道元素 var allItems = new List(); foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems) { CollectAllItems(rootItem, allItems); } var nonChannelItems = allItems.Except(_selectedChannels).ToList(); // 使用可见性管理器隐藏非通道元素 var result = _visibilityManager.HideNonLogisticsItemsInstance(); if (result.Success) { OnStatusChanged($"已隐藏 {nonChannelItems.Count} 个非通道元素"); return true; } else { OnErrorOccurred($"隐藏非通道元素失败: {result.Message}"); return false; } } catch (Exception ex) { OnErrorOccurred($"隐藏非通道元素时发生错误: {ex.Message}"); return false; } } /// /// 显示所有元素 /// /// 是否成功 public bool ShowAllElements() { try { var result = _visibilityManager.ShowAllItemsInstance(); if (result.Success) { OnStatusChanged("已显示所有元素"); return true; } else { OnErrorOccurred($"显示所有元素失败: {result.Message}"); return false; } } catch (Exception ex) { OnErrorOccurred($"显示所有元素时发生错误: {ex.Message}"); return false; } } /// /// 获取路径统计信息 /// /// 统计信息字符串 public string GetStatistics() { var stats = $"通道数量: {_selectedChannels.Count}\n"; stats += $"路径数量: {_routes.Count}\n"; if (_currentRoute != null) { stats += $"当前路径: {_currentRoute.Name}\n"; stats += $"路径点数: {_currentRoute.Points.Count}\n"; stats += $"路径长度: {_currentRoute.TotalLength:F2}米\n"; stats += $"预估时间: {_currentRoute.EstimatedTime:F1}秒\n"; } if (_combinedChannelBounds != null) { double scaleX = 0, scaleY = 0; if (_coordinateConverter != null) { _coordinateConverter.GetMapScale(out scaleX, out scaleY); } stats += $"通道范围: {_combinedChannelBounds.MinPoint.X:F2},{_combinedChannelBounds.MinPoint.Y:F2} - {_combinedChannelBounds.MaxPoint.X:F2},{_combinedChannelBounds.MaxPoint.Y:F2}\n"; stats += $"地图缩放: {scaleX:F4}, {scaleY:F4}"; } return stats; } /// /// 清理资源 /// public void Dispose() { try { // 停止3D点击监听 Stop3DClickListener(); if (_mapWindow != null && !_mapWindow.IsDisposed) { _mapWindow.Close(); _mapWindow.Dispose(); } } catch { // 忽略清理错误 } } #region 事件处理器 private void MapWindow_PathGenerated(object sender, PathRoute route) { GeneratePath(route); } private void MapWindow_PointSelected(object sender, PathPoint point) { OnStatusChanged($"已选中路径点: {point.Name} ({point.Type})"); } private void MapWindow_PointAdded(object sender, PathPoint point) { OnStatusChanged($"已添加路径点: {point.Name} ({point.Type})"); } private void MapWindow_PointRemoved(object sender, PathPoint point) { OnStatusChanged($"已移除路径点: {point.Name} ({point.Type})"); } private void MapWindow_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e) { OnStatusChanged("导航地图窗口已关闭"); } #endregion #region 事件触发方法 private void OnStatusChanged(string status) { StatusChanged?.Invoke(this, status); } private void OnErrorOccurred(string error) { ErrorOccurred?.Invoke(this, error); } /// /// 写入调试日志 /// /// 日志消息 private static void WriteLog(string message) { try { string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}"; File.AppendAllText(_logFilePath, logEntry + Environment.NewLine); } catch { // 忽略日志写入错误 } } #endregion /// /// 获取所有通道选择结果 /// /// 通道选择结果 public ChannelSelectionResult GetAllChannelSelectionResults() { var result = new ChannelSelectionResult(); try { // 获取所有模型项 var allItems = new List(); foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems) { CollectAllItems(rootItem, allItems); } var filteredItems = allItems.Where(item => item.HasGeometry).ToArray(); result.TotalModelItems = filteredItems.Length; // 自动检测通道 result.AutoDetectedChannels = AutoDetectChannels(filteredItems); // 筛选已标记的物流元素 result.LogisticsMarkedItems = FilterLogisticsMarkedItems(filteredItems); // 筛选可通行区域 result.TraversableAreas = FilterTraversableAreas(result.LogisticsMarkedItems); // 筛选通道类型 result.ChannelItems = FilterChannelItems(result.LogisticsMarkedItems); result.Success = true; result.Message = $"成功分析 {result.TotalModelItems} 个模型项"; } catch (Exception ex) { result.Success = false; result.Message = $"通道分析失败: {ex.Message}"; System.Diagnostics.Debug.WriteLine($"获取通道选择结果失败: {ex.Message}"); } return result; } /// /// 自动检测可能的通道 /// /// 模型项数组 /// 自动检测的通道集合 private ModelItemCollection AutoDetectChannels(ModelItem[] items) { var channels = new ModelItemCollection(); try { foreach (var item in items) { // 根据几何特征和命名规则自动检测通道 if (IsLikelyChannel(item)) { channels.Add(item); } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"自动检测通道失败: {ex.Message}"); } return channels; } /// /// 判断模型项是否可能是通道 /// /// 模型项 /// 是否可能是通道 private bool IsLikelyChannel(ModelItem item) { try { // 检查显示名称中的关键词 var displayName = item.DisplayName?.ToLower() ?? ""; var channelKeywords = new[] { "通道", "corridor", "passage", "walkway", "path", "道路", "路径" }; if (channelKeywords.Any(keyword => displayName.Contains(keyword))) { return true; } // 检查几何特征(简化判断) if (item.HasGeometry) { var boundingBox = item.BoundingBox(); if (boundingBox != null) { var width = boundingBox.Max.X - boundingBox.Min.X; var length = boundingBox.Max.Y - boundingBox.Min.Y; var height = boundingBox.Max.Z - boundingBox.Min.Z; // 判断是否为长条形(可能是通道) var lengthToWidthRatio = Math.Max(width, length) / Math.Min(width, length); var isLongNarrow = lengthToWidthRatio > 3.0 && height > 2.0 && height < 5.0; if (isLongNarrow) { return true; } } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"判断通道特征失败: {ex.Message}"); } return false; } /// /// 筛选已标记物流属性的模型项 /// /// 模型项数组 /// 已标记的物流模型项集合 private ModelItemCollection FilterLogisticsMarkedItems(ModelItem[] items) { var markedItems = new ModelItemCollection(); try { foreach (var item in items) { if (CategoryAttributeManager.HasLogisticsAttributes(item)) { markedItems.Add(item); } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"筛选已标记物流项失败: {ex.Message}"); } return markedItems; } /// /// 筛选可通行区域 /// /// 模型项集合 /// 可通行区域集合 private ModelItemCollection FilterTraversableAreas(ModelItemCollection items) { var traversableItems = new ModelItemCollection(); try { traversableItems = CategoryAttributeManager.FilterTraversableItems(items); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"筛选可通行区域失败: {ex.Message}"); } return traversableItems; } /// /// 筛选通道类型的模型项 /// /// 模型项集合 /// 通道类型模型项集合 private ModelItemCollection FilterChannelItems(ModelItemCollection items) { var channelItems = new ModelItemCollection(); try { channelItems = CategoryAttributeManager.FilterByLogisticsType(items, CategoryAttributeManager.LogisticsElementType.通道); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"筛选通道类型项失败: {ex.Message}"); } return channelItems; } /// /// 根据车辆尺寸筛选适用通道 /// /// 车辆尺寸 /// 适用的通道集合 public ModelItemCollection FilterChannelsByVehicleSize(string vehicleSize) { var applicableChannels = new ModelItemCollection(); try { // 获取所有已标记的物流项 var allItems = new List(); foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems) { CollectAllItems(rootItem, allItems); } var filteredItems = allItems.Where(item => item.HasGeometry && CategoryAttributeManager.HasLogisticsAttributes(item)).ToArray(); var logisticsItems = new ModelItemCollection(); foreach (var item in allItems) { logisticsItems.Add(item); } // 筛选适用车辆尺寸的通道 applicableChannels = CategoryAttributeManager.FilterByVehicleSize(logisticsItems, vehicleSize); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"根据车辆尺寸筛选通道失败: {ex.Message}"); } return applicableChannels; } /// /// 手动选择通道 /// /// 手动选择的通道集合 public ModelItemCollection GetManuallySelectedChannels() { var selectedChannels = new ModelItemCollection(); try { // 获取当前用户选择的模型项 var currentSelection = Application.ActiveDocument.CurrentSelection.SelectedItems; foreach (ModelItem item in currentSelection) { selectedChannels.Add(item); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"获取手动选择通道失败: {ex.Message}"); } return selectedChannels; } /// /// 设置选中的通道为活动通道 /// /// 通道集合 public void SetActiveChannels(ModelItemCollection channels) { try { _selectedChannels.Clear(); foreach (ModelItem channel in channels) { _selectedChannels.Add(channel); } // 计算通道边界 CalculateCombinedBounds(); // 触发通道选择事件 ChannelsSelected?.Invoke(this, _selectedChannels); System.Diagnostics.Debug.WriteLine($"设置了 {_selectedChannels.Count} 个活动通道"); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"设置活动通道失败: {ex.Message}"); } } /// /// 高亮显示通道 /// /// 要高亮的通道集合 /// 高亮颜色 public void HighlightChannels(ModelItemCollection channels, System.Drawing.Color color) { try { if (channels == null || channels.Count == 0) return; // 使用临时颜色覆盖高亮通道 var navisColor = new Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f); Application.ActiveDocument.Models.OverrideTemporaryColor(channels, navisColor); // 刷新视图 Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"高亮通道失败: {ex.Message}"); } } /// /// 清除通道高亮 /// public void ClearChannelHighlight() { try { // 重置所有临时材质 Application.ActiveDocument.Models.ResetAllTemporaryMaterials(); // 刷新视图 Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"清除通道高亮失败: {ex.Message}"); } } /// /// 显示通道选择对话框 /// /// 用户选择结果 public ChannelSelectionDialogResult ShowChannelSelectionDialog() { var result = new ChannelSelectionDialogResult(); try { // 获取通道分析结果 var analysisResult = GetAllChannelSelectionResults(); // 创建通道选择对话框 var dialog = new ChannelSelectionDialog(analysisResult); if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { result.Success = true; result.SelectedChannels = dialog.SelectedChannels; result.SelectionMethod = dialog.SelectionMethod; result.VehicleSize = dialog.VehicleSize; // 设置选中的通道为活动通道 SetActiveChannels(result.SelectedChannels); } else { result.Success = false; result.Message = "用户取消了通道选择"; } } catch (Exception ex) { result.Success = false; result.Message = $"显示通道选择对话框失败: {ex.Message}"; System.Diagnostics.Debug.WriteLine($"显示通道选择对话框失败: {ex.Message}"); } return result; } /// /// 获取通道统计信息 /// /// 通道统计信息 public string GetChannelStatistics() { try { var analysisResult = GetAllChannelSelectionResults(); var statistics = $"通道分析统计:\n" + $"总模型项: {analysisResult.TotalModelItems}\n" + $"已标记物流项: {analysisResult.LogisticsMarkedItems.Count}\n" + $"可通行区域: {analysisResult.TraversableAreas.Count}\n" + $"通道类型项: {analysisResult.ChannelItems.Count}\n" + $"自动检测通道: {analysisResult.AutoDetectedChannels.Count}\n" + $"当前选中通道: {_selectedChannels.Count}"; return statistics; } catch (Exception ex) { return $"获取统计信息失败: {ex.Message}"; } } #region 路径验证与优化 /// /// 验证路径有效性 /// /// 要验证的路径 /// 验证结果 public PathValidationResult ValidatePath(PathRoute route) { var result = new PathValidationResult { RouteId = route?.Id ?? "", RouteName = route?.Name ?? "" }; if (route == null) { result.IsValid = false; result.Errors.Add("路径对象为空"); return result; } try { // 基本路径验证 ValidateBasicPathStructure(route, result); // 几何有效性验证 ValidatePathGeometry(route, result); // 通道约束验证 ValidateChannelConstraints(route, result); // 碰撞检测 ValidatePathCollisions(route, result); // 可达性验证 ValidatePathReachability(route, result); // 设置总体验证结果 result.IsValid = result.Errors.Count == 0; if (result.IsValid) { result.Message = "路径验证通过"; } else { result.Message = $"路径验证失败,发现 {result.Errors.Count} 个错误"; } } catch (Exception ex) { result.IsValid = false; result.Errors.Add($"验证过程发生异常: {ex.Message}"); result.Message = "路径验证异常"; System.Diagnostics.Debug.WriteLine($"路径验证失败: {ex.Message}"); } return result; } /// /// 验证基本路径结构 /// /// 路径 /// 验证结果 private void ValidateBasicPathStructure(PathRoute route, PathValidationResult result) { // 检查路径点数量 if (route.Points.Count < 2) { result.Errors.Add("路径必须至少包含2个点(起点和终点)"); return; } // 检查起点和终点 var startPoints = route.Points.Where(p => p.Type == PathPointType.StartPoint).ToList(); var endPoints = route.Points.Where(p => p.Type == PathPointType.EndPoint).ToList(); if (startPoints.Count == 0) { result.Errors.Add("路径缺少起点"); } else if (startPoints.Count > 1) { result.Warnings.Add("路径包含多个起点,建议只保留一个"); } if (endPoints.Count == 0) { result.Errors.Add("路径缺少终点"); } else if (endPoints.Count > 1) { result.Warnings.Add("路径包含多个终点,建议只保留一个"); } // 检查路径点索引连续性 var sortedPoints = route.GetSortedPoints(); for (int i = 0; i < sortedPoints.Count; i++) { if (sortedPoints[i].Index != i) { result.Warnings.Add($"路径点索引不连续,位置 {i} 的点索引为 {sortedPoints[i].Index}"); } } } /// /// 验证路径几何有效性 /// /// 路径 /// 验证结果 private void ValidatePathGeometry(PathRoute route, PathValidationResult result) { var points = route.GetSortedPoints(); // 检查重复点 for (int i = 0; i < points.Count - 1; i++) { var distance = CalculateDistance3D(points[i].Position, points[i + 1].Position); if (distance < 0.01) // 1厘米以内认为重复 { result.Warnings.Add($"检测到重复点:点 {i} 和点 {i + 1} 距离过近 ({distance:F3}m)"); } } // 检查路径段长度 for (int i = 0; i < points.Count - 1; i++) { var distance = CalculateDistance3D(points[i].Position, points[i + 1].Position); if (distance < 0.1) // 10厘米 { result.Warnings.Add($"路径段 {i}-{i + 1} 过短 ({distance:F3}m)"); } else if (distance > 100.0) // 100米 { result.Warnings.Add($"路径段 {i}-{i + 1} 过长 ({distance:F3}m),建议添加中间点"); } } // 检查总路径长度 if (route.TotalLength < 0.5) { result.Warnings.Add($"路径总长度过短 ({route.TotalLength:F3}m)"); } else if (route.TotalLength > 1000.0) { result.Warnings.Add($"路径总长度过长 ({route.TotalLength:F3}m),建议分段处理"); } } /// /// 验证通道约束 /// /// 路径 /// 验证结果 private void ValidateChannelConstraints(PathRoute route, PathValidationResult result) { if (_selectedChannels == null || _selectedChannels.Count == 0) { result.Warnings.Add("未选择任何通道,无法验证通道约束"); return; } result.Warnings.Add($"当前选中通道数量: {_selectedChannels.Count}"); var points = route.GetSortedPoints(); foreach (var point in points) { bool isInChannel = false; // 检查点是否在选定的通道内 foreach (ModelItem channel in _selectedChannels) { if (IsPointInChannel(point.Position, channel)) { isInChannel = true; break; } } if (!isInChannel) { result.Warnings.Add($"路径点 '{point.Name}' 不在选定的通道范围内"); } } } /// /// 验证路径碰撞 /// /// 路径 /// 验证结果 private void ValidatePathCollisions(PathRoute route, PathValidationResult result) { try { var points = route.GetSortedPoints(); // 获取所有障碍物 var obstacles = GetObstacles(); if (obstacles.Count == 0) { result.Warnings.Add("未找到障碍物数据,无法进行碰撞检测"); return; } // 检查路径点碰撞 foreach (var point in points) { if (IsPointCollidingWithObstacles(point.Position, obstacles)) { result.Errors.Add($"路径点 '{point.Name}' 与障碍物发生碰撞"); } } // 检查路径段碰撞 for (int i = 0; i < points.Count - 1; i++) { if (IsPathSegmentCollidingWithObstacles(points[i].Position, points[i + 1].Position, obstacles)) { result.Errors.Add($"路径段 {i}-{i + 1} 与障碍物发生碰撞"); } } } catch (Exception ex) { result.Warnings.Add($"碰撞检测失败: {ex.Message}"); } } /// /// 验证路径可达性 /// /// 路径 /// 验证结果 private void ValidatePathReachability(PathRoute route, PathValidationResult result) { var points = route.GetSortedPoints(); // 检查每个路径段的可达性 for (int i = 0; i < points.Count - 1; i++) { var startPoint = points[i].Position; var endPoint = points[i + 1].Position; // 简化的可达性检查:检查高度差 var heightDifference = Math.Abs(endPoint.Z - startPoint.Z); if (heightDifference > 5.0) // 5米高度差 { result.Warnings.Add($"路径段 {i}-{i + 1} 高度差过大 ({heightDifference:F3}m),可能需要电梯或楼梯"); } // 检查坡度 var horizontalDistance = Math.Sqrt( Math.Pow(endPoint.X - startPoint.X, 2) + Math.Pow(endPoint.Y - startPoint.Y, 2) ); if (horizontalDistance > 0.1) { var slope = heightDifference / horizontalDistance; if (slope > 0.2) // 20%坡度 { result.Warnings.Add($"路径段 {i}-{i + 1} 坡度过大 ({slope * 100:F1}%)"); } } } } /// /// 优化路径 /// /// 要优化的路径 /// 优化选项 /// 优化结果 public PathOptimizationResult OptimizePath(PathRoute route, PathOptimizationOptions optimizationOptions = null) { var result = new PathOptimizationResult { OriginalRoute = route, OptimizedRoute = route.Clone() }; if (optimizationOptions == null) { optimizationOptions = new PathOptimizationOptions(); } try { var optimizedRoute = result.OptimizedRoute; // 记录原始路径信息 result.OriginalLength = route.TotalLength; result.OriginalPointCount = route.Points.Count; // 应用各种优化策略 if (optimizationOptions.RemoveDuplicatePoints) { RemoveDuplicatePoints(optimizedRoute, result); } if (optimizationOptions.SmoothPath) { SmoothPath(optimizedRoute, result); } if (optimizationOptions.OptimizeAngles) { OptimizePathAngles(optimizedRoute, result); } if (optimizationOptions.AdjustToChannels) { AdjustPathToChannels(optimizedRoute, result); } // 重新计算优化后的路径信息 optimizedRoute.RecalculateLength(); result.OptimizedLength = optimizedRoute.TotalLength; result.OptimizedPointCount = optimizedRoute.Points.Count; // 计算优化效果 result.LengthReduction = result.OriginalLength - result.OptimizedLength; result.PointReduction = result.OriginalPointCount - result.OptimizedPointCount; result.Success = true; result.Message = $"路径优化完成,长度减少 {result.LengthReduction:F3}m,点数减少 {result.PointReduction}"; } catch (Exception ex) { result.Success = false; result.Message = $"路径优化失败: {ex.Message}"; System.Diagnostics.Debug.WriteLine($"路径优化失败: {ex.Message}"); } return result; } /// /// 移除重复点 /// /// 路径 /// 优化结果 private void RemoveDuplicatePoints(PathRoute route, PathOptimizationResult result) { var points = route.GetSortedPoints(); var pointsToRemove = new List(); for (int i = 0; i < points.Count - 1; i++) { var distance = CalculateDistance3D(points[i].Position, points[i + 1].Position); if (distance < 0.01) // 1厘米阈值 { // 保留索引较小的点,移除后面的点 pointsToRemove.Add(points[i + 1]); result.OptimizationSteps.Add($"移除重复点: {points[i + 1].Name}"); } } foreach (var point in pointsToRemove) { route.RemovePoint(point.Id); } } /// /// 平滑路径 /// /// 路径 /// 优化结果 private void SmoothPath(PathRoute route, PathOptimizationResult result) { var points = route.GetSortedPoints(); if (points.Count < 3) return; // 应用简单的移动平均平滑 for (int i = 1; i < points.Count - 1; i++) { var prevPoint = points[i - 1].Position; var currentPoint = points[i].Position; var nextPoint = points[i + 1].Position; // 计算平滑后的位置 var smoothedPosition = new Point3D( (prevPoint.X + currentPoint.X + nextPoint.X) / 3.0, (prevPoint.Y + currentPoint.Y + nextPoint.Y) / 3.0, (prevPoint.Z + currentPoint.Z + nextPoint.Z) / 3.0 ); // 更新点位置 points[i].Position = smoothedPosition; result.OptimizationSteps.Add($"平滑点: {points[i].Name}"); } } /// /// 优化路径角度 /// /// 路径 /// 优化结果 private void OptimizePathAngles(PathRoute route, PathOptimizationResult result) { var points = route.GetSortedPoints(); if (points.Count < 3) return; var pointsToRemove = new List(); // 检查是否有几乎共线的三个点 for (int i = 1; i < points.Count - 1; i++) { var p1 = points[i - 1].Position; var p2 = points[i].Position; var p3 = points[i + 1].Position; // 计算角度 var angle = CalculateAngle(p1, p2, p3); // 如果角度接近180度(共线),考虑移除中间点 if (Math.Abs(angle - Math.PI) < 0.1) // 约5.7度的容差 { pointsToRemove.Add(points[i]); result.OptimizationSteps.Add($"移除冗余点: {points[i].Name} (角度: {angle * 180 / Math.PI:F1}°)"); } } foreach (var point in pointsToRemove) { route.RemovePoint(point.Id); } } /// /// 调整路径到通道中心 /// /// 路径 /// 优化结果 private void AdjustPathToChannels(PathRoute route, PathOptimizationResult result) { if (_selectedChannels.Count == 0) return; var points = route.GetSortedPoints(); foreach (var point in points) { var adjustedPosition = GetOptimalPositionInChannels(point.Position); if (adjustedPosition != null) { var originalPosition = point.Position; point.Position = adjustedPosition; var distance = CalculateDistance3D(originalPosition, adjustedPosition); result.OptimizationSteps.Add($"调整点到通道中心: {point.Name} (移动距离: {distance:F3}m)"); } } } #region 辅助方法 /// /// 递归收集所有ModelItem /// /// 当前ModelItem /// 收集列表 private void CollectAllItems(ModelItem item, List collection) { if (item == null) return; collection.Add(item); if (item.Children != null && item.Children.Count() > 0) { foreach (ModelItem child in item.Children) { CollectAllItems(child, collection); } } } /// /// 计算两个3D点之间的距离 /// /// 第一个点 /// 第二个点 /// 距离 private double CalculateDistance3D(Point3D point1, Point3D point2) { if (point1 == null || point2 == null) throw new ArgumentNullException("坐标点不能为空"); double dx = point2.X - point1.X; double dy = point2.Y - point1.Y; double dz = point2.Z - point1.Z; return Math.Sqrt(dx * dx + dy * dy + dz * dz); } /// /// 检查点是否在通道内 /// /// 点位置 /// 通道模型项 /// 是否在通道内 private bool IsPointInChannel(Point3D point, ModelItem channel) { try { var boundingBox = channel.BoundingBox(); if (boundingBox == null) return false; return point.X >= boundingBox.Min.X && point.X <= boundingBox.Max.X && point.Y >= boundingBox.Min.Y && point.Y <= boundingBox.Max.Y && point.Z >= boundingBox.Min.Z && point.Z <= boundingBox.Max.Z; } catch { return false; } } /// /// 获取障碍物集合 /// /// 障碍物集合 private ModelItemCollection GetObstacles() { var obstacles = new ModelItemCollection(); try { // 获取所有标记为障碍物的模型项 var allItems = new List(); foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems) { CollectAllItems(rootItem, allItems); } var filteredItems = allItems.Where(item => item.HasGeometry && CategoryAttributeManager.HasLogisticsAttributes(item)).ToArray(); var logisticsItems = new ModelItemCollection(); foreach (var item in filteredItems) { logisticsItems.Add(item); } obstacles = CategoryAttributeManager.FilterByLogisticsType( logisticsItems, CategoryAttributeManager.LogisticsElementType.障碍物); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"获取障碍物失败: {ex.Message}"); } return obstacles; } /// /// 检查点是否与障碍物碰撞 /// /// 点位置 /// 障碍物集合 /// 是否碰撞 private bool IsPointCollidingWithObstacles(Point3D point, ModelItemCollection obstacles) { foreach (ModelItem obstacle in obstacles) { if (IsPointInChannel(point, obstacle)) { return true; } } return false; } /// /// 检查路径段是否与障碍物碰撞 /// /// 起点 /// 终点 /// 障碍物集合 /// 是否碰撞 private bool IsPathSegmentCollidingWithObstacles(Point3D startPoint, Point3D endPoint, ModelItemCollection obstacles) { // 简化的线段碰撞检测:在线段上采样多个点进行检查 const int sampleCount = 10; for (int i = 0; i <= sampleCount; i++) { var t = (double)i / sampleCount; var samplePoint = new Point3D( startPoint.X + t * (endPoint.X - startPoint.X), startPoint.Y + t * (endPoint.Y - startPoint.Y), startPoint.Z + t * (endPoint.Z - startPoint.Z) ); if (IsPointCollidingWithObstacles(samplePoint, obstacles)) { return true; } } return false; } /// /// 计算三点间的角度 /// /// 第一个点 /// 中间点 /// 第三个点 /// 角度(弧度) private double CalculateAngle(Point3D p1, Point3D p2, Point3D p3) { var v1 = new Point3D(p1.X - p2.X, p1.Y - p2.Y, p1.Z - p2.Z); var v2 = new Point3D(p3.X - p2.X, p3.Y - p2.Y, p3.Z - p2.Z); var dotProduct = v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z; var length1 = Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y + v1.Z * v1.Z); var length2 = Math.Sqrt(v2.X * v2.X + v2.Y * v2.Y + v2.Z * v2.Z); if (length1 == 0 || length2 == 0) return 0; var cosAngle = dotProduct / (length1 * length2); cosAngle = Math.Max(-1, Math.Min(1, cosAngle)); // 限制在[-1,1]范围内 return Math.Acos(cosAngle); } /// /// 获取在通道中的最优位置 /// /// 原始位置 /// 最优位置 private Point3D GetOptimalPositionInChannels(Point3D originalPosition) { // 简化实现:返回最近通道的中心点 Point3D bestPosition = originalPosition; double minDistance = double.MaxValue; foreach (ModelItem channel in _selectedChannels) { var boundingBox = channel.BoundingBox(); if (boundingBox != null) { var centerPoint = new Point3D( (boundingBox.Min.X + boundingBox.Max.X) / 2, (boundingBox.Min.Y + boundingBox.Max.Y) / 2, originalPosition.Z // 保持原始高度 ); var distance = CalculateDistance3D(originalPosition, centerPoint); if (distance < minDistance) { minDistance = distance; bestPosition = centerPoint; } } } return bestPosition; } /// /// 获取Navisworks文档单位并转换为米的系数 /// /// 转换系数(文档单位转换为米) private double GetUnitsToMetersConversionFactor() { try { var units = Application.ActiveDocument.Units; switch (units) { case Units.Millimeters: return 0.001; case Units.Centimeters: return 0.01; case Units.Meters: return 1.0; case Units.Inches: return 0.0254; case Units.Feet: return 0.3048; case Units.Kilometers: return 1000.0; case Units.Micrometers: return 0.000001; case Units.Microinches: return 0.0000000254; case Units.Mils: return 0.0000254; case Units.Yards: return 0.9144; case Units.Miles: return 1609.43; default: return 1.0; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"获取文档单位失败: {ex.Message},默认为米"); return 1.0; } } /// /// 转换包围盒到米单位 /// /// 原始包围盒 /// 转换系数 /// 转换后的包围盒(米单位) private BoundingBox3D ConvertBoundingBoxToMeters(BoundingBox3D boundingBox, double conversionFactor) { var minPoint = new Point3D( boundingBox.Min.X * conversionFactor, boundingBox.Min.Y * conversionFactor, boundingBox.Min.Z * conversionFactor ); var maxPoint = new Point3D( boundingBox.Max.X * conversionFactor, boundingBox.Max.Y * conversionFactor, boundingBox.Max.Z * conversionFactor ); return new BoundingBox3D(minPoint, maxPoint); } #endregion #region 新增:3D交互模式相关 /// /// 高亮显示选中的通道 /// /// 高亮颜色,为null时使用默认颜色 /// 是否成功高亮 public bool HighlightSelectedChannels(System.Drawing.Color? highlightColor = null) { try { if (_selectedChannels.Count == 0) { OnErrorOccurred("没有选择任何通道,请先选择通道"); return false; } WriteLog($"[高亮] 开始高亮 {_selectedChannels.Count} 个通道"); // 使用明显的高亮颜色 - 纯绿色,不透明 var color = highlightColor ?? System.Drawing.Color.Green; // 转换为Navisworks颜色 var navisColor = new Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f); WriteLog($"[高亮] 使用颜色: R={navisColor.R}, G={navisColor.G}, B={navisColor.B}"); // 创建ModelItemCollection var itemsToHighlight = new ModelItemCollection(); foreach (var channel in _selectedChannels) { itemsToHighlight.Add(channel); WriteLog($"[高亮] 添加通道: {channel.DisplayName}"); } // 先清除之前的高亮 Application.ActiveDocument.Models.ResetAllTemporaryMaterials(); // 应用临时颜色覆盖 Application.ActiveDocument.Models.OverrideTemporaryColor(itemsToHighlight, navisColor); // 强制刷新视图 Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); OnStatusChanged($"已高亮显示 {_selectedChannels.Count} 个通道"); WriteLog($"[高亮] 高亮完成"); return true; } catch (Exception ex) { WriteLog($"[高亮] 高亮失败: {ex.Message}"); OnErrorOccurred($"高亮通道失败: {ex.Message}"); return false; } } /// /// 清除通道高亮显示 /// /// 是否成功清除 public bool ClearChannelHighlighting() { try { // 重置所有临时材质 Application.ActiveDocument.Models.ResetAllTemporaryMaterials(); // 刷新视图 Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); OnStatusChanged("已清除通道高亮"); return true; } catch (Exception ex) { OnErrorOccurred($"清除通道高亮失败: {ex.Message}"); return false; } } /// /// 进入3D路径编辑模式 /// /// 是否成功进入编辑模式 public bool EnterPathEditMode() { try { if (_selectedChannels.Count == 0) { OnErrorOccurred("请先选择通道再进入路径编辑模式"); return false; } _isPathEditMode = true; _globalIsPathEditMode = true; // 设置全局标志 // 设置活动管理器 _activePathManager = this; // 初始化当前路径(如果没有的话) if (_currentRoute == null) { _currentRoute = CreateNewRoute("路径1"); } // 重置点类型为起点(准备接收第一个点) _currentPointType = PathPointType.StartPoint; // 高亮显示通道 HighlightSelectedChannels(); // 启动3D点击监听 Start3DClickListener(); // 触发模式变更事件 PathEditModeChanged?.Invoke(this, true); OnStatusChanged("已进入3D路径编辑模式,下一个点将设为起点"); WriteLog($"[模式] 已进入路径编辑模式,当前点类型: {GetPointTypeName(_currentPointType)}"); return true; } catch (Exception ex) { OnErrorOccurred($"进入路径编辑模式失败: {ex.Message}"); return false; } } /// /// 退出3D路径编辑模式 /// /// 是否成功退出编辑模式 public bool ExitPathEditMode() { try { // 将最后一个点设为终点(如果有路径点的话) if (_currentRoute != null && _currentRoute.Points != null && _currentRoute.Points.Count > 0) { var lastPoint = _currentRoute.Points[_currentRoute.Points.Count - 1]; if (lastPoint.Type != PathPointType.EndPoint) { // 更新最后一个点为终点 var updatedPoint = new PathPoint { Name = lastPoint.Name.Replace("路径点", "终点").Replace("起点", "终点"), Position = lastPoint.Position, Type = PathPointType.EndPoint, CreatedTime = lastPoint.CreatedTime }; // 替换最后一个点 _currentRoute.Points[_currentRoute.Points.Count - 1] = updatedPoint; // 重新绘制3D标记以更新颜色 Clear3DPathMarkers(); foreach (var point in _currentRoute.Points) { Draw3DPathPoint(point); } WriteLog($"[模式] 已将最后一个点设为终点: {updatedPoint.Name}"); } } _isPathEditMode = false; _globalIsPathEditMode = false; // 清除全局标志 // 停止3D点击监听 Stop3DClickListener(); // 清除通道高亮 ClearChannelHighlighting(); // 触发模式变更事件 PathEditModeChanged?.Invoke(this, false); OnStatusChanged("已退出3D路径编辑模式,最后一个点已设为终点"); return true; } catch (Exception ex) { OnErrorOccurred($"退出路径编辑模式失败: {ex.Message}"); return false; } } /// /// 在3D视图中添加路径点 /// /// 3D世界坐标 /// 路径点类型,为null时使用当前类型 /// 添加的路径点,失败时返回null public PathPoint AddPathPointIn3D(Point3D worldPoint, PathPointType? pointType = null) { try { if (!_isPathEditMode) { OnErrorOccurred("请先进入路径编辑模式"); return null; } var actualPointType = pointType ?? _currentPointType; // 验证点是否在通道范围内 if (!IsPointInSelectedChannels(worldPoint)) { OnErrorOccurred("路径点必须设置在选中的通道内"); return null; } // 创建路径点 var pointName = GeneratePointName(actualPointType); var pathPoint = new PathPoint(worldPoint, pointName, actualPointType); // 添加到当前路径 if (_currentRoute == null) { _currentRoute = new PathRoute("默认路径"); _routes.Add(_currentRoute); } _currentRoute.AddPoint(pathPoint); // 在3D视图中绘制路径点标记 Draw3DPathPoint(pathPoint); // 触发事件 PathPointAddedIn3D?.Invoke(this, pathPoint); // PointAdded?.Invoke(this, pathPoint); // TODO: 定义PointAdded事件 OnStatusChanged($"已添加{GetPointTypeName(actualPointType)}: {pointName}"); return pathPoint; } catch (Exception ex) { OnErrorOccurred($"添加3D路径点失败: {ex.Message}"); return null; } } /// /// 检查点是否在选中的通道内 /// /// 世界坐标点 /// 是否在通道内 private bool IsPointInSelectedChannels(Point3D worldPoint) { try { foreach (var channel in _selectedChannels) { var boundingBox = channel.BoundingBox(); if (boundingBox != null) { if (worldPoint.X >= boundingBox.Min.X && worldPoint.X <= boundingBox.Max.X && worldPoint.Y >= boundingBox.Min.Y && worldPoint.Y <= boundingBox.Max.Y && worldPoint.Z >= boundingBox.Min.Z && worldPoint.Z <= boundingBox.Max.Z) { return true; } } } return false; } catch { return true; // 如果检查失败,允许设置点(宽容处理) } } /// /// 生成路径点名称 /// /// 点类型 /// 生成的名称 private string GeneratePointName(PathPointType pointType) { var currentPoints = _currentRoute?.Points ?? new List(); var typeCount = currentPoints.Count(p => p.Type == pointType) + 1; switch (pointType) { case PathPointType.StartPoint: return $"起点{typeCount}"; case PathPointType.EndPoint: return $"终点{typeCount}"; case PathPointType.WayPoint: return $"路径点{typeCount}"; default: return $"点{typeCount}"; } } /// /// 在3D视图中绘制路径点标记(简化版本) /// /// 路径点 private void Draw3DPathPoint(PathPoint pathPoint) { try { // TODO: 实现Navisworks 2017兼容的3D标记绘制 // 当前版本使用日志记录代替实际绘制 var colorName = GetPointColorName(pathPoint.Type); WriteLog($"[3D标记] {pathPoint.Name} ({pathPoint.Type}) at ({pathPoint.Position.X:F2}, {pathPoint.Position.Y:F2}, {pathPoint.Position.Z:F2}) - {colorName}"); // 可选:使用临时颜色高亮附近的模型项来间接标记位置 HighlightNearbyItems(pathPoint.Position, GetPointColor(pathPoint.Type)); } catch (Exception ex) { WriteLog($"绘制3D路径点失败: {ex.Message}"); } } /// /// 获取点类型对应的颜色名称 /// /// 路径点类型 /// 颜色名称 private string GetPointColorName(PathPointType pointType) { switch (pointType) { case PathPointType.StartPoint: return "绿色"; case PathPointType.EndPoint: return "红色"; default: return "蓝色"; } } /// /// 获取点类型对应的Navisworks颜色 /// /// 路径点类型 /// Navisworks颜色 private Color GetPointColor(PathPointType pointType) { switch (pointType) { case PathPointType.StartPoint: return Color.Green; case PathPointType.EndPoint: return Color.Red; default: return Color.Blue; } } /// /// 高亮显示路径点附近的模型项 /// /// 位置 /// 高亮颜色 private void HighlightNearbyItems(Point3D position, Color color) { try { // 简化实现:在路径点位置附近查找模型项并高亮 // 这是一个间接的方式来标记路径点位置 foreach (var channel in _selectedChannels) { var boundingBox = channel.BoundingBox(); if (boundingBox != null && IsPointInBoundingBox(position, boundingBox)) { var itemCollection = new ModelItemCollection(); itemCollection.Add(channel); Application.ActiveDocument.Models.OverrideTemporaryColor(itemCollection, color); break; // 只高亮第一个匹配的通道 } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"高亮附近项目失败: {ex.Message}"); } } /// /// 检查点是否在包围盒内 /// /// 点位置 /// 包围盒 /// 是否在包围盒内 private bool IsPointInBoundingBox(Point3D point, BoundingBox3D boundingBox) { return point.X >= boundingBox.Min.X && point.X <= boundingBox.Max.X && point.Y >= boundingBox.Min.Y && point.Y <= boundingBox.Max.Y && point.Z >= boundingBox.Min.Z && point.Z <= boundingBox.Max.Z; } /// /// 获取路径点类型的中文名称 /// /// 路径点类型 /// 中文名称 private string GetPointTypeName(PathPointType pointType) { switch (pointType) { case PathPointType.StartPoint: return "起点"; case PathPointType.EndPoint: return "终点"; case PathPointType.WayPoint: return "路径点"; default: return "路径点"; } } /// /// 清除所有3D路径标记 /// public void Clear3DPathMarkers() { try { // 清除所有临时材质和颜色覆盖 Application.ActiveDocument.Models.ResetAllTemporaryMaterials(); // 刷新视图 Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All); OnStatusChanged("已清除所有3D路径标记"); } catch (Exception ex) { OnErrorOccurred($"清除3D路径标记失败: {ex.Message}"); } } #endregion #region 3D点击监听功能 // 3D点击监听相关 private System.Windows.Forms.Timer _clickListenerTimer; private static PathPlanningManager _activePathManager; // 静态引用,用于处理全局点击事件 /// /// 启动3D点击监听 /// private void Start3DClickListener() { try { WriteLog($"[监听] 启动3D点击监听"); // 设置当前实例为活动管理器 _activePathManager = this; // 创建定时器来定期检查鼠标点击 _clickListenerTimer = new System.Windows.Forms.Timer(); _clickListenerTimer.Interval = 100; // 100ms检查一次 _clickListenerTimer.Tick += ClickListenerTimer_Tick; _clickListenerTimer.Start(); WriteLog($"[监听] 定时器已启动,间隔: {_clickListenerTimer.Interval}ms"); OnStatusChanged("3D点击监听已启动,请在高亮的通道上点击设置路径点"); } catch (Exception ex) { WriteLog($"[监听] 启动失败: {ex.Message}"); OnErrorOccurred($"启动3D点击监听失败: {ex.Message}"); } } /// /// 停止3D点击监听 /// private void Stop3DClickListener() { try { if (_clickListenerTimer != null) { _clickListenerTimer.Stop(); _clickListenerTimer.Dispose(); _clickListenerTimer = null; } // 清除活动管理器引用 if (_activePathManager == this) { _activePathManager = null; } OnStatusChanged("3D点击监听已停止"); } catch (Exception ex) { OnErrorOccurred($"停止3D点击监听失败: {ex.Message}"); } } /// /// 定时器事件处理器 - 检查鼠标点击 /// private void ClickListenerTimer_Tick(object sender, EventArgs e) { try { // 检查当前是否有选择的模型项(通过鼠标点击产生) var currentSelection = Application.ActiveDocument.CurrentSelection.SelectedItems; if (currentSelection.Count > 0) { WriteLog($"[点击监听] 检测到选择项: {currentSelection.Count} 个"); // 获取第一个选中的项目 ModelItem selectedItem = currentSelection.First(); WriteLog($"[点击监听] 选中项: {selectedItem.DisplayName}"); // 检查是否点击在选定的通道内 if (IsItemInSelectedChannels(selectedItem)) { WriteLog($"[点击监听] 点击在通道内,准备添加路径点"); // 获取点击位置(使用选中项的包围盒中心作为近似位置) var boundingBox = selectedItem.BoundingBox(); if (boundingBox != null) { var clickPosition = new Point3D( (boundingBox.Min.X + boundingBox.Max.X) / 2, (boundingBox.Min.Y + boundingBox.Max.Y) / 2, (boundingBox.Min.Z + boundingBox.Max.Z) / 2 ); WriteLog($"[点击监听] 点击位置: ({clickPosition.X:F2}, {clickPosition.Y:F2}, {clickPosition.Z:F2})"); // 在添加点之前确定正确的点类型 PathPointType pointTypeToUse; if (_currentRoute == null || _currentRoute.Points.Count == 0) { pointTypeToUse = PathPointType.StartPoint; // 第一个点 } else { pointTypeToUse = PathPointType.WayPoint; // 后续点都是路径点 } // 添加路径点 var pathPoint = AddPathPointIn3D(clickPosition, pointTypeToUse); if (pathPoint != null) { WriteLog($"[点击监听] 成功添加路径点: {pathPoint.Name}"); // 清除当前选择,为下次点击做准备 Application.ActiveDocument.CurrentSelection.Clear(); // 更新当前点类型状态 _currentPointType = pointTypeToUse; } else { WriteLog($"[点击监听] 添加路径点失败"); } } else { WriteLog($"[点击监听] 无法获取选中项的包围盒"); } } else { WriteLog($"[点击监听] 点击不在通道内,清除选择"); // 点击了非通道区域,清除选择并提示 Application.ActiveDocument.CurrentSelection.Clear(); OnStatusChanged("请点击高亮的通道区域设置路径点"); } } } catch (Exception ex) { WriteLog($"[点击监听] 监听错误: {ex.Message}"); } } /// /// 检查选中的项目是否在选定的通道中 /// /// 选中的模型项 /// 是否在通道中 private bool IsItemInSelectedChannels(ModelItem item) { return _selectedChannels.Contains(item) || IsItemChildOfSelectedChannels(item); } /// /// 检查项目是否为选定通道的子项 /// /// 要检查的项目 /// 是否为子项 private bool IsItemChildOfSelectedChannels(ModelItem item) { foreach (var channel in _selectedChannels) { if (IsChildOf(item, channel)) { return true; } } return false; } /// /// 递归检查是否为子项 /// /// 子项 /// 父项 /// 是否为子项 private bool IsChildOf(ModelItem child, ModelItem parent) { var currentParent = child.Parent; while (currentParent != null) { if (currentParent == parent) { return true; } currentParent = currentParent.Parent; } return false; } /// /// 自动切换点类型 /// private void AutoSwitchPointType() { try { if (_currentRoute == null || _currentRoute.Points == null) return; // 根据当前路径点数量自动设置点类型 int pointCount = _currentRoute.Points.Count; if (pointCount == 0) { // 第一个点设为起点 _currentPointType = PathPointType.StartPoint; } else if (pointCount == 1 && _currentPointType == PathPointType.StartPoint) { // 第二个点开始设为路径点 _currentPointType = PathPointType.WayPoint; OnStatusChanged("下一个点将设为路径点,退出编辑时最后一个点自动设为终点"); } // 其他情况保持路径点类型 } catch (Exception ex) { WriteLog($"自动切换点类型失败: {ex.Message}"); } } /// /// 静态方法:获取当前活动的路径管理器 /// /// 当前活动的路径管理器 public static PathPlanningManager GetActivePathManager() { return _activePathManager; } /// /// 手动设置当前点类型(供外部调用) /// /// 要设置的点类型 public void SetCurrentPointType(PathPointType pointType) { _currentPointType = pointType; OnStatusChanged($"已切换到{GetPointTypeName(pointType)}模式"); } #endregion #endregion } }