001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 *  image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause 
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 
006 * Copyright (c) 2006-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 
007 * Visit http://imagingbook.com for additional details.
008 *******************************************************************************/
009
010package imagingbook.pub.regions;
011
012import java.awt.Point;
013import java.awt.geom.Path2D;
014import java.util.ArrayList;
015import java.util.Iterator;
016import java.util.List;
017
018/**
019 * This class represents a closed contour as a sequence of
020 * pixel coordinates. It implements the {@link Comparable}
021 * interface for sorting contours by length.
022 * It supports iteration over the points along the contour, 
023 * e.g., by
024 * <pre>
025 * Contour C = ...;
026 * for (Point p : C) {
027 *    // process p ...
028 * }
029 * </pre>
030 * 
031 * @version 2016-11-08
032 */
033public class Contour implements Comparable<Contour>, Iterable<Point> {
034        
035        static private int INITIAL_SIZE = 50;
036        
037        private final int label;
038        private final List<Point> points;
039        
040        /**
041         * Creates a new (empty) contour with the given region label.
042         * @param label the region label for this contour.
043         */
044        public Contour (int label) {
045                this.label = label;
046                points = new ArrayList<Point>(INITIAL_SIZE);
047        }
048        
049        protected void addPoint (Point p) {
050                points.add(p);
051        }
052        
053        //--------------------- retrieve contour points -------
054        
055        /**
056         * Get the list of contour points.
057         * @return a reference to the internal list of contour points.
058         */
059        public List<Point> getPointList() {
060                return points;
061        }
062        
063        /**
064         * Get the contour points as an array.
065         * @return a new array of contour points.
066         */
067        public Point[] getPointArray() {
068                return points.toArray(new Point[0]);
069        }
070                
071        //--------------------- contour statistics ------------
072        
073        /**
074         * Get the length of the contour.
075         * @return the number of points on the contour.
076         */
077        public int getLength() {
078                return points.size();
079        }
080        
081        /**
082         * Get the region label associated with this contour.
083         * @return the region label of the contour.
084         */
085        public int getLabel() {
086                return label;
087        }
088        
089        //--------------------- debug methods ------------------
090        
091//      private void printPoints () {
092//              for (Point pt: points) {
093//                      IJ.log(pt.toString());
094//              }
095//      }
096        
097        @Override
098        public String toString(){
099                return
100                        "Contour " + label + ": " + this.getLength() + " points";
101        }
102        
103        /**
104         * Get the polygon for this contour (for subsequent drawing).
105         * @return the polygon.
106         */
107        public Path2D getPolygonPath() {
108                return getPolygonPath(0.5, 0.5);        // offset by 0.5 to pass through pixel centers
109        }
110        
111        /**
112         * Get the polygon for this contour (for subsequent drawing).
113         * An offset can be specified for shifting the contour positions
114         * at pixel centers (set to 0.5/0.5).
115         * 
116         * @param xOffset the horizontal offset.
117         * @param yOffset the vertical offset.
118         * @return a polygon.
119         */
120        public Path2D getPolygonPath(double xOffset, double yOffset) {
121                Path2D path = new Path2D.Float();
122                Point[] pnts = this.getPointArray();
123                if (pnts.length > 1){
124                        path.moveTo(pnts[0].x + xOffset, pnts[0].y + yOffset);
125                        for (int i = 1; i < pnts.length; i++) {
126                                path.lineTo(pnts[i].x + xOffset,  pnts[i].y + yOffset);
127                        }
128                        path.closePath();
129                }
130                else {  // mark single pixel region "X"
131                        double x = pnts[0].x;
132                        double y = pnts[0].y;
133                        path.moveTo(x + xOffset - 0.5, y + yOffset - 0.5);
134                        path.lineTo(x + xOffset + 0.5, y + yOffset + 0.5);
135                        path.moveTo(x + xOffset - 0.5, y + yOffset + 0.5);
136                        path.lineTo(x + xOffset + 0.5, y + yOffset - 0.5);
137                }
138                return path;
139        }
140
141                
142        // Compare method for sorting contours by length (longer contours at front)
143        @Override
144        public int compareTo(Contour c2) {
145                return c2.points.size() - this.points.size();
146        }
147
148        @Override
149        public Iterator<Point> iterator() {
150                return points.iterator();
151        }
152
153}