View Javadoc

1   
2   package math.geom2d.polygon;
3   
4   import java.awt.Graphics2D;
5   import java.util.ArrayList;
6   import java.util.Collection;
7   import java.util.Collections;
8   
9   import math.geom2d.AffineTransform2D;
10  import math.geom2d.Box2D;
11  import math.geom2d.Point2D;
12  import math.geom2d.circulinear.CirculinearBoundarySet2D;
13  import math.geom2d.circulinear.CirculinearCurve2DUtils;
14  import math.geom2d.circulinear.CirculinearDomain2D;
15  import math.geom2d.circulinear.GenericCirculinearDomain2D;
16  import math.geom2d.domain.Boundary2D;
17  import math.geom2d.domain.Boundary2DUtils;
18  import math.geom2d.domain.BoundarySet2D;
19  import math.geom2d.domain.ContinuousBoundary2D;
20  import math.geom2d.domain.Domain2D;
21  import math.geom2d.line.LineSegment2D;
22  import math.geom2d.transform.CircleInversion2D;
23  
24  /**
25   * A polygonal domain whose boundary is composed of several disjoint continuous
26   * LinearRing2D.
27   * 
28   * @author dlegland
29   */
30  public class MultiPolygon2D implements Domain2D, Polygon2D {
31  
32      ArrayList<LinearRing2D> rings = new ArrayList<LinearRing2D>();
33  
34      // ===================================================================
35      // Constructors
36  
37      public MultiPolygon2D() {
38      }
39  
40      public MultiPolygon2D(LinearRing2D ring) {
41          rings.add(ring);
42      }
43  
44      public MultiPolygon2D(LinearRing2D[] rings) {
45          for (LinearRing2D ring : rings)
46              this.rings.add(ring);
47      }
48  
49      public MultiPolygon2D(SimplePolygon2D polygon) {
50          rings.addAll(polygon.getBoundary().getCurves());
51      }
52  
53      public MultiPolygon2D(Collection<LinearRing2D> lines) {
54          rings.addAll(lines);
55      }
56  
57      // ===================================================================
58      // methods specific to MultiPolygon2D
59  
60      public void addPolygon(SimplePolygon2D polygon) {
61          rings.addAll(polygon.getBoundary().getCurves());
62      }
63  
64      /**
65       * Return the set of (oriented) polygons forming this MultiPolygon2D.
66       * 
67       * @return a set of Polygon2D.
68       */
69      public Collection<SimplePolygon2D> getPolygons() {
70          // allocate memory for polygon array
71          ArrayList<SimplePolygon2D> polygons = new ArrayList<SimplePolygon2D>();
72          
73          // create a new SimplePolygon with each ring
74          for (LinearRing2D ring : rings)
75              polygons.add(new SimplePolygon2D(ring.getVertices()));
76          return polygons;
77      }
78  
79      /**
80       * Deprecated use addRing instead (0.8.0)
81       */
82      @Deprecated 
83      public void addPolyline(LinearRing2D ring) {
84          rings.add(ring);
85      }
86  
87      public void addRing(LinearRing2D ring) {
88          rings.add(ring);
89      }
90  
91      // ===================================================================
92      // methods implementing the Polygon2D interface
93  
94    
95      /* (non-Javadoc)
96       * @see math.geom2d.polygon.Polygon2D#getRings()
97       */
98      public Collection<LinearRing2D> getRings() {
99          return Collections.unmodifiableList(rings);
100     }
101 
102 	// ===================================================================
103     // methods inherited from Domain2D interface
104 
105 	/* (non-Javadoc)
106 	 * @see math.geom2d.circulinear.CirculinearDomain2D#transform(math.geom2d.transform.CircleInversion2D)
107 	 */
108 	public CirculinearDomain2D transform(CircleInversion2D inv) {
109 		return new GenericCirculinearDomain2D(
110 				this.getBoundary().transform(inv));
111 	}
112 
113 	/* (non-Javadoc)
114 	 * @see math.geom2d.circulinear.CirculinearShape2D#getBuffer(double)
115 	 */
116 	public CirculinearDomain2D getBuffer(double dist) {
117 		return CirculinearCurve2DUtils.computeBuffer(
118 				this.getBoundary(), dist);
119 	}
120 
121 	
122     // ===================================================================
123     // methods inherited from interface Domain2D
124 
125     public CirculinearBoundarySet2D<LinearRing2D> getBoundary() {
126         return new CirculinearBoundarySet2D<LinearRing2D>(rings);
127     }
128 
129     public Polygon2D complement() {
130         // allocate memory for array of reversed rings
131         ArrayList<LinearRing2D> reverseLines = new ArrayList<LinearRing2D>(rings.size());
132         
133         // reverse each ring
134         for (LinearRing2D ring : rings)
135             reverseLines.add(ring.getReverseCurve());
136         
137         // create the new MultiMpolygon2D with set of reversed rings
138         return new MultiPolygon2D(reverseLines);
139     }
140 
141     // ===================================================================
142     // methods implementing the interface Polygon2D
143 
144     public Collection<LineSegment2D> getEdges() {
145         ArrayList<LineSegment2D> edges = new ArrayList<LineSegment2D>();
146         for (LinearRing2D ring : rings)
147             edges.addAll(ring.getEdges());
148         return edges;
149     }
150 
151     public int getEdgeNumber() {
152         int count = 0;
153         for (LinearRing2D ring : rings)
154             count += ring.getVertexNumber();
155         return count;
156     }
157 
158     public Collection<Point2D> getVertices() {
159         ArrayList<Point2D> points = new ArrayList<Point2D>();
160         for (LinearRing2D ring : rings)
161             points.addAll(ring.getVertices());
162         return points;
163     }
164 
165     /**
166      * Returns the i-th vertex of the polygon.
167      * 
168      * @param i index of the vertex, between 0 and the number of vertices
169      */
170     public Point2D getVertex(int i) {
171         int count = 0;
172         LinearRing2D boundary = null;
173 
174         for (LinearRing2D ring : rings) {
175             int nv = ring.getVertexNumber();
176             if (count+nv>i) {
177                 boundary = ring;
178                 break;
179             }
180             count += nv;
181         }
182 
183         if (boundary==null)
184             throw new IndexOutOfBoundsException();
185 
186         return boundary.getVertex(i-count);
187     }
188 
189     public int getVertexNumber() {
190         int count = 0;
191         for (LinearRing2D ring : rings)
192             count += ring.getVertexNumber();
193         return count;
194     }
195 
196     // ===================================================================
197     // methods inherited from interface Shape2D
198 
199     public Box2D getBoundingBox() {
200         // start with empty bounding box
201         Box2D box = new Box2D(
202         		Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 
203                 Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY);
204         
205         // compute union of all bounding boxes
206         for (LinearRing2D ring : this.rings)
207             box = box.union(ring.getBoundingBox());
208         
209         // return result
210         return box;
211     }
212 
213     /**
214      * Returns a new instance of MultiPolygon2D.
215      */
216     public MultiPolygon2D clip(Box2D box) {
217         // call generic method for computing clipped boundary
218         BoundarySet2D<?> boundary = 
219             Boundary2DUtils.clipBoundary(this.getBoundary(), box);
220         
221         // convert boundary to list of rings
222         ArrayList<LinearRing2D> boundaries = new ArrayList<LinearRing2D>(
223                 boundary.getCurveNumber());
224         for (ContinuousBoundary2D curve : boundary.getBoundaryCurves())
225             boundaries.add((LinearRing2D) curve);
226         
227         // create new MultiPolygon with the set of rings
228         return new MultiPolygon2D(boundaries);
229     }
230 
231     public double getDistance(java.awt.geom.Point2D p) {
232         return Math.max(this.getBoundary().getSignedDistance(p), 0);
233     }
234 
235     public double getDistance(double x, double y) {
236         return Math.max(this.getBoundary().getSignedDistance(x, y), 0);
237     }
238 
239     public boolean isBounded() {
240         // If boundary is not bounded, the polygon is not
241         Boundary2D boundary = this.getBoundary();
242         if (!boundary.isBounded())
243             return false;
244 
245         // Computes the signed area
246         double area = 0;
247         for (LinearRing2D ring : rings)
248             area += ring.getSignedArea();
249 
250         // bounded if positive area
251         return area>0;
252     }
253 
254     /**
255      * The MultiPolygon2D is empty either if it contains no ring, or if all
256      * rings are empty.
257      */
258     public boolean isEmpty() {
259         // return true if at least one ring is not empty
260         for (LinearRing2D ring : rings)
261             if (!ring.isEmpty())
262                 return false;
263         return true;
264     }
265 
266     public MultiPolygon2D transform(AffineTransform2D trans) {
267         // allocate memory for transformed rings
268         ArrayList<LinearRing2D> transformed = 
269             new ArrayList<LinearRing2D>(rings.size());
270         
271         // trasnform each ring
272         for (LinearRing2D ring : rings)
273             transformed.add(ring.transform(trans));
274         
275         // creates a new MultiPolygon2D with the set of trasnformed rings
276         return new MultiPolygon2D(transformed);
277     }
278 
279     public boolean contains(java.awt.geom.Point2D point) {
280         double angle = 0;
281         for (LinearRing2D ring : this.rings)
282             angle += ring.getWindingAngle(point);
283         return angle>Math.PI;
284     }
285 
286     public boolean contains(double x, double y) {
287         return this.contains(new math.geom2d.Point2D(x, y));
288     }
289 
290     public void draw(Graphics2D g2) {
291         g2.draw(this.getBoundary().getGeneralPath());
292     }
293 
294     public void fill(Graphics2D g) {
295         g.fill(this.getBoundary().getGeneralPath());
296     }
297     
298 	@Override
299     public boolean equals(Object obj) {
300         if(!(obj instanceof MultiPolygon2D))
301             return false;
302         
303         // check if the two objects have same number of rings
304         MultiPolygon2D polygon = (MultiPolygon2D) obj;
305         if(polygon.rings.size()!=this.rings.size()) 
306             return false;
307         
308         // check each couple of ring
309         for(int i=0; i<rings.size(); i++)
310             if(!this.rings.get(i).equals(polygon.rings.get(i)))
311                 return false;
312         
313         return true;
314     }
315    
316 	@Override
317     public MultiPolygon2D clone() {
318         // allocate memory for new ring array
319         ArrayList<LinearRing2D> array = new ArrayList<LinearRing2D>(rings.size());
320         
321         // clone each ring
322         for(LinearRing2D ring : rings)
323             array.add(ring.clone());
324         
325         // create a new polygon with cloned rings
326         return new MultiPolygon2D(array);
327     }
328 }