Author: luc
Date: Thu Jan 9 17:08:00 2014
New Revision: 1556880
URL: http://svn.apache.org/r1556880
Log:
Improved robustness of 1-sphere regions (ArcsSet) at 0/2\pi crossing.
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
commons/proper/math/trunk/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java?rev=1556880&r1=1556879&r2=1556880&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java
Thu Jan 9 17:08:00 2014
@@ -115,6 +115,7 @@ public enum LocalizedFormats implements
GCD_OVERFLOW_64_BITS("overflow: gcd({0}, {1}) is 2^63"),
HOLE_BETWEEN_MODELS_TIME_RANGES("{0} wide hole between models time
ranges"),
ILL_CONDITIONED_OPERATOR("condition number {1} is too high "),
+ INCONSISTENT_STATE_AT_2_PI_WRAPPING("inconsistent state at 2\u03c0
wrapping"),
INDEX_LARGER_THAN_MAX("the index specified: {0} is larger than the current
maximal index {1}"),
INDEX_NOT_POSITIVE("index ({0}) is not positive"),
INDEX_OUT_OF_RANGE("index {0} out of allowed range [{1}, {2}]"),
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java?rev=1556880&r1=1556879&r2=1556880&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java
Thu Jan 9 17:08:00 2014
@@ -22,6 +22,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
import org.apache.commons.math3.exception.MathInternalError;
import org.apache.commons.math3.exception.NumberIsTooLargeException;
import org.apache.commons.math3.exception.util.LocalizedFormats;
@@ -80,9 +81,13 @@ public class ArcsSet extends AbstractReg
* {@code Boolean.TRUE} and {@code Boolean.FALSE}</p>
* @param tree inside/outside BSP tree representing the arcs set
* @param tolerance tolerance below which close sub-arcs are merged
together
+ * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not
+ * consistent across the \( 0, 2 \pi \) crossing
*/
- public ArcsSet(final BSPTree<Sphere1D> tree, final double tolerance) {
+ public ArcsSet(final BSPTree<Sphere1D> tree, final double tolerance)
+ throws InconsistentStateAt2PiWrapping {
super(tree, tolerance);
+ check2PiConsistency();
}
/** Build an arcs set from a Boundary REPresentation (B-rep).
@@ -104,9 +109,13 @@ public class ArcsSet extends AbstractReg
* space.</p>
* @param boundary collection of boundary elements
* @param tolerance tolerance below which close sub-arcs are merged
together
+ * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not
+ * consistent across the \( 0, 2 \pi \) crossing
*/
- public ArcsSet(final Collection<SubHyperplane<Sphere1D>> boundary, final
double tolerance) {
+ public ArcsSet(final Collection<SubHyperplane<Sphere1D>> boundary, final
double tolerance)
+ throws InconsistentStateAt2PiWrapping {
super(boundary, tolerance);
+ check2PiConsistency();
}
/** Build an inside/outside tree representing a single arc.
@@ -160,6 +169,70 @@ public class ArcsSet extends AbstractReg
}
+ /** Check consistency.
+ * @exception InconsistentStateAt2PiWrapping if the tree leaf nodes are not
+ * consistent across the \( 0, 2 \pi \) crossing
+ */
+ private void check2PiConsistency() throws InconsistentStateAt2PiWrapping {
+
+ // start search at the tree root
+ BSPTree<Sphere1D> root = getTree(false);
+ if (root.getCut() == null) {
+ return;
+ }
+
+ // find the inside/outside state before the smallest internal node
+ final Boolean stateBefore = (Boolean)
getFirstLeaf(root).getAttribute();
+
+ // find the inside/outside state after the largest internal node
+ final Boolean stateAfter = (Boolean) getLastLeaf(root).getAttribute();
+
+ if (stateBefore ^ stateAfter) {
+ throw new InconsistentStateAt2PiWrapping();
+ }
+
+ }
+
+ /** Get the first leaf node of a tree.
+ * @param root tree root
+ * @return first leaf node (i.e. node corresponding to the region just
after 0.0 radians)
+ */
+ private BSPTree<Sphere1D> getFirstLeaf(final BSPTree<Sphere1D> root) {
+
+ if (root.getCut() == null) {
+ return root;
+ }
+
+ // find the smallest internal node
+ BSPTree<Sphere1D> smallest = null;
+ for (BSPTree<Sphere1D> n = root; n != null; n =
previousInternalNode(n)) {
+ smallest = n;
+ }
+
+ return leafBefore(smallest);
+
+ }
+
+ /** Get the last leaf node of a tree.
+ * @param root tree root
+ * @return last leaf node (i.e. node corresponding to the region just
before \( 2 \pi \) radians)
+ */
+ private BSPTree<Sphere1D> getLastLeaf(final BSPTree<Sphere1D> root) {
+
+ if (root.getCut() == null) {
+ return root;
+ }
+
+ // find the largest internal node
+ BSPTree<Sphere1D> largest = null;
+ for (BSPTree<Sphere1D> n = root; n != null; n = nextInternalNode(n)) {
+ largest = n;
+ }
+
+ return leafAfter(largest);
+
+ }
+
/** Get the node corresponding to the first arc start.
* @return smallest internal node (i.e. first after 0.0 radians, in
trigonometric direction),
* or null if there are no internal nodes (i.e. the set is either empty or
covers the full circle)
@@ -173,11 +246,7 @@ public class ArcsSet extends AbstractReg
}
// walk tree until we find the smallest internal node
- BSPTree<Sphere1D> previous = previousInternalNode(node);
- while (previous != null) {
- node = previous;
- previous = previousInternalNode(node);
- }
+ node = getFirstLeaf(node).getParent();
// walk tree until we find an arc start
while (node != null && !isArcStart(node)) {
@@ -372,22 +441,6 @@ public class ArcsSet extends AbstractReg
return ((LimitAngle)
node.getCut().getHyperplane()).getLocation().getAlpha();
}
- /** Build a sub-hyperplane corresponding to an arc start.
- * @param alpha arc start
- * @return sub-hyperplane for start of arc
- */
- private SubLimitAngle arcStart(final double alpha) {
- return new LimitAngle(new S1Point(alpha), false,
getTolerance()).wholeHyperplane();
- }
-
- /** Build a sub-hyperplane corresponding to an arc end.
- * @param alpha arc end
- * @return sub-hyperplane for end of arc
- */
- private SubLimitAngle arcEnd(final double alpha) {
- return new LimitAngle(new S1Point(alpha), true,
getTolerance()).wholeHyperplane();
- }
-
/** {@inheritDoc} */
@Override
public ArcsSet buildNew(final BSPTree<Sphere1D> tree) {
@@ -454,7 +507,7 @@ public class ArcsSet extends AbstractReg
private final BSPTree<Sphere1D> firstStart;
/** Current node. */
- private BSPTree<Sphere1D> node;
+ private BSPTree<Sphere1D> current;
/** Sub-arc no yet returned. */
private double[] pending;
@@ -464,7 +517,7 @@ public class ArcsSet extends AbstractReg
public SubArcsIterator() {
firstStart = getFirstArcStart();
- node = firstStart;
+ current = firstStart;
if (firstStart == null) {
// the tree has a single node
@@ -476,13 +529,6 @@ public class ArcsSet extends AbstractReg
} else {
pending = null;
}
- } else if (previousInternalNode(firstStart) == null &&
nextInternalNode(firstStart) == null) {
- // the tree is a degenerate tree (probably build from a custom
collection of hyperplanes) with a single cut
- // we ignore the cut and consider the tree represents the full
circle
- node = null;
- pending = new double[] {
- 0, MathUtils.TWO_PI
- };
} else {
selectPending();
}
@@ -493,14 +539,14 @@ public class ArcsSet extends AbstractReg
private void selectPending() {
// look for the start of the arc
- BSPTree<Sphere1D> start = node;
+ BSPTree<Sphere1D> start = current;
while (start != null && !isArcStart(start)) {
start = nextInternalNode(start);
}
if (start == null) {
// we have exhausted the iterator
- node = null;
+ current = null;
pending = null;
return;
}
@@ -519,7 +565,7 @@ public class ArcsSet extends AbstractReg
};
// prepare search for next arc
- node = end;
+ current = end;
} else {
@@ -539,7 +585,7 @@ public class ArcsSet extends AbstractReg
};
// there won't be any other arcs
- node = null;
+ current = null;
}
@@ -619,8 +665,12 @@ public class ArcsSet extends AbstractReg
*/
public Split split(final Arc arc) {
- final List<SubHyperplane<Sphere1D>> minus = new
ArrayList<SubHyperplane<Sphere1D>>();
- final List<SubHyperplane<Sphere1D>> plus = new
ArrayList<SubHyperplane<Sphere1D>>();
+ final BSPTree<Sphere1D> minus = new BSPTree<Sphere1D>();
+ minus.setAttribute(Boolean.FALSE);
+ boolean minusIgnored = false;
+ final BSPTree<Sphere1D> plus = new BSPTree<Sphere1D>();
+ plus.setAttribute(Boolean.FALSE);
+ boolean plusIgnored = false;
final double reference = FastMath.PI + arc.getInf();
final double arcLength = arc.getSup() - arc.getInf();
@@ -631,57 +681,145 @@ public class ArcsSet extends AbstractReg
final double syncedEnd = a[1] - arcOffset;
if (syncedStart < arcLength) {
// the start point a[0] is in the minus part of the arc
- minus.add(arcStart(a[0]));
+ minusIgnored = addArcStart(minus, a[0], minusIgnored);
if (syncedEnd > arcLength) {
// the end point a[1] is past the end of the arc
// so we leave the minus part and enter the plus part
final double minusToPlus = arcLength + arcOffset;
- minus.add(arcEnd(minusToPlus));
- plus.add(arcStart(minusToPlus));
+ minusIgnored = addArcEnd(minus, minusToPlus, minusIgnored);
+ plusIgnored = addArcStart(plus, minusToPlus, plusIgnored);
if (syncedEnd > MathUtils.TWO_PI) {
// in fact the end point a[1] goes far enough that we
// leave the plus part of the arc and enter the minus
part again
final double plusToMinus = MathUtils.TWO_PI +
arcOffset;
- plus.add(arcEnd(plusToMinus));
- minus.add(arcStart(plusToMinus));
- minus.add(arcEnd(a[1]));
+ plusIgnored = addArcEnd(plus, plusToMinus,
plusIgnored);
+ minusIgnored = addArcStart(minus, plusToMinus,
minusIgnored);
+ minusIgnored = addArcEnd(minus, a[1], minusIgnored);
} else {
// the end point a[1] is in the plus part of the arc
- plus.add(arcEnd(a[1]));
+ plusIgnored = addArcEnd(plus, a[1], plusIgnored);
}
} else {
// the end point a[1] is in the minus part of the arc
- minus.add(arcEnd(a[1]));
+ minusIgnored = addArcEnd(minus, a[1], minusIgnored);
}
} else {
// the start point a[0] is in the plus part of the arc
- plus.add(arcStart(a[0]));
+ plusIgnored = addArcStart(plus, a[0], plusIgnored);
if (syncedEnd > MathUtils.TWO_PI) {
// the end point a[1] wraps around to the start of the arc
// so we leave the plus part and enter the minus part
final double plusToMinus = MathUtils.TWO_PI + arcOffset;
- plus.add(arcEnd(plusToMinus));
- minus.add(arcStart(plusToMinus));
+ plusIgnored = addArcEnd(plus, plusToMinus, plusIgnored);
+ minusIgnored = addArcStart(minus, plusToMinus,
minusIgnored);
if (syncedEnd > MathUtils.TWO_PI + arcLength) {
// in fact the end point a[1] goes far enough that we
// leave the minus part of the arc and enter the plus
part again
final double minusToPlus = MathUtils.TWO_PI +
arcLength + arcOffset;
- minus.add(arcEnd(minusToPlus));
- plus.add(arcStart(minusToPlus));
- plus.add(arcEnd(a[1]));
+ minusIgnored = addArcEnd(minus, minusToPlus,
minusIgnored);
+ plusIgnored = addArcStart(plus, minusToPlus,
plusIgnored);
+ plusIgnored = addArcEnd(plus, a[1], plusIgnored);
} else {
// the end point a[1] is in the minus part of the arc
- minus.add(arcEnd(a[1]));
+ minusIgnored = addArcEnd(minus, a[1], minusIgnored);
}
} else {
// the end point a[1] is in the plus part of the arc
- plus.add(arcEnd(a[1]));
+ plusIgnored = addArcEnd(plus, a[1], plusIgnored);
}
}
}
- return new Split(plus.isEmpty() ? null : new ArcsSet(plus,
getTolerance()),
- minus.isEmpty() ? null : new
ArcsSet(minus,getTolerance()));
+ return new Split(createSplitPart(plus, plusIgnored),
createSplitPart(minus, minusIgnored));
+
+ }
+
+ /** Add an arc start to a BSP tree under construction.
+ * <p>
+ * Note that this method <em>MUST</em> be called in increasing angle order.
+ * </p>
+ * @param tree BSP tree under construction
+ * @param alpha arc start
+ * @param ignored if true, some end points have been ignored previously
+ * @return true if some points have been ignored, taking this arc end into
account
+ */
+ private boolean addArcStart(final BSPTree<Sphere1D> tree, final double
alpha, final boolean ignored) {
+
+ final BSPTree<Sphere1D> last = getLastLeaf(tree);
+
+ if (alpha <= getTolerance()) {
+ // don't add a spurious cut hyperplane at the start of the circle,
+ last.setAttribute(Boolean.TRUE);
+ return true;
+ } else {
+ last.insertCut(new LimitAngle(new S1Point(alpha), false,
getTolerance()));
+ last.setAttribute(null);
+ last.getPlus().setAttribute(Boolean.FALSE);
+ last.getMinus().setAttribute(Boolean.TRUE);
+ return ignored;
+ }
+
+ }
+
+ /** Add an arc end to a BSP tree under construction.
+ * <p>
+ * Note that this method <em>MUST</em> be called in increasing angle order.
+ * </p>
+ * @param tree BSP tree under construction
+ * @param alpha arc end
+ * @param ignored if true, some end points have been ignored previously
+ * @return true if some points have been ignored, taking this arc end into
account
+ */
+ private boolean addArcEnd(final BSPTree<Sphere1D> tree, final double
alpha, final boolean ignored) {
+
+ final BSPTree<Sphere1D> last = getLastLeaf(tree);
+
+ if (alpha >= MathUtils.TWO_PI - getTolerance()) {
+
+ // don't add a spurious cut hyperplane at the end of the circle,
+ last.setAttribute(Boolean.TRUE);
+ return true;
+
+ } else {
+ last.insertCut(new LimitAngle(new S1Point(alpha), true,
getTolerance()));
+ last.setAttribute(null);
+ last.getPlus().setAttribute(Boolean.FALSE);
+ last.getMinus().setAttribute(Boolean.TRUE);
+ return ignored;
+ }
+
+ }
+
+ /** Create a split part.
+ * @param tree BSP tree containing the limit angles of the split part
+ * @param ignored if true, some end points have been ignored previously
+ * @return split part (may be null)
+ */
+ private ArcsSet createSplitPart(final BSPTree<Sphere1D> tree, final
boolean ignored) {
+
+ if (ignored) {
+ // ensure consistent state at 0 / 2 \pi crossing
+
+ final BSPTree<Sphere1D> first = getFirstLeaf(tree);
+ final boolean firstState = (Boolean) first.getAttribute();
+ final BSPTree<Sphere1D> last = getLastLeaf(tree);
+ final boolean lastState = (Boolean) last.getAttribute();
+ if (firstState ^ lastState) {
+ // there should be a real boundary at the crossing. Since it
is not accurately
+ // representable due to S1Point normalizing the angles between
0 (included)
+ // and 2 \pi (excluded), we insert it at the *beginning* of
the tree,
+ // with an angle forced to 0.0
+ first.insertCut(new LimitAngle(new S1Point(0.0), true,
getTolerance()));
+ first.getPlus().setAttribute(firstState);
+ first.getMinus().setAttribute(lastState);
+ }
+ }
+
+ if (tree.getCut() == null && !(Boolean) tree.getAttribute()) {
+ return null;
+ } else {
+ return new ArcsSet(tree, getTolerance());
+ }
}
@@ -722,4 +860,24 @@ public class ArcsSet extends AbstractReg
}
+ /** Specialized exception for inconsistent BSP tree state inconsistency.
+ * <p>
+ * This exception is thrown at {@link ArcsSet} construction time when the
+ * {@link Location inside/outside} state is not consistent at the 0,
+ * \(2 \pi \) crossing.
+ * </p>
+ */
+ public static class InconsistentStateAt2PiWrapping extends
MathIllegalArgumentException {
+
+ /** Serializable UID. */
+ private static final long serialVersionUID = 20140107L;
+
+ /** Simple constructor.
+ */
+ public InconsistentStateAt2PiWrapping() {
+ super(LocalizedFormats.INCONSISTENT_STATE_AT_2_PI_WRAPPING);
+ }
+
+ }
+
}
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java?rev=1556880&r1=1556879&r2=1556880&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java
Thu Jan 9 17:08:00 2014
@@ -59,7 +59,7 @@ public class LimitAngle implements Hyper
/** {@inheritDoc} */
public double getOffset(final Point<Sphere1D> point) {
- final double delta = ((S1Point) point).getAlpha()- location.getAlpha();
+ final double delta = ((S1Point) point).getAlpha() -
location.getAlpha();
return direct ? delta : -delta;
}
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java?rev=1556880&r1=1556879&r2=1556880&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
Thu Jan 9 17:08:00 2014
@@ -172,11 +172,12 @@ public class SphericalPolygonsSet extend
// build the edges
List<Edge> edges = new ArrayList<Edge>(n);
+ Vertex end = vArray[n - 1];
for (int i = 0; i < n; ++i) {
// get the endpoints of the edge
- final Vertex start = vArray[i];
- final Vertex end = vArray[(i + 1) % n];
+ final Vertex start = end;
+ end = vArray[i];
// get the circle supporting the edge, taking care not to recreate
it
// if it was already created earlier due to another edge being
aligned
Modified:
commons/proper/math/trunk/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties?rev=1556880&r1=1556879&r2=1556880&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties
(original)
+++
commons/proper/math/trunk/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties
Thu Jan 9 17:08:00 2014
@@ -87,6 +87,7 @@ GCD_OVERFLOW_32_BITS = d\u00e9passement
GCD_OVERFLOW_64_BITS = d\u00e9passement de capacit\u00e9 : le PGCD de {0} et
{1} vaut 2^63
HOLE_BETWEEN_MODELS_TIME_RANGES = trou de longueur {0} entre les domaines
temporels des mod\u00e8les
ILL_CONDITIONED_OPERATOR = le conditionnement {1} est trop \u00e9lev\u00e9
+INCONSISTENT_STATE_AT_2_PI_WRAPPING = \u00e9tat incoh\u00e9rent au niveau du
recollement \u00e0 2\u03c0
INDEX_LARGER_THAN_MAX = l''index sp\u00e9cifi\u00e9 ({0}) d\u00e9passe
l''index maximal courant ({1})
INDEX_NOT_POSITIVE = l''indice ({0}) n''est pas positif
INDEX_OUT_OF_RANGE = l''indice ({0}) est hors du domaine autoris\u00e9 [{1},
{2}]
Modified:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java?rev=1556880&r1=1556879&r2=1556880&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java
(original)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java
Thu Jan 9 17:08:00 2014
@@ -30,7 +30,7 @@ public class LocalizedFormatsTest {
@Test
public void testMessageNumber() {
- Assert.assertEquals(316, LocalizedFormats.values().length);
+ Assert.assertEquals(317, LocalizedFormats.values().length);
}
@Test
Modified:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java?rev=1556880&r1=1556879&r2=1556880&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java
(original)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java
Thu Jan 9 17:08:00 2014
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.math3.exception.NumberIsTooLargeException;
+import org.apache.commons.math3.geometry.partitioning.BSPTree;
import org.apache.commons.math3.geometry.partitioning.Region;
import org.apache.commons.math3.geometry.partitioning.Region.Location;
import org.apache.commons.math3.geometry.partitioning.RegionFactory;
@@ -67,6 +68,49 @@ public class ArcsSetTest {
Assert.assertEquals(2.3 + MathUtils.TWO_PI,
set.asList().get(0).getSup(), 1.0e-10);
}
+ @Test
+ public void testSplitOver2Pi() {
+ ArcsSet set = new ArcsSet(1.0e-10);
+ Arc arc = new Arc(1.5 * FastMath.PI, 2.5 * FastMath.PI, 1.0e-10);
+ ArcsSet.Split split = set.split(arc);
+ for (double alpha = 0; alpha <= MathUtils.TWO_PI; alpha += 0.01) {
+ S1Point p = new S1Point(alpha);
+ if (alpha < 0.5 * FastMath.PI || alpha > 1.5 * FastMath.PI) {
+ Assert.assertEquals(Location.OUTSIDE,
split.getPlus().checkPoint(p));
+ Assert.assertEquals(Location.INSIDE,
split.getMinus().checkPoint(p));
+ } else {
+ Assert.assertEquals(Location.INSIDE,
split.getPlus().checkPoint(p));
+ Assert.assertEquals(Location.OUTSIDE,
split.getMinus().checkPoint(p));
+ }
+ }
+ }
+
+ @Test
+ public void testSplitAtEnd() {
+ ArcsSet set = new ArcsSet(1.0e-10);
+ Arc arc = new Arc(FastMath.PI, MathUtils.TWO_PI, 1.0e-10);
+ ArcsSet.Split split = set.split(arc);
+ for (double alpha = 0.01; alpha < MathUtils.TWO_PI; alpha += 0.01) {
+ S1Point p = new S1Point(alpha);
+ if (alpha > FastMath.PI) {
+ Assert.assertEquals(Location.OUTSIDE,
split.getPlus().checkPoint(p));
+ Assert.assertEquals(Location.INSIDE,
split.getMinus().checkPoint(p));
+ } else {
+ Assert.assertEquals(Location.INSIDE,
split.getPlus().checkPoint(p));
+ Assert.assertEquals(Location.OUTSIDE,
split.getMinus().checkPoint(p));
+ }
+ }
+
+ S1Point zero = new S1Point(0.0);
+ Assert.assertEquals(Location.BOUNDARY,
split.getPlus().checkPoint(zero));
+ Assert.assertEquals(Location.BOUNDARY,
split.getMinus().checkPoint(zero));
+
+ S1Point pi = new S1Point(FastMath.PI);
+ Assert.assertEquals(Location.BOUNDARY,
split.getPlus().checkPoint(pi));
+ Assert.assertEquals(Location.BOUNDARY,
split.getMinus().checkPoint(pi));
+
+ }
+
@Test(expected=NumberIsTooLargeException.class)
public void testWrongInterval() {
new ArcsSet(1.2, 0.0, 1.0e-10);
@@ -122,7 +166,7 @@ public class ArcsSetTest {
public void testSpecialConstruction() {
List<SubHyperplane<Sphere1D>> boundary = new
ArrayList<SubHyperplane<Sphere1D>>();
boundary.add(new LimitAngle(new S1Point(0.0), false,
1.0e-10).wholeHyperplane());
- boundary.add(new LimitAngle(new S1Point(MathUtils.TWO_PI), true,
1.0e-10).wholeHyperplane());
+ boundary.add(new LimitAngle(new S1Point(MathUtils.TWO_PI - 1.0e-11),
true, 1.0e-10).wholeHyperplane());
ArcsSet set = new ArcsSet(boundary, 1.0e-10);
Assert.assertEquals(MathUtils.TWO_PI, set.getSize(), 1.0e-10);
Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20);
@@ -288,6 +332,53 @@ public class ArcsSetTest {
}
@Test
+ public void testEmptyTree() {
+ Assert.assertEquals(MathUtils.TWO_PI, new ArcsSet(new
BSPTree<Sphere1D>(Boolean.TRUE), 1.0e-10).getSize(), 1.0e-10);
+ }
+
+ @Test
+ public void testShiftedAngles() {
+ for (int k = -2; k < 3; ++k) {
+ SubLimitAngle l1 = new LimitAngle(new S1Point(1.0 + k *
MathUtils.TWO_PI), false, 1.0e-10).wholeHyperplane();
+ SubLimitAngle l2 = new LimitAngle(new S1Point(1.5 + k *
MathUtils.TWO_PI), true, 1.0e-10).wholeHyperplane();
+ ArcsSet set = new ArcsSet(new BSPTree<Sphere1D>(l1,
+ new
BSPTree<Sphere1D>(Boolean.FALSE),
+ new
BSPTree<Sphere1D>(l2,
+
new BSPTree<Sphere1D>(Boolean.FALSE),
+
new BSPTree<Sphere1D>(Boolean.TRUE),
+
null),
+ null),
+ 1.0e-10);
+ for (double alpha = 1.0e-6; alpha < MathUtils.TWO_PI; alpha +=
0.001) {
+ if (alpha < 1 || alpha > 1.5) {
+ Assert.assertEquals(Location.OUTSIDE, set.checkPoint(new
S1Point(alpha)));
+ } else {
+ Assert.assertEquals(Location.INSIDE, set.checkPoint(new
S1Point(alpha)));
+ }
+ }
+ }
+
+ }
+
+ @Test(expected=ArcsSet.InconsistentStateAt2PiWrapping.class)
+ public void testInconsistentState() {
+ SubLimitAngle l1 = new LimitAngle(new S1Point(1.0), false,
1.0e-10).wholeHyperplane();
+ SubLimitAngle l2 = new LimitAngle(new S1Point(2.0), true,
1.0e-10).wholeHyperplane();
+ SubLimitAngle l3 = new LimitAngle(new S1Point(3.0), false,
1.0e-10).wholeHyperplane();
+ new ArcsSet(new BSPTree<Sphere1D>(l1,
+ new BSPTree<Sphere1D>(Boolean.FALSE),
+ new BSPTree<Sphere1D>(l2,
+ new
BSPTree<Sphere1D>(l3,
+
new BSPTree<Sphere1D>(Boolean.FALSE),
+
new BSPTree<Sphere1D>(Boolean.TRUE),
+
null),
+ new
BSPTree<Sphere1D>(Boolean.TRUE),
+ null),
+ null),
+ 1.0e-10);
+ }
+
+ @Test
public void testSide() {
ArcsSet set = (ArcsSet) new RegionFactory<Sphere1D>().difference(new
ArcsSet(1.0, 6.0, 1.0e-10),
new
ArcsSet(3.0, 5.0, 1.0e-10));