#include "gpc.h"
#include <stdlib.h>
#include <float.h>
#include <math.h>
#ifndef TRUE
#define FALSE 0
#define TRUE 1
#endif
#define LEFT 0
#define RIGHT 1
#define ABOVE 0
#define BELOW 1
#define CLIP 0
#define SUBJ 1
#define INVERT_TRISTRIPS FALSE
#define EQ(a, b) (fabs((a) - (b)) <= GPC_EPSILON)
#define PREV_INDEX(i, n) ((i - 1 + n) % n)
#define NEXT_INDEX(i, n) ((i + 1 ) % n)
#define OPTIMAL(v, i, n) ((v[PREV_INDEX(i, n)].y != v[i].y) || \
(v[NEXT_INDEX(i, n)].y != v[i].y))
#define FWD_MIN(v, i, n) ((v[PREV_INDEX(i, n)].vertex.y >= v[i].vertex.y) \
&& (v[NEXT_INDEX(i, n)].vertex.y > v[i].vertex.y))
#define NOT_FMAX(v, i, n) (v[NEXT_INDEX(i, n)].vertex.y > v[i].vertex.y)
#define REV_MIN(v, i, n) ((v[PREV_INDEX(i, n)].vertex.y > v[i].vertex.y) \
&& (v[NEXT_INDEX(i, n)].vertex.y >= v[i].vertex.y))
#define NOT_RMAX(v, i, n) (v[PREV_INDEX(i, n)].vertex.y > v[i].vertex.y)
#define VERTEX(e,p,s,x,y) {add_vertex(&((e)->outp[(p)]->v[(s)]), x, y); \
(e)->outp[(p)]->active++;}
#define P_EDGE(d,e,p,i,j) {(d)= (e); \
do {(d)= (d)->prev;} while (!(d)->outp[(p)]); \
(i)= (d)->bot.x + (d)->dx * ((j)-(d)->bot.y);}
#define N_EDGE(d,e,p,i,j) {(d)= (e); \
do {(d)= (d)->next;} while (!(d)->outp[(p)]); \
(i)= (d)->bot.x + (d)->dx * ((j)-(d)->bot.y);}
#define MALLOC(p, b, s, t) {if ((b) > 0) { \
p= (t*)malloc(b); if (!(p)) { \
fprintf(stderr, "gpc malloc failure: %s\n", s); \
exit(0);}} else p= NULL;}
#define FREE(p) {if (p) {free(p); (p)= NULL;}}
typedef enum
{
NUL,
EMX,
ELI,
TED,
ERI,
RED,
IMM,
IMN,
EMN,
EMM,
LED,
ILI,
BED,
IRI,
IMX,
FUL
} vertex_type;
typedef enum
{
NH,
BH,
TH
} h_state;
typedef enum
{
UNBUNDLED,
BUNDLE_HEAD,
BUNDLE_TAIL
} bundle_state;
typedef struct v_shape
{
double x;
double y;
struct v_shape *next;
} vertex_node;
typedef struct p_shape
{
int active;
int hole;
vertex_node *v[2];
struct p_shape *next;
struct p_shape *proxy;
} polygon_node;
typedef struct edge_shape
{
gpc_vertex vertex;
gpc_vertex bot;
gpc_vertex top;
double xb;
double xt;
double dx;
int type;
int bundle[2][2];
int bside[2];
bundle_state bstate[2];
polygon_node *outp[2];
struct edge_shape *prev;
struct edge_shape *next;
struct edge_shape *pred;
struct edge_shape *succ;
struct edge_shape *next_bound;
} edge_node;
typedef struct lmt_shape
{
double y;
edge_node *first_bound;
struct lmt_shape *next;
} lmt_node;
typedef struct sbt_t_shape
{
double y;
struct sbt_t_shape *less;
struct sbt_t_shape *more;
} sb_tree;
typedef struct it_shape
{
edge_node *ie[2];
gpc_vertex point;
struct it_shape *next;
} it_node;
typedef struct st_shape
{
edge_node *edge;
double xb;
double xt;
double dx;
struct st_shape *prev;
} st_node;
typedef struct bbox_shape
{
double xmin;
double ymin;
double xmax;
double ymax;
} bbox;
const h_state next_h_state[3][6]=
{
{BH, TH, TH, BH, NH, NH},
{NH, NH, NH, NH, TH, TH},
{NH, NH, NH, NH, BH, BH}
};
static void reset_it(it_node **it)
{
it_node *itn;
while (*it)
{
itn= (*it)->next;
FREE(*it);
*it= itn;
}
}
static void reset_lmt(lmt_node **lmt)
{
lmt_node *lmtn;
while (*lmt)
{
lmtn= (*lmt)->next;
FREE(*lmt);
*lmt= lmtn;
}
}
static void insert_bound(edge_node **b, edge_node *e)
{
edge_node *existing_bound;
if (!*b)
{
*b= e;
}
else
{
if (e[0].bot.x < (*b)[0].bot.x)
{
existing_bound= *b;
*b= e;
(*b)->next_bound= existing_bound;
}
else
{
if (e[0].bot.x == (*b)[0].bot.x)
{
if (e[0].dx < (*b)[0].dx)
{
existing_bound= *b;
*b= e;
(*b)->next_bound= existing_bound;
}
else
{
insert_bound(&((*b)->next_bound), e);
}
}
else
{
insert_bound(&((*b)->next_bound), e);
}
}
}
}
static edge_node **bound_list(lmt_node **lmt, double y)
{
lmt_node *existing_node;
if (!*lmt)
{
MALLOC(*lmt, sizeof(lmt_node), "LMT insertion", lmt_node);
(*lmt)->y= y;
(*lmt)->first_bound= NULL;
(*lmt)->next= NULL;
return &((*lmt)->first_bound);
}
else
if (y < (*lmt)->y)
{
existing_node= *lmt;
MALLOC(*lmt, sizeof(lmt_node), "LMT insertion", lmt_node);
(*lmt)->y= y;
(*lmt)->first_bound= NULL;
(*lmt)->next= existing_node;
return &((*lmt)->first_bound);
}
else
if (y > (*lmt)->y)
return bound_list(&((*lmt)->next), y);
else
return &((*lmt)->first_bound);
}
static void add_to_sbtree(int *entries, sb_tree **sbtree, double y)
{
if (!*sbtree)
{
MALLOC(*sbtree, sizeof(sb_tree), "scanbeam tree insertion", sb_tree);
(*sbtree)->y= y;
(*sbtree)->less= NULL;
(*sbtree)->more= NULL;
(*entries)++;
}
else
{
if ((*sbtree)->y > y)
{
add_to_sbtree(entries, &((*sbtree)->less), y);
}
else
{
if ((*sbtree)->y < y)
{
add_to_sbtree(entries, &((*sbtree)->more), y);
}
}
}
}
static void build_sbt(int *entries, double *sbt, sb_tree *sbtree)
{
if (sbtree->less)
build_sbt(entries, sbt, sbtree->less);
sbt[*entries]= sbtree->y;
(*entries)++;
if (sbtree->more)
build_sbt(entries, sbt, sbtree->more);
}
static void free_sbtree(sb_tree **sbtree)
{
if (*sbtree)
{
free_sbtree(&((*sbtree)->less));
free_sbtree(&((*sbtree)->more));
FREE(*sbtree);
}
}
static int count_optimal_vertices(gpc_vertex_list c)
{
int result= 0, i;
if (c.num_vertices > 0)
{
for (i= 0; i < c.num_vertices; i++)
if (OPTIMAL(c.vertex, i, c.num_vertices))
result++;
}
return result;
}
static edge_node *build_lmt(lmt_node **lmt, sb_tree **sbtree,
int *sbt_entries, gpc_polygon *p, int type,
gpc_op op)
{
int c, i, min, max, num_edges, v, num_vertices;
int total_vertices= 0, e_index=0;
edge_node *e, *edge_table;
for (c= 0; c < p->num_contours; c++)
total_vertices+= count_optimal_vertices(p->contour[c]);
MALLOC(edge_table, total_vertices * sizeof(edge_node),
"edge table creation", edge_node);
for (c= 0; c < p->num_contours; c++)
{
if (p->contour[c].num_vertices < 0)
{
p->contour[c].num_vertices= -p->contour[c].num_vertices;
}
else
{
num_vertices= 0;
for (i= 0; i < p->contour[c].num_vertices; i++)
if (OPTIMAL(p->contour[c].vertex, i, p->contour[c].num_vertices))
{
edge_table[num_vertices].vertex.x= p->contour[c].vertex[i].x;
edge_table[num_vertices].vertex.y= p->contour[c].vertex[i].y;
add_to_sbtree(sbt_entries, sbtree,
edge_table[num_vertices].vertex.y);
num_vertices++;
}
for (min= 0; min < num_vertices; min++)
{
if (FWD_MIN(edge_table, min, num_vertices))
{
num_edges= 1;
max= NEXT_INDEX(min, num_vertices);
while (NOT_FMAX(edge_table, max, num_vertices))
{
num_edges++;
max= NEXT_INDEX(max, num_vertices);
}
e= &edge_table[e_index];
e_index+= num_edges;
v= min;
e[0].bstate[BELOW]= UNBUNDLED;
e[0].bundle[BELOW][CLIP]= FALSE;
e[0].bundle[BELOW][SUBJ]= FALSE;
for (i= 0; i < num_edges; i++)
{
e[i].xb= edge_table[v].vertex.x;
e[i].bot.x= edge_table[v].vertex.x;
e[i].bot.y= edge_table[v].vertex.y;
v= NEXT_INDEX(v, num_vertices);
e[i].top.x= edge_table[v].vertex.x;
e[i].top.y= edge_table[v].vertex.y;
e[i].dx= (edge_table[v].vertex.x - e[i].bot.x) /
(e[i].top.y - e[i].bot.y);
e[i].type= type;
e[i].outp[ABOVE]= NULL;
e[i].outp[BELOW]= NULL;
e[i].next= NULL;
e[i].prev= NULL;
e[i].succ= ((num_edges > 1) && (i < (num_edges - 1))) ?
&(e[i + 1]) : NULL;
e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : NULL;
e[i].next_bound= NULL;
e[i].bside[CLIP]= (op == GPC_DIFF) ? RIGHT : LEFT;
e[i].bside[SUBJ]= LEFT;
}
insert_bound(bound_list(lmt, edge_table[min].vertex.y), e);
}
}
for (min= 0; min < num_vertices; min++)
{
if (REV_MIN(edge_table, min, num_vertices))
{
num_edges= 1;
max= PREV_INDEX(min, num_vertices);
while (NOT_RMAX(edge_table, max, num_vertices))
{
num_edges++;
max= PREV_INDEX(max, num_vertices);
}
e= &edge_table[e_index];
e_index+= num_edges;
v= min;
e[0].bstate[BELOW]= UNBUNDLED;
e[0].bundle[BELOW][CLIP]= FALSE;
e[0].bundle[BELOW][SUBJ]= FALSE;
for (i= 0; i < num_edges; i++)
{
e[i].xb= edge_table[v].vertex.x;
e[i].bot.x= edge_table[v].vertex.x;
e[i].bot.y= edge_table[v].vertex.y;
v= PREV_INDEX(v, num_vertices);
e[i].top.x= edge_table[v].vertex.x;
e[i].top.y= edge_table[v].vertex.y;
e[i].dx= (edge_table[v].vertex.x - e[i].bot.x) /
(e[i].top.y - e[i].bot.y);
e[i].type= type;
e[i].outp[ABOVE]= NULL;
e[i].outp[BELOW]= NULL;
e[i].next= NULL;
e[i].prev= NULL;
e[i].succ= ((num_edges > 1) && (i < (num_edges - 1))) ?
&(e[i + 1]) : NULL;
e[i].pred= ((num_edges > 1) && (i > 0)) ? &(e[i - 1]) : NULL;
e[i].next_bound= NULL;
e[i].bside[CLIP]= (op == GPC_DIFF) ? RIGHT : LEFT;
e[i].bside[SUBJ]= LEFT;
}
insert_bound(bound_list(lmt, edge_table[min].vertex.y), e);
}
}
}
}
return edge_table;
}
static void add_edge_to_aet(edge_node **aet, edge_node *edge, edge_node *prev)
{
if (!*aet)
{
*aet= edge;
edge->prev= prev;
edge->next= NULL;
}
else
{
if (edge->xb < (*aet)->xb)
{
edge->prev= prev;
edge->next= *aet;
(*aet)->prev= edge;
*aet= edge;
}
else
{
if (edge->xb == (*aet)->xb)
{
if (edge->dx < (*aet)->dx)
{
edge->prev= prev;
edge->next= *aet;
(*aet)->prev= edge;
*aet= edge;
}
else
{
add_edge_to_aet(&((*aet)->next), edge, *aet);
}
}
else
{
add_edge_to_aet(&((*aet)->next), edge, *aet);
}
}
}
}
static void add_intersection(it_node **it, edge_node *edge0, edge_node *edge1,
double x, double y)
{
it_node *existing_node;
if (!*it)
{
MALLOC(*it, sizeof(it_node), "IT insertion", it_node);
(*it)->ie[0]= edge0;
(*it)->ie[1]= edge1;
(*it)->point.x= x;
(*it)->point.y= y;
(*it)->next= NULL;
}
else
{
if ((*it)->point.y > y)
{
existing_node= *it;
MALLOC(*it, sizeof(it_node), "IT insertion", it_node);
(*it)->ie[0]= edge0;
(*it)->ie[1]= edge1;
(*it)->point.x= x;
(*it)->point.y= y;
(*it)->next= existing_node;
}
else
add_intersection(&((*it)->next), edge0, edge1, x, y);
}
}
static void add_st_edge(st_node **st, it_node **it, edge_node *edge,
double dy)
{
st_node *existing_node;
double den, r, x, y;
if (!*st)
{
MALLOC(*st, sizeof(st_node), "ST insertion", st_node);
(*st)->edge= edge;
(*st)->xb= edge->xb;
(*st)->xt= edge->xt;
(*st)->dx= edge->dx;
(*st)->prev= NULL;
}
else
{
den= ((*st)->xt - (*st)->xb) - (edge->xt - edge->xb);
if ((edge->xt >= (*st)->xt) || (edge->dx == (*st)->dx) ||
(fabs(den) <= DBL_EPSILON))
{
existing_node= *st;
MALLOC(*st, sizeof(st_node), "ST insertion", st_node);
(*st)->edge= edge;
(*st)->xb= edge->xb;
(*st)->xt= edge->xt;
(*st)->dx= edge->dx;
(*st)->prev= existing_node;
}
else
{
r= (edge->xb - (*st)->xb) / den;
x= (*st)->xb + r * ((*st)->xt - (*st)->xb);
y= r * dy;
add_intersection(it, (*st)->edge, edge, x, y);
add_st_edge(&((*st)->prev), it, edge, dy);
}
}
}
static void build_intersection_table(it_node **it, edge_node *aet, double dy)
{
st_node *st, *stp;
edge_node *edge;
reset_it(it);
st= NULL;
for (edge= aet; edge; edge= edge->next)
{
if ((edge->bstate[ABOVE] == BUNDLE_HEAD) ||
edge->bundle[ABOVE][CLIP] || edge->bundle[ABOVE][SUBJ])
add_st_edge(&st, it, edge, dy);
}
while (st)
{
stp= st->prev;
FREE(st);
st= stp;
}
}
static int count_contours(polygon_node *polygon)
{
int nc, nv;
vertex_node *v, *nextv;
for (nc= 0; polygon; polygon= polygon->next)
if (polygon->active)
{
nv= 0;
for (v= polygon->proxy->v[LEFT]; v; v= v->next)
nv++;
if (nv > 2)
{
polygon->active= nv;
nc++;
}
else
{
for (v= polygon->proxy->v[LEFT]; v; v= nextv)
{
nextv= v->next;
FREE(v);
}
polygon->active= 0;
}
}
return nc;
}
static void add_left(polygon_node *p, double x, double y)
{
vertex_node *nv;
MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node);
nv->x= x;
nv->y= y;
nv->next= p->proxy->v[LEFT];
p->proxy->v[LEFT]= nv;
}
static void merge_left(polygon_node *p, polygon_node *q, polygon_node *list)
{
polygon_node *target;
q->proxy->hole= TRUE;
if (p->proxy != q->proxy)
{
p->proxy->v[RIGHT]->next= q->proxy->v[LEFT];
q->proxy->v[LEFT]= p->proxy->v[LEFT];
for (target= p->proxy; list; list= list->next)
{
if (list->proxy == target)
{
list->active= FALSE;
list->proxy= q->proxy;
}
}
}
}
static void add_right(polygon_node *p, double x, double y)
{
vertex_node *nv;
MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node);
nv->x= x;
nv->y= y;
nv->next= NULL;
p->proxy->v[RIGHT]->next= nv;
p->proxy->v[RIGHT]= nv;
}
static void merge_right(polygon_node *p, polygon_node *q, polygon_node *list)
{
polygon_node *target;
q->proxy->hole= FALSE;
if (p->proxy != q->proxy)
{
q->proxy->v[RIGHT]->next= p->proxy->v[LEFT];
q->proxy->v[RIGHT]= p->proxy->v[RIGHT];
for (target= p->proxy; list; list= list->next)
{
if (list->proxy == target)
{
list->active= FALSE;
list->proxy= q->proxy;
}
}
}
}
static void add_local_min(polygon_node **p, edge_node *edge,
double x, double y)
{
polygon_node *existing_min;
vertex_node *nv;
existing_min= *p;
MALLOC(*p, sizeof(polygon_node), "polygon node creation", polygon_node);
MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node);
nv->x= x;
nv->y= y;
nv->next= NULL;
(*p)->proxy= (*p);
(*p)->active= TRUE;
(*p)->next= existing_min;
(*p)->v[LEFT]= nv;
(*p)->v[RIGHT]= nv;
edge->outp[ABOVE]= *p;
}
static int count_tristrips(polygon_node *tn)
{
int total;
for (total= 0; tn; tn= tn->next)
if (tn->active > 2)
total++;
return total;
}
static void add_vertex(vertex_node **t, double x, double y)
{
if (!(*t))
{
MALLOC(*t, sizeof(vertex_node), "tristrip vertex creation", vertex_node);
(*t)->x= x;
(*t)->y= y;
(*t)->next= NULL;
}
else
add_vertex(&((*t)->next), x, y);
}
static void new_tristrip(polygon_node **tn, edge_node *edge,
double x, double y)
{
if (!(*tn))
{
MALLOC(*tn, sizeof(polygon_node), "tristrip node creation", polygon_node);
(*tn)->next= NULL;
(*tn)->v[LEFT]= NULL;
(*tn)->v[RIGHT]= NULL;
(*tn)->active= 1;
add_vertex(&((*tn)->v[LEFT]), x, y);
edge->outp[ABOVE]= *tn;
}
else
new_tristrip(&((*tn)->next), edge, x, y);
}
static bbox *create_contour_bboxes(gpc_polygon *p)
{
bbox *box;
int c, v;
MALLOC(box, p->num_contours * sizeof(bbox), "Bounding box creation", bbox);
for (c= 0; c < p->num_contours; c++)
{
box[c].xmin= DBL_MAX;
box[c].ymin= DBL_MAX;
box[c].xmax= -DBL_MAX;
box[c].ymax= -DBL_MAX;
for (v= 0; v < p->contour[c].num_vertices; v++)
{
if (p->contour[c].vertex[v].x < box[c].xmin)
box[c].xmin= p->contour[c].vertex[v].x;
if (p->contour[c].vertex[v].y < box[c].ymin)
box[c].ymin= p->contour[c].vertex[v].y;
if (p->contour[c].vertex[v].x > box[c].xmax)
box[c].xmax= p->contour[c].vertex[v].x;
if (p->contour[c].vertex[v].y > box[c].ymax)
box[c].ymax= p->contour[c].vertex[v].y;
}
}
return box;
}
static void minimax_test(gpc_polygon *subj, gpc_polygon *clip, gpc_op op)
{
bbox *s_bbox, *c_bbox;
int s, c, *o_table, overlap;
s_bbox= create_contour_bboxes(subj);
c_bbox= create_contour_bboxes(clip);
MALLOC(o_table, subj->num_contours * clip->num_contours * sizeof(int),
"overlap table creation", int);
for (s= 0; s < subj->num_contours; s++)
for (c= 0; c < clip->num_contours; c++)
o_table[c * subj->num_contours + s]=
(!((s_bbox[s].xmax < c_bbox[c].xmin) ||
(s_bbox[s].xmin > c_bbox[c].xmax))) &&
(!((s_bbox[s].ymax < c_bbox[c].ymin) ||
(s_bbox[s].ymin > c_bbox[c].ymax)));
for (c= 0; c < clip->num_contours; c++)
{
overlap= 0;
for (s= 0; (!overlap) && (s < subj->num_contours); s++)
overlap= o_table[c * subj->num_contours + s];
if (!overlap)
clip->contour[c].num_vertices = -clip->contour[c].num_vertices;
}
if (op == GPC_INT)
{
for (s= 0; s < subj->num_contours; s++)
{
overlap= 0;
for (c= 0; (!overlap) && (c < clip->num_contours); c++)
overlap= o_table[c * subj->num_contours + s];
if (!overlap)
subj->contour[s].num_vertices = -subj->contour[s].num_vertices;
}
}
FREE(s_bbox);
FREE(c_bbox);
FREE(o_table);
}
void gpc_free_polygon(gpc_polygon *p)
{
int c;
for (c= 0; c < p->num_contours; c++)
FREE(p->contour[c].vertex);
FREE(p->hole);
FREE(p->contour);
p->num_contours= 0;
}
void gpc_read_polygon(FILE *fp, int read_hole_flags, gpc_polygon *p)
{
int c, v;
fscanf(fp, "%d", &(p->num_contours));
MALLOC(p->hole, p->num_contours * sizeof(int),
"hole flag array creation", int);
MALLOC(p->contour, p->num_contours
* sizeof(gpc_vertex_list), "contour creation", gpc_vertex_list);
for (c= 0; c < p->num_contours; c++)
{
fscanf(fp, "%d", &(p->contour[c].num_vertices));
if (read_hole_flags)
fscanf(fp, "%d", &(p->hole[c]));
else
p->hole[c]= FALSE;
MALLOC(p->contour[c].vertex, p->contour[c].num_vertices
* sizeof(gpc_vertex), "vertex creation", gpc_vertex);
for (v= 0; v < p->contour[c].num_vertices; v++)
fscanf(fp, "%lf %lf", &(p->contour[c].vertex[v].x),
&(p->contour[c].vertex[v].y));
}
}
void gpc_write_polygon(FILE *fp, int write_hole_flags, gpc_polygon *p)
{
int c, v;
fprintf(fp, "%d\n", p->num_contours);
for (c= 0; c < p->num_contours; c++)
{
fprintf(fp, "%d\n", p->contour[c].num_vertices);
if (write_hole_flags)
fprintf(fp, "%d\n", p->hole[c]);
for (v= 0; v < p->contour[c].num_vertices; v++)
fprintf(fp, "% .*lf % .*lf\n",
DBL_DIG, p->contour[c].vertex[v].x,
DBL_DIG, p->contour[c].vertex[v].y);
}
}
void gpc_add_contour(gpc_polygon *p, gpc_vertex_list *new_contour, int hole)
{
int *extended_hole, c, v;
gpc_vertex_list *extended_contour;
MALLOC(extended_hole, (p->num_contours + 1)
* sizeof(int), "contour hole addition", int);
MALLOC(extended_contour, (p->num_contours + 1)
* sizeof(gpc_vertex_list), "contour addition", gpc_vertex_list);
for (c= 0; c < p->num_contours; c++)
{
extended_hole[c]= p->hole[c];
extended_contour[c]= p->contour[c];
}
c= p->num_contours;
extended_hole[c]= hole;
extended_contour[c].num_vertices= new_contour->num_vertices;
MALLOC(extended_contour[c].vertex, new_contour->num_vertices
* sizeof(gpc_vertex), "contour addition", gpc_vertex);
for (v= 0; v < new_contour->num_vertices; v++)
extended_contour[c].vertex[v]= new_contour->vertex[v];
FREE(p->contour);
FREE(p->hole);
p->num_contours++;
p->hole= extended_hole;
p->contour= extended_contour;
}
void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip,
gpc_polygon *result)
{
sb_tree *sbtree= NULL;
it_node *it= NULL, *intersect;
edge_node *edge, *prev_edge, *next_edge, *succ_edge, *e0, *e1;
edge_node *aet= NULL, *c_heap= NULL, *s_heap= NULL;
lmt_node *lmt= NULL, *local_min;
polygon_node *out_poly= NULL, *p, *q, *poly, *npoly, *cf= NULL;
vertex_node *vtx, *nv;
h_state horiz[2];
int in[2], exists[2], parity[2]= {LEFT, LEFT};
int c, v, contributing, search, scanbeam= 0, sbt_entries= 0;
int vclass, bl, br, tl, tr;
double *sbt= NULL, xb, px, yb, yt, dy, ix, iy;
if (((subj->num_contours == 0) && (clip->num_contours == 0))
|| ((subj->num_contours == 0) && ((op == GPC_INT) || (op == GPC_DIFF)))
|| ((clip->num_contours == 0) && (op == GPC_INT)))
{
result->num_contours= 0;
result->hole= NULL;
result->contour= NULL;
return;
}
if (((op == GPC_INT) || (op == GPC_DIFF))
&& (subj->num_contours > 0) && (clip->num_contours > 0))
minimax_test(subj, clip, op);
if (subj->num_contours > 0)
s_heap= build_lmt(&lmt, &sbtree, &sbt_entries, subj, SUBJ, op);
if (clip->num_contours > 0)
c_heap= build_lmt(&lmt, &sbtree, &sbt_entries, clip, CLIP, op);
if (lmt == NULL)
{
result->num_contours= 0;
result->hole= NULL;
result->contour= NULL;
reset_lmt(&lmt);
FREE(s_heap);
FREE(c_heap);
return;
}
MALLOC(sbt, sbt_entries * sizeof(double), "sbt creation", double);
build_sbt(&scanbeam, sbt, sbtree);
scanbeam= 0;
free_sbtree(&sbtree);
if (subj == result)
gpc_free_polygon(subj);
if (clip == result)
gpc_free_polygon(clip);
if (op == GPC_DIFF)
parity[CLIP]= RIGHT;
local_min= lmt;
while (scanbeam < sbt_entries)
{
yb= sbt[scanbeam++];
if (scanbeam < sbt_entries)
{
yt= sbt[scanbeam];
dy= yt - yb;
}
if (local_min)
{
if (local_min->y == yb)
{
for (edge= local_min->first_bound; edge; edge= edge->next_bound)
add_edge_to_aet(&aet, edge, NULL);
local_min= local_min->next;
}
}
px= -DBL_MAX;
e0= aet;
e1= aet;
aet->bundle[ABOVE][ aet->type]= (aet->top.y != yb);
aet->bundle[ABOVE][!aet->type]= FALSE;
aet->bstate[ABOVE]= UNBUNDLED;
for (next_edge= aet->next; next_edge; next_edge= next_edge->next)
{
next_edge->bundle[ABOVE][ next_edge->type]= (next_edge->top.y != yb);
next_edge->bundle[ABOVE][!next_edge->type]= FALSE;
next_edge->bstate[ABOVE]= UNBUNDLED;
if (next_edge->bundle[ABOVE][next_edge->type])
{
if (EQ(e0->xb, next_edge->xb) && EQ(e0->dx, next_edge->dx)
&& (e0->top.y != yb))
{
next_edge->bundle[ABOVE][ next_edge->type]^=
e0->bundle[ABOVE][ next_edge->type];
next_edge->bundle[ABOVE][!next_edge->type]=
e0->bundle[ABOVE][!next_edge->type];
next_edge->bstate[ABOVE]= BUNDLE_HEAD;
e0->bundle[ABOVE][CLIP]= FALSE;
e0->bundle[ABOVE][SUBJ]= FALSE;
e0->bstate[ABOVE]= BUNDLE_TAIL;
}
e0= next_edge;
}
}
horiz[CLIP]= NH;
horiz[SUBJ]= NH;
for (edge= aet; edge; edge= edge->next)
{
exists[CLIP]= edge->bundle[ABOVE][CLIP] +
(edge->bundle[BELOW][CLIP] << 1);
exists[SUBJ]= edge->bundle[ABOVE][SUBJ] +
(edge->bundle[BELOW][SUBJ] << 1);
if (exists[CLIP] || exists[SUBJ])
{
edge->bside[CLIP]= parity[CLIP];
edge->bside[SUBJ]= parity[SUBJ];
switch (op)
{
case GPC_DIFF:
case GPC_INT:
contributing= (exists[CLIP] && (parity[SUBJ] || horiz[SUBJ]))
|| (exists[SUBJ] && (parity[CLIP] || horiz[CLIP]))
|| (exists[CLIP] && exists[SUBJ]
&& (parity[CLIP] == parity[SUBJ]));
br= (parity[CLIP])
&& (parity[SUBJ]);
bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP])
&& (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ (horiz[CLIP]!=NH))
&& (parity[SUBJ] ^ (horiz[SUBJ]!=NH));
tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP])
&& (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]);
break;
case GPC_XOR:
contributing= exists[CLIP] || exists[SUBJ];
br= (parity[CLIP])
^ (parity[SUBJ]);
bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP])
^ (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ (horiz[CLIP]!=NH))
^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH));
tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP])
^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]);
break;
case GPC_UNION:
contributing= (exists[CLIP] && (!parity[SUBJ] || horiz[SUBJ]))
|| (exists[SUBJ] && (!parity[CLIP] || horiz[CLIP]))
|| (exists[CLIP] && exists[SUBJ]
&& (parity[CLIP] == parity[SUBJ]));
br= (parity[CLIP])
|| (parity[SUBJ]);
bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP])
|| (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ (horiz[CLIP]!=NH))
|| (parity[SUBJ] ^ (horiz[SUBJ]!=NH));
tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP])
|| (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]);
break;
}
parity[CLIP]^= edge->bundle[ABOVE][CLIP];
parity[SUBJ]^= edge->bundle[ABOVE][SUBJ];
if (exists[CLIP])
horiz[CLIP]=
next_h_state[horiz[CLIP]]
[((exists[CLIP] - 1) << 1) + parity[CLIP]];
if (exists[SUBJ])
horiz[SUBJ]=
next_h_state[horiz[SUBJ]]
[((exists[SUBJ] - 1) << 1) + parity[SUBJ]];
vclass= tr + (tl << 1) + (br << 2) + (bl << 3);
if (contributing)
{
xb= edge->xb;
switch (vclass)
{
case EMN:
case IMN:
add_local_min(&out_poly, edge, xb, yb);
px= xb;
cf= edge->outp[ABOVE];
break;
case ERI:
if (xb != px)
{
add_right(cf, xb, yb);
px= xb;
}
edge->outp[ABOVE]= cf;
cf= NULL;
break;
case ELI:
add_left(edge->outp[BELOW], xb, yb);
px= xb;
cf= edge->outp[BELOW];
break;
case EMX:
if (xb != px)
{
add_left(cf, xb, yb);
px= xb;
}
merge_right(cf, edge->outp[BELOW], out_poly);
cf= NULL;
break;
case ILI:
if (xb != px)
{
add_left(cf, xb, yb);
px= xb;
}
edge->outp[ABOVE]= cf;
cf= NULL;
break;
case IRI:
add_right(edge->outp[BELOW], xb, yb);
px= xb;
cf= edge->outp[BELOW];
edge->outp[BELOW]= NULL;
break;
case IMX:
if (xb != px)
{
add_right(cf, xb, yb);
px= xb;
}
merge_left(cf, edge->outp[BELOW], out_poly);
cf= NULL;
edge->outp[BELOW]= NULL;
break;
case IMM:
if (xb != px)
{
add_right(cf, xb, yb);
px= xb;
}
merge_left(cf, edge->outp[BELOW], out_poly);
edge->outp[BELOW]= NULL;
add_local_min(&out_poly, edge, xb, yb);
cf= edge->outp[ABOVE];
break;
case EMM:
if (xb != px)
{
add_left(cf, xb, yb);
px= xb;
}
merge_right(cf, edge->outp[BELOW], out_poly);
edge->outp[BELOW]= NULL;
add_local_min(&out_poly, edge, xb, yb);
cf= edge->outp[ABOVE];
break;
case LED:
if (edge->bot.y == yb)
add_left(edge->outp[BELOW], xb, yb);
edge->outp[ABOVE]= edge->outp[BELOW];
px= xb;
break;
case RED:
if (edge->bot.y == yb)
add_right(edge->outp[BELOW], xb, yb);
edge->outp[ABOVE]= edge->outp[BELOW];
px= xb;
break;
default:
break;
}
}
}
}
for (edge= aet; edge; edge= edge->next)
{
if (edge->top.y == yb)
{
prev_edge= edge->prev;
next_edge= edge->next;
if (prev_edge)
prev_edge->next= next_edge;
else
aet= next_edge;
if (next_edge)
next_edge->prev= prev_edge;
if ((edge->bstate[BELOW] == BUNDLE_HEAD) && prev_edge)
{
if (prev_edge->bstate[BELOW] == BUNDLE_TAIL)
{
prev_edge->outp[BELOW]= edge->outp[BELOW];
prev_edge->bstate[BELOW]= UNBUNDLED;
if (prev_edge->prev)
if (prev_edge->prev->bstate[BELOW] == BUNDLE_TAIL)
prev_edge->bstate[BELOW]= BUNDLE_HEAD;
}
}
}
else
{
if (edge->top.y == yt)
edge->xt= edge->top.x;
else
edge->xt= edge->bot.x + edge->dx * (yt - edge->bot.y);
}
}
if (scanbeam < sbt_entries)
{
build_intersection_table(&it, aet, dy);
for (intersect= it; intersect; intersect= intersect->next)
{
e0= intersect->ie[0];
e1= intersect->ie[1];
if ((e0->bundle[ABOVE][CLIP] || e0->bundle[ABOVE][SUBJ])
&& (e1->bundle[ABOVE][CLIP] || e1->bundle[ABOVE][SUBJ]))
{
p= e0->outp[ABOVE];
q= e1->outp[ABOVE];
ix= intersect->point.x;
iy= intersect->point.y + yb;
in[CLIP]= ( e0->bundle[ABOVE][CLIP] && !e0->bside[CLIP])
|| ( e1->bundle[ABOVE][CLIP] && e1->bside[CLIP])
|| (!e0->bundle[ABOVE][CLIP] && !e1->bundle[ABOVE][CLIP]
&& e0->bside[CLIP] && e1->bside[CLIP]);
in[SUBJ]= ( e0->bundle[ABOVE][SUBJ] && !e0->bside[SUBJ])
|| ( e1->bundle[ABOVE][SUBJ] && e1->bside[SUBJ])
|| (!e0->bundle[ABOVE][SUBJ] && !e1->bundle[ABOVE][SUBJ]
&& e0->bside[SUBJ] && e1->bside[SUBJ]);
switch (op)
{
case GPC_DIFF:
case GPC_INT:
tr= (in[CLIP])
&& (in[SUBJ]);
tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP])
&& (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]);
br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP])
&& (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP])
&& (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
break;
case GPC_XOR:
tr= (in[CLIP])
^ (in[SUBJ]);
tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP])
^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]);
br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP])
^ (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP])
^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
break;
case GPC_UNION:
tr= (in[CLIP])
|| (in[SUBJ]);
tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP])
|| (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]);
br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP])
|| (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP])
|| (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
break;
}
vclass= tr + (tl << 1) + (br << 2) + (bl << 3);
switch (vclass)
{
case EMN:
add_local_min(&out_poly, e0, ix, iy);
e1->outp[ABOVE]= e0->outp[ABOVE];
break;
case ERI:
if (p)
{
add_right(p, ix, iy);
e1->outp[ABOVE]= p;
e0->outp[ABOVE]= NULL;
}
break;
case ELI:
if (q)
{
add_left(q, ix, iy);
e0->outp[ABOVE]= q;
e1->outp[ABOVE]= NULL;
}
break;
case EMX:
if (p && q)
{
add_left(p, ix, iy);
merge_right(p, q, out_poly);
e0->outp[ABOVE]= NULL;
e1->outp[ABOVE]= NULL;
}
break;
case IMN:
add_local_min(&out_poly, e0, ix, iy);
e1->outp[ABOVE]= e0->outp[ABOVE];
break;
case ILI:
if (p)
{
add_left(p, ix, iy);
e1->outp[ABOVE]= p;
e0->outp[ABOVE]= NULL;
}
break;
case IRI:
if (q)
{
add_right(q, ix, iy);
e0->outp[ABOVE]= q;
e1->outp[ABOVE]= NULL;
}
break;
case IMX:
if (p && q)
{
add_right(p, ix, iy);
merge_left(p, q, out_poly);
e0->outp[ABOVE]= NULL;
e1->outp[ABOVE]= NULL;
}
break;
case IMM:
if (p && q)
{
add_right(p, ix, iy);
merge_left(p, q, out_poly);
add_local_min(&out_poly, e0, ix, iy);
e1->outp[ABOVE]= e0->outp[ABOVE];
}
break;
case EMM:
if (p && q)
{
add_left(p, ix, iy);
merge_right(p, q, out_poly);
add_local_min(&out_poly, e0, ix, iy);
e1->outp[ABOVE]= e0->outp[ABOVE];
}
break;
default:
break;
}
}
if (e0->bundle[ABOVE][CLIP])
e1->bside[CLIP]= !e1->bside[CLIP];
if (e1->bundle[ABOVE][CLIP])
e0->bside[CLIP]= !e0->bside[CLIP];
if (e0->bundle[ABOVE][SUBJ])
e1->bside[SUBJ]= !e1->bside[SUBJ];
if (e1->bundle[ABOVE][SUBJ])
e0->bside[SUBJ]= !e0->bside[SUBJ];
prev_edge= e0->prev;
next_edge= e1->next;
if (next_edge)
next_edge->prev= e0;
if (e0->bstate[ABOVE] == BUNDLE_HEAD)
{
search= TRUE;
while (search)
{
prev_edge= prev_edge->prev;
if (prev_edge)
{
if (prev_edge->bstate[ABOVE] != BUNDLE_TAIL)
search= FALSE;
}
else
search= FALSE;
}
}
if (!prev_edge)
{
aet->prev= e1;
e1->next= aet;
aet= e0->next;
}
else
{
prev_edge->next->prev= e1;
e1->next= prev_edge->next;
prev_edge->next= e0->next;
}
e0->next->prev= prev_edge;
e1->next->prev= e1;
e0->next= next_edge;
}
for (edge= aet; edge; edge= next_edge)
{
next_edge= edge->next;
succ_edge= edge->succ;
if ((edge->top.y == yt) && succ_edge)
{
succ_edge->outp[BELOW]= edge->outp[ABOVE];
succ_edge->bstate[BELOW]= edge->bstate[ABOVE];
succ_edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP];
succ_edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ];
prev_edge= edge->prev;
if (prev_edge)
prev_edge->next= succ_edge;
else
aet= succ_edge;
if (next_edge)
next_edge->prev= succ_edge;
succ_edge->prev= prev_edge;
succ_edge->next= next_edge;
}
else
{
edge->outp[BELOW]= edge->outp[ABOVE];
edge->bstate[BELOW]= edge->bstate[ABOVE];
edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP];
edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ];
edge->xb= edge->xt;
}
edge->outp[ABOVE]= NULL;
}
}
}
result->contour= NULL;
result->hole= NULL;
result->num_contours= count_contours(out_poly);
if (result->num_contours > 0)
{
MALLOC(result->hole, result->num_contours
* sizeof(int), "hole flag table creation", int);
MALLOC(result->contour, result->num_contours
* sizeof(gpc_vertex_list), "contour creation", gpc_vertex_list);
c= 0;
for (poly= out_poly; poly; poly= npoly)
{
npoly= poly->next;
if (poly->active)
{
result->hole[c]= poly->proxy->hole;
result->contour[c].num_vertices= poly->active;
MALLOC(result->contour[c].vertex,
result->contour[c].num_vertices * sizeof(gpc_vertex),
"vertex creation", gpc_vertex);
v= result->contour[c].num_vertices - 1;
for (vtx= poly->proxy->v[LEFT]; vtx; vtx= nv)
{
nv= vtx->next;
result->contour[c].vertex[v].x= vtx->x;
result->contour[c].vertex[v].y= vtx->y;
FREE(vtx);
v--;
}
c++;
}
FREE(poly);
}
}
else
{
for (poly= out_poly; poly; poly= npoly)
{
npoly= poly->next;
FREE(poly);
}
}
reset_it(&it);
reset_lmt(&lmt);
FREE(c_heap);
FREE(s_heap);
FREE(sbt);
}
void gpc_free_tristrip(gpc_tristrip *t)
{
int s;
for (s= 0; s < t->num_strips; s++)
FREE(t->strip[s].vertex);
FREE(t->strip);
t->num_strips= 0;
}
void gpc_polygon_to_tristrip(gpc_polygon *s, gpc_tristrip *t)
{
gpc_polygon c;
c.num_contours= 0;
c.hole= NULL;
c.contour= NULL;
gpc_tristrip_clip(GPC_DIFF, s, &c, t);
}
void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip,
gpc_tristrip *result)
{
sb_tree *sbtree= NULL;
it_node *it= NULL, *intersect;
edge_node *edge, *prev_edge, *next_edge, *succ_edge, *e0, *e1;
edge_node *aet= NULL, *c_heap= NULL, *s_heap= NULL, *cf;
lmt_node *lmt= NULL, *local_min;
polygon_node *tlist= NULL, *tn, *tnn, *p, *q;
vertex_node *lt, *ltn, *rt, *rtn;
h_state horiz[2];
vertex_type cft;
int in[2], exists[2], parity[2]= {LEFT, LEFT};
int s, v, contributing, search, scanbeam= 0, sbt_entries= 0;
int vclass, bl, br, tl, tr;
double *sbt= NULL, xb, px, nx, yb, yt, dy, ix, iy;
if (((subj->num_contours == 0) && (clip->num_contours == 0))
|| ((subj->num_contours == 0) && ((op == GPC_INT) || (op == GPC_DIFF)))
|| ((clip->num_contours == 0) && (op == GPC_INT)))
{
result->num_strips= 0;
result->strip= NULL;
return;
}
if (((op == GPC_INT) || (op == GPC_DIFF))
&& (subj->num_contours > 0) && (clip->num_contours > 0))
minimax_test(subj, clip, op);
if (subj->num_contours > 0)
s_heap= build_lmt(&lmt, &sbtree, &sbt_entries, subj, SUBJ, op);
if (clip->num_contours > 0)
c_heap= build_lmt(&lmt, &sbtree, &sbt_entries, clip, CLIP, op);
if (lmt == NULL)
{
result->num_strips= 0;
result->strip= NULL;
reset_lmt(&lmt);
FREE(s_heap);
FREE(c_heap);
return;
}
MALLOC(sbt, sbt_entries * sizeof(double), "sbt creation", double);
build_sbt(&scanbeam, sbt, sbtree);
scanbeam= 0;
free_sbtree(&sbtree);
if (op == GPC_DIFF)
parity[CLIP]= RIGHT;
local_min= lmt;
while (scanbeam < sbt_entries)
{
yb= sbt[scanbeam++];
if (scanbeam < sbt_entries)
{
yt= sbt[scanbeam];
dy= yt - yb;
}
if (local_min)
{
if (local_min->y == yb)
{
for (edge= local_min->first_bound; edge; edge= edge->next_bound)
add_edge_to_aet(&aet, edge, NULL);
local_min= local_min->next;
}
}
px= -DBL_MAX;
e0= aet;
e1= aet;
aet->bundle[ABOVE][ aet->type]= (aet->top.y != yb);
aet->bundle[ABOVE][!aet->type]= FALSE;
aet->bstate[ABOVE]= UNBUNDLED;
for (next_edge= aet->next; next_edge; next_edge= next_edge->next)
{
next_edge->bundle[ABOVE][ next_edge->type]= (next_edge->top.y != yb);
next_edge->bundle[ABOVE][!next_edge->type]= FALSE;
next_edge->bstate[ABOVE]= UNBUNDLED;
if (next_edge->bundle[ABOVE][next_edge->type])
{
if (EQ(e0->xb, next_edge->xb) && EQ(e0->dx, next_edge->dx)
&& (e0->top.y != yb))
{
next_edge->bundle[ABOVE][ next_edge->type]^=
e0->bundle[ABOVE][ next_edge->type];
next_edge->bundle[ABOVE][!next_edge->type]=
e0->bundle[ABOVE][!next_edge->type];
next_edge->bstate[ABOVE]= BUNDLE_HEAD;
e0->bundle[ABOVE][CLIP]= FALSE;
e0->bundle[ABOVE][SUBJ]= FALSE;
e0->bstate[ABOVE]= BUNDLE_TAIL;
}
e0= next_edge;
}
}
horiz[CLIP]= NH;
horiz[SUBJ]= NH;
for (edge= aet; edge; edge= edge->next)
{
exists[CLIP]= edge->bundle[ABOVE][CLIP] +
(edge->bundle[BELOW][CLIP] << 1);
exists[SUBJ]= edge->bundle[ABOVE][SUBJ] +
(edge->bundle[BELOW][SUBJ] << 1);
if (exists[CLIP] || exists[SUBJ])
{
edge->bside[CLIP]= parity[CLIP];
edge->bside[SUBJ]= parity[SUBJ];
switch (op)
{
case GPC_DIFF:
case GPC_INT:
contributing= (exists[CLIP] && (parity[SUBJ] || horiz[SUBJ]))
|| (exists[SUBJ] && (parity[CLIP] || horiz[CLIP]))
|| (exists[CLIP] && exists[SUBJ]
&& (parity[CLIP] == parity[SUBJ]));
br= (parity[CLIP])
&& (parity[SUBJ]);
bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP])
&& (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ (horiz[CLIP]!=NH))
&& (parity[SUBJ] ^ (horiz[SUBJ]!=NH));
tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP])
&& (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]);
break;
case GPC_XOR:
contributing= exists[CLIP] || exists[SUBJ];
br= (parity[CLIP])
^ (parity[SUBJ]);
bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP])
^ (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ (horiz[CLIP]!=NH))
^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH));
tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP])
^ (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]);
break;
case GPC_UNION:
contributing= (exists[CLIP] && (!parity[SUBJ] || horiz[SUBJ]))
|| (exists[SUBJ] && (!parity[CLIP] || horiz[CLIP]))
|| (exists[CLIP] && exists[SUBJ]
&& (parity[CLIP] == parity[SUBJ]));
br= (parity[CLIP])
|| (parity[SUBJ]);
bl= (parity[CLIP] ^ edge->bundle[ABOVE][CLIP])
|| (parity[SUBJ] ^ edge->bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ (horiz[CLIP]!=NH))
|| (parity[SUBJ] ^ (horiz[SUBJ]!=NH));
tl= (parity[CLIP] ^ (horiz[CLIP]!=NH) ^ edge->bundle[BELOW][CLIP])
|| (parity[SUBJ] ^ (horiz[SUBJ]!=NH) ^ edge->bundle[BELOW][SUBJ]);
break;
}
parity[CLIP]^= edge->bundle[ABOVE][CLIP];
parity[SUBJ]^= edge->bundle[ABOVE][SUBJ];
if (exists[CLIP])
horiz[CLIP]=
next_h_state[horiz[CLIP]]
[((exists[CLIP] - 1) << 1) + parity[CLIP]];
if (exists[SUBJ])
horiz[SUBJ]=
next_h_state[horiz[SUBJ]]
[((exists[SUBJ] - 1) << 1) + parity[SUBJ]];
vclass= tr + (tl << 1) + (br << 2) + (bl << 3);
if (contributing)
{
xb= edge->xb;
switch (vclass)
{
case EMN:
new_tristrip(&tlist, edge, xb, yb);
cf= edge;
break;
case ERI:
edge->outp[ABOVE]= cf->outp[ABOVE];
if (xb != cf->xb)
VERTEX(edge, ABOVE, RIGHT, xb, yb);
cf= NULL;
break;
case ELI:
VERTEX(edge, BELOW, LEFT, xb, yb);
edge->outp[ABOVE]= NULL;
cf= edge;
break;
case EMX:
if (xb != cf->xb)
VERTEX(edge, BELOW, RIGHT, xb, yb);
edge->outp[ABOVE]= NULL;
cf= NULL;
break;
case IMN:
if (cft == LED)
{
if (cf->bot.y != yb)
VERTEX(cf, BELOW, LEFT, cf->xb, yb);
new_tristrip(&tlist, cf, cf->xb, yb);
}
edge->outp[ABOVE]= cf->outp[ABOVE];
VERTEX(edge, ABOVE, RIGHT, xb, yb);
break;
case ILI:
new_tristrip(&tlist, edge, xb, yb);
cf= edge;
cft= ILI;
break;
case IRI:
if (cft == LED)
{
if (cf->bot.y != yb)
VERTEX(cf, BELOW, LEFT, cf->xb, yb);
new_tristrip(&tlist, cf, cf->xb, yb);
}
VERTEX(edge, BELOW, RIGHT, xb, yb);
edge->outp[ABOVE]= NULL;
break;
case IMX:
VERTEX(edge, BELOW, LEFT, xb, yb);
edge->outp[ABOVE]= NULL;
cft= IMX;
break;
case IMM:
VERTEX(edge, BELOW, LEFT, xb, yb);
edge->outp[ABOVE]= cf->outp[ABOVE];
if (xb != cf->xb)
VERTEX(cf, ABOVE, RIGHT, xb, yb);
cf= edge;
break;
case EMM:
VERTEX(edge, BELOW, RIGHT, xb, yb);
edge->outp[ABOVE]= NULL;
new_tristrip(&tlist, edge, xb, yb);
cf= edge;
break;
case LED:
if (edge->bot.y == yb)
VERTEX(edge, BELOW, LEFT, xb, yb);
edge->outp[ABOVE]= edge->outp[BELOW];
cf= edge;
cft= LED;
break;
case RED:
edge->outp[ABOVE]= cf->outp[ABOVE];
if (cft == LED)
{
if (cf->bot.y == yb)
{
VERTEX(edge, BELOW, RIGHT, xb, yb);
}
else
{
if (edge->bot.y == yb)
{
VERTEX(cf, BELOW, LEFT, cf->xb, yb);
VERTEX(edge, BELOW, RIGHT, xb, yb);
}
}
}
else
{
VERTEX(edge, BELOW, RIGHT, xb, yb);
VERTEX(edge, ABOVE, RIGHT, xb, yb);
}
cf= NULL;
break;
default:
break;
}
}
}
}
for (edge= aet; edge; edge= edge->next)
{
if (edge->top.y == yb)
{
prev_edge= edge->prev;
next_edge= edge->next;
if (prev_edge)
prev_edge->next= next_edge;
else
aet= next_edge;
if (next_edge)
next_edge->prev= prev_edge;
if ((edge->bstate[BELOW] == BUNDLE_HEAD) && prev_edge)
{
if (prev_edge->bstate[BELOW] == BUNDLE_TAIL)
{
prev_edge->outp[BELOW]= edge->outp[BELOW];
prev_edge->bstate[BELOW]= UNBUNDLED;
if (prev_edge->prev)
if (prev_edge->prev->bstate[BELOW] == BUNDLE_TAIL)
prev_edge->bstate[BELOW]= BUNDLE_HEAD;
}
}
}
else
{
if (edge->top.y == yt)
edge->xt= edge->top.x;
else
edge->xt= edge->bot.x + edge->dx * (yt - edge->bot.y);
}
}
if (scanbeam < sbt_entries)
{
build_intersection_table(&it, aet, dy);
for (intersect= it; intersect; intersect= intersect->next)
{
e0= intersect->ie[0];
e1= intersect->ie[1];
if ((e0->bundle[ABOVE][CLIP] || e0->bundle[ABOVE][SUBJ])
&& (e1->bundle[ABOVE][CLIP] || e1->bundle[ABOVE][SUBJ]))
{
p= e0->outp[ABOVE];
q= e1->outp[ABOVE];
ix= intersect->point.x;
iy= intersect->point.y + yb;
in[CLIP]= ( e0->bundle[ABOVE][CLIP] && !e0->bside[CLIP])
|| ( e1->bundle[ABOVE][CLIP] && e1->bside[CLIP])
|| (!e0->bundle[ABOVE][CLIP] && !e1->bundle[ABOVE][CLIP]
&& e0->bside[CLIP] && e1->bside[CLIP]);
in[SUBJ]= ( e0->bundle[ABOVE][SUBJ] && !e0->bside[SUBJ])
|| ( e1->bundle[ABOVE][SUBJ] && e1->bside[SUBJ])
|| (!e0->bundle[ABOVE][SUBJ] && !e1->bundle[ABOVE][SUBJ]
&& e0->bside[SUBJ] && e1->bside[SUBJ]);
switch (op)
{
case GPC_DIFF:
case GPC_INT:
tr= (in[CLIP])
&& (in[SUBJ]);
tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP])
&& (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]);
br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP])
&& (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP])
&& (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
break;
case GPC_XOR:
tr= (in[CLIP])
^ (in[SUBJ]);
tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP])
^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]);
br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP])
^ (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP])
^ (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
break;
case GPC_UNION:
tr= (in[CLIP])
|| (in[SUBJ]);
tl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP])
|| (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ]);
br= (in[CLIP] ^ e0->bundle[ABOVE][CLIP])
|| (in[SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
bl= (in[CLIP] ^ e1->bundle[ABOVE][CLIP] ^ e0->bundle[ABOVE][CLIP])
|| (in[SUBJ] ^ e1->bundle[ABOVE][SUBJ] ^ e0->bundle[ABOVE][SUBJ]);
break;
}
vclass= tr + (tl << 1) + (br << 2) + (bl << 3);
switch (vclass)
{
case EMN:
new_tristrip(&tlist, e1, ix, iy);
e0->outp[ABOVE]= e1->outp[ABOVE];
break;
case ERI:
if (p)
{
P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
VERTEX(e0, ABOVE, RIGHT, ix, iy);
e1->outp[ABOVE]= e0->outp[ABOVE];
e0->outp[ABOVE]= NULL;
}
break;
case ELI:
if (q)
{
N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(e1, ABOVE, LEFT, ix, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
e0->outp[ABOVE]= e1->outp[ABOVE];
e1->outp[ABOVE]= NULL;
}
break;
case EMX:
if (p && q)
{
VERTEX(e0, ABOVE, LEFT, ix, iy);
e0->outp[ABOVE]= NULL;
e1->outp[ABOVE]= NULL;
}
break;
case IMN:
P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
new_tristrip(&tlist, prev_edge, px, iy);
e1->outp[ABOVE]= prev_edge->outp[ABOVE];
VERTEX(e1, ABOVE, RIGHT, ix, iy);
new_tristrip(&tlist, e0, ix, iy);
next_edge->outp[ABOVE]= e0->outp[ABOVE];
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
break;
case ILI:
if (p)
{
VERTEX(e0, ABOVE, LEFT, ix, iy);
N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
e1->outp[ABOVE]= e0->outp[ABOVE];
e0->outp[ABOVE]= NULL;
}
break;
case IRI:
if (q)
{
VERTEX(e1, ABOVE, RIGHT, ix, iy);
P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
e0->outp[ABOVE]= e1->outp[ABOVE];
e1->outp[ABOVE]= NULL;
}
break;
case IMX:
if (p && q)
{
VERTEX(e0, ABOVE, RIGHT, ix, iy);
VERTEX(e1, ABOVE, LEFT, ix, iy);
e0->outp[ABOVE]= NULL;
e1->outp[ABOVE]= NULL;
P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
new_tristrip(&tlist, prev_edge, px, iy);
N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
next_edge->outp[ABOVE]= prev_edge->outp[ABOVE];
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
}
break;
case IMM:
if (p && q)
{
VERTEX(e0, ABOVE, RIGHT, ix, iy);
VERTEX(e1, ABOVE, LEFT, ix, iy);
P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
new_tristrip(&tlist, prev_edge, px, iy);
N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
e1->outp[ABOVE]= prev_edge->outp[ABOVE];
VERTEX(e1, ABOVE, RIGHT, ix, iy);
new_tristrip(&tlist, e0, ix, iy);
next_edge->outp[ABOVE]= e0->outp[ABOVE];
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
}
break;
case EMM:
if (p && q)
{
VERTEX(e0, ABOVE, LEFT, ix, iy);
new_tristrip(&tlist, e1, ix, iy);
e0->outp[ABOVE]= e1->outp[ABOVE];
}
break;
default:
break;
}
}
if (e0->bundle[ABOVE][CLIP])
e1->bside[CLIP]= !e1->bside[CLIP];
if (e1->bundle[ABOVE][CLIP])
e0->bside[CLIP]= !e0->bside[CLIP];
if (e0->bundle[ABOVE][SUBJ])
e1->bside[SUBJ]= !e1->bside[SUBJ];
if (e1->bundle[ABOVE][SUBJ])
e0->bside[SUBJ]= !e0->bside[SUBJ];
prev_edge= e0->prev;
next_edge= e1->next;
if (e1->next)
e1->next->prev= e0;
if (e0->bstate[ABOVE] == BUNDLE_HEAD)
{
search= TRUE;
while (search)
{
prev_edge= prev_edge->prev;
if (prev_edge)
{
if (prev_edge->bundle[ABOVE][CLIP]
|| prev_edge->bundle[ABOVE][SUBJ]
|| (prev_edge->bstate[ABOVE] == BUNDLE_HEAD))
search= FALSE;
}
else
search= FALSE;
}
}
if (!prev_edge)
{
e1->next= aet;
aet= e0->next;
}
else
{
e1->next= prev_edge->next;
prev_edge->next= e0->next;
}
e0->next->prev= prev_edge;
e1->next->prev= e1;
e0->next= next_edge;
}
for (edge= aet; edge; edge= next_edge)
{
next_edge= edge->next;
succ_edge= edge->succ;
if ((edge->top.y == yt) && succ_edge)
{
succ_edge->outp[BELOW]= edge->outp[ABOVE];
succ_edge->bstate[BELOW]= edge->bstate[ABOVE];
succ_edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP];
succ_edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ];
prev_edge= edge->prev;
if (prev_edge)
prev_edge->next= succ_edge;
else
aet= succ_edge;
if (next_edge)
next_edge->prev= succ_edge;
succ_edge->prev= prev_edge;
succ_edge->next= next_edge;
}
else
{
edge->outp[BELOW]= edge->outp[ABOVE];
edge->bstate[BELOW]= edge->bstate[ABOVE];
edge->bundle[BELOW][CLIP]= edge->bundle[ABOVE][CLIP];
edge->bundle[BELOW][SUBJ]= edge->bundle[ABOVE][SUBJ];
edge->xb= edge->xt;
}
edge->outp[ABOVE]= NULL;
}
}
}
result->strip= NULL;
result->num_strips= count_tristrips(tlist);
if (result->num_strips > 0)
{
MALLOC(result->strip, result->num_strips * sizeof(gpc_vertex_list),
"tristrip list creation", gpc_vertex_list);
s= 0;
for (tn= tlist; tn; tn= tnn)
{
tnn= tn->next;
if (tn->active > 2)
{
result->strip[s].num_vertices= tn->active;
MALLOC(result->strip[s].vertex, tn->active * sizeof(gpc_vertex),
"tristrip creation", gpc_vertex);
v= 0;
if (INVERT_TRISTRIPS)
{
lt= tn->v[RIGHT];
rt= tn->v[LEFT];
}
else
{
lt= tn->v[LEFT];
rt= tn->v[RIGHT];
}
while (lt || rt)
{
if (lt)
{
ltn= lt->next;
result->strip[s].vertex[v].x= lt->x;
result->strip[s].vertex[v].y= lt->y;
v++;
FREE(lt);
lt= ltn;
}
if (rt)
{
rtn= rt->next;
result->strip[s].vertex[v].x= rt->x;
result->strip[s].vertex[v].y= rt->y;
v++;
FREE(rt);
rt= rtn;
}
}
s++;
}
else
{
for (lt= tn->v[LEFT]; lt; lt= ltn)
{
ltn= lt->next;
FREE(lt);
}
for (rt= tn->v[RIGHT]; rt; rt=rtn)
{
rtn= rt->next;
FREE(rt);
}
}
FREE(tn);
}
}
reset_it(&it);
reset_lmt(&lmt);
FREE(c_heap);
FREE(s_heap);
FREE(sbt);
}