/* * Copyright 2010-2013 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 icy.canvas.IcyCanvas; import icy.canvas.IcyCanvas2D; import icy.painter.Anchor2D; import icy.painter.LineAnchor2D; import icy.resource.ResourceUtil; import icy.roi.ROI; import icy.sequence.Sequence; import icy.system.thread.ThreadUtil; import icy.type.point.Point5D; import icy.util.GraphicsUtil; import icy.util.ShapeUtil; import icy.util.XMLUtil; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.w3c.dom.Element; import org.w3c.dom.Node; import plugins.kernel.canvas.VtkCanvas; import vtk.vtkActor; /** * @author Stephane */ public class ROI2DPolyLine extends ROI2DShape { protected class ROI2DPolyLineAnchor2D extends LineAnchor2D { public ROI2DPolyLineAnchor2D(Point2D position, Color color, Color selectedColor) { super(position, color, selectedColor); } @Override protected Anchor2D getPreviousPoint() { final int ind = controlPoints.indexOf(this); if (ind == 0) { if (controlPoints.size() > 1) return controlPoints.get(1); return null; } if (ind != -1) return controlPoints.get(ind - 1); return null; } } public static final String ID_POINTS = "points"; public static final String ID_POINT = "point"; public class ROI2DPolyLinePainter extends ROI2DShapePainter { @Override protected void drawROI(Graphics2D g, Sequence sequence, IcyCanvas canvas) { if (canvas instanceof IcyCanvas2D) { final Graphics2D g2 = (Graphics2D) g.create(); final Rectangle2D bounds = shape.getBounds2D(); // trivial paint optimization final boolean shapeVisible = GraphicsUtil.isVisible(g2, bounds); final boolean small; final boolean tiny; // disable LOD when creating the ROI if (isCreating()) { small = false; tiny = false; } else { final AffineTransform trans = g.getTransform(); final double scale = Math.max(trans.getScaleX(), trans.getScaleY()); final double size = Math.max(scale * bounds.getWidth(), scale * bounds.getHeight()); small = size < LOD_SMALL; tiny = size < LOD_TINY; } // simplified draw if (small) { // trivial paint optimization if (shapeVisible) { // draw shape (simplified version) g2.setStroke(new BasicStroke((float) ROI.getAdjustedStroke(canvas, stroke))); g2.setColor(getDisplayColor()); g2.draw(shape); if (!tiny) { // draw simplified control points if (isSelected() && !isReadOnly()) { final int ray = (int) canvas.canvasToImageDeltaX(2); for (Anchor2D pt : controlPoints) { if (pt.isVisible()) { if (pt.isSelected()) g2.setColor(pt.getSelectedColor()); else g2.setColor(pt.getColor()); g2.fillRect((int) pt.getPositionX() - ray, (int) pt.getPositionY() - ray, ray * 2, ray * 2); } } } } } } // normal draw else { if (shapeVisible) { if (isSelected()) { // just draw plain object shape without border g2.setStroke(new BasicStroke((float) ROI.getAdjustedStroke(canvas, stroke + 1d))); g2.setColor(getDisplayColor()); g2.draw(shape); } else { // draw border g2.setStroke(new BasicStroke((float) ROI.getAdjustedStroke(canvas, stroke + 1d))); g2.setColor(Color.black); g2.draw(shape); // draw shape g2.setStroke(new BasicStroke((float) ROI.getAdjustedStroke(canvas, stroke))); g2.setColor(getDisplayColor()); g2.draw(shape); } } if (isSelected() && !isReadOnly()) { // draw control point if selected for (Anchor2D pt : controlPoints) pt.paint(g2, sequence, canvas); } } g2.dispose(); } if (canvas instanceof VtkCanvas) { // 3D canvas final VtkCanvas cnv = (VtkCanvas) canvas; // 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; } // need to rebuild 3D data structures ? if (needRebuild) { // initialize VTK objects if not yet done if (actor == null) initVtkObjects(); // request rebuild 3D objects canvas3d = cnv; ThreadUtil.runSingle(this); needRebuild = false; } // actor can be accessed in canvas3d for rendering so we need to synchronize access cnv.lock(); try { // update visibility if (actor != null) ((vtkActor) actor).SetVisibility(canvas.isVisible(this) ? 1 : 0); } finally { cnv.unlock(); } } } } /** * @deprecated */ @Deprecated public ROI2DPolyLine(Point2D pt, boolean cm) { this(pt); } /** * */ public ROI2DPolyLine(Point2D pt) { super(new Path2D.Double()); // add points to list final Anchor2D anchor = createAnchor(pt); // just add the new point at last position addPoint(anchor); // always select anchor.setSelected(true); // getOverlay().setMousePos(new Point5D.Double(pt.getX(), pt.getY(), -1d, -1d, -1d)); updateShape(); // set name and icon setName("PolyLine2D"); setIcon(ResourceUtil.ICON_ROI_POLYLINE); } /** * Generic constructor for interactive mode */ public ROI2DPolyLine(Point5D pt) { this(pt.toPoint2D()); // getOverlay().setMousePos(pt); } public ROI2DPolyLine(Polygon polygon) { this(new Point2D.Double()); setPolygon(polygon); } public ROI2DPolyLine(List<Point2D> points) { this(new Point2D.Double()); setPoints(points); } public ROI2DPolyLine() { this(new Point2D.Double()); } @Override protected Anchor2D createAnchor(Point2D pos) { return new ROI2DPolyLineAnchor2D(pos, getColor(), getFocusedColor()); } @Override protected ROI2DPolyLinePainter createPainter() { return new ROI2DPolyLinePainter(); } protected Path2D getPath() { return (Path2D) shape; } public void setPoints(List<Point2D> pts) { beginUpdate(); try { removeAllPoint(); for (Point2D pt : pts) addPoint(new Anchor2D(pt.getX(), pt.getY())); } finally { endUpdate(); } } /** * @deprecated Use {@link #setPoints(ArrayList)} instead. */ @Deprecated public void setPoints(ArrayList<Point2D> pts) { setPoints((List<Point2D>) pts); } public Polygon getPolygon() { final Polygon result = new Polygon(); for (Anchor2D point : controlPoints) result.addPoint((int) point.getX(), (int) point.getY()); return result; } public void setPolygon(Polygon polygon) { beginUpdate(); try { removeAllPoint(); final Color color = getColor(); final Color focusedColor = getFocusedColor(); for (int i = 0; i < polygon.npoints; i++) addPoint(new Anchor2D(polygon.xpoints[i], polygon.ypoints[i], color, focusedColor)); } finally { endUpdate(); } } @Override protected double getTotalDistance(List<Point2D> points) { // by default the total length don't need last point connection return super.getTotalDistance(points, false); } @Override public double computeNumberOfContourPoints() { return getTotalDistance(getPoints()); } @Override public double computeNumberOfPoints() { return 0d; } @Override protected void updateShape() { final Path2D path = getPath(); path.reset(); // initial move if (controlPoints.size() > 0) { final Point2D pos = controlPoints.get(0).getPosition(); path.moveTo(pos.getX(), pos.getY()); } // special case we have only one point if (controlPoints.size() == 1) { final Point2D pos = controlPoints.get(0).getPosition(); path.lineTo(pos.getX(), pos.getY()); } else { // lines for (int i = 1; i < controlPoints.size(); i++) { final Point2D pos = controlPoints.get(i).getPosition(); path.lineTo(pos.getX(), pos.getY()); } } // call super method after shape has been updated super.updateShape(); } @Override public boolean contains(double x, double y, double w, double h) { // this ROI doesn't contains anything return false; } @Override public boolean contains(double x, double y) { // this ROI doesn't contains anything return false; } @Override public boolean contains(Point2D p) { // this ROI doesn't contains anything return false; } @Override public boolean contains(Rectangle2D r) { // this ROI doesn't contains anything return false; } @Override public boolean intersects(double x, double y, double w, double h) { return intersects(new Rectangle2D.Double(x, y, w, h)); } @Override public boolean intersects(Rectangle2D r) { // just take care about path return ShapeUtil.pathIntersects(getPathIterator(null, 0.1), r); } @Override public boolean[] getBooleanMask(int x, int y, int w, int h, boolean inclusive) { if ((w <= 0) || (h <= 0)) return new boolean[0]; // this ROI doesn't contains area if (!inclusive) return new boolean[w * h]; final BufferedImage maskImg = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY); final Graphics2D g = maskImg.createGraphics(); // draw shape in image g.setColor(Color.white); g.translate(-x, -y); g.draw(shape); g.dispose(); // use the image to define the mask final byte[] maskData = ((DataBufferByte) maskImg.getRaster().getDataBuffer()).getData(); final boolean[] result = new boolean[w * h]; for (int i = 0; i < result.length; i++) result[i] = (maskData[i] != 0); return result; } @Override public boolean loadFromXML(Node node) { beginUpdate(); try { if (!super.loadFromXML(node)) return false; removeAllPoint(); final ArrayList<Node> nodesPoint = XMLUtil.getChildren(XMLUtil.getElement(node, ID_POINTS), ID_POINT); if (nodesPoint != null) { for (Node n : nodesPoint) { final Anchor2D pt = new Anchor2D(); pt.loadPositionFromXML(n); addPoint(pt); } } } finally { endUpdate(); } return true; } @Override public boolean saveToXML(Node node) { if (!super.saveToXML(node)) return false; final Element dependances = XMLUtil.setElement(node, ID_POINTS); for (Anchor2D pt : controlPoints) pt.savePositionToXML(XMLUtil.addElement(dependances, ID_POINT)); return true; } }