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