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