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.util.LinkedHashSet;
13 import java.util.Optional;
14 import java.util.Set;
15
16 import javax.swing.JPanel;
17 import javax.swing.ToolTipManager;
18
19 import org.djutils.event.Event;
20 import org.djutils.event.EventListener;
21 import org.djutils.event.LocalEventProducer;
22 import org.opentrafficsim.base.logger.Logger;
23 import org.opentrafficsim.road.network.lane.object.detector.TrafficLightDetector;
24 import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
25
26
27
28
29
30
31
32
33
34 public class TrafCodDisplay extends JPanel implements MouseMotionListener, MouseListener
35 {
36
37 private static final long serialVersionUID = 20161115L;
38
39
40 private final BufferedImage image;
41
42
43 private Set<TrafCODObject> trafCODObjects = new LinkedHashSet<>();
44
45
46 private final int defaultInitialDelay = ToolTipManager.sharedInstance().getInitialDelay();
47
48
49
50
51
52
53
54 public TrafCodDisplay(final BufferedImage image)
55 {
56 this.image = image;
57 super.setPreferredSize(new Dimension(this.image.getWidth(), this.image.getHeight()));
58 addMouseMotionListener(this);
59 }
60
61
62
63
64
65
66 public Optional<DetectorImage> getDetectorImage(final String id)
67 {
68 for (TrafCODObject tco : this.trafCODObjects)
69 {
70 if (tco instanceof DetectorImage && ((DetectorImage) tco).getId().equals(id))
71 {
72 return Optional.of((DetectorImage) tco);
73 }
74 }
75 return Optional.empty();
76 }
77
78 @Override
79 protected void paintComponent(final Graphics g)
80 {
81 super.paintComponent(g);
82 g.drawImage(this.image, 0, 0, null);
83 for (TrafCODObject tco : this.trafCODObjects)
84 {
85 tco.draw((Graphics2D) g);
86 }
87 }
88
89
90
91
92
93 void addTrafCODObject(final TrafCODObject trafCODObject)
94 {
95 this.trafCODObjects.add(trafCODObject);
96 }
97
98 @Override
99 public void mouseDragged(final MouseEvent e)
100 {
101 mouseMoved(e);
102 }
103
104 @Override
105 public void mouseMoved(final MouseEvent e)
106 {
107 String toolTipText = null;
108 for (TrafCODObject tco : this.trafCODObjects)
109 {
110 toolTipText = tco.toolTipHit(e.getX(), e.getY()).orElse(null);
111 if (null != toolTipText)
112 {
113 break;
114 }
115 }
116 Logger.ots().trace("Setting tool tip text to " + toolTipText);
117 setToolTipText(toolTipText);
118 }
119
120 @Override
121 public void mouseClicked(final MouseEvent e)
122 {
123
124 }
125
126 @Override
127 public void mousePressed(final MouseEvent e)
128 {
129
130 }
131
132 @Override
133 public void mouseReleased(final MouseEvent e)
134 {
135
136 }
137
138 @Override
139 public void mouseEntered(final MouseEvent e)
140 {
141 ToolTipManager.sharedInstance().setInitialDelay(0);
142 }
143
144 @Override
145 public void mouseExited(final MouseEvent e)
146 {
147 ToolTipManager.sharedInstance().setInitialDelay(this.defaultInitialDelay);
148 }
149
150 }
151
152
153
154
155
156 interface TrafCODObject
157 {
158
159
160
161
162 void draw(Graphics2D g2);
163
164
165
166
167
168
169
170
171 Optional<String> toolTipHit(int testX, int testY);
172
173 }
174
175
176
177
178 class DetectorImage implements TrafCODObject, EventListener
179 {
180
181 private final TrafCodDisplay display;
182
183
184 private final int x;
185
186
187 private final int y;
188
189
190 private final String description;
191
192
193 private final String id;
194
195
196 private Color fillColor = Color.WHITE;
197
198
199 private static final int BOX_SIZE = 13;
200
201
202 private static final int X_OFFSET = 5;
203
204
205 private static final int Y_OFFSET = 5;
206
207
208
209
210
211
212
213
214 DetectorImage(final TrafCodDisplay display, final Point2D center, final String id, final String description)
215 {
216 this.display = display;
217 this.x = (int) center.getX();
218 this.y = (int) center.getY();
219 this.id = id;
220 this.description = description;
221 display.addTrafCODObject(this);
222 }
223
224 @Override
225 public void draw(final Graphics2D g2)
226 {
227 g2.setColor(this.fillColor);
228 g2.fillRect(X_OFFSET + this.x - BOX_SIZE / 2, Y_OFFSET + this.y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
229 g2.setColor(Color.BLACK);
230 g2.drawRect(X_OFFSET + this.x - BOX_SIZE / 2, Y_OFFSET + this.y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
231 }
232
233 @Override
234 public void notify(final Event event)
235 {
236 if (event.getType().equals(TrafficLightDetector.TRAFFIC_LIGHT_DETECTOR_TRIGGER_ENTRY_EVENT))
237 {
238 this.fillColor = Color.BLUE;
239 }
240 else if (event.getType().equals(TrafficLightDetector.TRAFFIC_LIGHT_DETECTOR_TRIGGER_EXIT_EVENT))
241 {
242 this.fillColor = Color.WHITE;
243 }
244 this.display.repaint();
245 }
246
247 @Override
248 public Optional<String> toolTipHit(final int testX, final int testY)
249 {
250 if (testX < X_OFFSET + this.x - BOX_SIZE / 2 || testX >= X_OFFSET + this.x + BOX_SIZE / 2
251 || testY < Y_OFFSET - BOX_SIZE / 2 + this.y || testY >= Y_OFFSET + this.y + BOX_SIZE / 2)
252 {
253 return Optional.empty();
254 }
255 return Optional.ofNullable(this.description);
256 }
257
258
259
260
261
262 public String getId()
263 {
264 return this.id;
265 }
266
267 }
268
269
270
271
272 class TrafficLightImage extends LocalEventProducer implements TrafCODObject
273 {
274
275 private final TrafCodDisplay display;
276
277
278 private final int x;
279
280
281 private final int y;
282
283
284 private final String description;
285
286
287 private TrafficLightColor color = TrafficLightColor.BLACK;
288
289
290
291
292
293
294
295 TrafficLightImage(final TrafCodDisplay display, final Point2D center, final String description)
296 {
297 this.display = display;
298 this.x = (int) center.getX();
299 this.y = (int) center.getY();
300 this.description = description;
301 display.addTrafCODObject(this);
302 }
303
304 @Override
305 public Optional<String> toolTipHit(final int testX, final int testY)
306 {
307 if (testX < this.x - DISC_SIZE / 2 || testX >= this.x + DISC_SIZE / 2 || testY < this.y - DISC_SIZE / 2
308 || testY >= this.y + DISC_SIZE / 2)
309 {
310 return Optional.empty();
311 }
312 return Optional.ofNullable(this.description);
313 }
314
315
316
317
318
319 public void setTrafficLightColor(final TrafficLightColor trafficLightColor)
320 {
321 this.color = trafficLightColor;
322 this.display.repaint();
323 }
324
325
326 private static final int DISC_SIZE = 11;
327
328 @Override
329 public void draw(final Graphics2D g2)
330 {
331 Color lightColor;
332 switch (this.color)
333 {
334 case BLACK:
335 lightColor = Color.BLACK;
336 break;
337
338 case GREEN:
339 lightColor = Color.green;
340 break;
341
342 case YELLOW:
343 lightColor = Color.YELLOW;
344 break;
345
346 case RED:
347 lightColor = Color.RED;
348 break;
349
350 default:
351 Logger.ots().error("Unhandled TrafficLightColor: {}", this.color);
352 return;
353 }
354 g2.setColor(lightColor);
355 g2.fillOval(this.x - DISC_SIZE / 2, this.y - DISC_SIZE / 2, DISC_SIZE, DISC_SIZE);
356 Logger.ots().trace("Drawn disk in color {}", lightColor);
357 }
358
359 }