View Javadoc
1   package org.opentrafficsim.trafficcontrol.trafcod;
2   
3   import java.awt.Color;
4   import java.awt.Dimension;
5   import java.awt.Graphics;
6   import java.awt.Graphics2D;
7   import java.awt.event.MouseEvent;
8   import java.awt.event.MouseListener;
9   import java.awt.event.MouseMotionListener;
10  import java.awt.geom.Point2D;
11  import java.awt.image.BufferedImage;
12  import java.rmi.RemoteException;
13  import java.util.HashSet;
14  import java.util.Set;
15  
16  import javax.media.j3d.Bounds;
17  import javax.swing.JPanel;
18  import javax.swing.ToolTipManager;
19  
20  import nl.tudelft.simulation.event.EventInterface;
21  import nl.tudelft.simulation.event.EventListenerInterface;
22  import nl.tudelft.simulation.event.EventType;
23  import nl.tudelft.simulation.language.d3.DirectedPoint;
24  
25  import org.djunits.value.vdouble.scalar.Length;
26  import org.opentrafficsim.core.geometry.OTSLine3D;
27  import org.opentrafficsim.road.network.lane.Lane;
28  import org.opentrafficsim.road.network.lane.object.sensor.NonDirectionalOccupancySensor;
29  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
30  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
31  
32  /**
33   * Display the current state of a TrafCOD machine.
34   * <p>
35   * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
36   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
37   * <p>
38   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Nov 15, 2016 <br>
39   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
40   */
41  public class TrafCODDisplay extends JPanel implements MouseMotionListener, MouseListener
42  {
43      /** */
44      private static final long serialVersionUID = 20161115L;
45  
46      /** Background image. */
47      private final BufferedImage image;
48  
49      /** The set of objects drawn on the image. */
50      private Set<TrafCODObject> trafCODObjects = new HashSet<>();
51  
52      /** Store the tool tip delay so we can restore it when the mouse exits this TrafCODDisplay. */
53      final int defaultInitialDelay = ToolTipManager.sharedInstance().getInitialDelay();
54  
55      /**
56       * Construct a new TrafCODDisplay.
57       * @param image BufferedImage; the background image. This constructor does not make a deep copy of the image. Please do not
58       *            modify the image after calling this constructor
59       */
60      public TrafCODDisplay(final BufferedImage image)
61      {
62          this.image = image;
63          super.setPreferredSize(new Dimension(this.image.getWidth(), this.image.getHeight()));
64          addMouseMotionListener(this);
65      }
66  
67      @Override
68      protected void paintComponent(final Graphics g)
69      {
70          super.paintComponent(g);
71          g.drawImage(this.image, 0, 0, null);
72          for (TrafCODObject tco : this.trafCODObjects)
73          {
74              tco.draw((Graphics2D) g);
75          }
76      }
77  
78      /**
79       * Add one TrafCODObject to this TrafCODDisplay.
80       * @param trafCODObject TrafCODObject; the TrafCOD object that must be added
81       */
82      void addTrafCODObject(final TrafCODObject trafCODObject)
83      {
84          this.trafCODObjects.add(trafCODObject);
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      public void mouseDragged(MouseEvent e)
90      {
91          mouseMoved(e); // Do the same as in the mouse move event
92      }
93  
94      /** {@inheritDoc} */
95      @Override
96      public void mouseMoved(MouseEvent e)
97      {
98          String toolTipText = null;
99          for (TrafCODObject tco : this.trafCODObjects)
100         {
101             toolTipText = tco.toolTipHit(e.getX(), e.getY());
102             if (null != toolTipText)
103             {
104                 break;
105             }
106         }
107         // System.out.println("Setting tool tip text to " + toolTipText);
108         setToolTipText(toolTipText);
109     }
110 
111     /** {@inheritDoc} */
112     @Override
113     public void mouseClicked(MouseEvent e)
114     {
115         // Ignore
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public void mousePressed(MouseEvent e)
121     {
122         // Ignore
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public void mouseReleased(MouseEvent e)
128     {
129         // Ignore
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public void mouseEntered(MouseEvent e)
135     {
136         ToolTipManager.sharedInstance().setInitialDelay(0);
137     }
138 
139     /** {@inheritDoc} */
140     @Override
141     public void mouseExited(MouseEvent e)
142     {
143         ToolTipManager.sharedInstance().setInitialDelay(this.defaultInitialDelay);
144     }
145 
146 }
147 
148 /**
149  * Interface for objects that can draw themselves onto a Graphics2D and may want to show their own tool tip text when the mouse
150  * hits them.
151  */
152 interface TrafCODObject
153 {
154     /**
155      * Draw yourself at the indicated location/
156      * @param g2 Graphics2D; the graphics context
157      */
158     void draw(Graphics2D g2);
159 
160     /**
161      * Check if the given coordinates hit the TrafCODObject. If it does return a String to be used as a tool tip text. If the
162      * coordinates do not hit this TrafCODObject return null.
163      * @param testX int; the x-coordinate
164      * @param testY int; the y-coordinate
165      * @return String; the tool tip text or null if the coordinates do not hit the TrafCodObject
166      */
167     String toolTipHit(int testX, int testY);
168 
169 }
170 
171 /**
172  * Draws a detector.
173  */
174 class DetectorImage implements TrafCODObject, EventListenerInterface
175 {
176     /** The TrafCOD display. */
177     private final TrafCODDisplay display;
178 
179     /** X-coordinate on the TrafCOD display image where this traffic light must be drawn. */
180     private final int x;
181 
182     /** Y-coordinate on the TrafCOD display image where this traffic light must be drawn. */
183     private final int y;
184 
185     /** Tool tip text for this detector image. */
186     private final String description;
187 
188     /** Fill color (used to indicate the occupancy state of the detector). */
189     private Color fillColor = Color.WHITE;
190 
191     /** Size of the box that is drawn. */
192     private static final int BOX_SIZE = 13;
193 
194     /** Correction to make the result match that of the C++Builder version. */
195     private static final int xOffset = 5;
196 
197     /** Correction to make the result match that of the C++Builder version. */
198     private static final int yOffset = 5;
199 
200     /**
201      * Construct a new DetectorImage.
202      * @param display TrafCODDisplay; the TrafCOD display on which this detector image will be rendered
203      * @param center Point2D; the center location of the detector image on the TrafCOD display
204      * @param description String; name of the detector (displayed as tool tip text)
205      */
206     public DetectorImage(final TrafCODDisplay display, Point2D center, String description)
207     {
208         this.display = display;
209         this.x = (int) center.getX();
210         this.y = (int) center.getY();
211         this.description = description;
212         display.addTrafCODObject(this);
213     }
214 
215     /** {@inheritDoc} */
216     @Override
217     public void draw(Graphics2D g2)
218     {
219         g2.setColor(this.fillColor);
220         g2.fillRect(xOffset + this.x - BOX_SIZE / 2, yOffset + this.y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
221         g2.setColor(Color.BLACK);
222         g2.drawRect(xOffset + this.x - BOX_SIZE / 2, yOffset + this.y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public void notify(EventInterface event) throws RemoteException
228     {
229         if (event.getType().equals(NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_ENTRY_EVENT))
230         {
231             this.fillColor = Color.BLUE;
232         }
233         else if (event.getType().equals(NonDirectionalOccupancySensor.NON_DIRECTIONAL_OCCUPANCY_SENSOR_TRIGGER_EXIT_EVENT))
234         {
235             this.fillColor = Color.WHITE;
236         }
237         this.display.repaint();
238     }
239 
240     /** {@inheritDoc} */
241     @Override
242     public String toolTipHit(final int testX, final int testY)
243     {
244         if (testX < xOffset + this.x - BOX_SIZE / 2 || testX >= xOffset + this.x + BOX_SIZE / 2
245                 || testY < yOffset - BOX_SIZE / 2 + this.y || testY >= yOffset + this.y + BOX_SIZE / 2)
246         {
247             return null;
248         }
249         return this.description;
250     }
251 
252 }
253 
254 /**
255  * Draws a traffic light. <br>
256  * The implementation of TrafficLight only implements setTrafficLightColor. All other methods are dummies.
257  */
258 class TrafficLightImage implements TrafficLight, TrafCODObject
259 {
260     /** The TrafCOD display. */
261     private final TrafCODDisplay display;
262 
263     /** X-coordinate on the TrafCOD display image where this traffic light must be drawn. */
264     private final int x;
265 
266     /** Y-coordinate on the TrafCOD display image where this traffic light must be drawn. */
267     private final int y;
268 
269     /** Tool tip text for this traffic light image. */
270     private final String description;
271 
272     /** The current color. */
273     private TrafficLightColor color = TrafficLightColor.BLACK;
274 
275     /**
276      * Create a traffic light image.
277      * @param display TrafCODDisplay; the TrafCOD display on which this traffic light image will be rendered
278      * @param center Point2D; coordinates in the image where this traffic light is centered on
279      * @param description String; tool tip text for the new traffic light image
280      */
281     public TrafficLightImage(final TrafCODDisplay display, final Point2D center, final String description)
282     {
283         this.display = display;
284         this.x = (int) center.getX();
285         this.y = (int) center.getY();
286         this.description = description;
287         display.addTrafCODObject(this);
288     }
289 
290     /** {@inheritDoc} */
291     @Override
292     public String toolTipHit(int testX, int testY)
293     {
294         if (testX < this.x - DISC_SIZE / 2 || testX >= this.x + DISC_SIZE / 2 || testY < this.y - DISC_SIZE / 2
295                 || testY >= this.y + DISC_SIZE / 2)
296         {
297             return null;
298         }
299         return this.description;
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     public DirectedPoint getLocation()
305     {
306         return null;
307     }
308 
309     /** {@inheritDoc} */
310     @Override
311     public Bounds getBounds()
312     {
313         return null;
314     }
315 
316     /** {@inheritDoc} */
317     @Override
318     public Lane getLane()
319     {
320         return null;
321     }
322 
323     /** {@inheritDoc} */
324     @Override
325     public Length getLongitudinalPosition()
326     {
327         return null;
328     }
329 
330     /** {@inheritDoc} */
331     @Override
332     public OTSLine3D getGeometry()
333     {
334         return null;
335     }
336 
337     /** {@inheritDoc} */
338     @Override
339     public Length getHeight()
340     {
341         return null;
342     }
343 
344     /** {@inheritDoc} */
345     @Override
346     public String getId()
347     {
348         return null;
349     }
350 
351     /** {@inheritDoc} */
352     @Override
353     public boolean addListener(EventListenerInterface listener, EventType eventType) throws RemoteException
354     {
355         return false;
356     }
357 
358     /** {@inheritDoc} */
359     @Override
360     public boolean addListener(EventListenerInterface listener, EventType eventType, boolean weak) throws RemoteException
361     {
362         return false;
363     }
364 
365     /** {@inheritDoc} */
366     @Override
367     public boolean addListener(EventListenerInterface listener, EventType eventType, short position) throws RemoteException
368     {
369         return false;
370     }
371 
372     /** {@inheritDoc} */
373     @Override
374     public boolean addListener(EventListenerInterface listener, EventType eventType, short position, boolean weak)
375             throws RemoteException
376     {
377         return false;
378     }
379 
380     /** {@inheritDoc} */
381     @Override
382     public boolean removeListener(EventListenerInterface listener, EventType eventType) throws RemoteException
383     {
384         return false;
385     }
386 
387     /** {@inheritDoc} */
388     @Override
389     public TrafficLightColor getTrafficLightColor()
390     {
391         return null;
392     }
393 
394     /** {@inheritDoc} */
395     @Override
396     public void setTrafficLightColor(TrafficLightColor trafficLightColor)
397     {
398         this.color = trafficLightColor;
399         this.display.repaint();
400     }
401 
402     /** Diameter of a traffic light disk in pixels. */
403     private static final int DISC_SIZE = 11;
404 
405     /** {@inheritDoc} */
406     @Override
407     public void draw(Graphics2D g2)
408     {
409         Color lightColor;
410         switch (this.color)
411         {
412             case BLACK:
413                 lightColor = Color.BLACK;
414                 break;
415 
416             case GREEN:
417                 lightColor = Color.green;
418                 break;
419 
420             case YELLOW:
421                 lightColor = Color.YELLOW;
422                 break;
423 
424             case RED:
425                 lightColor = Color.RED;
426                 break;
427 
428             default:
429                 System.err.println("Unhandled TrafficLightColor: " + this.color);
430                 return;
431         }
432         g2.setColor(lightColor);
433         g2.fillOval(this.x - DISC_SIZE / 2, this.y - DISC_SIZE / 2, DISC_SIZE, DISC_SIZE);
434         // System.out.println("Drawn disk in color " + lightColor);
435     }
436 
437 }