All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ags.utils.dataStructures.trees.thirdGenKD.KdTree Maven / Gradle / Ivy

The newest version!
package ags.utils.dataStructures.trees.thirdGenKD;

import ags.utils.dataStructures.BinaryHeap;
import ags.utils.dataStructures.MaxHeap;
import ags.utils.dataStructures.MinHeap;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 *
 */
public class KdTree extends KdNode {
    public KdTree(int dimensions) {
        this(dimensions, 24);
    }

    public KdTree(int dimensions, int bucketCapacity) {
        super(dimensions, bucketCapacity);
    }

    public NearestNeighborIterator getNearestNeighborIterator(double[] searchPoint, int maxPointsReturned, DistanceFunction distanceFunction) {
        return new NearestNeighborIterator(this, searchPoint, maxPointsReturned, distanceFunction);
    }

    public MaxHeap findNearestNeighbors(double[] searchPoint, int maxPointsReturned, DistanceFunction distanceFunction) {
        BinaryHeap.Min> pendingPaths = new BinaryHeap.Min>();
        BinaryHeap.Max evaluatedPoints = new BinaryHeap.Max();
        int pointsRemaining = Math.min(maxPointsReturned, size());
        pendingPaths.offer(0, this);

        while (pendingPaths.size() > 0 && (evaluatedPoints.size() < pointsRemaining || (pendingPaths.getMinKey() < evaluatedPoints.getMaxKey()))) {
            nearestNeighborSearchStep(pendingPaths, evaluatedPoints, pointsRemaining, distanceFunction, searchPoint);
        }

        return evaluatedPoints;
    }

    public List findInBounds(double [] minBound, double [] maxBound) {
        if (minBound.length != dimensions || maxBound.length != dimensions) {
            throw new IllegalArgumentException();
        }
        for (int i = 0; i < minBound.length; i++) {
            if (minBound[i] > maxBound[i]) {
                throw new IllegalArgumentException();
            }
        }

        List results = new ArrayList();

        // Give up if there is no hope of overlap
        if (!this.isLeaf()) {
            for (int i = 0; i < dimensions; i++) {
                if (!overlaps(minBound[i], maxBound[i], this.minBound[i], this.maxBound[i])) {
                    return results;
                }
            }
        }

        List> queue = new LinkedList>();
        queue.add(this);

        while (!queue.isEmpty()) {
            KdNode node = queue.remove(0);

            // Add matching points
            for (int i = 0; i < node.size(); i++) {
                if (inBounds(minBound, maxBound, node.points[i])) {
                    results.add((T) node.data[i]);
                }
            }

            // Descend if necessary
            int d = node.splitDimension;
            if (node.left != null && overlaps(node.minBound[d], node.splitValue, minBound[d], maxBound[d])) {
                queue.add(node.left);
            }
            if (node.right != null && overlaps(node.splitValue, node.maxBound[d], minBound[d], maxBound[d])) {
                queue.add(node.right);
            }
        }

        return results;
    }

    private boolean inBounds(double [] minPoint, double [] maxPoints, double [] candidate) {
        for (int i = 0; i < candidate.length; i++) {
            if (candidate[i] < minPoint[i] || candidate[i] > maxPoints[i]) {
                return false;
            }
        }
        return true;
    }

    private boolean overlaps(double minA, double maxA, double minB, double maxB) {
        return (minA <= maxB) && (minB <= maxA);
    }

    @SuppressWarnings("unchecked")
    protected static  void nearestNeighborSearchStep (
            MinHeap> pendingPaths, MaxHeap evaluatedPoints, int desiredPoints,
            DistanceFunction distanceFunction, double[] searchPoint) {
        // If there are pending paths possibly closer than the nearest evaluated point, check it out
        KdNode cursor = pendingPaths.getMin();
        pendingPaths.removeMin();

        // Descend the tree, recording paths not taken
        while (!cursor.isLeaf()) {
            KdNode pathNotTaken;
            if (searchPoint[cursor.splitDimension] > cursor.splitValue) {
                pathNotTaken = cursor.left;
                cursor = cursor.right;
            }
            else {
                pathNotTaken = cursor.right;
                cursor = cursor.left;
            }
            double otherDistance = distanceFunction.distanceToRect(searchPoint, pathNotTaken.minBound, pathNotTaken.maxBound);
            // Only add a path if we either need more points or it's closer than furthest point on list so far
            if (evaluatedPoints.size() < desiredPoints || otherDistance <= evaluatedPoints.getMaxKey()) {
                pendingPaths.offer(otherDistance, pathNotTaken);
            }
        }

        if (cursor.singlePoint) {
            double nodeDistance = distanceFunction.distance(cursor.points[0], searchPoint);
            // Only add a point if either need more points or it's closer than furthest on list so far
            if (evaluatedPoints.size() < desiredPoints || nodeDistance <= evaluatedPoints.getMaxKey()) {
                for (int i = 0; i < cursor.size(); i++) {
                    T value = (T) cursor.data[i];

                    // If we don't need any more, replace max
                    if (evaluatedPoints.size() == desiredPoints) {
                        evaluatedPoints.replaceMax(nodeDistance, value);
                    } else {
                        evaluatedPoints.offer(nodeDistance, value);
                    }
                }
            }
        } else {
            // Add the points at the cursor
            for (int i = 0; i < cursor.size(); i++) {
                double[] point = cursor.points[i];
                T value = (T) cursor.data[i];
                double distance = distanceFunction.distance(point, searchPoint);
                // Only add a point if either need more points or it's closer than furthest on list so far
                if (evaluatedPoints.size() < desiredPoints) {
                    evaluatedPoints.offer(distance, value);
                } else if (distance < evaluatedPoints.getMaxKey()) {
                    evaluatedPoints.replaceMax(distance, value);
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy