1 package org.opentrafficsim.draw.core;
2
3 import java.awt.Color;
4 import java.awt.Font;
5 import java.awt.FontMetrics;
6 import java.awt.Graphics2D;
7 import java.awt.geom.Rectangle2D;
8 import java.awt.image.ImageObserver;
9 import java.io.Serializable;
10 import java.rmi.RemoteException;
11
12 import javax.naming.NamingException;
13
14 import org.djutils.draw.Oriented;
15 import org.djutils.draw.bounds.Bounds2d;
16 import org.djutils.draw.point.Point;
17 import org.djutils.draw.point.Point2d;
18 import org.djutils.logger.CategoryLogger;
19 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
20 import org.opentrafficsim.core.geometry.Bounds;
21 import org.opentrafficsim.core.geometry.DirectedPoint;
22
23 import nl.tudelft.simulation.dsol.animation.Locatable;
24 import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
25 import nl.tudelft.simulation.naming.context.Contextualized;
26
27
28
29
30
31
32
33
34
35
36
37 public abstract class TextAnimation implements Locatable, Serializable
38 {
39
40 private static final long serialVersionUID = 20161211L;
41
42
43 private final Locatable source;
44
45
46 private String text;
47
48
49 private float dx;
50
51
52 private float dy;
53
54
55 private final TextAlignment textAlignment;
56
57
58 private Color color;
59
60
61 private final float fontSize;
62
63
64 private final float minFontSize;
65
66
67 private final float maxFontSize;
68
69
70 private final AnimationImpl animationImpl;
71
72
73 private Font font;
74
75
76 private final ContrastToBackground background;
77
78
79 private Rectangle2D fontRectangle = null;
80
81
82 private final ScaleDependentRendering scaleDependentRendering;
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 @SuppressWarnings("checkstyle:parameternumber")
103 public TextAnimation(final Locatable source, final String text, final float dx, final float dy,
104 final TextAlignment textAlignment, final Color color, final float fontSize, final float minFontSize,
105 final float maxFontSize, final OtsSimulatorInterface simulator, final ContrastToBackground background,
106 final ScaleDependentRendering scaleDependentRendering) throws RemoteException, NamingException
107 {
108 this.source = source;
109 this.text = text;
110 this.dx = dx;
111 this.dy = dy;
112 this.textAlignment = textAlignment;
113 this.color = color;
114 this.fontSize = fontSize;
115 this.minFontSize = minFontSize;
116 this.maxFontSize = maxFontSize;
117 this.background = background;
118 this.scaleDependentRendering = scaleDependentRendering;
119
120 this.font = new Font("SansSerif", Font.PLAIN, 2);
121 if (this.fontSize != 2.0f)
122 {
123 this.font = this.font.deriveFont(this.fontSize);
124 }
125
126 this.animationImpl = new AnimationImpl(this, simulator);
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 @SuppressWarnings("checkstyle:parameternumber")
146 public TextAnimation(final Locatable source, final String text, final float dx, final float dy,
147 final TextAlignment textAlignment, final Color color, final float fontSize, final float minFontSize,
148 final float maxFontSize, final OtsSimulatorInterface simulator,
149 final ScaleDependentRendering scaleDependentRendering) throws RemoteException, NamingException
150 {
151 this(source, text, dx, dy, textAlignment, color, fontSize, minFontSize, maxFontSize, simulator, null,
152 scaleDependentRendering);
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166
167 public TextAnimation(final Locatable source, final String text, final float dx, final float dy,
168 final TextAlignment textAlignment, final Color color, final OtsSimulatorInterface simulator,
169 final ScaleDependentRendering scaleDependentRendering) throws RemoteException, NamingException
170 {
171 this(source, text, dx, dy, textAlignment, color, 2.0f, 12.0f, 50f, simulator, scaleDependentRendering);
172 }
173
174
175 @Override
176 public DirectedPoint getLocation()
177 {
178
179 try
180 {
181 Point<?> p = this.source.getLocation();
182 return new DirectedPoint(p.getX(), p.getY(), Double.MAX_VALUE, 0.0, 0.0,
183 p instanceof Oriented ? ((Oriented<?>) p).getDirZ() : 0.0);
184 }
185 catch (RemoteException exception)
186 {
187 CategoryLogger.always().warn(exception);
188 return new DirectedPoint(0, 0, 0);
189 }
190 }
191
192
193 @Override
194 public final Bounds getBounds() throws RemoteException
195 {
196 return new Bounds(0.0, 0.0, 0.0);
197 }
198
199
200
201
202
203
204 @SuppressWarnings("checkstyle:designforextension")
205 public void paint(final Graphics2D graphics, final ImageObserver observer)
206 {
207 double scale = Math.sqrt(graphics.getTransform().getDeterminant());
208 Rectangle2D scaledFontRectangle;
209 synchronized (this.font)
210 {
211 if (!this.scaleDependentRendering.isRendered(scale))
212 {
213 return;
214 }
215 if (scale < this.minFontSize / this.fontSize)
216 {
217 graphics.setFont(this.font.deriveFont((float) (this.minFontSize / scale)));
218 FontMetrics fm = graphics.getFontMetrics();
219 scaledFontRectangle = fm.getStringBounds(this.text, graphics);
220 }
221 else if (scale > this.maxFontSize / this.fontSize)
222 {
223 graphics.setFont(this.font.deriveFont((float) (this.maxFontSize / scale)));
224 FontMetrics fm = graphics.getFontMetrics();
225 scaledFontRectangle = fm.getStringBounds(this.text, graphics);
226 }
227 else
228 {
229 graphics.setFont(this.font);
230 if (this.fontRectangle == null)
231 {
232 FontMetrics fm = graphics.getFontMetrics();
233 this.fontRectangle = fm.getStringBounds(this.text, graphics);
234 }
235 scaledFontRectangle = this.fontRectangle;
236 }
237 Color useColor = this.color;
238 if (null != this.background && useColor.equals(this.background.getBackgroundColor()))
239 {
240
241 if (Color.BLACK.equals(useColor))
242 {
243 useColor = Color.WHITE;
244 }
245 else
246 {
247 useColor = Color.BLACK;
248 }
249 }
250 graphics.setColor(useColor);
251 float dxText =
252 this.textAlignment.equals(TextAlignment.LEFT) ? 0.0f : this.textAlignment.equals(TextAlignment.CENTER)
253 ? (float) -scaledFontRectangle.getWidth() / 2.0f : (float) -scaledFontRectangle.getWidth();
254 graphics.drawString(this.text, dxText + this.dx, -this.dy);
255 }
256 }
257
258
259
260
261
262 public final void destroy(final Contextualized contextProvider)
263 {
264 this.animationImpl.destroy(contextProvider);
265 }
266
267
268
269
270
271 protected final Locatable getSource()
272 {
273 return this.source;
274 }
275
276
277
278
279
280 protected final float getDx()
281 {
282 return this.dx;
283 }
284
285
286
287
288
289 protected final float getDy()
290 {
291 return this.dy;
292 }
293
294
295
296
297
298
299 protected final void setXY(final float x, final float y)
300 {
301 this.dx = x;
302 this.dy = y;
303 }
304
305
306
307
308
309 protected final TextAlignment getTextAlignment()
310 {
311 return this.textAlignment;
312 }
313
314
315
316
317
318 protected final float getFontSize()
319 {
320 return this.fontSize;
321 }
322
323
324
325
326
327 protected final Font getFont()
328 {
329 return this.font;
330 }
331
332
333
334
335
336 protected final String getText()
337 {
338 return this.text;
339 }
340
341
342
343
344
345 protected final void setText(final String text)
346 {
347 this.text = text;
348 synchronized (this.font)
349 {
350 this.fontRectangle = null;
351 }
352 }
353
354
355
356
357
358 protected final Color getColor()
359 {
360 return this.color;
361 }
362
363
364
365
366
367 protected final void setColor(final Color color)
368 {
369 this.color = color;
370 }
371
372
373
374
375
376 public final boolean isFlip()
377 {
378 return this.animationImpl.isFlip();
379 }
380
381
382
383
384
385 public final void setFlip(final boolean flip)
386 {
387 this.animationImpl.setFlip(flip);
388 }
389
390
391
392
393
394 public final boolean isRotate()
395 {
396 return this.animationImpl.isRotate();
397 }
398
399
400
401
402
403 public final void setRotate(final boolean rotate)
404 {
405 this.animationImpl.setRotate(rotate);
406
407 }
408
409
410
411
412
413 public final boolean isScale()
414 {
415 return this.animationImpl.isScale();
416 }
417
418
419
420
421
422 public final void setScale(final boolean scale)
423 {
424 this.animationImpl.setScale(scale);
425 }
426
427
428
429
430
431 public final boolean isTranslate()
432 {
433 return this.animationImpl.isTranslate();
434 }
435
436
437
438
439
440 public final void setTranslate(final boolean translate)
441 {
442 this.animationImpl.setTranslate(translate);
443 }
444
445
446
447
448
449
450
451
452
453
454
455
456 private static class AnimationImpl extends Renderable2D<Locatable> implements Serializable
457 {
458
459 private static final long serialVersionUID = 20170400L;
460
461
462
463
464
465
466
467
468 AnimationImpl(final Locatable source, final OtsSimulatorInterface simulator) throws NamingException, RemoteException
469 {
470 super(source, simulator);
471 }
472
473
474 @Override
475 public final void paint(final Graphics2D graphics, final ImageObserver observer)
476 {
477 TextAnimation ta = ((TextAnimation) getSource());
478 ta.paint(graphics, observer);
479 }
480
481
482 @Override
483 public boolean contains(final Point2d pointWorldCoordinates, final Bounds2d extent)
484 {
485 return false;
486 }
487
488
489 @Override
490 public final String toString()
491 {
492 return "TextAnimation.AnimationImpl []";
493 }
494
495 }
496
497
498
499
500
501 protected ScaleDependentRendering getScaleDependentRendering()
502 {
503 return this.scaleDependentRendering;
504 }
505
506
507
508
509 public interface ContrastToBackground
510 {
511
512
513
514
515 Color getBackgroundColor();
516 }
517
518
519
520
521 public interface ScaleDependentRendering
522 {
523
524
525
526
527
528
529 boolean isRendered(double scale);
530 }
531
532
533 public static final ScaleDependentRendering RENDERALWAYS = new ScaleDependentRendering()
534 {
535
536 @Override
537 public boolean isRendered(final double scale)
538 {
539 return true;
540 }
541 };
542
543
544 public static final ScaleDependentRendering RENDERWHEN1 = new ScaleDependentRendering()
545 {
546
547 @Override
548 public boolean isRendered(final double scale)
549 {
550 return scale >= 1.0;
551 }
552 };
553
554
555 public static final ScaleDependentRendering RENDERWHEN10 = new ScaleDependentRendering()
556 {
557
558 @Override
559 public boolean isRendered(final double scale)
560 {
561 return scale >= 0.1;
562 }
563 };
564
565
566 public static final ScaleDependentRendering RENDERWHEN100 = new ScaleDependentRendering()
567 {
568
569 @Override
570 public boolean isRendered(final double scale)
571 {
572 return scale >= 0.01;
573 }
574 };
575
576 }