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