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