From bc1ad3f4c3ed057e41fd9b0e08c3a769e8e3d7b8 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 18 Jan 2026 16:25:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=80=9A=E8=A1=8C=E7=A9=BA?= =?UTF-8?q?=E9=97=B4=E5=8F=AF=E8=A7=86=E5=8C=96=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=A0=B9=E6=8D=AE=E8=B7=AF=E5=BE=84=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=88=87=E6=8D=A2=E5=8F=AF=E8=A7=86=E5=8C=96=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=B9=B6=E4=BC=98=E5=8C=96=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFLOW.md | 26 ++ default_config.toml | 2 +- src/Core/PathPointRenderPlugin.cs | 245 ++++++++++++++++-- .../ViewModels/AnimationControlViewModel.cs | 214 +++++++++++++++ src/UI/WPF/ViewModels/PathEditingViewModel.cs | 44 ++++ 5 files changed, 507 insertions(+), 24 deletions(-) diff --git a/IFLOW.md b/IFLOW.md index 6616c24..6553336 100644 --- a/IFLOW.md +++ b/IFLOW.md @@ -292,6 +292,32 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log ## 开发约定 +### 代码分析方法 + +**重要教训:分析可视化功能时的错误思考方式** + +在分析"通行空间可视化"功能时,犯了以下错误: + +1. **错误:只看方法名,不看实现** + - 搜索到 `UpdateGridVisualizationSettings()` 等方法名中包含"Grid",就误以为需要生成网格地图 + - 实际上这里的"网格"指的是路径规划用的网格地图,而通行空间可视化只是改变路径的渲染风格 + +2. **错误:没有深入查看渲染代码** + - 虽然找到了 `PathPointRenderPlugin.cs`,但没有仔细看它的渲染逻辑 + - 应该先看渲染插件如何渲染路径,发现 `PathVisualizationMode.VehicleSpace` 模式 + +3. **错误:被表面信息误导** + - 日志显示"[网格可视化] 无缓存网格地图",就以为需要生成网格地图 + - 实际上通行空间可视化不是生成新的可视化内容,而是改变现有路径的渲染方式(从圆柱体改为高高度长方体) + +**正确的分析方法应该是**: + +1. **先看渲染代码**:对于可视化功能,应该先看渲染插件的代码(如 `PathPointRenderPlugin.cs`) +2. **理解本质**:问自己"这个功能是什么",而不是"怎么做"。通行空间可视化是"改变渲染风格",而不是"生成网格地图" +3. **查看现有实现**:如果日志中已经有相关提示(如"路径可视化模式已更改: 车辆通行空间"),说明功能已经存在,只需要在合适的时机调用 +4. **不要只看方法名**:方法名可能误导,要看实际实现代码 +5. **深入查看核心代码**:对于可视化、动画等功能,应该先看对应的渲染/动画管理器的代码 + ### 架构模式 - **MVVM模式**: WPF UI开发使用MVVM架构 diff --git a/default_config.toml b/default_config.toml index 8d3d81e..9936f43 100644 --- a/default_config.toml +++ b/default_config.toml @@ -9,7 +9,7 @@ cell_size_meters = 0.5 max_height_diff_meters = 0.35 # 车辆长度(米) -vehicle_length_meters = 1.0 +vehicle_length_meters = 1.2 # 车辆宽度(米) vehicle_width_meters = 1.0 diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs index 3838e75..baade85 100644 --- a/src/Core/PathPointRenderPlugin.cs +++ b/src/Core/PathPointRenderPlugin.cs @@ -1244,7 +1244,7 @@ namespace NavisworksTransport // 地面路径使用曲线化后的路径(Edges) // 空轨路径只显示控制点连线,不显示路径连线 - // 吊装路径使用控制点连线,不显示路径连线 + // 吊装路径使用控制点连线,不显示路径连线(除非启用通行空间可视化) if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Rail) { // 空轨路径:只显示控制点连线,不构建路径连线 @@ -1252,8 +1252,17 @@ namespace NavisworksTransport } else if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Hoisting) { - // 吊装路径:只显示控制点连线,不构建路径连线 - LogManager.Debug($"[路径渲染] 吊装路径只显示控制点连线,不显示路径连线"); + // 吊装路径:默认只显示控制点连线 + // 但如果启用了通行空间可视化模式,则构建路径连线以显示通行空间 + if (_visualizationMode == PathVisualizationMode.VehicleSpace) + { + LogManager.Debug($"[路径渲染] 吊装路径启用通行空间可视化,构建路径连线"); + BuildPathLines(visualization, sortedPoints); + } + else + { + LogManager.Debug($"[路径渲染] 吊装路径只显示控制点连线,不显示路径连线"); + } } else if (visualization.PathRoute.Edges != null && visualization.PathRoute.Edges.Count > 0) { @@ -1327,6 +1336,14 @@ namespace NavisworksTransport { var edges = visualization.PathRoute.Edges; + // 处理 Edges 为空的情况(如吊装路径未经过曲线化) + if (edges == null || edges.Count == 0) + { + LogManager.Debug($"[路径渲染] Edges 为空,直接使用路径点构建直线连线"); + BuildDirectPathLines(visualization, sortedPoints); + return; + } + // 1. 收集在路径上的控制点ID var onPathPointIds = new HashSet(); foreach (var edge in edges) @@ -1360,8 +1377,21 @@ namespace NavisworksTransport if (_visualizationMode == PathVisualizationMode.VehicleSpace) { // VehicleSpace 模式:使用车辆高度和宽度 - heightInMeters = _vehicleHeight + _safetyMargin; - widthInMeters = Math.Max(_vehicleLength, _vehicleWidth) + 2 * _safetyMargin; + // 根据路径类型调整参数 + if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Hoisting) + { + // 吊装路径:通行空间截面使用车辆的宽度和长度(不是高度) + heightInMeters = _vehicleLength + _safetyMargin; // Y方向使用车辆长度 + widthInMeters = _vehicleWidth + 2 * _safetyMargin; // X方向使用车辆宽度 + LogManager.Info($"[通行空间渲染] 吊装路径: 车辆参数(长×宽×高)={_vehicleLength:F2}×{_vehicleWidth:F2}×{_vehicleHeight:F2}m, 安全间隙={_safetyMargin:F2}m, 通行空间截面(宽×高)={widthInMeters:F2}×{heightInMeters:F2}m"); + } + else + { + // 地面路径和空轨:使用最大宽度和高度 + heightInMeters = _vehicleHeight + _safetyMargin; + widthInMeters = _vehicleWidth + 2 * _safetyMargin; + LogManager.Info($"[通行空间渲染] {visualization.PathRoute.PathType}路径: 车辆参数(长×宽×高)={_vehicleLength:F2}×{_vehicleWidth:F2}×{_vehicleHeight:F2}m, 安全间隙={_safetyMargin:F2}m, 通行空间截面(宽×高)={widthInMeters:F2}×{heightInMeters:F2}m"); + } var renderStyle = GetRenderStyle(RenderStyleName.VehicleSpace); lineColor = renderStyle.Color; lineOpacity = renderStyle.Alpha; @@ -1389,6 +1419,23 @@ namespace NavisworksTransport double heightInModelUnits = heightInMeters * metersToModelUnits; double widthInModelUnits = widthInMeters * metersToModelUnits; + // 计算通行空间的垂直偏移(根据路径类型) + double verticalOffset = 0; + if (_visualizationMode == PathVisualizationMode.VehicleSpace) + { + if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Ground) + { + // 地面路径:路径点是地面位置,通行空间底面在地面,中心需要向上偏移半个高度 + verticalOffset = heightInModelUnits / 2.0; + } + else if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Rail) + { + // 空轨路径:路径点是空轨下表面位置,通行空间顶面在空轨下表面,中心需要向下偏移半个高度 + verticalOffset = -heightInModelUnits / 2.0; + } + // 吊装路径:路径点是设备中心位置,不需要偏移 + } + // 3. 构建路径连线(Edges包含直线段和圆弧段) foreach (var edge in edges) { @@ -1397,10 +1444,17 @@ namespace NavisworksTransport // 直线段:检查SampledPoints是否有效 if (edge.SampledPoints != null && edge.SampledPoints.Count >= 2) { + var startPoint = edge.SampledPoints.First(); + var endPoint = edge.SampledPoints.Last(); + + // 根据路径类型调整起点和终点位置 + var adjustedStartPoint = new Point3D(startPoint.X, startPoint.Y, startPoint.Z + verticalOffset); + var adjustedEndPoint = new Point3D(endPoint.X, endPoint.Y, endPoint.Z + verticalOffset); + var lineMarker = new LineMarker { - StartPoint = edge.SampledPoints.First(), - EndPoint = edge.SampledPoints.Last(), + StartPoint = adjustedStartPoint, + EndPoint = adjustedEndPoint, Color = lineColor, Radius = GetLineRadius(), SegmentType = PathSegmentType.Straight, @@ -1429,10 +1483,17 @@ namespace NavisworksTransport arcLineOpacity = warningStyle.Alpha; } + var startPoint = edge.Trajectory.Ts; + var endPoint = edge.Trajectory.Te; + + // 根据路径类型调整起点和终点位置 + var adjustedStartPoint = new Point3D(startPoint.X, startPoint.Y, startPoint.Z + verticalOffset); + var adjustedEndPoint = new Point3D(endPoint.X, endPoint.Y, endPoint.Z + verticalOffset); + var arcMarker = new LineMarker { - StartPoint = edge.Trajectory.Ts, - EndPoint = edge.Trajectory.Te, + StartPoint = adjustedStartPoint, + EndPoint = adjustedEndPoint, Color = arcLineColor, Radius = GetLineRadius(), SegmentType = PathSegmentType.Arc, @@ -1453,6 +1514,114 @@ namespace NavisworksTransport } } + /// + /// 直接使用路径点构建直线连线(用于吊装路径等未曲线化的路径) + /// + /// 路径可视化对象 + /// 排序后的路径点列表 + private void BuildDirectPathLines(PathVisualization visualization, List sortedPoints) + { + if (sortedPoints == null || sortedPoints.Count < 2) + return; + + // 获取单位转换系数 + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); + + // 根据可视化模式计算高度和宽度 + double heightInMeters; + double widthInMeters; + Color lineColor; + double lineOpacity; + + if (_visualizationMode == PathVisualizationMode.VehicleSpace) + { + // VehicleSpace 模式:使用车辆高度和宽度 + // 根据路径类型调整参数 + if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Hoisting) + { + // 吊装路径:通行空间截面使用车辆的宽度和长度(不是高度) + heightInMeters = _vehicleLength + _safetyMargin; // Y方向使用车辆长度 + widthInMeters = _vehicleWidth + 2 * _safetyMargin; // X方向使用车辆宽度 + LogManager.Info($"[通行空间渲染] 吊装路径(直接连线): 车辆参数(长×宽×高)={_vehicleLength:F2}×{_vehicleWidth:F2}×{_vehicleHeight:F2}m, 安全间隙={_safetyMargin:F2}m, 通行空间截面(宽×高)={widthInMeters:F2}×{heightInMeters:F2}m"); + } + else + { + // 地面路径和空轨:使用最大宽度和高度 + heightInMeters = _vehicleHeight + _safetyMargin; + widthInMeters = _vehicleWidth + 2 * _safetyMargin; + LogManager.Info($"[通行空间渲染] {visualization.PathRoute.PathType}路径(直接连线): 车辆参数(长×宽×高)={_vehicleLength:F2}×{_vehicleWidth:F2}×{_vehicleHeight:F2}m, 安全间隙={_safetyMargin:F2}m, 通行空间截面(宽×高)={widthInMeters:F2}×{heightInMeters:F2}m"); + } + var renderStyle = GetRenderStyle(RenderStyleName.VehicleSpace); + lineColor = renderStyle.Color; + lineOpacity = renderStyle.Alpha; + } + else if (_visualizationMode == PathVisualizationMode.RibbonLine) + { + // RibbonLine 模式:使用较低的带状高度 + heightInMeters = GetLineRadius() / metersToModelUnits * 0.1; + widthInMeters = GetLineRadius() / metersToModelUnits * 2; // 路径宽度(转换为米) + var renderStyle = GetRenderStyle(RenderStyleName.Line); + lineColor = renderStyle.Color; + lineOpacity = renderStyle.Alpha; + } + else + { + // StandardLine 模式:不需要高度和宽度(圆柱体) + heightInMeters = 0; + widthInMeters = 0; + var renderStyle = GetRenderStyle(RenderStyleName.Line); + lineColor = renderStyle.Color; + lineOpacity = renderStyle.Alpha; + } + + // 转换为模型单位 + double heightInModelUnits = heightInMeters * metersToModelUnits; + double widthInModelUnits = widthInMeters * metersToModelUnits; + + // 计算通行空间的垂直偏移(根据路径类型) + double verticalOffset = 0; + if (_visualizationMode == PathVisualizationMode.VehicleSpace) + { + if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Ground) + { + // 地面路径:路径点是地面位置,通行空间底面在地面,中心需要向上偏移半个高度 + verticalOffset = heightInModelUnits / 2.0; + } + else if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Rail) + { + // 空轨路径:路径点是空轨下表面位置,通行空间顶面在空轨下表面,中心需要向下偏移半个高度 + verticalOffset = -heightInModelUnits / 2.0; + } + // 吊装路径:路径点是设备中心位置,不需要偏移 + } + + // 直接使用路径点构建直线连线 + for (int i = 0; i < sortedPoints.Count - 1; i++) + { + var startPoint = sortedPoints[i]; + var endPoint = sortedPoints[i + 1]; + + // 根据路径类型调整起点和终点位置 + var adjustedStartPoint = new Point3D(startPoint.Position.X, startPoint.Position.Y, startPoint.Position.Z + verticalOffset); + var adjustedEndPoint = new Point3D(endPoint.Position.X, endPoint.Position.Y, endPoint.Position.Z + verticalOffset); + + var lineMarker = new LineMarker + { + StartPoint = adjustedStartPoint, + EndPoint = adjustedEndPoint, + Color = lineColor, + Radius = GetLineRadius(), + SegmentType = PathSegmentType.Straight, + FromIndex = startPoint.Index, + ToIndex = endPoint.Index, + Height = heightInModelUnits, // 根据模式设置高度 + Width = widthInModelUnits, // 根据模式设置宽度 + Opacity = lineOpacity // 根据模式设置透明度 + }; + visualization.PathLineMarkers.Add(lineMarker); + } + } + /// /// 添加切点标记 /// @@ -2247,20 +2416,35 @@ namespace NavisworksTransport right = new Vector3D(right.X / rightLength, right.Y / rightLength, right.Z / rightLength); } + // 计算高度向量(垂直于方向向量和宽度向量) + var up = new Vector3D( + direction.Y * right.Z - direction.Z * right.Y, + direction.Z * right.X - direction.X * right.Z, + direction.X * right.Y - direction.Y * right.X + ); + + // 归一化up向量 + var upLength = Math.Sqrt(up.X * up.X + up.Y * up.Y + up.Z * up.Z); + if (upLength > 0.001) + { + up = new Vector3D(up.X / upLength, up.Y / upLength, up.Z / upLength); + } + // 计算长方体的边界框和向量 var halfWidth = width / 2.0; + var halfHeight = height / 2.0; - // 计算立方体原点(起点左下角) + // 计算立方体原点(使长方体中心在起点位置) var origin = new Point3D( - startPoint.X - halfWidth * right.X, - startPoint.Y - halfWidth * right.Y, - startPoint.Z + startPoint.X - halfWidth * right.X - halfHeight * up.X, + startPoint.Y - halfWidth * right.Y - halfHeight * up.Y, + startPoint.Z - halfWidth * right.Z - halfHeight * up.Z ); // 定义立方体的三个轴向量 - var xVector = new Vector3D(direction.X * length, direction.Y * length, direction.Z * length); // 长度方向 - var yVector = new Vector3D(right.X * width, right.Y * width, right.Z * width); // 宽度方向 - var zVector = new Vector3D(0, 0, height); // 高度方向 + var xVector = new Vector3D(direction.X * length, direction.Y * length, direction.Z * length); // 长度方向(沿路径) + var yVector = new Vector3D(right.X * width, right.Y * width, right.Z * width); // 宽度方向(垂直于路径) + var zVector = new Vector3D(up.X * height, up.Y * height, up.Z * height); // 高度方向(垂直于路径和宽度) // 使用Cuboid API渲染长方体 graphics.Cuboid(origin, xVector, yVector, zVector, true); @@ -2333,20 +2517,35 @@ namespace NavisworksTransport right = new Vector3D(right.X / rightLength, right.Y / rightLength, right.Z / rightLength); } + // 计算高度向量(垂直于方向向量和宽度向量) + var up = new Vector3D( + direction.Y * right.Z - direction.Z * right.Y, + direction.Z * right.X - direction.X * right.Z, + direction.X * right.Y - direction.Y * right.X + ); + + // 归一化up向量 + var upLength = Math.Sqrt(up.X * up.X + up.Y * up.Y + up.Z * up.Z); + if (upLength > 0.001) + { + up = new Vector3D(up.X / upLength, up.Y / upLength, up.Z / upLength); + } + // 计算长方体的边界框和向量 var halfWidth = width / 2.0; + var halfHeight = height / 2.0; - // 计算立方体原点(起点左下角) + // 计算立方体原点(使长方体中心在起点位置) var origin = new Point3D( - startPoint.X - halfWidth * right.X, - startPoint.Y - halfWidth * right.Y, - startPoint.Z + startPoint.X - halfWidth * right.X - halfHeight * up.X, + startPoint.Y - halfWidth * right.Y - halfHeight * up.Y, + startPoint.Z - halfWidth * right.Z - halfHeight * up.Z ); // 定义立方体的三个轴向量 - var xVector = new Vector3D(direction.X * length, direction.Y * length, direction.Z * length); // 使用指定长度 - var yVector = new Vector3D(right.X * width, right.Y * width, right.Z * width); // 宽度方向 - var zVector = new Vector3D(0, 0, height); // 高度方向 + var xVector = new Vector3D(direction.X * length, direction.Y * length, direction.Z * length); // 使用指定长度(沿路径) + var yVector = new Vector3D(right.X * width, right.Y * width, right.Z * width); // 宽度方向(垂直于路径) + var zVector = new Vector3D(up.X * height, up.Y * height, up.Z * height); // 高度方向(垂直于路径和宽度) // 使用Cuboid API渲染长方体 graphics.Cuboid(origin, xVector, yVector, zVector, true); diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs index b8a8600..620e3fd 100644 --- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs +++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs @@ -468,6 +468,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 预计算排除列表(异步) _ = PrecomputeCollisionExclusionsAsync(value); + + // 当选择了物体时,根据路径类型打开通行空间可视化 + if (value != null) + { + UpdatePassageSpaceVisualization(); + } } } } @@ -540,6 +546,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels var points = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList(); _pathAnimationManager?.MoveVehicleToPathStart(vModel, points); } + + // 当切换到虚拟车辆模式时,根据路径类型打开通行空间可视化 + UpdatePassageSpaceVisualization(); } catch (Exception ex) { @@ -2386,6 +2395,211 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } + /// + /// 根据路径类型更新通行空间可视化 + /// 空轨和吊装路径默认打开通行空间(车辆通行空间模式),地面路径不打开 + /// 根据选择的物体或虚拟车辆尺寸更新通行空间渲染 + /// + private void UpdatePassageSpaceVisualization() + { + try + { + if (CurrentPathRoute == null) + { + LogManager.Debug("[通行空间可视化] 没有选择路径,跳过更新"); + return; + } + + // 根据路径类型决定是否打开通行空间 + bool enableVehicleSpace = false; + + switch (CurrentPathRoute.PathType) + { + case NavisworksTransport.PathType.Rail: + case NavisworksTransport.PathType.Hoisting: + enableVehicleSpace = true; + LogManager.Info($"[通行空间可视化] 路径类型={CurrentPathRoute.PathType},自动切换到车辆通行空间模式"); + break; + + case NavisworksTransport.PathType.Ground: + enableVehicleSpace = false; + LogManager.Debug($"[通行空间可视化] 路径类型={CurrentPathRoute.PathType},不切换到车辆通行空间模式"); + break; + + default: + LogManager.Warning($"[通行空间可视化] 未知的路径类型: {CurrentPathRoute.PathType}"); + return; + } + + // 设置路径可视化模式 + if (enableVehicleSpace) + { + var renderPlugin = PathPointRenderPlugin.Instance; + if (renderPlugin != null) + { + // 获取车辆尺寸(米) + double vehicleLength, vehicleWidth, vehicleHeight; + + if (UseVirtualVehicle) + + { + + // 使用虚拟车辆尺寸,但需要根据路径类型调整 + + if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Hoisting) + + { + + // 吊装路径:通行空间截面使用车辆的宽度和长度(不是高度) + + vehicleWidth = VirtualVehicleWidth; // X方向 + + vehicleHeight = VirtualVehicleLength; // Y方向(使用车辆长度) + + // 计算路径的Z长度差 + + if (CurrentPathRoute.Points.Count >= 2) + + { + + var startPoint = CurrentPathRoute.Points.First(); + + var endPoint = CurrentPathRoute.Points.Last(); + + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); + + vehicleLength = Math.Abs(endPoint.Z - startPoint.Z) / metersToModelUnits; + + } + + else + + { + + vehicleLength = VirtualVehicleLength; // fallback + + } + + LogManager.Info($"[通行空间可视化] 吊装路径使用虚拟车辆尺寸: 宽×高×长={vehicleWidth:F2}×{vehicleHeight:F2}×{vehicleLength:F2}m"); + + } + + else + + { + + // 地面路径和空轨:使用虚拟车辆的宽度和高度 + + vehicleWidth = VirtualVehicleWidth; // 通行空间宽度 = 车辆宽度(垂直于路径方向) + + vehicleHeight = VirtualVehicleHeight; // 通行空间高度 = 车辆高度 + + vehicleLength = VirtualVehicleLength; // 车辆长度(沿路径方向) + + LogManager.Info($"[通行空间可视化] 地面/空轨路径使用虚拟车辆尺寸: 通行空间宽×高={vehicleWidth:F2}×{vehicleHeight:F2}m, 车辆长度={vehicleLength:F2}m"); + + } + + } + else if (SelectedAnimatedObject != null) + { + // 使用选择物体的包围盒尺寸 + try + { + var bbox = SelectedAnimatedObject.BoundingBox(); + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); + + // 将模型单位转换为米 + double sizeX = (bbox.Max.X - bbox.Min.X) / metersToModelUnits; + double sizeY = (bbox.Max.Y - bbox.Min.Y) / metersToModelUnits; + double sizeZ = (bbox.Max.Z - bbox.Min.Z) / metersToModelUnits; + + // 根据路径类型确定车辆的长度、宽度、高度 + if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Hoisting) + { + // 吊装路径(严格垂直路径): + // - 宽度 = X方向尺寸 + // - 高度 = Y方向尺寸 + // - 长度 = 路径起点到终点的Z值差 + vehicleWidth = sizeX; + vehicleHeight = sizeY; + + // 计算路径的Z长度差 + if (CurrentPathRoute.Points.Count >= 2) + { + var startPoint = CurrentPathRoute.Points.First(); + var endPoint = CurrentPathRoute.Points.Last(); + vehicleLength = Math.Abs(endPoint.Z - startPoint.Z) / metersToModelUnits; + } + else + { + vehicleLength = sizeZ; // fallback + } + + LogManager.Info($"[通行空间可视化] 吊装路径使用物体XY尺寸 ({SelectedAnimatedObject.DisplayName}): 宽={vehicleWidth:F2}m, 高={vehicleHeight:F2}m, 长度(路径Z差)={vehicleLength:F2}m"); + } + else + { + // 地面路径和空轨(可能有坡度): + // - 物体的Z轴和路径垂直 + // - 通行空间宽度 = 车辆的宽度(垂直于路径方向) + // - 通行空间高度 = 车辆的高度(Z方向) + // - 通行空间长度 = 由路径起点和终点决定 + vehicleLength = Math.Max(sizeX, sizeY); // 车辆沿路径方向的长度 + vehicleWidth = Math.Min(sizeX, sizeY); // 通行空间宽度 = 车辆的宽度(垂直于路径方向) + vehicleHeight = sizeZ; // 通行空间高度 = 车辆的高度 + LogManager.Info($"[通行空间可视化] 地面/空轨路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 通行空间宽×高={vehicleWidth:F2}×{vehicleHeight:F2}m, 车辆长度={vehicleLength:F2}m"); + } + } + catch (Exception ex) + { + LogManager.Warning($"[通行空间可视化] 获取物体包围盒失败: {ex.Message},使用默认尺寸"); + vehicleLength = 1.0; + vehicleWidth = 1.0; + vehicleHeight = 2.0; + } + } + else + { + // 没有选择物体,使用默认尺寸 + vehicleLength = 1.0; + vehicleWidth = 1.0; + vehicleHeight = 2.0; + LogManager.Info("[通行空间可视化] 使用默认车辆尺寸: 1.0×1.0×2.0m"); + } + + // 获取安全间隙(从配置) + var config = Core.Config.ConfigManager.Instance?.Current; + double safetyMargin = config?.PathEditing?.SafetyMarginMeters ?? 0.05; + + // 设置车辆参数到渲染插件 + renderPlugin.SetVehicleParameters(vehicleLength, vehicleWidth, vehicleHeight, safetyMargin); + LogManager.Info($"[通行空间可视化] 已设置车辆参数: {vehicleLength:F2}×{vehicleWidth:F2}×{vehicleHeight:F2}m, 安全间隙: {safetyMargin:F2}m"); + + // 切换到车辆通行空间模式 + renderPlugin.VisualizationMode = PathVisualizationMode.VehicleSpace; + LogManager.Info("[通行空间可视化] 已切换到车辆通行空间模式"); + + // 刷新路径可视化以应用新的尺寸 + var coreRoute = PathPlanningManager.Instance?.Routes?.FirstOrDefault(r => r.Id == CurrentPathRoute.Id); + if (coreRoute != null) + { + PathPlanningManager.Instance.DrawRouteVisualization(coreRoute, isAutoPath: false); + LogManager.Info("[通行空间可视化] 已刷新路径可视化"); + } + } + else + { + LogManager.Warning("[通行空间可视化] 无法获取PathPointRenderPlugin实例"); + } + } + } + catch (Exception ex) + { + LogManager.Error($"[通行空间可视化] 更新失败: {ex.Message}", ex); + } + } + /// /// 递归检查ModelItem是否包含几何体 /// diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index c6fdecc..2d8dfe7 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -127,6 +127,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels { _pathPlanningManager.SetCurrentRoute(coreRoute); LogManager.Info($"UI路径切换:已同步PathPlanningManager的CurrentRoute到 {value.Name}"); + + // 根据路径类型自动设置通行空间可视化 + UpdatePassageSpaceVisualizationForPathType(coreRoute.PathType); } else { @@ -2675,6 +2678,47 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } + /// + /// 根据路径类型自动设置通行空间可视化 + /// 空轨和吊装路径自动打开通行空间,地面路径不打开 + /// + private void UpdatePassageSpaceVisualizationForPathType(PathType pathType) + { + try + { + bool enableVehicleSpace = false; + + switch (pathType) + { + case PathType.Rail: + case PathType.Hoisting: + enableVehicleSpace = true; + LogManager.Info($"[路径编辑] 路径类型={pathType},自动打开通行空间可视化"); + break; + + case PathType.Ground: + enableVehicleSpace = false; + LogManager.Debug($"[路径编辑] 路径类型={pathType},不打开通行空间可视化"); + break; + + default: + LogManager.Warning($"[路径编辑] 未知的路径类型: {pathType}"); + return; + } + + // 设置通行空间可视化开关 + if (enableVehicleSpace) + { + IsVehicleSpaceMode = true; + LogManager.Info("[路径编辑] 已自动打开通行空间可视化开关"); + } + } + catch (Exception ex) + { + LogManager.Error($"[路径编辑] 设置通行空间可视化失败: {ex.Message}", ex); + } + } + /// /// 路径可视化模式变更事件处理 ///