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