diff --git a/IndoorMap.pro b/IndoorMap.pro index b5e4150..35e13f4 100644 --- a/IndoorMap.pro +++ b/IndoorMap.pro @@ -4,7 +4,9 @@ # #------------------------------------------------- -QT += core gui +QT += core gui opengl + +CONFIG += c++11 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets svg @@ -14,8 +16,10 @@ TEMPLATE = app INCLUDEPATH += \ ../ -SOURCES += main.cpp\ - MainWindow.cpp \ +SOURCES += \ + lib/gpc/gpc.cpp \ + main.cpp \ + MainWindow.cpp \ MainController.cpp \ mapview/tools/ToolSelector.cpp \ mapview/tools/Tool.cpp \ @@ -25,7 +29,9 @@ SOURCES += main.cpp\ params/ActionWidget.cpp \ params/ToolBox.cpp \ mapview/model/MapModel.cpp \ - tree/MapTreeModel.cpp + tree/MapTreeModel.cpp \ + mapview/3D/MapView3D.cpp + HEADERS += MainWindow.h \ mapview/Painter.h \ @@ -74,16 +80,33 @@ HEADERS += MainWindow.h \ mapview/model/MMFloorBeacon.h \ mapview/model/MMFloorAccessPoints.h \ mapview/model/MMFloorBeacons.h \ - mapview/model/MMFloorUnderlay.h \ mapview/model/IHasFile.h \ mapview/elements/MV2DElementFloorUnderlay.h \ mapview/model/MMFloorUnderlayImage.h \ - mapview/model/IHasParams.h + mapview/model/IHasParams.h \ + mapview/model/MMFloorUnderlays.h \ + mapview/elements/MV2DElementPOI.h \ + mapview/model/MMFloorPOIs.h \ + mapview/model/MMFloorPOI.h \ + mapview/3D/MapView3D.h \ + mapview/3D/MV3DElement.h \ + mapview/3D/MV3DElementFloorObstacleWall.h \ + mapview/3D/MV3DElementFloorObstaclePillar.h \ + mapview/3D/MV3DElementAccessPoint.h \ + mapview/3D/misc/Cube.h \ + mapview/3D/MV3DElementFloorOutlinePolygon.h \ + mapview/3D/MV3DElementFloorOutline.h \ + mapview/3D/misc/Polygon.h \ + mapview/grid/MapView3DGrid.h \ + mapview/grid/MapModelGrid.h \ + mapview/3DGrid/GridModel.h \ + mapview/3DGrid/GridRenderer.h \ + mapview/3DGrid/MyNode.h FORMS += MainWindow.ui SOURCES += \ - /apps/android/workspace/Indoor/lib/tinyxml/tinyxml2.cpp + ../Indoor/lib/tinyxml/tinyxml2.cpp RESOURCES += \ res.qrc diff --git a/MainController.cpp b/MainController.cpp index aae1609..d1198eb 100644 --- a/MainController.cpp +++ b/MainController.cpp @@ -4,6 +4,7 @@ #include "mapview/model/MapModel.h" #include "mapview/model/MapModelElement.h" + #include "mapview/tools/ToolSelector.h" #include "mapview/tools/ToolMoveMap.h" #include "mapview/tools/ToolMapZoom.h" @@ -26,11 +27,17 @@ MainController::MainController() { mw->resize(1000, 700); MapView2D* mapView2D = mw->getMapView2D(); + MapView3D* mapView3D = mw->getMapView3D(); QTreeView* layerTree = mw->getTree(); + + // model setup mapModel = new MapModel(); - //mapModel->load("/apps/android/workspace/IndoorApp/res/map.xml"); mapView2D->setModel(mapModel); + mapView3D->setModel(mapModel); + + mapTreeModel = new MapTreeModel(mapModel); + layerTree->setModel(mapTreeModel); ToolMoveMap* moveMap = new ToolMoveMap(); ToolRuler* ruler = new ToolRuler(); @@ -44,19 +51,32 @@ MainController::MainController() { mapView2D->getTools().enable(mapSelector); mapView2D->getTools().enable(ruler); - - mapTreeModel = new MapTreeModel(mapModel); - layerTree->setModel(mapTreeModel); - connect(layerTree, SIGNAL(clicked(QModelIndex)), this, SLOT(layerSelected(QModelIndex))); connect(mapSelector, SIGNAL(onMapElementSelected(MapModelElement*)), this, SLOT(mapElementSelected(MapModelElement*))); - connect(mw->getActionWidget(), SIGNAL(onLoad()), this, SLOT(onLoad())); - connect(mw->getActionWidget(), SIGNAL(onSave()), this, SLOT(onSave())); + + // model events connect(mapModel, SIGNAL(aboutToReset()), this, SLOT(onMapModelAboutToReset())); connect(mapModel, SIGNAL(reset()), this, SLOT(onMapModelReset())); - mapModel->load("/apps/map9.xml"); + // load/save + connect(mw->getActionWidget(), SIGNAL(onLoad()), this, SLOT(onLoad())); + connect(mw->getActionWidget(), SIGNAL(onSave()), this, SLOT(onSave())); + // 3D view change + connect(mw->getShow3DFloorplan(), SIGNAL(triggered(bool)), this, SLOT(onShow3DFloorplan())); + connect(mw->getShow3DGrid(), SIGNAL(triggered(bool)), this, SLOT(onShow3DGrid())); + + + mapModel->load("../IndoorMap/maps/SHL9.xml"); + +} + +void MainController::onShow3DFloorplan() { + mw->getMapView3D()->showFloorplan(); +} + +void MainController::onShow3DGrid() { + mw->getMapView3D()->showGrid(); } void MainController::layerSelected(QModelIndex idx) { diff --git a/MainController.h b/MainController.h index ae138cb..2d4aaf1 100644 --- a/MainController.h +++ b/MainController.h @@ -34,6 +34,11 @@ public slots: void onMapModelAboutToReset(); void onMapModelReset(); +private slots: + + void onShow3DFloorplan(); + void onShow3DGrid(); + private: diff --git a/MainWindow.cpp b/MainWindow.cpp index a1a6bf6..abd0bc6 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -9,6 +9,8 @@ #include +#include "mapview/3D/MapView3D.h" + #include "mapview/tools/ToolMoveMap.h" #include "mapview/tools/ToolRuler.h" #include "mapview/tools/ToolMapZoom.h" @@ -24,9 +26,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { + ui->setupUi(this); mapView2D = new MapView2D(); + mapView3D = new MapView3D(); + elementParamWidget = new ElementParamWidget(); layerParamWidget = new LayerParamWidget(); actionWidget = new ActionWidget(); @@ -35,18 +40,29 @@ MainWindow::MainWindow(QWidget *parent) : ui->layButtons->addWidget(toolBoxWidget); ui->layMap->addWidget(mapView2D); + ui->layMap->addWidget(mapView3D); ui->layTree->addWidget(layerParamWidget); ui->layTree->addWidget(elementParamWidget); ui->layTree->addWidget(actionWidget); + } MainWindow::~MainWindow() { delete ui; } + +QAction* MainWindow::getShow3DFloorplan() { + return ui->actShow3DFloorplan; +} + +QAction* MainWindow::getShow3DGrid() { + return ui->actShow3DGrid; +} + QTreeView* MainWindow::getTree() { return ui->layerTree; } diff --git a/MainWindow.h b/MainWindow.h index 69fc227..e61b06b 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -3,6 +3,7 @@ #include class MapView2D; +class MapView3D; class QTreeView; class ElementParamWidget; class LayerParamWidget; @@ -22,16 +23,22 @@ public: ~MainWindow(); MapView2D* getMapView2D() {return mapView2D;} + MapView3D* getMapView3D() {return mapView3D;} + ElementParamWidget* getElementParamWidget() {return elementParamWidget;} LayerParamWidget* getLayerParamWidget() {return layerParamWidget;} ToolBoxWidget* getToolBoxWidget() {return toolBoxWidget;} ActionWidget* getActionWidget() {return actionWidget;} + QAction* getShow3DFloorplan(); + QAction* getShow3DGrid(); + QTreeView* getTree(); private: Ui::MainWindow *ui; MapView2D* mapView2D; + MapView3D* mapView3D; ElementParamWidget* elementParamWidget; LayerParamWidget* layerParamWidget; ActionWidget* actionWidget; diff --git a/MainWindow.ui b/MainWindow.ui index 37acab0..5ff07fc 100644 --- a/MainWindow.ui +++ b/MainWindow.ui @@ -27,7 +27,7 @@ - 200 + 250 16777215 @@ -38,6 +38,46 @@ + + + + 0 + 0 + 777 + 26 + + + + + View + + + + 3D + + + + + + + + + + + + 2D + + + + + Floorplan + + + + + Grid + + 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/mapview/3D/MV3DElement.h b/mapview/3D/MV3DElement.h new file mode 100644 index 0000000..583132f --- /dev/null +++ b/mapview/3D/MV3DElement.h @@ -0,0 +1,25 @@ +#ifndef MV3DELEMENT_H +#define MV3DELEMENT_H + +#include "MapView3D.h" + +/** + * represents one drawable + * element shown within the MapView3D + */ +class MV3DElement { + +public: + + /** dtor */ + virtual ~MV3DElement() {;} + +public: + + /** repaint me */ + virtual void paintGL() = 0; + + +}; + +#endif // MV3DELEMENT_H diff --git a/mapview/3D/MV3DElementAccessPoint.h b/mapview/3D/MV3DElementAccessPoint.h new file mode 100644 index 0000000..1d87fb9 --- /dev/null +++ b/mapview/3D/MV3DElementAccessPoint.h @@ -0,0 +1,34 @@ +#ifndef MV3DELEMENTACCESSPOINT_H +#define MV3DELEMENTACCESSPOINT_H + +#include + +#include "misc/Cube.h" +#include "MV3DElement.h" + +class MV3DElementAccessPoint : public MV3DElement { + + Floorplan::Floor* f; + Floorplan::AccessPoint* ap; + +public: + + /** ctor */ + MV3DElementAccessPoint(Floorplan::Floor* f, Floorplan::AccessPoint* ap) : f(f), ap(ap) { + ; + } + +protected: + + + /** repaint me */ + void paintGL() override { + + Cube cube(ap->getPos(f), 0.5); + cube.paintGL(); + + } + +}; + +#endif // MV3DELEMENTACCESSPOINT_H diff --git a/mapview/3D/MV3DElementFloorObstaclePillar.h b/mapview/3D/MV3DElementFloorObstaclePillar.h new file mode 100644 index 0000000..218633e --- /dev/null +++ b/mapview/3D/MV3DElementFloorObstaclePillar.h @@ -0,0 +1,4 @@ +#ifndef MV3DELEMENTFLOOROBSTACLEPILLAR_H +#define MV3DELEMENTFLOOROBSTACLEPILLAR_H + +#endif // MV3DELEMENTFLOOROBSTACLEPILLAR_H diff --git a/mapview/3D/MV3DElementFloorObstacleWall.h b/mapview/3D/MV3DElementFloorObstacleWall.h new file mode 100644 index 0000000..9b11405 --- /dev/null +++ b/mapview/3D/MV3DElementFloorObstacleWall.h @@ -0,0 +1,86 @@ +#ifndef MV3DELEMENTFLOOROBSTACLEWALL_H +#define MV3DELEMENTFLOOROBSTACLEWALL_H + +#include + +#include "MV3DElement.h" + +class MV3DElementFloorObstacleWall : public MV3DElement { + + Floorplan::Floor* f; + Floorplan::FloorObstacleLine* fo; + +public: + + /** ctor */ + MV3DElementFloorObstacleWall(Floorplan::Floor* f, Floorplan::FloorObstacleLine* fo) : f(f), fo(fo) { + ; + } + +protected: + + Point3 cross(Point3 u, Point3 v) { + float x = u.y*v.z - u.z*v.y; + float y = u.z*v.x - u.x*v.z; + float z = u.x*v.y - u.y*v.x; + return Point3(x,y,z); + } + + /** repaint me */ + void paintGL() override { + + float y1 = f->atHeight; + float y2 = y1+f->height; + + + + + + + Point3 p1 = Point3(fo->from.x, y1, fo->from.y); + Point3 p2 = Point3(fo->to.x, y1, fo->to.y); + Point3 p3 = Point3(fo->to.x, y2, fo->to.y); + Point3 p4 = Point3(fo->from.x, y2, fo->from.y); + + Point3 v1 = p2-p1; + Point3 v2 = p3-p1; + Point3 n = cross(v1, v2); + n/=n.length(); + + // align normals to virtual viewport + Point3 view(99,99,99); + if ((view-n).length() > (view+n).length()) {n = -n;} + + if (fo->type == Floorplan::ObstacleType::WALL) { + + // fill the wall + glColor3f(0.75, 0.75, 0.75); + glDisable(GL_CULL_FACE); + glBegin(GL_QUADS); + glNormal3f(n.x, n.y, n.z); + glVertex3f(p1.x, p1.y, p1.z); + glVertex3f(p2.x, p2.y, p2.z); + glVertex3f(p3.x, p3.y, p3.z); + glVertex3f(p4.x, p4.y, p4.z); + glEnd(); + glEnable(GL_CULL_FACE); + + } + + glColor3f(0,0,0); + +// glDisable(GL_LIGHTING); +// glBegin(GL_LINE_STRIP); +// glVertex3f(p1.x, p1.y, p1.z); +// glVertex3f(p2.x, p2.y, p2.z); +// glVertex3f(p3.x, p3.y, p3.z); +// glVertex3f(p4.x, p4.y, p4.z); +// glVertex3f(p1.x, p1.y, p1.z); +// glEnd(); +// glEnable(GL_LIGHTING); + + } + +}; + +#endif // MV3DELEMENTFLOOROBSTACLEWALL_H diff --git a/mapview/3D/MV3DElementFloorOutline.h b/mapview/3D/MV3DElementFloorOutline.h new file mode 100644 index 0000000..211548b --- /dev/null +++ b/mapview/3D/MV3DElementFloorOutline.h @@ -0,0 +1,71 @@ +#ifndef MV3DELEMENTFLOOROUTLINE_H +#define MV3DELEMENTFLOOROUTLINE_H + +#include + +#include "misc/Cube.h" +#include "MV3DElement.h" + +#include "../../lib/gpc/gpc.h" +#include "misc/Polygon.h" + + +class MV3DElementFloorOutline : public MV3DElement { + + Floorplan::Floor* f; + Floorplan::FloorOutline* out; + +public: + + /** ctor */ + MV3DElementFloorOutline(Floorplan::Floor* f, Floorplan::FloorOutline* out) : f(f), out(out) { + ; + } + +protected: + + + + + + /** repaint me */ + void paintGL() override { + + std::vector add; + std::vector rem; + + std::vector polys = *out; + + 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(f->atHeight); + + glColor3f(0.2, 0.2, 0.2); + glDisable(GL_CULL_FACE); + for (const std::vector& tria : trias) { + glBegin(GL_TRIANGLE_STRIP); + for (const Point3& p3 : tria) { + glVertex3f(p3.x, p3.z, p3.y); + } + glEnd(); + } + glEnable(GL_CULL_FACE); + + + + } + + + +}; + + +#endif // MV3DELEMENTFLOOROUTLINE_H diff --git a/mapview/3D/MV3DElementFloorOutlinePolygon.h b/mapview/3D/MV3DElementFloorOutlinePolygon.h new file mode 100644 index 0000000..5b63c6e --- /dev/null +++ b/mapview/3D/MV3DElementFloorOutlinePolygon.h @@ -0,0 +1,53 @@ +#ifndef MV3DELEMENTFLOOROUTLINEPOLYGON_H +#define MV3DELEMENTFLOOROUTLINEPOLYGON_H + +#include + +#include "misc/Cube.h" +#include "MV3DElement.h" + +class MV3DElementFloorOutlinePolygon : public MV3DElement { + + Floorplan::Floor* f; + Floorplan::FloorOutlinePolygon* poly; + +public: + + /** ctor */ + MV3DElementFloorOutlinePolygon(Floorplan::Floor* f, Floorplan::FloorOutlinePolygon* poly) : f(f), poly(poly) { + ; + } + +protected: + + + /** repaint me */ + void paintGL() override { + + glDisable(GL_CULL_FACE); + + switch (poly->method) { + case Floorplan::OutlineMethod::ADD: + glColor3f(1,1,1); + break; + case Floorplan::OutlineMethod::REMOVE: + glColor3f(0.2, 0.2, 0.2); + break; + } + + + glBegin(GL_POLYGON); + glNormal3f(0,1,0); + for (Point2 p2 : poly->poly.points) { + Point3 p3(p2.x, p2.y, f->atHeight); + glVertex3f(p3.x, p3.z, p3.y); + } + glEnd(); + + glEnable(GL_CULL_FACE); + + } + +}; + +#endif // MV3DELEMENTFLOOROUTLINEPOLYGON_H diff --git a/mapview/3D/MapView3D.cpp b/mapview/3D/MapView3D.cpp new file mode 100644 index 0000000..e37bcd3 --- /dev/null +++ b/mapview/3D/MapView3D.cpp @@ -0,0 +1,194 @@ +#include "MapView3D.h" + + +#include "../model/MapModelElement.h" +#include "../model/MapModel.h" + +#include "../3DGrid/GridModel.h" +#include "../3DGrid/GridRenderer.h" + +MapView3D::MapView3D(QWidget *parent) : QGLWidget(parent) { + + rot.x = 45; + rot.y = 0; + rot.z = 45; + + center.x = 0; + center.y = 0; + center.z = 0; + + scale.x = 0.05f; + scale.y = 0.05f; + scale.z = 0.05f; + +} + + +void MapView3D::initializeGL() { + + //setFormat(QGLFormat(QGL::SampleBuffers)); + + QGLWidget::initializeGL(); + + // culling, lighting, depth-test, ... + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glShadeModel(GL_SMOOTH); + + glEnable(GL_MULTISAMPLE); +// glEnable(GL_LINE_SMOOTH); + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + + GLfloat light0_position [] = {+5, 5, +5, 1}; + GLfloat light1_position [] = {-5, 5, -5, 1}; + + glLightfv ( GL_LIGHT0, GL_POSITION, light0_position ); + glLightfv ( GL_LIGHT1, GL_POSITION, light1_position ); + + GLfloat light_diffuse []={ 0.7, 0.7, 0.7, 1.0 }; + glLightfv ( GL_LIGHT0, GL_DIFFUSE, light_diffuse ); + glLightfv ( GL_LIGHT1, GL_DIFFUSE, light_diffuse ); + + + // otherwise scaling the scene kills lighting normals! + glEnable(GL_NORMALIZE); + + // allow using glColor3(r,g,b) + glEnable(GL_COLOR_MATERIAL); + +// GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; +// GLfloat mat_shininess[] = { 50.0 }; +// glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); +// glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); + + // background color + qglClearColor(Qt::white); + +} + +void MapView3D::paintGL() { + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glLoadIdentity(); + + + + // 3) scale + glScalef(scale.x, scale.y, scale.z); + + // 2) rotate around center + glRotatef(rot.x, 1.0, 0.0, 0.0); + glRotatef(rot.z, 0.0, 1.0, 0.0); + glRotatef(rot.y, 0.0, 0.0, 1.0); + + // 1) post translate (mouse moving) + glTranslatef(center.x, center.z, center.y); + + // 0) swap the y axis + //glScalef(+1, -1, +1); + + + +// // 1) translate into center +// glTranslatef(tra.x, tra.y, tra.z); + + draw(); + + +} + +void MapView3D::resizeGL(int width, int height) { + + //int side = qMin(width, height); + //glViewport((width - side) / 2, (height - side) / 2, side, side); + + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); +#ifdef QT_OPENGL_ES_1 + //glOrthof(-2, +2, -2, +2, 1.0, 15.0); +#else + //glOrtho(-2, +2, -2, +2, 1.0, 25.0); + //glFrustum(-1,1, -1,1, 0.1,20); + viewport.size.x = 2.0f; + viewport.size.y = 2.0f * height / width; + const float w = viewport.size.x; + const float h = viewport.size.y; + glOrtho(-w, +w, -h, +h, -20, +20); + //glFrustum(+w, -w, -h, +h, -20, +20); +#endif + glMatrixMode(GL_MODELVIEW); +} + + +void MapView3D::mousePressEvent(QMouseEvent* e) { + mouse.btn = e->button(); + mouse.x = e->x(); + mouse.y = e->y(); + update(); +} + +void MapView3D::mouseMoveEvent(QMouseEvent* e) { + float dx = mouse.x - e->x(); + float dy = mouse.y - e->y(); + if (mouse.btn == 1) { + rot.z -= dx/2.0f; // upward + rot.x -= dy/2.0f; + } else if (mouse.btn == 4) { + Point3 vec(-dx / width() * 2 * viewport.size.x, 0, dy / height() * 2 * viewport.size.y); + vec = vec.rot(rot.x/180*M_PI, rot.y/180*M_PI, rot.z/180*M_PI); + vec /= scale; + center += vec; + } + mouse.x = e->x(); + mouse.y = e->y(); + update(); +} + +void MapView3D::mouseReleaseEvent(QMouseEvent* e) { + update(); +} + +void MapView3D::wheelEvent(QWheelEvent* e) { + float f = e->delta() / 120.0f; + scale *= (f > 0) ? (2) : (0.5); + update(); +} + + +void MapView3D::showFloorplan() { + if (gridModel) {delete gridModel; gridModel = nullptr;} + update(); +} + +void MapView3D::showGrid() { + if (gridModel) {delete gridModel; gridModel = nullptr;} + GridModel* gm = new GridModel(); + Floorplan::IndoorMap* im = getModel()->getMap(); + gm->rebuild(im); + this->gridModel = gm; + update(); +} + +void MapView3D::draw() { + + if (gridModel) { + + // show grid + GridRenderer renderer(gridModel->getGrid()); + renderer.paintGL(); + + } else { + + // show floorplan + for (MapModelElement* el : getModel()->getVisibleElements()) { + if (el->getMV3D()) {el->getMV3D()->paintGL();} + } + } + +} diff --git a/mapview/3D/MapView3D.h b/mapview/3D/MapView3D.h new file mode 100644 index 0000000..10127b6 --- /dev/null +++ b/mapview/3D/MapView3D.h @@ -0,0 +1,72 @@ +#ifndef MAPVIEW3D_H +#define MAPVIEW3D_H + +#include +#include + +#include + +class MapModel; +class GridModel; + +class MapView3D : public QGLWidget { + + Q_OBJECT + +public: + + MapView3D(QWidget* parent = 0); + + /** set the underlying data-model */ + void setModel(MapModel* mdl) { + this->model = mdl; + update(); + } + + /** get the underlying data-model */ + MapModel* getModel() {return model;} + + /** show 3D rendered floorplan */ + void showFloorplan(); + + /** show 3D rendered grid derived from the floorplan */ + void showGrid(); + +private: + + /** the underlying data-model */ + MapModel* model = nullptr; + GridModel* gridModel = nullptr; + + Point3 rot; + Point3 center; + Point3 scale; + + struct Mouse { + int btn; + float x; + float y; + } mouse; + + struct Viewport { + Point2 size; + } viewport; + + + +protected: + void initializeGL(); + void paintGL(); + void resizeGL(int width, int height); + + void mousePressEvent(QMouseEvent* e); + void mouseMoveEvent(QMouseEvent* e); + void mouseReleaseEvent(QMouseEvent* e); + void wheelEvent(QWheelEvent* e); + +private: + void draw(); + +}; + +#endif // MAPVIEW3D_H diff --git a/mapview/3D/misc/Cube.h b/mapview/3D/misc/Cube.h new file mode 100644 index 0000000..7819a32 --- /dev/null +++ b/mapview/3D/misc/Cube.h @@ -0,0 +1,82 @@ +#ifndef CUBE_H +#define CUBE_H + +#include +#include + +class Cube { + +private: + + Point3 pos; + float size; + + +public: + + Cube(Point3 pos, float size) : pos(pos), size(size) { + + } + + void paintGL() { + + float s = size; + + glPushMatrix(); + glTranslatef(pos.x, pos.z, pos.y); + + glColor3f(0,0,1); + + glBegin(GL_QUADS); + + // bottom + glNormal3f(0,-1,0); + glVertex3f(+s, -s, -s); + glVertex3f(+s, -s, +s); + glVertex3f(-s, -s, +s); + glVertex3f(-s, -s, -s); + + // top + glNormal3f(0,+1,0); + glVertex3f(-s, +s, -s); + glVertex3f(-s, +s, +s); + glVertex3f(+s, +s, +s); + glVertex3f(+s, +s, -s); + + // left + glNormal3f(-1,0,0); + glVertex3f(-s, -s, -s); + glVertex3f(-s, -s, +s); + glVertex3f(-s, +s, +s); + glVertex3f(-s, +s, -s); + + // right + glNormal3f(+1,0,0); + glVertex3f(+s, +s, -s); + glVertex3f(+s, +s, +s); + glVertex3f(+s, -s, +s); + glVertex3f(+s, -s, -s); + + // front + glNormal3f(0,0,+1); + glVertex3f(+s, +s, +s); + glVertex3f(-s, +s, +s); + glVertex3f(-s, -s, +s); + glVertex3f(+s, -s, +s); + + // rear + glNormal3f(0,0,-1); + glVertex3f(+s, -s, -s); + glVertex3f(-s, -s, -s); + glVertex3f(-s, +s, -s); + glVertex3f(+s, +s, -s); + + glEnd(); + + glPopMatrix(); + + } + +}; + +#endif // CUBE_H diff --git a/mapview/3D/misc/Polygon.h b/mapview/3D/misc/Polygon.h new file mode 100644 index 0000000..6aa1757 --- /dev/null +++ b/mapview/3D/misc/Polygon.h @@ -0,0 +1,109 @@ +#ifndef POLYGON_H +#define POLYGON_H + +#include +#include "../../../lib/gpc/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 // POLYGON_H diff --git a/mapview/3DGrid/GridModel.h b/mapview/3DGrid/GridModel.h new file mode 100644 index 0000000..42bb5a5 --- /dev/null +++ b/mapview/3DGrid/GridModel.h @@ -0,0 +1,38 @@ +#ifndef GRIDMODEL_H +#define GRIDMODEL_H + +#include +#include +#include + + +#include "MyNode.h" + +/** + * used for 3D grid rendering + */ +class GridModel { + +private: + + int gridSize_cm = 40; + Grid grid; + Floorplan::IndoorMap* im; + +public: + + GridModel() : grid(gridSize_cm) { + ; + } + + Grid* getGrid() {return &grid;} + + void rebuild(Floorplan::IndoorMap* im) { + GridFactory fac(grid); + fac.build(im); + int i = 0;(void) i; + } + +}; + +#endif // GRIDMODEL_H diff --git a/mapview/3DGrid/GridRenderer.h b/mapview/3DGrid/GridRenderer.h new file mode 100644 index 0000000..4828bf4 --- /dev/null +++ b/mapview/3DGrid/GridRenderer.h @@ -0,0 +1,40 @@ +#ifndef GRIDRENDERER_H +#define GRIDRENDERER_H + +#include "../3D/MV3DElement.h" +#include "MyNode.h" + +#include + + +class GridRenderer : public MV3DElement { + +private: + + Grid* grid; + +public: + + GridRenderer(Grid* grid) : grid(grid) { + ; + } + + virtual void paintGL() override { + + glDisable(GL_LIGHTING); + glColor3f(0,0,0); + + glPointSize(0.1f); + glBegin(GL_POINTS); + for (MyNode& n : *grid) { + glVertex3f(n.x_cm/100.0f, n.z_cm/100.0f*5, n.y_cm/100.0f); + } + glEnd(); + + glEnable(GL_LIGHTING); + + } + +}; + +#endif // GRIDRENDERER_H diff --git a/mapview/3DGrid/MyNode.h b/mapview/3DGrid/MyNode.h new file mode 100644 index 0000000..193b05d --- /dev/null +++ b/mapview/3DGrid/MyNode.h @@ -0,0 +1,10 @@ +#ifndef MYNODE_H +#define MYNODE_H + +#include + +struct MyNode : public GridNode, public GridPoint { + MyNode(float x, float y, float z) : GridPoint(x,y,z) {;} +}; + +#endif // MYNODE_H diff --git a/mapview/MapView2D.cpp b/mapview/MapView2D.cpp index ea032c3..0f435c0 100644 --- a/mapview/MapView2D.cpp +++ b/mapview/MapView2D.cpp @@ -44,7 +44,9 @@ void MapView2D::paintGL() { // render all visible elements qp.setRenderHint( QPainter::Antialiasing, true ); - for (MapModelElement* el : getModel()->getVisibleElements()) {el->getMV2D()->paint(p);} + for (MapModelElement* el : getModel()->getVisibleElements()) { + if (el->getMV2D()) {el->getMV2D()->paint(p);} + } qp.setRenderHint( QPainter::Antialiasing, false ); // foreground tools diff --git a/mapview/elements/MV2DElementAccessPoint.h b/mapview/elements/MV2DElementAccessPoint.h index 922ca21..2657dde 100644 --- a/mapview/elements/MV2DElementAccessPoint.h +++ b/mapview/elements/MV2DElementAccessPoint.h @@ -46,8 +46,13 @@ public: } // label + p.setPenBrush(Qt::black, Qt::NoBrush); if (p.getScaler().getScale() >= 25) { - p.p->drawText(p.getScaler().xms(ap->pos.x) + 10, p.getScaler().yms(ap->pos.y) + 5, ap->mac.c_str()); + const std::string str = ap->name + " (" + ap->name + ")"; + p.p->drawText(p.getScaler().xms(ap->pos.x) + 10, p.getScaler().yms(ap->pos.y) + 5, str.c_str()); + } else if (p.getScaler().getScale() >= 10) { + const std::string str = ap->name; + p.p->drawText(p.getScaler().xms(ap->pos.x) + 10, p.getScaler().yms(ap->pos.y) + 5, str.c_str()); } } diff --git a/mapview/elements/MV2DElementBeacon.h b/mapview/elements/MV2DElementBeacon.h index 6d8ef8c..6b8c2bc 100644 --- a/mapview/elements/MV2DElementBeacon.h +++ b/mapview/elements/MV2DElementBeacon.h @@ -45,8 +45,13 @@ public: } // label + p.setPenBrush(Qt::black, Qt::NoBrush); if (p.getScaler().getScale() >= 25) { - p.p->drawText(p.getScaler().xms(b->pos.x) + 10, p.getScaler().yms(b->pos.y) + 5, b->mac.c_str()); + const std::string str = b->name + " (" + b->mac + ")"; + p.p->drawText(p.getScaler().xms(b->pos.x) + 10, p.getScaler().yms(b->pos.y) + 5, str.c_str()); + } else if (p.getScaler().getScale() >= 10) { + const std::string str = b->name; + p.p->drawText(p.getScaler().xms(b->pos.x) + 10, p.getScaler().yms(b->pos.y) + 5, str.c_str()); } } diff --git a/mapview/elements/MV2DElementFloorUnderlay.h b/mapview/elements/MV2DElementFloorUnderlay.h index 3771089..66e3a9c 100644 --- a/mapview/elements/MV2DElementFloorUnderlay.h +++ b/mapview/elements/MV2DElementFloorUnderlay.h @@ -87,7 +87,10 @@ public: bbox.add(Point2(mx2, my2)); + float opacity = p.p->opacity(); + p.p->setOpacity(0.65f); p.p->drawImage(QRectF(sx1, sy1-sh, sw, sh), img, QRectF(0,0,img.width(),img.height())); + p.p->setOpacity(opacity); // selected endpoint(s)? if (hasFocus()) { @@ -125,9 +128,19 @@ public: } virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; - (void) e; + + const float s = (e->modifiers() & Qt::ShiftModifier) ? (1.0f) : (0.1f); + + if (e->key() == Qt::Key_Up) {underlay->anchor += Point2(0, +s); return true;} + if (e->key() == Qt::Key_Down) {underlay->anchor += Point2(0, -s); return true;} + + if (e->key() == Qt::Key_Left) {underlay->anchor += Point2(-s, 0); return true;} + if (e->key() == Qt::Key_Right) {underlay->anchor += Point2(+s, 0); return true;} + return false; + } diff --git a/mapview/elements/MV2DElementPOI.h b/mapview/elements/MV2DElementPOI.h new file mode 100644 index 0000000..c484530 --- /dev/null +++ b/mapview/elements/MV2DElementPOI.h @@ -0,0 +1,99 @@ +#ifndef MV2DELEMENTPOI_H +#define MV2DELEMENTPOI_H + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" +#include + +class MV2DElementPOI : public MV2DElement { + +private: + + bool sel = false; + Floorplan::POI* poi; + +public: + + /** ctor with the AP to render/edit */ + MV2DElementPOI(Floorplan::POI* poi) : poi(poi) {;} + + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + BBox2 bbox; + bbox.add(Point2(poi->pos.x, poi->pos.y)); + bbox.grow(Point2(0.1, 0.1)); + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + return p.getDistance(poi->pos); + } + + /** repaint me */ + void paint(Painter& p) override { + + if (sel) { + p.setPenBrush(Qt::black, CFG::SEL_COLOR); + p.drawCircle(poi->pos); + } else if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawCircle(poi->pos); + } else { + p.setPenBrush(Qt::gray, Qt::NoBrush); + p.drawCircle(poi->pos); + } + + // label + p.setPenBrush(Qt::black, Qt::NoBrush); + if (p.getScaler().getScale() >= 10) { + const std::string str = poi->name; + p.p->drawText(p.getScaler().xms(poi->pos.x) + 10, p.getScaler().yms(poi->pos.y) + 5, str.c_str()); + } + + } + + + + /** mouse pressed at the given point */ + virtual void mousePressed(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + + } + + /** mouse moved to the given point */ + virtual void mouseMove(MapView2D* v, const Point2 _p) override { + (void) v; + if (sel) { + const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + poi->pos.x = p.x; + poi->pos.y = p.y; + } + } + + /** mouse released */ + virtual void mouseReleased(MapView2D* v, const Point2 _p) override { + (void) v; + (void) _p; + sel = true; + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + return false; + } + + virtual void onFocus() override { + + } + + virtual void onUnfocus() override { + sel = false; + } + +}; + +#endif // MV2DELEMENTPOI_H diff --git a/mapview/model/IHasAttributes.h b/mapview/model/IHasAttributes.h index a0ee7e8..661fbc3 100644 --- a/mapview/model/IHasAttributes.h +++ b/mapview/model/IHasAttributes.h @@ -1,21 +1,21 @@ #ifndef IHASATTRIBUTES_H #define IHASATTRIBUTES_H -#include +//#include -class IHasAttributes { +//class IHasAttributes { -public: +//public: - /** set the value for the given key */ - virtual void setAttribute(const std::string& key, const std::string& val) = 0; +// /** set the value for the given key */ +// virtual void setAttribute(const std::string& key, const std::string& val) = 0; - /** get the value for the given key */ - virtual const std::string& getAttribute(const std::string& key) const = 0; +// /** get the value for the given key */ +// virtual const std::string& getAttribute(const std::string& key) const = 0; - /** get all attributes as map */ - virtual const std::unordered_map getAttributes() const = 0; +// /** get all attributes as map */ +// virtual const std::unordered_map getAttributes() const = 0; -}; +//}; #endif // IHASATTRIBUTES_H diff --git a/mapview/model/IHasFile.h b/mapview/model/IHasFile.h index b3cfbfb..aaafbab 100644 --- a/mapview/model/IHasFile.h +++ b/mapview/model/IHasFile.h @@ -1,17 +1,17 @@ #ifndef IHASFILE_H #define IHASFILE_H -#include +//#include -class IHasFile { +//class IHasFile { -public: +//public: - virtual void setFileName(const std::string& file) = 0; +// virtual void setFileName(const std::string& file) = 0; - virtual const std::string& getFileName() const = 0; +// virtual const std::string& getFileName() const = 0; -}; +//}; #endif // IHASFILE_H diff --git a/mapview/model/IHasMAC.h b/mapview/model/IHasMAC.h index 71d13fb..cae5964 100644 --- a/mapview/model/IHasMAC.h +++ b/mapview/model/IHasMAC.h @@ -1,15 +1,15 @@ #ifndef IHASMAC_H #define IHASMAC_H -#include +//#include -class IHasMAC { -public: +//class IHasMAC { +//public: - virtual void setMAC(const std::string& mac) = 0; +// virtual void setMAC(const std::string& mac) = 0; - virtual const std::string& getMAC() const = 0; +// virtual const std::string& getMAC() const = 0; -}; +//}; #endif // IHASMAC_H diff --git a/mapview/model/IHasParams.h b/mapview/model/IHasParams.h index f2cf2ad..ced9212 100644 --- a/mapview/model/IHasParams.h +++ b/mapview/model/IHasParams.h @@ -2,13 +2,16 @@ #define IHASPARAMS_H #include -#include +#include +#include enum class ParamType { INT, FLOAT, STRING, FILE, + POINT2, + POINT3, }; class ParamValue { @@ -17,6 +20,7 @@ private: union { int _int; float _float; + float _arr[3]; }; std::string _str; @@ -29,7 +33,11 @@ public: void setValue(const std::string& val) {_str = val;} void setValue(float val) {_float = val;} void setValue(int val) {_int = val;} + void setValue(Point2 p) {_arr[0] = p.x; _arr[1] = p.y;} + void setValue(Point3 p) {_arr[0] = p.x; _arr[1] = p.y; _arr[2] = p.z;} + Point2 toPoint2() const {return Point2(_arr[0], _arr[1]);} + Point3 toPoint3() const {return Point3(_arr[0], _arr[1], _arr[2]);} std::string toString() const {return _str;} float toFloat() const {return _float;} int toInt() const {return _int;} diff --git a/mapview/model/IHasPosition3D.h b/mapview/model/IHasPosition3D.h index 1e4eaa3..966652a 100644 --- a/mapview/model/IHasPosition3D.h +++ b/mapview/model/IHasPosition3D.h @@ -1,19 +1,18 @@ #ifndef IHASPOSITION3D_H #define IHASPOSITION3D_H +//#include -#include +//class IHasPosition3D { -class IHasPosition3D { +//public: -public: +// /** set the element's 3D position */ +// virtual void setPosition3D(const Point3& p) = 0; - /** set the element's 3D position */ - virtual void setPosition3D(const Point3& p) = 0; +// /** get the element's 3D position */ +// virtual Point3 getPosition3D() const = 0; - /** get the element's 3D position */ - virtual Point3 getPosition3D() const = 0; - -}; +//}; #endif // IHASPOSITION3D_H diff --git a/mapview/model/MMFloor.h b/mapview/model/MMFloor.h index a6e5041..ed79f3c 100644 --- a/mapview/model/MMFloor.h +++ b/mapview/model/MMFloor.h @@ -6,7 +6,10 @@ #include "MMFloorObstacles.h" #include "MMFloorAccessPoints.h" #include "MMFloorBeacons.h" -#include "MMFloorUnderlay.h" +#include "MMFloorUnderlays.h" +#include "MMFloorPOIs.h" + +#include "IHasParams.h" #include @@ -16,7 +19,7 @@ * floor-layer containing one floor * and its outline, obstacles, access-points, ... */ -class MMFloor : public MapLayer, public IHasName { +class MMFloor : public MapLayer, public IHasParams { private: @@ -28,11 +31,13 @@ public: /** ctor. existing floor */ MMFloor(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR), floor(floor) { - new MMFloorOutline(this, floor); + new MMFloorUnderlays(this, floor); + elements.push_back(new MMFloorOutline(this, floor)); new MMFloorObstacles(this, floor); new MMFloorAccessPoints(this, floor); new MMFloorBeacons(this, floor); - new MMFloorUnderlay(this, floor); + new MMFloorPOIs(this, floor); + } @@ -47,9 +52,29 @@ public: std::string getLayerName() const override {return floor->name;} + virtual int getNumParams() const override { + return 1; + } - virtual void setName(const std::string& name) {this->floor->name = name;} - virtual const std::string& getName() const {return this->floor->name;} + virtual Param getParamDesc(const int idx) const override { + switch(idx) { + case 0: return Param("anem", ParamType::STRING); + } + throw 1; + } + + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return floor->name; + } + throw 1; + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch(idx) { + case 0: floor->name = val.toString(); break; + } + } }; diff --git a/mapview/model/MMFloorAccessPoint.h b/mapview/model/MMFloorAccessPoint.h index 43a83d1..656dcfd 100644 --- a/mapview/model/MMFloorAccessPoint.h +++ b/mapview/model/MMFloorAccessPoint.h @@ -2,35 +2,61 @@ #define MAPVIEWELEMENTACCESSPOINT_H #include "MapModelElement.h" -#include "IHasMAC.h" -#include "IHasName.h" +#include "IHasParams.h" #include "../elements/MV2DElementAccessPoint.h" +#include "../3D/MV3DElementAccessPoint.h" #include -class MMFloorAccessPoint : public MapModelElement, public IHasMAC, public IHasName { +class MMFloorAccessPoint : public MapModelElement, public IHasParams { private: Floorplan::Floor* floor; Floorplan::AccessPoint* ap; MV2DElementAccessPoint mv2d; + MV3DElementAccessPoint mv3d; public: MMFloorAccessPoint(MapLayer* parent, Floorplan::Floor* floor, Floorplan::AccessPoint* ap) : - MapModelElement(parent), floor(floor), ap(ap), mv2d(ap) { + MapModelElement(parent), floor(floor), ap(ap), mv2d(ap), mv3d(floor, ap) { } - virtual void setMAC(const std::string& mac) override {ap->mac = mac;} - virtual const std::string& getMAC() const override {return ap->mac;} + virtual int getNumParams() const override { + return 3; + } - virtual void setName(const std::string& name) override {ap->name = name;} - virtual const std::string& getName() const override {return ap->name;} + virtual Param getParamDesc(const int idx) const override { + switch(idx) { + case 0: return Param("name", ParamType::STRING); + case 1: return Param("MAC", ParamType::STRING); + case 2: return Param("Position", ParamType::POINT3); + } + throw 1; + } + + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return ap->name; + case 1: return ap->mac; + case 2: return ap->pos; + } + throw 1; + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch(idx) { + case 0: ap->name = val.toString(); break; + case 1: ap->mac = val.toString(); break; + case 2: ap->pos = val.toPoint3(); break; + } + } MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} void deleteMe() const override { parent->removeElement(this); diff --git a/mapview/model/MMFloorAccessPoints.h b/mapview/model/MMFloorAccessPoints.h index f9f0fc8..be64440 100644 --- a/mapview/model/MMFloorAccessPoints.h +++ b/mapview/model/MMFloorAccessPoints.h @@ -22,7 +22,7 @@ public: // add all APs for (Floorplan::AccessPoint* ap : floor->accesspoints) { - new MMFloorAccessPoint(this, floor, ap); + elements.push_back(new MMFloorAccessPoint(this, floor, ap)); } } diff --git a/mapview/model/MMFloorBeacon.h b/mapview/model/MMFloorBeacon.h index f63c16f..0f01bde 100644 --- a/mapview/model/MMFloorBeacon.h +++ b/mapview/model/MMFloorBeacon.h @@ -2,14 +2,13 @@ #define MAPVIEWELEMENTIBEACON_H #include "MapModelElement.h" -#include "IHasMAC.h" -#include "IHasName.h" +#include "IHasParams.h" #include "../elements/MV2DElementBeacon.h" #include -class MMFloorBeacon : public MapModelElement, public IHasMAC, public IHasName { +class MMFloorBeacon : public MapModelElement, public IHasParams { private: @@ -20,14 +19,38 @@ private: public: MMFloorBeacon(MapLayer* parent, Floorplan::Floor* floor, Floorplan::Beacon* b) : MapModelElement(parent), floor(floor), b(b), mv2d(b) { - + ; } - virtual void setMAC(const std::string& mac) override {b->mac = mac;} - virtual const std::string& getMAC() const override {return b->mac;} + virtual int getNumParams() const override { + return 3; + } - virtual void setName(const std::string& name) override {b->name = name;} - virtual const std::string& getName() const override {return b->name;} + virtual Param getParamDesc(const int idx) const override { + switch(idx) { + case 0: return Param("name", ParamType::STRING); + case 1: return Param("MAC", ParamType::STRING); + case 2: return Param("Position", ParamType::POINT3); + } + throw 1; + } + + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return b->name; + case 1: return b->mac; + case 2: return b->pos; + } + throw 1; + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch(idx) { + case 0: b->name = val.toString(); break; + case 1: b->mac = val.toString(); break; + case 2: b->pos = val.toPoint3(); break; + } + } MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} diff --git a/mapview/model/MMFloorBeacons.h b/mapview/model/MMFloorBeacons.h index a9326b9..6c45b4f 100644 --- a/mapview/model/MMFloorBeacons.h +++ b/mapview/model/MMFloorBeacons.h @@ -22,7 +22,7 @@ public: // add all Beacons for (Floorplan::Beacon* b : floor->beacons) { - new MMFloorBeacon(this, floor, b); + elements.push_back(new MMFloorBeacon(this, floor, b)); } } diff --git a/mapview/model/MMFloorObstacleLine.h b/mapview/model/MMFloorObstacleLine.h index 1701f7d..33e1145 100644 --- a/mapview/model/MMFloorObstacleLine.h +++ b/mapview/model/MMFloorObstacleLine.h @@ -8,6 +8,7 @@ #include "IHasObstacleType.h" #include "../elements/MV2DElementFloorObstacleLine.h" +#include "../3D/MV3DElementFloorObstacleWall.h" #include @@ -19,11 +20,12 @@ public: Floorplan::Floor* mf; Floorplan::FloorObstacleLine* fo; MV2DElementFloorObstacleLine mv2d; + MV3DElementFloorObstacleWall mv3d; public: MMFloorObstacleLine(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorObstacleLine* fo) : - MapModelElement(parent), mf(mf), fo(fo), mv2d(fo) { + MapModelElement(parent), mf(mf), fo(fo), mv2d(fo), mv3d(mf,fo) { } @@ -34,6 +36,7 @@ public: Floorplan::ObstacleType getObatcleType() const override {return fo->type;} MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} void deleteMe() const override { parent->removeElement(this); diff --git a/mapview/model/MMFloorOutline.h b/mapview/model/MMFloorOutline.h index 3ed2305..e74d1a4 100644 --- a/mapview/model/MMFloorOutline.h +++ b/mapview/model/MMFloorOutline.h @@ -3,6 +3,7 @@ #include "MapLayer.h" #include "MMFloorOutlinePolygon.h" +#include "../3D/MV3DElementFloorOutline.h" #include @@ -10,17 +11,20 @@ /** * layer containing all elements describing a floor's outline */ -class MMFloorOutline : public MapLayer { +class MMFloorOutline : public MapLayer, public MapModelElement { private: /** the underlying model */ Floorplan::Floor* floor; + MV3DElementFloorOutline mv3d; + public: /** ctor with the underlying model */ - MMFloorOutline(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_GROUND), floor(floor) { + MMFloorOutline(MapLayer* parent, Floorplan::Floor* floor) : + MapLayer(parent, MapLayerType::FLOOR_GROUND), MapModelElement(parent), floor(floor), mv3d(floor, &floor->outline) { // the outline for (Floorplan::FloorOutlinePolygon* poly : floor->outline) { @@ -29,6 +33,8 @@ public: } + MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} + /** get the corresponding floor from the underlying model */ Floorplan::Floor* getFloor() {return floor;} diff --git a/mapview/model/MMFloorOutlinePolygon.h b/mapview/model/MMFloorOutlinePolygon.h index cd56537..1d541d0 100644 --- a/mapview/model/MMFloorOutlinePolygon.h +++ b/mapview/model/MMFloorOutlinePolygon.h @@ -1,10 +1,11 @@ #ifndef MAPELEMENTFLOORGROUND_H #define MAPELEMENTFLOORGROUND_H -#include "IHasName.h" +#include "IHasParams.h" #include "MapModelElement.h" #include "../elements/MV2DElementFloorOutlinePolygon.h" +#include "../3D/MV3DElementFloorOutlinePolygon.h" #include @@ -12,18 +13,20 @@ /** * describes one polygon within a floor's outline */ -class MMFloorOutlinePolygon : public MapModelElement, public IHasName { +class MMFloorOutlinePolygon : public MapModelElement, public IHasParams { private: Floorplan::Floor* mf; Floorplan::FloorOutlinePolygon* fo; MV2DElementFloorOutlinePolygon mv2d; + MV3DElementFloorOutlinePolygon mv3d; public: /** ctor */ - MMFloorOutlinePolygon(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorOutlinePolygon* fo) : MapModelElement(parent), mf(mf), fo(fo), mv2d(*fo) { + MMFloorOutlinePolygon(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorOutlinePolygon* fo) : + MapModelElement(parent), mf(mf), fo(fo), mv2d(*fo), mv3d(mf, fo) { ; } @@ -32,10 +35,32 @@ public: Floorplan::OutlineMethod getMethod() const {return fo->method;} void setMethod(const Floorplan::OutlineMethod m) {this->fo->method = m;} - void setName(const std::string& name) override {fo->name = name;} - const std::string& getName() const override {return fo->name;} - MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + //MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} + + virtual int getNumParams() const override { + return 1; + } + + virtual Param getParamDesc(const int idx) const override { + switch(idx) { + case 0: return Param("anem", ParamType::STRING); + } + throw 1; + } + + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return fo->name; + } + throw 1; + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch(idx) { + case 0: fo->name = val.toString(); break; + } + } void deleteMe() const override { diff --git a/mapview/model/MMFloorPOI.h b/mapview/model/MMFloorPOI.h new file mode 100644 index 0000000..a018eef --- /dev/null +++ b/mapview/model/MMFloorPOI.h @@ -0,0 +1,65 @@ +#ifndef MMFLOORPOI_H +#define MMFLOORPOI_H + +#include "MapModelElement.h" +#include "IHasParams.h" + +#include "../elements/MV2DElementPOI.h" + +#include + +class MMFloorPOI : public MapModelElement, public IHasParams { + +private: + + Floorplan::Floor* floor; + Floorplan::POI* poi; + MV2DElementPOI mv2d; + +public: + + MMFloorPOI(MapLayer* parent, Floorplan::Floor* floor, Floorplan::POI* poi) : MapModelElement(parent), floor(floor), poi(poi), mv2d(poi) { + ; + } + + virtual int getNumParams() const override { + return 3; + } + + virtual Param getParamDesc(const int idx) const override { + switch(idx) { + case 0: return Param("name", ParamType::STRING); + case 1: return Param("type", ParamType::INT); + case 2: return Param("position", ParamType::POINT2); + } + throw 1; + } + + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return poi->name; + case 1: return (int) poi->type; + case 2: return poi->pos; + } + throw 1; + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch(idx) { + case 0: poi->name = val.toString(); break; + case 1: poi->type = (Floorplan::POIType) val.toInt(); break; + case 2: poi->pos = val.toPoint2(); break; + } + } + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + + void deleteMe() const override { + parent->removeElement(this); + floor->pois.erase(std::remove(floor->pois.begin(), floor->pois.end(), poi), floor->pois.end()); + } + +}; + + +#endif // MMFLOORPOI_H diff --git a/mapview/model/MMFloorPOIs.h b/mapview/model/MMFloorPOIs.h new file mode 100644 index 0000000..efaa8d7 --- /dev/null +++ b/mapview/model/MMFloorPOIs.h @@ -0,0 +1,46 @@ +#ifndef MMFLOORPOIS_H +#define MMFLOORPOIS_H + +#include "MapLayer.h" +#include "MMFloorPOI.h" + +#include + +/** + * layer that contains all of one floor's POIs + */ +class MMFloorPOIs : public MapLayer { + + Floorplan::Floor* floor; + +public: + + /** ctor with the floor */ + MMFloorPOIs(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_POIS), floor(floor) { + + // the POIs + for (Floorplan::POI* poi : floor->pois) { + elements.push_back(new MMFloorPOI(this, floor, poi)); + } + + } + + /** get the corresponding floor from the underlying model */ + Floorplan::Floor* getFloor() {return floor;} + + //TODO: check + void createPOI(Floorplan::POI* poi) { + + // add to underlying model + floor->pois.push_back(poi); + + // add to myself as element + elements.push_back(new MMFloorPOI(this, floor, poi)); + + } + + std::string getLayerName() const override {return "POIs";} + +}; + +#endif // MMFLOORPOIS_H diff --git a/mapview/model/MMFloorUnderlayImage.h b/mapview/model/MMFloorUnderlayImage.h index 60c3165..85b3833 100644 --- a/mapview/model/MMFloorUnderlayImage.h +++ b/mapview/model/MMFloorUnderlayImage.h @@ -2,7 +2,6 @@ #define MMFLOORUNDERLAYIMAGE_H -#include "IHasFile.h" #include "IHasParams.h" #include "MapModelElement.h" @@ -12,7 +11,7 @@ /** * add an external image file as underlay (to copy it onto the map) */ -class MMFloorUnderlayImage : public MapModelElement, public MapLayer, public IHasFile, public IHasParams { +class MMFloorUnderlayImage : public MapModelElement, public IHasParams { private: @@ -24,19 +23,15 @@ private: public: /** ctor */ - MMFloorUnderlayImage(MapLayer* parent, Floorplan::Floor* floor, Floorplan::UnderlayImage* img) : MapModelElement(parent), MapLayer(parent, MapLayerType::FLOOR_UNDERLAY), floor(floor), img(img), mv2d(img) { - setFileName(img->filename); + MMFloorUnderlayImage(MapLayer* parent, Floorplan::Floor* floor, Floorplan::UnderlayImage* img) : MapModelElement(parent), floor(floor), img(img), mv2d(img) { + ; } - virtual void setFileName(const std::string& file) {img->filename = file;} - virtual const std::string& getFileName() const {return img->filename;} - void setAnchor(const Point2 anchor) {img->anchor = anchor;} Point2 getAnchor() const {return img->anchor;} void setScale(const float x, const float y) {img->scaleX = x; img->scaleY = y;} - virtual std::string getLayerName() const override {return "underlay";} MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} @@ -44,13 +39,14 @@ public: ; } - int getNumParams() const override {return 3;} + int getNumParams() const override {return 4;} virtual Param getParamDesc(const int idx) const override { switch (idx) { case 0: return Param("file", ParamType::FILE); case 1: return Param("scale X", ParamType::FLOAT); case 2: return Param("scale Y", ParamType::FLOAT); + case 3: return Param("Anchor", ParamType::POINT2); default: throw 1; } } @@ -60,15 +56,17 @@ public: case 0: return ParamValue(img->filename); case 1: return ParamValue(img->scaleX); case 2: return ParamValue(img->scaleY); + case 3: return ParamValue(img->anchor); default: throw 1; } } virtual void setParamValue(const int idx, const ParamValue& val) const override { switch (idx) { - case 0: img->filename = val.toString(); - case 1: img->scaleX = val.toFloat(); - case 2: img->scaleY = val.toFloat(); + case 0: img->filename = val.toString(); break; + case 1: img->scaleX = val.toFloat(); break; + case 2: img->scaleY = val.toFloat(); break; + case 3: img->anchor = val.toPoint2(); break; default: throw 1; } } diff --git a/mapview/model/MMFloorUnderlay.h b/mapview/model/MMFloorUnderlays.h similarity index 84% rename from mapview/model/MMFloorUnderlay.h rename to mapview/model/MMFloorUnderlays.h index cfbacea..3102869 100644 --- a/mapview/model/MMFloorUnderlay.h +++ b/mapview/model/MMFloorUnderlays.h @@ -10,7 +10,7 @@ /** * add an external file as underlay (to copy it onto the map) */ -class MMFloorUnderlay : public MapLayer { +class MMFloorUnderlays : public MapLayer { private: @@ -19,7 +19,7 @@ private: public: /** ctor */ - MMFloorUnderlay(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_UNDERLAY), floor(floor) { + MMFloorUnderlays(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_UNDERLAYS), floor(floor) { // the underlays for (Floorplan::UnderlayImage* img : floor->underlays) { diff --git a/mapview/model/MapLayer.h b/mapview/model/MapLayer.h index b54ef03..7d1a3b0 100644 --- a/mapview/model/MapLayer.h +++ b/mapview/model/MapLayer.h @@ -17,7 +17,8 @@ enum class MapLayerType { FLOOR_OBSTACLES, FLOOR_BEACONS, FLOOR_ACCESS_POINTS, - FLOOR_UNDERLAY, + FLOOR_UNDERLAYS, + FLOOR_POIS, }; diff --git a/mapview/model/MapModel.h b/mapview/model/MapModel.h index c24d419..4d2da38 100644 --- a/mapview/model/MapModel.h +++ b/mapview/model/MapModel.h @@ -68,6 +68,11 @@ public: } + /** get the constructed map */ + Floorplan::IndoorMap* getMap() const { + return im; + } + /** get the map's root-layer containing all other layers */ MapLayer* getRootLayer() { return root; } diff --git a/mapview/model/MapModelElement.h b/mapview/model/MapModelElement.h index ed3d49d..0e4488f 100644 --- a/mapview/model/MapModelElement.h +++ b/mapview/model/MapModelElement.h @@ -5,6 +5,7 @@ #include "MapLayer.h" class MV2DElement; +class MV3DElement; class MapModelElement { @@ -21,10 +22,13 @@ public: virtual ~MapModelElement() {;} /** get the 2D interaction class for this element */ - virtual MV2DElement* getMV2D() const = 0; + virtual MV2DElement* getMV2D() const {return nullptr;} + + /** get the 3D interaction class for this element */ + virtual MV3DElement* getMV3D() const {return nullptr;} /** delete this element from the model */ - virtual void deleteMe() const = 0; + virtual void deleteMe() const {;} /** get the parent element */ MapLayer* getParent() const {return parent;} diff --git a/mapview/tools/ToolMapGrid.h b/mapview/tools/ToolMapGrid.h index b3f9eea..db8c4cb 100644 --- a/mapview/tools/ToolMapGrid.h +++ b/mapview/tools/ToolMapGrid.h @@ -5,12 +5,23 @@ class ToolMapGrid : public Tool { +private: + + bool show = true; + public: + virtual void keyPressEvent(MapView2D* m, QKeyEvent* e) { + (void) m; + if (e->key() == Qt::Key_NumberSign) { show = !show; } + } + void paintBefore(MapView2D* m, Painter& p) override { (void) m; + if (!show) {return;} + static const QColor cB(250,250,250); static const QColor cN(235,235,235); static const QColor c0(128,128,128); diff --git a/mapview/tools/ToolRuler.h b/mapview/tools/ToolRuler.h index 122407c..8893529 100644 --- a/mapview/tools/ToolRuler.h +++ b/mapview/tools/ToolRuler.h @@ -86,11 +86,9 @@ public: p.p->drawLine(mouseX, 0, mouseX, rs); - p.setPenBrush(Qt::black, Qt::NoBrush); char buf[128]; - // y-axis p.p->setClipRect(ry); for (float y = r.y0; y <= r.y1; y += step) { @@ -131,6 +129,7 @@ public: p.p->drawText(xMajor+2, 18, buf); } + p.p->setClipping(false); } diff --git a/mapview/tools/ToolSelector.h b/mapview/tools/ToolSelector.h index 8ae6158..3b0fefb 100644 --- a/mapview/tools/ToolSelector.h +++ b/mapview/tools/ToolSelector.h @@ -39,6 +39,7 @@ public: // get all elements with bounding-box matchings std::vector possible; for (MapModelElement* el : m->getModel()->getSelectedLayerElements()) { + if (!el->getMV2D()) {continue;} BBox2 bbox = el->getMV2D()->getBoundingBox(); // elements 3D bbox bbox.grow(Point2(g, g)); // grow a little (needed for straight lines) if (bbox.contains(p2)) {possible.push_back(el);} // intersection? diff --git a/params/ElementParamWidget.cpp b/params/ElementParamWidget.cpp index 76625bf..ae9a01e 100644 --- a/params/ElementParamWidget.cpp +++ b/params/ElementParamWidget.cpp @@ -6,6 +6,7 @@ #include "../mapview/model/IHasMAC.h" #include "../mapview/model/IHasFile.h" +#include "../mapview/model/IHasParams.h" #include @@ -65,62 +66,13 @@ QComboBox* getOutlineMethods() { ElementParamWidget::ElementParamWidget(QWidget *parent) : QGroupBox(parent) { + this->lay = new QGridLayout(this); + setMinimumSize(100, 100); - setMaximumWidth(200); + setMaximumWidth(250); setTitle("MapElement Parameters"); - QGridLayout* lay = new QGridLayout(this); - int r = 0; - - lblDetails = new QLabel(); - lay->addWidget(new QLabel("selected"),r,0); - lay->addWidget(lblDetails,r,1); - ++r; - - name.txt = new QLineEdit(); - name.lbl = new QLabel("name"); - lay->addWidget(name.lbl,r,0); - lay->addWidget(name.txt,r,1); - connect(name.txt , SIGNAL(textChanged(QString)), this, SLOT(onNameChange())); - ++r; - - mac.lbl = new QLabel("MAC"); - mac.txt = new QLineEdit(); - lay->addWidget(mac.lbl, r, 0); - lay->addWidget(mac.txt, r, 1); - connect(mac.txt, SIGNAL(textChanged(QString)), this, SLOT(onMACChanged())); - ++r; - - - obstacleType.cmb = getObstacleTypes(); - obstacleType.lbl = new QLabel("type"); - lay->addWidget(obstacleType.lbl,r,0); - lay->addWidget(obstacleType.cmb,r,1); - connect(obstacleType.cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(onObstacleTypeChange())); - ++r; - - material.cmb = getMaterials(); - material.lbl = new QLabel("material"); - lay->addWidget(material.lbl,r,0); - lay->addWidget(material.cmb,r,1); - connect(material.cmb , SIGNAL(currentIndexChanged(int)), this, SLOT(onMaterialChange())); - ++r; - - fileName.txt = new QLabel(); - fileName.lbl = new QLabel("file"); - fileName.btn = new QPushButton("op"); - lay->addWidget(fileName.lbl,r,0); - lay->addWidget(fileName.txt,r,1); - lay->addWidget(fileName.btn,r,2); - connect(fileName.btn , SIGNAL(clicked(bool)), this, SLOT(onSelectFileName())); - ++r; - - outlineMethod.cmb = getOutlineMethods(); - outlineMethod.lbl = new QLabel("outline"); - lay->addWidget(outlineMethod.lbl,r,0); - lay->addWidget(outlineMethod.cmb,r,1); - connect(outlineMethod.cmb , SIGNAL(currentIndexChanged(int)), this, SLOT(onOutlineMethodChange())); - ++r; + QGridLayout* lay = new QGridLayout(); // start empty setElement(nullptr); @@ -129,68 +81,176 @@ ElementParamWidget::ElementParamWidget(QWidget *parent) : QGroupBox(parent) { void ElementParamWidget::setElement(MapModelElement* el) { + while ( QWidget* w = this->findChild() ) {delete w;} + delete this->layout(); + + this->lay = new QGridLayout(); + this->setLayout(lay); + int r = 0; + this->curElement = el; - if (el == nullptr) { - lblDetails->setText("-"); - } else if (dynamic_cast(el)) { - MMFloorObstacleLine* obs = dynamic_cast(el); - lblDetails->setText("Obstacle"); - } else if (dynamic_cast(el)) { - MMFloorOutlinePolygon* poly = dynamic_cast(el); - std::string txt = "Polygon (" + std::to_string(poly->getPolygon()->poly.points.size()) + " Points)"; - lblDetails->setText(txt.c_str()); - } else { - lblDetails->setText("?"); - } +// if (el == nullptr) { +// lblDetails->setText("-"); +// } else if (dynamic_cast(el)) { +// MMFloorObstacleLine* obs = dynamic_cast(el); +// lblDetails->setText("Obstacle"); +// } else if (dynamic_cast(el)) { +// MMFloorOutlinePolygon* poly = dynamic_cast(el); +// std::string txt = "Polygon (" + std::to_string(poly->getPolygon()->poly.points.size()) + " Points)"; +// lblDetails->setText(txt.c_str()); +// } else { +// lblDetails->setText("?"); +// } // material? -> select in combo-box { IHasMaterial* elem = dynamic_cast(el); - material.cmb->setVisible(elem != nullptr); - material.lbl->setVisible(elem != nullptr); - if (elem) {material.cmb->setCurrentIndex((int)elem->getMaterial());} + if (elem) { + material.cmb = getMaterials(); + material.lbl = new QLabel("material"); + lay->addWidget(material.lbl,r,0); + lay->addWidget(material.cmb,r,1); + connect(material.cmb , SIGNAL(currentIndexChanged(int)), this, SLOT(onMaterialChange())); + material.cmb->setCurrentIndex((int)elem->getMaterial()); + ++r; + } } // obstacle-type? -> select in combo-box { IHasObstacleType* elem = dynamic_cast(el); - obstacleType.cmb->setVisible(elem != nullptr); - obstacleType.lbl->setVisible(elem != nullptr); - if (elem) {obstacleType.cmb->setCurrentIndex((int)elem->getObatcleType());} + if (elem) { + obstacleType.cmb = getObstacleTypes(); + obstacleType.lbl = new QLabel("type"); + lay->addWidget(obstacleType.lbl,r,0); + lay->addWidget(obstacleType.cmb,r,1); + connect(obstacleType.cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(onObstacleTypeChange())); + obstacleType.cmb->setCurrentIndex((int)elem->getObatcleType()); + ++r; + } } - // has name? - { - IHasName* elem = dynamic_cast(el); - name.txt->setVisible(elem != nullptr); - name.lbl->setVisible(elem != nullptr); - if (elem) {name.txt->setText(elem->getName().c_str());} - } - - // has MAC? - { - IHasMAC* elem = dynamic_cast(el); - mac.lbl->setVisible(elem); - mac.txt->setVisible(elem); - if (elem) {mac.txt->setText(elem->getMAC().c_str());} - } // has outline method? { MMFloorOutlinePolygon* elem = dynamic_cast(el); - outlineMethod.cmb->setVisible(elem); - outlineMethod.lbl->setVisible(elem); - if (elem) {outlineMethod.cmb->setCurrentIndex((int)elem->getMethod());} + if (elem) { + QComboBox* cmb = getOutlineMethods(); + QLabel* lbl = new QLabel("outline"); + lay->addWidget(lbl,r,0); + lay->addWidget(cmb,r,1); + cmb->setCurrentIndex((int)elem->getMethod()); + connect(cmb, static_cast(&QComboBox::currentIndexChanged), [elem, cmb] (int idx) { + elem->setMethod( (Floorplan::OutlineMethod) cmb->currentData().toInt() ); + }); + ++r; + } } - // has File { - IHasFile* elem = dynamic_cast(el); - fileName.lbl->setVisible(elem); - fileName.txt->setVisible(elem); - fileName.btn->setVisible(elem); - if (elem) {fileName.txt->setText(elem->getFileName().c_str());} + IHasParams* elem = dynamic_cast(el); + if (elem) { + + for(int i = 0; i < elem->getNumParams(); ++i) { + + const Param param = elem->getParamDesc(i); + const ParamValue value = elem->getParamValue(i); + + lay->addWidget(new QLabel(param.name.c_str()),r,0); + + switch(param.type) { + + case ParamType::FLOAT: { + const std::string str = std::to_string(value.toFloat()); + QLineEdit* le = new QLineEdit( str.c_str() ); + connect(le, &QLineEdit::textChanged, [i,elem] (const QString& str) { + const float val = std::stof(str.toStdString()); + elem->setParamValue(i, ParamValue(val)); + }); + lay->addWidget(le,r,1); + break; + } + + case ParamType::INT: { + const std::string str = std::to_string(value.toInt()); + QLineEdit* le = new QLineEdit( str.c_str() ); + connect(le, &QLineEdit::textChanged, [i,elem] (const QString& str) { + const int val = std::stoi(str.toStdString()); + elem->setParamValue(i, ParamValue(val)); + }); + lay->addWidget(le,r,1); + break; + } + + case ParamType::STRING: { + const std::string str = value.toString(); + QLineEdit* le = new QLineEdit( str.c_str() ); + connect(le, &QLineEdit::textChanged, [i,elem] (const QString& str) { + elem->setParamValue(i, ParamValue(str.toStdString())); + }); + lay->addWidget(le,r,1); + break; + } + + case ParamType::FILE: { + const std::string str = value.toString(); + QLabel* lblFile = new QLabel(str.c_str()); + QPushButton* btn = new QPushButton("<"); + btn->setMaximumSize(32,32); + connect(btn, &QPushButton::clicked, [i,elem,lblFile] (const bool checked) { + QString res = QFileDialog::getOpenFileName(); + elem->setParamValue(i, ParamValue(res.toStdString())); + lblFile->setText(res); + }); + lay->addWidget(lblFile,r,1); + lay->addWidget(btn,r,2); + break; + } + + case ParamType::POINT2: { + const Point2 p2 = value.toPoint2(); + QWidget* subWidget = new QWidget(); + QGridLayout* laySub = new QGridLayout(subWidget); laySub->setMargin(0); + QLineEdit* txtX = new QLineEdit(QString::number(p2.x)); + QLineEdit* txtY = new QLineEdit(QString::number(p2.y)); + laySub->addWidget(txtX,0,0); + laySub->addWidget(txtY,0,1); + lay->addWidget(subWidget,r,1); + auto onChange = [i,elem,txtX,txtY] (const QString& str) { + elem->setParamValue(i, ParamValue( Point2(txtX->text().toFloat(), txtY->text().toFloat()) )); + }; + connect(txtX, &QLineEdit::textChanged, onChange); + connect(txtY, &QLineEdit::textChanged, onChange); + break; + } + + case ParamType::POINT3: { + const Point3 p3 = value.toPoint3(); + QWidget* subWidget = new QWidget(); + QGridLayout* laySub = new QGridLayout(subWidget); laySub->setMargin(0); + QLineEdit* txtX = new QLineEdit(QString::number(p3.x)); + QLineEdit* txtY = new QLineEdit(QString::number(p3.y)); + QLineEdit* txtZ = new QLineEdit(QString::number(p3.z)); + laySub->addWidget(txtX,0,0); + laySub->addWidget(txtY,0,1); + laySub->addWidget(txtZ,0,2); + lay->addWidget(subWidget,r,1); + auto onChange = [i,elem,txtX,txtY,txtZ] (const QString& str) { + elem->setParamValue(i, ParamValue( Point3(txtX->text().toFloat(), txtY->text().toFloat(), txtZ->text().toFloat()) )); + }; + connect(txtX, &QLineEdit::textChanged, onChange); + connect(txtY, &QLineEdit::textChanged, onChange); + connect(txtZ, &QLineEdit::textChanged, onChange); + break; + } + + } + + ++r; + + } + } } } @@ -205,22 +265,22 @@ void ElementParamWidget::onObstacleTypeChange() { if (el) {el->setObstacleType((Floorplan::ObstacleType) obstacleType.cmb->currentData().toInt() );} } -void ElementParamWidget::onNameChange() { - IHasName* el = dynamic_cast(this->curElement); - if (el) {el->setName(name.txt->text().toStdString());} -} +//void ElementParamWidget::onNameChange() { +// IHasName* el = dynamic_cast(this->curElement); +// if (el) {el->setName(name.txt->text().toStdString());} +//} -void ElementParamWidget::onOutlineMethodChange() { - MMFloorOutlinePolygon* el = dynamic_cast(this->curElement); - if (el) {el->setMethod( (Floorplan::OutlineMethod) outlineMethod.cmb->currentData().toInt() );} -} +//void ElementParamWidget::onOutlineMethodChange() { +// MMFloorOutlinePolygon* el = dynamic_cast(this->curElement); +// if (el) {el->setMethod( (Floorplan::OutlineMethod) outlineMethod.cmb->currentData().toInt() );} +//} -void ElementParamWidget::onMACChanged() { - dynamic_cast(curElement)->setMAC(mac.txt->text().toStdString()); -} +//void ElementParamWidget::onMACChanged() { +// dynamic_cast(curElement)->setMAC(mac.txt->text().toStdString()); +//} -void ElementParamWidget::onSelectFileName() { - QString res = QFileDialog::getOpenFileName(this); - dynamic_cast(curElement)->setFileName(res.toStdString()); - fileName.lbl->setText(res); -} +//void ElementParamWidget::onSelectFileName() { +// QString res = QFileDialog::getOpenFileName(this); +// dynamic_cast(curElement)->setFileName(res.toStdString()); +// fileName.txt->setText(res); +//} diff --git a/params/ElementParamWidget.h b/params/ElementParamWidget.h index 81cbc95..43f0065 100644 --- a/params/ElementParamWidget.h +++ b/params/ElementParamWidget.h @@ -8,6 +8,7 @@ class QLabel; class QComboBox; class QLineEdit; class QPushButton; +class QGridLayout; class ElementParamWidget : public QGroupBox { @@ -21,8 +22,11 @@ public: private: + QGridLayout* lay; + QLabel* lblDetails; MapModelElement* curElement = nullptr; + int r = 0; struct { QComboBox* cmb; @@ -34,27 +38,6 @@ private: QLabel* lbl; } obstacleType; - struct { - QLineEdit* txt; - QLabel* lbl; - } name; - - struct { - QLineEdit* txt; - QLabel* lbl; - } mac; - - struct { - QLabel* txt; - QPushButton* btn; - QLabel* lbl; - } fileName; - - struct { - QComboBox* cmb; - QLabel* lbl; - } outlineMethod; - signals: public slots: @@ -66,10 +49,6 @@ private slots: void onMaterialChange(); void onObstacleTypeChange(); - void onNameChange(); - void onMACChanged(); - void onOutlineMethodChange(); - void onSelectFileName(); }; diff --git a/params/LayerParamWidget.cpp b/params/LayerParamWidget.cpp index 12912e1..3dc75bc 100644 --- a/params/LayerParamWidget.cpp +++ b/params/LayerParamWidget.cpp @@ -4,12 +4,13 @@ #include #include +#include "mapview/model/IHasName.h" #include "mapview/model/MMFloor.h" LayerParamWidget::LayerParamWidget(QWidget *parent) : QGroupBox(parent) { setMinimumSize(100, 100); - setMaximumWidth(200); + setMaximumWidth(250); setTitle("MapLayer Parameters"); QGridLayout* lay = new QGridLayout(this); diff --git a/params/ToolBox.cpp b/params/ToolBox.cpp index bc090ff..64d233d 100644 --- a/params/ToolBox.cpp +++ b/params/ToolBox.cpp @@ -22,7 +22,7 @@ ToolBoxWidget::ToolBoxWidget(MapView2D* view, QWidget *parent) : QWidget(parent) setMinimumWidth(48); QGridLayout* lay = new QGridLayout(this); - int r = 0; + // OBSTACLES btnGround = new QPushButton(UIHelper::getIcon("floor"), ""); @@ -57,6 +57,11 @@ ToolBoxWidget::ToolBoxWidget(MapView2D* view, QWidget *parent) : QWidget(parent) lay->addWidget(btnImage, r++, 0, 1,1,Qt::AlignTop); connect(btnImage, SIGNAL(clicked(bool)), this, SLOT(onNewImage())); + // POI + btnPOI = new QPushButton(UIHelper::getIcon("poi"), ""); + btnPOI->setMinimumSize(s,s); + lay->addWidget(btnPOI, r++, 0, 1,1,Qt::AlignTop); + connect(btnPOI, SIGNAL(clicked(bool)), this, SLOT(onNewPOI())); // FILL lay->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), r, 0); @@ -77,8 +82,9 @@ void ToolBoxWidget::setSelectedLayer(MapLayer *ml) { btnWifi->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_ACCESS_POINTS)); btnBeacon->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_BEACONS)); + btnPOI->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_POIS)); - btnImage->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_UNDERLAY)); + btnImage->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_UNDERLAYS)); } @@ -164,11 +170,23 @@ void ToolBoxWidget::onNewBeacon() { } +void ToolBoxWidget::onNewPOI() { + + const Point2 center = view->getScaler().getCenter(); + Floorplan::POI* poi = new Floorplan::POI( + Floorplan::POIType::ROOM, "noname", Point2(center.x, center.y) + ); + + MMFloorPOIs* pois = (MMFloorPOIs*) curLayer; + pois->createPOI(poi); + +} + void ToolBoxWidget::onNewImage() { const Point2 center = view->getScaler().getCenter(); - MMFloorUnderlay* underlay = (MMFloorUnderlay*) curLayer; - underlay->createImage(center); + MMFloorUnderlays* underlays = (MMFloorUnderlays*) curLayer; + underlays->createImage(center); } diff --git a/params/ToolBoxWidget.h b/params/ToolBoxWidget.h index 98e5048..0402c66 100644 --- a/params/ToolBoxWidget.h +++ b/params/ToolBoxWidget.h @@ -26,6 +26,7 @@ private: MapView2D* view; MapLayer* curLayer; + int r = 0; QPushButton* btnGround; QPushButton* btnWall; @@ -33,6 +34,7 @@ private: QPushButton* btnWifi; QPushButton* btnBeacon; + QPushButton* btnPOI; QPushButton* btnImage; @@ -44,6 +46,7 @@ private slots: void onNewAccessPoint(); void onNewBeacon(); + void onNewPOI(); void onNewImage(); diff --git a/res.qrc b/res.qrc index c205e50..4106595 100644 --- a/res.qrc +++ b/res.qrc @@ -9,5 +9,6 @@ res/icons/beacon.svg res/icons/image.svg res/icons/cross.png + res/icons/poi.svg diff --git a/res/icons/poi.svg b/res/icons/poi.svg new file mode 100644 index 0000000..3d388e6 --- /dev/null +++ b/res/icons/poi.svg @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + +