refactor: 借鉴 PointHashGrid3d 优化空间哈希网格

改进内容:
1. 使用 ScaleGridIndexer3 实现更清晰的坐标转换(职责分离)
2. 优化 FindInRadius:提前判断 distanceFunc 是否为 null,避免循环中重复检查
3. 使用 TryGetValue 避免字典的两次查找
4. 增强代码注释,说明设计参考和增强点

参考 geometry3Sharp 的 PointHashGrid3d 设计,但保留了我们实现的优势:
- 支持返回范围内所有对象(不仅是最近的一个)
- 支持直接访问网格单元
- 提供详细的统计信息
This commit is contained in:
tian 2025-10-14 11:49:39 +08:00
parent d889635c1c
commit 8cec18a141

View File

@ -7,12 +7,18 @@ namespace NavisworksTransport.Core.Spatial
/// <summary>
/// 自定义3D空间哈希网格
/// 用于高效的空间范围查询,复杂度 O(1) 平均
///
/// 设计参考 geometry3Sharp 的 PointHashGrid3d但提供了以下增强
/// 1. 支持返回范围内的所有对象(而不仅是最近的一个)
/// 2. 支持直接访问网格单元GetObjectsInCell
/// 3. 提供详细的统计信息用于性能分析
/// 4. 使用 ScaleGridIndexer3 实现更清晰的坐标转换
/// </summary>
/// <typeparam name="T">存储的对象类型</typeparam>
public class SpatialHashGrid<T>
{
private readonly Dictionary<Vector3i, List<T>> _grid;
private readonly double _cellSize;
private readonly ScaleGridIndexer3 _indexer;
/// <summary>
/// 创建空间哈希网格
@ -23,14 +29,14 @@ namespace NavisworksTransport.Core.Spatial
if (cellSize <= 0)
throw new ArgumentException("格子大小必须大于0", nameof(cellSize));
_cellSize = cellSize;
_indexer = new ScaleGridIndexer3() { CellSize = cellSize };
_grid = new Dictionary<Vector3i, List<T>>();
}
/// <summary>
/// 格子大小(模型单位)
/// </summary>
public double CellSize => _cellSize;
public double CellSize => _indexer.CellSize;
/// <summary>
/// 格子总数
@ -60,14 +66,15 @@ namespace NavisworksTransport.Core.Spatial
/// <param name="position">对象的3D位置</param>
public void Insert(T item, Vector3d position)
{
var gridCell = ToGridCoordinates(position);
var gridCell = _indexer.ToGrid(position);
if (!_grid.ContainsKey(gridCell))
if (!_grid.TryGetValue(gridCell, out var cellList))
{
_grid[gridCell] = new List<T>();
cellList = new List<T>();
_grid[gridCell] = cellList;
}
_grid[gridCell].Add(item);
cellList.Add(item);
}
/// <summary>
@ -111,8 +118,11 @@ namespace NavisworksTransport.Core.Spatial
var results = new HashSet<T>();
// 计算需要检查的格子范围
int gridRadius = (int)Math.Ceiling(radius / _cellSize);
var centerGrid = ToGridCoordinates(center);
int gridRadius = (int)Math.Ceiling(radius / _indexer.CellSize);
var centerGrid = _indexer.ToGrid(center);
// 优化:提前创建忽略函数,避免在循环中检查 null借鉴 PointHashGrid3d
bool hasDistanceCheck = (distanceFunc != null);
// 遍历查询范围内的所有格子
for (int dx = -gridRadius; dx <= gridRadius; dx++)
@ -127,12 +137,13 @@ namespace NavisworksTransport.Core.Spatial
centerGrid.z + dz
);
// 使用 TryGetValue 避免两次字典查找(借鉴 PointHashGrid3d
if (_grid.TryGetValue(gridCell, out var cellObjects))
{
foreach (var obj in cellObjects)
{
// 如果提供了距离函数,进行精确距离检查
if (distanceFunc != null)
if (hasDistanceCheck)
{
double distance = distanceFunc(obj);
if (distance <= radius)
@ -185,21 +196,7 @@ namespace NavisworksTransport.Core.Spatial
}
/// <summary>
/// 将世界坐标转换为网格坐标
/// </summary>
/// <param name="position">世界坐标</param>
/// <returns>网格坐标</returns>
private Vector3i ToGridCoordinates(Vector3d position)
{
return new Vector3i(
(int)Math.Floor(position.x / _cellSize),
(int)Math.Floor(position.y / _cellSize),
(int)Math.Floor(position.z / _cellSize)
);
}
/// <summary>
/// 获取网格统计信息(用于调试)
/// 获取网格统计信息(用于调试和性能分析)
/// </summary>
/// <returns>统计信息字符串</returns>
public string GetStatistics()
@ -230,7 +227,7 @@ namespace NavisworksTransport.Core.Spatial
minObjectsPerCell = 0;
return $"空间哈希网格统计:\n" +
$" 格子大小: {_cellSize:F2} 模型单位\n" +
$" 格子大小: {_indexer.CellSize:F2} 模型单位\n" +
$" 总格子数: {_grid.Count}\n" +
$" 总对象数: {totalObjects}\n" +
$" 平均每格对象数: {avgObjectsPerCell:F2}\n" +