public class DPoint { /* This class represents a simple point (x,y) in double format. More importantly, it includes wonderful convenient simple functions for manipulating points. */ public double x, y; // constructors ----------------------------------------------------------------------------- public DPoint(DPoint p) { x = p.x; y = p.y; } // make a copy of a point public DPoint(double x, double y) { this.x = x; this.y = y; } // create a new point public DPoint(DPoint p, double r, double a) // create a new point = p + polar(r,a) { x = p.x + r*Math.cos(a); y = p.y + r*Math.sin(a); } public DPoint(DPoint p, DPoint q, DPoint a) // create a new point = perpendicular (pq, a) { double m = DPoint.slope(p,q); if (Double.isNaN(m)) { x = p.x; y = p.y; } // special case: p == q, just use p else if (Double.isInfinite(m)) { x = p.x; y = a.y; } // special case: pq is vertical else // find the point of intersection of the perpendicular from a to pq { y = (p.y + m*m*a.y + m*a.x - m*p.x) / (m*m + 1.0); x = m * (a.y - y) + a.x; } } //------------------------------------------------------------------------------------------- public boolean equals(DPoint p) // return true if the points are close enough { return Math.abs(this.x - p.x) < 0.0001 && Math.abs(this.y - p.y) < 0.0001; } public double distanceFrom(DPoint p) // distance between the points { return Math.sqrt( (p.x-x) * (p.x-x) + (p.y-y) * (p.y-y) ); } public static double slope(DPoint p, DPoint q) // slope of the segment between the points { return (p.y - q.y) / (p.x - q.x); } public static double angle(DPoint p, DPoint q) // angle of the segment FROM p TO q { if (p.equals(q)) return 0.0; return Math.atan(DPoint.slope(p,q)) + (q.x < p.x ? Math.PI : 0.0); } public void translate(double dx, double dy) // move the point { x += dx; y += dy; } public void rotate(DPoint origin, double angle) { // rotates a point counter-clockwise around the specified origin // find (xr,yr) relative to origin double xr = x - origin.x, yr = y - origin.y; // rotate point (xr,yr) around (0,0) x = xr * Math.cos(angle) - yr * Math.sin(angle); y = xr * Math.sin(angle) + yr * Math.cos(angle); // translate the relative point back to the original coordinate system x += origin.x; y += origin.y; } // rotate() public boolean isBetween(DPoint p, DPoint q) { // tests if DPoint is inside the rectangle defined by p and q double d = 0.1; // boundary value to allow for errors in rounding return ( ((p.x-d < x && x < q.x+d) || (p.x+d > x && x > q.x-d)) && ((p.y-d < y && y < q.y+d) || (p.y+d > y && y > q.y-d)) ); } public static DPoint intersection(DPoint p, DPoint q, DPoint r, DPoint s) { // returns the DPoint at the intersection of lines pq and rs double pqSlope = slope(p,q), rsSlope = slope(r,s); if (pqSlope == rsSlope) return null; // no intersection // check for vertical lines boolean rsIsVertical = Double.isInfinite(rsSlope); if (Double.isInfinite(pqSlope)) // pq is vertical return rsIsVertical ? null : new DPoint(p.x, r.y + rsSlope * (p.x - r.x)); else if (rsIsVertical) return new DPoint(r.x, p.y + pqSlope * (r.x - p.x) ); // non-vertical lines with different slopes: intersect them double x = (pqSlope * p.x - rsSlope * r.x - p.y + r.y) / (pqSlope - rsSlope); return new DPoint(x, p.y + pqSlope * (x - p.x)); } }