添加虚拟车辆尺寸参数到路径动画管理器和碰撞检测结果,优化数据库保存功能

This commit is contained in:
tian 2026-01-08 19:50:44 +08:00
parent 40fce35bc8
commit ee1b0cbe32
5 changed files with 308 additions and 10 deletions

View File

@ -90,6 +90,9 @@ namespace NavisworksTransport.Core.Animation
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 List<Point3D> _pathPoints;
private List<ModelItem> _manualCollisionTargets = new List<ModelItem>();
private bool _manualCollisionOverrideEnabled = false;
@ -1174,7 +1177,10 @@ namespace NavisworksTransport.Core.Animation
_animatedObject,
_isVirtualVehicle,
_animationFrameRate,
_animationDuration
_animationDuration,
_virtualVehicleLength,
_virtualVehicleWidth,
_virtualVehicleHeight
);
_completedCollisionTests.Add(_currentAnimationHash); // 记录此配置已完成碰撞检测
LogManager.Info($"碰撞测试汇总已创建并记录,此配置后续播放将跳过碰撞检测");
@ -2228,11 +2234,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)
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)
{
_pathName = pathName;
_currentRouteId = routeId;
_isVirtualVehicle = isVirtualVehicle; // 设置是否使用虚拟车辆
_virtualVehicleLength = virtualVehicleLength;
_virtualVehicleWidth = virtualVehicleWidth;
_virtualVehicleHeight = virtualVehicleHeight;
SetupAnimation(animatedObject, durationSeconds, _route);
SetState(AnimationState.Ready);

View File

@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Clash;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Config;
using NavisworksTransport.Utils;
namespace NavisworksTransport
@ -165,7 +167,8 @@ namespace NavisworksTransport
/// </summary>
private void SaveClashDetectiveResultToDatabase(string pathName, string routeId, List<CollisionResult> clashResults,
int frameRate, double duration, double detectionGap,
ModelItem animatedObject, bool isVirtualVehicle)
ModelItem animatedObject, bool isVirtualVehicle,
double virtualVehicleLength, double virtualVehicleWidth, double virtualVehicleHeight)
{
try
{
@ -174,6 +177,7 @@ namespace NavisworksTransport
{
// 获取动画对象名称
string animatedObjectName = "未知对象";
if (!isVirtualVehicle && animatedObject != null)
{
animatedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animatedObject);
@ -196,6 +200,9 @@ namespace NavisworksTransport
}
}
// 打印虚拟车辆尺寸(用于调试)
LogManager.Info($"[SaveClashDetectiveResultToDatabase] IsVirtualVehicle={isVirtualVehicle}, 虚拟车辆尺寸: Length={virtualVehicleLength:F2}m, Width={virtualVehicleWidth:F2}m, Height={virtualVehicleHeight:F2}m");
var record = new ClashDetectiveResultRecord
{
TestName = _currentTestName,
@ -210,6 +217,9 @@ namespace NavisworksTransport
AnimatedObjectName = animatedObjectName,
IsVirtualVehicle = isVirtualVehicle,
VehicleModelItemPath = vehicleModelItemPath,
VirtualVehicleLength = virtualVehicleLength,
VirtualVehicleWidth = virtualVehicleWidth,
VirtualVehicleHeight = virtualVehicleHeight,
CreatedAt = DateTime.Now
};
var resultId = pathDatabase.SaveClashDetectiveResult(record);
@ -254,6 +264,169 @@ namespace NavisworksTransport
}
}
/// <summary>
/// 从数据库加载ClashDetective碰撞结果
/// </summary>
/// <param name="testName">测试名称</param>
/// <returns>碰撞结果列表如果加载失败返回null</returns>
private List<CollisionResult> LoadClashDetectiveResultsFromDatabase(string testName)
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] PathDatabase不可用");
return null;
}
// 1. 从数据库读取测试信息
var testInfoSql = @"
SELECT Id, PathName, RouteId, IsVirtualVehicle, VehicleModelItemPath,
VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight
FROM ClashDetectiveResults
WHERE TestName = @testName
";
ClashDetectiveResultRecord testInfo = null;
using (var cmd = new System.Data.SQLite.SQLiteCommand(testInfoSql, pathDatabase._connection))
{
cmd.Parameters.AddWithValue("@testName", testName);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
testInfo = new ClashDetectiveResultRecord
{
Id = Convert.ToInt32(reader["Id"]),
PathName = reader["PathName"].ToString(),
RouteId = reader["RouteId"]?.ToString(),
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
VehicleModelItemPath = reader["VehicleModelItemPath"]?.ToString(),
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
};
}
}
}
if (testInfo == null)
{
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 未找到测试记录: {testName}");
return null;
}
// 2. 重建车辆对象
ModelItem vehicleObject = null;
if (testInfo.IsVirtualVehicle)
{
// 创建虚拟车辆
vehicleObject = VirtualVehicleManager.Instance.CreateVirtualVehicle(
testInfo.VirtualVehicleLength,
testInfo.VirtualVehicleWidth,
testInfo.VirtualVehicleHeight
);
if (vehicleObject == null)
{
LogManager.Error($"[LoadClashDetectiveResultsFromDatabase] 创建虚拟车辆失败");
return null;
}
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 已创建虚拟车辆: {testInfo.VirtualVehicleLength}×{testInfo.VirtualVehicleWidth}×{testInfo.VirtualVehicleHeight}m");
}
else if (!string.IsNullOrEmpty(testInfo.VehicleModelItemPath))
{
// 通过路径查找真实车辆
var paths = testInfo.VehicleModelItemPath.Split(';');
vehicleObject = NavisworksApiHelper.FindModelItemByPath(paths[0]);
if (vehicleObject == null)
{
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 无法通过路径找到车辆对象: {paths[0]}");
return null;
}
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 已找到真实车辆: {ModelItemAnalysisHelper.GetSafeDisplayName(vehicleObject)}");
}
if (vehicleObject == null)
{
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 无法重建车辆对象");
return null;
}
// 3. 从数据库读取被撞物体信息
var collisionObjectsSql = @"
SELECT ModelItemPath, DisplayName, ObjectName
FROM ClashDetectiveCollisionObjects
WHERE ResultId = @resultId
";
var collisionObjects = new List<ClashDetectiveCollisionObjectRecord>();
using (var cmd = new System.Data.SQLite.SQLiteCommand(collisionObjectsSql, pathDatabase._connection))
{
cmd.Parameters.AddWithValue("@resultId", testInfo.Id);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
collisionObjects.Add(new ClashDetectiveCollisionObjectRecord
{
ModelItemPath = reader["ModelItemPath"]?.ToString(),
DisplayName = reader["DisplayName"]?.ToString(),
ObjectName = reader["ObjectName"]?.ToString()
});
}
}
}
// 4. 重建碰撞结果
var results = new List<CollisionResult>();
var processedPaths = new HashSet<string>();
foreach (var obj in collisionObjects)
{
// 通过路径查找被撞物体
var collidedObject = NavisworksApiHelper.FindModelItemByPath(obj.ModelItemPath);
if (collidedObject != null && ModelItemAnalysisHelper.IsModelItemValid(collidedObject))
{
// 避免重复添加(同一物体可能被撞多次)
if (!processedPaths.Contains(obj.ModelItemPath))
{
processedPaths.Add(obj.ModelItemPath);
var collisionResult = new CollisionResult
{
ClashGuid = Guid.NewGuid(),
DisplayName = $"历史碰撞: {obj.DisplayName}",
Status = ClashResultStatus.Active,
Item1 = vehicleObject,
Item2 = collidedObject,
Center = collidedObject.BoundingBox().Center,
Distance = 0.0,
CreatedTime = DateTime.Now,
OriginalItem1 = vehicleObject,
OriginalItem2 = collidedObject,
HasContainerMapping = true
};
results.Add(collisionResult);
}
}
else
{
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 无法通过路径找到被撞物体: {obj.ModelItemPath}");
}
}
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 从数据库加载测试 '{testName}' 完成,重建了 {results.Count} 个碰撞结果");
return results;
}
catch (Exception ex)
{
LogManager.Error($"[LoadClashDetectiveResultsFromDatabase] 加载失败: {ex.Message}", ex);
return null;
}
}
/// <summary>
/// 合并复合对象碰撞结果
/// 将几何体级别的碰撞结果合并成复合对象级别的碰撞结果(去重)
@ -411,10 +584,14 @@ namespace NavisworksTransport
/// <param name="isVirtualVehicle">是否使用虚拟车辆</param>
/// <param name="frameRate">帧率</param>
/// <param name="duration">动画时长</param>
/// <param name="virtualVehicleLength">虚拟车辆长度(米)</param>
/// <param name="virtualVehicleWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualVehicleHeight">虚拟车辆高度(米)</param>
public void CreateAllAnimationCollisionTests(List<CollisionResult> precomputedCollisions, double detectionGap = 0.05,
string pathName = "未知路径", string routeId = null,
ModelItem animatedObject = null, bool isVirtualVehicle = false,
int frameRate = 30, double duration = 10.0)
int frameRate = 30, double duration = 10.0,
double virtualVehicleLength = 0, double virtualVehicleWidth = 0, double virtualVehicleHeight = 0)
{
try
{
@ -703,7 +880,8 @@ namespace NavisworksTransport
_clashDetectiveCollisionCount = clashResults.Count;
// 保存到数据库
SaveClashDetectiveResultToDatabase(pathName, routeId, clashResults, frameRate, duration, detectionGap, animatedObject, isVirtualVehicle);
SaveClashDetectiveResultToDatabase(pathName, routeId, clashResults, frameRate, duration, detectionGap, animatedObject, isVirtualVehicle,
virtualVehicleLength, virtualVehicleWidth, virtualVehicleHeight);
// 第四步:将分组添加到主测试
if (collisionGroup.Children.Count > 0)
@ -1077,14 +1255,27 @@ namespace NavisworksTransport
{
lock (_clashResultsCacheLock)
{
// 先尝试从缓存获取
if (_clashDetectiveResultsCache.TryGetValue(testName, out var cachedResults))
{
LogManager.Debug($"[ClashDetective结果] 从缓存获取 '{testName}' 的结果:{cachedResults.Count}个碰撞");
return cachedResults;
}
// 缓存中没有,尝试从数据库加载
LogManager.Info($"[ClashDetective结果] 缓存中没有 '{testName}',尝试从数据库加载");
var loadedResults = LoadClashDetectiveResultsFromDatabase(testName);
if (loadedResults != null && loadedResults.Count > 0)
{
// 缓存加载的结果
_clashDetectiveResultsCache[testName] = loadedResults;
LogManager.Info($"[ClashDetective结果] 已从数据库加载 '{testName}' 的结果并缓存:{loadedResults.Count}个碰撞");
return loadedResults;
}
}
throw new InvalidOperationException($"未找到测试 '{testName}' 的ClashDetective缓存结果请先运行碰撞检测");
throw new InvalidOperationException($"未找到测试 '{testName}' 的ClashDetective结果(缓存和数据库中都没有)");
}
/// <summary>

View File

@ -14,9 +14,14 @@ namespace NavisworksTransport
/// </summary>
public class PathDatabase : IDisposable
{
private SQLiteConnection _connection;
internal SQLiteConnection _connection;
private readonly string _dbPath;
/// <summary>
/// 数据库连接(供内部使用)
/// </summary>
internal SQLiteConnection Connection => _connection;
/// <summary>
/// 初始化路径数据库
/// </summary>
@ -172,6 +177,9 @@ namespace NavisworksTransport
AnimatedObjectName TEXT,
IsVirtualVehicle INTEGER,
VehicleModelItemPath TEXT,
VirtualVehicleLength REAL,
VirtualVehicleWidth REAL,
VirtualVehicleHeight REAL,
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
)
");
@ -406,9 +414,11 @@ namespace NavisworksTransport
var sql = @"
INSERT INTO ClashDetectiveResults
(TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelItemPath, CreatedAt)
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelItemPath,
VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight, CreatedAt)
VALUES (@testName, @pathName, @routeId, @testTime, @collisionCount, @animationCollisionCount,
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualVehicle, @vehiclePath, @createdAt)
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualVehicle, @vehiclePath,
@virtualVehicleLength, @virtualVehicleWidth, @virtualVehicleHeight, @createdAt)
";
long newId = 0;
@ -426,6 +436,9 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@animatedObjectName", record.AnimatedObjectName ?? "");
cmd.Parameters.AddWithValue("@isVirtualVehicle", record.IsVirtualVehicle);
cmd.Parameters.AddWithValue("@vehiclePath", record.VehicleModelItemPath ?? "");
cmd.Parameters.AddWithValue("@virtualVehicleLength", record.VirtualVehicleLength);
cmd.Parameters.AddWithValue("@virtualVehicleWidth", record.VirtualVehicleWidth);
cmd.Parameters.AddWithValue("@virtualVehicleHeight", record.VirtualVehicleHeight);
cmd.Parameters.AddWithValue("@createdAt", record.CreatedAt);
cmd.ExecuteNonQuery();
newId = _connection.LastInsertRowId;
@ -1198,6 +1211,9 @@ namespace NavisworksTransport
public string AnimatedObjectName { get; set; }
public bool IsVirtualVehicle { get; set; }
public string VehicleModelItemPath { get; set; }
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
public DateTime CreatedAt { get; set; }
}

View File

@ -2187,7 +2187,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 先设置路径到动画管理器
_pathAnimationManager.SetRoute(pathRoute);
_pathAnimationManager.CreateAnimation(animatedObject, pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id, UseVirtualVehicle);
// 准备车辆尺寸参数
double vLength = UseVirtualVehicle ? VirtualVehicleLength : 0;
double vWidth = UseVirtualVehicle ? VirtualVehicleWidth : 0;
double vHeight = UseVirtualVehicle ? VirtualVehicleHeight : 0;
LogManager.Info($"[ExecuteGenerateAnimation] 准备调用CreateAnimation: UseVirtualVehicle={UseVirtualVehicle}, 车辆尺寸: {vLength:F2}×{vWidth:F2}×{vHeight:F2}m");
_pathAnimationManager.CreateAnimation(animatedObject, pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id, UseVirtualVehicle,
vLength, vWidth, vHeight);
var totalElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
LogManager.Info($"[动画生成] 动画生成完成,总耗时: {totalElapsed:F1}ms");

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
using Autodesk.Navisworks.Api;
namespace NavisworksTransport.Utils
@ -130,5 +131,77 @@ namespace NavisworksTransport.Utils
return "获取失败";
}
}
/// <summary>
/// 通过索引路径查找ModelItem
/// 路径格式:逗号分隔的索引数组,如"1,3,5"
/// 注意COM API的路径是1-based索引需要转换为0-based
/// </summary>
/// <param name="pathString">路径字符串</param>
/// <returns>找到的ModelItem如果找不到返回null</returns>
public static ModelItem FindModelItemByPath(string pathString)
{
if (string.IsNullOrEmpty(pathString))
return null;
try
{
// 解析路径数组1-based索引
var pathParts = pathString.Split(',')
.Select(p => p.Trim())
.Where(p => !string.IsNullOrEmpty(p))
.Select(p => int.Parse(p))
.ToArray();
if (pathParts.Length == 0)
{
LogManager.Warning($"[FindModelItemByPath] 路径 '{pathString}' 解析失败");
return null;
}
// 从RootItems开始查找
var rootItems = Application.ActiveDocument.Models.RootItems;
var firstRoot = rootItems.FirstOrDefault();
if (firstRoot == null)
{
LogManager.Warning("[FindModelItemByPath] 文档根节点为空");
return null;
}
// 递归查找
var currentItem = firstRoot;
for (int i = 0; i < pathParts.Length; i++)
{
// COM API是1-based索引转换为0-based
var targetIndex = pathParts[i] - 1;
var children = currentItem.Children;
if (children == null || children.Count() <= targetIndex)
{
LogManager.Warning($"[FindModelItemByPath] 索引超出范围: 索引={targetIndex}, 子节点数={children?.Count() ?? 0}");
return null;
}
currentItem = children.ElementAt(targetIndex);
}
if (currentItem != null)
{
LogManager.Debug($"[FindModelItemByPath] 成功找到ModelItem: {currentItem.DisplayName}");
}
else
{
LogManager.Warning($"[FindModelItemByPath] 未找到ModelItem: {pathString}");
}
return currentItem;
}
catch (Exception ex)
{
LogManager.Error($"[FindModelItemByPath] 查找失败: {ex.Message}", ex);
return null;
}
}
}
}