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.canvas.IcyCanvas;
022import icy.painter.Anchor2D;
023import icy.painter.RectAnchor2D;
024import icy.util.XMLUtil;
025
026import java.awt.Color;
027import java.awt.geom.Point2D;
028import java.awt.geom.Rectangle2D;
029import java.awt.geom.RectangularShape;
030
031import org.w3c.dom.Node;
032
033/**
034 * Base class for rectangular shape ROI.
035 * 
036 * @author Stephane
037 */
038public abstract class ROI2DRectShape extends ROI2DShape
039{
040    protected class ROI2DRectAnchor2D extends RectAnchor2D
041    {
042        public ROI2DRectAnchor2D(Point2D position, Color color, Color selectedColor)
043        {
044            super(position, color, selectedColor);
045        }
046
047        @Override
048        protected Anchor2D getOppositePoint()
049        {
050            if (this == topLeft)
051                return bottomRight;
052            if (this == topRight)
053                return bottomLeft;
054            if (this == bottomLeft)
055                return topRight;
056
057            return topLeft;
058        };
059    }
060
061    public static final String ID_TOPLEFT = "top_left";
062    public static final String ID_BOTTOMRIGHT = "bottom_right";
063
064    protected final Anchor2D topLeft;
065    protected final Anchor2D topRight;
066    protected final Anchor2D bottomLeft;
067    protected final Anchor2D bottomRight;
068
069    protected boolean internalPositionSet;
070
071    /**
072     * 
073     */
074    public ROI2DRectShape(RectangularShape shape, Point2D topLeft, Point2D bottomRight)
075    {
076        super(shape);
077
078        this.topLeft = createAnchor(topLeft);
079        this.topRight = createAnchor(new Point2D.Double(bottomRight.getX(), topLeft.getY()));
080        this.bottomLeft = createAnchor(new Point2D.Double(topLeft.getX(), bottomRight.getY()));
081        this.bottomRight = createAnchor(bottomRight);
082        // select the bottom right point by default for interactive mode
083        this.bottomRight.setSelected(true);
084
085        internalPositionSet = false;
086
087        // order is important as we compute distance from connected points
088        addPoint(this.topLeft);
089        addPoint(this.topRight);
090        addPoint(this.bottomRight);
091        addPoint(this.bottomLeft);
092    }
093
094    @Override
095    protected Anchor2D createAnchor(Point2D pos)
096    {
097        return new ROI2DRectAnchor2D(pos, getColor(), getFocusedColor());
098    }
099
100    protected RectangularShape getRectangularShape()
101    {
102        return (RectangularShape) shape;
103    }
104
105    @Override
106    public boolean canSetBounds()
107    {
108        return true;
109    }
110
111    @Override
112    public void setBounds2D(Rectangle2D bounds)
113    {
114        beginUpdate();
115        try
116        {
117            // set anchors (only 2 significants anchors need to be adjusted)
118            topLeft.setPosition(bounds.getMinX(), bounds.getMinY());
119            bottomRight.setPosition(bounds.getMaxX(), bounds.getMaxY());
120        }
121        finally
122        {
123            endUpdate();
124        }
125    }
126
127    @Override
128    protected void updateShape()
129    {
130        getRectangularShape().setFrameFromDiagonal(topLeft.getPosition(), bottomRight.getPosition());
131
132        // call super method after shape has been updated
133        super.updateShape();
134    }
135
136    @Override
137    public boolean canAddPoint()
138    {
139        // this ROI doesn't support point add
140        return false;
141    }
142
143    @Override
144    public boolean canRemovePoint()
145    {
146        // this ROI doesn't support point remove
147        return false;
148    }
149
150    @Override
151    protected boolean removePoint(IcyCanvas canvas, Anchor2D pt)
152    {
153        // this ROI doesn't support point remove
154        return false;
155    }
156
157    @Override
158    public void controlPointPositionChanged(Anchor2D source)
159    {
160        // we are modifying internally the position --> exit
161        if (internalPositionSet)
162            return;
163
164        internalPositionSet = true;
165        try
166        {
167            // adjust dependents anchors
168            if (source == topLeft)
169            {
170                bottomLeft.setX(topLeft.getX());
171                topRight.setY(topLeft.getY());
172            }
173            else if (source == topRight)
174            {
175                bottomRight.setX(topRight.getX());
176                topLeft.setY(topRight.getY());
177            }
178            else if (source == bottomLeft)
179            {
180                topLeft.setX(bottomLeft.getX());
181                bottomRight.setY(bottomLeft.getY());
182            }
183            else if (source == bottomRight)
184            {
185                topRight.setX(bottomRight.getX());
186                bottomLeft.setY(bottomRight.getY());
187            }
188        }
189        finally
190        {
191            internalPositionSet = false;
192        }
193
194        super.controlPointPositionChanged(source);
195    }
196
197    @Override
198    public void translate(double dx, double dy)
199    {
200        beginUpdate();
201        try
202        {
203            // translate (only 2 significants anchors need to be adjusted)
204            topLeft.translate(dx, dy);
205            bottomRight.translate(dx, dy);
206        }
207        finally
208        {
209            endUpdate();
210        }
211    }
212
213    @Override
214    public boolean loadFromXML(Node node)
215    {
216        beginUpdate();
217        try
218        {
219            if (!super.loadFromXML(node))
220                return false;
221
222            topLeft.loadPositionFromXML(XMLUtil.getElement(node, ID_TOPLEFT));
223            bottomRight.loadPositionFromXML(XMLUtil.getElement(node, ID_BOTTOMRIGHT));
224        }
225        finally
226        {
227            endUpdate();
228        }
229
230        return true;
231    }
232
233    @Override
234    public boolean saveToXML(Node node)
235    {
236        if (!super.saveToXML(node))
237            return false;
238
239        topLeft.savePositionToXML(XMLUtil.setElement(node, ID_TOPLEFT));
240        bottomRight.savePositionToXML(XMLUtil.setElement(node, ID_BOTTOMRIGHT));
241
242        return true;
243    }
244
245}