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