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 }