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.draw.DrawRuntimeException;
12 import org.djutils.draw.bounds.Bounds3d;
13 import org.djutils.draw.point.Point3d;
14 import org.junit.Test;
15
16
17
18
19
20
21
22
23
24
25 public class LineSegment3dTest
26 {
27
28
29
30 @Test
31 public void constructorTest()
32 {
33 verifySegment("Segment from four coordinates", new LineSegment3d(1, 2, 3, 4, 5, 6), 1, 2, 3, 4, 5, 6);
34 verifySegment("Segment from two coordinates and a Point3d", new LineSegment3d(1, 2, 3, new Point3d(4, 5, 6)), 1, 2, 3,
35 4, 5, 6);
36 verifySegment("Segment from a Point3d and two coordinates", new LineSegment3d(new Point3d(1, 2, 3), 4, 5, 6), 1, 2, 3,
37 4, 5, 6);
38 verifySegment("Segment from two Point3d objects", new LineSegment3d(new Point3d(1, 2, 3), new Point3d(4, 5, 6)), 1, 2,
39 3, 4, 5, 6);
40
41 try
42 {
43 new LineSegment3d(1, 2, 3, 1, 2, 3);
44 fail("idential start and end should have thrown a DrawRuntimeException");
45 }
46 catch (DrawRuntimeException dre)
47 {
48
49 }
50
51 new LineSegment3d(1, 2, 3, 1, 2, 4);
52 new LineSegment3d(1, 2, 3, 1, 3, 3);
53 new LineSegment3d(1, 2, 3, 2, 2, 3);
54
55 }
56
57
58
59
60
61
62
63
64
65
66
67
68 public void verifySegment(final String description, final LineSegment3d segment, final double expectedStartX,
69 final double expectedStartY, final double expectedStartZ, final double expectedEndX, final double expectedEndY,
70 final double expectedEndZ)
71 {
72 assertEquals(description + " startX", expectedStartX, segment.startX, 0.0001);
73 assertEquals(description + " startY", expectedStartY, segment.startY, 0.0001);
74 assertEquals(description + " startZ", expectedStartZ, segment.startZ, 0.0001);
75 assertEquals(description + " toX", expectedEndX, segment.endX, 0.0001);
76 assertEquals(description + " endY", expectedEndY, segment.endY, 0.0001);
77 assertEquals(description + " endZ", expectedEndZ, segment.endZ, 0.0001);
78 assertEquals(description + " getStartPoint x", expectedStartX, segment.getStartPoint().x, 0.0001);
79 assertEquals(description + " getStartPoint y", expectedStartY, segment.getStartPoint().y, 0.0001);
80 assertEquals(description + " getStartPoint z", expectedStartZ, segment.getStartPoint().z, 0.0001);
81 assertEquals(description + " getEndPoint x", expectedEndX, segment.getEndPoint().x, 0.0001);
82 assertEquals(description + " getEndPoint y", expectedEndY, segment.getEndPoint().y, 0.0001);
83 assertEquals(description + " getEndPoint z", expectedEndZ, segment.getEndPoint().z, 0.0001);
84 assertEquals(description + " length", Math
85 .hypot(Math.hypot(expectedEndX - expectedStartX, expectedEndY - expectedStartY), expectedEndZ - expectedStartZ),
86 segment.getLength(), 0.0001);
87 assertEquals(description + " size is 2", 2, segment.size());
88 Iterator<? extends Point3d> iterator = segment.getPoints();
89 assertTrue(description + " iterator has data", iterator.hasNext());
90 Point3d point = iterator.next();
91 assertEquals(description + " iterator first point x", expectedStartX, point.x, 0.0001);
92 assertEquals(description + " iterator first point y", expectedStartY, point.y, 0.0001);
93 assertEquals(description + " iterator first point z", expectedStartZ, point.z, 0.0001);
94 assertTrue(description + " iterator has more data", iterator.hasNext());
95 point = iterator.next();
96 assertEquals(description + " iterator second point x", expectedEndX, point.x, 0.0001);
97 assertEquals(description + " iterator second point y", expectedEndY, point.y, 0.0001);
98 assertEquals(description + " iterator second point z", expectedEndZ, point.z, 0.0001);
99 assertFalse(description + " iterator has no more data", iterator.hasNext());
100 Bounds3d bounds = segment.getBounds();
101 assertEquals(description + " bounds minX", Math.min(expectedStartX, expectedEndX), bounds.getMinX(), 0.0001);
102 assertEquals(description + " bounds maxX", Math.max(expectedStartX, expectedEndX), bounds.getMaxX(), 0.0001);
103 assertEquals(description + " bounds minY", Math.min(expectedStartY, expectedEndY), bounds.getMinY(), 0.0001);
104 assertEquals(description + " bounds maxY", Math.max(expectedStartY, expectedEndY), bounds.getMaxY(), 0.0001);
105 assertEquals(description + " bounds minZ", Math.min(expectedStartZ, expectedEndZ), bounds.getMinZ(), 0.0001);
106 assertEquals(description + " bounds maxZ", Math.max(expectedStartZ, expectedEndZ), bounds.getMaxZ(), 0.0001);
107 assertTrue(description + " toString returns something descriptive", segment.toString().startsWith("LineSegment3d "));
108 assertTrue(description + " toString can suppress the class name",
109 segment.toString().indexOf(segment.toString(true)) > 0);
110 }
111
112
113
114
115 @Test
116 public void locationTest()
117 {
118 Point3d startPoint = new Point3d(3, 4, 5);
119 Point3d endPoint = new Point3d(9, 20, 15);
120 LineSegment3d segment = new LineSegment3d(startPoint, endPoint);
121 try
122 {
123 segment.getLocation(Double.NaN);
124 fail("NaN position should have thrown a DrawRuntimeException");
125 }
126 catch (DrawRuntimeException dre)
127 {
128
129 }
130
131 try
132 {
133 segment.getLocationExtended(Double.POSITIVE_INFINITY);
134 fail("Infinity position should have thrown a DrawRuntimeException");
135 }
136 catch (DrawRuntimeException dre)
137 {
138
139 }
140
141 try
142 {
143 segment.getLocationExtended(Double.NEGATIVE_INFINITY);
144 fail("Infinity position should have thrown a DrawRuntimeException");
145 }
146 catch (DrawRuntimeException dre)
147 {
148
149 }
150
151 for (double position : new double[] {-3, -0.5, 0, 1, 10, 100})
152 {
153 if (position < 0 || position > segment.getLength())
154 {
155 try
156 {
157 segment.getLocation(position);
158 fail("position out of bounds should have thrown a DrawRuntimeException");
159 }
160 catch (DrawRuntimeException dre)
161 {
162
163 }
164 }
165 else
166 {
167 Ray3d ray = segment.getLocation(position);
168 assertEquals("distance from start point", position, ray.distance(startPoint), 0.0001);
169 assertEquals("distance from end point", segment.getLength() - position, ray.distance(endPoint), 0.0001);
170 assertEquals("direction of ray phi", startPoint.project().directionTo(endPoint.project()), ray.phi, 0.0001);
171 assertEquals("direction of ray theta", Math.atan2(endPoint.z - startPoint.z, segment.project().getLength()),
172 ray.theta, 0.0001);
173 }
174 Ray3d ray = segment.getLocationExtended(position);
175 assertEquals("distance from start point", Math.abs(position), ray.distance(startPoint), 0.0001);
176 assertEquals("distance from end point", Math.abs(segment.getLength() - position), ray.distance(endPoint), 0.0001);
177 assertEquals("direction of ray phi", startPoint.project().directionTo(endPoint.project()), ray.phi, 0.0001);
178 assertEquals("direction of ray theta", Math.atan2(endPoint.z - startPoint.z, segment.project().getLength()),
179 ray.theta, 0.0001);
180 }
181 }
182
183
184
185
186 @Test
187 public void closestPointOnSegmentTest()
188 {
189 LineSegment3d segment = new LineSegment3d(1, 2, 3, 20, 10, 15);
190 try
191 {
192 segment.closestPointOnSegment(null);
193 fail("Null for point should have thrown a NullPointerException");
194 }
195 catch (NullPointerException npe)
196 {
197
198 }
199
200 Point3d result = segment.closestPointOnSegment(new Point3d(1, 2, 0));
201 assertEquals("result is start point x", segment.startX, result.x, 0);
202 assertEquals("result is start point y", segment.startY, result.y, 0);
203 assertEquals("result is start point z", segment.startZ, result.z, 0);
204 result = segment.closestPointOnSegment(new Point3d(1, 0, 3));
205 assertEquals("result is start point x", segment.startX, result.x, 0);
206 assertEquals("result is start point y", segment.startY, result.y, 0);
207 assertEquals("result is start point z", segment.startZ, result.z, 0);
208 result = segment.closestPointOnSegment(new Point3d(0, 2, 3));
209 assertEquals("result is start point x", segment.startX, result.x, 0);
210 assertEquals("result is start point y", segment.startY, result.y, 0);
211 assertEquals("result is start point z", segment.startZ, result.z, 0);
212 result = segment.closestPointOnSegment(new Point3d(1, 2, 3));
213 assertEquals("result is start point x", segment.startX, result.x, 0);
214 assertEquals("result is start point y", segment.startY, result.y, 0);
215 assertEquals("result is start point z", segment.startZ, result.z, 0);
216
217 Point3d projectingPoint = new Point3d(10, 10, 10);
218 result = segment.closestPointOnSegment(projectingPoint);
219 double distanceFromStart = result.distance(segment.getStartPoint());
220 assertTrue("distance from start is > 0", distanceFromStart > 0);
221 double distanceToEnd = result.distance(segment.getEndPoint());
222
223 assertTrue("distance to end point is > 0", distanceToEnd > 0);
224 assertEquals("sum of distances is length of segment", segment.getLength(), distanceFromStart + distanceToEnd, 0.0001);
225
226 Point3d doubleProjected = segment.closestPointOnSegment(result);
227 assertEquals("projecting the projection yields the projection", 0, doubleProjected.distance(result), 0.0001);
228
229 result = segment.closestPointOnSegment(new Point3d(21, 10, 15));
230 assertEquals("result is end point", segment.endX, result.x, 0);
231 assertEquals("result is end point", segment.endY, result.y, 0);
232 result = segment.closestPointOnSegment(new Point3d(20, 11, 15));
233 assertEquals("result is end point", segment.endX, result.x, 0);
234 assertEquals("result is end point", segment.endY, result.y, 0);
235 result = segment.closestPointOnSegment(new Point3d(20, 10, 16));
236 assertEquals("result is end point", segment.endX, result.x, 0);
237 assertEquals("result is end point", segment.endY, result.y, 0);
238 result = segment.closestPointOnSegment(new Point3d(20, 10, 15));
239 assertEquals("result is end point", segment.endX, result.x, 0);
240 assertEquals("result is end point", segment.endY, result.y, 0);
241 }
242
243
244
245
246 @Test
247 public void testProject()
248 {
249 LineSegment3d segment = new LineSegment3d(1, 2, 3, 20, 10, 5);
250 assertTrue("projects outside", Double.isNaN(segment.projectOrthogonalFractional(new Point3d(1, 1, 1))));
251 assertTrue("projects before start", segment.projectOrthogonalFractionalExtended(new Point3d(1, 1, 1)) < 0);
252 assertEquals("projects at -2", -2,
253 segment.projectOrthogonalFractionalExtended(new Point3d(1 - 19 - 19 + 8, 2 - 8 - 8 - 19, 3 - 2 - 2)), 0.0001);
254 assertEquals("point near half way (not on segment) project at about half way", 0.5,
255 segment.projectOrthogonalFractional(new Point3d(11, 1, 4)), 0.1);
256 assertTrue("projects outside", Double.isNaN(segment.projectOrthogonalFractional(new Point3d(25, 15, 6))));
257 assertTrue("projects after end", segment.projectOrthogonalFractionalExtended(new Point3d(25, 15, 6)) > 1);
258 assertEquals("projects at 2", 2,
259 segment.projectOrthogonalFractionalExtended(new Point3d(1 + 19 + 19 - 8, 2 + 8 + 8 + 19, 3 + 2 + 2)), 0.0001);
260 }
261
262
263
264
265
266 @Test
267 public void testToExcel() throws NumberFormatException
268 {
269 LineSegment3d segment = new LineSegment3d(1, 2, 3, 20, 10, 5);
270 String result = segment.toExcel();
271 String[] lines = result.split("\n");
272 assertEquals("result is two lines", 2, lines.length);
273 for (int lineNo = 0; lineNo < lines.length; lineNo++)
274 {
275 String[] fields = lines[lineNo].trim().split("\t");
276 assertEquals("Line consists of three fields", 3, fields.length);
277 for (int fieldNo = 0; fieldNo < fields.length; fieldNo++)
278 {
279 double value = Double.parseDouble(fields[fieldNo]);
280 double expectedValue =
281 lineNo == 0 ? (fieldNo == 0 ? segment.startX : fieldNo == 1 ? segment.startY : segment.startZ)
282 : (fieldNo == 0 ? segment.endX : fieldNo == 1 ? segment.endY : segment.endZ);
283 assertEquals("field contains the correct value", expectedValue, value, 0.0001);
284 }
285 }
286 }
287
288
289
290
291 @Test
292 public void equalsAndHashCodeTest()
293 {
294 LineSegment3d segment = new LineSegment3d(1, 2, 3, -3, -4, -5);
295 assertEquals("equal to itself", segment, segment);
296 assertNotEquals("not equal to null", segment, null);
297 assertNotEquals("not equal to a totally different object", segment, "no way");
298 assertNotEquals("not equal to line segment with different start x", segment, new LineSegment3d(2, 2, 3, -3, -4, -5));
299 assertNotEquals("not equal to line segment with different start y", segment, new LineSegment3d(1, 3, 3, -3, -4, -5));
300 assertNotEquals("not equal to line segment with different start z", segment, new LineSegment3d(1, 2, 4, -3, -4, -5));
301 assertNotEquals("not equal to line segment with different end x", segment, new LineSegment3d(1, 2, 3, -4, -4, -5));
302 assertNotEquals("not equal to line segment with different end y", segment, new LineSegment3d(1, 2, 3, -3, -5, -5));
303 assertNotEquals("not equal to line segment with different end y", segment, new LineSegment3d(1, 2, 3, -3, -4, -6));
304 assertEquals("equal to another line segment with same start and end x, y, z", segment,
305 new LineSegment3d(1, 2, 3, -3, -4, -5));
306
307 assertNotEquals("hashCode depends on start x", segment.hashCode(), new LineSegment3d(2, 2, 3, -3, -4, -5));
308 assertNotEquals("hashCode depends on start y", segment.hashCode(), new LineSegment3d(1, 3, 3, -3, -4, -5));
309 assertNotEquals("hashCode depends on start z", segment.hashCode(), new LineSegment3d(1, 3, 4, -3, -4, -5));
310 assertNotEquals("hashCode depends on end x", segment.hashCode(), new LineSegment3d(1, 2, 3, -4, -4, -5));
311 assertNotEquals("hashCode depends on end y", segment.hashCode(), new LineSegment3d(1, 2, 3, -4, -5, -5));
312 assertNotEquals("hashCode depends on end z", segment.hashCode(), new LineSegment3d(1, 2, 3, -4, -5, -6));
313 }
314
315 }