|
// Copyright 2000,
softSurfer (www.softsurfer.com)
// This code may be freely used and modified for any purpose
// providing that this copyright notice is included with it.
// SoftSurfer makes no warranty for this code, and cannot be held
// liable for any real or imagined damage resulting from its use.
// Users of this code must verify correctness for their application.
// a Point
(or vector) is defined by its coordinates
typedef struct {int x, y, z;} Point; // exclude z for
2D
// a Triangle is given by three points: Point V0, V1, V2
// a Polygon is given by:
// int n = number of vertex points
// Point* V[] = an array of points
with V[n]=V[0], V[n+1]=V[1]
// Note: for efficiency
low-level functions are declared to be inline.
// isLeft():
tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and
P1
// =0
for P2 on the line
// <0
for P2 right of the line
inline int
isLeft( Point P0, Point P1, Point P2 )
{
return ( (P1.x - P0.x) * (P2.y - P0.y)
- (P2.x
- P0.x) * (P1.y - P0.y) );
}
//===================================================================
//orientation2D_Triangle()
: test the orientation of a triangle
// Input: three vertex points V0, V1, V2
// Return: >0 for counterclockwise
// =0
for none (degenerate)
// <0
for clockwise
inline int
orientation2D_Triangle( Point V0, Point V1, Point V2 )
{
return isLeft(V0, V1, V2);
}
//===================================================================
//area2D_Triangle():
compute the area of a triangle
// Input: three vertex points V0, V1, V2
// Return: the (float) area of T
inline float
area2D_Triangle( Point V0, Point V1, Point V2 )
{
return (float)isLeft(V0, V1, V2) / 2.0;
}
//===================================================================
//orientation2D_Polygon():
tests the orientation of a simple polygon
// Input: int n = the number of vertices in the
polygon
// Point*
V = an array of n+1 vertices with V[n]=V[0]
// Return: >0 for counterclockwise
// =0
for none (degenerate)
// <0
for clockwise
// Note: this algorithm is faster than computing the
signed area.
int
orientation2D_Polygon( int n, Point* V )
{
// first find rightmost lowest vertex of the polygon
int rmin = 0;
int xmin = V[0].x;
int ymin = V[0].y;
for (int i=1; i<n; i++) {
if (V[i].y > ymin)
continue;
if (V[i].y == ymin) {
// just as low
if (V[i].x
< xmin) // and to left
continue;
}
rmin = i;
// a new rightmost lowest vertex
xmin = V[i].x;
ymin = V[i].y;
}
// test orientation at this rmin vertex
// ccw <=> the edge leaving is left of the entering
edge
if (rmin == 0)
return isLeft( V[n-1], V[0],
V[1] );
else
return isLeft( V[rmin-1], V[rmin],
V[rmin+1] );
}
//===================================================================
//area2D_Polygon():
computes the area of a 2D polygon
// Input: int n = the number of vertices in the
polygon
// Point*
V = an array of n+2 vertices
//
with V[n]=V[0] and V[n+1]=V[1]
// Return: the (float) area of the polygon
float
area2D_Polygon( int n, Point* V )
{
float area = 0;
int i, j, k; //
indices
for (i=1, j=2, k=0; i<=n; i++, j++, k++) {
area += V[i].x * (V[j].y - V[k].y);
}
return area / 2.0;
}
//===================================================================
//area3D_Polygon(): computes the area
of a 3D planar polygon
// Input: int n = the number of vertices in the
polygon
// Point*
V = an array of n+2 vertices in a plane
//
with V[n]=V[0] and V[n+1]=V[1]
// Point
N = unit normal vector of the polygon's plane
// Return: the (float) area of the polygon
float
area3D_Polygon( int n, Point* V, Point N )
{
float area = 0;
float an, ax, ay, az; // abs value of normal and
its coords
int coord;
// coord to ignore: 1=x, 2=y, 3=z
int i, j, k;
// loop indices
// select largest abs coordinate to ignore for projection
ax = (N.x>0 ? N.x : -N.x);
// abs x-coord
ay = (N.y>0 ? N.y : -N.y);
// abs y-coord
az = (N.z>0 ? N.z : -N.z);
// abs z-coord
coord = 3;
// ignore z-coord
if (ax > ay) {
if (ax > az) coord = 1;
// ignore x-coord
}
else if (ay > az) coord = 2; // ignore
y-coord
// compute area of the 2D projection
for (i=1, j=2, k=0; i<=n; i++, j++, k++)
switch (coord) {
case 1:
area
+= (V[i].y * (V[j].z - V[k].z));
continue;
case 2:
area
+= (V[i].x * (V[j].z - V[k].z));
continue;
case 3:
area
+= (V[i].x * (V[j].y - V[k].y));
continue;
}
// scale to get area before projection
an = sqrt( ax*ax + ay*ay + az*az); // length of
normal vector
switch (coord) {
case 1:
area *= (an / (2*ax));
break;
case 2:
area *= (an / (2*ay));
break;
case 3:
area *= (an / (2*az));
}
return ara;
}
// Assume that classes
are already given for the objects:
// Point and Vector with
// coordinates {float x, y, z;}
(z=0 for 2D)
// appropriate operators
for:
// Point
= Point ± Vector
// Vector
= Point - Point
// Vector
= Scalar * Vector
// Line with defining endpoints {Point P0, P1;}
// Segment with defining endpoints {Point P0,
P1;}
//===================================================================
// dot product (3D)
which allows vector operations in arguments
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
#define norm(v) sqrt(dot(v,v)) // norm = length
of vector
#define d(u,v) norm(u-v)
// distance = norm of difference
//closest2D_Point_to_Line():
finds the closest 2D Point to a Line
// Input: an array P[] of n points, and a Line
L
// Return: the index i of the Point P[i] closest to L
int
closest2D_Point_to_Line( Point P[], int n, Line L)
{
// Get coefficients of the implicit line equation.
// Do NOT normalize since scaling by a constant
// is irrelevant for just comparing distances.
float a = L.P0.y - L.P1.y;
float b = L.P1.x - L.P0.x;
float c = L.P0.x * L.P1.y - L.P1.x * L.P0.y;
// initialize min index and distance to P[0]
int mi = 0;
float min = a * P[0].x + b * P[0].y + c;
if (min < 0) min = -min; // absolute
value
// loop through Point array testing for min distance
to L
for (i=1; i<n; i++) {
// just use dist squared (sqrt
not needed for comparison)
float dist = a * P[i].x + b
* P[i].y + c;
if (dist < 0) dist = -dist;
// absolute value
if (dist < min) {
// this point is closer
mi =
i; // so have a new
minimum
min
= dist;
}
}
return mi; // the index of the closest
Point P[mi]
}
//===================================================================
//
dist_Point_to_Line(): get the distance of a point to a line.
// Input: a Point P and a Line L (in any dimension)
// Return: the shortest distance from P to L
float
dist_Point_to_Line( Point P, Line L)
{
Vector v = L.P1 - L.P0;
Vector w = P - L.P0;
double c1 = dot(w,v);
double c2 = dot(v,v);
double b = c1 / c2;
Point Pb = L.P0 + b * v;
return d(P, Pb);
}
//===================================================================
//dist_Point_to_Segment():
get the distance of a point to a segment.
// Input: a Point P and a Segment S (in any dimension)
// Return: the shortest distance from P to S
float
dist_Point_to_Segment( Point P, Segment S)
{
Vector v = S.P1 - S.P0;
Vector w = P - S.P0;
double c1 = dot(w,v);
if ( c1 <= 0 )
return d(P, S.P0);
double c2 = dot(v,v);
if ( c2 <= c1 )
return d(P, S.P1);
double b = c1 / c2;
Point Pb = S.P0 + b * v;
return d(P, Pb);
}
//
a Point is defined by its coordinates {int x, y;}
//===================================================================
// isLeft():
tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and
P1
// =0
for P2 on the line
// <0
for P2 right of the line
// See: the January 2001 Algorithm "
Area of 2D and 3D Triangles and Polygons
"
inline int
isLeft( Point P0, Point P1, Point P2 )
{
return ( (P1.x - P0.x) * (P2.y - P0.y)
- (P2.x
- P0.x) * (P1.y - P0.y) );
}
//===================================================================
// cn_PnPoly():
crossing number test for a point in a polygon
// Input: P = a point,
//
V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
// Return: 0 = outside, 1 = inside
// This code is patterned after [Franklin, 2000]
int
cn_PnPoly( Point P, Point* V, int n )
{
int cn = 0; // the
crossing number counter
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge
from V[i] to V[i+1]
if (((V[i].y <= P.y) &&
(V[i+1].y > P.y)) // an upward crossing
|| ((V[i].y > P.y) &&
(V[i+1].y <= P.y))) { // a downward crossing
// compute
the actual edge-ray intersect x-coordinate
float
vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
if (P.x
< V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
return (cn&1); // 0 if even (out),
and 1 if odd (in)
}
//===================================================================
// wn_PnPoly(): winding number test for
a point in a polygon
// Input: P = a point,
//
V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
// Return: wn = the winding number
(=0 only if P is outside V[])
int
wn_PnPoly( Point P, Point* V, int n )
{
int wn = 0; // the
winding number counter
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge from
V[i] to V[i+1]
if (V[i].y <= P.y) {
// start y <= P.y
if (V[i+1].y
> P.y) // an upward crossing
if (
isLeft
( V[i], V[i+1], P) > 0) // P left of edge
++wn; //
have a valid up intersect
}
else {
// start y > P.y (no test needed)
if (V[i+1].y
<= P.y) // a downward crossing
if (
isLeft
( V[i], V[i+1], P) < 0) // P right of edge
--wn; //
have a valid down intersect
}
}
return wn;
}
//
a Point is defined by its coordinates {int x, y;}
//===================================================================
// isLeft():
tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and
P1
// =0
for P2 on the line
// <0
for P2 right of the line
// See: the January 2001 Algorithm "
Area of 2D and 3D Triangles and Polygons
"
inline int
isLeft( Point P0, Point P1, Point P2 )
{
return ( (P1.x - P0.x) * (P2.y - P0.y)
- (P2.x
- P0.x) * (P1.y - P0.y) );
}
//===================================================================
// cn_PnPoly():
crossing number test for a point in a polygon
// Input: P = a point,
//
V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
// Return: 0 = outside, 1 = inside
// This code is patterned after [Franklin, 2000]
int
cn_PnPoly( Point P, Point* V, int n )
{
int cn = 0; // the
crossing number counter
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge
from V[i] to V[i+1]
if (((V[i].y <= P.y) &&
(V[i+1].y > P.y)) // an upward crossing
|| ((V[i].y > P.y) &&
(V[i+1].y <= P.y))) { // a downward crossing
// compute
the actual edge-ray intersect x-coordinate
float
vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y);
if (P.x
< V[i].x + vt * (V[i+1].x - V[i].x)) // P.x < intersect
++cn; // a valid crossing of y=P.y right of P.x
}
}
return (cn&1); // 0 if even (out),
and 1 if odd (in)
}
//===================================================================
// wn_PnPoly(): winding number test for
a point in a polygon
// Input: P = a point,
//
V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
// Return: wn = the winding number
(=0 only if P is outside V[])
int
wn_PnPoly( Point P, Point* V, int n )
{
int wn = 0; // the
winding number counter
// loop through all edges of the polygon
for (int i=0; i<n; i++) { // edge from
V[i] to V[i+1]
if (V[i].y <= P.y) {
// start y <= P.y
if (V[i+1].y
> P.y) // an upward crossing
if (
isLeft
( V[i], V[i+1], P) > 0) // P left of edge
++wn; //
have a valid up intersect
}
else {
// start y > P.y (no test needed)
if (V[i+1].y
<= P.y) // a downward crossing
if (
isLeft
( V[i], V[i+1], P) < 0) // P right of edge
--wn; //
have a valid down intersect
}
}
return wn;
}
// Assume that classes
are already given for the objects:
// Point and Vector with
// coordinates {float x, y, z;}
// operators for:
// ==
to test equality
// !=
to test inequality
// Point
= Point ± Vector
// Vector
= Point - Point
// Vector
= Scalar * Vector (scalar product)
// Vector
= Vector * Vector (3D cross product)
// Line and Ray and Segment with
defining points {Point P0, P1;}
// (a Line is infinite, Rays
and Segments start at P0)
// (a Ray extends beyond P1,
but a Segment ends at P1)
// Plane with a point and a normal {Point V0;
Vector n;}
//===================================================================
#define SMALL_NUM
0.00000001 // anything that avoids division overflow
// dot product (3D) which allows vector operations in arguments
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
#define perp(u,v) ((u).x * (v).y - (u).y * (v).x) // perp product
(2D)
//intersect2D_2Segments
(): the intersection of 2 finite 2D segments
// Input: two finite segments S1 and S2
// Output: *I0 = intersect point (when it exists)
// *I1
= endpoint of intersect segment [I0,I1] (when it exists)
// Return: 0=disjoint (no intersect)
// 1=intersect
in unique point I0
// 2=overlap
in segment from I0 to I1
int
intersect2D_Segments( Segment S1, Segment S2, Point* I0, Point* I1 )
{
Vector u = S1.P1 - S1.P0;
Vector v = S2.P1 - S2.P0;
Vector w = S1.P0 - S2.P0;
float D = perp(u,v);
// test if they are parallel (includes either being a point)
if (fabs(D) < SMALL_NUM) {
// S1 and S2 are parallel
if (perp(u,w) != 0 || perp(v,w)
!= 0) {
return
0;
// they are NOT collinear
}
// they are collinear or degenerate
// check if they are degenerate
points
float du = dot(u,u);
float dv = dot(v,v);
if (du==0 && dv==0)
{ // both segments
are points
if (S1.P0
!= S2.P0) // they are distinct
points
return 0;
*I0
= S1.P0;
// they are the same point
return
1;
}
if (du==0) {
// S1 is a single point
if
(inSegment(S1.P0, S2) == 0) // but is not in S2
return 0;
*I0
= S1.P0;
return
1;
}
if (dv==0) {
// S2 a single point
if
(inSegment(S2.P0, S1) == 0) // but is not in S1
return 0;
*I0
= S2.P0;
return
1;
}
// they are collinear segments
- get overlap (or not)
float t0, t1;
// endpoints of S1 in eqn for S2
Vector w2 = S1.P1 - S2.P0;
if (v.x != 0) {
t0 = w.x / v.x;
t1 = w2.x / v.x;
}
else {
t0 = w.y / v.y;
t1 = w2.y / v.y;
}
if (t0 > t1) {
// must have t0 smaller than t1
float t=t0; t0=t1; t1=t; // swap if not
}
if (t0 > 1 || t1 < 0)
{
return
0; // NO overlap
}
t0 = t0<0? 0 : t0;
// clip to min 0
t1 = t1>1? 1 : t1;
// clip to max 1
if (t0 == t1) {
// intersect is a point
*I0
= S2.P0 + t0 * v;
return
1;
}
// they overlap in a valid subsegment
*I0 = S2.P0 + t0 * v;
*I1 = S2.P0 + t1 * v;
return 2;
}
// the segments are skew and may intersect in a point
// get the intersect parameter for S1
float sI = perp(v,w) / D;
if (sI < 0 || sI > 1)
// no intersect with S1
return 0;
// get the intersect parameter
for S2
float tI = perp(u,w) / D;
if (tI < 0 || tI > 1)
// no intersect with S2
return 0;
*I0 = S1.P0 + sI * u;
// compute S1 intersect point
return 1;
}
//===================================================================
// inSegment()
: determine if a point is inside a segment
// Input: a point P, and a collinear segment S
// Return: 1 = P is inside S
// 0
= P is not inside S
int
inSegment( Point P, Segment S)
{
if (S.P0.x != S.P1.x) { // S is not
vertical
if (S.P0.x <= P.x &&
P.x <= S.P1.x)
return
1;
if (S.P0.x >= P.x &&
P.x >= S.P1.x)
return
1;
}
else { // S is vertical, so test y
coordinate
if (S.P0.y <= P.y &&
P.y <= S.P1.y)
return
1;
if (S.P0.y >= P.y &&
P.y >= S.P1.y)
return
1;
}
return 0;
}
//===================================================================
//intersect3D_SegmentPlane
(): intersect a segment and a plane
// Input: S = a segment, and Pn = a plane = {Point
V0; Vector n;}
// Output: *I0 = the intersect point (when it exists)
// Return: 0 = disjoint (no intersection)
// 1
= intersection in the unique point *I0
// 2
= the segment lies in the plane
int
intersect3D_SegmentPlane( Segment S, Plane Pn, Point* I )
{
Vector u = S.P1 - S.P0;
Vector w = S.P0 - Pn.V0;
float D = dot(Pn.n, u);
float N = -dot(Pn.n, w);
if (fabs(D) < SMALL_NUM) {
// segment is parallel to plane
if (N == 0)
// segment lies in plane
return
2;
else
return
0;
// no intersection
}
// they are not parallel
// compute intersect param
float sI = N / D;
if (sI < 0 || sI > 1)
return 0;
// no intersection
*I = S.P0 + sI * u;
// compute segment intersect point
return 1;
}
//===================================================================
//intersect3D_2Planes(): the 3D intersect
of two planes
// Input: two planes Pn1 and Pn2
// Output: *L = the intersection line (when it exists)
// Return: 0 = disjoint (no intersection)
// 1
= the two planes coincide
// 2
= intersection in the unique line *L
int
intersect3D_2Planes( Plane Pn1, Plane Pn2, Line* L )
{
Vector u = Pn1.n * Pn2.n;
// cross product
float ax = (u.x >= 0 ? u.x : -u.x);
float ay = (u.y >= 0 ? u.y : -u.y);
float az = (u.z >= 0 ? u.z : -u.z);
// test if the two planes are parallel
if ((ax+ay+az) < SMALL_NUM) {
// Pn1 and Pn2 are near parallel
// test if disjoint or coincide
Vector v = Pn2.V0
- Pn1.V0;
if (dot(Pn1.n, v) == 0)
// Pn2.V0 lies in Pn1
return
1;
// Pn1 and Pn2 coincide
else
return
0;
// Pn1 and Pn2 are disjoint
}
// Pn1 and Pn2 intersect in a line
// first determine max abs coordinate of cross product
int maxc;
// max coordinate
if (ax > ay) {
if (ax > az)
maxc = 1;
else maxc = 3;
}
else {
if (ay > az)
maxc = 2;
else maxc = 3;
}
// next, to get a point on the intersect line
// zero the max coord, and solve for the other two
Point iP;
// intersect point
float d1, d2;
// the constants in the 2 plane equations
d1 = -dot(Pn1.n, Pn1.V0); // note: could be pre-stored
with plane
d2 = -dot(Pn2.n, Pn2.V0); // ditto
switch (maxc) {
// select max coordinate
case 1:
// intersect with x=0
iP.x = 0;
iP.y = (d2*Pn1.n.z - d1*Pn2.n.z)
/ u.x;
iP.z = (d1*Pn2.n.y - d2*Pn1.n.y)
/ u.x;
break;
case 2:
// intersect with y=0
iP.x = (d1*Pn2.n.z - d2*Pn1.n.z)
/ u.y;
iP.y = 0;
iP.z = (d2*Pn1.n.x - d1*Pn2.n.x)
/ u.y;
break;
case 3:
// intersect with z=0
iP.x = (d2*Pn1.n.y - d1*Pn2.n.y)
/ u.z;
iP.y = (d1*Pn2.n.x - d2*Pn1.n.x)
/ u.z;
iP.z = 0;
}
L->P0 = iP;
L->P1 = iP + u;
return 2;
}
// Assume that classes
are already given for the objects:
// Point and Vector with
// coordinates {float x, y, z;}
// operators for:
// ==
to test equality
// !=
to test inequality
// (Vector)0
= (0,0,0) (null vector)
// Point
= Point ± Vector
// Vector
= Point - Point
// Vector
= Scalar * Vector (scalar product)
// Vector
= Vector * Vector (cross product)
// Line and Ray and Segment with
defining points {Point P0, P1;}
// (a Line is infinite, Rays
and Segments start at P0)
// (a Ray extends beyond P1,
but a Segment ends at P1)
// Plane with a point and a normal {Point V0;
Vector n;}
// Triangle with defining vertices {Point V0,
V1, V2;}
// Polyline and Polygon with n vertices
{int n; Point *V;}
// (a Polygon has V[n]=V[0])
//===================================================================
#define SMALL_NUM
0.00000001 // anything that avoids division overflow
// dot product (3D) which allows vector operations in arguments
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
//intersect_RayTriangle():
intersect a ray with a 3D triangle
// Input: a ray R, and a triangle T
// Output: *I = intersection point (when it exists)
// Return: -1 = triangle is degenerate (a segment or
point)
//
0 = disjoint (no intersect)
//
1 = intersect in unique point I1
//
2 = are in the same plane
int
intersect_RayTriangle( Ray R, Triangle T, Point* I )
{
Vector u, v, n;
// triangle vectors
Vector dir, w0, w;
// ray vectors
float r, a, b;
// params to calc ray-plane intersect
// get triangle edge vectors and plane normal
u = T.V1 - T.V0;
v = T.V2 - T.V0;
n = u * v;
// cross product
if (n == (Vector)0)
// triangle is degenerate
return -1;
// do not deal with this case
dir = R.P1 - R.P0;
// ray direction vector
w0 = R.P0 - T.V0;
a = dot(n,w0);
b = dot(n,dir);
if (fabs(b) < SMALL_NUM) {
// ray is parallel to triangle plane
if (a == 0)
// ray lies in triangle plane
return
2;
else return 0;
// ray disjoint from plane
}
// get intersect point of ray with triangle plane
r = -a / b;
if (r < 0.0)
// ray goes away from triangle
return 0;
// => no intersect
// for a segment, also test if (r > 1.0) => no
intersect
*I = R.P0 + r * dir;
// intersect point of ray and plane
// is I inside T?
float uu, uv, vv, wu, wv, D;
uu = dot(u,u);
uv = dot(u,v);
vv = dot(v,v);
w = *I - T.V0;
wu = dot(w,u);
wv = dot(w,v);
D = uv * uv - uu * vv;
// get and test parametric coords
float s, t;
s = (uv * wv - vv * wu) / D;
if (s < 0.0 || s > 1.0)
// I is outside T
return 0;
t = (uv * wu - uu * wv) / D;
if (t < 0.0 || (s + t) > 1.0) // I is outside
T
return 0;
return 1;
// I is in T
}
// Assume that classes
are already given for the objects:
// Point and Vector with
// coordinates {float x, y, z;}
// operators for:
// Point
= Point ± Vector
// Vector
= Point - Point
// Vector
= Vector ± Vector
// Vector
= Scalar * Vector
// Line and Segment with defining points
{Point P0, P1;}
// Track with initial position and velocity vector
// {Point
P0; Vector v;}
//===================================================================
#define SMALL_NUM
0.00000001 // anything that avoids division overflow
// dot product (3D) which allows vector operations in arguments
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
#define norm(v) sqrt(dot(v,v)) // norm = length
of vector
#define d(u,v) norm(u-v)
// distance = norm of difference
//
dist3D_Line_to_Line():
// Input: two 3D lines L1 and L2
// Return: the shortest distance between L1 and L2
float
dist3D_Line_to_Line( Line L1, Line L2)
{
Vector u = L1.P1 - L1.P0;
Vector v = L2.P1 - L2.P0;
Vector w = L1.P0 - L2.P0;
float a = dot(u,u);
// always >= 0
float b = dot(u,v);
float c = dot(v,v);
// always >= 0
float d = dot(u,w);
float e = dot(v,w);
float D = a*c - b*b;
// always >= 0
float sc, tc;
// compute the line parameters of the two closest points
if (D < SMALL_NUM) {
// the lines are almost parallel
sc = 0.0;
tc = (b>c ? d/b : e/c);
// use the largest denominator
}
else {
sc = (b*e - c*d) / D;
tc = (a*e - b*d) / D;
}
// get the difference of the two closest points
Vector dP = w + (sc * u) - (tc * v);
// = L1(sc) - L2(tc)
return norm(dP); // return the closest distance
}
//===================================================================
//dist3D_Segment_to_Segment():
// Input: two 3D line segments S1 and S2
// Return: the shortest distance between S1 and S2
float
dist3D_Segment_to_Segment( Segment S1, Segment S2)
{
Vector u = S1.P1 - S1.P0;
Vector v = S2.P1 - S2.P0;
Vector w = S1.P0 - S2.P0;
float a = dot(u,u);
// always >= 0
float b = dot(u,v);
float c = dot(v,v);
// always >= 0
float d = dot(u,w);
float e = dot(v,w);
float D = a*c - b*b;
// always >= 0
float sc, sN, sD = D;
// sc = sN / sD, default sD = D >= 0
float tc, tN, tD = D;
// tc = tN / tD, default tD = D >= 0
// compute the line parameters of the two closest points
if (D < SMALL_NUM) { // the lines are almost parallel
sN = 0.0;
tN = e;
tD = c;
}
else {
// get the closest points on the infinite lines
sN = (b*e - c*d);
tN = (a*e - b*d);
if (sN < 0) {
// sc < 0 => the s=0 edge is visible
sN =
0.0;
tN =
e;
tD =
c;
}
else if (sN > sD) {
// sc > 1 => the s=1 edge is visible
sN =
sD;
tN =
e + b;
tD =
c;
}
}
if (tN < 0) {
// tc < 0 => the t=0 edge is visible
tN = 0.0;
// recompute sc for this edge
if (-d < 0)
sN =
0.0;
else if (-d > a)
sN =
sD;
else {
sN =
-d;
sD =
a;
}
}
else if (tN > tD) {
// tc > 1 => the t=1 edge is visible
tN = tD;
// recompute sc for this edge
if ((-d + b) < 0)
sN =
0;
else if ((-d + b) > a)
sN =
sD;
else {
sN =
(-d + b);
sD =
a;
}
}
// finally do the division to get sc and tc
sc = sN / sD;
tc = tN / tD;
// get the difference of the two closest points
Vector dP = w + (sc * u) - (tc * v);
// = S1(sc) - S2(tc)
return norm(dP); // return the closest distance
}
//===================================================================
// cpa_time():
compute the time of CPA for two tracks
// Input: two tracks Tr1 and Tr2
// Return: the time at which the two tracks are closest
float
cpa_time( Track Tr1, Track Tr2 )
{
Vector dv = Tr1.v - Tr2.v;
float dv2 = dot(dv,dv);
if (dv2 < SMALL_NUM)
// the tracks are almost parallel
return 0.0;
// any time is ok. Use time 0.
Vector w0 = Tr1.P0 - Tr2.P0;
float cpatime = -dot(w0,dv) / dv2;
return cpatime;
// time of CPA
}
//===================================================================
//cpa_distance():
compute the distance at CPA for two tracks
// Input: two tracks Tr1 and Tr2
// Return: the distance for which the two tracks are
closest
float
cpa_distance( Track Tr1, Track Tr2 )
{
float ctime = cpa_time( Tr1, Tr2);
Point P1 = Tr1.P0 + (ctime * Tr1.v);
Point P2 = Tr2.P0 + (ctime * Tr2.v);
return d(P1,P2);
// distance at CPA
}
//===================================================================
// Assume that classes
are already given for the objects:
// Point and Vector with
// coordinates {float x, y;}
// operators for:
// Point
= Point ± Vector
// Vector
= Point - Point
// Vector
= Vector ± Vector
// Vector
= Scalar * Vector (scalar product)
// Vector
= Vector / Scalar (scalar division)
// Ball with a center and radius {Point center;
float radius;}
//===================================================================
// dot product which
allows vector operations in arguments
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y)
#define norm2(v) dot(v,v)
// norm2 = squared length of vector
#define norm(v) sqrt(norm2(v)) // norm = length
of vector
#define d(u,v) norm(u-v)
// distance = norm of difference
// fastBall():
a fast approximation of the bounding ball for a point set
//
based on the algorithm given by [Jack Ritter, 1990]
// Input: an array V[] of n points
// Output: a bounding ball = {Point center; float radius;}
void
fastBall( Point V[], int n, Ball* B)
{
Point C;
// Center of ball
float rad, rad2;
// radius and radius squared
float xmin, xmax, ymin, ymax;
// bounding box extremes
int Pxmin, Pxmax, Pymin, Pymax; //
index of V[] at box extreme
// find a large diameter to start with
// first get the bounding box and V[] extreme points
for it
xmin = xmax = V[0].x;
ymin = ymax = V[0].y;
Pxmin = Pxmax = Pymin = Pymax = 0;
for (int i=1; i<n; i++) {
if (V[i].x < xmin) {
xmin
= V[i].x;
Pxmin
= i;
}
else if (V[i].x > xmax) {
xmax
= V[i].x;
Pxmax
= i;
}
if (V[i].y < ymin) {
ymin
= V[i].y;
Pymin
= i;
}
else if (V[i].y > ymax) {
ymax
= V[i].y;
Pymax
= i;
}
}
// select the largest extent as an initial diameter
for the ball
Vector dVx = V[Pxmax] - V[Pxmin]; // diff of Vx max
and min
Vector dVy = V[Pymax] - V[Pymin]; // diff of Vy max
and min
float dx2 = norm2(dVx); // Vx diff squared
float dy2 = norm2(dVy); // Vy diff squared
if (dx2 >= dy2) {
// x direction is largest extent
C = V[Pxmin] + (dVx / 2.0);
// Center = midpoint of extremes
rad2 = norm2(V[Pxmax] - C);
// radius squared
}
else {
// y direction is largest extent
C = V[Pymin] + (dVy / 2.0);
// Center = midpoint of extremes
rad2 = norm2(V[Pymax] - C);
// radius squared
}
rad = sqrt(rad2);
// now check that all points V[i] are in the ball
// and if not, expand the ball just enough to include
them
Vector dV;
float dist, dist2;
for (int i=0; i<n; i++) {
dV = V[i] - C;
dist2 = norm2(dV);
if (dist2 <= rad2)
// V[i] is inside the ball already
continue;
// V[i] not in ball, so expand
ball to include it
dist = sqrt(dist2);
rad = (rad + dist) / 2.0;
// enlarge radius just enough
rad2 = rad * rad;
C = C + ((dist-rad)/dist) *
dV; // shift Center toward V[i]
}
B->Center = C;
B->radius = rad;
return;
}
// Assume
that classes are already given for the objects:
// Point with 2D coordinates {float x, y;}
// Polygon with n vertices {int n; Point *V;}
with V[n]=V[0]
// Tnode is a node element structure for a BBT
// BBT is a class for a Balanced Binary Tree
// such as an AVL, a 2-3, or
a red-black tree
// with methods given by the
placeholder code:
typedef struct _BBTnode Tnode;
struct _BBTnode {
void* val;
// plus node mgmt info ...
};
class BBT {
Tnode *root;
public:
BBT() {root = (Tnode*)0;} // constructor
~BBT() {freetree();} // destructor
Tnode* insert( void* ){};
// insert data into the tree
Tnode* find( void* ){};
// find data from the tree
Tnode* next( Tnode* ){};
// get next tree node
Tnode* prev( Tnode* ){};
// get previous tree node
void remove(
Tnode* ){}; // remove node from the tree
void freetree(){};
// free all tree data structs
};
// NOTE:
// Code for these methods must be provided for the algorithm to work.
// We have not provided it since binary tree algorithms are well-known
// and code is widely available. Further, we want to reduce the clutter
// accompanying the essential sweep line algorithm.
//===================================================================
#define
FALSE 0
#define TRUE 1
#define LEFT 0
#define RIGHT 1
extern void
qsort(void*, unsigned, unsigned, int(*)(const void*,const void*));
// xyorder(): determines the xy lexicographical order of two points
// returns: (+1) if p1 > p2; (-1) if p1
< p2; and 0 if equal
int xyorder( Point* p1, Point* p2 )
{
// test the x-coord first
if (p1->x > p2->x) return 1;
if (p1->x < p2->x) return (-1);
// and test the y-coord second
if (p1->y > p2->y) return 1;
if (p1->y < p2->y) return (-1);
// when you exclude all other possibilities, what remains
is...
return 0; // they are the same point
}
// isLeft(): tests if point P2 is Left|On|Right of the line P0 to
P1.
// returns: >0 for left, 0 for on, and
<0 for right of the line.
// (see the January 2001 Algorithm on
Area of Triangles
)
inline float
isLeft( Point P0, Point P1, Point P2 )
{
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y
- P0.y);
}
//===================================================================
//EventQueue
Class
// Event element data struct
typedef struct _event Event;
struct _event {
int edge;
// polygon edge i is V[i] to V[i+1]
int type;
// event type: LEFT or RIGHT vertex
Point* eV;
// event vertex
};
int E_compare( const void* v1, const void* v2 ) // qsort compare two events
{
Event** pe1 = (Event**)v1;
Event** pe2 = (Event**)v2;
return xyorder( (*pe1)->eV, (*pe2)->eV );
}
// the EventQueue is a presorted array (no insertions needed)
class EventQueue {
int ne;
// total number of events in array
int ix;
// index of next event on queue
Event* Edata;
// array of all events
Event** Eq;
// sorted list of event pointers
public:
EventQueue(Polygon P); // constructor
~EventQueue(void)
// destructor
{ delete Eq; delete Edata;}
Event* next();
// next event on queue
};
// EventQueue Routines
EventQueue::EventQueue( Polygon P )
{
ix = 0;
ne = 2 * P.n;
// 2 vertex events for each edge
Edata = (Event*)new Event[ne];
Eq = (Event**)new (Event*)[ne];
for (int i=0; i < ne; i++)
// init Eq array pointers
Eq[i] = &Edata[i];
// Initialize event queue with edge segment endpoints
for (int i=0; i < P.n; i++) {
// init data for edge i
Eq[2*i]->edge = i;
Eq[2*i+1]->edge = i;
Eq[2*i]->eV =
&(P.V[i]);
Eq[2*i+1]->eV = &(P.V[i+1]);
if (xyorder( &P.V[i], &P.V[i+1])
< 0) { // determine type
Eq[2*i]->type
= LEFT;
Eq[2*i+1]->type
= RIGHT;
}
else {
Eq[2*i]->type
= RIGHT;
Eq[2*i+1]->type
= LEFT;
}
}
// Sort Eq[] by increasing x and y
qsort( Eq, ne, sizeof(Event*), E_compare );
}
Event* EventQueue::next()
{
if (ix >= ne)
return (Event*)0;
else
return Eq[ix++];
}
//===================================================================
//SweepLine
Class
// SweepLine segment data struct
typedef struct _SL_segment SLseg;
struct _SL_segment {
int edge;
// polygon edge i is V[i] to V[i+1]
Point lP;
// leftmost vertex point
Point rP;
// rightmost vertex point
SLseg* above;
// segment above this one
SLseg* below;
// segment below this one
};
// the Sweep Line itself
class SweepLine {
int nv;
// number of vertices in polygon
Polygon* Pn;
// initial Polygon
BBT Tree;
// balanced binary tree
public:
SweepLine(Polygon P)
// constructor
{ nv = P.n; Pn = &P; }
~SweepLine(void)
// destructor
{ Tree.freetree();}
SLseg* add( Event* );
SLseg* find( Event* );
int intersect( SLseg*,
SLseg* );
void remove( SLseg* );
};
SLseg* SweepLine::add( Event* E )
{
// fill in SLseg element data
SLseg* s = new SLseg;
s->edge = E->edge;
// if it is being added, then it must be a LEFT edge
event
// but need to determine which endpoint is the left
one
Point* v1 = &(Pn->V[s->edge]);
Point* v2 = &(Pn->V[s->edge+1]);
if (xyorder( v1, v2) < 0) { // determine which is
leftmost
s->lP = *v1;
s->rP = *v2;
}
else {
s->rP = *v1;
s->lP = *v2;
}
s->above = (SLseg*)0;
s->below = (SLseg*)0;
// add a node to the balanced binary tree
Tnode* nd = Tree.insert(s);
Tnode* nx = Tree.next(nd);
Tnode* np = Tree.prev(nd);
if (nx != (Tnode*)0) {
s->above = (SLseg*)nx->val;
s->above->below = s;
}
if (np != (Tnode*)0) {
s->below = (SLseg*)np->val;
s->below->above = s;
}
return s;
}
SLseg* SweepLine::find( Event* E )
{
// need a segment to find it in the tree
SLseg* s = new SLseg;
s->edge = E->edge;
s->above = (SLseg*)0;
s->below = (SLseg*)0;
Tnode* nd = Tree.find(s);
delete s;
if (nd == (Tnode*)0)
return (SLseg*)0;
return (SLseg*)nd->val;
}
void SweepLine::remove( SLseg* s )
{
// remove the node from the balanced binary tree
Tnode* nd = Tree.find(s);
if (nd == (Tnode*)0)
return;
// not there !
// get the above and below segments pointing to each
other
Tnode* nx = Tree.next(nd);
if (nx != (Tnode*)0) {
SLseg* sx = (SLseg*)(nx->val);
sx->below = s->below;
}
Tnode* np = Tree.prev(nd);
if (np != (Tnode*)0) {
SLseg* sp = (SLseg*)(np->val);
sp->above = s->above;
}
Tree.remove(nd);
// now can safely remove it
delete s;
}
// test intersect of 2 segments and return: 0=none, 1=intersect
int SweepLine::intersect( SLseg* s1, SLseg* s2)
{
if (s1 == (SLseg*)0 || s2 == (SLseg*)0)
return FALSE;
// no intersect if either segment doesn't exist
// check for consecutive edges in polygon
int e1 = s1->edge;
int e2 = s2->edge;
if (((e1+1)%nv == e2) || (e1 == (e2+1)%nv))
return FALSE;
// no non-simple intersect since consecutive
// test for existence of an intersect point
float lsign, rsign;
lsign = isLeft(s1->lP, s1->rP, s2->lP);
// s2 left point sign
rsign = isLeft(s1->lP, s1->rP, s2->rP);
// s2 right point sign
if (lsign * rsign > 0) // s2 endpoints have same
sign relative to s1
return FALSE;
// => on same side => no intersect is possible
lsign = isLeft(s2->lP, s2->rP, s1->lP);
// s1 left point sign
rsign = isLeft(s2->lP, s2->rP, s1->rP);
// s1 right point sign
if (lsign * rsign > 0) // s1 endpoints have same
sign relative to s2
return FALSE;
// => on same side => no intersect is possible
// the segments s1 and s2 straddle each other
return TRUE;
// => an intersect exists
}
//===================================================================
//simple_Polygon()
: test if a Polygon P is simple or not
// Input: Pn = a polygon with n vertices
V[]
// Return: FALSE(0) = is NOT simple
//
TRUE(1) = IS simple
int
simple_Polygon( Polygon Pn )
{
EventQueue Eq(Pn);
SweepLine
SL(Pn);
Event* e;
// the current event
SLseg* s;
// the current SL segment
// This loop processes all events in the sorted queue
// Events are only left or right vertices since
// No new events will be added (an intersect => Done)
while (e = Eq.next()) {
// while there are events
if (e->type == LEFT) {
// process a left vertex
s =
SL.add(e); // add it to
the sweep line
if (SL.intersect(
s, s->above))
return FALSE; // Pn is NOT simple
if (SL.intersect(
s, s->below))
return FALSE; // Pn is NOT simple
}
else {
// processs a right vertex
s =
SL.find(e);
if (SL.intersect(
s->above, s->below))
return FALSE; // Pn is NOT simple
SL.remove(s);
// remove it from the sweep line
}
}
return TRUE; // Pn is
simple
}
//===================================================================
// Assume that a
class is already given for the object:
// Point with coordinates {float x, y;}
//===================================================================
//
isLeft(): tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and
P1
// =0
for P2 on the line
// <0
for P2 right of the line
//
isLeft( Point P0, Point P1, Point P2 )
{
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y
- P0.y);
}
//===================================================================
//chainHull_2D():
Andrew's monotone chain 2D convex hull algorithm
// Input: P[] = an array of 2D points
//
presorted by increasing x- and y-coordinates
//
n = the number of points in P[]
// Output: H[] = an array of the convex hull vertices
(max is n)
// Return: the number of points in H[]
int
chainHull_2D( Point* P, int n, Point* H )
{
// the output array H[] will be used as the stack
int bot=0, top=(-1); // indices
for bottom and top of the stack
int i;
// array scan index
// Get the indices of points with min x-coord and min|max
y-coord
int minmin = 0, minmax;
float xmin = P[0].x;
for (i=1; i<n; i++)
if (P[i].x != xmin) break;
minmax = i-1;
if (minmax == n-1) {
// degenerate case: all x-coords == xmin
H[++top] = P[minmin];
if (P[minmax].y != P[minmin].y)
// a nontrivial segment
H[++top]
= P[minmax];
return top+1;
}
// Get the indices of points with max x-coord and min|max
y-coord
int maxmin, maxmax = n-1;
float xmax = P[n-1].x;
for (i=n-2; i>=0; i--)
if (P[i].x != xmax) break;
maxmin = i+1;
// Compute the lower hull on the stack H
H[++top] = P[minmin];
// push minmin point onto stack
i = minmax;
while (++i <= maxmin)
{
// the lower line joins P[minmin]
with P[maxmin]
if (isLeft( P[minmin], P[maxmin],
P[i]) >= 0 && i < maxmin)
continue;
// ignore P[i] above or on the lower line
while (top > 0)
// there are at least 2 points on the stack
{
// test
if P[i] is left of the line at the stack top
if (isLeft(
H[top-1], H[top], P[i]) > 0)
break; // P[i] is a new hull
vertex
else
top--; // pop top point off
stack
}
H[++top] = P[i];
// push P[i] onto stack
}
// Next, compute the upper hull on the stack
H above the bottom hull
if (maxmax != maxmin)
// if distinct xmax points
H[++top] = P[maxmax]; // push maxmax point onto stack
bot = top;
// the bottom point of the upper hull stack
i = maxmin;
while (--i >= minmax)
{
// the upper line joins P[maxmax]
with P[minmax]
if (isLeft( P[maxmax], P[minmax],
P[i]) >= 0 && i > minmax)
continue;
// ignore P[i] below or on the upper line
while (top > bot)
// at least 2 points on the upper stack
{
// test
if P[i] is left of the line at the stack top
if (isLeft(
H[top-1], H[top], P[i]) > 0)
break; // P[i] is a new hull
vertex
else
top--; // pop top point off
stack
}
H[++top] = P[i];
// push P[i] onto stack
}
if (minmax != minmin)
H[++top] = P[minmin];
// push joining endpoint onto stack
return top+1;
}
//
isLeft(): tests if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and
P1
// =0
for P2 on the line
// <0
for P2 right of the line
isLeft( Point P0, Point P1, Point P2 )
{
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y
- P0.y);
}
//===================================================================
#define NONE
(-1)
typedef struct range_bin Bin;
struct range_bin {
int min; // index
of min point P[] in bin (>=0 or NONE)
int max; // index
of max point P[] in bin (>=0 or NONE)
};
//nearHull_2D():
the BFP fast approximate 2D convex hull algorithm
// Input: P[] = an (unsorted) array of 2D
points
//
n = the number of points in P[]
//
k = the approximation accuracy (large k = more accurate)
// Output: H[] = an array of the convex hull vertices
(max is n)
// Return: the number of points in H[]
int
nearHull_2D( Point* P, int n, int k, Point* H )
{
int minmin=0, minmax=0;
int maxmin=0, maxmax=0;
float xmin = P[0].x, xmax = P[0].x;
Point* cP;
// the current point being considered
int bot=0, top=(-1); // indices
for bottom and top of the stack
// Get the points with (1) min-max x-coord, and (2)
min-max y-coord
for (int i=1; i<n; i++) {
cP = &P[i];
if (cP->x <= xmin) {
if (cP->x
< xmin) { // new xmin
xmin = cP->x;
minmin = minmax = i;
}
else
{
// another xmin
if (cP->y < P[minmin].y)
minmin = i;
else if (cP->y > P[minmax].y)
minmax = i;
}
}
if (cP->x >= xmax) {
if (cP->x
> xmax) { // new xmax
xmax = cP->x;
maxmin = maxmax = i;
}
else
{
// another xmax
if (cP->y < P[maxmin].y)
maxmin = i;
else if (cP->y > P[maxmax].y)
maxmax = i;
}
}
}
if (xmin == xmax) { //
degenerate case: all x-coords == xmin
H[++top] = P[minmin];
// a point, or
if (minmax != minmin)
// a nontrivial segment
H[++top]
= P[minmax];
return top+1;
// one or two points
}
// Next, get the max and min points in the k range bins
Bin* B = new Bin[k+2]; // first
allocate the bins
B[0].min = minmin;
B[0].max = minmax; // set bin 0
B[k+1].min = maxmin;
B[k+1].max = maxmax; // set bin k+1
for (int b=1; b<=k; b++) { // initially nothing is
in the other bins
B[b].min = B[b].max = NONE;
}
for (int b, i=0; i<n; i++) {
cP = &P[i];
if (cP->x == xmin || cP->x
== xmax) // already have bins 0 and k+1
continue;
// check if a lower or upper
point
if (isLeft( P[minmin], P[maxmin],
*cP) < 0) { // below lower line
b =
(int)( k * (cP->x - xmin) / (xmax - xmin) ) + 1; // bin #
if (B[b].min
== NONE) // no min point in this range
B[b].min = i;
// first min
else
if (cP->x < B[b].min)
B[b].min = i;
// new min
continue;
}
if (isLeft( P[minmax], P[maxmax],
*cP) > 0) { // above upper line
b =
(int)( k * (cP->x - xmin) / (xmax - xmin) ) + 1; // bin #
if (B[b].max
== NONE) // no max point in this range
B[b].max = i;
// first max
else
if (cP->x < B[b].min)
B[b].max = i;
// new max
continue;
}
}
// Now, use the chain algorithm to get the lower and
upper hulls
// the output array H[] will be used as the stack
// First, compute the lower hull on the stack
H
for (int i=0; i <= k+1; ++i)
{
if (B[i].min == NONE)
// no min point in this range
continue;
cP = &P[ B[i].min ];
// select the current min point
while (top > 0)
// there are at least 2 points on the stack
{
// test
if current point is left of the line at the stack top
if (isLeft(
H[top-1], H[top], *cP) > 0)
break; // cP is a new hull
vertex
else
top--; // pop top point off
stack
}
H[++top] = *cP;
// push current point onto stack
}
// Next, compute the upper hull on the stack
H above the bottom hull
if (maxmax != maxmin)
// if distinct xmax points
H[++top] = P[maxmax]; // push maxmax point onto stack
bot = top;
// the bottom point of the upper hull stack
for (int i=k; i >= 0; --i)
{
if (B[i].max == NONE)
// no max point in this range
continue;
cP = &P[ B[i].max ];
// select the current max point
while (top > bot)
// at least 2 points on the upper stack
{
// test
if current point is left of the line at the stack top
if (isLeft(
H[top-1], H[top], *cP) > 0)
break; // current point is
a new hull vertex
else
top--; // pop top point off
stack
}
H[++top] = *cP;
// push current point onto stack
}
if (minmax != minmin)
H[++top] = P[minmin];
// push joining endpoint onto stack
delete B;
// free bins before returning
return top+1;
// # of points on the stack
}
// Assume that classes
are already given for the objects:
// Point and Vector with
// coordinates {float x, y;}
// operators for:
// ==
to test equality
// !=
to test inequality
// Point
= Point ± Vector
// Vector
= Point - Point
// Vector
= Vector ± Vector
// Vector
= Scalar * Vector (scalar product)
// Segment with defining endpoints {Point P0,
P1;}
//===================================================================
#define TRUE
1
#define FALSE 0
#define SMALL_NUM 0.00000001 // anything that avoids division overflow
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y)
// 2D dot product
#define perp(u,v) ((u).x * (v).y - (u).y * (v).x) //
2D perp product
//intersect2D_SegPoly():
// Input: S = 2D segment to intersect
with the convex polygon
// n
= number of 2D points in the polygon
// V[]
= array of n+1 vertex points with V[n]=V[0]
// Note: The polygon MUST be convex and
//
have vertices oriented counterclockwise (ccw).
// This
code does not check for and verify these conditions.
// Output: *IS = the intersection segment (when it exists)
// Return: FALSE = no intersection
// TRUE
= a valid intersection segment exists
int
intersect2D_SegPoly( Segment S, Point* V, int n, Segment* IS)
{
if (S.P0 == S.P1) {
// the segment S is a single point
// test for inclusion of S.P0
in the polygon
*IS = S;
// same point if inside polygon
return cn_PnPoly( S.P0, V, n
);
}
float tE = 0;
// the maximum entering segment parameter
float tL = 1;
// the minimum leaving segment parameter
float t, N, D;
// intersect parameter t = N / D
Vector dS = S.P1- S.P0; // the segment
direction vector
Vector e;
// edge vector
// Vector ne;
// edge outward normal (not explicit in code)
for (int i=0; i < n; i++) // process polygon
edge V[i]V[i+1]
{
e = V[i+1] - V[i];
N = perp(e, S.P0-V[i]);// =
-dot(ne, S.P0-V[i])
D = -perp(e, dS);
// = dot(ne, dS)
if (fabs(D) < SMALL_NUM)
{ // S is nearly parallel to this edge
if (N
< 0)
// P0 is outside this edge, so
return FALSE; // S is outside the polygon
else
// S cannot cross this edge, so
continue; // ignore
this edge
}
t = N / D;
if (D < 0) {
// segment S is entering across this edge
if (t
> tE) { // new max tE
tE = t;
if (tE > tL) // S enters after leaving polygon
return FALSE;
}
}
else {
// segment S is leaving across this edge
if (t
< tL) { // new min tL
tL = t;
if (tL < tE) // S leaves before entering polygon
return FALSE;
}
}
}
// tE <= tL implies that there is a valid intersection
subsegment
IS->P0 = S.P0 + tE * dS; //
= P(tE) = point where S enters polygon
IS->P1 = S.P0 + tL * dS; //
= P(tL) = point where S leaves polygon
return TRUE;
}
// isLeft():
test if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and
P1
// =0
for P2 on the line
// <0
for P2 right of the line
//
isLeft( Point P0, Point P1, Point P2 )
{
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y
- P0.y);
}
// tests for polygon vertex ordering relative to a fixed point P
#define above(P,Vi,Vj) (isLeft(P,Vi,Vj) > 0) // true
if Vi is above Vj
#define below(P,Vi,Vj) (isLeft(P,Vi,Vj) < 0) // true
if Vi is below Vj
//===================================================================
// tangent_PointPoly()
: find any polygon's exterior tangents
// Input: P = a 2D point (exterior to the
polygon)
// n
= number of polygon vertices
// V
= array of vertices for any 2D polygon with V[n]=V[0]
// Output: *rtan = index of rightmost tangent point V[*rtan]
// *ltan
= index of leftmost tangent point V[*ltan]
void
tangent_PointPoly( Point P, int n, Point* V, int* rtan, int* ltan )
{
float eprev, enext;
// V[i] previous and next edge turn direction
*rtan = *ltan = 0;
// initially assume V[0] = both tangents
eprev =
isLeft
(V[0], V[1], P);
for (int i=1; i<n; i++) {
enext =
isLeft
(V[i], V[i+1], P);
if ((eprev <= 0) &&
(enext > 0)) {
if (!below(P,
V[i], V[*rtan]))
*rtan = i;
}
else if ((eprev > 0) &&
(enext <= 0)) {
if (!above(P,
V[i], V[*ltan]))
*ltan = i;
}
eprev = enext;
}
return;
}
//===================================================================
//tangent_PointPolyC()
: binary search for convex polygon tangents
// Input: P = a 2D point (exterior to the
polygon)
// n
= number of polygon vertices
// V
= array of vertices for a 2D convex polygon with V[n]=V[0]
// Output: *rtan = index of rightmost tangent point V[*rtan]
// *ltan
= index of leftmost tangent point V[*ltan]
void
tangent_PointPolyC( Point P, int n, Point* V, int* rtan, int* ltan )
{
*rtan =
Rtangent_PointPolyC
(P, n,V);
*ltan =
Ltangent_PointPolyC
(P, n,V);
}
//Rtangent_PointPolyC()
: binary search for convex polygon right tangent
// Input: P = a 2D point (exterior to the
polygon)
// n
= number of polygon vertices
// V
= array of vertices for a 2D convex polygon with V[n]=V[0]
// Return: index "i" of rightmost tangent point V[i]
int
Rtangent_PointPolyC( Point P, int n, Point* V )
{
// use binary search for large convex polygons
int a, b, c;
// indices for edge chain endpoints
int upA, dnC;
// test for up direction of edges a and c
// rightmost tangent = maximum for the
isLeft
ordering
// test if V[0] is a local maximum
if (below(P,V[1],V[0]) && !above(P,V[n-1],V[0]))
return 0;
// V[0] is the maximum tangent point
for (a=0, b=n;;) {
// start chain = [0,n] with V[n]=V[0]
c = (a + b) / 2;
// midpoint of [a,b], and 0<c<n
dnC = below(P,V[c+1],V[c]);
if (dnC && !above(P,V[c-1],V[c]))
return
c; // V[c] is the maximum
tangent point
// no max yet, so continue with
the binary search
// pick one of the two subchains
[a,c] or [c,b]
upA = above(P,V[a+1],V[a]);
if (upA) {
// edge a points up
if (dnC)
// edge c points down
b = c;
// select [a,c]
else
{
// edge c points up
if (above(P,V[a],V[c])) // V[a] above
V[c]
b = c;
// select [a,c]
else
// V[a] below V[c]
a = c;
// select [c,b]
}
}
else {
// edge a points down
if (!dnC)
// edge c points up
a = c;
// select [c,b]
else
{
// edge c points down
if (below(P,V[a],V[c])) // V[a] below
V[c]
b = c;
// select [a,c]
else
// V[a] above V[c]
a = c;
// select [c,b]
}
}
}
}
//Ltangent_PointPolyC()
: binary search for convex polygon left tangent
// Input: P = a 2D point (exterior to the
polygon)
// n
= number of polygon vertices
// V
= array of vertices for a 2D convex polygon with V[n]=V[0]
// Return: index "i" of leftmost tangent point V[i]
int
Ltangent_PointPolyC( Point P, int n, Point* V )
{
// use binary search for large convex polygons
int a, b, c;
// indices for edge chain endpoints
int dnA, dnC;
// test for down direction of edges a and c
// leftmost tangent = minimum for the
isLeft
ordering
// test if V[0] is a local minimum
if (above(P,V[n-1],V[0]) && !below(P,V[1],V[0]))
return 0;
// V[0] is the minimum tangent point
for (a=0, b=n;;) {
// start chain = [0,n] with V[n]=V[0]
c = (a + b) / 2;
// midpoint of [a,b], and 0<c<n
dnC = below(P,V[c+1],V[c]);
if (above(P,V[c-1],V[c]) &&
!dnC)
return
c; // V[c] is the minimum
tangent point
// no min yet, so continue with
the binary search
// pick one of the two subchains
[a,c] or [c,b]
dnA = below(P,V[a+1],V[a]);
if (dnA) {
// edge a points down
if (!dnC)
// edge c points up
b = c;
// select [a,c]
else
{
// edge c points down
if (below(P,V[a],V[c])) // V[a] below
V[c]
b = c;
// select [a,c]
else
// V[a] above V[c]
a = c;
// select [c,b]
}
}
else {
// edge a points up
if (dnC)
// edge c points down
a = c;
// select [c,b]
else
{
// edge c points up
if (above(P,V[a],V[c])) // V[a] above
V[c]
b = c;
// select [a,c]
else
// V[a] below V[c]
a = c;
// select [c,b]
}
}
}
}
//===================================================================
//RLtangent_PolyPolyC()
: get the RL tangent between two convex polygons
// Input: m = number of vertices in polygon 1
// V
= array of vertices for convex polygon 1 with V[m]=V[0]
// n
= number of vertices in polygon 2
// W
= array of vertices for convex polygon 2 with W[n]=W[0]
// Output: *t1 = index of tangent point V[t1] for polygon
1
// *t2
= index of tangent point W[t2] for polygon 2
void
RLtangent_PolyPolyC( int m, Point* V, int n, Point* W, int* t1, int* t2
)
{
int ix1, ix2; // search
indices for polygons 1 and 2
// first get the initial vertex on each polygon
ix1 =
Rtangent_PointPolyC
(W[0], m,V); // right tangent from W[0] to V
ix2 =
Ltangent_PointPolyC
(V[ix1], n,W); // left tangent from V[ix1] to W
// ping-pong linear search until it stabilizes
int done = FALSE;
// flag when done
while (done==FALSE) {
done = TRUE;
// assume done until...
while (
isLeft
(W[ix2], V[ix1], V[ix1+1]) <= 0){
++ix1;
// get Rtangent from W[ix2] to V
}
while (
isLeft
(V[ix1], W[ix2], W[ix2-1]) >= 0){
--ix2;
// get Ltangent from V[ix1] to W
done
= FALSE;
// not done if had to adjust this
}
}
*t1 = ix1;
*t2 = ix2;
return;
}
//===================================================================
// isLeft():
test if a point is Left|On|Right of an infinite line.
// Input: three points P0, P1, and P2
// Return: >0 for P2 left of the line through P0 and
P1
// =0
for P2 on the line
// <0
for P2 right of the line
//
isLeft( Point P0, Point P1, Point P2 )
{
return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y
- P0.y);
}
// simpleHull_2D():
// Input: V[] = polyline array of 2D vertex
points
//
n = the number of points in V[]
// Output: H[] = output convex hull array of vertices
(max is n)
// Return: h = the number of points in H[]
int
simpleHull_2D( Point* V, int n, Point* H )
{
// initialize a deque D[] from bottom to top so that
the
// 1st three vertices of V[] are a counterclockwise
triangle
Point* D = new Point[2*n+1];
int bot = n-2, top = bot+3; // initial bottom
and top deque indices
D[bot] = D[top] = V[2];
// 3rd vertex is at both bot and top
if (
isLeft
(V[0], V[1], V[2]) > 0) {
D[bot+1] = V[0];
D[bot+2] = V[1];
// ccw vertices are: 2,0,1,2
}
else {
D[bot+1] = V[1];
D[bot+2] = V[0];
// ccw vertices are: 2,1,0,2
}
// compute the hull on the deque D[]
for (int i=3; i < n; i++) { // process
the rest of vertices
// test if next vertex is inside
the deque hull
if ((
isLeft
(D[bot], D[bot+1], V[i]) > 0) &&
(
isLeft
(D[top-1], D[top], V[i]) > 0) )
continue; // skip an interior
vertex
// incrementally add an exterior
vertex to the deque hull
// get the rightmost tangent
at the deque bot
while (
isLeft
(D[bot], D[bot+1], V[i]) <= 0)
++bot;
// remove bot of deque
D[--bot] = V[i];
// insert V[i] at bot of deque
// get the leftmost tangent
at the deque top
while (
isLeft
(D[top-1], D[top], V[i]) <= 0)
--top;
// pop top of deque
D[++top] = V[i];
// push V[i] onto top of deque
}
// transcribe deque D[] to the output hull array H[]
int h; //
hull vertex counter
for (h=0; h <= (top-bot); h++)
H[h] = D[bot + h];
delete D;
return h-1;
}
|
|