View Javadoc
1   package org.opentrafficsim.core.geometry;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertFalse;
5   import static org.junit.Assert.assertTrue;
6   import static org.junit.Assert.fail;
7   
8   import java.awt.geom.Path2D;
9   import java.awt.geom.PathIterator;
10  import java.awt.geom.Rectangle2D;
11  import java.util.ArrayList;
12  import java.util.List;
13  
14  import org.junit.Test;
15  
16  import com.vividsolutions.jts.geom.Coordinate;
17  import com.vividsolutions.jts.geom.CoordinateSequence;
18  import com.vividsolutions.jts.geom.Geometry;
19  import com.vividsolutions.jts.geom.GeometryFactory;
20  import com.vividsolutions.jts.geom.LineString;
21  import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
22  
23  /**
24   * Test the OTSShape class.
25   * <p>
26   * Copyright (c) 2013-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
28   * <p>
29   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jan 9, 2017 <br>
30   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
32   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
33   */
34  public class OTSShapeTest
35  {
36      /**
37       * Test the OTSShape class.
38       * @throws OTSGeometryException if this happens uncaught; this test has failed
39       */
40      @Test
41      public final void testOTSShape() throws OTSGeometryException
42      {
43          OTSPoint3D p1 = new OTSPoint3D(1, 2, 3);
44          OTSPoint3D p2 = new OTSPoint3D(10, 2, 3);
45          OTSPoint3D p3 = new OTSPoint3D(11, 22, 3);
46  
47          OTSShape s = new OTSShape(p1, p2, p3);
48          verifyShape(s, true, p1, p2, p3);
49          s = new OTSShape(new Coordinate[] { p1.getCoordinate(), p2.getCoordinate(), p3.getCoordinate() });
50          verifyShape(s, true, p1, p2, p3);
51          GeometryFactory factory = new GeometryFactory();
52          CoordinateSequence cs =
53                  new CoordinateArraySequence(new Coordinate[] { p1.getCoordinate(), p2.getCoordinate(), p3.getCoordinate() });
54          LineString ls = new LineString(cs, factory);
55          s = new OTSShape(ls);
56          verifyShape(s, true, p1, p2, p3);
57          Geometry g = ls;
58          s = new OTSShape(g);
59          verifyShape(s, true, p1, p2, p3);
60          List<OTSPoint3D> list = new ArrayList<>();
61          list.add(p1);
62          list.add(p2);
63          list.add(p3);
64          s = new OTSShape(list);
65          verifyShape(s, true, p1, p2, p3);
66          Path2D path = new Path2D.Double();
67          path.moveTo(p1.x, p1.y);
68          path.lineTo(p2.x, p2.y);
69          path.lineTo(p3.x, p3.y);
70          s = new OTSShape(path);
71          verifyShape(s, false, p1, p2, p3);
72          Path2D shape = s.getShape();
73          PathIterator pi = shape.getPathIterator(null);
74          assertFalse(pi.isDone());
75          double[] coords = new double[6];
76          assertEquals("must be SEG_MOVETO", PathIterator.SEG_MOVETO, pi.currentSegment(coords));
77          assertEquals("x", p1.x, coords[0], 0.00001);
78          assertEquals("y", p1.y, coords[1], 0.00001);
79          pi.next();
80          assertFalse(pi.isDone());
81          assertEquals("must be SEG_LINETO", PathIterator.SEG_LINETO, pi.currentSegment(coords));
82          assertEquals("x", p2.x, coords[0], 0.00001);
83          assertEquals("y", p2.y, coords[1], 0.00001);
84          pi.next();
85          assertFalse(pi.isDone());
86          assertEquals("must be SEG_LINETO", PathIterator.SEG_LINETO, pi.currentSegment(coords));
87          assertEquals("x", p3.x, coords[0], 0.00001);
88          assertEquals("y", p3.y, coords[1], 0.00001);
89          pi.next();
90          assertFalse(pi.isDone());
91          assertEquals("must be SEG_CLOSE", PathIterator.SEG_CLOSE, pi.currentSegment(coords));
92          pi.next();
93          assertTrue(pi.isDone());
94          Path2D shape2 = s.getShape();
95          assertEquals("repeated get returns same thing", shape, shape2);
96          OTSPoint3D insidePoint = new OTSPoint3D(5, 5, 5);
97          assertTrue("shape contains inSide point", s.contains(insidePoint));
98          OTSPoint3D outsidePoint = new OTSPoint3D(-1, -2, -3);
99          assertFalse("shape does not contains unrelated point", s.contains(outsidePoint));
100         outsidePoint = new OTSPoint3D(0, 5, 5);
101         assertFalse("shape does not contains unrelated point", s.contains(outsidePoint));
102         outsidePoint = new OTSPoint3D(12, 5, 5);
103         assertFalse("shape does not contains unrelated point", s.contains(outsidePoint));
104         outsidePoint = new OTSPoint3D(5, 30, 5);
105         assertFalse("shape does not contains unrelated point", s.contains(outsidePoint));
106         Rectangle2D inRect = new Rectangle2D.Double(5, 5, 1, 1);
107         assertTrue("rectangle is inside shape", s.contains(inRect));
108         Rectangle2D partlyOutRect = new Rectangle2D.Double(5, 5, 20, 1);
109         assertFalse("Rectangle is not fully inside shape", s.contains(partlyOutRect));
110         partlyOutRect = new Rectangle2D.Double(5, 5, 1, 30);
111         assertFalse("Rectangle is not fully inside shape", s.contains(partlyOutRect));
112         assertTrue("toString result contains class name", s.toString().contains("OTSShape"));
113     }
114 
115     /**
116      * Test the intersects OTSShape method.
117      * @throws OTSGeometryException if that happens this test has failed
118      */
119     @Test
120     public final void testIntersects() throws OTSGeometryException
121     {
122         double radius = 10;
123         double cx = 5;
124         double cy = -5;
125         OTSShape reference = new OTSShape(makePolygon(cx, cy, radius, 18));
126         for (int dx = -20; dx <= 20; dx++)
127         {
128             for (int dy = -20; dy <= 20; dy++)
129             {
130                 boolean hit = true;
131                 double distance = Math.sqrt(dx * dx + dy * dy);
132                 double radius2 = 2;
133                 if (distance > radius + radius2)
134                 {
135                     hit = false;
136                 }
137                 else if (distance > radius + radius2 - 0.1)
138                 {
139                     continue; // too close to be sure
140                 }
141                 OTSShape other = new OTSShape(makePolygon(cx + dx, cy + dy, radius2, 16));
142                 if (hit)
143                 {
144                     assertTrue("shapes hit", reference.intersects(other));
145                 }
146                 else
147                 {
148                     assertFalse("shapes do not hit", reference.intersects(other));
149                 }
150             }
151         }
152         reference =
153                 new OTSShape(new OTSPoint3D[] { new OTSPoint3D(0, 0, 0), new OTSPoint3D(10, 0, 0), new OTSPoint3D(10, 10, 0) });
154         // Make shapes that overlap along the X axis
155         for (int dx = -20; dx <= 20; dx++)
156         {
157             OTSShape other = new OTSShape(
158                     new OTSPoint3D[] { new OTSPoint3D(dx, 0, 0), new OTSPoint3D(dx + 5, 0, 0), new OTSPoint3D(dx, -20, 0) });
159             boolean hit = dx >= -5 && dx <= 10;
160             if (hit)
161             {
162                 assertTrue("shapes hit", reference.intersects(other));
163             }
164             else
165             {
166                 assertFalse("shapes do not hit", reference.intersects(other));
167             }
168         }
169         // Make shapes that overlap along the Y axis
170         for (int dy = -20; dy <= 20; dy++)
171         {
172             OTSShape other = new OTSShape(
173                     new OTSPoint3D[] { new OTSPoint3D(20, dy, 0), new OTSPoint3D(10, dy, 0), new OTSPoint3D(10, dy + 10, 0) });
174             boolean hit = dy >= -10 && dy <= 10;
175             if (hit)
176             {
177                 assertTrue("shapes hit", reference.intersects(other));
178             }
179             else
180             {
181                 assertFalse("shapes do not hit", reference.intersects(other));
182             }
183         }
184         // Make vertical and horizontal box
185         OTSShape vertical = new OTSShape(new OTSPoint3D[] { new OTSPoint3D(-1, -10, 0), new OTSPoint3D(1, -10, 0),
186                 new OTSPoint3D(1, 10, 0), new OTSPoint3D(-1, 10, 0), new OTSPoint3D(-1, -10, 0) });
187         OTSShape horizontal = new OTSShape(new OTSPoint3D[] { new OTSPoint3D(-10, -1, 0), new OTSPoint3D(10, -1, 0),
188                 new OTSPoint3D(10, 1, 0), new OTSPoint3D(-10, 1, 0), new OTSPoint3D(-10, -1, 0) });
189         assertTrue("shapes hit", vertical.intersects(horizontal));
190     }
191 
192     /**
193      * Test the create and clean constructors.
194      * @throws OTSGeometryException should not happen uncaught
195      */
196     @Test
197     public final void testCleanConstructors() throws OTSGeometryException
198     {
199         try
200         {
201             OTSShape.createAndCleanOTSShape(new OTSPoint3D[] {});
202             fail("empty array should have thrown an OTSGeometryException");
203         }
204         catch (OTSGeometryException oge)
205         {
206             // Ignore expected exception
207         }
208         try
209         {
210             OTSShape.createAndCleanOTSShape(new OTSPoint3D[] { new OTSPoint3D(1, 2, 3) });
211             fail("array of one point should have thrown an OTSGeometryException");
212         }
213         catch (OTSGeometryException oge)
214         {
215             // Ignore expected exception
216         }
217         try
218         {
219             OTSShape.createAndCleanOTSShape(new OTSPoint3D[] { new OTSPoint3D(1, 2, 3), new OTSPoint3D(1, 2, 3) });
220             fail("array of two identical points should have thrown an OTSGeometryException");
221         }
222         catch (OTSGeometryException oge)
223         {
224             // Ignore expected exception
225         }
226         try
227         {
228             OTSShape.createAndCleanOTSShape(
229                     new OTSPoint3D[] { new OTSPoint3D(1, 2, 3), new OTSPoint3D(1, 2, 3), new OTSPoint3D(1, 2, 3) });
230             fail("array of three identical points should have thrown an OTSGeometryException");
231         }
232         catch (OTSGeometryException oge)
233         {
234             // Ignore expected exception
235         }
236         // FIXME: No geometry exception if thrown for two points that only differ in Z
237         OTSShape.createAndCleanOTSShape(new OTSPoint3D[] { new OTSPoint3D(1, 2, 3), new OTSPoint3D(1, 2, 1) });
238         OTSShape.createAndCleanOTSShape(
239                 new OTSPoint3D[] { new OTSPoint3D(1, 2, 3), new OTSPoint3D(1, 3, 3), new OTSPoint3D(1, 2, 3) });
240         List<OTSPoint3D> points = new ArrayList<>();
241         points.add(new OTSPoint3D(1, 2, 3));
242         points.add(new OTSPoint3D(1, 2, 3));
243         points.add(new OTSPoint3D(1, 2, 3));
244         points.add(new OTSPoint3D(1, 2, 3));
245         try
246         {
247             OTSShape.createAndCleanOTSShape(points);
248             fail("list of four identical points should have thrown an OTSGeometryException");
249         }
250         catch (OTSGeometryException oge)
251         {
252             // Ignore expected exception
253         }
254         // FIXME: do we really want this behavior?
255         assertEquals("list now has only one point", 1, points.size());
256     }
257 
258     /**
259      * Construct a list of OTSPoin3D spread out regularly over a circle.
260      * @param centerX double; center X of the circle
261      * @param centerY double; center Y of the circle
262      * @param radius double; radius of the circle
263      * @param size int; number of points in the polygon
264      * @return List&lt;OTSPoin3D&gt;; the points that lie on a regular polygon
265      * @throws OTSGeometryException if the number of points is too small or the radius is 0
266      */
267     private List<OTSPoint3D> makePolygon(final double centerX, final double centerY, final double radius, final int size)
268             throws OTSGeometryException
269     {
270         List<OTSPoint3D> points = new ArrayList<>(size);
271         for (int i = 0; i < size; i++)
272         {
273             double angle = Math.PI * 2 * i / size;
274             points.add(new OTSPoint3D(centerX + radius * Math.cos(angle), centerY + radius * Math.sin(angle)));
275         }
276         return points;
277     }
278 
279     /**
280      * Verify that an OTSShape contains exactly the given points in the given order.
281      * @param shape OTSShape; the shape to test
282      * @param verifyZ boolean; if true; also check the Z coordinates; if false; check that all Z coordinates are 0
283      * @param points OTSPoint3D...; the points to expect
284      * @throws OTSGeometryException if that happens; this test has failed
285      */
286     private void verifyShape(final OTSShape shape, final boolean verifyZ, final OTSPoint3D... points)
287             throws OTSGeometryException
288     {
289         assertEquals("shape contains correct number of points", shape.size(), points.length);
290         for (int i = 0; i < points.length; i++)
291         {
292             assertEquals("point 1 matches x", points[i].x, shape.get(i).x, 0.0001);
293             assertEquals("point 1 matches y", points[i].y, shape.get(i).y, 0.0001);
294             if (verifyZ)
295             {
296                 assertEquals("point 1 matches z", points[i].z, shape.get(i).z, 0.0001);
297             }
298             else
299             {
300                 assertEquals("point 1 z is 0", 0, shape.get(i).z, 0.0000001);
301             }
302         }
303     }
304 
305 }