BSPTree.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.geometry.partitioning;

  22. import java.util.ArrayList;
  23. import java.util.List;

  24. import org.hipparchus.exception.MathRuntimeException;
  25. import org.hipparchus.geometry.Geometry;
  26. import org.hipparchus.geometry.Point;
  27. import org.hipparchus.geometry.Space;
  28. import org.hipparchus.util.FastMath;

  29. /** This class represent a Binary Space Partition tree.

  30.  * <p>BSP trees are an efficient way to represent space partitions and
  31.  * to associate attributes with each cell. Each node in a BSP tree
  32.  * represents a convex region which is partitioned in two convex
  33.  * sub-regions at each side of a cut hyperplane. The root tree
  34.  * contains the complete space.</p>

  35.  * <p>The main use of such partitions is to use a boolean attribute to
  36.  * define an inside/outside property, hence representing arbitrary
  37.  * polytopes (line segments in 1D, polygons in 2D and polyhedrons in
  38.  * 3D) and to operate on them.</p>

  39.  * <p>Another example would be to represent Voronoi tesselations, the
  40.  * attribute of each cell holding the defining point of the cell.</p>

  41.  * <p>The application-defined attributes are shared among copied
  42.  * instances and propagated to split parts. These attributes are not
  43.  * used by the BSP-tree algorithms themselves, so the application can
  44.  * use them for any purpose. Since the tree visiting method holds
  45.  * internal and leaf nodes differently, it is possible to use
  46.  * different classes for internal nodes attributes and leaf nodes
  47.  * attributes. This should be used with care, though, because if the
  48.  * tree is modified in any way after attributes have been set, some
  49.  * internal nodes may become leaf nodes and some leaf nodes may become
  50.  * internal nodes.</p>

  51.  * <p>One of the main sources for the development of this package was
  52.  * Bruce Naylor, John Amanatides and William Thibault paper <a
  53.  * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging
  54.  * BSP Trees Yields Polyhedral Set Operations</a> Proc. Siggraph '90,
  55.  * Computer Graphics 24(4), August 1990, pp 115-124, published by the
  56.  * Association for Computing Machinery (ACM).</p>

  57.  * @param <S> Type of the space.
  58.  * @param <P> Type of the points in space.
  59.  * @param <H> Type of the hyperplane.
  60.  * @param <I> Type of the sub-hyperplane.

  61.  */
  62. public class BSPTree<S extends Space,
  63.                      P extends Point<S, P>,
  64.                      H extends Hyperplane<S, P, H, I>,
  65.                      I extends SubHyperplane<S, P, H, I>> {

  66.     /** Cut sub-hyperplane. */
  67.     private I cut;

  68.     /** Tree at the plus side of the cut hyperplane. */
  69.     private BSPTree<S, P, H, I> plus;

  70.     /** Tree at the minus side of the cut hyperplane. */
  71.     private BSPTree<S, P, H, I> minus;

  72.     /** Parent tree. */
  73.     private BSPTree<S, P, H, I> parent;

  74.     /** Application-defined attribute. */
  75.     private Object attribute;

  76.     /** Build a tree having only one root cell representing the whole space.
  77.      */
  78.     public BSPTree() {
  79.         cut       = null;
  80.         plus      = null;
  81.         minus     = null;
  82.         parent    = null;
  83.         attribute = null;
  84.     }

  85.     /** Build a tree having only one root cell representing the whole space.
  86.      * @param attribute attribute of the tree (may be null)
  87.      */
  88.     public BSPTree(final Object attribute) {
  89.         cut    = null;
  90.         plus   = null;
  91.         minus  = null;
  92.         parent = null;
  93.         this.attribute = attribute;
  94.     }

  95.     /** Build a BSPTree from its underlying elements.
  96.      * <p>This method does <em>not</em> perform any verification on
  97.      * consistency of its arguments, it should therefore be used only
  98.      * when then caller knows what it is doing.</p>
  99.      * <p>This method is mainly useful to build trees
  100.      * bottom-up. Building trees top-down is realized with the help of
  101.      * method {@link #insertCut insertCut}.</p>
  102.      * @param cut cut sub-hyperplane for the tree
  103.      * @param plus plus side sub-tree
  104.      * @param minus minus side sub-tree
  105.      * @param attribute attribute associated with the node (may be null)
  106.      * @see #insertCut
  107.      */
  108.     public BSPTree(final I cut, final BSPTree<S, P, H, I> plus, final BSPTree<S, P, H, I> minus,
  109.                    final Object attribute) {
  110.         this.cut       = cut;
  111.         this.plus      = plus;
  112.         this.minus     = minus;
  113.         this.parent    = null;
  114.         this.attribute = attribute;
  115.         plus.parent    = this;
  116.         minus.parent   = this;
  117.     }

  118.     /** Insert a cut sub-hyperplane in a node.
  119.      * <p>The sub-tree starting at this node will be completely
  120.      * overwritten. The new cut sub-hyperplane will be built from the
  121.      * intersection of the provided hyperplane with the cell. If the
  122.      * hyperplane does intersect the cell, the cell will have two
  123.      * children cells with {@code null} attributes on each side of
  124.      * the inserted cut sub-hyperplane. If the hyperplane does not
  125.      * intersect the cell then <em>no</em> cut hyperplane will be
  126.      * inserted and the cell will be changed to a leaf cell. The
  127.      * attribute of the node is never changed.</p>
  128.      * <p>This method is mainly useful when called on leaf nodes
  129.      * (i.e. nodes for which {@link #getCut getCut} returns
  130.      * {@code null}), in this case it provides a way to build a
  131.      * tree top-down (whereas the {@link #BSPTree(SubHyperplane,
  132.      * BSPTree, BSPTree, Object) 4 arguments constructor} is devoted to
  133.      * build trees bottom-up).</p>
  134.      * @param hyperplane hyperplane to insert, it will be chopped in
  135.      * order to fit in the cell defined by the parent nodes of the
  136.      * instance
  137.      * @return true if a cut sub-hyperplane has been inserted (i.e. if
  138.      * the cell now has two leaf child nodes)
  139.      * @see #BSPTree(SubHyperplane, BSPTree, BSPTree, Object)
  140.      */
  141.     public boolean insertCut(final H hyperplane) {

  142.         if (cut != null) {
  143.             plus.parent  = null;
  144.             minus.parent = null;
  145.         }

  146.         final I chopped = fitToCell(hyperplane.wholeHyperplane(), null);
  147.         if (chopped == null || chopped.isEmpty()) {
  148.             cut          = null;
  149.             plus         = null;
  150.             minus        = null;
  151.             return false;
  152.         }

  153.         cut          = chopped;
  154.         plus         = new BSPTree<>();
  155.         plus.parent  = this;
  156.         minus        = new BSPTree<>();
  157.         minus.parent = this;
  158.         return true;

  159.     }

  160.     /** Copy the instance.
  161.      * <p>The instance created is completely independent of the original
  162.      * one. A deep copy is used, none of the underlying objects are
  163.      * shared (except for the nodes attributes and immutable
  164.      * objects).</p>
  165.      * @return a new tree, copy of the instance
  166.      */
  167.     public BSPTree<S, P, H, I> copySelf() {

  168.         if (cut == null) {
  169.             return new BSPTree<>(attribute);
  170.         }

  171.         return new BSPTree<>(cut.copySelf(), plus.copySelf(), minus.copySelf(), attribute);

  172.     }

  173.     /** Get the cut sub-hyperplane.
  174.      * @return cut sub-hyperplane, null if this is a leaf tree
  175.      */
  176.     public I getCut() {
  177.         return cut;
  178.     }

  179.     /** Get the tree on the plus side of the cut hyperplane.
  180.      * @return tree on the plus side of the cut hyperplane, null if this
  181.      * is a leaf tree
  182.      */
  183.     public BSPTree<S, P, H, I> getPlus() {
  184.         return plus;
  185.     }

  186.     /** Get the tree on the minus side of the cut hyperplane.
  187.      * @return tree on the minus side of the cut hyperplane, null if this
  188.      * is a leaf tree
  189.      */
  190.     public BSPTree<S, P, H, I> getMinus() {
  191.         return minus;
  192.     }

  193.     /** Get the parent node.
  194.      * @return parent node, null if the node has no parents
  195.      */
  196.     public BSPTree<S, P, H, I> getParent() {
  197.         return parent;
  198.     }

  199.     /** Associate an attribute with the instance.
  200.      * @param attribute attribute to associate with the node
  201.      * @see #getAttribute
  202.      */
  203.     public void setAttribute(final Object attribute) {
  204.         this.attribute = attribute;
  205.     }

  206.     /** Get the attribute associated with the instance.
  207.      * @return attribute associated with the node or null if no
  208.      * attribute has been explicitly set using the {@link #setAttribute
  209.      * setAttribute} method
  210.      * @see #setAttribute
  211.      */
  212.     public Object getAttribute() {
  213.         return attribute;
  214.     }

  215.     /** Visit the BSP tree nodes.
  216.      * @param visitor object visiting the tree nodes
  217.      */
  218.     public void visit(final BSPTreeVisitor<S, P, H, I> visitor) {
  219.         if (cut == null) {
  220.             visitor.visitLeafNode(this);
  221.         } else {
  222.             switch (visitor.visitOrder(this)) {
  223.             case PLUS_MINUS_SUB:
  224.                 plus.visit(visitor);
  225.                 minus.visit(visitor);
  226.                 visitor.visitInternalNode(this);
  227.                 break;
  228.             case PLUS_SUB_MINUS:
  229.                 plus.visit(visitor);
  230.                 visitor.visitInternalNode(this);
  231.                 minus.visit(visitor);
  232.                 break;
  233.             case MINUS_PLUS_SUB:
  234.                 minus.visit(visitor);
  235.                 plus.visit(visitor);
  236.                 visitor.visitInternalNode(this);
  237.                 break;
  238.             case MINUS_SUB_PLUS:
  239.                 minus.visit(visitor);
  240.                 visitor.visitInternalNode(this);
  241.                 plus.visit(visitor);
  242.                 break;
  243.             case SUB_PLUS_MINUS:
  244.                 visitor.visitInternalNode(this);
  245.                 plus.visit(visitor);
  246.                 minus.visit(visitor);
  247.                 break;
  248.             case SUB_MINUS_PLUS:
  249.                 visitor.visitInternalNode(this);
  250.                 minus.visit(visitor);
  251.                 plus.visit(visitor);
  252.                 break;
  253.             default:
  254.                 throw MathRuntimeException.createInternalError();
  255.             }

  256.         }
  257.     }

  258.     /** Fit a sub-hyperplane inside the cell defined by the instance.
  259.      * <p>Fitting is done by chopping off the parts of the
  260.      * sub-hyperplane that lie outside the cell using the
  261.      * cut-hyperplanes of the parent nodes of the instance.</p>
  262.      * @param sub sub-hyperplane to fit
  263.      * @param ignored tree node to ignore (null if all nodes should be considered)
  264.      * @return a new sub-hyperplane, guaranteed to have no part outside
  265.      * of the instance cell
  266.      */
  267.     private I fitToCell(final I sub, final BSPTree<S, P, H, I> ignored) {
  268.         I s = sub;
  269.         for (BSPTree<S, P, H, I> tree = this; tree.parent != null && s != null; tree = tree.parent) {
  270.             if (tree.parent != ignored) {
  271.                 if (tree == tree.parent.plus) {
  272.                     s = s.split(tree.parent.cut.getHyperplane()).getPlus();
  273.                 }
  274.                 else {
  275.                     s = s.split(tree.parent.cut.getHyperplane()).getMinus();
  276.                 }
  277.             }
  278.         }
  279.         return s;
  280.     }

  281.     /** Get a point that is interior to the cell.
  282.      * @param defaultPoint default point to return if tree is empty
  283.      * @return point that is interior to the cell
  284.      * @since 4.0
  285.      */
  286.     public InteriorPoint<S, P> getInteriorPoint(final P defaultPoint) {

  287.         // find edges/facets interior points
  288.         final List<P> edgePoints = new ArrayList<>();
  289.         for (BSPTree<S, P, H, I> n = parent; n != null; n = n.getParent()) {
  290.             final I fit = fitToCell(n.getCut().getHyperplane().wholeHyperplane(), n);
  291.             if (fit != null) {
  292.                 final P p = fit.getInteriorPoint();
  293.                 if (p != null) {
  294.                     edgePoints.add(p);
  295.                 }
  296.             }
  297.         }

  298.         if (edgePoints.isEmpty()) {
  299.             // there are no edges/facets interior points at all!
  300.             // we are in a cell representing the whole space
  301.             return new InteriorPoint<>(defaultPoint, Double.POSITIVE_INFINITY);
  302.         } else {
  303.             // compute barycenter of edges/facets interior point
  304.             final P barycenter = Geometry.barycenter(edgePoints);

  305.             // compute distance to the closest edge/facet
  306.             double min = Double.POSITIVE_INFINITY;
  307.             for (BSPTree<S, P, H, I> n = parent; n != null; n = n.getParent()) {
  308.                 min = FastMath.min(min, FastMath.abs(n.getCut().getHyperplane().getOffset(barycenter)));
  309.             }

  310.             return new InteriorPoint<>(barycenter, min);
  311.         }

  312.     }

  313.     /** Get the cell to which a point belongs.
  314.      * <p>If the returned cell is a leaf node the points belongs to the
  315.      * interior of the node, if the cell is an internal node the points
  316.      * belongs to the node cut sub-hyperplane.</p>
  317.      * @param point point to check
  318.      * @param tolerance tolerance below which points close to a cut hyperplane
  319.      * are considered to belong to the hyperplane itself
  320.      * @return the tree cell to which the point belongs
  321.      */
  322.     public BSPTree<S, P, H, I> getCell(final P point, final double tolerance) {

  323.         if (cut == null) {
  324.             return this;
  325.         }

  326.         // position of the point with respect to the cut hyperplane
  327.         final double offset = cut.getHyperplane().getOffset(point);

  328.         if (FastMath.abs(offset) < tolerance) {
  329.             return this;
  330.         } else if (offset <= 0) {
  331.             // point is on the minus side of the cut hyperplane
  332.             return minus.getCell(point, tolerance);
  333.         } else {
  334.             // point is on the plus side of the cut hyperplane
  335.             return plus.getCell(point, tolerance);
  336.         }

  337.     }

  338.     /** Perform condensation on a tree.
  339.      * <p>The condensation operation is not recursive, it must be called
  340.      * explicitly from leaves to root.</p>
  341.      */
  342.     private void condense() {
  343.         if ((cut != null) && (plus.cut == null) && (minus.cut == null) &&
  344.             (((plus.attribute == null) && (minus.attribute == null)) ||
  345.              ((plus.attribute != null) && plus.attribute.equals(minus.attribute)))) {
  346.             attribute = (plus.attribute == null) ? minus.attribute : plus.attribute;
  347.             cut       = null;
  348.             plus      = null;
  349.             minus     = null;
  350.         }
  351.     }

  352.     /** Merge a BSP tree with the instance.
  353.      * <p>All trees are modified (parts of them are reused in the new
  354.      * tree), it is the responsibility of the caller to ensure a copy
  355.      * has been done before if any of the former tree should be
  356.      * preserved, <em>no</em> such copy is done here!</p>
  357.      * <p>The algorithm used here is directly derived from the one
  358.      * described in the Naylor, Amanatides and Thibault paper (section
  359.      * III, Binary Partitioning of a BSP Tree).</p>
  360.      * @param tree other tree to merge with the instance (will be
  361.      * <em>unusable</em> after the operation, as well as the
  362.      * instance itself)
  363.      * @param leafMerger object implementing the final merging phase
  364.      * (this is where the semantic of the operation occurs, generally
  365.      * depending on the attribute of the leaf node)
  366.      * @return a new tree, result of <code>instance &lt;op&gt;
  367.      * tree</code>, this value can be ignored if parentTree is not null
  368.      * since all connections have already been established
  369.      */
  370.     public BSPTree<S, P, H, I> merge(final BSPTree<S, P, H, I> tree, final LeafMerger<S, P, H, I> leafMerger) {
  371.         final BSPTree<S, P, H, I> merged = merge(tree, leafMerger, null, false);
  372.         merged.fixCuts();
  373.         return merged;
  374.     }

  375.     /** Merge a BSP tree with the instance.
  376.      * @param tree other tree to merge with the instance (will be
  377.      * <em>unusable</em> after the operation, as well as the
  378.      * instance itself)
  379.      * @param leafMerger object implementing the final merging phase
  380.      * (this is where the semantic of the operation occurs, generally
  381.      * depending on the attribute of the leaf node)
  382.      * @param parentTree parent tree to connect to (may be null)
  383.      * @param isPlusChild if true and if parentTree is not null, the
  384.      * resulting tree should be the plus child of its parent, ignored if
  385.      * parentTree is null
  386.      * @return a new tree, result of <code>instance &lt;op&gt;
  387.      * tree</code>, this value can be ignored if parentTree is not null
  388.      * since all connections have already been established
  389.      */
  390.     private BSPTree<S, P, H, I> merge(final BSPTree<S, P, H, I> tree, final LeafMerger<S, P, H, I> leafMerger,
  391.                                       final BSPTree<S, P, H, I> parentTree, final boolean isPlusChild) {
  392.         if (cut == null) {
  393.             // cell/tree operation
  394.             return leafMerger.merge(this, tree, parentTree, isPlusChild, true);
  395.         } else if (tree.cut == null) {
  396.             // tree/cell operation
  397.             return leafMerger.merge(tree, this, parentTree, isPlusChild, false);
  398.         } else {
  399.             // tree/tree operation
  400.             final BSPTree<S, P, H, I> merged = tree.split(cut);
  401.             if (parentTree != null) {
  402.                 merged.parent = parentTree;
  403.                 if (isPlusChild) {
  404.                     parentTree.plus = merged;
  405.                 } else {
  406.                     parentTree.minus = merged;
  407.                 }
  408.             }

  409.             // merging phase
  410.             plus.merge(merged.plus, leafMerger, merged, true);
  411.             minus.merge(merged.minus, leafMerger, merged, false);
  412.             merged.condense();
  413.             if (merged.cut != null) {
  414.                 merged.cut = merged.fitToCell(merged.cut.getHyperplane().wholeHyperplane(), null);
  415.             }

  416.             return merged;

  417.         }
  418.     }

  419.     /** Fix cut sub-hyperplanes.
  420.      * <p>
  421.      * In some cases, cut sub-hyperplanes boundaries in a tree may be inconsistent.
  422.      * One cause can be merge operations, where some cuts are inherited from one of
  423.      * the merged tree, because the trees returned by {@link #split(SubHyperplane)}
  424.      * are not upward-consistent. Another cause can be trees built bottom-up.
  425.      * </p>
  426.      * <p>
  427.      * This method recomputes the sub-hyperplanes once the full tree has been built
  428.      * </p>
  429.      */
  430.     private void fixCuts() {
  431.         if (cut != null) {
  432.             final I fit = fitToCell(cut.getHyperplane().wholeHyperplane(), null);
  433.             if (fit == null) {
  434.                 // cut sub-hyperplane vanished
  435.                 cut = cut.getHyperplane().emptyHyperplane();
  436.             } else {
  437.                 cut = fit;
  438.             }
  439.             plus.fixCuts();
  440.             minus.fixCuts();
  441.         }
  442.     }

  443.     /** This interface gather the merging operations between a BSP tree
  444.      * leaf and another BSP tree.
  445.      * <p>As explained in Bruce Naylor, John Amanatides and William
  446.      * Thibault paper <a
  447.      * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging
  448.      * BSP Trees Yields Polyhedral Set Operations</a>,
  449.      * the operations on {@link BSPTree BSP trees} can be expressed as a
  450.      * generic recursive merging operation where only the final part,
  451.      * when one of the operand is a leaf, is specific to the real
  452.      * operation semantics. For example, a tree representing a region
  453.      * using a boolean attribute to identify inside cells and outside
  454.      * cells would use four different objects to implement the final
  455.      * merging phase of the four set operations union, intersection,
  456.      * difference and symmetric difference (exclusive or).</p>
  457.      * @param <S> Type of the space.
  458.      * @param <P> Type of the points in space.
  459.      * @param <H> Type of the hyperplane.
  460.      * @param <I> Type of the sub-hyperplane.
  461.      */
  462.     public interface LeafMerger<S extends Space,
  463.                                 P extends Point<S, P>,
  464.                                 H extends Hyperplane<S, P, H, I>,
  465.                                 I extends SubHyperplane<S, P, H, I>> {

  466.         /** Merge a leaf node and a tree node.
  467.          * <p>This method is called at the end of a recursive merging
  468.          * resulting from a {@code tree1.merge(tree2, leafMerger)}
  469.          * call, when one of the sub-trees involved is a leaf (i.e. when
  470.          * its cut-hyperplane is null). This is the only place where the
  471.          * precise semantics of the operation are required. For all upper
  472.          * level nodes in the tree, the merging operation is only a
  473.          * generic partitioning algorithm.</p>
  474.          * <p>Since the final operation may be non-commutative, it is
  475.          * important to know if the leaf node comes from the instance tree
  476.          * ({@code tree1}) or the argument tree
  477.          * ({@code tree2}). The third argument of the method is
  478.          * devoted to this. It can be ignored for commutative
  479.          * operations.</p>
  480.          * <p>The {@link BSPTree#insertInTree BSPTree.insertInTree} method
  481.          * may be useful to implement this method.</p>
  482.          * @param leaf leaf node (its cut hyperplane is guaranteed to be
  483.          * null)
  484.          * @param tree tree node (its cut hyperplane may be null or not)
  485.          * @param parentTree parent tree to connect to (may be null)
  486.          * @param isPlusChild if true and if parentTree is not null, the
  487.          * resulting tree should be the plus child of its parent, ignored if
  488.          * parentTree is null
  489.          * @param leafFromInstance if true, the leaf node comes from the
  490.          * instance tree ({@code tree1}) and the tree node comes from
  491.          * the argument tree ({@code tree2})
  492.          * @return the BSP tree resulting from the merging (may be one of
  493.          * the arguments)
  494.          */
  495.         BSPTree<S, P, H, I> merge(BSPTree<S, P, H, I> leaf, BSPTree<S, P, H, I> tree,
  496.                                   BSPTree<S, P, H, I> parentTree, boolean isPlusChild, boolean leafFromInstance);

  497.     }

  498.     /** This interface handles the corner cases when an internal node cut sub-hyperplane vanishes.
  499.      * <p>
  500.      * Such cases happens for example when a cut sub-hyperplane is inserted into
  501.      * another tree (during a merge operation), and is split in several parts,
  502.      * some of which becomes smaller than the tolerance. The corresponding node
  503.      * as then no cut sub-hyperplane anymore, but does have children. This interface
  504.      * specifies how to handle this situation.
  505.      * setting
  506.      * </p>
  507.      * @param <S> Type of the space.
  508.      * @param <P> Type of the points in space.
  509.      * @param <H> Type of the hyperplane.
  510.      * @param <I> Type of the sub-hyperplane.
  511.      */
  512.     public interface VanishingCutHandler<S extends Space,
  513.                                          P extends Point<S, P>,
  514.                                          H extends Hyperplane<S, P, H, I>,
  515.                                          I extends SubHyperplane<S, P, H, I>> {

  516.         /** Fix a node with both vanished cut and children.
  517.          * @param node node to fix
  518.          * @return fixed node
  519.          */
  520.         BSPTree<S, P, H, I> fixNode(BSPTree<S, P, H, I> node);

  521.     }

  522.     /** Split a BSP tree by an external sub-hyperplane.
  523.      * <p>Split a tree in two halves, on each side of the
  524.      * sub-hyperplane. The instance is not modified.</p>
  525.      * <p>The tree returned is not upward-consistent: despite all of its
  526.      * sub-trees cut sub-hyperplanes (including its own cut
  527.      * sub-hyperplane) are bounded to the current cell, it is <em>not</em>
  528.      * attached to any parent tree yet. This tree is intended to be
  529.      * later inserted into a higher level tree.</p>
  530.      * <p>The algorithm used here is the one given in Naylor, Amanatides
  531.      * and Thibault paper (section III, Binary Partitioning of a BSP
  532.      * Tree).</p>
  533.      * @param sub partitioning sub-hyperplane, must be already clipped
  534.      * to the convex region represented by the instance, will be used as
  535.      * the cut sub-hyperplane of the returned tree
  536.      * @return a tree having the specified sub-hyperplane as its cut
  537.      * sub-hyperplane, the two parts of the split instance as its two
  538.      * sub-trees and a null parent
  539.      */
  540.     public BSPTree<S, P, H, I> split(final I sub) {

  541.         if (cut == null) {
  542.             return new BSPTree<>(sub, copySelf(), new BSPTree<>(attribute), null);
  543.         }

  544.         final H cHyperplane = cut.getHyperplane();
  545.         final H sHyperplane = sub.getHyperplane();
  546.         final SubHyperplane.SplitSubHyperplane<S, P, H, I> subParts = sub.split(cHyperplane);
  547.         switch (subParts.getSide()) {
  548.         case PLUS :
  549.         { // the partitioning sub-hyperplane is entirely in the plus sub-tree
  550.             final BSPTree<S, P, H, I> split = plus.split(sub);
  551.             if (cut.split(sHyperplane).getSide() == Side.PLUS) {
  552.                 split.plus = new BSPTree<>(cut.copySelf(), split.plus, minus.copySelf(), attribute);
  553.                 split.plus.condense();
  554.                 split.plus.parent = split;
  555.             } else {
  556.                 split.minus = new BSPTree<>(cut.copySelf(), split.minus, minus.copySelf(), attribute);
  557.                 split.minus.condense();
  558.                 split.minus.parent = split;
  559.             }
  560.             return split;
  561.         }
  562.         case MINUS :
  563.         { // the partitioning sub-hyperplane is entirely in the minus sub-tree
  564.             final BSPTree<S, P, H, I> split = minus.split(sub);
  565.             if (cut.split(sHyperplane).getSide() == Side.PLUS) {
  566.                 split.plus = new BSPTree<>(cut.copySelf(), plus.copySelf(), split.plus, attribute);
  567.                 split.plus.condense();
  568.                 split.plus.parent = split;
  569.             } else {
  570.                 split.minus = new BSPTree<>(cut.copySelf(), plus.copySelf(), split.minus, attribute);
  571.                 split.minus.condense();
  572.                 split.minus.parent = split;
  573.             }
  574.             return split;
  575.         }
  576.         case BOTH :
  577.         {
  578.             final SubHyperplane.SplitSubHyperplane<S, P, H, I> cutParts = cut.split(sHyperplane);
  579.             final BSPTree<S, P, H, I> split = new BSPTree<>(sub,
  580.                                                             plus.split(subParts.getPlus()),
  581.                                                             minus.split(subParts.getMinus()),
  582.                                                             null);
  583.             final BSPTree<S, P, H, I> tmp    = split.plus.minus;
  584.             split.plus.minus        = split.minus.plus;
  585.             split.plus.minus.parent = split.plus;
  586.             split.minus.plus        = tmp;
  587.             split.minus.plus.parent = split.minus;
  588.             if (cutParts.getPlus() == null) {
  589.                 split.plus.cut = cut.getHyperplane().emptyHyperplane();
  590.             } else {
  591.                 split.plus.cut = cutParts.getPlus();
  592.             }
  593.             if (cutParts.getMinus() == null) {
  594.                 split.minus.cut = cut.getHyperplane().emptyHyperplane();
  595.             } else {
  596.                 split.minus.cut = cutParts.getMinus();
  597.             }
  598.             split.plus.condense();
  599.             split.minus.condense();
  600.             return split;

  601.         }
  602.         default :
  603.             return cHyperplane.sameOrientationAs(sHyperplane) ?
  604.                    new BSPTree<>(sub, plus.copySelf(),  minus.copySelf(), attribute) :
  605.                    new BSPTree<>(sub, minus.copySelf(), plus.copySelf(),  attribute);
  606.         }

  607.     }

  608.     /** Insert the instance into another tree.
  609.      * <p>The instance itself is modified so its former parent should
  610.      * not be used anymore.</p>
  611.      * @param parentTree parent tree to connect to (may be null)
  612.      * @param isPlusChild if true and if parentTree is not null, the
  613.      * resulting tree should be the plus child of its parent, ignored if
  614.      * parentTree is null
  615.      * @param vanishingHandler handler to use for handling very rare corner
  616.      * cases of vanishing cut sub-hyperplanes in internal nodes during merging
  617.      * @see LeafMerger
  618.      */
  619.     public void insertInTree(final BSPTree<S, P, H, I> parentTree, final boolean isPlusChild,
  620.                              final VanishingCutHandler<S, P, H, I> vanishingHandler) {

  621.         // set up parent/child links
  622.         parent = parentTree;
  623.         if (parentTree != null) {
  624.             if (isPlusChild) {
  625.                 parentTree.plus = this;
  626.             } else {
  627.                 parentTree.minus = this;
  628.             }
  629.         }

  630.         // make sure the inserted tree lies in the cell defined by its parent nodes
  631.         if (cut != null) {

  632.             // explore the parent nodes from here towards tree root
  633.             for (BSPTree<S, P, H, I> tree = this; tree.parent != null; tree = tree.parent) {

  634.                 // this is an hyperplane of some parent node
  635.                 final H hyperplane = tree.parent.cut.getHyperplane();

  636.                 // chop off the parts of the inserted tree that extend
  637.                 // on the wrong side of this parent hyperplane
  638.                 if (tree == tree.parent.plus) {
  639.                     cut = cut.split(hyperplane).getPlus();
  640.                     fixVanishingCut(vanishingHandler);
  641.                     if (cut == null) {
  642.                         break;
  643.                     }
  644.                     plus.chopOffMinus(hyperplane, vanishingHandler);
  645.                     minus.chopOffMinus(hyperplane, vanishingHandler);
  646.                 } else {
  647.                     cut = cut.split(hyperplane).getMinus();
  648.                     fixVanishingCut(vanishingHandler);
  649.                     if (cut == null) {
  650.                         break;
  651.                     }
  652.                     plus.chopOffPlus(hyperplane, vanishingHandler);
  653.                     minus.chopOffPlus(hyperplane, vanishingHandler);
  654.                 }
  655.             }

  656.             // since we may have drop some parts of the inserted tree,
  657.             // perform a condensation pass to keep the tree structure simple
  658.             condense();

  659.         }

  660.     }

  661.     /** Prune a tree around a cell.
  662.      * <p>
  663.      * This method can be used to extract a convex cell from a tree.
  664.      * The original cell may either be a leaf node or an internal node.
  665.      * If it is an internal node, it's subtree will be ignored (i.e. the
  666.      * extracted cell will be a leaf node in all cases). The original
  667.      * tree to which the original cell belongs is not touched at all,
  668.      * a new independent tree will be built.
  669.      * </p>
  670.      * @param cellAttribute attribute to set for the leaf node
  671.      * corresponding to the initial instance cell
  672.      * @param otherLeafsAttributes attribute to set for the other leaf
  673.      * nodes
  674.      * @param internalAttributes attribute to set for the internal nodes
  675.      * @return a new tree (the original tree is left untouched) containing
  676.      * a single branch with the cell as a leaf node, and other leaf nodes
  677.      * as the remnants of the pruned branches
  678.      */
  679.     public BSPTree<S, P, H, I> pruneAroundConvexCell(final Object cellAttribute,
  680.                                                      final Object otherLeafsAttributes,
  681.                                                      final Object internalAttributes) {

  682.         // build the current cell leaf
  683.         BSPTree<S, P, H, I> tree = new BSPTree<>(cellAttribute);

  684.         // build the pruned tree bottom-up
  685.         for (BSPTree<S, P, H, I> current = this; current.parent != null; current = current.parent) {
  686.             final I parentCut = current.parent.cut.copySelf();
  687.             final BSPTree<S, P, H, I> sibling   = new BSPTree<>(otherLeafsAttributes);
  688.             if (current == current.parent.plus) {
  689.                 tree = new BSPTree<>(parentCut, tree, sibling, internalAttributes);
  690.             } else {
  691.                 tree = new BSPTree<>(parentCut, sibling, tree, internalAttributes);
  692.             }
  693.         }

  694.         // fix the cuts so the pruned tree is consistent
  695.         tree.fixCuts();

  696.         return tree;

  697.     }

  698.     /** Chop off parts of the tree.
  699.      * <p>The instance is modified in place, all the parts that are on
  700.      * the minus side of the chopping hyperplane are discarded, only the
  701.      * parts on the plus side remain.</p>
  702.      * @param hyperplane chopping hyperplane
  703.      * @param vanishingHandler handler to use for handling very rare corner
  704.      * cases of vanishing cut sub-hyperplanes in internal nodes during merging
  705.      */
  706.     private void chopOffMinus(final H hyperplane, final VanishingCutHandler<S, P, H, I> vanishingHandler) {
  707.         if (cut != null) {

  708.             cut = cut.split(hyperplane).getPlus();
  709.             plus.chopOffMinus(hyperplane, vanishingHandler);
  710.             minus.chopOffMinus(hyperplane, vanishingHandler);

  711.             fixVanishingCut(vanishingHandler);

  712.         }
  713.     }

  714.     /** Chop off parts of the tree.
  715.      * <p>The instance is modified in place, all the parts that are on
  716.      * the plus side of the chopping hyperplane are discarded, only the
  717.      * parts on the minus side remain.</p>
  718.      * @param hyperplane chopping hyperplane
  719.      * @param vanishingHandler handler to use for handling very rare corner
  720.      * cases of vanishing cut sub-hyperplanes in internal nodes during merging
  721.      */
  722.     private void chopOffPlus(final H hyperplane, final VanishingCutHandler<S, P, H, I> vanishingHandler) {
  723.         if (cut != null) {

  724.             cut = cut.split(hyperplane).getMinus();
  725.             plus.chopOffPlus(hyperplane, vanishingHandler);
  726.             minus.chopOffPlus(hyperplane, vanishingHandler);

  727.             fixVanishingCut(vanishingHandler);

  728.         }
  729.     }

  730.     /** Fix vanishing cut.
  731.      * @param vanishingHandler handler to use for handling very rare corner
  732.      */
  733.     private void fixVanishingCut(final VanishingCutHandler<S, P, H, I> vanishingHandler) {
  734.         if (cut == null) {
  735.             // the cut sub-hyperplane has vanished
  736.             final BSPTree<S, P, H, I> fixed = vanishingHandler.fixNode(this);
  737.             cut       = fixed.cut;
  738.             plus      = fixed.plus;
  739.             minus     = fixed.minus;
  740.             attribute = fixed.attribute;
  741.         }
  742.     }

  743.     /** Container for cell interior points.
  744.      * @param <S> Type of the space.
  745.      * @param <P> Type of the points in space.
  746.      * @since 4.0
  747.      */
  748.     public static final class InteriorPoint <S extends Space, P extends Point<S, P>> {

  749.         /** Point. */
  750.         private final P point;

  751.         /** Distance to the closest edge/facet. */
  752.         private final double distance;

  753.         /** Simple constructor.
  754.          * @param point point
  755.          * @param distance d
  756.          */
  757.         InteriorPoint(final P point, final double distance) {
  758.             this.point    = point;
  759.             this.distance = distance;
  760.         }

  761.         /** Get the interior point.
  762.          * @return interior point
  763.          */
  764.         public P getPoint() {
  765.             return point;
  766.         }

  767.         /** Get the distance to the closest edge/facet.
  768.          * @return distance to the closest edge/facet.
  769.          */
  770.         public double getDistance() {
  771.             return distance;
  772.         }

  773.     }

  774. }