1 package org.opentrafficsim.draw.core;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Font;
6 import java.awt.FontMetrics;
7 import java.awt.Graphics2D;
8 import java.awt.geom.Point2D;
9 import java.awt.geom.Rectangle2D;
10 import java.awt.image.ImageObserver;
11 import java.io.Serializable;
12 import java.rmi.RemoteException;
13
14 import javax.media.j3d.Bounds;
15 import javax.naming.NamingException;
16
17 import org.djutils.logger.CategoryLogger;
18
19 import nl.tudelft.simulation.dsol.animation.Locatable;
20 import nl.tudelft.simulation.dsol.animation.D2.Renderable2D;
21 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
22 import nl.tudelft.simulation.language.d3.BoundingBox;
23 import nl.tudelft.simulation.language.d3.DirectedPoint;
24
25
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 SimulatorInterface.TimeDoubleUnit 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 SimulatorInterface.TimeDoubleUnit 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 SimulatorInterface.TimeDoubleUnit 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 @SuppressWarnings("checkstyle:designforextension")
177 public DirectedPoint getLocation() throws RemoteException
178 {
179
180 DirectedPoint p = this.source.getLocation();
181 return new DirectedPoint(p.x, p.y, Double.MAX_VALUE, 0.0, 0.0, p.getRotZ());
182 }
183
184
185 @Override
186 public final Bounds getBounds() throws RemoteException
187 {
188 return new BoundingBox(0.0, 0.0, 0.0);
189 }
190
191
192
193
194
195
196
197 @SuppressWarnings("checkstyle:designforextension")
198 public void paint(final Graphics2D graphics, final ImageObserver observer) throws RemoteException
199 {
200 double scale = Math.sqrt(graphics.getTransform().getDeterminant());
201 Rectangle2D scaledFontRectangle;
202 synchronized (this.font)
203 {
204 if (!this.scaleDependentRendering.isRendered(scale))
205 {
206 return;
207 }
208 if (scale < this.minFontSize / this.fontSize)
209 {
210 graphics.setFont(this.font.deriveFont((float) (this.minFontSize / scale)));
211 FontMetrics fm = graphics.getFontMetrics();
212 scaledFontRectangle = fm.getStringBounds(this.text, graphics);
213 }
214 else if (scale > this.maxFontSize / this.fontSize)
215 {
216 graphics.setFont(this.font.deriveFont((float) (this.maxFontSize / scale)));
217 FontMetrics fm = graphics.getFontMetrics();
218 scaledFontRectangle = fm.getStringBounds(this.text, graphics);
219 }
220 else
221 {
222 graphics.setFont(this.font);
223 if (this.fontRectangle == null)
224 {
225 FontMetrics fm = graphics.getFontMetrics();
226 this.fontRectangle = fm.getStringBounds(this.text, graphics);
227 }
228 scaledFontRectangle = this.fontRectangle;
229 }
230 Color useColor = this.color;
231 if (null != this.background && useColor.equals(this.background.getBackgroundColor()))
232 {
233
234 if (Color.BLACK.equals(useColor))
235 {
236 useColor = Color.WHITE;
237 }
238 else
239 {
240 useColor = Color.BLACK;
241 }
242 }
243 graphics.setColor(useColor);
244 float dxText =
245 this.textAlignment.equals(TextAlignment.LEFT) ? 0.0f : this.textAlignment.equals(TextAlignment.CENTER)
246 ? (float) -scaledFontRectangle.getWidth() / 2.0f : (float) -scaledFontRectangle.getWidth();
247 graphics.drawString(this.text, dxText + this.dx, this.fontSize / 2.0f - this.dy);
248 }
249 }
250
251
252
253
254 public final void destroy()
255 {
256 try
257 {
258 this.animationImpl.destroy();
259 }
260 catch (NamingException | RemoteException exception)
261 {
262 CategoryLogger.always().warn(exception, "Tried to destroy Text for GTU animation of GTU {}",
263 this.source.toString());
264 }
265 }
266
267
268
269
270
271
272
273
274
275 public abstract TextAnimation clone(Locatable newSource, SimulatorInterface.TimeDoubleUnit newSimulator)
276 throws RemoteException, NamingException;
277
278
279
280
281
282 protected final Locatable getSource()
283 {
284 return this.source;
285 }
286
287
288
289
290
291 protected final float getDx()
292 {
293 return this.dx;
294 }
295
296
297
298
299
300 protected final float getDy()
301 {
302 return this.dy;
303 }
304
305
306
307
308
309
310 protected final void setXY(final float x, final float y)
311 {
312 this.dx = x;
313 this.dy = y;
314 }
315
316
317
318
319
320 protected final TextAlignment getTextAlignment()
321 {
322 return this.textAlignment;
323 }
324
325
326
327
328
329 protected final float getFontSize()
330 {
331 return this.fontSize;
332 }
333
334
335
336
337
338 protected final Font getFont()
339 {
340 return this.font;
341 }
342
343
344
345
346
347 protected final String getText()
348 {
349 return this.text;
350 }
351
352
353
354
355
356 protected final void setText(final String text)
357 {
358 this.text = text;
359 synchronized (this.font)
360 {
361 this.fontRectangle = null;
362 }
363 }
364
365
366
367
368
369 protected final Color getColor()
370 {
371 return this.color;
372 }
373
374
375
376
377
378 protected final void setColor(final Color color)
379 {
380 this.color = color;
381 }
382
383
384
385
386
387 public final boolean isFlip()
388 {
389 return this.animationImpl.isFlip();
390 }
391
392
393
394
395
396 public final void setFlip(final boolean flip)
397 {
398 this.animationImpl.setFlip(flip);
399 }
400
401
402
403
404
405 public final boolean isRotate()
406 {
407 return this.animationImpl.isRotate();
408 }
409
410
411
412
413
414 public final void setRotate(final boolean rotate)
415 {
416 this.animationImpl.setRotate(rotate);
417
418 }
419
420
421
422
423
424 public final boolean isScale()
425 {
426 return this.animationImpl.isScale();
427 }
428
429
430
431
432
433 public final void setScale(final boolean scale)
434 {
435 this.animationImpl.setScale(scale);
436 }
437
438
439
440
441
442 public final boolean isTranslate()
443 {
444 return this.animationImpl.isTranslate();
445 }
446
447
448
449
450
451 public final void setTranslate(final boolean translate)
452 {
453 this.animationImpl.setTranslate(translate);
454 }
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469 private static class AnimationImpl extends Renderable2D<Locatable> implements Serializable
470 {
471
472 private static final long serialVersionUID = 20170400L;
473
474
475
476
477
478
479
480
481 AnimationImpl(final Locatable source, final SimulatorInterface.TimeDoubleUnit simulator)
482 throws NamingException, RemoteException
483 {
484 super(source, simulator);
485 }
486
487
488 @Override
489 public final void paint(final Graphics2D graphics, final ImageObserver observer) throws RemoteException
490 {
491 TextAnimation./../../org/opentrafficsim/draw/core/TextAnimation.html#TextAnimation">TextAnimation ta = ((TextAnimation) getSource());
492 ta.paint(graphics, observer);
493 }
494
495
496 @Override
497 public boolean contains(final Point2D pointWorldCoordinates, final Rectangle2D extent, final Dimension screen)
498 {
499 return false;
500 }
501
502
503 @Override
504 public final String toString()
505 {
506 return "TextAnimation.AnimationImpl []";
507 }
508
509 }
510
511
512
513
514
515 protected ScaleDependentRendering getScaleDependentRendering()
516 {
517 return this.scaleDependentRendering;
518 }
519
520
521
522
523 public interface ContrastToBackground
524 {
525
526
527
528
529 Color getBackgroundColor();
530 }
531
532
533
534
535 public interface ScaleDependentRendering
536 {
537
538
539
540
541
542
543 boolean isRendered(double scale);
544 }
545
546
547 public static final ScaleDependentRendering RENDERALWAYS = new ScaleDependentRendering()
548 {
549
550 @Override
551 public boolean isRendered(final double scale)
552 {
553 return true;
554 }
555 };
556
557
558 public static final ScaleDependentRendering RENDERWHEN1 = new ScaleDependentRendering()
559 {
560
561 @Override
562 public boolean isRendered(final double scale)
563 {
564 return scale >= 1.0;
565 }
566 };
567
568
569 public static final ScaleDependentRendering RENDERWHEN10 = new ScaleDependentRendering()
570 {
571
572 @Override
573 public boolean isRendered(final double scale)
574 {
575 return scale >= 0.1;
576 }
577 };
578
579
580 public static final ScaleDependentRendering RENDERWHEN100 = new ScaleDependentRendering()
581 {
582
583 @Override
584 public boolean isRendered(final double scale)
585 {
586 return scale >= 0.01;
587 }
588 };
589
590 }