gov.sandia.cognition.math.geometry.Quadtree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cognitive-foundry Show documentation
Show all versions of cognitive-foundry Show documentation
A single jar with all the Cognitive Foundry components.
/*
* 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 extends DataType> 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 extends DataType> 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 extends DataType> 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 extends DataType> 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;
}
}
}