View Javadoc

1   /* File LineSegment2D.java 
2    *
3    * Project : Java Geometry Library
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  
24  // package
25  
26  package math.geom2d.line;
27  
28  import math.geom2d.AffineTransform2D;
29  import math.geom2d.Angle2D;
30  import math.geom2d.Box2D;
31  import math.geom2d.Point2D;
32  import math.geom2d.Shape2D;
33  import math.geom2d.Vector2D;
34  import math.geom2d.circulinear.CirculinearElement2D;
35  
36  
37  /**
38   * Line segment, defined as the set of points located between the two end
39   * points.
40   * {@See Line2D}
41   */
42  public class LineSegment2D extends AbstractLine2D
43  implements Cloneable, CirculinearElement2D {
44  
45      // ===================================================================
46      // constants
47  
48      // ===================================================================
49      // class variables
50  
51      // ===================================================================
52      // constructors
53  
54      /** Define a new Edge with two extremities. */
55      public LineSegment2D(java.awt.geom.Point2D point1,
56              java.awt.geom.Point2D point2) {
57          this(point1.getX(), point1.getY(), point2.getX(), point2.getY());
58      }
59  
60      /** Define a new Edge with two extremities. */
61      public LineSegment2D(double x1, double y1, double x2, double y2) {
62          super(x1, y1, x2-x1, y2-y1);
63      }
64  
65      // ===================================================================
66      // static methods
67  
68      /**
69       * Static factory for creating a new line segment between two points.
70       * @since 0.8.1
71       */
72      public final static LineSegment2D create(Point2D p1, Point2D p2) {
73      	return new LineSegment2D(p1, p2);
74      }
75  
76      public final static StraightLine2D getMedian(LineSegment2D edge) {
77          return new StraightLine2D(
78          		edge.x0+edge.dx*.5, edge.y0+edge.dy*.5,
79                  -edge.dy, edge.dx);
80      }
81  
82      /**
83       * Returns angle between two edges sharing one vertex.
84       */
85      public final static double getEdgeAngle(LineSegment2D edge1,
86              LineSegment2D edge2) {
87          double x0, y0, x1, y1, x2, y2;
88  
89          if (Math.abs(edge1.x0-edge2.x0)<Shape2D.ACCURACY
90                  &&Math.abs(edge1.y0-edge2.y0)<Shape2D.ACCURACY) {
91              x0 = edge1.x0;
92              y0 = edge1.y0;
93              x1 = edge1.x0+edge1.dx;
94              y1 = edge1.y0+edge1.dy;
95              x2 = edge2.x0+edge2.dx;
96              y2 = edge2.y0+edge2.dy;
97          } else if (Math.abs(edge1.x0+edge1.dx-edge2.x0)<Shape2D.ACCURACY
98                  &&Math.abs(edge1.y0+edge1.dy-edge2.y0)<Shape2D.ACCURACY) {
99              x0 = edge1.x0+edge1.dx;
100             y0 = edge1.y0+edge1.dy;
101             x1 = edge1.x0;
102             y1 = edge1.y0;
103             x2 = edge2.x0+edge2.dx;
104             y2 = edge2.y0+edge2.dy;
105         } else if (Math.abs(edge1.x0+edge1.dx-edge2.x0-edge2.dx)<Shape2D.ACCURACY
106                 &&Math.abs(edge1.y0+edge1.dy-edge2.y0-edge2.dy)<Shape2D.ACCURACY) {
107             x0 = edge1.x0+edge1.dx;
108             y0 = edge1.y0+edge1.dy;
109             x1 = edge1.x0;
110             y1 = edge1.y0;
111             x2 = edge2.x0;
112             y2 = edge2.y0;
113         } else if (Math.abs(edge1.x0-edge2.x0-edge2.dx)<Shape2D.ACCURACY
114                 &&Math.abs(edge1.y0-edge2.y0-edge2.dy)<Shape2D.ACCURACY) {
115             x0 = edge1.x0;
116             y0 = edge1.y0;
117             x1 = edge1.x0+edge1.dx;
118             y1 = edge1.y0+edge1.dy;
119             x2 = edge2.x0;
120             y2 = edge2.y0;
121         } else {// no common vertex -> return NaN
122             return Double.NaN;
123         }
124 
125         return Angle2D.getAngle(new Vector2D(x1-x0, y1-y0), new Vector2D(x2-x0,
126                 y2-y0));
127     }
128 
129     /**
130      * Checks if two line segment intersect. Uses the Point2D.ccw() method,
131      * which is based on Sedgewick algorithm.
132      * 
133      * @param edge1 a line segment
134      * @param edge2 a line segment
135      * @return true if the 2 line segments intersect
136      */
137     public final static boolean intersects(LineSegment2D edge1,
138             LineSegment2D edge2) {
139         Point2D e1p1 = edge1.getFirstPoint();
140         Point2D e1p2 = edge1.getLastPoint();
141         Point2D e2p1 = edge2.getFirstPoint();
142         Point2D e2p2 = edge2.getLastPoint();
143 
144         boolean b1 = Point2D.ccw(e1p1, e1p2, e2p1)
145                 *Point2D.ccw(e1p1, e1p2, e2p2)<=0;
146         boolean b2 = Point2D.ccw(e2p1, e2p2, e1p1)
147                 *Point2D.ccw(e2p1, e2p2, e1p2)<=0;
148         return b1&&b2;
149     }
150 
151     
152     // ===================================================================
153     // Methods specific to LineSegment2D
154 
155     /**
156      * Return the opposite vertex of the edge.
157      * 
158      * @param point one of the vertices of the edge
159      * @return the other vertex, or null if point is nor a vertex of the edge
160      */
161     public Point2D getOtherPoint(Point2D point) {
162         if (point.equals(new Point2D(x0, y0)))
163             return new Point2D(x0+dx, y0+dy);
164         if (point.equals(new Point2D(x0+dx, y0+dy)))
165             return new Point2D(x0, y0);
166         return null;
167     }
168 
169     /**
170      * Return the median of the edge, that is the locus of points located at
171      * equal distance of each vertex.
172      */
173     public StraightLine2D getMedian() {
174         // initial point is the middle of the edge -> x = x0+.5*dx
175         // direction vector is the initial direction vector rotated by pi/2.
176         return new StraightLine2D(x0+dx*.5, y0+dy*.5, -dy, dx);
177     }
178 
179     /**
180      * @deprecated lines will become immutable in a future release
181      */
182     @Deprecated
183     public void setLineSegment(Point2D p1, Point2D p2) {
184         this.x0 = p1.getX();
185         this.y0 = p1.getY();
186         this.dx = p2.getX()-this.x0;
187         this.dy = p2.getY()-this.y0;
188     }
189 
190     /**
191      * @deprecated lines will become immutable in a future release
192      */
193     @Deprecated
194     public void setLineSegment(double x1, double y1, double x2, double y2) {
195         this.x0 = x1;
196         this.y0 = y1;
197         this.dx = x2-x1;
198         this.dy = y2-y1;
199     }
200 
201     // ===================================================================
202     // methods implementing the CirculinearCurve2D interface
203 
204     /**
205      * Returns the length of the line segment.
206      */
207 	@Override
208     public double getLength() {
209         return Math.hypot(dx, dy);
210     }
211 
212 	/* (non-Javadoc)
213 	 * @see math.geom2d.circulinear.CirculinearCurve2D#getParallel(double)
214 	 */
215 	public LineSegment2D getParallel(double d) {
216         double dd = Math.sqrt(dx*dx+dy*dy);
217         return new LineSegment2D(
218         		x0+dy*d/dd, y0-dx*d/dd, 
219         		x0+dx+dy*d/dd, y0+dy-dx*d/dd);
220 	}
221 
222     // ===================================================================
223     // Methods implementing the OrientedCurve2D interface
224 
225     @Override
226     public double getSignedDistance(double x, double y) {
227         Point2D proj = super.getProjectedPoint(x, y);
228         if (contains(proj))
229             return super.getSignedDistance(x, y);
230 
231         double d = this.getDistance(x, y);
232         return super.getSignedDistance(x, y)>0 ? d : -d;
233     }
234     
235 //    // ===================================================================
236 //    // Methods implementing the CirculinearCurve2D interface
237 //
238 //	/* (non-Javadoc)
239 //	 * @see math.geom2d.circulinear.CirculinearCurve2D#transform(math.geom2d.transform.CircleInversion2D)
240 //	 */
241 //	public CirculinearCurve2D transform(CircleInversion2D inv) {
242 //		// Extract inversion parameters
243 //        Point2D center = inv.getCenter();
244 //        double r = inv.getRadius();
245 //        
246 //        StraightLine2D line = this.getSupportingLine();
247 //        
248 //        Point2D po = line.getProjectedPoint(center);
249 //        double d = line.getDistance(po);
250 //
251 //        // transform limits of edge, to obtain limits of arc
252 //        Point2D p1 = this.getFirstPoint().transform(inv);
253 //        Point2D p2 = this.getLastPoint().transform(inv);
254 //
255 //        // Degenerate case of a point belonging to the line:
256 //        // the transform is the line itself.
257 //        if (Math.abs(d)<Shape2D.ACCURACY){
258 //        	return new LineSegment2D(p1, p2);
259 //        }
260 //        
261 //        // angle from center to line
262 //        double angle = Angle2D.getHorizontalAngle(center, po);
263 //
264 //        // center of transformed circle
265 //        double r2 = r*r/d/2;
266 //        Point2D c2 = Point2D.createPolar(center, r2, angle);
267 //
268 //        // compute start and end angles of arc
269 //        double theta1 = Angle2D.getHorizontalAngle(c2, p1);
270 //        double theta2 = Angle2D.getHorizontalAngle(c2, p2);
271 //
272 //        boolean direct = line.isInside(center);
273 //
274 //        return new CircleArc2D(c2, r2, theta1, theta2, direct);
275 //	}
276 
277 
278     // ===================================================================
279     // Methods implementing the Curve2D interface
280 
281     /**
282      * Return the first point of the edge.
283      * 
284      * @return the first point of the edge
285      */
286 	@Override
287     public Point2D getFirstPoint() {
288         return new Point2D(x0, y0);
289     }
290 
291     /**
292      * Return the last point of the edge.
293      * 
294      * @return the last point of the edge
295      */
296 	@Override
297     public Point2D getLastPoint() {
298         return new Point2D(x0+dx, y0+dy);
299     }
300 
301     /**
302      * Returns the parameter of the first point of the edge, equals to 0.
303      */
304     public double getT0() {
305         return 0.0;
306     }
307 
308     /**
309      * Returns the parameter of the last point of the edge, equals to 1.
310      */
311     public double getT1() {
312         return 1.0;
313     }
314 
315     public Point2D getPoint(double t) {
316         t = Math.min(Math.max(t, 0), 1);
317         return new Point2D(x0+dx*t, y0+dy*t);
318     }
319 
320     /**
321      * Returns the LineSegment which start from last point of this line segment,
322      * and which ends at the fist point of this last segment.
323      */
324     public LineSegment2D getReverseCurve() {
325         return new LineSegment2D(x0+dx, y0+dy, x0, y0);
326     }
327 
328     // ===================================================================
329     // Methods implementing the Shape2D interface
330 
331     /**
332      * Returns true
333      */
334     public boolean isBounded() {
335         return true;
336     }
337 
338     public boolean contains(double xp, double yp) {
339         if (!super.supportContains(xp, yp))
340             return false;
341 
342         // compute position on the line
343         double t = getPositionOnLine(xp, yp);
344 
345         if (t<-ACCURACY)
346             return false;
347         if (t-1>ACCURACY)
348             return false;
349 
350         return true;
351     }
352 
353     /**
354      * Get the distance of the point (x, y) to this edge.
355      */
356     @Override
357     public double getDistance(double x, double y) {
358         Point2D proj = super.getProjectedPoint(x, y);
359         if (contains(proj))
360             return proj.distance(x, y);
361         double d1 = Math.hypot(x0-x, y0-y);
362         double d2 = Math.hypot(x0+dx-x, y0+dy-y);
363         return Math.min(d1, d2);
364     }
365 
366     @Override
367     public LineSegment2D transform(AffineTransform2D trans) {
368         double[] tab = trans.getCoefficients();
369         double x1 = x0*tab[0]+y0*tab[1]+tab[2];
370         double y1 = x0*tab[3]+y0*tab[4]+tab[5];
371         double x2 = (x0+dx)*tab[0]+(y0+dy)*tab[1]+tab[2];
372         double y2 = (x0+dx)*tab[3]+(y0+dy)*tab[4]+tab[5];
373         return new LineSegment2D(x1, y1, x2, y2);
374     }
375 
376     public Box2D getBoundingBox() {
377         return new Box2D(x0, x0+dx, y0, y0+dy);
378     }
379 
380     // =================================
381     // Methods implementing the Shape interface
382 
383     /**
384      * Appends a line to the current path.
385      * 
386      * @param path the path to modify
387      * @return the modified path
388      */
389     public java.awt.geom.GeneralPath appendPath(java.awt.geom.GeneralPath path) {
390         path.lineTo((float) x0+dx, (float) y0+dy);
391         return path;
392     }
393 
394     /**
395      * deprecated
396      */
397     public java.awt.geom.GeneralPath getGeneralPath() {
398         java.awt.geom.GeneralPath path = new java.awt.geom.GeneralPath();
399         path.moveTo((float) x0, (float) y0);
400         path.lineTo((float) (x0+dx), (float) (y0+dy));
401         return path;
402     }
403 
404     // ===================================================================
405     // Methods implementing the Object interface
406 
407     @Override
408     public String toString() {
409         return new String("LineSegment2D[(" + x0 + "," + y0 + ")-(" 
410                 + (x0+dx) + "," + (y0+dy) + ")]");
411     }
412     
413     @Override
414     public boolean equals(Object obj) {
415         if (!(obj instanceof LineSegment2D))
416             return false;
417         LineSegment2D edge = (LineSegment2D) obj;
418 
419         if (Math.abs(x0-edge.x0)>Shape2D.ACCURACY)
420             return false;
421         if (Math.abs(y0-edge.y0)>Shape2D.ACCURACY)
422             return false;
423         if (Math.abs(dx-edge.dx)>Shape2D.ACCURACY)
424             return false;
425         if (Math.abs(dy-edge.dy)>Shape2D.ACCURACY)
426             return false;
427         return true;
428     }
429     
430     @Override
431     public LineSegment2D clone() {
432         return new LineSegment2D(x0, y0, x0+dx, y0+dy);
433     }
434 }