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