diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs index b4e090e..7b95aee 100644 --- a/src/Core/PathPlanningManager.cs +++ b/src/Core/PathPlanningManager.cs @@ -61,7 +61,7 @@ namespace NavisworksTransport private int _editingPointIndex = -1; // 正在修改的路径点索引 private PathPoint _originalPoint = null; // 修改前的原始路径点 private PathPoint _editingPreviewPoint = null; // 修改时的预览路径点 - + /// /// ToolPlugin是否处于激活状态(供InputMonitor使用) /// @@ -499,7 +499,7 @@ namespace NavisworksTransport } } - private void RaisePathPointsListUpdated(PathRoute route, string updateReason) + public void RaisePathPointsListUpdated(PathRoute route, string updateReason) { try { @@ -561,7 +561,7 @@ namespace NavisworksTransport RaiseErrorOccurred(ex.Message, ex); } } - + /// /// 清理无效的对象引用 /// @@ -570,10 +570,10 @@ namespace NavisworksTransport try { LogManager.Info("[PathPlanningManager] 清理无效对象引用"); - + // 重置编辑状态 PathEditState = PathEditState.None; - + // 清空当前路径 if (_currentRoute != null) { @@ -842,7 +842,7 @@ namespace NavisworksTransport try { // 使用2.5D通道检测模式 - LogManager.Info("尝试使用ChannelBased2_5D模式生成网格"); + LogManager.Info("使用BoundingBoxBased2_5D模式生成网格"); gridMap = gridMapGenerator.GenerateFromBIM( document, bounds, @@ -904,7 +904,8 @@ namespace NavisworksTransport } else { - LogManager.Info("未找到通道数据,将使用传统边界框模式进行路径规划"); + LogManager.Error("未找到通道数据,无法进行路径规划"); + throw new Exception("路径规划需要通道数据,请先进行通道检测"); } // 5. 执行A*路径查找 @@ -917,28 +918,11 @@ namespace NavisworksTransport try { pathFinder = new AutoPathFinder(); - LogManager.Info("AutoPathFinder实例创建成功"); - LogManager.Info("开始调用FindPath方法..."); - // 🔥 关键修复:传递ChannelCoverage数据和车辆高度 - if (channelCoverage != null) - { - LogManager.Info($"使用2.5D模式进行路径查找,车辆高度: {vehicleHeight}m,策略: {strategy}"); - pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, vehicleHeight, strategy); - } - else - { - LogManager.Info($"使用传统边界框模式进行路径查找,策略: {strategy}"); - var legacyPathPoints = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap); - pathResult = new PathFindingResult - { - PathPoints = legacyPathPoints, - IsComplete = true, - OriginalEndPoint = endPoint.Position, - ActualEndPoint = endPoint.Position, - CompletionPercentage = 100.0 - }; - } + double vehicleHeightInModelUnits = vehicleHeight * UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units); + LogManager.Info($"使用2.5D模式进行路径查找,车辆高度: {vehicleHeight}m ({vehicleHeightInModelUnits:F2}模型单位),策略: {strategy}"); + pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, vehicleHeightInModelUnits, strategy); + LogManager.Info("FindPath方法调用完成"); } catch (Exception ex) @@ -1003,25 +987,8 @@ namespace NavisworksTransport // 11. 触发事件 RaiseRouteGenerated(autoRoute, RouteGenerationMethod.AutoPlanning, gridSize); - // 12. 最终确保状态正确(防御性编程) - // 使用延迟执行确保在所有事件处理完成后状态仍然正确 - if (_uiStateManager != null) - { - _uiStateManager.QueueUIUpdate(() => - { - LogManager.Info("延迟确保自动路径规划完成后状态为Viewing"); - if (PathEditState != PathEditState.Viewing) - { - LogManager.Warning($"检测到状态被意外修改为 {PathEditState},强制重置为Viewing"); - PathEditState = PathEditState.Viewing; - EditingRoute = null; - } - }, UIUpdatePriority.Normal, forceSync: false); - } - var statusMessage = channelCoverage != null - ? $"自动路径规划完成(2.5D模式): {routeName},使用网格大小 {gridSize:F2}米" - : $"自动路径规划完成(边界框模式): {routeName},使用网格大小 {gridSize:F2}米"; + var statusMessage = $"自动路径规划完成(2.5D模式): {routeName},使用网格大小 {gridSize:F2}米"; // 添加完成度信息 if (!autoRoute.IsComplete) @@ -1138,7 +1105,7 @@ namespace NavisworksTransport _walkableAreas.AddRange(channelItems); LogManager.Info($"[SelectChannels] 通过CategoryAttributeManager直接筛选到 {channelItems.Count} 个通道"); } - + RaiseStatusChanged($"通过类别筛选到 {_walkableAreas.Count} 个通道", PathPlanningStatusType.Success); } @@ -2373,12 +2340,12 @@ namespace NavisworksTransport // 4. 设置为活动工具 LogManager.WriteLog("[ToolPlugin] 步骤4: 设置为活动工具"); - + // 先重置工具状态,清除可能的导航工具 // 这一步很重要,确保从导航工具(如Pan、Orbit)切换回自定义工具 Application.MainDocument.Tool.Value = Tool.None; LogManager.WriteLog("[ToolPlugin] 已重置工具状态为None"); - + // 然后设置自定义工具插件 Application.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin); LogManager.WriteLog("[ToolPlugin] ✓ 工具设置成功"); @@ -2417,7 +2384,7 @@ namespace NavisworksTransport { // 检查当前状态和实际工具值 if ((PathEditState == PathEditState.Creating || - PathEditState == PathEditState.AddingPoints || + PathEditState == PathEditState.AddingPoints || PathEditState == PathEditState.EditingPoint)) { // 检查当前工具是否为自定义工具,如果不是则需要重新激活 @@ -2425,10 +2392,10 @@ namespace NavisworksTransport if (currentTool != Tool.CustomToolPlugin) { LogManager.WriteLog($"[ReactivateToolPlugin] 当前工具为{currentTool},开始重新激活ToolPlugin"); - + // 重要:先将激活标志设置为false,强制执行完整的激活流程 _isToolPluginActive = false; - + // 调用现有的激活方法,但不重复订阅事件 if (ActivateToolPlugin(false)) { @@ -2731,19 +2698,19 @@ namespace NavisworksTransport try { LogManager.WriteLog($"[工具插件重初始化] 开始强制重新初始化ToolPlugin,订阅事件: {subscribeToEvents}"); - + // 1. 停用当前ToolPlugin bool deactivated = DeactivateToolPlugin(); LogManager.WriteLog($"[工具插件重初始化] 停用结果: {deactivated}"); - + // 2. 强制重置激活状态 _isToolPluginActive = false; LogManager.WriteLog("[工具插件重初始化] 已重置激活状态标志"); - + // 3. 重新激活ToolPlugin bool activated = ActivateToolPlugin(subscribeToEvents); LogManager.WriteLog($"[工具插件重初始化] 激活结果(事件订阅: {subscribeToEvents}): {activated}"); - + if (activated) { LogManager.WriteLog("[工具插件重初始化] ToolPlugin重新初始化成功,已获得鼠标焦点"); @@ -2752,7 +2719,7 @@ namespace NavisworksTransport { LogManager.WriteLog("[工具插件重初始化] ToolPlugin激活失败"); } - + return activated; } catch (Exception ex) @@ -2867,7 +2834,7 @@ namespace NavisworksTransport channelItems.AddRange(gridMap.ChannelItems); return channelItems; } - + // 如果用户已选择通道,优先使用用户选择的通道 if (_walkableAreas != null && _walkableAreas.Count > 0) { @@ -3170,15 +3137,15 @@ namespace NavisworksTransport try { LogManager.Info($"[网格可视化设置] 更新设置: 通行={showWalkable}, 障碍物={showObstacle}, 未知={showUnknown}, 门={showDoor}"); - + _showWalkableGrid = showWalkable; _showObstacleGrid = showObstacle; _showUnknownGrid = showUnknown; _showDoorGrid = showDoor; - + // 如果当前有网格正在显示,重新应用可视化 RefreshGridVisualization(); - + LogManager.Info("[网格可视化设置] 设置更新完成"); } catch (Exception ex) @@ -3266,17 +3233,17 @@ namespace NavisworksTransport { Id = "grid_visualization_channel" }; - + var unknownRoute = new PathRoute("GridVisualization_Unknown") { Id = "grid_visualization_unknown" }; - + var obstacleRoute = new PathRoute("GridVisualization_Obstacle") { Id = "grid_visualization_obstacle" }; - + var doorRoute = new PathRoute("GridVisualization_Door") { Id = "grid_visualization_door" @@ -3296,7 +3263,7 @@ namespace NavisworksTransport for (int y = 0; y < gridMap.Height; y++) { var cell = gridMap.Cells[x, y]; - + // 计算网格单元格的世界中心位置 var gridPos = new NavisworksTransport.PathPlanning.GridPoint2D(x, y); var worldCenter = gridMap.GridToWorld3D(gridPos, cell.WorldPosition.Z); @@ -3304,7 +3271,7 @@ namespace NavisworksTransport PathRoute targetRoute = null; string gridTypeName = ""; - + // 根据网格类型和用户设置确定目标路径和名称 if (cell.IsWalkable && cell.CellType == CategoryAttributeManager.LogisticsElementType.通道) { @@ -3351,7 +3318,7 @@ namespace NavisworksTransport obstacleCells++; } } - + // 如果有对应的路径,创建并添加网格点 if (targetRoute != null) { @@ -3376,19 +3343,19 @@ namespace NavisworksTransport renderPlugin.RenderPointOnly(channelRoute); LogManager.Info($"[网格可视化] 渲染通道/开放空间网格点: {channelRoute.Points.Count} 个"); } - + if (doorRoute.Points.Count > 0) { renderPlugin.RenderPointOnly(doorRoute); LogManager.Info($"[网格可视化] 渲染门网格点: {doorRoute.Points.Count} 个(50%透明绿色)"); } - + if (unknownRoute.Points.Count > 0) { renderPlugin.RenderPointOnly(unknownRoute); LogManager.Info($"[网格可视化] 渲染Unknown网格点: {unknownRoute.Points.Count} 个"); } - + if (obstacleRoute.Points.Count > 0) { renderPlugin.RenderPointOnly(obstacleRoute); diff --git a/src/PathPlanning/AutoPathFinder.cs b/src/PathPlanning/AutoPathFinder.cs index e3e878b..56503e7 100644 --- a/src/PathPlanning/AutoPathFinder.cs +++ b/src/PathPlanning/AutoPathFinder.cs @@ -48,19 +48,6 @@ namespace NavisworksTransport.PathPlanning /// public class AutoPathFinder { - /// - /// 查找路径(传统方法,保持向后兼容) - /// - /// 起点(世界坐标) - /// 终点(世界坐标) - /// 网格地图 - /// 路径点列表(世界坐标) - public List FindPath(Point3D start, Point3D end, GridMap gridMap) - { - var result = FindPath(start, end, gridMap, null, 3.0); - return result?.PathPoints ?? new List(); - } - /// /// 查找路径(支持通道数据和高度约束) /// @@ -68,7 +55,7 @@ namespace NavisworksTransport.PathPlanning /// 终点(世界坐标) /// 网格地图 /// 通道覆盖数据(可选,用于2.5D路径规划) - /// 车辆高度(用于高度约束检查) + /// 车辆高度(模型单位,用于高度约束检查) /// 路径查找结果 public PathFindingResult FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight) { @@ -83,7 +70,7 @@ namespace NavisworksTransport.PathPlanning /// 终点(世界坐标) /// 网格地图 /// 通道覆盖数据(可选,用于2.5D路径规划) - /// 车辆高度(用于高度约束检查) + /// 车辆高度(模型单位,用于高度约束检查) /// 路径规划策略 /// 路径查找结果 public PathFindingResult FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, PathStrategy strategy) @@ -107,7 +94,7 @@ namespace NavisworksTransport.PathPlanning /// 终点(世界坐标) /// 网格地图 /// 通道覆盖数据 - /// 车辆高度 + /// 车辆高度(模型单位) /// 路径规划策略 /// 路径查找结果 private PathFindingResult FindPath2_5D(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, PathStrategy strategy = PathStrategy.Shortest) @@ -197,7 +184,7 @@ namespace NavisworksTransport.PathPlanning /// /// 网格地图 /// 通道覆盖数据 - /// 车辆高度 + /// 车辆高度(模型单位) /// A*算法网格 private Grid ConvertToAStarGridWith2_5D(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight) { @@ -258,9 +245,8 @@ namespace NavisworksTransport.PathPlanning { if (cell.PassableHeights != null && cell.PassableHeights.Any()) { - // 将车辆高度从米转换为模型单位 - double vehicleHeightInModelUnits = vehicleHeight * UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units); - bool heightOk = cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeightInModelUnits); + // vehicleHeight已经是模型单位,直接比较即可 + bool heightOk = cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); if (!heightOk) { heightConstrainedCells++; @@ -268,7 +254,7 @@ namespace NavisworksTransport.PathPlanning // 详细日志:记录被高度约束排除的单元格(仅记录前10个避免日志过多) if (heightConstrainedCells <= 10) { - LogManager.Info($"[A*高度约束] 单元格({x},{y})被高度约束排除:车辆高度{vehicleHeight}m({vehicleHeightInModelUnits:F2}模型单位),可用区间:{string.Join(", ", cell.PassableHeights.Select(i => $"{i.MinZ:F2}-{i.MaxZ:F2}(跨度{i.GetSpan():F2})"))}"); + LogManager.Info($"[A*高度约束] 单元格({x},{y})被高度约束排除:车辆高度{vehicleHeight:F2}模型单位,可用区间:{string.Join(", ", cell.PassableHeights.Select(i => $"{i.MinZ:F2}-{i.MaxZ:F2}(跨度{i.GetSpan():F2})"))}"); } else if (heightConstrainedCells == 11) { @@ -297,7 +283,7 @@ namespace NavisworksTransport.PathPlanning bool rightPassable = rightCell.IsWalkable; if (rightPassable && rightCell.PassableHeights != null && rightCell.PassableHeights.Any()) { - rightPassable = rightCell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeightInModelUnits); + rightPassable = rightCell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); } if (rightPassable) { @@ -314,7 +300,7 @@ namespace NavisworksTransport.PathPlanning bool bottomPassable = bottomCell.IsWalkable; if (bottomPassable && bottomCell.PassableHeights != null && bottomCell.PassableHeights.Any()) { - bottomPassable = bottomCell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeightInModelUnits); + bottomPassable = bottomCell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); } if (bottomPassable) { @@ -348,7 +334,7 @@ namespace NavisworksTransport.PathPlanning /// /// 网格地图 /// 通道覆盖数据 - /// 车辆高度 + /// 车辆高度(模型单位) /// 起点坐标 /// 终点坐标 /// 路径规划策略 @@ -381,7 +367,7 @@ namespace NavisworksTransport.PathPlanning /// /// 网格地图 /// 通道覆盖数据 - /// 车辆高度 + /// 车辆高度(模型单位) /// 起点坐标 /// 终点坐标 /// A*网格 @@ -429,6 +415,7 @@ namespace NavisworksTransport.PathPlanning bool isPassable = cell.IsWalkable; if (isPassable && cell.PassableHeights != null && cell.PassableHeights.Any()) { + // vehicleHeight已经是模型单位,直接比较即可 bool heightOk = cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); if (!heightOk) { @@ -551,7 +538,7 @@ namespace NavisworksTransport.PathPlanning /// 检查单元格是否满足高度约束 /// /// 网格单元格 - /// 车辆高度 + /// 车辆高度(模型单位) /// 是否可通行 private bool IsPassableWithHeight(GridCell cell, double vehicleHeight) { @@ -560,6 +547,7 @@ namespace NavisworksTransport.PathPlanning if (cell.PassableHeights != null && cell.PassableHeights.Any()) { + // vehicleHeight已经是模型单位,直接比较即可 return cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); } @@ -572,7 +560,7 @@ namespace NavisworksTransport.PathPlanning /// 起始网格位置 /// 方向 (1,0)=右, (0,1)=下, (-1,0)=左, (0,-1)=上 /// 网格地图 - /// 车辆高度 + /// 车辆高度(模型单位) /// 直线距离(网格单位) private int CalculateStraightDistance(GridPoint2D startPos, GridPoint2D direction, GridMap gridMap, double vehicleHeight) @@ -598,7 +586,7 @@ namespace NavisworksTransport.PathPlanning /// /// 网格位置 /// 网格地图 - /// 车辆高度 + /// 车辆高度(模型单位) /// 是否有效且可通行 private bool IsValidAndPassable(GridPoint2D pos, GridMap gridMap, double vehicleHeight) { @@ -1255,14 +1243,14 @@ namespace NavisworksTransport.PathPlanning /// /// 检查点 /// 网格地图 - /// 车辆高度 + /// 车辆高度(模型单位) /// 是否可通行 /// /// 检查指定点在给定高度约束下是否可通行 /// /// 检查点 /// 网格地图 - /// 车辆高度 + /// 车辆高度(模型单位) /// 是否可通行 private bool IsPointPassableAtHeight(Point3D point, GridMap gridMap, double vehicleHeight) { @@ -1332,7 +1320,7 @@ namespace NavisworksTransport.PathPlanning /// /// 原始路径 /// 网格地图 - /// 车辆高度 + /// 车辆高度(模型单位) /// 插值后的路径 private List InterpolatePathHeights2_5D(List path, GridMap gridMap, double vehicleHeight) { @@ -1378,7 +1366,7 @@ namespace NavisworksTransport.PathPlanning /// /// 世界坐标点 /// 网格地图 - /// 车辆高度 + /// 车辆高度(模型单位) /// 最优高度,如果无法确定则返回null private double? GetOptimalHeightFromGrid(Point3D point, GridMap gridMap, double vehicleHeight) { @@ -1401,6 +1389,7 @@ namespace NavisworksTransport.PathPlanning if (cell.PassableHeights != null && cell.PassableHeights.Any()) { // 查找满足车辆高度要求的最佳区间 + // vehicleHeight已经是模型单位,直接比较即可 var validIntervals = cell.PassableHeights .Where(interval => interval.GetSpan() >= vehicleHeight) .OrderBy(interval => interval.MinZ) @@ -1529,7 +1518,7 @@ namespace NavisworksTransport.PathPlanning /// /// 路径点列表 /// 网格地图 - /// 车辆高度 + /// 车辆高度(模型单位) private void ValidatePathHeightConstraints(List path, GridMap gridMap, double vehicleHeight) { LogManager.Info($"[路径高度验证] 开始验证 {path.Count} 个路径点的高度约束"); @@ -1729,6 +1718,7 @@ namespace NavisworksTransport.PathPlanning bool isPassable = cell.IsWalkable; if (isPassable && cell.PassableHeights != null && cell.PassableHeights.Any()) { + // vehicleHeight已经是模型单位,直接比较即可 bool heightOk = cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); if (!heightOk) { diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index 04b383e..feda929 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -155,6 +155,37 @@ namespace NavisworksTransport.UI.WPF.ViewModels { // 更新修改路径点按钮的启用状态 OnPropertyChanged(nameof(CanExecuteModifyPoint)); + + // 路径点选中可视化:将选中的路径点显示为预览样式 + try + { + if (value != null) + { + // 从PathPointViewModel创建PathPoint对象 + var pathPoint = new PathPoint( + new Point3D(value.X, value.Y, value.Z), + value.Name, + value.Type + ) + { + Id = value.Id + }; + + // 将选中的路径点设为预览样式 + PathPointRenderPlugin.Instance.RenderPreviewPoint(pathPoint); + LogManager.Info($"路径点已设为预览样式: {value.Name}"); + } + else + { + // 清除预览样式 + PathPointRenderPlugin.Instance.ClearPreviewPoint(); + LogManager.Info("已清除路径点预览样式"); + } + } + catch (Exception ex) + { + LogManager.Error($"路径点选中可视化失败: {ex.Message}"); + } } } } @@ -1264,7 +1295,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels return; } - // 2. 在Core路径中查找对应的点(通过位置和名称匹配) + // 2. 路径完整性验证:检查删除后是否至少还有2个点 + if (coreRoute.Points.Count <= 2) + { + LogManager.Warning($"路径点不足,无法删除。当前路径只有{coreRoute.Points.Count}个点,至少需要保留2个点(起点和终点)"); + UpdateMainStatus($"❌ 无法删除路径点:路径至少需要保留2个点(起点和终点),当前只有{coreRoute.Points.Count}个点"); + return; + } + + // 3. 在Core路径中查找对应的点(通过位置和名称匹配) var corePoint = coreRoute.Points.FirstOrDefault(p => p.Name == point.Name && Math.Abs(p.Position.X - point.X) < 0.001 && @@ -1279,27 +1318,49 @@ namespace NavisworksTransport.UI.WPF.ViewModels await _uiStateManager.ExecuteUIUpdateAsync(() => { - // 3. 同时删除UI和Core数据 - SelectedPathRoute.Points.Remove(point); - LogManager.Info($"已从UI删除路径点: {point.Name}"); - + // 4. 起点和终点的特殊处理逻辑 if (corePoint != null) { + int pointIndex = coreRoute.Points.IndexOf(corePoint); + + if (corePoint.Type == PathPointType.StartPoint && coreRoute.Points.Count > 1) + { + // 删除起点:下一个点变为起点 + var nextPoint = coreRoute.Points[pointIndex + 1]; + nextPoint.Type = PathPointType.StartPoint; + LogManager.Info($"起点被删除,下一个路径点 {nextPoint.Name} 已设为新的起点"); + } + else if (corePoint.Type == PathPointType.EndPoint && coreRoute.Points.Count > 1) + { + // 删除终点:上一个点变为终点 + var prevPoint = coreRoute.Points[pointIndex - 1]; + prevPoint.Type = PathPointType.EndPoint; + LogManager.Info($"终点被删除,上一个路径点 {prevPoint.Name} 已设为新的终点"); + } + // 删除Core数据 coreRoute.Points.Remove(corePoint); LogManager.Info($"已从Core数据删除路径点: {corePoint.Name}"); // 调用PathPlanningManager的3D删除方法 _pathPlanningManager.RemovePathPointFrom3D(corePoint); + + // 触发UI同步更新事件 + _pathPlanningManager.RaisePathPointsListUpdated(coreRoute, "删除路径点并重新分配类型"); + LogManager.Info("已触发PathPointsListUpdated事件,UI将自动同步"); } - // 4. 更新UI状态 - UpdateMainStatus($"已删除路径点: {point.Name}"); + // 5. 删除UI数据(事件驱动会自动处理,这里只作为备份) + SelectedPathRoute.Points.Remove(point); + LogManager.Info($"已从UI删除路径点: {point.Name}"); + + // 6. 更新UI状态 + UpdateMainStatus($"✅ 已删除路径点: {point.Name}"); LogManager.Info($"路径点删除完成,当前UI点数: {SelectedPathRoute.Points.Count}, Core点数: {coreRoute?.Points.Count ?? 0}"); }); - // 5. 刷新3D可视化显示 + // 7. 刷新3D可视化显示(路径变更会自动更新可视化) UpdatePathVisualization(); LogManager.Info("已触发路径可视化更新");