View Javadoc

1   /* file : ParabolaArc2D.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 02 May 2007
24   *
25   */
26  
27  package math.geom2d.conic;
28  
29  import java.util.ArrayList;
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.Shape2D;
37  import math.geom2d.UnboundedShapeException;
38  import math.geom2d.Vector2D;
39  import math.geom2d.curve.AbstractSmoothCurve2D;
40  import math.geom2d.curve.Curve2D;
41  import math.geom2d.curve.Curve2DUtils;
42  import math.geom2d.curve.CurveArray2D;
43  import math.geom2d.curve.CurveSet2D;
44  import math.geom2d.curve.SmoothCurve2D;
45  import math.geom2d.domain.SmoothOrientedCurve2D;
46  import math.geom2d.line.LinearShape2D;
47  import math.geom2d.line.StraightLine2D;
48  
49  /**
50   * An arc of parabola, defined by a parent parabola, and two limits for the
51   * parametrization.
52   * 
53   * @author dlegland
54   */
55  public class ParabolaArc2D extends AbstractSmoothCurve2D
56  implements SmoothOrientedCurve2D, Cloneable {
57  
58      protected Parabola2D parabola = new Parabola2D();
59  
60      protected double     t0       = -10;
61      protected double     t1       = 10;
62  
63      public ParabolaArc2D(Parabola2D parabola, double t0, double t1) {
64          this.parabola = parabola;
65          this.t0 = t0;
66          this.t1 = t1;
67      }
68  
69      // ==========================================================
70      // methods specific to ParabolaArc2D
71  
72      public Parabola2D getParabola() {
73          return this.parabola;
74      }
75  
76      // ==========================================================
77      // methods implementing the OrientedCurve2D interface
78  
79      public double getWindingAngle(java.awt.geom.Point2D point) {
80          double angle0, angle1;
81  
82          boolean direct = parabola.isDirect();
83          boolean inside = this.isInside(point);
84  
85          if (Double.isInfinite(t0)) {
86              angle0 = parabola.getAngle()+(direct ? +1 : -1)*Math.PI/2;
87          } else {
88              angle0 = Angle2D.getHorizontalAngle(point, parabola.getPoint(t0));
89          }
90  
91          if (Double.isInfinite(t1)) {
92              angle1 = parabola.getAngle()+(direct ? +1 : -1)*Math.PI/2;
93          } else {
94              angle1 = Angle2D.getHorizontalAngle(point, parabola.getPoint(t1));
95          }
96  
97          if (inside) {
98              // turn CCW -> return positive angle
99              if (angle0>angle1)
100                 return 2*Math.PI-angle0+angle1;
101             else
102                 return angle1-angle0;
103         } else {
104             // turn CW -> return negative angle
105             if (angle0>angle1)
106                 return angle1-angle0;
107             else
108                 return (angle1-angle0)-2*Math.PI;
109         }
110     }
111 
112     public double getSignedDistance(java.awt.geom.Point2D p) {
113         return getSignedDistance(p.getX(), p.getY());
114     }
115 
116     public double getSignedDistance(double x, double y) {
117         if (isInside(new Point2D(x, y)))
118             return -getDistance(x, y);
119         return -getDistance(x, y);
120     }
121 
122     public boolean isInside(java.awt.geom.Point2D point) {
123         boolean direct = parabola.isDirect();
124         boolean inside = parabola.isInside(point);
125         if (inside&&direct)
126             return true;
127         if (!inside&&!direct)
128             return false;
129 
130         double pos = parabola.project(point);
131 
132         if (pos<t0) {
133             Point2D p0 = parabola.getPoint(t0);
134             Vector2D v0 = parabola.getTangent(t0);
135             StraightLine2D line0 = new StraightLine2D(p0, v0);
136             return line0.isInside(point);
137         }
138 
139         if (pos>t1) {
140             Point2D p1 = parabola.getPoint(t1);
141             Vector2D v1 = parabola.getTangent(t1);
142             StraightLine2D line1 = new StraightLine2D(p1, v1);
143             return line1.isInside(point);
144         }
145         return !direct;
146     }
147 
148     // ==========================================================
149     // methods implementing the SmoothCurve2D interface
150 
151     public Vector2D getTangent(double t) {
152         return parabola.getTangent(t);
153     }
154 
155     /**
156      * Returns the curvature of the parabola arc.
157      */
158     public double getCurvature(double t) {
159         return parabola.getCurvature(t);
160     }
161 
162     // ==========================================================
163     // methods implementing the ContinuousCurve2D interface
164 
165     /** Returns false, by definition of a parabola arc */
166     public boolean isClosed() {
167         return false;
168     }
169 
170     // ====================================================================
171     // methods implementing the Curve2D interface
172 
173     /**
174      * Returns the position of the first point of the parabola arc.
175      */
176     public double getT0() {
177         return t0;
178     }
179 
180     /**
181      * Returns the position of the last point of the parabola arc.
182      */
183     public double getT1() {
184         return t1;
185     }
186 
187     public Point2D getPoint(double t) {
188         t = Math.min(Math.max(t, t0), t1);
189         return parabola.getPoint(t);
190     }
191 
192     public double getPosition(java.awt.geom.Point2D point) {
193         if (!this.parabola.contains(point))
194             return Double.NaN;
195         double t = this.parabola.getPosition(point);
196         if (t-t0<-ACCURACY)
197             return Double.NaN;
198         if (t1-t<ACCURACY)
199             return Double.NaN;
200         return t;
201     }
202 
203     public double project(java.awt.geom.Point2D point) {
204         double t = this.parabola.project(point);
205         return Math.min(Math.max(t, t0), t1);
206     }
207 
208     public Collection<Point2D> getIntersections(LinearShape2D line) {
209         Collection<Point2D> inters0 = this.parabola.getIntersections(line);
210         ArrayList<Point2D> inters = new ArrayList<Point2D>();
211         for (Point2D point : inters0) {
212             double pos = this.parabola.getPosition(point);
213             if (pos>this.t0&&pos<this.t1)
214                 inters.add(point);
215         }
216 
217         return inters;
218     }
219 
220     /**
221      * Returns the parabola arc which refers to the reversed parent parabola,
222      * and with inverted parametrization bounds.
223      */
224     public ParabolaArc2D getReverseCurve() {
225         return new ParabolaArc2D(this.parabola.getReverseCurve(), -t1, -t0);
226     }
227 
228     public ParabolaArc2D getSubCurve(double t0, double t1) {
229         if (t1<t0)
230             return null;
231         t0 = Math.max(this.t0, t0);
232         t1 = Math.min(this.t1, t1);
233         return new ParabolaArc2D(parabola, t0, t1);
234     }
235 
236     // ====================================================================
237     // methods implementing the Shape2D interface
238 
239     public double getDistance(java.awt.geom.Point2D p) {
240         return getDistance(p.getX(), p.getY());
241     }
242 
243     public double getDistance(double x, double y) {
244         // TODO Auto-generated method stub
245         return this.getAsPolyline(100).getDistance(x, y);
246     }
247 
248     /**
249      * Returns true if the arc is bounded, i.e. if both limits are finite.
250      */
251     public boolean isBounded() {
252         if (t0==Double.NEGATIVE_INFINITY)
253             return false;
254         if (t1==Double.POSITIVE_INFINITY)
255             return false;
256         return true;
257     }
258 
259     /**
260      * Return true if t1<t0.
261      */
262     public boolean isEmpty() {
263         return t1<=t0;
264     }
265 
266     /**
267      * Clip the parabola arc by a box. The result is an instance of CurveSet2D<ParabolaArc2D>,
268      * which contains only instances of ParabolaArc2D. If the parabola arc is
269      * not clipped, the result is an instance of CurveSet2D<ParabolaArc2D>
270      * which contains 0 curves.
271      */
272     public CurveSet2D<? extends ParabolaArc2D> clip(Box2D box) {
273         // Clip the curve
274         CurveSet2D<SmoothCurve2D> set = Curve2DUtils.clipSmoothCurve(this, box);
275 
276         // Stores the result in appropriate structure
277         CurveArray2D<ParabolaArc2D> result = 
278         	new CurveArray2D<ParabolaArc2D>(set.getCurveNumber());
279 
280         // convert the result
281         for (Curve2D curve : set.getCurves()) {
282             if (curve instanceof ParabolaArc2D)
283                 result.addCurve((ParabolaArc2D) curve);
284         }
285         return result;
286     }
287 
288     public Box2D getBoundingBox() {
289         // TODO Auto-generated method stub
290         return this.getAsPolyline(100).getBoundingBox();
291     }
292 
293     public ParabolaArc2D transform(AffineTransform2D trans) {
294         Parabola2D par = parabola.transform(trans);
295 
296         // Compute position of end points on the transformed parabola
297         double startPos = Double.isInfinite(t0) ? Double.NEGATIVE_INFINITY
298                 : par.project(this.getFirstPoint().transform(trans));
299         double endPos = Double.isInfinite(t1) ? Double.POSITIVE_INFINITY : par
300                 .project(this.getLastPoint().transform(trans));
301 
302         // Compute the new arc
303         return new ParabolaArc2D(par, startPos, endPos);
304     }
305 
306     // ====================================================================
307     // methods implementing the Shape interface
308 
309     public boolean contains(double x, double y) {
310         // Check on parent parabola
311         if (!parabola.contains(x, y))
312             return false;
313         
314         // Check if position of point is inside of bounds
315         double t = parabola.getPosition(new Point2D(x, y));
316         if (t<this.t0)
317             return false;
318         if (t>this.t1)
319             return false;
320 
321         return true;
322     }
323 
324     public boolean contains(java.awt.geom.Point2D point) {
325         return contains(point.getX(), point.getY());
326     }
327 
328     // ====================================================================
329     // Drawing methods
330 
331     public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
332     	// Check curve is bounded
333         if (!this.isBounded())
334             throw new UnboundedShapeException(this);
335 
336         // Compute position and tangent at extremities
337         Point2D p1 = this.getFirstPoint();
338         Point2D p2 = this.getLastPoint();
339         Vector2D v1 = this.getTangent(this.getT0());
340         Vector2D v2 = this.getTangent(this.getT1());
341         
342         // Compute tangent lines at extremities
343         StraightLine2D line1 = new StraightLine2D(p1, v1);
344         StraightLine2D line2 = new StraightLine2D(p2, v2);
345         
346         // Compute intersection point of tangent lines
347         Point2D pc = line1.getIntersection(line2);
348         
349         // Use quadratic curve to represent (exactly) the parabola arc
350         path.quadTo(pc.getX(), pc.getY(), p2.getX(), p2.getY());
351         return path;
352     }
353 
354     public java.awt.geom.GeneralPath getGeneralPath() {
355         if (!this.isBounded())
356             throw new UnboundedShapeException(this);
357         return this.getAsPolyline(32).getGeneralPath();
358     }
359 
360     // ====================================================================
361     // Methods inherited from object interface
362 
363     @Override
364     public String toString() {
365     	return String.format("ParabolaArc2D(%f,%f,%f,%f,%f,%f)", 
366     			parabola.xv, parabola.yv, parabola.a, parabola.theta, t0, t1);
367     }
368 
369     @Override
370     public boolean equals(Object obj) {
371         if (!(obj instanceof ParabolaArc2D))
372             return false;
373         ParabolaArc2D arc = (ParabolaArc2D) obj;
374 
375         if (!this.parabola.equals(arc.parabola))
376             return false;
377         if (Math.abs(this.t0-arc.t0)>Shape2D.ACCURACY)
378             return false;
379         if (Math.abs(this.t1-arc.t1)>Shape2D.ACCURACY)
380             return false;
381 
382         return true;
383     }
384     
385     @Override
386     public ParabolaArc2D clone() {
387         return new ParabolaArc2D(parabola.clone(), t0, t1);
388     }
389 }