Commit dab0fd9e authored by CHARRAS's avatar CHARRAS

added: libraries and infos relatives to polygons (usefull for zone redesign)

parent 611c2208
/*
===========================================================================
Project: Generic Polygon Clipper
A new algorithm for calculating the difference, intersection,
exclusive-or or union of arbitrary polygon sets.
File: gpc.c
Author: Alan Murta (email: gpc@cs.man.ac.uk)
Version: 2.32
Date: 17th December 2004
Copyright: (C) Advanced Interfaces Group,
University of Manchester.
This software is free for non-commercial use. It may be copied,
modified, and redistributed provided that this copyright notice
is preserved on all copies. The intellectual property rights of
the algorithms used reside with the University of Manchester
Advanced Interfaces Group.
You may not use this software, in whole or in part, in support
of any commercial product without the express consent of the
author.
There is no warranty or other guarantee of fitness of this
software for any purpose. It is provided solely "as is".
===========================================================================
*/
/*
===========================================================================
Includes
===========================================================================
*/
#include "GenericPolygonClipperLibrary.h"
#include <stdlib.h>
#include <float.h>
#include <math.h>
/*
===========================================================================
Constants
===========================================================================
*/
#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
/*
===========================================================================
Macros
===========================================================================
*/
#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;}}
/*
===========================================================================
Private Data Types
===========================================================================
*/
typedef enum /* Edge intersection classes */
{
NUL, /* Empty non-intersection */
EMX, /* External maximum */
ELI, /* External left intermediate */
TED, /* Top edge */
ERI, /* External right intermediate */
RED, /* Right edge */
IMM, /* Internal maximum and minimum */
IMN, /* Internal minimum */
EMN, /* External minimum */
EMM, /* External maximum and minimum */
LED, /* Left edge */
ILI, /* Internal left intermediate */
BED, /* Bottom edge */
IRI, /* Internal right intermediate */
IMX, /* Internal maximum */
FUL /* Full non-intersection */
} vertex_type;
typedef enum /* Horizontal edge states */
{
NH, /* No horizontal edge */
BH, /* Bottom horizontal edge */
TH /* Top horizontal edge */
} h_state;
typedef enum /* Edge bundle state */
{
UNBUNDLED, /* Isolated edge not within a bundle */
BUNDLE_HEAD, /* Bundle head node */
BUNDLE_TAIL /* Passive bundle tail node */
} bundle_state;
typedef struct v_shape /* Internal vertex list datatype */
{
double x; /* X coordinate component */
double y; /* Y coordinate component */
struct v_shape *next; /* Pointer to next vertex in list */
} vertex_node;
typedef struct p_shape /* Internal contour / tristrip type */
{
int active; /* Active flag / vertex count */
int hole; /* Hole / external contour flag */
vertex_node *v[2]; /* Left and right vertex list ptrs */
struct p_shape *next; /* Pointer to next polygon contour */
struct p_shape *proxy; /* Pointer to actual structure used */
} polygon_node;
typedef struct edge_shape
{
gpc_vertex vertex; /* Piggy-backed contour vertex data */
gpc_vertex bot; /* Edge lower (x, y) coordinate */
gpc_vertex top; /* Edge upper (x, y) coordinate */
double xb; /* Scanbeam bottom x coordinate */
double xt; /* Scanbeam top x coordinate */
double dx; /* Change in x for a unit y increase */
int type; /* Clip / subject edge flag */
int bundle[2][2]; /* Bundle edge flags */
int bside[2]; /* Bundle left / right indicators */
bundle_state bstate[2]; /* Edge bundle state */
polygon_node *outp[2]; /* Output polygon / tristrip pointer */
struct edge_shape *prev; /* Previous edge in the AET */
struct edge_shape *next; /* Next edge in the AET */
struct edge_shape *pred; /* Edge connected at the lower end */
struct edge_shape *succ; /* Edge connected at the upper end */
struct edge_shape *next_bound; /* Pointer to next bound in LMT */
} edge_node;
typedef struct lmt_shape /* Local minima table */
{
double y; /* Y coordinate at local minimum */
edge_node *first_bound; /* Pointer to bound list */
struct lmt_shape *next; /* Pointer to next local minimum */
} lmt_node;
typedef struct sbt_t_shape /* Scanbeam tree */
{
double y; /* Scanbeam node y value */
struct sbt_t_shape *less; /* Pointer to nodes with lower y */
struct sbt_t_shape *more; /* Pointer to nodes with higher y */
} sb_tree;
typedef struct it_shape /* Intersection table */
{
edge_node *ie[2]; /* Intersecting edge (bundle) pair */
gpc_vertex point; /* Point of intersection */
struct it_shape *next; /* The next intersection table node */
} it_node;
typedef struct st_shape /* Sorted edge table */
{
edge_node *edge; /* Pointer to AET edge */
double xb; /* Scanbeam bottom x coordinate */
double xt; /* Scanbeam top x coordinate */
double dx; /* Change in x for a unit y increase */
struct st_shape *prev; /* Previous edge in sorted list */
} st_node;
typedef struct bbox_shape /* Contour axis-aligned bounding box */
{
double xmin; /* Minimum x coordinate */
double ymin; /* Minimum y coordinate */
double xmax; /* Maximum x coordinate */
double ymax; /* Maximum y coordinate */
} bbox;
/*
===========================================================================
Global Data
===========================================================================
*/
/* Horizontal edge state transitions within scanbeam boundary */
const h_state next_h_state[3][6]=
{
/* ABOVE BELOW CROSS */
/* L R L R L R */
/* NH */ {BH, TH, TH, BH, NH, NH},
/* BH */ {NH, NH, NH, NH, TH, TH},
/* TH */ {NH, NH, NH, NH, BH, BH}
};
/*
===========================================================================
Private Functions
===========================================================================
*/
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)
{
/* Link node e to the tail of the list */
*b= e;
}
else
{
/* Do primary sort on the x field */
if (e[0].bot.x < (*b)[0].bot.x)
{
/* Insert a new node mid-list */
existing_bound= *b;
*b= e;
(*b)->next_bound= existing_bound;
}
else
{
if (e[0].bot.x == (*b)[0].bot.x)
{
/* Do secondary sort on the dx field */
if (e[0].dx < (*b)[0].dx)
{
/* Insert a new node mid-list */
existing_bound= *b;
*b= e;
(*b)->next_bound= existing_bound;
}
else
{
/* Head further down the list */
insert_bound(&((*b)->next_bound), e);
}
}
else
{
/* Head further down the list */
insert_bound(&((*b)->next_bound), e);
}
}
}
}
static edge_node **bound_list(lmt_node **lmt, double y)
{
lmt_node *existing_node;
if (!*lmt)
{
/* Add node onto the tail end of the 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)
{
/* Insert a new LMT node before the current node */
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)
/* Head further up the LMT */
return bound_list(&((*lmt)->next), y);
else
/* Use this existing LMT node */
return &((*lmt)->first_bound);
}
static void add_to_sbtree(int *entries, sb_tree **sbtree, double y)
{
if (!*sbtree)
{
/* Add a new tree node here */
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)
{
/* Head into the 'less' sub-tree */
add_to_sbtree(entries, &((*sbtree)->less), y);
}
else
{
if ((*sbtree)->y < y)
{
/* Head into the 'more' sub-tree */
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;
/* Ignore non-contributing contours */
if (c.num_vertices > 0)
{
for (i= 0; i < c.num_vertices; i++)
/* Ignore superfluous vertices embedded in horizontal edges */
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]);
/* Create the entire input polygon edge table in one go */
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)
{
/* Ignore the non-contributing contour and repair the vertex count */
p->contour[c].num_vertices= -p->contour[c].num_vertices;
}
else
{
/* Perform contour optimisation */
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;
/* Record vertex in the scanbeam table */
add_to_sbtree(sbt_entries, sbtree,
edge_table[num_vertices].vertex.y);
num_vertices++;
}
/* Do the contour forward pass */
for (min= 0; min < num_vertices; min++)
{
/* If a forward local minimum... */
if (FWD_MIN(edge_table, min, num_vertices))
{
/* Search for the next local maximum... */
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);
}
/* Build the next edge list */
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);
}
}
/* Do the contour reverse pass */
for (min= 0; min < num_vertices; min++)
{
/* If a reverse local minimum... */
if (REV_MIN(edge_table, min, num_vertices))
{
/* Search for the previous local maximum... */
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);
}
/* Build the previous edge list */
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)
{
/* Append edge onto the tail end of the AET */
*aet= edge;
edge->prev= prev;
edge->next= NULL;
}
else
{
/* Do primary sort on the xb field */
if (edge->xb < (*aet)->xb)
{
/* Insert edge here (before the AET edge) */
edge->prev= prev;
edge->next= *aet;
(*aet)->prev= edge;
*aet= edge;
}
else
{
if (edge->xb == (*aet)->xb)
{
/* Do secondary sort on the dx field */
if (edge->dx < (*aet)->dx)
{
/* Insert edge here (before the AET edge) */
edge->prev= prev;
edge->next= *aet;
(*aet)->prev= edge;
*aet= edge;
}
else
{
/* Head further into the AET */
add_edge_to_aet(&((*aet)->next), edge, *aet);
}
}
else
{
/* Head further into the AET */
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)
{
/* Append a new node to the tail of the list */
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)
{
/* Insert a new node mid-list */
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
/* Head further down the list */
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)
{
/* Append edge onto the tail end of the 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 new edge and ST edge don't cross */
if ((edge->xt >= (*st)->xt) || (edge->dx == (*st)->dx) ||
(fabs(den) <= DBL_EPSILON))
{
/* No intersection - insert edge here (before the ST edge) */
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
{
/* Compute intersection between new edge and ST edge */
r= (edge->xb - (*st)->xb) / den;
x= (*st)->xb + r * ((*st)->xt - (*st)->xb);
y= r * dy;
/* Insert the edge pointers and the intersection point in the IT */
add_intersection(it, (*st)->edge, edge, x, y);
/* Head further into the ST */
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;
/* Build intersection table for the current scanbeam */
reset_it(it);
st= NULL;
/* Process each AET edge */
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);
}
/* Free the sorted edge table */
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)
{
/* Count the vertices in the current contour */
nv= 0;
for (v= polygon->proxy->v[LEFT]; v; v= v->next)
nv++;
/* Record valid vertex counts in the active field */
if (nv > 2)
{
polygon->active= nv;
nc++;
}
else
{
/* Invalid contour: just free the heap */
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;
/* Create a new vertex node and set its fields */
MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node);
nv->x= x;
nv->y= y;
/* Add vertex nv to the left end of the polygon's vertex list */
nv->next= p->proxy->v[LEFT];
/* Update proxy->[LEFT] to point to nv */
p->proxy->v[LEFT]= nv;
}
static void merge_left(polygon_node *p, polygon_node *q, polygon_node *list)
{
polygon_node *target;
/* Label contour as a hole */
q->proxy->hole= TRUE;
if (p->proxy != q->proxy)
{
/* Assign p's vertex list to the left end of q's list */
p->proxy->v[RIGHT]->next= q->proxy->v[LEFT];
q->proxy->v[LEFT]= p->proxy->v[LEFT];
/* Redirect any p->proxy references to q->proxy */
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;
/* Create a new vertex node and set its fields */
MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node);
nv->x= x;
nv->y= y;
nv->next= NULL;
/* Add vertex nv to the right end of the polygon's vertex list */
p->proxy->v[RIGHT]->next= nv;
/* Update proxy->v[RIGHT] to point to nv */
p->proxy->v[RIGHT]= nv;
}
static void merge_right(polygon_node *p, polygon_node *q, polygon_node *list)
{
polygon_node *target;
/* Label contour as external */
q->proxy->hole= FALSE;
if (p->proxy != q->proxy)
{
/* Assign p's vertex list to the right end of q's list */
q->proxy->v[RIGHT]->next= p->proxy->v[LEFT];
q->proxy->v[RIGHT]= p->proxy->v[RIGHT];
/* Redirect any p->proxy references to q->proxy */
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);
/* Create a new vertex node and set its fields */
MALLOC(nv, sizeof(vertex_node), "vertex node creation", vertex_node);
nv->x= x;
nv->y= y;
nv->next= NULL;
/* Initialise proxy to point to p itself */
(*p)->proxy= (*p);
(*p)->active= TRUE;
(*p)->next= existing_min;
/* Make v[LEFT] and v[RIGHT] point to new vertex nv */
(*p)->v[LEFT]= nv;
(*p)->v[RIGHT]= nv;
/* Assign polygon p to the edge */
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
/* Head further down the list */
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
/* Head further down the list */
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);
/* Construct contour bounding boxes */
for (c= 0; c < p->num_contours; c++)
{
/* Initialise bounding box extent */
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++)
{
/* Adjust bounding box */
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);
/* Check all subject contour bounding boxes against clip boxes */
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 each clip contour, search for any subject contour overlaps */
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)
/* Flag non contributing status by negating vertex count */
clip->contour[c].num_vertices = -clip->contour[c].num_vertices;
}
if (op == GPC_INT)
{
/* For each subject contour, search for any clip contour overlaps */
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)
/* Flag non contributing status by negating vertex count */
subj->contour[s].num_vertices = -subj->contour[s].num_vertices;
}
}
FREE(s_bbox);
FREE(c_bbox);
FREE(o_table);
}
/*
===========================================================================
Public Functions
===========================================================================
*/
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; /* Assume all contours to be external */
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;
/* Create an extended hole array */
MALLOC(extended_hole, (p->num_contours + 1)
* sizeof(int), "contour hole addition", int);
/* Create an extended contour array */
MALLOC(extended_contour, (p->num_contours + 1)
* sizeof(gpc_vertex_list), "contour addition", gpc_vertex_list);
/* Copy the old contour and hole data into the extended arrays */
for (c= 0; c < p->num_contours; c++)
{
extended_hole[c]= p->hole[c];
extended_contour[c]= p->contour[c];
}
/* Copy the new contour and hole onto the end of the extended arrays */
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];
/* Dispose of the old contour */
FREE(p->contour);
FREE(p->hole);
/* Update the polygon information */
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;
/* Test for trivial NULL result cases */
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;
}
/* Identify potentialy contributing contours */
if (((op == GPC_INT) || (op == GPC_DIFF))
&& (subj->num_contours > 0) && (clip->num_contours > 0))
minimax_test(subj, clip, op);
/* Build LMT */
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);
/* Return a NULL result if no contours contribute */
if (lmt == NULL)
{
result->num_contours= 0;
result->hole= NULL;
result->contour= NULL;
reset_lmt(&lmt);
FREE(s_heap);
FREE(c_heap);
return;
}
/* Build scanbeam table from scanbeam tree */
MALLOC(sbt, sbt_entries * sizeof(double), "sbt creation", double);
build_sbt(&scanbeam, sbt, sbtree);
scanbeam= 0;
free_sbtree(&sbtree);
/* Allow pointer re-use without causing memory leak */
if (subj == result)
gpc_free_polygon(subj);
if (clip == result)
gpc_free_polygon(clip);
/* Invert clip polygon for difference operation */
if (op == GPC_DIFF)
parity[CLIP]= RIGHT;
local_min= lmt;
/* Process each scanbeam */
while (scanbeam < sbt_entries)
{
/* Set yb and yt to the bottom and top of the scanbeam */
yb= sbt[scanbeam++];
if (scanbeam < sbt_entries)
{
yt= sbt[scanbeam];
dy= yt - yb;
}
/* === SCANBEAM BOUNDARY PROCESSING ================================ */
/* If LMT node corresponding to yb exists */
if (local_min)
{
if (local_min->y == yb)
{
/* Add edges starting at this local minimum to the AET */
for (edge= local_min->first_bound; edge; edge= edge->next_bound)
add_edge_to_aet(&aet, edge, NULL);
local_min= local_min->next;
}
}
/* Set dummy previous x value */
px= -DBL_MAX;
/* Create bundles within AET */
e0= aet;
e1= aet;
/* Set up bundle fields of first edge */
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)
{
/* Set up bundle fields of next edge */
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;
/* Bundle edges above the scanbeam boundary if they coincide */
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;
/* Process each edge at this scanbeam boundary */
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])
{
/* Set bundle side */
edge->bside[CLIP]= parity[CLIP];
edge->bside[SUBJ]= parity[SUBJ];
/* Determine contributing status and quadrant occupancies */
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;
}
/* Update parity */
parity[CLIP]^= edge->bundle[ABOVE][CLIP];
parity[SUBJ]^= edge->bundle[ABOVE][SUBJ];
/* Update horizontal state */
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;
} /* End of switch */
} /* End of contributing conditional */
} /* End of edge exists conditional */
} /* End of AET loop */
/* Delete terminating edges from the AET, otherwise compute xt */
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;
/* Copy bundle head state to the adjacent tail edge if required */
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)
{
/* === SCANBEAM INTERIOR PROCESSING ============================== */
build_intersection_table(&it, aet, dy);
/* Process each node in the intersection table */
for (intersect= it; intersect; intersect= intersect->next)
{
e0= intersect->ie[0];
e1= intersect->ie[1];
/* Only generate output for contributing intersections */
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]);
/* Determine quadrant occupancies */
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;
} /* End of switch */
} /* End of contributing intersection conditional */
/* Swap bundle sides in response to edge crossing */
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];
/* Swap e0 and e1 bundles in the AET */
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;
} /* End of IT loop*/
/* Prepare for next scanbeam */
for (edge= aet; edge; edge= next_edge)
{
next_edge= edge->next;
succ_edge= edge->succ;
if ((edge->top.y == yt) && succ_edge)
{
/* Replace AET edge by its successor */
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
{
/* Update this edge */
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;
}
}
} /* === END OF SCANBEAM PROCESSING ================================== */
/* Generate result polygon from out_poly */
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);
}
}
/* Tidy up */
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;
/* Test for trivial NULL result cases */
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;
}
/* Identify potentialy contributing contours */
if (((op == GPC_INT) || (op == GPC_DIFF))
&& (subj->num_contours > 0) && (clip->num_contours > 0))
minimax_test(subj, clip, op);
/* Build LMT */
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);
/* Return a NULL result if no contours contribute */
if (lmt == NULL)
{
result->num_strips= 0;
result->strip= NULL;
reset_lmt(&lmt);
FREE(s_heap);
FREE(c_heap);
return;
}
/* Build scanbeam table from scanbeam tree */
MALLOC(sbt, sbt_entries * sizeof(double), "sbt creation", double);
build_sbt(&scanbeam, sbt, sbtree);
scanbeam= 0;
free_sbtree(&sbtree);
/* Invert clip polygon for difference operation */
if (op == GPC_DIFF)
parity[CLIP]= RIGHT;
local_min= lmt;
/* Process each scanbeam */
while (scanbeam < sbt_entries)
{
/* Set yb and yt to the bottom and top of the scanbeam */
yb= sbt[scanbeam++];
if (scanbeam < sbt_entries)
{
yt= sbt[scanbeam];
dy= yt - yb;
}
/* === SCANBEAM BOUNDARY PROCESSING ================================ */
/* If LMT node corresponding to yb exists */
if (local_min)
{
if (local_min->y == yb)
{
/* Add edges starting at this local minimum to the AET */
for (edge= local_min->first_bound; edge; edge= edge->next_bound)
add_edge_to_aet(&aet, edge, NULL);
local_min= local_min->next;
}
}
/* Set dummy previous x value */
px= -DBL_MAX;
/* Create bundles within AET */
e0= aet;
e1= aet;
/* Set up bundle fields of first edge */
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)
{
/* Set up bundle fields of next edge */
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;
/* Bundle edges above the scanbeam boundary if they coincide */
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;
/* Process each edge at this scanbeam boundary */
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])
{
/* Set bundle side */
edge->bside[CLIP]= parity[CLIP];
edge->bside[SUBJ]= parity[SUBJ];
/* Determine contributing status and quadrant occupancies */
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;
}
/* Update parity */
parity[CLIP]^= edge->bundle[ABOVE][CLIP];
parity[SUBJ]^= edge->bundle[ABOVE][SUBJ];
/* Update horizontal state */
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;
} /* End of switch */
} /* End of contributing conditional */
} /* End of edge exists conditional */
} /* End of AET loop */
/* Delete terminating edges from the AET, otherwise compute xt */
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;
/* Copy bundle head state to the adjacent tail edge if required */
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)
{
/* === SCANBEAM INTERIOR PROCESSING ============================== */
build_intersection_table(&it, aet, dy);
/* Process each node in the intersection table */
for (intersect= it; intersect; intersect= intersect->next)
{
e0= intersect->ie[0];
e1= intersect->ie[1];
/* Only generate output for contributing intersections */
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]);
/* Determine quadrant occupancies */
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;
} /* End of switch */
} /* End of contributing intersection conditional */
/* Swap bundle sides in response to edge crossing */
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];
/* Swap e0 and e1 bundles in the AET */
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;
} /* End of IT loop*/
/* Prepare for next scanbeam */
for (edge= aet; edge; edge= next_edge)
{
next_edge= edge->next;
succ_edge= edge->succ;
if ((edge->top.y == yt) && succ_edge)
{
/* Replace AET edge by its successor */
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
{
/* Update this edge */
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;
}
}
} /* === END OF SCANBEAM PROCESSING ================================== */
/* Generate result tristrip from tlist */
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)
{
/* Valid tristrip: copy the vertices and free the heap */
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
{
/* Invalid tristrip: just free the heap */
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);
}
}
/* Tidy up */
reset_it(&it);
reset_lmt(&lmt);
FREE(c_heap);
FREE(s_heap);
FREE(sbt);
}
/*
===========================================================================
End of file: gpc.c
===========================================================================
*/
/*
===========================================================================
Project: Generic Polygon Clipper
A new algorithm for calculating the difference, intersection,
exclusive-or or union of arbitrary polygon sets.
File: gpc.h
Author: Alan Murta (email: gpc@cs.man.ac.uk)
Version: 2.32
Date: 17th December 2004
Copyright: (C) Advanced Interfaces Group,
University of Manchester.
This software is free for non-commercial use. It may be copied,
modified, and redistributed provided that this copyright notice
is preserved on all copies. The intellectual property rights of
the algorithms used reside with the University of Manchester
Advanced Interfaces Group.
You may not use this software, in whole or in part, in support
of any commercial product without the express consent of the
author.
There is no warranty or other guarantee of fitness of this
software for any purpose. It is provided solely "as is".
===========================================================================
*/
#ifndef __gpc_h
#define __gpc_h
#include <stdio.h>
/*
===========================================================================
Constants
===========================================================================
*/
/* Increase GPC_EPSILON to encourage merging of near coincident edges */
#define GPC_EPSILON (DBL_EPSILON)
#define GPC_VERSION "2.32"
/*
===========================================================================
Public Data Types
===========================================================================
*/
typedef enum /* Set operation type */
{
GPC_DIFF, /* Difference */
GPC_INT, /* Intersection */
GPC_XOR, /* Exclusive or */
GPC_UNION /* Union */
} gpc_op;
typedef struct /* Polygon vertex structure */
{
double x; /* Vertex x component */
double y; /* vertex y component */
} gpc_vertex;
typedef struct /* Vertex list structure */
{
int num_vertices; /* Number of vertices in list */
gpc_vertex *vertex; /* Vertex array pointer */
} gpc_vertex_list;
typedef struct /* Polygon set structure */
{
int num_contours; /* Number of contours in polygon */
int *hole; /* Hole / external contour flags */
gpc_vertex_list *contour; /* Contour array pointer */
} gpc_polygon;
typedef struct /* Tristrip set structure */
{
int num_strips; /* Number of tristrips */
gpc_vertex_list *strip; /* Tristrip array pointer */
} gpc_tristrip;
/*
===========================================================================
Public Function Prototypes
===========================================================================
*/
void gpc_read_polygon (FILE *infile_ptr,
int read_hole_flags,
gpc_polygon *polygon);
void gpc_write_polygon (FILE *outfile_ptr,
int write_hole_flags,
gpc_polygon *polygon);
void gpc_add_contour (gpc_polygon *polygon,
gpc_vertex_list *contour,
int hole);
void gpc_polygon_clip (gpc_op set_operation,
gpc_polygon *subject_polygon,
gpc_polygon *clip_polygon,
gpc_polygon *result_polygon);
void gpc_tristrip_clip (gpc_op set_operation,
gpc_polygon *subject_polygon,
gpc_polygon *clip_polygon,
gpc_tristrip *result_tristrip);
void gpc_polygon_to_tristrip (gpc_polygon *polygon,
gpc_tristrip *tristrip);
void gpc_free_polygon (gpc_polygon *polygon);
void gpc_free_tristrip (gpc_tristrip *tristrip);
#endif
/*
===========================================================================
End of file: gpc.h
===========================================================================
*/
Generic Polygon Clipper (gpc) Revision History
==============================================
v2.32 17th Dec 2004
---------------------
Fixed occasional memory leak occurring when processing some
degenerate polygon arrangements.
Added explicit type casting to memory allocator in support of
increased code portability.
v2.31 4th Jun 1999
---------------------
Separated edge merging measure based on a user-defined GPC_EPSILON
value from general numeric equality testing and ordering, which now
uses direct arithmetic comparison rather an EPSILON based proximity
test.
Fixed problem with numerical equality test during construction of
local minima and scanbeam tables, leading to occasional crash.
Fixed hole array memory leak in gpc_add_contour.
Fixed uninitialised hole field bug in gpc_polygon_clip result.
v2.30 11th Apr 1999
---------------------
Major re-write.
Minor API change: additional 'hole' array field added to gpc_polygon
datatype to indicate which constituent contours are internal holes,
and which form external boundaries.
Minor API change: additional 'hole' argument to gpc_add_contour
to indicate whether the new contour is a hole or external contour.
Minor API change: additional parameter to gpc_read_polygon and
gpc_write_polygon to indicate whether or not to read or write
contour hole flags.
Fixed NULL pointer bug in add/merge left/right operations.
Fixed numerical problem in intersection table generation.
Fixed zero byte malloc problem.
Fixed problem producing occasional 2 vertex contours.
Added bounding box test optimisations.
Simplified edge bundle creation, detection of scanbeam internal
edge intersections and tristrip scanbeam boundary code.
Renamed 'class' variable to be C++ friendly.
v2.22 17th Oct 1998
---------------------
Re-implemented edge interpolation and intersection calculations
to improve numerical robustness.
Simplified setting of GPC_EPSILON.
v2.21 19th Aug 1998
---------------------
Fixed problem causing occasional incorrect output when processing
self-intersecting polygons (bow-ties etc).
Removed bug which may lead to non-generation of uppermost triangle
in tristrip output.
v2.20 26th May 1998
---------------------
Major re-write.
Added exclusive-or polygon set operation.
Replaced table-based processing of edge intersections with
rule-based system.
Replaced two-pass approach to scanbeam interior processing with
single pass method.
v2.10a 14th May 1998
---------------------
Minor bug-fixes to counter some v2.10 reliability problems.
v2.10 11th May 1998
---------------------
Major re-write.
Incorporated edge bundle processing of AET to overcome coincident
edge problems present in previous releases.
Replaced Vatti's method for processing scanbeam interior regions
with an adapted version of the scanbeam boundary processing
algorithm.
v2.02 16th Apr 1998 (unreleased)
----------------------------------
Fixed internal minimum vertex duplication in gpc_polygon_clip
result.
Improved line intersection code discourage superfluous
intersections near line ends.
Removed limited precision number formatting in gpc_write_polygon.
Modification to allow subject or clip polygon to be reused as the
result in gpc_polygon_clip without memory leakage.
v2.01 23rd Feb 1998
---------------------
Removed bug causing duplicated vertices in output polygon.
Fixed scanbeam table index overrun problem.
v2.00 25th Nov 1997
---------------------
Major re-write.
Replaced temporary horizontal edge work-around (using tilting)
with true horizontal edge handling.
Trapezoidal output replaced by tristrips.
gpc_op constants now feature a `GPC_' prefix.
Data structures now passed by reference to gpc functions.
Replaced AET search by proxy addressing in polygon table.
Eliminated most (all?) coincident vertex / edge crashes.
v1.02 18th Oct 1997 (unreleased)
----------------------------------
Significantly reduced number of mallocs in build_lmt.
Scanbeam table now built using heapsort rather than insertion
sort.
v1.01 12th Oct 1997
---------------------
Fixed memory leak during output polygon build in
gpc_clip_polygon.
Removed superfluous logfile debug code.
Commented out malloc counts.
Added missing horizontal edge tilt-correction code in
gpc_clip_polygon.
v1.00 8th Oct 1997
--------------------
First release.
/**********************/
/* Some usual defines */
/**********************/
#ifndef DEFS_MACROS_H
#define DEFS_MACROS_H
#ifndef BOOL
#define BOOL bool
#endif
#ifndef FALSE
#define FALSE false
#endif
#ifndef TRUE
#define TRUE true
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef abs
#define abs(x) (((x) >=0) ? (x) : (-(x)))
#endif
#define TRACE printf
#define ASSERT(x) // todo : change to DEBUG, under wxWidgets
#endif // ifndef DEFS_MACROS_H
links to software relative to polygons (clipping and and other operations)
used in freePCB (Written by Alan Wright)
gpc (here: GenericPolygonClipperLibrary.cpp)
http://www.cs.man.ac.uk/~toby/alan/software/gpc.html
polygon.php (ported in "C++" by Alan Wright)
the c++ corresponding file is php_polygon.cpp
http://www.phpclasses.org/browse/file/10683.html
used in gpcb:
polygon1.c:
http://www.koders.com/c/
and for this file:
http://www.koders.com/c/fidE26CF2236C2DF7E435D597390A05B982EDFB4C38.aspx
gpcb uses a modified file (integer coordinates)
WXDIR = $(WXWIN)
TARGET = lib_polygon.a
all: $(TARGET)
include ../libs.win
include makefile.include
$(TARGET): $(OBJECTS) ../libs.win makefile.include
ar ruv $@ $(OBJECTS)
ranlib $@
clean:
rm -f *.bak
rm -f *.o
rm -f $(TARGET)
## Makefile for common.a
CC = gcc
include ../libs.linux
# Compiler flags.
CPPFLAGS += -I./ -I../include
EDACPPFLAGS = $(CPPFLAGS)
TARGET = lib_polygon.a
all: $(TARGET)
deps:
$(CXX) $(CPPFLAGS) -E -MMD -MG *.cpp >/dev/null
include makefile.include
-include *.d
CPPFLAGS += $(EXTRACPPFLAGS) -fno-strict-aliasing
EDACPPFLAGS = $(CPPFLAGS)
$(TARGET): $(OBJECTS) makefile.gtk makefile.include
rm -f $@
ar -rv $@ $(OBJECTS)
ranlib $@
install:$(TARGET)
clean:
rm -f *.o *~ core *.bak *.obj *.d
rm -f $(TARGET)
EXTRACPPFLAGS += -I$(SYSINCLUDE) -I./ -Ibitmaps -I../include
COMMON =
OBJECTS= \
GenericPolygonClipperLibrary.o \
php_polygon.o\
php_polygon_vertex.o
GenericPolygonClipperLibrary.o: GenericPolygonClipperLibrary.cpp GenericPolygonClipperLibrary.h
php_polygon.o: php_polygon.cpp php_polygon.h php_polygon_vertex.h defs-macros.h
#polygon1.o: polygon1.cpp polyarea.h vectmatr.h
## Makefile for common.a
include ../libs.macosx
TARGET = lib_polygon.a
all: $(TARGET)
deps:
$(CXX) $(CPPFLAGS) -E -MMD -MG *.cpp >/dev/null
include makefile.include
-include *.d
CPPFLAGS += $(EXTRACPPFLAGS)
EDACPPFLAGS = $(CPPFLAGS)
$(TARGET): $(OBJECTS) makefile.macosx makefile.include
rm -f $@
ar -rv $@ $(OBJECTS)
ranlib $@
clean:
rm -f *.o; rm -f *~
rm -f $(TARGET)
// file php_polygon.cpp
// This is a port of a php class written by Brenor Brophy (see below)
/*------------------------------------------------------------------------------
** File: polygon.php
** Description: PHP class for a polygon.
** Version: 1.1
** Author: Brenor Brophy
** Email: brenor at sbcglobal dot net
** Homepage: www.brenorbrophy.com
**------------------------------------------------------------------------------
** COPYRIGHT (c) 2005 BRENOR BROPHY
**
** The source code included in this package is free software; you can
** redistribute it and/or modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation. This license can be
** read at:
**
** http://www.opensource.org/licenses/gpl-license.php
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
**------------------------------------------------------------------------------
**
** Based on the paper "Efficient Clipping of Arbitary Polygons" by Gunther
** Greiner (greiner at informatik dot uni-erlangen dot de) and Kai Hormann
** (hormann at informatik dot tu-clausthal dot de), ACM Transactions on Graphics
** 1998;17(2):71-83.
**
** Available at: www.in.tu-clausthal.de/~hormann/papers/clipping.pdf
**
** Another useful site describing the algorithm and with some example
** C code by Ionel Daniel Stroe is at:
**
** http://davis.wpi.edu/~matt/courses/clipping/
**
** The algorithm is extended by Brenor Brophy to allow polygons with
** arcs between vertices.
**
** Rev History
** -----------------------------------------------------------------------------
** 1.0 08/25/2005 Initial Release
** 1.1 09/04/2005 Added Move(), Rotate(), isPolyInside() and bRect() methods.
** Added software license language to header comments
*/
//#include "stdafx.h"
#include <stdio.h>
#include <math.h>
#include "php_polygon_vertex.h"
#include "php_polygon.h"
const double PT = 0.99999;
//const double eps = (1.0 - PT)/10.0;
const double eps = 0.0;
polygon::polygon( vertex * first )
{
m_first = first;
m_cnt = 0;
}
polygon::~polygon()
{
while( m_cnt > 1 )
{
vertex * v = getFirst();
del( v->m_nextV );
}
if( m_first )
{
delete m_first;
}
}
vertex * polygon::getFirst()
{
return m_first;
}
polygon * polygon::NextPoly()
{
return m_first->NextPoly();
}
/*
** Add a vertex object to the polygon (vertex is added at the "end" of the list)
** Which because polygons are closed lists means it is added just before the first
** vertex.
*/
void polygon::add( vertex * nv )
{
if ( m_cnt == 0 ) // If this is the first vertex in the polygon
{
m_first = nv; // Save a reference to it in the polygon
m_first->setNext(nv); // Set its pointer to point to itself
m_first->setPrev(nv); // because it is the only vertex in the list
segment * ps = m_first->Nseg(); // Get ref to the Next segment object
m_first->setPseg(ps); // and save it as Prev segment as well
}
else // At least one other vertex already exists
{
// p <-> nv <-> n
// ps ns
vertex * n = m_first; // Get a ref to the first vertex in the list
vertex * p = n->Prev(); // Get ref to previous vertex
n->setPrev(nv); // Add at end of list (just before first)
nv->setNext(n); // link the new vertex to it
nv->setPrev(p); // link to the pervious EOL vertex
p->setNext(nv); // And finally link the previous EOL vertex
// Segments
segment * ns = nv->Nseg(); // Get ref to the new next segment
segment * ps = p->Nseg(); // Get ref to the previous segment
n->setPseg(ns); // Set new previous seg for m_first
nv->setPseg(ps); // Set previous seg of the new vertex
}
m_cnt++; // Increment the count of vertices
}
/*
** Create a vertex and then add it to the polygon
*/
void polygon::addv ( double x, double y,
double xc, double yc, int d )
{
vertex * nv = new vertex( x, y, xc, yc, d );
add( nv );
}
/*
** Delete a vertex object from the polygon. This is not used by the main algorithm
** but instead is used to clean-up a polygon so that a second boolean operation can
** be performed.
*/
vertex * polygon::del( vertex * v )
{
// p <-> v <-> n Will delete v and ns
// ps ns
vertex * p = v->Prev(); // Get ref to previous vertex
vertex * n = v->Next(); // Get ref to next vertex
p->setNext(n); // Link previous forward to next
n->setPrev(p); // Link next back to previous
// Segments
segment * ps = p->Nseg(); // Get ref to previous segment
segment * ns = v->Nseg(); // Get ref to next segment
n->setPseg(ps); // Link next back to previous segment
delete ns; //AMW
v->m_nSeg = NULL; // AMW
delete v; //AMW
// ns = NULL;
// v = NULL; // Free the memory
m_cnt--; // One less vertex
return n; // Return a ref to the next valid vertex
}
/*
** Reset Polygon - Deletes all intersection vertices. This is used to
** restore a polygon that has been processed by the boolean method
** so that it can be processed again.
*/
void polygon::res()
{
vertex * v = getFirst(); // Get the first vertex
do
{
v = v->Next(); // Get the next vertex in the polygon
while (v->isIntersect()) // Delete all intersection vertices
v = del(v);
}
while (v->id() != m_first->id());
}
/*
** Copy Polygon - Returns a reference to a new copy of the poly object
** including all its vertices & their segments
*/
polygon * polygon::copy_poly()
{
polygon * n = new polygon; // Create a new instance of this class
vertex * v = getFirst();
do
{
n->addv(v->X(),v->Y(),v->Xc(),v->Yc(),v->d());
v = v->Next();
}
while (v->id() != m_first->id());
return n;
}
/*
** Insert and Sort a vertex between a specified pair of vertices (start and end)
**
** This function inserts a vertex (most likely an intersection point) between two
** other vertices. These other vertices cannot be intersections (that is they must
** be actual vertices of the original polygon). If there are multiple intersection
** points between the two vertices then the new vertex is inserted based on its
** alpha value.
*/
void polygon::insertSort( vertex * nv, vertex * s, vertex * e )
{
vertex * c = s; // Set current to the starting vertex
// Move current past any intersections
// whose alpha is lower but don't go past
// the end vertex
while( c->id() != e->id() && c->Alpha() < nv->Alpha() )
c = c->Next();
// p <-> nv <-> c
nv->setNext(c); // Link new vertex forward to curent one
vertex * p = c->Prev(); // Get a link to the previous vertex
nv->setPrev(p); // Link the new vertex back to the previous one
p->setNext(nv); // Link previous vertex forward to new vertex
c->setPrev(nv); // Link current vertex back to the new vertex
// Segments
segment * ps = p->Nseg();
nv->setPseg(ps);
segment * ns = nv->Nseg();
c->setPseg(ns);
m_cnt++; // Just added a new vertex
}
/*
** return the next non intersecting vertex after the one specified
*/
vertex * polygon::nxt( vertex * v )
{
vertex * c = v; // Initialize current vertex
while (c && c->isIntersect()) // Move until a non-intersection
c = c->Next(); // vertex if found
return c; // return that vertex
}
/*
** Check if any unchecked intersections remain in the polygon. The boolean
** method is complete when all intersections have been checked.
*/
BOOL polygon::unckd_remain()
{
BOOL remain = FALSE;
vertex * v = m_first;
do
{
if (v->isIntersect() && !v->isChecked())
remain = TRUE; // Set if an unchecked intersection is found
v = v->Next();
}
while (v->id() != m_first->id());
return remain;
}
/*
** Return a ref to the first unchecked intersection point in the polygon.
** If none are found then just the first vertex is returned.
*/
vertex * polygon::first_unckd_intersect()
{
vertex * v = m_first;
do // Do-While
{ // Not yet reached end of the polygon
v = v->Next(); // AND the vertex if NOT an intersection
} // OR it IS an intersection, but has been checked already
while(v->id() != m_first->id() && ( !v->isIntersect() || ( v->isIntersect() && v->isChecked() ) ) );
return v;
}
/*
** Return the distance between two points
*/
double polygon::dist( double x1, double y1, double x2, double y2 )
{
return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
}
/*
** Calculate the angle between 2 points, where Xc,Yc is the center of a circle
** and x,y is a point on its circumference. All angles are relative to
** the 3 O'Clock position. Result returned in radians
*/
double polygon::angle( double xc, double yc, double x1, double y1 )
{
double d = dist(xc, yc, x1, y1); // calc distance between two points
double a1;
if ( asin( (y1-yc)/d ) >= 0 )
a1 = acos( (x1-xc)/d );
else
a1 = 2*PI - acos( (x1-xc)/d );
return a1;
}
/*
** Return Alpha value for an Arc
**
** X1/Y1 & X2/Y2 are the end points of the arc, Xc/Yc is the center & Xi/Yi
** the intersection point on the arc. d is the direction of the arc
*/
double polygon::aAlpha( double x1, double y1, double x2, double y2,
double xc, double yc, double xi, double yi, double d )
{
double sa = angle(xc, yc, x1, y1); // Start Angle
double ea = angle(xc, yc, x2, y2); // End Angle
double ia = angle(xc, yc, xi, yi); // Intersection Angle
double arc, aint;
if (d == 1) // Anti-Clockwise
{
arc = ea - sa;
aint = ia - sa;
}
else // Clockwise
{
arc = sa - ea;
aint = sa - ia;
}
if (arc < 0)
arc += 2*PI;
if (aint < 0)
aint += 2*PI;
double a = aint/arc;
return a;
}
/*
** This function handles the degenerate case where a vertex of one
** polygon lies directly on an edge of the other. This case can
** also occur during the isInside() function, where the search
** line exactly intersects with a vertex. The function works
** by shortening the line by a tiny amount.
*/
void polygon::perturb( vertex * p1, vertex * p2, vertex * q1, vertex * q2,
double aP, double aQ )
{
// if (aP == 0) // Move vertex p1 closer to p2
if( abs(aP) <= eps ) // Move vertex p1 closer to p2
{
p1->setX(p1->X() + (1-PT) * (p2->X() - p1->X()));
p1->setY(p1->Y() + (1-PT) * (p2->Y() - p1->Y()));
}
// else if (aP == 1) // Move vertex p2 closer to p1
else if( abs(1-aP) <= eps ) // Move vertex p2 closer to p1
{
p2->setX(p1->X() + PT * (p2->X() - p1->X()));
p2->setY(p1->Y() + PT * (p2->Y() - p1->Y()));
}
//** else if (aQ == 0) // Move vertex q1 closer to q2
if( abs(aQ) <= eps ) // Move vertex q1 closer to q2
{
q1->setX(q1->X() + (1-PT) * (q2->X() - q1->X()));
q1->setY(q1->Y() + (1-PT) * (q2->Y() - q1->Y()));
}
//** else if (aQ == 1) // Move vertex q2 closer to q1
else if( abs(1-aQ) <= eps ) // Move vertex q2 closer to q1
{
q2->setX(q1->X() + PT * (q2->X() - q1->X()));
q2->setY(q1->Y() + PT * (q2->Y() - q1->Y()));
}
}
/*
** Determine the intersection between two pairs of vertices p1/p2, q1/q2
**
** Either or both of the segments passed to this function could be arcs.
** Thus we must first determine if the intersection is line/line, arc/line
** or arc/arc. Then apply the correct math to calculate the intersection(s).
**
** Line/Line can have 0 (no intersection) or 1 intersection
** Line/Arc and Arc/Arc can have 0, 1 or 2 intersections
**
** The function returns TRUE is any intersections are found
** The number found is returned in n
** The arrays ix[], iy[], alphaP[] & alphaQ[] return the intersection points
** and their associated alpha values.
*/
BOOL polygon::ints( vertex * p1, vertex * p2, vertex * q1, vertex * q2,
int * n, double ix[], double iy[], double alphaP[], double alphaQ[] )
{
BOOL found = FALSE;
*n = 0; // No intersections found yet
int pt = p1->d();
int qt = q1->d(); // Do we have Arcs or Lines?
if (pt == 0 && qt == 0) // Is it line/Line ?
{
/* LINE/LINE
** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/
*/
double x1 = p1->X();
double y1 = p1->Y();
double x2 = p2->X();
double y2 = p2->Y();
double x3 = q1->X();
double y3 = q1->Y();
double x4 = q2->X();
double y4 = q2->Y();
double d = ((y4-y3)*(x2-x1)-(x4-x3)*(y2-y1));
if (d != 0)
{ // The lines intersect at a point somewhere
double ua = ((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/d;
double ub = ((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/d;
TRACE( " ints: ua = %.17f, ub = %.17f\n", ua, ub );
// The values of $ua and $ub tell us where the intersection occurred.
// A value between 0 and 1 means the intersection occurred within the
// line segment.
// A value less than 0 or greater than 1 means the intersection occurred
// outside the line segment
// A value of exactly 0 or 1 means the intersection occurred right at the
// start or end of the line segment. For our purposes we will consider this
// NOT to be an intersection and we will move the vertex a tiny distance
// away from the intersecting line.
// if( ua == 0 || ua == 1 || ub == 0 || ub == 1 )
if( abs(ua)<=eps || abs(1.0-ua)<=eps || abs(ub)<=eps || abs(1.0-ub)<=eps )
{
// Degenerate case - vertex touches a line
perturb(p1, p2, q1, q2, ua, ub);
//** for testing, see if we have successfully resolved the degeneracy
{
double tx1 = p1->X();
double ty1 = p1->Y();
double tx2 = p2->X();
double ty2 = p2->Y();
double tx3 = q1->X();
double ty3 = q1->Y();
double tx4 = q2->X();
double ty4 = q2->Y();
double td = ((ty4-ty3)*(tx2-tx1)-(tx4-tx3)*(ty2-ty1));
if (td != 0)
{
// The lines intersect at a point somewhere
double tua = ((tx4-tx3)*(ty1-ty3)-(ty4-ty3)*(tx1-tx3))/td;
double tub = ((tx2-tx1)*(ty1-ty3)-(ty2-ty1)*(tx1-tx3))/td;
if( abs(tua)<=eps || abs(1.0-tua)<=eps || abs(tub)<=eps || abs(1.0-tub)<=eps )
ASSERT(0);
else if( (tua > 0 && tua < 1) && (tub > 0 && tub < 1) )
ASSERT(0);
TRACE( " perturb:\n new s = (%f,%f) to (%f,%f)\n new c = (%f,%f) to (%f,%f)\n new ua = %.17f, ub = %.17f\n",
tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4, tua, tub );
}
}
//** end test
found = FALSE;
}
else if ((ua > 0 && ua < 1) && (ub > 0 && ub < 1))
{
// Intersection occurs on both line segments
double x = x1 + ua*(x2-x1);
double y = y1 + ua*(y2-y1);
iy[0] = y;
ix[0] = x;
alphaP[0] = ua;
alphaQ[0] = ub;
*n = 1;
found = TRUE;
}
else
{
// The lines do not intersect
found = FALSE;
}
}
else
{
// The lines do not intersect (they are parallel)
found = FALSE;
}
} // End of find Line/Line intersection
else if (pt != 0 && qt != 0) // Is it Arc/Arc?
{
/* ARC/ARC
** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/2circle/
*/
double x0 = p1->Xc();
double y0 = p1->Yc(); // Center of first Arc
double r0 = dist(x0,y0,p1->X(),p1->Y()); // Calc the radius
double x1 = q1->Xc();
double y1 = q1->Yc(); // Center of second Arc
double r1 = dist(x1,y1,q1->X(),q1->Y()); // Calc the radius
double dx = x1 - x0; // dx and dy are the vertical and horizontal
double dy = y1 - y0; // distances between the circle centers.
double d = sqrt((dy*dy) + (dx*dx)); // Distance between the centers.
if(d > (r0 + r1)) // Check for solvability.
{ // no solution. circles do not intersect.
found = FALSE;
}
else if(d < abs(r0 - r1) )
{ // no solution. one circle inside the other
found = FALSE;
}
else
{
/*
** 'xy2' is the point where the line through the circle intersection
** points crosses the line between the circle centers.
*/
double a = ((r0*r0)-(r1*r1)+(d*d))/(2.0*d); // Calc the distance from xy0 to xy2.
double x2 = x0 + (dx * a/d); // Determine the coordinates of xy2.
double y2 = y0 + (dy * a/d);
if (d == (r0 + r1)) // Arcs touch at xy2 exactly (unlikely)
{
alphaP[0] = aAlpha(p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x2, y2, pt);
alphaQ[0] = aAlpha(q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x2, y2, qt);
if ((alphaP[0] >0 && alphaP[0] < 1) && (alphaQ[0] >0 && alphaQ[0] < 1))
{
ix[0] = x2;
iy[0] = y2;
*n = 1; found = TRUE;
}
}
else // Arcs intersect at two points
{
double alP[2], alQ[2];
double h = sqrt((r0*r0) - (a*a)); // Calc the distance from xy2 to either
// of the intersection points.
double rx = -dy * (h/d); // Now determine the offsets of the
double ry = dx * (h/d);
// intersection points from xy2
double x[2], y[2];
x[0] = x2 + rx; x[1] = x2 - rx; // Calc the absolute intersection points.
y[0] = y2 + ry; y[1] = y2 - ry;
alP[0] = aAlpha(p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x[0], y[0], pt);
alQ[0] = aAlpha(q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x[0], y[0], qt);
alP[1] = aAlpha(p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x[1], y[1], pt);
alQ[1] = aAlpha(q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x[1], y[1], qt);
for (int i=0; i<=1; i++)
if ((alP[i] >0 && alP[i] < 1) && (alQ[i] >0 && alQ[i] < 1))
{
ix[*n] = x[i];
iy[*n] = y[i];
alphaP[*n] = alP[i];
alphaQ[*n] = alQ[i];
*n++;
found = TRUE;
}
}
}
} // End of find Arc/Arc intersection
else // It must be Arc/Line
{
/* ARC/LINE
** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/sphereline/
*/
double d, x1, x2, xc, xs, xe;
double y1, y2, yc, ys, ye;
if (pt == 0) // Segment p1,p2 is the line
{ // Segment q1,q2 is the arc
x1 = p1->X();
y1 = p1->Y();
x2 = p2->X();
y2 = p2->Y();
xc = q1->Xc();
yc = q1->Yc();
xs = q1->X();
ys = q1->Y();
xe = q2->X();
ye = q2->Y();
d = qt;
}
else // Segment q1,q2 is the line
{ // Segment p1,p2 is the arc
x1 = q1->X(); y1 = q1->Y();
x2 = q2->X(); y2 = q2->Y();
xc = p1->Xc(); yc = p1->Yc();
xs = p1->X(); ys = p1->Y();
xe = p2->X(); ye = p2->Y();
d = pt;
}
double r = dist(xc,yc,xs,ys);
double a = pow((x2 - x1),2)+pow((y2 - y1),2);
double b = 2* ( (x2 - x1)*(x1 - xc)
+ (y2 - y1)*(y1 - yc) );
double c = pow(xc,2) + pow(yc,2) +
pow(x1,2) + pow(y1,2) -
2* ( xc*x1 + yc*y1) - pow(r,2);
double i = b * b - 4 * a * c;
if ( i < 0.0 ) // no intersection
{
found = FALSE;
}
else if ( i == 0.0 ) // one intersection
{
double mu = -b/(2*a);
double x = x1 + mu*(x2-x1);
double y = y1 + mu*(y2-y1);
double al = mu; // Line Alpha
double aa = this->aAlpha(xs, ys, xe, ye, xc, yc, x, y, d); // Arc Alpha
if ((al >0 && al <1)&&(aa >0 && aa <1))
{
ix[0] = x; iy[0] = y;
*n = 1;
found = TRUE;
if (pt == 0)
{
alphaP[0] = al; alphaQ[0] = aa;
}
else
{
alphaP[0] = aa; alphaQ[0] = al;
}
}
}
else if ( i > 0.0 ) // two intersections
{
double mu[2], x[2], y[2], al[2], aa[2];
mu[0] = (-b + sqrt( pow(b,2) - 4*a*c )) / (2*a); // first intersection
x[0] = x1 + mu[0]*(x2-x1);
y[0] = y1 + mu[0]*(y2-y1);
mu[1] = (-b - sqrt(pow(b,2) - 4*a*c )) / (2*a); // second intersection
x[1] = x1 + mu[1]*(x2-x1);
y[1] = y1 + mu[1]*(y2-y1);
al[0] = mu[0];
aa[0] = aAlpha(xs, ys, xe, ye, xc, yc, x[0], y[0], d);
al[1] = mu[1];
aa[1] = aAlpha(xs, ys, xe, ye, xc, yc, x[1], y[1], d);
for (int i=0; i<=1; i++)
if ((al[i] >0 && al[i] < 1) && (aa[i] >0 && aa[i] < 1))
{
ix[*n] = x[i];
iy[*n] = y[i];
if (pt == 0)
{
alphaP[*n] = al[i];
alphaQ[*n] = aa[i];
}
else
{
alphaP[*n] = aa[i];
alphaQ[*n] = al[i];
}
*n++;
found = TRUE;
}
}
} // End of find Arc/Line intersection
return found;
} // end of intersect function
/*
** Test if a vertex lies inside the polygon
**
** This function calculates the "winding" number for the point. This number
** represents the number of times a ray emitted from the point to infinity
** intersects any edge of the polygon. An even winding number means the point
** lies OUTSIDE the polygon, an odd number means it lies INSIDE it.
**
** Right now infinity is set to -10000000, some people might argue that infinity
** actually is a bit bigger. Those people have no lives.
**
** Allan Wright 4/16/2006: I guess I have no life: I had to increase it to -1000000000
*/
BOOL polygon::isInside( vertex * v )
{
//** modified for testing
if( v->isIntersect() )
ASSERT(0);
int winding_number = 0;
int winding_number2 = 0;
int winding_number3 = 0;
int winding_number4 = 0;
//** vertex * point_at_infinity = new vertex(-10000000,v->Y()); // Create point at infinity
vertex * point_at_infinity = new vertex(-1000000000,-50000000); // Create point at infinity
vertex * point_at_infinity2 = new vertex(1000000000,+50000000); // Create point at infinity
vertex * point_at_infinity3 = new vertex(500000000,1000000000); // Create point at infinity
vertex * point_at_infinity4 = new vertex(-500000000,1000000000); // Create point at infinity
vertex * q = m_first; // End vertex of a line segment in polygon
do
{
if (!q->isIntersect())
{
int n;
double x[2], y[2], aP[2], aQ[2];
if( ints( point_at_infinity, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) )
winding_number += n; // Add number of intersections found
if( ints( point_at_infinity2, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) )
winding_number2 += n; // Add number of intersections found
if( ints( point_at_infinity3, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) )
winding_number3 += n; // Add number of intersections found
if( ints( point_at_infinity4, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) )
winding_number4 += n; // Add number of intersections found
}
q = q->Next();
}
while( q->id() != m_first->id() );
delete point_at_infinity;
delete point_at_infinity2;
if( winding_number%2 != winding_number2%2
|| winding_number3%2 != winding_number4%2
|| winding_number%2 != winding_number3%2 )
ASSERT(0);
if( winding_number%2 == 0 ) // Check even or odd
return FALSE; // even == outside
else
return TRUE; // odd == inside
}
/*
** Execute a Boolean operation on a polygon
**
** This is the key method. It allows you to AND/OR this polygon with another one
** (equvalent to a UNION or INTERSECT operation. You may also subtract one from
** the other (same as DIFFERENCE). Given two polygons A, B the following operations
** may be performed:
**
** A|B ... A OR B (Union of A and B)
** A&B ... A AND B (Intersection of A and B)
** A\B ... A - B
** B\A ... B - A
**
** A is the object and B is the polygon passed to the method.
*/
polygon * polygon::boolean( polygon * polyB, int oper )
{
polygon * last = NULL;
vertex * s = m_first; // First vertex of the subject polygon
vertex * c = polyB->getFirst(); // First vertex of the "clip" polygon
/*
** Phase 1 of the algoritm is to find all intersection points between the two
** polygons. A new vertex is created for each intersection and it is added to
** the linked lists for both polygons. The "neighbor" reference in each vertex
** stores the link between the same intersection point in each polygon.
*/
TRACE( "boolean...phase 1\n" );
do
{
TRACE( "s=(%f,%f) to (%f,%f) I=%d\n",
s->m_x, s->m_y, s->m_nextV->m_x, s->m_nextV->m_y, s->m_intersect );
if (!s->isIntersect())
{
do
{
TRACE( " c=(%f,%f) to (%f,%f) I=%d\n",
c->m_x, c->m_y, c->m_nextV->m_x, c->m_nextV->m_y, c->m_intersect );
if (!c->isIntersect())
{
int n;
double ix[2], iy[2], alphaS[2], alphaC[2];
BOOL bInt = ints(s, nxt(s->Next()),c, polyB->nxt(c->Next()), &n, ix, iy, alphaS, alphaC);
if( bInt )
{
TRACE( " int at (%f,%f) aS = %.17f, aC = %.17f\n", ix[0], iy[0], alphaS[0], alphaC[0] );
for (int i=0; i<n; i++)
{
vertex * is = new vertex(ix[i], iy[i], s->Xc(), s->Yc(), s->d(), NULL, NULL, NULL, TRUE, NULL, alphaS[i], FALSE, FALSE);
vertex * ic = new vertex(ix[i], iy[i], c->Xc(), c->Yc(), c->d(), NULL, NULL, NULL, TRUE, NULL, alphaC[i], FALSE, FALSE);
is->setNeighbor(ic);
ic->setNeighbor(is);
insertSort(is, s, this->nxt(s->Next()));
polyB->insertSort(ic, c, polyB->nxt(c->Next()));
}
}
} // end if c is not an intersect point
c = c->Next();
}
while (c->id() != polyB->m_first->id());
} // end if s not an intersect point
s = s->Next();
}
while(s->id() != m_first->id());
//** for testing...check number of intersections in each poly
TRACE( "boolean...phase 1 testing\n" );
int n_ints = 0;
s = m_first;
do
{
if( s->isIntersect() )
n_ints++;
s = s->Next();
} while( s->id() != m_first->id() );
int n_polyB_ints = 0;
s = polyB->m_first;
do
{
if( s->isIntersect() )
n_polyB_ints++;
s = s->Next();
} while( s->id() != polyB->m_first->id() );
if( n_ints != n_polyB_ints )
ASSERT(0);
if( n_ints%2 != 0 )
ASSERT(0);
//** end test
/*
** Phase 2 of the algorithm is to identify every intersection point as an
** entry or exit point to the other polygon. This will set the entry bits
** in each vertex object.
**
** What is really stored in the entry record for each intersection is the
** direction the algorithm should take when it arrives at that entry point.
** Depending in the operation requested (A&B, A|B, A/B, B/A) the direction is
** set as follows for entry points (f=foreward, b=Back), exit points are always set
** to the opposite:
** Enter Exit
** A B A B
** A|B b b f f
** A&B f f b b
** A\B b f f b
** B\A f b b f
**
** f = TRUE, b = FALSE when stored in the entry record
*/
BOOL A, B;
switch (oper)
{
case A_OR_B: A = FALSE; B = FALSE; break;
case A_AND_B: A = TRUE; B = TRUE; break;
case A_MINUS_B: A = FALSE; B = TRUE; break;
case B_MINUS_A: A = TRUE; B = FALSE; break;
default: A = TRUE; B = TRUE; break;
}
s = m_first;
//** testing
if( s->isIntersect() )
ASSERT(0);
//** end test
BOOL entry;
if (polyB->isInside(s)) // if we are already inside
entry = !A; // next intersection must be an exit
else // otherwise
entry = A; // next intersection must be an entry
do
{
if (s->isIntersect())
{
s->setEntry(entry);
entry = !entry;
}
s = s->Next();
}
while (s->id() != m_first->id());
/*
** Repeat for other polygon
*/
c = polyB->m_first;
if (this->isInside(c)) // if we are already inside
entry = !B; // next intersection must be an exit
else // otherwise
entry = B; // next intersection must be an entry
do
{
if (c->isIntersect())
{
c->setEntry(entry);
entry = !entry;
}
c = c->Next();
}
while (c->id() != polyB->m_first->id());
/*
** Phase 3 of the algorithm is to scan the linked lists of the
** two input polygons an construct a linked list of result
** polygons. We start at the first intersection then depending
** on whether it is an entry or exit point we continue building
** our result polygon by following the source or clip polygon
** either forwards or backwards.
*/
while (this->unckd_remain()) // Loop while unchecked intersections remain
{
vertex * v = first_unckd_intersect(); // Get the first unchecked intersect point
polygon * r = new polygon; // Create a new instance of that class
do
{
v->setChecked(); // Set checked flag true for this intersection
if (v->isEntry())
{
do
{
v = v->Next();
vertex * nv = new vertex(v->X(),v->Y(),v->Xc(),v->Yc(),v->d());
r->add(nv);
}
while (!v->isIntersect());
}
else
{
do
{
v = v->Prev();
vertex * nv = new vertex(v->X(),v->Y(),v->Xc(FALSE),v->Yc(FALSE),v->d(FALSE));
r->add(nv);
}
while (!v->isIntersect());
}
v = v->Neighbor();
}
while (!v->isChecked()); // until polygon closed
if (last) // Check in case first time thru the loop
r->m_first->setNextPoly(last); // Save ref to the last poly in the first vertex
// of this poly
last = r; // Save this polygon
} // end of while there is another intersection to check
/*
** Clean up the input polygons by deleting the intersection points
*/
res();
polyB->res();
/*
** It is possible that no intersection between the polygons was found and
** there is no result to return. In this case we make function fail
** gracefully as follows (depending on the requested operation):
**
** A|B : Return this with polyB in m_first->nextPoly
** A&B : Return this
** A\B : Return this
** B\A : return polyB
*/
polygon * p;
if (!last)
{
switch (oper)
{
case A_OR_B:
last = copy_poly();
p = polyB->copy_poly();
last->m_first->setNextPoly(p);
break;
case A_AND_B:
last = copy_poly();
break;
case A_MINUS_B:
last = copy_poly();
break;
case B_MINUS_A:
last = polyB->copy_poly();
break;
default:
last = copy_poly();
break;
}
}
else if (m_first->m_nextPoly)
{
last->m_first->m_nextPoly = m_first->NextPoly();
}
return last;
} // end of boolean function
/*
** Test if a polygon lies entirly inside this polygon
**
** First every point in the polygon is tested to determine if it is
** inside this polygon. If all points are inside, then the second
** test is performed that looks for any intersections between the
** two polygons. If no intersections are found then the polygon
** must be completely enclosed by this polygon.
*/
#if 0
function polygon::isPolyInside (p)
{
inside = TRUE;
c = p->getFirst(); // Get the first vertex in polygon p
do
{
if (!this->isInside(c)) // If vertex is NOT inside this polygon
inside = FALSE; // then set flag to false
c = c->Next(); // Get the next vertex in polygon p
}
while (c->id() != p->first->id());
if (inside)
{
c = p->getFirst(); // Get the first vertex in polygon p
s = getFirst(); // Get the first vertex in this polygon
do
{
do
{
if (this->ints(s, s->Next(),c, c->Next(), n, x, y, aS, aC))
inside = FALSE;
c = c->Next();
}
while (c->id() != p->first->id());
s = s->Next();
}
while (s->id() != m_first->id());
}
return inside;
} // end of isPolyInside
/*
** Move Polygon
**
** Translates polygon by delta X and delta Y
*/
function polygon::move (dx, dy)
{
v = getFirst();
do
{
v->setX(v->X() + dx);
v->setY(v->Y() + dy);
if (v->d() != 0)
{
v->setXc(v->Xc() + dx);
v->setYc(v->Yc() + dy);
}
v = v->Next();
}
while(v->id() != m_first->id());
} // end of move polygon
/*
** Rotate Polygon
**
** Rotates a polgon about point xr/yr by a radians
*/
function polygon::rotate (xr, yr, a)
{
this->move(-xr,-yr); // Move the polygon so that the point of
// rotation is at the origin (0,0)
if (a < 0) // We might be passed a negitive angle
a += 2*pi(); // make it positive
v = m_first;
do
{
x=v->X(); y=v->Y();
v->setX(x*cos(a) - y*sin(a)); // x' = xCos(a)-ySin(a)
v->setY(x*sin(a) + y*cos(a)); // y' = xSin(a)+yCos(a)
if (v->d() != 0)
{
x=v->Xc(); y=v->Yc();
v->setXc(x*cos(a) - y*sin(a));
v->setYc(x*sin(a) + y*cos(a));
}
v = v->Next();
}
while(v->id() != m_first->id());
this->move(xr,yr); // Move the rotated polygon back
} // end of rotate polygon
/*
** Return Bounding Rectangle for a Polygon
**
** returns a polygon object that represents the bounding rectangle
** for this polygon. Arc segments are correctly handled.
*/
function polygon::&bRect ()
{
minX = INF; minY = INF; maxX = -INF; maxY = -INF;
v = m_first;
do
{
if (v->d() != 0) // Is it an arc segment
{
vn = v->Next(); // end vertex of the arc segment
v1 = new vertex(v->Xc(), -infinity); // bottom point of vertical line thru arc center
v2 = new vertex(v->Xc(), +infinity); // top point of vertical line thru arc center
if (this->ints(v, vn, v1, v2, n, x, y, aS, aC)) // Does line intersect the arc ?
{
for (i=0; i<n; i++) // check y portion of all intersections
{
minY = min(minY, y[i], v->Y());
maxY = max(maxY, y[i], v->Y());
}
}
else // There was no intersection so bounding rect is determined
{ // by the start point only, not teh edge of the arc
minY = min(minY, v->Y());
maxY = max(maxY, v->Y());
}
v1 = NULL; v2 = NULL; // Free the memory used
h1 = new vertex(-infinity, v->Yc()); // left point of horozontal line thru arc center
h2 = new vertex(+infinity, v->Yc()); // right point of horozontal line thru arc center
if (this->ints(v, vn, h1, h2, n, x, y, aS, aC)) // Does line intersect the arc ?
{
for (i=0; i<n; i++) // check x portion of all intersections
{
minX = min(minX, x[i], v->X());
maxX = max(maxX, x[i], v->X());
}
}
else
{
minX = min(minX, v->X());
maxX = max(maxX, v->X());
}
h1 = NULL; h2 = NULL;
}
else // Straight segment so just check the vertex
{
minX = min(minX, v->X());
minY = min(minY, v->Y());
maxX = max(maxX, v->X());
maxY = max(maxY, v->Y());
}
v = v->Next();
}
while(v->id() != m_first->id());
//
// Now create an return a polygon with the bounding rectangle
//
this_class = get_class(this); // Findout the class I'm in (might be an extension of polygon)
p = new this_class; // Create a new instance of that class
p->addv(minX,minY);
p->addv(minX,maxY);
p->addv(maxX,maxY);
p->addv(maxX,minY);
return p;
} // end of bounding rectangle
#endif
// file php_polygon.h
// See comments in php_polygon.cpp
#ifndef PHP_POLYGON_H
#define PHP_POLYGON_H
class vertex;
class segment;
#define infinity 100000000 // for places that are far far away
#define PI 3.14159265359
enum{ A_OR_B, A_AND_B, A_MINUS_B, B_MINUS_A };
class polygon
{
public:
/*------------------------------------------------------------------------------
** This class manages a doubly linked list of vertex objects that represents
** a polygon. The class consists of basic methods to manage the list
** and methods to implement boolean operations between polygon objects.
*/
vertex * m_first; // Reference to first vertex in the linked list
int m_cnt; // Tracks number of vertices in the polygon
polygon( vertex * first = NULL );
~polygon();
vertex * getFirst();
polygon * NextPoly();
void add( vertex * nv );
void addv( double x, double y,
double xc=0, double yc=0, int d=0);
vertex * del( vertex * v );
void res();
polygon * copy_poly();
void insertSort( vertex * nv, vertex * s, vertex * e );
vertex * nxt( vertex * v );
BOOL unckd_remain();
vertex * first_unckd_intersect();
double dist( double x1, double y1, double x2, double y2 );
double angle( double xc, double yc, double x1, double y1 );
double aAlpha( double x1, double y1, double x2, double y2,
double xc, double yc, double xi, double yi, double d );
void perturb( vertex * p1, vertex * p2, vertex * q1, vertex * q2,
double aP, double aQ );
BOOL ints( vertex * p1, vertex * p2, vertex * q1, vertex * q2,
int * n, double ix[], double iy[], double alphaP[], double alphaQ[] );
BOOL isInside ( vertex * v );
polygon * boolean( polygon * polyB, int oper );
#if 0
function isPolyInside (p);
function move (dx, dy);
function rotate (xr, yr, a);
function &bRect ();
#endif
}; //end of class polygon
#endif // ifndef PHP_POLYGON_H
// file php_polygon_vertex.cpp
// This is a port of a php class written by Brenor Brophy (see below)
/*------------------------------------------------------------------------------
** File: vertex.php
** Description: PHP class for a polygon vertex. Used as the base object to
** build a class of polygons.
** Version: 1.1
** Author: Brenor Brophy
** Email: brenor at sbcglobal dot net
** Homepage: www.brenorbrophy.com
**------------------------------------------------------------------------------
** COPYRIGHT (c) 2005 BRENOR BROPHY
**
** The source code included in this package is free software; you can
** redistribute it and/or modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation. This license can be
** read at:
**
** http://www.opensource.org/licenses/gpl-license.php
**
** This program is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
**------------------------------------------------------------------------------
**
** Based on the paper "Efficient Clipping of Arbitary Polygons" by Gunther
** Greiner (greiner at informatik dot uni-erlangen dot de) and Kai Hormann
** (hormann at informatik dot tu-clausthal dot de), ACM Transactions on Graphics
** 1998;17(2):71-83.
**
** Available at: www.in.tu-clausthal.de/~hormann/papers/clipping.pdf
**
** Another useful site describing the algorithm and with some example
** C code by Ionel Daniel Stroe is at:
**
** http://davis.wpi.edu/~matt/courses/clipping/
**
** The algorithm is extended by Brenor Brophy to allow polygons with
** arcs between vertices.
**
** Rev History
** -----------------------------------------------------------------------------
** 1.0 08/25/2005 Initial Release
** 1.1 09/04/2005 Added software license language to header comments
*/
//#include "stdafx.h"
#include <math.h>
#include "php_polygon_vertex.h"
segment::segment(double xc, double yc, int d )
{
m_xc = xc;
m_yc = yc;
m_d = d;
}
vertex::vertex( double x, double y,
double xc, double yc, double d,
vertex * nextV, vertex * prevV,
polygon * nextPoly,
BOOL intersect,
vertex * neighbor,
double alpha,
BOOL entry,
BOOL checked )
{
m_x = x;
m_y = y;
m_nextV = nextV;
m_prevV = prevV;
m_nextPoly = nextPoly;
m_intersect = intersect;
m_neighbor = neighbor;
m_alpha = alpha;
m_entry = entry;
m_checked = checked;
m_id = 0;
m_nSeg = new segment( xc, yc, d );
m_pSeg = NULL;
}
vertex::~vertex()
{
if( m_nSeg )
delete m_nSeg;
}
double vertex::Xc ( BOOL g )
{
if ( isIntersect() )
{
if ( m_neighbor->isEntry() )
return m_neighbor->m_nSeg->Xc();
else
return m_neighbor->m_pSeg->Xc();
}
else
if (g)
return m_nSeg->Xc();
else
return m_pSeg->Xc();
}
double vertex::Yc ( BOOL g )
{
if ( isIntersect() )
{
if ( m_neighbor->isEntry() )
return m_neighbor->m_nSeg->Yc();
else
return m_neighbor->m_pSeg->Yc();
}
else
if (g)
return m_nSeg->Yc();
else
return m_pSeg->Yc();
}
double vertex::d ( BOOL g )
{
if ( isIntersect() )
{
if ( m_neighbor->isEntry() )
return m_neighbor->m_nSeg->d();
else
return (-1*m_neighbor->m_pSeg->d());
}
else
if (g)
return m_nSeg->d();
else
return (-1*m_pSeg->d());
}
void vertex::setChecked( BOOL check )
{
m_checked = check;
if( m_neighbor )
if( !m_neighbor->isChecked() )
m_neighbor->setChecked();
}
// file php_polygon_vertex.h
// See comments in file php_polygon_vertex.cpp
#ifndef PHP_POLYGON_VERTEX_H
#define PHP_POLYGON_VERTEX_H
#include "defs-macros.h"
class vertex;
class polygon;
class segment
{
public:
segment(double xc=0.0, double yc=0.0, int d=0 );
double Xc(){ return m_xc; };
double Yc(){ return m_yc; };
int d(){ return m_d; };
void setXc( double xc ){ m_xc = xc; };
void setYc( double yc ){ m_yc = yc; };
double m_xc, m_yc; // center of arc
int m_d; // direction (-1=CW, 0=LINE, 1=CCW)
};
class vertex
{
public:
vertex( double x, double y,
double xc=0.0, double yc=0.0, double d=0.0,
vertex * nextV=NULL, vertex * prevV=NULL,
polygon * nextPoly=NULL,
BOOL intersect=FALSE,
vertex * neighbor=NULL,
double alpha=0.0,
BOOL entry=TRUE,
BOOL checked=FALSE );
~vertex();
int id() { return m_id; };
double X() { return m_x; };
void setX( double x ) { m_x = x; };
double Y() { return m_y; };
void setY( double y ) { m_y = y; };
double Xc ( BOOL g = TRUE );
double Yc ( BOOL g = TRUE );
double d ( BOOL g = TRUE );
void setXc ( double xc ) { m_nSeg->setXc(xc); };
void setYc ( double yc ) { m_nSeg->setYc(yc); };
void setNext ( vertex* nextV ){ m_nextV = nextV; };
vertex * Next (){ return m_nextV; };
void setPrev ( vertex *prevV ){ m_prevV = prevV; };
vertex * Prev (){ return m_prevV; };
void setNseg ( segment * nSeg ){ m_nSeg = nSeg; };
segment * Nseg (){ return m_nSeg; };
void setPseg ( segment * pSeg ){ m_pSeg = pSeg; };
segment * Pseg (){ return m_pSeg; };
void setNextPoly ( polygon * nextPoly ){ m_nextPoly = nextPoly; };
polygon * NextPoly (){ return m_nextPoly; };
void setNeighbor ( vertex * neighbor ){ m_neighbor = neighbor; };
vertex * Neighbor (){ return m_neighbor; };
double Alpha (){ return m_alpha; };
BOOL isIntersect (){ return m_intersect; };
void setChecked( BOOL check = TRUE);
BOOL isChecked () { return m_checked; };
void setEntry ( BOOL entry = TRUE){ m_entry = entry; }
BOOL isEntry (){ return m_entry; };
double m_x, m_y; // coords
vertex * m_nextV; // links to next and prev vertices
vertex * m_prevV; // links to next and prev vertices
segment * m_nSeg, * m_pSeg; // links to next and prev segments
polygon * m_nextPoly;
BOOL m_intersect;
vertex * m_neighbor;
double m_alpha;
BOOL m_entry;
BOOL m_checked;
int m_id;
};
#endif // ifndef PHP_POLYGON_VERTEX_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment