新增碰撞时运动物体位置和朝向记录功能,在碰撞报告中可以查看两者碰撞情况
This commit is contained in:
parent
cf5aed422b
commit
ee33e43048
@ -4,7 +4,7 @@
|
||||
|
||||
### [2026/2/8]
|
||||
|
||||
1. [.] (功能)增加预计算结果分析和排除建议
|
||||
1. [x] (功能)增加预计算结果分析和排除建议
|
||||
2. [ ] (优化)考虑在碰撞报告中,给每一个碰撞元素自动建立截图
|
||||
3. [ ] (优化)考虑是否给截图按碰撞记录组织成子文件夹
|
||||
4. [ ] (优化)研究如何利用ClashDetective的多线程(支持打开多线程)
|
||||
|
||||
@ -1100,6 +1100,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
framePosition.Z + (virtualBoundingBox.Max.Z - virtualBoundingBox.Min.Z) / 2
|
||||
),
|
||||
Item2Position = GetObjectPosition(collider),
|
||||
Item1YawRadians = yawRadians, // 记录运动物体朝向
|
||||
HasPositionInfo = true
|
||||
};
|
||||
|
||||
@ -1114,11 +1115,15 @@ namespace NavisworksTransport.Core.Animation
|
||||
// 统计碰撞信息
|
||||
var framesWithCollision = _animationFrames.Count(f => f.HasCollision);
|
||||
var totalCollisions = _animationFrames.Sum(f => f.Collisions.Count);
|
||||
// 检查碰撞结果是否包含位置信息
|
||||
var collisionsWithPosition = _allCollisionResults.Count(c => c.HasPositionInfo && c.Item1Position != null);
|
||||
|
||||
LogManager.Info($"=== 预计算完成 ===");
|
||||
LogManager.Info($"总帧数: {_animationFrames.Count}");
|
||||
LogManager.Info($"包含碰撞的帧: {framesWithCollision}");
|
||||
LogManager.Info($"总碰撞次数: {totalCollisions}");
|
||||
LogManager.Info($"记录的碰撞结果总数: {_allCollisionResults.Count} 个");
|
||||
LogManager.Info($"包含位置信息的碰撞: {collisionsWithPosition}/{_allCollisionResults.Count}");
|
||||
|
||||
// 🔥 Human-in-the-Loop: 记录排除统计
|
||||
if (_excludedObjects.Count > 0)
|
||||
|
||||
@ -140,6 +140,55 @@ namespace NavisworksTransport
|
||||
private Dictionary<string, List<CollisionResult>> _clashDetectiveResultsCache = new Dictionary<string, List<CollisionResult>>();
|
||||
private readonly object _clashResultsCacheLock = new object();
|
||||
|
||||
// 🔥 新增:记录ClashDetective确认碰撞时的位置信息
|
||||
// key: "Item1Id|Item2Id", value: 确认碰撞时的位置信息
|
||||
private Dictionary<string, CollisionPositionInfo> _confirmedCollisionPositions = new Dictionary<string, CollisionPositionInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// 碰撞位置信息(用于记录ClashDetective确认时的位置)
|
||||
/// </summary>
|
||||
private class CollisionPositionInfo
|
||||
{
|
||||
public Point3D Item1Position { get; set; }
|
||||
public Point3D Item2Position { get; set; }
|
||||
public double Item1YawRadians { get; set; }
|
||||
public bool HasPositionInfo { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成碰撞对象的唯一key(使用PathId,不使用InstanceGuid因为多数元素为0)
|
||||
/// </summary>
|
||||
private string GetCollisionObjectKey(ModelItem item1, ModelItem item2)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = Application.ActiveDocument;
|
||||
string key1 = "null";
|
||||
string key2 = "null";
|
||||
|
||||
if (item1 != null && doc != null)
|
||||
{
|
||||
var pathId = doc.Models.CreatePathId(item1);
|
||||
key1 = $"{pathId.ModelIndex}:{pathId.PathId}";
|
||||
}
|
||||
|
||||
if (item2 != null && doc != null)
|
||||
{
|
||||
var pathId = doc.Models.CreatePathId(item2);
|
||||
key2 = $"{pathId.ModelIndex}:{pathId.PathId}";
|
||||
}
|
||||
|
||||
return $"{key1}|{key2}";
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 回退到DisplayName(可能不唯一,但总比空key好)
|
||||
var key1 = item1?.DisplayName ?? "null";
|
||||
var key2 = item2?.DisplayName ?? "null";
|
||||
return $"{key1}|{key2}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取去重后的预计算碰撞结果(第一次去重,按碰撞对象对去重)
|
||||
/// </summary>
|
||||
@ -210,6 +259,7 @@ namespace NavisworksTransport
|
||||
LogManager.Info($"ClashDetective结果已保存到数据库,Id={resultId}, IsVirtualVehicle={isVirtualVehicle}");
|
||||
|
||||
// 保存被撞物体信息(只保存Item2,不保存车辆Item1)
|
||||
// 同时保存碰撞时运动物体的位置和朝向,用于还原碰撞场景
|
||||
var collisionObjects = new List<ClashDetectiveCollisionObjectRecord>();
|
||||
|
||||
foreach (var collision in clashResults)
|
||||
@ -220,14 +270,28 @@ namespace NavisworksTransport
|
||||
// 使用 CreatePathId API 获取 ModelIndex 和 PathId
|
||||
var pathId = Application.ActiveDocument.Models.CreatePathId(collision.Item2);
|
||||
|
||||
collisionObjects.Add(new ClashDetectiveCollisionObjectRecord
|
||||
var collisionRecord = new ClashDetectiveCollisionObjectRecord
|
||||
{
|
||||
ResultId = resultId,
|
||||
ModelIndex = pathId.ModelIndex,
|
||||
PathId = pathId.PathId,
|
||||
DisplayName = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2),
|
||||
ObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2)
|
||||
});
|
||||
};
|
||||
|
||||
// 保存碰撞时运动物体的位置和朝向(如果可用)
|
||||
if (collision.HasPositionInfo && collision.Item1Position != null)
|
||||
{
|
||||
collisionRecord.Item1PosX = collision.Item1Position.X;
|
||||
collisionRecord.Item1PosY = collision.Item1Position.Y;
|
||||
collisionRecord.Item1PosZ = collision.Item1Position.Z;
|
||||
collisionRecord.Item1YawRadians = collision.Item1YawRadians;
|
||||
collisionRecord.HasPositionInfo = true;
|
||||
|
||||
LogManager.Debug($"[保存碰撞对象] 记录运动物体位置: ({collisionRecord.Item1PosX:F2}, {collisionRecord.Item1PosY:F2}, {collisionRecord.Item1PosZ:F2}), 朝向: {collisionRecord.Item1YawRadians:F2} rad");
|
||||
}
|
||||
|
||||
collisionObjects.Add(collisionRecord);
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,9 +456,10 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 从数据库读取被撞物体信息
|
||||
// 3. 从数据库读取被撞物体信息(包含碰撞时运动物体的位置和朝向)
|
||||
var collisionObjectsSql = @"
|
||||
SELECT ModelIndex, PathId, DisplayName, ObjectName
|
||||
SELECT ModelIndex, PathId, DisplayName, ObjectName,
|
||||
Item1PosX, Item1PosY, Item1PosZ, Item1YawRadians, HasPositionInfo
|
||||
FROM ClashDetectiveCollisionObjects
|
||||
WHERE ResultId = @resultId
|
||||
";
|
||||
@ -412,7 +477,12 @@ namespace NavisworksTransport
|
||||
ModelIndex = reader["ModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["ModelIndex"]) : (int?)null,
|
||||
PathId = reader["PathId"] != DBNull.Value ? reader["PathId"].ToString() : null,
|
||||
DisplayName = reader["DisplayName"]?.ToString(),
|
||||
ObjectName = reader["ObjectName"]?.ToString()
|
||||
ObjectName = reader["ObjectName"]?.ToString(),
|
||||
Item1PosX = reader["Item1PosX"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosX"]) : (double?)null,
|
||||
Item1PosY = reader["Item1PosY"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosY"]) : (double?)null,
|
||||
Item1PosZ = reader["Item1PosZ"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosZ"]) : (double?)null,
|
||||
Item1YawRadians = reader["Item1YawRadians"] != DBNull.Value ? Convert.ToDouble(reader["Item1YawRadians"]) : (double?)null,
|
||||
HasPositionInfo = reader["HasPositionInfo"] != DBNull.Value && Convert.ToInt32(reader["HasPositionInfo"]) == 1
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -465,6 +535,16 @@ namespace NavisworksTransport
|
||||
Distance = 0.0,
|
||||
CreatedTime = DateTime.Now
|
||||
};
|
||||
|
||||
// 恢复碰撞时运动物体的位置和朝向(如果可用)
|
||||
if (obj.HasPositionInfo && obj.Item1PosX.HasValue && obj.Item1PosY.HasValue && obj.Item1PosZ.HasValue)
|
||||
{
|
||||
collisionResult.Item1Position = new Point3D(obj.Item1PosX.Value, obj.Item1PosY.Value, obj.Item1PosZ.Value);
|
||||
collisionResult.Item1YawRadians = obj.Item1YawRadians ?? 0.0;
|
||||
collisionResult.HasPositionInfo = true;
|
||||
|
||||
LogManager.Debug($"[加载碰撞结果] 恢复运动物体位置: ({obj.Item1PosX:F2}, {obj.Item1PosY:F2}, {obj.Item1PosZ:F2}), 朝向: {obj.Item1YawRadians:F2} rad");
|
||||
}
|
||||
|
||||
results.Add(collisionResult);
|
||||
}
|
||||
@ -676,6 +756,9 @@ namespace NavisworksTransport
|
||||
|
||||
LogManager.Info($"[分组测试] 智能去重: {validCollisions.Count} 个检测点 -> {groupedCollisions.Count} 个唯一碰撞对");
|
||||
|
||||
// 🔥 初始化确认碰撞位置字典
|
||||
_confirmedCollisionPositions.Clear();
|
||||
|
||||
// 缓存去重后的碰撞结果(每组取第一个)
|
||||
lock (_resultsLock)
|
||||
{
|
||||
@ -796,6 +879,18 @@ namespace NavisworksTransport
|
||||
confirmedCount++;
|
||||
skippedCount += (sortedCandidates.Count - 1 - i); // 记录跳过的数量
|
||||
|
||||
// 🔥 关键:记录确认碰撞的候选帧位置信息,供后续使用
|
||||
// 使用Item2作为key,因为最终去重也是按Item2合并的
|
||||
var confirmedPositionKey = GetCollisionObjectKey(candidate.Item1, candidate.Item2);
|
||||
_confirmedCollisionPositions[confirmedPositionKey] = new CollisionPositionInfo
|
||||
{
|
||||
Item1Position = candidate.Item1Position,
|
||||
Item2Position = candidate.Item2Position,
|
||||
Item1YawRadians = candidate.Item1YawRadians,
|
||||
HasPositionInfo = candidate.HasPositionInfo
|
||||
};
|
||||
LogManager.Debug($"[ClashDetective确认] 记录碰撞位置: {confirmedPositionKey}, 位置: ({candidate.Item1Position.X:F2}, {candidate.Item1Position.Y:F2}, {candidate.Item1Position.Z:F2})");
|
||||
|
||||
int subResultIndex = 1;
|
||||
// 🔥 优化:预获取运动物体名称,避免在循环中重复获取
|
||||
var animatedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animatedObject);
|
||||
@ -873,6 +968,10 @@ namespace NavisworksTransport
|
||||
originalItem2 = clashResult.Item2; // 回退到原始对象
|
||||
}
|
||||
|
||||
// 🔥 从确认碰撞位置字典中获取位置信息(ClashDetective实际验证通过的帧)
|
||||
var positionKey = GetCollisionObjectKey(animatedObject, originalItem2);
|
||||
_confirmedCollisionPositions.TryGetValue(positionKey, out var confirmedPosition);
|
||||
|
||||
var collisionResult = new CollisionResult
|
||||
{
|
||||
ClashGuid = clashResult.Guid,
|
||||
@ -882,7 +981,12 @@ namespace NavisworksTransport
|
||||
Item2 = originalItem2,
|
||||
Center = clashResult.Center,
|
||||
Distance = clashResult.Distance,
|
||||
CreatedTime = DateTime.Now
|
||||
CreatedTime = DateTime.Now,
|
||||
// 🔥 使用ClashDetective确认时的位置信息
|
||||
Item1Position = confirmedPosition?.Item1Position,
|
||||
Item2Position = confirmedPosition?.Item2Position,
|
||||
Item1YawRadians = confirmedPosition?.Item1YawRadians ?? 0,
|
||||
HasPositionInfo = confirmedPosition != null
|
||||
};
|
||||
clashResults.Add(collisionResult);
|
||||
}
|
||||
@ -895,7 +999,9 @@ namespace NavisworksTransport
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
LogManager.Info($"[最终去重] ClashDetective结果去重: {clashResults.Count} 个碰撞 -> {finalClashResults.Count} 个唯一碰撞对");
|
||||
// 🔥 日志:统计位置信息保留情况
|
||||
var withPositionCount = finalClashResults.Count(c => c.HasPositionInfo && c.Item1Position != null);
|
||||
LogManager.Info($"[最终去重] ClashDetective结果去重: {clashResults.Count} 个碰撞 -> {finalClashResults.Count} 个唯一碰撞对,其中 {withPositionCount} 个包含位置信息");
|
||||
|
||||
// 🔥 优化:在去重后为结果设置详细的 DisplayName
|
||||
// 这样只对保留的结果进行名称计算,避免在子碰撞结果上浪费性能
|
||||
@ -961,6 +1067,10 @@ namespace NavisworksTransport
|
||||
LogManager.Info($"=== 使用预计算碰撞数据创建ClashDetective测试(容差: {detectionGap}米)===");
|
||||
|
||||
LogManager.Info($"[预计算数据] 共有 {precomputedCollisions.Count} 个碰撞记录");
|
||||
|
||||
// 检查预计算碰撞结果是否包含位置信息
|
||||
var collisionsWithPosition = precomputedCollisions.Count(c => c.HasPositionInfo && c.Item1Position != null);
|
||||
LogManager.Info($"[预计算数据] 包含位置信息的碰撞: {collisionsWithPosition}/{precomputedCollisions.Count}");
|
||||
|
||||
// 直接使用所有预计算结果,只过滤有效对象(不去重)
|
||||
var collisionResults = precomputedCollisions
|
||||
@ -1884,9 +1994,10 @@ namespace NavisworksTransport
|
||||
public double Distance { get; set; }
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
// 位置信息用于恢复测试
|
||||
// 位置和朝向信息用于还原碰撞场景
|
||||
public Point3D Item1Position { get; set; }
|
||||
public Point3D Item2Position { get; set; }
|
||||
public double Item1YawRadians { get; set; } // 运动物体朝向(弧度)
|
||||
public bool HasPositionInfo { get; set; }
|
||||
|
||||
// IEquatable<CollisionResult> 实现:基于碰撞对象进行去重
|
||||
|
||||
@ -195,6 +195,7 @@ namespace NavisworksTransport
|
||||
");
|
||||
|
||||
// 7. ClashDetective碰撞对象表
|
||||
// 扩展字段:记录碰撞时运动物体的位置和朝向,用于还原碰撞场景
|
||||
ExecuteNonQuery(@"
|
||||
CREATE TABLE IF NOT EXISTS ClashDetectiveCollisionObjects (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@ -203,6 +204,12 @@ namespace NavisworksTransport
|
||||
PathId TEXT,
|
||||
DisplayName TEXT,
|
||||
ObjectName TEXT,
|
||||
-- 碰撞时运动物体的位置和朝向(用于还原碰撞场景)
|
||||
Item1PosX REAL,
|
||||
Item1PosY REAL,
|
||||
Item1PosZ REAL,
|
||||
Item1YawRadians REAL,
|
||||
HasPositionInfo INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(ResultId) REFERENCES ClashDetectiveResults(Id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
@ -227,7 +234,7 @@ namespace NavisworksTransport
|
||||
// 9. 设置数据库版本(SQLite内置user_version)
|
||||
// 版本号格式:主版本*10000 + 次版本*100 + 修订号
|
||||
// 例如:2.1.3 = 20103
|
||||
ExecuteNonQuery("PRAGMA user_version = 20100"); // v2.1.0
|
||||
ExecuteNonQuery("PRAGMA user_version = 20101"); // v2.1.1 - 增加碰撞时运动物体位置和朝向字段
|
||||
|
||||
// 创建索引
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)");
|
||||
@ -1074,8 +1081,10 @@ namespace NavisworksTransport
|
||||
{
|
||||
var sql = @"
|
||||
INSERT INTO ClashDetectiveCollisionObjects
|
||||
(ResultId, ModelIndex, PathId, DisplayName, ObjectName)
|
||||
VALUES (@resultId, @modelIndex, @pathId, @displayName, @objectName)
|
||||
(ResultId, ModelIndex, PathId, DisplayName, ObjectName,
|
||||
Item1PosX, Item1PosY, Item1PosZ, Item1YawRadians, HasPositionInfo)
|
||||
VALUES (@resultId, @modelIndex, @pathId, @displayName, @objectName,
|
||||
@item1PosX, @item1PosY, @item1PosZ, @item1YawRadians, @hasPositionInfo)
|
||||
";
|
||||
|
||||
foreach (var obj in objects)
|
||||
@ -1087,6 +1096,11 @@ namespace NavisworksTransport
|
||||
cmd.Parameters.AddWithValue("@pathId", obj.PathId ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@displayName", obj.DisplayName ?? "");
|
||||
cmd.Parameters.AddWithValue("@objectName", obj.ObjectName ?? "");
|
||||
cmd.Parameters.AddWithValue("@item1PosX", obj.Item1PosX.HasValue ? (object)obj.Item1PosX.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1PosY", obj.Item1PosY.HasValue ? (object)obj.Item1PosY.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1PosZ", obj.Item1PosZ.HasValue ? (object)obj.Item1PosZ.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1YawRadians", obj.Item1YawRadians.HasValue ? (object)obj.Item1YawRadians.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@hasPositionInfo", obj.HasPositionInfo ? 1 : 0);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
@ -1112,7 +1126,8 @@ namespace NavisworksTransport
|
||||
try
|
||||
{
|
||||
var sql = @"
|
||||
SELECT Id, ResultId, ModelIndex, PathId, DisplayName, ObjectName
|
||||
SELECT Id, ResultId, ModelIndex, PathId, DisplayName, ObjectName,
|
||||
Item1PosX, Item1PosY, Item1PosZ, Item1YawRadians, HasPositionInfo
|
||||
FROM ClashDetectiveCollisionObjects
|
||||
WHERE ResultId = @resultId
|
||||
";
|
||||
@ -1131,7 +1146,12 @@ namespace NavisworksTransport
|
||||
ModelIndex = reader["ModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["ModelIndex"]) : (int?)null,
|
||||
PathId = reader["PathId"] != DBNull.Value ? reader["PathId"].ToString() : null,
|
||||
DisplayName = reader["DisplayName"]?.ToString(),
|
||||
ObjectName = reader["ObjectName"]?.ToString()
|
||||
ObjectName = reader["ObjectName"]?.ToString(),
|
||||
Item1PosX = reader["Item1PosX"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosX"]) : (double?)null,
|
||||
Item1PosY = reader["Item1PosY"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosY"]) : (double?)null,
|
||||
Item1PosZ = reader["Item1PosZ"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosZ"]) : (double?)null,
|
||||
Item1YawRadians = reader["Item1YawRadians"] != DBNull.Value ? Convert.ToDouble(reader["Item1YawRadians"]) : (double?)null,
|
||||
HasPositionInfo = reader["HasPositionInfo"] != DBNull.Value && Convert.ToInt32(reader["HasPositionInfo"]) == 1
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -2598,6 +2618,7 @@ namespace NavisworksTransport
|
||||
|
||||
/// <summary>
|
||||
/// ClashDetective碰撞对象数据模型
|
||||
/// 包含碰撞时运动物体的位置和朝向信息,用于还原碰撞场景
|
||||
/// </summary>
|
||||
public class ClashDetectiveCollisionObjectRecord
|
||||
{
|
||||
@ -2607,6 +2628,13 @@ namespace NavisworksTransport
|
||||
public string PathId { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string ObjectName { get; set; }
|
||||
|
||||
// 碰撞时运动物体的位置和朝向(用于还原碰撞场景)
|
||||
public double? Item1PosX { get; set; }
|
||||
public double? Item1PosY { get; set; }
|
||||
public double? Item1PosZ { get; set; }
|
||||
public double? Item1YawRadians { get; set; }
|
||||
public bool HasPositionInfo { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -89,6 +89,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private double _detectionTolerance;
|
||||
private CollisionReportResult _currentReport;
|
||||
private ObservableCollection<ExcludedObjectRecord> _excludedObjects;
|
||||
|
||||
// 保存运动物体原始状态,用于碰撞查看后恢复
|
||||
private ModelItemTransformHelper.ObjectStateSnapshot _savedAnimatedObjectState;
|
||||
private ModelItem _currentAnimatedObject;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -637,10 +641,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
collisionItems.Add(item);
|
||||
}
|
||||
|
||||
// 更新UI集合
|
||||
// 更新UI集合(按序号排序保持顺序)
|
||||
SafeExecute(() =>
|
||||
{
|
||||
foreach (var item in collisionItems.OrderBy(i => i.Title))
|
||||
foreach (var item in collisionItems.OrderBy(i => i.Index))
|
||||
AnimationCollisions.Add(item);
|
||||
|
||||
}, "更新碰撞报告项目列表", true);
|
||||
@ -1229,6 +1233,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
/// <summary>
|
||||
/// 高亮并聚焦到碰撞项目中的被撞对象(Item2)
|
||||
/// 同时将运动物体(Item1)移动到碰撞时的位置和朝向,还原碰撞场景
|
||||
/// </summary>
|
||||
public void HighlightAndFocusCollisionItem(CollisionReportItem collisionItem)
|
||||
{
|
||||
@ -1259,12 +1264,92 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
LogManager.Info($"聚焦到被撞对象: {collisionItem.Title}");
|
||||
}
|
||||
|
||||
// 还原运动物体到碰撞时的位置和朝向
|
||||
LogManager.Debug($"[碰撞详情点击] Item1={collisionData.Item1?.DisplayName}, HasPositionInfo={collisionData.HasPositionInfo}, Item1Position={collisionData.Item1Position}");
|
||||
if (collisionData.Item1 != null && collisionData.HasPositionInfo && collisionData.Item1Position != null)
|
||||
{
|
||||
// 首次查看碰撞时保存运动物体状态
|
||||
if (_savedAnimatedObjectState == null || _currentAnimatedObject != collisionData.Item1)
|
||||
{
|
||||
_currentAnimatedObject = collisionData.Item1;
|
||||
_savedAnimatedObjectState = ModelItemTransformHelper.SaveObjectState(collisionData.Item1);
|
||||
LogManager.Info($"[碰撞查看] 已保存运动物体原始状态,供后续恢复使用");
|
||||
}
|
||||
|
||||
RestoreAnimatedObjectToCollisionPosition(collisionData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning($"[碰撞详情点击] 无法还原运动物体位置: Item1={(collisionData.Item1 != null ? "有" : "无")}, HasPositionInfo={collisionData.HasPositionInfo}, Item1Position={(collisionData.Item1Position != null ? "有" : "无")}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"聚焦到被撞对象失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将运动物体还原到碰撞时的位置和朝向
|
||||
/// 使用ModelItemTransformHelper.MoveItemToPositionAndYaw从CAD原始状态精确定位
|
||||
/// </summary>
|
||||
private void RestoreAnimatedObjectToCollisionPosition(CollisionResult collisionData)
|
||||
{
|
||||
try
|
||||
{
|
||||
var animatedObject = collisionData.Item1;
|
||||
var targetPosition = collisionData.Item1Position;
|
||||
var targetYaw = collisionData.Item1YawRadians;
|
||||
|
||||
if (animatedObject == null || targetPosition == null)
|
||||
{
|
||||
LogManager.Warning("[还原碰撞位置] 运动物体或位置信息为空");
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算目标底面位置(Item1Position存储的是包围盒中心,需要转换为底面)
|
||||
var currentBounds = animatedObject.BoundingBox();
|
||||
double halfHeight = (currentBounds.Max.Z - currentBounds.Min.Z) / 2.0;
|
||||
var targetGroundPosition = new Point3D(
|
||||
targetPosition.X,
|
||||
targetPosition.Y,
|
||||
targetPosition.Z - halfHeight
|
||||
);
|
||||
|
||||
// 使用工具方法从CAD原始状态移动到目标位置
|
||||
ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, targetYaw);
|
||||
|
||||
LogManager.Info($"[还原碰撞位置] 运动物体已移动到碰撞位置: ({targetGroundPosition.X:F2}, {targetGroundPosition.Y:F2}, {targetGroundPosition.Z:F2}), 朝向: {targetYaw * 180 / Math.PI:F2}°");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[还原碰撞位置] 失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复运动物体到原始状态(查看碰撞前保存的状态)
|
||||
/// </summary>
|
||||
public void RestoreAnimatedObjectToOriginalState()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_currentAnimatedObject != null && _savedAnimatedObjectState != null)
|
||||
{
|
||||
ModelItemTransformHelper.RestoreObjectState(_currentAnimatedObject, _savedAnimatedObjectState);
|
||||
LogManager.Info("[碰撞查看] 运动物体已恢复到原始状态");
|
||||
|
||||
// 清除保存的状态
|
||||
_savedAnimatedObjectState = null;
|
||||
_currentAnimatedObject = null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[恢复运动物体状态] 失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -134,6 +134,12 @@ namespace NavisworksTransport.UI.WPF.Views
|
||||
{
|
||||
try
|
||||
{
|
||||
// 恢复运动物体到原始状态(查看碰撞前保存的状态)
|
||||
if (_viewModel != null)
|
||||
{
|
||||
_viewModel.RestoreAnimatedObjectToOriginalState();
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
if (_viewModel != null)
|
||||
{
|
||||
|
||||
@ -101,5 +101,152 @@ namespace NavisworksTransport.Utils
|
||||
var restoreTransform = Transform3D.CreateTranslation(restoreOffset);
|
||||
doc.Models.OverridePermanentTransform(modelItems, restoreTransform, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将物体从当前位置移动到指定位置和朝向(增量模式)
|
||||
/// 适用于碰撞位置还原等场景
|
||||
/// </summary>
|
||||
/// <param name="item">要移动的物体</param>
|
||||
/// <param name="targetPosition">目标位置(地面位置,即包围盒底面)</param>
|
||||
/// <param name="targetYaw">目标朝向(弧度,绕Z轴)</param>
|
||||
public static void MoveItemToPositionAndYaw(ModelItem item, Point3D targetPosition, double targetYaw)
|
||||
{
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { item };
|
||||
|
||||
// 获取当前状态
|
||||
var currentBounds = item.BoundingBox();
|
||||
var currentGroundPos = new Point3D(
|
||||
currentBounds.Center.X,
|
||||
currentBounds.Center.Y,
|
||||
currentBounds.Min.Z
|
||||
);
|
||||
var currentYaw = GetYawFromTransform(item.Transform);
|
||||
|
||||
// 计算位置和旋转增量
|
||||
var deltaPos = new Vector3D(
|
||||
targetPosition.X - currentGroundPos.X,
|
||||
targetPosition.Y - currentGroundPos.Y,
|
||||
targetPosition.Z - currentGroundPos.Z
|
||||
);
|
||||
double deltaYaw = targetYaw - currentYaw;
|
||||
|
||||
// 应用增量变换(和ClashDetective中的做法一致)
|
||||
Transform3D transform;
|
||||
if (Math.Abs(deltaYaw) > 0.001)
|
||||
{
|
||||
// 有旋转:需要补偿绕原点旋转带来的位置偏移
|
||||
double cos = Math.Cos(deltaYaw);
|
||||
double sin = Math.Sin(deltaYaw);
|
||||
double rotatedX = currentGroundPos.X * cos - currentGroundPos.Y * sin;
|
||||
double rotatedY = currentGroundPos.X * sin + currentGroundPos.Y * cos;
|
||||
|
||||
var compensatedTranslation = new Vector3D(
|
||||
targetPosition.X - rotatedX,
|
||||
targetPosition.Y - rotatedY,
|
||||
deltaPos.Z
|
||||
);
|
||||
|
||||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||||
var components = identity.Factor();
|
||||
components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
|
||||
components.Translation = compensatedTranslation;
|
||||
transform = components.Combine();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 纯平移
|
||||
transform = Transform3D.CreateTranslation(deltaPos);
|
||||
}
|
||||
|
||||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物体状态快照,用于保存和恢复
|
||||
/// </summary>
|
||||
public class ObjectStateSnapshot
|
||||
{
|
||||
public Point3D Position { get; set; }
|
||||
public double YawRadians { get; set; }
|
||||
public Transform3D Transform { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存物体当前状态
|
||||
/// </summary>
|
||||
public static ObjectStateSnapshot SaveObjectState(ModelItem item)
|
||||
{
|
||||
if (item == null) return null;
|
||||
|
||||
var bounds = item.BoundingBox();
|
||||
return new ObjectStateSnapshot
|
||||
{
|
||||
Position = new Point3D(bounds.Center.X, bounds.Center.Y, bounds.Min.Z),
|
||||
YawRadians = GetYawFromTransform(item.Transform),
|
||||
Transform = item.Transform
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从状态快照恢复物体位置
|
||||
/// 先回到CAD原始位置,再移动到保存的位置
|
||||
/// </summary>
|
||||
public static void RestoreObjectState(ModelItem item, ObjectStateSnapshot state)
|
||||
{
|
||||
if (item == null || state == null) return;
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { item };
|
||||
|
||||
// 1. 先回到CAD原始位置
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
// 2. 从CAD原始位置移动到保存的位置
|
||||
// 获取CAD原始状态
|
||||
var originalBounds = item.BoundingBox();
|
||||
var originalGroundPos = new Point3D(
|
||||
originalBounds.Center.X,
|
||||
originalBounds.Center.Y,
|
||||
originalBounds.Min.Z
|
||||
);
|
||||
var originalYaw = GetYawFromTransform(item.Transform);
|
||||
|
||||
// 计算到保存位置的增量
|
||||
var deltaPos = new Vector3D(
|
||||
state.Position.X - originalGroundPos.X,
|
||||
state.Position.Y - originalGroundPos.Y,
|
||||
state.Position.Z - originalGroundPos.Z
|
||||
);
|
||||
double deltaYaw = state.YawRadians - originalYaw;
|
||||
|
||||
// 应用增量变换
|
||||
Transform3D transform;
|
||||
if (Math.Abs(deltaYaw) > 0.001)
|
||||
{
|
||||
double cos = Math.Cos(deltaYaw);
|
||||
double sin = Math.Sin(deltaYaw);
|
||||
double rotatedX = originalGroundPos.X * cos - originalGroundPos.Y * sin;
|
||||
double rotatedY = originalGroundPos.X * sin + originalGroundPos.Y * cos;
|
||||
|
||||
var compensatedTranslation = new Vector3D(
|
||||
state.Position.X - rotatedX,
|
||||
state.Position.Y - rotatedY,
|
||||
deltaPos.Z
|
||||
);
|
||||
|
||||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||||
var components = identity.Factor();
|
||||
components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
|
||||
components.Translation = compensatedTranslation;
|
||||
transform = components.Combine();
|
||||
}
|
||||
else
|
||||
{
|
||||
transform = Transform3D.CreateTranslation(deltaPos);
|
||||
}
|
||||
|
||||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user