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:
parent
292eefa57c
commit
2b24ae34b7
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue