using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.ComApi;
using ComBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
using COMApi = Autodesk.Navisworks.Api.Interop.ComApi;
using NavisworksTransport.Core;
using NavisworksTransport.Utils;
namespace NavisworksTransport.PathPlanning
{
///
/// 垂直扫描处理器
/// 实现多级筛选优化的障碍物检测和2.5D高度区间计算
/// 预期实现10,000-40,000倍性能提升
///
public class VerticalScanProcessor
{
#region 私有字段
///
/// 空间哈希表,用于快速查找邻域内的模型项
///
private readonly Dictionary> _spatialHashMap;
///
/// 空间哈希的网格大小(米)
///
private readonly double _spatialHashSize;
///
/// 并行处理的任务数量
///
private readonly int _parallelDegree;
///
/// 候选项缓存,基于空间哈希桶坐标(线程安全)
///
private readonly ConcurrentDictionary> _candidateCache;
///
/// 缓存大小限制
///
private int _maxCacheSize;
///
/// 缓存命中统计
///
private int _cacheHits;
///
/// 缓存未命中统计
///
private int _cacheMisses;
///
/// 高度筛选的容差值(米)
///
private const double HEIGHT_TOLERANCE = 0.1;
///
/// 默认人行高度(米)- 用于未检测到通道时的默认高度
///
private const double DEFAULT_WALKING_HEIGHT = 2.5;
///
/// 最小通行高度(米)
///
private const double MIN_PASSABLE_HEIGHT = 1.8;
#endregion
#region 构造函数
///
/// 构造函数
///
/// 空间哈希网格大小(米)或实际网格大小。
/// 并行度,默认为CPU核心数的一半,避免过度并行导致崩溃
public VerticalScanProcessor(double spatialHashSize, int parallelDegree = 0)
{
_spatialHashSize = spatialHashSize;
// 限制并行度,避免过度并行导致崩溃,最大为CPU核心数的一半
int maxParallelism = Math.Max(1, Environment.ProcessorCount / 2);
_parallelDegree = parallelDegree > 0 ? Math.Min(parallelDegree, maxParallelism) : maxParallelism;
_spatialHashMap = new Dictionary>();
// 初始化线程安全的缓存
_candidateCache = new ConcurrentDictionary>();
_maxCacheSize = 1000; // 默认值,将在BuildSpatialHashIndex中动态调整
_cacheHits = 0;
_cacheMisses = 0;
// 确定查询策略
string queryStrategy;
int bucketCount;
// 只用单桶模式测试
queryStrategy = "单桶模式";
bucketCount = 1;
// if (_spatialHashSize < 1.5)
// {
// queryStrategy = "单桶模式";
// bucketCount = 1;
// }
// else if (_spatialHashSize < 3.0)
// {
// queryStrategy = "十字形模式";
// bucketCount = 5;
// }
// else
// {
// queryStrategy = "3x3模式";
// bucketCount = 9;
// }
LogManager.Info($"[垂直扫描处理器] 初始化完成,空间哈希大小: {_spatialHashSize}m, 查询策略: {queryStrategy}({bucketCount}桶), 并行度: {_parallelDegree}");
}
#endregion
#region 公共方法
///
/// 构建空间哈希索引
/// 第一级筛选:邻域筛选
///
/// 所有模型项
/// 扫描边界
/// 已确定的通道元素列表(将被排除)
public void BuildSpatialHashIndex(IEnumerable modelItems, BoundingBox3D bounds, IEnumerable channelItems = null)
{
LogManager.Info("【垂直扫描处理器】 开始构建空间哈希索引");
var startTime = DateTime.Now;
_spatialHashMap.Clear();
// 创建通道元素的HashSet以便快速查找
var channelItemsSet = new HashSet(channelItems ?? new List());
// 统计变量
var totalItems = modelItems?.Count() ?? 0;
LogManager.Info($"【垂直扫描处理器】 输入统计 - 总模型项: {totalItems}, 将排除通道元素: {channelItemsSet.Count}");
LogManager.Info($"【垂直扫描处理器】 扫描边界: [{bounds.Min.X:F1},{bounds.Min.Y:F1},{bounds.Min.Z:F1}] - [{bounds.Max.X:F1},{bounds.Max.Y:F1},{bounds.Max.Z:F1}]");
var itemsWithBounds = new ConcurrentBag<(ModelItem item, BoundingBox3D bbox)>();
// 统计计数器(线程安全)
var geometryCounter = 0;
var channelExcludedCounter = 0;
var noBoundsCounter = 0;
var outOfBoundsCounter = 0;
try
{
// 并行计算所有模型项的边界框,使用ConcurrentBag避免锁竞争
Parallel.ForEach(modelItems, new ParallelOptions { MaxDegreeOfParallelism = _parallelDegree }, item =>
{
try
{
// 🔥 关键修复:检查是否需要排除通道元素(包括容器节点和子节点)
if (channelItemsSet.Contains(item) || IsChildOfChannelItems(item, channelItemsSet))
{
Interlocked.Increment(ref channelExcludedCounter);
return; // 跳过通道元素及其子节点
}
// 然后检查是否有几何体,只对有几何体的元素进行空间哈希处理
if (item?.HasGeometry == true)
{
Interlocked.Increment(ref geometryCounter);
var bbox = item.BoundingBox();
if (bbox == null)
{
Interlocked.Increment(ref noBoundsCounter);
return;
}
if (IsWithinBounds(bbox, bounds))
{
itemsWithBounds.Add((item, bbox));
}
else
{
Interlocked.Increment(ref outOfBoundsCounter);
}
}
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 获取边界框失败: {item?.DisplayName ?? "NULL"}, {ex.Message}");
}
});
}
catch (Exception ex)
{
LogManager.Error($"【垂直扫描处理器】 并行计算边界框时发生严重错误: {ex.Message}");
return; // 提前退出,避免进一步崩溃
}
// 输出详细统计信息
LogManager.Info($"【垂直扫描处理器】 元素筛选统计:");
LogManager.Info($" - 有几何体的元素: {geometryCounter}");
LogManager.Info($" - 被排除的通道元素: {channelExcludedCounter}");
LogManager.Info($" - 无边界框的元素: {noBoundsCounter}");
LogManager.Info($" - 超出扫描边界的元素: {outOfBoundsCounter}");
LogManager.Info($" - 符合条件并添加到空间哈希的元素: {itemsWithBounds.Count}");
// 记录待索引元素数量
LogManager.Info($"【垂直扫描处理器】 待索引元素数量: {itemsWithBounds.Count()}");
// 构建空间哈希
var totalHashEntries = 0;
var hashKeyDistribution = new Dictionary();
foreach (var (item, bbox) in itemsWithBounds)
{
var hashKeys = GetSpatialHashKeys(bbox);
foreach (var key in hashKeys)
{
if (!_spatialHashMap.ContainsKey(key))
{
_spatialHashMap[key] = new List();
}
_spatialHashMap[key].Add(item);
totalHashEntries++;
// 统计哈希键分布 - 修复 .NET Framework 4.8 兼容性
if (hashKeyDistribution.ContainsKey(key))
{
hashKeyDistribution[key]++;
}
else
{
hashKeyDistribution[key] = 1;
}
}
}
// 统计哈希桶信息(简化版)
var bucketSizes = _spatialHashMap.Values.Select(list => list.Count).ToList();
LogManager.Info($"【垂直扫描处理器】 空间哈希构建完成 - 哈希桶数量: {_spatialHashMap.Count}, 平均桶大小: {bucketSizes.DefaultIfEmpty(0).Average():F1}");
// 空间哈希索引构建完成
// 🔥 关键新增:动态计算缓存容量
double rangeX = bounds.Max.X - bounds.Min.X;
double rangeY = bounds.Max.Y - bounds.Min.Y;
int bucketsX = (int)(rangeX / _spatialHashSize + 1);
int bucketsY = (int)(rangeY / _spatialHashSize + 1);
int estimatedBuckets = bucketsX * bucketsY;
if (estimatedBuckets > 50000) // 大量哈希桶,需要更大缓存
{
_maxCacheSize = Math.Min(200000, estimatedBuckets); // 直接匹配桶数量
}
else if (estimatedBuckets > 20000)
{
_maxCacheSize = Math.Min(100000, estimatedBuckets);
}
else
{
_maxCacheSize = Math.Min(20000, estimatedBuckets * 2);
}
LogManager.Info($"【垂直扫描处理器】 空间哈希大小: {_spatialHashSize}m");
LogManager.Info($"【垂直扫描处理器】 范围: X={rangeX:F1}m, Y={rangeY:F1}m");
LogManager.Info($"【垂直扫描处理器】 桶数: X={bucketsX}, Y={bucketsY}, 总计={estimatedBuckets}");
LogManager.Info($"【垂直扫描处理器】 动态设置缓存容量: {_maxCacheSize} (预计哈希桶: {estimatedBuckets}, 实际哈希桶: {_spatialHashMap.Count})");
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
LogManager.Info($"【垂直扫描处理器】 空间哈希索引构建完成,耗时: {elapsed:F1}ms");
LogManager.Info($"【垂直扫描处理器】 最终统计 - 参与元素: {itemsWithBounds.Count}, 哈希桶: {_spatialHashMap.Count}, 总哈希条目: {totalHashEntries}");
}
///
/// 并行扫描网格点的2.5D高度区间
///
/// 网格点列表
/// 扫描高度(从地面向上的距离)
/// 车辆高度(用于通行检查)
/// 每个网格点的高度区间列表
public Dictionary> ParallelScanHeightIntervals(
IEnumerable gridPoints,
double scanHeight = 20.0,
double vehicleHeight = 3.0)
{
LogManager.Info($"[垂直扫描处理器] 开始并行扫描高度区间,扫描高度: {scanHeight}m, 车辆高度: {vehicleHeight}m");
var startTime = DateTime.Now;
var results = new ConcurrentDictionary>();
var pointsList = gridPoints?.ToList() ?? new List();
LogManager.Info($"[垂直扫描处理器] 准备处理{pointsList.Count}个点");
try
{
// 并行处理每个网格点,添加更严格的异常处理
Parallel.ForEach(pointsList, new ParallelOptions
{
MaxDegreeOfParallelism = _parallelDegree,
CancellationToken = System.Threading.CancellationToken.None
}, gridPoint =>
{
try
{
if (gridPoint != null)
{
var intervals = ScanVerticalLine(gridPoint, scanHeight, vehicleHeight);
results[gridPoint] = intervals ?? new List();
}
}
catch (OutOfMemoryException)
{
LogManager.Error($"[垂直扫描处理器] 内存不足,跳过点 {gridPoint}");
if (gridPoint != null)
{
results[gridPoint] = new List();
}
throw; // 内存不足需要重新抛出
}
catch (Exception ex)
{
LogManager.Error($"[垂直扫描处理器] 扫描点 {gridPoint} 失败: {ex.Message}");
if (gridPoint != null)
{
results[gridPoint] = new List();
}
}
});
}
catch (Exception ex)
{
LogManager.Error($"[垂直扫描处理器] 并行扫描过程中发生严重错误: {ex.Message}");
// 如果并行处理失败,尝试串行处理作为备选方案
LogManager.Info("[垂直扫描处理器] 尝试串行处理作为备选方案");
foreach (var gridPoint in pointsList)
{
try
{
if (gridPoint != null && !results.ContainsKey(gridPoint))
{
var intervals = ScanVerticalLine(gridPoint, scanHeight, vehicleHeight);
results[gridPoint] = intervals ?? new List();
}
}
catch (Exception serialEx)
{
LogManager.Error($"[垂直扫描处理器] 串行扫描点 {gridPoint} 也失败: {serialEx.Message}");
if (gridPoint != null)
{
results[gridPoint] = new List();
}
}
}
}
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
var totalIntervals = results.Values.Sum(list => list.Count);
// 输出缓存统计信息
if (_cacheHits + _cacheMisses > 0)
{
double hitRate = (double)_cacheHits / (_cacheHits + _cacheMisses) * 100;
LogManager.Info($"[垂直扫描处理器] 缓存统计 - 命中: {_cacheHits}, 未命中: {_cacheMisses}, 命中率: {hitRate:F1}%, 缓存大小: {_candidateCache.Count}");
}
LogManager.Info($"[垂直扫描处理器] 并行扫描完成,耗时: {elapsed:F1}ms, 扫描点: {pointsList.Count}, 总区间: {totalIntervals}");
// 清理缓存,为下次扫描做准备
_candidateCache.Clear();
_cacheHits = 0;
_cacheMisses = 0;
return new Dictionary>(results);
}
///
/// 扫描单个垂直线上的通行区间
///
/// 基础点(X,Y坐标)
/// 扫描高度
/// 车辆高度
/// 可通行的高度区间列表
public List ScanVerticalLine(Point3D basePoint, double scanHeight, double vehicleHeight)
{
// 第一级筛选:邻域筛选 - 获取空间哈希邻域内的候选项
var candidateItems = GetCandidateItemsFromSpatialHash(basePoint);
// 第二级筛选:高度筛选 - 只保留在扫描高度范围内的项目
var heightFilteredItems = HeightFiltering(candidateItems, basePoint.Z, basePoint.Z + scanHeight);
// 第三级筛选:空间哈希 - 精确的几何相交测试
var intersectionResults = PerformIntersectionTests(heightFilteredItems, basePoint, scanHeight);
// 计算可通行区间
var passableIntervals = CalculatePassableIntervals(intersectionResults, basePoint.Z, scanHeight, vehicleHeight);
return passableIntervals;
}
#endregion
#region 私有方法 - 多级筛选实现
///
/// 第一级筛选:从空间哈希中获取候选模型项(邻域筛选)
///
/// 查询点
/// 候选模型项列表
private IEnumerable GetCandidateItemsFromSpatialHash(Point3D point)
{
try
{
// 🔥 关键修复:检查坐标值的有效性
if (double.IsNaN(point.X) || double.IsNaN(point.Y) ||
double.IsInfinity(point.X) || double.IsInfinity(point.Y))
{
LogManager.Warning($"[垂直扫描处理器] 无效的坐标点: ({point.X}, {point.Y}, {point.Z})");
return new HashSet(); // 返回空集合
}
// 使用粗粒度的缓存键(基于空间哈希桶坐标)
int gridX = (int)Math.Floor(point.X / _spatialHashSize);
int gridY = (int)Math.Floor(point.Y / _spatialHashSize);
string cacheKey = $"{gridX},{gridY}";
// 🔥 关键修复:使用ConcurrentDictionary的GetOrAdd原子操作
var candidates = _candidateCache.GetOrAdd(cacheKey, key =>
{
// 这个函数只在缓存未命中时执行
Interlocked.Increment(ref _cacheMisses);
// 构建候选集合
var items = new HashSet();
var hashKeys = GetSpatialHashKeysForPoint(point);
foreach (var hkey in hashKeys)
{
if (_spatialHashMap.TryGetValue(hkey, out var bucketItems))
{
items.UnionWith(bucketItems);
}
}
// 检查缓存大小,如果超限则清空整个缓存
if (_candidateCache.Count > _maxCacheSize)
{
_candidateCache.Clear();
LogManager.Info($"[垂直扫描处理器] 缓存已满({_candidateCache.Count}>{_maxCacheSize}),清空缓存重新开始");
}
return items;
});
// 如果键已存在(GetOrAdd没有执行工厂函数),说明是缓存命中
if (_candidateCache.ContainsKey(cacheKey))
{
Interlocked.Increment(ref _cacheHits);
}
// 🔥 关键修复:返回副本以保证线程安全,避免并发修改原HashSet
return new HashSet(candidates);
}
catch (Exception ex)
{
LogManager.Error($"[垂直扫描处理器] 获取候选项失败: {ex.Message},点坐标: ({point.X}, {point.Y}, {point.Z})");
return new HashSet(); // 返回空集合
}
}
///
/// 第二级筛选:高度筛选
///
/// 候选模型项
/// 最小Z坐标
/// 最大Z坐标
/// 高度筛选后的模型项
private List HeightFiltering(IEnumerable items, double minZ, double maxZ)
{
var filtered = new ConcurrentBag();
try
{
// 使用ConcurrentBag避免锁竞争,提高性能和稳定性
Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = _parallelDegree }, item =>
{
try
{
if (item?.HasGeometry == true)
{
var bbox = item.BoundingBox();
if (bbox != null)
{
// 检查高度范围是否有重叠(加上容差)
if (bbox.Max.Z >= (minZ - HEIGHT_TOLERANCE) &&
bbox.Min.Z <= (maxZ + HEIGHT_TOLERANCE))
{
filtered.Add(item);
}
}
}
}
catch (Exception ex)
{
LogManager.Debug($"[垂直扫描处理器] 高度筛选失败: {item?.DisplayName ?? "NULL"}, {ex.Message}");
}
});
}
catch (Exception ex)
{
LogManager.Warning($"[垂直扫描处理器] 高度筛选并行处理失败: {ex.Message},回退到串行处理");
// 如果并行处理失败,使用串行处理
foreach (var item in items ?? new List())
{
try
{
if (item?.HasGeometry == true)
{
var bbox = item.BoundingBox();
if (bbox != null &&
bbox.Max.Z >= (minZ - HEIGHT_TOLERANCE) &&
bbox.Min.Z <= (maxZ + HEIGHT_TOLERANCE))
{
filtered.Add(item);
}
}
}
catch (Exception serialEx)
{
LogManager.Debug($"[垂直扫描处理器] 串行高度筛选失败: {item?.DisplayName ?? "NULL"}, {serialEx.Message}");
}
}
}
return filtered.ToList();
}
///
/// 第三级筛选:精确几何相交测试(空间哈希优化)
///
/// 高度筛选后的模型项
/// 基础点
/// 扫描高度
/// 相交测试结果
private List PerformIntersectionTests(List items, Point3D basePoint, double scanHeight)
{
var results = new ConcurrentBag();
try
{
// 并行执行相交测试,添加更强的异常处理
Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = _parallelDegree }, item =>
{
try
{
if (item != null)
{
var intersectionData = TestVerticalLineIntersection(item, basePoint, scanHeight);
if (intersectionData != null)
{
// 简化逻辑:所有与垂直射线相交的非通道元素都视为障碍物
// 因为在空间哈希构建时已经排除了通道元素
results.Add(new IntersectionResult
{
ModelItem = item,
IntersectionData = intersectionData,
IsObstacle = true, // 所有相交的非通道元素都是障碍物
IsPassable = false // 不可通行
});
}
}
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 相交测试失败: {item?.DisplayName ?? "NULL"}, {ex.Message}");
}
});
}
catch (Exception ex)
{
LogManager.Warning($"【垂直扫描处理器】 并行相交测试失败: {ex.Message},回退到串行处理");
// 如果并行处理失败,使用串行处理
foreach (var item in items ?? new List())
{
try
{
if (item != null)
{
var intersectionData = TestVerticalLineIntersection(item, basePoint, scanHeight);
if (intersectionData != null)
{
// 简化逻辑:所有与垂直射线相交的非通道元素都视为障碍物
results.Add(new IntersectionResult
{
ModelItem = item,
IntersectionData = intersectionData,
IsObstacle = true, // 所有相交的非通道元素都是障碍物
IsPassable = false // 不可通行
});
}
}
}
catch (Exception serialEx)
{
LogManager.Debug($"【垂直扫描处理器】 串行相交测试失败: {item?.DisplayName ?? "NULL"}, {serialEx.Message}");
}
}
}
return results.ToList();
}
///
/// 计算可通行区间
///
/// 相交测试结果
/// 基础Z坐标
/// 扫描高度
/// 车辆高度
/// 可通行区间列表
private List CalculatePassableIntervals(
List intersectionResults,
double baseZ,
double scanHeight,
double vehicleHeight)
{
// 收集所有障碍物的高度范围
var obstacles = new List();
var floors = new List();
foreach (var result in intersectionResults)
{
if (result.IsObstacle && result.IntersectionData != null)
{
var obstacleInterval = new HeightInterval(
result.IntersectionData.MinZ,
result.IntersectionData.MaxZ
);
obstacles.Add(obstacleInterval);
}
else if (result.IsPassable && result.IntersectionData != null)
{
// 检查是否为地面/楼板
if (IsFloorLike(result.ModelItem, result.IntersectionData))
{
var floorInterval = new HeightInterval(
result.IntersectionData.MinZ,
result.IntersectionData.MaxZ
);
floors.Add(floorInterval);
}
}
}
// 合并重叠的障碍物区间
var mergedObstacles = MergeOverlappingIntervals(obstacles);
// 计算可通行区间
var passableIntervals = new List();
var scanRange = new HeightInterval(baseZ, baseZ + scanHeight);
// 如果没有找到地面,使用基础Z坐标作为默认地面
if (!floors.Any())
{
floors.Add(new HeightInterval(baseZ - 0.1, baseZ + 0.1));
}
// 对每个潜在的地面,计算其上方的可通行空间
foreach (var floor in floors)
{
double floorTop = floor.MaxZ;
double availableTop = baseZ + scanHeight;
// 检查地面上方是否有足够的净空高度
var conflictingObstacles = mergedObstacles
.Where(obs => obs.MinZ < availableTop && obs.MaxZ > floorTop)
.OrderBy(obs => obs.MinZ)
.ToList();
if (!conflictingObstacles.Any())
{
// 没有障碍物,整个高度范围都可通行
double clearHeight = availableTop - floorTop;
if (clearHeight >= MIN_PASSABLE_HEIGHT)
{
var interval = new HeightInterval(floorTop, availableTop);
passableIntervals.Add(interval);
}
}
else
{
// 有障碍物,计算障碍物之间的可通行空间
double currentBottom = floorTop;
foreach (var obstacle in conflictingObstacles)
{
double obstacleBottom = Math.Max(obstacle.MinZ, currentBottom);
if (obstacleBottom > currentBottom)
{
double clearHeight = obstacleBottom - currentBottom;
if (clearHeight >= vehicleHeight)
{
var interval = new HeightInterval(currentBottom, obstacleBottom);
passableIntervals.Add(interval);
}
}
currentBottom = Math.Max(currentBottom, obstacle.MaxZ);
}
// 检查最后一个障碍物之后的空间
if (currentBottom < availableTop)
{
double clearHeight = availableTop - currentBottom;
if (clearHeight >= vehicleHeight)
{
var interval = new HeightInterval(currentBottom, availableTop);
passableIntervals.Add(interval);
}
}
}
}
return passableIntervals;
}
#endregion
#region 私有辅助方法
///
/// 检查边界框是否在指定范围内
///
/// 要检查的边界框
/// 范围边界框
/// 是否在范围内
private bool IsWithinBounds(BoundingBox3D bbox, BoundingBox3D bounds)
{
return bbox.Max.X >= bounds.Min.X && bbox.Min.X <= bounds.Max.X &&
bbox.Max.Y >= bounds.Min.Y && bbox.Min.Y <= bounds.Max.Y &&
bbox.Max.Z >= bounds.Min.Z && bbox.Min.Z <= bounds.Max.Z;
}
///
/// 获取边界框的所有空间哈希键
///
/// 边界框
/// 空间哈希键列表
private List GetSpatialHashKeys(BoundingBox3D bbox)
{
var keys = new List();
int minX = (int)Math.Floor(bbox.Min.X / _spatialHashSize);
int maxX = (int)Math.Floor(bbox.Max.X / _spatialHashSize);
int minY = (int)Math.Floor(bbox.Min.Y / _spatialHashSize);
int maxY = (int)Math.Floor(bbox.Max.Y / _spatialHashSize);
for (int x = minX; x <= maxX; x++)
{
for (int y = minY; y <= maxY; y++)
{
keys.Add($"{x},{y}");
}
}
return keys;
}
///
/// 获取点的空间哈希键(包括相邻网格)
///
/// 查询点
/// 空间哈希键列表
private List GetSpatialHashKeysForPoint(Point3D point)
{
var keys = new List();
int centerX = (int)Math.Floor(point.X / _spatialHashSize);
int centerY = (int)Math.Floor(point.Y / _spatialHashSize);
// 单桶模式
keys.Add($"{centerX},{centerY}");
// 智能查询范围策略:根据空间哈希大小调整查询桶数量
// - 哈希桶<1.5m:只查询中心桶(1个)
// - 哈希桶1.5-3m:查询十字形(5个桶)
// - 哈希桶>3m:查询3x3(9个桶)
// if (_spatialHashSize < 1.5)
// {
// // 单桶模式 - 对于很小的哈希桶,相邻桶基本不会有相关模型
// keys.Add($"{centerX},{centerY}");
// }
// else if (_spatialHashSize < 3.0)
// {
// // 5个桶模式(十字形)- 只查询上下左右和中心
// keys.Add($"{centerX},{centerY}");
// keys.Add($"{centerX-1},{centerY}");
// keys.Add($"{centerX+1},{centerY}");
// keys.Add($"{centerX},{centerY-1}");
// keys.Add($"{centerX},{centerY+1}");
// }
// else
// {
// // 9个桶模式(3x3)- 原有方式,用于较大的哈希桶
// for (int dx = -1; dx <= 1; dx++)
// {
// for (int dy = -1; dy <= 1; dy++)
// {
// keys.Add($"{centerX + dx},{centerY + dy}");
// }
// }
// }
return keys;
}
///
/// 测试垂直线与模型项的相交
///
/// 模型项
/// 基础点
/// 扫描高度
/// 相交数据,如果不相交则返回null
private IntersectionData TestVerticalLineIntersection(ModelItem item, Point3D basePoint, double scanHeight)
{
try
{
if (!item.HasGeometry)
return null;
// 直接提取模型项的三角形几何数据
var triangles = GeometryHelper.ExtractTrianglesOptimized(item);
if (triangles == null || triangles.Count == 0)
{
return null;
}
// 执行射线-三角形相交检测
var intersections = PerformVerticalRayIntersection(basePoint, scanHeight, triangles);
if (intersections == null || intersections.Count == 0)
{
return null;
}
// 计算相交的Z范围
double minZ = intersections.Min();
double maxZ = intersections.Max();
// 确保相交区间与扫描区间有重叠
double scanMinZ = basePoint.Z;
double scanMaxZ = basePoint.Z + scanHeight;
double intersectionMinZ = Math.Max(minZ, scanMinZ);
double intersectionMaxZ = Math.Min(maxZ, scanMaxZ);
if (intersectionMaxZ > intersectionMinZ)
{
return new IntersectionData
{
MinZ = intersectionMinZ,
MaxZ = intersectionMaxZ,
IntersectionPoint = new Point3D(basePoint.X, basePoint.Y, (intersectionMinZ + intersectionMaxZ) / 2)
};
}
return null;
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 射线相交测试异常: {item.DisplayName}, {ex.Message}");
return null;
}
}
///
/// 执行垂直射线与三角形相交检测
///
/// 射线起点
/// 扫描高度
/// 三角形列表
/// 相交点Z坐标列表
private List PerformVerticalRayIntersection(Point3D basePoint, double scanHeight, List triangles)
{
var intersectionPoints = new List();
try
{
// 创建垂直向上的射线(从basePoint开始向上扫描scanHeight距离)
var rayOrigin = new Point3D(basePoint.X, basePoint.Y, basePoint.Z);
var rayDirection = new Point3D(0, 0, 1); // 向上
foreach (var triangle in triangles)
{
if (GeometryHelper.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
{
// 检查相交点是否在扫描范围内
if (intersectionZ >= basePoint.Z && intersectionZ <= basePoint.Z + scanHeight)
{
intersectionPoints.Add(intersectionZ);
}
}
}
return intersectionPoints;
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 射线-三角形相交计算失败: {ex.Message}");
return intersectionPoints;
}
}
///
/// 检查模型项是否类似地面/楼板
///
/// 模型项
/// 相交数据
/// 是否为地面类型
private bool IsFloorLike(ModelItem item, IntersectionData intersectionData)
{
string displayName = item.DisplayName?.ToLower() ?? "";
// 通过名称判断
string[] floorKeywords = {
"地面", "floor", "楼板", "slab", "板", "deck",
"地板", "flooring", "基础", "foundation"
};
foreach (string keyword in floorKeywords)
{
if (displayName.Contains(keyword))
{
return true;
}
}
// 通过几何特征判断(薄的水平结构)
double thickness = intersectionData.MaxZ - intersectionData.MinZ;
return thickness < 0.5; // 小于50cm厚度认为是楼板
}
///
/// 合并重叠的区间
///
/// 区间列表
/// 合并后的区间列表
private List MergeOverlappingIntervals(List intervals)
{
if (!intervals.Any())
return new List();
var sortedIntervals = intervals.OrderBy(i => i.MinZ).ToList();
var merged = new List();
var current = sortedIntervals[0];
for (int i = 1; i < sortedIntervals.Count; i++)
{
var next = sortedIntervals[i];
if (next.MinZ <= current.MaxZ + HEIGHT_TOLERANCE)
{
// 合并重叠区间
current = new HeightInterval(current.MinZ, Math.Max(current.MaxZ, next.MaxZ));
}
else
{
merged.Add(current);
current = next;
}
}
merged.Add(current);
return merged;
}
///
/// 检查指定的ModelItem是否为通道集合中任意一个通道的子节点
///
/// 要检查的ModelItem
/// 通道集合
/// 如果是通道的子节点则返回true
private bool IsChildOfChannelItems(ModelItem item, HashSet channelItemsSet)
{
try
{
if (item?.Parent == null)
return false;
// 递归向上检查父节点链
var currentParent = item.Parent;
while (currentParent != null)
{
if (channelItemsSet.Contains(currentParent))
{
return true; // 找到了通道父节点
}
currentParent = currentParent.Parent;
}
return false;
}
catch (Exception ex)
{
LogManager.Debug($"【垂直扫描处理器】 检查子节点关系时出错: {item?.DisplayName ?? "NULL"}, {ex.Message}");
return false; // 出错时保守处理,不排除
}
}
#endregion
#region 内部数据结构
///
/// 相交数据结构
///
private class IntersectionData
{
public double MinZ { get; set; }
public double MaxZ { get; set; }
public Point3D IntersectionPoint { get; set; }
}
///
/// 相交测试结果
///
private class IntersectionResult
{
public ModelItem ModelItem { get; set; }
public IntersectionData IntersectionData { get; set; }
public bool IsObstacle { get; set; }
public bool IsPassable { get; set; }
}
#endregion
}
}