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  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  public final class ConflictBuilderParallel
49  {
50  
51      
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  
62  
63      private ConflictBuilderParallel()
64      {
65          
66      }
67  
68      
69  
70  
71  
72  
73  
74  
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  
85  
86  
87  
88  
89  
90  
91  
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          
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 
119 
120 
121 
122 
123 
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 
135 
136 
137 
138 
139 
140 
141 
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         
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         
156         for (Lane lane : lanes)
157         {
158             lane.getContour().getEnvelope();
159         }
160 
161         
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                     
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) 
210                         {
211                             try
212                             {
213                                 Thread.sleep(0, 10);
214                             }
215                             catch (InterruptedException exception)
216                             {
217                                 
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         
234         while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
235         {
236             try
237             {
238                 Thread.sleep(10);
239             }
240             catch (InterruptedException exception)
241             {
242                 
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                 
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 
268 
269 
270 
271 
272 
273 
274 
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         
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         
289         for (Lane lane : lanes)
290         {
291             lane.getContour().getEnvelope();
292         }
293 
294         
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) 
317                 {
318                     try
319                     {
320                         Thread.sleep(0, 10);
321                     }
322                     catch (InterruptedException exception)
323                     {
324                         
325                     }
326                 }
327                 numberOfJobs.incrementAndGet();
328                 final int starti = i;
329 
330                 
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                             
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                 
381             }
382         }
383 
384         System.out.println("WAITING FOR LAST " + maxqueue + " JOBS");
385         long time = System.currentTimeMillis();
386         
387         while (numberOfJobs.get() > 0 && System.currentTimeMillis() - time < 60000)
388         {
389             try
390             {
391                 Thread.sleep(10);
392             }
393             catch (InterruptedException exception)
394             {
395                 
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                 
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 
430 
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             
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 
456 
457 
458     static void buildConflicts(final ConflictBuilderRecord cbr)
459     {
460         
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 
474 
475 
476 
477 
478 
479 
480 
481 
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 
493 
494 
495 
496 
497 
498 
499 
500 
501 
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 
525 
526 
527 
528 
529 
530 
531 
532 
533 
534 
535 
536 
537 
538 
539 
540 
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         
553         
554         
555         
556         
557 
558         
559         
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         
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         
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                     
618                     double fraction1 = Double.NaN;
619                     double fraction2 = Double.NaN;
620                     for (Intersection intersection : intersections)
621                     {
622                         
623                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
624                         {
625                             fraction1 = intersection.getFraction1();
626                             fraction2 = intersection.getFraction2();
627                         }
628                     }
629                     
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                     
645                     buildMergeConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator,
646                             permitted);
647                     
648                     merge = true;
649                 }
650             }
651         }
652 
653         
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                     
666                     double fraction1 = Double.NaN;
667                     double fraction2 = Double.NaN;
668                     for (Intersection intersection : intersections)
669                     {
670                         
671                         if (intersection.getCombo() == 1 || intersection.getCombo() == 2)
672                         {
673                             fraction1 = intersection.getFraction1();
674                             fraction2 = intersection.getFraction2();
675                             break; 
676                         }
677                     }
678                     
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                             
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                     
699                     buildSplitConflict(lane1, dir1, fraction1, lane2, dir2, fraction2, gtuType, simulator, widthGenerator);
700                     
701                     split = true;
702                 }
703             }
704         }
705 
706         
707         if (!lane1.getParentLink().equals(lane2.getParentLink())) 
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                 
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                 
725                 crossed[intersection.getCombo()] = !crossed[intersection.getCombo()];
726                 
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 
753 
754 
755 
756 
757 
758 
759 
760 
761 
762 
763 
764 
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         
774         double f1end = dir1.isPlus() ? 1.0 : 0.0;
775         double f2end = dir2.isPlus() ? 1.0 : 0.0;
776 
777         
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         
784         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
785         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
786 
787         
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         
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 
809 
810 
811 
812 
813 
814 
815 
816 
817 
818 
819 
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         
829         double f1start = dir1.isPlus() ? 0.0 : 1.0;
830         double f2start = dir2.isPlus() ? 0.0 : 1.0;
831 
832         
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         
839         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
840         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
841 
842         
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 
851 
852 
853 
854 
855 
856 
857 
858 
859 
860 
861 
862 
863 
864 
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         
874         
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         
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         
901         OTSLine3D geometry1 = getGeometry(lane1, f1start, f1end, widthGenerator);
902         OTSLine3D geometry2 = getGeometry(lane2, f2start, f2end, widthGenerator);
903 
904         
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         
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 
926 
927 
928 
929 
930 
931 
932 
933     private static OTSLine3D getGeometry(final Lane lane, final double fStart, final double fEnd,
934             final WidthGenerator widthGenerator) throws OTSGeometryException
935     {
936         
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 
961 
962 
963 
964 
965 
966 
967 
968 
969 
970 
971 
972     private static class Intersection implements Comparable<Intersection>
973     {
974 
975         
976         private final double fraction1;
977 
978         
979         private final double fraction2;
980 
981         
982         private final int combo;
983 
984         
985 
986 
987 
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 
998 
999         public final double getFraction1()
1000         {
1001             return this.fraction1;
1002         }
1003 
1004         
1005 
1006 
1007         public final double getFraction2()
1008         {
1009             return this.fraction2;
1010         }
1011 
1012         
1013 
1014 
1015         public final int getCombo()
1016         {
1017             return this.combo;
1018         }
1019 
1020         
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         
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         
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 
1086 
1087 
1088 
1089 
1090 
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             
1097             
1098             
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                         
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         
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 
1155 
1156 
1157 
1158 
1159 
1160 
1161 
1162 
1163 
1164 
1165     public interface WidthGenerator
1166     {
1167 
1168         
1169 
1170 
1171 
1172 
1173 
1174         double getWidth(Lane lane, double fraction);
1175 
1176     }
1177 
1178     
1179 
1180 
1181 
1182 
1183 
1184 
1185 
1186 
1187 
1188 
1189 
1190     public static class FixedWidthGenerator implements WidthGenerator
1191     {
1192 
1193         
1194         private final double width;
1195 
1196         
1197 
1198 
1199 
1200         public FixedWidthGenerator(final Length width)
1201         {
1202             this.width = width.si;
1203         }
1204 
1205         
1206         @Override
1207         public final double getWidth(final Lane lane, final double fraction)
1208         {
1209             return this.width;
1210         }
1211 
1212         
1213         @Override
1214         public final String toString()
1215         {
1216             return "FixedWidthGenerator [width=" + this.width + "]";
1217         }
1218 
1219     }
1220 
1221     
1222 
1223 
1224 
1225 
1226 
1227 
1228 
1229 
1230 
1231 
1232 
1233     public static class RelativeWidthGenerator implements WidthGenerator
1234     {
1235 
1236         
1237         private final double factor;
1238 
1239         
1240 
1241 
1242 
1243         public RelativeWidthGenerator(final double factor)
1244         {
1245             this.factor = factor;
1246         }
1247 
1248         
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         
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 
1312 
1313 
1314 
1315 
1316 
1317 
1318 
1319 
1320 
1321 
1322 
1323 
1324 
1325 
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 }