diff --git a/src/Commands/GenerateCollisionReportCommand.cs b/src/Commands/GenerateCollisionReportCommand.cs
index c14fe60..afda48c 100644
--- a/src/Commands/GenerateCollisionReportCommand.cs
+++ b/src/Commands/GenerateCollisionReportCommand.cs
@@ -345,6 +345,22 @@ namespace NavisworksTransport.Commands
{
UpdateProgress(92, "生成默认路径截图...");
+ // 记录截图前虚拟物体实际位置和朝向
+ var virtualObject = VirtualObjectManager.Instance.CurrentVirtualObject;
+ if (virtualObject != null)
+ {
+ var bounds = virtualObject.BoundingBox();
+ var actualYaw = ModelItemTransformHelper.GetYawFromTransform(virtualObject.Transform);
+ var pam = PathAnimationManager.GetInstance();
+ LogManager.Info(string.Format("[默认截图前] 虚拟物体实际位置: ({0:F2},{1:F2},{2:F2}), 实际朝向: {3:F2}°, PAM记录朝向: {4:F2}°",
+ bounds.Center.X, bounds.Center.Y, bounds.Min.Z,
+ actualYaw * 180 / Math.PI, pam.CurrentYaw * 180 / Math.PI));
+ }
+ else
+ {
+ LogManager.Warning("[默认截图前] 无法获取虚拟物体");
+ }
+
// 获取当前视窗尺寸
var generator = new NavigationMapGenerator();
var viewportSize = generator.GetCurrentViewportSize();
@@ -359,6 +375,18 @@ namespace NavisworksTransport.Commands
System.Drawing.Imaging.ImageFormat.Jpeg,
"overview"
);
+
+ // 记录截图后虚拟物体实际位置和朝向
+ virtualObject = VirtualObjectManager.Instance.CurrentVirtualObject;
+ if (virtualObject != null)
+ {
+ var bounds = virtualObject.BoundingBox();
+ var actualYaw = ModelItemTransformHelper.GetYawFromTransform(virtualObject.Transform);
+ var pam = PathAnimationManager.GetInstance();
+ LogManager.Info(string.Format("[默认截图后] 虚拟物体实际位置: ({0:F2},{1:F2},{2:F2}), 实际朝向: {3:F2}°, PAM记录朝向: {4:F2}°",
+ bounds.Center.X, bounds.Center.Y, bounds.Min.Z,
+ actualYaw * 180 / Math.PI, pam.CurrentYaw * 180 / Math.PI));
+ }
if (screenshotPath != null)
{
diff --git a/src/Core/Collision/ClashDetectiveIntegration.cs b/src/Core/Collision/ClashDetectiveIntegration.cs
index da231a6..368b280 100644
--- a/src/Core/Collision/ClashDetectiveIntegration.cs
+++ b/src/Core/Collision/ClashDetectiveIntegration.cs
@@ -412,15 +412,13 @@ namespace NavisworksTransport
ModelItem objectObject = null;
if (testInfo.IsVirtualObject)
{
- // 显示虚拟物体
+ // 创建虚拟物体(保留现有变换如果尺寸相同)
var modelToMeters = UnitsConverter.GetUnitsToMetersConversionFactor();
- VirtualObjectManager.Instance.ShowVirtualObject(
+ objectObject = VirtualObjectManager.Instance.CreateVirtualObject(
testInfo.VirtualObjectLength * modelToMeters,
testInfo.VirtualObjectWidth * modelToMeters,
testInfo.VirtualObjectHeight * modelToMeters
- );
- objectObject = VirtualObjectManager.Instance.CurrentVirtualObject
- ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 显示虚拟物体失败");
+ ) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 获取虚拟物体失败");
}
else if (testInfo.ObjectModelIndex.HasValue && !string.IsNullOrEmpty(testInfo.ObjectPathId))
{
@@ -446,8 +444,10 @@ namespace NavisworksTransport
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法重建物体对象: IsVirtualObject={testInfo.IsVirtualObject}, ObjectModelIndex={testInfo.ObjectModelIndex}, ObjectPathId={testInfo.ObjectPathId}");
}
- // 如果是虚拟物体,将其移动到路径起点
- if (testInfo.IsVirtualObject && !string.IsNullOrEmpty(testInfo.RouteId))
+ // 如果是虚拟物体且是新创建的(不是已存在的),将其移动到路径起点
+ // 注意:如果虚拟物体已存在(动画刚结束),保持当前位置(终点),不要移动到起点
+ if (testInfo.IsVirtualObject && !string.IsNullOrEmpty(testInfo.RouteId) &&
+ !VirtualObjectManager.Instance.IsVirtualObjectActive)
{
try
{
@@ -471,6 +471,10 @@ namespace NavisworksTransport
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 移动虚拟物体到起点失败: {ex.Message}");
}
}
+ else if (testInfo.IsVirtualObject && VirtualObjectManager.Instance.IsVirtualObjectActive)
+ {
+ LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟物体已存在,保持当前位置(终点),不移动到起点");
+ }
// 3. 从数据库读取被撞物体信息(包含碰撞时运动物体的位置和朝向)
var collisionObjectsSql = @"
diff --git a/src/Core/VirtualObjectManager.cs b/src/Core/VirtualObjectManager.cs
index 34e7350..4152169 100644
--- a/src/Core/VirtualObjectManager.cs
+++ b/src/Core/VirtualObjectManager.cs
@@ -3,21 +3,23 @@ using System.IO;
using System.Linq;
using System.Reflection;
using Autodesk.Navisworks.Api;
+using NavisworksTransport.Utils;
namespace NavisworksTransport.Core
{
///
/// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体
- /// 通过追加预制的单位立方体NWC文件并缩放来创建指定尺寸的虚拟物体
+ /// 设计原则:
+ /// 1. 虚拟物体只创建一次(创建会载入模型,开销大)
+ /// 2. 不用时可以隐藏,或移动到CAD原始位置(不改变尺寸)
+ /// 3. 移动位置和朝向时,只使用变换,不修改尺寸
+ /// 4. 修改尺寸只在创建或尺寸参数变更时进行(会触发文档更新事件)
///
public class VirtualObjectManager
{
private static VirtualObjectManager _instance;
private static readonly object _lock = new object();
- ///
- /// 单例实例
- ///
public static VirtualObjectManager Instance
{
get
@@ -39,28 +41,14 @@ namespace NavisworksTransport.Core
private ModelItem _virtualObjectModelItem;
private Model _virtualObjectModel;
private bool _isVirtualObjectActive;
-
- // 🔥 新增:标记是否正在更新虚拟物体,用于避免触发缓存重建
private bool _isUpdatingVirtualObject = false;
-
- ///
- /// 是否正在更新虚拟物体(用于外部检测是否应该跳过缓存重建)
- ///
public bool IsUpdatingVirtualObject => _isUpdatingVirtualObject;
- // 🔥 记住虚拟物体的尺寸变换参数(避免动态计算)
private double _currentLengthMeters = 0;
private double _currentWidthMeters = 0;
private double _currentHeightMeters = 0;
- ///
- /// 当前虚拟物体ModelItem
- ///
public ModelItem CurrentVirtualObject => _virtualObjectModelItem;
-
- ///
- /// 虚拟物体是否激活
- ///
public bool IsVirtualObjectActive => _isVirtualObjectActive;
private VirtualObjectManager()
@@ -68,384 +56,380 @@ namespace NavisworksTransport.Core
_isVirtualObjectActive = false;
}
- ///
- /// 显示虚拟物体
- ///
- /// 长度(米)
- /// 宽度(米)
- /// 高度(米)
- public void ShowVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
+ #region 1. 创建(只执行一次,开销大)
+
+ public ModelItem CreateVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{
- // 🔥 设置标志,防止文档变化事件触发缓存重建
_isUpdatingVirtualObject = true;
try
{
- var doc = Application.ActiveDocument;
-
- // 检查虚拟物体模型是否已存在
- if (_virtualObjectModel != null)
+ // 检查是否已存在且尺寸相同
+ if (_virtualObjectModel != null &&
+ Application.ActiveDocument.Models.IndexOf(_virtualObjectModel) >= 0 &&
+ Math.Abs(_currentLengthMeters - lengthMeters) < 0.001 &&
+ Math.Abs(_currentWidthMeters - widthMeters) < 0.001 &&
+ Math.Abs(_currentHeightMeters - heightMeters) < 0.001)
{
- int currentIndex = doc.Models.IndexOf(_virtualObjectModel);
- if (currentIndex >= 0)
- {
- // 模型存在,显示它并更新尺寸
- LogManager.Info($"虚拟物体模型已存在(索引: {currentIndex}),显示并更新尺寸");
-
- var modelItems = new ModelItemCollection { _virtualObjectModelItem };
- doc.Models.SetHidden(modelItems, false);
-
- // 获取实际的几何体项
- _virtualObjectModelItem = _virtualObjectModel.RootItem;
- var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
- if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
- {
- _virtualObjectModelItem = geometryItem;
- }
-
- // 清除覆盖变换(之前动画移动和旋转的累积)
- modelItems.Clear();
- modelItems.Add(_virtualObjectModelItem);
- doc.Models.ResetPermanentTransform(modelItems);
-
- // 重置变换并缩放到目标尺寸
- ResetVirtualObjectTransform();
- ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
-
- _isVirtualObjectActive = true;
- LogManager.Info($"虚拟物体更新成功");
- return;
- }
- else
- {
- LogManager.Warning($"已保存的虚拟物体模型不在文档中,需要重新创建");
- }
+ LogManager.Info($"虚拟物体已存在且尺寸相同,直接返回");
+ _isVirtualObjectActive = true;
+ return _virtualObjectModelItem;
}
- // 模型不存在,创建新的
- CreateVirtualObjectInternal(doc, lengthMeters, widthMeters, heightMeters);
+ // 检查是否已存在但尺寸不同
+ if (_virtualObjectModel != null &&
+ Application.ActiveDocument.Models.IndexOf(_virtualObjectModel) >= 0)
+ {
+ LogManager.Info($"虚拟物体已存在但尺寸不同,更新尺寸");
+ UpdateVirtualObjectSize(lengthMeters, widthMeters, heightMeters);
+ _isVirtualObjectActive = true;
+ return _virtualObjectModelItem;
+ }
+
+ // 创建新的虚拟物体
+ LogManager.Info($"=== 创建虚拟物体 ===");
+ LogManager.Info($"目标尺寸: {lengthMeters:F2}m x {widthMeters:F2}m x {heightMeters:F2}m");
+
+ var doc = Application.ActiveDocument;
+ var unitCubePath = GetUnitCubeFilePath();
+
+ if (string.IsNullOrEmpty(unitCubePath) || !File.Exists(unitCubePath))
+ {
+ throw new FileNotFoundException($"找不到单位立方体文件: {unitCubePath}");
+ }
+
+ int modelCountBefore = doc.Models.Count;
+ if (!doc.TryAppendFile(unitCubePath))
+ {
+ throw new InvalidOperationException($"无法追加文件: {unitCubePath}");
+ }
+
+ int modelCountAfter = doc.Models.Count;
+ if (modelCountAfter <= modelCountBefore)
+ {
+ throw new InvalidOperationException("追加文件后模型数量未增加");
+ }
+
+ _virtualObjectModel = doc.Models.Last();
+ _virtualObjectModelItem = _virtualObjectModel.RootItem;
+
+ var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
+ if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
+ {
+ _virtualObjectModelItem = geometryItem;
+ }
+
+ _currentLengthMeters = lengthMeters;
+ _currentWidthMeters = widthMeters;
+ _currentHeightMeters = heightMeters;
+ ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
+
+ _isVirtualObjectActive = true;
+ LogManager.Info($"虚拟物体创建成功");
+
+ return _virtualObjectModelItem;
}
catch (Exception ex)
{
- LogManager.Error($"显示虚拟物体失败: {ex.Message}");
+ LogManager.Error($"创建虚拟物体失败: {ex.Message}");
+ throw;
}
finally
{
- // 🔥 清除标志
_isUpdatingVirtualObject = false;
}
}
- ///
- /// 隐藏虚拟物体
- ///
+ #endregion
+
+ #region 2. 显示/隐藏
+
+ public void ShowVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
+ {
+ if (_virtualObjectModel == null ||
+ Application.ActiveDocument.Models.IndexOf(_virtualObjectModel) < 0)
+ {
+ CreateVirtualObject(lengthMeters, widthMeters, heightMeters);
+ return;
+ }
+
+ var modelItems = new ModelItemCollection { _virtualObjectModelItem };
+ Application.ActiveDocument.Models.SetHidden(modelItems, false);
+ _isVirtualObjectActive = true;
+ LogManager.Info("虚拟物体已显示");
+ }
+
public void HideVirtualObject()
{
+ if (_virtualObjectModelItem == null) return;
+
try
{
- if (_virtualObjectModelItem != null)
- {
- var doc = Application.ActiveDocument;
- var modelItems = new ModelItemCollection { _virtualObjectModelItem };
- doc.Models.SetHidden(modelItems, true);
-
- LogManager.Info($"已隐藏虚拟物体模型");
- }
-
- // 隐藏时设置为非激活状态,但保留模型引用
+ var modelItems = new ModelItemCollection { _virtualObjectModelItem };
+ Application.ActiveDocument.Models.SetHidden(modelItems, true);
_isVirtualObjectActive = false;
+ LogManager.Info("虚拟物体已隐藏");
}
catch (Exception ex)
{
LogManager.Error($"隐藏虚拟物体失败: {ex.Message}");
- _isVirtualObjectActive = false;
}
}
- ///
- /// 创建虚拟物体(内部方法)
- ///
- private void CreateVirtualObjectInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
+ #endregion
+
+ #region 3. 移动位置和朝向
+
+ public void MoveVirtualObject(Point3D position, double yawRadians)
{
- LogManager.Info($"=== 创建虚拟物体 ===");
- LogManager.Info($"目标尺寸: {lengthMeters:F2}m × {widthMeters:F2}m × {heightMeters:F2}m");
+ if (_virtualObjectModelItem == null) return;
- // 加载新的虚拟物体模型
- LoadNewVirtualObjectModel(doc);
-
- // 获取实际的几何体项
- _virtualObjectModelItem = _virtualObjectModel.RootItem;
- var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
- if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
+ try
{
- _virtualObjectModelItem = geometryItem;
+ var doc = Application.ActiveDocument;
+ var modelItems = new ModelItemCollection { _virtualObjectModelItem };
+
+ // 获取当前缩放
+ var currentTransform = _virtualObjectModelItem.Transform;
+ var currentComponents = currentTransform.Factor();
+ var currentScale = currentComponents.Scale;
+
+ // 重置到CAD原始状态
+ doc.Models.ResetPermanentTransform(modelItems);
+
+ // 获取CAD原始状态
+ var originalBounds = _virtualObjectModelItem.BoundingBox();
+ var originalGroundPos = new Point3D(
+ originalBounds.Center.X,
+ originalBounds.Center.Y,
+ originalBounds.Min.Z
+ );
+ var originalYaw = GetYawFromTransform(_virtualObjectModelItem.Transform);
+
+ // 计算增量
+ var deltaPos = new Vector3D(
+ position.X - originalGroundPos.X,
+ position.Y - originalGroundPos.Y,
+ position.Z - originalGroundPos.Z
+ );
+ double deltaYaw = yawRadians - originalYaw;
+
+ // 构建变换,保留当前缩放
+ var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
+ var components = identity.Factor();
+ components.Scale = currentScale;
+
+ 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.Rotation = new Rotation3D(new UnitVector3D(0, 0, 1), deltaYaw);
+ components.Translation = new Vector3D(
+ position.X - rotatedX,
+ position.Y - rotatedY,
+ deltaPos.Z
+ );
+ }
+ else
+ {
+ components.Translation = deltaPos;
+ }
+
+ Transform3D transform = components.Combine();
+ doc.Models.OverridePermanentTransform(modelItems, transform, false);
+
+ LogManager.Debug($"虚拟物体已移动");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"移动虚拟物体失败: {ex.Message}");
}
-
- // 重置变换并缩放到目标尺寸
- ResetVirtualObjectTransform();
- ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
-
- // 设置状态
- _isVirtualObjectActive = true;
-
- LogManager.Info($"虚拟物体创建成功");
}
- ///
- /// 加载新的虚拟物体模型
- ///
- private void LoadNewVirtualObjectModel(Document doc)
+ public void ResetToCADPosition()
{
- LogManager.Info($"需要加载新的虚拟物体模型");
+ if (_virtualObjectModelItem == null) return;
- // 1. 获取单位立方体文件路径
- var unitCubePath = GetUnitCubeFilePath();
- if (string.IsNullOrEmpty(unitCubePath) || !File.Exists(unitCubePath))
+ try
{
- throw new FileNotFoundException($"找不到单位立方体文件: {unitCubePath}");
+ var doc = Application.ActiveDocument;
+ var modelItems = new ModelItemCollection { _virtualObjectModelItem };
+
+ // 获取当前缩放
+ var currentTransform = _virtualObjectModelItem.Transform;
+ var currentComponents = currentTransform.Factor();
+ var currentScale = currentComponents.Scale;
+
+ // 重置到CAD原始状态
+ doc.Models.ResetPermanentTransform(modelItems);
+
+ // 重新应用缩放
+ var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
+ var components = identity.Factor();
+ components.Scale = currentScale;
+
+ Transform3D newTransform = components.Combine();
+ doc.Models.OverridePermanentTransform(modelItems, newTransform, false);
+
+ LogManager.Info("虚拟物体已恢复到CAD原始位置");
}
-
- LogManager.Info($"单位立方体文件: {unitCubePath}");
-
- // 2. 记录追加前的模型数量
- int modelCountBefore = doc.Models.Count;
- LogManager.Info($"追加前模型数量: {modelCountBefore}");
-
- // 3. 追加单位立方体文件
- if (!doc.TryAppendFile(unitCubePath))
+ catch (Exception ex)
{
- throw new InvalidOperationException($"无法追加文件: {unitCubePath}");
+ LogManager.Error($"恢复虚拟物体失败: {ex.Message}");
}
-
- // 4. 验证并获取新追加的模型
- int modelCountAfter = doc.Models.Count;
- LogManager.Info($"追加后模型数量: {modelCountAfter}");
-
- if (modelCountAfter <= modelCountBefore)
- {
- throw new InvalidOperationException("追加文件后模型数量未增加");
- }
-
- // 保存模型引用(最后一个模型就是刚追加的)
- _virtualObjectModel = doc.Models.Last();
-
- LogManager.Info($"虚拟物体模型: {_virtualObjectModel.FileName}");
- LogManager.Info($"虚拟物体根项: {_virtualObjectModel.RootItem.DisplayName}");
}
- ///
- ///
- /// 重置虚拟物体变换为单位矩阵(公开方法,供批处理使用)
- ///
- public void ResetVirtualObjectTransform()
+ #endregion
+
+ #region 4. 修改尺寸
+
+ public void UpdateVirtualObjectSize(double lengthMeters, double widthMeters, double heightMeters)
{
if (_virtualObjectModel == null) return;
- var doc = Application.ActiveDocument;
+ _isUpdatingVirtualObject = true;
- // 创建单位变换矩阵
- Transform3D identityTransform = new Transform3D();
- Transform3DComponents identityComponents = identityTransform.Factor();
-
- // 应用单位变换
- Units currentUnits = _virtualObjectModel.Units;
- doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, identityTransform, false);
-
- LogManager.Info("已重置虚拟物体变换");
- }
-
- ///
- /// 重新应用当前记录的缩放(用于ResetPermanentTransform后恢复缩放)
- ///
- public void ReapplyCurrentScale()
- {
- if (_currentLengthMeters > 0 && _currentWidthMeters > 0 && _currentHeightMeters > 0)
+ try
{
- LogManager.Info($"重新应用虚拟物体缩放: {_currentLengthMeters:F2}m × {_currentWidthMeters:F2}m × {_currentHeightMeters:F2}m");
- ScaleVirtualObject(_currentLengthMeters, _currentWidthMeters, _currentHeightMeters);
+ _currentLengthMeters = lengthMeters;
+ _currentWidthMeters = widthMeters;
+ _currentHeightMeters = heightMeters;
+
+ // 获取当前位置和旋转
+ var currentTransform = _virtualObjectModelItem.Transform;
+ var currentComponents = currentTransform.Factor();
+ var currentTranslation = currentComponents.Translation;
+ var currentRotation = currentComponents.Rotation;
+
+ // 应用新缩放
+ ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
+
+ // 如果之前有位置/旋转,重新应用
+ bool hasTranslation = currentTranslation.X != 0 || currentTranslation.Y != 0 || currentTranslation.Z != 0;
+ // 使用 ToAxisAndAngle 检查是否有旋转
+ var rotationResult = currentRotation.ToAxisAndAngle();
+ bool hasRotation = Math.Abs(rotationResult.Angle) > 0.001;
+ if (hasTranslation || hasRotation)
+ {
+ var newTransform = _virtualObjectModelItem.Transform;
+ var newComponents = newTransform.Factor();
+ newComponents.Translation = currentTranslation;
+ newComponents.Rotation = currentRotation;
+
+ Transform3D combinedTransform = newComponents.Combine();
+ var doc = Application.ActiveDocument;
+ var modelItems = new ModelItemCollection { _virtualObjectModelItem };
+ doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, _virtualObjectModel.Units, combinedTransform, false);
+ }
+
+ LogManager.Info($"虚拟物体尺寸已更新");
}
- else
+ finally
{
- LogManager.Warning("无法重新应用缩放:当前尺寸记录无效");
+ _isUpdatingVirtualObject = false;
}
}
- ///
- /// 缩放虚拟物体到目标尺寸
- /// 使用DocumentModels.SetModelUnitsAndTransform方法进行模型级缩放
- ///
+ #endregion
+
+ #region 辅助方法
+
private void ScaleVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{
if (_virtualObjectModel == null || _virtualObjectModelItem == null) return;
var doc = Application.ActiveDocument;
-
- // 🔥 记住尺寸参数(避免动态计算)
- _currentLengthMeters = lengthMeters;
- _currentWidthMeters = widthMeters;
- _currentHeightMeters = heightMeters;
-
- // 获取单位转换因子(米到模型单位)
double metersToUnits = Utils.UnitsConverter.GetMetersToUnitsConversionFactor(doc.Units);
- // 🔥 对于虚拟物体,直接应用缩放比例(不动态读取当前尺寸)
- // 原因:虚拟物体可能被旋转,导致包围盒尺寸不准确
- // 单位立方体是 0.01m × 0.01m × 0.01m
- // 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;
+ double scaleY = widthMeters / baseSizeMeters;
+ double scaleZ = heightMeters / baseSizeMeters;
- 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 = _virtualObjectModel.Transform;
- Transform3DComponents transformComponents = currentTransform.Factor();
-
- // 获取当前值
- Vector3D currentTranslation = transformComponents.Translation;
- Rotation3D currentRotation = transformComponents.Rotation;
- Vector3D currentScale = transformComponents.Scale;
-
- LogManager.Info($"当前变换 - 平移: ({currentTranslation.X:F2}, {currentTranslation.Y:F2}, {currentTranslation.Z:F2})");
- LogManager.Info($"当前变换 - 缩放: ({currentScale.X:F2}, {currentScale.Y:F2}, {currentScale.Z:F2})");
-
- // 🔥 直接应用新的缩放值(不乘以当前缩放,避免累积)
+ var currentTransform = _virtualObjectModel.Transform;
+ var transformComponents = currentTransform.Factor();
transformComponents.Scale = new Vector3D(scaleX, scaleY, scaleZ);
- // 组合成新的变换
Transform3D newTransform = transformComponents.Combine();
-
- // 获取当前模型单位
- Units currentUnits = _virtualObjectModel.Units;
-
- // 应用新的变换
- doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, newTransform, false);
-
- // 验证缩放结果
- 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;
-
- double newLengthMeters = newLength / metersToUnits;
- double newWidthMeters = newWidth / metersToUnits;
- double newHeightMeters = newHeight / metersToUnits;
-
- LogManager.Info($"缩放后尺寸: {newLengthMeters:F2}m × {newWidthMeters:F2}m × {newHeightMeters:F2}m");
+ doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, _virtualObjectModel.Units, newTransform, false);
}
///
- /// 移除虚拟物体(使用 TryRemoveFile 真正删除模型)
+ /// 从Transform中提取Yaw角度(简化版本)
///
- public void RemoveVirtualObject()
+ private double GetYawFromTransform(Transform3D transform)
{
try
{
- var doc = Application.ActiveDocument;
+ // 使用ModelItemTransformHelper中的方法
+ return ModelItemTransformHelper.GetYawFromTransform(transform);
+ }
+ catch { }
+ return 0.0;
+ }
+ ///
+ /// 清理虚拟物体(用于程序退出或切换文档时)
+ ///
+ public void Cleanup()
+ {
+ try
+ {
if (_virtualObjectModel != null)
{
- // 动态查找模型索引
- int currentIndex = doc.Models.IndexOf(_virtualObjectModel);
-
- if (currentIndex >= 0)
+ var doc = Application.ActiveDocument;
+ int index = doc.Models.IndexOf(_virtualObjectModel);
+ if (index >= 0)
{
- // 使用 TryRemoveFile 真正删除模型
- bool removed = doc.TryRemoveFile(currentIndex);
-
- if (removed)
- {
- LogManager.Info($"已删除虚拟物体模型(索引: {currentIndex})");
- }
- else
- {
- LogManager.Warning($"删除虚拟物体模型失败(索引: {currentIndex})");
- }
- }
- else
- {
- LogManager.Warning($"虚拟物体模型不在文档中,无法删除");
+ doc.TryRemoveFile(index);
}
}
-
- // 清理引用
- _virtualObjectModel = null;
- _virtualObjectModelItem = null;
- _isVirtualObjectActive = false;
}
catch (Exception ex)
{
- LogManager.Error($"删除虚拟物体失败: {ex.Message}");
+ LogManager.Error($"清理虚拟物体失败: {ex.Message}");
+ }
+ finally
+ {
_virtualObjectModel = null;
_virtualObjectModelItem = null;
_isVirtualObjectActive = false;
}
}
- ///
- /// 获取单位立方体文件路径
- ///
+ private ModelItem FindFirstGeometryItem(ModelItem item)
+ {
+ if (item == null) return null;
+ if (item.HasGeometry) return item;
+ foreach (var child in item.Children)
+ {
+ var result = FindFirstGeometryItem(child);
+ if (result != null) return result;
+ }
+ return item;
+ }
+
private string GetUnitCubeFilePath()
{
- // 方法1:从插件目录获取
- var assemblyLocation = Assembly.GetExecutingAssembly().Location;
- var pluginDir = Path.GetDirectoryName(assemblyLocation);
-
- // 尝试多个可能的位置(现在统一放在resources目录下)
- var possiblePaths = new[]
+ string pluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ string[] possiblePaths = new[]
{
Path.Combine(pluginDir, "resources", "unit_cube.nwc"),
- // 开发时的备用位置
+ Path.Combine(pluginDir, "..", "..", "resources", "unit_cube.nwc"),
@"c:\Users\Tellme\apps\NavisworksTransport\resources\unit_cube.nwc"
};
foreach (var path in possiblePaths)
{
- var fullPath = Path.GetFullPath(path);
- if (File.Exists(fullPath))
- {
- LogManager.Info($"找到单位立方体文件: {fullPath}");
- return fullPath;
- }
+ if (File.Exists(path))
+ return path;
}
-
- LogManager.Warning($"在以下位置未找到单位立方体文件:");
- foreach (var path in possiblePaths)
- {
- LogManager.Warning($" - {Path.GetFullPath(path)}");
- }
-
- return null;
+ return possiblePaths[0];
}
- ///
- /// 查找第一个包含几何体的ModelItem
- ///
- private ModelItem FindFirstGeometryItem(ModelItem root)
- {
- if (root.HasGeometry)
- return root;
-
- foreach (var child in root.Children)
- {
- var result = FindFirstGeometryItem(child);
- if (result != null)
- return result;
- }
-
- return null;
- }
-
- ///
- /// 清理资源
- ///
- public void Cleanup()
- {
- RemoveVirtualObject();
- }
+ #endregion
}
}
diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
index bbfbdbc..b0585ec 100644
--- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
+++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
@@ -2403,7 +2403,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (collisionObject?.ModelItem == null) return;
// 从历史记录中获取运动物体
- var animatedObject = SelectedClashDetectiveResult?.AnimatedObject;
+ var selectedResult = SelectedClashDetectiveResult;
+ var animatedObject = selectedResult?.AnimatedObject;
+
+ // 如果是虚拟物体且尚未创建,现在创建它
+ if (selectedResult?.Record?.IsVirtualObject == true && animatedObject == null)
+ {
+ try
+ {
+ double unitsToMeters = UnitsConverter.GetUnitsToMetersConversionFactor(Autodesk.Navisworks.Api.Application.ActiveDocument.Units);
+ // 创建并显示虚拟物体(尺寸从模型单位转换为米)
+ animatedObject = VirtualObjectManager.Instance.CreateVirtualObject(
+ selectedResult.VirtualObjectLength * unitsToMeters,
+ selectedResult.VirtualObjectWidth * unitsToMeters,
+ selectedResult.VirtualObjectHeight * unitsToMeters);
+ selectedResult.AnimatedObject = animatedObject;
+ LogManager.Info($"[碰撞构件] 创建虚拟物体并聚焦: {selectedResult.VirtualObjectLength:F2}x{selectedResult.VirtualObjectWidth:F2}x{selectedResult.VirtualObjectHeight:F2}");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[碰撞构件] 创建虚拟物体失败: {ex.Message}");
+ }
+ }
+
if (animatedObject == null)
{
LogManager.Warning($"[碰撞构件] 未找到运动物体,仅聚焦到被撞对象: {collisionObject.DisplayName}");
@@ -2434,12 +2456,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
collisionObject.Item1YawRadians,
collisionObject.HasPositionInfo);
- // 🔥 关键:如果是虚拟物体,重新应用缩放(因为MoveItemToPositionAndYaw会重置变换)
- if (VirtualObjectManager.Instance.IsVirtualObjectActive &&
- VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject)
- {
- VirtualObjectManager.Instance.ReapplyCurrentScale();
- }
+ // 🔥 虚拟物体缩放已通过MoveItemToPositionAndYawWithCurrentScale保留
UpdateMainStatus($"已聚焦到碰撞构件: {collisionObject.DisplayName}");
LogManager.Info($"聚焦到碰撞构件: {collisionObject.DisplayName}");
@@ -3064,28 +3081,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var resultViewModel = new ClashDetectiveResultViewModel(record, RefreshClashDetectiveResultsList);
// 加载运动物体(Item1)
+ // 虚拟物体:只记录尺寸(数据库中是模型单位),等用户点击时再创建
+ // 真实物体:尝试解析PathId获取引用
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))
{
diff --git a/src/Utils/CollisionSceneHelper.cs b/src/Utils/CollisionSceneHelper.cs
index cd510a0..5465247 100644
--- a/src/Utils/CollisionSceneHelper.cs
+++ b/src/Utils/CollisionSceneHelper.cs
@@ -62,8 +62,22 @@ namespace NavisworksTransport.Utils
item1Position.Z - halfHeight
);
+ // 检查是否是虚拟物体
+ bool isVirtual = VirtualObjectManager.Instance.IsVirtualObjectActive &&
+ VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject;
+
// 使用工具方法从CAD原始状态移动到目标位置
- ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, item1YawRadians);
+ if (isVirtual)
+ {
+ // 虚拟物体:保留当前缩放
+ ModelItemTransformHelper.MoveItemToPositionAndYawWithCurrentScale(
+ animatedObject, targetGroundPosition, item1YawRadians);
+ }
+ else
+ {
+ // 普通物体:标准移动
+ ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, item1YawRadians);
+ }
LogManager.Info(string.Format("运动物体已移动到碰撞位置: ({0:F2}, {1:F2}, {2:F2}), 朝向: {3:F2}°",
targetGroundPosition.X, targetGroundPosition.Y, targetGroundPosition.Z,
@@ -116,8 +130,25 @@ namespace NavisworksTransport.Utils
try
{
- ModelItemTransformHelper.RestoreObjectState(animatedObject, state);
- LogManager.Info(string.Format("动画物体已恢复到原始状态: {0}", animatedObject.DisplayName));
+ // 检查是否是虚拟物体
+ bool isVirtual = VirtualObjectManager.Instance.IsVirtualObjectActive &&
+ VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject;
+
+ if (isVirtual)
+ {
+ // 虚拟物体:使用带当前缩放的移动方法
+ ModelItemTransformHelper.MoveItemToPositionAndYawWithCurrentScale(
+ animatedObject, state.Position, state.YawRadians);
+ LogManager.Info(string.Format("虚拟物体已恢复到原始状态: {0}, 位置=({1:F2},{2:F2},{3:F2}), 朝向={4:F2}°",
+ animatedObject.DisplayName, state.Position.X, state.Position.Y, state.Position.Z,
+ state.YawRadians * 180 / Math.PI));
+ }
+ else
+ {
+ // 普通物体:使用标准恢复
+ ModelItemTransformHelper.RestoreObjectState(animatedObject, state);
+ LogManager.Info(string.Format("动画物体已恢复到原始状态: {0}", animatedObject.DisplayName));
+ }
}
catch (Exception ex)
{
diff --git a/src/Utils/ModelItemTransformHelper.cs b/src/Utils/ModelItemTransformHelper.cs
index 119f112..bf8814d 100644
--- a/src/Utils/ModelItemTransformHelper.cs
+++ b/src/Utils/ModelItemTransformHelper.cs
@@ -281,10 +281,25 @@ namespace NavisworksTransport.Utils
}
///
- /// 从状态快照恢复物体位置
- /// 先回到CAD原始位置,再移动到保存的位置
+ /// 从状态快照恢复物体位置和朝向(标准版本,会重置缩放)
///
public static void RestoreObjectState(ModelItem item, ObjectStateSnapshot state)
+ {
+ RestoreObjectStateInternal(item, state, preserveScale: false);
+ }
+
+ ///
+ /// 从状态快照恢复物体位置和朝向(保留缩放版本,用于虚拟物体)
+ ///
+ public static void RestoreObjectStateWithScale(ModelItem item, ObjectStateSnapshot state)
+ {
+ RestoreObjectStateInternal(item, state, preserveScale: true);
+ }
+
+ ///
+ /// 从状态快照恢复物体位置的内部实现
+ ///
+ private static void RestoreObjectStateInternal(ModelItem item, ObjectStateSnapshot state, bool preserveScale)
{
if (item == null || state == null) return;
@@ -340,5 +355,72 @@ namespace NavisworksTransport.Utils
doc.Models.OverridePermanentTransform(modelItems, transform, false);
}
+
+ ///
+ /// 将物体移动到指定位置和朝向,同时保持当前缩放(专为虚拟物体设计)
+ ///
+ public static void MoveItemToPositionAndYawWithCurrentScale(ModelItem item, Point3D targetPosition, double targetYaw)
+ {
+ var doc = Application.ActiveDocument;
+ var modelItems = new ModelItemCollection { item };
+
+ // 获取当前变换中的缩放
+ var currentTransform = item.Transform;
+ var currentComponents = currentTransform.Factor();
+ var currentScale = currentComponents.Scale;
+
+ // 重置到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 = currentScale; // 保留当前缩放
+
+ 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);
+ }
}
}
diff --git a/src/Utils/ViewpointHelper.cs b/src/Utils/ViewpointHelper.cs
index 8a96912..4ce8656 100644
--- a/src/Utils/ViewpointHelper.cs
+++ b/src/Utils/ViewpointHelper.cs
@@ -373,7 +373,7 @@ namespace NavisworksTransport.Utils
Math.Max(boundingBox.Max.Y - boundingBox.Min.Y, boundingBox.Max.Z - boundingBox.Min.Z)
);
- LogManager.Info($"聚焦到模型元素: {modelItem.DisplayName}, 最大边: {maxDimension:F2}");
+ LogManager.Debug($"聚焦到模型元素: {modelItem.DisplayName}, 最大边: {maxDimension:F2}");
// 3. 计算相机位置(使用模型标准视角方向)
Point3D cameraPosition = CalculateCameraPosition(center, maxDimension, viewAngleDegrees, targetViewRatio);
@@ -382,7 +382,7 @@ namespace NavisworksTransport.Utils
Vector3D upVector = doc.FrontRightTopViewUpVector;
ApplyViewpoint(cameraPosition, center, upVector, useAlignDirection: true);
- LogManager.Info($"视角已调整: 标准前右上视角, 目标占据视图{targetViewRatio:P0}");
+ LogManager.Debug($"视角已调整: 标准前右上视角, 目标占据视图{targetViewRatio:P0}");
}
catch (Exception ex)
{