/* * 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 plugins.kernel.roi.roi2d; import java.awt.Color; import java.awt.Graphics2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; 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.Anchor2D; 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.point.Point5D; 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 2D Point class.<br> * Define a single point ROI<br> * * @author Stephane Dallongeville */ public class ROI2DPoint extends ROI2DShape { public class ROI2DPointPainter extends ROI2DShapePainter { 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 (Anchor2D pt : controlPoints) pt.paint(g2, sequence, canvas); } } else { final Point2D pos = getPoint(); final double ray = getAdjustedStroke(canvas); final Ellipse2D ellipse = new Ellipse2D.Double(pos.getX() - ray, pos.getY() - ray, ray * 2, ray * 2); // 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 Point2D pos = getPoint(); double curZ = getZ(); // all slices ? if (curZ == -1) // 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.SetRadius(getStroke() * scaling[0]); vtkSource.SetCenter(pos.getX() * scaling[0], pos.getY() * scaling[1], (curZ + 0.5d) * 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 Anchor2D position; /** * @deprecated */ @Deprecated public ROI2DPoint(Point2D pt, boolean cm) { this(pt); } public ROI2DPoint(Point2D position) { super(new Line2D.Double()); this.position = createAnchor(position); this.position.setSelected(true); addPoint(this.position); // set icon (default name is defined by getDefaultName()) setIcon(ResourceUtil.ICON_ROI_POINT); } /** * Generic constructor for interactive mode */ public ROI2DPoint(Point5D pt) { this(pt.toPoint2D()); // getOverlay().setMousePos(pt); } public ROI2DPoint(double x, double y) { this(new Point2D.Double(x, y)); } public ROI2DPoint() { this(new Point2D.Double()); } @Override public String getDefaultName() { return "Point2D"; } @Override protected ROI2DShapePainter createPainter() { return new ROI2DPointPainter(); } /** * @deprecated Use {@link #getLine()} instead. */ @Deprecated public Rectangle2D getRectangle() { final Point2D pt = getPoint(); return new Rectangle2D.Double(pt.getX(), pt.getY(), 0d, 0d); } public Line2D getLine() { return (Line2D) shape; } public Point2D 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(double x, double y) { return false; } @Override public boolean contains(Point2D p) { return false; } @Override public boolean contains(double x, double y, double w, double h) { return false; } @Override public boolean contains(Rectangle2D r) { return false; } @Override public boolean contains(ROI roi) { return false; } @Override public boolean intersects(ROI r) { // special case of ROI2DPoint if (r instanceof ROI2DPoint) return onSamePos(((ROI2DPoint) r), false) && ((ROI2DPoint) r).getPoint().equals(getPoint()); return super.intersects(r); } /** * 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)) ((ROI2DShapePainter) 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 Point2D pt = getPoint(); final double x = pt.getX(); final double y = pt.getY(); getLine().setLine(x, y, x, y); // 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, Anchor2D 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; } }