- Home /
Find Nearby Points
I'm having trouble efficiently getting nearby points within a certain distance. I have a large collection of points (Vector3's) which tops out at around 500,000. I've found some solutions for finding the nearest point (nearest-neighbor search) with a KD-Tree. However, I cannot find anything that has range-searching. Are there any other methods I could use for this, or will I need to roll my own KD-Tree with range-searching? The "problem" is sort of like this: for every point, get points within a range of the point, and modify those points. The abstract idea has to do with changing colors and other properties based on proximity to other points.
I've never implemented one my self, but Octree's have been suggested for similar Questons on list in the past:
http://en.wikipedia.org/wiki/Octree
I just searched to see if Octree's were in any way superior to $$anonymous$$D-Trees and bumped into this reference:
http://cstheory.stackexchange.com/questions/8470/why-would-one-ever-use-an-octree-over-a-kd-tree
$$anonymous$$ Eppstein's answer suggest that an Octree might be superior for what you are trying to do.
Yes, I read that. I don't know though... I have never seen an Octree used for range-based finding. Do note that I'm not trying to find the nearest-neighbor, but nearest-neighbor*s* within a given radius (probably fairly small--around 1.5 or so).
I've never used an Octree or a $$anonymous$$D-Tree, so you are likely in a better position to judge the algorithms. But what I got from a quick read was that with an Octree, it is easy to find a node or leaf in the tree that contains all possible nearby points as defined by your criteria. That is it is efficient at partitioning your search set. And that it might be, on average, more efficient to search a bucket for a set of points rather than repeatedly walk a tree.
@robertbu - Yeah, this isn't my first foray into the phantasmagorical world of spatial indexing... I understand where you're co$$anonymous$$g from though, with Octrees. I'm by no mean an expert at this though. :P
Perhaps I will look into Octrees... I just don't know if it'll work for my particular problem. I'm not looking for all the nearby points, collectively. I'm looking for all the points around a given point... so searching inside a node might not work, right?
Take point A and point B. I'm trying to find all points within 1.5f units of A. B falls within that range, and it's also inside the same node/leaf as A. However, C is within 1.5f units from A, but it isn't in the same node.
The way I see it is that you walk the tree and identify any bucket/leaf that is within your search area. Assu$$anonymous$$g your bucket size is more than 2 * dist, then your best case scenario is a single bucket, and your worst is four buckets.
Answer by Jamora · Sep 25, 2013 at 06:26 PM
The most efficient solution I can think of is just plain sort the list of vectors by their sqrMagnitude to the point being compared to. Sorting is relatively fast; the fastest comparison sorts (e.g. MergeSort or QuickSort) can be done in O( n*log(n) ). If you save the sqrMagnitudes, finding the set of points within a given range is a matter of selecting an appropriate amount of elements from the beginning with a sqrMagnitude smaller than searchedDistance^2. Using binary search, this can be done in O( log(n) ) time.
However, with a large dataset even the fastest methods will take a while, so if you're looking for performance, you should first and foremost figure out a way to make your dataset smaller.
EDIT:
Some kind of a data structure would be preferable if searches are frequent. The sorting would have to be redone on each Vector3 whose nearest neighbors must be found.
Originally I was thinking of a spherical radius, which was the vibe I got from the question. However, if a cube works too, it is even simpler to find the Vectors:
List allVectors of Vector3
List neighbors of Vector3
Vector3 origin
float distance
foreach (vector in allVectors){
if (CheckDistance(origin.x, vector.x) &&
CheckDistance(origin.y, vector.y) &&
CheckDistance(origin.z, vector.z)){
neighbors.Add(vector);
}
}
bool CheckDistance(float origin, float otherPoint){
return (otherPoint > origin-distance &&
otherPoint < origin+distance)
}
If a spherical distance is required, this method could be used to pre-compute suitable vectors to minimize the work done when sorting.
Yeah, definitely agree with that last point. :P
I actually am working with dynamic meshes that can top out at several million vertices if left unoptimized, so I had to work that out first. I cut down the vertices by about 90%, so a few $$anonymous$$utes would be a plausible time to do these operations, I think.
Right now, the most expensive operation I'm doing on the vertices is a raycast. Take 12$$anonymous$$ vertices on a single mesh that needs this operation. Square that. 144 million raycasts--i.e. not feasible. Now take 12$$anonymous$$ * n where n is the number of nearby points to a given vertex. Due to the density of the mesh's geometry, n is about 10 on average. So, 120$$anonymous$$ raycasts is way, way, way faster and allows me to do some extra work with the raycasts.
I will see about sorting them in a Dictionary with your approach when I get to my dev machine later. I'll report back when I have something (hopefully good news). Thanks for the insight. Perhaps I can even run the sort in another thread and cheat a little bit. ;)
Hmm, just thinking about this, how would I efficiently get the nearest points though? Wouldn't I have to re-sort the entire list based on the point?
Good thinking! I didn't think about precomputing to cut back on necessary sorting.
I don't know if a square will work, but I'll definitely try it.
Answer by EvilWarren · Sep 26, 2013 at 05:14 PM
Interesting problem. You could throw a cube down your kd tree and see what it catches. Been a while since I used a kd tree but I remember passing a line segment down the tree to catch triangle intersections so I think throwing a cube down there should be relatively fast. You could use a sphere as well but the calculations at each node might bog you down. You can just test the sphere intersection at leaf nodes. It would probably work out quicker if you're working with small distances. I haven't checked this code for correctness but hopefully it will give you the general idea and of course I have no idea how you implemented your tree so adapt as needed
List<Vector3> GetVerticesInRange(Vector3 point, float range, KdTreeNode node)
{
List<Vector3> vertices = new List<Vector3>();
if(node.isLeafNode())
{
foreach(Vector3 v in node.verticeList)
{
float xd = point.x - v.x;
float yd = point.y - v.y;
float zd = point.z - v.z;
if((xd*xd + yd*yd + zd*zd) <= range*range )
{
vertices.Add(v);
}
}
return vertices;
}
if(point[axis]- range <= median)
{
vertices.AddRange(GetVerticesInRange(point, range, node.left));
}
if(point[axis]+ range > median)
{
vertices.AddRange(GetVerticesInRange(point, range, node.right));
}
return vertices;
}