NavisworksTransport/src/Core/VirtualVehicleManager.cs

421 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}
}