diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs index e59992b..3ea8994 100644 --- a/src/Core/Animation/PathAnimationManager.cs +++ b/src/Core/Animation/PathAnimationManager.cs @@ -246,48 +246,11 @@ namespace NavisworksTransport.Core.Animation /// /// 仅仅将物理模型同步到路径起点(不清理数据,不重置状态) + /// 用于加载碰撞检测结果时 /// public void SyncToPathStart(ModelItem item, List points) { - try - { - if (item == null || points == null || points.Count == 0) return; - - // 更新引用,确保后续 MoveVehicleToPathStart 使用正确物体 - _animatedObject = item; - _pathPoints = new List(points); - - // 记录原始位置(用于归位) - _originalTransform = item.Transform; - _currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform); - _originalCenter = item.BoundingBox().Center; - _currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, item.BoundingBox().Min.Z); - - if (points.Count >= 2) - { - double yaw = Math.Atan2(points[1].Y - points[0].Y, points[1].X - points[0].X); - - // 🔥 根据路径类型调整起点位置 - Point3D startPosition = points[0]; - if (_route?.PathType == PathType.Rail) - { - double vehicleHeight = item.BoundingBox().Max.Z - item.BoundingBox().Min.Z; - startPosition = new Point3D(startPosition.X, startPosition.Y, startPosition.Z - vehicleHeight); - } - - UpdateObjectPosition(startPosition, yaw); - } - else - { - UpdateObjectPosition(points[0]); - } - - LogManager.Debug($"[Sync] 物体 {item.DisplayName} 已移动到起点"); - } - catch (Exception ex) - { - LogManager.Error($"[Sync] 同步到起点失败: {ex.Message}"); - } + MoveVehicleToPathStart(item, points); } /// @@ -408,28 +371,67 @@ namespace NavisworksTransport.Core.Animation /// /// 将车辆移动到路径起点 /// - private void MoveVehicleToPathStart() + /// 动画对象(可选,如果不提供则使用当前的_animatedObject) + /// 路径点(可选,如果不提供则使用当前的_pathPoints) + public void MoveVehicleToPathStart(ModelItem animatedObject = null, List pathPoints = null) { try { - if (_pathPoints.Count == 0 || _animationFrames == null || _animationFrames.Count == 0) return; - - var firstFrame = _animationFrames[0]; - - // 🔥 根据路径类型调整起点位置 - Point3D startPosition = firstFrame.Position; - if (_route.PathType == PathType.Rail) + // 如果提供了参数,更新内部状态 + if (animatedObject != null) { - // 空轨路径:路径点是空轨下表面,车辆顶面应该在这里 + _animatedObject = animatedObject; + _originalTransform = animatedObject.Transform; + _originalCenter = animatedObject.BoundingBox().Center; + _currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, animatedObject.BoundingBox().Min.Z); + + // 对于虚拟车辆,重置 _currentYaw 为 0,因为虚拟车辆刚创建时已经被重置为单位变换 + // 对于普通物体,保持原始朝向 + if (_isVirtualVehicle) + { + _currentYaw = 0.0; + } + else + { + _currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform); + } + } + + if (pathPoints != null) + { + _pathPoints = pathPoints; + } + + // 检查路径点 + if (_pathPoints == null || _pathPoints.Count < 2) + { + LogManager.Warning("[移动到起点] 没有可用的路径点"); + return; + } + + // 计算朝向(使用前两个路径点的方向) + double yaw = Math.Atan2(_pathPoints[1].Y - _pathPoints[0].Y, _pathPoints[1].X - _pathPoints[0].X); + + // 根据路径类型调整起点位置 + Point3D startPosition = _pathPoints[0]; + if (_route?.PathType == PathType.Rail) + { + // 空轨路径:points[0] 是空轨下表面位置,车辆顶面应该在这里 + // 所以车辆底面 = points[0] - 车辆高度 double vehicleHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z; startPosition = new Point3D(startPosition.X, startPosition.Y, startPosition.Z - vehicleHeight); + LogManager.Debug($"[移动到起点] 空轨路径调整: 空轨Z={_pathPoints[0].Z:F2}, 车辆底面Z={startPosition.Z:F2}, 车辆高度={vehicleHeight:F2}"); + } + else + { + // 地面路径:points[0] 是地面位置,车辆底面应该在这里 + LogManager.Debug($"[移动到起点] 地面路径: pos=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2})"); } // 使用 UpdateObjectPosition 统一处理移动和旋转 - // 这将物体从当前位置(底面中心)移动到第一帧的位置,并旋转到第一帧的朝向 - UpdateObjectPosition(startPosition, firstFrame.YawRadians); + UpdateObjectPosition(startPosition, yaw); - LogManager.Info($"物体已初始化到路径起点并对齐朝向: pos=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2}), yaw={firstFrame.YawRadians:F3}rad"); + LogManager.Info($"物体已初始化到路径起点并对齐朝向: pos=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2}), yaw={yaw:F3}rad, 路径类型={(_route?.PathType == PathType.Rail ? "空轨" : "地面")}"); } catch (Exception ex) { diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs index d0f9d44..0fa8166 100644 --- a/src/Core/Collision/ClashDetectiveIntegration.cs +++ b/src/Core/Collision/ClashDetectiveIntegration.cs @@ -347,7 +347,7 @@ namespace NavisworksTransport // 使用 PathAnimationManager 将车辆移动到起点 var pathAnimationManager = Core.Animation.PathAnimationManager.GetInstance(); - pathAnimationManager.SyncToPathStart(vehicleObject, new List { startPointPosition, startPointPosition }); + pathAnimationManager.MoveVehicleToPathStart(vehicleObject, new List { startPointPosition, startPointPosition }); LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟车辆已移动到路径起点: ({startPointPosition.X:F2}, {startPointPosition.Y:F2}, {startPointPosition.Z:F2})"); } } diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs index bfe4918..673d79f 100644 --- a/src/Core/PathPointRenderPlugin.cs +++ b/src/Core/PathPointRenderPlugin.cs @@ -1089,25 +1089,11 @@ namespace NavisworksTransport BuildControlLines(visualization, sortedPoints); // 地面路径使用曲线化后的路径(Edges) - // 空轨路径直接使用控制点连线作为路径连线 + // 空轨路径只显示控制点连线,不显示路径连线 if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Rail) { - // 空轨路径:将控制点连线复制到路径连线,使用不透明样式 - foreach (var controlLine in visualization.ControlLineMarkers) - { - var pathLineMarker = new LineMarker - { - StartPoint = controlLine.StartPoint, - EndPoint = controlLine.EndPoint, - Color = GetRenderStyle(RenderStyleName.Line).Color, - Radius = GetLineRadius(), - SegmentType = PathSegmentType.Straight, - FromIndex = controlLine.FromIndex, - ToIndex = controlLine.ToIndex - }; - visualization.PathLineMarkers.Add(pathLineMarker); - } - LogManager.Debug($"[路径渲染] 空轨路径使用控制点连线,共 {visualization.PathLineMarkers.Count} 条"); + // 空轨路径:只显示控制点连线,不构建路径连线 + LogManager.Debug($"[路径渲染] 空轨路径只显示控制点连线,不显示路径连线"); } else if (visualization.PathRoute.Edges != null && visualization.PathRoute.Edges.Count > 0) { diff --git a/src/PathPlanning/RailGeometryHelper.cs b/src/PathPlanning/RailGeometryHelper.cs index c7f806e..60c10c6 100644 --- a/src/PathPlanning/RailGeometryHelper.cs +++ b/src/PathPlanning/RailGeometryHelper.cs @@ -7,6 +7,64 @@ using NavisworksTransport.Core; namespace NavisworksTransport.PathPlanning { + /// + /// 定向包围盒(OBB) + /// + public class OrientedBoundingBox + { + /// + /// 中心点 + /// + public Point3D Center { get; set; } + + /// + /// 主轴(3个正交向量) + /// + public Point3D[] Axes { get; set; } + + /// + /// 半长(沿各轴的半长度) + /// + public Point3D Extents { get; set; } + + /// + /// 投影范围的最小 Z 值(在主轴坐标系中) + /// + public double ProjectedMinZ { get; set; } + + /// + /// 投影范围的最大 Z 值(在主轴坐标系中) + /// + public double ProjectedMaxZ { get; set; } + /// + /// 获取指定索引的轴 + /// + public Point3D GetAxis(int index) + { + if (index < 0 || index >= Axes.Length) + throw new ArgumentOutOfRangeException(nameof(index)); + return Axes[index]; + } + + /// + /// 获取指定轴的长度 + /// + public double GetAxisLength(int index) + { + if (index < 0 || index >= Axes.Length) + throw new ArgumentOutOfRangeException(nameof(index)); + + if (index == 0) + return 2 * Extents.X; + else if (index == 1) + return 2 * Extents.Y; + else if (index == 2) + return 2 * Extents.Z; + else + return 0; + } + } + /// /// 空轨几何体辅助工具 /// 负责从空轨几何体提取下表面中心线,支持拐弯和倾斜 @@ -58,11 +116,6 @@ namespace NavisworksTransport.PathPlanning /// public double TotalLength { get; set; } - /// - /// 包围盒 - /// - public BoundingBox3D Bounds { get; set; } - /// /// 起点 /// @@ -132,32 +185,18 @@ namespace NavisworksTransport.PathPlanning } } - // 提取下表面信息 - var surfaceInfo = ExtractRailBottomSurface(railModel); - if (surfaceInfo == null || surfaceInfo.BottomTriangles.Count == 0) - { - throw new InvalidOperationException($"无法从空轨模型提取下表面三角形: {railModel.DisplayName}"); - } - - LogManager.Info($"[空轨] 提取到 {surfaceInfo.BottomTriangles.Count} 个下表面三角形"); - - // 提取骨架线 - var skeleton = ExtractSkeletonFromTriangles(surfaceInfo.BottomTriangles); - LogManager.Info($"[空轨] 提取到 {skeleton.Count} 个骨架点"); - - // 沿骨架线生成采样点(完整路径) - var pathPoints = GenerateSamplePointsAlongSkeleton(skeleton, samplingInterval); + // 使用 OBB 方法提取基准路径 + var pathPoints = ExtractRailBottomCenterLineByOBB(railModel, samplingInterval); LogManager.Info($"[空轨] 生成 {pathPoints.Count} 个采样点"); // 计算路径长度 double totalLength = CalculatePathLength(pathPoints); - + // 创建基准路径对象 var baselinePath = new RailBaselinePath { PathPoints = pathPoints, TotalLength = totalLength, - Bounds = surfaceInfo.Bounds, StartPoint = pathPoints.First(), EndPoint = pathPoints.Last(), RailModel = railModel, @@ -420,6 +459,330 @@ namespace NavisworksTransport.PathPlanning } } + /// + /// 兼容方法:从空轨几何体提取下表面中心线(一步完成) + /// + /// 空轨模型项 + /// 起点(3D坐标) + /// 终点(3D坐标) + /// 采样间隔(模型单位),默认0.5 + /// 路径点列表(包含精确的Z坐标) + + /// + /// 使用 OBB 包围体提取空轨下表面中心线(新方法,支持倾斜) + /// + /// 空轨模型项 + /// 采样间隔(模型单位),默认0.5 + /// 路径点列表(下表面中心线) + public static List ExtractRailBottomCenterLineByOBB( + ModelItem railModel, + double samplingInterval = 0.5) + { + try + { + LogManager.Info($"[空轨] 使用 OBB 方法提取下表面中心线: {railModel.DisplayName}"); + LogManager.Info($"[空轨] 采样间隔: {samplingInterval:F2}"); + + // 1. 计算 OBB 包围体 + var obb = ComputeOrientedBoundingBox(railModel); + + // 2. 获取主轴(最长边,通常是空轨的长度方向) + var principalAxis = obb.GetAxis(0); + var axisLength = obb.GetAxisLength(0); + var principalAxisVector = principalAxis.ToVector3D(); + + LogManager.Info($"[空轨] OBB 主轴长度: {axisLength:F2}(模型单位)"); + LogManager.Info($"[空轨] 主轴方向: ({principalAxisVector.X:F3}, {principalAxisVector.Y:F3}, {principalAxisVector.Z:F3})"); + + // 3. 计算中心线的起点和终点(下表面) + var center = obb.Center; + var direction = principalAxis.ToVector3D(); + + // 找到最接近"向下"方向的轴(Z 轴负方向) + var downAxisIndex = 1; // 默认使用 Axis 1(次长轴) + var minZComponent = 1.0; // Z 分量的最小值 + + for (int i = 1; i < 3; i++) // 检查 Axis 1 和 Axis 2 + { + var axis = obb.GetAxis(i); + var axisVector = axis.ToVector3D(); + var zComponent = axisVector.Z; + + LogManager.Info($"[空轨] Axis {i} Z分量: {zComponent:F3}"); + + // 找到 Z 分量最小的轴(最向下,即最负) + if (zComponent < minZComponent) + { + minZComponent = zComponent; + downAxisIndex = i; + } + } + + var downAxis = obb.GetAxis(downAxisIndex); + var downAxisVector = downAxis.ToVector3D(); + + LogManager.Info($"[空轨] 下表面轴索引: {downAxisIndex}, 方向: ({downAxisVector.X:F3}, {downAxisVector.Y:F3}, {downAxisVector.Z:F3})"); + + // 计算下表面偏移(使用投影范围的 minZ) + var downOffsetZ = obb.ProjectedMinZ; + + LogManager.Info($"[空轨] 下表面偏移(投影 minZ): {downOffsetZ:F3}"); + + // 在主轴坐标系中,起点和终点的下表面位置 + // 起点:Axis0 = minX, Axis1 = 0, Axis2 = minZ + // 终点:Axis0 = maxX, Axis1 = 0, Axis2 = minZ + var axis0Min = -obb.GetAxisLength(0) / 2; + var axis0Max = obb.GetAxisLength(0) / 2; + + // 将主轴坐标系中的下表面点转换回世界坐标系 + var obbAxes = obb.Axes; + + var startProjected = new Point3D( + axis0Min, + 0, + downOffsetZ + ); + + var endProjected = new Point3D( + axis0Max, + 0, + downOffsetZ + ); + + LogManager.Info($"[空轨] 主轴坐标系起点: ({startProjected.X:F2}, {startProjected.Y:F2}, {startProjected.Z:F2})"); + LogManager.Info($"[空轨] 主轴坐标系终点: ({endProjected.X:F2}, {endProjected.Y:F2}, {endProjected.Z:F2})"); + + var startCenter = new Point3D( + center.X + startProjected.X * obbAxes[0].X + startProjected.Y * obbAxes[1].X + startProjected.Z * obbAxes[2].X, + center.Y + startProjected.X * obbAxes[0].Y + startProjected.Y * obbAxes[1].Y + startProjected.Z * obbAxes[2].Y, + center.Z + startProjected.X * obbAxes[0].Z + startProjected.Y * obbAxes[1].Z + startProjected.Z * obbAxes[2].Z + ); + + var endCenter = new Point3D( + center.X + endProjected.X * obbAxes[0].X + endProjected.Y * obbAxes[1].X + endProjected.Z * obbAxes[2].X, + center.Y + endProjected.X * obbAxes[0].Y + endProjected.Y * obbAxes[1].Y + endProjected.Z * obbAxes[2].Y, + center.Z + endProjected.X * obbAxes[0].Z + endProjected.Y * obbAxes[1].Z + endProjected.Z * obbAxes[2].Z + ); + + LogManager.Info($"[空轨] 中心线起点: ({startCenter.X:F2}, {startCenter.Y:F2}, {startCenter.Z:F2})"); + LogManager.Info($"[空轨] 中心线终点: ({endCenter.X:F2}, {endCenter.Y:F2}, {endCenter.Z:F2})"); + + // 4. 沿中心线采样 + var baseline = SampleLine(startCenter, endCenter, samplingInterval); + + LogManager.Info($"[空轨] OBB 方法完成,生成 {baseline.Count} 个采样点"); + + return baseline; + } + catch (Exception ex) + { + LogManager.Error($"[空轨] OBB 方法提取下表面中心线失败: {ex.Message}"); + throw; + } + } + + /// + /// 计算 OBB 包围体(使用 PCA 方法) + /// + private static OrientedBoundingBox ComputeOrientedBoundingBox(ModelItem modelItem) + { + try + { + // 输出 ModelItem 的 AABB 包围盒信息 + var aabb = modelItem.BoundingBox(); + LogManager.Info($"[空轨] AABB包围盒: Min({aabb.Min.X:F2}, {aabb.Min.Y:F2}, {aabb.Min.Z:F2}), Max({aabb.Max.X:F2}, {aabb.Max.Y:F2}, {aabb.Max.Z:F2})"); + LogManager.Info($"[空轨] AABB尺寸: X={aabb.Max.X - aabb.Min.X:F2}, Y={aabb.Max.Y - aabb.Min.Y:F2}, Z={aabb.Max.Z - aabb.Min.Z:F2}"); + + // 使用 GeometryHelper 提取三角形 + var triangles = GeometryHelper.ExtractTriangles(new[] { modelItem }); + + // 从三角形中提取所有顶点(去重) + var pointSet = new HashSet(); + foreach (var triangle in triangles) + { + pointSet.Add(triangle.Point1); + pointSet.Add(triangle.Point2); + pointSet.Add(triangle.Point3); + } + + var points = pointSet.ToList(); + + if (points.Count == 0) + { + throw new InvalidOperationException($"模型 {modelItem.DisplayName} 没有几何体顶点"); + } + + LogManager.Info($"[空轨] 提取到 {points.Count} 个唯一顶点"); + + // 使用 PCA 计算 OBB + return ComputeOBBByPCA(points); + } + catch (Exception ex) + { + LogManager.Error($"[空轨] 计算 OBB 失败: {ex.Message}"); + throw; + } + } + + /// + /// 使用 PCA 计算 OBB + /// + private static OrientedBoundingBox ComputeOBBByPCA(List points) + { + // 1. 计算质心 + var centroid = new Point3D( + points.Average(p => p.X), + points.Average(p => p.Y), + points.Average(p => p.Z) + ); + + LogManager.Info($"[空轨] 质心: ({centroid.X:F2}, {centroid.Y:F2}, {centroid.Z:F2})"); + + // 2. 计算协方差矩阵 + double covXX = 0, covYY = 0, covZZ = 0; + double covXY = 0, covXZ = 0, covYZ = 0; + + foreach (var point in points) + { + double dx = point.X - centroid.X; + double dy = point.Y - centroid.Y; + double dz = point.Z - centroid.Z; + + covXX += dx * dx; + covYY += dy * dy; + covZZ += dz * dz; + covXY += dx * dy; + covXZ += dx * dz; + covYZ += dy * dz; + } + + covXX /= points.Count; + covYY /= points.Count; + covZZ /= points.Count; + covXY /= points.Count; + covXZ /= points.Count; + covYZ /= points.Count; + + // 3. 计算特征值和特征向量 + var eigenvalues = new double[3]; + var eigenvectors = new Point3D[3]; + ComputeEigen3x3(covXX, covYY, covZZ, covXY, covXZ, covYZ, eigenvalues, eigenvectors); + + // 4. 按特征值降序排列(确保 Axis 0 是最长轴) + var sortedIndices = new[] { 0, 1, 2 }; + Array.Sort(sortedIndices, (i, j) => eigenvalues[j].CompareTo(eigenvalues[i])); + + var sortedEigenvalues = new double[3]; + var sortedEigenvectors = new Point3D[3]; + for (int i = 0; i < 3; i++) + { + sortedEigenvalues[i] = eigenvalues[sortedIndices[i]]; + sortedEigenvectors[i] = eigenvectors[sortedIndices[i]]; + } + + LogManager.Info($"[空轨] OBB特征值(降序): {sortedEigenvalues[0]:F4}, {sortedEigenvalues[1]:F4}, {sortedEigenvalues[2]:F4}"); + + // 5. 将点转换到主轴坐标系 + var axes = sortedEigenvectors; + var projectedPoints = new List(); + foreach (var point in points) + { + var dx = point.X - centroid.X; + var dy = point.Y - centroid.Y; + var dz = point.Z - centroid.Z; + + var projected = new Point3D( + dx * axes[0].X + dy * axes[0].Y + dz * axes[0].Z, + dx * axes[1].X + dy * axes[1].Y + dz * axes[1].Z, + dx * axes[2].X + dy * axes[2].Y + dz * axes[2].Z + ); + projectedPoints.Add(projected); + } + + // 6. 计算 AABB(在主轴坐标系中) + var minX = projectedPoints.Min(p => p.X); + var maxX = projectedPoints.Max(p => p.X); + var minY = projectedPoints.Min(p => p.Y); + var maxY = projectedPoints.Max(p => p.Y); + var minZ = projectedPoints.Min(p => p.Z); + var maxZ = projectedPoints.Max(p => p.Z); + + LogManager.Info($"[空轨] 投影范围: Axis0=[{minX:F2}, {maxX:F2}], Axis1=[{minY:F2}, {maxY:F2}], Axis2=[{minZ:F2}, {maxZ:F2}]"); + + // 7. 创建 OBB(使用投影范围的中点作为中心,而不是质心) + var obbCenter = new Point3D( + centroid.X + axes[0].X * (minX + maxX) / 2 + axes[1].X * (minY + maxY) / 2 + axes[2].X * (minZ + maxZ) / 2, + centroid.Y + axes[0].Y * (minX + maxX) / 2 + axes[1].Y * (minY + maxY) / 2 + axes[2].Y * (minZ + maxZ) / 2, + centroid.Z + axes[0].Z * (minX + maxX) / 2 + axes[1].Z * (minY + maxY) / 2 + axes[2].Z * (minZ + maxZ) / 2 + ); + + var obb = new OrientedBoundingBox + { + Center = obbCenter, + Axes = axes, + Extents = new Point3D( + (maxX - minX) / 2, + (maxY - minY) / 2, + (maxZ - minZ) / 2 + ) + }; + + // 保存投影范围信息,用于计算下表面 + obb.ProjectedMinZ = minZ; + obb.ProjectedMaxZ = maxZ; + + LogManager.Info($"[空轨] OBB各轴长度(模型单位): {obb.GetAxisLength(0):F2}, {obb.GetAxisLength(1):F2}, {obb.GetAxisLength(2):F2}"); + + // 输出 OBB 中心和轴方向 + LogManager.Info($"[空轨] OBB中心: ({obb.Center.X:F2}, {obb.Center.Y:F2}, {obb.Center.Z:F2})"); + for (int i = 0; i < 3; i++) + { + var axis = obb.GetAxis(i); + var axisVector = axis.ToVector3D(); + LogManager.Info($"[空轨] Axis {i} 方向: ({axisVector.X:F3}, {axisVector.Y:F3}, {axisVector.Z:F3}), 长度: {obb.GetAxisLength(i):F2}"); + } + + return obb; + } + + /// + /// 沿直线采样 + /// + private static List SampleLine(Point3D start, Point3D end, double interval) + { + var result = new List(); + + var direction = new Vector3D(end.X - start.X, end.Y - start.Y, end.Z - start.Z); + var length = Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z); + + if (length < 0.001) + { + result.Add(start); + return result; + } + + // 归一化方向 + direction.X /= length; + direction.Y /= length; + direction.Z /= length; + + // 计算采样点数 + int numPoints = (int)Math.Ceiling(length / interval) + 1; + + for (int i = 0; i < numPoints; i++) + { + var t = (double)i / (numPoints - 1); + var point = new Point3D( + start.X + direction.X * length * t, + start.Y + direction.Y * length * t, + start.Z + direction.Z * length * t + ); + result.Add(point); + } + + return result; + } + /// /// 兼容方法:从空轨几何体提取下表面中心线(一步完成) /// diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs index 5981a6b..9b887ea 100644 --- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs +++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs @@ -490,7 +490,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (vModel != null && CurrentPathRoute != null && CurrentPathRoute.Points != null) { var points = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList(); - _pathAnimationManager?.SyncToPathStart(vModel, points); + _pathAnimationManager?.MoveVehicleToPathStart(vModel, points); } } catch (Exception ex) @@ -1211,7 +1211,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (CurrentPathRoute != null && CurrentPathRoute.Points != null && _pathAnimationManager != null) { var points = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList(); - _pathAnimationManager.SyncToPathStart(newObject, points); + _pathAnimationManager.MoveVehicleToPathStart(newObject, points); } } catch (Exception ex) @@ -1255,6 +1255,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels { CurrentPathRoute = pathRoute; + // 同步路径到 PathAnimationManager + if (pathRoute != null && _pathAnimationManager != null) + { + // 从 PathPlanningManager 获取对应的 PathRoute 对象 + var pathPlanningManager = PathPlanningManager.Instance; + var coreRoute = pathPlanningManager.GetAllRoutes().FirstOrDefault(r => r.Id == pathRoute.Id); + if (coreRoute != null) + { + _pathAnimationManager.SetRoute(coreRoute); + } + } + // 添加调试日志 if (pathRoute != null) { diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index 69ce25c..540c701 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -218,12 +218,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 检查是否在编辑状态 bool isInEditMode = _pathPlanningManager?.IsInEditableState ?? false; - // 如果选择的是空轨路径,自动提取并显示空轨基准线 - if (_selectedPathRoute?.PathType == PathType.Rail && !isInEditMode) - { - ExtractAndRenderRailBaselinePaths(); - } - // 1. 清理现有的路径可视化,但保留网格可视化和正在编辑的路径 if (isInEditMode) { @@ -3194,6 +3188,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels string statusMessage = $"✅ 已从{e.LoadSource}加载 {e.RouteCount} 条历史路径"; UpdateMainStatus(statusMessage); LogManager.Info($"*** 状态已更新: {statusMessage} ***"); + + // 自动提取并显示所有空轨模型的基准线 + ExtractAndRenderRailBaselinePaths(); }, "处理路径加载完成事件"); } catch (Exception ex)