在Revit二次开发过程中,常常需要需要按位置查找图元;例如查找某个图元邻近的图元,采用遍历过滤器的方法时间复杂度O(n),如果需要查找每个图元的邻近图元时间复杂度则可以达到O(n²)。采用八叉树查找则可将其时间复杂度降到O(logn)和O(nlogn),大大提高查找效率。
public class OctTreeNode<T>
{
private XYZ _origin;
/// <summary>
/// 节点中心位置
/// </summary>
public XYZ Origin { get => _origin; }
/// <summary>
/// 是否为叶子节点
/// </summary>
public bool IsLeaf { get => this.Range <= Tolerance; }
private double _range;
/// <summary>
/// 节点范围
/// </summary>
public double Range { get => _range; }
private double _tolerance;
/// <summary>
/// 叶子节点精度值
/// </summary>
public double Tolerance { get => _tolerance; }
private List<T> _items = null;
/// <summary>
/// 所有位于节点内的元素
/// </summary>
public IList<T> Items
{
get
{
if (_items == null)
{
_items = new List<T>();
}
return _items;
}
}
private OctTreeNode<T>[,,] _children = null;
/// <summary>
/// 子节点
/// </summary>
public OctTreeNode<T>[,,] Children
{
get
{
if (_children == null)
{
_children = new OctTreeNode<T>[2, 2, 2];
}
return _children;
}
}
private OctTreeNode<T> _parent = null;
/// <summary>
/// 父节点
/// </summary>
public OctTreeNode<T> Parent { get => _parent; set => _parent = value; }
/// <summary>
/// 使用默认值创建节点
/// </summary>
public OctTreeNode() : this(new XYZ(0, 0, 0), 1e14, 1e-6) { }
/// <summary>
/// 使用指定参数创建节点
/// </summary>
/// <param name="origin">节点中心位置</param>
/// <param name="range">节点范围</param>
/// <param name="tolerance">精度</param>
public OctTreeNode(XYZ origin, double range, double tolerance)
{
this._origin = origin;
this._range = range;
this._tolerance = tolerance;
}
private OctTreeNode(OctTreeNode<T> parent, XYZ location)
{
int x_multiplier = location.X >= parent._origin.X ? 1 : -1;
int y_multiplier = location.Y >= parent._origin.Y ? 1 : -1;
int z_multiplier = location.Z >= parent._origin.Z ? 1 : -1;
int x_index = location.X >= parent._origin.X ? 1 : 0;
int y_index = location.Y >= parent._origin.Y ? 1 : 0;
int z_index = location.Z >= parent._origin.Z ? 1 : 0;
this._range = parent._range / 2;
this._tolerance = parent._tolerance;
this._origin = new XYZ(parent._origin.X + x_multiplier * parent._range / 4,
parent._origin.Y + y_multiplier * parent._range / 4,
parent._origin.Z + z_multiplier * parent._range / 4);
this.Parent = parent;
parent.Children[x_index, y_index, z_index] = this;
}
/// <summary>
/// 获取叶子节点,如果没有则创建新的叶子节点
/// </summary>
/// <param name="location"></param>
/// <returns></returns>
private OctTreeNode<T> GetOrCreate_LeafNode(XYZ location)
{
var result = this;
while (!result.IsLeaf)
{
result = result.Get_ChildNode(location) ?? new OctTreeNode<T>(result, location);
}
return result;
}
/// <summary>
/// 获取叶子节点,如果没有则返回Null
/// </summary>
/// <param name="location"></param>
/// <returns></returns>
private OctTreeNode<T> Get_LeafNode(XYZ location)
{
var result = this;
while (result != null && !result.IsLeaf)
{
result = result.Get_ChildNode(location);
}
return result;
}
/// <summary>
/// 获取子节点
/// </summary>
/// <param name="location"></param>
/// <returns></returns>
private OctTreeNode<T> Get_ChildNode(XYZ location)
{
var x_index = location.X >= this._origin.X ? 1 : 0;
var y_index = location.Y >= this._origin.Y ? 1 : 0;
var z_index = location.Z >= this._origin.Z ? 1 : 0;
return this.Children[x_index, y_index, z_index];
}
/// <summary>
/// 坐标点及附加范围的任意部分是否在节点范围内
/// </summary>
/// <param name="point">坐标点</param>
/// <param name="rangeX">X轴方向附加范围</param>
/// <param name="rangeY">Y轴方向附加范围</param>
/// <param name="rangeZ">Z轴方向附加范围</param>
/// <returns></returns>
private bool Is_Inside(XYZ point, double rangeX = 0, double rangeY = 0, double rangeZ = 0)
{
var distanceX = Math.Abs(point.X - this._origin.X);
var distanceY = Math.Abs(point.Y - this._origin.Y);
var distanceZ = Math.Abs(point.Z - this._origin.Z);
return distanceX <= _range / 2 + rangeX / 2 &&
distanceY <= _range / 2 + rangeY / 2 &&
distanceZ <= _range / 2 + rangeZ / 2;
}
/// <summary>
/// 将元素添加到对应位置
/// </summary>
/// <param name="item">要添加的元素</param>
/// <param name="location">位置坐标</param>
public void Add(T item, XYZ location)
{
if (this.IsLeaf)
{
this.Add(item);
}
else
{
this.GetOrCreate_LeafNode(location).Add(item);
}
}
private void Add(T item)
{
this.Items.Add(item);
}
private bool Remove(T item)
{
return this.Items.Remove(item);
}
/// <summary>
/// 将元素从对应位置移除
/// </summary>
/// <param name="item"></param>
/// <param name="location"></param>
public bool Remove(T item, XYZ location)
{
if (this.IsLeaf)
{
return this.Remove(item);
}
else
{
return (bool)this.Get_LeafNode(location)?.Remove(item);
}
}
/// <summary>
/// 移除所有元素
/// </summary>
public void Clear()
{
this.Items.Clear();
this._children = null;
}
/// <summary>
/// 查找指定区域内的元素
/// </summary>
/// <param name="coord">坐标位置</param>
/// <param name="xRange">X轴方向范围</param>
/// <param name="yRange">Y轴方向范围</param>
/// <param name="zRange">Z轴方向范围</param>
/// <returns></returns>
public IEnumerable<T> Find(XYZ coord, double xRange = 0, double yRange = 0, double zRange = 0)
{
List<T> result = new List<T>();
Queue<OctTreeNode<T>> nodes = new Queue<OctTreeNode<T>>(new OctTreeNode<T>[1] { this });
while (nodes.Count > 0)
{
var currrent = nodes.Dequeue();
if (currrent == null || !currrent.Is_Inside(coord, xRange, yRange, zRange))
{
continue;
}
else if (!currrent.IsLeaf)
{
foreach (var child in currrent.Children)
{
nodes.Enqueue(child);
}
}
else
{
result.AddRange(currrent.Items);
}
}
return result;
}
/// <summary>
/// 获取所有元素
/// </summary>
/// <returns></returns>
public IEnumerable<T> GetAll()
{
var result = new List<T>();
var nodes = new Queue<OctTreeNode<T>>(new OctTreeNode<T>[1] { this });
while (nodes.Count > 0)
{
var current = nodes.Dequeue();
if (current == null) continue;
result.AddRange(current.Items);
if (!current.IsLeaf)
{
foreach (var child in current.Children)
{
nodes.Enqueue(child);
}
}
}
return result;
}
}