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

org.openscience.cdk.graph.rebond.Bspt Maven / Gradle / Ivy

There is a newer version: 2.9
Show newest version
/* Copyright (C) 2003-2007  Miguel Howard 
 *
 * Contact: [email protected]
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.openscience.cdk.graph.rebond;


import java.util.Enumeration;

/**
 *  BSP-Tree stands for Binary Space Partitioning Tree.
 *  The tree partitions n-dimensional space (in our case 3) into little
 *  boxes, facilitating searches for things which are *nearby*.
 *  For some useful background info, search the web for "bsp tree faq".
 *  Our application is somewhat simpler because we are storing points instead
 *  of polygons.
 *
 * 

We are working with three dimensions. For the purposes of the Bspt code * these dimensions are stored as 0, 1, or 2. Each node of the tree splits * along the next dimension, wrapping around to 0. *

 *    mySplitDimension = (parentSplitDimension + 1) % 3;
 * 
* A split value is stored in the node. Values which are ≤ splitValue are * stored down the left branch. Values which are ≥ splitValue are stored * down the right branch. When this happens, the search must proceed down * both branches. * Planar and crystalline substructures can generate values which are == along * one dimension. * *

To get a good picture in your head, first think about it in one dimension, * points on a number line. The tree just partitions the points. * Now think about 2 dimensions. The first node of the tree splits the plane * into two rectangles along the x dimension. The second level of the tree * splits the subplanes (independently) along the y dimension into smaller * rectangles. The third level splits along the x dimension. * In three dimensions, we are doing the same thing, only working with * 3-d boxes. * *

Three enumerators are provided *

    *
  • enumNear(Bspt.Tuple center, double distance)
    * returns all the points contained in of all the boxes which are within * distance from the center. *
  • enumSphere(Bspt.Tuple center, double distance)
    * returns all the points which are contained within the sphere (inclusive) * defined by center + distance *
  • enumHemiSphere(Bspt.Tuple center, double distance)
    * same as sphere, but only the points which are greater along the * x dimension *
* * @author Miguel Howard * @cdk.created 2003-05 * * @cdk.module standard * @cdk.githash * @cdk.keyword rebonding * @cdk.keyword Binary Space Partitioning Tree * @cdk.keyword join-the-dots */ public final class Bspt { private final static int LEAF_COUNT = 4; private final static int STACK_DEPTH = 64;/* * this corresponds to the max * height of the tree */ int dimMax; Element eleRoot; /* * static double distance(int dim, Tuple t1, Tuple t2) { return * Math.sqrt(distance2(dim, t1, t2)); } static double distance2(int dim, * Tuple t1, Tuple t2) { double distance2 = 0.0; while (--dim >= 0) { double * distT = t1.getDimValue(dim) - t2.getDimValue(dim); distance2 += * distT*distT; } return distance2; } */ public Bspt(int dimMax) { this.dimMax = dimMax; this.eleRoot = new Leaf(); } public void addTuple(Tuple tuple) { if (!eleRoot.addTuple(tuple)) { eleRoot = new Node(0, dimMax, (Leaf) eleRoot); if (!eleRoot.addTuple(tuple)) throw new Error("Bspt.addTuple() failed"); } } @Override public String toString() { return eleRoot.toString(); } protected void dump() { eleRoot.dump(0); } protected Enumeration enumeration() { return new EnumerateAll(); } class EnumerateAll implements Enumeration { Node[] stack; int sp; int i; Leaf leaf; EnumerateAll() { stack = new Node[STACK_DEPTH]; sp = 0; Element ele = eleRoot; while (ele instanceof Node) { Node node = (Node) ele; if (sp == STACK_DEPTH) throw new Error("Bspt.EnumerateAll tree stack overflow"); stack[sp++] = node; ele = node.eleLE; } leaf = (Leaf) ele; i = 0; } @Override public boolean hasMoreElements() { return (i < leaf.count) || (sp > 0); } @Override public Object nextElement() { if (i == leaf.count) { // logger.debug("-->" + stack[sp-1].splitValue); Element ele = stack[--sp].eleGE; while (ele instanceof Node) { Node node = (Node) ele; stack[sp++] = node; ele = node.eleLE; } leaf = (Leaf) ele; i = 0; } return leaf.tuples[i++]; } } protected Enumeration enumNear(Tuple center, double distance) { return new EnumerateNear(center, distance); } class EnumerateNear implements Enumeration { Node[] stack; int sp; int i; Leaf leaf; double distance; Tuple center; EnumerateNear(Tuple center, double distance) { this.distance = distance; this.center = center; stack = new Node[STACK_DEPTH]; sp = 0; Element ele = eleRoot; while (ele instanceof Node) { Node node = (Node) ele; if (center.getDimValue(node.dim) - distance <= node.splitValue) { if (sp == STACK_DEPTH) throw new Error("Bspt.EnumerateNear tree stack overflow"); stack[sp++] = node; ele = node.eleLE; } else { ele = node.eleGE; } } leaf = (Leaf) ele; i = 0; } @Override public boolean hasMoreElements() { if (i < leaf.count) return true; if (sp == 0) return false; Element ele = stack[--sp]; while (ele instanceof Node) { Node node = (Node) ele; if (center.getDimValue(node.dim) + distance < node.splitValue) { if (sp == 0) return false; ele = stack[--sp]; } else { ele = node.eleGE; while (ele instanceof Node) { Node nodeLeft = (Node) ele; stack[sp++] = nodeLeft; ele = nodeLeft.eleLE; } } } leaf = (Leaf) ele; i = 0; return true; } @Override public Object nextElement() { return leaf.tuples[i++]; } } protected EnumerateSphere enumSphere(Tuple center, double distance) { return new EnumerateSphere(center, distance, false); } protected EnumerateSphere enumHemiSphere(Tuple center, double distance) { return new EnumerateSphere(center, distance, true); } class EnumerateSphere implements Enumeration { Node[] stack; int sp; int i; Leaf leaf; double distance; double distance2; Tuple center; double centerValues[]; double foundDistance2; // the dist squared of a found Element; // when set, only the hemisphere sphere .GT. or .EQ. the point // (on the first dim) is returned boolean tHemisphere; EnumerateSphere(Tuple center, double distance, boolean tHemisphere) { this.distance = distance; this.distance2 = distance * distance; this.center = center; this.tHemisphere = tHemisphere; centerValues = new double[dimMax]; for (int dim = dimMax; --dim >= 0;) centerValues[dim] = center.getDimValue(dim); stack = new Node[STACK_DEPTH]; sp = 0; Element ele = eleRoot; while (ele instanceof Node) { Node node = (Node) ele; if (center.getDimValue(node.dim) - distance <= node.splitValue) { if (sp == STACK_DEPTH) throw new Error("Bspt.EnumerateSphere tree stack overflow"); stack[sp++] = node; ele = node.eleLE; } else { ele = node.eleGE; } } leaf = (Leaf) ele; i = 0; } private boolean isWithin(Tuple t) { double dist2; double distT; distT = t.getDimValue(0) - centerValues[0]; if (tHemisphere && distT < 0) { return false; } dist2 = distT * distT; if (dist2 > distance2) { return false; } for (int dim = dimMax; --dim > 0;) { distT = t.getDimValue(dim) - centerValues[dim]; dist2 += distT * distT; if (dist2 > distance2) { return false; } } this.foundDistance2 = dist2; return true; } @Override public boolean hasMoreElements() { while (true) { for (; i < leaf.count; ++i) if (isWithin(leaf.tuples[i])) return true; if (sp == 0) return false; Element ele = stack[--sp]; while (ele instanceof Node) { Node node = (Node) ele; if (center.getDimValue(node.dim) + distance < node.splitValue) { if (sp == 0) return false; ele = stack[--sp]; } else { ele = node.eleGE; while (ele instanceof Node) { Node nodeLeft = (Node) ele; stack[sp++] = nodeLeft; ele = nodeLeft.eleLE; } } } leaf = (Leaf) ele; i = 0; } } @Override public Object nextElement() { return leaf.tuples[i++]; } public double foundDistance2() { return foundDistance2; } } public interface Tuple { public double getDimValue(int dim); } interface Element { boolean addTuple(Tuple tuple); void dump(int level); boolean isLeafWithSpace(); } class Node implements Element { Element eleLE; int dim; int dimMax; double splitValue; Element eleGE; Node(int dim, int dimMax, Leaf leafLE) { this.eleLE = leafLE; this.dim = dim; this.dimMax = dimMax; this.splitValue = leafLE.getSplitValue(dim); this.eleGE = new Leaf(leafLE, dim, splitValue); } @Override public boolean addTuple(Tuple tuple) { if (tuple.getDimValue(dim) < splitValue) { if (eleLE.addTuple(tuple)) return true; eleLE = new Node((dim + 1) % dimMax, dimMax, (Leaf) eleLE); return eleLE.addTuple(tuple); } if (tuple.getDimValue(dim) > splitValue) { if (eleGE.addTuple(tuple)) return true; eleGE = new Node((dim + 1) % dimMax, dimMax, (Leaf) eleGE); return eleGE.addTuple(tuple); } if (eleLE.isLeafWithSpace()) eleLE.addTuple(tuple); else if (eleGE.isLeafWithSpace()) eleGE.addTuple(tuple); else if (eleLE instanceof Node) eleLE.addTuple(tuple); else if (eleGE instanceof Node) eleGE.addTuple(tuple); else { eleLE = new Node((dim + 1) % dimMax, dimMax, (Leaf) eleLE); return eleLE.addTuple(tuple); } return true; } @Override public String toString() { return eleLE.toString() + dim + ":" + splitValue + "\n" + eleGE.toString(); } @Override public void dump(int level) { System.out.println(""); eleLE.dump(level + 1); for (int i = 0; i < level; ++i) System.out.print("-"); System.out.println(">" + splitValue); eleGE.dump(level + 1); } @Override public boolean isLeafWithSpace() { return false; } } class Leaf implements Element { int count; Tuple[] tuples; Leaf() { count = 0; tuples = new Tuple[LEAF_COUNT]; } Leaf(Leaf leaf, int dim, double splitValue) { this(); for (int i = LEAF_COUNT; --i >= 0;) { Tuple tuple = leaf.tuples[i]; double value = tuple.getDimValue(dim); if (value > splitValue || (value == splitValue && ((i & 1) == 1))) { leaf.tuples[i] = null; tuples[count++] = tuple; } } int dest = 0; for (int src = 0; src < LEAF_COUNT; ++src) if (leaf.tuples[src] != null) leaf.tuples[dest++] = leaf.tuples[src]; leaf.count = dest; if (count == 0) tuples[LEAF_COUNT] = null; // explode } public double getSplitValue(int dim) { if (count != LEAF_COUNT) tuples[LEAF_COUNT] = null; return (tuples[0].getDimValue(dim) + tuples[LEAF_COUNT - 1].getDimValue(dim)) / 2; } @Override public String toString() { return "leaf:" + count + "\n"; } @Override public boolean addTuple(Tuple tuple) { if (count == LEAF_COUNT) return false; tuples[count++] = tuple; return true; } @Override public void dump(int level) { for (int i = 0; i < count; ++i) { Tuple t = tuples[i]; for (int j = 0; j < level; ++j) System.out.print("."); for (int dim = 0; dim < dimMax - 1; ++dim) System.out.print("" + t.getDimValue(dim) + ","); System.out.println("" + t.getDimValue(dimMax - 1)); } } @Override public boolean isLeafWithSpace() { return count < LEAF_COUNT; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy