1   package org.opentrafficsim.trafficcontrol.trafcod;
2   
3   import java.awt.BorderLayout;
4   import java.awt.Color;
5   import java.awt.Component;
6   import java.awt.Dimension;
7   import java.awt.Graphics2D;
8   import java.awt.event.ActionEvent;
9   import java.awt.event.ActionListener;
10  import java.awt.image.BufferedImage;
11  import java.util.ArrayList;
12  import java.util.Comparator;
13  import java.util.LinkedHashMap;
14  import java.util.LinkedHashSet;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Set;
18  
19  import javax.swing.BoxLayout;
20  import javax.swing.ImageIcon;
21  import javax.swing.JCheckBox;
22  import javax.swing.JFrame;
23  import javax.swing.JLabel;
24  import javax.swing.JPanel;
25  import javax.swing.JScrollPane;
26  import javax.swing.SwingUtilities;
27  
28  import org.djutils.exceptions.Throw;
29  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightException;
30  import org.opentrafficsim.trafficcontrol.TrafficController;
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  public class Diagram
45  {
46      
47      
48      final static int DIVIDER_1 = 0;
49  
50      
51      final static int CAR_ROUNDABOUT_LEFT = 1;
52  
53      
54      final static int PT_DIV_L = 3;
55  
56      
57      final static int DIVIDER_2 = 4;
58  
59      
60      final static int CAR_LEFT = 5;
61  
62      
63      final static int CAR_CENTER = 7;
64  
65      
66      final static int CAR_RIGHT = 9;
67  
68      
69      final static int DIVIDER_3 = 10;
70  
71      
72      final static int PT_RIGHT_BICYCLE = 11;
73  
74      
75      final static int DIVIDER_4 = 12;
76  
77      
78      final static int BICYCLE = 13;
79  
80      
81      final static int DIVIDER_5 = 14;
82  
83      
84      final static int PT_BICYCLE_SIDEWALK = 15;
85  
86      
87      final static int DIVIDER_6 = 16;
88  
89      
90      final static int SIDEWALK = 17;
91  
92      
93      final static int DIVIDER_7 = 18;
94  
95      
96      final static int PT_SIDEWALK_SHOULDER = 19;
97  
98      
99      final static int SHOULDER = 20;
100 
101     
102     final static int BOUNDARY = 21;
103 
104     
105     final List<Short> streams;
106 
107     
108     final Map<Short, XYPair[]> routes = new LinkedHashMap<>();
109 
110     
111 
112 
113 
114 
115     public Diagram(final Set<Short> streams) throws TrafficLightException
116     {
117         this.streams = new ArrayList<Short>(streams); 
118         this.streams.sort(new Comparator<Short>()
119         {
120 
121             @Override
122             public int compare(Short o1, Short o2)
123             {
124                 return o1 - o2;
125             }
126         });
127         
128         
129         
130         
131         
132         
133 
134         
135         
136         for (short stream = 1; stream <= 12; stream += 3)
137         {
138             int quadrant = (stream - 1) / 3;
139             this.routes.put(stream, rotateRoute(quadrant, assembleRoute(
140                     new RouteStep(-BOUNDARY, CAR_RIGHT), 
141                     new RouteStep(-SHOULDER, CAR_RIGHT,Command.STOP_LINE_AND_ICON), 
142                     new RouteStep(-CAR_CENTER, CAR_RIGHT), 
143                     new RouteStep(-CAR_CENTER, BOUNDARY))));
144             this.routes.put((short) (stream + 1), rotateRoute(quadrant, assembleRoute(
145                     new RouteStep(-BOUNDARY, CAR_CENTER), 
146                     new RouteStep(-SHOULDER, CAR_CENTER, Command.STOP_LINE_AND_ICON), 
147                     new RouteStep(Command.IF, stream + 1 + 60), 
148                         new RouteStep(-CAR_ROUNDABOUT_LEFT, CAR_CENTER), 
149                     new RouteStep(Command.ELSE), 
150                         new RouteStep(BOUNDARY, CAR_CENTER), 
151                     new RouteStep(Command.END_IF))));
152             this.routes.put((short) (stream + 2), rotateRoute(quadrant, assembleRoute(
153                     new RouteStep(-BOUNDARY, CAR_LEFT), 
154                     new RouteStep(-SHOULDER, CAR_LEFT, Command.STOP_LINE_AND_ICON), 
155                     new RouteStep(Command.IF, stream + 2 + 60), 
156                         new RouteStep(-CAR_ROUNDABOUT_LEFT, CAR_LEFT), 
157                     new RouteStep(Command.ELSE_IF, (stream + 10) % 12 + 60),
158                         new RouteStep(CAR_CENTER, CAR_LEFT), 
159                         new RouteStep(CAR_CENTER, CAR_ROUNDABOUT_LEFT),
160                     new RouteStep(Command.ELSE), 
161                         new RouteStep(-CAR_LEFT, CAR_LEFT), 
162                         new RouteStep(-CAR_LEFT, PT_DIV_L), 
163                         new RouteStep(-CAR_ROUNDABOUT_LEFT, PT_DIV_L), 
164                         new RouteStep(-CAR_ROUNDABOUT_LEFT, -CAR_LEFT), 
165                         new RouteStep(PT_DIV_L, -CAR_LEFT),
166                         new RouteStep(PT_DIV_L, -CAR_CENTER), 
167                         new RouteStep(CAR_CENTER, -CAR_CENTER),
168                         new RouteStep(CAR_CENTER, -BOUNDARY),
169                     new RouteStep(Command.END_IF))));
170         }
171         
172         for (short stream = 21; stream <= 28; stream += 2)
173         {
174             int quadrant = (stream - 19) / 2 % 4;
175             this.routes.put(stream, rotateRoute(quadrant, assembleRoute(
176                     new RouteStep(DIVIDER_1, BICYCLE, Command.ICON), 
177                     new RouteStep(SHOULDER, BICYCLE),
178                     new RouteStep(BOUNDARY, BICYCLE, Command.ICON))));
179             this.routes.put((short) (stream + 1), rotateRoute(quadrant, assembleRoute(
180                     new RouteStep(-BOUNDARY, BICYCLE), 
181                     new RouteStep(-DIVIDER_3, BICYCLE, Command.ICON),
182                     new RouteStep(Command.IF, stream), 
183                     new RouteStep(-DIVIDER_1, BICYCLE, Command.ICON),
184                     new RouteStep(Command.ELSE), 
185                         new RouteStep(SHOULDER, BICYCLE), 
186                         new RouteStep(BOUNDARY, BICYCLE, Command.ICON), 
187                     new RouteStep(Command.END_IF))));
188         }
189         
190         for (short stream = 31; stream <= 38; stream += 2)
191         {
192             int quadrant = (stream - 29) / 2 % 4;
193             this.routes.put(stream, rotateRoute(quadrant, assembleRoute(
194                     new RouteStep(DIVIDER_1, SIDEWALK), 
195                     new RouteStep(BOUNDARY, SIDEWALK))));
196             this.routes.put((short) (stream + 1), rotateRoute(quadrant, assembleRoute(
197                     new RouteStep(-BOUNDARY, SIDEWALK), 
198                     new RouteStep(Command.IF, stream), 
199                         new RouteStep(-DIVIDER_1, SIDEWALK), 
200                     new RouteStep(Command.ELSE), 
201                         new RouteStep(BOUNDARY, SIDEWALK),
202                     new RouteStep(Command.END_IF))));
203         }
204         
205         for (short stream = 41; stream <= 52; stream += 3)
206         {
207             int quadrant = (stream - 41) / 3;
208             this.routes.put(stream, rotateRoute(quadrant, assembleRoute(
209                     new RouteStep(-BOUNDARY, PT_DIV_L), 
210                     new RouteStep(-SHOULDER, PT_DIV_L, Command.STOP_LINE), 
211                     new RouteStep(-PT_SIDEWALK_SHOULDER, PT_DIV_L, Command.ICON),
212                     new RouteStep(-CAR_RIGHT, PT_DIV_L), 
213                     new RouteStep(-CAR_RIGHT, CAR_LEFT), 
214                     new RouteStep(-PT_DIV_L, CAR_LEFT), 
215                     new RouteStep(-PT_DIV_L, SHOULDER), 
216                     new RouteStep(-PT_DIV_L, BOUNDARY, Command.ICON))));
217             this.routes.put((short) (stream + 1), rotateRoute(quadrant, assembleRoute(
218                     new RouteStep(-BOUNDARY, PT_DIV_L), 
219                     new RouteStep(-SHOULDER, PT_DIV_L, Command.STOP_LINE), 
220                     new RouteStep(-PT_SIDEWALK_SHOULDER, PT_DIV_L, Command.ICON),
221                     new RouteStep(SHOULDER, PT_DIV_L), 
222                     new RouteStep(BOUNDARY, PT_DIV_L))));
223             this.routes.put((short) (stream + 2), rotateRoute(quadrant, assembleRoute(
224                     new RouteStep(-BOUNDARY, PT_DIV_L), 
225                     new RouteStep(-SHOULDER, PT_DIV_L, Command.STOP_LINE), 
226                     new RouteStep(-PT_SIDEWALK_SHOULDER, PT_DIV_L, Command.ICON),
227                     new RouteStep(-CAR_RIGHT, PT_DIV_L), 
228                     new RouteStep(-CAR_RIGHT, CAR_ROUNDABOUT_LEFT), 
229                     new RouteStep(-PT_DIV_L, CAR_ROUNDABOUT_LEFT), 
230                     new RouteStep(Command.IF, (stream + 2 - 40) % 12 + 60), 
231                         new RouteStep(-PT_DIV_L, -PT_DIV_L), 
232                         new RouteStep(PT_DIV_L, -PT_DIV_L), 
233                     new RouteStep(Command.ELSE), 
234                         new RouteStep(-PT_DIV_L, -CAR_CENTER), 
235                         new RouteStep(CAR_ROUNDABOUT_LEFT, -CAR_CENTER), 
236                         new RouteStep(CAR_ROUNDABOUT_LEFT, -CAR_RIGHT), 
237                         new RouteStep(PT_DIV_L, -CAR_RIGHT), 
238                     new RouteStep(Command.END_IF),
239                     new RouteStep(PT_DIV_L, -SHOULDER), 
240                     new RouteStep(PT_DIV_L, -BOUNDARY, Command.ICON))));
241         }
242         
243         for (short stream = 62; stream <= 72; stream += 3)
244         {
245             int quadrant = (stream - 61) / 3;
246             this.routes.put(stream, rotateRoute(quadrant, assembleRoute(
247                     new RouteStep(-CAR_ROUNDABOUT_LEFT, CAR_CENTER), 
248                     new RouteStep(CAR_ROUNDABOUT_LEFT, CAR_CENTER, Command.STOP_LINE_AND_ICON), 
249                     new RouteStep(BOUNDARY, CAR_CENTER))));
250             this.routes.put((short) (stream + 1), rotateRoute(quadrant, assembleRoute(
251                     new RouteStep(-CAR_ROUNDABOUT_LEFT, CAR_LEFT), 
252                     new RouteStep(CAR_ROUNDABOUT_LEFT, CAR_LEFT, Command.STOP_LINE_AND_ICON), 
253                     new RouteStep(CAR_CENTER, CAR_LEFT), 
254                     new RouteStep(Command.IF, ((stream - 61) + 11) % 12 + 60),
255                     new RouteStep(CAR_CENTER, CAR_ROUNDABOUT_LEFT), 
256                     new RouteStep(Command.ELSE), 
257                     new RouteStep(CAR_CENTER, -BOUNDARY), 
258                     new RouteStep(Command.END_IF))));
259         }
260        
261     }
262 
263     
264 
265 
266 
267 
268     private boolean streamExists(final short stream)
269     {
270         return this.streams.contains(stream);
271     }
272 
273     
274 
275 
276 
277 
278     public final static boolean isGrass(final int i)
279     {
280         return i == DIVIDER_1 || i == DIVIDER_2 || i == DIVIDER_3 || i == DIVIDER_4 || i == DIVIDER_5 || i == DIVIDER_6
281                 || i == DIVIDER_7 || i == SHOULDER;
282     }
283 
284     
285 
286 
287 
288 
289     final LaneType laneType(final int streamNumber)
290     {
291         if (streamNumber < 20 || streamNumber > 60 && streamNumber <= 80)
292         {
293             return LaneType.CAR_LANE;
294         }
295         if (streamNumber >= 20 && streamNumber < 30)
296         {
297             return LaneType.BICYCLE_LANE;
298         }
299         if (streamNumber >= 30 && streamNumber < 40)
300         {
301             return LaneType.PEDESTRIAN_LANE;
302         }
303         if (streamNumber > 40 && streamNumber <= 52 || streamNumber >= 81 && streamNumber <= 92)
304         {
305             return LaneType.PUBLIC_TRANSIT_LANE;
306         }
307         return null;
308     }
309 
310     
311 
312 
313     enum LaneType
314     {
315         
316         CAR_LANE,
317         
318         BICYCLE_LANE,
319         
320         PUBLIC_TRANSIT_LANE,
321         
322         PEDESTRIAN_LANE,
323     }
324 
325     
326 
327 
328 
329 
330 
331     final int rotatedX(final XYPair xyPair, final int rotation)
332     {
333         switch (rotation % 4)
334         {
335             case 0:
336                 return xyPair.getX();
337             case 1:
338                 return -xyPair.getY();
339             case 2:
340                 return -xyPair.getX();
341             case 3:
342                 return xyPair.getY();
343         }
344         return 0; 
345     }
346 
347     
348 
349 
350 
351 
352 
353     final int rotatedY(final XYPair xyPair, final int rotation)
354     {
355         switch (rotation % 4)
356         {
357             case 0:
358                 return xyPair.getY();
359             case 1:
360                 return xyPair.getX();
361             case 2:
362                 return -xyPair.getY();
363             case 3:
364                 return -xyPair.getX();
365         }
366         return 0; 
367     }
368 
369     
370 
371 
372     enum Command
373     {
374         
375         NO_OP,
376         
377         IF,
378         
379         ELSE,
380         
381         ELSE_IF,
382         
383         END_IF,
384         
385         STOP_LINE,
386         
387         ICON,
388         
389         STOP_LINE_AND_ICON,
390     }
391 
392     
393 
394 
395     class RouteStep
396     {
397         
398         final private int x;
399 
400         
401         final private int y;
402 
403         
404         final private Command command;
405 
406         
407         final private int streamCondition;
408 
409         
410 
411 
412 
413 
414         public RouteStep(final int x, final int y)
415         {
416             this.x = x;
417             this.y = y;
418             this.command = Command.NO_OP;
419             this.streamCondition = TrafficController.NO_STREAM;
420         }
421 
422         
423 
424 
425 
426 
427 
428 
429 
430         public RouteStep(final int x, final int y, final Command command) throws TrafficLightException
431         {
432             Throw.when(
433                     Command.STOP_LINE != command && Command.NO_OP != command && Command.ICON != command
434                             && Command.STOP_LINE_AND_ICON != command,
435                     TrafficLightException.class,
436                     "X and Y should only be provided with a NO_OP, STOP_LINE, ICON, or STOP_LINE_AND_ICON command; not with "
437                             + command);
438             this.x = x;
439             this.y = y;
440             this.command = command;
441             this.streamCondition = TrafficController.NO_STREAM;
442         }
443 
444         
445 
446 
447 
448 
449 
450 
451         public RouteStep(final Command command, final int streamCondition) throws TrafficLightException
452         {
453             Throw.when(Command.IF != command && Command.ELSE_IF != command, TrafficLightException.class,
454                     "RouteStep constructor with stream condition must use command IF or ELSE_IF");
455             this.x = TrafficController.NO_STREAM;
456             this.y = TrafficController.NO_STREAM;
457             this.command = command;
458             Throw.when(streamCondition == TrafficController.NO_STREAM, TrafficLightException.class,
459                     "IF or ELSE_IF need a valid traffic stream number");
460             this.streamCondition = streamCondition;
461         }
462 
463         
464 
465 
466 
467 
468         public RouteStep(final Command command) throws TrafficLightException
469         {
470             Throw.when(Command.ELSE != command && Command.END_IF != command, TrafficLightException.class,
471                     "RouteStep constructor with single command parameter requires ELSE or END_IF command");
472             this.x = TrafficController.NO_STREAM;
473             this.y = TrafficController.NO_STREAM;
474             this.command = command;
475             this.streamCondition = TrafficController.NO_STREAM;
476         }
477 
478         
479 
480 
481 
482         public int getX()
483         {
484             return this.x;
485         }
486 
487         
488 
489 
490 
491         public int getY()
492         {
493             return this.y;
494         }
495 
496         
497 
498 
499 
500         public Command getCommand()
501         {
502             return this.command;
503         }
504 
505         
506 
507 
508 
509         public int getStreamCondition()
510         {
511             return this.streamCondition;
512         }
513 
514         
515         @Override
516         public String toString()
517         {
518             return "RouteStep [x=" + this.x + ", y=" + this.y + ", command=" + this.command + ", streamCondition="
519                     + this.streamCondition + "]";
520         }
521 
522     }
523 
524     
525 
526 
527     class XYPair
528     {
529         
530         private final int x;
531 
532         
533         private final int y;
534 
535         
536 
537 
538 
539 
540         public XYPair(final int x, final int y)
541         {
542             this.x = x;
543             this.y = y;
544         }
545 
546         
547 
548 
549 
550         public XYPair(final RouteStep routeStep)
551         {
552             this.x = routeStep.getX();
553             this.y = routeStep.getY();
554         }
555 
556         
557 
558 
559 
560 
561         public XYPair(final XYPair in, final int quadrant)
562         {
563             this.x = rotatedX(in, quadrant);
564             this.y = rotatedY(in, quadrant);
565         }
566 
567         
568 
569 
570 
571         public int getX()
572         {
573             return this.x;
574         }
575 
576         
577 
578 
579 
580         public int getY()
581         {
582             return this.y;
583         }
584 
585         
586         @Override
587         public String toString()
588         {
589             return "XYPair [x=" + this.x + ", y=" + this.y + "]";
590         }
591 
592     }
593 
594     
595 
596 
597 
598 
599 
600 
601     private XYPair[] rotateRoute(final int quadrant, final RouteStep... steps) throws TrafficLightException
602     {
603         List<XYPair> route = new ArrayList<>();
604         boolean on = true;
605 
606         for (RouteStep step : steps)
607         {
608             switch (step.getCommand())
609             {
610                 case NO_OP:
611                 case STOP_LINE:
612                 case ICON:
613                 case STOP_LINE_AND_ICON:
614                     if (on)
615                     {
616                         route.add(new XYPair(new XYPair(step), quadrant));
617                     }
618                     break;
619 
620                 default:
621                     throw new TrafficLightException("Bad command in rotateRoute: " + step.getCommand());
622 
623             }
624         }
625         return route.toArray(new XYPair[route.size()]);
626     }
627 
628     
629 
630 
631 
632 
633 
634     private RouteStep[] assembleRoute(RouteStep... steps) throws TrafficLightException
635     {
636         List<RouteStep> result = new ArrayList<>();
637         RouteStep step;
638         for (int pointNo = 0; null != (step = routePoint(pointNo, steps)); pointNo++)
639         {
640             result.add(step);
641         }
642         return result.toArray(new RouteStep[result.size()]);
643     }
644 
645     
646 
647 
648 
649 
650 
651 
652     private RouteStep routePoint(final int pointNo, final RouteStep... steps) throws TrafficLightException
653     {
654         boolean active = true;
655         boolean beenActive = false;
656         int index = 0;
657 
658         for (RouteStep routeStep : steps)
659         {
660             switch (routeStep.getCommand())
661             {
662                 case NO_OP:
663                 case STOP_LINE:
664                 case ICON:
665                 case STOP_LINE_AND_ICON:
666                     if (active)
667                     {
668                         if (index++ == pointNo)
669                         {
670                             return routeStep;
671                         }
672                     }
673                     break;
674 
675                 case IF:
676                     active = streamExists((short) routeStep.getStreamCondition());
677                     beenActive = active;
678                     break;
679 
680                 case ELSE_IF:
681                     if (active)
682                     {
683                         active = false;
684                     }
685                     else if (!beenActive)
686                     {
687                         active = this.streams.contains(routeStep.getStreamCondition());
688                     }
689                     if (active)
690                     {
691                         beenActive = true;
692                     }
693                     break;
694 
695                 case ELSE:
696                     active = !beenActive;
697                     break;
698 
699                 case END_IF:
700                     active = true;
701                     break;
702 
703                 default:
704                     throw new TrafficLightException("Bad switch: " + routeStep);
705 
706             }
707         }
708         return null;
709     }
710 
711     
712 
713 
714 
715     public BufferedImage render()
716     {
717         int range = 2 * BOUNDARY + 1;
718         int cellSize = 10;
719         BufferedImage result = new BufferedImage(range * cellSize, range * cellSize, BufferedImage.TYPE_INT_RGB);
720         Graphics2D graphics = (Graphics2D) result.getGraphics();
721         graphics.setColor(Color.GREEN);
722         graphics.fillRect(0, 0, result.getWidth(), result.getHeight());
723         for (Short stream : this.streams)
724         {
725             switch (laneType(stream))
726             {
727                 case BICYCLE_LANE:
728                     graphics.setColor(Color.RED);
729                     break;
730 
731                 case CAR_LANE:
732                     graphics.setColor(Color.BLACK);
733                     break;
734 
735                 case PEDESTRIAN_LANE:
736                     graphics.setColor(Color.BLUE);
737                     break;
738 
739                 case PUBLIC_TRANSIT_LANE:
740                     graphics.setColor(Color.BLACK);
741                     break;
742 
743                 default:
744                     graphics.setColor(Color.WHITE);
745                     break;
746 
747             }
748             XYPair[] path = this.routes.get(stream);
749             if (null == path)
750             {
751                 System.err.println("Cannot find path for stream " + stream);
752                 continue;
753             }
754             XYPair prevPair = null;
755             for (XYPair xyPair : path)
756             {
757                 if (null != prevPair)
758                 {
759                     int dx = (int) Math.signum(xyPair.getX() - prevPair.getX());
760                     int dy = (int) Math.signum(xyPair.getY() - prevPair.getY());
761                     int x = prevPair.getX() + dx;
762                     int y = prevPair.getY() + dy;
763                     while (x != xyPair.getX() || y != xyPair.getY())
764                     {
765                         fillXYPair(graphics, new XYPair(x, y));
766                         if (x != xyPair.getX())
767                         {
768                             x += dx;
769                         }
770                         if (y != xyPair.getY())
771                         {
772                             y += dy;
773                         }
774                     }
775 
776                 }
777                 fillXYPair(graphics, xyPair);
778                 prevPair = xyPair;
779             }
780         }
781         return result;
782     }
783 
784     
785 
786 
787 
788 
789     private void fillXYPair(final Graphics2D graphics, final XYPair xyPair)
790     {
791         int cellSize = 10;
792         graphics.fillRect(cellSize * (BOUNDARY - xyPair.getX()), cellSize * (BOUNDARY - xyPair.getY()), cellSize, cellSize);
793     }
794 
795     
796 
797 
798 
799     public static void main(final String[] args)
800     {
801         SwingUtilities.invokeLater(new Runnable()
802         {
803             @Override
804             public void run()
805             {
806                 JFrame frame = new JFrame("Diagram test");
807                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
808                 frame.setMinimumSize(new Dimension(1000, 1000));
809                 JPanel mainPanel = new JPanel(new BorderLayout());
810                 frame.add(mainPanel);
811                 checkBoxPanel = new JPanel();
812                 checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.Y_AXIS));
813                 JScrollPane scrollPane = new JScrollPane(checkBoxPanel);
814                 scrollPane.setPreferredSize(new Dimension(150, 1000));
815                 mainPanel.add(scrollPane, BorderLayout.LINE_START);
816                 for (int stream = 1; stream <= 12; stream++)
817                 {
818                     checkBoxPanel.add(makeCheckBox(stream, stream % 3 == 2));
819                 }
820                 for (int stream = 21; stream <= 28; stream++)
821                 {
822                     checkBoxPanel.add(makeCheckBox(stream, false));
823                 }
824                 for (int stream = 31; stream <= 38; stream++)
825                 {
826                     checkBoxPanel.add(makeCheckBox(stream, false));
827                 }
828                 for (int stream = 41; stream <= 52; stream++)
829                 {
830                     checkBoxPanel.add(makeCheckBox(stream, false));
831                 }
832                 for (int stream = 61; stream <= 72; stream++)
833                 {
834                     if (stream % 3 == 1)
835                     {
836                         continue;
837                     }
838                     checkBoxPanel.add(makeCheckBox(stream, false));
839                 }
840                 testPanel = new JPanel();
841                 rebuildTestPanel();
842                 mainPanel.add(testPanel, BorderLayout.CENTER);
843                 frame.setVisible(true);
844             }
845         });
846 
847     }
848 
849     
850 
851 
852 
853 
854 
855     public static JCheckBox makeCheckBox(final int stream, final boolean initialState)
856     {
857         JCheckBox result = new JCheckBox(String.format("Stream %02d", stream));
858         result.setSelected(initialState);
859         result.addActionListener(new ActionListener()
860         {
861 
862             @Override
863             public void actionPerformed(ActionEvent e)
864             {
865                 rebuildTestPanel();
866             }
867         });
868         return result;
869     }
870 
871     
872     static JPanel testPanel = null;
873 
874     
875     static JPanel checkBoxPanel = null;
876 
877     
878 
879 
880     static void rebuildTestPanel()
881     {
882         testPanel.removeAll();
883         Set<Short> streamList = new LinkedHashSet<>();
884         for (Component c : checkBoxPanel.getComponents())
885         {
886             if (c instanceof JCheckBox)
887             {
888                 JCheckBox checkBox = (JCheckBox) c;
889                 if (checkBox.isSelected())
890                 {
891                     String caption = checkBox.getText();
892                     String streamText = caption.substring(caption.length() - 2);
893                     Short stream = Short.parseShort(streamText);
894                     streamList.add(stream);
895                 }
896             }
897         }
898         try
899         {
900             Diagramrol/trafcod/Diagram.html#Diagram">Diagram diagram = new Diagram(streamList);
901             testPanel.add(new JLabel(new ImageIcon(diagram.render())));
902         }
903         catch (TrafficLightException exception)
904         {
905             exception.printStackTrace();
906         }
907         testPanel.repaint();
908         testPanel.revalidate();
909     }
910 
911 }