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