View Javadoc

1   /* file : PolyCurve2D.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 1 mai 2006
24   *
25   */
26  
27  package math.geom2d.curve;
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.Box2D;
35  import math.geom2d.Point2D;
36  import math.geom2d.Shape2D;
37  import math.geom2d.Vector2D;
38  import math.geom2d.polygon.Polyline2D;
39  
40  /**
41   * A PolyCurve2D is a set of piecewise smooth curve arcs, such that the end of a
42   * curve is the beginning of the next curve, and such that they do not intersect
43   * nor self-intersect.
44   * <p>
45   * 
46   * @author dlegland
47   */
48  public class PolyCurve2D<T extends ContinuousCurve2D> extends CurveArray2D<T>
49          implements ContinuousCurve2D {
50  
51      /** flag for indicating if the curve is closed or not (default is open) */
52      protected boolean closed = false;
53  
54      // ===================================================================
55      // Constructors
56  
57      public PolyCurve2D() {
58      }
59      
60      public PolyCurve2D(int n) {
61      	super(n);
62      }
63  
64      public PolyCurve2D(T[] curves) {
65          super(curves);
66      }
67  
68      public PolyCurve2D(T[] curves, boolean closed) {
69          super(curves);
70          this.closed = closed;
71      }
72  
73      public PolyCurve2D(Collection<? extends T> curves) {
74          super(curves);
75      }
76  
77      public PolyCurve2D(Collection<? extends T> curves, boolean closed) {
78          super(curves);
79          this.closed = closed;
80      }
81  
82      
83      // ===================================================================
84      // static methods
85  
86  //    /**
87  //     * Static factory for creating a new PolyCurve2D from a collection of
88  //     * curves.
89  //     * @since 0.8.1
90  //     */
91  //    public static <T extends ContinuousCurve2D> PolyCurve2D<T> create(
92  //    		Collection<T> curves) {
93  //    	return new PolyCurve2D<T>(curves);
94  //    }
95  //    
96  //    /**
97  //     * Static factory for creating a new PolyCurve2D from an array of
98  //     * curves.
99  //     * @since 0.8.1
100 //     */
101 //    public static <T extends ContinuousCurve2D> PolyCurve2D<T> create(
102 //    		T[] curves) {
103 //    	return new PolyCurve2D<T>(curves);
104 //    }
105 
106 	protected static <T extends ContinuousCurve2D> Collection<T> wrapCurve(T curve) {
107 		ArrayList<T> list = new ArrayList<T> (1);
108 		list.add(curve);
109 		return list;
110 	}
111 	
112    
113     // ===================================================================
114     // Methods specific to PolyCurve2D
115 
116     /**
117      * Toggle the 'closed' flag of the polycurve.
118      */
119     public void setClosed(boolean b) {
120         closed = b;
121     }
122 
123     // ===================================================================
124     // Methods implementing the ContinuousCurve2D interface
125 
126 	/* (non-Javadoc)
127 	 * @see math.geom2d.curve.ContinuousCurve2D#getLeftTangent(double)
128 	 */
129 	public Vector2D getLeftTangent(double t) {
130 		return this.getChildCurve(t).getLeftTangent(this.getLocalPosition(t));
131 	}
132 
133 	/* (non-Javadoc)
134 	 * @see math.geom2d.curve.ContinuousCurve2D#getRightTangent(double)
135 	 */
136 	public Vector2D getRightTangent(double t) {
137 		return this.getChildCurve(t).getRightTangent(this.getLocalPosition(t));
138 	}
139 
140 	/**
141      * Returns true if the PolyCurve2D is closed.
142      */
143     public boolean isClosed() {
144         return closed;
145     }
146 
147     public Polyline2D getAsPolyline(int n) {
148         Point2D[] points = new Point2D[n+1];
149         double t0 = this.getT0();
150         double t1 = this.getT1();
151         double dt = (t1-t0)/n;
152         for (int i = 0; i<n; i++)
153             points[i] = this.getPoint(i*dt+t0);
154         return new Polyline2D(points);
155     }
156 
157     /**
158      * Returns a collection containing only instances of SmoothCurve2D.
159      * 
160      * @return a collection of SmoothCurve2D
161      */
162     public Collection<? extends SmoothCurve2D> getSmoothPieces() {
163         ArrayList<SmoothCurve2D> list = new ArrayList<SmoothCurve2D>();
164         for (Curve2D curve : this.curves)
165             list.addAll(PolyCurve2D.getSmoothCurves(curve));
166         return list;
167     }
168 
169     /**
170      * Returns a collection containing only instances of SmoothCurve2D.
171      * 
172      * @param curve the curve to decompose
173      * @return a collection of SmoothCurve2D
174      */
175     private final static Collection<SmoothCurve2D> getSmoothCurves(Curve2D curve) {
176     	// create array for result
177         ArrayList<SmoothCurve2D> array = new ArrayList<SmoothCurve2D>();
178 
179         // If curve is smooth, add it to the array and return.
180         if (curve instanceof SmoothCurve2D) {
181             array.add((SmoothCurve2D) curve);
182             return array;
183         }
184 
185         // Otherwise, iterate on curves of the curve set
186         if (curve instanceof CurveSet2D) {
187             for (Curve2D curve2 : ((CurveSet2D<?>) curve).getCurves())
188                 array.addAll(getSmoothCurves(curve2));
189             return array;
190         }
191 
192         if (curve==null)
193             return array;
194 
195         System.err.println("could not find smooth parts of curve with class "
196                 +curve.getClass().getName());
197         return array;
198     }
199 
200     // ===================================================================
201     // Methods implementing the ContinuousCurve2D interface
202 
203     @Override
204     public Collection<? extends PolyCurve2D<?>> getContinuousCurves() {
205     	return wrapCurve(this);
206     }
207 
208    @Override
209     public PolyCurve2D<? extends ContinuousCurve2D> getReverseCurve() {
210     	// create array for storing reversed curves
211     	int n = curves.size();
212         ContinuousCurve2D[] curves2 = new ContinuousCurve2D[n];
213         
214         // reverse each curve
215         for (int i = 0; i<n; i++)
216             curves2[i] = (ContinuousCurve2D)curves.get(n-1-i).getReverseCurve();
217         
218         // create the new reversed curve
219         return new PolyCurve2D<ContinuousCurve2D>(curves2);
220     }
221 
222     /**
223      * Returns an instance of PolyCurve2D. If t0>t1 and curve is not closed,
224      * return a PolyCurve2D without curves inside.
225      */
226     @Override
227     public PolyCurve2D<? extends ContinuousCurve2D> getSubCurve(double t0,
228             double t1) {
229         // check limit conditions
230         if (t1<t0&!this.isClosed())
231             return new PolyCurve2D<ContinuousCurve2D>();
232 
233         // Call the parent method
234         CurveSet2D<?> set = super.getSubCurve(t0, t1);
235         
236         // create result object, with appropriate numbe of curves
237         PolyCurve2D<ContinuousCurve2D> subCurve = 
238         	new PolyCurve2D<ContinuousCurve2D>(set.getCurveNumber());
239 
240         // If a part is selected, the result is obviously open
241         subCurve.setClosed(false);
242 
243         // convert to PolySmoothCurve by adding curves, after class cast
244         for (Curve2D curve : set.getCurves())
245             subCurve.addCurve((ContinuousCurve2D) curve);
246 
247         // return the resulting portion of curve
248         return subCurve;
249     }
250 
251     /**
252      * Clip the PolyCurve2D by a box. The result is an instance of CurveSet2D<ContinuousCurve2D>,
253      * which contains only instances of ContinuousCurve2D. If the PolyCurve2D is
254      * not clipped, the result is an instance of CurveSet2D<ContinuousCurve2D>
255      * which contains 0 curves.
256      */
257     @Override
258     public CurveSet2D<? extends ContinuousCurve2D> clip(Box2D box) {
259         // Clip the curve
260         CurveSet2D<? extends Curve2D> set = Curve2DUtils.clipCurve(this, box);
261 
262         // Stores the result in appropriate structure
263         CurveArray2D<ContinuousCurve2D> result = 
264         	new CurveArray2D<ContinuousCurve2D>(set.getCurveNumber());
265 
266         // convert the result
267         for (Curve2D curve : set.getCurves()) {
268             if (curve instanceof ContinuousCurve2D)
269                 result.addCurve((ContinuousCurve2D) curve);
270         }
271         return result;
272     }
273 
274     @Override
275     public PolyCurve2D<? extends ContinuousCurve2D> transform(
276             AffineTransform2D trans) {
277         PolyCurve2D<ContinuousCurve2D> result = new PolyCurve2D<ContinuousCurve2D>();
278         for (ContinuousCurve2D curve : curves)
279             result.addCurve((ContinuousCurve2D)curve.transform(trans));
280         result.setClosed(this.isClosed());
281         return result;
282     }
283 
284     public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
285         Point2D point;
286         for (ContinuousCurve2D curve : getCurves()) {
287             point = curve.getPoint(curve.getT0());
288             path.lineTo((float) point.getX(), (float) point.getY());
289             curve.appendPath(path);
290         }
291 
292         // eventually close the curve
293         if (closed) {
294             point = this.getFirstPoint();
295             path.lineTo((float) point.getX(), (float) point.getY());
296         }
297 
298         return path;
299     }
300 
301     @Override
302     public java.awt.geom.GeneralPath getGeneralPath() {
303         // create new path
304         java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
305 
306         // avoid degenerate case
307         if (curves.size()==0)
308             return path;
309 
310         // move to the first point
311         Point2D start, current;
312         start = this.getFirstPoint();
313         path.moveTo((float) start.getX(), (float) start.getY());
314         current = start;
315 
316         // add the path of the first curve
317         for(ContinuousCurve2D curve : curves) {
318         	start = curve.getFirstPoint();
319         	if (start.distance(current)>Shape2D.ACCURACY)
320         		path.lineTo((float) start.getX(), (float) start.getY());
321         	path = curve.appendPath(path);
322         	current = start;
323         }
324         
325         // eventually closes the curve
326         if (closed) {
327             path.closePath();
328         }
329 
330         // return the final path
331         return path;
332     }
333     
334     @Override
335      public void draw(Graphics2D g2) {
336     	g2.draw(this.getGeneralPath());
337     }
338 
339     @Override
340     public boolean equals(Object obj) {
341         // check class, and cast type
342         if (!(obj instanceof CurveSet2D))
343             return false;
344         PolyCurve2D<?> curveSet = (PolyCurve2D<?>) obj;
345 
346         // check the number of curves in each set
347         if (this.getCurveNumber()!=curveSet.getCurveNumber())
348             return false;
349 
350         // return false if at least one couple of curves does not match
351         for(int i=0; i<curves.size(); i++)
352             if(!this.curves.get(i).equals(curveSet.curves.get(i)))
353                 return false;
354         
355         // otherwise return true
356         return true;
357     }
358 
359 }