//package whitebox.structures; // //import java.util.Arrays; //import java.util.HashSet; //import java.util.LinkedList; //import java.util.List; //import java.util.ListIterator; //import java.util.Set; // ///** // * Implementation of an arbitrary-dimension RTree. Based on R-Trees: A Dynamic // * Index Structure for Spatial Searching (Antonn Guttmann, 1984) // * // * This class is not thread-safe. // * // * Copyright 2010 Russ Weeks rweeks@newbrightidea.com Licensed under the GNU // * LGPL License details here: http://www.gnu.org/licenses/lgpl-3.0.txt // * // * @param <T> the type of entry to store in this RTree. // */ //public class RTree<T> { // // private final int maxEntries; // private final int minEntries; // private final int numDims; // private Node root; // private volatile int size; // // /** // * Creates a new RTree. // * // * @param maxEntries maximum number of entries per node // * @param minEntries minimum number of entries per node (except for the root // * node) // * @param numDims the number of dimensions of the RTree. // */ // public RTree(int maxEntries, int minEntries, int numDims) { // assert (minEntries <= (maxEntries / 2)); // this.numDims = numDims; // this.maxEntries = maxEntries; // this.minEntries = minEntries; // root = buildRoot(true); // } // // private Node buildRoot(boolean asLeaf) { // float[] initCoords = new float[numDims]; // float[] initDimensions = new float[numDims]; // for (int i = 0; i < this.numDims; i++) { // initCoords[i] = (float) Math.sqrt(Float.MAX_VALUE); // initDimensions[i] = -2.0f * (float) Math.sqrt(Float.MAX_VALUE); // } // return new Node(initCoords, initDimensions, asLeaf); // } // // /** // * Builds a new RTree using default parameters: maximum 50 entries per node // * minimum 2 entries per node 2 dimensions // */ // public RTree() { // this(50, 2, 2); // } // // /** // * @return the maximum number of entries per node // */ // public int getMaxEntries() { // return maxEntries; // } // // /** // * @return the minimum number of entries per node for all nodes except the // * root. // */ // public int getMinEntries() { // return minEntries; // } // // /** // * @return the number of dimensions of the tree // */ // public int getNumDims() { // return numDims; // } // // /** // * @return the number of items in this tree. // */ // public int size() { // return size; // } // // /** // * Searches the RTree for objects overlapping with the given rectangle. // * // * @param coords the corner of the rectangle that is the lower bound of // * every dimension (eg. the top-left corner) // * @param dimensions the dimensions of the rectangle. // * @return a list of objects whose rectangles overlap with the given // * rectangle. // */ // public List<T> search(float[] coords, float[] dimensions) { // assert (coords.length == numDims); // assert (dimensions.length == numDims); // LinkedList<T> results = new LinkedList<T>(); // search(coords, dimensions, root, results); // return results; // } // // private void search(float[] coords, float[] dimensions, Node n, // LinkedList<T> results) { // if (n.leaf) { // for (Node e : n.children) { // if (isOverlap(coords, dimensions, e.coords, e.dimensions)) { // results.add(((Entry) e).entry); // } // } // } else { // for (Node c : n.children) { // if (isOverlap(coords, dimensions, c.coords, c.dimensions)) { // search(coords, dimensions, c, results); // } // } // } // } // // /** // * Deletes the entry associated with the given rectangle from the RTree // * // * @param coords the corner of the rectangle that is the lower bound in // * every dimension // * @param dimensions the dimensions of the rectangle // * @param entry the entry to delete // * @return true iff the entry was deleted from the RTree. // */ // public boolean delete(float[] coords, float[] dimensions, T entry) { // assert (coords.length == numDims); // assert (dimensions.length == numDims); // Node l = findLeaf(root, coords, dimensions, entry); // assert (l.leaf); // ListIterator<Node> li = l.children.listIterator(); // T removed = null; // while (li.hasNext()) { // @SuppressWarnings("unchecked") // Entry e = (Entry) li.next(); // if (e.entry.equals(entry)) { // removed = e.entry; // li.remove(); // break; // } // } // if (removed != null) { // condenseTree(l); // size--; // } // if (size == 0) { // root = buildRoot(true); // } // return (removed != null); // } // // private Node findLeaf(Node n, float[] coords, float[] dimensions, T entry) { // if (n.leaf) { // for (Node c : n.children) { // if (((Entry) c).entry.equals(entry)) { // return n; // } // } // return null; // } else { // for (Node c : n.children) { // if (isOverlap(c.coords, c.dimensions, coords, dimensions)) { // Node result = findLeaf(c, coords, dimensions, entry); // if (result != null) { // return result; // } // } // } // return null; // } // } // // private void condenseTree(Node n) { // Set<Node> q = new HashSet<Node>(); // while (n != root) { // if (n.leaf && (n.children.size() < minEntries)) { // q.addAll(n.children); // n.parent.children.remove(n); // } else if (!n.leaf && (n.children.size() < minEntries)) { // // probably a more efficient way to do this... // LinkedList<Node> toVisit = new LinkedList<Node>(n.children); // while (!toVisit.isEmpty()) { // Node c = toVisit.pop(); // if (c.leaf) { // q.addAll(c.children); // } else { // toVisit.addAll(c.children); // } // } // n.parent.children.remove(n); // } else { // tighten(n); // } // n = n.parent; // } // for (Node ne : q) { // @SuppressWarnings("unchecked") // Entry e = (Entry) ne; // insert(e.coords, e.dimensions, e.entry); // } // } // // /** // * Inserts the given entry into the RTree, associated with the given // * rectangle. // * // * @param coords the corner of the rectangle that is the lower bound in // * every dimension // * @param dimensions the dimensions of the rectangle // * @param entry the entry to insert // */ // public void insert(float[] coords, float[] dimensions, T entry) { // assert (coords.length == numDims); // assert (dimensions.length == numDims); // Entry e = new Entry(coords, dimensions, entry); // Node l = chooseLeaf(root, e); // l.children.add(e); // size++; // e.parent = l; // if (l.children.size() > maxEntries) { // Node[] splits = splitNode(l); // adjustTree(splits[0], splits[1]); // } else { // adjustTree(l, null); // } // } // // /** // * Empties the RTree // */ // public void clear() { // root = buildRoot(true); // // let the GC take care of the rest. // } // // private void adjustTree(Node n, Node nn) { // if (n == root) { // if (nn != null) { // // build new root and add children. // root = buildRoot(false); // root.children.add(n); // n.parent = root; // root.children.add(nn); // nn.parent = root; // } // tighten(root); // return; // } // tighten(n); // if (nn != null) { // tighten(nn); // if (n.parent.children.size() > maxEntries) { // Node[] splits = splitNode(n.parent); // adjustTree(splits[0], splits[1]); // } // } else if (n.parent != null) { // adjustTree(n.parent, null); // } // } // // private Node[] splitNode(Node n) { // @SuppressWarnings("unchecked") // Node[] nn = new RTree.Node[]{n, new Node(n.coords, n.dimensions, n.leaf)}; // nn[1].parent = n.parent; // if (nn[1].parent != null) { // nn[1].parent.children.add(nn[1]); // } // LinkedList<Node> cc = new LinkedList<Node>(n.children); // n.children.clear(); // Node[] ss = pickSeeds(cc); // nn[0].children.add(ss[0]); // nn[1].children.add(ss[1]); // while (!cc.isEmpty()) { // if ((nn[0].children.size() >= minEntries) // && (nn[1].children.size() + cc.size() == minEntries)) { // nn[1].children.addAll(cc); // cc.clear(); // return nn; // } else if ((nn[1].children.size() >= minEntries) // && (nn[0].children.size() + cc.size() == minEntries)) { // nn[0].children.addAll(cc); // cc.clear(); // return nn; // } // Node c = cc.pop(); // Node preferred; // // Implementation of linear PickNext // float e0 = getRequiredExpansion(nn[0].coords, nn[0].dimensions, c); // float e1 = getRequiredExpansion(nn[1].coords, nn[1].dimensions, c); // if (e0 < e1) { // preferred = nn[0]; // } else if (e0 > e1) { // preferred = nn[1]; // } else { // float a0 = getArea(nn[0].dimensions); // float a1 = getArea(nn[1].dimensions); // if (a0 < a1) { // preferred = nn[0]; // } else if (e0 > a1) { // preferred = nn[1]; // } else { // if (nn[0].children.size() < nn[1].children.size()) { // preferred = nn[0]; // } else if (nn[0].children.size() > nn[1].children.size()) { // preferred = nn[1]; // } else { // preferred = nn[(int) Math.round(Math.random())]; // } // } // } // preferred.children.add(c); // } // tighten(nn[0]); // tighten(nn[1]); // return nn; // } // // // Implementation of LinearPickSeeds // private RTree<T>.Node[] pickSeeds(LinkedList<Node> nn) { // RTree<T>.Node[] bestPair = null; // float bestSep = 0.0f; // for (int i = 0; i < numDims; i++) { // float dimLb = Float.MAX_VALUE, dimMinUb = Float.MAX_VALUE; // float dimUb = -1.0f * Float.MAX_VALUE, dimMaxLb = -1.0f * Float.MAX_VALUE; // Node nMaxLb = null, nMinUb = null; // for (Node n : nn) { // if (n.coords[i] < dimLb) { // dimLb = n.coords[i]; // } // if (n.dimensions[i] + n.coords[i] > dimUb) { // dimUb = n.dimensions[i] + n.coords[i]; // } // if (n.coords[i] > dimMaxLb) { // dimMaxLb = n.coords[i]; // nMaxLb = n; // } // if (n.dimensions[i] + n.coords[i] < dimMinUb) { // dimMinUb = n.dimensions[i] + n.coords[i]; // nMinUb = n; // } // } // float sep = (nMaxLb == nMinUb) ? -1.0f // : Math.abs((dimMinUb - dimMaxLb) / (dimUb - dimLb)); // if (sep >= bestSep) { // bestPair = new RTree.Node[]{nMaxLb, nMinUb}; // bestSep = sep; // } // } // // In the degenerate case where all points are the same, the above // // algorithm does not find a best pair. Just pick the first 2 // // children. // if (bestPair == null) { // bestPair = new RTree.Node[]{nn.get(0), nn.get(1)}; // } // nn.remove(bestPair[0]); // nn.remove(bestPair[1]); // return bestPair; // } // // private void tighten(Node n) { // float[] minCoords = new float[n.coords.length]; // float[] maxDimensions = new float[n.dimensions.length]; // for (int i = 0; i < minCoords.length; i++) { // minCoords[i] = Float.MAX_VALUE; // maxDimensions[i] = 0.0f; // // for (Node c : n.children) { // // we may have bulk-added a bunch of children to a node (eg. in // // splitNode) // // so here we just enforce the child->parent relationship. // c.parent = n; // if (c.coords[i] < minCoords[i]) { // minCoords[i] = c.coords[i]; // } // if ((c.coords[i] + c.dimensions[i]) > maxDimensions[i]) { // maxDimensions[i] = (c.coords[i] + c.dimensions[i]); // } // } // } // System.arraycopy(minCoords, 0, n.coords, 0, minCoords.length); // System.arraycopy(maxDimensions, 0, n.dimensions, 0, maxDimensions.length); // } // // private RTree<T>.Node chooseLeaf(RTree<T>.Node n, RTree<T>.Entry e) { // if (n.leaf) { // return n; // } // float minInc = Float.MAX_VALUE; // Node next = null; // for (RTree<T>.Node c : n.children) { // float inc = getRequiredExpansion(c.coords, c.dimensions, e); // if (inc < minInc) { // minInc = inc; // next = c; // } else if (inc == minInc) { // float curArea = 1.0f; // float thisArea = 1.0f; // for (int i = 0; i < c.dimensions.length; i++) { // curArea *= next.dimensions[i]; // thisArea *= c.dimensions[i]; // } // if (thisArea < curArea) { // next = c; // } // } // } // return chooseLeaf(next, e); // } // // /** // * Returns the increase in area necessary for the given rectangle to cover // * the given entry. // */ // private float getRequiredExpansion(float[] coords, float[] dimensions, Node e) { // float area = getArea(dimensions); // float[] deltas = new float[dimensions.length]; // for (int i = 0; i < deltas.length; i++) { // if (coords[i] + dimensions[i] < e.coords[i] + e.dimensions[i]) { // deltas[i] = e.coords[i] + e.dimensions[i] - coords[i] - dimensions[i]; // } else if (coords[i] + dimensions[i] > e.coords[i] + e.dimensions[i]) { // deltas[i] = coords[i] - e.coords[i]; // } // } // float expanded = 1.0f; // for (int i = 0; i < dimensions.length; i++) { // area *= dimensions[i] + deltas[i]; // } // return (expanded - area); // } // // private float getArea(float[] dimensions) { // float area = 1.0f; // for (int i = 0; i < dimensions.length; i++) { // area *= dimensions[i]; // } // return area; // } // // private boolean isOverlap(float[] scoords, float[] sdimensions, // float[] coords, float[] dimensions) { // for (int i = 0; i < scoords.length; i++) { // boolean overlapInThisDimension = false; // if (scoords[i] == coords[i]) { // overlapInThisDimension = true; // } else if (scoords[i] < coords[i]) { // if (scoords[i] + sdimensions[i] >= coords[i]) { // overlapInThisDimension = true; // } // } else if (scoords[i] > coords[i]) { // if (coords[i] + dimensions[i] >= scoords[i]) { // overlapInThisDimension = true; // } // } // if (!overlapInThisDimension) { // return false; // } // } // return true; // } // // private class Node { // // final float[] coords; // final float[] dimensions; // final LinkedList<Node> children; // final boolean leaf; // Node parent; // // private Node(float[] coords, float[] dimensions, boolean leaf) { // this.coords = new float[coords.length]; // this.dimensions = new float[dimensions.length]; // System.arraycopy(coords, 0, this.coords, 0, coords.length); // System.arraycopy(dimensions, 0, this.dimensions, 0, dimensions.length); // this.leaf = leaf; // children = new LinkedList<Node>(); // } // } // // private class Entry extends Node { // // final T entry; // // public Entry(float[] coords, float[] dimensions, T entry) { // // an entry isn't actually a leaf (its parent is a leaf) // // but all the algorithms should stop at the first leaf they encounter, // // so this little hack shouldn't be a problem. // super(coords, dimensions, true); // this.entry = entry; // } // // public String toString() { // return "Entry: " + entry; // } // } // // The methods below this point can be used to create an HTML rendering // // of the RTree. Maybe useful for debugging? // private static final int elemWidth = 150; // private static final int elemHeight = 120; // // public String visualize() { // int ubDepth = (int) Math.ceil(Math.log(size) / Math.log(minEntries)) * elemHeight; // int ubWidth = size * elemWidth; // java.io.StringWriter sw = new java.io.StringWriter(); // java.io.PrintWriter pw = new java.io.PrintWriter(sw); // pw.println("<html><head></head><body>"); // visualize(root, pw, 0, 0, ubWidth, ubDepth); // pw.println("</body>"); // pw.flush(); // return sw.toString(); // } // // private void visualize(Node n, java.io.PrintWriter pw, int x0, int y0, int w, int h) { // pw.printf("<div style=\"position:absolute; left: %d; top: %d; width: %d; height: %d; border: 1px dashed\">\n", // x0, y0, w, h); // pw.println("<pre>"); // pw.println("Node: " + n.toString() + " (root==" + (n == root) + ") \n"); // pw.println("Coords: " + Arrays.toString(n.coords) + "\n"); // pw.println("Dimensions: " + Arrays.toString(n.dimensions) + "\n"); // pw.println("# Children: " + ((n.children == null) ? 0 : n.children.size()) + "\n"); // pw.println("isLeaf: " + n.leaf + "\n"); // pw.println("</pre>"); // int numChildren = (n.children == null) ? 0 : n.children.size(); // for (int i = 0; i < numChildren; i++) { // visualize(n.children.get(i), pw, (int) (x0 + (i * w / (float) numChildren)), // y0 + elemHeight, (int) (w / (float) numChildren), h - elemHeight); // } // pw.println("</div>"); // } //}