1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.twod.path;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.Random;
24 import java.util.function.Consumer;
25
26 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
27 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
28 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
29 import org.apache.commons.geometry.euclidean.twod.LineConvexSubset;
30 import org.apache.commons.geometry.euclidean.twod.Lines;
31 import org.apache.commons.geometry.euclidean.twod.Ray;
32 import org.apache.commons.geometry.euclidean.twod.ReverseRay;
33 import org.apache.commons.geometry.euclidean.twod.Vector2D;
34 import org.apache.commons.geometry.euclidean.twod.path.InteriorAngleLinePathConnector.Maximize;
35 import org.apache.commons.geometry.euclidean.twod.path.InteriorAngleLinePathConnector.Minimize;
36 import org.apache.commons.numbers.angle.PlaneAngleRadians;
37 import org.junit.Assert;
38 import org.junit.Test;
39
40 public class InteriorAngleLinePathConnectorTest {
41
42 private static final double TEST_EPS = 1e-10;
43
44 private static final DoublePrecisionContext TEST_PRECISION =
45 new EpsilonDoublePrecisionContext(TEST_EPS);
46
47 @Test
48 public void testConnectAll_noSegments() {
49 runWithMaxAndMin(connector -> {
50
51 final List<LineConvexSubset> segments = new ArrayList<>();
52
53
54 final List<LinePath> paths = connector.connectAll(segments);
55
56
57 Assert.assertEquals(0, paths.size());
58 });
59 }
60
61 @Test
62 public void testConnectAll_singleFiniteSegment() {
63 runWithMaxAndMin(connector -> {
64
65 final List<LineConvexSubset> segments = Collections.singletonList(
66 Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION)
67 );
68
69
70 final List<LinePath> paths = connector.connectAll(segments);
71
72
73 Assert.assertEquals(1, paths.size());
74
75 assertFinitePath(paths.get(0), Vector2D.ZERO, Vector2D.Unit.PLUS_X);
76 });
77 }
78
79 @Test
80 public void testConnectAll_dualConnectedSegments() {
81 runWithMaxAndMin(connector -> {
82
83 final List<LineConvexSubset> segments = Arrays.asList(
84 Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION),
85 Lines.segmentFromPoints(Vector2D.Unit.PLUS_X, Vector2D.ZERO, TEST_PRECISION)
86 );
87
88
89 final List<LinePath> paths = connector.connectAll(segments);
90
91
92 Assert.assertEquals(1, paths.size());
93
94 Assert.assertTrue(paths.get(0).isClosed());
95 assertFinitePath(paths.get(0), Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.ZERO);
96 });
97 }
98
99 @Test
100 public void testConnectAll_singleFiniteSegmentLoop() {
101 runWithMaxAndMin(connector -> {
102
103 final List<LineConvexSubset> segments = shuffle(createSquare(Vector2D.ZERO, 1, 1));
104
105
106 final List<LinePath> paths = connector.connectAll(segments);
107
108
109 Assert.assertEquals(1, paths.size());
110
111 assertFinitePath(paths.get(0),
112 Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
113 Vector2D.of(0, 1), Vector2D.ZERO);
114 });
115 }
116
117 @Test
118 public void testConnectAll_disjointPaths() {
119 runWithMaxAndMin(connector -> {
120
121 final List<LineConvexSubset> segments = new ArrayList<>(createSquare(Vector2D.ZERO, 1, 1));
122
123 final Vector2D pt = Vector2D.of(0, 2);
124 final ReverseRay a = Lines.fromPointAndAngle(pt, 0.0, TEST_PRECISION).reverseRayTo(pt);
125 final Ray b = Lines.fromPointAndAngle(pt, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION).rayFrom(pt);
126
127 segments.add(a);
128 segments.add(b);
129
130 shuffle(segments);
131
132
133 final List<LinePath> paths = connector.connectAll(segments);
134
135
136 Assert.assertEquals(2, paths.size());
137
138 assertFinitePath(paths.get(0),
139 Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
140 Vector2D.of(0, 1), Vector2D.ZERO);
141
142 assertInfinitePath(paths.get(1), a, b, pt);
143 });
144 }
145
146 @Test
147 public void testConnectAll_squaresJoinedAtVertex_maximize() {
148
149 final Maximize connector = new Maximize();
150
151 final List<LineConvexSubset> segments = new ArrayList<>();
152 segments.addAll(createSquare(Vector2D.ZERO, 1, 1));
153 segments.addAll(createSquare(Vector2D.of(1, 1), 1, 1));
154
155 shuffle(segments);
156
157
158 final List<LinePath> paths = connector.connectAll(segments);
159
160
161 Assert.assertEquals(1, paths.size());
162
163 assertFinitePath(paths.get(0),
164 Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
165 Vector2D.of(2, 1), Vector2D.of(2, 2),
166 Vector2D.of(1, 2), Vector2D.of(1, 1),
167 Vector2D.of(0, 1), Vector2D.ZERO);
168 }
169
170 @Test
171 public void testConnectAll_multipleSegmentsAtVertex_maximize() {
172
173 final Maximize connector = new Maximize();
174
175 final List<LineConvexSubset> segments = new ArrayList<>();
176 segments.add(Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));
177
178 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
179 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));
180
181
182 final List<LinePath> paths = connector.connectAll(segments);
183
184
185 Assert.assertEquals(2, paths.size());
186
187 assertFinitePath(paths.get(0),
188 Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(2, 4));
189
190 assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(1, 3));
191 }
192
193 @Test
194 public void testConnectAll_squaresJoinedAtVertex_minimize() {
195
196 final Minimize connector = new Minimize();
197
198 final List<LineConvexSubset> segments = new ArrayList<>();
199 segments.addAll(createSquare(Vector2D.ZERO, 1, 1));
200 segments.addAll(createSquare(Vector2D.of(1, 1), 1, 1));
201
202 shuffle(segments);
203
204
205 final List<LinePath> paths = connector.connectAll(segments);
206
207
208 Assert.assertEquals(2, paths.size());
209
210 assertFinitePath(paths.get(0),
211 Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
212 Vector2D.of(0, 1), Vector2D.ZERO);
213
214 assertFinitePath(paths.get(1),
215 Vector2D.of(1, 1), Vector2D.of(2, 1), Vector2D.of(2, 2),
216 Vector2D.of(1, 2), Vector2D.of(1, 1));
217 }
218
219 @Test
220 public void testConnectAll_multipleSegmentsAtVertex_minimize() {
221
222 final Minimize connector = new Minimize();
223
224 final List<LineConvexSubset> segments = new ArrayList<>();
225 segments.add(Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));
226
227 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
228 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));
229
230
231 final List<LinePath> paths = connector.connectAll(segments);
232
233
234 Assert.assertEquals(2, paths.size());
235
236 assertFinitePath(paths.get(0),
237 Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(1, 3));
238
239 assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(2, 4));
240 }
241
242 @Test
243 public void testConnectMaximized() {
244
245 final List<LineConvexSubset> segments = new ArrayList<>();
246 segments.add(Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));
247
248 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
249 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));
250
251
252 final List<LinePath> paths = InteriorAngleLinePathConnector.connectMaximized(segments);
253
254
255 Assert.assertEquals(2, paths.size());
256
257 assertFinitePath(paths.get(0),
258 Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(2, 4));
259
260 assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(1, 3));
261 }
262
263 @Test
264 public void testConnectMinimized() {
265
266 final List<LineConvexSubset> segments = new ArrayList<>();
267 segments.add(Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));
268
269 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
270 segments.add(Lines.segmentFromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));
271
272
273 final List<LinePath> paths = InteriorAngleLinePathConnector.connectMinimized(segments);
274
275
276 Assert.assertEquals(2, paths.size());
277
278 assertFinitePath(paths.get(0),
279 Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(1, 3));
280
281 assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(2, 4));
282 }
283
284
285
286
287
288 private static void runWithMaxAndMin(final Consumer<InteriorAngleLinePathConnector> body) {
289 body.accept(new Maximize());
290 body.accept(new Minimize());
291 }
292
293 private static List<LineConvexSubset> createSquare(final Vector2D lowerLeft, final double width, final double height) {
294 final Vector2D lowerRight = Vector2D.of(lowerLeft.getX() + width, lowerLeft.getY());
295 final Vector2D upperRight = Vector2D.of(lowerLeft.getX() + width, lowerLeft.getY() + height);
296 final Vector2D upperLeft = Vector2D.of(lowerLeft.getX(), lowerLeft.getY() + height);
297
298 return Arrays.asList(
299 Lines.segmentFromPoints(lowerLeft, lowerRight, TEST_PRECISION),
300 Lines.segmentFromPoints(lowerRight, upperRight, TEST_PRECISION),
301 Lines.segmentFromPoints(upperRight, upperLeft, TEST_PRECISION),
302 Lines.segmentFromPoints(upperLeft, lowerLeft, TEST_PRECISION)
303 );
304 }
305
306 private static List<LineConvexSubset> shuffle(final List<LineConvexSubset> segments) {
307 return shuffle(segments, 1);
308 }
309
310 private static List<LineConvexSubset> shuffle(final List<LineConvexSubset> segments, final int seed) {
311 Collections.shuffle(segments, new Random(seed));
312
313 return segments;
314 }
315
316 private static void assertInfinitePath(final LinePath path, final LineConvexSubset start, final LineConvexSubset end, final Vector2D... vertices) {
317 Assert.assertTrue(path.isInfinite());
318 Assert.assertFalse(path.isFinite());
319
320 Assert.assertEquals(start, path.getStart());
321 Assert.assertEquals(end, path.getEnd());
322
323 assertPathVertices(path, vertices);
324 }
325
326 private static void assertFinitePath(final LinePath path, final Vector2D... vertices) {
327 Assert.assertFalse(path.isInfinite());
328 Assert.assertTrue(path.isFinite());
329
330 assertPathVertices(path, vertices);
331 }
332
333 private static void assertPathVertices(final LinePath path, final Vector2D... vertices) {
334 final List<Vector2D> expectedVertices = Arrays.asList(vertices);
335 final List<Vector2D> actualVertices = path.getVertexSequence();
336
337 final String msg = "Expected path vertices to equal " + expectedVertices + " but was " + actualVertices;
338 Assert.assertEquals(msg, expectedVertices.size(), actualVertices.size());
339
340 for (int i = 0; i < expectedVertices.size(); ++i) {
341 EuclideanTestUtils.assertCoordinatesEqual(expectedVertices.get(i), actualVertices.get(i), TEST_EPS);
342 }
343 }
344 }