View Javadoc

1   /* file : LineArc2D.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 24 dec. 2005
24   *
25   */
26  
27  package math.geom2d.line;
28  
29  import java.util.ArrayList;
30  import java.util.Collection;
31  
32  import math.geom2d.AffineTransform2D;
33  import math.geom2d.Box2D;
34  import math.geom2d.Point2D;
35  import math.geom2d.Shape2D;
36  import math.geom2d.UnboundedShapeException;
37  import math.geom2d.domain.SmoothOrientedCurve2D;
38  
39  /**
40   * LineArc2D is a generic class to represent edges, straight lines, and rays.
41   * It is defined like other linear shapes: origin point, and direction vector.
42   * Moreover, two internal variables t0 and t1 define the limit of the object
43   * (with t0<t1). t0=0 and t1=1: this is an edge. t0=-inf and t1=inf: this is a
44   * straight line. t0=0 and t1=inf: this is a ray.
45   * 
46   * @author dlegland
47   */
48  public class LineArc2D extends AbstractLine2D 
49  implements SmoothOrientedCurve2D, Cloneable {
50  
51      protected double t0 = 0;
52      protected double t1 = 1;
53  
54      // ===================================================================
55      // Constructors
56  
57      /**
58       * @param point1 the point located at t=0
59       * @param point2 the point located at t=1
60       * @param t0 the lower bound of line arc parameterization
61       * @param t1 the upper bound of line arc parameterization
62       */
63      public LineArc2D(Point2D point1, Point2D point2, double t0, double t1) {
64          this(point1.getX(), point1.getY(), 
65          		point2.getX()-point1.getX(), point2.getY()-point1.getY(), 
66          		t0, t1);
67      }
68  
69      /**
70       * Construct a line arc contained in the same straight line as first
71       * argument, with bounds of arc given by t0 and t1
72       * 
73       * @param line an object defining the supporting line
74       * @param t0 the lower bound of line arc parameterization
75       * @param t1 the upper bound of line arc parameterization
76       */
77      public LineArc2D(LinearShape2D line, double t0, double t1) {
78          super(line);
79          this.t0 = t0;
80          this.t1 = t1;
81      }
82  
83      /**
84       * Construction by copy of another line arc
85       * 
86       * @param line the line to copy
87       */
88      public LineArc2D(LineArc2D line) {
89          this(line.x0, line.y0, line.dx, line.dy, line.t0, line.t1);
90      }
91  
92      /**
93       * Construct a line arc by the parameters of the supporting line and two
94       * positions on the line.
95       * 
96       * @param x1 the x-coordinate of the first point
97       * @param y1 the y-coordinate of the first point
98       * @param dx the x-coordinate of the direction vector
99       * @param dy the y-coordinate of the direction vector
100      * @param t0 the starting position of the arc
101      * @param t1 the ending position of the arc
102      */
103     public LineArc2D(double x1, double y1, double dx, double dy, double t0,
104             double t1) {
105         super(x1, y1, dx, dy);
106         this.t0 = t0;
107         this.t1 = t1;
108     }
109 
110     
111     // ===================================================================
112     // Static methods
113     
114     /**
115      * Static factory for creating a new LineArc2D
116      * @since 0.8.1
117      */
118     public LineArc2D create(Point2D p1, Point2D p2, double t0, double t1) {
119     	return new LineArc2D(p1, p2, t0, t1);
120     }
121 
122     
123     // ===================================================================
124     // methods specific to LineArc2D
125 
126     /**
127      * Returns the length of the line arc.
128      */
129 	@Override
130     public double getLength() {
131         if (t0!=Double.NEGATIVE_INFINITY&&t1!=Double.POSITIVE_INFINITY)
132             return getPoint1().getDistance(getPoint2());
133         else
134             return Double.POSITIVE_INFINITY;
135     }
136 
137     /**
138      * Return the first point of the line arc. In the case of a line arc
139      * starting from -infinity, throws an UnboundedShapeException.
140      * 
141      * @return the first point of the arc
142      */
143     public Point2D getPoint1() {
144         if (t0!=Double.NEGATIVE_INFINITY)
145             return new Point2D(x0+t0*dx, y0+t0*dy);
146         else
147             throw new UnboundedShapeException(this);
148     }
149 
150     /**
151      * Return the last point of the line arc. In the case of a line arc ending
152      * at infinity, throws an UnboundedShapeException.
153      * 
154      * @return the last point of the arc.
155      */
156     public Point2D getPoint2() {
157         if (t1!=Double.POSITIVE_INFINITY)
158             return new Point2D(x0+t1*dx, y0+t1*dy);
159         else
160             throw new UnboundedShapeException(this);
161     }
162 
163     public double getX1() {
164         if (t0!=Double.NEGATIVE_INFINITY)
165             return x0+t0*dx;
166         else
167             return Double.NEGATIVE_INFINITY;
168     }
169 
170     public double getY1() {
171         if (t0!=Double.NEGATIVE_INFINITY)
172             return y0+t0*dy;
173         else
174             return Double.NEGATIVE_INFINITY;
175     }
176 
177     public double getX2() {
178         if (t1!=Double.POSITIVE_INFINITY)
179             return x0+t1*dx;
180         else
181             return Double.POSITIVE_INFINITY;
182     }
183 
184     public double getY2() {
185         if (t1!=Double.POSITIVE_INFINITY)
186             return y0+t1*dy;
187         else
188             return Double.POSITIVE_INFINITY;
189     }
190 
191     // ===================================================================
192     // methods implementing the CirculinearCurve2D interface
193 
194 	/* (non-Javadoc)
195 	 * @see math.geom2d.circulinear.CirculinearCurve2D#getParallel(double)
196 	 */
197 	public LineArc2D getParallel(double d) {
198         double dd = Math.hypot(dx, dy);
199         return new LineArc2D(x0+dy*d/dd, y0-dx*d/dd, dx, dy, t0, t1);
200 	}
201 
202 
203 	// ===================================================================
204     // methods of Curve2D interface
205 
206     /**
207      * Returns the parameter of the first point of the line arc, 
208      * arbitrarily set to 0.
209      */
210     public double getT0() {
211         return t0;
212     }
213 
214     /**
215      * Returns the parameter of the last point of the line arc, 
216      * arbitrarily set to 1.
217      */
218     public double getT1() {
219         return t1;
220     }
221 
222     public Point2D getPoint(double t) {
223         if (t<t0)
224             t = t0;
225         if (t>t1)
226             t = t1;
227 
228         if (Double.isInfinite(t))
229             throw new UnboundedShapeException(this);
230         else
231             return new Point2D(x0+dx*t, y0+dy*t);
232     }
233 
234     /**
235      * Return the first point of the edge. In the case of a line, or a ray
236      * starting from -infinity, returns Point2D.INFINITY_POINT.
237      * 
238      * @return the last point of the arc
239      */
240 	@Override
241     public Point2D getFirstPoint() {
242         if (!Double.isInfinite(t0))
243             return new Point2D(x0+t0*dx, y0+t0*dy);
244         else
245             throw new UnboundedShapeException(this);
246     }
247 
248     /**
249      * Return the last point of the edge. In the case of a line, or a ray ending
250      * at infinity, returns Point2D.INFINITY_POINT.
251      * 
252      * @return the last point of the arc
253      */
254 	@Override
255     public Point2D getLastPoint() {
256         if (!Double.isInfinite(t1))
257             return new Point2D(x0+t1*dx, y0+t1*dy);
258         else
259             throw new UnboundedShapeException(this);
260     }
261 
262 	@Override
263     public Collection<Point2D> getSingularPoints() {
264         ArrayList<Point2D> list = new ArrayList<Point2D>(2);
265         if (t0!=Double.NEGATIVE_INFINITY)
266             list.add(this.getFirstPoint());
267         if (t1!=Double.POSITIVE_INFINITY)
268             list.add(this.getLastPoint());
269         return list;
270     }
271 
272 	@Override
273     public boolean isSingular(double pos) {
274         if (Math.abs(pos-t0)<Shape2D.ACCURACY)
275             return true;
276         if (Math.abs(pos-t1)<Shape2D.ACCURACY)
277             return true;
278         return false;
279     }
280 
281     @Override
282     public Collection<? extends LineArc2D> getContinuousCurves() {
283     	return wrapCurve(this);
284     }
285 
286     /**
287      * Returns the line arc which have the same trace, but has the inverse
288      * parameterization.
289      */
290     public LineArc2D getReverseCurve() {
291         return new LineArc2D(x0, y0, -dx, -dy, -t1, -t0);
292     }
293 
294     /**
295      * Returns a new LineArc2D, which is the portion of this LineArc2D delimited
296      * by parameters t0 and t1.
297      */
298     @Override
299     public LineArc2D getSubCurve(double t0, double t1) {
300         t0 = Math.max(t0, this.getT0());
301         t1 = Math.min(t1, this.getT1());
302         return new LineArc2D(this, t0, t1);
303     }
304 
305     // ===================================================================
306     // methods of Shape2D interface
307 
308     /** return true if both t0 and t1 are different from infinity. */
309     public boolean isBounded() {
310         if (t1==Double.POSITIVE_INFINITY)
311             return false;
312         if (t0==Double.NEGATIVE_INFINITY)
313             return false;
314         return true;
315     }
316 
317     public Box2D getBoundingBox() {
318         return new Box2D(x0+t0*dx, x0+t1*dx, y0+t0*dy, y0+t1*dy);
319     }
320 
321     // ===================================================================
322     // methods of Shape interface
323 
324     @Override
325     public boolean contains(java.awt.geom.Point2D pt) {
326         return contains(pt.getX(), pt.getY());
327     }
328 
329     public boolean contains(double xp, double yp) {
330         if (!super.supportContains(xp, yp))
331             return false;
332 
333         // compute position on the line
334         double t = getPositionOnLine(xp, yp);
335 
336         if (t-t0<-ACCURACY)
337             return false;
338         if (t-t1>ACCURACY)
339             return false;
340 
341         return true;
342     }
343 
344     public java.awt.geom.GeneralPath getGeneralPath() {
345         if (!this.isBounded())
346             throw new UnboundedShapeException(this);
347         java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
348         path.moveTo((float) (x0+t0*dx), (float) (y0+t0*dy));
349         path.lineTo((float) (x0+t1*dx), (float) (y0+t1*dy));
350         return path;
351     }
352 
353     /**
354      * Appends a line to the current path. If t0 or t1 is infinite, throws a new
355      * UnboundedShapeException.
356      * 
357      * @param path the path to modify
358      * @return the modified path
359      */
360     public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
361         if (!this.isBounded())
362             throw new UnboundedShapeException(this);
363         if (t0==Double.NEGATIVE_INFINITY)
364             return path;
365         if (t1==Double.POSITIVE_INFINITY)
366             return path;
367         path.lineTo((float) getX1(), (float) getY1());
368         path.lineTo((float) getX2(), (float) getY2());
369         return path;
370     }
371 
372     @Override
373     public LineArc2D transform(AffineTransform2D trans) {
374         double[] tab = trans.getCoefficients();
375         double x1 = x0*tab[0]+y0*tab[1]+tab[2];
376         double y1 = x0*tab[3]+y0*tab[4]+tab[5];
377         return new LineArc2D(x1, y1, dx*tab[0]+dy*tab[1], dx*tab[3]+dy*tab[4],
378                 t0, t1);
379     }
380 
381     @Override
382     public String toString() {
383         return new String("LineArc2D(" + x0 + "," + y0 + "," + 
384             		dx + "," + dy + "," + t0 + "," + t1 +")");
385    }
386 
387     // ===================================================================
388     // methods of Object interface
389 
390     @Override
391     public boolean equals(Object obj) {
392         if (!(obj instanceof LineArc2D))
393             return false;
394         LineArc2D arc = (LineArc2D) obj;
395 
396         // First check if two arcs lie on the same line
397         if (!this.isColinear(arc))
398             return false;
399 
400         // Check limits for straight lines
401         if (t0==Double.NEGATIVE_INFINITY&&t1==Double.POSITIVE_INFINITY) {
402             // Check limits
403             if (arc.t0!=Double.NEGATIVE_INFINITY)
404                 return false;
405             if (arc.t1!=Double.POSITIVE_INFINITY)
406                 return false;
407             return true;
408         }
409 
410         // Check limits for rays
411         if (t0==Double.NEGATIVE_INFINITY) {
412             // Check limits
413             if (arc.t0==Double.NEGATIVE_INFINITY)
414                 return this.getPoint2().getDistance(arc.getPoint2())<Shape2D.ACCURACY;
415             if (arc.t1==Double.POSITIVE_INFINITY)
416                 return this.getPoint2().getDistance(arc.getPoint1())<Shape2D.ACCURACY;
417             return false;
418         }
419         if (t1==Double.POSITIVE_INFINITY) {
420             // Check limits
421             if (arc.t0==Double.NEGATIVE_INFINITY)
422                 return this.getPoint1().getDistance(arc.getPoint2())<Shape2D.ACCURACY;
423             if (arc.t1==Double.POSITIVE_INFINITY)
424                 return this.getPoint1().getDistance(arc.getPoint1())<Shape2D.ACCURACY;
425             return false;
426         }
427 
428         // current line arc is neither a line nor an arc, check that arc is an
429         // edge
430         if (arc.t0==Double.NEGATIVE_INFINITY||arc.t0==Double.POSITIVE_INFINITY)
431             return false;
432         if (arc.t1==Double.NEGATIVE_INFINITY||arc.t1==Double.POSITIVE_INFINITY)
433             return false;
434 
435         // We still have to test the case of edges
436         if (getPoint1().getDistance(arc.getPoint1())<ACCURACY)
437             return getPoint2().getDistance(arc.getPoint2())<ACCURACY;
438 
439         if (getPoint1().getDistance(arc.getPoint2())>ACCURACY)
440             return false;
441         if (getPoint2().getDistance(arc.getPoint1())>ACCURACY)
442             return false;
443         return true;
444     }
445     
446     @Override
447     public LineArc2D clone() {
448         return new LineArc2D(x0, y0, dx, dy, t0, t1);
449     }
450 }