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