View Javadoc
1   package org.djutils.draw.bounds;
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.assertNull;
7   import static org.junit.Assert.assertTrue;
8   import static org.junit.Assert.fail;
9   
10  import java.awt.geom.Rectangle2D;
11  import java.util.ArrayList;
12  import java.util.Collection;
13  import java.util.Iterator;
14  
15  import org.djutils.draw.DrawRuntimeException;
16  import org.djutils.draw.Drawable2d;
17  import org.djutils.draw.line.LineSegment2d;
18  import org.djutils.draw.line.PolyLine2d;
19  import org.djutils.draw.point.Point2d;
20  import org.junit.Test;
21  
22  /**
23   * Bounds2dTest.java.
24   * <p>
25   * Copyright (c) 2020-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
27   * </p>
28   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
29   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
30   */
31  public class Bounds2dTest
32  {
33  
34      /**
35       * Test the bounding rectangle constructor.
36       * @throws DrawRuntimeException if that happens uncaught; this test has failed
37       * @throws IllegalArgumentException if that happens uncaught; this test has failed
38       */
39      @Test
40      public void constructorTest() throws IllegalArgumentException, DrawRuntimeException
41      {
42          try
43          {
44              new Bounds2d(Double.NaN, 0, 0, 0);
45              fail("Nan should have thrown an IllegalArgumentException");
46          }
47          catch (IllegalArgumentException iae)
48          {
49              // Ignore expected exception
50          }
51  
52          try
53          {
54              new Bounds2d(0, Double.NaN, 0, 0);
55              fail("Nan should have thrown an IllegalArgumentException");
56          }
57          catch (IllegalArgumentException iae)
58          {
59              // Ignore expected exception
60          }
61  
62          try
63          {
64              new Bounds2d(0, 0, Double.NaN, 0);
65              fail("Nan should have thrown an IllegalArgumentException");
66          }
67          catch (IllegalArgumentException iae)
68          {
69              // Ignore expected exception
70          }
71  
72          try
73          {
74              new Bounds2d(0, 0, 0, Double.NaN);
75              fail("Nan should have thrown an IllegalArgumentException");
76          }
77          catch (IllegalArgumentException iae)
78          {
79              // Ignore expected exception
80          }
81  
82          try
83          {
84              new Bounds2d(2, -2, 0, 0);
85              fail("Negative x-range should have thrown an IllegalArgumentException");
86          }
87          catch (IllegalArgumentException iae)
88          {
89              // Ignore expected exception
90          }
91  
92          try
93          {
94              new Bounds2d(0, 0, 2, -2);
95              fail("Negative y-range should have thrown an IllegalArgumentException");
96          }
97          catch (IllegalArgumentException iae)
98          {
99              // Ignore expected exception
100         }
101 
102         try
103         {
104             new Bounds2d(new Drawable2d[] {});
105             fail("Empty array should have thrown an IllegalArgumentException");
106         }
107         catch (IllegalArgumentException iae)
108         {
109             // Ignore expected exception
110         }
111 
112         Bounds2d br = new Bounds2d(1, 2, 3, 6);
113         assertEquals("minX", 1, br.getMinX(), 0);
114         assertEquals("maxX", 2, br.getMaxX(), 0);
115         assertEquals("minY", 3, br.getMinY(), 0);
116         assertEquals("maxY", 6, br.getMaxY(), 0);
117 
118         try
119         {
120             new Bounds2d(Double.NaN, 0);
121             fail("Nan should have thrown an IllegalArgumentException");
122         }
123         catch (IllegalArgumentException iae)
124         {
125             // Ignore expected exception
126         }
127 
128         try
129         {
130             new Bounds2d(0, Double.NaN);
131             fail("Nan should have thrown an IllegalArgumentException");
132         }
133         catch (IllegalArgumentException iae)
134         {
135             // Ignore expected exception
136         }
137 
138         try
139         {
140             new Bounds2d(-3, 0);
141             fail("Negative x-range should have thrown an IllegalArgumentException");
142         }
143         catch (IllegalArgumentException iae)
144         {
145             // Ignore expected exception
146         }
147 
148         try
149         {
150             new Bounds2d(0, -3);
151             fail("Negative y-range should have thrown an IllegalArgumentException");
152         }
153         catch (IllegalArgumentException iae)
154         {
155             // Ignore expected exception
156         }
157 
158         br = new Bounds2d(20, 30);
159         assertEquals("deltaX", 20, br.getDeltaX(), 0);
160         assertEquals("deltaY", 30, br.getDeltaY(), 0);
161         assertEquals("volume", 20 * 30, br.getArea(), 0);
162         assertFalse("contains does not include boundaries", br.contains(-10, 0));
163         assertFalse("contains does not include boundaries", br.contains(10, 0));
164         assertFalse("contains does not include boundaries", br.contains(0, -15));
165         assertFalse("contains does not include boundaries", br.contains(0, 15));
166         assertTrue("contains", br.contains(-0.999, 0));
167         assertTrue("contains", br.contains(0.999, 0));
168         assertTrue("contains", br.contains(0, -14.999));
169         assertTrue("contains", br.contains(0, 14.999));
170         assertTrue("covers includes boundaries", br.covers(-10, 0));
171         assertTrue("covers includes boundaries", br.covers(10, 0));
172         assertTrue("covers includes boundaries", br.covers(0, -15));
173         assertTrue("covers includes boundaries", br.covers(0, 15));
174         assertFalse("covers", br.covers(-10.001, 0));
175         assertFalse("covers", br.covers(10.001, 0));
176         assertFalse("covers", br.covers(0, -15.001));
177         assertFalse("covers", br.covers(0, 15.001));
178 
179         Collection<Drawable2d> drawable2dCollection = new ArrayList<>();
180         try
181         {
182             new Bounds2d(drawable2dCollection);
183             fail("Empty drawable collection should have thrown an IllegalArgumentException");
184         }
185         catch (IllegalArgumentException iae)
186         {
187             // Ignore expected exception
188         }
189 
190         drawable2dCollection.add(null);
191         try
192         {
193             new Bounds2d(drawable2dCollection);
194             fail("null element in collection should have thrown an NullPointerException");
195         }
196         catch (NullPointerException npe)
197         {
198             // Ignore expected exception
199         }
200         drawable2dCollection.clear();
201 
202         drawable2dCollection.add(new Point2d(10, 20));
203         br = new Bounds2d(drawable2dCollection);
204         assertEquals("minX", 10, br.getMinX(), 0);
205         assertEquals("maxX", 10, br.getMaxX(), 0);
206         assertEquals("minY", 20, br.getMinY(), 0);
207         assertEquals("maxY", 20, br.getMaxY(), 0);
208 
209         drawable2dCollection.add(new Point2d(-5, -6));
210         br = new Bounds2d(drawable2dCollection);
211         assertEquals("minX", -5, br.getMinX(), 0);
212         assertEquals("maxX", 10, br.getMaxX(), 0);
213         assertEquals("minY", -6, br.getMinY(), 0);
214         assertEquals("maxY", 20, br.getMaxY(), 0);
215 
216         drawable2dCollection.add(new LineSegment2d(20, 30, 40, 50));
217         br = new Bounds2d(drawable2dCollection);
218         assertEquals("minX", -5, br.getMinX(), 0);
219         assertEquals("maxX", 40, br.getMaxX(), 0);
220         assertEquals("minY", -6, br.getMinY(), 0);
221         assertEquals("maxY", 50, br.getMaxY(), 0);
222 
223         assertTrue("toString returns something descriptive", br.toString().startsWith("Bounds2d "));
224         assertEquals("toString with false argument produces same as toString with no argument", br.toString(),
225                 br.toString(false));
226         assertTrue("toString with true argument produces rhs of toString with no argument",
227                 br.toString().indexOf(br.toString(true)) > 0);
228 
229         drawable2dCollection.add(new Point2d(40, 50));
230         // This collection is an ArrayList, so the elements are stored in the order in which they were added
231         br = new Bounds2d(drawable2dCollection);
232         assertEquals("minX", -5, br.getMinX(), 0);
233         assertEquals("maxX", 40, br.getMaxX(), 0);
234         assertEquals("minY", -6, br.getMinY(), 0);
235         assertEquals("maxY", 50, br.getMaxY(), 0);
236 
237         br = new Bounds2d(drawable2dCollection.toArray((new Drawable2d[0])));
238         assertEquals("minX", -5, br.getMinX(), 0);
239         assertEquals("maxX", 40, br.getMaxX(), 0);
240         assertEquals("minY", -6, br.getMinY(), 0);
241         assertEquals("maxY", 50, br.getMaxY(), 0);
242 
243         drawable2dCollection.add(null);
244         try
245         {
246             new Bounds2d(drawable2dCollection);
247             fail("null element in collection should have thrown an NullPointerException");
248         }
249         catch (NullPointerException npe)
250         {
251             // Ignore expected exception
252         }
253 
254         PolyLine2d line = new PolyLine2d(new Point2d(1, 12), new Point2d(3, 12), new Point2d(2, 11));
255         br = new Bounds2d(line);
256         assertEquals("minX", 1, br.getMinX(), 0);
257         assertEquals("minY", 11, br.getMinY(), 0);
258         assertEquals("maxX", 3, br.getMaxX(), 0);
259         assertEquals("maxY", 12, br.getMaxY(), 0);
260 
261         assertEquals("bounding box of reversed line", br, new Bounds2d(line.reverse()));
262 
263         Point2d p2d = new Point2d(123, 456);
264         br = new Bounds2d(p2d);
265         assertEquals("minX", 123, br.getMinX(), 0);
266         assertEquals("maxX", 123, br.getMaxX(), 0);
267         assertEquals("minY", 456, br.getMinY(), 0);
268         assertEquals("maxY", 456, br.getMaxY(), 0);
269         assertFalse("contains does not include boundaries", br.contains(p2d));
270         assertTrue("covers includes boundaries", br.covers(p2d));
271 
272         try
273         {
274             new Bounds2d((Point2d) null);
275             fail("Null parameter should have thrown a NullPointerException");
276         }
277         catch (NullPointerException npe)
278         {
279             // Ignore expected exception
280         }
281 
282         assertEquals("Size of a Bounds2d is always 4", 4, br.size());
283 
284         br = new Bounds2d(line, p2d);
285         assertEquals("minX", 1, br.getMinX(), 0);
286         assertEquals("minY", 11, br.getMinY(), 0);
287         assertEquals("maxX", 123, br.getMaxX(), 0);
288         assertEquals("maxY", 456, br.getMaxY(), 0);
289 
290         br = new Bounds2d(p2d, line);
291         assertEquals("minX", 1, br.getMinX(), 0);
292         assertEquals("minY", 11, br.getMinY(), 0);
293         assertEquals("maxX", 123, br.getMaxX(), 0);
294         assertEquals("maxY", 456, br.getMaxY(), 0);
295 
296         br = new Bounds2d(line, line);
297         assertEquals("minX", 1, br.getMinX(), 0);
298         assertEquals("minY", 11, br.getMinY(), 0);
299         assertEquals("maxX", 3, br.getMaxX(), 0);
300         assertEquals("maxY", 12, br.getMaxY(), 0);
301 
302         try
303         {
304             new Bounds2d(line, p2d, null);
305             fail("Null parameter should have thrown a NullPointerException");
306         }
307         catch (NullPointerException npe)
308         {
309             // Ignore expected exception
310         }
311 
312         try
313         {
314             new Bounds2d(new Iterator<Point2d>()
315             {
316 
317                 @Override
318                 public boolean hasNext()
319                 {
320                     return false;
321                 }
322 
323                 @Override
324                 public Point2d next()
325                 {
326                     return null;
327                 }
328             });
329             fail("iterator that yields zero points should have thrown an IllegalArgumentException");
330         }
331         catch (IllegalArgumentException iae)
332         {
333             // Ignore expected exception
334         }
335 
336     }
337 
338     /**
339      * Test various methods of a Bounds2d.
340      * @throws DrawRuntimeException when that happens uncaught; this test has failed
341      * @throws IllegalArgumentException when that happens uncaught; this test has failed
342      * @throws NullPointerException when that happens uncaught; this test has failed
343      */
344     @Test
345     @SuppressWarnings("unlikely-arg-type")
346     public void methodTest() throws NullPointerException, IllegalArgumentException, DrawRuntimeException
347     {
348         PolyLine2d l2d = new PolyLine2d(new Point2d(10, 10), new Point2d(30, -20), new Point2d(-40, 100));
349         Bounds2d br = new Bounds2d(l2d);
350         assertEquals("minX", -40, br.getMinX(), 0);
351         assertEquals("maxX", 30, br.getMaxX(), 0);
352         assertEquals("minY", -20, br.getMinY(), 0);
353         assertEquals("maxY", 100, br.getMaxY(), 0);
354 
355         Point2d midPoint = br.midPoint();
356         assertEquals("midPoint x", (br.getMinX() + br.getMaxX()) / 2, midPoint.x, 0);
357         assertEquals("midPoint y", (br.getMinY() + br.getMaxY()) / 2, midPoint.y, 0);
358         assertEquals("midPoint of bounds of point is point", midPoint, new Bounds2d(midPoint).midPoint());
359 
360         try
361         {
362             br.contains(Double.NaN, 0);
363             fail("NaN should have thrown an IllegalArgumentException");
364         }
365         catch (IllegalArgumentException iae)
366         {
367             // Ignore expected exception
368         }
369 
370         try
371         {
372             br.contains(0, Double.NaN);
373             fail("NaN should have thrown an IllegalArgumentException");
374         }
375         catch (IllegalArgumentException iae)
376         {
377             // Ignore expected exception
378         }
379 
380         assertFalse("boundingbox does not contain itself", br.contains(br));
381         Bounds2d br2 = new Bounds2d(br.getMinX() - 0.0001, br.getMaxX() + 0.0001, br.getMinY() - 0.0001, br.getMaxY() + 0.0001);
382         assertTrue("Slightly enlarged bounding box contains non-enlarged version", br2.contains(br));
383 
384         try
385         {
386             br.covers((Bounds2d) null);
387             fail("Should have thrown a NullPointerException");
388         }
389         catch (NullPointerException npe)
390         {
391             // Ignore expected exception
392         }
393 
394         try
395         {
396             br.covers(Double.NaN, 0);
397             fail("Should have thrown an IllegalArgumentException");
398         }
399         catch (IllegalArgumentException iae)
400         {
401             // Ignore expected exception
402         }
403 
404         try
405         {
406             br.covers(0, Double.NaN);
407             fail("Should have thrown an IllegalArgumentException");
408         }
409         catch (IllegalArgumentException iae)
410         {
411             // Ignore expected exception
412         }
413 
414         assertTrue("Bounds2d covers itself", br.covers(br));
415         assertFalse("Bounds2d does not cover slightly enlarged version of itself", br.covers(br2));
416         br2 = new Bounds2d(br.getMinX() + 0.0001, br.getMaxX() + 0.0001, br.getMinY() + 0.0001, br.getMaxY() + 0.0001);
417         assertFalse("Bounds2d does not cover slightly moved version of itself", br.covers(br2));
418 
419         assertFalse("Overlapping Bounds2d is not disjoint", br.disjoint(br2));
420         assertTrue("Overlapping Bounds2d is not disjoint", br.intersects(br2));
421 
422         br2 = new Bounds2d(br.getMinX() + 1000, br.getMaxX() + 1000, br.getMinY() + 1000, br.getMaxY() + 1000);
423         assertFalse("No intersection", br.intersects(br2));
424         assertTrue("Disjoint", br.disjoint(br2));
425         br2 = new Bounds2d(br.getMaxX(), br.getMaxX() + 0.0001, br.getMinY() + 0.0001, br.getMaxY() + 0.0001);
426         assertTrue("Only touching at vertical line is disjoint", br.disjoint(br2));
427         assertTrue("Only touching at vertical line is disjoint", br2.disjoint(br));
428 
429         try
430         {
431             br.intersection(null);
432             fail("Should have thrown a NullPointerException");
433         }
434         catch (NullPointerException npe)
435         {
436             // Ignore expected exception
437         }
438 
439         double[] shifts = new double[] { -200, -5, 0, 5, 200 };
440         for (double dx : shifts)
441         {
442             for (double dy : shifts)
443             {
444                 br2 = new Bounds2d(br.getMinX() + dx, br.getMaxX() + dx, br.getMinY() + dy, br.getMaxY() + dy);
445                 Bounds2d intersection = br.intersection(br2);
446                 if (Math.abs(dx) >= 200 || Math.abs(dy) >= 200)
447                 {
448                     assertNull("intersection is null", intersection);
449                 }
450                 else
451                 {
452                     assertEquals("min x", Math.max(br.getMinX(), br2.getMinX()), intersection.getMinX(), 0);
453                     assertEquals("max x", Math.min(br.getMaxX(), br2.getMaxX()), intersection.getMaxX(), 0);
454                     assertEquals("min y", Math.max(br.getMinY(), br2.getMinY()), intersection.getMinY(), 0);
455                     assertEquals("max y", Math.min(br.getMaxY(), br2.getMaxY()), intersection.getMaxY(), 0);
456                 }
457             }
458         }
459         Rectangle2D r2D = br.toRectangle2D();
460         assertEquals("x", r2D.getX(), br.getMinX(), 0);
461         assertEquals("y", r2D.getY(), br.getMinY(), 0);
462         assertEquals("w", r2D.getWidth(), br.getDeltaX(), 0.000001);
463         assertEquals("h", r2D.getHeight(), br.getDeltaY(), 0.000001);
464         assertEquals("getBounds returns this", br, br.getBounds());
465         assertNotEquals("HashCode uses minX", br.hashCode(),
466                 new Bounds2d(br.getMinX() + 1, br.getMaxX(), br.getMinY(), br.getMaxY()));
467         assertNotEquals("HashCode uses maxX", br.hashCode(),
468                 new Bounds2d(br.getMinX(), br.getMaxX() + 1, br.getMinY(), br.getMaxY()));
469         assertNotEquals("HashCode uses minY", br.hashCode(),
470                 new Bounds2d(br.getMinX(), br.getMaxX(), br.getMinY() + 1, br.getMaxY()));
471         assertNotEquals("HashCode uses maxY", br.hashCode(),
472                 new Bounds2d(br.getMinX(), br.getMaxX(), br.getMinY(), br.getMaxY() + 1));
473 
474         assertFalse("equals checks for null", br.equals(null));
475         assertFalse("equals checks for different kind of object", br.equals("string"));
476         assertFalse("equals checks minX", br.equals(new Bounds2d(br.getMinX() + 1, br.getMaxX(), br.getMinY(), br.getMaxY())));
477         assertFalse("equals checks maxX", br.equals(new Bounds2d(br.getMinX(), br.getMaxX() + 1, br.getMinY(), br.getMaxY())));
478         assertFalse("equals checks minY", br.equals(new Bounds2d(br.getMinX(), br.getMaxX(), br.getMinY() + 1, br.getMaxY())));
479         assertFalse("equals checks maxy", br.equals(new Bounds2d(br.getMinX(), br.getMaxX(), br.getMinY(), br.getMaxY() + 1)));
480         assertTrue("equals to copy of itself", br.equals(new Bounds2d(br)));
481     }
482 
483 }