Skip to content
Permalink
Browse files
Added splitters in Boundary attributes.
  • Loading branch information
Luc Maisonobe committed Dec 2, 2014
1 parent 26ee481 commit 6525cc4035fe8df3308c03f0ca9f450728e122ac
Showing 8 changed files with 338 additions and 68 deletions.
@@ -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>
@@ -19,7 +19,9 @@
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 @@ protected Location checkPoint(final BSPTree<S> node, final Point<S> point) {
/** {@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 @@ private SubHyperplane<S> recurseIntersection(final BSPTree<S> node, final SubHyp
* 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;

}

@@ -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 AbstractSubHyperplane<S, T> reunite(final SubHyperplane<S> other) {
*/
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;

}

@@ -45,18 +45,43 @@
*/
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
* 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)
* @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 SubHyperplane<S> getPlusInside() {
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;
}

}
@@ -28,11 +28,6 @@
*/
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 @@ public void visitInternalNode(BSPTree<S> node) {

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 @@ public void visitInternalNode(BSPTree<S> node) {
// 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 @@ public void visitInternalNode(BSPTree<S> node) {
// 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));

}

0 comments on commit 6525cc4

Please sign in to comment.