package wblut.geom; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import wblut.WB_Epsilon; /** * Class WB_KDTree. * * @param <T> * generic type * @param <V> * value type */ public class WB_KDTree<T extends WB_Point3d, V> { /** dim. */ private final int _dim; /** maximum bin size. */ private final int _maximumBinSize; /** root. */ private final WB_KDNode root; public WB_KDTree() { this._dim = 3; this._maximumBinSize = 32; this.root = new WB_KDNode(); } public WB_KDTree(final int binsize) { this._dim = 3; this._maximumBinSize = binsize; this.root = new WB_KDNode(); } public List<WB_AABB<T>> getLeafBounds() { List<WB_AABB<T>> leafs = new ArrayList(); root.addLeafBounds(leafs); return leafs; } public List<WB_AABB<T>> getAllBounds() { List<WB_AABB<T>> all = new ArrayList(); root.addBox(all, 0); return all; } /** * Get the leaf regions. * * @return leaf regions */ public List<WB_AABB<T>> getLeafRegions() { List<WB_AABB<T>> leafs = new ArrayList(); root.addLeafRegion(leafs); return leafs; } /** * Get all regions. * * @return all regions */ public List<WB_AABB<T>> getAllRegions() { List<WB_AABB<T>> all = new ArrayList(); root.addRegion(all, 0); return all; } public V add(final T coord, final V val) { return (V) root.add(new WB_KDEntry(coord, val, -1)); } public WB_KDEntry<T, V>[] getRange(final WB_AABB<T> aabb) { return root.range(aabb); } public WB_KDEntry<T, V>[] getRange(final T center, final double radius) { double r2 = radius * radius; return root.range(center, r2); } public WB_KDEntry<T, V>[] getRange(final T center, final double lower, final double upper) { double lower2 = lower * lower; double upper2 = upper * upper; return root.range(center, lower2, upper2); } public WB_KDEntry<T, V>[] getNearestNeighbors(final T coord, final int num) { QueryResult<T, V> heap = new QueryResult<T, V>(num); root.findNearest(heap, coord); return heap.entries; } public WB_KDEntry<T, V> getNearestNeighbor(final T coord) { QueryResult<T, V> heap = new QueryResult<T, V>(1); root.findNearest(heap, coord); return heap.entries[0]; } public static class WB_KDEntry<T extends WB_Point3d, V> { public T coord; public V value; public double d2; public WB_KDEntry(final T coord, final V value, final double d2) { this.coord = coord; this.value = value; this.d2 = d2; } } private class WB_KDNode<T extends WB_Point3d, V> { private WB_AABB<T> _limits; private WB_KDNode _negative, _positive; private final WB_AABB<T> _region; private WB_KDEntry<T, V>[] _bin; private boolean _isLeaf; private int _binSize; private int _discriminator; private double _sliceValue; private int _id; private WB_KDNode() { _bin = new WB_KDEntry[_maximumBinSize]; _negative = _positive = null; _limits = null; _isLeaf = true; _binSize = 0; double[] min = new double[_dim]; Arrays.fill(min, Double.NEGATIVE_INFINITY); double[] max = new double[_dim]; Arrays.fill(max, Double.POSITIVE_INFINITY); _region = new WB_AABB(min, max); _id = 0; } private void addLeafBounds(final List<WB_AABB<T>> leafs) { if (_isLeaf) { WB_AABB<T> box = _limits.get(); box.setId(_id); leafs.add(box); } else { _positive.addLeafBounds(leafs); _negative.addLeafBounds(leafs); } } private void addLeafRegion(final List<WB_AABB<T>> leafs) { if (_isLeaf) { WB_AABB<T> box = _region.get(); box.setId(_id); leafs.add(box); } else { _positive.addLeafRegion(leafs); _negative.addLeafRegion(leafs); } } private void addBox(final List<WB_AABB<T>> leafs, final int level) { WB_AABB<T> box = _limits.get(); box.setId(_id); box.setData("level", new Integer(level)); leafs.add(box); if (!_isLeaf) { _positive.addBox(leafs, level + 1); _negative.addBox(leafs, level + 1); } } private void addRegion(final List<WB_AABB<T>> leafs, final int level) { WB_AABB<T> box = _region.get(); box.setId(_id); box.setData("level", new Integer(level)); leafs.add(box); if (!_isLeaf) { _positive.addRegion(leafs, level + 1); _negative.addRegion(leafs, level + 1); } } private V add(final WB_KDEntry<T, V> entry) { if (_isLeaf) { return addInLeaf(entry); } else { extendBounds(entry.coord); if (entry.coord.get(_discriminator) > _sliceValue) { return (V) _positive.add(entry); } else { return (V) _negative.add(entry); } } } private V addInLeaf(final WB_KDEntry<T, V> entry) { V lookup = lookup(entry.coord); if (lookup == null) { extendBounds(entry.coord); if (_binSize + 1 > _maximumBinSize) { addLevel(); add(entry); return null; } _bin[_binSize] = entry; _binSize++; return null; } return lookup; } private V lookup(final T point) { for (int i = 0; i < _binSize; i++) { if (WB_Epsilon.isZeroSq(WB_Distance.sqDistance(point, _bin[i].coord))) { return _bin[i].value; } } return null; } private void findNearest(final QueryResult<T, V> heap, final T data) { if (_binSize == 0) { return; } if (_isLeaf) { for (int i = 0; i < _binSize; i++) { double dist = WB_Distance.sqDistance(_bin[i].coord, data); heap.tryToAdd(dist, _bin[i]); } } else { if (data.get(_discriminator) > _sliceValue) { _positive.findNearest(heap, data); if (_negative._binSize == 0) { return; } if ((heap.size < heap.capacity) || _negative._limits.getDistanceSquare(data) < heap .getDistanceSquare(heap.size - 1)) { _negative.findNearest(heap, data); } } else { _negative.findNearest(heap, data); if (_positive._binSize == 0) { return; } if ((heap.size < heap.capacity) || _positive._limits.getDistanceSquare(data) < heap .getDistanceSquare(heap.size - 1)) { _positive.findNearest(heap, data); } } } } private WB_KDEntry<T, V>[] range(final WB_AABB<T> range) { if (_bin == null) { WB_KDEntry<T, V>[] tmp = new WB_KDEntry[0]; if (_negative._limits.intersects(range)) { WB_KDEntry<T, V>[] tmpl = _negative.range(range); if (0 == tmp.length) { tmp = tmpl; } } if (_positive._limits.intersects(range)) { WB_KDEntry<T, V>[] tmpr = _positive.range(range); if (0 == tmp.length) { tmp = tmpr; } else if (0 < tmpr.length) { WB_KDEntry<T, V>[] tmp2 = new WB_KDEntry[tmp.length + tmpr.length]; System.arraycopy(tmp, 0, tmp2, 0, tmp.length); System.arraycopy(tmpr, 0, tmp2, tmp.length, tmpr.length); tmp = tmp2; } } return tmp; } WB_KDEntry<T, V>[] tmp = new WB_KDEntry[_binSize]; int n = 0; for (int i = 0; i < _binSize; i++) { if (range.contains(_bin[i].coord)) { tmp[n++] = _bin[i]; } } WB_KDEntry<T, V>[] tmp2 = new WB_KDEntry[n]; System.arraycopy(tmp, 0, tmp2, 0, n); return tmp2; } private WB_KDEntry<T, V>[] range(final T center, final double r2) { if (_bin == null) { WB_KDEntry<T, V>[] tmp = new WB_KDEntry[0]; if (_negative._limits.getDistanceSquare(center) <= r2) { WB_KDEntry<T, V>[] tmpl = _negative.range(center, r2); if (tmp.length == 0) { tmp = tmpl; } } if (_positive._limits.getDistanceSquare(center) <= r2) { WB_KDEntry<T, V>[] tmpr = _positive.range(center, r2); if (tmp.length == 0) { tmp = tmpr; } else if (0 < tmpr.length) { WB_KDEntry<T, V>[] tmp2 = new WB_KDEntry[tmp.length + tmpr.length]; System.arraycopy(tmp, 0, tmp2, 0, tmp.length); System.arraycopy(tmpr, 0, tmp2, tmp.length, tmpr.length); tmp = tmp2; } } return tmp; } WB_KDEntry<T, V>[] tmp = new WB_KDEntry[_binSize]; int n = 0; for (int i = 0; i < _binSize; i++) { double d2 = WB_Distance.sqDistance(center, _bin[i].coord); if (d2 <= r2) { _bin[i].d2 = d2; tmp[n++] = _bin[i]; } } WB_KDEntry<T, V>[] tmp2 = new WB_KDEntry[n]; System.arraycopy(tmp, 0, tmp2, 0, n); return tmp2; } private WB_KDEntry<T, V>[] range(final T center, final double lower2, final double upper2) { if (_bin == null) { WB_KDEntry<T, V>[] tmp = new WB_KDEntry[0]; if (_negative._limits.getDistanceSquare(center) <= upper2) { WB_KDEntry<T, V>[] tmpl = _negative.range(center, lower2, upper2); if (tmp.length == 0) { tmp = tmpl; } } if (_positive._limits.getDistanceSquare(center) <= upper2) { WB_KDEntry<T, V>[] tmpr = _positive.range(center, lower2, upper2); if (tmp.length == 0) { tmp = tmpr; } else if (0 < tmpr.length) { WB_KDEntry<T, V>[] tmp2 = new WB_KDEntry[tmp.length + tmpr.length]; System.arraycopy(tmp, 0, tmp2, 0, tmp.length); System.arraycopy(tmpr, 0, tmp2, tmp.length, tmpr.length); tmp = tmp2; } } return tmp; } WB_KDEntry<T, V>[] tmp = new WB_KDEntry[_binSize]; int n = 0; for (int i = 0; i < _binSize; i++) { double d2 = WB_Distance.sqDistance(center, _bin[i].coord); if ((d2 <= upper2) && (d2 >= lower2)) { _bin[i].d2 = d2; tmp[n++] = _bin[i]; } } WB_KDEntry<T, V>[] tmp2 = new WB_KDEntry[n]; System.arraycopy(tmp, 0, tmp2, 0, n); return tmp2; } /** * Adds the level. */ private void addLevel() { _discriminator = _limits.maxOrdinate(); _negative = new WB_KDNode(); _positive = new WB_KDNode(); _negative._id = 2 * _id; _positive._id = 2 * _id + 1; _sliceValue = (_limits.getMax(_discriminator) + _limits .getMin(_discriminator)) * 0.5; for (int i = 0; i < _dim; i++) { _negative._region._min[i] = _region._min[i]; _positive._region._max[i] = _region._max[i]; if (i == _discriminator) { _negative._region._max[i] = _sliceValue; _positive._region._min[i] = _sliceValue; } else { _negative._region._max[i] = _region._max[i]; _positive._region._min[i] = _region._min[i]; } } for (int i = 0; i < _binSize; ++i) { if (_bin[i].coord.get(_discriminator) > _sliceValue) { _positive.addInLeaf(_bin[i]); } else { _negative.addInLeaf(_bin[i]); } } _bin = null; _isLeaf = false; } private void extendBounds(final T coord) { if (_limits == null) { _limits = new WB_AABB<T>(coord); } else { _limits.expandToInclude(coord); } } } public class QueryResult<T extends WB_Point3d, V> { /** The entries. */ private final WB_KDEntry<T, V>[] entries; /** The dist sqs. */ private final double[] distSqs; /** The capacity. */ private final int capacity; /** The size. */ private int size; protected QueryResult(final int capacity) { this.entries = new WB_KDEntry[capacity]; this.distSqs = new double[capacity]; this.capacity = capacity; this.size = 0; } protected void tryToAdd(final double dist, final WB_KDEntry<T, V> entry) { int i = size; for (; i > 0 && distSqs[i - 1] > dist; --i) { ; } if (i >= capacity) { return; } if (size < capacity) { size++; } int j = i + 1; System.arraycopy(distSqs, i, distSqs, j, size - j); distSqs[i] = dist; System.arraycopy(entries, i, entries, j, size - j); entry.d2 = dist; entries[i] = entry; } /** * Gets the entry. * * @param i * the i * @return the entry */ public WB_KDEntry<T, V> getEntry(final int i) { if (size == 0) { return null; } return entries[i]; } /** * Gets the distance square. * * @param i * the i * @return the distance square */ public double getDistanceSquare(final int i) { if (size == 0) { return Double.NaN; } return distSqs[i]; } /** * Size. * * @return the int */ public int size() { return size; } } }