/*
* 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;
}
}