diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj
index 47e48a8..1efdb79 100644
--- a/NavisworksTransportPlugin.csproj
+++ b/NavisworksTransportPlugin.csproj
@@ -131,6 +131,8 @@
+
+
diff --git a/src/Core/SectionBoxExporter.cs b/src/Core/SectionBoxExporter.cs
new file mode 100644
index 0000000..6c1e322
--- /dev/null
+++ b/src/Core/SectionBoxExporter.cs
@@ -0,0 +1,354 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+using Autodesk.Navisworks.Api;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace NavisworksTransport.Core
+{
+ ///
+ /// 剖面盒导出器 - 导出剖面盒内的对象包围盒信息到JSON
+ ///
+ public class SectionBoxExporter
+ {
+ ///
+ /// 导出剖面盒信息到JSON文件
+ ///
+ /// Navisworks文档
+ /// 导出的文件路径,失败返回null
+ public string ExportToJson(Document document)
+ {
+ if (document == null)
+ throw new ArgumentNullException(nameof(document));
+
+ // 1. 获取当前视点的裁剪平面
+ var viewpoint = document.CurrentViewpoint.Value;
+ var clipPlanes = viewpoint.ClipPlanes;
+
+ if (clipPlanes == null)
+ {
+ MessageBox.Show(
+ "当前没有活动的剖面盒!\n请先使用 Navisworks 的剖面工具创建剖面盒。",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
+ return null;
+ }
+
+ // 检查是否启用
+ if (!clipPlanes.Enabled)
+ {
+ MessageBox.Show(
+ "剖面盒未启用!\n请先在菜单【视点】-【启用剖分】,然后在菜单【剖分工具】的【模式】中选择【长方体】。\n【移动】、【缩放】或【旋转】剖面盒到适当的位置,或者用【适应选择】调整",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
+ return null;
+ }
+
+ // 检查是否为长方体模式(Box),排除平面裁剪模式(Clip)
+ if (clipPlanes.Mode != ClipPlaneSetMode.Box)
+ {
+ MessageBox.Show(
+ "当前是平面裁剪模式,请切换为剖面盒模式!\n在 Navisworks 剖面工具中选择'Box'模式。",
+ "提示",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
+ return null;
+ }
+
+ // 2. 获取剖面盒的包围盒
+ BoundingBox3D sectionBoxBounds;
+ if (!GetSectionBoxBounds(clipPlanes, out sectionBoxBounds))
+ {
+ MessageBox.Show(
+ "无法获取剖面盒边界!\n请确保剖面盒已正确设置。",
+ "错误",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
+ return null;
+ }
+
+ // 3. 选择保存路径
+ string filePath = ShowSaveFileDialog();
+ if (string.IsNullOrEmpty(filePath))
+ return null;
+
+ try
+ {
+ // 4. 获取剖面盒内的对象
+ var objectsInSectionBox = GetObjectsInSectionBox(document, sectionBoxBounds);
+
+ // 5. 构建导出数据
+ var exportData = BuildExportData(sectionBoxBounds, objectsInSectionBox, document);
+
+ // 6. 序列化为JSON并保存
+ string json = JsonConvert.SerializeObject(exportData, Formatting.Indented);
+ File.WriteAllText(filePath, json);
+
+ LogManager.Info($"剖面盒导出完成: {objectsInSectionBox.Count} 个对象 -> {filePath}");
+ return filePath;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"导出剖面盒失败: {ex.Message}", ex);
+ MessageBox.Show(
+ $"导出失败:{ex.Message}",
+ "错误",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
+ return null;
+ }
+ }
+
+ ///
+ /// 获取剖面盒的包围盒
+ ///
+ private bool GetSectionBoxBounds(ClipPlaneSet clipPlanes, out BoundingBox3D result)
+ {
+ result = default(BoundingBox3D);
+
+ try
+ {
+ // 直接获取 Box 属性(长方体模式)
+ var box = clipPlanes.Box;
+ if (box == null)
+ {
+ LogManager.Warning("ClipPlanes.Box 为 null");
+ return false;
+ }
+
+ // 转换为 BoundingBox3D
+ result = new BoundingBox3D(
+ new Point3D(box.Min.X, box.Min.Y, box.Min.Z),
+ new Point3D(box.Max.X, box.Max.Y, box.Max.Z)
+ );
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogManager.Warning($"获取剖面盒边界失败: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 获取剖面盒内的所有对象
+ ///
+ 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();
+
+ // 检查是否有体积(三个方向尺寸都大于0)
+ 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 && BoundingBoxesIntersect(bbox, sectionBox))
+ {
+ result.Add(item);
+ }
+ // 找到几何体,停止向下遍历(无论是否相交)
+ return;
+ }
+
+ // 没有几何体,继续遍历子节点
+ foreach (var child in item.Children)
+ {
+ TraverseModelItem(child, sectionBox, result);
+ }
+ }
+
+ ///
+ /// 检查两个包围盒是否相交
+ ///
+ private bool BoundingBoxesIntersect(BoundingBox3D a, BoundingBox3D b)
+ {
+ return (a.Min.X <= b.Max.X && a.Max.X >= b.Min.X) &&
+ (a.Min.Y <= b.Max.Y && a.Max.Y >= b.Min.Y) &&
+ (a.Min.Z <= b.Max.Z && a.Max.Z >= b.Min.Z);
+ }
+
+ ///
+ /// 构建导出数据
+ ///
+ private JObject BuildExportData(BoundingBox3D sectionBox, List objects, Document document)
+ {
+ var root = new JObject();
+
+ // 1. 导出剖面盒信息
+ var sectionBoxInfo = new JObject
+ {
+ ["min"] = new JArray { sectionBox.Min.X, sectionBox.Min.Y, sectionBox.Min.Z },
+ ["max"] = new JArray { sectionBox.Max.X, sectionBox.Max.Y, sectionBox.Max.Z },
+ ["center"] = new JArray
+ {
+ (sectionBox.Min.X + sectionBox.Max.X) / 2,
+ (sectionBox.Min.Y + sectionBox.Max.Y) / 2,
+ (sectionBox.Min.Z + sectionBox.Max.Z) / 2
+ },
+ ["size"] = new JArray
+ {
+ sectionBox.Max.X - sectionBox.Min.X,
+ sectionBox.Max.Y - sectionBox.Min.Y,
+ sectionBox.Max.Z - sectionBox.Min.Z
+ }
+ };
+ root["sectionBox"] = sectionBoxInfo;
+
+ // 2. 导出文档信息
+ var docInfo = new JObject
+ {
+ ["fileName"] = document.FileName ?? "Unknown",
+ ["units"] = document.Units.ToString(),
+ ["exportTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
+ ["objectCount"] = objects.Count
+ };
+ root["documentInfo"] = docInfo;
+
+ // 3. 导出对象列表
+ var objectsArray = new JArray();
+ foreach (var item in objects)
+ {
+ var bbox = item.BoundingBox();
+ var objInfo = new JObject
+ {
+ ["name"] = GetFullPathName(item),
+ ["boundingBox"] = new JObject
+ {
+ ["min"] = new JArray { bbox.Min.X, bbox.Min.Y, bbox.Min.Z },
+ ["max"] = new JArray { bbox.Max.X, bbox.Max.Y, bbox.Max.Z },
+ ["center"] = new JArray
+ {
+ (bbox.Min.X + bbox.Max.X) / 2,
+ (bbox.Min.Y + bbox.Max.Y) / 2,
+ (bbox.Min.Z + bbox.Max.Z) / 2
+ },
+ ["size"] = new JArray
+ {
+ bbox.Max.X - bbox.Min.X,
+ bbox.Max.Y - bbox.Min.Y,
+ bbox.Max.Z - bbox.Min.Z
+ }
+ }
+ };
+
+ // 添加Transform信息(仅非单位变换)
+ try
+ {
+ var components = item.Transform.Factor();
+ var rotation = components.Rotation.ToAxisAndAngle();
+ bool isIdentity = Math.Abs(rotation.Angle) < 0.0001 &&
+ Math.Abs(components.Scale.X - 1) < 0.0001 &&
+ Math.Abs(components.Scale.Y - 1) < 0.0001 &&
+ Math.Abs(components.Scale.Z - 1) < 0.0001 &&
+ Math.Abs(components.Translation.X) < 0.0001 &&
+ Math.Abs(components.Translation.Y) < 0.0001 &&
+ Math.Abs(components.Translation.Z) < 0.0001;
+
+ if (!isIdentity)
+ {
+ objInfo["transform"] = new JObject
+ {
+ ["translation"] = new JArray { components.Translation.X, components.Translation.Y, components.Translation.Z },
+ ["rotation"] = new JObject
+ {
+ ["axis"] = new JArray { rotation.Axis.X, rotation.Axis.Y, rotation.Axis.Z },
+ ["angle"] = rotation.Angle
+ },
+ ["scale"] = new JArray { components.Scale.X, components.Scale.Y, components.Scale.Z }
+ };
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Debug($"获取Transform信息失败: {ex.Message}");
+ }
+
+ objectsArray.Add(objInfo);
+ }
+ root["objects"] = objectsArray;
+
+ return root;
+ }
+
+ ///
+ /// 获取对象的全路径名称
+ ///
+ private string GetFullPathName(ModelItem item)
+ {
+ if (item == null) return "";
+
+ var pathParts = new List();
+
+ // 从当前节点向上遍历到根节点
+ var current = item;
+ while (current != null)
+ {
+ string name = current.DisplayName;
+ if (!string.IsNullOrEmpty(name))
+ {
+ pathParts.Insert(0, name);
+ }
+ current = current.Parent;
+ }
+
+ // 使用 "/" 连接路径
+ return string.Join("/", pathParts);
+ }
+
+ ///
+ /// 显示保存文件对话框
+ ///
+ private string ShowSaveFileDialog()
+ {
+ using (var dialog = new SaveFileDialog())
+ {
+ dialog.Filter = "JSON files (*.json)|*.json|All files (*.*)|*.*";
+ dialog.DefaultExt = "json";
+ dialog.FileName = $"SectionBox_Export_{DateTime.Now:yyyyMMdd_HHmmss}.json";
+ dialog.Title = "导出剖面盒信息";
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ return dialog.FileName;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/UI/WPF/ViewModels/SystemManagementViewModel.cs b/src/UI/WPF/ViewModels/SystemManagementViewModel.cs
index b16fb64..7d5060e 100644
--- a/src/UI/WPF/ViewModels/SystemManagementViewModel.cs
+++ b/src/UI/WPF/ViewModels/SystemManagementViewModel.cs
@@ -171,6 +171,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; }
@@ -308,6 +309,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" };
@@ -1764,6 +1766,68 @@ 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/SystemManagementView.xaml b/src/UI/WPF/Views/SystemManagementView.xaml
index 4492eca..535e408 100644
--- a/src/UI/WPF/Views/SystemManagementView.xaml
+++ b/src/UI/WPF/Views/SystemManagementView.xaml
@@ -236,6 +236,11 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
+
+