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