View Javadoc
1   package org.opentrafficsim.simulationengine;
2   
3   import java.awt.Container;
4   import java.awt.Dimension;
5   import java.awt.Frame;
6   import java.awt.Rectangle;
7   import java.awt.geom.Rectangle2D;
8   import java.io.Serializable;
9   import java.rmi.RemoteException;
10  import java.util.ArrayList;
11  import java.util.List;
12  
13  import javax.naming.NamingException;
14  import javax.swing.WindowConstants;
15  import javax.vecmath.Point3d;
16  
17  import org.djunits.value.vdouble.scalar.Duration;
18  import org.djunits.value.vdouble.scalar.Time;
19  import org.opentrafficsim.base.modelproperties.Property;
20  import org.opentrafficsim.base.modelproperties.PropertyException;
21  import org.opentrafficsim.core.dsol.OTSModelInterface;
22  import org.opentrafficsim.core.gtu.animation.DefaultSwitchableGTUColorer;
23  import org.opentrafficsim.core.gtu.animation.GTUColorer;
24  import org.opentrafficsim.core.network.Link;
25  import org.opentrafficsim.core.network.NetworkException;
26  import org.opentrafficsim.gui.OTSAnimationPanel;
27  import org.opentrafficsim.gui.SimulatorFrame;
28  
29  import nl.tudelft.simulation.dsol.SimRuntimeException;
30  import nl.tudelft.simulation.dsol.animation.Locatable;
31  import nl.tudelft.simulation.language.d3.BoundingBox;
32  import nl.tudelft.simulation.language.d3.DirectedPoint;
33  
34  /**
35   * <p>
36   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
37   * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
38   * <p>
39   * $LastChangedDate: 2017-01-16 01:48:07 +0100 (Mon, 16 Jan 2017) $, @version $Revision: 3281 $, by $Author: averbraeck $,
40   * initial version Jun 18, 2015 <br>
41   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
42   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
43   */
44  public abstract class AbstractWrappableAnimation implements WrappableAnimation, Serializable
45  {
46      /** */
47      private static final long serialVersionUID = 20150000L;
48  
49      /** The properties exhibited by this simulation. */
50      @SuppressWarnings("checkstyle:visibilitymodifier")
51      protected List<Property<?>> properties = new ArrayList<>();
52  
53      /** The properties after (possible) editing by the user. */
54      @SuppressWarnings("checkstyle:visibilitymodifier")
55      protected List<Property<?>> savedUserModifiedProperties;
56  
57      /** Use EXIT_ON_CLOSE when true, DISPOSE_ON_CLOSE when false on closing of the window. */
58      @SuppressWarnings("checkstyle:visibilitymodifier")
59      protected boolean exitOnClose;
60  
61      /** The tabbed panel so other tabs can be added by the classes that extend this class. */
62      @SuppressWarnings("checkstyle:visibilitymodifier")
63      protected OTSAnimationPanel panel;
64  
65      /** Save the startTime for restarting the simulation. */
66      private Time savedStartTime;
67  
68      /** Save the startTime for restarting the simulation. */
69      private Duration savedWarmupPeriod;
70  
71      /** Save the runLength for restarting the simulation. */
72      private Duration savedRunLength;
73  
74      /** the model. */
75      private OTSModelInterface model;
76  
77      /**
78       * Build the animator.
79       * @param startTime Time; the start time
80       * @param warmupPeriod Duration; the warm up period
81       * @param runLength Duration; the duration of the simulation / animation
82       * @param otsModel OTSModelInterface; the simulation model
83       * @return SimpleAnimator; a newly constructed animator
84       * @throws SimRuntimeException on ???
85       * @throws NamingException when context for the animation cannot be created
86       * @throws PropertyException when one of the user modified properties has the empty string as key
87       */
88      @SuppressWarnings("checkstyle:designforextension")
89      protected SimpleAnimator buildSimpleAnimator(final Time startTime, final Duration warmupPeriod, final Duration runLength,
90              final OTSModelInterface otsModel) throws SimRuntimeException, NamingException, PropertyException
91      {
92          return new SimpleAnimator(startTime, warmupPeriod, runLength, otsModel);
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      @SuppressWarnings("checkstyle:designforextension")
98      public SimpleAnimator buildAnimator(final Time startTime, final Duration warmupPeriod, final Duration runLength,
99              final List<Property<?>> userModifiedProperties, final Rectangle rect, final boolean eoc)
100             throws SimRuntimeException, NamingException, OTSSimulationException, PropertyException
101     {
102         this.savedUserModifiedProperties = userModifiedProperties;
103         this.exitOnClose = eoc;
104 
105         this.savedStartTime = startTime;
106         this.savedWarmupPeriod = warmupPeriod;
107         this.savedRunLength = runLength;
108 
109         GTUColorer colorer = new DefaultSwitchableGTUColorer();
110         this.model = makeModel(colorer);
111 
112         if (null == this.model)
113         {
114             return null; // Happens when the user cancels the file open dialog in the OpenStreetMap demo.
115         }
116 
117         final SimpleAnimator simulator = buildSimpleAnimator(startTime, warmupPeriod, runLength, this.model);
118         try
119         {
120             this.panel = new OTSAnimationPanel(makeAnimationRectangle(), new Dimension(1024, 768), simulator, this, colorer);
121         }
122         catch (RemoteException exception)
123         {
124             throw new SimRuntimeException(exception);
125         }
126 
127         addAnimationToggles();
128         addTabs(simulator);
129 
130         SimulatorFrame frame = new SimulatorFrame(shortName(), this.panel);
131         if (rect != null)
132         {
133             frame.setBounds(rect);
134         }
135         else
136         {
137             frame.setExtendedState(Frame.MAXIMIZED_BOTH);
138         }
139 
140         frame.setDefaultCloseOperation(this.exitOnClose ? WindowConstants.EXIT_ON_CLOSE : WindowConstants.DISPOSE_ON_CLOSE);
141 
142         return simulator;
143     }
144 
145     /**
146      * Make additional tabs in the main simulation window.
147      * @param simulator SimpleSimulatorInterface; the simulator
148      * @throws OTSSimulationException in case the chart, axes or legend cannot be generated
149      * @throws PropertyException when one of the user modified properties has the empty string as key
150      */
151     protected void addTabs(final SimpleSimulatorInterface simulator) throws OTSSimulationException, PropertyException
152     {
153         // Override this method to add custom tabs
154     }
155 
156     /**
157      * Placeholder method to place animation buttons or to show/hide classes on the animation.
158      */
159     @SuppressWarnings("checkstyle:designforextension")
160     protected void addAnimationToggles()
161     {
162         // overridable placeholder to place animation buttons or to show/hide classes on the animation.
163     }
164 
165     /**
166      * Add a button for toggling an animatable class on or off. Button icons for which 'idButton' is true will be placed to the
167      * right of the previous button, which should be the corresponding button without the id. An example is an icon for
168      * showing/hiding the class 'Lane' followed by the button to show/hide the Lane ids.
169      * @param name the name of the button
170      * @param locatableClass the class for which the button holds (e.g., GTU.class)
171      * @param iconPath the path to the 24x24 icon to display
172      * @param toolTipText the tool tip text to show when hovering over the button
173      * @param initiallyVisible whether the class is initially shown or not
174      * @param idButton id button that needs to be placed next to the previous button
175      */
176     public final void addToggleAnimationButtonIcon(final String name, final Class<? extends Locatable> locatableClass,
177             final String iconPath, final String toolTipText, final boolean initiallyVisible, final boolean idButton)
178     {
179         this.panel.addToggleAnimationButtonIcon(name, locatableClass, iconPath, toolTipText, initiallyVisible, idButton);
180     }
181 
182     /**
183      * Add a button for toggling an animatable class on or off.
184      * @param name the name of the button
185      * @param locatableClass the class for which the button holds (e.g., GTU.class)
186      * @param toolTipText the tool tip text to show when hovering over the button
187      * @param initiallyVisible whether the class is initially shown or not
188      */
189     public final void addToggleAnimationButtonText(final String name, final Class<? extends Locatable> locatableClass,
190             final String toolTipText, final boolean initiallyVisible)
191     {
192         this.panel.addToggleAnimationButtonText(name, locatableClass, toolTipText, initiallyVisible);
193     }
194 
195     /**
196      * Set a class to be shown in the animation to true.
197      * @param locatableClass the class for which the animation has to be shown.
198      */
199     public final void showAnimationClass(final Class<? extends Locatable> locatableClass)
200     {
201         this.panel.getAnimationPanel().showClass(locatableClass);
202     }
203 
204     /**
205      * Set a class to be hidden in the animation to true.
206      * @param locatableClass the class for which the animation has to be hidden.
207      */
208     public final void hideAnimationClass(final Class<? extends Locatable> locatableClass)
209     {
210         this.panel.getAnimationPanel().hideClass(locatableClass);
211     }
212 
213     /**
214      * Toggle a class to be displayed in the animation to its reverse value.
215      * @param locatableClass the class for which a visible animation has to be turned off or vice versa.
216      */
217     public final void toggleAnimationClass(final Class<? extends Locatable> locatableClass)
218     {
219         this.panel.getAnimationPanel().toggleClass(locatableClass);
220     }
221 
222     /**
223      * @param colorer the GTU colorer to use.
224      * @return the demo model. Don't forget to keep a local copy.
225      * @throws OTSSimulationException in case the construction of the model fails
226      */
227     protected abstract OTSModelInterface makeModel(GTUColorer colorer) throws OTSSimulationException;
228 
229     /**
230      * Return the initial 'home' extent for the animation. The 'Home' button returns to this extent. Override this method when a
231      * smaller or larger part of the infra should be shown. In the default setting, all currently visible objects are shown.
232      * @return the initial and 'home' rectangle for the animation.
233      */
234     @SuppressWarnings("checkstyle:designforextension")
235     protected Rectangle2D makeAnimationRectangle()
236     {
237         double minX = Double.MAX_VALUE;
238         double maxX = -Double.MAX_VALUE;
239         double minY = Double.MAX_VALUE;
240         double maxY = -Double.MAX_VALUE;
241         Point3d p3dL = new Point3d();
242         Point3d p3dU = new Point3d();
243         try
244         {
245             for (Link link : this.model.getNetwork().getLinkMap().values())
246             {
247                 DirectedPoint l = link.getLocation();
248                 BoundingBox b = new BoundingBox(link.getBounds());
249                 b.getLower(p3dL);
250                 b.getUpper(p3dU);
251                 minX = Math.min(minX, l.x + Math.min(p3dL.x, p3dU.x));
252                 minY = Math.min(minY, l.y + Math.min(p3dL.y, p3dU.y));
253                 maxX = Math.max(maxX, l.x + Math.max(p3dL.x, p3dU.x));
254                 maxY = Math.max(maxY, l.y + Math.max(p3dL.y, p3dU.y));
255             }
256         }
257         catch (Exception e)
258         {
259             // ignore
260         }
261 
262         minX = minX - 0.05 * Math.abs(minX);
263         minY = minY - 0.05 * Math.abs(minY);
264         maxX = maxX + 0.05 * Math.abs(maxX);
265         maxY = maxY + 0.05 * Math.abs(maxY);
266 
267         return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
268     }
269 
270     /** {@inheritDoc} */
271     @Override
272     public final ArrayList<Property<?>> getProperties()
273     {
274         return new ArrayList<Property<?>>(this.properties);
275     }
276 
277     /** {@inheritDoc} */
278     @Override
279     public final SimpleSimulatorInterface rebuildSimulator(final Rectangle rect)
280             throws SimRuntimeException, NetworkException, NamingException, OTSSimulationException, PropertyException
281     {
282         return buildAnimator(this.savedStartTime, this.savedWarmupPeriod, this.savedRunLength, this.savedUserModifiedProperties,
283                 rect, this.exitOnClose);
284     }
285 
286     /** {@inheritDoc} */
287     @Override
288     public final List<Property<?>> getUserModifiedProperties()
289     {
290         return this.savedUserModifiedProperties;
291     }
292 
293     /** {@inheritDoc} */
294     @Override
295     @SuppressWarnings("checkstyle:designforextension")
296     public void stopTimersThreads()
297     {
298         if (this.panel != null && this.panel.getStatusBar() != null)
299         {
300             this.panel.getStatusBar().cancelTimer();
301         }
302         this.panel = null;
303     }
304 
305     /**
306      * @return panel
307      */
308     public final OTSAnimationPanel getPanel()
309     {
310         return this.panel;
311     }
312 
313     /**
314      * Add a tab to the simulation window. This method can not be called from constructModel because the TabbedPane has not yet
315      * been constructed at that time; recommended: override addTabs and call this method from there.
316      * @param index int; index of the new tab; use <code>getTabCount()</code> to obtain the valid range
317      * @param caption String; caption of the new tab
318      * @param container Container; content of the new tab
319      */
320     public final void addTab(final int index, final String caption, final Container container)
321     {
322         this.panel.getTabbedPane().addTab(index, caption, container);
323     }
324 
325     /**
326      * Report the current number of tabs in the simulation window. This method can not be called from constructModel because the
327      * TabbedPane has not yet been constructed at that time; recommended: override addTabs and call this method from there.
328      * @return int; the number of tabs in the simulation window
329      */
330     public final int getTabCount()
331     {
332         return this.panel.getTabbedPane().getTabCount();
333     }
334 
335 }