diff --git a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs
index 58ca869..4396f4f 100644
--- a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs
+++ b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs
@@ -737,6 +737,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ICommand SaveSelectedItemsCommand { get; private set; }
public ICommand CancelOperationCommand { get; private set; }
public ICommand TestExportToNwdCommand { get; private set; }
+ public ICommand ExportSectionBoxCommand { get; private set; }
// 楼层属性相关命令
public ICommand RefreshSelectionCommand { get; private set; }
@@ -894,6 +895,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
async () => await SaveSelectedItemsAsync(),
() => HasSelectedItems);
+ ExportSectionBoxCommand = new RelayCommand(
+ async () => await ExportSectionBoxAsync(),
+ () => IsNotProcessing);
+
CancelOperationCommand = new RelayCommand(CancelOperation, () => IsProcessing);
TestExportToNwdCommand = new RelayCommand(
@@ -1792,27 +1797,42 @@ namespace NavisworksTransport.UI.WPF.ViewModels
///
private async Task SaveSelectedItemsAsync()
{
+ var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
+ if (document?.CurrentSelection?.SelectedItems == null || document.CurrentSelection.SelectedItems.Count == 0)
+ {
+ MessageBox.Show("请先选择要保存的项目", "保存提示", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ var items = document.CurrentSelection.SelectedItems.ToList();
+ string defaultFileName = GenerateDefaultFileNameForMultipleItems(document, items);
+
+ await ExportItemsToNwdAsync(items, "保存当前选择项", defaultFileName, true);
+ }
+
+ ///
+ /// 通用导出方法 - 导出指定对象列表到NWD
+ ///
+ /// 要导出的对象列表
+ /// 对话框标题
+ /// 默认文件名
+ /// 是否恢复原始选择
+ private async Task ExportItemsToNwdAsync(List items, string dialogTitle, string defaultFileName, bool restoreSelection = false)
+ {
+ if (items == null || items.Count == 0)
+ {
+ MessageBox.Show("没有要导出的对象", "导出提示", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
try
{
- LogManager.Info("[LayerManagementViewModel] 开始保存当前选择项");
-
+ LogManager.Info($"[LayerManagementViewModel] 开始导出 {items.Count} 个对象");
IsProcessing = true;
+ CurrentOperationText = "准备导出...";
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
- if (document?.Models?.Count == 0 || document.CurrentSelection.SelectedItems.Count == 0)
- {
- LogManager.Warning("[LayerManagementViewModel] 没有活动文档或未选择节点");
- MessageBox.Show("请先选择要保存的项目", "保存提示", MessageBoxButton.OK, MessageBoxImage.Warning);
- return;
- }
-
- // 保存当前选择状态
- var originalSelection = new List(document.CurrentSelection.SelectedItems);
-
- LogManager.Info($"[LayerManagementViewModel] 用户选中了 {originalSelection.Count} 个节点");
-
- // 生成默认文件名
- string defaultFileName = GenerateDefaultFileNameForMultipleItems(document, originalSelection);
+ var originalSelection = restoreSelection ? document.CurrentSelection.SelectedItems.ToList() : null;
// 获取保存路径
string saveFilePath = null;
@@ -1820,7 +1840,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
var saveDialog = new Microsoft.Win32.SaveFileDialog
{
- Title = "保存当前选择项",
+ Title = dialogTitle,
Filter = "Navisworks文件 (*.nwd)|*.nwd",
DefaultExt = "nwd",
FileName = defaultFileName
@@ -1840,22 +1860,36 @@ namespace NavisworksTransport.UI.WPF.ViewModels
bool exportResult = false;
string errorMessage = "";
- // 在主线程执行导出
+ // 在主线程执行导出,显示Navisworks系统进度条
await Task.Run(() =>
{
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
+ // 设置等待光标
+ System.Windows.Input.Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait;
+
+ // 开始Navisworks系统进度条
+ var progress = Autodesk.Navisworks.Api.Application.BeginProgress(
+ dialogTitle,
+ $"正在导出 {items.Count} 个对象到 NWD 文件...");
+
try
{
- // 收集要导出的项目 (仅根节点)
+ CurrentOperationText = $"正在导出 {items.Count} 个对象...";
+ progress.Update(0.2);
+
+ // 收集要导出的项目
var itemsToExport = new ModelItemCollection();
- itemsToExport.AddRange(originalSelection);
+ itemsToExport.AddRange(items);
LogManager.Info($"[LayerManagementViewModel] 准备导出 {itemsToExport.Count} 个节点");
+ progress.Update(0.4);
// 在隔离模式下执行导出,自动恢复可见性
VisibilityHelper.ExecuteInIsolation(itemsToExport, () =>
{
+ progress.Update(0.6);
+
// 导出选项
var exportOptions = new Autodesk.Navisworks.Api.NwdExportOptions
{
@@ -1864,10 +1898,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
PreventObjectPropertyExport = PreventObjectPropertyExport
};
+ progress.Update(0.8);
document.ExportToNwd(saveFilePath, exportOptions);
exportResult = true;
LogManager.Info("[LayerManagementViewModel] ExportToNwd API调用完成");
});
+
+ progress.Update(1.0);
}
catch (Exception ex)
{
@@ -1875,22 +1912,32 @@ namespace NavisworksTransport.UI.WPF.ViewModels
errorMessage = ex.Message;
exportResult = false;
}
+ finally
+ {
+ // 确保进度条被关闭
+ Autodesk.Navisworks.Api.Application.EndProgress();
+ // 恢复光标
+ System.Windows.Input.Mouse.OverrideCursor = null;
+ }
});
});
- // 恢复原始选择 (需要在主线程)
- System.Windows.Application.Current.Dispatcher.Invoke(() =>
+ // 恢复原始选择(如果需要)
+ if (restoreSelection && originalSelection != null)
{
- try
+ System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
- document.CurrentSelection.Clear();
- document.CurrentSelection.AddRange(originalSelection);
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[LayerManagementViewModel] 恢复选择失败: {ex.Message}");
- }
- });
+ try
+ {
+ document.CurrentSelection.Clear();
+ document.CurrentSelection.AddRange(originalSelection);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"[LayerManagementViewModel] 恢复选择失败: {ex.Message}");
+ }
+ });
+ }
// 显示结果
if (exportResult)
@@ -1899,23 +1946,163 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
var fileInfo = new FileInfo(saveFilePath);
MessageBox.Show(
- $"保存成功!\n\n文件: {saveFilePath}\n大小: {fileInfo.Length / 1024} KB",
- "保存结果", MessageBoxButton.OK, MessageBoxImage.Information);
+ $"导出成功!\n\n文件: {saveFilePath}\n大小: {fileInfo.Length / 1024} KB",
+ "导出结果", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
else
{
- MessageBox.Show($"保存失败: {errorMessage}", "保存错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ MessageBox.Show($"导出失败: {errorMessage}", "导出错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (Exception ex)
{
- LogManager.Error($"[LayerManagementViewModel] 保存选中项目过程异常: {ex.Message}", ex);
- MessageBox.Show($"保存过程异常: {ex.Message}", "保存错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ LogManager.Error($"[LayerManagementViewModel] 导出过程异常: {ex.Message}", ex);
+ MessageBox.Show($"导出过程异常: {ex.Message}", "导出错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
IsProcessing = false;
+ CurrentOperationText = "";
+ }
+ }
+
+ ///
+ /// 导出剖面盒内的对象
+ ///
+ private async Task ExportSectionBoxAsync()
+ {
+ try
+ {
+ IsProcessing = true;
+ CurrentOperationText = "检查剖面盒...";
+
+ var document = NavisApplication.ActiveDocument;
+ if (document == null)
+ {
+ MessageBox.Show("没有活动的文档!请先打开一个模型。", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ // 检查剖面盒
+ var viewpoint = document.CurrentViewpoint.Value;
+ var clipPlanes = viewpoint.ClipPlanes;
+
+ if (clipPlanes == null || !clipPlanes.Enabled)
+ {
+ MessageBox.Show(
+ "当前没有活动的剖面盒!\n请先使用 Navisworks 的剖面工具创建剖面盒。",
+ "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+
+ if (clipPlanes.Mode != ClipPlaneSetMode.Box)
+ {
+ MessageBox.Show(
+ "当前是平面裁剪模式,请切换为剖面盒模式!\n在 Navisworks 剖面工具中选择'Box'模式。",
+ "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+
+ // 获取剖面盒边界
+ var box = clipPlanes.Box;
+ if (box == null)
+ {
+ MessageBox.Show("无法获取剖面盒边界!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ var sectionBoxBounds = new BoundingBox3D(
+ new Point3D(box.Min.X, box.Min.Y, box.Min.Z),
+ new Point3D(box.Max.X, box.Max.Y, box.Max.Z)
+ );
+
+ // 异步获取剖面盒内的对象
+ CurrentOperationText = "正在查找剖面盒内的对象...";
+ List objectsInSectionBox = null;
+
+ await Task.Run(() =>
+ {
+ objectsInSectionBox = GetObjectsInSectionBox(document, sectionBoxBounds);
+ });
+
+ if (objectsInSectionBox.Count == 0)
+ {
+ MessageBox.Show("剖面盒内没有找到对象!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+
+ LogManager.Info($"[LayerManagementViewModel] 剖面盒内找到 {objectsInSectionBox.Count} 个对象");
+
+ // 生成默认文件名
+ string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
+ string defaultFileName = $"SectionBox_Export_{timestamp}";
+
+ // 调用通用导出方法
+ await ExportItemsToNwdAsync(objectsInSectionBox, "导出剖面盒", defaultFileName, false);
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"[LayerManagementViewModel] 导出剖面盒异常: {ex.Message}", ex);
+ MessageBox.Show($"导出剖面盒异常: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ finally
+ {
+ IsProcessing = false;
+ CurrentOperationText = "";
+ }
+ }
+
+ ///
+ /// 获取剖面盒内的所有对象
+ ///
+ private List GetObjectsInSectionBox(Document document, BoundingBox3D sectionBox)
+ {
+ var result = new List();
+
+ foreach (var model in document.Models)
+ {
+ var rootItem = model.RootItem;
+ if (rootItem != null)
+ {
+ TraverseModelItem(rootItem, sectionBox, result);
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// 递归遍历模型项 - 剪枝策略
+ ///
+ private void TraverseModelItem(ModelItem item, BoundingBox3D sectionBox, List result)
+ {
+ if (item == null) return;
+ if (item.IsHidden) return;
+
+ // 如果有几何体信息,检查并添加
+ if (item.HasGeometry)
+ {
+ var bbox = item.BoundingBox();
+
+ // 检查是否有体积
+ double sizeX = bbox.Max.X - bbox.Min.X;
+ double sizeY = bbox.Max.Y - bbox.Min.Y;
+ double sizeZ = bbox.Max.Z - bbox.Min.Z;
+ bool hasVolume = sizeX > 0.0001 && sizeY > 0.0001 && sizeZ > 0.0001;
+
+ // 有体积且与剖面盒相交才添加
+ if (hasVolume && BoundingBoxGeometryUtils.BoundingBoxesIntersect(bbox, sectionBox))
+ {
+ result.Add(item);
+ }
+ return;
+ }
+
+ // 没有几何体,继续遍历子节点
+ foreach (var child in item.Children)
+ {
+ TraverseModelItem(child, sectionBox, result);
}
}
diff --git a/src/UI/WPF/ViewModels/SystemManagementViewModel.cs b/src/UI/WPF/ViewModels/SystemManagementViewModel.cs
index 43c3a49..f24006e 100644
--- a/src/UI/WPF/ViewModels/SystemManagementViewModel.cs
+++ b/src/UI/WPF/ViewModels/SystemManagementViewModel.cs
@@ -174,7 +174,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ICommand TestVoxelPathFindingCommand { get; private set; }
public ICommand ReadTransformTestCommand { get; private set; }
public ICommand CoordinateSystemExplorerCommand { get; private set; }
- public ICommand ExportSectionBoxCommand { get; private set; }
+
// 坐标系设置
public ObservableCollection CoordinateSystemOptions { get; private set; }
@@ -311,7 +311,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
TestVoxelPathFindingCommand = new RelayCommand(() => ExecuteTestVoxelPathFinding());
ReadTransformTestCommand = new RelayCommand(() => ExecuteReadTransformTest());
CoordinateSystemExplorerCommand = new RelayCommand(() => ExecuteCoordinateSystemExplorer());
- ExportSectionBoxCommand = new RelayCommand(() => ExecuteExportSectionBox());
+
// 初始化坐标系选项
CoordinateSystemOptions = new ObservableCollection { "AutoDetect", "ZUp", "YUp" };
@@ -1658,68 +1658,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}, "坐标系检测");
}
- ///
- /// 执行导出剖面盒功能
- ///
- private void ExecuteExportSectionBox()
- {
- SafeExecute(() =>
- {
- try
- {
- UpdateMainStatus("正在导出剖面盒信息...");
- LogManager.Info("开始导出剖面盒信息");
-
- var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
- if (doc == null || doc.IsClear)
- {
- System.Windows.MessageBox.Show(
- "没有活动的文档!请先打开一个模型。",
- "错误",
- System.Windows.MessageBoxButton.OK,
- System.Windows.MessageBoxImage.Error);
- UpdateMainStatus("导出剖面盒失败:无活动文档");
- return;
- }
-
- // 使用导出功能类
- var exporter = new NavisworksTransport.Core.SectionBoxExporter();
- string filePath = exporter.ExportToJson(doc);
-
- if (!string.IsNullOrEmpty(filePath))
- {
- var result = System.Windows.MessageBox.Show(
- $"剖面盒信息已导出到:\n{filePath}\n\n是否打开文件所在目录?",
- "导出成功",
- System.Windows.MessageBoxButton.YesNo,
- System.Windows.MessageBoxImage.Information);
-
- if (result == System.Windows.MessageBoxResult.Yes)
- {
- System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{filePath}\"");
- }
-
- UpdateMainStatus($"剖面盒信息已导出: {filePath}");
- LogManager.Info($"剖面盒信息导出成功: {filePath}");
- }
- else
- {
- UpdateMainStatus("导出剖面盒被取消或失败");
- }
- }
- catch (Exception ex)
- {
- LogManager.Error($"导出剖面盒异常: {ex.Message}", ex);
- System.Windows.MessageBox.Show(
- $"导出剖面盒出现异常:\n{ex.Message}",
- "错误",
- System.Windows.MessageBoxButton.OK,
- System.Windows.MessageBoxImage.Error);
- UpdateMainStatus($"导出剖面盒异常: {ex.Message}");
- }
- }, "导出剖面盒");
- }
-
#endregion
#region 辅助方法
diff --git a/src/UI/WPF/Views/LayerManagementView.xaml b/src/UI/WPF/Views/LayerManagementView.xaml
index d0d0b80..7796f3f 100644
--- a/src/UI/WPF/Views/LayerManagementView.xaml
+++ b/src/UI/WPF/Views/LayerManagementView.xaml
@@ -325,6 +325,28 @@ NavisworksTransport 分层管理页签视图 - 重构优化版本
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/UI/WPF/Views/SystemManagementView.xaml b/src/UI/WPF/Views/SystemManagementView.xaml
index 101a99d..6b05f58 100644
--- a/src/UI/WPF/Views/SystemManagementView.xaml
+++ b/src/UI/WPF/Views/SystemManagementView.xaml
@@ -226,11 +226,6 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
Style="{StaticResource SecondaryButtonStyle}"
Command="{Binding DiagnosticCommand}"
ToolTip="检查运行环境和线程状态"/>
-
-
diff --git a/src/Utils/VisibilityHelper.cs b/src/Utils/VisibilityHelper.cs
index 1dd9e26..075e12e 100644
--- a/src/Utils/VisibilityHelper.cs
+++ b/src/Utils/VisibilityHelper.cs
@@ -32,7 +32,7 @@ namespace NavisworksTransport
}
///
- /// 仅显示指定的模型项集合,隐藏其他所有项
+ /// 仅显示指定的模型项集合,隐藏其他所有项(高性能版本)
///
/// 要显示的模型项集合
/// 操作是否成功
@@ -55,41 +55,66 @@ namespace NavisworksTransport
return false;
}
+ LogManager.Info($"[VisibilityManager] 开始隔离显示,共 {itemsToShow.Count} 个对象");
+
// 1. 重置所有隐藏状态
document.Models.ResetAllHidden();
- // 2. 创建可见项集合(要保证从根节点开始的路径完整)
- ModelItemCollection visible = new ModelItemCollection();
-
+ // 2. 使用 HashSet 收集需要显示的所有节点(包括祖先)
+ var visibleSet = new HashSet();
foreach (ModelItem item in itemsToShow)
{
- // 添加祖先路径
- if (item.AncestorsAndSelf != null)
- visible.AddRange(item.AncestorsAndSelf);
- }
-
- // 3. 创建候选隐藏集合 - 收集所有可见项的兄弟节点
- ModelItemCollection hidden = new ModelItemCollection();
-
- foreach (ModelItem toShow in visible)
- {
- if (toShow.Parent != null)
+ var ancestors = item.AncestorsAndSelf;
+ if (ancestors != null)
{
- // 添加父节点的所有子节点
- hidden.AddRange(toShow.Parent.Children);
+ foreach (var ancestor in ancestors)
+ {
+ visibleSet.Add(ancestor);
+ }
}
}
- // 4. 从隐藏集合中移除可见项
- foreach (ModelItem toShow in visible)
+ LogManager.Info($"[VisibilityManager] 收集祖先完成,visibleSet={visibleSet.Count}");
+
+ // 3. 收集需要隐藏的节点(兄弟节点)
+ // 优化:先收集唯一的父节点,避免重复遍历同一个Parent的Children
+ var uniqueParents = new HashSet();
+ foreach (ModelItem visibleItem in visibleSet)
{
- hidden.Remove(toShow);
+ if (visibleItem.Parent != null)
+ {
+ uniqueParents.Add(visibleItem.Parent);
+ }
}
- // 5. 执行隐藏操作
- if (hidden.Count > 0)
+ LogManager.Info($"[VisibilityManager] 收集唯一父节点完成,uniqueParents={uniqueParents.Count}");
+
+ // 然后只遍历唯一的父节点的Children
+ var hiddenSet = new HashSet();
+ foreach (var parent in uniqueParents)
{
- document.Models.SetHidden(hidden, true);
+ foreach (var sibling in parent.Children)
+ {
+ hiddenSet.Add(sibling);
+ }
+ }
+
+ LogManager.Info($"[VisibilityManager] 收集兄弟完成,hiddenSet={hiddenSet.Count}");
+
+ // 4. 从隐藏集合中移除可见项(HashSet.Remove 是 O(1))
+ hiddenSet.ExceptWith(visibleSet);
+
+ LogManager.Info($"[VisibilityManager] 排除可见项后,hiddenSet={hiddenSet.Count}");
+
+ // 5. 执行隐藏操作
+ if (hiddenSet.Count > 0)
+ {
+ var hiddenCollection = new ModelItemCollection();
+ foreach (var item in hiddenSet)
+ {
+ hiddenCollection.Add(item);
+ }
+ document.Models.SetHidden(hiddenCollection, true);
}
stopwatch.Stop();