/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Icy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.painter; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.EventListener; import java.util.List; import org.w3c.dom.Node; import icy.canvas.IcyCanvas; import icy.canvas.IcyCanvas2D; import icy.common.CollapsibleEvent; import icy.painter.OverlayEvent.OverlayEventType; import icy.painter.PainterEvent.PainterEventType; import icy.sequence.Sequence; import icy.system.thread.ThreadUtil; import icy.type.point.Point5D; import icy.util.EventUtil; import icy.util.ShapeUtil; import icy.util.XMLUtil; import icy.vtk.IcyVtkPanel; import plugins.kernel.canvas.VtkCanvas; import vtk.vtkActor; import vtk.vtkInformation; import vtk.vtkPolyDataMapper; import vtk.vtkProp; import vtk.vtkSphereSource; /** * Anchor2D class, used for 2D point control. * * @author Stephane */ public class Anchor2D extends Overlay implements VtkPainter, Runnable { /** * Interface to listen Anchor2D position change */ public static interface Anchor2DPositionListener extends EventListener { public void positionChanged(Anchor2D source); } /** * @deprecated Use {@link Anchor2DPositionListener} listener or {@link OverlayListener} */ @Deprecated public static interface Anchor2DListener extends PainterListener { public void positionChanged(Anchor2D source); } public static class Anchor2DEvent implements CollapsibleEvent { private final Anchor2D source; public Anchor2DEvent(Anchor2D source) { super(); this.source = source; } /** * @return the source */ public Anchor2D getSource() { return source; } @Override public boolean collapse(CollapsibleEvent event) { if (equals(event)) { // nothing to do here return true; } return false; } @Override public int hashCode() { return source.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Anchor2DEvent) { final Anchor2DEvent event = (Anchor2DEvent) obj; return (event.getSource() == source); } return super.equals(obj); } } protected static final String ID_COLOR = "color"; protected static final String ID_SELECTEDCOLOR = "selected_color"; protected static final String ID_SELECTED = "selected"; protected static final String ID_POS_X = "pos_x"; protected static final String ID_POS_Y = "pos_y"; protected static final String ID_RAY = "ray"; protected static final String ID_VISIBLE = "visible"; public static final int DEFAULT_RAY = 6; public static final Color DEFAULT_NORMAL_COLOR = Color.YELLOW; public static final Color DEFAULT_SELECTED_COLOR = Color.WHITE; public static final String PROPERTY_COLOR = ID_COLOR; public static final String PROPERTY_SELECTEDCOLOR = ID_SELECTEDCOLOR; public static final String PROPERTY_SELECTED = ID_SELECTED; public static final String PROPERTY_RAY = ID_RAY; /** * position (canvas) */ protected final Point2D.Double position; /** * position Z (default = -1 = ALL) */ protected int z; /** * radius */ protected int ray; /** * color */ protected Color color; /** * selection color */ protected Color selectedColor; /** * selection flag */ protected boolean selected; /** * flag that indicate if anchor is visible */ protected boolean visible; /** * internals */ protected final Ellipse2D ellipse; protected Point2D startDragMousePosition; protected Point2D startDragPainterPosition; // VTK 3D objects vtkSphereSource vtkSource; protected vtkPolyDataMapper polyMapper; protected vtkActor actor; protected vtkInformation vtkInfo; // 3D internal protected boolean needRebuild; protected boolean needPropertiesUpdate; protected double scaling[]; protected WeakReference<VtkCanvas> canvas3d; protected final List<Anchor2DListener> anchor2Dlisteners; protected final List<Anchor2DPositionListener> anchor2DPositionlisteners; public Anchor2D(double x, double y, int ray, Color color, Color selectedColor) { super("Anchor", OverlayPriority.SHAPE_NORMAL); position = new Point2D.Double(x, y); z = -1; this.ray = ray; this.color = color; this.selectedColor = selectedColor; selected = false; visible = true; ellipse = new Ellipse2D.Double(); startDragMousePosition = null; startDragPainterPosition = null; vtkSource = null; polyMapper = null; actor = null; vtkInfo = null; scaling = new double[3]; Arrays.fill(scaling, 1d); needRebuild = true; needPropertiesUpdate = false; canvas3d = new WeakReference<VtkCanvas>(null); anchor2Dlisteners = new ArrayList<Anchor2DListener>(); anchor2DPositionlisteners = new ArrayList<Anchor2DPositionListener>(); } public Anchor2D(double x, double y, int ray, Color color) { this(x, y, ray, color, DEFAULT_SELECTED_COLOR); } /** * @param x * @param y * @param ray */ public Anchor2D(double x, double y, int ray) { this(x, y, ray, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); } /** * @param x * @param y * @param color */ public Anchor2D(double x, double y, Color color, Color selectedColor) { this(x, y, DEFAULT_RAY, color, selectedColor); } /** * @param x * @param y * @param color */ public Anchor2D(double x, double y, Color color) { this(x, y, DEFAULT_RAY, color, DEFAULT_SELECTED_COLOR); } /** * @param x * @param y */ public Anchor2D(double x, double y) { this(x, y, DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); } /** */ public Anchor2D() { this(0d, 0d, DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); } /** * @deprecated Use {@link #Anchor2D(double, double, int, Color, Color)} instead. */ @Deprecated public Anchor2D(Point2D position, int ray, Color color, Color selectedColor) { this(position.getX(), position.getY(), ray, color, selectedColor); } /** * @deprecated Use {@link #Anchor2D(double, double, int, Color)} instead. */ @Deprecated public Anchor2D(Point2D position, int ray, Color color) { this(position.getX(), position.getY(), ray, color, DEFAULT_SELECTED_COLOR); } /** * @deprecated Use {@link #Anchor2D(double, double, int)} instead. */ @Deprecated public Anchor2D(Point2D position, int ray) { this(position.getX(), position.getY(), ray, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); } /** * @deprecated Use {@link #Anchor2D(double, double, Color, Color)} instead. */ @Deprecated public Anchor2D(Point2D position, Color color, Color selectedColor) { this(position.getX(), position.getY(), DEFAULT_RAY, color, selectedColor); } /** * @deprecated Use {@link #Anchor2D(double, double, Color)} instead. */ @Deprecated public Anchor2D(Point2D position, Color color) { this(position.getX(), position.getY(), DEFAULT_RAY, color, DEFAULT_SELECTED_COLOR); } /** * @deprecated Use {@link #Anchor2D(double, double)} instead. */ @Deprecated public Anchor2D(Point2D position) { this(position.getX(), position.getY(), DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, int, Color, Color)} instead. */ @Deprecated public Anchor2D(Sequence sequence, double x, double y, int ray, Color color, Color selectedColor) { this(x, y, ray, color, selectedColor); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, int, Color, Color)} instead. */ @Deprecated public Anchor2D(Sequence sequence, Point2D position, int ray, Color color, Color selectedColor) { this(position.getX(), position.getY(), ray, color, selectedColor); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(Point2D, int, Color)} instead. */ @Deprecated public Anchor2D(Sequence sequence, Point2D position, int ray, Color color) { this(position.getX(), position.getY(), ray, color, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, int)} instead. */ @Deprecated public Anchor2D(Sequence sequence, double x, double y, int ray) { this(x, y, ray, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, Color)} instead. */ @Deprecated public Anchor2D(Sequence sequence, Point2D position, Color color) { this(position.getX(), position.getY(), DEFAULT_RAY, color, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, int)} instead. */ @Deprecated public Anchor2D(Sequence sequence, Point2D position, int ray) { this(position.getX(), position.getY(), ray, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, int)} instead. */ @Deprecated public Anchor2D(Sequence sequence, double x, double y, Color color, Color selectedColor) { this(x, y, DEFAULT_RAY, color, selectedColor); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, Color, Color)} instead. */ @Deprecated public Anchor2D(Sequence sequence, Point2D position, Color color, Color selectedColor) { this(position.getX(), position.getY(), DEFAULT_RAY, color, selectedColor); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double, Color, Color)} instead. */ @Deprecated public Anchor2D(Sequence sequence, double x, double y, Color color) { this(x, y, DEFAULT_RAY, color, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double)} instead. */ @Deprecated public Anchor2D(Sequence sequence, double x, double y) { this(sequence, x, y, DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D(double, double)} instead. */ @Deprecated public Anchor2D(Sequence sequence, Point2D position) { this(position.getX(), position.getY(), DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } /** * @deprecated Use {@link Anchor2D#Anchor2D()} instead. */ @Deprecated public Anchor2D(Sequence sequence) { this(0d, 0d, DEFAULT_RAY, DEFAULT_NORMAL_COLOR, DEFAULT_SELECTED_COLOR); sequence.addOverlay(this); } @Override protected void finalize() throws Throwable { super.finalize(); // release allocated VTK resources if (vtkSource != null) vtkSource.Delete(); if (actor != null) { actor.SetPropertyKeys(null); actor.Delete(); } if (vtkInfo != null) { vtkInfo.Remove(VtkCanvas.visibilityKey); vtkInfo.Delete(); } if (polyMapper != null) polyMapper.Delete(); } /** * @return the x */ public double getX() { return position.x; } /** * @param x * the x to set */ public void setX(double x) { setPosition(x, position.y); } /** * @return the y */ public double getY() { return position.y; } /** * @param y * the y to set */ public void setY(double y) { setPosition(position.x, y); } /** * Get anchor position (return the internal reference) */ public Point2D getPositionInternal() { return position; } public Point2D getPosition() { return new Point2D.Double(position.x, position.y); } public double getPositionX() { return position.x; } public double getPositionY() { return position.y; } public void moveTo(Point2D p) { setPosition(p.getX(), p.getY()); } public void moveTo(double x, double y) { setPosition(x, y); } public void setPosition(Point2D p) { setPosition(p.getX(), p.getY()); } public void setPosition(double x, double y) { if ((position.x != x) || (position.y != y)) { position.x = x; position.y = y; needRebuild = true; positionChanged(); painterChanged(); } } public void translate(double dx, double dy) { setPosition(position.x + dx, position.y + dy); } /** * @return Z position (-1 = ALL) */ public int getZ() { return z; } /** * Set Z position (-1 = ALL) */ public void setZ(int value) { final int v; // special value for infinite dimension --> change to -1 if (value == Integer.MIN_VALUE) v = -1; else v = value; if (this.z != v) { this.z = v; needRebuild = true; painterChanged(); // FIXME: do this really need to send a position change event ? positionChanged(); } } /** * @return the ray */ public int getRay() { return ray; } /** * @param value * the ray to set */ public void setRay(int value) { if (ray != value) { ray = value; needRebuild = true; painterChanged(); } } /** * @return the color */ public Color getColor() { return color; } /** * @param value * the color to set */ public void setColor(Color value) { if (color != value) { color = value; needPropertiesUpdate = true; propertyChanged(PROPERTY_COLOR); painterChanged(); } } /** * @return the selectedColor */ public Color getSelectedColor() { return selectedColor; } /** * @param value * the selectedColor to set */ public void setSelectedColor(Color value) { if (selectedColor != value) { selectedColor = value; needPropertiesUpdate = true; propertyChanged(PROPERTY_SELECTEDCOLOR); painterChanged(); } } /** * @return the selected */ public boolean isSelected() { return selected; } /** * @param value * the selected to set */ public void setSelected(boolean value) { if (selected != value) { selected = value; // end drag if (!value) startDragMousePosition = null; needPropertiesUpdate = true; propertyChanged(PROPERTY_SELECTED); painterChanged(); } } // /** // * @return the canRemove // */ // public boolean isCanRemove() // { // return canRemove; // } // // /** // * @param value // * the canRemove to set // */ // public void setCanRemove(boolean value) // { // canRemove = value; // } /** * @return the visible */ public boolean isVisible() { return visible; } /** * @param value * the visible to set */ public void setVisible(boolean value) { if (visible != value) { visible = value; needPropertiesUpdate = true; painterChanged(); } } protected void initVtkObjects() { // init 3D painters stuff vtkSource = new vtkSphereSource(); vtkSource.SetRadius(getRay()); vtkSource.SetThetaResolution(12); vtkSource.SetPhiResolution(12); polyMapper = new vtkPolyDataMapper(); polyMapper.SetInputConnection((vtkSource).GetOutputPort()); actor = new vtkActor(); actor.SetMapper(polyMapper); // use vtkInformations to store outline visibility state (hacky) vtkInfo = new vtkInformation(); vtkInfo.Set(VtkCanvas.visibilityKey, 0); // VtkCanvas use this to restore correctly outline visibility flag actor.SetPropertyKeys(vtkInfo); // initialize color final Color col = getColor(); actor.GetProperty().SetColor(col.getRed() / 255d, col.getGreen() / 255d, col.getBlue() / 255d); } /** * update 3D painter for 3D canvas (called only when VTK is loaded). */ protected boolean rebuildVtkObjects() { final VtkCanvas canvas = canvas3d.get(); // canvas was closed if (canvas == null) return false; final IcyVtkPanel vtkPanel = canvas.getVtkPanel(); // canvas was closed if (vtkPanel == null) return false; final Sequence seq = canvas.getSequence(); // nothing to update if (seq == null) return false; final Point2D pos = getPosition(); double curZ = getZ(); // all slices ? if (curZ == -1d) // set object at middle of the volume curZ = seq.getSizeZ() / 2d; // actor can be accessed in canvas3d for rendering so we need to synchronize access vtkPanel.lock(); try { // need to handle scaling on radius and position to keep a "round" sphere (else we obtain ellipsoid) vtkSource.SetCenter(pos.getX() * scaling[0], pos.getY() * scaling[1], (curZ + 0.5d) * scaling[2]); polyMapper.Update(); // actor.SetScale(scaling); } finally { vtkPanel.unlock(); } // need to repaint painterChanged(); return true; } protected void updateVtkDisplayProperties() { if (actor == null) return; final VtkCanvas cnv = canvas3d.get(); final Color col = isSelected() ? getSelectedColor() : getColor(); final double r = col.getRed() / 255d; final double g = col.getGreen() / 255d; final double b = col.getBlue() / 255d; // final float opacity = getOpacity(); final IcyVtkPanel vtkPanel = (cnv != null) ? cnv.getVtkPanel() : null; // we need to lock canvas as actor can be accessed during rendering if (vtkPanel != null) vtkPanel.lock(); try { actor.GetProperty().SetColor(r, g, b); if (isVisible()) { actor.SetVisibility(1); vtkInfo.Set(VtkCanvas.visibilityKey, 1); } else { actor.SetVisibility(0); vtkInfo.Set(VtkCanvas.visibilityKey, 0); } } finally { if (vtkPanel != null) vtkPanel.unlock(); } // need to repaint painterChanged(); } protected void updateVtkRadius() { final VtkCanvas canvas = canvas3d.get(); // canvas was closed if (canvas == null) return; final IcyVtkPanel vtkPanel = canvas.getVtkPanel(); // canvas was closed if (vtkPanel == null) return; if (vtkSource == null) return; // update sphere radius base on canvas scale X final double radius = getAdjRay(canvas) * scaling[0]; if (vtkSource.GetRadius() != radius) { // actor can be accessed in canvas3d for rendering so we need to synchronize access vtkPanel.lock(); try { vtkSource.SetRadius(radius); vtkSource.Update(); } finally { vtkPanel.unlock(); } // need to repaint painterChanged(); } } @Override public vtkProp[] getProps() { // initialize VTK objects if not yet done if (actor == null) initVtkObjects(); return new vtkActor[] {actor}; } /** * Returns <code>true</code> if specified Point3D is over the anchor in the specified canvas */ public boolean isOver(IcyCanvas canvas, Point2D imagePoint) { updateEllipseForCanvas(canvas); // specific VTK canvas processing if (canvas instanceof VtkCanvas) { // faster to use picked object return (actor != null) && (actor == ((VtkCanvas) canvas).getPickedObject()); } // at this point we need image position if (imagePoint == null) return false; // fast contains test to start with if (ellipse.getBounds2D().contains(imagePoint)) return ellipse.contains(imagePoint); return false; } public boolean isOver(IcyCanvas canvas, double x, double y) { return isOver(canvas, new Point2D.Double(x, y)); } protected double getAdjRay(IcyCanvas canvas) { // assume X dimension is ok here return canvas.canvasToImageLogDeltaX(ray); } /** * Update internal ellipse for specified Canvas */ protected void updateEllipseForCanvas(IcyCanvas canvas) { // specific VTK canvas processing if (canvas instanceof VtkCanvas) { // 3D canvas final VtkCanvas cnv = (VtkCanvas) canvas; // update reference if needed if (canvas3d.get() != cnv) canvas3d = new WeakReference<VtkCanvas>(cnv); // FIXME : need a better implementation final double[] s = cnv.getVolumeScale(); // scaling changed ? if (!Arrays.equals(scaling, s)) { // update scaling scaling = s; // need rebuild needRebuild = true; } if (needRebuild) { // initialize VTK objects if not yet done if (actor == null) initVtkObjects(); // request rebuild 3D objects ThreadUtil.runSingle(this); needRebuild = false; } if (needPropertiesUpdate) { updateVtkDisplayProperties(); needPropertiesUpdate = false; } // update sphere radius updateVtkRadius(); } else { final double adjRay = getAdjRay(canvas); ellipse.setFrame(position.x - adjRay, position.y - adjRay, adjRay * 2, adjRay * 2); } } protected boolean updateDrag(InputEvent e, double x, double y) { // not dragging --> exit if (startDragMousePosition == null) return false; double dx = x - startDragMousePosition.getX(); double dy = y - startDragMousePosition.getY(); // shift action --> limit to one direction if (EventUtil.isShiftDown(e)) { // X drag if (Math.abs(dx) > Math.abs(dy)) dy = 0; // Y drag else dx = 0; } // set new position setPosition(startDragPainterPosition.getX() + dx, startDragPainterPosition.getY() + dy); return true; } protected boolean updateDrag(InputEvent e, Point2D pt) { return updateDrag(e, pt.getX(), pt.getY()); } /** * called when anchor position has changed */ protected void positionChanged() { updater.changed(new Anchor2DEvent(this)); } @SuppressWarnings("deprecation") @Override public void onChanged(CollapsibleEvent object) { if (object instanceof Anchor2DEvent) { firePositionChangedEvent(((Anchor2DEvent) object).getSource()); return; } // provide event backward compatibility if (object instanceof OverlayEvent) { final OverlayEvent event = (OverlayEvent) object; if (event.getType() == OverlayEventType.PAINTER_CHANGED) { final PainterEvent pe = new PainterEvent(this, PainterEventType.PAINTER_CHANGED); for (Anchor2DListener listener : new ArrayList<Anchor2DListener>(anchor2Dlisteners)) listener.painterChanged(pe); } } super.onChanged(object); } protected void firePositionChangedEvent(Anchor2D source) { for (Anchor2DPositionListener listener : new ArrayList<Anchor2DPositionListener>(anchor2DPositionlisteners)) listener.positionChanged(source); // backward compatibility for (Anchor2DListener listener : new ArrayList<Anchor2DListener>(anchor2Dlisteners)) listener.positionChanged(source); } public void addPositionListener(Anchor2DPositionListener listener) { anchor2DPositionlisteners.add(listener); } public void removePositionListener(Anchor2DPositionListener listener) { anchor2DPositionlisteners.remove(listener); } /** * @deprecated Use {@link #addPositionListener(Anchor2DPositionListener)} or * {@link #addOverlayListener(OverlayListener)} instead. */ @Deprecated public void addAnchorListener(Anchor2DListener listener) { anchor2Dlisteners.add(listener); } /** * @deprecated Use {@link #removePositionListener(Anchor2DPositionListener)} or * {@link #removeOverlayListener(OverlayListener)} instead. */ @Deprecated public void removeAnchorListener(Anchor2DListener listener) { anchor2Dlisteners.remove(listener); } /** * @deprecated Use {@link #addPositionListener(Anchor2DPositionListener)} or * {@link #addOverlayListener(OverlayListener)} instead. */ @Deprecated public void addListener(Anchor2DListener listener) { addAnchorListener(listener); } /** * @deprecated Use {@link #removePositionListener(Anchor2DPositionListener)} or * {@link #removeOverlayListener(OverlayListener)} instead. */ @Deprecated public void removeListener(Anchor2DListener listener) { removeAnchorListener(listener); } public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas, boolean simplified) { // this will update VTK objects if needed updateEllipseForCanvas(canvas); if (canvas instanceof IcyCanvas2D) { // nothing to do here when not visible if (!isVisible()) return; // get canvas Z position final int cnvZ = canvas.getPositionZ(); final int z = getZ(); boolean sameZPos = ((z == -1) || (cnvZ == -1) || (z == cnvZ)); if (sameZPos) { // trivial paint optimization if (ShapeUtil.isVisible(g, ellipse)) { final Graphics2D g2 = (Graphics2D) g.create(); // draw content if (isSelected()) g2.setColor(getSelectedColor()); else g2.setColor(getColor()); // simplified small drawing if (simplified) { final int ray = (int) canvas.canvasToImageDeltaX(2); g2.fillRect((int) getPositionX() - ray, (int) getPositionY() - ray, ray * 2, ray * 2); } // normal drawing else { // draw ellipse content g2.fill(ellipse); // draw black border g2.setStroke(new BasicStroke((float) (getAdjRay(canvas) / 8f))); g2.setColor(Color.black); g2.draw(ellipse); } g2.dispose(); } } } else if (canvas instanceof VtkCanvas) { // nothing to do here } } @Override public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) { paint(g, sequence, canvas, false); } /* * only for backward compatibility */ @Deprecated @Override public void keyPressed(KeyEvent e, Point2D imagePoint, IcyCanvas canvas) { if (!isVisible()) return; // VtkCanvas not handled here if (canvas instanceof VtkCanvas) return; // no image position --> exit if (imagePoint == null) return; // just for the shift key state change updateDrag(e, imagePoint); } /* * only for backward compatibility */ @Deprecated @Override public void keyReleased(KeyEvent e, Point2D imagePoint, IcyCanvas canvas) { if (!isVisible()) return; // VtkCanvas not handled here if (canvas instanceof VtkCanvas) return; // no image position --> exit if (imagePoint == null) return; // just for the shift key state change updateDrag(e, imagePoint); } /* * only for backward compatibility */ @Deprecated @Override public void mousePressed(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { if (!isVisible()) return; if (e.isConsumed()) return; // VtkCanvas not handled here if (canvas instanceof VtkCanvas) return; // no image position --> exit if (imagePoint == null) return; if (EventUtil.isLeftMouseButton(e)) { // consume event to activate drag if (isSelected()) { startDragMousePosition = imagePoint; startDragPainterPosition = getPosition(); e.consume(); } } } /* * only for backward compatibility */ @Deprecated @Override public void mouseReleased(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { startDragMousePosition = null; } /* * only for backward compatibility */ @Deprecated @Override public void mouseDrag(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { if (!isVisible()) return; if (e.isConsumed()) return; // VtkCanvas not handled here if (canvas instanceof VtkCanvas) return; // no image position --> exit if (imagePoint == null) return; if (EventUtil.isLeftMouseButton(e)) { // if selected then move according to mouse position if (isSelected()) { // force start drag if not already the case if (startDragMousePosition == null) { startDragMousePosition = getPosition(); startDragPainterPosition = getPosition(); } updateDrag(e, imagePoint); e.consume(); } } } /* * only for backward compatibility */ @Deprecated @Override public void mouseMove(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { if (!isVisible()) return; // VtkCanvas not handled here if (canvas instanceof VtkCanvas) return; // no image position --> exit if (imagePoint == null) return; // already consumed, no selection possible if (e.isConsumed()) setSelected(false); else { final boolean overlapped = isOver(canvas, imagePoint.getX(), imagePoint.getY()); setSelected(overlapped); // so we can only have one selected at once if (overlapped) e.consume(); } } @Override public void keyPressed(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { if (!isVisible()) return; // no image position --> exit if (imagePoint == null) return; // just for the shift key state change updateDrag(e, imagePoint.x, imagePoint.y); } @Override public void keyReleased(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { if (!isVisible()) return; // no image position --> exit if (imagePoint == null) return; // just for the shift key state change updateDrag(e, imagePoint.x, imagePoint.y); } @Override public void mousePressed(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { if (!isVisible()) return; if (e.isConsumed()) return; // no image position --> exit if (imagePoint == null) return; if (EventUtil.isLeftMouseButton(e)) { // consume event to activate drag if (isSelected()) { startDragMousePosition = imagePoint.toPoint2D(); startDragPainterPosition = getPosition(); e.consume(); } } } @Override public void mouseReleased(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { startDragMousePosition = null; } @Override public void mouseDrag(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { if (!isVisible()) return; if (e.isConsumed()) return; // no image position --> exit if (imagePoint == null) return; if (EventUtil.isLeftMouseButton(e)) { // if selected then move according to mouse position if (isSelected()) { // force start drag if not already the case if (startDragMousePosition == null) { startDragMousePosition = getPosition(); startDragPainterPosition = getPosition(); } updateDrag(e, imagePoint.x, imagePoint.y); e.consume(); } } } @Override public void mouseMove(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { if (!isVisible()) return; // already consumed, no selection possible if (e.isConsumed()) setSelected(false); else { final boolean overlapped = isOver(canvas, (imagePoint != null) ? imagePoint.toPoint2D() : null); setSelected(overlapped); // so we can only have one selected at once if (overlapped) e.consume(); } } @Override public void run() { rebuildVtkObjects(); } public boolean loadPositionFromXML(Node node) { if (node == null) return false; beginUpdate(); try { setX(XMLUtil.getElementDoubleValue(node, ID_POS_X, 0d)); setY(XMLUtil.getElementDoubleValue(node, ID_POS_Y, 0d)); } finally { endUpdate(); } return true; } public boolean savePositionToXML(Node node) { if (node == null) return false; XMLUtil.setElementDoubleValue(node, ID_POS_X, getX()); XMLUtil.setElementDoubleValue(node, ID_POS_Y, getY()); return true; } @Override public boolean loadFromXML(Node node) { if (node == null) return false; beginUpdate(); try { setColor(new Color(XMLUtil.getElementIntValue(node, ID_COLOR, DEFAULT_NORMAL_COLOR.getRGB()))); setSelectedColor( new Color(XMLUtil.getElementIntValue(node, ID_SELECTEDCOLOR, DEFAULT_SELECTED_COLOR.getRGB()))); setX(XMLUtil.getElementDoubleValue(node, ID_POS_X, 0d)); setY(XMLUtil.getElementDoubleValue(node, ID_POS_Y, 0d)); setRay(XMLUtil.getElementIntValue(node, ID_RAY, DEFAULT_RAY)); setVisible(XMLUtil.getElementBooleanValue(node, ID_VISIBLE, true)); } finally { endUpdate(); } return true; } @Override public boolean saveToXML(Node node) { if (node == null) return false; XMLUtil.setElementIntValue(node, ID_COLOR, getColor().getRGB()); XMLUtil.setElementIntValue(node, ID_SELECTEDCOLOR, getSelectedColor().getRGB()); XMLUtil.setElementDoubleValue(node, ID_POS_X, getX()); XMLUtil.setElementDoubleValue(node, ID_POS_Y, getY()); XMLUtil.setElementIntValue(node, ID_RAY, getRay()); XMLUtil.setElementBooleanValue(node, ID_VISIBLE, isVisible()); return true; } }