38
\$\begingroup\$

I have a sizable game engine and I'd like a feature for finding the nearest of a list of points.

I could simply use the Pythagorean theorem to find each distance and choose the minimum one, but that requires iterating through all of them.

I also have a collision system, where essentially I turn objects into smaller objects on a smaller grid (kind of like a minimap) and only if objects exist in the same gridspace do I check for collisions. I could do that, only make the grid spacing larger to check for closeness. (Rather than checking every single object.) However, that would take additional setup in my base class and clutter up the already cluttered object. Is it worth it?

Is there something efficient and accurate I could use to detect which object is closest, based on a list of points and sizes?

\$\endgroup\$
3

5 Answers 5

41
\$\begingroup\$

The problem with a quad/octree in nearest-neighbor searches is that the closest object may be sitting right across the division between nodes. For collisions, this is okay, because if it's not in the node, we don't care about it. But consider this 2D example with a quadtree:

Quadtree example

Here, even though the black item and green item are in the same node, the black item is closest to the blue item. ultifinitus' answer can only guarantee the nearest-neighbor only every item in your tree is placed in the smallest possible node that could contain it, or in a unique node - this leads to more inefficient quadtrees. (Note that there are many different ways to implement a structure which could be called a quad/octree - more strict implementations may work better in this application.)

A better option would be a kd-tree. Kd-trees have a very efficient nearest-neighbor search algorithm you can implement, and can contain any number of dimensions (hence "k" dimensions.)

A great and informative animation from Wikipedia: kd-tree nearest-neighbor search

The biggest problem with using kd-trees, if I recall correctly, is that they are more difficult to insert/remove items from while maintaining balance. Therefore, I would recommend using one kd-tree for static objects such as houses and trees which is highly balanced, and another which contains players and vehicles, which needs balancing regularly. Find the nearest static object and the nearest mobile object, and compare those two.

Lastly, kd-trees are relatively simple to implement, and I'm sure you can find a multitude of C++ libraries with them. From what I remember, R-trees are much more complicated, and probably overkill if all you need is a simple nearest-neighbor search.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Great answer, small detail "an only guarantee the nearest-neighbor only every item in your tree is placed in the smallest possible node" I meant in my answer iterating over all items in the same and neighbour nodes, so you loop over 10 instead of 10.000. \$\endgroup\$
    – Roy T.
    Commented Jul 2, 2011 at 8:27
  • 1
    \$\begingroup\$ Very true - I suppose "only" was a rather harsh word. There are definitely ways to coax quadtrees into nearest-neighbor searches depending on how you implement them, but if you're not using them for other reasons already (such as collision detection,) I'd stick with the more optimized kd-tree. \$\endgroup\$
    – dlras2
    Commented Jul 2, 2011 at 8:56
  • \$\begingroup\$ I wanted to note that I made a implementation that deals with the black green blue problem. Check the bottom. \$\endgroup\$
    – clankill3r
    Commented Jan 9, 2015 at 16:34
19
\$\begingroup\$

sqrt() is monotonic, or order-preserving, for non-negative arguments so:

sqrt(x) < sqrt(y) iff x < y

And vice versa.

So if you only want to compare two distances but are not interested in their actual values you can just cut out the sqrt()-step from your Pythagoras-stuff:

pseudoDistanceB = (A.x - B.x)² + (A.y - B.y)²
pseudoDistanceC = (A.x - C.x)² + (A.y - C.y)²
if (pseudoDistanceB < pseudoDistanceC)
{
    A is closest to B!
}
else
{
    A is closest to C!
}

It's not as efficient as the oct-tree thing, but it's easier to implement and does bump up the speed at least a little bit

\$\endgroup\$
1
  • 2
    \$\begingroup\$ That metric is also referred to as the squared euclidean distance. \$\endgroup\$
    – moooeeeep
    Commented Jul 31, 2014 at 15:42
11
\$\begingroup\$

You have to do spatial partitioning, in this case you make an efficient data structure (usually an octree). In this case each object is inside one or more spaces (cubes) And if you know in which spaces you are you can look up O(1) which spaces are your neighbors.

In this case the nearest object can be found by first iterating over all objects in your own space looking to which one is the closest there. If there is no-one there you can check your first neighbors, if no-one is there you can check their neighbors, etc...

This way you can easily find the nearest object without having to iterate through all objects in your world. As usual this speed gain does require a bit of bookkeeping, but it's really useful for all kinds of stuff so if you have a big world it's definitely worth implementing spatial partitioning and an octree.

As usual, see also the wikipedia article: http://en.wikipedia.org/wiki/Octree

\$\endgroup\$
1
  • 7
    \$\begingroup\$ @ultifinitus To add to this: If your game is 2D you can use QuadTrees instead of Octrees. \$\endgroup\$
    – TravisG
    Commented Jul 1, 2011 at 16:13
1
\$\begingroup\$

Maybe try organizing your spatial data in an RTree, which is kind of like a btree for stuff in space and allows queries like "nearest N neighbors" etc... http://en.wikipedia.org/wiki/Rtree

\$\endgroup\$
0
\$\begingroup\$

Here is my java implementation to get the closest one from a quadTree. It deals with the problem dlras2 is describing:

enter image description here

I think the operation is really efficient. It is based on the distance to a quad to avoid searching in quads further way then the current closest.

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

public T getClosest(float x, float y) {

    Closest closest = new Closest();
    getClosest(x, y, closest);

    return closest.item;
}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

protected void getClosest(float x, float y, Closest closestInfo) {


    if (hasQuads) {

        // we have no starting point yet
        // so get one
        if (closestInfo.item == null) {
            // check all 4 cause there could be a empty one
            for (int i = 0; i < 4; i++) {
                quads[i].getClosest(x, y, closestInfo);
                if (closestInfo.item != null) {
                    // now we have a starting point
                    getClosest(x, y, closestInfo);
                    return;
                }

            }
        }
        else {

            // we have a item set as closest
            // we should check if this quad is
            // closer then the current closest distance
            // let's start with the closest from index

            int closestIndex = getIndex(x, y);

            float d = quads[closestIndex].bounds.distToPointSQ(x, y);

            if (d < closestInfo.dist) {
                quads[closestIndex].getClosest(x, y, closestInfo);
            }

            // check the others
            for (int i = 0; i < 4; i++) {
                if (i == closestIndex) continue;

                d = quads[i].bounds.distToPointSQ(x, y);

                if (d < closestInfo.dist) {
                    quads[i].getClosest(x, y, closestInfo);
                }

            }

        }

    }
    else {

        for (int i = 0; i < items.size(); i++) {

            T item = items.get(i);

            float dist = distSQ(x, y, getXY.x(item), getXY.y(item));

            if (dist < closestInfo.dist) {
                closestInfo.dist = dist;
                closestInfo.item = item;
                closestInfo.tree = this;
            }

        }
    }

}

// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


class Closest {

    QuadTree<T> tree;
    T item;
    float dist = Float.MAX_VALUE;

}
\$\endgroup\$
2
  • \$\begingroup\$ p.s. I still think it's better to use a kd-tree or something, but this might help people. \$\endgroup\$
    – clankill3r
    Commented Jan 9, 2015 at 19:09
  • \$\begingroup\$ also look at this: bl.ocks.org/llb4ll/8709363 \$\endgroup\$
    – clankill3r
    Commented Jan 10, 2015 at 10:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .