GEOMETRY // LineTest.cpp /* Created by Daniel Rodriguez Contents: Point class (2D) Line class operations: Construct a line from two points. Construct a line from a point and slope get the slope of this line get the slope of the perpendicular to this line get the y-intercept of this line get the intersecting point of two lines get the distance from a point to this line */ #include #include #include #include <iostream> // cout, cin <math.h> // sqrt(...) and pow(...) <cstdlib> // EXIT_SUCCESS <vector> // vector<T> using namespace std; class Point { public: Point( double xv = 0.0, double yv = 0.0) { x = xv; y = yv; } double distanceToPoint(const Point & p) const { return sqrt( pow(x-p.x, 2) + pow(y-p.y,2) ); } double x; double y; }; class Line { public: Line { a b c } (double av, double bv, double cv) = av; = bv; = cv; Line( const Point & p1, const Point & p2 ) { a = p2.y - p1.y; b = p1.x - p2.x; c = - b * p1.y - a * p1.x; 1 } Line( const Point & p, double slope ) { a = -slope; b = 1; c = -p.y + slope * p.x; } double slope() const { return - a / b; } double yIntercept() const { return - c / b; } double perpendicularSlope() const { return (-1.0 / slope() ); } Point intersection( const Line & L2 ) const { double x = - (c * L2.b - L2.c * b) / (a * L2.b - L2.a * b); // breaks if both lines are parallel double y; if (b != 0) y = (slope() * x) + yIntercept(); else if (L2.b != 0) y = L2.slope() * x + L2.yIntercept(); else y = 0; // undefined, both lines are the same return Point(x,y); } public double distanceToPoint(Point p) { double x2 = p.x; double y2 = p.y; double area = 0.5 * Math.abs( x0*y1-x1*y0 + x1*y2-x2*y1 + x2*y0-x0*y2 ); double base = Math.sqrt( (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1) ); return ( 2* area ) / base; } double y(double x) const { return slope() * x + yIntercept(); } double a,b,c; }; class Circle { public: Circle(const Point & center, double radius) cx = center.x; 2 { cy = center.y; r = radius; } vector<Point> intersection(const Line & l) const { double a = 1 + pow(l.slope(), 2); double b = - 2 * cx + 2 * l.slope() * (l.yIntercept() - cy); double c = pow(cx, 2) + pow(l.yIntercept() - cy, 2) - r*r; double x, y; double det = b*b - 4 * a * c; vector<Point> points; if (det == 0) { // one intersection x = - b / (2*a); y = l.y(x); points.push_back(Point(x,y)); } else if (det > 0) { // two intersections x = (-b + sqrt(det)) / (2*a); y = l.y(x); points.push_back( Point(x,y) ); x = (-b - sqrt(det)) / (2*a); y = l.y(x); points.push_back( Point(x,y) ); } else { // no intersection } return points; } double cx, cy, r; }; int main() { Line L(Point(0.0, 1.0), Point(1.0,2.0)); cout << "Slope = " << L.slope() << endl; cout << "Y-intercept = " << L.yIntercept() << endl; cout << "distance to point = " << L.distanceToPoint( Point(1,1) ) << endl; Circle circle(Point(0.0, 0.0), 1.0); vector<Point> points = circle.intersection(L); cout << "circle intersections = "; vector<Point>::const_iterator I = points.begin(); while (I != points.end()){ cout << "(" << I->x << "," << I->y << ") "; I++; } if (points.begin() == points.end()) cout << "no intersections"; cout << endl; return 0; } 3 /* * (C) 2004 - Geotechnical Software Services * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ package no.geosoft.cc.geometry; /** * Collection of geometry utility methods. All methods are static. * * @author <a href="mailto:jacob.dreyer@geosoft.no">Jacob Dreyer</a> */ public final class Geometry { /** * Return true if c is between a and b. */ private static boolean isBetween (int a, int b, int c) { return b > a ? c >= a && c <= b : c >= b && c <= a; } /** * Return true if c is between a and b. */ private static boolean isBetween (double a, double b, double c) { return b > a ? c >= a && c <= b : c >= b && c <= a; } /** * Check if two double precision numbers are "equal", i.e. close enough * to a given limit. * * @param a First number to check * @param b Second number to check * @param limit The definition of "equal". * @return True if the twho numbers are "equal", false otherwise */ private static boolean equals (double a, double b, double limit) { return Math.abs (a - b) < limit; } /** 4 * Check if two double precision numbers are "equal", i.e. close enough * to a prespecified limit. * * @param a First number to check * @param b Second number to check * @return True if the twho numbers are "equal", false otherwise */ private static boolean equals (double a, double b) { return equals (a, b, 1.0e-5); } /** * Return smallest of four numbers. * * @param a First number to find smallest among. * @param b Second number to find smallest among. * @param c Third number to find smallest among. * @param d Fourth number to find smallest among. * @return Smallest of a, b, c and d. */ private static double min (double a, double b, double c, double d) { return Math.min (Math.min (a, b), Math.min (c, d)); } /** * Return largest of four numbers. * * @param a First number to find largest among. * @param b Second number to find largest among. * @param c Third number to find largest among. * @param d Fourth number to find largest among. * @return Largest of a, b, c and d. */ private static double max (double a, double b, double c, double d) { return Math.max (Math.max (a, b), Math.max (c, d)); } /** * Check if a specified point is inside a specified rectangle. * * @param x0, y0, x1, y1 Upper left and lower right corner of rectangle * (inclusive) * @param x,y Point to check. * @return True if the point is inside the rectangle, * false otherwise. */ public static boolean isPointInsideRectangle (int x0, int y0, int x1, int y1, int x, int y) { return x >= x0 && x < x1 && y >= y0 && y < y1; } /** * Check if a given point is inside a given (complex) polygon. * @param x, y Polygon. * @param pointX, pointY Point to check. * @return True if the given point is inside the polygon, false otherwise. 5 */ public static boolean isPointInsidePolygon (double[] x, double[] y, double pointX, double pointY) { boolean isInside = false; int nPoints = x.length; int j = 0; for (int i = 0; i < nPoints; i++) { j++; if (j == nPoints) j = 0; if (y[i] < pointY && y[j] >= pointY || y[j] < pointY && y[i] >= pointY) { if (x[i] + (pointY - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < pointX) { isInside = !isInside; } } } return isInside; } /** * Check if a given point is inside a given polygon. Integer domain. * @param x, y Polygon. * @param pointX, pointY Point to check. * @return True if the given point is inside the polygon, false otherwise. */ public static boolean isPointInsidePolygon (int[] x, int[] y, int pointX, int pointY) { boolean isInside = false; int nPoints = x.length; int j = 0; for (int i = 0; i < nPoints; i++) { j++; if (j == nPoints) j = 0; if (y[i] < pointY && y[j] >= pointY || y[j] < pointY && y[i] >= pointY) { if (x[i] + (double) (pointY - y[i]) / (double) (y[j] - y[i]) * (x[j] - x[i]) < pointX) { isInside = !isInside; } } } return isInside; } /** * Find the point on the line p0,p1 [x,y,z] a given fraction from p0. * Fraction of 0.0 whould give back p0, 1.0 give back p1, 0.5 returns * midpoint of line p0,p1 and so on. Fraction can be >1 and it can be * negative to return any point on the line specified by p0,p1. * @param p0 First coordinale of line [x,y,z]. * @param p0 Second coordinale of line [x,y,z]. * @param fractionFromP0 Point we are looking for coordinates of * @param p Coordinate of point we are looking for */ public static double[] computePointOnLine (double[] p0, double[] p1, double fractionFromP0) 6 { double[] p = new double[3]; p[0] = p0[0] + fractionFromP0 * (p1[0] - p0[0]); p[1] = p0[1] + fractionFromP0 * (p1[1] - p0[1]); p[2] = p0[2] + fractionFromP0 * (p1[2] - p0[2]); return p; } /** * Find the point on the line defined by x0,y0,x1,y1 a given fraction * from x0,y0. 2D version of method above.. * * @param x0, y0 First point defining the line * @param x1, y1 Second point defining the line * @param fractionFrom0 Distance from (x0,y0) * @return x, y Coordinate of point we are looking for */ public static double[] computePointOnLine (double x0, double y0, double x1, double y1, double fractionFrom0) { double[] p0 = {x0, y0, 0.0}; double[] p1 = {x1, y1, 0.0}; double[] p = Geometry.computePointOnLine (p0, p1, fractionFrom0); double[] r = {p[0], p[1]}; return r; } /** * Extend a given line segment to a specified length. * * @param p0, p1 Line segment to extend [x,y,z]. * @param toLength Length of new line segment. * @param anchor Specifies the fixed point during extension. * If anchor is 0.0, p0 is fixed and p1 is adjusted. * If anchor is 1.0, p1 is fixed and p0 is adjusted. * If anchor is 0.5, the line is adjusted equally in each * direction and so on. */ public static void extendLine (double[] p0, double[] p1, double toLength, double anchor) { double[] p = Geometry.computePointOnLine (p0, p1, anchor); double length0 = toLength * anchor; double length1 = toLength * (1.0 - anchor); Geometry.extendLine (p, p0, length0); Geometry.extendLine (p, p1, length1); } 7 /** * Extend a given line segment to a given length and holding the first * point of the line as fixed. * * @param p0, p1 Line segment to extend. p0 is fixed during extension * @param length Length of new line segment. */ public static void extendLine (double[] p0, double[] p1, double toLength) { double oldLength = Geometry.length (p0, p1); double lengthFraction = oldLength != 0.0 ? toLength / oldLength : 0.0; p1[0] = p0[0] + (p1[0] - p0[0]) * lengthFraction; p1[1] = p0[1] + (p1[1] - p0[1]) * lengthFraction; p1[2] = p0[2] + (p1[2] - p0[2]) * lengthFraction; } /** * Return the length of a vector. * * @param v Vector to compute length of [x,y,z]. * @return Length of vector. */ public static double length (double[] v) { return Math.sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); } /** * Compute distance between two points. * * @param p0, p1 Points to compute distance between [x,y,z]. * @return Distance between points. */ public static double length (double[] p0, double[] p1) { double[] v = Geometry.createVector (p0, p1); return length (v); } /** * Compute the length of the line from (x0,y0) to (x1,y1) * * @param x0, y0 First line end point. * @param x1, y1 Second line end point. * @return Length of line from (x0,y0) to (x1,y1). */ public static double length (int x0, int y0, int x1, int y1) { return Geometry.length ((double) x0, (double) y0, (double) x1, (double) y1); } 8 /** * Compute the length of the line from (x0,y0) to (x1,y1) * * @param x0, y0 First line end point. * @param x1, y1 Second line end point. * @return Length of line from (x0,y0) to (x1,y1). */ public static double length (double x0, double y0, double x1, double y1) { double dx = x1 - x0; double dy = y1 - y0; return Math.sqrt (dx*dx + dy*dy); } /** * Compute the length of a polyline. * * @param x, y Arrays of x,y coordinates * @param nPoints Number of elements in the above. * @param isClosed True if this is a closed polygon, false otherwise * @return Length of polyline defined by x, y and nPoints. */ public static double length (int[] x, int[] y, boolean isClosed) { double length = 0.0; int nPoints = x.length; for (int i = 0; i < nPoints-1; i++) length += Geometry.length (x[i], y[i], x[i+1], y[i+1]); // Add last leg if this is a polygon if (isClosed && nPoints > 1) length += Geometry.length (x[nPoints-1], y[nPoints-1], x[0], y[0]); return length; } /** * Return distance bwetween the line defined by (x0,y0) and (x1,y1) * and the point (x,y). * Ref: http://astronomy.swin.edu.au/pbourke/geometry/pointline/ * The 3D case should be similar. * * @param x0, y0 First point of line. * @param x1, y1 Second point of line. * @param x, y, Point to consider. * @return Distance from x,y down to the (extended) line defined * by x0, y0, x1, y1. */ public static double distance (int x0, int y0, int x1, int y1, int x, int y) { // If x0,y0,x1,y1 is same point, we return distance to that point double length = Geometry.length (x0, y0, x1, y1); if (length == 0.0) return Geometry.length (x0, y0, x, y); 9 // If u is [0,1] then (xp,yp) is on the line segment (x0,y0),(x1,y1). double u = ((x - x0) * (x1 - x0) + (y - y0) * (y1 - y0)) / (length * length); // This is the // TODO: Might double xp = x0 double yp = y0 intersection point of the normal. consider returning this as well. + u * (x1 - x0); + u * (y1 - y0); length = Geometry.length (xp, yp, x, y); return length; } /** * Find the angle between twree points. P0 is center point * * @param p0, p1, p2 Three points finding angle between [x,y,z]. * @return Angle (in radians) between given points. */ public static double computeAngle (double[] p0, double[] p1, double[] p2) { double[] v0 = Geometry.createVector (p0, p1); double[] v1 = Geometry.createVector (p0, p2); double dotProduct = Geometry.computeDotProduct (v0, v1); double length1 = Geometry.length (v0); double length2 = Geometry.length (v1); double denominator = length1 * length2; double product = denominator != 0.0 ? dotProduct / denominator : 0.0; double angle = Math.acos (product); return angle; } /** * Compute the dot product (a scalar) between two vectors. * * @param v0, v1 Vectors to compute dot product between [x,y,z]. * @return Dot product of given vectors. */ public static double computeDotProduct (double[] v0, double[] v1) { return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; } /** * Compute the cross product (a vector) of two vectors. * * @param v0, v1 Vectors to compute cross product between [x,y,z]. * @param crossProduct Cross product of specified vectors [x,y,z]. 10 */ public static double[] computeCrossProduct (double[] v0, double[] v1) { double crossProduct[] = new double[3]; crossProduct[0] = v0[1] * v1[2] - v0[2] * v1[1]; crossProduct[1] = v0[2] * v1[0] - v0[0] * v1[2]; crossProduct[2] = v0[0] * v1[1] - v0[1] * v1[0]; return crossProduct; } /** * Construct the vector specified by two points. * * @param p0, p1 Points the construct vector between [x,y,z]. * @return v Vector from p0 to p1 [x,y,z]. */ public static double[] createVector (double[] p0, double[] p1) { double v[] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]}; return v; } /** * Left turn test. * * @param x0, y0, x1, y1 The line. * @param px0, py0 point. * @return <0 if point is on the right of the line. * =0 if point is on the line. * >0 if point is on the left of the line. */ private static int leftTurnTest (double x0, double y0, double x1, double y1, double px0, double py0) { double double double double dx dy dx1 dy1 = = = = x1 y1 px0 py0 - x0; y0; x0; y0; // Cross product of the vector from the endpoint of the line to the point double c1 = dx * dy1 - dy * dx1; return (int)c1; } /** * Check if two points are on the same side of a given line. * Algorithm from Sedgewick page 350. * * @param x0, y0, x1, y1 The line. * @param px0, py0 First point. * @param px1, py1 Second point. * @return <0 if points on opposite sides. * =0 if one of the points is exactly on the line * >0 if points on same side. 11 */ private static int sameSide (double x0, double y0, double x1, double y1, double px0, double py0, double px1, double py1) { int sameSide = 0; double double double double double double dx dy dx1 dy1 dx2 dy2 = = = = = = x1 y1 px0 py0 px1 py1 - x0; y0; x0; y0; x1; y1; // Cross product of the vector from the endpoint of the line to the point double c1 = dx * dy1 - dy * dx1; double c2 = dx * dy2 - dy * dx2; if (c1 != 0 && c2 != 0) sameSide = c1 < 0 != c2 < 0 ? -1 : 1; else if (dx == 0 && dx1 == 0 && dx2 == 0) sameSide = !isBetween (y0, y1, py0) && !isBetween (y0, y1, py1) ? 1 : 0; else if (dy == 0 && dy1 == 0 && dy2 == 0) sameSide = !isBetween (x0, x1, px0) && !isBetween (x0, x1, px1) ? 1 : 0; return sameSide; } /** * Check if two points are on the same side of a given line. Integer domain. * * @param x0, y0, x1, y1 The line. * @param px0, py0 First point. * @param px1, py1 Second point. * @return <0 if points on opposite sides. * =0 if one of the points is exactly on the line * >0 if points on same side. */ private static int sameSide (int x0, int y0, int x1, int y1, int px0, int py0, int px1, int py1) { return sameSide ((double) x0, (double) y0, (double) x1, (double) y1, (double) px0, (double) py0, (double) px1, (double) py1); } /** * Check if two line segments intersects. Integer domain. * * @param x0, y0, x1, y1 End points of first line to check. * @param x2, yy, x3, y3 End points of second line to check. * @return True if the two lines intersects. */ public static boolean isLineIntersectingLine (int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) { int s1 = Geometry.sameSide (x0, y0, x1, y1, x2, y2, x3, y3); int s2 = Geometry.sameSide (x2, y2, x3, y3, x0, y0, x1, y1); 12 return s1 <= 0 && s2 <= 0; } /** * Check if a specified line intersects a specified rectangle. * Integer domain. * * @param lx0, ly0 1st end point of line * @param ly1, ly1 2nd end point of line * @param x0, y0, x1, y1 Upper left and lower right corner of rectangle * (inclusive). * @return True if the line intersects the rectangle, * false otherwise. */ public static boolean isLineIntersectingRectangle (int lx0, int ly0, int lx1, int ly1, int x0, int y0, int x1, int y1) { // Is one of the line endpoints inside the rectangle if (Geometry.isPointInsideRectangle (x0, y0, x1, y1, lx0, ly0) || Geometry.isPointInsideRectangle (x0, y0, x1, y1, lx1, ly1)) return true; // If it intersects it goes through. Need to check three sides only. // Check against top rectangle line if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, x0, y0, x1, y0)) return true; // Check against left rectangle line if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, x0, y0, x0, y1)) return true; // Check against bottom rectangle line if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, x0, y1, x1, y1)) return true; return false; } /** * Check if a specified polyline intersects a specified rectangle. * Integer domain. * * @param x, y Polyline to check. * @param x0, y0, x1, y1 Upper left and lower left corner of rectangle * (inclusive). * @return True if the polyline intersects the rectangle, * false otherwise. */ public static boolean isPolylineIntersectingRectangle (int[] x, int[] y, int x0, int y0, int x1, int y1) 13 { if (x.length == 0) return false; if (Geometry.isPointInsideRectangle (x[0], y[0], x0, y0, x1, y1)) return true; else if (x.length == 1) return false; for (int i = 1; i < x.length; i++) { if (x[i-1] != x[i] || y[i-1] != y[i]) if (Geometry.isLineIntersectingRectangle (x[i-1], y[i-1], x[i], y[i], x0, y0, x1, y1)) return true; } return false; } /** * Check if a specified polygon intersects a specified rectangle. * Integer domain. * * @param x X coordinates of polyline. * @param y Y coordinates of polyline. * @param x0 X of upper left corner of rectangle. * @param y0 Y of upper left corner of rectangle. * @param x1 X of lower right corner of rectangle. * @param y1 Y of lower right corner of rectangle. * @return True if the polyline intersects the rectangle, false otherwise. */ public static boolean isPolygonIntersectingRectangle (int[] x, int[] y, int x0, int y0, int x1, int y1) { int n = x.length; if (n == 0) return false; if (n == 1) return Geometry.isPointInsideRectangle (x0, y0, x1, y1, x[0], y[0]); // // If the polyline constituting the polygon intersects the rectangle // the polygon does too. // if (Geometry.isPolylineIntersectingRectangle (x, y, x0, y0, x1, y1)) return true; // Check last leg as well if (Geometry.isLineIntersectingRectangle (x[n-2], y[n-2], x[n-1], y[n-1], x0, y0, x1, y1)) return true; // // The rectangle and polygon are now completely including each other // or separate. 14 // if (Geometry.isPointInsidePolygon (x, y, x0, y0) || Geometry.isPointInsideRectangle (x0, y0, x1, y1, x[0], y[0])) return true; // Separate return false; } /** * Compute the area of the specfied polygon. * * @param x X coordinates of polygon. * @param y Y coordinates of polygon. * @return Area of specified polygon. */ public static double computePolygonArea (double[] x, double[] y) { int n = x.length; double area = 0.0; for (int i = 0; i < n - 1; i++) area += (x[i] * y[i+1]) - (x[i+1] * y[i]); area += (x[n-1] * y[0]) - (x[0] * y[n-1]); area *= 0.5; return area; } /** * Compute the area of the specfied polygon. * * @param xy Geometry of polygon [x,y,...] * @return Area of specified polygon. */ public static double computePolygonArea (double[] xy) { int n = xy.length; double area = 0.0; for (int i = 0; i < n - 2; i += 2) area += (xy[i] * xy[i+3]) - (xy[i+2] * xy[i+1]); area += (xy[xy.length-2] * xy[1]) - (xy[0] * xy[xy.length-1]); area *= 0.5; return area; } /** * Compute centorid (center of gravity) of specified polygon. * * @param x X coordinates of polygon. * @param y Y coordinates of polygon. 15 * @return Centroid [x,y] of specified polygon. */ public static double[] computePolygonCentroid (double[] x, double[] y) { double cx = 0.0; double cy = 0.0; int n = x.length; for (int i = 0; i < n - 1; i++) { double a = x[i] * y[i+1] - x[i+1] * y[i]; cx += (x[i] + x[i+1]) * a; cy += (y[i] + y[i+1]) * a; } double a = x[n-1] * y[0] - x[0] * y[n-1]; cx += (x[n-1] + x[0]) * a; cy += (y[n-1] + y[0]) * a; double area = Geometry.computePolygonArea (x, y); cx /= 6 * area; cy /= 6 * area; return new double[] {cx, cy}; } /** * Find the 3D extent of a polyline. * * @param x X coordinates of polyline. * @param y Y coordinates of polyline. * @param z Z coordinates of polyline. * May be null if this is a 2D case. * @param xExtent Will upon return contain [xMin,xMax]. * @param yExtent Will upon return contain [xMin,xMax]. * @param zExtent Will upon return contain [xMin,xMax]. Unused (may be * set to null) if z is null. */ public static void findPolygonExtent (double[] x, double[] y, double[] z, double[] xExtent, double[] yExtent, double[] zExtent) { double xMin = +Double.MAX_VALUE; double xMax = -Double.MAX_VALUE; double yMin = +Double.MAX_VALUE; double yMax = -Double.MAX_VALUE; double zMin = +Double.MAX_VALUE; double zMax = -Double.MAX_VALUE; for (int i = 0; i < x.length; i++) { if (x[i] < xMin) xMin = x[i]; if (x[i] > xMax) xMax = x[i]; if (y[i] < yMin) yMin = y[i]; if (y[i] > yMax) yMax = y[i]; if (z != null) { if (z[i] < zMin) zMin = z[i]; 16 if (z[i] > zMax) zMax = z[i]; } } xExtent[0] = xMin; xExtent[1] = xMax; yExtent[0] = yMin; yExtent[1] = yMax; if (z != null) { zExtent[0] = zMin; zExtent[1] = zMax; } } /** * Find the extent * * @param x * @param y * @param xExtent * @param yExtent */ public static void of a polygon. X coordinates of Y coordinates of Will upon return Will upon return polygon. polygon. contain [xMin, xMax] contain [yMin, yMax] findPolygonExtent (int[] x, int[] y, int[] xExtent, // xMin, xMax int[] yExtent) // yMin, yMax { int xMin = + Integer.MAX_VALUE; int xMax = - Integer.MAX_VALUE; int yMin = + Integer.MAX_VALUE; int yMax = - Integer.MAX_VALUE; for (int i = 0; i < x.length; i++) { if (x[i] < xMin) xMin = x[i]; if (x[i] > xMax) xMax = x[i]; if (y[i] < yMin) yMin = y[i]; if (y[i] > yMax) yMax = y[i]; } xExtent[0] = xMin; xExtent[1] = xMax; yExtent[0] = yMin; yExtent[1] = yMax; } /** * Compute the intersection between two line segments, or two lines * of infinite length. * * @param x0 X coordinate first end point first line segment. * @param y0 Y coordinate first end point first line segment. * @param x1 X coordinate second end point first line segment. * @param y1 Y coordinate second end point first line segment. 17 * @param x2 X coordinate first end point second line segment. * @param y2 Y coordinate first end point second line segment. * @param x3 X coordinate second end point second line segment. * @param y3 Y coordinate second end point second line segment. * @param intersection[2] Preallocated by caller to double[2] * @return -1 if lines are parallel (x,y unset), * -2 if lines are parallel and overlapping (x, y center) * 0 if intesrection outside segments (x,y set) * +1 if segments intersect (x,y set) */ public static int findLineSegmentIntersection (double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double[] intersection) { // TODO: Make limit depend on input domain final double LIMIT = 1e-5; final double INFINITY = 1e10; double x, y; // // Convert the lines to the form y = ax + b // // Slope of the two lines double a0 = Geometry.equals (x0, INFINITY : (y0 - y1) double a1 = Geometry.equals (x2, INFINITY : (y2 - y3) x1, LIMIT) ? / (x0 - x1); x3, LIMIT) ? / (x2 - x3); double b0 = y0 - a0 * x0; double b1 = y2 - a1 * x2; // Check if lines are parallel if (Geometry.equals (a0, a1)) { if (!Geometry.equals (b0, b1)) return -1; // Parallell non-overlapping else { if (Geometry.equals (x0, x1)) { if (Math.min (y0, y1) < Math.max (y2, y3) || Math.max (y0, y1) > Math.min (y2, y3)) { double twoMiddle = y0 + y1 + y2 + y3 Geometry.min (y0, y1, y2, Geometry.max (y0, y1, y2, y = (twoMiddle) / 2.0; x = (y - b0) / a0; } else return -1; // Parallell non-overlapping } else { if (Math.min (x0, x1) < Math.max (x2, x3) || Math.max (x0, x1) > Math.min (x2, x3)) { double twoMiddle = x0 + x1 + x2 + x3 Geometry.min (x0, x1, x2, Geometry.max (x0, x1, x2, x = (twoMiddle) / 2.0; y = a0 * x + b0; } 18 y3) y3); x3) x3); else return -1; } intersection[0] = x; intersection[1] = y; return -2; } } // Find correct intersection point if (Geometry.equals (a0, INFINITY)) { x = x0; y = a1 * x + b1; } else if (Geometry.equals (a1, INFINITY)) { x = x2; y = a0 * x + b0; } else { x = - (b0 - b1) / (a0 - a1); y = a0 * x + b0; } intersection[0] = x; intersection[1] = y; // Then check if intersection is within line double distanceFrom1; if (Geometry.equals (x0, x1)) { if (y0 < y1) distanceFrom1 = y < y0 ? Geometry.length y > y1 ? Geometry.length else distanceFrom1 = y < y1 ? Geometry.length y > y0 ? Geometry.length } else { if (x0 < x1) distanceFrom1 = x < x0 ? Geometry.length x > x1 ? Geometry.length else distanceFrom1 = x < x1 ? Geometry.length x > x0 ? Geometry.length } double distanceFrom2; if (Geometry.equals (x2, x3)) { if (y2 < y3) distanceFrom2 = y < y2 ? Geometry.length y > y3 ? Geometry.length else distanceFrom2 = y < y3 ? Geometry.length y > y2 ? Geometry.length } else { if (x2 < x3) distanceFrom2 = x < x2 ? Geometry.length x > x3 ? Geometry.length else distanceFrom2 = x < x3 ? Geometry.length x > x2 ? Geometry.length 19 segments (x, y, x0, y0) : (x, y, x1, y1) : 0.0; (x, y, x1, y1) : (x, y, x0, y0) : 0.0; (x, y, x0, y0) : (x, y, x1, y1) : 0.0; (x, y, x1, y1) : (x, y, x0, y0) : 0.0; (x, y, x2, y2) : (x, y, x3, y3) : 0.0; (x, y, x3, y3) : (x, y, x2, y2) : 0.0; (x, y, x2, y2) : (x, y, x3, y3) : 0.0; (x, y, x3, y3) : (x, y, x2, y2) : 0.0; } return Geometry.equals (distanceFrom1, 0.0) && Geometry.equals (distanceFrom2, 0.0) ? 1 : 0; } /** * Find the intersections between a polygon and a straight line. * * NOTE: This method is only guaranteed to work if the polygon * is first preprocessed so that "unneccesary" vertices are removed * (i.e vertices on the straight line between its neighbours). * * @param x X coordinates of polygon. * @param y Y coordinates of polygon. * @param x0 X first end point of line. * @param x0 Y first end point of line. * @param x0 X second end point of line. * @param x0 Y second end point of line. * @return Intersections [x,y,x,y...]. */ public static double[] findLinePolygonIntersections (double[] x, double[] y, double x0, double y0, double x1, double y1) { double x2, y2, x3, y3; double xi, yi; int nPoints = x.length; int nIntersections = 0; double[] intersections = new double[24]; double[] intersection = new double[2]; // Result vector x,y,x,y,... // Any given intersection x,y for (int i = 0; i < nPoints; i++) { int next = i == nPoints - 1 ? 0 : i + 1; // x2 y2 x3 y3 The line segment of the polyline to check = x[i]; = y[i]; = x[next]; = y[next]; boolean isIntersecting = false; // Ignore segments of zero length if (Geometry.equals (x2, x3) && Geometry.equals (y2, y3)) continue; int type = Geometry.findLineSegmentIntersection (x0, y0, x1, y1, x2, y2, x3, y3, intersection); if (type == -2) { // Overlapping int p1 = i == 0 ? nPoints - 1 : i - 1; int p2 = next == nPoints - 1 ? 0 : next + 1; int side = Geometry.sameSide (x0, y0, x1, y1, x[p1], y[p1], x[p2], y[p2]); 20 if (side < 0) isIntersecting = true; } else if (type == 1) isIntersecting = true; // Add the intersection point if (isIntersecting) { // Reallocate if necessary if (nIntersections << 1 == intersections.length) { double[] newArray = new double[nIntersections << 2]; System.arraycopy (intersections, 0, newArray, 0, intersections.length); intersections = newArray; } // Then add intersections[nIntersections << 1 + 0] = intersection[0]; intersections[nIntersections << 1 + 1] = intersection[1]; nIntersections++; } } if (nIntersections == 0) return null; // Reallocate result so array match number of intersections double[] finalArray = new double[nIntersections << 2]; System.arraycopy (intersections, 0, finalArray, 0, finalArray.length); return finalArray; } /** * Return the geometry of an ellipse based on its four top points. * Integer domain. The method use the generic createEllipse() * method for the main task, and then transforms this according * to any rotation or skew defined by the given top points. * * @param x X array of four top points of ellipse. * @param y Y array of four top points of ellipse. * @return Geometry of ellipse [x,y,x,y...]. */ public static int[] createEllipse (int[] x, int[] y) { // Center of ellipse int x0 = (x[0] + x[2]) / 2; int y0 = (y[0] + y[2]) / 2; // Angle double[] double[] double[] between axis define skew p0 = {(double) x0, (double) y0, 0.0}; p1 = {(double) x[0], (double) y[0], 0.0}; p2 = {(double) x[1], (double) y[1], 0.0}; double axisAngle = Geometry.computeAngle (p0, p1, p2); // dx / dy double dx = Geometry.length (x0, y0, x[1], y[1]); 21 double dy = Geometry.length (x0, y0, x[0], y[0]) * Math.sin (axisAngle); // Create geometry for unrotated / unsheared ellipse int[] ellipse = createEllipse (x0, y0, (int) Math.round (dx), (int) Math.round (dy)); int nPoints = ellipse.length / 2; // // // if Shear if neccessary. If angle is close to 90 there is no shear. If angle is close to 0 or 180 shear is infinite, and we set it to zero as well. (!Geometry.equals (axisAngle, Math.PI/2.0, 0.1) && !Geometry.equals (axisAngle, Math.PI, 0.1) && !Geometry.equals (axisAngle, 0.0, 0.1)) { double xShear = 1.0 / Math.tan (axisAngle); for (int i = 0; i < nPoints; i++) ellipse[i*2 + 0] += Math.round ((ellipse[i*2 + 1] - y0) * xShear); } // Rotate int ddx = x[1] - x0; int ddy = y0 - y[1]; double angle; if (ddx == 0 && ddy == 0) angle = 0.0; else if (ddx == 0) angle = Math.PI / 2.0; else angle = Math. atan ((double) ddy / (double) ddx); double cosAngle = Math.cos (angle); double sinAngle = Math.sin (angle); for (int i = 0; i < nPoints; i++) { int xr = (int) Math.round (x0 + (ellipse[i*2+0] (ellipse[i*2+1] int yr = (int) Math.round (y0 (ellipse[i*2+1] (ellipse[i*2+0] - x0) * cosAngle - y0) * sinAngle); - y0) * cosAngle - x0) * sinAngle); ellipse[i*2+0] = xr; ellipse[i*2+1] = yr; } return ellipse; } /** * Create the geometry for an unrotated, unskewed ellipse. * Integer domain. * * @param x0 X center of ellipse. * @param y0 Y center of ellipse. * @param dx X ellipse radius. * @param dy Y ellipse radius. * @return Ellipse geometry [x,y,x,y,...]. */ public static int[] createEllipse (int x0, int y0, int dx, int dy) { // Make sure deltas are positive 22 dx = Math.abs (dx); dy = Math.abs (dy); // This is an // surface on int nPoints = nPoints /= 2; if (nPoints < approximate number of points we need to make a smooth a quater of the ellipse dx > dy ? dx : dy; 1) nPoints = 1; // Allocate arrays for holding the complete set of vertices around // the ellipse. Note that this is a complete ellipse: First and last // point coincide. int[] ellipse = new int[nPoints*8 + 2]; // Compute some intermediate results to save time in the inner loop int dxdy = dx * dy; int dx2 = dx * dx; int dy2 = dy * dy; // Handcode the entries in the four "corner" points of the ellipse, // i.e. at point 0, 90, 180, 270 and 360 degrees ellipse[nPoints*0 + 0] = x0 + dx; ellipse[nPoints*0 + 1] = y0; ellipse[nPoints*8 + 0] = x0 + dx; ellipse[nPoints*8 + 1] = y0; ellipse[nPoints*2 + 0] = x0; ellipse[nPoints*2 + 1] = y0 - dy; ellipse[nPoints*4 + 0] = x0 - dx; ellipse[nPoints*4 + 1] = y0; ellipse[nPoints*6 + 0] = x0; ellipse[nPoints*6 + 1] = y0 + dy; // Find the angle step double angleStep = nPoints > 0 ? Math.PI / 2.0 / nPoints : 0.0; // Loop over angles from 0 to 90. The rest of the ellipse can be derrived // from this first quadrant. For each angle set the four corresponding // ellipse points. double a = 0.0; for (int i = 1; i < nPoints; i++) { a += angleStep; double t = Math.tan (a); double x = (double) dxdy / Math.sqrt (t * t * dx2 + dy2); double y = x * t; int xi = (int) (x + 0.5); int yi = (int) (y + 0.5); ellipse[(nPoints*0 ellipse[(nPoints*2 ellipse[(nPoints*2 ellipse[(nPoints*4 + + - i) i) i) i) * * * * 2 2 2 2 + + + + 0] 0] 0] 0] = = = = x0 x0 x0 x0 + + xi; xi; xi; xi; ellipse[(nPoints*0 + i) * 2 + 1] = y0 - yi; ellipse[(nPoints*2 - i) * 2 + 1] = y0 - yi; 23 ellipse[(nPoints*2 + i) * 2 + 1] = y0 + yi; ellipse[(nPoints*4 - i) * 2 + 1] = y0 + yi; } return ellipse; } /** * Create the geometry for an unrotated, unskewed ellipse. * Floating point domain. * * @param x0 X center of ellipse. * @param y0 Y center of ellipse. * @param dx X ellipse radius. * @param dy Y ellipse radius. * @return Ellipse geometry [x,y,x,y,...]. */ public static double[] createEllipse (double x0, double y0, double dx, double dy) { // Make sure deltas are positive dx = Math.abs (dx); dy = Math.abs (dy); // As we don't know the resolution of the appliance of the ellipse // we create one vertex per 2nd degree. The nPoints variable holds // number of points in a quater of the ellipse. int nPoints = 45; // Allocate arrays for holding the complete set of vertices around // the ellipse. Note that this is a complete ellipse: First and last // point coincide. double[] ellipse = new double[nPoints*8 + 2]; // Compute some intermediate results to save time in the inner loop double dxdy = dx * dy; double dx2 = dx * dx; double dy2 = dy * dy; // Handcode the entries in the four "corner" points of the ellipse, // i.e. at point 0, 90, 180, 270 and 360 degrees ellipse[nPoints*0 + 0] = x0 + dx; ellipse[nPoints*0 + 1] = y0; ellipse[nPoints*8 + 0] = x0 + dx; ellipse[nPoints*8 + 1] = y0; ellipse[nPoints*2 + 0] = x0; ellipse[nPoints*2 + 1] = y0 - dy; ellipse[nPoints*4 + 0] = x0 - dx; ellipse[nPoints*4 + 1] = y0; ellipse[nPoints*6 + 0] = x0; ellipse[nPoints*6 + 1] = y0 + dy; // Find the angle step double angleStep = nPoints > 0 ? Math.PI / 2.0 / nPoints : 0.0; 24 // Loop over angles from 0 to 90. The rest of the ellipse can be derrived // from this first quadrant. For each angle set the four corresponding // ellipse points. double a = 0.0; for (int i = 1; i < nPoints; i++) { a += angleStep; double t = Math.tan (a); double x = (double) dxdy / Math.sqrt (t * t * dx2 + dy2); double y = x * t + 0.5; ellipse[(nPoints*0 ellipse[(nPoints*2 ellipse[(nPoints*2 ellipse[(nPoints*4 + + - i) i) i) i) * * * * 2 2 2 2 + + + + 0] 0] 0] 0] = = = = x0 x0 x0 x0 + + x; x; x; x; ellipse[(nPoints*0 ellipse[(nPoints*2 ellipse[(nPoints*2 ellipse[(nPoints*4 + + - i) i) i) i) * * * * 2 2 2 2 + + + + 1] 1] 1] 1] = = = = y0 y0 y0 y0 + + y; y; y; y; } return ellipse; } /** * Create geometry for a circle. Integer domain. * * @param x0 X center of circle. * @param y0 Y center of circle. * @param radius Radius of circle. * @return Geometry of circle [x,y,...] */ public static int[] createCircle (int x0, int y0, int radius) { return createEllipse (x0, y0, radius, radius); } /** * Create geometry for a circle. Floating point domain. * * @param x0 X center of circle. * @param y0 Y center of circle. * @param radius Radius of circle. * @return Geometry of circle [x,y,...] */ public static double[] createCircle (double x0, double y0, double radius) { return createEllipse (x0, y0, radius, radius); } /** * Create the geometry of a sector of an ellipse. 25 * * @param x0 X coordinate of center of ellipse. * @param y0 Y coordinate of center of ellipse. * @param dx X radius of ellipse. * @param dy Y radius of ellipse. * @param angle0 First angle of sector (in radians). * @param angle1 Second angle of sector (in radians). * @return Geometry of secor [x,y,...] */ public static int[] createSector (int x0, int y0, int dx, int dy, double angle0, double angle1) { // Determine a sensible number of points for arc double angleSpan = Math.abs (angle1 - angle0); double arcDistance = Math.max (dx, dy) * angleSpan; int nPoints = (int) Math.round (arcDistance / 15); double angleStep = angleSpan / (nPoints - 1); int[] xy = new int[nPoints*2 + 4]; int index = 0; for (int i = 0; i < nPoints; i++) { double angle = angle0 + angleStep * i; double x = dx * Math.cos (angle); double y = dy * Math.sin (angle); xy[index+0] = x0 + (int) Math.round (x); xy[index+1] = y0 - (int) Math.round (y); index += 2; } // Start and xy[nPoints*2 xy[nPoints*2 xy[nPoints*2 xy[nPoints*2 end geometry at center of ellipse to make it a closed polygon + 0] = x0; + 1] = y0; + 2] = xy[0]; + 3] = xy[1]; return xy; } /** * Create the geometry of a sector of a circle. * * @param x0 X coordinate of center of ellipse. * @param y0 Y coordinate of center of ellipse. * @param dx X radius of ellipse. * @param dy Y radius of ellipse. * @param angle0 First angle of sector (in radians). * @param angle1 Second angle of sector (in radians). * @return Geometry of secor [x,y,...] */ public static int[] createSector (int x0, int y0, int radius, double angle0, double angle1) { return createSector (x0, y0, radius, radius, angle0, angle1); } 26 /** * Create the geometry of an arrow. The arrow is positioned at the * end (last point) of the specified polyline, as follows: * * 0,4--, * \ --, * \ --, * \ --, * \ --, * -------------------------3-----------1 * / --' * / --' * / --' * / --' * 2--' * * @param x X coordinates of polyline of where arrow is positioned * in the end. Must contain at least two points. * @param y Y coordinates of polyline of where arrow is positioned * in the end. * @param length Length along the main axis from point 1 to the * projection of point 0. * @param angle Angle between the main axis and the line 1,0 * (and 1,2) in radians. * @param inset Specification of point 3 [0.0-1.0], 1.0 will put * point 3 at distance length from 1, 0.0 will put it * at point 1. * @return Array of the five coordinates [x,y,...]. */ public static int[] createArrow (int[] x, int[] y, double length, double angle, double inset) { int[] arrow = new int[10]; int x0 = x[x.length - 1]; int y0 = y[y.length - 1]; arrow[2] = x0; arrow[3] = y0; // Find position of interior of the arrow along the polyline int[] pos1 = new int[2]; Geometry.findPolygonPosition (x, y, length, pos1); // Angles double dx = x0 - pos1[0]; double dy = y0 - pos1[1]; // Polyline angle double v = dx == 0.0 ? Math.PI / 2.0 : Math.atan (Math.abs (dy / dx)); v = dx dx dx dx > > <= <= 0.0 0.0 0.0 0.0 && && && && dy dy dy dy <= >= < > 0.0 0.0 0.0 0.0 ? ? ? ? Math.PI + v : Math.PI - v : -v : +v : 0.0; double v0 = v + angle; double v1 = v - angle; double edgeLength = length / Math.cos (angle); 27 arrow[0] = x0 + (int) Math.round (edgeLength * Math.cos (v0)); arrow[1] = y0 - (int) Math.round (edgeLength * Math.sin (v0)); arrow[4] = x0 + (int) Math.round (edgeLength * Math.cos (v1)); arrow[5] = y0 - (int) Math.round (edgeLength * Math.sin (v1)); double c1 = inset * length; arrow[6] = x0 + (int) Math.round (c1 * Math.cos (v)); arrow[7] = y0 - (int) Math.round (c1 * Math.sin (v)); // Close polygon arrow[8] = arrow[0]; arrow[9] = arrow[1]; return arrow; } /** * Create geometry for an arrow along the specified line and with * tip at x1,y1. See general method above. * * @param x0 X first end point of line. * @param y0 Y first end point of line. * @param x1 X second end point of line. * @param y1 Y second end point of line. * @param length Length along the main axis from point 1 to the * projection of point 0. * @param angle Angle between the main axis and the line 1,0 * (and 1.2) * @param inset Specification of point 3 [0.0-1.0], 1.0 will put * point 3 at distance length from 1, 0.0 will put it * at point 1. * @return Array of the four coordinates [x,y,...]. */ public static int[] createArrow (int x0, int y0, int x1, int y1, double length, double angle, double inset) { int[] x = {x0, x1}; int[] y = {y0, y1}; return createArrow (x, y, length, angle, inset); } /** * Create geometry for a rectangle. Returns a closed polygon; first * and last points matches. Integer domain. * * @param x0 X corner of rectangle. * @param y0 Y corner of rectangle. * @param width Width (may be negative to indicate leftwards direction) * @param height Height (may be negative to indicaten upwards direction) */ public static int[] createRectangle (int x0, int y0, int width, int height) { return new int[] {x0, y0, 28 x0 + (width - 1), y0, x0 + (width - 1), y0 + (height - 1), x0, y0 + (height - 1), x0, y0}; } /** * Create geometry for a rectangle. Returns a closed polygon; first * and last points matches. Floating point domain. * * @param x0 X corner of rectangle. * @param y0 Y corner of rectangle. * @param width Width (may be negative to indicate leftwards direction) * @param height Height (may be negative to indicaten upwards direction) */ public static double[] createRectangle (double x0, double y0, double width, double height) { return new double[] {x0, y0, x0 + width, y0, x0 + width, y0 + height, x0, y0 + height, x0, y0}; } /** * Create geometry of a star. Integer domain. * * @param x0 X center of star. * @param y0 Y center of star. * @param innerRadius Inner radis of arms. * @param outerRadius Outer radius of arms. * @param nArms Number of arms. * @return Geometry of star [x,y,x,y,...]. */ public static int[] createStar (int x0, int y0, int innerRadius, int outerRadius, int nArms) { int nPoints = nArms * 2 + 1; int[] xy = new int[nPoints * 2]; double angleStep = 2.0 * Math.PI / nArms / 2.0; for (int i = 0; i < nArms * 2; i++) { double angle = i * angleStep; double radius = (i % 2) == 0 ? innerRadius : outerRadius; double x = x0 + radius * Math.cos (angle); double y = y0 + radius * Math.sin (angle); xy[i*2 + 0] = (int) Math.round (x); xy[i*2 + 1] = (int) Math.round (y); } // Close polygon 29 xy[nPoints*2 - 2] = xy[0]; xy[nPoints*2 - 1] = xy[1]; return xy; } /** * Create geometry of a star. Floating point domain. * * @param x0 X center of star. * @param y0 Y center of star. * @param innerRadius Inner radis of arms. * @param outerRadius Outer radius of arms. * @param nArms Number of arms. * @return Geometry of star [x,y,x,y,...]. */ public static double[] createStar (double x0, double y0, double innerRadius, double outerRadius, int nArms) { int nPoints = nArms * 2 + 1; double[] xy = new double[nPoints * 2]; double angleStep = 2.0 * Math.PI / nArms / 2.0; for (int i = 0; i < nArms * 2; i++) { double angle = i * angleStep; double radius = (i % 2) == 0 ? innerRadius : outerRadius; xy[i*2 + 0] = x0 + radius * Math.cos (angle); xy[i*2 + 1] = y0 + radius * Math.sin (angle); } // Close polygon xy[nPoints*2 - 2] = xy[0]; xy[nPoints*2 - 1] = xy[1]; return xy; } /** * Return the x,y position at distance "length" into the given polyline. * * @param x X coordinates of polyline * @param y Y coordinates of polyline * @param length Requested position * @param position Preallocated to int[2] * @return True if point is within polyline, false otherwise */ public static boolean findPolygonPosition (int[] x, int[] y, double length, int[] position) { if (length < 0) return false; double accumulatedLength = 0.0; 30 for (int i = 1; i < x.length; i++) { double legLength = Geometry.length (x[i-1], y[i-1], x[i], y[i]); if (legLength + accumulatedLength >= length) { double part = length - accumulatedLength; double fraction = part / legLength; position[0] = (int) Math.round (x[i-1] + fraction * (x[i] - x[i-1])); position[1] = (int) Math.round (y[i-1] + fraction * (y[i] - y[i-1])); return true; } accumulatedLength += legLength; } // Length is longer than polyline return false; } } 31 GEOMETRY – SOLVED PROBLEMS Problem: calculate the narrowest width of a rectangular box that can hold a coin of irregular shape (convex polygon). import java.io.*; import java.util.*; class Coins { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextInt()) { int numverts = in.nextInt(); Vertex[] verts = new Vertex[numverts]; for (int i = 0; i < numverts; i++) verts[i] = new Vertex(in.nextInt(), in.nextInt()); System.out.printf("%.2f\n", findmin(verts)); } } static double findmin(Vertex[] verts) { double min = Double.POSITIVE_INFINITY; for (int i = 0; i < verts.length - 1; i++) { Vertex a = verts[i]; Vertex b = verts[i+1]; double width = findp(verts, a, b); if (width < min) min = width; } return min; } static double findp(Vertex[] verts, Vertex a, Vertex b) { double max_dist = 0; Vertex maxV = null; for (int i = 0; i < verts.length; i++) { if (verts[i] != a && verts[i] != b) { double some_dist = width(a, b, verts[i], dist(a,b)); if (some_dist > max_dist) { max_dist = some_dist; maxV = verts[i]; } } } return max_dist; } static double dist(Vertex a, Vertex b) { return Math.sqrt(Math.pow(a.x -b.x, 2) + Math.pow(a.y-b.y, 2)); } static double width(Vertex p, Vertex q, Vertex r, double basedist) { double a,b,c,d; a = p.x-r.x; 32 b = p.y-r.y; c = q.x-r.x; d = q.y-r.y; return (Math.abs((a*d)-(b*c)) / basedist); } } class Vertex { public int x, y; public Vertex(int a, int b) { x = a; y = b; } } 33 // Coins.cpp - Solution to Coin Width problem by Daniel Rodriguez Problem: Find the smallest distance between two parallel lines (sides of a box) that can contain a convex polygon (an irregular coin). #include <iostream> #include <vector> #include <cmath> using namespace std; // distance formula double D(pair<double,double> r1, pair<double,double> r2){ return sqrt( pow(r1.first - r2.first, 2) + pow(r1.second - r2.second, 2) ); } // calculate distance of a point (o) to a line(r1,r2) double D(pair<double,double> r1, pair<double,double> r2, pair<double,double> o){ double x1 = r1.first - o.first; double y1 = r1.second - o.second; double x2 = r2.first - o.first; double y2 = r2.second - o.second; double a = (x1*y2 - x2*y1) / 2; return 2 * a / D(r1,r2); } int main(){ int n; cin >> n; // number of vertices vector<pair<double,double>> p; // polygon for (int i=0; i<n; i++){ double x,y; cin >> x >> y; p.push_back(pair<double,double>(x,y)); } double sol = 0; // min box width // for all sides of the polygon for (int i=0; i<n-1; i++){ double max = 0; // for all points on the polygon not on side(i to i+1) for (int j=0; j<n; j++) { if (i != j && i+1 != j) { // calculate the distance from the side to the point double d= D(p[i],p[i+1],p[j]); // if the greatest distance so far, save it if (d > max) max = d; } } // if this distance is lower than sol (min box width) // update sol if (max < sol || i == 0) 34 sol = max; } printf("%.2f\n",sol); return 0; } 35 // The Dog and the Gopher UVA 10310 // Solution by Jesus Ramos #include <iostream> #include <cmath> using namespace std; double distance(double, double, double, double); int main() { unsigned int n; double gx, gy, dx, dy; while (scanf("%d %lf %lf %lf %lf", &n, &gx, &gy, &dx, &dy)!=EOF) { double hx, hy; bool escaped = false; for (unsigned int i = 0; i<n; i++) { scanf("%lf %lf", &hx, &hy); if (!escaped) { double dist_g_h = distance(gx,gy,hx,hy); double dist_d_h = 2 * distance(dx,dy,hx,hy); if (dist_g_h>dist_d_h) { escaped = true; printf("The gopher can escape through the hole " "at (%.3f,%.3f).\n", hx, hy); } } } if (!escaped) printf("The gopher cannot escape.\n"); } return 0; } double distance(double x1, double y1, double x2, double y2) { return sqrt(pow(x2-x1,2) + pow(y2-y1,2)); } 36 // IntersectingRectangles.java // Vertical line sweep import java.io.*; import java.util.*; public class IntersectingRectangles { static boolean[][] grid; static int totArea; public static void main(String[] args) throws FileNotFoundException { Scanner in = new Scanner(new FileReader("IntersectingRectangles.in")); //Scanner in = new Scanner(System.in); int cases = in.nextInt(); for (int i = 0; i < cases; i++) { totArea = 0; int maxX = 0; int maxY = 0; ArrayList<Rectangle> rectangles = new ArrayList<Rectangle>(); int numRectangles = in.nextInt(); for (int j = 0; j < numRectangles; j++) { int xmin = in.nextInt(); int ymin = in.nextInt(); int xmax = in.nextInt(); int ymax = in.nextInt(); if (xmax > maxX) maxX = xmax + 1; if (ymax > maxY) maxY = ymax + 1; rectangles.add(new Rectangle(xmin, ymin, xmax, ymax)); } grid = new boolean[maxY][maxX]; for (Rectangle r : rectangles) plotPointInGrid(r); Rectangle mainRect = rectangles.get(0); doDfsOnMainRectangle(mainRect.xmin, mainRect.ymin); System.out.println(totArea + "\n"); } } static void plotPointInGrid(Rectangle r) { for (int y = r.ymin; y < r.ymax; y++) for (int x = r.xmin; x < r.xmax; x++) grid[y][x] = true; } static void doDfsOnMainRectangle(int x, int y) { grid[y][x] = false; totArea++; //up if (inBound(x, y - 1) && grid[y - 1][x]) doDfsOnMainRectangle(x, y - 1); //down if (inBound(x, y + 1) && grid[y + 1][x]) doDfsOnMainRectangle(x, y + 1); //left 37 if (inBound(x - 1, y) && grid[y][x - 1]) doDfsOnMainRectangle(x - 1, y); //right if (inBound(x + 1, y) && grid[y][x + 1]) doDfsOnMainRectangle(x + 1, y); } static boolean inBound(int x, int y) { return x >= 0 && y >= 0 && x < grid[0].length && y < grid.length; } } class Rectangle { public int xmin, ymin, xmax, ymax; public Rectangle(int x1, int y1, int x2, int y2) { xmin = x1; ymin = y1; xmax = x2; ymax = y2; } } 38 // MyAcmTriangles.java import java.io.*; import java.util.*; // Solution by Jesus Ramos class Problem10112 { static Point[] points; static double largestArea; static String largest; public static void main(String[] args) throws Exception { Scanner in = new Scanner(System.in); while (in.hasNextInt()) { largestArea = 0; largest = ""; int p = in.nextInt(); if (p==0) break; points = new Point[p]; for (int j=0; j<p; j++) points[j] = new Point(in.next(), in.nextInt(), in.nextInt()); findSolution(); } } static void findSolution() { int lim = points.length; for (int a=0; a<lim; a++) for (int b=a+1; b<lim; b++) for (int c=b+1; c<lim; c++) { boolean validTriangle = true; Point a1 = points[a]; Point a2 = points[b]; Point a3 = points[c]; double area = triArea(a1.x, a1.y, a2.x, a2.y, a3.x, a3.y); for (Point p : points) { boolean pInside = pointInside(p.x, p.y, a1.x, a1.y, a2.x, a2.y, a3.x, a3.y); boolean otherPoint = p!=a1 && p!=a2 && p!=a3; if (pInside && otherPoint) validTriangle = false; } if (validTriangle && area > largestArea) { largestArea = area; largest = a1.name + a2.name + a3.name; } } System.out.println(largest); } 39 static boolean pointInside(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3) //x and y are the points being tested { double tot, t1, t2, t3; tot = triArea(x1, y1, x2, y2, x3, y3); t1 = triArea(x, y, x2, y2, x3, y3); t2 = triArea(x, y, x1, y1, x2, y2); t3 = triArea(x, y, x1, y1, x3, y3); return t1+t2+t3==tot; } static double triArea(double x1, double y1, double x2, double y2, double x3, double y3) { double a,b,c,d; a = x1-x3; b = y1-y3; c = x2-x3; d = y2-y3; return (0.5 * Math.abs((a*d)-(b*c))); } } class Point { public int x, y; public String name; public Point(String n, int xx, int yy) { name = n; x = xx; y = yy; } } 40 // PoolTable.java - by Jesus Ramos import java.util.*; import java.io.*; public class PoolTable { private static double shortest; //store the best solution so far, reset on each iteration of main algo private static int cx, cy, tx, ty; //cue pos and target pos private static int reflections; //number of reflections allowed private static int len, wid; //dimensions of the table public static void main(String[] args) throws Exception { Scanner in = new Scanner(new FileReader("pooltable.in")); PrintWriter out = new PrintWriter("pooltable.out"); //lot of output, print to file to read later while (in.hasNext()) { shortest = Double.MAX_VALUE; len = in.nextInt(); wid = in.nextInt(); cx = in.nextInt(); cy = in.nextInt(); tx = in.nextInt(); ty = in.nextInt(); reflections = in.nextInt(); if (len == 0 || wid ==0) //done processing input break; //like giri said, check the BT and LR reflections by switching up the amounts //as long as they add up to the total reflections for (int i = 0; i<=reflections; i++) { int j = reflections - i; //check all possible sides shortest = Math.min(shortest, algo(i, j)); shortest = Math.min(shortest, algo(-i, j)); shortest = Math.min(shortest, algo(i, -j)); shortest = Math.min(shortest, algo(-i, -j)); } out.printf("%.3f\n", shortest); //round to 3 places } out.close(); } public static double distance(int x, int y, int x1, int y1) { return Math.sqrt(Math.pow(x1-x, 2) + Math.pow(y1-y, 2)); } public static boolean onLine(int x, int y, int x1, int y1, int x2, int y2) //checks if the points are on the same line, that means cue hits the target { if (y==y1) return y1==y2; else if (x==x1) return x1==x2; //check slopes of the lines created between the points, if they are //the same then so are the slopes 41 return ((y1-y)*(x2-x1) == (y2-y1)*(x1-x)); } public static int xCoord(int a) //gets the x coord of the reflection { int b; if (a%2==0) b = tx; else b = len - tx; return (a * len + b); } public static int yCoord(int a) { int b; if (a%2==0) b = ty; else b = wid - ty; return (a * wid + b); } public static boolean pointBetween(int x, int y, int x1, int y1, int x2, int y2) //checks if x1,y1 is between the two end points { //first check if the points are in the right range, //because you can get a false positive from the //onLine method if the points have some odd config //that makes it so the slopes are the same even if they are //not on the same line boolean xrange = false, yrange = false, sameLine = false; sameLine = onLine(x,y,x1,y1,x2,y2); if (x<x2 && x<=x1 && x1<=x2) xrange = true; else if (x2<=x1 && x1<=x) xrange = true; if (y<y2 && y<=y1 && y1<=y2) yrange = true; else if (y2<=y1 && y1<=y) yrange = true; return xrange && yrange && sameLine; } // @param horizontal and vertical are the amount of times you've gone // in either direction public static double algo(int horizontal, int vertical) { int refX = xCoord(vertical); int refY = yCoord(horizontal); boolean hit = false; //yes it is possible for the target to be hit on one of the //reflections before you reach the amount you actually need //these are all the reflections you made, check them all in case of an early hit int hUpper = Math.max(horizontal, 0); //you can go up/down in which case down is negative int hLower = Math.min(horizontal, 0); int vUpper = Math.max(vertical, 0); //you can also go left/right where left is negative int vLower = Math.min(vertical, 0); //the longest part of the algorithm.... 42 //check all reflections and make sure that none of them intersect the target ball for (int i=hLower; i<=hUpper && !hit; i++) for (int j=vLower; j<=vUpper && !hit; j++) { if (i!=horizontal || j!=vertical) //ignore the last reflection which obviously hits the ball hit = pointBetween(cx, cy, xCoord(j), yCoord(i), refX, refY); //if the point is on the same line then it means a hit } if (!hit) return distance(cx, cy, refX, refY); //not hit, return distance to the point else return Double.MAX_VALUE; } } 43 // Problem478.java - Points in Figures (polygons) UVA 478 // by Jesus Ramos /* Given a list of figures (rectangles, circles, and triangles) and a list of points in the x-y plane, determine for each point which figures (if any) contain the point. */ import java.util.*; public class Problem478 { public static void main(String[] args) { Scanner in = new Scanner(System.in); ArrayList<Shape> list = new ArrayList<Shape>(); while (true) { String t = in.next(); if (t.equals("r")) list.add(new Rectangle(in.nextDouble(), in.nextDouble(), in.nextDouble(), in.nextDouble())); else if (t.equals("c")) list.add(new Circle(in.nextDouble(), in.nextDouble(), in.nextDouble())); else if (t.equals("t")) list.add(new Triangle(in.nextDouble(), in.nextDouble(), in.nextDouble(), in.nextDouble(), in.nextDouble(), in.nextDouble())); else if (t.equals("*")) break; } int pointCount = 1; while (in.hasNextDouble()) { double x = in.nextDouble(), y = in.nextDouble(); if (x == 9999.9 && x == y) break; boolean inside = false; for (int i = 0; i < list.size(); i++) { Shape s = list.get(i); if ((s.type == 'r' && insideRectangle(x, y, (Rectangle)s)) || (s.type == 'c' && insideCircle(x, y, (Circle)s)) || (s.type == 't' && insideTriangle(x, y, (Triangle)s))) { System.out.printf("Point %d is contained in figure %d\n", pointCount, i + 1); inside = true; } } if (!inside) System.out.printf("Point %d is not contained in any figure\n", pointCount); pointCount++; } } static boolean insideRectangle(double x, double y, Rectangle r) { return x > r.x1 && x < r.x2 && y < r.y1 && y > r.y2; } 44 static boolean insideCircle(double x, double y, Circle c) { return distance(x, y, c.x, c.y) < c.rad; } static double distance(double x1, double y1, double x2, double y2) { return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); } static double triArea(Triangle t) //Heron's area formula { double a,b,c,d; a = t.x1-t.x3; b = t.y1-t.y3; c = t.x2-t.x3; d = t.y2-t.y3; return (0.5 * Math.abs((a*d)-(b*c))); } static boolean insideTriangle(double x, double y, Triangle t) //tells you if x and y is in a triangle bounded by remaining points, //NOT INCLUDING BORDERS { double tot, t1, t2, t3; tot = triArea(new Triangle(t.x1, t.y1, t.x2, t.y2, t.x3, t.y3)); t1 = triArea(new Triangle(x, y, t.x2, t.y2, t.x3, t.y3)); t2 = triArea(new Triangle(x, y, t.x1, t.y1, t.x2, t.y2)); t3 = triArea(new Triangle(x, y, t.x1, t.y1, t.x3, t.y3)); return Math.abs(t1+t2+t3 - tot) <= 0.000001 && (t1 != 0 && t2 !=0 && t3 != 0); //checks if the area of triangles linked to the main triangle //equals the total area of the main triangle to confirm if the point is inside } } class Triangle extends Shape { public double x1, y1, x2, y2, x3, y3; public Triangle(double a, double b, double c, double d, double e, double f) { super('t'); x1 = a; y1 = b; x2 = c; y2 = d; x3 = e; y3 = f; } } class Circle extends Shape { public double x, y, rad; public Circle(double a, double b, double c) { super('c'); x = a; y = b; rad = c; } 45 } 46 class Rectangle extends Shape { public double x1, y1, x2, y2; public Rectangle(double a, double b, double c, double d) { super('r'); x1 = a; y1 = b; x2 = c; y2 = d; } } class Shape { public char type; public Shape(char c) { type = c; } } 47