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
}
}