View Javadoc

1   /* file : PolyOrientedCurve2D.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.domain;
28  
29  // Imports
30  import java.util.Collection;
31  
32  import math.geom2d.AffineTransform2D;
33  import math.geom2d.Angle2D;
34  import math.geom2d.Box2D;
35  import math.geom2d.Point2D;
36  import math.geom2d.Vector2D;
37  import math.geom2d.curve.ContinuousCurve2D;
38  import math.geom2d.curve.Curve2D;
39  import math.geom2d.curve.Curve2DUtils;
40  import math.geom2d.curve.CurveArray2D;
41  import math.geom2d.curve.CurveSet2D;
42  import math.geom2d.curve.PolyCurve2D;
43  import math.geom2d.curve.SmoothCurve2D;
44  import math.geom2d.line.StraightLine2D;
45  
46  /**
47   * A PolyOrientedCurve2D is a set of piecewise smooth curve arcs, such that the
48   * end of a curve is the beginning of the next curve, and such that they do not
49   * intersect nor self-intersect.
50   * <p>
51   * 
52   * @author dlegland
53   */
54  public class PolyOrientedCurve2D<T extends ContinuousOrientedCurve2D> extends
55          PolyCurve2D<T> implements ContinuousOrientedCurve2D {
56  
57      // ===================================================================
58      // Constructors
59  
60      public PolyOrientedCurve2D() {
61          super();
62      }
63  
64      public PolyOrientedCurve2D(int size) {
65          super(size);
66      }
67  
68      public PolyOrientedCurve2D(T[] curves) {
69          super(curves);
70      }
71  
72      public PolyOrientedCurve2D(T[] curves, boolean closed) {
73          super(curves, closed);
74      }
75  
76      public PolyOrientedCurve2D(Collection<? extends T> curves) {
77          super(curves);
78      }
79  
80      public PolyOrientedCurve2D(Collection<? extends T> curves, boolean closed) {
81          super(curves, closed);
82      }
83  
84      
85      // ===================================================================
86      // static methods
87  
88  //    /**
89  //     * Static factory for creating a new PolyOrientedCurve2D from a collection of
90  //     * curves.
91  //     * @since 0.8.1
92  //     */
93  //    public static <T extends ContinuousOrientedCurve2D> PolyOrientedCurve2D<T>
94  //    create(Collection<T> curves) {
95  //    	return new PolyOrientedCurve2D<T>(curves);
96  //    }
97  //    
98  //    /**
99  //     * Static factory for creating a new PolyOrientedCurve2D from an array of
100 //     * curves.
101 //     * @since 0.8.1
102 //     */
103 //    public static <T extends ContinuousOrientedCurve2D> 
104 //    PolyOrientedCurve2D<T> create(T[] curves) {
105 //    	return new PolyOrientedCurve2D<T>(curves);
106 //    }
107 
108     
109     // ===================================================================
110     // Methods specific to PolyOrientedCurve2D
111 
112     public double getWindingAngle(java.awt.geom.Point2D point) {
113         double angle = 0;
114         for (OrientedCurve2D curve : this.curves)
115             angle += curve.getWindingAngle(point);
116         return angle;
117     }
118 
119     public double getSignedDistance(java.awt.geom.Point2D p) {
120         return getSignedDistance(p.getX(), p.getY());
121     }
122 
123     /*
124      * (non-Javadoc)
125      * 
126      * @see math.geom2d.Shape2D#getSignedDistance(math.geom2d.Point2D)
127      */
128     public double getSignedDistance(double x, double y) {
129         double dist = this.getDistance(x, y);
130 
131         if (this.isInside(new Point2D(x, y)))
132             dist = -dist;
133 
134         return dist;
135     }
136 
137     private static Vector2D getTangent(ContinuousCurve2D curve, double pos) {
138         // For smooth curves, simply call the getTangent() method
139         if (curve instanceof SmoothCurve2D)
140             return ((SmoothCurve2D) curve).getTangent(pos);
141 
142         // Extract sub curve and recursively call this method on the sub curve
143         if (curve instanceof CurveSet2D) {
144             CurveSet2D<?> curveSet = (CurveSet2D<?>) curve;
145             double pos2 = curveSet.getLocalPosition(pos);
146             Curve2D subCurve = curveSet.getChildCurve(pos);
147             return getTangent((ContinuousCurve2D) subCurve, pos2);
148         }
149 
150         System.err
151                 .println("Unknown type of curve: should be either continuous or curveset");
152         return null;
153     }
154 
155     public boolean isInside(java.awt.geom.Point2D point) {
156         double pos = this.project(point);
157 
158         if (this.isSingular(pos)) {
159 
160             // number of curves
161             int n = this.getCurveNumber();
162 
163             // vertex index and position
164             int i = this.getCurveIndex(pos);
165             if (pos/2-i>.25)
166                 i++;
167 
168             // Test case of point equal to last position
169             if (Math.round(pos)==2*n-1) {
170                 pos = 0;
171                 i = 0;
172             }
173 
174             // int i = (int) Math.floor((pos+1.0)/2);
175             Point2D vertex = this.getPoint(2*pos);
176 
177             // indices of previous and next curves
178             int iPrev = i>0 ? i-1 : n-1;
179             // int iNext = i<n-1 ? i+1 : 0;
180             int iNext = i;
181 
182             // previous and next curves
183             T prev = this.curves.get(iPrev);
184             T next = this.curves.get(iNext);
185 
186             // tangent vectors of the 2 neighbor curves
187             Vector2D v1 = getTangent(prev, prev.getT1());
188             Vector2D v2 = getTangent(next, next.getT0());
189 
190             // compute on which side of each ray the test point lies
191             boolean in1 = new StraightLine2D(vertex, v1).isInside(point);
192             boolean in2 = new StraightLine2D(vertex, v2).isInside(point);
193 
194             // check if angle is acute or obtuse
195             if (Angle2D.getAngle(v1, v2)<Math.PI) {
196                 return in1&&in2;
197             } else {
198                 return in1||in2;
199             }
200         } else {
201             // Simply call the method isInside on the child curve
202             return this.getChildCurve(pos).isInside(point);
203         }
204     }
205 
206     @Override
207     public PolyOrientedCurve2D<? extends ContinuousOrientedCurve2D> getReverseCurve() {
208         ContinuousOrientedCurve2D[] curves2 = new ContinuousOrientedCurve2D[curves
209                 .size()];
210         int n = curves.size();
211         for (int i = 0; i<n; i++)
212             curves2[i] = (ContinuousOrientedCurve2D)curves.get(n-1-i).getReverseCurve();
213         return new PolyOrientedCurve2D<ContinuousOrientedCurve2D>(curves2);
214     }
215 
216     /**
217      * Return an instance of PolyOrientedCurve2D.
218      */
219     @Override
220     public PolyOrientedCurve2D<? extends ContinuousOrientedCurve2D> getSubCurve(
221             double t0, double t1) {
222         PolyCurve2D<?> set = super.getSubCurve(t0, t1);
223         PolyOrientedCurve2D<ContinuousOrientedCurve2D> subCurve = new PolyOrientedCurve2D<ContinuousOrientedCurve2D>();
224         subCurve.setClosed(false);
225 
226         // convert to PolySmoothCurve by adding curves.
227         for (Curve2D curve : set.getCurves())
228             subCurve.addCurve((ContinuousOrientedCurve2D) curve);
229 
230         return subCurve;
231     }
232 
233     /**
234      * Clip the PolyCurve2D by a box. The result is an instance of CurveSet2D<ContinuousOrientedCurve2D>,
235      * which contains only instances of ContinuousOrientedCurve2D. If the
236      * PolyCurve2D is not clipped, the result is an instance of CurveSet2D<ContinuousOrientedCurve2D>
237      * which contains 0 curves.
238      */
239     @Override
240     public CurveSet2D<? extends ContinuousOrientedCurve2D> clip(Box2D box) {
241         // Clip the curve
242         CurveSet2D<? extends Curve2D> set = Curve2DUtils.clipCurve(this, box);
243 
244         // Stores the result in appropriate structure
245         int n = set.getCurveNumber();
246         CurveArray2D<ContinuousOrientedCurve2D> result = 
247         	new CurveArray2D<ContinuousOrientedCurve2D>(n);
248 
249         // convert the result
250         for (Curve2D curve : set.getCurves()) {
251             if (curve instanceof ContinuousOrientedCurve2D)
252                 result.addCurve((ContinuousOrientedCurve2D) curve);
253         }
254         return result;
255     }
256 
257     @Override
258     public PolyOrientedCurve2D<?> transform(AffineTransform2D trans) {
259         PolyOrientedCurve2D<ContinuousOrientedCurve2D> result = 
260         	new PolyOrientedCurve2D<ContinuousOrientedCurve2D>();
261         for (ContinuousOrientedCurve2D curve : curves)
262             result.addCurve((ContinuousOrientedCurve2D)curve.transform(trans));
263         return result;
264     }
265 }