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 } }