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

gov.sandia.cognition.math.geometry.Quadtree Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * File:                Quadtree.java
 * Authors:             Justin Basilico
 * Company:             Sandia National Laboratories
 * Project:             Cognitive Foundry
 * 
 * Copyright May 16, 2008, Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive 
 * license for use of this work by or on behalf of the U.S. Government. Export 
 * of this program may require a license from the United States Government. 
 * See CopyrightHistory.txt for complete details.
 * 
 */

package gov.sandia.cognition.math.geometry;

import gov.sandia.cognition.annotation.CodeReview;
import gov.sandia.cognition.collection.DefaultMultiCollection;
import gov.sandia.cognition.math.matrix.Vector;
import gov.sandia.cognition.math.matrix.Vectorizable;
import gov.sandia.cognition.math.matrix.mtj.Vector2;
import gov.sandia.cognition.util.AbstractCloneableSerializable;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * Implements the quadtree region-partitioning algorithm and data structure. The 
 * quadtree works on two-dimensional data by building a tree over the data. 
 * Each node in the tree represents a square region of the data. Each interior 
 * node contains four children, corresponding to the four equal-sized quadrants 
 * of the node (hence the name quadtree). All of the data items are contained 
 * at the leaves of the tree. The algorithm maintains a threshold of the 
 * maximum number of items allowed in a leaf node. If a node exceeds that 
 * limit, then it is split into its four quadrants.
 * 
 * @param    The type of data that is to be stored in the tree. It
 *      must be able to be converted to a two-dimensional vector via the
 *      {@code Vectorizable} interface.
 * @author  Justin Basilico
 * @since   2.1
 */
@CodeReview(
    reviewer="Kevin R. Dixon",
    date="2008-12-02",
    changesNeeded=false,
    comments={
        "Made Quadtree and Node extend AbstractCloneableSerializable",
        "Otherwise, class looks great!"
    }
)
public class Quadtree
    extends AbstractCloneableSerializable
{
    /**
     * This is the default minimum number of items allowed in a leaf node,
     * {@value}.
     */
    public static final int DEFAULT_SPLIT_THRESHOLD = 10;
    
    /** The minimum number of items allowed in a leaf node. If there are more
     *  than this, then a node must be split. This number must be greater than
     *  zero. */
    protected int splitThreshold;
    
    /** All of the items in the tree. It should never be null. */
    protected LinkedList items;
    
    /** The initial bounds for the tree. This may be null if they are not 
     *  specified. */
    protected Rectangle2D.Double initalBounds;
    
    /** The root node of the tree. It should never be null. */
    protected Node root;
    
    /**
     * Creates a new, empty {@code Quadtree}.
     */
    public Quadtree()
    {
        this(DEFAULT_SPLIT_THRESHOLD);
    }
    
    /**
     * Creates a new, empty {@code Quadtree} with the given split threshold.
     * 
     * @param   splitThreshold
     *      The maximum number of items allowed in a tree leaf node before
     *      it is split. Must be positive.
     */
    public Quadtree(
        final int splitThreshold)
    {
        this(splitThreshold, (Rectangle2D.Double) null);
    }
    
    /**
     * Creates a new, empty {@code Quadtree} with the given initial bounds.
     * 
     * @param   initialBounds The initial bounds for the quadtree.
     */
    public Quadtree(
        final Rectangle2D.Double initialBounds)
    {
        this(DEFAULT_SPLIT_THRESHOLD, initialBounds);
    }
    
    /**
     * Creates a new, empty {@code Quadtree} with the given split threshold.
     * 
     * @param   splitThreshold
     *      The maximum number of items allowed in a tree leaf node before
     *      it is split. Must be positive.
     * @param   initialBounds 
     *      The initial bounds for the quadtree.
     */
    public Quadtree(
        final int splitThreshold,
        final Rectangle2D.Double initialBounds)
    {
        super();
        
        this.items = new LinkedList();
        
        this.root = new Node(null, null);
        
        this.setSplitThreshold(splitThreshold);
    }
    
    /**
     * Creates a new {@code Quadtree}, populating it with the given items.
     * 
     * @param  items The initial items to populate the tree with.
     */
    public Quadtree(
        final Collection items)
    {
        this(DEFAULT_SPLIT_THRESHOLD, items);
    }
    
    /**
     * Creates a new {@code Quadtree}, populating it with the given items.
     * 
     * @param   splitThreshold
     *      The maximum number of items allowed in a tree leaf node before
     *      it is split. Must be positive.
     * @param  items The initial items to populate the tree with.
     */
    public Quadtree(
        final int splitThreshold,
        final Collection items)
    {
        this(splitThreshold);
        
        this.items.addAll(items);
        this.rebuild();
    }
    
    /**
     * Adds an item to the tree. If the item is outside the current bounds of
     * the tree, it will rebuild the tree to fit the new item.
     * 
     * @param   item The item to add to the tree.
     */
    public void add(
        final DataType item)
    {
        final Vector2 point = this.convertTo2D(item);
        
        // Add the item to the total list of items.
        this.items.add(item);
        
        if (!this.root.boundsContain(point))
        {
            // Point is not in bounds, so the entire tree needs to be rebuilt
            // to handle the new bounds.
            this.rebuild();
        }
        else
        {
            // Find the closest matching node.
            final Node node = this.find(point);
            
            // Add the point to the node.
            node.getLocalItems().add(item);
            
            // Split the node if necessary.
            if (this.shouldSplit(node))
            {
                this.split(node);
            }
        }
    }
    
    /**
     * Adds all the items to the 
     * 
     * @param   newItems The new items to add to the tree.
     */
    public void addAll(
        final Collection newItems)
    {
        // See if all the items are contained in the current bounding box.
        boolean containsAll = true;
        for (DataType item : newItems)
        {
            // This item is contained.
            if (!this.root.isInBounds(item))
            {
                containsAll = false;
                break;
            }
        }
        
        if (!containsAll)
        {
            // The current tree does not contain all the items. Add them to
            // the list of items in the tree and rebuild it all.
            this.items.addAll(newItems);
            this.rebuild();
        }
        else
        {
            // They are all contained in the bounds of the current tree, so add
            // them one at a time.
            for (DataType item : newItems)
            {
                this.add(item);
            }
        }
    }
    
    /**
     * Rebuilds the entire quadtree. It destroys the current root node and then
     * repopulates the tree from the current list of items for the tree.
     */
    protected void rebuild()
    {
        this.root = null;
        
        // Compute the quadtree bounds.
        Rectangle2D.Double bounds = this.computeBounds(this.items);
        
        // Create the root node.
        this.root = new Node(null, bounds);
        this.root.getLocalItems().addAll(this.items);
        
        // Start the splitting of the root node, if required.
        if (this.shouldSplit(this.root))
        {
            this.split(this.root);
        }
    }
    
    /**
     * Computes the bounding rectangle of a given collection of points. This
     * takes into account the initial bounds of the quadtree, fi they are specified.
     * 
     * @param   items The items to compute the bounds for.
     * @return  The minimum bounding rectangle for the given items.
     */
    protected Rectangle2D.Double computeBounds(
        final Collection items)
    {
        // Start with the initial bounds.
        Rectangle2D.Double bounds = this.initalBounds;
        
        // Go through all the items and compute their bounds.
        for (DataType item : this.items)
        {
            final Vector2 point = this.convertTo2D(item);
            
            if (bounds == null)
            {
                // This is the first item, so initialize the bounds.
                bounds = new Rectangle2D.Double(point.getX(), point.getY(),
                    0.0, 0.0);
            }
            else
            {
                bounds.add(point.getX(), point.getY());
            }
        }
        
        if (bounds != null)
        {
            // Make the bounding box a bounding square by using the larger of
            // the width and the height.
            final double size = Math.max(bounds.getWidth(), bounds.getHeight());
            bounds.width = size;
            bounds.height = size;
        }
        
        return bounds;
    }
    
    /**
     * Determines if a given node should be split. This is done according to
     * the split threshold.
     * 
     * @param   node The node to check to see if it should be split.
     * @return  True if the node should be split and false otherwise.
     */
    protected boolean shouldSplit(
        final Node node)
    {
        return node.getLocalCount() > this.splitThreshold;
    }
    
    /** 
     * Splits the given node. This is the real meat of the algorithm.
     * 
     * @param   node The node to split into its two children.
     */
    protected void split(
        final Node node)
    {
        if (node == null)
        {
            return;
        }
        else if (!node.isLeaf())
        {
            throw new IllegalArgumentException(
                "Only leaf nodes can be split");
        }
        else if (node.areLocalItemsSame())
        {
            // All the local items are the same, so can't split this node.
            return;
        }
        
        // Figure out the splits.
        final Rectangle2D.Double bounds = node.getBounds();
        final double minX = bounds.getMinX();
        final double minY = bounds.getMinY();
        final double midX = bounds.getCenterX();
        final double midY = bounds.getCenterY();
        final double splitWidth = midX - minX;
        final double splitHeight = midY - minY;
        
        // Create the children.
        node.setLowerLeft(new Node(node, 
            new Rectangle2D.Double(minX, minY, splitWidth, splitHeight)));
        node.setLowerRight(new Node(node, 
            new Rectangle2D.Double(midX, minY, splitWidth, splitHeight)));
        node.setUpperLeft(new Node(node, 
            new Rectangle2D.Double(minX, midY, splitWidth, splitHeight)));
        node.setUpperRight(new Node(node, 
            new Rectangle2D.Double(midX, midY, splitWidth, splitHeight)));
        
        // Build the list of children.
        final ArrayList children = new ArrayList(4);
        children.add(node.getLowerLeft());
        children.add(node.getLowerRight());
        children.add(node.getUpperLeft());
        children.add(node.getUpperRight());
        node.setChildren(children);
        
        // Go through all the items and add to the children.
        for (DataType item : node.getLocalItems())
        {
            final Node child = node.findChild(item);
            child.localItems.add(item);
        }
        
        // Apply the changes to the node, transforming it from a leaf node to
        // an interior node.
        node.localItems.clear();

        // Now see if the children should be split.
        for (Node child : children)
        {
            if (this.shouldSplit(child))
            {
                this.split(child);
            }
        }
    }
    
    /**
     * Converts the given item into a two-dimensional vector. It throws an
     * illegal argument exception
     * 
     * @param   item The item to convert to a two-dimensional vector.
     * @return  The two-dimenaional vector version of the item.
     */
    public Vector2 convertTo2D(
        final DataType item)
    {
        final Vector vector = item.convertToVector();
        if (vector.getDimensionality() != 2)
        {
            throw new IllegalArgumentException(
                "Quadtree only accepts two-dimensional data");
        }
        return new Vector2(vector);
    }
    
    /**
     * Locates the node in the tree that has the smallest bounding box that
     * contains the item.
     * 
     * @param   item The item to find the node for.
     * @return  The node with the smallest bounding box that contains the item.
     */
    public Node find(
        final DataType item)
    {
        return this.find(this.convertTo2D(item));
    }
    
    /**
     * Locates the node in the tree that has the smallest bounding box that
     * contains the point.
     * 
     * @param   point The point to find the node for.
     * @return  The node with the smallest bounding box that contains the point.
     */
    public Node find(
        final Vector2 point)
    {
        return this.find(point.getX(), point.getY());
    }
    
    /**
     * Locates the node in the tree that has the smallest bounding box that
     * contains the point.
     * 
     * @param   x The x-coordinate of the point.
     * @param   y The y-coordinate of the point.
     * @return  The node with the smallest bounding box that contains the point.
     */
    public Node find(
        final double x,
        final double y)
    {
        if (!this.boundsContain(x, y))
        {
            // Not in the bounds of the tree.
            return null;
        }
        
        // Start at the root node and keep finding the proper child until
        // we hit a leaf.
        Node node = this.root;
        while (node != null && !node.isLeaf())
        {
            node = node.findChild(x, y);
        }
        
        return node;
    }
    
    /**
     * Finds all of the items in the quadtree that are contained in the given
     * rectangle.
     * 
     * @param   rectangle The rectangle to search for.
     * @return  The items in the quad tree that fit in the given rectangle.
     */
    public LinkedList findItems(
        final Rectangle2D rectangle)
    {
        // Start searching at the root.
        LinkedList result = new LinkedList();
        this.root.findItems(rectangle, result);
        return result;
    }
    
    /**
     * Finds the list of nodes that overlap with the given rectangle, chosing 
     * the highest-level nodes in the tree that are contained in the rectangle.
     * 
     * @param   rectangle The rectangle to search for.
     * @return  The list of the highest-level nodes that are contained in the
     *      given rectangle plus the leaves that intersect the rectangle.
     */
    public LinkedList findNodes(
        final Rectangle2D rectangle)
    {
        final LinkedList result = new LinkedList();
        this.findNodes(rectangle, this.root, result);
        return result;
    }
    
    /**
     * Internal find nodes implementation that accumulates the result in a
     * given list.
     * 
     * @param   rectangle The rectangle to search for.
     * @param   node The node to search in.
     * @param   result The result to accumulate the results.
     */
    private void findNodes(
        final Rectangle2D rectangle,
        final Node node,
        final LinkedList result)
    {
        if (node.boundsOverlap(rectangle))
        {
            if (node.isLeaf() || rectangle.contains(node.bounds))
            {
                // This is a leaf node or the rectangle contains this entire
                // node, so add it to the result.
                result.add(node);
            }
            else
            {
                // The entire node isn't contained, but some part overlaps, so
                // go through the children and search them.
                for (Node child : node.children)
                {
                    findNodes(rectangle, child, result);
                }
            }
        }
    }
    
    /**
     * Determines if the given point is within the bounds of the quadtree.
     * 
     * @param   item The point to determine if it is the bounds.
     * @return  True if the given point is in the bounds of the quadtree; 
     *      otherwise, false.
     */
    public boolean boundsContain(
        final DataType item)
    {
        return this.boundsContain(this.convertTo2D(item));
    }
    
    /**
     * Determines if the given point is within the bounds of the quadtree.
     * 
     * @param   point The point to determine if it is the bounds.
     * @return  True if the given point is in the bounds of the quadtree; 
     *      otherwise, false.
     */
    public boolean boundsContain(
        final Vector2 point)
    {
        return this.boundsContain(point.getX(), point.getY());
    }
    
    
    /**
     * Determines if the given point is within the bounds of the quadtree.
     * 
     * @param   x The x-coordinate of the point.
     * @param   y The y-coordinate of the point.
     * @return  True if the given point is in the bounds of the quadtree; 
     *      otherwise, false.
     */
    public boolean boundsContain(
        final double x,
        final double y)
    {
        return this.root.boundsContain(x, y);
    }
    
    /**
     * Gets the split threshold for the tree. This is the maximum number of
     * items that are allowed in a leaf node.
     * 
     * @return  The split threshold for a node in the tree.
     */
    public int getSplitThreshold()
    {
        return this.splitThreshold;
    }
    
    /**
     * Sets the split threshold for the node. If this changes threshold, then
     * the tree is rebuilt.
     * 
     * @param   splitThreshold The new split threshold. Must be positive.
     */
    public void setSplitThreshold(
        final int splitThreshold)
    {
        if (splitThreshold <= 0)
        {
            throw new IllegalArgumentException(
                "splitThreshold must be positive.");
        }
        else if (splitThreshold == this.splitThreshold)
        {
            // Nothing actually changed.
            return;
        } 
        
        this.splitThreshold = splitThreshold;
        
        // The split threshold has changed, so we need to rebuild the entire
        // tree.
        this.rebuild();
    }
    
    /**
     * Gets the root node of the quadtree.
     * 
     * @return   The root node of the quadtree.
     */
    public Node getRoot()
    {
        return this.root;
    }
    
    /**
     * Represents a node in the quadtree.
     */
    public class Node
        extends AbstractCloneableSerializable
    {
        /** The parent of this node in the tree. Null only for the root node. 
         */
        protected Node parent;
        
        /** The two-dimensional bounds for this node. This is only null if it
         *  is the root node and has no elements and no default bounds. */
        protected Rectangle2D.Double bounds;
        
        /** The depth of this node in the tree. */
        protected int depth;
        
        /** The local items stored at this node. */
        protected LinkedList localItems;
        
        /** The child for the lower-right quadrant of this node. */
        protected Node lowerRight;
        
        /** The child for the lower-left quadrant of this node. */
        protected Node lowerLeft;
        
        /** The child for the upper-left quadrant of this node. */
        protected Node upperLeft;
        
        /** The child for the upper-right quadrant of this node. */
        protected Node upperRight;
        
        /** The list of children for this node. Null to indicate that it has
         *  no children and thus is a leaf node. */
        protected ArrayList children;
        
        /**
         * Creates a new {@code Node} with the given parent and region bounds.
         * 
         * @param   parent
         *      The parent node. Null for the root node.
         * @param bounds
         *      The bounding rectangle for the region the node represents.
         */
        public Node(
            final Node parent,
            final Rectangle2D.Double bounds)
        {
            this.setParent(parent);
            this.setBounds(bounds);
            this.setDepth(parent == null ? 0 : parent.getDepth() + 1);
            this.setLocalItems(new LinkedList());
            this.setUpperLeft(null);
            this.setUpperRight(null);
            this.setLowerLeft(null);
            this.setLowerRight(null);
            this.setChildren(null);
        }
        
        /**
         * Returns true if the given point is within the bounds of this node.
         * 
         * @param   item The point.
         * @return  True if-and-only-if the point is in the bounds of this node.
         */
        public boolean isInBounds(
            final DataType item)
        {
            return boundsContain(convertTo2D(item));
        }
        
        /**
         * Returns true if the given point is within the bounds of this node.
         * 
         * @param   point The point.
         * @return  True if-and-only-if the point is in the bounds of this node.
         */
        public boolean boundsContain(
            final Vector2 point)
        {
            return this.boundsContain(point.getX(), point.getY());
        }
        
        /**
         * Returns true if the given point is within the bounds of this node.
         * 
         * @param   point The point.
         * @return  True if-and-only-if the point is in the bounds of this node.
         */
        public boolean boundsContain(
            final Point2D point)
        {
            return this.boundsContain(point.getX(), point.getY());
        }
        
        /**
         * Returns true if the given point is within the bounds of this node.
         * 
         * @param   x The x-coordinate of the point.
         * @param   y The y-coordinate of the point.
         * @return  True if-and-only-if the point is in the bounds of this node.
         */
        public boolean boundsContain(
            final double x,
            final double y)
        {
            // Note: This does not call the method on the Rectangle2D since it
            // does not allow things at the bounds to return true for being
            // contained in the rectangle.
            return this.bounds != null
                && x >= this.bounds.getMinX() && y >= this.bounds.getMinY() 
                && x <= this.bounds.getMaxX() && y <= this.bounds.getMaxY();
        }
        
        /**
         * Returns true if the given rectangle is completely within the bounds 
         * of this node.
         * 
         * @param   rectangle The rectangle to test.
         * @return  True if-and-only-if the given rectangle is within the bounds
         *      of this rectangle.
         */
        public boolean boundsContain(
            final Rectangle2D rectangle)
        {
            // Note: This does not call the method on the Rectangle2D since it
            // does not allow things at the bounds to return true for being
            // contained in the rectangle.
            return this.bounds != null
                && rectangle.getMinX() >= this.bounds.getMinX()
                && rectangle.getMinY() >= this.bounds.getMinY()
                && rectangle.getMaxX() <= this.bounds.getMaxX()
                && rectangle.getMaxY() <= this.bounds.getMaxY();
        }
        
        /**
         * Returns true if the given rectangle intersects the bounds for this
         * node.
         * 
         * @param   rectangle The rectangle to test.
         * @return  True if-and-only-if the given rectangle intersects with
         *      the bounds of this node.
         */
        public boolean boundsOverlap(
            final Rectangle2D rectangle)
        {
            return this.bounds.intersects(rectangle);
        }
        
        /**
         * Finds the child corresponding to the given point. Note that if the
         * point is outide the bounds of the node, it will still return a child
         * node, because it just compares to the splitting planes in the node
         * for efficiency.
         * 
         * @param   item The item to query.
         * @return  The node that most closely matches the given point.
         */
        public Node findChild(
            final DataType item)
        {
            return this.findChild(convertTo2D(item));
        }
        
        /**
         * Finds the child corresponding to the given point. Note that if the
         * point is outide the bounds of the node, it will still return a child
         * node, because it just compares to the splitting planes in the node
         * for efficiency.
         * 
         * @param   point The point to query.
         * @return  The node that most closely matches the given point.
         */
        public Node findChild(
            final Vector2 point)
        {
            return this.findChild(point.getX(), point.getY());
        }
            
        /**
         * Finds the child corresponding to the given point. Note that if the
         * point is outide the bounds of the node, it will still return a child
         * node, because it just compares to the splitting planes in the node
         * for efficiency.
         * 
         * @param   x The x-coordinate to query.
         * @param   y The y-coordinate to query.
         * @return  The node that most closely matches the given point.
         */
        public Node findChild(
            final double x,
            final double y)
        {
            if (this.isLeaf())
            {
                // The node has no children.
                return null;
            }
            else if (x <= this.bounds.getCenterX())
            {
                if (y <= this.bounds.getCenterY())
                {
                    return this.lowerLeft;
                }
                else
                {
                    return this.upperLeft;
                }
            }
            else
            {
                if (y <= this.bounds.getCenterY())
                {
                    return this.lowerRight;
                }
                else
                {
                    return this.upperRight;
                }
            }
        }
        
        /**
         * Finds all of the items that fall within the region defined by this
         * node (and its children) and adds it to the given list.
         * 
         * @param   rectangle The rectangle to search in.
         * @param   result The result list to put the items in.
         */
        public void findItems(
            final Rectangle2D rectangle,
            final LinkedList result)
        {
            // First see if the rectangle overlaps this node.
            if (this.boundsOverlap(rectangle))
            {
                if (rectangle.contains(this.bounds))
                {
                    // If the bounds are contained in the rectangle, add
                    // all the items in this node.
                    result.addAll(this.getItems());
                }
                else if (this.isLeaf())
                {
                    // This is a leaf node so test all of the items in the node
                    // to see if they fall in the rectangle.
                    for (DataType item : this.localItems)
                    {
                        final Vector2 point = convertTo2D(item);
                        if (rectangle.contains(point.getX(), point.getY()))
                        {
                            result.add(item);
                        }
                    }
                }
                else
                {
                    // Go through the child nodes.
                    for (Node child : this.children)
                    {
                        child.findItems(rectangle, result);
                    }
                }
            }
            // else - The bounds of this node do not overlap.
        }
        
        /**
         * Returns true if this is a leaf and all the local items are the same.
         * 
         * @return  True if the local items are the same.
         */
        public boolean areLocalItemsSame()
        {
            if (!this.isLeaf())
            {
                // No local items.
                return false;
            }
            else if (this.getLocalCount() <= 1)
            {
                // Only one local items, so they're all the same.
                return true;
            }
            
            // Get the first and then check to see if the rest are equal.
            final Vector2 first = convertTo2D(this.localItems.getFirst());
            for (DataType item : this.localItems)
            {
                final Vector2 vector = convertTo2D(item);
                if (!first.equals(vector))
                {
                    // Found one that is not equal.
                    return false;
                }
            }
            
            // They are all equal.
            return true;
        }
        
        
        /**
         * Gets the collection of items contained in this node and all subnodes.
         * 
         * @return  The collection of items contained in this node and all
         *      subnodes.
         */
        public Collection getItems()
        {
            if (this.isLeaf())
            {
                return this.localItems;
            }
            else
            {
                // See if there is a more efficient way to do this. (jdbasil)
                // I couldn't think of one, so I removed the task.
                return new DefaultMultiCollection(
                    new DefaultMultiCollection(
                        this.lowerLeft.getItems(),
                        this.lowerRight.getItems()),
                    new DefaultMultiCollection(
                        this.upperLeft.getItems(),
                        this.upperRight.getItems()));
            }
        }
        
        /**
         * Gets the children of this node.
         * 
         * @return  The children of this node.
         */
        public List getChildren()
        {
            if (this.children == null)
            {
                return Collections.emptyList();
            }
            else
            {
                return this.children;
            }
        }
        
        /**
         * Returns true if this is a leaf node and has no items in it.
         * 
         * @return  True if this is a leaf node and has no items in it.
         */
        public boolean isEmpty()
        {
            return this.isLeaf() && this.getLocalCount() <= 0;
        }
        
        /**
         * Returns true if this node is a leaf node, which means it has no
         * children.
         * 
         * @return  True if-and-only-if this is a leaf node.
         */
        public boolean isLeaf()
        {
            return this.children == null;
        }
        
        /**
         * Gets the number of items that are locally contained at the node. 
         * This does not count items at child nodes. For non-leaf nodes, this
         * should be zero.
         * 
         * @return  The number of items locally at the node.
         */
        public int getLocalCount()
        {
            return this.getLocalItems().size();
        }
        
        /**
         * The depth in the tree that this node exists at. The root starts at
         * depth 0.
         * 
         * @return  The depth in the tree of this node.
         */
        public int getDepth()
        {
            return this.depth;
        }

        /**
         * Sets the depth in the tree of this node.
         * 
         *  @param  depth   The new depth in the tree for the node.
         */
        protected void setDepth(
            final int depth)
        {
            this.depth = depth;
        }
        
        /**
         * Gets the parent node of this node. This is only null if it is a root
         * node.
         * 
         * @return The parent node of this node.
         */
        public Node getParent()
        {
            return this.parent;
        }

        /**
         * Sets the parent node of this node. It should only be null if this is
         * the root node.
         * 
         * @param   parent The parent node of this node.
         */
        protected void setParent(
            final Node parent)
        {
            this.parent = parent;
        }
        
        /**
         * Gets the bounding box of the region represented by this node.
         * 
         * @return  The bounding box of the region represented by this node.
         */
        public Rectangle2D.Double getBounds()
        {
            return this.bounds;
        }

        
        /**
         * Sets the bounding box of the region represented by this node.
         * 
         * @param bounds 
         *      The bounding box of the region represented by this node.
         */
        protected void setBounds(
            final Rectangle2D.Double bounds)
        {
            this.bounds = bounds;
        }
        
        /**
         * Gets the list of items stored locally at the node in the tree.
         * 
         * @return  The local items stored at the node.
         */
        public LinkedList getLocalItems()
        {
            return this.localItems;
        }
        
        /**
         * Gets the list of items stored locally at the node in the tree.
         * 
         * @param   localItems The local items stored at the node.
         */
        protected void setLocalItems(
            final LinkedList localItems)
        {
            this.localItems = localItems;
        }

        /**
         * Gets the child representing lower-left quadrant of the node, when
         * treating the 2D coordinate grid in a mathematical representation
         * (positive x values go left and positive y values go up).
         * 
         * @return  The child representing the lower-left quadrant of the node.
         */
        public Node getLowerLeft()
        {
            return this.lowerLeft;
        }
        
        /**
         * Sets the lower-left child.
         * 
         * @param   lowerLeft The lower-left child.
         */
        protected void setLowerLeft(
            final Node lowerLeft)
        {
            this.lowerLeft = lowerLeft;
        }

        /**
         * Gets the child representing lower-right quadrant of the node, when
         * treating the 2D coordinate grid in a mathematical representation
         * (positive x values go left and positive y values go up).
         * 
         * @return  The child representing the lower-right quadrant of the node.
         */
        public Node getLowerRight()
        {
            return this.lowerRight;
        }
        
        /**
         * Sets the lower-right child.
         * 
         * @param   lowerRight The lower-right child.
         */
        protected void setLowerRight(
            final Node lowerRight)
        {
            this.lowerRight = lowerRight;
        }
        
        /**
         * Gets the child representing upper-left quadrant of the node, when
         * treating the 2D coordinate grid in a mathematical representation
         * (positive x values go left and positive y values go up).
         * 
         * @return  The child representing the upper-left quadrant of the node.
         */
        public Node getUpperLeft()
        {
            return this.upperLeft;
        }

        /**
         * Sets the upper-left child.
         * 
         * @param   upperLeft The upper-left child.
         */
        protected void setUpperLeft(
            final Node upperLeft)
        {
            this.upperLeft = upperLeft;
        }

        /**
         * Gets the child representing upper-right quadrant of the node, when
         * treating the 2D coordinate grid in a mathematical representation
         * (positive x values go left and positive y values go up).
         * 
         * @return  The child representing the upper-right quadrant of the node.
         */
        public Node getUpperRight()
        {
            return this.upperRight;
        }

        /**
         * Sets the upper-right child.
         * 
         * @param   upperRight The upper-right child.
         */
        protected void setUpperRight(
            final Node upperRight)
        {
            this.upperRight = upperRight;
        }

        /**
         * Sets the list of child nodes of this node. It should only be the
         * four defined children that have pointers.
         * 
         * @param   children The list of child nodes of this node.
         */
        protected void setChildren(
            final ArrayList children)
        {
            this.children = children;
        }

        
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy