View Javadoc
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  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 org.djunits.locale.DefaultLocale;
43  import org.djunits.unit.UNITS;
44  import org.djunits.value.vdouble.scalar.Acceleration;
45  import org.djunits.value.vdouble.scalar.Duration;
46  import org.djunits.value.vdouble.scalar.Length;
47  import org.djunits.value.vdouble.scalar.Time;
48  import org.opentrafficsim.base.modelproperties.BooleanProperty;
49  import org.opentrafficsim.base.modelproperties.CompoundProperty;
50  import org.opentrafficsim.base.modelproperties.ContinuousProperty;
51  import org.opentrafficsim.base.modelproperties.IntegerProperty;
52  import org.opentrafficsim.base.modelproperties.ProbabilityDistributionProperty;
53  import org.opentrafficsim.base.modelproperties.Property;
54  import org.opentrafficsim.base.modelproperties.PropertyException;
55  import org.opentrafficsim.base.modelproperties.SelectionProperty;
56  import org.opentrafficsim.base.modelproperties.StringProperty;
57  import org.opentrafficsim.core.network.NetworkException;
58  import org.opentrafficsim.demo.trafficcontrol.TrafCODDemo2;
59  import org.opentrafficsim.gui.LabeledPanel;
60  import org.opentrafficsim.gui.ProbabilityDistributionEditor;
61  import org.opentrafficsim.gui.SimulatorFrame;
62  import org.opentrafficsim.road.modelproperties.IDMPropertySet;
63  import org.opentrafficsim.simulationengine.OTSSimulationException;
64  import org.opentrafficsim.simulationengine.WrappableAnimation;
65  
66  import nl.tudelft.simulation.dsol.SimRuntimeException;
67  
68  /**
69   * Several demos in one application.
70   * <p>
71   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
72   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
73   * <p>
74   * $LastChangedDate: 2018-09-19 13:55:45 +0200 (Wed, 19 Sep 2018) $, @version $Revision: 4006 $, by $Author: averbraeck $,
75   * initial version 17 dec. 2014 <br>
76   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
77   */
78  public class SuperDemo implements UNITS
79  {
80      /** The JPanel that holds the user settable properties. */
81      private JPanel propertyPanel;
82  
83      /** The JPanel that holds the simulation selection radio buttons. */
84      @SuppressWarnings("checkstyle:visibilitymodifier")
85      protected JPanel simulationSelection;
86  
87      /** Properties of the currently selected demonstration. */
88      @SuppressWarnings("checkstyle:visibilitymodifier")
89      protected List<Property<?>> activeProperties = null;
90  
91      /** Panel with the description of the currently selected demonstration. */
92      private LabeledPanel descriptionPanel;
93  
94      /**
95       * Start the application.
96       * @param args String[]; the command line arguments (not used)
97       */
98      public static void main(final String[] args)
99      {
100         SwingUtilities.invokeLater(new Runnable()
101         {
102             @Override
103             public void run()
104             {
105                 try
106                 {
107                     JFrame frame = new SimulatorFrame("Open Traffic Simulator Demonstrations", new SuperDemo().buildGUI());
108                     frame.setExtendedState(frame.getExtendedState() & ~Frame.MAXIMIZED_BOTH);
109                     // frame.setExtendedState(frame.getExtendedState() | Frame.MAXIMIZED_VERT);
110                     // The code above does not work; the code below does work. Code found on
111                     // http://stackoverflow.com/questions/5195634/how-to-maximize-a-the-height-of-a-jframe
112                     Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(frame.getGraphicsConfiguration());
113                     int taskHeight = screenInsets.bottom;
114                     Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
115                     frame.setSize(d.width / 2, d.height - taskHeight);
116                     frame.setLocation(0, 0);
117                 }
118                 catch (PropertyException exception)
119                 {
120                     exception.printStackTrace();
121                 }
122             }
123         });
124     }
125 
126     /**
127      * Build the GUI.
128      * @return JPanel; the JPanel that holds the application
129      * @throws PropertyException when one of the demonstrations has a user modified property with the empty string as key
130      */
131     public final JPanel buildGUI() throws PropertyException
132     {
133         final JPanel mainPanel = new JPanel(new BorderLayout());
134         // Ensure that the window does not shrink into (almost) nothingness when un-maximized
135         mainPanel.setPreferredSize(new Dimension(800, 800));
136         final ArrayList<WrappableAnimation> demonstrations = new ArrayList<>();
137         demonstrations.add(new Straight());
138         demonstrations.add(new SequentialLanes());
139         demonstrations.add(new CircularLane());
140         demonstrations.add(new CircularRoad());
141         demonstrations.add(new XMLNetworks());
142         demonstrations.add(new XMLNetworks2());
143         demonstrations.add(new XMLSampler());
144         demonstrations.add(new OpenStreetMap());
145         demonstrations.add(new CrossingTrafficLights());
146         demonstrations.add(new TrafCODDemo2());
147         // final JPanel left = new LabeledPanel("Simulation Settings");
148         this.simulationSelection = new LabeledPanel("Network");
149         this.simulationSelection.setLayout(new GridBagLayout());
150         GridBagConstraints gbcSimulation = new GridBagConstraints();
151         gbcSimulation.gridx = 0;
152         gbcSimulation.gridy = -1;
153         gbcSimulation.anchor = GridBagConstraints.LINE_START;
154         gbcSimulation.weighty = 0;
155         gbcSimulation.fill = GridBagConstraints.HORIZONTAL;
156         final JPanel left = new JPanel(new GridBagLayout());
157         GridBagConstraints gbcLeft = new GridBagConstraints();
158         gbcLeft.gridx = 0;
159         gbcLeft.gridy = 0;
160         gbcLeft.anchor = GridBagConstraints.LINE_START;
161         gbcLeft.weighty = 0;
162         gbcLeft.fill = GridBagConstraints.HORIZONTAL;
163         final JLabel description = new JLabel("Please select a demonstration from the buttons on the left");
164         final JPanel centerPanel = new JPanel(new BorderLayout());
165         this.propertyPanel = new JPanel();
166         this.propertyPanel.setLayout(new BoxLayout(this.propertyPanel, BoxLayout.Y_AXIS));
167         rebuildPropertyPanel(new ArrayList<Property<?>>());
168         mainPanel.add(centerPanel, BorderLayout.CENTER);
169         this.descriptionPanel = new LabeledPanel("Description");
170         this.descriptionPanel.setLayout(new BorderLayout());
171         this.descriptionPanel.add(description);
172         centerPanel.add(this.descriptionPanel, BorderLayout.CENTER);
173         final JButton startButton = new JButton("Start simulation");
174         ButtonGroup buttonGroup = new ButtonGroup();
175         for (final WrappableAnimation demo : demonstrations)
176         {
177             CleverRadioButton button = new CleverRadioButton(demo);
178             // button.setPreferredSize(new Dimension(Integer.MAX_VALUE, button.getPreferredSize().height));
179             button.addActionListener(new ActionListener()
180             {
181                 @Override
182                 public void actionPerformed(final ActionEvent e)
183                 {
184                     // System.out.println("selected " + demo.shortName());
185                     description.setText(demo.description());
186                     startButton.setEnabled(true);
187                     startButton.setVisible(true);
188                     rebuildPropertyPanel(demo.getProperties());
189                 }
190 
191             });
192             buttonGroup.add(button);
193             gbcSimulation.gridy++;
194             this.simulationSelection.add(button, gbcSimulation);
195         }
196         JScrollPane scrollPane = new JScrollPane(left);
197         scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
198         scrollPane.getVerticalScrollBar().setUnitIncrement(10);
199         mainPanel.add(scrollPane, BorderLayout.LINE_START);
200         startButton.setEnabled(false);
201         startButton.setVisible(false);
202         startButton.addActionListener(new ActionListener()
203         {
204             @Override
205             public void actionPerformed(final ActionEvent e)
206             {
207                 System.out.println("Starting simulation");
208                 WrappableAnimation simulation = null;
209                 for (Component c : SuperDemo.this.simulationSelection.getComponents())
210                 {
211                     if (c instanceof CleverRadioButton)
212                     {
213                         CleverRadioButton crb = (CleverRadioButton) c;
214                         if (crb.isSelected())
215                         {
216                             simulation = crb.getAnimation();
217                         }
218                     }
219                 }
220 
221                 if (null == simulation)
222                 {
223                     throw new Error("Cannot find a selected button");
224                 }
225 
226                 try
227                 {
228                     System.out.println("Active properties: " + SuperDemo.this.activeProperties);
229                     simulation.buildAnimator(Time.ZERO, Duration.ZERO, new Duration(3600.0, SECOND),
230                             SuperDemo.this.activeProperties, null, false);
231                 }
232                 catch (SimRuntimeException | NetworkException | NamingException | OTSSimulationException 
233                         | PropertyException exception)
234                 {
235                     exception.printStackTrace();
236                 }
237             }
238         });
239         gbcLeft.gridy++;
240         left.add(this.propertyPanel, gbcLeft);
241         gbcLeft.gridy++;
242         gbcLeft.weighty = 1;
243         JPanel filler = new JPanel();
244         filler.setMinimumSize(new Dimension(400, 0));
245         filler.setPreferredSize(new Dimension(400, 0));
246         left.add(filler, gbcLeft); // add a filler that also enforces a reasonable width
247         gbcLeft.weighty = 0;
248         gbcLeft.anchor = GridBagConstraints.SOUTH;
249         left.add(startButton, gbcLeft);
250         JPanel rightFiller = new JPanel();
251         rightFiller.setPreferredSize(new Dimension((int) new JScrollBar().getPreferredSize().getWidth(), 0));
252         gbcLeft.gridx = 2;
253         left.add(rightFiller, gbcLeft);
254         return mainPanel;
255     }
256 
257     /**
258      * Regenerate the contents of the propertyPanel.
259      * @param properties ArrayList&lt;AbstractProperty&lt;?&gt;&gt;; the demo-specific properties to display
260      */
261     final void rebuildPropertyPanel(final List<Property<?>> properties)
262     {
263         this.propertyPanel.removeAll();
264         try
265         {
266             CompoundProperty simulationSettings =
267                     new CompoundProperty("SimulationSettings", "Simulation settings",
268                             "Select the simulation network and traffic composition", null, false, 0);
269             /*
270              * This is ugly, but it gets the job done... Insert a dummy property at the top and later replace the property
271              * editor for the dummy property by the simulationSelection JPanel.
272              */
273             BooleanProperty dummy = new BooleanProperty("Dummy", "Dummy", "Dummy", false, false, 0);
274             simulationSettings.add(dummy);
275             if (properties.size() > 0)
276             {
277                 while (true)
278                 {
279                     boolean movedAny = false;
280                     // Move the properties that has display priority < 100 into the simulationSettings group.
281                     for (Property<?> ap : properties)
282                     {
283                         if (ap.getDisplayPriority() < 100)
284                         {
285                             // Move it into the simulationSettings group
286                             simulationSettings.add(ap);
287                             properties.remove(ap);
288                             movedAny = true;
289                             break;
290                         }
291                     }
292                     if (!movedAny)
293                     {
294                         break;
295                     }
296                 }
297                 simulationSettings.add(new ProbabilityDistributionProperty("TrafficComposition", "Traffic composition",
298                         "<html>Mix of passenger cars and trucks</html>", new String[] { "passenger car", "truck" },
299                         new Double[] { 0.8, 0.2 }, false, 5));
300                 CompoundProperty modelSelection =
301                         new CompoundProperty("ModelSelection", "Model selection", "Modeling specific settings", null, false,
302                                 300);
303                 modelSelection.add(new SelectionProperty("SimulationScale", "Simulation scale",
304                         "Level of detail of the simulation", new String[] { "Micro", "Macro", "Meta" }, 0, true, 0));
305                 modelSelection.add(new SelectionProperty("CarFollowingModel", "Car following model",
306                         "<html>The car following model determines "
307                                 + "the acceleration that a vehicle will make taking into account "
308                                 + "nearby vehicles, infrastructural restrictions (e.g. speed limit, "
309                                 + "curvature of the road) capabilities of the vehicle and personality "
310                                 + "of the driver.</html>", new String[] { "IDM", "IDM+" }, 1, false, 1));
311                 modelSelection.add(IDMPropertySet.makeIDMPropertySet("IDMCar", "Car",
312                         new Acceleration(1.56, METER_PER_SECOND_2), new Acceleration(2.09, METER_PER_SECOND_2),
313                         new Length(3.0, METER), new Duration(1.2, SECOND), 2));
314                 modelSelection.add(IDMPropertySet.makeIDMPropertySet("IDMTruck", "Truck", new Acceleration(0.75,
315                         METER_PER_SECOND_2), new Acceleration(1.25, METER_PER_SECOND_2), new Length(3.0, METER), new Duration(
316                         1.2, SECOND), 3));
317                 properties.add(properties.size() > 0 ? 1 : 0, modelSelection);
318             }
319             properties.add(0, simulationSettings);
320             boolean fixedDummy = false;
321             for (Property<?> p : new CompoundProperty("", "", "", properties, false, 0).displayOrderedValue())
322             {
323                 JPanel propertySubPanel = makePropertyEditor(p);
324                 if (!fixedDummy)
325                 {
326                     // Replace the dummy property editor by the simulationSelection JPanel.
327                     JPanel subPanel = (JPanel) propertySubPanel.getComponent(0);
328                     subPanel.removeAll();
329                     subPanel.add(this.simulationSelection);
330                     fixedDummy = true;
331                 }
332                 this.propertyPanel.add(propertySubPanel);
333             }
334             simulationSettings.remove(dummy);
335             SuperDemo.this.activeProperties = properties;
336         }
337         catch (PropertyException exception)
338         {
339             exception.printStackTrace();
340         }
341     }
342 
343     /**
344      * Create a graphical editor for an AbstractProperty.
345      * @param ap AbstractProperty; the abstract property for which an editor must be created
346      * @return JPanel
347      */
348     @SuppressWarnings("checkstyle:methodlength")
349     final JPanel makePropertyEditor(final Property<?> ap)
350     {
351         JPanel result;
352         if (ap instanceof SelectionProperty)
353         {
354             result = new JPanel();
355             result.setLayout(new BorderLayout());
356             final SelectionProperty sp = (SelectionProperty) ap;
357             final JComboBox<String> comboBox = new JComboBox<String>(sp.getOptionNames());
358             comboBox.setSelectedItem(sp.getValue());
359             comboBox.setToolTipText(sp.getDescription());
360             comboBox.addItemListener(new ItemListener()
361             {
362                 @Override
363                 public void itemStateChanged(final ItemEvent itemEvent)
364                 {
365                     if (itemEvent.getStateChange() == ItemEvent.SELECTED)
366                     {
367                         String itemText = (String) itemEvent.getItem();
368                         try
369                         {
370                             sp.setValue(itemText);
371                         }
372                         catch (PropertyException exception)
373                         {
374                             exception.printStackTrace();
375                         }
376                     }
377                 }
378             });
379             if (ap.isReadOnly())
380             {
381                 comboBox.removeItemListener(comboBox.getItemListeners()[0]);
382                 comboBox.addActionListener(new ActionListener()
383                 {
384 
385                     @Override
386                     public void actionPerformed(final ActionEvent actionEvent)
387                     {
388                         if (comboBox.getSelectedIndex() != 0)
389                         {
390                             comboBox.setSelectedIndex(0);
391                         }
392                     }
393                 });
394             }
395             result.setToolTipText(sp.getDescription());
396             result.add(new JLabel(ap.getShortName() + ": "), BorderLayout.LINE_START);
397             result.add(comboBox, BorderLayout.CENTER);
398             result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) comboBox.getPreferredSize().getHeight()));
399         }
400         else if (ap instanceof ProbabilityDistributionProperty)
401         {
402             result = new LabeledPanel(ap.getShortName());
403             result.setLayout(new BorderLayout());
404             final ProbabilityDistributionProperty pdp = (ProbabilityDistributionProperty) ap;
405             final ProbabilityDistributionEditor pdpe = new ProbabilityDistributionEditor(pdp.getElementNames(), pdp.getValue());
406             pdpe.addPropertyChangeListener(new PropertyChangeListener()
407             {
408                 @Override
409                 public void propertyChange(final PropertyChangeEvent arg0)
410                 {
411                     try
412                     {
413                         pdp.setValue(pdpe.getProbabilities());
414                     }
415                     catch (PropertyException exception)
416                     {
417                         exception.printStackTrace();
418                     }
419                 }
420 
421             });
422             result.add(pdpe, BorderLayout.LINE_END);
423             result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) new JLabel("ABC").getPreferredSize().getHeight()));
424             result.setToolTipText(pdp.getDescription());
425         }
426         else if (ap instanceof IntegerProperty)
427         {
428             final IntegerProperty ip = (IntegerProperty) ap;
429             result = new LabeledPanel(ap.getShortName());
430             result.setLayout(new BorderLayout());
431             final JSlider slider = new JSlider();
432             slider.setMaximum(ip.getMaximumValue());
433             slider.setMinimum(ip.getMinimumValue());
434             slider.setValue(ip.getValue());
435             slider.setPaintTicks(true);
436             final JLabel currentValue =
437                     new JLabel(String.format(DefaultLocale.getLocale(), ip.getFormatString(), ip.getValue()),
438                             SwingConstants.RIGHT);
439             slider.addChangeListener(new ChangeListener()
440             {
441                 @Override
442                 public void stateChanged(final ChangeEvent changeEvent)
443                 {
444                     int value = slider.getValue();
445                     currentValue.setText(String.format(DefaultLocale.getLocale(), ip.getFormatString(), value));
446                     if (slider.getValueIsAdjusting())
447                     {
448                         return;
449                     }
450                     try
451                     {
452                         ip.setValue(value);
453                     }
454                     catch (PropertyException exception)
455                     {
456                         exception.printStackTrace();
457                     }
458                 }
459             });
460             result.setToolTipText(ap.getDescription());
461             result.add(slider, BorderLayout.CENTER);
462             result.add(currentValue, BorderLayout.SOUTH);
463             result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) slider.getPreferredSize().getHeight()));
464         }
465         else if (ap instanceof ContinuousProperty)
466         {
467             final ContinuousProperty cp = (ContinuousProperty) ap;
468             result = new LabeledPanel(ap.getShortName());
469             result.setLayout(new BorderLayout());
470             final JSlider slider = new JSlider();
471             final int useSteps = 1000;
472             slider.setMaximum(useSteps);
473             slider.setMinimum(0);
474             slider.setValue((int) (useSteps * (cp.getValue() - cp.getMinimumValue()) / (cp.getMaximumValue() - cp
475                     .getMinimumValue())));
476             final JLabel currentValue =
477                     new JLabel(String.format(DefaultLocale.getLocale(), cp.getFormatString(), cp.getValue()),
478                             SwingConstants.RIGHT);
479             slider.addChangeListener(new ChangeListener()
480             {
481                 @Override
482                 public void stateChanged(final ChangeEvent changeEvent)
483                 {
484                     double value =
485                             slider.getValue() * (cp.getMaximumValue() - cp.getMinimumValue()) / useSteps + cp.getMinimumValue();
486                     currentValue.setText(String.format(DefaultLocale.getLocale(), cp.getFormatString(), value));
487                     if (slider.getValueIsAdjusting())
488                     {
489                         return;
490                     }
491                     try
492                     {
493                         cp.setValue(value);
494                     }
495                     catch (PropertyException exception)
496                     {
497                         exception.printStackTrace();
498                     }
499                 }
500             });
501             result.setToolTipText(ap.getDescription());
502             result.add(slider, BorderLayout.CENTER);
503             result.add(currentValue, BorderLayout.SOUTH);
504             result.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int) slider.getPreferredSize().getHeight() * 4));
505         }
506         else if (ap instanceof BooleanProperty)
507         {
508             final BooleanProperty bp = (BooleanProperty) ap;
509             result = new JPanel(new BorderLayout());
510             final JCheckBox checkBox = new JCheckBox(bp.getShortName(), bp.getValue());
511             checkBox.setToolTipText(bp.getDescription());
512             checkBox.setEnabled(!bp.isReadOnly());
513             checkBox.addChangeListener(new ChangeListener()
514             {
515                 @Override
516                 public void stateChanged(final ChangeEvent arg0)
517                 {
518                     try
519                     {
520                         bp.setValue(checkBox.isSelected());
521                     }
522                     catch (PropertyException exception)
523                     {
524                         exception.printStackTrace();
525                     }
526                 }
527             });
528             JPanel filler = new JPanel();
529             filler.setPreferredSize(new Dimension(0, (int) checkBox.getPreferredSize().getHeight()));
530             result.add(checkBox, BorderLayout.CENTER);
531             result.add(filler, BorderLayout.LINE_END);
532         }
533         else if (ap instanceof CompoundProperty)
534         {
535             CompoundProperty cp = (CompoundProperty) ap;
536             result = new LabeledPanel(ap.getShortName());
537             result.setLayout(new BoxLayout(result, BoxLayout.Y_AXIS));
538             for (Property<?> subProperty : cp.displayOrderedValue())
539             {
540                 result.add(makePropertyEditor(subProperty));
541             }
542         }
543         else if (ap instanceof StringProperty)
544         {
545             StringProperty sp = (StringProperty) ap;
546             result = new LabeledPanel(sp.getShortName());
547             result.setLayout(new BorderLayout());
548             JTextField textField = new JTextField(sp.getValue(), 30);
549             // TODO add a listener that detects editing
550             textField.getDocument().addDocumentListener(new DocumentListener()
551             {
552 
553                 @Override
554                 public void insertUpdate(final DocumentEvent e)
555                 {
556                     try
557                     {
558                         sp.setValue(textField.getText());
559                     }
560                     catch (PropertyException exception)
561                     {
562                         exception.printStackTrace();
563                     }
564                 }
565 
566                 @Override
567                 public void removeUpdate(final DocumentEvent e)
568                 {
569                     try
570                     {
571                         sp.setValue(textField.getText());
572                     }
573                     catch (PropertyException exception)
574                     {
575                         exception.printStackTrace();
576                     }
577                 }
578 
579                 @Override
580                 public void changedUpdate(final DocumentEvent e)
581                 {
582                     try
583                     {
584                         sp.setValue(textField.getText());
585                     }
586                     catch (PropertyException exception)
587                     {
588                         exception.printStackTrace();
589                     }
590                 }
591 
592             });
593             result.add(textField, BorderLayout.CENTER);
594         }
595         else
596         {
597             throw new Error("Unhandled property: " + ap.getDescription());
598         }
599         return result;
600     }
601 
602 }
603 
604 /** JRadioButton that also stores a WrappableAnimation. */
605 class CleverRadioButton extends JRadioButton
606 {
607     /** */
608     private static final long serialVersionUID = 20141217L;
609 
610     /** The WrappableAnimation. */
611     private final WrappableAnimation animation;
612 
613     /**
614      * Construct a JRadioButton that also stores a WrappableAnimation.
615      * @param animation WrappableAnimation; the simulation to run if this radio button is selected and the start simulation
616      *            button is clicked
617      */
618     CleverRadioButton(final WrappableAnimation animation)
619     {
620         super(animation.shortName());
621         this.animation = animation;
622     }
623 
624     /**
625      * Retrieve the simulation.
626      * @return WrappableAnimation
627      */
628     public final WrappableAnimation getAnimation()
629     {
630         return this.animation;
631     }
632 }