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