View Javadoc

1   /**
2    * 
3    */
4   
5   package math.geom2d.polygon;
6   
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.SortedSet;
10  import java.util.TreeSet;
11  
12  import math.geom2d.Point2D;
13  import math.geom2d.domain.BoundaryPolyCurve2D;
14  import math.geom2d.domain.BoundarySet2D;
15  import math.geom2d.domain.Domain2D;
16  import math.geom2d.domain.GenericDomain2D;
17  import math.geom2d.domain.SmoothOrientedCurve2D;
18  import math.geom2d.line.LineSegment2D;
19  
20  /**
21   * @author dlegland
22   */
23  public abstract class Polygon2DUtils {
24  
25      /**
26       * Computes the winding number of the polygon. Algorithm adapted from
27       * http://www.geometryalgorithms.com/Archive/algorithm_0103/algorithm_0103.htm
28       * 
29       * @param vertices the vertices of the polygon
30       * @param point the reference point
31       * @return the number of windings of the curve around the point
32       */
33      public final static int windingNumber(Collection<Point2D> vertices,
34              java.awt.geom.Point2D point) {
35          int wn = 0; // the winding number counter
36  
37          // Extract the last point of the collection
38          Point2D previous = null;
39          for (Point2D vertex : vertices)
40              previous = vertex;
41          double x1 = previous.getX();
42          double y1 = previous.getY();
43          double x2, y2;
44  
45          // Iterate on couple of vertices, starting from couple (last,first)
46          double y = point.getY();
47          for (Point2D p : vertices) {
48              // second vertex of current edge
49              x2 = p.getX();
50              y2 = p.getY();
51  
52              // TODO: should avoid create new objects, and use a dedicated method
53              if (y1<=y) {
54                  if (y2>y) // an upward crossing
55                      if (new LineSegment2D(x1, y1, x2, y2).isInside(point))
56                          wn++;
57              } else {
58                  if (y2<=y) // a downward crossing
59                      if (!(new LineSegment2D(x1, y1, x2, y2).isInside(point)))
60                          wn--;
61              }
62  
63              // for next iteration
64              x1 = x2;
65              y1 = y2;
66          }
67  
68          return wn;
69      }
70  
71      public final static Domain2D createBuffer(Polygon2D polygon, double d) {
72          BoundarySet2D<BoundaryPolyCurve2D<SmoothOrientedCurve2D>> result = 
73              new BoundarySet2D<BoundaryPolyCurve2D<SmoothOrientedCurve2D>>();
74  
75          for (LinearRing2D ring : polygon.getBoundary())
76              result.addCurve(Polyline2DUtils.createClosedParallel(ring, d));
77  
78          return new GenericDomain2D(result);
79      }
80      
81      public final static Polygon2D union(Polygon2D polygon1, 
82              Polygon2D polygon2) {
83          
84          // The resulting boundary
85          ArrayList<LinearRing2D> boundary = new ArrayList<LinearRing2D>();
86          
87          // Extract polygon boundaries
88          BoundarySet2D<? extends LinearRing2D> boundary1 = polygon1.getBoundary();
89          BoundarySet2D<? extends LinearRing2D> boundary2 = polygon2.getBoundary();
90          
91          // compute intersections
92          ArrayList<Point2D> intersections = new ArrayList<Point2D>();
93          for(LinearRing2D ring1 : boundary1.getCurves()){
94              for(LinearRing2D ring2 : boundary2.getCurves()){
95                  intersections.addAll(Polyline2DUtils.intersect(ring1, ring2));
96              }
97          }
98          
99          // Check the case of no intersection
100         if(intersections.size()==0) {
101             if(!polygon1.contains(boundary2.getFirstPoint())){
102                 boundary.addAll(boundary2.getCurves());
103             }
104             if(!polygon2.contains(boundary1.getFirstPoint())){
105                 boundary.addAll(boundary1.getCurves());
106             }
107             return new MultiPolygon2D(boundary);
108         }
109         
110         // locate intersection on each boundary
111         SortedSet<Double> positions1 = new TreeSet<Double>();
112         SortedSet<Double> positions2 = new TreeSet<Double>();
113         for (Point2D p : intersections) {
114             positions1.add(new Double(boundary1.getPosition(p)));
115             positions2.add(new Double(boundary2.getPosition(p)));
116         }
117         
118         //TODO: manage several boundaries
119         
120         // ---
121         // Manage next LinearRing2D
122         
123         // prepare the point list for the new ring
124         ArrayList<Point2D> points = new ArrayList<Point2D>();
125 
126         // get an unprocessed intersection point
127         Point2D refPoint = intersections.iterator().next();
128         points.add(refPoint);
129         
130         // check if the point is going inside or outside from poly1 when
131         // following boundary of poly2
132         double pos = boundary1.getPosition(refPoint);
133         
134         // find the position of the next intersection point on boundary1
135         double nextPos = findNext(positions1, pos);
136         double middlePos = chooseBetween(pos, nextPos);
137         Point2D middlePoint = boundary1.getPoint(middlePos);
138         //TODO: not sure about non continuous curves
139         
140         BoundarySet2D<? extends LinearRing2D> currentBoundary = 
141             polygon2.contains(middlePoint) ? boundary2 : boundary1;
142         boolean isOnFirstBoundary = !polygon2.contains(middlePoint);
143         
144         // iterate on each boundary until we come back to ref point
145         Point2D point = refPoint;
146         do{
147             if(isOnFirstBoundary){
148                 nextPos = findNext(positions1, pos);
149                 
150             }
151         }while(point!=refPoint);
152         
153         
154         return null;
155     }
156     
157     private final static <T> T findNext(SortedSet<T> set, T element){
158         SortedSet<T> tail = set.tailSet(element);
159         return tail.isEmpty() ? set.first() : tail.first();
160     }
161     
162     private final static double chooseBetween(double pos1, double pos2) {
163         if(pos2>pos1) {
164             return pos1 + (pos2-pos1)/2;
165         } else {
166             return pos2/2;
167         }
168     }
169 }