Replaced Characterization by an internal class in AbstractRegion.

Note that the suppresses class was a package private one and did not
belong to the public API. It was used only when building the boundaries
in the AbstractRegion class. The change simplifies the package and makes
better use of the existing visitor pattern.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1345328 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2012-06-01 20:02:04 +00:00
parent 06c509b3b8
commit e0885ee01f
2 changed files with 95 additions and 153 deletions

View File

@ -16,6 +16,7 @@
*/
package org.apache.commons.math3.geometry.partitioning;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
@ -278,94 +279,125 @@ public abstract class AbstractRegion<S extends Space, T extends Space> implement
public BSPTree<S> getTree(final boolean includeBoundaryAttributes) {
if (includeBoundaryAttributes && (tree.getCut() != null) && (tree.getAttribute() == null)) {
// we need to compute the boundary attributes
recurseBuildBoundary(tree);
tree.visit(new BoundaryBuilder<S>());
}
return tree;
}
/** Recursively build the boundary shell tree.
* @param node current node in the inout tree
/** Visitor building boundary shell tree.
* <p>
* The boundary shell is represented as {@link BoundaryAttribute boundary attributes}
* at each internal node.
* </p>
*/
private void recurseBuildBoundary(final BSPTree<S> node) {
if (node.getCut() != null) {
private static class BoundaryBuilder<S extends Space> implements BSPTreeVisitor<S> {
/** {@inheritDoc} */
public Order visitOrder(BSPTree<S> node) {
return Order.PLUS_MINUS_SUB;
}
/** {@inheritDoc} */
public void visitInternalNode(BSPTree<S> node) {
SubHyperplane<S> plusOutside = null;
SubHyperplane<S> plusInside = null;
// characterize the cut sub-hyperplane,
// first with respect to the plus sub-tree
final Characterization<S> plusChar = new Characterization<S>();
@SuppressWarnings("unchecked")
final SubHyperplane<S>[] plusChar = (SubHyperplane<S>[]) Array.newInstance(SubHyperplane.class, 2);
characterize(node.getPlus(), node.getCut().copySelf(), plusChar);
if (plusChar.hasOut()) {
// plusChar.getOut() corresponds to a subset of the cut
// sub-hyperplane known to have outside cells on its plus
// side, we want to check if parts of this subset do have
// inside cells on their minus side
final Characterization<S> minusChar = new Characterization<S>();
characterize(node.getMinus(), plusChar.getOut(), minusChar);
if (minusChar.hasIn()) {
plusOutside = minusChar.getIn();
if (plusChar[0] != null && !plusChar[0].isEmpty()) {
// plusChar[0] corresponds to a subset of the cut sub-hyperplane known to have
// outside cells on its plus side, we want to check if parts of this subset
// do have inside cells on their minus side
@SuppressWarnings("unchecked")
final SubHyperplane<S>[] minusChar = (SubHyperplane<S>[]) Array.newInstance(SubHyperplane.class, 2);
characterize(node.getMinus(), plusChar[0], minusChar);
if (minusChar[1] != null && !minusChar[1].isEmpty()) {
// this part belongs to the boundary,
// it has the outside on its plus side and the inside on its minus side
plusOutside = minusChar[1];
}
}
if (plusChar.hasIn()) {
// plusChar.getIn() corresponds to a subset of the cut
// sub-hyperplane known to have inside cells on its plus
// side, we want to check if parts of this subset do have
// outside cells on their minus side
final Characterization<S> minusChar = new Characterization<S>();
characterize(node.getMinus(), plusChar.getIn(), minusChar);
if (minusChar.hasOut()) {
plusInside = minusChar.getOut();
if (plusChar[1] != null && !plusChar[1].isEmpty()) {
// plusChar[1] corresponds to a subset of the cut sub-hyperplane known to have
// inside cells on its plus side, we want to check if parts of this subset
// do have outside cells on their minus side
@SuppressWarnings("unchecked")
final SubHyperplane<S>[] minusChar = (SubHyperplane<S>[]) Array.newInstance(SubHyperplane.class, 2);
characterize(node.getMinus(), plusChar[1], minusChar);
if (minusChar[0] != null && !minusChar[0].isEmpty()) {
// this part belongs to the boundary,
// it has the inside on its plus side and the outside on its minus side
plusInside = minusChar[0];
}
}
// set the boundary attribute at non-leaf nodes
node.setAttribute(new BoundaryAttribute<S>(plusOutside, plusInside));
recurseBuildBoundary(node.getPlus());
recurseBuildBoundary(node.getMinus());
}
}
/** Filter the parts of an hyperplane belonging to the boundary.
* <p>The filtering consist in splitting the specified
* sub-hyperplane into several parts lying in inside and outside
* cells of the tree. The principle is to call this method twice for
* each cut sub-hyperplane in the tree, once one the plus node and
* once on the minus node. The parts that have the same flag
* (inside/inside or outside/outside) do not belong to the boundary
* while parts that have different flags (inside/outside or
* outside/inside) do belong to the boundary.</p>
* @param node current BSP tree node
* @param sub sub-hyperplane to characterize
* @param characterization placeholder where to put the characterized parts
*/
private void characterize(final BSPTree<S> node, final SubHyperplane<S> sub,
final Characterization<S> characterization) {
if (node.getCut() == null) {
// we have reached a leaf node
final boolean inside = (Boolean) node.getAttribute();
characterization.add(sub, inside);
} else {
final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
switch (sub.side(hyperplane)) {
case PLUS:
characterize(node.getPlus(), sub, characterization);
break;
case MINUS:
characterize(node.getMinus(), sub, characterization);
break;
case BOTH:
final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
characterize(node.getPlus(), split.getPlus(), characterization);
characterize(node.getMinus(), split.getMinus(), characterization);
break;
default:
// this should not happen
throw new MathInternalError();
/** {@inheritDoc} */
public void visitLeafNode(BSPTree<S> node) {
}
/** Filter the parts of an hyperplane belonging to the boundary.
* <p>The filtering consist in splitting the specified
* sub-hyperplane into several parts lying in inside and outside
* cells of the tree. The principle is to call this method twice for
* each cut sub-hyperplane in the tree, once one the plus node and
* once on the minus node. The parts that have the same flag
* (inside/inside or outside/outside) do not belong to the boundary
* while parts that have different flags (inside/outside or
* outside/inside) do belong to the boundary.</p>
* @param node current BSP tree node
* @param sub sub-hyperplane to characterize
* @param characterization placeholder where to put the characterized parts
*/
private void characterize(final BSPTree<S> node, final SubHyperplane<S> sub,
final SubHyperplane<S>[] characterization) {
if (node.getCut() == null) {
// we have reached a leaf node
final boolean inside = (Boolean) node.getAttribute();
if (inside) {
if (characterization[1] == null) {
characterization[1] = sub;
} else {
characterization[1] = characterization[1].reunite(sub);
}
} else {
if (characterization[0] == null) {
characterization[0] = sub;
} else {
characterization[0] = characterization[0].reunite(sub);
}
}
} else {
final Hyperplane<S> hyperplane = node.getCut().getHyperplane();
switch (sub.side(hyperplane)) {
case PLUS:
characterize(node.getPlus(), sub, characterization);
break;
case MINUS:
characterize(node.getMinus(), sub, characterization);
break;
case BOTH:
final SubHyperplane.SplitSubHyperplane<S> split = sub.split(hyperplane);
characterize(node.getPlus(), split.getPlus(), characterization);
characterize(node.getMinus(), split.getMinus(), characterization);
break;
default:
// this should not happen
throw new MathInternalError();
}
}
}
}
/** {@inheritDoc} */

View File

@ -1,90 +0,0 @@
/*
* 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 org.apache.commons.math3.geometry.Space;
/** Characterization of a sub-hyperplane.
* @param <S> Type of the space.
* @version $Id$
* @since 3.0
*/
class Characterization<S extends Space> {
/** Parts of the sub-hyperplane that have inside cells on the tested side. */
private SubHyperplane<S> in;
/** Parts of the sub-hyperplane that have outside cells on the tested side. */
private SubHyperplane<S> out;
/** Create an empty characterization of a sub-hyperplane.
*/
public Characterization() {
in = null;
out = null;
}
/** Check if the sub-hyperplane that have inside cells on the tested side.
* @return true if the sub-hyperplane that have inside cells on the tested side
*/
public boolean hasIn() {
return (in != null) && (!in.isEmpty());
}
/** Get the parts of the sub-hyperplane that have inside cells on the tested side.
* @return parts of the sub-hyperplane that have inside cells on the tested side
*/
public SubHyperplane<S> getIn() {
return in;
}
/** Check if the sub-hyperplane that have outside cells on the tested side.
* @return true if the sub-hyperplane that have outside cells on the tested side
*/
public boolean hasOut() {
return (out != null) && (!out.isEmpty());
}
/** Get the parts of the sub-hyperplane that have outside cells on the tested side.
* @return parts of the sub-hyperplane that have outside cells on the tested side
*/
public SubHyperplane<S> getOut() {
return out;
}
/** Add a part of the sub-hyperplane known to have inside or outside cell on the tested side.
* @param sub part of the sub-hyperplane to add
* @param inside if true, the part added as an inside cell on the tested side, otherwise
* it has an outside cell on the tested side
*/
public void add(final SubHyperplane<S> sub, final boolean inside) {
if (inside) {
if (in == null) {
in = sub;
} else {
in = in.reunite(sub);
}
} else {
if (out == null) {
out = sub;
} else {
out = out.reunite(sub);
}
}
}
}