16 KiB
16 KiB
模型分层拆分功能设计方案
需求分析
根据用户需求第6项:将大型建筑模型按照楼层等自定义属性,拆分成多个单独的文件保存(每层一个Navisworks文件)
功能概述
实现一个模型分层拆分管理器(ModelSplitterManager),能够:
- 按楼层属性自动识别和分组模型元素
- 按自定义属性进行模型分组
- 将分组后的模型导出为独立的Navisworks文件
- 提供批量处理和进度监控功能
- 支持分层预览和验证功能
技术架构设计
1. 核心类设计
ModelSplitterManager 主管理类
public class ModelSplitterManager
{
// 分层策略枚举
public enum SplitStrategy
{
ByFloor, // 按楼层
ByCustomAttribute, // 按自定义属性
ByCategory, // 按类别
BySelection // 按选择集
}
// 分层配置
public class SplitConfiguration
{
public SplitStrategy Strategy { get; set; }
public string AttributeName { get; set; } // 用于自定义属性分层
public string OutputDirectory { get; set; }
public string FileNamePattern { get; set; } // 文件命名模式
public bool IncludeEmptyLayers { get; set; }
public bool PreserveOriginalStructure { get; set; }
}
// 分层结果
public class SplitResult
{
public string LayerName { get; set; }
public ModelItemCollection Items { get; set; }
public string OutputFilePath { get; set; }
public bool Success { get; set; }
public string ErrorMessage { get; set; }
public int ItemCount { get; set; }
}
}
FloorDetector 楼层检测器
public class FloorDetector
{
// 楼层信息结构
public class FloorInfo
{
public string FloorName { get; set; }
public double Elevation { get; set; }
public BoundingBox3D Bounds { get; set; }
public ModelItemCollection Items { get; set; }
}
// 检测方法
public List<FloorInfo> DetectFloors(ModelItemCollection allItems);
public FloorInfo GetFloorByElevation(double elevation);
public List<string> GetAvailableFloorAttributes();
}
AttributeGrouper 属性分组器
public class AttributeGrouper
{
// 分组结果
public class AttributeGroup
{
public string GroupName { get; set; }
public string AttributeValue { get; set; }
public ModelItemCollection Items { get; set; }
public Dictionary<string, object> Metadata { get; set; }
}
// 分组方法
public List<AttributeGroup> GroupByAttribute(ModelItemCollection items, string attributeName);
public List<string> GetAvailableAttributes(ModelItemCollection items);
public Dictionary<string, int> GetAttributeValueCounts(ModelItemCollection items, string attributeName);
}
2. Navisworks API 集成
文件导出核心逻辑
public class NavisworksFileExporter
{
public bool ExportModelItems(ModelItemCollection items, string outputPath)
{
try
{
// 1. 创建新的文档实例
var tempDocument = Application.NewDocument();
// 2. 设置选择集
tempDocument.CurrentSelection.CopyFrom(items);
// 3. 隐藏未选中的元素
HideUnselectedItems(tempDocument, items);
// 4. 导出文件
tempDocument.SaveFile(outputPath);
// 5. 清理临时文档
tempDocument.Close();
return true;
}
catch (Exception ex)
{
LogManager.Error($"导出文件失败: {ex.Message}");
return false;
}
}
private void HideUnselectedItems(Document document, ModelItemCollection selectedItems)
{
// 使用VisibilityManager隐藏未选中的元素
var visibilityManager = new VisibilityManager();
// 获取所有模型元素
var allItems = document.Models.RootItemDescendantsAndSelf;
// 创建要隐藏的元素集合
var itemsToHide = new ModelItemCollection();
foreach (ModelItem item in allItems)
{
if (!selectedItems.Contains(item))
{
itemsToHide.Add(item);
}
}
// 隐藏未选中的元素
visibilityManager.HideItems(itemsToHide);
}
}
3. 楼层检测算法
基于高程的楼层检测
public class ElevationBasedFloorDetector
{
private const double FLOOR_HEIGHT_THRESHOLD = 2.5; // 最小楼层高度(米)
private const double ELEVATION_TOLERANCE = 0.5; // 高程容差(米)
public List<FloorInfo> DetectFloorsByElevation(ModelItemCollection items)
{
var floorGroups = new Dictionary<double, List<ModelItem>>();
foreach (ModelItem item in items)
{
// 获取元素的边界框
var bounds = item.BoundingBox();
if (bounds.HasValue)
{
double elevation = bounds.Value.Min.Z; // 使用Z坐标最小值作为高程
// 查找相近的楼层组
double floorElevation = FindNearestFloorElevation(floorGroups.Keys, elevation);
if (floorElevation == double.MinValue)
{
// 创建新楼层组
floorGroups[elevation] = new List<ModelItem> { item };
}
else
{
// 添加到现有楼层组
floorGroups[floorElevation].Add(item);
}
}
}
// 转换为FloorInfo列表
var floors = new List<FloorInfo>();
int floorIndex = 1;
foreach (var kvp in floorGroups.OrderBy(x => x.Key))
{
var floorItems = new ModelItemCollection();
floorItems.AddRange(kvp.Value);
floors.Add(new FloorInfo
{
FloorName = $"Floor_{floorIndex:D2}",
Elevation = kvp.Key,
Items = floorItems,
Bounds = CalculateBounds(floorItems)
});
floorIndex++;
}
return floors;
}
private double FindNearestFloorElevation(IEnumerable<double> existingElevations, double targetElevation)
{
foreach (double elevation in existingElevations)
{
if (Math.Abs(elevation - targetElevation) <= ELEVATION_TOLERANCE)
{
return elevation;
}
}
return double.MinValue;
}
}
基于属性的楼层检测
public class AttributeBasedFloorDetector
{
private readonly string[] COMMON_FLOOR_ATTRIBUTES = {
"Level", "Floor", "Storey", "楼层", "层", "Level Name", "Story"
};
public List<FloorInfo> DetectFloorsByAttribute(ModelItemCollection items)
{
// 1. 查找楼层属性
string floorAttribute = FindFloorAttribute(items);
if (string.IsNullOrEmpty(floorAttribute))
{
LogManager.Warning("未找到楼层属性,使用高程检测");
return new ElevationBasedFloorDetector().DetectFloorsByElevation(items);
}
// 2. 按属性值分组
var attributeGrouper = new AttributeGrouper();
var groups = attributeGrouper.GroupByAttribute(items, floorAttribute);
// 3. 转换为FloorInfo
var floors = new List<FloorInfo>();
foreach (var group in groups)
{
floors.Add(new FloorInfo
{
FloorName = SanitizeFloorName(group.AttributeValue),
Elevation = CalculateAverageElevation(group.Items),
Items = group.Items,
Bounds = CalculateBounds(group.Items)
});
}
return floors.OrderBy(f => f.Elevation).ToList();
}
private string FindFloorAttribute(ModelItemCollection items)
{
var attributeCounts = new Dictionary<string, int>();
// 统计各属性的出现频率
foreach (ModelItem item in items.Take(100)) // 采样前100个元素
{
var properties = item.PropertyCategories;
foreach (PropertyCategory category in properties)
{
foreach (DataProperty property in category.Properties)
{
string propName = property.DisplayName;
if (COMMON_FLOOR_ATTRIBUTES.Any(attr =>
propName.IndexOf(attr, StringComparison.OrdinalIgnoreCase) >= 0))
{
attributeCounts[propName] = attributeCounts.GetValueOrDefault(propName, 0) + 1;
}
}
}
}
// 返回出现频率最高的楼层属性
return attributeCounts.OrderByDescending(kvp => kvp.Value).FirstOrDefault().Key;
}
}
4. 用户界面设计
分层配置对话框
public partial class ModelSplitterDialog : Form
{
private ModelSplitterManager _splitterManager;
private FloorDetector _floorDetector;
// UI控件
private ComboBox _strategyComboBox;
private ComboBox _attributeComboBox;
private TextBox _outputDirectoryTextBox;
private TextBox _fileNamePatternTextBox;
private ListView _previewListView;
private ProgressBar _progressBar;
private Button _previewButton;
private Button _executeButton;
public ModelSplitterDialog()
{
InitializeComponent();
InitializeManagers();
LoadAvailableAttributes();
}
private void InitializeManagers()
{
_splitterManager = new ModelSplitterManager();
_floorDetector = new FloorDetector();
// 订阅事件
_splitterManager.ProgressChanged += OnProgressChanged;
_splitterManager.LayerProcessed += OnLayerProcessed;
_splitterManager.SplitCompleted += OnSplitCompleted;
}
private void OnPreviewButtonClick(object sender, EventArgs e)
{
try
{
var config = GetCurrentConfiguration();
var previewResults = _splitterManager.PreviewSplit(config);
UpdatePreviewList(previewResults);
}
catch (Exception ex)
{
MessageBox.Show($"预览失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void OnExecuteButtonClick(object sender, EventArgs e)
{
try
{
var config = GetCurrentConfiguration();
// 验证配置
if (!ValidateConfiguration(config))
return;
// 开始分层处理
_executeButton.Enabled = false;
_progressBar.Visible = true;
Task.Run(() => _splitterManager.ExecuteSplit(config));
}
catch (Exception ex)
{
MessageBox.Show($"执行失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
_executeButton.Enabled = true;
_progressBar.Visible = false;
}
}
}
5. 集成到主插件
在MainPlugin中添加分层功能
// 在CreateSystemManagementTab方法中添加模型分层控件
private void CreateModelSplitterControls(GroupBox groupBox)
{
Button splitterButton = new Button
{
Text = "模型分层拆分",
Location = new Point(15, 25),
Size = new Size(120, 30),
Font = new Font("微软雅黑", 8)
};
groupBox.Controls.Add(splitterButton);
splitterButton.Click += (sender, e) => {
GlobalExceptionHandler.SafeExecute(() =>
{
// 检查是否有打开的模型
if (Application.ActiveDocument?.Models?.Count == 0)
{
MessageBox.Show("请先打开一个Navisworks模型文件", "提示",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
// 显示模型分层对话框
var splitterDialog = new ModelSplitterDialog();
splitterDialog.ShowDialog();
}, "打开模型分层对话框");
};
Label infoLabel = new Label
{
Text = "将大型模型按楼层或属性拆分为多个文件",
Location = new Point(15, 65),
Size = new Size(300, 40),
Font = new Font("微软雅黑", 7),
ForeColor = Color.Gray
};
groupBox.Controls.Add(infoLabel);
}
实现步骤
第一阶段:核心功能实现
- 创建ModelSplitterManager基础框架
- 实现FloorDetector楼层检测功能
- 实现AttributeGrouper属性分组功能
- 实现NavisworksFileExporter文件导出功能
第二阶段:算法优化
- 完善基于高程的楼层检测算法
- 实现基于属性的楼层检测算法
- 添加智能楼层识别和验证功能
- 优化大模型处理性能
第三阶段:用户界面
- 创建ModelSplitterDialog配置对话框
- 实现分层预览功能
- 添加进度监控和错误处理
- 集成到主插件界面
第四阶段:测试和优化
- 单元测试和集成测试
- 性能优化和内存管理
- 错误处理和异常恢复
- 用户体验优化
技术难点和解决方案
1. Navisworks API限制
问题: Navisworks API不支持直接创建新文档并添加特定模型元素 解决方案:
- 使用选择集和可见性控制来实现"虚拟分层"
- 通过SaveFile API导出包含特定元素的文件
- 利用临时文档和选择集操作
2. 大模型性能问题
问题: 处理大型模型时可能出现内存不足或处理缓慢 解决方案:
- 分批处理模型元素
- 使用异步处理和进度监控
- 实现内存管理和垃圾回收优化
- 提供取消操作功能
3. 楼层识别准确性
问题: 不同模型的楼层信息表示方式不统一 解决方案:
- 多种检测算法组合使用
- 用户可手动调整检测结果
- 提供预览和验证功能
- 支持自定义楼层识别规则
4. 文件命名和组织
问题: 生成的文件需要有意义的命名和良好的组织结构 解决方案:
- 可配置的文件命名模式
- 自动创建目录结构
- 支持批量重命名功能
- 生成分层报告文档
配置参数说明
分层策略配置
{
"splitStrategy": "ByFloor",
"attributeName": "Level",
"outputDirectory": "C:\\NavisworksSplits",
"fileNamePattern": "{ProjectName}_{LayerName}_{DateTime}",
"includeEmptyLayers": false,
"preserveOriginalStructure": true,
"elevationTolerance": 0.5,
"minFloorHeight": 2.5,
"maxLayersPerFile": 1
}
输出文件组织
OutputDirectory/
├── ProjectName_Floor_01_20250716/
│ ├── ProjectName_Floor_01.nwd
│ ├── ProjectName_Floor_01_report.txt
│ └── ProjectName_Floor_01_metadata.json
├── ProjectName_Floor_02_20250716/
│ ├── ProjectName_Floor_02.nwd
│ ├── ProjectName_Floor_02_report.txt
│ └── ProjectName_Floor_02_metadata.json
└── split_summary_report.html
扩展功能
1. 批量处理支持
- 支持处理多个Navisworks文件
- 提供批处理脚本和命令行接口
- 支持网络路径和云存储
2. 高级分层规则
- 支持复合条件分层(楼层+区域)
- 支持正则表达式匹配
- 支持自定义脚本分层逻辑
3. 质量检查功能
- 检查分层完整性
- 验证文件大小和元素数量
- 生成分层质量报告
4. 集成其他功能
- 与路径规划功能联动
- 支持分层动画预览
- 集成到DELMIA导出流程
总结
该设计方案提供了完整的模型分层拆分解决方案,能够满足用户需求第6项的要求。通过模块化设计和灵活的配置选项,可以适应不同类型的建筑模型和用户需求。同时考虑了Navisworks API的限制和大模型处理的性能问题,提供了可靠的技术实现路径。