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 }