From 174749e2874bd056865b0672016cf1466162eddf Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 4 Jan 2026 17:12:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=80=9A=E8=A1=8C=E7=A9=BA?= =?UTF-8?q?=E9=97=B4=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Core/PathPlanningManager.cs | 106 ++++++-- src/Core/PathPointRenderPlugin.cs | 254 +++++++++++------- src/UI/WPF/ViewModels/PathEditingViewModel.cs | 3 + 3 files changed, 235 insertions(+), 128 deletions(-) diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs index d9690b9..c1660da 100644 --- a/src/Core/PathPlanningManager.cs +++ b/src/Core/PathPlanningManager.cs @@ -506,6 +506,13 @@ namespace NavisworksTransport { LogManager.Info($"*** RaiseRouteGenerated被调用: {route?.Name}, Method: {generationMethod}, GridSize: {gridSize} ***"); + // 在触发事件前,进行曲线化计算和保存 + if (route != null && route.Points.Count >= 2) + { + // 使用统一的曲线化和保存方法 + RecalculateAndSaveRoute(route, $"RouteGenerated({generationMethod})"); + } + var eventArgs = new RouteGeneratedEventArgs(route, generationMethod, gridSize, _managerId); // 检查是否有订阅者 @@ -587,6 +594,41 @@ namespace NavisworksTransport { try { + // 在触发事件前,检查是否需要重新计算曲线化路径 + // 根据 updateReason 判断: + // - "编辑完成":需要重新计算(刚完成编辑) + // - "路径点修改完成":需要重新计算(位置变化) + // - "添加路径点":需要重新计算(新增点) + // - "确认添加预览点":需要重新计算(新增点) + // - "删除路径点":需要重新计算(删除点) + bool needRecalculate = false; + + if (route != null && route.Points.Count >= 2) + { + // 检查 Edges 是否为空 + if (route.Edges == null || route.Edges.Count == 0) + { + needRecalculate = true; + LogManager.Info($"路径 Edges 为空,需要重新计算: {route.Name}"); + } + // 根据更新原因判断 + else if (updateReason == "编辑完成" || + updateReason == "路径点修改完成" || + updateReason == "添加路径点" || + updateReason == "确认添加预览点" || + updateReason == "删除路径点并重新分配类型") + { + needRecalculate = true; + LogManager.Info($"根据更新原因需要重新计算曲线化: {route.Name}, 原因: {updateReason}"); + } + } + + if (needRecalculate) + { + // 使用统一的曲线化和保存方法 + RecalculateAndSaveRoute(route, $"PathPointsListUpdated({updateReason})"); + } + var eventArgs = new PathPointsListUpdatedEventArgs(route, updateReason, _managerId); if (_uiStateManager != null) @@ -1045,8 +1087,6 @@ namespace NavisworksTransport if (!_routes.Contains(autoRoute)) { _routes.Add(autoRoute); - // 保存自动生成的路径到数据库 - SavePathToDatabase(autoRoute); } SetCurrentRouteInternal(autoRoute, triggerEvent: true); @@ -1465,18 +1505,6 @@ namespace NavisworksTransport } } - // === 应用曲线化 === - if (CurrentRoute != null) - { - double samplingStep = ConfigManager.Instance.Current.PathEditing.ArcSamplingStep; - - // 使用配置文件中的默认转弯半径 - CurrentRoute.TurnRadius = ConfigManager.Instance.Current.PathEditing.DefaultPathTurnRadius; - - PathCurveEngine.ApplyCurvatureToRoute(CurrentRoute, samplingStep); - LogManager.Info($"路径曲线化完成: {CurrentRoute.Name}, 转弯半径: {CurrentRoute.TurnRadius:F2}m, 边数: {CurrentRoute.Edges.Count}"); - } - // 如果是创建模式,将当前路径添加到路径集合 if (_pathEditState == PathEditState.Creating && CurrentRoute != null) { @@ -1484,9 +1512,6 @@ namespace NavisworksTransport { _routes.Add(CurrentRoute); - // 保存到数据库 - SavePathToDatabase(CurrentRoute); - // 添加历史记录 var historyEntry = new PathHistoryEntry( CurrentRoute.Id, @@ -1500,9 +1525,6 @@ namespace NavisworksTransport // 如果是编辑模式,保存编辑历史 if (_pathEditState == PathEditState.Editing && EditingRoute != null) { - // 更新数据库 - SavePathToDatabase(EditingRoute); - var historyEntry = new PathHistoryEntry( EditingRoute.Id, PathHistoryOperationType.Edited, @@ -1652,9 +1674,6 @@ namespace NavisworksTransport // 触发路径点列表更新事件 RaisePathPointsListUpdated(CurrentRoute, "路径点修改完成"); - // 保存到数据库 - SavePathToDatabase(CurrentRoute); - RaiseStatusChanged($"路径点 {pointToUpdate.Name} 修改完成", PathPlanningStatusType.Success); LogManager.Info($"路径点修改完成: {pointToUpdate.Name}"); @@ -2027,9 +2046,6 @@ namespace NavisworksTransport // 触发路径列表更新事件 RaisePathPointsListUpdated(CurrentRoute, "确认添加预览点"); - // 保存到数据库 - SavePathToDatabase(CurrentRoute); - return confirmPoint; } catch (Exception ex) @@ -2876,6 +2892,40 @@ namespace NavisworksTransport #endregion + #region 路径计算和保存 + + /// + /// 重新计算曲线化路径并保存到数据库 + /// + /// 要计算的路径 + /// 上下文信息(用于日志) + private void RecalculateAndSaveRoute(PathRoute route, string context) + { + if (route == null || route.Points.Count < 2) + { + LogManager.Info($"跳过曲线化计算(路径点不足): {context}"); + return; + } + + try + { + double samplingStep = ConfigManager.Instance.Current.PathEditing.ArcSamplingStep; + route.TurnRadius = ConfigManager.Instance.Current.PathEditing.DefaultPathTurnRadius; + PathCurveEngine.ApplyCurvatureToRoute(route, samplingStep); + LogManager.Info($"路径曲线化完成({context}): {route.Name}, 转弯半径: {route.TurnRadius:F2}m, 边数: {route.Edges.Count}"); + + // 曲线化计算完成后,立即保存数据库 + SavePathToDatabase(route); + LogManager.Info($"路径曲线化后已保存到数据库: {route.Name}"); + } + catch (Exception ex) + { + LogManager.Error($"路径曲线化和保存失败({context}): {ex.Message}", ex); + } + } + + #endregion + #region 公共访问方法 /// @@ -2892,7 +2942,9 @@ namespace NavisworksTransport public void UpdateRoute(PathRoute route) { if (route == null) return; - SavePathToDatabase(route); + + // 使用统一的曲线化和保存方法 + RecalculateAndSaveRoute(route, "UpdateRoute"); } #endregion diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs index 58df052..9cd6c15 100644 --- a/src/Core/PathPointRenderPlugin.cs +++ b/src/Core/PathPointRenderPlugin.cs @@ -250,10 +250,20 @@ namespace NavisworksTransport public List SampledPoints { get; set; } /// - /// 带状连线高度(仅用于 RibbonLine 模式) + /// 带状连线高度(仅用于 RibbonLine 和 VehicleSpace 模式) /// public double Height { get; set; } + /// + /// 带状连线宽度(仅用于 VehicleSpace 模式) + /// + public double Width { get; set; } + + /// + /// 透明度(0.0=完全透明,1.0=完全不透明) + /// + public double Opacity { get; set; } = 1.0; + public override string ToString() { return $"LineMarker[{FromIndex}->{ToIndex}, 类型={SegmentType}, 起点=({StartPoint.X:F2},{StartPoint.Y:F2},{StartPoint.Z:F2}), 终点=({EndPoint.X:F2},{EndPoint.Y:F2},{EndPoint.Z:F2})]"; @@ -507,39 +517,50 @@ namespace NavisworksTransport // 渲染控制点连线(半透明) foreach (var controlLineMarker in visualization.ControlLineMarkers) { - graphics.Color(controlLineMarker.Color, 0.3); // 30%透明度 + var style = GetRenderStyle(RenderStyleName.PreviewLine); + graphics.Color(style.Color, style.Alpha); // 完全使用PreviewLine的颜色和透明度 graphics.Cylinder(controlLineMarker.StartPoint, controlLineMarker.EndPoint, controlLineMarker.Radius); } } - // 渲染真实路径可视化(Edges + 切点) - // 先渲染切点(在底层) - foreach (var tangentMarker in visualization.TangentMarkers) + // 渲染真实路径可视化(根据模式选择) + if (_visualizationMode == PathVisualizationMode.VehicleSpace) { - graphics.Color(tangentMarker.Color, tangentMarker.Alpha); - RenderSquareMarker(graphics, tangentMarker); - } - - // 再渲染路径连线(直线段和圆弧段) - foreach (var pathLineMarker in visualization.PathLineMarkers) - { - // 圆弧段使用半透明灰色,直线段使用正常颜色 - if (pathLineMarker.SegmentType == PathSegmentType.Arc) + // VehicleSpace 模式:使用车辆通行空间渲染(高高度长方体) + foreach (var pathLineMarker in visualization.PathLineMarkers) { - graphics.Color(GetRenderStyle(RenderStyleName.PreviewLine).Color, 0.5); // 半透明灰色 + RenderRibbonLineMarker(graphics, pathLineMarker); } - else - { - graphics.Color(pathLineMarker.Color, 1.0); // 不透明 - } - RenderLineMarker(graphics, pathLineMarker); } - - // 渲染车辆通行空间(矩形通道) - foreach (var vehicleSpaceMarker in visualization.VehicleSpaceMarkers) + else if (_visualizationMode == PathVisualizationMode.RibbonLine) { - graphics.Color(vehicleSpaceMarker.Color, vehicleSpaceMarker.Alpha); - RenderVehicleSpace(graphics, vehicleSpaceMarker); + // RibbonLine 模式:使用带状连线渲染(低高度长方体) + // 先渲染切点(在底层) + foreach (var tangentMarker in visualization.TangentMarkers) + { + RenderSquareMarker(graphics, tangentMarker); + } + + // 再渲染路径连线 + foreach (var pathLineMarker in visualization.PathLineMarkers) + { + RenderRibbonLineMarker(graphics, pathLineMarker); + } + } + else + { + // StandardLine 模式:使用圆柱体渲染 + // 先渲染切点(在底层) + foreach (var tangentMarker in visualization.TangentMarkers) + { + RenderSquareMarker(graphics, tangentMarker); + } + + // 再渲染路径连线 + foreach (var pathLineMarker in visualization.PathLineMarkers) + { + RenderLineMarker(graphics, pathLineMarker); + } } } @@ -910,23 +931,11 @@ namespace NavisworksTransport // 构建控制点连线(用户意图,半透明) BuildControlLines(visualization, sortedPoints); - // 构建路径连线(真实路径,不透明,仅当Edges存在时) + // 所有模式都使用曲线化后的路径(Edges) if (visualization.PathRoute.Edges != null && visualization.PathRoute.Edges.Count > 0) { BuildPathLines(visualization, sortedPoints); } - else if (_visualizationMode == PathVisualizationMode.VehicleSpace) - { - // 构建车辆通行空间标记 - for (int i = 0; i < sortedPoints.Count - 1; i++) - { - var currentPoint = sortedPoints[i]; - var nextPoint = sortedPoints[i + 1]; - - var vehicleSpaceMarker = CreateVehicleSpaceMarker(currentPoint, nextPoint); - visualization.VehicleSpaceMarkers.Add(vehicleSpaceMarker); - } - } // 如果路径未完成,添加灰色的原始终点标记 if (!visualization.PathRoute.IsComplete && visualization.PathRoute.OriginalEndPoint != null) @@ -968,7 +977,7 @@ namespace NavisworksTransport { StartPoint = currentPoint.Position, EndPoint = nextPoint.Position, - Color = GetRenderStyle(RenderStyleName.Line).Color, + Color = GetRenderStyle(RenderStyleName.PreviewLine).Color, Radius = GetLineRadius() * 0.5, // 比实际路径细 SegmentType = PathSegmentType.Straight, FromIndex = currentPoint.Index, @@ -1008,8 +1017,46 @@ namespace NavisworksTransport } } - // 计算带状连线的高度(比切点标记低:GetLineRadius() * 0.2) - double ribbonHeight = GetLineRadius() * 0.2; + // 获取单位转换系数 + double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); + + // 根据可视化模式计算高度和宽度 + double heightInMeters; + double widthInMeters; + Color lineColor; + double lineOpacity; + + if (_visualizationMode == PathVisualizationMode.VehicleSpace) + { + // VehicleSpace 模式:使用车辆高度和宽度 + heightInMeters = _vehicleHeight + _safetyMargin; + widthInMeters = Math.Max(_vehicleLength, _vehicleWidth) + 2 * _safetyMargin; + 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; // 3. 构建路径连线(Edges包含直线段和圆弧段) foreach (var edge in edges) @@ -1023,11 +1070,13 @@ namespace NavisworksTransport { StartPoint = edge.SampledPoints.First(), EndPoint = edge.SampledPoints.Last(), - Color = GetRenderStyle(RenderStyleName.Line).Color, + Color = lineColor, Radius = GetLineRadius(), SegmentType = PathSegmentType.Straight, SampledPoints = edge.SampledPoints, - Height = ribbonHeight // 设置带状连线高度 + Height = heightInModelUnits, // 根据模式设置高度 + Width = widthInModelUnits, // 根据模式设置宽度 + Opacity = lineOpacity // 根据模式设置透明度 }; visualization.PathLineMarkers.Add(lineMarker); } @@ -1039,17 +1088,22 @@ namespace NavisworksTransport { StartPoint = edge.Trajectory.Ts, EndPoint = edge.Trajectory.Te, - Color = GetRenderStyle(RenderStyleName.Line).Color, + Color = lineColor, Radius = GetLineRadius(), SegmentType = PathSegmentType.Arc, Trajectory = edge.Trajectory, SampledPoints = edge.SampledPoints, - Height = ribbonHeight // 设置带状连线高度 + Height = heightInModelUnits, // 根据模式设置高度 + Width = widthInModelUnits, // 根据模式设置宽度 + Opacity = lineOpacity // 根据模式设置透明度 }; visualization.PathLineMarkers.Add(arcMarker); - // 添加切点标记 - AddTangentMarkers(visualization, edge); + // 添加切点标记(StandardLine 和 RibbonLine 模式才添加) + if (_visualizationMode != PathVisualizationMode.VehicleSpace) + { + AddTangentMarkers(visualization, edge); + } } } } @@ -1263,7 +1317,7 @@ namespace NavisworksTransport return new RenderStyle(Color.FromByteRGB(33, 150, 243), 0.9); // Material Blue路径点,10%透明 case RenderStyleName.Line: - return new RenderStyle(Color.FromByteRGB(255, 152, 0), 0.85); // Material Orange连线,15%透明 + return new RenderStyle(Color.FromByteRGB(255, 152, 0), 0.8); // Material Orange连线,20%透明 case RenderStyleName.VehicleSpace: return new RenderStyle(Color.FromByteRGB(158, 158, 158), 0.4); // Material Grey车辆空间,60%透明 @@ -1272,7 +1326,7 @@ namespace NavisworksTransport return new RenderStyle(Color.White, 0.7); // 白色预览点,30%透明 case RenderStyleName.PreviewLine: - return new RenderStyle(Color.FromByteRGB(128, 128, 128), 0.7); // 灰色预览连线,30%透明 + return new RenderStyle(Color.FromByteRGB(128, 128, 128), 0.5); // 灰色预览连线,50%透明 case RenderStyleName.UnreachedEndPoint: return new RenderStyle(Color.FromByteRGB(139, 0, 0), 0.7); // 深红色未到达终点,30%透明 @@ -1749,13 +1803,13 @@ namespace NavisworksTransport double baseRadiusInMeters; if (pointType == PathPointType.WayPoint) { - // 路径点为标准尺寸的80% - baseRadiusInMeters = standardRadiusInMeters * 0.8; + // 路径点为标准尺寸的1/4 + baseRadiusInMeters = standardRadiusInMeters * 0.25; } else { - // 起点/终点使用标准尺寸(100%) - baseRadiusInMeters = standardRadiusInMeters * 1.0; + // 起点/终点使用标准尺寸的1/2 + baseRadiusInMeters = standardRadiusInMeters * 0.5; } // 获取真实文档单位转换系数 @@ -1803,9 +1857,7 @@ namespace NavisworksTransport /// 终点 /// 宽度 /// 高度 - /// 颜色 - /// 透明度 - private void RenderCuboidMarker(Graphics graphics, Point3D startPoint, Point3D endPoint, double width, double height, Color color, double alpha) + private void RenderCuboidMarker(Graphics graphics, Point3D startPoint, Point3D endPoint, double width, double height) { try { @@ -1856,9 +1908,6 @@ namespace NavisworksTransport var yVector = new Vector3D(right.X * width, right.Y * width, right.Z * width); // 宽度方向 var zVector = new Vector3D(0, 0, height); // 高度方向 - // 设置颜色和透明度 - graphics.Color(color, alpha); - // 使用Cuboid API渲染长方体 graphics.Cuboid(origin, xVector, yVector, zVector, true); } @@ -1876,14 +1925,14 @@ namespace NavisworksTransport private void RenderVehicleSpace(Graphics graphics, VehicleSpaceMarker vehicleSpace) { var style = GetRenderStyle(RenderStyleName.VehicleSpace); + // 设置颜色和透明度 + graphics.Color(vehicleSpace.Color, vehicleSpace.Alpha); RenderCuboidMarker( graphics, vehicleSpace.StartPoint, vehicleSpace.EndPoint, vehicleSpace.Width, - vehicleSpace.Height, - vehicleSpace.Color, - vehicleSpace.Alpha + vehicleSpace.Height ); } @@ -1894,17 +1943,41 @@ namespace NavisworksTransport /// 连线标记 private void RenderRibbonLineMarker(Graphics graphics, LineMarker lineMarker) { - // 计算带状连线的宽度(直径) - var width = lineMarker.Radius * 2; - RenderCuboidMarker( - graphics, - lineMarker.StartPoint, - lineMarker.EndPoint, - width, - lineMarker.Height, - lineMarker.Color, - 1.0 // 带状连线使用不透明 - ); + // 使用 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) + { + // 圆弧段:多段长方体拼接 + for (int i = 0; i < lineMarker.SampledPoints.Count - 1; i++) + { + // 设置颜色和透明度 + graphics.Color(lineMarker.Color, lineMarker.Opacity); + RenderCuboidMarker( + graphics, + lineMarker.SampledPoints[i], + lineMarker.SampledPoints[i + 1], + width, + lineMarker.Height + ); + } + } + else + { + // 直线段:单个长方体 + // 设置颜色和透明度 + graphics.Color(lineMarker.Color, lineMarker.Opacity); + RenderCuboidMarker( + graphics, + lineMarker.StartPoint, + lineMarker.EndPoint, + width, + lineMarker.Height + ); + } } @@ -2005,8 +2078,7 @@ namespace NavisworksTransport ); // 使用统一样式 - var style = GetRenderStyle(RenderStyleName.TangentPoint); - graphics.Color(style.Color, marker.Alpha); + graphics.Color(marker.Color, marker.Alpha); // 使用Cone API渲染圆锥体(底部半径 = radius,顶部半径 = 0) graphics.Cone(startPoint, endPoint, radius, 0); @@ -2024,36 +2096,16 @@ namespace NavisworksTransport /// 连线标记 private void RenderLineMarker(Graphics graphics, LineMarker lineMarker) { - if (_visualizationMode == PathVisualizationMode.RibbonLine) + if (_visualizationMode == PathVisualizationMode.VehicleSpace || _visualizationMode == PathVisualizationMode.RibbonLine) { - // 带状连线模式:使用长方体渲染 - if (lineMarker.SegmentType == PathSegmentType.Arc && - lineMarker.SampledPoints != null && - lineMarker.SampledPoints.Count >= 2) - { - // 圆弧段:多段长方体拼接 - for (int i = 0; i < lineMarker.SampledPoints.Count - 1; i++) - { - var segmentMarker = new LineMarker - { - StartPoint = lineMarker.SampledPoints[i], - EndPoint = lineMarker.SampledPoints[i + 1], - Radius = lineMarker.Radius, - Height = lineMarker.Height, - Color = lineMarker.Color - }; - RenderRibbonLineMarker(graphics, segmentMarker); - } - } - else - { - // 直线段:单个长方体 - RenderRibbonLineMarker(graphics, lineMarker); - } + // VehicleSpace 或 RibbonLine 模式:使用长方体渲染 + RenderRibbonLineMarker(graphics, lineMarker); } else { - // 标准连线模式:使用圆柱体渲染 + // StandardLine 模式:使用圆柱体渲染 + var style = GetRenderStyle(RenderStyleName.Line); + graphics.Color(lineMarker.Color, style.Alpha); if (lineMarker.SegmentType == PathSegmentType.Arc && lineMarker.SampledPoints != null && lineMarker.SampledPoints.Count >= 2) diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index 2c627c3..c8925fb 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -2735,6 +2735,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels pathViewModel.Points.Add(pointViewModel); } LogManager.Info($"已更新路径点列表,当前点数: {pathViewModel.Points.Count}"); + + // 更新真实路径可视化 + UpdatePathVisualization(); } else {