package com.revolsys.geometry.index.rtree; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; import com.revolsys.collection.ArrayUtil; import com.revolsys.geometry.model.BoundingBox; public class RTreeBranch<T> extends RTreeNode<T> { private RTreeNode<T>[] nodes; private int size; public RTreeBranch() { } @SuppressWarnings("unchecked") public RTreeBranch(final int size) { this.nodes = ArrayUtil.newArray(RTreeNode.class, size); } protected RTreeBranch(final int size, final List<RTreeNode<T>> nodes) { this(size); for (final RTreeNode<T> node : nodes) { add(node); } } private void add(final RTreeNode<T> node) { this.nodes[this.size] = node; this.size++; expandBoundingBox(node); } @Override protected RTreeLeaf<T> chooseLeaf(final List<RTreeBranch<T>> path, final BoundingBox boundingBox) { expandBoundingBox(boundingBox); path.add(this); double minExpansion = Float.MAX_VALUE; RTreeNode<T> next = null; final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; final double expansion = childNode.getRequiredExpansion(boundingBox); if (expansion < minExpansion) { minExpansion = expansion; next = childNode; } else if (expansion == minExpansion) { final double childArea = childNode.getArea(); final double minArea = next.getArea(); if (childArea < minArea) { next = childNode; } } } return next.chooseLeaf(path, boundingBox); } @Override protected void expandBoundingBox(final double... bounds) { super.expandBoundingBox(bounds); } @Override protected void expandBoundingBox(final double minX, final double minY, final double maxX, final double maxY) { super.expandBoundingBox(minX, minY, maxX, maxY); } @Override public void forEach(final double x, final double y, final Consumer<? super T> action) { final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; if (childNode.intersectsBoundingBox(x, y)) { childNode.forEach(x, y, action); } } } @Override public void forEach(final double minX, final double minY, final double maxX, final double maxY, final Consumer<? super T> action) { final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; if (childNode.intersectsBoundingBox(minX, minY, maxX, maxY)) { childNode.forEach(minX, minY, maxX, maxY, action); } } } @Override public void forEach(final double minX, final double minY, final double maxX, final double maxY, final Predicate<? super T> filter, final Consumer<? super T> action) { final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; if (childNode.intersectsBoundingBox(minX, minY, maxX, maxY)) { childNode.forEach(minX, minY, maxX, maxY, filter, action); } } } @Override public void forEachValue(final Consumer<? super T> action) { final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; childNode.forEachValue(action); } } @Override public void forEachValue(final Predicate<? super T> filter, final Consumer<? super T> action) { final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; childNode.forEachValue(filter, action); } } public int getSize() { return this.size; } @Override public boolean remove(final LinkedList<RTreeNode<T>> path, final double minX, final double minY, final double maxX, final double maxY, final T object) { final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; if (childNode.covers(minX, minY, maxX, maxY)) { if (childNode.remove(path, minX, minY, maxX, maxY, object)) { if (path != null) { path.addFirst(this); } updateEnvelope(); return true; } } } return false; } public void replace(final RTreeNode<T> node, final List<RTreeNode<T>> newNodes) { for (int i = 1; i < newNodes.size(); i++) { final RTreeNode<T> newNode = newNodes.get(i); add(newNode); } final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; if (childNode == node) { childNodes[i] = newNodes.get(0); return; } } } public List<RTreeNode<T>> split(final RTreeNode<T> node, final List<RTreeNode<T>> newNodes) { final RTreeBranch<T> branch1 = new RTreeBranch<>(this.nodes.length); final RTreeBranch<T> branch2 = new RTreeBranch<>(this.nodes.length); // TODO Add some ordering to the results final int midPoint = (int)Math.ceil(this.size / 2.0); for (int i = 0; i <= midPoint; i++) { final RTreeNode<T> childNode = this.nodes[i]; if (childNode == node) { branch1.add(newNodes.get(0)); } else { branch1.add(childNode); } } for (int i = midPoint + 1; i < this.size; i++) { final RTreeNode<T> childNode = this.nodes[i]; if (childNode == node) { branch1.add(newNodes.get(0)); } else { branch2.add(childNode); } } final RTreeNode<T> newNode = newNodes.get(1); branch2.add(newNode); return Arrays.<RTreeNode<T>> asList(branch1, branch2); } @Override protected void updateEnvelope() { double minX = Double.MAX_VALUE; double maxX = -Double.MAX_VALUE; double minY = Double.MAX_VALUE; double maxY = -Double.MAX_VALUE; final int childCount = this.size; final RTreeNode<T>[] childNodes = this.nodes; for (int i = 0; i < childCount; i++) { final RTreeNode<T> childNode = childNodes[i]; final double nodeMinX = childNode.getMinX(); if (nodeMinX < minX) { minX = nodeMinX; } final double nodeMinY = childNode.getMinY(); if (nodeMinY < minY) { minY = nodeMinY; } final double nodeMaxX = childNode.getMaxX(); if (nodeMaxX > maxX) { maxX = nodeMaxX; } final double nodeMaxY = childNode.getMaxY(); if (nodeMaxY > maxY) { maxY = nodeMaxY; } } setBoundingBox(minX, minY, maxX, maxY); } }