View Javadoc

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 }