geometry_examples

advertisement
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
Download