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.assertNotEquals;
6 import static org.junit.Assert.assertTrue;
7 import static org.junit.Assert.fail;
8
9 import java.awt.geom.Path2D;
10 import java.awt.geom.PathIterator;
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.Method;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Random;
16
17 import org.djunits.unit.DirectionUnit;
18 import org.djunits.unit.LengthUnit;
19 import org.djunits.value.vdouble.scalar.Direction;
20 import org.djunits.value.vdouble.scalar.Length;
21 import org.junit.Test;
22 import org.locationtech.jts.geom.Coordinate;
23 import org.locationtech.jts.geom.Geometry;
24 import org.locationtech.jts.geom.GeometryFactory;
25 import org.locationtech.jts.geom.LineString;
26 import org.opentrafficsim.core.geometry.OTSLine3D.FractionalFallback;
27 import org.opentrafficsim.core.network.NetworkException;
28
29
30
31
32
33
34
35
36
37 public class OTSLine3DTest
38 {
39
40
41
42
43
44 @Test
45 public final void constructorsTest() throws OTSGeometryException, NetworkException
46 {
47 double[] values = { -999, 0, 99, 9999 };
48 OTSPoint3D[] points = new OTSPoint3D[0];
49 try
50 {
51 runConstructors(points);
52 fail("Should have thrown a NetworkException");
53 }
54 catch (OTSGeometryException exception)
55 {
56
57 }
58 for (double x0 : values)
59 {
60 for (double y0 : values)
61 {
62 for (double z0 : values)
63 {
64 points = new OTSPoint3D[1];
65 points[0] = new OTSPoint3D(x0, y0, z0);
66 try
67 {
68 runConstructors(points);
69 fail("Should have thrown a NetworkException");
70 }
71 catch (OTSGeometryException exception)
72 {
73
74 }
75 for (double x1 : values)
76 {
77 for (double y1 : values)
78 {
79 for (double z1 : values)
80 {
81 points = new OTSPoint3D[2];
82 points[0] = new OTSPoint3D(x0, y0, z0);
83 points[1] = new OTSPoint3D(x1, y1, z1);
84 if (0 == points[0].distance(points[1]).si)
85 {
86 try
87 {
88 runConstructors(points);
89 fail("Should have thrown a NetworkException");
90 }
91 catch (OTSGeometryException exception)
92 {
93
94 }
95 }
96 else
97 {
98 runConstructors(points);
99 for (double x2 : values)
100 {
101 for (double y2 : values)
102 {
103 for (double z2 : values)
104 {
105 points = new OTSPoint3D[3];
106 points[0] = new OTSPoint3D(x0, y0, z0);
107 points[1] = new OTSPoint3D(x1, y1, z1);
108 points[2] = new OTSPoint3D(x2, y2, z2);
109 if (0 == points[1].distance(points[2]).si)
110 {
111 try
112 {
113 runConstructors(points);
114 fail("Should have thrown a NetworkException");
115 }
116 catch (OTSGeometryException exception)
117 {
118
119 }
120 }
121 else
122 {
123 runConstructors(points);
124 }
125 }
126 }
127 }
128 }
129 }
130 }
131 }
132 }
133 }
134 }
135 }
136
137
138
139
140
141
142
143 private void runConstructors(final OTSPoint3D[] points) throws OTSGeometryException, NetworkException
144 {
145 verifyPoints(new OTSLine3D(points), points);
146 Coordinate[] coordinates = new Coordinate[points.length];
147 for (int i = 0; i < points.length; i++)
148 {
149 coordinates[i] = new Coordinate(points[i].x, points[i].y, points[i].z);
150 }
151 verifyPoints(new OTSLine3D(coordinates), points);
152 GeometryFactory gm = new GeometryFactory();
153 LineString lineString = gm.createLineString(coordinates);
154 verifyPoints(new OTSLine3D(lineString), points);
155 verifyPoints(new OTSLine3D((Geometry) lineString), points);
156 List<OTSPoint3D> list = new ArrayList<>();
157 for (int i = 0; i < points.length; i++)
158 {
159 list.add(points[i]);
160 }
161 OTSLine3D line = new OTSLine3D(list);
162 verifyPoints(line, points);
163
164 verifyPoints(new OTSLine3D(line.getCoordinates()), points);
165
166 verifyPoints(new OTSLine3D(line.getLineString()), points);
167
168 verifyPoints(new OTSLine3D(line.getPoints()), points);
169 double length = 0;
170 for (int i = 1; i < points.length; i++)
171 {
172 length += Math.sqrt(Math.pow(points[i].x - points[i - 1].x, 2) + Math.pow(points[i].y - points[i - 1].y, 2)
173 + Math.pow(points[i].z - points[i - 1].z, 2));
174 }
175 assertEquals("length", length, line.getLength().si, 10 * Math.ulp(length));
176 assertEquals("length", length, line.getLength().si, 10 * Math.ulp(length));
177 assertEquals("length", length, line.getLengthSI(), 10 * Math.ulp(length));
178 assertEquals("length", length, line.getLengthSI(), 10 * Math.ulp(length));
179
180 int horizontalMoves = 0;
181 Path2D path = new Path2D.Double();
182 path.moveTo(points[0].x, points[0].y);
183
184 for (int i = 1; i < points.length; i++)
185 {
186 if (points[i].x != points[i - 1].x || points[i].y != points[i - 1].y)
187 {
188 path.lineTo(points[i].x, points[i].y);
189
190 horizontalMoves++;
191 }
192 }
193 try
194 {
195 line = new OTSLine3D(path);
196 if (0 == horizontalMoves)
197 {
198 fail("Construction of OTSLine3D from path with degenerate projection should have failed");
199 }
200
201 assertEquals("number of points should match", horizontalMoves + 1, line.size());
202 int indexInLine = 0;
203 for (int i = 0; i < points.length; i++)
204 {
205 if (i > 0 && (points[i].x != points[i - 1].x || points[i].y != points[i - 1].y))
206 {
207 indexInLine++;
208 }
209 assertEquals("x in line", points[i].x, line.get(indexInLine).x, 0.001);
210 assertEquals("y in line", points[i].y, line.get(indexInLine).y, 0.001);
211 }
212 }
213 catch (OTSGeometryException e)
214 {
215 if (0 != horizontalMoves)
216 {
217 fail("Construction of OTSLine3D from path with non-degenerate projection should not have failed");
218 }
219 }
220 }
221
222
223
224
225
226 public final void printPath2D(final Path2D path)
227 {
228 PathIterator pi = path.getPathIterator(null);
229 double[] p = new double[6];
230 while (!pi.isDone())
231 {
232 int segType = pi.currentSegment(p);
233 if (segType == PathIterator.SEG_MOVETO)
234 {
235 System.out.print(" move to " + new OTSPoint3D(p[0], p[1]));
236 }
237 if (segType == PathIterator.SEG_LINETO)
238 {
239 System.out.print(" line to " + new OTSPoint3D(p[0], p[1]));
240 }
241 else if (segType == PathIterator.SEG_CLOSE)
242 {
243 System.out.print(" close");
244 }
245 pi.next();
246 }
247 System.out.println("");
248 }
249
250
251
252
253
254
255
256 private void verifyPoints(final OTSLine3D line, final OTSPoint3D[] points) throws OTSGeometryException
257 {
258 assertEquals("Line should have same number of points as point array", line.size(), points.length);
259 for (int i = 0; i < points.length; i++)
260 {
261 assertEquals("x of point i should match", points[i].x, line.get(i).x, Math.ulp(points[i].x));
262 assertEquals("y of point i should match", points[i].y, line.get(i).y, Math.ulp(points[i].y));
263 assertEquals("z of point i should match", points[i].z, line.get(i).z, Math.ulp(points[i].z));
264 }
265 }
266
267
268
269
270
271 @Test
272 public final void exceptionTest() throws OTSGeometryException
273 {
274 OTSLine3D line = new OTSLine3D(new OTSPoint3D[] { new OTSPoint3D(1, 2, 3), new OTSPoint3D(4, 5, 6) });
275 try
276 {
277 line.get(-1);
278 fail("Should have thrown an exception");
279 }
280 catch (OTSGeometryException oe)
281 {
282
283 }
284 try
285 {
286 line.get(2);
287 fail("Should have thrown an exception");
288 }
289 catch (OTSGeometryException oe)
290 {
291
292 }
293 }
294
295
296
297
298
299 @Test
300 public final void locationExtendedTest() throws OTSGeometryException
301 {
302 OTSPoint3D p0 = new OTSPoint3D(10, 20, 30);
303 OTSPoint3D p1 = new OTSPoint3D(40, 50, 60);
304 OTSPoint3D p2 = new OTSPoint3D(90, 80, 70);
305 OTSLine3D l = new OTSLine3D(new OTSPoint3D[] { p0, p1, p2 });
306 checkGetLocation(l, -10, null, Math.atan2(p1.y - p0.y, p1.x - p0.x));
307 checkGetLocation(l, -0.0001, p0, Math.atan2(p1.y - p0.y, p1.x - p0.x));
308 checkGetLocation(l, 0, p0, Math.atan2(p1.y - p0.y, p1.x - p0.x));
309 checkGetLocation(l, 0.0001, p0, Math.atan2(p1.y - p0.y, p1.x - p0.x));
310 checkGetLocation(l, 0.9999, p2, Math.atan2(p2.y - p1.y, p2.x - p1.x));
311 checkGetLocation(l, 1, p2, Math.atan2(p2.y - p1.y, p2.x - p1.x));
312 checkGetLocation(l, 1.0001, p2, Math.atan2(p2.y - p1.y, p2.x - p1.x));
313 checkGetLocation(l, 10, null, Math.atan2(p2.y - p1.y, p2.x - p1.x));
314 }
315
316
317
318
319
320
321
322
323
324 private void checkGetLocation(final OTSLine3D line, final double fraction, final OTSPoint3D expectedPoint,
325 final double expectedZRotation) throws OTSGeometryException
326 {
327 double length = line.getLengthSI();
328 checkDirectedPoint(line.getLocationExtendedSI(fraction * length), expectedPoint, expectedZRotation);
329 Length typedLength = new Length(fraction * length, LengthUnit.METER);
330 checkDirectedPoint(line.getLocationExtended(typedLength), expectedPoint, expectedZRotation);
331 if (fraction < 0 || fraction > 1)
332 {
333 try
334 {
335 line.getLocationSI(fraction * length);
336 fail("getLocation should have thrown a OTSGeometryException");
337 }
338 catch (OTSGeometryException ne)
339 {
340
341 }
342 try
343 {
344 line.getLocation(typedLength);
345 fail("getLocation should have thrown a OTSGeometryException");
346 }
347 catch (OTSGeometryException ne)
348 {
349
350 }
351 try
352 {
353 line.getLocationFraction(fraction);
354 fail("getLocation should have thrown a OTSGeometryException");
355 }
356 catch (OTSGeometryException ne)
357 {
358
359 }
360 }
361 else
362 {
363 checkDirectedPoint(line.getLocationSI(fraction * length), expectedPoint, expectedZRotation);
364 checkDirectedPoint(line.getLocation(typedLength), expectedPoint, expectedZRotation);
365 checkDirectedPoint(line.getLocationFraction(fraction), expectedPoint, expectedZRotation);
366 }
367
368 }
369
370
371
372
373
374
375
376 private void checkDirectedPoint(final DirectedPoint dp, final OTSPoint3D expectedPoint, final double expectedZRotation)
377 {
378
379 if (null != expectedPoint)
380 {
381 OTSPoint3D p = new OTSPoint3D(dp);
382 assertEquals("locationExtendedSI(0) returns approximately expected point", 0, expectedPoint.distanceSI(p), 0.1);
383 }
384 assertEquals("z-rotation at 0", expectedZRotation, dp.getRotZ(), 0.001);
385 }
386
387
388
389
390
391 @Test
392 public final void locationTest() throws OTSGeometryException
393 {
394 OTSPoint3D p0 = new OTSPoint3D(10, 20, 60);
395 OTSPoint3D p1 = new OTSPoint3D(40, 50, 60);
396 OTSPoint3D p2 = new OTSPoint3D(90, 70, 90);
397 OTSLine3D l = new OTSLine3D(new OTSPoint3D[] { p0, p1, p2 });
398 DirectedPoint dp = l.getLocation();
399 assertEquals("centroid x", 50, dp.x, 0.001);
400 assertEquals("centroid y", 45, dp.y, 0.001);
401 assertEquals("centroid z", 75, dp.z, 0.001);
402 l = new OTSLine3D(new OTSPoint3D[] { p1, p0, p2 });
403 dp = l.getLocation();
404 assertEquals("centroid x", 50, dp.x, 0.001);
405 assertEquals("centroid y", 45, dp.y, 0.001);
406 assertEquals("centroid z", 75, dp.z, 0.001);
407 l = new OTSLine3D(new OTSPoint3D[] { p0, p1 });
408 dp = l.getLocation();
409 assertEquals("centroid x", 25, dp.x, 0.001);
410 assertEquals("centroid y", 35, dp.y, 0.001);
411 assertEquals("centroid z", 60, dp.z, 0.001);
412 }
413
414
415
416
417
418 @Test
419 public final void cleanTest() throws OTSGeometryException
420 {
421 OTSPoint3D[] tooShort = new OTSPoint3D[] {};
422 try
423 {
424 OTSLine3D.createAndCleanOTSLine3D(tooShort);
425 fail("Array with no points should have thrown an exception");
426 }
427 catch (OTSGeometryException ne)
428 {
429
430 }
431 tooShort = new OTSPoint3D[] { new OTSPoint3D(1, 2, 3) };
432 try
433 {
434 OTSLine3D.createAndCleanOTSLine3D(tooShort);
435 fail("Array with no points should have thrown an exception");
436 }
437 catch (OTSGeometryException ne)
438 {
439
440 }
441 OTSPoint3D p0 = new OTSPoint3D(1, 2, 3);
442 OTSPoint3D p1 = new OTSPoint3D(4, 5, 6);
443 OTSPoint3D[] points = new OTSPoint3D[] { p0, p1 };
444 OTSLine3D result = OTSLine3D.createAndCleanOTSLine3D(points);
445 assertTrue("first point is p0", p0.equals(result.get(0)));
446 assertTrue("second point is p1", p1.equals(result.get(1)));
447 OTSPoint3D p1Same = new OTSPoint3D(4, 5, 6);
448 result = OTSLine3D.createAndCleanOTSLine3D(new OTSPoint3D[] { p0, p0, p0, p0, p1Same, p0, p1, p1, p1Same, p1, p1 });
449 assertEquals("result should contain 4 points", 4, result.size());
450 assertTrue("first point is p0", p0.equals(result.get(0)));
451 assertTrue("second point is p1", p1.equals(result.get(1)));
452 assertTrue("third point is p0", p0.equals(result.get(0)));
453 assertTrue("last point is p1", p1.equals(result.get(1)));
454 }
455
456
457
458
459
460 @Test
461 public final void equalsTest() throws OTSGeometryException
462 {
463 OTSPoint3D p0 = new OTSPoint3D(1.1, 2.2, 3.3);
464 OTSPoint3D p1 = new OTSPoint3D(2.1, 2.2, 3.3);
465 OTSPoint3D p2 = new OTSPoint3D(3.1, 2.2, 3.3);
466
467 OTSLine3D line = new OTSLine3D(new OTSPoint3D[] { p0, p1, p2 });
468 assertTrue("OTSLine3D is equal to itself", line.equals(line));
469 assertFalse("OTSLine3D is not equal to null", line.equals(null));
470 assertFalse("OTSLine3D is not equals to some other kind of Object", line.equals(new String("hello")));
471 OTSLine3D line2 = new OTSLine3D(new OTSPoint3D[] { p0, p1, p2 });
472 assertTrue("OTSLine3D is equal ot other OTSLine3D that has the exact same list of OTSPoint3D", line.equals(line2));
473 OTSPoint3D p2Same = new OTSPoint3D(3.1, 2.2, 3.3);
474 line2 = new OTSLine3D(new OTSPoint3D[] { p0, p1, p2Same });
475 assertTrue("OTSLine3D is equal ot other OTSLine3D that has the exact same list of OTSPoint3D; even if some of "
476 + "those point are different instances with the same coordinates", line.equals(line2));
477 OTSPoint3D p2NotSame = new OTSPoint3D(3.1, 2.2, 3.35);
478 line2 = new OTSLine3D(new OTSPoint3D[] { p0, p1, p2NotSame });
479 assertFalse("OTSLine3D is not equal ot other OTSLine3D that differs in one coordinate", line.equals(line2));
480 line2 = new OTSLine3D(new OTSPoint3D[] { p0, p1, p2, p2NotSame });
481 assertFalse("OTSLine3D is not equal ot other OTSLine3D that has more points (but is identical up to the common length)",
482 line.equals(line2));
483 assertFalse(
484 "OTSLine3D is not equal ot other OTSLine3D that has fewer points (but is identical up to the common length)",
485 line2.equals(line));
486 }
487
488
489
490
491
492 @Test
493 public final void concatenateTest() throws OTSGeometryException
494 {
495 OTSPoint3D p0 = new OTSPoint3D(1.1, 2.2, 3.3);
496 OTSPoint3D p1 = new OTSPoint3D(2.1, 2.2, 3.3);
497 OTSPoint3D p2 = new OTSPoint3D(3.1, 2.2, 3.3);
498 OTSPoint3D p3 = new OTSPoint3D(4.1, 2.2, 3.3);
499 OTSPoint3D p4 = new OTSPoint3D(5.1, 2.2, 3.3);
500 OTSPoint3D p5 = new OTSPoint3D(6.1, 2.2, 3.3);
501
502 OTSLine3D l0 = new OTSLine3D(p0, p1, p2);
503 OTSLine3D l1 = new OTSLine3D(p2, p3);
504 OTSLine3D l2 = new OTSLine3D(p3, p4, p5);
505 OTSLine3D ll = OTSLine3D.concatenate(l0, l1, l2);
506 assertEquals("size is 6", 6, ll.size());
507 assertEquals("point 0 is p0", p0, ll.get(0));
508 assertEquals("point 1 is p1", p1, ll.get(1));
509 assertEquals("point 2 is p2", p2, ll.get(2));
510 assertEquals("point 3 is p3", p3, ll.get(3));
511 assertEquals("point 4 is p4", p4, ll.get(4));
512 assertEquals("point 5 is p5", p5, ll.get(5));
513
514 ll = OTSLine3D.concatenate(l1);
515 assertEquals("size is 2", 2, ll.size());
516 assertEquals("point 0 is p2", p2, ll.get(0));
517 assertEquals("point 1 is p3", p3, ll.get(1));
518
519 try
520 {
521 OTSLine3D.concatenate(l0, l2);
522 fail("Gap should have throw an exception");
523 }
524 catch (OTSGeometryException e)
525 {
526
527 }
528 try
529 {
530 OTSLine3D.concatenate();
531 fail("concatenate of empty list should have thrown an exception");
532 }
533 catch (OTSGeometryException e)
534 {
535
536 }
537
538
539 OTSLine3D thirdLine = new OTSLine3D(p4, p5);
540 for (double tolerance : new double[] { 0.1, 0.01, 0.001, 0.0001, 0.00001 })
541 {
542 for (double actualError : new double[] { tolerance * 0.9, tolerance * 1.1 })
543 {
544 int maxDirection = 10;
545 for (int direction = 0; direction < maxDirection; direction++)
546 {
547 double dx = actualError * Math.cos(Math.PI * 2 * direction / maxDirection);
548 double dy = actualError * Math.sin(Math.PI * 2 * direction / maxDirection);
549 OTSLine3D otherLine = new OTSLine3D(new OTSPoint3D(p2.x + dx, p2.y + dy, p2.z), p3, p4);
550 if (actualError < tolerance)
551 {
552 try
553 {
554 OTSLine3D.concatenate(tolerance, l0, otherLine);
555 }
556 catch (OTSGeometryException oge)
557 {
558 OTSLine3D.concatenate(tolerance, l0, otherLine);
559 fail("concatenation with error " + actualError + " and tolerance " + tolerance
560 + " should not have failed");
561 }
562 try
563 {
564 OTSLine3D.concatenate(tolerance, l0, otherLine, thirdLine);
565 }
566 catch (OTSGeometryException oge)
567 {
568 fail("concatenation with error " + actualError + " and tolerance " + tolerance
569 + " should not have failed");
570 }
571 }
572 else
573 {
574 try
575 {
576 OTSLine3D.concatenate(tolerance, l0, otherLine);
577 }
578 catch (OTSGeometryException oge)
579 {
580
581 }
582 try
583 {
584 OTSLine3D.concatenate(tolerance, l0, otherLine, thirdLine);
585 }
586 catch (OTSGeometryException oge)
587 {
588
589 }
590 }
591 }
592 }
593 }
594 }
595
596
597
598
599
600 @Test
601 public final void noiseFilterRamerDouglasPeuckerTest() throws OTSGeometryException
602 {
603 OTSPoint3D start = new OTSPoint3D(1, 2, 3);
604 int maxDirection = 20;
605 double length = 100;
606 for (boolean useHorizontalDistance : new boolean[] { true, false })
607 {
608 for (int direction = 0; direction < maxDirection; direction++)
609 {
610 double angle = Math.PI * 2 * direction / maxDirection;
611 double dx = length * Math.cos(angle);
612 double dy = length * Math.sin(angle);
613 OTSPoint3D end = new OTSPoint3D(start.x + dx, start.y + dy, start.z);
614 OTSLine3D straightLine = new OTSLine3D(start, end);
615 int intermediatePointCount = 5;
616 for (double tolerance : new double[] { 0.1, 0.01, 0.001 })
617 {
618 double error = tolerance * 0.9;
619 List<OTSPoint3D> pointsOnTestLine = new ArrayList<>();
620 pointsOnTestLine.add(start);
621 for (int intermediatePoint = 0; intermediatePoint < intermediatePointCount; intermediatePoint++)
622 {
623 double iAngle = Math.PI * 2 * intermediatePoint / intermediatePointCount;
624 double idx = error * Math.cos(iAngle);
625 double idy = error * Math.sin(iAngle);
626 double idz = useHorizontalDistance ? (intermediatePoint % 2 * 2 - 1) * 10 : 0;
627 DirectedPoint exactPoint =
628 straightLine.getLocationFraction((intermediatePoint + 0.5) / intermediatePointCount);
629 OTSPoint3D additionalPoint = new OTSPoint3D(exactPoint.x + idx, exactPoint.y + idy, exactPoint.z + idz);
630 pointsOnTestLine.add(additionalPoint);
631 }
632 pointsOnTestLine.add(end);
633 OTSLine3D testLine = new OTSLine3D(pointsOnTestLine);
634 OTSLine3D filteredLine = testLine.noiseFilterRamerDouglasPeucker(tolerance, useHorizontalDistance);
635 assertEquals("RamerDouglasPeuker filter should have removed all intermediate points", 2,
636 filteredLine.size());
637
638
639 OTSPoint3D newStart = new OTSPoint3D(start.x + 10 * tolerance * dy / length,
640 start.y - 10 * tolerance * dx / length, start.z);
641 pointsOnTestLine.add(0, newStart);
642
643
644 testLine = new OTSLine3D(pointsOnTestLine);
645 filteredLine = testLine.noiseFilterRamerDouglasPeucker(tolerance, useHorizontalDistance);
646
647
648
649
650 assertEquals("RamerDouglasPeuker filter should have left three points", 3, filteredLine.size());
651 pointsOnTestLine.remove(0);
652 OTSPoint3D newEnd =
653 new OTSPoint3D(end.x + 10 * tolerance * dy / length, end.y - 10 * tolerance * dx / length, end.z);
654 pointsOnTestLine.add(newEnd);
655 testLine = new OTSLine3D(pointsOnTestLine);
656 filteredLine = testLine.noiseFilterRamerDouglasPeucker(tolerance, useHorizontalDistance);
657
658
659
660
661 assertEquals("RamerDouglasPeuker filter should have left three points", 3, filteredLine.size());
662 }
663 }
664 }
665 }
666
667
668
669
670
671 @Test
672 public final void reverseTest() throws OTSGeometryException
673 {
674 OTSPoint3D p0 = new OTSPoint3D(1.1, 2.2, 3.3);
675 OTSPoint3D p1 = new OTSPoint3D(2.1, 2.2, 3.3);
676 OTSPoint3D p2 = new OTSPoint3D(3.1, 2.2, 3.3);
677 OTSPoint3D p3 = new OTSPoint3D(4.1, 2.2, 3.3);
678 OTSPoint3D p4 = new OTSPoint3D(5.1, 2.2, 3.3);
679 OTSPoint3D p5 = new OTSPoint3D(6.1, 2.2, 3.3);
680
681 OTSLine3D l01 = new OTSLine3D(p0, p1);
682 OTSLine3D r = l01.reverse();
683 assertEquals("result has size 2", 2, r.size());
684 assertEquals("point 0 is p1", p1, r.get(0));
685 assertEquals("point 1 is p0", p0, r.get(1));
686
687 OTSLine3D l05 = new OTSLine3D(p0, p1, p2, p3, p4, p5);
688 r = l05.reverse();
689 assertEquals("result has size 6", 6, r.size());
690 assertEquals("point 0 is p5", p5, r.get(0));
691 assertEquals("point 1 is p4", p4, r.get(1));
692 assertEquals("point 2 is p3", p3, r.get(2));
693 assertEquals("point 3 is p2", p2, r.get(3));
694 assertEquals("point 4 is p1", p1, r.get(4));
695 assertEquals("point 5 is p0", p0, r.get(5));
696
697 }
698
699
700
701
702
703 @SuppressWarnings("checkstyle:methodlength")
704 @Test
705 public final void extractTest() throws OTSGeometryException
706 {
707 OTSPoint3D p0 = new OTSPoint3D(1, 2, 3);
708 OTSPoint3D p1 = new OTSPoint3D(2, 3, 4);
709 OTSPoint3D p1a = new OTSPoint3D(2.01, 3.01, 4.01);
710 OTSPoint3D p1b = new OTSPoint3D(2.02, 3.02, 4.02);
711 OTSPoint3D p1c = new OTSPoint3D(2.03, 3.03, 4.03);
712 OTSPoint3D p2 = new OTSPoint3D(12, 13, 14);
713
714 OTSLine3D l = new OTSLine3D(p0, p1);
715 OTSLine3D e = l.extractFractional(0, 1);
716 assertEquals("size of extraction is 2", 2, e.size());
717 assertEquals("point 0 is p0", p0, e.get(0));
718 assertEquals("point 1 is p1", p1, e.get(1));
719 try
720 {
721 l.extractFractional(-0.1, 1);
722 fail("negative start should have thrown an exception");
723 }
724 catch (OTSGeometryException exception)
725 {
726
727 }
728 try
729 {
730 l.extractFractional(Double.NaN, 1);
731 fail("NaN start should have thrown an exception");
732 }
733 catch (OTSGeometryException exception)
734 {
735
736 }
737 try
738 {
739 l.extractFractional(0, 1.1);
740 fail("end > 1 should have thrown an exception");
741 }
742 catch (OTSGeometryException exception)
743 {
744
745 }
746 try
747 {
748 l.extractFractional(0, Double.NaN);
749 fail("NaN end should have thrown an exception");
750 }
751 catch (OTSGeometryException exception)
752 {
753
754 }
755 try
756 {
757 l.extractFractional(0.6, 0.4);
758 fail("start > end should have thrown an exception");
759 }
760 catch (OTSGeometryException exception)
761 {
762
763 }
764 try
765 {
766 l.extract(-0.1, 1);
767 fail("negative start should have thrown an exception");
768 }
769 catch (OTSGeometryException exception)
770 {
771
772 }
773 try
774 {
775 l.extract(Double.NaN, 1);
776 fail("NaN start should have thrown an exception");
777 }
778 catch (OTSGeometryException exception)
779 {
780
781 }
782 try
783 {
784 l.extract(0, l.getLengthSI() + 0.1);
785 fail("end > length should have thrown an exception");
786 }
787 catch (OTSGeometryException exception)
788 {
789
790 }
791 try
792 {
793 l.extract(0, Double.NaN);
794 fail("NaN end should have thrown an exception");
795 }
796 catch (OTSGeometryException exception)
797 {
798
799 }
800 try
801 {
802 l.extract(0.6, 0.4);
803 fail("start > end should have thrown an exception");
804 }
805 catch (OTSGeometryException exception)
806 {
807
808 }
809
810 for (int i = 0; i < 10; i++)
811 {
812 for (int j = i + 1; j < 10; j++)
813 {
814 double start = i * l.getLengthSI() / 10;
815 double end = j * l.getLengthSI() / 10;
816
817 for (OTSLine3D extractedLine : new OTSLine3D[] { l.extract(start, end),
818 l.extract(new Length(start, LengthUnit.SI), new Length(end, LengthUnit.SI)),
819 l.extractFractional(1.0 * i / 10, 1.0 * j / 10) })
820 {
821 assertEquals("size of extract is 2", 2, extractedLine.size());
822 assertEquals("x of 0", p0.x + (p1.x - p0.x) * i / 10, extractedLine.get(0).x, 0.0001);
823 assertEquals("y of 0", p0.y + (p1.y - p0.y) * i / 10, extractedLine.get(0).y, 0.0001);
824 assertEquals("z of 0", p0.z + (p1.z - p0.z) * i / 10, extractedLine.get(0).z, 0.0001);
825 assertEquals("x of 1", p0.x + (p1.x - p0.x) * j / 10, extractedLine.get(1).x, 0.0001);
826 assertEquals("y of 1", p0.y + (p1.y - p0.y) * j / 10, extractedLine.get(1).y, 0.0001);
827 assertEquals("z of 1", p0.z + (p1.z - p0.z) * j / 10, extractedLine.get(1).z, 0.0001);
828 }
829 }
830 }
831
832 for (OTSLine3D line : new OTSLine3D[] { new OTSLine3D(p0, p1, p2), new OTSLine3D(p0, p1, p1a, p1b, p1c, p2) })
833 {
834 for (int i = 0; i < 110; i++)
835 {
836 if (10 == i)
837 {
838 continue;
839 }
840 for (int j = i + 1; j < 110; j++)
841 {
842 if (10 == j)
843 {
844 continue;
845 }
846 double start = i * line.getLengthSI() / 110;
847 double end = j * line.getLengthSI() / 110;
848
849
850
851 for (OTSLine3D extractedLine : new OTSLine3D[] { line.extract(start, end),
852 line.extract(new Length(start, LengthUnit.SI), new Length(end, LengthUnit.SI)),
853 line.extractFractional(1.0 * i / 110, 1.0 * j / 110) })
854 {
855 int expectedSize = i < 10 && j > 10 ? line.size() : 2;
856 assertEquals("size is " + expectedSize, expectedSize, extractedLine.size());
857 if (i < 10)
858 {
859 assertEquals("x of 0", p0.x + (p1.x - p0.x) * i / 10, extractedLine.get(0).x, 0.0001);
860 assertEquals("y of 0", p0.y + (p1.y - p0.y) * i / 10, extractedLine.get(0).y, 0.0001);
861 assertEquals("z of 0", p0.z + (p1.z - p0.z) * i / 10, extractedLine.get(0).z, 0.0001);
862 }
863 else
864 {
865 assertEquals("x of 0", p1.x + (p2.x - p1.x) * (i - 10) / 100, extractedLine.get(0).x, 0.0001);
866 assertEquals("y of 0", p1.y + (p2.y - p1.y) * (i - 10) / 100, extractedLine.get(0).y, 0.0001);
867 assertEquals("z of 0", p1.z + (p2.z - p1.z) * (i - 10) / 100, extractedLine.get(0).z, 0.0001);
868 }
869 if (j < 10)
870 {
871 assertEquals("x of 1", p0.x + (p1.x - p0.x) * j / 10, extractedLine.get(1).x, 0.0001);
872 assertEquals("y of 1", p0.y + (p1.y - p0.y) * j / 10, extractedLine.get(1).y, 0.0001);
873 assertEquals("z of 1", p0.z + (p1.z - p0.z) * j / 10, extractedLine.get(1).z, 0.0001);
874 }
875 else
876 {
877 assertEquals("x of last", p1.x + (p2.x - p1.x) * (j - 10) / 100, extractedLine.getLast().x, 0.0001);
878 assertEquals("y of last", p1.y + (p2.y - p1.y) * (j - 10) / 100, extractedLine.getLast().y, 0.0001);
879 assertEquals("z of last", p1.z + (p2.z - p1.z) * (j - 10) / 100, extractedLine.getLast().z, 0.0001);
880 }
881 if (extractedLine.size() > 2)
882 {
883 assertEquals("x of mid", p1.x, extractedLine.get(1).x, 0.0001);
884 assertEquals("y of mid", p1.y, extractedLine.get(1).y, 0.0001);
885 assertEquals("z of mid", p1.z, extractedLine.get(1).z, 0.0001);
886 }
887 }
888 }
889 }
890 }
891 }
892
893
894
895
896
897 @Test
898 public final void offsetLineTest() throws OTSGeometryException
899 {
900 OTSPoint3D from = new OTSPoint3D(1, 2, 3);
901 OTSPoint3D to = new OTSPoint3D(4, 3, 2);
902 OTSLine3D line = new OTSLine3D(from, to);
903 double lineLengthHorizontal = new OTSPoint3D(from.x, from.y).distanceSI(new OTSPoint3D(to.x, to.y));
904 for (int step = -5; step <= 5; step++)
905 {
906 OTSLine3D offsetLine = line.offsetLine(step);
907 assertEquals("Offset line of a single straight segment has two points", 2, offsetLine.size());
908 assertEquals("Distance between start points should be equal to offset", Math.abs(step),
909 offsetLine.getFirst().horizontalDistanceSI(line.getFirst()), 0.0001);
910 assertEquals("Distance between end points should be equal to offset", Math.abs(step),
911 offsetLine.getLast().horizontalDistanceSI(line.getLast()), 0.0001);
912
913
914
915 assertEquals("Length of offset line of straight segment should equal length of reference line",
916 lineLengthHorizontal, offsetLine.getLengthSI(), 0.001);
917 }
918 OTSPoint3D via = new OTSPoint3D(4, 3, 3);
919 line = new OTSLine3D(from, via, to);
920 for (int step = -5; step <= 5; step++)
921 {
922 OTSLine3D offsetLine = line.offsetLine(step);
923
924
925
926 assertTrue("Offset line has > 2 points", 2 <= offsetLine.size());
927 assertEquals("Distance between start points should be equal to offset", Math.abs(step),
928 offsetLine.getFirst().horizontalDistanceSI(line.getFirst()), 0.0001);
929 assertEquals("Distance between end points should be equal to offset", Math.abs(step),
930 offsetLine.getLast().horizontalDistanceSI(line.getLast()), 0.0001);
931 }
932 }
933
934
935
936
937
938 @Test
939 public final void testFilter() throws OTSGeometryException
940 {
941 OTSPoint3D from = new OTSPoint3D(1, 2, 3);
942 OTSPoint3D to = new OTSPoint3D(4, 3, 2);
943 for (int steps = 0; steps < 10; steps++)
944 {
945 List<OTSPoint3D> points = new ArrayList<>(2 + steps);
946 points.add(from);
947 for (int i = 0; i < steps; i++)
948 {
949 points.add(OTSPoint3D.interpolate(1.0 * (i + 1) / (steps + 2), from, to));
950 }
951 points.add(to);
952 OTSLine3D line = new OTSLine3D(points);
953
954 double segmentLength = line.getFirst().distanceSI(line.get(1));
955 OTSLine3D filteredLine = line.noiseFilteredLine(segmentLength * 0.9);
956 assertEquals("filtering with a filter that is smaller than any segment should return the original", line.size(),
957 filteredLine.size());
958 filteredLine = line.noiseFilteredLine(segmentLength * 1.1);
959 int expectedSize = 2 + steps / 2;
960 assertEquals("filtering with a filter slightly larger than each segment should return a line with " + expectedSize
961 + " points", expectedSize, filteredLine.size());
962 filteredLine = line.noiseFilteredLine(segmentLength * 2.1);
963
964 expectedSize = 2 + (steps - 1) / 3;
965 assertEquals("filtering with a filter slightly larger than twice the length of each segment should return a "
966 + "line with " + expectedSize + " points", expectedSize, filteredLine.size());
967
968 points.clear();
969 points.add(from);
970 for (int i = 1; i < 10; i++)
971 {
972 points.add(new OTSPoint3D(from.x + 0.0001 * i, from.y + 0.0001 * i, from.z));
973 }
974 points.add(from);
975 line = new OTSLine3D(points);
976 filteredLine = line.noiseFilteredLine(0.2);
977 assertEquals("filter returns line of three points", 3, filteredLine.getPoints().length);
978 assertEquals("first point matches", from, filteredLine.getPoints()[0]);
979 assertEquals("last point matches", from, filteredLine.getPoints()[filteredLine.getPoints().length - 1]);
980 assertNotEquals("intermediate point differs from first point", from, filteredLine.getPoints()[1]);
981 }
982 }
983
984
985
986
987
988 @Test
989 public final void testFractionalProjection() throws OTSGeometryException
990 {
991 Direction zeroDir = Direction.ZERO;
992
993 OTSLine3D line = new OTSLine3D(new OTSPoint3D(0, 0), new OTSPoint3D(1, 1), new OTSPoint3D(2, 0), new OTSPoint3D(3, 1),
994 new OTSPoint3D(4, 0));
995 double fraction;
996 fraction = line.projectFractional(zeroDir, zeroDir, 1.5, -5.0, FractionalFallback.ORTHOGONAL);
997 checkGetLocation(line, fraction, new OTSPoint3D(1.5, .5, 0), Math.atan2(-1, 1));
998 fraction = line.projectFractional(zeroDir, zeroDir, 1.5, 5.0, FractionalFallback.ORTHOGONAL);
999 checkGetLocation(line, fraction, new OTSPoint3D(1.5, .5, 0), Math.atan2(-1, 1));
1000 fraction = line.projectFractional(zeroDir, zeroDir, 2.5, -5.0, FractionalFallback.ORTHOGONAL);
1001 checkGetLocation(line, fraction, new OTSPoint3D(2.5, .5, 0), Math.atan2(1, 1));
1002 fraction = line.projectFractional(zeroDir, zeroDir, 2.5, 5.0, FractionalFallback.ORTHOGONAL);
1003 checkGetLocation(line, fraction, new OTSPoint3D(2.5, .5, 0), Math.atan2(1, 1));
1004
1005 line = new OTSLine3D(new OTSPoint3D(0, 0), new OTSPoint3D(2, 2), new OTSPoint3D(4, 4), new OTSPoint3D(6, 6));
1006 fraction = line.projectFractional(zeroDir, zeroDir, 2, 4, FractionalFallback.ORTHOGONAL);
1007 checkGetLocation(line, fraction, new OTSPoint3D(3, 3, 0), Math.atan2(1, 1));
1008 fraction = line.projectFractional(zeroDir, zeroDir, 4, 2, FractionalFallback.ORTHOGONAL);
1009 checkGetLocation(line, fraction, new OTSPoint3D(3, 3, 0), Math.atan2(1, 1));
1010
1011 line = new OTSLine3D(new OTSPoint3D(-2, -2), new OTSPoint3D(2, -2), new OTSPoint3D(2, 2), new OTSPoint3D(-2, 2));
1012 for (double f = 0; f < 0; f += .1)
1013 {
1014 fraction = line.projectFractional(zeroDir, zeroDir, 1, -1 + f * 2, FractionalFallback.ORTHOGONAL);
1015
1016
1017
1018 checkGetLocation(line, fraction, new OTSPoint3D(2, -2 + f * 4, 0), Math.atan2(1, 0));
1019 }
1020
1021 double[] e = new double[] { 1e-3, 1e-6, 1e-9, 1e-12, 1e-16, 1e-32 };
1022 double[] d = new double[] { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e9, 1e12 };
1023 for (int i = 0; i < e.length; i++)
1024 {
1025 line = new OTSLine3D(new OTSPoint3D(e[i], 0), new OTSPoint3D(2 + e[i], 2), new OTSPoint3D(4, 4),
1026 new OTSPoint3D(6, 6 - e[i]), new OTSPoint3D(8, 8 - e[i]));
1027 for (int j = 0; j < d.length; j++)
1028 {
1029
1030 fraction = line.projectFractional(zeroDir, zeroDir, 4 - d[j], 4 + d[j], FractionalFallback.ENDPOINT);
1031 if (Math.abs(fraction - 0.5) > 0.001)
1032 {
1033 line.projectFractional(zeroDir, zeroDir, 4 - d[j], 4 + d[j], FractionalFallback.ENDPOINT);
1034 }
1035 if (e[i] >= 1e-3)
1036 {
1037 assertTrue("Projection of point on outside of very slight bend was wrong with e=" + e[i] + " and d=" + d[j],
1038 Math.abs(fraction - 0.5) < 0.001);
1039 }
1040 else
1041 {
1042 assertTrue("Projection of point on outside of very slight bend was wrong with e=" + e[i] + " and d=" + d[j],
1043 fraction >= 0.0 && fraction <= 1.0);
1044 }
1045 }
1046 }
1047
1048 Direction start = new Direction(Math.PI / 2, DirectionUnit.EAST_RADIAN);
1049 Direction end = new Direction(-Math.PI / 2, DirectionUnit.EAST_RADIAN);
1050 for (double radius : new double[] { 1e16, 1e12, 1e9, 1e6, 1e3, 1, 0.1, 1e-3, 1e-6, 1e-9, 1e-12 })
1051 {
1052 for (int n : new int[] { 9, 10, 901, 1000 })
1053 {
1054 List<OTSPoint3D> list = new ArrayList<>();
1055 for (double r = 0; r <= Math.PI; r += Math.PI / n)
1056 {
1057 list.add(new OTSPoint3D(Math.cos(r) * radius, Math.sin(r) * radius));
1058 }
1059 line = new OTSLine3D(list);
1060 for (double x : new double[] { 0, 1e-3, 1e-6, 1e-9, 1e-12 })
1061 {
1062 for (double y : new double[] { 0, 1e-3, 1e-6, 1e-9, 1e-12 })
1063 {
1064 double f = line.projectFractional(start, end, x, y, FractionalFallback.ORTHOGONAL);
1065 assertTrue("Fractional projection on circle is not between 0.0 and 1.0.", f >= 0.0 && f <= 1.0);
1066 }
1067 }
1068 }
1069 }
1070
1071 Random random = new Random(0);
1072 for (int n = 0; n < 10; n++)
1073 {
1074
1075 List<OTSPoint3D> list = new ArrayList<>();
1076 double prevX = 0;
1077 for (int i = 0; i < 100; i++)
1078 {
1079 double x = prevX + random.nextDouble() - 0.4;
1080 prevX = x;
1081 list.add(new OTSPoint3D(x, random.nextDouble()));
1082
1083 }
1084 line = new OTSLine3D(list);
1085 for (double x = -2; x < 12; x += 0.01)
1086 {
1087 for (double y = -1; y <= 2; y += 0.1)
1088 {
1089 double f = line.projectFractional(zeroDir, zeroDir, x, y, FractionalFallback.ORTHOGONAL);
1090 assertTrue("Fractional projection on circle is not between 0.0 and 1.0.", f >= 0.0 && f <= 1.0);
1091 }
1092 }
1093 }
1094
1095 line = new OTSLine3D(new OTSPoint3D(0, 0), new OTSPoint3D(1, 1));
1096 fraction = line.projectFractional(zeroDir, zeroDir, .5, 1, FractionalFallback.ORTHOGONAL);
1097 assertTrue("Projection on line with single segment is not correct.", Math.abs(fraction - 0.5) < 0.001);
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119 }
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130 @Test
1131 public final void testFind() throws OTSGeometryException, NoSuchMethodException, SecurityException, IllegalAccessException,
1132 IllegalArgumentException, InvocationTargetException
1133 {
1134
1135 List<OTSPoint3D> points = new ArrayList<>();
1136 for (int i = 0; i < 20; i++)
1137 {
1138 points.add(new OTSPoint3D(Math.pow(2, i) - 1, 10, 20));
1139 }
1140 OTSLine3D line = new OTSLine3D(points);
1141 double end = points.get(points.size() - 1).x;
1142 Method findMethod = line.getClass().getDeclaredMethod("find", double.class);
1143 findMethod.setAccessible(true);
1144 for (int i = 0; i < end; i++)
1145 {
1146 double pos = i + 0.5;
1147 int index = (int) findMethod.invoke(line, pos);
1148 assertTrue("segment starts before pos", line.get(index).x <= pos);
1149 assertTrue("next segment starts after pos", line.get(index + 1).x >= pos);
1150 }
1151 assertEquals("pos 0 returns index 0", 0, (int) findMethod.invoke(line, 0.0));
1152 }
1153
1154
1155
1156
1157
1158 @Test
1159 public final void testTruncate() throws OTSGeometryException
1160 {
1161 OTSPoint3D from = new OTSPoint3D(10, 20, 30);
1162 OTSPoint3D to = new OTSPoint3D(70, 80, 90);
1163 double length = from.distanceSI(to);
1164 OTSLine3D line = new OTSLine3D(from, to);
1165 OTSLine3D truncatedLine = line.truncate(length);
1166 assertEquals("Start of line truncated at full length is the same as start of the input line", truncatedLine.get(0),
1167 from);
1168 assertEquals("End of line truncated at full length is about the same as end of input line", 0,
1169 truncatedLine.get(1).distance(to).si, 0.0001);
1170 try
1171 {
1172 line.truncate(-0.1);
1173 fail("truncate at negative length should have thrown OTSGeometryException");
1174 }
1175 catch (OTSGeometryException e)
1176 {
1177
1178 }
1179 try
1180 {
1181 line.truncate(length + 0.1);
1182 fail("truncate at length beyond length of line should have thrown OTSGeometryException");
1183 }
1184 catch (OTSGeometryException e)
1185 {
1186
1187 }
1188 truncatedLine = line.truncate(length / 2);
1189 assertEquals("Start of truncated line is the same as start of the input line", truncatedLine.get(0), from);
1190 OTSPoint3D halfWay = new OTSPoint3D((from.x + to.x) / 2, (from.y + to.y) / 2, (from.z + to.z) / 2);
1191 assertEquals("End of 50%, truncated 2-point line should be at the half way point", 0,
1192 halfWay.distanceSI(truncatedLine.get(1)), 0.0001);
1193 OTSPoint3D intermediatePoint = new OTSPoint3D(20, 20, 20);
1194 line = new OTSLine3D(from, intermediatePoint, to);
1195 length = from.distance(intermediatePoint).plus(intermediatePoint.distance(to)).si;
1196 truncatedLine = line.truncate(length);
1197 assertEquals("Start of line truncated at full length is the same as start of the input line", truncatedLine.get(0),
1198 from);
1199 assertEquals("End of line truncated at full length is about the same as end of input line", 0,
1200 truncatedLine.get(2).distance(to).si, 0.0001);
1201 truncatedLine = line.truncate(from.distanceSI(intermediatePoint));
1202 assertEquals("Start of line truncated at full length is the same as start of the input line", truncatedLine.get(0),
1203 from);
1204 assertEquals("Line truncated at intermediate point ends at that intermediate point", 0,
1205 truncatedLine.get(1).distanceSI(intermediatePoint), 0.0001);
1206 }
1207
1208
1209
1210
1211
1212 @Test
1213 public void testRadius() throws OTSGeometryException
1214 {
1215
1216 OTSLine3D line = new OTSLine3D(new OTSPoint3D[] { new OTSPoint3D(10, 20, 30), new OTSPoint3D(20, 30, 30) });
1217 Length radius = line.getProjectedRadius(0.5);
1218 assertTrue("should be NaN", Double.isNaN(radius.getSI()));
1219
1220 line = new OTSLine3D(
1221 new OTSPoint3D[] { new OTSPoint3D(10, 20, 30), new OTSPoint3D(20, 30, 30), new OTSPoint3D(30, 40, 30) });
1222 radius = line.getProjectedRadius(0.5);
1223 assertTrue("should be NaN", Double.isNaN(radius.getSI()));
1224
1225 line = new OTSLine3D(
1226 new OTSPoint3D[] { new OTSPoint3D(10, 30, 30), new OTSPoint3D(20, 30, 30), new OTSPoint3D(30, 40, 30) });
1227
1228 for (int step = 0; step <= 10; step++)
1229 {
1230 double fraction = step / 10.0;
1231 radius = line.getProjectedRadius(fraction);
1232 assertEquals("radius should be about 12", 12, radius.si, 0.1);
1233 }
1234 System.out.println("radius is " + radius);
1235
1236 line = new OTSLine3D(new OTSPoint3D[] { new OTSPoint3D(10, 30, 30), new OTSPoint3D(20, 30, 30),
1237 new OTSPoint3D(30, 40, 30), new OTSPoint3D(30, 30, 30) });
1238 System.out.println(line.toPlot());
1239 double boundary = 1 / (2 + Math.sqrt(2));
1240 double length = line.getLengthSI();
1241 for (int percentage = 0; percentage <= 100; percentage++)
1242 {
1243 double fraction = percentage / 100.0;
1244 double radiusAtFraction = line.getProjectedRadius(fraction).si;
1245 OTSPoint3D pointAtFraction = new OTSPoint3D(line.getLocationSI(fraction * length));
1246
1247
1248 if (fraction < boundary)
1249 {
1250 assertEquals("in first segment radius should be about 12", 12, radiusAtFraction, 0.1);
1251 }
1252 else
1253 {
1254 assertEquals("in other segments radius shoudl be about -2", -2, radiusAtFraction, 0.1);
1255 }
1256 }
1257 }
1258
1259 }