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.roi5d; 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.ROI4D; 045import icy.roi.ROI5D; 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.Rectangle4D; 052import icy.type.rectangle.Rectangle5D; 053import icy.util.XMLUtil; 054 055/** 056 * Abstract class defining a generic 5D ROI as a stack of individual 4D ROI slices. 057 * 058 * @author Alexandre Dufour 059 * @author Stephane Dallongeville 060 * @param <R> 061 * the type of 4D ROI for each slice of this 5D ROI 062 */ 063public class ROI5DStack<R extends ROI4D> extends ROI5D 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 translateC; 073 074 /** 075 * Creates a new 5D ROI based on the given 4D ROI type. 076 */ 077 public ROI5DStack(Class<R> roiClass) 078 { 079 super(); 080 081 this.roiClass = roiClass; 082 useChildColor = false; 083 modifyingSlice = new Semaphore(1); 084 translateC = 0d; 085 } 086 087 @Override 088 public String getDefaultName() 089 { 090 return "ROI4D stack"; 091 } 092 093 @Override 094 protected ROIPainter createPainter() 095 { 096 return new ROI5DStackPainter(); 097 } 098 099 /** 100 * Create a new empty 4D 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 4D slice color draw property and <code>false</code> if it 117 * uses the global 5D 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 4D slice color draw property and <code>false</code> to 126 * keep the global 5D 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 c, Color value) 147 { 148 final ROI4D slice = getSlice(c); 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 /** 362 * Returns <code>true</code> if the ROI stack is empty. 363 */ 364 public boolean isEmpty() 365 { 366 return slices.isEmpty(); 367 } 368 369 /** 370 * @return The size of this ROI stack along C.<br> 371 * Note that the returned value indicates the difference between upper and lower bounds 372 * of this ROI, but doesn't guarantee that all slices in-between exist ( {@link #getSlice(int)} may still 373 * return <code>null</code>.<br> 374 */ 375 public int getSizeC() 376 { 377 synchronized (slices) 378 { 379 if (slices.isEmpty()) 380 return 0; 381 382 return (slices.lastKey().intValue() - slices.firstKey().intValue()) + 1; 383 } 384 } 385 386 /** 387 * Returns the ROI slice at given C position. 388 */ 389 public R getSlice(int c) 390 { 391 return slices.get(Integer.valueOf(c)); 392 } 393 394 /** 395 * Returns the ROI slice at given C position. 396 */ 397 public R getSlice(int c, boolean createIfNull) 398 { 399 R result = getSlice(c); 400 401 if ((result == null) && createIfNull) 402 { 403 result = createSlice(); 404 if (result != null) 405 setSlice(c, result); 406 } 407 408 return result; 409 } 410 411 /** 412 * Sets the slice for the given C position. 413 */ 414 public void setSlice(int c, R roi4d) 415 { 416 if (roi4d == null) 417 throw new IllegalArgumentException("Cannot set an empty slice in a 5D ROI"); 418 419 // set C position 420 roi4d.setC(c); 421 // listen events from this ROI and its overlay 422 roi4d.addListener(this); 423 roi4d.getOverlay().addOverlayListener(this); 424 425 synchronized (slices) 426 { 427 slices.put(Integer.valueOf(c), roi4d); 428 } 429 430 // notify ROI changed 431 roiChanged(true); 432 } 433 434 /** 435 * Removes slice at the given C position and returns it. 436 */ 437 public R removeSlice(int c) 438 { 439 final R result; 440 441 synchronized (slices) 442 { 443 // remove the current slice (if any) 444 result = slices.remove(Integer.valueOf(c)); 445 } 446 447 // remove listeners 448 if (result != null) 449 { 450 result.removeListener(this); 451 result.getOverlay().removeOverlayListener(this); 452 } 453 454 // notify ROI changed 455 roiChanged(true); 456 457 return result; 458 } 459 460 /** 461 * Removes all slices. 462 */ 463 public void clear() 464 { 465 // nothing to do 466 if (isEmpty()) 467 return; 468 469 synchronized (slices) 470 { 471 for (R slice : slices.values()) 472 { 473 slice.removeListener(this); 474 slice.getOverlay().removeOverlayListener(this); 475 } 476 477 slices.clear(); 478 } 479 480 roiChanged(true); 481 } 482 483 /** 484 * Called when a ROI slice has changed. 485 */ 486 protected void sliceChanged(ROIEvent event) 487 { 488 if (modifyingSlice.availablePermits() <= 0) 489 return; 490 491 final ROI source = event.getSource(); 492 493 switch (event.getType()) 494 { 495 case ROI_CHANGED: 496 // position change of a slice can change global bounds --> transform to 'content changed' event type 497 roiChanged(true); 498 // roiChanged(StringUtil.equals(event.getPropertyName(), ROI_CHANGED_ALL)); 499 break; 500 501 case FOCUS_CHANGED: 502 setFocused(source.isFocused()); 503 break; 504 505 case SELECTION_CHANGED: 506 setSelected(source.isSelected()); 507 break; 508 509 case PROPERTY_CHANGED: 510 final String propertyName = event.getPropertyName(); 511 512 if ((propertyName == null) || propertyName.equals(PROPERTY_READONLY)) 513 setReadOnly(source.isReadOnly()); 514 if ((propertyName == null) || propertyName.equals(PROPERTY_CREATING)) 515 setCreating(source.isCreating()); 516 break; 517 } 518 } 519 520 /** 521 * Called when a ROI slice overlay has changed. 522 */ 523 protected void sliceOverlayChanged(OverlayEvent event) 524 { 525 switch (event.getType()) 526 { 527 case PAINTER_CHANGED: 528 // forward the event to ROI stack overlay 529 getOverlay().painterChanged(); 530 break; 531 532 case PROPERTY_CHANGED: 533 // forward the event to ROI stack overlay 534 getOverlay().propertyChanged(event.getPropertyName()); 535 break; 536 } 537 } 538 539 @Override 540 public Rectangle5D computeBounds5D() 541 { 542 Rectangle4D xyztBounds = null; 543 544 synchronized (slices) 545 { 546 for (R slice : slices.values()) 547 { 548 final Rectangle4D bnd4d = slice.getBounds4D(); 549 550 // only add non empty bounds 551 if (!bnd4d.isEmpty()) 552 { 553 if (xyztBounds == null) 554 xyztBounds = (Rectangle4D) bnd4d.clone(); 555 else 556 xyztBounds.add(bnd4d); 557 } 558 } 559 } 560 561 // create empty 4D bounds 562 if (xyztBounds == null) 563 xyztBounds = new Rectangle4D.Double(); 564 565 final int c; 566 final int sizeC; 567 568 synchronized (slices) 569 { 570 if (!slices.isEmpty()) 571 { 572 c = slices.firstKey().intValue(); 573 sizeC = getSizeC(); 574 } 575 else 576 { 577 c = 0; 578 sizeC = 0; 579 } 580 } 581 582 return new Rectangle5D.Double(xyztBounds.getX(), xyztBounds.getY(), xyztBounds.getZ(), xyztBounds.getT(), c, 583 xyztBounds.getSizeX(), xyztBounds.getSizeY(), xyztBounds.getSizeZ(), xyztBounds.getSizeT(), sizeC); 584 } 585 586 @Override 587 public boolean contains(double x, double y, double z, double t, double c) 588 { 589 final R roi4d = getSlice((int) Math.floor(c)); 590 591 if (roi4d != null) 592 return roi4d.contains(x, y, z, t); 593 594 return false; 595 } 596 597 @Override 598 public boolean contains(double x, double y, double z, double t, double c, double sizeX, double sizeY, double sizeZ, 599 double sizeT, double sizeC) 600 { 601 final Rectangle5D bounds = getBounds5D(); 602 603 // easy discard 604 if (!bounds.contains(x, y, z, t, c, sizeX, sizeY, sizeZ, sizeT, sizeC)) 605 return false; 606 607 final int lim = (int) Math.floor(c + sizeC); 608 for (int cc = (int) Math.floor(c); cc < lim; cc++) 609 { 610 final R roi4d = getSlice(cc); 611 if ((roi4d == null) || !roi4d.contains(x, y, z, t, sizeX, sizeY, sizeZ, sizeT)) 612 return false; 613 } 614 615 return true; 616 } 617 618 @Override 619 public boolean intersects(double x, double y, double z, double t, double c, double sizeX, double sizeY, 620 double sizeZ, double sizeT, double sizeC) 621 { 622 final Rectangle5D bounds = getBounds5D(); 623 624 // easy discard 625 if (!bounds.intersects(x, y, z, t, c, sizeX, sizeY, sizeZ, sizeT, sizeC)) 626 return false; 627 628 final int lim = (int) Math.floor(c + sizeC); 629 for (int cc = (int) Math.floor(c); cc < lim; cc++) 630 { 631 final R roi4d = getSlice(cc); 632 if ((roi4d != null) && roi4d.intersects(x, y, z, t, sizeX, sizeY, sizeZ, sizeT)) 633 return true; 634 } 635 636 return false; 637 } 638 639 @Override 640 public boolean hasSelectedPoint() 641 { 642 // default 643 return false; 644 } 645 646 @Override 647 public void unselectAllPoints() 648 { 649 beginUpdate(); 650 try 651 { 652 modifyingSlice.acquireUninterruptibly(); 653 try 654 { 655 synchronized (slices) 656 { 657 for (R slice : slices.values()) 658 slice.unselectAllPoints(); 659 } 660 } 661 finally 662 { 663 modifyingSlice.release(); 664 } 665 } 666 finally 667 { 668 endUpdate(); 669 } 670 } 671 672 // default approximated implementation for ROI5DStack 673 @Override 674 public double computeNumberOfContourPoints() 675 { 676 // 5D contour points = first slice points + inter slices contour points + last slice points 677 double perimeter = 0; 678 679 synchronized (slices) 680 { 681 if (slices.size() <= 2) 682 { 683 for (R slice : slices.values()) 684 perimeter += slice.getNumberOfPoints(); 685 } 686 else 687 { 688 final Entry<Integer, R> firstEntry = slices.firstEntry(); 689 final Entry<Integer, R> lastEntry = slices.lastEntry(); 690 final Integer firstKey = firstEntry.getKey(); 691 final Integer lastKey = lastEntry.getKey(); 692 693 perimeter = firstEntry.getValue().getNumberOfPoints(); 694 695 for (R slice : slices.subMap(firstKey, false, lastKey, false).values()) 696 perimeter += slice.getNumberOfContourPoints(); 697 698 perimeter += lastEntry.getValue().getNumberOfPoints(); 699 } 700 } 701 702 return perimeter; 703 } 704 705 @Override 706 public double computeNumberOfPoints() 707 { 708 double volume = 0; 709 710 synchronized (slices) 711 { 712 for (R slice : slices.values()) 713 volume += slice.getNumberOfPoints(); 714 } 715 716 return volume; 717 } 718 719 @Override 720 public boolean canTranslate() 721 { 722 synchronized (slices) 723 { 724 // only need to test the first entry 725 if (!slices.isEmpty()) 726 return slices.firstEntry().getValue().canTranslate(); 727 } 728 729 return false; 730 } 731 732 /** 733 * Translate the stack of specified C position. 734 */ 735 public void translate(int c) 736 { 737 // easy optimizations 738 if ((c == 0) || isEmpty()) 739 return; 740 741 synchronized (slices) 742 { 743 final Map<Integer, R> map = new HashMap<Integer, R>(slices); 744 745 slices.clear(); 746 for (Entry<Integer, R> entry : map.entrySet()) 747 { 748 final R roi = entry.getValue(); 749 final int newC = roi.getC() + c; 750 751 // only positive value accepted 752 if (newC >= 0) 753 { 754 roi.setC(newC); 755 slices.put(Integer.valueOf(newC), roi); 756 } 757 } 758 } 759 760 // notify ROI changed 761 roiChanged(false); 762 } 763 764 @Override 765 public void translate(double dx, double dy, double dz, double dt, double dc) 766 { 767 beginUpdate(); 768 try 769 { 770 translateC += dc; 771 // convert to integer 772 final int dci = (int) translateC; 773 // keep trace of not used floating part 774 translateC -= dci; 775 776 translate(dci); 777 778 modifyingSlice.acquireUninterruptibly(); 779 try 780 { 781 synchronized (slices) 782 { 783 for (R slice : slices.values()) 784 slice.translate(dx, dy, dz, dt); 785 } 786 } 787 finally 788 { 789 modifyingSlice.release(); 790 } 791 792 // notify ROI changed because we modified slice 'internally' 793 if ((dx != 0d) || (dy != 0d) || (dz != 0d) || (dt != 0d)) 794 roiChanged(false); 795 } 796 finally 797 { 798 endUpdate(); 799 } 800 } 801 802 @Override 803 public boolean[] getBooleanMask2D(int x, int y, int width, int height, int z, int t, int c, boolean inclusive) 804 { 805 final R roi4d = getSlice(c); 806 807 if (roi4d != null) 808 return roi4d.getBooleanMask2D(x, y, width, height, z, t, inclusive); 809 810 return new boolean[width * height]; 811 } 812 813 @Override 814 public BooleanMask2D getBooleanMask2D(int z, int t, int c, boolean inclusive) 815 { 816 final R roi4d = getSlice(c); 817 818 if (roi4d != null) 819 return roi4d.getBooleanMask2D(z, t, inclusive); 820 821 return new BooleanMask2D(new Rectangle(), new boolean[0]); 822 } 823 824 // called when one of the slice ROI changed 825 @Override 826 public void roiChanged(ROIEvent event) 827 { 828 // propagate children change event 829 sliceChanged(event); 830 } 831 832 // called when one of the slice ROI overlay changed 833 @Override 834 public void overlayChanged(OverlayEvent event) 835 { 836 // propagate children overlay change event 837 sliceOverlayChanged(event); 838 } 839 840 @Override 841 public Iterator<R> iterator() 842 { 843 return slices.values().iterator(); 844 } 845 846 @Override 847 public boolean loadFromXML(Node node) 848 { 849 beginUpdate(); 850 try 851 { 852 if (!super.loadFromXML(node)) 853 return false; 854 855 // we don't need to save the 4D ROI class as the parent class already do it 856 clear(); 857 858 for (Element e : XMLUtil.getElements(node, "slice")) 859 { 860 // faster than using complete XML serialization 861 final R slice = createSlice(); 862 863 // error while reloading the ROI from XML 864 if ((slice == null) || !slice.loadFromXML(e)) 865 return false; 866 867 setSlice(slice.getC(), slice); 868 } 869 } 870 finally 871 { 872 endUpdate(); 873 } 874 875 return true; 876 } 877 878 @Override 879 public boolean saveToXML(Node node) 880 { 881 if (!super.saveToXML(node)) 882 return false; 883 884 synchronized (slices) 885 { 886 for (R slice : slices.values()) 887 { 888 Element sliceNode = XMLUtil.addElement(node, "slice"); 889 890 if (!slice.saveToXML(sliceNode)) 891 return false; 892 } 893 } 894 895 return true; 896 } 897 898 public class ROI5DStackPainter extends ROIPainter 899 { 900 R getSliceForCanvas(IcyCanvas canvas) 901 { 902 final int c = canvas.getPositionC(); 903 904 if (c >= 0) 905 return getSlice(c); 906 907 return null; 908 } 909 910 @Override 911 public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) 912 { 913 super.paint(g, sequence, canvas); 914 915 if (isActiveFor(canvas)) 916 { 917 if (canvas instanceof IcyCanvas3D) 918 { 919 // TODO 920 921 } 922 else if (canvas instanceof IcyCanvas2D) 923 { 924 // forward event to current slice 925 final R slice = getSliceForCanvas(canvas); 926 927 if (slice != null) 928 slice.getOverlay().paint(g, sequence, canvas); 929 } 930 } 931 } 932 933 @Override 934 public void keyPressed(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 935 { 936 // send event to parent first 937 super.keyPressed(e, imagePoint, canvas); 938 939 // then send it to active slice 940 if (isActiveFor(canvas)) 941 { 942 // forward event to current slice 943 final R slice = getSliceForCanvas(canvas); 944 945 if (slice != null) 946 slice.getOverlay().keyPressed(e, imagePoint, canvas); 947 } 948 } 949 950 @Override 951 public void keyReleased(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 952 { 953 // send event to parent first 954 super.keyReleased(e, imagePoint, canvas); 955 956 // then send it to active slice 957 if (isActiveFor(canvas)) 958 { 959 // forward event to current slice 960 final R slice = getSliceForCanvas(canvas); 961 962 if (slice != null) 963 slice.getOverlay().keyReleased(e, imagePoint, canvas); 964 } 965 } 966 967 @Override 968 public void mouseEntered(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 969 { 970 // send event to parent first 971 super.mouseEntered(e, imagePoint, canvas); 972 973 // then send it to active slice 974 if (isActiveFor(canvas)) 975 { 976 // forward event to current slice 977 final R slice = getSliceForCanvas(canvas); 978 979 if (slice != null) 980 slice.getOverlay().mouseEntered(e, imagePoint, canvas); 981 } 982 } 983 984 @Override 985 public void mouseExited(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 986 { 987 // send event to parent first 988 super.mouseExited(e, imagePoint, canvas); 989 990 // then send it to active slice 991 if (isActiveFor(canvas)) 992 { 993 // forward event to current slice 994 final R slice = getSliceForCanvas(canvas); 995 996 if (slice != null) 997 slice.getOverlay().mouseExited(e, imagePoint, canvas); 998 } 999 } 1000 1001 @Override 1002 public void mouseMove(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1003 { 1004 // send event to parent first 1005 super.mouseMove(e, imagePoint, canvas); 1006 1007 // then send it to active slice 1008 if (isActiveFor(canvas)) 1009 { 1010 // forward event to current slice 1011 final R slice = getSliceForCanvas(canvas); 1012 1013 if (slice != null) 1014 slice.getOverlay().mouseMove(e, imagePoint, canvas); 1015 } 1016 } 1017 1018 @Override 1019 public void mouseDrag(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1020 { 1021 // send event to parent first 1022 super.mouseDrag(e, imagePoint, canvas); 1023 1024 // then send it to active slice 1025 if (isActiveFor(canvas)) 1026 { 1027 // forward event to current slice 1028 final R slice = getSliceForCanvas(canvas); 1029 1030 if (slice != null) 1031 slice.getOverlay().mouseDrag(e, imagePoint, canvas); 1032 } 1033 } 1034 1035 @Override 1036 public void mousePressed(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1037 { 1038 // send event to parent first 1039 super.mousePressed(e, imagePoint, canvas); 1040 1041 // then send it to active slice 1042 if (isActiveFor(canvas)) 1043 { 1044 // forward event to current slice 1045 final R slice = getSliceForCanvas(canvas); 1046 1047 if (slice != null) 1048 slice.getOverlay().mousePressed(e, imagePoint, canvas); 1049 } 1050 } 1051 1052 @Override 1053 public void mouseReleased(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1054 { 1055 // send event to parent first 1056 super.mouseReleased(e, imagePoint, canvas); 1057 1058 // then send it to active slice 1059 if (isActiveFor(canvas)) 1060 { 1061 // forward event to current slice 1062 final R slice = getSliceForCanvas(canvas); 1063 1064 if (slice != null) 1065 slice.getOverlay().mouseReleased(e, imagePoint, canvas); 1066 } 1067 } 1068 1069 @Override 1070 public void mouseClick(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1071 { 1072 // send event to parent first 1073 super.mouseClick(e, imagePoint, canvas); 1074 1075 // then send it to active slice 1076 if (isActiveFor(canvas)) 1077 { 1078 // forward event to current slice 1079 final R slice = getSliceForCanvas(canvas); 1080 1081 if (slice != null) 1082 slice.getOverlay().mouseClick(e, imagePoint, canvas); 1083 } 1084 } 1085 1086 @Override 1087 public void mouseWheelMoved(MouseWheelEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 1088 { 1089 // send event to parent first 1090 super.mouseWheelMoved(e, imagePoint, canvas); 1091 1092 // then send it to active slice 1093 if (isActiveFor(canvas)) 1094 { 1095 // forward event to current slice 1096 final R slice = getSliceForCanvas(canvas); 1097 1098 if (slice != null) 1099 slice.getOverlay().mouseWheelMoved(e, imagePoint, canvas); 1100 } 1101 } 1102 } 1103}