NavisworksTransport/PathPlanningManager.cs

3847 lines
146 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 Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
namespace NavisworksTransport
{
/// <summary>
/// 路径规划管理器
/// 负责路径规划的核心业务逻辑
/// </summary>
public class PathPlanningManager
{
private CategoryAttributeManager _categoryManager;
private VisibilityManager _visibilityManager;
private CoordinateConverter _coordinateConverter;
private PathVisualizer _pathVisualizer;
private PathPointRenderPlugin _renderPlugin;
private List<ModelItem> _selectedChannels;
private List<PathRoute> _routes;
private PathRoute _currentRoute;
private ChannelBounds _combinedChannelBounds;
// 新增:路径编辑状态管理
private PathEditState _pathEditState = PathEditState.Viewing;
private PathPointType _currentPointType = PathPointType.WayPoint;
private PathRoute _editingRoute = null; // 当前正在编辑的路径
private PathHistoryManager _historyManager; // 路径历史记录管理器
// 日志管理已统一到LogManager类
/// <summary>
/// 当前路径编辑状态
/// </summary>
public PathEditState PathEditState
{
get { return _pathEditState; }
private set
{
_pathEditState = value;
PathEditStateChanged?.Invoke(this, _pathEditState);
}
}
/// <summary>
/// 当前正在编辑的路径
/// </summary>
public PathRoute EditingRoute
{
get { return _editingRoute; }
private set { _editingRoute = value; }
}
/// <summary>
/// 是否处于可编辑状态(新建或编辑模式)
/// </summary>
public bool IsInEditableState
{
get { return _pathEditState == PathEditState.Creating || _pathEditState == PathEditState.Editing; }
}
/// <summary>
/// 切换到查看状态
/// </summary>
public void SwitchToViewingState()
{
PathEditState = PathEditState.Viewing;
EditingRoute = null;
// 智能管理ToolPlugin状态
ManageToolPluginForEditState();
// 清理高亮
ClearChannelHighlight();
LogManager.Info("已切换到查看状态");
}
/// <summary>
/// 切换到编辑状态
/// </summary>
/// <param name="route">要编辑的路径</param>
public void SwitchToEditingState(PathRoute route)
{
if (route == null)
{
throw new ArgumentNullException(nameof(route));
}
PathEditState = PathEditState.Editing;
EditingRoute = route;
CurrentRoute = route;
// 智能管理ToolPlugin状态
ManageToolPluginForEditState();
// 高亮通道
HighlightLogisticsChannels();
LogManager.Info($"已切换到编辑状态,正在编辑路径: {route.Name}");
}
/// <summary>
/// 完成当前编辑并保存
/// </summary>
/// <returns>是否成功完成编辑</returns>
public bool FinishEditing()
{
if (!IsInEditableState)
{
LogManager.Warning("当前不在编辑状态,无法完成编辑");
return false;
}
try
{
// 自动设置最后一个点为终点
if (CurrentRoute != null && CurrentRoute.Points.Count > 1)
{
var lastPoint = CurrentRoute.Points.Last();
if (lastPoint.Type != PathPointType.StartPoint) // 起点不能是终点
{
lastPoint.Type = PathPointType.EndPoint;
LogManager.Info($"已自动设置最后一个点为终点: {lastPoint.Name}");
// 更新3D标记的外观
UpdateMarkerAppearance(lastPoint);
}
}
// 如果是创建模式,将当前路径添加到路径集合
if (_pathEditState == PathEditState.Creating && CurrentRoute != null)
{
if (!_routes.Contains(CurrentRoute))
{
_routes.Add(CurrentRoute);
LogManager.Info($"新路径已添加到路径集合: {CurrentRoute.Name}");
// 添加历史记录
var historyEntry = new PathHistoryEntry(
CurrentRoute.Id,
PathHistoryOperationType.Created,
CurrentRoute,
$"创建新路径: {CurrentRoute.Name}");
_historyManager.AddHistoryEntry(historyEntry);
}
}
// 如果是编辑模式,保存编辑历史
if (_pathEditState == PathEditState.Editing && EditingRoute != null)
{
var historyEntry = new PathHistoryEntry(
EditingRoute.Id,
PathHistoryOperationType.Edited,
EditingRoute,
$"编辑路径: {EditingRoute.Name}");
_historyManager.AddHistoryEntry(historyEntry);
}
// 切换回查看状态
SwitchToViewingState();
// 触发路径点列表更新事件,以刷新终点显示
PathPointsListUpdated?.Invoke(this, CurrentRoute);
// 触发路径生成事件
RouteGenerated?.Invoke(this, CurrentRoute);
LogManager.Info("路径编辑已完成");
return true;
}
catch (Exception ex)
{
LogManager.Error($"完成编辑时发生错误: {ex.Message}");
OnErrorOccurred($"完成编辑失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 取消当前编辑
/// </summary>
/// <returns>是否成功取消编辑</returns>
public bool CancelEditing()
{
if (!IsInEditableState)
{
LogManager.Warning("当前不在编辑状态,无法取消编辑");
return false;
}
try
{
// 如果是创建模式,清理当前路径
if (_pathEditState == PathEditState.Creating && CurrentRoute != null)
{
CurrentRoute.Points.Clear();
Clear3DPathMarkers();
LogManager.Info("已清理新建路径的临时数据");
}
// 如果是编辑模式,恢复原始数据(这里需要实现备份恢复机制)
if (_pathEditState == PathEditState.Editing && EditingRoute != null)
{
// TODO: 实现路径数据恢复机制
LogManager.Info("已取消路径编辑(数据恢复功能待实现)");
}
// 切换回查看状态
SwitchToViewingState();
LogManager.Info("路径编辑已取消");
return true;
}
catch (Exception ex)
{
LogManager.Error($"取消编辑时发生错误: {ex.Message}");
OnErrorOccurred($"取消编辑失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 重置路径编辑状态(用于异常恢复)
/// </summary>
public void ResetPathEditState()
{
try
{
PathEditState = PathEditState.Viewing;
EditingRoute = null;
// 智能管理ToolPlugin状态强制停用
ManageToolPluginForEditState();
Clear3DPathMarkers();
ClearChannelHighlight();
LogManager.Info("路径编辑状态已重置");
}
catch (Exception ex)
{
LogManager.Error($"重置路径编辑状态时发生错误: {ex.Message}");
}
}
/// <summary>
/// 手动保存当前路径到历史记录
/// </summary>
/// <param name="description">保存描述</param>
/// <returns>是否保存成功</returns>
public bool SaveCurrentRouteToHistory(string description = "手动保存")
{
try
{
if (CurrentRoute == null)
{
LogManager.Warning("当前没有路径可保存");
return false;
}
var historyEntry = new PathHistoryEntry(
CurrentRoute.Id,
PathHistoryOperationType.ManualSave,
CurrentRoute,
description);
_historyManager.AddHistoryEntry(historyEntry);
LogManager.Info($"路径已保存到历史记录: {CurrentRoute.Name}");
return true;
}
catch (Exception ex)
{
LogManager.Error($"保存路径到历史记录时发生错误: {ex.Message}");
OnErrorOccurred($"保存历史记录失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 当前设置的路径点类型
/// </summary>
public PathPointType CurrentPointType
{
get { return _currentPointType; }
set { _currentPointType = value; }
}
// 新增事件
public event EventHandler<PathEditState> PathEditStateChanged;
public event EventHandler<PathPoint> PathPointAddedIn3D;
public event EventHandler<PathPoint> PathPointRemovedFrom3D;
public event EventHandler<PathRoute> PathPointsListUpdated;
/// <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;
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));
_pathVisualizer = new PathVisualizer();
// 设置静态引用,确保所有地方都使用同一个实例
_activePathManager = this;
LogManager.WriteLog("[路径管理] 设置_activePathManager静态引用");
// 获取已注册的圆形渲染插件实例
try
{
// 等待RenderPlugin自动注册然后获取静态实例
_renderPlugin = PathPointRenderPlugin.Instance;
if (_renderPlugin != null)
{
LogManager.WriteLog("[路径管理] PathPointRenderPlugin实例获取成功");
}
else
{
LogManager.WriteLog("[路径管理] PathPointRenderPlugin实例尚未就绪将在后续尝试获取");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径管理] PathPointRenderPlugin实例获取失败: {ex.Message}");
_renderPlugin = null;
}
_selectedChannels = new List<ModelItem>();
_routes = new List<PathRoute>();
_currentRoute = new PathRoute("默认路径");
_historyManager = new PathHistoryManager(50); // 最多保存50个历史记录
}
/// <summary>
/// 无参构造函数(为向后兼容而保留)
/// </summary>
public PathPlanningManager()
{
_categoryManager = new CategoryAttributeManager();
_visibilityManager = new VisibilityManager();
_pathVisualizer = new PathVisualizer();
// 设置静态引用,确保所有地方都使用同一个实例
_activePathManager = this;
LogManager.WriteLog("[路径管理] 设置_activePathManager静态引用无参构造");
// 获取已注册的圆形渲染插件实例
try
{
// 等待RenderPlugin自动注册然后获取静态实例
_renderPlugin = PathPointRenderPlugin.Instance;
if (_renderPlugin != null)
{
LogManager.WriteLog("[路径管理] PathPointRenderPlugin实例获取成功无参构造");
}
else
{
LogManager.WriteLog("[路径管理] PathPointRenderPlugin实例尚未就绪无参构造");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径管理] PathPointRenderPlugin实例获取失败无参构造: {ex.Message}");
_renderPlugin = null;
}
_selectedChannels = new List<ModelItem>();
_routes = new List<PathRoute>();
_currentRoute = new PathRoute("默认路径");
_historyManager = new PathHistoryManager(50); // 最多保存50个历史记录
}
/// <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 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>
/// 添加路径到管理器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>
/// <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
{
// 清理圆形渲染插件
if (_renderPlugin != null)
{
_renderPlugin.CleanUp();
_renderPlugin = null;
LogManager.WriteLog("[路径管理] PathPointRenderPlugin已清理");
}
// 停用ToolPlugin
if (_isToolPluginActive)
{
DeactivateToolPlugin();
}
}
catch
{
// 忽略清理错误
}
}
#region
private void OnStatusChanged(string status)
{
StatusChanged?.Invoke(this, status);
}
private void OnErrorOccurred(string error)
{
ErrorOccurred?.Invoke(this, error);
}
// 日志方法已移动到LogManager类
#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}";
// 添加历史记录
if (route != null)
{
var historyEntry = new PathHistoryEntry(
route.Id,
PathHistoryOperationType.Optimized,
route,
$"路径优化: 长度减少{result.LengthReduction:F3}m点数减少{result.PointReduction}");
_historyManager.AddHistoryEntry(historyEntry);
}
}
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>
/// <summary>
/// 检查ModelItem是否有效且未被释放
/// </summary>
/// <param name="item">要检查的ModelItem</param>
/// <returns>是否有效</returns>
private bool IsModelItemValid(ModelItem item)
{
try
{
if (item == null) return false;
// 尝试访问基本属性来检查对象是否有效
var name = item.DisplayName;
var hasGeometry = item.HasGeometry;
return true;
}
catch (ObjectDisposedException)
{
return false;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 检查Application和Document是否有效且未被释放
/// </summary>
/// <returns>是否有效</returns>
private bool IsApplicationDocumentValid()
{
try
{
if (Application.ActiveDocument == null) return false;
if (Application.ActiveDocument.CurrentSelection == null) return false;
// 尝试访问基本属性来检查对象是否有效
var fileName = Application.ActiveDocument.FileName;
var selectionCount = Application.ActiveDocument.CurrentSelection.SelectedItems.Count;
return true;
}
catch (ObjectDisposedException)
{
return false;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 安全地清除当前选择
/// </summary>
/// <returns>是否成功清除</returns>
private bool SafelyClearSelection()
{
try
{
LogManager.WriteLog("[SafelyClearSelection] 方法开始");
LogManager.WriteLog("[SafelyClearSelection] 开始检查Application状态");
if (!IsApplicationDocumentValid())
{
LogManager.WriteLog("[选择清除] Application或Document对象无效跳过清除");
LogManager.WriteLog("[SafelyClearSelection] 方法结束(状态无效)");
return false;
}
LogManager.WriteLog("[SafelyClearSelection] Application状态检查通过");
LogManager.WriteLog("[SafelyClearSelection] 开始调用Application.ActiveDocument.CurrentSelection.Clear()");
Application.ActiveDocument.CurrentSelection.Clear();
LogManager.WriteLog("[SafelyClearSelection] Clear()调用完成");
LogManager.WriteLog("[选择清除] 成功清除选择");
LogManager.WriteLog("[SafelyClearSelection] 方法结束(成功)");
return true;
}
catch (ObjectDisposedException ex)
{
LogManager.WriteLog($"[选择清除] 对象已释放: {ex.Message}");
LogManager.WriteLog($"[SafelyClearSelection] ObjectDisposedException堆栈: {ex.StackTrace}");
LogManager.WriteLog("[SafelyClearSelection] 方法结束(对象已释放异常)");
return false;
}
catch (Exception ex)
{
LogManager.WriteLog($"[选择清除] 清除失败: {ex.Message}");
LogManager.WriteLog($"[选择清除] 异常类型: {ex.GetType().Name}");
LogManager.WriteLog($"[SafelyClearSelection] 异常堆栈: {ex.StackTrace}");
LogManager.WriteLog("[SafelyClearSelection] 方法结束(异常)");
return false;
}
}
public bool HighlightSelectedChannels(System.Drawing.Color? highlightColor = null)
{
try
{
if (_selectedChannels == null || _selectedChannels.Count == 0)
{
LogManager.WriteLog("[高亮] 没有选择任何通道");
OnErrorOccurred("没有选择任何通道,请先选择通道");
return false;
}
LogManager.WriteLog($"[高亮] 开始高亮 {_selectedChannels.Count} 个通道");
// 过滤出有效的通道
var validChannels = _selectedChannels.Where(IsModelItemValid).ToList();
if (validChannels.Count == 0)
{
LogManager.WriteLog("[高亮] 没有有效的通道对象可以高亮");
OnErrorOccurred("选中的通道对象已失效,请重新选择通道");
return false;
}
LogManager.WriteLog($"[高亮] 有效通道数量: {validChannels.Count}/{_selectedChannels.Count}");
// 使用明显的高亮颜色 - 鲜艳的绿色
var color = highlightColor ?? System.Drawing.Color.LimeGreen;
// 转换为Navisworks颜色
var navisColor = new Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f);
LogManager.WriteLog($"[高亮] 使用颜色: R={navisColor.R:F3}, G={navisColor.G:F3}, B={navisColor.B:F3}");
// 创建ModelItemCollection
var itemsToHighlight = new ModelItemCollection();
int addedCount = 0;
foreach (var channel in validChannels)
{
try
{
itemsToHighlight.Add(channel);
addedCount++;
LogManager.WriteLog($"[高亮] 添加通道 {addedCount}: {channel.DisplayName}");
}
catch (Exception ex)
{
LogManager.WriteLog($"[高亮] 添加通道失败: {ex.Message}");
continue;
}
}
if (addedCount == 0)
{
LogManager.WriteLog("[高亮] 没有成功添加的通道可以高亮");
return false;
}
// 先清除之前的高亮
Application.ActiveDocument.Models.ResetAllTemporaryMaterials();
LogManager.WriteLog("[高亮] 已清除之前的高亮");
// 应用临时颜色覆盖
Application.ActiveDocument.Models.OverrideTemporaryColor(itemsToHighlight, navisColor);
LogManager.WriteLog($"[高亮] 已应用颜色覆盖到 {addedCount} 个通道");
// 强制刷新视图
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
LogManager.WriteLog("[高亮] 已请求视图刷新");
OnStatusChanged($"已高亮显示 {addedCount} 个通道");
LogManager.WriteLog($"[高亮] 高亮完成,成功处理 {addedCount} 个通道");
return true;
}
catch (Exception ex)
{
LogManager.WriteLog($"[高亮] 高亮失败: {ex.Message}");
LogManager.WriteLog($"[高亮] 异常堆栈: {ex.StackTrace}");
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>
/// <summary>
/// 在3D视图中添加路径点
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <param name="pointType">路径点类型为null时使用当前类型</param>
/// <returns>添加的路径点失败时返回null</returns>
/// <summary>
/// 在3D视图中添加路径点增强版支持智能通道检测
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <param name="pointType">路径点类型为null时使用当前类型</param>
/// <returns>添加的路径点失败时返回null</returns>
public PathPoint AddPathPointIn3D(Point3D worldPoint, PathPointType? pointType = null)
{
// 确保在编辑状态下才能添加点
if (!IsInEditableState)
{
LogManager.Warning("不在编辑状态,无法添加路径点");
OnStatusChanged("请先进入新建或编辑模式");
return null;
}
// 如果没有当前路径,则无法添加
if (CurrentRoute == null)
{
LogManager.Error("当前路径(CurrentRoute)为null无法添加路径点");
OnErrorOccurred("内部错误:当前路径丢失");
return null;
}
try
{
// 确定路径点类型
PathPointType finalPointType;
if (pointType.HasValue)
{
finalPointType = pointType.Value;
}
else
{
// 自动判断类型:第一个点为起点,其余为路径点
finalPointType = (CurrentRoute.Points.Count == 0)
? PathPointType.StartPoint
: PathPointType.WayPoint;
}
// 创建路径点
var pathPoint = new PathPoint
{
Name = GeneratePointName(finalPointType),
Position = worldPoint,
Type = finalPointType
};
CurrentRoute.Points.Add(pathPoint);
LogManager.Info($"路径点已添加: {pathPoint.Name}, 位置: ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2})");
// 在3D视图中绘制标记
Draw3DPathPoint(pathPoint);
// 触发路径点添加事件
PathPointAddedIn3D?.Invoke(this, pathPoint);
// 触发路径列表更新事件
PathPointsListUpdated?.Invoke(this, CurrentRoute);
return pathPoint;
}
catch (Exception ex)
{
LogManager.Error($"添加3D路径点失败: {ex.Message}");
OnErrorOccurred($"添加路径点失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 执行智能通道检测
/// </summary>
/// <param name="worldPoint">检测点的世界坐标</param>
/// <returns>检测结果</returns>
private ChannelDetectionResult PerformIntelligentChannelDetection(Point3D worldPoint)
{
try
{
LogManager.WriteLog($"[智能检测] 开始智能通道检测");
// 1. 如果有预选通道,验证点是否在预选通道内
if (_selectedChannels != null && _selectedChannels.Count > 0)
{
LogManager.WriteLog($"[智能检测] 使用预选通道模式,通道数量: {_selectedChannels.Count}");
if (IsPointInSelectedChannels(worldPoint))
{
return new ChannelDetectionResult
{
IsValidLocation = true,
Message = "点击位置在预选通道内,可设置路径点",
DetectionMethod = "预选通道验证"
};
}
else
{
return new ChannelDetectionResult
{
IsValidLocation = false,
Message = "点击位置不在预选通道内,请在高亮的通道区域内点击",
DetectionMethod = "预选通道验证"
};
}
}
// 2. 自动检测点击位置的物流属性
LogManager.WriteLog($"[智能检测] 启用自动检测模式");
var autoDetectionResult = AutoDetectLogisticsChannel(worldPoint);
if (autoDetectionResult.IsValidChannel)
{
LogManager.WriteLog($"[智能检测] 自动检测到有效物流通道: {autoDetectionResult.ChannelType}");
// 如果检测到有效通道,自动设为选中通道
if (autoDetectionResult.DetectedChannel != null)
{
_selectedChannels = new List<ModelItem> { autoDetectionResult.DetectedChannel };
HighlightLogisticsChannels();
LogManager.WriteLog($"[智能检测] 已自动选中检测到的通道");
}
return new ChannelDetectionResult
{
IsValidLocation = true,
Message = $"检测到{autoDetectionResult.ChannelType},可设置路径点",
DetectionMethod = "自动检测"
};
}
else
{
return new ChannelDetectionResult
{
IsValidLocation = false,
Message = autoDetectionResult.ErrorMessage ?? "点击位置不是物流通道,请在通道、走廊等可通行区域点击",
DetectionMethod = "自动检测"
};
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[智能检测] 智能检测异常: {ex.Message}");
return new ChannelDetectionResult
{
IsValidLocation = false,
Message = "智能检测失败,请手动选择通道后再设置路径点",
DetectionMethod = "异常处理"
};
}
}
/// <summary>
/// 自动检测物流通道
/// </summary>
/// <param name="worldPoint">检测点</param>
/// <returns>检测结果</returns>
private LogisticsChannelDetectionResult AutoDetectLogisticsChannel(Point3D worldPoint)
{
try
{
LogManager.WriteLog($"[自动检测] 开始分析点击位置的物流属性");
// 获取点击位置附近的所有模型项
var nearbyItems = GetNearbyModelItems(worldPoint, 2.0); // 2米范围内
LogManager.WriteLog($"[自动检测] 找到附近模型项数量: {nearbyItems.Count}");
foreach (var item in nearbyItems)
{
// 检查是否有物流属性标记
if (CategoryAttributeManager.HasLogisticsAttributes(item))
{
var typeValue = CategoryAttributeManager.GetLogisticsPropertyValue(item, CategoryAttributeManager.LogisticsProperties.TYPE);
LogManager.WriteLog($"[自动检测] 发现物流属性类型: {typeValue}");
if (!string.IsNullOrEmpty(typeValue))
{
// 尝试解析物流类型
if (Enum.TryParse<CategoryAttributeManager.LogisticsElementType>(typeValue, out var logisticsCategory))
{
if (IsLogisticsChannelType(logisticsCategory))
{
return new LogisticsChannelDetectionResult
{
IsValidChannel = true,
ChannelType = GetChannelTypeName(logisticsCategory),
DetectedChannel = item,
LogisticsCategory = logisticsCategory
};
}
else
{
return new LogisticsChannelDetectionResult
{
IsValidChannel = false,
ErrorMessage = $"检测到{GetChannelTypeName(logisticsCategory)},但此类型不可作为路径点",
LogisticsCategory = logisticsCategory
};
}
}
else
{
// 如果不能解析为枚举,基于字符串进行简单判断
var isChannel = IsChannelTypeByName(typeValue);
return new LogisticsChannelDetectionResult
{
IsValidChannel = isChannel,
ChannelType = typeValue,
DetectedChannel = item,
ErrorMessage = isChannel ? null : $"检测到{typeValue},但此类型不可作为路径点"
};
}
}
}
}
// 如果没有明确的物流属性,尝试基于几何特征判断
var geometryAnalysisResult = AnalyzeGeometryForChannelLikelihood(nearbyItems, worldPoint);
if (geometryAnalysisResult.IsLikelyChannel)
{
LogManager.WriteLog($"[自动检测] 几何分析显示可能是通道: {geometryAnalysisResult.Reason}");
return new LogisticsChannelDetectionResult
{
IsValidChannel = true,
ChannelType = "推测通道",
DetectedChannel = geometryAnalysisResult.MostLikelyChannel,
DetectionConfidence = geometryAnalysisResult.Confidence
};
}
LogManager.WriteLog($"[自动检测] 未检测到有效的物流通道属性");
return new LogisticsChannelDetectionResult
{
IsValidChannel = false,
ErrorMessage = "此位置未识别为物流通道"
};
}
catch (Exception ex)
{
LogManager.WriteLog($"[自动检测] 自动检测异常: {ex.Message}");
return new LogisticsChannelDetectionResult
{
IsValidChannel = false,
ErrorMessage = $"自动检测失败: {ex.Message}"
};
}
}
/// <summary>
/// 获取附近的模型项
/// </summary>
/// <param name="worldPoint">检测点</param>
/// <param name="radius">搜索半径(米)</param>
/// <returns>附近的模型项列表</returns>
private List<ModelItem> GetNearbyModelItems(Point3D worldPoint, double radius)
{
var nearbyItems = new List<ModelItem>();
try
{
var allItems = Application.ActiveDocument.Models.First.RootItem.DescendantsAndSelf;
foreach (var item in allItems)
{
var boundingBox = item.BoundingBox();
if (boundingBox != null)
{
// 检查点是否在扩展的包围盒内
var expandedMin = new Point3D(
boundingBox.Min.X - radius,
boundingBox.Min.Y - radius,
boundingBox.Min.Z - radius);
var expandedMax = new Point3D(
boundingBox.Max.X + radius,
boundingBox.Max.Y + radius,
boundingBox.Max.Z + radius);
if (worldPoint.X >= expandedMin.X && worldPoint.X <= expandedMax.X &&
worldPoint.Y >= expandedMin.Y && worldPoint.Y <= expandedMax.Y &&
worldPoint.Z >= expandedMin.Z && worldPoint.Z <= expandedMax.Z)
{
nearbyItems.Add(item);
}
}
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[附近项搜索] 异常: {ex.Message}");
}
return nearbyItems;
}
/// <summary>
/// 判断物流类别是否为通道类型
/// </summary>
/// <param name="category">物流类别</param>
/// <returns>是否为通道类型</returns>
private bool IsLogisticsChannelType(CategoryAttributeManager.LogisticsElementType category)
{
switch (category)
{
case CategoryAttributeManager.LogisticsElementType.:
case CategoryAttributeManager.LogisticsElementType.:
case CategoryAttributeManager.LogisticsElementType.:
case CategoryAttributeManager.LogisticsElementType.:
case CategoryAttributeManager.LogisticsElementType.:
return true;
case CategoryAttributeManager.LogisticsElementType.:
return false;
default:
return true; // 默认其他类型可通行
}
}
/// <summary>
/// 获取通道类型名称
/// </summary>
/// <param name="category">物流类别</param>
/// <returns>类型名称</returns>
private string GetChannelTypeName(CategoryAttributeManager.LogisticsElementType category)
{
switch (category)
{
case CategoryAttributeManager.LogisticsElementType.:
return "通道";
case CategoryAttributeManager.LogisticsElementType.:
return "门";
case CategoryAttributeManager.LogisticsElementType.:
return "电梯";
case CategoryAttributeManager.LogisticsElementType.:
return "楼梯";
case CategoryAttributeManager.LogisticsElementType.:
return "装卸区";
case CategoryAttributeManager.LogisticsElementType.:
return "障碍物";
case CategoryAttributeManager.LogisticsElementType.:
return "停车位";
case CategoryAttributeManager.LogisticsElementType.:
return "检查点";
default:
return "未知类型";
}
}
/// <summary>
/// 基于名称判断是否为通道类型
/// </summary>
/// <param name="typeName">类型名称</param>
/// <returns>是否为通道类型</returns>
private bool IsChannelTypeByName(string typeName)
{
if (string.IsNullOrEmpty(typeName))
return false;
// 通道相关的关键词
var channelKeywords = new[] { "通道", "门", "电梯", "楼梯", "装卸", "走廊", "过道", "通路" };
var obstacleKeywords = new[] { "障碍物", "墙", "柱子", "设备" };
var typeLower = typeName.ToLower();
// 如果包含障碍物关键词返回false
if (obstacleKeywords.Any(keyword => typeLower.Contains(keyword.ToLower())))
return false;
// 如果包含通道关键词返回true
if (channelKeywords.Any(keyword => typeLower.Contains(keyword.ToLower())))
return true;
// 默认允许通过(宽容处理)
return true;
}
/// <summary>
/// 基于几何特征分析通道可能性
/// </summary>
/// <param name="items">模型项列表</param>
/// <param name="worldPoint">检测点</param>
/// <returns>几何分析结果</returns>
private GeometryAnalysisResult AnalyzeGeometryForChannelLikelihood(List<ModelItem> items, Point3D worldPoint)
{
try
{
// 简化实现:如果有模型项且点在其包围盒内,则认为可能是通道
foreach (var item in items)
{
var boundingBox = item.BoundingBox();
if (boundingBox != null)
{
if (IsPointInBoundingBox(worldPoint, boundingBox))
{
// 基于包围盒尺寸判断是否像通道
var width = boundingBox.Max.X - boundingBox.Min.X;
var height = boundingBox.Max.Y - boundingBox.Min.Y;
var depth = boundingBox.Max.Z - boundingBox.Min.Z;
// 简单的启发式规则:如果有一个维度明显较大,可能是通道
var maxDimension = Math.Max(Math.Max(width, height), depth);
var minDimension = Math.Min(Math.Min(width, height), depth);
if (maxDimension > minDimension * 3) // 长宽比大于3:1
{
return new GeometryAnalysisResult
{
IsLikelyChannel = true,
Reason = "检测到细长形状的模型,可能是通道",
MostLikelyChannel = item,
Confidence = 0.7
};
}
}
}
}
return new GeometryAnalysisResult
{
IsLikelyChannel = false,
Reason = "几何特征不符合通道特征",
Confidence = 0.2
};
}
catch (Exception ex)
{
LogManager.WriteLog($"[几何分析] 异常: {ex.Message}");
return new GeometryAnalysisResult
{
IsLikelyChannel = false,
Reason = $"几何分析失败: {ex.Message}",
Confidence = 0.0
};
}
}
/// <summary>
/// 检查点是否在选中的通道内
/// </summary>
/// <param name="worldPoint">世界坐标点</param>
/// <returns>是否在通道内</returns>
private bool IsPointInSelectedChannels(Point3D worldPoint)
{
try
{
LogManager.WriteLog($"[通道验证] 检查点 ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2}) 是否在通道内");
if (_selectedChannels == null || _selectedChannels.Count == 0)
{
LogManager.WriteLog($"[通道验证] 警告:没有选中的通道,跳过验证");
return true; // 如果没有选中通道,允许设置点
}
for (int i = 0; i < _selectedChannels.Count; i++)
{
var channel = _selectedChannels[i];
LogManager.WriteLog($"[通道验证] 检查通道 {i + 1}/{_selectedChannels.Count}: {channel?.DisplayName ?? "Unknown"}");
var boundingBox = channel.BoundingBox();
if (boundingBox != null)
{
LogManager.WriteLog($"[通道验证] 通道包围盒: Min({boundingBox.Min.X:F2}, {boundingBox.Min.Y:F2}, {boundingBox.Min.Z:F2}) Max({boundingBox.Max.X:F2}, {boundingBox.Max.Y:F2}, {boundingBox.Max.Z:F2})");
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)
{
LogManager.WriteLog($"[通道验证] 点在通道 {i + 1} 内,验证通过");
return true;
}
else
{
LogManager.WriteLog($"[通道验证] 点不在通道 {i + 1} 内");
}
}
else
{
LogManager.WriteLog($"[通道验证] 通道 {i + 1} 包围盒为空");
}
}
LogManager.WriteLog($"[通道验证] 点不在任何选中通道内,验证失败");
return false;
}
catch (Exception ex)
{
LogManager.WriteLog($"[通道验证] 异常: {ex.Message},允许设置点(宽容处理)");
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
{
var pointNumber = GetPathPointNumber(pathPoint);
LogManager.WriteLog($"[3D标记] 开始绘制路径点标记: {pathPoint.Name}");
// 1. 创建圆形标记如果RenderPlugin可用
if (_renderPlugin == null)
{
// 尝试重新获取RenderPlugin实例
_renderPlugin = PathPointRenderPlugin.Instance;
}
if (_renderPlugin != null)
{
_renderPlugin.AddCircleMarker(pathPoint.Position, pathPoint.Type, pointNumber);
LogManager.WriteLog($"[3D标记] 圆形标记创建成功");
}
else
{
LogManager.WriteLog($"[3D标记] 圆形标记不可用RenderPlugin未注册");
}
// 2. 创建文本标注序号
CreateTextLabel(pathPoint.Position, pointNumber.ToString(), pathPoint);
var colorName = GetPointColorName(pathPoint.Type);
LogManager.WriteLog($"[3D标记] {pathPoint.Name} (序号:{pointNumber}) at ({pathPoint.Position.X:F2}, {pathPoint.Position.Y:F2}, {pathPoint.Position.Z:F2}) - {colorName} [圆形标记系统]");
}
catch (Exception ex)
{
LogManager.WriteLog($"[3D标记] 绘制3D路径点失败: {ex.Message}");
LogManager.WriteLog($"[3D标记] 异常堆栈: {ex.StackTrace}");
}
}
/// <summary>
/// 创建文本标注
/// </summary>
/// <param name="position">标注位置</param>
/// <param name="text">标注文本</param>
/// <param name="pathPoint">关联的路径点</param>
private void CreateTextLabel(Point3D position, string text, PathPoint pathPoint)
{
try
{
// 注意Navisworks 2017的文本标注API可能有限
// 这里使用日志记录作为临时实现后续可改进为真正的3D文本
LogManager.WriteLog($"[文本标注] 在位置 ({position.X:F2}, {position.Y:F2}, {position.Z:F2}) 创建标注: \"{text}\"");
// 记录文本标注信息
if (_pathPointMarkers == null)
_pathPointMarkers = new List<PathPointMarker>();
_pathPointMarkers.Add(new PathPointMarker
{
PathPoint = pathPoint,
LabelText = text,
LabelPosition = position,
MarkerType = PathPointMarkerType.TextLabel
});
// TODO: 实现真正的3D文本标注如果Navisworks 2017 API支持
// 可能的实现方式:
// 1. 使用COM接口的标注功能
// 2. 创建简单的几何文本
// 3. 使用视点注释功能
}
catch (Exception ex)
{
LogManager.WriteLog($"[文本标注] 创建文本标注失败: {ex.Message}");
}
}
/// <summary>
/// 获取路径点在当前路径中的序号
/// </summary>
/// <param name="pathPoint">路径点</param>
/// <returns>序号从1开始</returns>
private int GetPathPointNumber(PathPoint pathPoint)
{
if (_currentRoute?.Points != null)
{
var index = _currentRoute.Points.IndexOf(pathPoint);
return index >= 0 ? index + 1 : 0;
}
return 0;
}
/// <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>
/// 获取点类型对应的Navisworks API颜色用于PathVisualizer
/// </summary>
/// <param name="pointType">路径点类型</param>
/// <returns>Navisworks API颜色</returns>
private Autodesk.Navisworks.Api.Color GetNavisworksColor(PathPointType pointType)
{
switch (pointType)
{
case PathPointType.StartPoint:
return Autodesk.Navisworks.Api.Color.Green;
case PathPointType.EndPoint:
return Autodesk.Navisworks.Api.Color.Red;
default:
return Autodesk.Navisworks.Api.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
{
LogManager.WriteLog("[3D标记清理] 开始清除所有路径点标记");
// 1. 清除圆形标记如果RenderPlugin可用
if (_renderPlugin != null)
{
var circleCount = _renderPlugin.MarkerCount;
_renderPlugin.ClearAllMarkers();
LogManager.WriteLog($"[3D标记清理] 清除 {circleCount} 个圆形标记");
}
// 2. 清除文本标注记录
if (_pathPointMarkers != null)
{
var labelMarkers = _pathPointMarkers.Where(m => m.MarkerType == PathPointMarkerType.TextLabel).ToList();
foreach (var marker in labelMarkers)
{
LogManager.WriteLog($"[3D标记清理] 清除路径点 {marker.PathPoint?.Name} 的文本标注: {marker.LabelText}");
// TODO: 清除实际的3D文本标注当实现后
}
LogManager.WriteLog($"[3D标记清理] 共清除 {labelMarkers.Count} 个文本标注记录");
// 清空标记列表
_pathPointMarkers.Clear();
}
// 3. 刷新视图
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
LogManager.WriteLog("[3D标记清理] 3D路径标记清除完成");
OnStatusChanged("已清除所有3D路径标记");
}
catch (Exception ex)
{
LogManager.WriteLog($"[3D标记清理] 清除3D路径标记失败: {ex.Message}");
OnErrorOccurred($"清除3D路径标记失败: {ex.Message}");
}
}
#endregion
#region 3D
private static PathPlanningManager _activePathManager; // 静态引用用于处理ToolPlugin事件
/// <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
{
LogManager.WriteLog("[智能类型] 开始智能点类型切换");
if (_currentRoute == null || _currentRoute.Points == null)
{
_currentPointType = PathPointType.StartPoint;
LogManager.WriteLog("[智能类型] 无路径或无点,设置为起点类型");
return;
}
var pointCount = _currentRoute.Points.Count;
var hasStartPoint = _currentRoute.Points.Any(p => p.Type == PathPointType.StartPoint);
var hasEndPoint = _currentRoute.Points.Any(p => p.Type == PathPointType.EndPoint);
LogManager.WriteLog($"[智能类型] 路径状态分析:点数={pointCount}, 有起点={hasStartPoint}, 有终点={hasEndPoint}");
if (pointCount == 0 || !hasStartPoint)
{
// 没有点或没有起点时,下一个点应该是起点
_currentPointType = PathPointType.StartPoint;
LogManager.WriteLog("[智能类型] 自动设置为起点(首个点或缺失起点)");
}
else if (hasStartPoint && !hasEndPoint && pointCount >= 2)
{
// 已有起点没有终点且已有至少2个点起点+1个路径点可以选择添加终点
// 但默认仍为路径点,用户可手动选择终点
_currentPointType = PathPointType.WayPoint;
LogManager.WriteLog("[智能类型] 自动设置为路径点(建议手动设置终点)");
}
else if (hasStartPoint && !hasEndPoint)
{
// 已有起点没有终点点数少于2个继续添加路径点
_currentPointType = PathPointType.WayPoint;
LogManager.WriteLog("[智能类型] 自动设置为路径点(构建中间路径)");
}
else
{
// 其他情况默认为路径点
_currentPointType = PathPointType.WayPoint;
LogManager.WriteLog("[智能类型] 默认设置为路径点");
}
// 触发类型变更事件
OnCurrentPointTypeChanged();
LogManager.WriteLog("[智能类型] 智能点类型切换完成");
}
catch (Exception ex)
{
LogManager.WriteLog($"[智能类型] 智能切换异常: {ex.Message}");
LogManager.WriteLog($"[智能类型] 异常堆栈: {ex.StackTrace}");
}
}
/// <summary>
/// 智能确定终点
/// 当用户明确想要结束路径时调用
/// </summary>
public void SetNextPointAsEndPoint()
{
if (_currentRoute != null && _currentRoute.Points.Count > 0)
{
var hasEndPoint = _currentRoute.Points.Any(p => p.Type == PathPointType.EndPoint);
if (!hasEndPoint)
{
_currentPointType = PathPointType.EndPoint;
LogManager.WriteLog("[智能类型] 用户设置下一个点为终点");
OnCurrentPointTypeChanged();
}
else
{
LogManager.WriteLog("[智能类型] 路径已有终点,无法再设置终点");
OnErrorOccurred("当前路径已有终点,无法添加新的终点");
}
}
else
{
LogManager.WriteLog("[智能类型] 没有路径或路径为空,无法设置终点");
OnErrorOccurred("请先添加起点和路径点,再设置终点");
}
}
/// <summary>
/// 自动调整已有路径点的类型
/// 确保路径逻辑正确:第一个点为起点,最后一个点为终点
/// </summary>
public void AutoAdjustPathPointTypes()
{
if (_currentRoute == null || _currentRoute.Points == null || _currentRoute.Points.Count == 0)
{
LogManager.WriteLog("[类型调整] 无路径点需要调整");
return;
}
var points = _currentRoute.Points;
var adjustmentsMade = false;
LogManager.WriteLog($"[类型调整] 开始调整路径点类型,总点数: {points.Count}");
// 确保第一个点是起点
if (points.Count > 0 && points[0].Type != PathPointType.StartPoint)
{
var oldType = points[0].Type;
points[0].Type = PathPointType.StartPoint;
points[0].Name = GeneratePointName(PathPointType.StartPoint);
adjustmentsMade = true;
LogManager.WriteLog($"[类型调整] 第1个点从{oldType}调整为起点: {points[0].Name}");
}
// 确保最后一个点是终点(如果路径已完整)
if (points.Count > 1)
{
var lastPoint = points[points.Count - 1];
if (lastPoint.Type != PathPointType.EndPoint)
{
var oldType = lastPoint.Type;
lastPoint.Type = PathPointType.EndPoint;
lastPoint.Name = GeneratePointName(PathPointType.EndPoint);
adjustmentsMade = true;
LogManager.WriteLog($"[类型调整] 最后一个点从{oldType}调整为终点: {lastPoint.Name}");
}
}
// 确保中间的点都是路径点
for (int i = 1; i < points.Count - 1; i++)
{
if (points[i].Type != PathPointType.WayPoint)
{
var oldType = points[i].Type;
points[i].Type = PathPointType.WayPoint;
points[i].Name = GeneratePointName(PathPointType.WayPoint);
adjustmentsMade = true;
LogManager.WriteLog($"[类型调整] 第{i + 1}个点从{oldType}调整为路径点: {points[i].Name}");
}
}
if (adjustmentsMade)
{
LogManager.WriteLog("[类型调整] 路径点类型调整完成");
OnStatusChanged("已自动调整路径点类型:起点→路径点→终点");
// 触发路径更新事件
CurrentRouteChanged?.Invoke(this, _currentRoute);
PathPointAddedIn3D?.Invoke(this, null); // 触发UI更新
}
else
{
LogManager.WriteLog("[类型调整] 路径点类型已正确,无需调整");
}
}
/// <summary>
/// 触发当前点类型变更事件
/// </summary>
private void OnCurrentPointTypeChanged()
{
try
{
// 这里可以触发UI更新比如更新按钮状态、标签文本等
LogManager.WriteLog($"[事件] 当前点类型已变更为: {_currentPointType}");
OnStatusChanged($"当前点类型: {GetPointTypeName(_currentPointType)}");
}
catch (Exception ex)
{
LogManager.WriteLog($"[事件] 触发点类型变更事件时异常: {ex.Message}");
}
}
/// <summary>
/// 从3D视图和当前路径中删除路径点
/// </summary>
/// <param name="pathPoint">要删除的路径点</param>
/// <returns>是否成功删除</returns>
public bool RemovePathPointFrom3D(PathPoint pathPoint)
{
try
{
LogManager.WriteLog($"[路径点删除] 开始删除路径点: {pathPoint?.Name}");
if (pathPoint == null)
{
LogManager.WriteLog("[路径点删除] 路径点为空,无法删除");
OnErrorOccurred("无法删除空的路径点");
return false;
}
if (_currentRoute == null)
{
LogManager.WriteLog("[路径点删除] 当前路径为空,无法删除路径点");
OnErrorOccurred("当前没有活动路径");
return false;
}
// 从当前路径中删除点
var removed = _currentRoute.RemovePoint(pathPoint);
if (!removed)
{
LogManager.WriteLog($"[路径点删除] 路径点不存在于当前路径中: {pathPoint.Name}");
OnErrorOccurred("路径点不存在于当前路径中");
return false;
}
// 清除3D标记
Clear3DPathPointMarker(pathPoint);
// 重新绘制剩余的路径点(更新序号)
RedrawAllPathPoints();
// 触发删除事件
PathPointRemovedFrom3D?.Invoke(this, pathPoint);
// 触发路径更新事件
PathPointsListUpdated?.Invoke(this, _currentRoute);
// 添加历史记录(仅在编辑状态下)
if (IsInEditableState && _currentRoute != null)
{
var historyEntry = new PathHistoryEntry(
_currentRoute.Id,
PathHistoryOperationType.PointRemoved,
_currentRoute,
$"删除路径点: {pathPoint.Name}");
_historyManager.AddHistoryEntry(historyEntry);
}
LogManager.WriteLog($"[路径点删除] 成功删除路径点: {pathPoint.Name}");
OnStatusChanged($"已删除路径点: {pathPoint.Name}");
return true;
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径点删除] 删除路径点异常: {ex.Message}");
LogManager.WriteLog($"[路径点删除] 异常堆栈: {ex.StackTrace}");
OnErrorOccurred($"删除路径点失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 清除指定路径点的3D标记
/// </summary>
/// <param name="pathPoint">要清除标记的路径点</param>
private void Clear3DPathPointMarker(PathPoint pathPoint)
{
try
{
if (_pathPointMarkers != null)
{
// 移除相关的标记
_pathPointMarkers.RemoveAll(marker => marker.PathPoint?.Id == pathPoint.Id);
LogManager.WriteLog($"[标记清除] 已清除路径点的3D标记: {pathPoint.Name}");
}
// 使用RenderPlugin清除圆形标记
if (_renderPlugin != null)
{
var removed = _renderPlugin.RemoveMarkerAt(pathPoint.Position, 1.0);
LogManager.WriteLog($"[标记清除] 圆形标记清除结果: {removed}, 路径点: {pathPoint.Name}");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[标记清除] 清除3D标记异常: {ex.Message}");
}
}
/// <summary>
/// 重新绘制所有路径点(更新序号和标记)
/// </summary>
private void RedrawAllPathPoints()
{
try
{
LogManager.WriteLog("[重绘路径] 开始重新绘制所有路径点");
if (_currentRoute?.Points == null || _currentRoute.Points.Count == 0)
{
LogManager.WriteLog("[重绘路径] 当前路径无点,无需重绘");
return;
}
// 先清除所有现有标记
Clear3DPathMarkers();
// 重新绘制每个点
foreach (var point in _currentRoute.Points)
{
Draw3DPathPoint(point);
}
LogManager.WriteLog($"[重绘路径] 重新绘制完成,共{_currentRoute.Points.Count}个路径点");
}
catch (Exception ex)
{
LogManager.WriteLog($"[重绘路径] 重绘路径点异常: {ex.Message}");
}
}
/// <summary>
/// 获取指定位置附近的路径点
/// </summary>
/// <param name="position">3D位置</param>
/// <param name="tolerance">容差范围(米)</param>
/// <returns>附近的路径点如果没有返回null</returns>
public PathPoint GetNearbyPathPoint(Point3D position, double tolerance = 1.0)
{
try
{
if (_currentRoute?.Points == null || _currentRoute.Points.Count == 0)
return null;
PathPoint nearestPoint = null;
double minDistance = double.MaxValue;
foreach (var point in _currentRoute.Points)
{
var distance = CalculateDistance3D(position, point.Position);
if (distance <= tolerance && distance < minDistance)
{
minDistance = distance;
nearestPoint = point;
}
}
if (nearestPoint != null)
{
LogManager.WriteLog($"[附近搜索] 找到附近路径点: {nearestPoint.Name}, 距离: {minDistance:F2}米");
}
return nearestPoint;
}
catch (Exception ex)
{
LogManager.WriteLog($"[附近搜索] 搜索附近路径点异常: {ex.Message}");
return null;
}
}
/// <summary>
/// 实时同步路径点列表
/// 当路径发生变化时通知UI更新
/// </summary>
public void SyncPathPointsList()
{
try
{
LogManager.WriteLog("[实时同步] 开始同步路径点列表");
if (_currentRoute != null)
{
// 重新计算路径长度
_currentRoute.RecalculateLength();
// 触发路径更新事件
PathPointsListUpdated?.Invoke(this, _currentRoute);
CurrentRouteChanged?.Invoke(this, _currentRoute);
LogManager.WriteLog($"[实时同步] 路径同步完成,当前点数: {_currentRoute.Points.Count}");
}
else
{
LogManager.WriteLog("[实时同步] 当前路径为空,触发空路径同步");
PathPointsListUpdated?.Invoke(this, null);
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[实时同步] 同步路径点列表异常: {ex.Message}");
}
}
/// <summary>
/// 选择路径点(高亮显示)
/// </summary>
/// <param name="pathPoint">要选择的路径点</param>
public void SelectPathPoint(PathPoint pathPoint)
{
try
{
if (pathPoint == null)
{
LogManager.WriteLog("[路径点选择] 取消路径点选择");
return;
}
LogManager.WriteLog($"[路径点选择] 选择路径点: {pathPoint.Name}");
// 高亮显示选中的路径点
HighlightNearbyItems(pathPoint.Position, new Color(1.0f, 1.0f, 0.0f)); // 黄色
// 更新状态信息
OnStatusChanged($"已选择路径点: {pathPoint.Name} ({GetPointTypeName(pathPoint.Type)})");
}
catch (Exception ex)
{
LogManager.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
#region ToolPlugin
/// <summary>
/// ToolPlugin是否已激活
/// </summary>
private bool _isToolPluginActive = false;
/// <summary>
/// 智能ToolPlugin管理根据编辑状态自动激活或停用
/// </summary>
private void ManageToolPluginForEditState()
{
if (IsInEditableState)
{
// 编辑状态确保ToolPlugin已激活
if (!_isToolPluginActive)
{
ActivateToolPlugin();
}
}
else
{
// 查看状态确保ToolPlugin已停用
if (_isToolPluginActive)
{
DeactivateToolPlugin();
}
}
}
/// <summary>
/// 激活自定义ToolPlugin进行精确点击检测
/// </summary>
private bool ActivateToolPlugin()
{
// 如果已经激活,直接返回成功
if (_isToolPluginActive)
{
LogManager.WriteLog("[ToolPlugin] ToolPlugin已激活无需重复激活");
return true;
}
try
{
LogManager.WriteLog("[ToolPlugin] ===== 开始激活ToolPlugin =====");
LogManager.WriteLog($"[ToolPlugin] 当前应用程序状态: {Application.IsAutomated}");
LogManager.WriteLog($"[ToolPlugin] 当前文档状态: {Application.ActiveDocument?.Title ?? "NULL"}");
// 1. 加载插件程序集
LogManager.WriteLog("[ToolPlugin] 步骤1: 加载插件程序集");
var assemblyPath = PathClickToolPlugin.AssemblyPath;
LogManager.WriteLog($"[ToolPlugin] 程序集路径: {assemblyPath}");
LogManager.WriteLog($"[ToolPlugin] 程序集文件是否存在: {System.IO.File.Exists(assemblyPath)}");
Application.Plugins.AddPluginAssembly(assemblyPath);
LogManager.WriteLog("[ToolPlugin] ✓ 程序集加载完成");
// 2. 查找插件
LogManager.WriteLog("[ToolPlugin] 步骤2: 查找插件");
ToolPluginRecord toolPluginRecord = (ToolPluginRecord)Application.Plugins.FindPlugin("PathClickTool.NavisworksTransport");
if (toolPluginRecord == null)
{
LogManager.WriteLog("[ToolPlugin] ✗ 错误: 无法找到PathClickTool插件");
return false;
}
LogManager.WriteLog("[ToolPlugin] ✓ 插件查找成功");
// 3. 加载插件
LogManager.WriteLog("[ToolPlugin] 步骤3: 加载插件");
var loadedPlugin = toolPluginRecord.LoadPlugin();
if (loadedPlugin == null)
{
LogManager.WriteLog("[ToolPlugin] ✗ 错误: 插件加载失败");
return false;
}
LogManager.WriteLog("[ToolPlugin] ✓ 插件加载成功");
// 4. 设置为活动工具
LogManager.WriteLog("[ToolPlugin] 步骤4: 设置为活动工具");
Application.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin);
LogManager.WriteLog("[ToolPlugin] ✓ 工具设置成功");
// 5. 订阅点击事件
LogManager.WriteLog("[ToolPlugin] 步骤5: 订阅点击事件");
PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked;
LogManager.WriteLog("[ToolPlugin] ✓ 事件订阅成功");
_isToolPluginActive = true;
LogManager.WriteLog("[ToolPlugin] ===== ToolPlugin激活完成 =====");
return true;
}
catch (Exception ex)
{
LogManager.WriteLog($"[ToolPlugin] 激活异常: {ex.Message}");
LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// 停用ToolPlugin
/// </summary>
private bool DeactivateToolPlugin()
{
try
{
LogManager.WriteLog("[ToolPlugin] ===== 开始停用ToolPlugin =====");
if (_isToolPluginActive)
{
// 取消订阅事件
LogManager.WriteLog("[ToolPlugin] 取消事件订阅");
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
// 恢复默认选择工具
LogManager.WriteLog("[ToolPlugin] 恢复默认选择工具");
Application.MainDocument.Tool.Value = Tool.Select;
_isToolPluginActive = false;
LogManager.WriteLog("[ToolPlugin] ✓ ToolPlugin停用完成");
}
else
{
LogManager.WriteLog("[ToolPlugin] ToolPlugin未激活无需停用");
}
return true;
}
catch (Exception ex)
{
LogManager.WriteLog($"[ToolPlugin] 停用异常: {ex.Message}");
return false;
}
}
/// <summary>
/// 处理ToolPlugin的鼠标点击事件
/// </summary>
private void OnToolPluginMouseClicked(object sender, PickItemResult pickResult)
{
try
{
LogManager.WriteLog("[ToolPlugin事件] ===== 收到精确点击坐标 =====");
LogManager.WriteLog($"[ToolPlugin事件] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
LogManager.WriteLog($"[ToolPlugin事件] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}");
// 检查是否在选定的通道内
if (_selectedChannels != null && _selectedChannels.Any())
{
bool isInChannel = IsItemInSelectedChannels(pickResult.ModelItem) ||
IsItemChildOfSelectedChannels(pickResult.ModelItem);
LogManager.WriteLog($"[ToolPlugin事件] 在选定通道内: {isInChannel}");
if (isInChannel)
{
// 不再传递点类型让AddPathPointIn3D方法内部自动判断
LogManager.WriteLog($"[ToolPlugin事件] 调用AddPathPointIn3D添加路径点");
var pathPoint = AddPathPointIn3D(pickResult.Point);
if (pathPoint != null)
{
LogManager.WriteLog($"[ToolPlugin事件] ✓ 路径点添加成功: {pathPoint.Name}");
}
else
{
LogManager.WriteLog("[ToolPlugin事件] ✗ 路径点添加失败");
}
}
else
{
LogManager.WriteLog("[ToolPlugin事件] ✗ 点击位置不在选定通道内");
}
}
else
{
LogManager.WriteLog("[ToolPlugin事件] ✗ 未选择通道");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[ToolPlugin事件] 处理异常: {ex.Message}");
LogManager.WriteLog($"[ToolPlugin事件] 堆栈: {ex.StackTrace}");
}
}
#endregion
#endregion
// 路径点3D标记管理
private List<PathPointMarker> _pathPointMarkers;
#region
/// <summary>
/// 开始新建路径
/// </summary>
/// <param name="routeName">路径名称</param>
/// <returns>创建的新路径</returns>
public PathRoute StartCreatingNewRoute(string routeName = null)
{
try
{
// 清除旧的3D标记避免连接到上一条路径
if (_renderPlugin != null)
{
_renderPlugin.ClearAllMarkers();
LogManager.Info("已清除旧的3D路径标记准备创建新路径");
}
// 设置为创建状态
PathEditState = PathEditState.Creating;
// 创建新路径
var newRoute = new PathRoute(routeName ?? $"路径{DateTime.Now:yyyyMMdd_HHmmss}");
_editingRoute = newRoute;
CurrentRoute = newRoute;
// 自动选择所有物流通道
AutoSelectLogisticsChannels();
// 智能管理ToolPlugin状态
ManageToolPluginForEditState();
// 高亮通道以便用户点击
HighlightLogisticsChannels();
LogManager.Info($"开始新建路径: {newRoute.Name}");
OnStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中点击设置路径点");
return newRoute;
}
catch (Exception ex)
{
OnErrorOccurred($"开始新建路径失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 自动选择所有物流通道
/// </summary>
private void AutoSelectLogisticsChannels()
{
try
{
var document = Application.ActiveDocument;
if (document?.Models == null) return;
_selectedChannels.Clear();
// 收集所有物流标记的模型
foreach (ModelItem rootItem in document.Models.RootItems)
{
foreach (ModelItem item in rootItem.DescendantsAndSelf)
{
if (CategoryAttributeManager.HasLogisticsAttributes(item))
{
_selectedChannels.Add(item);
}
}
}
if (_selectedChannels.Count > 0)
{
// 计算组合边界
CalculateCombinedBounds();
// 触发事件
ChannelsSelected?.Invoke(this, _selectedChannels);
LogManager.Info($"自动选择了 {_selectedChannels.Count} 个物流通道");
}
else
{
LogManager.Warning("未找到任何物流通道");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"自动选择物流通道失败: {ex.Message}");
}
}
/// <summary>
/// 高亮所有物流通道
/// </summary>
private void HighlightLogisticsChannels()
{
try
{
var document = Application.ActiveDocument;
if (document?.Models == null) return;
var logisticsChannels = new ModelItemCollection();
// 收集所有物流标记的模型
foreach (ModelItem rootItem in document.Models.RootItems)
{
foreach (ModelItem item in rootItem.DescendantsAndSelf)
{
if (CategoryAttributeManager.HasLogisticsAttributes(item))
{
logisticsChannels.Add(item);
}
}
}
// 高亮显示
if (logisticsChannels.Count > 0)
{
// 改为淡青色,避免与黄色连线冲突
HighlightChannels(logisticsChannels, System.Drawing.Color.Cyan);
LogManager.Info($"高亮显示 {logisticsChannels.Count} 个物流通道为青色");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"高亮物流通道失败: {ex.Message}");
}
}
#endregion
/// <summary>
/// 路径历史记录管理器(只读访问)
/// </summary>
public PathHistoryManager HistoryManager => _historyManager;
/// <summary>
/// 历史记录变更事件
/// </summary>
public event EventHandler<PathHistoryEntry> HistoryEntryAdded
{
add { _historyManager.HistoryEntryAdded += value; }
remove { _historyManager.HistoryEntryAdded -= value; }
}
/// <summary>
/// 更新3D路径点标记的外观
/// </summary>
private void UpdateMarkerAppearance(PathPoint point)
{
if (_renderPlugin == null) return;
// 获取点的序号从1开始
int sequenceNumber = GetPathPointNumber(point);
if (sequenceNumber == -1) return;
// 获取新类型对应的颜色和半径
var newColor = _renderPlugin.GetColorForPointType(point.Type);
var newRadius = _renderPlugin.GetRadiusForPointType(point.Type);
// 调用渲染插件来更新标记
_renderPlugin.UpdateMarker(sequenceNumber, newColor, newRadius);
}
}
}