using System; using System.Collections.Generic; using System.Linq; using Autodesk.Navisworks.Api; using NavisApplication = Autodesk.Navisworks.Api.Application; using NavisworksTransport.Core; using NavisworksTransport.Core.Config; using NavisworksTransport.Utils; namespace NavisworksTransport.UI.WPF.ViewModels { /// /// 物流控制面板主ViewModel /// public class LogisticsControlViewModel : ViewModelBase { private enum ClipBoxFocusMode { None, Path, Selection } #region 私有字段 private string _statusText; private bool _isProcessing = false; private bool _showControlVisualization = true; // UI状态管理和命令框架 private readonly UIStateManager _uiStateManager; private readonly NavisworksTransport.Commands.CommandManager _commandManager; private readonly PathPlanningManager _pathPlanningManager; #endregion #region 公共属性 /// /// 状态文本 /// public string StatusText { get => _statusText; set => SetProperty(ref _statusText, value); } /// /// 是否正在处理中(用于统一状态栏进度条显示) /// public bool IsProcessing { get => _isProcessing; set => SetProperty(ref _isProcessing, value); } private double _progressPercentage = 0; /// /// 进度百分比(0-100,-1表示不确定进度) /// public double ProgressPercentage { get => _progressPercentage; set { if (SetProperty(ref _progressPercentage, value)) { OnPropertyChanged(nameof(IsIndeterminateProgress)); } } } /// /// 是否使用不确定进度模式 /// public bool IsIndeterminateProgress => ProgressPercentage < 0; /// /// 是否显示控制点连线 /// public bool ShowControlVisualization { get => _showControlVisualization; set { if (SetProperty(ref _showControlVisualization, value)) { // 更新所有路径的控制点显示 var renderPlugin = PathPointRenderPlugin.Instance; if (renderPlugin != null) { lock (typeof(PathPointRenderPlugin)) { var allVisualizations = renderPlugin.GetAllPathVisualizations(); foreach (var visualization in allVisualizations) { visualization.ShowControlVisualization = value; } } // 触发视图刷新 if (NavisApplication.ActiveDocument?.ActiveView != null) { NavisApplication.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.Render); } } } } } private bool _showPathLines = true; private bool _showObjectSpace = false; /// /// 是否显示路径线/地面连线 /// public bool ShowPathLines { get => _showPathLines; set { if (SetProperty(ref _showPathLines, value)) { var renderPlugin = PathPointRenderPlugin.Instance; if (renderPlugin != null) { renderPlugin.ShowPathLines = value; } } } } /// /// 是否显示通行空间 /// public bool ShowObjectSpace { get => _showObjectSpace; set { if (SetProperty(ref _showObjectSpace, value)) { var renderPlugin = PathPointRenderPlugin.Instance; if (renderPlugin != null) { renderPlugin.ShowObjectSpace = value; } } } } private bool _isClipBoxEnabled; private bool _isSelectionClipBoxEnabled; private bool _isUpdatingClipBoxState; private ClipBoxFocusMode _clipBoxFocusMode = ClipBoxFocusMode.None; private readonly SelectionClipBoxLockState _selectionClipBoxLockState = new SelectionClipBoxLockState(); /// /// 是否启用剖面盒(聚焦当前路径) /// public bool IsClipBoxEnabled { get => _isClipBoxEnabled; set { if (SetProperty(ref _isClipBoxEnabled, value)) { if (_isUpdatingClipBoxState) { return; } if (value) { ActivatePathClipBox(); } else if (_clipBoxFocusMode == ClipBoxFocusMode.Path) { ClearActiveClipBox(); } } } } /// /// 是否启用剖面盒(聚焦当前选择对象) /// public bool IsSelectionClipBoxEnabled { get => _isSelectionClipBoxEnabled; set { if (SetProperty(ref _isSelectionClipBoxEnabled, value)) { if (_isUpdatingClipBoxState) { return; } if (value) { ActivateSelectionClipBox(); } else if (_clipBoxFocusMode == ClipBoxFocusMode.Selection) { ClearActiveClipBox(); } } } } /// /// 从 PathPointRenderPlugin 同步渲染状态 /// private void SyncVisualizationStateFromPlugin() { var renderPlugin = PathPointRenderPlugin.Instance; if (renderPlugin != null) { _showPathLines = renderPlugin.ShowPathLines; _showObjectSpace = renderPlugin.ShowObjectSpace; OnPropertyChanged(nameof(ShowPathLines)); OnPropertyChanged(nameof(ShowObjectSpace)); } } /// /// 当前路径是否可以使用路径线(仅地面路径) /// public bool CanUsePathLines { get { var currentRoute = _pathPlanningManager?.CurrentRoute; if (currentRoute == null) return true; return currentRoute.PathType == PathType.Ground; } } #endregion #region 构造函数 public LogisticsControlViewModel() : base() { try { // 获取UI状态管理器和命令管理器实例 _uiStateManager = UIStateManager.Instance; _commandManager = NavisworksTransport.Commands.CommandManager.Instance; _pathPlanningManager = PathPlanningManager.Instance; // 验证关键组件是否正常初始化 if (_uiStateManager == null) { LogManager.Error("UIStateManager初始化失败"); throw new InvalidOperationException("UIStateManager初始化失败"); } if (_commandManager == null) { LogManager.Error("CommandManager初始化失败"); throw new InvalidOperationException("CommandManager初始化失败"); } // 订阅可视化状态变更事件,同步按钮状态 if (_pathPlanningManager != null) { _pathPlanningManager.VisualizationStateChanged += (s, e) => { // 通知 CanUsePathLines 变更(影响LN按钮的启用状态) OnPropertyChanged(nameof(CanUsePathLines)); // 从 PathPointRenderPlugin 同步最新的渲染状态 SyncVisualizationStateFromPlugin(); }; // 订阅当前路径切换事件(总是触发,包括UI调用SetCurrentRoute时) // 用于剖面盒自动跟随路径切换 _pathPlanningManager.CurrentRouteSwitched += (s, e) => { if (_clipBoxFocusMode == ClipBoxFocusMode.Path) { if (_pathPlanningManager.PathEditState == PathEditState.Creating) { LogManager.Info("[剖面盒] 当前处于新建路径流程,跳过自动跟随"); return; } LogManager.Info("[剖面盒] 路径已切换,重新设置剖面盒聚焦到新路径"); EnableClipBoxForCurrentRoute(); } }; } if (NavisApplication.ActiveDocument?.CurrentSelection != null) { NavisApplication.ActiveDocument.CurrentSelection.Changed += OnCurrentSelectionChanged; } // 初始化状态 StatusText = "插件已就绪"; LogManager.Info("LogisticsControlViewModel构造函数执行完成"); } catch (Exception ex) { LogManager.Error($"LogisticsControlViewModel构造函数异常: {ex.Message}", ex); // 在构造函数中尽量保证对象处于可用状态 StatusText = "初始化失败,请检查日志"; throw; } } #endregion #region 统一状态栏方法 /// /// 更新统一状态栏信息(简单版本,不显示进度条) /// /// 状态消息 public void UpdateStatus(string message) { SafeExecute(() => { StatusText = message ?? "就绪"; IsProcessing = false; // 简单状态更新不显示进度条 }, "更新统一状态栏", runOnUIThread: true); } /// /// 更新统一状态栏信息(完整版本) /// /// 状态消息 /// 进度值 (0-100),默认不显示进度条 /// 是否正在处理中(显示进度条动画) public void UpdateStatus(string message, double progress = -1, bool? isProcessing = null) { SafeExecute(() => { StatusText = message ?? "就绪"; if (progress >= 0) { ProgressPercentage = Math.Max(0, Math.Min(100, progress)); } if (isProcessing.HasValue) { IsProcessing = isProcessing.Value; } }, "更新统一状态栏", runOnUIThread: true); } /// /// 更新统一状态栏信息(支持百分比进度) /// /// 状态消息 /// 进度百分比 (0-100) public void UpdateStatus(string message, double percentage) { SafeExecute(() => { StatusText = message ?? "就绪"; ProgressPercentage = Math.Max(0, Math.Min(100, percentage)); IsProcessing = true; // 有百分比进度时显示进度条 }, "更新统一状态栏(百分比模式)", runOnUIThread: true); } /// /// 重置状态栏到默认状态 /// public void ResetStatus() { UpdateStatus("插件已就绪"); } #endregion #region 剖面盒功能 private void ActivatePathClipBox() { SetClipBoxState(ClipBoxFocusMode.Path); EnableClipBoxForCurrentRoute(); } private void ActivateSelectionClipBox() { SetClipBoxState(ClipBoxFocusMode.Selection); EnableClipBoxForCurrentSelection(); } private void ClearActiveClipBox() { SetClipBoxState(ClipBoxFocusMode.None); _selectionClipBoxLockState.Clear(); SectionClipHelper.ClearClipBox(); LogManager.Info("[剖面盒] 已关闭"); } private void SetClipBoxState(ClipBoxFocusMode mode) { _clipBoxFocusMode = mode; bool pathEnabled = mode == ClipBoxFocusMode.Path; bool selectionEnabled = mode == ClipBoxFocusMode.Selection; _isUpdatingClipBoxState = true; try { if (_isClipBoxEnabled != pathEnabled) { _isClipBoxEnabled = pathEnabled; OnPropertyChanged(nameof(IsClipBoxEnabled)); } if (_isSelectionClipBoxEnabled != selectionEnabled) { _isSelectionClipBoxEnabled = selectionEnabled; OnPropertyChanged(nameof(IsSelectionClipBoxEnabled)); } } finally { _isUpdatingClipBoxState = false; } } private void OnCurrentSelectionChanged(object sender, EventArgs e) { if (_clipBoxFocusMode != ClipBoxFocusMode.Selection) { return; } if (!_selectionClipBoxLockState.ShouldRefreshOnSelectionChanged(isSelectionClipBoxMode: true)) { LogManager.Info($"[剖面盒] 选择已变化,但当前剖面盒保持锁定,锁定对象数: {_selectionClipBoxLockState.LockedSelectionCount}"); return; } LogManager.Info("[剖面盒] 选择已变化,重新聚焦到当前选择对象"); EnableClipBoxForCurrentSelection(); } /// /// 启用剖面盒并聚焦到当前路径 /// private void EnableClipBoxForCurrentRoute() { try { var currentRoute = _pathPlanningManager?.CurrentRoute; if (currentRoute?.Points == null || currentRoute.Points.Count == 0) { LogManager.Warning("[剖面盒] 当前没有路径,无法设置剖面盒"); SectionClipHelper.ClearClipBox(); SetClipBoxState(ClipBoxFocusMode.None); return; } // 获取虚拟物体默认高度(米) double objectHeightInMeters = ConfigManager.Instance.Current.PathEditing.ObjectHeightMeters; // ========== 第1步:处理路径,计算包含虚拟物体高度的包围盒 ========== // 转换路径点,并根据路径类型调整Z值范围(包含虚拟物体高度) var adjustedPoints = currentRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList(); // 根据路径类型,添加包含虚拟物体高度的额外点 if (currentRoute.PathType == PathType.Ground) { // 地面路径:在路径点上方添加物体高度点 foreach (var p in currentRoute.Points) { adjustedPoints.Add(new Point3D(p.X, p.Y, p.Z + objectHeightInMeters)); } } else { // 空中路径:在路径点下方添加物体高度点 foreach (var p in currentRoute.Points) { adjustedPoints.Add(new Point3D(p.X, p.Y, p.Z - objectHeightInMeters)); } } // ========== 第2步:先清除旧的剖面盒,再设置新的 ========== SectionClipHelper.ClearClipBox(); // ========== 第3步:统一扩展(水平4米,高度2米)========== bool success = SectionClipHelper.SetClipBoxByPath(adjustedPoints, marginInMeters: 4.0, // 水平方向扩展4米 heightMarginInMeters: 2.0 // 高度方向上下各扩展2米 ); if (success) { LogManager.Info($"[剖面盒] 已启用,路径点数量: {currentRoute.Points.Count}, 各方向扩展: 2m"); } else { LogManager.Warning("[剖面盒] 设置失败"); SectionClipHelper.ClearClipBox(); SetClipBoxState(ClipBoxFocusMode.None); } } catch (Exception ex) { LogManager.Error($"[剖面盒] 启用失败: {ex.Message}"); SectionClipHelper.ClearClipBox(); SetClipBoxState(ClipBoxFocusMode.None); } } /// /// 启用剖面盒并聚焦到当前选择对象 /// private void EnableClipBoxForCurrentSelection() { try { var document = NavisApplication.ActiveDocument; var selectedItems = document?.CurrentSelection?.SelectedItems?.Cast().Where(item => item != null).ToList(); if (selectedItems == null || selectedItems.Count == 0) { LogManager.Warning("[剖面盒] 当前没有选中的模型对象,无法设置剖面盒"); _selectionClipBoxLockState.Clear(); SectionClipHelper.ClearClipBox(); SetClipBoxState(ClipBoxFocusMode.None); return; } _selectionClipBoxLockState.LockSelection(selectedItems); SectionClipHelper.ClearClipBox(); bool success = SectionClipHelper.SetClipBoxByModelItems( selectedItems, marginInMeters: 2.0, heightMarginInMeters: 2.0); if (success) { ViewpointHelper.FocusOnModelItems(selectedItems, ViewpointHelper.ViewpointStrategy.ModelFocus); LogManager.Info($"[剖面盒] 已按当前选择对象启用,选择数量: {selectedItems.Count}"); } else { LogManager.Warning("[剖面盒] 按选择对象设置失败"); _selectionClipBoxLockState.Clear(); SectionClipHelper.ClearClipBox(); SetClipBoxState(ClipBoxFocusMode.None); } } catch (Exception ex) { LogManager.Error($"[剖面盒] 按选择对象启用失败: {ex.Message}"); _selectionClipBoxLockState.Clear(); SectionClipHelper.ClearClipBox(); SetClipBoxState(ClipBoxFocusMode.None); } } /// /// 计算点列表的包围盒 /// private (Point3D Center, double SizeX, double SizeY, double SizeZ) CalculatePointsBoundingBox(List points) { double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue; double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue; foreach (var p in points) { minX = Math.Min(minX, p.X); minY = Math.Min(minY, p.Y); minZ = Math.Min(minZ, p.Z); maxX = Math.Max(maxX, p.X); maxY = Math.Max(maxY, p.Y); maxZ = Math.Max(maxZ, p.Z); } var center = new Point3D((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2); return (center, maxX - minX, maxY - minY, maxZ - minZ); } #endregion } }