From eec957ad2e9d1157070ab5bc0755f951c242ea2f Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 18 Feb 2026 00:05:22 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=99=9A=E6=8B=9F=E7=89=A9?= =?UTF-8?q?=E4=BD=93=E7=AE=A1=E7=90=86=E5=99=A8=EF=BC=9B=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=A2=B0=E6=92=9E=E5=8E=86=E5=8F=B2=E5=88=97=E8=A1=A8=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=A7=BB=E5=8A=A8=E7=89=A9=E4=BD=93=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GenerateCollisionReportCommand.cs | 28 + .../Collision/ClashDetectiveIntegration.cs | 18 +- src/Core/VirtualObjectManager.cs | 578 +++++++++--------- .../ViewModels/AnimationControlViewModel.cs | 50 +- src/Utils/CollisionSceneHelper.cs | 37 +- src/Utils/ModelItemTransformHelper.cs | 86 ++- src/Utils/ViewpointHelper.cs | 4 +- 7 files changed, 466 insertions(+), 335 deletions(-) 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) {