diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs index a0a7222..7786918 100644 --- a/src/Core/PathPointRenderPlugin.cs +++ b/src/Core/PathPointRenderPlugin.cs @@ -366,7 +366,7 @@ namespace NavisworksTransport private double _currentGridSizeInMeters; // 路径可视化模式配置 - private PathVisualizationMode _visualizationMode = PathVisualizationMode.StandardLine; + private PathVisualizationMode _visualizationMode = PathVisualizationMode.RibbonLine; // 网格点类型配置 private GridPointType _gridPointType = GridPointType.Rectangle; @@ -528,11 +528,7 @@ namespace NavisworksTransport graphics.Cylinder(controlLineMarker.StartPoint, controlLineMarker.EndPoint, controlLineMarker.Radius); } } - else - { - LogManager.Debug($"[Render] 路径 {visualization.PathId} 的 ShowControlVisualization 为 false,跳过控制点渲染"); - } - + // 渲染真实路径可视化(根据模式选择) if (_visualizationMode == PathVisualizationMode.VehicleSpace) { @@ -1946,6 +1942,73 @@ namespace NavisworksTransport ); } + /// + /// 渲染长方体标记(指定长度) + /// + /// 图形上下文 + /// 起点 + /// 终点 + /// 宽度 + /// 高度 + /// 指定长度(覆盖默认计算值) + private void RenderCuboidMarkerWithLength(Graphics graphics, Point3D startPoint, Point3D endPoint, double width, double height, double length) + { + try + { + // 计算方向向量 + var direction = new Vector3D( + endPoint.X - startPoint.X, + endPoint.Y - startPoint.Y, + endPoint.Z - startPoint.Z + ); + + // 归一化方向向量 + var directionLength = Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z); + if (directionLength < 0.001) // 避免零长度线段 + return; + + direction = new Vector3D(direction.X / directionLength, direction.Y / directionLength, direction.Z / directionLength); + + // 计算垂直向量(在XY平面内垂直于方向向量) + var right = new Vector3D(-direction.Y, direction.X, 0); + + // 如果方向向量垂直于XY平面,则使用不同的right向量 + if (Math.Abs(direction.Z) > 0.9) + { + right = new Vector3D(1, 0, 0); + } + + // 归一化right向量 + var rightLength = Math.Sqrt(right.X * right.X + right.Y * right.Y + right.Z * right.Z); + if (rightLength > 0.001) + { + right = new Vector3D(right.X / rightLength, right.Y / rightLength, right.Z / rightLength); + } + + // 计算长方体的边界框和向量 + var halfWidth = width / 2.0; + + // 计算立方体原点(起点左下角) + var origin = new Point3D( + startPoint.X - halfWidth * right.X, + startPoint.Y - halfWidth * right.Y, + startPoint.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); // 高度方向 + + // 使用Cuboid API渲染长方体 + graphics.Cuboid(origin, xVector, yVector, zVector, true); + } + catch (Exception ex) + { + LogManager.Error($"[长方体渲染] 渲染长方体失败: {ex.Message}", ex); + } + } + /// /// 渲染带状连线(长方体) /// @@ -1955,23 +2018,93 @@ namespace NavisworksTransport { // 使用 LineMarker 的 Width 属性(如果没有设置则使用 Radius * 2) var width = lineMarker.Width > 0 ? lineMarker.Width : lineMarker.Radius * 2; - + // 检查是否是圆弧段且有多段采样点 if (lineMarker.SegmentType == PathSegmentType.Arc && lineMarker.SampledPoints != null && lineMarker.SampledPoints.Count >= 2) { // 圆弧段:多段长方体拼接 + // 为了避免外侧出现缝隙,需要根据外侧弧线长度调整每个长方体的长度(厚度) + + // 计算外侧弧线长度 = (圆弧半径 + 宽度/2) * 偏转角 + // 注意:宽度是长方体的总宽度,所以半径是 width/2 + double outerArcRadius = width / 2.0; // 长方体的半径(宽度的一半) + double outerArcLength = 0; + double centerArcRadius = 0; + + if (lineMarker.Trajectory != null) + { + centerArcRadius = lineMarker.Trajectory.ActualRadius; + outerArcLength = (centerArcRadius + outerArcRadius) * lineMarker.Trajectory.DeflectionAngle; + } + + // 计算每个长方体在外侧应该覆盖的长度 + int segmentCount = lineMarker.SampledPoints.Count - 1; + double outerSegmentLength = outerArcLength / segmentCount; + + // 提前计算比例因子,避免在循环中重复计算 + double lengthScaleFactor = (centerArcRadius + outerArcRadius) / centerArcRadius; + + // 设置颜色和透明度 + graphics.Color(lineMarker.Color, lineMarker.Opacity); + + // 添加过渡切片(在第一个切片前) + // 从第一个切片反向画一个切片,填补外侧缝隙 + // 计算第一个切片的方向(从 Ts 到 SampledPoints[1],跳过第一个点因为它就是Ts) + var firstSliceDirection = new Vector3D( + lineMarker.SampledPoints[1].X - lineMarker.Trajectory.Ts.X, + lineMarker.SampledPoints[1].Y - lineMarker.Trajectory.Ts.Y, + lineMarker.SampledPoints[1].Z - lineMarker.Trajectory.Ts.Z + ); + var firstSliceLength = Math.Sqrt( + firstSliceDirection.X * firstSliceDirection.X + + firstSliceDirection.Y * firstSliceDirection.Y + + firstSliceDirection.Z * firstSliceDirection.Z + ); + + firstSliceDirection = new Vector3D( + firstSliceDirection.X / firstSliceLength, + firstSliceDirection.Y / firstSliceLength, + firstSliceDirection.Z / firstSliceLength + ); + + // 反向切片:从 Ts 沿第一个切片的反方向延伸 + // 使用完整的 outerSegmentLength 作为长度 + var reverseSliceEndPoint = new Point3D( + lineMarker.Trajectory.Ts.X - firstSliceDirection.X * outerSegmentLength, + lineMarker.Trajectory.Ts.Y - firstSliceDirection.Y * outerSegmentLength, + lineMarker.Trajectory.Ts.Z - firstSliceDirection.Z * outerSegmentLength + ); + + RenderCuboidMarkerWithLength( + graphics, + reverseSliceEndPoint, + lineMarker.Trajectory.Ts, + width, + lineMarker.Height, + outerSegmentLength + ); + for (int i = 0; i < lineMarker.SampledPoints.Count - 1; i++) { - // 设置颜色和透明度 - graphics.Color(lineMarker.Color, lineMarker.Opacity); - RenderCuboidMarker( + // 计算当前段的中心线长度 + double centerSegmentLength = GeometryHelper.CalculatePointDistance( + lineMarker.SampledPoints[i], + lineMarker.SampledPoints[i + 1] + ); + + // 计算长方体应该使用的长度(基于外侧弧线) + double cuboidLength = centerSegmentLength * lengthScaleFactor; + + // 渲染长方体,使用调整后的长度 + RenderCuboidMarkerWithLength( graphics, lineMarker.SampledPoints[i], lineMarker.SampledPoints[i + 1], width, - lineMarker.Height + lineMarker.Height, + cuboidLength ); } } @@ -2121,9 +2254,26 @@ namespace NavisworksTransport lineMarker.SampledPoints.Count >= 2) { // 圆弧段:多段圆柱体 + // 为了避免外侧出现缝隙,需要根据外侧弧线长度调整每个圆柱体的厚度(半径) + double cylinderRadius = lineMarker.Radius; // 默认使用原始半径 + + if (lineMarker.Trajectory != null) + { + // 计算外侧弧线长度 = (圆弧半径 + 连线半径) * 偏转角 + double outerArcLength = (lineMarker.Trajectory.ActualRadius + lineMarker.Radius) * lineMarker.Trajectory.DeflectionAngle; + + // 计算每个圆柱体在外侧应该覆盖的长度 + int segmentCount = lineMarker.SampledPoints.Count - 1; + double outerSegmentLength = outerArcLength / segmentCount; + + // 圆柱体的半径(厚度)应该等于外侧段长度的一半 + // 这样圆柱体在外侧的覆盖长度 = 2 * radius = outerSegmentLength,正好填满外侧弧线 + cylinderRadius = outerSegmentLength / 2.0; + } + for (int i = 0; i < lineMarker.SampledPoints.Count - 1; i++) { - graphics.Cylinder(lineMarker.SampledPoints[i], lineMarker.SampledPoints[i + 1], lineMarker.Radius); + graphics.Cylinder(lineMarker.SampledPoints[i], lineMarker.SampledPoints[i + 1], cylinderRadius); } } else diff --git a/src/UI/WPF/Views/PathEditingView.xaml b/src/UI/WPF/Views/PathEditingView.xaml index 9f78557..9b37d54 100644 --- a/src/UI/WPF/Views/PathEditingView.xaml +++ b/src/UI/WPF/Views/PathEditingView.xaml @@ -449,18 +449,12 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管 VerticalAlignment="Center" Margin="5,0,0,0"> - - + ToolTip="显示地表连线(长方体)"/>