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.Arrays;
20  import java.util.Collections;
21  import java.util.List;
22  
23  import org.apache.commons.geometry.euclidean.threed.Vector3D;
24  import org.apache.commons.geometry.spherical.SphericalTestUtils;
25  import org.apache.commons.geometry.spherical.twod.AbstractGreatArcConnector.ConnectableGreatArc;
26  import org.apache.commons.numbers.angle.Angle;
27  import org.apache.commons.numbers.core.Precision;
28  import org.junit.jupiter.api.Assertions;
29  import org.junit.jupiter.api.Test;
30  
31  class AbstractGreatArcPathConnectorTest {
32  
33      private static final double TEST_EPS = 1e-10;
34  
35      private static final Precision.DoubleEquivalence TEST_PRECISION =
36              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
37  
38      private static final GreatCircle XY_PLANE = GreatCircles.fromPoleAndU(
39              Vector3D.Unit.PLUS_Z, Vector3D.Unit.PLUS_X, TEST_PRECISION);
40  
41      private static final GreatCircle XZ_PLANE = GreatCircles.fromPoleAndU(
42              Vector3D.Unit.PLUS_Y, Vector3D.Unit.PLUS_X, TEST_PRECISION);
43  
44      private final TestConnector connector = new TestConnector();
45  
46      @Test
47      void testConnectAll_emptyCollection() {
48          // act
49          final List<GreatArcPath> paths = connector.connectAll(Collections.emptyList());
50  
51          // assert
52          Assertions.assertEquals(0, paths.size());
53      }
54  
55      @Test
56      void testConnectAll_singleFullArc() {
57          // act
58          connector.add(Collections.singletonList(XY_PLANE.span()));
59          final List<GreatArcPath> paths = connector.connectAll();
60  
61          // assert
62          Assertions.assertEquals(1, paths.size());
63  
64          final GreatArcPath a = paths.get(0);
65          Assertions.assertEquals(1, a.getArcs().size());
66          Assertions.assertSame(XY_PLANE, a.getStartArc().getCircle());
67      }
68  
69      @Test
70      void testConnectAll_twoFullArcs() {
71          // act
72          connector.add(XZ_PLANE.span());
73          final List<GreatArcPath> paths = connector.connectAll(Collections.singletonList(XY_PLANE.span()));
74  
75          // assert
76          Assertions.assertEquals(2, paths.size());
77  
78          final GreatArcPath a = paths.get(0);
79          Assertions.assertEquals(1, a.getArcs().size());
80          Assertions.assertSame(XY_PLANE, a.getStartArc().getCircle());
81  
82          final GreatArcPath b = paths.get(1);
83          Assertions.assertEquals(1, b.getArcs().size());
84          Assertions.assertSame(XZ_PLANE, b.getStartArc().getCircle());
85      }
86  
87      @Test
88      void testConnectAll_singleLune() {
89          // arrange
90          final GreatCircle upperBound = GreatCircles.fromPoleAndU(
91                  Vector3D.of(0, 1, -1), Vector3D.Unit.PLUS_X, TEST_PRECISION);
92  
93          connector.add(XY_PLANE.arc(0, Math.PI));
94          connector.add(upperBound.arc(Math.PI, 0));
95  
96          // act
97          final List<GreatArcPath> paths = connector.connectAll();
98  
99          // assert
100         Assertions.assertEquals(1, paths.size());
101 
102         final GreatArcPath a = paths.get(0);
103         Assertions.assertEquals(2, a.getArcs().size());
104         Assertions.assertSame(XY_PLANE, a.getStartArc().getCircle());
105         Assertions.assertSame(upperBound, a.getEndArc().getCircle());
106     }
107 
108     @Test
109     void testConnectAll_singleLune_pathsNotOrientedCorrectly() {
110         // arrange
111         final GreatCircle upperBound = GreatCircles.fromPoleAndU(
112                 Vector3D.of(0, 1, -1), Vector3D.Unit.PLUS_X, TEST_PRECISION);
113 
114         connector.add(XY_PLANE.arc(0, Math.PI));
115         connector.add(upperBound.arc(0, Math.PI));
116 
117         // act
118         final List<GreatArcPath> paths = connector.connectAll();
119 
120         // assert
121         Assertions.assertEquals(2, paths.size());
122 
123         final GreatArcPath a = paths.get(0);
124         Assertions.assertEquals(1, a.getArcs().size());
125         Assertions.assertSame(XY_PLANE, a.getStartArc().getCircle());
126 
127         final GreatArcPath b = paths.get(1);
128         Assertions.assertEquals(1, b.getArcs().size());
129         Assertions.assertSame(upperBound, b.getStartArc().getCircle());
130     }
131 
132     @Test
133     void testConnectAll_largeTriangle() {
134         // arrange
135         final Point2S p1 = Point2S.PLUS_I;
136         final Point2S p2 = Point2S.PLUS_J;
137         final Point2S p3 = Point2S.PLUS_K;
138 
139         // act
140         final List<GreatArcPath> paths = connector.connectAll(Arrays.asList(
141                     GreatCircles.arcFromPoints(p1, p2, TEST_PRECISION),
142                     GreatCircles.arcFromPoints(p2, p3, TEST_PRECISION),
143                     GreatCircles.arcFromPoints(p3, p1, TEST_PRECISION)
144                 ));
145 
146         // assert
147         Assertions.assertEquals(1, paths.size());
148 
149         final GreatArcPath a = paths.get(0);
150         Assertions.assertEquals(3, a.getArcs().size());
151 
152         assertPathPoints(a, p3, p1, p2, p3);
153     }
154 
155     @Test
156     void testConnectAll_smallTriangleWithDisconnectedLuneAndArc() {
157         // arrange
158         final Point2S p1 = Point2S.of(0, 0);
159         final Point2S p2 = Point2S.of(0, 0.1 * Math.PI);
160         final Point2S p3 = Point2S.of(0.1, 0.1 * Math.PI);
161 
162         final GreatArc luneEdge1 = GreatCircles.fromPoints(
163                     Point2S.PLUS_J,
164                     Point2S.MINUS_I,
165                     TEST_PRECISION)
166                 .arc(0, Math.PI);
167         final GreatArc luneEdge2 = GreatCircles.fromPoints(
168                     Point2S.MINUS_J,
169                     Point2S.of(Angle.PI_OVER_TWO, 0.4 * Math.PI),
170                     TEST_PRECISION)
171                 .arc(0, Math.PI);
172 
173         final GreatArc separateArc = GreatCircles.arcFromPoints(
174                 Point2S.of(-Angle.PI_OVER_TWO, 0.7 * Math.PI),
175                 Point2S.of(-Angle.PI_OVER_TWO, 0.8 * Math.PI),
176                 TEST_PRECISION);
177 
178         // act
179         final List<GreatArcPath> paths = connector.connectAll(Arrays.asList(
180                     luneEdge1,
181                     GreatCircles.arcFromPoints(p2, p3, TEST_PRECISION),
182                     separateArc,
183                     GreatCircles.arcFromPoints(p1, p2, TEST_PRECISION),
184                     GreatCircles.arcFromPoints(p3, p1, TEST_PRECISION),
185                     luneEdge2
186                 ));
187 
188         // assert
189         Assertions.assertEquals(3, paths.size());
190 
191         final GreatArcPath triangle = paths.get(0);
192         Assertions.assertEquals(3, triangle.getArcs().size());
193         assertPathPoints(triangle, p1, p2, p3, p1);
194 
195         final GreatArcPath lune = paths.get(1);
196         Assertions.assertEquals(2, lune.getArcs().size());
197         Assertions.assertSame(luneEdge1, lune.getStartArc());
198         Assertions.assertSame(luneEdge2, lune.getEndArc());
199 
200         final GreatArcPath separate = paths.get(2);
201         Assertions.assertEquals(1, separate.getArcs().size());
202         Assertions.assertSame(separateArc, separate.getStartArc());
203     }
204 
205     @Test
206     void testConnectAll_choosesBestPointLikeConnection() {
207         // arrange
208         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-1);
209 
210         final Point2S p1 = Point2S.PLUS_I;
211         final Point2S p2 = Point2S.of(1, Angle.PI_OVER_TWO);
212         final Point2S p3 = Point2S.of(1.001, 0.491 * Math.PI);
213         final Point2S p4 = Point2S.of(1.001, 0.502 * Math.PI);
214 
215         connector.add(GreatCircles.arcFromPoints(p2, p3, TEST_PRECISION));
216         connector.add(GreatCircles.arcFromPoints(p2, p4, TEST_PRECISION));
217         connector.add(GreatCircles.arcFromPoints(p1, p2, precision));
218 
219         // act
220         final List<GreatArcPath> paths = connector.connectAll();
221 
222         // assert
223         Assertions.assertEquals(2, paths.size());
224 
225         final GreatArcPath a = paths.get(0);
226         Assertions.assertEquals(2, a.getArcs().size());
227         assertPathPoints(a, p1, p2, p4);
228 
229         final GreatArcPath b = paths.get(1);
230         Assertions.assertEquals(1, b.getArcs().size());
231         assertPathPoints(b, p2, p3);
232     }
233 
234     @Test
235     void testConnect() {
236         // arrange
237         final GreatArc arcA = GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
238         final GreatArc arcB = GreatCircles.arcFromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION);
239         final GreatArc arcC = GreatCircles.arcFromPoints(Point2S.PLUS_J, Point2S.PLUS_K, TEST_PRECISION);
240 
241         // act
242         connector.connect(Arrays.asList(
243                     arcB,
244                     arcA
245                 ));
246 
247         connector.connect(Collections.singletonList(arcC));
248 
249         final List<GreatArcPath> paths = connector.connectAll();
250 
251         // assert
252         Assertions.assertEquals(2, paths.size());
253 
254         final GreatArcPath a = paths.get(0);
255         Assertions.assertEquals(2, a.getArcs().size());
256         assertPathPoints(a, Point2S.PLUS_I, Point2S.PLUS_J, Point2S.MINUS_I);
257 
258         final GreatArcPath b = paths.get(1);
259         Assertions.assertEquals(1, b.getArcs().size());
260         assertPathPoints(b, Point2S.PLUS_J, Point2S.PLUS_K);
261     }
262 
263     @Test
264     void testConnectableSegment_hashCode() {
265         // arrange
266         final GreatArc arcA = GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
267         final GreatArc arcB = GreatCircles.arcFromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION);
268 
269         final ConnectableGreatArc a = new ConnectableGreatArc(arcA);
270 
271         // act
272         final int hash = a.hashCode();
273 
274         // assert
275         Assertions.assertEquals(hash, a.hashCode());
276 
277         Assertions.assertNotEquals(hash, new ConnectableGreatArc(arcB).hashCode());
278         Assertions.assertNotEquals(hash, new ConnectableGreatArc(Point2S.MINUS_I).hashCode());
279 
280         Assertions.assertEquals(hash, new ConnectableGreatArc(arcA).hashCode());
281     }
282 
283     @Test
284     void testConnectableSegment_equals() {
285         // arrange
286         final GreatArc arcA = GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
287         final GreatArc arcB = GreatCircles.arcFromPoints(Point2S.PLUS_J, Point2S.MINUS_I, TEST_PRECISION);
288 
289         final ConnectableGreatArc a = new ConnectableGreatArc(arcA);
290 
291         // act/assert
292         Assertions.assertEquals(a, a);
293 
294         Assertions.assertFalse(a.equals(null));
295         Assertions.assertFalse(a.equals(new Object()));
296 
297         Assertions.assertNotEquals(a, new ConnectableGreatArc(arcB));
298         Assertions.assertNotEquals(a, new ConnectableGreatArc(Point2S.MINUS_I));
299 
300         Assertions.assertEquals(a, new ConnectableGreatArc(arcA));
301     }
302 
303     @Test
304     void testConnectorCanBeReused() {
305         // arrange
306         final GreatArc a = GreatCircles.arcFromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION);
307         final GreatArc b = GreatCircles.arcFromPoints(Point2S.MINUS_I, Point2S.MINUS_J, TEST_PRECISION);
308 
309         // act
310         final List<GreatArcPath> path1 = connector.connectAll(Collections.singletonList(a));
311         final List<GreatArcPath> path2 = connector.connectAll(Collections.singletonList(b));
312 
313         // assert
314         Assertions.assertEquals(1, path1.size());
315         assertPathPoints(path1.get(0), Point2S.PLUS_I, Point2S.PLUS_J);
316 
317         Assertions.assertEquals(1, path2.size());
318         assertPathPoints(path2.get(0), Point2S.MINUS_I, Point2S.MINUS_J);
319     }
320 
321     private static void assertPathPoints(final GreatArcPath path, final Point2S... points) {
322         final List<Point2S> expectedPoints = Arrays.asList(points);
323         final List<Point2S> actualPoints = path.getVertices();
324 
325         final String msg = "Expected path points to equal " + expectedPoints + " but was " + actualPoints;
326         Assertions.assertEquals(expectedPoints.size(), actualPoints.size(), msg);
327 
328         for (int i = 0; i < expectedPoints.size(); ++i) {
329             SphericalTestUtils.assertPointsEq(expectedPoints.get(i), actualPoints.get(i), TEST_EPS);
330         }
331     }
332 
333     private static class TestConnector extends AbstractGreatArcConnector {
334 
335         @Override
336         protected ConnectableGreatArc selectConnection(final ConnectableGreatArc incoming,
337                                                        final List<ConnectableGreatArc> outgoing) {
338 
339             // just choose the first element
340             return outgoing.get(0);
341         }
342     }
343 }