1 package org.djutils.draw.point;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertFalse;
5 import static org.junit.jupiter.api.Assertions.assertNotEquals;
6 import static org.junit.jupiter.api.Assertions.assertNotNull;
7 import static org.junit.jupiter.api.Assertions.assertNull;
8 import static org.junit.jupiter.api.Assertions.assertTrue;
9 import static org.junit.jupiter.api.Assertions.fail;
10
11 import java.awt.geom.Point2D;
12 import java.util.List;
13
14 import org.djutils.draw.bounds.Bounds2d;
15 import org.djutils.draw.line.LineSegment2d;
16 import org.djutils.draw.line.PolyLine2d;
17 import org.djutils.exceptions.Try;
18 import org.junit.jupiter.api.Test;
19
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, "Access x");
42 assertEquals(-20.0, p.y, 1E-6, "Access y");
43 assertEquals(2, p.getDimensions(), "Dimensions is 2");
44
45 assertEquals(1, p.size(), "Size method returns 1");
46
47 try
48 {
49 new Point2d(Double.NaN, 0);
50 fail("NaN should have thrown an ArithmeticException");
51 }
52 catch (ArithmeticException e)
53 {
54
55 }
56
57 try
58 {
59 new Point2d(0, Double.NaN);
60 fail("NaN should have thrown an ArithmeticException");
61 }
62 catch (ArithmeticException e)
63 {
64
65 }
66
67 double[] p2Arr = new double[] {5.0, 6.0};
68 p = new Point2d(p2Arr);
69 assertEquals(5.0, p.x, 0);
70 assertEquals(6.0, p.y, 0);
71 Point2D.Double p2DD = new Point2D.Double(-0.1, -0.2);
72 p = new Point2d(p2DD);
73 assertEquals(-0.1, p.x, 1E-6);
74 assertEquals(-0.2, p.y, 1E-6);
75 assertEquals(p2DD, p.toPoint2D());
76
77 Try.testFail(new Try.Execution()
78 {
79 @Override
80 public void execute() throws Throwable
81 {
82 new Point2d((Point2D.Double) null);
83 }
84 }, "Should throw NPE", NullPointerException.class);
85
86 Try.testFail(new Try.Execution()
87 {
88 @Override
89 public void execute() throws Throwable
90 {
91 new Point2d(new double[] {});
92 }
93 }, "Should throw IAE", IllegalArgumentException.class);
94
95 Try.testFail(new Try.Execution()
96 {
97 @Override
98 public void execute() throws Throwable
99 {
100 new Point2d(new double[] {1.0});
101 }
102 }, "Should throw IAE", IllegalArgumentException.class);
103
104 Try.testFail(new Try.Execution()
105 {
106 @Override
107 public void execute() throws Throwable
108 {
109 new Point2d(new double[] {1.0, 2.0, 3.0});
110 }
111 }, "Should throw IAE", IllegalArgumentException.class);
112
113 Try.testFail(new Try.Execution()
114 {
115 @Override
116 public void execute() throws Throwable
117 {
118 new Point2d(new Point2D.Double(Double.NaN, 2));
119 }
120 }, "Should throw ArithmeticException", ArithmeticException.class);
121
122 Try.testFail(new Try.Execution()
123 {
124 @Override
125 public void execute() throws Throwable
126 {
127 new Point2d(new Point2D.Double(1, Double.NaN));
128 }
129 }, "Should throw ArithmeticException", ArithmeticException.class);
130
131
132 assertTrue(p.equals(p));
133 assertEquals(p.hashCode(), p.hashCode());
134 Point3d p3d = p.translate(1.0, 2.0, 3.0);
135 assertFalse(p.equals(p3d));
136 assertFalse(p.equals(null));
137 assertNotEquals(p3d.hashCode(), p.hashCode());
138 assertEquals(p, p.translate(0.0, 0.0));
139 assertNotEquals(p, p.translate(1.0, 0.0));
140 assertNotEquals(p, p.translate(0.0, 1.0));
141 assertEquals(p.x + 1, p3d.x, 0.00001, "x");
142 assertEquals(p.y + 2, p3d.y, 0.00001, "y");
143 assertEquals(3, p3d.z, 0, "z");
144
145
146 p = new Point2d(10.0, 20.0);
147 assertEquals("Point2d [x=10.000000, y=20.000000]", p.toString());
148 assertEquals("Point2d [x=10.0, y=20.0]", p.toString("%.1f"));
149 assertEquals("[x=10, y=20]", p.toString("%.0f", true));
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 ArithmeticException");
197 }
198 catch (ArithmeticException e)
199 {
200
201 }
202
203 try
204 {
205 p.translate(1.0, Double.NaN);
206 fail("NaN translation should have thrown an ArithmeticException");
207 }
208 catch (ArithmeticException e)
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 IAE", IllegalArgumentException.class);
246
247 Bounds2d bounds = p1.getAbsoluteBounds();
248 assertEquals(p1.x, bounds.getMinX(), 0, "Bounds min x");
249 assertEquals(p1.y, bounds.getMinY(), 0, "Bounds min y");
250 assertEquals(p1.x, bounds.getMaxX(), 0, "Bounds max x");
251 assertEquals(p1.y, bounds.getMaxY(), 0, "Bounds max y");
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 ArithmeticException");
266 }
267 catch (ArithmeticException e)
268 {
269
270 }
271
272 try
273 {
274 p1.translate(1.0, Double.NaN);
275 fail("NaN translation should have thrown an ArithmeticException");
276 }
277 catch (ArithmeticException e)
278 {
279
280 }
281
282 try
283 {
284 p1.translate(Double.NaN, 2.0, 3.0);
285 fail("NaN translation should have thrown an ArithmeticException");
286 }
287 catch (ArithmeticException e)
288 {
289
290 }
291
292 try
293 {
294 p1.translate(1.0, Double.NaN, 3.0);
295 fail("NaN translation should have thrown an ArithmeticException");
296 }
297 catch (ArithmeticException e)
298 {
299
300 }
301
302 try
303 {
304 p1.translate(1.0, 2.0, Double.NaN);
305 fail("NaN translation should have thrown an ArithmeticException");
306 }
307 catch (ArithmeticException e)
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 Point2d p = new Point2d(1, 2);
340 Try.testFail(new Try.Execution()
341 {
342
343 @Override
344 public void execute() throws Throwable
345 {
346 p.epsilonEquals(p, -0.1);
347 }
348 }, "Should throw IllegalArgumentException", IllegalArgumentException.class);
349 }
350
351
352
353
354 @Test
355 public void testIntersectionOfLineSegments()
356 {
357 assertNull(
358 Point2d.intersectionOfLineSegments(new Point2d(1, 2), new Point2d(4, 2), new Point2d(1, 2), new Point2d(4, 2)),
359 "horizontal line intersection with itself returns null");
360 assertNull(Point2d.intersectionOfLineSegments(new Point2d(1, 2), new Point2d(1, 10), new Point2d(1, 2),
361 new Point2d(1, 10)), "vertical line intersection with itself returns null");
362 assertEquals(new Point2d(2, 2),
363 Point2d.intersectionOfLineSegments(new Point2d(1, 1), new Point2d(6, 6), new Point2d(4, 2), new Point2d(-2, 2)),
364 "Intersection is at (2,2)");
365 assertEquals(new Point2d(2, 2), Point2d.intersectionOfLineSegments(1, 1, 6, 6, 4, 2, -2, 2),
366 "Intersection is at (2,2)");
367 assertEquals(new Point2d(2, 2),
368 Point2d.intersectionOfLineSegments(new LineSegment2d(1, 1, 6, 6), new LineSegment2d(4, 2, -2, 2)),
369 "Intersection is at (2,2)");
370
371 assertNull(Point2d.intersectionOfLineSegments(new Point2d(1, 1), new Point2d(5, 5), new Point2d(0, -3),
372 new Point2d(10, 0)), "line two passes before start of line one");
373 assertNull(Point2d.intersectionOfLineSegments(new Point2d(1, 1), new Point2d(5, 5), new Point2d(0, 20),
374 new Point2d(100, 30)), "line two passes before after end of line one");
375 assertNull(
376 Point2d.intersectionOfLineSegments(new Point2d(1, 1), new Point2d(5, 5), new Point2d(5, 3), new Point2d(10, 2)),
377 "line one passes before start of line two");
378 assertNull(Point2d.intersectionOfLineSegments(new Point2d(1, 1), new Point2d(5, 5), new Point2d(-10, 3),
379 new Point2d(0, 2)), "line one passes after end of line two");
380 assertNull(Point2d.intersectionOfLineSegments(1, 1, 5, 5, 0, -3, 10, 0), "line two passes before start of line one");
381 assertNull(Point2d.intersectionOfLineSegments(new LineSegment2d(1, 15, 5, 5), new LineSegment2d(0, -3, 10, 0)),
382 "line two passes before start of line one");
383 assertNull(Point2d.intersectionOfLineSegments(1, 1, 5, 5, 0, 20, 100, 30),
384 "line two passes before after end of line one");
385 assertNull(Point2d.intersectionOfLineSegments(1, 1, 5, 5, 5, 3, 10, 2), "line one passes before start of line two");
386 assertNull(Point2d.intersectionOfLineSegments(new LineSegment2d(1, 1, 5, 5), new LineSegment2d(5, 3, 10, 2)),
387 "line one passes before start of line two");
388 assertNull(Point2d.intersectionOfLineSegments(1, 1, 5, 5, -10, 3, 0, 2), "line one passes after end of line two");
389 assertNull(Point2d.intersectionOfLineSegments(new LineSegment2d(1, 1, 5, 5), new LineSegment2d(-10, 3, 0, 2)),
390 "line one passes after end of line two");
391
392 Point2d line1P1 = new Point2d(1, 2);
393 Point2d line1P2 = new Point2d(3, 2);
394 Point2d line2P1 = new Point2d(2, 0);
395 Point2d line2P2 = new Point2d(2, 4);
396 try
397 {
398 Point2d.intersectionOfLines(null, line1P2, line2P1, line2P2);
399 fail("Null parameter should have thrown a NullPointerException");
400 }
401 catch (NullPointerException npe)
402 {
403
404 }
405
406 try
407 {
408 Point2d.intersectionOfLines(line1P1, null, line2P1, line2P2);
409 fail("Null parameter should have thrown a NullPointerException");
410 }
411 catch (NullPointerException npe)
412 {
413
414 }
415
416 try
417 {
418 Point2d.intersectionOfLines(line1P1, line1P2, null, line2P2);
419 fail("Null parameter should have thrown a NullPointerException");
420 }
421 catch (NullPointerException npe)
422 {
423
424 }
425
426 try
427 {
428 Point2d.intersectionOfLines(line1P1, line1P2, line2P1, null);
429 fail("Null parameter should have thrown a NullPointerException");
430 }
431 catch (NullPointerException npe)
432 {
433
434 }
435
436 try
437 {
438 Point2d.intersectionOfLineSegments(null, line1P2, line2P1, line2P2);
439 fail("Null parameter should have thrown a NullPointerException");
440 }
441 catch (NullPointerException npe)
442 {
443
444 }
445
446 try
447 {
448 Point2d.intersectionOfLineSegments(line1P1, null, line2P1, line2P2);
449 fail("Null parameter should have thrown a NullPointerException");
450 }
451 catch (NullPointerException npe)
452 {
453
454 }
455
456 try
457 {
458 Point2d.intersectionOfLineSegments(line1P1, line1P2, null, line2P2);
459 fail("Null parameter should have thrown a NullPointerException");
460 }
461 catch (NullPointerException npe)
462 {
463
464 }
465
466 try
467 {
468 Point2d.intersectionOfLineSegments(line1P1, line1P2, line2P1, null);
469 fail("Null parameter should have thrown a NullPointerException");
470 }
471 catch (NullPointerException npe)
472 {
473
474 }
475
476 }
477
478
479
480
481 @Test
482 public void testIntersectionOfLines()
483 {
484 assertNull(Point2d.intersectionOfLines(new Point2d(1, 2), new Point2d(4, 2), new Point2d(1, 2), new Point2d(4, 2)),
485 "horizontal line intersection with itself returns null");
486 assertNull(Point2d.intersectionOfLines(1, 2, 4, 2, 1, 2, 4, 2),
487 "horizontal line intersection with itself returns null");
488 assertNull(Point2d.intersectionOfLineSegments(new Point2d(1, 2), new Point2d(1, 10), new Point2d(1, 2),
489 new Point2d(1, 10)), "vertical line intersection with itself returns null");
490 assertNull(Point2d.intersectionOfLineSegments(1, 2, 1, 10, 1, 2, 1, 10),
491 "vertical line intersection with itself returns null");
492 assertNull(Point2d.intersectionOfLineSegments(new LineSegment2d(1, 2, 1, 10), new LineSegment2d(1, 2, 1, 10)),
493 "vertical line intersection with itself returns null");
494 assertEquals(new Point2d(2, 2),
495 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(6, 6), new Point2d(4, 2), new Point2d(-2, 2)),
496 "Intersection is at (2,2)");
497
498 assertEquals(new Point2d(-1.5, -1.5),
499 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(0, -3), new Point2d(10, -13)),
500 "line two passes before start of line one");
501 assertEquals(new Point2d(20, 20),
502 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(0, 20), new Point2d(100, 20)),
503 "line two passes before after end of line one");
504 assertEquals(new Point2d(4, 4),
505 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(7, 1), new Point2d(10, -2)),
506 "line one passes before start of line two");
507 assertEquals(new Point2d(-3.5, -3.5),
508 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(5, 5), new Point2d(-10, 3), new Point2d(0, -7)),
509 "line one passes after end of line two");
510
511 assertEquals(new Point2d(1, 1),
512 Point2d.intersectionOfLines(new Point2d(1, 1), new Point2d(2, 1), new Point2d(1, 0), new Point2d(1, 3)),
513 "begin of first is on second");
514 assertEquals(new Point2d(1, 1),
515 Point2d.intersectionOfLines(new Point2d(-1, 1), new Point2d(1, 1), new Point2d(1, 0), new Point2d(1, 3)),
516 "end of first is on second");
517 assertEquals(new Point2d(1, 1),
518 Point2d.intersectionOfLines(new Point2d(-1, 1), new Point2d(2, 1), new Point2d(1, 1), new Point2d(1, 3)),
519 "begin of second is on first");
520 assertEquals(new Point2d(1, 1),
521 Point2d.intersectionOfLines(new Point2d(-1, 1), new Point2d(2, 1), new Point2d(1, -1), new Point2d(1, 1)),
522 "end of second is on first");
523
524 assertTrue(new Point2d(1, 1).epsilonEquals(
525 Point2d.intersectionOfLines(new Point2d(1.001, 1), new Point2d(2, 1), new Point2d(1, 0), new Point2d(1, 3)),
526 0.0001), "begin of first is just over second");
527 assertTrue(new Point2d(1, 1).epsilonEquals(
528 Point2d.intersectionOfLines(new Point2d(-1, 1), new Point2d(0.999, 1), new Point2d(1, 0), new Point2d(1, 3)),
529 0.0001), "end of first is just over second");
530 assertTrue(new Point2d(1, 1).epsilonEquals(
531 Point2d.intersectionOfLines(new Point2d(-1, 1), new Point2d(2, 1), new Point2d(1, 1.001), new Point2d(1, 3)),
532 0.0001), "begin of second is just over first");
533 assertTrue(new Point2d(1, 1).epsilonEquals(
534 Point2d.intersectionOfLines(new Point2d(-1, 1), new Point2d(2, 1), new Point2d(1, -1), new Point2d(1, 0.999)),
535 0.0001), "end of second is just over first");
536
537 assertNull(Point2d.intersectionOfLineSegments(new Point2d(1.001, 1), new Point2d(2, 1), new Point2d(1, 0),
538 new Point2d(1, 3)), "begin of first is just not on second");
539 assertNull(Point2d.intersectionOfLineSegments(new Point2d(-1, 1), new Point2d(0.999, 1), new Point2d(1, 0),
540 new Point2d(1, 3)), "end of first is just not on second");
541 assertNull(Point2d.intersectionOfLineSegments(new Point2d(-1, 1), new Point2d(2, 1), new Point2d(1, 1.001),
542 new Point2d(1, 3)), "begin of second is just not on first");
543 assertNull(Point2d.intersectionOfLineSegments(new Point2d(-1, 1), new Point2d(2, 1), new Point2d(1, -1),
544 new Point2d(1, 0.999)), "end of second is just not on first");
545 }
546
547
548
549
550 @Test
551 public void testClosestPointOnSegmentAndLine()
552 {
553 Point2d p1 = new Point2d(-2, 3);
554 for (Point2d p2 : new Point2d[] {new Point2d(7, 4), new Point2d(-3, 6) ,
555 new Point2d(-2, -5) , new Point2d(8, 3) })
556 {
557 PolyLine2d line = new PolyLine2d(p1, p2);
558 for (double x = -10; x <= 10; x += 0.5)
559 {
560 for (double y = -10; y <= 10; y += 0.5)
561 {
562 Point2d p = new Point2d(x, y);
563
564 double fraction = 0.5;
565 double step = 0.25;
566 Point2d approximation = line.getLocationFraction(fraction);
567 double distance = approximation.distance(p);
568
569 for (int iteration = 0; iteration < 10; iteration++)
570 {
571
572 double upFraction = fraction + step;
573 Point2d upApproximation = line.getLocationFraction(upFraction);
574 double upDistance = upApproximation.distance(p);
575 if (upDistance < distance)
576 {
577 distance = upDistance;
578 fraction = upFraction;
579 approximation = upApproximation;
580 }
581 else
582 {
583
584 double downFraction = fraction - step;
585 Point2d downApproximation = line.getLocationFraction(downFraction);
586 double downDistance = downApproximation.distance(p);
587 if (downDistance < distance)
588 {
589 distance = downDistance;
590 fraction = downFraction;
591 approximation = downApproximation;
592 }
593 }
594 step /= 2;
595 }
596 Point2d result = p.closestPointOnSegment(p1, p2);
597 assertEquals(0, approximation.distance(result), line.getLength() / 1000,
598 "distance should be less than one thousandth of line length");
599 assertEquals(p1, p.closestPointOnSegment(p1, p1),
600 "zero length line segment should always return start point");
601 result = p.closestPointOnSegment(p1.x, p1.y, p2.x, p2.y);
602 assertEquals(0, approximation.distance(result), line.getLength() / 1000,
603 "distance should be less than one thousandth of line length");
604
605 if (fraction > 0.001 && fraction < 0.999)
606 {
607 result = p.closestPointOnLine(p1, p2);
608 assertEquals(0, approximation.distance(result), line.getLength() / 1000,
609 "distance should be less than one thousandth of line length");
610 result = p.closestPointOnLine(p1, p2);
611 assertEquals(0, approximation.distance(result), line.getLength() / 1000,
612 "distance should be less than one thousandth of line length");
613 result = p.closestPointOnLine(p1.x, p1.y, p2.x, p2.y);
614 assertEquals(0, approximation.distance(result), line.getLength() / 1000,
615 "distance should be less than one thousandth of line length");
616 }
617 else
618 {
619
620 double range = Math.max(Math.max(line.getLength(), p.distance(p1)), p.distance(p2));
621 step = 5.0;
622 fraction = 0.5;
623 distance = range;
624
625 for (int iteration = 0; iteration < 20; iteration++)
626 {
627
628 double upFraction = fraction + step;
629 Point2d upApproximation = line.getLocationFractionExtended(upFraction);
630 double upDistance = upApproximation.distance(p);
631 if (upDistance < distance)
632 {
633 distance = upDistance;
634 fraction = upFraction;
635 approximation = upApproximation;
636 }
637 else
638 {
639
640 double downFraction = fraction - step;
641 Point2d downApproximation = line.getLocationFractionExtended(downFraction);
642 double downDistance = downApproximation.distance(p);
643 if (downDistance < distance)
644 {
645 distance = downDistance;
646 fraction = downFraction;
647 approximation = downApproximation;
648 }
649 }
650 step /= 2;
651 }
652 result = p.closestPointOnLine(p1, p2);
653 assertEquals(0, approximation.distance(result), range / 1000,
654 "distance should be less than one thousandth of range");
655 result = p.closestPointOnLine(p1, p2);
656 assertEquals(0, approximation.distance(result), range / 1000,
657 "distance should be less than one thousandth of range");
658 result = p.closestPointOnLine(p1.x, p1.y, p2.x, p2.y);
659 assertEquals(0, approximation.distance(result), range / 1000,
660 "distance should be less than one thousandth of range");
661 if (fraction < -0.001 || fraction > 1.001)
662 {
663 assertNull(new LineSegment2d(p1, p2).projectOrthogonal(p), "projectOrthogonal should return null");
664 assertEquals(result, new LineSegment2d(p1, p2).projectOrthogonalExtended(p),
665 "projectOrthogonalExtended should return same result as closestPointOnLine");
666 }
667 }
668 }
669 }
670 }
671
672 try
673 {
674 p1.closestPointOnLine(null, new Point2d(5, 6));
675 fail("null should have thrown a NullPointerException");
676 }
677 catch (NullPointerException e)
678 {
679
680 }
681
682 try
683 {
684 p1.closestPointOnLine(new Point2d(5, 6), null);
685 fail("null should have thrown a NullPointerException");
686 }
687 catch (NullPointerException e)
688 {
689
690 }
691
692 try
693 {
694 p1.closestPointOnSegment(Double.NaN, 7, 8, 9);
695 fail("NaN value should have thrown an ArithmeticException");
696 }
697 catch (ArithmeticException e)
698 {
699
700 }
701
702 try
703 {
704 p1.closestPointOnSegment(6, Double.NaN, 8, 9);
705 fail("NaN value should have thrown na ArithmeticException");
706 }
707 catch (ArithmeticException e)
708 {
709
710 }
711
712 try
713 {
714 p1.closestPointOnSegment(6, 7, Double.NaN, 9);
715 fail("NaN value should have thrown an ArithmeticException");
716 }
717 catch (ArithmeticException e)
718 {
719
720 }
721
722 try
723 {
724 p1.closestPointOnSegment(6, 7, 8, Double.NaN);
725 fail("NaN value should have thrown an ArithmeticException");
726 }
727 catch (ArithmeticException e)
728 {
729
730 }
731
732 try
733 {
734 p1.closestPointOnLine(Double.NaN, 7, 8, 9);
735 fail("NaN value should have thrown a ArithmeticException");
736 }
737 catch (ArithmeticException e)
738 {
739
740 }
741
742 try
743 {
744 p1.closestPointOnLine(6, Double.NaN, 8, 9);
745 fail("NaN value should have thrown an ArithmeticException");
746 }
747 catch (ArithmeticException e)
748 {
749
750 }
751
752 try
753 {
754 p1.closestPointOnLine(6, 7, Double.NaN, 9);
755 fail("NaN value should have thrown an ArithmeticException");
756 }
757 catch (ArithmeticException e)
758 {
759
760 }
761
762 try
763 {
764 p1.closestPointOnLine(6, 7, 8, Double.NaN);
765 fail("NaN value should have thrown an ArithmeticException");
766 }
767 catch (ArithmeticException e)
768 {
769
770 }
771
772 try
773 {
774 p1.closestPointOnLine(6, 7, 6, 7);
775 fail("identical points should have thrown a IllegalArgumentException");
776 }
777 catch (IllegalArgumentException e)
778 {
779
780 }
781
782 }
783
784
785
786
787 @Test
788 public void circleIntersectionTest()
789 {
790 for (int x1 = -5; x1 <= 5; x1++)
791 {
792 for (int y1 = -5; y1 <= 5; y1++)
793 {
794 Point2d p1 = new Point2d(x1, y1);
795 for (int r1 = 0; r1 < 5; r1++)
796 {
797 for (int x2 = -5; x2 <= 5; x2++)
798 {
799 for (int y2 = -5; y2 <= 5; y2++)
800 {
801 Point2d p2 = new Point2d(x2, y2);
802 double distance = p1.distance(p2);
803 for (int r2 = 0; r2 < 5; r2++)
804 {
805 if (x1 == x2 && y1 == y2 && r1 == r2)
806 {
807 try
808 {
809 Point2d.circleIntersections(p1, r1, p2, r2);
810 fail("Identical circles should have thrown an IllegalArgumentException");
811 }
812 catch (IllegalArgumentException e)
813 {
814
815 }
816 }
817 else
818 {
819 List<Point2d> result = Point2d.circleIntersections(p1, r1, p2, r2);
820
821
822
823
824
825
826
827 if (distance > r1 + r2 + 0.0001)
828 {
829 if (result.size() > 0)
830 {
831 Point2d.circleIntersections(p1, r1, p2, r2);
832 }
833 assertEquals(0, result.size(), "There are 0 intersections");
834 }
835 if (distance < r1 + r2 - 0.0001 && distance > Math.abs(r2 - r1) + 0.0001)
836 {
837 if (result.size() != 2)
838 {
839 Point2d.circleIntersections(p1, r1, p2, r2);
840 }
841 assertEquals(2, result.size(), "There are 2 intersections");
842 }
843 for (Point2d p : result)
844 {
845 if (Math.abs(r1 - p.distance(p1)) > 0.1 || Math.abs(r2 - p.distance(p2)) > 0.1)
846 {
847 Point2d.circleIntersections(p1, r1, p2, r2);
848 }
849 assertEquals(r1, p.distance(p1), 0.0001, "result is at r1 from p1");
850 assertEquals(r2, p.distance(p2), 0.0001, "result is at r2 from p2");
851 }
852 }
853 }
854 }
855 }
856 }
857 }
858 }
859 try
860 {
861 Point2d.circleIntersections(new Point2d(1, 2), -1, new Point2d(3, 4), 2);
862 fail("negative radius should have thrown an IllegalArgumentException");
863 }
864 catch (IllegalArgumentException e)
865 {
866
867 }
868
869 try
870 {
871 Point2d.circleIntersections(new Point2d(1, 2), 5, new Point2d(3, 4), -2);
872 fail("negative radius should have thrown an IllegalArgumentException");
873 }
874 catch (IllegalArgumentException e)
875 {
876
877 }
878
879 try
880 {
881 Point2d.circleIntersections(null, 5, new Point2d(3, 4), 2);
882 fail("null for center1 should have thrown a NullPointerException");
883 }
884 catch (NullPointerException npe)
885 {
886
887 }
888
889 try
890 {
891 Point2d.circleIntersections(new Point2d(3, 4), 5, null, 2);
892 fail("null for center1 should have thrown a NullPointerException");
893 }
894 catch (NullPointerException npe)
895 {
896
897 }
898 }
899
900
901
902
903 @Test
904 public void testDirection()
905 {
906 Point2d reference = new Point2d(5, 8);
907 assertEquals(0, reference.directionTo(new Point2d(reference.x + 10, reference.y)), 0, "East");
908 assertEquals(Math.PI / 2, reference.directionTo(new Point2d(reference.x, reference.y + 5)), 0.00001, "North");
909 assertEquals(Math.PI / 4, reference.directionTo(new Point2d(reference.x + 2, reference.y + 2)), 0.00001, "NorthEast");
910 assertEquals(Math.PI, reference.directionTo(new Point2d(reference.x - 1, reference.y)), 0, "West");
911 assertEquals(-Math.PI / 2, reference.directionTo(new Point2d(reference.x, reference.y - 0.5)), 0.00001, "South");
912 assertEquals(-3 * Math.PI / 4, reference.directionTo(new Point2d(reference.x - 0.2, reference.y - 0.2)), 0.00001,
913 "SouthWst");
914 }
915
916 }