This commit is contained in:
Gilles 2014-12-02 12:06:10 +01:00
commit e11c00085c
5 changed files with 156 additions and 25 deletions

View File

@ -73,6 +73,9 @@ 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 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). counterpart in either Math or StrictMath (cf. MATH-740 and MATH-901).
"> ">
<action dev="luc" type="fix" issue="MATH-1162" >
Fixed a problem with vanishing cut sub-hyperplanes during BSP tree merging.
</action>
<action dev="erans" type="fix" issue="MATH-1167" due-to="Neil Ireson"> <action dev="erans" type="fix" issue="MATH-1167" due-to="Neil Ireson">
"o.a.c.m.stat.regression.OLSMultipleLinearRegression": Use threshold "o.a.c.m.stat.regression.OLSMultipleLinearRegression": Use threshold
when performing "QRDecomposition". when performing "QRDecomposition".

View File

@ -710,7 +710,8 @@ public class PolygonsSet extends AbstractRegion<Euclidean2D, Euclidean1D> {
int i = 0; int i = 0;
for (final List<ComparableSegment> loop : loops) { for (final List<ComparableSegment> loop : loops) {
if (loop.size() < 2) { if (loop.size() < 2 ||
(loop.size() == 2 && loop.get(0).getStart() == null && loop.get(1).getEnd() == null)) {
// single infinite line // single infinite line
final Line line = loop.get(0).getLine(); final Line line = loop.get(0).getLine();
vertices[i++] = new Vector2D[] { vertices[i++] = new Vector2D[] {

View File

@ -19,7 +19,9 @@ package org.apache.commons.math3.geometry.partitioning;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.math3.exception.MathIllegalStateException;
import org.apache.commons.math3.exception.MathInternalError; import org.apache.commons.math3.exception.MathInternalError;
import org.apache.commons.math3.exception.util.LocalizedFormats;
import org.apache.commons.math3.geometry.Point; import org.apache.commons.math3.geometry.Point;
import org.apache.commons.math3.geometry.Space; import org.apache.commons.math3.geometry.Space;
import org.apache.commons.math3.geometry.Vector; import org.apache.commons.math3.geometry.Vector;
@ -465,8 +467,7 @@ public class BSPTree<S extends Space> {
minus.merge(merged.minus, leafMerger, merged, false); minus.merge(merged.minus, leafMerger, merged, false);
merged.condense(); merged.condense();
if (merged.cut != null) { if (merged.cut != null) {
merged.cut = merged.cut = merged.fitToCell(merged.cut.getHyperplane().wholeHyperplane());
merged.fitToCell(merged.cut.getHyperplane().wholeHyperplane());
} }
return merged; return merged;
@ -526,6 +527,27 @@ public class BSPTree<S extends Space> {
} }
/** This interface handles the corner cases when an internal node cut sub-hyperplane vanishes.
* <p>
* Such cases happens for example when a cut sub-hyperplane is inserted into
* another tree (during a merge operation), and is split in several parts,
* some of which becomes smaller than the tolerance. The corresponding node
* as then no cut sub-hyperplane anymore, but does have children. This interface
* specifies how to handle this situation.
* setting
* </p>
* @since 3.4
*/
public interface VanishingCutHandler<S extends Space> {
/** Fix a node with both vanished cut and children.
* @param node node to fix
* @return fixed node
*/
BSPTree<S> fixNode(BSPTree<S> node);
}
/** Split a BSP tree by an external sub-hyperplane. /** Split a BSP tree by an external sub-hyperplane.
* <p>Split a tree in two halves, on each side of the * <p>Split a tree in two halves, on each side of the
* sub-hyperplane. The instance is not modified.</p> * sub-hyperplane. The instance is not modified.</p>
@ -547,8 +569,7 @@ public class BSPTree<S extends Space> {
public BSPTree<S> split(final SubHyperplane<S> sub) { public BSPTree<S> split(final SubHyperplane<S> sub) {
if (cut == null) { if (cut == null) {
return new BSPTree<S>(sub, copySelf(), return new BSPTree<S>(sub, copySelf(), new BSPTree<S>(attribute), null);
new BSPTree<S>(attribute), null);
} }
final Hyperplane<S> cHyperplane = cut.getHyperplane(); final Hyperplane<S> cHyperplane = cut.getHyperplane();
@ -620,8 +641,33 @@ public class BSPTree<S extends Space> {
* resulting tree should be the plus child of its parent, ignored if * resulting tree should be the plus child of its parent, ignored if
* parentTree is null * parentTree is null
* @see LeafMerger * @see LeafMerger
* @deprecated as of 3.4, replaced with {@link #insertInTree(BSPTree, boolean, VanishingCutHandler)}
*/ */
@Deprecated
public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild) { public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild) {
insertInTree(parentTree, isPlusChild, new VanishingCutHandler<S>() {
/** {@inheritDoc} */
public BSPTree<S> fixNode(BSPTree<S> node) {
// the cut should not be null
throw new MathIllegalStateException(LocalizedFormats.NULL_NOT_ALLOWED);
}
});
}
/** Insert the instance into another tree.
* <p>The instance itself is modified so its former parent should
* not be used anymore.</p>
* @param parentTree parent tree to connect to (may be null)
* @param isPlusChild if true and if parentTree is not null, the
* resulting tree should be the plus child of its parent, ignored if
* parentTree is null
* @param vanishingHandler handler to use for handling very rare corner
* cases of vanishing cut sub-hyperplanes in internal nodes during merging
* @see LeafMerger
* @since 3.4
*/
public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild,
final VanishingCutHandler<S> vanishingHandler) {
// set up parent/child links // set up parent/child links
parent = parentTree; parent = parentTree;
@ -646,12 +692,21 @@ public class BSPTree<S extends Space> {
// on the wrong side of this parent hyperplane // on the wrong side of this parent hyperplane
if (tree == tree.parent.plus) { if (tree == tree.parent.plus) {
cut = cut.split(hyperplane).getPlus(); cut = cut.split(hyperplane).getPlus();
plus.chopOffMinus(hyperplane); plus.chopOffMinus(hyperplane, vanishingHandler);
minus.chopOffMinus(hyperplane); minus.chopOffMinus(hyperplane, vanishingHandler);
} else { } else {
cut = cut.split(hyperplane).getMinus(); cut = cut.split(hyperplane).getMinus();
plus.chopOffPlus(hyperplane); plus.chopOffPlus(hyperplane, vanishingHandler);
minus.chopOffPlus(hyperplane); minus.chopOffPlus(hyperplane, vanishingHandler);
}
if (cut == null) {
// the cut sub-hyperplane has vanished
final BSPTree<S> fixed = vanishingHandler.fixNode(this);
cut = fixed.cut;
plus = fixed.plus;
minus = fixed.minus;
attribute = fixed.attribute;
} }
} }
@ -710,12 +765,25 @@ public class BSPTree<S extends Space> {
* the minus side of the chopping hyperplane are discarded, only the * the minus side of the chopping hyperplane are discarded, only the
* parts on the plus side remain.</p> * parts on the plus side remain.</p>
* @param hyperplane chopping hyperplane * @param hyperplane chopping hyperplane
* @param vanishingHandler handler to use for handling very rare corner
* cases of vanishing cut sub-hyperplanes in internal nodes during merging
*/ */
private void chopOffMinus(final Hyperplane<S> hyperplane) { private void chopOffMinus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) {
if (cut != null) { if (cut != null) {
cut = cut.split(hyperplane).getPlus(); cut = cut.split(hyperplane).getPlus();
plus.chopOffMinus(hyperplane); plus.chopOffMinus(hyperplane, vanishingHandler);
minus.chopOffMinus(hyperplane); minus.chopOffMinus(hyperplane, vanishingHandler);
if (cut == null) {
// the cut sub-hyperplane has vanished
final BSPTree<S> fixed = vanishingHandler.fixNode(this);
cut = fixed.cut;
plus = fixed.plus;
minus = fixed.minus;
attribute = fixed.attribute;
}
} }
} }
@ -724,12 +792,25 @@ public class BSPTree<S extends Space> {
* the plus side of the chopping hyperplane are discarded, only the * the plus side of the chopping hyperplane are discarded, only the
* parts on the minus side remain.</p> * parts on the minus side remain.</p>
* @param hyperplane chopping hyperplane * @param hyperplane chopping hyperplane
* @param vanishingHandler handler to use for handling very rare corner
* cases of vanishing cut sub-hyperplanes in internal nodes during merging
*/ */
private void chopOffPlus(final Hyperplane<S> hyperplane) { private void chopOffPlus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) {
if (cut != null) { if (cut != null) {
cut = cut.split(hyperplane).getMinus(); cut = cut.split(hyperplane).getMinus();
plus.chopOffPlus(hyperplane); plus.chopOffPlus(hyperplane, vanishingHandler);
minus.chopOffPlus(hyperplane); minus.chopOffPlus(hyperplane, vanishingHandler);
if (cut == null) {
// the cut sub-hyperplane has vanished
final BSPTree<S> fixed = vanishingHandler.fixNode(this);
cut = fixed.cut;
plus = fixed.plus;
minus = fixed.minus;
attribute = fixed.attribute;
}
} }
} }

View File

@ -17,6 +17,7 @@
package org.apache.commons.math3.geometry.partitioning; package org.apache.commons.math3.geometry.partitioning;
import org.apache.commons.math3.geometry.Space; import org.apache.commons.math3.geometry.Space;
import org.apache.commons.math3.geometry.partitioning.BSPTree.VanishingCutHandler;
/** This class is a factory for {@link Region}. /** This class is a factory for {@link Region}.
@ -162,16 +163,16 @@ public class RegionFactory<S extends Space> {
final boolean isPlusChild, final boolean leafFromInstance) { final boolean isPlusChild, final boolean leafFromInstance) {
if ((Boolean) leaf.getAttribute()) { if ((Boolean) leaf.getAttribute()) {
// the leaf node represents an inside cell // the leaf node represents an inside cell
leaf.insertInTree(parentTree, isPlusChild); leaf.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
return leaf; return leaf;
} }
// the leaf node represents an outside cell // the leaf node represents an outside cell
tree.insertInTree(parentTree, isPlusChild); tree.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(false));
return tree; return tree;
} }
} }
/** BSP tree leaf merger computing union of two regions. */ /** BSP tree leaf merger computing intersection of two regions. */
private class IntersectionMerger implements BSPTree.LeafMerger<S> { private class IntersectionMerger implements BSPTree.LeafMerger<S> {
/** {@inheritDoc} */ /** {@inheritDoc} */
public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree, public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
@ -179,16 +180,16 @@ public class RegionFactory<S extends Space> {
final boolean isPlusChild, final boolean leafFromInstance) { final boolean isPlusChild, final boolean leafFromInstance) {
if ((Boolean) leaf.getAttribute()) { if ((Boolean) leaf.getAttribute()) {
// the leaf node represents an inside cell // the leaf node represents an inside cell
tree.insertInTree(parentTree, isPlusChild); tree.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
return tree; return tree;
} }
// the leaf node represents an outside cell // the leaf node represents an outside cell
leaf.insertInTree(parentTree, isPlusChild); leaf.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(false));
return leaf; return leaf;
} }
} }
/** BSP tree leaf merger computing union of two regions. */ /** BSP tree leaf merger computing symmetric difference (exclusive or) of two regions. */
private class XorMerger implements BSPTree.LeafMerger<S> { private class XorMerger implements BSPTree.LeafMerger<S> {
/** {@inheritDoc} */ /** {@inheritDoc} */
public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree, public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
@ -199,12 +200,12 @@ public class RegionFactory<S extends Space> {
// the leaf node represents an inside cell // the leaf node represents an inside cell
t = recurseComplement(t); t = recurseComplement(t);
} }
t.insertInTree(parentTree, isPlusChild); t.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
return t; return t;
} }
} }
/** BSP tree leaf merger computing union of two regions. */ /** BSP tree leaf merger computing difference of two regions. */
private class DifferenceMerger implements BSPTree.LeafMerger<S> { private class DifferenceMerger implements BSPTree.LeafMerger<S> {
/** {@inheritDoc} */ /** {@inheritDoc} */
public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree, public BSPTree<S> merge(final BSPTree<S> leaf, final BSPTree<S> tree,
@ -214,13 +215,13 @@ public class RegionFactory<S extends Space> {
// the leaf node represents an inside cell // the leaf node represents an inside cell
final BSPTree<S> argTree = final BSPTree<S> argTree =
recurseComplement(leafFromInstance ? tree : leaf); recurseComplement(leafFromInstance ? tree : leaf);
argTree.insertInTree(parentTree, isPlusChild); argTree.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
return argTree; return argTree;
} }
// the leaf node represents an outside cell // the leaf node represents an outside cell
final BSPTree<S> instanceTree = final BSPTree<S> instanceTree =
leafFromInstance ? leaf : tree; leafFromInstance ? leaf : tree;
instanceTree.insertInTree(parentTree, isPlusChild); instanceTree.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(false));
return instanceTree; return instanceTree;
} }
} }
@ -244,4 +245,30 @@ public class RegionFactory<S extends Space> {
} }
/** Handler replacing nodes with vanishing cuts with leaf nodes. */
private class VanishingToLeaf implements VanishingCutHandler<S> {
/** Inside/outside indocator to use for ambiguous nodes. */
private final boolean inside;
/** Simple constructor.
* @param inside inside/outside indocator to use for ambiguous nodes
*/
public VanishingToLeaf(final boolean inside) {
this.inside = inside;
}
/** {@inheritDoc} */
public BSPTree<S> fixNode(final BSPTree<S> node) {
if (node.getPlus().getAttribute().equals(node.getMinus().getAttribute())) {
// no ambiguity
return new BSPTree<S>(node.getPlus().getAttribute());
} else {
// ambiguous node
return new BSPTree<S>(inside);
}
}
}
} }

View File

@ -1088,6 +1088,25 @@ public class PolygonsSetTest {
} }
} }
@Test
public void testIssue1162() {
PolygonsSet p = new PolygonsSet(1.0e-10,
new Vector2D(4.267199999996532, -11.928637756014894),
new Vector2D(4.267200000026445, -14.12360595809307),
new Vector2D(9.144000000273694, -14.12360595809307),
new Vector2D(9.144000000233383, -11.928637756020067));
PolygonsSet w = new PolygonsSet(1.0e-10,
new Vector2D(2.56735636510452512E-9, -11.933116461089332),
new Vector2D(2.56735636510452512E-9, -12.393225665247766),
new Vector2D(2.56735636510452512E-9, -27.785625665247778),
new Vector2D(4.267200000030211, -27.785625665247778),
new Vector2D(4.267200000030211, -11.933116461089332));
Assert.assertFalse(p.contains(w));
}
private PolygonsSet buildSet(Vector2D[][] vertices) { private PolygonsSet buildSet(Vector2D[][] vertices) {
ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>(); ArrayList<SubHyperplane<Euclidean2D>> edges = new ArrayList<SubHyperplane<Euclidean2D>>();
for (int i = 0; i < vertices.length; ++i) { for (int i = 0; i < vertices.length; ++i) {