001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.math.geometry.shape.algorithm;
031
032import java.util.ArrayList;
033import java.util.List;
034
035import org.openimaj.math.geometry.point.Point2d;
036import org.openimaj.math.geometry.shape.Polygon;
037import org.openimaj.math.geometry.shape.Triangle;
038
039/**
040 * Class representing a convexity defect of a polygon, with methods for finding
041 * said defects. A convexity defect is a triangle formed between two points on
042 * the convex hull of a polygon, and the deepest point on the shape polygon
043 * between the points on the hull.
044 * 
045 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
046 * 
047 */
048public class ConvexityDefect {
049        /**
050         * The starting point on the convex hull
051         */
052        public Point2d start;
053
054        /**
055         * The ending point on the convex hull
056         */
057        public Point2d end;
058
059        /**
060         * The deepest point on the shape polygon
061         */
062        public Point2d deepestPoint;
063
064        /**
065         * The depth of the deepest point
066         */
067        public float depth;
068
069        private ConvexityDefect() {
070        }
071
072        /**
073         * Get the triangle represented by this defect.
074         * 
075         * @return the triangle represented by this defect.
076         */
077        public Triangle getTriangle() {
078                return new Triangle(start, deepestPoint, end);
079        }
080
081        /**
082         * Find the defects of the given polygon. The convex hull of the polygon is
083         * computed internally.
084         * 
085         * @param p
086         *            the polygon
087         * @return a list of all the convexity defexts
088         */
089        public static List<ConvexityDefect> findDefects(Polygon p) {
090                return findDefects(p, p.calculateConvexHull());
091        }
092
093        /**
094         * Find the defects of the given polygon, based on the given convex hull
095         * 
096         * @param p
097         *            the polygon
098         * @param hull
099         *            the convex hull of the polygon
100         * @return a list of all the convexity defexts
101         */
102        public static List<ConvexityDefect> findDefects(Polygon p, Polygon hull) {
103                // test orientation of hull w.r.t poly
104                final int index1 = p.points.indexOf(hull.points.get(0));
105                final int index2 = p.points.indexOf(hull.points.get(1));
106                final int index3 = p.points.indexOf(hull.points.get(2));
107
108                int sign = 0;
109                sign += (index2 > index1) ? 1 : 0;
110                sign += (index3 > index2) ? 1 : 0;
111                sign += (index1 > index3) ? 1 : 0;
112
113                final boolean reverseOri = (sign == 2) ? false : true;
114
115                final List<ConvexityDefect> defects = new ArrayList<ConvexityDefect>();
116
117                for (int i = 0; i < hull.points.size(); i++) {
118                        final ConvexityDefect defect = new ConvexityDefect();
119                        defect.start = hull.get(i);
120
121                        if (i == hull.points.size() - 1) {
122                                defect.end = hull.get(0);
123                        } else {
124                                defect.end = hull.get(i + 1);
125                        }
126
127                        final double dx0 = defect.end.getX() - defect.start.getX();
128                        final double dy0 = defect.end.getY() - defect.start.getY();
129                        final double scale = 1f / Math.sqrt(dx0 * dx0 + dy0 * dy0);
130
131                        float depth = 0;
132                        boolean isDefect = false;
133                        int curi = p.points.indexOf(defect.start);
134                        while (true) {
135                                if (reverseOri) {
136                                        curi--;
137                                        if (curi < 0)
138                                                curi = p.points.size() - 1;
139                                } else {
140                                        curi++;
141                                        if (curi >= p.points.size())
142                                                curi = 0;
143                                }
144
145                                final Point2d cur = p.points.get(curi);
146                                if (cur == defect.end)
147                                        break;
148
149                                final double dx = (double) cur.getX() - (double) defect.start.getX();
150                                final double dy = (double) cur.getY() - (double) defect.start.getY();
151
152                                /* compute depth */
153                                final double dist = Math.abs(-dy0 * dx + dx0 * dy) * scale;
154
155                                if (dist > depth)
156                                {
157                                        depth = (float) dist;
158                                        defect.deepestPoint = cur;
159                                        defect.depth = depth;
160                                        isDefect = true;
161                                }
162                        }
163
164                        if (isDefect) {
165                                defects.add(defect);
166                        }
167                }
168
169                return defects;
170        }
171}