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