diff --git a/IndoorMap.pro b/IndoorMap.pro index 35e13f4..7c6ec85 100644 --- a/IndoorMap.pro +++ b/IndoorMap.pro @@ -6,16 +6,21 @@ QT += core gui opengl -CONFIG += c++11 +CONFIG += c++11 -g3 -O0 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets svg TARGET = IndoorMap TEMPLATE = app +DEFINES += WITH_ASSERTIONS +DEFINES += WITH_DEBUG_LOG + INCLUDEPATH += \ ../ + + SOURCES += \ lib/gpc/gpc.cpp \ main.cpp \ @@ -30,7 +35,8 @@ SOURCES += \ params/ToolBox.cpp \ mapview/model/MapModel.cpp \ tree/MapTreeModel.cpp \ - mapview/3D/MapView3D.cpp + mapview/3D/MapView3D.cpp \ + params/StairBuilder.cpp HEADERS += MainWindow.h \ @@ -101,7 +107,21 @@ HEADERS += MainWindow.h \ mapview/grid/MapModelGrid.h \ mapview/3DGrid/GridModel.h \ mapview/3DGrid/GridRenderer.h \ - mapview/3DGrid/MyNode.h + mapview/3DGrid/MyNode.h \ + mapview/3D/MV3DElementStair.h \ + mapview/model/MMFloorStair.h \ + mapview/model/MMFloorStairs.h \ + mapview/elements/MV2DElementStair.h \ + params/StairBuilder.h \ + misc/GnuplotExport.h \ + exp.h \ + ray.h \ + mapview/model/MMFloorObstacleDoor.h \ + mapview/elements/MV2DElementFloorObstacleDoor.h \ + mapview/model/IHasDoorType.h \ + mapview/3D/MV3DElementFloorObstacleDoor.h \ + mapview/3D/misc/Plane.h \ + mapview/elements/HasMoveableNodes.h FORMS += MainWindow.ui diff --git a/MainController.cpp b/MainController.cpp index d1198eb..b026fe0 100644 --- a/MainController.cpp +++ b/MainController.cpp @@ -21,6 +21,10 @@ #include #include +#include "mapview/3DGrid/GridRenderer.h" + + + MainController::MainController() { mw = new MainWindow(); @@ -53,6 +57,7 @@ MainController::MainController() { connect(layerTree, SIGNAL(clicked(QModelIndex)), this, SLOT(layerSelected(QModelIndex))); connect(mapSelector, SIGNAL(onMapElementSelected(MapModelElement*)), this, SLOT(mapElementSelected(MapModelElement*))); + connect(mw->getMapView2D(), SIGNAL(onElementChange(MV2DElement*)), this, SLOT(curMapElementChanged())); // model events connect(mapModel, SIGNAL(aboutToReset()), this, SLOT(onMapModelAboutToReset())); @@ -63,11 +68,17 @@ MainController::MainController() { 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())); + connect(mw, SIGNAL(onShow3DFloorplan()), this, SLOT(onShow3DFloorplan())); + connect(mw, SIGNAL(onShow3DGrid()), this, SLOT(onShow3DGrid())); + + // 3D grid view config + connect(mw, &MainWindow::onGridNodeColorImp, [&] () {mw->getMapView3D()->getGridRenderer()->setNodeColorMode(GridRendererColorMode::SHOW_NODE_IMPORTANCE);} ); + connect(mw, &MainWindow::onGridNodeColorType, [&] () {mw->getMapView3D()->getGridRenderer()->setNodeColorMode(GridRendererColorMode::SHOW_NODE_TYPE);} ); + connect(mw, &MainWindow::onGridShowEdges, [&] (const bool show) {mw->getMapView3D()->getGridRenderer()->setShowEdges(show);} ); - mapModel->load("../IndoorMap/maps/SHL9.xml"); + mapModel->load("../IndoorMap/maps/SHL17.xml"); + //mapModel->load("../IndoorMap/maps/test.xml"); } @@ -84,9 +95,13 @@ void MainController::layerSelected(QModelIndex idx) { MapLayer* ml = static_cast(idx.internalPointer()); mapModel->setSelectedLayer(ml); mw->getMapView2D()->layerChange(); + mw->getMapView3D()->layerChange(); mw->getLayerParamWidget()->setElement(ml); mw->getToolBoxWidget()->setSelectedLayer(ml); +} +void MainController::curMapElementChanged() { + mw->getElementParamWidget()->refresh(); } void MainController::mapElementSelected(MapModelElement* el) { diff --git a/MainController.h b/MainController.h index 2d4aaf1..6d39d0c 100644 --- a/MainController.h +++ b/MainController.h @@ -27,6 +27,10 @@ public slots: /** MapElement selection has changed */ void mapElementSelected(MapModelElement* el); + /** the currently selected MapElement has changed */ + void curMapElementChanged(); + + void onLoad(); void onSave(); diff --git a/MainWindow.cpp b/MainWindow.cpp index abd0bc6..6c5d3d3 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -23,12 +23,18 @@ #include "tree/MapTreeModel.h" -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) { +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); + QString css; + css += "QPushButton { border: 1px solid #000000; background-color: #ffffff; }"; + css += "QPushButton:!enabled { border: 1px solid #cccccc; background-color:none; }"; + css += "QPushButton:hover { border: 1px solid #000000; background-color: #dddddd; }"; + css += "QPushButton:pressed { border: 1px solid #000000; background-color: #bbbbbb; }"; + + this->setStyleSheet(css); + mapView2D = new MapView2D(); mapView3D = new MapView3D(); @@ -47,22 +53,37 @@ MainWindow::MainWindow(QWidget *parent) : ui->layTree->addWidget(actionWidget); + // show mode + QActionGroup* grpMode = new QActionGroup(this); + grpMode->addAction(ui->actShow3DFloorplan); + grpMode->addAction(ui->actShow3DGrid); + + connect(ui->actShow2D, SIGNAL(triggered(bool)), this, SIGNAL(onShow2D())); + connect(ui->actShow3DGrid, SIGNAL(triggered(bool)), this, SIGNAL(onShow3DGrid())); + connect(ui->actShow3DFloorplan, SIGNAL(triggered(bool)), this, SIGNAL(onShow3DFloorplan())); + + + // node coloring + QActionGroup* grpNodeCol = new QActionGroup(this); + grpNodeCol->addAction(ui->actGridNodeColorImportance); + grpNodeCol->addAction(ui->actGridNodeColorType); + + connect(ui->actGridNodeColorImportance, SIGNAL(triggered(bool)), this, SIGNAL(onGridNodeColorImp())); + connect(ui->actGridNodeColorType, SIGNAL(triggered(bool)), this, SIGNAL(onGridNodeColorType())); + + // edges + connect(ui->actGridShowEdges, &QAction::triggered, this, &MainWindow::onGridShowEdges); + } + + 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 e61b06b..a178834 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -30,11 +30,18 @@ public: ToolBoxWidget* getToolBoxWidget() {return toolBoxWidget;} ActionWidget* getActionWidget() {return actionWidget;} - QAction* getShow3DFloorplan(); - QAction* getShow3DGrid(); QTreeView* getTree(); +signals: + + void onShow2D(); + void onShow3DFloorplan(); + void onShow3DGrid(); + void onGridShowEdges(bool); + void onGridNodeColorImp(); + void onGridNodeColorType(); + private: Ui::MainWindow *ui; MapView2D* mapView2D; diff --git a/MainWindow.ui b/MainWindow.ui index 5ff07fc..24d078d 100644 --- a/MainWindow.ui +++ b/MainWindow.ui @@ -58,22 +58,70 @@ - + + + + Grid + + + + node color + + + + + + + + - + + + true + 2D + + true + Floorplan + + + true + + + show edges + + + + + true + + + node type + + + + + true + + + node importance + + + + true + Grid diff --git a/UIHelper.h b/UIHelper.h index 3bbcb7c..6bf0f0f 100644 --- a/UIHelper.h +++ b/UIHelper.h @@ -3,8 +3,12 @@ #include #include +#include + #include +#include + class UIHelper { public: @@ -15,16 +19,71 @@ public: // return QIcon(img); // } - static QIcon getIcon(const std::string& name) { - const int size = 32; - const QColor fill = Qt::transparent; - const std::string file = "://res/icons/" + name + ".svg"; - QSvgRenderer renderer(QString(file.c_str())); - QPixmap pm(size, size); - pm.fill(fill); - QPainter painter(&pm); - renderer.render(&painter, pm.rect()); - return QIcon(pm); + static const QPixmap& getPixmap(const std::string& name, const int size = 32) { + + // caching + static std::unordered_map cache; + + // try to get the image from the cache + const std::string cacheKey = std::to_string(size) + name; + auto it = cache.find(cacheKey); + + // not in cache? + if (it == cache.end()) { + + // build + const QColor fill = Qt::transparent; + const std::string file = "://res/icons/" + name + ".svg"; + QSvgRenderer renderer(QString(file.c_str())); + QPixmap pm(size, size); + pm.fill(fill); + QPainter painter(&pm); + renderer.render(&painter, pm.rect()); + + // add to cache + cache[cacheKey] = pm; + + } + + // done + return cache[cacheKey]; + + } + + static const QPixmap& getPixmapColored(const std::string& name, const QColor color, const int size = 32) { + + // caching + static std::unordered_map cache; + + // try to get the image from the cache + const QString hex = color.name(); + const std::string cacheKey = hex.toStdString() + "_" + std::to_string(size) + "_" + name; + auto it = cache.find(cacheKey); + + // not in cache? + if (it == cache.end()) { + + // copy + QPixmap colored = getPixmap(name, size); + QPainter painter(&colored); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + painter.fillRect(colored.rect(), color); + painter.end(); + + // add to cache + cache[cacheKey] = colored; + + } + + // done + return cache[cacheKey]; + + } + + static QIcon getIcon(const std::string& name, const int size = 32) { + + return QIcon(getPixmap(name, size)); + } }; diff --git a/main.cpp b/main.cpp index a2b849b..87d67c0 100644 --- a/main.cpp +++ b/main.cpp @@ -1,16 +1,19 @@ #include "MainController.h" #include - +#include "exp.h" +#include "ray.h" int main(int argc, char *argv[]) { - QApplication a(argc, argv); + //exp(); + //ray(); + QApplication a(argc, argv); MainController mc; mc.show(); - return a.exec(); + } diff --git a/mapview/3D/MV3DElementFloorObstacleDoor.h b/mapview/3D/MV3DElementFloorObstacleDoor.h new file mode 100644 index 0000000..00223d6 --- /dev/null +++ b/mapview/3D/MV3DElementFloorObstacleDoor.h @@ -0,0 +1,35 @@ +#ifndef MV3DELEMENTFLOOROBSTACLEDOOR_H +#define MV3DELEMENTFLOOROBSTACLEDOOR_H + +#include +#include + +#include "MV3DElement.h" +#include "misc/Plane.h" + + + +class MV3DElementFloorObstacleDoor : public MV3DElement { + + Floorplan::Floor* f; + Floorplan::FloorObstacleDoor* fo; + +public: + + /** ctor */ + MV3DElementFloorObstacleDoor(Floorplan::Floor* f, Floorplan::FloorObstacleDoor* fo) : f(f), fo(fo) { + ; + } + + /** repaint me */ + void paintGL() override { + + glColor3f(0,1,0); + Plane p(fo->from, fo->to, f->atHeight, fo->height); + p.paintGL(); + + } + +}; + +#endif // MV3DELEMENTFLOOROBSTACLEDOOR_H diff --git a/mapview/3D/MV3DElementFloorObstacleWall.h b/mapview/3D/MV3DElementFloorObstacleWall.h index 9b11405..06f2533 100644 --- a/mapview/3D/MV3DElementFloorObstacleWall.h +++ b/mapview/3D/MV3DElementFloorObstacleWall.h @@ -2,9 +2,12 @@ #define MV3DELEMENTFLOOROBSTACLEWALL_H #include +#include #include "MV3DElement.h" + + class MV3DElementFloorObstacleWall : public MV3DElement { Floorplan::Floor* f; @@ -19,39 +22,39 @@ public: 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; + struct Wall { + Point2 from; + Point2 to; + float atHeight; + float height; + Wall(const Point2 from, const Point2 to, float atHeight, float height) : + from(from), to(to), atHeight(atHeight), height(height) {;} + void paintGL() { - 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); + float y1 = atHeight; + float y2 = atHeight + height; - Point3 v1 = p2-p1; - Point3 v2 = p3-p1; - Point3 n = cross(v1, v2); - n/=n.length(); + // polygon edges + Point3 p1 = Point3(from.x, y1, from.y); + Point3 p2 = Point3(to.x, y1, to.y); + Point3 p3 = Point3(to.x, y2, to.y); + Point3 p4 = Point3(from.x, y2, from.y); - // align normals to virtual viewport - Point3 view(99,99,99); - if ((view-n).length() > (view+n).length()) {n = -n;} + // calculate normal +// Point3 v1 = p2-p1; +// Point3 v2 = p3-p1; +// Point3 n = cross(v1, v2); +// n/=n.length(); + Point3 n = Math::normal(p2-p1, p3-p1); - if (fo->type == Floorplan::ObstacleType::WALL) { + // align normals to virtual viewport + Point3 view(99,99,99); + if ((view-n).length() > (view+n).length()) {n = -n;} // fill the wall glColor3f(0.75, 0.75, 0.75); @@ -67,17 +70,85 @@ protected: } - 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); + struct Handrail { + + Point2 from; + Point2 to; + float atHeight; + float height; + + Handrail(const Point2 from, const Point2 to, float atHeight, float height) : + from(from), to(to), atHeight(atHeight), height(height) {;} + + void paintGL() { + + float y1 = atHeight; + float y2 = atHeight + height; + + // polygon edges + Point3 p1 = Point3(from.x, y1, from.y); + Point3 p2 = Point3(to.x, y1, to.y); + + Point3 p3 = Point3(from.x, y2, from.y); + Point3 p4 = Point3(to.x, y2, to.y); + + glDisable(GL_LIGHTING); + glBegin(GL_LINES); + + glColor3f(1,1,1); + + // top + glVertex3f(p3.x, p3.y, p3.z); + glVertex3f(p4.x, p4.y, p4.z); + + // start bar + glVertex3f(p1.x, p1.y, p1.z); + glVertex3f(p3.x, p3.y, p3.z); + + // end bar + glVertex3f(p2.x, p2.y, p2.z); + glVertex3f(p4.x, p4.y, p4.z); + + glColor3f(0.6, 0.6, 0.6); + + // intermediate bars + const Point3 d1 = p2-p1; + const Point3 d2 = p4-p3; + const int numBars = d2.length() / 1; + for (int i = 1; i < numBars; ++i) { + const Point3 s = p1 + d1 * i / numBars; + const Point3 e = p3 + d2 * i / numBars; + glVertex3f(s.x, s.y, s.z); + glVertex3f(e.x, e.y, e.z); + } + + glEnd(); + glEnable(GL_LIGHTING); + + } + + }; + + /** repaint me */ + void paintGL() override { + + + + + + if (fo->type == Floorplan::ObstacleType::WALL) { + + Wall wall(fo->from, fo->to, f->atHeight, f->height); + wall.paintGL(); + + } else if (fo->type == Floorplan::ObstacleType::HANDRAIL) { + + Handrail rail(fo->from, fo->to, f->atHeight, 1.0); + rail.paintGL(); + + } } diff --git a/mapview/3D/MV3DElementStair.h b/mapview/3D/MV3DElementStair.h new file mode 100644 index 0000000..d674a77 --- /dev/null +++ b/mapview/3D/MV3DElementStair.h @@ -0,0 +1,59 @@ +#ifndef MV3DELEMENTSTAIR_H +#define MV3DELEMENTSTAIR_H + +#include + +#include "misc/Cube.h" +#include "MV3DElement.h" + +class MV3DElementStair : public MV3DElement { + + Floorplan::Floor* floor; + Floorplan::Stair* stair; + +public: + + /** ctor */ + MV3DElementStair(Floorplan::Floor* floor, Floorplan::Stair* stair) : floor(floor), stair(stair) { + ; + } + +protected: + + + /** repaint me */ + void paintGL() override { + + glDisable(GL_CULL_FACE); + + glColor3f(1.0, 0.55, 0.55); + glBegin(GL_QUADS); + + const std::vector parts = stair->getParts(); + const std::vector quads = Floorplan::getQuads(parts, floor); + + for (int i = 0; i < (int) parts.size(); ++i) { + + //const Floorplan::StairPart& part = parts[i]; + const Floorplan::Quad3& quad = quads[i]; + + //const Floorplan::Quad3 quad = part.getQuad(floor); + const Point3 p1 = quad.p2-quad.p1; + const Point3 p2 = quad.p4-quad.p1; + const Point3 n = Math::normal(p1,p2); + glNormal3f(n.x, n.z, n.z); + glVertex3f(quad.p1.x, quad.p1.z, quad.p1.y); + glVertex3f(quad.p2.x, quad.p2.z, quad.p2.y); + glVertex3f(quad.p3.x, quad.p3.z, quad.p3.y); + glVertex3f(quad.p4.x, quad.p4.z, quad.p4.y); + + } + glEnd(); + + glEnable(GL_CULL_FACE); + + } + +}; + +#endif // MV3DELEMENTSTAIR_H diff --git a/mapview/3D/MapView3D.cpp b/mapview/3D/MapView3D.cpp index e37bcd3..e3fc841 100644 --- a/mapview/3D/MapView3D.cpp +++ b/mapview/3D/MapView3D.cpp @@ -21,6 +21,8 @@ MapView3D::MapView3D(QWidget *parent) : QGLWidget(parent) { scale.y = 0.05f; scale.z = 0.05f; + gridRenderer = new GridRenderer(); + } @@ -40,10 +42,10 @@ void MapView3D::initializeGL() { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); - glEnable(GL_LIGHT1); + //glEnable(GL_LIGHT1); - GLfloat light0_position [] = {+5, 5, +5, 1}; - GLfloat light1_position [] = {-5, 5, -5, 1}; + GLfloat light0_position [] = {+50, 50, +50, 1}; + GLfloat light1_position [] = {-50, 50, -50, 1}; glLightfv ( GL_LIGHT0, GL_POSITION, light0_position ); glLightfv ( GL_LIGHT1, GL_POSITION, light1_position ); @@ -75,8 +77,6 @@ void MapView3D::paintGL() { glLoadIdentity(); - - // 3) scale glScalef(scale.x, scale.y, scale.z); @@ -180,8 +180,7 @@ void MapView3D::draw() { if (gridModel) { // show grid - GridRenderer renderer(gridModel->getGrid()); - renderer.paintGL(); + gridRenderer->paintGL(gridModel->getGrid()); } else { @@ -189,6 +188,7 @@ void MapView3D::draw() { for (MapModelElement* el : getModel()->getVisibleElements()) { if (el->getMV3D()) {el->getMV3D()->paintGL();} } + } } diff --git a/mapview/3D/MapView3D.h b/mapview/3D/MapView3D.h index 10127b6..7b46ed6 100644 --- a/mapview/3D/MapView3D.h +++ b/mapview/3D/MapView3D.h @@ -8,6 +8,7 @@ class MapModel; class GridModel; +class GridRenderer; class MapView3D : public QGLWidget { @@ -23,20 +24,31 @@ public: update(); } + void layerChange() { + update(); + } + /** get the underlying data-model */ MapModel* getModel() {return model;} + /** get the renderer to use for the grid */ + GridRenderer* getGridRenderer() {return gridRenderer;} + + /** 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; + GridRenderer* gridRenderer = nullptr; Point3 rot; Point3 center; diff --git a/mapview/3D/misc/Plane.h b/mapview/3D/misc/Plane.h new file mode 100644 index 0000000..d1de2e4 --- /dev/null +++ b/mapview/3D/misc/Plane.h @@ -0,0 +1,52 @@ +#ifndef PLANE_H +#define PLANE_H + +#include +#include + +class Plane { + +private: + + Point3 p1; + Point3 p2; + Point3 p3; + Point3 p4; + Point3 n; + + +public: + + /** construct from 2D line and heights */ + Plane(const Point2 from, const Point2 to, const float atHeight, const float height) { + + p1 = Point3(from.x, from.y, atHeight); + p2 = Point3(to.x, to.y, atHeight); + p3 = Point3(to.x, to.y, atHeight+height); + p4 = Point3(from.x, from.y, atHeight+height); + + const Point2 perp = (to-from).perpendicular(); + n = Point3(perp.x, perp.y, 0); + + } + + void paintGL() { + + glDisable(GL_CULL_FACE); + glBegin(GL_QUADS); + + // bottom + glNormal3f(n.x, n.z, n.y); + glVertex3f(p1.x, p1.z, p1.y); + glVertex3f(p2.x, p2.z, p2.y); + glVertex3f(p3.x, p3.z, p3.y); + glVertex3f(p4.x, p4.z, p4.y); + + glEnd(); + glEnable(GL_CULL_FACE); + + } + +}; + +#endif // PLANE_H diff --git a/mapview/3DGrid/GridModel.h b/mapview/3DGrid/GridModel.h index 42bb5a5..d149b32 100644 --- a/mapview/3DGrid/GridModel.h +++ b/mapview/3DGrid/GridModel.h @@ -4,7 +4,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "MyNode.h" @@ -15,7 +21,7 @@ class GridModel { private: - int gridSize_cm = 40; + int gridSize_cm = 31; Grid grid; Floorplan::IndoorMap* im; @@ -27,10 +33,60 @@ public: Grid* getGrid() {return &grid;} + + class Listener : public GridFactoryListener { + + private: + QDialog dlg; + QLabel* lbl1; + QLabel* lbl2; + QProgressBar* bar1; + QProgressBar* bar2; + + public: + Listener() { + QVBoxLayout* lay = new QVBoxLayout(&dlg); + lbl1 = new QLabel(); lay->addWidget(lbl1); + bar1 = new QProgressBar(); lay->addWidget(bar1); + lbl2 = new QLabel(); lay->addWidget(lbl2); + bar2 = new QProgressBar(); lay->addWidget(bar2); + dlg.resize(350, 120); + dlg.show(); + } + ~Listener() { + dlg.close(); + } + + void onGridBuildUpdateMajor(const std::string& what) override { + lbl1->setText(what.c_str()); + QApplication::processEvents(); + } + void onGridBuildUpdateMajor(const int cnt, const int cur) override { + bar1->setValue(cur*100/cnt); + QApplication::processEvents(); + } + + void onGridBuildUpdateMinor(const std::string& what) override { + lbl2->setText(what.c_str()); + QApplication::processEvents(); + } + void onGridBuildUpdateMinor(const int cnt, const int cur) override { + bar2->setValue(cur*100/cnt); + QApplication::processEvents(); + } + + }; + void rebuild(Floorplan::IndoorMap* im) { + + Listener l; + GridFactory fac(grid); - fac.build(im); - int i = 0;(void) i; + fac.build(im, true, &l); + + Importance::addImportance(grid, 0); + //Importance::addImportance(grid, 400); + } }; diff --git a/mapview/3DGrid/GridRenderer.h b/mapview/3DGrid/GridRenderer.h index 4828bf4..e71ccba 100644 --- a/mapview/3DGrid/GridRenderer.h +++ b/mapview/3DGrid/GridRenderer.h @@ -1,40 +1,125 @@ #ifndef GRIDRENDERER_H #define GRIDRENDERER_H -#include "../3D/MV3DElement.h" #include "MyNode.h" +#include #include +enum class GridRendererColorMode { + SHOW_NODE_TYPE, + SHOW_NODE_IMPORTANCE, +}; -class GridRenderer : public MV3DElement { +class GridRenderer { private: - Grid* grid; + // settings + GridRendererColorMode colorMode = GridRendererColorMode::SHOW_NODE_IMPORTANCE; + bool showEdges = false; + + struct Color { + float r,g,b; + Color(float r, float g, float b) : r(r), g(g), b(b) {;} + }; + + Color colors[4] = { + Color(0.3, 0.3, 0.3), Color(0,0,1), Color(0,0,1), Color(0,0.65,0) + }; public: - GridRenderer(Grid* grid) : grid(grid) { + /** ctor */ + GridRenderer() { ; } - virtual void paintGL() override { + + void setNodeColorMode(const GridRendererColorMode mode) {this->colorMode = mode;} + void setShowEdges(const bool show) {this->showEdges = show;} + + + /** render the given grid using GL commands */ + void paintGL(Grid* grid) { glDisable(GL_LIGHTING); - glColor3f(0,0,0); - glPointSize(0.1f); + const float s = 2; + + if (showEdges) { + std::unordered_set used; + glBegin(GL_LINES); + for (MyNode& n1 : *grid) { + glColor3f(0.5, 0.5, 0.5); + for (MyNode& n2 : grid->neighbors(n1)) { + uint64_t min = std::min(n1.getIdx(), n2.getIdx()); + uint64_t max = std::max(n1.getIdx(), n2.getIdx()); + uint64_t idx = max << 32 | min; + if (used.find(idx) == used.end()) { + glVertex3f(n1.x_cm/100.0f, n1.z_cm/100.0f*s, n1.y_cm/100.0f); + glVertex3f(n2.x_cm/100.0f, n2.z_cm/100.0f*s, n2.y_cm/100.0f); + used.insert(idx); + } + } + } + glEnd(); + } + + + glPointSize(3.0f); glBegin(GL_POINTS); for (MyNode& n : *grid) { - glVertex3f(n.x_cm/100.0f, n.z_cm/100.0f*5, n.y_cm/100.0f); + + // get the color to use + switch(colorMode) { + case GridRendererColorMode::SHOW_NODE_TYPE: { + const Color c = colors[n.getType()]; + glColor3f(c.r, c.g, c.b); + break; + } + + case GridRendererColorMode::SHOW_NODE_IMPORTANCE: { + const float xx = n.imp - 0.6; + glColor3f(xx, xx, xx); + break; + } + + } + + glVertex3f(n.x_cm/100.0f, n.z_cm/100.0f*s, n.y_cm/100.0f); + } glEnd(); +// std::vector vec = lint(); +// glPointSize(4.0f); +// glBegin(GL_POINTS); +// glColor3f(1,0,0); +// for (MyNode& n : vec) { +// glVertex3f(n.x_cm/100.0f, n.z_cm/100.0f*s, n.y_cm/100.0f); +// } +// glEnd(); + + //glEnable(GL_DEPTH_TEST); + + glEnable(GL_LIGHTING); } +// std::vector lint() { +// std::vector vec; +// lintStair(vec); +// return vec; +// } + +// void lintStair(std::vector& vec) { +// for (MyNode& n1 : *grid) { +// if (n1.getNumNeighbors() <= 5) { vec.push_back(n1);} +// } +// } + }; #endif // GRIDRENDERER_H diff --git a/mapview/3DGrid/MyNode.h b/mapview/3DGrid/MyNode.h index 193b05d..192d22a 100644 --- a/mapview/3DGrid/MyNode.h +++ b/mapview/3DGrid/MyNode.h @@ -4,7 +4,11 @@ #include struct MyNode : public GridNode, public GridPoint { + + float imp; + MyNode(float x, float y, float z) : GridPoint(x,y,z) {;} + }; #endif // MYNODE_H diff --git a/mapview/MapView2D.h b/mapview/MapView2D.h index b7e7d83..92fb050 100644 --- a/mapview/MapView2D.h +++ b/mapview/MapView2D.h @@ -6,6 +6,8 @@ #include "Scaler.h" class MapModel; +class MV2DElement; + #include "mapview/tools/Tools.h" @@ -60,6 +62,11 @@ public: Scaler& getScaler() {return s;} +signals: + + void onElementChange(MV2DElement*e); + + protected: diff --git a/mapview/Painter.h b/mapview/Painter.h index 6801ba9..10ec592 100644 --- a/mapview/Painter.h +++ b/mapview/Painter.h @@ -2,6 +2,7 @@ #define PAINTER_H #include +#include #include #include @@ -36,6 +37,17 @@ public: p->drawLine(s.xms(p1.x), s.yms(p1.y), s.xms(p2.x), s.yms(p2.y)); } + float radToDeg(const float rad) const { + return rad * 180 / M_PI; + } + + void drawArc(const Point2 center, const float radius, const float startAngleRad, const float spanAngleRad) { + const float wh = s.ms(radius) * 2; + const float x1 = s.xms(center.x) - wh/2; + const float y1 = s.yms(center.y) - wh/2; + p->drawArc(x1, y1, wh, wh, radToDeg(startAngleRad)*16, radToDeg(spanAngleRad)*16); + } + void drawCircle(const Point3 center) { int r = 5; p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r); @@ -46,6 +58,11 @@ public: p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r); } + void drawDot(const Point2 center) { + int r = 1; + p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r); + } + void drawCircle(const Point2 center, const float size_m) { int r = s.ms(size_m); p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r); @@ -55,14 +72,23 @@ public: p->drawLine(s.xms(x1), s.yms(y1), s.xms(x2), s.yms(y2)); } + void drawRect(const Point2 p1, const Point2 p2) { + drawRect(p1.x, p1.y, p2.x, p2.y); + } + void drawRect(const float x1, const float y1, const float x2, const float y2) { float w = x2-x1; float h = y1-y2; p->drawRect(s.xms(x1), s.yms(y1), s.ms(w), s.ms(h)); } + void drawRect(const Point2 center) { + float r = s.sm(5); // 5 pixel + drawRect(center-Point2(r,r), center+Point2(r,r)); + } + void drawText(const Point2 pos, const std::string& text) { - p->drawText(s.xms(pos.x), s.xms(pos.y), text.c_str()); + p->drawText(s.xms(pos.x), s.yms(pos.y), text.c_str()); } void drawPolygon(const std::vector& points) { @@ -80,6 +106,14 @@ public: p->drawPolygon(vec.data(), vec.size()); } + void drawPixmap(const Point2 pt, const QPixmap& img) { + p->drawPixmap(s.xms(pt.x)-img.width()/2, s.yms(pt.y)-img.height()/2, img); + } + + void drawImage(const Point2 pt, const QImage& img) { + p->drawImage(s.xms(pt.x)-img.width()/2, s.yms(pt.y)-img.height()/2, img); + } + void setBrush(const QBrush& brush) { p->setBrush(brush); } void setBrush(const Qt::BrushStyle& brush) { p->setBrush(brush); } diff --git a/mapview/Scaler.h b/mapview/Scaler.h index 86acae6..1eca86a 100644 --- a/mapview/Scaler.h +++ b/mapview/Scaler.h @@ -88,9 +88,21 @@ public: public: + float majorGridLineStep() const {return 1.0f * getLODstep();} + float minorGridLineStep() const {return majorGridLineStep() / 5.0f;} + + /** snap everything to minor grid lines */ + float snap(const float v) const { return snap(v, minorGridLineStep()); } + + Point2 snap(const Point2 p) const { return Point2(snap(p.x), snap(p.y)); } + Point3 snap(const Point3 p) const { return Point3(snap(p.x), snap(p.y), snap(p.z)); } + + +private: + static float snap(const float v, const float grid) { return std::round(v/grid)*grid; } - static Point2 snap(const Point2 p, const float grid) { return Point2(snap(p.x, grid), snap(p.y, grid)); } - static Point3 snap(const Point3 p, const float grid) { return Point3(snap(p.x, grid), snap(p.y, grid), snap(p.z, grid)); } + //static Point2 snap(const Point2 p, const float grid) { return Point2(snap(p.x, grid), snap(p.y, grid)); } + //tatic Point3 snap(const Point3 p, const float grid) { return Point3(snap(p.x, grid), snap(p.y, grid), snap(p.z, grid)); } static float snapCeil(const float v, const float grid) { return std::ceil(v/grid) * grid; } static float snapFloor(const float v, const float grid) { return std::floor(v/grid) * grid; } diff --git a/mapview/elements/HasMoveableNodes.h b/mapview/elements/HasMoveableNodes.h new file mode 100644 index 0000000..48c2ff2 --- /dev/null +++ b/mapview/elements/HasMoveableNodes.h @@ -0,0 +1,64 @@ +#ifndef MV2D_IHASMOVEABLENODES_H +#define MV2D_IHASMOVEABLENODES_H + +#include +#include "../MapView2D.h" + +/** the selectable/moveable node */ +struct MoveableNode { + + /** user-defined index */ + int userIdx; + + /** the node's position */ + Point2 pos; + + + /** ctor */ + MoveableNode(const int userIdx, const Point2 pos) : userIdx(userIdx), pos(pos) {;} + +}; + +/** + * base for all 2D elements that have selectable and moveable nodes + * the ToolSelector is able to get, select, and move those nodes + */ +class HasMoveableNodes { + +protected: + + /** currently selected node */ + int selectedUserIdx = -1; + +public: + + + /** get a list of all nodes that are selectable / moveable */ + virtual std::vector getMoveableNodes() const = 0; + + /** the given node was moved */ + virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) = 0; + + + /** the given node was selected */ + virtual void onNodeSelect(MapView2D* v, const int userIdx) { + (void) v; + this->selectedUserIdx = userIdx; + } + + /** unselect any selected node */ + virtual void onNodeUnselect(MapView2D* v) { + (void) v; + this->selectedUserIdx = -1; + } + + /** get the currently selected node */ + virtual int getSelectedNode() const { + return selectedUserIdx; + } + + + +}; + +#endif // MV2D_IHASMOVEABLENODES_H diff --git a/mapview/elements/MV2DElement.h b/mapview/elements/MV2DElement.h index 264378d..71e4f17 100644 --- a/mapview/elements/MV2DElement.h +++ b/mapview/elements/MV2DElement.h @@ -63,6 +63,7 @@ public: /** key pressed. return true when consumed. */ virtual bool keyPressEvent(MapView2D* v, QKeyEvent* e) = 0; + protected: virtual void onFocus() = 0; diff --git a/mapview/elements/MV2DElementAccessPoint.h b/mapview/elements/MV2DElementAccessPoint.h index 2657dde..922dd85 100644 --- a/mapview/elements/MV2DElementAccessPoint.h +++ b/mapview/elements/MV2DElementAccessPoint.h @@ -2,14 +2,18 @@ #define MV2DELEMENTACCESSPOINT_H #include "MV2DElement.h" +#include "HasMoveableNodes.h" + #include "MapViewElementHelper.h" #include -class MV2DElementAccessPoint : public MV2DElement { +#include "../../UIHelper.h" + +class MV2DElementAccessPoint : public MV2DElement, public HasMoveableNodes { private: - bool sel = false; + //bool sel = false; Floorplan::AccessPoint* ap; public: @@ -34,19 +38,28 @@ public: /** repaint me */ void paint(Painter& p) override { - if (sel) { - p.setPenBrush(Qt::black, CFG::SEL_COLOR); - p.drawCircle(ap->pos.xy()); + static const QPixmap& pixmapUnfocused = UIHelper::getPixmapColored("wifi", CFG::UNFOCUS_COLOR, 16); + static const QPixmap& pixmapFocused = UIHelper::getPixmapColored("wifi", CFG::FOCUS_COLOR, 16); + static const QPixmap& pixmapSel = UIHelper::getPixmapColored("wifi", CFG::SEL_COLOR, 16); + + + if (selectedUserIdx == 0) { + //p.setPenBrush(Qt::black, CFG::SEL_COLOR); + //p.drawCircle(ap->pos.xy()); + p.drawPixmap(ap->pos.xy(), pixmapSel); } else if (hasFocus()) { - p.setPenBrush(Qt::black, Qt::NoBrush); - p.drawCircle(ap->pos.xy()); + //p.setPenBrush(Qt::black, Qt::NoBrush); + //p.drawCircle(ap->pos.xy()); + p.drawPixmap(ap->pos.xy(), pixmapFocused); } else { - p.setPenBrush(Qt::gray, Qt::NoBrush); - p.drawCircle(ap->pos.xy()); + //p.setPenBrush(Qt::gray, Qt::NoBrush); + //p.drawCircle(ap->pos.xy()); + p.drawPixmap(ap->pos.xy(), pixmapUnfocused); } // label p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawDot(ap->pos.xy()); if (p.getScaler().getScale() >= 25) { 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()); @@ -57,6 +70,14 @@ public: } + virtual std::vector getMoveableNodes() const override { + return { MoveableNode(0, ap->pos.xy()) }; + } + + virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override { + (void) v; + if (userIdx == 0) {ap->pos.x = newPos.x; ap->pos.y = newPos.y;} + } /** mouse pressed at the given point */ @@ -67,20 +88,15 @@ public: } /** mouse moved to the given point */ - virtual void mouseMove(MapView2D* v, const Point2 _p) override { + virtual void mouseMove(MapView2D* v, const Point2 p) override { (void) v; - if (sel) { - const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); - ap->pos.x = p.x; - ap->pos.y = p.y; - } + (void) p; } /** mouse released */ - virtual void mouseReleased(MapView2D* v, const Point2 _p) override { + virtual void mouseReleased(MapView2D* v, const Point2 p) override { (void) v; - (void) _p; - sel = true; + (void) p; } virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { @@ -90,11 +106,11 @@ public: } virtual void onFocus() override { - + ; } virtual void onUnfocus() override { - sel = false; + ; } }; diff --git a/mapview/elements/MV2DElementBeacon.h b/mapview/elements/MV2DElementBeacon.h index 6b8c2bc..34503d3 100644 --- a/mapview/elements/MV2DElementBeacon.h +++ b/mapview/elements/MV2DElementBeacon.h @@ -2,14 +2,17 @@ #define MV2DELEMENTBEACON_H #include "MV2DElement.h" +#include "HasMoveableNodes.h" + #include "MapViewElementHelper.h" #include -class MV2DElementBeacon : public MV2DElement { +#include "../../UIHelper.h" + +class MV2DElementBeacon : public MV2DElement, public HasMoveableNodes { private: - bool sel = false; Floorplan::Beacon* b; public: @@ -33,19 +36,28 @@ public: /** repaint me */ void paint(Painter& p) override { - if (sel) { - p.setPenBrush(Qt::black, CFG::SEL_COLOR); - p.drawCircle(b->pos.xy()); + static const QPixmap& pixmapUnfocused = UIHelper::getPixmapColored("beacon", CFG::UNFOCUS_COLOR, 16); + static const QPixmap& pixmapFocused = UIHelper::getPixmapColored("beacon", CFG::FOCUS_COLOR, 16); + static const QPixmap& pixmapSel = UIHelper::getPixmapColored("beacon", CFG::SEL_COLOR, 16); + + if (selectedUserIdx == 0) { + //p.setPenBrush(Qt::black, CFG::SEL_COLOR); + //p.drawCircle(b->pos.xy()); + p.drawPixmap(b->pos.xy(), pixmapSel); + } else if (hasFocus()) { - p.setPenBrush(Qt::black, Qt::NoBrush); - p.drawCircle(b->pos.xy()); + //p.setPenBrush(Qt::black, Qt::NoBrush); + //p.drawCircle(b->pos.xy()); + p.drawPixmap(b->pos.xy(), pixmapFocused); } else { - p.setPenBrush(Qt::gray, Qt::NoBrush); - p.drawCircle(b->pos.xy()); + //p.setPenBrush(Qt::gray, Qt::NoBrush); + //p.drawCircle(b->pos.xy()); + p.drawPixmap(b->pos.xy(), pixmapUnfocused); } // label p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawDot(b->pos.xy()); if (p.getScaler().getScale() >= 25) { 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()); @@ -56,12 +68,21 @@ public: } + virtual std::vector getMoveableNodes() const override { + return { MoveableNode(0, b->pos.xy()) }; + } + + virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override { + (void) v; + if (userIdx == 0) {b->pos.x = newPos.x; b->pos.y = newPos.y;} + } + virtual void onFocus() override { ; } virtual void onUnfocus() override { - sel = false; + ; } /** mouse pressed at the given point */ @@ -71,20 +92,15 @@ public: } /** mouse moved to the given point */ - virtual void mouseMove(MapView2D* v, const Point2 _p) override { + virtual void mouseMove(MapView2D* v, const Point2 p) override { (void) v; - if (sel) { - const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); - b->pos.x = p.x; - b->pos.y = p.y; - } + (void) p; } /** 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 { diff --git a/mapview/elements/MV2DElementFloorObstacleCircle.h b/mapview/elements/MV2DElementFloorObstacleCircle.h index b228ca0..f8c7bf4 100644 --- a/mapview/elements/MV2DElementFloorObstacleCircle.h +++ b/mapview/elements/MV2DElementFloorObstacleCircle.h @@ -35,7 +35,7 @@ public: /** repaint me */ void paint(Painter& p) override { - QPen pen = MapElementHelper::getPen(c->material, c->type, hasFocus()); + QPen pen;// = MapElementHelper::getPen(c->material, c->type, hasFocus()); p.setPenBrush(pen, Qt::NoBrush); p.drawCircle(c->center, c->radius); @@ -81,7 +81,7 @@ public: virtual void mouseMove(MapView2D* v, const Point2 _p) override { (void) v; if (selPoint == -1) {return;} - const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + const Point2 p = v->getScaler().snap(_p); if (selPoint == 0) {c->center = p;} if (selPoint == 1) {c->radius = p.getDistance(c->center);} } diff --git a/mapview/elements/MV2DElementFloorObstacleDoor.h b/mapview/elements/MV2DElementFloorObstacleDoor.h new file mode 100644 index 0000000..d219054 --- /dev/null +++ b/mapview/elements/MV2DElementFloorObstacleDoor.h @@ -0,0 +1,146 @@ +#ifndef MV2DELEMENTFLOOROBSTACLEDOOR_H +#define MV2DELEMENTFLOOROBSTACLEDOOR_H + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" +#include "HasMoveableNodes.h" +#include + +class MV2DElementFloorObstacleDoor : public MV2DElement, public HasMoveableNodes { + +private: + + //int selPoint = -1; + Floorplan::FloorObstacleDoor* fo; + +public: + + /** ctor */ + MV2DElementFloorObstacleDoor(Floorplan::FloorObstacleDoor* fo) : fo(fo) {;} + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + BBox2 bbox; + bbox.add(fo->from); + bbox.add(fo->to); + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + return MapElementHelper::getLineDistanceXY(fo->from, fo->to, p); + } + + + + /** repaint me */ + void paint(Painter& p) override { + + // selected endpoints? + if (hasFocus()) { + p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR); + if (selectedUserIdx == 0) {p.drawCircle(fo->from);} + if (selectedUserIdx == 1) {p.drawCircle(fo->to);} + } + + QPen pen; + pen.setColor(QColor(0.5,0.5,0.5)); + pen.setStyle(Qt::PenStyle::DotLine); + p.setPenBrush(pen, Qt::NoBrush); + + // opening indicator + const float open = (fo->swap) ? (-M_PI * 0.5) : (+M_PI * 0.5); + const float len = (fo->to - fo->from).length(); + const float angle1 = std::atan2(fo->to.y-fo->from.y, fo->to.x-fo->from.x); + const float angle2 = angle1 + open; + const Point2 pOpen = Point2( std::cos(angle2) * len, std::sin(angle2) * len ) + fo->from; + + pen.setWidth(2); p.setPen(pen); + p.drawLine(fo->from, fo->to); + + pen.setWidth(1); p.setPen(pen); + p.drawLine(fo->from, pOpen); + p.drawArc(fo->from, len, angle1, open); + + // available endpoints + if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawCircle(fo->from); + p.drawCircle(fo->to); + } + + } + + + virtual void onFocus() override { + ; + } + + virtual void onUnfocus() override { + selectedUserIdx = -1; // clear selection + } + + virtual void mousePressed(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + } + + virtual void mouseMove(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + } + + virtual void mouseReleased(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 (selPoint == -1) {return;} +// const Point2 p = v->getScaler().snap(_p); +// if (selPoint == 0) {fo->from.x = p.x; fo->from.y = p.y;} +// if (selPoint == 1) {fo->to.x = p.x; fo->to.y = p.y;} +// emit v->onElementChange(this); +// } + + /** mouse released */ +// virtual void mouseReleased(MapView2D* v, const Point2 _p) override { +// // select a new point on mouse-release (more robust than on mouse-press) +// const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX); +// const float l1 = _p.getDistance(fo->from); +// const float l2 = _p.getDistance(fo->to); +// if (l1 < l2 && l1 <= t) {selPoint = 0;} +// else if (l2 < l1 && l2 <= t) {selPoint = 1;} +// else {selPoint = -1;} + +// } + + /** get a list of all nodes that are selectable / moveable */ + virtual std::vector getMoveableNodes() const override { + std::vector nodes = { + MoveableNode(0, fo->from), + MoveableNode(1, fo->to) + }; + return nodes; + } + + /** the given node was moved */ + virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override { + switch (userIdx) { + case 0: fo->from = newPos; break; + case 1: fo->to = newPos; break; + } + emit v->onElementChange(this); + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + return false; + } + +}; + +#endif // MV2DELEMENTFLOOROBSTACLEDOOR_H diff --git a/mapview/elements/MV2DElementFloorObstacleLine.h b/mapview/elements/MV2DElementFloorObstacleLine.h index 5f3d48c..c8baeda 100644 --- a/mapview/elements/MV2DElementFloorObstacleLine.h +++ b/mapview/elements/MV2DElementFloorObstacleLine.h @@ -42,10 +42,18 @@ public: if (selPoint == 1) {p.drawCircle(fo->to);} } - // line + p.setPenBrush(MapElementHelper::getPen(fo->material, fo->type, hasFocus()), Qt::NoBrush); p.drawLine(fo->from, fo->to); +// // door? +// if (fo->type == Floorplan::ObstacleType::DOOR) { +// paintDoor(p); +// } else { +// p.setPenBrush(MapElementHelper::getPen(fo->material, fo->type, hasFocus()), Qt::NoBrush); +// p.drawLine(fo->from, fo->to); +// } + // available endpoints if (hasFocus()) { p.setPenBrush(Qt::black, Qt::NoBrush); @@ -55,6 +63,29 @@ public: } + void paintDoor(Painter& p) { + + QPen pen; + pen.setColor(QColor(0.5,0.5,0.5)); + pen.setStyle(Qt::PenStyle::DotLine); + p.setPenBrush(pen, Qt::NoBrush); + + + // opening indicator + const float open = M_PI / 4; + const float len = (fo->to - fo->from).length(); + const float angle1 = std::atan2(fo->to.y-fo->from.y, fo->to.x-fo->from.x); + const float angle2 = angle1 + open; + const Point2 pOpen = Point2( std::cos(angle2) * len, std::sin(angle2) * len ) + fo->from; + + p.drawLine(fo->from, fo->to); + p.drawLine(fo->from, pOpen); + + p.drawArc(fo->from, len, angle1, open); + //p.drawLine(fo->to, pOpen); + + } + virtual void onFocus() override { ; } @@ -74,7 +105,7 @@ public: virtual void mouseMove(MapView2D* v, const Point2 _p) override { (void) v; if (selPoint == -1) {return;} - const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + const Point2 p = v->getScaler().snap(_p); if (selPoint == 0) {fo->from.x = p.x; fo->from.y = p.y;} if (selPoint == 1) {fo->to.x = p.x; fo->to.y = p.y;} } diff --git a/mapview/elements/MV2DElementFloorOutlinePolygon.h b/mapview/elements/MV2DElementFloorOutlinePolygon.h index b4aa802..34f10e1 100644 --- a/mapview/elements/MV2DElementFloorOutlinePolygon.h +++ b/mapview/elements/MV2DElementFloorOutlinePolygon.h @@ -93,7 +93,7 @@ public: virtual void mouseMove(MapView2D* v, const Point2 _p) override { (void) v; if (selPoint == -1) {return;} - const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + const Point2 p = v->getScaler().snap(_p); fo.poly.points[selPoint].x = p.x; fo.poly.points[selPoint].y = p.y; } diff --git a/mapview/elements/MV2DElementFloorUnderlay.h b/mapview/elements/MV2DElementFloorUnderlay.h index 66e3a9c..9dfc5ca 100644 --- a/mapview/elements/MV2DElementFloorUnderlay.h +++ b/mapview/elements/MV2DElementFloorUnderlay.h @@ -115,7 +115,7 @@ public: virtual void mouseMove(MapView2D* v, const Point2 _p) override { (void) v; if (selPoint == -1) {return;} - const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + const Point2 p = v->getScaler().snap(_p); if (selPoint == 0) {underlay->anchor = p;} } diff --git a/mapview/elements/MV2DElementPOI.h b/mapview/elements/MV2DElementPOI.h index c484530..839e2ad 100644 --- a/mapview/elements/MV2DElementPOI.h +++ b/mapview/elements/MV2DElementPOI.h @@ -2,14 +2,17 @@ #define MV2DELEMENTPOI_H #include "MV2DElement.h" +#include "HasMoveableNodes.h" #include "MapViewElementHelper.h" #include -class MV2DElementPOI : public MV2DElement { +#include "../../UIHelper.h" + +class MV2DElementPOI : public MV2DElement, public HasMoveableNodes { private: - bool sel = false; + //bool sel = false; Floorplan::POI* poi; public: @@ -34,19 +37,28 @@ public: /** repaint me */ void paint(Painter& p) override { - if (sel) { - p.setPenBrush(Qt::black, CFG::SEL_COLOR); - p.drawCircle(poi->pos); + static const QPixmap& pixmapUnfocused = UIHelper::getPixmapColored("poi", CFG::UNFOCUS_COLOR, 16); + static const QPixmap& pixmapFocused = UIHelper::getPixmapColored("poi", CFG::FOCUS_COLOR, 16); + static const QPixmap& pixmapSel = UIHelper::getPixmapColored("poi", CFG::SEL_COLOR, 16); + + + if (selectedUserIdx == 0) { +// p.setPenBrush(Qt::black, CFG::SEL_COLOR); +// p.drawCircle(poi->pos); + p.drawPixmap(poi->pos, pixmapSel); } else if (hasFocus()) { - p.setPenBrush(Qt::black, Qt::NoBrush); - p.drawCircle(poi->pos); +// p.setPenBrush(Qt::black, Qt::NoBrush); +// p.drawCircle(poi->pos); + p.drawPixmap(poi->pos, pixmapFocused); } else { - p.setPenBrush(Qt::gray, Qt::NoBrush); - p.drawCircle(poi->pos); +// p.setPenBrush(Qt::gray, Qt::NoBrush); +// p.drawCircle(poi->pos); + p.drawPixmap(poi->pos, pixmapUnfocused); } // label p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawDot(poi->pos); 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()); @@ -55,29 +67,37 @@ public: } + virtual std::vector getMoveableNodes() const override { + return { MoveableNode(0, poi->pos) }; + } + + virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override { + (void) v; + if (userIdx == 0) {poi->pos = newPos;} + } + /** 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 { + 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; - } + (void) p; +// if (sel) { +// const Point2 p = v->getScaler().snap(_p); +// poi->pos.x = p.x; +// poi->pos.y = p.y; +// } } /** mouse released */ - virtual void mouseReleased(MapView2D* v, const Point2 _p) override { + virtual void mouseReleased(MapView2D* v, const Point2 p) override { (void) v; - (void) _p; - sel = true; + (void) p; } virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { @@ -87,11 +107,11 @@ public: } virtual void onFocus() override { - + ; } virtual void onUnfocus() override { - sel = false; + ; } }; diff --git a/mapview/elements/MV2DElementStair.h b/mapview/elements/MV2DElementStair.h new file mode 100644 index 0000000..ac6f325 --- /dev/null +++ b/mapview/elements/MV2DElementStair.h @@ -0,0 +1,233 @@ +#ifndef MV2DELEMENTSTAIR_H +#define MV2DELEMENTSTAIR_H + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" +#include + +class MV2DElementStair : public MV2DElement { + +private: + + bool sel = false; + Floorplan::Floor* floor; + Floorplan::Stair* stair; + int selPart = -1; + int selNode = -1; + +public: + + /** ctor with the AP to render/edit */ + MV2DElementStair(Floorplan::Floor* floor, Floorplan::Stair* stair) : floor(floor), stair(stair) {;} + + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + BBox2 bbox; + if (dynamic_cast(stair)) { + Floorplan::StairFreeform* stair = dynamic_cast(this->stair); + for (const Floorplan::StairPart p : stair->parts) { + bbox.add(p.start.xy()); + bbox.add(p.end.xy()); + } + } + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + + auto comp = [p] (const Floorplan::StairPart& p1, const Floorplan::StairPart& p2) { + const float d1 = MapElementHelper::getLineDistanceXY(p1.start.xy(), p1.end.xy(), p); + const float d2 = MapElementHelper::getLineDistanceXY(p2.start.xy(), p2.end.xy(), p); + return d1 < d2; + }; + + auto parts = stair->getParts(); + auto min = std::min_element(parts.begin(), parts.end(), comp); + return MapElementHelper::getLineDistanceXY(min->start.xy(), min->end.xy(), p); + + } + + + + + int getSelPart() const {return selPart;} + int getSelNode() const {return selNode;} + + static inline float clamp01(const float val) { + if (val < 0) {return 0;} + if (val > 1) {return 1;} + return val; + } + + /** repaint me */ + void paint(Painter& p) override { + + Floorplan::StairFreeform* stair = dynamic_cast(this->stair); + + + + + + +// if (sel) { +// p.setPenBrush(Qt::black, CFG::SEL_COLOR); +// p.drawCircle(stair->center); +// } else if (hasFocus()) { +// p.setPenBrush(Qt::black, Qt::NoBrush); +// p.drawCircle(stair->center); +// } else { +// p.setPenBrush(Qt::gray, Qt::NoBrush); +// p.drawCircle(stair->center); +// } + + // draw all parts of the stair (all polygons) + p.setPenBrush(Qt::black, Qt::NoBrush); + + std::vector parts = stair->getParts(); + std::vector quads = Floorplan::getQuads(parts, floor); + + for (int i = 0; i < (int) parts.size(); ++i) { + + const Floorplan::StairPart& part = parts[i]; + const Floorplan::Quad3& quad = quads[i]; + + // fill the polygon with a gradient corresponding with the stair's height relative to the floor's height + QLinearGradient gradient(p.s.xms(part.start.x), p.s.yms(part.start.y), p.s.xms(part.end.x), p.s.yms(part.end.y)); + const float p1 = 0.1 + clamp01( part.start.z / floor->height) * 0.8; + const float p2 = 0.1 + clamp01( part.end.z / floor->height) * 0.8; + gradient.setColorAt(0, QColor(p1*255, p1*255, p1*255)); + gradient.setColorAt(1, QColor(p2*255, p2*255, p2*255)); + p.setBrush(gradient); + p.setPen(QColor(0,0,0,128)); + + // polygon-construction + //const Floorplan::Quad3 quad = part.getQuad(floor); + const std::vector points = {quad.p1, quad.p2, quad.p3, quad.p4}; + p.drawPolygon(points); + + } + + if (hasFocus()) { + int cnt = 0; + std::vector parts = stair->getParts(); + for (const Floorplan::StairPart& part : parts) { + p.setPenBrush(Qt::black, (cnt == selPart && selNode == 0) ? CFG::SEL_COLOR : Qt::NoBrush); // part start + p.drawCircle(part.start.xy()); + p.setPenBrush(Qt::black, (cnt == selPart && selNode == 1) ? CFG::SEL_COLOR : Qt::NoBrush); // part end + p.drawRect(part.end.xy()); + p.setPenBrush(Qt::blue, Qt::NoBrush); + Point2 ctr = (part.start+part.end).xy() / 2; + p.drawText(ctr, "p" + std::to_string(cnt+1)); // part name + ++cnt; + } + for (int i = 0; i < (int)parts.size() - 1; ++i) { + const Point3 p1 = parts[i+0][1]; + const Point3 p2 = parts[i+1][0]; + p.drawLine(p1.xy(), p2.xy()); + } + } + + } + + + + /** 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 (selPart == -1) {return;} + Floorplan::StairFreeform* stair = dynamic_cast(this->stair); + const Point2 p = v->getScaler().snap(_p); + stair->parts[selPart][selNode].x = p.x; + stair->parts[selPart][selNode].y = p.y; + } + + /** mouse released */ + virtual void mouseReleased(MapView2D* v, const Point2 _p) override { + (void) v; + (void) _p; + // select a new point on mouse-release (more robust than on mouse-press) + Floorplan::StairFreeform* stair = dynamic_cast(this->stair); + const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX); +// auto comp = [&] (const Point3 a, const Point3 b) {return a.xy().getDistance(_p) < b.xy().getDistance(_p);}; +// auto it = std::min_element(stair->nodes.begin(), stair->nodes.end(), comp); +// if (it == stair->nodes.end()) {selIdx = -1;} // none found -> skip +// else if ((*it).xy().getDistance(_p) > t) {selIdx = -1;} // nearest distance is above threshold -> skip +// else {selIdx = it - stair->nodes.begin();} + + float best = 999999; + int minPart; int minNode; + for (int part = 0; part < (int) stair->parts.size(); ++part) { + for (int node = 0; node < 2; ++node) { + const float dist = stair->parts[part][node].xy().getDistance(_p); + if (dist < best) {best = dist; minPart = part; minNode = node;} + } + } + + if (best <= t) { + selPart = minPart; selNode = minNode; + } else { + selPart = -1; selNode = -1; + } + + emit v->onElementChange(this); + + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + + Floorplan::StairFreeform* stair = dynamic_cast(this->stair); + + if (e->key() == Qt::Key_Delete) { + + // delete the currently selected vertex? + if (selPart != -1) { +// stair->nodes.erase(stair->nodes.begin() + selIdx); +// selIdx = -1; +// return true; + } + + } else if (e->key() == Qt::Key_Plus && selPart != -1) { +// int idx1 = selIdx; +// int idx2 = (selIdx + 1) % stair->nodes.size(); +// int idxNew = idx2; +// Point3 pNew = (stair->nodes[idx1] + stair->nodes[idx2]) / 2.0f; +// stair->nodes.insert(stair->nodes.begin() + idxNew, pNew); +// selIdx = idxNew; +// return true; + const int idxNew = selPart + 1; + const Point3 p0 = stair->parts[selPart][1]; + const Point3 p1 = p0 + Point3(1,1,0); + const Point3 p2 = p1 + Point3(2,2,0); + const float w = stair->parts[selPart].width; + stair->parts.insert(stair->parts.begin() + idxNew, Floorplan::StairPart(p1, p2, w)); + return true; + } + + // not consumed + return false; + + } + + virtual void onFocus() override { + selPart = -1; + selNode = -1; + } + + virtual void onUnfocus() override { + sel = false; + } + +}; + +#endif // MV2DELEMENTSTAIR_H diff --git a/mapview/elements/MapViewElementHelper.h b/mapview/elements/MapViewElementHelper.h index 3776bda..5156709 100644 --- a/mapview/elements/MapViewElementHelper.h +++ b/mapview/elements/MapViewElementHelper.h @@ -15,6 +15,8 @@ namespace CFG { const float MOVE_SNAP_SIZE_M = 0.1f; // in meter (= map-space) const int SEL_THRESHOLD_SIZE_PX = 15; // in screen-pixels (-> should depend on the current zoom) + const QColor FOCUS_COLOR = Qt::black; + const QColor UNFOCUS_COLOR = Qt::gray; const QColor SEL_COLOR = Qt::blue; } @@ -52,7 +54,9 @@ public: } else { // no cut detected - return 9999999; + const float d1 = p1.getDistance(dst); + const float d2 = p2.getDistance(dst); + return std::min(d1, d2); } diff --git a/mapview/model/IHasDoorType.h b/mapview/model/IHasDoorType.h new file mode 100644 index 0000000..c0988a9 --- /dev/null +++ b/mapview/model/IHasDoorType.h @@ -0,0 +1,16 @@ +#ifndef IHASDOORTYPE_H +#define IHASDOORTYPE_H + +#include + +class IHasDoorType { +public: + + virtual void setDoorType(const Floorplan::DoorType t) = 0; + + virtual Floorplan::DoorType getDoorType() const = 0; + +}; + + +#endif // IHASDOORTYPE_H diff --git a/mapview/model/IHasParams.h b/mapview/model/IHasParams.h index ced9212..8797823 100644 --- a/mapview/model/IHasParams.h +++ b/mapview/model/IHasParams.h @@ -6,6 +6,8 @@ #include enum class ParamType { + NOT_AVAILABLE, + BOOL, INT, FLOAT, STRING, @@ -18,6 +20,7 @@ class ParamValue { private: union { + bool _bool; int _int; float _float; float _arr[3]; @@ -31,16 +34,18 @@ 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;} + void setValue(const float val) {_float = val;} + void setValue(const int val) {_int = val;} + void setValue(const bool val) {_bool = val;} + void setValue(const Point2 p) {_arr[0] = p.x; _arr[1] = p.y;} + void setValue(const 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;} + bool toBool() const {return _bool;} }; @@ -66,9 +71,23 @@ public: //}; struct Param { + + /** parameter name */ std::string name; + + /** parameter type */ ParamType type; - Param(const std::string& name, const ParamType type) : name(name), type(type) {;} + + /** read-only parameter? */ + bool readOnly; + + + /** ctor */ + Param(const std::string& name, const ParamType type, const bool readOnly = false) : name(name), type(type), readOnly(readOnly) {;} + + /** special parameter */ + static Param getNA() { return Param("", ParamType::NOT_AVAILABLE); } + }; /** free parameters */ diff --git a/mapview/model/MMFloor.h b/mapview/model/MMFloor.h index ed79f3c..3b2d67b 100644 --- a/mapview/model/MMFloor.h +++ b/mapview/model/MMFloor.h @@ -8,6 +8,7 @@ #include "MMFloorBeacons.h" #include "MMFloorUnderlays.h" #include "MMFloorPOIs.h" +#include "MMFloorStairs.h" #include "IHasParams.h" @@ -37,6 +38,7 @@ public: new MMFloorAccessPoints(this, floor); new MMFloorBeacons(this, floor); new MMFloorPOIs(this, floor); + new MMFloorStairs(this, floor); } diff --git a/mapview/model/MMFloorObstacleCircle.h b/mapview/model/MMFloorObstacleCircle.h index 653c6f4..6737530 100644 --- a/mapview/model/MMFloorObstacleCircle.h +++ b/mapview/model/MMFloorObstacleCircle.h @@ -13,7 +13,7 @@ #include -class MMFloorObstacleCircle : public MapModelElement, public IHasMaterial, public IHasObstacleType { +class MMFloorObstacleCircle : public MapModelElement, public IHasMaterial { private: @@ -31,8 +31,8 @@ public: void setMaterial(const Floorplan::Material m) override {c->material = m;} Floorplan::Material getMaterial() const override {return c->material;} - void setObstacleType(const Floorplan::ObstacleType t) override {c->type = t;} - Floorplan::ObstacleType getObatcleType() const override {return c->type;} +// void setObstacleType(const Floorplan::ObstacleType t) override {c->type = t;} +// Floorplan::ObstacleType getObatcleType() const override {return c->type;} MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} diff --git a/mapview/model/MMFloorObstacleDoor.h b/mapview/model/MMFloorObstacleDoor.h new file mode 100644 index 0000000..cb6bc89 --- /dev/null +++ b/mapview/model/MMFloorObstacleDoor.h @@ -0,0 +1,84 @@ +#ifndef MMFLOOROBSTACLEDOOR_H +#define MMFLOOROBSTACLEDOOR_H + +#include "MapModelElement.h" +#include "../elements/MapViewElementHelper.h" + +#include "IHasMaterial.h" +#include "IHasDoorType.h" +#include "IHasParams.h" + +#include "../elements/MV2DElementFloorObstacleDoor.h" +#include "../3D/MV3DElementFloorObstacleDoor.h" + +#include + + +class MMFloorObstacleDoor : public MapModelElement, public IHasMaterial, public IHasDoorType, public IHasParams { + +public: + + Floorplan::Floor* mf; + Floorplan::FloorObstacleDoor* fo; + MV2DElementFloorObstacleDoor mv2d; + MV3DElementFloorObstacleDoor mv3d; + +public: + + MMFloorObstacleDoor(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorObstacleDoor* fo) : + MapModelElement(parent), mf(mf), fo(fo), mv2d(fo), mv3d(mf,fo) { + + } + + void setMaterial(const Floorplan::Material m) override {fo->material = m;} + Floorplan::Material getMaterial() const override {return fo->material;} + + void setDoorType(const Floorplan::DoorType t) override {fo->type = t;} + Floorplan::DoorType getDoorType() 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); + mf->obstacles.erase(std::remove(mf->obstacles.begin(), mf->obstacles.end(), fo), mf->obstacles.end()); + } + + /** get the number of parameters */ + int getNumParams() const override { + return 3; + } + + /** get the description of the idx-th parameter */ + virtual Param getParamDesc(const int idx) const override { + switch (idx) { + case 0: return Param("width", ParamType::FLOAT, true); + case 1: return Param("height", ParamType::FLOAT); + case 2: return Param("swap", ParamType::BOOL); + default: throw Exception("out of bounds"); + } + } + + /** get the idx-th param's value */ + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return fo->getSize(); + case 1: return fo->height; + case 2: return fo->swap; + default: throw Exception("out of bounds"); + } + } + + /** set the idx-th param's value */ + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch (idx) { + case 0: break; + case 1: fo->height = val.toFloat(); break; + case 2: fo->swap = val.toBool(); break; + default: throw Exception("out of bounds"); + } + } + +}; + +#endif // MMFLOOROBSTACLEDOOR_H diff --git a/mapview/model/MMFloorObstacles.h b/mapview/model/MMFloorObstacles.h index 5144dc6..f58f8b8 100644 --- a/mapview/model/MMFloorObstacles.h +++ b/mapview/model/MMFloorObstacles.h @@ -4,6 +4,7 @@ #include "MapLayer.h" #include "MMFloorObstacleCircle.h" #include "MMFloorObstacleLine.h" +#include "MMFloorObstacleDoor.h" #include @@ -25,6 +26,10 @@ public: elements.push_back(new MMFloorObstacleLine(this, floor, (Floorplan::FloorObstacleLine*)o)); } else if (dynamic_cast(o)) { elements.push_back(new MMFloorObstacleCircle(this, floor, (Floorplan::FloorObstacleCircle*)o)); + } else if (dynamic_cast(o)) { + elements.push_back(new MMFloorObstacleDoor(this, floor, (Floorplan::FloorObstacleDoor*)o)); + } else { + throw new Exception("todo: not yet implemented obstacle type"); } } @@ -33,6 +38,17 @@ public: /** get the corresponding floor from the underlying model */ Floorplan::Floor* getFloor() {return floor;} + //TODO: check + void createDoor(Floorplan::FloorObstacleDoor* obs) { + + // add to underlying model + floor->obstacles.push_back(obs); + + // add to myself as element + elements.push_back(new MMFloorObstacleDoor(this, floor, obs)); + + } + //TODO: check void createLine(Floorplan::FloorObstacleLine* obs) { diff --git a/mapview/model/MMFloorOutlinePolygon.h b/mapview/model/MMFloorOutlinePolygon.h index 1d541d0..4a225e6 100644 --- a/mapview/model/MMFloorOutlinePolygon.h +++ b/mapview/model/MMFloorOutlinePolygon.h @@ -44,7 +44,7 @@ public: virtual Param getParamDesc(const int idx) const override { switch(idx) { - case 0: return Param("anem", ParamType::STRING); + case 0: return Param("name", ParamType::STRING); } throw 1; } diff --git a/mapview/model/MMFloorStair.h b/mapview/model/MMFloorStair.h new file mode 100644 index 0000000..55530a6 --- /dev/null +++ b/mapview/model/MMFloorStair.h @@ -0,0 +1,132 @@ +#ifndef MMFLOORSTAIR_H +#define MMFLOORSTAIR_H + +#include "MapLayer.h" +#include "IHasParams.h" +#include "MMFloorOutlinePolygon.h" + +#include "../elements/MV2DElementStair.h" +#include "../3D/MV3DElementStair.h" + + +#include + +/** + * layer containing all elements describing a floor's outline + */ +class MMFloorStair : public MapModelElement, public IHasParams { + +private: + + /** the underlying model */ + Floorplan::Floor* floor; + Floorplan::StairFreeform* stair; + + MV2DElementStair mv2d; + MV3DElementStair mv3d; + +public: + + /** ctor with the underlying model */ + MMFloorStair(MapLayer* parent, Floorplan::Floor* floor, Floorplan::StairFreeform* stair) : + MapModelElement(parent), floor(floor), stair(stair), mv2d(floor, stair), mv3d(floor, stair) { + + ; + + } + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} + + + virtual int getNumParams() const override { + const int selPart = mv2d.getSelPart(); + return (selPart >= 0) ? (3) : (0); + } + + virtual Param getParamDesc(const int idx) const override { + const int selPart = mv2d.getSelPart(); + const int selNode = mv2d.getSelNode(); + switch(idx) { + case 0: return Param("node height", ParamType::FLOAT); + case 1: return (selPart >= 0 && selNode == 0) ? Param("part width", ParamType::FLOAT) : Param::getNA(); + case 2: return (selPart >= 0 && selNode == 0) ? Param("connect", ParamType::BOOL) : Param::getNA(); + } + throw 1; + } + + virtual ParamValue getParamValue(const int idx) const override { + const int selPart = mv2d.getSelPart(); + const int selNode = mv2d.getSelNode(); + switch(idx) { + case 0: if (selPart >= 0) {return stair->parts[selPart][selNode].z;} else {return NAN;} + case 1: if (selPart >= 0) {return stair->parts[selPart].width;} else {return NAN;} + case 2: if (selPart >= 0) {return stair->parts[selPart].connectWithPrev;} else {return false;} + } + throw 1; + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + const int selPart = mv2d.getSelPart(); + const int selNode = mv2d.getSelNode(); + switch(idx) { + case 0: if (selPart >= 0) {stair->parts[selPart][selNode].z = val.toFloat();} break; + case 1: if (selPart >= 0) {stair->parts[selPart].width = val.toFloat();} break; + case 2: if (selPart >= 0) {stair->parts[selPart].connectWithPrev = val.toBool();} break; + } + } + + void deleteMe() const override { + parent->removeElement(this); + floor->stairs.erase(std::remove(floor->stairs.begin(), floor->stairs.end(), stair), floor->stairs.end()); + } + +// virtual int getNumParams() const override { +// return 4; +// } + +// virtual Param getParamDesc(const int idx) const override { +// switch(idx) { +// case 0: return Param("center", ParamType::POINT2); +// case 1: return Param("at height", ParamType::FLOAT); +// case 2: return Param("height", ParamType::FLOAT); +// case 3: return Param("angle", ParamType::FLOAT); +// } +// throw 1; +// } + +// virtual ParamValue getParamValue(const int idx) const override { +// switch(idx) { +// case 0: return stair->center; +// case 1: return stair->atHeight; +// case 2: return stair->height; +// case 3: return stair->angleDeg; +// } +// throw 1; +// } + +// virtual void setParamValue(const int idx, const ParamValue& val) const override { +// switch(idx) { +// case 0: stair->center = val.toPoint2(); break; +// case 1: stair->atHeight = val.toFloat(); break; +// case 2: stair->height = val.toFloat(); break; +// case 3: stair->angleDeg = val.toFloat(); break; +// } +// } + + + +// //TODO: check +// void create(Floorplan::FloorOutlinePolygon* poly) { + +// // add to underlying model +// floor->outline.push_back(poly); + +// // add to myself as element +// elements.push_back(new MMFloorOutlinePolygon(this, floor, poly)); + +// } + +}; + +#endif // MMFLOORSTAIR_H diff --git a/mapview/model/MMFloorStairs.h b/mapview/model/MMFloorStairs.h new file mode 100644 index 0000000..ace1fce --- /dev/null +++ b/mapview/model/MMFloorStairs.h @@ -0,0 +1,48 @@ +#ifndef MMFLOORSTAIRS_H +#define MMFLOORSTAIRS_H + +#include "MapLayer.h" +#include "MMFloorStair.h" + +#include + +/** + * layer containing all stairs of one floor + */ +class MMFloorStairs : public MapLayer { + +private: + + Floorplan::Floor* floor; + +public: + + /** ctor with the underlying model */ + MMFloorStairs(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_STAIRS), floor(floor) { + + // add all floors + for (Floorplan::Stair* stair : floor->stairs) { + if (dynamic_cast(stair)) { + elements.push_back( new MMFloorStair(this, floor, (Floorplan::StairFreeform*)stair) ); + } + } + + } + + void create(Floorplan::StairFreeform* stair) { + + // add to underlying model + floor->stairs.push_back(stair); + + // add to myself as element + elements.push_back(new MMFloorStair(this, floor, stair)); + + } + + std::string getLayerName() const override {return "stairs";} + + +}; + + +#endif // MMFLOORSTAIRS_H diff --git a/mapview/model/MapLayer.h b/mapview/model/MapLayer.h index 7d1a3b0..0166f65 100644 --- a/mapview/model/MapLayer.h +++ b/mapview/model/MapLayer.h @@ -19,6 +19,7 @@ enum class MapLayerType { FLOOR_ACCESS_POINTS, FLOOR_UNDERLAYS, FLOOR_POIS, + FLOOR_STAIRS, }; diff --git a/mapview/tools/ToolMapGrid.h b/mapview/tools/ToolMapGrid.h index db8c4cb..ce1f5e4 100644 --- a/mapview/tools/ToolMapGrid.h +++ b/mapview/tools/ToolMapGrid.h @@ -22,8 +22,8 @@ public: if (!show) {return;} - static const QColor cB(250,250,250); - static const QColor cN(235,235,235); + static const QColor cB(245,245,245); + static const QColor cN(225,225,225); static const QColor c0(128,128,128); int w = p.width(); diff --git a/mapview/tools/ToolSelector.h b/mapview/tools/ToolSelector.h index 3b0fefb..3c4d2e9 100644 --- a/mapview/tools/ToolSelector.h +++ b/mapview/tools/ToolSelector.h @@ -7,6 +7,12 @@ #include "../model/MapModel.h" +/** + * this tool allows: + * - selecting 2D elements within the view (focus/unfocus) + * - selecting and moving nodes of elements inheriting from HasMoveableNodes + * + */ class ToolSelector : public Tool { Q_OBJECT @@ -24,54 +30,115 @@ public: virtual ~ToolSelector() {;} - virtual void mousePressEvent(MapView2D* m, QMouseEvent* e) override { - if (e->button() != Qt::MouseButton::LeftButton) {return;} - - mouseIsDown = true; - - const Scaler& s = m->getScaler(); - Point2 p2(s.xsm(e->x()), s.ysm(e->y())); - //Point3 p3(s.xsm(e->x()), s.ysm(e->y()), 0); - - const float g = m->getScaler().sm(10); // increase each BBox by 10 px (needed mainly for hor/ver lines) - - // 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? - } - - // among those, find the best-matching one (smallest distance to an intersection) - auto lambda = [&] (const MapModelElement* el1, const MapModelElement* el2) {return el1->getMV2D()->getMinDistanceXY(p2) < el2->getMV2D()->getMinDistanceXY(p2);}; - auto it = std::min_element(possible.begin(), possible.end(), lambda); - MapModelElement* el = (it == possible.end()) ? (nullptr) : (*it); - - - - // focus changed? -> unfocus the old one (if any) - if (setFocused(el)) { - - ; - - } else { - - // focus kept. provide the currently focused element with events - if (focused) {focused->getMV2D()->mousePressed(m, p2);} - - } - - } /** needed eg. when the focused element gets invalid (switching visible layers) */ void layerChange(MapView2D* m) override { (void) m; - setFocused(nullptr); + setFocused(m, nullptr); } +private: + + void processUnfocus(MapView2D* m, MapModelElement* elem) { + + MV2DElement* me = elem->getMV2D(); + if (!me) {return;} + + // elements has selectedable nodes? unselect them + if (dynamic_cast(me)) { + dynamic_cast(me)->onNodeUnselect(m); + } + + // let the element itself process the unfocus + me->unfocus(); + + } + + void processPress(MapView2D* m, const Point2 p, MapModelElement* elem) { + + MV2DElement* me = elem->getMV2D(); + if (!me) {return;} + + // let the element itself process all events + me->mousePressed(m, p); + + } + + void processMove(MapView2D* m, const Point2 p, MapModelElement* elem) { + + MV2DElement* me = elem->getMV2D(); + if (!me) {return;} + + // elements has selectedable nodes? try to process them + if (dynamic_cast(me)) { + if (moveNode(m, p, dynamic_cast(me))) {return;} + } + + // otherwise: let the element itself process all events + me->mouseMove(m, p); + + } + + void processRelease(MapView2D* m, const Point2 p, MapModelElement* elem) { + + MV2DElement* me = elem->getMV2D(); + if (!me) {return;} + + // element has selectedable nodes? try to process them + if (dynamic_cast(me)) { + if (selectNode(m, p, dynamic_cast(me))) {return;} + } + + // otherwise: let the element itself process all events + me->mouseReleased(m, p); + + } + + + +private: + + /** the given element has selectable nodes. try to select the one near to the mouse. true if successful */ + bool selectNode(MapView2D* v, Point2 p, HasMoveableNodes* elem) { + + // select a new point on mouse-release (more robust than on mouse-press) + + // find the node nearest to p + auto comp = [&] (const MoveableNode& n1, const MoveableNode& n2) {return n1.pos.getDistance(p) < n2.pos.getDistance(p);}; + auto lst = elem->getMoveableNodes(); + auto it = std::min_element(lst.begin(), lst.end(), comp); + + // is the nearest point below the threshold? + const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX); + const float d = it->pos.getDistance(p); + if (d < t) { + elem->onNodeSelect(v, it->userIdx); + return true; + } else { + elem->onNodeSelect(v, -1); + return false; + } + + } + + /** move the currently selected node. true if successful */ + bool moveNode(MapView2D* v, Point2 p, HasMoveableNodes* elem) { + + // no node selected? -> done + if (elem->getSelectedNode() == -1) {return false;} + + // snap the node + const Point2 pSnapped = v->getScaler().snap(p); + + // move + elem->onNodeMove(v, elem->getSelectedNode(), pSnapped); + return true; + + } + + + private: /** @@ -79,7 +146,7 @@ private: * @param el the to-be-focused element or null * @return true if the focused element has changed. false otherwise */ - bool setFocused(MapModelElement* el) { + bool setFocused(MapView2D* v, MapModelElement* el) { // whether the focus has changed or not const bool focusChanged = (focused != el); @@ -87,7 +154,7 @@ private: if (focusChanged) { // unfocus the old one (if any) - if (focused) {focused->getMV2D()->unfocus();} + if (focused) {processUnfocus(v, focused);} // set the currently focused object (if any) focused = el; @@ -104,6 +171,53 @@ private: } + + +private: + + virtual void mousePressEvent(MapView2D* m, QMouseEvent* e) override { + + if (e->button() != Qt::MouseButton::LeftButton) {return;} + + mouseIsDown = true; + + const Scaler& s = m->getScaler(); + const Point2 p2(s.xsm(e->x()), s.ysm(e->y())); + + const float g = m->getScaler().sm(15); // increase each BBox by 15 px (needed mainly for hor/ver lines) + + // 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 2D bbox + bbox.grow(Point2(g, g)); // grow a little (needed for straight lines) + if (bbox.contains(p2)) {possible.push_back(el);} // intersection? + } + + // among those, find the best-matching one (smallest distance to an intersection) + auto lambda = [&] (const MapModelElement* el1, const MapModelElement* el2) {return el1->getMV2D()->getMinDistanceXY(p2) < el2->getMV2D()->getMinDistanceXY(p2);}; + auto it = std::min_element(possible.begin(), possible.end(), lambda); + MapModelElement* el = (it == possible.end()) ? (nullptr) : (*it); + + + + // focus changed? -> unfocus the old one (if any) + if (setFocused(m, el)) { + + ; + + } else { + + // focus kept. provide the currently focused element with events + if (focused) { + processPress(m, p2, focused); + } + + } + + } + virtual void mouseMoveEvent(MapView2D* m, QMouseEvent* e) override { const Scaler& s = m->getScaler(); @@ -113,7 +227,7 @@ private: if (mouseIsDown) { // provide the focused element with the mouse event? - if (focused) {focused->getMV2D()->mouseMove(m, p2); return;} + if (focused) {processMove(m, p2, focused);} } @@ -125,7 +239,7 @@ private: Point2 p2(s.xsm(e->x()), s.ysm(e->y())); // provide the focused element with the mouse event? - if (focused) {focused->getMV2D()->mouseReleased(m, p2);} + if (focused) {processRelease(m, p2, focused);} mouseIsDown = false; @@ -142,12 +256,12 @@ private: // not consumed -> additional options // ESCAPE -> unfocus - if (e->key() == Qt::Key_Escape) {setFocused(nullptr);} + if (e->key() == Qt::Key_Escape) {setFocused(m, nullptr);} // DELETE -> delete if (e->key() == Qt::Key_Delete) { focused->deleteMe(); - setFocused(nullptr); + setFocused(m, nullptr); } } diff --git a/misc/GnuplotExport.h b/misc/GnuplotExport.h new file mode 100644 index 0000000..07a4a14 --- /dev/null +++ b/misc/GnuplotExport.h @@ -0,0 +1,171 @@ +#ifndef GNUPLOTEXPORT_H +#define GNUPLOTEXPORT_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +class GnuplotExport { + + +public: + + +// template static void exp(const Floorplan::IndoorMap& map, Grid& grid) { + +// K::GnuplotSplotElementLines* walls =getWalls(map); +// write("/tmp/walls.dat", walls); + + +// K::GnuplotSplotElementPoints* nodesFloor = getNodes(grid, true, false); +// write("/tmp/nodesFloor.dat", nodesFloor); + +// K::GnuplotSplotElementPoints* nodesStair = getNodes(grid, false, true); +// write("/tmp/nodesStair.dat", nodesStair); + +// K::GnuplotSplotElementPoints* nodesAll = getNodes(grid, true, true); +// write("/tmp/nodesAll.dat", nodesAll); + + +// K::GnuplotSplotElementColorPoints* nodesAllImp = getColorNodes(grid, true, true); +// write("/tmp/nodesAllImp.dat", nodesAllImp); +// const std::string xxx = getColorNodes2(grid, true, true); +// write("/tmp/nodesAllImpPoly.dat", xxx); +// const std::string yyy = getColorNodes2(grid, true, false); +// write("/tmp/nodesFloorImpPoly.dat", yyy); + + +// K::GnuplotSplotElementLines* edgesFloor = getEdges(grid, true, false); +// write("/tmp/edgesFloor.dat", edgesFloor); + +// K::GnuplotSplotElementLines* edgesStair= getEdges(grid, false, true); +// write("/tmp/edgesStair.dat", edgesStair); + +// K::GnuplotSplotElementLines* edgesAll = getEdges(grid, true, true); +// write("/tmp/edgesAll.dat", edgesAll); + + +// } + + + template static void write(const std::string& file, const T* elem) { + std::ofstream os(file.c_str()); + if (!os.good()) {throw "error!";} + elem->addDataTo(os); + os.close(); + } + + static void write(const std::string& file, const std::string& str) { + std::ofstream os(file.c_str()); + if (!os.good()) {throw "error!";} + os << str; + os.close(); + } + + static K::GnuplotSplotElementLines* getWalls(const Floorplan::IndoorMap& map) { + + K::GnuplotSplotElementLines* lines = new K::GnuplotSplotElementLines(); + for (const Floorplan::Floor* f : map.floors) { + for (const Floorplan::FloorObstacle* o : f->obstacles) { + if (dynamic_cast(o)) { + Floorplan::FloorObstacleLine* l = (Floorplan::FloorObstacleLine*) o; + K::GnuplotPoint3 p1(l->from.x, l->from.y, f->atHeight); + K::GnuplotPoint3 p2(l->to.x, l->to.y, f->atHeight); + lines->addSegment(p1*100, p2*100); + } + } + } + return lines; + + } + + template static K::GnuplotSplotElementPoints* getNodes(Grid& grid, bool addFloor, bool addStair) { + + K::GnuplotSplotElementPoints* points = new K::GnuplotSplotElementPoints(); + for (const T& n : grid) { + if (n.getType() == GridNode::TYPE_STAIR && !addStair) {continue;} + if (n.getType() == GridNode::TYPE_FLOOR && !addFloor) {continue;} + + K::GnuplotPoint3 p(n.x_cm, n.y_cm, n.z_cm); + points->add(p); + } + return points; + + } + + + template static std::string getColorNodes2(Grid& grid, bool addFloor, bool addStair) { + + std::stringstream ss; + for (const T& n : grid) { + if (n.getType() == GridNode::TYPE_STAIR && !addStair) {continue;} + if (n.getType() == GridNode::TYPE_FLOOR && !addFloor) {continue;} + + int s = 15; + ss << "set object polygon from "; + ss << n.x_cm-s << "," << n.y_cm-s << "," << n.z_cm << " to "; + ss << n.x_cm+s << "," << n.y_cm-s << "," << n.z_cm << " to "; + ss << n.x_cm+s << "," << n.y_cm+s << "," << n.z_cm << " to "; + ss << n.x_cm-s << "," << n.y_cm+s << "," << n.z_cm << " "; + ss << "fs solid fc palette cb " << n.imp << "\n"; + } + return ss.str(); + + } + + template static K::GnuplotSplotElementColorPoints* getColorNodes(Grid& grid, bool addFloor, bool addStair) { + + K::GnuplotSplotElementColorPoints* points = new K::GnuplotSplotElementColorPoints(); + for (const T& n : grid) { + if (n.getType() == GridNode::TYPE_STAIR && !addStair) {continue;} + if (n.getType() == GridNode::TYPE_FLOOR && !addFloor) {continue;} + + K::GnuplotPoint3 p(n.x_cm, n.y_cm, n.z_cm); + points->add(p, n.imp); + } + return points; + + } + + template static K::GnuplotSplotElementLines* getEdges(Grid& grid, bool addFloor, bool addStair) { + + std::set filter; + K::GnuplotSplotElementLines* lines = new K::GnuplotSplotElementLines(); + for (const T& n1 : grid) { + if (n1.getType() == GridNode::TYPE_STAIR && !addStair) {continue;} + if (n1.getType() == GridNode::TYPE_FLOOR && !addFloor) {continue;} + + for (const T& n2 : grid.neighbors(n1)) { + + const uint64_t i1 = std::min(n1.getIdx(), n2.getIdx()); + const uint64_t i2 = std::max(n1.getIdx(), n2.getIdx()); + const uint64_t idx = i2 << 32 | i1; + + if (filter.find(idx) == filter.end()) { + + filter.insert(idx); + K::GnuplotPoint3 p1(n1.x_cm, n1.y_cm, n1.z_cm); + K::GnuplotPoint3 p2(n2.x_cm, n2.y_cm, n2.z_cm); + lines->addSegment(p1, p2); + + } + + } + + } + return lines; + + } + +}; + +#endif // GNUPLOTEXPORT_H diff --git a/params/ElementParamWidget.cpp b/params/ElementParamWidget.cpp index ae9a01e..2d2a329 100644 --- a/params/ElementParamWidget.cpp +++ b/params/ElementParamWidget.cpp @@ -38,7 +38,6 @@ QComboBox* getObstacleTypes() { QComboBox* cmb = new QComboBox(); for (int i = 0; i < (int)ObstacleType::_END; ++i) { switch ((ObstacleType)i) { - case ObstacleType::DOOR: cmb->addItem("Door", i); break; case ObstacleType::UNKNOWN: cmb->addItem("Unknown ", i); break; case ObstacleType::WALL: cmb->addItem("Wall", i); break; case ObstacleType::WINDOW: cmb->addItem("Window", i); break; @@ -64,6 +63,7 @@ QComboBox* getOutlineMethods() { } + ElementParamWidget::ElementParamWidget(QWidget *parent) : QGroupBox(parent) { this->lay = new QGridLayout(this); @@ -81,6 +81,13 @@ ElementParamWidget::ElementParamWidget(QWidget *parent) : QGroupBox(parent) { void ElementParamWidget::setElement(MapModelElement* el) { + this->curElement = el; + refresh(); + +} + +void ElementParamWidget::refresh() { + while ( QWidget* w = this->findChild() ) {delete w;} delete this->layout(); @@ -88,20 +95,7 @@ void ElementParamWidget::setElement(MapModelElement* el) { 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("?"); -// } + MapModelElement* el = this->curElement; // material? -> select in combo-box { @@ -157,18 +151,39 @@ void ElementParamWidget::setElement(MapModelElement* el) { const Param param = elem->getParamDesc(i); const ParamValue value = elem->getParamValue(i); + // skip Not-Available entries + if (param.type == ParamType::NOT_AVAILABLE) {continue;} + lay->addWidget(new QLabel(param.name.c_str()),r,0); switch(param.type) { + case ParamType::BOOL: { + QCheckBox* chk = new QCheckBox( ); + chk->setChecked(value.toBool()); + if (param.readOnly) { + chk->setEnabled(false); + } else { + connect(chk, &QCheckBox::clicked, [i,elem] (const bool checked) { + elem->setParamValue(i, ParamValue(checked)); + }); + } + lay->addWidget(chk,r,1); + break; + } + 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); + if (param.readOnly) { + lay->addWidget(new QLabel(str.c_str()),r,1); + } else { + QLineEdit* le = new QLineEdit( str.c_str() ); + connect(le, &QLineEdit::textChanged, [i,elem] (const QString& str) { + const float val = str.toFloat(); + elem->setParamValue(i, ParamValue(val)); + }); + lay->addWidget(le,r,1); + } break; } @@ -176,7 +191,7 @@ void ElementParamWidget::setElement(MapModelElement* el) { 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()); + const int val = str.toInt(); elem->setParamValue(i, ParamValue(val)); }); lay->addWidget(le,r,1); @@ -237,6 +252,7 @@ void ElementParamWidget::setElement(MapModelElement* el) { laySub->addWidget(txtZ,0,2); lay->addWidget(subWidget,r,1); auto onChange = [i,elem,txtX,txtY,txtZ] (const QString& str) { + (void) str; elem->setParamValue(i, ParamValue( Point3(txtX->text().toFloat(), txtY->text().toFloat(), txtZ->text().toFloat()) )); }; connect(txtX, &QLineEdit::textChanged, onChange); diff --git a/params/ElementParamWidget.h b/params/ElementParamWidget.h index 43f0065..b31f0b7 100644 --- a/params/ElementParamWidget.h +++ b/params/ElementParamWidget.h @@ -18,7 +18,8 @@ public: explicit ElementParamWidget(QWidget *parent = 0); - + /** refresh the currently selected element */ + void refresh(); private: diff --git a/params/StairBuilder.cpp b/params/StairBuilder.cpp new file mode 100644 index 0000000..0355592 --- /dev/null +++ b/params/StairBuilder.cpp @@ -0,0 +1,6 @@ +#include "StairBuilder.h" + +StairBuilder::StairBuilder(QWidget *parent) : QWidget(parent) +{ + +} diff --git a/params/StairBuilder.h b/params/StairBuilder.h new file mode 100644 index 0000000..862eddc --- /dev/null +++ b/params/StairBuilder.h @@ -0,0 +1,17 @@ +#ifndef STAIRBUILDER_H +#define STAIRBUILDER_H + +#include + +class StairBuilder : public QWidget +{ + Q_OBJECT +public: + explicit StairBuilder(QWidget *parent = 0); + +signals: + +public slots: +}; + +#endif // STAIRBUILDER_H \ No newline at end of file diff --git a/params/ToolBox.cpp b/params/ToolBox.cpp index 64d233d..a9c3e33 100644 --- a/params/ToolBox.cpp +++ b/params/ToolBox.cpp @@ -40,6 +40,17 @@ ToolBoxWidget::ToolBoxWidget(MapView2D* view, QWidget *parent) : QWidget(parent) lay->addWidget(btnPillar, r++, 0, 1,1,Qt::AlignTop); connect(btnPillar, SIGNAL(clicked(bool)), this, SLOT(onNewPillar())); + btnDoor = new QPushButton(UIHelper::getIcon("door"), ""); + btnDoor->setMinimumSize(s,s); + lay->addWidget(btnDoor, r++, 0, 1,1,Qt::AlignTop); + connect(btnDoor, SIGNAL(clicked(bool)), this, SLOT(onNewDoor())); + + btnStair = new QPushButton(UIHelper::getIcon("stair"), ""); + btnStair->setMinimumSize(s,s); + lay->addWidget(btnStair, r++, 0, 1,1,Qt::AlignTop); + connect(btnStair, SIGNAL(clicked(bool)), this, SLOT(onNewStair())); + + // TRANSMITTERS btnWifi = new QPushButton(UIHelper::getIcon("wifi"), ""); btnWifi->setMinimumSize(s,s); @@ -79,6 +90,9 @@ void ToolBoxWidget::setSelectedLayer(MapLayer *ml) { btnWall->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_OBSTACLES)); btnPillar->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_OBSTACLES)); + btnDoor->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_OBSTACLES)); + + btnStair->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_STAIRS)); btnWifi->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_ACCESS_POINTS)); btnBeacon->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_BEACONS)); @@ -133,7 +147,6 @@ void ToolBoxWidget::onNewPillar() { float s = view->getScaler().sm(50); Floorplan::FloorObstacleCircle* pillar = new Floorplan::FloorObstacleCircle( - Floorplan::ObstacleType::PILLAR, Floorplan::Material::DRYWALL, Point2(center.x-s, center.y), s @@ -146,6 +159,43 @@ void ToolBoxWidget::onNewPillar() { } +void ToolBoxWidget::onNewDoor() { + + const Point2 center = view->getScaler().getCenter(); + float s = view->getScaler().sm(50); + + Floorplan::FloorObstacleDoor* door = new Floorplan::FloorObstacleDoor( + Floorplan::DoorType::SWING, + Floorplan::Material::WOOD, + Point2(center.x-s, center.y), + Point2(center.x+s, center.y) + ); + + MMFloorObstacles* obs = (MMFloorObstacles*)curLayer; + obs->createDoor(door); + + view->getModel()->reselect(); + +} + +void ToolBoxWidget::onNewStair() { + + const Point2 center = view->getScaler().getCenter(); + +// Floorplan::Stair* stair = new Floorplan::StairNormal(center, 0, 0, 3, 2, 6); +// stair->center = center; + + Floorplan::StairFreeform* stair = new Floorplan::StairFreeform(); + Floorplan::StairPart part(Point3(center.x-3, center.y, 0), Point3(center.x+3, center.y, 3), 3); + stair->parts.push_back( part ); + + MMFloorStairs* stairs = (MMFloorStairs*)curLayer; + stairs->create(stair); + + view->getModel()->reselect(); + +} + void ToolBoxWidget::onNewAccessPoint() { const Point2 center = view->getScaler().getCenter(); diff --git a/params/ToolBoxWidget.h b/params/ToolBoxWidget.h index 0402c66..56f0ab6 100644 --- a/params/ToolBoxWidget.h +++ b/params/ToolBoxWidget.h @@ -31,6 +31,9 @@ private: QPushButton* btnGround; QPushButton* btnWall; QPushButton* btnPillar; + QPushButton* btnDoor; + QPushButton* btnStair; + QPushButton* btnWifi; QPushButton* btnBeacon; @@ -43,6 +46,8 @@ private slots: void onNewGround(); void onNewWall(); void onNewPillar(); + void onNewDoor(); + void onNewStair(); void onNewAccessPoint(); void onNewBeacon(); diff --git a/res.qrc b/res.qrc index 4106595..705e61a 100644 --- a/res.qrc +++ b/res.qrc @@ -10,5 +10,7 @@ res/icons/image.svg res/icons/cross.png res/icons/poi.svg + res/icons/stair.svg + res/icons/door.svg diff --git a/res/icons/beacon.svg b/res/icons/beacon.svg index a21519d..316d3ce 100644 --- a/res/icons/beacon.svg +++ b/res/icons/beacon.svg @@ -1,770 +1,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - Wireless sensor - 2011-07-29T10:27:30 - A wireless sensor clipart influenced by Tango project guidelines - https://openclipart.org/detail/152365/wireless-sensor-by-b.gaultier - - - b.gaultier - - - - - electronic - sensor - tango - wireless - wireless sensor - - - - - - - - - - - + + + \ No newline at end of file diff --git a/res/icons/door.svg b/res/icons/door.svg new file mode 100644 index 0000000..49a55ca --- /dev/null +++ b/res/icons/door.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/floor.svg b/res/icons/floor.svg index 9d3e9c5..572eb99 100644 --- a/res/icons/floor.svg +++ b/res/icons/floor.svg @@ -1,1070 +1,69 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - isometric floor tile 4 - 2006-12-26T00:00:00 - - https://openclipart.org/detail/20739/-by--20739 - - - rg1024 - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/image.svg b/res/icons/image.svg index ad64f3a..22ebdc9 100644 --- a/res/icons/image.svg +++ b/res/icons/image.svg @@ -1,81 +1,46 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMAGE - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/pillar.svg b/res/icons/pillar.svg index 8cc95b7..d5a4619 100644 --- a/res/icons/pillar.svg +++ b/res/icons/pillar.svg @@ -1,753 +1,62 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - Antique pillar - 2013-05-08T10:46:42 - antique, pillar - https://openclipart.org/detail/177883/antique-pillar-by-lfidnl-177883 - - - LFIdnl - - - - - antique - pillar - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/poi.svg b/res/icons/poi.svg index 3d388e6..de4c392 100644 --- a/res/icons/poi.svg +++ b/res/icons/poi.svg @@ -1,189 +1,44 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/stair.svg b/res/icons/stair.svg new file mode 100644 index 0000000..6968d28 --- /dev/null +++ b/res/icons/stair.svg @@ -0,0 +1,167 @@ + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + aiga stairs up + 2009-04-19T07:19:44 + Set of international airport symbols. \n \nSource: http://www.aiga.org/content.cfm/symbol-signs \n \nConverted to SVG by Jean-Victor Balin. + https://openclipart.org/detail/25072/aiga-stairs-up-by-anonymous-25072 + + + Anonymous + + + + + aiga + aiga no bg + arrow + black and white + externalsource + icon + map symbol + sign + silhouette + stairs + symbol + + + + + + + + + + + diff --git a/res/icons/wall.svg b/res/icons/wall.svg index 31a41fb..3199293 100644 --- a/res/icons/wall.svg +++ b/res/icons/wall.svg @@ -1,117 +1,52 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - wall 2 - 2013-01-21T11:03:01 - - https://openclipart.org/detail/174369/wall-2-by-jarda-174369 - - - jarda - - - - - brick - bricks - wall - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/wifi.svg b/res/icons/wifi.svg index 079a0c9..2588cb2 100644 --- a/res/icons/wifi.svg +++ b/res/icons/wifi.svg @@ -1,126 +1,56 @@ - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +