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 icy.painter; 020 021import icy.canvas.IcyCanvas; 022import icy.canvas.IcyCanvas2D; 023import icy.common.CollapsibleEvent; 024import icy.sequence.Sequence; 025import icy.system.thread.ThreadUtil; 026import icy.type.point.Point3D; 027import icy.type.point.Point5D; 028import icy.util.EventUtil; 029import icy.util.GraphicsUtil; 030import icy.util.ShapeUtil; 031import icy.util.XMLUtil; 032import icy.vtk.IcyVtkPanel; 033 034import java.awt.BasicStroke; 035import java.awt.Color; 036import java.awt.Graphics2D; 037import java.awt.event.InputEvent; 038import java.awt.event.KeyEvent; 039import java.awt.event.MouseEvent; 040import java.awt.geom.Ellipse2D; 041import java.lang.ref.WeakReference; 042import java.util.ArrayList; 043import java.util.Arrays; 044import java.util.EventListener; 045import java.util.List; 046 047import org.w3c.dom.Node; 048 049import plugins.kernel.canvas.VtkCanvas; 050import vtk.vtkActor; 051import vtk.vtkInformation; 052import vtk.vtkPolyDataMapper; 053import vtk.vtkProp; 054import vtk.vtkSphereSource; 055 056/** 057 * Anchor3D class, used for 3D point control. 058 * 059 * @author Stephane 060 */ 061public class Anchor3D extends Overlay implements VtkPainter, Runnable 062{ 063 /** 064 * Interface to listen Anchor3D position change 065 */ 066 public static interface Anchor3DPositionListener extends EventListener 067 { 068 public void positionChanged(Anchor3D source); 069 } 070 071 public static class Anchor3DEvent implements CollapsibleEvent 072 { 073 private final Anchor3D source; 074 075 public Anchor3DEvent(Anchor3D source) 076 { 077 super(); 078 079 this.source = source; 080 } 081 082 /** 083 * @return the source 084 */ 085 public Anchor3D getSource() 086 { 087 return source; 088 } 089 090 @Override 091 public boolean collapse(CollapsibleEvent event) 092 { 093 if (equals(event)) 094 { 095 // nothing to do here 096 return true; 097 } 098 099 return false; 100 } 101 102 @Override 103 public int hashCode() 104 { 105 return source.hashCode(); 106 } 107 108 @Override 109 public boolean equals(Object obj) 110 { 111 if (obj == this) 112 return true; 113 114 if (obj instanceof Anchor3DEvent) 115 { 116 final Anchor3DEvent event = (Anchor3DEvent) obj; 117 118 return (event.getSource() == source); 119 } 120 121 return super.equals(obj); 122 } 123 } 124 125 protected static final String ID_COLOR = "color"; 126 protected static final String ID_SELECTEDCOLOR = "selected_color"; 127 protected static final String ID_SELECTED = "selected"; 128 protected static final String ID_POS_X = "pos_x"; 129 protected static final String ID_POS_Y = "pos_y"; 130 protected static final String ID_POS_Z = "pos_z"; 131 protected static final String ID_RAY = "ray"; 132 protected static final String ID_VISIBLE = "visible"; 133 134 public static final int DEFAULT_RAY = 6; 135 public static final Color DEFAULT_NORMAL_COLOR = Color.GREEN; 136 public static final Color DEFAULT_SELECTED_COLOR = Color.WHITE; 137 138 /** 139 * position (canvas) 140 */ 141 protected final Point3D.Double position; 142 /** 143 * radius (integer as we express it in pixel) 144 */ 145 protected int ray; 146 147 /** 148 * color 149 */ 150 protected Color color; 151 /** 152 * selection color 153 */ 154 protected Color selectedColor; 155 /** 156 * selection flag 157 */ 158 protected boolean selected; 159 /** 160 * flag that indicate if anchor is visible 161 */ 162 protected boolean visible; 163 164 // drag internals 165 protected Point3D startDragMousePosition; 166 protected Point3D startDragPainterPosition; 167 168 // 2D shape for X,Y contains test 169 protected final Ellipse2D ellipse; 170 171 // VTK 3D objects 172 vtkSphereSource vtkSource; 173 protected vtkPolyDataMapper polyMapper; 174 protected vtkActor actor; 175 protected vtkInformation vtkInfo; 176 177 // 3D internal 178 protected boolean needRebuild; 179 protected boolean needPropertiesUpdate; 180 protected double scaling[]; 181 protected WeakReference<VtkCanvas> canvas3d; 182 183 // listeners 184 protected final List<Anchor3DPositionListener> anchor3DPositionlisteners; 185 186 public Anchor3D(double x, double y, double z, int ray, Color color, Color selectedColor) 187 { 188 super("Anchor", OverlayPriority.SHAPE_NORMAL); 189 190 position = new Point3D.Double(x, y, z); 191 this.ray = ray; 192 this.color = color; 193 this.selectedColor = selectedColor; 194 selected = false; 195 visible = true; 196 197 startDragMousePosition = null; 198 startDragPainterPosition = null; 199 200 ellipse = new Ellipse2D.Double(); 201 202 vtkSource = null; 203 polyMapper = null; 204 actor = null; 205 vtkInfo = null; 206 207 scaling = new double[3]; 208 Arrays.fill(scaling, 1d); 209 210 needRebuild = true; 211 needPropertiesUpdate = false; 212 213 canvas3d = new WeakReference<VtkCanvas>(null); 214 215 anchor3DPositionlisteners = new ArrayList<Anchor3DPositionListener>(); 216 } 217 218 public Anchor3D(double x, double y, double z, Color color, Color selectedColor) 219 { 220 this(x, y, z, DEFAULT_RAY, color, selectedColor); 221 } 222 223 public Anchor3D(double x, double y, double z, int ray) 224 { 225 this(x, y, z, ray, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); 226 } 227 228 public Anchor3D(double x, double y, double z) 229 { 230 this(x, y, z, DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); 231 } 232 233 public Anchor3D() 234 { 235 this(0d, 0d, 0d, DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); 236 } 237 238 @Override 239 protected void finalize() throws Throwable 240 { 241 super.finalize(); 242 243 // release allocated VTK resources 244 if (vtkSource != null) 245 vtkSource.Delete(); 246 if (actor != null) 247 { 248 actor.SetPropertyKeys(null); 249 actor.Delete(); 250 } 251 if (vtkInfo != null) 252 { 253 vtkInfo.Remove(VtkCanvas.visibilityKey); 254 vtkInfo.Delete(); 255 } 256 if (polyMapper != null) 257 polyMapper.Delete(); 258 } 259 260 /** 261 * @return X coordinate position 262 */ 263 public double getX() 264 { 265 return position.x; 266 } 267 268 /** 269 * Sets the X coordinate position 270 */ 271 public void setX(double value) 272 { 273 setPosition(value, position.y, position.z); 274 } 275 276 /** 277 * @return Y coordinate position 278 */ 279 public double getY() 280 { 281 return position.y; 282 } 283 284 /** 285 * Sets the Y coordinate position 286 */ 287 public void setY(double value) 288 { 289 setPosition(position.x, value, position.z); 290 } 291 292 /** 293 * @return Z coordinate position 294 */ 295 public double getZ() 296 { 297 return position.z; 298 } 299 300 /** 301 * Sets the Z coordinate position 302 */ 303 public void setZ(double value) 304 { 305 setPosition(position.x, position.y, value); 306 } 307 308 /** 309 * Get anchor position (return the internal reference) 310 */ 311 public Point3D getPositionInternal() 312 { 313 return position; 314 } 315 316 /** 317 * Get anchor position 318 */ 319 public Point3D getPosition() 320 { 321 return new Point3D.Double(position.x, position.y, position.z); 322 } 323 324 /** 325 * Sets anchor position 326 */ 327 public void setPosition(Point3D p) 328 { 329 setPosition(p.getX(), p.getY(), p.getZ()); 330 } 331 332 /** 333 * Sets anchor position 334 */ 335 public void setPosition(double x, double y, double z) 336 { 337 if ((position.x != x) || (position.y != y) || (position.z != z)) 338 { 339 position.x = x; 340 position.y = y; 341 position.z = z; 342 343 needRebuild = true; 344 positionChanged(); 345 painterChanged(); 346 } 347 } 348 349 /** 350 * Performs a translation on the anchor position 351 */ 352 public void translate(double dx, double dy, double dz) 353 { 354 setPosition(position.x + dx, position.y + dy, position.z + dz); 355 } 356 357 /** 358 * @return the ray 359 */ 360 public int getRay() 361 { 362 return ray; 363 } 364 365 /** 366 * Sets the ray 367 */ 368 public void setRay(int value) 369 { 370 if (ray != value) 371 { 372 ray = value; 373 needRebuild = true; 374 painterChanged(); 375 } 376 } 377 378 /** 379 * @return the color 380 */ 381 public Color getColor() 382 { 383 return color; 384 } 385 386 /** 387 * Sets the color 388 */ 389 public void setColor(Color value) 390 { 391 if (color != value) 392 { 393 color = value; 394 needPropertiesUpdate = true; 395 painterChanged(); 396 } 397 } 398 399 /** 400 * @return the <code>selected</code> state color 401 */ 402 public Color getSelectedColor() 403 { 404 return selectedColor; 405 } 406 407 /** 408 * Sets the <code>selected</code> state color 409 */ 410 public void setSelectedColor(Color value) 411 { 412 if (selectedColor != value) 413 { 414 selectedColor = value; 415 needPropertiesUpdate = true; 416 painterChanged(); 417 } 418 } 419 420 /** 421 * @return the <code>selected</code> state 422 */ 423 public boolean isSelected() 424 { 425 return selected; 426 } 427 428 /** 429 * Sets the <code>selected</code> state 430 */ 431 public void setSelected(boolean value) 432 { 433 if (selected != value) 434 { 435 selected = value; 436 437 // end drag 438 if (!value) 439 startDragMousePosition = null; 440 441 needPropertiesUpdate = true; 442 painterChanged(); 443 } 444 } 445 446 /** 447 * @return the visible 448 */ 449 public boolean isVisible() 450 { 451 return visible; 452 } 453 454 /** 455 * @param value 456 * the visible to set 457 */ 458 public void setVisible(boolean value) 459 { 460 if (visible != value) 461 { 462 visible = value; 463 needPropertiesUpdate = true; 464 painterChanged(); 465 } 466 } 467 468 /** 469 * Returns <code>true</code> if specified Point3D is over the anchor in the specified canvas 470 */ 471 public boolean isOver(IcyCanvas canvas, Point3D imagePoint) 472 { 473 updateEllipseForCanvas(canvas); 474 475 // specific VTK canvas processing 476 if (canvas instanceof VtkCanvas) 477 { 478 // faster to use picked object 479 return (actor != null) && (actor == ((VtkCanvas) canvas).getPickedObject()); 480 } 481 482 // at this point we need image position 483 if (imagePoint == null) 484 return false; 485 486 final double x = imagePoint.getX(); 487 final double y = imagePoint.getY(); 488 final double z = imagePoint.getZ(); 489 490 // default processing for other canvas 491 final int cnvZ = canvas.getPositionZ(); 492 493 // same Z position ? 494 if ((cnvZ == -1) || (z == -1d) || (cnvZ == (int) z)) 495 { 496 // fast contains test to start with 497 if (ellipse.getBounds2D().contains(x, y)) 498 return ellipse.contains(x, y); 499 } 500 501 return false; 502 } 503 504 /** 505 * Returns adjusted ray for specified Canvas 506 */ 507 protected double getAdjRay(IcyCanvas canvas) 508 { 509 // assume X dimension is ok here 510 return canvas.canvasToImageLogDeltaX(ray); 511 } 512 513 /** 514 * Update internal ellipse for specified Canvas 515 */ 516 protected void updateEllipseForCanvas(IcyCanvas canvas) 517 { 518 // specific VTK canvas processing 519 if (canvas instanceof VtkCanvas) 520 { 521 // 3D canvas 522 final VtkCanvas cnv = (VtkCanvas) canvas; 523 // update reference if needed 524 if (canvas3d.get() != cnv) 525 canvas3d = new WeakReference<VtkCanvas>(cnv); 526 527 // FIXME : need a better implementation 528 final double[] s = cnv.getVolumeScale(); 529 530 // scaling changed ? 531 if (!Arrays.equals(scaling, s)) 532 { 533 // update scaling 534 scaling = s; 535 // need rebuild 536 needRebuild = true; 537 } 538 539 if (needRebuild) 540 { 541 // initialize VTK objects if not yet done 542 if (actor == null) 543 initVtkObjects(); 544 545 // request rebuild 3D objects 546 ThreadUtil.runSingle(this); 547 needRebuild = false; 548 } 549 550 if (needPropertiesUpdate) 551 { 552 updateVtkDisplayProperties(); 553 needPropertiesUpdate = false; 554 } 555 556 // update sphere radius 557 updateVtkRadius(); 558 } 559 else 560 { 561 final double adjRay = getAdjRay(canvas); 562 563 ellipse.setFrame(position.x - adjRay, position.y - adjRay, adjRay * 2, adjRay * 2); 564 } 565 } 566 567 protected boolean updateDrag(InputEvent e, double x, double y, double z) 568 { 569 // not dragging --> exit 570 if (startDragMousePosition == null) 571 return false; 572 573 double dx = x - startDragMousePosition.getX(); 574 double dy = y - startDragMousePosition.getY(); 575 double dz = z - startDragMousePosition.getZ(); 576 577 // shift action --> limit to one direction 578 if (EventUtil.isShiftDown(e)) 579 { 580 // X or Z drag 581 if (Math.abs(dx) > Math.abs(dy)) 582 { 583 dy = 0d; 584 585 // Z drag 586 if (Math.abs(dz) > Math.abs(dx)) 587 dx = 0d; 588 else 589 dz = 0d; 590 } 591 // Y or Z drag 592 else 593 { 594 dx = 0d; 595 596 // Z drag 597 if (Math.abs(dz) > Math.abs(dy)) 598 dy = 0d; 599 else 600 dz = 0d; 601 } 602 } 603 604 // set new position 605 setPosition(new Point3D.Double(startDragPainterPosition.getX() + dx, startDragPainterPosition.getY() + dy, 606 startDragPainterPosition.getZ() + dz)); 607 608 return true; 609 } 610 611 protected boolean updateDrag(InputEvent e, Point3D pt) 612 { 613 return updateDrag(e, pt.getX(), pt.getY(), pt.getZ()); 614 } 615 616 /** 617 * called when anchor position has changed 618 */ 619 protected void positionChanged() 620 { 621 updater.changed(new Anchor3DEvent(this)); 622 } 623 624 @Override 625 public void onChanged(CollapsibleEvent object) 626 { 627 // we got a position change event 628 if (object instanceof Anchor3DEvent) 629 { 630 firePositionChangedEvent(((Anchor3DEvent) object).getSource()); 631 return; 632 } 633 634 super.onChanged(object); 635 } 636 637 protected void firePositionChangedEvent(Anchor3D source) 638 { 639 for (Anchor3DPositionListener listener : new ArrayList<Anchor3DPositionListener>(anchor3DPositionlisteners)) 640 listener.positionChanged(source); 641 } 642 643 public void addPositionListener(Anchor3DPositionListener listener) 644 { 645 anchor3DPositionlisteners.add(listener); 646 } 647 648 public void removePositionListener(Anchor3DPositionListener listener) 649 { 650 anchor3DPositionlisteners.remove(listener); 651 } 652 653 protected void initVtkObjects() 654 { 655 // init 3D painters stuff 656 vtkSource = new vtkSphereSource(); 657 vtkSource.SetRadius(getRay()); 658 vtkSource.SetThetaResolution(12); 659 vtkSource.SetPhiResolution(12); 660 661 polyMapper = new vtkPolyDataMapper(); 662 polyMapper.SetInputConnection((vtkSource).GetOutputPort()); 663 664 actor = new vtkActor(); 665 actor.SetMapper(polyMapper); 666 667 // use vtkInformations to store outline visibility state (hacky) 668 vtkInfo = new vtkInformation(); 669 vtkInfo.Set(VtkCanvas.visibilityKey, 0); 670 // VtkCanvas use this to restore correctly outline visibility flag 671 actor.SetPropertyKeys(vtkInfo); 672 673 // initialize color 674 final Color col = getColor(); 675 actor.GetProperty().SetColor(col.getRed() / 255d, col.getGreen() / 255d, col.getBlue() / 255d); 676 } 677 678 /** 679 * update 3D painter for 3D canvas (called only when VTK is loaded). 680 */ 681 protected boolean rebuildVtkObjects() 682 { 683 final VtkCanvas canvas = canvas3d.get(); 684 // canvas was closed 685 if (canvas == null) 686 return false; 687 688 final IcyVtkPanel vtkPanel = canvas.getVtkPanel(); 689 // canvas was closed 690 if (vtkPanel == null) 691 return false; 692 693 final Point3D pos = getPosition(); 694 695 // actor can be accessed in canvas3d for rendering so we need to synchronize access 696 vtkPanel.lock(); 697 try 698 { 699 // need to handle scaling on radius and position to keep a "round" sphere (else we obtain ellipsoid) 700 vtkSource.SetCenter(pos.getX() * scaling[0], pos.getY() * scaling[1], pos.getZ() * scaling[2]); 701 vtkSource.Update(); 702 polyMapper.Update(); 703 704 // actor.SetScale(scaling[0], scaling[1], scaling[0]); 705 } 706 finally 707 { 708 vtkPanel.unlock(); 709 } 710 711 // need to repaint 712 painterChanged(); 713 714 return true; 715 } 716 717 protected void updateVtkDisplayProperties() 718 { 719 if (actor == null) 720 return; 721 722 final VtkCanvas cnv = canvas3d.get(); 723 final Color col = isSelected() ? getSelectedColor() : getColor(); 724 final double r = col.getRed() / 255d; 725 final double g = col.getGreen() / 255d; 726 final double b = col.getBlue() / 255d; 727 // final float opacity = getOpacity(); 728 729 final IcyVtkPanel vtkPanel = (cnv != null) ? cnv.getVtkPanel() : null; 730 731 // we need to lock canvas as actor can be accessed during rendering 732 if (vtkPanel != null) 733 vtkPanel.lock(); 734 try 735 { 736 actor.GetProperty().SetColor(r, g, b); 737 if (isVisible()) 738 { 739 actor.SetVisibility(1); 740 vtkInfo.Set(VtkCanvas.visibilityKey, 1); 741 } 742 else 743 { 744 actor.SetVisibility(0); 745 vtkInfo.Set(VtkCanvas.visibilityKey, 0); 746 } 747 } 748 finally 749 { 750 if (vtkPanel != null) 751 vtkPanel.unlock(); 752 } 753 754 // need to repaint 755 painterChanged(); 756 } 757 758 protected void updateVtkRadius() 759 { 760 final VtkCanvas canvas = canvas3d.get(); 761 // canvas was closed 762 if (canvas == null) 763 return; 764 765 final IcyVtkPanel vtkPanel = canvas.getVtkPanel(); 766 // canvas was closed 767 if (vtkPanel == null) 768 return; 769 770 if (vtkSource == null) 771 return; 772 773 // update sphere radius base on canvas scale X 774 final double radius = getAdjRay(canvas) * scaling[0]; 775 776 if (vtkSource.GetRadius() != radius) 777 { 778 // actor can be accessed in canvas3d for rendering so we need to synchronize access 779 vtkPanel.lock(); 780 try 781 { 782 vtkSource.SetRadius(radius); 783 vtkSource.Update(); 784 } 785 finally 786 { 787 vtkPanel.unlock(); 788 } 789 790 // need to repaint 791 painterChanged(); 792 } 793 } 794 795 @Override 796 public vtkProp[] getProps() 797 { 798 // initialize VTK objects if not yet done 799 if (actor == null) 800 initVtkObjects(); 801 802 return new vtkActor[] {actor}; 803 } 804 805 @Override 806 public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) 807 { 808 paint(g, sequence, canvas, false); 809 } 810 811 public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas, boolean simplified) 812 { 813 // this will update VTK objects if needed 814 updateEllipseForCanvas(canvas); 815 816 if (canvas instanceof IcyCanvas2D) 817 { 818 // nothing to do here when not visible 819 if (!isVisible()) 820 return; 821 822 // get canvas Z position 823 final int cnvZ = canvas.getPositionZ(); 824 // get delta Z (difference between canvas Z position and anchor Z pos) 825 final int dz = Math.abs(((int) getZ()) - cnvZ); 826 // calculate z fade range 827 final int zRange = Math.min(10, Math.max(3, sequence.getSizeZ() / 10)); 828 829 // not visible on this Z position 830 if (dz > zRange) 831 return; 832 833 // trivial paint optimization 834 final boolean shapeVisible = ShapeUtil.isVisible(g, ellipse); 835 836 if (shapeVisible) 837 { 838 final Graphics2D g2 = (Graphics2D) g.create(); 839 // ration for size / opacity 840 final float ratio = 1f - ((float) dz / (float) zRange); 841 842 if (ratio != 1f) 843 GraphicsUtil.mixAlpha(g2, ratio); 844 845 // draw content 846 if (isSelected()) 847 g2.setColor(getSelectedColor()); 848 else 849 g2.setColor(getColor()); 850 851 // simplified small drawing 852 if (simplified) 853 { 854 final int ray = (int) canvas.canvasToImageDeltaX(2); 855 g2.fillRect((int) getX() - ray, (int) getY() - ray, ray * 2, ray * 2); 856 } 857 // normal drawing 858 else 859 { 860 // draw ellipse content 861 g2.fill(ellipse); 862 // draw black border 863 g2.setStroke(new BasicStroke((float) (getAdjRay(canvas) / 8f))); 864 g2.setColor(Color.black); 865 g2.draw(ellipse); 866 } 867 868 g2.dispose(); 869 } 870 } 871 else if (canvas instanceof VtkCanvas) 872 { 873 // nothing to do here 874 } 875 } 876 877 @Override 878 public void keyPressed(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 879 { 880 if (!isVisible() && !getReceiveKeyEventOnHidden()) 881 return; 882 883 // no image position --> exit 884 if (imagePoint == null) 885 return; 886 887 // just for the shift key state change 888 updateDrag(e, imagePoint.x, imagePoint.y, imagePoint.z); 889 } 890 891 @Override 892 public void keyReleased(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 893 { 894 if (!isVisible() && !getReceiveKeyEventOnHidden()) 895 return; 896 897 // no image position --> exit 898 if (imagePoint == null) 899 return; 900 901 // just for the shift key state change 902 updateDrag(e, imagePoint.x, imagePoint.y, imagePoint.z); 903 } 904 905 @Override 906 public void mousePressed(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 907 { 908 if (!isVisible() && !getReceiveMouseEventOnHidden()) 909 return; 910 911 if (e.isConsumed()) 912 return; 913 914 // no image position --> exit 915 if (imagePoint == null) 916 return; 917 918 if (EventUtil.isLeftMouseButton(e)) 919 { 920 // consume event to activate drag 921 if (isSelected()) 922 { 923 startDragMousePosition = imagePoint.toPoint3D(); 924 startDragPainterPosition = getPosition(); 925 e.consume(); 926 } 927 } 928 } 929 930 @Override 931 public void mouseReleased(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 932 { 933 startDragMousePosition = null; 934 } 935 936 @Override 937 public void mouseDrag(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 938 { 939 if (!isVisible() && !getReceiveMouseEventOnHidden()) 940 return; 941 942 if (e.isConsumed()) 943 return; 944 945 // no image position --> exit 946 if (imagePoint == null) 947 return; 948 949 if (EventUtil.isLeftMouseButton(e)) 950 { 951 // if selected then move according to mouse position 952 if (isSelected()) 953 { 954 // force start drag if not already the case 955 if (startDragMousePosition == null) 956 { 957 startDragMousePosition = imagePoint.toPoint3D(); 958 startDragPainterPosition = getPosition(); 959 } 960 961 updateDrag(e, imagePoint.x, imagePoint.y, imagePoint.z); 962 963 e.consume(); 964 } 965 } 966 } 967 968 @Override 969 public void mouseMove(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) 970 { 971 if (!isVisible() && !getReceiveMouseEventOnHidden()) 972 return; 973 974 // already consumed, no selection possible 975 if (e.isConsumed()) 976 setSelected(false); 977 else 978 { 979 final boolean overlapped = isOver(canvas, (imagePoint != null) ? imagePoint.toPoint3D() : null); 980 981 setSelected(overlapped); 982 983 // so we can only have one selected at once 984 if (overlapped) 985 e.consume(); 986 } 987 } 988 989 @Override 990 public void run() 991 { 992 rebuildVtkObjects(); 993 } 994 995 public boolean loadPositionFromXML(Node node) 996 { 997 if (node == null) 998 return false; 999 1000 beginUpdate(); 1001 try 1002 { 1003 setX(XMLUtil.getElementDoubleValue(node, ID_POS_X, 0d)); 1004 setY(XMLUtil.getElementDoubleValue(node, ID_POS_Y, 0d)); 1005 setZ(XMLUtil.getElementDoubleValue(node, ID_POS_Z, 0d)); 1006 } 1007 finally 1008 { 1009 endUpdate(); 1010 } 1011 1012 return true; 1013 } 1014 1015 public boolean savePositionToXML(Node node) 1016 { 1017 if (node == null) 1018 return false; 1019 1020 XMLUtil.setElementDoubleValue(node, ID_POS_X, getX()); 1021 XMLUtil.setElementDoubleValue(node, ID_POS_Y, getY()); 1022 XMLUtil.setElementDoubleValue(node, ID_POS_Z, getZ()); 1023 1024 return true; 1025 } 1026 1027 @Override 1028 public boolean loadFromXML(Node node) 1029 { 1030 if (node == null) 1031 return false; 1032 1033 beginUpdate(); 1034 try 1035 { 1036 setColor(new Color(XMLUtil.getElementIntValue(node, ID_COLOR, DEFAULT_NORMAL_COLOR.getRGB()))); 1037 setSelectedColor( 1038 new Color(XMLUtil.getElementIntValue(node, ID_SELECTEDCOLOR, DEFAULT_SELECTED_COLOR.getRGB()))); 1039 setX(XMLUtil.getElementDoubleValue(node, ID_POS_X, 0d)); 1040 setY(XMLUtil.getElementDoubleValue(node, ID_POS_Y, 0d)); 1041 setZ(XMLUtil.getElementDoubleValue(node, ID_POS_Z, 0d)); 1042 setRay(XMLUtil.getElementIntValue(node, ID_RAY, DEFAULT_RAY)); 1043 setVisible(XMLUtil.getElementBooleanValue(node, ID_VISIBLE, true)); 1044 } 1045 finally 1046 { 1047 endUpdate(); 1048 } 1049 1050 return true; 1051 } 1052 1053 @Override 1054 public boolean saveToXML(Node node) 1055 { 1056 if (node == null) 1057 return false; 1058 1059 XMLUtil.setElementIntValue(node, ID_COLOR, getColor().getRGB()); 1060 XMLUtil.setElementIntValue(node, ID_SELECTEDCOLOR, getSelectedColor().getRGB()); 1061 XMLUtil.setElementDoubleValue(node, ID_POS_X, getX()); 1062 XMLUtil.setElementDoubleValue(node, ID_POS_Y, getY()); 1063 XMLUtil.setElementDoubleValue(node, ID_POS_Z, getY()); 1064 XMLUtil.setElementIntValue(node, ID_RAY, getRay()); 1065 XMLUtil.setElementBooleanValue(node, ID_VISIBLE, isVisible()); 1066 1067 return true; 1068 } 1069}