Repository: commons-math
Updated Branches:
  refs/heads/master e11c00085 -> d21d3a94a


Added splitters in Boundary attributes.


Project: http://git-wip-us.apache.org/repos/asf/commons-math/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-math/commit/6525cc40
Tree: http://git-wip-us.apache.org/repos/asf/commons-math/tree/6525cc40
Diff: http://git-wip-us.apache.org/repos/asf/commons-math/diff/6525cc40

Branch: refs/heads/master
Commit: 6525cc4035fe8df3308c03f0ca9f450728e122ac
Parents: 26ee481
Author: Luc Maisonobe <l...@apache.org>
Authored: Sun Nov 30 18:09:06 2014 +0100
Committer: Luc Maisonobe <l...@apache.org>
Committed: Tue Dec 2 15:24:31 2014 +0100

----------------------------------------------------------------------
 src/changes/changes.xml                         |  4 ++
 .../geometry/partitioning/AbstractRegion.java   | 67 +++++++++++++-----
 .../partitioning/AbstractSubHyperplane.java     | 67 +++++++++++++-----
 .../partitioning/BoundaryAttribute.java         | 32 +++++++++
 .../geometry/partitioning/BoundaryBuilder.java  | 23 ++++--
 .../geometry/partitioning/Characterization.java | 67 ++++++++++++++----
 .../math3/geometry/partitioning/NodesSet.java   | 72 +++++++++++++++++++
 .../geometry/partitioning/RegionFactory.java    | 74 ++++++++++++++++----
 8 files changed, 338 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c8f0afd..46ad704 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -73,6 +73,10 @@ Users are encouraged to upgrade to this version as this 
release not
   2. A few methods in the FastMath class are in fact slower that their
   counterpart in either Math or StrictMath (cf. MATH-740 and MATH-901).
 ">
+      <action dev="luc" type="add" >
+        Boundary attributes in regions now provides the BSP tree nodes that
+        were used to split the sub-hyperplane froming the boundary part of the 
facet. 
+      </action>
       <action dev="luc" type="fix" issue="MATH-1162" >
         Fixed a problem with vanishing cut sub-hyperplanes during BSP tree 
merging.
       </action>

http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java
 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java
index 3fca476..fd3dcc7 100644
--- 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java
+++ 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractRegion.java
@@ -19,7 +19,9 @@ package org.apache.commons.math3.geometry.partitioning;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.TreeSet;
 
 import org.apache.commons.math3.geometry.Point;
@@ -349,7 +351,7 @@ public abstract class AbstractRegion<S extends Space, T 
extends Space> implement
     /** {@inheritDoc} */
     public BSPTree<S> getTree(final boolean includeBoundaryAttributes) {
         if (includeBoundaryAttributes && (tree.getCut() != null) && 
(tree.getAttribute() == null)) {
-            // we need to compute the boundary attributes
+            // compute the boundary attributes
             tree.visit(new BoundaryBuilder<S>());
         }
         return tree;
@@ -465,36 +467,65 @@ public abstract class AbstractRegion<S extends Space, T 
extends Space> implement
      * transform to the instance
      */
     public AbstractRegion<S, T> applyTransform(final Transform<S, T> 
transform) {
-        return buildNew(recurseTransform(getTree(false), transform));
+
+        // transform the tree, except for boundary attribute splitters
+        final Map<BSPTree<S>, BSPTree<S>> map = new HashMap<BSPTree<S>, 
BSPTree<S>>();
+        final BSPTree<S> transformedTree = recurseTransform(getTree(false), 
transform, map);
+
+        // set up the boundary attributes splitters
+        for (final Map.Entry<BSPTree<S>, BSPTree<S>> entry : map.entrySet()) {
+            if (entry.getKey().getCut() != null) {
+                @SuppressWarnings("unchecked")
+                BoundaryAttribute<S> original = (BoundaryAttribute<S>) 
entry.getKey().getAttribute();
+                if (original != null) {
+                    @SuppressWarnings("unchecked")
+                    BoundaryAttribute<S> transformed = (BoundaryAttribute<S>) 
entry.getValue().getAttribute();
+                    for (final BSPTree<S> splitter : original.getSplitters()) {
+                        transformed.getSplitters().add(map.get(splitter));
+                    }
+                }
+            }
+        }
+
+        return buildNew(transformedTree);
+
     }
 
     /** Recursively transform an inside/outside BSP-tree.
      * @param node current BSP tree node
      * @param transform transform to apply
+     * @param map transformed nodes map
      * @return a new tree
      */
     @SuppressWarnings("unchecked")
-    private BSPTree<S> recurseTransform(final BSPTree<S> node, final 
Transform<S, T> transform) {
+    private BSPTree<S> recurseTransform(final BSPTree<S> node, final 
Transform<S, T> transform,
+                                        final Map<BSPTree<S>, BSPTree<S>> map) 
{
 
+        final BSPTree<S> transformedNode;
         if (node.getCut() == null) {
-            return new BSPTree<S>(node.getAttribute());
-        }
+            transformedNode = new BSPTree<S>(node.getAttribute());
+        } else {
+
+            final SubHyperplane<S>  sub = node.getCut();
+            final SubHyperplane<S> tSub = ((AbstractSubHyperplane<S, T>) 
sub).applyTransform(transform);
+            BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) 
node.getAttribute();
+            if (attribute != null) {
+                final SubHyperplane<S> tPO = (attribute.getPlusOutside() == 
null) ?
+                    null : ((AbstractSubHyperplane<S, T>) 
attribute.getPlusOutside()).applyTransform(transform);
+                final SubHyperplane<S> tPI = (attribute.getPlusInside()  == 
null) ?
+                    null  : ((AbstractSubHyperplane<S, T>) 
attribute.getPlusInside()).applyTransform(transform);
+                // we start with an empty list of splitters, it will be filled 
in out of recursion
+                attribute = new BoundaryAttribute<S>(tPO, tPI, new 
NodesSet<S>());
+            }
 
-        final SubHyperplane<S>  sub = node.getCut();
-        final SubHyperplane<S> tSub = ((AbstractSubHyperplane<S, T>) 
sub).applyTransform(transform);
-        BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) 
node.getAttribute();
-        if (attribute != null) {
-            final SubHyperplane<S> tPO = (attribute.getPlusOutside() == null) ?
-                null : ((AbstractSubHyperplane<S, T>) 
attribute.getPlusOutside()).applyTransform(transform);
-            final SubHyperplane<S> tPI = (attribute.getPlusInside()  == null) ?
-                null  : ((AbstractSubHyperplane<S, T>) 
attribute.getPlusInside()).applyTransform(transform);
-            attribute = new BoundaryAttribute<S>(tPO, tPI);
+            transformedNode = new BSPTree<S>(tSub,
+                                             recurseTransform(node.getPlus(),  
transform, map),
+                                             recurseTransform(node.getMinus(), 
transform, map),
+                                             attribute);
         }
 
-        return new BSPTree<S>(tSub,
-                                    recurseTransform(node.getPlus(),  
transform),
-                                    recurseTransform(node.getMinus(), 
transform),
-                                    attribute);
+        map.put(node, transformedNode);
+        return transformedNode;
 
     }
 

http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java
 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java
index 8a4931f..3fd6b54 100644
--- 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java
+++ 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/AbstractSubHyperplane.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.math3.geometry.partitioning;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.commons.math3.geometry.Space;
 
 /** This class implements the dimension-independent parts of {@link 
SubHyperplane}.
@@ -107,39 +110,67 @@ public abstract class AbstractSubHyperplane<S extends 
Space, T extends Space>
      */
     public AbstractSubHyperplane<S, T> applyTransform(final Transform<S, T> 
transform) {
         final Hyperplane<S> tHyperplane = transform.apply(hyperplane);
+
+        // transform the tree, except for boundary attribute splitters
+        final Map<BSPTree<T>, BSPTree<T>> map = new HashMap<BSPTree<T>, 
BSPTree<T>>();
         final BSPTree<T> tTree =
-            recurseTransform(remainingRegion.getTree(false), tHyperplane, 
transform);
+            recurseTransform(remainingRegion.getTree(false), tHyperplane, 
transform, map);
+
+        // set up the boundary attributes splitters
+        for (final Map.Entry<BSPTree<T>, BSPTree<T>> entry : map.entrySet()) {
+            if (entry.getKey().getCut() != null) {
+                @SuppressWarnings("unchecked")
+                BoundaryAttribute<T> original = (BoundaryAttribute<T>) 
entry.getKey().getAttribute();
+                if (original != null) {
+                    @SuppressWarnings("unchecked")
+                    BoundaryAttribute<T> transformed = (BoundaryAttribute<T>) 
entry.getValue().getAttribute();
+                    for (final BSPTree<T> splitter : original.getSplitters()) {
+                        transformed.getSplitters().add(map.get(splitter));
+                    }
+                }
+            }
+        }
+
         return buildNew(tHyperplane, remainingRegion.buildNew(tTree));
+
     }
 
     /** Recursively transform a BSP-tree from a sub-hyperplane.
      * @param node current BSP tree node
      * @param transformed image of the instance hyperplane by the transform
      * @param transform transform to apply
+     * @param map transformed nodes map
      * @return a new tree
      */
     private BSPTree<T> recurseTransform(final BSPTree<T> node,
                                         final Hyperplane<S> transformed,
-                                        final Transform<S, T> transform) {
-        if (node.getCut() == null) {
-            return new BSPTree<T>(node.getAttribute());
-        }
+                                        final Transform<S, T> transform,
+                                        final Map<BSPTree<T>, BSPTree<T>> map) 
{
 
-        @SuppressWarnings("unchecked")
-        BoundaryAttribute<T> attribute =
-            (BoundaryAttribute<T>) node.getAttribute();
-        if (attribute != null) {
-            final SubHyperplane<T> tPO = (attribute.getPlusOutside() == null) ?
-                null : transform.apply(attribute.getPlusOutside(), hyperplane, 
transformed);
-            final SubHyperplane<T> tPI = (attribute.getPlusInside() == null) ?
-                null : transform.apply(attribute.getPlusInside(), hyperplane, 
transformed);
-            attribute = new BoundaryAttribute<T>(tPO, tPI);
+        final BSPTree<T> transformedNode;
+        if (node.getCut() == null) {
+            transformedNode = new BSPTree<T>(node.getAttribute());
+        } else {
+
+            @SuppressWarnings("unchecked")
+            BoundaryAttribute<T> attribute = (BoundaryAttribute<T>) 
node.getAttribute();
+            if (attribute != null) {
+                final SubHyperplane<T> tPO = (attribute.getPlusOutside() == 
null) ?
+                    null : transform.apply(attribute.getPlusOutside(), 
hyperplane, transformed);
+                final SubHyperplane<T> tPI = (attribute.getPlusInside() == 
null) ?
+                    null : transform.apply(attribute.getPlusInside(), 
hyperplane, transformed);
+                // we start with an empty list of splitters, it will be filled 
in out of recursion
+                attribute = new BoundaryAttribute<T>(tPO, tPI, new 
NodesSet<T>());
+            }
+
+            transformedNode = new BSPTree<T>(transform.apply(node.getCut(), 
hyperplane, transformed),
+                    recurseTransform(node.getPlus(),  transformed, transform, 
map),
+                    recurseTransform(node.getMinus(), transformed, transform, 
map),
+                    attribute);
         }
 
-        return new BSPTree<T>(transform.apply(node.getCut(), hyperplane, 
transformed),
-                              recurseTransform(node.getPlus(), transformed, 
transform),
-                              recurseTransform(node.getMinus(), transformed, 
transform),
-                              attribute);
+        map.put(node, transformedNode);
+        return transformedNode;
 
     }
 

http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java
 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java
index ea7d9a7..dad884c 100644
--- 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java
+++ 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryAttribute.java
@@ -45,6 +45,9 @@ public class BoundaryAttribute<S extends Space> {
      */
     private final SubHyperplane<S> plusInside;
 
+    /** Sub-hyperplanes that were used to split the boundary part. */
+    private final NodesSet<S> splitters;
+
     /** Simple constructor.
      * @param plusOutside part of the node cut sub-hyperplane that
      * belongs to the boundary and has the outside of the region on
@@ -52,11 +55,33 @@ public class BoundaryAttribute<S extends Space> {
      * @param plusInside part of the node cut sub-hyperplane that
      * belongs to the boundary and has the inside of the region on the
      * plus side of its underlying hyperplane (may be null)
+     * @deprecated as of 3.4, the constructor has been replaced by a new one
+     * which is not public anymore, as it is intended to be used only by
+     * {@link BoundaryBuilder}
      */
+    @Deprecated
     public BoundaryAttribute(final SubHyperplane<S> plusOutside,
                              final SubHyperplane<S> plusInside) {
+        this(plusOutside, plusInside, null);
+    }
+
+    /** Simple constructor.
+     * @param plusOutside part of the node cut sub-hyperplane that
+     * belongs to the boundary and has the outside of the region on
+     * the plus side of its underlying hyperplane (may be null)
+     * @param plusInside part of the node cut sub-hyperplane that
+     * belongs to the boundary and has the inside of the region on the
+     * plus side of its underlying hyperplane (may be null)
+     * @param splitters sub-hyperplanes that were used to
+     * split the boundary part (may be null)
+     * @since 3.4
+     */
+    BoundaryAttribute(final SubHyperplane<S> plusOutside,
+                      final SubHyperplane<S> plusInside,
+                      final NodesSet<S> splitters) {
         this.plusOutside = plusOutside;
         this.plusInside  = plusInside;
+        this.splitters   = splitters;
     }
 
     /** Get the part of the node cut sub-hyperplane that belongs to the
@@ -81,4 +106,11 @@ public class BoundaryAttribute<S extends Space> {
         return plusInside;
     }
 
+    /** Get the sub-hyperplanes that were used to split the boundary part.
+     * @return sub-hyperplanes that were used to split the boundary part
+     */
+    public NodesSet<S> getSplitters() {
+        return splitters;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java
 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java
index 80038f1..cea4de3 100644
--- 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java
+++ 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/BoundaryBuilder.java
@@ -28,11 +28,6 @@ import org.apache.commons.math3.geometry.Space;
  */
 class BoundaryBuilder<S extends Space> implements BSPTreeVisitor<S> {
 
-    /** Simple constructor.
-     */
-    public BoundaryBuilder() {
-    }
-
     /** {@inheritDoc} */
     public Order visitOrder(BSPTree<S> node) {
         return Order.PLUS_MINUS_SUB;
@@ -43,6 +38,7 @@ class BoundaryBuilder<S extends Space> implements 
BSPTreeVisitor<S> {
 
         SubHyperplane<S> plusOutside = null;
         SubHyperplane<S> plusInside  = null;
+        NodesSet<S>      splitters   = null;
 
         // characterize the cut sub-hyperplane,
         // first with respect to the plus sub-tree
@@ -57,6 +53,9 @@ class BoundaryBuilder<S extends Space> implements 
BSPTreeVisitor<S> {
                 // this part belongs to the boundary,
                 // it has the outside on its plus side and the inside on its 
minus side
                 plusOutside = minusChar.insideTouching();
+                splitters = new NodesSet<S>();
+                splitters.addAll(minusChar.getInsideSplitters());
+                splitters.addAll(plusChar.getOutsideSplitters());
             }
         }
 
@@ -69,11 +68,23 @@ class BoundaryBuilder<S extends Space> implements 
BSPTreeVisitor<S> {
                 // this part belongs to the boundary,
                 // it has the inside on its plus side and the outside on its 
minus side
                 plusInside = minusChar.outsideTouching();
+                if (splitters == null) {
+                    splitters = new NodesSet<S>();
+                }
+                splitters.addAll(minusChar.getOutsideSplitters());
+                splitters.addAll(plusChar.getInsideSplitters());
+            }
+        }
+
+        if (splitters != null) {
+            // the parent nodes are natural splitters for boundary 
sub-hyperplanes
+            for (BSPTree<S> up = node.getParent(); up != null; up = 
up.getParent()) {
+                splitters.add(up);
             }
         }
 
         // set the boundary attribute at non-leaf nodes
-        node.setAttribute(new BoundaryAttribute<S>(plusOutside, plusInside));
+        node.setAttribute(new BoundaryAttribute<S>(plusOutside, plusInside, 
splitters));
 
     }
 

http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java
 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java
index a67f873..58efe0f 100644
--- 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java
+++ 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/Characterization.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.math3.geometry.partitioning;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.commons.math3.exception.MathInternalError;
 import org.apache.commons.math3.geometry.Space;
 
@@ -32,6 +35,12 @@ class Characterization<S extends Space> {
     /** Part of the cut sub-hyperplane that touch inside cells. */
     private SubHyperplane<S> insideTouching;
 
+    /** Nodes that were used to split the outside touching part. */
+    private final NodesSet<S> outsideSplitters;
+
+    /** Nodes that were used to split the outside touching part. */
+    private final NodesSet<S> insideSplitters;
+
     /** Simple constructor.
      * <p>Characterization consists in splitting the specified
      * sub-hyperplane into several parts lying in inside and outside
@@ -45,9 +54,11 @@ class Characterization<S extends Space> {
      * @param sub sub-hyperplane to characterize
      */
     public Characterization(final BSPTree<S> node, final SubHyperplane<S> sub) 
{
-        outsideTouching = null;
-        insideTouching  = null;
-        characterize(node, sub);
+        outsideTouching  = null;
+        insideTouching   = null;
+        outsideSplitters = new NodesSet<S>();
+        insideSplitters  = new NodesSet<S>();
+        characterize(node, sub, new ArrayList<BSPTree<S>>());
     }
 
     /** Filter the parts of an hyperplane belonging to the boundary.
@@ -61,29 +72,33 @@ class Characterization<S extends Space> {
      * outside/inside) do belong to the boundary.</p>
      * @param node current BSP tree node
      * @param sub sub-hyperplane to characterize
+     * @param splitters nodes that did split the current one
      */
-    private void characterize(final BSPTree<S> node, final SubHyperplane<S> 
sub) {
+    private void characterize(final BSPTree<S> node, final SubHyperplane<S> 
sub,
+                              final List<BSPTree<S>> splitters) {
         if (node.getCut() == null) {
             // we have reached a leaf node
             final boolean inside = (Boolean) node.getAttribute();
             if (inside) {
-                addInsideTouching(sub);
+                addInsideTouching(sub, splitters);
             } else {
-                addOutsideTouching(sub);
+                addOutsideTouching(sub, splitters);
             }
         } else {
             final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
             switch (sub.side(hyperplane)) {
             case PLUS:
-                characterize(node.getPlus(), sub);
+                characterize(node.getPlus(),  sub, splitters);
                 break;
             case MINUS:
-                characterize(node.getMinus(), sub);
+                characterize(node.getMinus(), sub, splitters);
                 break;
             case BOTH:
                 final SubHyperplane.SplitSubHyperplane<S> split = 
sub.split(hyperplane);
-                characterize(node.getPlus(),  split.getPlus());
-                characterize(node.getMinus(), split.getMinus());
+                splitters.add(node);
+                characterize(node.getPlus(),  split.getPlus(),  splitters);
+                characterize(node.getMinus(), split.getMinus(), splitters);
+                splitters.remove(splitters.size() - 1);
                 break;
             default:
                 // this should not happen
@@ -94,24 +109,30 @@ class Characterization<S extends Space> {
 
     /** Add a part of the cut sub-hyperplane known to touch an outside cell.
      * @param sub part of the cut sub-hyperplane known to touch an outside cell
+     * @param splitters sub-hyperplanes that did split the current one
      */
-    private void addOutsideTouching(final SubHyperplane<S> sub) {
+    private void addOutsideTouching(final SubHyperplane<S> sub,
+                                    final List<BSPTree<S>> splitters) {
         if (outsideTouching == null) {
             outsideTouching = sub;
         } else {
             outsideTouching = outsideTouching.reunite(sub);
         }
+        outsideSplitters.addAll(splitters);
     }
 
     /** Add a part of the cut sub-hyperplane known to touch an inside cell.
      * @param sub part of the cut sub-hyperplane known to touch an inside cell
+     * @param splitters sub-hyperplanes that did split the current one
      */
-    private void addInsideTouching(final SubHyperplane<S> sub) {
+    private void addInsideTouching(final SubHyperplane<S> sub,
+                                   final List<BSPTree<S>> splitters) {
         if (insideTouching == null) {
             insideTouching = sub;
         } else {
             insideTouching = insideTouching.reunite(sub);
         }
+        insideSplitters.addAll(splitters);
     }
 
     /** Check if the cut sub-hyperplane touches outside cells.
@@ -129,6 +150,17 @@ class Characterization<S extends Space> {
         return outsideTouching;
     }
 
+    /** Get the nodes that were used to split the outside touching part.
+     * <p>
+     * Splitting nodes are internal nodes (i.e. they have a non-null
+     * cut sub-hyperplane).
+     * </p>
+     * @return nodes that were used to split the outside touching part
+     */
+    public NodesSet<S> getOutsideSplitters() {
+        return outsideSplitters;
+    }
+
     /** Check if the cut sub-hyperplane touches inside cells.
      * @return true if the cut sub-hyperplane touches inside cells
      */
@@ -144,4 +176,15 @@ class Characterization<S extends Space> {
         return insideTouching;
     }
 
+    /** Get the nodes that were used to split the inside touching part.
+     * <p>
+     * Splitting nodes are internal nodes (i.e. they have a non-null
+     * cut sub-hyperplane).
+     * </p>
+     * @return nodes that were used to split the inside touching part
+     */
+    public NodesSet<S> getInsideSplitters() {
+        return insideSplitters;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java
new file mode 100644
index 0000000..688279a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/geometry/partitioning/NodesSet.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math3.geometry.partitioning;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.math3.geometry.Space;
+
+/** Set of {@link BSPTree BSP tree} nodes.
+ * @see BoundaryAttribute
+ * @param <S> Type of the space.
+ * @since 3.4
+ */
+public class NodesSet<S extends Space> implements Iterable<BSPTree<S>> {
+
+    /** List of sub-hyperplanes. */
+    private List<BSPTree<S>> list;
+
+    /** Simple constructor.
+     */
+    public NodesSet() {
+        list = new ArrayList<BSPTree<S>>();
+    }
+
+    /** Add a node if not already known.
+     * @param node node to add
+     */
+    public void add(final BSPTree<S> node) {
+
+        for (final BSPTree<S> existing : list) {
+            if (node == existing) {
+                // the node is already known, don't add it
+                return;
+            }
+        }
+
+        // the node was not known, add it
+        list.add(node);
+
+    }
+
+    /** Add nodes if they are not already known.
+     * @param iterator nodes iterator
+     */
+    public void addAll(final Iterable<BSPTree<S>> iterator) {
+        for (final BSPTree<S> node : iterator) {
+            add(node);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Iterator<BSPTree<S>> iterator() {
+        return list.iterator();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-math/blob/6525cc40/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java
 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java
index 47b28bd..8cf2603 100644
--- 
a/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java
+++ 
b/src/main/java/org/apache/commons/math3/geometry/partitioning/RegionFactory.java
@@ -16,6 +16,9 @@
  */
 package org.apache.commons.math3.geometry.partitioning;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.commons.math3.geometry.Point;
 import org.apache.commons.math3.geometry.Space;
 import org.apache.commons.math3.geometry.euclidean.twod.PolygonsSet;
@@ -129,6 +132,11 @@ public class RegionFactory<S extends Space> {
      * region independent region will be built
      * @return a new region, complement of the specified one
      */
+    /** Get the complement of the region (exchanged interior/exterior).
+     * @param region region to complement, it will not modified, a new
+     * region independent region will be built
+     * @return a new region, complement of the specified one
+     */
     public Region<S> getComplement(final Region<S> region) {
         return region.buildNew(recurseComplement(region.getTree(false)));
     }
@@ -138,24 +146,62 @@ public class RegionFactory<S extends Space> {
      * @return new tree, complement of the node
      */
     private BSPTree<S> recurseComplement(final BSPTree<S> node) {
-        if (node.getCut() == null) {
-            return new BSPTree<S>(((Boolean) node.getAttribute()) ? 
Boolean.FALSE : Boolean.TRUE);
+
+        // transform the tree, except for boundary attribute splitters
+        final Map<BSPTree<S>, BSPTree<S>> map = new HashMap<BSPTree<S>, 
BSPTree<S>>();
+        final BSPTree<S> transformedTree = recurseComplement(node, map);
+
+        // set up the boundary attributes splitters
+        for (final Map.Entry<BSPTree<S>, BSPTree<S>> entry : map.entrySet()) {
+            if (entry.getKey().getCut() != null) {
+                @SuppressWarnings("unchecked")
+                BoundaryAttribute<S> original = (BoundaryAttribute<S>) 
entry.getKey().getAttribute();
+                if (original != null) {
+                    @SuppressWarnings("unchecked")
+                    BoundaryAttribute<S> transformed = (BoundaryAttribute<S>) 
entry.getValue().getAttribute();
+                    for (final BSPTree<S> splitter : original.getSplitters()) {
+                        transformed.getSplitters().add(map.get(splitter));
+                    }
+                }
+            }
         }
 
-        @SuppressWarnings("unchecked")
-        BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) 
node.getAttribute();
-        if (attribute != null) {
-            final SubHyperplane<S> plusOutside =
-                (attribute.getPlusInside() == null) ? null : 
attribute.getPlusInside().copySelf();
-            final SubHyperplane<S> plusInside  =
-                (attribute.getPlusOutside() == null) ? null : 
attribute.getPlusOutside().copySelf();
-            attribute = new BoundaryAttribute<S>(plusOutside, plusInside);
+        return transformedTree;
+
+    }
+
+    /** Recursively build the complement of a BSP tree.
+     * @param node current node of the original tree
+     * @param map transformed nodes map
+     * @return new tree, complement of the node
+     */
+    private BSPTree<S> recurseComplement(final BSPTree<S> node,
+                                         final Map<BSPTree<S>, BSPTree<S>> 
map) {
+
+        final BSPTree<S> transformedNode;
+        if (node.getCut() == null) {
+            transformedNode = new BSPTree<S>(((Boolean) node.getAttribute()) ? 
Boolean.FALSE : Boolean.TRUE);
+        } else {
+
+            @SuppressWarnings("unchecked")
+            BoundaryAttribute<S> attribute = (BoundaryAttribute<S>) 
node.getAttribute();
+            if (attribute != null) {
+                final SubHyperplane<S> plusOutside =
+                        (attribute.getPlusInside() == null) ? null : 
attribute.getPlusInside().copySelf();
+                final SubHyperplane<S> plusInside  =
+                        (attribute.getPlusOutside() == null) ? null : 
attribute.getPlusOutside().copySelf();
+                // we start with an empty list of splitters, it will be filled 
in out of recursion
+                attribute = new BoundaryAttribute<S>(plusOutside, plusInside, 
new NodesSet<S>());
+            }
+
+            transformedNode = new BSPTree<S>(node.getCut().copySelf(),
+                                             recurseComplement(node.getPlus(), 
 map),
+                                             
recurseComplement(node.getMinus(), map),
+                                             attribute);
         }
 
-        return new BSPTree<S>(node.getCut().copySelf(),
-                              recurseComplement(node.getPlus()),
-                              recurseComplement(node.getMinus()),
-                              attribute);
+        map.put(node, transformedNode);
+        return transformedNode;
 
     }
 

Reply via email to