1 package nl.tudelft.simulation.dsol.swing.animation.d2;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Graphics;
6 import java.awt.Graphics2D;
7 import java.awt.event.MouseEvent;
8 import java.awt.geom.Point2D;
9 import java.awt.geom.RectangularShape;
10 import java.rmi.RemoteException;
11 import java.text.NumberFormat;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.Iterator;
15 import java.util.LinkedHashMap;
16 import java.util.LinkedHashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.SortedSet;
21 import java.util.TreeSet;
22
23 import javax.naming.NamingException;
24 import javax.swing.JPanel;
25 import javax.swing.JPopupMenu;
26 import javax.swing.JSeparator;
27
28 import org.djutils.draw.bounds.Bounds;
29 import org.djutils.draw.bounds.Bounds2d;
30 import org.djutils.draw.point.Point;
31 import org.djutils.draw.point.Point2d;
32 import org.djutils.event.Event;
33 import org.djutils.event.EventListener;
34 import org.djutils.event.EventListenerMap;
35 import org.djutils.event.EventProducer;
36 import org.djutils.event.EventType;
37 import org.djutils.event.LocalEventProducer;
38 import org.djutils.event.reference.ReferenceType;
39 import org.djutils.logger.CategoryLogger;
40 import org.djutils.metadata.MetaData;
41 import org.djutils.metadata.ObjectDescriptor;
42
43 import nl.tudelft.simulation.dsol.animation.Locatable;
44 import nl.tudelft.simulation.dsol.animation.d2.Renderable2dComparator;
45 import nl.tudelft.simulation.dsol.animation.d2.Renderable2dInterface;
46 import nl.tudelft.simulation.dsol.animation.d2.RenderableScale;
47 import nl.tudelft.simulation.dsol.simulators.AnimatorInterface;
48 import nl.tudelft.simulation.dsol.swing.animation.d2.actions.IntrospectionAction;
49 import nl.tudelft.simulation.naming.context.ContextInterface;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public class VisualizationPanel extends JPanel implements EventProducer, EventListener
87 {
88
89 private static final long serialVersionUID = 20230305L;
90
91
92 public static final int UP = 1;
93
94
95 public static final int DOWN = 2;
96
97
98 public static final int LEFT = 3;
99
100
101 public static final int RIGHT = 4;
102
103
104 public static final double ZOOMFACTOR = 1.2;
105
106
107 protected static final Color GRIDCOLOR = Color.BLACK;
108
109
110 @SuppressWarnings("checkstyle:visibilitymodifier")
111 private Bounds2d extent = null;
112
113
114 @SuppressWarnings("checkstyle:visibilitymodifier")
115 private Bounds2d homeExtent = null;
116
117
118 @SuppressWarnings("checkstyle:visibilitymodifier")
119 protected boolean showGrid = true;
120
121
122 @SuppressWarnings("checkstyle:visibilitymodifier")
123 protected double gridSizeX = 100.0;
124
125
126 @SuppressWarnings("checkstyle:visibilitymodifier")
127 protected double gridSizeY = 100.0;
128
129
130 @SuppressWarnings("checkstyle:visibilitymodifier")
131 protected NumberFormat formatter = NumberFormat.getInstance();
132
133
134 @SuppressWarnings("checkstyle:visibilitymodifier")
135 protected Dimension lastDimension = null;
136
137
138 @SuppressWarnings("checkstyle:visibilitymodifier")
139 protected Point2d worldCoordinate = new Point2d(0.0, 0.0);
140
141
142 @SuppressWarnings("checkstyle:visibilitymodifier")
143 protected boolean showToolTip = true;
144
145
146 private RenderableScale renderableScale;
147
148
149 @SuppressWarnings("checkstyle:visibilitymodifier")
150 protected SortedSet<Renderable2dInterface<? extends Locatable>> elements =
151 new TreeSet<Renderable2dInterface<? extends Locatable>>(new Renderable2dComparator());
152
153
154 @SuppressWarnings("checkstyle:visibilitymodifier")
155 protected Map<Class<? extends Locatable>, Boolean> visibilityMap = Collections.synchronizedMap(new LinkedHashMap<>());
156
157
158 @SuppressWarnings("checkstyle:visibilitymodifier")
159 protected Set<Class<? extends Locatable>> hiddenClasses = new LinkedHashSet<>();
160
161
162 @SuppressWarnings("checkstyle:visibilitymodifier")
163 protected Set<Class<? extends Locatable>> shownClasses = new LinkedHashSet<>();
164
165
166 @SuppressWarnings("checkstyle:visibilitymodifier")
167 protected ContextInterface context = null;
168
169
170 private int[] dragLine = new int[4];
171
172
173 private boolean dragLineEnabled = false;
174
175
176 @SuppressWarnings("checkstyle:visibilitymodifier")
177 protected List<Renderable2dInterface<? extends Locatable>> elementList = new ArrayList<>();
178
179
180 private boolean dirty = false;
181
182
183 private final AnimationEventProducer animationEventProducer;
184
185
186 public static final double EXTENT_MARGIN_FACTOR = 0.05;
187
188
189 public static final EventType ANIMATION_MOUSE_CLICK_EVENT = new EventType(new MetaData("ANIMATION_MOUSE_CLICK_EVENT",
190 "ANIMATION_MOUSE_CLICK_EVENT",
191 new ObjectDescriptor("worldCoordinate", "x and y position in world coordinates", Point2d.class),
192 new ObjectDescriptor("screenCoordinate", "x and y position in screen coordinates", java.awt.Point.class),
193 new ObjectDescriptor("shiftCtrlAlt", "shift[0], ctrl[1], and/or alt[2] pressed", boolean[].class),
194 new ObjectDescriptor("objectList", "List of objects whose bounding box includes the coordinate", List.class)));
195
196
197 public static final EventType ANIMATION_MOUSE_POPUP_EVENT = new EventType(new MetaData("ANIMATION_MOUSE_POPUP_EVENT",
198 "ANIMATION_MOUSE_POPUP_EVENT",
199 new ObjectDescriptor("worldCoordinate", "x and y position in world coordinates", Point2d.class),
200 new ObjectDescriptor("screenCoordinate", "x and y position in screen coordinates", java.awt.Point.class),
201 new ObjectDescriptor("shiftCtrlAlt", "shift[0], ctrl[1], and/or alt[2] pressed", boolean[].class),
202 new ObjectDescriptor("object", "Selected object whose bounding box includes the coordinate", Object.class)));
203
204
205
206
207
208
209
210 public VisualizationPanel(final Bounds2d homeExtent, final EventProducer producer) throws RemoteException
211 {
212 setPreferredSize(new Dimension(1024, 768));
213 this.animationEventProducer = new AnimationEventProducer();
214 this.showGrid = true;
215 InputListener listener = new InputListener(this);
216 this.addMouseListener(listener);
217 this.addMouseMotionListener(listener);
218 this.addMouseWheelListener(listener);
219 this.addKeyListener(listener);
220 this.renderableScale = new RenderableScale();
221 this.homeExtent = homeExtent;
222 this.setBackground(Color.WHITE);
223 this.lastDimension = this.getSize();
224 setExtent(homeExtent);
225 producer.addListener(this, AnimatorInterface.UPDATE_ANIMATION_EVENT);
226 }
227
228
229
230
231
232
233
234
235
236 public VisualizationPanel(final Bounds2d homeExtent, final EventProducer producer, final ContextInterface context)
237 throws RemoteException, NamingException
238 {
239 this(homeExtent, producer);
240 this.context = context.createSubcontext("animation/2D");
241 subscribeToContext();
242 }
243
244
245 @Override
246 public void paintComponent(final Graphics g)
247 {
248 Graphics2D g2 = (Graphics2D) g;
249 super.paintComponent(g);
250
251
252 if (!this.getSize().equals(this.lastDimension))
253 {
254 this.lastDimension = this.getSize();
255 setExtent(this.renderableScale.computeVisibleExtent(this.extent, this.getSize()));
256 }
257 if (this.showGrid)
258 {
259 this.drawGrid(g);
260 }
261
262
263 if (this.dirty)
264 {
265 synchronized (this.elementList)
266 {
267 this.elementList.clear();
268 this.elementList.addAll(this.elements);
269 this.dirty = false;
270 }
271 }
272
273
274 for (Renderable2dInterface<? extends Locatable> element : this.elementList)
275 {
276
277 if (element.getSource() == null)
278 {
279 objectRemoved(element);
280 }
281 else if (isShowElement(element))
282 {
283 element.paintComponent(g2, this.getExtent(), this.getSize(), getRenderableScale(), this);
284 }
285 }
286
287
288 if (this.dragLineEnabled)
289 {
290 g.setColor(Color.BLACK);
291 g.drawLine(this.dragLine[0], this.dragLine[1], this.dragLine[2], this.dragLine[3]);
292 this.dragLineEnabled = false;
293 }
294 }
295
296
297
298
299
300
301 public boolean isShowElement(final Renderable2dInterface<? extends Locatable> element)
302 {
303 return element.getSource() == null ? false : isShowClass(element.getSource().getClass());
304 }
305
306
307
308
309
310
311
312 public boolean isShowClass(final Class<? extends Locatable> locatableClass)
313 {
314 if (this.hiddenClasses.contains(locatableClass))
315 {
316 return false;
317 }
318 else
319 {
320 boolean show = true;
321 if (!this.shownClasses.contains(locatableClass))
322 {
323 synchronized (this.visibilityMap)
324 {
325 for (Class<? extends Locatable> lc : this.visibilityMap.keySet())
326 {
327 if (lc.isAssignableFrom(locatableClass))
328 {
329 if (!this.visibilityMap.get(lc))
330 {
331 show = false;
332 }
333 }
334 }
335
336 if (show)
337 {
338 this.shownClasses.add(locatableClass);
339 }
340 else
341 {
342 this.hiddenClasses.add(locatableClass);
343 }
344 }
345 }
346 return show;
347 }
348 }
349
350
351
352
353
354 @SuppressWarnings("unchecked")
355 protected void subscribeToContext() throws RemoteException
356 {
357 this.context.addListener(this, ContextInterface.OBJECT_ADDED_EVENT);
358 this.context.addListener(this, ContextInterface.OBJECT_REMOVED_EVENT);
359 for (Object element : this.context.values())
360 {
361 if (element instanceof Renderable2dInterface)
362 {
363 objectAdded((Renderable2dInterface<? extends Locatable>) element);
364 }
365 else
366 {
367 System.err.println("odd object in context: " + element);
368 }
369 }
370 this.repaint();
371 }
372
373
374 @SuppressWarnings("unchecked")
375 @Override
376 public void notify(final Event event) throws RemoteException
377 {
378 if (event.getType().equals(AnimatorInterface.UPDATE_ANIMATION_EVENT) && this.isShowing())
379 {
380 if (this.getWidth() > 0 || this.getHeight() > 0)
381 {
382 this.repaint();
383 }
384 return;
385 }
386
387 else if (event.getType().equals(ContextInterface.OBJECT_ADDED_EVENT))
388 {
389 objectAdded((Renderable2dInterface<? extends Locatable>) ((Object[]) event.getContent())[2]);
390 }
391
392 else if (event.getType().equals(ContextInterface.OBJECT_REMOVED_EVENT))
393 {
394 objectRemoved((Renderable2dInterface<? extends Locatable>) ((Object[]) event.getContent())[2]);
395 }
396 }
397
398
399
400
401
402 public Bounds2d getExtent()
403 {
404 return this.extent;
405 }
406
407
408
409
410
411 public void setExtent(final Bounds2d extent)
412 {
413 this.extent = extent;
414 this.repaint();
415 }
416
417
418
419
420
421 public synchronized void showGrid(final boolean bool)
422 {
423 this.showGrid = bool;
424 this.repaint();
425 }
426
427
428
429
430
431 public synchronized void setWorldCoordinate(final Point2d point)
432 {
433 this.worldCoordinate = point;
434 }
435
436
437
438
439 public synchronized Point2d getWorldCoordinate()
440 {
441 return this.worldCoordinate;
442 }
443
444
445
446
447 public synchronized void displayWorldCoordinateToolTip()
448 {
449 if (this.showToolTip)
450 {
451 String worldPoint = "(x=" + this.formatter.format(this.worldCoordinate.getX()) + " ; y="
452 + this.formatter.format(this.worldCoordinate.getY()) + ")";
453 setToolTipText(worldPoint);
454 }
455 }
456
457
458
459
460 public synchronized boolean isShowToolTip()
461 {
462 return this.showToolTip;
463 }
464
465
466
467
468 public synchronized void setShowToolTip(final boolean showToolTip)
469 {
470 this.showToolTip = showToolTip;
471 }
472
473
474
475
476
477
478 public synchronized void pan(final int direction, final double percentage)
479 {
480 if (percentage <= 0 || percentage > 1.0)
481 {
482 throw new IllegalArgumentException("percentage<=0 || >1.0");
483 }
484 switch (direction)
485 {
486 case LEFT:
487 setExtent(new Bounds2d(this.extent.getMinX() - percentage * this.extent.getDeltaX(),
488 this.extent.getMaxX() - percentage * this.extent.getDeltaX(), this.extent.getMinY(),
489 this.extent.getMaxY()));
490 break;
491 case RIGHT:
492 setExtent(new Bounds2d(this.extent.getMinX() + percentage * this.extent.getDeltaX(),
493 this.extent.getMaxX() + percentage * this.extent.getDeltaX(), this.extent.getMinY(),
494 this.extent.getMaxY()));
495 break;
496 case UP:
497 setExtent(new Bounds2d(this.extent.getMinX(), this.extent.getMaxX(),
498 this.extent.getMinY() + percentage * this.extent.getDeltaY(),
499 this.extent.getMaxY() + percentage * this.extent.getDeltaY()));
500 break;
501 case DOWN:
502 setExtent(new Bounds2d(this.extent.getMinX(), this.extent.getMaxX(),
503 this.extent.getMinY() - percentage * this.extent.getDeltaY(),
504 this.extent.getMaxY() - percentage * this.extent.getDeltaY()));
505 break;
506 default:
507 throw new IllegalArgumentException("direction unkown");
508 }
509 }
510
511
512
513
514 public synchronized void home()
515 {
516 setExtent(this.renderableScale.computeVisibleExtent(this.homeExtent, this.getSize()));
517 }
518
519
520
521
522 public boolean isShowGrid()
523 {
524 return this.showGrid;
525 }
526
527
528
529
530 public void setShowGrid(final boolean showGrid)
531 {
532 this.showGrid = showGrid;
533 }
534
535
536
537
538
539 public synchronized void zoom(final double factor)
540 {
541 zoom(factor, (int) (this.getWidth() / 2.0), (int) (this.getHeight() / 2.0));
542 }
543
544
545
546
547
548
549
550 public synchronized void zoom(final double factor, final int mouseX, final int mouseY)
551 {
552 Point2d mwc = this.renderableScale.getWorldCoordinates(new Point2D.Double(mouseX, mouseY), this.extent, this.getSize());
553 double minX = mwc.getX() - (mwc.getX() - this.extent.getMinX()) * factor;
554 double minY = mwc.getY() - (mwc.getY() - this.extent.getMinY()) * factor;
555 double w = this.extent.getDeltaX() * factor;
556 double h = this.extent.getDeltaY() * factor;
557
558 setExtent(new Bounds2d(minX, minX + w, minY, minY + h));
559 }
560
561
562
563
564
565
566 protected synchronized void drawGrid(final Graphics g)
567 {
568
569 g.setFont(g.getFont().deriveFont(11.0f));
570 g.setColor(GRIDCOLOR);
571 double scaleX = this.renderableScale.getXScale(this.extent, this.getSize());
572 double scaleY = this.renderableScale.getYScale(this.extent, this.getSize());
573
574 int count = 0;
575 int gridSizePixelsX = (int) Math.round(this.gridSizeX / scaleX);
576 while (gridSizePixelsX < 40)
577 {
578 this.gridSizeX = 10 * this.gridSizeX;
579 int maximumNumberOfDigits = (int) Math.max(0, 1 + Math.ceil(Math.log(1 / this.gridSizeX) / Math.log(10)));
580 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
581 gridSizePixelsX = (int) Math.round(this.gridSizeX / scaleX);
582 if (count++ > 10)
583 {
584 break;
585 }
586 }
587
588 count = 0;
589 while (gridSizePixelsX > 10 * 40)
590 {
591 int maximumNumberOfDigits = (int) Math.max(0, 2 + Math.ceil(Math.log(1 / this.gridSizeX) / Math.log(10)));
592 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
593 this.gridSizeX = this.gridSizeX / 10;
594 gridSizePixelsX = (int) Math.round(this.gridSizeX / scaleX);
595 if (count++ > 10)
596 {
597 break;
598 }
599 }
600
601 int gridSizePixelsY = (int) Math.round(this.gridSizeY / scaleY);
602 while (gridSizePixelsY < 40)
603 {
604 this.gridSizeY = 10 * this.gridSizeY;
605 int maximumNumberOfDigits = (int) Math.max(0, 1 + Math.ceil(Math.log(1 / this.gridSizeY) / Math.log(10)));
606 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
607 gridSizePixelsY = (int) Math.round(this.gridSizeY / scaleY);
608 if (count++ > 10)
609 {
610 break;
611 }
612 }
613
614 count = 0;
615 while (gridSizePixelsY > 10 * 40)
616 {
617 int maximumNumberOfDigits = (int) Math.max(0, 2 + Math.ceil(Math.log(1 / this.gridSizeY) / Math.log(10)));
618 this.formatter.setMaximumFractionDigits(maximumNumberOfDigits);
619 this.gridSizeY = this.gridSizeY / 10;
620 gridSizePixelsY = (int) Math.round(this.gridSizeY / scaleY);
621 if (count++ > 10)
622 {
623 break;
624 }
625 }
626
627
628 double mod = this.extent.getMinX() % this.gridSizeX;
629 int x = (int) -Math.round(mod / scaleX);
630 while (x < this.getWidth())
631 {
632 Point2d point = this.renderableScale.getWorldCoordinates(new Point2D.Double(x, 0), this.extent, this.getSize());
633 if (point != null)
634 {
635 String label = this.formatter.format(Math.round(point.getX() / this.gridSizeX) * this.gridSizeX);
636 double labelWidth = this.getFontMetrics(this.getFont()).getStringBounds(label, g).getWidth();
637 if (x > labelWidth + 4)
638 {
639 g.drawLine(x, 15, x, this.getHeight());
640 g.drawString(label, (int) Math.round(x - 0.5 * labelWidth), 11);
641 }
642 }
643 x = x + gridSizePixelsX;
644 }
645
646
647 mod = Math.abs(this.extent.getMinY()) % this.gridSizeY;
648 int y = (int) Math.round(this.getSize().getHeight() - (mod / scaleY));
649 while (y > 15)
650 {
651 Point2d point = this.renderableScale.getWorldCoordinates(new Point2D.Double(0, y), this.extent, this.getSize());
652 if (point != null)
653 {
654 String label = this.formatter.format(Math.round(point.getY() / this.gridSizeY) * this.gridSizeY);
655 RectangularShape labelBounds = this.getFontMetrics(this.getFont()).getStringBounds(label, g);
656 g.drawLine((int) Math.round(labelBounds.getWidth() + 4), y, this.getWidth(), y);
657 g.drawString(label, 2, (int) Math.round(y + labelBounds.getHeight() * 0.3));
658 }
659 y = y - gridSizePixelsY;
660 }
661 }
662
663
664
665
666 public RenderableScale getRenderableScale()
667 {
668 return this.renderableScale;
669 }
670
671
672
673
674 public void setRenderableScale(final RenderableScale renderableScale)
675 {
676 this.renderableScale = renderableScale;
677 }
678
679
680
681
682
683 public void objectAdded(final Renderable2dInterface<? extends Locatable> element)
684 {
685 synchronized (this.elementList)
686 {
687 this.elements.add(element);
688 this.dirty = true;
689 }
690 }
691
692
693
694
695
696 public void objectRemoved(final Renderable2dInterface<? extends Locatable> element)
697 {
698 synchronized (this.elementList)
699 {
700 this.elements.remove(element);
701 this.dirty = true;
702 }
703 }
704
705
706
707
708
709 public synchronized Bounds2d fullExtent()
710 {
711 double minX = Double.MAX_VALUE;
712 double maxX = -Double.MAX_VALUE;
713 double minY = Double.MAX_VALUE;
714 double maxY = -Double.MAX_VALUE;
715 try
716 {
717 for (Renderable2dInterface<? extends Locatable> renderable : this.elementList)
718 {
719 if (renderable.getSource() == null)
720 {
721 continue;
722 }
723 Point<?> l = renderable.getSource().getLocation();
724 if (l != null)
725 {
726 Bounds<?, ?, ?> b = renderable.getSource().getBounds();
727 minX = Math.min(minX, l.getX() + b.getMinX());
728 minY = Math.min(minY, l.getY() + b.getMinY());
729 maxX = Math.max(maxX, l.getX() + b.getMaxX());
730 maxY = Math.max(maxY, l.getY() + b.getMaxY());
731 }
732 }
733 }
734 catch (Exception e)
735 {
736
737 }
738
739 minX -= EXTENT_MARGIN_FACTOR * Math.abs(maxX - minX);
740 minY -= EXTENT_MARGIN_FACTOR * Math.abs(maxY - minY);
741 maxX += EXTENT_MARGIN_FACTOR * Math.abs(maxX - minX);
742 maxY += EXTENT_MARGIN_FACTOR * Math.abs(maxY - minY);
743
744 return new Bounds2d(minX, maxX, minY, maxY);
745 }
746
747
748
749
750 public synchronized void zoomAll()
751 {
752 setExtent(getRenderableScale().computeVisibleExtent(fullExtent(), this.getSize()));
753 }
754
755
756
757
758
759 public void showClass(final Class<? extends Locatable> locatableClass)
760 {
761 synchronized (this.visibilityMap)
762 {
763 this.visibilityMap.put(locatableClass, true);
764 }
765 this.shownClasses.clear();
766 this.hiddenClasses.clear();
767 this.repaint();
768 }
769
770
771
772
773
774 public void hideClass(final Class<? extends Locatable> locatableClass)
775 {
776 synchronized (this.visibilityMap)
777 {
778 this.visibilityMap.put(locatableClass, false);
779 }
780 this.shownClasses.clear();
781 this.hiddenClasses.clear();
782 this.repaint();
783 }
784
785
786
787
788
789
790 public void toggleClass(final Class<? extends Locatable> locatableClass)
791 {
792 synchronized (this.visibilityMap)
793 {
794 if (!this.visibilityMap.containsKey(locatableClass))
795 {
796 showClass(locatableClass);
797 }
798 this.visibilityMap.put(locatableClass, !this.visibilityMap.get(locatableClass));
799 }
800 this.shownClasses.clear();
801 this.hiddenClasses.clear();
802 this.repaint();
803 }
804
805
806
807
808
809 public void mouseMoved(final java.awt.Point point)
810 {
811 Point2d world = getRenderableScale().getWorldCoordinates(point, getExtent(), getSize());
812 setWorldCoordinate(world);
813 displayWorldCoordinateToolTip();
814 }
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835 protected void pan(final Point2D mouseClickedPoint, final Point2D mouseReleasedPoint)
836 {
837
838 double dx = mouseReleasedPoint.getX() - mouseClickedPoint.getX();
839 double dy = mouseReleasedPoint.getY() - mouseClickedPoint.getY();
840 double scaleX = getRenderableScale().getXScale(getExtent(), getSize());
841 double scaleY = getRenderableScale().getYScale(getExtent(), getSize());
842 Bounds2d extent = getExtent();
843 setExtent(new Bounds2d(extent.getMinX() - dx * scaleX, extent.getMinX() - dx * scaleX + extent.getDeltaX(),
844 extent.getMinY() + dy * scaleY, extent.getMinY() + dy * scaleY + extent.getDeltaY()));
845 }
846
847
848
849
850
851
852 protected List<Locatable> getSelectedObjects(final Point2D mousePoint)
853 {
854 List<Locatable> targets = new ArrayList<Locatable>();
855 try
856 {
857 Point2d point = getRenderableScale().getWorldCoordinates(mousePoint, getExtent(), getSize());
858 for (Renderable2dInterface<?> renderable : getElements())
859 {
860 if (isShowElement(renderable) && renderable.contains(point, getExtent()))
861 {
862 targets.add(renderable.getSource());
863 }
864 }
865 }
866 catch (Exception exception)
867 {
868 CategoryLogger.always().warn(exception, "getSelectedObjects");
869 }
870 return targets;
871 }
872
873
874
875
876
877 protected void popup(final MouseEvent e)
878 {
879 List<Locatable> targets = this.getSelectedObjects(e.getPoint());
880 if (targets.size() > 0)
881 {
882 JPopupMenu popupMenu = new JPopupMenu();
883 popupMenu.add("Introspect");
884 popupMenu.add(new JSeparator());
885 for (Iterator<Locatable> i = targets.iterator(); i.hasNext();)
886 {
887 popupMenu.add(new IntrospectionAction(i.next()));
888 }
889 popupMenu.show(this, e.getX(), e.getY());
890 }
891 }
892
893
894
895
896
897
898 protected Object getSelectedObject(final List<Locatable> targets)
899 {
900 Object selectedObject = null;
901 try
902 {
903 double zValue = -Double.MAX_VALUE;
904 for (Locatable next : targets)
905 {
906 double z = next.getZ();
907 if (z > zValue)
908 {
909 zValue = z;
910 selectedObject = next;
911 }
912 }
913 }
914 catch (RemoteException exception)
915 {
916 CategoryLogger.always().warn(exception, "getSelectedObject");
917 }
918 return selectedObject;
919 }
920
921
922
923
924
925
926 protected void setDragLine(final Point2D mousePosition, final Point2D mouseClicked)
927 {
928 if ((mousePosition != null) && (mouseClicked != null))
929 {
930 setDragLineEnabled(false);
931 this.dragLine = new int[4];
932 this.dragLine[0] = (int) mousePosition.getX();
933 this.dragLine[1] = (int) mousePosition.getY();
934 this.dragLine[2] = (int) mouseClicked.getX();
935 this.dragLine[3] = (int) mouseClicked.getY();
936 setDragLineEnabled(true);
937 }
938 }
939
940
941
942
943 public int[] getDragLine()
944 {
945 return this.dragLine;
946 }
947
948
949
950
951 public boolean isDragLineEnabled()
952 {
953 return this.dragLineEnabled;
954 }
955
956
957
958
959 public void setDragLineEnabled(final boolean dragLineEnabled)
960 {
961 this.dragLineEnabled = dragLineEnabled;
962 }
963
964
965
966
967 public SortedSet<Renderable2dInterface<? extends Locatable>> getElements()
968 {
969 return this.elements;
970 }
971
972
973
974
975
976
977
978
979
980
981
982 class AnimationEventProducer extends LocalEventProducer
983 {
984
985 private static final long serialVersionUID = 20210213L;
986
987
988 @Override
989 public void fireEvent(final Event event)
990 {
991 super.fireEvent(event);
992 }
993 }
994
995
996
997
998
999 public AnimationEventProducer getAnimationEventProducer()
1000 {
1001 return this.animationEventProducer;
1002 }
1003
1004
1005 @Override
1006 public boolean addListener(final EventListener listener, final EventType eventType)
1007 {
1008 return this.animationEventProducer.addListener(listener, eventType);
1009 }
1010
1011
1012 @Override
1013 public boolean addListener(final EventListener listener, final EventType eventType, final ReferenceType referenceType)
1014 {
1015 return this.animationEventProducer.addListener(listener, eventType, referenceType);
1016 }
1017
1018
1019 @Override
1020 public boolean addListener(final EventListener listener, final EventType eventType, final int position)
1021 {
1022 return this.animationEventProducer.addListener(listener, eventType, position);
1023 }
1024
1025
1026 @Override
1027 public boolean addListener(final EventListener listener, final EventType eventType, final int position,
1028 final ReferenceType referenceType)
1029 {
1030 return this.animationEventProducer.addListener(listener, eventType, position, referenceType);
1031 }
1032
1033
1034 @Override
1035 public boolean removeListener(final EventListener listener, final EventType eventType)
1036 {
1037 return this.animationEventProducer.removeListener(listener, eventType);
1038 }
1039
1040
1041 @Override
1042 public int removeAllListeners()
1043 {
1044 return this.animationEventProducer.removeAllListeners();
1045 }
1046
1047
1048 @Override
1049 public EventListenerMap getEventListenerMap() throws RemoteException
1050 {
1051 return this.animationEventProducer.getEventListenerMap();
1052 }
1053
1054 }