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