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