1 package org.djutils.draw.point;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertFalse;
5 import static org.junit.Assert.assertNotEquals;
6 import static org.junit.Assert.assertNotNull;
7 import static org.junit.Assert.assertNull;
8 import static org.junit.Assert.assertTrue;
9 import static org.junit.Assert.fail;
10
11 import java.awt.geom.Point2D;
12 import java.util.List;
13
14 import org.djutils.draw.DrawException;
15 import org.djutils.draw.DrawRuntimeException;
16 import org.djutils.draw.bounds.Bounds2d;
17 import org.djutils.draw.line.PolyLine2d;
18 import org.djutils.exceptions.Try;
19 import org.junit.Test;
20
21
22
23
24
25
26
27
28
29
30 public class Point2dTest
31 {
32
33
34
35 @SuppressWarnings("unlikely-arg-type")
36 @Test
37 public void testPoint2dConstruction()
38 {
39 Point2d p = new Point2d(10.0, -20.0);
40 assertNotNull(p);
41 assertEquals(10.0, p.x, 1E-6);
42 assertEquals(-20.0, p.y, 1E-6);
43
44 assertEquals("size method returns 1", 1, p.size());
45
46 try
47 {
48 new Point2d(Double.NaN, 0);
49 fail("NaN should have thrown an IllegalArgumentException");
50 }
51 catch (IllegalArgumentException iae)
52 {
53
54 }
55
56 try
57 {
58 new Point2d(0, Double.NaN);
59 fail("NaN should have thrown an IllegalArgumentException");
60 }
61 catch (IllegalArgumentException iae)
62 {
63
64 }
65
66 double[] p2Arr = new double[] {5.0, 6.0};
67 p = new Point2d(p2Arr);
68 assertEquals(5.0, p.x, 0);
69 assertEquals(6.0, p.y, 0);
70 Point2D.Double p2DD = new Point2D.Double(-0.1, -0.2);
71 p = new Point2d(p2DD);
72 assertEquals(-0.1, p.x, 1E-6);
73 assertEquals(-0.2, p.y, 1E-6);
74 assertEquals(p2DD, p.toPoint2D());
75
76 Try.testFail(new Try.Execution()
77 {
78 @Override
79 public void execute() throws Throwable
80 {
81 new Point2d((Point2D.Double) null);
82 }
83 }, "Should throw NPE", NullPointerException.class);
84
85 Try.testFail(new Try.Execution()
86 {
87 @Override
88 public void execute() throws Throwable
89 {
90 new Point2d(new double[] {});
91 }
92 }, "Should throw IAE", IllegalArgumentException.class);
93
94 Try.testFail(new Try.Execution()
95 {
96 @Override
97 public void execute() throws Throwable
98 {
99 new Point2d(new double[] {1.0});
100 }
101 }, "Should throw IAE", IllegalArgumentException.class);
102
103 Try.testFail(new Try.Execution()
104 {
105 @Override
106 public void execute() throws Throwable
107 {
108 new Point2d(new double[] {1.0, 2.0, 3.0});
109 }
110 }, "Should throw IAE", IllegalArgumentException.class);
111
112 Try.testFail(new Try.Execution()
113 {
114 @Override
115 public void execute() throws Throwable
116 {
117 new Point2d(new Point2D.Double(Double.NaN, 2));
118 }
119 }, "Should throw IAE", IllegalArgumentException.class);
120
121 Try.testFail(new Try.Execution()
122 {
123 @Override
124 public void execute() throws Throwable
125 {
126 new Point2d(new Point2D.Double(1, Double.NaN));
127 }
128 }, "Should throw IAE", IllegalArgumentException.class);
129
130
131 assertTrue(p.equals(p));
132 assertEquals(p.hashCode(), p.hashCode());
133 Point3d p3d = p.translate(1.0, 2.0, 3.0);
134 assertFalse(p.equals(p3d));
135 assertFalse(p.equals(null));
136 assertNotEquals(p3d.hashCode(), p.hashCode());
137 assertEquals(p, p.translate(0.0, 0.0));
138 assertNotEquals(p, p.translate(1.0, 0.0));
139 assertNotEquals(p, p.translate(0.0, 1.0));
140 assertEquals("x", p.x + 1, p3d.x, 0.00001);
141 assertEquals("y", p.y + 2, p3d.y, 0.00001);
142 assertEquals("z", 3, p3d.z, 0);
143
144
145 p = new Point2d(10.0, 20.0);
146 assertEquals("(10.000000,20.000000)", p.toString());
147 assertEquals("(10.0,20.0)", p.toString(1));
148 assertEquals("(10,20)", p.toString(0));
149 assertEquals("(10,20)", p.toString(-1));
150
151
152 assertTrue(p.epsilonEquals(p, 0.1));
153 assertTrue(p.epsilonEquals(p, 0.001));
154 assertTrue(p.epsilonEquals(p, 0.0));
155 Point2d p3 = p.translate(0.001, 0.0);
156 assertTrue(p.epsilonEquals(p3, 0.09));
157 assertTrue(p3.epsilonEquals(p, 0.09));
158 assertFalse(p.epsilonEquals(p3, 0.0009));
159 assertFalse(p3.epsilonEquals(p, 0.0009));
160 p3 = p.translate(0.0, 0.001);
161 assertTrue(p.epsilonEquals(p3, 0.09));
162 assertTrue(p3.epsilonEquals(p, 0.09));
163 assertFalse(p.epsilonEquals(p3, 0.0009));
164 assertFalse(p3.epsilonEquals(p, 0.0009));
165 }
166
167
168
169
170 @Test
171 public void testPoint2dOperators()
172 {
173 Point2d p = new Point2d(-0.1, -0.2);
174 assertEquals(0.1, p.abs().x, 1E-6);
175 assertEquals(0.2, p.abs().y, 1E-6);
176 p = p.neg();
177 assertEquals(0.1, p.x, 1E-6);
178 assertEquals(0.2, p.y, 1E-6);
179 p = p.scale(1.0);
180 assertEquals(0.1, p.x, 1E-6);
181 assertEquals(0.2, p.y, 1E-6);
182 p = p.scale(10.0);
183 assertEquals(1.0, p.x, 1E-6);
184 assertEquals(2.0, p.y, 1E-6);
185 p = p.translate(5.0, -1.0);
186 assertEquals(6.0, p.x, 1E-6);
187 assertEquals(1.0, p.y, 1E-6);
188 Point3d p3d = p.translate(1.0, 1.0, 1.0);
189 assertEquals(7.0, p3d.x, 1E-6);
190 assertEquals(2.0, p3d.y, 1E-6);
191 assertEquals(1.0, p3d.z, 1E-6);
192
193 try
194 {
195 p.translate(Double.NaN, 2.0);
196 fail("NaN translation should have thrown an IllegalArgumentException");
197 }
198 catch (IllegalArgumentException iae)
199 {
200
201 }
202
203 try
204 {
205 p.translate(1.0, Double.NaN);
206 fail("NaN translation should have thrown an IllegalArgumentException");
207 }
208 catch (IllegalArgumentException iae)
209 {
210
211 }
212
213
214 Point2d p1 = new Point2d(1.0, 1.0);
215 Point2d p2 = new Point2d(5.0, 5.0);
216 assertEquals(p1, p1.interpolate(p2, 0.0));
217 assertEquals(p2, p2.interpolate(p1, 0.0));
218 assertEquals(p1, p1.interpolate(p1, 0.0));
219 assertEquals(new Point2d(3.0, 3.0), p1.interpolate(p2, 0.5));
220
221
222 assertEquals(Math.sqrt(32.0), p1.distance(p2), 0.001);
223 assertEquals(32.0, p1.distanceSquared(p2), 0.001);
224
225
226
227
228
229
230
231
232
233
234 Point2d pn = p2.normalize();
235 assertEquals(1.0 / Math.sqrt(2.0), pn.x, 0.001);
236 assertEquals(1.0 / Math.sqrt(2.0), pn.y, 0.001);
237
238 Try.testFail(new Try.Execution()
239 {
240 @Override
241 public void execute() throws Throwable
242 {
243 new Point2d(0.0, 0.0).normalize();
244 }
245 }, "Should throw DRtE", DrawRuntimeException.class);
246
247 Bounds2d bounds = p1.getBounds();
248 assertEquals("Bounds min x", p1.x, bounds.getMinX(), 0);
249 assertEquals("Bounds min y", p1.y, bounds.getMinY(), 0);
250 assertEquals("Bounds max x", p1.x, bounds.getMaxX(), 0);
251 assertEquals("Bounds max y", p1.y, bounds.getMaxY(), 0);
252 }
253
254
255
256
257 @Test
258 public void testPoint2dOperatorsNPE()
259 {
260 final Point2d p1 = new Point2d(1.0, 1.0);
261
262 try
263 {
264 p1.translate(Double.NaN, 2.0);
265 fail("NaN translation should have thrown an IllegalArgumentException");
266 }
267 catch (IllegalArgumentException iae)
268 {
269
270 }
271
272 try
273 {
274 p1.translate(1.0, Double.NaN);
275 fail("NaN translation should have thrown an IllegalArgumentException");
276 }
277 catch (IllegalArgumentException iae)
278 {
279
280 }
281
282 try
283 {
284 p1.translate(Double.NaN, 2.0, 3.0);
285 fail("NaN translation should have thrown an IllegalArgumentException");
286 }
287 catch (IllegalArgumentException iae)
288 {
289
290 }
291
292 try
293 {
294 p1.translate(1.0, Double.NaN, 3.0);
295 fail("NaN translation should have thrown an IllegalArgumentException");
296 }
297 catch (IllegalArgumentException iae)
298 {
299
300 }
301
302 try
303 {
304 p1.translate(1.0, 2.0, Double.NaN);
305 fail("NaN translation should have thrown an IllegalArgumentException");
306 }
307 catch (IllegalArgumentException iae)
308 {
309
310 }
311
312 Try.testFail(new Try.Execution()
313 {
314 @Override
315 public void execute() throws Throwable
316 {
317 p1.interpolate(null, 0.5);
318 }
319 }, "Should throw NPE", NullPointerException.class);
320
321 Try.testFail(new Try.Execution()
322 {
323 @Override
324 public void execute() throws Throwable
325 {
326 p1.distance(null);
327 }
328 }, "Should throw NPE", NullPointerException.class);
329
330 Try.testFail(new Try.Execution()
331 {
332 @Override
333 public void execute() throws Throwable
334 {
335 p1.distanceSquared(null);
336 }
337 }, "Should throw NPE", NullPointerException.class);
338 }
339
340
341
342
343 @Test
344 public void testIntersectionOfLineSegments()
345 {
346 assertNull("horizontal line intersection with itself returns null",
347 Point2d.intersectionOfLineSegments(new Point2d(1, 2), new Point2d(4, 2), new Point2d(1, 2), new Point2d(4, 2)));
348 assertNull("vertical line intersection with itself returns null", Point2d.intersectionOfLineSegments(new Point2d(1, 2),
349 new Point2d(1, 10), new Point2d(1, 2), new Point2d(1, 10)));
350 assertEquals("Intersection is at (2,2)", new Point2d(2, 2), Point2d.intersectionOfLineSegments(new Point2d(1, 1),
351 new Point2d(6, 6), new Point2d(4, 2), new Point2d(-2, 2)));
352
353 assertNull("line two passes before start of line one", Point2d.intersectionOfLineSegments(new Point2d(1, 1),
354 new Point2d(5, 5), new Point2d(0, -3), new Point2d(10, 0)));
355 assertNull("line two passes before after end of line one", Point2d.intersectionOfLineSegments(new Point2d(1, 1),
356 new Point2d(5, 5), new Point2d(0, 20), new Point2d(100, 30)));
357 assertNull("line one passes before start of line two", Point2d.intersectionOfLineSegments(new Point2d(1, 1),
358 new Point2d(5, 5), new Point2d(5, 3), new Point2d(10, 2)));
359 assertNull("line one passes after end of line two", Point2d.intersectionOfLineSegments(new Point2d(1, 1),
360 new Point2d(5, 5), new Point2d(-10, 3), new Point2d(0, 2)));
361 }
362
363
364
365
366 @Test
367 public void testIntersectionOfLines()
368 {
369 assertNull("horizontal line intersection with itself returns null",
370 Point2d.intersectionOfLines(new Point2d(1, 2), new Point2d(4, 2), new Point2d(1, 2), new Point2d(4, 2)));
371 assertNull("vertical line intersection with itself returns null", Point2d.intersectionOfLineSegments(new Point2d(1, 2),
372 new Point2d(1, 10), new Point2d(1, 2), new Point2d(1, 10)));
373 assertEquals("Intersection is at (2,2)", new Point2d(2, 2),
374 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(6, 6), new Point2d(4, 2), new Point2d(-2, 2)));
375
376 assertEquals("line two passes before start of line one", new Point2d(-1.5, -1.5),
377 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(0, -3), new Point2d(10, -13)));
378 assertEquals("line two passes before after end of line one", new Point2d(20, 20),
379 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(0, 20), new Point2d(100, 20)));
380 assertEquals("line one passes before start of line two", new Point2d(4, 4),
381 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(7, 1), new Point2d(10, -2)));
382 assertEquals("line one passes after end of line two", new Point2d(-3.5, -3.5),
383 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(-10, 3), new Point2d(0, -7)));
384 }
385
386
387
388
389
390 @Test
391 public void testClosestPointOnSegment() throws DrawException
392 {
393 Point2d p1 = new Point2d(-2, 3);
394 for (Point2d p2 : new Point2d[] {new Point2d(7, 4), new Point2d(-3, 6) ,
395 new Point2d(-2, -5) , new Point2d(8, 3) })
396 {
397 PolyLine2d line = new PolyLine2d(p1, p2);
398 for (double x = -10; x <= 10; x += 0.5)
399 {
400 for (double y = -10; y <= 10; y += 0.5)
401 {
402 Point2d p = new Point2d(x, y);
403 Point2d result = p.closestPointOnSegment(p1, p2);
404
405 double fraction = 0.5;
406 double step = 0.25;
407 Point2d approximation = line.getLocationFraction(fraction);
408 double distance = approximation.distance(p);
409
410 for (int iteration = 0; iteration < 10; iteration++)
411 {
412
413 double upFraction = fraction + step;
414 Point2d upApproximation = line.getLocationFraction(upFraction);
415 double upDistance = upApproximation.distance(p);
416 if (upDistance < distance)
417 {
418 distance = upDistance;
419 fraction = upFraction;
420 approximation = upApproximation;
421 }
422 else
423 {
424
425 double downFraction = fraction - step;
426 Point2d downApproximation = line.getLocationFraction(downFraction);
427 double downDistance = downApproximation.distance(p);
428 if (downDistance < distance)
429 {
430 distance = downDistance;
431 fraction = downFraction;
432 approximation = downApproximation;
433 }
434 }
435 step /= 2;
436 }
437 assertEquals("distance should be less than one thousandth of line length", 0,
438 approximation.distance(result), line.getLength() / 1000);
439 assertEquals("zero length line segment should always return start point", p1,
440 p.closestPointOnSegment(p1, p1));
441 }
442 }
443 }
444 }
445
446
447
448
449 @Test
450 public void circleIntersectionTest()
451 {
452 for (int x1 = -5; x1 <= 5; x1++)
453 {
454 for (int y1 = -5; y1 <= 5; y1++)
455 {
456 Point2d p1 = new Point2d(x1, y1);
457 for (int r1 = 0; r1 < 5; r1++)
458 {
459 for (int x2 = -5; x2 <= 5; x2++)
460 {
461 for (int y2 = -5; y2 <= 5; y2++)
462 {
463 Point2d p2 = new Point2d(x2, y2);
464 double distance = p1.distance(p2);
465 for (int r2 = 0; r2 < 5; r2++)
466 {
467 if (x1 == x2 && y1 == y2 && r1 == r2)
468 {
469 try
470 {
471 Point2d.circleIntersections(p1, r1, p2, r2);
472 fail("Identical circles should have thrown a DrawRuntimeException");
473 }
474 catch (DrawRuntimeException dre)
475 {
476
477 }
478 }
479 else
480 {
481 List<Point2d> result = Point2d.circleIntersections(p1, r1, p2, r2);
482
483
484
485
486
487
488
489 if (distance > r1 + r2 + 0.0001)
490 {
491 if (result.size() > 0)
492 {
493 Point2d.circleIntersections(p1, r1, p2, r2);
494 }
495 assertEquals("There are 0 intersections", 0, result.size());
496 }
497 if (distance < r1 + r2 - 0.0001 && distance > Math.abs(r2 - r1) + 0.0001)
498 {
499 if (result.size() != 2)
500 {
501 Point2d.circleIntersections(p1, r1, p2, r2);
502 }
503 assertEquals("There are 2 intersections", 2, result.size());
504 }
505 for (Point2d p : result)
506 {
507 if (Math.abs(r1 - p.distance(p1)) > 0.1 || Math.abs(r2 - p.distance(p2)) > 0.1)
508 {
509 Point2d.circleIntersections(p1, r1, p2, r2);
510 }
511 assertEquals("result is at r1 from p1", r1, p.distance(p1), 0.0001);
512 assertEquals("result is at r2 from p2", r2, p.distance(p2), 0.0001);
513 }
514 }
515 }
516 }
517 }
518 }
519 }
520 }
521 try
522 {
523 Point2d.circleIntersections(new Point2d(1, 2), -1, new Point2d(3, 4), 2);
524 fail("negative radius should have thrown a DrawRuntimeException");
525 }
526 catch (DrawRuntimeException dre)
527 {
528
529 }
530
531 try
532 {
533 Point2d.circleIntersections(new Point2d(1, 2), 5, new Point2d(3, 4), -2);
534 fail("negative radius should have thrown a DrawRuntimeException");
535 }
536 catch (DrawRuntimeException dre)
537 {
538
539 }
540
541 try
542 {
543 Point2d.circleIntersections(null, 5, new Point2d(3, 4), 2);
544 fail("null for center1 should have thrown a NullPointerException");
545 }
546 catch (NullPointerException npe)
547 {
548
549 }
550
551 try
552 {
553 Point2d.circleIntersections(new Point2d(3, 4), 5, null, 2);
554 fail("null for center1 should have thrown a NullPointerException");
555 }
556 catch (NullPointerException npe)
557 {
558
559 }
560 }
561
562
563
564
565 @Test
566 public void testDirection()
567 {
568 Point2d reference = new Point2d(5, 8);
569 assertEquals("East", 0, reference.directionTo(new Point2d(reference.x + 10, reference.y)), 0);
570 assertEquals("North", Math.PI / 2, reference.directionTo(new Point2d(reference.x, reference.y + 5)), 0.00001);
571 assertEquals("NorthEast", Math.PI / 4, reference.directionTo(new Point2d(reference.x + 2, reference.y + 2)), 0.00001);
572 assertEquals("West", Math.PI, reference.directionTo(new Point2d(reference.x - 1, reference.y)), 0);
573 assertEquals("South", -Math.PI / 2, reference.directionTo(new Point2d(reference.x, reference.y - 0.5)), 0.00001);
574 assertEquals("SouthWst", -3 * Math.PI / 4, reference.directionTo(new Point2d(reference.x - 0.2, reference.y - 0.2)),
575 0.00001);
576 }
577
578 }