Added KD tree
This commit is contained in:
136
Runtime/KDTree/KDQuery/Base.cs
Normal file
136
Runtime/KDTree/KDQuery/Base.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
/*MIT License
|
||||
|
||||
Copyright(c) 2018 Vili Volčini / viliwonka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
The object used for querying. This object should be persistent - re-used for querying.
|
||||
Contains internal array for pooling, so that it doesn't generate (too much) garbage.
|
||||
The array never down-sizes, only up-sizes, so the more you use this object, less garbage will it make over time.
|
||||
|
||||
Should be used only by 1 thread,
|
||||
which means each thread should have it's own KDQuery object in order for querying to be thread safe.
|
||||
|
||||
KDQuery can query different KDTrees.
|
||||
*/
|
||||
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace DataStructures.ViliWonka.KDTree {
|
||||
|
||||
public partial class KDQuery {
|
||||
|
||||
protected KDQueryNode[] queueArray; // queue array
|
||||
protected Heap.MinHeap<KDQueryNode> minHeap; //heap for k-nearest
|
||||
protected int count = 0; // size of queue
|
||||
protected int queryIndex = 0; // current index at stack
|
||||
|
||||
/// <summary>
|
||||
/// Returns initialized node from stack that also acts as a pool
|
||||
/// The returned reference to node stays in stack
|
||||
/// </summary>
|
||||
/// <returns>Reference to pooled node</returns>
|
||||
private KDQueryNode PushGetQueue() {
|
||||
|
||||
KDQueryNode node = null;
|
||||
|
||||
if (count < queueArray.Length) {
|
||||
|
||||
if (queueArray[count] == null)
|
||||
queueArray[count] = node = new KDQueryNode();
|
||||
else
|
||||
node = queueArray[count];
|
||||
}
|
||||
else {
|
||||
|
||||
// automatic resize of pool
|
||||
Array.Resize(ref queueArray, queueArray.Length * 2);
|
||||
node = queueArray[count] = new KDQueryNode();
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
protected void PushToQueue(KDNode node, Vector3 tempClosestPoint) {
|
||||
|
||||
var queryNode = PushGetQueue();
|
||||
queryNode.node = node;
|
||||
queryNode.tempClosestPoint = tempClosestPoint;
|
||||
}
|
||||
|
||||
protected void PushToHeap(KDNode node, Vector3 tempClosestPoint, Vector3 queryPosition) {
|
||||
|
||||
var queryNode = PushGetQueue();
|
||||
queryNode.node = node;
|
||||
queryNode.tempClosestPoint = tempClosestPoint;
|
||||
|
||||
float sqrDist = Vector3.SqrMagnitude(tempClosestPoint - queryPosition);
|
||||
queryNode.distance = sqrDist;
|
||||
minHeap.PushObj(queryNode, sqrDist);
|
||||
}
|
||||
|
||||
protected int LeftToProcess {
|
||||
|
||||
get {
|
||||
return count - queryIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// just gets unprocessed node from stack
|
||||
// increases queryIndex
|
||||
protected KDQueryNode PopFromQueue() {
|
||||
|
||||
var node = queueArray[queryIndex];
|
||||
queryIndex++;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
protected KDQueryNode PopFromHeap() {
|
||||
|
||||
KDQueryNode heapNode = minHeap.PopObj();
|
||||
|
||||
queueArray[queryIndex]= heapNode;
|
||||
queryIndex++;
|
||||
|
||||
return heapNode;
|
||||
}
|
||||
|
||||
protected void Reset() {
|
||||
|
||||
count = 0;
|
||||
queryIndex = 0;
|
||||
minHeap.Clear();
|
||||
}
|
||||
|
||||
public KDQuery(int queryNodesContainersInitialSize = 2048) {
|
||||
queueArray = new KDQueryNode[queryNodesContainersInitialSize];
|
||||
minHeap = new Heap.MinHeap<KDQueryNode>(queryNodesContainersInitialSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
2
Runtime/KDTree/KDQuery/Base.cs.meta
Normal file
2
Runtime/KDTree/KDQuery/Base.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7967af123067d1622a5c5fbc948b1f19
|
||||
56
Runtime/KDTree/KDQuery/Debug.cs
Normal file
56
Runtime/KDTree/KDQuery/Debug.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
/*MIT License
|
||||
|
||||
Copyright(c) 2018 Vili Volčini / viliwonka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#define KDTREE_VISUAL_DEBUG
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace DataStructures.ViliWonka.KDTree {
|
||||
|
||||
public partial class KDQuery {
|
||||
|
||||
// uses gizmos
|
||||
public void DrawLastQuery() {
|
||||
|
||||
Color start = Color.red;
|
||||
Color end = Color.green;
|
||||
|
||||
start.a = 0.25f;
|
||||
end.a = 0.25f;
|
||||
|
||||
for(int i = 0; i < queryIndex; i++) {
|
||||
|
||||
float val = i / (float)queryIndex;
|
||||
|
||||
Gizmos.color = Color.Lerp(end, start, val);
|
||||
|
||||
Bounds b = queueArray[i].node.bounds.Bounds;
|
||||
|
||||
Gizmos.DrawWireCube(b.center, b.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2
Runtime/KDTree/KDQuery/Debug.cs.meta
Normal file
2
Runtime/KDTree/KDQuery/Debug.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2a2d436a25ed2ea89df09bc3572fb7d
|
||||
45
Runtime/KDTree/KDQuery/KDQueryNode.cs
Normal file
45
Runtime/KDTree/KDQuery/KDQueryNode.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
/*MIT License
|
||||
|
||||
Copyright(c) 2018 Vili Volčini / viliwonka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace DataStructures.ViliWonka.KDTree {
|
||||
|
||||
public class KDQueryNode {
|
||||
|
||||
public KDNode node;
|
||||
public Vector3 tempClosestPoint;
|
||||
public float distance;
|
||||
|
||||
public KDQueryNode() {
|
||||
|
||||
}
|
||||
|
||||
public KDQueryNode(KDNode node, Vector3 tempClosestPoint) {
|
||||
this.node = node;
|
||||
this.tempClosestPoint = tempClosestPoint;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
2
Runtime/KDTree/KDQuery/KDQueryNode.cs.meta
Normal file
2
Runtime/KDTree/KDQuery/KDQueryNode.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0db79addaf1dc6c12939c505533a7fe7
|
||||
144
Runtime/KDTree/KDQuery/QueryClosest.cs
Normal file
144
Runtime/KDTree/KDQuery/QueryClosest.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
/*MIT License
|
||||
|
||||
Copyright(c) 2018 Vili Volčini / viliwonka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace DataStructures.ViliWonka.KDTree {
|
||||
|
||||
using Heap;
|
||||
|
||||
public partial class KDQuery {
|
||||
|
||||
public void ClosestPoint(KDTree tree, Vector3 queryPosition, List<int> resultIndices, List<float> resultDistances = null) {
|
||||
|
||||
Reset();
|
||||
|
||||
Vector3[] points = tree.Points;
|
||||
int[] permutation = tree.Permutation;
|
||||
|
||||
if (points.Length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int smallestIndex = 0;
|
||||
/// Smallest Squared Radius
|
||||
float SSR = Single.PositiveInfinity;
|
||||
|
||||
|
||||
var rootNode = tree.RootNode;
|
||||
|
||||
Vector3 rootClosestPoint = rootNode.bounds.ClosestPoint(queryPosition);
|
||||
|
||||
PushToHeap(rootNode, rootClosestPoint, queryPosition);
|
||||
|
||||
KDQueryNode queryNode = null;
|
||||
KDNode node = null;
|
||||
|
||||
int partitionAxis;
|
||||
float partitionCoord;
|
||||
|
||||
Vector3 tempClosestPoint;
|
||||
|
||||
// searching
|
||||
while(minHeap.Count > 0) {
|
||||
|
||||
queryNode = PopFromHeap();
|
||||
|
||||
if(queryNode.distance > SSR)
|
||||
continue;
|
||||
|
||||
node = queryNode.node;
|
||||
|
||||
if(!node.Leaf) {
|
||||
|
||||
partitionAxis = node.partitionAxis;
|
||||
partitionCoord = node.partitionCoordinate;
|
||||
|
||||
tempClosestPoint = queryNode.tempClosestPoint;
|
||||
|
||||
if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) {
|
||||
|
||||
// we already know we are on the side of negative bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
PushToHeap(node.negativeChild, tempClosestPoint, queryPosition);
|
||||
// project the tempClosestPoint to other bound
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
if(node.positiveChild.Count != 0) {
|
||||
|
||||
PushToHeap(node.positiveChild, tempClosestPoint, queryPosition);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// we already know we are on the side of positive bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
PushToHeap(node.positiveChild, tempClosestPoint, queryPosition);
|
||||
// project the tempClosestPoint to other bound
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
if(node.positiveChild.Count != 0) {
|
||||
|
||||
PushToHeap(node.negativeChild, tempClosestPoint, queryPosition);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
float sqrDist;
|
||||
// LEAF
|
||||
for(int i = node.start; i < node.end; i++) {
|
||||
|
||||
int index = permutation[i];
|
||||
|
||||
sqrDist = Vector3.SqrMagnitude(points[index] - queryPosition);
|
||||
|
||||
if(sqrDist <= SSR) {
|
||||
|
||||
SSR = sqrDist;
|
||||
smallestIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
resultIndices.Add(smallestIndex);
|
||||
|
||||
if(resultDistances != null) {
|
||||
resultDistances.Add(SSR);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
2
Runtime/KDTree/KDQuery/QueryClosest.cs.meta
Normal file
2
Runtime/KDTree/KDQuery/QueryClosest.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c02618e93fdf8299bc09f6285de4d5e
|
||||
151
Runtime/KDTree/KDQuery/QueryInterval.cs
Normal file
151
Runtime/KDTree/KDQuery/QueryInterval.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
/*MIT License
|
||||
|
||||
Copyright(c) 2018 Vili Volčini / viliwonka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace DataStructures.ViliWonka.KDTree {
|
||||
|
||||
public partial class KDQuery {
|
||||
|
||||
public void Interval(KDTree tree, Vector3 min, Vector3 max, List<int> resultIndices) {
|
||||
|
||||
Reset();
|
||||
|
||||
Vector3[] points = tree.Points;
|
||||
int[] permutation = tree.Permutation;
|
||||
|
||||
var rootNode = tree.RootNode;
|
||||
|
||||
PushToQueue(
|
||||
|
||||
rootNode,
|
||||
rootNode.bounds.ClosestPoint((min + max) / 2)
|
||||
);
|
||||
|
||||
KDQueryNode queryNode = null;
|
||||
KDNode node = null;
|
||||
|
||||
|
||||
// KD search with pruning (don't visit areas which distance is more away than range)
|
||||
// Recursion done on Stack
|
||||
while(LeftToProcess > 0) {
|
||||
|
||||
queryNode = PopFromQueue();
|
||||
node = queryNode.node;
|
||||
|
||||
if(!node.Leaf) {
|
||||
|
||||
int partitionAxis = node.partitionAxis;
|
||||
float partitionCoord = node.partitionCoordinate;
|
||||
|
||||
Vector3 tempClosestPoint = queryNode.tempClosestPoint;
|
||||
|
||||
if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) {
|
||||
|
||||
// we already know we are inside negative bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
// tempClosestPoint is inside negative side
|
||||
// assign it to negativeChild
|
||||
PushToQueue(node.negativeChild, tempClosestPoint);
|
||||
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
// testing other side
|
||||
if(node.positiveChild.Count != 0
|
||||
&& tempClosestPoint[partitionAxis] <= max[partitionAxis]) {
|
||||
|
||||
PushToQueue(node.positiveChild, tempClosestPoint);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// we already know we are inside positive bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
// tempClosestPoint is inside positive side
|
||||
// assign it to positiveChild
|
||||
PushToQueue(node.positiveChild, tempClosestPoint);
|
||||
|
||||
// project the tempClosestPoint to other bound
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
// testing other side
|
||||
if(node.negativeChild.Count != 0
|
||||
&& tempClosestPoint[partitionAxis] >= min[partitionAxis]) {
|
||||
|
||||
PushToQueue(node.negativeChild, tempClosestPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// LEAF
|
||||
|
||||
// testing if node bounds are inside the query interval
|
||||
if(node.bounds.min[0] >= min[0]
|
||||
&& node.bounds.min[1] >= min[1]
|
||||
&& node.bounds.min[2] >= min[2]
|
||||
|
||||
&& node.bounds.max[0] <= max[0]
|
||||
&& node.bounds.max[1] <= max[1]
|
||||
&& node.bounds.max[2] <= max[2]) {
|
||||
|
||||
for(int i = node.start; i < node.end; i++) {
|
||||
|
||||
resultIndices.Add(permutation[i]);
|
||||
}
|
||||
|
||||
}
|
||||
// node is not inside query interval, need to do test on each point separately
|
||||
else {
|
||||
|
||||
for(int i = node.start; i < node.end; i++) {
|
||||
|
||||
int index = permutation[i];
|
||||
|
||||
Vector3 v = points[index];
|
||||
|
||||
if(v[0] >= min[0]
|
||||
&& v[1] >= min[1]
|
||||
&& v[2] >= min[2]
|
||||
|
||||
&& v[0] <= max[0]
|
||||
&& v[1] <= max[1]
|
||||
&& v[2] <= max[2]) {
|
||||
|
||||
resultIndices.Add(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2
Runtime/KDTree/KDQuery/QueryInterval.cs.meta
Normal file
2
Runtime/KDTree/KDQuery/QueryInterval.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 201cd9cbfb7b7005285e57eb971698cf
|
||||
160
Runtime/KDTree/KDQuery/QueryKNearest.cs
Normal file
160
Runtime/KDTree/KDQuery/QueryKNearest.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
/*MIT License
|
||||
|
||||
Copyright(c) 2018 Vili Volčini / viliwonka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#define KDTREE_VISUAL_DEBUG
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace DataStructures.ViliWonka.KDTree {
|
||||
|
||||
using Heap;
|
||||
|
||||
public partial class KDQuery {
|
||||
|
||||
SortedList<int, KSmallestHeap<int>> _heaps = new SortedList<int, KSmallestHeap<int>>();
|
||||
/// <summary>
|
||||
/// Returns indices to k closest points, and optionaly can return distances
|
||||
/// </summary>
|
||||
/// <param name="tree">Tree to do search on</param>
|
||||
/// <param name="queryPosition">Position</param>
|
||||
/// <param name="k">Max number of points</param>
|
||||
/// <param name="resultIndices">List where resulting indices will be stored</param>
|
||||
/// <param name="resultDistances">Optional list where resulting distances will be stored</param>
|
||||
public void KNearest(KDTree tree, Vector3 queryPosition, int k, List<int> resultIndices, List<float> resultDistances = null) {
|
||||
|
||||
// pooled heap arrays
|
||||
KSmallestHeap<int> kHeap;
|
||||
|
||||
_heaps.TryGetValue(k, out kHeap);
|
||||
|
||||
if(kHeap == null) {
|
||||
|
||||
kHeap = new KSmallestHeap<int>(k);
|
||||
_heaps.Add(k, kHeap);
|
||||
}
|
||||
|
||||
kHeap.Clear();
|
||||
Reset();
|
||||
|
||||
Vector3[] points = tree.Points;
|
||||
int[] permutation = tree.Permutation;
|
||||
|
||||
///Biggest Smallest Squared Radius
|
||||
float BSSR = Single.PositiveInfinity;
|
||||
|
||||
var rootNode = tree.RootNode;
|
||||
|
||||
Vector3 rootClosestPoint = rootNode.bounds.ClosestPoint(queryPosition);
|
||||
|
||||
PushToHeap(rootNode, rootClosestPoint, queryPosition);
|
||||
|
||||
KDQueryNode queryNode = null;
|
||||
KDNode node = null;
|
||||
|
||||
int partitionAxis;
|
||||
float partitionCoord;
|
||||
|
||||
Vector3 tempClosestPoint;
|
||||
|
||||
// searching
|
||||
while(minHeap.Count > 0) {
|
||||
|
||||
queryNode = PopFromHeap();
|
||||
|
||||
if(queryNode.distance > BSSR)
|
||||
continue;
|
||||
|
||||
node = queryNode.node;
|
||||
|
||||
if(!node.Leaf) {
|
||||
|
||||
partitionAxis = node.partitionAxis;
|
||||
partitionCoord = node.partitionCoordinate;
|
||||
|
||||
tempClosestPoint = queryNode.tempClosestPoint;
|
||||
|
||||
if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) {
|
||||
|
||||
// we already know we are on the side of negative bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
PushToHeap(node.negativeChild, tempClosestPoint, queryPosition);
|
||||
// project the tempClosestPoint to other bound
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
if(node.positiveChild.Count != 0) {
|
||||
|
||||
PushToHeap(node.positiveChild, tempClosestPoint, queryPosition);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// we already know we are on the side of positive bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
PushToHeap(node.positiveChild, tempClosestPoint, queryPosition);
|
||||
// project the tempClosestPoint to other bound
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
if(node.positiveChild.Count != 0) {
|
||||
|
||||
PushToHeap(node.negativeChild, tempClosestPoint, queryPosition);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
float sqrDist;
|
||||
// LEAF
|
||||
for(int i = node.start; i < node.end; i++) {
|
||||
|
||||
int index = permutation[i];
|
||||
|
||||
sqrDist = Vector3.SqrMagnitude(points[index] - queryPosition);
|
||||
|
||||
if(sqrDist <= BSSR) {
|
||||
|
||||
kHeap.PushObj(index, sqrDist);
|
||||
|
||||
if(kHeap.Full) {
|
||||
BSSR = kHeap.HeadValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
kHeap.FlushResult(resultIndices, resultDistances);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
2
Runtime/KDTree/KDQuery/QueryKNearest.cs.meta
Normal file
2
Runtime/KDTree/KDQuery/QueryKNearest.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3a3e4ea20ad993c68c63d2dccd56e56
|
||||
131
Runtime/KDTree/KDQuery/QueryRadius.cs
Normal file
131
Runtime/KDTree/KDQuery/QueryRadius.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
/*MIT License
|
||||
|
||||
Copyright(c) 2018 Vili Volčini / viliwonka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace DataStructures.ViliWonka.KDTree {
|
||||
|
||||
public partial class KDQuery {
|
||||
|
||||
/// <summary>
|
||||
/// Search by radius method.
|
||||
/// </summary>
|
||||
/// <param name="tree">Tree to do search on</param>
|
||||
/// <param name="queryPosition">Position</param>
|
||||
/// <param name="queryRadius">Radius</param>
|
||||
/// <param name="resultIndices">Initialized list, cleared.</param>
|
||||
public void Radius(KDTree tree, Vector3 queryPosition, float queryRadius, List<int> resultIndices) {
|
||||
|
||||
Reset();
|
||||
|
||||
Vector3[] points = tree.Points;
|
||||
int[] permutation = tree.Permutation;
|
||||
|
||||
float squaredRadius = queryRadius * queryRadius;
|
||||
|
||||
var rootNode = tree.RootNode;
|
||||
|
||||
PushToQueue(rootNode, rootNode.bounds.ClosestPoint(queryPosition));
|
||||
|
||||
KDQueryNode queryNode = null;
|
||||
KDNode node = null;
|
||||
|
||||
// KD search with pruning (don't visit areas which distance is more away than range)
|
||||
// Recursion done on Stack
|
||||
while(LeftToProcess > 0) {
|
||||
|
||||
queryNode = PopFromQueue();
|
||||
node = queryNode.node;
|
||||
|
||||
if(!node.Leaf) {
|
||||
|
||||
int partitionAxis = node.partitionAxis;
|
||||
float partitionCoord = node.partitionCoordinate;
|
||||
|
||||
Vector3 tempClosestPoint = queryNode.tempClosestPoint;
|
||||
|
||||
if((tempClosestPoint[partitionAxis] - partitionCoord) < 0) {
|
||||
|
||||
// we already know we are inside negative bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
// tempClosestPoint is inside negative side
|
||||
// assign it to negativeChild
|
||||
PushToQueue(node.negativeChild, tempClosestPoint);
|
||||
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
float sqrDist = Vector3.SqrMagnitude(tempClosestPoint - queryPosition);
|
||||
|
||||
// testing other side
|
||||
if(node.positiveChild.Count != 0
|
||||
&& sqrDist <= squaredRadius) {
|
||||
|
||||
PushToQueue(node.positiveChild, tempClosestPoint);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// we already know we are inside positive bound/node,
|
||||
// so we don't need to test for distance
|
||||
// push to stack for later querying
|
||||
|
||||
// tempClosestPoint is inside positive side
|
||||
// assign it to positiveChild
|
||||
PushToQueue(node.positiveChild, tempClosestPoint);
|
||||
|
||||
// project the tempClosestPoint to other bound
|
||||
tempClosestPoint[partitionAxis] = partitionCoord;
|
||||
|
||||
float sqrDist = Vector3.SqrMagnitude(tempClosestPoint - queryPosition);
|
||||
|
||||
// testing other side
|
||||
if(node.negativeChild.Count != 0
|
||||
&& sqrDist <= squaredRadius) {
|
||||
|
||||
PushToQueue(node.negativeChild, tempClosestPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// LEAF
|
||||
for(int i = node.start; i < node.end; i++) {
|
||||
|
||||
int index = permutation[i];
|
||||
|
||||
if(Vector3.SqrMagnitude(points[index] - queryPosition) <= squaredRadius) {
|
||||
|
||||
resultIndices.Add(index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
2
Runtime/KDTree/KDQuery/QueryRadius.cs.meta
Normal file
2
Runtime/KDTree/KDQuery/QueryRadius.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ac26aab538b2e7b3934a1ef1e96549a
|
||||
Reference in New Issue
Block a user