Improved robustness of 1-sphere regions (ArcsSet) at 0/2\pi crossing.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1556880 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f2db15c31f
commit
0a75cbc380
|
@ -115,6 +115,7 @@ public enum LocalizedFormats implements Localizable {
|
|||
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}]"),
|
||||
|
|
|
@ -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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
* {@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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
* 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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
|
||||
}
|
||||
|
||||
/** 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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
}
|
||||
|
||||
// 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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
} 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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
};
|
||||
|
||||
// prepare search for next arc
|
||||
node = end;
|
||||
current = end;
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -539,7 +585,7 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
};
|
||||
|
||||
// there won't be any other arcs
|
||||
node = null;
|
||||
current = null;
|
||||
|
||||
}
|
||||
|
||||
|
@ -619,8 +665,12 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
*/
|
||||
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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
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 AbstractRegion<Sphere1D, Sphere1D> implements Itera
|
|||
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class LimitAngle implements Hyperplane<Sphere1D> {
|
|||
|
||||
/** {@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;
|
||||
}
|
||||
|
||||
|
|
|
@ -172,11 +172,12 @@ public class SphericalPolygonsSet extends AbstractRegion<Sphere2D, Sphere1D> {
|
|||
|
||||
// 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
|
||||
|
|
|
@ -87,6 +87,7 @@ GCD_OVERFLOW_32_BITS = d\u00e9passement de capacit\u00e9 : le PGCD de {0} et {1}
|
|||
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}]
|
||||
|
|
|
@ -30,7 +30,7 @@ public class LocalizedFormatsTest {
|
|||
|
||||
@Test
|
||||
public void testMessageNumber() {
|
||||
Assert.assertEquals(316, LocalizedFormats.values().length);
|
||||
Assert.assertEquals(317, LocalizedFormats.values().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -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);
|
||||
|
@ -287,6 +331,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),
|
||||
|
|
Loading…
Reference in New Issue