View Javadoc
1   package com.github.celldynamics.quimp.geom;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.List;
6   
7   import org.scijava.vecmath.Point2d;
8   import org.scijava.vecmath.Tuple2d;
9   import org.scijava.vecmath.Vector2d;
10  import org.slf4j.Logger;
11  import org.slf4j.LoggerFactory;
12  
13  /**
14   * Calculates basic geometry on polygons defined as list of point in specified direction.
15   * 
16   * @author p.baniukiewicz
17   * @see <a href= "link">http://www.mathopenref.com/coordpolygonarea.html</a>
18   */
19  public class BasicPolygons {
20  
21    /**
22     * The Constant LOGGER.
23     */
24    static final Logger LOGGER = LoggerFactory.getLogger(BasicPolygons.class.getName());
25  
26    /**
27     * Default constructor.
28     */
29    public BasicPolygons() {
30  
31    }
32  
33    /**
34     * Calculates area of polygon.
35     * 
36     * <p>Supports triangles, regular and irregular polygons, convex or concave polygons
37     * 
38     * <p><b>Warning</b>
39     * 
40     * <p>Polygon can not intersect itself.
41     * 
42     * @param p Vertices of polygon in specified order
43     * @return Area
44     * 
45     */
46    public double getPolyArea(final List<? extends Tuple2d> p) {
47      int i;
48      int j;
49      double area = 0;
50  
51      for (i = 0; i < p.size(); i++) {
52        j = (i + 1) % p.size(); // will round pointer to 0 for last point
53        Tuple2d pi = p.get(i);
54        Tuple2d pj = p.get(j);
55        area += pi.getX() * pj.getY();
56        area -= pi.getY() * pj.getX();
57      }
58      area /= 2.0;
59      return (Math.abs(area));
60    }
61  
62    /**
63     * Calculates perimeter of polygon.
64     * 
65     * @param p Vertices of polygon in specified order
66     * @return Perimeter
67     */
68    public double getPolyPerim(final List<? extends Tuple2d> p) {
69      int i;
70      int j;
71      double len = 0;
72      ArrayList<Vector2d> tmpV = new ArrayList<>();
73      // get vectors between points
74      for (i = 0; i < p.size(); i++) {
75        j = (i + 1) % p.size(); // will round pointer to 0 for last point
76        Tuple2d first = p.get(i);
77        Tuple2d second = p.get(j);
78        Vector2d tmp = new Vector2d(second.getX() - first.getX(), second.getY() - first.getY());
79        tmpV.add(tmp);
80      }
81      for (Vector2d v : tmpV) {
82        len += v.length();
83      }
84      return len;
85    }
86  
87    /**
88     * Test whether <tt>Ptest</tt> is inside polygon <tt>P</tt>
89     * 
90     * @param p Vertices of polygon in specified order
91     * @param testP Point to be tested
92     * @return true if <tt>Ptest</tt> is inside <tt>P</tt>, false otherwise
93     * @see <a href=
94     *      "link">http://www.shodor.org/~jmorrell/interactivate/org/shodor/util11/PolygonUtils.java</a>
95     */
96    public boolean isPointInside(final List<? extends Tuple2d> p, final Tuple2d testP) {
97      double angle = 0;
98      Point2d p1 = null;
99      Point2d p2 = null;
100     for (int i = 0; i < p.size(); i++) {
101       p1 = new Point2d(p.get(i).getX() - testP.getX(), p.get(i).getY() - testP.getY());
102       p2 = new Point2d(p.get((i + 1) % p.size()).getX() - testP.getX(),
103               p.get((i + 1) % p.size()).getY() - testP.getY());
104       angle += angle2D(p1, p2);
105     }
106     return Math.abs(angle) >= Math.PI;
107   }
108 
109   /**
110    * Helper method.
111    * 
112    * @param p1 first point
113    * @param p2 second point
114    * @return angle between points.
115    */
116   private double angle2D(Point2d p1, Point2d p2) {
117     double dtheta = Math.atan2(p2.y, p2.x) - Math.atan2(p1.y, p1.x);
118     while (dtheta > Math.PI) {
119       dtheta -= 2.0 * Math.PI;
120     }
121     while (dtheta < -1.0 * Math.PI) {
122       dtheta += 2.0 * Math.PI;
123     }
124     return dtheta;
125   }
126 
127   /**
128    * Test if all points <tt>Ptest</tt> are inside polygon <tt>P</tt>.
129    * 
130    * @param polygon polygon to test points with
131    * @param testP points to test
132    * @return true if all points are inside polygon
133    * 
134    * @see #isPointInside(List, Tuple2d)
135    */
136   public boolean areAllPointsInside(final List<? extends Tuple2d> polygon,
137           final List<? extends Tuple2d> testP) {
138     boolean result = true;
139     Iterator<? extends Tuple2d> it = testP.iterator();
140     while (it.hasNext() && result) {
141       result = isPointInside(polygon, it.next());
142     }
143     return result;
144   }
145 
146   /**
147    * Test if all points from <tt>Ptest</tt> are outside of <tt>P</tt>.
148    * 
149    * @param polygon polygon to test points with
150    * @param testP points to test
151    * @return true if all points are outside polygon
152    * 
153    * @see #isPointInside(List, Tuple2d)
154    */
155   public boolean areAllPointOutside(final List<? extends Tuple2d> polygon,
156           final List<? extends Tuple2d> testP) {
157     boolean result = false;
158     Iterator<? extends Tuple2d> it = testP.iterator();
159     while (it.hasNext() && !result) {
160       result = isPointInside(polygon, it.next());
161     }
162     return !result;
163   }
164 
165   /**
166    * Get center of mass of polygon.
167    * 
168    * <p><b>Warning</b>
169    * 
170    * <p>Require correct polygon with non crossing edges.
171    * 
172    * @param polygon Vertices of polygon in specified order
173    * @return Point of center of mass
174    * @throws IllegalArgumentException when defective polygon is given (area equals 0)
175    * @see <a href=
176    *      "link">http://stackoverflow.com/questions/5271583/center-of-gravity-of-a-polygon</a>
177    */
178   public Point2d polygonCenterOfMass(final List<? extends Tuple2d> polygon) {
179 
180     int n = polygon.size();
181     Point2d[] polygonTmp = new Point2d[n];
182 
183     for (int q = 0; q < n; q++) {
184       polygonTmp[q] = new Point2d(polygon.get(q));
185     }
186 
187     double cx = 0;
188     double cy = 0;
189     double a = getPolyArea(polygon);
190     if (a == 0) {
191       throw new IllegalArgumentException("Defective polygon, area is 0");
192     }
193     int i;
194     int j;
195 
196     double factor = 0;
197     for (i = 0; i < n; i++) {
198       j = (i + 1) % n;
199       factor = (polygonTmp[i].x * polygonTmp[j].y - polygonTmp[j].x * polygonTmp[i].y);
200       cx += (polygonTmp[i].x + polygonTmp[j].x) * factor;
201       cy += (polygonTmp[i].y + polygonTmp[j].y) * factor;
202     }
203     factor = 1.0 / (6.0 * a);
204     cx *= factor;
205     cy *= factor;
206     return new Point2d(Math.abs(cx), Math.abs((cy)));
207   }
208 
209 }