1 package org.opentrafficsim.demo.carFollowing;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Dimension;
6 import java.awt.Frame;
7 import java.awt.GridBagConstraints;
8 import java.awt.GridBagLayout;
9 import java.awt.Insets;
10 import java.awt.Toolkit;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import java.awt.event.ItemEvent;
14 import java.awt.event.ItemListener;
15 import java.beans.PropertyChangeEvent;
16 import java.beans.PropertyChangeListener;
17 import java.util.ArrayList;
18 import java.util.List;
19
20 import javax.naming.NamingException;
21 import javax.swing.BoxLayout;
22 import javax.swing.ButtonGroup;
23 import javax.swing.JButton;
24 import javax.swing.JCheckBox;
25 import javax.swing.JComboBox;
26 import javax.swing.JFrame;
27 import javax.swing.JLabel;
28 import javax.swing.JPanel;
29 import javax.swing.JRadioButton;
30 import javax.swing.JScrollBar;
31 import javax.swing.JScrollPane;
32 import javax.swing.JSlider;
33 import javax.swing.JTextField;
34 import javax.swing.ScrollPaneConstants;
35 import javax.swing.SwingConstants;
36 import javax.swing.SwingUtilities;
37 import javax.swing.event.ChangeEvent;
38 import javax.swing.event.ChangeListener;
39 import javax.swing.event.DocumentEvent;
40 import javax.swing.event.DocumentListener;
41
42 import nl.tudelft.simulation.dsol.SimRuntimeException;
43
44 import org.djunits.locale.DefaultLocale;
45 import org.djunits.unit.UNITS;
46 import org.djunits.value.vdouble.scalar.Acceleration;
47 import org.djunits.value.vdouble.scalar.Duration;
48 import org.djunits.value.vdouble.scalar.Length;
49 import org.djunits.value.vdouble.scalar.Time;
50 import org.opentrafficsim.base.modelproperties.BooleanProperty;
51 import org.opentrafficsim.base.modelproperties.CompoundProperty;
52 import org.opentrafficsim.base.modelproperties.ContinuousProperty;
53 import org.opentrafficsim.base.modelproperties.IntegerProperty;
54 import org.opentrafficsim.base.modelproperties.ProbabilityDistributionProperty;
55 import org.opentrafficsim.base.modelproperties.Property;
56 import org.opentrafficsim.base.modelproperties.PropertyException;
57 import org.opentrafficsim.base.modelproperties.SelectionProperty;
58 import org.opentrafficsim.base.modelproperties.StringProperty;
59 import org.opentrafficsim.core.network.NetworkException;
60 import org.opentrafficsim.demo.trafficcontrol.TrafCODDemo2;
61 import org.opentrafficsim.gui.LabeledPanel;
62 import org.opentrafficsim.gui.ProbabilityDistributionEditor;
63 import org.opentrafficsim.gui.SimulatorFrame;
64 import org.opentrafficsim.road.modelproperties.IDMPropertySet;
65 import org.opentrafficsim.simulationengine.OTSSimulationException;
66 import org.opentrafficsim.simulationengine.WrappableAnimation;
67
68
69
70
71
72
73
74
75
76
77
78 public class SuperDemo implements UNITS
79 {
80
81 private JPanel propertyPanel;
82
83
84 @SuppressWarnings("checkstyle:visibilitymodifier")
85 protected JPanel simulationSelection;
86
87
88 @SuppressWarnings("checkstyle:visibilitymodifier")
89 protected List<Property<?>> activeProperties = null;
90
91
92 private LabeledPanel descriptionPanel;
93
94
95
96
97
98 public static void main(final String[] args)
99 {
100 SwingUtilities.invokeLater(new Runnable()
101 {
102 @Override
103 public void run()
104 {
105 try
106 {
107 JFrame frame = new SimulatorFrame("Open Traffic Simulator Demonstrations", new SuperDemo().buildGUI());
108 frame.setExtendedState(frame.getExtendedState() & ~Frame.MAXIMIZED_BOTH);
109
110
111
112 Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(frame.getGraphicsConfiguration());
113 int taskHeight = screenInsets.bottom;
114 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
115 frame.setSize(d.width / 2, d.height - taskHeight);
116 frame.setLocation(0, 0);
117 }
118 catch (PropertyException exception)
119 {
120 exception.printStackTrace();
121 }
122 }
123 });
124 }
125
126
127
128
129
130
131 public final JPanel buildGUI() throws PropertyException
132 {
133 final JPanel mainPanel = new JPanel(new BorderLayout());
134
135 mainPanel.setPreferredSize(new Dimension(800, 800));
136 final ArrayList<WrappableAnimation> demonstrations = new ArrayList<>();
137 demonstrations.add(new Straight());
138 demonstrations.add(new SequentialLanes());
139 demonstrations.add(new CircularLane());
140 demonstrations.add(new CircularRoad());
141 demonstrations.add(new XMLNetworks());
142 demonstrations.add(new OpenStreetMap());
143 demonstrations.add(new CrossingTrafficLights());
144 demonstrations.add(new TrafCODDemo2());
145
146 this.simulationSelection = new LabeledPanel("Network");
147 this.simulationSelection.setLayout(new GridBagLayout());
148 GridBagConstraints gbcSimulation = new GridBagConstraints();
149 gbcSimulation.gridx = 0;
150 gbcSimulation.gridy = -1;
151 gbcSimulation.anchor = GridBagConstraints.LINE_START;
152 gbcSimulation.weighty = 0;
153 gbcSimulation.fill = GridBagConstraints.HORIZONTAL;
154 final JPanel left = new JPanel(new GridBagLayout());
155 GridBagConstraints gbcLeft = new GridBagConstraints();
156 gbcLeft.gridx = 0;
157 gbcLeft.gridy = 0;
158 gbcLeft.anchor = GridBagConstraints.LINE_START;
159 gbcLeft.weighty = 0;
160 gbcLeft.fill = GridBagConstraints.HORIZONTAL;
161 final JLabel description = new JLabel("Please select a demonstration from the buttons on the left");
162 final JPanel centerPanel = new JPanel(new BorderLayout());
163 this.propertyPanel = new JPanel();
164 this.propertyPanel.setLayout(new BoxLayout(this.propertyPanel, BoxLayout.Y_AXIS));
165 rebuildPropertyPanel(new ArrayList<Property<?>>());
166 mainPanel.add(centerPanel, BorderLayout.CENTER);
167 this.descriptionPanel = new LabeledPanel("Description");
168 this.descriptionPanel.setLayout(new BorderLayout());
169 this.descriptionPanel.add(description);
170 centerPanel.add(this.descriptionPanel, BorderLayout.CENTER);
171 final JButton startButton = new JButton("Start simulation");
172 ButtonGroup buttonGroup = new ButtonGroup();
173 for (final WrappableAnimation demo : demonstrations)
174 {
175 CleverRadioButton button = new CleverRadioButton(demo);
176
177 button.addActionListener(new ActionListener()
178 {
179 @Override
180 public void actionPerformed(final ActionEvent e)
181 {
182
183 description.setText(demo.description());
184 startButton.setEnabled(true);
185 startButton.setVisible(true);
186 rebuildPropertyPanel(demo.getProperties());
187 }
188
189 });
190 buttonGroup.add(button);
191 gbcSimulation.gridy++;
192 this.simulationSelection.add(button, gbcSimulation);
193 }
194 JScrollPane scrollPane = new JScrollPane(left);
195 scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
196 scrollPane.getVerticalScrollBar().setUnitIncrement(10);
197 mainPanel.add(scrollPane, BorderLayout.LINE_START);
198 startButton.setEnabled(false);
199 startButton.setVisible(false);
200 startButton.addActionListener(new ActionListener()
201 {
202 @Override
203 public void actionPerformed(final ActionEvent e)
204 {
205 System.out.println("Starting simulation");
206 WrappableAnimation simulation = null;
207 for (Component c : SuperDemo.this.simulationSelection.getComponents())
208 {
209 if (c instanceof CleverRadioButton)
210 {
211 CleverRadioButton crb = (CleverRadioButton) c;
212 if (crb.isSelected())
213 {
214 simulation = crb.getAnimation();
215 }
216 }
217 }
218
219 if (null == simulation)
220 {
221 throw new Error("Cannot find a selected button");
222 }
223
224 try
225 {
226 System.out.println("Active properties: " + SuperDemo.this.activeProperties);
227 simulation.buildAnimator(new Time(0.0, SECOND), new Duration(0.0, SECOND), new Duration(3600.0, SECOND),
228 SuperDemo.this.activeProperties, null, false);
229 }
230 catch (SimRuntimeException | NetworkException | NamingException | OTSSimulationException
231 | PropertyException exception)
232 {
233 exception.printStackTrace();
234 }
235 }
236 });
237 gbcLeft.gridy++;
238 left.add(this.propertyPanel, gbcLeft);
239 gbcLeft.gridy++;
240 gbcLeft.weighty = 1;
241 JPanel filler = new JPanel();
242 filler.setMinimumSize(new Dimension(400, 0));
243 filler.setPreferredSize(new Dimension(400, 0));
244 left.add(filler, gbcLeft);
245 gbcLeft.weighty = 0;
246 gbcLeft.anchor = GridBagConstraints.SOUTH;
247 left.add(startButton, gbcLeft);
248 JPanel rightFiller = new JPanel();
249 rightFiller.setPreferredSize(new Dimension((int) new JScrollBar().getPreferredSize().getWidth(), 0));
250 gbcLeft.gridx = 2;
251 left.add(rightFiller, gbcLeft);
252 return mainPanel;
253 }
254
255
256
257
258
259 final void rebuildPropertyPanel(final List<Property<?>> properties)
260 {
261 this.propertyPanel.removeAll();
262 try
263 {
264 CompoundProperty simulationSettings =
265 new CompoundProperty("SimulationSettings", "Simulation settings",
266 "Select the simulation network and traffic composition", null, false, 0);
267
268
269
270
271 BooleanProperty dummy = new BooleanProperty("Dummy", "Dummy", "Dummy", false, false, 0);
272 simulationSettings.add(dummy);
273 if (properties.size() > 0)
274 {
275 while (true)
276 {
277 boolean movedAny = false;
278
279 for (Property<?> ap : properties)
280 {
281 if (ap.getDisplayPriority() < 100)
282 {
283
284 simulationSettings.add(ap);
285 properties.remove(ap);
286 movedAny = true;
287 break;
288 }
289 }
290 if (!movedAny)
291 {
292 break;
293 }
294 }
295 simulationSettings.add(new ProbabilityDistributionProperty("TrafficComposition", "Traffic composition",
296 "<html>Mix of passenger cars and trucks</html>", new String[] { "passenger car", "truck" },
297 new Double[] { 0.8, 0.2 }, false, 5));
298 CompoundProperty modelSelection =
299 new CompoundProperty("ModelSelection", "Model selection", "Modeling specific settings", null, false,
300 300);
301 modelSelection.add(new SelectionProperty("SimulationScale", "Simulation scale",
302 "Level of detail of the simulation", new String[] { "Micro", "Macro", "Meta" }, 0, true, 0));
303 modelSelection.add(new SelectionProperty("CarFollowingModel", "Car following model",
304 "<html>The car following model determines "
305 + "the acceleration that a vehicle will make taking into account "
306 + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
307 + "curvature of the road) capabilities of the vehicle and personality "
308 + "of the driver.</html>", new String[] { "IDM", "IDM+" }, 1, false, 1));
309 modelSelection.add(IDMPropertySet.makeIDMPropertySet("IDMCar", "Car",
310 new Acceleration(1.56, METER_PER_SECOND_2), new Acceleration(2.09, METER_PER_SECOND_2),
311 new Length(3.0, METER), new Duration(1.2, SECOND), 2));
312 modelSelection.add(IDMPropertySet.makeIDMPropertySet("IDMTruck", "Truck", new Acceleration(0.75,
313 METER_PER_SECOND_2), new Acceleration(1.25, METER_PER_SECOND_2), new Length(3.0, METER), new Duration(
314 1.2, SECOND), 3));
315 properties.add(properties.size() > 0 ? 1 : 0, modelSelection);
316 }
317 properties.add(0, simulationSettings);
318 boolean fixedDummy = false;
319 for (Property<?> p : new CompoundProperty("", "", "", properties, false, 0).displayOrderedValue())
320 {
321 JPanel propertySubPanel = makePropertyEditor(p);
322 if (!fixedDummy)
323 {
324
325 JPanel subPanel = (JPanel) propertySubPanel.getComponent(0);
326 subPanel.removeAll();
327 subPanel.add(this.simulationSelection);
328 fixedDummy = true;
329 }
330 this.propertyPanel.add(propertySubPanel);
331 }
332 simulationSettings.remove(dummy);
333 SuperDemo.this.activeProperties = properties;
334 }
335 catch (PropertyException exception)
336 {
337 exception.printStackTrace();
338 }
339 }
340
341
342
343
344
345
346 @SuppressWarnings("checkstyle:methodlength")
347 final JPanel makePropertyEditor(final Property<?> ap)
348 {
349 JPanel result;
350 if (ap instanceof SelectionProperty)
351 {
352 result = new JPanel();
353 result.setLayout(new BorderLayout());
354 final SelectionProperty sp = (SelectionProperty) ap;
355 final JComboBox<String> comboBox = new JComboBox<String>(sp.getOptionNames());
356 comboBox.setSelectedItem(sp.getValue());
357 comboBox.setToolTipText(sp.getDescription());
358 comboBox.addItemListener(new ItemListener()
359 {
360 @Override
361 public void itemStateChanged(final ItemEvent itemEvent)
362 {
363 if (itemEvent.getStateChange() == ItemEvent.SELECTED)
364 {
365 String itemText = (String) itemEvent.getItem();
366 try
367 {
368 sp.setValue(itemText);
369 }
370 catch (PropertyException exception)
371 {
372 exception.printStackTrace();
373 }
374 }
375 }
376 });
377 if (ap.isReadOnly())
378 {
379 comboBox.removeItemListener(comboBox.getItemListeners()[0]);
380 comboBox.addActionListener(new ActionListener()
381 {
382
383 @Override
384 public void actionPerformed(final ActionEvent actionEvent)
385 {
386 if (comboBox.getSelectedIndex() != 0)
387 {
388 comboBox.setSelectedIndex(0);
389 }
390 }
391 });
392 }
393 result.setToolTipText(sp.getDescription());
394 result.add(new JLabel(ap.getShortName() + ": "), BorderLayout.LINE_START);
395 result.add(comboBox, BorderLayout.CENTER);
396 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) comboBox.getPreferredSize().getHeight()));
397 }
398 else if (ap instanceof ProbabilityDistributionProperty)
399 {
400 result = new LabeledPanel(ap.getShortName());
401 result.setLayout(new BorderLayout());
402 final ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
403 final ProbabilityDistributionEditor pdpe = new ProbabilityDistributionEditor(pdp.getElementNames(), pdp.getValue());
404 pdpe.addPropertyChangeListener(new PropertyChangeListener()
405 {
406 @Override
407 public void propertyChange(final PropertyChangeEvent arg0)
408 {
409 try
410 {
411 pdp.setValue(pdpe.getProbabilities());
412 }
413 catch (PropertyException exception)
414 {
415 exception.printStackTrace();
416 }
417 }
418
419 });
420 result.add(pdpe, BorderLayout.LINE_END);
421 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) new JLabel("ABC").getPreferredSize().getHeight()));
422 result.setToolTipText(pdp.getDescription());
423 }
424 else if (ap instanceof IntegerProperty)
425 {
426 final IntegerProperty ip = (IntegerProperty) ap;
427 result = new LabeledPanel(ap.getShortName());
428 result.setLayout(new BorderLayout());
429 final JSlider slider = new JSlider();
430 slider.setMaximum(ip.getMaximumValue());
431 slider.setMinimum(ip.getMinimumValue());
432 slider.setValue(ip.getValue());
433 slider.setPaintTicks(true);
434 final JLabel currentValue =
435 new JLabel(String.format(DefaultLocale.getLocale(), ip.getFormatString(), ip.getValue()),
436 SwingConstants.RIGHT);
437 slider.addChangeListener(new ChangeListener()
438 {
439 @Override
440 public void stateChanged(final ChangeEvent changeEvent)
441 {
442 int value = slider.getValue();
443 currentValue.setText(String.format(DefaultLocale.getLocale(), ip.getFormatString(), value));
444 if (slider.getValueIsAdjusting())
445 {
446 return;
447 }
448 try
449 {
450 ip.setValue(value);
451 }
452 catch (PropertyException exception)
453 {
454 exception.printStackTrace();
455 }
456 }
457 });
458 result.setToolTipText(ap.getDescription());
459 result.add(slider, BorderLayout.CENTER);
460 result.add(currentValue, BorderLayout.SOUTH);
461 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) slider.getPreferredSize().getHeight()));
462 }
463 else if (ap instanceof ContinuousProperty)
464 {
465 final ContinuousProperty cp = (ContinuousProperty) ap;
466 result = new LabeledPanel(ap.getShortName());
467 result.setLayout(new BorderLayout());
468 final JSlider slider = new JSlider();
469 final int useSteps = 1000;
470 slider.setMaximum(useSteps);
471 slider.setMinimum(0);
472 slider.setValue((int) (useSteps * (cp.getValue() - cp.getMinimumValue()) / (cp.getMaximumValue() - cp
473 .getMinimumValue())));
474 final JLabel currentValue =
475 new JLabel(String.format(DefaultLocale.getLocale(), cp.getFormatString(), cp.getValue()),
476 SwingConstants.RIGHT);
477 slider.addChangeListener(new ChangeListener()
478 {
479 @Override
480 public void stateChanged(final ChangeEvent changeEvent)
481 {
482 double value =
483 slider.getValue() * (cp.getMaximumValue() - cp.getMinimumValue()) / useSteps + cp.getMinimumValue();
484 currentValue.setText(String.format(DefaultLocale.getLocale(), cp.getFormatString(), value));
485 if (slider.getValueIsAdjusting())
486 {
487 return;
488 }
489 try
490 {
491 cp.setValue(value);
492 }
493 catch (PropertyException exception)
494 {
495 exception.printStackTrace();
496 }
497 }
498 });
499 result.setToolTipText(ap.getDescription());
500 result.add(slider, BorderLayout.CENTER);
501 result.add(currentValue, BorderLayout.SOUTH);
502 result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) slider.getPreferredSize().getHeight() * 4));
503 }
504 else if (ap instanceof BooleanProperty)
505 {
506 final BooleanProperty bp = (BooleanProperty) ap;
507 result = new JPanel(new BorderLayout());
508 final JCheckBox checkBox = new JCheckBox(bp.getShortName(), bp.getValue());
509 checkBox.setToolTipText(bp.getDescription());
510 checkBox.setEnabled(!bp.isReadOnly());
511 checkBox.addChangeListener(new ChangeListener()
512 {
513 @Override
514 public void stateChanged(final ChangeEvent arg0)
515 {
516 try
517 {
518 bp.setValue(checkBox.isSelected());
519 }
520 catch (PropertyException exception)
521 {
522 exception.printStackTrace();
523 }
524 }
525 });
526 JPanel filler = new JPanel();
527 filler.setPreferredSize(new Dimension(0, (int) checkBox.getPreferredSize().getHeight()));
528 result.add(checkBox, BorderLayout.CENTER);
529 result.add(filler, BorderLayout.LINE_END);
530 }
531 else if (ap instanceof CompoundProperty)
532 {
533 CompoundProperty cp = (CompoundProperty) ap;
534 result = new LabeledPanel(ap.getShortName());
535 result.setLayout(new BoxLayout(result, BoxLayout.Y_AXIS));
536 for (Property<?> subProperty : cp.displayOrderedValue())
537 {
538 result.add(makePropertyEditor(subProperty));
539 }
540 }
541 else if (ap instanceof StringProperty)
542 {
543 StringProperty sp = (StringProperty) ap;
544 result = new LabeledPanel(sp.getShortName());
545 result.setLayout(new BorderLayout());
546 JTextField textField = new JTextField(sp.getValue(), 30);
547
548 textField.getDocument().addDocumentListener(new DocumentListener()
549 {
550
551 @Override
552 public void insertUpdate(final DocumentEvent e)
553 {
554 try
555 {
556 sp.setValue(textField.getText());
557 }
558 catch (PropertyException exception)
559 {
560 exception.printStackTrace();
561 }
562 }
563
564 @Override
565 public void removeUpdate(final DocumentEvent e)
566 {
567 try
568 {
569 sp.setValue(textField.getText());
570 }
571 catch (PropertyException exception)
572 {
573 exception.printStackTrace();
574 }
575 }
576
577 @Override
578 public void changedUpdate(final DocumentEvent e)
579 {
580 try
581 {
582 sp.setValue(textField.getText());
583 }
584 catch (PropertyException exception)
585 {
586 exception.printStackTrace();
587 }
588 }
589
590 });
591 result.add(textField, BorderLayout.CENTER);
592 }
593 else
594 {
595 throw new Error("Unhandled property: " + ap.getDescription());
596 }
597 return result;
598 }
599
600 }
601
602
603 class CleverRadioButton extends JRadioButton
604 {
605
606 private static final long serialVersionUID = 20141217L;
607
608
609 private final WrappableAnimation animation;
610
611
612
613
614
615
616 CleverRadioButton(final WrappableAnimation animation)
617 {
618 super(animation.shortName());
619 this.animation = animation;
620 }
621
622
623
624
625
626 public final WrappableAnimation getAnimation()
627 {
628 return this.animation;
629 }
630 }