NavisworksTransport/doc/design/2017/model_splitter_design.md

16 KiB
Raw Blame History

模型分层拆分功能设计方案

需求分析

根据用户需求第6项将大型建筑模型按照楼层等自定义属性拆分成多个单独的文件保存每层一个Navisworks文件

功能概述

实现一个模型分层拆分管理器ModelSplitterManager能够

  1. 按楼层属性自动识别和分组模型元素
  2. 按自定义属性进行模型分组
  3. 将分组后的模型导出为独立的Navisworks文件
  4. 提供批量处理和进度监控功能
  5. 支持分层预览和验证功能

技术架构设计

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

实现步骤

第一阶段:核心功能实现

  1. 创建ModelSplitterManager基础框架
  2. 实现FloorDetector楼层检测功能
  3. 实现AttributeGrouper属性分组功能
  4. 实现NavisworksFileExporter文件导出功能

第二阶段:算法优化

  1. 完善基于高程的楼层检测算法
  2. 实现基于属性的楼层检测算法
  3. 添加智能楼层识别和验证功能
  4. 优化大模型处理性能

第三阶段:用户界面

  1. 创建ModelSplitterDialog配置对话框
  2. 实现分层预览功能
  3. 添加进度监控和错误处理
  4. 集成到主插件界面

第四阶段:测试和优化

  1. 单元测试和集成测试
  2. 性能优化和内存管理
  3. 错误处理和异常恢复
  4. 用户体验优化

技术难点和解决方案

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的限制和大模型处理的性能问题提供了可靠的技术实现路径。