# 分层预览中自定义预览的分层实现逻辑分析

## 概述

本文档详细分析NavisworksTransport插件中分层预览功能的实现逻辑，特别是自定义预览的分层机制、节点遍历深度控制和界面交互方式。

## 核心架构分析

### 1. 节点遍历深度控制机制

#### 深度选择界面设计

系统在UI层提供了灵活的深度控制选项：

```csharp
// LayerManagementViewModel.cs - 深度选项定义
public ThreadSafeObservableCollection<string> DepthOptions { get; } =
    new ThreadSafeObservableCollection<string>
    {
        "1级", "2级", "3级", "4级", "5级", "全部"
    };
```

#### 深度值转换逻辑

界面选择通过`CurrentDepthValue`属性转换为具体的遍历参数：

```csharp
public int CurrentDepthValue
{
    get
    {
        switch (SelectedDepth)
        {
            case "1级": return 1;
            case "2级": return 2;
            case "3级": return 3;
            case "4级": return 4;
            case "5级": return 5;
            case "全部": return 0;  // 0表示无限深度
            default: return 1;
        }
    }
}
```

#### 核心遍历算法

**主遍历方法**：`GetItemsByDepthUnified(int maxDepth)`

```csharp
private ModelItemCollection GetItemsByDepthUnified(int maxDepth)
{
    // 深度为0表示包含所有级别
    if (maxDepth <= 0)
    {
        result.AddRange(document.Models.RootItemDescendantsAndSelf);
        return result;
    }
    
    // 精确深度控制：从文档的根模型开始遍历
    foreach (Model model in document.Models)
    {
        if (model.RootItem != null && maxDepth >= 1)
        {
            // 添加第一级节点（模型根的直接子节点）
            foreach (ModelItem firstLevelItem in model.RootItem.Children)
            {
                result.Add(firstLevelItem);
                // 递归添加更深层次的节点
                AddChildrenToDepth(firstLevelItem, result, 2, maxDepth);
            }
        }
    }
}
```

**递归遍历方法**：`AddChildrenToDepth()`

```csharp
private void AddChildrenToDepth(ModelItem parentItem, ModelItemCollection result, 
                               int currentDepth, int maxDepth)
{
    if (currentDepth > maxDepth || parentItem?.Children == null)
        return;

    foreach (ModelItem child in parentItem.Children)
    {
        result.Add(child);
        
        // 如果还没有达到最大深度，继续遍历子项
        if (currentDepth < maxDepth)
        {
            AddChildrenToDepth(child, result, currentDepth + 1, maxDepth);
        }
    }
}
```

### 2. 分层预览处理流程

#### 预览触发链路

```
用户界面操作
└── LayerManagementViewModel.PreviewSplitAsync()
    ├── 生成SplitConfiguration（包含MaxDepth参数）
    └── SimplifiedModelSplitterManager.PreviewSplit(config)
        ├── GetAllModelItems(config.MaxDepth)
        │   └── GetItemsByDepthUnified(maxDepth) // 深度控制核心
        ├── PreviewSplitByAttribute(allItems, config, strategy)
        │   └── strategy.GroupItems() // 按选定策略分组
        └── 返回List<SplitPreviewResult>
```

#### 分层策略处理

**支持的分层策略**：

- `BySmartFloorDetection`：智能楼层检测分层
- `ByFloorAttribute`：基于楼层属性分层  
- `ByZoneAttribute`：基于分区属性分层
- `BySubSystemAttribute`：基于子系统属性分层

**分层处理核心方法**：`PreviewSplitByAttribute()`

```csharp
private List<SplitPreviewResult> PreviewSplitByAttribute(
    ModelItemCollection items, SplitConfiguration config, IGroupingStrategy strategy)
{
    // 属性分组阶段 (35-70%)
    var groups = strategy.GroupItems(items, config);
    
    // 生成预览结果阶段 (70-85%)
    foreach (var group in groups.Values)
    {
        var previewResult = new SplitPreviewResult
        {
            LayerName = SanitizeLayerName(group.GroupName),
            ItemCount = group.Items.Count,
            EstimatedFileSize = EstimateFileSize(group.Items.Count),
            Status = "就绪",
            Items = new ModelItemCollection()  // 关键：保存分组的模型项
        };
        
        // 添加模型项到集合中（用于后续的隔离显示）
        if (group.Items != null && group.Items.Count > 0)
        {
            previewResult.Items.AddRange(group.Items);
        }
        
        results.Add(previewResult);
    }
}
```

### 3. 数据结构设计

#### 预览结果数据模型

**SplitPreviewItem类**（已具备选择保存功能）：

```csharp
public class SplitPreviewItem : INotifyPropertyChanged
{
    public string LayerName { get; set; }           // 分层名称
    public int ObjectCount { get; set; }            // 对象数量  
    public string EstimatedSize { get; set; }       // 预估文件大小
    public string Status { get; set; }              // 状态信息
    
    // 重要：已存在的选择保存功能
    private bool _isSelectedForSave = true;
    public bool IsSelectedForSave { get; set; }     // 是否选中用于保存，默认true
    
    // 关联的模型项集合（用于隔离显示功能）
    public ModelItemCollection Items { get; set; } = new ModelItemCollection();
}
```

#### UI数据绑定

**ViewModel中的集合管理**：

```csharp
// 预览结果集合
public ThreadSafeObservableCollection<SplitPreviewItem> SplitPreviewResults { get; }

// 当前选中的预览项
public SplitPreviewItem SelectedPreviewResult { get; set; }

// 控制预览列表显示
public bool ShowPreviewResults { get; set; }
```

### 4. 可见性控制机制

#### 参考实现：SaveSelectedItemsAsync方法

系统已有完整的节点隔离显示逻辑，核心步骤：

**1. 收集需要保持可见的节点**
```csharp
var nodesToKeepVisible = new HashSet<ModelItem>();

foreach (var selectedItem in originalSelection)
{
    // 添加选中节点本身
    nodesToKeepVisible.Add(selectedItem);
    
    // 添加所有祖先节点，确保选中节点的路径可见
    var current = selectedItem.Parent;
    while (current != null)
    {
        nodesToKeepVisible.Add(current);
        current = current.Parent;
    }
    
    // 如果启用了包含子节点选项，添加所有子节点
    if (IncludeChildNodes)
    {
        var childItems = selectedItem.DescendantsAndSelf.Where(x => x != selectedItem);
        foreach (ModelItem child in childItems)
        {
            nodesToKeepVisible.Add(child);
        }
    }
}
```

**2. 智能隐藏策略**
```csharp
// 收集所有顶级节点
var allTopLevelItems = new List<ModelItem>();
foreach (Model model in document.Models)
{
    foreach (ModelItem topLevelItem in model.RootItem.Children)
    {
        allTopLevelItems.Add(topLevelItem);
    }
}

// 决定哪些顶级分支需要隐藏
var itemsToHide = new ModelItemCollection();
foreach (ModelItem topLevelItem in allTopLevelItems)
{
    bool shouldKeepTopLevel = false;
    
    // 检查这个顶级节点或其任何子节点是否需要保持可见
    if (nodesToKeepVisible.Contains(topLevelItem))
    {
        shouldKeepTopLevel = true;
    }
    else
    {
        // 检查是否有任何选中节点是这个顶级节点的后代
        foreach (var selectedItem in originalSelection)
        {
            var current = selectedItem;
            while (current != null)
            {
                if (current == topLevelItem)
                {
                    shouldKeepTopLevel = true;
                    break;
                }
                current = current.Parent;
            }
            if (shouldKeepTopLevel) break;
        }
    }
    
    // 如果这个顶级节点不需要保持可见，则隐藏它
    if (!shouldKeepTopLevel)
    {
        itemsToHide.Add(topLevelItem);
    }
}
```

**3. 执行可见性操作**
```csharp
// 执行隐藏操作
if (itemsToHide.Count > 0)
{
    document.Models.SetHidden(itemsToHide, true);
}

// 操作完成后恢复所有项目可见性
var allItems = new ModelItemCollection();
foreach (Model model in document.Models)
{
    foreach (ModelItem item in model.RootItem.Children)
    {
        allItems.Add(item);
    }
}

if (allItems.Count > 0)
{
    document.Models.SetHidden(allItems, false);
}
```

## 深度遍历使用场景分析

### 遍历深度对分层效果的影响

| 深度级别 | 遍历范围 | 适用场景 | 性能特点 |
|----------|----------|----------|----------|
| 1级 | 只包含模型根节点的直接子节点 | 粗粒度分层，适合大型模型的快速概览 | 处理速度最快，内存占用最小 |
| 2级 | 包含二级子节点 | 中等粒度分层，平衡详细度与性能 | 适中的处理时间和内存使用 |
| 3-5级 | 包含更深层次的节点结构 | 细粒度分层，适合详细分析 | 处理时间较长，内存占用较大 |
| 全部 | 包含所有层级的节点 | 最详细的分层分析 | 处理时间最长，内存占用最大 |

### 深度设置的实际应用

**楼层分析场景**：
- 1-2级：适合按建筑主要结构分层
- 3-5级：适合按房间或具体构件分层
- 全部：适合最详细的构件级别分析

**系统分析场景**：
- 较低深度：按主要系统分类（如电气、暖通、给排水）
- 较高深度：按具体设备或管线分层

## 缓存机制

系统实现了分层结果的缓存机制，提高重复操作的性能：

```csharp
// 生成缓存键
string cacheKey = GenerateCacheKey(config);

// 检查缓存
List<SplitPreviewResult> cachedResults = GetFromCache(config.Strategy, cacheKey);
if (cachedResults != null && cachedResults.Count > 0)
{
    return cachedResults;  // 直接返回缓存结果
}

// 处理完成后保存到缓存
SaveToCache(config.Strategy, cacheKey, previewResults);
```

## 总结

NavisworksTransport的分层预览功能具备以下关键特性：

1. **灵活的深度控制**：支持1-5级精确遍历和无限深度选项
2. **完善的数据结构**：`SplitPreviewItem`已包含选择保存和模型项关联功能
3. **成熟的可见性控制**：基于`SaveSelectedItemsAsync`的参考实现
4. **多样的分层策略**：支持楼层、分区、子系统等多种分层方式
5. **高效的缓存机制**：避免重复计算，提升用户体验

系统架构设计合理，为后续的功能扩展（如选择性保存、单独显示等）提供了良好的基础。