增加剖面盒基本功能;修改变量名车辆为物体

This commit is contained in:
tian 2026-02-12 12:42:10 +08:00
parent e4433ee073
commit 949c7ed6b5
38 changed files with 1219 additions and 975 deletions

View File

@ -8,14 +8,14 @@ cell_size_meters = 0.5
# 最大高度差(米)- 楼梯、坡道可接受的高度阈值
max_height_diff_meters = 0.35
# 车辆长度(米)
vehicle_length_meters = 1.5
# 运动物体长度(米)
object_length_meters = 1.5
# 车辆宽度(米)
vehicle_width_meters = 1.0
# 运动物体宽度(米)
object_width_meters = 1.0
# 车辆高度(米)
vehicle_height_meters = 2.0
# 运动物体高度(米)
object_height_meters = 2.0
# 安全间隙(米)
safety_margin_meters = 0.1

View File

@ -24,19 +24,19 @@ namespace NavisworksTransport.Commands
public Point3D EndPoint { get; set; }
/// <summary>
/// 车辆长度(米)
/// 运动物体长度(米)
/// </summary>
public double VehicleLength { get; set; } = 1.0;
public double ObjectLength { get; set; } = 1.0;
/// <summary>
/// 车辆宽度(米)
/// 运动物体宽度(米)
/// </summary>
public double VehicleWidth { get; set; } = 1.0;
public double ObjectWidth { get; set; } = 1.0;
/// <summary>
/// 车辆高度(米)
/// 运动物体高度(米)
/// </summary>
public double VehicleHeight { get; set; } = 2.0;
public double ObjectHeight { get; set; } = 2.0;
/// <summary>
/// 安全边距(米)
@ -76,13 +76,13 @@ namespace NavisworksTransport.Commands
if (EndPoint == null)
errors.Add("终点不能为空");
if (VehicleLength <= 0 || VehicleLength > 20)
if (ObjectLength <= 0 || ObjectLength > 20)
errors.Add("车辆长度必须在0-20米之间");
if (VehicleWidth <= 0 || VehicleWidth > 10)
if (ObjectWidth <= 0 || ObjectWidth > 10)
errors.Add("车辆宽度必须在0-10米之间");
if (VehicleHeight <= 0 || VehicleHeight > 10)
if (ObjectHeight <= 0 || ObjectHeight > 10)
errors.Add("车辆高度必须在0-10米之间");
if (SafetyMargin < 0 || SafetyMargin > 5)
@ -279,7 +279,7 @@ namespace NavisworksTransport.Commands
var actualGridSize = _parameters.GridSize > 0 ? _parameters.GridSize : -1;
LogInfo($"使用参数 - 起点: ({_parameters.StartPoint.X:F2}, {_parameters.StartPoint.Y:F2}), " +
$"终点: ({_parameters.EndPoint.X:F2}, {_parameters.EndPoint.Y:F2}), " +
$"车辆宽度: {_parameters.VehicleWidth}米, 安全边距: {_parameters.SafetyMargin}米, " +
$"运动物体宽度: {_parameters.ObjectWidth}米, 安全边距: {_parameters.SafetyMargin}米, " +
$"网格大小: {(actualGridSize > 0 ? $" {actualGridSize:F2}" : "")}");
// 第三阶段执行路径规划30% - 80%
@ -309,17 +309,17 @@ namespace NavisworksTransport.Commands
};
// 计算车辆半径:基于长度和宽度的较大值的一半
var vehicleRadius = Math.Max(_parameters.VehicleLength, _parameters.VehicleWidth) / 2.0;
LogInfo($"使用车辆半径: {vehicleRadius}m基于车辆长度{_parameters.VehicleLength}m和宽度{_parameters.VehicleWidth}m的较大值");
var objectRadius = Math.Max(_parameters.ObjectLength, _parameters.ObjectWidth) / 2.0;
LogInfo($"使用运动物体半径: {objectRadius}m基于长度{_parameters.ObjectLength}m和宽度{_parameters.ObjectWidth}m的较大值");
// 调用PathPlanningManager的AutoPlanPath方法执行真实的A*算法
var pathPlanTask = _pathPlanningManager.AutoPlanPath(
startPathPoint,
endPathPoint,
vehicleRadius,
objectRadius,
_parameters.SafetyMargin,
actualGridSize,
_parameters.VehicleHeight, // 使用参数中的车辆高度
_parameters.ObjectHeight, // 使用参数中的运动物体高度
_parameters.Strategy); // 使用参数中指定的路径策略
generatedRoute = await pathPlanTask;
@ -454,21 +454,21 @@ namespace NavisworksTransport.Commands
/// </summary>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="vehicleSize">车辆尺寸(应用于长度和宽度)</param>
/// <param name="objectSize">运动物体尺寸(应用于长度和宽度)</param>
/// <param name="pathName">路径名称</param>
/// <param name="vehicleHeight">车辆高度可选默认2.0米)</param>
/// <param name="objectHeight">运动物体高度可选默认2.0米)</param>
/// <param name="strategy">路径策略(可选,默认最短路径)</param>
/// <returns>自动路径规划命令实例</returns>
public static AutoPathPlanningCommand CreateQuick(Point3D startPoint, Point3D endPoint,
double vehicleSize = 1.0, string pathName = null, double vehicleHeight = 2.0, PathStrategy strategy = PathStrategy.Shortest)
double objectSize = 1.0, string pathName = null, double objectHeight = 2.0, PathStrategy strategy = PathStrategy.Shortest)
{
var parameters = new AutoPathPlanningParameters
{
StartPoint = startPoint,
EndPoint = endPoint,
VehicleLength = vehicleSize,
VehicleWidth = vehicleSize,
VehicleHeight = vehicleHeight,
ObjectLength = objectSize,
ObjectWidth = objectSize,
ObjectHeight = objectHeight,
SafetyMargin = 0.5,
GridSize = -1, // 自动选择
PathName = pathName,
@ -487,7 +487,7 @@ namespace NavisworksTransport.Commands
{
return $"自动路径规划命令 - 起点:({_parameters.StartPoint?.X:F1}, {_parameters.StartPoint?.Y:F1}), " +
$"终点:({_parameters.EndPoint?.X:F1}, {_parameters.EndPoint?.Y:F1}), " +
$"车辆尺寸:长{_parameters.VehicleLength}×宽{_parameters.VehicleWidth}×高{_parameters.VehicleHeight}米, " +
$"运动物体尺寸:长{_parameters.ObjectLength}×宽{_parameters.ObjectWidth}×高{_parameters.ObjectHeight}米, " +
$"状态:{Status}";
}

View File

@ -160,7 +160,7 @@ namespace NavisworksTransport.Commands
{
StartPoint = startPoint,
EndPoint = endPoint,
VehicleWidth = args.Length > 2 && args[2] is double v ? v : 1.0,
ObjectWidth = args.Length > 2 && args[2] is double v ? v : 1.0,
SafetyMargin = args.Length > 3 && args[3] is double v1 ? v1 : 0.5,
GridSize = args.Length > 4 && args[4] is double v2 ? v2 : -1,
PathName = args.Length > 5 ? args[5]?.ToString() : null,
@ -182,10 +182,10 @@ namespace NavisworksTransport.Commands
if (startPoint == null || endPoint == null)
throw new ArgumentException("起点和终点必须是 Point3D 类型");
var vehicleSize = args.Length > 2 && args[2] is double v ? v : 1.0;
var objectSize = args.Length > 2 && args[2] is double v ? v : 1.0;
var pathName = args.Length > 3 ? args[3]?.ToString() : null;
return AutoPathPlanningCommand.CreateQuick(startPoint, endPoint, vehicleSize, pathName);
return AutoPathPlanningCommand.CreateQuick(startPoint, endPoint, objectSize, pathName);
});
// 注册模拟路径规划命令(保留用于测试)- 暂时注释掉避免类型推断问题

View File

@ -82,9 +82,9 @@ namespace NavisworksTransport.Commands
// 设置车辆参数(从配置获取)
var config = ConfigManager.Instance.Current;
route.MaxVehicleLength = config.PathEditing.VehicleLengthMeters;
route.MaxVehicleWidth = config.PathEditing.VehicleWidthMeters;
route.MaxVehicleHeight = config.PathEditing.VehicleHeightMeters;
route.MaxObjectLength = config.PathEditing.ObjectLengthMeters;
route.MaxObjectWidth = config.PathEditing.ObjectWidthMeters;
route.MaxObjectHeight = config.PathEditing.ObjectHeightMeters;
route.SafetyMargin = config.PathEditing.SafetyMarginMeters;
// 计算总长度

View File

@ -17,22 +17,22 @@ namespace NavisworksTransport.Commands
private readonly Document _document;
private readonly double _cellSize;
private readonly int _samplingRate;
private readonly double _vehicleRadius;
private readonly double _vehicleHeight;
private readonly double _objectRadius;
private readonly double _objectHeight;
public VoxelGridSDFTestCommand(
Document document,
double cellSize,
int samplingRate,
double vehicleRadius,
double vehicleHeight)
double objectRadius,
double objectHeight)
: base("VoxelGridSDFTest", "体素网格SDF测试", "使用签名距离场进行精确体素化")
{
_document = document ?? throw new ArgumentNullException(nameof(document));
_cellSize = cellSize;
_samplingRate = samplingRate;
_vehicleRadius = vehicleRadius;
_vehicleHeight = vehicleHeight;
_objectRadius = objectRadius;
_objectHeight = objectHeight;
}
protected override PathPlanningResult ValidateParameters()
@ -56,8 +56,8 @@ namespace NavisworksTransport.Commands
{
LogInfo("=== 开始体素网格 SDF 测试 ===");
LogInfo($"体素大小: {_cellSize}米");
LogInfo($"车辆半径: {_vehicleRadius}米");
LogInfo($"车辆高度: {_vehicleHeight}米");
LogInfo($"车辆半径: {_objectRadius}米");
LogInfo($"车辆高度: {_objectHeight}米");
LogInfo($"采样率: {_samplingRate}");
UpdateProgress(5, "正在获取模型空间范围...");
@ -90,8 +90,8 @@ namespace NavisworksTransport.Commands
var voxelGrid = generator.GenerateFromBIMWithSDF(
spaceBounds,
_cellSize,
_vehicleRadius,
_vehicleHeight,
_objectRadius,
_objectHeight,
allModelItems);
LogInfo($"体素网格生成完成: {voxelGrid.SizeX}×{voxelGrid.SizeY}×{voxelGrid.SizeZ} = {voxelGrid.TotalVoxels} 个体素");

View File

@ -15,24 +15,24 @@ namespace NavisworksTransport.Commands
{
private readonly Document _document;
private readonly double _cellSize;
private readonly double _vehicleRadius;
private readonly double _vehicleHeight;
private readonly double _objectRadius;
private readonly double _objectHeight;
private readonly Point3D _startPoint;
private readonly Point3D _endPoint;
public VoxelPathFindingTestCommand(
Document document,
double cellSize,
double vehicleRadius,
double vehicleHeight,
double objectRadius,
double objectHeight,
Point3D startPoint,
Point3D endPoint)
: base("VoxelPathFindingTest", "体素路径规划测试", "测试VoxelPathFinder的3D A*路径规划")
{
_document = document ?? throw new ArgumentNullException(nameof(document));
_cellSize = cellSize;
_vehicleRadius = vehicleRadius;
_vehicleHeight = vehicleHeight;
_objectRadius = objectRadius;
_objectHeight = objectHeight;
_startPoint = startPoint;
_endPoint = endPoint;
}
@ -58,8 +58,8 @@ namespace NavisworksTransport.Commands
{
LogInfo("=== 开始体素路径规划测试 ===");
LogInfo($"体素大小: {_cellSize}米");
LogInfo($"车辆半径: {_vehicleRadius}米");
LogInfo($"车辆高度: {_vehicleHeight}米");
LogInfo($"车辆半径: {_objectRadius}米");
LogInfo($"车辆高度: {_objectHeight}米");
LogInfo($"起点: ({_startPoint.X:F2}, {_startPoint.Y:F2}, {_startPoint.Z:F2})");
LogInfo($"终点: ({_endPoint.X:F2}, {_endPoint.Y:F2}, {_endPoint.Z:F2})");
@ -92,8 +92,8 @@ namespace NavisworksTransport.Commands
var voxelGrid = generator.GenerateFromBIMWithSDF(
spaceBounds,
_cellSize,
_vehicleRadius,
_vehicleHeight,
_objectRadius,
_objectHeight,
allModelItems);
LogInfo($"体素网格生成完成: {voxelGrid.SizeX}×{voxelGrid.SizeY}×{voxelGrid.SizeZ} = {voxelGrid.TotalVoxels:N0} 个体素");

View File

@ -89,10 +89,11 @@ namespace NavisworksTransport.Core.Animation
private static readonly Dictionary<string, List<AnimationFrame>> _animationFrameCache = new Dictionary<string, List<AnimationFrame>>(); // 动画帧缓存
private static readonly Dictionary<string, List<CollisionResult>> _collisionResultCache = new Dictionary<string, List<CollisionResult>>(); // 碰撞结果缓存
private ModelItem _animatedObject;
private bool _isVirtualVehicle = false; // 是否使用虚拟车辆
private double _virtualVehicleLength = 0; // 虚拟车辆长度(米)
private double _virtualVehicleWidth = 0; // 虚拟车辆宽度(米)
private double _virtualVehicleHeight = 0; // 虚拟车辆高度(米)
private bool _useVirtualObject = false; // 是否使用虚拟运动物体
private double _virtualObjectLength = 0; // 虚拟运动物体长度(模型单位)
private double _virtualObjectWidth = 0; // 虚拟运动物体宽度(模型单位)
private double _virtualObjectHeight = 0; // 虚拟运动物体高度(模型单位)
private bool _useSectionClip = true; // 是否使用剖面盒优化(默认启用)
private List<Point3D> _pathPoints;
private List<ModelItem> _manualCollisionTargets = new List<ModelItem>();
private bool _manualCollisionOverrideEnabled = false;
@ -350,7 +351,7 @@ namespace NavisworksTransport.Core.Animation
/// </summary>
public void SyncToPathStart(ModelItem item, List<Point3D> points)
{
MoveVehicleToPathStart(item, points);
MoveObjectToPathStart(item, points);
}
/// <summary>
@ -364,16 +365,16 @@ namespace NavisworksTransport.Core.Animation
var doc = NavisApplication.ActiveDocument;
if (doc == null) return;
// 🔥 支持虚拟车辆的恢复
// 🔥 支持虚拟物体的恢复
ModelItem objectToRestore = null;
bool isVirtual = _isVirtualVehicle;
bool isVirtual = _useVirtualObject;
if (isVirtual)
{
objectToRestore = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
objectToRestore = VirtualObjectManager.Instance.CurrentVirtualObject;
if (objectToRestore == null)
{
LogManager.Warning("[归位] 虚拟车辆未激活,跳过恢复");
LogManager.Warning("[归位] 虚拟物体未激活,跳过恢复");
return;
}
}
@ -403,7 +404,7 @@ namespace NavisworksTransport.Core.Animation
originalBoundingBox.Min.Z
);
string objectName = isVirtual ? "虚拟车辆" : objectToRestore.DisplayName;
string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName;
LogManager.Info($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}");
}
catch (Exception ex)
@ -429,18 +430,27 @@ namespace NavisworksTransport.Core.Animation
public void SetAnimatedObject(ModelItem animatedObject)
{
_animatedObject = animatedObject;
_isVirtualVehicle = (animatedObject == null);
_useVirtualObject = (animatedObject == null);
}
/// <summary>
/// 设置虚拟车辆参数(批处理专用)
/// 设置虚拟物体参数(批处理专用)
/// </summary>
public void SetVirtualVehicleParameters(bool isVirtualVehicle, double length, double width, double height)
public void SetVirtualObjectParameters(bool useVirtualObject, double length, double width, double height)
{
_isVirtualVehicle = isVirtualVehicle;
_virtualVehicleLength = length;
_virtualVehicleWidth = width;
_virtualVehicleHeight = height;
_useVirtualObject = useVirtualObject;
_virtualObjectLength = length;
_virtualObjectWidth = width;
_virtualObjectHeight = height;
}
/// <summary>
/// 设置是否使用剖面盒优化
/// </summary>
public void SetUseSectionClip(bool useSectionClip)
{
_useSectionClip = useSectionClip;
LogManager.Info($"[PathAnimationManager] 剖面盒优化已{(useSectionClip ? "" : "")}");
}
/// <summary>
@ -561,18 +571,18 @@ namespace NavisworksTransport.Core.Animation
}
/// <summary>
/// 将车辆移动到路径起点
/// 将物体移动到路径起点
/// </summary>
/// <param name="animatedObject">动画对象可选如果不提供则使用当前的_animatedObject</param>
/// <param name="pathPoints">路径点可选如果不提供则使用当前的_pathPoints</param>
public void MoveVehicleToPathStart(ModelItem animatedObject = null, List<Point3D> pathPoints = null)
public void MoveObjectToPathStart(ModelItem animatedObject = null, List<Point3D> pathPoints = null)
{
try
{
// 🔥 重要先恢复物体到原始状态CAD位置和原始朝向确保每次计算都从相同的状态开始
// 这样可以避免之前旋转导致的包围盒尺寸计算不准确的问题
// 注意:也要处理虚拟车辆_isVirtualVehicle为true时_animatedObject可能为null
if (_animatedObject != null || _isVirtualVehicle)
// 注意:也要处理虚拟物体_useVirtualObject为true时_animatedObject可能为null
if (_animatedObject != null || _useVirtualObject)
{
RestoreObjectToCADPosition();
LogManager.Info($"[移动到起点] 已恢复物体到原始状态, _currentYaw={_currentYaw * 180 / Math.PI:F2}°");
@ -586,7 +596,7 @@ namespace NavisworksTransport.Core.Animation
_originalCenter = animatedObject.BoundingBox().Center;
_currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, animatedObject.BoundingBox().Min.Z);
// 统一逻辑:从当前 Transform 中提取实际朝向(无论虚拟车辆还是普通物体)
// 统一逻辑:从当前 Transform 中提取实际朝向(无论虚拟物体还是普通物体)
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
}
@ -636,19 +646,19 @@ namespace NavisworksTransport.Core.Animation
if (_route?.PathType == PathType.Hoisting)
{
// 吊装路径:第一个路径点(起吊点)是地面位置,物体底面应该在这里
// 不需要向下移动车辆高度
// 不需要向下移动物体高度
LogManager.Debug($"[移动到起点] 吊装路径起吊点是地面位置物体底面Z={startPosition.Z:F2}");
}
else if (_route?.PathType == PathType.Rail)
{
// 空轨路径:路径点是悬挂点,物体悬挂在下方
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}");
double objectHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z;
startPosition = new Point3D(startPosition.X, startPosition.Y, startPosition.Z - objectHeight);
LogManager.Debug($"[移动到起点] 空轨路径调整: 悬挂点Z={_pathPoints[0].Z:F2}, 物体底面Z={startPosition.Z:F2}, 物体高度={objectHeight:F2}");
}
else
{
// 地面路径points[0] 是地面位置,车辆底面应该在这里
// 地面路径points[0] 是地面位置,物体底面应该在这里
LogManager.Debug($"[移动到起点] 地面路径: pos=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2})");
}
@ -680,16 +690,16 @@ namespace NavisworksTransport.Core.Animation
LogManager.Info($"实际物体位置: X={actualCenter.X:F2}, Y={actualCenter.Y:F2}, Z={actualCenter.Z:F2}");
LogManager.Info($"实际物体朝向: {actualYaw * 180 / Math.PI:F2}度");
}
else if (_isVirtualVehicle)
else if (_useVirtualObject)
{
var virtualVehicle = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
if (virtualVehicle != null)
var virtualObject = VirtualObjectManager.Instance.CurrentVirtualObject;
if (virtualObject != null)
{
var bbox = virtualVehicle.BoundingBox();
var bbox = virtualObject.BoundingBox();
var actualCenter = bbox.Center;
var actualYaw = ModelItemTransformHelper.GetYawFromTransform(virtualVehicle.Transform);
LogManager.Info($"虚拟车辆位置: X={actualCenter.X:F2}, Y={actualCenter.Y:F2}, Z={actualCenter.Z:F2}");
LogManager.Info($"虚拟车辆朝向: {actualYaw * 180 / Math.PI:F2}度");
var actualYaw = ModelItemTransformHelper.GetYawFromTransform(virtualObject.Transform);
LogManager.Info($"虚拟物体位置: X={actualCenter.X:F2}, Y={actualCenter.Y:F2}, Z={actualCenter.Z:F2}");
LogManager.Info($"虚拟物体朝向: {actualYaw * 180 / Math.PI:F2}度");
}
}
}
@ -708,11 +718,50 @@ namespace NavisworksTransport.Core.Animation
{
LogManager.Info("=== 使用路径预计算动画帧 ===");
// 🔥 设置剖面盒优化(根据配置)
if (_useSectionClip && _route != null && _route.Points != null && _route.Points.Count > 0)
{
var pathPoints = _route.Points.Select(p => p.Position).ToList();
// 计算剖面盒边距(模型单位):物体尺寸 + 安全间隙
double objectLength, objectWidth, objectHeight;
if (_useVirtualObject)
{
// 虚拟物体:使用预设尺寸
objectLength = _virtualObjectLength;
objectWidth = _virtualObjectWidth;
objectHeight = _virtualObjectHeight;
LogManager.Debug($"[预计算] 使用虚拟运动物体尺寸: {objectLength:F2} x {objectWidth:F2} x {objectHeight:F2} (模型单位)");
}
else
{
// 真实运动物体:从包围盒获取尺寸
var bbox = _animatedObject.BoundingBox();
objectLength = bbox.Max.X - bbox.Min.X;
objectWidth = bbox.Max.Y - bbox.Min.Y;
objectHeight = bbox.Max.Z - bbox.Min.Z;
LogManager.Debug($"[预计算] 使用真实运动物体尺寸: {objectLength:F2} x {objectWidth:F2} x {objectHeight:F2} (模型单位), 物体: {_animatedObject.DisplayName}");
}
double maxObjectSize = Math.Max(Math.Max(objectLength, objectWidth), objectHeight);
double margin = maxObjectSize + _safetyMargin;
double heightMargin = objectHeight + _safetyMargin;
SectionClipHelper.SetClipBoxByPath(pathPoints, margin, heightMargin);
LogManager.Info($"[预计算] 已启用剖面盒优化 - 运动物体最大尺寸: {maxObjectSize:F2}, 安全间隙: {_safetyMargin:F2}, 边距: {margin:F2}, 高度边距: {heightMargin:F2} (模型单位)");
}
else if (!_useSectionClip)
{
SectionClipHelper.ClearClipBox();
LogManager.Info("[预计算] 已禁用剖面盒优化(性能对比测试模式)");
}
// 🔥 重要:预计算前先将物体移动到起点位置
if (_animatedObject != null && _route != null && _route.Points != null && _route.Points.Count > 0)
{
var pathPoints = _route.Points.Select(p => p.Position).ToList();
MoveVehicleToPathStart(_animatedObject, pathPoints);
MoveObjectToPathStart(_animatedObject, pathPoints);
LogManager.Info("[预计算] 物体已移动到路径起点");
}
@ -849,7 +898,7 @@ namespace NavisworksTransport.Core.Animation
LogManager.Info(spatialIndexManager.GetStatistics());
// 🔥 优化使用AABB查询代替球形查询查询范围更精确
// 不再预计算球形半径,改为每帧计算车辆AABB + 安全间隙的搜索AABB
// 不再预计算球形半径,改为每帧计算物体AABB + 安全间隙的搜索AABB
LogManager.Info($"空间查询使用AABB模式安全间隙: {_safetyMargin:F4}模型单位");
}
@ -889,14 +938,14 @@ namespace NavisworksTransport.Core.Animation
// 计算朝向(使用当前线段的方向)
yawRadians = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
// 吊装路径所有线段都使用水平吊运方向与MoveVehicleToPathStart保持一致
// 吊装路径所有线段都使用水平吊运方向与MoveObjectToPathStart保持一致
if (_route.PathType == PathType.Hoisting)
{
yawRadians = Math.Atan2(_pathPoints[2].Y - _pathPoints[1].Y, _pathPoints[2].X - _pathPoints[1].X);
}
// 🔥 空中路径:根据路径类型和线段索引调整物体位置
double vehicleHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z;
double objectHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z;
if (_route.PathType == PathType.Hoisting)
{
@ -919,25 +968,25 @@ namespace NavisworksTransport.Core.Animation
if (segmentIndex == firstSegment)
{
// 起吊段:从地面逐渐上升到悬挂点-车辆高度
// 起吊段:从地面逐渐上升到悬挂点-物体高度
// 进度0时地面物体底面在地面不向下移动
// 进度1时悬挂点物体顶面在悬挂点物体底面=悬挂点-车辆高度
double heightOffset = segmentProgress * vehicleHeight;
// 进度1时悬挂点物体顶面在悬挂点物体底面=悬挂点-物体高度
double heightOffset = segmentProgress * objectHeight;
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - heightOffset);
LogManager.Debug($"[吊装路径-起吊段] 进度={segmentProgress:F2}, 高度偏移={heightOffset:F2}, 物体底面Z={framePosition.Z:F2}");
}
else if (segmentIndex > firstSegment && segmentIndex < lastSegment)
{
// 平移段(中间的所有线段):全程都是悬挂点,物体顶面在悬挂点
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - vehicleHeight);
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - objectHeight);
LogManager.Debug($"[吊装路径-平移段] 线段{segmentIndex}, 进度={segmentProgress:F2}, 物体底面Z={framePosition.Z:F2}");
}
else if (segmentIndex == lastSegment)
{
// 下降段:从悬挂点-车辆高度逐渐下降到地面
// 进度0时悬挂点物体顶面在悬挂点物体底面=悬挂点-车辆高度
// 下降段:从悬挂点-物体高度逐渐下降到地面
// 进度0时悬挂点物体顶面在悬挂点物体底面=悬挂点-物体高度
// 进度1时地面物体底面在地面不向下移动
double heightOffset = (1 - segmentProgress) * vehicleHeight;
double heightOffset = (1 - segmentProgress) * objectHeight;
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - heightOffset);
LogManager.Debug($"[吊装路径-下降段] 进度={segmentProgress:F2}, 高度偏移={heightOffset:F2}, 物体底面Z={framePosition.Z:F2}");
}
@ -946,8 +995,8 @@ namespace NavisworksTransport.Core.Animation
{
// 空轨路径:路径点是悬挂点,物体悬挂在下方
// 物体顶面应该在路径点,所以物体底面 = 路径点 - 物体高度
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - vehicleHeight);
LogManager.Debug($"[空轨路径] 调整物体位置: 悬挂点Z={framePosition.Z + vehicleHeight:F2}, 物体底面Z={framePosition.Z:F2}");
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - objectHeight);
LogManager.Debug($"[空轨路径] 调整物体位置: 悬挂点Z={framePosition.Z + objectHeight:F2}, 物体底面Z={framePosition.Z:F2}");
}
}
else
@ -994,30 +1043,30 @@ namespace NavisworksTransport.Core.Animation
// 虚拟碰撞检测
// 计算车辆包围盒相对于framePosition的偏移
var currentVehicleBoundingBox = _animatedObject.BoundingBox();
// 计算物体包围盒相对于framePosition的偏移
var currentObjectBoundingBox = _animatedObject.BoundingBox();
var originalCenter = new Point3D(
(currentVehicleBoundingBox.Min.X + currentVehicleBoundingBox.Max.X) / 2,
(currentVehicleBoundingBox.Min.Y + currentVehicleBoundingBox.Max.Y) / 2,
(currentVehicleBoundingBox.Min.Z + currentVehicleBoundingBox.Max.Z) / 2
(currentObjectBoundingBox.Min.X + currentObjectBoundingBox.Max.X) / 2,
(currentObjectBoundingBox.Min.Y + currentObjectBoundingBox.Max.Y) / 2,
(currentObjectBoundingBox.Min.Z + currentObjectBoundingBox.Max.Z) / 2
);
// 计算偏移量当前包围盒中心到framePosition的偏移
var offsetX = framePosition.X - originalCenter.X;
var offsetY = framePosition.Y - originalCenter.Y;
var offsetZ = framePosition.Z - currentVehicleBoundingBox.Min.Z; // Z方向framePosition是底面originalCenter是中心
var offsetZ = framePosition.Z - currentObjectBoundingBox.Min.Z; // Z方向framePosition是底面originalCenter是中心
// 创建新的包围盒将当前包围盒移动到framePosition
var virtualBoundingBox = new BoundingBox3D(
new Point3D(
currentVehicleBoundingBox.Min.X + offsetX,
currentVehicleBoundingBox.Min.Y + offsetY,
currentObjectBoundingBox.Min.X + offsetX,
currentObjectBoundingBox.Min.Y + offsetY,
framePosition.Z // 底面Z坐标
),
new Point3D(
currentVehicleBoundingBox.Max.X + offsetX,
currentVehicleBoundingBox.Max.Y + offsetY,
framePosition.Z + (currentVehicleBoundingBox.Max.Z - currentVehicleBoundingBox.Min.Z) // 保持高度
currentObjectBoundingBox.Max.X + offsetX,
currentObjectBoundingBox.Max.Y + offsetY,
framePosition.Z + (currentObjectBoundingBox.Max.Z - currentObjectBoundingBox.Min.Z) // 保持高度
)
);
@ -1034,7 +1083,7 @@ namespace NavisworksTransport.Core.Animation
else
{
// 🔥 优化使用AABB查询代替球形查询避免球体扩大的无效范围
// 查询AABB = 车辆AABB + 安全间隙扩展
// 查询AABB = 物体AABB + 安全间隙扩展
var searchBounds = new BoundingBox3D(
new Point3D(
virtualBoundingBox.Min.X - _safetyMargin,
@ -1088,12 +1137,12 @@ namespace NavisworksTransport.Core.Animation
(virtualBoundingBox.Min.Y + virtualBoundingBox.Max.Y + colliderBox.Min.Y + colliderBox.Max.Y) / 4,
(virtualBoundingBox.Min.Z + virtualBoundingBox.Max.Z + colliderBox.Min.Z + colliderBox.Max.Z) / 4
),
// 🔥 重要:记录虚拟车辆的包围盒中心,而不是底面中心
// 🔥 重要:记录虚拟物体的包围盒中心,而不是底面中心
// 原因:
// 1. framePosition是路径点代表车辆在地面上的位置(底面中心)
// 1. framePosition是路径点代表物体在地面上的位置(底面中心)
// 2. ClashDetective移动逻辑使用包围盒中心进行定位
// 3. 如果记录底面中心ClashDetective会把包围盒中心移动到底面位置导致车辆下沉半个高度
// 4. 记录包围盒中心可以确保ClashDetective移动后的位置与预计算时的虚拟车辆位置一致
// 3. 如果记录底面中心ClashDetective会把包围盒中心移动到底面位置导致物体下沉半个高度
// 4. 记录包围盒中心可以确保ClashDetective移动后的位置与预计算时的虚拟物体位置一致
Item1Position = new Point3D(
framePosition.X,
framePosition.Y,
@ -1355,7 +1404,7 @@ namespace NavisworksTransport.Core.Animation
if (distanceToStart > 0.1 || yawDiff > 0.01)
{
LogManager.Info($"[动画开始] 物体不在起点,重置到起点: 距离={distanceToStart:F2}m, 角度差={yawDiff * 180 / Math.PI:F2}°");
MoveVehicleToPathStart();
MoveObjectToPathStart();
}
}
@ -1423,10 +1472,10 @@ namespace NavisworksTransport.Core.Animation
{
var firstFrame = _animationFrames[0];
LogManager.Debug($"[动画开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}");
LogManager.Debug($"[动画开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _useVirtualObject={_useVirtualObject}");
// 🔥 确保物体在起点位置和朝向
// 注意MoveVehicleToPathStart 已在 StartAnimation 中调用,这里只是日志记录
// 注意MoveObjectToPathStart 已在 StartAnimation 中调用,这里只是日志记录
LogManager.Debug($"[动画开始] 物体应在起点: pos=({firstFrame.Position.X:F2},{firstFrame.Position.Y:F2}), yaw={firstFrame.YawRadians:F3}rad");
}
else
@ -1446,12 +1495,12 @@ namespace NavisworksTransport.Core.Animation
ModelHighlightHelper.ClearCollisionHighlights();
LogManager.Debug("[动画开始] 已清除所有碰撞高亮");
// 不再高亮车辆对象,保持原始外观(客户要求)
// 不再高亮物体对象,保持原始外观(客户要求)
// if (_animatedObject != null)
// {
// var vehicleItems = new List<ModelItem> { _animatedObject };
// ModelHighlightHelper.HighlightItems(ModelHighlightHelper.AnimatedObjectCategory, vehicleItems);
// LogManager.Debug("[动画开始] 已高亮车辆对象为绿色");
// var animatedObjectItems = new List<ModelItem> { _animatedObject };
// ModelHighlightHelper.HighlightItems(ModelHighlightHelper.AnimatedObjectCategory, animatedObjectItems);
// LogManager.Debug("[动画开始] 已高亮物体对象为绿色");
// }
// 启动动画播放
@ -1572,7 +1621,7 @@ namespace NavisworksTransport.Core.Animation
_pausedProgress = 0.0; // 重置暂停进度
_currentFrameIndex = 0; // 重置帧索引
// 清除所有高亮(包括车辆和碰撞)
// 清除所有高亮(包括物体和碰撞)
ModelHighlightHelper.ClearAllHighlights();
LogManager.Info("动画停止:已清除所有高亮");
@ -1658,7 +1707,7 @@ namespace NavisworksTransport.Core.Animation
// 直接设置为完成状态,避免中间状态切换
SetState(AnimationState.Finished);
// 清除所有高亮(包括车辆和碰撞)
// 清除所有高亮(包括物体和碰撞)
ModelHighlightHelper.ClearAllHighlights();
LogManager.Info("动画完成:已清除所有高亮");
@ -1689,12 +1738,12 @@ namespace NavisworksTransport.Core.Animation
_detectionTolerance,
_currentRouteId,
_animatedObject,
_isVirtualVehicle,
_useVirtualObject,
_animationFrameRate,
_animationDuration,
_virtualVehicleLength,
_virtualVehicleWidth,
_virtualVehicleHeight,
_virtualObjectLength,
_virtualObjectWidth,
_virtualObjectHeight,
_pathPoints
);
}
@ -1811,7 +1860,7 @@ namespace NavisworksTransport.Core.Animation
// 第二步:如果已经生成了动画路径,则重新对齐到起点(带旋转)
if (_animationFrames != null && _animationFrames.Count > 0)
{
MoveVehicleToPathStart();
MoveObjectToPathStart();
LogManager.Info("已重新对齐到路径起点");
}
}
@ -2809,15 +2858,15 @@ namespace NavisworksTransport.Core.Animation
/// <param name="durationSeconds">动画持续时间(秒)</param>
/// <param name="pathName">路径名称</param>
/// <param name="routeId">路由ID</param>
public void CreateAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0, string pathName = "未知路径", string routeId = null, bool isVirtualVehicle = false,
double virtualVehicleLength = 0, double virtualVehicleWidth = 0, double virtualVehicleHeight = 0, double safetyMargin = 0)
public void CreateAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0, string pathName = "未知路径", string routeId = null, bool useVirtualObject = false,
double virtualObjectLength = 0, double virtualObjectWidth = 0, double virtualObjectHeight = 0, double safetyMargin = 0)
{
_pathName = pathName;
_currentRouteId = routeId;
_isVirtualVehicle = isVirtualVehicle; // 设置是否使用虚拟车辆
_virtualVehicleLength = virtualVehicleLength; // 模型单位
_virtualVehicleWidth = virtualVehicleWidth; // 模型单位
_virtualVehicleHeight = virtualVehicleHeight; // 模型单位
_useVirtualObject = useVirtualObject; // 设置是否使用虚拟运动物体
_virtualObjectLength = virtualObjectLength; // 模型单位
_virtualObjectWidth = virtualObjectWidth; // 模型单位
_virtualObjectHeight = virtualObjectHeight; // 模型单位
_safetyMargin = safetyMargin; // 模型单位
_pathPoints = pathPoints;
_animatedObject = animatedObject;
@ -2829,14 +2878,14 @@ namespace NavisworksTransport.Core.Animation
_originalCenter = animatedObject.BoundingBox().Center;
_currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, animatedObject.BoundingBox().Min.Z);
// 保持当前的 _currentYaw因为物体可能已经被 MoveVehicleToPathStart 旋转)
// 保持当前的 _currentYaw因为物体可能已经被 MoveObjectToPathStart 旋转)
// 不要从 Transform 中提取,因为 Transform 返回的是原始值,不是当前值
LogManager.Debug($"[CreateAnimation] 保持_currentYaw={_currentYaw * 180 / Math.PI:F2}度不变, _isVirtualVehicle={_isVirtualVehicle}");
LogManager.Debug($"[CreateAnimation] 保持_currentYaw={_currentYaw * 180 / Math.PI:F2}度不变, _useVirtualObject={_useVirtualObject}");
}
// 设置动画参数并预计算动画帧
// 注意物体应该在调用CreateAnimation之前已经通过MoveVehicleToPathStart旋转到起点
LogManager.Debug($"[CreateAnimation开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}");
// 注意物体应该在调用CreateAnimation之前已经通过MoveObjectToPathStart旋转到起点
LogManager.Debug($"[CreateAnimation开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _useVirtualObject={_useVirtualObject}");
SetupAnimation(animatedObject, durationSeconds, _route);
LogManager.Debug($"[CreateAnimation结束] _currentYaw之后={_currentYaw * 180 / Math.PI:F2}度");
// 设置动画状态为Ready动画已生成可以播放
@ -2858,7 +2907,7 @@ namespace NavisworksTransport.Core.Animation
try
{
// 重新计算并应用朝向
MoveVehicleToPathStart();
MoveObjectToPathStart();
LogManager.Info($"[角度修正] 物体角度已更新: {_objectRotationCorrection:F1}°");
}
catch (Exception ex)
@ -2930,7 +2979,7 @@ namespace NavisworksTransport.Core.Animation
}
else
{
// 无碰撞:清除碰撞高亮(保留车辆高亮)
// 无碰撞:清除碰撞高亮(保留物体高亮)
ModelHighlightHelper.ClearCategory(ModelHighlightHelper.PrecomputeCollisionResultsCategory);
LogManager.Debug($"[高亮状态] 帧{_currentFrameIndex}: 清除碰撞高亮");
}
@ -3131,10 +3180,10 @@ namespace NavisworksTransport.Core.Animation
sb.Append($"|DetectionTolerance:{_detectionTolerance:F4}");
sb.Append("|");
// 包含虚拟车辆尺寸(确保车辆尺寸改变时重新检测)
// 使用传入的车辆尺寸参数而不是从ConfigManager读取
var vehicleSizeString = $"Vehicle:{_virtualVehicleLength:F2}x{_virtualVehicleWidth:F2}x{_virtualVehicleHeight:F2}";
sb.Append(vehicleSizeString);
// 包含虚拟物体尺寸(确保物体尺寸改变时重新检测)
// 使用传入的物体尺寸参数而不是从ConfigManager读取
var objectSizeString = $"Object:{_virtualObjectLength:F2}x{_virtualObjectWidth:F2}x{_virtualObjectHeight:F2}";
sb.Append(objectSizeString);
sb.Append("|");
// 包含角度修正(确保角度修正改变时重新检测)

View File

@ -398,19 +398,19 @@ namespace NavisworksTransport.Core
// 获取运动物体
ModelItem animatedObject = null;
bool isVirtualVehicle = item.IsVirtualVehicle;
bool isVirtualObject = item.IsVirtualObject;
if (isVirtualVehicle)
if (isVirtualObject)
{
// 使用现有的虚拟车辆,不创建和清理
// ShowVirtualVehicle 会显示虚拟车辆并更新尺寸(如果已存在)
VirtualVehicleManager.Instance.ShowVirtualVehicle(
item.VirtualVehicleLength,
item.VirtualVehicleWidth,
item.VirtualVehicleHeight
// ShowVirtualObject 会显示虚拟车辆并更新尺寸(如果已存在)
VirtualObjectManager.Instance.ShowVirtualObject(
item.VirtualObjectLength,
item.VirtualObjectWidth,
item.VirtualObjectHeight
);
animatedObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
animatedObject = VirtualObjectManager.Instance.CurrentVirtualObject;
if (animatedObject == null)
{
@ -516,19 +516,19 @@ namespace NavisworksTransport.Core
FrameRate = item.FrameRate,
DurationSeconds = item.DurationSeconds,
DetectionToleranceMeters = item.DetectionToleranceMeters,
VirtualVehicleLength = item.VirtualVehicleLength,
VirtualVehicleWidth = item.VirtualVehicleWidth,
VirtualVehicleHeight = item.VirtualVehicleHeight,
VirtualObjectLength = item.VirtualObjectLength,
VirtualObjectWidth = item.VirtualObjectWidth,
VirtualObjectHeight = item.VirtualObjectHeight,
CollisionDetectionEnabled = true,
ReportGenerationEnabled = true
};
var frames = _processor.PrecomputeFrames(
pathRoute,
animatedObject,
isVirtualVehicle,
config.VirtualVehicleLength,
config.VirtualVehicleWidth,
config.VirtualVehicleHeight,
isVirtualObject,
config.VirtualObjectLength,
config.VirtualObjectWidth,
config.VirtualObjectHeight,
config.FrameRate,
config.DurationSeconds,
config.DetectionToleranceMeters,
@ -555,12 +555,12 @@ namespace NavisworksTransport.Core
pathRoute.Name,
pathRoute.Id,
animatedObject,
isVirtualVehicle,
isVirtualObject,
item.FrameRate,
item.DurationSeconds,
item.VirtualVehicleLength,
item.VirtualVehicleWidth,
item.VirtualVehicleHeight,
item.VirtualObjectLength,
item.VirtualObjectWidth,
item.VirtualObjectHeight,
pathPoints
);

View File

@ -26,10 +26,10 @@ namespace NavisworksTransport.Core.Collision
public List<AnimationFrame> PrecomputeFrames(
PathRoute route,
ModelItem animatedObject,
bool isVirtualVehicle,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
bool isVirtualObject,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
int frameRate,
double duration,
double detectionGap,
@ -43,7 +43,7 @@ namespace NavisworksTransport.Core.Collision
// 设置动画管理器参数但不触发UI操作
_animationManager.SetRoute(route);
_animationManager.SetAnimatedObject(animatedObject);
_animationManager.SetVirtualVehicleParameters(isVirtualVehicle, virtualVehicleLength, virtualVehicleWidth, virtualVehicleHeight);
_animationManager.SetVirtualObjectParameters(isVirtualObject, virtualObjectLength, virtualObjectWidth, virtualObjectHeight);
_animationManager.SetAnimationParameters(frameRate, duration, detectionGap);
_animationManager.SetObjectRotationCorrectionDirect(objectRotationCorrection);
@ -75,12 +75,12 @@ namespace NavisworksTransport.Core.Collision
string pathName,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null)
{
try
@ -93,12 +93,12 @@ namespace NavisworksTransport.Core.Collision
detectionGap,
routeId,
animatedObject,
isVirtualVehicle,
isVirtualObject,
frameRate,
duration,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
pathPoints
);

View File

@ -206,8 +206,8 @@ namespace NavisworksTransport
/// </summary>
private void SaveClashDetectiveResultToDatabase(string routeId, List<CollisionResult> clashResults,
int frameRate, double duration, double detectionGap,
ModelItem animatedObject, bool isVirtualVehicle,
double virtualVehicleLength, double virtualVehicleWidth, double virtualVehicleHeight,
ModelItem animatedObject, bool isVirtualObject,
double virtualObjectLength, double virtualObjectWidth, double virtualObjectHeight,
int precomputedCollisionCount = 0)
{
try
@ -217,24 +217,24 @@ namespace NavisworksTransport
{
// 获取动画对象名称和路径信息
string animatedObjectName = "未知对象";
int? vehicleModelIndex = null;
string vehiclePathId = null;
int? ObjectModelIndex = null;
string ObjectPathId = null;
if (!isVirtualVehicle && animatedObject != null)
if (!isVirtualObject && animatedObject != null)
{
animatedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animatedObject);
// 获取真实车辆的 PathId 信息
var pathId = Application.ActiveDocument.Models.CreatePathId(animatedObject);
vehicleModelIndex = pathId.ModelIndex;
vehiclePathId = pathId.PathId;
ObjectModelIndex = pathId.ModelIndex;
ObjectPathId = pathId.PathId;
}
else if (isVirtualVehicle)
else if (isVirtualObject)
{
animatedObjectName = "虚拟车辆";
}
// 打印虚拟车辆尺寸(用于调试)
LogManager.Info($"[SaveClashDetectiveResultToDatabase] IsVirtualVehicle={isVirtualVehicle}, 虚拟车辆尺寸: Length={virtualVehicleLength:F2}m, Width={virtualVehicleWidth:F2}m, Height={virtualVehicleHeight:F2}m");
// 打印虚拟物体尺寸(用于调试)
LogManager.Info($"[SaveClashDetectiveResultToDatabase] IsVirtualObject={isVirtualObject}, 虚拟物体尺寸: Length={virtualObjectLength:F2}m, Width={virtualObjectWidth:F2}m, Height={virtualObjectHeight:F2}m");
var record = new ClashDetectiveResultRecord
{
@ -247,16 +247,16 @@ namespace NavisworksTransport
Duration = duration,
DetectionGap = detectionGap,
AnimatedObjectName = animatedObjectName,
IsVirtualVehicle = isVirtualVehicle,
VehicleModelIndex = vehicleModelIndex,
VehiclePathId = vehiclePathId,
VirtualVehicleLength = virtualVehicleLength,
VirtualVehicleWidth = virtualVehicleWidth,
VirtualVehicleHeight = virtualVehicleHeight,
IsVirtualObject = isVirtualObject,
ObjectModelIndex = ObjectModelIndex,
ObjectPathId = ObjectPathId,
VirtualObjectLength = virtualObjectLength,
VirtualObjectWidth = virtualObjectWidth,
VirtualObjectHeight = virtualObjectHeight,
CreatedAt = DateTime.Now
};
var resultId = pathDatabase.SaveClashDetectiveResult(record);
LogManager.Info($"ClashDetective结果已保存到数据库Id={resultId}, IsVirtualVehicle={isVirtualVehicle}");
LogManager.Info($"ClashDetective结果已保存到数据库Id={resultId}, IsVirtualObject={isVirtualObject}");
// 保存被撞物体信息只保存Item2不保存车辆Item1
// 同时保存碰撞时运动物体的位置和朝向,用于还原碰撞场景
@ -355,8 +355,8 @@ namespace NavisworksTransport
// 1. 从数据库读取测试信息添加JOIN获取PathName
var testInfoSql = @"
SELECT cdr.Id, pr.Name AS PathName, cdr.RouteId, cdr.IsVirtualVehicle, cdr.VehicleModelIndex, cdr.VehiclePathId,
cdr.VirtualVehicleLength, cdr.VirtualVehicleWidth, cdr.VirtualVehicleHeight
SELECT cdr.Id, pr.Name AS PathName, cdr.RouteId, cdr.IsVirtualObject, cdr.ObjectModelIndex, cdr.ObjectPathId,
cdr.VirtualObjectLength, cdr.VirtualObjectWidth, cdr.VirtualObjectHeight
FROM ClashDetectiveResults cdr
INNER JOIN PathRoutes pr ON cdr.RouteId = pr.Id
WHERE cdr.TestName = @testName
@ -375,12 +375,12 @@ namespace NavisworksTransport
Id = Convert.ToInt32(reader["Id"]),
PathName = reader["PathName"].ToString(),
RouteId = reader["RouteId"]?.ToString(),
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
VehicleModelIndex = reader["VehicleModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["VehicleModelIndex"]) : (int?)null,
VehiclePathId = reader["VehiclePathId"] != DBNull.Value ? reader["VehiclePathId"].ToString() : null,
VirtualVehicleLength = reader["VirtualVehicleLength"] != DBNull.Value ? Convert.ToDouble(reader["VirtualVehicleLength"]) : 0.0,
VirtualVehicleWidth = reader["VirtualVehicleWidth"] != DBNull.Value ? Convert.ToDouble(reader["VirtualVehicleWidth"]) : 0.0,
VirtualVehicleHeight = reader["VirtualVehicleHeight"] != DBNull.Value ? Convert.ToDouble(reader["VirtualVehicleHeight"]) : 0.0
IsVirtualObject = Convert.ToBoolean(reader["IsVirtualObject"]),
ObjectModelIndex = reader["ObjectModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["ObjectModelIndex"]) : (int?)null,
ObjectPathId = reader["ObjectPathId"] != DBNull.Value ? reader["ObjectPathId"].ToString() : null,
VirtualObjectLength = reader["VirtualObjectLength"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectLength"]) : 0.0,
VirtualObjectWidth = reader["VirtualObjectWidth"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectWidth"]) : 0.0,
VirtualObjectHeight = reader["VirtualObjectHeight"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectHeight"]) : 0.0
};
}
}
@ -393,45 +393,45 @@ namespace NavisworksTransport
}
// 2. 重建车辆对象
ModelItem vehicleObject = null;
if (testInfo.IsVirtualVehicle)
ModelItem ObjectObject = null;
if (testInfo.IsVirtualObject)
{
// 显示虚拟车辆
var modelToMeters = UnitsConverter.GetUnitsToMetersConversionFactor();
VirtualVehicleManager.Instance.ShowVirtualVehicle(
testInfo.VirtualVehicleLength * modelToMeters,
testInfo.VirtualVehicleWidth * modelToMeters,
testInfo.VirtualVehicleHeight * modelToMeters
VirtualObjectManager.Instance.ShowVirtualObject(
testInfo.VirtualObjectLength * modelToMeters,
testInfo.VirtualObjectWidth * modelToMeters,
testInfo.VirtualObjectHeight * modelToMeters
);
vehicleObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle
ObjectObject = VirtualObjectManager.Instance.CurrentVirtualObject
?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 显示虚拟车辆失败");
}
else if (testInfo.VehicleModelIndex.HasValue && !string.IsNullOrEmpty(testInfo.VehiclePathId))
else if (testInfo.ObjectModelIndex.HasValue && !string.IsNullOrEmpty(testInfo.ObjectPathId))
{
// 通过 PathId 查找真实车辆
try
{
var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
{
ModelIndex = testInfo.VehicleModelIndex.Value,
PathId = testInfo.VehiclePathId
ModelIndex = testInfo.ObjectModelIndex.Value,
PathId = testInfo.ObjectPathId
};
vehicleObject = Application.ActiveDocument.Models.ResolvePathId(pathIdObj) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过 PathId 找到车辆对象: ModelIndex={testInfo.VehicleModelIndex}, PathId={testInfo.VehiclePathId}");
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 已找到真实车辆: {ModelItemAnalysisHelper.GetSafeDisplayName(vehicleObject)}");
ObjectObject = Application.ActiveDocument.Models.ResolvePathId(pathIdObj) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过 PathId 找到车辆对象: ModelIndex={testInfo.ObjectModelIndex}, PathId={testInfo.ObjectPathId}");
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 已找到真实车辆: {ModelItemAnalysisHelper.GetSafeDisplayName(ObjectObject)}");
}
catch (Exception ex)
{
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] ResolvePathId 失败: ModelIndex={testInfo.VehicleModelIndex}, PathId={testInfo.VehiclePathId}", ex);
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] ResolvePathId 失败: ModelIndex={testInfo.ObjectModelIndex}, PathId={testInfo.ObjectPathId}", ex);
}
}
if (vehicleObject == null)
if (ObjectObject == null)
{
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法重建车辆对象: IsVirtualVehicle={testInfo.IsVirtualVehicle}, VehicleModelIndex={testInfo.VehicleModelIndex}, VehiclePathId={testInfo.VehiclePathId}");
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法重建车辆对象: IsVirtualObject={testInfo.IsVirtualObject}, ObjectModelIndex={testInfo.ObjectModelIndex}, ObjectPathId={testInfo.ObjectPathId}");
}
// 如果是虚拟车辆,将其移动到路径起点
if (testInfo.IsVirtualVehicle && !string.IsNullOrEmpty(testInfo.RouteId))
if (testInfo.IsVirtualObject && !string.IsNullOrEmpty(testInfo.RouteId))
{
try
{
@ -446,7 +446,7 @@ namespace NavisworksTransport
// 使用 PathAnimationManager 将车辆移动到起点
var pathAnimationManager = PathAnimationManager.GetInstance();
pathAnimationManager.MoveVehicleToPathStart(vehicleObject, new List<Point3D> { startPointPosition, startPointPosition });
pathAnimationManager.MoveObjectToPathStart(ObjectObject, new List<Point3D> { startPointPosition, startPointPosition });
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟车辆已移动到路径起点: ({startPointPosition.X:F2}, {startPointPosition.Y:F2}, {startPointPosition.Z:F2})");
}
}
@ -529,7 +529,7 @@ namespace NavisworksTransport
ClashGuid = Guid.NewGuid(),
DisplayName = $"历史碰撞: {collidedObjectName}",
Status = ClashResultStatus.Active,
Item1 = vehicleObject,
Item1 = ObjectObject,
Item2 = collidedObject,
Center = collidedObject.BoundingBox().Center,
Distance = 0.0,
@ -669,12 +669,12 @@ namespace NavisworksTransport
double detectionGap,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
Progress progress = null,
int precomputedCollisionCount = 0)
{
@ -738,10 +738,10 @@ namespace NavisworksTransport
duration,
detectionGap,
animatedObject,
isVirtualVehicle,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
isVirtualObject,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
precomputedCollisionCount
);
@ -1061,8 +1061,8 @@ namespace NavisworksTransport
_clashDetectiveCollisionCount = clashResults.Count;
// 保存到数据库(使用去重后的结果)
SaveClashDetectiveResultToDatabase(routeId, finalClashResults, frameRate, duration, detectionGap, animatedObject, isVirtualVehicle,
virtualVehicleLength, virtualVehicleWidth, virtualVehicleHeight, precomputedCollisionCount);
SaveClashDetectiveResultToDatabase(routeId, finalClashResults, frameRate, duration, detectionGap, animatedObject, isVirtualObject,
virtualObjectLength, virtualObjectWidth, virtualObjectHeight, precomputedCollisionCount);
LogManager.Info($"[ClashDetective] 结果已保存到数据库");
@ -1077,24 +1077,24 @@ namespace NavisworksTransport
/// <param name="detectionGap">检测间隙容差</param>
/// <param name="routeId">路由ID</param>
/// <param name="animatedObject">动画对象(如果是真实车辆)</param>
/// <param name="isVirtualVehicle">是否使用虚拟车辆</param>
/// <param name="isVirtualObject">是否使用虚拟物体</param>
/// <param name="frameRate">帧率</param>
/// <param name="duration">动画时长</param>
/// <param name="virtualVehicleLength">虚拟车辆长度(米)</param>
/// <param name="virtualVehicleWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualVehicleHeight">虚拟车辆高度(米)</param>
/// <param name="virtualObjectLength">虚拟车辆长度(米)</param>
/// <param name="virtualObjectWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualObjectHeight">虚拟车辆高度(米)</param>
/// <param name="pathPoints">路径点列表(用于测试完成后恢复物体位置)</param>
public void CreateAllAnimationCollisionTests(
List<CollisionResult> precomputedCollisions,
double detectionGap,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null)
{
try
@ -1137,12 +1137,12 @@ namespace NavisworksTransport
detectionGap,
routeId,
animatedObject,
isVirtualVehicle,
isVirtualObject,
frameRate,
duration,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
progress,
precomputedCollisionCount
);
@ -1929,12 +1929,12 @@ namespace NavisworksTransport
double detectionGap,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null)
{
try
@ -1953,12 +1953,12 @@ namespace NavisworksTransport
detectionGap,
routeId,
animatedObject,
isVirtualVehicle,
isVirtualObject,
frameRate,
duration,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
null,
precomputedCollisionCount
);
@ -1987,7 +1987,7 @@ namespace NavisworksTransport
// {
// try
// {
// PathAnimationManager.GetInstance().MoveVehicleToPathStart(animatedObject, pathPoints);
// PathAnimationManager.GetInstance().MoveObjectToPathStart(animatedObject, pathPoints);
// LogManager.Info($"[批处理] 已将 {animatedObject.DisplayName} 恢复到路径起点位置");
// }
// catch (Exception restoreEx)

View File

@ -15,11 +15,11 @@ namespace NavisworksTransport.Core.Collision
/// 预计算动画帧和碰撞纯计算无UI操作
/// </summary>
/// <param name="route">路径</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟车辆</param>
/// <param name="isVirtualVehicle">是否使用虚拟车辆</param>
/// <param name="virtualVehicleLength">虚拟车辆长度(米)</param>
/// <param name="virtualVehicleWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualVehicleHeight">虚拟车辆高度(米)</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟物体</param>
/// <param name="isVirtualObject">是否使用虚拟物体</param>
/// <param name="virtualObjectLength">虚拟物体长度(米)</param>
/// <param name="virtualObjectWidth">虚拟物体宽度(米)</param>
/// <param name="virtualObjectHeight">虚拟物体高度(米)</param>
/// <param name="frameRate">帧率</param>
/// <param name="duration">持续时间(秒)</param>
/// <param name="detectionGap">检测间隙(米)</param>
@ -28,10 +28,10 @@ namespace NavisworksTransport.Core.Collision
List<AnimationFrame> PrecomputeFrames(
PathRoute route,
ModelItem animatedObject,
bool isVirtualVehicle,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
bool isVirtualObject,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
int frameRate,
double duration,
double detectionGap,
@ -46,13 +46,13 @@ namespace NavisworksTransport.Core.Collision
/// <param name="detectionGap">检测间隙(米)</param>
/// <param name="pathName">路径名称</param>
/// <param name="routeId">路径ID</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟车辆</param>
/// <param name="isVirtualVehicle">是否使用虚拟车辆</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟物体</param>
/// <param name="isVirtualObject">是否使用虚拟物体</param>
/// <param name="frameRate">帧率</param>
/// <param name="duration">持续时间(秒)</param>
/// <param name="virtualVehicleLength">虚拟车辆长度(米)</param>
/// <param name="virtualVehicleWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualVehicleHeight">虚拟车辆高度(米)</param>
/// <param name="virtualObjectLength">虚拟物体长度(米)</param>
/// <param name="virtualObjectWidth">虚拟物体宽度(米)</param>
/// <param name="virtualObjectHeight">虚拟物体高度(米)</param>
/// <param name="pathPoints">路径点列表(用于测试完成后恢复物体位置)</param>
/// <returns>ClashDetective测试名称</returns>
string CreateAndRunClashDetectiveTest(
@ -61,12 +61,12 @@ namespace NavisworksTransport.Core.Collision
string pathName,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null);
}
}

View File

@ -388,9 +388,9 @@ namespace NavisworksTransport.Core.Config
// 使用带默认值的读取方法,不抛出异常
config.PathEditing.CellSizeMeters = GetDoubleValueWithDefault(pathEdit, "cell_size_meters", 0.5, missingItems);
config.PathEditing.MaxHeightDiffMeters = GetDoubleValueWithDefault(pathEdit, "max_height_diff_meters", 0.35, missingItems);
config.PathEditing.VehicleLengthMeters = GetDoubleValueWithDefault(pathEdit, "vehicle_length_meters", 1.5, missingItems);
config.PathEditing.VehicleWidthMeters = GetDoubleValueWithDefault(pathEdit, "vehicle_width_meters", 1.0, missingItems);
config.PathEditing.VehicleHeightMeters = GetDoubleValueWithDefault(pathEdit, "vehicle_height_meters", 2.0, missingItems);
config.PathEditing.ObjectLengthMeters = GetDoubleValueWithDefault(pathEdit, "object_length_meters", 1.5, missingItems);
config.PathEditing.ObjectWidthMeters = GetDoubleValueWithDefault(pathEdit, "object_width_meters", 1.0, missingItems);
config.PathEditing.ObjectHeightMeters = GetDoubleValueWithDefault(pathEdit, "object_height_meters", 2.0, missingItems);
config.PathEditing.SafetyMarginMeters = GetDoubleValueWithDefault(pathEdit, "safety_margin_meters", 0.1, missingItems);
config.PathEditing.DefaultPathTurnRadiusMeters = GetDoubleValueWithDefault(pathEdit, "default_path_turn_radius", 2.5, missingItems);
config.PathEditing.ArcSamplingStepMeters = GetDoubleValueWithDefault(pathEdit, "arc_sampling_step", 0.05, missingItems);
@ -613,9 +613,9 @@ namespace NavisworksTransport.Core.Config
{
pathEdit["cell_size_meters"] = config.PathEditing.CellSizeMeters;
pathEdit["max_height_diff_meters"] = config.PathEditing.MaxHeightDiffMeters;
pathEdit["vehicle_length_meters"] = config.PathEditing.VehicleLengthMeters;
pathEdit["vehicle_width_meters"] = config.PathEditing.VehicleWidthMeters;
pathEdit["vehicle_height_meters"] = config.PathEditing.VehicleHeightMeters;
pathEdit["object_length_meters"] = config.PathEditing.ObjectLengthMeters;
pathEdit["object_width_meters"] = config.PathEditing.ObjectWidthMeters;
pathEdit["object_height_meters"] = config.PathEditing.ObjectHeightMeters;
pathEdit["safety_margin_meters"] = config.PathEditing.SafetyMarginMeters;
pathEdit["default_path_turn_radius"] = config.PathEditing.DefaultPathTurnRadiusMeters;
pathEdit["arc_sampling_step"] = config.PathEditing.ArcSamplingStepMeters;
@ -714,7 +714,7 @@ namespace NavisworksTransport.Core.Config
var newConfig = ParseTomlToConfig(templateContent);
LogManager.Info($"解析后的配置 - CellSizeMeters: {newConfig.PathEditing.CellSizeMeters}");
LogManager.Info($"解析后的配置 - VehicleLengthMeters: {newConfig.PathEditing.VehicleLengthMeters}");
LogManager.Info($"解析后的配置 - ObjectLengthMeters: {newConfig.PathEditing.ObjectLengthMeters}");
_currentConfig = newConfig;

View File

@ -72,19 +72,19 @@ namespace NavisworksTransport.Core.Config
public double MaxHeightDiffMeters { get; set; }
/// <summary>
/// 车辆长度(米)
/// 运动物体长度(米)
/// </summary>
public double VehicleLengthMeters { get; set; }
public double ObjectLengthMeters { get; set; }
/// <summary>
/// 车辆宽度(米)
/// 运动物体宽度(米)
/// </summary>
public double VehicleWidthMeters { get; set; }
public double ObjectWidthMeters { get; set; }
/// <summary>
/// 车辆高度(米)
/// 运动物体高度(米)
/// </summary>
public double VehicleHeightMeters { get; set; }
public double ObjectHeightMeters { get; set; }
/// <summary>
/// 安全间隙(米)
@ -114,19 +114,19 @@ namespace NavisworksTransport.Core.Config
public double MaxHeightDiff => MaxHeightDiffMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 车辆长度(模型单位)
/// 运动物体长度(模型单位)
/// </summary>
public double VehicleLength => VehicleLengthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
public double ObjectLength => ObjectLengthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 车辆宽度(模型单位)
/// 运动物体宽度(模型单位)
/// </summary>
public double VehicleWidth => VehicleWidthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
public double ObjectWidth => ObjectWidthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 车辆高度(模型单位)
/// 运动物体高度(模型单位)
/// </summary>
public double VehicleHeight => VehicleHeightMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
public double ObjectHeight => ObjectHeightMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 安全间隙(模型单位)

View File

@ -387,8 +387,8 @@ namespace NavisworksTransport
if (activeDoc != null && activeDoc.Models != null && activeDoc.Models.Count > 0)
{
LogManager.Info("[文档管理] 插件加载时发现包含模型的活动文档,立即初始化管理器");
// 🔥 插件加载时不是虚拟车辆变化,传入 false
InitializeManagers(isVirtualVehicleChange: false);
// 🔥 插件加载时不是虚拟物体变化,传入 false
InitializeManagers(isVirtualObjectChange: false);
}
else if (activeDoc != null)
{
@ -495,14 +495,14 @@ namespace NavisworksTransport
LogManager.Warning($"[文档管理] 订阅新文档事件时出现警告: {ex.Message}");
}
// 🔥 优化:检测是否是虚拟车辆引起的文档变化
bool isVirtualVehicleChange = IsVirtualVehicleChange(activeDoc);
if (isVirtualVehicleChange)
// 🔥 优化:检测是否是虚拟物体引起的文档变化
bool isVirtualObjectChange = IsVirtualObjectChange(activeDoc);
if (isVirtualObjectChange)
{
LogManager.Info("[文档管理] 检测到虚拟车辆文档变化,使用快速初始化模式");
LogManager.Info("[文档管理] 检测到虚拟物体文档变化,使用快速初始化模式");
}
InitializeManagers(isVirtualVehicleChange);
InitializeManagers(isVirtualObjectChange);
}
else
{
@ -513,29 +513,29 @@ namespace NavisworksTransport
}
/// <summary>
/// 检测是否是虚拟车辆引起的文档变化
/// 🔥 优化:通过 VirtualVehicleManager 的标志精确检测,避免误触发缓存重建
/// 检测是否是虚拟物体引起的文档变化
/// 🔥 优化:通过 VirtualObjectManager 的标志精确检测,避免误触发缓存重建
/// </summary>
private bool IsVirtualVehicleChange(Document doc)
private bool IsVirtualObjectChange(Document doc)
{
try
{
// 🔥 优先检查 VirtualVehicleManager 的更新标志(最准确)
if (VirtualVehicleManager.Instance.IsUpdatingVirtualVehicle)
// 🔥 优先检查 VirtualObjectManager 的更新标志(最准确)
if (VirtualObjectManager.Instance.IsUpdatingVirtualObject)
{
LogManager.Debug("[文档管理] VirtualVehicleManager 正在更新,判定为虚拟车辆变化");
LogManager.Debug("[文档管理] VirtualObjectManager 正在更新,判定为虚拟物体变化");
return true;
}
if (doc?.Models == null) return false;
// 备选:检查是否包含虚拟车辆模型
// 备选:检查是否包含虚拟物体模型
foreach (var model in doc.Models)
{
var modelName = model.RootItem?.DisplayName ?? "";
if (modelName.Contains("unit_cube") || modelName.Contains("VirtualVehicle"))
if (modelName.Contains("unit_cube") || modelName.Contains("VirtualObject"))
{
LogManager.Debug($"[文档管理] 检测到虚拟车辆模型: {modelName}");
LogManager.Debug($"[文档管理] 检测到虚拟物体模型: {modelName}");
return true;
}
}
@ -544,7 +544,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.Debug($"[文档管理] 检测虚拟车辆变化时出错: {ex.Message}");
LogManager.Debug($"[文档管理] 检测虚拟物体变化时出错: {ex.Message}");
return false;
}
}
@ -693,8 +693,8 @@ namespace NavisworksTransport
/// <summary>
/// 初始化各个管理器
/// </summary>
/// <param name="isVirtualVehicleChange">是否由虚拟车辆变化引起</param>
private void InitializeManagers(bool isVirtualVehicleChange = false)
/// <param name="isVirtualObjectChange">是否由虚拟物体变化引起</param>
private void InitializeManagers(bool isVirtualObjectChange = false)
{
try
{
@ -713,10 +713,10 @@ namespace NavisworksTransport
// 通知DocumentStateManager文档已就绪
DocumentStateManager.Instance.OnDocumentReady();
// 🔥 优化:虚拟车辆变化时不清除缓存避免80秒重建
if (isVirtualVehicleChange)
// 🔥 优化:虚拟物体变化时不清除缓存避免80秒重建
if (isVirtualObjectChange)
{
LogManager.Info("[文档管理] 检测到虚拟车辆变化,保留现有缓存,仅更新运动物体引用");
LogManager.Info("[文档管理] 检测到虚拟物体变化,保留现有缓存,仅更新运动物体引用");
// 只清除运动物体引用,不清除基础缓存
ClashDetectiveIntegration.ClearAnimatedObject();
}
@ -811,15 +811,15 @@ namespace NavisworksTransport
LogManager.Warning($"[文档管理] 清理Idle事件管理器时出现警告: {ex.Message}");
}
// 清理虚拟车辆
// 清理虚拟物体
try
{
VirtualVehicleManager.Instance.Cleanup();
LogManager.Info("[文档管理] 已清理虚拟车辆");
VirtualObjectManager.Instance.Cleanup();
LogManager.Info("[文档管理] 已清理虚拟物体");
}
catch (Exception ex)
{
LogManager.Warning($"[文档管理] 清理虚拟车辆时出现警告: {ex.Message}");
LogManager.Warning($"[文档管理] 清理虚拟物体时出现警告: {ex.Message}");
}
// 清除临时材质

View File

@ -25,11 +25,11 @@ namespace NavisworksTransport.Core.Models
public double DetectionToleranceMeters { get; set; }
// 运动物体配置
public bool IsVirtualVehicle { get; set; }
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
public string MovingObjectName { get; set; } // 运动物体名称,虚拟车辆时为 null
public bool IsVirtualObject { get; set; }
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
public string MovingObjectName { get; set; } // 运动物体名称,虚拟物体时为 null
// 碰撞检测配置
public bool DetectAllObjects { get; set; } = true;

View File

@ -11,10 +11,10 @@ namespace NavisworksTransport.Core.Models
public bool CollisionDetectionEnabled { get; set; }
public bool ReportGenerationEnabled { get; set; }
// 虚拟车辆参数
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
// 虚拟物体参数
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
/// <summary>
/// 序列化为JSON字符串简化版

View File

@ -31,7 +31,7 @@ namespace NavisworksTransport
/// <summary>
/// JSON格式的车辆限制数据
/// </summary>
public class JsonVehicleLimits
public class JsonObjectLimits
{
public double maxLength { get; set; }
public double maxWidth { get; set; }
@ -49,7 +49,7 @@ namespace NavisworksTransport
public string description { get; set; }
public string pathType { get; set; }
public double totalLength { get; set; }
public JsonVehicleLimits vehicleLimits { get; set; }
public JsonObjectLimits ObjectLimits { get; set; }
public double gridSize { get; set; }
public double liftHeightMeters { get; set; }
public string created { get; set; }
@ -270,11 +270,11 @@ namespace NavisworksTransport
description = route.Description ?? "",
pathType = route.PathType.ToString(),
totalLength = Math.Round(route.TotalLength, exportSettings?.Precision ?? 3),
vehicleLimits = new
ObjectLimits = new
{
maxLength = Math.Round(route.MaxVehicleLength, exportSettings?.Precision ?? 3),
maxWidth = Math.Round(route.MaxVehicleWidth, exportSettings?.Precision ?? 3),
maxHeight = Math.Round(route.MaxVehicleHeight, exportSettings?.Precision ?? 3),
maxLength = Math.Round(route.MaxObjectLength, exportSettings?.Precision ?? 3),
maxWidth = Math.Round(route.MaxObjectWidth, exportSettings?.Precision ?? 3),
maxHeight = Math.Round(route.MaxObjectHeight, exportSettings?.Precision ?? 3),
safetyMargin = Math.Round(route.SafetyMargin, exportSettings?.Precision ?? 3)
},
gridSize = Math.Round(route.GridSize, exportSettings?.Precision ?? 3),
@ -476,10 +476,10 @@ namespace NavisworksTransport
Id = jsonRoute.id,
Description = jsonRoute.description ?? "",
CreatedTime = ParseJsonDateTime(jsonRoute.created),
MaxVehicleLength = jsonRoute.vehicleLimits?.maxLength ?? 0,
MaxVehicleWidth = jsonRoute.vehicleLimits?.maxWidth ?? 0,
MaxVehicleHeight = jsonRoute.vehicleLimits?.maxHeight ?? 0,
SafetyMargin = jsonRoute.vehicleLimits?.safetyMargin ?? 0,
MaxObjectLength = jsonRoute.ObjectLimits?.maxLength ?? 0,
MaxObjectWidth = jsonRoute.ObjectLimits?.maxWidth ?? 0,
MaxObjectHeight = jsonRoute.ObjectLimits?.maxHeight ?? 0,
SafetyMargin = jsonRoute.ObjectLimits?.safetyMargin ?? 0,
GridSize = jsonRoute.gridSize,
LiftHeightMeters = jsonRoute.liftHeightMeters
};
@ -989,9 +989,9 @@ namespace NavisworksTransport
routeElement.SetAttribute("description", route.Description ?? "");
routeElement.SetAttribute("pathType", route.PathType.ToString());
routeElement.SetAttribute("totalLength", route.TotalLength.ToString("F3"));
routeElement.SetAttribute("maxVehicleLength", route.MaxVehicleLength.ToString("F3"));
routeElement.SetAttribute("maxVehicleWidth", route.MaxVehicleWidth.ToString("F3"));
routeElement.SetAttribute("maxVehicleHeight", route.MaxVehicleHeight.ToString("F3"));
routeElement.SetAttribute("maxObjectLength", route.MaxObjectLength.ToString("F3"));
routeElement.SetAttribute("maxObjectWidth", route.MaxObjectWidth.ToString("F3"));
routeElement.SetAttribute("maxObjectHeight", route.MaxObjectHeight.ToString("F3"));
routeElement.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
routeElement.SetAttribute("gridSize", route.GridSize.ToString("F3"));
routeElement.SetAttribute("liftHeightMeters", route.LiftHeightMeters.ToString("F3"));
@ -1112,9 +1112,9 @@ namespace NavisworksTransport
Name = routeNode.Attributes?["name"]?.Value ?? "导入路径",
Description = routeNode.Attributes?["description"]?.Value ?? "",
PathType = pathType,
MaxVehicleLength = double.TryParse(routeNode.Attributes?["maxVehicleLength"]?.Value, out double maxLength) ? maxLength : 0,
MaxVehicleWidth = double.TryParse(routeNode.Attributes?["maxVehicleWidth"]?.Value, out double maxWidth) ? maxWidth : 0,
MaxVehicleHeight = double.TryParse(routeNode.Attributes?["maxVehicleHeight"]?.Value, out double maxHeight) ? maxHeight : 0,
MaxObjectLength = double.TryParse(routeNode.Attributes?["maxObjectLength"]?.Value, out double maxLength) ? maxLength : 0,
MaxObjectWidth = double.TryParse(routeNode.Attributes?["maxObjectWidth"]?.Value, out double maxWidth) ? maxWidth : 0,
MaxObjectHeight = double.TryParse(routeNode.Attributes?["maxObjectHeight"]?.Value, out double maxHeight) ? maxHeight : 0,
SafetyMargin = double.TryParse(routeNode.Attributes?["safetyMargin"]?.Value, out double safetyMargin) ? safetyMargin : 0,
GridSize = double.TryParse(routeNode.Attributes?["gridSize"]?.Value, out double gridSize) ? gridSize : 0,
LiftHeightMeters = double.TryParse(routeNode.Attributes?["liftHeightMeters"]?.Value, out double liftHeight) ? liftHeight : 0
@ -1212,13 +1212,13 @@ namespace NavisworksTransport
pathElement.SetAttribute("length", route.TotalLength.ToString("F3"));
// 添加车辆限制元素
var vehicleConstraints = xmlDoc.CreateElement("VehicleConstraints", _delmiaNamespace);
vehicleConstraints.SetAttribute("maxLength", route.MaxVehicleLength.ToString("F3"));
vehicleConstraints.SetAttribute("maxWidth", route.MaxVehicleWidth.ToString("F3"));
vehicleConstraints.SetAttribute("maxHeight", route.MaxVehicleHeight.ToString("F3"));
vehicleConstraints.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
vehicleConstraints.SetAttribute("gridSize", route.GridSize.ToString("F3"));
pathElement.AppendChild(vehicleConstraints);
var ObjectConstraints = xmlDoc.CreateElement("ObjectConstraints", _delmiaNamespace);
ObjectConstraints.SetAttribute("maxLength", route.MaxObjectLength.ToString("F3"));
ObjectConstraints.SetAttribute("maxWidth", route.MaxObjectWidth.ToString("F3"));
ObjectConstraints.SetAttribute("maxHeight", route.MaxObjectHeight.ToString("F3"));
ObjectConstraints.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
ObjectConstraints.SetAttribute("gridSize", route.GridSize.ToString("F3"));
pathElement.AppendChild(ObjectConstraints);
// 添加路径段
var segmentsElement = xmlDoc.CreateElement("Segments", _delmiaNamespace);

View File

@ -62,6 +62,12 @@ namespace NavisworksTransport
// 这样可以确保所有表都存在,避免在旧数据库中缺少新表的问题
CreateTables();
// 执行数据库迁移(处理字段名变更等)
if (!isNewDatabase)
{
RunMigrations();
}
if (isNewDatabase)
{
LogManager.Info($"创建新数据库: {_dbPath}");
@ -86,9 +92,9 @@ namespace NavisworksTransport
EstimatedTime REAL,
TurnRadius REAL,
IsCurved INTEGER,
MaxVehicleLength REAL,
MaxVehicleWidth REAL,
MaxVehicleHeight REAL,
MaxObjectLength REAL,
MaxObjectWidth REAL,
MaxObjectHeight REAL,
SafetyMargin REAL,
GridSize REAL,
PathType INTEGER,
@ -184,12 +190,12 @@ namespace NavisworksTransport
Duration REAL,
DetectionGap REAL,
AnimatedObjectName TEXT,
IsVirtualVehicle INTEGER,
VehicleModelIndex INTEGER,
VehiclePathId TEXT,
VirtualVehicleLength REAL,
VirtualVehicleWidth REAL,
VirtualVehicleHeight REAL,
IsVirtualObject INTEGER,
ObjectModelIndex INTEGER,
ObjectPathId TEXT,
VirtualObjectLength REAL,
VirtualObjectWidth REAL,
VirtualObjectHeight REAL,
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
)
");
@ -260,10 +266,10 @@ namespace NavisworksTransport
FrameRate INTEGER NOT NULL,
DurationSeconds REAL NOT NULL,
DetectionToleranceMeters REAL NOT NULL,
IsVirtualVehicle INTEGER NOT NULL,
VirtualVehicleLength REAL,
VirtualVehicleWidth REAL,
VirtualVehicleHeight REAL,
IsVirtualObject INTEGER NOT NULL,
VirtualObjectLength REAL,
VirtualObjectWidth REAL,
VirtualObjectHeight REAL,
DetectAllObjects INTEGER NOT NULL DEFAULT 1,
ClashDetectiveTestName TEXT,
CollisionCount INTEGER,
@ -339,6 +345,124 @@ namespace NavisworksTransport
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_excluded_object ON ClashDetectiveExcludedObjects(ExcludedObjectId)");
}
/// <summary>
/// 执行数据库迁移
/// 处理字段名变更等结构升级
/// </summary>
private void RunMigrations()
{
try
{
// 迁移1: ClashDetectiveResults 表字段名变更 (VirtualVehicleXxx -> VirtualObjectXxx)
MigrateClashDetectiveResultsTable();
// 迁移2: BatchQueueItems 表字段名变更
MigrateBatchQueueItemsTable();
}
catch (Exception ex)
{
LogManager.Warning($"[数据库迁移] 执行迁移时出错: {ex.Message}");
}
}
/// <summary>
/// 迁移 ClashDetectiveResults 表字段名
/// </summary>
private void MigrateClashDetectiveResultsTable()
{
try
{
// 检查是否存在旧字段名
var columns = GetTableColumns("ClashDetectiveResults");
// 迁移 VirtualVehicleWidth -> VirtualObjectWidth
if (columns.Contains("VirtualVehicleWidth") && !columns.Contains("VirtualObjectWidth"))
{
ExecuteNonQuery("ALTER TABLE ClashDetectiveResults RENAME COLUMN VirtualVehicleWidth TO VirtualObjectWidth");
LogManager.Info("[数据库迁移] ClashDetectiveResults.VirtualVehicleWidth -> VirtualObjectWidth");
}
// 迁移 VirtualVehicleHeight -> VirtualObjectHeight
if (columns.Contains("VirtualVehicleHeight") && !columns.Contains("VirtualObjectHeight"))
{
ExecuteNonQuery("ALTER TABLE ClashDetectiveResults RENAME COLUMN VirtualVehicleHeight TO VirtualObjectHeight");
LogManager.Info("[数据库迁移] ClashDetectiveResults.VirtualVehicleHeight -> VirtualObjectHeight");
}
// 迁移 VirtualObjecctLength -> VirtualObjectLength (同时修复拼写错误)
if (columns.Contains("VirtualObjecctLength") && !columns.Contains("VirtualObjectLength"))
{
ExecuteNonQuery("ALTER TABLE ClashDetectiveResults RENAME COLUMN VirtualObjecctLength TO VirtualObjectLength");
LogManager.Info("[数据库迁移] ClashDetectiveResults.VirtualObjecctLength -> VirtualObjectLength");
}
}
catch (Exception ex)
{
LogManager.Warning($"[数据库迁移] ClashDetectiveResults 表迁移失败: {ex.Message}");
}
}
/// <summary>
/// 迁移 BatchQueueItems 表字段名
/// </summary>
private void MigrateBatchQueueItemsTable()
{
try
{
// 检查是否存在旧字段名
var columns = GetTableColumns("BatchQueueItems");
// 迁移 VirtualVehicleWidth -> VirtualObjectWidth
if (columns.Contains("VirtualVehicleWidth") && !columns.Contains("VirtualObjectWidth"))
{
ExecuteNonQuery("ALTER TABLE BatchQueueItems RENAME COLUMN VirtualVehicleWidth TO VirtualObjectWidth");
LogManager.Info("[数据库迁移] BatchQueueItems.VirtualVehicleWidth -> VirtualObjectWidth");
}
// 迁移 VirtualVehicleHeight -> VirtualObjectHeight
if (columns.Contains("VirtualVehicleHeight") && !columns.Contains("VirtualObjectHeight"))
{
ExecuteNonQuery("ALTER TABLE BatchQueueItems RENAME COLUMN VirtualVehicleHeight TO VirtualObjectHeight");
LogManager.Info("[数据库迁移] BatchQueueItems.VirtualVehicleHeight -> VirtualObjectHeight");
}
// 迁移 VirtualObjecctLength -> VirtualObjectLength
if (columns.Contains("VirtualObjecctLength") && !columns.Contains("VirtualObjectLength"))
{
ExecuteNonQuery("ALTER TABLE BatchQueueItems RENAME COLUMN VirtualObjecctLength TO VirtualObjectLength");
LogManager.Info("[数据库迁移] BatchQueueItems.VirtualObjecctLength -> VirtualObjectLength");
}
}
catch (Exception ex)
{
LogManager.Warning($"[数据库迁移] BatchQueueItems 表迁移失败: {ex.Message}");
}
}
/// <summary>
/// 获取表的列名列表
/// </summary>
private List<string> GetTableColumns(string tableName)
{
var columns = new List<string>();
try
{
using (var cmd = new SQLiteCommand($"PRAGMA table_info({tableName})", _connection))
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
columns.Add(reader["name"].ToString());
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[数据库迁移] 获取 {tableName} 表列信息失败: {ex.Message}");
}
return columns;
}
/// <summary>
/// 保存路径基本信息
/// </summary>
@ -352,7 +476,7 @@ namespace NavisworksTransport
// 保存路径基本信息
var sql = @"
INSERT OR REPLACE INTO PathRoutes
(Id, Name, TotalLength, EstimatedTime, TurnRadius, IsCurved, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight, SafetyMargin, GridSize, PathType, LiftHeightMeters, CreatedTime, LastModified)
(Id, Name, TotalLength, EstimatedTime, TurnRadius, IsCurved, MaxObjectLength, MaxObjectWidth, MaxObjectHeight, SafetyMargin, GridSize, PathType, LiftHeightMeters, CreatedTime, LastModified)
VALUES (@id, @name, @length, @time, @turnRadius, @isCurved, @maxLength, @maxWidth, @maxHeight, @safetyMargin, @gridSize, @pathType, @liftHeightMeters, @created, @modified)
";
@ -364,9 +488,9 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@time", route.EstimatedTime);
cmd.Parameters.AddWithValue("@turnRadius", route.TurnRadius);
cmd.Parameters.AddWithValue("@isCurved", route.IsCurved ? 1 : 0);
cmd.Parameters.AddWithValue("@maxLength", route.MaxVehicleLength);
cmd.Parameters.AddWithValue("@maxWidth", route.MaxVehicleWidth);
cmd.Parameters.AddWithValue("@maxHeight", route.MaxVehicleHeight);
cmd.Parameters.AddWithValue("@maxLength", route.MaxObjectLength);
cmd.Parameters.AddWithValue("@maxWidth", route.MaxObjectWidth);
cmd.Parameters.AddWithValue("@maxHeight", route.MaxObjectHeight);
cmd.Parameters.AddWithValue("@safetyMargin", route.SafetyMargin);
cmd.Parameters.AddWithValue("@gridSize", route.GridSize);
cmd.Parameters.AddWithValue("@pathType", (int)route.PathType);
@ -755,11 +879,11 @@ namespace NavisworksTransport
var sql = @"
INSERT INTO ClashDetectiveResults
(TestName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelIndex, VehiclePathId,
VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight, CreatedAt)
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualObject, ObjectModelIndex, ObjectPathId,
VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight, CreatedAt)
VALUES (@testName, @routeId, @testTime, @collisionCount, @animationCollisionCount,
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualVehicle, @vehicleModelIndex, @vehiclePathId,
@virtualVehicleLength, @virtualVehicleWidth, @virtualVehicleHeight, @createdAt)
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualObject, @ObjectModelIndex, @ObjectPathId,
@virtualObjectLength, @virtualObjectWidth, @virtualObjectHeight, @createdAt)
";
long newId = 0;
@ -774,12 +898,12 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@duration", record.Duration);
cmd.Parameters.AddWithValue("@detectionGap", record.DetectionGap);
cmd.Parameters.AddWithValue("@animatedObjectName", record.AnimatedObjectName ?? "");
cmd.Parameters.AddWithValue("@isVirtualVehicle", record.IsVirtualVehicle);
cmd.Parameters.AddWithValue("@vehicleModelIndex", record.VehicleModelIndex.HasValue ? (object)record.VehicleModelIndex.Value : DBNull.Value);
cmd.Parameters.AddWithValue("@vehiclePathId", record.VehiclePathId ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@virtualVehicleLength", record.VirtualVehicleLength);
cmd.Parameters.AddWithValue("@virtualVehicleWidth", record.VirtualVehicleWidth);
cmd.Parameters.AddWithValue("@virtualVehicleHeight", record.VirtualVehicleHeight);
cmd.Parameters.AddWithValue("@isVirtualObject", record.IsVirtualObject);
cmd.Parameters.AddWithValue("@ObjectModelIndex", record.ObjectModelIndex.HasValue ? (object)record.ObjectModelIndex.Value : DBNull.Value);
cmd.Parameters.AddWithValue("@ObjectPathId", record.ObjectPathId ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@virtualObjectLength", record.VirtualObjectLength);
cmd.Parameters.AddWithValue("@virtualObjectWidth", record.VirtualObjectWidth);
cmd.Parameters.AddWithValue("@virtualObjectHeight", record.VirtualObjectHeight);
cmd.Parameters.AddWithValue("@createdAt", record.CreatedAt);
cmd.ExecuteNonQuery();
newId = _connection.LastInsertRowId;
@ -1320,9 +1444,9 @@ namespace NavisworksTransport
EstimatedTime = Convert.ToDouble(reader["EstimatedTime"]),
TurnRadius = Convert.ToDouble(reader["TurnRadius"]),
IsCurved = Convert.ToInt32(reader["IsCurved"]) == 1,
MaxVehicleLength = Convert.ToDouble(reader["MaxVehicleLength"]),
MaxVehicleWidth = Convert.ToDouble(reader["MaxVehicleWidth"]),
MaxVehicleHeight = Convert.ToDouble(reader["MaxVehicleHeight"]),
MaxObjectLength = Convert.ToDouble(reader["MaxObjectLength"]),
MaxObjectWidth = Convert.ToDouble(reader["MaxObjectWidth"]),
MaxObjectHeight = Convert.ToDouble(reader["MaxObjectHeight"]),
SafetyMargin = Convert.ToDouble(reader["SafetyMargin"]),
GridSize = Convert.ToDouble(reader["GridSize"]),
PathType = (PathType)Convert.ToInt32(reader["PathType"]),
@ -2099,7 +2223,7 @@ namespace NavisworksTransport
using (var cmd = new SQLiteCommand(_connection))
{
cmd.CommandText = @"
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount, FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelIndex, VehiclePathId, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight, CreatedAt
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount, FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualObject, ObjectModelIndex, ObjectPathId, VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight, CreatedAt
FROM ClashDetectiveResults
WHERE TestName = @TestName";
@ -2122,12 +2246,12 @@ namespace NavisworksTransport
Duration = Convert.ToDouble(reader["Duration"]),
DetectionGap = Convert.ToDouble(reader["DetectionGap"]),
AnimatedObjectName = reader["AnimatedObjectName"].ToString(),
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
VehicleModelIndex = !Convert.IsDBNull(reader["VehicleModelIndex"]) ? (int?)Convert.ToInt32(reader["VehicleModelIndex"]) : null,
VehiclePathId = reader["VehiclePathId"].ToString(),
VirtualVehicleLength = Convert.ToDouble(reader["VirtualVehicleLength"]),
VirtualVehicleWidth = Convert.ToDouble(reader["VirtualVehicleWidth"]),
VirtualVehicleHeight = Convert.ToDouble(reader["VirtualVehicleHeight"]),
IsVirtualObject = Convert.ToBoolean(reader["IsVirtualObject"]),
ObjectModelIndex = !Convert.IsDBNull(reader["ObjectModelIndex"]) ? (int?)Convert.ToInt32(reader["ObjectModelIndex"]) : null,
ObjectPathId = reader["ObjectPathId"].ToString(),
VirtualObjectLength = Convert.ToDouble(reader["VirtualObjectLength"]),
VirtualObjectWidth = Convert.ToDouble(reader["VirtualObjectWidth"]),
VirtualObjectHeight = Convert.ToDouble(reader["VirtualObjectHeight"]),
CreatedAt = DateTime.Parse(reader["CreatedAt"].ToString())
};
}
@ -2157,7 +2281,7 @@ namespace NavisworksTransport
{
cmd.CommandText = @"
SELECT Id, Name, CreatedTime, LastModified, TotalLength,
TurnRadius, IsCurved, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight,
TurnRadius, IsCurved, MaxObjectLength, MaxObjectWidth, MaxObjectHeight,
SafetyMargin, GridSize, PathType, LiftHeightMeters
FROM PathRoutes
WHERE Id = @Id";
@ -2176,9 +2300,9 @@ namespace NavisworksTransport
TotalLength = Convert.ToDouble(reader["TotalLength"]),
TurnRadius = Convert.ToDouble(reader["TurnRadius"]),
IsCurved = Convert.ToInt32(reader["IsCurved"]) == 1,
MaxVehicleLength = Convert.ToDouble(reader["MaxVehicleLength"]),
MaxVehicleWidth = Convert.ToDouble(reader["MaxVehicleWidth"]),
MaxVehicleHeight = Convert.ToDouble(reader["MaxVehicleHeight"]),
MaxObjectLength = Convert.ToDouble(reader["MaxObjectLength"]),
MaxObjectWidth = Convert.ToDouble(reader["MaxObjectWidth"]),
MaxObjectHeight = Convert.ToDouble(reader["MaxObjectHeight"]),
SafetyMargin = Convert.ToDouble(reader["SafetyMargin"]),
GridSize = Convert.ToDouble(reader["GridSize"]),
PathType = (PathType)Convert.ToInt32(reader["PathType"]),
@ -2212,14 +2336,14 @@ namespace NavisworksTransport
INSERT INTO BatchQueueItems (
RouteId, Status, CreatedTime, StartTime, EndTime, ErrorMessage,
FrameRate, DurationSeconds, DetectionToleranceMeters,
IsVirtualVehicle, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight,
IsVirtualObject, VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight,
DetectAllObjects,
ClashDetectiveTestName, CollisionCount, ObjectRotationCorrection
)
VALUES (
@RouteId, @Status, @CreatedTime, @StartTime, @EndTime, @ErrorMessage,
@FrameRate, @DurationSeconds, @DetectionToleranceMeters,
@IsVirtualVehicle, @VirtualVehicleLength, @VirtualVehicleWidth, @VirtualVehicleHeight,
@IsVirtualObject, @VirtualObjectLength, @VirtualObjectWidth, @VirtualObjectHeight,
@DetectAllObjects,
@ClashDetectiveTestName, @CollisionCount, @ObjectRotationCorrection
);
@ -2234,10 +2358,10 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@FrameRate", item.FrameRate);
cmd.Parameters.AddWithValue("@DurationSeconds", item.DurationSeconds);
cmd.Parameters.AddWithValue("@DetectionToleranceMeters", item.DetectionToleranceMeters);
cmd.Parameters.AddWithValue("@IsVirtualVehicle", item.IsVirtualVehicle ? 1 : 0);
cmd.Parameters.AddWithValue("@VirtualVehicleLength", item.VirtualVehicleLength);
cmd.Parameters.AddWithValue("@VirtualVehicleWidth", item.VirtualVehicleWidth);
cmd.Parameters.AddWithValue("@VirtualVehicleHeight", item.VirtualVehicleHeight);
cmd.Parameters.AddWithValue("@IsVirtualObject", item.IsVirtualObject ? 1 : 0);
cmd.Parameters.AddWithValue("@VirtualObjectLength", item.VirtualObjectLength);
cmd.Parameters.AddWithValue("@VirtualObjectWidth", item.VirtualObjectWidth);
cmd.Parameters.AddWithValue("@VirtualObjectHeight", item.VirtualObjectHeight);
cmd.Parameters.AddWithValue("@DetectAllObjects", item.DetectAllObjects ? 1 : 0);
cmd.Parameters.AddWithValue("@ClashDetectiveTestName", item.ClashDetectiveTestName ?? "");
cmd.Parameters.AddWithValue("@CollisionCount", item.CollisionCount ?? (object)DBNull.Value);
@ -2261,7 +2385,7 @@ namespace NavisworksTransport
private BatchQueueItem ReadBatchQueueItemFromReader(SQLiteDataReader reader)
{
var itemId = Convert.ToInt32(reader["Id"]);
var isVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]);
var isVirtualObject = Convert.ToBoolean(reader["IsVirtualObject"]);
var item = new BatchQueueItem
{
@ -2277,10 +2401,10 @@ namespace NavisworksTransport
FrameRate = Convert.ToInt32(reader["FrameRate"]),
DurationSeconds = Convert.ToDouble(reader["DurationSeconds"]),
DetectionToleranceMeters = Convert.ToDouble(reader["DetectionToleranceMeters"]),
IsVirtualVehicle = isVirtualVehicle,
VirtualVehicleLength = Convert.ToDouble(reader["VirtualVehicleLength"]),
VirtualVehicleWidth = Convert.ToDouble(reader["VirtualVehicleWidth"]),
VirtualVehicleHeight = Convert.ToDouble(reader["VirtualVehicleHeight"]),
IsVirtualObject = isVirtualObject,
VirtualObjectLength = Convert.ToDouble(reader["VirtualObjectLength"]),
VirtualObjectWidth = Convert.ToDouble(reader["VirtualObjectWidth"]),
VirtualObjectHeight = Convert.ToDouble(reader["VirtualObjectHeight"]),
DetectAllObjects = Convert.ToBoolean(reader["DetectAllObjects"]),
ClashDetectiveTestName = reader["ClashDetectiveTestName"].ToString(),
CollisionCount = !Convert.IsDBNull(reader["CollisionCount"]) ? (int?)Convert.ToInt32(reader["CollisionCount"]) : null,
@ -2288,7 +2412,7 @@ namespace NavisworksTransport
};
// 填充 MovingObjectName 属性
if (!isVirtualVehicle)
if (!isVirtualObject)
{
// 从 ModelItemReferences 表查询运动物体名称
var movingObjectReferences = GetModelItemReferencesSync(itemId, "BatchQueueItem", "MovingObject");
@ -2315,7 +2439,7 @@ namespace NavisworksTransport
var sql = @"
SELECT bqi.Id, bqi.RouteId, pr.Name AS PathRouteName, pr.PathType, bqi.Status, bqi.CreatedTime, bqi.StartTime, bqi.EndTime, bqi.ErrorMessage,
bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
bqi.IsVirtualVehicle, bqi.VirtualVehicleLength, bqi.VirtualVehicleWidth, bqi.VirtualVehicleHeight,
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
bqi.DetectAllObjects,
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection
FROM BatchQueueItems bqi
@ -2374,7 +2498,7 @@ namespace NavisworksTransport
cmd.CommandText = @"
SELECT bqi.Id, bqi.RouteId, pr.Name AS PathRouteName, pr.PathType, bqi.Status, bqi.CreatedTime, bqi.StartTime, bqi.EndTime, bqi.ErrorMessage,
bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
bqi.IsVirtualVehicle, bqi.VirtualVehicleLength, bqi.VirtualVehicleWidth, bqi.VirtualVehicleHeight,
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
bqi.DetectAllObjects,
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection
FROM BatchQueueItems bqi
@ -2607,12 +2731,12 @@ namespace NavisworksTransport
public double Duration { get; set; }
public double DetectionGap { get; set; }
public string AnimatedObjectName { get; set; }
public bool IsVirtualVehicle { get; set; }
public int? VehicleModelIndex { get; set; }
public string VehiclePathId { get; set; }
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
public bool IsVirtualObject { get; set; }
public int? ObjectModelIndex { get; set; }
public string ObjectPathId { get; set; }
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
public DateTime CreatedAt { get; set; }
}

View File

@ -74,7 +74,7 @@ namespace NavisworksTransport
private bool _showUnknownGrid = false;
private bool _showDoorGrid = false;
private GridMap _currentGridMap = null; // 保存当前网格地图用于刷新
private double _currentVehicleHeight = 2.0; // 保存当前车辆高度(米)用于网格刷新
private double _currentObjectHeight = 2.0; // 保存当前物体高度(米)用于网格刷新
private bool _isPreviewMode = false;
private int _previewInsertIndex = -1; // 保存预览点应该插入的索引位置
@ -141,7 +141,7 @@ namespace NavisworksTransport
LogManager.Info("[路径管理] ✅ PathPointRenderPlugin实例获取成功");
LogManager.Debug($"[路径管理] 渲染插件状态 - 启用: {_renderPlugin.IsEnabled}, 标记数量: {_renderPlugin.MarkerCount}");
// 推送默认的网格大小和车辆参数,确保渲染插件有合理的初始值
// 推送默认的网格大小和物体参数,确保渲染插件有合理的初始值
InitializeRenderPluginDefaults();
}
else
@ -205,7 +205,7 @@ namespace NavisworksTransport
/// <summary>
/// 初始化渲染插件的默认值
/// 从配置文件读取网格大小和车辆参数,确保渲染插件在任何情况下都有正确的可视化效果
/// 从配置文件读取网格大小和物体参数,确保渲染插件在任何情况下都有正确的可视化效果
/// </summary>
private void InitializeRenderPluginDefaults()
{
@ -1011,13 +1011,13 @@ namespace NavisworksTransport
/// </summary>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="vehicleRadius">车辆尺寸(米)</param>
/// <param name="ObjectRadius">物体尺寸(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="gridSize">网格精度(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="ObjectHeight">物体高度(米)</param>
/// <param name="strategy">路径规划策略</param>
/// <returns>规划结果</returns>
public Task<PathRoute> AutoPlanPath(PathPoint startPoint, PathPoint endPoint, double vehicleRadius, double safetyMargin, double gridSize, double vehicleHeight, PathStrategy strategy)
public Task<PathRoute> AutoPlanPath(PathPoint startPoint, PathPoint endPoint, double ObjectRadius, double safetyMargin, double gridSize, double ObjectHeight, PathStrategy strategy)
{
try
{
@ -1029,7 +1029,7 @@ namespace NavisworksTransport
LogManager.Info($"开始自动路径规划: {startPoint.Name} -> {endPoint.Name}");
LogManager.Info($"起点坐标: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})");
LogManager.Info($"终点坐标: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})");
LogManager.Info($"车辆半径: {vehicleRadius}m, 安全间隙: {safetyMargin}m, 车辆高度: {vehicleHeight}m");
LogManager.Info($"物体半径: {ObjectRadius}m, 安全间隙: {safetyMargin}m, 物体高度: {ObjectHeight}m");
RaiseStatusChanged("正在进行自动路径规划...", PathPlanningStatusType.Info);
@ -1060,7 +1060,7 @@ namespace NavisworksTransport
// 获取当前文档
var document = Application.ActiveDocument;
LogManager.Info($"网格生成参数 - 边界: {bounds.Min.X:F2},{bounds.Min.Y:F2} -> {bounds.Max.X:F2},{bounds.Max.Y:F2}");
LogManager.Info($"网格生成参数 - 网格大小: {gridSize}m, 车辆半径: {vehicleRadius}m, 安全边距: {safetyMargin}m");
LogManager.Info($"网格生成参数 - 网格大小: {gridSize}m, 物体半径: {ObjectRadius}m, 安全边距: {safetyMargin}m");
LogManager.Info($"网格生成参数 - 起点: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})");
LogManager.Info($"网格生成参数 - 终点: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})");
@ -1069,11 +1069,11 @@ namespace NavisworksTransport
gridMap = gridMapGenerator.GenerateFromBIM(
bounds,
gridSize,
vehicleRadius,
ObjectRadius,
safetyMargin,
startPoint.Position,
endPoint.Position,
vehicleHeight
ObjectHeight
);
LogManager.Info("✅ 网格地图生成成功");
}
@ -1096,7 +1096,7 @@ namespace NavisworksTransport
if (_showWalkableGrid || _showObstacleGrid || _showUnknownGrid || _showDoorGrid)
{
LogManager.Info("开始网格可视化,显示所有可通行网格单元");
VisualizeGridCells(gridMap, vehicleHeight);
VisualizeGridCells(gridMap, ObjectHeight);
}
// 3. 获取通道高度数据
@ -1132,9 +1132,9 @@ namespace NavisworksTransport
try
{
pathFinder = new AutoPathFinder();
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);
double ObjectHeightInModelUnits = ObjectHeight * UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
LogManager.Info($"使用2.5D模式进行路径查找,物体高度: {ObjectHeight}m ({ObjectHeightInModelUnits:F2}模型单位),策略: {strategy}");
pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, ObjectHeightInModelUnits, strategy);
LogManager.Info("FindPath方法调用完成");
}
catch (Exception ex)
@ -1170,10 +1170,10 @@ namespace NavisworksTransport
// 保存GridMap和参数到PathRoute以便后续恢复网格可视化
autoRoute.AssociatedGridMap = gridMap;
autoRoute.GridSize = gridSize;
// 将vehicleRadius拆分为长宽暂时使用相同值后续可以传入更详细的参数
autoRoute.MaxVehicleLength = vehicleRadius * 2; // 车辆半径转换为长度
autoRoute.MaxVehicleWidth = vehicleRadius * 2; // 车辆半径转换为宽度
autoRoute.MaxVehicleHeight = vehicleHeight;
// 将ObjectRadius拆分为长宽暂时使用相同值后续可以传入更详细的参数
autoRoute.MaxObjectLength = ObjectRadius * 2; // 物体半径转换为长度
autoRoute.MaxObjectWidth = ObjectRadius * 2; // 物体半径转换为宽度
autoRoute.MaxObjectHeight = ObjectHeight;
autoRoute.SafetyMargin = safetyMargin;
LogManager.Info($"已保存GridMap到路径: {routeName}, 网格大小: {gridSize}米");
@ -4535,7 +4535,7 @@ namespace NavisworksTransport
if (_currentGridMap != null && IsAnyGridVisualizationEnabled)
{
LogManager.Info("[网格可视化] 使用缓存的网格地图重新渲染");
VisualizeGridCells(_currentGridMap, _currentVehicleHeight);
VisualizeGridCells(_currentGridMap, _currentObjectHeight);
}
else if (_currentGridMap == null)
{
@ -4557,8 +4557,8 @@ namespace NavisworksTransport
/// 在每个可通行网格的中心绘制一个绿色小球
/// </summary>
/// <param name="gridMap">要可视化的网格地图</param>
/// <param name="vehicleHeight">车辆高度(米),用于判断层高是否足够</param>
public void VisualizeGridCells(GridMap gridMap, double vehicleHeight = 2.0)
/// <param name="ObjectHeight">物体高度(米),用于判断层高是否足够</param>
public void VisualizeGridCells(GridMap gridMap, double ObjectHeight = 2.0)
{
if (gridMap == null)
{
@ -4568,10 +4568,10 @@ namespace NavisworksTransport
try
{
// 保存当前网格地图和车辆高度用于后续刷新
// 保存当前网格地图和物体高度用于后续刷新
_currentGridMap = gridMap;
_currentVehicleHeight = vehicleHeight;
LogManager.Info($"[网格可视化] 开始可视化网格:{gridMap.Width}x{gridMap.Height}, 车辆高度:{vehicleHeight}m");
_currentObjectHeight = ObjectHeight;
LogManager.Info($"[网格可视化] 开始可视化网格:{gridMap.Width}x{gridMap.Height}, 物体高度:{ObjectHeight}m");
// 获取渲染插件
var renderPlugin = PathPointRenderPlugin.Instance;

View File

@ -570,17 +570,17 @@ namespace NavisworksTransport
/// <summary>
/// 最大车辆长度(米) - 路径适用的车辆长度限制
/// </summary>
public double MaxVehicleLength { get; set; }
public double MaxObjectLength { get; set; }
/// <summary>
/// 最大车辆宽度(米) - 路径适用的车辆宽度限制
/// </summary>
public double MaxVehicleWidth { get; set; }
public double MaxObjectWidth { get; set; }
/// <summary>
/// 最大车辆高度(米) - 路径适用的车辆高度限制
/// </summary>
public double MaxVehicleHeight { get; set; }
public double MaxObjectHeight { get; set; }
/// <summary>
/// 安全间隙(米)
@ -906,9 +906,9 @@ namespace NavisworksTransport
CompletionPercentage = CompletionPercentage,
AssociatedGridMap = AssociatedGridMap, // 共享引用,不深拷贝
GridSize = GridSize,
MaxVehicleLength = MaxVehicleLength,
MaxVehicleWidth = MaxVehicleWidth,
MaxVehicleHeight = MaxVehicleHeight,
MaxObjectLength = MaxObjectLength,
MaxObjectWidth = MaxObjectWidth,
MaxObjectHeight = MaxObjectHeight,
SafetyMargin = SafetyMargin
};
@ -1428,7 +1428,7 @@ namespace NavisworksTransport
// 获取通道属性信息
string channelType = "通道";
string traversable = "是";
string vehicleSize = "标准";
string ObjectSize = "标准";
try
{
@ -1456,7 +1456,7 @@ namespace NavisworksTransport
// 添加子项
item.SubItems.Add(channelType);
item.SubItems.Add(traversable);
item.SubItems.Add(vehicleSize);
item.SubItems.Add(ObjectSize);
item.SubItems.Add(length.ToString("F2"));
item.SubItems.Add(width.ToString("F2"));
item.SubItems.Add(height.ToString("F2"));

View File

@ -25,9 +25,9 @@ namespace NavisworksTransport
RibbonLine,
/// <summary>
/// 车辆通行空间模式 - 矩形通道
/// 物体通行空间模式 - 矩形通道
/// </summary>
VehicleSpace
ObjectSpace
}
/// <summary>
@ -78,9 +78,9 @@ namespace NavisworksTransport
Line,
/// <summary>
/// 车辆通行空间样式(灰色)
/// 物体通行空间样式(灰色)
/// </summary>
VehicleSpace,
ObjectSpace,
/// <summary>
/// 预览点样式(白色)
@ -166,9 +166,9 @@ namespace NavisworksTransport
}
/// <summary>
/// 车辆通行空间标记,用于渲染车辆通道空间
/// 物体通行空间标记,用于渲染物体通道空间
/// </summary>
public class VehicleSpaceMarker
public class ObjectSpaceMarker
{
/// <summary>
/// 通道起点
@ -212,7 +212,7 @@ namespace NavisworksTransport
public override string ToString()
{
return $"VehicleSpaceMarker[{FromIndex}->{ToIndex}, 宽度={Width:F2}m, 高度={Height:F2}m, 透明度={Alpha:F1}]";
return $"ObjectSpaceMarker[{FromIndex}->{ToIndex}, 宽度={Width:F2}m, 高度={Height:F2}m, 透明度={Alpha:F1}]";
}
}
@ -267,12 +267,12 @@ namespace NavisworksTransport
public List<Point3D> SampledPoints { get; set; }
/// <summary>
/// 带状连线高度(仅用于 RibbonLine 和 VehicleSpace 模式)
/// 带状连线高度(仅用于 RibbonLine 和 ObjectSpace 模式)
/// </summary>
public double Height { get; set; }
/// <summary>
/// 带状连线宽度(仅用于 VehicleSpace 模式)
/// 带状连线宽度(仅用于 ObjectSpace 模式)
/// </summary>
public double Width { get; set; }
@ -328,9 +328,9 @@ namespace NavisworksTransport
public List<SquareMarker> TangentMarkers { get; set; }
/// <summary>
/// 车辆通行空间标记集合
/// 物体通行空间标记集合
/// </summary>
public List<VehicleSpaceMarker> VehicleSpaceMarkers { get; set; }
public List<ObjectSpaceMarker> ObjectSpaceMarkers { get; set; }
/// <summary>
/// 是否显示控制点可视化(用户意图)
@ -351,7 +351,7 @@ namespace NavisworksTransport
ControlLineMarkers = new List<LineMarker>();
PathLineMarkers = new List<LineMarker>();
TangentMarkers = new List<SquareMarker>();
VehicleSpaceMarkers = new List<VehicleSpaceMarker>();
ObjectSpaceMarkers = new List<ObjectSpaceMarker>();
LastUpdated = DateTime.Now;
}
@ -388,7 +388,7 @@ namespace NavisworksTransport
// 独立可视化开关(替代单一模式)
private bool _showPathLines = true; // 显示路径线(地面连线/带状线)
private bool _showVehicleSpace = false; // 显示通行空间
private bool _showObjectSpace = false; // 显示通行空间
// 网格点类型配置
private GridPointType _gridPointType = GridPointType.Rectangle;
@ -464,14 +464,14 @@ namespace NavisworksTransport
public static PathPointRenderPlugin Instance => _instance;
/// <summary>
/// 路径可视化模式(向后兼容,根据 ShowPathLines 和 ShowVehicleSpace 计算)
/// 路径可视化模式(向后兼容,根据 ShowPathLines 和 ShowObjectSpace 计算)
/// </summary>
public PathVisualizationMode VisualizationMode
{
get
{
// 根据新的独立开关计算模式
if (_showVehicleSpace) return PathVisualizationMode.VehicleSpace;
if (_showObjectSpace) return PathVisualizationMode.ObjectSpace;
if (_showPathLines) return PathVisualizationMode.RibbonLine;
return PathVisualizationMode.StandardLine;
}
@ -479,7 +479,7 @@ namespace NavisworksTransport
{
_visualizationMode = value;
// 同步到新的独立开关
_showVehicleSpace = (value == PathVisualizationMode.VehicleSpace);
_showObjectSpace = (value == PathVisualizationMode.ObjectSpace);
_showPathLines = (value == PathVisualizationMode.RibbonLine || value == PathVisualizationMode.StandardLine);
// 模式改变时刷新所有普通路径(排除网格)
RefreshNormalPaths();
@ -500,14 +500,14 @@ namespace NavisworksTransport
}
/// <summary>
/// 是否显示通行空间(车辆通行空间)
/// 是否显示通行空间(物体通行空间)
/// </summary>
public bool ShowVehicleSpace
public bool ShowObjectSpace
{
get { return _showVehicleSpace; }
get { return _showObjectSpace; }
set
{
_showVehicleSpace = value;
_showObjectSpace = value;
RefreshNormalPaths();
}
}
@ -654,9 +654,9 @@ namespace NavisworksTransport
}
// 再渲染通行空间(可以在路径线之上)
if (_showVehicleSpace)
if (_showObjectSpace)
{
// VehicleSpace 模式:使用车辆通行空间渲染(高高度长方体)
// ObjectSpace 模式:使用物体通行空间渲染(高高度长方体)
foreach (var pathLineMarker in visualization.PathLineMarkers)
{
RenderRibbonLineMarker(graphics, pathLineMarker);
@ -1290,7 +1290,7 @@ namespace NavisworksTransport
visualization.ControlLineMarkers.Clear();
visualization.PathLineMarkers.Clear();
visualization.TangentMarkers.Clear();
visualization.VehicleSpaceMarkers.Clear();
visualization.ObjectSpaceMarkers.Clear();
var points = visualization.PathRoute.Points;
if (points.Count == 0) return;
@ -1315,7 +1315,7 @@ namespace NavisworksTransport
{
// 空中路径:默认只显示控制点连线
// 但如果启用了通行空间可视化,则构建路径连线以显示通行空间
if (_showVehicleSpace)
if (_showObjectSpace)
{
string aerialSubTypeName = visualization.PathRoute.PathType == NavisworksTransport.PathType.Rail ? "空轨" : "吊装";
BuildPathLines(visualization, sortedPoints);
@ -1406,7 +1406,7 @@ namespace NavisworksTransport
Color lineColor;
double lineOpacity;
if (_showVehicleSpace)
if (_showObjectSpace)
{
// 通行空间模式:根据路径类型和段类型计算通行空间尺寸
// _passage* 和 _object* 字段已经是模型单位(由 SetPassageSpaceParameters 转换)
@ -1430,7 +1430,7 @@ namespace NavisworksTransport
height = _passageNormalToPath;
}
var renderStyle = GetRenderStyle(RenderStyleName.VehicleSpace);
var renderStyle = GetRenderStyle(RenderStyleName.ObjectSpace);
lineColor = renderStyle.Color;
lineOpacity = renderStyle.Alpha;
}
@ -1456,7 +1456,7 @@ namespace NavisworksTransport
// 计算通行空间的垂直偏移(根据路径类型)
double verticalOffset = 0;
if (_showVehicleSpace)
if (_showObjectSpace)
{
if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Ground)
{
@ -1549,7 +1549,7 @@ namespace NavisworksTransport
Point3D adjustedStartPoint;
Point3D adjustedEndPoint;
if (_showVehicleSpace && Math.Abs(verticalOffset) > 0.001)
if (_showObjectSpace && Math.Abs(verticalOffset) > 0.001)
{
// 通行空间模式且有垂直偏移时沿着up向量方向平移
adjustedStartPoint = new Point3D(
@ -1571,9 +1571,9 @@ namespace NavisworksTransport
}
// 🔥 地面/空轨路径水平段:起点和终点向路径方向延伸半个物体尺寸(仅通行空间)
if (_showVehicleSpace)
if (_showObjectSpace)
{
ExtendLineSegmentForVehicleSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
ExtendLineSegmentForObjectSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
}
var lineMarker = new LineMarker
@ -1620,7 +1620,7 @@ namespace NavisworksTransport
Point3D adjustedEndPoint;
List<Point3D> adjustedSampledPoints;
if (_showVehicleSpace && Math.Abs(verticalOffset) > 0.001)
if (_showObjectSpace && Math.Abs(verticalOffset) > 0.001)
{
// 通行空间模式且有垂直偏移时沿着up向量方向平移
adjustedStartPoint = new Point3D(
@ -1657,9 +1657,9 @@ namespace NavisworksTransport
}
// 🔥 圆弧段:起点和终点向路径方向延伸半个物体尺寸(仅通行空间)
if (_showVehicleSpace)
if (_showObjectSpace)
{
ExtendLineSegmentForVehicleSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
ExtendLineSegmentForObjectSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
}
var arcMarker = new LineMarker
@ -1847,7 +1847,7 @@ namespace NavisworksTransport
Point3D adjustedStartPoint;
Point3D adjustedEndPoint;
if (_showVehicleSpace && Math.Abs(verticalOffset) > 0.001)
if (_showObjectSpace && Math.Abs(verticalOffset) > 0.001)
{
// 通行空间模式且有垂直偏移时沿着up向量方向平移
adjustedStartPoint = new Point3D(
@ -1869,7 +1869,7 @@ namespace NavisworksTransport
}
// 🔥 通行空间包裹调整:水平段起点和终点延伸以完全包裹物体(仅通行空间)
if (_showVehicleSpace && !isVerticalSegment)
if (_showObjectSpace && !isVerticalSegment)
{
// 沿路径方向的物体尺寸吊装路径根据是否转折90度选择
double alongPathSize = _passageAlongPath;
@ -1877,7 +1877,7 @@ namespace NavisworksTransport
{
alongPathSize = isTurn90Horizontal ? _passageAcrossPath : _objectLength;
}
ExtendLineSegmentForVehicleSpace(ref adjustedStartPoint, ref adjustedEndPoint, alongPathSize);
ExtendLineSegmentForObjectSpace(ref adjustedStartPoint, ref adjustedEndPoint, alongPathSize);
}
var lineMarker = new LineMarker
@ -1995,21 +1995,21 @@ namespace NavisworksTransport
}
/// <summary>
/// 创建车辆通行空间标记
/// 创建物体通行空间标记
/// </summary>
/// <param name="fromPoint">起点</param>
/// <param name="toPoint">终点</param>
/// <returns>车辆通行空间标记</returns>
private VehicleSpaceMarker CreateVehicleSpaceMarker(PathPoint fromPoint, PathPoint toPoint)
/// <returns>物体通行空间标记</returns>
private ObjectSpaceMarker CreateObjectSpaceMarker(PathPoint fromPoint, PathPoint toPoint)
{
var style = GetRenderStyle(RenderStyleName.VehicleSpace);
return new VehicleSpaceMarker
var style = GetRenderStyle(RenderStyleName.ObjectSpace);
return new ObjectSpaceMarker
{
StartPoint = fromPoint.Position,
EndPoint = toPoint.Position,
Width = _passageAcrossPath, // 直接使用模型单位
Height = _passageNormalToPath, // 直接使用模型单位
Color = style.Color, // 车辆通行空间颜色
Color = style.Color, // 物体通行空间颜色
Alpha = style.Alpha, // 透明度,统一管理
FromIndex = fromPoint.Index,
ToIndex = toPoint.Index
@ -2102,11 +2102,11 @@ namespace NavisworksTransport
}
/// <summary>
/// 设置车辆参数从PathPlanningManager同步
/// 设置物体参数从PathPlanningManager同步
/// </summary>
/// <param name="vehicleLength">车辆长度(米)</param>
/// <param name="vehicleWidth">车辆宽度(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="ObjectLength">物体长度(米)</param>
/// <param name="ObjectWidth">物体宽度(米)</param>
/// <param name="ObjectHeight">物体高度(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="passageNormalToPathVertical">垂直段的高度(物体长度 + 2×安全间隙模型单位</param>
/// <param name="passageNormalToPathHorizontal">水平段的高度(物体高度 + 2×安全间隙模型单位</param>
@ -2125,7 +2125,7 @@ namespace NavisworksTransport
_objectLength = passageNormalToPathVerticalInMeters * factor;
_objectHeight = passageNormalToPathHorizontalInMeters * factor;
// 参数改变时刷新所有普通路径因为RibbonLine模式也使用车辆宽度)
// 参数改变时刷新所有普通路径因为RibbonLine模式也使用物体宽度)
RefreshNormalPaths();
}
@ -2152,7 +2152,7 @@ namespace NavisworksTransport
case RenderStyleName.Line:
return new RenderStyle(Color.FromByteRGB(255, 152, 0), 0.8); // Material Orange连线20%透明
case RenderStyleName.VehicleSpace:
case RenderStyleName.ObjectSpace:
{
// 从配置读取通行空间透明度默认为0.440%不透明60%透明)
double opacity = ConfigManager.Instance.Current.Visualization.PassageSpaceOpacity;
@ -2759,7 +2759,7 @@ namespace NavisworksTransport
lineMarker.SampledPoints != null &&
lineMarker.SampledPoints.Count >= 2)
{
// 圆弧段:在每个采样点渲染完整的车辆长方体
// 圆弧段:在每个采样点渲染完整的物体长方体
RenderArcSegmentCuboids(graphics, lineMarker, width);
}
else
@ -2779,8 +2779,8 @@ namespace NavisworksTransport
}
/// <summary>
/// 渲染圆弧段的车辆长方体
/// 在每个采样点处渲染一个完整的车辆长方体,中心在采样点,向前后各延伸 L/2
/// 渲染圆弧段的物体长方体
/// 在每个采样点处渲染一个完整的物体长方体,中心在采样点,向前后各延伸 L/2
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="lineMarker">连线标记(必须是圆弧段)</param>
@ -2790,7 +2790,7 @@ namespace NavisworksTransport
// 设置颜色和透明度
graphics.Color(lineMarker.Color, lineMarker.Opacity);
// 在每个采样点处渲染完整的车辆长方体
// 在每个采样点处渲染完整的物体长方体
for (int i = 0; i < lineMarker.SampledPoints.Count; i++)
{
// 计算当前采样点的切线方向
@ -2992,10 +2992,10 @@ namespace NavisworksTransport
private void RenderLineMarker(Graphics graphics, LineMarker lineMarker)
{
// 🔥 路径线渲染逻辑:保持原有行为
// 注意ShowVehicleSpace 的通行空间使用 RenderRibbonLineMarker 单独渲染
if (_visualizationMode == PathVisualizationMode.VehicleSpace || _visualizationMode == PathVisualizationMode.RibbonLine)
// 注意ShowObjectSpace 的通行空间使用 RenderRibbonLineMarker 单独渲染
if (_visualizationMode == PathVisualizationMode.ObjectSpace || _visualizationMode == PathVisualizationMode.RibbonLine)
{
// VehicleSpace 或 RibbonLine 模式:使用长方体渲染
// ObjectSpace 或 RibbonLine 模式:使用长方体渲染
RenderRibbonLineMarker(graphics, lineMarker);
}
else
@ -3039,14 +3039,14 @@ namespace NavisworksTransport
}
/// <summary>
/// 延伸线段起点和终点以完全包裹物体(VehicleSpace模式
/// 延伸线段起点和终点以完全包裹物体(ObjectSpace模式
/// </summary>
/// <param name="startPoint">起点(会被修改)</param>
/// <param name="endPoint">终点(会被修改)</param>
private void ExtendLineSegmentForVehicleSpace(ref Point3D startPoint, ref Point3D endPoint, double alongPathSize)
private void ExtendLineSegmentForObjectSpace(ref Point3D startPoint, ref Point3D endPoint, double alongPathSize)
{
// 注意:调用此方法前应该检查 _showVehicleSpace
if (!_showVehicleSpace)
// 注意:调用此方法前应该检查 _showObjectSpace
if (!_showObjectSpace)
return;
var segDx = endPoint.X - startPoint.X;

View File

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Linq;
using Autodesk.Navisworks.Api;
using g4;
using NavisworksTransport.Utils;
// 使用命名空间引用以避免类名冲突
using ClashIntegration = NavisworksTransport.ClashDetectiveIntegration;
@ -152,6 +153,7 @@ namespace NavisworksTransport.Core.Spatial
/// <summary>
/// 范围查询:查找指定位置附近的对象
/// 支持剖面盒精筛(如果剖面盒已启用)
/// </summary>
/// <param name="position">查询中心位置Navisworks Point3D</param>
/// <param name="searchRadiusInModelUnits">查询半径(模型单位)</param>
@ -175,7 +177,7 @@ namespace NavisworksTransport.Core.Spatial
// 转换为 geometry4Sharp 的 Vector3d
var centerVec = new Vector3d(position.X, position.Y, position.Z);
// 使用空间哈希网格进行范围查询(带精确距离检查
// 步骤1使用空间哈希网格进行范围查询快速粗筛
var nearbyObjects = _globalSpatialIndex.FindInRadiusExact(
centerVec,
searchRadiusInModelUnits,
@ -197,7 +199,23 @@ namespace NavisworksTransport.Core.Spatial
}
);
// 排除指定对象
// 步骤2剖面盒精筛如果启用
int beforeClipFilter = nearbyObjects.Count;
if (SectionClipHelper.IsClipBoxEnabled)
{
nearbyObjects = nearbyObjects
.Where(item => SectionClipHelper.IntersectsClipBox(item.BoundingBox()))
.ToList();
int filteredCount = beforeClipFilter - nearbyObjects.Count;
LogManager.Debug($"[空间查询] 剖面盒过滤: 粗筛{beforeClipFilter}个 → 精筛{nearbyObjects.Count}个,过滤{filteredCount}个({(beforeClipFilter > 0 ? filteredCount*100.0/beforeClipFilter : 0):F1}%)");
}
else
{
LogManager.Debug($"[空间查询] 剖面盒未启用,使用全部{beforeClipFilter}个对象");
}
// 步骤3排除指定对象
if (excludeObject != null)
{
nearbyObjects = nearbyObjects.Where(obj => !obj.Equals(excludeObject)).ToList();
@ -240,6 +258,7 @@ namespace NavisworksTransport.Core.Spatial
/// <summary>
/// AABB范围查询查找与指定AABB相交的对象
/// 比球形查询更精确,避免球体扩大的无效范围
/// 支持剖面盒精筛(如果剖面盒已启用)
/// </summary>
/// <param name="bounds">查询AABBNavisworks BoundingBox3D</param>
/// <param name="excludeObject">要排除的对象</param>
@ -256,10 +275,40 @@ namespace NavisworksTransport.Core.Spatial
try
{
// 直接使用AABB查询更精确
// 步骤1使用空间索引进行AABB查询快速粗筛
var nearbyObjects = _globalSpatialIndex.FindInAABB(bounds);
// 排除指定对象
// 步骤2剖面盒精筛如果启用
int beforeClipFilter = nearbyObjects.Count;
if (SectionClipHelper.IsClipBoxEnabled)
{
int intersectCount = 0;
int outsideCount = 0;
nearbyObjects = nearbyObjects
.Where(item => {
bool intersects = SectionClipHelper.IntersectsClipBox(item.BoundingBox());
if (intersects) intersectCount++;
else outsideCount++;
return intersects;
})
.ToList();
int filteredCount = beforeClipFilter - nearbyObjects.Count;
LogManager.Debug($"[AABB查询] 剖面盒过滤: 粗筛{beforeClipFilter}个 → 精筛{nearbyObjects.Count}个,过滤{filteredCount}个({(beforeClipFilter > 0 ? filteredCount*100.0/beforeClipFilter : 0):F1}%)");
// 如果全部过滤,输出警告并采样被过滤的对象
if (nearbyObjects.Count == 0 && beforeClipFilter > 0)
{
SectionClipHelper.TryGetCurrentClipBox(out var clipBox);
LogManager.Warning($"[AABB查询] 剖面盒过滤后无对象!剖面盒范围: X[{clipBox.Min.X:F2},{clipBox.Max.X:F2}], Y[{clipBox.Min.Y:F2},{clipBox.Max.Y:F2}], Z[{clipBox.Min.Z:F2},{clipBox.Max.Z:F2}]");
}
}
else
{
LogManager.Debug($"[AABB查询] 剖面盒未启用,使用全部{beforeClipFilter}个对象");
}
// 步骤3排除指定对象
if (excludeObject != null)
{
nearbyObjects = nearbyObjects.Where(obj => !obj.Equals(excludeObject)).ToList();

View File

@ -7,18 +7,18 @@ using Autodesk.Navisworks.Api;
namespace NavisworksTransport.Core
{
/// <summary>
/// 虚拟车辆管理器 - 负责创建和管理虚拟车辆几何体
/// 通过追加预制的单位立方体NWC文件并缩放来创建指定尺寸的虚拟车辆
/// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体
/// 通过追加预制的单位立方体NWC文件并缩放来创建指定尺寸的虚拟物体
/// </summary>
public class VirtualVehicleManager
public class VirtualObjectManager
{
private static VirtualVehicleManager _instance;
private static VirtualObjectManager _instance;
private static readonly object _lock = new object();
/// <summary>
/// 单例实例
/// </summary>
public static VirtualVehicleManager Instance
public static VirtualObjectManager Instance
{
get
{
@ -28,7 +28,7 @@ namespace NavisworksTransport.Core
{
if (_instance == null)
{
_instance = new VirtualVehicleManager();
_instance = new VirtualObjectManager();
}
}
}
@ -36,167 +36,167 @@ namespace NavisworksTransport.Core
}
}
private ModelItem _virtualVehicleModelItem;
private Model _virtualVehicleModel;
private bool _isVirtualVehicleActive;
private ModelItem _virtualObjectModelItem;
private Model _virtualObjectModel;
private bool _isVirtualObjectActive;
// 🔥 新增:标记是否正在更新虚拟车辆,用于避免触发缓存重建
private bool _isUpdatingVirtualVehicle = false;
// 🔥 新增:标记是否正在更新虚拟物体,用于避免触发缓存重建
private bool _isUpdatingVirtualObject = false;
/// <summary>
/// 是否正在更新虚拟车辆(用于外部检测是否应该跳过缓存重建)
/// 是否正在更新虚拟物体(用于外部检测是否应该跳过缓存重建)
/// </summary>
public bool IsUpdatingVirtualVehicle => _isUpdatingVirtualVehicle;
public bool IsUpdatingVirtualObject => _isUpdatingVirtualObject;
// 🔥 记住虚拟车辆的尺寸变换参数(避免动态计算)
// 🔥 记住虚拟物体的尺寸变换参数(避免动态计算)
private double _currentLengthMeters = 0;
private double _currentWidthMeters = 0;
private double _currentHeightMeters = 0;
/// <summary>
/// 当前虚拟车辆ModelItem
/// 当前虚拟物体ModelItem
/// </summary>
public ModelItem CurrentVirtualVehicle => _virtualVehicleModelItem;
public ModelItem CurrentVirtualObject => _virtualObjectModelItem;
/// <summary>
/// 虚拟车辆是否激活
/// 虚拟物体是否激活
/// </summary>
public bool IsVirtualVehicleActive => _isVirtualVehicleActive;
public bool IsVirtualObjectActive => _isVirtualObjectActive;
private VirtualVehicleManager()
private VirtualObjectManager()
{
_isVirtualVehicleActive = false;
_isVirtualObjectActive = false;
}
/// <summary>
/// 显示虚拟车辆
/// 显示虚拟物体
/// </summary>
/// <param name="lengthMeters">长度(米)</param>
/// <param name="widthMeters">宽度(米)</param>
/// <param name="heightMeters">高度(米)</param>
public void ShowVirtualVehicle(double lengthMeters, double widthMeters, double heightMeters)
public void ShowVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{
// 🔥 设置标志,防止文档变化事件触发缓存重建
_isUpdatingVirtualVehicle = true;
_isUpdatingVirtualObject = true;
try
{
var doc = Application.ActiveDocument;
// 检查虚拟车辆模型是否已存在
if (_virtualVehicleModel != null)
// 检查虚拟物体模型是否已存在
if (_virtualObjectModel != null)
{
int currentIndex = doc.Models.IndexOf(_virtualVehicleModel);
int currentIndex = doc.Models.IndexOf(_virtualObjectModel);
if (currentIndex >= 0)
{
// 模型存在,显示它并更新尺寸
LogManager.Info($"虚拟车辆模型已存在(索引: {currentIndex}),显示并更新尺寸");
LogManager.Info($"虚拟物体模型已存在(索引: {currentIndex}),显示并更新尺寸");
var modelItems = new ModelItemCollection { _virtualVehicleModelItem };
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
doc.Models.SetHidden(modelItems, false);
// 获取实际的几何体项
_virtualVehicleModelItem = _virtualVehicleModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualVehicleModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualVehicleModelItem))
_virtualObjectModelItem = _virtualObjectModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
{
_virtualVehicleModelItem = geometryItem;
_virtualObjectModelItem = geometryItem;
}
// 清除覆盖变换(之前动画移动和旋转的累积)
modelItems.Clear();
modelItems.Add(_virtualVehicleModelItem);
modelItems.Add(_virtualObjectModelItem);
doc.Models.ResetPermanentTransform(modelItems);
// 重置变换并缩放到目标尺寸
ResetVirtualVehicleTransform();
ScaleVirtualVehicle(lengthMeters, widthMeters, heightMeters);
ResetVirtualObjectTransform();
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
_isVirtualVehicleActive = true;
LogManager.Info($"虚拟车辆更新成功");
_isVirtualObjectActive = true;
LogManager.Info($"虚拟物体更新成功");
return;
}
else
{
LogManager.Warning($"已保存的虚拟车辆模型不在文档中,需要重新创建");
LogManager.Warning($"已保存的虚拟物体模型不在文档中,需要重新创建");
}
}
// 模型不存在,创建新的
CreateVirtualVehicleInternal(doc, lengthMeters, widthMeters, heightMeters);
CreateVirtualObjectInternal(doc, lengthMeters, widthMeters, heightMeters);
}
catch (Exception ex)
{
LogManager.Error($"显示虚拟车辆失败: {ex.Message}");
LogManager.Error($"显示虚拟物体失败: {ex.Message}");
}
finally
{
// 🔥 清除标志
_isUpdatingVirtualVehicle = false;
_isUpdatingVirtualObject = false;
}
}
/// <summary>
/// 隐藏虚拟车辆
/// 隐藏虚拟物体
/// </summary>
public void HideVirtualVehicle()
public void HideVirtualObject()
{
try
{
if (_virtualVehicleModelItem != null)
if (_virtualObjectModelItem != null)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { _virtualVehicleModelItem };
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
doc.Models.SetHidden(modelItems, true);
LogManager.Info($"已隐藏虚拟车辆模型");
LogManager.Info($"已隐藏虚拟物体模型");
}
// 隐藏时设置为非激活状态,但保留模型引用
_isVirtualVehicleActive = false;
_isVirtualObjectActive = false;
}
catch (Exception ex)
{
LogManager.Error($"隐藏虚拟车辆失败: {ex.Message}");
_isVirtualVehicleActive = false;
LogManager.Error($"隐藏虚拟物体失败: {ex.Message}");
_isVirtualObjectActive = false;
}
}
/// <summary>
/// 创建虚拟车辆(内部方法)
/// 创建虚拟物体(内部方法)
/// </summary>
private void CreateVirtualVehicleInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
private void CreateVirtualObjectInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
{
LogManager.Info($"=== 创建虚拟车辆 ===");
LogManager.Info($"=== 创建虚拟物体 ===");
LogManager.Info($"目标尺寸: {lengthMeters:F2}m × {widthMeters:F2}m × {heightMeters:F2}m");
// 加载新的虚拟车辆模型
LoadNewVirtualVehicleModel(doc);
// 加载新的虚拟物体模型
LoadNewVirtualObjectModel(doc);
// 获取实际的几何体项
_virtualVehicleModelItem = _virtualVehicleModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualVehicleModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualVehicleModelItem))
_virtualObjectModelItem = _virtualObjectModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
{
_virtualVehicleModelItem = geometryItem;
_virtualObjectModelItem = geometryItem;
}
// 重置变换并缩放到目标尺寸
ResetVirtualVehicleTransform();
ScaleVirtualVehicle(lengthMeters, widthMeters, heightMeters);
ResetVirtualObjectTransform();
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
// 设置状态
_isVirtualVehicleActive = true;
_isVirtualObjectActive = true;
LogManager.Info($"虚拟车辆创建成功");
LogManager.Info($"虚拟物体创建成功");
}
/// <summary>
/// 加载新的虚拟车辆模型
/// 加载新的虚拟物体模型
/// </summary>
private void LoadNewVirtualVehicleModel(Document doc)
private void LoadNewVirtualObjectModel(Document doc)
{
LogManager.Info($"需要加载新的虚拟车辆模型");
LogManager.Info($"需要加载新的虚拟物体模型");
// 1. 获取单位立方体文件路径
var unitCubePath = GetUnitCubeFilePath();
@ -227,19 +227,19 @@ namespace NavisworksTransport.Core
}
// 保存模型引用(最后一个模型就是刚追加的)
_virtualVehicleModel = doc.Models.Last();
_virtualObjectModel = doc.Models.Last();
LogManager.Info($"虚拟车辆模型: {_virtualVehicleModel.FileName}");
LogManager.Info($"虚拟车辆根项: {_virtualVehicleModel.RootItem.DisplayName}");
LogManager.Info($"虚拟物体模型: {_virtualObjectModel.FileName}");
LogManager.Info($"虚拟物体根项: {_virtualObjectModel.RootItem.DisplayName}");
}
/// <summary>
/// <summary>
/// 重置虚拟车辆变换为单位矩阵(公开方法,供批处理使用)
/// 重置虚拟物体变换为单位矩阵(公开方法,供批处理使用)
/// </summary>
public void ResetVirtualVehicleTransform()
public void ResetVirtualObjectTransform()
{
if (_virtualVehicleModel == null) return;
if (_virtualObjectModel == null) return;
var doc = Application.ActiveDocument;
@ -248,19 +248,19 @@ namespace NavisworksTransport.Core
Transform3DComponents identityComponents = identityTransform.Factor();
// 应用单位变换
Units currentUnits = _virtualVehicleModel.Units;
doc.Models.SetModelUnitsAndTransform(_virtualVehicleModel, currentUnits, identityTransform, false);
Units currentUnits = _virtualObjectModel.Units;
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, identityTransform, false);
LogManager.Info("已重置虚拟车辆变换");
LogManager.Info("已重置虚拟物体变换");
}
/// <summary>
/// 缩放虚拟车辆到目标尺寸
/// 缩放虚拟物体到目标尺寸
/// 使用DocumentModels.SetModelUnitsAndTransform方法进行模型级缩放
/// </summary>
private void ScaleVirtualVehicle(double lengthMeters, double widthMeters, double heightMeters)
private void ScaleVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{
if (_virtualVehicleModel == null || _virtualVehicleModelItem == null) return;
if (_virtualObjectModel == null || _virtualObjectModelItem == null) return;
var doc = Application.ActiveDocument;
@ -272,21 +272,21 @@ namespace NavisworksTransport.Core
// 获取单位转换因子(米到模型单位)
double metersToUnits = Utils.UnitsConverter.GetMetersToUnitsConversionFactor(doc.Units);
// 🔥 对于虚拟车辆,直接应用缩放比例(不动态读取当前尺寸)
// 原因:虚拟车辆可能被旋转,导致包围盒尺寸不准确
// 🔥 对于虚拟物体,直接应用缩放比例(不动态读取当前尺寸)
// 原因:虚拟物体可能被旋转,导致包围盒尺寸不准确
// 单位立方体是 0.01m × 0.01m × 0.01m
// X方向 = 车辆长度前进方向Y方向 = 车辆宽度侧面Z方向 = 车辆高度
// X方向 = 物体长度前进方向Y方向 = 物体宽度侧面Z方向 = 物体高度
double baseSizeMeters = 0.01;
double scaleX = lengthMeters / baseSizeMeters; // X方向 = 车辆长度(前进方向)
double scaleY = widthMeters / baseSizeMeters; // Y方向 = 车辆宽度(侧面)
double scaleZ = heightMeters / baseSizeMeters; // Z方向 = 车辆高度
double scaleX = lengthMeters / baseSizeMeters; // X方向 = 物体长度(前进方向)
double scaleY = widthMeters / baseSizeMeters; // Y方向 = 物体宽度(侧面)
double scaleZ = heightMeters / baseSizeMeters; // Z方向 = 物体高度
LogManager.Info($"目标尺寸: 长度={lengthMeters:F2}m, 宽度={widthMeters:F2}m, 高度={heightMeters:F2}m");
LogManager.Info($"缩放比例: X(长度)={scaleX:F2}, Y(宽度)={scaleY:F2}, Z(高度)={scaleZ:F2}");
// 使用Transform3DComponents进行缩放
// 获取当前模型的变换并分解
Transform3D currentTransform = _virtualVehicleModel.Transform;
Transform3D currentTransform = _virtualObjectModel.Transform;
Transform3DComponents transformComponents = currentTransform.Factor();
// 获取当前值
@ -304,13 +304,13 @@ namespace NavisworksTransport.Core
Transform3D newTransform = transformComponents.Combine();
// 获取当前模型单位
Units currentUnits = _virtualVehicleModel.Units;
Units currentUnits = _virtualObjectModel.Units;
// 应用新的变换
doc.Models.SetModelUnitsAndTransform(_virtualVehicleModel, currentUnits, newTransform, false);
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, newTransform, false);
// 验证缩放结果
var newBoundingBox = _virtualVehicleModelItem.BoundingBox();
var newBoundingBox = _virtualObjectModelItem.BoundingBox();
double newLength = newBoundingBox.Max.X - newBoundingBox.Min.X;
double newWidth = newBoundingBox.Max.Y - newBoundingBox.Min.Y;
double newHeight = newBoundingBox.Max.Z - newBoundingBox.Min.Z;
@ -323,18 +323,18 @@ namespace NavisworksTransport.Core
}
/// <summary>
/// 移除虚拟车辆(使用 TryRemoveFile 真正删除模型)
/// 移除虚拟物体(使用 TryRemoveFile 真正删除模型)
/// </summary>
public void RemoveVirtualVehicle()
public void RemoveVirtualObject()
{
try
{
var doc = Application.ActiveDocument;
if (_virtualVehicleModel != null)
if (_virtualObjectModel != null)
{
// 动态查找模型索引
int currentIndex = doc.Models.IndexOf(_virtualVehicleModel);
int currentIndex = doc.Models.IndexOf(_virtualObjectModel);
if (currentIndex >= 0)
{
@ -343,30 +343,30 @@ namespace NavisworksTransport.Core
if (removed)
{
LogManager.Info($"已删除虚拟车辆模型(索引: {currentIndex}");
LogManager.Info($"已删除虚拟物体模型(索引: {currentIndex}");
}
else
{
LogManager.Warning($"删除虚拟车辆模型失败(索引: {currentIndex}");
LogManager.Warning($"删除虚拟物体模型失败(索引: {currentIndex}");
}
}
else
{
LogManager.Warning($"虚拟车辆模型不在文档中,无法删除");
LogManager.Warning($"虚拟物体模型不在文档中,无法删除");
}
}
// 清理引用
_virtualVehicleModel = null;
_virtualVehicleModelItem = null;
_isVirtualVehicleActive = false;
_virtualObjectModel = null;
_virtualObjectModelItem = null;
_isVirtualObjectActive = false;
}
catch (Exception ex)
{
LogManager.Error($"删除虚拟车辆失败: {ex.Message}");
_virtualVehicleModel = null;
_virtualVehicleModelItem = null;
_isVirtualVehicleActive = false;
LogManager.Error($"删除虚拟物体失败: {ex.Message}");
_virtualObjectModel = null;
_virtualObjectModelItem = null;
_isVirtualObjectActive = false;
}
}
@ -431,7 +431,7 @@ namespace NavisworksTransport.Core
/// </summary>
public void Cleanup()
{
RemoveVirtualVehicle();
RemoveVirtualObject();
}
}
}

View File

@ -123,14 +123,14 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 真3D路径规划使用通道覆盖数据、高度约束和路径策略
/// </summary>
public PathFindingResult FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, PathStrategy strategy = PathStrategy.Shortest)
public PathFindingResult FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, PathStrategy strategy = PathStrategy.Shortest)
{
try
{
LogManager.Info("[3D路径规划] 开始执行");
// 构建3D图
var graph3D = BuildGraphWithStrategy(gridMap, channelCoverage, vehicleHeight, start, end, strategy);
var graph3D = BuildGraphWithStrategy(gridMap, channelCoverage, objectHeight, start, end, strategy);
// 执行A*搜索
var pathResult = ExecuteAStarOnGraph(start, end, gridMap, graph3D);
@ -159,7 +159,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 构建3D图每个(x,y,layerIndex)作为独立节点)
/// </summary>
private Graph3DResult BuildGraph3D(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos, SpeedCalculator speedCalculator = null)
private Graph3DResult BuildGraph3D(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos, SpeedCalculator speedCalculator = null)
{
LogManager.Info("[3D图构建] 开始构建真3D图");
@ -200,7 +200,7 @@ namespace NavisworksTransport.PathPlanning
continue;
}
if (layer.PassableHeight.GetSpan() < vehicleHeight)
if (layer.PassableHeight.GetSpan() < objectHeight)
continue;
// 使用2D位置用于A*启发式)
@ -239,7 +239,7 @@ namespace NavisworksTransport.PathPlanning
for (int li = 0; li < cell.HeightLayers.Count; li++)
{
var currentLayer = cell.HeightLayers[li];
if (currentLayer.PassableHeight.GetSpan() < vehicleHeight)
if (currentLayer.PassableHeight.GetSpan() < objectHeight)
continue;
if (!nodes.TryGetValue((x, y, li), out var currentNode))
@ -263,7 +263,7 @@ namespace NavisworksTransport.PathPlanning
for (int nli = 0; nli < neighborCell.HeightLayers.Count; nli++)
{
var neighborLayer = neighborCell.HeightLayers[nli];
if (neighborLayer.PassableHeight.GetSpan() < vehicleHeight)
if (neighborLayer.PassableHeight.GetSpan() < objectHeight)
continue;
double heightDiff = Math.Abs(neighborLayer.Z - currentZ);
@ -331,8 +331,8 @@ namespace NavisworksTransport.PathPlanning
layer1.Type != CategoryAttributeManager.LogisticsElementType.)
continue;
if (layer1.PassableHeight.GetSpan() < vehicleHeight ||
layer2.PassableHeight.GetSpan() < vehicleHeight)
if (layer1.PassableHeight.GetSpan() < objectHeight ||
layer2.PassableHeight.GetSpan() < objectHeight)
continue;
double heightDiff = Math.Abs(layer2.Z - layer1.Z);
@ -390,7 +390,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 根据策略构建图
/// </summary>
private Graph3DResult BuildGraphWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
private Graph3DResult BuildGraphWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
{
SpeedCalculator speedCalc = null;
@ -415,7 +415,7 @@ namespace NavisworksTransport.PathPlanning
var currentPos = new GridPoint2D(cx, cy);
// 计算沿该方向能够直走的距离
int straightDistance = CalculateStraightDistance(currentPos, direction, gridMap, vehicleHeight);
int straightDistance = CalculateStraightDistance(currentPos, direction, gridMap, objectHeight);
// 直走距离越长,速度加成越高(最多+30 km/h
float bonusSpeed = Math.Min(straightDistance * 0.5f, 30.0f);
@ -458,7 +458,7 @@ namespace NavisworksTransport.PathPlanning
break;
}
return BuildGraph3D(gridMap, channelCoverage, vehicleHeight, startPos, endPos, speedCalc);
return BuildGraph3D(gridMap, channelCoverage, objectHeight, startPos, endPos, speedCalc);
}
/// <summary>
@ -723,7 +723,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 2.5D路径规划(使用通道覆盖数据、高度约束和路径策略)[已废弃,保留作为备份]
/// </summary>
private PathFindingResult FindPath_Legacy_2_5D(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, PathStrategy strategy = PathStrategy.Shortest)
private PathFindingResult FindPath_Legacy_2_5D(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, PathStrategy strategy = PathStrategy.Shortest)
{
try
{
@ -735,19 +735,19 @@ namespace NavisworksTransport.PathPlanning
// 1. 检查起点和终点的高度约束
var startGridPos = gridMap.WorldToGrid(start);
if (!IsPointPassableAtHeight(startGridPos, start, gridMap, vehicleHeight))
if (!IsPointPassableAtHeight(startGridPos, start, gridMap, objectHeight))
{
LogManager.Warning("[2.5D路径规划] 起点位置不满足高度约束");
}
var endGridPos = gridMap.WorldToGrid(end);
if (!IsPointPassableAtHeight(endGridPos, end, gridMap, vehicleHeight))
if (!IsPointPassableAtHeight(endGridPos, end, gridMap, objectHeight))
{
LogManager.Warning("[2.5D路径规划] 终点位置不满足高度约束");
}
// 2. 根据策略选择合适的网格转换方法返回A*网格和激活层字典)
var (astarGrid, activeLayerZ) = ConvertToAStarGridWithStrategy(gridMap, channelCoverage, vehicleHeight, start, end, strategy);
var (astarGrid, activeLayerZ) = ConvertToAStarGridWithStrategy(gridMap, channelCoverage, objectHeight, start, end, strategy);
// 3. 执行A*算法
var pathResult = ExecuteAStarAlgorithm(start, end, gridMap, astarGrid);
@ -794,13 +794,13 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="channelCoverage">通道覆盖数据</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <returns>A*算法网格</returns>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWith2_5D(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWith2_5D(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos)
{
try
{
LogManager.Info($"[A*转换-2.5D] 开始转换网格格式: {gridMap.Width}x{gridMap.Height}车辆高度: {vehicleHeight}m");
LogManager.Info($"[A*转换-2.5D] 开始转换网格格式: {gridMap.Width}x{gridMap.Height}运动物体高度: {objectHeight}m");
// 初始化激活层字典
var activeLayerZ = new Dictionary<GridPoint2D, double>();
@ -871,8 +871,8 @@ namespace NavisworksTransport.PathPlanning
totalNonWalkableCells++;
}
// 检查是否有满足车辆高度的层
if (!HasCompatibleLayer(cell, vehicleHeight))
// 检查是否有满足运动物体高度的层
if (!HasCompatibleLayer(cell, objectHeight))
{
if (cell.HasAnyWalkableLayer())
{
@ -882,7 +882,7 @@ namespace NavisworksTransport.PathPlanning
var layerInfo = cell.HeightLayers != null && cell.HeightLayers.Count > 0
? $"有{cell.HeightLayers.Count}个高度层"
: "无高度层";
LogManager.Info($"[A*高度约束] 单元格({x},{y})被高度约束排除:车辆高度{vehicleHeight:F2}模型单位,{layerInfo}");
LogManager.Info($"[A*高度约束] 单元格({x},{y})被高度约束排除:运动物体高度{objectHeight:F2}模型单位,{layerInfo}");
}
else if (heightConstrainedCells == 11)
{
@ -893,7 +893,7 @@ namespace NavisworksTransport.PathPlanning
}
// 为当前单元格选择最优高度层
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, vehicleHeight);
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, objectHeight);
if (!currentZ.HasValue)
continue;
@ -908,7 +908,7 @@ namespace NavisworksTransport.PathPlanning
if (x + 1 < gridMap.Width)
{
var rightCell = gridMap.Cells[x + 1, y];
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, vehicleHeight);
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, objectHeight);
if (rightZ.HasValue)
{
var rightPos = new GridPosition(x + 1, y);
@ -935,7 +935,7 @@ namespace NavisworksTransport.PathPlanning
if (y + 1 < gridMap.Height)
{
var bottomCell = gridMap.Cells[x, y + 1];
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, vehicleHeight);
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, objectHeight);
if (bottomZ.HasValue)
{
var bottomPos = new GridPosition(x, y + 1);
@ -954,7 +954,7 @@ namespace NavisworksTransport.PathPlanning
if (x - 1 >= 0)
{
var leftCell = gridMap.Cells[x - 1, y];
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, vehicleHeight);
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, objectHeight);
if (leftZ.HasValue)
{
var leftPos = new GridPosition(x - 1, y);
@ -973,7 +973,7 @@ namespace NavisworksTransport.PathPlanning
if (y - 1 >= 0)
{
var topCell = gridMap.Cells[x, y - 1];
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, vehicleHeight);
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, objectHeight);
if (topZ.HasValue)
{
var topPos = new GridPosition(x, y - 1);
@ -1013,30 +1013,30 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="channelCoverage">通道覆盖数据</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <param name="startPos">起点坐标</param>
/// <param name="endPos">终点坐标</param>
/// <param name="strategy">路径规划策略</param>
/// <returns>A*网格和激活层字典的元组</returns>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
{
switch (strategy)
{
case PathStrategy.Shortest:
LogManager.Info($"[策略路由] 使用最短路径策略");
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, objectHeight, startPos, endPos);
case PathStrategy.Straightest:
LogManager.Info($"[策略路由] 使用直线优先策略");
return ConvertToAStarGridStraightest(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridStraightest(gridMap, channelCoverage, objectHeight, startPos, endPos);
case PathStrategy.SafestCenter:
LogManager.Info($"[策略路由] 使用安全优先策略");
return ConvertToAStarGridSafestCenter(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridSafestCenter(gridMap, channelCoverage, objectHeight, startPos, endPos);
default:
LogManager.Warning($"[策略路由] 未知策略 {strategy},使用默认最短路径");
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, objectHeight, startPos, endPos);
}
}
@ -1046,11 +1046,11 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="channelCoverage">通道覆盖数据</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <param name="startPos">起点坐标</param>
/// <param name="endPos">终点坐标</param>
/// <returns>A*网格</returns>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridStraightest(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridStraightest(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos)
{
try
{
@ -1097,7 +1097,7 @@ namespace NavisworksTransport.PathPlanning
var cell = gridMap.Cells[x, y];
// 检查是否有兼容的高度层
if (!HasCompatibleLayer(cell, vehicleHeight))
if (!HasCompatibleLayer(cell, objectHeight))
{
if (cell.HasAnyWalkableLayer())
{
@ -1107,7 +1107,7 @@ namespace NavisworksTransport.PathPlanning
}
// 选择当前网格的最佳高度层
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, vehicleHeight);
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, objectHeight);
if (!currentZ.HasValue)
{
continue;
@ -1126,10 +1126,10 @@ namespace NavisworksTransport.PathPlanning
var rightCell = gridMap.Cells[x + 1, y];
// 检查右侧网格是否有兼容的高度层
if (HasCompatibleLayer(rightCell, vehicleHeight))
if (HasCompatibleLayer(rightCell, objectHeight))
{
// 选择右侧网格的最佳高度层
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, vehicleHeight);
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, objectHeight);
if (rightZ.HasValue)
{
var rightPos = new GridPosition(x + 1, y);
@ -1139,7 +1139,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[rightGridPos] = rightZ.Value;
// 计算沿右方向的直线距离
var rightDistance = CalculateStraightDistance(gridPos, new GridPoint2D(1, 0), gridMap, vehicleHeight);
var rightDistance = CalculateStraightDistance(gridPos, new GridPoint2D(1, 0), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1165,10 +1165,10 @@ namespace NavisworksTransport.PathPlanning
var bottomCell = gridMap.Cells[x, y + 1];
// 检查下方网格是否有兼容的高度层
if (HasCompatibleLayer(bottomCell, vehicleHeight))
if (HasCompatibleLayer(bottomCell, objectHeight))
{
// 选择下方网格的最佳高度层
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, vehicleHeight);
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, objectHeight);
if (bottomZ.HasValue)
{
var bottomPos = new GridPosition(x, y + 1);
@ -1178,7 +1178,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[bottomGridPos] = bottomZ.Value;
// 计算沿下方向的直线距离
var downDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, 1), gridMap, vehicleHeight);
var downDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, 1), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1204,10 +1204,10 @@ namespace NavisworksTransport.PathPlanning
var leftCell = gridMap.Cells[x - 1, y];
// 检查左侧网格是否有兼容的高度层
if (HasCompatibleLayer(leftCell, vehicleHeight))
if (HasCompatibleLayer(leftCell, objectHeight))
{
// 选择左侧网格的最佳高度层
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, vehicleHeight);
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, objectHeight);
if (leftZ.HasValue)
{
var leftPos = new GridPosition(x - 1, y);
@ -1217,7 +1217,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[leftGridPos] = leftZ.Value;
// 计算沿左方向的直线距离
var leftDistance = CalculateStraightDistance(gridPos, new GridPoint2D(-1, 0), gridMap, vehicleHeight);
var leftDistance = CalculateStraightDistance(gridPos, new GridPoint2D(-1, 0), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1243,10 +1243,10 @@ namespace NavisworksTransport.PathPlanning
var topCell = gridMap.Cells[x, y - 1];
// 检查上方网格是否有兼容的高度层
if (HasCompatibleLayer(topCell, vehicleHeight))
if (HasCompatibleLayer(topCell, objectHeight))
{
// 选择上方网格的最佳高度层
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, vehicleHeight);
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, objectHeight);
if (topZ.HasValue)
{
var topPos = new GridPosition(x, y - 1);
@ -1256,7 +1256,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[topGridPos] = topZ.Value;
// 计算沿上方向的直线距离
var upDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, -1), gridMap, vehicleHeight);
var upDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, -1), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1281,9 +1281,9 @@ namespace NavisworksTransport.PathPlanning
if (x + 1 < gridMap.Width && y + 1 < gridMap.Height)
{
var diagonalCell = gridMap.Cells[x + 1, y + 1];
if (HasCompatibleLayer(diagonalCell, vehicleHeight))
if (HasCompatibleLayer(diagonalCell, objectHeight))
{
var diagonalZ = SelectBestLayerForTarget(diagonalCell.HeightLayers, targetZ, vehicleHeight);
var diagonalZ = SelectBestLayerForTarget(diagonalCell.HeightLayers, targetZ, objectHeight);
if (diagonalZ.HasValue)
{
var diagonalPos = new GridPosition(x + 1, y + 1);
@ -1317,19 +1317,19 @@ namespace NavisworksTransport.PathPlanning
/// 检查单元格是否满足高度约束
/// </summary>
/// <param name="cell">网格单元格</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <returns>是否可通行</returns>
private bool IsPassableWithHeight(GridCell cell, double vehicleHeight)
private bool IsPassableWithHeight(GridCell cell, double objectHeight)
{
if (!cell.HasAnyWalkableLayer())
return false;
// 检查是否有任何高度层满足车辆高度要求
// 检查是否有任何高度层满足运动物体高度要求
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
{
foreach (var layer in cell.HeightLayers)
{
if (layer.PassableHeight.GetSpan() >= vehicleHeight)
if (layer.PassableHeight.GetSpan() >= objectHeight)
return true;
}
return false;
@ -1339,12 +1339,12 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 检查单元格是否有满足车辆高度的高度层
/// 检查单元格是否有满足运动物体高度的高度层
/// </summary>
/// <param name="cell">网格单元格</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <returns>是否有兼容的高度层</returns>
private bool HasCompatibleLayer(GridCell cell, double vehicleHeight)
private bool HasCompatibleLayer(GridCell cell, double objectHeight)
{
if (!cell.HasAnyWalkableLayer())
return false;
@ -1354,7 +1354,7 @@ namespace NavisworksTransport.PathPlanning
foreach (var layer in cell.HeightLayers)
{
if (layer.PassableHeight.GetSpan() >= vehicleHeight)
if (layer.PassableHeight.GetSpan() >= objectHeight)
return true;
}
@ -1362,13 +1362,13 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 从高度层列表中选择最接近目标高度且满足车辆高度的层
/// 从高度层列表中选择最接近目标高度且满足运动物体高度的层
/// </summary>
/// <param name="layers">高度层列表</param>
/// <param name="targetZ">目标高度(米)</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <returns>选中的高度层Z坐标如果没有合适的返回null</returns>
private double? SelectBestLayerForTarget(List<HeightLayer> layers, double targetZ, double vehicleHeight)
private double? SelectBestLayerForTarget(List<HeightLayer> layers, double targetZ, double objectHeight)
{
if (layers == null || layers.Count == 0)
return null;
@ -1378,8 +1378,8 @@ namespace NavisworksTransport.PathPlanning
foreach (var layer in layers)
{
// 必须满足车辆高度
if (layer.PassableHeight.GetSpan() < vehicleHeight)
// 必须满足运动物体高度
if (layer.PassableHeight.GetSpan() < objectHeight)
continue;
// 选择最接近目标高度的层
@ -1414,16 +1414,16 @@ namespace NavisworksTransport.PathPlanning
/// <param name="startPos">起始网格位置</param>
/// <param name="direction">方向 (1,0)=右, (0,1)=下, (-1,0)=左, (0,-1)=上</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <returns>直线距离(网格单位)</returns>
private int CalculateStraightDistance(GridPoint2D startPos, GridPoint2D direction,
GridMap gridMap, double vehicleHeight)
GridMap gridMap, double objectHeight)
{
int distance = 0;
var currentPos = new GridPoint2D(startPos.X + direction.X, startPos.Y + direction.Y);
// 沿方向前进,直到遇到障碍
while (IsValidAndPassable(currentPos, gridMap, vehicleHeight))
while (IsValidAndPassable(currentPos, gridMap, objectHeight))
{
distance++;
currentPos = new GridPoint2D(currentPos.X + direction.X, currentPos.Y + direction.Y);
@ -1440,9 +1440,9 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="pos">网格位置</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <returns>是否有效且可通行</returns>
private bool IsValidAndPassable(GridPoint2D pos, GridMap gridMap, double vehicleHeight)
private bool IsValidAndPassable(GridPoint2D pos, GridMap gridMap, double objectHeight)
{
// 检查边界
if (pos.X < 0 || pos.X >= gridMap.Width || pos.Y < 0 || pos.Y >= gridMap.Height)
@ -1450,7 +1450,7 @@ namespace NavisworksTransport.PathPlanning
// 检查可通行性
var cell = gridMap.Cells[pos.X, pos.Y];
return IsPassableWithHeight(cell, vehicleHeight);
return IsPassableWithHeight(cell, objectHeight);
}
/// <summary>
@ -2034,9 +2034,9 @@ namespace NavisworksTransport.PathPlanning
/// <param name="gridPos">网格坐标</param>
/// <param name="point">检查点(世界坐标)</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <returns>是否可通行</returns>
private bool IsPointPassableAtHeight(GridPoint2D gridPos, Point3D point, GridMap gridMap, double vehicleHeight)
private bool IsPointPassableAtHeight(GridPoint2D gridPos, Point3D point, GridMap gridMap, double objectHeight)
{
try
{
@ -2062,19 +2062,19 @@ namespace NavisworksTransport.PathPlanning
// 检查高度层约束
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
{
// 查找包含指定Z坐标且满足车辆高度的层
// 查找包含指定Z坐标且满足运动物体高度的层
var matchingLayer = gridMap.FindLayerContainingZ(gridPos, point.Z, tolerance: 0.5);
if (matchingLayer.HasValue)
{
bool heightOk = matchingLayer.Value.PassableHeight.GetSpan() >= vehicleHeight;
bool heightOk = matchingLayer.Value.PassableHeight.GetSpan() >= objectHeight;
if (heightOk)
{
return true;
}
else
{
LogManager.Warning($"[高度检查] ❌ 找到匹配Z={point.Z:F2}的层Z={matchingLayer.Value.Z:F2},但高度不足:车辆高度{vehicleHeight:F2},层高度跨度{matchingLayer.Value.PassableHeight.GetSpan():F2}");
LogManager.Warning($"[高度检查] ❌ 找到匹配Z={point.Z:F2}的层Z={matchingLayer.Value.Z:F2},但高度不足:运动物体高度{objectHeight:F2},层高度跨度{matchingLayer.Value.PassableHeight.GetSpan():F2}");
}
}
else
@ -2099,11 +2099,11 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="pathWithGridCoords">路径点和对应的网格坐标</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">运动物体高度(模型单位)</param>
/// <param name="originalStart">用户指定的原始起点保留原始Z坐标</param>
/// <param name="originalEnd">用户指定的原始终点保留原始Z坐标</param>
/// <returns>调整后的路径</returns>
private List<Point3D> ApplyGridHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double vehicleHeight, Point3D originalStart, Point3D originalEnd)
private List<Point3D> ApplyGridHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double objectHeight, Point3D originalStart, Point3D originalEnd)
{
LogManager.Info($"[智能选层] 开始为路径点选择最优高度层,路径点数: {pathWithGridCoords.Count}");
@ -2148,7 +2148,7 @@ namespace NavisworksTransport.PathPlanning
// 起点和终点锁定到包含其Z坐标的层
if (i == 0)
{
var selectedLayer = SelectBestLayer(cell.HeightLayers, startZ, vehicleHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
var selectedLayer = SelectBestLayer(cell.HeightLayers, startZ, objectHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
if (selectedLayer.HasValue)
{
adjustedPath.Add(new Point3D(point.X, point.Y, selectedLayer.Value.Z));
@ -2161,7 +2161,7 @@ namespace NavisworksTransport.PathPlanning
}
else if (i == pathWithGridCoords.Count - 1)
{
var selectedLayer = SelectBestLayer(cell.HeightLayers, endZ, vehicleHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
var selectedLayer = SelectBestLayer(cell.HeightLayers, endZ, objectHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
if (selectedLayer.HasValue)
{
adjustedPath.Add(new Point3D(point.X, point.Y, selectedLayer.Value.Z));
@ -2176,7 +2176,7 @@ namespace NavisworksTransport.PathPlanning
{
// 中间点:根据高度趋势选择最佳层
var prevZ = adjustedPath[i - 1].Z;
var selectedLayer = SelectBestLayer(cell.HeightLayers, prevZ, vehicleHeight, startZ, endZ, i, pathWithGridCoords.Count, false);
var selectedLayer = SelectBestLayer(cell.HeightLayers, prevZ, objectHeight, startZ, endZ, i, pathWithGridCoords.Count, false);
if (selectedLayer.HasValue)
{
@ -2260,14 +2260,14 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="layers">可用的高度层列表</param>
/// <param name="referenceZ">参考Z坐标起点/终点为目标Z中间点为前一点Z</param>
/// <param name="vehicleHeight">车辆高度</param>
/// <param name="objectHeight">运动物体高度</param>
/// <param name="startZ">路径起点Z</param>
/// <param name="endZ">路径终点Z</param>
/// <param name="currentIndex">当前点索引</param>
/// <param name="totalPoints">总点数</param>
/// <param name="exactMatch">是否精确匹配(起点/终点使用)</param>
/// <returns>选中的高度层如果没有合适的返回null</returns>
private HeightLayer? SelectBestLayer(List<HeightLayer> layers, double referenceZ, double vehicleHeight,
private HeightLayer? SelectBestLayer(List<HeightLayer> layers, double referenceZ, double objectHeight,
double startZ, double endZ, int currentIndex, int totalPoints, bool exactMatch)
{
if (layers == null || layers.Count == 0)
@ -2277,7 +2277,7 @@ namespace NavisworksTransport.PathPlanning
if (layers.Count == 1)
{
var layer = layers[0];
if (layer.PassableHeight.GetSpan() >= vehicleHeight)
if (layer.PassableHeight.GetSpan() >= objectHeight)
return layer;
return null;
}
@ -2307,13 +2307,13 @@ namespace NavisworksTransport.PathPlanning
}
}
// 选择最接近目标Z且满足车辆高度要求的层
// 选择最接近目标Z且满足运动物体高度要求的层
HeightLayer? bestLayer = null;
double minDistance = double.MaxValue;
foreach (var layer in layers)
{
if (layer.PassableHeight.GetSpan() < vehicleHeight)
if (layer.PassableHeight.GetSpan() < objectHeight)
continue;
double distance = Math.Abs(layer.Z - targetZ);
@ -2439,8 +2439,8 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="pathWithGridCoords">路径点和对应的网格坐标</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
private void ValidatePathHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double vehicleHeight)
/// <param name="objectHeight">运动物体高度(模型单位)</param>
private void ValidatePathHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double objectHeight)
{
LogManager.Info($"[路径高度验证] 开始验证 {pathWithGridCoords.Count} 个路径点的高度约束");
@ -2466,7 +2466,7 @@ namespace NavisworksTransport.PathPlanning
}
// 进行高度约束检查
bool isPassable = IsPointPassableAtHeight(gridPos, point, gridMap, vehicleHeight);
bool isPassable = IsPointPassableAtHeight(gridPos, point, gridMap, objectHeight);
if (isPassable)
{
passedPoints++;
@ -2595,7 +2595,7 @@ namespace NavisworksTransport.PathPlanning
/// 安全优先的A*网格转换方法
/// 🔥 重写:参照局部直线优先算法的成功模式,重新构建网格连接
/// </summary>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridSafestCenter(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridSafestCenter(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos)
{
try
{
@ -2648,7 +2648,7 @@ namespace NavisworksTransport.PathPlanning
var cell = gridMap.Cells[x, y];
// 检查是否有兼容的高度层
if (!HasCompatibleLayer(cell, vehicleHeight))
if (!HasCompatibleLayer(cell, objectHeight))
{
if (cell.HasAnyWalkableLayer())
{
@ -2658,7 +2658,7 @@ namespace NavisworksTransport.PathPlanning
}
// 选择当前网格的最佳高度层
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, vehicleHeight);
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, objectHeight);
if (!currentZ.HasValue)
{
continue;
@ -2678,10 +2678,10 @@ namespace NavisworksTransport.PathPlanning
var rightCell = gridMap.Cells[x + 1, y];
// 检查右侧网格是否有兼容的高度层
if (HasCompatibleLayer(rightCell, vehicleHeight))
if (HasCompatibleLayer(rightCell, objectHeight))
{
// 选择右侧网格的最佳高度层
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, vehicleHeight);
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, objectHeight);
if (rightZ.HasValue)
{
var rightPos = new GridPosition(x + 1, y);
@ -2720,10 +2720,10 @@ namespace NavisworksTransport.PathPlanning
var bottomCell = gridMap.Cells[x, y + 1];
// 检查下方网格是否有兼容的高度层
if (HasCompatibleLayer(bottomCell, vehicleHeight))
if (HasCompatibleLayer(bottomCell, objectHeight))
{
// 选择下方网格的最佳高度层
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, vehicleHeight);
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, objectHeight);
if (bottomZ.HasValue)
{
var bottomPos = new GridPosition(x, y + 1);
@ -2762,10 +2762,10 @@ namespace NavisworksTransport.PathPlanning
var leftCell = gridMap.Cells[x - 1, y];
// 检查左侧网格是否有兼容的高度层
if (HasCompatibleLayer(leftCell, vehicleHeight))
if (HasCompatibleLayer(leftCell, objectHeight))
{
// 选择左侧网格的最佳高度层
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, vehicleHeight);
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, objectHeight);
if (leftZ.HasValue)
{
var leftPos = new GridPosition(x - 1, y);
@ -2804,10 +2804,10 @@ namespace NavisworksTransport.PathPlanning
var topCell = gridMap.Cells[x, y - 1];
// 检查上方网格是否有兼容的高度层
if (HasCompatibleLayer(topCell, vehicleHeight))
if (HasCompatibleLayer(topCell, objectHeight))
{
// 选择上方网格的最佳高度层
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, vehicleHeight);
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, objectHeight);
if (topZ.HasValue)
{
var topPos = new GridPosition(x, y - 1);

View File

@ -8,7 +8,7 @@ namespace NavisworksTransport.PathPlanning
{
/// <summary>
/// GridMap缓存键用于标识唯一的GridMap生成配置
/// 基于所有物流属性构件、车辆尺寸、网格尺寸等关键参数
/// 基于所有物流属性构件、运动物体尺寸、网格尺寸等关键参数
/// </summary>
public class GridMapCacheKey : IEquatable<GridMapCacheKey>
{
@ -28,9 +28,9 @@ namespace NavisworksTransport.PathPlanning
public double CellSize { get; set; }
/// <summary>
/// 车辆半径(米)
/// 运动物体半径(米)
/// </summary>
public double VehicleRadius { get; set; }
public double ObjectRadius { get; set; }
/// <summary>
/// 安全间隙(米)
@ -38,22 +38,22 @@ namespace NavisworksTransport.PathPlanning
public double SafetyMargin { get; set; }
/// <summary>
/// 车辆高度(米)
/// 运动物体高度(米)
/// </summary>
public double VehicleHeight { get; set; }
public double ObjectHeight { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public GridMapCacheKey(string logisticsDataHash, string boundsHash, double cellSize,
double vehicleRadius, double safetyMargin, double vehicleHeight)
double objectRadius, double safetyMargin, double objectHeight)
{
LogisticsDataHash = logisticsDataHash ?? throw new ArgumentNullException(nameof(logisticsDataHash));
BoundsHash = boundsHash ?? throw new ArgumentNullException(nameof(boundsHash));
CellSize = cellSize;
VehicleRadius = vehicleRadius;
ObjectRadius = objectRadius;
SafetyMargin = safetyMargin;
VehicleHeight = vehicleHeight;
ObjectHeight = objectHeight;
}
/// <summary>
@ -67,9 +67,9 @@ namespace NavisworksTransport.PathPlanning
return LogisticsDataHash == other.LogisticsDataHash &&
BoundsHash == other.BoundsHash &&
Math.Abs(CellSize - other.CellSize) < 1e-6 &&
Math.Abs(VehicleRadius - other.VehicleRadius) < 1e-6 &&
Math.Abs(ObjectRadius - other.ObjectRadius) < 1e-6 &&
Math.Abs(SafetyMargin - other.SafetyMargin) < 1e-6 &&
Math.Abs(VehicleHeight - other.VehicleHeight) < 1e-6;
Math.Abs(ObjectHeight - other.ObjectHeight) < 1e-6;
}
/// <summary>
@ -91,9 +91,9 @@ namespace NavisworksTransport.PathPlanning
hash = hash * 23 + (LogisticsDataHash?.GetHashCode() ?? 0);
hash = hash * 23 + (BoundsHash?.GetHashCode() ?? 0);
hash = hash * 23 + CellSize.GetHashCode();
hash = hash * 23 + VehicleRadius.GetHashCode();
hash = hash * 23 + ObjectRadius.GetHashCode();
hash = hash * 23 + SafetyMargin.GetHashCode();
hash = hash * 23 + VehicleHeight.GetHashCode();
hash = hash * 23 + ObjectHeight.GetHashCode();
return hash;
}
}
@ -111,7 +111,7 @@ namespace NavisworksTransport.PathPlanning
"NULL";
return $"GridMapKey[Logistics={logisticsPreview}, Bounds={boundsPreview}, " +
$"Cell={CellSize:F2}, VR={VehicleRadius:F1}m, SM={SafetyMargin:F1}m, VH={VehicleHeight:F1}m]";
$"Cell={CellSize:F2}, OR={ObjectRadius:F1}m, SM={SafetyMargin:F1}m, OH={ObjectHeight:F1}m]";
}
/// <summary>
@ -119,19 +119,19 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="bounds">边界范围</param>
/// <param name="cellSize">网格大小</param>
/// <param name="vehicleRadius">车辆半径(米)</param>
/// <param name="objectRadius">运动物体半径(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="objectHeight">运动物体高度(米)</param>
/// <returns>缓存键</returns>
public static GridMapCacheKey CreateFrom(BoundingBox3D bounds,
double cellSize, double vehicleRadius,
double safetyMargin, double vehicleHeight)
double cellSize, double objectRadius,
double safetyMargin, double objectHeight)
{
var logisticsDataHash = ComputeLogisticsDataHash();
var boundsHash = ComputeBoundsHash(bounds);
return new GridMapCacheKey(logisticsDataHash, boundsHash, cellSize,
vehicleRadius, safetyMargin, vehicleHeight);
objectRadius, safetyMargin, objectHeight);
}
/// <summary>

View File

@ -58,35 +58,35 @@ namespace NavisworksTransport.PathPlanning
/// <param name="document">Navisworks文档</param>
/// <param name="bounds">扫描边界</param>
/// <param name="cellSize">网格单元大小(米)</param>
/// <param name="vehicleRadius">车辆半径(米)</param>
/// <param name="objectRadius">运动物体半径(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="planningStartPoint">规划起点</param>
/// <param name="planningEndPoint">规划终点</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="objectHeight">运动物体高度(米)</param>
/// <returns>生成的网格地图</returns>
public GridMap GenerateFromBIM(BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, double vehicleHeight)
public GridMap GenerateFromBIM(BoundingBox3D bounds, double cellSize, double objectRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, double objectHeight)
{
try
{
// 第一步:统一转换所有米制参数为模型单位
double metersToModelUnitsConversionFactor = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
double cellSizeInModelUnits = cellSize * metersToModelUnitsConversionFactor;
double vehicleRadiusInModelUnits = vehicleRadius * metersToModelUnitsConversionFactor;
double objectRadiusInModelUnits = objectRadius * metersToModelUnitsConversionFactor;
double safetyMarginInModelUnits = safetyMargin * metersToModelUnitsConversionFactor;
double vehicleHeightInModelUnits = vehicleHeight * metersToModelUnitsConversionFactor;
double scanHeightInModelUnits = vehicleHeightInModelUnits + safetyMarginInModelUnits; // 扫描高度 = 车辆高度 + 安全间隙
double totalInflationRadiusInModelUnits = vehicleRadiusInModelUnits + safetyMarginInModelUnits; // 膨胀半径 = 车辆半径 + 安全间隙
double objectHeightInModelUnits = objectHeight * metersToModelUnitsConversionFactor;
double scanHeightInModelUnits = objectHeightInModelUnits + safetyMarginInModelUnits; // 扫描高度 = 运动物体高度 + 安全间隙
double totalInflationRadiusInModelUnits = objectRadiusInModelUnits + safetyMarginInModelUnits; // 膨胀半径 = 运动物体半径 + 安全间隙
LogManager.Info($"【生成网格地图】参数单位转换完成 (转换系数: {metersToModelUnitsConversionFactor:F2}):");
LogManager.Info($" 网格大小: {cellSize}米 → {cellSizeInModelUnits:F2}模型单位");
LogManager.Info($" 车辆半径: {vehicleRadius}米 → {vehicleRadiusInModelUnits:F2}模型单位");
LogManager.Info($" 运动物体半径: {objectRadius}米 → {objectRadiusInModelUnits:F2}模型单位");
LogManager.Info($" 安全间隙: {safetyMargin}米 → {safetyMarginInModelUnits:F2}模型单位");
LogManager.Info($" 车辆高度: {vehicleHeight}米 → {vehicleHeightInModelUnits:F2}模型单位");
LogManager.Info($" 运动物体高度: {objectHeight}米 → {objectHeightInModelUnits:F2}模型单位");
LogManager.Info($" 扫描高度: {scanHeightInModelUnits:F2}模型单位");
LogManager.Info($" 膨胀半径: {totalInflationRadiusInModelUnits:F2}模型单位");
// 生成缓存键(使用转换后的模型单位参数确保一致性)
var cacheKey = GridMapCacheKey.CreateFrom(bounds, cellSizeInModelUnits, vehicleRadiusInModelUnits, safetyMarginInModelUnits, vehicleHeightInModelUnits);
var cacheKey = GridMapCacheKey.CreateFrom(bounds, cellSizeInModelUnits, objectRadiusInModelUnits, safetyMarginInModelUnits, objectHeightInModelUnits);
// 尝试从缓存获取
var cachedGridMap = GlobalGridMapCache.Instance.Get(cacheKey, 5000); // 预估生成需要5秒
@ -163,10 +163,10 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"【阶段2.7完成】边界层标记后网格统计: {channelCoverage.GridMap.GetStatistics()}");
// 3. 应用车辆尺寸膨胀
if (vehicleRadius > 0 || safetyMargin > 0)
if (objectRadius > 0 || safetyMargin > 0)
{
LogManager.Info("【生成网格地图】步骤3: 应用车辆膨胀");
ApplyVehicleInflation(channelCoverage.GridMap, totalInflationRadiusInModelUnits);
ApplyObjectInflation(channelCoverage.GridMap, totalInflationRadiusInModelUnits);
LogManager.Info($"【生成网格地图】车辆膨胀完成,膨胀半径: {totalInflationRadiusInModelUnits:F2}模型单位");
LogManager.Info($"【阶段3完成】车辆膨胀后网格统计: {channelCoverage.GridMap.GetStatistics()}");
}
@ -834,19 +834,19 @@ namespace NavisworksTransport.PathPlanning
/// 在障碍物周围扩展不可通行区域,考虑车辆尺寸
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleRadiusInModelUnits">车辆半径(模型单位)</param>
private void ApplyVehicleInflation(GridMap gridMap, double vehicleRadiusInModelUnits)
/// <param name="objectRadiusInModelUnits">运动物体半径(模型单位)</param>
private void ApplyObjectInflation(GridMap gridMap, double objectRadiusInModelUnits)
{
if (vehicleRadiusInModelUnits <= 0) return;
if (objectRadiusInModelUnits <= 0) return;
try
{
// 计算膨胀半径(网格单元格数)
int inflationRadius = (int)Math.Ceiling(vehicleRadiusInModelUnits / gridMap.CellSize);
int inflationRadius = (int)Math.Ceiling(objectRadiusInModelUnits / gridMap.CellSize);
LogManager.Info($"[高效膨胀] 膨胀半径: {inflationRadius}个网格单元");
// 使用高效的距离变换算法
ApplyVehicleInflation(gridMap, inflationRadius);
ApplyObjectInflation(gridMap, inflationRadius);
}
catch (Exception ex)
{
@ -859,7 +859,7 @@ namespace NavisworksTransport.PathPlanning
/// 多层膨胀算法 - 分两次处理:障碍物膨胀 + 边界膨胀
/// 核心原则:障碍物从外围膨胀(不含本身),边界从边界本身膨胀(包含边界)
/// </summary>
private void ApplyVehicleInflation(GridMap gridMap, int inflationRadius)
private void ApplyObjectInflation(GridMap gridMap, int inflationRadius)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
@ -1313,7 +1313,7 @@ namespace NavisworksTransport.PathPlanning
var walkableGridCount = channelCoverage.WalkableGridCount;
var walkableHeightRange = walkableMaxZ - walkableMinZ;
LogManager.Info($"[高性能障碍物处理] 可通行网格高度信息 - 最低点: {walkableMinZ:F2}, 最高点: {walkableMaxZ:F2}");
LogManager.Info($"[高性能障碍物处理] 可通行网格数量: {walkableGridCount}, 高度范围: {walkableHeightRange:F2}, 加车辆高度后扫描范围: [{walkableMinZ:F2}, {walkableMaxZ + scanHeightInModelUnits:F2}]");
LogManager.Info($"[高性能障碍物处理] 可通行网格数量: {walkableGridCount}, 高度范围: {walkableHeightRange:F2}, 加运动物体高度后扫描范围: [{walkableMinZ:F2}, {walkableMaxZ + scanHeightInModelUnits:F2}]");
// 阶段4网格覆盖计算并行几何计算- 使用50%CPU核心优化 + 基于通道的精确高度检查
LogManager.Info("[高性能障碍物处理] 阶段4: 并行网格覆盖计算 + 基于通道高度的精确高度检查");
@ -1337,7 +1337,7 @@ namespace NavisworksTransport.PathPlanning
var centerCell = gridMap.Cells[centerGridPos.X, centerGridPos.Y];
// 使用可通行网格高度范围计算扫描范围
// 扫描范围:从可通行网格最低点开始,到可通行网格最高点+车辆高度+安全间隙
// 扫描范围:从可通行网格最低点开始,到可通行网格最高点+运动物体高度+安全间隙
var scanMin = walkableMinZ;
var scanMax = walkableMaxZ + scanHeightInModelUnits;

View File

@ -47,14 +47,14 @@ namespace NavisworksTransport.PathPlanning
public bool EnableCollisionCheck { get; set; }
/// <summary>
/// 车辆长度(米,未来扩展)
/// 物体长度(米,未来扩展)
/// </summary>
public double VehicleLength { get; set; }
public double ObjectLength { get; set; }
/// <summary>
/// 车辆宽度(米,未来扩展)
/// 物体宽度(米,未来扩展)
/// </summary>
public double VehicleWidth { get; set; }
public double ObjectWidth { get; set; }
}
private readonly OptimizationConfig _config;

View File

@ -93,15 +93,15 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="bounds">网格边界(世界坐标,模型单位)</param>
/// <param name="voxelSizeMeters">体素尺寸(米)</param>
/// <param name="vehicleRadiusMeters">车辆半径(米),用于安全间隙</param>
/// <param name="vehicleHeightMeters">车辆高度(米)</param>
/// <param name="objectRadiusMeters">车辆半径(米),用于安全间隙</param>
/// <param name="objectHeightMeters">车辆高度(米)</param>
/// <param name="obstacleItems">障碍物模型元素列表</param>
/// <returns>生成的体素网格</returns>
public VoxelGrid GenerateFromBIMWithSDF(
BoundingBox3D bounds,
double voxelSizeMeters,
double vehicleRadiusMeters,
double vehicleHeightMeters,
double objectRadiusMeters,
double objectHeightMeters,
IEnumerable<ModelItem> obstacleItems)
{
var stopwatch = Stopwatch.StartNew();
@ -112,13 +112,13 @@ namespace NavisworksTransport.PathPlanning
Autodesk.Navisworks.Api.Application.ActiveDocument.Units);
double voxelSizeInModelUnits = voxelSizeMeters * metersToModelUnits;
double vehicleRadiusInModelUnits = vehicleRadiusMeters * metersToModelUnits;
double vehicleHeightInModelUnits = vehicleHeightMeters * metersToModelUnits;
double safetyMarginInModelUnits = vehicleRadiusInModelUnits; // 安全间隙 = 车辆半径
double objectRadiusInModelUnits = objectRadiusMeters * metersToModelUnits;
double objectHeightInModelUnits = objectHeightMeters * metersToModelUnits;
double safetyMarginInModelUnits = objectRadiusInModelUnits; // 安全间隙 = 车辆半径
LogManager.Info($"单位转换系数: {metersToModelUnits:F4}");
LogManager.Info($"体素尺寸: {voxelSizeMeters}米 = {voxelSizeInModelUnits:F2}模型单位");
LogManager.Info($"安全间隙: {vehicleRadiusMeters}米 = {safetyMarginInModelUnits:F2}模型单位");
LogManager.Info($"安全间隙: {objectRadiusMeters}米 = {safetyMarginInModelUnits:F2}模型单位");
// 第二步:创建体素网格
var voxelGrid = new VoxelGrid(bounds, voxelSizeInModelUnits);
@ -286,12 +286,12 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"标记统计: 障碍物={obstacleCount:N0}, 可通行={passableCount:N0}, 边界={boundaryCount:N0}");
// 第六步:障碍物膨胀(可选)
if (vehicleRadiusMeters > 0)
if (objectRadiusMeters > 0)
{
LogManager.Info("开始障碍物膨胀...");
var inflationStopwatch = Stopwatch.StartNew();
int inflatedCount = voxelGrid.InflateObstacles(vehicleRadiusMeters, metersToModelUnits);
int inflatedCount = voxelGrid.InflateObstacles(objectRadiusMeters, metersToModelUnits);
inflationStopwatch.Stop();
LogManager.Info($"障碍物膨胀完成,耗时: {inflationStopwatch.ElapsedMilliseconds} ms");

View File

@ -313,12 +313,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private bool _canGenerateAnimation = false;
// 虚拟车辆相关字段
private bool _useVirtualVehicle = false; // 使用虚拟车辆
private double _virtualVehicleLength; // 虚拟车辆长度(米)
private double _virtualVehicleWidth; // 虚拟车辆宽度(米)
private double _virtualVehicleHeight; // 虚拟车辆高度(米)
private bool _useVirtualObject = false; // 使用虚拟车辆
private double _virtualObjectLength; // 虚拟车辆长度(米)
private double _virtualObjectWidth; // 虚拟车辆宽度(米)
private double _virtualObjectHeight; // 虚拟车辆高度(米)
private double _safetyMargin; // 检测间隙(米),从路径编辑同步
// 剖面盒优化相关字段
private bool _useSectionClip = true; // 使用剖面盒优化(默认启用,用于性能对比测试)
// 角度修正相关字段
private double _objectRotationCorrection; // 物体角度修正值(度,顺时针)
@ -613,14 +616,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// 是否使用选择的模型物体
/// </summary>
/// <summary>
/// 是否使用虚拟车辆
/// 是否使用剖面盒优化(用于性能对比测试)
/// </summary>
public bool UseVirtualVehicle
public bool UseSectionClip
{
get => _useVirtualVehicle;
get => _useSectionClip;
set
{
if (SetProperty(ref _useVirtualVehicle, value))
if (SetProperty(ref _useSectionClip, value))
{
LogManager.Info($"[剖面盒] 优化开关已{(value ? "" : "")}");
}
}
}
/// <summary>
/// 是否使用虚拟车辆
/// </summary>
public bool UseVirtualObject
{
get => _useVirtualObject;
set
{
if (SetProperty(ref _useVirtualObject, value))
{
// ✨ 模式切换清理:切换到虚拟车辆模式时,移除选择物体的动画数据
if (value)
@ -630,7 +648,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 只有在当前动画对象不是虚拟车辆时才执行重置
if (_pathAnimationManager != null &&
_pathAnimationManager.AnimatedObject != null &&
!VirtualVehicleManager.Instance.IsVirtualVehicleActive)
!VirtualObjectManager.Instance.IsVirtualObjectActive)
{
_pathAnimationManager.RestoreObjectToCADPosition();
_pathAnimationManager.ClearAnimationResults(); // 清理手动选择物体留下的动画数据
@ -639,8 +657,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 显示虚拟车辆
LogManager.Info("正在显示虚拟车辆...");
VirtualVehicleManager.Instance.ShowVirtualVehicle(
VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight);
VirtualObjectManager.Instance.ShowVirtualObject(
VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight);
// 重置角度修正值(虚拟车辆重新选择时重置)
ObjectRotationCorrection = 0.0;
@ -663,7 +681,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
try
{
// 不删除虚拟车辆,只隐藏它
VirtualVehicleManager.Instance.HideVirtualVehicle();
VirtualObjectManager.Instance.HideVirtualObject();
_pathAnimationManager?.ClearAnimationResults(); // 清理虚拟车辆留下的动画数据
LogManager.Info("已切换到手动选择模式,自动隐藏虚拟车辆并清理动画数据");
}
@ -680,28 +698,28 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// <summary>
/// 虚拟车辆长度(米)- 只读,从路径编辑同步
/// </summary>
public double VirtualVehicleLength
public double VirtualObjectLength
{
get => _virtualVehicleLength;
private set => SetProperty(ref _virtualVehicleLength, value);
get => _virtualObjectLength;
private set => SetProperty(ref _virtualObjectLength, value);
}
/// <summary>
/// 虚拟车辆宽度(米)- 只读,从路径编辑同步
/// </summary>
public double VirtualVehicleWidth
public double VirtualObjectWidth
{
get => _virtualVehicleWidth;
private set => SetProperty(ref _virtualVehicleWidth, value);
get => _virtualObjectWidth;
private set => SetProperty(ref _virtualObjectWidth, value);
}
/// <summary>
/// 虚拟车辆高度(米)- 只读,从路径编辑同步
/// </summary>
public double VirtualVehicleHeight
public double VirtualObjectHeight
{
get => _virtualVehicleHeight;
private set => SetProperty(ref _virtualVehicleHeight, value);
get => _virtualObjectHeight;
private set => SetProperty(ref _virtualObjectHeight, value);
}
/// <summary>
@ -724,18 +742,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// 设置虚拟车辆参数由主ViewModel调用
/// 参数单位:米
/// </summary>
public void SetVirtualVehicleParameters(double length, double width, double height, double safetyMargin)
public void SetVirtualObjectParameters(double length, double width, double height, double safetyMargin)
{
LogManager.Info($"[AnimationControlViewModel] SetVirtualVehicleParameters被调用: length={length:F4}, width={width:F4}, height={height:F4}, safetyMargin={safetyMargin:F4}");
VirtualVehicleLength = length;
VirtualVehicleWidth = width;
VirtualVehicleHeight = height;
LogManager.Info($"[AnimationControlViewModel] SetVirtualObjectParameters被调用: length={length:F4}, width={width:F4}, height={height:F4}, safetyMargin={safetyMargin:F4}");
VirtualObjectLength = length;
VirtualObjectWidth = width;
VirtualObjectHeight = height;
_safetyMargin = safetyMargin;
LogManager.Info($"[AnimationControlViewModel] 虚拟车辆参数已更新: {length:F1}米 × {width:F1}米 × {height:F1}米, 检测间隙: {safetyMargin:F2}米, _safetyMargin={_safetyMargin:F4}");
// 如果当前使用虚拟车辆模式,更新生成动画的可用状态
if (UseVirtualVehicle)
if (UseVirtualObject)
{
UpdateCanGenerateAnimation();
// 更新通行空间可视化
@ -1053,9 +1071,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
_detectionTolerance = config.Animation.DetectionToleranceMeters;
// 🔥 从配置加载虚拟车辆尺寸(与路径编辑保持一致,使用米单位接口)
VirtualVehicleLength = config.PathEditing.VehicleLengthMeters;
VirtualVehicleWidth = config.PathEditing.VehicleWidthMeters;
VirtualVehicleHeight = config.PathEditing.VehicleHeightMeters;
VirtualObjectLength = config.PathEditing.ObjectLengthMeters;
VirtualObjectWidth = config.PathEditing.ObjectWidthMeters;
VirtualObjectHeight = config.PathEditing.ObjectHeightMeters;
// 设置动画按钮的初始状态
UpdateAnimationButtonStates();
@ -1134,12 +1152,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var config = ConfigManager.Instance.Current;
// 更新车辆参数
VirtualVehicleLength = config.PathEditing.VehicleLengthMeters;
VirtualVehicleWidth = config.PathEditing.VehicleWidthMeters;
VirtualVehicleHeight = config.PathEditing.VehicleHeightMeters;
VirtualObjectLength = config.PathEditing.ObjectLengthMeters;
VirtualObjectWidth = config.PathEditing.ObjectWidthMeters;
VirtualObjectHeight = config.PathEditing.ObjectHeightMeters;
_safetyMargin = config.PathEditing.SafetyMarginMeters;
LogManager.Info($"车辆参数已更新 - 尺寸:{VirtualVehicleLength:F1}x{VirtualVehicleWidth:F1}x{VirtualVehicleHeight:F1}米, 安全间隙:{_safetyMargin:F2}米");
LogManager.Info($"车辆参数已更新 - 尺寸:{VirtualObjectLength:F1}x{VirtualObjectWidth:F1}x{VirtualObjectHeight:F1}米, 安全间隙:{_safetyMargin:F2}米");
}
}
catch (Exception ex)
@ -3089,7 +3107,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// <summary>
/// 获取运动物体的 PathId 信息
/// </summary>
private (int ModelIndex, string PathId) GetVehiclePathIdInfo()
private (int ModelIndex, string PathId) GetObjectPathIdInfo()
{
try
{
@ -3258,7 +3276,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
Mouse.OverrideCursor = Cursors.Wait;
UpdateMainStatus("正在生成动画...", -1, true);
LogManager.Info($"开始生成动画 - 模式: {(UseVirtualVehicle ? "" : "")}");
LogManager.Info($"开始生成动画 - 模式: {(UseVirtualObject ? "" : "")}");
var cacheStartTime = DateTime.Now;
@ -3277,7 +3295,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
// 🔥 预计算动画对象的排除列表(仅在非虚拟车辆、非手工模式下)
if (!manualModeEnabled && !UseVirtualVehicle)
if (!manualModeEnabled && !UseVirtualObject)
{
UpdateMainStatus("正在分析动画对象...", -1, true);
@ -3313,18 +3331,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ModelItem animatedObject = null;
if (UseVirtualVehicle)
if (UseVirtualObject)
{
// 使用虚拟车辆:总是调用 ShowVirtualVehicle 来更新尺寸
// 使用虚拟车辆:总是调用 ShowVirtualObject 来更新尺寸
LogManager.Info("[ExecuteGenerateAnimation] 显示并更新虚拟车辆...");
UpdateMainStatus("正在显示虚拟车辆...", -1, true);
VirtualVehicleManager.Instance.ShowVirtualVehicle(
VirtualVehicleLength,
VirtualVehicleWidth,
VirtualVehicleHeight
VirtualObjectManager.Instance.ShowVirtualObject(
VirtualObjectLength,
VirtualObjectWidth,
VirtualObjectHeight
);
animatedObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
animatedObject = VirtualObjectManager.Instance.CurrentVirtualObject;
if (animatedObject == null)
{
@ -3337,7 +3355,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (CurrentPathRoute != null && CurrentPathRoute.Points != null)
{
var points = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList();
_pathAnimationManager?.MoveVehicleToPathStart(animatedObject, points);
_pathAnimationManager?.MoveObjectToPathStart(animatedObject, points);
LogManager.Info("[ExecuteGenerateAnimation] 虚拟车辆已移到路径起点");
}
@ -3380,23 +3398,26 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 准备车辆尺寸参数(从米转换为模型单位)
var metersToModelUnits = Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
double vLength = UseVirtualVehicle ? VirtualVehicleLength * metersToModelUnits : 0;
double vWidth = UseVirtualVehicle ? VirtualVehicleWidth * metersToModelUnits : 0;
double vHeight = UseVirtualVehicle ? VirtualVehicleHeight * metersToModelUnits : 0;
double vLength = UseVirtualObject ? VirtualObjectLength * metersToModelUnits : 0;
double vWidth = UseVirtualObject ? VirtualObjectWidth * metersToModelUnits : 0;
double vHeight = UseVirtualObject ? VirtualObjectHeight * metersToModelUnits : 0;
double safetyMargin = _safetyMargin * metersToModelUnits;
LogManager.Info($"[ExecuteGenerateAnimation] 准备调用CreateAnimation: UseVirtualVehicle={UseVirtualVehicle}, 车辆尺寸: {vLength:F2}×{vWidth:F2}×{vHeight:F2}模型单位, 安全间隙: {safetyMargin:F4}模型单位 (_safetyMargin={_safetyMargin:F4}米)");
LogManager.Info($"[ExecuteGenerateAnimation] 准备调用CreateAnimation: UseVirtualObject={UseVirtualObject}, 车辆尺寸: {vLength:F2}×{vWidth:F2}×{vHeight:F2}模型单位, 安全间隙: {safetyMargin:F4}模型单位 (_safetyMargin={_safetyMargin:F4}米)");
_pathAnimationManager.CreateAnimation(animatedObject, pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id, UseVirtualVehicle,
// 设置剖面盒优化开关
_pathAnimationManager.SetUseSectionClip(UseSectionClip);
_pathAnimationManager.CreateAnimation(animatedObject, pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id, UseVirtualObject,
vLength, vWidth, vHeight, safetyMargin);
var totalElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
LogManager.Info($"[动画生成] 动画生成完成,总耗时: {totalElapsed:F1}ms");
LogManager.Info($"动画生成成功: 物体={animatedObject.DisplayName}, 路径={CurrentPathRoute.Name}");
if (UseVirtualVehicle)
if (UseVirtualObject)
{
LogManager.Info($"虚拟车辆尺寸: {VirtualVehicleLength:F1}×{VirtualVehicleWidth:F1}×{VirtualVehicleHeight:F1}m");
LogManager.Info($"虚拟车辆尺寸: {VirtualObjectLength:F1}×{VirtualObjectWidth:F1}×{VirtualObjectHeight:F1}m");
}
if (IsManualCollisionTargetEnabled)
@ -3458,10 +3479,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
double baseLength, baseWidth;
if (UseVirtualVehicle)
if (UseVirtualObject)
{
baseLength = VirtualVehicleLength;
baseWidth = VirtualVehicleWidth;
baseLength = VirtualObjectLength;
baseWidth = VirtualObjectWidth;
}
else if (SelectedAnimatedObject != null)
{
@ -3526,18 +3547,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 使用从路径编辑同步过来的检测间隙
double safetyMargin = _safetyMargin;
if (UseVirtualVehicle)
if (UseVirtualObject)
{
// 虚拟车辆的xyz定义X方向 = 长度前进方向Y方向 = 宽度侧面Z方向 = 高度
if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Rail || CurrentPathRoute.PathType == NavisworksTransport.PathType.Hoisting)
{
// 空中路径(空轨或吊装):高度上下都加间隙
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
passageNormalToPath = VirtualVehicleHeight + 2 * safetyMargin; // Z方向高度+ 2*间隙(法线方向)
passageNormalToPath = VirtualObjectHeight + 2 * safetyMargin; // Z方向高度+ 2*间隙(法线方向)
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
// 吊装路径分段参数
passageNormalToPathVertical = effectiveLength + 2 * safetyMargin; // 垂直段:物体长度 + 2*间隙
passageNormalToPathHorizontal = VirtualVehicleHeight + 2 * safetyMargin; // 水平段:物体高度 + 2*间隙
passageNormalToPathHorizontal = VirtualObjectHeight + 2 * safetyMargin; // 水平段:物体高度 + 2*间隙
LogManager.Debug($"[通行空间可视化] 空中路径使用虚拟车辆尺寸: 有效长度={effectiveLength:F2}m, 有效宽度={effectiveWidth:F2}m, 通行空间沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m");
}
else if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Ground)
@ -3545,7 +3566,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 地面路径物体的长度方向X轴前进方向朝向路径方向
// 通行空间沿路径方向 = X方向尺寸长度垂直于路径方向 = Y方向尺寸宽度高度上方加间隙下方不需要因为有地面
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
passageNormalToPath = VirtualVehicleHeight + safetyMargin; // Z方向高度+ 间隙(法线方向,只有上方)
passageNormalToPath = VirtualObjectHeight + safetyMargin; // Z方向高度+ 间隙(法线方向,只有上方)
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
// 地面路径不需要分段参数
passageNormalToPathVertical = passageNormalToPath;
@ -3557,7 +3578,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 空轨路径物体的长度方向X轴前进方向朝向路径方向
// 通行空间沿路径方向 = X方向尺寸长度垂直于路径方向 = Y方向尺寸宽度高度上下都加间隙在空中
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
passageNormalToPath = VirtualVehicleHeight + 2 * safetyMargin; // Z方向高度+ 2*间隙(法线方向,上下都加)
passageNormalToPath = VirtualObjectHeight + 2 * safetyMargin; // Z方向高度+ 2*间隙(法线方向,上下都加)
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
// 空轨路径不需要分段参数
passageNormalToPathVertical = passageNormalToPath;
@ -3631,16 +3652,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Debug($"[通行空间可视化] 已设置通行空间参数: 沿路径={passageAlongPath:F2}m, 垂直路径={passageAcrossPath:F2}m, 法线={passageNormalToPath:F2}m, 安全间隙={safetyMargin:F2}m");
// 根据路径类型决定是否切换到车辆通行空间模式
bool enableVehicleSpace = false;
bool enableObjectSpace = false;
switch (CurrentPathRoute.PathType)
{
case NavisworksTransport.PathType.Rail:
case NavisworksTransport.PathType.Hoisting:
enableVehicleSpace = true;
enableObjectSpace = true;
LogManager.Debug($"[通行空间可视化] 路径类型={CurrentPathRoute.PathType}(空中路径),切换到车辆通行空间模式");
break;
case NavisworksTransport.PathType.Ground:
enableVehicleSpace = false;
enableObjectSpace = false;
LogManager.Debug($"[通行空间可视化] 路径类型={CurrentPathRoute.PathType},不切换到车辆通行空间模式");
break;
default:
@ -3649,9 +3670,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
// 切换到车辆通行空间模式(如果需要)
if (enableVehicleSpace)
if (enableObjectSpace)
{
renderPlugin.VisualizationMode = PathVisualizationMode.VehicleSpace;
renderPlugin.VisualizationMode = PathVisualizationMode.ObjectSpace;
LogManager.Debug("[通行空间可视化] 已切换到车辆通行空间模式");
}
@ -3697,12 +3718,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var hasPath = CurrentPathRoute != null && CurrentPathRoute.Points.Count >= 2;
bool hasValidObject;
if (UseVirtualVehicle)
if (UseVirtualObject)
{
// 使用虚拟车辆时,只需要有效的车辆尺寸
hasValidObject = VirtualVehicleLength > 0 &&
VirtualVehicleWidth > 0 &&
VirtualVehicleHeight > 0;
hasValidObject = VirtualObjectLength > 0 &&
VirtualObjectWidth > 0 &&
VirtualObjectHeight > 0;
}
else
{
@ -3722,11 +3743,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else if (!hasPath && !hasValidObject)
{
UpdateMainStatus(UseVirtualVehicle ? "请选择动画路径" : "请选择移动物体和动画路径");
UpdateMainStatus(UseVirtualObject ? "请选择动画路径" : "请选择移动物体和动画路径");
}
else if (!hasValidObject)
{
UpdateMainStatus(UseVirtualVehicle ? "虚拟车辆参数无效" : "请选择移动物体");
UpdateMainStatus(UseVirtualObject ? "虚拟车辆参数无效" : "请选择移动物体");
}
else if (!hasPath)
{
@ -4356,10 +4377,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
DetectionToleranceMeters = _detectionTolerance,
// 运动物体配置
IsVirtualVehicle = UseVirtualVehicle,
VirtualVehicleLength = VirtualVehicleLength,
VirtualVehicleWidth = VirtualVehicleWidth,
VirtualVehicleHeight = VirtualVehicleHeight,
IsVirtualObject = UseVirtualObject,
VirtualObjectLength = VirtualObjectLength,
VirtualObjectWidth = VirtualObjectWidth,
VirtualObjectHeight = VirtualObjectHeight,
// 被检测项配置
DetectAllObjects = !IsManualCollisionTargetEnabled,
@ -4380,10 +4401,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var itemId = await batchQueueManager.AddQueueItemAsync(queueItem);
// 存储运动物体到 ModelItemReferences 表
if (!UseVirtualVehicle && SelectedAnimatedObject != null)
if (!UseVirtualObject && SelectedAnimatedObject != null)
{
LogManager.Info($"[批处理] 准备存储运动物体: {SelectedAnimatedObject.DisplayName}");
var pathIdInfo = GetVehiclePathIdInfo();
var pathIdInfo = GetObjectPathIdInfo();
LogManager.Info($"[批处理] 运动物体 PathId: ModelIndex={pathIdInfo.ModelIndex}, PathId={pathIdInfo.PathId}");
// 只要 PathId 不为空就认为是有效的ModelIndex=0 表示主模型,也是有效的)
@ -4413,7 +4434,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else
{
LogManager.Info($"[批处理] 跳过存储运动物体: UseVirtualVehicle={UseVirtualVehicle}, SelectedAnimatedObject 是否为 null={SelectedAnimatedObject == null}");
LogManager.Info($"[批处理] 跳过存储运动物体: UseVirtualObject={UseVirtualObject}, SelectedAnimatedObject 是否为 null={SelectedAnimatedObject == null}");
}
// 存储手工指定的碰撞检测目标到 ModelItemReferences 表
@ -4441,7 +4462,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
LogManager.Info($"[批处理] 已创建队列项: {CurrentPathRoute.Name}, " +
$"虚拟车辆: {queueItem.IsVirtualVehicle}, " +
$"虚拟车辆: {queueItem.IsVirtualObject}, " +
$"帧率: {queueItem.FrameRate}, " +
$"时长: {queueItem.DurationSeconds:F2}秒, " +
$"角度修正: {queueItem.ObjectRotationCorrection:F1}°, " +

View File

@ -99,7 +99,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
private bool _showPathLines = true;
private bool _showVehicleSpace = false;
private bool _showObjectSpace = false;
/// <summary>
/// 是否显示路径线/地面连线
@ -123,17 +123,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// <summary>
/// 是否显示通行空间
/// </summary>
public bool ShowVehicleSpace
public bool ShowObjectSpace
{
get => _showVehicleSpace;
get => _showObjectSpace;
set
{
if (SetProperty(ref _showVehicleSpace, value))
if (SetProperty(ref _showObjectSpace, value))
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin != null)
{
renderPlugin.ShowVehicleSpace = value;
renderPlugin.ShowObjectSpace = value;
}
}
}
@ -148,9 +148,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (renderPlugin != null)
{
_showPathLines = renderPlugin.ShowPathLines;
_showVehicleSpace = renderPlugin.ShowVehicleSpace;
_showObjectSpace = renderPlugin.ShowObjectSpace;
OnPropertyChanged(nameof(ShowPathLines));
OnPropertyChanged(nameof(ShowVehicleSpace));
OnPropertyChanged(nameof(ShowObjectSpace));
}
}

View File

@ -68,9 +68,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private bool _isSelectingEndPoint = false;
// 车辆参数 - 改为三个独立参数(从配置初始化)
private double _vehicleLength; // 车辆长度(米)
private double _vehicleWidth; // 车辆宽度(米)
private double _vehicleHeight; // 车辆高度(米)
private double _ObjectLength; // 车辆长度(米)
private double _ObjectWidth; // 车辆宽度(米)
private double _ObjectHeight; // 车辆高度(米)
private double _safetyMargin; // 安全间隙(米)
// 网格大小参数(从配置初始化)
@ -86,11 +86,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 三种渲染模式独立开关
private bool _showControlVisualization = true; // 默认显示控制点连线
private bool _showPathLines = true; // 默认显示路径线(地面连线)
private bool _showVehicleSpace = false; // 默认不显示通行空间
// 兼容旧代码的互斥属性(已废弃,使用独立开关)
private bool _isStandardLineMode = false;
private bool _isRibbonLineMode = true;
private bool _isVehicleSpaceMode = false;
private bool _showObjectSpace = false; // 默认不显示通行空间
// 路径策略参数
private PathStrategyOption _selectedPathStrategy;
@ -242,7 +238,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
// 0. 先同步车辆参数到渲染插件
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
// 检查是否在编辑状态
bool isInEditMode = _pathPlanningManager?.IsInEditableState ?? false;
@ -330,40 +326,40 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
public double VehicleLength
public double ObjectLength
{
get => _vehicleLength;
get => _ObjectLength;
set
{
if (SetProperty(ref _vehicleLength, value))
if (SetProperty(ref _ObjectLength, value))
{
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public double VehicleWidth
public double ObjectWidth
{
get => _vehicleWidth;
get => _ObjectWidth;
set
{
if (SetProperty(ref _vehicleWidth, value))
if (SetProperty(ref _ObjectWidth, value))
{
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public double VehicleHeight
public double ObjectHeight
{
get => _vehicleHeight;
get => _ObjectHeight;
set
{
if (SetProperty(ref _vehicleHeight, value))
if (SetProperty(ref _ObjectHeight, value))
{
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
@ -378,7 +374,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var roundedValue = Math.Round(value, 2);
if (SetProperty(ref _safetyMargin, roundedValue))
{
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
@ -558,12 +554,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// <summary>
/// 是否显示通行空间(独立开关)
/// </summary>
public bool ShowVehicleSpace
public bool ShowObjectSpace
{
get => _showVehicleSpace;
get => _showObjectSpace;
set
{
if (SetProperty(ref _showVehicleSpace, value))
if (SetProperty(ref _showObjectSpace, value))
{
OnPathVisualizationModeChanged();
}
@ -589,24 +585,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// </summary>
public bool IsStandardLineMode
{
get => _showPathLines && !_showVehicleSpace;
get => _showPathLines && !_showObjectSpace;
set
{
if (value)
{
ShowPathLines = true;
ShowVehicleSpace = false;
ShowObjectSpace = false;
}
}
}
/// <summary>
/// 是否使用车辆通行空间模式(向后兼容,等同于 ShowVehicleSpace
/// 是否使用车辆通行空间模式(向后兼容,等同于 ShowObjectSpace
/// </summary>
public bool IsVehicleSpaceMode
public bool IsObjectSpaceMode
{
get => _showVehicleSpace;
set => ShowVehicleSpace = value;
get => _showObjectSpace;
set => ShowObjectSpace = value;
}
/// <summary>
@ -711,7 +707,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#region Can Execute属性
public bool CanExecuteAutoPlanPath => _hasStartPoint && _hasEndPoint &&
VehicleLength > 0 && VehicleWidth > 0 && VehicleHeight > 0 &&
ObjectLength > 0 && ObjectWidth > 0 && ObjectHeight > 0 &&
SafetyMargin >= 0;
public bool CanExecuteNewPath => !IsSelectingStartPoint && !IsSelectingEndPoint;
@ -780,7 +776,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 初始化车辆参数同步在PathPointRenderPlugin实例不为空时才同步
// 如果PathPointRenderPlugin尚未初始化将在属性变更时自动同步
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
// 注意不在这里订阅PathPlanningManager事件
// 因为PathPlanningManager还没有设置事件订阅在PathPlanningManager属性setter中处理
@ -841,12 +837,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 从 PathEditing 配置加载所有参数(使用米单位接口)
_gridSize = config.PathEditing.CellSizeMeters;
_vehicleLength = config.PathEditing.VehicleLengthMeters;
_vehicleWidth = config.PathEditing.VehicleWidthMeters;
_vehicleHeight = config.PathEditing.VehicleHeightMeters;
_ObjectLength = config.PathEditing.ObjectLengthMeters;
_ObjectWidth = config.PathEditing.ObjectWidthMeters;
_ObjectHeight = config.PathEditing.ObjectHeightMeters;
_safetyMargin = config.PathEditing.SafetyMarginMeters;
LogManager.Info($"从配置加载参数 - 车辆: {_vehicleLength:F1}x{_vehicleWidth:F1}x{_vehicleHeight:F1}米, 安全间隙: {_safetyMargin:F2}米, 网格: {_gridSize:F1}米");
LogManager.Info($"从配置加载参数 - 车辆: {_ObjectLength:F1}x{_ObjectWidth:F1}x{_ObjectHeight:F1}米, 安全间隙: {_safetyMargin:F2}米, 网格: {_gridSize:F1}米");
}
catch (Exception ex)
{
@ -910,19 +906,19 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 触发属性变更通知更新UI
OnPropertiesChanged(
nameof(VehicleLength),
nameof(VehicleWidth),
nameof(VehicleHeight),
nameof(ObjectLength),
nameof(ObjectWidth),
nameof(ObjectHeight),
nameof(SafetyMargin),
nameof(GridSize)
);
// 同步车辆参数到渲染插件
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
// 更新状态栏
string source = eventArgs.IsExternalChange ? "外部修改" : "内部更新";
UpdateMainStatus($"配置已更新 ({source}) - 车辆参数: {VehicleLength:F1}x{VehicleWidth:F1}x{VehicleHeight:F1}米");
UpdateMainStatus($"配置已更新 ({source}) - 车辆参数: {ObjectLength:F1}x{ObjectWidth:F1}x{ObjectHeight:F1}米");
LogManager.Info($"PathEditing 配置参数已更新完成 - 来源: {source}");
}
@ -1639,10 +1635,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info($"终点: ({_endPoint3D.X:F2}, {_endPoint3D.Y:F2}, {_endPoint3D.Z:F2})");
// 计算车辆半径:基于长度和宽度的较大值的一半,高度暂时不参与半径计算
var vehicleRadius = Math.Max(VehicleLength, VehicleWidth) / 2.0;
var ObjectRadius = Math.Max(ObjectLength, ObjectWidth) / 2.0;
LogManager.Info($"车辆参数: 长{VehicleLength:F1}m × 宽{VehicleWidth:F1}m × 高{VehicleHeight:F1}m");
LogManager.Info($"计算得出车辆半径: {vehicleRadius:F2}m (基于长宽较大值的一半)");
LogManager.Info($"车辆参数: 长{ObjectLength:F1}m × 宽{ObjectWidth:F1}m × 高{ObjectHeight:F1}m");
LogManager.Info($"计算得出车辆半径: {ObjectRadius:F2}m (基于长宽较大值的一半)");
// 确定网格大小:如果启用手动设置则使用用户设置的值,否则使用-1自动选择
var gridSize = IsGridSizeManuallyEnabled ? GridSize : -1;
@ -1654,7 +1650,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info($"路径策略: {SelectedPathStrategy?.DisplayName ?? ""}");
// 在STA线程中执行Navisworks API调用
var pathRoute = await _pathPlanningManager?.AutoPlanPath(startPathPoint, endPathPoint, vehicleRadius, SafetyMargin, gridSize, VehicleHeight, selectedStrategy);
var pathRoute = await _pathPlanningManager?.AutoPlanPath(startPathPoint, endPathPoint, ObjectRadius, SafetyMargin, gridSize, ObjectHeight, selectedStrategy);
if (pathRoute != null && pathRoute.Points.Count > 0)
{
@ -2778,7 +2774,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// <summary>
/// 同步车辆参数到渲染插件
/// </summary>
private void SyncVehicleParametersToRenderPlugin()
private void SyncObjectParametersToRenderPlugin()
{
try
{
@ -2800,30 +2796,30 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (pathType == PathType.Rail)
{
// 空轨路径:车辆水平悬挂,垂直于路径的截面包含车辆的宽度和高度
passageAcrossPath = VehicleWidth + 2 * SafetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageNormalToPath = VehicleHeight + 2 * SafetyMargin; // Z方向高度+ 2*间隙(法线方向)
passageAlongPath = VehicleLength; // X方向长度沿路径方向
passageAcrossPath = ObjectWidth + 2 * SafetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageNormalToPath = ObjectHeight + 2 * SafetyMargin; // Z方向高度+ 2*间隙(法线方向)
passageAlongPath = ObjectLength; // X方向长度沿路径方向
}
else if (pathType == PathType.Hoisting)
{
// 吊装路径:车辆垂直悬挂,垂直于路径的截面包含车辆的长度和宽度
passageAcrossPath = VehicleWidth + 2 * SafetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageNormalToPath = VehicleLength + 2 * SafetyMargin; // X方向长度+ 2*间隙(法线方向)
passageAlongPath = VehicleHeight; // Z方向高度沿路径方向
passageAcrossPath = ObjectWidth + 2 * SafetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageNormalToPath = ObjectLength + 2 * SafetyMargin; // X方向长度+ 2*间隙(法线方向)
passageAlongPath = ObjectHeight; // Z方向高度沿路径方向
}
else // Ground
{
// 地面路径:高度只有上方加间隙
passageAcrossPath = VehicleWidth + 2 * SafetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageNormalToPath = VehicleHeight + SafetyMargin; // Z方向高度+ 间隙(法线方向,只有上方)
passageAlongPath = VehicleLength; // X方向长度沿路径方向
passageAcrossPath = ObjectWidth + 2 * SafetyMargin; // Y方向宽度+ 2*间隙(垂直于路径方向)
passageNormalToPath = ObjectHeight + SafetyMargin; // Z方向高度+ 间隙(法线方向,只有上方)
passageAlongPath = ObjectLength; // X方向长度沿路径方向
}
// 设置通行空间参数到渲染插件
// passageNormalToPathVertical: 垂直段的高度(物体长度 + 2×安全间隙
// passageNormalToPathHorizontal: 水平段的高度(物体高度 + 2×安全间隙
double passageNormalToPathVertical = VehicleLength + 2 * SafetyMargin;
double passageNormalToPathHorizontal = VehicleHeight + 2 * SafetyMargin;
double passageNormalToPathVertical = ObjectLength + 2 * SafetyMargin;
double passageNormalToPathHorizontal = ObjectHeight + 2 * SafetyMargin;
PathPointRenderPlugin.Instance.SetPassageSpaceParameters(
passageAcrossPath,
passageNormalToPath,
@ -2943,14 +2939,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
case PathType.Hoisting:
// 空中路径:通行空间 + 控制点,不能使用路径线
ShowPathLines = false;
ShowVehicleSpace = true;
ShowObjectSpace = true;
LogManager.Debug($"[路径编辑] 路径类型={pathType}(空中路径),默认模式:控制点+通行空间");
break;
case PathType.Ground:
// 地面路径:路径线 + 控制点,不使用通行空间
ShowPathLines = true;
ShowVehicleSpace = false;
ShowObjectSpace = false;
LogManager.Debug($"[路径编辑] 路径类型={pathType}(地面路径),默认模式:控制点+路径线");
break;
@ -2959,7 +2955,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
return;
}
LogManager.Info($"[路径编辑] 已根据路径类型自动设置可视化模式:控制点={ShowControlVisualization}, 路径线={ShowPathLines}, 通行空间={ShowVehicleSpace}");
LogManager.Info($"[路径编辑] 已根据路径类型自动设置可视化模式:控制点={ShowControlVisualization}, 路径线={ShowPathLines}, 通行空间={ShowObjectSpace}");
// 🔥 触发可视化状态变更事件,通知状态栏刷新按钮
_pathPlanningManager?.RaiseVisualizationStateChanged();
@ -2982,14 +2978,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
// 使用新的独立开关设置渲染插件
renderPlugin.ShowPathLines = ShowPathLines;
renderPlugin.ShowVehicleSpace = ShowVehicleSpace;
renderPlugin.ShowObjectSpace = ShowObjectSpace;
string modeName = "";
if (ShowPathLines && ShowVehicleSpace)
if (ShowPathLines && ShowObjectSpace)
modeName = "路径线 + 通行空间";
else if (ShowPathLines)
modeName = "路径线";
else if (ShowVehicleSpace)
else if (ShowObjectSpace)
modeName = "通行空间";
else
modeName = "无路径可视化";
@ -3806,7 +3802,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// <summary>
/// 车辆参数类 - 用于传递车辆尺寸信息给路径规划算法
/// </summary>
public class VehicleParameters
public class ObjectParameters
{
public double Length { get; set; }
public double Width { get; set; }

View File

@ -1268,8 +1268,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
document,
cellSize: cellSizeMeters, // 使用系统配置的体素大小
samplingRate: 1, // 采样率 1显示所有体素
vehicleRadius: 0.6, // 车辆半径 0.6米(必须 >= 体素大小才能看到膨胀效果)
vehicleHeight: 1.8 // 车辆高度 1.8米
objectRadius: 0.6, // 物体半径 0.6米(必须 >= 体素大小才能看到膨胀效果)
objectHeight: 1.8 // 物体高度 1.8米
);
var result = await testCommand.ExecuteAsync();
@ -1359,8 +1359,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var testCommand = new VoxelPathFindingTestCommand(
document,
cellSize: cellSizeMeters, // 体素大小(米)
vehicleRadius: 0.6, // 车辆半径 0.6米
vehicleHeight: 1.8, // 车辆高度 1.8米
objectRadius: 0.6, // 物体半径 0.6米
objectHeight: 1.8, // 物体高度 1.8米
startPoint: startPoint, // 起点
endPoint: endPoint // 终点
);

View File

@ -322,7 +322,7 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
GroupName="ObjectSource"
VerticalAlignment="Center"/>
<RadioButton Content="使用虚拟车辆"
IsChecked="{Binding UseVirtualVehicle}"
IsChecked="{Binding UseVirtualObject}"
GroupName="ObjectSource"
VerticalAlignment="Center"
Margin="10,0,0,0"/>
@ -330,7 +330,7 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
<!-- 方式1选择模型物体 -->
<Grid Margin="0,5,0,0"
Visibility="{Binding UseVirtualVehicle, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=Inverse}">
Visibility="{Binding UseVirtualObject, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=Inverse}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
@ -361,7 +361,7 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
<!-- 方式2虚拟车辆 -->
<Grid Margin="0,5,0,0"
Visibility="{Binding UseVirtualVehicle, Converter={StaticResource BoolToVisibilityConverter}}">
Visibility="{Binding UseVirtualObject, Converter={StaticResource BoolToVisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
@ -375,11 +375,11 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
Foreground="#FF2B579A"
Background="#FFF5F5F5">
<Run Text="长 "/>
<Run Text="{Binding VirtualVehicleLength, StringFormat=F1, Mode=OneWay}"/>
<Run Text="{Binding VirtualObjectLength, StringFormat=F1, Mode=OneWay}"/>
<Run Text="m × 宽 "/>
<Run Text="{Binding VirtualVehicleWidth, StringFormat=F1, Mode=OneWay}"/>
<Run Text="{Binding VirtualObjectWidth, StringFormat=F1, Mode=OneWay}"/>
<Run Text="m × 高 "/>
<Run Text="{Binding VirtualVehicleHeight, StringFormat=F1, Mode=OneWay}"/>
<Run Text="{Binding VirtualObjectHeight, StringFormat=F1, Mode=OneWay}"/>
<Run Text="m"/>
</TextBlock>
<Button Grid.Column="2"
@ -394,7 +394,7 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
Style="{StaticResource StatusTextStyle}"
Foreground="#FF666666"
Margin="5,0,0,5"
Visibility="{Binding UseVirtualVehicle, Converter={StaticResource BoolToVisibilityConverter}}"/>
Visibility="{Binding UseVirtualObject, Converter={StaticResource BoolToVisibilityConverter}}"/>
<!-- 选择路径 -->
<Grid Margin="0,5,0,0">
@ -427,7 +427,12 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
<Button Content="添加到批处理"
Command="{Binding AddToBatchQueueCommand}"
IsEnabled="{Binding CanGenerateAnimation}"
Style="{StaticResource ActionButtonStyle}"/>
Style="{StaticResource ActionButtonStyle}"
Margin="0,0,10,0"/>
<CheckBox Content="使用剖面盒优化"
IsChecked="{Binding UseSectionClip}"
VerticalAlignment="Center"
ToolTip="启用剖面盒可大幅提升大型模型的碰撞检测性能(用于性能对比测试)"/>
</StackPanel>
<!-- 生成状态提示 -->

View File

@ -132,12 +132,12 @@ NavisworksTransport 主控制面板 - 采用与其他视图一致的Navisworks 2
<TextBlock Text="LN" FontSize="9" FontWeight="Bold"
Foreground="{Binding ShowPathLines, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Blue}"/>
</ToggleButton>
<ToggleButton IsChecked="{Binding ShowVehicleSpace}"
<ToggleButton IsChecked="{Binding ShowObjectSpace}"
ToolTip="通行空间"
Width="28" Height="24"
Margin="2,0">
<TextBlock Text="SP" FontSize="9" FontWeight="Bold"
Foreground="{Binding ShowVehicleSpace, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Orange}"/>
Foreground="{Binding ShowObjectSpace, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Orange}"/>
</ToggleButton>
</StackPanel>

View File

@ -169,10 +169,10 @@ namespace NavisworksTransport.UI.WPF
LogManager.Info("AnimationControlView初始化完成 - 已设置PathPlanningManager");
}
// 初始化时同步车辆参数
SyncVehicleParametersToAnimationView();
// 初始化时同步物体参数
SyncObjectParametersToAnimationView();
LogManager.Info("AnimationControlView初始化完成 - 支持统一状态栏和虚拟车辆");
LogManager.Info("AnimationControlView初始化完成 - 支持统一状态栏和虚拟物体");
}
else
{
@ -464,13 +464,13 @@ namespace NavisworksTransport.UI.WPF
SyncCurrentPathToAnimationView();
LogManager.Info("PathEditingView路径选择已变化同步到动画控制视图");
}
// 当车辆参数变化时同步到AnimationControlView
else if (e.PropertyName == nameof(PathEditingViewModel.VehicleLength) ||
e.PropertyName == nameof(PathEditingViewModel.VehicleWidth) ||
e.PropertyName == nameof(PathEditingViewModel.VehicleHeight) ||
// 当物体参数变化时同步到AnimationControlView
else if (e.PropertyName == nameof(PathEditingViewModel.ObjectLength) ||
e.PropertyName == nameof(PathEditingViewModel.ObjectWidth) ||
e.PropertyName == nameof(PathEditingViewModel.ObjectHeight) ||
e.PropertyName == nameof(PathEditingViewModel.SafetyMargin))
{
SyncVehicleParametersToAnimationView();
SyncObjectParametersToAnimationView();
}
}
catch (Exception ex)
@ -480,27 +480,27 @@ namespace NavisworksTransport.UI.WPF
}
/// <summary>
/// 同步车辆参数到动画控制视图
/// 同步物体参数到动画控制视图
/// </summary>
private void SyncVehicleParametersToAnimationView()
private void SyncObjectParametersToAnimationView()
{
try
{
if (AnimationControlView?.ViewModel != null && PathEditingView?.ViewModel != null)
{
var pathEditingVM = PathEditingView.ViewModel;
AnimationControlView.ViewModel.SetVirtualVehicleParameters(
pathEditingVM.VehicleLength,
pathEditingVM.VehicleWidth,
pathEditingVM.VehicleHeight,
AnimationControlView.ViewModel.SetVirtualObjectParameters(
pathEditingVM.ObjectLength,
pathEditingVM.ObjectWidth,
pathEditingVM.ObjectHeight,
pathEditingVM.SafetyMargin
);
LogManager.Debug($"车辆参数已同步到动画控制视图: {pathEditingVM.VehicleLength:F1}×{pathEditingVM.VehicleWidth:F1}×{pathEditingVM.VehicleHeight:F1}m, 检测间隙: {pathEditingVM.SafetyMargin:F2}m");
LogManager.Debug($"物体参数已同步到动画控制视图: {pathEditingVM.ObjectLength:F1}×{pathEditingVM.ObjectWidth:F1}×{pathEditingVM.ObjectHeight:F1}m, 检测间隙: {pathEditingVM.SafetyMargin:F2}m");
}
}
catch (Exception ex)
{
LogManager.Error($"同步车辆参数到动画控制视图失败: {ex.Message}");
LogManager.Error($"同步物体参数到动画控制视图失败: {ex.Message}");
}
}

View File

@ -43,7 +43,7 @@
<StackPanel>
<Label Content="物流类型" Style="{StaticResource SectionHeaderStyle}"/>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<RadioButton Name="rbVirtualVehicle" Content="使用虚拟物体" Margin="0,0,20,0" Checked="rbVirtualVehicle_Checked"/>
<RadioButton Name="rbVirtualObject" Content="使用虚拟物体" Margin="0,0,20,0" Checked="rbVirtualObject_Checked"/>
<RadioButton Name="rbRealObject" Content="使用真实物体" Checked="rbRealObject_Checked"/>
</StackPanel>
</StackPanel>
@ -51,7 +51,7 @@
<!-- 虚拟物体参数 -->
<Border Grid.Row="1" BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Padding="12" Margin="0,0,0,10"
Name="borderVirtualVehicle">
Name="borderVirtualObject">
<StackPanel>
<Label Content="虚拟物体尺寸(米)" Style="{StaticResource SectionHeaderStyle}"/>
<Grid Margin="0,10,0,0">
@ -67,15 +67,15 @@
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="长度X轴:" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" Name="txtVehicleLength" Text="1.0" Margin="0,0,5,5"/>
<TextBox Grid.Row="0" Grid.Column="1" Name="txtObjectLength" Text="1.0" Margin="0,0,5,5"/>
<TextBlock Grid.Row="0" Grid.Column="2" Text="米" VerticalAlignment="Center" Margin="0,0,0,5"/>
<Label Grid.Row="1" Grid.Column="0" Content="宽度Y轴:" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Name="txtVehicleWidth" Text="1.0" Margin="0,0,5,5"/>
<TextBox Grid.Row="1" Grid.Column="1" Name="txtObjectWidth" Text="1.0" Margin="0,0,5,5"/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="米" VerticalAlignment="Center" Margin="0,0,0,5"/>
<Label Grid.Row="2" Grid.Column="0" Content="高度Z轴:" VerticalAlignment="Center"/>
<TextBox Grid.Row="2" Grid.Column="1" Name="txtVehicleHeight" Text="2.0" Margin="0,0,5,5"/>
<TextBox Grid.Row="2" Grid.Column="1" Name="txtObjectHeight" Text="2.0" Margin="0,0,5,5"/>
<TextBlock Grid.Row="2" Grid.Column="2" Text="米" VerticalAlignment="Center" Margin="0,0,0,5"/>
</Grid>
</StackPanel>

View File

@ -25,12 +25,12 @@ namespace NavisworksTransport.UI.WPF.Views
// 从系统配置读取虚拟车辆默认值
var systemConfig = ConfigManager.Instance.Current;
txtVehicleLength.Text = systemConfig.PathEditing.VehicleLengthMeters.ToString("F2");
txtVehicleWidth.Text = systemConfig.PathEditing.VehicleWidthMeters.ToString("F2");
txtVehicleHeight.Text = systemConfig.PathEditing.VehicleHeightMeters.ToString("F2");
txtObjectLength.Text = systemConfig.PathEditing.ObjectLengthMeters.ToString("F2");
txtObjectWidth.Text = systemConfig.PathEditing.ObjectWidthMeters.ToString("F2");
txtObjectHeight.Text = systemConfig.PathEditing.ObjectHeightMeters.ToString("F2");
// 设置初始状态(在 InitializeComponent 之后,避免触发 Checked 事件)
rbVirtualVehicle.IsChecked = true;
rbVirtualObject.IsChecked = true;
// 如果有现有配置,加载它(会覆盖上面的默认值)
if (existingConfig != null)
@ -46,10 +46,10 @@ namespace NavisworksTransport.UI.WPF.Views
{
var result = new PathConfigResult
{
IsVirtualVehicle = rbVirtualVehicle.IsChecked == true,
VirtualVehicleLength = double.Parse(txtVehicleLength.Text),
VirtualVehicleWidth = double.Parse(txtVehicleWidth.Text),
VirtualVehicleHeight = double.Parse(txtVehicleHeight.Text),
IsVirtualObject = rbVirtualObject.IsChecked == true,
VirtualObjectLength = double.Parse(txtObjectLength.Text),
VirtualObjectWidth = double.Parse(txtObjectWidth.Text),
VirtualObjectHeight = double.Parse(txtObjectHeight.Text),
MovingObjectId = _selectedObject?.ToString(),
MovingObjectName = _selectedObject?.DisplayName,
DetectionItems = _detectionItems.Select(i => i.ToString()).ToList(),
@ -77,9 +77,9 @@ namespace NavisworksTransport.UI.WPF.Views
{
if (config == null) return;
txtVehicleLength.Text = config.VirtualVehicleLength.ToString("F2");
txtVehicleWidth.Text = config.VirtualVehicleWidth.ToString("F2");
txtVehicleHeight.Text = config.VirtualVehicleHeight.ToString("F2");
txtObjectLength.Text = config.VirtualObjectLength.ToString("F2");
txtObjectWidth.Text = config.VirtualObjectWidth.ToString("F2");
txtObjectHeight.Text = config.VirtualObjectHeight.ToString("F2");
txtFrameRate.Text = config.FrameRate.ToString();
txtDuration.Text = config.DurationSeconds.ToString("F2");
txtDetectionGap.Text = config.DetectionToleranceMeters.ToString("F2");
@ -88,9 +88,9 @@ namespace NavisworksTransport.UI.WPF.Views
/// <summary>
/// 虚拟车辆选中
/// </summary>
private void rbVirtualVehicle_Checked(object sender, RoutedEventArgs e)
private void rbVirtualObject_Checked(object sender, RoutedEventArgs e)
{
borderVirtualVehicle.Visibility = Visibility.Visible;
borderVirtualObject.Visibility = Visibility.Visible;
borderRealObject.Visibility = Visibility.Collapsed;
}
@ -99,7 +99,7 @@ namespace NavisworksTransport.UI.WPF.Views
/// </summary>
private void rbRealObject_Checked(object sender, RoutedEventArgs e)
{
borderVirtualVehicle.Visibility = Visibility.Collapsed;
borderVirtualObject.Visibility = Visibility.Collapsed;
borderRealObject.Visibility = Visibility.Visible;
}
@ -208,21 +208,21 @@ namespace NavisworksTransport.UI.WPF.Views
try
{
// 验证虚拟车辆参数
if (rbVirtualVehicle.IsChecked == true)
if (rbVirtualObject.IsChecked == true)
{
if (!double.TryParse(txtVehicleLength.Text, out var length) || length <= 0)
if (!double.TryParse(txtObjectLength.Text, out var length) || length <= 0)
{
MessageBox.Show("请输入有效的车辆长度", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (!double.TryParse(txtVehicleWidth.Text, out var width) || width <= 0)
if (!double.TryParse(txtObjectWidth.Text, out var width) || width <= 0)
{
MessageBox.Show("请输入有效的车辆宽度", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (!double.TryParse(txtVehicleHeight.Text, out var height) || height <= 0)
if (!double.TryParse(txtObjectHeight.Text, out var height) || height <= 0)
{
MessageBox.Show("请输入有效的车辆高度", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
@ -282,10 +282,10 @@ namespace NavisworksTransport.UI.WPF.Views
/// </summary>
public class PathConfigResult
{
public bool IsVirtualVehicle { get; set; }
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
public bool IsVirtualObject { get; set; }
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
public string MovingObjectId { get; set; }
public string MovingObjectName { get; set; }
public List<string> DetectionItems { get; set; }

View File

@ -2,7 +2,7 @@
NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管理一致的Navisworks 2026风格
功能说明:
1. 自动路径规划:起终点选择、车辆尺寸参数(长宽高)、安全间隙设置
1. 自动路径规划:起终点选择、运动物体尺寸参数(长宽高)、安全间隙设置
2. 路径列表:新建、删除、重命名路径,显示路径状态
3. 路径编辑:开始编辑、结束编辑、清空路径、路径点管理
4. 路径文件管理:导入、导出全部、导出选中路径操作和状态显示
@ -65,7 +65,7 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
</StackPanel>
</StackPanel>
<!-- 车辆参数设置 - 改为三个独立参数 -->
<!-- 运动物体参数设置 - 改为三个独立参数 -->
<Grid Margin="0,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@ -80,24 +80,24 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 车辆长度 -->
<Label Grid.Row="0" Grid.Column="0" Content="车辆长度:" Style="{StaticResource ParameterLabelStyle}"/>
<!-- 运动物体长度 -->
<Label Grid.Row="0" Grid.Column="0" Content="运动物体长度:" Style="{StaticResource ParameterLabelStyle}"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding VehicleLength, StringFormat=0.0###}"
Text="{Binding ObjectLength, StringFormat=0.0###}"
Style="{StaticResource ParameterInputStyle}"/>
<Label Grid.Row="0" Grid.Column="2" Content="米" Style="{StaticResource UnitLabelStyle}"/>
<!-- 车辆宽度 -->
<Label Grid.Row="0" Grid.Column="3" Content="车辆宽度:" Style="{StaticResource ParameterLabelStyle}"/>
<!-- 运动物体宽度 -->
<Label Grid.Row="0" Grid.Column="3" Content="运动物体宽度:" Style="{StaticResource ParameterLabelStyle}"/>
<TextBox Grid.Row="0" Grid.Column="4"
Text="{Binding VehicleWidth, StringFormat=0.0###}"
Text="{Binding ObjectWidth, StringFormat=0.0###}"
Style="{StaticResource ParameterInputStyle}"/>
<Label Grid.Row="0" Grid.Column="5" Content="米" Style="{StaticResource UnitLabelStyle}"/>
<!-- 车辆高度 -->
<Label Grid.Row="1" Grid.Column="0" Content="车辆高度:" Style="{StaticResource ParameterLabelStyle}"/>
<!-- 运动物体高度 -->
<Label Grid.Row="1" Grid.Column="0" Content="运动物体高度:" Style="{StaticResource ParameterLabelStyle}"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding VehicleHeight, StringFormat=0.0###}"
Text="{Binding ObjectHeight, StringFormat=0.0###}"
Style="{StaticResource ParameterInputStyle}"/>
<Label Grid.Row="1" Grid.Column="2" Content="米" Style="{StaticResource UnitLabelStyle}"/>
@ -463,7 +463,7 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
ToolTip="显示路径线/地面连线(仅地面路径可用)"/>
<!-- 通行空间开关 -->
<CheckBox Content="通行空间"
IsChecked="{Binding ShowVehicleSpace}"
IsChecked="{Binding ShowObjectSpace}"
VerticalAlignment="Center"
ToolTip="显示通行空间(矩形通道)"/>
</StackPanel>