837 lines
32 KiB
C#
837 lines
32 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Windows.Forms;
|
||
using Autodesk.Navisworks.Api;
|
||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||
|
||
namespace NavisworksTransport
|
||
{
|
||
/// <summary>
|
||
/// 定义动画播放的状态
|
||
/// </summary>
|
||
public enum AnimationState
|
||
{
|
||
Idle, // 空闲,未生成动画
|
||
Ready, // 已就绪,动画已生成但未播放
|
||
Playing, // 播放中
|
||
Paused, // 暂停
|
||
Stopped, // 已停止
|
||
Finished // 已完成
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径动画管理器 - 基于TimeLiner和动态变换实现沿路径的动画效果
|
||
/// 注意:由于Navisworks API限制,无法直接使用Animator API,因此使用OverridePermanentTransform实现动画
|
||
/// 已集成 TimeLiner 功能,支持在 TimeLiner 中显示和管理动画任务
|
||
/// </summary>
|
||
public class PathAnimationManager
|
||
{
|
||
private ModelItem _animatedObject;
|
||
private List<Point3D> _pathPoints;
|
||
private Timer _animationTimer;
|
||
private double _animationDuration = 10.0; // 动画总时长(秒)
|
||
private DateTime _animationStartTime;
|
||
private Transform3D _originalTransform;
|
||
private Point3D _originalCenter; // 存储部件的原始中心位置
|
||
private Point3D _currentPosition; // 存储部件的当前位置
|
||
private AnimationState _currentState = AnimationState.Idle;
|
||
|
||
// TimeLiner 集成
|
||
private TimeLinerIntegrationManager _timeLinerManager;
|
||
private string _currentTaskId;
|
||
|
||
// --- 新增事件 ---
|
||
/// <summary>
|
||
/// 当动画状态发生改变时触发
|
||
/// </summary>
|
||
public event EventHandler<AnimationState> StateChanged;
|
||
|
||
/// <summary>
|
||
/// 当动画进度更新时触发 (0-100)
|
||
/// </summary>
|
||
public event EventHandler<int> ProgressChanged;
|
||
|
||
// 动画完成事件 (旧版,保留兼容性)
|
||
public event EventHandler AnimationCompleted;
|
||
|
||
public PathAnimationManager()
|
||
{
|
||
_pathPoints = new List<Point3D>();
|
||
|
||
// 初始化 TimeLiner 集成
|
||
try
|
||
{
|
||
_timeLinerManager = new TimeLinerIntegrationManager();
|
||
LogManager.Info("PathAnimationManager 已集成 TimeLiner 功能");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"TimeLiner 集成初始化失败,将使用基础动画功能: {ex.Message}");
|
||
_timeLinerManager = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置动画参数
|
||
/// </summary>
|
||
/// <param name="animatedObject">要动画化的模型对象</param>
|
||
/// <param name="pathPoints">路径点列表</param>
|
||
/// <param name="durationSeconds">动画持续时间(秒)</param>
|
||
public void SetupAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0)
|
||
{
|
||
try
|
||
{
|
||
if (animatedObject == null)
|
||
throw new ArgumentNullException(nameof(animatedObject));
|
||
|
||
if (pathPoints == null || pathPoints.Count < 2)
|
||
throw new ArgumentException("路径点数量必须至少为2个", nameof(pathPoints));
|
||
|
||
// 添加路径点坐标有效性验证
|
||
LogManager.Info("=== 动画管理器坐标验证 ===");
|
||
bool hasInvalidCoordinates = false;
|
||
for (int i = 0; i < pathPoints.Count; i++)
|
||
{
|
||
var point = pathPoints[i];
|
||
bool isValid = !double.IsNaN(point.X) && !double.IsNaN(point.Y) && !double.IsNaN(point.Z) &&
|
||
!double.IsInfinity(point.X) && !double.IsInfinity(point.Y) && !double.IsInfinity(point.Z);
|
||
|
||
LogManager.Info($"路径点[{i}]坐标验证: ({point.X:F6},{point.Y:F6},{point.Z:F6}) - {(isValid ? "有效" : "无效")}");
|
||
|
||
if (!isValid)
|
||
{
|
||
hasInvalidCoordinates = true;
|
||
LogManager.Error($"检测到无效坐标: 路径点[{i}] = ({point.X},{point.Y},{point.Z})");
|
||
}
|
||
|
||
// 检查是否为零坐标(可能表示数据丢失)
|
||
if (isValid && point.X == 0.0 && point.Y == 0.0 && point.Z == 0.0)
|
||
{
|
||
LogManager.Warning($"路径点[{i}]坐标为零,可能存在数据丢失问题");
|
||
}
|
||
}
|
||
|
||
if (hasInvalidCoordinates)
|
||
{
|
||
throw new ArgumentException("路径点包含无效坐标(NaN或无穷大),无法创建动画");
|
||
}
|
||
|
||
LogManager.Info("=== 坐标验证完成 ===");
|
||
|
||
_animatedObject = animatedObject;
|
||
_pathPoints = new List<Point3D>(pathPoints);
|
||
_animationDuration = durationSeconds;
|
||
|
||
// 保存原始变换以便重置
|
||
_originalTransform = GetCurrentTransform(_animatedObject);
|
||
|
||
// 保存车辆的原始中心位置
|
||
var originalBoundingBox = animatedObject.BoundingBox();
|
||
_originalCenter = originalBoundingBox.Center;
|
||
|
||
// 关键修复:将车辆立即移动到路径起点
|
||
// 这样动画就从路径起点开始,而不是从车辆当前位置开始
|
||
MoveVehicleToPathStart();
|
||
|
||
// 记录文档单位信息
|
||
var documentUnits = GetDocumentUnitsInfo();
|
||
|
||
// 添加详细的调试信息
|
||
LogManager.Info($"动画设置完成:对象={_animatedObject.DisplayName}, 路径点数={_pathPoints.Count}, 时长={_animationDuration}秒");
|
||
LogManager.Info($"文档单位: {documentUnits}");
|
||
LogManager.Info($"路径起点: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})");
|
||
LogManager.Info($"路径终点: ({_pathPoints[_pathPoints.Count-1].X:F2},{_pathPoints[_pathPoints.Count-1].Y:F2},{_pathPoints[_pathPoints.Count-1].Z:F2})");
|
||
|
||
var totalDist = CalculateTotalPathDistance();
|
||
LogManager.Info($"路径总长度: {totalDist:F2}");
|
||
LogManager.Info($"车辆已移动到路径起点,动画将从起点开始");
|
||
LogManager.Info($"=== 调试信息结束 ===");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"设置动画失败: {ex.Message}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将车辆移动到路径起点
|
||
/// </summary>
|
||
private void MoveVehicleToPathStart()
|
||
{
|
||
try
|
||
{
|
||
if (_pathPoints.Count == 0) return;
|
||
|
||
var doc = NavisApplication.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { _animatedObject };
|
||
|
||
// 计算从部件原始中心到路径起点的偏移
|
||
var startOffset = new Vector3D(
|
||
_pathPoints[0].X - _originalCenter.X,
|
||
_pathPoints[0].Y - _originalCenter.Y,
|
||
_pathPoints[0].Z - _originalCenter.Z
|
||
);
|
||
|
||
// 创建变换并应用
|
||
var startTransform = Transform3D.CreateTranslation(startOffset);
|
||
doc.Models.OverridePermanentTransform(modelItems, startTransform, false);
|
||
|
||
// 更新当前位置为路径起点
|
||
_currentPosition = _pathPoints[0];
|
||
|
||
LogManager.Info($"部件已移动到路径起点,偏移: ({startOffset.X:F2},{startOffset.Y:F2},{startOffset.Z:F2})");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"移动部件到路径起点失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始播放动画
|
||
/// </summary>
|
||
public void StartAnimation()
|
||
{
|
||
try
|
||
{
|
||
if (_animatedObject == null || _pathPoints.Count < 2)
|
||
{
|
||
throw new InvalidOperationException("请先调用SetupAnimation设置动画参数");
|
||
}
|
||
|
||
// 停止之前的动画
|
||
StopAnimation();
|
||
|
||
// 创建 TimeLiner 任务(如果可用)
|
||
if (_timeLinerManager != null && _timeLinerManager.IsTimeLinerAvailable)
|
||
{
|
||
var taskName = $"{_animatedObject.DisplayName}_运输_{DateTime.Now:HHmmss}";
|
||
var duration = TimeSpan.FromSeconds(_animationDuration);
|
||
|
||
LogManager.Info($"创建 TimeLiner 任务: {taskName}");
|
||
|
||
_currentTaskId = _timeLinerManager.CreateTransportTask(
|
||
taskName,
|
||
_pathPoints,
|
||
duration,
|
||
_animatedObject);
|
||
|
||
if (!string.IsNullOrEmpty(_currentTaskId))
|
||
{
|
||
LogManager.Info($"✓ TimeLiner 任务创建成功: {taskName} (ID: {_currentTaskId})");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("TimeLiner 任务创建失败,继续使用基础动画功能");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("TimeLiner 不可用,使用基础动画功能");
|
||
}
|
||
|
||
// 设置动态碰撞检测(简化版本)
|
||
SetupDynamicClashDetection();
|
||
|
||
// 初始化动画状态
|
||
_animationStartTime = DateTime.Now;
|
||
|
||
// 创建并启动定时器(每50ms更新一次,实现流畅动画)
|
||
_animationTimer = new Timer();
|
||
_animationTimer.Interval = 50; // 20 FPS
|
||
_animationTimer.Tick += AnimationTimer_Tick;
|
||
_animationTimer.Start();
|
||
|
||
SetState(AnimationState.Playing);
|
||
LogManager.Info("动画开始播放");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"启动动画失败: {ex.Message}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止动画
|
||
/// </summary>
|
||
public void StopAnimation()
|
||
{
|
||
try
|
||
{
|
||
if (_animationTimer != null)
|
||
{
|
||
_animationTimer.Stop();
|
||
_animationTimer.Dispose();
|
||
_animationTimer = null;
|
||
|
||
SetState(AnimationState.Stopped);
|
||
LogManager.Info("动画已停止");
|
||
}
|
||
|
||
// 更新 TimeLiner 任务状态
|
||
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
|
||
{
|
||
_timeLinerManager.UpdateTaskProgress(_currentTaskId, 0.0, AnimationState.Stopped);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"停止动画失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置动画对象到原始位置
|
||
/// </summary>
|
||
public void ResetAnimation()
|
||
{
|
||
try
|
||
{
|
||
StopAnimation(); // 停止当前动画
|
||
|
||
// 恢复对象的原始变换
|
||
if (_animatedObject != null)
|
||
{
|
||
var modelItems = new ModelItemCollection { _animatedObject };
|
||
NavisApplication.ActiveDocument.Models.OverridePermanentTransform(modelItems, _originalTransform, false);
|
||
LogManager.Info($"部件 {_animatedObject.DisplayName} 已重置到原始位置");
|
||
}
|
||
|
||
ProgressChanged?.Invoke(this, 0); // 重置进度条
|
||
SetState(AnimationState.Ready); // 重置后回到就绪状态
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"重置动画失败: {ex.Message}");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 动画定时器事件处理
|
||
/// </summary>
|
||
private void AnimationTimer_Tick(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
double elapsedSeconds = (DateTime.Now - _animationStartTime).TotalSeconds;
|
||
double progress = Math.Min(elapsedSeconds / _animationDuration, 1.0);
|
||
|
||
// 更新UI进度条
|
||
ProgressChanged?.Invoke(this, (int)(progress * 100));
|
||
|
||
// 同步进度到 TimeLiner(如果可用)
|
||
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
|
||
{
|
||
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
|
||
}
|
||
|
||
Point3D newPosition = InterpolatePosition(progress);
|
||
UpdateObjectPosition(newPosition);
|
||
|
||
// 检查碰撞
|
||
CheckAndHighlightCollisions();
|
||
|
||
if (progress >= 1.0)
|
||
{
|
||
StopAnimation();
|
||
SetState(AnimationState.Finished); // 标记为完成
|
||
AnimationCompleted?.Invoke(this, EventArgs.Empty); // 触发旧版完成事件
|
||
LogManager.Info("动画播放完成");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"动画帧更新失败: {ex.Message}");
|
||
StopAnimation(); // 发生错误时停止动画
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据进度插值计算当前位置
|
||
/// </summary>
|
||
private Point3D InterpolatePosition(double progress)
|
||
{
|
||
if (_pathPoints.Count < 2)
|
||
return _pathPoints[0];
|
||
|
||
// 确保进度在0-1范围内
|
||
progress = Math.Max(0.0, Math.Min(1.0, progress));
|
||
|
||
// 如果进度达到100%,直接返回终点
|
||
if (progress >= 1.0)
|
||
{
|
||
return _pathPoints[_pathPoints.Count - 1];
|
||
}
|
||
|
||
// 计算总路径长度
|
||
var totalDistance = CalculateTotalPathDistance();
|
||
|
||
// 检查总距离是否有效
|
||
if (totalDistance <= 0.0 || double.IsNaN(totalDistance) || double.IsInfinity(totalDistance))
|
||
{
|
||
LogManager.Error($"路径总长度无效: {totalDistance},返回起点坐标");
|
||
return _pathPoints[0];
|
||
}
|
||
|
||
var targetDistance = totalDistance * progress;
|
||
|
||
// 找到当前应该在哪两个点之间
|
||
var accumulatedDistance = 0.0;
|
||
for (int i = 0; i < _pathPoints.Count - 1; i++)
|
||
{
|
||
var segmentDistance = CalculateDistance(_pathPoints[i], _pathPoints[i + 1]);
|
||
|
||
// 检查段距离是否有效
|
||
if (double.IsNaN(segmentDistance) || double.IsInfinity(segmentDistance))
|
||
{
|
||
LogManager.Error($"路径段[{i}-{i+1}]距离无效: {segmentDistance},跳过此段");
|
||
continue;
|
||
}
|
||
|
||
if (accumulatedDistance + segmentDistance >= targetDistance)
|
||
{
|
||
// 在这个线段内
|
||
var segmentProgress = segmentDistance > 0 ? (targetDistance - accumulatedDistance) / segmentDistance : 0.0;
|
||
// 确保段内进度也在0-1范围内
|
||
segmentProgress = Math.Max(0.0, Math.Min(1.0, segmentProgress));
|
||
|
||
var interpolatedPoint = InterpolatePoints(_pathPoints[i], _pathPoints[i + 1], segmentProgress);
|
||
|
||
// 验证插值结果
|
||
if (double.IsNaN(interpolatedPoint.X) || double.IsNaN(interpolatedPoint.Y) || double.IsNaN(interpolatedPoint.Z))
|
||
{
|
||
LogManager.Error($"插值计算结果无效: ({interpolatedPoint.X},{interpolatedPoint.Y},{interpolatedPoint.Z}),返回起点");
|
||
return _pathPoints[0];
|
||
}
|
||
|
||
return interpolatedPoint;
|
||
}
|
||
|
||
accumulatedDistance += segmentDistance;
|
||
}
|
||
|
||
// 如果到达这里,返回最后一个点
|
||
return _pathPoints[_pathPoints.Count - 1];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在两点间插值
|
||
/// </summary>
|
||
private Point3D InterpolatePoints(Point3D point1, Point3D point2, double t)
|
||
{
|
||
return new Point3D(
|
||
point1.X + (point2.X - point1.X) * t,
|
||
point1.Y + (point2.Y - point1.Y) * t,
|
||
point1.Z + (point2.Z - point1.Z) * t
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算路径总长度
|
||
/// </summary>
|
||
private double CalculateTotalPathDistance()
|
||
{
|
||
var totalDistance = 0.0;
|
||
for (int i = 0; i < _pathPoints.Count - 1; i++)
|
||
{
|
||
totalDistance += CalculateDistance(_pathPoints[i], _pathPoints[i + 1]);
|
||
}
|
||
return totalDistance;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算两点间距离
|
||
/// </summary>
|
||
private double CalculateDistance(Point3D point1, Point3D point2)
|
||
{
|
||
var dx = point2.X - point1.X;
|
||
var dy = point2.Y - point1.Y;
|
||
var dz = point2.Z - point1.Z;
|
||
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新对象位置
|
||
/// </summary>
|
||
private void UpdateObjectPosition(Point3D newPosition)
|
||
{
|
||
try
|
||
{
|
||
var doc = NavisApplication.ActiveDocument;
|
||
var modelItems = new ModelItemCollection { _animatedObject };
|
||
|
||
// 正确的增量变换:计算从当前位置到新位置的偏移
|
||
var incrementalOffset = new Vector3D(
|
||
newPosition.X - _currentPosition.X,
|
||
newPosition.Y - _currentPosition.Y,
|
||
newPosition.Z - _currentPosition.Z
|
||
);
|
||
|
||
// 创建增量变换
|
||
var incrementalTransform = Transform3D.CreateTranslation(incrementalOffset);
|
||
|
||
// 应用增量变换(不重置之前的变换)
|
||
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
|
||
|
||
LogManager.Debug($"部件位置更新:从({_currentPosition.X:F2},{_currentPosition.Y:F2},{_currentPosition.Z:F2})到({newPosition.X:F2},{newPosition.Y:F2},{newPosition.Z:F2}),增量偏移({incrementalOffset.X:F2},{incrementalOffset.Y:F2},{incrementalOffset.Z:F2})");
|
||
|
||
// 更新当前位置
|
||
_currentPosition = newPosition;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"更新部件位置失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从车辆变换矩阵中获取真实的缩放系数
|
||
/// 注意:此方法已废弃但保留,用于车辆模型的缩放处理。
|
||
/// 现在使用场馆部件进行动画,通常不需要复杂的缩放计算。
|
||
/// </summary>
|
||
private double GetVehicleScaleFactor()
|
||
{
|
||
try
|
||
{
|
||
var transform = _animatedObject.Transform;
|
||
var linear = transform.Linear;
|
||
|
||
// 获取变换矩阵的第一行的长度作为缩放系数
|
||
// 变换矩阵: (a, b, c)
|
||
// (d, e, f)
|
||
// (g, h, i)
|
||
// 缩放系数 ≈ sqrt(a² + b² + c²)
|
||
var scaleX = Math.Sqrt(
|
||
linear.Get(0, 0) * linear.Get(0, 0) +
|
||
linear.Get(0, 1) * linear.Get(0, 1) +
|
||
linear.Get(0, 2) * linear.Get(0, 2)
|
||
);
|
||
|
||
LogManager.Debug($"车辆缩放系数计算: {scaleX:F2} (变换矩阵第一行模长)");
|
||
|
||
// 如果缩放系数接近1550(39.37²),说明是米到英寸的平方缩放
|
||
if (Math.Abs(scaleX - 1550.0) < 10.0)
|
||
{
|
||
var linearScale = Math.Sqrt(scaleX);
|
||
LogManager.Info($"检测到车辆使用平方缩放系数: {scaleX:F2},已修正为线性缩放系数: {linearScale:F2}");
|
||
return linearScale;
|
||
}
|
||
// 如果缩放系数接近39.37,说明是简单的米到英寸缩放
|
||
else if (Math.Abs(scaleX - 39.37) < 1.0)
|
||
{
|
||
LogManager.Info($"检测到车辆使用线性缩放系数: {scaleX:F2}");
|
||
return scaleX;
|
||
}
|
||
// 如果接近1,说明没有缩放
|
||
else if (Math.Abs(scaleX - 1.0) < 0.1)
|
||
{
|
||
LogManager.Info($"车辆无缩放: {scaleX:F2}");
|
||
return 1.0;
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"未知的车辆缩放系数: {scaleX:F2},使用原值");
|
||
return scaleX;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"获取车辆缩放系数失败: {ex.Message},使用默认值1.0");
|
||
return 1.0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取文档单位信息(用于日志记录)
|
||
/// </summary>
|
||
private string GetDocumentUnitsInfo()
|
||
{
|
||
try
|
||
{
|
||
var doc = NavisApplication.ActiveDocument;
|
||
var units = doc.Units;
|
||
LogManager.Debug($"[单位检测] 文档单位: {units}");
|
||
return units.ToString();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"获取文档单位信息失败: {ex.Message}");
|
||
return "Unknown";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取模型当前变换
|
||
/// </summary>
|
||
private Transform3D GetCurrentTransform(ModelItem item)
|
||
{
|
||
// 获取包围盒中心作为参考点
|
||
var boundingBox = item.BoundingBox();
|
||
var center = boundingBox.Center;
|
||
|
||
// 创建基于中心点的单位变换
|
||
return Transform3D.CreateTranslation(new Vector3D(center.X, center.Y, center.Z));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置动态碰撞检测(简化版本,因为Clash Detective API在2017版本中有限制)
|
||
/// </summary>
|
||
private void SetupDynamicClashDetection()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("动态碰撞检测设置完成(简化版本)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"设置动态碰撞检测失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查并高亮碰撞(简化版本)
|
||
/// </summary>
|
||
private void CheckAndHighlightCollisions()
|
||
{
|
||
try
|
||
{
|
||
// 简化的碰撞检测:检查动画对象是否与其他对象的包围盒相交
|
||
var doc = NavisApplication.ActiveDocument;
|
||
var animatedBoundingBox = _animatedObject.BoundingBox();
|
||
|
||
// 获取所有其他有几何体的对象
|
||
var allItems = doc.Models.RootItemDescendantsAndSelf
|
||
.Where(item => item.HasGeometry && !item.Equals(_animatedObject))
|
||
.ToList();
|
||
|
||
var collidingItems = new ModelItemCollection();
|
||
|
||
foreach (var item in allItems)
|
||
{
|
||
var itemBoundingBox = item.BoundingBox();
|
||
if (BoundingBoxesIntersect(animatedBoundingBox, itemBoundingBox))
|
||
{
|
||
collidingItems.Add(item);
|
||
}
|
||
}
|
||
|
||
// 清除之前的高亮
|
||
doc.Models.ResetAllTemporaryMaterials();
|
||
|
||
// 高亮碰撞对象(红色)
|
||
if (collidingItems.Count > 0)
|
||
{
|
||
doc.Models.OverrideTemporaryColor(collidingItems, Color.Red);
|
||
LogManager.Debug($"检测到 {collidingItems.Count} 处碰撞");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Debug($"碰撞检测更新失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查两个包围盒是否相交
|
||
/// </summary>
|
||
private bool BoundingBoxesIntersect(BoundingBox3D box1, BoundingBox3D box2)
|
||
{
|
||
return !(box1.Max.X < box2.Min.X || box2.Max.X < box1.Min.X ||
|
||
box1.Max.Y < box2.Min.Y || box2.Max.Y < box1.Min.Y ||
|
||
box1.Max.Z < box2.Min.Z || box2.Max.Z < box1.Min.Z);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置动画持续时间
|
||
/// </summary>
|
||
public void SetAnimationDuration(double durationSeconds)
|
||
{
|
||
_animationDuration = Math.Max(1.0, durationSeconds);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取动画状态
|
||
/// </summary>
|
||
public bool IsAnimating => _animationTimer != null && _animationTimer.Enabled;
|
||
|
||
/// <summary>
|
||
/// 获取 TimeLiner 是否可用
|
||
/// </summary>
|
||
public bool IsTimeLinerAvailable => _timeLinerManager?.IsTimeLinerAvailable ?? false;
|
||
|
||
/// <summary>
|
||
/// 获取当前动画状态
|
||
/// </summary>
|
||
public AnimationState CurrentState => _currentState;
|
||
|
||
/// <summary>
|
||
/// 获取当前 TimeLiner 任务ID
|
||
/// </summary>
|
||
public string CurrentTaskId => _currentTaskId;
|
||
|
||
/// <summary>
|
||
/// 获取 TimeLiner 集成管理器
|
||
/// </summary>
|
||
public TimeLinerIntegrationManager TimeLinerManager => _timeLinerManager;
|
||
|
||
/// <summary>
|
||
/// 资源清理
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
StopAnimation();
|
||
ResetAnimation();
|
||
|
||
// 清理 TimeLiner 资源
|
||
if (_timeLinerManager != null)
|
||
{
|
||
try
|
||
{
|
||
_timeLinerManager.Dispose();
|
||
_timeLinerManager = null;
|
||
LogManager.Info("TimeLiner 集成资源已清理");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"清理 TimeLiner 资源时出现警告: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 快速测试TimeLiner API的可用性
|
||
/// </summary>
|
||
public bool TestTimeLinerAPI()
|
||
{
|
||
try
|
||
{
|
||
var doc = NavisApplication.ActiveDocument;
|
||
|
||
// 尝试访问TimeLiner
|
||
var timeliner = doc.Timeliner;
|
||
if (timeliner != null)
|
||
{
|
||
LogManager.Info("TimeLiner API基本可用");
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("TimeLiner API不可用");
|
||
return false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Info($"TimeLiner API测试失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 简化的动画设置方法,支持直接传入部件和路径
|
||
/// </summary>
|
||
/// <param name="component">部件模型</param>
|
||
/// <param name="pathPoints">路径点列表</param>
|
||
/// <param name="durationSeconds">动画持续时间(秒)</param>
|
||
public bool SetupSimpleAnimation(ModelItem component, List<Point3D> pathPoints, double durationSeconds = 10.0)
|
||
{
|
||
try
|
||
{
|
||
if (component == null)
|
||
{
|
||
LogManager.Error("部件模型不能为空");
|
||
return false;
|
||
}
|
||
|
||
if (pathPoints == null || pathPoints.Count < 2)
|
||
{
|
||
LogManager.Error("路径点数量必须至少为2个");
|
||
return false;
|
||
}
|
||
|
||
// 使用现有的SetupAnimation方法
|
||
SetupAnimation(component, pathPoints, durationSeconds);
|
||
|
||
LogManager.Info($"简化动画设置成功:部件={component.DisplayName}, 路径点数={pathPoints.Count}, 时长={durationSeconds}秒");
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"简化动画设置失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前选中的部件模型(简化版本)
|
||
/// </summary>
|
||
/// <returns>选中的部件模型,如果没有选中或选中多个则返回null</returns>
|
||
public static ModelItem GetSelectedComponent()
|
||
{
|
||
try
|
||
{
|
||
var doc = NavisApplication.ActiveDocument;
|
||
var selectedItems = doc.CurrentSelection.SelectedItems;
|
||
|
||
if (selectedItems.Count == 1)
|
||
{
|
||
var item = selectedItems.First;
|
||
if (item.HasGeometry)
|
||
{
|
||
LogManager.Info($"已获取选中部件: {item.DisplayName}");
|
||
return item;
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("选中的项目没有几何体,可能不适合用于动画");
|
||
return null;
|
||
}
|
||
}
|
||
else if (selectedItems.Count == 0)
|
||
{
|
||
LogManager.Info("没有选中任何项目");
|
||
return null;
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info($"选中了{selectedItems.Count}个项目,请只选择一个部件");
|
||
return null;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"获取选中部件失败: {ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置并触发状态变更事件
|
||
/// </summary>
|
||
/// <param name="newState">新的动画状态</param>
|
||
private void SetState(AnimationState newState)
|
||
{
|
||
if (_currentState != newState)
|
||
{
|
||
_currentState = newState;
|
||
StateChanged?.Invoke(this, _currentState);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建动画
|
||
/// </summary>
|
||
/// <param name="animatedObject">要动画化的模型对象</param>
|
||
/// <param name="pathPoints">路径点列表</param>
|
||
/// <param name="durationSeconds">动画持续时间(秒)</param>
|
||
public void CreateAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0)
|
||
{
|
||
SetupAnimation(animatedObject, pathPoints, durationSeconds);
|
||
SetState(AnimationState.Ready);
|
||
}
|
||
}
|
||
} |