From d83cdb913332b02fd310f5cdf3df045162c4cc54 Mon Sep 17 00:00:00 2001 From: Luc Maisonobe Date: Wed, 1 Jan 2014 17:30:06 +0000 Subject: [PATCH] Simplified 1-sphere case. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1554654 13f79535-47bb-0310-9956-ffa450edef68 --- .../math3/geometry/spherical/oned/Arc.java | 59 +++- .../geometry/spherical/oned/ArcsSet.java | 286 +++++++++++++-- .../oned/{Chord.java => LimitAngle.java} | 84 ++--- .../geometry/spherical/oned/S1Point.java | 3 +- .../geometry/spherical/oned/SubChord.java | 332 ------------------ .../spherical/oned/SubLimitAngle.java | 74 ++++ .../math3/geometry/spherical/twod/Circle.java | 10 +- .../geometry/spherical/twod/SubCircle.java | 24 +- .../geometry/spherical/oned/ArcTest.java | 47 +-- .../geometry/spherical/oned/ArcsSetTest.java | 103 +++++- .../geometry/spherical/oned/ChordTest.java | 92 ----- .../geometry/spherical/oned/SubChordTest.java | 279 --------------- 12 files changed, 535 insertions(+), 858 deletions(-) rename src/main/java/org/apache/commons/math3/geometry/spherical/oned/{Chord.java => LimitAngle.java} (58%) delete mode 100644 src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubChord.java create mode 100644 src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java delete mode 100644 src/test/java/org/apache/commons/math3/geometry/spherical/oned/ChordTest.java delete mode 100644 src/test/java/org/apache/commons/math3/geometry/spherical/oned/SubChordTest.java diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java index 24ec0c341..026e0128c 100644 --- a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java +++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Arc.java @@ -16,6 +16,8 @@ */ 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.util.FastMath; import org.apache.commons.math3.util.MathUtils; @@ -38,38 +40,54 @@ public class Arc { /** Middle point of the arc. */ private final double middle; + /** Tolerance below which angles are considered identical. */ + private final double tolerance; + /** Simple constructor. *

- * 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. + * If either {@code lower} is equals to {@code upper} or + * the interval exceeds \( 2 \pi \), the arc is considered + * to be the full circle and its initial defining boundaries + * will be forgotten. {@code lower} is not allowed to be + * greater than {@code upper} (an exception is thrown in this case). + * {@code lower} will be canonicalized between 0 and \( 2 \pi \), and + * upper shifted accordingly, so the {@link #getInf()} and {@link #getSup()} + * may not return the value used at instance construction. *

* @param lower lower 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) { - this.lower = lower; - if (Precision.equals(lower, upper, 0)) { - this.upper = MathUtils.TWO_PI + lower; + public Arc(final double lower, final double upper, final double tolerance) + throws NumberIsTooLargeException { + this.tolerance = tolerance; + 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 { - 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. - * @return lower angular bound of the arc + * @return lower angular bound of the arc, + * always between 0 and \( 2 \pi \) */ public double getInf() { return lower; } /** 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() { return upper; @@ -89,14 +107,19 @@ public class Arc { 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. * @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 * 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); if (normalizedPoint < lower - tolerance || normalizedPoint > upper + tolerance) { return Location.OUTSIDE; diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java index 5678dda89..ae8eaa6e0 100644 --- a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java +++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSet.java @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.Collections; 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.BSPTree; 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; /** This class represents a region of a circle: a set of arcs. + *

+ * 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. + *

* @version $Id$ * @since 3.3 */ @@ -47,19 +56,19 @@ public class ArcsSet extends AbstractRegion { /** Build an arcs set corresponding to a single arc. *

- * 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. + * If either {@code lower} is equals to {@code upper} or + * the interval exceeds \( 2 \pi \), the arc is considered + * to be the full circle and its initial defining boundaries + * will be forgotten. {@code lower} is not allowed to be greater + * than {@code upper} (an exception is thrown in this case). *

* @param lower lower bound of the arc * @param upper upper bound of the arc * @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)); this.tolerance = tolerance; } @@ -105,33 +114,236 @@ public class ArcsSet extends AbstractRegion { } /** Build an inside/outside tree representing a single arc. - *

- * 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. - *

* @param lower lower angular bound of the arc * @param upper upper angular bound of the arc * @param tolerance tolerance below which close sub-arcs are merged together * @return the built tree + * @exception NumberIsTooLargeException if lower is greater than upper */ - private static BSPTree buildTree(final double lower, final double upper, final double tolerance) { + private static BSPTree buildTree(final double lower, final double upper, + final double tolerance) + throws NumberIsTooLargeException { - if (Precision.equals(lower, upper, 0)) { - // the tree must cover the whole real line + if (Precision.equals(lower, upper, 0) || (upper - lower) >= MathUtils.TWO_PI) { + // the tree must cover the whole circle return new BSPTree(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 - final double normalizedUpper = MathUtils.normalizeAngle(upper, lower + FastMath.PI); - return new BSPTree(new Chord(lower, normalizedUpper, tolerance).wholeHyperplane(), - new BSPTree(Boolean.FALSE), - new BSPTree(Boolean.TRUE), - null); + // this is a regular arc, covering only part of the circle + final double normalizedLower = MathUtils.normalizeAngle(lower, FastMath.PI); + final double normalizedUpper = normalizedLower + (upper - lower); + final SubHyperplane lowerCut = + new LimitAngle(new S1Point(normalizedLower), false, tolerance).wholeHyperplane(); + + if (normalizedUpper <= MathUtils.TWO_PI) { + // simple arc starting after 0 and ending before 2 \pi + final SubHyperplane upperCut = + new LimitAngle(new S1Point(normalizedUpper), true, tolerance).wholeHyperplane(); + return new BSPTree(lowerCut, + new BSPTree(Boolean.FALSE), + new BSPTree(upperCut, + new BSPTree(Boolean.FALSE), + new BSPTree(Boolean.TRUE), + null), + null); + } else { + // arc wrapping around 2 \pi + final SubHyperplane upperCut = + new LimitAngle(new S1Point(normalizedUpper - MathUtils.TWO_PI), true, tolerance).wholeHyperplane(); + return new BSPTree(lowerCut, + new BSPTree(upperCut, + new BSPTree(Boolean.FALSE), + new BSPTree(Boolean.TRUE), + null), + new BSPTree(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 node = getTree(false); + if (node.getCut() == null) { + return null; + } + + BSPTree 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 node = getTree(false); + if (node.getCut() == null) { + return null; + } + + BSPTree 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 nextNode(BSPTree node) { + + final BSPTree 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 previousNode(BSPTree node) { + + final BSPTree 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 node) { + final BSPTree 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 node) { + final BSPTree 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 findSmallest(BSPTree node) { + + BSPTree 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 findLargest(BSPTree node) { + + BSPTree 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 { } else if (size >= Precision.SAFE_MIN) { setBarycenter(new S1Point(sum / size)); } else { - final Chord chord = (Chord) getTree(false).getCut().getHyperplane(); - setBarycenter(new S1Point(0.5 * (chord.getStart() + chord.getEnd()))); + final LimitAngle limit = (LimitAngle) getTree(false).getCut().getHyperplane(); + setBarycenter(limit.getLocation()); } } } @@ -182,7 +394,7 @@ public class ArcsSet extends AbstractRegion { // the tree has a single node if ((Boolean) root.getAttribute()) { // 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 { @@ -190,6 +402,11 @@ public class ArcsSet extends AbstractRegion { final LimitsCollector finder = new LimitsCollector(); root.visit(finder); final List 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 Collections.sort(limits); @@ -201,7 +418,7 @@ public class ArcsSet extends AbstractRegion { // we can now build the list 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 { /** {@inheritDoc} */ public void visitInternalNode(final BSPTree node) { // check if the chord end points are arc limits - final Chord chord = (Chord) node.getCut().getHyperplane(); - if (checkPoint(new S1Point(chord.getStart())) == Location.BOUNDARY) { - limits.add(MathUtils.normalizeAngle(chord.getStart(), FastMath.PI)); - } - if (checkPoint(new S1Point(chord.getEnd())) == Location.BOUNDARY) { - limits.add(MathUtils.normalizeAngle(chord.getEnd(), FastMath.PI)); + final LimitAngle limit = (LimitAngle) node.getCut().getHyperplane(); + if (checkPoint(limit.getLocation()) == Location.BOUNDARY) { + limits.add(limit.getLocation().getAlpha()); } } diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Chord.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java similarity index 58% rename from src/main/java/org/apache/commons/math3/geometry/spherical/oned/Chord.java rename to src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java index dd92ab14a..6c0e7a138 100644 --- a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/Chord.java +++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/LimitAngle.java @@ -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.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. - *

An hyperplane on the 1-sphere is a chord that splits - * the circle in two parts.

+ *

An hyperplane on the 1-sphere is an angle with an orientation.

*

Instances of this class are guaranteed to be immutable.

* @version $Id$ * @since 3.3 */ -public class Chord implements Hyperplane { +public class LimitAngle implements Hyperplane { - /** Start angle of the chord. */ - private final double start; + /** Angle location. */ + private S1Point location; - /** End angle of the chord. */ - private final double end; + /** Orientation. */ + private boolean direct; - /** Cosine of the half aperture. */ - private final double cos; - - /** Middle point of the chord. */ - private final S1Point middle; - - /** Tolerance below which close sub-arcs are merged together. */ + /** Tolerance below which angles are considered identical. */ private final double tolerance; /** Simple constructor. - * @param start start angle of the chord - * @param end end angle of the chord - * @param tolerance tolerance below which close sub-arcs are merged together + * @param location location of the hyperplane + * @param direct if true, the plus side of the hyperplane is towards + * 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) { - this.start = start; - this.end = end; - this.middle = new S1Point(0.5 * (start + end)); - this.cos = FastMath.cos(0.5 * (end - start)); + public LimitAngle(final S1Point location, final boolean direct, final double tolerance) { + this.location = location; + this.direct = direct; this.tolerance = tolerance; } @@ -63,22 +53,31 @@ public class Chord implements Hyperplane { * the instance.

* @return the instance itself */ - public Chord copySelf() { + public LimitAngle copySelf() { return this; } /** {@inheritDoc} */ public double getOffset(final Point 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 a chord with reversed orientation with respect to the + *

Get a limit angle with reversed orientation with respect to the * instance. A new object is built, the instance is untouched.

- * @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() { - return new Chord(end, MathUtils.normalizeAngle(start, end + FastMath.PI), tolerance); + public LimitAngle getReverse() { + return new LimitAngle(location, !direct, tolerance); } /** Build a region covering the whole hyperplane. @@ -92,8 +91,8 @@ public class Chord implements Hyperplane { * not be used otherwise.

* @return a dummy sub hyperplane */ - public SubChord wholeHyperplane() { - return new SubChord(this); + public SubLimitAngle wholeHyperplane() { + return new SubLimitAngle(this, null); } /** Build a region covering the whole space. @@ -106,25 +105,18 @@ public class Chord implements Hyperplane { /** {@inheritDoc} */ public boolean sameOrientationAs(final Hyperplane other) { - return middle.getVector().dotProduct(((Chord) other).middle.getVector()) >= 0.0; + return !(direct ^ ((LimitAngle) other).direct); } - /** Get the start angle of the chord. - * @return start angle of the chord. + /** Get the hyperplane location on the circle. + * @return the hyperplane location */ - public double getStart() { - return start; + public S1Point getLocation() { + return location; } - /** Get the end angle of the chord. - * @return end angle of the chord. - */ - 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 + /** Get the tolerance below which angles are considered identical. + * @return tolerance below which angles are considered identical */ public double getTolerance() { return tolerance; diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java index e7184d93d..72e612c69 100644 --- a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java +++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/S1Point.java @@ -49,7 +49,8 @@ public class S1Point implements Point { * @see #getAlpha() */ 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. diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubChord.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubChord.java deleted file mode 100644 index 9bede9d48..000000000 --- a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubChord.java +++ /dev/null @@ -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}. - *

Instances of this class are guaranteed to be immutable.

- * @version $Id$ - * @since 3.3 - */ -public class SubChord implements SubHyperplane { - - /** Underlying hyperplane. */ - private final Chord chord; - - /** Boundary angles. */ - private final List limits; - - /** Simple constructor. - * @param chord underlying hyperplane - */ - public SubChord(final Chord chord) { - this.chord = chord; - this.limits = new ArrayList(); - 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 limits) { - this.chord = chord; - this.limits = new ArrayList(limits); - } - - /** Get the sub-arcs. - * @return a newly created list with sub-arcs - */ - public List getSubArcs() { - final List subArcs = new ArrayList(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 ith sub-arc. - * @param i index of the desired arc (counting from 0) - * @return start of the ith sub-arc - */ - public double getStart(final int i) { - return limits.get(2 * i); - } - - /** Get the end of the ith sub-arc. - * @param i index of the desired arc (counting from 0) - * @return end of the ith 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 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 split(final Hyperplane hyperplane) { - - final List minus = new ArrayList(limits.size()); - final List plus = new ArrayList(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(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 other) { - - final List otherLimits = ((SubChord) other).limits; - - final List merged; - if (other.isEmpty()) { - merged = limits; - } else if (isEmpty()) { - merged = otherLimits; - } else { - - merged = new ArrayList(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); - - } - -} diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java new file mode 100644 index 000000000..bf3cd43ad --- /dev/null +++ b/src/main/java/org/apache/commons/math3/geometry/spherical/oned/SubLimitAngle.java @@ -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}. + *

Instances of this class are guaranteed to be immutable.

+ * @version $Id$ + * @since 3.3 + */ +public class SubLimitAngle extends AbstractSubHyperplane { + + /** Simple constructor. + * @param hyperplane underlying hyperplane + * @param remainingRegion remaining region of the hyperplane + */ + public SubLimitAngle(final Hyperplane hyperplane, + final Region remainingRegion) { + super(hyperplane, remainingRegion); + } + + /** {@inheritDoc} */ + @Override + public double getSize() { + return 0; + } + + /** {@inheritDoc} */ + public boolean isEmpty() { + return false; + } + + /** {@inheritDoc} */ + @Override + protected AbstractSubHyperplane buildNew(final Hyperplane hyperplane, + final Region remainingRegion) { + return new SubLimitAngle(hyperplane, remainingRegion); + } + + /** {@inheritDoc} */ + @Override + public Side side(final Hyperplane 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 split(final Hyperplane hyperplane) { + final double global = hyperplane.getOffset(((LimitAngle) getHyperplane()).getLocation()); + return (global < -1.0e-10) ? + new SplitSubHyperplane(null, this) : + new SplitSubHyperplane(this, null); + } + +} diff --git a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java index 184479aea..5d2355d7a 100644 --- a/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java +++ b/src/main/java/org/apache/commons/math3/geometry/spherical/twod/Circle.java @@ -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.SubHyperplane; 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.Chord; import org.apache.commons.math3.geometry.spherical.oned.S1Point; import org.apache.commons.math3.geometry.spherical.oned.Sphere1D; import org.apache.commons.math3.util.FastMath; @@ -223,15 +223,15 @@ public class Circle implements Hyperplane, Embedding { final Circle thisCircle = (Circle) getHyperplane(); final Circle otherCircle = (Circle) hyperplane; - final Chord chord = thisCircle.getChord(otherCircle); - - 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); + final Arc arc = thisCircle.getInsideArc(otherCircle); + return ((ArcsSet) getRemainingRegion()).side(arc); } @@ -73,17 +66,8 @@ public class SubCircle extends AbstractSubHyperplane { final Circle thisCircle = (Circle) getHyperplane(); 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(null, this) : - new SplitSubHyperplane(this, null); - } - - // the circles do intersect final SubHyperplane subMinus = chord.wholeHyperplane(); final SubHyperplane subPlus = chord.getReverse().wholeHyperplane(); final BSPTree splitTree = getRemainingRegion().getTree(false).split(subMinus); diff --git a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcTest.java b/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcTest.java index 76f7d1ded..b835ea8ad 100644 --- a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcTest.java +++ b/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcTest.java @@ -16,8 +16,10 @@ */ 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.util.FastMath; +import org.apache.commons.math3.util.MathUtils; import org.apache.commons.math3.util.Precision; import org.junit.Assert; import org.junit.Test; @@ -26,49 +28,52 @@ public class ArcTest { @Test 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(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(5.7, 1.0e-10)); - Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(1.2, 1.0e-10)); - Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(8.5, 1.0e-10)); - Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(8.7, 1.0e-10)); - Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(3.0, 1.0e-10)); + Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(2.3)); + Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(5.7)); + Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(1.2)); + Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(8.5)); + Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(8.7)); + Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(3.0)); Assert.assertEquals(2.3, arc.getInf(), 1.0e-10); Assert.assertEquals(5.7, arc.getSup(), 1.0e-10); Assert.assertEquals(4.0, arc.getBarycenter(), 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 public void testTolerance() { - Arc arc = new Arc(2.3, 5.7); - Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(1.2, 1.0)); - Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(1.2, 1.2)); - Assert.assertEquals(Region.Location.OUTSIDE, arc.checkPoint(6.5, 0.7)); - Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(6.5, 0.9)); - Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(3.0, 0.6)); - Assert.assertEquals(Region.Location.BOUNDARY, arc.checkPoint(3.0, 0.8)); + Assert.assertEquals(Region.Location.OUTSIDE, new Arc(2.3, 5.7, 1.0).checkPoint(1.2)); + Assert.assertEquals(Region.Location.BOUNDARY, new Arc(2.3, 5.7, 1.2).checkPoint(1.2)); + Assert.assertEquals(Region.Location.OUTSIDE, new Arc(2.3, 5.7, 0.7).checkPoint(6.5)); + Assert.assertEquals(Region.Location.BOUNDARY, new Arc(2.3, 5.7, 0.9).checkPoint(6.5)); + Assert.assertEquals(Region.Location.INSIDE, new Arc(2.3, 5.7, 0.6).checkPoint(3.0)); + Assert.assertEquals(Region.Location.BOUNDARY, new Arc(2.3, 5.7, 0.8).checkPoint(3.0)); } @Test 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 - Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(9.0, 1.0e-10)); - Assert.assertEquals(9.0, arc.getInf(), 1.0e-10); - Assert.assertEquals(9.0 + 2.0 * FastMath.PI, arc.getSup(), 1.0e-10); + Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(9.0)); + Assert.assertEquals(.0, arc.getInf(), 1.0e-10); + Assert.assertEquals(MathUtils.TWO_PI, arc.getSup(), 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) { - Assert.assertEquals(Region.Location.INSIDE, - arc.checkPoint(alpha, 1.0e-10)); + Assert.assertEquals(Region.Location.INSIDE, arc.checkPoint(alpha)); } } @Test 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(1.0, arc.getBarycenter(), Precision.EPSILON); } diff --git a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java b/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java index f729853b1..415505819 100644 --- a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java +++ b/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ArcsSetTest.java @@ -16,11 +16,14 @@ */ package org.apache.commons.math3.geometry.spherical.oned; +import java.util.ArrayList; 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.Location; 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.MathUtils; import org.apache.commons.math3.util.Precision; @@ -33,7 +36,7 @@ public class ArcsSetTest { public void testArc() { ArcsSet set = new ArcsSet(2.3, 5.7, 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(5.7))); 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(2.3, set.asList().get(0).getInf(), 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(3.4, set.getSize(), 1.0e-10); + Assert.assertEquals(2.3, set.getSmallestLimit().getLocation().getAlpha(), 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 - public void testFullCircle() { - ArcsSet set = new ArcsSet(9.0, 9.0, 1.0e-10); + public void testWrapAround2PiArc() { + 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))); for (double alpha = -20.0; alpha <= 20.0; alpha += 0.1) { 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); } + @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().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> boundary = new ArrayList>(); + 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 public void testDifference() { @@ -139,9 +226,11 @@ public class ArcsSetTest { } List 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(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.0, 5.5, 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(4.0))); Assert.assertEquals(Region.Location.OUTSIDE, set.checkPoint(new S1Point(6.0))); @@ -179,7 +267,6 @@ public class ArcsSetTest { public void testSinglePoint() { 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(1.0, ((S1Point) set.getBarycenter()).getAlpha(), Precision.EPSILON); } } diff --git a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ChordTest.java b/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ChordTest.java deleted file mode 100644 index 828172424..000000000 --- a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/ChordTest.java +++ /dev/null @@ -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()); - } - } - -} diff --git a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/SubChordTest.java b/src/test/java/org/apache/commons/math3/geometry/spherical/oned/SubChordTest.java deleted file mode 100644 index e92bcc8cf..000000000 --- a/src/test/java/org/apache/commons/math3/geometry/spherical/oned/SubChordTest.java +++ /dev/null @@ -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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); - } - -}