From 13a89df8d672a0e1a05cd1bbf3173de9cef5727d Mon Sep 17 00:00:00 2001 From: kazu Date: Tue, 24 May 2016 16:55:19 +0200 Subject: [PATCH] initial commit --- IndoorMap.pro | 93 ++ MainController.cpp | 103 ++ MainController.h | 46 + MainWindow.cpp | 52 + MainWindow.h | 42 + MainWindow.ui | 45 + UIHelper.h | 32 + main.cpp | 16 + mapview/MapView2D.cpp | 90 ++ mapview/MapView2D.h | 76 ++ mapview/Painter.h | 102 ++ mapview/Scaler.h | 104 ++ mapview/elements/MV2DElement.h | 74 ++ mapview/elements/MV2DElementAccessPoint.h | 97 ++ mapview/elements/MV2DElementBeacon.h | 94 ++ .../elements/MV2DElementFloorObstacleCircle.h | 108 ++ .../elements/MV2DElementFloorObstacleLine.h | 108 ++ .../elements/MV2DElementFloorOutlinePolygon.h | 152 +++ mapview/elements/MV2DElementFloorUnderlay.h | 136 +++ mapview/elements/MapViewElementHelper.h | 74 ++ mapview/model/IHasAttributes.h | 21 + mapview/model/IHasFile.h | 17 + mapview/model/IHasMAC.h | 15 + mapview/model/IHasMaterial.h | 15 + mapview/model/IHasName.h | 15 + mapview/model/IHasObstacleType.h | 16 + mapview/model/IHasParams.h | 84 ++ mapview/model/IHasPosition3D.h | 19 + mapview/model/MMFloor.h | 56 + mapview/model/MMFloorAccessPoint.h | 42 + mapview/model/MMFloorAccessPoints.h | 45 + mapview/model/MMFloorBeacon.h | 41 + mapview/model/MMFloorBeacons.h | 44 + mapview/model/MMFloorObstacleCircle.h | 46 + mapview/model/MMFloorObstacleLine.h | 45 + mapview/model/MMFloorObstacles.h | 63 + mapview/model/MMFloorOutline.h | 52 + mapview/model/MMFloorOutlinePolygon.h | 53 + mapview/model/MMFloorUnderlay.h | 52 + mapview/model/MMFloorUnderlayImage.h | 78 ++ mapview/model/MMFloors.h | 37 + mapview/model/MMRoot.h | 38 + mapview/model/MapLayer.h | 127 ++ mapview/model/MapLayers.h | 72 ++ mapview/model/MapModel.cpp | 0 mapview/model/MapModel.h | 112 ++ mapview/model/MapModelElement.h | 34 + mapview/tools/Tool.cpp | 0 mapview/tools/Tool.h | 35 + mapview/tools/ToolMapGrid.h | 71 ++ mapview/tools/ToolMapZoom.h | 28 + mapview/tools/ToolMoveMap.h | 49 + mapview/tools/ToolRuler.h | 140 +++ mapview/tools/ToolSelector.cpp | 0 mapview/tools/ToolSelector.h | 163 +++ mapview/tools/Tools.h | 59 + params/ActionWidget.cpp | 23 + params/ActionWidget.h | 24 + params/ElementParamWidget.cpp | 226 ++++ params/ElementParamWidget.h | 76 ++ params/LayerParamWidget.cpp | 106 ++ params/LayerParamWidget.h | 61 + params/ToolBox.cpp | 174 +++ params/ToolBoxWidget.h | 53 + res.qrc | 13 + res/icons/beacon.svg | 770 ++++++++++++ res/icons/cross.png | Bin 0 -> 2415 bytes res/icons/floor.svg | 1070 +++++++++++++++++ res/icons/image.svg | 81 ++ res/icons/pillar.svg | 753 ++++++++++++ res/icons/polygon.svg | 233 ++++ res/icons/polygon16.png | Bin 0 -> 659 bytes res/icons/polygon32.png | Bin 0 -> 1331 bytes res/icons/wall.svg | 117 ++ res/icons/wifi.svg | 126 ++ tree/MapTreeModel.cpp | 0 tree/MapTreeModel.h | 120 ++ 77 files changed, 7454 insertions(+) create mode 100644 IndoorMap.pro create mode 100644 MainController.cpp create mode 100644 MainController.h create mode 100644 MainWindow.cpp create mode 100644 MainWindow.h create mode 100644 MainWindow.ui create mode 100644 UIHelper.h create mode 100644 main.cpp create mode 100644 mapview/MapView2D.cpp create mode 100644 mapview/MapView2D.h create mode 100644 mapview/Painter.h create mode 100644 mapview/Scaler.h create mode 100644 mapview/elements/MV2DElement.h create mode 100644 mapview/elements/MV2DElementAccessPoint.h create mode 100644 mapview/elements/MV2DElementBeacon.h create mode 100644 mapview/elements/MV2DElementFloorObstacleCircle.h create mode 100644 mapview/elements/MV2DElementFloorObstacleLine.h create mode 100644 mapview/elements/MV2DElementFloorOutlinePolygon.h create mode 100644 mapview/elements/MV2DElementFloorUnderlay.h create mode 100644 mapview/elements/MapViewElementHelper.h create mode 100644 mapview/model/IHasAttributes.h create mode 100644 mapview/model/IHasFile.h create mode 100644 mapview/model/IHasMAC.h create mode 100644 mapview/model/IHasMaterial.h create mode 100644 mapview/model/IHasName.h create mode 100644 mapview/model/IHasObstacleType.h create mode 100644 mapview/model/IHasParams.h create mode 100644 mapview/model/IHasPosition3D.h create mode 100644 mapview/model/MMFloor.h create mode 100644 mapview/model/MMFloorAccessPoint.h create mode 100644 mapview/model/MMFloorAccessPoints.h create mode 100644 mapview/model/MMFloorBeacon.h create mode 100644 mapview/model/MMFloorBeacons.h create mode 100644 mapview/model/MMFloorObstacleCircle.h create mode 100644 mapview/model/MMFloorObstacleLine.h create mode 100644 mapview/model/MMFloorObstacles.h create mode 100644 mapview/model/MMFloorOutline.h create mode 100644 mapview/model/MMFloorOutlinePolygon.h create mode 100644 mapview/model/MMFloorUnderlay.h create mode 100644 mapview/model/MMFloorUnderlayImage.h create mode 100644 mapview/model/MMFloors.h create mode 100644 mapview/model/MMRoot.h create mode 100644 mapview/model/MapLayer.h create mode 100644 mapview/model/MapLayers.h create mode 100644 mapview/model/MapModel.cpp create mode 100644 mapview/model/MapModel.h create mode 100644 mapview/model/MapModelElement.h create mode 100644 mapview/tools/Tool.cpp create mode 100644 mapview/tools/Tool.h create mode 100644 mapview/tools/ToolMapGrid.h create mode 100644 mapview/tools/ToolMapZoom.h create mode 100644 mapview/tools/ToolMoveMap.h create mode 100644 mapview/tools/ToolRuler.h create mode 100644 mapview/tools/ToolSelector.cpp create mode 100644 mapview/tools/ToolSelector.h create mode 100644 mapview/tools/Tools.h create mode 100644 params/ActionWidget.cpp create mode 100644 params/ActionWidget.h create mode 100644 params/ElementParamWidget.cpp create mode 100644 params/ElementParamWidget.h create mode 100644 params/LayerParamWidget.cpp create mode 100644 params/LayerParamWidget.h create mode 100644 params/ToolBox.cpp create mode 100644 params/ToolBoxWidget.h create mode 100644 res.qrc create mode 100644 res/icons/beacon.svg create mode 100644 res/icons/cross.png create mode 100644 res/icons/floor.svg create mode 100644 res/icons/image.svg create mode 100644 res/icons/pillar.svg create mode 100644 res/icons/polygon.svg create mode 100644 res/icons/polygon16.png create mode 100644 res/icons/polygon32.png create mode 100644 res/icons/wall.svg create mode 100644 res/icons/wifi.svg create mode 100644 tree/MapTreeModel.cpp create mode 100644 tree/MapTreeModel.h diff --git a/IndoorMap.pro b/IndoorMap.pro new file mode 100644 index 0000000..b5e4150 --- /dev/null +++ b/IndoorMap.pro @@ -0,0 +1,93 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-03-27T13:14:56 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets svg + +TARGET = IndoorMap +TEMPLATE = app + +INCLUDEPATH += \ + ../ + +SOURCES += main.cpp\ + MainWindow.cpp \ + MainController.cpp \ + mapview/tools/ToolSelector.cpp \ + mapview/tools/Tool.cpp \ + mapview/MapView2D.cpp \ + params/ElementParamWidget.cpp \ + params/LayerParamWidget.cpp \ + params/ActionWidget.cpp \ + params/ToolBox.cpp \ + mapview/model/MapModel.cpp \ + tree/MapTreeModel.cpp + +HEADERS += MainWindow.h \ + mapview/Painter.h \ + mapview/Scaler.h \ + mapview/tools/ToolSelector.h \ + mapview/tools/Tool.h \ + mapview/model/MapLayer.h \ + mapview/model/MapModel.h \ + mapview/tools/ToolMoveMap.h \ + mapview/tools/Tools.h \ + mapview/tools/ToolRuler.h \ + mapview/tools/ToolMapZoom.h \ + tree/MapTreeModel.h \ + MainController.h \ + mapview/tools/ToolMapGrid.h \ + mapview/model/MapLayers.h \ + mapview/MapView2D.h \ + params/ElementParamWidget.h \ + params/LayerParamWidget.h \ + params/ActionWidget.h \ + params/ToolBoxWidget.h \ + UIHelper.h \ + mapview/model/MapModelElement.h \ + mapview/elements/MapViewElementHelper.h \ + mapview/model/IHasAttributes.h \ + mapview/model/IHasMAC.h \ + mapview/model/IHasMaterial.h \ + mapview/model/IHasName.h \ + mapview/model/IHasObstacleType.h \ + mapview/model/IHasPosition3D.h \ + mapview/elements/MV2DElementFloorObstacleLine.h \ + mapview/elements/MV2DElement.h \ + mapview/elements/MV2DElementFloorOutlinePolygon.h \ + mapview/elements/MV2DElementBeacon.h \ + mapview/elements/MV2DElementAccessPoint.h \ + mapview/elements/MV2DElementFloorObstacleCircle.h \ + mapview/model/MMFloorObstacleCircle.h \ + mapview/model/MMFloorObstacleLine.h \ + mapview/model/MMFloorOutlinePolygon.h \ + mapview/model/MMFloor.h \ + mapview/model/MMFloors.h \ + mapview/model/MMFloorObstacles.h \ + mapview/model/MMFloorOutline.h \ + mapview/model/MMRoot.h \ + mapview/model/MMFloorAccessPoint.h \ + mapview/model/MMFloorBeacon.h \ + mapview/model/MMFloorAccessPoints.h \ + mapview/model/MMFloorBeacons.h \ + mapview/model/MMFloorUnderlay.h \ + mapview/model/IHasFile.h \ + mapview/elements/MV2DElementFloorUnderlay.h \ + mapview/model/MMFloorUnderlayImage.h \ + mapview/model/IHasParams.h + +FORMS += MainWindow.ui + +SOURCES += \ + /apps/android/workspace/Indoor/lib/tinyxml/tinyxml2.cpp + +RESOURCES += \ + res.qrc + +DISTFILES += \ + res/icons/polygon.svg + diff --git a/MainController.cpp b/MainController.cpp new file mode 100644 index 0000000..aae1609 --- /dev/null +++ b/MainController.cpp @@ -0,0 +1,103 @@ +#include "MainController.h" + +#include "MainWindow.h" +#include "mapview/model/MapModel.h" +#include "mapview/model/MapModelElement.h" + +#include "mapview/tools/ToolSelector.h" +#include "mapview/tools/ToolMoveMap.h" +#include "mapview/tools/ToolMapZoom.h" +#include "mapview/tools/ToolRuler.h" +#include "mapview/tools/ToolMapGrid.h" + +#include "params/ElementParamWidget.h" +#include "params/LayerParamWidget.h" +#include "params/ToolBoxWidget.h" +#include "params/ActionWidget.h" + +#include "tree/MapTreeModel.h" + +#include +#include + +MainController::MainController() { + + mw = new MainWindow(); + mw->resize(1000, 700); + + MapView2D* mapView2D = mw->getMapView2D(); + QTreeView* layerTree = mw->getTree(); + + mapModel = new MapModel(); + //mapModel->load("/apps/android/workspace/IndoorApp/res/map.xml"); + mapView2D->setModel(mapModel); + + ToolMoveMap* moveMap = new ToolMoveMap(); + ToolRuler* ruler = new ToolRuler(); + ToolMapZoom* mapZoom = new ToolMapZoom(); + ToolMapGrid* mapGrid = new ToolMapGrid(); + ToolSelector* mapSelector = new ToolSelector(); + + mapView2D->getTools().enable(mapGrid); + mapView2D->getTools().enable(moveMap); + mapView2D->getTools().enable(mapZoom); + mapView2D->getTools().enable(mapSelector); + mapView2D->getTools().enable(ruler); + + + mapTreeModel = new MapTreeModel(mapModel); + layerTree->setModel(mapTreeModel); + + connect(layerTree, SIGNAL(clicked(QModelIndex)), this, SLOT(layerSelected(QModelIndex))); + connect(mapSelector, SIGNAL(onMapElementSelected(MapModelElement*)), this, SLOT(mapElementSelected(MapModelElement*))); + connect(mw->getActionWidget(), SIGNAL(onLoad()), this, SLOT(onLoad())); + connect(mw->getActionWidget(), SIGNAL(onSave()), this, SLOT(onSave())); + connect(mapModel, SIGNAL(aboutToReset()), this, SLOT(onMapModelAboutToReset())); + connect(mapModel, SIGNAL(reset()), this, SLOT(onMapModelReset())); + + mapModel->load("/apps/map9.xml"); + +} + +void MainController::layerSelected(QModelIndex idx) { + mw->getMapView2D()->layerChange(); + MapLayer* ml = static_cast(idx.internalPointer()); + mapModel->setSelectedLayer(ml); + mw->getMapView2D()->layerChange(); + mw->getLayerParamWidget()->setElement(ml); + mw->getToolBoxWidget()->setSelectedLayer(ml); + +} + +void MainController::mapElementSelected(MapModelElement* el) { + mw->getElementParamWidget()->setElement(el); +} + +void MainController::onMapModelAboutToReset() { + mw->getLayerParamWidget()->setElement(nullptr); + mw->getToolBoxWidget()->setSelectedLayer(nullptr); + mw->getMapView2D()->update(); +} + +void MainController::onMapModelReset() { + mw->getTree()->expandAll(); +} + +void MainController::onLoad() { + + QString file = QFileDialog::getOpenFileName(mw, "open a map"); + if (file != "") { + mapModel->load(file.toStdString()); + } +} + +void MainController::onSave() { + + QString file = QFileDialog::getSaveFileName(mw, "save the map"); + if (file != "") { + mapModel->save(file.toStdString()); + } + +} + + diff --git a/MainController.h b/MainController.h new file mode 100644 index 0000000..ae138cb --- /dev/null +++ b/MainController.h @@ -0,0 +1,46 @@ +#ifndef MAINCONTROLLER_H +#define MAINCONTROLLER_H + +#include +#include +#include "MainWindow.h" + +class MapTreeModel; +class MapModelElement; +class MapModel; + +class MainController : public QObject { + Q_OBJECT + +public: + explicit MainController(); + + void show() {mw->show();} + +signals: + +public slots: + + /** MapLayer selection changed */ + void layerSelected(QModelIndex idx); + + /** MapElement selection has changed */ + void mapElementSelected(MapModelElement* el); + + void onLoad(); + + void onSave(); + + void onMapModelAboutToReset(); + void onMapModelReset(); + + +private: + + MainWindow* mw; + MapTreeModel* mapTreeModel; + MapModel* mapModel; + +}; + +#endif // MAINCONTROLLER_H diff --git a/MainWindow.cpp b/MainWindow.cpp new file mode 100644 index 0000000..a1a6bf6 --- /dev/null +++ b/MainWindow.cpp @@ -0,0 +1,52 @@ +#include "MainWindow.h" +#include "ui_MainWindow.h" + +#include + +#include "mapview/MapView2D.h" + +#include "mapview/model/MMFloorObstacleLine.h" + +#include + +#include "mapview/tools/ToolMoveMap.h" +#include "mapview/tools/ToolRuler.h" +#include "mapview/tools/ToolMapZoom.h" + +#include "params/ElementParamWidget.h" +#include "params/LayerParamWidget.h" +#include "params/ActionWidget.h" +#include "params/ToolBoxWidget.h" + +#include "tree/MapTreeModel.h" + + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) { + ui->setupUi(this); + + mapView2D = new MapView2D(); + elementParamWidget = new ElementParamWidget(); + layerParamWidget = new LayerParamWidget(); + actionWidget = new ActionWidget(); + toolBoxWidget = new ToolBoxWidget(mapView2D); + + ui->layButtons->addWidget(toolBoxWidget); + + ui->layMap->addWidget(mapView2D); + + ui->layTree->addWidget(layerParamWidget); + ui->layTree->addWidget(elementParamWidget); + ui->layTree->addWidget(actionWidget); + + +} + +MainWindow::~MainWindow() { + delete ui; +} + +QTreeView* MainWindow::getTree() { + return ui->layerTree; +} diff --git a/MainWindow.h b/MainWindow.h new file mode 100644 index 0000000..69fc227 --- /dev/null +++ b/MainWindow.h @@ -0,0 +1,42 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +class MapView2D; +class QTreeView; +class ElementParamWidget; +class LayerParamWidget; +class ActionWidget; +class ToolBoxWidget; + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow { + + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + MapView2D* getMapView2D() {return mapView2D;} + ElementParamWidget* getElementParamWidget() {return elementParamWidget;} + LayerParamWidget* getLayerParamWidget() {return layerParamWidget;} + ToolBoxWidget* getToolBoxWidget() {return toolBoxWidget;} + ActionWidget* getActionWidget() {return actionWidget;} + + QTreeView* getTree(); + +private: + Ui::MainWindow *ui; + MapView2D* mapView2D; + ElementParamWidget* elementParamWidget; + LayerParamWidget* layerParamWidget; + ActionWidget* actionWidget; + ToolBoxWidget* toolBoxWidget; + +}; + +#endif // MAINWINDOW_H diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..37acab0 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,45 @@ + + + MainWindow + + + + 0 + 0 + 777 + 407 + + + + MainWindow + + + + + + + + + + + + + + + + 200 + 16777215 + + + + + + + + + + + + + + diff --git a/UIHelper.h b/UIHelper.h new file mode 100644 index 0000000..3bbcb7c --- /dev/null +++ b/UIHelper.h @@ -0,0 +1,32 @@ +#ifndef UIHELPER_H +#define UIHELPER_H + +#include +#include +#include + +class UIHelper { + +public: + +// static QIcon getIcon(const std::string& name) { +// const std::string file = "://res/icons/" + name + "16.png"; +// QPixmap img(file.c_str()); +// 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); + } + +}; + +#endif // UIHELPER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..a2b849b --- /dev/null +++ b/main.cpp @@ -0,0 +1,16 @@ +#include "MainController.h" +#include + + + +int main(int argc, char *argv[]) { + + QApplication a(argc, argv); + + + MainController mc; + mc.show(); + + + return a.exec(); +} diff --git a/mapview/MapView2D.cpp b/mapview/MapView2D.cpp new file mode 100644 index 0000000..ea032c3 --- /dev/null +++ b/mapview/MapView2D.cpp @@ -0,0 +1,90 @@ +#include "MapView2D.h" + +#include +#include +#include +#include + +#include + +#include "model/MapModelElement.h" +#include "model/MapModel.h" + +MapView2D::MapView2D(QWidget* parent) : QOpenGLWidget(parent) { + + // openGL params + QSurfaceFormat format; + format.setDepthBufferSize(24); + format.setStencilBufferSize(8); + format.setSamples(1); +// format.setVersion(3, 2); + format.setProfile(QSurfaceFormat::CoreProfile); + setFormat(format); + + // receive mouse-move even when mouse is not down + setMouseTracking(true); + setFocusPolicy(Qt::StrongFocus); + + // defaults + s.setScale(5); + +} + +void MapView2D::paintGL() { + + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClear(GL_COLOR_BUFFER_BIT); + + QPainter qp(this); + Painter p(s, &qp, width(), height()); + s.setScreenSize(width(), height()); + + // background tools + tools.paintBefore(this, p); + + // render all visible elements + qp.setRenderHint( QPainter::Antialiasing, true ); + for (MapModelElement* el : getModel()->getVisibleElements()) {el->getMV2D()->paint(p);} + qp.setRenderHint( QPainter::Antialiasing, false ); + + // foreground tools + tools.paintAfter(this, p); + +} + +void MapView2D::initializeGL() { + + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + +} + +void MapView2D::resizeGL() { + // TODO ? +} + + +void MapView2D::keyPressEvent(QKeyEvent *e) { + tools.keyPressEvent(this, e); + update(); +} + +void MapView2D::wheelEvent(QWheelEvent* e) { + tools.wheelEvent(this, e); + update(); +} + +void MapView2D::mousePressEvent(QMouseEvent* e) { + tools.mousePressEvent(this, e); + update(); +} + +void MapView2D::mouseMoveEvent(QMouseEvent* e) { + tools.mouseMoveEvent(this, e); + update(); +} + +void MapView2D::mouseReleaseEvent(QMouseEvent* e) { + tools.mouseReleaseEvent(this, e); + update(); +} diff --git a/mapview/MapView2D.h b/mapview/MapView2D.h new file mode 100644 index 0000000..b7e7d83 --- /dev/null +++ b/mapview/MapView2D.h @@ -0,0 +1,76 @@ +#ifndef MAP2D_H +#define MAP2D_H + +#include +#include + +#include "Scaler.h" +class MapModel; +#include "mapview/tools/Tools.h" + + +/** + * view to render (and edit) MapElements + */ +class MapView2D : public QOpenGLWidget { + Q_OBJECT + + +private: + + /** scaling within the map */ + Scaler s; + + /** the currently active tool (if any) */ + Tools tools; + + /** the underlying data-model */ + MapModel* model = nullptr; + + +public: + + /** ctor */ + MapView2D(QWidget* parent = nullptr); + + void layerChange() { + update(); + tools.layerChange(this); + } + +public: + + void paintGL(); + void initializeGL(); + void resizeGL(); + + + Tools& getTools() {return tools;} + + /** get the underlying data-model */ + MapModel* getModel() {return model;} + + /** set the underlying data-model */ + void setModel(MapModel* mdl) { + this->model = mdl; + update(); + } + + /** get the underlying scaling device */ + Scaler& getScaler() {return s;} + + + +protected: + + void mousePressEvent(QMouseEvent* e); + void mouseMoveEvent(QMouseEvent* e); + void mouseReleaseEvent(QMouseEvent* e); + + void wheelEvent(QWheelEvent* e); + + void keyPressEvent(QKeyEvent* e); + +}; + +#endif // MAP2D_H diff --git a/mapview/Painter.h b/mapview/Painter.h new file mode 100644 index 0000000..6801ba9 --- /dev/null +++ b/mapview/Painter.h @@ -0,0 +1,102 @@ +#ifndef PAINTER_H +#define PAINTER_H + +#include + +#include +#include + +#include "Scaler.h" + +class Painter { + +public: + + Scaler& s; + QPainter* p; + int w; + int h; + +public: + + /** ctor */ + Painter(Scaler& s, QPainter* p, int w, int h) : s(s), p(p), w(w), h(h) { + p->setPen(Qt::black); + p->setBrush(Qt::NoBrush); + } + + int width() {return w;} + + int height() {return h;} + + void drawLine(const Point2 p1, const Point2 p2) { + p->drawLine(s.xms(p1.x), s.yms(p1.y), s.xms(p2.x), s.yms(p2.y)); + } + void drawLine(const Point3 p1, const Point3 p2) { + p->drawLine(s.xms(p1.x), s.yms(p1.y), s.xms(p2.x), s.yms(p2.y)); + } + + void drawCircle(const Point3 center) { + int r = 5; + p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r); + } + + void drawCircle(const Point2 center) { + int r = 5; + 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); + } + + void drawLine(const float x1, const float y1, const float x2, const float y2) { + p->drawLine(s.xms(x1), s.yms(y1), s.xms(x2), s.yms(y2)); + } + + 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 drawText(const Point2 pos, const std::string& text) { + p->drawText(s.xms(pos.x), s.xms(pos.y), text.c_str()); + } + + void drawPolygon(const std::vector& points) { + std::vector vec; + for (const Point2 p : points) { + vec.push_back(QPointF(s.xms(p.x), s.yms(p.y))); + } + p->drawPolygon(vec.data(), vec.size()); + } + void drawPolygon(const std::vector& points) { + std::vector vec; + for (const Point3 p : points) { + vec.push_back(QPointF(s.xms(p.x), s.yms(p.y))); + } + p->drawPolygon(vec.data(), vec.size()); + } + + void setBrush(const QBrush& brush) { p->setBrush(brush); } + void setBrush(const Qt::BrushStyle& brush) { p->setBrush(brush); } + + void setPen(const QPen& pen) {p->setPen(pen); } + void setPen(const QColor& pen) {p->setPen(pen); } + void setPen(const Qt::PenStyle& pen) {p->setPen(pen); } + + + + template void setPenBrush(const Pen& pen, const Brush& brush) {setPen(pen); setBrush(brush);} + + const Scaler& getScaler() {return s;} + +private: + + + +}; + +#endif // PAINTER_H diff --git a/mapview/Scaler.h b/mapview/Scaler.h new file mode 100644 index 0000000..86acae6 --- /dev/null +++ b/mapview/Scaler.h @@ -0,0 +1,104 @@ +#ifndef SCALER_H +#define SCALER_H + +#include + +#include + +struct Rect { + float x0; + float y0; + float x1; + float y1; +}; + +/** + * - scale between map- and screen-space. + * - inverts y-axis (for openGL) + */ +class Scaler { + +private: + + int w; + int h; + float _scale = 50; + float _snap = 0.1; + Point3 _offset; + +public: + + /** map->screen (no offset) */ + float ms(const float m) const {return m*_scale;} + /** x map->screen */ + float xms(const float x) const {return w/2+(x+_offset.x)*_scale;} + /** x map->screen */ + float yms(const float y) const {return h/2-(y-_offset.y)*_scale;} + + + /** screen->map (no offset) */ + float sm(const float s) const {return (s/_scale);} + /** x screen->map */ + float xsm(const float x) const {return ((x-w/2)/_scale)-_offset.x;} + /** y screen->map */ + float ysm(const float y) const {return ((h/2-y)/_scale)+_offset.y;} + + void setScale(const float s) {_scale = s;} + float getScale() const {return _scale;} + + void setOffset(Point3 p) {_offset = p;} + Point3 getOffset() const {return _offset;} + void addOffset(int px, int py) {_offset.x += sm(px); _offset.y += sm(py);} + + float getLODstep() const { + float step = 0.01; + if (_scale <= 1000) {step = 0.1;} + if (_scale <= 500) {step = 0.5;} + if (_scale <= 100) {step = 1.0;} + if (_scale <= 50) {step = 5.0;} + if (_scale <= 10) {step = 10.0;} + if (_scale <= 1) {step = 100.0;} + return step; + } + + /** get the currently visible map-region given the provided screen-size */ + Rect getMapVisible(const int screenW, const int screenH) const { + Rect r; + r.x0 = xsm(0); + r.x1 = xsm(screenW); + r.y0 = ysm(screenH); + r.y1 = ysm(0); + return r; + } + + /** get the currently visible map-region given the provided screen-size, SNAPED to the given grid-size */ + Rect getMapVisible(const int screenW, const int screenH, const float mapGridSize) const { + Rect r = getMapVisible(screenW, screenH); + r.x0 = snapFloor(r.x0, mapGridSize); + r.y0 = snapFloor(r.y0, mapGridSize); + r.x1 = snapCeil(r.x1, mapGridSize); + r.y1 = snapCeil(r.y1, mapGridSize); + return r; + } + + //Point2 getOffset() const {return _offset.xy();} + Point2 getCenter() const {return Point2(-_offset.x, _offset.y);} + + void setScreenSize(const int w, const int h) {this->w = w; this->h = h;} + +public: + + 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 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; } + + //float snap(const float v) const { return v; } + +}; + +#endif // SCALER_H + + diff --git a/mapview/elements/MV2DElement.h b/mapview/elements/MV2DElement.h new file mode 100644 index 0000000..264378d --- /dev/null +++ b/mapview/elements/MV2DElement.h @@ -0,0 +1,74 @@ +#ifndef MV2DELEMENT_H +#define MV2DELEMENT_H + +#include + +#include "../MapView2D.h" +#include "../Painter.h" + +#include +#include + +/** + * represents one drawable, selectable, editable, ... + * element shown within the MapView2D + */ +class MV2DElement { + +private: + + bool _focused = false; + +public: + + /** dtor */ + virtual ~MV2DElement() {;} + + + /** get the element's 2D bounding box */ + virtual BBox2 getBoundingBox() const = 0; + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + virtual float getMinDistanceXY(const Point2 p) const = 0; + + /** repaint me */ + virtual void paint(Painter& p) = 0; + + + + /** got focus */ + void focus() { + _focused = true; + onFocus(); + } + + /** lost focus */ + void unfocus() { + _focused = false; + onUnfocus(); + } + + bool hasFocus() {return _focused;} + + + /** mouse pressed at the given point */ + virtual void mousePressed(MapView2D* v, const Point2 p) = 0; + + /** mouse moved to the given point */ + virtual void mouseMove(MapView2D* v, const Point2 p) = 0; + + /** mouse released */ + virtual void mouseReleased(MapView2D* v, const Point2 p) = 0; + + /** key pressed. return true when consumed. */ + virtual bool keyPressEvent(MapView2D* v, QKeyEvent* e) = 0; + +protected: + + virtual void onFocus() = 0; + + virtual void onUnfocus() = 0; + +}; + +#endif // MV2DELEMENT_H diff --git a/mapview/elements/MV2DElementAccessPoint.h b/mapview/elements/MV2DElementAccessPoint.h new file mode 100644 index 0000000..922ca21 --- /dev/null +++ b/mapview/elements/MV2DElementAccessPoint.h @@ -0,0 +1,97 @@ +#ifndef MV2DELEMENTACCESSPOINT_H +#define MV2DELEMENTACCESSPOINT_H + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" +#include + +class MV2DElementAccessPoint : public MV2DElement { + +private: + + bool sel = false; + Floorplan::AccessPoint* ap; + +public: + + /** ctor with the AP to render/edit */ + MV2DElementAccessPoint(Floorplan::AccessPoint* ap) : ap(ap) {;} + + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + BBox2 bbox; + bbox.add(Point2(ap->pos.x, ap->pos.y)); + bbox.grow(Point2(0.1, 0.1)); + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + return p.getDistance(ap->pos.xy()); + } + + /** repaint me */ + void paint(Painter& p) override { + + if (sel) { + p.setPenBrush(Qt::black, CFG::SEL_COLOR); + p.drawCircle(ap->pos.xy()); + } else if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawCircle(ap->pos.xy()); + } else { + p.setPenBrush(Qt::gray, Qt::NoBrush); + p.drawCircle(ap->pos.xy()); + } + + // label + if (p.getScaler().getScale() >= 25) { + p.p->drawText(p.getScaler().xms(ap->pos.x) + 10, p.getScaler().yms(ap->pos.y) + 5, ap->mac.c_str()); + } + + } + + + + /** mouse pressed at the given point */ + virtual void mousePressed(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + + } + + /** mouse moved to the given point */ + virtual void mouseMove(MapView2D* v, const Point2 _p) override { + (void) v; + if (sel) { + const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + ap->pos.x = p.x; + ap->pos.y = p.y; + } + } + + /** mouse released */ + virtual void mouseReleased(MapView2D* v, const Point2 _p) override { + (void) v; + (void) _p; + sel = true; + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + return false; + } + + virtual void onFocus() override { + + } + + virtual void onUnfocus() override { + sel = false; + } + +}; + +#endif // MV2DELEMENTACCESSPOINT_H diff --git a/mapview/elements/MV2DElementBeacon.h b/mapview/elements/MV2DElementBeacon.h new file mode 100644 index 0000000..6d8ef8c --- /dev/null +++ b/mapview/elements/MV2DElementBeacon.h @@ -0,0 +1,94 @@ +#ifndef MV2DELEMENTBEACON_H +#define MV2DELEMENTBEACON_H + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" +#include + +class MV2DElementBeacon : public MV2DElement { + +private: + + bool sel = false; + Floorplan::Beacon* b; + +public: + + /** ctor with the Beacon to render/edit */ + MV2DElementBeacon(Floorplan::Beacon* b) : b(b) {;} + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + BBox2 bbox; + bbox.add(Point2(b->pos.x, b->pos.y)); + bbox.grow(Point2(0.1, 0.1)); + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + return p.getDistance(b->pos.xy()); + } + + /** repaint me */ + void paint(Painter& p) override { + + if (sel) { + p.setPenBrush(Qt::black, CFG::SEL_COLOR); + p.drawCircle(b->pos.xy()); + } else if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawCircle(b->pos.xy()); + } else { + p.setPenBrush(Qt::gray, Qt::NoBrush); + p.drawCircle(b->pos.xy()); + } + + // label + if (p.getScaler().getScale() >= 25) { + p.p->drawText(p.getScaler().xms(b->pos.x) + 10, p.getScaler().yms(b->pos.y) + 5, b->mac.c_str()); + } + + } + + virtual void onFocus() override { + ; + } + + virtual void onUnfocus() override { + sel = false; + } + + /** mouse pressed at the given point */ + virtual void mousePressed(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + } + + /** mouse moved to the given point */ + virtual void mouseMove(MapView2D* v, const Point2 _p) override { + (void) v; + if (sel) { + const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + b->pos.x = p.x; + b->pos.y = p.y; + } + } + + /** mouse released */ + virtual void mouseReleased(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + sel = true; + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + return false; + } + + +}; + +#endif // MV2DELEMENTBEACON_H diff --git a/mapview/elements/MV2DElementFloorObstacleCircle.h b/mapview/elements/MV2DElementFloorObstacleCircle.h new file mode 100644 index 0000000..b228ca0 --- /dev/null +++ b/mapview/elements/MV2DElementFloorObstacleCircle.h @@ -0,0 +1,108 @@ +#ifndef MV2DELEMENTFLOOROBSTACLECIRCLE_H +#define MV2DELEMENTFLOOROBSTACLECIRCLE_H + + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" +#include + +class MV2DElementFloorObstacleCircle : public MV2DElement { + +private: + + int selPoint = -1; + Floorplan::FloorObstacleCircle* c; + +public: + + /** ctor */ + MV2DElementFloorObstacleCircle(Floorplan::FloorObstacleCircle* c) : c(c) {;} + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + BBox2 bbox; + bbox.add(c->center); + bbox.grow(Point2(c->radius, c->radius)); + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + return std::min(p.getDistance(getSelPoints()[0]), p.getDistance(getSelPoints()[1])); + } + + + /** repaint me */ + void paint(Painter& p) override { + + QPen pen = MapElementHelper::getPen(c->material, c->type, hasFocus()); + + p.setPenBrush(pen, Qt::NoBrush); + p.drawCircle(c->center, c->radius); + + //QBrush brush = MapElementHelper::getBru(c->material, c->type, _focused); + + // selected endpoints? + if (hasFocus()) { + p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR); + if (selPoint == 0) {p.drawCircle(getSelPoints()[0]);} + if (selPoint == 1) {p.drawCircle(getSelPoints()[1]);} + } + + // available endpoints + if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawCircle(getSelPoints()[0]); + p.drawCircle(getSelPoints()[1]); + } + + } + + std::vector getSelPoints() const { + return {c->center, (c->center + Point2(c->radius,0))}; + } + + virtual void onFocus() override { + ; + } + + virtual void onUnfocus() override { + selPoint = -1; + } + + /** 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 (selPoint == -1) {return;} + const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + if (selPoint == 0) {c->center = p;} + if (selPoint == 1) {c->radius = p.getDistance(c->center);} + } + + /** mouse released */ + virtual void mouseReleased(MapView2D* v, const Point2 _p) override { + const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX); + const float l1 = _p.getDistance(getSelPoints()[0]); + const float l2 = _p.getDistance(getSelPoints()[1]); + if (l1 < l2 && l1 <= t) {selPoint = 0;} + else if (l2 < l1 && l2 <= t) {selPoint = 1;} + else {selPoint = -1;} + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + + return false; + } + +}; + +#endif // MV2DELEMENTFLOOROBSTACLECIRCLE_H diff --git a/mapview/elements/MV2DElementFloorObstacleLine.h b/mapview/elements/MV2DElementFloorObstacleLine.h new file mode 100644 index 0000000..5f3d48c --- /dev/null +++ b/mapview/elements/MV2DElementFloorObstacleLine.h @@ -0,0 +1,108 @@ +#ifndef MV2DELEMENTFLOOROBSTACLELINE_H +#define MV2DELEMENTFLOOROBSTACLELINE_H + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" +#include + +class MV2DElementFloorObstacleLine : public MV2DElement { + +private: + + int selPoint = -1; + Floorplan::FloorObstacleLine* fo; + +public: + + /** ctor */ + MV2DElementFloorObstacleLine(Floorplan::FloorObstacleLine* 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 (selPoint == 0) {p.drawCircle(fo->from);} + 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); + + // 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 { + selPoint = -1; // clear selection + } + + /** 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 (selPoint == -1) {return;} + const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + 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;} + } + + /** mouse released */ + virtual void mouseReleased(MapView2D* v, const Point2 _p) override { +// (void) v; +// if (selPoint == -1) {return;} +// const Point3 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); +// 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;} + + // 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;} + + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + return false; + } + +}; + +#endif // MV2DELEMENTFLOOROBSTACLELINE_H diff --git a/mapview/elements/MV2DElementFloorOutlinePolygon.h b/mapview/elements/MV2DElementFloorOutlinePolygon.h new file mode 100644 index 0000000..b4aa802 --- /dev/null +++ b/mapview/elements/MV2DElementFloorOutlinePolygon.h @@ -0,0 +1,152 @@ +#ifndef MV2DELEMENTFLOOROUTLINEPOLYGON_H +#define MV2DELEMENTFLOOROUTLINEPOLYGON_H + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" + +#include + +class MV2DElementFloorOutlinePolygon : public MV2DElement { + +private: + + int selPoint = -1; + Floorplan::FloorOutlinePolygon& fo; + +public: + + /** ctor */ + MV2DElementFloorOutlinePolygon(Floorplan::FloorOutlinePolygon& fo) : fo(fo) {;} + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + BBox2 bbox; + for (const Point2 p : fo.poly.points) { bbox.add(p); } + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + float min = 999999; + for (int i = 0; i < (int)fo.poly.points.size()-1; ++i) { + const Point2 p1 = fo.poly.points[i]; + const Point2 p2 = fo.poly.points[i+1]; + const float dst = MapElementHelper::getLineDistanceXY(p1, p2, p); + if (dst < min) {min = dst;} + } + return min; + } + + virtual void onFocus() override { + + } + + virtual void onUnfocus() override { + selPoint = -1; // clear selection + } + + void paint(Painter& p) override { + + QBrush brush; + + // fill-style (depends on the mode) + switch (fo.method) { + case Floorplan::OutlineMethod::ADD: + brush.setStyle(Qt::BrushStyle::SolidPattern); + brush.setColor(QColor(0,0,0,24)); + break; + case Floorplan::OutlineMethod::REMOVE: + brush.setStyle(Qt::BrushStyle::DiagCrossPattern); + brush.setColor(QColor(0,0,0)); + break; + default: + brush.setStyle(Qt::BrushStyle::SolidPattern); + brush.setColor(QColor(255,0,0)); + } + + // outline + filled area + p.setPenBrush(Qt::black, brush); + p.drawPolygon(fo.poly.points); + + // selected endpoints? + if (hasFocus() && selPoint != -1) { + p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR); + p.drawCircle(fo.poly.points[selPoint]); + } + + // available endpoints + if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + for (const Point2 pt : fo.poly.points) { + p.drawCircle(pt); + } + } + + + } + + virtual void mousePressed(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + } + + 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); + fo.poly.points[selPoint].x = p.x; + fo.poly.points[selPoint].y = p.y; + } + + virtual void mouseReleased(MapView2D* v, const Point2 _p) override { + + (void) v; + + // if (selPoint != -1) { + // const Point3 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M); + // fo.poly.points[selPoint].x = p.x; + // fo.poly.points[selPoint].y = p.y; + // } + + // select a new point on mouse-release (more robust than on mouse-press) + const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX); + auto comp = [&] (const Point2 a, const Point2 b) {return a.getDistance(_p) < b.getDistance(_p);}; + auto it = std::min_element(fo.poly.points.begin(), fo.poly.points.end(), comp); + if (it == fo.poly.points.end()) {selPoint = -1;} // none found -> skip + else if ((*it).getDistance(_p) > t) {selPoint = -1;} // nearest distance is above threshold -> skip + else {selPoint = it - fo.poly.points.begin();} + + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + + + if (e->key() == Qt::Key_Delete) { + + // delete the currently selected vertex? + if (selPoint != -1) { + fo.poly.points.erase(fo.poly.points.begin() + selPoint); + selPoint = -1; + return true; + } + + } else if (e->key() == Qt::Key_Plus && selPoint != -1) { + int idx1 = selPoint; + int idx2 = (selPoint + 1) % fo.poly.points.size(); + int idxNew = idx2; + Point2 pNew = (fo.poly.points[idx1] + fo.poly.points[idx2]) / 2.0f; + fo.poly.points.insert(fo.poly.points.begin() + idxNew, pNew); + selPoint = idxNew; + return true; + } + + // not consumed + return false; + + } + + +}; + +#endif // MV2DELEMENTFLOOROUTLINEPOLYGON_H diff --git a/mapview/elements/MV2DElementFloorUnderlay.h b/mapview/elements/MV2DElementFloorUnderlay.h new file mode 100644 index 0000000..3771089 --- /dev/null +++ b/mapview/elements/MV2DElementFloorUnderlay.h @@ -0,0 +1,136 @@ +#ifndef MV2DELEMENTFLOORUNDERLAY_H +#define MV2DELEMENTFLOORUNDERLAY_H + + +#include "MV2DElement.h" +#include "MapViewElementHelper.h" + +#include + +/** + * display e.g. a PNG-file below the map [as reference] + */ +class MV2DElementFloorUnderlay : public MV2DElement { + +private: + + QImage img; + std::string tmpFile; + Floorplan::UnderlayImage* underlay; + BBox2 bbox; + + int selPoint = -1; + +public: + + /** ctor */ + MV2DElementFloorUnderlay(Floorplan::UnderlayImage* underlay) : underlay(underlay) { + missing(); + } + + void missing() { + //img = QImage(QSize(64, 64), QImage::Format_ARGB32); + //img.fill(0xFF000000); + img = QImage("://res/icons/cross.png"); + } + + /** get the element's 3D bounding box */ + BBox2 getBoundingBox() const override { + return bbox; + } + + /** get the element's minimal distance (nearest whatsoever) to the given point */ + float getMinDistanceXY(const Point2 p) const override { + (void) p; + return CFG::SEL_THRESHOLD_SIZE_PX; // we do not know the distance from the image + } + + virtual void onFocus() override { + + } + + virtual void onUnfocus() override { + selPoint = -1; // clear selection + } + + void paint(Painter& p) override { + (void) p; + + if (tmpFile != underlay->filename) { + img = QImage(underlay->filename.c_str()); + if (img.size().width() == 0) { + missing(); + } + tmpFile = underlay->filename; + } + + + + // map coordinates + float mx1 = underlay->anchor.x; + float my1 = underlay->anchor.y; + float mw = img.width() * underlay->scaleX; + float mh = img.height() * underlay->scaleY; + float mx2 = mx1+mw; + float my2 = my1+mh; + + // screen coordinates + float sx1 = p.s.xms(mx1); + float sy1 = p.s.yms(my1); + float sx2 = p.s.xms(mx2); + float sy2 = p.s.yms(my2); + float sw = abs(sx1-sx2); + float sh = abs(sy1-sy2); + + bbox = BBox2(); + bbox.add(Point2(mx1, my1)); + bbox.add(Point2(mx2, my2)); + + + p.p->drawImage(QRectF(sx1, sy1-sh, sw, sh), img, QRectF(0,0,img.width(),img.height())); + + // selected endpoint(s)? + if (hasFocus()) { + p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR); + if (selPoint == 0) {p.drawCircle(bbox.getMin());} + } + + // just focused? + if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawRect(mx1,my1,mx2,my2); + p.drawCircle(bbox.getMin()); + } + + } + + virtual void mousePressed(MapView2D* v, const Point2 p) override { + (void) v; + (void) p; + } + + 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); + if (selPoint == 0) {underlay->anchor = p;} + } + + 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(bbox.getMin()); + if (l1 <= t) {selPoint = 0;} + else {selPoint = -1;} + } + + virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override { + (void) v; + (void) e; + return false; + } + + +}; + +#endif // MV2DELEMENTFLOORUNDERLAY_H diff --git a/mapview/elements/MapViewElementHelper.h b/mapview/elements/MapViewElementHelper.h new file mode 100644 index 0000000..3776bda --- /dev/null +++ b/mapview/elements/MapViewElementHelper.h @@ -0,0 +1,74 @@ +#ifndef MAPELEMENTHELPER_H +#define MAPELEMENTHELPER_H + +#include +#include +#include + +#include +#include +#include + +#include + +/** configuration */ +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 SEL_COLOR = Qt::blue; +} + +/** + * contains some common helper-methods + */ +class MapElementHelper { + +public: + + /** + * get the minimal distance of dst to the line denoted by (p1, p2). + * this will generate line l perpendicular to (p1, p2) + * move l into dst + * and calculate the cut-point between l and (p1, p2) + */ + static float getLineDistanceXY(Point2 p1, Point2 p2, Point2 dst) { + + // the line (p1, p2) + const Line2 line(p1, p2); + + // 90 degree rotation L of the line (p1, p2) + const Point2 vec = (p1 - p2).perpendicular(); + + // the line L + const Line2 perb(dst-vec*100, dst+vec*100); + + // calculate the cut betwen L and (p1,p2) (if any) + Point2 cut(0,0); + if (line.getSegmentIntersection(perb, cut)) { + + // distance between cut-point and mouse + return cut.getDistance(dst); + + } else { + + // no cut detected + return 9999999; + + } + + } + + static QPen getPen(Floorplan::Material mat, Floorplan::ObstacleType type, bool focus) { + using namespace Floorplan; + QPen pen; pen.setColor(Qt::darkGray); + if (focus) {pen.setColor(Qt::black);} + if (mat == Material::CONCRETE) {pen.setWidth(3);} + if (mat == Material::GLASS) {pen.setStyle(Qt::PenStyle::DotLine);} + if (type == ObstacleType::HANDRAIL) {pen.setStyle(Qt::PenStyle::DashLine);} + return pen; + } + + +}; + +#endif // MAPELEMENTHELPER_H diff --git a/mapview/model/IHasAttributes.h b/mapview/model/IHasAttributes.h new file mode 100644 index 0000000..a0ee7e8 --- /dev/null +++ b/mapview/model/IHasAttributes.h @@ -0,0 +1,21 @@ +#ifndef IHASATTRIBUTES_H +#define IHASATTRIBUTES_H + +#include + +class IHasAttributes { + +public: + + /** set the value for the given key */ + virtual void setAttribute(const std::string& key, const std::string& val) = 0; + + /** get the value for the given key */ + virtual const std::string& getAttribute(const std::string& key) const = 0; + + /** get all attributes as map */ + virtual const std::unordered_map getAttributes() const = 0; + +}; + +#endif // IHASATTRIBUTES_H diff --git a/mapview/model/IHasFile.h b/mapview/model/IHasFile.h new file mode 100644 index 0000000..b3cfbfb --- /dev/null +++ b/mapview/model/IHasFile.h @@ -0,0 +1,17 @@ +#ifndef IHASFILE_H +#define IHASFILE_H + +#include + +class IHasFile { + +public: + + virtual void setFileName(const std::string& file) = 0; + + virtual const std::string& getFileName() const = 0; + +}; + + +#endif // IHASFILE_H diff --git a/mapview/model/IHasMAC.h b/mapview/model/IHasMAC.h new file mode 100644 index 0000000..71d13fb --- /dev/null +++ b/mapview/model/IHasMAC.h @@ -0,0 +1,15 @@ +#ifndef IHASMAC_H +#define IHASMAC_H + +#include + +class IHasMAC { +public: + + virtual void setMAC(const std::string& mac) = 0; + + virtual const std::string& getMAC() const = 0; + +}; + +#endif // IHASMAC_H diff --git a/mapview/model/IHasMaterial.h b/mapview/model/IHasMaterial.h new file mode 100644 index 0000000..ea33829 --- /dev/null +++ b/mapview/model/IHasMaterial.h @@ -0,0 +1,15 @@ +#ifndef IHASMATERIAL_H +#define IHASMATERIAL_H + +#include + +class IHasMaterial { +public: + + virtual void setMaterial(const Floorplan::Material m) = 0; + + virtual Floorplan::Material getMaterial() const = 0; + +}; + +#endif // IHASMATERIAL_H diff --git a/mapview/model/IHasName.h b/mapview/model/IHasName.h new file mode 100644 index 0000000..6188818 --- /dev/null +++ b/mapview/model/IHasName.h @@ -0,0 +1,15 @@ +#ifndef IHASNAME_H +#define IHASNAME_H + +#include + +class IHasName { +public: + + virtual void setName(const std::string& name) = 0; + + virtual const std::string& getName() const = 0; + +}; + +#endif // IHASNAME_H diff --git a/mapview/model/IHasObstacleType.h b/mapview/model/IHasObstacleType.h new file mode 100644 index 0000000..1b10d22 --- /dev/null +++ b/mapview/model/IHasObstacleType.h @@ -0,0 +1,16 @@ +#ifndef IHASOBSTACLETYPE_H +#define IHASOBSTACLETYPE_H + +#include + +class IHasObstacleType { +public: + + virtual void setObstacleType(const Floorplan::ObstacleType t) = 0; + + virtual Floorplan::ObstacleType getObatcleType() const = 0; + +}; + + +#endif // IHASOBSTACLETYPE_H diff --git a/mapview/model/IHasParams.h b/mapview/model/IHasParams.h new file mode 100644 index 0000000..f2cf2ad --- /dev/null +++ b/mapview/model/IHasParams.h @@ -0,0 +1,84 @@ +#ifndef IHASPARAMS_H +#define IHASPARAMS_H + +#include +#include + +enum class ParamType { + INT, + FLOAT, + STRING, + FILE, +}; + +class ParamValue { + +private: + union { + int _int; + float _float; + }; + std::string _str; + +public: + + template ParamValue(const T val) { + setValue(val); + } + + void setValue(const std::string& val) {_str = val;} + void setValue(float val) {_float = val;} + void setValue(int val) {_int = val;} + + std::string toString() const {return _str;} + float toFloat() const {return _float;} + int toInt() const {return _int;} + +}; + + +//union ParamValue { + +// int _int; +// float _float; +// std::string _string; + +// template ParamValue(const T val) { +// setValue(val); +// } + +// int toInt() const {return _int;} +// float toFloat() const {return _float;} +// const std::string& toString() const {return _string;} + +// void setValue(const float val) {_float = val;} +// void setValue(const int val) {_int = val;} +// void setValue(const std::string& val) {_string = val;} + +//}; + +struct Param { + std::string name; + ParamType type; + Param(const std::string& name, const ParamType type) : name(name), type(type) {;} +}; + +/** free parameters */ +class IHasParams { +public: + + /** get the number of parameters */ + virtual int getNumParams() const = 0; + + /** get the description of the idx-th parameter */ + virtual Param getParamDesc(const int idx) const = 0; + + /** get the idx-th param's value */ + virtual ParamValue getParamValue(const int idx) const = 0; + + /** set the idx-th param's value */ + virtual void setParamValue(const int idx, const ParamValue& val) const = 0; + +}; + +#endif // IHASPARAMS_H diff --git a/mapview/model/IHasPosition3D.h b/mapview/model/IHasPosition3D.h new file mode 100644 index 0000000..1e4eaa3 --- /dev/null +++ b/mapview/model/IHasPosition3D.h @@ -0,0 +1,19 @@ +#ifndef IHASPOSITION3D_H +#define IHASPOSITION3D_H + + +#include + +class IHasPosition3D { + +public: + + /** set the element's 3D position */ + virtual void setPosition3D(const Point3& p) = 0; + + /** get the element's 3D position */ + virtual Point3 getPosition3D() const = 0; + +}; + +#endif // IHASPOSITION3D_H diff --git a/mapview/model/MMFloor.h b/mapview/model/MMFloor.h new file mode 100644 index 0000000..a6e5041 --- /dev/null +++ b/mapview/model/MMFloor.h @@ -0,0 +1,56 @@ +#ifndef MMFLOOR_H +#define MMFLOOR_H + +#include "MapLayer.h" +#include "MMFloorOutline.h" +#include "MMFloorObstacles.h" +#include "MMFloorAccessPoints.h" +#include "MMFloorBeacons.h" +#include "MMFloorUnderlay.h" + + +#include + + +/** + * floor-layer containing one floor + * and its outline, obstacles, access-points, ... + */ +class MMFloor : public MapLayer, public IHasName { + +private: + + /** the underlying data-structure */ + Floorplan::Floor* floor; + +public: + + /** ctor. existing floor */ + MMFloor(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR), floor(floor) { + + new MMFloorOutline(this, floor); + new MMFloorObstacles(this, floor); + new MMFloorAccessPoints(this, floor); + new MMFloorBeacons(this, floor); + new MMFloorUnderlay(this, floor); + + } + + /** ctor. new floor. */ + MMFloor(MapLayer* parent) : MapLayer(parent), floor(nullptr) { + throw "not yet implemented"; + } + + /** get the underlying model */ + Floorplan::Floor& getFloor() {return *floor;} + + + std::string getLayerName() const override {return floor->name;} + + + virtual void setName(const std::string& name) {this->floor->name = name;} + virtual const std::string& getName() const {return this->floor->name;} + +}; + +#endif // MMFLOOR_H diff --git a/mapview/model/MMFloorAccessPoint.h b/mapview/model/MMFloorAccessPoint.h new file mode 100644 index 0000000..43a83d1 --- /dev/null +++ b/mapview/model/MMFloorAccessPoint.h @@ -0,0 +1,42 @@ +#ifndef MAPVIEWELEMENTACCESSPOINT_H +#define MAPVIEWELEMENTACCESSPOINT_H + +#include "MapModelElement.h" +#include "IHasMAC.h" +#include "IHasName.h" + +#include "../elements/MV2DElementAccessPoint.h" + +#include + +class MMFloorAccessPoint : public MapModelElement, public IHasMAC, public IHasName { + +private: + + Floorplan::Floor* floor; + Floorplan::AccessPoint* ap; + MV2DElementAccessPoint mv2d; + +public: + + MMFloorAccessPoint(MapLayer* parent, Floorplan::Floor* floor, Floorplan::AccessPoint* ap) : + MapModelElement(parent), floor(floor), ap(ap), mv2d(ap) { + + } + + virtual void setMAC(const std::string& mac) override {ap->mac = mac;} + virtual const std::string& getMAC() const override {return ap->mac;} + + virtual void setName(const std::string& name) override {ap->name = name;} + virtual const std::string& getName() const override {return ap->name;} + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + + void deleteMe() const override { + parent->removeElement(this); + floor->accesspoints.erase(std::remove(floor->accesspoints.begin(), floor->accesspoints.end(), ap), floor->accesspoints.end()); + } + +}; + +#endif // MAPVIEWELEMENTACCESSPOINT_H diff --git a/mapview/model/MMFloorAccessPoints.h b/mapview/model/MMFloorAccessPoints.h new file mode 100644 index 0000000..f9f0fc8 --- /dev/null +++ b/mapview/model/MMFloorAccessPoints.h @@ -0,0 +1,45 @@ +#ifndef MMFLOORACCESSPOINTS_H +#define MMFLOORACCESSPOINTS_H + +#include "MapLayer.h" +#include "MMFloorAccessPoint.h" + +#include + +/** + * layer containing all of the map's floors + */ +class MMFloorAccessPoints : public MapLayer { + +private: + + Floorplan::Floor* floor; + +public: + + /** ctor with the underlying model */ + MMFloorAccessPoints(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_ACCESS_POINTS), floor(floor) { + + // add all APs + for (Floorplan::AccessPoint* ap : floor->accesspoints) { + new MMFloorAccessPoint(this, floor, ap); + } + + } + + std::string getLayerName() const override {return "APs";} + + //TODO: check + void createAP(Floorplan::AccessPoint* ap) { + + // add to underlying model + floor->accesspoints.push_back(ap); + + // add to myself as element + elements.push_back(new MMFloorAccessPoint(this, floor, ap)); + + } + +}; + +#endif // MMFLOORACCESSPOINTS_H diff --git a/mapview/model/MMFloorBeacon.h b/mapview/model/MMFloorBeacon.h new file mode 100644 index 0000000..f63c16f --- /dev/null +++ b/mapview/model/MMFloorBeacon.h @@ -0,0 +1,41 @@ +#ifndef MAPVIEWELEMENTIBEACON_H +#define MAPVIEWELEMENTIBEACON_H + +#include "MapModelElement.h" +#include "IHasMAC.h" +#include "IHasName.h" + +#include "../elements/MV2DElementBeacon.h" + +#include + +class MMFloorBeacon : public MapModelElement, public IHasMAC, public IHasName { + +private: + + Floorplan::Floor* floor; + Floorplan::Beacon* b; + MV2DElementBeacon mv2d; + +public: + + MMFloorBeacon(MapLayer* parent, Floorplan::Floor* floor, Floorplan::Beacon* b) : MapModelElement(parent), floor(floor), b(b), mv2d(b) { + + } + + virtual void setMAC(const std::string& mac) override {b->mac = mac;} + virtual const std::string& getMAC() const override {return b->mac;} + + virtual void setName(const std::string& name) override {b->name = name;} + virtual const std::string& getName() const override {return b->name;} + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + + void deleteMe() const override { + parent->removeElement(this); + floor->beacons.erase(std::remove(floor->beacons.begin(), floor->beacons.end(), b), floor->beacons.end()); + } + +}; + +#endif // MAPVIEWELEMENTIBEACON_H diff --git a/mapview/model/MMFloorBeacons.h b/mapview/model/MMFloorBeacons.h new file mode 100644 index 0000000..a9326b9 --- /dev/null +++ b/mapview/model/MMFloorBeacons.h @@ -0,0 +1,44 @@ +#ifndef MMFLOORBEACONS_H +#define MMFLOORBEACONS_H + +#include "MapLayer.h" +#include "MMFloorBeacon.h" + +#include + +/** + * layer containing all of the floor's beaconss + */ +class MMFloorBeacons : public MapLayer { + +private: + + Floorplan::Floor* floor; + +public: + + /** ctor with the underlying model */ + MMFloorBeacons(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_BEACONS), floor(floor) { + + // add all Beacons + for (Floorplan::Beacon* b : floor->beacons) { + new MMFloorBeacon(this, floor, b); + } + + } + + std::string getLayerName() const override {return "Beacons";} + + //TODO: check + void createBeacon(Floorplan::Beacon* b) { + + // add to underlying model + floor->beacons.push_back(b); + + // add to myself as element + elements.push_back(new MMFloorBeacon(this, floor, b)); + } + +}; + +#endif // MMFLOORBEACONS_H diff --git a/mapview/model/MMFloorObstacleCircle.h b/mapview/model/MMFloorObstacleCircle.h new file mode 100644 index 0000000..653c6f4 --- /dev/null +++ b/mapview/model/MMFloorObstacleCircle.h @@ -0,0 +1,46 @@ +#ifndef MAPMODELELEMENTFLOOROBSTACLECIRCLE_H +#define MAPMODELELEMENTFLOOROBSTACLECIRCLE_H + + +#include "MapModelElement.h" +#include "../elements/MapViewElementHelper.h" + +#include "IHasMaterial.h" +#include "IHasObstacleType.h" + +#include "../elements/MV2DElementFloorObstacleCircle.h" + +#include + + +class MMFloorObstacleCircle : public MapModelElement, public IHasMaterial, public IHasObstacleType { + +private: + + Floorplan::Floor* mf; + Floorplan::FloorObstacleCircle* c; + MV2DElementFloorObstacleCircle mv2d; + +public: + + MMFloorObstacleCircle(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorObstacleCircle* c) : + MapModelElement(parent), mf(mf), c(c), mv2d(c) { + + } + + 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;} + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + + void deleteMe() const override { + parent->removeElement(this); + mf->obstacles.erase(std::remove(mf->obstacles.begin(), mf->obstacles.end(), c), mf->obstacles.end()); + } + +}; + +#endif // MAPMODELELEMENTFLOOROBSTACLECIRCLE_H diff --git a/mapview/model/MMFloorObstacleLine.h b/mapview/model/MMFloorObstacleLine.h new file mode 100644 index 0000000..1701f7d --- /dev/null +++ b/mapview/model/MMFloorObstacleLine.h @@ -0,0 +1,45 @@ +#ifndef MAPELEMENTOBSTACLE_H +#define MAPELEMENTOBSTACLE_H + +#include "MapModelElement.h" +#include "../elements/MapViewElementHelper.h" + +#include "IHasMaterial.h" +#include "IHasObstacleType.h" + +#include "../elements/MV2DElementFloorObstacleLine.h" + +#include + + +class MMFloorObstacleLine : public MapModelElement, public IHasMaterial, public IHasObstacleType { + +public: + + Floorplan::Floor* mf; + Floorplan::FloorObstacleLine* fo; + MV2DElementFloorObstacleLine mv2d; + +public: + + MMFloorObstacleLine(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorObstacleLine* fo) : + MapModelElement(parent), mf(mf), fo(fo), mv2d(fo) { + + } + + void setMaterial(const Floorplan::Material m) override {fo->material = m;} + Floorplan::Material getMaterial() const override {return fo->material;} + + void setObstacleType(const Floorplan::ObstacleType t) override {fo->type = t;} + Floorplan::ObstacleType getObatcleType() const override {return fo->type;} + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + + void deleteMe() const override { + parent->removeElement(this); + mf->obstacles.erase(std::remove(mf->obstacles.begin(), mf->obstacles.end(), fo), mf->obstacles.end()); + } + +}; + +#endif // MAPELEMENTOBSTACLE_H diff --git a/mapview/model/MMFloorObstacles.h b/mapview/model/MMFloorObstacles.h new file mode 100644 index 0000000..5144dc6 --- /dev/null +++ b/mapview/model/MMFloorObstacles.h @@ -0,0 +1,63 @@ +#ifndef MMFLOOROBSTACLES_H +#define MMFLOOROBSTACLES_H + +#include "MapLayer.h" +#include "MMFloorObstacleCircle.h" +#include "MMFloorObstacleLine.h" + +#include + +/** + * layer that contains all of one floor's obstacles + */ +class MMFloorObstacles : public MapLayer { + + Floorplan::Floor* floor; + +public: + + /** ctor with the floor */ + MMFloorObstacles(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_OBSTACLES), floor(floor) { + + // the obstacles + for (Floorplan::FloorObstacle* o : floor->obstacles) { + if (dynamic_cast(o)) { + 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)); + } + } + + } + + /** get the corresponding floor from the underlying model */ + Floorplan::Floor* getFloor() {return floor;} + + //TODO: check + void createLine(Floorplan::FloorObstacleLine* obs) { + + // add to underlying model + floor->obstacles.push_back(obs); + + // add to myself as element + elements.push_back(new MMFloorObstacleLine(this, floor, obs)); + + } + + //TODO: check + void createCircle(Floorplan::FloorObstacleCircle* obs) { + + // add to underlying model + floor->obstacles.push_back(obs); + + // add to myself as element + elements.push_back(new MMFloorObstacleCircle(this, floor, obs)); + + } + + + std::string getLayerName() const override {return "obstacles";} + +}; + +#endif // MMFLOOROBSTACLES_H diff --git a/mapview/model/MMFloorOutline.h b/mapview/model/MMFloorOutline.h new file mode 100644 index 0000000..3ed2305 --- /dev/null +++ b/mapview/model/MMFloorOutline.h @@ -0,0 +1,52 @@ +#ifndef MMFLOOROUTLINE_H +#define MMFLOOROUTLINE_H + +#include "MapLayer.h" +#include "MMFloorOutlinePolygon.h" + + +#include + +/** + * layer containing all elements describing a floor's outline + */ +class MMFloorOutline : public MapLayer { + +private: + + /** the underlying model */ + Floorplan::Floor* floor; + +public: + + /** ctor with the underlying model */ + MMFloorOutline(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_GROUND), floor(floor) { + + // the outline + for (Floorplan::FloorOutlinePolygon* poly : floor->outline) { + elements.push_back(new MMFloorOutlinePolygon(this, floor, poly)); + } + + } + + + /** get the corresponding floor from the underlying model */ + Floorplan::Floor* getFloor() {return floor;} + + //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)); + + } + + + std::string getLayerName() const override {return "outline";} + +}; + +#endif // MMFLOOROUTLINE_H diff --git a/mapview/model/MMFloorOutlinePolygon.h b/mapview/model/MMFloorOutlinePolygon.h new file mode 100644 index 0000000..cd56537 --- /dev/null +++ b/mapview/model/MMFloorOutlinePolygon.h @@ -0,0 +1,53 @@ +#ifndef MAPELEMENTFLOORGROUND_H +#define MAPELEMENTFLOORGROUND_H + +#include "IHasName.h" +#include "MapModelElement.h" + +#include "../elements/MV2DElementFloorOutlinePolygon.h" + +#include + + +/** + * describes one polygon within a floor's outline + */ +class MMFloorOutlinePolygon : public MapModelElement, public IHasName { + +private: + + Floorplan::Floor* mf; + Floorplan::FloorOutlinePolygon* fo; + MV2DElementFloorOutlinePolygon mv2d; + +public: + + /** ctor */ + MMFloorOutlinePolygon(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorOutlinePolygon* fo) : MapModelElement(parent), mf(mf), fo(fo), mv2d(*fo) { + ; + } + + Floorplan::FloorOutlinePolygon* getPolygon() {return fo;} + + Floorplan::OutlineMethod getMethod() const {return fo->method;} + void setMethod(const Floorplan::OutlineMethod m) {this->fo->method = m;} + + void setName(const std::string& name) override {fo->name = name;} + const std::string& getName() const override {return fo->name;} + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + + void deleteMe() const override { + + // delete from the parent + parent->removeElement(this); + + // delete from the underlying model + mf->outline.erase(std::remove(mf->outline.begin(), mf->outline.end(), fo), mf->outline.end()); + + } + +}; + + +#endif // MAPELEMENTFLOORGROUND_H diff --git a/mapview/model/MMFloorUnderlay.h b/mapview/model/MMFloorUnderlay.h new file mode 100644 index 0000000..cfbacea --- /dev/null +++ b/mapview/model/MMFloorUnderlay.h @@ -0,0 +1,52 @@ +#ifndef MMFLOORUNDERLAY_H +#define MMFLOORUNDERLAY_H + +#include "MMFloorUnderlayImage.h" + +#include "../elements/MV2DElementFloorUnderlay.h" + +#include + +/** + * add an external file as underlay (to copy it onto the map) + */ +class MMFloorUnderlay : public MapLayer { + +private: + + Floorplan::Floor* floor; + +public: + + /** ctor */ + MMFloorUnderlay(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_UNDERLAY), floor(floor) { + + // the underlays + for (Floorplan::UnderlayImage* img : floor->underlays) { + elements.push_back(new MMFloorUnderlayImage(this, floor, img)); + } + + } + + //TODO: check + void createImage(const Point2 center) { + + Floorplan::UnderlayImage* elem = new Floorplan::UnderlayImage(); + + // add to underlying model + floor->underlays.push_back(elem); + + // add to myself as element + MMFloorUnderlayImage* img = new MMFloorUnderlayImage(this, floor, elem); + elements.push_back(img); + img->setAnchor(center); + img->setScale(0.1, 0.1); + + } + + virtual std::string getLayerName() const override {return "underlay";} + +}; + + +#endif // MMFLOORUNDERLAY_H diff --git a/mapview/model/MMFloorUnderlayImage.h b/mapview/model/MMFloorUnderlayImage.h new file mode 100644 index 0000000..60c3165 --- /dev/null +++ b/mapview/model/MMFloorUnderlayImage.h @@ -0,0 +1,78 @@ +#ifndef MMFLOORUNDERLAYIMAGE_H +#define MMFLOORUNDERLAYIMAGE_H + + +#include "IHasFile.h" +#include "IHasParams.h" +#include "MapModelElement.h" + +#include "../elements/MV2DElementFloorUnderlay.h" +#include + +/** + * add an external image file as underlay (to copy it onto the map) + */ +class MMFloorUnderlayImage : public MapModelElement, public MapLayer, public IHasFile, public IHasParams { + +private: + + Floorplan::Floor* floor; + Floorplan::UnderlayImage* img; + MV2DElementFloorUnderlay mv2d; + std::string fileName; + +public: + + /** ctor */ + MMFloorUnderlayImage(MapLayer* parent, Floorplan::Floor* floor, Floorplan::UnderlayImage* img) : MapModelElement(parent), MapLayer(parent, MapLayerType::FLOOR_UNDERLAY), floor(floor), img(img), mv2d(img) { + setFileName(img->filename); + } + + virtual void setFileName(const std::string& file) {img->filename = file;} + virtual const std::string& getFileName() const {return img->filename;} + + void setAnchor(const Point2 anchor) {img->anchor = anchor;} + Point2 getAnchor() const {return img->anchor;} + + void setScale(const float x, const float y) {img->scaleX = x; img->scaleY = y;} + + virtual std::string getLayerName() const override {return "underlay";} + + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} + + void deleteMe() const override { + ; + } + + int getNumParams() const override {return 3;} + + virtual Param getParamDesc(const int idx) const override { + switch (idx) { + case 0: return Param("file", ParamType::FILE); + case 1: return Param("scale X", ParamType::FLOAT); + case 2: return Param("scale Y", ParamType::FLOAT); + default: throw 1; + } + } + + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return ParamValue(img->filename); + case 1: return ParamValue(img->scaleX); + case 2: return ParamValue(img->scaleY); + default: throw 1; + } + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch (idx) { + case 0: img->filename = val.toString(); + case 1: img->scaleX = val.toFloat(); + case 2: img->scaleY = val.toFloat(); + default: throw 1; + } + } + +}; + +#endif // MMFLOORUNDERLAYIMAGE_H diff --git a/mapview/model/MMFloors.h b/mapview/model/MMFloors.h new file mode 100644 index 0000000..ec40cad --- /dev/null +++ b/mapview/model/MMFloors.h @@ -0,0 +1,37 @@ +#ifndef MMFLOORS_H +#define MMFLOORS_H + +#include "MapLayer.h" +#include "MMFloor.h" + +#include + +/** + * layer containing all of the map's floors + */ +class MMFloors : public MapLayer { + +private: + + Floorplan::IndoorMap* map; + +public: + + /** ctor with the underlying model */ + MMFloors(MapLayer* parent, Floorplan::IndoorMap* map) : MapLayer(parent, MapLayerType::FLOORS), map(map) { + + // add all floors + for (Floorplan::Floor* floor : map->floors) { + new MMFloor(this, floor); + } + + } + + std::string getLayerName() const override {return "floors";} + + /** get the underlying model */ + Floorplan::IndoorMap* getMap() {return map;} + +}; + +#endif // MMFLOORS_H diff --git a/mapview/model/MMRoot.h b/mapview/model/MMRoot.h new file mode 100644 index 0000000..b79b487 --- /dev/null +++ b/mapview/model/MMRoot.h @@ -0,0 +1,38 @@ +#ifndef MMROOT_H +#define MMROOT_H + +#include "MapLayer.h" +#include "MMFloors.h" + +#include + +/** + * floor-layer containing one floor + * and its outline, obstacles, access-points, ... + */ +class MMRoot : public MapLayer { + +private: + + /** the underlying data-structure */ + Floorplan::IndoorMap* map; + +public: + + /** ctor. existing floor */ + MMRoot(MapLayer* parent, Floorplan::IndoorMap* map) : MapLayer(parent), map(map) { + + // all floors + new MMFloors(this, map); + + } + + /** get the underlying model */ + Floorplan::IndoorMap* getMap() {return map;} + + + std::string getLayerName() const override {return "root";} + +}; + +#endif // MMROOT_H diff --git a/mapview/model/MapLayer.h b/mapview/model/MapLayer.h new file mode 100644 index 0000000..b54ef03 --- /dev/null +++ b/mapview/model/MapLayer.h @@ -0,0 +1,127 @@ +#ifndef MAPLAYER_H +#define MAPLAYER_H + +#include +#include +#include + +class MapModelElement; + + +enum class MapLayerType { + UNKNOWN, + ROOT, + FLOORS, + FLOOR, + FLOOR_GROUND, + FLOOR_OBSTACLES, + FLOOR_BEACONS, + FLOOR_ACCESS_POINTS, + FLOOR_UNDERLAY, +}; + + + +class MapLayer { + +protected: + + /** this layer's parent */ + MapLayer* parent; + + /** this layer's elements */ + std::vector elements; + + /** this layer's sublayers */ + std::vector sublayers; + + /** this layer's type */ + MapLayerType type; + + /** whether this layer is visible */ + bool visible; + +public: + + /** ctor */ + MapLayer(MapLayer* parent) : parent(parent), type(MapLayerType::UNKNOWN), visible(true) { + if (parent) {parent->addSublayer(this);} // attach myself as child of my parent + } + + /** ctor */ + MapLayer(MapLayer* parent, MapLayerType type) : parent(parent), type(type), visible(true) { + if (parent) {parent->addSublayer(this);} // attach myself as child of my parent + } + + /** dtor */ + virtual ~MapLayer() {;} + + /** get the layer's parent */ + MapLayer* getParent() const {return parent;} + + /** get the layer's name */ + virtual std::string getLayerName() const = 0; + + /** get the layer's type */ + MapLayerType getLayerType() const {return type;} + + + /** get all elements within this layer */ + const std::vector& getElements() const {return elements;} + + /** get all elements within this layer */ + size_t getNumElements() const {return elements.size();} + + /** remove the given element from the elements list */ + void removeElement(const MapModelElement* elem) { elements.erase(std::remove(elements.begin(), elements.end(), elem), elements.end()); } + + + /** is this layer currently visible? */ + bool isVisible() const {return visible;} + + /** make this layer visible */ + void setVisible(const bool visible) {this->visible = visible;} + + + /** get all sub-layers within this layer */ + const std::vector& getSubLayers() const {return sublayers;} + + + /** helper method to get all elements and those of all sub-layers */ + void getVisibleElementsRecursive(std::vector& el) { + if (isVisible()) { + std::vector local = getElements(); + el.insert(el.end(), local.begin(), local.end()); + for (MapLayer* sub : getSubLayers()) { + sub->getVisibleElementsRecursive(el); + } + } + } + +protected: + + + + /** add a new sublayer to this layer */ + void addSublayer(MapLayer* ml) { + + // sanity check before adding + if (std::find(sublayers.begin(), sublayers.end(), ml) != sublayers.end()) {throw "layer already present!";} + sublayers.push_back(ml); + + } + +}; + +class MapLayerRoot : public MapLayer { + +public: + + MapLayerRoot(MapLayer* parent) : MapLayer(parent, MapLayerType::ROOT) {;} + + std::string getLayerName() const override {return "root";} + +}; + + +#endif // MAPLAYER_H diff --git a/mapview/model/MapLayers.h b/mapview/model/MapLayers.h new file mode 100644 index 0000000..bfc60f4 --- /dev/null +++ b/mapview/model/MapLayers.h @@ -0,0 +1,72 @@ +#ifndef MAPLAYERFLOOR_H +#define MAPLAYERFLOOR_H + +#include "MapLayer.h" +#include + +#include "../model/MMFloorObstacleLine.h" +#include "../model/MMFloorObstacleCircle.h" +#include "../model/MMFloorOutlinePolygon.h" +#include "../model/MMFloorBeacon.h" + + +/* +class MapLayerFloorOutlineAdd : public TypedMapLayer { +private: + Floorplan::Floor& floor; +public: + MapLayerFloorOutlineAdd(MapLayer* parent, Floorplan::Floor& floor) : TypedMapLayer(parent, MapLayerType::FLOOR_GROUND_ADD), floor(floor) {;} + std::string getLayerName() const override {return "add";} + Floorplan::Floor& getFloor() {return floor;} + std::vector getElements() const override { + std::vector vec; + for (Floorplan::FloorOutlinePolygon& p : floor.outline.add) {vec.push_back(new MapViewElementFloorOutlinePolygon(floor, p, MapViewElementFloorOutlinePolygon::Type::ADD));} + return vec; + } + void create(const Floorplan::FloorOutlinePolygon& poly) { + floor.outline.add.push_back(poly); + } + virtual size_t getNumElements() const override {return floor.outline.add.size();} +}; + +class MapLayerFloorOutlineRemove : public TypedMapLayer { +private: + Floorplan::Floor& floor; +public: + MapLayerFloorOutlineRemove(MapLayer* parent, Floorplan::Floor& floor) : TypedMapLayer(parent, MapLayerType::FLOOR_GROUND_REMOVE), floor(floor) {;} + std::string getLayerName() const override {return "remove";} + Floorplan::Floor& getFloor() {return floor;} + std::vector getElements() const override { + std::vector vec; + for (Floorplan::FloorOutlinePolygon& p : floor.outline.remove) {vec.push_back(new MapViewElementFloorOutlinePolygon(floor, p, MapViewElementFloorOutlinePolygon::Type::REMOVE));} + return vec; + } + void create(const Floorplan::FloorOutlinePolygon& poly) { + floor.outline.remove.push_back(poly); + } + virtual size_t getNumElements() const override {return floor.outline.remove.size();} +}; + + + + + +class MapLayerFloorOutlinePolygon : public TypedMapLayer { +private: + Floorplan::Floor& floor; +public: + MapLayerFloorOutlinePolygon(MapLayer* parent, Floorplan::Floor& floor) : TypedMapLayer(parent, MapLayerType::FLOOR_GROUND), floor(floor) { + new MapLayerFloorOutlineAdd(this, floor); + new MapLayerFloorOutlineRemove(this, floor); + } + std::string getLayerName() const override {return "ground";} + Floorplan::Floor& getFloor() {return floor;} +}; +*/ + + + + + + +#endif // MAPLAYERFLOOR_H diff --git a/mapview/model/MapModel.cpp b/mapview/model/MapModel.cpp new file mode 100644 index 0000000..e69de29 diff --git a/mapview/model/MapModel.h b/mapview/model/MapModel.h new file mode 100644 index 0000000..c24d419 --- /dev/null +++ b/mapview/model/MapModel.h @@ -0,0 +1,112 @@ +#ifndef MAPMODEL_H +#define MAPMODEL_H + +#include + +#include "MapLayer.h" +#include "MapModelElement.h" + +#include "MMRoot.h" + + +#include +#include +#include + +class MapModel : public QObject { + + Q_OBJECT + +private: + + ///** wrapper-classes for all elements */ + //std::vector selElements; + + /** the map's root-layer containing all other layers */ + MapLayer* root = nullptr; + + /** the currently selected layer (if any) */ + MapLayer* selLayer = nullptr; + + /** the loaded floorplan */ + Floorplan::IndoorMap* im; + +public: + + /** ctor */ + MapModel() { + root = new MapLayerRoot(nullptr); + } + + virtual ~MapModel() { + cleanup(); + } + + void cleanup() { + selLayer = nullptr; + //selElements.clear(); + if (root) {delete root; root = nullptr;} + } + + void load(const std::string& file) { + + emit aboutToReset(); + + cleanup(); + + // load the indoor-map using the given XML-file + im = Floorplan::Reader::readFromFile(file); + root = new MMRoot(nullptr, im); + + emit reset(); + + } + + void save(const std::string& file) { + + Floorplan::Writer::writeToFile(im, file); + + } + + /** get the map's root-layer containing all other layers */ + MapLayer* getRootLayer() { return root; } + + /** get all elements within the currently selected layer */ + std::vector getSelectedLayerElements() { + //return selElements; + //return (selLayer) ? (selLayer->getElementsRecursive()) : (std::vector()); + std::vector elements; + root->getVisibleElementsRecursive(elements); + return elements; + } + + /** get all currently visible elements */ + std::vector getVisibleElements() { + return getSelectedLayerElements(); + } + + /** set the currently selected layer */ + void setSelectedLayer(MapLayer* ml) { + //selElements.clear(); + //for (MapModelElement* el : ml->getElementsRecursive()) {selElements.push_back(el);} + selLayer = ml; + } + + /** get the currently selected layer */ + MapLayer* getSelectedLayer() const { + return selLayer; + } + + void reselect() { + setSelectedLayer(selLayer); + emit reset(); + } + +signals: + + void aboutToReset(); + void reset(); + +}; + +#endif // MAPMODEL_H diff --git a/mapview/model/MapModelElement.h b/mapview/model/MapModelElement.h new file mode 100644 index 0000000..ed3d49d --- /dev/null +++ b/mapview/model/MapModelElement.h @@ -0,0 +1,34 @@ +#ifndef MAPMODELELEMENT_H +#define MAPMODELELEMENT_H + + +#include "MapLayer.h" + +class MV2DElement; + +class MapModelElement { + +protected: + + MapLayer* parent; + +public: + + /** ctor */ + MapModelElement(MapLayer* parent) : parent(parent) {;} + + /** dtor */ + virtual ~MapModelElement() {;} + + /** get the 2D interaction class for this element */ + virtual MV2DElement* getMV2D() const = 0; + + /** delete this element from the model */ + virtual void deleteMe() const = 0; + + /** get the parent element */ + MapLayer* getParent() const {return parent;} + +}; + +#endif // MAPMODELELEMENT_H diff --git a/mapview/tools/Tool.cpp b/mapview/tools/Tool.cpp new file mode 100644 index 0000000..e69de29 diff --git a/mapview/tools/Tool.h b/mapview/tools/Tool.h new file mode 100644 index 0000000..6338fef --- /dev/null +++ b/mapview/tools/Tool.h @@ -0,0 +1,35 @@ +#ifndef TOOL_H +#define TOOL_H + +#include +#include +#include + +#include "../Painter.h" + +class MapView2D; + +class Tool : public QObject { + + Q_OBJECT + +public: + + virtual ~Tool() {;} + + virtual void mousePressEvent(MapView2D* m, QMouseEvent* e) { (void) m; (void) e; } + virtual void mouseMoveEvent(MapView2D* m, QMouseEvent* e) { (void) m; (void) e; } + virtual void mouseReleaseEvent(MapView2D* m, QMouseEvent* e) { (void) m; (void) e; } + + virtual void wheelEvent(MapView2D* m, QWheelEvent* e) { (void) m; (void) e; } + + virtual void keyPressEvent(MapView2D* m, QKeyEvent* e) { (void) m; (void) e; } + + virtual void paintBefore(MapView2D* m, Painter& p) { (void) m; (void) p; } + virtual void paintAfter(MapView2D* m, Painter& p) { (void) m; (void) p; } + + virtual void layerChange(MapView2D* m) { (void) m; } + +}; + +#endif // TOOL_H diff --git a/mapview/tools/ToolMapGrid.h b/mapview/tools/ToolMapGrid.h new file mode 100644 index 0000000..b3f9eea --- /dev/null +++ b/mapview/tools/ToolMapGrid.h @@ -0,0 +1,71 @@ +#ifndef TOOLMAPGRID_H +#define TOOLMAPGRID_H + +#include "Tool.h" + +class ToolMapGrid : public Tool { + +public: + + void paintBefore(MapView2D* m, Painter& p) override { + + (void) m; + + static const QColor cB(250,250,250); + static const QColor cN(235,235,235); + static const QColor c0(128,128,128); + + int w = p.width(); + int h = p.height(); + + // grid-size + const float step = p.getScaler().getLODstep(); + const float sStep = step/5; + + // map-region visible on the screen + const Rect r = p.getScaler().getMapVisible(w, h, step); + + // x-lines + for (float x = r.x0; x < r.x1; x += step) { + + // major-lines (without center) + if (std::abs(x) > 0.001) { + p.setPenBrush(cN, Qt::NoBrush); + p.drawLine(x, r.y0, x, r.y1); + } + + // minor-lines + p.setPenBrush(cB, Qt::NoBrush); + for (float x1 = x+sStep; x1 < x+step; x1 += sStep) { + p.drawLine(x1, r.y0, x1, r.y1); + } + + } + + // y-lines + for (float y = r.y0; y < r.y1; y += step) { + + // major-lines (without center) + if (std::abs(y) > 0.001) { + p.setPenBrush(cN, Qt::NoBrush); + p.drawLine(r.x0, y, r.x1, y); + } + + // minor-lines + p.setPenBrush(cB, Qt::NoBrush); + for (float y1 = y+sStep; y1 < y+step; y1 += sStep) { + p.drawLine(r.x0, y1, r.x1, y1); + } + + } + + // thick center lines + p.setPenBrush(c0, Qt::NoBrush); + p.drawLine(0, r.y0, 0, r.y1); + p.drawLine(r.x0, 0, r.x1, 0); + + } + +}; + +#endif // TOOLMAPGRID_H diff --git a/mapview/tools/ToolMapZoom.h b/mapview/tools/ToolMapZoom.h new file mode 100644 index 0000000..83acb9b --- /dev/null +++ b/mapview/tools/ToolMapZoom.h @@ -0,0 +1,28 @@ +#ifndef TOOLMAPZOOM_H +#define TOOLMAPZOOM_H + +#include "Tool.h" +#include "../MapView2D.h" + +class ToolMapZoom : public Tool { + +public: + + virtual void wheelEvent(MapView2D* m, QWheelEvent* e) override { + + Scaler& s = m->getScaler(); + + if (e->delta() < 0) { + s.setScale(s.getScale() * 0.5); + } else { + s.setScale(s.getScale() / 0.5); + } + + if (s.getScale() > 1000) {s.setScale(1000);} + if (s.getScale() < 5) {s.setScale(5);} + + } + +}; + +#endif // TOOLMAPZOOM_H diff --git a/mapview/tools/ToolMoveMap.h b/mapview/tools/ToolMoveMap.h new file mode 100644 index 0000000..72cf959 --- /dev/null +++ b/mapview/tools/ToolMoveMap.h @@ -0,0 +1,49 @@ +#ifndef TOOLMOVEMAP_H +#define TOOLMOVEMAP_H + +#include "Tool.h" +#include "../MapView2D.h" + +/** + * + */ +class ToolMoveMap : public Tool { + +private: + + bool mouseIsDown = false; + Point3 startOffset; + int sx; + int sy; + + virtual void mousePressEvent(MapView2D* m, QMouseEvent* e) override { + if (e->button() == Qt::MouseButton::MidButton) { + mouseIsDown = true; + this->sx = e->x(); + this->sy = e->y(); + this->startOffset = m->getScaler().getOffset(); + } + } + + virtual void mouseMoveEvent(MapView2D* m, QMouseEvent* e) override { + if (!mouseIsDown) {return;} + m->getScaler().setOffset(startOffset); + m->getScaler().addOffset(e->x()-sx, e->y()-sy); + } + + virtual void mouseReleaseEvent(MapView2D* m, QMouseEvent* e) override { + (void) m; + (void) e; + mouseIsDown = false; + } + + virtual void keyPressEvent(MapView2D* m, QKeyEvent* e) override { + (void) m; + (void) e; + // TODO: move on arrow keys? + } + + +}; + +#endif // TOOLMOVEMAP_H diff --git a/mapview/tools/ToolRuler.h b/mapview/tools/ToolRuler.h new file mode 100644 index 0000000..122407c --- /dev/null +++ b/mapview/tools/ToolRuler.h @@ -0,0 +1,140 @@ +#ifndef TOOLRULER_H +#define TOOLRULER_H + +#include "Tool.h" + +class ToolRuler : public Tool { + +private: + + int mouseX; + int mouseY; + + const bool fullSize = true; + const int rs = 40; // ruler size + const int tsMajor = 20; // major-tick size + const int tsMinor = 5; // minor-tick size + +public: + + virtual void mouseMoveEvent(MapView2D* m, QMouseEvent* e) override { + + (void) m; + + this->mouseX = e->x(); + this->mouseY = e->y(); + } + + virtual void paintBefore(MapView2D* m, Painter& p) override { + + (void) m; + + // mouse-indicator within the image? + if (fullSize) { + const int w = p.width(); + const int h = p.height(); + p.setPenBrush(Qt::lightGray, Qt::NoBrush); + p.p->drawLine(rs, mouseY, w, mouseY); + p.p->drawLine(mouseX, rs, mouseX, h); + } + + } + + virtual void paintAfter(MapView2D* m, Painter& p) override { + + (void) m; + + static const QColor c1(240,240,240); + static const QColor c2(200,200,200); + + const int w = p.width(); + const int h = p.height(); + + // ruler step-size depending on the current zoom level + const float step = p.getScaler().getLODstep(); + const float sStep = step / 10.0f; + + // the visible map-rect + const Rect r = p.getScaler().getMapVisible(w, h, step); + + QRect rx(rs,0,w-1-rs,rs); + QRect ry(0,rs,rs,h-1-rs); + + // background + { + + // samller font + QFont f = p.p->font(); f.setPointSize(8); + p.p->setFont(f); + + // outline + p.setPen(Qt::darkGray); + + // x-axis + QLinearGradient gx(0,0,0,rs); gx.setColorAt(0, c1); gx.setColorAt(1, c2); p.setBrush(gx); + p.p->drawRect(rx); + + // y-axis + QLinearGradient gy(0,0,rs,0); gy.setColorAt(0, c2); gy.setColorAt(1, c1); p.setBrush(gy); + p.p->drawRect(ry); + + } + + // mouse-indicator + p.setPenBrush(Qt::darkGray, Qt::NoBrush); + p.p->drawLine(0, mouseY, rs, mouseY); + p.p->drawLine(mouseX, 0, mouseX, rs); + + + + p.setPenBrush(Qt::black, Qt::NoBrush); + char buf[128]; + + + // y-axis + p.p->setClipRect(ry); + for (float y = r.y0; y <= r.y1; y += step) { + + // major-lines + const float yMajor = p.s.yms(y); + //if (yMajor < 0 || yMajor > h) {continue;} // stop at the end + p.p->drawLine(0,yMajor, tsMajor,yMajor); + + // minor-lines + for (float y1 = y+sStep; y1 < y+step; y1 += sStep) { + const float yMinor = p.s.yms(y1); + p.p->drawLine(0,yMinor, tsMinor,yMinor); + } + + // text-label + std::sprintf(buf, "%.1f", y); + p.p->drawText(6, yMajor-2, buf); + } + + // x-axis + p.p->setClipRect(rx); + for (float x = r.x0; x <= r.x1; x += step) { + + // major-lines + const float xMajor = p.s.xms(x); + //if (xMajor < 0 || xMajor > w) {continue;} // stop at the end + p.p->drawLine(xMajor,0, xMajor,tsMajor); + + // minor-lines + for (float x1 = x+sStep; x1 < x+step; x1 += sStep) { + const float xMinor = p.s.xms(x1); + p.p->drawLine(xMinor,0, xMinor,tsMinor); + } + + // text-label + std::sprintf(buf, "%.1f", x); + p.p->drawText(xMajor+2, 18, buf); + } + + p.p->setClipping(false); + + } + +}; + +#endif // TOOLRULER_H diff --git a/mapview/tools/ToolSelector.cpp b/mapview/tools/ToolSelector.cpp new file mode 100644 index 0000000..e69de29 diff --git a/mapview/tools/ToolSelector.h b/mapview/tools/ToolSelector.h new file mode 100644 index 0000000..8ae6158 --- /dev/null +++ b/mapview/tools/ToolSelector.h @@ -0,0 +1,163 @@ +#ifndef TOOLSELECTOR_H +#define TOOLSELECTOR_H + +#include "Tool.h" +#include "../model/MapModelElement.h" +#include "../MapView2D.h" +#include "../model/MapModel.h" + + +class ToolSelector : public Tool { + + Q_OBJECT + +private: + + /** the currently focused MapElement (if any) */ + MapModelElement* focused = nullptr; + + /** whether the mouse-button is currently pressed */ + bool mouseIsDown = false; + +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()) { + 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); + } + +private: + + /** + * @brief change the currently focused element. throws an event. + * @param el the to-be-focused element or null + * @return true if the focused element has changed. false otherwise + */ + bool setFocused(MapModelElement* el) { + + // whether the focus has changed or not + const bool focusChanged = (focused != el); + + if (focusChanged) { + + // unfocus the old one (if any) + if (focused) {focused->getMV2D()->unfocus();} + + // set the currently focused object (if any) + focused = el; + + // focus the new one (if any) + if (focused) {focused->getMV2D()->focus();} + + emit onMapElementSelected(el); + + } + + // done + return focusChanged; + + } + + virtual void mouseMoveEvent(MapView2D* m, QMouseEvent* e) override { + + const Scaler& s = m->getScaler(); + Point2 p2(s.xsm(e->x()), s.ysm(e->y())); + + // mouse pressed? + if (mouseIsDown) { + + // provide the focused element with the mouse event? + if (focused) {focused->getMV2D()->mouseMove(m, p2); return;} + + } + + } + + virtual void mouseReleaseEvent(MapView2D* m, QMouseEvent* e) override { + + const Scaler& s = m->getScaler(); + 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);} + + mouseIsDown = false; + + } + + virtual void keyPressEvent(MapView2D* m, QKeyEvent* e) override { + (void) m; + + if (focused) { + + // pass it to the element which may consume this event + if (focused->getMV2D()->keyPressEvent(m, e)) {return;} + + // not consumed -> additional options + + // ESCAPE -> unfocus + if (e->key() == Qt::Key_Escape) {setFocused(nullptr);} + + // DELETE -> delete + if (e->key() == Qt::Key_Delete) { + focused->deleteMe(); + setFocused(nullptr); + } + + } + + } + +signals: + + /** map element was selected */ + void onMapElementSelected(MapModelElement* el); + +}; + +#endif // TOOLSELECTOR_H diff --git a/mapview/tools/Tools.h b/mapview/tools/Tools.h new file mode 100644 index 0000000..0063ab7 --- /dev/null +++ b/mapview/tools/Tools.h @@ -0,0 +1,59 @@ +#ifndef TOOLS_H +#define TOOLS_H + +#include +#include "Tool.h" + +/** + * combine several tools under the interface for one tool + */ +class Tools : public Tool { + +private: + + std::vector tools; + +public: + + /** add this tool */ + void enable(Tool* t) {tools.push_back(t);} + + /** remove this tool */ + void disable(Tool* t) {tools.erase(std::remove(tools.begin(), tools.end(), t));} + + + virtual void mousePressEvent(MapView2D* m, QMouseEvent* e) override { + for (Tool* t : tools) {t->mousePressEvent(m, e);} + } + + virtual void mouseMoveEvent(MapView2D* m, QMouseEvent* e) override { + for (Tool* t : tools) {t->mouseMoveEvent(m, e);} + } + + virtual void mouseReleaseEvent(MapView2D* m, QMouseEvent* e) override { + for (Tool* t : tools) {t->mouseReleaseEvent(m, e);} + } + + virtual void wheelEvent(MapView2D* m, QWheelEvent* e) override { + for (Tool* t : tools) {t->wheelEvent(m, e);} + } + + virtual void keyPressEvent(MapView2D* m, QKeyEvent* e) override { + for (Tool* t : tools) {t->keyPressEvent(m, e);} + } + + virtual void paintBefore(MapView2D* m, Painter& p) override { + for (Tool* t : tools) {t->paintBefore(m, p);} + } + + virtual void paintAfter(MapView2D* m, Painter& p) override { + for (Tool* t : tools) {t->paintAfter(m, p);} + } + + virtual void layerChange(MapView2D* m) override { + for (Tool* t : tools) {t->layerChange(m);} + } + +}; + +#endif // TOOLS_H diff --git a/params/ActionWidget.cpp b/params/ActionWidget.cpp new file mode 100644 index 0000000..462199a --- /dev/null +++ b/params/ActionWidget.cpp @@ -0,0 +1,23 @@ +#include "ActionWidget.h" + +#include +#include + +ActionWidget::ActionWidget(QWidget *parent) : QGroupBox(parent) { + + setTitle("actions"); + + QHBoxLayout* lay = new QHBoxLayout(this); + + QPushButton* btnLoad = new QPushButton("load"); + lay->addWidget(btnLoad); + + QPushButton* btnSave = new QPushButton("save"); + lay->addWidget(btnSave); + + connect(btnLoad, SIGNAL(clicked(bool)), this, SIGNAL(onLoad())); + + connect(btnSave, SIGNAL(clicked(bool)), this, SIGNAL(onSave())); + + +} diff --git a/params/ActionWidget.h b/params/ActionWidget.h new file mode 100644 index 0000000..4655df7 --- /dev/null +++ b/params/ActionWidget.h @@ -0,0 +1,24 @@ +#ifndef ACTIONWIDGET_H +#define ACTIONWIDGET_H + +#include + +class ActionWidget : public QGroupBox { + + Q_OBJECT + +public: + + explicit ActionWidget(QWidget *parent = 0); + +signals: + + void onLoad(); + + void onSave(); + +public slots: + +}; + +#endif // ACTIONWIDGET_H diff --git a/params/ElementParamWidget.cpp b/params/ElementParamWidget.cpp new file mode 100644 index 0000000..76625bf --- /dev/null +++ b/params/ElementParamWidget.cpp @@ -0,0 +1,226 @@ +#include "ElementParamWidget.h" + +#include "../mapview/model/MapModelElement.h" +#include "../mapview/model/MMFloorObstacleLine.h" +#include "../mapview/model/MMFloorOutlinePolygon.h" + +#include "../mapview/model/IHasMAC.h" +#include "../mapview/model/IHasFile.h" + +#include + +#include +#include +#include +#include +#include +#include + +QComboBox* getMaterials() { + using namespace Floorplan; + QComboBox* cmbMaterial = new QComboBox(); + for (int i = 0; i < (int)Material::_END; ++i) { + switch ((Material)i) { + case Material::CONCRETE: cmbMaterial->addItem("Concrete", i); break; + case Material::UNKNOWN: cmbMaterial->addItem("Unknown ", i); break; + case Material::DRYWALL: cmbMaterial->addItem("Drywall", i); break; + case Material::WOOD: cmbMaterial->addItem("Wood", i); break; + case Material::GLASS: cmbMaterial->addItem("Glass", i); break; + case Material::_END: throw 1; + } + } + return cmbMaterial; +} + +QComboBox* getObstacleTypes() { + using namespace Floorplan; + 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; + case ObstacleType::HANDRAIL:cmb->addItem("Handrail", i); break; + case ObstacleType::PILLAR: cmb->addItem("Pillar", i); break; + case ObstacleType::_END: throw 1; + } + } + return cmb; +} + +QComboBox* getOutlineMethods() { + using namespace Floorplan; + QComboBox* cmb = new QComboBox(); + for (int i = 0; i < (int)OutlineMethod::_END; ++i) { + switch ((OutlineMethod)i) { + case OutlineMethod::ADD: cmb->addItem("add", i); break; + case OutlineMethod::REMOVE: cmb->addItem("remove ", i); break; + case OutlineMethod::_END: throw 1; + } + } + return cmb; +} + + +ElementParamWidget::ElementParamWidget(QWidget *parent) : QGroupBox(parent) { + + setMinimumSize(100, 100); + setMaximumWidth(200); + + setTitle("MapElement Parameters"); + QGridLayout* lay = new QGridLayout(this); + int r = 0; + + lblDetails = new QLabel(); + lay->addWidget(new QLabel("selected"),r,0); + lay->addWidget(lblDetails,r,1); + ++r; + + name.txt = new QLineEdit(); + name.lbl = new QLabel("name"); + lay->addWidget(name.lbl,r,0); + lay->addWidget(name.txt,r,1); + connect(name.txt , SIGNAL(textChanged(QString)), this, SLOT(onNameChange())); + ++r; + + mac.lbl = new QLabel("MAC"); + mac.txt = new QLineEdit(); + lay->addWidget(mac.lbl, r, 0); + lay->addWidget(mac.txt, r, 1); + connect(mac.txt, SIGNAL(textChanged(QString)), this, SLOT(onMACChanged())); + ++r; + + + obstacleType.cmb = getObstacleTypes(); + obstacleType.lbl = new QLabel("type"); + lay->addWidget(obstacleType.lbl,r,0); + lay->addWidget(obstacleType.cmb,r,1); + connect(obstacleType.cmb, SIGNAL(currentIndexChanged(int)), this, SLOT(onObstacleTypeChange())); + ++r; + + material.cmb = getMaterials(); + material.lbl = new QLabel("material"); + lay->addWidget(material.lbl,r,0); + lay->addWidget(material.cmb,r,1); + connect(material.cmb , SIGNAL(currentIndexChanged(int)), this, SLOT(onMaterialChange())); + ++r; + + fileName.txt = new QLabel(); + fileName.lbl = new QLabel("file"); + fileName.btn = new QPushButton("op"); + lay->addWidget(fileName.lbl,r,0); + lay->addWidget(fileName.txt,r,1); + lay->addWidget(fileName.btn,r,2); + connect(fileName.btn , SIGNAL(clicked(bool)), this, SLOT(onSelectFileName())); + ++r; + + outlineMethod.cmb = getOutlineMethods(); + outlineMethod.lbl = new QLabel("outline"); + lay->addWidget(outlineMethod.lbl,r,0); + lay->addWidget(outlineMethod.cmb,r,1); + connect(outlineMethod.cmb , SIGNAL(currentIndexChanged(int)), this, SLOT(onOutlineMethodChange())); + ++r; + + // start empty + setElement(nullptr); + +} + +void ElementParamWidget::setElement(MapModelElement* el) { + + 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("?"); + } + + // material? -> select in combo-box + { + IHasMaterial* elem = dynamic_cast(el); + material.cmb->setVisible(elem != nullptr); + material.lbl->setVisible(elem != nullptr); + if (elem) {material.cmb->setCurrentIndex((int)elem->getMaterial());} + } + + // obstacle-type? -> select in combo-box + { + IHasObstacleType* elem = dynamic_cast(el); + obstacleType.cmb->setVisible(elem != nullptr); + obstacleType.lbl->setVisible(elem != nullptr); + if (elem) {obstacleType.cmb->setCurrentIndex((int)elem->getObatcleType());} + } + + // has name? + { + IHasName* elem = dynamic_cast(el); + name.txt->setVisible(elem != nullptr); + name.lbl->setVisible(elem != nullptr); + if (elem) {name.txt->setText(elem->getName().c_str());} + } + + // has MAC? + { + IHasMAC* elem = dynamic_cast(el); + mac.lbl->setVisible(elem); + mac.txt->setVisible(elem); + if (elem) {mac.txt->setText(elem->getMAC().c_str());} + } + + // has outline method? + { + MMFloorOutlinePolygon* elem = dynamic_cast(el); + outlineMethod.cmb->setVisible(elem); + outlineMethod.lbl->setVisible(elem); + if (elem) {outlineMethod.cmb->setCurrentIndex((int)elem->getMethod());} + } + + // has File + { + IHasFile* elem = dynamic_cast(el); + fileName.lbl->setVisible(elem); + fileName.txt->setVisible(elem); + fileName.btn->setVisible(elem); + if (elem) {fileName.txt->setText(elem->getFileName().c_str());} + } + +} + +void ElementParamWidget::onMaterialChange() { + IHasMaterial* el = dynamic_cast(this->curElement); + if (el) {el->setMaterial( (Floorplan::Material) material.cmb->currentData().toInt() );} +} + +void ElementParamWidget::onObstacleTypeChange() { + IHasObstacleType* el = dynamic_cast(this->curElement); + if (el) {el->setObstacleType((Floorplan::ObstacleType) obstacleType.cmb->currentData().toInt() );} +} + +void ElementParamWidget::onNameChange() { + IHasName* el = dynamic_cast(this->curElement); + if (el) {el->setName(name.txt->text().toStdString());} +} + +void ElementParamWidget::onOutlineMethodChange() { + MMFloorOutlinePolygon* el = dynamic_cast(this->curElement); + if (el) {el->setMethod( (Floorplan::OutlineMethod) outlineMethod.cmb->currentData().toInt() );} +} + +void ElementParamWidget::onMACChanged() { + dynamic_cast(curElement)->setMAC(mac.txt->text().toStdString()); +} + +void ElementParamWidget::onSelectFileName() { + QString res = QFileDialog::getOpenFileName(this); + dynamic_cast(curElement)->setFileName(res.toStdString()); + fileName.lbl->setText(res); +} diff --git a/params/ElementParamWidget.h b/params/ElementParamWidget.h new file mode 100644 index 0000000..81cbc95 --- /dev/null +++ b/params/ElementParamWidget.h @@ -0,0 +1,76 @@ +#ifndef PARAMWIDGET_H +#define PARAMWIDGET_H + +#include +#include "../mapview/model/MapModelElement.h" + +class QLabel; +class QComboBox; +class QLineEdit; +class QPushButton; + +class ElementParamWidget : public QGroupBox { + + Q_OBJECT + +public: + + explicit ElementParamWidget(QWidget *parent = 0); + + + +private: + + QLabel* lblDetails; + MapModelElement* curElement = nullptr; + + struct { + QComboBox* cmb; + QLabel* lbl; + } material; + + struct { + QComboBox* cmb; + QLabel* lbl; + } obstacleType; + + struct { + QLineEdit* txt; + QLabel* lbl; + } name; + + struct { + QLineEdit* txt; + QLabel* lbl; + } mac; + + struct { + QLabel* txt; + QPushButton* btn; + QLabel* lbl; + } fileName; + + struct { + QComboBox* cmb; + QLabel* lbl; + } outlineMethod; + +signals: + +public slots: + + /** set the to-be-edited element */ + void setElement(MapModelElement* el); + +private slots: + + void onMaterialChange(); + void onObstacleTypeChange(); + void onNameChange(); + void onMACChanged(); + void onOutlineMethodChange(); + void onSelectFileName(); + +}; + +#endif // PARAMWIDGET_H diff --git a/params/LayerParamWidget.cpp b/params/LayerParamWidget.cpp new file mode 100644 index 0000000..12912e1 --- /dev/null +++ b/params/LayerParamWidget.cpp @@ -0,0 +1,106 @@ +#include "LayerParamWidget.h" +#include + +#include +#include + +#include "mapview/model/MMFloor.h" + +LayerParamWidget::LayerParamWidget(QWidget *parent) : QGroupBox(parent) { + + setMinimumSize(100, 100); + setMaximumWidth(200); + setTitle("MapLayer Parameters"); + + QGridLayout* lay = new QGridLayout(this); + int r = 0; + + + selected.lbl = new QLabel("selected"); + selected.info = new QLabel(); + lay->addWidget(selected.lbl, r, 0); + lay->addWidget(selected.info, r, 1); + ++r; + + name.lbl = new QLabel("name"); + name.txt = new QLineEdit(); + lay->addWidget(name.lbl, r, 0); + lay->addWidget(name.txt, r, 1); + connect(name.txt, SIGNAL(textChanged(QString)), this, SLOT(onNameChanged())); + ++r; + + atHeight.lbl = new QLabel("at height"); + atHeight.txt = new QLineEdit(); + lay->addWidget(atHeight.lbl, r, 0); + lay->addWidget(atHeight.txt, r, 1); + connect(atHeight.txt, SIGNAL(textChanged(QString)), this, SLOT(onAtHeightChanged())); + ++r; + + height.lbl = new QLabel("height"); + height.txt = new QLineEdit(); + lay->addWidget(height.lbl, r, 0); + lay->addWidget(height.txt, r, 1); + connect(height.txt, SIGNAL(textChanged(QString)), this, SLOT(onHeightChanged())); + ++r; + + // start empty + setElement(nullptr); + +} + +void LayerParamWidget::setElement(MapLayer* l) { + + this->curElement = l; + + if (l) { + std::string info = l->getLayerName() + " (" + std::to_string(l->getNumElements()) + " elements)"; + selected.info->setText(info.c_str()); + } else { + selected.info->setText("-"); + } + + { + MMFloor* floor = dynamic_cast(l); + atHeight.lbl->setVisible(floor); + atHeight.txt->setVisible(floor); + height.lbl->setVisible(floor); + height.txt->setVisible(floor); + if (floor) { + std::string _atHeight = std::to_string(floor->getFloor().atHeight); + atHeight.txt->setText( _atHeight.c_str() ); + std::string _height = std::to_string(floor->getFloor().height); + height.txt->setText( _height.c_str() ); + } + } + + // has name + { + IHasName* elem = dynamic_cast(l); + name.lbl->setVisible(elem); + name.txt->setVisible(elem); + if (elem) {name.txt->setText(elem->getName().c_str());} + } + +} + + + +void LayerParamWidget::onAtHeightChanged() { + MMFloor* floor = dynamic_cast(curElement); + try { + if (floor) {floor->getFloor().atHeight = std::stof(atHeight.txt->text().toStdString());} + } catch (...) {;} +} + +void LayerParamWidget::onHeightChanged() { + MMFloor* floor = dynamic_cast(curElement); + try { + if (floor) {floor->getFloor().height = std::stof(height.txt->text().toStdString());} + } catch (...) {;} +} + +void LayerParamWidget::onNameChanged() { + dynamic_cast(curElement)->setName(name.txt->text().toStdString()); +} + + diff --git a/params/LayerParamWidget.h b/params/LayerParamWidget.h new file mode 100644 index 0000000..e16ba4f --- /dev/null +++ b/params/LayerParamWidget.h @@ -0,0 +1,61 @@ +#ifndef LAYERPARAMWIDGET_H +#define LAYERPARAMWIDGET_H + +#include + +class MapLayer; + +class QLabel; +class QLineEdit; +class QComboBox; + +class LayerParamWidget : public QGroupBox { + + Q_OBJECT + +public: + + explicit LayerParamWidget(QWidget *parent = 0); + +private: + + MapLayer* curElement = nullptr; + + struct { + QLabel* info; + QLabel* lbl; + } selected; + + struct { + QLineEdit* txt; + QLabel* lbl; + } name; + + struct { + QLineEdit* txt; + QLabel* lbl; + } atHeight; + + struct { + QLineEdit* txt; + QLabel* lbl; + } height; + + + +signals: + +public slots: + + /** set the to-be-edited element */ + void setElement(MapLayer* l); + +private slots: + + void onAtHeightChanged(); + void onHeightChanged(); + void onNameChanged(); + +}; + +#endif // LAYERPARAMWIDGET_H diff --git a/params/ToolBox.cpp b/params/ToolBox.cpp new file mode 100644 index 0000000..bc090ff --- /dev/null +++ b/params/ToolBox.cpp @@ -0,0 +1,174 @@ +#include "ToolBoxWidget.h" + +#include +#include + +#include +#include + +#include "../mapview/model/MapLayers.h" + +#include "../mapview/MapView2D.h" +#include "../mapview/model/MapModel.h" +#include "../mapview/model/MMFloorAccessPoint.h" +#include "../mapview/model/MMFloorBeacon.h" + +#include "../UIHelper.h" + +ToolBoxWidget::ToolBoxWidget(MapView2D* view, QWidget *parent) : QWidget(parent), view(view) { + + const int s = 32; + setMaximumWidth(48); + setMinimumWidth(48); + + QGridLayout* lay = new QGridLayout(this); + int r = 0; + + // OBSTACLES + btnGround = new QPushButton(UIHelper::getIcon("floor"), ""); + btnGround->setMinimumSize(s,s); + lay->addWidget(btnGround, r++, 0, 1,1,Qt::AlignTop); + connect(btnGround, SIGNAL(clicked(bool)), this, SLOT(onNewGround())); + + btnWall = new QPushButton(UIHelper::getIcon("wall"), ""); + btnWall->setMinimumSize(s,s); + lay->addWidget(btnWall, r++, 0, 1,1,Qt::AlignTop); + connect(btnWall, SIGNAL(clicked(bool)), this, SLOT(onNewWall())); + + btnPillar = new QPushButton(UIHelper::getIcon("pillar"), ""); + btnPillar->setMinimumSize(s,s); + lay->addWidget(btnPillar, r++, 0, 1,1,Qt::AlignTop); + connect(btnPillar, SIGNAL(clicked(bool)), this, SLOT(onNewPillar())); + + // TRANSMITTERS + btnWifi = new QPushButton(UIHelper::getIcon("wifi"), ""); + btnWifi->setMinimumSize(s,s); + lay->addWidget(btnWifi, r++, 0, 1,1,Qt::AlignTop); + connect(btnWifi, SIGNAL(clicked(bool)), this, SLOT(onNewAccessPoint())); + + btnBeacon = new QPushButton(UIHelper::getIcon("beacon"), ""); + btnBeacon->setMinimumSize(s,s); + lay->addWidget(btnBeacon, r++, 0, 1,1,Qt::AlignTop); + connect(btnBeacon, SIGNAL(clicked(bool)), this, SLOT(onNewBeacon())); + + // IMAGES + btnImage = new QPushButton(UIHelper::getIcon("image"), ""); + btnImage->setMinimumSize(s,s); + lay->addWidget(btnImage, r++, 0, 1,1,Qt::AlignTop); + connect(btnImage, SIGNAL(clicked(bool)), this, SLOT(onNewImage())); + + + // FILL + lay->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum, QSizePolicy::MinimumExpanding), r, 0); + + // start empty + setSelectedLayer(nullptr); + +} + +void ToolBoxWidget::setSelectedLayer(MapLayer *ml) { + + this->curLayer = ml; + + btnGround->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_GROUND)); + + btnWall->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_OBSTACLES)); + btnPillar->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_OBSTACLES)); + + btnWifi->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_ACCESS_POINTS)); + btnBeacon->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_BEACONS)); + + btnImage->setEnabled(ml && (ml->getLayerType() == MapLayerType::FLOOR_UNDERLAY)); + +} + + +void ToolBoxWidget::onNewGround() { + + const Point2 center = view->getScaler().getCenter(); + float s = view->getScaler().sm(50); + + Floorplan::FloorOutlinePolygon* poly = new Floorplan::FloorOutlinePolygon(); + poly->name = "new"; + poly->poly.points.push_back(Point2(center.x-s, center.y-s)); + poly->poly.points.push_back(Point2(center.x+s, center.y-s)); + poly->poly.points.push_back(Point2(center.x, center.y+s)); + + + MMFloorOutline* ml = (MMFloorOutline*)curLayer; + ml->create(poly); + + view->getModel()->reselect(); + +} + +void ToolBoxWidget::onNewWall() { + + const Point2 center = view->getScaler().getCenter(); + float s = view->getScaler().sm(50); + + Floorplan::FloorObstacleLine* wall = new Floorplan::FloorObstacleLine( + Floorplan::ObstacleType::WALL, + Floorplan::Material::DRYWALL, + Point2(center.x-s, center.y), + Point2(center.x+s, center.y) + ); + + MMFloorObstacles* obs = (MMFloorObstacles*)curLayer; + obs->createLine(wall); + + view->getModel()->reselect(); + +} + +void ToolBoxWidget::onNewPillar() { + + const Point2 center = view->getScaler().getCenter(); + 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 + ); + + MMFloorObstacles* obs = (MMFloorObstacles*)curLayer; + obs->createCircle(pillar); + + view->getModel()->reselect(); + +} + +void ToolBoxWidget::onNewAccessPoint() { + + const Point2 center = view->getScaler().getCenter(); + Floorplan::AccessPoint* ap = new Floorplan::AccessPoint( + "noname", "00:00:00:00:00:00", Point3(center.x, center.y, 0) + ); + + MMFloorAccessPoints* aps = (MMFloorAccessPoints*) curLayer; + aps->createAP(ap); + +} + +void ToolBoxWidget::onNewBeacon() { + + const Point2 center = view->getScaler().getCenter(); + Floorplan::Beacon* b = new Floorplan::Beacon( + "noname", "00:00:00:00:00:00", Point3(center.x, center.y, 0) + ); + + MMFloorBeacons* beacons = (MMFloorBeacons*) curLayer; + beacons->createBeacon(b); + +} + +void ToolBoxWidget::onNewImage() { + + const Point2 center = view->getScaler().getCenter(); + + MMFloorUnderlay* underlay = (MMFloorUnderlay*) curLayer; + underlay->createImage(center); + +} diff --git a/params/ToolBoxWidget.h b/params/ToolBoxWidget.h new file mode 100644 index 0000000..98e5048 --- /dev/null +++ b/params/ToolBoxWidget.h @@ -0,0 +1,53 @@ +#ifndef TOOLBOX_H +#define TOOLBOX_H + +#include + +class MapLayer; +class QPushButton; +class MapView2D; + +class ToolBoxWidget : public QWidget { + + Q_OBJECT + +public: + + explicit ToolBoxWidget(MapView2D* view, QWidget *parent = 0); + +signals: + +public slots: + + /** set the currently selected map layer */ + void setSelectedLayer(MapLayer* ml); + +private: + + MapView2D* view; + MapLayer* curLayer; + + QPushButton* btnGround; + QPushButton* btnWall; + QPushButton* btnPillar; + + QPushButton* btnWifi; + QPushButton* btnBeacon; + + QPushButton* btnImage; + +private slots: + + void onNewGround(); + void onNewWall(); + void onNewPillar(); + + void onNewAccessPoint(); + void onNewBeacon(); + + void onNewImage(); + + +}; + +#endif // TOOLBOX_H diff --git a/res.qrc b/res.qrc new file mode 100644 index 0000000..c205e50 --- /dev/null +++ b/res.qrc @@ -0,0 +1,13 @@ + + + res/icons/polygon32.png + res/icons/polygon16.png + res/icons/floor.svg + res/icons/wall.svg + res/icons/pillar.svg + res/icons/wifi.svg + res/icons/beacon.svg + res/icons/image.svg + res/icons/cross.png + + diff --git a/res/icons/beacon.svg b/res/icons/beacon.svg new file mode 100644 index 0000000..a21519d --- /dev/null +++ b/res/icons/beacon.svg @@ -0,0 +1,770 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + diff --git a/res/icons/cross.png b/res/icons/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..bbcb2add47f217127b8e16f2c756981335bcdf4f GIT binary patch literal 2415 zcmaKtX;f4962K!6phC!lmQbNs3dJQcpi!!ViVCR`5iOESxDjL(l9a^=vIzQ~8mS6O z!Q?4KWD%uFZrSb)h=j!&41rdXkVGtDQG#W$N(5P4Xzcs)KJ}cLGs}PG|2y+#=EA|C zJ(%T=%TXv4CSb3BFbcJ7-AA=FHD-b;Pwg=p)8o4XLM$yUe;+w8Yz#k1*&B8owak?B zQI}PPZ2MqLu1F=*QV%5`O-+wt$Dz{G(>)kTtm83JDRCaj?D&#Dw>cV%XdjFGl2bC- zjQE69l%L;vmxF21-!o!d{F4)-7)dVvaj}e;s8mLBl1oBrYKpg~XAC>e7@vrXNlr{m zPCEYaGtZR2dt(1?@yv>2Cwp%2Snt{SOtN5PJUSQ{LN?k~G`0t(Aq$kvd$ z)^6;=Z|Wj=b)#*1(3l>yjS`Jfp>0&=7?rt=)&ld`!uqiVrr!div#=htupU02H)3f$ zlAAyB!{w0+S4J)tJhikQ<(26F%hzAKp}$_LFTOceQZ{y@Vyv`sZfIow>FE43{ru?I z+n2B2zM6hJZCF}bG8e5bHI9T$4Gubl+PKNvXWPyu?c;u(;q}b5&*KMBD2t*1|6L&& zPhRLwhEl1YpYo>$DSAJk{7g`57gECnzn@7q3J%;1K}4U-VZe+ zQ|iU@G)>94rashHV%HPorc>53$@?A(Fhrsd2hWJ!u^3>cR*4v(sziO*z9D%HkHZZm zIuUMy0I(9761`)Dftjj$#84HNs1N8KlwZM};jI;@vRO4BZW^ z2{NQ;6~|fip(LI6B)1{SeTGs&e=cFGp{@ws=>`8XdUwS3txeCX=|z&}^rkIYCNGWS z484M)sAS)r~OK20JNfSrd4E_W}LP8)2RzhXqP4I57yv~Ff-pDTF#-mje z4Ha5BV#1iE|H#tQcfsdH)d)p|0)GJ>fN_8ti~(S<3gCjzfLov*pn`{htDqNP52}E3 zU>YC=*8-^^0&qYE5DU%%WFspCv=o{^sRUPI9#srnw$I1O@rMYHF8W)=bE~*4aybW< z_sSbxd+&qpEH3Rjs~oaJxUTAK0YnLt+A9l(x zJ%@O=I1DfAnJC67Ck{_4K9p(jmXn$Swc+JLPF*`I3c_7FtLlDF%5Z4>H|@$v-*8`_ zd*#>+BCIwjPf8l6KhHxHOZQ~r0$zdo#8UY8e?xp=HU zI*ehT6tk(bUV+!Su=3-|sV&zZf3fd~FuSCAJ5LNqd8bU)g+s+rw$`*_kb2f@P3;Uk z@vS%|lEI)}Zv3i4sSr5xADpQwr*4nEXudB(cq)4ZmIvb^`rem%x`pwVyZ5vJ$IllkHC00F}8@xeiep+V>Gyim8d?)N|t*{j{C%S!)3D36z zKrM5O!?{RxtGRzwRGiTzII3=+JV_?ZS^0%Pe9x`HVz%Vkew}=p6ktkdv`X18;(NUK zrk;J5(cU8;Iq{hvA>HXlAjD5B-za7yTU7&ch4_dGVY8Ebt%#q8cd272Ii^uN{wFJ{2Ju`aV6QHLxc2Ue$@=+=DgXwpV`Dq4eJcx+lHc zZdoRI-Rw?C9FNR*KFw*g+v4ynkkvT%P4B}a`Ocy@cF7#=;_M9D7dwbv&Ki}q?zyw) zjYLW>M+W&T`x{rCjvbnbDoCCNXwYV6?ylc!Kvw(m%Bm!Hp;rQ@msDHW=U6AulIjXoj)*`_sxMR)*9ugm zhC)R#EKrc(!d^-(UQ0smv)seD%oRqy9VCZ)pjxmCa0ktRy^ysqo4B072YA$IGVSJw@%>AW-9BimIYPpu&qO3RfdSP+P32fbm*E zeX-IN!K($ZqqYLUs{~?4m8%%9Fri^s-_iA~U83`_3}PcYf%o7HkO{6A?jTwKTfv>e zNzg;2M9P4h%&m14wFy03&5q&TMmIK#^fKl*^y`P6=A8y7jN78axH-5hzFT!q{Ru5+ z3V2=}8lW?A-mDlR9*|I`G`N9@3i{MvcvkBnGN-QhWNNU zdWD?QeTSRR3nf?)+ki!|6oQd#`ZzPQQQ2omuDYW>!{q@^fHw%ya+FK8B+Y$91*{<4NJ5mtqJhH_x9h(PH^=|;nf%`gefrKK;{Kzh-CZ{pjh7o0Ko0W% Jd3WTQzW@^QmID9) literal 0 HcmV?d00001 diff --git a/res/icons/floor.svg b/res/icons/floor.svg new file mode 100644 index 0000000..9d3e9c5 --- /dev/null +++ b/res/icons/floor.svg @@ -0,0 +1,1070 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000..ad64f3a --- /dev/null +++ b/res/icons/image.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IMAGE + + diff --git a/res/icons/pillar.svg b/res/icons/pillar.svg new file mode 100644 index 0000000..8cc95b7 --- /dev/null +++ b/res/icons/pillar.svg @@ -0,0 +1,753 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/polygon.svg b/res/icons/polygon.svg new file mode 100644 index 0000000..ee14629 --- /dev/null +++ b/res/icons/polygon.svg @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/polygon16.png b/res/icons/polygon16.png new file mode 100644 index 0000000000000000000000000000000000000000..3bda1f5e7aef387f1a7fb2ec5e01d9b1dfc939a3 GIT binary patch literal 659 zcmV;E0&M+>P)^B@1KB^InpBUT!Qz zN~~GiE@5c zT`8gtN5(0E)MGbkdHs3>SJU4iF$&?`>&OFfaxJqm{q>iPTc>7B{GI%&|2? z!=3(C1=-glZDv<)7ze}FwD74d3sw(2Fum;L2-Z#`T5fx>kFyGCHp~ZOhiIfb)cA(G zVp~kGe^dYf373IbWOomDs6uiRmmCi#EYIi9xMSDt$k%C6)R&t`^svX?8<~UD*|z{? zki_cDjic(a@VpmB`-nSw&CT{Ei3i@KFSnPLdVxvlw3PIIW~6ZqE%X#`9!V}+*LLjU tddK+HgMYi<9>28Hm6*QsFIoM6egW%<<<+d8iLn3x002ovPDHLkV1n#QATs~} literal 0 HcmV?d00001 diff --git a/res/icons/polygon32.png b/res/icons/polygon32.png new file mode 100644 index 0000000000000000000000000000000000000000..8e06b2d1334f3bd2712ce23b4ab0dc5bfea7ca69 GIT binary patch literal 1331 zcmV-31&m(d5TDU>KR)&=D2yk-*WaI#KHipz0-61rE$j$V_CO{c0gfVWTvMZA07Z zs0;-l##&N#0=o@sm}mA@!AA$MsZmC)o0y<-RaFQ@g_5x^czZ9%Gh+^o^O2_}$ zay#w2VMT|2ctLGAHZtr_f7Qj>og4%cMaJiucn%ba4`+`weN~a7T@R4p?3^pOef5Y$ zLj^E<?PS41xP?aGB>Dfo_wIny3h?&~ZU1H#im02Bm`OA-qeK+C%ct)24rg{FeSy+Ul003vygyHrK00;?yF3X2u-?4h$`fEkcC*WEWSj41IB<~du zi(6T(?%TbE0RYI(@D4)e2vzpvb#18#&GMY z^+o^$Ai#p2j`slse0x621x<4(&CQK{6H}ceX_A^uj<96rj~&g%WF(UC-Ebb}5G+%Q za+x)HJOOL++wJdkRTOudPwk%@IQH|mkE#+68ZYG%8y$=cem`ib~eviB_Lee z_y%^bkBzlQP2cb_zuNk>?V(jkU@}J$3g5JBv{eJ(Ki(mdo$CDt1s0*9UsmcWce$!} zbSJ#Ie=M3KMqPAT_w?_;O(o4N{K$B1>U?9<7vZ`jEo^f-E)OKG*=cD6fc5bu>KNPP zMQ=cAG + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000..079a0c9 --- /dev/null +++ b/res/icons/wifi.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + diff --git a/tree/MapTreeModel.cpp b/tree/MapTreeModel.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tree/MapTreeModel.h b/tree/MapTreeModel.h new file mode 100644 index 0000000..e9fcae3 --- /dev/null +++ b/tree/MapTreeModel.h @@ -0,0 +1,120 @@ +#ifndef MAPTREE_H +#define MAPTREE_H + +#include +#include "../mapview/model/MapModel.h" + +// http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html + +class MapTreeModel : public QAbstractItemModel { + + Q_OBJECT + +private: + + MapModel* mdl; + +public: + + /** ctor */ + MapTreeModel(MapModel* mdl) : mdl(mdl) { + + connect(mdl, SIGNAL(reset()), this, SLOT(onMapModelReset())); + + } + + virtual ~MapTreeModel() {;} + + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override { + + MapLayer* _parent = nullptr; + + if (!parent.isValid()) { + _parent = mdl->getRootLayer(); + } else { + _parent = static_cast(parent.internalPointer()); + } + + return createIndex(row, column, _parent->getSubLayers().at(row)); + + } + + virtual QModelIndex parent(const QModelIndex& child) const override { + MapLayer* _child = static_cast(child.internalPointer()); + if (!_child) {return QModelIndex();} + return createIndex(0,0,_child->getParent()); + } + + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override { + +// if (parent.column() > 0) { +// return 0; +// } + + MapLayer* _parent = nullptr; + + if (!parent.isValid()) { // root + _parent = mdl->getRootLayer(); + } else { // deeper layer + _parent = static_cast(parent.internalPointer()); + } + + return _parent->getSubLayers().size(); + + } + + virtual int columnCount(const QModelIndex& parent) const override { + if (!parent.isValid()) { + return 1; // root + } else { + return 1; // TODO + } + } + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override { + + MapLayer* _elem = static_cast(index.internalPointer()); + if (index.column() == 0) { + if (role == Qt::DisplayRole) {return _elem->getLayerName().c_str();} + if (role == Qt::CheckStateRole) {return _elem->isVisible() ? Qt::Checked : Qt::Unchecked;} + } + + return QVariant(); + + } + + Qt::ItemFlags flags(const QModelIndex &index) const override { + + (void) index; + + // each item is selectable, enable, and has an editable checkbox + int flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; + return (Qt::ItemFlags) flags; + + } + + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override { + + // checking / unchecking a layer (visibility) + if (role == Qt::CheckStateRole) { + MapLayer* _elem = static_cast(index.internalPointer()); + _elem->setVisible( value != 0 ); + return true; + } + + return false; + + } + +public slots: + + /** the underlying map-model has changed */ + void onMapModelReset() { + //emit dataChanged(QModelIndex(), QModelIndex(-1)); + //emit modelReset(QPrivateSignal()); + endResetModel(); + } + +}; + +#endif // MAPTREE_H