1 package nl.tudelft.simulation.dsol.web.animation.D2;
2
3 import java.awt.Canvas;
4 import java.awt.Color;
5 import java.awt.Dimension;
6 import java.awt.Font;
7 import java.awt.FontMetrics;
8 import java.awt.Graphics;
9 import java.awt.Image;
10 import java.awt.geom.Point2D;
11 import java.awt.geom.RectangularShape;
12 import java.awt.image.ImageObserver;
13 import java.text.NumberFormat;
14
15 import org.djutils.draw.bounds.Bounds2d;
16 import org.djutils.draw.point.Point2d;
17
18 import nl.tudelft.simulation.dsol.animation.D2.RenderableScale;
19 import nl.tudelft.simulation.dsol.web.animation.HTMLGraphics2D;
20
21
22
23
24
25
26
27
28
29
30
31 public class HTMLGridPanel implements ImageObserver
32 {
33
34 public static final int UP = 1;
35
36
37 public static final int DOWN = 2;
38
39
40 public static final int LEFT = 3;
41
42
43 public static final int RIGHT = 4;
44
45
46 public static final double ZOOMFACTOR = 1.2;
47
48
49 protected static final Color GRIDCOLOR = Color.BLACK;
50
51
52 @SuppressWarnings("checkstyle:visibilitymodifier")
53 protected Bounds2d extent = null;
54
55
56 @SuppressWarnings("checkstyle:visibilitymodifier")
57 protected Bounds2d homeExtent = null;
58
59
60 @SuppressWarnings("checkstyle:visibilitymodifier")
61 protected boolean showGrid = true;
62
63
64 @SuppressWarnings("checkstyle:visibilitymodifier")
65 protected double gridSizeX = 100.0;
66
67
68 @SuppressWarnings("checkstyle:visibilitymodifier")
69 protected double gridSizeY = 100.0;
70
71
72 @SuppressWarnings("checkstyle:visibilitymodifier")
73 protected NumberFormat formatter = NumberFormat.getInstance();
74
75
76 @SuppressWarnings("checkstyle:visibilitymodifier")
77 protected Dimension lastDimension = null;
78
79
80 @SuppressWarnings("checkstyle:visibilitymodifier")
81 protected Dimension size = null;
82
83
84 @SuppressWarnings("checkstyle:visibilitymodifier")
85 protected Dimension preferredSize = null;
86
87
88 @SuppressWarnings("checkstyle:visibilitymodifier")
89 protected Point2d worldCoordinate = new Point2d(0, 0);
90
91
92 @SuppressWarnings("checkstyle:visibilitymodifier")
93 protected boolean showToolTip = true;
94
95
96 private Color background;
97
98
99 private String toolTipText = "";
100
101
102 private boolean showing = true;
103
104
105 private Font currentFont = new Font(Font.SANS_SERIF, Font.PLAIN, 10);
106
107
108 private Canvas canvas = new Canvas();
109
110
111 @SuppressWarnings("checkstyle:visibilitymodifier")
112 protected HTMLGraphics2D htmlGraphics2D;
113
114
115 @SuppressWarnings("checkstyle:visibilitymodifier")
116 protected RenderableScale renderableScale;
117
118
119 private boolean dirty = false;
120
121
122
123
124
125 public HTMLGridPanel(final Bounds2d extent)
126 {
127 this(extent, new Dimension(600, 600));
128 }
129
130
131
132
133
134
135 public HTMLGridPanel(final Bounds2d extent, final Dimension size)
136 {
137 this.htmlGraphics2D = new HTMLGraphics2D();
138 this.homeExtent = extent;
139 this.renderableScale = new RenderableScale();
140 this.setBackground(Color.WHITE);
141 this.setPreferredSize(size);
142 this.size = (Dimension) size.clone();
143 this.lastDimension = this.getSize();
144 setExtent(this.homeExtent);
145 }
146
147
148
149
150
151 public String getDrawingCommands()
152 {
153 this.htmlGraphics2D.clearCommand();
154 this.paintComponent(this.htmlGraphics2D);
155 return this.htmlGraphics2D.closeAndGetCommands();
156 }
157
158
159
160
161
162 public void paintComponent(final HTMLGraphics2D g)
163 {
164 if (!this.getSize().equals(this.lastDimension))
165 {
166 this.lastDimension = this.getSize();
167 setExtent(this.renderableScale.computeVisibleExtent(this.extent, this.getSize()));
168 }
169 if (this.showGrid)
170 {
171 this.drawGrid(g);
172 }
173 }
174
175
176
177
178
179 public final synchronized void showGrid(final boolean bool)
180 {
181 this.showGrid = bool;
182 this.repaint();
183 }
184
185
186
187
188
189 public final Bounds2d getExtent()
190 {
191 return this.extent;
192 }
193
194
195
196
197
198 public final void setExtent(final Bounds2d extent)
199 {
200 this.extent = extent;
201 this.repaint();
202 }
203
204
205
206
207
208 public final synchronized void setWorldCoordinate(final Point2d point)
209 {
210 this.worldCoordinate = point;
211 }
212
213
214
215
216 public final synchronized Point2d getWorldCoordinate()
217 {
218 return this.worldCoordinate;
219 }
220
221
222
223
224 public final synchronized void displayWorldCoordinateToolTip()
225 {
226 if (this.showToolTip)
227 {
228 String worldPoint = "(x=" + this.formatter.format(this.worldCoordinate.getX()) + " ; y="
229 + this.formatter.format(this.worldCoordinate.getY()) + ")";
230 setToolTipText(worldPoint);
231 }
232 }
233
234
235
236
237 public final synchronized boolean isShowToolTip()
238 {
239 return this.showToolTip;
240 }
241
242
243
244
245 public final synchronized void setShowToolTip(final boolean showToolTip)
246 {
247 this.showToolTip = showToolTip;
248 }
249
250
251
252
253
254
255 public final synchronized void pan(final int direction, final double percentage)
256 {
257 if (percentage <= 0 || percentage > 1.0)
258 {
259 throw new IllegalArgumentException("percentage<=0 || >1.0");
260 }
261 switch (direction)
262 {
263 case LEFT:
264 setExtent(new Bounds2d(this.extent.getMinX() - percentage * this.extent.getDeltaX(),
265 this.extent.getMaxX() - percentage * this.extent.getDeltaX(), this.extent.getMinY(),
266 this.extent.getMaxY()));
267 break;
268 case RIGHT:
269 setExtent(new Bounds2d(this.extent.getMinX() + percentage * this.extent.getDeltaX(),
270 this.extent.getMaxX() + percentage * this.extent.getDeltaX(), this.extent.getMinY(),
271 this.extent.getMaxY()));
272 break;
273 case UP:
274 setExtent(new Bounds2d(this.extent.getMinX(), this.extent.getMaxX(),
275 this.extent.getMinY() + percentage * this.extent.getDeltaY(),
276 this.extent.getMaxY() + percentage * this.extent.getDeltaY()));
277 break;
278 case DOWN:
279 setExtent(new Bounds2d(this.extent.getMinX(), this.extent.getMaxX(),
280 this.extent.getMinY() - percentage * this.extent.getDeltaY(),
281 this.extent.getMaxY() - percentage * this.extent.getDeltaY()));
282 break;
283 default:
284 throw new IllegalArgumentException("direction unkown");
285 }
286 this.repaint();
287 }
288
289
290
291
292 public final synchronized void home()
293 {
294 setExtent(this.renderableScale.computeVisibleExtent(this.homeExtent, this.getSize()));
295 this.repaint();
296 }
297
298
299
300
301 public final boolean isShowGrid()
302 {
303 return this.showGrid;
304 }
305
306
307
308
309 public final void setShowGrid(final boolean showGrid)
310 {
311 this.showGrid = showGrid;
312 }
313
314
315
316
317
318 public final synchronized void zoom(final double factor)
319 {
320 zoom(factor, (int) (this.getWidth() / 2.0), (int) (this.getHeight() / 2.0));
321 }
322
323
324
325
326
327
328
329 public final synchronized void zoom(final double factor, final int mouseX, final int mouseY)
330 {
331 Point2d mwc = this.renderableScale.getWorldCoordinates(new Point2D.Double(mouseX, mouseY), this.extent, this.getSize());
332 double minX = mwc.getX() - (mwc.getX() - this.extent.getMinX()) * factor;
333 double minY = mwc.getY() - (mwc.getY() - this.extent.getMinY()) * factor;
334 double w = this.extent.getDeltaX() * factor;
335 double h = this.extent.getDeltaY() * factor;
336
337 setExtent(new Bounds2d(minX, minX + w, minY, minY + h));
338 this.repaint();
339 }
340
341
342
343
344
345
346 protected synchronized void drawGrid(final Graphics g)
347 {
348
349 g.setFont(g.getFont().deriveFont(11.0f));
350 g.setColor(GRIDCOLOR);
351 double scaleX = this.renderableScale.getXScale(this.extent, this.getSize());
352 double scaleY = this.renderableScale.getYScale(this.extent, this.getSize());
353
354 int count = 0;
355 int gridSizePixelsX = (int) Math.round(this.gridSizeX / scaleX);
356 while (gridSizePixelsX < 40)
357 {
358 this.gridSizeX = 10 * this.gridSizeX;
359 int maximumNumberOfDigits = (int) Math.max(0, 1 + Math.ceil(Math.log(1 / this.gridSizeX) / Math.log(10)));
360 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
361 gridSizePixelsX = (int) Math.round(this.gridSizeX / scaleX);
362 if (count++ > 10)
363 {
364 break;
365 }
366 }
367
368 count = 0;
369 while (gridSizePixelsX > 10 * 40)
370 {
371 int maximumNumberOfDigits = (int) Math.max(0, 2 + Math.ceil(Math.log(1 / this.gridSizeX) / Math.log(10)));
372 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
373 this.gridSizeX = this.gridSizeX / 10;
374 gridSizePixelsX = (int) Math.round(this.gridSizeX / scaleX);
375 if (count++ > 10)
376 {
377 break;
378 }
379 }
380
381 int gridSizePixelsY = (int) Math.round(this.gridSizeY / scaleY);
382 while (gridSizePixelsY < 40)
383 {
384 this.gridSizeY = 10 * this.gridSizeY;
385 int maximumNumberOfDigits = (int) Math.max(0, 1 + Math.ceil(Math.log(1 / this.gridSizeY) / Math.log(10)));
386 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
387 gridSizePixelsY = (int) Math.round(this.gridSizeY / scaleY);
388 if (count++ > 10)
389 {
390 break;
391 }
392 }
393
394 count = 0;
395 while (gridSizePixelsY > 10 * 40)
396 {
397 int maximumNumberOfDigits = (int) Math.max(0, 2 + Math.ceil(Math.log(1 / this.gridSizeY) / Math.log(10)));
398 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
399 this.gridSizeY = this.gridSizeY / 10;
400 gridSizePixelsY = (int) Math.round(this.gridSizeY / scaleY);
401 if (count++ > 10)
402 {
403 break;
404 }
405 }
406
407
408 double mod = this.extent.getMinX() % this.gridSizeX;
409 int x = (int) -Math.round(mod / scaleX);
410 while (x < this.getWidth())
411 {
412 Point2d point = this.renderableScale.getWorldCoordinates(new Point2D.Double(x, 0), this.extent, this.getSize());
413 if (point != null)
414 {
415 String label = this.formatter.format(Math.round(point.getX() / this.gridSizeX) * this.gridSizeX);
416 double labelWidth = this.getFontMetrics(this.getFont()).getStringBounds(label, g).getWidth();
417 if (x > labelWidth + 4)
418 {
419 g.drawLine(x, 15, x, this.getHeight());
420 g.drawString(label, (int) Math.round(x - 0.5 * labelWidth), 11);
421 }
422 }
423 x = x + gridSizePixelsX;
424 }
425
426
427 mod = Math.abs(this.extent.getMinY()) % this.gridSizeY;
428 int y = (int) Math.round(this.getSize().getHeight() - (mod / scaleY));
429 while (y > 15)
430 {
431 Point2d point = this.renderableScale.getWorldCoordinates(new Point2D.Double(0, y), this.extent, this.getSize());
432 if (point != null)
433 {
434 String label = this.formatter.format(Math.round(point.getY() / this.gridSizeY) * this.gridSizeY);
435 RectangularShape labelBounds = this.getFontMetrics(this.getFont()).getStringBounds(label, g);
436 g.drawLine((int) Math.round(labelBounds.getWidth() + 4), y, this.getWidth(), y);
437 g.drawString(label, 2, (int) Math.round(y + labelBounds.getHeight() * 0.3));
438 }
439 y = y - gridSizePixelsY;
440 }
441 }
442
443
444
445
446 public final RenderableScale getRenderableScale()
447 {
448 return this.renderableScale;
449 }
450
451
452
453
454 public final void setRenderableScale(final RenderableScale renderableScale)
455 {
456 this.renderableScale = renderableScale;
457 }
458
459
460
461
462 public void repaint()
463 {
464
465 this.dirty = true;
466 }
467
468
469
470
471 public final Dimension getSize()
472 {
473 return this.size;
474 }
475
476
477
478
479 public final void setSize(final Dimension size)
480 {
481 this.size = size;
482 }
483
484
485
486
487 public final Color getBackground()
488 {
489 return this.background;
490 }
491
492
493
494
495 public final void setBackground(final Color background)
496 {
497 this.background = background;
498 }
499
500
501
502
503 public final int getWidth()
504 {
505 return this.size.width;
506 }
507
508
509
510
511 public final int getHeight()
512 {
513 return this.size.height;
514 }
515
516
517
518
519 public final Dimension getPreferredSize()
520 {
521 return this.preferredSize;
522 }
523
524
525
526
527 public final void setPreferredSize(final Dimension preferredSize)
528 {
529 this.preferredSize = preferredSize;
530 }
531
532
533
534
535 public final String getToolTipText()
536 {
537 return this.toolTipText;
538 }
539
540
541
542
543 public final void setToolTipText(final String toolTipText)
544 {
545 this.toolTipText = toolTipText;
546 }
547
548
549
550
551 public final boolean isShowing()
552 {
553 return this.showing;
554 }
555
556
557
558
559 public final void setShowing(final boolean showing)
560 {
561 this.showing = showing;
562 }
563
564
565
566
567 public final Font getFont()
568 {
569 return this.currentFont;
570 }
571
572
573
574
575 public final void setFont(final Font font)
576 {
577 this.currentFont = font;
578 }
579
580
581
582
583
584 public final FontMetrics getFontMetrics(final Font font)
585 {
586 return this.canvas.getFontMetrics(font);
587 }
588
589
590
591
592 public final boolean isDirty()
593 {
594 return this.dirty;
595 }
596
597
598 @Override
599 public boolean imageUpdate(final Image img, final int infoflags, final int x, final int y, final int width, final int height)
600 {
601 return false;
602 }
603
604 }