给碰撞构件清单,也加上碰撞对聚焦

This commit is contained in:
tian 2026-02-17 11:37:45 +08:00
parent fac4d3e2e3
commit ac6d3b4017
6 changed files with 252 additions and 22 deletions

View File

@ -11,8 +11,8 @@
### [2026/2/8]
1. [x] (功能)增加预计算结果分析和排除建议
2. [ ] (优化)考虑在碰撞报告中,给每一个碰撞元素自动建立截图
3. [ ] (优化)考虑是否给截图按碰撞记录组织成子文件夹
2. [x] (优化)在碰撞报告中,给每一个碰撞元素自动建立截图
3. [x] (优化)截图按碰撞记录组织成子文件夹
4. [ ] 优化研究如何利用ClashDetective的多线程支持打开多线程
5. [x] 优化给每个检测到的碰撞对象增加单独视角45度右上方
6. [x] (优化)点击碰撞列表的碰撞详情时,要把运动物体移动到位,查看碰撞情况

View File

@ -851,7 +851,9 @@ namespace NavisworksTransport
{
var sql = @"
SELECT cdr.Id, cdr.TestName, cdr.RouteId, pr.Name AS PathName, cdr.TestTime, cdr.CollisionCount, cdr.AnimationCollisionCount,
cdr.FrameRate, cdr.Duration, cdr.DetectionGap, cdr.AnimatedObjectName, cdr.CreatedAt
cdr.FrameRate, cdr.Duration, cdr.DetectionGap, cdr.AnimatedObjectName, cdr.IsVirtualObject,
cdr.ObjectModelIndex, cdr.ObjectPathId, cdr.VirtualObjectLength, cdr.VirtualObjectWidth, cdr.VirtualObjectHeight,
cdr.CreatedAt
FROM ClashDetectiveResults cdr
INNER JOIN PathRoutes pr ON cdr.RouteId = pr.Id
WHERE pr.Name = @pathName
@ -878,6 +880,12 @@ namespace NavisworksTransport
Duration = Convert.ToDouble(reader["Duration"]),
DetectionGap = Convert.ToDouble(reader["DetectionGap"]),
AnimatedObjectName = reader["AnimatedObjectName"].ToString(),
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 = Convert.ToDateTime(reader["CreatedAt"])
});
}

View File

@ -254,6 +254,22 @@ namespace NavisworksTransport.Core
LogManager.Info("已重置虚拟物体变换");
}
/// <summary>
/// 重新应用当前记录的缩放用于ResetPermanentTransform后恢复缩放
/// </summary>
public void ReapplyCurrentScale()
{
if (_currentLengthMeters > 0 && _currentWidthMeters > 0 && _currentHeightMeters > 0)
{
LogManager.Info($"重新应用虚拟物体缩放: {_currentLengthMeters:F2}m × {_currentWidthMeters:F2}m × {_currentHeightMeters:F2}m");
ScaleVirtualObject(_currentLengthMeters, _currentWidthMeters, _currentHeightMeters);
}
else
{
LogManager.Warning("无法重新应用缩放:当前尺寸记录无效");
}
}
/// <summary>
/// 缩放虚拟物体到目标尺寸
/// 使用DocumentModels.SetModelUnitsAndTransform方法进行模型级缩放

View File

@ -177,6 +177,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ModelItem ModelItem { get; set; }
public ICommand HighlightCommand { get; set; }
public ICommand ClearHighlightCommand { get; set; }
// 碰撞位置信息(用于还原碰撞场景)
public Point3D Item1Position { get; set; }
public double Item1YawRadians { get; set; }
public bool HasPositionInfo { get; set; }
}
/// <summary>
@ -197,7 +202,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
public ClashDetectiveResultRecord Record { get; }
public string TestTimeDisplay => Record.TestTime.ToString("MM-dd HH:mm:ss");
// 运动物体(从碰撞历史数据库中加载)
public ModelItem AnimatedObject { get; set; }
// 虚拟物体尺寸当IsVirtualObject=true时使用
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
public string TestTimeDisplay => Record.TestTime.ToString("MM-dd HH:mm:ss");
public string CollisionCountDisplay => $"{Record.CollisionCount}个";
public ICommand ViewCommand { get; }
public ICommand ReportCommand { get; }
@ -286,6 +300,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private readonly ClashDetectiveIntegration _clashIntegration;
private readonly UIStateManager _uiStateManager;
private PathPlanningManager _pathPlanningManager;
// 碰撞构件查看时保存的动画物体状态(用于恢复)
private ModelItem _savedAnimatedObjectForCollision;
private ModelItemTransformHelper.ObjectStateSnapshot _savedAnimatedObjectStateForCollision;
// 动画参数相关字段(从配置初始化)
private ObservableCollection<int> _availableFrameRates;
@ -831,6 +849,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
if (_selectedClashDetectiveResult != value)
{
// 切换前恢复动画物体状态
if (_selectedClashDetectiveResult != null)
{
RestoreAnimatedObjectFromCollisionView();
}
_selectedClashDetectiveResult = value;
OnPropertyChanged();
}
@ -2372,20 +2396,50 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
/// <summary>
/// 聚焦到碰撞构件
/// 聚焦到碰撞构件,并移动动画物体到碰撞位置
/// </summary>
public void FocusOnCollisionObject(CollisionObjectViewModel collisionObject)
{
if (collisionObject?.ModelItem == null) return;
// 从历史记录中获取运动物体
var animatedObject = SelectedClashDetectiveResult?.AnimatedObject;
if (animatedObject == null)
{
LogManager.Warning($"[碰撞构件] 未找到运动物体,仅聚焦到被撞对象: {collisionObject.DisplayName}");
// 仅聚焦,不移动物体
CollisionSceneHelper.MoveToCollisionAndFocus(
collisionObject.ModelItem,
null,
collisionObject.Item1Position,
collisionObject.Item1YawRadians,
false);
return;
}
try
{
// 先高亮该对象
var items = new List<ModelItem> { collisionObject.ModelItem };
ModelHighlightHelper.HighlightItems(ModelHighlightHelper.ClashDetectiveResultsCategory, items);
// 首次查看时保存动画物体状态
if (_savedAnimatedObjectStateForCollision == null || _savedAnimatedObjectForCollision != animatedObject)
{
_savedAnimatedObjectForCollision = animatedObject;
_savedAnimatedObjectStateForCollision = CollisionSceneHelper.SaveAnimatedObjectState(animatedObject);
}
// 聚焦到对象斜上方45度视角
ViewpointHelper.FocusOnModelItem(collisionObject.ModelItem, viewAngleDegrees: 60.0, targetViewRatio: 0.25);
// 移动动画物体到碰撞位置并聚焦
CollisionSceneHelper.MoveToCollisionAndFocus(
collisionObject.ModelItem,
animatedObject,
collisionObject.Item1Position,
collisionObject.Item1YawRadians,
collisionObject.HasPositionInfo);
// 🔥 关键如果是虚拟物体重新应用缩放因为MoveItemToPositionAndYaw会重置变换
if (VirtualObjectManager.Instance.IsVirtualObjectActive &&
VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject)
{
VirtualObjectManager.Instance.ReapplyCurrentScale();
}
UpdateMainStatus($"已聚焦到碰撞构件: {collisionObject.DisplayName}");
LogManager.Info($"聚焦到碰撞构件: {collisionObject.DisplayName}");
@ -2396,6 +2450,28 @@ namespace NavisworksTransport.UI.WPF.ViewModels
UpdateMainStatus($"聚焦失败: {ex.Message}");
}
}
/// <summary>
/// 恢复动画物体到原始状态(失去焦点或切换历史记录时调用)
/// </summary>
public void RestoreAnimatedObjectFromCollisionView()
{
if (_savedAnimatedObjectForCollision != null && _savedAnimatedObjectStateForCollision != null)
{
CollisionSceneHelper.RestoreAnimatedObjectState(_savedAnimatedObjectForCollision, _savedAnimatedObjectStateForCollision);
LogManager.Info($"[碰撞构件] 运动物体已恢复到原始状态: {_savedAnimatedObjectForCollision.DisplayName}");
}
// 隐藏虚拟物体(如果之前显示的是虚拟物体)
if (VirtualObjectManager.Instance.IsVirtualObjectActive)
{
VirtualObjectManager.Instance.HideVirtualObject();
LogManager.Info("[碰撞构件] 虚拟物体已隐藏");
}
_savedAnimatedObjectStateForCollision = null;
_savedAnimatedObjectForCollision = null;
}
#endregion
@ -2987,6 +3063,48 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
var resultViewModel = new ClashDetectiveResultViewModel(record, RefreshClashDetectiveResultsList);
// 加载运动物体Item1
if (record.IsVirtualObject)
{
// 使用虚拟物体,设置历史记录中的尺寸(数据库中是模型单位)
resultViewModel.VirtualObjectLength = record.VirtualObjectLength;
resultViewModel.VirtualObjectWidth = record.VirtualObjectWidth;
resultViewModel.VirtualObjectHeight = record.VirtualObjectHeight;
// 显示对应尺寸的虚拟物体ShowVirtualObject需要米单位
try
{
double unitsToMeters = UnitsConverter.GetUnitsToMetersConversionFactor(Autodesk.Navisworks.Api.Application.ActiveDocument.Units);
VirtualObjectManager.Instance.ShowVirtualObject(
record.VirtualObjectLength * unitsToMeters,
record.VirtualObjectWidth * unitsToMeters,
record.VirtualObjectHeight * unitsToMeters);
resultViewModel.AnimatedObject = VirtualObjectManager.Instance.CurrentVirtualObject;
LogManager.Info($"[碰撞历史] 使用虚拟物体(尺寸:{record.VirtualObjectLength:F2}x{record.VirtualObjectWidth:F2}x{record.VirtualObjectHeight:F2}): {resultViewModel.AnimatedObject?.DisplayName ?? ""} for TestName={record.TestName}");
}
catch (Exception ex)
{
LogManager.Warning($"[碰撞历史] 显示虚拟物体失败: {ex.Message}");
}
}
else if (record.ObjectModelIndex.HasValue && !string.IsNullOrEmpty(record.ObjectPathId))
{
try
{
var animatedObjectPathId = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
{
ModelIndex = record.ObjectModelIndex.Value,
PathId = record.ObjectPathId
};
resultViewModel.AnimatedObject = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.ResolvePathId(animatedObjectPathId);
LogManager.Info($"[碰撞历史] 已加载运动物体: {resultViewModel.AnimatedObject?.DisplayName ?? ""} for TestName={record.TestName}");
}
catch (Exception ex)
{
LogManager.Warning($"[碰撞历史] 无法加载运动物体: ModelIndex={record.ObjectModelIndex}, PathId={record.ObjectPathId}, 错误: {ex.Message}");
}
}
// 同时加载该结果的碰撞构件清单
var collisionObjectRecords = pathDatabase.GetClashDetectiveCollisionObjects(record.Id);
if (collisionObjectRecords != null)
@ -3033,7 +3151,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ModelIndex = objRecord.ModelIndex,
PathId = objRecord.PathId,
ModelItem = modelItem,
HighlightCommand = new RelayCommand(() => resultViewModel.ExecuteHighlightCollisionObject(objViewModel))
HighlightCommand = new RelayCommand(() => resultViewModel.ExecuteHighlightCollisionObject(objViewModel)),
// 填充碰撞位置信息
Item1Position = objRecord.HasPositionInfo && objRecord.Item1PosX.HasValue ?
new Point3D(objRecord.Item1PosX.Value, objRecord.Item1PosY.Value, objRecord.Item1PosZ.Value) : null,
Item1YawRadians = objRecord.Item1YawRadians ?? 0,
HasPositionInfo = objRecord.HasPositionInfo
};
resultViewModel.CollisionObjects.Add(objViewModel);
}

View File

@ -19,12 +19,23 @@ namespace NavisworksTransport.Utils
public static void MoveToCollisionAndFocus(CollisionResult collision, ModelItem animatedObject)
{
if (collision == null) return;
MoveToCollisionAndFocus(collision.Item2 ?? collision.Item1, animatedObject,
collision.Item1Position, collision.Item1YawRadians, collision.HasPositionInfo);
}
/// <summary>
/// 将动画物体移动到碰撞位置并聚焦到碰撞场景(通用版本)
/// </summary>
/// <param name="hitObject">被撞对象</param>
/// <param name="animatedObject">动画物体(运动物体)</param>
/// <param name="item1Position">碰撞位置</param>
/// <param name="item1YawRadians">碰撞朝向</param>
/// <param name="hasPositionInfo">是否有位置信息</param>
public static void MoveToCollisionAndFocus(ModelItem hitObject, ModelItem animatedObject,
Point3D item1Position, double item1YawRadians, bool hasPositionInfo)
{
try
{
// 获取被撞对象Item2是运动物体碰撞的静态对象
var hitObject = collision.Item2 ?? collision.Item1;
if (hitObject != null)
{
// 清除之前的高亮
@ -40,25 +51,24 @@ namespace NavisworksTransport.Utils
}
// 移动动画物体到碰撞位置
if (animatedObject != null && collision.Item1 == animatedObject && collision.HasPositionInfo && collision.Item1Position != null)
if (animatedObject != null && hasPositionInfo && item1Position != null)
{
// 计算目标底面位置Item1Position存储的是包围盒中心需要转换为底面
var bounds = animatedObject.BoundingBox();
double halfHeight = (bounds.Max.Z - bounds.Min.Z) / 2.0;
var targetGroundPosition = new Point3D(
collision.Item1Position.X,
collision.Item1Position.Y,
collision.Item1Position.Z - halfHeight
item1Position.X,
item1Position.Y,
item1Position.Z - halfHeight
);
// 使用工具方法从CAD原始状态移动到目标位置
ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, collision.Item1YawRadians);
ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, item1YawRadians);
LogManager.Info(string.Format("运动物体已移动到碰撞位置: ({0:F2}, {1:F2}, {2:F2}), 朝向: {3:F2}°",
targetGroundPosition.X, targetGroundPosition.Y, targetGroundPosition.Z,
collision.Item1YawRadians * 180 / Math.PI));
item1YawRadians * 180 / Math.PI));
}
}
catch (Exception ex)
{

View File

@ -165,6 +165,79 @@ namespace NavisworksTransport.Utils
doc.Models.OverridePermanentTransform(modelItems, transform, false);
}
/// <summary>
/// 将物体移动到指定位置和朝向,同时保持缩放比例
/// 专为虚拟物体设计,避免缩放被覆盖
/// </summary>
/// <param name="item">要移动的物体</param>
/// <param name="targetPosition">目标位置(地面位置)</param>
/// <param name="targetYaw">目标朝向(弧度)</param>
/// <param name="scaleX">X方向缩放</param>
/// <param name="scaleY">Y方向缩放</param>
/// <param name="scaleZ">Z方向缩放</param>
public static void MoveItemToPositionAndYawWithScale(ModelItem item, Point3D targetPosition, double targetYaw,
double scaleX, double scaleY, double scaleZ)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { item };
// 重置到CAD原始状态
doc.Models.ResetPermanentTransform(modelItems);
// 获取CAD原始状态
var originalBounds = item.BoundingBox();
var originalGroundPos = new Point3D(
originalBounds.Center.X,
originalBounds.Center.Y,
originalBounds.Min.Z
);
var originalYaw = GetYawFromTransform(item.Transform);
// 计算从CAD原始位置到目标位置的增量
var deltaPos = new Vector3D(
targetPosition.X - originalGroundPos.X,
targetPosition.Y - originalGroundPos.Y,
targetPosition.Z - originalGroundPos.Z
);
double deltaYaw = targetYaw - originalYaw;
// 构建变换组件
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
var components = identity.Factor();
// 应用缩放
components.Scale = new Vector3D(scaleX, scaleY, scaleZ);
// 应用旋转
if (Math.Abs(deltaYaw) > 0.001)
{
components.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
}
// 计算平移(考虑旋转带来的位置偏移)
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;
components.Translation = new Vector3D(
targetPosition.X - rotatedX,
targetPosition.Y - rotatedY,
deltaPos.Z
);
}
else
{
components.Translation = deltaPos;
}
// 应用组合变换
Transform3D transform = components.Combine();
doc.Models.OverridePermanentTransform(modelItems, transform, false);
}
/// <summary>
/// 物体状态快照,用于保存和恢复
/// </summary>