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.util.ArrayList;
11  import java.util.Collection;
12  import java.util.Iterator;
13  
14  import org.djutils.draw.DrawRuntimeException;
15  import org.djutils.draw.Drawable3d;
16  import org.djutils.draw.line.LineSegment3d;
17  import org.djutils.draw.line.PolyLine3d;
18  import org.djutils.draw.point.Point3d;
19  import org.junit.Test;
20  
21  /**
22   * Bounds3dText.java.
23   * <p>
24   * Copyright (c) 2020-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
25   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
26   * </p>
27   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
28   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
29   */
30  public class Bounds3dTest
31  {
32  
33      /**
34       * Test the bounding box constructor.
35       * @throws DrawRuntimeException if that happens uncaught; this test has failed
36       * @throws IllegalArgumentException on unexpected error
37       */
38      @Test
39      public void constructorTest() throws IllegalArgumentException, DrawRuntimeException
40      {
41          try
42          {
43              new Bounds3d(Double.NaN, 0, 0, 0, 0, 0);
44              fail("Nan should have thrown an IllegalArgumentException");
45          }
46          catch (IllegalArgumentException iae)
47          {
48              // Ignore expected exception
49          }
50  
51          try
52          {
53              new Bounds3d(0, Double.NaN, 0, 0, 0, 0);
54              fail("Nan should have thrown an IllegalArgumentException");
55          }
56          catch (IllegalArgumentException iae)
57          {
58              // Ignore expected exception
59          }
60  
61          try
62          {
63              new Bounds3d(0, 0, Double.NaN, 0, 0, 0);
64              fail("Nan should have thrown an IllegalArgumentException");
65          }
66          catch (IllegalArgumentException iae)
67          {
68              // Ignore expected exception
69          }
70  
71          try
72          {
73              new Bounds3d(0, 0, 0, Double.NaN, 0, 0);
74              fail("Nan should have thrown an IllegalArgumentException");
75          }
76          catch (IllegalArgumentException iae)
77          {
78              // Ignore expected exception
79          }
80  
81          try
82          {
83              new Bounds3d(0, 0, 0, 0, Double.NaN, 0);
84              fail("Nan should have thrown an IllegalArgumentException");
85          }
86          catch (IllegalArgumentException iae)
87          {
88              // Ignore expected exception
89          }
90  
91          try
92          {
93              new Bounds3d(0, 0, 0, 0, 0, Double.NaN);
94              fail("Nan should have thrown an IllegalArgumentException");
95          }
96          catch (IllegalArgumentException iae)
97          {
98              // Ignore expected exception
99          }
100 
101         try
102         {
103             new Bounds3d(2, -2, 0, 0, 0, 0);
104             fail("Negative x-range should have thrown an IllegalArgumentException");
105         }
106         catch (IllegalArgumentException iae)
107         {
108             // Ignore expected exception
109         }
110 
111         try
112         {
113             new Bounds3d(0, 0, 2, -2, 0, 0);
114             fail("Negative y-range should have thrown an IllegalArgumentException");
115         }
116         catch (IllegalArgumentException iae)
117         {
118             // Ignore expected exception
119         }
120 
121         try
122         {
123             new Bounds3d(0, 0, 0, 0, 2, -2);
124             fail("Negative z-range should have thrown an IllegalArgumentException");
125         }
126         catch (IllegalArgumentException iae)
127         {
128             // Ignore expected exception
129         }
130 
131         try
132         {
133             new Bounds3d(new Drawable3d[] {});
134             fail("Empty array should have thrown an IllegalArgumentException");
135         }
136         catch (IllegalArgumentException iae)
137         {
138             // Ignore expected exception
139         }
140 
141         Bounds3d bb = new Bounds3d(1, 2, 3, 6, 5, 10);
142         assertEquals("minX", 1, bb.getMinX(), 0);
143         assertEquals("maxX", 2, bb.getMaxX(), 0);
144         assertEquals("minY", 3, bb.getMinY(), 0);
145         assertEquals("maxY", 6, bb.getMaxY(), 0);
146         assertEquals("minZ", 5, bb.getMinZ(), 0);
147         assertEquals("maxZ", 10, bb.getMaxZ(), 0);
148 
149         try
150         {
151             new Bounds3d(Double.NaN, 0, 0);
152             fail("Nan should have thrown an IllegalArgumentException");
153         }
154         catch (IllegalArgumentException iae)
155         {
156             // Ignore expected exception
157         }
158 
159         try
160         {
161             new Bounds3d(0, Double.NaN, 0);
162             fail("Nan should have thrown an IllegalArgumentException");
163         }
164         catch (IllegalArgumentException iae)
165         {
166             // Ignore expected exception
167         }
168 
169         try
170         {
171             new Bounds3d(0, 0, Double.NaN);
172             fail("Nan should have thrown an IllegalArgumentException");
173         }
174         catch (IllegalArgumentException iae)
175         {
176             // Ignore expected exception
177         }
178 
179         try
180         {
181             new Bounds3d(-3, 0, 0);
182             fail("Negative x-range should have thrown an IllegalArgumentException");
183         }
184         catch (IllegalArgumentException iae)
185         {
186             // Ignore expected exception
187         }
188 
189         try
190         {
191             new Bounds3d(0, -3, 0);
192             fail("Negative y-range should have thrown an IllegalArgumentException");
193         }
194         catch (IllegalArgumentException iae)
195         {
196             // Ignore expected exception
197         }
198 
199         try
200         {
201             new Bounds3d(0, 0, -3);
202             fail("Negative z-range should have thrown an IllegalArgumentException");
203         }
204         catch (IllegalArgumentException iae)
205         {
206             // Ignore expected exception
207         }
208 
209         bb = new Bounds3d(20, 30, 40);
210         assertEquals("minX", -10, bb.getMinX(), 0);
211         assertEquals("maxX", 10, bb.getMaxX(), 0);
212         assertEquals("minY", -15, bb.getMinY(), 0);
213         assertEquals("maxY", 15, bb.getMaxY(), 0);
214         assertEquals("minZ", -20, bb.getMinZ(), 0);
215         assertEquals("maxZ", 20, bb.getMaxZ(), 0);
216         assertEquals("deltaX", 20, bb.getDeltaX(), 0);
217         assertEquals("deltaY", 30, bb.getDeltaY(), 0);
218         assertEquals("deltaZ", 40, bb.getDeltaZ(), 0);
219         assertEquals("volume", 20 * 30 * 40, bb.getVolume(), 0);
220         assertFalse("contains does not include boundaries", bb.contains(-10, 0, 0));
221         assertFalse("contains does not include boundaries", bb.contains(10, 0, 0));
222         assertFalse("contains does not include boundaries", bb.contains(0, -15, 0));
223         assertFalse("contains does not include boundaries", bb.contains(0, 15, 0));
224         assertFalse("contains does not include boundaries", bb.contains(0, 0, -20));
225         assertFalse("contains does not include boundaries", bb.contains(0, 0, 20));
226         assertTrue("contains", bb.contains(-0.999, 0, 0));
227         assertTrue("contains", bb.contains(0.999, 0, 0));
228         assertTrue("contains", bb.contains(0, -14.999, 0));
229         assertTrue("contains", bb.contains(0, 14.999, 0));
230         assertTrue("contains", bb.contains(0, 0, -19.999));
231         assertTrue("contains", bb.contains(0, 0, 19.999));
232         assertTrue("covers includes boundaries", bb.covers(-10, 0, 0));
233         assertTrue("covers includes boundaries", bb.covers(10, 0, 0));
234         assertTrue("covers includes boundaries", bb.covers(0, -15, 0));
235         assertTrue("covers includes boundaries", bb.covers(0, 15, 0));
236         assertTrue("covers includes boundaries", bb.covers(0, 0, -20));
237         assertTrue("covers includes boundaries", bb.covers(0, 0, 20));
238         assertFalse("covers", bb.covers(-10.001, 0, 0));
239         assertFalse("covers", bb.covers(10.001, 0, 0));
240         assertFalse("covers", bb.covers(0, -15.001, 0));
241         assertFalse("covers", bb.covers(0, 15.001, 0));
242         assertFalse("covers", bb.covers(0, 0, -20.001));
243         assertFalse("covers", bb.covers(0, 0, 20.001));
244 
245         Collection<Drawable3d> drawable3dCollection = new ArrayList<>();
246         try
247         {
248             new Bounds3d(drawable3dCollection);
249             fail("Empty point collection should have thrown an IllegalArgumentException");
250         }
251         catch (IllegalArgumentException iae)
252         {
253             // Ignore expected exception
254         }
255 
256         drawable3dCollection.add(new Point3d(10, 20, 30));
257         bb = new Bounds3d(drawable3dCollection);
258         assertEquals("minX", 10, bb.getMinX(), 0);
259         assertEquals("maxX", 10, bb.getMaxX(), 0);
260         assertEquals("minY", 20, bb.getMinY(), 0);
261         assertEquals("maxY", 20, bb.getMaxY(), 0);
262         assertEquals("minZ", 30, bb.getMinZ(), 0);
263         assertEquals("maxZ", 30, bb.getMaxZ(), 0);
264 
265         drawable3dCollection.add(new Point3d(-5, -6, -7));
266         bb = new Bounds3d(drawable3dCollection);
267         assertEquals("minX", -5, bb.getMinX(), 0);
268         assertEquals("maxX", 10, bb.getMaxX(), 0);
269         assertEquals("minY", -6, bb.getMinY(), 0);
270         assertEquals("maxY", 20, bb.getMaxY(), 0);
271         assertEquals("minZ", -7, bb.getMinZ(), 0);
272         assertEquals("maxZ", 30, bb.getMaxZ(), 0);
273 
274         drawable3dCollection.add(new LineSegment3d(20, 30, 40, 40, 50, 60));
275         bb = new Bounds3d(drawable3dCollection);
276         assertEquals("minX", -5, bb.getMinX(), 0);
277         assertEquals("maxX", 40, bb.getMaxX(), 0);
278         assertEquals("minY", -6, bb.getMinY(), 0);
279         assertEquals("maxY", 50, bb.getMaxY(), 0);
280         assertEquals("minZ", -7, bb.getMinZ(), 0);
281         assertEquals("maxZ", 60, bb.getMaxZ(), 0);
282 
283         assertTrue("toString returns something descriptive", bb.toString().startsWith("Bounds3d "));
284         assertEquals("toString with false argument produces same as toString with no argument", bb.toString(),
285                 bb.toString(false));
286         assertTrue("toString with true argument produces rhs of toString with no argument",
287                 bb.toString().indexOf(bb.toString(true)) > 0);
288 
289         drawable3dCollection.add(new Point3d(40, 50, 60));
290         // This collection is an ArrayList, so the elements are stored in the order in which they were added
291         bb = new Bounds3d(drawable3dCollection);
292         assertEquals("minX", -5, bb.getMinX(), 0);
293         assertEquals("maxX", 40, bb.getMaxX(), 0);
294         assertEquals("minY", -6, bb.getMinY(), 0);
295         assertEquals("maxY", 50, bb.getMaxY(), 0);
296         assertEquals("minZ", -7, bb.getMinZ(), 0);
297         assertEquals("maxZ", 60, bb.getMaxZ(), 0);
298 
299         bb = new Bounds3d(drawable3dCollection.toArray((new Drawable3d[0])));
300         assertEquals("minX", -5, bb.getMinX(), 0);
301         assertEquals("maxX", 40, bb.getMaxX(), 0);
302         assertEquals("minY", -6, bb.getMinY(), 0);
303         assertEquals("maxY", 50, bb.getMaxY(), 0);
304         assertEquals("minZ", -7, bb.getMinZ(), 0);
305         assertEquals("maxZ", 60, bb.getMaxZ(), 0);
306 
307         drawable3dCollection.add(null);
308         try
309         {
310             new Bounds3d(drawable3dCollection);
311             fail("null element in collection should have thrown an NullPointerException");
312         }
313         catch (NullPointerException npe)
314         {
315             // Ignore expected exception
316         }
317 
318         Bounds3d bb2 = new Bounds3d(-100, -90, -100, -90, -100, -90);
319         assertNull("empty bounding box", bb.intersection(bb2));
320 
321         PolyLine3d line = new PolyLine3d(new Point3d(1, 12, 23), new Point3d(3, 12, 21), new Point3d(2, 11, 23));
322         bb = new Bounds3d(line);
323         assertEquals("minX", 1, bb.getMinX(), 0);
324         assertEquals("minY", 11, bb.getMinY(), 0);
325         assertEquals("minZ", 21, bb.getMinZ(), 0);
326         assertEquals("maxX", 3, bb.getMaxX(), 0);
327         assertEquals("maxY", 12, bb.getMaxY(), 0);
328         assertEquals("maxZ", 23, bb.getMaxZ(), 0);
329 
330         assertEquals("bounding box of reversed line", bb, new Bounds3d(line.reverse()));
331 
332         Point3d p3d = new Point3d(123, 456, 789);
333         bb = new Bounds3d(p3d);
334         assertEquals("minX", 123, bb.getMinX(), 0);
335         assertEquals("maxX", 123, bb.getMaxX(), 0);
336         assertEquals("minY", 456, bb.getMinY(), 0);
337         assertEquals("maxY", 456, bb.getMaxY(), 0);
338         assertEquals("minZ", 789, bb.getMinZ(), 0);
339         assertEquals("maxZ", 789, bb.getMaxZ(), 0);
340         assertFalse("contains does not include boundaries", bb.contains(p3d));
341         assertTrue("covers includes boundaries", bb.covers(p3d));
342 
343         try
344         {
345             new Bounds3d((Point3d) null);
346             fail("Null parameter should have thrown a NullPointerException");
347         }
348         catch (NullPointerException npe)
349         {
350             // Ignore expected exception
351         }
352 
353         assertEquals("size of a Bounds3d is always 8", 8, bb.size());
354 
355         bb = new Bounds3d(line, p3d);
356         assertEquals("minX", 1, bb.getMinX(), 0);
357         assertEquals("minY", 11, bb.getMinY(), 0);
358         assertEquals("maxX", 123, bb.getMaxX(), 0);
359         assertEquals("maxY", 456, bb.getMaxY(), 0);
360         assertEquals("minZ", 21, bb.getMinZ(), 0);
361         assertEquals("maxZ", 789, bb.getMaxZ(), 0);
362 
363         bb = new Bounds3d(p3d, line);
364         assertEquals("minX", 1, bb.getMinX(), 0);
365         assertEquals("minY", 11, bb.getMinY(), 0);
366         assertEquals("maxX", 123, bb.getMaxX(), 0);
367         assertEquals("maxY", 456, bb.getMaxY(), 0);
368         assertEquals("minZ", 21, bb.getMinZ(), 0);
369         assertEquals("maxZ", 789, bb.getMaxZ(), 0);
370 
371         bb = new Bounds3d(line, line);
372         assertEquals("minX", 1, bb.getMinX(), 0);
373         assertEquals("minY", 11, bb.getMinY(), 0);
374         assertEquals("maxX", 3, bb.getMaxX(), 0);
375         assertEquals("maxY", 12, bb.getMaxY(), 0);
376         assertEquals("minZ", 21, bb.getMinZ(), 0);
377         assertEquals("maxZ", 23, bb.getMaxZ(), 0);
378 
379         try
380         {
381             new Bounds3d(line, p3d, null);
382             fail("Null parameter should have thrown a NullPointerException");
383         }
384         catch (NullPointerException npe)
385         {
386             // Ignore expected exception
387         }
388 
389         try
390         {
391             new Bounds3d(new Iterator<Point3d>()
392             {
393 
394                 @Override
395                 public boolean hasNext()
396                 {
397                     return false;
398                 }
399 
400                 @Override
401                 public Point3d next()
402                 {
403                     return null;
404                 }
405             });
406             fail("iterator that yields zero points should have thrown an IllegalArgumentException");
407         }
408         catch (IllegalArgumentException iae)
409         {
410             // Ignore expected exception
411         }
412 
413     }
414 
415     /**
416      * Test various methods of a Bounds2d.
417      * @throws DrawRuntimeException when that happens uncaught; this test has failed
418      * @throws IllegalArgumentException when that happens uncaught; this test has failed
419      * @throws NullPointerException when that happens uncaught; this test has failed
420      */
421     @Test
422     public void methodTest() throws NullPointerException, IllegalArgumentException, DrawRuntimeException
423     {
424         PolyLine3d l3d = new PolyLine3d(new Point3d(10, 10, 10), new Point3d(30, -20, 40), new Point3d(-40, 100, 0));
425         Bounds3d bb = new Bounds3d(l3d);
426         assertEquals("minX", -40, bb.getMinX(), 0);
427         assertEquals("maxX", 30, bb.getMaxX(), 0);
428         assertEquals("minY", -20, bb.getMinY(), 0);
429         assertEquals("maxY", 100, bb.getMaxY(), 0);
430         assertEquals("minZ", 0, bb.getMinZ(), 0);
431         assertEquals("maxZ", 40, bb.getMaxZ(), 0);
432 
433         Point3d midPoint = bb.midPoint();
434         assertEquals("midPoint x", (bb.getMinX() + bb.getMaxX()) / 2, midPoint.x, 0);
435         assertEquals("midPoint y", (bb.getMinY() + bb.getMaxY()) / 2, midPoint.y, 0);
436         assertEquals("midPoint z", (bb.getMinZ() + bb.getMaxZ()) / 2, midPoint.z, 0);
437         assertEquals("midPoint of bounds of point is point", midPoint, new Bounds3d(midPoint).midPoint());
438 
439         try
440         {
441             bb.contains(Double.NaN, 0, 0);
442             fail("NaN should have thrown an IllegalArgumentException");
443         }
444         catch (IllegalArgumentException iae)
445         {
446             // Ignore expected exception
447         }
448 
449         try
450         {
451             bb.contains(0, Double.NaN, 0);
452             fail("NaN should have thrown an IllegalArgumentException");
453         }
454         catch (IllegalArgumentException iae)
455         {
456             // Ignore expected exception
457         }
458 
459         try
460         {
461             bb.contains(0, 0, Double.NaN);
462             fail("NaN should have thrown an IllegalArgumentException");
463         }
464         catch (IllegalArgumentException iae)
465         {
466             // Ignore expected exception
467         }
468 
469         assertFalse("boundingbox does not contain itself", bb.contains(bb));
470         Bounds3d bb2 = new Bounds3d(bb.getMinX() - 0.0001, bb.getMaxX() + 0.0001, bb.getMinY() - 0.0001, bb.getMaxY() + 0.0001,
471                 bb.getMinZ() - 0.0001, bb.getMaxZ() + 0.0001);
472         assertTrue("Slightly enlarged bounding box contains non-enlarged version", bb2.contains(bb));
473 
474         try
475         {
476             bb.covers((Bounds3d) null);
477             fail("Should have thrown a NullPointerException");
478         }
479         catch (NullPointerException npe)
480         {
481             // Ignore expected exception
482         }
483 
484         try
485         {
486             bb.covers(Double.NaN, 0, 0);
487             fail("Should have thrown an IllegalArgumentException");
488         }
489         catch (IllegalArgumentException iae)
490         {
491             // Ignore expected exception
492         }
493 
494         try
495         {
496             bb.covers(0, Double.NaN, 0);
497             fail("Should have thrown an IllegalArgumentException");
498         }
499         catch (IllegalArgumentException iae)
500         {
501             // Ignore expected exception
502         }
503 
504         try
505         {
506             bb.covers(0, 0, Double.NaN);
507             fail("Should have thrown an IllegalArgumentException");
508         }
509         catch (IllegalArgumentException iae)
510         {
511             // Ignore expected exception
512         }
513 
514         assertTrue("Bounds2d covers itself", bb.covers(bb));
515         assertFalse("Bounds2d does not cover slightly enlarged version of itself", bb.covers(bb2));
516         bb2 = new Bounds3d(bb.getMinX() + 0.0001, bb.getMaxX() + 0.0001, bb.getMinY() + 0.0001, bb.getMaxY() + 0.0001,
517                 bb.getMinZ() + 0.0001, bb.getMaxZ() + 0.0001);
518         assertFalse("Bounds2d does not cover slightly moved version of itself", bb.covers(bb2));
519 
520         assertFalse("Overlapping Bounds2d is not disjoint", bb.disjoint(bb2));
521         assertTrue("Overlapping Bounds2d is not disjoint", bb.intersects(bb2));
522         bb2 = new Bounds3d(bb.getMinX() + 1000, bb.getMaxX() + 1000, bb.getMinY() + 1000, bb.getMaxY() + 1000,
523                 bb.getMinZ() + 1000, bb.getMaxZ() + 1000);
524         assertFalse("No intersection", bb.intersects(bb2));
525         assertTrue("Disjoint", bb.disjoint(bb2));
526         bb2 = new Bounds3d(bb.getMaxX(), bb.getMaxX() + 0.0001, bb.getMinY() + 0.0001, bb.getMaxY() + 0.0001,
527                 bb.getMinZ() + 0.0001, bb.getMaxZ() + 0.0001);
528         assertTrue("Only touching at vertical line is disjoint", bb.disjoint(bb2));
529         assertTrue("Only touching at vertical line is disjoint", bb2.disjoint(bb));
530 
531         try
532         {
533             bb.intersection(null);
534             fail("Should have thrown a NullPointerException");
535         }
536         catch (NullPointerException npe)
537         {
538             // Ignore expected exception
539         }
540 
541         double[] shifts = new double[] { -200, -5, 0, 5, 200 };
542         for (double dx : shifts)
543         {
544             for (double dy : shifts)
545             {
546                 for (double dz : shifts)
547                 {
548                     bb2 = new Bounds3d(bb.getMinX() + dx, bb.getMaxX() + dx, bb.getMinY() + dy, bb.getMaxY() + dy,
549                             bb.getMinZ() + dz, bb.getMaxZ() + dz);
550                     Bounds3d intersection = bb.intersection(bb2);
551                     if (Math.abs(dx) >= 200 || Math.abs(dy) >= 200 || Math.abs(dz) >= 200)
552                     {
553                         assertNull("intersection is null", intersection);
554                     }
555                     else
556                     {
557                         assertEquals("min x", Math.max(bb.getMinX(), bb2.getMinX()), intersection.getMinX(), 0);
558                         assertEquals("max x", Math.min(bb.getMaxX(), bb2.getMaxX()), intersection.getMaxX(), 0);
559                         assertEquals("min y", Math.max(bb.getMinY(), bb2.getMinY()), intersection.getMinY(), 0);
560                         assertEquals("max y", Math.min(bb.getMaxY(), bb2.getMaxY()), intersection.getMaxY(), 0);
561                         assertEquals("min z", Math.max(bb.getMinZ(), bb2.getMinZ()), intersection.getMinZ(), 0);
562                         assertEquals("max z", Math.min(bb.getMaxZ(), bb2.getMaxZ()), intersection.getMaxZ(), 0);
563                     }
564                 }
565             }
566         }
567         assertEquals("getBounds returns this", bb, bb.getBounds());
568         assertNotEquals("HashCode uses minX", bb.hashCode(),
569                 new Bounds3d(bb.getMinX() + 1, bb.getMaxX(), bb.getMinY(), bb.getMaxY(), bb.getMinZ(), bb.getMaxZ()));
570         assertNotEquals("HashCode uses maxX", bb.hashCode(),
571                 new Bounds3d(bb.getMinX(), bb.getMaxX() + 1, bb.getMinY(), bb.getMaxY(), bb.getMinZ(), bb.getMaxZ()));
572         assertNotEquals("HashCode uses minY", bb.hashCode(),
573                 new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY() + 1, bb.getMaxY(), bb.getMinZ(), bb.getMaxZ()));
574         assertNotEquals("HashCode uses maxY", bb.hashCode(),
575                 new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY() + 1, bb.getMinZ(), bb.getMaxZ()));
576         assertNotEquals("HashCode uses minZ", bb.hashCode(),
577                 new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY(), bb.getMinZ() + 1, bb.getMaxZ()));
578         assertNotEquals("HashCode uses maxZ", bb.hashCode(),
579                 new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY() + 1, bb.getMinZ(), bb.getMaxZ() + 1));
580 
581         assertFalse("equals checks for null", bb.equals(null));
582         assertFalse("equals checks for different kind of object", bb.equals("string"));
583         assertFalse("equals checks minX", bb
584                 .equals(new Bounds3d(bb.getMinX() + 1, bb.getMaxX(), bb.getMinY(), bb.getMaxY(), bb.getMinZ(), bb.getMaxZ())));
585         assertFalse("equals checks maxX", bb
586                 .equals(new Bounds3d(bb.getMinX(), bb.getMaxX() + 1, bb.getMinY(), bb.getMaxY(), bb.getMinZ(), bb.getMaxZ())));
587         assertFalse("equals checks minY", bb
588                 .equals(new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY() + 1, bb.getMaxY(), bb.getMinZ(), bb.getMaxZ())));
589         assertFalse("equals checks maxy", bb
590                 .equals(new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY() + 1, bb.getMinZ(), bb.getMaxZ())));
591         assertFalse("equals checks minZ", bb
592                 .equals(new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY(), bb.getMinZ() + 1, bb.getMaxZ())));
593         assertFalse("equals checks maxZ", bb
594                 .equals(new Bounds3d(bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY(), bb.getMinZ(), bb.getMaxZ() + 1)));
595         assertTrue("equals to copy of itself", bb.equals(new Bounds3d(bb)));
596 
597         Bounds2d projection = bb.project();
598         assertEquals("projection minX", projection.getMinX(), bb.getMinX(), 0);
599         assertEquals("projection maxX", projection.getMaxX(), bb.getMaxX(), 0);
600         assertEquals("projection minY", projection.getMinY(), bb.getMinY(), 0);
601         assertEquals("projection maxY", projection.getMaxY(), bb.getMaxY(), 0);
602     }
603 
604 }