home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Current Shareware 1994 January
/
SHAR194.ISO
/
graphuti
/
polyops.zip
/
POLYCALC.C
< prev
next >
Wrap
Text File
|
1991-07-19
|
27KB
|
871 lines
/* Partial implementation in C of the program described in "An algorithm
for computing the union,intersection and difference of two polygons",
A. Margalit and G. D. Knott, Comput. & Graphics, 13, 2, 167-183, 1989.
Only the case of simple, regular curves is handled.
Programmed by:
B. J. Ball, 3304 Glen Rose Drive, Austin, Texas 78731 (CompuServe 71141,2321)
*/
#include <stdio.h>
#include <math.h>
#define MAXPTS 100 /* maximum size of ANY polygon used, not just input */
#define MAXX 1.1 /* greater than any permissible x-coordinate */
#define MAXCOMPS 20 /* maximum number of output polygons allowed */
#define MIN(a,b) (a) < (b) ? (a) : (b)
#define MAX(a,b) (a) > (b) ? (a) : (b)
typedef struct {double x; double y;} point;
typedef struct {point first; point second;} segment;
typedef struct {int nverts; double x[MAXPTS]; double y[MAXPTS];} polygon;
typedef struct {point v; int class; int next;} ventry; /* for vertex rings */
typedef struct {point p1; point p2; int del;} fentry; /* for frags list */
/* function prototypes */
void
changeorientation(polygon *),
consolidate(int *,double x[],double y[]),
initializeAV_BV(void),
insertAV(point,int,int),
insertBV(point,int,int),
insertE(point,point);
int
build_poly(int),
cross_check(point *,segment,segment),
equal(point,point),
equalflt(double,double),
findintersection(segment *,segment,segment),
findposA(point,segment,int),
findposB(point,segment,int),
gt(double,double),
gte(double,double),
inside(polygon,point),
onseg(point,segment,double *),
orientation(polygon),
polygonsoperation(void),
simple(polygon);
/* prompt() is part of the demo program, used here to display error messages;
it should be replaced or rewritten if polycalc.c is used independently. */
extern int prompt(int, int, int, char *);
ventry AV[MAXPTS],BV[MAXPTS]; /* linked lists of vertices and isects */
fentry EF[MAXPTS]; /* edge fragment list */
polygon outpoly[MAXCOMPS]; /* program output */
int ptype[MAXCOMPS]; /* types of the output polygons */
int numcomps; /* number of output polygons */
int skip[MAXPTS]; /* flags used in build_poly() function */
polygon A,B; /* input polygons */
int oper; /* operation: 0,1,2,3 = intersection,union,A-B,B-A */
int Atype=0, Btype=0; /* 0 = "island", 1 = "hole" */
int AVsize, BVsize, EFsize; /* current sizes of AV,BV,EF arrays */
double roundoff = .0000001; /* for checking equality of float calculations */
/* Specialized arrays used for distinguishing various cases */
int polygonsorientation[2][2][4] =
{{{1,1,-1,-1},{-1,-1,1,1}},{{-1,-1,1,1},{1,1,-1,-1}}};
int fragmenttype[2][2][4][2] =
{{{{1,1},{-1,-1},{-1,1},{1,-1}},{{-1,-1},{1,-1},{1,1},{-1,-1}}},
{{{1,-1},{-1,1},{-1,-1},{1,1}},{{-1,-1},{1,1},{1,-1},{-1,1}}}};
int resultorientation[2][2][4] =
{{{1,1,1,-1},{1,-1,1,1}},{{-1,1,1,1},{1,1,-1,1}}};
/* ====================================================================== */
/* principal routine */
/* ====================================================================== */
/*
Input: polygons A,B
int oper (operation: 0=intersection, 1=union, 2=A-B, 3=B-A)
Output: int numcomp = number of output components
polygon outpoly[numcomps] = array of output polygons
int ptype[numcomps] = array giving type of each output polygon
*/
polygonsoperation(void)
{
int orientationA, orientationB;
int i,i1,j,j1,k,type,t1,t2,cnt,k1,k2,err_flag=0;
double u1,u2;
segment a,b,c,tmp;
point m,p1,p2;
orientationA = orientation(A);
orientationB = orientation(B);
if (polygonsorientation[Atype][Btype][oper] == 1)
{
if (orientationA != orientationB)
changeorientation(&B);
}
else if (orientationA == orientationB)
changeorientation(&B);
initializeAV_BV();
/* find intersections of sides of A with sides of B, insert in VA and VB */
for (i=0; i<A.nverts; i++)
{
i1 = (i+1) % A.nverts;
a.first.x = AV[i].v.x, a.first.y = AV[i].v.y;
a.second.x = AV[i1].v.x, a.second.y = AV[i1].v.y;
for (j=0; j<B.nverts; j++)
{
j1 = (j+1) % B.nverts;
b.first.x = BV[j].v.x, b.first.y = BV[j].v.y;
b.second.x = BV[j1].v.x, b.second.y = BV[j1].v.y;
k = findintersection(&c,a,b);
if (k==1) /* only one point of intersection */
{
k1 = findposA(c.first,a,i); /* last pt. of AV[] <= c.first on a */
insertAV(c.first,0,k1); /* insert c.first here */
k1 = findposB(c.first,b,j); /* repeat for BV */
insertBV(c.first,0,k1);
}
else if (k==2) /* two points of intersection */
{
/* set p1,p2 to c.first,c.second in order on a */
onseg(c.first,a,&u1); /* u1,u2 are coordinates of.. */
onseg(c.second,a,&u2); /* .. c.first,c.second on segment a */
if (gt(u2,u1))
{
p1.x = c.first.x, p1.y = c.first.y;
p2.x = c.second.x, p2.y = c.second.y;
}
else
{
p2.x = c.first.x, p2.y = c.first.y;
p1.x = c.second.x, p1.y = c.second.y;
}
k1 = findposA(p1,a,i); /* last point of AV[] <= p1 on a */
if (equal(AV[k1].v,p1)) /* p1,p2 should follow in order */
insertAV(p2,0,k1);
else
{
insertAV(p1,0,k1); /* set AV[AVsize] = p1, AVsize++ */
insertAV(p2,0,AVsize-1); /* next position following p1 */
}
/* repeat above procedure for B */
onseg(c.first,b,&u1); /* u1,u2 are coordinates of.. */
onseg(c.second,b,&u2); /* .. c.first,c.second on segment b */
if (gt(u2,u1))
{
p1.x = c.first.x, p1.y = c.first.y;
p2.x = c.second.x, p2.y = c.second.y;
}
else
{
p2.x = c.first.x, p2.y = c.first.y;
p1.x = c.second.x, p1.y = c.second.y;
}
k1 = findposB(p1,b,j); /* last point of BV[] <= p1 on a */
if (equal(BV[k1].v,p1)) /* p1,p2 should follow in order */
insertBV(p2,0,k1);
else
{
insertBV(p1,0,k1); /* set BV[AVsize] = p1, BVsize++ */
insertBV(p2,0,BVsize-1); /* next position following p1 */
}
}
}
}
/* select edge fragments and insert into EF[] */
type = fragmenttype[Atype][Btype][oper][0]; /* last index specifies A */
i = 0; j = AV[i].next; cnt=0;
while (cnt < AVsize)
{
t1 = AV[i].class;
t2 = AV[j].class;
if (t1 == type || t2 == type)
insertE(AV[i].v,AV[j].v);
else if (t1==0 && t2==0)
{
m.x = (AV[i].v.x + AV[j].v.x)/2;
m.y = (AV[i].v.y + AV[j].v.y)/2;
k = inside(B,m);
if (k==type || k==0)
insertE(AV[i].v,AV[j].v);
}
i = j; j = AV[i].next; cnt++;
}
type = fragmenttype[Atype][Btype][oper][1]; /* last index specifies B */
i = 0; j = BV[i].next; cnt = 0;
while (cnt < BVsize)
{
t1 = BV[i].class;
t2 = BV[j].class;
if (t1 == type || t2 == type)
insertE(BV[i].v,BV[j].v);
if (t1==0 && t2==0)
{
m.x = (BV[i].v.x + BV[j].v.x)/2;
m.y = (BV[i].v.y + BV[j].v.y)/2;
k = inside(A,m);
if (k==type || k==0)
insertE(BV[i].v,BV[j].v);
}
i = j; j = BV[i].next; cnt++;
}
/* arrange edge fragments into polygons */
k = 0;
while ((err_flag = build_poly(k)) > 0)
{
i = orientation(outpoly[k]);
if (i == orientationA)
{
if (resultorientation[Atype][Btype][oper] == 1)
ptype[k] = Atype;
else
ptype[k] = 1-Atype;
}
else
{
if (resultorientation[Atype][Btype][oper] == 1)
ptype[k] = 1-Atype;
else
ptype[k] = Atype;
}
k++;
if (k > MAXCOMPS)
{
err_flag = -1;
prompt(32,192,15,
"Too many components in result. Press any key to continue");
getch();
break;
}
}
numcomps = k; /* number of output polygons found */
if (err_flag)
return -1; /* build_poly failed */
else
return numcomps;
}
/* ------------------ initializeAV_BV() ----------------------------------- */
/* classify original vertices and insert them in vertex rings AV[],BV[] */
void
initializeAV_BV(void)
{
int i,n;
point p;
n = A.nverts;
for (i=0; i<n; i++)
{
p.x = AV[i].v.x = A.x[i];
p.y = AV[i].v.y = A.y[i];
AV[i].class = inside(B,p);
AV[i].next = i+1;
}
AV[n-1].next = 0.0; /* close up ring */
AVsize = n;
n = B.nverts;
for (i=0; i<n; i++)
{
p.x = BV[i].v.x = B.x[i];
p.y = BV[i].v.y = B.y[i];
BV[i].class = inside(A,p);
BV[i].next = i+1;
}
BV[n-1].next = 0.0; /* close up ring */
BVsize = n;
}
/* ======================================================================== */
/* Principal supporting routines, listed alphabetically */
/* ======================================================================== */
/* --------------- construct a polygon from boundary fragments ------------- */
/* fills outpoly[k], marks points used */
int
build_poly(int k)
{
int i,j,j1,nverts,match_found;
point u0,u1; /* u0 = first vertex of outpoly, u1 = current last vertex */
for (i=0; i<EFsize; i++)
{
if (skip[i] || EF[i].del) continue;
skip[i] = 1;
outpoly[k].x[0] = u0.x = EF[i].p1.x;
outpoly[k].y[0] = u0.y = EF[i].p1.y; /* u0 = EF[i].p1 */
outpoly[k].x[1] = u1.x = EF[i].p2.x;
outpoly[k].y[1] = u1.y = EF[i].p2.y; /* u1 = EF[i].p2 */
nverts = 2; /* number of vertices added to outpoly[k] so far */
do
{
match_found = 0; /* continue flag */
j1 = 0; /* index of matching seg */
for (j=i+1; j<EFsize; j++)
{
if (skip[j] || EF[j].del) continue;
if (equal(EF[j].p1,u1)) /* if EF[j].p1 = u1, then.. */
{
j1 = j;
if (equal(EF[j].p2,u0)) /* if back at starting point ... */
{
skip[j] = 1;
outpoly[k].nverts = nverts; /* .. polygon completed */
/* eliminate collinear sequences of vertices */
consolidate(&outpoly[k].nverts,outpoly[k].x,outpoly[k].y);
return 1;
}
}
}
if (!j1)
{
prompt(32,192,15,
"Computation failed - press any key to continue");
getch();
goto err_exit;
}
skip[j1] = 1;
u1.x = EF[j1].p2.x, u1.y = EF[j1].p2.y; /* ..set u1 = EF[j].p2 */
match_found = 1; /* continue flag */
outpoly[k].x[nverts] = u1.x;
outpoly[k].y[nverts] = u1.y;
nverts++;
} while (nverts <= 2*MAXPTS && match_found);
if (nverts > 2*MAXPTS)
{
prompt(32,192,15,
"Too many sides in one component. Press any key to continue");
getch();
goto err_exit;
}
}
return 0;
err_exit:
return -1;
}
/* ---------------- change orientation ------------------------------- */
/* (reverse order of vertices of polygon) */
void
changeorientation(polygon *pgn)
{
int i,j,n = pgn->nverts;
double tempx[MAXPTS], tempy[MAXPTS];
for (i=0; i<n; i++)
{
tempx[i] = pgn->x[i];
tempy[i] = pgn->y[i];
}
for (i=0,j=n-1; i<n; i++,j--)
{
pgn->x[i] = tempx[j];
pgn->y[i] = tempy[j];
}
}
/* --------------------------------------------------------------------- */
/* collinear() returns 1 if the input points are collinear, 0 otherwise */
int
collinear(double a1, double a2, double b1, double b2, double c1, double c2)
{
if (fabs((a1-b1)*(a2-c2) - (a1-c1)*(a2-b2)) > roundoff)
return 0;
else
return 1;
}
/* ----------------------------------------------------------------------- */
/* routine for eliminating collinear sequences of points from x[0],...x[n] */
/* resets n to new value if necessary */
void
consolidate(int *n, double x[], double y[])
{
int i=0,j,m=*n;
while (i < m-2)
{
if (!collinear(x[i],y[i],x[i+1],y[i+1], x[i+2],y[i+2]))
i++;
else
{
for (j=i+1; j<m-1; j++) /* delete middle point, (x[i+1],y[i+1]) */
x[j]=x[j+1], y[j]=y[j+1];
m--;
}
}
if (m > 2 && collinear(x[m-2],y[m-2],x[m-1],y[m-1],x[0],y[0]))
m--; /* delete last point, if necessary */
*n = m; /* change original value of n to the calculated value */
}
/* ---------------- cross_check(point *,segment,segment) ------------------- */
/* returns -1 if segments a and b are parallel, 0 if they do not intersect,
and 1 if the intersection is a single point. In this last case, the point
of intersection is returned in p. This routine is from "A Programmer's
Geometry", Bowyer and Woodwark, London, 1988. */
int
cross_check(point *p, segment a, segment b)
{
double xlk = a.second.x - a.first.x, ylk = a.second.y - a.first.y;
double xnm = b.second.x - b.first.x, ynm = b.second.y - b.first.y;
double xmk = b.first.x - a.first.x, ymk = b.first.y - a.first.y;
double det,detinv,s,t,x,y;
det = xnm*ylk - ynm*xlk;
if (fabs(det) < roundoff)
return -1;
detinv = 1.0/det;
s = (xnm*ymk - ynm*xmk)*detinv;
if (gt(0,s) || gt(s,1))
return 0;
t = (xlk*ymk - ylk*xmk)*detinv;
if (gt(0,t) || gt(t,1))
return 0;
p->x = a.first.x + xlk*s;
p->y = a.first.y + ylk*s;
return 1;
}
/* ---------------- find intersection of two segments ----------------------- */
/* Input: segment a, segment b, segment *c.
Returns 0 if the intersection of a and b is empty, 1 if it is a single point
and 2 if it is a nondegenerate segment. In the latter two cases, the segment
c is set to the intersection of a and b. */
int
findintersection(segment *c, segment a, segment b)
{
int i,cnt;
point p[2];
double tmp; /* dummy variable for onseg() routine */
/* run simple box tests first */
if (gt(MIN(b.first.x,b.second.x),MAX(a.first.x,a.second.x)))
return 0;
if (gt(MIN(a.first.x,a.second.x),MAX(b.first.x,b.second.x)))
return 0;
if (gt(MIN(b.first.y,b.second.y),MAX(a.first.y,a.second.y)))
return 0;
if (gt(MIN(a.first.y,a.second.y),MAX(b.first.y,b.second.y)))
return 0;
/* if box tests do not rule out intersection, use cross_check() routine */
i = cross_check(&p[0],a,b); /* test for crossing segments */
if (i==0) /* no intersection */
return 0;
if (i==1) /* only one point of intersection */
{
c->first.x = c->second.x = p[0].x;
c->first.y = c->second.y = p[0].y;
return 1;
}
cnt = 0; /* check endpoints, quit when two intersections found */
if (onseg(a.first,b,&tmp))
{
p[cnt].x = a.first.x;
p[cnt].y = a.first.y;
cnt++;
}
if (onseg(a.second,b,&tmp))
{
p[cnt].x = a.second.x;
p[cnt].y = a.second.y;
cnt++;
if (cnt>1)
goto b;
}
if (onseg(b.first,a,&tmp))
{
p[cnt].x = b.first.x;
p[cnt].y = b.first.y;
cnt++;
if (cnt>1)
goto b;
}
if (onseg(b.second,a,&tmp))
{
p[cnt].x = b.second.x;
p[cnt].y = b.second.y;
cnt++;
if (cnt>1)
goto b;
}
if (!cnt)
return 0;
else
{
c->first.x = c->second.x = p[0].x;
c->first.y = c->second.y = p[0].y;
return 1;
}
b: c->first.x = p[0].x, c->first.y = p[0].y;
c->second.x = p[1].x, c->second.y = p[1].y;
return 2;
}
/* ---------------- find order of added vertices on polygon sides --------- */
/* Input: point p, segment a containing p, integer j; for findposA, a = AV[j]
and for findposB(), p = BV[j]. Return the index of the last point in AV[]
or BV[], respectively, which does not follow p on segment a. */
int
findposA(point p, segment a, int j)
{
int i,i0;
double t,t0,tmp;
if (!onseg(p,a,&t))
{
textcolor(15);
cputs("internal error - cannot continue");
getch();
return -1;
}
i0 = j; t0 = 0.0;
for (i=0; i<AVsize; i++)
{
if (!onseg(AV[i].v,a,&tmp)) continue;
if (equalflt(tmp,t))
{
i0=i;
break;
}
if (gt(tmp,t0) && gt(t,tmp)) /* t0 < tmp < t */
t0 = tmp, i0 = i;
}
return i0;
}
/* ---------------------------------- */
int
findposB(point p, segment a, int j)
{
int i,i0;
double t,t0,tmp;
if (!onseg(p,a,&t))
{
textcolor(15);
cputs("internal error - cannot continue");
getch();
return -1;
}
i0 = j; t0 = 0.0;
for (i=0; i<BVsize; i++)
{
if (!onseg(BV[i].v,a,&tmp)) continue;
if (equalflt(tmp,t))
{
i0=i;
break;
}
if (gt(tmp,t0) && gt(t,tmp)) /* t0 < tmp < t */
t0 = tmp, i0 = i;
}
return i0;
}
/* ---------------- insertAV(point,type,k) -------------------------------- */
/* add point at end of AV, then change links so new point follows current k */
void
insertAV(point p, int type, int k)
{
int i,n;
/* check on current appearances of p in AV[] */
for (i=0; i<AVsize; i++)
if (equal(AV[i].v, p))
return; /* a point can appear at most once */
n = AVsize;
AVsize++;
AV[n].v.x = p.x, AV[n].v.y = p.y;
AV[n].next = AV[k].next;
AV[n].class = type;
AV[k].next = n;
}
/* ---------------- insertBV(point,type,k) -------------------------------- */
/* add point at end of BV, then change links so new point follows current k */
void
insertBV(point p, int type, int k)
{
int i,n;
/* check on current appearances of p in BV[] */
for (i=0; i<BVsize; i++)
if (equal(BV[i].v, p))
return; /* a point can appear at most once */
n = BVsize;
BVsize++;
BV[n].v.x = p.x, BV[n].v.y = p.y;
BV[n].next = BV[k].next;
BV[n].class = type;
BV[k].next = n;
}
/* ---------------- insertE(point,point) ---------------------------------- */
/* add segment to list of edge fragments if it is not already there, except */
/* if the inverse of the segment in in EF, delete it and don't insert seg */
/* (if non-regular output is to be allowed, this routine must be modified) */
void
insertE(point a, point b)
{
int i,k1,k2;
/* check for presence of segment ab or ba in EF[] */
for (i=0; i<EFsize; i++)
{
k1 = ((equal(EF[i].p1,a) && equal(EF[i].p2,b)));
if (k1) /* ab found in EF[] */
return;
k2 = (equal(EF[i].p1,b) && equal(EF[i].p2,a));
if (k2) /* ba found in EF[] */
{
EF[i].del = 1;
return;
}
}
EF[EFsize].p1.x = a.x, EF[EFsize].p1.y = a.y;
EF[EFsize].p2.x = b.x, EF[EFsize].p2.y = b.y;
EFsize++;
}
/* ---------------------- inside(polygon,point) -------------------------- */
/* returns -1,0,1 according as point p is outside, on boundary, inside pgn */
int
inside(polygon pgn, point p)
{
int i,j,k,cnt;
double y1,tmp; /* tmp is dummy variable for onseg() routine */
point p1,p2;
segment a,b,c;
/* set segment a to start at p and go horizontally right beyond pgn */
a.first.x = p.x, a.first.y = a.second.y = p.y; a.second.x = MAXX;
/* check intersection of segment a with each side of pgn */
cnt = 0;
for (i=0; i<pgn.nverts; i++)
{
b.first.x = pgn.x[i], b.first.y = pgn.y[i];
j = (i+1) % pgn.nverts;
b.second.x = pgn.x[j], b.second.y = pgn.y[j];
if (onseg(p,b,&tmp))
return 0;
if (gt(p.x,MAX(b.first.x,b.second.x))
|| gt(p.y,MAX(b.first.y,b.second.y))
|| gt(MIN(b.first.y,b.second.y),p.y))
continue; /* b does not intersect a */
k = findintersection(&c,a,b);
if (k)
{
y1 = MIN(b.first.y, b.second.y);
if (!equalflt(c.first.y,y1) && !equalflt(c.second.y,y1))
cnt++;
}
}
if (cnt %2)
return 1;
else
return -1;
}
/* ----------------------- onseg(p,s,&t) ---------------------------------- */
/* returns 0 if point is not on segment; otherwise returns 1 and sets t to
the coordinate of p on s; i.e., p = (s.first) + t*(s.second) */
int
onseg(point p, segment seg, double *t)
{
double x1=seg.first.x, y1=seg.first.y, x2=seg.second.x, y2=seg.second.y;
double x=p.x, y=p.y;
double temp;
if (equalflt(x1,x2)) /* vertical segment */
{
if (!equalflt(x,x1))
return 0;
if (equalflt(y1,y2)) /* degenerate segment */
{
if (equalflt(y,y1)) return 1;
else return 0;
}
temp = (y-y1)/(y2-y1);
goto a;
}
if (equalflt(y1,y2)) /* horizontal segment */
{
if (!equalflt(y,y1))
return 0;
if (equalflt(x1,x2)) /* degenerate segment */
{
if (equalflt(x,x1)) return 1;
else return 0;
}
temp = (x-x1)/(x2-x1);
goto a;
}
if (!equalflt((x-x1)*(y2-y1),(y-y1)*(x2-x1)))
return 0;
temp = (x-x1)/(x2-x1);
a:
*t = temp;
if (gte(temp,0.0) && gte(1.0,temp)) /* if 0 <= temp <= 1, p is on seg */
return 1;
else
return 0;
}
/* ---------------------- orientation(polygon) ----------------------------- */
/* returns +1 for counterclockwise orientation, -1 for clockwise */
/* (the routine described in the Margalit-Knott paper does not work in all */
/* situations which the present program presents to it; there is a kludge */
/* here which makes things all right in the current program but still fails */
/* for non-regular polygons. Clearly the authors had something in mind other */
/* than what is given in the paper, but it is not clear exactly what the */
/* routine ought to be in the general case. Surely not this ugly mess.) */
int
orientation(polygon pgn)
{
point p,q,r;
int i,j,k,l,m,n=pgn.nverts,i0,i1,j0=0,temp;
if (n<=2) return 0; /* empty or degenerate polygon */
/* find a vertex of pgn with minimum x-coordinate */
for (j=0; j<n; j++)
if (pgn.x[j] < pgn.x[j0])
j0 = j;
q.x = pgn.x[j0], q.y = pgn.y[j0]; /* q is a left-most vertex of pgn */
if (j0>0)
i0 = j0-1;
else
i0 = n-1; /* i0 is the index of an immediate predecessor of q on pgn */
for (i=0; i<n-1; i++)
{
i1 = (i+1)%n;
if (pgn.x[i1]==q.x && pgn.y[i+1]==q.y && pgn.x[i]<pgn.x[i0])
i0 = i;
}
i = i0, j = (i+1)%n, k = (j+1) % n;
/* now i is the index of the left-most immediate predecessor of q,
j is an index of q in pgn, and
k is the index of the next point on pgn */
p.x = pgn.x[i], p.y = pgn.y[i];
r.x = pgn.x[k], r.y = pgn.y[k];
/* if either of the segments pq, qr is vertical, the orientation is obvious */
if (equalflt(p.x, q.x))
if (gt(p.y,q.y))
return 1;
else
return -1;
if (equalflt(q.x, r.x))
if (gt(q.y,r.y))
return 1;
else
return -1;
/* otherwise, calculate the orientation from the slopes of pq and qr */
if (gt((p.y-q.y)/(p.x-q.x), (q.y-r.y)/(q.x-r.x)))
temp = 1;
else
temp = -1;
/* if q is an interior point of a side other than pq and qp, that side must
be vertical; if this situation obtains, the calculation just made is wrong.
Check it out. */
/* since this routine was written, the build_poly() function has been changed
so that the situation envisioned here should not occur. However ... */
for (l=0; l<n; l++)
{
if (l==i || l==j || l==k) continue;
if (!equalflt(pgn.x[l], q.x)) continue;
m = (l+1) % n;
if (!equalflt(pgn.x[m], q.x)) continue;
if ((gt(q.y,pgn.y[l]) && gt(pgn.y[m],q.y))
|| (gt(q.y,pgn.y[m]) && gt(pgn.y[l],q.y)))
return -temp;
}
return temp;
}
/* ----------------------------------------------------------------------- */
/* check input polygons for simplicity */
int
simple(polygon pgn)
{
int i,i1,j,j1,n=pgn.nverts;
segment c, seg[MAXPTS];
for (i=0; i<n; i++)
{
i1 = (i+1)%n;
seg[i].first.x = pgn.x[i];
seg[i].first.y = pgn.y[i];
seg[i].second.x = pgn.x[i1];
seg[i].second.y = pgn.y[i1];
}
for (j=2; j<n-1; j++)
if (findintersection(&c,seg[0],seg[j]))
return 0;
for (i=1; i<n-2; i++)
for (j=i+2; j<n; j++)
if (findintersection(&c,seg[i],seg[j]))
return 0;
return 1;
}
/* ======================================================================= */
/* Miscellaneous floating point utilities */
/* ======================================================================= */
/* the value of 'roundoff' might have to be adjusted for some applications */
/* check two doubles for equality */
int
equalflt(double x, double y)
{
return (fabs(x-y)<roundoff);
}
/* check for x > y */
int
gt(double x, double y)
{
return (x-y > roundoff);
}
/* check for x >= y */
int
gte(double x, double y)
{
return (gt(x,y) + equalflt(x,y));
}
/* check two points for equality */
int
equal(point a, point b)
{
return ((fabs(a.x-b.x)<roundoff) && (fabs(a.y-b.y)<roundoff));
}