001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package plugins.kernel.roi.roi4d; 020 021import java.awt.Color; 022import java.awt.Graphics2D; 023import java.awt.Rectangle; 024import java.awt.event.KeyEvent; 025import java.awt.event.MouseEvent; 026import java.awt.event.MouseWheelEvent; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.Map; 030import java.util.Map.Entry; 031import java.util.TreeMap; 032import java.util.concurrent.Semaphore; 033 034import org.w3c.dom.Element; 035import org.w3c.dom.Node; 036 037import icy.canvas.IcyCanvas; 038import icy.canvas.IcyCanvas2D; 039import icy.canvas.IcyCanvas3D; 040import icy.painter.OverlayEvent; 041import icy.painter.OverlayListener; 042import icy.roi.BooleanMask2D; 043import icy.roi.ROI; 044import icy.roi.ROI3D; 045import icy.roi.ROI4D; 046import icy.roi.ROIEvent; 047import icy.roi.ROIListener; 048import icy.sequence.Sequence; 049import icy.system.IcyExceptionHandler; 050import icy.type.point.Point5D; 051import icy.type.rectangle.Rectangle3D; 052import icy.type.rectangle.Rectangle4D; 053import icy.util.XMLUtil; 054 055/** 056 * Abstract class defining a generic 4D ROI as a stack of individual 3D ROI slices. 057 * 058 * @author Alexandre Dufour 059 * @author Stephane Dallongeville 060 * @param <R> 061 * the type of 3D ROI for each slice of this 4D ROI 062 */ 063public class ROI4DStack<R extends ROI3D> extends ROI4D implements ROIListener, OverlayListener, Iterable<R> 064{ 065 public static final String PROPERTY_USECHILDCOLOR = "useChildColor"; 066 067 protected final TreeMap<Integer, R> slices = new TreeMap<Integer, R>(); 068 069 protected final Class<R> roiClass; 070 protected boolean useChildColor; 071 protected Semaphore modifyingSlice; 072 protected double translateT; 073 074 /** 075 * Creates a new 4D ROI based on the given 3D ROI type. 076 */ 077 public ROI4DStack(Class<R> roiClass) 078 { 079 super(); 080 081 this.roiClass = roiClass; 082 useChildColor = false; 083 modifyingSlice = new Semaphore(1); 084 translateT = 0d; 085 } 086 087 @Override 088 public String getDefaultName() 089 { 090 return "ROI3D stack"; 091 } 092 093 @Override 094 protected ROIPainter createPainter() 095 { 096 return new ROI4DStackPainter(); 097 } 098 099 /** 100 * Create a new empty 3D ROI slice. 101 */ 102 protected R createSlice() 103 { 104 try 105 { 106 return roiClass.newInstance(); 107 } 108 catch (Exception e) 109 { 110 IcyExceptionHandler.showErrorMessage(e, true, true); 111 return null; 112 } 113 } 114 115 /** 116 * Returns <code>true</code> if the ROI directly uses the 3D slice color draw property and <code>false</code> if it 117 * uses the global 4D ROI color draw property. 118 */ 119 public boolean getUseChildColor() 120 { 121 return useChildColor; 122 } 123 124 /** 125 * Set to <code>true</code> if you want to directly use the 3D slice color draw property and <code>false</code> to 126 * keep the global 4D ROI color draw property. 127 * 128 * @see #setColor(int, Color) 129 */ 130 public void setUseChildColor(boolean value) 131 { 132 if (useChildColor != value) 133 { 134 useChildColor = value; 135 propertyChanged(PROPERTY_USECHILDCOLOR); 136 // need to redraw it 137 getOverlay().painterChanged(); 138 } 139 } 140 141 /** 142 * Set the painter color for the specified ROI slice. 143 * 144 * @see #setUseChildColor(boolean) 145 */ 146 public void setColor(int t, Color value) 147 { 148 final ROI3D slice = getSlice(t); 149 150 modifyingSlice.acquireUninterruptibly(); 151 try 152 { 153 if (slice != null) 154 slice.setColor(value); 155 } 156 finally 157 { 158 modifyingSlice.release(); 159 } 160 } 161 162 @Override 163 public void setColor(Color value) 164 { 165 beginUpdate(); 166 try 167 { 168 super.setColor(value); 169 170 if (!getUseChildColor()) 171 { 172 modifyingSlice.acquireUninterruptibly(); 173 try 174 { 175 synchronized (slices) 176 { 177 for (R slice : slices.values()) 178 slice.setColor(value); 179 } 180 } 181 finally 182 { 183 modifyingSlice.release(); 184 } 185 } 186 } 187 finally 188 { 189 endUpdate(); 190 } 191 } 192 193 @Override 194 public void setOpacity(float value) 195 { 196 beginUpdate(); 197 try 198 { 199 super.setOpacity(value); 200 201 modifyingSlice.acquireUninterruptibly(); 202 try 203 { 204 synchronized (slices) 205 { 206 for (R slice : slices.values()) 207 slice.setOpacity(value); 208 } 209 } 210 finally 211 { 212 modifyingSlice.release(); 213 } 214 } 215 finally 216 { 217 endUpdate(); 218 } 219 } 220 221 @Override 222 public void setStroke(double value) 223 { 224 beginUpdate(); 225 try 226 { 227 super.setStroke(value); 228 229 modifyingSlice.acquireUninterruptibly(); 230 try 231 { 232 synchronized (slices) 233 { 234 for (R slice : slices.values()) 235 slice.setStroke(value); 236 } 237 } 238 finally 239 { 240 modifyingSlice.release(); 241 } 242 } 243 finally 244 { 245 endUpdate(); 246 } 247 } 248 249 @Override 250 public void setCreating(boolean value) 251 { 252 beginUpdate(); 253 try 254 { 255 super.setCreating(value); 256 257 modifyingSlice.acquireUninterruptibly(); 258 try 259 { 260 synchronized (slices) 261 { 262 for (R slice : slices.values()) 263 slice.setCreating(value); 264 } 265 } 266 finally 267 { 268 modifyingSlice.release(); 269 } 270 } 271 finally 272 { 273 endUpdate(); 274 } 275 } 276 277 @Override 278 public void setReadOnly(boolean value) 279 { 280 beginUpdate(); 281 try 282 { 283 super.setReadOnly(value); 284 285 modifyingSlice.acquireUninterruptibly(); 286 try 287 { 288 synchronized (slices) 289 { 290 for (R slice : slices.values()) 291 slice.setReadOnly(value); 292 } 293 } 294 finally 295 { 296 modifyingSlice.release(); 297 } 298 } 299 finally 300 { 301 endUpdate(); 302 } 303 } 304 305 @Override 306 public void setFocused(boolean value) 307 { 308 beginUpdate(); 309 try 310 { 311 super.setFocused(value); 312 313 modifyingSlice.acquireUninterruptibly(); 314 try 315 { 316 synchronized (slices) 317 { 318 for (R slice : slices.values()) 319 slice.setFocused(value); 320 } 321 } 322 finally 323 { 324 modifyingSlice.release(); 325 } 326 } 327 finally 328 { 329 endUpdate(); 330 } 331 } 332 333 @Override 334 public void setSelected(boolean value) 335 { 336 beginUpdate(); 337 try 338 { 339 super.setSelected(value); 340 341 modifyingSlice.acquireUninterruptibly(); 342 try 343 { 344 synchronized (slices) 345 { 346 for (R slice : slices.values()) 347 slice.setSelected(value); 348 } 349 } 350 finally 351 { 352 modifyingSlice.release(); 353 } 354 } 355 finally 356 { 357 endUpdate(); 358 } 359 } 360 361 @Override 362 public void setC(int value) 363 { 364 beginUpdate(); 365 try 366 { 367 super.setC(value); 368 369 modifyingSlice.acquireUninterruptibly(); 370 try 371 { 372 synchronized (slices) 373 { 374 for (R slice : slices.values()) 375 slice.setC(value); 376 } 377 } 378 finally 379 { 380 modifyingSlice.release(); 381 } 382 } 383 finally 384 { 385 endUpdate(); 386 } 387 } 388 389 /** 390 * Returns <code>true</code> if the ROI stack is empty. 391 */ 392 public boolean isEmpty() 393 { 394 return slices.isEmpty(); 395 } 396 397 /** 398 * @return The size of this ROI stack along T.<br> 399 * Note that the returned value indicates the difference between upper and lower bounds 400 * of this ROI, but doesn't guarantee that all slices in-between exist ( {@link #getSlice(int)} may still 401 * return <code>null</code>.<br> 402 */ 403 public int getSizeT() 404 { 405 synchronized (slices) 406 { 407 if (slices.isEmpty()) 408 return 0; 409 410 return (slices.lastKey().intValue() - slices.firstKey().intValue()) + 1; 411 } 412 } 413 414 /** 415 * Returns the ROI slice at given T position. 416 */ 417 public R getSlice(int t) 418 { 419 return slices.get(Integer.valueOf(t)); 420 } 421 422 /** 423 * Returns the ROI slice at given T position. 424 */ 425 public R getSlice(int t, boolean createIfNull) 426 { 427 R result = getSlice(t); 428 429 if ((result == null) && createIfNull) 430 { 431 result = createSlice(); 432 if (result != null) 433 setSlice(t, result); 434 } 435 436 return result; 437 } 438 439 /** 440 * Sets the slice for the given T position. 441 */ 442 public void setSlice(int t, R roi3d) 443 { 444 if (roi3d == null) 445 throw new IllegalArgumentException("Cannot set an empty slice in a 4D ROI"); 446 447 roi3d.beginUpdate(); 448 try 449 { 450 // set T and C position 451 roi3d.setT(t); 452 roi3d.setC(getC()); 453 } 454 finally 455 { 456 roi3d.endUpdate(); 457 } 458 459 // listen events from this ROI and its overlay 460 roi3d.addListener(this); 461 roi3d.getOverlay().addOverlayListener(this); 462 463 synchronized (slices) 464 { 465 slices.put(Integer.valueOf(t), roi3d); 466 } 467 468 // notify ROI changed 469 roiChanged(true); 470 } 471 472 /** 473 * Removes slice at the given T position and returns it. 474 */ 475 public R removeSlice(int t) 476 { 477 final R result; 478 479 synchronized (slices) 480 { 481 // remove the current slice (if any) 482 result = slices.remove(Integer.valueOf(t)); 483 } 484 485 // remove listeners 486 if (result != null) 487 { 488 result.removeListener(this); 489 result.getOverlay().removeOverlayListener(this); 490 } 491 492 // notify ROI changed 493 roiChanged(true); 494 495 return result; 496 } 497 498 /** 499 * Removes all slices. 500 */ 501 public void clear() 502 { 503 // nothing to do 504 if (isEmpty()) 505 return; 506 507 synchronized (slices) 508 { 509 for (R slice : slices.values()) 510 { 511 slice.removeListener(this); 512 slice.getOverlay().removeOverlayListener(this); 513 } 514 515 slices.clear(); 516 } 517 518 roiChanged(true); 519 } 520 521 /** 522 * Called when a ROI slice has changed. 523 */ 524 protected void sliceChanged(ROIEvent event) 525 { 526 if (modifyingSlice.availablePermits() <= 0) 527 return; 528 529 final ROI source = event.getSource(); 530 531 switch (event.getType()) 532 { 533 case ROI_CHANGED: 534 // position change of a slice can change global bounds --> transform to 'content changed' event type 535 roiChanged(true); 536 // roiChanged(StringUtil.equals(event.getPropertyName(), ROI_CHANGED_ALL)); 537 break; 538 539 case FOCUS_CHANGED: 540 setFocused(source.isFocused()); 541 break; 542 543 case SELECTION_CHANGED: 544 setSelected(source.isSelected()); 545 break; 546 547 case PROPERTY_CHANGED: 548 final String propertyName = event.getPropertyName(); 549 550 if ((propertyName == null) || propertyName.equals(PROPERTY_READONLY)) 551 setReadOnly(source.isReadOnly()); 552 if ((propertyName == null) || propertyName.equals(PROPERTY_CREATING)) 553 setCreating(source.isCreating()); 554 break; 555 } 556 } 557 558 /** 559 * Called when a ROI slice overlay has changed. 560 */ 561 protected void sliceOverlayChanged(OverlayEvent event) 562 { 563 switch (event.getType()) 564 { 565 case PAINTER_CHANGED: 566 // forward the event to ROI stack overlay 567 getOverlay().painterChanged(); 568 break; 569 570 case PROPERTY_CHANGED: 571 // forward the event to ROI stack overlay 572 getOverlay().propertyChanged(event.getPropertyName()); 573 break; 574 } 575 } 576 577 @Override 578 public Rectangle4D computeBounds4D() 579 { 580 Rectangle3D xyzBounds = null; 581 582 synchronized (slices) 583 { 584 for (R slice : slices.values()) 585 { 586 final Rectangle3D bnd3d = slice.getBounds3D(); 587 588 // only add non empty bounds 589 if (!bnd3d.isEmpty()) 590 { 591 if (xyzBounds == null) 592 xyzBounds = (Rectangle3D) bnd3d.clone(); 593 else 594 xyzBounds.add(bnd3d); 595 } 596 } 597 } 598 599 // create empty 3D bounds 600 if (xyzBounds == null) 601 xyzBounds = new Rectangle3D.Double(); 602 603 final int t; 604 final int sizeT; 605 606 synchronized (slices) 607 { 608 if (!slices.isEmpty()) 609 { 610 t = slices.firstKey().intValue(); 611 sizeT = getSizeT(); 612 } 613 else 614 { 615 t = 0; 616 sizeT = 0; 617 } 618 } 619 return new Rectangle4D.Double(xyzBounds.getX(), xyzBounds.getY(), xyzBounds.getZ(), t, xyzBounds.getSizeX(), 620 xyzBounds.getSizeY(), xyzBounds.getSizeZ(), sizeT); 621 } 622 623 @Override 624 public boolean contains(double x, double y, double z, double t) 625 { 626 final R roi3d = getSlice((int) Math.floor(t)); 627 628 if (roi3d != null) 629 return roi3d.contains(x, y, z); 630 631 return false; 632 } 633 634 @Override 635 public boolean contains(double x, double y, double z, double t, double sizeX, double sizeY, double sizeZ, 636 double sizeT) 637 { 638 final Rectangle4D bounds = getBounds4D(); 639 640 // easy discard 641 if (!bounds.contains(x, y, z, t, sizeX, sizeY, sizeZ, sizeT)) 642 return false; 643 644 final int lim = (int) Math.floor(t + sizeT); 645 for (int tc = (int) Math.floor(t); tc < lim; tc++) 646 { 647 final R roi3d = getSlice(tc); 648 if ((roi3d == null) || !roi3d.contains(x, y, z, sizeX, sizeY, sizeZ)) 649 return false; 650 } 651 652 return true; 653 } 654 655 @Override 656 public boolean intersects(double x, double y, double z, double t, double sizeX, double sizeY, double sizeZ, 657 double sizeT) 658 { 659 final Rectangle4D bounds = getBounds4D(); 660 661 // easy discard 662 if (!bounds.intersects(x, y, z, t, sizeX, sizeY, sizeZ, sizeT)) 663 return false; 664 665 final int lim = (int) Math.floor(t + sizeT); 666 for (int tc = (int) Math.floor(t); tc < lim; tc++) 667 { 668 final R roi3d = getSlice(tc); 669 if ((roi3d != null) && roi3d.intersects(x, y, z, sizeX, sizeY, sizeZ)) 670 return true; 671 } 672 673 return false; 674 } 675 676 @Override 677 public boolean hasSelectedPoint() 678 { 679 // default 680 return false; 681 } 682 683 @Override 684 public void unselectAllPoints() 685 { 686 beginUpdate(); 687 try 688 { 689 modifyingSlice.acquireUninterruptibly(); 690 try 691 { 692 synchronized (slices) 693 { 694 for (R slice : slices.values()) 695 slice.unselectAllPoints(); 696 } 697 } 698 finally 699 { 700 modifyingSlice.release(); 701 } 702 } 703 finally 704 { 705 endUpdate(); 706 } 707 } 708 709 // default approximated implementation for ROI4DStack 710 @Override 711 public double computeNumberOfContourPoints() 712 { 713 // 4D contour points = first slice points + inter slices contour points + last slice points 714 double result = 0; 715 716 synchronized (slices) 717 { 718 if (slices.size() <= 2) 719 { 720 for (R slice : slices.values()) 721 result += slice.getNumberOfPoints(); 722 } 723 else 724 { 725 final Entry<Integer, R> firstEntry = slices.firstEntry(); 726 final Entry<Integer, R> lastEntry = slices.lastEntry(); 727 final Integer firstKey = firstEntry.getKey(); 728 final Integer lastKey = lastEntry.getKey(); 729 730 result = firstEntry.getValue().getNumberOfPoints(); 731 732 for (R slice : slices.subMap(firstKey, false, lastKey, false).values()) 733 result += slice.getNumberOfContourPoints(); 734 735 result += lastEntry.getValue().getNumberOfPoints(); 736 } 737 } 738 739 return result; 740 } 741 742 @Override 743 public double computeNumberOfPoints() 744 { 745 double volume = 0; 746 747 synchronized (slices) 748 { 749 for (R slice : slices.values()) 750 volume += slice.getNumberOfPoints(); 751 } 752 753 return volume; 754 } 755 756 @Override 757 public boolean canTranslate() 758 { 759 synchronized (slices) 760 { 761 // only need to test the first entry 762 if (!slices.isEmpty()) 763 return slices.firstEntry().getValue().canTranslate(); 764 } 765 766 return false; 767 } 768 769 /** 770 * Translate the stack of specified T position. 771 */ 772 public void translate(int t) 773 { 774 // easy optimizations 775 if ((t == 0) || isEmpty()) 776 return; 777 778 synchronized (slices) 779 { 780 final Map<Integer, R> map = new HashMap<Integer, R>(slices); 781 782 slices.clear(); 783 for (Entry<Integer, R> entry : map.entrySet()) 784 { 785 final R roi = entry.getValue(); 786 final int newT = roi.getT() + t; 787 788 // only positive value accepted 789 if (newT >= 0) 790 { 791 roi.setT(newT); 792 slices.put(Integer.valueOf(newT), roi); 793 } 794 } 795 } 796 797 // notify ROI changed 798 roiChanged(false); 799 } 800 801 @Override 802 public void translate(double dx, double dy, double dz, double dt) 803 { 804 beginUpdate(); 805 try 806 { 807 translateT += dt; 808 // convert to integer 809 final int dti = (int) translateT; 810 // keep trace of not used floating part 811 translateT -= dti; 812 813 translate(dti); 814 815 modifyingSlice.acquireUninterruptibly(); 816 try 817 { 818 synchronized (slices) 819 { 820 for (R slice : slices.values()) 821 slice.translate(dx, dy, dz); 822 } 823 } 824 finally 825 { 826 modifyingSlice.release(); 827 } 828 829 // notify ROI changed because we modified slice 'internally' 830 if ((dx != 0d) || (dy != 0d) || (dz != 0d)) 831 roiChanged(false); 832 } 833 finally 834 { 835 endUpdate(); 836 } 837 } 838 839 @Override 840 public boolean[] getBooleanMask2D(int x, int y, int width, int height, int z, int t, boolean inclusive) 841 { 842 final R roi3d = getSlice(t); 843 844 if (roi3d != null) 845 return roi3d.getBooleanMask2D(x, y, width, height, z, inclusive); 846 847 return new boolean[width * height]; 848 } 849 850 @Override 851 public BooleanMask2D getBooleanMask2D(int z, int t, boolean inclusive) 852 { 853 final R roi3d = getSlice(t); 854 855 if (roi3d != null) 856 return roi3d.getBooleanMask2D(z, inclusive); 857 858 return new BooleanMask2D(new Rectangle(), new boolean[0]); 859 } 860 861 // called when one of the slice ROI changed 862 @Override 863 public void roiChanged(ROIEvent event) 864 { 865 // propagate children change event 866 sliceChanged(event); 867 } 868 869 // called when one of the slice ROI overlay changed 870 @Override 871 public void overlayChanged(OverlayEvent event) 872 { 873 // propagate children overlay change event 874 sliceOverlayChanged(event); 875 } 876 877 @Override 878 public Iterator<R> iterator() 879 { 880 return slices.values().iterator(); 881 } 882 883 @Override 884 public boolean loadFromXML(Node node) 885 { 886 beginUpdate(); 887 try 888 { 889 if (!super.loadFromXML(node)) 890 return false; 891 892 // we don't need to save the 3D ROI class as the parent class already do it 893 clear(); 894 895 for (Element e : XMLUtil.getElements(node, "slice")) 896 { 897 // faster than using complete XML serialization 898 final R slice = createSlice(); 899 900 // error while reloading the ROI from XML 901 if ((slice == null) || !slice.loadFromXML(e)) 902 return false; 903 904 setSlice(slice.getT(), slice); 905 } 906 } 907 finally 908 { 909 endUpdate(); 910 } 911 912 return true; 913 } 914 915 @Override 916 public boolean saveToXML(Node node) 917 { 918 if (!super.saveToXML(node)) 919 return false; 920 921 synchronized (slices) 922 { 923 for (R slice : slices.values()) 924 { 925 Element sliceNode = XMLUtil.addElement(node, "slice"); 926 927 if (!slice.saveToXML(sliceNode)) 928 return false; 929 } 930 } 931 932 return true; 933 } 934 935 public class ROI4DStackPainter extends ROIPainter 936 { 937 R getSliceForCanvas(IcyCanvas canvas) 938 { 939 final int t = canvas.getPositionT(); 940 941 if (t >= 0) 942 return getSlice(t); 943 944 return null; 945 } 946 947 @Override 948 public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) 949 { 950 super.paint(g, sequence, canvas); 951 952 if (isActiveFor(canvas)) 953 { 954 if (canvas instanceof IcyCanvas3D) 955 { 956 // TODO 957 958 } 959 else if (canvas instanceof IcyCanvas2D) 960 { 961 // forward event to current slice 962 final R slice = getSliceForCanvas(canvas); 963 964 if (slice != null) 965 slice.getOverlay().paint(g, sequence, canvas); 966 } 967 } 968 } 969 970 @Override 971 public void keyPressed(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 972 { 973 // send event to parent first 974 super.keyPressed(e, imagePoint, canvas); 975 976 // then send it to active slice 977 if (isActiveFor(canvas)) 978 { 979 // forward event to current slice 980 final R slice = getSliceForCanvas(canvas); 981 982 if (slice != null) 983 slice.getOverlay().keyPressed(e, imagePoint, canvas); 984 } 985 } 986 987 @Override 988 public void keyReleased(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 989 { 990 // send event to parent first 991 super.keyReleased(e, imagePoint, canvas); 992 993 // then send it to active slice 994 if (isActiveFor(canvas)) 995 { 996 // forward event to current slice 997 final R slice = getSliceForCanvas(canvas); 998 999 if (slice != null) 1000 slice.getOverlay().keyReleased(e, imagePoint, canvas); 1001 } 1002 } 1003 1004 @Override 1005 public void mouseEntered(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1006 { 1007 // send event to parent first 1008 super.mouseEntered(e, imagePoint, canvas); 1009 1010 // then send it to active slice 1011 if (isActiveFor(canvas)) 1012 { 1013 // forward event to current slice 1014 final R slice = getSliceForCanvas(canvas); 1015 1016 if (slice != null) 1017 slice.getOverlay().mouseEntered(e, imagePoint, canvas); 1018 } 1019 } 1020 1021 @Override 1022 public void mouseExited(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1023 { 1024 // send event to parent first 1025 super.mouseExited(e, imagePoint, canvas); 1026 1027 // then send it to active slice 1028 if (isActiveFor(canvas)) 1029 { 1030 // forward event to current slice 1031 final R slice = getSliceForCanvas(canvas); 1032 1033 if (slice != null) 1034 slice.getOverlay().mouseExited(e, imagePoint, canvas); 1035 } 1036 } 1037 1038 @Override 1039 public void mouseMove(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1040 { 1041 // send event to parent first 1042 super.mouseMove(e, imagePoint, canvas); 1043 1044 // then send it to active slice 1045 if (isActiveFor(canvas)) 1046 { 1047 // forward event to current slice 1048 final R slice = getSliceForCanvas(canvas); 1049 1050 if (slice != null) 1051 slice.getOverlay().mouseMove(e, imagePoint, canvas); 1052 } 1053 } 1054 1055 @Override 1056 public void mouseDrag(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1057 { 1058 // send event to parent first 1059 super.mouseDrag(e, imagePoint, canvas); 1060 1061 // then send it to active slice 1062 if (isActiveFor(canvas)) 1063 { 1064 // forward event to current slice 1065 final R slice = getSliceForCanvas(canvas); 1066 1067 if (slice != null) 1068 slice.getOverlay().mouseDrag(e, imagePoint, canvas); 1069 } 1070 } 1071 1072 @Override 1073 public void mousePressed(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1074 { 1075 // send event to parent first 1076 super.mousePressed(e, imagePoint, canvas); 1077 1078 // then send it to active slice 1079 if (isActiveFor(canvas)) 1080 { 1081 // forward event to current slice 1082 final R slice = getSliceForCanvas(canvas); 1083 1084 if (slice != null) 1085 slice.getOverlay().mousePressed(e, imagePoint, canvas); 1086 } 1087 } 1088 1089 @Override 1090 public void mouseReleased(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1091 { 1092 // send event to parent first 1093 super.mouseReleased(e, imagePoint, canvas); 1094 1095 // then send it to active slice 1096 if (isActiveFor(canvas)) 1097 { 1098 // forward event to current slice 1099 final R slice = getSliceForCanvas(canvas); 1100 1101 if (slice != null) 1102 slice.getOverlay().mouseReleased(e, imagePoint, canvas); 1103 } 1104 } 1105 1106 @Override 1107 public void mouseClick(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1108 { 1109 // send event to parent first 1110 super.mouseClick(e, imagePoint, canvas); 1111 1112 // then send it to active slice 1113 if (isActiveFor(canvas)) 1114 { 1115 // forward event to current slice 1116 final R slice = getSliceForCanvas(canvas); 1117 1118 if (slice != null) 1119 slice.getOverlay().mouseClick(e, imagePoint, canvas); 1120 } 1121 } 1122 1123 @Override 1124 public void mouseWheelMoved(MouseWheelEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1125 { 1126 // send event to parent first 1127 super.mouseWheelMoved(e, imagePoint, canvas); 1128 1129 // then send it to active slice 1130 if (isActiveFor(canvas)) 1131 { 1132 // forward event to current slice 1133 final R slice = getSliceForCanvas(canvas); 1134 1135 if (slice != null) 1136 slice.getOverlay().mouseWheelMoved(e, imagePoint, canvas); 1137 } 1138 } 1139 } 1140}