Improved 1-sphere to provide methods useful for the 2-sphere.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1554658 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2014-01-01 17:33:39 +00:00
parent 292eefa57c
commit 2b24ae34b7
4 changed files with 554 additions and 129 deletions

View File

@ -18,13 +18,16 @@ package org.apache.commons.math3.geometry.spherical.oned;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.math3.exception.MathInternalError;
import org.apache.commons.math3.exception.NumberIsTooLargeException;
import org.apache.commons.math3.exception.util.LocalizedFormats;
import org.apache.commons.math3.geometry.partitioning.AbstractRegion;
import org.apache.commons.math3.geometry.partitioning.BSPTree;
import org.apache.commons.math3.geometry.partitioning.Side;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.MathUtils;
@ -41,7 +44,7 @@ import org.apache.commons.math3.util.Precision;
* @version $Id$
* @since 3.3
*/
public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> implements Iterable<double[]> {
/** Tolerance below which close sub-arcs are merged together. */
private final double tolerance;
@ -183,32 +186,27 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
}
// walk tree until we find the smallest internal node
BSPTree<Sphere1D> previous = previousNode(node);
BSPTree<Sphere1D> previous = previousInternalNode(node);
while (previous != null) {
node = previous;
previous = previousNode(node);
previous = previousInternalNode(node);
}
// walk tree until we find an arc start
while (node != null && !isArcStart(node)) {
node = nextNode(node);
node = nextInternalNode(node);
}
return node;
}
/** Check if a node corresponds to the start angle of an arc.
* @param node node to check
/** Check if an internal node corresponds to the start angle of an arc.
* @param node internal node to check
* @return true if the node corresponds to the start angle of an arc
*/
private boolean isArcStart(final BSPTree<Sphere1D> node) {
if (node.getCut() == null) {
// it's not even a limit angle, it cannot start an arc!
return false;
}
if ((Boolean) leafBefore(node).getAttribute()) {
// it has an inside cell before it, it may end an arc but not start it
return false;
@ -219,23 +217,18 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
return false;
}
// the cell defines a limit angle, with an outside before and an inside after
// the cell has an outside before and an inside after it
// it is the start of an arc
return true;
}
/** Check if a node corresponds to the end angle of an arc.
* @param node node to check
/** Check if an internal node corresponds to the end angle of an arc.
* @param node internal node to check
* @return true if the node corresponds to the end angle of an arc
*/
private boolean isArcEnd(final BSPTree<Sphere1D> node) {
if (node.getCut() == null) {
// it's not even a limit angle, it cannot start an arc!
return false;
}
if (!(Boolean) leafBefore(node).getAttribute()) {
// it has an outside cell before it, it may start an arc but not end it
return false;
@ -246,18 +239,18 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
return false;
}
// the cell defines a limit angle, with an inside before and an outside after
// the cell has an inside before and an outside after it
// it is the end of an arc
return true;
}
/** Get the next internal node.
* @param node current node
* @param node current internal node
* @return next internal node in trigonometric order, or null
* if this is the last internal node
*/
private BSPTree<Sphere1D> nextNode(BSPTree<Sphere1D> node) {
private BSPTree<Sphere1D> nextInternalNode(BSPTree<Sphere1D> node) {
if (childAfter(node).getCut() != null) {
// the next node is in the sub-tree
@ -273,11 +266,11 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
}
/** Get the previous internal node.
* @param node current node
* @param node current internal node
* @return previous internal node in trigonometric order, or null
* if this is the first internal node
*/
private BSPTree<Sphere1D> previousNode(BSPTree<Sphere1D> node) {
private BSPTree<Sphere1D> previousInternalNode(BSPTree<Sphere1D> node) {
if (childBefore(node).getCut() != null) {
// the next node is in the sub-tree
@ -293,15 +286,12 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
}
/** Find the leaf node just before an internal node.
* @param node node at which the sub-tree starts
* @param node internal node at which the sub-tree starts
* @return leaf node just before the internal node
*/
private BSPTree<Sphere1D> leafBefore(BSPTree<Sphere1D> node) {
if (node.getCut() != null) {
node = childBefore(node);
}
node = childBefore(node);
while (node.getCut() != null) {
node = childAfter(node);
}
@ -311,15 +301,12 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
}
/** Find the leaf node just after an internal node.
* @param node node at which the sub-tree starts
* @param node internal node at which the sub-tree starts
* @return leaf node just after the internal node
*/
private BSPTree<Sphere1D> leafAfter(BSPTree<Sphere1D> node) {
if (node.getCut() != null) {
node = childAfter(node);
}
node = childAfter(node);
while (node.getCut() != null) {
node = childBefore(node);
}
@ -355,7 +342,7 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
}
/** Find the child node just before an internal node.
* @param node node at which the sub-tree starts
* @param node internal node at which the sub-tree starts
* @return child node just before the internal node
*/
private BSPTree<Sphere1D> childBefore(BSPTree<Sphere1D> node) {
@ -369,7 +356,7 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
}
/** Find the child node just after an internal node.
* @param node node at which the sub-tree starts
* @param node internal node at which the sub-tree starts
* @return child node just after the internal node
*/
private BSPTree<Sphere1D> childAfter(BSPTree<Sphere1D> node) {
@ -383,21 +370,37 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
}
/** Check if an internal node has a direct limit angle.
* @param node node to check
* @param node internal node to check
* @return true if the limit angle is direct
*/
private boolean isDirect(final BSPTree<Sphere1D> node) {
return ((LimitAngle) node.getCut().getHyperplane()).isDirect();
}
/** Get the limit angle of a node.
* @param node node to check
/** Get the limit angle of an internal node.
* @param node internal node to check
* @return true if the limit angle is direct
*/
private double getAngle(final BSPTree<Sphere1D> node) {
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, tolerance).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, tolerance).wholeHyperplane();
}
/** {@inheritDoc} */
@Override
public ArcsSet buildNew(final BSPTree<Sphere1D> tree) {
@ -413,15 +416,16 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
} else {
double size = 0.0;
double sum = 0.0;
for (final Arc arc : asList()) {
size += arc.getSize();
sum += arc.getSize() * arc.getBarycenter();
for (final double[] a : this) {
final double length = a[1] - a[0];
size += length;
sum += length * (a[0] + a[1]);
}
setSize(size);
if (Precision.equals(size, MathUtils.TWO_PI, 0)) {
setBarycenter(S1Point.NaN);
} else if (size >= Precision.SAFE_MIN) {
setBarycenter(new S1Point(sum / size));
setBarycenter(new S1Point(sum / (2 * size)));
} else {
final LimitAngle limit = (LimitAngle) getTree(false).getCut().getHyperplane();
setBarycenter(limit.getLocation());
@ -437,67 +441,297 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
* @return a new ordered list containing {@link Arc Arc} elements
*/
public List<Arc> asList() {
final List<Arc> list = new ArrayList<Arc>();
final BSPTree<Sphere1D> firstStart = getFirstArcStart();
for (final double[] a : this) {
list.add(new Arc(a[0], a[1], tolerance));
}
return list;
}
if (firstStart == null) {
/** {@inheritDoc}
* <p>
* The iterator returns the limit angles pairs of sub-arcs in trigonometric order.
* </p>
* <p>
* The iterator does <em>not</em> support the optional {@code remove} operation.
* </p>
*/
public Iterator<double[]> iterator() {
return new SubArcsIterator();
}
/** Local iterator for sub-arcs. */
private class SubArcsIterator implements Iterator<double[]> {
/** Start of the first arc. */
private final BSPTree<Sphere1D> firstStart;
/** Current node. */
private BSPTree<Sphere1D> node;
/** Sub-arc no yet returned. */
private double[] pending;
/** Simple constructor.
*/
public SubArcsIterator() {
firstStart = getFirstArcStart();
node = firstStart;
if (firstStart == null) {
// the tree has a single node
if ((Boolean) getTree(false).getAttribute()) {
// it is an inside node, it represents the full circle
list.add(new Arc(0.0, 0.0, tolerance)); // since lower == upper, the arc covers the full circle
}
} else if (previousNode(firstStart) == null && nextNode(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
list.add(new Arc(0.0, 0.0, tolerance)); // since lower == upper, the arc covers the full circle
} else {
// find all arcs
BSPTree<Sphere1D> start = firstStart;
while (start != null) {
// look for the end of the current arc
BSPTree<Sphere1D> end = start;
while (end != null && !isArcEnd(end)) {
end = nextNode(end);
}
if (end != null) {
// we have identified the current arc
list.add(new Arc(getAngle(start), getAngle(end), tolerance));
// prepare search for next arc
start = end;
while (start != null && !isArcStart(start)) {
start = nextNode(start);
}
pending = new double[] {
0, MathUtils.TWO_PI
};
} else {
// the final arc wraps around 2\pi, its end is before the first start
end = firstStart;
while (end != null && !isArcEnd(end)) {
end = previousNode(end);
}
if (end == null) {
// this should never happen
throw new MathInternalError();
}
// we have identified the last arc
list.add(new Arc(getAngle(start), getAngle(end) + MathUtils.TWO_PI, tolerance));
// stop the loop
start = null;
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();
}
}
/** Walk the tree to select the pending sub-arc.
*/
private void selectPending() {
// look for the start of the arc
BSPTree<Sphere1D> start = node;
while (start != null && !isArcStart(start)) {
start = nextInternalNode(start);
}
if (start == null) {
// we have exhausted the iterator
node = null;
pending = null;
return;
}
// look for the end of the arc
BSPTree<Sphere1D> end = start;
while (end != null && !isArcEnd(end)) {
end = nextInternalNode(end);
}
if (end != null) {
// we have identified the arc
pending = new double[] {
getAngle(start), getAngle(end)
};
// prepare search for next arc
node = end;
} else {
// the final arc wraps around 2\pi, its end is before the first start
end = firstStart;
while (end != null && !isArcEnd(end)) {
end = previousInternalNode(end);
}
if (end == null) {
// this should never happen
throw new MathInternalError();
}
// we have identified the last arc
pending = new double[] {
getAngle(start), getAngle(end) + MathUtils.TWO_PI
};
// there won't be any other arcs
node = null;
}
}
return list;
/** {@inheritDoc} */
public boolean hasNext() {
return pending != null;
}
/** {@inheritDoc} */
public double[] next() {
if (pending == null) {
throw new NoSuchElementException();
}
final double[] next = pending;
selectPending();
return next;
}
/** {@inheritDoc} */
public void remove() {
throw new UnsupportedOperationException();
}
}
/** Compute the relative position of the instance with respect
* to an arc.
* <p>
* The {@link Side#MINUS} side of the arc is the one covered by the arc.
* </p>
* @param arc arc to check instance against
* @return one of {@link Side#PLUS}, {@link Side#MINUS}, {@link Side#BOTH}
* or {@link Side#HYPER}
*/
public Side side(final Arc arc) {
final double reference = FastMath.PI + arc.getInf();
final double arcLength = arc.getSup() - arc.getInf();
boolean inMinus = false;
boolean inPlus = false;
for (final double[] a : this) {
final double syncedStart = MathUtils.normalizeAngle(a[0], reference) - arc.getInf();
final double arcOffset = a[0] - syncedStart;
final double syncedEnd = a[1] - arcOffset;
if (syncedStart < arcLength || syncedEnd > MathUtils.TWO_PI) {
inMinus = true;
}
if (syncedEnd > arcLength) {
inPlus = true;
}
}
if (inMinus) {
if (inPlus) {
return Side.BOTH;
} else {
return Side.MINUS;
}
} else {
if (inPlus) {
return Side.PLUS;
} else {
return Side.HYPER;
}
}
}
/** Split the instance in two parts by an arc.
* @param arc splitting arc
* @return an object containing both the part of the instance
* on the plus side of the arc and the part of the
* instance on the minus side of the arc
*/
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 double reference = FastMath.PI + arc.getInf();
final double arcLength = arc.getSup() - arc.getInf();
for (final double[] a : this) {
final double syncedStart = MathUtils.normalizeAngle(a[0], reference) - arc.getInf();
final double arcOffset = a[0] - syncedStart;
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]));
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));
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]));
} else {
// the end point a[1] is in the plus part of the arc
plus.add(arcEnd(a[1]));
}
} else {
// the end point a[1] is in the minus part of the arc
minus.add(arcEnd(a[1]));
}
} else {
// the start point a[0] is in the plus part of the arc
plus.add(arcStart(a[0]));
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));
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]));
} else {
// the end point a[1] is in the minus part of the arc
minus.add(arcEnd(a[1]));
}
} else {
// the end point a[1] is in the plus part of the arc
plus.add(arcEnd(a[1]));
}
}
}
return new Split(plus.isEmpty() ? null : new ArcsSet(plus, tolerance),
minus.isEmpty() ? null : new ArcsSet(minus,tolerance));
}
/** Class holding the results of the {@link #split split} method.
*/
public static class Split {
/** Part of the arcs set on the plus side of the splitting arc. */
private final ArcsSet plus;
/** Part of the arcs set on the minus side of the splitting arc. */
private final ArcsSet minus;
/** Build a Split from its parts.
* @param plus part of the arcs set on the plus side of the
* splitting arc
* @param minus part of the arcs set on the minus side of the
* splitting arc
*/
private Split(final ArcsSet plus, final ArcsSet minus) {
this.plus = plus;
this.minus = minus;
}
/** Get the part of the arcs set on the plus side of the splitting arc.
* @return part of the arcs set on the plus side of the splitting arc
*/
public ArcsSet getPlus() {
return plus;
}
/** Get the part of the arcs set on the minus side of the splitting arc.
* @return part of the arcs set on the minus side of the splitting arc
*/
public ArcsSet getMinus() {
return minus;
}
}

View File

@ -489,27 +489,27 @@ public class SphericalPolygonsSet extends AbstractRegion<Sphere2D, Sphere1D> {
final List<Edge> outsideList, final List<Edge> insideList) {
// get the inside chord, synchronizing its phase with the edge itself
final double edgeStart = circle.getPhase(start.getLocation().getVector());
final double chordRelativeStart = MathUtils.normalizeAngle(circle.getChord(splitCircle).getStart(),
edgeStart + FastMath.PI) - edgeStart;
final double chordRelativeEnd = chordRelativeStart + FastMath.PI;
final double unwrappedEnd = chordRelativeStart - FastMath.PI;
final double edgeStart = circle.getPhase(start.getLocation().getVector());
final double arcRelativeStart = MathUtils.normalizeAngle(circle.getInsideArc(splitCircle).getInf(),
edgeStart + FastMath.PI) - edgeStart;
final double arcRelativeEnd = arcRelativeStart + FastMath.PI;
final double unwrappedEnd = arcRelativeStart - FastMath.PI;
// build the sub-edges
if (chordRelativeStart < length) {
if (arcRelativeStart < length) {
if (unwrappedEnd > 0) {
// the edge starts inside the circle, then goes outside, then comes back inside
// create intermediate vertices
final Vertex vExit = new Vertex(new S2Point(circle.getPointAt(edgeStart + chordRelativeEnd)));
final Vertex vEnter = new Vertex(new S2Point(circle.getPointAt(edgeStart + chordRelativeStart)));
final Vertex vExit = new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeEnd)));
final Vertex vEnter = new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeStart)));
vExit.bindWith(splitCircle);
vEnter.bindWith(splitCircle);
// create sub-edges
final Edge eStartIn = new Edge(start, vExit, unwrappedEnd, circle);
final Edge eMiddleOut = new Edge(vExit, vEnter, chordRelativeStart - unwrappedEnd, circle);
final Edge eEndIn = new Edge(vEnter, end, length - chordRelativeStart, circle);
final Edge eMiddleOut = new Edge(vExit, vEnter, arcRelativeStart - unwrappedEnd, circle);
final Edge eEndIn = new Edge(vEnter, end, length - arcRelativeStart, circle);
eStartIn.setNode(node);
eMiddleOut.setNode(node);
eEndIn.setNode(node);
@ -523,12 +523,12 @@ public class SphericalPolygonsSet extends AbstractRegion<Sphere2D, Sphere1D> {
// the edge starts outside of the circle, then comes inside
// create intermediate vertices
final Vertex vEnter = new Vertex(new S2Point(circle.getPointAt(edgeStart + chordRelativeStart)));
final Vertex vEnter = new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeStart)));
vEnter.bindWith(splitCircle);
// create sub-edges
final Edge eStartOut = new Edge(start, vEnter, chordRelativeStart, circle);
final Edge eEndIn = new Edge(vEnter, end, length - chordRelativeStart, circle);
final Edge eStartOut = new Edge(start, vEnter, arcRelativeStart, circle);
final Edge eEndIn = new Edge(vEnter, end, length - arcRelativeStart, circle);
eStartOut.setNode(node);
eEndIn.setNode(node);
@ -547,12 +547,12 @@ public class SphericalPolygonsSet extends AbstractRegion<Sphere2D, Sphere1D> {
// the edge starts inside the circle, then goes outside
// create intermediate vertices
final Vertex vExit = new Vertex(new S2Point(circle.getPointAt(edgeStart + chordRelativeEnd)));
final Vertex vExit = new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeEnd)));
vExit.bindWith(splitCircle);
// create sub-edges
final Edge eStartIn = new Edge(start, vExit, chordRelativeEnd, circle);
final Edge eEndOut = new Edge(vExit, end, length - chordRelativeEnd, circle);
final Edge eStartIn = new Edge(start, vExit, arcRelativeEnd, circle);
final Edge eEndOut = new Edge(vExit, end, length - arcRelativeEnd, circle);
eStartIn.setNode(node);
eEndOut.setNode(node);

View File

@ -17,14 +17,11 @@
package org.apache.commons.math3.geometry.spherical.twod;
import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
import org.apache.commons.math3.geometry.partitioning.BSPTree;
import org.apache.commons.math3.geometry.partitioning.Hyperplane;
import org.apache.commons.math3.geometry.partitioning.Region;
import org.apache.commons.math3.geometry.partitioning.Side;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
import org.apache.commons.math3.geometry.spherical.oned.Arc;
import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
import org.apache.commons.math3.geometry.spherical.oned.Chord;
import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
/** This class represents a sub-hyperplane for {@link Circle}.
@ -64,24 +61,12 @@ public class SubCircle extends AbstractSubHyperplane<Sphere2D, Sphere1D> {
@Override
public SplitSubHyperplane<Sphere2D> split(final Hyperplane<Sphere2D> hyperplane) {
final Circle thisCircle = (Circle) getHyperplane();
final Circle otherCircle = (Circle) hyperplane;
final Arc arc = thisCircle.getInsideArc(otherCircle);
final SubHyperplane<Sphere1D> subMinus = chord.wholeHyperplane();
final SubHyperplane<Sphere1D> subPlus = chord.getReverse().wholeHyperplane();
final BSPTree<Sphere1D> splitTree = getRemainingRegion().getTree(false).split(subMinus);
final BSPTree<Sphere1D> plusTree = getRemainingRegion().isEmpty(splitTree.getPlus()) ?
new BSPTree<Sphere1D>(Boolean.FALSE) :
new BSPTree<Sphere1D>(subPlus, new BSPTree<Sphere1D>(Boolean.FALSE),
splitTree.getPlus(), null);
final BSPTree<Sphere1D> minusTree = getRemainingRegion().isEmpty(splitTree.getMinus()) ?
new BSPTree<Sphere1D>(Boolean.FALSE) :
new BSPTree<Sphere1D>(subMinus, new BSPTree<Sphere1D>(Boolean.FALSE),
splitTree.getMinus(), null);
return new SplitSubHyperplane<Sphere2D>(new SubCircle(thisCircle.copySelf(), new ArcsSet(plusTree, thisCircle.getTolerance())),
new SubCircle(thisCircle.copySelf(), new ArcsSet(minusTree, thisCircle.getTolerance())));
final Circle thisCircle = (Circle) getHyperplane();
final Circle otherCircle = (Circle) hyperplane;
final Arc arc = thisCircle.getInsideArc(otherCircle);
final ArcsSet.Split split = ((ArcsSet) getRemainingRegion()).split(arc);
return new SplitSubHyperplane<Sphere2D>(new SubCircle(thisCircle.copySelf(), split.getPlus()),
new SubCircle(thisCircle.copySelf(), split.getMinus()));
}

View File

@ -17,12 +17,15 @@
package org.apache.commons.math3.geometry.spherical.oned;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.math3.exception.NumberIsTooLargeException;
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;
import org.apache.commons.math3.geometry.partitioning.Side;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.MathUtils;
@ -250,4 +253,207 @@ public class ArcsSetTest {
Assert.assertEquals(2 * Precision.EPSILON, set.getSize(), Precision.SAFE_MIN);
}
@Test
public void testIteration() {
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));
Iterator<double[]> iterator = set.iterator();
try {
iterator.remove();
Assert.fail("an exception should have been thrown");
} catch (UnsupportedOperationException uoe) {
// expected
}
Assert.assertTrue(iterator.hasNext());
double[] a0 = iterator.next();
Assert.assertEquals(2, a0.length);
Assert.assertEquals(1.0, a0[0], 1.0e-10);
Assert.assertEquals(3.0, a0[1], 1.0e-10);
Assert.assertTrue(iterator.hasNext());
double[] a1 = iterator.next();
Assert.assertEquals(2, a1.length);
Assert.assertEquals(5.0, a1[0], 1.0e-10);
Assert.assertEquals(6.0, a1[1], 1.0e-10);
Assert.assertFalse(iterator.hasNext());
try {
iterator.next();
Assert.fail("an exception should have been thrown");
} catch (NoSuchElementException nsee) {
// expected
}
}
@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));
for (int k = -2; k < 3; ++k) {
Assert.assertEquals(Side.MINUS, set.side(new Arc(0.5 + k * MathUtils.TWO_PI,
6.1 + k * MathUtils.TWO_PI,
set.getTolerance())));
Assert.assertEquals(Side.PLUS, set.side(new Arc(0.5 + k * MathUtils.TWO_PI,
0.8 + k * MathUtils.TWO_PI,
set.getTolerance())));
Assert.assertEquals(Side.PLUS, set.side(new Arc(6.2 + k * MathUtils.TWO_PI,
6.3 + k * MathUtils.TWO_PI,
set.getTolerance())));
Assert.assertEquals(Side.PLUS, set.side(new Arc(3.5 + k * MathUtils.TWO_PI,
4.5 + k * MathUtils.TWO_PI,
set.getTolerance())));
Assert.assertEquals(Side.BOTH, set.side(new Arc(2.9 + k * MathUtils.TWO_PI,
4.5 + k * MathUtils.TWO_PI,
set.getTolerance())));
Assert.assertEquals(Side.BOTH, set.side(new Arc(0.5 + k * MathUtils.TWO_PI,
1.2 + k * MathUtils.TWO_PI,
set.getTolerance())));
Assert.assertEquals(Side.BOTH, set.side(new Arc(0.5 + k * MathUtils.TWO_PI,
5.9 + k * MathUtils.TWO_PI,
set.getTolerance())));
}
}
@Test
public void testSideEmbedded() {
ArcsSet s35 = new ArcsSet(3.0, 5.0, 1.0e-10);
ArcsSet s16 = new ArcsSet(1.0, 6.0, 1.0e-10);
Assert.assertEquals(Side.BOTH, s16.side(new Arc(3.0, 5.0, 1.0e-10)));
Assert.assertEquals(Side.BOTH, s16.side(new Arc(5.0, 3.0 + MathUtils.TWO_PI, 1.0e-10)));
Assert.assertEquals(Side.MINUS, s35.side(new Arc(1.0, 6.0, 1.0e-10)));
Assert.assertEquals(Side.PLUS, s35.side(new Arc(6.0, 1.0 + MathUtils.TWO_PI, 1.0e-10)));
}
@Test
public void testSideOverlapping() {
ArcsSet s35 = new ArcsSet(3.0, 5.0, 1.0e-10);
ArcsSet s46 = new ArcsSet(4.0, 6.0, 1.0e-10);
Assert.assertEquals(Side.BOTH, s46.side(new Arc(3.0, 5.0, 1.0e-10)));
Assert.assertEquals(Side.BOTH, s46.side(new Arc(5.0, 3.0 + MathUtils.TWO_PI, 1.0e-10)));
Assert.assertEquals(Side.BOTH, s35.side(new Arc(4.0, 6.0, 1.0e-10)));
Assert.assertEquals(Side.BOTH, s35.side(new Arc(6.0, 4.0 + MathUtils.TWO_PI, 1.0e-10)));
}
@Test
public void testSideHyper() {
ArcsSet sub = (ArcsSet) new RegionFactory<Sphere1D>().getComplement(new ArcsSet(1.0e-10));
Assert.assertTrue(sub.isEmpty());
Assert.assertEquals(Side.HYPER, sub.side(new Arc(2.0, 3.0, 1.0e-10)));
}
@Test
public void testSplitEmbedded() {
ArcsSet s35 = new ArcsSet(3.0, 5.0, 1.0e-10);
ArcsSet s16 = new ArcsSet(1.0, 6.0, 1.0e-10);
ArcsSet.Split split1 = s16.split(new Arc(3.0, 5.0, 1.0e-10));
ArcsSet split1Plus = (ArcsSet) split1.getPlus();
ArcsSet split1Minus = (ArcsSet) split1.getMinus();
Assert.assertEquals(3.0, split1Plus.getSize(), 1.0e-10);
Assert.assertEquals(2, split1Plus.asList().size());
Assert.assertEquals(1.0, split1Plus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(3.0, split1Plus.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(5.0, split1Plus.asList().get(1).getInf(), 1.0e-10);
Assert.assertEquals(6.0, split1Plus.asList().get(1).getSup(), 1.0e-10);
Assert.assertEquals(2.0, split1Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split1Minus.asList().size());
Assert.assertEquals(3.0, split1Minus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split1Minus.asList().get(0).getSup(), 1.0e-10);
ArcsSet.Split split2 = s16.split(new Arc(5.0, 3.0 + MathUtils.TWO_PI, 1.0e-10));
ArcsSet split2Plus = (ArcsSet) split2.getPlus();
ArcsSet split2Minus = (ArcsSet) split2.getMinus();
Assert.assertEquals(2.0, split2Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split2Plus.asList().size());
Assert.assertEquals(3.0, split2Plus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split2Plus.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(3.0, split2Minus.getSize(), 1.0e-10);
Assert.assertEquals(2, split2Minus.asList().size());
Assert.assertEquals(1.0, split2Minus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(3.0, split2Minus.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(5.0, split2Minus.asList().get(1).getInf(), 1.0e-10);
Assert.assertEquals(6.0, split2Minus.asList().get(1).getSup(), 1.0e-10);
ArcsSet.Split split3 = s35.split(new Arc(1.0, 6.0, 1.0e-10));
ArcsSet split3Plus = (ArcsSet) split3.getPlus();
ArcsSet split3Minus = (ArcsSet) split3.getMinus();
Assert.assertNull(split3Plus);
Assert.assertEquals(2.0, split3Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split3Minus.asList().size());
Assert.assertEquals(3.0, split3Minus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split3Minus.asList().get(0).getSup(), 1.0e-10);
ArcsSet.Split split4 = s35.split(new Arc(6.0, 1.0 + MathUtils.TWO_PI, 1.0e-10));
ArcsSet split4Plus = (ArcsSet) split4.getPlus();
ArcsSet split4Minus = (ArcsSet) split4.getMinus();
Assert.assertEquals(2.0, split4Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split4Plus.asList().size());
Assert.assertEquals(3.0, split4Plus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split4Plus.asList().get(0).getSup(), 1.0e-10);
Assert.assertNull(split4Minus);
}
@Test
public void testSplitOverlapping() {
ArcsSet s35 = new ArcsSet(3.0, 5.0, 1.0e-10);
ArcsSet s46 = new ArcsSet(4.0, 6.0, 1.0e-10);
ArcsSet.Split split1 = s46.split(new Arc(3.0, 5.0, 1.0e-10));
ArcsSet split1Plus = (ArcsSet) split1.getPlus();
ArcsSet split1Minus = (ArcsSet) split1.getMinus();
Assert.assertEquals(1.0, split1Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split1Plus.asList().size());
Assert.assertEquals(5.0, split1Plus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(6.0, split1Plus.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.0, split1Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split1Minus.asList().size());
Assert.assertEquals(4.0, split1Minus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split1Minus.asList().get(0).getSup(), 1.0e-10);
ArcsSet.Split split2 = s46.split(new Arc(5.0, 3.0 + MathUtils.TWO_PI, 1.0e-10));
ArcsSet split2Plus = (ArcsSet) split2.getPlus();
ArcsSet split2Minus = (ArcsSet) split2.getMinus();
Assert.assertEquals(1.0, split2Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split2Plus.asList().size());
Assert.assertEquals(4.0, split2Plus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split2Plus.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.0, split2Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split2Minus.asList().size());
Assert.assertEquals(5.0, split2Minus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(6.0, split2Minus.asList().get(0).getSup(), 1.0e-10);
ArcsSet.Split split3 = s35.split(new Arc(4.0, 6.0, 1.0e-10));
ArcsSet split3Plus = (ArcsSet) split3.getPlus();
ArcsSet split3Minus = (ArcsSet) split3.getMinus();
Assert.assertEquals(1.0, split3Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split3Plus.asList().size());
Assert.assertEquals(3.0, split3Plus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(4.0, split3Plus.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.0, split3Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split3Minus.asList().size());
Assert.assertEquals(4.0, split3Minus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split3Minus.asList().get(0).getSup(), 1.0e-10);
ArcsSet.Split split4 = s35.split(new Arc(6.0, 4.0 + MathUtils.TWO_PI, 1.0e-10));
ArcsSet split4Plus = (ArcsSet) split4.getPlus();
ArcsSet split4Minus = (ArcsSet) split4.getMinus();
Assert.assertEquals(1.0, split4Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split4Plus.asList().size());
Assert.assertEquals(4.0, split4Plus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.0, split4Plus.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.0, split4Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split4Minus.asList().size());
Assert.assertEquals(3.0, split4Minus.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(4.0, split4Minus.asList().get(0).getSup(), 1.0e-10);
}
}