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
19
20
21
22
23
24
25
26 public class LineSegment2dTest
27 {
28
29
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
47 }
48
49 new LineSegment2d(1, 2, 1, 3);
50 new LineSegment2d(1, 2, 0, 2);
51 }
52
53
54
55
56
57
58
59
60
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
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
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
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
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
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
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
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);
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
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
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
233
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
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 }