1 /* File Point2D.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;
27
28 import java.awt.Graphics2D;
29 import java.io.Serializable;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Iterator;
33
34 import math.geom2d.circulinear.CirculinearDomain2D;
35 import math.geom2d.circulinear.CirculinearShape2D;
36 import math.geom2d.circulinear.GenericCirculinearDomain2D;
37 import math.geom2d.conic.Circle2D;
38 import math.geom2d.point.PointArray2D;
39 import math.geom2d.point.PointShape2D;
40 import math.geom2d.transform.CircleInversion2D;
41
42 /**
43 * <p>
44 * A point in the plane defined by its 2 Cartesian coordinates x and y.
45 * The class provides static methods to compute distance between two points.
46 * </p>
47 * <p>
48 * Important note: in a future release, Point2D will not extend
49 * <code>java.awt.geom.Point2D.Double<code> any more.
50 * </p>
51 */
52 public class Point2D extends java.awt.geom.Point2D.Double
53 implements PointShape2D, Cloneable, CirculinearShape2D {
54
55 // ===================================================================
56 // constants
57
58 private static final long serialVersionUID = 1L;
59
60 /**
61 * The point located at the infinity. This point is virtual, it is contained
62 * in every infinite shape, such as straight lines, hyperbolas and
63 * parabolas.
64 * @deprecated it is recommended to use exceptions instead (0.7.0)
65 */
66 @Deprecated
67 public final static Point2D INFINITY_POINT = new Point2D(
68 java.lang.Double.POSITIVE_INFINITY,
69 java.lang.Double.POSITIVE_INFINITY);
70
71 // ===================================================================
72 // class variables
73
74 // coordinates are inherited from java class for Point
75
76 // ===================================================================
77 // constructors
78
79 /** Constructs a new Point2D at position (0,0). */
80 public Point2D() {
81 super(0, 0);
82 }
83
84 /**
85 * Constructs a new Point2D at the given given position.
86 * Please consider using the static factory method instead (0.8.1).
87 */
88 public Point2D(double x, double y) {
89 super(x, y);
90 }
91
92 /**
93 * Constructs a new Point2D by copying coordinates of given java point.
94 * Please consider using the static factory method instead (0.8.1)
95 */
96 public Point2D(java.awt.geom.Point2D point) {
97 super(point.getX(), point.getY());
98 }
99
100 /**
101 * Constructs a new Point2D from two java awt.geom Point2D,
102 * summing their coordinates.
103 * @deprecated since 0.7.0
104 */
105 @Deprecated
106 public Point2D(java.awt.geom.Point2D point1, java.awt.geom.Point2D point2) {
107 super(point1.getX()+point2.getX(), point1.getY()+point2.getY());
108 }
109
110 /**
111 * Constructor from a java awt.geom Point2D, and two double. The (x,y)
112 * coordinates are added to the coordinates of given point.
113 * @deprecated use Point2D.createPolar() instead (0.7.0)
114 */
115 @Deprecated
116 public Point2D(java.awt.geom.Point2D point1, double x, double y) {
117 super(point1.getX()+x, point1.getY()+y);
118 }
119
120
121 // ===================================================================
122 // static methods
123
124 /**
125 * Static factory for creating a new point in cartesian coordinates.
126 * @since 0.8.1
127 */
128 public final static Point2D create(double x, double y) {
129 return new Point2D(x, y);
130 }
131
132 /**
133 * Static factory for creating a new point from an existing instance of
134 * java point.
135 * @since 0.8.1
136 */
137 public final static Point2D create(java.awt.geom.Point2D point) {
138 return new Point2D(point.getX(), point.getY());
139 }
140
141 /**
142 * Creates a new point from polar coordinates <code>rho</code> and
143 * <code>theta</code>.
144 */
145 public final static Point2D createPolar(double rho, double theta) {
146 return new Point2D(rho*Math.cos(theta), rho*Math.sin(theta));
147 }
148
149 /**
150 * Creates a new point from polar coordinates <code>rho</code> and
151 * <code>theta</code>, from the given point.
152 */
153 public final static Point2D createPolar(Point2D point, double rho,
154 double theta) {
155 return new Point2D(
156 point.getX()+rho*Math.cos(theta),
157 point.getY()+rho*Math.sin(theta));
158 }
159
160 /**
161 * Creates a new point from polar coordinates <code>rho</code> and
162 * <code>theta</code>, from the position (x0,y0).
163 */
164 public final static Point2D createPolar(double x0, double y0, double rho,
165 double theta) {
166 return new Point2D(x0+rho*Math.cos(theta), y0+rho*Math.sin(theta));
167 }
168
169 /**
170 * Computes the Euclidean distance between two points, given by their
171 * coordinates.
172 * Uses robust computation (via Math.hypot() method).
173 * @return the Euclidean distance between p1 and p2.
174 */
175 public final static double getDistance(double x1, double y1, double x2,
176 double y2) {
177 return Math.hypot(x2-x1, y2-y1);
178 }
179
180 /**
181 * Computes the Euclidean distance between two points.
182 * Uses robust computation (via Math.hypot() method).
183 * @param p1 the first point
184 * @param p2 the second point
185 * @return the Euclidean distance between p1 and p2.
186 */
187 public final static double getDistance(java.awt.geom.Point2D p1,
188 java.awt.geom.Point2D p2) {
189 return Math.hypot(p1.getX()-p2.getX(), p1.getY()-p2.getY());
190 }
191
192 /**
193 * Tests if the three points are colinear.
194 *
195 * @return true if three points lie on the same line.
196 */
197 public final static boolean isColinear(java.awt.geom.Point2D p1,
198 java.awt.geom.Point2D p2, java.awt.geom.Point2D p3) {
199 double dx1, dx2, dy1, dy2;
200 dx1 = p2.getX()-p1.getX();
201 dy1 = p2.getY()-p1.getY();
202 dx2 = p3.getX()-p1.getX();
203 dy2 = p3.getY()-p1.getY();
204
205 // tests if the two lines are parallel
206 return Math.abs(dx1*dy2-dy1*dx2)<Shape2D.ACCURACY;
207 }
208
209 /**
210 * Computes the orientation of the 3 points: returns +1 is the path
211 * P0->P1->P2 turns Counter-Clockwise, -1 if the path turns Clockwise, and 0
212 * if the point P2 is located on the line segment [P0 P1]. Algorithm taken
213 * from Sedgewick.
214 *
215 * @param p0 the initial point
216 * @param p1 the middle point
217 * @param p2 the last point
218 * @return +1, 0 or -1, depending on the relative position of the points
219 */
220 public final static int ccw(Point2D p0, Point2D p1, Point2D p2) {
221 double x0 = p0.getX();
222 double y0 = p0.getY();
223 double dx1 = p1.getX()-x0;
224 double dy1 = p1.getY()-y0;
225 double dx2 = p2.getX()-x0;
226 double dy2 = p2.getY()-y0;
227
228 if (dx1*dy2>dy1*dx2)
229 return +1;
230 if (dx1*dy2<dy1*dx2)
231 return -1;
232 if ((dx1*dx2<0)||(dy1*dy2<0))
233 return -1;
234 if ((dx1*dx1+dy1*dy1)<(dx2*dx2+dy2*dy2))
235 return +1;
236 return 0;
237 }
238
239 public final static Point2D midPoint(java.awt.geom.Point2D p1,
240 java.awt.geom.Point2D p2) {
241 return new Point2D((p1.getX()+p2.getX())/2, (p1.getY()+p2.getY())/2);
242 }
243
244 /**
245 * Computes the centroid, or center of mass, of an array of points.
246 *
247 * @param points an array of points
248 * @return the centroid of the points
249 */
250 public final static Point2D centroid(java.awt.geom.Point2D[] points) {
251 int n = points.length;
252 double sx = 0, sy = 0;
253 for (int i = 0; i<n; i++) {
254 sx += points[i].getX();
255 sy += points[i].getY();
256 }
257 return new Point2D(sx/n, sy/n);
258 }
259
260 /**
261 * Computes the weighted centroid, or center of mass, of an array of
262 * points.
263 *
264 * @param points an array of points
265 * @param weights an array of weights the same size as points
266 * @return the centroid of the points
267 */
268 public final static Point2D centroid(
269 java.awt.geom.Point2D[] points,
270 double[] weights) {
271 // number of points
272 int n = points.length;
273
274 // check size of second array
275 if(n!=weights.length) {
276 throw new RuntimeException("Arrays must have the same size");
277 }
278
279 // sum up weighted coordinates
280 double sx = 0, sy = 0, sw=0;
281 double w;
282 for (int i = 0; i<n; i++) {
283 w = weights[i];
284 sx += points[i].getX()*w;
285 sy += points[i].getY()*w;
286 sw += w;
287 }
288
289 // compute weighted average of each coordinate
290 return new Point2D(sx/sw, sy/sw);
291 }
292
293 /**
294 * Computes the centroid, or center of mass, of a collection of points.
295 *
296 * @param points a collection of points
297 * @return the centroid of the points
298 */
299 public final static Point2D centroid(Collection<? extends Point2D> points) {
300 int n = points.size();
301 double sx = 0, sy = 0;
302 for (Point2D point : points) {
303 sx += point.getX();
304 sy += point.getY();
305 }
306 return new Point2D(sx/n, sy/n);
307 }
308
309 /**
310 * Compute the centroid of three points.
311 *
312 * @param pt1 the first point
313 * @param pt2 the second point
314 * @param pt3 the third point
315 * @return the centroid of the 3 points
316 */
317 public final static Point2D centroid(java.awt.geom.Point2D pt1,
318 java.awt.geom.Point2D pt2, java.awt.geom.Point2D pt3) {
319 return new Point2D(
320 (pt1.getX()+pt2.getX()+pt3.getX())/3,
321 (pt1.getY()+pt2.getY()+pt3.getY())/3);
322 }
323
324
325 // ===================================================================
326 // Methods specific to Point2D
327
328 public Point2D plus(java.awt.geom.Point2D p) {
329 return new Point2D(p.getX()+x, p.getY()+y);
330 }
331
332 public Point2D minus(java.awt.geom.Point2D p) {
333 return new Point2D(x-p.getX(), y-p.getY());
334 }
335
336 /**
337 * Returns the new point translated by amount given in each direction.
338 * @param tx the translation in x direction
339 * @param ty the translation in y direction
340 * @return the translated point
341 */
342 public Point2D translate(double tx, double ty) {
343 return new Point2D(this.x+tx, this.y+ty);
344 }
345
346 /**
347 * Returns the new point scaled by amount given in each direction.
348 * @param kx the scale factor in x direction
349 * @param ky the scale factor in y direction
350 * @return the scaled point
351 */
352 public Point2D scale(double kx, double ky) {
353 return new Point2D(this.x*kx, this.y*ky);
354 }
355
356 /**
357 * Returns the new point scaled by the same amount in each direction.
358 * @param k the scale factor
359 * @return the scaled point
360 */
361 public Point2D scale(double k) {
362 return new Point2D(this.x*k, this.y*k);
363 }
364
365 /**
366 * Rotates the point by a given angle around the origin.
367 * @param theta the angle of rotation
368 * @return the rotated point.
369 */
370 public Point2D rotate(double theta){
371 double cot = Math.cos(theta);
372 double sit = Math.sin(theta);
373 return new Point2D(x*cot-y*sit, x*sit+y*cot);
374 }
375
376 /**
377 * Rotates the point by a given angle around an arbitrary center.
378 * @param center the center of the rotation
379 * @param theta the angle of rotation
380 * @return the rotated point.
381 */
382 public Point2D rotate(Point2D center, double theta){
383 double cx = center.getX();
384 double cy = center.getY();
385 double cot = Math.cos(theta);
386 double sit = Math.sin(theta);
387 return new Point2D(
388 x*cot-y*sit+(1-cot)*cx+sit*cy,
389 x*sit+y*cot+(1-cot)*cy-sit*cx);
390 }
391
392 // ===================================================================
393 // Methods specific to Point2D
394
395 /**
396 * Converts point to an integer version. Coordinates are rounded to the
397 * nearest integer.
398 *
399 * @return an instance of java.awt.Point
400 */
401 public java.awt.Point getAsInt() {
402 return new java.awt.Point((int) x, (int) y);
403 }
404
405 /**
406 * Converts point to a double version.
407 */
408 public java.awt.geom.Point2D.Double getAsDouble() {
409 return new java.awt.geom.Point2D.Double(x, y);
410 }
411
412 /**
413 * Converts point to a float version. Coordinates are rounded to the nearest
414 * float.
415 */
416 public java.awt.geom.Point2D.Float getAsFloat() {
417 return new java.awt.geom.Point2D.Float((float) x, (float) y);
418 }
419
420 /**
421 * Sets location specified as polar coordinate : distance from origin + angle
422 * with horizontal.
423 * @deprecated use Point2D.createPolar() instead (0.7.0)
424 */
425 @Deprecated
426 public void setPolarLocation(double rho, double theta) {
427 x = rho*Math.cos(theta);
428 y = rho*Math.sin(theta);
429 }
430
431 /**
432 * Sets location at distance 'rho' from given point, and making an angle
433 * 'theta' with horizontal.
434 * @deprecated use Point2D.createPolar() instead (0.7.0)
435 */
436 @Deprecated
437 public void setPolarLocation(java.awt.geom.Point2D point, double rho,
438 double theta) {
439 x = point.getX()+rho*Math.cos(theta);
440 y = point.getY()+rho*Math.sin(theta);
441 }
442
443
444 // ===================================================================
445 // Methods implementing CirculinearShape2D interface
446
447 /* (non-Javadoc)
448 * @see math.geom2d.circulinear.CirculinearShape2D#getBuffer(double)
449 */
450 public CirculinearDomain2D getBuffer(double dist) {
451 return new GenericCirculinearDomain2D(
452 new Circle2D(this, Math.abs(dist), dist>0));
453 }
454
455 /* (non-Javadoc)
456 * @see math.geom2d.circulinear.CirculinearShape2D#transform(CircleInversion2D)
457 */
458 public Point2D transform(CircleInversion2D inv) {
459 // get inversion parameters
460 Point2D center = inv.getCenter();
461 double r = inv.getRadius();
462
463 // compute distance and angle of transformed point
464 double d = r*r/Point2D.getDistance(this, center);
465 double theta = Angle2D.getHorizontalAngle(center, this);
466
467 // create the new point
468 return Point2D.createPolar(center, d, theta);
469 }
470
471
472 // ===================================================================
473 // Methods implementing Shape2D interface
474
475 /**
476 * Computes the distance between this and the point <code>point</code>.
477 */
478 public double getDistance(java.awt.geom.Point2D point) {
479 return getDistance(point.getX(), point.getY());
480 }
481
482 /**
483 * Computes the distance between current point and point with coordinate
484 * <code>(x,y)</code>. Uses the <code>Math.hypot()</code> function for
485 * better robustness than simple square root.
486 */
487 public double getDistance(double x, double y) {
488 return Math.hypot(getX()-x, getY()-y);
489 }
490
491 /**
492 * Returns true if the point is bounded. A point is unbounded if at least
493 * one of its coordinates is infinite or NaN.
494 *
495 * @return true if both coordinates of the point are finite
496 */
497 public boolean isBounded() {
498 if (java.lang.Double.isInfinite(this.x))
499 return false;
500 if (java.lang.Double.isInfinite(this.y))
501 return false;
502 if (java.lang.Double.isNaN(this.x))
503 return false;
504 if (java.lang.Double.isNaN(this.y))
505 return false;
506 return true;
507 }
508
509 public boolean isEmpty() {
510 return false;
511 }
512
513 /**
514 * Returns true if the two points are equal.
515 */
516 public boolean contains(double x, double y) {
517 return this.equals(new Point2D(x, y));
518 }
519
520 /**
521 * Returns true if the two points are equal.
522 */
523 public boolean contains(java.awt.geom.Point2D p) {
524 return this.equals(p);
525 }
526
527 /**
528 * Returns a PointSet2D, containing 0 or 1 point, depending on
529 * whether the point lies inside the specified box.
530 */
531 public math.geom2d.point.PointSet2D clip(Box2D box) {
532 // init result array
533 math.geom2d.point.PointSet2D set = new PointArray2D(0);
534
535 // return empty array if point is clipped
536 if (x<box.getMinX())
537 return set;
538 if (y<box.getMinY())
539 return set;
540 if (x>box.getMaxX())
541 return set;
542 if (y>box.getMaxY())
543 return set;
544
545 // return an array with the point
546 set.addPoint(this);
547 return set;
548 }
549
550 /**
551 * Returns a bounding box with zero width and zero height, whose coordinates
552 * limits are point coordinates.
553 */
554 public Box2D getBoundingBox() {
555 return new Box2D(getX(), getX(), getY(), getY());
556 }
557
558 /**
559 * Returns the transformed point.
560 */
561 public Point2D transform(AffineTransform2D trans) {
562 double[] tab = trans.getCoefficients();
563 return new Point2D(
564 x*tab[0]+y*tab[1]+tab[2],
565 x*tab[3]+y*tab[4]+tab[5]);
566 }
567
568 // ===================================================================
569 // Graphical methods
570
571 /**
572 * Draws the point on the specified Graphics2D, using default radius equal
573 * to 1.
574 *
575 * @param g2 the graphics to draw the point
576 */
577 public void draw(Graphics2D g2) {
578 this.draw(g2, 1);
579 }
580
581 /**
582 * Draws the point on the specified Graphics2D, by filling a disc with a
583 * given radius.
584 *
585 * @param g2 the graphics to draw the point
586 */
587 public void draw(Graphics2D g2, double r) {
588 g2.fill(new java.awt.geom.Ellipse2D.Double(x-r, y-r, 2*r, 2*r));
589 }
590
591 // ===================================================================
592 // Methods implementing the PointShape2D interface
593
594 /* (non-Javadoc)
595 * @see math.geom2d.point.PointShape2D#getPointNumber()
596 */
597 public int getPointNumber() {
598 return 1;
599 }
600
601 /* (non-Javadoc)
602 * @see math.geom2d.point.PointShape2D#getPoints()
603 */
604 public Collection<Point2D> getPoints() {
605 ArrayList<Point2D> array = new ArrayList<Point2D>(1);
606 array.add(this);
607 return array;
608 }
609
610 /* (non-Javadoc)
611 * @see java.lang.Iterable#iterator()
612 */
613 public Iterator<Point2D> iterator() {
614 return this.getPoints().iterator();
615 }
616
617
618 // ===================================================================
619 // Override of Object methods
620
621 @Override
622 public String toString() {
623 return new String("Point2D(" + x + ", "+y+")");
624 }
625
626 /**
627 * Two points are considered equal if their Euclidean distance is less
628 * than Shape2D.ACCURACY.
629 */
630 @Override
631 public boolean equals(Object obj) {
632 if (!(obj instanceof java.awt.geom.Point2D))
633 return false;
634 java.awt.geom.Point2D p = (java.awt.geom.Point2D) obj;
635 return this.distance(p.getX(), p.getY())<Shape2D.ACCURACY;
636 }
637
638 /**
639 * Creates a new Point2D object with same coordinates.
640 */
641 @Override
642 public Point2D clone() {
643 return new Point2D(x, y);
644 }
645 }