# 多层建筑楼层过滤优化方案

## 背景

在多层建筑的路径规划中，当前系统处理所有楼层的障碍物，导致大量无关数据参与计算。如果能快速过滤掉其他楼层的障碍物，性能提升会非常显著。

## 当前实现状态

### 1. 楼层过滤已被禁用

```csharp
// GridMapGenerator.cs line 157-158
// TODO: 楼层过滤功能已暂时注释，需要时可重新启用
var modelItems = allModelItems; // 直接使用所有模型项，不进行楼层过滤
```

### 2. 现有方法的局限性

- **基于属性的过滤**（`ApplyFloorAwareFilteringWithSearchAPI`）
  - 依赖模型的"层"属性
  - 不是所有BIM模型都有规范的楼层属性
  - 需要手动维护属性映射

- **简化高程过滤**（`ApplySimplifiedFloorFiltering`）
  - 使用±10米容差
  - 过于粗糙，会包含2-3个楼层
  - 无法精确定位单个楼层

## 优化方案

### 方案1：基于Z轴高度的智能楼层识别

#### 1.1 自动检测楼层高度

```csharp
private class FloorDetector
{
    // 自动识别建筑中的楼层
    public List<FloorInfo> DetectFloors(List<ModelItem> allItems)
    {
        var floors = new List<FloorInfo>();
        
        // 1. 收集所有楼板类构件
        var slabs = allItems.Where(item => IsFloorSlab(item)).ToList();
        
        // 2. 提取楼板顶面Z坐标
        var slabZValues = slabs
            .Select(s => s.BoundingBox()?.Max.Z)
            .Where(z => z.HasValue)
            .Select(z => z.Value)
            .OrderBy(z => z)
            .ToList();
        
        // 3. 聚类相近Z值，识别独立楼层
        var floorLevels = ClusterZValues(slabZValues, tolerance: 0.5);
        
        // 4. 为每个楼层创建信息
        for (int i = 0; i < floorLevels.Count; i++)
        {
            floors.Add(new FloorInfo
            {
                Index = i,
                BaseZ = floorLevels[i],
                Height = i < floorLevels.Count - 1 
                    ? floorLevels[i + 1] - floorLevels[i]
                    : 4.0, // 默认楼层高度
                Name = $"第{i + 1}层"
            });
        }
        
        return floors;
    }
    
    // 判断是否为楼板
    private bool IsFloorSlab(ModelItem item)
    {
        var name = item.DisplayName?.ToLower() ?? "";
        var className = item.ClassName?.ToLower() ?? "";
        
        // 楼板关键词
        string[] slabKeywords = {
            "floor", "楼板", "slab", "板",
            "deck", "地面", "楼面"
        };
        
        if (slabKeywords.Any(k => name.Contains(k) || className.Contains(k)))
        {
            var bbox = item.BoundingBox();
            if (bbox != null)
            {
                // 楼板特征：水平、薄
                double thickness = bbox.Max.Z - bbox.Min.Z;
                double width = bbox.Max.X - bbox.Min.X;
                double length = bbox.Max.Y - bbox.Min.Y;
                
                // 厚度小，面积大
                return thickness < 0.5 && width > 2.0 && length > 2.0;
            }
        }
        
        return false;
    }
}
```

#### 1.2 快速楼层过滤

```csharp
private class FloorFilter
{
    private List<FloorInfo> _floors;
    private int _targetFloorIndex;
    
    public FloorFilter(List<FloorInfo> floors, Point3D startPoint, Point3D endPoint)
    {
        _floors = floors;
        _targetFloorIndex = DetermineTargetFloor(startPoint, endPoint);
    }
    
    // 确定路径所在楼层
    private int DetermineTargetFloor(Point3D startPoint, Point3D endPoint)
    {
        double avgZ = (startPoint.Z + endPoint.Z) / 2.0;
        
        // 找到最接近的楼层
        for (int i = 0; i < _floors.Count; i++)
        {
            var floor = _floors[i];
            if (avgZ >= floor.BaseZ && avgZ < floor.BaseZ + floor.Height)
            {
                return i;
            }
        }
        
        // 如果没找到，返回最接近的
        return _floors
            .Select((f, i) => new { Index = i, Distance = Math.Abs(f.BaseZ - avgZ) })
            .OrderBy(x => x.Distance)
            .First().Index;
    }
    
    // 快速判断模型项是否在目标楼层
    public bool IsOnTargetFloor(ModelItem item)
    {
        var bbox = item.BoundingBox();
        if (bbox == null) return false;
        
        var targetFloor = _floors[_targetFloorIndex];
        double floorBottom = targetFloor.BaseZ - 0.5; // 容差
        double floorTop = targetFloor.BaseZ + targetFloor.Height + 0.5;
        
        // 检查是否与楼层范围有交集
        return bbox.Max.Z >= floorBottom && bbox.Min.Z <= floorTop;
    }
    
    // 批量过滤
    public List<ModelItem> FilterItems(List<ModelItem> allItems)
    {
        return allItems.Where(IsOnTargetFloor).ToList();
    }
}
```

### 方案2：分层空间哈希索引

#### 2.1 分层索引结构

```csharp
public class LayeredSpatialHashIndex
{
    // 每个楼层有独立的空间哈希表
    private Dictionary<int, SpatialHashTable> _floorHashTables;
    private List<FloorInfo> _floors;
    private double _hashCellSize;
    
    public LayeredSpatialHashIndex(double cellSize)
    {
        _hashCellSize = cellSize;
        _floorHashTables = new Dictionary<int, SpatialHashTable>();
    }
    
    // 构建分层索引
    public void BuildIndex(List<ModelItem> items, List<FloorInfo> floors)
    {
        _floors = floors;
        
        // 为每个楼层创建独立的哈希表
        foreach (var floor in floors)
        {
            _floorHashTables[floor.Index] = new SpatialHashTable(_hashCellSize);
        }
        
        // 将模型项分配到对应楼层
        foreach (var item in items)
        {
            var bbox = item.BoundingBox();
            if (bbox == null) continue;
            
            // 确定物体所属楼层（可能跨越多个楼层）
            var overlappingFloors = GetOverlappingFloors(bbox);
            
            foreach (var floorIndex in overlappingFloors)
            {
                _floorHashTables[floorIndex].Add(item, bbox);
            }
        }
        
        LogManager.Info($"[分层索引] 构建完成：{floors.Count}个楼层，{items.Count}个模型项");
    }
    
    // 查询特定楼层的候选项
    public IEnumerable<ModelItem> QueryFloor(Point3D point, int floorIndex)
    {
        if (_floorHashTables.TryGetValue(floorIndex, out var hashTable))
        {
            return hashTable.Query(point);
        }
        return Enumerable.Empty<ModelItem>();
    }
    
    // 获取物体跨越的楼层
    private List<int> GetOverlappingFloors(BoundingBox3D bbox)
    {
        var floors = new List<int>();
        
        for (int i = 0; i < _floors.Count; i++)
        {
            var floor = _floors[i];
            double floorBottom = floor.BaseZ - 0.5;
            double floorTop = floor.BaseZ + floor.Height + 0.5;
            
            if (bbox.Max.Z >= floorBottom && bbox.Min.Z <= floorTop)
            {
                floors.Add(i);
            }
        }
        
        return floors;
    }
}
```

#### 2.2 优化的垂直扫描

```csharp
public class FloorAwareVerticalScanner
{
    private LayeredSpatialHashIndex _layeredIndex;
    private int _targetFloorIndex;
    private FloorInfo _targetFloor;
    
    // 限制扫描高度到单个楼层
    public List<HeightInterval> ScanVerticalLine(Point3D basePoint)
    {
        // 只扫描目标楼层的高度范围
        double scanMinZ = _targetFloor.BaseZ;
        double scanMaxZ = _targetFloor.BaseZ + _targetFloor.Height;
        
        // 只从目标楼层的空间哈希获取候选项
        var candidates = _layeredIndex.QueryFloor(basePoint, _targetFloorIndex);
        
        // 高度筛选（范围已经大幅缩小）
        var filtered = candidates.Where(item =>
        {
            var bbox = item.BoundingBox();
            return bbox != null && 
                   bbox.Max.Z >= scanMinZ && 
                   bbox.Min.Z <= scanMaxZ;
        });
        
        // 执行相交测试（候选项大幅减少）
        return PerformIntersectionTests(filtered, basePoint, scanMaxZ - scanMinZ);
    }
}
```

### 方案3：渐进式楼层过滤

#### 3.1 三级过滤策略

```csharp
public class ProgressiveFloorFilter
{
    // 第一级：粗略高程过滤（构建索引前）
    public List<ModelItem> CoarseFilter(List<ModelItem> items, double targetZ)
    {
        const double coarseTolerance = 5.0; // 5米容差
        
        return items.Where(item =>
        {
            var bbox = item.BoundingBox();
            if (bbox == null) return true; // 保留无边界框的项
            
            // 快速排除明显不在目标楼层的项
            return bbox.Max.Z >= targetZ - coarseTolerance && 
                   bbox.Min.Z <= targetZ + coarseTolerance;
        }).ToList();
    }
    
    // 第二级：精确楼层过滤（空间哈希构建时）
    public List<ModelItem> PreciseFilter(List<ModelItem> items, FloorInfo targetFloor)
    {
        return items.Where(item =>
        {
            var bbox = item.BoundingBox();
            if (bbox == null) return false;
            
            double floorBottom = targetFloor.BaseZ - 0.2;
            double floorTop = targetFloor.BaseZ + targetFloor.Height + 0.2;
            
            return bbox.Max.Z >= floorBottom && bbox.Min.Z <= floorTop;
        }).ToList();
    }
    
    // 第三级：垂直扫描时的高度限制
    public bool ShouldProcess(ModelItem item, double scanZ, double floorHeight)
    {
        var bbox = item.BoundingBox();
        if (bbox == null) return false;
        
        // 只处理与扫描高度相关的项
        return bbox.Max.Z >= scanZ && bbox.Min.Z <= scanZ + floorHeight;
    }
}
```

## 性能分析

### 不同场景的性能提升预期

| 建筑类型 | 楼层数 | 无过滤 | 简化过滤 | 精确过滤 | 分层索引 |
|---------|--------|--------|---------|---------|---------|
| 单层建筑 | 1 | 基准 | 无提升 | 无提升 | 无提升 |
| 低层建筑 | 3-5 | 基准 | 1.5-2倍 | 3-4倍 | 4-5倍 |
| 高层建筑 | 10-20 | 基准 | 2-3倍 | 10-15倍 | 15-20倍 |
| 超高层 | 30+ | 基准 | 3-4倍 | 20-30倍 | 30-40倍 |

### 内存开销分析

- **基础过滤**：几乎无额外内存
- **精确过滤**：楼层信息缓存，约1KB/楼层
- **分层索引**：每层独立哈希表，约100KB/楼层

## 实施建议

### 第一阶段（立即可行）

1. 重新启用现有的`GetFloorFilteredModelItems`方法
2. 将高程容差从±10米优化到±2米
3. 添加楼层检测日志，收集实际使用数据

### 第二阶段（短期优化）

1. 实现自动楼层检测
2. 基于Z轴的精确楼层过滤
3. 缓存楼层信息避免重复计算

### 第三阶段（长期优化）

1. 实现分层空间哈希索引
2. 支持跨楼层路径规划
3. 优化楼梯/电梯节点处理

## 风险与对策

### 风险1：楼层识别错误

- **对策**：提供手动楼层选择界面

### 风险2：跨楼层构件处理

- **对策**：将跨楼层构件添加到所有相关楼层

### 风险3：非标准楼层高度

- **对策**：自适应楼层高度检测

## 总结

楼层过滤是多层建筑路径规划的关键优化点。通过智能楼层识别和分层索引，可以实现10-40倍的性能提升。建议根据实际需求逐步实施，先验证简单方案的效果，再考虑复杂优化。
