1 package org.opentrafficsim.swing.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.ContainerEvent;
11 import java.awt.event.ContainerListener;
12 import java.awt.event.MouseAdapter;
13 import java.awt.event.MouseEvent;
14 import java.awt.event.MouseListener;
15 import java.awt.event.MouseWheelEvent;
16 import java.awt.event.MouseWheelListener;
17 import java.awt.event.WindowEvent;
18 import java.awt.event.WindowListener;
19 import java.awt.geom.Point2D;
20 import java.awt.geom.Rectangle2D;
21 import java.rmi.RemoteException;
22 import java.text.NumberFormat;
23 import java.util.ArrayList;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import javax.swing.Box;
29 import javax.swing.BoxLayout;
30 import javax.swing.Icon;
31 import javax.swing.JButton;
32 import javax.swing.JCheckBox;
33 import javax.swing.JFrame;
34 import javax.swing.JLabel;
35 import javax.swing.JPanel;
36 import javax.swing.JToggleButton;
37 import javax.swing.border.EmptyBorder;
38
39 import org.djutils.draw.bounds.Bounds2d;
40 import org.djutils.draw.point.Point;
41 import org.djutils.draw.point.Point2d;
42 import org.djutils.event.Event;
43 import org.djutils.event.EventListener;
44 import org.djutils.event.TimedEvent;
45 import org.djutils.exceptions.Throw;
46 import org.opentrafficsim.animation.gtu.colorer.GtuColorer;
47 import org.opentrafficsim.core.dsol.OtsAnimator;
48 import org.opentrafficsim.core.dsol.OtsModelInterface;
49 import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
50 import org.opentrafficsim.core.gtu.Gtu;
51 import org.opentrafficsim.core.network.Network;
52
53 import nl.tudelft.simulation.dsol.animation.Locatable;
54 import nl.tudelft.simulation.dsol.animation.d2.Renderable2dInterface;
55 import nl.tudelft.simulation.dsol.animation.gis.GisMapInterface;
56 import nl.tudelft.simulation.dsol.animation.gis.GisRenderable2d;
57 import nl.tudelft.simulation.dsol.experiment.Replication;
58 import nl.tudelft.simulation.dsol.swing.animation.d2.AnimationPanel;
59 import nl.tudelft.simulation.dsol.swing.animation.d2.InputListener;
60 import nl.tudelft.simulation.dsol.swing.animation.d2.VisualizationPanel;
61 import nl.tudelft.simulation.language.DsolException;
62
63
64
65
66
67
68
69
70
71
72 public class OtsAnimationPanel extends OtsSimulationPanel implements ActionListener, WindowListener, EventListener
73 {
74
75 private static final long serialVersionUID = 20150617L;
76
77
78 private final AutoAnimationPanel animationPanel;
79
80
81 private final JPanel borderPanel;
82
83
84 private final JPanel togglePanel;
85
86
87 private JPanel demoPanel = null;
88
89
90 private Map<String, Class<? extends Locatable>> toggleLocatableMap = new LinkedHashMap<>();
91
92
93 private Map<Class<? extends Locatable>, JToggleButton> toggleButtons = new LinkedHashMap<>();
94
95
96 private Map<String, GisMapInterface> toggleGISMap = new LinkedHashMap<>();
97
98
99 private Map<String, JToggleButton> toggleGISButtons = new LinkedHashMap<>();
100
101
102 private ColorControlPanel colorControlPanel = null;
103
104
105 private final JLabel coordinateField;
106
107
108 private final JLabel gtuCountField;
109
110
111 private int gtuCount = 0;
112
113
114 private final ArrayList<JButton> buttons = new ArrayList<>();
115
116
117 private static final NumberFormat FORMATTER = NumberFormat.getInstance();
118
119
120 @SuppressWarnings("checkstyle:visibilitymodifier")
121 protected boolean closeHandlerRegistered = false;
122
123
124 @SuppressWarnings("checkstyle:visibilitymodifier")
125 protected boolean windowExited = false;
126
127
128 private String autoPanId = null;
129
130
131 private OtsSearchPanel.ObjectKind<?> autoPanKind = null;
132
133
134 private boolean autoPanTrack = false;
135
136
137 private boolean autoPanOnNextPaintComponent = false;
138
139
140 static
141 {
142 FORMATTER.setMaximumFractionDigits(3);
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156 public OtsAnimationPanel(final Rectangle2D extent, final Dimension size, final OtsAnimator simulator,
157 final OtsModelInterface otsModel, final List<GtuColorer> gtuColorers, final Network network)
158 throws RemoteException, DsolException
159 {
160 super(simulator, otsModel);
161
162
163
164 this.animationPanel = new AutoAnimationPanel(extent, size, simulator, network);
165 this.animationPanel.showGrid(false);
166 this.borderPanel = new JPanel(new BorderLayout());
167 this.borderPanel.add(this.animationPanel, BorderLayout.CENTER);
168 getTabbedPane().addTab(0, "animation", this.borderPanel);
169 getTabbedPane().setSelectedIndex(0);
170
171
172 this.colorControlPanel = new ColorControlPanel();
173 for (GtuColorer colorer : gtuColorers)
174 {
175 this.colorControlPanel.addItem(colorer);
176 }
177 JPanel buttonPanel = new JPanel();
178 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
179 buttonPanel.setPreferredSize(new Dimension(200, 35));
180 this.borderPanel.add(buttonPanel, BorderLayout.NORTH);
181 buttonPanel.add(this.colorControlPanel);
182
183
184 this.togglePanel = new JPanel();
185 this.togglePanel.setLayout(new BoxLayout(this.togglePanel, BoxLayout.Y_AXIS));
186 this.borderPanel.add(this.togglePanel, BorderLayout.WEST);
187
188
189 buttonPanel.add(Box.createHorizontalStrut(10));
190 buttonPanel.add(makeButton("allButton", "/Expand.png", "ZoomAll", "Zoom whole network", true));
191 buttonPanel.add(makeButton("homeButton", "/Home.png", "Home", "Zoom to original extent", true));
192 buttonPanel.add(makeButton("gridButton", "/Grid.png", "Grid", "Toggle grid on/off", true));
193 buttonPanel.add(Box.createHorizontalStrut(10));
194
195
196 JPanel infoTextPanel = new JPanel();
197 buttonPanel.add(infoTextPanel);
198 infoTextPanel.setMinimumSize(new Dimension(250, 20));
199 infoTextPanel.setPreferredSize(new Dimension(250, 20));
200 infoTextPanel.setLayout(new BoxLayout(infoTextPanel, BoxLayout.Y_AXIS));
201 this.coordinateField = new JLabel("Mouse: ");
202 this.coordinateField.setMinimumSize(new Dimension(250, 10));
203 this.coordinateField.setPreferredSize(new Dimension(250, 10));
204 infoTextPanel.add(this.coordinateField);
205
206 JPanel gtuPanel = new JPanel();
207 gtuPanel.setAlignmentX(0.0f);
208 gtuPanel.setLayout(new BoxLayout(gtuPanel, BoxLayout.X_AXIS));
209 gtuPanel.setMinimumSize(new Dimension(250, 10));
210 gtuPanel.setPreferredSize(new Dimension(250, 10));
211 infoTextPanel.add(gtuPanel);
212 if (null != network)
213 {
214 network.addListener(this, Network.GTU_ADD_EVENT);
215 network.addListener(this, Network.GTU_REMOVE_EVENT);
216 }
217
218 this.gtuCountField = new JLabel("0 GTU's");
219 this.gtuCount = null == network ? 0 : network.getGTUs().size();
220 gtuPanel.add(this.gtuCountField);
221 setGtuCountText();
222
223
224 this.animationPanel
225 .notify(new TimedEvent<>(Replication.START_REPLICATION_EVENT, null, getSimulator().getSimulatorTime()));
226
227
228 this.animationPanel.setShowToolTip(false);
229
230
231 new UpdateTimer().start();
232
233
234 installWindowCloseHandler();
235
236 }
237
238
239
240
241
242
243
244 public void setAutoPan(final String newAutoPanId, final OtsSearchPanel.ObjectKind<?> newAutoPanKind,
245 final boolean newAutoPanTrack)
246 {
247 this.autoPanId = newAutoPanId;
248 this.autoPanKind = newAutoPanKind;
249 this.autoPanTrack = newAutoPanTrack;
250 this.autoPanOnNextPaintComponent = true;
251
252 if (null != this.autoPanId && null != OtsAnimationPanel.this.animationPanel && this.autoPanId.length() > 0
253 && null != this.autoPanKind)
254 {
255 OtsAnimationPanel.this.animationPanel.repaint();
256 }
257 }
258
259
260
261
262
263
264
265
266
267
268 private JButton makeButton(final String name, final String iconPath, final String actionCommand, final String toolTipText,
269 final boolean enabled)
270 {
271
272 JButton result = new JButton(OtsControlPanel.loadIcon(iconPath));
273 result.setPreferredSize(new Dimension(34, 32));
274 result.setName(name);
275 result.setEnabled(enabled);
276 result.setActionCommand(actionCommand);
277 result.setToolTipText(toolTipText);
278 result.addActionListener(this);
279 this.buttons.add(result);
280 return result;
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294 public final void addToggleAnimationButtonIcon(final String name, final Class<? extends Locatable> locatableClass,
295 final String iconPath, final String toolTipText, final boolean initiallyVisible, final boolean idButton)
296 {
297 JToggleButton button;
298 Icon icon = OtsControlPanel.loadIcon(iconPath);
299 Icon unIcon = OtsControlPanel.loadGrayscaleIcon(iconPath);
300 button = new JCheckBox();
301 button.setSelectedIcon(icon);
302 button.setIcon(unIcon);
303 button.setPreferredSize(new Dimension(32, 28));
304 button.setName(name);
305 button.setEnabled(true);
306 button.setSelected(initiallyVisible);
307 button.setActionCommand(name);
308 button.setToolTipText(toolTipText);
309 button.addActionListener(this);
310
311
312 if (idButton && this.togglePanel.getComponentCount() > 0)
313 {
314 JPanel lastToggleBox = (JPanel) this.togglePanel.getComponent(this.togglePanel.getComponentCount() - 1);
315 lastToggleBox.add(button);
316 }
317 else
318 {
319 JPanel toggleBox = new JPanel();
320 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
321 toggleBox.add(button);
322 this.togglePanel.add(toggleBox);
323 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
324 }
325
326 if (initiallyVisible)
327 {
328 this.animationPanel.showClass(locatableClass);
329 }
330 else
331 {
332 this.animationPanel.hideClass(locatableClass);
333 }
334 this.toggleLocatableMap.put(name, locatableClass);
335 this.toggleButtons.put(locatableClass, button);
336 }
337
338
339
340
341
342
343
344
345 public final void addToggleAnimationButtonText(final String name, final Class<? extends Locatable> locatableClass,
346 final String toolTipText, final boolean initiallyVisible)
347 {
348 JToggleButton button;
349 button = new JCheckBox(name);
350 button.setName(name);
351 button.setEnabled(true);
352 button.setSelected(initiallyVisible);
353 button.setActionCommand(name);
354 button.setToolTipText(toolTipText);
355 button.addActionListener(this);
356
357 JPanel toggleBox = new JPanel();
358 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
359 toggleBox.add(button);
360 this.togglePanel.add(toggleBox);
361 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
362
363 if (initiallyVisible)
364 {
365 this.animationPanel.showClass(locatableClass);
366 }
367 else
368 {
369 this.animationPanel.hideClass(locatableClass);
370 }
371 this.toggleLocatableMap.put(name, locatableClass);
372 this.toggleButtons.put(locatableClass, button);
373 }
374
375
376
377
378
379 public final void addToggleText(final String text)
380 {
381 JPanel textBox = new JPanel();
382 textBox.setLayout(new BoxLayout(textBox, BoxLayout.X_AXIS));
383 textBox.add(new JLabel(text));
384 this.togglePanel.add(textBox);
385 textBox.setAlignmentX(Component.LEFT_ALIGNMENT);
386 }
387
388
389
390
391
392
393
394 public final void addAllToggleGISButtonText(final String header, final GisRenderable2d gisMap, final String toolTipText)
395 {
396 addToggleText(" ");
397 addToggleText(header);
398 try
399 {
400 for (String layerName : gisMap.getMap().getLayerMap().keySet())
401 {
402 addToggleGISButtonText(layerName, layerName, gisMap, toolTipText);
403 }
404 }
405 catch (RemoteException exception)
406 {
407 exception.printStackTrace();
408 }
409 }
410
411
412
413
414
415
416
417
418 public final void addToggleGISButtonText(final String layerName, final String displayName, final GisRenderable2d gisMap,
419 final String toolTipText)
420 {
421 JToggleButton button;
422 button = new JCheckBox(displayName);
423 button.setName(layerName);
424 button.setEnabled(true);
425 button.setSelected(true);
426 button.setActionCommand(layerName);
427 button.setToolTipText(toolTipText);
428 button.addActionListener(this);
429
430 JPanel toggleBox = new JPanel();
431 toggleBox.setLayout(new BoxLayout(toggleBox, BoxLayout.X_AXIS));
432 toggleBox.add(button);
433 this.togglePanel.add(toggleBox);
434 toggleBox.setAlignmentX(Component.LEFT_ALIGNMENT);
435
436 this.toggleGISMap.put(layerName, gisMap.getMap());
437 this.toggleGISButtons.put(layerName, button);
438 }
439
440
441
442
443
444 public final void showGISLayer(final String layerName)
445 {
446 GisMapInterface gisMap = this.toggleGISMap.get(layerName);
447 if (gisMap != null)
448 {
449 try
450 {
451 gisMap.showLayer(layerName);
452 this.toggleGISButtons.get(layerName).setSelected(true);
453 this.animationPanel.repaint();
454 }
455 catch (RemoteException exception)
456 {
457 exception.printStackTrace();
458 }
459 }
460 }
461
462
463
464
465
466 public final void hideGISLayer(final String layerName)
467 {
468 GisMapInterface gisMap = this.toggleGISMap.get(layerName);
469 if (gisMap != null)
470 {
471 try
472 {
473 gisMap.hideLayer(layerName);
474 this.toggleGISButtons.get(layerName).setSelected(false);
475 this.animationPanel.repaint();
476 }
477 catch (RemoteException exception)
478 {
479 exception.printStackTrace();
480 }
481 }
482 }
483
484
485
486
487
488 public final void toggleGISLayer(final String layerName)
489 {
490 GisMapInterface gisMap = this.toggleGISMap.get(layerName);
491 if (gisMap != null)
492 {
493 try
494 {
495 if (gisMap.getVisibleLayers().contains(gisMap.getLayerMap().get(layerName)))
496 {
497 gisMap.hideLayer(layerName);
498 this.toggleGISButtons.get(layerName).setSelected(false);
499 }
500 else
501 {
502 gisMap.showLayer(layerName);
503 this.toggleGISButtons.get(layerName).setSelected(true);
504 }
505 this.animationPanel.repaint();
506 }
507 catch (RemoteException exception)
508 {
509 exception.printStackTrace();
510 }
511 }
512 }
513
514 @Override
515 public final void actionPerformed(final ActionEvent actionEvent)
516 {
517 String actionCommand = actionEvent.getActionCommand();
518
519 try
520 {
521 if (actionCommand.equals("Home"))
522 {
523 this.animationPanel.home();
524 }
525 if (actionCommand.equals("ZoomAll"))
526 {
527 this.animationPanel.zoomAll();
528 }
529 if (actionCommand.equals("Grid"))
530 {
531 this.animationPanel.showGrid(!this.animationPanel.isShowGrid());
532 }
533
534 if (this.toggleLocatableMap.containsKey(actionCommand))
535 {
536 Class<? extends Locatable> locatableClass = this.toggleLocatableMap.get(actionCommand);
537 this.animationPanel.toggleClass(locatableClass);
538 this.togglePanel.repaint();
539 }
540
541 if (this.toggleGISMap.containsKey(actionCommand))
542 {
543 this.toggleGISLayer(actionCommand);
544 this.togglePanel.repaint();
545 }
546 }
547 catch (Exception exception)
548 {
549 exception.printStackTrace();
550 }
551 }
552
553
554
555
556
557 public final AnimationPanel getAnimationPanel()
558 {
559 return this.animationPanel;
560 }
561
562
563
564
565
566
567 public void createDemoPanel(final DemoPanelPosition position)
568 {
569 Throw.when(this.demoPanel != null, IllegalStateException.class,
570 "Attempt to create demo panel, but it's already created");
571 Throw.whenNull(position, "Position may not be null.");
572 Container parent = this.animationPanel.getParent();
573 parent.remove(this.animationPanel);
574
575 JPanel splitPanel = new JPanel(new BorderLayout());
576 parent.add(splitPanel);
577 splitPanel.add(this.animationPanel, BorderLayout.CENTER);
578
579 this.demoPanel = new JPanel();
580 this.demoPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
581 splitPanel.add(this.demoPanel, position.getBorderLayoutPosition());
582 }
583
584
585
586
587
588 public JPanel getDemoPanel()
589 {
590 if (this.demoPanel == null)
591 {
592 createDemoPanel(DemoPanelPosition.RIGHT);
593
594
595
596
597
598 this.demoPanel.addContainerListener(new ContainerListener()
599 {
600 @Override
601 public void componentAdded(final ContainerEvent e)
602 {
603 try
604 {
605
606 }
607 catch (NullPointerException exception)
608 {
609
610 }
611 }
612
613 @Override
614 public void componentRemoved(final ContainerEvent e)
615 {
616
617 }
618 });
619 }
620 return this.demoPanel;
621 }
622
623
624
625
626
627 public final void updateAnimationClassCheckBox(final Class<? extends Locatable> locatableClass)
628 {
629 JToggleButton button = this.toggleButtons.get(locatableClass);
630 if (button == null)
631 {
632 return;
633 }
634 button.setSelected(getAnimationPanel().isShowClass(locatableClass));
635 }
636
637
638
639
640 protected final void updateWorldCoordinate()
641 {
642 String worldPoint = "(x=" + FORMATTER.format(this.animationPanel.getWorldCoordinate().getX()) + " ; y="
643 + FORMATTER.format(this.animationPanel.getWorldCoordinate().getY()) + ")";
644 this.coordinateField.setText("Mouse: " + worldPoint);
645 int requiredWidth = this.coordinateField.getGraphics().getFontMetrics().stringWidth(this.coordinateField.getText());
646 if (this.coordinateField.getPreferredSize().width < requiredWidth)
647 {
648 Dimension requiredSize = new Dimension(requiredWidth, this.coordinateField.getPreferredSize().height);
649 this.coordinateField.setPreferredSize(requiredSize);
650 this.coordinateField.setMinimumSize(requiredSize);
651 Container parent = this.coordinateField.getParent();
652 parent.setPreferredSize(requiredSize);
653 parent.setMinimumSize(requiredSize);
654
655 parent.revalidate();
656 }
657 this.coordinateField.repaint();
658 }
659
660
661
662
663
664
665 public final ColorControlPanel getColorControlPanel()
666 {
667 return this.colorControlPanel;
668 }
669
670
671
672
673 public final void installWindowCloseHandler()
674 {
675 if (this.closeHandlerRegistered)
676 {
677 return;
678 }
679
680
681 new DisposeOnCloseThread(this).start();
682 }
683
684
685 protected class DisposeOnCloseThread extends Thread
686 {
687
688 private OtsAnimationPanel panel;
689
690
691
692
693 public DisposeOnCloseThread(final OtsAnimationPanel panel)
694 {
695 this.panel = panel;
696 }
697
698 @Override
699 public final void run()
700 {
701 Container root = this.panel;
702 while (!(root instanceof JFrame))
703 {
704 try
705 {
706 Thread.sleep(10);
707 }
708 catch (InterruptedException exception)
709 {
710
711 }
712
713
714 root = this.panel;
715 while (null != root.getParent() && !(root instanceof JFrame))
716 {
717 root = root.getParent();
718 }
719 }
720 JFrame frame = (JFrame) root;
721 frame.addWindowListener(this.panel);
722 this.panel.closeHandlerRegistered = true;
723 }
724
725 @Override
726 public final String toString()
727 {
728 return "DisposeOnCloseThread of OtsAnimationPanel [panel=" + this.panel + "]";
729 }
730 }
731
732 @Override
733 public void windowOpened(final WindowEvent e)
734 {
735
736 }
737
738 @Override
739 public final void windowClosing(final WindowEvent e)
740 {
741
742 }
743
744 @Override
745 public final void windowClosed(final WindowEvent e)
746 {
747 this.windowExited = true;
748 }
749
750 @Override
751 public final void windowIconified(final WindowEvent e)
752 {
753
754 }
755
756 @Override
757 public final void windowDeiconified(final WindowEvent e)
758 {
759
760 }
761
762 @Override
763 public final void windowActivated(final WindowEvent e)
764 {
765
766 }
767
768 @Override
769 public final void windowDeactivated(final WindowEvent e)
770 {
771
772 }
773
774 @Override
775 public void notify(final Event event) throws RemoteException
776 {
777 if (event.getType().equals(Network.GTU_ADD_EVENT))
778 {
779 this.gtuCount++;
780 setGtuCountText();
781 }
782 else if (event.getType().equals(Network.GTU_REMOVE_EVENT))
783 {
784 this.gtuCount--;
785 setGtuCountText();
786 }
787 }
788
789
790
791
792 private void setGtuCountText()
793 {
794 this.gtuCountField.setText(this.gtuCount + " GTU's");
795 }
796
797
798
799
800 protected class UpdateTimer extends Thread
801 {
802 @Override
803 public final void run()
804 {
805 while (!OtsAnimationPanel.this.windowExited)
806 {
807 if (OtsAnimationPanel.this.isShowing())
808 {
809 OtsAnimationPanel.this.updateWorldCoordinate();
810 }
811 try
812 {
813 Thread.sleep(50);
814 }
815 catch (InterruptedException exception)
816 {
817
818 }
819 }
820 }
821
822 @Override
823 public final String toString()
824 {
825 return "UpdateTimer thread for OtsAnimationPanel";
826 }
827
828 }
829
830
831
832
833
834
835
836
837
838
839
840
841 private class AutoAnimationPanel extends AnimationPanel
842 {
843
844
845 private static final long serialVersionUID = 20180430L;
846
847
848 private final Network network;
849
850
851 private Gtu lastGtu;
852
853
854
855
856
857
858
859
860
861
862 AutoAnimationPanel(final Rectangle2D extent, final Dimension size, final OtsSimulatorInterface simulator,
863 final Network network) throws RemoteException, DsolException
864 {
865 super(new Bounds2d(extent.getMinX(), extent.getMaxX(), extent.getMinY(), extent.getMaxY()), simulator);
866 this.network = network;
867 MouseListener[] listeners = getMouseListeners();
868 for (MouseListener listener : listeners)
869 {
870 removeMouseListener(listener);
871 }
872 this.addMouseListener(new MouseAdapter()
873 {
874 @SuppressWarnings("synthetic-access")
875 @Override
876 public void mouseClicked(final MouseEvent e)
877 {
878 if (e.isControlDown())
879 {
880 Gtu gtu = getSelectedGTU(e.getPoint());
881 if (gtu != null)
882 {
883 getOtsControlPanel().getOtsSearchPanel().selectAndTrackObject("GTU", gtu.getId(), true);
884 e.consume();
885 }
886 }
887 e.consume();
888 }
889 });
890 for (MouseListener listener : listeners)
891 {
892 addMouseListener(listener);
893 }
894
895 MouseWheelListener[] wheelListeners = getMouseWheelListeners();
896 for (MouseWheelListener wheelListener : wheelListeners)
897 {
898 removeMouseWheelListener(wheelListener);
899 }
900 this.addMouseWheelListener(new InputListener(this)
901 {
902 @Override
903 public void mouseWheelMoved(final MouseWheelEvent e)
904 {
905 if (e.isShiftDown())
906 {
907 int amount = e.getUnitsToScroll();
908 if (amount > 0)
909 {
910 zoomVertical(VisualizationPanel.ZOOMFACTOR, e.getX(), e.getY());
911 }
912 else
913 {
914 zoomVertical(1.0 / VisualizationPanel.ZOOMFACTOR, e.getX(), e.getY());
915 }
916 }
917 else if (e.isAltDown())
918 {
919 int amount = e.getUnitsToScroll();
920 if (amount > 0)
921 {
922 zoomHorizontal(VisualizationPanel.ZOOMFACTOR, e.getX(), e.getY());
923 }
924 else
925 {
926 zoomHorizontal(1.0 / VisualizationPanel.ZOOMFACTOR, e.getX(), e.getY());
927 }
928 }
929 else
930 {
931 super.mouseWheelMoved(e);
932 }
933 }
934 });
935 }
936
937
938
939
940
941
942
943 final synchronized void zoomVertical(final double factor, final int mouseX, final int mouseY)
944 {
945 double minX = getExtent().getMinX();
946 Point2d mwc =
947 getRenderableScale().getWorldCoordinates(new Point2D.Double(mouseX, mouseY), getExtent(), this.getSize());
948 double minY = mwc.getY() - (mwc.getY() - getExtent().getMinY()) * factor;
949 double w = getExtent().getDeltaX();
950 double h = getExtent().getDeltaY() * factor;
951 setExtent(new Bounds2d(minX, minX + w, minY, minY + h));
952 }
953
954
955
956
957
958
959
960 final synchronized void zoomHorizontal(final double factor, final int mouseX, final int mouseY)
961 {
962 double minY = getExtent().getMinY();
963 Point2d mwc =
964 getRenderableScale().getWorldCoordinates(new Point2D.Double(mouseX, mouseY), getExtent(), this.getSize());
965 double minX = mwc.getX() - (mwc.getX() - getExtent().getMinX()) * factor;
966 double w = getExtent().getDeltaX() * factor;
967 double h = getExtent().getDeltaY();
968 setExtent(new Bounds2d(minX, minX + w, minY, minY + h));
969 }
970
971
972
973
974
975
976 @SuppressWarnings("synthetic-access")
977 protected Gtu getSelectedGTU(final Point2D mousePoint)
978 {
979 List<Gtu> targets = new ArrayList<>();
980 Point2d point = getRenderableScale().getWorldCoordinates(mousePoint, getExtent(), getSize());
981 for (Renderable2dInterface<?> renderable : getElements())
982 {
983 if (isShowElement(renderable) && renderable.contains(point, getExtent()))
984 {
985 if (renderable.getSource() instanceof Gtu)
986 {
987 targets.add((Gtu) renderable.getSource());
988 }
989 }
990 }
991 if (targets.size() == 1)
992 {
993 return targets.get(0);
994 }
995 return null;
996 }
997
998 @SuppressWarnings("synthetic-access")
999 @Override
1000 public void paintComponent(final Graphics g)
1001 {
1002 final OtsSearchPanel.ObjectKind<?> panKind = OtsAnimationPanel.this.autoPanKind;
1003 final String panId = OtsAnimationPanel.this.autoPanId;
1004 final boolean doPan = OtsAnimationPanel.this.autoPanOnNextPaintComponent;
1005 OtsAnimationPanel.this.autoPanOnNextPaintComponent = OtsAnimationPanel.this.autoPanTrack;
1006 if (doPan && panKind != null && panId != null)
1007 {
1008 Locatable locatable = panKind.searchNetwork(this.network, panId);
1009 if (null != locatable)
1010 {
1011 try
1012 {
1013 Point<?> point = locatable.getLocation();
1014 if (point != null)
1015 {
1016 double w = getExtent().getDeltaX();
1017 double h = getExtent().getDeltaY();
1018 setExtent(new Bounds2d(point.getX() - w / 2, point.getX() + w / 2, point.getY() - h / 2,
1019 point.getY() + h / 2));
1020 }
1021 }
1022 catch (RemoteException exception)
1023 {
1024 getSimulator().getLogger().always().warn(
1025 "Caught RemoteException trying to locate {} with id {} in network {}.", panKind, panId,
1026 this.network.getId());
1027 return;
1028 }
1029 }
1030 }
1031 super.paintComponent(g);
1032 }
1033
1034 @Override
1035 public String toString()
1036 {
1037 return "AutoAnimationPanel [network=" + this.network + ", lastGtu=" + this.lastGtu + "]";
1038 }
1039 }
1040
1041
1042
1043
1044
1045 public enum DemoPanelPosition
1046 {
1047
1048 TOP("First"),
1049
1050
1051 BOTTOM("Last"),
1052
1053
1054 LEFT("Before"),
1055
1056
1057 RIGHT("After");
1058
1059
1060 private final String direction;
1061
1062
1063
1064
1065 DemoPanelPosition(final String direction)
1066 {
1067 this.direction = direction;
1068 }
1069
1070
1071
1072
1073 public String getBorderLayoutPosition()
1074 {
1075 return this.direction;
1076 }
1077 }
1078
1079 }