1 package org.opentrafficsim.road.network.lane.conflict;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Map.Entry;
8 import java.util.SortedSet;
9 import java.util.TreeSet;
10
11 import org.djunits.value.vdouble.scalar.Length;
12 import org.opentrafficsim.core.dsol.OTSDEVSSimulatorInterface;
13 import org.opentrafficsim.core.geometry.OTSGeometryException;
14 import org.opentrafficsim.core.geometry.OTSLine3D;
15 import org.opentrafficsim.core.geometry.OTSPoint3D;
16 import org.opentrafficsim.core.gtu.GTUDirectionality;
17 import org.opentrafficsim.core.gtu.GTUType;
18 import org.opentrafficsim.core.network.Link;
19 import org.opentrafficsim.core.network.NetworkException;
20 import org.opentrafficsim.core.network.OTSNetwork;
21 import org.opentrafficsim.road.network.lane.CrossSectionElement;
22 import org.opentrafficsim.road.network.lane.CrossSectionLink;
23 import org.opentrafficsim.road.network.lane.Lane;
24
25 import nl.tudelft.simulation.immutablecollections.ImmutableMap;
26 import nl.tudelft.simulation.language.Throw;
27
28
29
30
31
32
33
34
35
36
37
38
39 public final class ConflictBuilder
40 {
41
42
43 public static final DefaultWidthGenerator DEFAULT_WIDTH_GENERATOR = new DefaultWidthGenerator();
44
45
46
47
48 private ConflictBuilder()
49 {
50
51 }
52
53
54
55
56
57
58
59
60
61 public static void buildConflicts(final OTSNetwork network, final GTUType gtuType,
62 final OTSDEVSSimulatorInterface simulator, final WidthGenerator widthGenerator) throws OTSGeometryException
63 {
64 buildConflicts(network, gtuType, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
65 }
66
67
68
69
70
71
72
73
74
75
76
77 public static void buildConflicts(final OTSNetwork network, final GTUType gtuType,
78 final OTSDEVSSimulatorInterface simulator, final WidthGenerator widthGenerator,
79 final LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
80 {
81
82 ImmutableMap<String, Link> links = network.getLinkMap();
83 List<Lane> lanes = new ArrayList<>();
84 for (String linkId : links.keySet())
85 {
86 Link link = links.get(linkId);
87 if (link instanceof CrossSectionLink)
88 {
89 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
90 {
91 if (element instanceof Lane)
92 {
93 lanes.add((Lane) element);
94 }
95 }
96 }
97 }
98 buildConflicts(lanes, gtuType, simulator, widthGenerator, ignoreList, permittedList);
99 }
100
101
102
103
104
105
106
107
108
109 public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
110 final WidthGenerator widthGenerator) throws OTSGeometryException
111 {
112 buildConflicts(lanes, gtuType, simulator, widthGenerator, new LaneCombinationList(), new LaneCombinationList());
113 }
114
115
116
117
118
119
120
121
122
123
124
125 public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
126 final WidthGenerator widthGenerator, final LaneCombinationList ignoreList, final LaneCombinationList permittedList)
127 throws OTSGeometryException
128 {
129
130 for (int i = 0; i < lanes.size(); i++)
131 {
132 Lane lane1 = lanes.get(i);
133 GTUDirectionality[] dirs1;
134 if (lane1.getDirectionality(gtuType).isForward())
135 {
136 dirs1 = new GTUDirectionality[] { GTUDirectionality.DIR_PLUS };
137 }
138 else if (lane1.getDirectionality(gtuType).isBackward())
139 {
140 dirs1 = new GTUDirectionality[] { GTUDirectionality.DIR_MINUS };
141 }
142 else
143 {
144 dirs1 = new GTUDirectionality[] { GTUDirectionality.DIR_PLUS, GTUDirectionality.DIR_MINUS };
145 }
146 for (GTUDirectionality dir1 : dirs1)
147 {
148 Map<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
149 Map<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
150
151 for (int j = i + 1; j < lanes.size(); j++)
152 {
153
154 Lane lane2 = lanes.get(j);
155 if (ignoreList.contains(lane1, lane2))
156 {
157 continue;
158 }
159 boolean permitted = permittedList.contains(lane1, lane2);
160
161 GTUDirectionality[] dirs2;
162 if (lane2.getDirectionality(gtuType).isForward())
163 {
164 dirs2 = new GTUDirectionality[] { GTUDirectionality.DIR_PLUS };
165 }
166 else if (lane2.getDirectionality(gtuType).isBackward())
167 {
168 dirs2 = new GTUDirectionality[] { GTUDirectionality.DIR_MINUS };
169 }
170 else
171 {
172 dirs2 = new GTUDirectionality[] { GTUDirectionality.DIR_PLUS, GTUDirectionality.DIR_MINUS };
173 }
174 for (GTUDirectionality dir2 : dirs2)
175 {
176 Map<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
177 Map<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
178
179 try
180 {
181 buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator,
182 widthGenerator);
183 }
184 catch (NetworkException ne)
185 {
186 throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
187 }
188 }
189 }
190 }
191 }
192
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206 @SuppressWarnings("checkstyle:parameternumber")
207 public static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Lane lane2,
208 final GTUDirectionality dir2, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
209 final WidthGenerator widthGenerator) throws OTSGeometryException
210 {
211 buildConflicts(lane1, dir1, lane2, dir2, gtuType, simulator, widthGenerator, false);
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226 @SuppressWarnings("checkstyle:parameternumber")
227 public static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Lane lane2,
228 final GTUDirectionality dir2, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
229 final WidthGenerator widthGenerator, final boolean permitted) throws OTSGeometryException
230 {
231 Map<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
232 Map<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
233 Map<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
234 Map<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
235 try
236 {
237 buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator, widthGenerator);
238 }
239 catch (NetworkException ne)
240 {
241 throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
242 }
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 @SuppressWarnings("checkstyle:parameternumber")
263 private static void buildConflicts(final Lane lane1, final GTUDirectionality dir1, final Map<Lane, GTUDirectionality> down1,
264 final Map<Lane, GTUDirectionality> up1, final Lane lane2, final GTUDirectionality dir2,
265 final Map<Lane, GTUDirectionality> down2, final Map<Lane, GTUDirectionality> up2, final GTUType gtuType,
266 final boolean permitted, final OTSDEVSSimulatorInterface simulator, final WidthGenerator widthGenerator)
267 throws OTSGeometryException, NetworkException
268 {
269
270
271
272
273 OTSLine3D line1 = lane1.getCenterLine();
274 OTSLine3D line2 = lane2.getCenterLine();
275 OTSLine3D left1 = line1.offsetLine(widthGenerator.getWidth(lane1, 0.0) / 2, widthGenerator.getWidth(lane1, 1.0) / 2);
276 OTSLine3D right1 = line1.offsetLine(-widthGenerator.getWidth(lane1, 0.0) / 2, -widthGenerator.getWidth(lane1, 1.0) / 2);
277 OTSLine3D left2 = line2.offsetLine(widthGenerator.getWidth(lane2, 0.0) / 2, widthGenerator.getWidth(lane2, 1.0) / 2);
278 OTSLine3D right2 = line2.offsetLine(-widthGenerator.getWidth(lane2, 0.0) / 2, -widthGenerator.getWidth(lane2, 1.0) / 2);
279
280
281 SortedSet<Intersection> intersections = Intersection.getIntersectionList(left1, left2, 0);
282 intersections.addAll(Intersection.getIntersectionList(left1, right2, 1));
283 intersections.addAll(Intersection.getIntersectionList(right1, left2, 2));
284 intersections.addAll(Intersection.getIntersectionList(right1, right2, 3));
285
286
287 Iterator<Entry<Lane, GTUDirectionality>> iterator1 = down1.entrySet().iterator();
288 Iterator<Entry<Lane, GTUDirectionality>> iterator2 = down2.entrySet().iterator();
289 boolean merge = false;
290 while (iterator1.hasNext() && !merge)
291 {
292 Entry<Lane, GTUDirectionality> next1 = iterator1.next();
293 while (iterator2.hasNext() && !merge)
294 {
295 Entry<Lane, GTUDirectionality> next2 = iterator2.next();
296 if (next1.equals(next2))
297 {
298
299 double fraction1 = Double.NaN;
300 double fraction2 = Double.NaN;
301 for (Intersection intersection : intersections)
302 {
303
304 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
305 {
306 fraction1 = intersection.getFraction1();
307 fraction2 = intersection.getFraction2();
308 }
309 }
310
311 Iterator<Intersection> iterator = intersections.iterator();
312 while (iterator.hasNext())
313 {
314 if (iterator.next().getFraction1() >= fraction1)
315 {
316 iterator.remove();
317 }
318 }
319
320 buildMergeConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator,
321 permitted);
322
323 merge = true;
324 }
325 }
326 }
327
328
329 iterator1 = up1.entrySet().iterator();
330 iterator2 = up2.entrySet().iterator();
331 boolean split = false;
332 while (iterator1.hasNext() && !split)
333 {
334 Entry<Lane, GTUDirectionality> prev1 = iterator1.next();
335 while (iterator2.hasNext() && !split)
336 {
337 Entry<Lane, GTUDirectionality> prev2 = iterator2.next();
338 if (prev1.equals(prev2))
339 {
340
341 double fraction1 = Double.NaN;
342 double fraction2 = Double.NaN;
343 for (Intersection intersection : intersections)
344 {
345
346 if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
347 {
348 fraction1 = intersection.getFraction1();
349 fraction2 = intersection.getFraction2();
350 break;
351 }
352 }
353
354 Iterator<Intersection> iterator = intersections.iterator();
355 while (iterator.hasNext())
356 {
357 if (iterator.next().getFraction1() <= fraction1)
358 {
359 iterator.remove();
360 }
361 else
362 {
363
364 break;
365 }
366 }
367
368 buildSplitConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator);
369
370 split = true;
371 }
372 }
373 }
374
375
376 boolean[] crossed = new boolean[4];
377 Iterator<Intersection> iterator = intersections.iterator();
378 double f1Start = Double.NaN;
379 double f2Start = Double.NaN;
380 while (iterator.hasNext())
381 {
382 Intersection intersection = iterator.next();
383
384 if (Double.isNaN(f1Start))
385 {
386 f1Start = intersection.getFraction1();
387 f2Start = intersection.getFraction2();
388 }
389
390 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
391
392 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
393 || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
394 {
395 buildCrossingConflict(lane1, dir1, f1Start, intersection.getFraction1(), lane2, dir2, f2Start,
396 intersection.getFraction2(), gtuType, simulator, widthGenerator, permitted);
397 f1Start = Double.NaN;
398 f2Start = Double.NaN;
399 }
400 }
401
402 }
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419 @SuppressWarnings("checkstyle:parameternumber")
420 private static void buildMergeConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
421 final Lane lane2, final GTUDirectionality dir2, final double f2start, final GTUType gtuType,
422 final OTSDEVSSimulatorInterface simulator, final WidthGenerator widthGenerator, final boolean permitted)
423 throws NetworkException, OTSGeometryException
424 {
425
426
427 double f1end = dir1.isPlus() ? 1.0 : 0.0;
428 double f2end = dir2.isPlus() ? 1.0 : 0.0;
429
430
431 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1start);
432 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2start);
433 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
434 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
435
436
437 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
438 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
439
440
441 ConflictRule conflictRule;
442 if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
443 {
444 Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
445 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
446 conflictRule = new BusStopConflictRule(simulator);
447 }
448 else
449 {
450 conflictRule = new DefaultConflictRule();
451 }
452
453
454 Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1, dir1,
455 geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472 @SuppressWarnings("checkstyle:parameternumber")
473 private static void buildSplitConflict(final Lane lane1, final GTUDirectionality dir1, final double f1end, final Lane lane2,
474 final GTUDirectionality dir2, final double f2end, final GTUType gtuType, final OTSDEVSSimulatorInterface simulator,
475 final WidthGenerator widthGenerator) throws NetworkException, OTSGeometryException
476 {
477
478
479 double f1start = dir1.isPlus() ? 0.0 : 1.0;
480 double f2start = dir2.isPlus() ? 0.0 : 1.0;
481
482
483 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1start);
484 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2start);
485 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
486 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
487
488
489 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
490 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
491
492
493 Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
494 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
495 }
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514 @SuppressWarnings("checkstyle:parameternumber")
515 private static void buildCrossingConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
516 final double f1end, final Lane lane2, final GTUDirectionality dir2, final double f2start, final double f2end,
517 final GTUType gtuType, final OTSDEVSSimulatorInterface simulator, final WidthGenerator widthGenerator,
518 final boolean permitted) throws NetworkException, OTSGeometryException
519 {
520
521
522
523 double f1startDirected;
524 double f2startDirected;
525 if ((dir1.isPlus() && f1end < f1start) || (dir1.isMinus() && f1end > f1start))
526 {
527 f1startDirected = f1end;
528 }
529 else
530 {
531 f1startDirected = f1start;
532 }
533 if ((dir2.isPlus() && f2end < f2start) || (dir2.isMinus() && f2end > f2start))
534 {
535 f2startDirected = f2end;
536 }
537 else
538 {
539 f2startDirected = f2start;
540 }
541
542
543 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1startDirected);
544 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2startDirected);
545 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
546 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
547
548
549 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
550 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
551
552
553 ConflictRule conflictRule;
554 if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
555 {
556 Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
557 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
558 conflictRule = new BusStopConflictRule(simulator);
559 }
560 else
561 {
562 conflictRule = new DefaultConflictRule();
563 }
564
565
566 Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
567 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
568 }
569
570
571
572
573
574
575
576
577
578
579 private static OTSLine3D getGeometry(final Lane lane, final double fStart, final double fEnd,
580 final WidthGenerator widthGenerator) throws OTSGeometryException
581 {
582
583 double f1;
584 double f2;
585 if (fEnd > fStart)
586 {
587 f1 = fStart;
588 f2 = fEnd;
589 }
590 else
591 {
592 f1 = fEnd;
593 f2 = fStart;
594 }
595 OTSLine3D centerLine = lane.getCenterLine().extractFractional(f1, f2);
596 OTSLine3D left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
597 OTSLine3D right =
598 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
599 OTSPoint3D[] points = new OTSPoint3D[left.size() + right.size()];
600 System.arraycopy(left.getPoints(), 0, points, 0, left.size());
601 System.arraycopy(right.getPoints(), 0, points, left.size(), right.size());
602 return new OTSLine3D(points);
603 }
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618 private static class Intersection implements Comparable<Intersection>
619 {
620
621
622 private final double fraction1;
623
624
625 private final double fraction2;
626
627
628 private final int combo;
629
630
631
632
633
634
635 Intersection(final double fraction1, final double fraction2, final int combo)
636 {
637 this.fraction1 = fraction1;
638 this.fraction2 = fraction2;
639 this.combo = combo;
640 }
641
642
643
644
645 public final double getFraction1()
646 {
647 return this.fraction1;
648 }
649
650
651
652
653 public final double getFraction2()
654 {
655 return this.fraction2;
656 }
657
658
659
660
661 public final int getCombo()
662 {
663 return this.combo;
664 }
665
666
667 @Override
668 public int compareTo(final Intersection o)
669 {
670 int out = Double.compare(this.fraction1, o.fraction1);
671 if (out != 0)
672 {
673 return out;
674 }
675 out = Double.compare(this.fraction2, o.fraction2);
676 if (out != 0)
677 {
678 return out;
679 }
680 return Integer.compare(this.combo, o.combo);
681 }
682
683
684 @Override
685 public int hashCode()
686 {
687 final int prime = 31;
688 int result = 1;
689 result = prime * result + this.combo;
690 long temp;
691 temp = Double.doubleToLongBits(this.fraction1);
692 result = prime * result + (int) (temp ^ (temp >>> 32));
693 temp = Double.doubleToLongBits(this.fraction2);
694 result = prime * result + (int) (temp ^ (temp >>> 32));
695 return result;
696 }
697
698
699 @Override
700 public boolean equals(final Object obj)
701 {
702 if (this == obj)
703 {
704 return true;
705 }
706 if (obj == null)
707 {
708 return false;
709 }
710 if (getClass() != obj.getClass())
711 {
712 return false;
713 }
714 Intersection other = (Intersection) obj;
715 if (this.combo != other.combo)
716 {
717 return false;
718 }
719 if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
720 {
721 return false;
722 }
723 if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
724 {
725 return false;
726 }
727 return true;
728 }
729
730
731
732
733
734
735
736
737
738 public static SortedSet<Intersection> getIntersectionList(final OTSLine3D line1, final OTSLine3D line2, final int combo)
739 throws OTSGeometryException
740 {
741 SortedSet<Intersection> out = new TreeSet<>();
742
743 double cumul1 = 0.0;
744 OTSPoint3D start1 = null;
745 OTSPoint3D end1 = line1.get(0);
746 for (int i = 0; i < line1.size() - 1; i++)
747 {
748 start1 = end1;
749 end1 = line1.get(i + 1);
750
751 double cumul2 = 0.0;
752 OTSPoint3D start2 = null;
753 OTSPoint3D end2 = line2.get(0);
754
755 for (int j = 0; j < line2.size() - 1; j++)
756 {
757 start2 = end2;
758 end2 = line2.get(j + 1);
759
760 OTSPoint3D p = OTSPoint3D.intersectionOfLineSegments(start1, end1, start2, end2);
761 if (p != null)
762 {
763
764 double dx = p.x - start1.x;
765 double dy = p.y - start1.y;
766 double length1 = cumul1 + Math.sqrt(dx * dx + dy * dy);
767 dx = p.x - start2.x;
768 dy = p.y - start2.y;
769 double length2 = cumul2 + Math.sqrt(dx * dx + dy * dy);
770 out.add(new Intersection(length1 / line1.getLengthSI(), length2 / line2.getLengthSI(), combo));
771 }
772
773 double dx = end2.x - start2.x;
774 double dy = end2.y - start2.y;
775 cumul2 += Math.sqrt(dx * dx + dy * dy);
776 }
777
778 double dx = end1.x - start1.x;
779 double dy = end1.y - start1.y;
780 cumul1 += Math.sqrt(dx * dx + dy * dy);
781 }
782
783 return out;
784 }
785
786
787 @Override
788 public String toString()
789 {
790 return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
791 + "]";
792 }
793
794 }
795
796
797
798
799
800
801
802
803
804
805
806
807
808 public interface WidthGenerator
809 {
810
811
812
813
814
815
816
817 default double getWidth(final Lane lane, final double fraction)
818 {
819 return lane.getWidth(fraction).si;
820 }
821
822 }
823
824
825
826
827
828
829
830
831
832
833
834
835
836 public static class DefaultWidthGenerator implements WidthGenerator
837 {
838
839 }
840
841
842
843
844
845
846
847
848
849
850
851
852
853 public static class FixedWidthGenerator implements WidthGenerator
854 {
855
856
857 private final double width;
858
859
860
861
862
863 public FixedWidthGenerator(final Length width)
864 {
865 this.width = width.si;
866 }
867
868
869 @Override
870 public final double getWidth(final Lane lane, final double fraction)
871 {
872 return this.width;
873 }
874
875 }
876
877
878
879
880
881
882
883
884
885
886
887
888
889 public static class RelativeWidthGenerator implements WidthGenerator
890 {
891
892
893 private final double factor;
894
895
896
897
898
899 public RelativeWidthGenerator(final double factor)
900 {
901 this.factor = factor;
902 }
903
904
905 @Override
906 public final double getWidth(final Lane lane, final double fraction)
907 {
908 return lane.getWidth(fraction).si * this.factor;
909 }
910
911 }
912
913 }