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