Simplified 1-sphere case.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1554654 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2014-01-01 17:30:06 +00:00
parent 57eff0d0c3
commit d83cdb9133
12 changed files with 535 additions and 858 deletions

View File

@ -16,6 +16,8 @@
*/ */
package org.apache.commons.math3.geometry.spherical.oned; package org.apache.commons.math3.geometry.spherical.oned;
import org.apache.commons.math3.exception.NumberIsTooLargeException;
import org.apache.commons.math3.exception.util.LocalizedFormats;
import org.apache.commons.math3.geometry.partitioning.Region.Location; import org.apache.commons.math3.geometry.partitioning.Region.Location;
import org.apache.commons.math3.util.FastMath; import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.MathUtils; import org.apache.commons.math3.util.MathUtils;
@ -38,38 +40,54 @@ public class Arc {
/** Middle point of the arc. */ /** Middle point of the arc. */
private final double middle; private final double middle;
/** Tolerance below which angles are considered identical. */
private final double tolerance;
/** Simple constructor. /** Simple constructor.
* <p> * <p>
* As the circle is a closed curve, {@code lower} is * If either {@code lower} is equals to {@code upper} or
* allowed to be greater than {@code upper}, and will * the interval exceeds \( 2 \pi \), the arc is considered
* be automatically canonicalized so the arc wraps * to be the full circle and its initial defining boundaries
* around \( 2\pi \), but without exceeding a total * will be forgotten. {@code lower} is not allowed to be
* length of \( 2\pi \). If {@code lower} is equals * greater than {@code upper} (an exception is thrown in this case).
* to {@code upper}, the arc is considered to be the full * {@code lower} will be canonicalized between 0 and \( 2 \pi \), and
* circle. * upper shifted accordingly, so the {@link #getInf()} and {@link #getSup()}
* may not return the value used at instance construction.
* </p> * </p>
* @param lower lower angular bound of the arc * @param lower lower angular bound of the arc
* @param upper upper angular bound of the arc * @param upper upper angular bound of the arc
* @param tolerance tolerance below which angles are considered identical
* @exception NumberIsTooLargeException if lower is greater than upper
*/ */
public Arc(final double lower, final double upper) { public Arc(final double lower, final double upper, final double tolerance)
this.lower = lower; throws NumberIsTooLargeException {
if (Precision.equals(lower, upper, 0)) { this.tolerance = tolerance;
this.upper = MathUtils.TWO_PI + lower; if (Precision.equals(lower, upper, 0) || (upper - lower) >= MathUtils.TWO_PI) {
// the arc must cover the whole circle
this.lower = 0;
this.upper = MathUtils.TWO_PI;
this.middle = FastMath.PI;
} else if (lower <= upper) {
this.lower = MathUtils.normalizeAngle(lower, FastMath.PI);
this.upper = this.lower + (upper - lower);
this.middle = 0.5 * (this.lower + this.upper);
} else { } else {
this.upper = MathUtils.normalizeAngle(upper, lower + FastMath.PI); throw new NumberIsTooLargeException(LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
lower, upper, true);
} }
this.middle = 0.5 * (this.lower + this.upper);
} }
/** Get the lower angular bound of the arc. /** Get the lower angular bound of the arc.
* @return lower angular bound of the arc * @return lower angular bound of the arc,
* always between 0 and \( 2 \pi \)
*/ */
public double getInf() { public double getInf() {
return lower; return lower;
} }
/** Get the upper angular bound of the arc. /** Get the upper angular bound of the arc.
* @return upper angular bound of the arc * @return upper angular bound of the arc,
* always between {@link #getInf()} and {@link #getInf()} \( + 2 \pi \)
*/ */
public double getSup() { public double getSup() {
return upper; return upper;
@ -89,14 +107,19 @@ public class Arc {
return middle; return middle;
} }
/** Get the tolerance below which angles are considered identical.
* @return tolerance below which angles are considered identical
*/
public double getTolerance() {
return tolerance;
}
/** Check a point with respect to the arc. /** Check a point with respect to the arc.
* @param point point to check * @param point point to check
* @param tolerance tolerance below which points are considered to
* belong to the boundary
* @return a code representing the point status: either {@link * @return a code representing the point status: either {@link
* Location#INSIDE}, {@link Location#OUTSIDE} or {@link Location#BOUNDARY} * Location#INSIDE}, {@link Location#OUTSIDE} or {@link Location#BOUNDARY}
*/ */
public Location checkPoint(final double point, final double tolerance) { public Location checkPoint(final double point) {
final double normalizedPoint = MathUtils.normalizeAngle(point, middle); final double normalizedPoint = MathUtils.normalizeAngle(point, middle);
if (normalizedPoint < lower - tolerance || normalizedPoint > upper + tolerance) { if (normalizedPoint < lower - tolerance || normalizedPoint > upper + tolerance) {
return Location.OUTSIDE; return Location.OUTSIDE;

View File

@ -21,6 +21,8 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
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.AbstractRegion;
import org.apache.commons.math3.geometry.partitioning.BSPTree; import org.apache.commons.math3.geometry.partitioning.BSPTree;
import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor; import org.apache.commons.math3.geometry.partitioning.BSPTreeVisitor;
@ -30,6 +32,13 @@ import org.apache.commons.math3.util.MathUtils;
import org.apache.commons.math3.util.Precision; import org.apache.commons.math3.util.Precision;
/** This class represents a region of a circle: a set of arcs. /** This class represents a region of a circle: a set of arcs.
* <p>
* Note that due to the wrapping around \(2 \pi\), barycenter is
* ill-defined here. It was defined only in order to fulfill
* the requirements of the {@link
* org.apache.commons.math3.geometry.partitioning.Region Region}
* interface, but its use is discouraged.
* </p>
* @version $Id$ * @version $Id$
* @since 3.3 * @since 3.3
*/ */
@ -47,19 +56,19 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
/** Build an arcs set corresponding to a single arc. /** Build an arcs set corresponding to a single arc.
* <p> * <p>
* As the circle is a closed curve, {@code lower} is * If either {@code lower} is equals to {@code upper} or
* allowed to be greater than {@code upper}, and will * the interval exceeds \( 2 \pi \), the arc is considered
* be automatically canonicalized so the arc wraps * to be the full circle and its initial defining boundaries
* around \( 2\pi \), but without exceeding a total * will be forgotten. {@code lower} is not allowed to be greater
* length of \( 2\pi \). If {@code lower} is equals * than {@code upper} (an exception is thrown in this case).
* to {@code upper}, the arc is considered to be the full
* circle.
* </p> * </p>
* @param lower lower bound of the arc * @param lower lower bound of the arc
* @param upper upper bound of the arc * @param upper upper bound of the arc
* @param tolerance tolerance below which close sub-arcs are merged together * @param tolerance tolerance below which close sub-arcs are merged together
* @exception NumberIsTooLargeException if lower is greater than upper
*/ */
public ArcsSet(final double lower, final double upper, final double tolerance) { public ArcsSet(final double lower, final double upper, final double tolerance)
throws NumberIsTooLargeException {
super(buildTree(lower, upper, tolerance)); super(buildTree(lower, upper, tolerance));
this.tolerance = tolerance; this.tolerance = tolerance;
} }
@ -105,33 +114,236 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
} }
/** Build an inside/outside tree representing a single arc. /** Build an inside/outside tree representing a single arc.
* <p>
* As the circle is a closed curve, {@code lower} is
* allowed to be greater than {@code upper}, and will
* be automatically canonicalized so the arc wraps
* around \( 2\pi \), but without exceeding a total
* length of \( 2\pi \). If {@code lower} is equals
* to {@code upper}, the arc is considered to be the full
* circle.
* </p>
* @param lower lower angular bound of the arc * @param lower lower angular bound of the arc
* @param upper upper angular bound of the arc * @param upper upper angular bound of the arc
* @param tolerance tolerance below which close sub-arcs are merged together * @param tolerance tolerance below which close sub-arcs are merged together
* @return the built tree * @return the built tree
* @exception NumberIsTooLargeException if lower is greater than upper
*/ */
private static BSPTree<Sphere1D> buildTree(final double lower, final double upper, final double tolerance) { private static BSPTree<Sphere1D> buildTree(final double lower, final double upper,
final double tolerance)
throws NumberIsTooLargeException {
if (Precision.equals(lower, upper, 0)) { if (Precision.equals(lower, upper, 0) || (upper - lower) >= MathUtils.TWO_PI) {
// the tree must cover the whole real line // the tree must cover the whole circle
return new BSPTree<Sphere1D>(Boolean.TRUE); return new BSPTree<Sphere1D>(Boolean.TRUE);
} else if (lower > upper) {
throw new NumberIsTooLargeException(LocalizedFormats.ENDPOINTS_NOT_AN_INTERVAL,
lower, upper, true);
} }
// the two boundary angles define only one cutting chord // this is a regular arc, covering only part of the circle
final double normalizedUpper = MathUtils.normalizeAngle(upper, lower + FastMath.PI); final double normalizedLower = MathUtils.normalizeAngle(lower, FastMath.PI);
return new BSPTree<Sphere1D>(new Chord(lower, normalizedUpper, tolerance).wholeHyperplane(), final double normalizedUpper = normalizedLower + (upper - lower);
new BSPTree<Sphere1D>(Boolean.FALSE), final SubHyperplane<Sphere1D> lowerCut =
new BSPTree<Sphere1D>(Boolean.TRUE), new LimitAngle(new S1Point(normalizedLower), false, tolerance).wholeHyperplane();
null);
if (normalizedUpper <= MathUtils.TWO_PI) {
// simple arc starting after 0 and ending before 2 \pi
final SubHyperplane<Sphere1D> upperCut =
new LimitAngle(new S1Point(normalizedUpper), true, tolerance).wholeHyperplane();
return new BSPTree<Sphere1D>(lowerCut,
new BSPTree<Sphere1D>(Boolean.FALSE),
new BSPTree<Sphere1D>(upperCut,
new BSPTree<Sphere1D>(Boolean.FALSE),
new BSPTree<Sphere1D>(Boolean.TRUE),
null),
null);
} else {
// arc wrapping around 2 \pi
final SubHyperplane<Sphere1D> upperCut =
new LimitAngle(new S1Point(normalizedUpper - MathUtils.TWO_PI), true, tolerance).wholeHyperplane();
return new BSPTree<Sphere1D>(lowerCut,
new BSPTree<Sphere1D>(upperCut,
new BSPTree<Sphere1D>(Boolean.FALSE),
new BSPTree<Sphere1D>(Boolean.TRUE),
null),
new BSPTree<Sphere1D>(Boolean.TRUE),
null);
}
}
/** Get the tolerance below which angles are considered identical.
* @return tolerance below which angles are considered identical
*/
public double getTolerance() {
return tolerance;
}
/** Get the smallest internal node.
* @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)
*/
public LimitAngle getSmallestLimit() {
// start search at the tree root
BSPTree<Sphere1D> node = getTree(false);
if (node.getCut() == null) {
return null;
}
BSPTree<Sphere1D> previous = previousNode(node);
while (previous != null) {
node = previous;
previous = previousNode(node);
}
return (LimitAngle) node.getCut().getHyperplane();
}
/** Get the largest limit angle in the set.
* @return largest limit angle (i.e. last before or at \(2 \pi) radians, in trigonometric direction),
* or null if there are no limits (i.e. the set is either empty or covers the full circle)
*/
public LimitAngle getLargestLimit() {
// start search at the tree root
BSPTree<Sphere1D> node = getTree(false);
if (node.getCut() == null) {
return null;
}
BSPTree<Sphere1D> next = nextNode(node);
while (next != null) {
node = next;
next = nextNode(node);
}
return (LimitAngle) node.getCut().getHyperplane();
}
/** Get the next internal node.
* @param node current node
* @return next internal node in trigonometric order, or null
* if this is the last internal node
*/
private BSPTree<Sphere1D> nextNode(BSPTree<Sphere1D> node) {
final BSPTree<Sphere1D> nextDeeper =
((LimitAngle) node.getCut().getHyperplane()).isDirect() ?
node.getPlus() : node.getMinus();
if (nextDeeper.getCut() != null) {
// the next node is in the sub-tree
return findSmallest(nextDeeper);
}
// there is nothing left deeper in the tree, we backtrack
while (isAfterParent(node)) {
node = node.getParent();
}
return node.getParent();
}
/** Get the previous internal node.
* @param node current node
* @return previous internal node in trigonometric order, or null
* if this is the first internal node
*/
private BSPTree<Sphere1D> previousNode(BSPTree<Sphere1D> node) {
final BSPTree<Sphere1D> nextDeeper =
((LimitAngle) node.getCut().getHyperplane()).isDirect() ?
node.getMinus() : node.getPlus();
if (nextDeeper.getCut() != null) {
// the next node is in the sub-tree
return findLargest(nextDeeper);
}
// there is nothing left deeper in the tree, we backtrack
while (isBeforeParent(node)) {
node = node.getParent();
}
return node.getParent();
}
/** Check if a node is the child before its parent in trigonometric order.
* @param node child node considered
* @return true is the node has a parent end is before it in trigonometric order
*/
private boolean isBeforeParent(final BSPTree<Sphere1D> node) {
final BSPTree<Sphere1D> parent = node.getParent();
if (parent == null) {
return false;
}
if (((LimitAngle) parent.getCut().getHyperplane()).isDirect()) {
// smaller angles are on minus side, larger angles are on plus side
return node == parent.getMinus();
} else {
// smaller angles are on plus side, larger angles are on minus side
return node == parent.getPlus();
}
}
/** Check if a node is the child after its parent in trigonometric order.
* @param node child node considered
* @return true is the node has a parent end is after it in trigonometric order
*/
private boolean isAfterParent(final BSPTree<Sphere1D> node) {
final BSPTree<Sphere1D> parent = node.getParent();
if (parent == null) {
return false;
}
if (((LimitAngle) parent.getCut().getHyperplane()).isDirect()) {
// smaller angles are on minus side, larger angles are on plus side
return node == parent.getPlus();
} else {
// smaller angles are on plus side, larger angles are on minus side
return node == parent.getMinus();
}
}
/** Find the smallest internal node in a sub-tree.
* @param node node at which the sub-tree starts
* @return smallest internal node (in trigonometric order), may be the
* provided node if no smaller internal node exist
*/
private BSPTree<Sphere1D> findSmallest(BSPTree<Sphere1D> node) {
BSPTree<Sphere1D> internal = null;
while (node.getCut() != null) {
internal = node;
if (((LimitAngle) node.getCut().getHyperplane()).isDirect()) {
// smaller angles are on minus side, larger angles are on plus side
node = node.getMinus();
} else {
// smaller angles are on plus side, larger angles are on minus side
node = node.getPlus();
}
}
return internal;
}
/** Find the largest internal node in a sub-tree.
* @param node node at which the sub-tree starts
* @return largest internal node (in trigonometric order), may be the
* provided node if no larger internal node exist
*/
private BSPTree<Sphere1D> findLargest(BSPTree<Sphere1D> node) {
BSPTree<Sphere1D> internal = null;
while (node.getCut() != null) {
internal = node;
if (((LimitAngle) node.getCut().getHyperplane()).isDirect()) {
// smaller angles are on minus side, larger angles are on plus side
node = node.getPlus();
} else {
// smaller angles are on plus side, larger angles are on minus side
node = node.getMinus();
}
}
return internal;
} }
@ -160,8 +372,8 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
} else if (size >= Precision.SAFE_MIN) { } else if (size >= Precision.SAFE_MIN) {
setBarycenter(new S1Point(sum / size)); setBarycenter(new S1Point(sum / size));
} else { } else {
final Chord chord = (Chord) getTree(false).getCut().getHyperplane(); final LimitAngle limit = (LimitAngle) getTree(false).getCut().getHyperplane();
setBarycenter(new S1Point(0.5 * (chord.getStart() + chord.getEnd()))); setBarycenter(limit.getLocation());
} }
} }
} }
@ -182,7 +394,7 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
// the tree has a single node // the tree has a single node
if ((Boolean) root.getAttribute()) { if ((Boolean) root.getAttribute()) {
// it is an inside node, it represents the full circle // it is an inside node, it represents the full circle
list.add(new Arc(0.0, 0.0)); // since lower == upper, the arc covers the full circle list.add(new Arc(0.0, 0.0, tolerance)); // since lower == upper, the arc covers the full circle
} }
} else { } else {
@ -190,6 +402,11 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
final LimitsCollector finder = new LimitsCollector(); final LimitsCollector finder = new LimitsCollector();
root.visit(finder); root.visit(finder);
final List<Double> limits = finder.getLimits(); final List<Double> limits = finder.getLimits();
if (limits.size() < 2) {
// the start and end angle collapsed to the same value, its the full circle again
list.add(new Arc(0.0, 0.0, tolerance)); // since lower == upper, the arc covers the full circle
return list;
}
// sort them so the first angle is an arc start // sort them so the first angle is an arc start
Collections.sort(limits); Collections.sort(limits);
@ -201,7 +418,7 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
// we can now build the list // we can now build the list
for (int i = 0; i < limits.size(); i += 2) { for (int i = 0; i < limits.size(); i += 2) {
list.add(new Arc(limits.get(i), limits.get(i + 1))); list.add(new Arc(limits.get(i), limits.get(i + 1), tolerance));
} }
} }
@ -229,12 +446,9 @@ public class ArcsSet extends AbstractRegion<Sphere1D, Sphere1D> {
/** {@inheritDoc} */ /** {@inheritDoc} */
public void visitInternalNode(final BSPTree<Sphere1D> node) { public void visitInternalNode(final BSPTree<Sphere1D> node) {
// check if the chord end points are arc limits // check if the chord end points are arc limits
final Chord chord = (Chord) node.getCut().getHyperplane(); final LimitAngle limit = (LimitAngle) node.getCut().getHyperplane();
if (checkPoint(new S1Point(chord.getStart())) == Location.BOUNDARY) { if (checkPoint(limit.getLocation()) == Location.BOUNDARY) {
limits.add(MathUtils.normalizeAngle(chord.getStart(), FastMath.PI)); limits.add(limit.getLocation().getAlpha());
}
if (checkPoint(new S1Point(chord.getEnd())) == Location.BOUNDARY) {
limits.add(MathUtils.normalizeAngle(chord.getEnd(), FastMath.PI));
} }
} }

View File

@ -18,43 +18,33 @@ package org.apache.commons.math3.geometry.spherical.oned;
import org.apache.commons.math3.geometry.Point; import org.apache.commons.math3.geometry.Point;
import org.apache.commons.math3.geometry.partitioning.Hyperplane; import org.apache.commons.math3.geometry.partitioning.Hyperplane;
import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.MathUtils;
/** This class represents a 1D oriented hyperplane on the circle. /** This class represents a 1D oriented hyperplane on the circle.
* <p>An hyperplane on the 1-sphere is a chord that splits * <p>An hyperplane on the 1-sphere is an angle with an orientation.</p>
* the circle in two parts.</p>
* <p>Instances of this class are guaranteed to be immutable.</p> * <p>Instances of this class are guaranteed to be immutable.</p>
* @version $Id$ * @version $Id$
* @since 3.3 * @since 3.3
*/ */
public class Chord implements Hyperplane<Sphere1D> { public class LimitAngle implements Hyperplane<Sphere1D> {
/** Start angle of the chord. */ /** Angle location. */
private final double start; private S1Point location;
/** End angle of the chord. */ /** Orientation. */
private final double end; private boolean direct;
/** Cosine of the half aperture. */ /** Tolerance below which angles are considered identical. */
private final double cos;
/** Middle point of the chord. */
private final S1Point middle;
/** Tolerance below which close sub-arcs are merged together. */
private final double tolerance; private final double tolerance;
/** Simple constructor. /** Simple constructor.
* @param start start angle of the chord * @param location location of the hyperplane
* @param end end angle of the chord * @param direct if true, the plus side of the hyperplane is towards
* @param tolerance tolerance below which close sub-arcs are merged together * angles greater than {@code location}
* @param tolerance tolerance below which angles are considered identical
*/ */
public Chord(final double start, final double end, final double tolerance) { public LimitAngle(final S1Point location, final boolean direct, final double tolerance) {
this.start = start; this.location = location;
this.end = end; this.direct = direct;
this.middle = new S1Point(0.5 * (start + end));
this.cos = FastMath.cos(0.5 * (end - start));
this.tolerance = tolerance; this.tolerance = tolerance;
} }
@ -63,22 +53,31 @@ public class Chord implements Hyperplane<Sphere1D> {
* the instance.</p> * the instance.</p>
* @return the instance itself * @return the instance itself
*/ */
public Chord copySelf() { public LimitAngle copySelf() {
return this; return this;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public double getOffset(final Point<Sphere1D> point) { public double getOffset(final Point<Sphere1D> point) {
return cos - middle.getVector().dotProduct(((S1Point) point).getVector()); final double delta = ((S1Point) point).getAlpha()- location.getAlpha();
return direct ? delta : -delta;
}
/** Check if the hyperplane orientation is direct.
* @return true if the plus side of the hyperplane is towards
* angles greater than hyperplane location
*/
public boolean isDirect() {
return direct;
} }
/** Get the reverse of the instance. /** Get the reverse of the instance.
* <p>Get a chord with reversed orientation with respect to the * <p>Get a limit angle with reversed orientation with respect to the
* instance. A new object is built, the instance is untouched.</p> * instance. A new object is built, the instance is untouched.</p>
* @return a new chord, with orientation opposite to the instance orientation * @return a new limit angle, with orientation opposite to the instance orientation
*/ */
public Chord getReverse() { public LimitAngle getReverse() {
return new Chord(end, MathUtils.normalizeAngle(start, end + FastMath.PI), tolerance); return new LimitAngle(location, !direct, tolerance);
} }
/** Build a region covering the whole hyperplane. /** Build a region covering the whole hyperplane.
@ -92,8 +91,8 @@ public class Chord implements Hyperplane<Sphere1D> {
* <em>not</em> be used otherwise.</p> * <em>not</em> be used otherwise.</p>
* @return a dummy sub hyperplane * @return a dummy sub hyperplane
*/ */
public SubChord wholeHyperplane() { public SubLimitAngle wholeHyperplane() {
return new SubChord(this); return new SubLimitAngle(this, null);
} }
/** Build a region covering the whole space. /** Build a region covering the whole space.
@ -106,25 +105,18 @@ public class Chord implements Hyperplane<Sphere1D> {
/** {@inheritDoc} */ /** {@inheritDoc} */
public boolean sameOrientationAs(final Hyperplane<Sphere1D> other) { public boolean sameOrientationAs(final Hyperplane<Sphere1D> other) {
return middle.getVector().dotProduct(((Chord) other).middle.getVector()) >= 0.0; return !(direct ^ ((LimitAngle) other).direct);
} }
/** Get the start angle of the chord. /** Get the hyperplane location on the circle.
* @return start angle of the chord. * @return the hyperplane location
*/ */
public double getStart() { public S1Point getLocation() {
return start; return location;
} }
/** Get the end angle of the chord. /** Get the tolerance below which angles are considered identical.
* @return end angle of the chord. * @return tolerance below which angles are considered identical
*/
public double getEnd() {
return end;
}
/** Get the tolerance below which close sub-arcs are merged together.
* @return tolerance below which close sub-arcs are merged together
*/ */
public double getTolerance() { public double getTolerance() {
return tolerance; return tolerance;

View File

@ -49,7 +49,8 @@ public class S1Point implements Point<Sphere1D> {
* @see #getAlpha() * @see #getAlpha()
*/ */
public S1Point(final double alpha) { public S1Point(final double alpha) {
this(alpha, new Vector2D(FastMath.cos(alpha), FastMath.sin(alpha))); this(MathUtils.normalizeAngle(alpha, FastMath.PI),
new Vector2D(FastMath.cos(alpha), FastMath.sin(alpha)));
} }
/** Build a point from its internal components. /** Build a point from its internal components.

View File

@ -1,332 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.spherical.oned;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.geometry.partitioning.Hyperplane;
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;
import org.apache.commons.math3.util.Precision;
/** This class represents sub-hyperplane for {@link Chord}.
* <p>Instances of this class are guaranteed to be immutable.</p>
* @version $Id$
* @since 3.3
*/
public class SubChord implements SubHyperplane<Sphere1D> {
/** Underlying hyperplane. */
private final Chord chord;
/** Boundary angles. */
private final List<Double> limits;
/** Simple constructor.
* @param chord underlying hyperplane
*/
public SubChord(final Chord chord) {
this.chord = chord;
this.limits = new ArrayList<Double>();
limits.add(chord.getStart());
limits.add(chord.getEnd());
}
/** Simple constructor.
* @param chord underlying hyperplane
* @param limits limit angles (the list will be copied into a new independent list)
*/
private SubChord(final Chord chord, final List<Double> limits) {
this.chord = chord;
this.limits = new ArrayList<Double>(limits);
}
/** Get the sub-arcs.
* @return a newly created list with sub-arcs
*/
public List<Arc> getSubArcs() {
final List<Arc> subArcs = new ArrayList<Arc>(limits.size() / 2);
for (int i = 0; i < limits.size(); i += 2) {
subArcs.add(new Arc(limits.get(i), limits.get(i + 1)));
}
return subArcs;
}
/** Get the number of sub-arcs.
* @return number of sub-arcs
*/
public int getNbSubArcs() {
return limits.size() / 2;
}
/** Get the start of the i<sup>th</sup> sub-arc.
* @param i index of the desired arc (counting from 0)
* @return start of the i<sup>th</sup> sub-arc
*/
public double getStart(final int i) {
return limits.get(2 * i);
}
/** Get the end of the i<sup>th</sup> sub-arc.
* @param i index of the desired arc (counting from 0)
* @return end of the i<sup>th</sup> sub-arc
*/
public double getEnd(final int i) {
return limits.get(2 * i + 1);
}
/** {@inheritDoc} */
public double getSize() {
double sum = 0;
for (int i = 0; i < limits.size(); i += 2) {
sum += limits.get(i + 1) - limits.get(i);
}
return sum;
}
/** {@inheritDoc} */
public Side side(final Hyperplane<Sphere1D> hyperplane) {
final Chord testChord = (Chord) hyperplane;
final double reference = FastMath.PI + testChord.getStart();
final double chordLength = testChord.getEnd() - testChord.getStart();
boolean inMinus = false;
boolean inPlus = false;
for (int i = 0; i < limits.size(); i += 2) {
final double syncedStart = MathUtils.normalizeAngle(limits.get(i), reference) - testChord.getStart();
final double chordOffset = limits.get(i) - syncedStart;
final double syncedEnd = limits.get(i + 1) - chordOffset;
if (syncedStart < chordLength || syncedEnd > MathUtils.TWO_PI) {
inMinus = true;
}
if (syncedEnd > chordLength) {
inPlus = true;
}
}
if (inMinus) {
if (inPlus) {
return Side.BOTH;
} else {
return Side.MINUS;
}
} else {
if (inPlus) {
return Side.PLUS;
} else {
return Side.HYPER;
}
}
}
/** {@inheritDoc} */
public SplitSubHyperplane<Sphere1D> split(final Hyperplane<Sphere1D> hyperplane) {
final List<Double> minus = new ArrayList<Double>(limits.size());
final List<Double> plus = new ArrayList<Double>(limits.size());
final Chord testChord = (Chord) hyperplane;
final double reference = FastMath.PI + testChord.getStart();
final double chordLength = testChord.getEnd() - testChord.getStart();
for (int i = 0; i < limits.size(); i += 2) {
final double syncedStart = MathUtils.normalizeAngle(limits.get(i), reference) - testChord.getStart();
final double chordOffset = limits.get(i) - syncedStart;
final double syncedEnd = limits.get(i + 1) - chordOffset;
if (syncedStart < chordLength) {
// the start point limits.get(i) is in the minus part of the chord
minus.add(limits.get(i));
if (syncedEnd > chordLength) {
// the end point limits.get(i + 1) is past the end of the chord
// so we leave the minus part and enter the plus part
final double minusToPlus = chordLength + chordOffset;
minus.add(minusToPlus);
plus.add(minusToPlus);
if (syncedEnd > MathUtils.TWO_PI) {
// in fact the end point limits.get(i + 1) goes far enough that we
// leave the plus part of the chord and enter the minus part again
final double plusToMinus = MathUtils.TWO_PI + chordOffset;
plus.add(plusToMinus);
minus.add(plusToMinus);
minus.add(limits.get(i + 1));
} else {
// the end point limits.get(i + 1) is in the plus part of the chord
plus.add(limits.get(i + 1));
}
} else {
// the end point limits.get(i + 1) is in the minus part of the chord
minus.add(limits.get(i + 1));
}
} else {
// the start point limits.get(i) is in the plus part of the chord
plus.add(limits.get(i));
if (syncedEnd > MathUtils.TWO_PI) {
// the end point limits.get(i + 1) wraps around to the start of the chord
// so we leave the plus part and enter the minus part
final double plusToMinus = MathUtils.TWO_PI + chordOffset;
plus.add(plusToMinus);
minus.add(plusToMinus);
if (syncedEnd > MathUtils.TWO_PI + chordLength) {
// in fact the end point limits.get(i + 1) goes far enough that we
// leave the minus part of the chord and enter the plus part again
final double minusToPlus = MathUtils.TWO_PI + chordLength + chordOffset;
minus.add(minusToPlus);
plus.add(minusToPlus);
plus.add(limits.get(i + 1));
} else {
// the end point limits.get(i + 1) is in the minus part of the chord
minus.add(limits.get(i + 1));
}
} else {
// the end point limits.get(i + 1) is in the plus part of the chord
plus.add(limits.get(i + 1));
}
}
}
return new SplitSubHyperplane<Sphere1D>(plus.isEmpty() ? null : new SubChord(chord, plus),
minus.isEmpty() ? null : new SubChord(chord, minus));
}
/** {@inheritDoc} */
public SubChord copySelf() {
return new SubChord(chord.copySelf(), limits);
}
/** {@inheritDoc} */
public Chord getHyperplane() {
return chord;
}
/** {@inheritDoc} */
public boolean isEmpty() {
return getSize() <= Precision.SAFE_MIN;
}
/** {@inheritDoc} */
public SubChord reunite(SubHyperplane<Sphere1D> other) {
final List<Double> otherLimits = ((SubChord) other).limits;
final List<Double> merged;
if (other.isEmpty()) {
merged = limits;
} else if (isEmpty()) {
merged = otherLimits;
} else {
merged = new ArrayList<Double>(limits.size() + otherLimits.size());
final double reference = limits.get(0) + FastMath.PI;
// initialize loop on first limits list
int i = 0;
int iEnd = limits.size() - 1;
boolean enteringI = true;
// initialize loop on second limits list
int j = otherLimits.size() - 1;
double angleAfter = Double.POSITIVE_INFINITY;
for (int jSearch = 0; jSearch < otherLimits.size(); ++jSearch) {
// look for the first angle in the second list that lies just after first limits start
final double angleJ = MathUtils.normalizeAngle(otherLimits.get(jSearch), reference);
if (angleJ < angleAfter) {
j = jSearch;
angleAfter = angleJ;
}
}
int jEnd = (j + otherLimits.size() - 1) % otherLimits.size();
boolean enteringJ = j % 2 == 0;
// perform merging loop
boolean inMerged = !enteringJ;
double angleI = MathUtils.normalizeAngle(limits.get(i), reference);
double angleJ = MathUtils.normalizeAngle(otherLimits.get(j), reference);
while (i >= 0 || j >= 0) {
if (i >= 0 && (j < 0 || angleI <= angleJ)) {
if (inMerged && (!enteringI) && enteringJ) {
// we were in a merged arc and exit from it
merged.add(angleI);
inMerged = false;
} else if (!inMerged && enteringI) {
// we were outside and enter into a merged arc
merged.add(angleI);
inMerged = true;
}
if (i == iEnd) {
i = -1;
} else {
++i;
angleI = MathUtils.normalizeAngle(limits.get(i), reference);
}
enteringI = !enteringI;
} else {
if (inMerged) {
if (enteringI && !enteringJ) {
// we were in a merged arc and exit from it
merged.add(angleJ);
inMerged = false;
}
} else {
if (enteringJ) {
// we were outside and enter into a merged arc
merged.add(angleJ);
inMerged = true;
}
}
if (j == jEnd) {
j = -1;
} else {
j = (j + 1) % otherLimits.size();
angleJ = MathUtils.normalizeAngle(otherLimits.get(j), reference);
}
enteringJ = !enteringJ;
}
}
if (inMerged) {
// we end the merging loop inside a merged arc,
// we have to put its start at the front of the limits list
if (merged.isEmpty()) {
// the merged arc covers all the circle
merged.add(0.0);
merged.add(MathUtils.TWO_PI);
} else {
double previousAngle = merged.get(merged.size() - 1) - MathUtils.TWO_PI;
for (int k = 0; k < merged.size() - 1; ++k) {
final double tmp = merged.get(k);
merged.set(k, previousAngle);
previousAngle = tmp;
}
merged.set(merged.size() - 1, previousAngle);
}
}
}
return new SubChord(chord, merged);
}
}

View File

@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.spherical.oned;
import org.apache.commons.math3.geometry.partitioning.AbstractSubHyperplane;
import org.apache.commons.math3.geometry.partitioning.Hyperplane;
import org.apache.commons.math3.geometry.partitioning.Region;
import org.apache.commons.math3.geometry.partitioning.Side;
/** This class represents sub-hyperplane for {@link LimitAngle}.
* <p>Instances of this class are guaranteed to be immutable.</p>
* @version $Id$
* @since 3.3
*/
public class SubLimitAngle extends AbstractSubHyperplane<Sphere1D, Sphere1D> {
/** Simple constructor.
* @param hyperplane underlying hyperplane
* @param remainingRegion remaining region of the hyperplane
*/
public SubLimitAngle(final Hyperplane<Sphere1D> hyperplane,
final Region<Sphere1D> remainingRegion) {
super(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
@Override
public double getSize() {
return 0;
}
/** {@inheritDoc} */
public boolean isEmpty() {
return false;
}
/** {@inheritDoc} */
@Override
protected AbstractSubHyperplane<Sphere1D, Sphere1D> buildNew(final Hyperplane<Sphere1D> hyperplane,
final Region<Sphere1D> remainingRegion) {
return new SubLimitAngle(hyperplane, remainingRegion);
}
/** {@inheritDoc} */
@Override
public Side side(final Hyperplane<Sphere1D> hyperplane) {
final double global = hyperplane.getOffset(((LimitAngle) getHyperplane()).getLocation());
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
/** {@inheritDoc} */
@Override
public SplitSubHyperplane<Sphere1D> split(final Hyperplane<Sphere1D> hyperplane) {
final double global = hyperplane.getOffset(((LimitAngle) getHyperplane()).getLocation());
return (global < -1.0e-10) ?
new SplitSubHyperplane<Sphere1D>(null, this) :
new SplitSubHyperplane<Sphere1D>(this, null);
}
}

View File

@ -23,8 +23,8 @@ import org.apache.commons.math3.geometry.partitioning.Embedding;
import org.apache.commons.math3.geometry.partitioning.Hyperplane; import org.apache.commons.math3.geometry.partitioning.Hyperplane;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane; import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
import org.apache.commons.math3.geometry.partitioning.Transform; import org.apache.commons.math3.geometry.partitioning.Transform;
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.ArcsSet;
import org.apache.commons.math3.geometry.spherical.oned.Chord;
import org.apache.commons.math3.geometry.spherical.oned.S1Point; import org.apache.commons.math3.geometry.spherical.oned.S1Point;
import org.apache.commons.math3.geometry.spherical.oned.Sphere1D; import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
import org.apache.commons.math3.util.FastMath; import org.apache.commons.math3.util.FastMath;
@ -223,15 +223,15 @@ public class Circle implements Hyperplane<Sphere2D>, Embedding<Sphere2D, Sphere1
return pole; return pole;
} }
/** Get the chord of the instance that lies inside the other circle. /** Get the arc of the instance that lies inside the other circle.
* @param other other circle * @param other other circle
* @return chord of the instance that lies inside the other circle * @return arc of the instance that lies inside the other circle
* (guaranteed to always have a length of \( \pi \)) * (guaranteed to always have a length of \( \pi \))
*/ */
public Chord getChord(final Circle other) { public Arc getInsideArc(final Circle other) {
final double alpha = getPhase(other.pole); final double alpha = getPhase(other.pole);
final double halfPi = 0.5 * FastMath.PI; final double halfPi = 0.5 * FastMath.PI;
return new Chord(alpha - halfPi, alpha + halfPi, tolerance); return new Arc(alpha - halfPi, alpha + halfPi, tolerance);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */

View File

@ -22,6 +22,7 @@ import org.apache.commons.math3.geometry.partitioning.Hyperplane;
import org.apache.commons.math3.geometry.partitioning.Region; import org.apache.commons.math3.geometry.partitioning.Region;
import org.apache.commons.math3.geometry.partitioning.Side; import org.apache.commons.math3.geometry.partitioning.Side;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane; 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.ArcsSet;
import org.apache.commons.math3.geometry.spherical.oned.Chord; import org.apache.commons.math3.geometry.spherical.oned.Chord;
import org.apache.commons.math3.geometry.spherical.oned.Sphere1D; import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
@ -54,16 +55,8 @@ public class SubCircle extends AbstractSubHyperplane<Sphere2D, Sphere1D> {
final Circle thisCircle = (Circle) getHyperplane(); final Circle thisCircle = (Circle) getHyperplane();
final Circle otherCircle = (Circle) hyperplane; final Circle otherCircle = (Circle) hyperplane;
final Chord chord = thisCircle.getChord(otherCircle); final Arc arc = thisCircle.getInsideArc(otherCircle);
return ((ArcsSet) getRemainingRegion()).side(arc);
if (chord == null) {
// the circles are disjoint
final double global = otherCircle.getOffset(thisCircle.getXAxis());
return (global < -1.0e-10) ? Side.MINUS : ((global > 1.0e-10) ? Side.PLUS : Side.HYPER);
}
// the circles do intersect each other
return getRemainingRegion().side(chord);
} }
@ -73,17 +66,8 @@ public class SubCircle extends AbstractSubHyperplane<Sphere2D, Sphere1D> {
final Circle thisCircle = (Circle) getHyperplane(); final Circle thisCircle = (Circle) getHyperplane();
final Circle otherCircle = (Circle) hyperplane; final Circle otherCircle = (Circle) hyperplane;
final Chord chord = thisCircle.getChord(otherCircle); final Arc arc = thisCircle.getInsideArc(otherCircle);
if (chord == null) {
// the circles are disjoint
final double global = otherCircle.getOffset(thisCircle.getXAxis());
return (global < -1.0e-10) ?
new SplitSubHyperplane<Sphere2D>(null, this) :
new SplitSubHyperplane<Sphere2D>(this, null);
}
// the circles do intersect
final SubHyperplane<Sphere1D> subMinus = chord.wholeHyperplane(); final SubHyperplane<Sphere1D> subMinus = chord.wholeHyperplane();
final SubHyperplane<Sphere1D> subPlus = chord.getReverse().wholeHyperplane(); final SubHyperplane<Sphere1D> subPlus = chord.getReverse().wholeHyperplane();
final BSPTree<Sphere1D> splitTree = getRemainingRegion().getTree(false).split(subMinus); final BSPTree<Sphere1D> splitTree = getRemainingRegion().getTree(false).split(subMinus);

View File

@ -16,8 +16,10 @@
*/ */
package org.apache.commons.math3.geometry.spherical.oned; package org.apache.commons.math3.geometry.spherical.oned;
import org.apache.commons.math3.exception.NumberIsTooLargeException;
import org.apache.commons.math3.geometry.partitioning.Region; import org.apache.commons.math3.geometry.partitioning.Region;
import org.apache.commons.math3.util.FastMath; import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.MathUtils;
import org.apache.commons.math3.util.Precision; import org.apache.commons.math3.util.Precision;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -26,49 +28,52 @@ public class ArcTest {
@Test @Test
public void testArc() { public void testArc() {
Arc arc = new Arc(2.3, 5.7); Arc arc = new Arc(2.3, 5.7, 1.0e-10);
Assert.assertEquals(3.4, arc.getSize(), 1.0e-10); Assert.assertEquals(3.4, arc.getSize(), 1.0e-10);
Assert.assertEquals(4.0, arc.getBarycenter(), 1.0e-10); Assert.assertEquals(4.0, arc.getBarycenter(), 1.0e-10);
Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(2.3, 1.0e-10)); Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(2.3));
Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(5.7, 1.0e-10)); Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(5.7));
Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(1.2, 1.0e-10)); Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(1.2));
Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(8.5, 1.0e-10)); Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(8.5));
Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(8.7, 1.0e-10)); Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(8.7));
Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(3.0, 1.0e-10)); Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(3.0));
Assert.assertEquals(2.3, arc.getInf(), 1.0e-10); Assert.assertEquals(2.3, arc.getInf(), 1.0e-10);
Assert.assertEquals(5.7, arc.getSup(), 1.0e-10); Assert.assertEquals(5.7, arc.getSup(), 1.0e-10);
Assert.assertEquals(4.0, arc.getBarycenter(), 1.0e-10); Assert.assertEquals(4.0, arc.getBarycenter(), 1.0e-10);
Assert.assertEquals(3.4, arc.getSize(), 1.0e-10); Assert.assertEquals(3.4, arc.getSize(), 1.0e-10);
} }
@Test(expected=NumberIsTooLargeException.class)
public void testWrongInterval() {
new Arc(1.2, 0.0, 1.0e-10);
}
@Test @Test
public void testTolerance() { public void testTolerance() {
Arc arc = new Arc(2.3, 5.7); Assert.assertEquals(Region.Location.OUTSIDE, new Arc(2.3, 5.7, 1.0).checkPoint(1.2));
Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(1.2, 1.0)); Assert.assertEquals(Region.Location.BOUNDARY, new Arc(2.3, 5.7, 1.2).checkPoint(1.2));
Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(1.2, 1.2)); Assert.assertEquals(Region.Location.OUTSIDE, new Arc(2.3, 5.7, 0.7).checkPoint(6.5));
Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(6.5, 0.7)); Assert.assertEquals(Region.Location.BOUNDARY, new Arc(2.3, 5.7, 0.9).checkPoint(6.5));
Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(6.5, 0.9)); Assert.assertEquals(Region.Location.INSIDE, new Arc(2.3, 5.7, 0.6).checkPoint(3.0));
Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(3.0, 0.6)); Assert.assertEquals(Region.Location.BOUNDARY, new Arc(2.3, 5.7, 0.8).checkPoint(3.0));
Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(3.0, 0.8));
} }
@Test @Test
public void testFullCircle() { public void testFullCircle() {
Arc arc = new Arc(9.0, 9.0); Arc arc = new Arc(9.0, 9.0, 1.0e-10);
// no boundaries on a full circle // no boundaries on a full circle
Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(9.0, 1.0e-10)); Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(9.0));
Assert.assertEquals(9.0, arc.getInf(), 1.0e-10); Assert.assertEquals(.0, arc.getInf(), 1.0e-10);
Assert.assertEquals(9.0 + 2.0 * FastMath.PI, arc.getSup(), 1.0e-10); Assert.assertEquals(MathUtils.TWO_PI, arc.getSup(), 1.0e-10);
Assert.assertEquals(2.0 * FastMath.PI, arc.getSize(), 1.0e-10); Assert.assertEquals(2.0 * FastMath.PI, arc.getSize(), 1.0e-10);
for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) { for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) {
Assert.assertEquals(Region.Location.INSIDE, Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(alpha));
arc.checkPoint(alpha, 1.0e-10));
} }
} }
@Test @Test
public void testSmall() { public void testSmall() {
Arc arc = new Arc(1.0, FastMath.nextAfter(1.0, Double.POSITIVE_INFINITY)); Arc arc = new Arc(1.0, FastMath.nextAfter(1.0, Double.POSITIVE_INFINITY), Precision.EPSILON);
Assert.assertEquals(2 * Precision.EPSILON, arc.getSize(), Precision.SAFE_MIN); Assert.assertEquals(2 * Precision.EPSILON, arc.getSize(), Precision.SAFE_MIN);
Assert.assertEquals(1.0, arc.getBarycenter(), Precision.EPSILON); Assert.assertEquals(1.0, arc.getBarycenter(), Precision.EPSILON);
} }

View File

@ -16,11 +16,14 @@
*/ */
package org.apache.commons.math3.geometry.spherical.oned; package org.apache.commons.math3.geometry.spherical.oned;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.commons.math3.exception.NumberIsTooLargeException;
import org.apache.commons.math3.geometry.partitioning.Region; import org.apache.commons.math3.geometry.partitioning.Region;
import org.apache.commons.math3.geometry.partitioning.Region.Location; import org.apache.commons.math3.geometry.partitioning.Region.Location;
import org.apache.commons.math3.geometry.partitioning.RegionFactory; import org.apache.commons.math3.geometry.partitioning.RegionFactory;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
import org.apache.commons.math3.util.FastMath; import org.apache.commons.math3.util.FastMath;
import org.apache.commons.math3.util.MathUtils; import org.apache.commons.math3.util.MathUtils;
import org.apache.commons.math3.util.Precision; import org.apache.commons.math3.util.Precision;
@ -33,7 +36,7 @@ public class ArcsSetTest {
public void testArc() { public void testArc() {
ArcsSet set = new ArcsSet(2.3, 5.7, 1.0e-10); ArcsSet set = new ArcsSet(2.3, 5.7, 1.0e-10);
Assert.assertEquals(3.4, set.getSize(), 1.0e-10); Assert.assertEquals(3.4, set.getSize(), 1.0e-10);
Assert.assertEquals(4.0, ((S1Point) set.getBarycenter()).getAlpha(), 1.0e-10); Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20);
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new S1Point(2.3))); Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new S1Point(2.3)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new S1Point(5.7))); Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new S1Point(5.7)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(1.2))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(1.2)));
@ -43,13 +46,43 @@ public class ArcsSetTest {
Assert.assertEquals(1, set.asList().size()); Assert.assertEquals(1, set.asList().size());
Assert.assertEquals(2.3, set.asList().get(0).getInf(), 1.0e-10); Assert.assertEquals(2.3, set.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.7, set.asList().get(0).getSup(), 1.0e-10); Assert.assertEquals(5.7, set.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(4.0, ((S1Point) set.getBarycenter()).getAlpha(), 1.0e-10); Assert.assertEquals(2.3, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertEquals(3.4, set.getSize(), 1.0e-10); Assert.assertFalse(set.getSmallestLimit().isDirect());
Assert.assertEquals(5.7, set.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertTrue(set.getLargestLimit().isDirect());
} }
@Test @Test
public void testFullCircle() { public void testWrapAround2PiArc() {
ArcsSet set = new ArcsSet(9.0, 9.0, 1.0e-10); ArcsSet set = new ArcsSet(5.7 - MathUtils.TWO_PI, 2.3, 1.0e-10);
Assert.assertEquals(MathUtils.TWO_PI - 3.4, set.getSize(), 1.0e-10);
Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20);
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new S1Point(2.3)));
Assert.assertEquals(Region.Location.BOUNDARY, set.checkPoint(new S1Point(5.7)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(1.2)));
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(8.5)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(8.7)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(3.0)));
Assert.assertEquals(1, set.asList().size());
Assert.assertEquals(5.7, set.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(2.3 + MathUtils.TWO_PI, set.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(2.3, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertTrue(set.getSmallestLimit().isDirect());
Assert.assertEquals(5.7, set.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertFalse(set.getLargestLimit().isDirect());
}
@Test(expected=NumberIsTooLargeException.class)
public void testWrongInterval() {
new ArcsSet(1.2, 0.0, 1.0e-10);
}
@Test
public void testFullEqualEndPoints() {
ArcsSet set = new ArcsSet(1.0, 1.0, 1.0e-10);
Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20);
Assert.assertNull(set.getSmallestLimit());
Assert.assertNull(set.getLargestLimit());
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(9.0))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(9.0)));
for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) { for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) {
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(alpha))); Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(alpha)));
@ -60,6 +93,60 @@ public class ArcsSetTest {
Assert.assertEquals(2 * FastMath.PI, set.getSize(), 1.0e-10); Assert.assertEquals(2 * FastMath.PI, set.getSize(), 1.0e-10);
} }
@Test
public void testFullCircle() {
ArcsSet set = new ArcsSet(1.0e-10);
Assert.assertEquals(1.0e-10, set.getTolerance(), 1.0e-20);
Assert.assertNull(set.getSmallestLimit());
Assert.assertNull(set.getLargestLimit());
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(9.0)));
for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) {
Assert.assertEquals(Region.Location.INSIDE, set.checkPoint(new S1Point(alpha)));
}
Assert.assertEquals(1, set.asList().size());
Assert.assertEquals(0.0, set.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(2 * FastMath.PI, set.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(2 * FastMath.PI, set.getSize(), 1.0e-10);
}
@Test
public void testEmpty() {
ArcsSet empty = (ArcsSet) new RegionFactory<Sphere1D>().getComplement(new ArcsSet(1.0e-10));
Assert.assertEquals(1.0e-10, empty.getTolerance(), 1.0e-20);
Assert.assertEquals(0.0, empty.getSize(), 1.0e-10);
Assert.assertTrue(empty.asList().isEmpty());
Assert.assertNull(empty.getSmallestLimit());
Assert.assertNull(empty.getLargestLimit());
}
@Test
public void testTiny() {
ArcsSet tiny = new ArcsSet(0.0, Precision.SAFE_MIN / 2, 1.0e-10);
Assert.assertEquals(1.0e-10, tiny.getTolerance(), 1.0e-20);
Assert.assertEquals(Precision.SAFE_MIN / 2, tiny.getSize(), 1.0e-10);
Assert.assertEquals(1, tiny.asList().size());
Assert.assertEquals(0.0, tiny.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(Precision.SAFE_MIN / 2, tiny.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(0.0, tiny.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertEquals(Precision.SAFE_MIN / 2, tiny.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
}
@Test
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());
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);
Assert.assertEquals(1, set.asList().size());
Assert.assertEquals(0.0, set.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(MathUtils.TWO_PI, set.asList().get(0).getSup(), 1.0e-10);
Assert.assertEquals(0.0, set.getSmallestLimit().getLocation().getAlpha(), 1.0e-10);
Assert.assertFalse(set.getSmallestLimit().isDirect());
Assert.assertEquals(0.0, set.getLargestLimit().getLocation().getAlpha(), 1.0e-10);
}
@Test @Test
public void testDifference() { public void testDifference() {
@ -139,9 +226,11 @@ public class ArcsSetTest {
} }
List<Arc> aMbList = aMb.asList(); List<Arc> aMbList = aMb.asList();
Assert.assertEquals(1, aMbList.size()); Assert.assertEquals(2, aMbList.size());
Assert.assertEquals(1.0, aMbList.get(0).getInf(), 1.0e-10); Assert.assertEquals(1.0, aMbList.get(0).getInf(), 1.0e-10);
Assert.assertEquals(3.0, aMbList.get(0).getSup(), 1.0e-10); Assert.assertEquals(3.0, aMbList.get(0).getSup(), 1.0e-10);
Assert.assertEquals(5.0, aMbList.get(1).getInf(), 1.0e-10);
Assert.assertEquals(5.5, aMbList.get(1).getSup(), 1.0e-10);
} }
@ -155,7 +244,6 @@ public class ArcsSetTest {
new ArcsSet(0.5, 2.0, 1.0e-10)), new ArcsSet(0.5, 2.0, 1.0e-10)),
new ArcsSet(0.0, 5.5, 1.0e-10)); new ArcsSet(0.0, 5.5, 1.0e-10));
Assert.assertEquals(3.0, set.getSize(), 1.0e-10); Assert.assertEquals(3.0, set.getSize(), 1.0e-10);
Assert.assertEquals(7.0 / 3.0, ((S1Point) set.getBarycenter()).getAlpha(), 1.0e-10);
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(0.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(0.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(4.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(4.0)));
Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(6.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(6.0)));
@ -179,7 +267,6 @@ public class ArcsSetTest {
public void testSinglePoint() { public void testSinglePoint() {
ArcsSet set = new ArcsSet(1.0, FastMath.nextAfter(1.0, Double.POSITIVE_INFINITY), 1.0e-10); ArcsSet set = new ArcsSet(1.0, FastMath.nextAfter(1.0, Double.POSITIVE_INFINITY), 1.0e-10);
Assert.assertEquals(2 * Precision.EPSILON, set.getSize(), Precision.SAFE_MIN); Assert.assertEquals(2 * Precision.EPSILON, set.getSize(), Precision.SAFE_MIN);
Assert.assertEquals(1.0, ((S1Point) set.getBarycenter()).getAlpha(), Precision.EPSILON);
} }
} }

View File

@ -1,92 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.spherical.oned;
import org.apache.commons.math3.util.MathUtils;
import org.junit.Assert;
import org.junit.Test;
public class ChordTest {
@Test
public void testChord() {
Chord chord = new Chord(2.3, 5.7, 1.0e-10);
Assert.assertEquals(2.3, chord.copySelf().getStart(), 1.0e-10);
Assert.assertEquals(5.7, chord.copySelf().getEnd(), 1.0e-10);
Assert.assertEquals(1.0e-10, chord.copySelf().getTolerance(), 1.0e-20);
Assert.assertEquals(3.4, chord.wholeHyperplane().getSize(), 1.0e-10);
checkOutside(chord, 2.25);
checkBoundary(chord, 2.3);
checkInside(chord, 2.35);
checkInside(chord, 5.65);
checkBoundary(chord, 5.7);
checkOutside(chord, 5.75);
}
@Test
public void testReverse() {
Chord chord = new Chord(2.3, 5.7, 1.0e-10);
Chord reversed = chord.getReverse();
Assert.assertEquals(chord.getEnd(), reversed.getStart(), 1.0e-10);
Assert.assertEquals(chord.getStart() + MathUtils.TWO_PI, reversed.getEnd(), 1.0e-10);
Assert.assertEquals(1.0e-10, reversed.getTolerance(), 1.0e-20);
Assert.assertEquals(MathUtils.TWO_PI - 3.4, reversed.wholeHyperplane().getSize(), 1.0e-10);
Assert.assertFalse(chord.sameOrientationAs(reversed));
checkInside(reversed, 2.25);
checkBoundary(reversed, 2.3);
checkOutside(reversed, 2.35);
checkOutside(reversed, 5.65);
checkBoundary(reversed, 5.7);
checkInside(reversed, 5.75);
}
@Test
public void testWholeHyperplane() {
Chord chord = new Chord(2.3, 5.7, 1.0e-10);
SubChord subChord = chord.wholeHyperplane();
Assert.assertTrue(chord == subChord.getHyperplane());
Assert.assertEquals(chord.getEnd() - chord.getStart(), subChord.getSize(), 1.0e-10);
}
@Test
public void testWholeSpace() {
Chord chord = new Chord(2.3, 5.7, 1.0e-10);
ArcsSet set = chord.wholeSpace();
Assert.assertEquals(1, set.asList().size());
Assert.assertEquals(0.0, set.asList().get(0).getInf(), 1.0e-10);
Assert.assertEquals(MathUtils.TWO_PI, set.asList().get(0).getSup(), 1.0e-10);
}
private void checkInside(Chord chord, double alpha) {
for (int i = -2; i < 3; ++i) {
Assert.assertTrue(chord.getOffset(new S1Point(alpha + i * MathUtils.TWO_PI)) < 0.0);
}
}
private void checkOutside(Chord chord, double alpha) {
for (int i = -2; i < 3; ++i) {
Assert.assertTrue(chord.getOffset(new S1Point(alpha + i * MathUtils.TWO_PI)) > 0.0);
}
}
private void checkBoundary(Chord chord, double alpha) {
for (int i = -2; i < 3; ++i) {
Assert.assertEquals(0.0, chord.getOffset(new S1Point(alpha + i * MathUtils.TWO_PI)), chord.getTolerance());
}
}
}

View File

@ -1,279 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.spherical.oned;
import java.util.List;
import org.apache.commons.math3.geometry.partitioning.Side;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane.SplitSubHyperplane;
import org.apache.commons.math3.util.MathUtils;
import org.junit.Assert;
import org.junit.Test;
public class SubChordTest {
@Test
public void testSubChord() {
SubChord subChord = new SubChord(new Chord(2.3, 5.7, 1.0e-10));
Assert.assertEquals(3.4, subChord.getSize(), 1.0e-10);
Assert.assertFalse(subChord.isEmpty());
Assert.assertEquals(1, subChord.getNbSubArcs());
Assert.assertEquals(2.3, subChord.getStart(0), 1.0e-10);
Assert.assertEquals(5.7, subChord.getEnd(0), 1.0e-10);
Assert.assertEquals(1, subChord.getSubArcs().size());
Assert.assertEquals(2.3, subChord.getSubArcs().get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.7, subChord.getSubArcs().get(0).getSup(), 1.0e-10);
// despite a deep copy is used, chord being immutable its copy returns the same instance
Assert.assertTrue(subChord.getHyperplane() == subChord.copySelf().getHyperplane());
}
@Test
public void testSideEmbedded() {
SubChord s35 = new SubChord(new Chord(3.0, 5.0, 1.0e-10));
SubChord s16 = new SubChord(new Chord(1.0, 6.0, 1.0e-10));
Assert.assertEquals(Side.BOTH, s16.side(s35.getHyperplane()));
Assert.assertEquals(Side.BOTH, s16.side(s35.getHyperplane().getReverse()));
Assert.assertEquals(Side.MINUS, s35.side(s16.getHyperplane()));
Assert.assertEquals(Side.PLUS, s35.side(s16.getHyperplane().getReverse()));
}
@Test
public void testSideOverlapping() {
SubChord s35 = new SubChord(new Chord(3.0, 5.0, 1.0e-10));
SubChord s46 = new SubChord(new Chord(4.0, 6.0, 1.0e-10));
Assert.assertEquals(Side.BOTH, s46.side(s35.getHyperplane()));
Assert.assertEquals(Side.BOTH, s46.side(s35.getHyperplane().getReverse()));
Assert.assertEquals(Side.BOTH, s35.side(s46.getHyperplane()));
Assert.assertEquals(Side.BOTH, s35.side(s46.getHyperplane().getReverse()));
}
@Test
public void testSideHyper() {
Chord zeroLength = new Chord(2.0, 2.0, 1.0e-10);
SubChord sub = new SubChord(zeroLength);
Assert.assertTrue(sub.isEmpty());
Assert.assertEquals(Side.HYPER, sub.side(zeroLength));
}
@Test
public void testSplitEmbedded() {
SubChord s35 = new SubChord(new Chord(3.0, 5.0, 1.0e-10));
SubChord s16 = new SubChord(new Chord(1.0, 6.0, 1.0e-10));
SplitSubHyperplane<Sphere1D> split1 = s16.split(s35.getHyperplane());
SubChord split1Plus = (SubChord) split1.getPlus();
SubChord split1Minus = (SubChord) split1.getMinus();
Assert.assertEquals(3.0, split1Plus.getSize(), 1.0e-10);
Assert.assertEquals(2, split1Plus.getNbSubArcs());
Assert.assertEquals(1.0, split1Plus.getStart(0), 1.0e-10);
Assert.assertEquals(3.0, split1Plus.getEnd(0), 1.0e-10);
Assert.assertEquals(5.0, split1Plus.getStart(1), 1.0e-10);
Assert.assertEquals(6.0, split1Plus.getEnd(1), 1.0e-10);
Assert.assertEquals(2.0, split1Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split1Minus.getNbSubArcs());
Assert.assertEquals(3.0, split1Minus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split1Minus.getEnd(0), 1.0e-10);
SplitSubHyperplane<Sphere1D> split2 = s16.split(s35.getHyperplane().getReverse());
SubChord split2Plus = (SubChord) split2.getPlus();
SubChord split2Minus = (SubChord) split2.getMinus();
Assert.assertEquals(2.0, split2Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split2Plus.getNbSubArcs());
Assert.assertEquals(3.0, split2Plus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split2Plus.getEnd(0), 1.0e-10);
Assert.assertEquals(3.0, split2Minus.getSize(), 1.0e-10);
Assert.assertEquals(2, split2Minus.getNbSubArcs());
Assert.assertEquals(1.0, split2Minus.getStart(0), 1.0e-10);
Assert.assertEquals(3.0, split2Minus.getEnd(0), 1.0e-10);
Assert.assertEquals(5.0, split2Minus.getStart(1), 1.0e-10);
Assert.assertEquals(6.0, split2Minus.getEnd(1), 1.0e-10);
SplitSubHyperplane<Sphere1D> split3 = s35.split(s16.getHyperplane());
SubChord split3Plus = (SubChord) split3.getPlus();
SubChord split3Minus = (SubChord) split3.getMinus();
Assert.assertNull(split3Plus);
Assert.assertEquals(2.0, split3Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split3Minus.getNbSubArcs());
Assert.assertEquals(3.0, split3Minus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split3Minus.getEnd(0), 1.0e-10);
SplitSubHyperplane<Sphere1D> split4 = s35.split(s16.getHyperplane().getReverse());
SubChord split4Plus = (SubChord) split4.getPlus();
SubChord split4Minus = (SubChord) split4.getMinus();
Assert.assertEquals(2.0, split4Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split4Plus.getNbSubArcs());
Assert.assertEquals(3.0, split4Plus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split4Plus.getEnd(0), 1.0e-10);
Assert.assertNull(split4Minus);
}
@Test
public void testSplitOverlapping() {
SubChord s35 = new SubChord(new Chord(3.0, 5.0, 1.0e-10));
SubChord s46 = new SubChord(new Chord(4.0, 6.0, 1.0e-10));
SplitSubHyperplane<Sphere1D> split1 = s46.split(s35.getHyperplane());
SubChord split1Plus = (SubChord) split1.getPlus();
SubChord split1Minus = (SubChord) split1.getMinus();
Assert.assertEquals(1.0, split1Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split1Plus.getNbSubArcs());
Assert.assertEquals(5.0, split1Plus.getStart(0), 1.0e-10);
Assert.assertEquals(6.0, split1Plus.getEnd(0), 1.0e-10);
Assert.assertEquals(1.0, split1Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split1Minus.getNbSubArcs());
Assert.assertEquals(4.0, split1Minus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split1Minus.getEnd(0), 1.0e-10);
SplitSubHyperplane<Sphere1D> split2 = s46.split(s35.getHyperplane().getReverse());
SubChord split2Plus = (SubChord) split2.getPlus();
SubChord split2Minus = (SubChord) split2.getMinus();
Assert.assertEquals(1.0, split2Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split2Plus.getNbSubArcs());
Assert.assertEquals(4.0, split2Plus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split2Plus.getEnd(0), 1.0e-10);
Assert.assertEquals(1.0, split2Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split2Minus.getNbSubArcs());
Assert.assertEquals(5.0, split2Minus.getStart(0), 1.0e-10);
Assert.assertEquals(6.0, split2Minus.getEnd(0), 1.0e-10);
SplitSubHyperplane<Sphere1D> split3 = s35.split(s46.getHyperplane());
SubChord split3Plus = (SubChord) split3.getPlus();
SubChord split3Minus = (SubChord) split3.getMinus();
Assert.assertEquals(1.0, split3Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split3Plus.getNbSubArcs());
Assert.assertEquals(3.0, split3Plus.getStart(0), 1.0e-10);
Assert.assertEquals(4.0, split3Plus.getEnd(0), 1.0e-10);
Assert.assertEquals(1.0, split3Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split3Minus.getNbSubArcs());
Assert.assertEquals(4.0, split3Minus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split3Minus.getEnd(0), 1.0e-10);
SplitSubHyperplane<Sphere1D> split4 = s35.split(s46.getHyperplane().getReverse());
SubChord split4Plus = (SubChord) split4.getPlus();
SubChord split4Minus = (SubChord) split4.getMinus();
Assert.assertEquals(1.0, split4Plus.getSize(), 1.0e-10);
Assert.assertEquals(1, split4Plus.getNbSubArcs());
Assert.assertEquals(4.0, split4Plus.getStart(0), 1.0e-10);
Assert.assertEquals(5.0, split4Plus.getEnd(0), 1.0e-10);
Assert.assertEquals(1.0, split4Minus.getSize(), 1.0e-10);
Assert.assertEquals(1, split4Minus.getNbSubArcs());
Assert.assertEquals(3.0, split4Minus.getStart(0), 1.0e-10);
Assert.assertEquals(4.0, split4Minus.getEnd(0), 1.0e-10);
}
@Test
public void testReunite() {
// build sub-chord with arcs: [0.5, 1.375], [1.5, 1.75], [2.25, 4.5]
SubChord sA1 = new SubChord(new Chord(0.5, 4.5, 1.0e-10));
SubChord sA2 = (SubChord) sA1.split(new Chord(1.375, 1.5, 1.0e-10)).getPlus();
SubChord sA = (SubChord) sA2.split(new Chord(1.75, 2.25, 1.0e-10)).getPlus();
List<Arc> listSa = sA.getSubArcs();
Assert.assertEquals(3, listSa.size());
Assert.assertEquals(0.5, listSa.get(0).getInf(), 1.0e-10);
Assert.assertEquals(1.375, listSa.get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.5, listSa.get(1).getInf(), 1.0e-10);
Assert.assertEquals(1.75, listSa.get(1).getSup(), 1.0e-10);
Assert.assertEquals(2.25, listSa.get(2).getInf(), 1.0e-10);
Assert.assertEquals(4.5, listSa.get(2).getSup(), 1.0e-10);
// sub-chord with arcs: [5.0, 5.5], [5.75, 1.0+2pi], [ 1.625+2pi, 1.875+2pi], [2.0+2pi, 2.125+2pi], [2.5+2pi, 3.5+2pi]
SubChord sB1 = new SubChord(new Chord(5.0, 3.5 + MathUtils.TWO_PI, 1.0e-10));
SubChord sB2 = (SubChord) sB1.split(new Chord(5.5, 5.75, 1.0e-10)).getPlus();
SubChord sB3 = (SubChord) sB2.split(new Chord(1.0 + MathUtils.TWO_PI, 1.625 + MathUtils.TWO_PI, 1.0e-10)).getPlus();
SubChord sB4 = (SubChord) sB3.split(new Chord(1.875 + MathUtils.TWO_PI, 2.0 + MathUtils.TWO_PI, 1.0e-10)).getPlus();
SubChord sB = (SubChord) sB4.split(new Chord(2.125 + MathUtils.TWO_PI, 2.5 + MathUtils.TWO_PI, 1.0e-10)).getPlus();
List<Arc> listSb = sB.getSubArcs();
Assert.assertEquals(5, listSb.size());
Assert.assertEquals(5.0, listSb.get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.5, listSb.get(0).getSup(), 1.0e-10);
Assert.assertEquals(5.75, listSb.get(1).getInf(), 1.0e-10);
Assert.assertEquals(1.0 + MathUtils.TWO_PI, listSb.get(1).getSup(), 1.0e-10);
Assert.assertEquals(1.625 + MathUtils.TWO_PI, listSb.get(2).getInf(), 1.0e-10);
Assert.assertEquals(1.875 + MathUtils.TWO_PI, listSb.get(2).getSup(), 1.0e-10);
Assert.assertEquals(2.0 + MathUtils.TWO_PI, listSb.get(3).getInf(), 1.0e-10);
Assert.assertEquals(2.125 + MathUtils.TWO_PI, listSb.get(3).getSup(), 1.0e-10);
Assert.assertEquals(2.5 + MathUtils.TWO_PI, listSb.get(4).getInf(), 1.0e-10);
Assert.assertEquals(3.5 + MathUtils.TWO_PI, listSb.get(4).getSup(), 1.0e-10);
List<Arc> listAB = sA.reunite(sB).getSubArcs();
Assert.assertEquals(5, listAB.size());
Assert.assertEquals(5.75 - MathUtils.TWO_PI, listAB.get(0).getInf(), 1.0e-10);
Assert.assertEquals(1.375, listAB.get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.5, listAB.get(1).getInf(), 1.0e-10);
Assert.assertEquals(1.875, listAB.get(1).getSup(), 1.0e-10);
Assert.assertEquals(2.0, listAB.get(2).getInf(), 1.0e-10);
Assert.assertEquals(2.125, listAB.get(2).getSup(), 1.0e-10);
Assert.assertEquals(2.25, listAB.get(3).getInf(), 1.0e-10);
Assert.assertEquals(4.5, listAB.get(3).getSup(), 1.0e-10);
Assert.assertEquals(5.0, listAB.get(4).getInf(), 1.0e-10);
Assert.assertEquals(5.5, listAB.get(4).getSup(), 1.0e-10);
List<Arc> listBA = sB.reunite(sA).getSubArcs();
Assert.assertEquals(5, listBA.size());
Assert.assertEquals(5.0, listBA.get(0).getInf(), 1.0e-10);
Assert.assertEquals(5.5, listBA.get(0).getSup(), 1.0e-10);
Assert.assertEquals(5.75, listBA.get(1).getInf(), 1.0e-10);
Assert.assertEquals(1.375 + MathUtils.TWO_PI, listBA.get(1).getSup(), 1.0e-10);
Assert.assertEquals(1.5 + MathUtils.TWO_PI, listBA.get(2).getInf(), 1.0e-10);
Assert.assertEquals(1.875 + MathUtils.TWO_PI, listBA.get(2).getSup(), 1.0e-10);
Assert.assertEquals(2.0 + MathUtils.TWO_PI, listBA.get(3).getInf(), 1.0e-10);
Assert.assertEquals(2.125 + MathUtils.TWO_PI, listBA.get(3).getSup(), 1.0e-10);
Assert.assertEquals(2.25 + MathUtils.TWO_PI, listBA.get(4).getInf(), 1.0e-10);
Assert.assertEquals(4.5 + MathUtils.TWO_PI, listBA.get(4).getSup(), 1.0e-10);
// special cases
List<Arc> listAEmpty = sA.reunite(new SubChord(new Chord(0, 0, 1.0e-10))).getSubArcs();
Assert.assertEquals(3, listAEmpty.size());
Assert.assertEquals(0.5, listAEmpty.get(0).getInf(), 1.0e-10);
Assert.assertEquals(1.375, listAEmpty.get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.5, listAEmpty.get(1).getInf(), 1.0e-10);
Assert.assertEquals(1.75, listAEmpty.get(1).getSup(), 1.0e-10);
Assert.assertEquals(2.25, listAEmpty.get(2).getInf(), 1.0e-10);
Assert.assertEquals(4.5, listAEmpty.get(2).getSup(), 1.0e-10);
List<Arc> listEmptyA = new SubChord(new Chord(0, 0, 1.0e-10)).reunite(sA).getSubArcs();
Assert.assertEquals(3, listAEmpty.size());
Assert.assertEquals(0.5, listEmptyA.get(0).getInf(), 1.0e-10);
Assert.assertEquals(1.375, listEmptyA.get(0).getSup(), 1.0e-10);
Assert.assertEquals(1.5, listEmptyA.get(1).getInf(), 1.0e-10);
Assert.assertEquals(1.75, listEmptyA.get(1).getSup(), 1.0e-10);
Assert.assertEquals(2.25, listEmptyA.get(2).getInf(), 1.0e-10);
Assert.assertEquals(4.5, listEmptyA.get(2).getSup(), 1.0e-10);
}
@Test
public void testReuniteFullCircle() {
SubChord s1 = new SubChord(new Chord(0, 4, 1.0e-10));
SubChord s2 = new SubChord(new Chord(3, 7, 1.0e-10));
Assert.assertEquals(MathUtils.TWO_PI, s1.reunite(s2).getSize(), 1.0e-10);
}
}