/**
*
*/
package plugins.kernel.roi.roi3d;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import org.w3c.dom.Node;
import icy.canvas.IcyCanvas;
import icy.canvas.IcyCanvas2D;
import icy.common.CollapsibleEvent;
import icy.painter.Anchor3D;
import icy.painter.OverlayEvent;
import icy.painter.OverlayEvent.OverlayEventType;
import icy.resource.ResourceUtil;
import icy.roi.ROI;
import icy.roi.ROIEvent;
import icy.sequence.Sequence;
import icy.type.geom.Line3D;
import icy.type.point.Point3D;
import icy.type.point.Point5D;
import icy.util.GraphicsUtil;
import icy.util.StringUtil;
import icy.util.XMLUtil;
import icy.vtk.IcyVtkPanel;
import plugins.kernel.canvas.VtkCanvas;
import vtk.vtkActor;
import vtk.vtkPolyDataMapper;
import vtk.vtkSphereSource;
/**
* ROI 3D Point class.<br>
*
* @author Stephane Dallongeville
*/
public class ROI3DPoint extends ROI3DShape
{
public class ROI3DPointPainter extends ROI3DShapePainter
{
vtkSphereSource vtkSource;
@Override
protected void finalize() throws Throwable
{
super.finalize();
// release extra VTK objects
if (vtkSource != null)
vtkSource.Delete();
}
@Override
protected boolean isSmall(Rectangle2D bounds, Graphics2D g, IcyCanvas canvas)
{
if (isSelected())
return false;
return true;
}
@Override
protected boolean isTiny(Rectangle2D bounds, Graphics2D g, IcyCanvas canvas)
{
if (isSelected())
return false;
return true;
}
@Override
public void drawROI(Graphics2D g, Sequence sequence, IcyCanvas canvas)
{
if (canvas instanceof IcyCanvas2D)
{
final Graphics2D g2 = (Graphics2D) g.create();
if (isSelected() && !isReadOnly())
{
// draw control point if selected
synchronized (controlPoints)
{
for (Anchor3D pt : controlPoints)
pt.paint(g2, sequence, canvas);
}
}
else
{
final Point3D pos = getPoint();
final double ray = getAdjustedStroke(canvas);
final Ellipse2D ellipse = new Ellipse2D.Double(pos.getX() - ray, pos.getY() - ray, ray * 2,
ray * 2);
// get canvas Z position
final int cnvZ = canvas.getPositionZ();
// calculate z fade range
final double zRange = Math.min(10d, Math.max(3d, sequence.getSizeZ() / 8d));
// get Z pos
final double z = pos.getZ();
// get delta Z (difference between canvas Z position and point Z pos)
final double dz = Math.abs(z - cnvZ);
// not visible on this Z position
if (dz > zRange)
return;
// ratio for size / opacity
final float ratio = 1f - (float) (dz / zRange);
if (ratio != 1f)
GraphicsUtil.mixAlpha(g2, ratio);
// draw shape
g2.setColor(getDisplayColor());
g2.fill(ellipse);
}
g2.dispose();
}
else
// just use parent method
super.drawROI(g, sequence, canvas);
}
@Override
protected void initVtkObjects()
{
// init 3D painters stuff
vtkSource = new vtkSphereSource();
vtkSource.SetRadius(getStroke());
vtkSource.SetThetaResolution(12);
vtkSource.SetPhiResolution(12);
polyMapper = new vtkPolyDataMapper();
polyMapper.SetInputConnection((vtkSource).GetOutputPort());
actor = new vtkActor();
actor.SetMapper(polyMapper);
// 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).
*/
@Override
protected void rebuildVtkObjects()
{
final VtkCanvas canvas = canvas3d.get();
// canvas was closed
if (canvas == null)
return;
final IcyVtkPanel vtkPanel = canvas.getVtkPanel();
// canvas was closed
if (vtkPanel == null)
return;
final Sequence seq = canvas.getSequence();
// nothing to update
if (seq == null)
return;
final Point3D pos = getPoint();
// 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.SetRadius(getStroke() * scaling[0]);
vtkSource.SetCenter(pos.getX() * scaling[0], pos.getY() * scaling[1], pos.getZ() * scaling[2]);
polyMapper.Update();
// vtkSource.SetRadius(getStroke());
// vtkSource.SetCenter(pos.getX(), pos.getY(), curZ);
// polyMapper.Update();
// actor.SetScale(scaling);
}
finally
{
vtkPanel.unlock();
}
// need to repaint
painterChanged();
}
@Override
protected void updateVtkDisplayProperties()
{
if (actor == null)
return;
final VtkCanvas cnv = canvas3d.get();
final Color col = getDisplayColor();
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);
}
finally
{
if (vtkPanel != null)
vtkPanel.unlock();
}
// need to repaint
painterChanged();
}
}
public static final String ID_POSITION = "position";
private final Anchor3D position;
/**
* @deprecated
*/
@Deprecated
public ROI3DPoint(Point3D pt, boolean cm)
{
this(pt);
}
public ROI3DPoint(Point3D position)
{
super(new Line3D());
this.position = createAnchor(position);
this.position.setSelected(true);
addPoint(this.position);
// set icon
setIcon(ResourceUtil.ICON_ROI_POINT);
}
/**
* Generic constructor for interactive mode
*/
public ROI3DPoint(Point5D pt)
{
this(pt.toPoint3D());
}
public ROI3DPoint(double x, double y, double z)
{
this(new Point3D.Double(x, y, z));
}
public ROI3DPoint()
{
this(new Point3D.Double());
}
@Override
public String getDefaultName()
{
return "Point3D";
}
@Override
protected ROI3DShapePainter createPainter()
{
return new ROI3DPointPainter();
}
public Line3D getLine()
{
return (Line3D) shape;
}
public Point3D getPoint()
{
return position.getPosition();
}
/**
* Called when anchor overlay changed
*/
@Override
public void controlPointOverlayChanged(OverlayEvent event)
{
// we only mind about painter change from anchor...
if (event.getType() == OverlayEventType.PAINTER_CHANGED)
{
// here we want to have ROI focused when point is selected (special case for ROIPoint)
if (hasSelectedPoint())
setFocused(true);
// anchor changed --> ROI painter changed
getOverlay().painterChanged();
}
}
@Override
public boolean contains(ROI roi)
{
return false;
}
@Override
public boolean intersects(ROI r)
{
// special case of ROI3DPoint
if (r instanceof ROI3DPoint)
return onSamePos(((ROI3DPoint) r), false) && ((ROI3DPoint) r).getPoint().equals(getPoint());
return super.intersects(r);
}
@Override
public boolean[] getBooleanMask2D(int x, int y, int width, int height, int z, boolean inclusive)
{
if ((width <= 0) || (height <= 0))
return new boolean[0];
final boolean[] result = new boolean[width * height];
// 2D bounds
final Rectangle bounds2d = new Rectangle(x, y, width, height);
final Point3D pos = getPoint();
// same Z ?
if (Math.floor(pos.getZ()) == z)
{
// inside the mask ?
if (bounds2d.contains(pos.toPoint2D()))
{
final int px = (int) Math.floor(pos.getX());
final int py = (int) Math.floor(pos.getY());
// set the pixel
result[(px - x) + ((py - y) * width)] = true;
}
}
return result;
}
/**
* roi changed
*/
@Override
public void onChanged(CollapsibleEvent object)
{
final ROIEvent event = (ROIEvent) object;
// do here global process on ROI change
switch (event.getType())
{
case PROPERTY_CHANGED:
final String property = event.getPropertyName();
// stroke changed --> rebuild vtk object
if (StringUtil.equals(property, PROPERTY_STROKE))
((ROI3DShapePainter) getOverlay()).needRebuild = true;
break;
case SELECTION_CHANGED:
// always select the control point when ROI was just selected
if (isSelected())
position.setSelected(true);
break;
default:
break;
}
super.onChanged(object);
}
@Override
protected void updateShape()
{
final Point3D pt = getPoint();
final double x = pt.getX();
final double y = pt.getY();
final double z = pt.getZ();
getLine().setLine(x, y, z, x, y, z);
// call super method after shape has been updated
super.updateShape();
}
@Override
public boolean canAddPoint()
{
// this ROI doesn't support point add
return false;
}
@Override
protected boolean removePoint(IcyCanvas canvas, Anchor3D pt)
{
if (canvas != null)
{
// remove point on this ROI remove the ROI from current sequence
canvas.getSequence().removeROI(this);
return true;
}
return false;
}
@Override
public double computeNumberOfContourPoints()
{
return 0d;
}
@Override
public double computeNumberOfPoints()
{
return 0d;
}
@Override
public boolean loadFromXML(Node node)
{
beginUpdate();
try
{
if (!super.loadFromXML(node))
return false;
position.loadPositionFromXML(XMLUtil.getElement(node, ID_POSITION));
}
finally
{
endUpdate();
}
return true;
}
@Override
public boolean saveToXML(Node node)
{
if (!super.saveToXML(node))
return false;
position.savePositionToXML(XMLUtil.setElement(node, ID_POSITION));
return true;
}
}