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.euclidean;
    18
    19  import java.util.List;
    20
    21  import org.apache.commons.geometry.core.Region;
    22  import org.apache.commons.geometry.core.RegionLocation;
    23  import org.apache.commons.geometry.core.partitioning.HyperplaneSubset;
    24  import org.apache.commons.geometry.euclidean.oned.Vector1D;
    25  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    26  import org.apache.commons.geometry.euclidean.twod.Vector2D;
    27  import org.apache.commons.numbers.core.Precision;
    28  import org.junit.jupiter.api.Assertions;
    29
    30  /**
    31   * Class containing various Euclidean-related test utilities.
    32   */
    33  public final class EuclideanTestUtils {
    34
    35      // no instantiation
    36      private EuclideanTestUtils() {}
    37
    38      /** Callback interface for {@link #permute(double, double, double, PermuteCallback2D)}. */
    39      @FunctionalInterface
    40      public interface PermuteCallback2D {
    41          void accept(double x, double y);
    42      }
    43
    44      /** Callback interface for {@link #permute(double, double, double, PermuteCallback3D)} */
    45      @FunctionalInterface
    46      public interface PermuteCallback3D {
    47          void accept(double x, double y, double z);
    48      }
    49
    50      /** Iterate through all {@code (x, y)} permutations for the given range of numbers and
    51       * call {@code callback} for each.
    52       *
    53       * @param min the minimum number in the range
    54       * @param max the maximum number in the range
    55       * @param step the step (increment) value for the range
    56       * @param callback callback to invoke for each permutation.
    57       */
    58      public static void permute(final double min, final double max, final double step, final PermuteCallback2D callback) {
    59          permuteInternal(min, max, step, false, callback);
    60      }
    61
    62      /** Same as {@link #permute(double, double, double, PermuteCallback2D)} but skips the {@code (0, 0))}
    63       * permutation.
    64       *
    65       * @param min the minimum number in the range
    66       * @param max the maximum number in the range
    67       * @param step the step (increment) value for the range
    68       * @param callback callback to invoke for each permutation.
    69       */
    70      public static void permuteSkipZero(final double min, final double max, final double step, final PermuteCallback2D callback) {
    71          permuteInternal(min, max, step, true, callback);
    72      }
    73
    74      /** Internal permutation method. Iterates through all {@code (x, y)} permutations for the given range
    75       * of numbers and calls {@code callback} for each.
    76       *
    77       * @param min the minimum number in the range
    78       * @param max the maximum number in the range
    79       * @param step the step (increment) value for the range
    80       * @param skipZero if true, the {@code (0, 0)} permutation will be skipped
    81       * @param callback callback to invoke for each permutation.
    82       */
    83      private static void permuteInternal(final double min, final double max, final double step, final boolean skipZero, final PermuteCallback2D callback) {
    84          for (double x = min; x <= max; x += step) {
    85              for (double y = min; y <= max; y += step) {
    86                  if (!skipZero || (x != 0.0 || y != 0.0)) {
    87                      callback.accept(x, y);
    88                  }
    89              }
    90          }
    91      }
    92
    93      /** Iterate through all {@code (x, y, z)} permutations for the given range of numbers and
    94       * call {@code callback} for each.
    95       *
    96       * @param min the minimum number in the range
    97       * @param max the maximum number in the range
    98       * @param step the step (increment) value for the range
    99       * @param callback callback to invoke for each permutation.
    100      */
    101     public static void permute(final double min, final double max, final double step, final PermuteCallback3D callback) {
    102         permuteInternal(min, max, step, false, callback);
    103     }
    104
    105     /** Same as {@link #permute(double, double, double, PermuteCallback3D)} but skips the {@code (0, 0, 0)}
    106      * permutation.
    107      *
    108      * @param min the minimum number in the range
    109      * @param max the maximum number in the range
    110      * @param step the step (increment) value for the range
    111      * @param callback callback to invoke for each permutation.
    112      */
    113     public static void permuteSkipZero(final double min, final double max, final double step, final PermuteCallback3D callback) {
    114         permuteInternal(min, max, step, true, callback);
    115     }
    116
    117     /** Internal permutation method. Iterates through all {@code (x, y)} permutations for the given range
    118      * of numbers and calls {@code callback} for each.
    119      *
    120      * @param min the minimum number in the range
    121      * @param max the maximum number in the range
    122      * @param step the step (increment) value for the range
    123      * @param skipZero if true, the {@code (0, 0, 0)} permutation will be skipped
    124      * @param callback callback to invoke for each permutation.
    125      */
    126     private static void permuteInternal(final double min, final double max, final double step, final boolean skipZero, final PermuteCallback3D callback) {
    127         for (double x = min; x <= max; x += step) {
    128             for (double y = min; y <= max; y += step) {
    129                 for (double z = min; z <= max; z += step) {
    130                     if (!skipZero || (x != 0.0 || y != 0.0 || z != 0.0)) {
    131                         callback.accept(x, y, z);
    132                     }
    133                 }
    134             }
    135         }
    136     }
    137
    138     /**
    139      * Asserts that corresponding values in the given vectors are equal, using the
    140      * specified tolerance value.
    141      *
    142      * @param expected
    143      * @param actual
    144      * @param tolerance
    145      */
    146     public static void assertCoordinatesEqual(final Vector1D expected, final Vector1D actual, final double tolerance) {
    147         final String msg = "Expected coordinates to equal " + expected + " but was " + actual + ";";
    148         Assertions.assertEquals(expected.getX(), actual.getX(), tolerance, msg);
    149     }
    150
    151     /**
    152      * Asserts that corresponding values in the given vectors are equal, using the
    153      * specified tolerance value.
    154      *
    155      * @param expected
    156      * @param actual
    157      * @param tolerance
    158      */
    159     public static void assertCoordinatesEqual(final Vector2D expected, final Vector2D actual, final double tolerance) {
    160         final String msg = "Expected coordinates to equal " + expected + " but was " + actual + ";";
    161         Assertions.assertEquals(expected.getX(), actual.getX(), tolerance, msg);
    162         Assertions.assertEquals(expected.getY(), actual.getY(), tolerance, msg);
    163     }
    164
    165     /**
    166      * Asserts that corresponding values in the given vectors are equal, using the
    167      * specified tolerance value.
    168      *
    169      * @param expected
    170      * @param actual
    171      * @param tolerance
    172      */
    173     public static void assertCoordinatesEqual(final Vector3D expected, final Vector3D actual, final double tolerance) {
    174         final String msg = "Expected coordinates to equal " + expected + " but was " + actual + ";";
    175         Assertions.assertEquals(expected.getX(), actual.getX(), tolerance, msg);
    176         Assertions.assertEquals(expected.getY(), actual.getY(), tolerance, msg);
    177         Assertions.assertEquals(expected.getZ(), actual.getZ(), tolerance, msg);
    178     }
    179
    180     /**
    181      * Asserts that the given value is positive infinity.
    182      *
    183      * @param value
    184      */
    185     public static void assertPositiveInfinity(final double value) {
    186         final String msg = "Expected value to be positive infinity but was " + value;
    187         Assertions.assertTrue(Double.isInfinite(value), msg);
    188         Assertions.assertTrue(value > 0, msg);
    189     }
    190
    191     /**
    192      * Assert that the given lists represent equivalent vertex loops. The loops must contain the same sequence
    193      * of vertices but do not need to start at the same point.
    194      * @param <V> Vector implementation type
    195      * @param expected
    196      * @param actual
    197      * @param precision
    198      */
    199     public static <V extends EuclideanVector<V>> void assertVertexLoopSequence(final List<V> expected, final List<V> actual,
    200                                                                                final Precision.DoubleEquivalence precision) {
    201         Assertions.assertEquals(expected.size(), actual.size(), "Vertex sequences have different sizes");
    202
    203         if (!expected.isEmpty()) {
    204
    205             int offset = -1;
    206             final V start = expected.get(0);
    207             for (int i = 0; i < actual.size(); ++i) {
    208                 if (actual.get(i).eq(start, precision)) {
    209                     offset = i;
    210                     break;
    211                 }
    212             }
    213
    214             if (offset < 0) {
    215                 Assertions.fail("Vertex loops do not share any points: expected " + expected + " but was " + actual);
    216             }
    217
    218             V expectedVertex;
    219             V actualVertex;
    220             for (int i = 0; i < expected.size(); ++i) {
    221                 expectedVertex = expected.get(i);
    222                 actualVertex = actual.get((i + offset) % actual.size());
    223
    224                 if (!expectedVertex.eq(actualVertex, precision)) {
    225                     Assertions.fail("Unexpected vertex at index " + i + ": expected " + expectedVertex +
    226                             " but was " + actualVertex);
    227                 }
    228             }
    229         }
    230     }
    231
    232     /**
    233      * Asserts that the given value is negative infinity..
    234      *
    235      * @param value
    236      */
    237     public static void assertNegativeInfinity(final double value) {
    238         final String msg = "Expected value to be negative infinity but was " + value;
    239         Assertions.assertTrue(Double.isInfinite(value), msg);
    240         Assertions.assertTrue(value < 0, msg);
    241     }
    242
    243     /** Assert that all of the given points lie within the specified location relative to
    244      * {@code region}.
    245      * @param region
    246      * @param loc
    247      * @param pts
    248      */
    249     public static void assertRegionLocation(final Region<Vector1D> region, final RegionLocation loc, final Vector1D... pts) {
    250         for (final Vector1D pt : pts) {
    251             Assertions.assertEquals(loc, region.classify(pt), "Unexpected region location for point " + pt);
    252         }
    253     }
    254
    255     /** Assert that all of the given points lie within the specified location relative to
    256      * {@code region}.
    257      * @param region
    258      * @param loc
    259      * @param pts
    260      */
    261     public static void assertRegionLocation(final Region<Vector2D> region, final RegionLocation loc, final Vector2D... pts) {
    262         for (final Vector2D pt : pts) {
    263             Assertions.assertEquals(loc, region.classify(pt), "Unexpected region location for point " + pt);
    264         }
    265     }
    266
    267     /** Assert that all of the given points lie within the specified location relative to
    268      * {@code region}.
    269      * @param region
    270      * @param loc
    271      * @param pts
    272      */
    273     public static void assertRegionLocation(final Region<Vector3D> region, final RegionLocation loc, final Vector3D... pts) {
    274         for (final Vector3D pt : pts) {
    275             Assertions.assertEquals(loc, region.classify(pt), "Unexpected region location for point " + pt);
    276         }
    277     }
    278
    279     /** Assert that all of the given points lie within the specified location relative to {@code sub}.
    280      * @param sub
    281      * @param loc
    282      * @param pts
    283      */
    284     public static void assertRegionLocation(final HyperplaneSubset<Vector1D> sub, final RegionLocation loc, final Vector1D... pts) {
    285         for (final Vector1D pt : pts) {
    286             Assertions.assertEquals(loc, sub.classify(pt), "Unexpected region location for point " + pt);
    287         }
    288     }
    289
    290     /** Assert that all of the given points lie within the specified location relative to {@code sub}.
    291      * @param sub
    292      * @param loc
    293      * @param pts
    294      */
    295     public static void assertRegionLocation(final HyperplaneSubset<Vector2D> sub, final RegionLocation loc, final Vector2D... pts) {
    296         for (final Vector2D pt : pts) {
    297             Assertions.assertEquals(loc, sub.classify(pt), "Unexpected region location for point " + pt);
    298         }
    299     }
    300
    301     /** Assert that all of the given points lie within the specified location relative to {@code sub}.
    302      * @param sub
    303      * @param loc
    304      * @param pts
    305      */
    306     public static void assertRegionLocation(final HyperplaneSubset<Vector3D> sub, final RegionLocation loc, final Vector3D... pts) {
    307         for (final Vector3D pt : pts) {
    308             Assertions.assertEquals(loc, sub.classify(pt), "Unexpected region location for point " + pt);
    309         }
    310     }
    311 }