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