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.core.network.OTSNetwork;
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 OTSNetwork 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 OTSNetwork 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 boolean[] crossed = new boolean[4];
355 Iterator<Intersection> iterator = intersections.iterator();
356 double f1Start = Double.NaN;
357 double f2Start = Double.NaN;
358 double f2End = Double.NaN;
359 while (iterator.hasNext())
360 {
361 Intersection intersection = iterator.next();
362
363 if (Double.isNaN(f1Start))
364 {
365 f1Start = intersection.getFraction1();
366 }
367 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
368 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
369
370 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
371
372 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
373 || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
374 {
375 if (dir2.isMinus())
376 {
377 double f2Temp = f2Start;
378 f2Start = f2End;
379 f2End = f2Temp;
380 }
381 buildCrossingConflict(lane1, dir1, f1Start, intersection.getFraction1(), lane2, dir2, f2Start, f2End, gtuType,
382 simulator, widthGenerator, permitted);
383 f1Start = Double.NaN;
384 f2Start = Double.NaN;
385 f2End = Double.NaN;
386 }
387 }
388
389 }
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406 @SuppressWarnings("checkstyle:parameternumber")
407 private static void buildMergeConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
408 final Lane lane2, final GTUDirectionality dir2, final double f2start, final GTUType gtuType,
409 final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator, final boolean permitted)
410 throws NetworkException, OTSGeometryException
411 {
412
413
414 double f1end = dir1.isPlus() ? 1.0 : 0.0;
415 double f2end = dir2.isPlus() ? 1.0 : 0.0;
416
417
418 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1start);
419 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2start);
420 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
421 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
422
423
424 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
425 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
426
427
428 ConflictRule conflictRule;
429 if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
430 {
431 Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
432 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
433 conflictRule = new BusStopConflictRule(simulator);
434 }
435 else
436 {
437 conflictRule = new DefaultConflictRule();
438 }
439
440
441 Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1, dir1,
442 geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459 @SuppressWarnings("checkstyle:parameternumber")
460 private static void buildSplitConflict(final Lane lane1, final GTUDirectionality dir1, final double f1end, final Lane lane2,
461 final GTUDirectionality dir2, final double f2end, final GTUType gtuType,
462 final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
463 throws NetworkException, OTSGeometryException
464 {
465
466
467 double f1start = dir1.isPlus() ? 0.0 : 1.0;
468 double f2start = dir2.isPlus() ? 0.0 : 1.0;
469
470
471 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1start);
472 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2start);
473 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
474 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
475
476
477 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
478 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
479
480
481 Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
482 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
483 }
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502 @SuppressWarnings("checkstyle:parameternumber")
503 private static void buildCrossingConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
504 final double f1end, final Lane lane2, final GTUDirectionality dir2, final double f2start, final double f2end,
505 final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
506 final boolean permitted) throws NetworkException, OTSGeometryException
507 {
508
509
510
511 double f1startDirected;
512 double f2startDirected;
513 if ((dir1.isPlus() && f1end < f1start) || (dir1.isMinus() && f1end > f1start))
514 {
515 f1startDirected = f1end;
516 }
517 else
518 {
519 f1startDirected = f1start;
520 }
521 if ((dir2.isPlus() && f2end < f2start) || (dir2.isMinus() && f2end > f2start))
522 {
523 f2startDirected = f2end;
524 }
525 else
526 {
527 f2startDirected = f2start;
528 }
529
530
531 Length longitudinalPosition1 = lane1.getLength().multiplyBy(f1startDirected);
532 Length longitudinalPosition2 = lane2.getLength().multiplyBy(f2startDirected);
533 Length length1 = lane1.getLength().multiplyBy(Math.abs(f1end - f1start));
534 Length length2 = lane2.getLength().multiplyBy(Math.abs(f2end - f2start));
535
536
537 OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
538 OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
539
540
541 ConflictRule conflictRule;
542 if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
543 {
544 Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
545 IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
546 conflictRule = new BusStopConflictRule(simulator);
547 }
548 else
549 {
550 conflictRule = new DefaultConflictRule();
551 }
552
553
554 Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
555 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
556 }
557
558
559
560
561
562
563
564
565
566
567 private static OTSLine3D getGeometry(final Lane lane, final double fStart, final double fEnd,
568 final WidthGenerator widthGenerator) throws OTSGeometryException
569 {
570
571 double f1;
572 double f2;
573 if (fEnd > fStart)
574 {
575 f1 = fStart;
576 f2 = fEnd;
577 }
578 else
579 {
580 f1 = fEnd;
581 f2 = fStart;
582 }
583 OTSLine3D centerLine = lane.getCenterLine().extractFractional(f1, f2);
584 OTSLine3D left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
585 OTSLine3D right =
586 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
587 OTSPoint3D[] points = new OTSPoint3D[left.size() + right.size()];
588 System.arraycopy(left.getPoints(), 0, points, 0, left.size());
589 System.arraycopy(right.getPoints(), 0, points, left.size(), right.size());
590 return new OTSLine3D(points);
591 }
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606 private static class Intersection implements Comparable<Intersection>
607 {
608
609
610 private final double fraction1;
611
612
613 private final double fraction2;
614
615
616 private final int combo;
617
618
619
620
621
622
623 Intersection(final double fraction1, final double fraction2, final int combo)
624 {
625 this.fraction1 = fraction1;
626 this.fraction2 = fraction2;
627 this.combo = combo;
628 }
629
630
631
632
633 public final double getFraction1()
634 {
635 return this.fraction1;
636 }
637
638
639
640
641 public final double getFraction2()
642 {
643 return this.fraction2;
644 }
645
646
647
648
649 public final int getCombo()
650 {
651 return this.combo;
652 }
653
654
655 @Override
656 public int compareTo(final Intersection o)
657 {
658 int out = Double.compare(this.fraction1, o.fraction1);
659 if (out != 0)
660 {
661 return out;
662 }
663 out = Double.compare(this.fraction2, o.fraction2);
664 if (out != 0)
665 {
666 return out;
667 }
668 return Integer.compare(this.combo, o.combo);
669 }
670
671
672 @Override
673 public int hashCode()
674 {
675 final int prime = 31;
676 int result = 1;
677 result = prime * result + this.combo;
678 long temp;
679 temp = Double.doubleToLongBits(this.fraction1);
680 result = prime * result + (int) (temp ^ (temp >>> 32));
681 temp = Double.doubleToLongBits(this.fraction2);
682 result = prime * result + (int) (temp ^ (temp >>> 32));
683 return result;
684 }
685
686
687 @Override
688 public boolean equals(final Object obj)
689 {
690 if (this == obj)
691 {
692 return true;
693 }
694 if (obj == null)
695 {
696 return false;
697 }
698 if (getClass() != obj.getClass())
699 {
700 return false;
701 }
702 Intersection other = (Intersection) obj;
703 if (this.combo != other.combo)
704 {
705 return false;
706 }
707 if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
708 {
709 return false;
710 }
711 if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
712 {
713 return false;
714 }
715 return true;
716 }
717
718
719
720
721
722
723
724
725
726 public static SortedSet<Intersection> getIntersectionList(final OTSLine3D line1, final OTSLine3D line2, final int combo)
727 throws OTSGeometryException
728 {
729 SortedSet<Intersection> out = new TreeSet<>();
730
731 double cumul1 = 0.0;
732 OTSPoint3D start1 = null;
733 OTSPoint3D end1 = line1.get(0);
734 for (int i = 0; i < line1.size() - 1; i++)
735 {
736 start1 = end1;
737 end1 = line1.get(i + 1);
738
739 double cumul2 = 0.0;
740 OTSPoint3D start2 = null;
741 OTSPoint3D end2 = line2.get(0);
742
743 for (int j = 0; j < line2.size() - 1; j++)
744 {
745 start2 = end2;
746 end2 = line2.get(j + 1);
747
748 OTSPoint3D p = OTSPoint3D.intersectionOfLineSegments(start1, end1, start2, end2);
749 if (p != null)
750 {
751
752 double dx = p.x - start1.x;
753 double dy = p.y - start1.y;
754 double length1 = cumul1 + Math.sqrt(dx * dx + dy * dy);
755 dx = p.x - start2.x;
756 dy = p.y - start2.y;
757 double length2 = cumul2 + Math.sqrt(dx * dx + dy * dy);
758 out.add(new Intersection(length1 / line1.getLengthSI(), length2 / line2.getLengthSI(), combo));
759 }
760
761 double dx = end2.x - start2.x;
762 double dy = end2.y - start2.y;
763 cumul2 += Math.sqrt(dx * dx + dy * dy);
764 }
765
766 double dx = end1.x - start1.x;
767 double dy = end1.y - start1.y;
768 cumul1 += Math.sqrt(dx * dx + dy * dy);
769 }
770
771 return out;
772 }
773
774
775 @Override
776 public String toString()
777 {
778 return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
779 + "]";
780 }
781
782 }
783
784
785
786
787
788
789
790
791
792
793
794
795
796 public interface WidthGenerator
797 {
798
799
800
801
802
803
804
805 double getWidth(Lane lane, double fraction);
806
807 }
808
809
810
811
812
813
814
815
816
817
818
819
820
821 public static class FixedWidthGenerator implements WidthGenerator
822 {
823
824
825 private final double width;
826
827
828
829
830
831 public FixedWidthGenerator(final Length width)
832 {
833 this.width = width.si;
834 }
835
836
837 @Override
838 public final double getWidth(final Lane lane, final double fraction)
839 {
840 return this.width;
841 }
842
843
844 @Override
845 public final String toString()
846 {
847 return "FixedWidthGenerator [width=" + this.width + "]";
848 }
849
850 }
851
852
853
854
855
856
857
858
859
860
861
862
863
864 public static class RelativeWidthGenerator implements WidthGenerator
865 {
866
867
868 private final double factor;
869
870
871
872
873
874 public RelativeWidthGenerator(final double factor)
875 {
876 this.factor = factor;
877 }
878
879
880 @Override
881 public final double getWidth(final Lane lane, final double fraction)
882 {
883 return lane.getWidth(fraction).si * this.factor;
884 }
885
886
887 @Override
888 public final String toString()
889 {
890 return "RelativeWidthGenerator [factor=" + this.factor + "]";
891 }
892
893 }
894
895 }