1 package org.opentrafficsim.kpi.sampling;
2
3 import java.util.Arrays;
4 import java.util.LinkedHashMap;
5 import java.util.Map;
6 import java.util.Set;
7
8 import org.djunits.unit.AccelerationUnit;
9 import org.djunits.unit.DurationUnit;
10 import org.djunits.unit.LengthUnit;
11 import org.djunits.unit.SpeedUnit;
12 import org.djunits.unit.TimeUnit;
13 import org.djunits.value.ValueRuntimeException;
14 import org.djunits.value.storage.StorageType;
15 import org.djunits.value.vdouble.scalar.Acceleration;
16 import org.djunits.value.vdouble.scalar.Duration;
17 import org.djunits.value.vdouble.scalar.Length;
18 import org.djunits.value.vdouble.scalar.Speed;
19 import org.djunits.value.vdouble.scalar.Time;
20 import org.djunits.value.vfloat.vector.FloatAccelerationVector;
21 import org.djunits.value.vfloat.vector.FloatLengthVector;
22 import org.djunits.value.vfloat.vector.FloatSpeedVector;
23 import org.djunits.value.vfloat.vector.FloatTimeVector;
24 import org.djunits.value.vfloat.vector.base.FloatVector;
25 import org.djutils.exceptions.Throw;
26 import org.opentrafficsim.kpi.interfaces.GtuData;
27 import org.opentrafficsim.kpi.interfaces.LaneData;
28 import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
29 import org.opentrafficsim.kpi.sampling.meta.FilterDataType;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 public final class Trajectory<G extends GtuData>
46 {
47
48
49 private static final int DEFAULT_CAPACITY = 10;
50
51
52 private int size = 0;
53
54
55
56
57
58 private float[] x = new float[DEFAULT_CAPACITY];
59
60
61 private float[] v = new float[DEFAULT_CAPACITY];
62
63
64 private float[] a = new float[DEFAULT_CAPACITY];
65
66
67 private float[] t = new float[DEFAULT_CAPACITY];
68
69
70 private final String gtuId;
71
72
73 private final Map<FilterDataType<?>, Object> filterData = new LinkedHashMap<>();
74
75
76 private final Map<ExtendedDataType<?, ?, ?, G>, Object> extendedData = new LinkedHashMap<>();
77
78
79 private final LaneData lane;
80
81
82
83
84
85
86
87 public Trajectory(final GtuData gtu, final Map<FilterDataType<?>, Object> filterData,
88 final Set<ExtendedDataType<?, ?, ?, G>> extendedData, final LaneData lane)
89 {
90 this(gtu == null ? null : gtu.getId(), filterData, extendedData, lane);
91 }
92
93
94
95
96
97
98
99
100 private Trajectory(final String gtuId, final Map<FilterDataType<?>, Object> filterData,
101 final Set<ExtendedDataType<?, ?, ?, G>> extendedData, final LaneData lane)
102 {
103 Throw.whenNull(gtuId, "GTU may not be null.");
104 Throw.whenNull(filterData, "Filter data may not be null.");
105 Throw.whenNull(extendedData, "Extended data may not be null.");
106 Throw.whenNull(lane, "Lane direction may not be null.");
107 this.gtuId = gtuId;
108 this.filterData.putAll(filterData);
109 for (ExtendedDataType<?, ?, ?, G> dataType : extendedData)
110 {
111 this.extendedData.put(dataType, dataType.initializeStorage());
112 }
113 this.lane = lane;
114 }
115
116
117
118
119
120
121
122
123
124 public void add(final Length position, final Speed speed, final Acceleration acceleration, final Time time)
125 {
126 add(position, speed, acceleration, time, null);
127 }
128
129
130
131
132
133
134
135
136
137
138 public void add(final Length position, final Speed speed, final Acceleration acceleration, final Time time, final G gtu)
139 {
140 Throw.whenNull(position, "Position may not be null.");
141 Throw.whenNull(speed, "Speed may not be null.");
142 Throw.whenNull(acceleration, "Acceleration may not be null.");
143 Throw.whenNull(time, "Time may not be null.");
144 if (!this.extendedData.isEmpty())
145 {
146 Throw.whenNull(gtu, "GTU may not be null.");
147 }
148 if (this.size == this.x.length)
149 {
150 int cap = this.size + (this.size >> 1);
151 this.x = Arrays.copyOf(this.x, cap);
152 this.v = Arrays.copyOf(this.v, cap);
153 this.a = Arrays.copyOf(this.a, cap);
154 this.t = Arrays.copyOf(this.t, cap);
155 }
156 this.x[this.size] = (float) position.si;
157 this.v[this.size] = (float) speed.si;
158 this.a[this.size] = (float) acceleration.si;
159 this.t[this.size] = (float) time.si;
160 for (ExtendedDataType<?, ?, ?, G> extendedDataType : this.extendedData.keySet())
161 {
162 appendValue(extendedDataType, gtu);
163 }
164 this.size++;
165 }
166
167
168
169
170
171
172
173
174 @SuppressWarnings("unchecked")
175 private <T, S> void appendValue(final ExtendedDataType<T, ?, S, G> extendedDataType, final G gtu)
176 {
177 S in = (S) this.extendedData.get(extendedDataType);
178 S out = extendedDataType.setValue(in, this.size, extendedDataType.getValue(gtu));
179 if (in != out)
180 {
181 this.extendedData.put(extendedDataType, out);
182 }
183 }
184
185
186
187
188
189 public int size()
190 {
191 return this.size;
192 }
193
194
195
196
197
198 public String getGtuId()
199 {
200 return this.gtuId;
201 }
202
203
204
205
206
207 public float[] getX()
208 {
209 return Arrays.copyOf(this.x, this.size);
210 }
211
212
213
214
215
216 public float[] getV()
217 {
218 return Arrays.copyOf(this.v, this.size);
219 }
220
221
222
223
224
225 public float[] getA()
226 {
227 return Arrays.copyOf(this.a, this.size);
228 }
229
230
231
232
233
234 public float[] getT()
235 {
236 return Arrays.copyOf(this.t, this.size);
237 }
238
239
240
241
242
243
244 public int binarySearchX(final float position)
245 {
246 if (this.x[0] >= position)
247 {
248 return 0;
249 }
250 int index = Arrays.binarySearch(this.x, 0, this.size, position);
251 return index < 0 ? -index - 2 : index;
252 }
253
254
255
256
257
258
259 public int binarySearchT(final float time)
260 {
261 if (this.t[0] >= time)
262 {
263 return 0;
264 }
265 int index = Arrays.binarySearch(this.t, 0, this.size, time);
266 return index < 0 ? -index - 2 : index;
267 }
268
269
270
271
272
273
274
275 public float getX(final int index) throws SamplingException
276 {
277 checkSample(index);
278 return this.x[index];
279 }
280
281
282
283
284
285
286
287 public float getV(final int index) throws SamplingException
288 {
289 checkSample(index);
290 return this.v[index];
291 }
292
293
294
295
296
297
298
299 public float getA(final int index) throws SamplingException
300 {
301 checkSample(index);
302 return this.a[index];
303 }
304
305
306
307
308
309
310
311 public float getT(final int index) throws SamplingException
312 {
313 checkSample(index);
314 return this.t[index];
315 }
316
317
318
319
320
321
322
323
324
325
326 @SuppressWarnings("unchecked")
327 public <T, S> T getExtendedData(final ExtendedDataType<T, ?, S, ?> extendedDataType, final int index)
328 throws SamplingException
329 {
330 checkSample(index);
331 return extendedDataType.getStorageValue((S) this.extendedData.get(extendedDataType), index);
332 }
333
334
335
336
337
338
339 private void checkSample(final int index) throws SamplingException
340 {
341 Throw.when(index < 0 || index >= this.size, SamplingException.class, "Index is out of bounds.");
342 }
343
344
345
346
347
348 public FloatLengthVector getPosition()
349 {
350 try
351 {
352 return FloatVector.instantiate(getX(), LengthUnit.SI, StorageType.DENSE);
353 }
354 catch (ValueRuntimeException exception)
355 {
356
357 throw new RuntimeException("Could not return trajectory data.", exception);
358 }
359 }
360
361
362
363
364
365 public FloatSpeedVector getSpeed()
366 {
367 try
368 {
369 return FloatVector.instantiate(getV(), SpeedUnit.SI, StorageType.DENSE);
370 }
371 catch (ValueRuntimeException exception)
372 {
373
374 throw new RuntimeException("Could not return trajectory data.", exception);
375 }
376 }
377
378
379
380
381
382 public FloatAccelerationVector getAcceleration()
383 {
384 try
385 {
386 return FloatVector.instantiate(getA(), AccelerationUnit.SI, StorageType.DENSE);
387 }
388 catch (ValueRuntimeException exception)
389 {
390
391 throw new RuntimeException("Could not return trajectory data.", exception);
392 }
393 }
394
395
396
397
398
399 public FloatTimeVector getTime()
400 {
401 try
402 {
403 return FloatVector.instantiate(getT(), TimeUnit.BASE_SECOND, StorageType.DENSE);
404 }
405 catch (ValueRuntimeException exception)
406 {
407
408 throw new RuntimeException("Could not return trajectory data.", exception);
409 }
410 }
411
412
413
414
415
416
417 public Length getTotalLength()
418 {
419
420
421 if (this.size == 0)
422 {
423 return Length.ZERO;
424 }
425 return new Length(this.x[this.size - 1] - this.x[0], LengthUnit.SI);
426 }
427
428
429
430
431
432
433 public Duration getTotalDuration()
434 {
435
436
437 if (this.size == 0)
438 {
439 return Duration.ZERO;
440 }
441 return new Duration(this.t[this.size - 1] - this.t[0], DurationUnit.SI);
442 }
443
444
445
446
447
448
449 public boolean contains(final FilterDataType<?> filterDataType)
450 {
451 return this.filterData.containsKey(filterDataType);
452 }
453
454
455
456
457
458
459
460 @SuppressWarnings("unchecked")
461 public <T> T getFilterData(final FilterDataType<T> filterDataType)
462 {
463 return (T) this.filterData.get(filterDataType);
464 }
465
466
467
468
469
470 public Set<FilterDataType<?>> getFilterDataTypes()
471 {
472 return this.filterData.keySet();
473 }
474
475
476
477
478
479
480 public boolean contains(final ExtendedDataType<?, ?, ?, ?> extendedDataType)
481 {
482 return this.extendedData.containsKey(extendedDataType);
483 }
484
485
486
487
488
489
490
491
492
493 @SuppressWarnings("unchecked")
494 public <O, S> O getExtendedData(final ExtendedDataType<?, O, S, ?> extendedDataType) throws SamplingException
495 {
496 Throw.when(!this.extendedData.containsKey(extendedDataType), SamplingException.class,
497 "Extended data type %s is not in the trajectory.", extendedDataType);
498 return extendedDataType.convert((S) this.extendedData.get(extendedDataType), this.size);
499 }
500
501
502
503
504
505 public Set<ExtendedDataType<?, ?, ?, G>> getExtendedDataTypes()
506 {
507 return this.extendedData.keySet();
508 }
509
510
511
512
513
514
515
516
517
518
519 public SpaceTimeView getSpaceTimeView(final Length startPosition, final Length endPosition, final Time startTime,
520 final Time endTime)
521 {
522 if (size() == 0)
523 {
524 return new SpaceTimeView(Length.ZERO, Duration.ZERO);
525 }
526 Boundaries bounds = spaceBoundaries(startPosition, endPosition).intersect(timeBoundaries(startTime, endTime));
527 double xFrom;
528 double tFrom;
529 if (bounds.fFrom > 0.0)
530 {
531 xFrom = this.x[bounds.from] * (1 - bounds.fFrom) + this.x[bounds.from + 1] * bounds.fFrom;
532 tFrom = this.t[bounds.from] * (1 - bounds.fFrom) + this.t[bounds.from + 1] * bounds.fFrom;
533 }
534 else
535 {
536 xFrom = this.x[bounds.from];
537 tFrom = this.t[bounds.from];
538 }
539 double xTo;
540 double tTo;
541 if (bounds.fTo > 0.0)
542 {
543 xTo = this.x[bounds.to] * (1 - bounds.fTo) + this.x[bounds.to + 1] * bounds.fTo;
544 tTo = this.t[bounds.to] * (1 - bounds.fTo) + this.t[bounds.to + 1] * bounds.fTo;
545 }
546 else
547 {
548 xTo = this.x[bounds.to];
549 tTo = this.t[bounds.to];
550 }
551 return new SpaceTimeView(Length.instantiateSI(xTo - xFrom), Duration.instantiateSI(tTo - tFrom));
552 }
553
554
555
556
557
558
559
560
561
562
563 public Trajectory<G> subSet(final Length startPosition, final Length endPosition)
564 {
565 Throw.whenNull(startPosition, "Start position may not be null");
566 Throw.whenNull(endPosition, "End position may not be null");
567 Throw.when(startPosition.gt(endPosition), IllegalArgumentException.class,
568 "Start position should be smaller than end position in the direction of travel");
569 if (this.size == 0)
570 {
571 return new Trajectory<>(this.gtuId, this.filterData, this.extendedData.keySet(), this.lane);
572 }
573 return subSet(spaceBoundaries(startPosition, endPosition));
574 }
575
576
577
578
579
580
581
582
583
584 public Trajectory<G> subSet(final Time startTime, final Time endTime)
585 {
586 Throw.whenNull(startTime, "Start time may not be null");
587 Throw.whenNull(endTime, "End time may not be null");
588 Throw.when(startTime.gt(endTime), IllegalArgumentException.class, "Start time should be smaller than end time.");
589 if (this.size == 0)
590 {
591 return new Trajectory<>(this.gtuId, this.filterData, this.extendedData.keySet(), this.lane);
592 }
593 return subSet(timeBoundaries(startTime, endTime));
594 }
595
596
597
598
599
600
601
602
603
604
605
606 public Trajectory<G> subSet(final Length startPosition, final Length endPosition, final Time startTime, final Time endTime)
607 {
608
609 Throw.whenNull(startPosition, "Start position may not be null");
610 Throw.whenNull(endPosition, "End position may not be null");
611 Throw.when(startPosition.gt(endPosition), IllegalArgumentException.class,
612 "Start position should be smaller than end position in the direction of travel");
613 Throw.whenNull(startTime, "Start time may not be null");
614 Throw.whenNull(endTime, "End time may not be null");
615 Throw.when(startTime.gt(endTime), IllegalArgumentException.class, "Start time should be smaller than end time.");
616 if (this.size == 0)
617 {
618 return new Trajectory<>(this.gtuId, this.filterData, this.extendedData.keySet(), this.lane);
619 }
620 return subSet(spaceBoundaries(startPosition, endPosition).intersect(timeBoundaries(startTime, endTime)));
621 }
622
623
624
625
626
627
628
629 private Boundaries spaceBoundaries(final Length startPosition, final Length endPosition)
630 {
631 if (startPosition.si > this.x[this.size - 1] || endPosition.si < this.x[0])
632 {
633 return new Boundaries(0, 0.0, 0, 0.0);
634 }
635
636 float startPos = (float) startPosition.si;
637 float endPos = (float) endPosition.si;
638 Boundary from = getBoundaryAtPosition(startPos, false);
639 Boundary to = getBoundaryAtPosition(endPos, true);
640 return new Boundaries(from.index, from.fraction, to.index, to.fraction);
641 }
642
643
644
645
646
647
648
649 private Boundaries timeBoundaries(final Time startTime, final Time endTime)
650 {
651 if (startTime.si > this.t[this.size - 1] || endTime.si < this.t[0])
652 {
653 return new Boundaries(0, 0.0, 0, 0.0);
654 }
655
656 float startTim = (float) startTime.si;
657 float endTim = (float) endTime.si;
658 Boundary from = getBoundaryAtTime(startTim, false);
659 Boundary to = getBoundaryAtTime(endTim, true);
660 return new Boundaries(from.index, from.fraction, to.index, to.fraction);
661 }
662
663
664
665
666
667
668
669 private Boundary getBoundaryAtPosition(final float position, final boolean end)
670 {
671 int index = binarySearchX(position);
672 double fraction = 0;
673 if (end ? index < this.size - 1 : this.x[index] < position)
674 {
675 fraction = (position - this.x[index]) / (this.x[index + 1] - this.x[index]);
676 }
677 return new Boundary(index, fraction);
678 }
679
680
681
682
683
684
685
686 private Boundary getBoundaryAtTime(final float time, final boolean end)
687 {
688 int index = binarySearchT(time);
689 double fraction = 0;
690 if (end ? index < this.size - 1 : this.t[index] < time)
691 {
692 fraction = (time - this.t[index]) / (this.t[index + 1] - this.t[index]);
693 }
694 return new Boundary(index, fraction);
695 }
696
697
698
699
700
701
702 public Time getTimeAtPosition(final Length position)
703 {
704 return Time.instantiateSI(getBoundaryAtPosition((float) position.si, false).getValue(this.t));
705 }
706
707
708
709
710
711
712 public Speed getSpeedAtPosition(final Length position)
713 {
714 return Speed.instantiateSI(getBoundaryAtPosition((float) position.si, false).getValue(this.v));
715 }
716
717
718
719
720
721
722 public Acceleration getAccelerationAtPosition(final Length position)
723 {
724 return Acceleration.instantiateSI(getBoundaryAtPosition((float) position.si, false).getValue(this.a));
725 }
726
727
728
729
730
731
732 public Length getPositionAtTime(final Time time)
733 {
734 return Length.instantiateSI(getBoundaryAtTime((float) time.si, false).getValue(this.x));
735 }
736
737
738
739
740
741
742 public Speed getSpeedAtTime(final Time time)
743 {
744 return Speed.instantiateSI(getBoundaryAtTime((float) time.si, false).getValue(this.v));
745 }
746
747
748
749
750
751
752 public Acceleration getAccelerationAtTime(final Time time)
753 {
754 return Acceleration.instantiateSI(getBoundaryAtTime((float) time.si, false).getValue(this.a));
755 }
756
757
758
759
760
761
762
763
764 @SuppressWarnings("unchecked")
765 private <T, S> Trajectory<G> subSet(final Boundaries bounds)
766 {
767 Trajectory<G> out = new Trajectory<>(this.gtuId, this.filterData, this.extendedData.keySet(), this.lane);
768 if (bounds.from < bounds.to)
769 {
770 int nBefore = bounds.fFrom < 1.0 ? 1 : 0;
771 int nAfter = bounds.fTo > 0.0 ? 1 : 0;
772 int n = bounds.to - bounds.from + nBefore + nAfter;
773 out.x = new float[n];
774 out.v = new float[n];
775 out.a = new float[n];
776 out.t = new float[n];
777 System.arraycopy(this.x, bounds.from + 1, out.x, nBefore, bounds.to - bounds.from);
778 System.arraycopy(this.v, bounds.from + 1, out.v, nBefore, bounds.to - bounds.from);
779 System.arraycopy(this.a, bounds.from + 1, out.a, nBefore, bounds.to - bounds.from);
780 System.arraycopy(this.t, bounds.from + 1, out.t, nBefore, bounds.to - bounds.from);
781 if (nBefore == 1)
782 {
783 out.x[0] = (float) (this.x[bounds.from] * (1 - bounds.fFrom) + this.x[bounds.from + 1] * bounds.fFrom);
784 out.v[0] = (float) (this.v[bounds.from] * (1 - bounds.fFrom) + this.v[bounds.from + 1] * bounds.fFrom);
785 out.a[0] = (float) (this.a[bounds.from] * (1 - bounds.fFrom) + this.a[bounds.from + 1] * bounds.fFrom);
786 out.t[0] = (float) (this.t[bounds.from] * (1 - bounds.fFrom) + this.t[bounds.from + 1] * bounds.fFrom);
787 }
788 if (nAfter == 1)
789 {
790 out.x[n - 1] = (float) (this.x[bounds.to] * (1 - bounds.fTo) + this.x[bounds.to + 1] * bounds.fTo);
791 out.v[n - 1] = (float) (this.v[bounds.to] * (1 - bounds.fTo) + this.v[bounds.to + 1] * bounds.fTo);
792 out.a[n - 1] = (float) (this.a[bounds.to] * (1 - bounds.fTo) + this.a[bounds.to + 1] * bounds.fTo);
793 out.t[n - 1] = (float) (this.t[bounds.to] * (1 - bounds.fTo) + this.t[bounds.to + 1] * bounds.fTo);
794 }
795 out.size = n;
796 for (ExtendedDataType<?, ?, ?, G> extendedDataType : this.extendedData.keySet())
797 {
798 int j = 0;
799 ExtendedDataType<T, ?, S, G> edt = (ExtendedDataType<T, ?, S, G>) extendedDataType;
800 S fromList = (S) this.extendedData.get(extendedDataType);
801 S toList = edt.initializeStorage();
802 try
803 {
804 if (nBefore == 1)
805 {
806 toList = edt.setValue(toList, j,
807 ((ExtendedDataType<T, ?, ?, G>) extendedDataType).interpolate(
808 edt.getStorageValue(fromList, bounds.from),
809 edt.getStorageValue(fromList, bounds.from + 1), bounds.fFrom));
810 j++;
811 }
812 for (int i = bounds.from + 1; i <= bounds.to; i++)
813 {
814 toList = edt.setValue(toList, j, edt.getStorageValue(fromList, i));
815 j++;
816 }
817 if (nAfter == 1)
818 {
819 toList = edt.setValue(toList, j,
820 ((ExtendedDataType<T, ?, ?, G>) extendedDataType).interpolate(
821 edt.getStorageValue(fromList, bounds.to), edt.getStorageValue(fromList, bounds.to + 1),
822 bounds.fTo));
823 }
824 }
825 catch (SamplingException se)
826 {
827
828 throw new RuntimeException("Error while obtaining subset of trajectory.", se);
829 }
830 out.extendedData.put(extendedDataType, toList);
831 }
832 }
833 return out;
834 }
835
836
837 @Override
838 public int hashCode()
839 {
840 final int prime = 31;
841 int result = 1;
842 result = prime * result + ((this.gtuId == null) ? 0 : this.gtuId.hashCode());
843 result = prime * result + this.size;
844 if (this.size > 0)
845 {
846 result = prime * result + Float.floatToIntBits(this.t[0]);
847 }
848 return result;
849 }
850
851
852 @Override
853 public boolean equals(final Object obj)
854 {
855 if (this == obj)
856 {
857 return true;
858 }
859 if (obj == null)
860 {
861 return false;
862 }
863 if (getClass() != obj.getClass())
864 {
865 return false;
866 }
867 Trajectory<?> other = (Trajectory<?>) obj;
868 if (this.size != other.size)
869 {
870 return false;
871 }
872 if (this.gtuId == null)
873 {
874 if (other.gtuId != null)
875 {
876 return false;
877 }
878 }
879 else if (!this.gtuId.equals(other.gtuId))
880 {
881 return false;
882 }
883 if (this.size > 0)
884 {
885 if (this.t[0] != other.t[0])
886 {
887 return false;
888 }
889 }
890 return true;
891 }
892
893
894 @Override
895 public String toString()
896 {
897 if (this.size > 0)
898 {
899 return "Trajectory [size=" + this.size + ", x={" + this.x[0] + "..." + this.x[this.size - 1] + "}, t={" + this.t[0]
900 + "..." + this.t[this.size - 1] + "}, filterData=" + this.filterData + ", gtuId=" + this.gtuId + "]";
901 }
902 return "Trajectory [size=" + this.size + ", x={}, t={}, filterData=" + this.filterData + ", gtuId=" + this.gtuId + "]";
903 }
904
905
906
907
908
909
910
911
912
913
914
915
916 public class Boundary
917 {
918
919 @SuppressWarnings("checkstyle:visibilitymodifier")
920 public final int index;
921
922
923 @SuppressWarnings("checkstyle:visibilitymodifier")
924 public final double fraction;
925
926
927
928
929
930 Boundary(final int index, final double fraction)
931 {
932 this.index = index;
933 this.fraction = fraction;
934 }
935
936
937 @Override
938 public final String toString()
939 {
940 return "Boundary [index=" + this.index + ", fraction=" + this.fraction + "]";
941 }
942
943
944
945
946
947
948 public double getValue(final float[] array)
949 {
950 if (this.fraction == 0.0)
951 {
952 return array[this.index];
953 }
954 if (this.fraction == 1.0)
955 {
956 return array[this.index + 1];
957 }
958 return (1 - this.fraction) * array[this.index] + this.fraction * array[this.index + 1];
959 }
960 }
961
962
963
964
965
966
967
968
969
970
971
972
973 private class Boundaries
974 {
975
976 @SuppressWarnings("checkstyle:visibilitymodifier")
977 public final int from;
978
979
980 @SuppressWarnings("checkstyle:visibilitymodifier")
981 public final double fFrom;
982
983
984 @SuppressWarnings("checkstyle:visibilitymodifier")
985 public final int to;
986
987
988 @SuppressWarnings("checkstyle:visibilitymodifier")
989 public final double fTo;
990
991
992
993
994
995
996
997 Boundaries(final int from, final double fFrom, final int to, final double fTo)
998 {
999 Throw.when(from < 0 || from > Trajectory.this.size() - 1, IllegalArgumentException.class,
1000 "Argument from (%d) is out of bounds.", from);
1001 Throw.when(fFrom < 0 || fFrom > 1, IllegalArgumentException.class, "Argument fFrom (%f) is out of bounds.", fFrom);
1002 Throw.when(from == Trajectory.this.size() && fFrom > 0, IllegalArgumentException.class,
1003 "Arguments from (%d) and fFrom (%f) are out of bounds.", from, fFrom);
1004 Throw.when(to < 0 || to >= Trajectory.this.size(), IllegalArgumentException.class,
1005 "Argument to (%d) is out of bounds.", to);
1006 Throw.when(fTo < 0 || fTo > 1, IllegalArgumentException.class, "Argument fTo (%f) is out of bounds.", fTo);
1007 Throw.when(to == Trajectory.this.size() && fTo > 0, IllegalArgumentException.class,
1008 "Arguments to (%d) and fTo (%f) are out of bounds.", to, fTo);
1009 this.from = from;
1010 this.fFrom = fFrom;
1011 this.to = to;
1012 this.fTo = fTo;
1013 }
1014
1015
1016
1017
1018
1019
1020 public Boundaries intersect(final Boundaries boundaries)
1021 {
1022 if (this.to < boundaries.from || boundaries.to < this.from
1023 || this.to == boundaries.from && this.fTo < boundaries.fFrom
1024 || boundaries.to == this.from && boundaries.fTo < this.fFrom)
1025 {
1026 return new Boundaries(0, 0.0, 0, 0.0);
1027 }
1028 int newFrom;
1029 double newFFrom;
1030 if (this.from > boundaries.from || this.from == boundaries.from && this.fFrom > boundaries.fFrom)
1031 {
1032 newFrom = this.from;
1033 newFFrom = this.fFrom;
1034 }
1035 else
1036 {
1037 newFrom = boundaries.from;
1038 newFFrom = boundaries.fFrom;
1039 }
1040 int newTo;
1041 double newFTo;
1042 if (this.to < boundaries.to || this.to == boundaries.to && this.fTo < boundaries.fTo)
1043 {
1044 newTo = this.to;
1045 newFTo = this.fTo;
1046 }
1047 else
1048 {
1049 newTo = boundaries.to;
1050 newFTo = boundaries.fTo;
1051 }
1052 return new Boundaries(newFrom, newFFrom, newTo, newFTo);
1053 }
1054
1055
1056 @Override
1057 public final String toString()
1058 {
1059 return "Boundaries [from=" + this.from + ", fFrom=" + this.fFrom + ", to=" + this.to + ", fTo=" + this.fTo + "]";
1060 }
1061
1062 }
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075 public static final class SpaceTimeView
1076 {
1077
1078
1079 private final Length distance;
1080
1081
1082 private final Duration time;
1083
1084
1085
1086
1087
1088
1089 private SpaceTimeView(final Length distance, final Duration time)
1090 {
1091 this.distance = distance;
1092 this.time = time;
1093 }
1094
1095
1096
1097
1098
1099 public Length getDistance()
1100 {
1101 return this.distance;
1102 }
1103
1104
1105
1106
1107
1108 public Duration getTime()
1109 {
1110 return this.time;
1111 }
1112
1113
1114 @Override
1115 public String toString()
1116 {
1117 return "SpaceTimeView [distance=" + this.distance + ", time=" + this.time + "]";
1118 }
1119 }
1120
1121 }