/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * This program 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 2 of the License, or (at your option) any later * version. * * This program 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 * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *******************************************************************************/ package org.andork.spatial; import static org.andork.spatial.Rectmath.nmax; import static org.andork.spatial.Rectmath.nmin; import static org.andork.spatial.Rectmath.union; import java.util.Arrays; import java.util.BitSet; import java.util.Comparator; public class RfStarTree<T> implements SpatialIndex<float[], T> { public static class Branch<T> extends Node<T> implements RBranch<float[], T> { /** * The level of this branch in the tree. 0 for the level above leaves, 1 * for the level above that, etc. */ final int level; int numChildren; Node<T>[] children; public Branch(int dimension, int level, int numChildren) { super(Rectmath.voidRectf(dimension)); this.level = level; this.children = new Node[numChildren]; } @Override public Node<T> childAt(int index) { return children[index]; } @Override public int level() { return level; } @Override public int numChildren() { return numChildren; } void recalcMbr() { Arrays.fill(mbr, Float.NaN); for (int i = 0; i < numChildren; i++) { union(mbr, children[i].mbr, mbr); } } } class CenterDistanceComparator implements Comparator<Node<?>> { float[] otherMbr; public CenterDistanceComparator(float[] otherMbr) { super(); this.otherMbr = otherMbr; } @Override public int compare(Node<?> o1, Node<?> o2) { return -Float.compare(centerDistSq(o1.mbr, otherMbr), centerDistSq(o2.mbr, otherMbr)); } } class EnlargementComparator implements Comparator<Node<?>> { float[] newMbr; public EnlargementComparator(float[] newMbr) { super(); this.newMbr = newMbr; } @Override public int compare(Node<?> o1, Node<?> o2) { return Float.compare(enlargement(o1.mbr, newMbr), enlargement(o2.mbr, newMbr)); } } public static class Leaf<T> extends Node<T> implements RLeaf<float[], T> { final T object; public Leaf(float[] mbr, T object) { super(mbr); this.object = object; } @Override public int level() { return 0; } @Override public T object() { return object; } } static class LowerUpperComparator implements Comparator<Node<?>> { final int axis; final int dimension; public LowerUpperComparator(int axis, int dimension) { super(); this.axis = axis; this.dimension = dimension; } @Override public int compare(Node<?> o1, Node<?> o2) { int result = Float.compare(o1.mbr[axis], o2.mbr[axis]); if (result != 0) { return result; } return Float.compare(o1.mbr[axis + dimension], o2.mbr[axis + dimension]); } } public static abstract class Node<T> implements RNode<float[], T> { Branch<T> parent; final float[] mbr; public Node(float[] mbr) { super(); this.mbr = mbr; } public abstract int level(); @Override public float[] mbr() { return mbr; } } static <T> void addChild(Branch<T> parent, Node<T> node) { if (parent.numChildren > 0 && parent.children[0] instanceof Leaf != node instanceof Leaf) { throw new IllegalArgumentException("Cannot mix leaf and non-leaf nodes in the same branch"); } if (parent.numChildren == parent.children.length) { parent.children = Arrays.copyOf(parent.children, parent.numChildren + 1); } node.parent = parent; parent.children[parent.numChildren++] = node; } static <T> void recalcMbrs(Branch<T> target) { while (target != null) { target.recalcMbr(); target = target.parent; } } static <T> void removeFromParent(Node<T> node) { if (node.parent != null) { int index = -1; for (int i = 0; i < node.parent.numChildren; i++) { if (node.parent.children[i] == node) { index = i; break; } } if (index >= 0) { if (index == node.parent.numChildren - 1) { node.parent.children[index] = null; } else { node.parent.children[index] = node.parent.children[node.parent.numChildren - 1]; } node.parent.numChildren--; node.parent = null; } } } int dimension; Branch<T> root; LowerUpperComparator[] chooseSplitAxisComparators; int maxChildrenPerBranch, minSplitSize, numToReinsert; float[] rt; float[][] rt0; float[][] rt1; int maxLevel = 0; public RfStarTree(int dimension, int maxChildrenPerBranch, int minSplitSize, int numToReinsert) { this.dimension = dimension; this.maxChildrenPerBranch = maxChildrenPerBranch; this.minSplitSize = minSplitSize; this.numToReinsert = numToReinsert; rt = Rectmath.voidRectf(dimension); rt0 = new float[maxChildrenPerBranch - minSplitSize + 2][dimension * 2]; rt1 = new float[maxChildrenPerBranch - minSplitSize + 2][dimension * 2]; chooseSplitAxisComparators = new LowerUpperComparator[dimension]; for (int axis = 0; axis < dimension; axis++) { chooseSplitAxisComparators[axis] = new LowerUpperComparator(axis, dimension); } root = new Branch<T>(dimension, 1, maxChildrenPerBranch); } float area(float[] mbr) { float area = 1f; for (int axis = 0; axis < dimension; axis++) { float span = mbr[axis + dimension] - mbr[axis]; if (span == 0) { span = Math.ulp(mbr[axis]); } area *= span; } return Float.isNaN(area) ? 0f : area; } float centerDistSq(float[] a, float[] b) { float distSq = 0f; for (int i = 0; i < dimension; i++) { float d = (a[i + dimension] + a[i] - b[i + dimension] - b[i]) * 0.5f; distSq += d * d; } return distSq; } int chooseSplitAxis(Branch<T> branch) { float bestMargin = 0f; int bestAxis = 0; for (int axis = 0; axis < dimension; axis++) { Arrays.sort(branch.children, chooseSplitAxisComparators[axis]); float totalMargin = 0f; for (int i = 0; i < minSplitSize - 1; i++) { union(rt, branch.children[i].mbr, rt); } for (int k = minSplitSize - 1; k < maxChildrenPerBranch + 1 - minSplitSize; k++) { union(rt, branch.children[k].mbr, rt); totalMargin += margin(rt); } Arrays.fill(rt, Float.NaN); for (int i = maxChildrenPerBranch; i > maxChildrenPerBranch + 1 - minSplitSize; i--) { union(rt, branch.children[i].mbr, rt); } for (int k = maxChildrenPerBranch + 1 - minSplitSize; k >= minSplitSize; k--) { union(rt, branch.children[k].mbr, rt); totalMargin += margin(rt); } if (axis == 0 || totalMargin < bestMargin) { bestAxis = axis; bestMargin = totalMargin; } } return bestAxis; } int chooseSplitIndex(Branch<T> branch, int axis) { float bestOverlap = 0f; float bestArea = 0f; int bestIndex = 0; Arrays.sort(branch.children, chooseSplitAxisComparators[axis]); Arrays.fill(rt0[0], Float.NaN); Arrays.fill(rt1[0], Float.NaN); for (int index = 1; index <= maxChildrenPerBranch + 1 - minSplitSize; index++) { union(rt0[index - 1], branch.children[index - 1].mbr, rt0[index]); union(rt1[index - 1], branch.children[maxChildrenPerBranch + 1 - index].mbr, rt1[index]); } for (int index = minSplitSize; index <= maxChildrenPerBranch + 1 - minSplitSize; index++) { float overlap = overlap(rt0[index], rt1[maxChildrenPerBranch + 1 - index]); float area = area(rt0[index]) + area(rt1[maxChildrenPerBranch + 1 - index]); if (index == minSplitSize || overlap < bestOverlap || overlap == bestOverlap && area < bestArea) { bestIndex = index; bestOverlap = overlap; bestArea = area; } } return bestIndex; } Branch<T> chooseSubtree(Node<T> toInsert, Branch<T> node) { while (node.level > toInsert.level() + 1) { int bestIndex = 0; if (node.level == 2) { Arrays.sort(node.children, 0, node.numChildren, new EnlargementComparator(toInsert.mbr)); float bestOverlapEnlargement = 0; float bestAreaEnlargement = 0; float bestArea = 0; for (int i = 0; i < Math.min(numToReinsert, node.numChildren); i++) { float[] current = node.children[i].mbr; float[] enlarged = rt0[0]; union(current, toInsert.mbr, enlarged); float overlap = 0; float enlargedOverlap = 0; for (int k = 0; k < node.numChildren; k++) { if (k == i) { continue; } overlap += overlap(current, node.children[k].mbr); enlargedOverlap += overlap(enlarged, node.children[k].mbr); } float overlapEnlargement = enlargedOverlap - overlap; float area = area(current); float areaEnlargement = area(enlarged) - area; if (i == 0 || overlapEnlargement < bestOverlapEnlargement || overlapEnlargement == bestOverlapEnlargement && areaEnlargement < bestAreaEnlargement || overlapEnlargement == bestOverlapEnlargement && areaEnlargement == bestAreaEnlargement && area < bestArea) { bestIndex = i; bestOverlapEnlargement = overlapEnlargement; bestAreaEnlargement = areaEnlargement; bestArea = area; } } } else { float bestAreaEnlargement = 0; float bestArea = 0; for (int i = 0; i < node.numChildren; i++) { float[] current = node.children[i].mbr; float[] enlarged = rt0[0]; union(current, toInsert.mbr, enlarged); float area = area(current); float areaEnlargement = area(enlarged) - area; if (i == 0 || areaEnlargement < bestAreaEnlargement || areaEnlargement == bestAreaEnlargement && area < bestArea) { bestIndex = i; bestAreaEnlargement = areaEnlargement; bestArea = area; } } } if (node.children[bestIndex] instanceof Leaf) { System.out.println("TEST"); } node = (Branch<T>) node.children[bestIndex]; } return node; } @Override public Leaf<T> createLeaf(float[] mbr, T object) { if (mbr.length != dimension * 2) { throw new IllegalArgumentException("mbr.length must equal " + dimension * 2); } return new Leaf<T>(mbr, object); } void doReinsert(Branch<T> overflowed, BitSet reinsertedLevels) { reinsertedLevels.set(overflowed.level - 1); Arrays.sort(overflowed.children, new CenterDistanceComparator(overflowed.mbr)); Node<T>[] pendingReinsertion = new Node[numToReinsert]; System.arraycopy(overflowed.children, 0, pendingReinsertion, 0, numToReinsert); System.arraycopy(overflowed.children, maxChildrenPerBranch + 1 - numToReinsert, overflowed.children, 0, numToReinsert); overflowed.children = Arrays.copyOf(overflowed.children, maxChildrenPerBranch); overflowed.numChildren = maxChildrenPerBranch + 1 - numToReinsert; recalcMbrs(overflowed); for (Node<T> node : pendingReinsertion) { node.parent = null; insert(node, reinsertedLevels); } } void doSplit(Branch<T> overflowed, BitSet reinsertedLevels) { Branch<T> parent = overflowed.parent; removeFromParent(overflowed); Branch<T>[] split = split(overflowed); if (overflowed == root) { maxLevel++; root = new Branch<T>(dimension, split[0].level + 1, maxChildrenPerBranch); addChild(root, split[0]); addChild(root, split[1]); root.recalcMbr(); } else { addChild(parent, split[0]); addChild(parent, split[1]); parent.recalcMbr(); } } float enlargement(float[] r, float[] radded) { float volume = 1f; float result = 1f; for (int i = 0; i < dimension; i++) { result *= nmax(r[i + dimension], radded[i + dimension]) - nmin(r[i], radded[i]); volume *= r[i + dimension] - r[i]; } return Float.isNaN(volume) ? result : result - volume; } @Override public Branch<T> getRoot() { return root; } public void insert(Leaf<T> newLeaf) { if (newLeaf.mbr.length != dimension * 2) { throw new IllegalArgumentException("newLeaf does not match the dimension of this tree"); } if (newLeaf.parent != null) { throw new IllegalArgumentException("newLeaf is already in a tree"); } insert(newLeaf, new BitSet()); } void insert(Node<T> toInsert, BitSet reinsertedLevels) { Branch<T> target = chooseSubtree(toInsert, root); if (target.numChildren < maxChildrenPerBranch) { toInsert.parent = target; target.children[target.numChildren++] = toInsert; recalcMbrs(target); } else { overflowTreatment(toInsert, target, reinsertedLevels); } } float margin(float[] mbr) { float margin = 0f; for (int axis = 0; axis < dimension; axis++) { margin += mbr[axis + dimension] - mbr[axis]; } return Float.isNaN(margin) ? 0f : margin; } void overflowTreatment(Node<T> toInsert, Branch<T> overflowed, BitSet reinsertedLevels) { addChild(overflowed, toInsert); while (overflowed != null && overflowed.numChildren > maxChildrenPerBranch) { if (!reinsertedLevels.get(toInsert.level())) { doReinsert(overflowed, reinsertedLevels); break; } else { Branch<T> nextParent = overflowed.parent; doSplit(overflowed, reinsertedLevels); overflowed = nextParent; } } } float overlap(float[] r1, float[] r2) { float overlap = 1f; for (int axis = 0; axis < dimension; axis++) { float hi1 = r1[axis + dimension]; float hi2 = r2[axis + dimension]; if (hi1 == r1[axis]) { hi1 += Math.ulp(hi1); } if (hi2 == r2[axis]) { hi2 += Math.ulp(hi2); } float span = nmin(hi1, hi2) - nmax(r1[axis], r2[axis]); if (span <= 0) { return 0; } overlap *= span; } return overlap; } Branch<T>[] split(Branch<T> overflowed) { int axis = chooseSplitAxis(overflowed); int index = chooseSplitIndex(overflowed, axis); Branch<T>[] result = new Branch[2]; result[0] = new Branch<T>(dimension, overflowed.level, maxChildrenPerBranch); result[1] = new Branch<T>(dimension, overflowed.level, maxChildrenPerBranch); result[0].numChildren = index; result[1].numChildren = maxChildrenPerBranch + 1 - index; System.arraycopy(overflowed.children, 0, result[0].children, 0, index); for (int i = 0; i < result[0].numChildren; i++) { result[0].children[i].parent = result[0]; } System.arraycopy(overflowed.children, index, result[1].children, 0, maxChildrenPerBranch + 1 - index); for (int i = 0; i < result[1].numChildren; i++) { result[1].children[i].parent = result[1]; } Arrays.fill(overflowed.children, null); overflowed.numChildren = 0; result[0].recalcMbr(); result[1].recalcMbr(); return result; } }