using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Clash;
using NavisworksTransport.Core;
using NavisworksTransport.Commands;
using NavisworksTransport.UI.WPF.Collections;
using NavisworksTransport.Utils;
using Color = Autodesk.Navisworks.Api.Color;
namespace NavisworksTransport.UI.WPF.ViewModels
{
///
/// 碰撞报告数据
///
public class CollisionReportData
{
public bool IsValid { get; set; }
public string ErrorMessage { get; set; } = string.Empty;
public DateTime GeneratedTime { get; set; }
public string AnimatedObjectName { get; set; } = string.Empty;
public string PathName { get; set; } = string.Empty;
public int TotalTests { get; set; }
public int TotalCollisions { get; set; }
public List Tests { get; set; } = new List();
}
///
/// 碰撞测试信息
///
public class CollisionTestInfo
{
public string TestName { get; set; } = string.Empty;
public string TestType { get; set; } = string.Empty;
public double Tolerance { get; set; }
public int CollisionCount { get; set; }
public string Status { get; set; } = string.Empty;
public List Collisions { get; set; } = new List();
}
///
/// 碰撞详细信息
///
public class CollisionDetailInfo
{
public string CollisionId { get; set; } = string.Empty;
public string Object1Name { get; set; } = string.Empty;
public string Object1Position { get; set; } = string.Empty;
public string Object2Name { get; set; } = string.Empty;
public string Object2Position { get; set; } = string.Empty;
public string CollisionCenter { get; set; } = string.Empty;
public double Distance { get; set; }
public string Status { get; set; } = string.Empty;
}
///
/// 手工指定的碰撞检测对象
///
public class ManualCollisionTargetViewModel
{
public ManualCollisionTargetViewModel(ModelItem modelItem, string displayName, string modelPath)
{
ModelItem = modelItem ?? throw new ArgumentNullException(nameof(modelItem));
DisplayName = displayName;
ModelPath = modelPath;
InstanceGuid = modelItem.InstanceGuid;
}
public ModelItem ModelItem { get; }
public string DisplayName { get; }
public string ModelPath { get; }
public Guid InstanceGuid { get; }
}
///
/// ClashDetective碰撞检测结果视图模型
///
public class ClashDetectiveResultViewModel
{
private readonly Action _refreshCallback;
public ClashDetectiveResultViewModel(ClashDetectiveResultRecord record, Action refreshCallback)
{
Record = record ?? throw new ArgumentNullException(nameof(record));
_refreshCallback = refreshCallback ?? throw new ArgumentNullException(nameof(refreshCallback));
ViewCommand = new RelayCommand(() => ExecuteView());
ReportCommand = new RelayCommand(() => ExecuteReport());
DeleteCommand = new RelayCommand(() => ExecuteDelete());
}
public ClashDetectiveResultRecord Record { get; }
public string TestTimeDisplay => Record.TestTime.ToString("MM-dd HH:mm:ss");
public string CollisionCountDisplay => $"{Record.CollisionCount}个";
public ICommand ViewCommand { get; }
public ICommand ReportCommand { get; }
public ICommand DeleteCommand { get; }
private void ExecuteView()
{
// 高亮显示该次检测的碰撞对象
var clashIntegration = ClashDetectiveIntegration.Instance;
ModelHighlightHelper.HighlightClashDetectiveResults(Record.TestName, clashIntegration.GetCurrentPathClashResults);
}
private void ExecuteReport()
{
// 打开该次检测的详细报告
try
{
var testName = Record.TestName;
LogManager.Info($"[历史报告] 开始生成历史碰撞报告: {testName}");
// 直接使用现有的报告生成命令,但指定测试名称
var command = GenerateCollisionReportCommand.CreateComprehensive(autoHighlight: false);
command.SetTestName(testName);
command.ExecuteAsync();
}
catch (Exception ex)
{
LogManager.Error($"[历史报告] 生成历史报告失败: {ex.Message}", ex);
System.Windows.MessageBox.Show($"生成历史报告失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
}
private void ExecuteDelete()
{
// 删除该次检测记录
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase != null)
{
pathDatabase.DeleteClashDetectiveResult(Record.Id);
// 通过回调刷新列表
_refreshCallback?.Invoke();
}
}
}
///
/// 动画控制视图模型
/// 专门处理路径动画播放和碰撞检测功能
///
public class AnimationControlViewModel : ViewModelBase, IDisposable
{
#region 私有字段
private readonly Core.Animation.PathAnimationManager _pathAnimationManager;
private readonly ClashDetectiveIntegration _clashIntegration;
private readonly UIStateManager _uiStateManager;
// 动画参数相关字段(从配置初始化)
private ObservableCollection _availableFrameRates;
private int _selectedFrameRate;
private double _animationDuration;
private bool _canStartAnimation = true;
private bool _canPauseAnimation = false;
private bool _canStopAnimation = false;
// 碰撞检测相关字段
private CollisionReportResult _lastGeneratedReport; // 缓存最后生成的报告
// 碰撞检测参数字段(从配置初始化)
private double _detectionGap; // 检测间隙(米)
private int _animationFrameRate; // 动画帧率(FPS)
// 当前选中路径
private PathRouteViewModel _currentPathRoute;
// 移动物体相关字段
private ModelItem _selectedAnimatedObject;
private string _selectedAnimatedObjectName;
private bool _hasSelectedAnimatedObject = false;
private bool _canGenerateAnimation = false;
// 虚拟车辆相关字段
private bool _useVirtualVehicle = false; // 使用虚拟车辆
private double _virtualVehicleLength; // 虚拟车辆长度(米)
private double _virtualVehicleWidth; // 虚拟车辆宽度(米)
private double _virtualVehicleHeight; // 虚拟车辆高度(米)
// 手工碰撞对象相关字段
private bool _isManualCollisionTargetEnabled = false;
private ObservableCollection _manualCollisionTargets;
private string _manualCollisionTargetSummary = "未指定碰撞检测对象";
private DateTime? _manualTargetsLastSyncTime;
private const int ManualCollisionTargetLimit = 60;
private const string ManualTargetsHighlightCategory = ModelHighlightHelper.ManualTargetsCategory;
private const string CollisionResultsHighlightCategory = ModelHighlightHelper.CollisionResultsCategory;
#endregion
#region 公共属性
///
/// 可用帧率集合
///
public ObservableCollection AvailableFrameRates
{
get => _availableFrameRates;
set => SetProperty(ref _availableFrameRates, value);
}
///
/// 选中的帧率
///
public int SelectedFrameRate
{
get => _selectedFrameRate;
set
{
if (SetProperty(ref _selectedFrameRate, value))
{
// 同步更新AnimationFrameRate字段
_animationFrameRate = value;
// 帧率变化时,步长需要重新计算(步长=路径长度/(帧率*时长))
OnPropertyChanged(nameof(CollisionDetectionAccuracy));
}
}
}
///
/// 动画持续时间(秒)
///
public double AnimationDuration
{
get => _animationDuration;
set
{
if (SetProperty(ref _animationDuration, value))
{
// 动画时长变化时,步长和速度需要重新计算
OnPropertyChanged(nameof(CollisionDetectionAccuracy));
OnPropertyChanged(nameof(MovementSpeed));
}
}
}
///
/// 是否可以开始动画
///
public bool CanStartAnimation
{
get => _canStartAnimation;
set => SetProperty(ref _canStartAnimation, value);
}
///
/// 是否可以暂停动画
///
public bool CanPauseAnimation
{
get => _canPauseAnimation;
set => SetProperty(ref _canPauseAnimation, value);
}
///
/// 是否可以停止动画
///
public bool CanStopAnimation
{
get => _canStopAnimation;
set => SetProperty(ref _canStopAnimation, value);
}
///
/// 是否有碰撞检测结果
///
private bool _hasClashDetectiveResults;
public bool HasClashDetectiveResults
{
get => _hasClashDetectiveResults;
set
{
if (SetProperty(ref _hasClashDetectiveResults, value))
{
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
}
}
}
///
/// 碰撞检测精度(米) - 步长,根据路径长度、帧率和时长计算得出
/// 如果无法计算则返回0,表示数据不完整
///
public double CollisionDetectionAccuracy
{
get
{
var pathLength = GetPathLength();
if (pathLength > 0 && _animationFrameRate > 0 && _animationDuration > 0)
{
// 步长 = 路径长度 / (帧率 * 时长)
return Math.Round(pathLength / (_animationFrameRate * _animationDuration), 3);
}
// 无法计算时返回0
// 注意:如果路径为空(pathLength==0),这是正常的初始状态,不输出警告
// 只有在配置参数异常时才输出警告
if (pathLength > 0 && (_animationFrameRate <= 0 || _animationDuration <= 0))
{
LogManager.Warning($"[AnimationControlViewModel] 无法计算碰撞检测精度 - 路径长度:{pathLength}m, 帧率:{_animationFrameRate}fps, 时长:{_animationDuration}s");
}
return 0;
}
}
///
/// 运动速度(米/秒) - 根据路径长度和时长计算得出
/// 如果无法计算则返回0,表示数据不完整
///
public double MovementSpeed
{
get
{
var pathLength = GetPathLength();
if (pathLength > 0 && _animationDuration > 0)
{
// 速度 = 路径长度 / 时长
return Math.Round(pathLength / _animationDuration, 2);
}
// 无法计算时返回0
// 注意:如果路径为空(pathLength==0),这是正常的初始状态,不输出警告
// 只有在配置参数异常时才输出警告
if (pathLength > 0 && _animationDuration <= 0)
{
LogManager.Warning($"[AnimationControlViewModel] 无法计算运动速度 - 路径长度:{pathLength}m, 时长:{_animationDuration}s");
}
return 0;
}
}
///
/// 路径长度(米) - 计算得出
///
public double PathLength
{
get
{
return GetPathLength();
}
}
///
/// 检测间隙(米)
///
public double DetectionGap
{
get => _detectionGap;
set
{
// 限制精度到2位小数
var roundedValue = Math.Round(value, 2);
SetProperty(ref _detectionGap, roundedValue);
}
}
///
/// 动画帧率(FPS)
///
public int AnimationFrameRate
{
get => _animationFrameRate;
set
{
if (SetProperty(ref _animationFrameRate, value))
{
// 帧率变化时,步长需要重新计算
OnPropertyChanged(nameof(CollisionDetectionAccuracy));
}
}
}
///
/// 当前选中的路径
///
public PathRouteViewModel CurrentPathRoute
{
get => _currentPathRoute;
set
{
if (SetProperty(ref _currentPathRoute, value))
{
// 路径改变时,步长、速度和路径长度需要重新计算
OnPropertyChanged(nameof(CollisionDetectionAccuracy));
OnPropertyChanged(nameof(MovementSpeed));
OnPropertyChanged(nameof(PathLength));
UpdateCanGenerateAnimation();
// 路径改变时,刷新ClashDetective结果列表
RefreshClashDetectiveResultsList();
}
}
}
///
/// 选中的移动物体
///
public ModelItem SelectedAnimatedObject
{
get => _selectedAnimatedObject;
set
{
if (SetProperty(ref _selectedAnimatedObject, value))
{
UpdateAnimatedObjectInfo();
UpdateCanGenerateAnimation();
// 预计算排除列表(异步)
_ = PrecomputeCollisionExclusionsAsync(value);
}
}
}
///
/// 选中的移动物体名称
///
public string SelectedAnimatedObjectName
{
get => _selectedAnimatedObjectName;
set => SetProperty(ref _selectedAnimatedObjectName, value);
}
///
/// 是否已选择移动物体
///
public bool HasSelectedAnimatedObject
{
get => _hasSelectedAnimatedObject;
set => SetProperty(ref _hasSelectedAnimatedObject, value);
}
///
/// 是否可以生成动画
///
public bool CanGenerateAnimation
{
get => _canGenerateAnimation;
set => SetProperty(ref _canGenerateAnimation, value);
}
#region 虚拟车辆属性
///
/// 是否使用选择的模型物体
///
///
/// 是否使用虚拟车辆
///
public bool UseVirtualVehicle
{
get => _useVirtualVehicle;
set
{
if (SetProperty(ref _useVirtualVehicle, value))
{
// ✨ 模式切换清理:切换到虚拟车辆模式时,移除选择物体的动画数据
if (value)
{
try
{
// 只有在当前动画对象不是虚拟车辆时才执行重置
if (_pathAnimationManager != null &&
_pathAnimationManager.AnimatedObject != null &&
!VirtualVehicleManager.Instance.IsVirtualVehicleActive)
{
_pathAnimationManager.RestoreObjectToCADPosition();
_pathAnimationManager.ClearAnimationResults(); // 清理手动选择物体留下的动画数据
LogManager.Info("已切换到虚拟车辆模式,将之前的手动选择物体归位并清理动画数据");
}
// 显示虚拟车辆,并同步到起点
LogManager.Info("正在显示虚拟车辆到起点...");
VirtualVehicleManager.Instance.ShowVirtualVehicle(
VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight);
var vModel = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
if (vModel != null && CurrentPathRoute != null && CurrentPathRoute.Points != null)
{
var points = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList();
_pathAnimationManager?.MoveVehicleToPathStart(vModel, points);
}
}
catch (Exception ex)
{
LogManager.Warning($"切换到虚拟车辆模式失败: {ex.Message}");
}
}
else
{
// ✨ 模式切换清理:切换到手动选择模式时,隐藏虚拟车辆并清理数据
try
{
// 不删除虚拟车辆,只隐藏它
VirtualVehicleManager.Instance.HideVirtualVehicle();
_pathAnimationManager?.ClearAnimationResults(); // 清理虚拟车辆留下的动画数据
LogManager.Info("已切换到手动选择模式,自动隐藏虚拟车辆并清理动画数据");
}
catch (Exception ex)
{
LogManager.Warning($"隐藏虚拟车辆失败: {ex.Message}");
}
}
UpdateCanGenerateAnimation();
}
}
}
///
/// 虚拟车辆长度(米)- 只读,从路径编辑同步
///
public double VirtualVehicleLength
{
get => _virtualVehicleLength;
private set => SetProperty(ref _virtualVehicleLength, value);
}
///
/// 虚拟车辆宽度(米)- 只读,从路径编辑同步
///
public double VirtualVehicleWidth
{
get => _virtualVehicleWidth;
private set => SetProperty(ref _virtualVehicleWidth, value);
}
///
/// 虚拟车辆高度(米)- 只读,从路径编辑同步
///
public double VirtualVehicleHeight
{
get => _virtualVehicleHeight;
private set => SetProperty(ref _virtualVehicleHeight, value);
}
///
/// 设置虚拟车辆参数(由主ViewModel调用)
///
public void SetVirtualVehicleParameters(double length, double width, double height)
{
VirtualVehicleLength = length;
VirtualVehicleWidth = width;
VirtualVehicleHeight = height;
LogManager.Info($"[AnimationControlViewModel] 虚拟车辆参数已更新: {length:F1}m × {width:F1}m × {height:F1}m");
// 如果当前使用虚拟车辆模式,更新生成动画的可用状态
if (UseVirtualVehicle)
{
UpdateCanGenerateAnimation();
}
}
#endregion
#region 手工碰撞对象属性
public bool IsManualCollisionTargetEnabled
{
get => _isManualCollisionTargetEnabled;
set
{
if (SetProperty(ref _isManualCollisionTargetEnabled, value))
{
HandleManualCollisionModeChanged();
}
}
}
public ObservableCollection ManualCollisionTargets => _manualCollisionTargets;
public string ManualCollisionTargetSummary
{
get => _manualCollisionTargetSummary;
set => SetProperty(ref _manualCollisionTargetSummary, value);
}
public bool HasManualCollisionTargets => _manualCollisionTargets?.Count > 0;
public bool IsManualTargetModeActive => IsManualCollisionTargetEnabled && HasManualCollisionTargets;
#endregion
#region ClashDetective结果管理
private ObservableCollection _clashDetectiveResults = new ObservableCollection();
public ObservableCollection ClashDetectiveResults => _clashDetectiveResults;
public ICommand RefreshClashDetectiveResultsCommand { get; private set; }
#endregion
#region 媒体控制属性
///
/// 时间轴位置(0-总帧数)
///
public double TimelinePosition
{
get => _pathAnimationManager?.CurrentFrame ?? 0;
set
{
if (_pathAnimationManager != null)
{
_pathAnimationManager.SeekToFrame((int)Math.Round(value));
}
OnPropertyChanged();
}
}
///
/// 时间轴持续时间(总帧数)
///
public double TimelineDuration => _pathAnimationManager?.TotalFrames - 1 ?? 0;
///
/// 当前时间显示(格式化)
///
public string CurrentTimeDisplay
{
get
{
if (_pathAnimationManager == null) return "00:00";
var currentSeconds = (_pathAnimationManager.CurrentFrame / (double)(_pathAnimationManager.TotalFrames - 1)) * _animationDuration;
return FormatTime(currentSeconds);
}
}
///
/// 总时长显示(格式化)
///
public string TotalTimeDisplay => FormatTime(_animationDuration);
///
/// 帧信息显示
///
public string FrameInfoDisplay
{
get
{
if (_pathAnimationManager == null) return "0/0";
return $"{_pathAnimationManager.CurrentFrame + 1}/{_pathAnimationManager.TotalFrames}";
}
}
///
/// 是否正在正向播放
///
public bool IsPlayingForward => _pathAnimationManager?.PlaybackDirection == 1 &&
_pathAnimationManager?.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Playing;
///
/// 是否正在反向播放
///
public bool IsPlayingReverse => _pathAnimationManager?.PlaybackDirection == -1 &&
_pathAnimationManager?.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Playing;
#endregion
#endregion
#region 命令
// 原有命令
public ICommand StartAnimationCommand { get; private set; }
public ICommand PauseAnimationCommand { get; private set; }
public ICommand StopAnimationCommand { get; private set; }
public ICommand ViewCollisionReportCommand { get; private set; }
public ICommand SelectAnimatedObjectCommand { get; private set; }
public ICommand ClearAnimatedObjectCommand { get; private set; }
public ICommand GenerateAnimationCommand { get; private set; }
public ICommand ApplyManualTargetsFromSelectionCommand { get; private set; }
public ICommand ClearManualTargetsCommand { get; private set; }
public ICommand RemoveManualTargetCommand { get; private set; }
public ICommand HighlightManualTargetsCommand { get; private set; }
public ICommand ClearManualHighlightsCommand { get; private set; }
public ICommand HighlightPrecomputedCollisionResultsCommand { get; private set; }
public ICommand ClearPrecomputedCollisionHighlightsCommand { get; private set; }
public ICommand HighlightClashDetectiveResultsCommand { get; private set; }
public ICommand ClearClashDetectiveHighlightsCommand { get; private set; }
// 媒体控制命令
public ICommand PlayForwardCommand { get; private set; }
public ICommand PlayReverseCommand { get; private set; }
public ICommand StepForwardCommand { get; private set; }
public ICommand StepBackwardCommand { get; private set; }
public ICommand FastForwardCommand { get; private set; }
public ICommand FastBackwardCommand { get; private set; }
public ICommand SeekToStartCommand { get; private set; }
public ICommand SeekToEndCommand { get; private set; }
#endregion
#region 构造函数
public AnimationControlViewModel() : this(null)
{
// 调用带参数的构造函数,传入null
}
///
/// 带主ViewModel参数的构造函数,支持统一状态栏
///
/// 主ViewModel,用于统一状态栏
public AnimationControlViewModel(LogisticsControlViewModel mainViewModel) : base()
{
try
{
// 设置主ViewModel引用到基类
SetMainViewModel(mainViewModel);
// 初始化管理器 - 使用单例模式确保与GenerateCollisionReportCommand使用同一实例
_pathAnimationManager = NavisworksTransport.Core.Animation.PathAnimationManager.GetInstance();
_clashIntegration = ClashDetectiveIntegration.Instance;
_uiStateManager = UIStateManager.Instance;
// 订阅PathAnimationManager事件
_pathAnimationManager.ProgressChanged += OnAnimationProgressChanged;
_pathAnimationManager.StateChanged += OnAnimationStateChanged;
// 初始化集合
AvailableFrameRates = new ThreadSafeObservableCollection();
_manualCollisionTargets = new ObservableCollection();
_manualCollisionTargets.CollectionChanged += OnManualCollisionTargetsChanged;
UpdateManualCollisionTargetSummary();
// 初始化设置
InitializeAnimationSettings();
// 初始化命令
InitializeCommands();
// 订阅碰撞检测事件
_clashIntegration.CollisionDetected += OnCollisionDetected;
_clashIntegration.ClashDetectiveResultSaved += OnClashDetectiveResultSaved;
// 订阅文档状态事件
DocumentStateManager.Instance.DocumentInvalidated += OnDocumentInvalidated;
DocumentStateManager.Instance.DocumentReady += OnDocumentReady;
LogManager.Info("AnimationControlViewModel初始化完成 - 支持统一状态栏");
}
catch (Exception ex)
{
LogManager.Error($"AnimationControlViewModel初始化失败: {ex.Message}", ex);
UpdateMainStatus("初始化失败,请检查日志");
throw;
}
}
#endregion
#region 初始化方法
///
/// 初始化动画设置
///
private void InitializeAnimationSettings()
{
// 从配置加载动画参数
var config = NavisworksTransport.Core.Config.ConfigManager.Instance.Current;
// 初始化可用帧率
AvailableFrameRates.Clear();
var frameRates = new[] { 15, 24, 30, 60 };
foreach (var rate in frameRates)
{
AvailableFrameRates.Add(rate);
}
// 从配置读取帧率
SelectedFrameRate = config.Animation.FrameRate;
// 从配置读取动画持续时间
AnimationDuration = config.Animation.DurationSeconds;
// 设置初始状态 - 默认状态未激活,等待有效动画
CanPauseAnimation = false;
CanStopAnimation = false;
// 初始化碰撞检测状态
HasClashDetectiveResults = false;
UpdateMainStatus("碰撞检测就绪");
// 从配置读取碰撞检测参数
DetectionGap = config.Animation.DetectionGapMeters;
AnimationFrameRate = config.Animation.FrameRate;
// 🔥 从配置加载虚拟车辆尺寸(与路径编辑保持一致)
VirtualVehicleLength = config.PathEditing.VehicleLengthMeters;
VirtualVehicleWidth = config.PathEditing.VehicleWidthMeters;
VirtualVehicleHeight = config.PathEditing.VehicleHeightMeters;
// 设置动画按钮的初始状态
UpdateAnimationButtonStates();
LogManager.Info($"动画设置初始化完成 - 帧率:{SelectedFrameRate}fps, 持续时间:{AnimationDuration}秒, 检测间隙:{DetectionGap}米");
}
///
/// 初始化命令
///
private void InitializeCommands()
{
StartAnimationCommand = new RelayCommand(async () => await ExecuteStartAnimationAsync(), () => CanStartAnimation);
PauseAnimationCommand = new RelayCommand(async () => await ExecutePauseAnimationAsync(), () => CanPauseAnimation);
StopAnimationCommand = new RelayCommand(async () => await ExecuteStopAnimationAsync(), () => CanStopAnimation);
ViewCollisionReportCommand = new RelayCommand(async () => await ExecuteViewCollisionReport(), () => HasClashDetectiveResults);
SelectAnimatedObjectCommand = new RelayCommand(ExecuteSelectAnimatedObject);
ClearAnimatedObjectCommand = new RelayCommand(ExecuteClearAnimatedObject, () => HasSelectedAnimatedObject);
GenerateAnimationCommand = new RelayCommand(ExecuteGenerateAnimation, () => CanGenerateAnimation);
ApplyManualTargetsFromSelectionCommand = new RelayCommand(ExecuteApplyManualTargetsFromSelection);
ClearManualTargetsCommand = new RelayCommand(ExecuteClearManualTargets, () => HasManualCollisionTargets);
RemoveManualTargetCommand = new RelayCommand(ExecuteRemoveManualTarget, target => target != null);
HighlightManualTargetsCommand = new RelayCommand(ExecuteHighlightManualTargets, () => HasManualCollisionTargets);
ClearManualHighlightsCommand = new RelayCommand(ExecuteClearManualHighlights, () => HasManualCollisionTargets);
HighlightPrecomputedCollisionResultsCommand = new RelayCommand(ExecuteHighlightPrecomputedCollisionResults, () => HasClashDetectiveResults);
ClearPrecomputedCollisionHighlightsCommand = new RelayCommand(ExecuteClearPrecomputedCollisionHighlights, () => HasClashDetectiveResults);
HighlightClashDetectiveResultsCommand = new RelayCommand(ExecuteHighlightClashDetectiveResults, () => HasClashDetectiveResults);
ClearClashDetectiveHighlightsCommand = new RelayCommand(ExecuteClearClashDetectiveHighlights, () => HasClashDetectiveResults);
RefreshClashDetectiveResultsCommand = new RelayCommand(ExecuteRefreshClashDetectiveResults);
PlayForwardCommand = new RelayCommand(ExecutePlayForward, CanExecuteMediaCommands);
PlayReverseCommand = new RelayCommand(ExecutePlayReverse, CanExecuteMediaCommands);
StepForwardCommand = new RelayCommand(ExecuteStepForward, CanExecuteMediaCommands);
StepBackwardCommand = new RelayCommand(ExecuteStepBackward, CanExecuteMediaCommands);
FastForwardCommand = new RelayCommand(ExecuteFastForward, CanExecuteMediaCommands);
FastBackwardCommand = new RelayCommand(ExecuteFastBackward, CanExecuteMediaCommands);
SeekToStartCommand = new RelayCommand(ExecuteSeekToStart, CanExecuteMediaCommands);
SeekToEndCommand = new RelayCommand(ExecuteSeekToEnd, CanExecuteMediaCommands);
LogManager.Info("动画控制命令初始化完成");
}
#endregion
#region 命令实现
///
/// 开始动画命令
///
private async Task ExecuteStartAnimationAsync()
{
if (!CanStartAnimation || CurrentPathRoute == null || CurrentPathRoute.Points.Count < 2)
{
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
UpdateMainStatus("请先选择包含至少2个点的路径");
});
return;
}
try
{
// 根据PathAnimationManager的状态决定操作
if (_pathAnimationManager.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Paused)
{
// 从暂停状态恢复
_pathAnimationManager.ResumeAnimation();
LogManager.Info("从暂停状态恢复动画播放");
return; // 恢复动画后直接返回,不再执行StartAnimation
}
else if (_pathAnimationManager.IsAnimating)
{
LogManager.Warning("动画已在播放中");
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
UpdateMainStatus("动画已在播放中");
CanStartAnimation = true;
CanPauseAnimation = false;
CanStopAnimation = false;
});
return;
}
// 开始播放物体移动动画
// 清理之前的碰撞缓存
_pathAnimationManager?.ClearExclusionCache();
LogManager.Info("已清理UI层碰撞检测缓存");
// 清理之前的碰撞高亮
ModelHighlightHelper.ClearCollisionHighlights();
LogManager.Info("已清理之前的碰撞高亮");
// 首先重置进度(开始全新动画时)
_pathAnimationManager.StartAnimation();
LogManager.Info($"开始物体移动动画播放: {CurrentPathRoute.Name}, 帧率: {SelectedFrameRate}fps");
}
catch (Exception ex)
{
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
UpdateMainStatus($"开始动画出错: {ex.Message}");
LogManager.Error($"开始动画异常: {ex.Message}", ex);
// 恢复按钮状态
CanStartAnimation = true;
CanPauseAnimation = false;
CanStopAnimation = false;
});
}
}
///
/// 暂停动画命令
///
private async Task ExecutePauseAnimationAsync()
{
try
{
// 调用PathAnimationManager暂停动画
// UI状态会通过OnAnimationStateChanged事件自动更新
_pathAnimationManager.PauseAnimation();
LogManager.Info("暂停动画播放");
}
catch (Exception ex)
{
LogManager.Error($"暂停动画异常: {ex.Message}", ex);
// 错误时手动恢复UI状态
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
UpdateMainStatus("暂停动画失败");
});
}
}
///
/// 停止动画命令
///
private async Task ExecuteStopAnimationAsync()
{
try
{
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
UpdateMainStatus("动画已停止");
CanStartAnimation = true;
CanPauseAnimation = false;
CanStopAnimation = false;
LogManager.Info("停止动画播放");
});
// 停止当前动画
_pathAnimationManager.CancelAnimation();
}
catch (Exception ex)
{
LogManager.Error($"停止动画异常: {ex.Message}", ex);
}
}
///
/// 查看碰撞报告命令(显示已缓存的报告)
///
private async Task ExecuteViewCollisionReport()
{
if (!HasClashDetectiveResults)
{
UpdateMainStatus("尚无碰撞检测结果可查看");
return;
}
try
{
// 检查是否有已缓存的报告
if (_lastGeneratedReport == null)
{
UpdateMainStatus("报告尚未生成,请等待碰撞检测完成");
LogManager.Warning("尝试查看报告但缓存为空");
return;
}
UpdateMainStatus("正在显示碰撞报告...", -1, true);
LogManager.Info("开始显示已缓存的碰撞报告");
// 在UI线程上显示缓存的报告
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
try
{
// 使用WPF碰撞报告对话框直接显示缓存的报告
var reportDialog = NavisworksTransport.UI.WPF.Views.CollisionReportDialog.ShowReport(_lastGeneratedReport);
if (reportDialog != null)
{
UpdateMainStatus("碰撞报告已显示");
LogManager.Info("成功显示缓存的碰撞报告");
}
else
{
UpdateMainStatus("显示报告对话框失败");
LogManager.Error("CollisionReportDialog.ShowReport返回null");
}
}
catch (Exception dialogEx)
{
UpdateMainStatus($"显示报告失败: {dialogEx.Message}");
LogManager.Error($"显示WPF报告对话框失败: {dialogEx.Message}");
}
});
}
catch (Exception ex)
{
UpdateMainStatus("查看碰撞报告出错");
LogManager.Error($"查看碰撞报告异常: {ex.Message}", ex);
}
}
#region 媒体控制命令实现
///
/// 检查媒体控制命令是否可执行
///
private bool CanExecuteMediaCommands()
{
return _pathAnimationManager != null &&
_pathAnimationManager.CurrentState != NavisworksTransport.Core.Animation.AnimationState.Idle &&
_pathAnimationManager.TotalFrames > 0;
}
///
/// 执行正向播放命令
///
private void ExecutePlayForward()
{
try
{
_pathAnimationManager?.PlayForward();
UpdateMediaControlProperties();
LogManager.Info("执行正向播放命令");
}
catch (Exception ex)
{
LogManager.Error($"正向播放命令执行失败: {ex.Message}");
UpdateMainStatus("正向播放失败");
}
}
///
/// 执行反向播放命令
///
private void ExecutePlayReverse()
{
try
{
_pathAnimationManager?.PlayReverse();
UpdateMediaControlProperties();
LogManager.Info("执行反向播放命令");
}
catch (Exception ex)
{
LogManager.Error($"反向播放命令执行失败: {ex.Message}");
UpdateMainStatus("反向播放失败");
}
}
///
/// 执行单帧前进命令
///
private void ExecuteStepForward()
{
try
{
_pathAnimationManager?.StepForward();
UpdateMediaControlProperties();
LogManager.Debug("执行单帧前进命令");
}
catch (Exception ex)
{
LogManager.Error($"单帧前进命令执行失败: {ex.Message}");
}
}
///
/// 执行单帧后退命令
///
private void ExecuteStepBackward()
{
try
{
_pathAnimationManager?.StepBackward();
UpdateMediaControlProperties();
LogManager.Debug("执行单帧后退命令");
}
catch (Exception ex)
{
LogManager.Error($"单帧后退命令执行失败: {ex.Message}");
}
}
///
/// 执行快进10帧命令
///
private void ExecuteFastForward()
{
try
{
_pathAnimationManager?.FastForward();
UpdateMediaControlProperties();
LogManager.Debug("执行快进10帧命令");
}
catch (Exception ex)
{
LogManager.Error($"快进命令执行失败: {ex.Message}");
}
}
///
/// 执行快退10帧命令
///
private void ExecuteFastBackward()
{
try
{
_pathAnimationManager?.FastBackward();
UpdateMediaControlProperties();
LogManager.Debug("执行快退10帧命令");
}
catch (Exception ex)
{
LogManager.Error($"快退命令执行失败: {ex.Message}");
}
}
///
/// 执行跳转到开头命令
///
private void ExecuteSeekToStart()
{
try
{
_pathAnimationManager?.SeekToFrame(0);
UpdateMediaControlProperties();
LogManager.Info("跳转到动画开头");
}
catch (Exception ex)
{
LogManager.Error($"跳转到开头失败: {ex.Message}");
}
}
///
/// 执行跳转到结尾命令
///
private void ExecuteSeekToEnd()
{
try
{
int lastFrame = (_pathAnimationManager?.TotalFrames ?? 1) - 1;
_pathAnimationManager?.SeekToFrame(lastFrame);
UpdateMediaControlProperties();
LogManager.Info("跳转到动画结尾");
}
catch (Exception ex)
{
LogManager.Error($"跳转到结尾失败: {ex.Message}");
}
}
///
/// 执行选择移动物体命令
///
private void ExecuteSelectAnimatedObject()
{
try
{
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
if (doc == null || doc.CurrentSelection.IsEmpty)
{
LogManager.Warning("请先在场景中选择一个物体");
return;
}
var newObject = doc.CurrentSelection.SelectedItems.First;
if (newObject == null) return;
// 1. 如果有旧物体,先归位并清理旧动画
if (_selectedAnimatedObject != null && !ReferenceEquals(_selectedAnimatedObject, newObject))
{
_pathAnimationManager?.RestoreObjectToCADPosition();
}
_pathAnimationManager?.ClearAnimationResults();
// 2. 设置新物体
SelectedAnimatedObject = newObject;
LogManager.Info($"已选择移动物体: {SelectedAnimatedObject.DisplayName}");
// 3. 立即移动到起点(如果路径存在)
if (CurrentPathRoute != null && CurrentPathRoute.Points != null && _pathAnimationManager != null)
{
var points = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList();
_pathAnimationManager.MoveVehicleToPathStart(newObject, points);
}
}
catch (Exception ex)
{
LogManager.Error($"选择物体失败: {ex.Message}");
}
}
///
/// 更新媒体控制相关属性
///
private void UpdateMediaControlProperties()
{
OnPropertyChanged(nameof(TimelinePosition));
OnPropertyChanged(nameof(TimelineDuration));
OnPropertyChanged(nameof(CurrentTimeDisplay));
OnPropertyChanged(nameof(FrameInfoDisplay));
OnPropertyChanged(nameof(IsPlayingForward));
OnPropertyChanged(nameof(IsPlayingReverse));
}
///
/// 格式化时间显示
///
private string FormatTime(double seconds)
{
var timeSpan = TimeSpan.FromSeconds(Math.Max(0, seconds));
return $"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}";
}
#endregion
#endregion
#region 辅助方法
///
/// 设置当前路径
///
public void SetCurrentPath(PathRouteViewModel pathRoute)
{
CurrentPathRoute = pathRoute;
// 同步路径到 PathAnimationManager
if (pathRoute != null && _pathAnimationManager != null)
{
// 从 PathPlanningManager 获取对应的 PathRoute 对象
var pathPlanningManager = PathPlanningManager.Instance;
var coreRoute = pathPlanningManager.GetAllRoutes().FirstOrDefault(r => r.Id == pathRoute.Id);
if (coreRoute != null)
{
_pathAnimationManager.SetRoute(coreRoute);
}
}
// 添加调试日志
if (pathRoute != null)
{
LogManager.Info($"[AnimationControlViewModel] 设置当前路径: 名称='{pathRoute.Name}', ID='{pathRoute.Id}', 点数={pathRoute.Points?.Count ?? 0}");
}
else
{
LogManager.Info("[AnimationControlViewModel] 清空当前路径");
}
// 路径改变时清除已生成的动画(如果有的话)
if (_pathAnimationManager != null &&
(_pathAnimationManager.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Ready ||
_pathAnimationManager.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Finished))
{
try
{
_pathAnimationManager.CancelAnimation(); // 这会将状态设置为Stopped,需要重新生成动画
LogManager.Info("路径更改,已清除之前生成的动画");
}
catch (Exception ex)
{
LogManager.Warning($"清除之前动画时出现警告: {ex.Message}");
}
}
// 路径改变时,步长、速度和路径长度需要重新计算
OnPropertyChanged(nameof(CollisionDetectionAccuracy));
OnPropertyChanged(nameof(MovementSpeed));
OnPropertyChanged(nameof(PathLength));
// 按钮状态更新
UpdateAnimationButtonStates();
// 更新碰撞状态
if (pathRoute == null)
{
UpdateMainStatus("请选择路径");
}
else if (pathRoute.Points.Count < 2)
{
UpdateMainStatus("路径点数不足");
}
else
{
UpdateMainStatus("碰撞检测就绪");
}
LogManager.Info($"AnimationControlViewModel当前路径更新: {pathRoute?.Name ?? "无"}, 点数: {pathRoute?.Points.Count ?? 0}");
}
///
/// 处理动画进度变化事件
/// 使用Dispatcher更新UI
///
private void OnAnimationProgressChanged(object sender, double progressPercent)
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// 更新时间轴相关属性
OnPropertyChanged(nameof(TimelinePosition));
OnPropertyChanged(nameof(CurrentTimeDisplay));
OnPropertyChanged(nameof(FrameInfoDisplay));
}));
}
///
/// 处理动画状态变化事件
///
private void OnAnimationStateChanged(object sender, Core.Animation.AnimationState state)
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
switch (state)
{
case Core.Animation.AnimationState.Playing:
CanStartAnimation = false;
CanPauseAnimation = true;
CanStopAnimation = true;
UpdateMainStatus("动画播放中");
UpdateMediaControlProperties(); // 更新媒体控制属性
break;
case Core.Animation.AnimationState.Paused:
CanStartAnimation = true; // 暂停状态下可以继续
CanPauseAnimation = false;
CanStopAnimation = true;
UpdateMainStatus("动画已暂停");
UpdateMediaControlProperties(); // 更新媒体控制属性
break;
case Core.Animation.AnimationState.Finished:
CanStartAnimation = true;
CanPauseAnimation = false;
CanStopAnimation = false;
UpdateMainStatus("动画已完成");
UpdateMediaControlProperties(); // 更新媒体控制属性
// 先清除所有碰撞高亮(包括预计算和ClashDetective)
ModelHighlightHelper.ClearCollisionHighlights();
// 检查并高亮ClashDetective结果
CheckAndHighlightClashDetectiveResults();
break;
case NavisworksTransport.Core.Animation.AnimationState.Stopped:
CanStartAnimation = true;
CanPauseAnimation = false;
CanStopAnimation = false;
UpdateMainStatus("动画已停止");
UpdateMediaControlProperties(); // 更新媒体控制属性
// 先清除所有碰撞高亮(包括预计算和ClashDetective)
ModelHighlightHelper.ClearCollisionHighlights();
// 检查并高亮ClashDetective结果
CheckAndHighlightClashDetectiveResults();
break;
default:
UpdateMainStatus($"动画状态: {state}");
UpdateMediaControlProperties(); // 更新媒体控制属性
break;
}
}));
}
///
/// 检查并高亮ClashDetective结果
///
private void CheckAndHighlightClashDetectiveResults()
{
try
{
// 检查是否有当前测试的ClashDetective结果
var clashResults = _clashIntegration?.GetCurrentTestResults();
if (clashResults != null && clashResults.Count > 0)
{
// 有ClashDetective结果,高亮显示
ModelHighlightHelper.HighlightClashDetectiveResults(_clashIntegration.CurrentTestName, _clashIntegration.GetCurrentPathClashResults);
HasClashDetectiveResults = true;
LogManager.Info($"动画结束,已高亮ClashDetective检测结果:{clashResults.Count}个碰撞");
}
// 如果没有ClashDetective结果,不做处理(可能第一次运行会自动计算,或者没有碰撞)
}
catch (Exception ex)
{
LogManager.Error($"检查并高亮ClashDetective结果失败: {ex.Message}");
}
}
///
/// 处理碰撞检测事件 - 自动生成报告并保存数据库
///
private async void OnCollisionDetected(object sender, CollisionDetectedEventArgs e)
{
try
{
// 获取ClashDetective结果
var clashResults = _clashIntegration?.GetCurrentTestResults();
var collisionCount = clashResults?.Count ?? 0;
if (collisionCount > 0)
{
UpdateMainStatus($"发现 {collisionCount} 个碰撞点,正在生成报告...", -1, true);
LogManager.Info($"碰撞检测完成,发现 {collisionCount} 个碰撞,开始自动生成报告");
// 自动生成碰撞报告
var generateReportCommand = NavisworksTransport.Commands.GenerateCollisionReportCommand.CreateComprehensive();
var result = await generateReportCommand.ExecuteAsync();
if (result.IsSuccess && result is PathPlanningResult reportResult)
{
// 缓存报告结果,供后续查看使用
_lastGeneratedReport = reportResult.Data;
// 保存到数据库
await SaveCollisionReportToDatabase(reportResult.Data);
UpdateMainStatus($"✅ 碰撞报告已生成并保存:{collisionCount} 个碰撞");
LogManager.Info("碰撞报告已自动生成并保存到数据库");
// 高亮ClashDetective结果
ModelHighlightHelper.HighlightClashDetectiveResults(_clashIntegration.CurrentTestName, _clashIntegration.GetCurrentPathClashResults);
HasClashDetectiveResults = true;
LogManager.Info("已自动高亮ClashDetective检测结果");
}
else
{
UpdateMainStatus($"发现 {collisionCount} 个碰撞点(报告生成失败)");
LogManager.Error($"自动生成碰撞报告失败: {result.ErrorMessage}");
}
}
else
{
UpdateMainStatus("未发现碰撞");
_lastGeneratedReport = null;
}
}
catch (Exception ex)
{
LogManager.Error($"处理碰撞检测事件失败: {ex.Message}", ex);
UpdateMainStatus("处理碰撞检测事件失败");
}
}
///
/// ClashDetective结果保存事件处理
///
private void OnClashDetectiveResultSaved(object sender, ClashDetectiveResultSavedEventArgs e)
{
try
{
// 刷新ClashDetective结果列表
RefreshClashDetectiveResultsList();
LogManager.Info($"ClashDetective结果已保存,列表已刷新: 路径={e.PathName}, 测试={e.TestName}, 碰撞数={e.CollisionCount}");
}
catch (Exception ex)
{
LogManager.Error($"处理ClashDetective结果保存事件失败: {ex.Message}", ex);
}
}
///
/// 保存碰撞报告到数据库
///
private async Task SaveCollisionReportToDatabase(CollisionReportResult reportResult)
{
try
{
var pathPlanningManager = PathPlanningManager.Instance;
var pathDatabase = pathPlanningManager?.GetPathDatabase();
if (pathDatabase != null && reportResult != null)
{
// 获取路由ID
string routeId = CurrentPathRoute?.Id ?? "";
// 从报告中获取所有需要的数据
await Task.Run(() =>
{
pathDatabase.SaveCollisionReport(
routeId,
reportResult.PathName,
SelectedAnimatedObjectName ?? "未知对象",
reportResult.UniqueCollidedObjectsCount,
reportResult.FrameRate,
reportResult.Duration,
reportResult.DetectionGap,
reportResult.AnimationCollisions,
reportResult.TotalCollisions
);
});
LogManager.Info($"碰撞报告已保存到数据库 - 路径:{reportResult.PathName}, " +
$"碰撞构件:{reportResult.UniqueCollidedObjectsCount}, " +
$"动画碰撞:{reportResult.AnimationCollisions}, " +
$"ClashDetective:{reportResult.TotalCollisions}");
}
else
{
LogManager.Warning("无法获取PathDatabase实例或报告数据为空,碰撞报告未保存到数据库");
}
}
catch (Exception ex)
{
LogManager.Error($"保存碰撞报告到数据库失败: {ex.Message}", ex);
}
}
///
/// 执行清除移动物体命令
/// 清除移动物体选择、停止当前动画并更新按钮状态
///
private void ExecuteClearAnimatedObject()
{
try
{
if (_pathAnimationManager != null)
{
// 1. 归位并清理
_pathAnimationManager.RestoreObjectToCADPosition();
_pathAnimationManager.ClearAnimationResults();
LogManager.Info("已清除PathAnimationManager中的动画数据并归位物体");
}
// 2. 重置属性
SelectedAnimatedObject = null;
UpdateAnimatedObjectInfo();
UpdateCanGenerateAnimation();
// 3. 清理高亮
ModelHighlightHelper.ClearCollisionHighlights();
LogManager.Info("移动物体已完全清除并归位");
UpdateMainStatus("已清除物体选择并恢复位置");
}
catch (Exception ex)
{
LogManager.Error($"清除失败: {ex.Message}");
UpdateMainStatus($"清除失败: {ex.Message}");
}
}
#region 手工碰撞对象命令
private void ExecuteApplyManualTargetsFromSelection()
{
try
{
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
var selectedItems = doc?.CurrentSelection?.SelectedItems;
if (selectedItems == null || selectedItems.Count == 0)
{
UpdateMainStatus("请在Navisworks中选择需要检测的对象");
LogManager.Warning("[手工碰撞] 未选择任何对象");
return;
}
var toAdd = new List();
foreach (ModelItem item in selectedItems)
{
if (item == null)
continue;
if (!ModelItemAnalysisHelper.IsModelItemValid(item) || !HasGeometryRecursive(item))
continue;
if (ManualTargetExists(item))
continue;
var displayName = ModelItemAnalysisHelper.GetSafeDisplayName(item);
var modelPath = BuildModelPath(item);
toAdd.Add(new ManualCollisionTargetViewModel(item, displayName, modelPath));
}
if (toAdd.Count == 0)
{
if (_manualCollisionTargets.Count >= ManualCollisionTargetLimit)
{
UpdateMainStatus($"手工目标数量已达上限({ManualCollisionTargetLimit})");
}
else
{
UpdateMainStatus("选中的对象已在列表中或不包含几何体");
}
return;
}
int availableSlots = ManualCollisionTargetLimit - _manualCollisionTargets.Count;
if (availableSlots <= 0)
{
UpdateMainStatus($"手工目标数量已达上限({ManualCollisionTargetLimit})");
return;
}
var addedCount = Math.Min(availableSlots, toAdd.Count);
foreach (var entry in toAdd.Take(addedCount))
{
_manualCollisionTargets.Add(entry);
}
_manualTargetsLastSyncTime = DateTime.Now;
UpdateManualCollisionTargetSummary();
RefreshManualTargetHighlights();
SyncManualCollisionTargetsToAnimationManager();
UpdateMainStatus($"已添加 {addedCount} 个碰撞检测对象");
LogManager.Info($"[手工碰撞] 添加 {addedCount} 个对象,目前共 {_manualCollisionTargets.Count} 个");
}
catch (Exception ex)
{
LogManager.Error($"[手工碰撞] 同步选中对象失败: {ex.Message}");
UpdateMainStatus("同步手工对象失败");
}
}
private void ExecuteClearManualTargets()
{
if (_manualCollisionTargets.Count == 0)
return;
_manualCollisionTargets.Clear();
_manualTargetsLastSyncTime = null;
ModelHighlightHelper.ClearCategory(ManualTargetsHighlightCategory);
SyncManualCollisionTargetsToAnimationManager();
UpdateManualCollisionTargetSummary();
UpdateMainStatus("已清空手工碰撞对象");
LogManager.Info("[手工碰撞] 手工对象列表已清空");
}
private void ExecuteRemoveManualTarget(ManualCollisionTargetViewModel target)
{
if (target == null)
return;
if (_manualCollisionTargets.Remove(target))
{
UpdateMainStatus($"已移除 {target.DisplayName}");
LogManager.Info($"[手工碰撞] 移除对象 {target.DisplayName}");
if (_manualCollisionTargets.Count == 0)
{
ModelHighlightHelper.ClearCategory(ManualTargetsHighlightCategory);
}
else
{
RefreshManualTargetHighlights();
}
}
}
private void ExecuteHighlightManualTargets()
{
if (_manualCollisionTargets.Count == 0)
return;
RefreshManualTargetHighlights(forceHighlight: true);
UpdateMainStatus("已高亮手工指定对象");
}
private void ExecuteClearManualHighlights()
{
ModelHighlightHelper.ClearCategory(ManualTargetsHighlightCategory);
UpdateMainStatus("已清除手工对象高亮");
}
#endregion
#region 碰撞结果高亮命令
private void ExecuteHighlightPrecomputedCollisionResults()
{
try
{
var deduplicatedResults = _clashIntegration?.GetDeduplicatedCollisionResults();
LogManager.Debug($"[预计算碰撞结果高亮] 获取到去重后的预计算结果数量: {deduplicatedResults?.Count ?? 0}");
if (deduplicatedResults == null || deduplicatedResults.Count == 0)
{
UpdateMainStatus("没有可高亮的去重预计算碰撞结果对象");
LogManager.Warning("[预计算碰撞结果高亮] 去重预计算缓存为空");
return;
}
// 输出前3个碰撞结果的信息
for (int i = 0; i < Math.Min(3, deduplicatedResults.Count); i++)
{
var collision = deduplicatedResults[i];
LogManager.Debug($"[预计算碰撞结果高亮] 碰撞{i+1}: {collision.DisplayName}, Item1={collision.Item1?.DisplayName}, Item2={collision.Item2?.DisplayName}");
}
ModelHighlightHelper.HighlightCollisionResults(deduplicatedResults, clearPrevious: false);
UpdateMainStatus($"已高亮 {deduplicatedResults.Count} 个去重预计算碰撞结果");
LogManager.Info($"[预计算碰撞结果高亮] 已高亮 {deduplicatedResults.Count} 个结果");
}
catch (Exception ex)
{
LogManager.Error($"高亮预计算碰撞结果失败: {ex.Message}", ex);
UpdateMainStatus("高亮预计算碰撞结果失败");
}
}
private void ExecuteClearPrecomputedCollisionHighlights()
{
ModelHighlightHelper.ClearCategory(CollisionResultsHighlightCategory);
UpdateMainStatus("已清除预计算碰撞结果高亮");
}
private void ExecuteHighlightClashDetectiveResults()
{
ModelHighlightHelper.HighlightClashDetectiveResults(_clashIntegration.CurrentTestName, _clashIntegration.GetCurrentPathClashResults);
UpdateMainStatus("已高亮ClashDetective检测结果");
}
private void ExecuteClearClashDetectiveHighlights()
{
ModelHighlightHelper.ClearClashDetectiveHighlights();
UpdateMainStatus("已清除ClashDetective高亮");
}
///
/// 刷新ClashDetective结果列表
///
private void RefreshClashDetectiveResultsList()
{
try
{
var pathName = CurrentPathRoute?.Name;
if (string.IsNullOrEmpty(pathName))
{
_clashDetectiveResults.Clear();
HasClashDetectiveResults = false;
return;
}
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
_clashDetectiveResults.Clear();
HasClashDetectiveResults = false;
return;
}
var records = pathDatabase.GetClashDetectiveResultsByPath(pathName);
_clashDetectiveResults.Clear();
foreach (var record in records)
{
_clashDetectiveResults.Add(new ClashDetectiveResultViewModel(record, RefreshClashDetectiveResultsList));
}
// 更新HasClashDetectiveResults属性
HasClashDetectiveResults = _clashDetectiveResults.Count > 0;
UpdateMainStatus($"已刷新ClashDetective结果列表:{_clashDetectiveResults.Count}条记录");
LogManager.Info($"ClashDetective结果列表已刷新:{_clashDetectiveResults.Count}条记录");
}
catch (Exception ex)
{
LogManager.Error($"刷新ClashDetective结果列表失败: {ex.Message}");
_clashDetectiveResults.Clear();
HasClashDetectiveResults = false;
}
}
private void ExecuteRefreshClashDetectiveResults()
{
RefreshClashDetectiveResultsList();
}
#endregion
///
/// 异步预计算碰撞排除列表
///
/// 动画对象
private async Task PrecomputeCollisionExclusionsAsync(ModelItem animationObject)
{
try
{
if (animationObject == null)
{
return;
}
LogManager.Info($"[缓存预计算] 开始为新选择的动画对象预计算: {animationObject.DisplayName}");
// 在UI线程中更新开始状态
await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
UpdateMainStatus("正在分析动画对象...", -1, true);
}));
// 在UI线程中执行Navisworks API操作(线程安全)
bool success;
try
{
success = _pathAnimationManager.PrecomputeCollisionExclusions(animationObject);
}
catch (Exception ex)
{
LogManager.Error($"[缓存预计算] 预计算失败: {ex.Message}", ex);
success = false;
}
// 异步更新UI状态
await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
if (success)
{
var cacheStats = _pathAnimationManager.GetCacheStats();
UpdateMainStatus("动画对象分析完成");
LogManager.Info($"[缓存预计算] 预计算成功 - {cacheStats}");
}
else
{
UpdateMainStatus("动画对象分析失败,无法生成动画");
LogManager.Error("[缓存预计算] 预计算失败: 无法构建碰撞排除列表");
}
}));
}
catch (Exception ex)
{
LogManager.Error($"[缓存预计算] 异步预计算过程异常: {ex.Message}", ex);
await System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
UpdateMainStatus("动画对象分析异常");
}));
}
}
#endregion
#region 手工碰撞对象辅助方法
private void OnManualCollisionTargetsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateManualCollisionTargetSummary();
OnPropertyChanged(nameof(HasManualCollisionTargets));
OnPropertyChanged(nameof(IsManualTargetModeActive));
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
RefreshManualTargetHighlights();
SyncManualCollisionTargetsToAnimationManager();
}
private void UpdateManualCollisionTargetSummary()
{
if (_manualCollisionTargets == null || _manualCollisionTargets.Count == 0)
{
ManualCollisionTargetSummary = "未指定碰撞检测对象";
return;
}
var syncPart = _manualTargetsLastSyncTime.HasValue
? $" • 最近同步 {_manualTargetsLastSyncTime.Value:HH:mm:ss}"
: string.Empty;
ManualCollisionTargetSummary = $"{_manualCollisionTargets.Count} 个对象{syncPart}";
}
private string BuildModelPath(ModelItem item)
{
try
{
var segments = new List();
var current = item;
int guard = 0;
while (current != null && guard < 15)
{
var name = ModelItemAnalysisHelper.GetSafeDisplayName(current);
if (!string.IsNullOrWhiteSpace(name))
{
segments.Add(name.Trim());
}
current = current.Parent;
guard++;
}
segments.Reverse();
return string.Join(" / ", segments);
}
catch
{
return "路径获取失败";
}
}
private List GetValidManualCollisionTargets(bool pruneInvalidEntries = false)
{
var validItems = new List();
if (_manualCollisionTargets == null)
{
return validItems;
}
var invalidItems = new List();
foreach (var entry in _manualCollisionTargets)
{
if (entry?.ModelItem != null &&
ModelItemAnalysisHelper.IsModelItemValid(entry.ModelItem) &&
HasGeometryRecursive(entry.ModelItem))
{
validItems.Add(entry.ModelItem);
}
else if (pruneInvalidEntries && entry != null)
{
invalidItems.Add(entry);
}
}
if (pruneInvalidEntries && invalidItems.Count > 0)
{
foreach (var invalid in invalidItems)
{
_manualCollisionTargets.Remove(invalid);
}
if (invalidItems.Count > 0)
{
UpdateMainStatus("部分手工碰撞对象已失效,已自动清理");
}
}
return validItems;
}
private void SyncManualCollisionTargetsToAnimationManager(bool pruneInvalidEntries = false)
{
if (_pathAnimationManager == null)
return;
var targets = GetValidManualCollisionTargets(pruneInvalidEntries);
if (IsManualCollisionTargetEnabled && targets.Count > 0)
{
_pathAnimationManager.SetManualCollisionTargets(targets, true);
}
else
{
_pathAnimationManager.SetManualCollisionTargets(null, false);
}
}
private bool ManualTargetExists(ModelItem item)
{
if (item == null)
{
return false;
}
if (_manualCollisionTargets == null || _manualCollisionTargets.Count == 0)
{
return false;
}
Guid itemGuid = item.InstanceGuid;
foreach (var target in _manualCollisionTargets)
{
if (target == null)
continue;
if (itemGuid != Guid.Empty && target.InstanceGuid != Guid.Empty)
{
if (target.InstanceGuid == itemGuid)
return true;
}
else if (ReferenceEquals(target.ModelItem, item))
{
return true;
}
}
return false;
}
private void RefreshManualTargetHighlights(bool forceHighlight = false)
{
try
{
if (_clashIntegration == null)
return;
var validItems = GetValidManualCollisionTargets();
if (validItems.Count == 0)
{
ModelHighlightHelper.ClearCategory(ManualTargetsHighlightCategory);
return;
}
if (IsManualTargetModeActive || forceHighlight)
{
ModelHighlightHelper.HighlightItems(ManualTargetsHighlightCategory, validItems);
}
else
{
ModelHighlightHelper.ClearCategory(ManualTargetsHighlightCategory);
}
}
catch (Exception ex)
{
LogManager.Warning($"[手工碰撞] 更新高亮失败: {ex.Message}");
}
}
private void HandleManualCollisionModeChanged()
{
RefreshManualTargetHighlights();
SyncManualCollisionTargetsToAnimationManager(true);
if (IsManualCollisionTargetEnabled && !HasManualCollisionTargets)
{
UpdateMainStatus("手工模式已启用,请先选择碰撞检测对象");
}
else if (!IsManualCollisionTargetEnabled)
{
UpdateMainStatus("已切换回默认碰撞检测对象范围");
}
}
#endregion
#region 动画辅助方法
///
/// 执行生成动画命令(同步执行,因为Navisworks API需要STA线程)
///
private void ExecuteGenerateAnimation()
{
try
{
if (!CanGenerateAnimation)
{
UpdateMainStatus("无法生成动画:缺少必要条件");
LogManager.Warning("尝试生成动画但条件不满足");
return;
}
UpdateMainStatus("正在生成动画...", -1, true);
LogManager.Info($"开始生成动画 - 模式: {(UseVirtualVehicle ? "虚拟车辆" : "选择物体")}");
var cacheStartTime = DateTime.Now;
var manualModeEnabled = IsManualCollisionTargetEnabled;
List manualTargets = null;
if (manualModeEnabled)
{
manualTargets = GetValidManualCollisionTargets(pruneInvalidEntries: true);
if (manualTargets.Count == 0)
{
UpdateMainStatus("手工模式已启用,但没有可用的碰撞对象");
LogManager.Warning("[动画生成] 手工模式启用但没有有效对象");
return;
}
}
// 构建碰撞检测缓存(两种模式都需要几何对象缓存)
UpdateMainStatus("正在构建碰撞检测缓存...", -1, true);
LogManager.Info("[动画生成] 开始构建碰撞检测缓存");
try
{
if (!manualModeEnabled)
{
ClashDetectiveIntegration.ClearAllCaches();
UpdateMainStatus("正在缓存几何对象列表...", -1, true);
ClashDetectiveIntegration.BuildNonHidddenGeometryItemsCache();
UpdateMainStatus("正在缓存通道对象...", -1, true);
var clashIntegration = ClashDetectiveIntegration.Instance;
clashIntegration.BuildChannelObjectsCache();
var cacheElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
LogManager.Info($"[动画生成] 碰撞检测缓存构建完成,耗时: {cacheElapsed:F1}ms");
if (!UseVirtualVehicle)
{
UpdateMainStatus("正在分析动画对象...", -1, true);
var success = _pathAnimationManager.PrecomputeCollisionExclusions(SelectedAnimatedObject);
if (!success)
{
var msg = "[动画生成] 动画对象分析失败,无法继续生成动画";
LogManager.Error(msg);
UpdateMainStatus("生成失败:对象分析错误");
return;
}
}
}
else
{
UpdateMainStatus("使用手工碰撞对象,跳过几何缓存构建", -1, true);
LogManager.Info("[动画生成] 手工碰撞对象模式,已跳过几何体/通道缓存和空间索引构建");
}
}
catch (Exception cacheEx)
{
LogManager.Error($"[动画生成] 缓存构建失败: {cacheEx.Message}");
UpdateMainStatus($"生成失败:{cacheEx.Message}");
return;
}
UpdateMainStatus("正在生成路径动画...", -1, true);
// 设置动画参数到PathAnimationManager
_pathAnimationManager.SetAnimationFrameRate(_animationFrameRate);
_pathAnimationManager.SetCollisionDetectionAccuracy(CollisionDetectionAccuracy);
_pathAnimationManager.SetMovementSpeed(MovementSpeed);
_pathAnimationManager.SetDetectionGap(_detectionGap);
// 将PathRouteViewModel的点转换为Point3D列表
var pathPoints = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList();
ModelItem animatedObject;
if (UseVirtualVehicle)
{
// 使用虚拟车辆:优先获取已存在的实例
animatedObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
if (animatedObject == null)
{
LogManager.Info("[ExecuteGenerateAnimation] 虚拟车辆不存在,正在显示...");
UpdateMainStatus("正在显示虚拟车辆...", -1, true);
VirtualVehicleManager.Instance.ShowVirtualVehicle(
VirtualVehicleLength,
VirtualVehicleWidth,
VirtualVehicleHeight
);
animatedObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
}
if (animatedObject == null)
{
LogManager.Error("[动画生成] 虚拟车辆显示失败");
UpdateMainStatus("生成失败:无法显示虚拟车辆");
return;
}
LogManager.Info($"使用虚拟车辆: {animatedObject.DisplayName}");
}
else
{
animatedObject = SelectedAnimatedObject;
}
if (manualModeEnabled)
{
_pathAnimationManager.SetManualCollisionTargets(manualTargets, true);
}
else
{
_pathAnimationManager.SetManualCollisionTargets(null, false);
}
// 【统一使用CreateAnimation,完全复用现有逻辑】
LogManager.Info($"[ExecuteGenerateAnimation] 准备创建动画: 路径名称='{CurrentPathRoute.Name}', ID='{CurrentPathRoute.Id}', " +
$"动画对象='{animatedObject.DisplayName}'");
// 从PathPlanningManager获取PathRoute对象
var pathPlanningManager = PathPlanningManager.Instance;
var pathRoute = pathPlanningManager.GetAllRoutes().FirstOrDefault(r => r.Id == CurrentPathRoute.Id);
if (pathRoute == null)
{
LogManager.Error($"无法找到路径对象,ID: {CurrentPathRoute.Id}");
UpdateMainStatus("生成失败:找不到路径数据");
return;
}
// 先设置路径到动画管理器
_pathAnimationManager.SetRoute(pathRoute);
// 准备车辆尺寸参数
double vLength = UseVirtualVehicle ? VirtualVehicleLength : 0;
double vWidth = UseVirtualVehicle ? VirtualVehicleWidth : 0;
double vHeight = UseVirtualVehicle ? VirtualVehicleHeight : 0;
LogManager.Info($"[ExecuteGenerateAnimation] 准备调用CreateAnimation: UseVirtualVehicle={UseVirtualVehicle}, 车辆尺寸: {vLength:F2}×{vWidth:F2}×{vHeight:F2}m");
_pathAnimationManager.CreateAnimation(animatedObject, pathPoints, AnimationDuration, CurrentPathRoute.Name, CurrentPathRoute.Id, UseVirtualVehicle,
vLength, vWidth, vHeight);
var totalElapsed = (DateTime.Now - cacheStartTime).TotalMilliseconds;
LogManager.Info($"[动画生成] 动画生成完成,总耗时: {totalElapsed:F1}ms");
LogManager.Info($"动画生成成功: 物体={animatedObject.DisplayName}, 路径={CurrentPathRoute.Name}");
if (UseVirtualVehicle)
{
LogManager.Info($"虚拟车辆尺寸: {VirtualVehicleLength:F1}×{VirtualVehicleWidth:F1}×{VirtualVehicleHeight:F1}m");
}
if (IsManualCollisionTargetEnabled)
{
RefreshManualTargetHighlights(forceHighlight: true);
}
// 更新状态
UpdateAnimationButtonStates();
UpdateMainStatus("动画生成成功");
}
catch (Exception ex)
{
UpdateMainStatus("动画生成失败");
LogManager.Error($"生成动画时发生错误: {ex.Message}");
LogManager.Error($"动画生成失败,详细错误信息:{ex}");
}
}
///
/// 更新移动物体信息
///
private void UpdateAnimatedObjectInfo()
{
if (SelectedAnimatedObject != null)
{
SelectedAnimatedObjectName = SelectedAnimatedObject.DisplayName ?? "未命名物体";
HasSelectedAnimatedObject = true;
}
else
{
SelectedAnimatedObjectName = "未选择移动物体";
HasSelectedAnimatedObject = false;
}
}
///
/// 递归检查ModelItem是否包含几何体
///
private bool HasGeometryRecursive(ModelItem item)
{
if (item == null) return false;
// 如果当前项有几何体,直接返回true
if (item.HasGeometry) return true;
// 递归检查子项
foreach (ModelItem child in item.Children)
{
if (HasGeometryRecursive(child))
return true;
}
return false;
}
///
/// 更新是否可以生成动画的状态
///
private void UpdateCanGenerateAnimation()
{
var hasPath = CurrentPathRoute != null && CurrentPathRoute.Points.Count >= 2;
bool hasValidObject;
if (UseVirtualVehicle)
{
// 使用虚拟车辆时,只需要有效的车辆尺寸
hasValidObject = VirtualVehicleLength > 0 &&
VirtualVehicleWidth > 0 &&
VirtualVehicleHeight > 0;
}
else
{
// 使用选择物体时,需要已选择物体
hasValidObject = SelectedAnimatedObject != null;
}
CanGenerateAnimation = hasValidObject && hasPath;
// 更新状态提示
if (IsManualCollisionTargetEnabled && !HasManualCollisionTargets)
{
UpdateMainStatus("手工模式已启用,请先选择碰撞检测对象");
}
else if (!hasPath && !hasValidObject)
{
UpdateMainStatus(UseVirtualVehicle ? "请选择动画路径" : "请选择移动物体和动画路径");
}
else if (!hasValidObject)
{
UpdateMainStatus(UseVirtualVehicle ? "虚拟车辆参数无效" : "请选择移动物体");
}
else if (!hasPath)
{
UpdateMainStatus("请选择有效的动画路径");
}
else
{
UpdateMainStatus("可以生成动画");
}
// 同时更新动画按钮状态,因为对象或路径的变化会影响"开始动画"按钮的可用性
UpdateAnimationButtonStates();
}
///
/// 计算当前路径的总长度
///
private double GetPathLength()
{
if (CurrentPathRoute == null || CurrentPathRoute.Points.Count < 2)
{
return 0.0;
}
double totalLength = 0.0;
var points = CurrentPathRoute.Points;
for (int i = 0; i < points.Count - 1; i++)
{
var p1 = points[i];
var p2 = points[i + 1];
// 计算两点之间的欧几里得距离(模型单位)
double dx = p2.X - p1.X;
double dy = p2.Y - p1.Y;
double dz = p2.Z - p1.Z;
double segmentLengthInModelUnits = Math.Sqrt(dx * dx + dy * dy + dz * dz);
// 转换为米单位
double segmentLengthInMeters = NavisworksTransport.Utils.UnitsConverter.ConvertToMeters(segmentLengthInModelUnits);
totalLength += segmentLengthInMeters;
}
return totalLength;
}
///
/// 统一更新动画按钮状态逻辑
/// 根据当前的路径、动画对象和动画管理器状态来决定按钮的可用性
///
private void UpdateAnimationButtonStates()
{
try
{
// 检查是否有有效的动画可以播放
var hasValidPath = CurrentPathRoute != null && CurrentPathRoute.Points.Count >= 2;
var hasValidAnimatedObject = SelectedAnimatedObject != null;
var hasValidAnimation = hasValidPath && hasValidAnimatedObject;
// 检查动画管理器当前状态
var animationState = _pathAnimationManager?.CurrentState ?? NavisworksTransport.Core.Animation.AnimationState.Stopped;
var isAnimating = _pathAnimationManager?.IsAnimating ?? false;
// 检查动画是否已经生成(Ready状态表示动画已生成但未播放)
var isAnimationReady = animationState == NavisworksTransport.Core.Animation.AnimationState.Ready ||
animationState == NavisworksTransport.Core.Animation.AnimationState.Finished;
// 根据动画状态和条件更新按钮状态
switch (animationState)
{
case NavisworksTransport.Core.Animation.AnimationState.Playing:
// 播放中:开始按钮禁用,暂停和停止按钮可用
CanStartAnimation = false;
CanPauseAnimation = true;
CanStopAnimation = true;
UpdateMainStatus("动画播放中");
break;
case NavisworksTransport.Core.Animation.AnimationState.Paused:
// 暂停中:开始按钮可用(显示为继续),暂停按钮禁用,停止按钮可用
CanStartAnimation = hasValidAnimation && isAnimationReady;
CanPauseAnimation = false;
CanStopAnimation = true;
UpdateMainStatus("动画已暂停");
break;
default:
// 停止或其他状态:需要同时满足有效动画条件和动画已生成条件
CanStartAnimation = hasValidAnimation && isAnimationReady;
CanPauseAnimation = false;
CanStopAnimation = false;
// 更新状态文本
if (IsManualCollisionTargetEnabled && !HasManualCollisionTargets)
{
UpdateMainStatus("手工模式已启用,请先选择碰撞检测对象");
}
else if (!hasValidPath && !hasValidAnimatedObject)
{
UpdateMainStatus("请选择路径和移动物体");
}
else if (!hasValidPath)
{
UpdateMainStatus("请选择有效路径(至少2个点)");
}
else if (!hasValidAnimatedObject)
{
UpdateMainStatus("请选择移动物体");
}
else if (!isAnimationReady)
{
UpdateMainStatus("请点击'生成动画'");
}
else
{
UpdateMainStatus("动画就绪");
}
break;
}
LogManager.Debug($"按钮状态已更新: CanStart={CanStartAnimation}, CanPause={CanPauseAnimation}, CanStop={CanStopAnimation}");
}
catch (Exception ex)
{
LogManager.Error($"更新动画按钮状态时发生错误: {ex.Message}", ex);
}
}
///
/// 生成碰撞检测报告数据
///
private CollisionReportData GenerateCollisionReport()
{
try
{
LogManager.Info("开始从Clash Detective获取碰撞检测结果");
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
var documentClash = doc.GetClash();
if (documentClash == null)
{
LogManager.Warning("无法获取Clash Detective文档");
return new CollisionReportData { IsValid = false, ErrorMessage = "Clash Detective不可用" };
}
var reportData = new CollisionReportData
{
IsValid = true,
GeneratedTime = DateTime.Now,
AnimatedObjectName = SelectedAnimatedObject?.DisplayName ?? "未知对象",
PathName = CurrentPathRoute?.Name ?? "未知路径"
};
// 获取所有动画相关的碰撞测试(包括新的分组测试)
var animationTests = documentClash.TestsData.Tests.Cast()
.Where(t => t.DisplayName.Contains("碰撞检测") )
.ToList();
LogManager.Info($"找到 {animationTests.Count} 个碰撞检测");
foreach (var test in animationTests)
{
// 递归统计测试中的碰撞总数(包括分组内的)
var totalCollisionCount = GetTotalClashResultCountForReport(test);
var testInfo = new CollisionTestInfo
{
TestName = test.DisplayName,
TestType = test.TestType.ToString(),
Tolerance = test.Tolerance,
CollisionCount = totalCollisionCount,
Status = test.Status.ToString()
};
// 递归获取每个碰撞的详细信息(包括分组内的)
var allClashResults = GetAllClashResultsFromTest(test);
foreach (var clashResult in allClashResults)
{
var collision = new CollisionDetailInfo
{
CollisionId = clashResult.Guid.ToString(),
Distance = clashResult.Distance,
Status = clashResult.Status.ToString()
};
// 获取碰撞对象信息
if (clashResult.CompositeItem1 != null)
{
collision.Object1Name = clashResult.CompositeItem1.DisplayName;
var point = clashResult.CompositeItem1.BoundingBox();
collision.Object1Position = $"({point.Center.X:F2}, {point.Center.Y:F2}, {point.Center.Z:F2})";
}
if (clashResult.CompositeItem2 != null)
{
collision.Object2Name = clashResult.CompositeItem2.DisplayName;
var point = clashResult.CompositeItem2.BoundingBox();
collision.Object2Position = $"({point.Center.X:F2}, {point.Center.Y:F2}, {point.Center.Z:F2})";
}
// 获取碰撞中心点
if (clashResult.Center != null)
{
collision.CollisionCenter = $"({clashResult.Center.X:F2}, {clashResult.Center.Y:F2}, {clashResult.Center.Z:F2})";
}
testInfo.Collisions.Add(collision);
}
reportData.Tests.Add(testInfo);
}
// 统计总体信息
reportData.TotalTests = reportData.Tests.Count;
reportData.TotalCollisions = reportData.Tests.Sum(t => t.CollisionCount);
LogManager.Info($"碰撞报告生成完成:{reportData.TotalTests}个测试,{reportData.TotalCollisions}个碰撞");
return reportData;
}
catch (Exception ex)
{
LogManager.Error($"生成碰撞报告异常: {ex.Message}");
return new CollisionReportData { IsValid = false, ErrorMessage = ex.Message };
}
}
///
/// 显示碰撞报告
///
private async Task ShowCollisionReport(CollisionReportData reportData)
{
try
{
// 更新UI摘要信息
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
if (reportData.IsValid)
{
// 🔧 修复:显示动画过程缓存的碰撞数量,而非测试数量
var animationCollisionCount = _clashIntegration?.AnimationCollisionCount ?? 0;
var summary = $"动画碰撞: {animationCollisionCount}个 | Clash Detective权威结果: {reportData.TotalCollisions}个";
UpdateMainStatus("碰撞报告已生成");
}
else
{
UpdateMainStatus($"报告生成失败:{reportData.ErrorMessage}");
}
});
// 如果报告有效且有碰撞数据,用户可通过"查看碰撞报告"按钮查看详细信息
if (reportData.IsValid && reportData.TotalCollisions > 0)
{
LogManager.Info($"碰撞检测完成,发现 {reportData.TotalCollisions} 个碰撞问题。用户可通过报告按钮查看详细信息");
}
else if (reportData.IsValid && reportData.TotalCollisions == 0)
{
// 没有碰撞时显示简单提示
LogManager.Info("未发现碰撞问题,路径规划良好");
System.Windows.MessageBox.Show(
"未发现碰撞问题,路径规划良好!",
"碰撞检测结果",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Information);
}
else
{
// 报告无效时的错误处理
LogManager.Error($"碰撞报告数据无效: {reportData.ErrorMessage}");
System.Windows.MessageBox.Show(
$"碰撞报告生成失败:{reportData.ErrorMessage}",
"错误",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
catch (Exception ex)
{
LogManager.Error($"显示碰撞报告异常: {ex.Message}");
System.Windows.MessageBox.Show(
$"显示碰撞报告时发生异常:{ex.Message}",
"错误",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}
#endregion
#region 文档管理
///
/// 获取单例实例
///
private static AnimationControlViewModel _instance;
public static AnimationControlViewModel Instance
{
get { return _instance; }
set { _instance = value; }
}
///
/// 文档失效时的处理
///
private void OnDocumentInvalidated(object sender, EventArgs e)
{
try
{
LogManager.Info("[AnimationControlViewModel] 文档失效,重置状态");
Reset();
}
catch (Exception ex)
{
LogManager.Error($"[AnimationControlViewModel] 处理文档失效失败: {ex.Message}");
}
}
///
/// 文档就绪时的处理
///
private void OnDocumentReady(object sender, EventArgs e)
{
try
{
LogManager.Info("[AnimationControlViewModel] 文档就绪,启用功能");
// 重新启用UI
CanStartAnimation = true;
CanGenerateAnimation = true;
UpdateMainStatus("文档已加载,就绪");
}
catch (Exception ex)
{
LogManager.Error($"[AnimationControlViewModel] 处理文档就绪失败: {ex.Message}");
}
}
///
/// 重置视图模型状态
///
public void Reset()
{
try
{
LogManager.Info("[AnimationControlViewModel] 重置状态");
// 停止动画
if (_pathAnimationManager != null && _pathAnimationManager.IsAnimating)
{
_pathAnimationManager.CancelAnimation();
}
// 清空选中对象
SelectedAnimatedObject = null;
SelectedAnimatedObjectName = string.Empty;
CurrentPathRoute = null;
// 禁用按钮
CanStartAnimation = false;
CanPauseAnimation = false;
CanStopAnimation = false;
CanGenerateAnimation = false;
HasSelectedAnimatedObject = false;
// 清空碰撞结果
HasClashDetectiveResults = false;
ModelHighlightHelper.ClearCollisionHighlights();
UpdateMainStatus("已重置");
}
catch (Exception ex)
{
LogManager.Error($"[AnimationControlViewModel] 重置失败: {ex.Message}");
}
}
#endregion
#region 资源清理
private bool _disposed = false;
///
/// 清理资源
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// 清理资源
///
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
try
{
LogManager.Info("开始清理AnimationControlViewModel资源");
// 1. 取消事件订阅(在停止动画之前,避免事件处理中访问已释放的对象)
// 取消DocumentStateManager事件订阅
try
{
DocumentStateManager.Instance.DocumentInvalidated -= OnDocumentInvalidated;
DocumentStateManager.Instance.DocumentReady -= OnDocumentReady;
LogManager.Debug("DocumentStateManager事件订阅取消完成");
}
catch (Exception docEx)
{
LogManager.Warning($"取消DocumentStateManager事件订阅时出现警告: {docEx.Message}");
}
if (_pathAnimationManager != null)
{
try
{
_pathAnimationManager.ProgressChanged -= OnAnimationProgressChanged;
_pathAnimationManager.StateChanged -= OnAnimationStateChanged;
LogManager.Debug("PathAnimationManager事件订阅取消完成");
}
catch (Exception eventEx)
{
LogManager.Warning($"取消PathAnimationManager事件订阅时出现警告: {eventEx.Message}");
}
}
if (_clashIntegration != null)
{
try
{
_clashIntegration.CollisionDetected -= OnCollisionDetected;
_clashIntegration.ClashDetectiveResultSaved -= OnClashDetectiveResultSaved;
LogManager.Debug("碰撞检测事件订阅取消完成");
}
catch (Exception eventEx)
{
LogManager.Warning($"取消碰撞检测事件订阅时出现警告: {eventEx.Message}");
}
}
if (_manualCollisionTargets != null)
{
_manualCollisionTargets.CollectionChanged -= OnManualCollisionTargetsChanged;
_manualCollisionTargets.Clear();
}
// 2. 不再清理PathAnimationManager - 现在使用单例模式,由应用程序生命周期管理
// 注意:PathAnimationManager.GetInstance()返回的是单例实例,
// 不应该在这里Dispose,否则会影响其他使用该单例的地方
if (_pathAnimationManager != null)
{
try
{
// 只清理事件订阅和停止动画,不调用Dispose,也不重置位置(避免访问已释放对象)
_pathAnimationManager.ShutdownAnimation();
LogManager.Debug("PathAnimationManager已执行关闭清理");
}
catch (Exception resetEx)
{
LogManager.Warning($"执行PathAnimationManager关闭清理时出现警告: {resetEx.Message}");
}
}
// 3. 清理PathAnimationManager缓存(缓存功能已迁移到PathAnimationManager)
// PathAnimationManager的Dispose已经包含了ClearExclusionCache的调用
LogManager.Info("AnimationControlViewModel资源清理完成");
}
catch (Exception ex)
{
LogManager.Error($"AnimationControlViewModel资源清理过程中发生异常: {ex.Message}");
// 即使清理过程中发生异常,也不应该抛出,因为这会干扰应用程序的正常关闭
}
}
_disposed = true;
}
}
///
/// 递归统计测试中的碰撞结果总数(包括分组内的结果)- 用于报告
///
private int GetTotalClashResultCountForReport(ClashTest test)
{
if (test == null) return 0;
int totalCount = 0;
foreach (var child in test.Children)
{
if (child is ClashResult)
{
totalCount++;
}
else if (child is ClashResultGroup group)
{
// 递归统计分组内的结果
totalCount += CountClashResultsInGroupForReport(group);
}
}
return totalCount;
}
///
/// 递归统计分组内的碰撞结果数量 - 用于报告
///
private int CountClashResultsInGroupForReport(ClashResultGroup group)
{
if (group == null) return 0;
int count = 0;
foreach (var child in group.Children)
{
if (child is ClashResult)
{
count++;
}
else if (child is ClashResultGroup nestedGroup)
{
// 递归处理嵌套分组
count += CountClashResultsInGroupForReport(nestedGroup);
}
}
return count;
}
///
/// 递归获取测试中的所有碰撞结果(包括分组内的)- 用于报告
///
private List GetAllClashResultsFromTest(ClashTest test)
{
var results = new List();
if (test == null) return results;
foreach (var child in test.Children)
{
if (child is ClashResult result)
{
results.Add(result);
}
else if (child is ClashResultGroup group)
{
// 递归获取分组内的结果
results.AddRange(GetAllClashResultsFromGroup(group));
}
}
return results;
}
///
/// 递归获取分组内的所有碰撞结果 - 用于报告
///
private List GetAllClashResultsFromGroup(ClashResultGroup group)
{
var results = new List();
if (group == null) return results;
foreach (var child in group.Children)
{
if (child is ClashResult result)
{
results.Add(result);
}
else if (child is ClashResultGroup nestedGroup)
{
// 递归处理嵌套分组
results.AddRange(GetAllClashResultsFromGroup(nestedGroup));
}
}
return results;
}
#endregion
}
}