1 package org.opentrafficsim.gui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.Container;
6 import java.awt.Dimension;
7 import java.awt.Graphics;
8 import java.awt.event.ActionEvent;
9 import java.awt.event.ActionListener;
10 import java.awt.event.MouseAdapter;
11 import java.awt.event.MouseEvent;
12 import java.awt.event.MouseListener;
13 import java.awt.event.WindowEvent;
14 import java.awt.event.WindowListener;
15 import java.awt.geom.Point2D;
16 import java.awt.geom.Rectangle2D;
17 import java.lang.reflect.Field;
18 import java.rmi.RemoteException;
19 import java.text.NumberFormat;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import javax.swing.BoxLayout;
26 import javax.swing.Icon;
27 import javax.swing.JButton;
28 import javax.swing.JCheckBox;
29 import javax.swing.JFrame;
30 import javax.swing.JLabel;
31 import javax.swing.JPanel;
32 import javax.swing.JTextField;
33 import javax.swing.JToggleButton;
34
35 import org.opentrafficsim.base.modelproperties.PropertyException;
36 import org.opentrafficsim.core.gtu.GTU;
37 import org.opentrafficsim.core.gtu.Try;
38 import org.opentrafficsim.core.gtu.animation.GTUColorer;
39 import org.opentrafficsim.core.network.Network;
40 import org.opentrafficsim.core.network.OTSNetwork;
41 import org.opentrafficsim.simulationengine.SimpleAnimator;
42 import org.opentrafficsim.simulationengine.WrappableAnimation;
43
44 import nl.javel.gisbeans.map.MapInterface;
45 import nl.tudelft.simulation.dsol.animation.Locatable;
46 import nl.tudelft.simulation.dsol.animation.D2.AnimationPanel;
47 import nl.tudelft.simulation.dsol.animation.D2.GisRenderable2D;
48 import nl.tudelft.simulation.dsol.animation.D2.Renderable2DInterface;
49 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
50 import nl.tudelft.simulation.event.Event;
51 import nl.tudelft.simulation.event.EventInterface;
52 import nl.tudelft.simulation.event.EventListenerInterface;
53 import nl.tudelft.simulation.language.d3.DirectedPoint;
54 import nl.tudelft.simulation.language.reflection.ClassUtil;
55
56
57
58
59
60
61
62
63
64
65
66
67 public class OTSAnimationPanel extends OTSSimulationPanel implements ActionListener, WindowListener, EventListenerInterface
68 {
69
70 private static final long serialVersionUID = 20150617L;
71
72
73 private final AutoAnimationPanel animationPanel;
74
75
76 private final JPanel borderPanel;
77
78
79 private final JPanel togglePanel;
80
81
82 private Map<String, Class<? extends Locatable>> toggleLocatableMap = new HashMap<>();
83
84
85 private Map<Class<? extends Locatable>, JToggleButton> toggleButtons = new HashMap<>();
86
87
88 private Map<String, MapInterface> toggleGISMap = new HashMap<>();
89
90
91 private Map<String, JToggleButton> toggleGISButtons = new HashMap<>();
92
93
94 private GTUColorer gtuColorer = null;
95
96
97 private ColorControlPanel colorControlPanel = null;
98
99
100 private final JLabel coordinateField;
101
102
103 private final JLabel gtuCountField;
104
105
106 private int gtuCount = 0;
107
108
109 private final ArrayList<JButton> buttons = new ArrayList<>();
110
111
112 private static final NumberFormat FORMATTER = NumberFormat.getInstance();
113
114
115 @SuppressWarnings("checkstyle:visibilitymodifier")
116 protected boolean closeHandlerRegistered = false;
117
118
119 @SuppressWarnings("checkstyle:visibilitymodifier")
120 protected boolean windowExited = false;
121
122
123 private boolean autoPan = false;
124
125
126 private final JCheckBox autoPanToggle;
127
128
129 private final JTextField autoPanField;
130
131
132 static
133 {
134 FORMATTER.setMaximumFractionDigits(3);
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148 public OTSAnimationPanel(final Rectangle2D extent, final Dimension size, final SimpleAnimator simulator,
149 final WrappableAnimation wrappableAnimation, final GTUColorer gtuColorer, final OTSNetwork network)
150 throws RemoteException, PropertyException
151 {
152 super(simulator, wrappableAnimation);
153
154
155
156 this.animationPanel = new AutoAnimationPanel(extent, size, simulator, network);
157 this.animationPanel.showGrid(false);
158 this.borderPanel = new JPanel(new BorderLayout());
159 this.borderPanel.add(this.animationPanel, BorderLayout.CENTER);
160 getTabbedPane().addTab(0, "animation", this.borderPanel);
161 getTabbedPane().setSelectedIndex(0);
162
163
164 this.gtuColorer = gtuColorer;
165 this.colorControlPanel = new ColorControlPanel(this.gtuColorer);
166 JPanel buttonPanel = new JPanel();
167 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
168 this.borderPanel.add(buttonPanel, BorderLayout.NORTH);
169 buttonPanel.add(this.colorControlPanel);
170
171
172 this.togglePanel = new JPanel();
173 this.togglePanel.setLayout(new BoxLayout(this.togglePanel, BoxLayout.Y_AXIS));
174 this.borderPanel.add(this.togglePanel, BorderLayout.WEST);
175
176
177 buttonPanel.add(new JLabel(" "));
178 buttonPanel.add(makeButton("allButton", "/Expand.png", "ZoomAll", "Zoom whole network", true));
179 buttonPanel.add(makeButton("homeButton", "/Home.png", "Home", "Zoom to original extent", true));
180 buttonPanel.add(makeButton("gridButton", "/Grid.png", "Grid", "Toggle grid on/off", true));
181 buttonPanel.add(new JLabel(" "));
182
183
184 JPanel infoTextPanel = new JPanel();
185 buttonPanel.add(infoTextPanel);
186 infoTextPanel.setMinimumSize(new Dimension(250, 20));
187 infoTextPanel.setPreferredSize(new Dimension(250, 20));
188 infoTextPanel.setLayout(new BoxLayout(infoTextPanel, BoxLayout.Y_AXIS));
189 this.coordinateField = new JLabel("Mouse: ");
190 this.coordinateField.setMinimumSize(new Dimension(250, 10));
191 this.coordinateField.setPreferredSize(new Dimension(250, 10));
192 infoTextPanel.add(this.coordinateField);
193
194 JPanel gtuPanel = new JPanel();
195 gtuPanel.setAlignmentX(0.0f);
196 gtuPanel.setLayout(new BoxLayout(gtuPanel, BoxLayout.X_AXIS));
197 gtuPanel.setMinimumSize(new Dimension(250, 10));
198 gtuPanel.setPreferredSize(new Dimension(250, 10));
199 infoTextPanel.add(gtuPanel);
200 if (null != network)
201 {
202 network.addListener(this, Network.GTU_ADD_EVENT);
203 network.addListener(this, Network.GTU_REMOVE_EVENT);
204 }
205
206
207 class AppearanceTextField extends JTextField implements AppearanceControl
208 {
209 private static final long serialVersionUID = 20180430L;
210
211
212 @Override
213 public String toString()
214 {
215 return "AppearanceTextField []";
216 }
217 }
218 this.autoPanField = new AppearanceTextField();
219 this.autoPanField.setMaximumSize(new Dimension(100, 20));
220 this.autoPanField.setVisible(false);
221 this.autoPanField.addActionListener(new ActionListener()
222 {
223 @SuppressWarnings("synthetic-access")
224 @Override
225 public void actionPerformed(final ActionEvent e)
226 {
227 OTSAnimationPanel.this.animationPanel.repaint();
228 }
229 });
230 this.autoPanToggle = new JCheckBox();
231 this.autoPanToggle.setToolTipText("Pan to GTU");
232 this.autoPanToggle.addActionListener(new ActionListener()
233 {
234 @SuppressWarnings("synthetic-access")
235 @Override
236 public void actionPerformed(final ActionEvent e)
237 {
238 OTSAnimationPanel.this.autoPan = !OTSAnimationPanel.this.autoPanField.isVisible();
239 OTSAnimationPanel.this.autoPanField.setVisible(OTSAnimationPanel.this.autoPan);
240 if (OTSAnimationPanel.this.autoPan)
241 {
242 OTSAnimationPanel.this.autoPanField.requestFocusInWindow();
243 OTSAnimationPanel.this.autoPanField.selectAll();
244 }
245 gtuPanel.revalidate();
246 }
247 });
248 gtuPanel.add(this.autoPanToggle);
249 gtuPanel.add(this.autoPanField);
250
251 this.gtuCountField = new JLabel("0 GTU's");
252 this.gtuCount = null == network ? 0 : network.getGTUs().size();
253 gtuPanel.add(this.gtuCountField);
254 setGtuCountText();
255
256
257 this.animationPanel.notify(new Event(SimulatorInterface.START_REPLICATION_EVENT, simulator, null));
258
259
260 this.animationPanel.setShowToolTip(false);
261
262
263 new UpdateTimer().start();
264
265
266 installWindowCloseHandler();
267 }
268
269
270
271
272
273
274
275
276
277
278 private JButton makeButton(final String name, final String iconPath, final String actionCommand, final String toolTipText,
279 final boolean enabled)
280 {
281
282 JButton result = new JButton(OTSControlPanel.loadIcon(iconPath));
283 result.setPreferredSize(new Dimension(34, 32));
284 result.setName(name);
285 result.setEnabled(enabled);
286 result.setActionCommand(actionCommand);
287 result.setToolTipText(toolTipText);
288 result.addActionListener(this);
289 this.buttons.add(result);
290 return result;
291 }
292
293
294
295
296
297
298
299
300
301
302
303
304 public final void addToggleAnimationButtonIcon(final String name, final Class<? extends Locatable> locatableClass,
305 final String iconPath, final String toolTipText, final boolean initiallyVisible, final boolean idButton)
306 {
307 JToggleButton button;
308 Icon icon = OTSControlPanel.loadIcon(iconPath);
309 Icon unIcon = OTSControlPanel.loadGrayscaleIcon(iconPath);
310 button = new JCheckBox();
311 button.setSelectedIcon(icon);
312 button.setIcon(unIcon);
313 button.setPreferredSize(new Dimension(32, 28));
314 button.setName(name);
315 button.setEnabled(true);
316 button.setSelected(initiallyVisible);
317 button.setActionCommand(name);
318 button.setToolTipText(toolTipText);
319 button.addActionListener(this);
320
321
322 if (idButton && this.togglePanel.getComponentCount() > 0)
323 {
324 JPanel lastToggleBox = (JPanel) this.togglePanel.getComponent(this.togglePanel.getComponentCount() - 1);
325 lastToggleBox.add(button);
326 }
327 else
328 {
329 JPanel toggleBox = new JPanel();
330 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
331 toggleBox.add(button);
332 this.togglePanel.add(toggleBox);
333 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
334 }
335
336 if (initiallyVisible)
337 {
338 this.animationPanel.showClass(locatableClass);
339 }
340 else
341 {
342 this.animationPanel.hideClass(locatableClass);
343 }
344 this.toggleLocatableMap.put(name, locatableClass);
345 this.toggleButtons.put(locatableClass, button);
346 }
347
348
349
350
351
352
353
354
355 public final void addToggleAnimationButtonText(final String name, final Class<? extends Locatable> locatableClass,
356 final String toolTipText, final boolean initiallyVisible)
357 {
358 JToggleButton button;
359 button = new JCheckBox(name);
360 button.setName(name);
361 button.setEnabled(true);
362 button.setSelected(initiallyVisible);
363 button.setActionCommand(name);
364 button.setToolTipText(toolTipText);
365 button.addActionListener(this);
366
367 JPanel toggleBox = new JPanel();
368 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
369 toggleBox.add(button);
370 this.togglePanel.add(toggleBox);
371 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
372
373 if (initiallyVisible)
374 {
375 this.animationPanel.showClass(locatableClass);
376 }
377 else
378 {
379 this.animationPanel.hideClass(locatableClass);
380 }
381 this.toggleLocatableMap.put(name, locatableClass);
382 this.toggleButtons.put(locatableClass, button);
383 }
384
385
386
387
388
389 public final void addToggleText(final String text)
390 {
391 JPanel textBox = new JPanel();
392 textBox.setLayout(new BoxLayout(textBox, BoxLayout.X_AXIS));
393 textBox.add(new JLabel(text));
394 this.togglePanel.add(textBox);
395 textBox.setAlignmentX(Component.LEFT_ALIGNMENT);
396 }
397
398
399
400
401
402
403
404
405 public final void addToggleGISButtonText(final String layerName, final String displayName, final GisRenderable2D gisMap,
406 final String toolTipText)
407 {
408 JToggleButton button;
409 button = new JCheckBox(displayName);
410 button.setName(layerName);
411 button.setEnabled(true);
412 button.setSelected(true);
413 button.setActionCommand(layerName);
414 button.setToolTipText(toolTipText);
415 button.addActionListener(this);
416
417 JPanel toggleBox = new JPanel();
418 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
419 toggleBox.add(button);
420 this.togglePanel.add(toggleBox);
421 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
422
423 this.toggleGISMap.put(layerName, gisMap.getMap());
424 this.toggleGISButtons.put(layerName, button);
425 }
426
427
428
429
430
431 public final void showGISLayer(final String layerName)
432 {
433 MapInterface gisMap = this.toggleGISMap.get(layerName);
434 if (gisMap != null)
435 {
436 try
437 {
438 gisMap.showLayer(layerName);
439 this.toggleGISButtons.get(layerName).setSelected(true);
440 this.animationPanel.repaint();
441 }
442 catch (RemoteException exception)
443 {
444 exception.printStackTrace();
445 }
446 }
447 }
448
449
450
451
452
453 public final void hideGISLayer(final String layerName)
454 {
455 MapInterface gisMap = this.toggleGISMap.get(layerName);
456 if (gisMap != null)
457 {
458 try
459 {
460 gisMap.hideLayer(layerName);
461 this.toggleGISButtons.get(layerName).setSelected(false);
462 this.animationPanel.repaint();
463 }
464 catch (RemoteException exception)
465 {
466 exception.printStackTrace();
467 }
468 }
469 }
470
471
472
473
474
475 public final void toggleGISLayer(final String layerName)
476 {
477 MapInterface gisMap = this.toggleGISMap.get(layerName);
478 if (gisMap != null)
479 {
480 try
481 {
482 if (gisMap.getVisibleLayers().contains(gisMap.getLayerMap().get(layerName)))
483 {
484 gisMap.hideLayer(layerName);
485 this.toggleGISButtons.get(layerName).setSelected(false);
486 }
487 else
488 {
489 gisMap.showLayer(layerName);
490 this.toggleGISButtons.get(layerName).setSelected(true);
491 }
492 this.animationPanel.repaint();
493 }
494 catch (RemoteException exception)
495 {
496 exception.printStackTrace();
497 }
498 }
499 }
500
501
502 @Override
503 public final void actionPerformed(final ActionEvent actionEvent)
504 {
505 String actionCommand = actionEvent.getActionCommand();
506 try
507 {
508 if (actionCommand.equals("Home"))
509 {
510 this.animationPanel.home();
511 }
512 if (actionCommand.equals("ZoomAll"))
513 {
514 this.animationPanel.zoomAll();
515 }
516 if (actionCommand.equals("Grid"))
517 {
518 this.animationPanel.showGrid(!this.animationPanel.isShowGrid());
519 }
520
521 if (this.toggleLocatableMap.containsKey(actionCommand))
522 {
523 Class<? extends Locatable> locatableClass = this.toggleLocatableMap.get(actionCommand);
524 this.animationPanel.toggleClass(locatableClass);
525 this.togglePanel.repaint();
526 }
527
528 if (this.toggleGISMap.containsKey(actionCommand))
529 {
530 this.toggleGISLayer(actionCommand);
531 this.togglePanel.repaint();
532 }
533 }
534 catch (Exception exception)
535 {
536 exception.printStackTrace();
537 }
538 }
539
540
541
542
543
544 public final AnimationPanel getAnimationPanel()
545 {
546 return this.animationPanel;
547 }
548
549
550
551
552
553 public final void updateAnimationClassCheckBox(final Class<? extends Locatable> locatableClass)
554 {
555 JToggleButton button = this.toggleButtons.get(locatableClass);
556 if (button == null)
557 {
558 return;
559 }
560
561 Field field = Try.assign(() -> ClassUtil.resolveField(AnimationPanel.class, "visibilityMap"), "No field visibilityMap");
562 field.setAccessible(true);
563 @SuppressWarnings("unchecked")
564 Map<Class<? extends Locatable>, Boolean> map =
565 Try.assign(() -> (Map<Class<? extends Locatable>, Boolean>) field.get(this.getAnimationPanel()),
566 "visibilityMap not a map?");
567 Boolean show = map.get(locatableClass);
568 if (show == null)
569 {
570 return;
571 }
572 button.setSelected(show);
573 }
574
575
576
577
578 protected final void updateWorldCoordinate()
579 {
580 String worldPoint = "(x=" + FORMATTER.format(this.animationPanel.getWorldCoordinate().getX()) + " ; y="
581 + FORMATTER.format(this.animationPanel.getWorldCoordinate().getY()) + ")";
582 this.coordinateField.setText("Mouse: " + worldPoint);
583 int requiredWidth = this.coordinateField.getGraphics().getFontMetrics().stringWidth(this.coordinateField.getText());
584 if (this.coordinateField.getPreferredSize().width < requiredWidth)
585 {
586 Dimension requiredSize = new Dimension(requiredWidth, this.coordinateField.getPreferredSize().height);
587 this.coordinateField.setPreferredSize(requiredSize);
588 this.coordinateField.setMinimumSize(requiredSize);
589 Container parent = this.coordinateField.getParent();
590 parent.setPreferredSize(requiredSize);
591 parent.setMinimumSize(requiredSize);
592
593 parent.revalidate();
594 }
595 this.coordinateField.repaint();
596 }
597
598
599
600
601
602
603 public final GTUColorer getGTUColorer()
604 {
605 return this.gtuColorer;
606 }
607
608
609
610
611
612
613 public final ColorControlPanel getColorControlPanel()
614 {
615 return this.colorControlPanel;
616 }
617
618
619
620
621 public final void installWindowCloseHandler()
622 {
623 if (this.closeHandlerRegistered)
624 {
625 return;
626 }
627
628
629 new DisposeOnCloseThread(this).start();
630 }
631
632
633 protected class DisposeOnCloseThread extends Thread
634 {
635
636 private OTSAnimationPanel panel;
637
638
639
640
641 public DisposeOnCloseThread(final OTSAnimationPanel panel)
642 {
643 super();
644 this.panel = panel;
645 }
646
647
648 @Override
649 public final void run()
650 {
651 Container root = this.panel;
652 while (!(root instanceof JFrame))
653 {
654 try
655 {
656 Thread.sleep(10);
657 }
658 catch (@SuppressWarnings("unused") InterruptedException exception)
659 {
660
661 }
662
663
664 root = this.panel;
665 while (null != root.getParent() && !(root instanceof JFrame))
666 {
667 root = root.getParent();
668 }
669 }
670 JFrame frame = (JFrame) root;
671 frame.addWindowListener(this.panel);
672 this.panel.closeHandlerRegistered = true;
673 }
674
675
676 @Override
677 public final String toString()
678 {
679 return "DisposeOnCloseThread of OTSAnimationPanel [panel=" + this.panel + "]";
680 }
681 }
682
683
684 @Override
685 public void windowOpened(final WindowEvent e)
686 {
687
688 }
689
690
691 @Override
692 public final void windowClosing(final WindowEvent e)
693 {
694
695 }
696
697
698 @Override
699 public final void windowClosed(final WindowEvent e)
700 {
701 this.windowExited = true;
702 }
703
704
705 @Override
706 public final void windowIconified(final WindowEvent e)
707 {
708
709 }
710
711
712 @Override
713 public final void windowDeiconified(final WindowEvent e)
714 {
715
716 }
717
718
719 @Override
720 public final void windowActivated(final WindowEvent e)
721 {
722
723 }
724
725
726 @Override
727 public final void windowDeactivated(final WindowEvent e)
728 {
729
730 }
731
732
733 @Override
734 public void notify(final EventInterface event) throws RemoteException
735 {
736 if (event.getType().equals(Network.GTU_ADD_EVENT))
737 {
738 this.gtuCount++;
739 setGtuCountText();
740 }
741 else if (event.getType().equals(Network.GTU_REMOVE_EVENT))
742 {
743 this.gtuCount--;
744 setGtuCountText();
745 }
746 }
747
748
749
750
751 private void setGtuCountText()
752 {
753 this.gtuCountField.setText(this.gtuCount + " GTU's");
754 }
755
756
757
758
759 protected class UpdateTimer extends Thread
760 {
761
762 @Override
763 public final void run()
764 {
765 while (!OTSAnimationPanel.this.windowExited)
766 {
767 if (OTSAnimationPanel.this.isShowing())
768 {
769 OTSAnimationPanel.this.updateWorldCoordinate();
770 }
771 try
772 {
773 Thread.sleep(50);
774 }
775 catch (@SuppressWarnings("unused") InterruptedException exception)
776 {
777
778 }
779 }
780 }
781
782
783 @Override
784 public final String toString()
785 {
786 return "UpdateTimer thread for OTSAnimationPanel";
787 }
788
789 }
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804 private class AutoAnimationPanel extends AnimationPanel
805 {
806
807
808 private static final long serialVersionUID = 20180430L;
809
810
811 private final OTSNetwork network;
812
813
814 private GTU lastGtu;
815
816
817
818
819
820
821
822
823
824 AutoAnimationPanel(final Rectangle2D extent, final Dimension size, final SimulatorInterface<?, ?, ?> simulator,
825 final OTSNetwork network) throws RemoteException
826 {
827 super(extent, size, simulator);
828 this.network = network;
829 MouseListener[] listeners = getMouseListeners();
830 for (MouseListener listener : listeners)
831 {
832 removeMouseListener(listener);
833 }
834 this.addMouseListener(new MouseAdapter()
835 {
836
837 @SuppressWarnings("synthetic-access")
838 @Override
839 public void mouseReleased(final MouseEvent e)
840 {
841 if (e.isControlDown())
842 {
843 GTU gtu = getSelectedGTU(e.getPoint());
844 if (gtu != null)
845 {
846 OTSAnimationPanel.this.autoPanField.setText(gtu.getId());
847 OTSAnimationPanel.this.autoPanField.setVisible(true);
848 OTSAnimationPanel.this.autoPanToggle.setSelected(true);
849 OTSAnimationPanel.this.autoPanToggle.getParent().validate();
850 OTSAnimationPanel.this.autoPan = true;
851 e.consume();
852 }
853 }
854 e.consume();
855 }
856 });
857 for (MouseListener listener : listeners)
858 {
859 addMouseListener(listener);
860 }
861 }
862
863
864
865
866
867
868 @SuppressWarnings("synthetic-access")
869 protected GTU getSelectedGTU(final Point2D mousePoint)
870 {
871 List<GTU> targets = new ArrayList<>();
872 Point2D point = Renderable2DInterface.Util.getWorldCoordinates(mousePoint,
873 OTSAnimationPanel.this.animationPanel.getExtent(), OTSAnimationPanel.this.animationPanel.getSize());
874 for (Renderable2DInterface<?> renderable : OTSAnimationPanel.this.animationPanel.getElements())
875 {
876 if (OTSAnimationPanel.this.animationPanel.isShowElement(renderable) && renderable.contains(point,
877 OTSAnimationPanel.this.animationPanel.getExtent(), OTSAnimationPanel.this.animationPanel.getSize()))
878 {
879 if (renderable.getSource() instanceof GTU)
880 {
881 targets.add((GTU) renderable.getSource());
882 }
883 }
884 }
885 if (targets.size() == 1)
886 {
887 return targets.get(0);
888 }
889 return null;
890 }
891
892
893 @SuppressWarnings("synthetic-access")
894 @Override
895 public void paint(final Graphics g)
896 {
897 if (OTSAnimationPanel.this.autoPan)
898 {
899 String id = OTSAnimationPanel.this.autoPanField.getText();
900 if (this.lastGtu == null || !this.lastGtu.getId().equals(id) || this.lastGtu.isDestroyed())
901 {
902 this.lastGtu = this.network.getGTU(id);
903 }
904 DirectedPoint point;
905 try
906 {
907 point = this.lastGtu != null ? this.lastGtu.getLocation() : null;
908 }
909 catch (@SuppressWarnings("unused") RemoteException exception)
910 {
911 System.err.println("Could not pan to GTU location.");
912 return;
913 }
914 if (point != null)
915 {
916 double w = this.extent.getWidth();
917 double h = this.extent.getHeight();
918 this.extent = new Rectangle2D.Double(point.getX() - w / 2, point.getY() - h / 2, w, h);
919 }
920 }
921 super.paint(g);
922 }
923
924
925 @Override
926 public String toString()
927 {
928 return "AutoAnimationPanel [network=" + this.network + ", lastGtu=" + this.lastGtu + "]";
929 }
930 }
931
932 }