com.sun.electric.database.topology.RTNode Maven / Gradle / Ivy
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: RTNode.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.database.topology;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.tool.Job;
import com.sun.electric.util.math.AbstractFixpRectangle;
import com.sun.electric.util.math.FixpCoord;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
/**
* The RTNode class implements R-Trees.
* R-trees come from this paper: Guttman, Antonin, "R-Trees: A Dynamic Index Structure for Spatial Searching",
* ACM SIGMOD, 14:2, 47-57, June 1984.
*
* R-trees are height-balanced trees in which all leaves are at the same depth and contain RTBounds objects
* (generally Geometric objects NodeInst and ArcInst). Entries higher in the tree store boundary information
* that tightly encloses the leaves below. All nodes hold from M to 2M entries, where M is 4.
* The bounding boxes of two entries may overlap, which allows arbitrary structures to be represented.
* A search for a point or an area is a simple recursive walk through the tree to collect appropriate leaf nodes.
* Insertion and deletion, however, are more complex operations. The figure below illustrates how R-Trees work:
*
*
*/
public class RTNode extends AbstractFixpRectangle {
/** lower bound on R-tree node size */
private static final int MINRTNODESIZE = 4;
/** upper bound on R-tree node size */
private static final int MAXRTNODESIZE = (MINRTNODESIZE * 2);
/** bounds of this node and its children */
private long fixpMinX;
private long fixpMinY;
private long fixpMaxX;
private long fixpMaxY;
/** number of children */
private int total;
/** children */
private final Object[] pointers = new Object[MAXRTNODESIZE];
/** nonzero if children are terminal */
private boolean flag;
/** parent node */
private RTNode parent;
@Override
public long getFixpMinX() {
return fixpMinX;
}
@Override
public long getFixpMinY() {
return fixpMinY;
}
@Override
public long getFixpMaxX() {
return fixpMaxX;
}
@Override
public long getFixpMaxY() {
return fixpMaxY;
}
@Override
public void setFixp(long fixpMinX, long fixpMinY, long fixpMaxX, long fixpMaxY) {
throw new UnsupportedOperationException();
}
@Override
public AbstractFixpRectangle createFixp(long fixpMinX, long fixpMinY, long fixpMaxX, long fixpMaxY) {
throw new UnsupportedOperationException();
}
public void add(AbstractFixpRectangle r) {
fixpMinX = Math.min(fixpMinX, r.getFixpMinX());
fixpMinY = Math.min(fixpMinY, r.getFixpMinY());
fixpMaxX = Math.max(fixpMaxX, r.getFixpMaxX());
fixpMaxY = Math.max(fixpMaxY, r.getFixpMaxY());
}
/** Method to get the number of children of this RTNode. */
public int getTotal() {
return total;
}
/** Method to set the number of children of this RTNode. */
private void setTotal(int total) {
this.total = total;
}
/** Method to get the parent of this RTNode. */
private RTNode getParent() {
return parent;
}
/** Method to set the parent of this RTNode. */
private void setParent(RTNode parent) {
this.parent = parent;
}
/** Method to get the number of children of this RTNode. */
public Object getChild(int index) {
return pointers[index];
}
@SuppressWarnings("unchecked")
public T getChildLeaf(int index) {
return (T)pointers[index];
}
@SuppressWarnings("unchecked")
public RTNode getChildTree(int index) {
return (RTNode)pointers[index];
}
/** Method to set the number of children of this RTNode. */
private void setChild(int index, Object obj) {
this.pointers[index] = obj;
}
/** Method to get the leaf/branch flag of this RTNode. */
public boolean getFlag() {
return flag;
}
/** Method to set the leaf/branch flag of this RTNode. */
private void setFlag(boolean flag) {
this.flag = flag;
}
/** Method to set the bounds of this RTNode. */
private void setBounds(AbstractFixpRectangle bounds) {
fixpMinX = bounds.getFixpMinX();
fixpMinY = bounds.getFixpMinY();
fixpMaxX = bounds.getFixpMaxX();
fixpMaxY = bounds.getFixpMaxY();
}
/** Method to extend the bounds of this RTNode by "bounds". */
private void unionBounds(AbstractFixpRectangle bounds) {
fixpMinX = Math.min(fixpMinX, bounds.getFixpMinX());
fixpMinY = Math.min(fixpMinY, bounds.getFixpMinY());
fixpMaxX = Math.max(fixpMaxX, bounds.getFixpMaxX());
fixpMaxY = Math.max(fixpMaxY, bounds.getFixpMaxY());
}
/**
* Method to create the top-level R-Tree structure for a new Cell.
* @return an RTNode object that is empty.
*/
public static RTNode makeTopLevel() {
RTNode top = new RTNode();
top.total = 0;
top.flag = true;
top.parent = null;
return top;
}
/**
* Method to link this RTBounds into the R-tree of its parent Cell.
* This is static, because it may modify the root node, and so it must
* take a root node and possibly return a different one.
* @param env the environment of this operation (for messages).
* @param root root of the RTree.
* @param geom RTBounds to link.
* @return new root of RTree.
*/
public static RTNode linkGeom(Object env, RTNode root, T geom) {
// find the bottom-level branch (a RTNode with leafs) that would expand least by adding this RTBounds
if (root == null) {
return null;
}
AbstractFixpRectangle geomBounds = geom.getBounds();
long geomMinX = geomBounds.getFixpMinX();
long geomMinY = geomBounds.getFixpMinY();
long geomMaxX = geomBounds.getFixpMaxX();
long geomMaxY = geomBounds.getFixpMaxY();
RTNode rtn = root;
for (;;) {
// if R-tree node contains primitives, exit loop
if (rtn.getFlag()) {
break;
}
// find sub-node that would expand the least
double bestExpand = 0;
int bestSubNode = 0;
for (int i = 0; i < rtn.getTotal(); i++) {
// get bounds and area of sub-node
RTNode subrtn = rtn.getChildTree(i);
double area = (double) subrtn.getFixpWidth() * (double) subrtn.getFixpHeight();
// get area of sub-node with new element
long fixpNewUnionMinX = Math.min(geomMinX, subrtn.fixpMinX);
long fixpNewUnionMinY = Math.min(geomMinY, subrtn.fixpMinY);
long fixpNewUnionMaxX = Math.max(geomMaxX, subrtn.fixpMaxX);
long fixpNewUnionMaxY = Math.max(geomMaxY, subrtn.fixpMaxY);
double newArea = (double) (fixpNewUnionMaxX - fixpNewUnionMinX) * (double) (fixpNewUnionMaxY - fixpNewUnionMinY);
// accumulate the least expansion
double expand = newArea - area;
// remember the child that expands the least
if (i != 0 && expand > bestExpand) {
continue;
}
bestExpand = expand;
bestSubNode = i;
}
// recurse down to sub-node that expanded least
rtn = rtn.getChildTree(bestSubNode);
}
// add this geometry element to the correct leaf R-tree node
return rtn.addToRTNode(geom, env, root);
}
/**
* Method to remove this geometry from the R-tree its parent cell.
* This is static, because it may modify the root node, and so it must
* take a root node and possibly return a different one.
* @param env the environment of this operation (for messages).
* @param root root of the RTree.
* @param geom RTBounds to unlink.
* @return new root of RTree.
*/
public static RTNode unLinkGeom(Object env, RTNode root, T geom) {
// find this node in the tree
RTNode whichRTN = null;
int whichInd = 0;
if (root == null) {
return null;
}
FindResult result = root.findGeom(geom);
if (result != null) {
whichRTN = result.rtnode;
whichInd = result.index;
} else {
result = root.findGeomAnywhere(geom);
// if (result == null)
// {
// System.out.println("Internal error: " + cell + " cannot find " + geom + " in R-Tree...Rebuilding R-Tree");
// root = makeTopLevel();
// for(Iterator it = cell.getArcs(); it.hasNext(); )
// root = linkGeom(env, root, (RTBounds)it.next());
// for(Iterator it = cell.getNodes(); it.hasNext(); )
// root = linkGeom(env, root, (RTBounds)it.next());
// return root;
// }
whichRTN = result.rtnode;
whichInd = result.index;
System.out.println("Internal warning: " + geom + " not in proper R-Tree location in " + env);
}
// delete geom from this R-tree node
return whichRTN.removeRTNode(whichInd, env, root);
}
private static int branchCount;
/**
* Debugging method to print this R-Tree.
* @param indent the level of the tree, for proper indentation.
*/
public void printRTree(int indent) {
if (indent == 0) {
branchCount = 0;
}
StringBuilder line = new StringBuilder();
for (int i = 0; i < indent; i++) {
line.append(' ');
}
line.append("RTNodeFixp");
if (flag) {
branchCount++;
line.append(" NUMBER ").append(branchCount);
}
appendRect(line, this);
line.append(" has ").append(total).append(" children:");
System.out.println(line);
for (int j = 0; j < total; j++) {
if (flag) {
line.setLength(0);
for (int i = 0; i < indent + 3; i++) {
line.append(' ');
}
T child = getChildLeaf(j);
line.append("Child");
appendRect(line, child.getBounds());
line.append(" is ").append(child);
System.out.println(line);
} else {
getChildTree(j).printRTree(indent + 3);
}
}
}
private static void appendRect(StringBuilder sb, AbstractFixpRectangle r) {
appendRect(sb, r.getFixpMinX(), r.getFixpMinY(), r.getFixpMaxX(), r.getFixpMaxY());
}
private static void appendRect(StringBuilder sb, long fixpMinX, long fixpMinY, long fixpMaxX, long fixpMaxY) {
sb.append(" X(").append(FixpCoord.fixpToLambda(fixpMinX)).append("-").append(FixpCoord.fixpToLambda(fixpMaxX))
.append(") Y(").append(FixpCoord.fixpToLambda(fixpMinY)).append("-").append(FixpCoord.fixpToLambda(fixpMaxY)).append(")");
}
/**
* Debugging method to display this R-Tree.
* @param cell the Cell in which to show the R-Tree.
*/
public void displayRTree(Cell cell) {
EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_();
wnd.clearHighlighting();
displaySubRTree(cell);
wnd.finishedHighlighting();
}
private void displaySubRTree(Cell cell) {
EditWindow_ wnd = Job.getUserInterface().getCurrentEditWindow_();
for (int j = 0; j < getTotal(); j++) {
if (getFlag()) {
wnd.addHighlightArea(getChildLeaf(j).getBounds(), cell);
} else {
getChildTree(j).displaySubRTree(cell);
}
}
}
/**
* Method to return the number of leaf entries in this RTree.
* @return the number of leaf entries in this RTree.
*/
public int tallyRTree() {
int total = 0;
if (getFlag()) {
total += getTotal();
} else {
for (int j = 0; j < getTotal(); j++) {
total += getChildTree(j).tallyRTree();
}
}
return total;
}
/**
* Method to check the validity of an RTree node.
* @param level the level of the node in the tree (for error reporting purposes).
* @param env the environment in which this node resides.
*/
public void checkRTree(int level, Object env) {
long minX, minY, maxX, maxY;
if (total == 0) {
minX = minY = maxX = maxY = 0;
} else {
minX = minY = Long.MAX_VALUE;
maxX = maxY = Long.MIN_VALUE;
for (int i = 0; i < total; i++) {
AbstractFixpRectangle r = getBBox(i);
minX = Math.min(minX, r.getFixpMinX());
minY = Math.min(minY, r.getFixpMinY());
maxX = Math.max(maxX, r.getFixpMaxX());
maxY = Math.max(maxY, r.getFixpMaxY());
}
}
if (minX != fixpMinX || minY != fixpMinY || maxX != fixpMaxX || maxY != fixpMaxY) {
StringBuilder sb = new StringBuilder();
sb.append("Tree of ").append(env).append(" at level ").append(level).append(" has bounds");
appendRect(sb, minX, minY, maxX, maxY);
sb.append(" but stored bounds are");
appendRect(sb, this);
System.out.println(sb);
for (int i = 0; i < total; i++) {
sb.setLength(0);
sb.append(" ---Child ").append(i).append(" is");
appendRect(sb, getBBox(i));
System.out.println(sb);
}
}
if (!flag) {
for (int j = 0; j < total; j++) {
getChildTree(j).checkRTree(level + 1, env);
}
}
}
/**
* Method to get the bounding box of child "child" of this R-tree node.
*/
private AbstractFixpRectangle getBBox(int child) {
return flag ? getChildLeaf(child).getBounds() : getChildTree(child);
}
/**
* Method to recompute the bounds of this R-tree node.
*/
private void figBounds() {
if (total == 0) {
fixpMinX = fixpMinY = fixpMaxX = fixpMaxY = 0;
return;
}
long minX = Long.MAX_VALUE;
long minY = Long.MAX_VALUE;
long maxX = Long.MIN_VALUE;
long maxY = Long.MIN_VALUE;
if (flag) {
for (int i = 0; i < total; i++) {
AbstractFixpRectangle r = getChildLeaf(i).getBounds();
minX = Math.min(minX, r.getFixpMinX());
minY = Math.min(minY, r.getFixpMinY());
maxX = Math.max(maxX, r.getFixpMaxX());
maxY = Math.max(maxY, r.getFixpMaxY());
}
} else {
for (int i = 0; i < total; i++) {
RTNode subrt = getChildTree(i);
minX = Math.min(minX, subrt.fixpMinX);
minY = Math.min(minY, subrt.fixpMinY);
maxX = Math.max(maxX, subrt.fixpMaxX);
maxY = Math.max(maxY, subrt.fixpMaxY);
}
}
this.fixpMinX = minX;
this.fixpMinY = minY;
this.fixpMaxX = maxX;
this.fixpMaxY = maxY;
}
/**
* Method to add object "rtnInsert" to this R-tree node, which is in cell "cell". Method may have to
* split the node and recurse up the tree
*/
private RTNode addToRTNode(Object rtnInsert, Object env, RTNode root) {
// see if there is room in the R-tree node
if (getTotal() >= MAXRTNODESIZE) {
// no room: copy list to temp one
RTNode temp = new RTNode();
temp.setTotal(getTotal());
temp.setFlag(getFlag());
for (int i = 0; i < getTotal(); i++) {
temp.setChild(i, getChild(i));
}
// find the element farthest from new object
AbstractFixpRectangle bounds;
if (rtnInsert instanceof RTBounds) {
@SuppressWarnings("unchecked")
T leaf = (T) rtnInsert;
bounds = leaf.getBounds();
} else {
@SuppressWarnings("unchecked")
RTNode tree = (RTNode) rtnInsert;
bounds = tree;
}
long thisFixpCenterX = bounds.getFixpCenterX();
long thisFixpCenterY = bounds.getFixpCenterY();
double newDistSq = 0;
int newN = 0;
for (int i = 0; i < temp.getTotal(); i++) {
AbstractFixpRectangle thisv = temp.getBBox(i);
double distSq = Point2D.distanceSq(thisFixpCenterX, thisFixpCenterY, thisv.getFixpCenterX(), thisv.getFixpCenterY());
if (distSq >= newDistSq) {
newDistSq = distSq;
newN = i;
}
}
// now find element farthest from "newN"
bounds = temp.getBBox(newN);
thisFixpCenterX = bounds.getFixpCenterX();
thisFixpCenterY = bounds.getFixpCenterY();
double oldDistSq = 0;
int oldN = 0;
if (oldN == newN) {
oldN++;
}
for (int i = 0; i < temp.getTotal(); i++) {
if (i == newN) {
continue;
}
AbstractFixpRectangle thisv = temp.getBBox(i);
double distSq = Point2D.distanceSq(thisFixpCenterX, thisFixpCenterY, thisv.getFixpCenterX(), thisv.getFixpCenterY());
if (distSq >= oldDistSq) {
oldDistSq = distSq;
oldN = i;
}
}
// allocate a new R-tree node
RTNode newrtn = new RTNode();
newrtn.setFlag(getFlag());
newrtn.setParent(getParent());
// put the first seed element into the new RTree
newrtn.setChild(0, temp.getChild(newN));
newrtn.setTotal(1);
if (!newrtn.getFlag()) {
newrtn.getChildTree(0).setParent(newrtn);
}
temp.setChild(newN, null);
AbstractFixpRectangle newBounds = newrtn.getBBox(0);
newrtn.setBounds(newBounds);
double newArea = (double) newBounds.getFixpWidth() * (double) newBounds.getFixpHeight();
// initialize the old R-tree node and put in the other seed element
setChild(0, temp.getChild(oldN));
if (!getFlag()) {
getChildTree(0).setParent(this);
}
for (int i = 1; i < getTotal(); i++) {
setChild(i, null);
}
setTotal(1);
temp.setChild(oldN, null);
AbstractFixpRectangle oldBounds = getBBox(0);
setBounds(oldBounds);
double oldArea = (double) oldBounds.getFixpWidth() * (double) oldBounds.getFixpHeight();
// cluster the rest of the nodes
for (;;) {
// search for a cluster about each new node
int bestNewNode = -1, bestOldNode = -1;
double bestNewExpand = 0, bestOldExpand = 0;
for (int i = 0; i < temp.getTotal(); i++) {
if (temp.getChild(i) == null) {
continue;
}
bounds = temp.getBBox(i);
long fixpNewUnionMinX = Math.min(newBounds.getFixpMinX(), bounds.getFixpMinX());
long fixpNewUnionMinY = Math.min(newBounds.getFixpMinY(), bounds.getFixpMinY());
long fixpNewUnionMaxX = Math.max(newBounds.getFixpMaxX(), bounds.getFixpMaxX());
long fixpNewUnionMaxY = Math.max(newBounds.getFixpMaxY(), bounds.getFixpMaxY());
double newAreaPlus = (double) (fixpNewUnionMaxX - fixpNewUnionMinX) * (double) (fixpNewUnionMaxY - fixpNewUnionMinY);
long fixpOldUnionMinX = Math.min(oldBounds.getFixpMinX(), bounds.getFixpMinX());
long fixpOldUnionMinY = Math.min(oldBounds.getFixpMinY(), bounds.getFixpMinY());
long fixpOldUnionMaxX = Math.max(oldBounds.getFixpMaxX(), bounds.getFixpMaxX());
long fixpOldUnionMaxY = Math.max(oldBounds.getFixpMaxY(), bounds.getFixpMaxY());
double oldAreaPlus = (double) (fixpOldUnionMaxX - fixpOldUnionMinX) * (double) (fixpOldUnionMaxY - fixpOldUnionMinY);
// remember the child that expands the new node the least
if (bestNewNode < 0 || newAreaPlus - newArea < bestNewExpand) {
bestNewExpand = newAreaPlus - newArea;
bestNewNode = i;
}
// remember the child that expands the old node the least
if (bestOldNode < 0 || oldAreaPlus - oldArea < bestOldExpand) {
bestOldExpand = oldAreaPlus - oldArea;
bestOldNode = i;
}
}
// if there were no nodes added, all have been clustered
if (bestNewNode == -1 && bestOldNode == -1) {
break;
}
// if both selected the same object, select another "old node"
if (bestNewNode == bestOldNode) {
bestOldNode = -1;
for (int i = 0; i < temp.getTotal(); i++) {
if (i == bestNewNode) {
continue;
}
if (temp.getChild(i) == null) {
continue;
}
bounds = temp.getBBox(i);
long fixpOldUnionMinX = Math.min(oldBounds.getFixpMinX(), bounds.getFixpMinX());
long fixpOldUnionMinY = Math.min(oldBounds.getFixpMinY(), bounds.getFixpMinY());
long fixpOldUnionMaxX = Math.max(oldBounds.getFixpMaxX(), bounds.getFixpMaxX());
long fixpOldUnionMaxY = Math.max(oldBounds.getFixpMaxY(), bounds.getFixpMaxY());
double oldAreaPlus = (double) (fixpOldUnionMaxX - fixpOldUnionMinX) * (double) (fixpOldUnionMaxY - fixpOldUnionMinY);
// remember the child that expands the old node the least
if (bestOldNode < 0 || oldAreaPlus - oldArea < bestOldExpand) {
bestOldExpand = oldAreaPlus - oldArea;
bestOldNode = i;
}
}
}
// add to the proper "old node" to the old node cluster
if (bestOldNode != -1) {
// add this node to "rtn"
int curPos = getTotal();
setChild(curPos, temp.getChild(bestOldNode));
if (!getFlag()) {
getChildTree(curPos).setParent(this);
}
setTotal(curPos + 1);
temp.setChild(bestOldNode, null);
unionBounds(getBBox(curPos));
oldBounds = this;
oldArea = (double) oldBounds.getFixpWidth() * (double) oldBounds.getFixpHeight();
}
// add to proper "new node" to the new node cluster
if (bestNewNode != -1) {
// add this node to "newrtn"
int curPos = newrtn.getTotal();
newrtn.setChild(curPos, temp.getChild(bestNewNode));
newrtn.setTotal(curPos + 1);
if (!newrtn.getFlag()) {
newrtn.getChildTree(curPos).setParent(newrtn);
}
temp.setChild(bestNewNode, null);
newrtn.unionBounds(newrtn.getBBox(curPos));
newBounds = newrtn;
newArea = (double) newBounds.getFixpWidth() * (double) newBounds.getFixpHeight();
}
}
// sensibility check
if (temp.getTotal() != getTotal() + newrtn.getTotal()) {
System.out.println("R-trees: " + temp.getTotal() + " nodes split to "
+ getTotal() + " and " + newrtn.getTotal() + "!");
}
// now recursively insert this new element up the tree
if (getParent() == null) {
// at top of tree: create a new level
assert root == this;
RTNode newroot = new RTNode();
newroot.setTotal(2);
newroot.setChild(0, this);
newroot.setChild(1, newrtn);
newroot.setFlag(false);
newroot.setParent(null);
setParent(newroot);
newrtn.setParent(newroot);
newroot.figBounds();
root = newroot;
} else {
// first recompute bounding box of R-tree nodes up the tree
for (RTNode r = getParent(); r != null; r = r.getParent()) {
r.figBounds();
}
// now add the new node up the tree
root = getParent().addToRTNode(newrtn, env, root);
}
}
// now add "rtnInsert" to the R-tree node
int curPos = getTotal();
setChild(curPos, rtnInsert);
setTotal(curPos + 1);
// compute the new bounds
AbstractFixpRectangle bounds = getBBox(curPos);
if (getTotal() == 1 && getParent() == null) {
// special case when adding the first node in a cell
setBounds(bounds);
return root;
}
// recursively update node sizes
RTNode climb = this;
for (;;) {
climb.unionBounds(bounds);
if (climb.getParent() == null) {
break;
}
climb = climb.getParent();
}
// now check the RTree
// checkRTree(0, env);
return root;
}
/**
* Method to remove entry "ind" from this R-tree node in cell "cell"
*/
private RTNode removeRTNode(int ind, Object env, RTNode root) {
// delete entry from this R-tree node
int j = 0;
for (int i = 0; i < getTotal(); i++) {
if (i != ind) {
setChild(j++, getChild(i));
}
}
setTotal(j);
// see if node is now too small
if (getTotal() < MINRTNODESIZE) {
// if recursed to top, shorten R-tree
RTNode prtn = getParent();
if (prtn == null) {
// if tree has no hierarchy, allow short node
if (getFlag()) {
// compute correct bounds of the top node
figBounds();
return root;
}
// save all top-level entries
RTNode temp = new RTNode();
temp.setTotal(getTotal());
temp.setFlag(true);
for (int i = 0; i < getTotal(); i++) {
temp.setChild(i, getChild(i));
}
// erase top level
setTotal(0);
setFlag(true);
// reinsert all data
for (int i = 0; i < temp.getTotal(); i++) {
root = temp.getChildTree(i).reInsert(env, root);
}
return root;
}
// node has too few entries, must delete it and reinsert members
int found = -1;
for (int i = 0; i < prtn.getTotal(); i++) {
if (prtn.getChild(i) == this) {
found = i;
break;
}
}
if (found < 0) {
System.out.println("R-trees: cannot find entry in parent");
}
// remove this entry from its parent
root = prtn.removeRTNode(found, env, root);
// reinsert the entries
return reInsert(env, root);
}
// recompute bounding box of this R-tree node and all up the tree
RTNode climb = this;
for (;;) {
climb.figBounds();
if (climb.getParent() == null) {
break;
}
climb = climb.getParent();
}
return root;
}
/**
* Method to reinsert the tree of nodes below this RTNode into cell "cell".
*/
private RTNode reInsert(Object env, RTNode root) {
if (getFlag()) {
for (int i = 0; i < getTotal(); i++) {
root = linkGeom(env, root, getChildLeaf(i));
}
} else {
for (int i = 0; i < getTotal(); i++) {
root = getChildTree(i).reInsert(env, root);
}
}
return root;
}
private static class FindResult {
private final RTNode rtnode;
private int index;
private FindResult(RTNode rtnode, int index) {
this.rtnode = rtnode;
this.index = index;
}
}
/**
* Method to find the location of geometry module "geom" in the R-tree
* below this. The subnode that contains this module is placed in "subrtn"
* and the index in that subnode is placed in "subind". The method returns
* null if it is unable to find the geometry module.
*/
private FindResult findGeom(T geom) {
// if R-tree node contains primitives, search for direct hit
if (getFlag()) {
for (int i = 0; i < getTotal(); i++) {
if (getChildLeaf(i) == geom) {
return new FindResult(this, i);
}
}
return null;
}
// recurse on all sub-nodes that would contain this geometry module
AbstractFixpRectangle geomBounds = geom.getBounds();
for (int i = 0; i < getTotal(); i++) {
// get bounds and area of sub-node
AbstractFixpRectangle bounds = getBBox(i);
if (bounds.getFixpMaxX() < geomBounds.getFixpMinX()) {
continue;
}
if (bounds.getFixpMinX() > geomBounds.getFixpMaxX()) {
continue;
}
if (bounds.getFixpMaxY() < geomBounds.getFixpMinY()) {
continue;
}
if (bounds.getFixpMinY() > geomBounds.getFixpMaxY()) {
continue;
}
FindResult subRet = getChildTree(i).findGeom(geom);
if (subRet != null) {
return subRet;
}
}
return null;
}
/**
* Method to find the location of geometry module "geom" anywhere in the R-tree
* at "rtn". The subnode that contains this module is placed in "subrtn"
* and the index in that subnode is placed in "subind". The method returns
* false if it is unable to find the geometry module.
*/
private FindResult findGeomAnywhere(T geom) {
// if R-tree node contains primitives, search for direct hit
if (getFlag()) {
for (int i = 0; i < getTotal(); i++) {
if (getChildLeaf(i) == geom) {
return new FindResult(this, i);
}
}
return null;
}
// recurse on all sub-nodes
for (int i = 0; i < getTotal(); i++) {
FindResult retVal = getChildTree(i).findGeomAnywhere(geom);
if (retVal != null) {
return retVal;
}
}
return null;
}
/**
* Class to search a given area of a Cell.
* This class acts like an Iterator, returning RTBounds objects that are inside the selected area.
*
* For example, here is the code to search cell "myCell" in the area "bounds" (in database coordinates):
*
*
* for(RTNode.Search sea = new RTNode.Search(bounds, cell); sea.hasNext(); )
* {
* Geometric geom = sea.next();
* if (geom instanceof NodeInst)
* {
* NodeInst ni = (NodeInst)geom;
* // process NodeInst ni in the selected area
* } else
* {
* ArcInst ai = (ArcInst)geom;
* // process ArcInst ai in the selected area
* }
* }
*
*/
public static class Search implements Iterator {
/** maximum depth of search */
private static final int MAXDEPTH = 100;
/** current depth of search */
private int depth;
/** RTNode stack of search */
@SuppressWarnings({ "unchecked", "rawtypes" })
private final RTNode[] rtn = new RTNode[MAXDEPTH];;
/** index stack of search */
private int[] position = new int[MAXDEPTH];
/** desired search bounds */
private final long searchBoundsMinX;
private final long searchBoundsMinY;
private final long searchBoundsMaxX;
private final long searchBoundsMaxY;
/** the next object to return */
private T nextObj;
/** includes objects on the search area edges */
private final boolean includeEdges;
public Search(Rectangle2D bounds, RTNode root, boolean includeEdges) {
this.depth = 0;
this.rtn[0] = root;
if (bounds instanceof AbstractFixpRectangle) {
AbstractFixpRectangle fr = (AbstractFixpRectangle)bounds;
searchBoundsMinX = fr.getFixpMinX();
searchBoundsMinY = fr.getFixpMinY();
searchBoundsMaxX = fr.getFixpMaxX();
searchBoundsMaxY = fr.getFixpMaxY();
} else {
searchBoundsMinX = FixpCoord.lambdaToFixp(bounds.getMinX());
searchBoundsMinY = FixpCoord.lambdaToFixp(bounds.getMinY());
searchBoundsMaxX = FixpCoord.lambdaToFixp(bounds.getMaxX());
searchBoundsMaxY = FixpCoord.lambdaToFixp(bounds.getMaxY());
}
this.includeEdges = includeEdges;
this.nextObj = null;
}
/**
* Method to return the next object in the bounds of the search.
* @return the next object found. Returns null when all objects have been reported.
*/
private T nextObject() {
for (;;) {
RTNode rtnode = rtn[depth];
int i = position[depth]++;
if (i < rtnode.getTotal()) {
AbstractFixpRectangle nodeBounds = rtnode.getBBox(i);
if (includeEdges) {
if (nodeBounds.getFixpMaxX() < searchBoundsMinX) {
continue;
}
if (nodeBounds.getFixpMinX() > searchBoundsMaxX) {
continue;
}
if (nodeBounds.getFixpMaxY() < searchBoundsMinY) {
continue;
}
if (nodeBounds.getFixpMinY() > searchBoundsMaxY) {
continue;
}
} else {
if (nodeBounds.getFixpMaxX() <= searchBoundsMinX) {
continue;
}
if (nodeBounds.getFixpMinX() >= searchBoundsMaxX) {
continue;
}
if (nodeBounds.getFixpMaxY() <= searchBoundsMinY) {
continue;
}
if (nodeBounds.getFixpMinY() >= searchBoundsMaxY) {
continue;
}
}
if (rtnode.getFlag()) {
return rtnode.getChildLeaf(i);
}
// look down the hierarchy
if (depth >= MAXDEPTH - 1) {
System.out.println("R-trees: search too deep");
continue;
}
depth++;
rtn[depth] = rtnode.getChildTree(i);
position[depth] = 0;
} else {
// pop up the hierarchy
if (depth == 0) {
break;
}
depth--;
}
}
return null;
}
@Override
public boolean hasNext() {
if (nextObj == null) {
nextObj = nextObject();
}
return nextObj != null;
}
@Override
public T next() {
if (nextObj != null) {
T ret = nextObj;
nextObj = null;
return ret;
}
return nextObject();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Search.remove()");
}
}
}