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