NavisworksTransport/PathAnimationManager.cs

370 lines
13 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.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Autodesk.Navisworks.Api;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport
{
/// <summary>
/// 路径动画管理器 - 基于TimeLiner和动态变换实现沿路径的动画效果
/// 注意由于Navisworks API限制无法直接使用Animator API因此使用OverridePermanentTransform实现动画
/// </summary>
public class PathAnimationManager
{
private ModelItem _animatedObject;
private List<Point3D> _pathPoints;
private Timer _animationTimer;
private int _currentPathIndex;
private double _animationDuration = 10.0; // 动画总时长(秒)
private DateTime _animationStartTime;
private Transform3D _originalTransform;
public PathAnimationManager()
{
_pathPoints = new List<Point3D>();
_currentPathIndex = 0;
}
/// <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));
_animatedObject = animatedObject;
_pathPoints = new List<Point3D>(pathPoints);
_animationDuration = durationSeconds;
// 保存原始变换以便重置
_originalTransform = GetCurrentTransform(_animatedObject);
LogManager.Info($"动画设置完成:对象={_animatedObject.DisplayName}, 路径点数={_pathPoints.Count}, 时长={_animationDuration}秒");
}
catch (Exception ex)
{
LogManager.Error($"设置动画失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 开始播放动画
/// </summary>
public void StartAnimation()
{
try
{
if (_animatedObject == null || _pathPoints.Count < 2)
{
throw new InvalidOperationException("请先调用SetupAnimation设置动画参数");
}
// 停止之前的动画
StopAnimation();
// 设置动态碰撞检测(简化版本)
SetupDynamicClashDetection();
// 初始化动画状态
_currentPathIndex = 0;
_animationStartTime = DateTime.Now;
// 创建并启动定时器每50ms更新一次实现流畅动画
_animationTimer = new Timer();
_animationTimer.Interval = 50; // 20 FPS
_animationTimer.Tick += AnimationTimer_Tick;
_animationTimer.Start();
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;
}
LogManager.Info("动画已停止");
}
catch (Exception ex)
{
LogManager.Error($"停止动画失败: {ex.Message}");
}
}
/// <summary>
/// 重置动画对象到原始位置
/// </summary>
public void ResetAnimation()
{
try
{
StopAnimation();
if (_animatedObject != null && _originalTransform != null)
{
var doc = NavisApplication.ActiveDocument;
var modelItems = new ModelItemCollection { _animatedObject };
doc.Models.OverridePermanentTransform(modelItems, _originalTransform, false);
// 清除碰撞高亮
doc.Models.ResetAllTemporaryMaterials();
}
_currentPathIndex = 0;
LogManager.Info("动画已重置到初始状态");
}
catch (Exception ex)
{
LogManager.Error($"重置动画失败: {ex.Message}");
}
}
/// <summary>
/// 动画定时器事件处理
/// </summary>
private void AnimationTimer_Tick(object sender, EventArgs e)
{
try
{
var elapsedTime = (DateTime.Now - _animationStartTime).TotalSeconds;
var progress = elapsedTime / _animationDuration;
if (progress >= 1.0)
{
// 动画完成
StopAnimation();
LogManager.Info("动画播放完成");
return;
}
// 计算当前应该在的位置
var currentPosition = InterpolatePosition(progress);
// 更新模型位置
UpdateObjectPosition(currentPosition);
// 检查碰撞(简化版本)
CheckAndHighlightCollisions();
}
catch (Exception ex)
{
LogManager.Error($"动画更新失败: {ex.Message}");
StopAnimation();
}
}
/// <summary>
/// 根据进度插值计算当前位置
/// </summary>
private Point3D InterpolatePosition(double progress)
{
if (_pathPoints.Count < 2)
return _pathPoints[0];
// 计算总路径长度
var totalDistance = CalculateTotalPathDistance();
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 (accumulatedDistance + segmentDistance >= targetDistance)
{
// 在这个线段内
var segmentProgress = (targetDistance - accumulatedDistance) / segmentDistance;
return InterpolatePoints(_pathPoints[i], _pathPoints[i + 1], segmentProgress);
}
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)
{
var doc = NavisApplication.ActiveDocument;
var modelItems = new ModelItemCollection { _animatedObject };
// 创建平移变换
var translation = new Vector3D(newPosition.X, newPosition.Y, newPosition.Z);
var transform = Transform3D.CreateTranslation(translation);
// 应用变换
doc.Models.OverridePermanentTransform(modelItems, transform, false);
}
/// <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>
/// 资源清理
/// </summary>
public void Dispose()
{
StopAnimation();
ResetAnimation();
}
}
}