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