diff --git a/Stairs.h b/Stairs.h new file mode 100644 index 0000000..6a5d251 --- /dev/null +++ b/Stairs.h @@ -0,0 +1,4 @@ +#ifndef STAIRS_H +#define STAIRS_H + +#endif // STAIRS_H diff --git a/lib/gpc/Polygon.h b/lib/gpc/Polygon.h new file mode 100644 index 0000000..69c5d72 --- /dev/null +++ b/lib/gpc/Polygon.h @@ -0,0 +1,109 @@ +#ifndef GPC_POLYGON_H +#define GPC_POLYGON_H + +#include +#include "gpc.h" + +class Polygon { + + struct GPCPolygon : gpc_polygon { + GPCPolygon() { +// contour = (gpc_vertex_list*) calloc(0, 1024); +// contour->num_vertices = 0; +// contour->vertex = (gpc_vertex*) calloc(0, 1024); +// hole = (int*) calloc(0, 1024); + num_contours = 0; + contour = nullptr; + hole = nullptr; + } + ~GPCPolygon() { + if (contour) { + gpc_free_polygon(this); + //free(contour->vertex); contour->vertex = nullptr; + } + free(contour); contour = nullptr; + free(hole); hole = nullptr; + + } + GPCPolygon& operator = (const GPCPolygon& o) = delete; + GPCPolygon& operator = (GPCPolygon& o) { + this->contour = o.contour; + this->hole = o.hole; + this->num_contours = o.num_contours; + o.contour = nullptr; + o.hole = nullptr; + return *this; + } + }; + +private: + + GPCPolygon state; + +public: + + void add(const Floorplan::Polygon2& poly) { + GPCPolygon cur = toGPC(poly); + //GPCPolygon out; + gpc_polygon_clip(GPC_UNION, &state, &cur, &state); + //state = out; + } + + void remove(const Floorplan::Polygon2& poly) { + GPCPolygon cur = toGPC(poly); + //GPCPolygon out; + gpc_polygon_clip(GPC_DIFF, &state, &cur, &state); + //state = out; + } + + std::vector> get(float z) { + + gpc_tristrip res; + res.num_strips = 0; + res.strip = nullptr; + + //res.strip = (gpc_vertex_list*) malloc(1024); + gpc_polygon_to_tristrip(&state, &res); + + std::vector> trias; + + for (int i = 0; i < res.num_strips; ++i) { + gpc_vertex_list lst = res.strip[i]; + std::vector tria; + for (int j = 0; j < lst.num_vertices; ++j) { + gpc_vertex& vert = lst.vertex[j]; + Point3 p3(vert.x, vert.y, z); + tria.push_back(p3); + } + trias.push_back(tria); + } + + gpc_free_tristrip(&res); + + return std::move(trias); + + } + +private: + + GPCPolygon toGPC(Floorplan::Polygon2 poly) { + + std::vector verts; + for (Point2 p2 : poly.points) { + gpc_vertex vert; vert.x = p2.x; vert.y = p2.y; + verts.push_back(vert); + } + + GPCPolygon gpol; + gpc_vertex_list list; + list.num_vertices = verts.size(); + list.vertex = verts.data(); + gpc_add_contour(&gpol, &list, 0); + + return gpol; + + } + +}; + +#endif // GPC_POLYGON_H diff --git a/lib/gpc/gpc.cpp b/lib/gpc/gpc.cpp new file mode 100755 index 0000000..114d9d1 --- /dev/null +++ b/lib/gpc/gpc.cpp @@ -0,0 +1,2472 @@ +/* +=========================================================================== + +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 "gpc.h" +#include +#include +#include + + +/* +=========================================================================== + 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 +=========================================================================== +*/ diff --git a/lib/gpc/gpc.h b/lib/gpc/gpc.h new file mode 100755 index 0000000..a975b61 --- /dev/null +++ b/lib/gpc/gpc.h @@ -0,0 +1,133 @@ +/* +=========================================================================== + +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 + + +/* +=========================================================================== + 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 +=========================================================================== +*/ diff --git a/main.cpp b/main.cpp index c45c2a8..7f94a52 100644 --- a/main.cpp +++ b/main.cpp @@ -7,11 +7,23 @@ #include #include +#include +#include + +#include +#include + #include "sensors/SensorFactory.h" #include "map/MapView.h" #include +#include +#include +#include +#include +#include + class LeListener : public SensorListener, public SensorListener, public SensorListener { public: @@ -29,23 +41,43 @@ public: }; +struct MyNode : public GridPoint, public GridNode, public WiFiGridNode<10> { + + float imp; + MyNode() {;} + MyNode(const float x_cm, const float y_cm, const float z_cm) : GridPoint(x_cm, y_cm, z_cm) {;} + + static void staticSerialize(std::ostream& out) { + WiFiGridNode::staticSerialize(out); + } + + static void staticDeserialize(std::istream& inp) { + WiFiGridNode::staticDeserialize(inp); + } + +}; + int main(int argc, char *argv[]) { -//// test(); -// LeListener listener; -// WiFiSensor& wifi = SensorFactory::getWiFi(); -// wifi.addListener(&listener); -// wifi.start(); -//// AccelerometerSensor& acc = SensorFactory::getAccelerometer(); -//// acc.addListener(&listener); -//// acc.start(); -// StepSensor& steps = SensorFactory::getSteps(); -// steps.addListener(&listener); -// steps.start();; + int sizeOfNode = sizeof(MyNode); -// std::this_thread::sleep_for(std::chrono::seconds(10000)); + std::vector points = { + Point3(1140,530,1060), Point3(1140,2100,1060), Point3(1140,2880,1060), Point3(1140,4442.21,1060), Point3(1190,4840,1060), Point3(1660,4840,890), Point3(1660,5040,890), Point3(1180,5040,720), Point3(1180,4840,720), Point3(1180,4460,720), Point3(2350,4460,720), Point3(4440,4440,720), Point3(5183.26,4280,720), Point3(5800,4280,550), Point3(6110,4280,380), Point3(7680,4280,380), Point3(7680,3860,190), Point3(7680,3400,0), Point3(7400,3400,0), Point3(7400,4030,0) + }; + std::vector path; + Interpolator interpol; + int ts = 0; + Point3 last = points[0]; + for (Point3 p : points) { + const float dist = p.getDistance(last); + const float diffMS = (dist / 100.0f) / 1.5f * 1000; + ts += diffMS; + interpol.add(ts, p); + last = p; + path.push_back(p / 100.0f); + } @@ -54,22 +86,175 @@ int main(int argc, char *argv[]) { // OpenGL Setup QSurfaceFormat format; - format.setDepthBufferSize(24); + format.setDepthBufferSize(16); QSurfaceFormat::setDefaultFormat(format); -// QMainWindow* win = new QMainWindow(); -// QVBoxLayout* lay = new QVBoxLayout(); -// win->setLayout(lay); + Floorplan::IndoorMap* im = Floorplan::Reader::readFromFile("/mnt/data/workspaces/IndoorMap/maps/SHL21.xml"); + + Grid grid(20); + GridFactory gf(grid); + + // build + //gf.build(im); + //Importance::addImportance(grid, 0); + //std::ofstream out("/tmp/grid.dat"); + //grid.write(out); out.close(); + + // read + std::ifstream inp("/tmp/grid.dat"); + grid.reset(); + grid.read(inp); + + // estimate WiFi signal strengths + WiFiModelLogDist mdlLogDist(-40.0f, 2.25f); + WiFiGridEstimator::estimate(grid, mdlLogDist, im); + + MapView* map = new MapView(); -// lay->addWidget(map); map->setMinimumHeight(200); map->setMinimumWidth(200); + map->setMap(im); + //map->setPath(path); map->show(); -// win->setMinimumWidth(400); -// win->setMinimumHeight(400); -// win->show(); + + + struct DijkstraMapper { + Grid& grid; + DijkstraMapper(Grid& grid) : grid(grid) {;} + int getNumNeighbors(const MyNode& n) const {return grid.getNumNeighbors(n);} + const MyNode* getNeighbor(const MyNode& n, const int idx) const {return &grid.getNeighbor(n, idx);} + float getWeightBetween(const MyNode& n1, const MyNode& n2) const {return n1.getDistanceInCM(n2) / n2.imp;} + + }; + + + + struct LeListener : public APIListener { + + Grid& grid; + Dijkstra& d; + const DijkstraNode* dnEnd; + MapView* map; + + Point3 nextPos; + Point3 pos1; + Point3 pos2; + + LeListener(Grid& grid, Dijkstra& d, const DijkstraNode* dnEnd, MapView* map) : grid(grid), d(d), dnEnd(dnEnd), map(map) { + + + std::thread t(&LeListener::update, this); + t.detach(); + + } + + /** the currently estimated path to the target has changed */ + virtual void onPathUpdate(const std::vector& curPath) override { + + } + + /** the currently estimated position has changed */ + virtual void onPositionUpdate(const Point3 pos) override { + + nextPos = pos; + + // update the path to the target + const GridPoint xxx(pos.x, pos.y, pos.z); + const DijkstraNode* dnStart = d.getNode( grid.getNearestNode(xxx) ); + const DijkstraPath dp(dnStart, dnEnd); + map->setPath(dp); + + } + + void update() { + + + std::this_thread::sleep_for(std::chrono::milliseconds(700)); + + const int transMS = 500; + const int updateMS = 75; + const float factor1 = ((float) updateMS / (float) transMS) * 0.7; + const float factor2 = factor1 * 0.4f; + const Point3 human(0, 0, 180); + + while(true) { + + + std::this_thread::sleep_for(std::chrono::milliseconds(updateMS)); + +// Point3 diff = (nextPos - pos1); +// if (diff.length() > 30) {diff = diff.normalized() * 30;} else {diff *= factor1;} +// pos1 += diff; +// pos2 += diff*0.5; + + + pos1 = pos1 * (1-factor1) + nextPos * (factor1); // fast update + pos2 = pos2 * (1-factor2) + nextPos * (factor2); // slow update + const Point3 dir = pos2 - pos1; + + map->setLookAt((pos1+human) / 100.0f, (dir) / 100.0f); + + } + + + } + + }; + + + DummyAPI api("/mnt/data/workspaces/navindoor"); + api.setTarget(4); + + + Dijkstra d; + //GridPoint end(points.front().x, points.front().y, points.front().z); + GridPoint end(api.getDst().x, api.getDst().y, api.getDst().z); + d.build(&grid.getNearestNode(end), DijkstraMapper(grid)); + const DijkstraNode* dnEnd = d.getNode(grid.getNearestNode(end)); + + api.addListener(new LeListener(grid, d, dnEnd, map)); + api.setSpeed(2); + api.startNavigation(); + +// auto run = [&] () { +// //int ts = 0; +// int ts = 97000; +// while(ts < interpol.getMaxKey() && ts >= 0) { +// std::this_thread::sleep_for(std::chrono::milliseconds(50)); +// //ts += 50; +// ts -= 150; +// const Point3 human(0, 0, 180); +//// const Point3 pos0 = interpol.get(ts-500); +//// const Point3 pos1 = interpol.get(ts+500); +// const Point3 pos = interpol.get(ts); +//// //const Point3 dir = Point3(-50000, -50000, 50000);//pos0 - pos; +//// Point3 dir = pos1 - pos0; dir.z /= 2; // only slight down/up looking +//// map->setLookAt((pos+human) / 100.0f, (dir) / 100.0f); + +// GridPoint cur(pos.x, pos.y, pos.z); +// const DijkstraNode* dnStart = d.getNode( grid.getNearestNode(cur) ); +// DijkstraPath dp(dnStart, dnEnd); +// map->setPath(dp); + + +//// Point3 next = +//// dp.getFromStart(2).element->inCentimeter() + +//// dp.getFromStart(4).element->inCentimeter() + +//// dp.getFromStart(6).element->inCentimeter(); +//// next /= 3; +//// const Point3 dir = pos - next ; +// static Point3 lastPos; +// lastPos = lastPos * 0.85 + pos * 0.15; + +// const Point3 dir = lastPos - pos ; +// map->setLookAt((pos+human) / 100.0f, (dir) / 100.0f); + +// } +// }; +// std::thread t(run); + // QQmlApplicationEngine engine; // engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/map/FloorRenderer.h b/map/FloorRenderer.h new file mode 100644 index 0000000..e06d96c --- /dev/null +++ b/map/FloorRenderer.h @@ -0,0 +1,222 @@ +#ifndef FLOORRENDERER_H +#define FLOORRENDERER_H + +#include +#include +#include +#include +#include "GL.h" + + +class FloorRenderer : protected QOpenGLFunctions { + +private: + + Floorplan::Floor* floor; + + QOpenGLBuffer arrayBuf; + QOpenGLBuffer indexBuf; + QOpenGLTexture* texture = nullptr; + + std::vector vertices; + std::vector indices; + + +public: + + /** ctor */ + FloorRenderer(Floorplan::Floor* floor) : floor(floor), arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer) { + ; + } + + /** dctor */ + ~FloorRenderer() { + arrayBuf.destroy(); + indexBuf.destroy(); + delete texture; + } + + /** render the floor */ + void render(QOpenGLShaderProgram *program) { + + // Tell OpenGL which VBOs to use + arrayBuf.bind(); + indexBuf.bind(); + texture->bind(); + + // vertices + int vertLoc = program->attributeLocation("a_position"); + program->enableAttributeArray(vertLoc); + program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0])); + + // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data + int normLoc = program->attributeLocation("a_normal"); + program->enableAttributeArray(normLoc); + program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0])); + + int texcoordLocation = program->attributeLocation("a_texcoord"); + program->enableAttributeArray(texcoordLocation); + program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0])); + + // texture + program->setUniformValue("texture", 0); + + // Draw cube geometry using indices from VBO 1 + glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, 0); + + } + + void initGL() { + initializeOpenGLFunctions(); + build(); + } + +private: + + void build() { + + QImage img(":/res/gl/tex/wall1.jpg"); + texture = new QOpenGLTexture(img); + texture->setMinificationFilter(QOpenGLTexture::Nearest); + texture->setMagnificationFilter(QOpenGLTexture::Linear); + texture->setWrapMode(QOpenGLTexture::Repeat); + + // build array of vertices and indices + for (Floorplan::FloorObstacle* obstacle : floor->obstacles) { + add(floor, obstacle, vertices, indices); + } + + // Transfer vertex data to VBO 0 + arrayBuf.create(); + arrayBuf.bind(); + arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0])); + + // Transfer index data to VBO 1 + indexBuf.create(); + indexBuf.bind(); + indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0])); + + } + + void add(Floorplan::Floor* floor, Floorplan::FloorObstacle* obstacle, std::vector& vertices, std::vector& indices) { + + if (dynamic_cast(obstacle)) { + + Floorplan::FloorObstacleLine* line = dynamic_cast(obstacle); + if (line->type != Floorplan::ObstacleType::WALL) {return;} + +// QVector3D p1(line->from.x, floor->getStartingZ(), line->from.y); +// QVector3D p2(line->to.x, floor->getStartingZ(), line->to.y); +// QVector3D p3(line->to.x, floor->getEndingZ(), line->to.y); +// QVector3D p4(line->from.x, floor->getEndingZ(), line->from.y); + +// addFace(p1,p2,p3,p4, vertices, indices); + + + addFace(line->from, line->to, floor->getStartingZ(), floor->getEndingZ(), vertices, indices); + addFace(line->to, line->from, floor->getStartingZ(), floor->getEndingZ(), vertices, indices); + + } + + } + + void addFace(const Point2 from, const Point2 to, const float h1, const float h2, std::vector& vertices, std::vector& indices) { + + // unit-face with unit normal (facing the camera) + QVector3D p1(-0.5, -0.0, 0); + QVector3D p2(+0.5, -0.0, 0); + QVector3D p3(+0.5, +1.0, 0); + QVector3D p4(-0.5, +1.0, 0); + QVector3D norm(0, 0, 1); + + // how to scale the unit-face to match the wall + const float h = h2-h1; + const float s = from.getDistance(to); + QMatrix4x4 scale; scale.scale(s, h, 1); + + // how to rotate the unit-face to match the wall + const float angle = std::atan2(to.y - from.y, to.x - from.x); + const float deg = angle * 180 / M_PI; + QMatrix4x4 rot; rot.rotate(deg, QVector3D(0,1,0)); + + // how to translate the unit-face to match the wall + const Point2 center = (from + to) / 2; + QMatrix4x4 move; move.translate(center.x, h1, center.y); + + // final matrix + QMatrix4x4 mat = move * rot * scale; + + // texture coordinates (scale only) + const QVector2D tex1 = (scale * p1).toVector2D() / 5; + const QVector2D tex2 = (scale * p2).toVector2D() / 5; + const QVector2D tex3 = (scale * p3).toVector2D() / 5; + const QVector2D tex4 = (scale * p4).toVector2D() / 5; + + // modify vertices + p1 = mat * p1; + p2 = mat * p2; + p3 = mat * p3; + p4 = mat * p4; + norm = (rot * norm).normalized(); + + const int start = vertices.size(); + vertices.push_back(VertNormTex(p1, norm, tex1)); + vertices.push_back(VertNormTex(p2, norm, tex2)); + vertices.push_back(VertNormTex(p3, norm, tex3)); + vertices.push_back(VertNormTex(p4, norm, tex4)); + + indices.push_back(start+0); + indices.push_back(start+1); + indices.push_back(start+2); + indices.push_back(start+3); + + } + +// void addFace(QVector3D p1, QVector3D p2, QVector3D p3, QVector3D p4, std::vector& vertices, std::vector& indices) { + +// const float s = 50; + +// // ensure camera facing (for culling) +// if (p1.x() != p2.x()) { +// if (p1.x() > p2.x()) {std::swap(p1, p2), std::swap(p3, p4);} +// } else { +// if (p1.z() > p2.z()) {std::swap(p1, p2), std::swap(p3, p4);} +// } + +// // corresponding normal vector +// QVector3D norm = QVector3D::crossProduct((p2-p1), (p3-p1)).normalized(); + +// // orient towards the viewport +// const QVector3D view(-99,-99,-99); +// if ((view-norm).length() > (view+norm).length()) {norm = -norm;} + + +// const int start = vertices.size(); + +// indices.push_back(start+0); +// indices.push_back(start+1); +// indices.push_back(start+2); +// indices.push_back(start+3); + +// vertices.push_back(VertNorm(p1/s, norm)); +// vertices.push_back(VertNorm(p2/s, norm)); +// vertices.push_back(VertNorm(p3/s, norm)); +// vertices.push_back(VertNorm(p4/s, norm)); + +// // and the other side (clockwise, negated normal) + +// indices.push_back(start+0); +// indices.push_back(start+3); +// indices.push_back(start+2); +// indices.push_back(start+1); + +// vertices.push_back(VertNorm(p1/s, -norm)); +// vertices.push_back(VertNorm(p2/s, -norm)); +// vertices.push_back(VertNorm(p3/s, -norm)); +// vertices.push_back(VertNorm(p4/s, -norm)); + +// } + +}; + +#endif // FLOORRENDERER_H diff --git a/map/Geometry.cpp b/map/Geometry.cpp deleted file mode 100644 index 76c54cd..0000000 --- a/map/Geometry.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "Geometry.h" - -#include -#include - -struct VertexData -{ - QVector3D position; - QVector2D texCoord; -}; - -Geometry::Geometry() : indexBuf(QOpenGLBuffer::IndexBuffer) { - - initializeOpenGLFunctions(); - - // Generate 2 VBOs - arrayBuf.create(); - indexBuf.create(); - - // Initializes cube geometry and transfers it to VBOs - initCubeGeometry(); - -} - -Geometry::~Geometry() { - arrayBuf.destroy(); - indexBuf.destroy(); -} - -void Geometry::initCubeGeometry() { - - // For cube we would need only 8 vertices but we have to - // duplicate vertex for each face because texture coordinate - // is different. - VertexData vertices[] = { - // Vertex data for face 0 - {QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.0f, 0.0f)}, // v0 - {QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.0f)}, // v1 - {QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.0f, 0.5f)}, // v2 - {QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v3 - - // Vertex data for face 1 - {QVector3D( 1.0f, -1.0f, 1.0f), QVector2D( 0.0f, 0.5f)}, // v4 - {QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.5f)}, // v5 - {QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.0f, 1.0f)}, // v6 - {QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v7 - - // Vertex data for face 2 - {QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v8 - {QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(1.0f, 0.5f)}, // v9 - {QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)}, // v10 - {QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(1.0f, 1.0f)}, // v11 - - // Vertex data for face 3 - {QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v12 - {QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(1.0f, 0.0f)}, // v13 - {QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v14 - {QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(1.0f, 0.5f)}, // v15 - - // Vertex data for face 4 - {QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.0f)}, // v16 - {QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v17 - {QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v18 - {QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v19 - - // Vertex data for face 5 - {QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v20 - {QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v21 - {QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v22 - {QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)} // v23 - }; - - // Indices for drawing cube faces using triangle strips. - // Triangle strips can be connected by duplicating indices - // between the strips. If connecting strips have opposite - // vertex order then last index of the first strip and first - // index of the second strip needs to be duplicated. If - // connecting strips have same vertex order then only last - // index of the first strip needs to be duplicated. - GLushort indices[] = { - 0, 1, 2, 3, 3, // Face 0 - triangle strip ( v0, v1, v2, v3) - 4, 4, 5, 6, 7, 7, // Face 1 - triangle strip ( v4, v5, v6, v7) - 8, 8, 9, 10, 11, 11, // Face 2 - triangle strip ( v8, v9, v10, v11) - 12, 12, 13, 14, 15, 15, // Face 3 - triangle strip (v12, v13, v14, v15) - 16, 16, 17, 18, 19, 19, // Face 4 - triangle strip (v16, v17, v18, v19) - 20, 20, 21, 22, 23 // Face 5 - triangle strip (v20, v21, v22, v23) - }; - - // Transfer vertex data to VBO 0 - arrayBuf.bind(); - arrayBuf.allocate(vertices, 24 * sizeof(VertexData)); - - // Transfer index data to VBO 1 - indexBuf.bind(); - indexBuf.allocate(indices, 34 * sizeof(GLushort)); - -} - -void Geometry::drawCubeGeometry(QOpenGLShaderProgram *program) { - - // Tell OpenGL which VBOs to use - arrayBuf.bind(); - indexBuf.bind(); - - // Offset for position - quintptr offset = 0; - - // Tell OpenGL programmable pipeline how to locate vertex position data - int vertexLocation = program->attributeLocation("a_position"); - program->enableAttributeArray(vertexLocation); - program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData)); - - // Offset for texture coordinate - offset += sizeof(QVector3D); - - // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data - int texcoordLocation = program->attributeLocation("a_texcoord"); - program->enableAttributeArray(texcoordLocation); - program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData)); - - // Draw cube geometry using indices from VBO 1 - glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, 0); - -} diff --git a/map/Geometry.h b/map/Geometry.h deleted file mode 100644 index 216a775..0000000 --- a/map/Geometry.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef GEOMETRY_H -#define GEOMETRY_H - -#include -#include -#include - -class Geometry : protected QOpenGLFunctions -{ -public: - Geometry(); - virtual ~Geometry(); - - void drawCubeGeometry(QOpenGLShaderProgram *program); - -private: - void initCubeGeometry(); - - QOpenGLBuffer arrayBuf; - QOpenGLBuffer indexBuf; -}; - -#endif // GEOMETRY_H diff --git a/map/MapView.cpp b/map/MapView.cpp index 0656384..b230f11 100644 --- a/map/MapView.cpp +++ b/map/MapView.cpp @@ -1,7 +1,13 @@ #include "MapView.h" #include -#include "Geometry.h" + +#include "elements/Walls.h" +#include "elements/Ground.h" +#include "elements/Handrails.h" +#include "elements/Stairs.h" +#include "elements/Doors.h" +#include "elements/Path.h" // http://doc.qt.io/qt-5/qtopengl-cube-example.html @@ -10,31 +16,42 @@ MapView::MapView(QWidget* parent) : QOpenGLWidget(parent) { }; +void MapView::setMap(Floorplan::IndoorMap* map) { + + for (Floorplan::Floor* floor : map->floors) { + elements.push_back(new Ground(floor)); + elements.push_back(new Walls(floor)); + elements.push_back(new Handrails(floor)); + elements.push_back(new Stairs(floor)); + elements.push_back(new Doors(floor)); + } + + this->path = new Path(); + elements.push_back(this->path); + +} + +void MapView::setPath(const std::vector& path) { + this->path->set(path); +} + + void MapView::timerEvent(QTimerEvent *) { update(); } void MapView::initializeGL() { - //qglClearColor(Qt::black); - - //glEnable(GL_DEPTH_TEST); - //glEnable(GL_CULL_FACE); - //glShadeModel(GL_SMOOTH); - //glEnable(GL_LIGHTING); - //glEnable(GL_LIGHT0); - - //static GLfloat lightPosition[4] = { 0, 0, 10, 1.0 }; - //glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); - initializeOpenGLFunctions(); - geo = new Geometry(); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); - if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/res/gl/vertex1.glsl")) {throw "1";} - if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/res/gl/fragment1.glsl")) {throw "2";} - if (!program.link()) {throw "3";} - if (!program.bind()) {throw "4";} + + + for (Renderable* r : elements) { + r->initGL(); + } timer.start(60, this); @@ -42,11 +59,6 @@ void MapView::initializeGL() { void MapView::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - //glLoadIdentity(); - //glTranslatef(0.0, 0.0, -10.0); - //glRotatef(20 / 16.0, 1.0, 0.0, 0.0); - //glRotatef(30 / 16.0, 0.0, 1.0, 0.0); - //glRotatef(60 / 16.0, 0.0, 0.0, 1.0); draw(); } @@ -55,75 +67,66 @@ void MapView::resizeGL(int w, int h) { // Calculate aspect ratio qreal aspect = qreal(w) / qreal(h ? h : 1); - // Set near plane to 3.0, far plane to 7.0, field of view 45 degrees - const qreal zNear = 3.0, zFar = 7.0, fov = 45.0; + // viewing frustrum [0:50] meter + const qreal zNear = 0.02, zFar = 50, fov = 50.0; // Reset projection - projection.setToIdentity(); + matProject.setToIdentity(); + matProject.scale(-1, 1, 1); + glCullFace(GL_FRONT); + //matProject.scale(0.05, 0.05, 0.05); + matProject.perspective(fov, aspect, zNear, zFar); + //matProject.scale(-0.01, 0.01, 0.01); - // Set perspective projection - projection.perspective(fov, aspect, zNear, zFar); +} +void MapView::setLookAt(const Point3 pos_m, const Point3 dir) { + QVector3D qDir(dir.x, dir.z, dir.y); + QVector3D lookAt = QVector3D(pos_m.x, pos_m.z, pos_m.y); + QVector3D eye = lookAt + qDir * 0.1; + QVector3D up = QVector3D(0,1,0); + matView.setToIdentity(); + //matView.scale(0.01, 0.01, 0.01); + matView.lookAt(eye, lookAt, up); + //matView.scale(0.99, 1, 1); + //matView.translate(0.7, 0, 0); + lightPos = eye + QVector3D(0.0, 4.0, 0.0); + eyePos = eye; } void MapView::draw() { + // clear everything + glClearColor(0,0,0,1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - static float angularSpeed = 0; - angularSpeed += 0.5; + //static float angularSpeed = 0; + //angularSpeed += 1.5; - // Clear color and depth buffer - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + //texture->bind(); + //QVector3D rotationAxis(1,1,1); + //QQuaternion rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed); - //texture->bind(); - QVector3D rotationAxis(1,1,1); - QQuaternion rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed); + // Calculate model view transformation + QMatrix4x4 matModel; + //matModel.setToIdentity(); + //matModel.translate(0.0, 0.0, 0.0); + //matModel.rotate(rotation); - // Calculate model view transformation - QMatrix4x4 matrix; - matrix.translate(0.0, 0.0, -5.0); - matrix.rotate(rotation); + for (Renderable* r : elements) { - // Set modelview-projection matrix - program.setUniformValue("mvp_matrix", projection * matrix); + QOpenGLShaderProgram& program = r->getProgram(); + program.bind(); - // Use texture unit 0 which contains cube.png - program.setUniformValue("texture", 0); + // set the matrices + program.setUniformValue("m_matrix", matModel); + program.setUniformValue("mv_matrix", matView * matModel); + program.setUniformValue("mvp_matrix", matProject * matView * matModel); + program.setUniformValue("lightWorldPos", lightPos); + program.setUniformValue("eyeWorldPos", eyePos); - // Draw cube geometry - geo->drawCubeGeometry(&program); + r->render(); - //qglColor(Qt::red); - /*glBegin(GL_QUADS); - glNormal3f(0,0,-1); - glVertex3f(-1,-1,0); - glVertex3f(-1,1,0); - glVertex3f(1,1,0); - glVertex3f(1,-1,0); + } - glEnd(); - glBegin(GL_TRIANGLES); - glNormal3f(0,-1,0.707); - glVertex3f(-1,-1,0); - glVertex3f(1,-1,0); - glVertex3f(0,0,1.2); - glEnd(); - glBegin(GL_TRIANGLES); - glNormal3f(1,0, 0.707); - glVertex3f(1,-1,0); - glVertex3f(1,1,0); - glVertex3f(0,0,1.2); - glEnd(); - glBegin(GL_TRIANGLES); - glNormal3f(0,1,0.707); - glVertex3f(1,1,0); - glVertex3f(-1,1,0); - glVertex3f(0,0,1.2); - glEnd(); - glBegin(GL_TRIANGLES); - glNormal3f(-1,0,0.707); - glVertex3f(-1,1,0); - glVertex3f(-1,-1,0); - glVertex3f(0,0,1.2); - glEnd();*/ } diff --git a/map/MapView.h b/map/MapView.h index f33ad47..c966da5 100644 --- a/map/MapView.h +++ b/map/MapView.h @@ -6,22 +6,57 @@ #include #include -class Geometry; +#include +#include + +#include "elements/Path.h" + +namespace Floorplan { + class IndoorMap; +} + +class Renderable; +class Path; + class MapView : public QOpenGLWidget, protected QOpenGLFunctions { private: - QMatrix4x4 projection; - QOpenGLShaderProgram program; - Geometry* geo; + QMatrix4x4 matProject; + QMatrix4x4 matView; + + QVector3D lightPos; + QVector3D eyePos; + QBasicTimer timer; + std::vector elements; + Path* path; + + + public: MapView(QWidget* parent = 0); + /** set the map to display */ + void setMap(Floorplan::IndoorMap* map); + + /** the position to look at */ + void setLookAt(const Point3 pos, const Point3 dir = Point3(-1, -1, 0.1)); + + + /** set the path to disply */ + void setPath(const std::vector& path); + + /** set the path to disply */ + template void setPath(const DijkstraPath& path) { + this->path->set(path); + } + + protected: void timerEvent(QTimerEvent *e) Q_DECL_OVERRIDE; diff --git a/map/Renderable.h b/map/Renderable.h new file mode 100644 index 0000000..24aacc8 --- /dev/null +++ b/map/Renderable.h @@ -0,0 +1,40 @@ +#ifndef RENDERABLE_H +#define RENDERABLE_H + +#include + +class Renderable { + +protected: + + QOpenGLShaderProgram program; + +public: + + /** get the renderable's shader */ + QOpenGLShaderProgram& getProgram() {return program;} + + /** render the renderable */ + void render() { + program.bind(); + _render(); + } + + + virtual void initGL() = 0; + + virtual void _render() = 0; + +protected: + + /** helper method to build the shader */ + void loadShader(const QString& vertex, const QString& fragment) { + if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, vertex)) {throw "1";} + if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, fragment)) {throw "2";} + if (!program.link()) {throw "3";} + if (!program.bind()) {throw "4";} + } + +}; + +#endif // RENDERABLE_H diff --git a/map/elements/Doors.h b/map/elements/Doors.h new file mode 100644 index 0000000..d7dd77e --- /dev/null +++ b/map/elements/Doors.h @@ -0,0 +1,101 @@ +#ifndef DOORS_H +#define DOORS_H + +#include +#include "../gl/GLHelper.h" +#include "../gl/GLTriangles.h" +#include "../Renderable.h" + +class Doors : public Renderable { + +private: + + Floorplan::Floor* floor; + + GLTriangles doors; + +public: + + /** ctor */ + Doors(Floorplan::Floor* floor) : floor(floor) { + ; + } + + + void initGL() override { + + build(); + doors.setDiffuse(":/res/gl/tex/door2.jpg"); + doors.setNormalMap(":/res/gl/tex/door2_normal.jpg"); + + doors.build(); + + loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl"); + program.setUniformValue("texDiffuse", 0); + program.setUniformValue("texNormalMap", 1); + + } + + /** render the floor */ + void _render() override { + doors.render(&program); + } + + + +private: + + void build() { + + for (Floorplan::FloorObstacle* obstacle : floor->obstacles) { + + if (dynamic_cast(obstacle)) { + Floorplan::FloorObstacleDoor* door = dynamic_cast(obstacle); + addFace(door->from, door->to, floor->getStartingZ(), floor->getStartingZ() + door->height, door->swap); + } + + } + + } + + void addFace(const Point2 from, Point2 to, const float h1, const float h2, const bool swap) { + + to = from + (to-from).rotated(60/180.0f*M_PI * ((swap)?(-1):(+1)) ); + + const QVector3D vert1(from.x, h1, from.y); + const QVector3D vert2(to.x, h1, to.y); + const QVector3D vert3(to.x, h2, to.y); + const QVector3D vert4(from.x, h2, from.y); + + const QVector3D n1 = GLHelper::getNormal(vert1, vert2, vert3); + const QVector3D n2 = -n1; + + QVector3D tan = (vert1-vert2).normalized(); + tan = GLHelper::isCCW(vert1, vert2, vert3) ? (tan) : (-tan); + + const QVector2D tex1(0, 0); + const QVector2D tex2(1, 0); + const QVector2D tex3(1, 1); + const QVector2D tex4(0, 1); + + { + const VertNormTexTan vnt1(vert1, n1, tex1, tan); + const VertNormTexTan vnt2(vert2, n1, tex2, tan); + const VertNormTexTan vnt3(vert3, n1, tex3, tan); + const VertNormTexTan vnt4(vert4, n1, tex4, tan); + doors.addQuadCCW(vnt1, vnt2, vnt3, vnt4); + } { + const VertNormTexTan vnt1(vert1, n2, tex1, -tan); + const VertNormTexTan vnt2(vert2, n2, tex2, -tan); + const VertNormTexTan vnt3(vert3, n2, tex3, -tan); + const VertNormTexTan vnt4(vert4, n2, tex4, -tan); + doors.addQuadCW(vnt1, vnt2, vnt3, vnt4); + } + + } + + + +}; + +#endif // DOORS_H diff --git a/map/elements/Ground.h b/map/elements/Ground.h new file mode 100644 index 0000000..60f08fa --- /dev/null +++ b/map/elements/Ground.h @@ -0,0 +1,129 @@ +#ifndef GROUND_H +#define GROUND_H + +#include +#include "../gl/GLHelper.h" +#include "../gl/GLTriangles.h" +#include "../Renderable.h" + +#include "../../lib/gpc/Polygon.h" + +class Ground : public Renderable { + +private: + + Floorplan::Floor* floor; + + GLTriangles flooring; + GLTriangles ceiling; + +public: + + /** ctor */ + Ground(Floorplan::Floor* floor) : floor(floor) { + ; + } + + + void initGL() override { + + build(); + + flooring.setDiffuse(":/res/gl/tex/floor4.jpg"); + flooring.setNormalMap(":/res/gl/tex/floor4_normal.jpg"); + + ceiling.setDiffuse(":/res/gl/tex/floor4.jpg"); + ceiling.setNormalMap(":/res/gl/tex/floor4_normal.jpg"); + + flooring.build(); + ceiling.build(); + + loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl"); + program.setUniformValue("texDiffuse", 0); + program.setUniformValue("texNormalMap", 1); + + } + + /** render the floor */ + void _render() override { + flooring.render(&program); + ceiling.render(&program); + } + + + +private: + + void build() { + + std::vector add; + std::vector rem; + + const std::vector& polys = floor->outline; + + Polygon pol; + + for (Floorplan::FloorOutlinePolygon* poly : polys) { + switch (poly->method) { + case Floorplan::OutlineMethod::ADD: pol.add(poly->poly); break; + case Floorplan::OutlineMethod::REMOVE: pol.remove(poly->poly); break; + default: throw 1; + } + } + + std::vector> trias = pol.get(floor->atHeight); + + + // is stored as TRIANGLE_STRIP + // => triangle might have more than 3 points + for (const std::vector& tria : trias) { + + + const QVector3D normFloor(0, +1, 0); + const QVector3D normCeil(0, +1, 0); // why +1??? + const QVector3D t(1,0,0); + + const float s = 0.6; + + // add vertices + for (int i = 2; i < (int) tria.size(); i+=1) { + + const Point3 p1 = tria[i-2]; + const Point3 p2 = tria[i-1]; + const Point3 p3 = tria[i-0]; + + const QVector3D vert1(p1.x, p1.z, p1.y); + const QVector3D vert2(p2.x, p2.z, p2.y); + const QVector3D vert3(p3.x, p3.z, p3.y); + + { + const VertNormTexTan vnt1(vert1, normFloor, tex(vert1*s), t); + const VertNormTexTan vnt2(vert2, normFloor, tex(vert2*s), t); + const VertNormTexTan vnt3(vert3, normFloor, tex(vert3*s), t); + flooring.addFaceCCW(vnt1, vnt2, vnt3); + } { + const VertNormTexTan vnt1(vert1, normCeil, tex(vert1*s), t); + const VertNormTexTan vnt2(vert2, normCeil, tex(vert2*s), t); + const VertNormTexTan vnt3(vert3, normCeil, tex(vert3*s), t); + ceiling.addFaceCW(vnt1, vnt2, vnt3); + } + + } + + } + + } + + +private: + + QVector2D tex(const QVector3D vert) { + return QVector2D(vert.x(), vert.z()); + } + + + + +}; + +#endif // GROUND_H diff --git a/map/elements/Handrails.h b/map/elements/Handrails.h new file mode 100644 index 0000000..fc22f75 --- /dev/null +++ b/map/elements/Handrails.h @@ -0,0 +1,89 @@ +#ifndef HANDRAIL_H +#define HANDRAIL_H + + +#include +#include "../gl/GLHelper.h" +#include "../gl/GLLines.h" +#include "../Renderable.h" + + +class Handrails : public Renderable { + +private: + + Floorplan::Floor* floor; + + GLLines lines; + +public: + + /** ctor */ + Handrails(Floorplan::Floor* floor) : floor(floor) { + ; + } + + + void initGL() override { + build(); + lines.build(); + loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl"); + program.setUniformValue("color", QVector4D(0.5, 0.5, 0.5, 1.0)); + } + + /** render the floor */ + void _render() override { + glLineWidth(2); + lines.render(&program); + } + + + +private: + + void build() { + + for (Floorplan::FloorObstacle* obstacle : floor->obstacles) { + + if (dynamic_cast(obstacle)) { + Floorplan::FloorObstacleLine* line = dynamic_cast(obstacle); + if (line->type != Floorplan::ObstacleType::HANDRAIL) {continue;} + add(line->from, line->to, floor->getStartingZ()); + } + + } + + } + + void add(const Point2 from, const Point2 to, const float h1) { + + // handrail height + const float h2 = h1 + 0.8; + + const QVector3D v1(to.x, h2, to.y); + const QVector3D v2(from.x, h2, from.y); + + // upper + lines.addLine(v1, v2); + + const float stepSize = 0.5; + const float len = from.getDistance(to); + const float steps = std::round(len / stepSize); + + for (int i = 0; i <= steps; ++i) { + const float percent = (float) i / (float) steps; + const Point2 pos = from + (to-from) * percent; + const QVector3D v1(pos.x, h1, pos.y); + const QVector3D v2(pos.x, h2, pos.y); + lines.addLine(v1, v2); + } + + + } + + +}; + + + +#endif // HANDRAIL_H diff --git a/map/elements/Path.h b/map/elements/Path.h new file mode 100644 index 0000000..f925712 --- /dev/null +++ b/map/elements/Path.h @@ -0,0 +1,318 @@ +#ifndef PATH_H +#define PATH_H + +#include +#include + + +#include "../gl/GLHelper.h" +#include "../gl/GLTriangles.h" +#include "../Renderable.h" + +#include "../../lib/gpc/Polygon.h" + +class Path : public Renderable { + +private: + + GLTriangles lines; + +public: + + /** ctor */ + Path() { + ; + } + + + void initGL() override { + + loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentLine.glsl"); + program.setUniformValue("color", QVector4D(0.0, 0.4, 1.0, 0.6)); + + //loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTexSimple.glsl"); + + lines.setDiffuse(":/res/gl/tex/arrows.png"); + program.setUniformValue("texNormalMap", 0); + + } + + /** render the floor */ + void _render() override { + lines.rebuild(); + glLineWidth(30); + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + lines.render(&program); + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + + } + + + template void set(const DijkstraPath& path) { + + std::vector out; + for (const DijkstraNode* node : path.getVector()) { + if (!node) {break;} + const Node* elem = node->element; + out.push_back(Point3(elem->x_cm/100.0f, elem->y_cm/100.0f, elem->z_cm/100.0f)); + } + + out = simplify(out); + set(out); + + } + + /* + void setSimple(const std::vector& path) { + + + lines.clear(); + const float s = 0.5; + + Point3 lastDir(0,0,0); + std::vector quads; + + for (int i = 1; i < (int) path.size(); ++i) { + + Point3 pa = path[i-1]; + Point3 pb = path[i-0]; + const Point3 pc(0, 0, 1); + + Point3 dir = pb - pa; dir /= dir.length(); + Point3 perb = cross(pa-pb, pc); perb /= perb.length(); + + const Point3 p1 = pa - perb*s; + const Point3 p2 = pa + perb*s; + const Point3 p3 = pb + perb*s; + const Point3 p4 = pb - perb*s; + + if (dir == lastDir) { + quads.back().p3 = p3; + quads.back().p4 = p4; + } else { + quads.push_back(Floorplan::Quad3(p1,p2,p3,p4)); + } + + lastDir = dir; + +// // produce a small gap between path-lines [will be filled with another quad!] +// pa += dir * 0.6; +// pb -= dir * 0.6; + +// + } + + for (int i = 0; i < (int) quads.size(); ++i) { + + // add the line-segment + const Floorplan::Quad3 q1 = quads[i]; + addQuad(q1); + +// // construct the quad between adjacent segments +// if (i < (int) quads.size() - 1) { +// const Floorplan::Quad3 q2 = quads[i+1]; +// const Floorplan::Quad3 q3(q1.p3, q2.p2, q2.p1, q1.p4); +// addQuad(q3); +// } + + + } + + } + */ + + /** combine nodes while the direction stays the same (many small quads -> one large quad) */ + std::vector simplify(const std::vector& path) { + + std::vector out; + + Point3 lastDir(0,0,0); + if (!path.empty()) { + out.push_back(path.back()); + } + + for (int i = path.size() - 1; i >= 1; --i) { + + const Point3 pa = path[i-0]; + const Point3 pb = path[i-1]; + const Point3 dir = (pb - pa).normalized(); + + if (dir == lastDir) { + out[out.size()-1] = pb; + } else { + out.push_back(pb); + } + + lastDir = dir; + + } + + // remove unneccesary nodes + for (int i = 1; i < (int) out.size() - 1; ++i) { + + const Point3 pa = out[i-1]; + const Point3 pb = out[i-0]; + const Point3 pc = out[i+1]; + + const float min = 0.6; + const float d1 = pb.getDistance(pa); + const float d2 = pb.getDistance(pc); + + if (d1 < min || d2 < min) { + out.erase(out.begin() + i); + } + + } + + return out; + + } + +// void set(const std::vector& path) { + +// lines.clear(); + +// const float s = 0.4; +// std::vector pts; + +// for (int i = 0; i < (int) path.size(); ++i) { + +// const Point3 pa = path[i-1]; +// const Point3 pb = path[i-0]; +// const Point3 pc(0, 0, 1); + +// const Point3 perb = cross(pa-pb, pc).normalized(); + +// // quad's edges +// const Point3 p1 = pa - perb*s; +// const Point3 p2 = pa + perb*s; +// const Point3 p3 = pb + perb*s; +// const Point3 p4 = pb - perb*s; + +// pts.push_back(p1); +// pts.push_back(p2); + +// } + +// std::vector quads; +// for (int i = 0; i < (int) pts.size(); i+=2) { +// quads.push_back(Floorplan::Quad3(pts[i+0], pts[i+1], pts[i+3], pts[i+2])); +// } + +// float l1 = 0; +// float l2 = 0; + +// for (int i = 0; i < (int) quads.size(); ++i) { + +// // add the line-segment +// const Floorplan::Quad3 q1 = quads[i]; +// l2 += ((q1.p1 + q1.p2) / 2).getDistance( (q1.p3 + q1.p4) / 2 ); +// addQuad(q1, l1, l2); +// l1 = l2; + +// } + +// } + + + + void set(const std::vector& path) { + + + lines.clear(); + + // half the width of the path + const float s = 0.4; + std::vector quads; + + for (int i = 1; i < (int) path.size(); ++i) { + + Point3 pa = path[i-1]; + Point3 pb = path[i-0]; + const Point3 pc(0, 0, 1); + Point3 dir = (pb - pa).normalized(); + + // produce a small gap between path-segments + // those segments will be smoothly connected using another quad + pa += dir * 0.35; + pb -= dir * 0.35; + + const Point3 perb = cross(pa-pb, pc).normalized(); + + // quad's edges + const Point3 p1 = pa - perb*s; + const Point3 p2 = pa + perb*s; + const Point3 p3 = pb + perb*s; + const Point3 p4 = pb - perb*s; + + // add + quads.push_back(Floorplan::Quad3(p1,p2,p3,p4)); + + } + + float l1 = 0; + float l2 = 0; + + for (int i = 0; i < (int) quads.size(); ++i) { + + // add the line-segment + const Floorplan::Quad3 q1 = quads[i]; + l2 += ((q1.p1 + q1.p2) / 2).getDistance( (q1.p3 + q1.p4) / 2 ); + addQuad(q1, l1, l2); + l1 = l2; + + // done? + if (i == (int) quads.size() - 1) {break;} + + // construct the quad between adjacent segments + const Floorplan::Quad3 q2 = quads[i+1]; + const Floorplan::Quad3 q3(q1.p4, q1.p3, q2.p2, q2.p1); + l2 += ((q3.p1 + q3.p2) / 2).getDistance( (q3.p3 + q3.p4) / 2 ); + addQuad(q3, l1, l2); + l1 = l2; + + } + + } + +private: + + void addQuad(const Floorplan::Quad3& q, const float l1, const float l2) { + + // move the path upwards (slightly above the ground) + const float h = 0.40; + + const QVector3D v1(q.p1.x, q.p1.z+h, q.p1.y); + const QVector3D v2(q.p2.x, q.p2.z+h, q.p2.y); + const QVector3D v3(q.p3.x, q.p3.z+h, q.p3.y); + const QVector3D v4(q.p4.x, q.p4.z+h, q.p4.y); + + const QVector3D n(0,1,0); + + const QVector2D tex1(0, l1); + const QVector2D tex2(1, l1); + const QVector2D tex3(1, l2); + const QVector2D tex4(0, l2); + +// const QVector2D tex1(q.p1.x, q.p1.y); +// const QVector2D tex2(q.p2.x, q.p2.y); +// const QVector2D tex3(q.p3.x, q.p3.y); +// const QVector2D tex4(q.p4.x, q.p4.y); + + const VertNormTex vnt1(v1, n, tex1); + const VertNormTex vnt2(v2, n, tex2); + const VertNormTex vnt3(v3, n, tex3); + const VertNormTex vnt4(v4, n, tex4); + + + lines.addQuadCCW(vnt1, vnt2, vnt3, vnt4); + + } + + +}; + +#endif // PATH_H diff --git a/map/elements/Stairs.h b/map/elements/Stairs.h new file mode 100644 index 0000000..7fbc529 --- /dev/null +++ b/map/elements/Stairs.h @@ -0,0 +1,151 @@ +#ifndef STAIRS_H +#define STAIRS_H + + +#include +#include "../gl/GLHelper.h" +#include "../gl/GLTriangles.h" +#include "../Renderable.h" + + +class Stairs : public Renderable { + +private: + + Floorplan::Floor* floor; + + GLTriangles parts; + +public: + + /** ctor */ + Stairs(Floorplan::Floor* floor) : floor(floor) { + ; + } + + + void initGL() override { + + build(); + parts.setDiffuse(":/res/gl/tex/granite1.jpg"); + parts.setNormalMap(":/res/gl/tex/granite1_normal.jpg"); + parts.build(); + + loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl"); + program.setUniformValue("texDiffuse", 0); + program.setUniformValue("texNormalMap", 1); + + } + + /** render the floor */ + void _render() override { + parts.render(&program); + } + + + +private: + + void build() { + + for (Floorplan::Stair* stair : floor->stairs) { + + if (dynamic_cast(stair)) { + Floorplan::StairFreeform* freeform = dynamic_cast(stair); + add(Floorplan::getQuads(freeform->getParts(), floor)); + } + + } + + } + + void add(const std::vector& quads) { + + for (const Floorplan::Quad3& quad : quads) { + + //void addQuad(quad); + + stepify(quad); + + } + + } + + void stepify(const Floorplan::Quad3& quad) { + + const float len = (quad.p4 - quad.p1).length(); + const float stepLen = 0.3; + const int steps = std::round(len / stepLen); + + for (int i = 0; i < steps; ++i) { + const float per1 = (float) (i+0) / (float) steps; + const float per2 = (float) (i+1) / (float) steps; + + const Point3 p1 = quad.p1 + (quad.p4 - quad.p1) * per1; + const Point3 p2 = quad.p2 + (quad.p3 - quad.p2) * per1; + const Point3 p5 = quad.p2 + (quad.p3 - quad.p2) * per2; + const Point3 p6 = quad.p1 + (quad.p4 - quad.p1) * per2; + Point3 p3 = p5; p3.z = p2.z; + Point3 p4 = p6; p4.z = p1.z; + + + addQuad(Floorplan::Quad3(p1, p2, p3, p4)); + addQuad(Floorplan::Quad3(p3, p4, p6, p5)); + + } + + } + + void addQuad(const Floorplan::Quad3& quad) { + + const QVector3D vert1(quad.p1.x, quad.p1.z, quad.p1.y); + const QVector3D vert2(quad.p2.x, quad.p2.z, quad.p2.y); + const QVector3D vert3(quad.p3.x, quad.p3.z, quad.p3.y); + const QVector3D vert4(quad.p4.x, quad.p4.z, quad.p4.y); + + const QVector3D n1 = GLHelper::getNormal(vert1, vert2, vert3); + const QVector3D n2 = -n1; + +// const float o = + +// const QVector2D tex1(quad.p1.length(), quad.p1.y); +// const QVector2D tex2(quad.p2.x, quad.p2.y+quad.p2.z); +// const QVector2D tex3(quad.p3.x, quad.p3.y+quad.p3.z); +// const QVector2D tex4(quad.p4.x, quad.p4.y+quad.p4.z); + + const float h = quad.p4.getDistance(quad.p1); + const float l = quad.p1.getDistance(quad.p2); + const float o = quad.p1.length(); + const float s = 1.1; // 0.5; + + const QVector2D tex1(o+0, h); // start texturing at the ceiling so above-door-sections and walls have the same textre + const QVector2D tex2(o+l, h); + const QVector2D tex3(o+l, 0); + const QVector2D tex4(o+0, 0); + +// const VertNormTex vnt1(vert1, n1, tex1*s); +// const VertNormTex vnt2(vert2, n1, tex2*s); +// const VertNormTex vnt3(vert3, n1, tex3*s); +// const VertNormTex vnt4(vert4, n1, tex4*s); + + { + const VertNormTex vnt1(vert1, n1, tex1*s); + const VertNormTex vnt2(vert2, n1, tex2*s); + const VertNormTex vnt3(vert3, n1, tex3*s); + const VertNormTex vnt4(vert4, n1, tex4*s); + parts.addQuadCCW(vnt1, vnt2, vnt3, vnt4); + } { + const VertNormTex vnt1(vert1, n2, tex1*s); + const VertNormTex vnt2(vert2, n2, tex2*s); + const VertNormTex vnt3(vert3, n2, tex3*s); + const VertNormTex vnt4(vert4, n2, tex4*s); + parts.addQuadCW(vnt1, vnt2, vnt3, vnt4); + } + + } + + +}; + + +#endif // STAIRS_H diff --git a/map/elements/Walls.h b/map/elements/Walls.h new file mode 100644 index 0000000..6e1482c --- /dev/null +++ b/map/elements/Walls.h @@ -0,0 +1,120 @@ +#ifndef WALLS_H +#define WALLS_H + +#include +#include "../gl/GLHelper.h" +#include "../gl/GLTriangles.h" +#include "../Renderable.h" + +#include "../../lib/gpc/Polygon.h" + +class Walls : public Renderable { + +private: + + Floorplan::Floor* floor; + + GLTriangles walls; + +public: + + /** ctor */ + Walls(Floorplan::Floor* floor) : floor(floor) { + ; + } + + + void initGL() override { + + build(); + walls.setDiffuse(":/res/gl/tex/floor3.jpg"); + walls.setNormalMap(":/res/gl/tex/floor3_normal.jpg"); + + walls.build(); + + loadShader(":/res/gl/vertex1.glsl", ":/res/gl/fragmentTex.glsl"); + program.setUniformValue("texDiffuse", 0); + program.setUniformValue("texNormalMap", 1); + //glEnable(GL_TEXTURE0 + 1); + + } + + /** render the floor */ + void _render() override { + walls.render(&program); + } + + + +private: + + void build() { + + for (Floorplan::FloorObstacle* obstacle : floor->obstacles) { + + if (dynamic_cast(obstacle)) { + Floorplan::FloorObstacleLine* line = dynamic_cast(obstacle); + if (line->type != Floorplan::ObstacleType::WALL) {continue;} + addFace(line->from, line->to, floor->getStartingZ(), floor->getEndingZ()); + } else if (dynamic_cast(obstacle)) { + Floorplan::FloorObstacleDoor* door = dynamic_cast(obstacle); + addFace(door->from, door->to, floor->getStartingZ() + door->height, floor->getEndingZ()); + } + + } + + } + + void addFace(const Point2 from, const Point2 to, const float h1, const float h2) { + + const QVector3D vert1(from.x, h1, from.y); + const QVector3D vert2(to.x, h1, to.y); + const QVector3D vert3(to.x, h2, to.y); + const QVector3D vert4(from.x, h2, from.y); + + const QVector3D n1 = GLHelper::getNormal(vert1, vert2, vert3); + const QVector3D n2 = -n1; + + + + QVector3D tan = (vert1-vert2).normalized(); + tan = GLHelper::isCCW(vert1, vert2, vert3) ? (tan) : (-tan); + + const float l = from.getDistance(to); + const float h = h2-h1; + const float o = std::min(from.length(), to.length()); + + const QVector2D tex1(o+0, h); // start texturing at the ceiling so above-door-sections and walls have the same textre + const QVector2D tex2(o+l, h); + const QVector2D tex3(o+l, 0); + const QVector2D tex4(o+0, 0); + + const float s = 0.65; + + + + { + const VertNormTexTan vnt1(vert1, n1, tex1*s, tan); + const VertNormTexTan vnt2(vert2, n1, tex2*s, tan); + const VertNormTexTan vnt3(vert3, n1, tex3*s, tan); + const VertNormTexTan vnt4(vert4, n1, tex4*s, tan); + walls.addQuadCCW(vnt1, vnt2, vnt3, vnt4); + } { + const VertNormTexTan vnt1(vert1, n2, tex1*s, -tan); + const VertNormTexTan vnt2(vert2, n2, tex2*s, -tan); + const VertNormTexTan vnt3(vert3, n2, tex3*s, -tan); + const VertNormTexTan vnt4(vert4, n2, tex4*s, -tan); + walls.addQuadCW(vnt1, vnt2, vnt3, vnt4); + } + + } + +//private: +// +// QVector2D tex(const QVector3D vert) { +// return QVector2D(vert.x(), vert.y()); +// } + +}; + +#endif // WALLS_H diff --git a/map/gl/GL.h b/map/gl/GL.h new file mode 100644 index 0000000..e294280 --- /dev/null +++ b/map/gl/GL.h @@ -0,0 +1,55 @@ +#ifndef HELPER_GL_H +#define HELPER_GL_H + +#include +#include +#include +#include + +struct Vert { + QVector3D vert; + Vert(QVector3D vert) : vert(vert) {;} + int getVertOffset() const {return 0;} + bool operator == (const Vert& o) const {return (vert == o.vert);} +}; + +struct VertNorm { + QVector3D vert; + QVector3D norm; + VertNorm(QVector3D vert, QVector3D norm) : vert(vert), norm(norm) {;} + int getVertOffset() const {return 0;} + int getNormOffset() const {return sizeof(QVector3D);} + int getTanOffset() const {throw "error";} + bool operator == (const VertNorm& o) const {return (vert == o.vert) && (norm == o.norm);} + static bool hasTangent() {return false;} + +}; + +struct VertNormTex { + QVector3D vert; + QVector3D norm; + QVector2D tex; + VertNormTex(QVector3D vert, QVector3D norm, QVector3D tex) : vert(vert), norm(norm), tex(tex) {;} + int getVertOffset() const {return 0;} + int getNormOffset() const {return sizeof(QVector3D);} + int getTexOffset() const {return sizeof(QVector3D)*2;} + int getTanOffset() const {throw "error";} + bool operator == (const VertNormTex& o) const {return (vert == o.vert) && (norm == o.norm) && (tex == o.tex);} + static bool hasTangent() {return false;} +}; + +struct VertNormTexTan { + QVector3D vert; + QVector3D norm; + QVector2D tex; + QVector3D tan; + VertNormTexTan(QVector3D vert, QVector3D norm, QVector3D tex, QVector3D tan) : vert(vert), norm(norm), tex(tex), tan(tan) {;} + int getVertOffset() const {return 0;} + int getNormOffset() const {return sizeof(QVector3D);} + int getTexOffset() const {return sizeof(QVector3D)*2;} + int getTanOffset() const {return sizeof(QVector3D)*2 + sizeof(QVector2D);} + bool operator == (const VertNormTexTan& o) const {return (vert == o.vert) && (norm == o.norm) && (tex == o.tex) && (tan == o.tan);} + static bool hasTangent() {return true;} +}; + +#endif // HELPER_GL_H diff --git a/map/gl/GLHelper.h b/map/gl/GLHelper.h new file mode 100644 index 0000000..3d53c54 --- /dev/null +++ b/map/gl/GLHelper.h @@ -0,0 +1,67 @@ +#ifndef MAP_HELPER_H +#define MAP_HELPER_H + +#include +#include + +class GLHelper { + +public: + + static QVector3D getNormal(const QVector3D& v1, const QVector3D& v2, const QVector3D& v3) { + + // get two of the triangle's edges + const QVector4D v21 = v2-v1; + const QVector4D v31 = v3-v1; + + const QVector3D n = QVector3D::crossProduct(v21.toVector3D(), v31.toVector3D()).normalized(); + + return isCCW(v1, v2, v3) ? (n) : (-n); + + } + + /** + * is the triangle given by p1,p2,p3 CCW? + * NOTE: uses OUR coordinate system (x,y,z) where z is the floor-height + */ + static bool isCCW(const Point3 p1, const Point3 p2, const Point3 p3) { + const QVector3D v1(p1.x, p1.z, p1.y); + const QVector3D v2(p2.x, p2.z, p2.y); + const QVector3D v3(p3.x, p3.z, p3.y); + return isCCW(v1, v2, v3); + } + + /** + * is the triangle given by p1,p2,p3 CCW? + * NOTE: uses OpenGL coordinate system (x,z,y) (our z is the floor-height) + */ + static bool isCCW(const QVector3D& p1, const QVector3D& p2, const QVector3D& p3) { + + // camera position + QMatrix4x4 proj; proj.lookAt(QVector3D(-1,20,-1), QVector3D(0,0,0), QVector3D(0,1,0)); + + // to camera space + QVector4D v1(p1.x(), p1.y(), p1.z(), 1); + QVector4D v2(p2.x(), p2.y(), p2.z(), 1); + QVector4D v3(p3.x(), p3.y(), p3.z(), 1); + v1 = proj * v1; + v2 = proj * v2; + v3 = proj * v3; + + // get two of the triangle's edges + const QVector4D v21 = v2-v1; + const QVector4D v31 = v3-v1; + + // check the angle between both + const float angle = QVector2D::dotProduct(v21.toVector2D(), v31.toVector2D()); + return angle > 0; + +// const QVector3D n = QVector3D::crossProduct(v21.toVector3D(), v31.toVector3D()); +// return n.z() > 0; + + } + + +}; + +#endif // MAP_HELPER_H diff --git a/map/gl/GLLines.h b/map/gl/GLLines.h new file mode 100644 index 0000000..a069a28 --- /dev/null +++ b/map/gl/GLLines.h @@ -0,0 +1,126 @@ +#ifndef GLLINES_H +#define GLLINES_H + +#include +#include "GL.h" +#include "GLHelper.h" + +#include + +class GLLines : protected QOpenGLFunctions { + +private: + + QOpenGLBuffer arrayBuf; + QOpenGLBuffer indexBuf; + + std::vector vertices; + std::vector indices; + + int mode = GL_LINES; + +public: + + /** ctor */ + GLLines() : arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer) { + ; + } + + /** dtor */ + ~GLLines() { + arrayBuf.destroy(); + indexBuf.destroy(); + } + + /** add a new face to this element */ + void addLine(const QVector3D& p1, const QVector3D& p2) { + + // add vertices (remove duplicates!) + const int i1 = addOnce(p1); + const int i2 = addOnce(p2); + + // add indices + indices.push_back(i1); + indices.push_back(i2); + + } + + void addVertex(const QVector3D& p1) { + const int i1 = addOnce(p1); + indices.push_back(i1); + } + + + /** build the underlying buffers */ + void build() { + + initializeOpenGLFunctions(); + + // Transfer vertex data to VBO 0 + arrayBuf.create(); + arrayBuf.bind(); + arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0])); + + // Transfer index data to VBO 1 + indexBuf.create(); + indexBuf.bind(); + indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0])); + + } + + void rebuild() { + if (indexBuf.isCreated()) {indexBuf.destroy();} + if (arrayBuf.isCreated()) {arrayBuf.destroy();} + build(); + } + + void clear() { + indices.clear(); + vertices.clear(); + } + + void setMode(const int mode) { + this->mode = mode; + } + + /** render the element */ + void render(QOpenGLShaderProgram *program) { + + // Tell OpenGL which VBOs to use + arrayBuf.bind(); + indexBuf.bind(); + + // vertices + int vertLoc = program->attributeLocation("a_position"); + program->enableAttributeArray(vertLoc); + program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0])); + + // Draw cube geometry using indices from VBO 1 + glDrawElements(mode, indices.size(), GL_UNSIGNED_SHORT, 0); + + } + +private: + + /** to conserve memory, avoid duplicate VNTs! */ + int addOnce(const Vert& vnt) { + + const auto it = std::find(vertices.begin(), vertices.end(), vnt); + + if (it == vertices.end()) { + const int idx = vertices.size(); + vertices.push_back(vnt); + return idx; + } else { + const int idx = it - vertices.begin(); + return idx; + } + + } + + + + +}; + +#endif // GLLINES_H diff --git a/map/gl/GLTriangles.h b/map/gl/GLTriangles.h new file mode 100644 index 0000000..62268a8 --- /dev/null +++ b/map/gl/GLTriangles.h @@ -0,0 +1,205 @@ +#ifndef GLTRIANGLES_H +#define GLTRIANGLES_H + +#include +#include "GL.h" +#include "GLHelper.h" + +#include +#include + +template class GLTriangles : protected QOpenGLFunctions { + +private: + + QOpenGLBuffer arrayBuf; + QOpenGLBuffer indexBuf; + QOpenGLTexture* textures[4]; + + std::vector vertices; + std::vector indices; + +public: + + /** ctor */ + GLTriangles() : arrayBuf(QOpenGLBuffer::VertexBuffer), indexBuf(QOpenGLBuffer::IndexBuffer), textures() { + ; + } + + /** dtor */ + ~GLTriangles() { + + arrayBuf.destroy(); + indexBuf.destroy(); + + for (int i = 0; i < 4; ++i) {delete textures[i];} + + } + + /** set the to-be-used texture */ + void setTexture(const int slot, const QString& textureFile) { + + const QImage img(textureFile); + if (img.width() <= 0) {throw "error";} + + QOpenGLTexture* texture = new QOpenGLTexture(img); + texture->setMinificationFilter(QOpenGLTexture::Linear); + texture->setMagnificationFilter(QOpenGLTexture::Linear); + texture->setWrapMode(QOpenGLTexture::Repeat); + texture->generateMipMaps(); + textures[slot] = texture; + + } + + void setDiffuse(const QString& textureFile) { + setTexture(0, textureFile); + } + + void setNormalMap(const QString& textureFile) { + setTexture(1, textureFile); + } + + /** add a new face to this element */ + void addFaceCCW(const T& vnt1, const T& vnt2, const T& vnt3) { + addFace(vnt1, vnt2, vnt3, 1); + } + + /** add a new face to this element */ + void addFaceCW(const T& vnt1, const T& vnt2, const T& vnt3) { + addFace(vnt1, vnt2, vnt3, 2); + } + + /** add a new quad to this element */ + void addQuadCCW(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) { + addFace(vnt1, vnt2, vnt3, 1); + addFace(vnt3, vnt4, vnt1, 1); + } + + /** add a new quad to this element */ + void addQuadCW(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) { + addFace(vnt1, vnt2, vnt3, 2); + addFace(vnt3, vnt4, vnt1, 2); + } + + /** add a new quad to this element */ + void addQuad(const T& vnt1, const T& vnt2, const T& vnt3, const T& vnt4) { + addFace(vnt1, vnt2, vnt3, 0); + addFace(vnt3, vnt4, vnt1, 0); + } + + /** build the underlying buffers */ + void build() { + + initializeOpenGLFunctions(); + + // Transfer vertex data to VBO 0 + arrayBuf.create(); + arrayBuf.bind(); + arrayBuf.allocate(vertices.data(), vertices.size() * sizeof(vertices[0])); + + // Transfer index data to VBO 1 + indexBuf.create(); + indexBuf.bind(); + indexBuf.allocate(indices.data(), indices.size() * sizeof(indices[0])); + + } + + void rebuild() { + if (indexBuf.isCreated()) {indexBuf.destroy();} + if (arrayBuf.isCreated()) {arrayBuf.destroy();} + build(); + } + + void clear() { + indices.clear(); + vertices.clear(); + } + + /** render the element */ + void render(QOpenGLShaderProgram* program) { + + // Tell OpenGL which VBOs to use + arrayBuf.bind(); + indexBuf.bind(); + + for (int i = 0; i < 4; ++i) { + if (textures[i]) { textures[i]->bind(i); } + } + + // vertices + int vertLoc = program->attributeLocation("a_position"); + program->enableAttributeArray(vertLoc); + program->setAttributeBuffer(vertLoc, GL_FLOAT, vertices[0].getVertOffset(), 3, sizeof(vertices[0])); + + // Tell OpenGL programmable pipeline how to locate vertex texture coordinate data + int normLoc = program->attributeLocation("a_normal"); + program->enableAttributeArray(normLoc); + program->setAttributeBuffer(normLoc, GL_FLOAT, vertices[0].getNormOffset(), 3, sizeof(vertices[0])); + + int texcoordLocation = program->attributeLocation("a_texcoord"); + program->enableAttributeArray(texcoordLocation); + program->setAttributeBuffer(texcoordLocation, GL_FLOAT, vertices[0].getTexOffset(), 2, sizeof(vertices[0])); + + // bind tangent data? + if (T::hasTangent()) { + int tanLocation = program->attributeLocation("a_tangent"); + program->enableAttributeArray(tanLocation); + program->setAttributeBuffer(tanLocation, GL_FLOAT, vertices[0].getTanOffset(), 3, sizeof(vertices[0])); + } + + + // texture + program->setUniformValue("texture", 0); + + // Draw cube geometry using indices from VBO 1 + glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0); + + } + +private: + + void addFace(const T& vnt1, const T& vnt2, const T& vnt3, const int mode) { + + // add vertices (remove duplicates!) + const int i1 = addOnce(vnt1); + const int i2 = addOnce(vnt2); + const int i3 = addOnce(vnt3); + + // get current orientation + const bool ccw = GLHelper::isCCW(vnt1.vert, vnt2.vert, vnt3.vert); + + // create indices + if (mode == 0 || (mode == 1 && ccw) || (mode == 2 && !ccw) ) { + indices.push_back(i1); + indices.push_back(i2); + indices.push_back(i3); + } else { + indices.push_back(i3); + indices.push_back(i2); + indices.push_back(i1); + } + + } + + /** to conserve memory, avoid duplicate VNTs! */ + int addOnce(const T& vnt) { + + const auto it = std::find(vertices.begin(), vertices.end(), vnt); + + if (it == vertices.end()) { + const int idx = vertices.size(); + vertices.push_back(vnt); + return idx; + } else { + const int idx = it - vertices.begin(); + return idx; + } + + } + + + + +}; + +#endif // GLTRIANGLES_H diff --git a/qml.qrc b/qml.qrc index 12aec8d..e8bac0b 100644 --- a/qml.qrc +++ b/qml.qrc @@ -2,5 +2,7 @@ res/gl/fragment1.glsl res/gl/vertex1.glsl + res/gl/tex/floor1.jpg + res/gl/tex/wall1.jpg diff --git a/res/gl/fragment1.glsl b/res/gl/fragmentLine.glsl similarity index 51% rename from res/gl/fragment1.glsl rename to res/gl/fragmentLine.glsl index 73d9263..3c0876b 100644 --- a/res/gl/fragment1.glsl +++ b/res/gl/fragmentLine.glsl @@ -4,13 +4,17 @@ precision mediump int; precision mediump float; #endif -uniform sampler2D texture; +uniform vec3 lightWorldPos; +uniform vec4 color; +// interpolated values +varying vec3 v_WorldPos; +varying vec3 v_normal; varying vec2 v_texcoord; -void main() -{ +void main() { + // Set fragment color from texture - gl_FragColor = texture2D(texture, v_texcoord); - gl_FragColor = vec4(1,1,1,1); + gl_FragColor = color; + } diff --git a/res/gl/fragmentTex.glsl b/res/gl/fragmentTex.glsl new file mode 100644 index 0000000..cb57729 --- /dev/null +++ b/res/gl/fragmentTex.glsl @@ -0,0 +1,76 @@ +#ifdef GL_ES +// Set default precision to medium +precision mediump int; +precision mediump float; +#endif + +uniform vec3 eyeWorldPos; +uniform vec3 lightWorldPos; + +uniform sampler2D texDiffuse; +uniform sampler2D texNormalMap; + +// interpolated values +varying vec3 v_WorldPos; +varying vec3 v_CamPos; +varying vec3 v_normal; +varying vec2 v_texcoord; +varying mat3 normalMat; + +void main() { + + // diffuse fragment color + + vec4 ambient = texture2D(texDiffuse, v_texcoord); + vec4 diffuse = ambient * vec4(0.8, 0.6, 0.35, 1.0); + vec4 specular = vec4(1,1,1,1); + + // get the normal from the normal map + //vec3 normal = vec3(0,0,1); + vec3 normal = normalize(texture2D(texNormalMap, v_texcoord).rgb - 0.5) * 2.0; + // and convert from texture's normal-space into the object's normal-space using the normal-matrix + normal = normalize(normalMat * normal); + + + // direction from fragment towards the light + vec3 lightDir = normalize(lightWorldPos - v_WorldPos); + + // direction from fragment towards the eye + vec3 eyeDir = normalize(eyeWorldPos - v_WorldPos); + + + // diffuse lighting + float diffuseIntensity = max(dot(lightDir, normal), 0.0); + + // specular lighting +// vec3 h = normalize(lightDir + eyeDir); +// float intSpec = max(dot(h, normal), 0.0); +// float specularIntensity = pow(intSpec, 2.0); + float specularIntensity = pow(max(0.0, dot(eyeDir, reflect(-lightDir, normal))), 16); + + + // distance between fragment and light (in meter) + float distToLight_m = length(lightWorldPos - v_WorldPos); + float lightInt = clamp(1.0 / (distToLight_m / 10.0), 0.0, 1.0); + + // Set fragment color from texture + vec4 finalColor = + ambient * 0.05 + + diffuse * 0.95 * diffuseIntensity * lightInt + + clamp(specular * specularIntensity, 0.0, 1.0) * 1.0; + + gl_FragColor = clamp(finalColor, 0.0, 1.0); + + // FOG + //float mixing = pow((1.0 - v_CamPos.z * 3.0), 2); + //gl_FragColor = mix(gl_FragColor, vec4(0.5, 0.5, 0.5, 1.0), 1.0-mixing); + + //gl_FragColor = gl_FragColor * 1; + +// gl_FragColor = +// ambient * 0.001 + +// diffuse * 0.009 * diffuseIntensity + +// specular * 0.6 * specularIntensity; + + +} diff --git a/res/gl/fragmentTexSimple.glsl b/res/gl/fragmentTexSimple.glsl new file mode 100644 index 0000000..5da6b3a --- /dev/null +++ b/res/gl/fragmentTexSimple.glsl @@ -0,0 +1,25 @@ +#ifdef GL_ES +// Set default precision to medium +precision mediump int; +precision mediump float; +#endif + +//uniform vec3 eyeWorldPos; +//uniform vec3 lightWorldPos; + +uniform sampler2D texDiffuse; + +// interpolated values +//varying vec3 v_WorldPos; +//varying vec3 v_normal; +varying vec2 v_texcoord; + +void main() { + + // fragment color + vec4 ambient = texture2D(texDiffuse, v_texcoord); + + // set + gl_FragColor = ambient; + +} diff --git a/res/gl/tex/arrows.png b/res/gl/tex/arrows.png new file mode 100644 index 0000000..3454106 Binary files /dev/null and b/res/gl/tex/arrows.png differ diff --git a/res/gl/tex/door1.jpg b/res/gl/tex/door1.jpg new file mode 100644 index 0000000..1fd3b2f Binary files /dev/null and b/res/gl/tex/door1.jpg differ diff --git a/res/gl/tex/door2.jpg b/res/gl/tex/door2.jpg new file mode 100644 index 0000000..329187f Binary files /dev/null and b/res/gl/tex/door2.jpg differ diff --git a/res/gl/tex/door2_normal.jpg b/res/gl/tex/door2_normal.jpg new file mode 100644 index 0000000..b0bcf13 Binary files /dev/null and b/res/gl/tex/door2_normal.jpg differ diff --git a/res/gl/tex/floor1.jpg b/res/gl/tex/floor1.jpg new file mode 100644 index 0000000..d591086 Binary files /dev/null and b/res/gl/tex/floor1.jpg differ diff --git a/res/gl/tex/floor2.jpg b/res/gl/tex/floor2.jpg new file mode 100644 index 0000000..45470d5 Binary files /dev/null and b/res/gl/tex/floor2.jpg differ diff --git a/res/gl/tex/floor2_normal.jpg b/res/gl/tex/floor2_normal.jpg new file mode 100644 index 0000000..629ec2a Binary files /dev/null and b/res/gl/tex/floor2_normal.jpg differ diff --git a/res/gl/tex/floor3.jpg b/res/gl/tex/floor3.jpg new file mode 100644 index 0000000..90a8893 Binary files /dev/null and b/res/gl/tex/floor3.jpg differ diff --git a/res/gl/tex/floor3_normal.jpg b/res/gl/tex/floor3_normal.jpg new file mode 100644 index 0000000..1eb52fc Binary files /dev/null and b/res/gl/tex/floor3_normal.jpg differ diff --git a/res/gl/tex/floor4.jpg b/res/gl/tex/floor4.jpg new file mode 100644 index 0000000..5bda1c5 Binary files /dev/null and b/res/gl/tex/floor4.jpg differ diff --git a/res/gl/tex/floor4_normal.jpg b/res/gl/tex/floor4_normal.jpg new file mode 100644 index 0000000..ea8049d Binary files /dev/null and b/res/gl/tex/floor4_normal.jpg differ diff --git a/res/gl/tex/granite1.jpg b/res/gl/tex/granite1.jpg new file mode 100644 index 0000000..67d9937 Binary files /dev/null and b/res/gl/tex/granite1.jpg differ diff --git a/res/gl/tex/granite1_normal.jpg b/res/gl/tex/granite1_normal.jpg new file mode 100644 index 0000000..3b4cafc Binary files /dev/null and b/res/gl/tex/granite1_normal.jpg differ diff --git a/res/gl/tex/wall1.jpg b/res/gl/tex/wall1.jpg new file mode 100644 index 0000000..8673f4a Binary files /dev/null and b/res/gl/tex/wall1.jpg differ diff --git a/res/gl/tex/wall2_normal.jpg b/res/gl/tex/wall2_normal.jpg new file mode 100644 index 0000000..bd6d53d Binary files /dev/null and b/res/gl/tex/wall2_normal.jpg differ diff --git a/res/gl/tex/wood1.jpg b/res/gl/tex/wood1.jpg new file mode 100644 index 0000000..f2b9442 Binary files /dev/null and b/res/gl/tex/wood1.jpg differ diff --git a/res/gl/vertex1.glsl b/res/gl/vertex1.glsl index 5fa4678..2543183 100644 --- a/res/gl/vertex1.glsl +++ b/res/gl/vertex1.glsl @@ -4,19 +4,39 @@ precision mediump int; precision mediump float; #endif +uniform mat4 m_matrix; +uniform mat4 mv_matrix; uniform mat4 mvp_matrix; -attribute vec4 a_position; +attribute vec3 a_position; +attribute vec3 a_normal; attribute vec2 a_texcoord; +attribute vec3 a_tangent; +varying vec3 v_WorldPos; +varying vec3 v_CamPos; + +varying vec3 v_normal; varying vec2 v_texcoord; +varying mat3 normalMat; -void main() -{ - // Calculate vertex position in screen space - gl_Position = mvp_matrix * a_position; +void main() { - // Pass texture coordinate to fragment shader - // Value will be automatically interpolated to fragments inside polygon faces + + + // interpolation: + v_WorldPos = vec3(m_matrix * vec4(a_position, 1.0)); + v_CamPos = vec3(mv_matrix * vec4(a_position, 1.0)); + v_normal = a_normal;//normalize(vec3(mv_matrix * vec4(a_normal, 0.0))); v_texcoord = a_texcoord; + + + + // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/ + vec3 bitangent = cross(a_normal, a_tangent); + normalMat = mat3(a_tangent, bitangent, a_normal); + + // 2D position for the vertex + gl_Position = mvp_matrix * vec4(a_position, 1.0); + } diff --git a/yasmin.pro b/yasmin.pro index e7db070..6fa1c3f 100644 --- a/yasmin.pro +++ b/yasmin.pro @@ -22,7 +22,9 @@ OTHER_FILES += \ SOURCES += \ main.cpp \ map/MapView.cpp \ - map/Geometry.cpp + map/Geometry.cpp \ + lib/gpc/gpc.cpp \ + ../Indoor/lib/tinyxml/tinyxml2.cpp RESOURCES += qml.qrc @@ -40,6 +42,7 @@ INSTALLS += target # android-sources/src/WiFi.java HEADERS += \ + lib/gpc/gpc.h \ sensors/linux/WiFiSensorLinux.h \ sensors/android/WiFiSensorAndroid.h \ sensors/StepSensor.h \ @@ -53,7 +56,12 @@ HEADERS += \ misc/fixc11.h \ sensors/dummy/WiFiSensorDummy.h \ map/MapView.h \ - map/Geometry.h + map/Geometry.h \ + map/FloorRenderer.h \ + map/Ground.h \ + lib/gpc/Polygon.h \ + map/GL.h \ + Stairs.h DISTFILES += \ android-sources/src/MyActivity.java \