NavisworksTransport/PathPlanningManager.cs

2556 lines
92 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport
{
/// <summary>
/// 路径规划管理器
/// 负责路径规划的核心业务逻辑
/// </summary>
public class PathPlanningManager
{
private CategoryAttributeManager _categoryManager;
private VisibilityManager _visibilityManager;
private CoordinateConverter _coordinateConverter;
private NavigationMapWindow _mapWindow;
private List<ModelItem> _selectedChannels;
private List<PathRoute> _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");
/// <summary>
/// 是否处于路径编辑模式
/// </summary>
public bool IsPathEditMode
{
get { return _isPathEditMode || _globalIsPathEditMode; }
}
/// <summary>
/// 全局3D编辑模式状态静态属性
/// </summary>
public static bool GlobalIsPathEditMode
{
get { return _globalIsPathEditMode; }
}
/// <summary>
/// 当前设置的路径点类型
/// </summary>
public PathPointType CurrentPointType
{
get { return _currentPointType; }
set { _currentPointType = value; }
}
// 新增事件
public event EventHandler<bool> PathEditModeChanged;
public event EventHandler<PathPoint> PathPointAddedIn3D;
/// <summary>
/// 当前选中的通道集合
/// </summary>
public List<ModelItem> SelectedChannels
{
get { return _selectedChannels; }
}
/// <summary>
/// 所有路径集合
/// </summary>
public List<PathRoute> Routes
{
get { return _routes; }
}
/// <summary>
/// 当前活动路径
/// </summary>
public PathRoute CurrentRoute
{
get { return _currentRoute; }
set
{
_currentRoute = value;
if (_mapWindow != null)
{
_mapWindow.CurrentRoute = _currentRoute;
}
CurrentRouteChanged?.Invoke(this, _currentRoute);
}
}
/// <summary>
/// 组合通道边界
/// </summary>
public ChannelBounds CombinedChannelBounds
{
get { return _combinedChannelBounds; }
}
// 事件定义
public event EventHandler<List<ModelItem>> ChannelsSelected;
public event EventHandler<PathRoute> CurrentRouteChanged;
public event EventHandler<PathRoute> RouteGenerated;
public event EventHandler<string> StatusChanged;
public event EventHandler<string> ErrorOccurred;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="categoryManager">类别属性管理器</param>
/// <param name="visibilityManager">可见性管理器</param>
public PathPlanningManager(CategoryAttributeManager categoryManager, VisibilityManager visibilityManager)
{
_categoryManager = categoryManager ?? throw new ArgumentNullException(nameof(categoryManager));
_visibilityManager = visibilityManager ?? throw new ArgumentNullException(nameof(visibilityManager));
_selectedChannels = new List<ModelItem>();
_routes = new List<PathRoute>();
_currentRoute = new PathRoute("默认路径");
}
/// <summary>
/// 无参构造函数(为向后兼容而保留)
/// </summary>
public PathPlanningManager()
{
_categoryManager = new CategoryAttributeManager();
_visibilityManager = new VisibilityManager();
_selectedChannels = new List<ModelItem>();
_routes = new List<PathRoute>();
_currentRoute = new PathRoute("默认路径");
}
/// <summary>
/// 选择通道模型
/// </summary>
/// <param name="useCurrentSelection">是否使用当前选择的模型</param>
/// <returns>选择的通道数量</returns>
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;
}
}
/// <summary>
/// 通过类别筛选通道模型
/// </summary>
/// <returns>通道模型集合</returns>
private List<ModelItem> FilterChannelsByCategory()
{
var channelItems = new List<ModelItem>();
try
{
// 获取所有模型项
var allItems = new List<ModelItem>();
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;
}
/// <summary>
/// 计算组合通道边界
/// </summary>
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<BoundingBox3D>();
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}");
}
}
/// <summary>
/// 显示导航地图窗口
/// </summary>
/// <param name="mapWidth">地图宽度</param>
/// <param name="mapHeight">地图高度</param>
/// <returns>是否成功显示</returns>
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;
}
}
/// <summary>
/// 显示路径规划界面MainPlugin兼容方法
/// </summary>
/// <returns>是否成功显示</returns>
public bool ShowPathPlanningInterface()
{
return ShowNavigationMap();
}
/// <summary>
/// 添加路径到管理器MainPlugin兼容方法
/// </summary>
/// <param name="route">要添加的路径</param>
/// <returns>是否成功添加</returns>
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;
}
}
/// <summary>
/// 设置地图窗口事件处理器
/// </summary>
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;
}
}
/// <summary>
/// 创建新路径
/// </summary>
/// <param name="routeName">路径名称</param>
/// <returns>新创建的路径</returns>
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;
}
/// <summary>
/// 删除路径
/// </summary>
/// <param name="route">要删除的路径</param>
/// <returns>是否成功删除</returns>
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;
}
}
/// <summary>
/// 生成路径
/// </summary>
/// <param name="route">要生成的路径为null时使用当前路径</param>
/// <returns>是否成功生成</returns>
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;
}
}
/// <summary>
/// 计算预估时间
/// </summary>
/// <param name="route">路径</param>
private void CalculateEstimatedTime(PathRoute route)
{
// 简单的时间估算假设平均速度1米/秒
const double averageSpeed = 1.0; // 米/秒
route.EstimatedTime = route.TotalLength / averageSpeed;
}
/// <summary>
/// 隐藏非通道元素
/// </summary>
/// <returns>是否成功</returns>
public bool HideNonChannelElements()
{
try
{
if (!_selectedChannels.Any())
{
OnErrorOccurred("请先选择通道模型");
return false;
}
// 获取所有非通道元素
var allItems = new List<ModelItem>();
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;
}
}
/// <summary>
/// 显示所有元素
/// </summary>
/// <returns>是否成功</returns>
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;
}
}
/// <summary>
/// 获取路径统计信息
/// </summary>
/// <returns>统计信息字符串</returns>
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;
}
/// <summary>
/// 清理资源
/// </summary>
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);
}
/// <summary>
/// 写入调试日志
/// </summary>
/// <param name="message">日志消息</param>
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
/// <summary>
/// 获取所有通道选择结果
/// </summary>
/// <returns>通道选择结果</returns>
public ChannelSelectionResult GetAllChannelSelectionResults()
{
var result = new ChannelSelectionResult();
try
{
// 获取所有模型项
var allItems = new List<ModelItem>();
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;
}
/// <summary>
/// 自动检测可能的通道
/// </summary>
/// <param name="items">模型项数组</param>
/// <returns>自动检测的通道集合</returns>
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;
}
/// <summary>
/// 判断模型项是否可能是通道
/// </summary>
/// <param name="item">模型项</param>
/// <returns>是否可能是通道</returns>
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;
}
/// <summary>
/// 筛选已标记物流属性的模型项
/// </summary>
/// <param name="items">模型项数组</param>
/// <returns>已标记的物流模型项集合</returns>
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;
}
/// <summary>
/// 筛选可通行区域
/// </summary>
/// <param name="items">模型项集合</param>
/// <returns>可通行区域集合</returns>
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;
}
/// <summary>
/// 筛选通道类型的模型项
/// </summary>
/// <param name="items">模型项集合</param>
/// <returns>通道类型模型项集合</returns>
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;
}
/// <summary>
/// 根据车辆尺寸筛选适用通道
/// </summary>
/// <param name="vehicleSize">车辆尺寸</param>
/// <returns>适用的通道集合</returns>
public ModelItemCollection FilterChannelsByVehicleSize(string vehicleSize)
{
var applicableChannels = new ModelItemCollection();
try
{
// 获取所有已标记的物流项
var allItems = new List<ModelItem>();
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;
}
/// <summary>
/// 手动选择通道
/// </summary>
/// <returns>手动选择的通道集合</returns>
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;
}
/// <summary>
/// 设置选中的通道为活动通道
/// </summary>
/// <param name="channels">通道集合</param>
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}");
}
}
/// <summary>
/// 高亮显示通道
/// </summary>
/// <param name="channels">要高亮的通道集合</param>
/// <param name="color">高亮颜色</param>
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}");
}
}
/// <summary>
/// 清除通道高亮
/// </summary>
public void ClearChannelHighlight()
{
try
{
// 重置所有临时材质
Application.ActiveDocument.Models.ResetAllTemporaryMaterials();
// 刷新视图
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"清除通道高亮失败: {ex.Message}");
}
}
/// <summary>
/// 显示通道选择对话框
/// </summary>
/// <returns>用户选择结果</returns>
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;
}
/// <summary>
/// 获取通道统计信息
/// </summary>
/// <returns>通道统计信息</returns>
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
/// <summary>
/// 验证路径有效性
/// </summary>
/// <param name="route">要验证的路径</param>
/// <returns>验证结果</returns>
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;
}
/// <summary>
/// 验证基本路径结构
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">验证结果</param>
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}");
}
}
}
/// <summary>
/// 验证路径几何有效性
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">验证结果</param>
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),建议分段处理");
}
}
/// <summary>
/// 验证通道约束
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">验证结果</param>
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}' 不在选定的通道范围内");
}
}
}
/// <summary>
/// 验证路径碰撞
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">验证结果</param>
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}");
}
}
/// <summary>
/// 验证路径可达性
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">验证结果</param>
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}%)");
}
}
}
}
/// <summary>
/// 优化路径
/// </summary>
/// <param name="route">要优化的路径</param>
/// <param name="optimizationOptions">优化选项</param>
/// <returns>优化结果</returns>
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;
}
/// <summary>
/// 移除重复点
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">优化结果</param>
private void RemoveDuplicatePoints(PathRoute route, PathOptimizationResult result)
{
var points = route.GetSortedPoints();
var pointsToRemove = new List<PathPoint>();
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);
}
}
/// <summary>
/// 平滑路径
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">优化结果</param>
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}");
}
}
/// <summary>
/// 优化路径角度
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">优化结果</param>
private void OptimizePathAngles(PathRoute route, PathOptimizationResult result)
{
var points = route.GetSortedPoints();
if (points.Count < 3) return;
var pointsToRemove = new List<PathPoint>();
// 检查是否有几乎共线的三个点
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);
}
}
/// <summary>
/// 调整路径到通道中心
/// </summary>
/// <param name="route">路径</param>
/// <param name="result">优化结果</param>
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
/// <summary>
/// 递归收集所有ModelItem
/// </summary>
/// <param name="item">当前ModelItem</param>
/// <param name="collection">收集列表</param>
private void CollectAllItems(ModelItem item, List<ModelItem> 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);
}
}
}
/// <summary>
/// 计算两个3D点之间的距离
/// </summary>
/// <param name="point1">第一个点</param>
/// <param name="point2">第二个点</param>
/// <returns>距离</returns>
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);
}
/// <summary>
/// 检查点是否在通道内
/// </summary>
/// <param name="point">点位置</param>
/// <param name="channel">通道模型项</param>
/// <returns>是否在通道内</returns>
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;
}
}
/// <summary>
/// 获取障碍物集合
/// </summary>
/// <returns>障碍物集合</returns>
private ModelItemCollection GetObstacles()
{
var obstacles = new ModelItemCollection();
try
{
// 获取所有标记为障碍物的模型项
var allItems = new List<ModelItem>();
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;
}
/// <summary>
/// 检查点是否与障碍物碰撞
/// </summary>
/// <param name="point">点位置</param>
/// <param name="obstacles">障碍物集合</param>
/// <returns>是否碰撞</returns>
private bool IsPointCollidingWithObstacles(Point3D point, ModelItemCollection obstacles)
{
foreach (ModelItem obstacle in obstacles)
{
if (IsPointInChannel(point, obstacle))
{
return true;
}
}
return false;
}
/// <summary>
/// 检查路径段是否与障碍物碰撞
/// </summary>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="obstacles">障碍物集合</param>
/// <returns>是否碰撞</returns>
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;
}
/// <summary>
/// 计算三点间的角度
/// </summary>
/// <param name="p1">第一个点</param>
/// <param name="p2">中间点</param>
/// <param name="p3">第三个点</param>
/// <returns>角度(弧度)</returns>
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);
}
/// <summary>
/// 获取在通道中的最优位置
/// </summary>
/// <param name="originalPosition">原始位置</param>
/// <returns>最优位置</returns>
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;
}
/// <summary>
/// 获取Navisworks文档单位并转换为米的系数
/// </summary>
/// <returns>转换系数(文档单位转换为米)</returns>
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;
}
}
/// <summary>
/// 转换包围盒到米单位
/// </summary>
/// <param name="boundingBox">原始包围盒</param>
/// <param name="conversionFactor">转换系数</param>
/// <returns>转换后的包围盒(米单位)</returns>
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
/// <summary>
/// 高亮显示选中的通道
/// </summary>
/// <param name="highlightColor">高亮颜色为null时使用默认颜色</param>
/// <returns>是否成功高亮</returns>
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;
}
}
/// <summary>
/// 清除通道高亮显示
/// </summary>
/// <returns>是否成功清除</returns>
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;
}
}
/// <summary>
/// 进入3D路径编辑模式
/// </summary>
/// <returns>是否成功进入编辑模式</returns>
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;
}
}
/// <summary>
/// 退出3D路径编辑模式
/// </summary>
/// <returns>是否成功退出编辑模式</returns>
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;
}
}
/// <summary>
/// 在3D视图中添加路径点
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <param name="pointType">路径点类型为null时使用当前类型</param>
/// <returns>添加的路径点失败时返回null</returns>
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;
}
}
/// <summary>
/// 检查点是否在选中的通道内
/// </summary>
/// <param name="worldPoint">世界坐标点</param>
/// <returns>是否在通道内</returns>
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; // 如果检查失败,允许设置点(宽容处理)
}
}
/// <summary>
/// 生成路径点名称
/// </summary>
/// <param name="pointType">点类型</param>
/// <returns>生成的名称</returns>
private string GeneratePointName(PathPointType pointType)
{
var currentPoints = _currentRoute?.Points ?? new List<PathPoint>();
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}";
}
}
/// <summary>
/// 在3D视图中绘制路径点标记简化版本
/// </summary>
/// <param name="pathPoint">路径点</param>
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}");
}
}
/// <summary>
/// 获取点类型对应的颜色名称
/// </summary>
/// <param name="pointType">路径点类型</param>
/// <returns>颜色名称</returns>
private string GetPointColorName(PathPointType pointType)
{
switch (pointType)
{
case PathPointType.StartPoint:
return "绿色";
case PathPointType.EndPoint:
return "红色";
default:
return "蓝色";
}
}
/// <summary>
/// 获取点类型对应的Navisworks颜色
/// </summary>
/// <param name="pointType">路径点类型</param>
/// <returns>Navisworks颜色</returns>
private Color GetPointColor(PathPointType pointType)
{
switch (pointType)
{
case PathPointType.StartPoint:
return Color.Green;
case PathPointType.EndPoint:
return Color.Red;
default:
return Color.Blue;
}
}
/// <summary>
/// 高亮显示路径点附近的模型项
/// </summary>
/// <param name="position">位置</param>
/// <param name="color">高亮颜色</param>
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}");
}
}
/// <summary>
/// 检查点是否在包围盒内
/// </summary>
/// <param name="point">点位置</param>
/// <param name="boundingBox">包围盒</param>
/// <returns>是否在包围盒内</returns>
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;
}
/// <summary>
/// 获取路径点类型的中文名称
/// </summary>
/// <param name="pointType">路径点类型</param>
/// <returns>中文名称</returns>
private string GetPointTypeName(PathPointType pointType)
{
switch (pointType)
{
case PathPointType.StartPoint:
return "起点";
case PathPointType.EndPoint:
return "终点";
case PathPointType.WayPoint:
return "路径点";
default:
return "路径点";
}
}
/// <summary>
/// 清除所有3D路径标记
/// </summary>
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; // 静态引用,用于处理全局点击事件
/// <summary>
/// 启动3D点击监听
/// </summary>
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}");
}
}
/// <summary>
/// 停止3D点击监听
/// </summary>
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}");
}
}
/// <summary>
/// 定时器事件处理器 - 检查鼠标点击
/// </summary>
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}");
}
}
/// <summary>
/// 检查选中的项目是否在选定的通道中
/// </summary>
/// <param name="item">选中的模型项</param>
/// <returns>是否在通道中</returns>
private bool IsItemInSelectedChannels(ModelItem item)
{
return _selectedChannels.Contains(item) || IsItemChildOfSelectedChannels(item);
}
/// <summary>
/// 检查项目是否为选定通道的子项
/// </summary>
/// <param name="item">要检查的项目</param>
/// <returns>是否为子项</returns>
private bool IsItemChildOfSelectedChannels(ModelItem item)
{
foreach (var channel in _selectedChannels)
{
if (IsChildOf(item, channel))
{
return true;
}
}
return false;
}
/// <summary>
/// 递归检查是否为子项
/// </summary>
/// <param name="child">子项</param>
/// <param name="parent">父项</param>
/// <returns>是否为子项</returns>
private bool IsChildOf(ModelItem child, ModelItem parent)
{
var currentParent = child.Parent;
while (currentParent != null)
{
if (currentParent == parent)
{
return true;
}
currentParent = currentParent.Parent;
}
return false;
}
/// <summary>
/// 自动切换点类型
/// </summary>
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}");
}
}
/// <summary>
/// 静态方法:获取当前活动的路径管理器
/// </summary>
/// <returns>当前活动的路径管理器</returns>
public static PathPlanningManager GetActivePathManager()
{
return _activePathManager;
}
/// <summary>
/// 手动设置当前点类型(供外部调用)
/// </summary>
/// <param name="pointType">要设置的点类型</param>
public void SetCurrentPointType(PathPointType pointType)
{
_currentPointType = pointType;
OnStatusChanged($"已切换到{GetPointTypeName(pointType)}模式");
}
#endregion
#endregion
}
}