001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package plugins.kernel.roi.roi2d;
020
021import icy.painter.Anchor2D;
022import icy.painter.LineAnchor2D;
023import icy.resource.ResourceUtil;
024import icy.roi.ROI;
025import icy.type.geom.Polyline2D;
026import icy.type.point.Point2DUtil;
027import icy.type.point.Point5D;
028import icy.util.XMLUtil;
029
030import java.awt.Color;
031import java.awt.Graphics2D;
032import java.awt.Polygon;
033import java.awt.geom.Path2D;
034import java.awt.geom.Point2D;
035import java.awt.geom.Rectangle2D;
036import java.awt.image.BufferedImage;
037import java.awt.image.DataBufferByte;
038import java.util.ArrayList;
039import java.util.List;
040
041import org.w3c.dom.Element;
042import org.w3c.dom.Node;
043
044/**
045 * @author Stephane
046 */
047public class ROI2DPolyLine extends ROI2DShape
048{
049    protected class ROI2DPolyLineAnchor2D extends LineAnchor2D
050    {
051        public ROI2DPolyLineAnchor2D(Point2D position, Color color, Color selectedColor)
052        {
053            super(position, color, selectedColor);
054        }
055
056        @Override
057        protected Anchor2D getPreviousPoint()
058        {
059            final int ind = controlPoints.indexOf(this);
060
061            if (ind == 0)
062            {
063                if (controlPoints.size() > 1)
064                    return controlPoints.get(1);
065
066                return null;
067            }
068
069            if (ind != -1)
070                return controlPoints.get(ind - 1);
071
072            return null;
073        }
074    }
075
076    public static final String ID_POINTS = "points";
077    public static final String ID_POINT = "point";
078
079    
080
081    /**
082     * @deprecated
083     */
084    @Deprecated
085    public ROI2DPolyLine(Point2D pt, boolean cm)
086    {
087        this(pt);
088    }
089
090    /**
091     * 
092     */
093    public ROI2DPolyLine(Point2D pt)
094    {
095        super(new Polyline2D());
096
097        final Anchor2D point = createAnchor(pt);
098        point.setSelected(true);
099        addPoint(point);
100
101        // set icon (default name is defined by getDefaultName()) 
102        setIcon(ResourceUtil.ICON_ROI_POLYLINE);
103    }
104
105    /**
106     * Generic constructor for interactive mode
107     */
108    public ROI2DPolyLine(Point5D pt)
109    {
110        this(pt.toPoint2D());
111        // getOverlay().setMousePos(pt);
112    }
113
114    public ROI2DPolyLine(Polygon polygon)
115    {
116        this(new Point2D.Double());
117
118        setPolygon(polygon);
119    }
120
121    public ROI2DPolyLine(Polyline2D polyline)
122    {
123        this(new Point2D.Double());
124
125        setPolyline2D(polyline);
126    }
127
128    public ROI2DPolyLine(List<Point2D> points)
129    {
130        this(new Point2D.Double());
131
132        setPoints(points);
133    }
134
135    public ROI2DPolyLine()
136    {
137        this(new Point2D.Double());
138    }
139    
140    @Override
141    public String getDefaultName()
142    {
143        return "PolyLine2D";
144    }
145
146    @Override
147    protected Anchor2D createAnchor(Point2D pos)
148    {
149        return new ROI2DPolyLineAnchor2D(pos, getColor(), getFocusedColor());
150    }
151
152    // @Override
153    // protected ROI2DPolyLinePainter createPainter()
154    // {
155    // return new ROI2DPolyLinePainter();
156    // }
157
158    /**
159     * @deprecated Use {@link #getPolyline2D()} instead
160     */
161    @Deprecated
162    protected Path2D getPath()
163    {
164        return new Path2D.Double(shape);
165    }
166
167    public void setPoints(List<Point2D> pts)
168    {
169        beginUpdate();
170        try
171        {
172            removeAllPoint();
173            for (Point2D pt : pts)
174                addNewPoint(pt, false);
175        }
176        finally
177        {
178            endUpdate();
179        }
180    }
181
182    /**
183     * @deprecated Use {@link #setPoints(List)} instead.
184     */
185    @Deprecated
186    public void setPoints(ArrayList<Point2D> pts)
187    {
188        setPoints((List<Point2D>) pts);
189    }
190
191    public Polyline2D getPolyline2D()
192    {
193        return (Polyline2D) shape;
194    }
195
196    public void setPolyline2D(Polyline2D polyline2D)
197    {
198        beginUpdate();
199        try
200        {
201            removeAllPoint();
202            for (int i = 0; i < polyline2D.npoints; i++)
203                addNewPoint(new Point2D.Double(polyline2D.xpoints[i], polyline2D.ypoints[i]), false);
204        }
205        finally
206        {
207            endUpdate();
208        }
209    }
210
211    public Polygon getPolygon()
212    {
213        return getPolyline2D().getPolygon2D().getPolygon();
214    }
215
216    public void setPolygon(Polygon polygon)
217    {
218        beginUpdate();
219        try
220        {
221            removeAllPoint();
222            for (int i = 0; i < polygon.npoints; i++)
223                addNewPoint(new Point2D.Double(polygon.xpoints[i], polygon.ypoints[i]), false);
224        }
225        finally
226        {
227            endUpdate();
228        }
229    }
230
231    @Override
232    public boolean contains(double x, double y)
233    {
234        return false;
235    }
236
237    @Override
238    public boolean contains(Point2D p)
239    {
240        return false;
241    }
242
243    @Override
244    public boolean contains(double x, double y, double w, double h)
245    {
246        return false;
247    }
248
249    @Override
250    public boolean contains(Rectangle2D r)
251    {
252        return false;
253    }
254
255    @Override
256    public boolean contains(ROI roi)
257    {
258        return false;
259    }
260
261    @Override
262    protected double getTotalDistance(List<Point2D> points, double factorX, double factorY)
263    {
264        // for polyline the total length don't need last point connection
265        return Point2DUtil.getTotalDistance(points, factorX, factorY, false);
266    }
267
268    @Override
269    public double computeNumberOfPoints()
270    {
271        return 0d;
272    }
273
274    @Override
275    protected void updateShape()
276    {
277        final int len;
278        final double[] ptsX;
279        final double[] ptsY;
280
281        synchronized (controlPoints)
282        {
283            len = controlPoints.size();
284            ptsX = new double[len];
285            ptsY = new double[len];
286
287            for (int i = 0; i < len; i++)
288            {
289                final Anchor2D pt = controlPoints.get(i);
290
291                ptsX[i] = pt.getX();
292                ptsY[i] = pt.getY();
293            }
294        }
295
296        final Polyline2D polyline2d = getPolyline2D();
297
298        // we can have a problem here if we try to redraw while we are modifying the polygon points
299        synchronized (polyline2d)
300        {
301            polyline2d.npoints = len;
302            polyline2d.xpoints = ptsX;
303            polyline2d.ypoints = ptsY;
304            polyline2d.calculatePath();
305        }
306
307        // call super method after shape has been updated
308        super.updateShape();
309    }
310
311    @Override
312    public boolean[] getBooleanMask(int x, int y, int w, int h, boolean inclusive)
313    {
314        if ((w <= 0) || (h <= 0))
315            return new boolean[0];
316
317        // this ROI doesn't contains area
318        if (!inclusive)
319            return new boolean[w * h];
320
321        final BufferedImage maskImg = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
322        final Graphics2D g = maskImg.createGraphics();
323
324        // draw shape in image
325        g.setColor(Color.white);
326        g.translate(-x, -y);
327        g.draw(shape);
328        g.dispose();
329
330        // use the image to define the mask
331        final byte[] maskData = ((DataBufferByte) maskImg.getRaster().getDataBuffer()).getData();
332        final boolean[] result = new boolean[w * h];
333
334        for (int i = 0; i < result.length; i++)
335            result[i] = (maskData[i] != 0);
336
337        return result;
338    }
339
340    @Override
341    public boolean loadFromXML(Node node)
342    {
343        beginUpdate();
344        try
345        {
346            if (!super.loadFromXML(node))
347                return false;
348
349            removeAllPoint();
350
351            final List<Node> pointsNode = XMLUtil.getChildren(XMLUtil.getElement(node, ID_POINTS), ID_POINT);
352            if (pointsNode != null)
353            {
354                for (Node n : pointsNode)
355                {
356                    final Anchor2D pt = createAnchor(new Point2D.Double());
357                    pt.loadPositionFromXML(n);
358                    addPoint(pt);
359                }
360            }
361        }
362        finally
363        {
364            endUpdate();
365        }
366
367        return true;
368    }
369
370    @Override
371    public boolean saveToXML(Node node)
372    {
373        if (!super.saveToXML(node))
374            return false;
375
376        final Element pointsNode = XMLUtil.setElement(node, ID_POINTS);
377        synchronized (controlPoints)
378        {
379            for (Anchor2D pt : controlPoints)
380                pt.savePositionToXML(XMLUtil.addElement(pointsNode, ID_POINT));
381        }
382
383        return true;
384    }
385}