View Javadoc
1   package org.djutils.draw.surface;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertNotEquals;
6   import static org.junit.Assert.assertNotNull;
7   import static org.junit.Assert.assertTrue;
8   import static org.junit.Assert.fail;
9   
10  import java.util.Iterator;
11  import java.util.NoSuchElementException;
12  
13  import org.djutils.draw.DrawRuntimeException;
14  import org.djutils.draw.bounds.Bounds3d;
15  import org.djutils.draw.point.Point3d;
16  import org.junit.Test;
17  
18  /**
19   * Surface3dTest.java; test the Surface3d class.
20   * <p>
21   * Copyright (c) 2021-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
22   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
23   * </p>
24   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
25   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
26   */
27  public class Surface3dTest
28  {
29      /**
30       * Test the constructor(s) of Surface3d.
31       */
32      @Test
33      public void testConstructors()
34      {
35          try
36          {
37              new Surface3d(null);
38              fail("null points should have thrown a NullPointerException");
39          }
40          catch (NullPointerException npe)
41          {
42              // Ignore expected exception
43          }
44  
45          try
46          {
47              new Surface3d(new Point3d[][] {});
48              fail("empty points should have thrown a DrawRuntimeException");
49          }
50          catch (DrawRuntimeException dre)
51          {
52              // Ignore expected exception
53          }
54  
55          try
56          {
57              new Surface3d(new Point3d[][] { { new Point3d(1, 2, 3) } });
58              fail("triangle with only one point should have thrown a DrawRuntimeException");
59          }
60          catch (DrawRuntimeException dre)
61          {
62              // Ignore expected exception
63          }
64  
65          try
66          {
67              new Surface3d(new Point3d[][] { { new Point3d(1, 2, 3), new Point3d(4, 5, 6) } });
68              fail("triangle with only two points should have thrown a DrawRuntimeException");
69          }
70          catch (DrawRuntimeException dre)
71          {
72              // Ignore expected exception
73          }
74  
75          try
76          {
77              new Surface3d(new Point3d[][] {
78                      { new Point3d(1, 2, 3), new Point3d(4, 5, 6), new Point3d(7, 8, 9), new Point3d(10, 11, 12) } });
79              fail("triangle with four points should have thrown a DrawRuntimeException");
80          }
81          catch (DrawRuntimeException dre)
82          {
83              // Ignore expected exception
84          }
85  
86          try
87          {
88              new Surface3d(new Point3d[][] { { new Point3d(1, 2, 3), new Point3d(4, 5, 6), new Point3d(1, 2, 3) } });
89              fail("triangle with duplicate point should have thrown a DrawRuntimeException");
90          }
91          catch (DrawRuntimeException dre)
92          {
93              // Ignore expected exception
94          }
95  
96          try
97          {
98              new Surface3d(new Point3d[][] { { new Point3d(1, 2, 3), new Point3d(1, 2, 3), new Point3d(7, 8, 9) } });
99              fail("triangle with duplicate point should have thrown a DrawRuntimeException");
100         }
101         catch (DrawRuntimeException dre)
102         {
103             // Ignore expected exception
104         }
105 
106         try
107         {
108             new Surface3d(new Point3d[][] { { new Point3d(1, 2, 3), new Point3d(4, 5, 6), new Point3d(4, 5, 6) } });
109             fail("triangle with duplicate point should have thrown a DrawRuntimeException");
110         }
111         catch (DrawRuntimeException dre)
112         {
113             // Ignore expected exception
114         }
115 
116         Point3d[][] points = new Point3d[10][];
117         for (int triangle = 0; triangle < points.length; triangle++)
118         {
119             points[triangle] = new Point3d[] { new Point3d(triangle, triangle + 1, triangle + 2),
120                     new Point3d(triangle + 10, triangle + 11, triangle + 12),
121                     new Point3d(triangle + 10, triangle + 11, triangle - 12) };
122         }
123         Surface3d surface3d = new Surface3d(points);
124         assertEquals("size", points.length * 3, surface3d.size());
125         Iterator<? extends Point3d> iterator = surface3d.getPoints();
126         assertNotNull("iterator is not null", iterator);
127         for (int triangle = 0; triangle < points.length; triangle++)
128         {
129             for (int i = 0; i < 3; i++)
130             {
131                 assertTrue("iterator should not be exhaused yet", iterator.hasNext());
132                 Object result = iterator.next();
133                 assertEquals("iterator returned correct point", points[triangle][i], result);
134             }
135         }
136         assertFalse("iterator should now be exhaused", iterator.hasNext());
137         try
138         {
139             iterator.next();
140             fail("exhausted iterator should have thrown a NoSuchElementException");
141         }
142         catch (NoSuchElementException nsee)
143         {
144             // Ignore expected exception
145         }
146 
147         iterator = new Iterator<Point3d>()
148         {
149             private int triangleIndex = 0;
150 
151             private int pointIndex = 0;
152 
153             @Override
154             public boolean hasNext()
155             {
156                 return this.triangleIndex < points.length;
157             }
158 
159             @Override
160             public Point3d next()
161             {
162                 Point3d result = points[this.triangleIndex][this.pointIndex++];
163                 if (this.pointIndex >= 3)
164                 {
165                     this.triangleIndex++;
166                     this.pointIndex = 0;
167                 }
168                 return result;
169             }
170         };
171         Bounds3d bounds = new Bounds3d(iterator);
172         assertEquals("Bounds match", bounds, surface3d.getBounds());
173 
174         // Triangulate a cube
175         Point3d[][] cubePoints = new Point3d[12][];
176         // bottom; minimal z
177         cubePoints[0] = new Point3d[] { new Point3d(-1, -1, -1), new Point3d(-1, 1, -1), new Point3d(1, 1, -1) };
178         cubePoints[1] = new Point3d[] { new Point3d(1, 1, -1), new Point3d(1, -1, -1), new Point3d(-1, -1, -1) };
179         // left; minimal x
180         cubePoints[2] = new Point3d[] { new Point3d(-1, -1, -1), new Point3d(-1, 1, -1), new Point3d(-1, 1, 1) };
181         cubePoints[3] = new Point3d[] { new Point3d(-1, 1, 1), new Point3d(-1, -1, 1), new Point3d(-1, -1, -1) };
182         // front; minimal y
183         cubePoints[4] = new Point3d[] { new Point3d(-1, -1, -1), new Point3d(1, -1, -1), new Point3d(1, -1, 1) };
184         cubePoints[5] = new Point3d[] { new Point3d(1, -1, 1), new Point3d(-1, -1, 1), new Point3d(-1, -1, -1) };
185         // top; maximal z
186         cubePoints[6] = new Point3d[] { new Point3d(-1, -1, 1), new Point3d(-1, 1, 1), new Point3d(1, 1, 1) };
187         cubePoints[7] = new Point3d[] { new Point3d(1, 1, 1), new Point3d(1, -1, 1), new Point3d(-1, -1, 1) };
188         // right; maximal x
189         cubePoints[8] = new Point3d[] { new Point3d(1, -1, -1), new Point3d(1, 1, -1), new Point3d(1, 1, 1) };
190         cubePoints[9] = new Point3d[] { new Point3d(1, 1, 1), new Point3d(1, -1, 1), new Point3d(1, -1, -1) };
191         // rear; maximal y
192         cubePoints[10] = new Point3d[] { new Point3d(-1, 1, -1), new Point3d(1, 1, -1), new Point3d(1, 1, 1) };
193         cubePoints[11] = new Point3d[] { new Point3d(1, 1, 1), new Point3d(-1, 1, 1), new Point3d(-1, 1, -1) };
194         surface3d = new Surface3d(cubePoints);
195         assertEquals("size (number of points in 12 triangles) is 36", 36, surface3d.size());
196         assertEquals("bounds", new Bounds3d(-1, 1, -1, 1, -1, 1), surface3d.getBounds());
197 
198         try
199         {
200             surface3d.project();
201             fail("should have thrown a DrawRuntimeException");
202         }
203         catch (DrawRuntimeException dre)
204         {
205             // Ignore expected exception
206         }
207 
208         assertTrue("toString results starts with class name (if not suppressed)",
209                 surface3d.toString().startsWith("Surface3d "));
210 
211         assertEquals("toString results with argument false is default", surface3d.toString(), surface3d.toString(false));
212 
213         assertTrue("toString result with argument true is substring of default result",
214                 surface3d.toString().indexOf(surface3d.toString(true)) > 5);
215 
216         assertEquals("toString result with argument \"%f\" is default", surface3d.toString(), surface3d.toString("%f"));
217     }
218 
219     /**
220      * Test the hashCode and Equals methods.
221      */
222     @SuppressWarnings("unlikely-arg-type")
223     @Test
224     public void testHashCodeAndEquals()
225     {
226         Point3d[][] referencePoints = new Point3d[][] { { new Point3d(1, 2, 3), new Point3d(4, 5, 6), new Point3d(7, 8, 9) },
227                 { new Point3d(11, 12, 13), new Point3d(14, 15, 16), new Point3d(17, 18, 19) } };
228         Surface3d referenceSurface = new Surface3d(referencePoints);
229         assertTrue("Equal to itself", referenceSurface.equals(referenceSurface));
230         assertFalse("Not equal to null", referenceSurface.equals(null));
231         assertFalse("Not equal to some other object", referenceSurface.equals("some string"));
232         // We could, in fact, patch the referencePoints array, but it is cleaner to work with a copy.
233         Point3d[][] otherPoints =
234                 java.util.Arrays.stream(referencePoints).map(el -> el.clone()).toArray($ -> referencePoints.clone());
235         assertTrue("Equal to other Surface3d created from copy of referencePoints",
236                 referenceSurface.equals(new Surface3d(otherPoints)));
237         assertEquals("hashCode is same", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
238         // Now alter one element at a time and check that hashCode changes and equals returns false
239         otherPoints[0][0] = new Point3d(1, 2, 3.5);
240         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
241                 referenceSurface.equals(new Surface3d(otherPoints)));
242         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
243         otherPoints[0][0] = new Point3d(1, 2.5, 3);
244         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
245                 referenceSurface.equals(new Surface3d(otherPoints)));
246         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
247         otherPoints[0][0] = new Point3d(1.5, 2, 3);
248         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
249                 referenceSurface.equals(new Surface3d(otherPoints)));
250         otherPoints[0][0] = new Point3d(1, 2, 3);
251         otherPoints[0][1] = new Point3d(4, 5, 6.5);
252         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
253                 referenceSurface.equals(new Surface3d(otherPoints)));
254         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
255         otherPoints[0][1] = new Point3d(4, 5.5, 6);
256         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
257                 referenceSurface.equals(new Surface3d(otherPoints)));
258         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
259         otherPoints[0][1] = new Point3d(4.5, 5, 6);
260         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
261                 referenceSurface.equals(new Surface3d(otherPoints)));
262         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
263         otherPoints[0][1] = new Point3d(4, 5, 6);
264         otherPoints[0][2] = new Point3d(7, 8, 9.5);
265         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
266                 referenceSurface.equals(new Surface3d(otherPoints)));
267         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
268         otherPoints[0][2] = new Point3d(7, 8.5, 9);
269         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
270                 referenceSurface.equals(new Surface3d(otherPoints)));
271         otherPoints[0][2] = new Point3d(7.5, 8, 9);
272         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
273                 referenceSurface.equals(new Surface3d(otherPoints)));
274         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
275         otherPoints[0][2] = new Point3d(7, 8, 9);
276         // Now we skip a few
277         otherPoints[1][2] = new Point3d(17, 18, 19.5);
278         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
279                 referenceSurface.equals(new Surface3d(otherPoints)));
280         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
281         otherPoints[1][2] = new Point3d(17, 18.5, 19);
282         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
283                 referenceSurface.equals(new Surface3d(otherPoints)));
284         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
285         otherPoints[1][2] = new Point3d(17.5, 18, 19);
286         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
287                 referenceSurface.equals(new Surface3d(otherPoints)));
288         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
289         // Now make one that uses the same set of points (and in the same order), but different indices
290         otherPoints = new Point3d[3][];
291         otherPoints[0] = referencePoints[0];
292         otherPoints[1] = referencePoints[1];
293         otherPoints[2] = referencePoints[1]; // A third triangle, using the same points as the second
294         assertFalse("Not equal to other Surface3d created from altered copy of referencePoints",
295                 referenceSurface.equals(new Surface3d(otherPoints)));
296         assertNotEquals("hashCode differs", referenceSurface.hashCode(), new Surface3d(otherPoints).hashCode());
297     }
298 
299 }