AbstractBSPTree.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.geometry.core.partitioning.bsp;

  18. import java.util.Deque;
  19. import java.util.Iterator;
  20. import java.util.LinkedList;
  21. import java.util.NoSuchElementException;

  22. import org.apache.commons.geometry.core.Point;
  23. import org.apache.commons.geometry.core.Transform;
  24. import org.apache.commons.geometry.core.partitioning.Hyperplane;
  25. import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
  26. import org.apache.commons.geometry.core.partitioning.HyperplaneLocation;
  27. import org.apache.commons.geometry.core.partitioning.Split;
  28. import org.apache.commons.geometry.core.partitioning.SplitLocation;

  29. /** Abstract class for Binary Space Partitioning (BSP) tree implementations. This
  30.  * class contains basic structures and algorithms that should be common
  31.  * to all BSP tree implementations, regardless of the end use cases for the tree
  32.  * (eg, whether the tree is intended to represent polytopes, hold attributes like
  33.  * a map, etc).
  34.  *
  35.  * <h2>Implementation Notes</h2>
  36.  * <ul>
  37.  *      <li>The {@link AbstractBSPTree} class is designed to work closely with its nested node type,
  38.  *      {@link AbstractNode}. The tree class handles all tree manipulation operations while the node type
  39.  *      is primarily a simple data type and delegates tree operations to internal methods within its
  40.  *      parent tree. This allows for easier overriding of tree behavior in subclasses.</li>
  41.  *      <li>Each time the structure of the tree is modified, a {@link #getVersion() version number} is
  42.  *      incremented in the tree. This allows certain tree properties to be computed lazily and then
  43.  *      cached. The tree version number is incremented with the {@link #invalidate() invalidate} method. Properties
  44.  *      can be cached directly on nodes using the {@link AbstractBSPTree.AbstractNode#checkValid() checkValid}
  45.  *      and {@link AbstractBSPTree.AbstractNode#nodeInvalidated() nodeInvalidated} methods.</li>
  46.  *      <li>Since the methods used to construct and modify trees can vary by use case, no public API is provided
  47.  *      for manipulating the tree. Subclasses are expected to use the protected methods of this class to
  48.  *      create their own. For tree construction, subclasses are expected to pass their own {@link SubtreeInitializer}
  49.  *      instances to {@link #insert(HyperplaneConvexSubset, SubtreeInitializer) insert} and
  50.  *      {@link #cutNode(AbstractNode, Hyperplane, SubtreeInitializer) cutNode} in order to set the correct properties on
  51.  *      tree nodes. To support tree copying, subclasses must also override
  52.  *      {@link #copyNodeProperties(AbstractNode, AbstractNode) copyNodeProperties}.</li>
  53.  *      <li>This class is not thread safe.</li>
  54.  * </ul>
  55.  *
  56.  * @param <P> Point implementation type
  57.  * @param <N> BSP tree node implementation type
  58.  * @see BSPTree
  59.  */
  60. public abstract class AbstractBSPTree<P extends Point<P>, N extends AbstractBSPTree.AbstractNode<P, N>>
  61.     implements BSPTree<P, N> {

  62.     /** Interface used to initialize newly created BSP subtrees, consisting of a single parent
  63.      * node and two child nodes.
  64.      * @param <N> BSP tree node implementation type
  65.      */
  66.     @FunctionalInterface
  67.     public interface SubtreeInitializer<N extends AbstractBSPTree.AbstractNode<?, ?>> {

  68.         /** Initialize the given newly-created subtree. The subtree consists of a single root node and two
  69.          * child nodes.
  70.          * @param root the root node of the new subtree
  71.          */
  72.         void initSubtree(N root);
  73.     }

  74.     /** The default number of levels to print when creating a string representation of the tree. */
  75.     private static final int DEFAULT_TREE_STRING_MAX_DEPTH = 8;

  76.     /** Integer value set on various node fields when a value is unknown. */
  77.     private static final int UNKNOWN_VALUE = -1;

  78.     /** The root node for the tree. */
  79.     private N root;

  80.     /** The current modification version for the tree structure. This is incremented each time
  81.      * a structural change occurs in the tree and is used to determine when cached values
  82.      * must be recomputed.
  83.      */
  84.     private int version;

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public N getRoot() {
  88.         if (root == null) {
  89.             setRoot(createNode());
  90.         }
  91.         return root;
  92.     }

  93.     /** Set the root node for the tree. Cached tree properties are invalidated
  94.      * with {@link #invalidate()}.
  95.      * @param root new root node for the tree
  96.      */
  97.     protected void setRoot(final N root) {
  98.         this.root = root;

  99.         this.root.makeRoot();

  100.         invalidate();
  101.     }

  102.     /** {@inheritDoc} */
  103.     @Override
  104.     public int count() {
  105.         return getRoot().count();
  106.     }

  107.     /** {@inheritDoc} */
  108.     @Override
  109.     public int height() {
  110.         return getRoot().height();
  111.     }

  112.     /** {@inheritDoc} */
  113.     @Override
  114.     public void accept(final BSPTreeVisitor<P, N> visitor) {
  115.         accept(getRoot(), visitor);
  116.     }

  117.     /** {@inheritDoc} */
  118.     @Override
  119.     public N findNode(final P pt, final FindNodeCutRule cutBehavior) {
  120.         return findNode(getRoot(), pt, cutBehavior);
  121.     }

  122.     /** {@inheritDoc} */
  123.     @Override
  124.     public Iterable<N> nodes() {
  125.         return () -> new NodeIterator<>(getRoot());
  126.     }

  127.     /** {@inheritDoc} */
  128.     @Override
  129.     public void copy(final BSPTree<P, N> src) {
  130.         copySubtree(src.getRoot(), getRoot());
  131.     }

  132.     /** {@inheritDoc} */
  133.     @Override
  134.     public void extract(final N node) {
  135.         // copy downward
  136.         final N extracted = importSubtree(node);

  137.         // extract upward
  138.         final N newRoot = extractParentPath(node, extracted);

  139.         // set the root of this tree
  140.         setRoot(newRoot);
  141.     }

  142.     /** {@inheritDoc} */
  143.     @Override
  144.     public void transform(final Transform<P> transform) {
  145.         final boolean swapChildren = swapsInsideOutside(transform);
  146.         transformRecursive(getRoot(), transform, swapChildren);

  147.         invalidate();
  148.     }

  149.     /** Get a simple string representation of the tree structure. The returned string contains
  150.      * the tree structure down to the default max depth of {@value #DEFAULT_TREE_STRING_MAX_DEPTH}.
  151.      * @return a string representation of the tree
  152.      */
  153.     public String treeString() {
  154.         return treeString(DEFAULT_TREE_STRING_MAX_DEPTH);
  155.     }

  156.     /** Get a simple string representation of the tree structure. The returned string contains
  157.      * the tree structure down to {@code maxDepth}.
  158.      * @param maxDepth the maximum depth in the tree to print; nodes below this depth are skipped
  159.      * @return a string representation of the tree
  160.      */
  161.     public String treeString(final int maxDepth) {
  162.         final BSPTreePrinter<P, N> printer = new BSPTreePrinter<>(maxDepth);
  163.         accept(printer);

  164.         return printer.toString();
  165.     }

  166.     /** {@inheritDoc} */
  167.     @Override
  168.     public String toString() {
  169.         return new StringBuilder()
  170.                 .append(getClass().getSimpleName())
  171.                 .append("[count= ")
  172.                 .append(count())
  173.                 .append(", height= ")
  174.                 .append(height())
  175.                 .append(']')
  176.                 .toString();
  177.     }

  178.     /** Create a new node for this tree.
  179.      * @return a new node for this tree
  180.      */
  181.     protected abstract N createNode();

  182.     /** Copy non-structural node properties from {@code src} to {@code dst}.
  183.      * Non-structural properties are those properties not directly related
  184.      * to the structure of the BSP tree, i.e. properties other than parent/child
  185.      * connections and cuts. Subclasses should override this method to copy additional
  186.      * properties stored on nodes.
  187.      * @param src source node
  188.      * @param dst destination node
  189.      */
  190.     protected abstract void copyNodeProperties(N src, N dst);

  191.     /** Create a non-structural copy of the given node. Properties such as parent/child
  192.      * connections and cuts are <em>not</em> copied.
  193.      * @param src the node to copy; does not need to belong to the current tree
  194.      * @return the copied node
  195.      * @see AbstractBSPTree#copyNodeProperties(AbstractNode, AbstractNode)
  196.      */
  197.     protected N copyNode(final N src) {
  198.         final N copy = createNode();
  199.         copyNodeProperties(src, copy);

  200.         return copy;
  201.     }

  202.     /** Recursively copy a subtree. The returned node is not attached to the current tree.
  203.      * Structural <em>and</em> non-structural properties are copied from the source subtree
  204.      * to the destination subtree. This method does nothing if {@code src} and {@code dst}
  205.      * reference the same node.
  206.      * @param src the node representing the source subtree; does not need to belong to the
  207.      *      current tree
  208.      * @param dst the node representing the destination subtree
  209.      * @return the copied node, ie {@code dst}
  210.      */
  211.     protected N copySubtree(final N src, final N dst) {
  212.         // only copy if we're actually switching nodes
  213.         if (src != dst) {
  214.             // copy non-structural properties
  215.             copyNodeProperties(src, dst);

  216.             // copy the subtree structure
  217.             HyperplaneConvexSubset<P> cut = null;
  218.             N minus = null;
  219.             N plus = null;

  220.             if (!src.isLeaf()) {
  221.                 final AbstractBSPTree<P, N> dstTree = dst.getTree();

  222.                 cut = src.getCut();
  223.                 minus = copySubtree(src.getMinus(), dstTree.createNode());
  224.                 plus = copySubtree(src.getPlus(), dstTree.createNode());
  225.             }

  226.             dst.setSubtree(cut, minus, plus);
  227.         }

  228.         return dst;
  229.     }

  230.     /** Import the subtree represented by the given node into this tree. If the given node
  231.      * already belongs to this tree, then the node is returned directly without modification.
  232.      * If the node does <em>not</em> belong to this tree, a new node is created and the src node
  233.      * subtree is copied into it.
  234.      *
  235.      * <p>This method does not modify the current structure of the tree.</p>
  236.      * @param src node to import
  237.      * @return the given node if it belongs to this tree, otherwise a new node containing
  238.      *      a copy of the given node's subtree
  239.      * @see #copySubtree(AbstractNode, AbstractNode)
  240.      */
  241.     protected N importSubtree(final N src) {
  242.         // create a copy of the node if it's not already in this tree
  243.         if (src.getTree() != this) {
  244.             return copySubtree(src, createNode());
  245.         }

  246.         return src;
  247.     }

  248.     /** Extract the path from {@code src} to the root of its tree and
  249.      * set it as the parent path of {@code dst}. Leaf nodes created during
  250.      * the extraction are given the same node properties as their counterparts
  251.      * in the source tree but without the cuts and child nodes. The properties
  252.      * of {@code dst} are not modified, with the exception of its parent node
  253.      * reference.
  254.      * @param src the source node to copy the parent path from
  255.      * @param dst the destination node to place under the extracted path
  256.      * @return the root node of the extracted path
  257.      */
  258.     protected N extractParentPath(final N src, final N dst) {
  259.         N dstParent = dst;
  260.         N dstChild;

  261.         N srcChild = src;
  262.         N srcParent = srcChild.getParent();

  263.         while (srcParent != null) {
  264.             dstChild = dstParent;
  265.             dstParent = copyNode(srcParent);

  266.             if (srcChild.isMinus()) {
  267.                 dstParent.setSubtree(
  268.                         srcParent.getCut(),
  269.                         dstChild,
  270.                         copyNode(srcParent.getPlus()));
  271.             } else {
  272.                 dstParent.setSubtree(
  273.                         srcParent.getCut(),
  274.                         copyNode(srcParent.getMinus()),
  275.                         dstChild);
  276.             }

  277.             srcChild = srcParent;
  278.             srcParent = srcChild.getParent();
  279.         }

  280.         return dstParent;
  281.     }

  282.     /** Find the smallest node in the tree containing the point, starting
  283.      * at the given node.
  284.      * @param start the node to begin the search with
  285.      * @param pt the point to check
  286.      * @param cutRule value determining the search behavior when the test point
  287.      *      lies directly on the cut of an internal node
  288.      * @return the smallest node in the tree containing the point
  289.      */
  290.     protected N findNode(final N start, final P pt, final FindNodeCutRule cutRule) {
  291.         final Hyperplane<P> cutHyper = start.getCutHyperplane();
  292.         if (cutHyper != null) {
  293.             final HyperplaneLocation cutLoc = cutHyper.classify(pt);

  294.             final boolean onPlusSide = cutLoc == HyperplaneLocation.PLUS;
  295.             final boolean onMinusSide = cutLoc == HyperplaneLocation.MINUS;
  296.             final boolean onCut = !onPlusSide && !onMinusSide;

  297.             if (onMinusSide || (onCut && cutRule == FindNodeCutRule.MINUS)) {
  298.                 return findNode(start.getMinus(), pt, cutRule);
  299.             } else if (onPlusSide || cutRule == FindNodeCutRule.PLUS) {
  300.                 return findNode(start.getPlus(), pt, cutRule);
  301.             }
  302.         }
  303.         return start;
  304.     }

  305.     /** Visit the nodes in a subtree.
  306.      * @param node the node to begin the visit process
  307.      * @param visitor the visitor to pass nodes to
  308.      */
  309.     protected void accept(final N node, final BSPTreeVisitor<P, N> visitor) {
  310.         acceptRecursive(node, visitor);
  311.     }

  312.     /** Recursively visit the nodes in the subtree rooted at the given node.
  313.      * @param node the node located at the root of the subtree to visit
  314.      * @param visitor the visitor to pass nodes to
  315.      * @return true if the visit operation should continue
  316.      */
  317.     private boolean acceptRecursive(final N node, final BSPTreeVisitor<P, N> visitor) {
  318.         if (node.isLeaf()) {
  319.             return shouldContinueVisit(visitor.visit(node));
  320.         } else {
  321.             final BSPTreeVisitor.Order order = visitor.visitOrder(node);

  322.             if (order != null) {

  323.                 switch (order) {
  324.                 case PLUS_MINUS_NODE:
  325.                     return acceptRecursive(node.getPlus(), visitor) &&
  326.                             acceptRecursive(node.getMinus(), visitor) &&
  327.                             shouldContinueVisit(visitor.visit(node));
  328.                 case PLUS_NODE_MINUS:
  329.                     return acceptRecursive(node.getPlus(), visitor) &&
  330.                             shouldContinueVisit(visitor.visit(node)) &&
  331.                             acceptRecursive(node.getMinus(), visitor);
  332.                 case MINUS_PLUS_NODE:
  333.                     return acceptRecursive(node.getMinus(), visitor) &&
  334.                             acceptRecursive(node.getPlus(), visitor) &&
  335.                             shouldContinueVisit(visitor.visit(node));
  336.                 case MINUS_NODE_PLUS:
  337.                     return acceptRecursive(node.getMinus(), visitor) &&
  338.                             shouldContinueVisit(visitor.visit(node)) &&
  339.                             acceptRecursive(node.getPlus(), visitor);
  340.                 case NODE_PLUS_MINUS:
  341.                     return shouldContinueVisit(visitor.visit(node)) &&
  342.                             acceptRecursive(node.getPlus(), visitor) &&
  343.                             acceptRecursive(node.getMinus(), visitor);
  344.                 case  NODE_MINUS_PLUS:
  345.                     return shouldContinueVisit(visitor.visit(node)) &&
  346.                             acceptRecursive(node.getMinus(), visitor) &&
  347.                             acceptRecursive(node.getPlus(), visitor);
  348.                 default: // NONE
  349.                     break;
  350.                 }
  351.             }

  352.             return true;
  353.         }
  354.     }

  355.     /** Return true if the given BSP tree visit result indicates that the current visit
  356.      * operation should continue.
  357.      * @param result visit result from BSP tree node visit operation
  358.      * @return true if the visit operation should continue with remaining nodes in the
  359.      *      BSP tree
  360.      */
  361.     private boolean shouldContinueVisit(final BSPTreeVisitor.Result result) {
  362.         return result == BSPTreeVisitor.Result.CONTINUE;
  363.     }

  364.     /** Cut a node with a hyperplane. The algorithm proceeds as follows:
  365.      * <ol>
  366.      *      <li>The hyperplane is trimmed by splitting it with each cut hyperplane on the
  367.      *      path from the given node to the root of the tree.</li>
  368.      *      <li>If the remaining portion of the hyperplane is <em>not</em> empty, then
  369.      *          <ul>
  370.      *              <li>the remaining portion becomes the cut hyperplane subset for the node,</li>
  371.      *              <li>two new child nodes are created and initialized with
  372.      *              {@code subtreeInitializer}, and</li>
  373.      *              <li>true is returned.</li>
  374.      *          </ul>
  375.      *      </li>
  376.      *      <li>If the remaining portion of the hyperplane <em>is</em> empty (ie, the
  377.      *      cutting hyperplane does not intersect the node's region), then
  378.      *          <ul>
  379.      *              <li>the node is converted to a leaf node (meaning that previous
  380.      *              child nodes are lost), and</li>
  381.      *              <li>false is returned.</li>
  382.      *          </ul>
  383.      *      </li>
  384.      * </ol>
  385.      *
  386.      * <p>It is important to note that since this method uses the path from given node
  387.      * to the tree root, it must only be used on nodes that are already inserted into
  388.      * the tree.</p>
  389.      *
  390.      * <p>This method calls {@link #invalidate()} to invalidate cached tree properties if the tree
  391.      * structure is changed.</p>
  392.      *
  393.      * @param node the node to cut
  394.      * @param cutter the hyperplane to cut the node with
  395.      * @param subtreeInitializer object used to initialize any newly-created subtrees
  396.      * @return true if the node was cut and two new child nodes were created;
  397.      *      otherwise false
  398.      * @see #trimToNode(AbstractNode, HyperplaneConvexSubset)
  399.      * @see #setNodeCut(AbstractNode, HyperplaneConvexSubset, SubtreeInitializer)
  400.      * @see #removeNodeCut(AbstractNode)
  401.      * @see #invalidate()
  402.      */
  403.     protected boolean cutNode(final N node, final Hyperplane<P> cutter,
  404.             final SubtreeInitializer<N> subtreeInitializer) {

  405.         // cut the hyperplane using all hyperplanes from this node up
  406.         // to the root
  407.         final HyperplaneConvexSubset<P> cut = trimToNode(node, cutter.span());
  408.         if (cut == null || cut.isEmpty()) {
  409.             // insertion failed; the node was not cut
  410.             removeNodeCut(node);
  411.             return false;
  412.         }

  413.         setNodeCut(node, cut, subtreeInitializer);
  414.         return true;
  415.     }

  416.     /** Trim the given hyperplane convex subset to the region defined by the given node. This method
  417.      * cuts the subset with the cut hyperplanes (binary partitioners) of all parent nodes up to the
  418.      * root and returns the trimmed subset or {@code null} if the subset lies outside of the region
  419.      * defined by the node.
  420.      *
  421.      * <p>If the subset is directly coincident with a binary partitioner of a parent node,
  422.      * then the relative orientations of the associated hyperplanes are used to determine the behavior,
  423.      * as described below.
  424.      * <ul>
  425.      *      <li>If the orientations are <strong>similar</strong>, then the subset is determined to
  426.      *      lie <em>outside</em> of the node's region and {@code null} is returned.</li>
  427.      *      <li>If the orientations are <strong>different</strong> (ie, opposite), then the subset
  428.      *      is determined to lie <em>inside</em> of the node's region and the fit operation continues
  429.      *      with the remaining parent nodes.</li>
  430.      * </ul>
  431.      * These rules are designed to allow the creation of trees with node regions that are the thickness
  432.      * of a single hyperplane. For example, in two dimensions, a tree could be constructed with an internal
  433.      * node containing a cut along the x-axis in the positive direction and with a child node containing a
  434.      * cut along the x-axis in the opposite direction. If the nodes in the tree are given inside and outside
  435.      * attributes, then this tree could be used to represent a region consisting of a single line or a region
  436.      * consisting of the entire space except for the single line. This would not be possible if nodes were not
  437.      * able to have cut hyperplanes that were coincident with parent cuts but in opposite directions.
  438.      *
  439.      * <p>
  440.      * Another way of looking at the rules above is that inserting a hyperplane into the tree that exactly
  441.      * matches the hyperplane of a parent node does not add any information to the tree. However, adding a
  442.      * hyperplane to the tree that is coincident with a parent node but with the opposite orientation,
  443.      * <em>does</em> add information to the tree.
  444.      *
  445.      * @param node the node representing the region to fit the hyperplane subset to
  446.      * @param sub the hyperplane subset to trim to the node's region
  447.      * @return the trimmed hyperplane subset or null if the given hyperplane subset does not intersect
  448.      *      the node's region
  449.      */
  450.     protected HyperplaneConvexSubset<P> trimToNode(final N node, final HyperplaneConvexSubset<P> sub) {

  451.         HyperplaneConvexSubset<P> result = sub;

  452.         N parentNode = node.getParent();
  453.         N currentNode = node;

  454.         while (parentNode != null && result != null) {
  455.             final Split<? extends HyperplaneConvexSubset<P>> split = result.split(parentNode.getCutHyperplane());

  456.             if (split.getLocation() == SplitLocation.NEITHER) {
  457.                 // if we're directly on the splitter and have the same orientation, then
  458.                 // we say the subset does not lie in the node's region (no new information
  459.                 // is added to the tree in this case)
  460.                 if (result.getHyperplane().similarOrientation(parentNode.getCutHyperplane())) {
  461.                     result = null;
  462.                 }
  463.             } else {
  464.                 result = currentNode.isPlus() ? split.getPlus() : split.getMinus();
  465.             }

  466.             currentNode = parentNode;
  467.             parentNode = parentNode.getParent();
  468.         }

  469.         return result;
  470.     }

  471.     /** Remove the cut from the given node. Returns true if the node had a cut before
  472.      * the call to this method. Any previous child nodes are lost.
  473.      *
  474.      * <p>This method calls {@link #invalidate()} to invalidate cached tree properties if the tree
  475.      * structure changed.</p>
  476.      * @param node the node to remove the cut from
  477.      * @return true if the node previously had a cut
  478.      */
  479.     protected boolean removeNodeCut(final N node) {
  480.         if (node.getCut() != null) {
  481.             node.setSubtree(null, null, null);

  482.             invalidate();

  483.             return true;
  484.         }

  485.         return false;
  486.     }

  487.     /** Set the cut hyperplane subset for the given node. Two new child nodes are created for the
  488.      * node and the new subtree is initialized with {@code subtreeInitializer}.
  489.      *
  490.      * <p>This method performs absolutely <em>no</em> validation on the given cut
  491.      * hyperplane subset. It is the responsibility of the caller to ensure that the
  492.      * hyperplane subset fits the region represented by the node.</p>
  493.      *
  494.      * <p>This method always calls {@link #invalidate()} to invalidate cached tree properties.</p>
  495.      * @param node the node to cut
  496.      * @param cut the hyperplane convex subset to set as the node cut
  497.      * @param subtreeInitializer object used to initialize the newly-created subtree
  498.      */
  499.     protected void setNodeCut(final N node, final HyperplaneConvexSubset<P> cut,
  500.             final SubtreeInitializer<N> subtreeInitializer) {

  501.         node.setSubtree(cut, createNode(), createNode());

  502.         subtreeInitializer.initSubtree(node);

  503.         invalidate();
  504.     }

  505.     /** Insert the given hyperplane convex subset into the tree, starting at the root node. Any subtrees
  506.      * created are initialized with {@code subtreeInit}.
  507.      * @param convexSub hyperplane convex subset to insert into the tree
  508.      * @param subtreeInit object used to initialize newly created subtrees
  509.      */
  510.     protected void insert(final HyperplaneConvexSubset<P> convexSub, final SubtreeInitializer<N> subtreeInit) {
  511.         insertRecursive(getRoot(), convexSub,
  512.                 convexSub.getHyperplane().span(), subtreeInit);
  513.     }

  514.     /** Recursively insert a hyperplane convex subset into the tree at the given node.
  515.      * @param node the node to begin insertion with
  516.      * @param insert the hyperplane subset to insert
  517.      * @param trimmed hyperplane subset containing the result of splitting the entire
  518.      *      space with each hyperplane from this node to the root
  519.      * @param subtreeInit object used to initialize newly created subtrees
  520.      */
  521.     private void insertRecursive(final N node, final HyperplaneConvexSubset<P> insert,
  522.             final HyperplaneConvexSubset<P> trimmed, final SubtreeInitializer<N> subtreeInit) {
  523.         if (node.isLeaf()) {
  524.             setNodeCut(node, trimmed, subtreeInit);
  525.         } else {
  526.             final Split<? extends HyperplaneConvexSubset<P>> insertSplit = insert.split(node.getCutHyperplane());

  527.             final HyperplaneConvexSubset<P> minus = insertSplit.getMinus();
  528.             final HyperplaneConvexSubset<P> plus = insertSplit.getPlus();

  529.             if (minus != null || plus != null) {
  530.                 final Split<? extends HyperplaneConvexSubset<P>> trimmedSplit = trimmed.split(node.getCutHyperplane());

  531.                 if (minus != null) {
  532.                     insertRecursive(node.getMinus(), minus, trimmedSplit.getMinus(), subtreeInit);
  533.                 }
  534.                 if (plus != null) {
  535.                     insertRecursive(node.getPlus(), plus, trimmedSplit.getPlus(), subtreeInit);
  536.                 }
  537.             }
  538.         }
  539.     }

  540.     /** Return true if the given transform swaps the inside and outside of
  541.      * the region.
  542.      *
  543.      * <p>The default behavior of this method is to return true if the transform
  544.      * does not preserve spatial orientation (ie, {@link Transform#preservesOrientation()}
  545.      * is false). Subclasses may need to override this method to implement the correct
  546.      * behavior for their space and dimension.</p>
  547.      * @param transform transform to check
  548.      * @return true if the given transform swaps the interior and exterior of
  549.      *      the region
  550.      */
  551.     protected boolean swapsInsideOutside(final Transform<P> transform) {
  552.         return !transform.preservesOrientation();
  553.     }

  554.     /** Transform the subtree rooted as {@code node} recursively.
  555.      * @param node the root node of the subtree to transform
  556.      * @param t the transform to apply
  557.      * @param swapChildren if true, the plus and minus child nodes of each internal node
  558.      *      will be swapped; this should be the case when the transform is a reflection
  559.      */
  560.     private void transformRecursive(final N node, final Transform<P> t, final boolean swapChildren) {
  561.         if (node.isInternal()) {
  562.             // transform our cut
  563.             final HyperplaneConvexSubset<P> transformedCut = node.getCut().transform(t);

  564.             // transform our children
  565.             transformRecursive(node.getMinus(), t, swapChildren);
  566.             transformRecursive(node.getPlus(), t, swapChildren);

  567.             final N transformedMinus = swapChildren ? node.getPlus() : node.getMinus();
  568.             final N transformedPlus = swapChildren ? node.getMinus() : node.getPlus();

  569.             // set our new state
  570.             node.setSubtree(transformedCut, transformedMinus, transformedPlus);
  571.         }
  572.     }

  573.     /** Split this tree with the given hyperplane, placing the split contents into the given
  574.      * target trees. One of the given trees may be null, in which case that portion of the split
  575.      * will not be exported. The current tree is not modified.
  576.      * @param splitter splitting hyperplane
  577.      * @param minus tree that will contain the portion of the tree on the minus side of the splitter
  578.      * @param plus tree that will contain the portion of the tree on the plus side of the splitter
  579.      */
  580.     protected void splitIntoTrees(final Hyperplane<P> splitter,
  581.             final AbstractBSPTree<P, N> minus, final AbstractBSPTree<P, N> plus) {

  582.         final AbstractBSPTree<P, N> temp = (minus != null) ? minus : plus;

  583.         final N splitRoot = temp.splitSubtree(this.getRoot(), splitter.span());

  584.         if (minus != null) {
  585.             if (plus != null) {
  586.                 plus.extract(splitRoot.getPlus());
  587.             }
  588.             minus.extract(splitRoot.getMinus());
  589.         } else {
  590.             plus.extract(splitRoot.getPlus());
  591.         }
  592.     }

  593.     /** Split the subtree rooted at the given node by a partitioning convex subset defined
  594.      * on the same region as the node. The subtree rooted at {@code node} is imported into
  595.      * this tree, meaning that if it comes from a different tree, the other tree is not
  596.      * modified.
  597.      * @param node the root node of the subtree to split; may come from a different tree,
  598.      *      in which case the other tree is not modified
  599.      * @param partitioner partitioning convex subset
  600.      * @return node containing the split subtree
  601.      */
  602.     protected N splitSubtree(final N node, final HyperplaneConvexSubset<P> partitioner) {
  603.         if (node.isLeaf()) {
  604.             return splitLeafNode(node, partitioner);
  605.         }
  606.         return splitInternalNode(node, partitioner);
  607.     }

  608.     /** Split the given leaf node by a partitioning convex subset defined on the
  609.      * same region and import it into this tree.
  610.      * @param node the leaf node to split
  611.      * @param partitioner partitioning convex subset
  612.      * @return node containing the split subtree
  613.      */
  614.     private N splitLeafNode(final N node, final HyperplaneConvexSubset<P> partitioner) {
  615.         // in this case, we just create a new parent node with the partitioner as its
  616.         // cut and two copies of the original node as children
  617.         final N parent = createNode();
  618.         parent.setSubtree(partitioner, copyNode(node), copyNode(node));

  619.         return parent;
  620.     }

  621.     /** Split the given internal node by a partitioning convex subset defined on the same region
  622.      * as the node and import it into this tree.
  623.      * @param node the internal node to split
  624.      * @param partitioner partitioning convex subset
  625.      * @return node containing the split subtree
  626.      */
  627.     private N splitInternalNode(final N node, final HyperplaneConvexSubset<P> partitioner) {
  628.         // split the partitioner and node cut with each other's hyperplanes to determine their relative positions
  629.         final Split<? extends HyperplaneConvexSubset<P>> partitionerSplit = partitioner.split(node.getCutHyperplane());
  630.         final Split<? extends HyperplaneConvexSubset<P>> nodeCutSplit =
  631.                 node.getCut().split(partitioner.getHyperplane());

  632.         final SplitLocation partitionerSplitSide = partitionerSplit.getLocation();
  633.         final SplitLocation nodeCutSplitSide = nodeCutSplit.getLocation();

  634.         final N result = createNode();

  635.         final N resultMinus;
  636.         final N resultPlus;

  637.         if (partitionerSplitSide == SplitLocation.PLUS) {
  638.             if (nodeCutSplitSide == SplitLocation.PLUS) {
  639.                 // partitioner is on node cut plus side, node cut is on partitioner plus side
  640.                 final N nodePlusSplit = splitSubtree(node.getPlus(), partitioner);

  641.                 resultMinus = nodePlusSplit.getMinus();

  642.                 resultPlus = copyNode(node);
  643.                 resultPlus.setSubtree(node.getCut(), importSubtree(node.getMinus()), nodePlusSplit.getPlus());
  644.             } else {
  645.                 // partitioner is on node cut plus side, node cut is on partitioner minus side
  646.                 final N nodePlusSplit = splitSubtree(node.getPlus(), partitioner);

  647.                 resultMinus = copyNode(node);
  648.                 resultMinus.setSubtree(node.getCut(), importSubtree(node.getMinus()), nodePlusSplit.getMinus());

  649.                 resultPlus = nodePlusSplit.getPlus();
  650.             }
  651.         } else if (partitionerSplitSide == SplitLocation.MINUS) {
  652.             if (nodeCutSplitSide == SplitLocation.MINUS) {
  653.                 // partitioner is on node cut minus side, node cut is on partitioner minus side
  654.                 final N nodeMinusSplit = splitSubtree(node.getMinus(), partitioner);

  655.                 resultMinus = copyNode(node);
  656.                 resultMinus.setSubtree(node.getCut(), nodeMinusSplit.getMinus(), importSubtree(node.getPlus()));

  657.                 resultPlus = nodeMinusSplit.getPlus();
  658.             } else {
  659.                 // partitioner is on node cut minus side, node cut is on partitioner plus side
  660.                 final N nodeMinusSplit = splitSubtree(node.getMinus(), partitioner);

  661.                 resultMinus = nodeMinusSplit.getMinus();

  662.                 resultPlus = copyNode(node);
  663.                 resultPlus.setSubtree(node.getCut(), nodeMinusSplit.getPlus(), importSubtree(node.getPlus()));
  664.             }
  665.         } else if (partitionerSplitSide == SplitLocation.BOTH) {
  666.             // partitioner and node cut split each other
  667.             final N nodeMinusSplit = splitSubtree(node.getMinus(), partitionerSplit.getMinus());
  668.             final N nodePlusSplit = splitSubtree(node.getPlus(), partitionerSplit.getPlus());

  669.             resultMinus = copyNode(node);
  670.             resultMinus.setSubtree(nodeCutSplit.getMinus(), nodeMinusSplit.getMinus(), nodePlusSplit.getMinus());

  671.             resultPlus = copyNode(node);
  672.             resultPlus.setSubtree(nodeCutSplit.getPlus(), nodeMinusSplit.getPlus(), nodePlusSplit.getPlus());
  673.         } else {
  674.             // partitioner and node cut are parallel or anti-parallel
  675.             final boolean sameOrientation = partitioner.getHyperplane().similarOrientation(node.getCutHyperplane());

  676.             resultMinus = importSubtree(sameOrientation ? node.getMinus() : node.getPlus());
  677.             resultPlus = importSubtree(sameOrientation ? node.getPlus() : node.getMinus());
  678.         }

  679.         result.setSubtree(partitioner, resultMinus, resultPlus);

  680.         return result;
  681.     }

  682.     /** Invalidate any previously computed properties that rely on the internal structure of the tree.
  683.      * This method must be called any time the tree's internal structure changes in order to force cacheable
  684.      * tree and node properties to be recomputed the next time they are requested.
  685.      *
  686.      * <p>This method increments the tree's {@link #version} property.</p>
  687.      * @see #getVersion()
  688.      */
  689.     protected void invalidate() {
  690.         version = Math.max(0, version + 1); // positive values only
  691.     }

  692.     /** Get the current structural version of the tree. This is incremented each time the
  693.      * tree structure is changes and can be used by nodes to allow caching of computed values.
  694.      * @return the current version of the tree structure
  695.      * @see #invalidate()
  696.      */
  697.     protected int getVersion() {
  698.         return version;
  699.     }

  700.     /** Abstract implementation of {@link BSPTree.Node}. This class is intended for use with
  701.      * {@link AbstractBSPTree} and delegates tree mutation methods back to the parent tree object.
  702.      * @param <P> Point implementation type
  703.      * @param <N> BSP tree node implementation type
  704.      */
  705.     public abstract static class AbstractNode<P extends Point<P>, N extends AbstractNode<P, N>>
  706.         implements BSPTree.Node<P, N> {
  707.         /** The owning tree instance. */
  708.         private final AbstractBSPTree<P, N> tree;

  709.         /** The parent node; this will be null for the tree root node. */
  710.         private N parent;

  711.         /** The hyperplane convex subset cutting the node's region; this will be null for leaf nodes. */
  712.         private HyperplaneConvexSubset<P> cut;

  713.         /** The node lying on the minus side of the cut hyperplane; this will be null
  714.          * for leaf nodes.
  715.          */
  716.         private N minus;

  717.         /** The node lying on the plus side of the cut hyperplane; this will be null
  718.          * for leaf nodes.
  719.          */
  720.         private N plus;

  721.         /** The current version of the node. This is set to track the tree's version
  722.          * and is used to detect when certain values need to be recomputed due to
  723.          * structural changes in the tree.
  724.          */
  725.         private int nodeVersion = -1;

  726.         /** The depth of this node in the tree. This will be zero for the root node and
  727.          * {@link AbstractBSPTree#UNKNOWN_VALUE} when the value needs to be computed.
  728.          */
  729.         private int depth = UNKNOWN_VALUE;

  730.         /** The total number of nodes in the subtree rooted at this node. This will be
  731.          * set to {@link AbstractBSPTree#UNKNOWN_VALUE} when the value needs
  732.          * to be computed.
  733.          */
  734.         private int count = UNKNOWN_VALUE;

  735.         /** The height of the subtree rooted at this node. This will
  736.          * be set to {@link AbstractBSPTree#UNKNOWN_VALUE} when the value needs
  737.          * to be computed.
  738.          */
  739.         private int height = UNKNOWN_VALUE;

  740.         /** Simple constructor.
  741.          * @param tree the tree instance that owns this node
  742.          */
  743.         protected AbstractNode(final AbstractBSPTree<P, N> tree) {
  744.             this.tree = tree;
  745.         }

  746.         /** {@inheritDoc} */
  747.         @Override
  748.         public AbstractBSPTree<P, N> getTree() {
  749.             return tree;
  750.         }

  751.         /** {@inheritDoc} */
  752.         @Override
  753.         public int depth() {
  754.             // Calculate our depth based on our parent's depth, if possible.
  755.             if (depth == UNKNOWN_VALUE &&
  756.                 parent != null) {
  757.                 final int parentDepth = parent.depth();
  758.                 if (parentDepth != UNKNOWN_VALUE) {
  759.                     depth = parentDepth + 1;
  760.                 }
  761.             }
  762.             return depth;
  763.         }

  764.         /** {@inheritDoc} */
  765.         @Override
  766.         public int height() {
  767.             checkValid();

  768.             if (height == UNKNOWN_VALUE) {
  769.                 if (isLeaf()) {
  770.                     height = 0;
  771.                 } else {
  772.                     height = Math.max(getMinus().height(), getPlus().height()) + 1;
  773.                 }
  774.             }

  775.             return height;
  776.         }

  777.         /** {@inheritDoc} */
  778.         @Override
  779.         public int count() {
  780.             checkValid();

  781.             if (count == UNKNOWN_VALUE) {
  782.                 count = 1;

  783.                 if (!isLeaf()) {
  784.                     count += minus.count() + plus.count();
  785.                 }
  786.             }

  787.             return count;
  788.         }

  789.         /** {@inheritDoc} */
  790.         @Override
  791.         public Iterable<N> nodes() {
  792.             return () -> new NodeIterator<>(getSelf());
  793.         }

  794.         /** {@inheritDoc} */
  795.         @Override
  796.         public void accept(final BSPTreeVisitor<P, N> visitor) {
  797.             tree.accept(getSelf(), visitor);
  798.         }

  799.         /** {@inheritDoc} */
  800.         @Override
  801.         public N getParent() {
  802.             return parent;
  803.         }

  804.         /** {@inheritDoc} */
  805.         @Override
  806.         public boolean isLeaf() {
  807.             return cut == null;
  808.         }

  809.         /** {@inheritDoc} */
  810.         @Override
  811.         public boolean isInternal() {
  812.             return cut != null;
  813.         }

  814.         /** {@inheritDoc} */
  815.         @Override
  816.         public boolean isPlus() {
  817.             return parent != null && parent.getPlus() == this;
  818.         }

  819.         /** {@inheritDoc} */
  820.         @Override
  821.         public boolean isMinus() {
  822.             return parent != null && parent.getMinus() == this;
  823.         }

  824.         /** {@inheritDoc} */
  825.         @Override
  826.         public HyperplaneConvexSubset<P> getCut() {
  827.             return cut;
  828.         }

  829.         /** {@inheritDoc} */
  830.         @Override
  831.         public Hyperplane<P> getCutHyperplane() {
  832.             return (cut != null) ? cut.getHyperplane() : null;
  833.         }

  834.         /** {@inheritDoc} */
  835.         @Override
  836.         public N getPlus() {
  837.             return plus;
  838.         }

  839.         /** {@inheritDoc} */
  840.         @Override
  841.         public N getMinus() {
  842.             return minus;
  843.         }

  844.         /** {@inheritDoc} */
  845.         @Override
  846.         public HyperplaneConvexSubset<P> trim(final HyperplaneConvexSubset<P> sub) {
  847.             return getTree().trimToNode(getSelf(), sub);
  848.         }

  849.         /** {@inheritDoc} */
  850.         @Override
  851.         public String toString() {
  852.             final StringBuilder sb = new StringBuilder();
  853.             sb.append(this.getClass().getSimpleName())
  854.                 .append("[cut= ")
  855.                 .append(getCut())
  856.                 .append(']');

  857.             return sb.toString();
  858.         }

  859.         /** Set the parameters for the subtree rooted at this node. The arguments should either be
  860.          * all null (representing a leaf node) or all non-null (representing an internal node).
  861.          *
  862.          * <p>Absolutely no validation is performed on the arguments. Callers are responsible for
  863.          * ensuring that any given hyperplane subset fits the region defined by the node and that
  864.          * any child nodes belong to this tree and are correctly initialized.</p>
  865.          *
  866.          * @param newCut the new cut hyperplane subset for the node
  867.          * @param newMinus the new minus child for the node
  868.          * @param newPlus the new plus child for the node
  869.          */
  870.         protected void setSubtree(final HyperplaneConvexSubset<P> newCut, final N newMinus, final N newPlus) {
  871.             this.cut = newCut;

  872.             final N self = getSelf();

  873.             // cast for access to private member
  874.             final AbstractNode<P, N> minusNode = newMinus;
  875.             final AbstractNode<P, N> plusNode = newPlus;

  876.             // get the child depth now if we know it offhand, otherwise set it to the unknown value
  877.             // and have the child pull it when needed
  878.             final int childDepth = (depth != UNKNOWN_VALUE) ? depth + 1 : UNKNOWN_VALUE;

  879.             if (newMinus != null) {
  880.                 minusNode.parent = self;
  881.                 minusNode.depth = childDepth;
  882.             }
  883.             this.minus = newMinus;

  884.             if (newPlus != null) {
  885.                 plusNode.parent = self;
  886.                 plusNode.depth = childDepth;
  887.             }
  888.             this.plus = newPlus;
  889.         }

  890.         /**
  891.          * Make this node a root node, detaching it from its parent and settings its depth to zero.
  892.          * Any previous parent node will be left in an invalid state since one of its children now
  893.          * does not have a reference back to it.
  894.          */
  895.         protected void makeRoot() {
  896.             parent = null;
  897.             depth = 0;
  898.         }

  899.         /** Check if cached node properties are valid, meaning that no structural updates have
  900.          * occurred in the tree since the last call to this method. If updates have occurred, the
  901.          * {@link #nodeInvalidated()} method is called to clear the cached properties. This method
  902.          * should be called at the beginning of any method that fetches cacheable properties
  903.          * to ensure that no stale values are returned.
  904.          */
  905.         protected void checkValid() {
  906.             final int treeVersion = tree.getVersion();

  907.             if (nodeVersion != treeVersion) {
  908.                 // the tree structure changed somewhere
  909.                 nodeInvalidated();

  910.                 // store the current version
  911.                 nodeVersion = treeVersion;
  912.             }
  913.         }

  914.         /** Method called from {@link #checkValid()} when updates
  915.          * are detected in the tree. This method should clear out any
  916.          * computed properties that rely on the structure of the tree
  917.          * and prepare them for recalculation.
  918.          */
  919.         protected void nodeInvalidated() {
  920.             count = UNKNOWN_VALUE;
  921.             height = UNKNOWN_VALUE;
  922.         }

  923.         /** Get a reference to the current instance, cast to type N.
  924.          * @return a reference to the current instance, as type N.
  925.          */
  926.         protected abstract N getSelf();
  927.     }

  928.     /** Class for iterating through the nodes in a BSP subtree.
  929.      * @param <P> Point implementation type
  930.      * @param <N> Node implementation type
  931.      */
  932.     private static final class NodeIterator<P extends Point<P>, N extends AbstractNode<P, N>> implements Iterator<N> {

  933.         /** The current node stack. */
  934.         private final Deque<N> stack = new LinkedList<>();

  935.         /** Create a new instance for iterating over the nodes in the given subtree.
  936.          * @param subtreeRoot the root node of the subtree to iterate
  937.          */
  938.         NodeIterator(final N subtreeRoot) {
  939.             stack.push(subtreeRoot);
  940.         }

  941.         /** {@inheritDoc} */
  942.         @Override
  943.         public boolean hasNext() {
  944.             return !stack.isEmpty();
  945.         }

  946.         /** {@inheritDoc} */
  947.         @Override
  948.         public N next() {
  949.             if (stack.isEmpty()) {
  950.                 throw new NoSuchElementException();
  951.             }

  952.             final N result = stack.pop();

  953.             if (result != null && !result.isLeaf()) {
  954.                 stack.push(result.getPlus());
  955.                 stack.push(result.getMinus());
  956.             }

  957.             return result;
  958.         }
  959.     }
  960. }