421 lines
16 KiB
C#
421 lines
16 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 VirtualVehicleManager
|
||
{
|
||
private static VirtualVehicleManager _instance;
|
||
private static readonly object _lock = new object();
|
||
|
||
/// <summary>
|
||
/// 单例实例
|
||
/// </summary>
|
||
public static VirtualVehicleManager Instance
|
||
{
|
||
get
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
lock (_lock)
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
_instance = new VirtualVehicleManager();
|
||
}
|
||
}
|
||
}
|
||
return _instance;
|
||
}
|
||
}
|
||
|
||
private ModelItem _virtualVehicleModelItem;
|
||
private Model _virtualVehicleModel;
|
||
private bool _isVirtualVehicleActive;
|
||
|
||
// 🔥 记住虚拟车辆的尺寸变换参数(避免动态计算)
|
||
private double _currentLengthMeters = 0;
|
||
private double _currentWidthMeters = 0;
|
||
private double _currentHeightMeters = 0;
|
||
|
||
/// <summary>
|
||
/// 当前虚拟车辆ModelItem
|
||
/// </summary>
|
||
public ModelItem CurrentVirtualVehicle => _virtualVehicleModelItem;
|
||
|
||
/// <summary>
|
||
/// 虚拟车辆是否激活
|
||
/// </summary>
|
||
public bool IsVirtualVehicleActive => _isVirtualVehicleActive;
|
||
|
||
private VirtualVehicleManager()
|
||
{
|
||
_isVirtualVehicleActive = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示虚拟车辆
|
||
/// </summary>
|
||
/// <param name="lengthMeters">长度(米)</param>
|
||
/// <param name="widthMeters">宽度(米)</param>
|
||
/// <param name="heightMeters">高度(米)</param>
|
||
public void ShowVirtualVehicle(double lengthMeters, double widthMeters, double heightMeters)
|
||
{
|
||
try
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
|
||
// 检查虚拟车辆模型是否已存在
|
||
if (_virtualVehicleModel != null)
|
||
{
|
||
int currentIndex = doc.Models.IndexOf(_virtualVehicleModel);
|
||
if (currentIndex >= 0)
|
||
{
|
||
// 模型存在,显示它并更新尺寸
|
||
LogManager.Info($"虚拟车辆模型已存在(索引: {currentIndex}),显示并更新尺寸");
|
||
|
||
var modelItems = new ModelItemCollection { _virtualVehicleModelItem };
|
||
doc.Models.SetHidden(modelItems, false);
|
||
|
||
// 获取实际的几何体项
|
||
_virtualVehicleModelItem = _virtualVehicleModel.RootItem;
|
||
var geometryItem = FindFirstGeometryItem(_virtualVehicleModelItem);
|
||
if (geometryItem != null && !geometryItem.Equals(_virtualVehicleModelItem))
|
||
{
|
||
_virtualVehicleModelItem = geometryItem;
|
||
}
|
||
|
||
// 清除覆盖变换(之前动画移动和旋转的累积)
|
||
modelItems.Clear();
|
||
modelItems.Add(_virtualVehicleModelItem);
|
||
doc.Models.ResetPermanentTransform(modelItems);
|
||
|
||
// 重置变换并缩放到目标尺寸
|
||
ResetVirtualVehicleTransform();
|
||
ScaleVirtualVehicle(lengthMeters, widthMeters, heightMeters);
|
||
|
||
_isVirtualVehicleActive = true;
|
||
LogManager.Info($"虚拟车辆更新成功");
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"已保存的虚拟车辆模型不在文档中,需要重新创建");
|
||
}
|
||
}
|
||
|
||
// 模型不存在,创建新的
|
||
CreateVirtualVehicleInternal(doc, lengthMeters, widthMeters, heightMeters);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"显示虚拟车辆失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 隐藏虚拟车辆
|
||
/// </summary>
|
||
public void HideVirtualVehicle()
|
||
{
|
||
try
|
||
{
|
||
if (_virtualVehicleModelItem != null)
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { _virtualVehicleModelItem };
|
||
doc.Models.SetHidden(modelItems, true);
|
||
|
||
LogManager.Info($"已隐藏虚拟车辆模型");
|
||
}
|
||
|
||
// 隐藏时设置为非激活状态,但保留模型引用
|
||
_isVirtualVehicleActive = false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"隐藏虚拟车辆失败: {ex.Message}");
|
||
_isVirtualVehicleActive = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建虚拟车辆(内部方法)
|
||
/// </summary>
|
||
private void CreateVirtualVehicleInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
|
||
{
|
||
LogManager.Info($"=== 创建虚拟车辆 ===");
|
||
LogManager.Info($"目标尺寸: {lengthMeters:F2}m × {widthMeters:F2}m × {heightMeters:F2}m");
|
||
|
||
// 加载新的虚拟车辆模型
|
||
LoadNewVirtualVehicleModel(doc);
|
||
|
||
// 获取实际的几何体项
|
||
_virtualVehicleModelItem = _virtualVehicleModel.RootItem;
|
||
var geometryItem = FindFirstGeometryItem(_virtualVehicleModelItem);
|
||
if (geometryItem != null && !geometryItem.Equals(_virtualVehicleModelItem))
|
||
{
|
||
_virtualVehicleModelItem = geometryItem;
|
||
}
|
||
|
||
// 重置变换并缩放到目标尺寸
|
||
ResetVirtualVehicleTransform();
|
||
ScaleVirtualVehicle(lengthMeters, widthMeters, heightMeters);
|
||
|
||
// 设置状态
|
||
_isVirtualVehicleActive = true;
|
||
|
||
LogManager.Info($"虚拟车辆创建成功");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载新的虚拟车辆模型
|
||
/// </summary>
|
||
private void LoadNewVirtualVehicleModel(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("追加文件后模型数量未增加");
|
||
}
|
||
|
||
// 保存模型引用(最后一个模型就是刚追加的)
|
||
_virtualVehicleModel = doc.Models.Last();
|
||
|
||
LogManager.Info($"虚拟车辆模型: {_virtualVehicleModel.FileName}");
|
||
LogManager.Info($"虚拟车辆根项: {_virtualVehicleModel.RootItem.DisplayName}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置虚拟车辆变换为单位矩阵
|
||
/// </summary>
|
||
private void ResetVirtualVehicleTransform()
|
||
{
|
||
if (_virtualVehicleModel == null) return;
|
||
|
||
var doc = Application.ActiveDocument;
|
||
|
||
// 创建单位变换矩阵
|
||
Transform3D identityTransform = new Transform3D();
|
||
Transform3DComponents identityComponents = identityTransform.Factor();
|
||
|
||
// 应用单位变换
|
||
Units currentUnits = _virtualVehicleModel.Units;
|
||
doc.Models.SetModelUnitsAndTransform(_virtualVehicleModel, currentUnits, identityTransform, false);
|
||
|
||
LogManager.Info("已重置虚拟车辆变换");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 缩放虚拟车辆到目标尺寸
|
||
/// 使用DocumentModels.SetModelUnitsAndTransform方法进行模型级缩放
|
||
/// </summary>
|
||
private void ScaleVirtualVehicle(double lengthMeters, double widthMeters, double heightMeters)
|
||
{
|
||
if (_virtualVehicleModel == null || _virtualVehicleModelItem == 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 = widthMeters / baseSizeMeters; // X方向 = 车辆宽度
|
||
double scaleY = lengthMeters / 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 = _virtualVehicleModel.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 = _virtualVehicleModel.Units;
|
||
|
||
// 应用新的变换
|
||
doc.Models.SetModelUnitsAndTransform(_virtualVehicleModel, currentUnits, newTransform, false);
|
||
|
||
// 验证缩放结果
|
||
var newBoundingBox = _virtualVehicleModelItem.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 RemoveVirtualVehicle()
|
||
{
|
||
try
|
||
{
|
||
var doc = Application.ActiveDocument;
|
||
|
||
if (_virtualVehicleModel != null)
|
||
{
|
||
// 动态查找模型索引
|
||
int currentIndex = doc.Models.IndexOf(_virtualVehicleModel);
|
||
|
||
if (currentIndex >= 0)
|
||
{
|
||
// 使用 TryRemoveFile 真正删除模型
|
||
bool removed = doc.TryRemoveFile(currentIndex);
|
||
|
||
if (removed)
|
||
{
|
||
LogManager.Info($"已删除虚拟车辆模型(索引: {currentIndex})");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"删除虚拟车辆模型失败(索引: {currentIndex})");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"虚拟车辆模型不在文档中,无法删除");
|
||
}
|
||
}
|
||
|
||
// 清理引用
|
||
_virtualVehicleModel = null;
|
||
_virtualVehicleModelItem = null;
|
||
_isVirtualVehicleActive = false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"删除虚拟车辆失败: {ex.Message}");
|
||
_virtualVehicleModel = null;
|
||
_virtualVehicleModelItem = null;
|
||
_isVirtualVehicleActive = 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()
|
||
{
|
||
RemoveVirtualVehicle();
|
||
}
|
||
}
|
||
}
|