123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- using System.Collections.Generic;
- namespace Pathfinding {
- using Pathfinding.Util;
-
-
-
-
-
-
- public class PointKDTree {
-
- public const int LeafSize = 10;
- public const int LeafArraySize = LeafSize*2 + 1;
- Node[] tree = new Node[16];
- int numNodes = 0;
- readonly List<GraphNode> largeList = new List<GraphNode>();
- readonly Stack<GraphNode[]> arrayCache = new Stack<GraphNode[]>();
- static readonly IComparer<GraphNode>[] comparers = new IComparer<GraphNode>[] { new CompareX(), new CompareY(), new CompareZ() };
- struct Node {
-
- public GraphNode[] data;
-
- public int split;
-
- public ushort count;
-
- public byte splitAxis;
- }
-
- class CompareX : IComparer<GraphNode> {
- public int Compare (GraphNode lhs, GraphNode rhs) { return lhs.position.x.CompareTo(rhs.position.x); }
- }
- class CompareY : IComparer<GraphNode> {
- public int Compare (GraphNode lhs, GraphNode rhs) { return lhs.position.y.CompareTo(rhs.position.y); }
- }
- class CompareZ : IComparer<GraphNode> {
- public int Compare (GraphNode lhs, GraphNode rhs) { return lhs.position.z.CompareTo(rhs.position.z); }
- }
- public PointKDTree() {
- tree[1] = new Node { data = GetOrCreateList() };
- }
-
- public void Add (GraphNode node) {
- numNodes++;
- Add(node, 1);
- }
-
- public void Rebuild (GraphNode[] nodes, int start, int end) {
- if (start < 0 || end < start || end > nodes.Length)
- throw new System.ArgumentException();
- for (int i = 0; i < tree.Length; i++) {
- var data = tree[i].data;
- if (data != null) {
- for (int j = 0; j < LeafArraySize; j++) data[j] = null;
- arrayCache.Push(data);
- tree[i].data = null;
- }
- }
- numNodes = end - start;
- Build(1, new List<GraphNode>(nodes), start, end);
- }
- GraphNode[] GetOrCreateList () {
-
- return arrayCache.Count > 0 ? arrayCache.Pop() : new GraphNode[LeafArraySize];
- }
- int Size (int index) {
- return tree[index].data != null ? tree[index].count : Size(2 * index) + Size(2 * index + 1);
- }
- void CollectAndClear (int index, List<GraphNode> buffer) {
- var nodes = tree[index].data;
- var count = tree[index].count;
- if (nodes != null) {
- tree[index] = new Node();
- for (int i = 0; i < count; i++) {
- buffer.Add(nodes[i]);
- nodes[i] = null;
- }
- arrayCache.Push(nodes);
- } else {
- CollectAndClear(index*2, buffer);
- CollectAndClear(index*2 + 1, buffer);
- }
- }
- static int MaxAllowedSize (int numNodes, int depth) {
-
-
-
-
- return System.Math.Min(((5 * numNodes) / 2) >> depth, (3 * numNodes) / 4);
- }
- void Rebalance (int index) {
- CollectAndClear(index, largeList);
- Build(index, largeList, 0, largeList.Count);
- largeList.ClearFast();
- }
- void EnsureSize (int index) {
- if (index >= tree.Length) {
- var newLeaves = new Node[System.Math.Max(index + 1, tree.Length*2)];
- tree.CopyTo(newLeaves, 0);
- tree = newLeaves;
- }
- }
- void Build (int index, List<GraphNode> nodes, int start, int end) {
- EnsureSize(index);
- if (end - start <= LeafSize) {
- var leafData = tree[index].data = GetOrCreateList();
- tree[index].count = (ushort)(end - start);
- for (int i = start; i < end; i++)
- leafData[i - start] = nodes[i];
- } else {
- Int3 mn, mx;
- mn = mx = nodes[start].position;
- for (int i = start; i < end; i++) {
- var p = nodes[i].position;
- mn = new Int3(System.Math.Min(mn.x, p.x), System.Math.Min(mn.y, p.y), System.Math.Min(mn.z, p.z));
- mx = new Int3(System.Math.Max(mx.x, p.x), System.Math.Max(mx.y, p.y), System.Math.Max(mx.z, p.z));
- }
- Int3 diff = mx - mn;
- var axis = diff.x > diff.y ? (diff.x > diff.z ? 0 : 2) : (diff.y > diff.z ? 1 : 2);
- nodes.Sort(start, end - start, comparers[axis]);
- int mid = (start+end)/2;
- tree[index].split = (nodes[mid-1].position[axis] + nodes[mid].position[axis] + 1)/2;
- tree[index].splitAxis = (byte)axis;
- Build(index*2 + 0, nodes, start, mid);
- Build(index*2 + 1, nodes, mid, end);
- }
- }
- void Add (GraphNode point, int index, int depth = 0) {
-
- while (tree[index].data == null) {
- index = 2 * index + (point.position[tree[index].splitAxis] < tree[index].split ? 0 : 1);
- depth++;
- }
-
- tree[index].data[tree[index].count++] = point;
-
- if (tree[index].count >= LeafArraySize) {
- int levelsUp = 0;
-
-
-
- while (depth - levelsUp > 0 && Size(index >> levelsUp) > MaxAllowedSize(numNodes, depth-levelsUp)) {
- levelsUp++;
- }
- Rebalance(index >> levelsUp);
- }
- }
-
- public GraphNode GetNearest (Int3 point, NNConstraint constraint) {
- GraphNode best = null;
- long bestSqrDist = long.MaxValue;
- GetNearestInternal(1, point, constraint, ref best, ref bestSqrDist);
- return best;
- }
- void GetNearestInternal (int index, Int3 point, NNConstraint constraint, ref GraphNode best, ref long bestSqrDist) {
- var data = tree[index].data;
- if (data != null) {
- for (int i = tree[index].count - 1; i >= 0; i--) {
- var dist = (data[i].position - point).sqrMagnitudeLong;
- if (dist < bestSqrDist && (constraint == null || constraint.Suitable(data[i]))) {
- bestSqrDist = dist;
- best = data[i];
- }
- }
- } else {
- var dist = (long)(point[tree[index].splitAxis] - tree[index].split);
- var childIndex = 2 * index + (dist < 0 ? 0 : 1);
- GetNearestInternal(childIndex, point, constraint, ref best, ref bestSqrDist);
-
- if (dist*dist < bestSqrDist) {
-
- GetNearestInternal(childIndex ^ 0x1, point, constraint, ref best, ref bestSqrDist);
- }
- }
- }
-
- public GraphNode GetNearestConnection (Int3 point, NNConstraint constraint, long maximumSqrConnectionLength) {
- GraphNode best = null;
- long bestSqrDist = long.MaxValue;
-
-
-
-
-
- long offset = (maximumSqrConnectionLength+3)/4;
- GetNearestConnectionInternal(1, point, constraint, ref best, ref bestSqrDist, offset);
- return best;
- }
- void GetNearestConnectionInternal (int index, Int3 point, NNConstraint constraint, ref GraphNode best, ref long bestSqrDist, long distanceThresholdOffset) {
- var data = tree[index].data;
- if (data != null) {
- var pointv3 = (UnityEngine.Vector3)point;
- for (int i = tree[index].count - 1; i >= 0; i--) {
- var dist = (data[i].position - point).sqrMagnitudeLong;
-
- if (dist - distanceThresholdOffset < bestSqrDist && (constraint == null || constraint.Suitable(data[i]))) {
-
-
- var conns = (data[i] as PointNode).connections;
- if (conns != null) {
- var nodePos = (UnityEngine.Vector3)data[i].position;
- for (int j = 0; j < conns.Length; j++) {
-
-
- var connectionMidpoint = ((UnityEngine.Vector3)conns[j].node.position + nodePos) * 0.5f;
- float sqrConnectionDistance = VectorMath.SqrDistancePointSegment(nodePos, connectionMidpoint, pointv3);
-
- long sqrConnectionDistanceInt = (long)(sqrConnectionDistance*Int3.FloatPrecision*Int3.FloatPrecision);
- if (sqrConnectionDistanceInt < bestSqrDist) {
- bestSqrDist = sqrConnectionDistanceInt;
- best = data[i];
- }
- }
- }
-
-
- if (dist < bestSqrDist) {
- bestSqrDist = dist;
- best = data[i];
- }
- }
- }
- } else {
- var dist = (long)(point[tree[index].splitAxis] - tree[index].split);
- var childIndex = 2 * index + (dist < 0 ? 0 : 1);
- GetNearestConnectionInternal(childIndex, point, constraint, ref best, ref bestSqrDist, distanceThresholdOffset);
-
-
- if (dist*dist - distanceThresholdOffset < bestSqrDist) {
-
- GetNearestConnectionInternal(childIndex ^ 0x1, point, constraint, ref best, ref bestSqrDist, distanceThresholdOffset);
- }
- }
- }
-
-
-
-
-
- public void GetInRange (Int3 point, long sqrRadius, List<GraphNode> buffer) {
- GetInRangeInternal(1, point, sqrRadius, buffer);
- }
- void GetInRangeInternal (int index, Int3 point, long sqrRadius, List<GraphNode> buffer) {
- var data = tree[index].data;
- if (data != null) {
- for (int i = tree[index].count - 1; i >= 0; i--) {
- var dist = (data[i].position - point).sqrMagnitudeLong;
- if (dist < sqrRadius) {
- buffer.Add(data[i]);
- }
- }
- } else {
- var dist = (long)(point[tree[index].splitAxis] - tree[index].split);
-
- var childIndex = 2 * index + (dist < 0 ? 0 : 1);
- GetInRangeInternal(childIndex, point, sqrRadius, buffer);
-
- if (dist*dist < sqrRadius) {
-
- GetInRangeInternal(childIndex ^ 0x1, point, sqrRadius, buffer);
- }
- }
- }
- }
- }
|