438 lines
17 KiB
C#
438 lines
17 KiB
C#
using System;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using Autodesk.Navisworks.Api;
|
||
|
||
namespace NavisworksTransport.Core
|
||
{
|
||
/// <summary>
|
||
/// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体
|
||
/// 通过追加预制的单位立方体NWC文件并缩放来创建指定尺寸的虚拟物体
|
||
/// </summary>
|
||
public class VirtualObjectManager
|
||
{
|
||
private static VirtualObjectManager _instance;
|
||
private static readonly object _lock = new object();
|
||
|
||
/// <summary>
|
||
/// 单例实例
|
||
/// </summary>
|
||
public static VirtualObjectManager Instance
|
||
{
|
||
get
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
lock (_lock)
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
_instance = new VirtualObjectManager();
|
||
}
|
||
}
|
||
}
|
||
return _instance;
|
||
}
|
||
}
|
||
|
||
private ModelItem _virtualObjectModelItem;
|
||
private Model _virtualObjectModel;
|
||
private bool _isVirtualObjectActive;
|
||
|
||
// 🔥 新增:标记是否正在更新虚拟物体,用于避免触发缓存重建
|
||
private bool _isUpdatingVirtualObject = false;
|
||
|
||
/// <summary>
|
||
/// 是否正在更新虚拟物体(用于外部检测是否应该跳过缓存重建)
|
||
/// </summary>
|
||
public bool IsUpdatingVirtualObject => _isUpdatingVirtualObject;
|
||
|
||
// 🔥 记住虚拟物体的尺寸变换参数(避免动态计算)
|
||
private double _currentLengthMeters = 0;
|
||
private double _currentWidthMeters = 0;
|
||
private double _currentHeightMeters = 0;
|
||
|
||
/// <summary>
|
||
/// 当前虚拟物体ModelItem
|
||
/// </summary>
|
||
public ModelItem CurrentVirtualObject => _virtualObjectModelItem;
|
||
|
||
/// <summary>
|
||
/// 虚拟物体是否激活
|
||
/// </summary>
|
||
public bool IsVirtualObjectActive => _isVirtualObjectActive;
|
||
|
||
private VirtualObjectManager()
|
||
{
|
||
_isVirtualObjectActive = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示虚拟物体
|
||
/// </summary>
|
||
/// <param name="lengthMeters">长度(米)</param>
|
||
/// <param name="widthMeters">宽度(米)</param>
|
||
/// <param name="heightMeters">高度(米)</param>
|
||
public void ShowVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
|
||
{
|
||
// 🔥 设置标志,防止文档变化事件触发缓存重建
|
||
_isUpdatingVirtualObject = true;
|
||
|
||
try
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
|
||
// 检查虚拟物体模型是否已存在
|
||
if (_virtualObjectModel != null)
|
||
{
|
||
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($"已保存的虚拟物体模型不在文档中,需要重新创建");
|
||
}
|
||
}
|
||
|
||
// 模型不存在,创建新的
|
||
CreateVirtualObjectInternal(doc, lengthMeters, widthMeters, heightMeters);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"显示虚拟物体失败: {ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
// 🔥 清除标志
|
||
_isUpdatingVirtualObject = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 隐藏虚拟物体
|
||
/// </summary>
|
||
public void HideVirtualObject()
|
||
{
|
||
try
|
||
{
|
||
if (_virtualObjectModelItem != null)
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
|
||
doc.Models.SetHidden(modelItems, true);
|
||
|
||
LogManager.Info($"已隐藏虚拟物体模型");
|
||
}
|
||
|
||
// 隐藏时设置为非激活状态,但保留模型引用
|
||
_isVirtualObjectActive = false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"隐藏虚拟物体失败: {ex.Message}");
|
||
_isVirtualObjectActive = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建虚拟物体(内部方法)
|
||
/// </summary>
|
||
private void CreateVirtualObjectInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
|
||
{
|
||
LogManager.Info($"=== 创建虚拟物体 ===");
|
||
LogManager.Info($"目标尺寸: {lengthMeters:F2}m × {widthMeters:F2}m × {heightMeters:F2}m");
|
||
|
||
// 加载新的虚拟物体模型
|
||
LoadNewVirtualObjectModel(doc);
|
||
|
||
// 获取实际的几何体项
|
||
_virtualObjectModelItem = _virtualObjectModel.RootItem;
|
||
var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
|
||
if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
|
||
{
|
||
_virtualObjectModelItem = geometryItem;
|
||
}
|
||
|
||
// 重置变换并缩放到目标尺寸
|
||
ResetVirtualObjectTransform();
|
||
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
|
||
|
||
// 设置状态
|
||
_isVirtualObjectActive = true;
|
||
|
||
LogManager.Info($"虚拟物体创建成功");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载新的虚拟物体模型
|
||
/// </summary>
|
||
private void LoadNewVirtualObjectModel(Document doc)
|
||
{
|
||
LogManager.Info($"需要加载新的虚拟物体模型");
|
||
|
||
// 1. 获取单位立方体文件路径
|
||
var unitCubePath = GetUnitCubeFilePath();
|
||
if (string.IsNullOrEmpty(unitCubePath) || !File.Exists(unitCubePath))
|
||
{
|
||
throw new FileNotFoundException($"找不到单位立方体文件: {unitCubePath}");
|
||
}
|
||
|
||
LogManager.Info($"单位立方体文件: {unitCubePath}");
|
||
|
||
// 2. 记录追加前的模型数量
|
||
int modelCountBefore = doc.Models.Count;
|
||
LogManager.Info($"追加前模型数量: {modelCountBefore}");
|
||
|
||
// 3. 追加单位立方体文件
|
||
if (!doc.TryAppendFile(unitCubePath))
|
||
{
|
||
throw new InvalidOperationException($"无法追加文件: {unitCubePath}");
|
||
}
|
||
|
||
// 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>
|
||
/// <summary>
|
||
/// 重置虚拟物体变换为单位矩阵(公开方法,供批处理使用)
|
||
/// </summary>
|
||
public void ResetVirtualObjectTransform()
|
||
{
|
||
if (_virtualObjectModel == null) return;
|
||
|
||
var doc = Application.ActiveDocument;
|
||
|
||
// 创建单位变换矩阵
|
||
Transform3D identityTransform = new Transform3D();
|
||
Transform3DComponents identityComponents = identityTransform.Factor();
|
||
|
||
// 应用单位变换
|
||
Units currentUnits = _virtualObjectModel.Units;
|
||
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, identityTransform, false);
|
||
|
||
LogManager.Info("已重置虚拟物体变换");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 缩放虚拟物体到目标尺寸
|
||
/// 使用DocumentModels.SetModelUnitsAndTransform方法进行模型级缩放
|
||
/// </summary>
|
||
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方向 = 物体高度
|
||
|
||
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})");
|
||
|
||
// 🔥 直接应用新的缩放值(不乘以当前缩放,避免累积)
|
||
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");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移除虚拟物体(使用 TryRemoveFile 真正删除模型)
|
||
/// </summary>
|
||
public void RemoveVirtualObject()
|
||
{
|
||
try
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
|
||
if (_virtualObjectModel != null)
|
||
{
|
||
// 动态查找模型索引
|
||
int currentIndex = doc.Models.IndexOf(_virtualObjectModel);
|
||
|
||
if (currentIndex >= 0)
|
||
{
|
||
// 使用 TryRemoveFile 真正删除模型
|
||
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)
|
||
{
|
||
LogManager.Error($"删除虚拟物体失败: {ex.Message}");
|
||
_virtualObjectModel = null;
|
||
_virtualObjectModelItem = null;
|
||
_isVirtualObjectActive = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取单位立方体文件路径
|
||
/// </summary>
|
||
private string GetUnitCubeFilePath()
|
||
{
|
||
// 方法1:从插件目录获取
|
||
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
|
||
var pluginDir = Path.GetDirectoryName(assemblyLocation);
|
||
|
||
// 尝试多个可能的位置
|
||
var possiblePaths = new[]
|
||
{
|
||
Path.Combine(pluginDir, "resources", "unit_cube.nwc"),
|
||
Path.Combine(pluginDir, "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;
|
||
}
|
||
}
|
||
|
||
LogManager.Warning($"在以下位置未找到单位立方体文件:");
|
||
foreach (var path in possiblePaths)
|
||
{
|
||
LogManager.Warning($" - {Path.GetFullPath(path)}");
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找第一个包含几何体的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();
|
||
}
|
||
}
|
||
}
|