重构虚拟物体管理器;修复碰撞历史列表自动移动物体的bug

This commit is contained in:
tian 2026-02-18 00:05:22 +08:00
parent 1b26e7b640
commit eec957ad2e
7 changed files with 466 additions and 335 deletions

View File

@ -345,6 +345,22 @@ namespace NavisworksTransport.Commands
{ {
UpdateProgress(92, "生成默认路径截图..."); 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 generator = new NavigationMapGenerator();
var viewportSize = generator.GetCurrentViewportSize(); var viewportSize = generator.GetCurrentViewportSize();
@ -359,6 +375,18 @@ namespace NavisworksTransport.Commands
System.Drawing.Imaging.ImageFormat.Jpeg, System.Drawing.Imaging.ImageFormat.Jpeg,
"overview" "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) if (screenshotPath != null)
{ {

View File

@ -412,15 +412,13 @@ namespace NavisworksTransport
ModelItem objectObject = null; ModelItem objectObject = null;
if (testInfo.IsVirtualObject) if (testInfo.IsVirtualObject)
{ {
// 显示虚拟物体 // 创建虚拟物体(保留现有变换如果尺寸相同)
var modelToMeters = UnitsConverter.GetUnitsToMetersConversionFactor(); var modelToMeters = UnitsConverter.GetUnitsToMetersConversionFactor();
VirtualObjectManager.Instance.ShowVirtualObject( objectObject = VirtualObjectManager.Instance.CreateVirtualObject(
testInfo.VirtualObjectLength * modelToMeters, testInfo.VirtualObjectLength * modelToMeters,
testInfo.VirtualObjectWidth * modelToMeters, testInfo.VirtualObjectWidth * modelToMeters,
testInfo.VirtualObjectHeight * modelToMeters testInfo.VirtualObjectHeight * modelToMeters
); ) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 获取虚拟物体失败");
objectObject = VirtualObjectManager.Instance.CurrentVirtualObject
?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 显示虚拟物体失败");
} }
else if (testInfo.ObjectModelIndex.HasValue && !string.IsNullOrEmpty(testInfo.ObjectPathId)) 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}"); 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 try
{ {
@ -471,6 +471,10 @@ namespace NavisworksTransport
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 移动虚拟物体到起点失败: {ex.Message}"); LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 移动虚拟物体到起点失败: {ex.Message}");
} }
} }
else if (testInfo.IsVirtualObject && VirtualObjectManager.Instance.IsVirtualObjectActive)
{
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟物体已存在,保持当前位置(终点),不移动到起点");
}
// 3. 从数据库读取被撞物体信息(包含碰撞时运动物体的位置和朝向) // 3. 从数据库读取被撞物体信息(包含碰撞时运动物体的位置和朝向)
var collisionObjectsSql = @" var collisionObjectsSql = @"

View File

@ -3,21 +3,23 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Autodesk.Navisworks.Api; using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;
namespace NavisworksTransport.Core namespace NavisworksTransport.Core
{ {
/// <summary> /// <summary>
/// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体 /// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体
/// 通过追加预制的单位立方体NWC文件并缩放来创建指定尺寸的虚拟物体 /// 设计原则:
/// 1. 虚拟物体只创建一次(创建会载入模型,开销大)
/// 2. 不用时可以隐藏或移动到CAD原始位置不改变尺寸
/// 3. 移动位置和朝向时,只使用变换,不修改尺寸
/// 4. 修改尺寸只在创建或尺寸参数变更时进行(会触发文档更新事件)
/// </summary> /// </summary>
public class VirtualObjectManager public class VirtualObjectManager
{ {
private static VirtualObjectManager _instance; private static VirtualObjectManager _instance;
private static readonly object _lock = new object(); private static readonly object _lock = new object();
/// <summary>
/// 单例实例
/// </summary>
public static VirtualObjectManager Instance public static VirtualObjectManager Instance
{ {
get get
@ -39,28 +41,14 @@ namespace NavisworksTransport.Core
private ModelItem _virtualObjectModelItem; private ModelItem _virtualObjectModelItem;
private Model _virtualObjectModel; private Model _virtualObjectModel;
private bool _isVirtualObjectActive; private bool _isVirtualObjectActive;
// 🔥 新增:标记是否正在更新虚拟物体,用于避免触发缓存重建
private bool _isUpdatingVirtualObject = false; private bool _isUpdatingVirtualObject = false;
/// <summary>
/// 是否正在更新虚拟物体(用于外部检测是否应该跳过缓存重建)
/// </summary>
public bool IsUpdatingVirtualObject => _isUpdatingVirtualObject; public bool IsUpdatingVirtualObject => _isUpdatingVirtualObject;
// 🔥 记住虚拟物体的尺寸变换参数(避免动态计算)
private double _currentLengthMeters = 0; private double _currentLengthMeters = 0;
private double _currentWidthMeters = 0; private double _currentWidthMeters = 0;
private double _currentHeightMeters = 0; private double _currentHeightMeters = 0;
/// <summary>
/// 当前虚拟物体ModelItem
/// </summary>
public ModelItem CurrentVirtualObject => _virtualObjectModelItem; public ModelItem CurrentVirtualObject => _virtualObjectModelItem;
/// <summary>
/// 虚拟物体是否激活
/// </summary>
public bool IsVirtualObjectActive => _isVirtualObjectActive; public bool IsVirtualObjectActive => _isVirtualObjectActive;
private VirtualObjectManager() private VirtualObjectManager()
@ -68,384 +56,380 @@ namespace NavisworksTransport.Core
_isVirtualObjectActive = false; _isVirtualObjectActive = false;
} }
/// <summary> #region 1.
/// 显示虚拟物体
/// </summary> public ModelItem CreateVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
/// <param name="lengthMeters">长度(米)</param>
/// <param name="widthMeters">宽度(米)</param>
/// <param name="heightMeters">高度(米)</param>
public void ShowVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{ {
// 🔥 设置标志,防止文档变化事件触发缓存重建
_isUpdatingVirtualObject = true; _isUpdatingVirtualObject = true;
try try
{ {
var doc = Application.ActiveDocument; // 检查是否已存在且尺寸相同
if (_virtualObjectModel != null &&
// 检查虚拟物体模型是否已存在 Application.ActiveDocument.Models.IndexOf(_virtualObjectModel) >= 0 &&
if (_virtualObjectModel != null) Math.Abs(_currentLengthMeters - lengthMeters) < 0.001 &&
Math.Abs(_currentWidthMeters - widthMeters) < 0.001 &&
Math.Abs(_currentHeightMeters - heightMeters) < 0.001)
{ {
int currentIndex = doc.Models.IndexOf(_virtualObjectModel); LogManager.Info($"虚拟物体已存在且尺寸相同,直接返回");
if (currentIndex >= 0) _isVirtualObjectActive = true;
{ return _virtualObjectModelItem;
// 模型存在,显示它并更新尺寸
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($"已保存的虚拟物体模型不在文档中,需要重新创建");
}
} }
// 模型不存在,创建新的 // 检查是否已存在但尺寸不同
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) catch (Exception ex)
{ {
LogManager.Error($"显示虚拟物体失败: {ex.Message}"); LogManager.Error($"创建虚拟物体失败: {ex.Message}");
throw;
} }
finally finally
{ {
// 🔥 清除标志
_isUpdatingVirtualObject = false; _isUpdatingVirtualObject = false;
} }
} }
/// <summary> #endregion
/// 隐藏虚拟物体
/// </summary> #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() public void HideVirtualObject()
{ {
if (_virtualObjectModelItem == null) return;
try try
{ {
if (_virtualObjectModelItem != null) var modelItems = new ModelItemCollection { _virtualObjectModelItem };
{ Application.ActiveDocument.Models.SetHidden(modelItems, true);
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
doc.Models.SetHidden(modelItems, true);
LogManager.Info($"已隐藏虚拟物体模型");
}
// 隐藏时设置为非激活状态,但保留模型引用
_isVirtualObjectActive = false; _isVirtualObjectActive = false;
LogManager.Info("虚拟物体已隐藏");
} }
catch (Exception ex) catch (Exception ex)
{ {
LogManager.Error($"隐藏虚拟物体失败: {ex.Message}"); LogManager.Error($"隐藏虚拟物体失败: {ex.Message}");
_isVirtualObjectActive = false;
} }
} }
/// <summary> #endregion
/// 创建虚拟物体(内部方法)
/// </summary> #region 3.
private void CreateVirtualObjectInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
public void MoveVirtualObject(Point3D position, double yawRadians)
{ {
LogManager.Info($"=== 创建虚拟物体 ==="); if (_virtualObjectModelItem == null) return;
LogManager.Info($"目标尺寸: {lengthMeters:F2}m × {widthMeters:F2}m × {heightMeters:F2}m");
// 加载新的虚拟物体模型 try
LoadNewVirtualObjectModel(doc);
// 获取实际的几何体项
_virtualObjectModelItem = _virtualObjectModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
{ {
_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($"虚拟物体创建成功");
} }
/// <summary> public void ResetToCADPosition()
/// 加载新的虚拟物体模型
/// </summary>
private void LoadNewVirtualObjectModel(Document doc)
{ {
LogManager.Info($"需要加载新的虚拟物体模型"); if (_virtualObjectModelItem == null) return;
// 1. 获取单位立方体文件路径 try
var unitCubePath = GetUnitCubeFilePath();
if (string.IsNullOrEmpty(unitCubePath) || !File.Exists(unitCubePath))
{ {
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原始位置");
} }
catch (Exception ex)
LogManager.Info($"单位立方体文件: {unitCubePath}");
// 2. 记录追加前的模型数量
int modelCountBefore = doc.Models.Count;
LogManager.Info($"追加前模型数量: {modelCountBefore}");
// 3. 追加单位立方体文件
if (!doc.TryAppendFile(unitCubePath))
{ {
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}");
} }
/// <summary> #endregion
/// <summary>
/// 重置虚拟物体变换为单位矩阵(公开方法,供批处理使用) #region 4.
/// </summary>
public void ResetVirtualObjectTransform() public void UpdateVirtualObjectSize(double lengthMeters, double widthMeters, double heightMeters)
{ {
if (_virtualObjectModel == null) return; if (_virtualObjectModel == null) return;
var doc = Application.ActiveDocument; _isUpdatingVirtualObject = true;
// 创建单位变换矩阵 try
Transform3D identityTransform = new Transform3D();
Transform3DComponents identityComponents = identityTransform.Factor();
// 应用单位变换
Units currentUnits = _virtualObjectModel.Units;
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, identityTransform, false);
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"); _currentLengthMeters = lengthMeters;
ScaleVirtualObject(_currentLengthMeters, _currentWidthMeters, _currentHeightMeters); _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;
} }
} }
/// <summary> #endregion
/// 缩放虚拟物体到目标尺寸
/// 使用DocumentModels.SetModelUnitsAndTransform方法进行模型级缩放 #region
/// </summary>
private void ScaleVirtualObject(double lengthMeters, double widthMeters, double heightMeters) private void ScaleVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{ {
if (_virtualObjectModel == null || _virtualObjectModelItem == null) return; if (_virtualObjectModel == null || _virtualObjectModelItem == null) return;
var doc = Application.ActiveDocument; var doc = Application.ActiveDocument;
// 🔥 记住尺寸参数(避免动态计算)
_currentLengthMeters = lengthMeters;
_currentWidthMeters = widthMeters;
_currentHeightMeters = heightMeters;
// 获取单位转换因子(米到模型单位)
double metersToUnits = Utils.UnitsConverter.GetMetersToUnitsConversionFactor(doc.Units); double metersToUnits = Utils.UnitsConverter.GetMetersToUnitsConversionFactor(doc.Units);
// 🔥 对于虚拟物体,直接应用缩放比例(不动态读取当前尺寸)
// 原因:虚拟物体可能被旋转,导致包围盒尺寸不准确
// 单位立方体是 0.01m × 0.01m × 0.01m
// X方向 = 物体长度前进方向Y方向 = 物体宽度侧面Z方向 = 物体高度
double baseSizeMeters = 0.01; double baseSizeMeters = 0.01;
double scaleX = lengthMeters / baseSizeMeters; // X方向 = 物体长度(前进方向) double scaleX = lengthMeters / baseSizeMeters;
double scaleY = widthMeters / baseSizeMeters; // Y方向 = 物体宽度(侧面) double scaleY = widthMeters / baseSizeMeters;
double scaleZ = heightMeters / baseSizeMeters; // Z方向 = 物体高度 double scaleZ = heightMeters / baseSizeMeters;
LogManager.Info($"目标尺寸: 长度={lengthMeters:F2}m, 宽度={widthMeters:F2}m, 高度={heightMeters:F2}m"); var currentTransform = _virtualObjectModel.Transform;
LogManager.Info($"缩放比例: X(长度)={scaleX:F2}, Y(宽度)={scaleY:F2}, Z(高度)={scaleZ:F2}"); var transformComponents = currentTransform.Factor();
// 使用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})");
// 🔥 直接应用新的缩放值(不乘以当前缩放,避免累积)
transformComponents.Scale = new Vector3D(scaleX, scaleY, scaleZ); transformComponents.Scale = new Vector3D(scaleX, scaleY, scaleZ);
// 组合成新的变换
Transform3D newTransform = transformComponents.Combine(); Transform3D newTransform = transformComponents.Combine();
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, _virtualObjectModel.Units, newTransform, false);
// 获取当前模型单位
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");
} }
/// <summary> /// <summary>
/// 移除虚拟物体(使用 TryRemoveFile 真正删除模型 /// 从Transform中提取Yaw角度简化版本
/// </summary> /// </summary>
public void RemoveVirtualObject() private double GetYawFromTransform(Transform3D transform)
{ {
try try
{ {
var doc = Application.ActiveDocument; // 使用ModelItemTransformHelper中的方法
return ModelItemTransformHelper.GetYawFromTransform(transform);
}
catch { }
return 0.0;
}
/// <summary>
/// 清理虚拟物体(用于程序退出或切换文档时)
/// </summary>
public void Cleanup()
{
try
{
if (_virtualObjectModel != null) if (_virtualObjectModel != null)
{ {
// 动态查找模型索引 var doc = Application.ActiveDocument;
int currentIndex = doc.Models.IndexOf(_virtualObjectModel); int index = doc.Models.IndexOf(_virtualObjectModel);
if (index >= 0)
if (currentIndex >= 0)
{ {
// 使用 TryRemoveFile 真正删除模型 doc.TryRemoveFile(index);
bool removed = doc.TryRemoveFile(currentIndex);
if (removed)
{
LogManager.Info($"已删除虚拟物体模型(索引: {currentIndex}");
}
else
{
LogManager.Warning($"删除虚拟物体模型失败(索引: {currentIndex}");
}
}
else
{
LogManager.Warning($"虚拟物体模型不在文档中,无法删除");
} }
} }
// 清理引用
_virtualObjectModel = null;
_virtualObjectModelItem = null;
_isVirtualObjectActive = false;
} }
catch (Exception ex) catch (Exception ex)
{ {
LogManager.Error($"删除虚拟物体失败: {ex.Message}"); LogManager.Error($"清理虚拟物体失败: {ex.Message}");
}
finally
{
_virtualObjectModel = null; _virtualObjectModel = null;
_virtualObjectModelItem = null; _virtualObjectModelItem = null;
_isVirtualObjectActive = false; _isVirtualObjectActive = false;
} }
} }
/// <summary> private ModelItem FindFirstGeometryItem(ModelItem item)
/// 获取单位立方体文件路径 {
/// </summary> 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() private string GetUnitCubeFilePath()
{ {
// 方法1从插件目录获取 string pluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var assemblyLocation = Assembly.GetExecutingAssembly().Location; string[] possiblePaths = new[]
var pluginDir = Path.GetDirectoryName(assemblyLocation);
// 尝试多个可能的位置现在统一放在resources目录下
var possiblePaths = new[]
{ {
Path.Combine(pluginDir, "resources", "unit_cube.nwc"), Path.Combine(pluginDir, "resources", "unit_cube.nwc"),
// 开发时的备用位置 Path.Combine(pluginDir, "..", "..", "resources", "unit_cube.nwc"),
@"c:\Users\Tellme\apps\NavisworksTransport\resources\unit_cube.nwc" @"c:\Users\Tellme\apps\NavisworksTransport\resources\unit_cube.nwc"
}; };
foreach (var path in possiblePaths) foreach (var path in possiblePaths)
{ {
var fullPath = Path.GetFullPath(path); if (File.Exists(path))
if (File.Exists(fullPath)) return path;
{
LogManager.Info($"找到单位立方体文件: {fullPath}");
return fullPath;
}
} }
return possiblePaths[0];
LogManager.Warning($"在以下位置未找到单位立方体文件:");
foreach (var path in possiblePaths)
{
LogManager.Warning($" - {Path.GetFullPath(path)}");
}
return null;
} }
/// <summary> #endregion
/// 查找第一个包含几何体的ModelItem
/// </summary>
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;
}
/// <summary>
/// 清理资源
/// </summary>
public void Cleanup()
{
RemoveVirtualObject();
}
} }
} }

View File

@ -2403,7 +2403,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (collisionObject?.ModelItem == null) return; 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) if (animatedObject == null)
{ {
LogManager.Warning($"[碰撞构件] 未找到运动物体,仅聚焦到被撞对象: {collisionObject.DisplayName}"); LogManager.Warning($"[碰撞构件] 未找到运动物体,仅聚焦到被撞对象: {collisionObject.DisplayName}");
@ -2434,12 +2456,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
collisionObject.Item1YawRadians, collisionObject.Item1YawRadians,
collisionObject.HasPositionInfo); collisionObject.HasPositionInfo);
// 🔥 关键如果是虚拟物体重新应用缩放因为MoveItemToPositionAndYaw会重置变换 // 🔥 虚拟物体缩放已通过MoveItemToPositionAndYawWithCurrentScale保留
if (VirtualObjectManager.Instance.IsVirtualObjectActive &&
VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject)
{
VirtualObjectManager.Instance.ReapplyCurrentScale();
}
UpdateMainStatus($"已聚焦到碰撞构件: {collisionObject.DisplayName}"); UpdateMainStatus($"已聚焦到碰撞构件: {collisionObject.DisplayName}");
LogManager.Info($"聚焦到碰撞构件: {collisionObject.DisplayName}"); LogManager.Info($"聚焦到碰撞构件: {collisionObject.DisplayName}");
@ -3064,28 +3081,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var resultViewModel = new ClashDetectiveResultViewModel(record, RefreshClashDetectiveResultsList); var resultViewModel = new ClashDetectiveResultViewModel(record, RefreshClashDetectiveResultsList);
// 加载运动物体Item1 // 加载运动物体Item1
// 虚拟物体:只记录尺寸(数据库中是模型单位),等用户点击时再创建
// 真实物体尝试解析PathId获取引用
if (record.IsVirtualObject) if (record.IsVirtualObject)
{ {
// 使用虚拟物体,设置历史记录中的尺寸(数据库中是模型单位)
resultViewModel.VirtualObjectLength = record.VirtualObjectLength; resultViewModel.VirtualObjectLength = record.VirtualObjectLength;
resultViewModel.VirtualObjectWidth = record.VirtualObjectWidth; resultViewModel.VirtualObjectWidth = record.VirtualObjectWidth;
resultViewModel.VirtualObjectHeight = record.VirtualObjectHeight; 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)) else if (record.ObjectModelIndex.HasValue && !string.IsNullOrEmpty(record.ObjectPathId))
{ {

View File

@ -62,8 +62,22 @@ namespace NavisworksTransport.Utils
item1Position.Z - halfHeight item1Position.Z - halfHeight
); );
// 检查是否是虚拟物体
bool isVirtual = VirtualObjectManager.Instance.IsVirtualObjectActive &&
VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject;
// 使用工具方法从CAD原始状态移动到目标位置 // 使用工具方法从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}°", LogManager.Info(string.Format("运动物体已移动到碰撞位置: ({0:F2}, {1:F2}, {2:F2}), 朝向: {3:F2}°",
targetGroundPosition.X, targetGroundPosition.Y, targetGroundPosition.Z, targetGroundPosition.X, targetGroundPosition.Y, targetGroundPosition.Z,
@ -116,8 +130,25 @@ namespace NavisworksTransport.Utils
try 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) catch (Exception ex)
{ {

View File

@ -281,10 +281,25 @@ namespace NavisworksTransport.Utils
} }
/// <summary> /// <summary>
/// 从状态快照恢复物体位置 /// 从状态快照恢复物体位置和朝向(标准版本,会重置缩放)
/// 先回到CAD原始位置再移动到保存的位置
/// </summary> /// </summary>
public static void RestoreObjectState(ModelItem item, ObjectStateSnapshot state) public static void RestoreObjectState(ModelItem item, ObjectStateSnapshot state)
{
RestoreObjectStateInternal(item, state, preserveScale: false);
}
/// <summary>
/// 从状态快照恢复物体位置和朝向(保留缩放版本,用于虚拟物体)
/// </summary>
public static void RestoreObjectStateWithScale(ModelItem item, ObjectStateSnapshot state)
{
RestoreObjectStateInternal(item, state, preserveScale: true);
}
/// <summary>
/// 从状态快照恢复物体位置的内部实现
/// </summary>
private static void RestoreObjectStateInternal(ModelItem item, ObjectStateSnapshot state, bool preserveScale)
{ {
if (item == null || state == null) return; if (item == null || state == null) return;
@ -340,5 +355,72 @@ namespace NavisworksTransport.Utils
doc.Models.OverridePermanentTransform(modelItems, transform, false); doc.Models.OverridePermanentTransform(modelItems, transform, false);
} }
/// <summary>
/// 将物体移动到指定位置和朝向,同时保持当前缩放(专为虚拟物体设计)
/// </summary>
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);
}
} }
} }

View File

@ -373,7 +373,7 @@ namespace NavisworksTransport.Utils
Math.Max(boundingBox.Max.Y - boundingBox.Min.Y, boundingBox.Max.Z - boundingBox.Min.Z) 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. 计算相机位置(使用模型标准视角方向) // 3. 计算相机位置(使用模型标准视角方向)
Point3D cameraPosition = CalculateCameraPosition(center, maxDimension, viewAngleDegrees, targetViewRatio); Point3D cameraPosition = CalculateCameraPosition(center, maxDimension, viewAngleDegrees, targetViewRatio);
@ -382,7 +382,7 @@ namespace NavisworksTransport.Utils
Vector3D upVector = doc.FrontRightTopViewUpVector; Vector3D upVector = doc.FrontRightTopViewUpVector;
ApplyViewpoint(cameraPosition, center, upVector, useAlignDirection: true); ApplyViewpoint(cameraPosition, center, upVector, useAlignDirection: true);
LogManager.Info($"视角已调整: 标准前右上视角, 目标占据视图{targetViewRatio:P0}"); LogManager.Debug($"视角已调整: 标准前右上视角, 目标占据视图{targetViewRatio:P0}");
} }
catch (Exception ex) catch (Exception ex)
{ {