View Javadoc
1   package org.djutils.draw.line;
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.assertTrue;
7   import static org.junit.Assert.fail;
8   
9   import java.util.Iterator;
10  
11  import org.djutils.base.AngleUtil;
12  import org.djutils.draw.DrawRuntimeException;
13  import org.djutils.draw.bounds.Bounds2d;
14  import org.djutils.draw.point.Point2d;
15  import org.junit.Test;
16  
17  /**
18   * Segment2dTest.java.
19   * <p>
20   * Copyright (c) 2020-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
21   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
22   * </p>
23   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
24   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
25   */
26  public class LineSegment2dTest
27  {
28      /**
29       * Test the constructors.
30       */
31      @Test
32      public void constructorTest()
33      {
34          verifySegment("Segment from four coordinates", new LineSegment2d(1, 2, 3, 4), 1, 2, 3, 4);
35          verifySegment("Segment from two coordinates and a Point2d", new LineSegment2d(1, 2, new Point2d(3, 4)), 1, 2, 3, 4);
36          verifySegment("Segment from a Point2d and two coordinates", new LineSegment2d(new Point2d(1, 2), 3, 4), 1, 2, 3, 4);
37          verifySegment("Segment from two Point2d objects", new LineSegment2d(new Point2d(1, 2), new Point2d(3, 4)), 1, 2, 3, 4);
38  
39          try
40          {
41              new LineSegment2d(1, 2, 1, 2);
42              fail("idential start and end should have thrown a DrawRuntimeException");
43          }
44          catch (DrawRuntimeException dre)
45          {
46              // Ignore expected exception
47          }
48  
49          new LineSegment2d(1, 2, 1, 3);
50          new LineSegment2d(1, 2, 0, 2);
51      }
52  
53      /**
54       * Check that a segment has all the right values.
55       * @param description String; description of the test
56       * @param segment Segment2d; the segment
57       * @param expectedStartX double; the expected x value for the start of the segment
58       * @param expectedStartY double; the expected y value for the start of the segment
59       * @param expectedEndX double; the expected x value for the end of the segment
60       * @param expectedEndY double; the expected y value for the end of the segment
61       */
62      public void verifySegment(final String description, final LineSegment2d segment, final double expectedStartX,
63              final double expectedStartY, final double expectedEndX, final double expectedEndY)
64      {
65          assertEquals(description + " startX", expectedStartX, segment.startX, 0.0001);
66          assertEquals(description + " startY", expectedStartY, segment.startY, 0.0001);
67          assertEquals(description + " endX", expectedEndX, segment.endX, 0.0001);
68          assertEquals(description + " endY", expectedEndY, segment.endY, 0.0001);
69          assertEquals(description + " getStartPoint x", expectedStartX, segment.getStartPoint().x, 0.0001);
70          assertEquals(description + " getStartPoint y", expectedStartY, segment.getStartPoint().y, 0.0001);
71          assertEquals(description + " getEndPoint x", expectedEndX, segment.getEndPoint().x, 0.0001);
72          assertEquals(description + " getEndPoint y", expectedEndY, segment.getEndPoint().y, 0.0001);
73          assertEquals(description + " length", Math.hypot(expectedEndX - expectedStartX, expectedEndY - expectedStartY),
74                  segment.getLength(), 0.0001);
75          assertEquals(description + " size is 2", 2, segment.size());
76          Iterator<? extends Point2d> iterator = segment.getPoints();
77          assertTrue(description + " iterator has data", iterator.hasNext());
78          Point2d point = iterator.next();
79          assertEquals(description + " iterator first point x", expectedStartX, point.x, 0.0001);
80          assertEquals(description + " iterator first point y", expectedStartY, point.y, 0.0001);
81          assertTrue(description + " iterator has more data", iterator.hasNext());
82          point = iterator.next();
83          assertEquals(description + " iterator second point x", expectedEndX, point.x, 0.0001);
84          assertEquals(description + " iterator second point y", expectedEndY, point.y, 0.0001);
85          assertFalse(description + " iterator has no more data", iterator.hasNext());
86          Bounds2d bounds = segment.getBounds();
87          assertEquals(description + " bounds minX", Math.min(expectedStartX, expectedEndX), bounds.getMinX(), 0.0001);
88          assertEquals(description + " bounds maxX", Math.max(expectedStartX, expectedEndX), bounds.getMaxX(), 0.0001);
89          assertEquals(description + " bounds minY", Math.min(expectedStartY, expectedEndY), bounds.getMinY(), 0.0001);
90          assertEquals(description + " bounds maxY", Math.max(expectedStartY, expectedEndY), bounds.getMaxY(), 0.0001);
91          assertTrue(description + " toString returns something descriptive", segment.toString().startsWith("LineSegment2d "));
92          assertTrue(description + " toString can suppress the class name",
93                  segment.toString().indexOf(segment.toString(true)) > 0);
94      }
95  
96      /**
97       * Test the getLocation methods.
98       */
99      @Test
100     public void locationTest()
101     {
102         Point2d startPoint = new Point2d(3, 4);
103         Point2d endPoint = new Point2d(9, 20);
104         LineSegment2d segment = new LineSegment2d(startPoint, endPoint);
105         try
106         {
107             segment.getLocation(Double.NaN);
108             fail("NaN position should have thrown a DrawRuntimeException");
109         }
110         catch (DrawRuntimeException dre)
111         {
112             // Ignore expected exception
113         }
114 
115         try
116         {
117             segment.getLocationExtended(Double.POSITIVE_INFINITY);
118             fail("Infinity position should have thrown a DrawRuntimeException");
119         }
120         catch (DrawRuntimeException dre)
121         {
122             // Ignore expected exception
123         }
124 
125         try
126         {
127             segment.getLocationExtended(Double.NEGATIVE_INFINITY);
128             fail("Infinity position should have thrown a DrawRuntimeException");
129         }
130         catch (DrawRuntimeException dre)
131         {
132             // Ignore expected exception
133         }
134 
135         for (double position : new double[] { -3, -0.5, 0, 1, 10, 100 })
136         {
137             if (position < 0 || position > segment.getLength())
138             {
139                 try
140                 {
141                     segment.getLocation(position);
142                     fail("position out of bounds should have thrown a DrawRuntimeException");
143                 }
144                 catch (DrawRuntimeException dre)
145                 {
146                     // Ignore expected exception
147                 }
148             }
149             else
150             {
151                 Ray2d ray = segment.getLocation(position);
152                 assertEquals("distance from start point", position, ray.distance(startPoint), 0.0001);
153                 assertEquals("distance from end point", segment.getLength() - position, ray.distance(endPoint), 0.0001);
154                 assertEquals("direction of ray", startPoint.directionTo(endPoint), ray.phi, 0.0001);
155             }
156             Ray2d ray = segment.getLocationExtended(position);
157             assertEquals("distance from start point", Math.abs(position), ray.distance(startPoint), 0.0001);
158             assertEquals("distance from end point", Math.abs(segment.getLength() - position), ray.distance(endPoint), 0.0001);
159             assertEquals("direction of ray", startPoint.directionTo(endPoint), ray.phi, 0.0001);
160         }
161     }
162 
163     /**
164      * Test the closestPointOnSegment method.
165      */
166     @Test
167     public void closestPointOnSegmentTest()
168     {
169         LineSegment2d segment = new LineSegment2d(1, 2, 20, 10);
170         try
171         {
172             segment.closestPointOnSegment(null);
173             fail("Null for point should have thrown a NullPointerException");
174         }
175         catch (NullPointerException npe)
176         {
177             // Ignore expected exception
178         }
179 
180         Point2d result = segment.closestPointOnSegment(new Point2d(1, 0));
181         assertEquals("result is start point", segment.startX, result.x, 0);
182         assertEquals("result is start point", segment.startY, result.y, 0);
183         result = segment.closestPointOnSegment(new Point2d(0, 2));
184         assertEquals("result is start point", segment.startX, result.x, 0);
185         assertEquals("result is start point", segment.startY, result.y, 0);
186         result = segment.closestPointOnSegment(new Point2d(1, 2));
187         assertEquals("result is start point", segment.startX, result.x, 0);
188         assertEquals("result is start point", segment.startY, result.y, 0);
189 
190         Point2d projectingPoint = new Point2d(10, 10);
191         result = segment.closestPointOnSegment(projectingPoint); // Projects at a point along the segment
192         double distanceFromStart = result.distance(segment.getStartPoint());
193         assertTrue("distance from start is > 0", distanceFromStart > 0);
194         double distanceToEnd = result.distance(segment.getEndPoint());
195         assertTrue("distance to end point is > 0", distanceToEnd > 0);
196         assertEquals("sum of distances is length of segment", segment.getLength(), distanceFromStart + distanceToEnd, 0.0001);
197         // Angle startPoint-result-test-projectingPoint should be 90 degrees
198         double angle = segment.getStartPoint().directionTo(segment.getEndPoint()) - result.directionTo(projectingPoint);
199         assertEquals("angle should be about 90 degrees", Math.PI / 2, Math.abs(AngleUtil.normalizeAroundZero(angle)), 0.0001);
200 
201         result = segment.closestPointOnSegment(new Point2d(21, 10));
202         assertEquals("result is end point", segment.endX, result.x, 0);
203         assertEquals("result is end point", segment.endY, result.y, 0);
204         result = segment.closestPointOnSegment(new Point2d(20, 11));
205         assertEquals("result is end point", segment.endX, result.x, 0);
206         assertEquals("result is end point", segment.endY, result.y, 0);
207         result = segment.closestPointOnSegment(new Point2d(20, 10));
208         assertEquals("result is end point", segment.endX, result.x, 0);
209         assertEquals("result is end point", segment.endY, result.y, 0);
210     }
211 
212     /**
213      * Test the project methods.
214      */
215     @Test
216     public void testProject()
217     {
218         LineSegment2d segment = new LineSegment2d(1, 2, 20, 10);
219         assertTrue("projects outside", Double.isNaN(segment.projectOrthogonalFractional(new Point2d(1, 1))));
220         assertTrue("projects before start", segment.projectOrthogonalFractionalExtended(new Point2d(1, 1)) < 0);
221         assertEquals("projects at -2", -2,
222                 segment.projectOrthogonalFractionalExtended(new Point2d(1 - 19 - 19 + 8, 2 - 8 - 8 - 19)), 0.0001);
223         assertEquals("point near half way (not on segment) project at about half way", 0.5,
224                 segment.projectOrthogonalFractional(new Point2d(11, 1)), 0.1);
225         assertTrue("projects outside", Double.isNaN(segment.projectOrthogonalFractional(new Point2d(25, 15))));
226         assertTrue("projects after end", segment.projectOrthogonalFractionalExtended(new Point2d(25, 15)) > 1);
227         assertEquals("projects at 2", 2,
228                 segment.projectOrthogonalFractionalExtended(new Point2d(1 + 19 + 19 - 8, 2 + 8 + 8 + 19)), 0.0001);
229     }
230 
231     /**
232      * Test the toExcel method.
233      * @throws NumberFormatException if that happens, this test has failed
234      */
235     @Test
236     public void testToExcel() throws NumberFormatException
237     {
238         LineSegment2d segment = new LineSegment2d(1, 2, 20, 10);
239         String result = segment.toExcel();
240         String[] lines = result.split("\n");
241         assertEquals("result is two lines", 2, lines.length);
242         for (int lineNo = 0; lineNo < lines.length; lineNo++)
243         {
244             String[] fields = lines[lineNo].trim().split("\t");
245             assertEquals("Line consists of two fields", 2, fields.length);
246             for (int fieldNo = 0; fieldNo < fields.length; fieldNo++)
247             {
248                 double value = Double.parseDouble(fields[fieldNo]);
249                 double expectedValue = lineNo == 0 ? (fieldNo == 0 ? segment.startX : segment.startY)
250                         : (fieldNo == 0 ? segment.endX : segment.endY);
251                 assertEquals("field contains the correct value", expectedValue, value, 0.0001);
252             }
253         }
254     }
255 
256     /**
257      * Test the equals and hasCode methods.
258      */
259     @Test
260     public void equalsAndHashCodeTest()
261     {
262         LineSegment2d segment = new LineSegment2d(1, 2, -3, -4);
263         assertEquals("equal to itself", segment, segment);
264         assertNotEquals("not equal to null", segment, null);
265         assertNotEquals("not equal to a totally different object", segment, "no way");
266         assertNotEquals("not equal to line segment with different start x", segment, new LineSegment2d(2, 2, -3, -4));
267         assertNotEquals("not equal to line segment with different start y", segment, new LineSegment2d(1, 3, -3, -4));
268         assertNotEquals("not equal to line segment with different end x", segment, new LineSegment2d(1, 2, -4, -4));
269         assertNotEquals("not equal to line segment with different end y", segment, new LineSegment2d(1, 2, -3, -5));
270         assertEquals("equal to another line segment with same start and end x, y", segment, new LineSegment2d(1, 2, -3, -4));
271 
272         assertNotEquals("hashCode depends on start x", segment.hashCode(), new LineSegment2d(2, 2, -3, -4));
273         assertNotEquals("hashCode depends on start y", segment.hashCode(), new LineSegment2d(1, 3, -3, -4));
274         assertNotEquals("hashCode depends on end x", segment.hashCode(), new LineSegment2d(1, 2, -4, -4));
275         assertNotEquals("hashCode depends on end y", segment.hashCode(), new LineSegment2d(1, 2, -4, -5));
276     }
277 
278 }