1 package org.opentrafficsim.core.geometry;
2
3 import java.awt.geom.Path2D;
4 import java.awt.geom.PathIterator;
5 import java.awt.geom.Rectangle2D;
6 import java.io.Serializable;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.List;
10
11 import javax.media.j3d.Bounds;
12
13 import nl.tudelft.simulation.dsol.animation.LocatableInterface;
14 import nl.tudelft.simulation.language.d3.BoundingBox;
15 import nl.tudelft.simulation.language.d3.DirectedPoint;
16
17 import org.djunits.unit.LengthUnit;
18 import org.djunits.value.vdouble.scalar.Length;
19
20 import com.vividsolutions.jts.geom.Coordinate;
21 import com.vividsolutions.jts.geom.CoordinateSequence;
22 import com.vividsolutions.jts.geom.Geometry;
23 import com.vividsolutions.jts.geom.GeometryFactory;
24 import com.vividsolutions.jts.geom.LineString;
25 import com.vividsolutions.jts.linearref.LengthIndexedLine;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 public class OTSLine3D implements LocatableInterface, Serializable
41 {
42
43 private static final long serialVersionUID = 20150722L;
44
45
46 private OTSPoint3D[] points;
47
48
49 private double[] lengthIndexedLine = null;
50
51
52 private double length = Double.NaN;
53
54
55 private OTSPoint3D centroid = null;
56
57
58 private Bounds bounds = null;
59
60 private java.awt.geom.Rectangle2D.Double boundingRect;
61
62
63
64
65
66
67
68 public OTSLine3D(final OTSPoint3D... points) throws OTSGeometryException
69 {
70 init(points);
71 }
72
73
74
75
76
77
78
79 private void init(final OTSPoint3D... pts) throws OTSGeometryException
80 {
81 if (pts.length < 2)
82 {
83 throw new OTSGeometryException("Degenerate OTSLine3D; has " + pts.length + " point"
84 + (pts.length != 1 ? "s" : ""));
85 }
86 for (int i = 1; i < pts.length; i++)
87 {
88 if (pts[i - 1].x == pts[i].x && pts[i - 1].y == pts[i].y && pts[i - 1].z == pts[i].z)
89 {
90 throw new OTSGeometryException("Degenerate OTSLine3D; point " + (i - 1)
91 + " has the same x, y and z as point " + i);
92 }
93 }
94 this.points = pts;
95 }
96
97
98 public enum OffsetMethod
99 {
100
101 JTS,
102
103
104 PK,
105
106
107 AV;
108 };
109
110
111 public static final OffsetMethod OFFSETMETHOD = OffsetMethod.PK;
112
113
114
115
116
117
118 public final OTSLine3D offsetLine(final double offset)
119 {
120 try
121 {
122 switch (OFFSETMETHOD)
123 {
124 case PK:
125 return OTSOffsetLinePK.offsetLine(this, offset);
126
127 case AV:
128 return OTSBufferingAV.offsetLine(this, offset);
129
130 case JTS:
131 return OTSBufferingJTS.offsetGeometryOLD(this, offset);
132
133 default:
134 return null;
135 }
136 }
137 catch (OTSGeometryException exception)
138 {
139 exception.printStackTrace();
140 return null;
141 }
142 }
143
144
145
146
147
148
149
150 public final OTSLine3D noiseFilteredLine(final double noiseLevel)
151 {
152 if (this.size() <= 2)
153 {
154 return this;
155 }
156 OTSPoint3D prevPoint = null;
157 List<OTSPoint3D> list = null;
158 for (int index = 0; index < this.size(); index++)
159 {
160 OTSPoint3D currentPoint = this.points[index];
161 if (null != prevPoint && prevPoint.distanceSI(currentPoint) < noiseLevel)
162 {
163 if (null == list)
164 {
165
166 list = new ArrayList<OTSPoint3D>();
167 for (int i = 0; i < index; i++)
168 {
169 list.add(this.points[i]);
170 }
171 }
172 if (index == this.size() - 1)
173 {
174 if (list.size() > 1)
175 {
176
177 list.set(list.size() - 1, currentPoint);
178 }
179 else
180 {
181
182
183 list.add(currentPoint);
184 }
185 }
186 continue;
187 }
188 else if (null != list)
189 {
190 list.add(currentPoint);
191 }
192 prevPoint = currentPoint;
193 }
194 if (null == list)
195 {
196 return this;
197 }
198 try
199 {
200 return new OTSLine3D(list);
201 }
202 catch (OTSGeometryException exception)
203 {
204 System.err.println("CANNOT HAPPEN");
205 exception.printStackTrace();
206 throw new Error(exception);
207 }
208 }
209
210
211
212
213
214
215
216
217
218 public final OTSLine3D offsetLine(final double offsetAtStart, final double offsetAtEnd) throws OTSGeometryException
219 {
220
221
222
223 OTSLine3D offsetLineAtStart = offsetLine(offsetAtStart);
224 if (offsetAtStart == offsetAtEnd)
225 {
226 return offsetLineAtStart;
227 }
228
229 OTSLine3D offsetLineAtEnd = offsetLine(offsetAtEnd);
230
231 Geometry startGeometry = offsetLineAtStart.getLineString();
232 Geometry endGeometry = offsetLineAtEnd.getLineString();
233 LengthIndexedLine first = new LengthIndexedLine(startGeometry);
234 double firstLength = startGeometry.getLength();
235 LengthIndexedLine second = new LengthIndexedLine(endGeometry);
236 double secondLength = endGeometry.getLength();
237 ArrayList<Coordinate> out = new ArrayList<Coordinate>();
238 Coordinate[] firstCoordinates = startGeometry.getCoordinates();
239 Coordinate[] secondCoordinates = endGeometry.getCoordinates();
240 int firstIndex = 0;
241 int secondIndex = 0;
242 Coordinate prevCoordinate = null;
243 final double tooClose = 0.05;
244 while (firstIndex < firstCoordinates.length && secondIndex < secondCoordinates.length)
245 {
246 double firstRatio =
247 firstIndex < firstCoordinates.length ? first.indexOf(firstCoordinates[firstIndex]) / firstLength
248 : Double.MAX_VALUE;
249 double secondRatio =
250 secondIndex < secondCoordinates.length ? second.indexOf(secondCoordinates[secondIndex]) / secondLength
251 : Double.MAX_VALUE;
252 double ratio;
253 if (firstRatio < secondRatio)
254 {
255 ratio = firstRatio;
256 firstIndex++;
257 }
258 else
259 {
260 ratio = secondRatio;
261 secondIndex++;
262 }
263 Coordinate firstCoordinate = first.extractPoint(ratio * firstLength);
264 Coordinate secondCoordinate = second.extractPoint(ratio * secondLength);
265 Coordinate resultCoordinate =
266 new Coordinate((1 - ratio) * firstCoordinate.x + ratio * secondCoordinate.x, (1 - ratio)
267 * firstCoordinate.y + ratio * secondCoordinate.y);
268 if (null == prevCoordinate || resultCoordinate.distance(prevCoordinate) > tooClose)
269 {
270 out.add(resultCoordinate);
271 prevCoordinate = resultCoordinate;
272 }
273 }
274 Coordinate[] resultCoordinates = new Coordinate[out.size()];
275 for (int index = 0; index < out.size(); index++)
276 {
277 resultCoordinates[index] = out.get(index);
278 }
279 return new OTSLine3D(resultCoordinates);
280 }
281
282
283
284
285
286
287
288
289
290
291 public final OTSLine3D offsetLine(final double[] relativeFractions, final double[] offsets)
292 throws OTSGeometryException
293 {
294 OTSLine3D[] offsetLine = new OTSLine3D[relativeFractions.length];
295 for (int i = 0; i < offsets.length; i++)
296 {
297 offsetLine[i] = offsetLine(offsets[i]);
298
299
300 }
301
302 ArrayList<Coordinate> out = new ArrayList<Coordinate>();
303 Coordinate prevCoordinate = null;
304 final double tooClose = 0.05;
305 for (int i = 0; i < offsets.length - 1; i++)
306 {
307 Geometry startGeometry =
308 offsetLine[i].extractFractional(relativeFractions[i], relativeFractions[i + 1]).getLineString();
309 Geometry endGeometry =
310 offsetLine[i + 1].extractFractional(relativeFractions[i], relativeFractions[i + 1]).getLineString();
311 LengthIndexedLine first = new LengthIndexedLine(startGeometry);
312 double firstLength = startGeometry.getLength();
313 LengthIndexedLine second = new LengthIndexedLine(endGeometry);
314 double secondLength = endGeometry.getLength();
315 Coordinate[] firstCoordinates = startGeometry.getCoordinates();
316 Coordinate[] secondCoordinates = endGeometry.getCoordinates();
317 int firstIndex = 0;
318 int secondIndex = 0;
319 while (firstIndex < firstCoordinates.length && secondIndex < secondCoordinates.length)
320 {
321 double firstRatio =
322 firstIndex < firstCoordinates.length ? first.indexOf(firstCoordinates[firstIndex]) / firstLength
323 : Double.MAX_VALUE;
324 double secondRatio =
325 secondIndex < secondCoordinates.length ? second.indexOf(secondCoordinates[secondIndex])
326 / secondLength : Double.MAX_VALUE;
327 double ratio;
328 if (firstRatio < secondRatio)
329 {
330 ratio = firstRatio;
331 firstIndex++;
332 }
333 else
334 {
335 ratio = secondRatio;
336 secondIndex++;
337 }
338 Coordinate firstCoordinate = first.extractPoint(ratio * firstLength);
339 Coordinate secondCoordinate = second.extractPoint(ratio * secondLength);
340 Coordinate resultCoordinate =
341 new Coordinate((1 - ratio) * firstCoordinate.x + ratio * secondCoordinate.x, (1 - ratio)
342 * firstCoordinate.y + ratio * secondCoordinate.y);
343 if (null == prevCoordinate || resultCoordinate.distance(prevCoordinate) > tooClose)
344 {
345 out.add(resultCoordinate);
346 prevCoordinate = resultCoordinate;
347 }
348 }
349 }
350
351 Coordinate[] resultCoordinates = new Coordinate[out.size()];
352 for (int index = 0; index < out.size(); index++)
353 {
354 resultCoordinates[index] = out.get(index);
355 }
356 return new OTSLine3D(resultCoordinates);
357 }
358
359
360
361
362
363
364
365
366 public static OTSLine3D concatenate(final OTSLine3D... lines) throws OTSGeometryException
367 {
368 return concatenate(0.0, lines);
369 }
370
371
372
373
374
375
376
377
378
379 public static OTSLine3D concatenate(final double toleranceSI, final OTSLine3D... lines) throws OTSGeometryException
380 {
381 if (0 == lines.length)
382 {
383 throw new OTSGeometryException("Empty argument list");
384 }
385 else if (1 == lines.length)
386 {
387 return lines[0];
388 }
389 int size = lines[0].size();
390 for (int i = 1; i < lines.length; i++)
391 {
392 if (lines[i - 1].getLast().distance(lines[i].getFirst()).si > toleranceSI)
393 {
394 throw new OTSGeometryException("Lines are not connected");
395 }
396 size += lines[i].size() - 1;
397 }
398 OTSPoint3D[] points = new OTSPoint3D[size];
399 int nextIndex = 0;
400 for (int i = 0; i < lines.length; i++)
401 {
402 OTSLine3D line = lines[i];
403 for (int j = 0 == i ? 0 : 1; j < line.size(); j++)
404 {
405 points[nextIndex++] = line.get(j);
406 }
407 }
408 return new OTSLine3D(points);
409 }
410
411
412
413
414
415 public final OTSLine3D reverse()
416 {
417 OTSPoint3D[] resultPoints = new OTSPoint3D[size()];
418 int nextIndex = size();
419 for (OTSPoint3D p : getPoints())
420 {
421 resultPoints[--nextIndex] = p;
422 }
423 try
424 {
425 return new OTSLine3D(resultPoints);
426 }
427 catch (OTSGeometryException exception)
428 {
429
430 throw new RuntimeException(exception);
431 }
432 }
433
434
435
436
437
438
439
440
441 public final OTSLine3D extractFractional(final double start, final double end) throws OTSGeometryException
442 {
443 if (start < 0 || start >= end || end > 1)
444 {
445 throw new OTSGeometryException("Bad interval");
446 }
447 getLength();
448 return extract(start * this.length, end * this.length);
449 }
450
451
452
453
454
455
456
457
458
459 public final OTSLine3D extract(final Length.Rel start, final Length.Rel end) throws OTSGeometryException
460 {
461 return extract(start.si, end.si);
462 }
463
464
465
466
467
468
469
470
471
472 public final OTSLine3D extract(final double start, final double end) throws OTSGeometryException
473 {
474 if (Double.isNaN(start) || Double.isNaN(end) || start < 0 || start >= end || end > getLengthSI())
475 {
476 throw new OTSGeometryException("Bad interval (" + start + ".." + end + "; length of this OTSLine3D is "
477 + this.getLengthSI() + ")");
478 }
479 double cumulativeLength = 0;
480 double nextCumulativeLength = 0;
481 double segmentLength = 0;
482 int index = 0;
483 List<OTSPoint3D> pointList = new ArrayList<>();
484
485 while (start > cumulativeLength)
486 {
487 OTSPoint3D fromPoint = this.points[index];
488 index++;
489 OTSPoint3D toPoint = this.points[index];
490 segmentLength = fromPoint.distanceSI(toPoint);
491 cumulativeLength = nextCumulativeLength;
492 nextCumulativeLength = cumulativeLength + segmentLength;
493 if (nextCumulativeLength >= start)
494 {
495 break;
496 }
497 }
498 if (start == nextCumulativeLength)
499 {
500 pointList.add(this.points[index]);
501 }
502 else
503 {
504 pointList.add(OTSPoint3D.interpolate((start - cumulativeLength) / segmentLength, this.points[index - 1],
505 this.points[index]));
506 if (end > nextCumulativeLength)
507 {
508 pointList.add(this.points[index]);
509 }
510 }
511 while (end > nextCumulativeLength)
512 {
513 OTSPoint3D fromPoint = this.points[index];
514 index++;
515 if (index >= this.points.length)
516 {
517 break;
518 }
519 OTSPoint3D toPoint = this.points[index];
520 segmentLength = fromPoint.distanceSI(toPoint);
521 cumulativeLength = nextCumulativeLength;
522 nextCumulativeLength = cumulativeLength + segmentLength;
523 if (nextCumulativeLength >= end)
524 {
525 break;
526 }
527 pointList.add(toPoint);
528 }
529 if (end == nextCumulativeLength)
530 {
531 pointList.add(this.points[index]);
532 }
533 else
534 {
535 pointList.add(OTSPoint3D.interpolate((end - cumulativeLength) / segmentLength, this.points[index - 1],
536 this.points[index]));
537 }
538 try
539 {
540 return new OTSLine3D(pointList);
541 }
542 catch (OTSGeometryException exception)
543 {
544 System.err.println("interval " + start + ".." + end + "too short");
545 throw new OTSGeometryException("interval " + start + ".." + end + "too short");
546 }
547 }
548
549
550
551
552
553
554 private static OTSPoint3D[] coordinatesToOTSPoint3D(final Coordinate[] coordinates)
555 {
556 OTSPoint3D[] result = new OTSPoint3D[coordinates.length];
557 for (int i = 0; i < coordinates.length; i++)
558 {
559 result[i] = new OTSPoint3D(coordinates[i]);
560 }
561 return result;
562 }
563
564
565
566
567
568
569
570 public static OTSLine3D createAndCleanOTSLine3D(final OTSPoint3D[] points) throws OTSGeometryException
571 {
572 if (points.length < 2)
573 {
574 throw new OTSGeometryException("Degenerate OTSLine3D; has " + points.length + " point"
575 + (points.length != 1 ? "s" : ""));
576 }
577 return createAndCleanOTSLine3D(new ArrayList<>(Arrays.asList(points)));
578 }
579
580
581
582
583
584
585
586
587 public static OTSLine3D createAndCleanOTSLine3D(final List<OTSPoint3D> pointList) throws OTSGeometryException
588 {
589
590 int i = 1;
591 while (i < pointList.size())
592 {
593 if (pointList.get(i - 1).equals(pointList.get(i)))
594 {
595 pointList.remove(i);
596 }
597 else
598 {
599 i++;
600 }
601 }
602 return new OTSLine3D(pointList);
603 }
604
605
606
607
608
609
610
611 public OTSLine3D(final Coordinate[] coordinates) throws OTSGeometryException
612 {
613 this(coordinatesToOTSPoint3D(coordinates));
614 }
615
616
617
618
619
620
621
622 public OTSLine3D(final LineString lineString) throws OTSGeometryException
623 {
624 this(lineString.getCoordinates());
625 }
626
627
628
629
630
631
632
633 public OTSLine3D(final Geometry geometry) throws OTSGeometryException
634 {
635 this(geometry.getCoordinates());
636 }
637
638
639
640
641
642
643
644 public OTSLine3D(final List<OTSPoint3D> pointList) throws OTSGeometryException
645 {
646 this(pointList.toArray(new OTSPoint3D[pointList.size()]));
647 }
648
649
650
651
652
653
654
655 public OTSLine3D(final Path2D path) throws OTSGeometryException
656 {
657 PathIterator pi = path.getPathIterator(null);
658 List<OTSPoint3D> pl = new ArrayList<>();
659 double[] p = new double[6];
660 while (!pi.isDone())
661 {
662 pi.next();
663 int segType = pi.currentSegment(p);
664 if (segType == PathIterator.SEG_MOVETO || segType == PathIterator.SEG_LINETO)
665 {
666 pl.add(new OTSPoint3D(p[0], p[1]));
667 }
668 else if (segType == PathIterator.SEG_CLOSE)
669 {
670 if (!pl.get(0).equals(pl.get(pl.size() - 1)))
671 {
672 pl.add(new OTSPoint3D(pl.get(0).x, pl.get(0).y));
673 }
674 break;
675 }
676 }
677 init(pl.toArray(new OTSPoint3D[pl.size() - 1]));
678 }
679
680
681
682
683
684 public final Coordinate[] getCoordinates()
685 {
686 Coordinate[] result = new Coordinate[size()];
687 for (int i = 0; i < size(); i++)
688 {
689 result[i] = this.points[i].getCoordinate();
690 }
691 return result;
692 }
693
694
695
696
697
698 public final LineString getLineString()
699 {
700 GeometryFactory factory = new GeometryFactory();
701 Coordinate[] coordinates = getCoordinates();
702 CoordinateSequence cs = factory.getCoordinateSequenceFactory().create(coordinates);
703 return new LineString(cs, factory);
704 }
705
706
707
708
709
710 public final int size()
711 {
712 return this.points.length;
713 }
714
715
716
717
718
719 public final OTSPoint3D getFirst()
720 {
721 return this.points[0];
722 }
723
724
725
726
727
728 public final OTSPoint3D getLast()
729 {
730 return this.points[size() - 1];
731 }
732
733
734
735
736
737
738
739 public final OTSPoint3D get(final int i) throws OTSGeometryException
740 {
741 if (i < 0 || i > size() - 1)
742 {
743 throw new OTSGeometryException("OTSLine3D.get(i=" + i + "); i<0 or i>=size(), which is " + size());
744 }
745 return this.points[i];
746 }
747
748
749
750
751
752
753 public final synchronized double getLengthSI()
754 {
755 if (Double.isNaN(this.length))
756 {
757 this.length = 0.0;
758 for (int i = 0; i < size() - 1; i++)
759 {
760 this.length += this.points[i].distanceSI(this.points[i + 1]);
761 }
762 }
763 return this.length;
764 }
765
766
767
768
769
770
771 public final Length.Rel getLength()
772 {
773 return new Length.Rel(getLengthSI(), LengthUnit.SI);
774 }
775
776
777
778
779
780 public final OTSPoint3D[] getPoints()
781 {
782 return this.points;
783 }
784
785
786
787
788 private void makeLengthIndexedLine()
789 {
790 if (this.lengthIndexedLine == null)
791 {
792 this.lengthIndexedLine = new double[this.points.length];
793 this.lengthIndexedLine[0] = 0.0;
794 for (int i = 1; i < this.points.length; i++)
795 {
796 this.lengthIndexedLine[i] =
797 this.lengthIndexedLine[i - 1] + this.points[i - 1].distanceSI(this.points[i]);
798 }
799 }
800 }
801
802
803
804
805
806
807
808 public final DirectedPoint getLocationExtended(final Length.Rel position)
809 {
810 return getLocationExtendedSI(position.getSI());
811 }
812
813
814
815
816
817
818
819 public final DirectedPoint getLocationExtendedSI(final double positionSI)
820 {
821 makeLengthIndexedLine();
822 if (positionSI >= 0.0 && positionSI <= getLengthSI())
823 {
824 try
825 {
826 return getLocationSI(positionSI);
827 }
828 catch (OTSGeometryException exception)
829 {
830
831 }
832 }
833
834
835 if (positionSI < 0.0)
836 {
837 double len = positionSI;
838 double fraction = len / (this.lengthIndexedLine[1] - this.lengthIndexedLine[0]);
839 OTSPoint3D p1 = this.points[0];
840 OTSPoint3D p2 = this.points[1];
841 return new DirectedPoint(p1.x + fraction * (p2.x - p1.x), p1.y + fraction * (p2.y - p1.y), p1.z + fraction
842 * (p2.z - p1.z), 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
843 }
844
845
846 int n1 = this.lengthIndexedLine.length - 1;
847 int n2 = this.lengthIndexedLine.length - 2;
848 double len = positionSI - getLengthSI();
849 double fraction = len / (this.lengthIndexedLine[n1] - this.lengthIndexedLine[n2]);
850 OTSPoint3D p1 = this.points[n2];
851 OTSPoint3D p2 = this.points[n1];
852 return new DirectedPoint(p2.x + fraction * (p2.x - p1.x), p2.y + fraction * (p2.y - p1.y), p2.z + fraction
853 * (p2.z - p1.z), 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
854 }
855
856
857
858
859
860
861
862 public final DirectedPoint getLocationFraction(final double fraction) throws OTSGeometryException
863 {
864 if (fraction < 0.0 || fraction > 1.0)
865 {
866 throw new OTSGeometryException("getLocationFraction for line: fraction < 0.0 or > 1.0. fraction = "
867 + fraction);
868 }
869 return getLocationSI(fraction * getLengthSI());
870 }
871
872
873
874
875
876
877
878
879 public final DirectedPoint getLocationFraction(final double fraction, final double tolerance)
880 throws OTSGeometryException
881 {
882 if (fraction < -tolerance || fraction > 1.0 + tolerance)
883 {
884 throw new OTSGeometryException(
885 "getLocationFraction for line: fraction < 0.0 - tolerance or > 1.0 + tolerance; fraction = " + fraction);
886 }
887 double f = fraction < 0 ? 0.0 : fraction > 1.0 ? 1.0 : fraction;
888 return getLocationSI(f * getLengthSI());
889 }
890
891
892
893
894
895
896 public final DirectedPoint getLocationFractionExtended(final double fraction)
897 {
898 return getLocationExtendedSI(fraction * getLengthSI());
899 }
900
901
902
903
904
905
906
907 public final DirectedPoint getLocation(final Length.Rel position) throws OTSGeometryException
908 {
909 return getLocationSI(position.getSI());
910 }
911
912
913
914
915
916
917
918 private int find(final double pos) throws OTSGeometryException
919 {
920 if (pos == 0)
921 {
922 return 0;
923 }
924
925 for (int i = 0; i < this.lengthIndexedLine.length - 2; i++)
926 {
927 if (pos > this.lengthIndexedLine[i] && pos <= this.lengthIndexedLine[i + 1])
928 {
929 return i;
930 }
931 }
932
933 return this.lengthIndexedLine.length - 2;
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957 }
958
959
960
961
962
963
964
965 public final DirectedPoint getLocationSI(final double positionSI) throws OTSGeometryException
966 {
967 makeLengthIndexedLine();
968 if (positionSI < 0.0 || positionSI > getLengthSI())
969 {
970 throw new OTSGeometryException("getLocationSI for line: position < 0.0 or > line length. Position = "
971 + positionSI + " m. Length = " + getLengthSI() + " m.");
972 }
973
974
975 if (positionSI == 0.0)
976 {
977 OTSPoint3D p1 = this.points[0];
978 OTSPoint3D p2 = this.points[1];
979 return new DirectedPoint(p1.x, p1.y, p1.z, 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
980 }
981 if (positionSI == getLengthSI())
982 {
983 OTSPoint3D p1 = this.points[this.points.length - 2];
984 OTSPoint3D p2 = this.points[this.points.length - 1];
985 return new DirectedPoint(p2.x, p2.y, p2.z, 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
986 }
987
988
989 int index = find(positionSI);
990 double remainder = positionSI - this.lengthIndexedLine[index];
991 double fraction = remainder / (this.lengthIndexedLine[index + 1] - this.lengthIndexedLine[index]);
992 OTSPoint3D p1 = this.points[index];
993 OTSPoint3D p2 = this.points[index + 1];
994 return new DirectedPoint(p1.x + fraction * (p2.x - p1.x), p1.y + fraction * (p2.y - p1.y), p1.z + fraction
995 * (p2.z - p1.z), 0.0, 0.0, Math.atan2(p2.y - p1.y, p2.x - p1.x));
996 }
997
998
999
1000
1001
1002
1003
1004 public final OTSLine3D truncate(final double lengthSI) throws OTSGeometryException
1005 {
1006 makeLengthIndexedLine();
1007 if (lengthSI <= 0.0 || lengthSI > getLengthSI())
1008 {
1009 throw new OTSGeometryException("truncate for line: position <= 0.0 or > line length. Position = "
1010 + lengthSI + " m. Length = " + getLengthSI() + " m.");
1011 }
1012
1013
1014 if (lengthSI == getLengthSI())
1015 {
1016 return new OTSLine3D(getPoints());
1017 }
1018
1019
1020 int index = find(lengthSI);
1021 double remainder = lengthSI - this.lengthIndexedLine[index];
1022 double fraction = remainder / (this.lengthIndexedLine[index + 1] - this.lengthIndexedLine[index]);
1023 OTSPoint3D p1 = this.points[index];
1024 OTSPoint3D p2 = this.points[index + 1];
1025 OTSPoint3D newLastPoint =
1026 new OTSPoint3D(p1.x + fraction * (p2.x - p1.x), p1.y + fraction * (p2.y - p1.y), p1.z + fraction
1027 * (p2.z - p1.z));
1028 OTSPoint3D[] coords = new OTSPoint3D[index + 2];
1029 for (int i = 0; i <= index; i++)
1030 {
1031 coords[i] = this.points[i];
1032 }
1033 coords[index + 1] = newLastPoint;
1034 return new OTSLine3D(coords);
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075 private void calcCentroidBounds()
1076 {
1077 double minX = Double.POSITIVE_INFINITY;
1078 double minY = Double.POSITIVE_INFINITY;
1079 double minZ = Double.POSITIVE_INFINITY;
1080 double maxX = Double.NEGATIVE_INFINITY;
1081 double maxY = Double.NEGATIVE_INFINITY;
1082 double maxZ = Double.NEGATIVE_INFINITY;
1083 for (OTSPoint3D p : this.points)
1084 {
1085 minX = Math.min(minX, p.x);
1086 minY = Math.min(minY, p.y);
1087 minZ = Math.min(minZ, p.z);
1088 maxX = Math.max(maxX, p.x);
1089 maxY = Math.max(maxY, p.y);
1090 maxZ = Math.max(maxZ, p.z);
1091 }
1092 this.centroid = new OTSPoint3D((maxX + minX) / 2, (maxY + minY) / 2, (maxZ + minZ) / 2);
1093 double deltaX = Math.max(maxX - minX, 0.5);
1094 double deltaY = Math.max(maxY - minY, 0.5);
1095 double deltaZ = Math.max(maxZ - minZ, 0.5);
1096 this.bounds = new BoundingBox(deltaX, deltaY, deltaZ);
1097 this.boundingRect = new Rectangle2D.Double(minX, minY, deltaX, deltaY);
1098 }
1099
1100
1101
1102
1103
1104 public final OTSPoint3D getCentroid()
1105 {
1106 if (this.centroid == null)
1107 {
1108 calcCentroidBounds();
1109 }
1110 return this.centroid;
1111 }
1112
1113
1114
1115
1116
1117 public final Rectangle2D getBoundingRectangle()
1118 {
1119 if (this.boundingRect == null)
1120 {
1121 calcCentroidBounds();
1122 }
1123 return this.boundingRect;
1124 }
1125
1126
1127 @Override
1128 @SuppressWarnings("checkstyle:designforextension")
1129 public DirectedPoint getLocation()
1130 {
1131 if (this.centroid == null)
1132 {
1133 calcCentroidBounds();
1134 }
1135 return this.centroid.getDirectedPoint();
1136 }
1137
1138
1139 @Override
1140 @SuppressWarnings("checkstyle:designforextension")
1141 public Bounds getBounds()
1142 {
1143 if (this.bounds == null)
1144 {
1145 calcCentroidBounds();
1146 }
1147 return this.bounds;
1148 }
1149
1150
1151 @Override
1152 @SuppressWarnings("checkstyle:designforextension")
1153 public String toString()
1154 {
1155 return Arrays.toString(this.points);
1156 }
1157
1158
1159 @Override
1160 @SuppressWarnings("checkstyle:designforextension")
1161 public int hashCode()
1162 {
1163 final int prime = 31;
1164 int result = 1;
1165 result = prime * result + Arrays.hashCode(this.points);
1166 return result;
1167 }
1168
1169
1170 @Override
1171 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
1172 public boolean equals(final Object obj)
1173 {
1174 if (this == obj)
1175 return true;
1176 if (obj == null)
1177 return false;
1178 if (getClass() != obj.getClass())
1179 return false;
1180 OTSLine3D other = (OTSLine3D) obj;
1181 if (!Arrays.equals(this.points, other.points))
1182 return false;
1183 return true;
1184 }
1185
1186
1187
1188
1189 public final String toExcel()
1190 {
1191 StringBuffer s = new StringBuffer();
1192 for (OTSPoint3D p : this.points)
1193 {
1194 s.append(p.x + "\t" + p.y + "\n");
1195 }
1196 return s.toString();
1197 }
1198
1199
1200
1201
1202
1203 public static void main(final String[] args) throws OTSGeometryException
1204 {
1205 OTSLine3D line =
1206 new OTSLine3D(new OTSPoint3D(-263.811, -86.551, 1.180), new OTSPoint3D(-262.945, -84.450, 1.180),
1207 new OTSPoint3D(-261.966, -82.074, 1.180), new OTSPoint3D(-260.890, -79.464, 1.198), new OTSPoint3D(
1208 -259.909, -76.955, 1.198), new OTSPoint3D(-258.911, -74.400, 1.198), new OTSPoint3D(-257.830,
1209 -71.633, 1.234));
1210 System.out.println(line.toExcel());
1211 double[] relativeFractions =
1212 new double[]{0.0, 0.19827228089475762, 0.30549496392494213, 0.5824753163948581, 0.6815307752261827,
1213 0.7903990449840241, 0.8942375145295614, 1.0};
1214 double[] offsets =
1215 new double[]{2.9779999256134, 4.6029999256134, 3.886839156071996, 2.3664845198627207, 1.7858981925396709,
1216 1.472348149010167, 2.0416709053157285, 2.798692100483229};
1217 System.out.println(line.offsetLine(relativeFractions, offsets).toExcel());
1218 }
1219 }