using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Autodesk.Navisworks.Api; using NavisworksTransport.Utils; using g4; // geometry4Sharp namespace NavisworksTransport.Core.Spatial { /// /// 全局空间索引管理器 /// 单例模式,为所有动画提供高效的空间查询服务 /// public class SpatialIndexManager { private static SpatialIndexManager _instance; private static readonly object _lockObj = new object(); /// /// 单例实例 /// public static SpatialIndexManager Instance { get { if (_instance == null) { lock (_lockObj) { if (_instance == null) { _instance = new SpatialIndexManager(); } } } return _instance; } } private SpatialHashGrid _globalSpatialIndex; private double _cellSize = 1.0; // 默认格子大小(模型单位) private bool _isInitialized = false; private int _indexedObjectCount = 0; // 缓存对象位置,避免重复计算 private readonly Dictionary _objectPositions = new Dictionary(); private SpatialIndexManager() { } /// /// 空间索引是否已初始化 /// public bool IsInitialized => _isInitialized; /// /// 格子大小(模型单位) /// public double CellSize => _cellSize; /// /// 已索引的对象数量 /// public int IndexedObjectCount => _indexedObjectCount; /// /// 构建全局空间索引 /// /// 格子大小(模型单位),建议设置为车辆半径的2倍 public void BuildGlobalIndex(double cellSizeInModelUnits = 1.0) { var sw = Stopwatch.StartNew(); LogManager.Info("[空间索引] 开始构建全局空间索引"); LogManager.Info($"[空间索引] 格子大小: {cellSizeInModelUnits:F2} 模型单位"); try { _cellSize = cellSizeInModelUnits; // 1. 获取所有几何对象 var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf .Where(item => item.HasGeometry) .ToList(); LogManager.Info($"[空间索引] 找到 {allItems.Count} 个几何对象"); if (allItems.Count == 0) { LogManager.Warning("[空间索引] 场景中没有几何对象"); return; } // 2. 创建空间哈希网格 _globalSpatialIndex = new SpatialHashGrid(cellSizeInModelUnits); _objectPositions.Clear(); // 3. 索引所有对象 int indexedCount = 0; int failedCount = 0; foreach (var item in allItems) { try { // 获取包围盒中心作为对象位置 var bbox = item.BoundingBox(); var center = new Vector3d( (bbox.Min.X + bbox.Max.X) / 2.0, (bbox.Min.Y + bbox.Max.Y) / 2.0, (bbox.Min.Z + bbox.Max.Z) / 2.0 ); // 插入到空间索引 _globalSpatialIndex.Insert(item, center); // 缓存位置 _objectPositions[item] = center; indexedCount++; } catch (Exception ex) { LogManager.Warning($"[空间索引] 索引对象失败: {item.DisplayName}, 错误: {ex.Message}"); failedCount++; } } _indexedObjectCount = indexedCount; _isInitialized = true; sw.Stop(); LogManager.Info("[空间索引] 构建完成"); LogManager.Info($" - 成功索引: {indexedCount} 个对象"); LogManager.Info($" - 失败: {failedCount} 个对象"); LogManager.Info($" - 格子数量: {_globalSpatialIndex.CellCount} 个"); LogManager.Info($" - 耗时: {sw.ElapsedMilliseconds} ms"); // 输出统计信息 LogManager.Debug("[空间索引] " + _globalSpatialIndex.GetStatistics()); } catch (Exception ex) { LogManager.Error($"[空间索引] 构建失败: {ex.Message}"); LogManager.Error($"[空间索引] 堆栈跟踪: {ex.StackTrace}"); _isInitialized = false; throw; } } /// /// 范围查询:查找指定位置附近的对象 /// /// 查询中心位置(Navisworks Point3D) /// 查询半径(模型单位) /// 要排除的对象(通常是移动物体本身) /// 范围内的对象列表 public List FindNearbyObjects( Point3D position, double searchRadiusInModelUnits, ModelItem excludeObject = null) { if (!_isInitialized) { throw new InvalidOperationException( "[空间索引] 空间索引未初始化,请先调用 BuildGlobalIndex()"); } var sw = Stopwatch.StartNew(); try { // 转换为 geometry4Sharp 的 Vector3d var centerVec = new Vector3d(position.X, position.Y, position.Z); // 使用空间哈希网格进行范围查询(带精确距离检查) var nearbyObjects = _globalSpatialIndex.FindInRadiusExact( centerVec, searchRadiusInModelUnits, getPositionFunc: item => { // 从缓存中获取对象位置 if (_objectPositions.TryGetValue(item, out var pos)) { return pos; } // 缓存未命中,重新计算(理论上不应该发生) var bbox = item.BoundingBox(); return new Vector3d( (bbox.Min.X + bbox.Max.X) / 2.0, (bbox.Min.Y + bbox.Max.Y) / 2.0, (bbox.Min.Z + bbox.Max.Z) / 2.0 ); } ); // 排除指定对象 if (excludeObject != null) { nearbyObjects = nearbyObjects.Where(obj => !obj.Equals(excludeObject)).ToList(); } sw.Stop(); LogManager.Debug($"[空间索引] 范围查询完成: " + $"位置=({position.X:F2}, {position.Y:F2}, {position.Z:F2}), " + $"半径={searchRadiusInModelUnits:F2}, " + $"结果={nearbyObjects.Count} 个对象, " + $"耗时={sw.Elapsed.TotalMilliseconds:F2}ms"); return nearbyObjects; } catch (Exception ex) { LogManager.Error($"[空间索引] 范围查询失败: {ex.Message}"); throw; } } /// /// 范围查询(带排除列表) /// /// 查询中心位置 /// 查询半径 /// 要排除的对象列表 /// 范围内的对象列表 public List FindNearbyObjects( Point3D position, double searchRadiusInModelUnits, IEnumerable excludeObjects) { var results = FindNearbyObjects(position, searchRadiusInModelUnits, excludeObject: null); if (excludeObjects != null) { var excludeSet = new HashSet(excludeObjects); results = results.Where(obj => !excludeSet.Contains(obj)).ToList(); } return results; } /// /// 获取对象的缓存位置 /// /// 模型对象 /// 对象的3D位置(Vector3d) public Vector3d? GetObjectPosition(ModelItem item) { if (_objectPositions.TryGetValue(item, out var pos)) { return pos; } return null; } /// /// 清除空间索引 /// public void Clear() { LogManager.Info("[空间索引] 清除空间索引"); _globalSpatialIndex?.Clear(); _objectPositions.Clear(); _isInitialized = false; _indexedObjectCount = 0; LogManager.Info("[空间索引] 空间索引已清除"); } /// /// 获取空间索引统计信息 /// /// 统计信息字符串 public string GetStatistics() { if (!_isInitialized) { return "[空间索引] 未初始化"; } return $"[空间索引] 统计信息:\n" + $" - 已索引对象: {_indexedObjectCount} 个\n" + $" - 格子大小: {_cellSize:F2} 模型单位\n" + $" - 格子数量: {_globalSpatialIndex.CellCount} 个\n" + $"{_globalSpatialIndex.GetStatistics()}"; } } }