commit 13a89df8d672a0e1a05cd1bbf3173de9cef5727d Author: kazu Date: Tue May 24 16:55:19 2016 +0200 initial commit 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 0000000..bbcb2ad Binary files /dev/null and b/res/icons/cross.png differ 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 0000000..3bda1f5 Binary files /dev/null and b/res/icons/polygon16.png differ diff --git a/res/icons/polygon32.png b/res/icons/polygon32.png new file mode 100644 index 0000000..8e06b2d Binary files /dev/null and b/res/icons/polygon32.png differ diff --git a/res/icons/wall.svg b/res/icons/wall.svg new file mode 100644 index 0000000..31a41fb --- /dev/null +++ b/res/icons/wall.svg @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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