// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @file src/gpc.cpp * @author huhan02(com@baidu.com) * @date 2015/12/18 14:17:30 * @brief * * @modified by sunyipeng * @email sunyipeng@baidu.com * @date 2018/6/12 **/ #include "paddle/fluid/operators/detection/gpc.h" #include "paddle/fluid/platform/enforce.h" namespace gpc { 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; gpc_free(*it); *it = itn; } } static void reset_lmt(lmt_node **lmt) { lmt_node *lmtn; while (*lmt) { lmtn = (*lmt)->next; gpc_free(*lmt); *lmt = lmtn; } } static void insert_bound(edge_node **b, edge_node *e) { edge_node *existing_bound = NULL; 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 */ gpc_malloc(*lmt, sizeof(lmt_node), const_cast("LMT insertion")); (*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; gpc_malloc(*lmt, sizeof(lmt_node), const_cast("LMT insertion")); (*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 */ gpc_malloc(*sbtree, sizeof(sb_tree), const_cast("scanbeam tree insertion")); (*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)); gpc_free(*sbtree); } } static int count_optimal_vertices(gpc_vertex_list c) { int result = 0; int i = 0; /* 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 (gpc_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 = 0; int i = 0; int min = 0; int max = 0; int num_edges = 0; int v = 0; int num_vertices = 0; int total_vertices = 0; int e_index = 0; edge_node *e = NULL; edge_node *edge_table = NULL; 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 */ gpc_malloc(edge_table, total_vertices * sizeof(edge_node), const_cast("edge table creation")); 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 (gpc_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 (gpc_fwd_min(edge_table, min, num_vertices)) { /* Search for the next local maximum... */ num_edges = 1; max = gpc_next_index(min, num_vertices); while (gpc_not_fmax(edge_table, max, num_vertices)) { num_edges++; max = gpc_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] = 0; e[0].bundle[BELOW][SUBJ] = 0; 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 = gpc_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 (gpc_rev_min(edge_table, min, num_vertices)) { /* Search for the previous local maximum... */ num_edges = 1; max = gpc_prev_index(min, num_vertices); while (gpc_not_rmax(edge_table, max, num_vertices)) { num_edges++; max = gpc_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] = 0; e[0].bundle[BELOW][SUBJ] = 0; 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 = gpc_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; } // NOLINT 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 */ gpc_malloc(*it, sizeof(it_node), const_cast("IT insertion")); (*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; gpc_malloc(*it, sizeof(it_node), const_cast("IT insertion")); (*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 = 0.0; double r = 0.0; double x = 0.0; double y = 0.0; if (!*st) { /* Append edge onto the tail end of the ST */ gpc_malloc(*st, sizeof(st_node), const_cast("ST insertion")); (*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; gpc_malloc(*st, sizeof(st_node), const_cast("ST insertion")); (*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; st_node *stp; edge_node *edge = NULL; /* 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; gpc_free(st); st = stp; } } static int count_contours(polygon_node *polygon) { int nc = 0; int nv = 0; vertex_node *v = NULL; vertex_node *nextv = NULL; 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; gpc_free(v); } polygon->active = 0; } } } return nc; } static void add_left(polygon_node *p, double x, double y) { vertex_node *nv = NULL; /* Create a new vertex node and set its fields */ gpc_malloc(nv, sizeof(vertex_node), const_cast("vertex node creation")); 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 = NULL; /* Label contour as a hole */ q->proxy->hole = 1; 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 = 0; list->proxy = q->proxy; } } } } static void add_right(polygon_node *p, double x, double y) { vertex_node *nv = NULL; /* Create a new vertex node and set its fields */ gpc_malloc(nv, sizeof(vertex_node), const_cast("vertex node creation")); 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 = NULL; /* Label contour as external */ q->proxy->hole = 0; 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 = 0; list->proxy = q->proxy; } } } } static void add_local_min(polygon_node **p, edge_node *edge, double x, double y) { polygon_node *existing_min = NULL; vertex_node *nv = NULL; existing_min = *p; gpc_malloc(*p, sizeof(polygon_node), const_cast("polygon node creation")); /* Create a new vertex node and set its fields */ gpc_malloc(nv, sizeof(vertex_node), const_cast("vertex node creation")); nv->x = x; nv->y = y; nv->next = NULL; /* Initialise proxy to point to p itself */ (*p)->proxy = (*p); (*p)->active = 1; (*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 = 0; for (total = 0; tn; tn = tn->next) { if (tn->active > 2) { total++; } } return total; } void add_vertex(vertex_node **t, double x, double y) { if (!(*t)) { gpc_malloc(*t, sizeof(vertex_node), const_cast("tristrip vertex creation")); (*t)->x = x; (*t)->y = y; (*t)->next = NULL; } else { /* Head further down the list */ add_vertex(&((*t)->next), x, y); } } void gpc_vertex_create(edge_node *e, int p, int s, double x, double y) { add_vertex(&(e->outp[p]->v[s]), x, y); e->outp[p]->active++; } static void new_tristrip(polygon_node **tn, edge_node *edge, double x, double y) { if (!(*tn)) { gpc_malloc(*tn, sizeof(polygon_node), const_cast("tristrip node creation")); (*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 = 0; int v = 0; gpc_malloc(box, p->num_contours * sizeof(bbox), const_cast("Bounding box creation")); PADDLE_ENFORCE_NOT_NULL(box); /* 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; bbox *c_bbox; int s = 0; int c = 0; int *o_table = NULL; int overlap = 0; s_bbox = create_contour_bboxes(subj); c_bbox = create_contour_bboxes(clip); gpc_malloc(o_table, subj->num_contours * clip->num_contours * sizeof(int), const_cast("overlap table creation")); /* 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; } } } gpc_free(s_bbox); gpc_free(c_bbox); gpc_free(o_table); } /* =========================================================================== Public Functions =========================================================================== */ void gpc_free_polygon(gpc_polygon *p) { int c = 0; for (c = 0; c < p->num_contours; c++) { gpc_free(p->contour[c].vertex); } gpc_free(p->hole); gpc_free(p->contour); p->num_contours = 0; } /* void gpc_read_polygon(FILE *fp, int read_hole_flags, gpc_polygon *p) { int c = 0; int v = 0; fscanf(fp, "%d", &(p->num_contours)); gpc_malloc(p->hole, p->num_contours * sizeof(int), (char *)"hole flag array creation"); gpc_malloc(p->contour, p->num_contours * sizeof(gpc_vertex_list), (char *)"contour creation"); 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] = 0; // Assume all contours to be external } gpc_malloc(p->contour[c].vertex, p->contour[c].num_vertices * sizeof(gpc_vertex), (char *)"vertex creation"); 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 = 0; int v = 0; 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 = NULL; int c = 0; int v = 0; gpc_vertex_list *extended_contour = NULL; /* Create an extended hole array */ gpc_malloc(extended_hole, (p->num_contours + 1) * sizeof(int), const_cast("contour hole addition")); PADDLE_ENFORCE_NOT_NULL(extended_hole); /* Create an extended contour array */ gpc_malloc(extended_contour, (p->num_contours + 1) * sizeof(gpc_vertex_list), const_cast("contour addition")); /* 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; gpc_malloc(extended_contour[c].vertex, new_contour->num_vertices * sizeof(gpc_vertex), const_cast("contour addition")); for (v = 0; v < new_contour->num_vertices; v++) { extended_contour[c].vertex[v] = new_contour->vertex[v]; } /* Dispose of the old contour */ gpc_free(p->contour); gpc_free(p->hole); /* Update the polygon information */ p->num_contours++; p->hole = extended_hole; p->contour = extended_contour; } // gpc_polygon_clip void gpc_polygon_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, gpc_polygon *result) { sb_tree *sbtree = NULL; it_node *it = NULL; it_node *intersect = NULL; edge_node *edge = NULL; edge_node *prev_edge = NULL; edge_node *next_edge = NULL; edge_node *succ_edge = NULL; edge_node *e0 = NULL; edge_node *e1 = NULL; edge_node *aet = NULL; edge_node *c_heap = NULL; edge_node *s_heap = NULL; lmt_node *lmt = NULL; lmt_node *local_min = NULL; polygon_node *out_poly = NULL; polygon_node *p = NULL; polygon_node *q = NULL; polygon_node *poly = NULL; polygon_node *npoly = NULL; polygon_node *cf = NULL; vertex_node *vtx = NULL; vertex_node *nv = NULL; h_state horiz[2]; int in[2]; int exists[2]; int parity[2] = {LEFT, LEFT}; int c = 0; int v = 0; int contributing = 0; int search = 0; int scanbeam = 0; int sbt_entries = 0; int vclass = 0; int bl = 0; int br = 0; int tl = 0; int tr = 0; double *sbt = NULL; double xb = 0.0; double px = 0.0; double yb = 0.0; double yt = 0.0; double dy = 0.0; double ix = 0.0; double iy = 0.0; /* 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); gpc_free(s_heap); gpc_free(c_heap); return; } /* Build scanbeam table from scanbeam tree */ gpc_malloc(sbt, sbt_entries * sizeof(double), const_cast("sbt creation")); PADDLE_ENFORCE_NOT_NULL(sbt); 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] = 0; 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] = 0; next_edge->bstate[ABOVE] = UNBUNDLED; /* Bundle edges above the scanbeam boundary if they coincide */ if (next_edge->bundle[ABOVE][next_edge->type]) { if (gpc_eq(e0->xb, next_edge->xb) && gpc_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] = 0; e0->bundle[ABOVE][SUBJ] = 0; 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 = 1; while (search) { prev_edge = prev_edge->prev; if (prev_edge) { if (prev_edge->bstate[ABOVE] != BUNDLE_TAIL) { search = 0; } } else { search = 0; } } } 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) { gpc_malloc(result->hole, result->num_contours * sizeof(int), const_cast("hole flag table creation")); gpc_malloc(result->contour, result->num_contours * sizeof(gpc_vertex_list), const_cast("contour creation")); 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; gpc_malloc( result->contour[c].vertex, result->contour[c].num_vertices * sizeof(gpc_vertex), const_cast("vertex creation")); 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; gpc_free(vtx); v--; } c++; } gpc_free(poly); } } else { for (poly = out_poly; poly; poly = npoly) { npoly = poly->next; gpc_free(poly); } } // Tidy up reset_it(&it); reset_lmt(&lmt); gpc_free(c_heap); gpc_free(s_heap); gpc_free(sbt); } // NOLINT void gpc_free_tristrip(gpc_tristrip *t) { int s = 0; for (s = 0; s < t->num_strips; s++) { gpc_free(t->strip[s].vertex); } gpc_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); } // gpc_tristrip_clip void gpc_tristrip_clip(gpc_op op, gpc_polygon *subj, gpc_polygon *clip, gpc_tristrip *result) { sb_tree *sbtree = NULL; it_node *it = NULL; it_node *intersect = NULL; edge_node *edge = NULL; edge_node *prev_edge = NULL; edge_node *next_edge = NULL; edge_node *succ_edge = NULL; edge_node *e0 = NULL; edge_node *e1 = NULL; edge_node *aet = NULL; edge_node *c_heap = NULL; edge_node *s_heap = NULL; edge_node *cf = NULL; lmt_node *lmt = NULL; lmt_node *local_min = NULL; polygon_node *tlist = NULL; polygon_node *tn = NULL; polygon_node *tnn = NULL; polygon_node *p = NULL; polygon_node *q = NULL; vertex_node *lt = NULL; vertex_node *ltn = NULL; vertex_node *rt = NULL; vertex_node *rtn = NULL; h_state horiz[2]; vertex_type cft = NUL; int in[2]; int exists[2]; int parity[2] = {LEFT, LEFT}; int s = 0; int v = 0; int contributing = 0; int search = 0; int scanbeam = 0; int sbt_entries = 0; int vclass = 0; int bl = 0; int br = 0; int tl = 0; int tr = 0; double *sbt = NULL; double xb = 0.0; double px = 0.0; double nx = 0.0; double yb = 0.0; double yt = 0.0; double dy = 0.0; double ix = 0.0; double iy = 0.0; /* 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); gpc_free(s_heap); gpc_free(c_heap); return; } /* Build scanbeam table from scanbeam tree */ gpc_malloc(sbt, sbt_entries * sizeof(double), const_cast("sbt creation")); PADDLE_ENFORCE_NOT_NULL(sbt); 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 */ /* Create bundles within AET */ px = -DBL_MAX; 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] = 0; 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] = 0; next_edge->bstate[ABOVE] = UNBUNDLED; /* Bundle edges above the scanbeam boundary if they coincide */ if (next_edge->bundle[ABOVE][next_edge->type]) { if (gpc_eq(e0->xb, next_edge->xb) && gpc_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] = 0; e0->bundle[ABOVE][SUBJ] = 0; 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) { gpc_vertex_create(edge, ABOVE, RIGHT, xb, yb); } cf = NULL; break; case ELI: gpc_vertex_create(edge, BELOW, LEFT, xb, yb); edge->outp[ABOVE] = NULL; cf = edge; break; case EMX: if (xb != cf->xb) { gpc_vertex_create(edge, BELOW, RIGHT, xb, yb); } edge->outp[ABOVE] = NULL; cf = NULL; break; case IMN: if (cft == LED) { if (cf->bot.y != yb) { gpc_vertex_create(cf, BELOW, LEFT, cf->xb, yb); } new_tristrip(&tlist, cf, cf->xb, yb); } edge->outp[ABOVE] = cf->outp[ABOVE]; gpc_vertex_create(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) { gpc_vertex_create(cf, BELOW, LEFT, cf->xb, yb); } new_tristrip(&tlist, cf, cf->xb, yb); } gpc_vertex_create(edge, BELOW, RIGHT, xb, yb); edge->outp[ABOVE] = NULL; break; case IMX: gpc_vertex_create(edge, BELOW, LEFT, xb, yb); edge->outp[ABOVE] = NULL; cft = IMX; break; case IMM: gpc_vertex_create(edge, BELOW, LEFT, xb, yb); edge->outp[ABOVE] = cf->outp[ABOVE]; if (xb != cf->xb) { gpc_vertex_create(cf, ABOVE, RIGHT, xb, yb); } cf = edge; break; case EMM: gpc_vertex_create(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) { gpc_vertex_create(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) { gpc_vertex_create(edge, BELOW, RIGHT, xb, yb); } else { if (edge->bot.y == yb) { gpc_vertex_create(cf, BELOW, LEFT, cf->xb, yb); gpc_vertex_create(edge, BELOW, RIGHT, xb, yb); } } } else { gpc_vertex_create(edge, BELOW, RIGHT, xb, yb); gpc_vertex_create(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]); switch (op) { // Determine quadrant occupancies 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) { gpc_p_edge(prev_edge, e0, ABOVE); gpc_vertex_create(prev_edge, ABOVE, LEFT, px, iy); gpc_vertex_create(e0, ABOVE, RIGHT, ix, iy); e1->outp[ABOVE] = e0->outp[ABOVE]; e0->outp[ABOVE] = NULL; } break; case ELI: if (q) { gpc_n_edge(next_edge, e1, ABOVE); gpc_vertex_create(e1, ABOVE, LEFT, ix, iy); gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); e0->outp[ABOVE] = e1->outp[ABOVE]; e1->outp[ABOVE] = NULL; } break; case EMX: if (p && q) { gpc_vertex_create(e0, ABOVE, LEFT, ix, iy); e0->outp[ABOVE] = NULL; e1->outp[ABOVE] = NULL; } break; case IMN: gpc_p_edge(prev_edge, e0, ABOVE); gpc_vertex_create(prev_edge, ABOVE, LEFT, px, iy); gpc_n_edge(next_edge, e1, ABOVE); gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); new_tristrip(&tlist, prev_edge, px, iy); e1->outp[ABOVE] = prev_edge->outp[ABOVE]; gpc_vertex_create(e1, ABOVE, RIGHT, ix, iy); new_tristrip(&tlist, e0, ix, iy); next_edge->outp[ABOVE] = e0->outp[ABOVE]; gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); break; case ILI: if (p) { gpc_vertex_create(e0, ABOVE, LEFT, ix, iy); gpc_n_edge(next_edge, e1, ABOVE); gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); e1->outp[ABOVE] = e0->outp[ABOVE]; e0->outp[ABOVE] = NULL; } break; case IRI: if (q) { gpc_vertex_create(e1, ABOVE, RIGHT, ix, iy); gpc_p_edge(prev_edge, e0, ABOVE); gpc_vertex_create(prev_edge, ABOVE, LEFT, px, iy); e0->outp[ABOVE] = e1->outp[ABOVE]; e1->outp[ABOVE] = NULL; } break; case IMX: if (p && q) { gpc_vertex_create(e0, ABOVE, RIGHT, ix, iy); gpc_vertex_create(e1, ABOVE, LEFT, ix, iy); e0->outp[ABOVE] = NULL; e1->outp[ABOVE] = NULL; gpc_p_edge(prev_edge, e0, ABOVE); gpc_vertex_create(prev_edge, ABOVE, LEFT, px, iy); new_tristrip(&tlist, prev_edge, px, iy); gpc_n_edge(next_edge, e1, ABOVE); gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); next_edge->outp[ABOVE] = prev_edge->outp[ABOVE]; gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); } break; case IMM: if (p && q) { gpc_vertex_create(e0, ABOVE, RIGHT, ix, iy); gpc_vertex_create(e1, ABOVE, LEFT, ix, iy); gpc_p_edge(prev_edge, e0, ABOVE); gpc_vertex_create(prev_edge, ABOVE, LEFT, px, iy); new_tristrip(&tlist, prev_edge, px, iy); gpc_n_edge(next_edge, e1, ABOVE); gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); e1->outp[ABOVE] = prev_edge->outp[ABOVE]; gpc_vertex_create(e1, ABOVE, RIGHT, ix, iy); new_tristrip(&tlist, e0, ix, iy); next_edge->outp[ABOVE] = e0->outp[ABOVE]; gpc_vertex_create(next_edge, ABOVE, RIGHT, nx, iy); } break; case EMM: if (p && q) { gpc_vertex_create(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 = 1; 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 = 0; } } else { search = 0; } } } 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) { gpc_malloc(result->strip, result->num_strips * sizeof(gpc_vertex_list), const_cast("tristrip list creation")); 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; gpc_malloc(result->strip[s].vertex, tn->active * sizeof(gpc_vertex), const_cast("tristrip creation")); v = 0; if (0) { 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++; gpc_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++; gpc_free(rt); rt = rtn; } } s++; } else { /* Invalid tristrip: just free the heap */ for (lt = tn->v[LEFT]; lt; lt = ltn) { ltn = lt->next; gpc_free(lt); } for (rt = tn->v[RIGHT]; rt; rt = rtn) { rtn = rt->next; gpc_free(rt); } } gpc_free(tn); } } // Tidy up reset_it(&it); reset_lmt(&lmt); gpc_free(c_heap); gpc_free(s_heap); gpc_free(sbt); } // NOLINT } // namespace gpc /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */