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