View Javadoc
1   package org.opentrafficsim.road.network.lane.conflict;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.LinkedHashMap;
6   import java.util.List;
7   import java.util.Map;
8   import java.util.SortedSet;
9   import java.util.TreeSet;
10  import java.util.concurrent.Executors;
11  import java.util.concurrent.ThreadPoolExecutor;
12  import java.util.concurrent.atomic.AtomicInteger;
13  
14  import org.djunits.value.vdouble.scalar.Length;
15  import org.djutils.exceptions.Throw;
16  import org.djutils.immutablecollections.ImmutableIterator;
17  import org.djutils.immutablecollections.ImmutableMap;
18  import org.djutils.immutablecollections.ImmutableMap.ImmutableEntry;
19  import org.djutils.logger.CategoryLogger;
20  import org.opentrafficsim.core.geometry.OTSGeometryException;
21  import org.opentrafficsim.core.geometry.OTSLine3D;
22  import org.opentrafficsim.core.geometry.OTSPoint3D;
23  import org.opentrafficsim.core.gtu.GTUDirectionality;
24  import org.opentrafficsim.core.gtu.GTUType;
25  import org.opentrafficsim.core.network.Link;
26  import org.opentrafficsim.core.network.Network;
27  import org.opentrafficsim.core.network.NetworkException;
28  import org.opentrafficsim.road.network.OTSRoadNetwork;
29  import org.opentrafficsim.road.network.lane.CrossSectionElement;
30  import org.opentrafficsim.road.network.lane.CrossSectionLink;
31  import org.opentrafficsim.road.network.lane.Lane;
32  import org.pmw.tinylog.Level;
33  
34  import nl.tudelft.simulation.dsol.logger.SimLogger;
35  import nl.tudelft.simulation.dsol.simulators.DEVSSimulatorInterface;
36  
37  /**
38   * <p>
39   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
40   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
41   * <p>
42   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 11 dec. 2016 <br>
43   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
44   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
45   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
46   */
47  // TODO use z-coordinate for intersections of lines
48  public final class ConflictBuilderParallel
49  {
50  
51      /** Default width generator for conflicts which uses 80% of the lane width. */
52      public static final WidthGenerator DEFAULT_WIDTH_GENERATOR = new RelativeWidthGenerator(0.8);
53  
54      private static AtomicInteger numberMergeConflicts = new AtomicInteger(0);
55  
56      private static AtomicInteger numberSplitConflicts = new AtomicInteger(0);
57  
58      private static AtomicInteger numberCrossConflicts = new AtomicInteger(0);
59  
60      /**
61       * Empty constructor.
62       */
63      private ConflictBuilderParallel()
64      {
65          //
66      }
67  
68      /**
69       * Build conflicts on network.
70       * @param network OTSRoadNetwork; network
71       * @param gtuType GTUType; gtu type
72       * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
73       * @param widthGenerator WidthGenerator; width generator
74       * @throws OTSGeometryException in case of geometry exception
75       */
76      public static void buildConflicts(final OTSRoadNetwork network, final GTUType gtuType,
77              final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
78              throws OTSGeometryException
79      {
80          buildConflicts(network, gtuType, simulator, widthGenerator, new LaneCombinationListrk/lane/conflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList(), new LaneCombinationList());
81      }
82  
83      /**
84       * Build conflicts on network.
85       * @param network OTSRoadNetwork; network
86       * @param gtuType GTUType; gtu type
87       * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
88       * @param widthGenerator WidthGenerator; width generator
89       * @param ignoreList LaneCombinationList; lane combinations to ignore
90       * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
91       * @throws OTSGeometryException in case of geometry exception
92       */
93      public static void buildConflicts(final OTSRoadNetwork network, final GTUType gtuType,
94              final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
95              final LaneCombinationListflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
96      {
97          // Create list of lanes
98          ImmutableMap<String, Link> links = network.getLinkMap();
99          List<Lane> lanes = new ArrayList<>();
100         for (String linkId : links.keySet())
101         {
102             Link link = links.get(linkId);
103             if (link instanceof CrossSectionLink)
104             {
105                 for (CrossSectionElement element : ((CrossSectionLink) link).getCrossSectionElementList())
106                 {
107                     if (element instanceof Lane)
108                     {
109                         lanes.add((Lane) element);
110                     }
111                 }
112             }
113         }
114         buildConflictsParallelBig(lanes, gtuType, simulator, widthGenerator, ignoreList, permittedList);
115     }
116 
117     /**
118      * Build conflicts on list of lanes.
119      * @param lanes List&lt;Lane&gt;; lanes
120      * @param gtuType GTUType; gtu type
121      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
122      * @param widthGenerator WidthGenerator; width generator
123      * @throws OTSGeometryException in case of geometry exception
124      */
125     public static void buildConflicts(final List<Lane> lanes, final GTUType gtuType,
126             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
127             throws OTSGeometryException
128     {
129         buildConflictsParallelBig(lanes, gtuType, simulator, widthGenerator, new LaneCombinationList(),
130                 new LaneCombinationList());
131     }
132 
133     /**
134      * Build conflicts on list of lanes. Small jobs for parallelization.
135      * @param lanes List&lt;Lane&gt;; list of Lanes
136      * @param gtuType GTUType; the GTU type
137      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator
138      * @param widthGenerator WidthGenerator; the width generator
139      * @param ignoreList LaneCombinationList; lane combinations to ignore
140      * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
141      * @throws OTSGeometryException in case of geometry exception
142      */
143     public static void buildConflictsParallelSmall(final List<Lane> lanes, final GTUType gtuType,
144             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
145             final LaneCombinationListflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
146     {
147         // Loop Lane / GTUDirectionality combinations
148         long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
149         System.out.println("GENERATING CONFLICTS (SMALL JOBS). " + totalCombinations + " COMBINATIONS");
150         CategoryLogger.setAllLogLevel(Level.DEBUG);
151         long lastReported = 0;
152         Map<Lane, OTSLine3D> leftEdges = new LinkedHashMap<>();
153         Map<Lane, OTSLine3D> rightEdges = new LinkedHashMap<>();
154 
155         // force the envelopes to be built first
156         for (Lane lane : lanes)
157         {
158             lane.getContour().getEnvelope();
159         }
160 
161         // make a threadpool and execute buildConflicts for all records
162         int cores = Runtime.getRuntime().availableProcessors();
163         System.out.println("USING " + cores + " CORES");
164         ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2 * cores);
165         AtomicInteger numberOfJobs = new AtomicInteger(0);
166         final int maxqueue = 200;
167 
168         for (int i = 0; i < lanes.size(); i++)
169         {
170             long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i)) / 2;
171             if (combinationsDone / 1000000 > lastReported)
172             {
173                 SimLogger.always()
174                         .debug(String.format("generating conflicts at %.2f%%", 100.0 * combinationsDone / totalCombinations));
175                 lastReported = combinationsDone / 1000000;
176             }
177             Lane lane1 = lanes.get(i);
178             for (GTUDirectionality dir1 : lane1.getLaneType().getDirectionality(gtuType).getDirectionalities())
179             {
180                 ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
181                 ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
182 
183                 for (int j = i + 1; j < lanes.size(); j++)
184                 {
185                     Lane lane2 = lanes.get(j);
186                     if (ignoreList.contains(lane1, lane2))
187                     {
188                         continue;
189                     }
190                     // Quick contour check, skip if non-overlapping envelopes
191                     try
192                     {
193                         if (!lane1.getContour().intersects(lane2.getContour()))
194                         {
195                             continue;
196                         }
197                     }
198                     catch (Exception e)
199                     {
200                         System.err.println("Contour problem - lane1 = [" + lane1.getFullId() + "], lane2 = ["
201                                 + lane2.getFullId() + "]; skipped");
202                         continue;
203                     }
204 
205                     boolean permitted = permittedList.contains(lane1, lane2);
206 
207                     for (GTUDirectionality dir2 : lane2.getLaneType().getDirectionality(gtuType).getDirectionalities())
208                     {
209                         while (numberOfJobs.get() > maxqueue) // keep max maxqueue jobs in the pool
210                         {
211                             try
212                             {
213                                 Thread.sleep(0, 10);
214                             }
215                             catch (InterruptedException exception)
216                             {
217                                 // ignore
218                             }
219                         }
220                         numberOfJobs.incrementAndGet();
221                         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
222                         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
223                         ConflictBuilderRecord cbr = new ConflictBuilderRecord(lane1, dir1, down1, up1, lane2, dir2, down2, up2,
224                                 gtuType, permitted, simulator, widthGenerator, leftEdges, rightEdges);
225                         executor.execute(new CbrTask(numberOfJobs, cbr));
226                     }
227                 }
228             }
229         }
230 
231         System.out.println("WAITING FOR LAST " + maxqueue + " JOBS");
232         long time = System.currentTimeMillis();
233         // wait max 60 sec for last maxqueue jobs
234         while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
235         {
236             try
237             {
238                 Thread.sleep(10);
239             }
240             catch (InterruptedException exception)
241             {
242                 // ignore
243             }
244         }
245 
246         executor.shutdown();
247         while (!executor.isTerminated())
248         {
249             try
250             {
251                 Thread.sleep(1);
252             }
253             catch (InterruptedException exception)
254             {
255                 // ignore
256             }
257         }
258 
259         System.out.println("MERGE CONFLICTS = " + numberMergeConflicts);
260         System.out.println("SPLIT CONFLICTS = " + numberSplitConflicts);
261         System.out.println("CROSS CONFLICTS = " + numberCrossConflicts);
262 
263         CategoryLogger.setAllLogLevel(Level.WARNING);
264     }
265 
266     /**
267      * Build conflicts on list of lanes. Parallelize bigger jobs
268      * @param lanes List&lt;Lane&gt;; list of Lanes
269      * @param gtuType GTUType; the GTU type
270      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; the simulator
271      * @param widthGenerator WidthGenerator; the width generator
272      * @param ignoreList LaneCombinationList; lane combinations to ignore
273      * @param permittedList LaneCombinationList; lane combinations that are permitted by traffic control
274      * @throws OTSGeometryException in case of geometry exception
275      */
276     public static void buildConflictsParallelBig(final List<Lane> lanes, final GTUType gtuType,
277             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
278             final LaneCombinationListflict/LaneCombinationList.html#LaneCombinationList">LaneCombinationList ignoreList, final LaneCombinationList permittedList) throws OTSGeometryException
279     {
280         // Loop Lane / GTUDirectionality combinations
281         long totalCombinations = ((long) lanes.size()) * ((long) lanes.size() - 1) / 2;
282         System.out.println("GENERATING CONFLICTS (BIG JOBS). " + totalCombinations + " COMBINATIONS");
283         CategoryLogger.setAllLogLevel(Level.DEBUG);
284         long lastReported = 0;
285         Map<Lane, OTSLine3D> leftEdges = new LinkedHashMap<>();
286         Map<Lane, OTSLine3D> rightEdges = new LinkedHashMap<>();
287 
288         // force the envelopes to be built first
289         for (Lane lane : lanes)
290         {
291             lane.getContour().getEnvelope();
292         }
293 
294         // make a threadpool and execute buildConflicts for all records
295         int cores = Runtime.getRuntime().availableProcessors();
296         System.out.println("USING " + cores + " CORES");
297         ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2 * cores);
298         AtomicInteger numberOfJobs = new AtomicInteger(0);
299         final int maxqueue = 200;
300 
301         for (int i = 0; i < lanes.size(); i++)
302         {
303             long combinationsDone = totalCombinations - ((long) (lanes.size() - i)) * ((long) (lanes.size() - i)) / 2;
304             if (combinationsDone / 1000000 > lastReported)
305             {
306                 SimLogger.always()
307                         .debug(String.format("generating conflicts at %.2f%%", 100.0 * combinationsDone / totalCombinations));
308                 lastReported = combinationsDone / 1000000;
309             }
310             Lane lane1 = lanes.get(i);
311             for (GTUDirectionality dir1 : lane1.getLaneType().getDirectionality(gtuType).getDirectionalities())
312             {
313                 ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
314                 ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
315 
316                 while (numberOfJobs.get() > maxqueue) // keep max maxqueue jobs in the pool
317                 {
318                     try
319                     {
320                         Thread.sleep(0, 10);
321                     }
322                     catch (InterruptedException exception)
323                     {
324                         // ignore
325                     }
326                 }
327                 numberOfJobs.incrementAndGet();
328                 final int starti = i;
329 
330                 // START JOB
331 
332                 executor.execute(new Runnable()
333                 {
334                     @Override
335                     public void run()
336                     {
337                         for (int j = starti + 1; j < lanes.size(); j++)
338                         {
339                             Lane lane2 = lanes.get(j);
340                             if (ignoreList.contains(lane1, lane2))
341                             {
342                                 continue;
343                             }
344                             // Quick contour check, skip if non-overlapping envelopes
345                             try
346                             {
347                                 if (!lane1.getContour().intersects(lane2.getContour()))
348                                 {
349                                     continue;
350                                 }
351                             }
352                             catch (Exception e)
353                             {
354                                 System.err.println("Contour problem - lane1 = [" + lane1.getFullId() + "], lane2 = ["
355                                         + lane2.getFullId() + "]; skipped");
356                                 continue;
357                             }
358 
359                             boolean permitted = permittedList.contains(lane1, lane2);
360 
361                             for (GTUDirectionality dir2 : lane2.getLaneType().getDirectionality(gtuType).getDirectionalities())
362                             {
363                                 ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
364                                 ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
365 
366                                 try
367                                 {
368                                     buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted,
369                                             simulator, widthGenerator, leftEdges, rightEdges);
370                                 }
371                                 catch (NetworkException | OTSGeometryException ne)
372                                 {
373                                     SimLogger.always().error(ne, "Conflict build with bad combination of types / rules.");
374                                 }
375                             }
376                         }
377                     }
378                 });
379 
380                 // END JOB
381             }
382         }
383 
384         System.out.println("WAITING FOR LAST " + maxqueue + " JOBS");
385         long time = System.currentTimeMillis();
386         // wait max 60 sec for last maxqueue jobs
387         while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
388         {
389             try
390             {
391                 Thread.sleep(10);
392             }
393             catch (InterruptedException exception)
394             {
395                 // ignore
396             }
397         }
398 
399         executor.shutdown();
400         while (!executor.isTerminated())
401         {
402             try
403             {
404                 Thread.sleep(1);
405             }
406             catch (InterruptedException exception)
407             {
408                 // ignore
409             }
410         }
411 
412         System.out.println("MERGE CONFLICTS = " + numberMergeConflicts);
413         System.out.println("SPLIT CONFLICTS = " + numberSplitConflicts);
414         System.out.println("CROSS CONFLICTS = " + numberCrossConflicts);
415 
416         CategoryLogger.setAllLogLevel(Level.WARNING);
417     }
418 
419     /** */
420     static class CbrTask implements Runnable
421     {
422         /** */
423         final ConflictBuilderRecord cbr;
424 
425         /** */
426         final AtomicInteger nrOfJobs;
427 
428         /**
429          * @param nrOfJobs nr
430          * @param cbr the record to execute
431          */
432         CbrTask(final AtomicInteger nrOfJobs, final ConflictBuilderRecord cbr)
433         {
434             this.nrOfJobs = nrOfJobs;
435             this.cbr = cbr;
436         }
437 
438         @Override
439         public void run()
440         {
441             // System.err.println("conflict #" + this.nr);
442             try
443             {
444                 buildConflicts(this.cbr);
445             }
446             catch (Exception e)
447             {
448                 e.printStackTrace();
449             }
450             this.nrOfJobs.decrementAndGet();
451         }
452     }
453 
454     /**
455      * Build conflicts for one record.
456      * @param cbr the lane record
457      */
458     static void buildConflicts(final ConflictBuilderRecord cbr)
459     {
460         // See if conflict needs to be build, and build if so
461         try
462         {
463             buildConflicts(cbr.lane1, cbr.dir1, cbr.down1, cbr.up1, cbr.lane2, cbr.dir2, cbr.down2, cbr.up2, cbr.gtuType,
464                     cbr.permitted, cbr.simulator, cbr.widthGenerator, cbr.leftEdges, cbr.rightEdges);
465         }
466         catch (NetworkException | OTSGeometryException ne)
467         {
468             throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
469         }
470     }
471 
472     /**
473      * Build conflict on single lane pair. Connecting lanes are determined.
474      * @param lane1 Lane; lane 1
475      * @param dir1 GTUDirectionality; gtu direction 1
476      * @param lane2 Lane; lane 2
477      * @param dir2 GTUDirectionality; gtu direction 2
478      * @param gtuType GTUType; gtu type
479      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
480      * @param widthGenerator WidthGenerator; width generator
481      * @throws OTSGeometryException in case of geometry exception
482      */
483     @SuppressWarnings("checkstyle:parameternumber")
484     public static void buildConflicts(final Lane lane1, Lane_keyword">final GTUDirectionality dir1, final Lane lane2,
485             final GTUDirectionality dir2, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
486             final WidthGenerator widthGenerator) throws OTSGeometryException
487     {
488         buildConflicts(lane1, dir1, lane2, dir2, gtuType, simulator, widthGenerator, false);
489     }
490 
491     /**
492      * Build conflict on single lane pair. Connecting lanes are determined.
493      * @param lane1 Lane; lane 1
494      * @param dir1 GTUDirectionality; gtu direction 1
495      * @param lane2 Lane; lane 2
496      * @param dir2 GTUDirectionality; gtu direction 2
497      * @param gtuType GTUType; gtu type
498      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
499      * @param widthGenerator WidthGenerator; width generator
500      * @param permitted boolean; conflict permitted by traffic control
501      * @throws OTSGeometryException in case of geometry exception
502      */
503     @SuppressWarnings("checkstyle:parameternumber")
504     public static void buildConflicts(final Lane lane1, Lane_keyword">final GTUDirectionality dir1, final Lane lane2,
505             final GTUDirectionality dir2, final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
506             final WidthGenerator widthGenerator, final boolean permitted) throws OTSGeometryException
507     {
508         ImmutableMap<Lane, GTUDirectionality> down1 = lane1.downstreamLanes(dir1, gtuType);
509         ImmutableMap<Lane, GTUDirectionality> up1 = lane1.upstreamLanes(dir1, gtuType);
510         ImmutableMap<Lane, GTUDirectionality> down2 = lane2.downstreamLanes(dir2, gtuType);
511         ImmutableMap<Lane, GTUDirectionality> up2 = lane2.upstreamLanes(dir2, gtuType);
512         try
513         {
514             buildConflicts(lane1, dir1, down1, up1, lane2, dir2, down2, up2, gtuType, permitted, simulator, widthGenerator,
515                     new LinkedHashMap<>(), new LinkedHashMap<>());
516         }
517         catch (NetworkException ne)
518         {
519             throw new RuntimeException("Conflict build with bad combination of types / rules.", ne);
520         }
521     }
522 
523     /**
524      * Build conflicts on single lane pair.
525      * @param lane1 Lane; lane 1
526      * @param dir1 GTUDirectionality; gtu direction 1
527      * @param down1 Map&lt;Lane,GTUDirectionality&gt;; downstream lanes 1
528      * @param up1 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 1
529      * @param lane2 Lane; lane 2
530      * @param dir2 GTUDirectionality; gtu direction 2
531      * @param down2 Map&lt;Lane,GTUDirectionality&gt;; downstream lane 2
532      * @param up2 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 2
533      * @param gtuType GTUType; gtu type
534      * @param permitted boolean; conflict permitted by traffic control
535      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
536      * @param widthGenerator WidthGenerator; width generator
537      * @param leftEdges Map<Lane, OTSLine3D>; cache of left edge lines
538      * @param rightEdges Map<Lane, OTSLine3D>; cache of right edge lines
539      * @throws OTSGeometryException in case of geometry exception
540      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
541      */
542     @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:methodlength"})
543     static void buildConflicts(final Lane lane1, final GTUDirectionality dir1,
544             final ImmutableMap<Lane, GTUDirectionality> down1, final ImmutableMap<Lane, GTUDirectionality> up1,
545             final Lane lane2, final GTUDirectionality dir2, final ImmutableMap<Lane, GTUDirectionality> down2,
546             final ImmutableMap<Lane, GTUDirectionality> up2, final GTUType gtuType, final boolean permitted,
547             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
548             final Map<Lane, OTSLine3D> leftEdges, final Map<Lane, OTSLine3D> rightEdges)
549             throws OTSGeometryException, NetworkException
550     {
551 
552         // Quick contour check, skip if not overlapping -- HAS TAKEN PLACE SO NOT REPEATED (expensive)
553         // if (!lane1.getContour().intersects(lane2.getContour()))
554         // {
555         // return;
556         // }
557 
558         // Get left and right lines at specified width
559         // TODO: we cache, but the width generator may be different
560 
561         OTSLine3D left1;
562         OTSLine3D right1;
563         synchronized (lane1)
564         {
565             left1 = leftEdges.get(lane1);
566             right1 = rightEdges.get(lane1);
567             OTSLine3D line1 = lane1.getCenterLine();
568             if (null == left1)
569             {
570                 left1 = line1.offsetLine(widthGenerator.getWidth(lane1, 0.0) / 2, widthGenerator.getWidth(lane1, 1.0) / 2);
571                 leftEdges.put(lane1, left1);
572             }
573             if (null == right1)
574             {
575                 right1 = line1.offsetLine(-widthGenerator.getWidth(lane1, 0.0) / 2, -widthGenerator.getWidth(lane1, 1.0) / 2);
576                 rightEdges.put(lane1, right1);
577             }
578         }
579 
580         OTSLine3D left2;
581         OTSLine3D right2;
582         synchronized (lane2)
583         {
584             left2 = leftEdges.get(lane2);
585             right2 = rightEdges.get(lane2);
586             OTSLine3D line2 = lane2.getCenterLine();
587             if (null == left2)
588             {
589                 left2 = line2.offsetLine(widthGenerator.getWidth(lane2, 0.0) / 2, widthGenerator.getWidth(lane2, 1.0) / 2);
590                 leftEdges.put(lane2, left2);
591             }
592             if (null == right2)
593             {
594                 right2 = line2.offsetLine(-widthGenerator.getWidth(lane2, 0.0) / 2, -widthGenerator.getWidth(lane2, 1.0) / 2);
595                 rightEdges.put(lane2, right2);
596             }
597         }
598 
599         // Get list of all intersection fractions
600         SortedSet<Intersection> intersections = Intersection.getIntersectionList(left1, left2, 0);
601         intersections.addAll(Intersection.getIntersectionList(left1, right2, 1));
602         intersections.addAll(Intersection.getIntersectionList(right1, left2, 2));
603         intersections.addAll(Intersection.getIntersectionList(right1, right2, 3));
604 
605         // Create merge
606         ImmutableIterator<ImmutableEntry<Lane, GTUDirectionality>> iterator1 = down1.entrySet().iterator();
607         ImmutableIterator<ImmutableEntry<Lane, GTUDirectionality>> iterator2 = down2.entrySet().iterator();
608         boolean merge = false;
609         while (iterator1.hasNext() && !merge)
610         {
611             ImmutableEntry<Lane, GTUDirectionality> next1 = iterator1.next();
612             while (iterator2.hasNext() && !merge)
613             {
614                 ImmutableEntry<Lane, GTUDirectionality> next2 = iterator2.next();
615                 if (next1.equals(next2))
616                 {
617                     // Same downstream lane, so a merge
618                     double fraction1 = Double.NaN;
619                     double fraction2 = Double.NaN;
620                     for (Intersection intersection : intersections)
621                     {
622                         // Only consider left/right and right/left intersections (others may or may not be at the end)
623                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
624                         {
625                             fraction1 = intersection.getFraction1();
626                             fraction2 = intersection.getFraction2();
627                         }
628                     }
629                     // Remove all intersections beyond this point, these are the result of line starts/ends matching
630                     Iterator<Intersection> iterator = intersections.iterator();
631                     while (iterator.hasNext())
632                     {
633                         if (iterator.next().getFraction1() >= fraction1)
634                         {
635                             iterator.remove();
636                         }
637                     }
638                     if (Double.isNaN(fraction1))
639                     {
640                         SimLogger.always().warn("Fixing fractions of merge conflict");
641                         fraction1 = 0;
642                         fraction2 = 0;
643                     }
644                     // Build conflict
645                     buildMergeConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator,
646                             permitted);
647                     // Skip loop for efficiency, and do not create multiple merges in case of multiple same downstream lanes
648                     merge = true;
649                 }
650             }
651         }
652 
653         // Create split
654         iterator1 = up1.entrySet().iterator();
655         iterator2 = up2.entrySet().iterator();
656         boolean split = false;
657         while (iterator1.hasNext() && !split)
658         {
659             ImmutableEntry<Lane, GTUDirectionality> prev1 = iterator1.next();
660             while (iterator2.hasNext() && !split)
661             {
662                 ImmutableEntry<Lane, GTUDirectionality> prev2 = iterator2.next();
663                 if (prev1.equals(prev2))
664                 {
665                     // Same upstream lane, so a split
666                     double fraction1 = Double.NaN;
667                     double fraction2 = Double.NaN;
668                     for (Intersection intersection : intersections)
669                     {
670                         // Only consider left/right and right/left intersections (others may or may not be at the start)
671                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
672                         {
673                             fraction1 = intersection.getFraction1();
674                             fraction2 = intersection.getFraction2();
675                             break; // Split so first, not last
676                         }
677                     }
678                     // Remove all intersections up to this point, these are the result of line starts/ends matching
679                     Iterator<Intersection> iterator = intersections.iterator();
680                     while (iterator.hasNext())
681                     {
682                         if (iterator.next().getFraction1() <= fraction1)
683                         {
684                             iterator.remove();
685                         }
686                         else
687                         {
688                             // May skip further fraction
689                             break;
690                         }
691                     }
692                     if (Double.isNaN(fraction1))
693                     {
694                         SimLogger.always().warn("Fixing fractions of split conflict");
695                         fraction1 = 1;
696                         fraction2 = 1;
697                     }
698                     // Build conflict
699                     buildSplitConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator);
700                     // Skip loop for efficiency, and do not create multiple splits in case of multiple same upstream lanes
701                     split = true;
702                 }
703             }
704         }
705 
706         // Create crossings
707         if (!lane1.getParentLink().equals(lane2.getParentLink())) // tight inner-curves with dedicated Bezier ignored
708         {
709             boolean[] crossed = new boolean[4];
710             Iterator<Intersection> iterator = intersections.iterator();
711             double f1Start = Double.NaN;
712             double f2Start = Double.NaN;
713             double f2End = Double.NaN;
714             while (iterator.hasNext())
715             {
716                 Intersection intersection = iterator.next();
717                 // First fraction found is start of conflict
718                 if (Double.isNaN(f1Start))
719                 {
720                     f1Start = intersection.getFraction1();
721                 }
722                 f2Start = Double.isNaN(f2Start) ? intersection.getFraction2() : Math.min(f2Start, intersection.getFraction2());
723                 f2End = Double.isNaN(f2End) ? intersection.getFraction2() : Math.max(f2End, intersection.getFraction2());
724                 // Flip crossed state of intersecting line combination
725                 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
726                 // If all crossed or all not crossed, end of conflict
727                 if ((crossed[0] && crossed[1] && crossed[2] && crossed[3])
728                         || (!crossed[0] && !crossed[1] && !crossed[2] && !crossed[3]))
729                 {
730                     if (dir2.isMinus())
731                     {
732                         double f2Temp = f2Start;
733                         f2Start = f2End;
734                         f2End = f2Temp;
735                     }
736                     if (Double.isNaN(f1Start) || Double.isNaN(f2Start) || Double.isNaN(f2End))
737                     {
738                         SimLogger.always().warn("NOT YET Fixing fractions of crossing conflict");
739                     }
740                     buildCrossingConflict(lane1, dir1, f1Start, intersection.getFraction1(), lane2, dir2, f2Start, f2End,
741                             gtuType, simulator, widthGenerator, permitted);
742                     f1Start = Double.NaN;
743                     f2Start = Double.NaN;
744                     f2End = Double.NaN;
745                 }
746             }
747         }
748 
749     }
750 
751     /**
752      * Build a merge conflict.
753      * @param lane1 Lane; lane 1
754      * @param dir1 GTUDirectionality; gtu direction 1
755      * @param f1start double; start fraction 1
756      * @param lane2 Lane; lane 2
757      * @param dir2 GTUDirectionality; gtu direction 2
758      * @param f2start double; start fraction 2
759      * @param gtuType GTUType; gtu type
760      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
761      * @param widthGenerator WidthGenerator; width generator
762      * @param permitted boolean; conflict permitted by traffic control
763      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
764      * @throws OTSGeometryException in case of geometry exception
765      */
766     @SuppressWarnings("checkstyle:parameternumber")
767     private static void buildMergeConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
768             final Lane lane2, final GTUDirectionality dir2, final double f2start, final GTUType gtuType,
769             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator, final boolean permitted)
770             throws NetworkException, OTSGeometryException
771     {
772 
773         // Determine lane end from direction
774         double f1end = dir1.isPlus() ? 1.0 : 0.0;
775         double f2end = dir2.isPlus() ? 1.0 : 0.0;
776 
777         // Get locations and length
778         Length longitudinalPosition1 = lane1.getLength().times(f1start);
779         Length longitudinalPosition2 = lane2.getLength().times(f2start);
780         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
781         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
782 
783         // Get geometries
784         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
785         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
786 
787         // Determine conflict rule
788         ConflictRule conflictRule;
789         if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
790         {
791             Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
792                     IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
793             conflictRule = new BusStopConflictRule(simulator);
794         }
795         else
796         {
797             conflictRule = new DefaultConflictRule();
798         }
799 
800         // Make conflict
801         Conflict.generateConflictPair(ConflictType.MERGE, conflictRule, permitted, lane1, longitudinalPosition1, length1, dir1,
802                 geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
803 
804         numberMergeConflicts.incrementAndGet();
805     }
806 
807     /**
808      * Build a split conflict.
809      * @param lane1 Lane; lane 1
810      * @param dir1 GTUDirectionality; gtu direction 1
811      * @param f1end double; end fraction 1
812      * @param lane2 Lane; lane 2
813      * @param dir2 GTUDirectionality; gtu direction 2
814      * @param f2end double; end fraction 2
815      * @param gtuType GTUType; gtu type
816      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
817      * @param widthGenerator WidthGenerator; width generator
818      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
819      * @throws OTSGeometryException in case of geometry exception
820      */
821     @SuppressWarnings("checkstyle:parameternumber")
822     private static void buildSplitConflict(final Lane lane1, final GTUDirectionality dir1, finalLanetrong class="jxr_keyword">double f1end, final Lane lane2,
823             final GTUDirectionality dir2, final double f2end, final GTUType gtuType,
824             final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator)
825             throws NetworkException, OTSGeometryException
826     {
827 
828         // Determine lane start from direction
829         double f1start = dir1.isPlus() ? 0.0 : 1.0;
830         double f2start = dir2.isPlus() ? 0.0 : 1.0;
831 
832         // Get locations and length
833         Length longitudinalPosition1 = lane1.getLength().times(f1start);
834         Length longitudinalPosition2 = lane2.getLength().times(f2start);
835         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
836         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
837 
838         // Get geometries
839         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
840         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
841 
842         // Make conflict
843         Conflict.generateConflictPair(ConflictType.SPLIT, new SplitConflictRule(), false, lane1, longitudinalPosition1, length1,
844                 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
845 
846         numberSplitConflicts.incrementAndGet();
847     }
848 
849     /**
850      * Build a crossing conflict.
851      * @param lane1 Lane; lane 1
852      * @param dir1 GTUDirectionality; gtu direction 1
853      * @param f1start double; start fraction 1
854      * @param f1end double; end fraction 1
855      * @param lane2 Lane; lane 2
856      * @param dir2 GTUDirectionality; gtu direction 2
857      * @param f2start double; start fraction 2
858      * @param f2end double; end fraction 2
859      * @param gtuType GTUType; gtu type
860      * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
861      * @param widthGenerator WidthGenerator; width generator
862      * @param permitted boolean; conflict permitted by traffic control
863      * @throws NetworkException if the combination of conflict type and both conflict rules is not correct
864      * @throws OTSGeometryException in case of geometry exception
865      */
866     @SuppressWarnings("checkstyle:parameternumber")
867     private static void buildCrossingConflict(final Lane lane1, final GTUDirectionality dir1, final double f1start,
868             final double f1end, final Lane lane2, final GTUDirectionality dir2, final double f2start, final double f2end,
869             final GTUType gtuType, final DEVSSimulatorInterface.TimeDoubleUnit simulator, final WidthGenerator widthGenerator,
870             final boolean permitted) throws NetworkException, OTSGeometryException
871     {
872 
873         // Fractions may be in opposite direction, for the start location this needs to be correct
874         // Note: for geometry (real order, not considering direction) and length (absolute value) this does not matter
875         double f1startDirected;
876         double f2startDirected;
877         if ((dir1.isPlus() && f1end < f1start) || (dir1.isMinus() && f1end > f1start))
878         {
879             f1startDirected = f1end;
880         }
881         else
882         {
883             f1startDirected = f1start;
884         }
885         if ((dir2.isPlus() && f2end < f2start) || (dir2.isMinus() && f2end > f2start))
886         {
887             f2startDirected = f2end;
888         }
889         else
890         {
891             f2startDirected = f2start;
892         }
893 
894         // Get locations and length
895         Length longitudinalPosition1 = lane1.getLength().times(f1startDirected);
896         Length longitudinalPosition2 = lane2.getLength().times(f2startDirected);
897         Length length1 = lane1.getLength().times(Math.abs(f1end - f1start));
898         Length length2 = lane2.getLength().times(Math.abs(f2end - f2start));
899 
900         // Get geometries
901         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
902         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
903 
904         // Determine conflict rule
905         ConflictRule conflictRule;
906         if (lane1.getParentLink().getPriority().isBusStop() || lane2.getParentLink().getPriority().isBusStop())
907         {
908             Throw.when(lane1.getParentLink().getPriority().isBusStop() && lane2.getParentLink().getPriority().isBusStop(),
909                     IllegalArgumentException.class, "Merge conflict between two links with bus stop priority not supported.");
910             conflictRule = new BusStopConflictRule(simulator);
911         }
912         else
913         {
914             conflictRule = new DefaultConflictRule();
915         }
916 
917         // Make conflict
918         Conflict.generateConflictPair(ConflictType.CROSSING, conflictRule, permitted, lane1, longitudinalPosition1, length1,
919                 dir1, geometry1, gtuType, lane2, longitudinalPosition2, length2, dir2, geometry2, gtuType, simulator);
920 
921         numberCrossConflicts.incrementAndGet();
922     }
923 
924     /**
925      * Creates geometry for conflict.
926      * @param lane Lane; lane
927      * @param fStart double; longitudinal fraction of start
928      * @param fEnd double; longitudinal fraction of end
929      * @param widthGenerator WidthGenerator; width generator
930      * @return geometry for conflict
931      * @throws OTSGeometryException in case of geometry exception
932      */
933     private static OTSLine3D getGeometry(final Lane lane, final double fStart, final double fEnd,
934             final WidthGenerator widthGenerator) throws OTSGeometryException
935     {
936         // extractFractional needs ordered fractions, irrespective of driving direction
937         double f1;
938         double f2;
939         if (fEnd > fStart)
940         {
941             f1 = fStart;
942             f2 = fEnd;
943         }
944         else
945         {
946             f1 = fEnd;
947             f2 = fStart;
948         }
949         OTSLine3D centerLine = lane.getCenterLine().extractFractional(f1, f2);
950         OTSLine3D left = centerLine.offsetLine(widthGenerator.getWidth(lane, f1) / 2, widthGenerator.getWidth(lane, f2) / 2);
951         OTSLine3D right =
952                 centerLine.offsetLine(-widthGenerator.getWidth(lane, f1) / 2, -widthGenerator.getWidth(lane, f2) / 2).reverse();
953         OTSPoint3D[] points = new OTSPoint3D[left.size() + right.size()];
954         System.arraycopy(left.getPoints(), 0, points, 0, left.size());
955         System.arraycopy(right.getPoints(), 0, points, left.size(), right.size());
956         return new OTSLine3D(points);
957     }
958 
959     /**
960      * Intersection holds two fractions where two lines have crossed. There is also a combo to identify which lines have been
961      * used to find the intersection.
962      * <p>
963      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
964      * <br>
965      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
966      * <p>
967      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 21 dec. 2016 <br>
968      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
969      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
970      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
971      */
972     private static class Intersection implements Comparable<Intersection>
973     {
974 
975         /** Fraction on lane 1. */
976         private final double fraction1;
977 
978         /** Fraction on lane 2. */
979         private final double fraction2;
980 
981         /** Edge combination number. */
982         private final int combo;
983 
984         /**
985          * @param fraction1 double; fraction on lane 1
986          * @param fraction2 double; fraction on lane 1
987          * @param combo int; edge combination number
988          */
989         Intersection(final double fraction1, final double fraction2, final int combo)
990         {
991             this.fraction1 = fraction1;
992             this.fraction2 = fraction2;
993             this.combo = combo;
994         }
995 
996         /**
997          * @return fraction1.
998          */
999         public final double getFraction1()
1000         {
1001             return this.fraction1;
1002         }
1003 
1004         /**
1005          * @return fraction2.
1006          */
1007         public final double getFraction2()
1008         {
1009             return this.fraction2;
1010         }
1011 
1012         /**
1013          * @return combo.
1014          */
1015         public final int getCombo()
1016         {
1017             return this.combo;
1018         }
1019 
1020         /** {@inheritDoc} */
1021         @Override
1022         public int compareTo(final Intersection o)
1023         {
1024             int out = Double.compare(this.fraction1, o.fraction1);
1025             if (out != 0)
1026             {
1027                 return out;
1028             }
1029             out = Double.compare(this.fraction2, o.fraction2);
1030             if (out != 0)
1031             {
1032                 return out;
1033             }
1034             return Integer.compare(this.combo, o.combo);
1035         }
1036 
1037         /** {@inheritDoc} */
1038         @Override
1039         public int hashCode()
1040         {
1041             final int prime = 31;
1042             int result = 1;
1043             result = prime * result + this.combo;
1044             long temp;
1045             temp = Double.doubleToLongBits(this.fraction1);
1046             result = prime * result + (int) (temp ^ (temp >>> 32));
1047             temp = Double.doubleToLongBits(this.fraction2);
1048             result = prime * result + (int) (temp ^ (temp >>> 32));
1049             return result;
1050         }
1051 
1052         /** {@inheritDoc} */
1053         @Override
1054         public boolean equals(final Object obj)
1055         {
1056             if (this == obj)
1057             {
1058                 return true;
1059             }
1060             if (obj == null)
1061             {
1062                 return false;
1063             }
1064             if (getClass() != obj.getClass())
1065             {
1066                 return false;
1067             }
1068             Intersection other = (Intersection) obj;
1069             if (this.combo != other.combo)
1070             {
1071                 return false;
1072             }
1073             if (Double.doubleToLongBits(this.fraction1) != Double.doubleToLongBits(other.fraction1))
1074             {
1075                 return false;
1076             }
1077             if (Double.doubleToLongBits(this.fraction2) != Double.doubleToLongBits(other.fraction2))
1078             {
1079                 return false;
1080             }
1081             return true;
1082         }
1083 
1084         /**
1085          * Returns a set of intersections, sorted by the fraction on line 1.
1086          * @param line1 OTSLine3D; line 1
1087          * @param line2 OTSLine3D; line 2
1088          * @param combo int; edge combination number
1089          * @return set of intersections, sorted by the fraction on line 1
1090          * @throws OTSGeometryException in case of geometry exception
1091          */
1092         public static SortedSet<Intersection> getIntersectionList(final OTSLine3D line1, final OTSLine3D line2, final int combo)
1093                 throws OTSGeometryException
1094         {
1095             SortedSet<Intersection> out = new TreeSet<>();
1096             // if (!line1.getBounds().intersect(line2.getBounds()))
1097             // {
1098             // return out;
1099             // }
1100             double cumul1 = 0.0;
1101             OTSPoint3D start1 = null;
1102             OTSPoint3D end1 = line1.get(0);
1103             for (int i = 0; i < line1.size() - 1; i++)
1104             {
1105                 start1 = end1;
1106                 end1 = line1.get(i + 1);
1107 
1108                 double cumul2 = 0.0;
1109                 OTSPoint3D start2 = null;
1110                 OTSPoint3D end2 = line2.get(0);
1111 
1112                 for (int j = 0; j < line2.size() - 1; j++)
1113                 {
1114                     start2 = end2;
1115                     end2 = line2.get(j + 1);
1116 
1117                     OTSPoint3D p = OTSPoint3D.intersectionOfLineSegments(start1, end1, start2, end2);
1118                     if (p != null)
1119                     {
1120                         // Segments intersect
1121                         double dx = p.x - start1.x;
1122                         double dy = p.y - start1.y;
1123                         double length1 = cumul1 + Math.sqrt(dx * dx + dy * dy);
1124                         dx = p.x - start2.x;
1125                         dy = p.y - start2.y;
1126                         double length2 = cumul2 + Math.sqrt(dx * dx + dy * dy);
1127                         out.add(new Intersection(length1 / line1.getLengthSI(), length2 / line2.getLengthSI(), combo));
1128                     }
1129 
1130                     double dx = end2.x - start2.x;
1131                     double dy = end2.y - start2.y;
1132                     cumul2 += Math.sqrt(dx * dx + dy * dy);
1133                 }
1134 
1135                 double dx = end1.x - start1.x;
1136                 double dy = end1.y - start1.y;
1137                 cumul1 += Math.sqrt(dx * dx + dy * dy);
1138             }
1139 
1140             return out;
1141         }
1142 
1143         /** {@inheritDoc} */
1144         @Override
1145         public String toString()
1146         {
1147             return "Intersection [fraction1=" + this.fraction1 + ", fraction2=" + this.fraction2 + ", combo=" + this.combo
1148                     + "]";
1149         }
1150 
1151     }
1152 
1153     /**
1154      * Generator for width.
1155      * <p>
1156      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
1157      * <br>
1158      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
1159      * <p>
1160      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 dec. 2016 <br>
1161      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
1162      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
1163      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
1164      */
1165     public interface WidthGenerator
1166     {
1167 
1168         /**
1169          * Returns the begin width of this lane.
1170          * @param lane Lane; lane
1171          * @param fraction double; fraction
1172          * @return begin width of this lane
1173          */
1174         double getWidth(Lane lane, double fraction);
1175 
1176     }
1177 
1178     /**
1179      * Generator with fixed width.
1180      * <p>
1181      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
1182      * <br>
1183      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
1184      * <p>
1185      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 dec. 2016 <br>
1186      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
1187      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
1188      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
1189      */
1190     public static class FixedWidthGenerator implements WidthGenerator
1191     {
1192 
1193         /** Fixed width. */
1194         private final double width;
1195 
1196         /**
1197          * Constructor with width.
1198          * @param width Length; width
1199          */
1200         public FixedWidthGenerator(final Length width)
1201         {
1202             this.width = width.si;
1203         }
1204 
1205         /** {@inheritDoc} */
1206         @Override
1207         public final double getWidth(final Lane lane, final double fraction)
1208         {
1209             return this.width;
1210         }
1211 
1212         /** {@inheritDoc} */
1213         @Override
1214         public final String toString()
1215         {
1216             return "FixedWidthGenerator [width=" + this.width + "]";
1217         }
1218 
1219     }
1220 
1221     /**
1222      * Generator with width factor on actual lane width.
1223      * <p>
1224      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
1225      * <br>
1226      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
1227      * <p>
1228      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 16 dec. 2016 <br>
1229      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
1230      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
1231      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
1232      */
1233     public static class RelativeWidthGenerator implements WidthGenerator
1234     {
1235 
1236         /** Width factor. */
1237         private final double factor;
1238 
1239         /**
1240          * Constructor with width factor.
1241          * @param factor double; width factor
1242          */
1243         public RelativeWidthGenerator(final double factor)
1244         {
1245             this.factor = factor;
1246         }
1247 
1248         /** {@inheritDoc} */
1249         @Override
1250         public final double getWidth(final Lane lane, final double fraction)
1251         {
1252             return lane.getWidth(fraction).si * this.factor;
1253         }
1254 
1255         /** {@inheritDoc} */
1256         @Override
1257         public final String toString()
1258         {
1259             return "RelativeWidthGenerator [factor=" + this.factor + "]";
1260         }
1261 
1262     }
1263 
1264     /** */
1265     @SuppressWarnings("checkstyle:visibilitymodifier")
1266     static class ConflictBuilderRecord
1267     {
1268         /** */
1269         final Lane lane1;
1270 
1271         /** */
1272         final GTUDirectionality dir1;
1273 
1274         /** */
1275         final ImmutableMap<Lane, GTUDirectionality> down1;
1276 
1277         /** */
1278         final ImmutableMap<Lane, GTUDirectionality> up1;
1279 
1280         /** */
1281         final Lane lane2;
1282 
1283         /** */
1284         final GTUDirectionality dir2;
1285 
1286         /** */
1287         final ImmutableMap<Lane, GTUDirectionality> down2;
1288 
1289         /** */
1290         final ImmutableMap<Lane, GTUDirectionality> up2;
1291 
1292         /** */
1293         final GTUType gtuType;
1294 
1295         /** */
1296         final boolean permitted;
1297 
1298         /** */
1299         final DEVSSimulatorInterface.TimeDoubleUnit simulator;
1300 
1301         /** */
1302         final WidthGenerator widthGenerator;
1303 
1304         /** */
1305         final Map<Lane, OTSLine3D> leftEdges;
1306 
1307         /** */
1308         final Map<Lane, OTSLine3D> rightEdges;
1309 
1310         /**
1311          * Stores conflicts about a single lane pair.
1312          * @param lane1 Lane; lane 1
1313          * @param dir1 GTUDirectionality; gtu direction 1
1314          * @param down1 Map&lt;Lane,GTUDirectionality&gt;; downstream lanes 1
1315          * @param up1 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 1
1316          * @param lane2 Lane; lane 2
1317          * @param dir2 GTUDirectionality; gtu direction 2
1318          * @param down2 Map&lt;Lane,GTUDirectionality&gt;; downstream lane 2
1319          * @param up2 Map&lt;Lane,GTUDirectionality&gt;; upstream lanes 2
1320          * @param gtuType GTUType; gtu type
1321          * @param permitted boolean; conflict permitted by traffic control
1322          * @param simulator DEVSSimulatorInterface.TimeDoubleUnit; simulator
1323          * @param widthGenerator WidthGenerator; width generator
1324          * @param leftEdges Map<Lane, OTSLine3D>; cache of left edge lines
1325          * @param rightEdges Map<Lane, OTSLine3D>; cache of right edge lines
1326          */
1327         @SuppressWarnings("checkstyle:parameternumber")
1328         ConflictBuilderRecord(final Lane lane1, final GTUDirectionality dir1, final ImmutableMap<Lane, GTUDirectionality> down1,
1329                 final ImmutableMap<Lane, GTUDirectionality> up1, final Lane lane2, final GTUDirectionality dir2,
1330                 final ImmutableMap<Lane, GTUDirectionality> down2, final ImmutableMap<Lane, GTUDirectionality> up2,
1331                 final GTUType gtuType, final boolean permitted, final DEVSSimulatorInterface.TimeDoubleUnit simulator,
1332                 final WidthGenerator widthGenerator, final Map<Lane, OTSLine3D> leftEdges,
1333                 final Map<Lane, OTSLine3D> rightEdges)
1334         {
1335             this.lane1 = lane1;
1336             this.dir1 = dir1;
1337             this.down1 = down1;
1338             this.up1 = up1;
1339             this.lane2 = lane2;
1340             this.dir2 = dir2;
1341             this.down2 = down2;
1342             this.up2 = up2;
1343             this.gtuType = gtuType;
1344             this.permitted = permitted;
1345             this.simulator = simulator;
1346             this.widthGenerator = widthGenerator;
1347             this.leftEdges = leftEdges;
1348             this.rightEdges = rightEdges;
1349         }
1350     }
1351 }