View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.geometry.spherical.twod;
    18
    19  import java.util.List;
    20
    21  import org.apache.commons.geometry.core.GeometryTestUtils;
    22  import org.apache.commons.geometry.core.RegionLocation;
    23  import org.apache.commons.geometry.core.partitioning.Split;
    24  import org.apache.commons.geometry.core.partitioning.SplitLocation;
    25  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    26  import org.apache.commons.geometry.spherical.SphericalTestUtils;
    27  import org.apache.commons.geometry.spherical.oned.AngularInterval;
    28  import org.apache.commons.numbers.angle.Angle;
    29  import org.apache.commons.numbers.core.Precision;
    30  import org.junit.jupiter.api.Assertions;
    31  import org.junit.jupiter.api.Test;
    32
    33  class GreatArcTest {
    34
    35      private static final double TEST_EPS = 1e-10;
    36
    37      private static final Precision.DoubleEquivalence TEST_PRECISION =
    38              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    39
    40      @Test
    41      void testFromInterval_full() {
    42          // act
    43          final GreatArc arc = GreatCircles.arcFromInterval(
    44                  GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
    45                  AngularInterval.full());
    46
    47          // assert
    48          Assertions.assertTrue(arc.isFull());
    49          Assertions.assertFalse(arc.isEmpty());
    50          Assertions.assertTrue(arc.isFinite());
    51          Assertions.assertFalse(arc.isInfinite());
    52
    53          Assertions.assertNull(arc.getStartPoint());
    54          Assertions.assertNull(arc.getEndPoint());
    55
    56          Assertions.assertEquals(Angle.TWO_PI, arc.getSize(), TEST_EPS);
    57          Assertions.assertNull(arc.getCentroid());
    58
    59          for (double az = 0; az < Angle.TWO_PI; az += 0.1) {
    60              checkClassify(arc, RegionLocation.INSIDE, Point2S.of(az, Angle.PI_OVER_TWO));
    61          }
    62
    63          checkClassify(arc, RegionLocation.OUTSIDE,
    64                  Point2S.PLUS_K, Point2S.of(0, Angle.PI_OVER_TWO + 0.1),
    65                  Point2S.MINUS_K, Point2S.of(0, Angle.PI_OVER_TWO - 0.1));
    66      }
    67
    68      @Test
    69      void testFromInterval_partial() {
    70          // arrange
    71          final GreatArc arc = GreatCircles.arcFromInterval(
    72                  GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION),
    73                  AngularInterval.Convex.of(Angle.PI_OVER_TWO, 1.5 * Math.PI, TEST_PRECISION));
    74
    75          // assert
    76          Assertions.assertFalse(arc.isFull());
    77          Assertions.assertFalse(arc.isEmpty());
    78          Assertions.assertTrue(arc.isFinite());
    79          Assertions.assertFalse(arc.isInfinite());
    80
    81          checkArc(arc, Point2S.PLUS_K, Point2S.MINUS_K);
    82      }
    83
    84      @Test
    85      void testFromPoints() {
    86          // arrange
    87          final Point2S start = Point2S.PLUS_I;
    88          final Point2S end = Point2S.MINUS_K;
    89
    90          // act
    91          final GreatArc arc = GreatCircles.arcFromPoints(start, end, TEST_PRECISION);
    92
    93          // assert
    94          Assertions.assertFalse(arc.isFull());
    95          Assertions.assertFalse(arc.isEmpty());
    96          Assertions.assertTrue(arc.isFinite());
    97          Assertions.assertFalse(arc.isInfinite());
    98
    99          SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_Y, arc.getCircle().getPole(), TEST_EPS);
    100
    101         checkArc(arc, start, end);
    102
    103         checkClassify(arc, RegionLocation.INSIDE, Point2S.of(0, 0.75 * Math.PI));
    104         checkClassify(arc, RegionLocation.BOUNDARY, start, end);
    105         checkClassify(arc, RegionLocation.OUTSIDE,
    106                 Point2S.of(0, 0.25 * Math.PI), Point2S.of(Math.PI, 0.75 * Math.PI),
    107                 Point2S.of(Math.PI, 0.25 * Math.PI));
    108     }
    109
    110     @Test
    111     void testFromPoints_almostPi() {
    112         // arrange
    113         final Point2S start = Point2S.PLUS_J;
    114         final Point2S end = Point2S.of(1.5 * Math.PI, Angle.PI_OVER_TWO - 1e-5);
    115
    116         // act
    117         final GreatArc arc = GreatCircles.arcFromPoints(start, end, TEST_PRECISION);
    118
    119         // assert
    120         Assertions.assertFalse(arc.isFull());
    121         Assertions.assertFalse(arc.isEmpty());
    122         Assertions.assertTrue(arc.isFinite());
    123         Assertions.assertFalse(arc.isInfinite());
    124
    125         SphericalTestUtils.assertVectorsEqual(Vector3D.Unit.PLUS_X, arc.getCircle().getPole(), TEST_EPS);
    126
    127         checkArc(arc, start, end);
    128
    129         checkClassify(arc, RegionLocation.INSIDE, Point2S.PLUS_K);
    130         checkClassify(arc, RegionLocation.BOUNDARY, start, end);
    131         checkClassify(arc, RegionLocation.OUTSIDE, Point2S.MINUS_K);
    132     }
    133
    134     @Test
    135     void testFromPoints_usesShortestPath() {
    136         // act/assert
    137         SphericalTestUtils.assertVectorsEqual(
    138                 Vector3D.Unit.MINUS_Y,
    139                 GreatCircles.arcFromPoints(
    140                         Point2S.PLUS_I,
    141                         Point2S.of(Math.PI, Angle.PI_OVER_TWO - 1e-5),
    142                         TEST_PRECISION).getCircle().getPole(), TEST_EPS);
    143
    144         SphericalTestUtils.assertVectorsEqual(
    145                 Vector3D.Unit.PLUS_Y,
    146                 GreatCircles.arcFromPoints(
    147                         Point2S.PLUS_I,
    148                         Point2S.of(Math.PI, Angle.PI_OVER_TWO + 1e-5),
    149                         TEST_PRECISION).getCircle().getPole(), TEST_EPS);
    150     }
    151
    152     @Test
    153     void testFromPoints_invalidPoints() {
    154         // act/assert
    155         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.of(1e-12, Angle.PI_OVER_TWO), TEST_PRECISION));
    156         Assertions.assertThrows(IllegalArgumentException.class, () -> GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.MINUS_I, TEST_PRECISION));
    157     }
    158
    159     @Test
    160     void testToConvex() {
    161         // arrange
    162         final GreatArc arc = GreatCircles.arcFromInterval(
    163                 GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION),
    164                 AngularInterval.Convex.of(0.0, Math.PI, TEST_PRECISION));
    165
    166         // act
    167         final List<GreatArc> result = arc.toConvex();
    168
    169         // assert
    170         Assertions.assertEquals(1, result.size());
    171         Assertions.assertSame(arc, result.get(0));
    172     }
    173
    174     @Test
    175     void testGetMidPoint() {
    176         // arrange
    177         final GreatArc arc = GreatCircles.arcFromInterval(
    178                 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
    179                 AngularInterval.Convex.of(0.8 * Math.PI, 0.9 * Math.PI, TEST_PRECISION));
    180
    181         // act/assert
    182         SphericalTestUtils.assertPointsEqual(Point2S.of(0.85 * Math.PI, 0.5 * Math.PI), arc.getMidPoint(), TEST_EPS);
    183     }
    184
    185     @Test
    186     void testGetMidPoint_full() {
    187         // arrange
    188         final GreatArc arc = GreatCircles.arcFromInterval(
    189                 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
    190                 AngularInterval.full());
    191
    192         // act/assert
    193         Assertions.assertNull(arc.getMidPoint());
    194     }
    195
    196     @Test
    197     void testReverse_full() {
    198         // arrange
    199         final GreatArc arc = GreatCircles.arcFromInterval(
    200                 GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION),
    201                 AngularInterval.full());
    202
    203         // act
    204         final GreatArc result = arc.reverse();
    205
    206         // assert
    207         checkGreatCircle(result.getCircle(), Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_Y);
    208
    209         Assertions.assertTrue(result.isFull());
    210     }
    211
    212     @Test
    213     void testReverse() {
    214         // arrange
    215         final GreatArc arc = GreatCircles.arcFromInterval(
    216                 GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION),
    217                 AngularInterval.Convex.of(Angle.PI_OVER_TWO, Math.PI, TEST_PRECISION));
    218
    219         // act
    220         final GreatArc result = arc.reverse();
    221
    222         // assert
    223         checkGreatCircle(result.getCircle(), Vector3D.Unit.MINUS_Z, Vector3D.Unit.PLUS_Y);
    224
    225         checkArc(result, Point2S.MINUS_J, Point2S.MINUS_I);
    226     }
    227
    228     @Test
    229     void testTransform() {
    230         // arrange
    231         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_K, Point2S.MINUS_I, TEST_PRECISION)
    232                 .arc(Math.PI, -Angle.PI_OVER_TWO);
    233
    234         final Transform2S t = Transform2S.createRotation(Point2S.PLUS_I, Angle.PI_OVER_TWO)
    235                 .reflect(Point2S.of(-0.25 * Math.PI,  Angle.PI_OVER_TWO));
    236
    237         // act
    238         final GreatArc result = arc.transform(t);
    239
    240         // assert
    241         checkArc(result, Point2S.PLUS_I, Point2S.PLUS_J);
    242     }
    243
    244     @Test
    245     void testSplit_full() {
    246         // arrange
    247         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
    248         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.of(-1, 0, 1), TEST_PRECISION);
    249
    250         // act
    251         final Split<GreatArc> split = arc.split(splitter);
    252
    253         // assert
    254         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
    255
    256         final GreatArc minus = split.getMinus();
    257         Assertions.assertSame(arc.getCircle(), minus.getCircle());
    258         checkArc(minus, Point2S.PLUS_J, Point2S.MINUS_J);
    259         checkClassify(minus, RegionLocation.OUTSIDE, Point2S.PLUS_I);
    260         checkClassify(minus, RegionLocation.INSIDE, Point2S.MINUS_I);
    261
    262         final GreatArc plus = split.getPlus();
    263         Assertions.assertSame(arc.getCircle(), plus.getCircle());
    264         checkArc(plus, Point2S.MINUS_J, Point2S.PLUS_J);
    265         checkClassify(plus, RegionLocation.INSIDE, Point2S.PLUS_I);
    266         checkClassify(plus, RegionLocation.OUTSIDE, Point2S.MINUS_I);
    267     }
    268
    269     @Test
    270     void testSplit_both() {
    271         // arrange
    272         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION)
    273                 .arc(Angle.PI_OVER_TWO, Math.PI);
    274         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.of(0, 1, 1), TEST_PRECISION);
    275
    276         // act
    277         final Split<GreatArc> split = arc.split(splitter);
    278
    279         // assert
    280         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
    281
    282         final GreatArc minus = split.getMinus();
    283         Assertions.assertSame(arc.getCircle(), minus.getCircle());
    284         checkArc(minus, Point2S.of(0, 0), Point2S.of(1.5 * Math.PI, 0.25 * Math.PI));
    285
    286         final GreatArc plus = split.getPlus();
    287         Assertions.assertSame(arc.getCircle(), plus.getCircle());
    288         checkArc(plus, Point2S.of(1.5 * Math.PI, 0.25 * Math.PI), Point2S.MINUS_J);
    289     }
    290
    291     @Test
    292     void testSplit_minus() {
    293         // arrange
    294         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION)
    295                 .arc(Angle.PI_OVER_TWO, Math.PI);
    296         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION);
    297
    298
    299         // act
    300         final Split<GreatArc> split = arc.split(splitter);
    301
    302         // assert
    303         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
    304
    305         final GreatArc minus = split.getMinus();
    306         Assertions.assertSame(arc, minus);
    307
    308         final GreatArc plus = split.getPlus();
    309         Assertions.assertNull(plus);
    310     }
    311
    312     @Test
    313     void testSplit_plus() {
    314         // arrange
    315         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION)
    316                 .arc(Angle.PI_OVER_TWO, Math.PI);
    317         final GreatCircle splitter = GreatCircles.fromPole(Vector3D.Unit.from(-1, 0, -1), TEST_PRECISION);
    318
    319         // act
    320         final Split<GreatArc> split = arc.split(splitter);
    321
    322         // assert
    323         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
    324
    325         final GreatArc minus = split.getMinus();
    326         Assertions.assertNull(minus);
    327
    328         final GreatArc plus = split.getPlus();
    329         Assertions.assertSame(arc, plus);
    330     }
    331
    332     @Test
    333     void testSplit_parallelAndAntiparallel() {
    334         // arrange
    335         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
    336
    337         // act/assert
    338         Assertions.assertEquals(SplitLocation.NEITHER,
    339                 arc.split(GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION)).getLocation());
    340         Assertions.assertEquals(SplitLocation.NEITHER,
    341                 arc.split(GreatCircles.fromPole(Vector3D.Unit.MINUS_Z, TEST_PRECISION)).getLocation());
    342     }
    343
    344     @Test
    345     void testToString_full() {
    346         // arrange
    347         final GreatArc arc = GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span();
    348
    349         // act
    350         final String str = arc.toString();
    351
    352         // assert
    353         GeometryTestUtils.assertContains("GreatArc[", str);
    354         GeometryTestUtils.assertContains("full= true", str);
    355         GeometryTestUtils.assertContains("circle= GreatCircle[", str);
    356     }
    357
    358     @Test
    359     void testToString_notFull() {
    360         // arrange
    361         final GreatArc arc = GreatCircles.arcFromInterval(
    362                 GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION),
    363                 AngularInterval.Convex.of(1, 2, TEST_PRECISION));
    364
    365         // act
    366         final String str = arc.toString();
    367
    368         // assert
    369         GeometryTestUtils.assertContains("GreatArc[", str);
    370         GeometryTestUtils.assertContains("start= (", str);
    371         GeometryTestUtils.assertContains("end= (", str);
    372     }
    373
    374     private static void checkClassify(final GreatArc arc, final RegionLocation loc, final Point2S... pts) {
    375         for (final Point2S pt : pts) {
    376             Assertions.assertEquals(loc, arc.classify(pt), "Unexpected location for point " + pt);
    377         }
    378     }
    379
    380     private static void checkArc(final GreatArc arc, final Point2S start, final Point2S end) {
    381         SphericalTestUtils.assertPointsEq(start, arc.getStartPoint(), TEST_EPS);
    382         SphericalTestUtils.assertPointsEq(end, arc.getEndPoint(), TEST_EPS);
    383
    384         checkClassify(arc, RegionLocation.BOUNDARY, start, end);
    385
    386         final Point2S mid = arc.getCircle().toSpace(arc.getInterval().getMidPoint());
    387
    388         checkClassify(arc, RegionLocation.INSIDE, mid);
    389         checkClassify(arc, RegionLocation.OUTSIDE, mid.antipodal());
    390
    391         Assertions.assertEquals(start.distance(end), arc.getSize(), TEST_EPS);
    392         SphericalTestUtils.assertPointsEq(mid, arc.getCentroid(), TEST_EPS);
    393     }
    394
    395     private static void checkGreatCircle(final GreatCircle circle, final Vector3D pole, final Vector3D x) {
    396         SphericalTestUtils.assertVectorsEqual(pole, circle.getPole(), TEST_EPS);
    397         SphericalTestUtils.assertVectorsEqual(x, circle.getU(), TEST_EPS);
    398         SphericalTestUtils.assertVectorsEqual(pole.cross(x), circle.getV(), TEST_EPS);
    399     }
    400 }