View Javadoc

1   /* file : LinearRing2D.java
2    * 
3    * Project : geometry
4    *
5    * ===========================================
6    * 
7    * This library is free software; you can redistribute it and/or modify it 
8    * under the terms of the GNU Lesser General Public License as published by
9    * the Free Software Foundation, either version 2.1 of the License, or (at
10   * your option) any later version.
11   *
12   * This library is distributed in the hope that it will be useful, but 
13   * WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
14   * or FITNESS FOR A PARTICULAR PURPOSE.
15   *
16   * See the GNU Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public License
19   * along with this library. if not, write to :
20   * The Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21   * Boston, MA 02111-1307, USA.
22   * 
23   * Created on 16 avr. 2007
24   *
25   */
26  
27  package math.geom2d.polygon;
28  
29  import java.awt.Graphics2D;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  
33  import math.geom2d.AffineTransform2D;
34  import math.geom2d.Point2D;
35  import math.geom2d.Shape2D;
36  import math.geom2d.circulinear.*;
37  import math.geom2d.domain.ContinuousBoundary2D;
38  import math.geom2d.domain.Domain2D;
39  import math.geom2d.domain.GenericDomain2D;
40  import math.geom2d.line.LineSegment2D;
41  import math.geom2d.transform.CircleInversion2D;
42  
43  /**
44   * <p>
45   * A LinearRing2D is a Polyline2D whose last point is connected to the first one.
46   * This is typically the boundary of a SimplePolygon2D.
47   * </p>
48   * <p>
49   * The name 'LinearRing2D' was used for 2 reasons:
50   * <ul><li>it is short</li> <li>it is consistent with the JTS name</li></ul>
51   * </p>
52   * @author dlegland
53   */
54  public class LinearRing2D extends Polyline2D implements CirculinearContour2D {
55  
56  	public LinearRing2D() {
57          super();
58      }
59  
60      public LinearRing2D(Point2D initialPoint) {
61          super(initialPoint);
62      }
63  
64      public LinearRing2D(Point2D[] points) {
65          super(points);
66      }
67  
68      public LinearRing2D(double[] xcoords, double[] ycoords) {
69          super(xcoords, ycoords);
70      }
71  
72      public LinearRing2D(Collection<? extends Point2D> points) {
73          super(points);
74      }
75  
76      
77      // ===================================================================
78      // Static methods
79      
80      /**
81       * Static factory for creating a new LinearRing2D from a collection of
82       * points.
83       * @since 0.8.1
84       */
85      public static LinearRing2D create(Collection<? extends Point2D> points) {
86      	return new LinearRing2D(points);
87      }
88      
89      /**
90       * Static factory for creating a new LinearRing2D from an array of
91       * points.
92       * @since 0.8.1
93       */
94      public static LinearRing2D create(Point2D[] points) {
95      	return new LinearRing2D(points);
96      }
97      
98  
99      // ===================================================================
100     // Methods specific to ClosedPolyline2D
101 
102     /**
103      * Computes area of the polyline, by returning the absolute value of the
104      * signed area.
105      */
106     public double getArea() {
107         return Math.abs(this.getSignedArea());
108     }
109 
110     /**
111      * Computes the signed area of the polyline. Algorithm is taken from page:
112      * <a href="http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/">
113      * http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/</a>. Signed are
114      * is positive if polyline is oriented counter-clockwise, and negative
115      * otherwise. Result is wrong if polyline is self-intersecting.
116      * 
117      * @return the signed area of the polyline.
118      */
119     public double getSignedArea() {
120         double area = 0;
121         Point2D prev = this.points.get(this.points.size()-1);
122         Point2D point;
123         for (int i = 0; i<points.size(); i++) {
124             point = this.points.get(i);
125             area += prev.getX()*point.getY()-prev.getY()*point.getX();
126             prev = point;
127         }
128         return area /= 2;
129     }
130 
131     // ===================================================================
132     // Methods specific to Polyline2D
133 
134     /**
135      * Returns an array of LineSegment2D. The number of edges is the same as
136      * the number of vertices.
137      * 
138      * @return the edges of the polyline
139      */
140     @Override
141     public Collection<LineSegment2D> getEdges() {
142         int n = points.size();
143         ArrayList<LineSegment2D> edges = new ArrayList<LineSegment2D>();
144 
145         if (n<2)
146             return edges;
147 
148         for (int i = 0; i<n-1; i++)
149             edges.add(new LineSegment2D(points.get(i), points.get(i+1)));
150         edges.add(new LineSegment2D(points.get(n-1), points.get(0)));
151         return edges;
152     }
153 
154     @Override
155     public LineSegment2D getLastEdge() {
156         int n = points.size();
157         if (n<2)
158             return null;
159         return new LineSegment2D(points.get(n-1), points.get(0));
160     }
161 
162 	// ===================================================================
163     // Methods inherited from CirculinearCurve2D
164 
165 	@Override
166     public CirculinearRing2D getParallel(double dist) {
167     	return GenericCirculinearRing2D.create(
168     			CirculinearCurve2DUtils.createContinuousParallel(this, dist)
169     			.getSmoothPieces());
170     }
171     
172 	/* (non-Javadoc)
173 	 * @see math.geom2d.circulinear.CirculinearCurve2D#transform(math.geom2d.transform.CircleInversion2D)
174 	 */
175 	@Override
176 	public CirculinearContour2D transform(CircleInversion2D inv) {
177 		
178 		// Create array for storing transformed arcs
179 		Collection<LineSegment2D> edges = this.getEdges();
180 		ArrayList<CirculinearElement2D> arcs = 
181 			new ArrayList<CirculinearElement2D>(edges.size());
182 		
183 		// Transform each arc
184 		for(LineSegment2D edge : edges) {
185 			arcs.add(edge.transform(inv));
186 		}
187 		
188 		// create the transformed shape
189 		return new BoundaryPolyCirculinearCurve2D<CirculinearElement2D>(arcs);
190 	}
191 
192 	// ===================================================================
193     // Methods inherited from Boundary2D
194 
195     public Collection<ContinuousBoundary2D> getBoundaryCurves() {
196         ArrayList<ContinuousBoundary2D> list = 
197             new ArrayList<ContinuousBoundary2D>(1);
198         list.add(this);
199         return list;
200     }
201 
202     public Domain2D getDomain() {
203         return new GenericDomain2D(this);
204     }
205 
206     public void fill(Graphics2D g2) {
207         g2.fill(this.getGeneralPath());
208     }
209 
210     // ===================================================================
211     // Methods inherited from interface OrientedCurve2D
212 
213     /*
214      * (non-Javadoc)
215      * 
216      * @see math.geom2d.OrientedCurve2D#getSignedDistance(double, double)
217      */
218     @Override
219     public double getSignedDistance(double x, double y) {
220         return (this.isInside(x, y) ? -1 : 1)*this.getDistance(x, y);
221     }
222 
223     /*
224      * (non-Javadoc)
225      * 
226      * @see math.geom2d.OrientedCurve2D#getSignedDistance(java.awt.geom.Point2D)
227      */
228     @Override
229     public double getSignedDistance(java.awt.geom.Point2D point) {
230         return getSignedDistance(point.getX(), point.getY());
231     }
232 
233     /*
234      * (non-Javadoc)
235      * 
236      * @see math.geom2d.OrientedCurve2D#getWindingAngle(java.awt.geom.Point2D)
237      */
238     @Override
239     public double getWindingAngle(java.awt.geom.Point2D point) {
240         int wn = Polygon2DUtils.windingNumber(this.points, point);
241         return wn*2*Math.PI;
242     }
243 
244     public boolean isInside(double x, double y) {
245         return this.isInside(new Point2D(x, y));
246     }
247 
248     /*
249      * (non-Javadoc)
250      * 
251      * @see math.geom2d.OrientedCurve2D#isInside(java.awt.geom.Point2D)
252      */
253     @Override
254     public boolean isInside(java.awt.geom.Point2D point) {
255         // TODO: choose convention for points on the boundary
256         int wn = Polygon2DUtils.windingNumber(this.points, point);
257         if (wn==1)
258             return true;
259         if (this.contains(point))
260             return true;
261         return false;
262     }
263 
264     // ===================================================================
265     // Methods inherited from interface ContinuousCurve2D
266 
267     /**
268      * return true, by definition.
269      */
270     @Override
271     public boolean isClosed() {
272         return true;
273     }
274 
275     // ===================================================================
276     // Methods inherited from interface Curve2D
277 
278     /**
279      * Returns point from position as double. Position t can be from 0 to n,
280      * with n equal to the number of vertices of the polyline.
281      */
282     @Override
283     public math.geom2d.Point2D getPoint(double t) {
284         // format position to stay between limits
285         double t0 = this.getT0();
286         double t1 = this.getT1();
287         t = Math.max(Math.min(t, t1), t0);
288 
289         int n = points.size();
290 
291         // index of vertex before point
292         int ind0 = (int) Math.floor(t+Shape2D.ACCURACY);
293         double tl = t-ind0;
294 
295         if (ind0==n)
296             ind0 = 0;
297         Point2D p0 = points.get(ind0);
298 
299         // check if equal to a vertex
300         if (Math.abs(t-ind0)<Shape2D.ACCURACY)
301             return new Point2D(p0);
302 
303         // index of vertex after point
304         int ind1 = ind0+1;
305         if (ind1==n)
306             ind1 = 0;
307         Point2D p1 = points.get(ind1);
308 
309         // position on line;
310         double x0 = p0.getX();
311         double y0 = p0.getY();
312         double dx = p1.getX()-x0;
313         double dy = p1.getY()-y0;
314 
315         return new Point2D(x0+tl*dx, y0+tl*dy);
316     }
317 
318     /**
319      * returns 0.
320      */
321     @Override
322     public double getT0() {
323         return 0;
324     }
325 
326     /**
327      * return the number of points in the polyline.
328      */
329     @Override
330     public double getT1() {
331         return points.size();
332     }
333 
334     /**
335      * return the first point of the polyline.
336      */
337     @Override
338     public Point2D getFirstPoint() {
339         if (points.size()==0)
340             return null;
341         return points.get(0);
342     }
343 
344     /**
345      * return the first point, as this is the same as the last point.
346      */
347     @Override
348     public Point2D getLastPoint() {
349         if (points.size()==0)
350             return null;
351         return points.get(0);
352     }
353 
354 	@Override
355     public Collection<? extends LinearRing2D> getContinuousCurves() {
356     	return wrapCurve(this);
357     }
358 
359     /**
360      * Returns the closed polyline with same points taken in reverse order. The
361      * first points is still the same. Points of reverse curve are the same as
362      * the original curve (same pointers).
363      */
364     @Override
365     public LinearRing2D getReverseCurve() {
366         Point2D[] points2 = new Point2D[points.size()];
367         int n = points.size();
368         if (n>0) {
369             points2[0] = points.get(0);
370             for (int i = 1; i<n; i++)
371                 points2[i] = points.get(n-i);
372         }
373         return new LinearRing2D(points2);
374     }
375 
376     /**
377      * Return an instance of Polyline2D. If t1 is lower than t0, the returned
378      * Polyline contains the origin of the curve.
379      */
380     @Override
381     public Polyline2D getSubCurve(double t0, double t1) {
382         // code adapted from CurveSet2D
383 
384         Polyline2D res = new Polyline2D();
385 
386         // number of points in the polyline
387         int indMax = this.getVertexNumber();
388 
389         // format to ensure t is between T0 and T1
390         t0 = Math.min(Math.max(t0, 0), indMax);
391         t1 = Math.min(Math.max(t1, 0), indMax);
392 
393         // find curves index
394         int ind0 = (int) Math.floor(t0+Shape2D.ACCURACY);
395         int ind1 = (int) Math.floor(t1+Shape2D.ACCURACY);
396 
397         // need to subdivide only one line segment
398         if (ind0==ind1&&t0<t1) {
399             // extract limit points
400             res.addPoint(this.getPoint(t0));
401             res.addPoint(this.getPoint(t1));
402             // return result
403             return res;
404         }
405 
406         // add the point corresponding to t0
407         res.addPoint(this.getPoint(t0));
408 
409         if (ind1>ind0) {
410             // add all the whole points between the 2 cuts
411             for (int n = ind0+1; n<=ind1; n++)
412                 res.addPoint(points.get(n));
413         } else {
414             // add all points until the end of the set
415             for (int n = ind0+1; n<indMax; n++)
416                 res.addPoint(points.get(n));
417 
418             // add all points from the beginning of the set
419             for (int n = 0; n<=ind1; n++)
420                 res.addPoint(points.get(n));
421         }
422 
423         // add the last point
424         res.addPoint(this.getPoint(t1));
425 
426         // return the curve set
427         return res;
428     }
429 
430     // ===================================================================
431     // Methods inherited from interface Shape2D
432 
433     /**
434      * Return the transformed shape, as a ClosePolyline2D.
435      */
436     @Override
437     public LinearRing2D transform(AffineTransform2D trans) {
438         Point2D[] pts = new Point2D[points.size()];
439         for (int i = 0; i<points.size(); i++)
440             pts[i] = trans.transform(points.get(i));
441         return new LinearRing2D(pts);
442     }
443 
444     /*
445      * (non-Javadoc)
446      * 
447      * @see math.geom2d.ContinuousCurve2D#appendPath(java.awt.geom.GeneralPath)
448      */
449     @Override
450     public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
451 
452         if (points.size()<2)
453             return path;
454 
455         // move to last first point of the curve (then a line will be drawn to
456         // the first point)
457         Point2D p0 = this.getLastPoint();
458         path.moveTo((float) p0.getX(), (float) p0.getY());
459         
460         // process each point
461         for(Point2D point : points)
462             path.lineTo((float) point.getX(), (float) point.getY());
463         
464         // close the path, even if the path is already at the right position
465         path.closePath();
466         
467         return path;
468     }
469 
470     /**
471      * Return a general path iterator.
472      */
473     @Override
474     public java.awt.geom.GeneralPath getGeneralPath() {
475         java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
476         if (points.size()<2)
477             return path;
478         return this.appendPath(path);
479 //        // get point iterator
480 //        Iterator<Point2D> iter = points.iterator();
481 //
482 //        // move to first point
483 //        Point2D point = iter.next();
484 //        path.moveTo((float) (point.getX()), (float) (point.getY()));
485 //
486 //        // line to each other point
487 //        while (iter.hasNext()) {
488 //            point = iter.next();
489 //            path.lineTo((float) (point.getX()), (float) (point.getY()));
490 //        }
491 //
492 //        // closes the path
493 //        path.closePath();
494 //
495 //        return path;
496     }
497     
498     @Override
499     public boolean equals(Object object) {
500         if (!(object instanceof LinearRing2D))
501             return false;
502         LinearRing2D ring = (LinearRing2D) object;
503 
504         if (points.size()!=ring.points.size())
505             return false;
506         for (int i = 0; i<points.size(); i++)
507             if (!(points.get(i)).equals(ring.points.get(i)))
508                 return false;
509         return true;
510     }
511     
512     @Override
513     public LinearRing2D clone() {
514         ArrayList<Point2D> array = new ArrayList<Point2D>(points.size());
515         for(Point2D point : points)
516             array.add(point.clone());
517         return new LinearRing2D(array);
518     }
519 }