diff --git a/IndoorMap.pro b/IndoorMap.pro index 5b94665..58812b1 100644 --- a/IndoorMap.pro +++ b/IndoorMap.pro @@ -37,7 +37,11 @@ SOURCES += \ tree/MapTreeModel.cpp \ mapview/3D/MapView3D.cpp \ params/StairBuilder.cpp \ - mapview/2D/tools/Tools.cpp + mapview/2D/tools/Tools.cpp \ + params/LayerTree.cpp \ + mapview/2D/tools/ToolMeasure.cpp \ + params/MetaEditWidget.cpp \ + params/MetaEditModel.cpp HEADERS += MainWindow.h \ @@ -64,6 +68,7 @@ HEADERS += MainWindow.h \ mapview/2D/MV2DElementBeacon.h \ mapview/2D/MV2DElementAccessPoint.h \ mapview/2D/MV2DElementFloorObstacleCircle.h \ + mapview/2D/MV2DElementFloorObstacleDoor.h \ mapview/2D/MapViewElementHelper.h \ mapview/2D/MV2DElementFloorUnderlay.h \ mapview/2D/MV2DElementPOI.h \ @@ -126,6 +131,16 @@ HEADERS += MainWindow.h \ mapview/2D/MV2DElementElevator.h \ mapview/model/MMFloorElevators.h \ mapview/model/MMFloorElevator.h \ + params/LayerTree.h \ + params/EditFields.h \ + mapview/2D/ClickDist.h \ + mapview/2D/tools/ToolMeasure.h \ + mapview/model/IHasEditableMeta.h \ + params/MetaEditWidget.h \ + params/MetaEditModel.h \ + mapview/model/MapLayerListener.h \ + mapview/model/MapModelListener.h \ + mapview/model/MMFloorOutlinePolygonCombined.h\ mapview/model/MMFloorGroundTruthPoints.h \ mapview/model/MMFloorGroundTruthPoint.h \ mapview/2D/MV2DElementGroundTruthPoint.h diff --git a/MainController.cpp b/MainController.cpp index daa954d..cd567b2 100644 --- a/MainController.cpp +++ b/MainController.cpp @@ -15,6 +15,8 @@ #include "params/LayerParamWidget.h" #include "params/ToolBoxWidget.h" #include "params/ActionWidget.h" +#include "params/LayerTree.h" +#include "params/MetaEditWidget.h" #include "tree/MapTreeModel.h" @@ -27,12 +29,20 @@ MainController::MainController() { +// Floorplan::Meta* meta = new Floorplan::Meta(); +// meta->setVal("a", "b"); +// meta->setVal("c", "d"); +// MetaEditWidget* mew = new MetaEditWidget(meta); +// mew->resize(300,300); +// mew->show(); + + mw = new MainWindow(); mw->resize(1000, 700); MapView2D* mapView2D = mw->getMapView2D(); MapView3D* mapView3D = mw->getMapView3D(); - QTreeView* layerTree = mw->getTree(); + LayerTree* layerTree = mw->getTree(); // model setup @@ -43,11 +53,11 @@ MainController::MainController() { mapTreeModel = new MapTreeModel(mapModel); layerTree->setModel(mapTreeModel); - ToolMoveMap* moveMap = new ToolMoveMap(); - ToolRuler* ruler = new ToolRuler(); - ToolMapZoom* mapZoom = new ToolMapZoom(); - ToolMapGrid* mapGrid = new ToolMapGrid(); - ToolSelector* mapSelector = new ToolSelector(); + moveMap = new ToolMoveMap(); + ruler = new ToolRuler(); + mapZoom = new ToolMapZoom(); + mapGrid = new ToolMapGrid(); + mapSelector = new ToolSelector(); mapView2D->getTools().addBackground(mapGrid); mapView2D->getTools().addBackground(moveMap); @@ -58,7 +68,8 @@ MainController::MainController() { //connect(layerTree, SIGNAL(activated(QModelIndex)), this, SLOT(layerSelected(QModelIndex))); - connect(layerTree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(layerSelected(QModelIndex))); + //connect(layerTree->getTree()->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(layerSelected(QModelIndex))); + connect(layerTree, SIGNAL(layerSelected(QModelIndex)), this, SLOT(layerSelected(QModelIndex))); connect(mapSelector, SIGNAL(onMapElementSelected(MapModelElement*)), this, SLOT(mapElementSelected(MapModelElement*))); connect(mw->getMapView2D(), SIGNAL(onElementChange(MV2DElement*)), this, SLOT(curMapElementChanged())); @@ -66,6 +77,7 @@ MainController::MainController() { // model events connect(mapModel, SIGNAL(aboutToReset()), this, SLOT(onMapModelAboutToReset())); connect(mapModel, SIGNAL(reset()), this, SLOT(onMapModelReset())); + mapModel->addListener(this); // load/save connect(mw->getActionWidget(), SIGNAL(onLoad()), this, SLOT(onLoad())); @@ -81,20 +93,18 @@ MainController::MainController() { connect(mw, &MainWindow::onGridShowEdges, [&] (const bool show) {mw->getMapView3D()->getGridRenderer()->setShowEdges(show);} ); - //mapModel->load("../IndoorMap/maps/SHL21.xml"); + mapModel->load("../IndoorMap/maps/SHL26.xml"); //mapModel->load("../IndoorMap/maps/test.xml"); + //mapModel->load("../IndoorMap/maps/APs.xml"); - //mapModel->load("/home/toni/Documents/programme/localization/IPIN2016/competition/src/competition/map/CAR/CAR10.xml"); - mapModel->load("/home/toni/Documents/programme/localization/IPIN2016/competition/src/competition/map/UAH/UAH12_rawObstacles.xml"); - //mapModel->load("/home/toni/Documents/programme/localization/IPIN2016/competition/src/competition/map/SHL/SHL25.xml"); - //mapModel->load("/home/toni/Documents/programme/localization/IPIN2016/competition/src/competition/map/UJI-UB/UJI-UB5.xml"); - //mapModel->load("/home/toni/Documents/programme/localization/IPIN2016/competition/src/competition/map/UJI-TI/UJI-TI4.xml"); + //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/CAR/CAR9.xml"); + mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/UAH/UAH9.xml"); //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/UJI-TI/UJI-TI4.xml"); - //mapModel->load("/home/toni/Documents/programme/localization/IPIN2016/competition/src/competition/map/UJI-UB/UJI-UB5.xml"); + //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/UJI-UB/UJI-UB4.xml"); //mapModel->load("/mnt/data/workspaces/Indoor/tests/data/WalkHeadingMap.xml"); //mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/test.xml"); - + //mapModel->startEmpty(); } @@ -130,6 +140,10 @@ void MainController::onMapModelAboutToReset() { mw->getMapView2D()->update(); } +void MainController::onMapModelNeedsRepaint() { + mw->getMapView2D()->update(); +} + void MainController::onMapModelReset() { mw->getTree()->expandAll(); } diff --git a/MainController.h b/MainController.h index 6d39d0c..77a2dca 100644 --- a/MainController.h +++ b/MainController.h @@ -4,19 +4,44 @@ #include #include #include "MainWindow.h" +#include "mapview/model/MapModelListener.h" class MapTreeModel; class MapModelElement; class MapModel; -class MainController : public QObject { +class ToolMoveMap; +class ToolRuler; +class ToolMapZoom; +class ToolMapGrid; +class ToolSelector; + +class MainController : public QObject, public MapModelListener { + Q_OBJECT + ToolMoveMap* moveMap; + ToolRuler* ruler; + ToolMapZoom* mapZoom; + ToolMapGrid* mapGrid; + ToolSelector* mapSelector; + + public: explicit MainController(); void show() {mw->show();} +public: + + void onLayerChanged(MapLayer* layer) override; + + void onLayerElementAdded(MapLayer* layer, MapModelElement* elem) override; + + void onLayerElementRemoved(MapLayer* layer, const MapModelElement* elem) override; + + void onLayerVisibilityChanged(MapLayer *layer, const bool visible) override; + signals: public slots: @@ -37,6 +62,7 @@ public slots: void onMapModelAboutToReset(); void onMapModelReset(); + void onMapModelNeedsRepaint(); private slots: diff --git a/MainWindow.cpp b/MainWindow.cpp index c77199b..6a6ce45 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -20,6 +20,7 @@ #include "params/LayerParamWidget.h" #include "params/ActionWidget.h" #include "params/ToolBoxWidget.h" +#include "params/LayerTree.h" #include "tree/MapTreeModel.h" @@ -39,7 +40,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi this->setStyleSheet(css); { - layerTree = new QTreeView(); + layerTree = new LayerTree(); QString css; //css += "QTreeView::item:selected:active {background-color: #888888;}"; //css += "QTreeView::item:selected:!active {background-color: #888888;}"; @@ -140,6 +141,3 @@ MainWindow::~MainWindow() { } -QTreeView* MainWindow::getTree() { - return layerTree; -} diff --git a/MainWindow.h b/MainWindow.h index 25ba600..6800b19 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -12,6 +12,7 @@ class ElementParamWidget; class LayerParamWidget; class ActionWidget; class ToolBoxWidget; +class LayerTree; namespace Ui { class MainWindow; @@ -32,9 +33,7 @@ public: LayerParamWidget* getLayerParamWidget() {return layerParamWidget;} ToolBoxWidget* getToolBoxWidget() {return toolBoxWidget;} ActionWidget* getActionWidget() {return actionWidget;} - - - QTreeView* getTree(); + LayerTree* getTree() {return layerTree;} signals: @@ -47,7 +46,7 @@ signals: private: Ui::MainWindow *ui; - QTreeView* layerTree; + LayerTree* layerTree; MapView2D* mapView2D; MapView3D* mapView3D; ElementParamWidget* elementParamWidget; diff --git a/main.cpp b/main.cpp index 8ded81e..2dc80b9 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,26 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); + QString str = R"( + + QDockWidget { + border: 1px solid #000; + } + + QDockWidget::title { + text-align: left; /* align the text to the left */ + background: #ccc; + padding: 1px; + } + + QDockWidget::title:hover { + background: #bbb; + } + + )"; + + a.setStyleSheet(str); + MainController mc; mc.show(); diff --git a/mapview/2D/ClickDist.h b/mapview/2D/ClickDist.h new file mode 100644 index 0000000..b8886b2 --- /dev/null +++ b/mapview/2D/ClickDist.h @@ -0,0 +1,59 @@ +#ifndef CLICKDIST_H +#define CLICKDIST_H + +enum class ClickDistType { + UNKNOWN, + DIRECT, // e.g. distance between cursor and an edge-point, POI, AP, Beacon, ... + CUT, // e.g. distance between cursor and an obstacle-line +}; + +/** describes distances between mouse-cursor and objects within the 2D view */ +struct ClickDist { + + /** distance in pixels */ + float dst_px; + + /** distance type */ + ClickDistType type; + + + /** ctor */ + ClickDist(const float dst_px, const ClickDistType type) : dst_px(dst_px), type(type) {;} + + /** max-dummy */ + static ClickDist max() {return ClickDist(9999999, ClickDistType::UNKNOWN);} + + + /** distance comparison */ + bool operator < (const ClickDist o) const { + return (this->dst_px * this->mod()) < (o.dst_px * o.mod()); + } + + /** multiply by constant */ + ClickDist operator * (const float val) const {return ClickDist(dst_px*val, type);} + +// /** weighted distance comparison based on ClickDistType */ +// bool compare(const ClickDist o) const { +// return ((*this)*mod()) < (o*o.mod()); +// } + +private: + + /** + * artificially modify the distance based on the given type. + * this enhances the selection in cases of equal distance + * like a POI residing dirctly one a line + */ + float mod() const { + switch (type) { + case ClickDistType::UNKNOWN: return 1.1; + case ClickDistType::CUT: return 1.0; + case ClickDistType::DIRECT: return 0.7; + default: throw "code error"; + } + } + +}; + + +#endif // CLICKDIST_H diff --git a/mapview/2D/MV2DElement.h b/mapview/2D/MV2DElement.h index 32cf049..6785b57 100644 --- a/mapview/2D/MV2DElement.h +++ b/mapview/2D/MV2DElement.h @@ -9,6 +9,8 @@ #include #include +#include "ClickDist.h" + /** * represents one drawable, selectable, editable, ... * element shown within the MapView2D @@ -29,7 +31,7 @@ public: 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; + virtual ClickDist getMinDistanceXY(const Point2 p) const = 0; /** repaint me */ virtual void paint(Painter& p) = 0; diff --git a/mapview/2D/MV2DElementAccessPoint.h b/mapview/2D/MV2DElementAccessPoint.h index a9d3c37..8c6b132 100644 --- a/mapview/2D/MV2DElementAccessPoint.h +++ b/mapview/2D/MV2DElementAccessPoint.h @@ -31,8 +31,8 @@ public: } /** 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()); + ClickDist getMinDistanceXY(const Point2 p) const override { + return ClickDist(p.getDistance(ap->pos.xy()), ClickDistType::DIRECT); } /** repaint me */ @@ -61,7 +61,7 @@ public: p.setPenBrush(Qt::black, Qt::NoBrush); p.drawDot(ap->pos.xy()); if (p.getScaler().getScale() >= 25) { - const std::string str = ap->name + " (" + ap->name + ")"; + const std::string str = ap->name + " (" + ap->mac + ")"; p.p->drawText(p.getScaler().xms(ap->pos.x) + 10, p.getScaler().yms(ap->pos.y) + 5, str.c_str()); } else if (p.getScaler().getScale() >= 10) { const std::string str = ap->name; diff --git a/mapview/2D/MV2DElementBeacon.h b/mapview/2D/MV2DElementBeacon.h index 34503d3..d836def 100644 --- a/mapview/2D/MV2DElementBeacon.h +++ b/mapview/2D/MV2DElementBeacon.h @@ -29,8 +29,8 @@ public: } /** 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()); + ClickDist getMinDistanceXY(const Point2 p) const override { + return ClickDist(p.getDistance(b->pos.xy()), ClickDistType::DIRECT); } /** repaint me */ diff --git a/mapview/2D/MV2DElementElevator.h b/mapview/2D/MV2DElementElevator.h index 3533ef8..aedd7b1 100644 --- a/mapview/2D/MV2DElementElevator.h +++ b/mapview/2D/MV2DElementElevator.h @@ -30,11 +30,11 @@ public: } /** get the element's minimal distance (nearest whatsoever) to the given point */ - float getMinDistanceXY(const Point2 p) const override { + ClickDist getMinDistanceXY(const Point2 p) const override { // std::vector points = elevator->getPoints().points; // points.push_back(elevator->center); // auto it minEl = std::min_element(points.begin(), points.end(), - return p.getDistance(elevator->center); + return ClickDist(p.getDistance(elevator->center), ClickDistType::DIRECT); } /** repaint me */ diff --git a/mapview/2D/MV2DElementFloorObstacleCircle.h b/mapview/2D/MV2DElementFloorObstacleCircle.h index f8c7bf4..cb33604 100644 --- a/mapview/2D/MV2DElementFloorObstacleCircle.h +++ b/mapview/2D/MV2DElementFloorObstacleCircle.h @@ -27,8 +27,11 @@ public: } /** 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])); + ClickDist getMinDistanceXY(const Point2 p) const override { + const ClickDist d1(p.getDistance(getSelPoints()[0]), ClickDistType::DIRECT); + const ClickDist d2(p.getDistance(getSelPoints()[1]), ClickDistType::DIRECT); + return std::min(d1, d2); + //return std::min(p.getDistance(getSelPoints()[0]), p.getDistance(getSelPoints()[1])); } diff --git a/mapview/2D/MV2DElementFloorObstacleDoor.h b/mapview/2D/MV2DElementFloorObstacleDoor.h index d219054..63d2880 100644 --- a/mapview/2D/MV2DElementFloorObstacleDoor.h +++ b/mapview/2D/MV2DElementFloorObstacleDoor.h @@ -27,7 +27,7 @@ public: } /** get the element's minimal distance (nearest whatsoever) to the given point */ - float getMinDistanceXY(const Point2 p) const override { + ClickDist getMinDistanceXY(const Point2 p) const override { return MapElementHelper::getLineDistanceXY(fo->from, fo->to, p); } @@ -69,6 +69,12 @@ public: p.drawCircle(fo->to); } + // obstacle length + if (hasFocus()) { + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawLength(fo->from, fo->to, fo->from.getDistance(fo->to)); + } + } diff --git a/mapview/2D/MV2DElementFloorObstacleLine.h b/mapview/2D/MV2DElementFloorObstacleLine.h index f415afd..ac515bf 100644 --- a/mapview/2D/MV2DElementFloorObstacleLine.h +++ b/mapview/2D/MV2DElementFloorObstacleLine.h @@ -6,6 +6,7 @@ #include "MapViewElementHelper.h" #include +#include class MV2DElementFloorObstacleLine : public MV2DElement, public HasMoveableNodes { @@ -27,7 +28,7 @@ public: } /** get the element's minimal distance (nearest whatsoever) to the given point */ - float getMinDistanceXY(const Point2 p) const override { + ClickDist getMinDistanceXY(const Point2 p) const override { return MapElementHelper::getLineDistanceXY(fo->from, fo->to, p); } @@ -60,33 +61,49 @@ public: p.setPenBrush(Qt::black, Qt::NoBrush); p.drawCircle(fo->from); p.drawCircle(fo->to); + + // obstacle length + p.setPenBrush(Qt::black, Qt::NoBrush); + p.drawLength(fo->from, fo->to, fo->from.getDistance(fo->to)); + + } else { + //p.setPenBrush(Qt::NoPen, Qt::black); + p.drawCircle_px(fo->from, 3); + p.drawCircle_px(fo->to, 3); } - } - - void paintDoor(Painter& p) { - - QPen pen; - pen.setColor(QColor(0.5,0.5,0.5)); - pen.setStyle(Qt::PenStyle::DotLine); - p.setPenBrush(pen, Qt::NoBrush); - // opening indicator - const float open = M_PI / 4; - const float len = (fo->to - fo->from).length(); - const float angle1 = std::atan2(fo->to.y-fo->from.y, fo->to.x-fo->from.x); - const float angle2 = angle1 + open; - const Point2 pOpen = Point2( std::cos(angle2) * len, std::sin(angle2) * len ) + fo->from; - - p.drawLine(fo->from, fo->to); - p.drawLine(fo->from, pOpen); - - p.drawArc(fo->from, len, angle1, open); - //p.drawLine(fo->to, pOpen); } +// void paintDoor(Painter& p) { + +// QPen pen; +// pen.setColor(QColor(0.5,0.5,0.5)); +// pen.setStyle(Qt::PenStyle::DotLine); +// p.setPenBrush(pen, Qt::NoBrush); + + +// // opening indicator +// const float open = M_PI / 4; +// const float len = (fo->to - fo->from).length(); +// const float angle1 = std::atan2(fo->to.y-fo->from.y, fo->to.x-fo->from.x); +// const float angle2 = angle1 + open; +// const Point2 pOpen = Point2( std::cos(angle2) * len, std::sin(angle2) * len ) + fo->from; + +// p.drawLine(fo->from, fo->to); +// p.drawLine(fo->from, pOpen); + +// p.drawArc(fo->from, len, angle1, open); +// //p.drawLine(fo->to, pOpen); + +// // obstacle length +// p.setPenBrush(Qt::black, Qt::NoBrush); +// p.drawLength(fo->from, fo->to, fo->from.getDistance(fo->to)); + +// } + void onFocus() override { ; } diff --git a/mapview/2D/MV2DElementFloorOutlinePolygon.h b/mapview/2D/MV2DElementFloorOutlinePolygon.h index f9bd0a4..aaa655b 100644 --- a/mapview/2D/MV2DElementFloorOutlinePolygon.h +++ b/mapview/2D/MV2DElementFloorOutlinePolygon.h @@ -26,12 +26,12 @@ public: } /** get the element's minimal distance (nearest whatsoever) to the given point */ - float getMinDistanceXY(const Point2 p) const override { - float min = 999999; + ClickDist getMinDistanceXY(const Point2 p) const override { + ClickDist min = ClickDist::max(); 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); + const ClickDist dst = MapElementHelper::getLineDistanceXY(p1, p2, p); if (dst < min) {min = dst;} } return min; diff --git a/mapview/2D/MV2DElementFloorUnderlay.h b/mapview/2D/MV2DElementFloorUnderlay.h index 79a3d1a..bf324dd 100644 --- a/mapview/2D/MV2DElementFloorUnderlay.h +++ b/mapview/2D/MV2DElementFloorUnderlay.h @@ -42,9 +42,9 @@ public: } /** get the element's minimal distance (nearest whatsoever) to the given point */ - float getMinDistanceXY(const Point2 p) const override { + ClickDist getMinDistanceXY(const Point2 p) const override { (void) p; - return CFG::SEL_THRESHOLD_SIZE_PX; // we do not know the distance from the image + return ClickDist(CFG::SEL_THRESHOLD_SIZE_PX, ClickDistType::UNKNOWN); // we do not know the distance from the image } virtual void onFocus() override { diff --git a/mapview/2D/MV2DElementPOI.h b/mapview/2D/MV2DElementPOI.h index 839e2ad..aee6d92 100644 --- a/mapview/2D/MV2DElementPOI.h +++ b/mapview/2D/MV2DElementPOI.h @@ -30,8 +30,8 @@ public: } /** get the element's minimal distance (nearest whatsoever) to the given point */ - float getMinDistanceXY(const Point2 p) const override { - return p.getDistance(poi->pos); + ClickDist getMinDistanceXY(const Point2 p) const override { + return ClickDist(p.getDistance(poi->pos), ClickDistType::DIRECT); } /** repaint me */ diff --git a/mapview/2D/MV2DElementStair.h b/mapview/2D/MV2DElementStair.h index 05c196f..2dc2962 100644 --- a/mapview/2D/MV2DElementStair.h +++ b/mapview/2D/MV2DElementStair.h @@ -35,11 +35,11 @@ public: } /** get the element's minimal distance (nearest whatsoever) to the given point */ - float getMinDistanceXY(const Point2 p) const override { + ClickDist getMinDistanceXY(const Point2 p) const override { auto comp = [p] (const Floorplan::StairPart& p1, const Floorplan::StairPart& p2) { - const float d1 = MapElementHelper::getLineDistanceXY(p1.start.xy(), p1.end.xy(), p); - const float d2 = MapElementHelper::getLineDistanceXY(p2.start.xy(), p2.end.xy(), p); + const ClickDist d1 = MapElementHelper::getLineDistanceXY(p1.start.xy(), p1.end.xy(), p); + const ClickDist d2 = MapElementHelper::getLineDistanceXY(p2.start.xy(), p2.end.xy(), p); return d1 < d2; }; diff --git a/mapview/2D/MapViewElementHelper.h b/mapview/2D/MapViewElementHelper.h index 6ad31ba..f2c76d2 100644 --- a/mapview/2D/MapViewElementHelper.h +++ b/mapview/2D/MapViewElementHelper.h @@ -11,6 +11,8 @@ #include +#include "ClickDist.h" + /** configuration */ namespace CFG { const float MOVE_SNAP_SIZE_M = 0.1f; // in meter (= map-space) @@ -33,7 +35,7 @@ public: * move l into dst * and calculate the cut-point between l and (p1, p2) */ - static float getLineDistanceXY(Point2 p1, Point2 p2, Point2 dst) { + static ClickDist getLineDistanceXY(Point2 p1, Point2 p2, Point2 dst) { // the line (p1, p2) const Line2 line(p1, p2); @@ -46,20 +48,21 @@ public: // calculate the cut betwen L and (p1,p2) (if any) Point2 cut(0,0); + ClickDist cutDist(99999999, ClickDistType::CUT); if (line.getSegmentIntersection(perb, cut)) { // distance between cut-point and mouse - return cut.getDistance(dst); - - } else { - - // no cut detected - const float d1 = p1.getDistance(dst); - const float d2 = p2.getDistance(dst); - return std::min(d1, d2); + cutDist.dst_px = cut.getDistance(dst); } + // distance from endpoints + const ClickDist d1(p1.getDistance(dst), ClickDistType::DIRECT); + const ClickDist d2(p2.getDistance(dst), ClickDistType::DIRECT); + + // return the nearest possibility: + return std::min(d1, std::min(d2, cutDist)); + } static QPen getPen(Floorplan::Material mat, Floorplan::ObstacleType type, bool focus) { diff --git a/mapview/2D/Painter.h b/mapview/2D/Painter.h index b2a9f07..1f4ebac 100644 --- a/mapview/2D/Painter.h +++ b/mapview/2D/Painter.h @@ -69,6 +69,11 @@ public: p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r); } + void drawCircle_px(const Point2 center, const float size_px) { + int r = size_px; + 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)); } @@ -115,6 +120,16 @@ public: p->drawImage(s.xms(pt.x)-img.width()/2, s.yms(pt.y)-img.height()/2, img); } + void drawLength(Point2 p1, Point2 p2, const float len) { + if (p1.x < p2.x) {swap(p1, p2);} + const Point2 center_m = (p1 + p2) / 2; + Point2 dir_px = (p2 - p1).perpendicular().normalized() * 5; + if (dir_px.x <= 0) {dir_px = -dir_px;} + const Point2 pos_m = center_m + dir_px / getScaler().getScale(); + char buf[64]; sprintf(buf, "%.1f", len); + drawText(pos_m, buf); + } + void setBrush(const QBrush& brush) { p->setBrush(brush); } void setBrush(const Qt::BrushStyle& brush) { p->setBrush(brush); } diff --git a/mapview/2D/tools/ToolMapZoom.h b/mapview/2D/tools/ToolMapZoom.h index d8468c5..f5fb4c5 100644 --- a/mapview/2D/tools/ToolMapZoom.h +++ b/mapview/2D/tools/ToolMapZoom.h @@ -20,9 +20,9 @@ public: Scaler& s = m->getScaler(); if (e->delta() < 0) { - s.setScale(s.getScale() * 0.5); + s.setScale(s.getScale() * 0.75); } else { - s.setScale(s.getScale() / 0.5); + s.setScale(s.getScale() / 0.75); } if (s.getScale() > 1000) {s.setScale(1000);} diff --git a/mapview/2D/tools/ToolMeasure.cpp b/mapview/2D/tools/ToolMeasure.cpp new file mode 100644 index 0000000..e69de29 diff --git a/mapview/2D/tools/ToolMeasure.h b/mapview/2D/tools/ToolMeasure.h new file mode 100644 index 0000000..1806c7f --- /dev/null +++ b/mapview/2D/tools/ToolMeasure.h @@ -0,0 +1,132 @@ +#ifndef TOOLMEASURE_H +#define TOOLMEASURE_H + + +#include "Tool.h" +#include "../MapView2D.h" + +#include "../../model/MapModelElement.h" +#include "../../model/MapModel.h" +#include "../MapViewElementHelper.h" + + +/** + * this tool allows: + * - selecting elements within the 2D view (focus/unfocus) + * - selecting and moving nodes of elements inheriting from HasMoveableNodes + * + */ +class ToolMeasure : public Tool { + + Q_OBJECT + + +protected: + + /** register this tool into the given tools-queue */ + Tools& tools; + Tool* oldMainTool; + + std::vector pts_m; + +public: + + /** ctor */ + ToolMeasure(Tools& tools) : tools(tools) { + + oldMainTool = tools.getMain(); // keep the current tool to reset it later + tools.setMain(this); + + resetMe(); + + } + + /** dtor */ + virtual ~ToolMeasure() { + tools.setMain(oldMainTool); // reset to the previous tool + } + + const std::string getName() const { + return "Measure"; + } + + virtual bool mousePressEvent(MapView2D*, QMouseEvent* e) override { + if (e->button() == Qt::MouseButton::LeftButton) { + pts_m.push_back(pts_m.back()); + return true; + } else { + return false; + } + } + + virtual bool mouseMoveEvent(MapView2D* m, QMouseEvent* e) override { + const Point2 onScreen(e->x(), e->y()); + Point2 onMap = m->getScaler().sm(onScreen); + onMap = m->getScaler().snap(onMap); + pts_m.back() = onMap; + return true; + } + + virtual bool mouseReleaseEvent(MapView2D*, QMouseEvent* e) override { + if (e->button() == Qt::MouseButton::LeftButton) { + return true; + } else if (e->button() == Qt::MouseButton::RightButton) { + resetMe(); + return true; + } else { + return false; + } + } + + virtual bool keyPressEvent(MapView2D* m, QKeyEvent* e) override { + (void) m; + if (e->key() == Qt::Key_Escape) { + disableMe(); + return true; + } + return false; + } + + virtual void paintAfter(MapView2D*, Painter& p) override { + + if (pts_m.size() < 1) {return;} + + p.setPen(Qt::black); + + for (const Point2 p_m : pts_m) { + p.drawCircle(p_m); + } + + float totalLen_m = 0; + for (size_t i = 0; i < pts_m.size() - 1; ++i) { + const Point2 p1 = pts_m[i]; + const Point2 p2 = pts_m[i+1]; + const float len_m = p1.getDistance(p2); + p.drawLine(p1, p2); + p.drawLength(p1, p2, len_m); + totalLen_m += len_m; + } + + if (pts_m.size() > 1) { + emit onHelpTextChange("total length is: " + QString::number(totalLen_m) + "m | right-click to restart"); + } + + } + +protected: + + + void resetMe() { + pts_m.resize(1); + emit onHelpTextChange("select the starting point for measuring"); + } + + /** finish creating new elements */ + void disableMe() { + delete this; // see dtor! + } + +}; + + +#endif // TOOLMEASURE_H diff --git a/mapview/2D/tools/ToolRuler.h b/mapview/2D/tools/ToolRuler.h index 2adbaf1..ce1ee7d 100644 --- a/mapview/2D/tools/ToolRuler.h +++ b/mapview/2D/tools/ToolRuler.h @@ -96,9 +96,19 @@ public: p.p->drawLine(mouseX, 0, mouseX, rs); + + p.setPenBrush(Qt::black, Qt::NoBrush); char buf[128]; + // coordinates + QRect ru(0,0,rs-1,rs-1); + p.p->fillRect(ru, Qt::white); + std::sprintf(buf, "%.1f", p.getScaler().xsm(this->mouseX)); + p.p->drawText(5,15, buf); + std::sprintf(buf, "%.1f", p.getScaler().ysm(this->mouseY)); + p.p->drawText(5,30, buf); + // y-axis p.p->setClipRect(ry); for (float y = r.y0; y <= r.y1; y += step) { @@ -141,6 +151,8 @@ public: p.p->setClipping(false); + + // snapped dot const Point2 mouseOnScreen(mouseX, mouseY); const Point2 mouseInMap = p.s.sm(mouseOnScreen); diff --git a/mapview/2D/tools/ToolSelector.h b/mapview/2D/tools/ToolSelector.h index 054f022..07d09b1 100644 --- a/mapview/2D/tools/ToolSelector.h +++ b/mapview/2D/tools/ToolSelector.h @@ -46,6 +46,15 @@ public: setFocused(m, nullptr); } + /** + * @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 focus(MapView2D* v, MapModelElement* el) { + setFocused(v, el); + } + private: void processUnfocus(MapView2D* m, MapModelElement* elem) { @@ -202,9 +211,11 @@ private: const float g = m->getScaler().sm(15); // increase each BBox by 15 px (needed mainly for hor/ver lines) +#warning "which elements to select? among all currently visible? or only among the selected layer?" // get all elements with bounding-box matchings std::vector possible; - for (MapModelElement* el : m->getModel()->getSelectedLayerElements()) { +// for (MapModelElement* el : m->getModel()->getSelectedLayerElements()) { + for (MapModelElement* el : m->getModel()->getVisibleElements()) { if (!el->getMV2D()) {continue;} BBox2 bbox = el->getMV2D()->getBoundingBox(); // elements 2D bbox bbox.grow(Point2(g, g)); // grow a little (needed for straight lines) diff --git a/mapview/3D/MV3DElementAccessPoint.h b/mapview/3D/MV3DElementAccessPoint.h index 1d87fb9..45af97e 100644 --- a/mapview/3D/MV3DElementAccessPoint.h +++ b/mapview/3D/MV3DElementAccessPoint.h @@ -24,7 +24,7 @@ protected: /** repaint me */ void paintGL() override { - Cube cube(ap->getPos(f), 0.5); + Cube cube(ap->getPos(f), 0.25); cube.paintGL(); } diff --git a/mapview/3D/MV3DElementFloorOutlinePolygon.h b/mapview/3D/MV3DElementFloorOutlinePolygon.h index 5b63c6e..00ad604 100644 --- a/mapview/3D/MV3DElementFloorOutlinePolygon.h +++ b/mapview/3D/MV3DElementFloorOutlinePolygon.h @@ -31,7 +31,7 @@ protected: glColor3f(1,1,1); break; case Floorplan::OutlineMethod::REMOVE: - glColor3f(0.2, 0.2, 0.2); + glColor3f(0.3, 0.3, 0.3); break; } diff --git a/mapview/3D/MapView3D.cpp b/mapview/3D/MapView3D.cpp index 8c762bd..454da88 100644 --- a/mapview/3D/MapView3D.cpp +++ b/mapview/3D/MapView3D.cpp @@ -32,11 +32,12 @@ void MapView3D::initializeGL() { QGLWidget::initializeGL(); - //glCullFace(GL_FRONT); + glCullFace(GL_FRONT); + //glDisable(GL_CULL_FACE); + glEnable(GL_CULL_FACE); // culling, lighting, depth-test, ... glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); glEnable(GL_MULTISAMPLE); diff --git a/mapview/model/IHasEditableMeta.h b/mapview/model/IHasEditableMeta.h new file mode 100644 index 0000000..e5a6d83 --- /dev/null +++ b/mapview/model/IHasEditableMeta.h @@ -0,0 +1,21 @@ +#ifndef IHASEDITABLEMETA_H +#define IHASEDITABLEMETA_H + +#include + +/** + * interface for all classes that provide editable meta information + */ +class IHasEditableMeta { + +public: + + /** get the meta-information object [if any] */ + virtual Floorplan::Meta* getMeta() = 0; + + /** set/overwrite the meta-information object */ + virtual void setMeta(Floorplan::Meta* meta) = 0; + +}; + +#endif // IHASEDITABLEMETA_H diff --git a/mapview/model/MMFloor.h b/mapview/model/MMFloor.h index acad39b..b060e16 100644 --- a/mapview/model/MMFloor.h +++ b/mapview/model/MMFloor.h @@ -26,16 +26,18 @@ class MMFloor : public MapLayer, public IHasParams { private: + Floorplan::IndoorMap* map; + /** the underlying data-structure */ Floorplan::Floor* floor; public: /** ctor. existing floor */ - MMFloor(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR), floor(floor) { + MMFloor(MapLayer* parent, Floorplan::IndoorMap* map, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR), map(map), floor(floor) { new MMFloorUnderlays(this, floor); - elements.push_back(new MMFloorOutline(this, floor)); + new MMFloorOutline(this, floor); new MMFloorObstacles(this, floor); new MMFloorAccessPoints(this, floor); new MMFloorBeacons(this, floor); @@ -58,12 +60,14 @@ public: std::string getLayerName() const override {return floor->name;} virtual int getNumParams() const override { - return 1; + return 3; } virtual Param getParamDesc(const int idx) const override { switch(idx) { - case 0: return Param("anem", ParamType::STRING); + case 0: return Param("name", ParamType::STRING); + case 1: return Param("height", ParamType::FLOAT); + case 2: return Param("at height", ParamType::FLOAT); } throw 1; } @@ -71,6 +75,8 @@ public: virtual ParamValue getParamValue(const int idx) const override { switch(idx) { case 0: return floor->name; + case 1: return floor->height; + case 2: return floor->atHeight; } throw 1; } @@ -78,9 +84,16 @@ public: virtual void setParamValue(const int idx, const ParamValue& val) const override { switch(idx) { case 0: floor->name = val.toString(); break; + case 1: floor->height = val.toFloat(); break; + case 2: floor->atHeight = val.toFloat(); break; } } + void deleteMe() { + parent->removeSublayer(this); + map->floors.erase(std::remove(map->floors.begin(), map->floors.end(), floor), map->floors.end()); + } + }; #endif // MMFLOOR_H diff --git a/mapview/model/MMFloorAccessPoint.h b/mapview/model/MMFloorAccessPoint.h index b364956..f71ab5c 100644 --- a/mapview/model/MMFloorAccessPoint.h +++ b/mapview/model/MMFloorAccessPoint.h @@ -3,13 +3,14 @@ #include "MapModelElement.h" #include "IHasParams.h" +#include "IHasEditableMeta.h" #include "../2D/MV2DElementAccessPoint.h" #include "../3D/MV3DElementAccessPoint.h" #include -class MMFloorAccessPoint : public MapModelElement, public IHasParams { +class MMFloorAccessPoint : public MapModelElement, public IHasParams, public IHasEditableMeta { private: @@ -26,7 +27,7 @@ public: } virtual int getNumParams() const override { - return 3; + return 6; } virtual Param getParamDesc(const int idx) const override { @@ -34,6 +35,9 @@ public: case 0: return Param("name", ParamType::STRING); case 1: return Param("MAC", ParamType::STRING); case 2: return Param("Position", ParamType::POINT3); + case 3: return Param("TXP", ParamType::FLOAT); + case 4: return Param("EXP", ParamType::FLOAT); + case 5: return Param("WAF", ParamType::FLOAT); } throw 1; } @@ -43,6 +47,9 @@ public: case 0: return ap->name; case 1: return ap->mac; case 2: return ap->pos; + case 3: return ap->model.txp; + case 4: return ap->model.exp; + case 5: return ap->model.waf; } throw 1; } @@ -52,9 +59,20 @@ public: case 0: ap->name = val.toString(); break; case 1: ap->mac = val.toString(); break; case 2: ap->pos = val.toPoint3(); break; + case 3: ap->model.txp = val.toFloat(); break; + case 4: ap->model.exp = val.toFloat(); break; + case 5: ap->model.waf = val.toFloat(); break; } } + virtual Floorplan::Meta* getMeta() override { + return ap->getMeta(); + } + + virtual void setMeta(Floorplan::Meta* meta) override { + ap->setMeta(meta); + } + MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;} MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} diff --git a/mapview/model/MMFloorAccessPoints.h b/mapview/model/MMFloorAccessPoints.h index be64440..fb4c89c 100644 --- a/mapview/model/MMFloorAccessPoints.h +++ b/mapview/model/MMFloorAccessPoints.h @@ -22,7 +22,7 @@ public: // add all APs for (Floorplan::AccessPoint* ap : floor->accesspoints) { - elements.push_back(new MMFloorAccessPoint(this, floor, ap)); + addElement(new MMFloorAccessPoint(this, floor, ap)); } } @@ -36,7 +36,7 @@ public: floor->accesspoints.push_back(ap); // add to myself as element - elements.push_back(new MMFloorAccessPoint(this, floor, ap)); + addElement(new MMFloorAccessPoint(this, floor, ap)); } diff --git a/mapview/model/MMFloorBeacon.h b/mapview/model/MMFloorBeacon.h index eb6f078..fffe8a8 100644 --- a/mapview/model/MMFloorBeacon.h +++ b/mapview/model/MMFloorBeacon.h @@ -23,14 +23,20 @@ public: } virtual int getNumParams() const override { - return 3; + return 9; } virtual Param getParamDesc(const int idx) const override { switch(idx) { case 0: return Param("name", ParamType::STRING); - case 1: return Param("MAC", ParamType::STRING); - case 2: return Param("Position", ParamType::POINT3); + case 1: return Param("Position", ParamType::POINT3); + case 2: return Param("MAC", ParamType::STRING); + case 3: return Param("major", ParamType::STRING); + case 4: return Param("minor", ParamType::STRING); + case 5: return Param("UUDI", ParamType::STRING); + case 6: return Param("TXP", ParamType::FLOAT); + case 7: return Param("EXP", ParamType::FLOAT); + case 8: return Param("WAF", ParamType::FLOAT); } throw 1; } @@ -38,8 +44,14 @@ public: virtual ParamValue getParamValue(const int idx) const override { switch(idx) { case 0: return b->name; - case 1: return b->mac; - case 2: return b->pos; + case 1: return b->pos; + case 2: return b->mac; + case 3: return b->major; + case 4: return b->minor; + case 5: return b->uuid; + case 6: return b->model.txp; + case 7: return b->model.exp; + case 8: return b->model.waf; } throw 1; } @@ -47,8 +59,14 @@ public: virtual void setParamValue(const int idx, const ParamValue& val) const override { switch(idx) { case 0: b->name = val.toString(); break; - case 1: b->mac = val.toString(); break; - case 2: b->pos = val.toPoint3(); break; + case 1: b->pos = val.toPoint3(); break; + case 2: b->mac = val.toString(); break; + case 3: b->major = val.toString(); break; + case 4: b->minor = val.toString(); break; + case 5: b->uuid = val.toString(); break; + case 6: b->uuid = val.toFloat(); break; + case 7: b->uuid = val.toFloat(); break; + case 8: b->uuid = val.toFloat(); break; } } diff --git a/mapview/model/MMFloorBeacons.h b/mapview/model/MMFloorBeacons.h index 6c45b4f..af2079d 100644 --- a/mapview/model/MMFloorBeacons.h +++ b/mapview/model/MMFloorBeacons.h @@ -22,7 +22,7 @@ public: // add all Beacons for (Floorplan::Beacon* b : floor->beacons) { - elements.push_back(new MMFloorBeacon(this, floor, b)); + addElement(new MMFloorBeacon(this, floor, b)); } } @@ -36,7 +36,7 @@ public: floor->beacons.push_back(b); // add to myself as element - elements.push_back(new MMFloorBeacon(this, floor, b)); + addElement(new MMFloorBeacon(this, floor, b)); } }; diff --git a/mapview/model/MMFloorElevators.h b/mapview/model/MMFloorElevators.h index 7212d54..9b40b26 100644 --- a/mapview/model/MMFloorElevators.h +++ b/mapview/model/MMFloorElevators.h @@ -23,7 +23,7 @@ public: // add all elevators for (Floorplan::Elevator* elevator : floor->elevators) { - elements.push_back(new MMFloorElevator(this, floor, elevator)); + addElement(new MMFloorElevator(this, floor, elevator)); } } @@ -37,7 +37,7 @@ public: floor->elevators.push_back(elevator); // add to myself as element - elements.push_back(new MMFloorElevator(this, floor, elevator)); + addElement(new MMFloorElevator(this, floor, elevator)); } diff --git a/mapview/model/MMFloorObstacleLine.h b/mapview/model/MMFloorObstacleLine.h index e36773a..31c165e 100644 --- a/mapview/model/MMFloorObstacleLine.h +++ b/mapview/model/MMFloorObstacleLine.h @@ -6,6 +6,7 @@ #include "IHasMaterial.h" #include "IHasObstacleType.h" +#include "IHasParams.h" #include "../2D/MV2DElementFloorObstacleLine.h" #include "../3D/MV3DElementFloorObstacleWall.h" @@ -13,7 +14,7 @@ #include -class MMFloorObstacleLine : public MapModelElement, public IHasMaterial, public IHasObstacleType { +class MMFloorObstacleLine : public MapModelElement, public IHasMaterial, public IHasObstacleType, public IHasParams { public: @@ -43,6 +44,31 @@ public: mf->obstacles.erase(std::remove(mf->obstacles.begin(), mf->obstacles.end(), fo), mf->obstacles.end()); } + + virtual int getNumParams() const override { + return 1; + } + + virtual Param getParamDesc(const int idx) const override { + switch(idx) { + case 0: return Param("length", ParamType::FLOAT, true); + } + throw 1; + } + + virtual ParamValue getParamValue(const int idx) const override { + switch(idx) { + case 0: return fo->from.getDistance(fo->to); + } + throw 1; + } + + virtual void setParamValue(const int idx, const ParamValue& val) const override { + switch(idx) { + case 0: break; + } + } + }; #endif // MAPELEMENTOBSTACLE_H diff --git a/mapview/model/MMFloorObstacles.h b/mapview/model/MMFloorObstacles.h index c03c32a..02dac5e 100644 --- a/mapview/model/MMFloorObstacles.h +++ b/mapview/model/MMFloorObstacles.h @@ -23,11 +23,11 @@ public: // the obstacles for (Floorplan::FloorObstacle* o : floor->obstacles) { if (dynamic_cast(o)) { - elements.push_back(new MMFloorObstacleLine(this, floor, (Floorplan::FloorObstacleLine*)o)); + addElement(new MMFloorObstacleLine(this, floor, (Floorplan::FloorObstacleLine*)o)); } else if (dynamic_cast(o)) { - elements.push_back(new MMFloorObstacleCircle(this, floor, (Floorplan::FloorObstacleCircle*)o)); + addElement(new MMFloorObstacleCircle(this, floor, (Floorplan::FloorObstacleCircle*)o)); } else if (dynamic_cast(o)) { - elements.push_back(new MMFloorObstacleDoor(this, floor, (Floorplan::FloorObstacleDoor*)o)); + addElement(new MMFloorObstacleDoor(this, floor, (Floorplan::FloorObstacleDoor*)o)); } else { throw new Exception("todo: not yet implemented obstacle type"); } @@ -46,7 +46,7 @@ public: // add to myself as element MMFloorObstacleDoor* mm = new MMFloorObstacleDoor(this, floor, obs); - elements.push_back(mm); + addElement(mm); return mm; } @@ -59,7 +59,7 @@ public: // add to myself as element MMFloorObstacleLine* mm = new MMFloorObstacleLine(this, floor, obs); - elements.push_back(mm); + addElement(mm); return mm; } @@ -71,7 +71,7 @@ public: floor->obstacles.push_back(obs); // add to myself as element - elements.push_back(new MMFloorObstacleCircle(this, floor, obs)); + addElement(new MMFloorObstacleCircle(this, floor, obs)); } diff --git a/mapview/model/MMFloorOutline.h b/mapview/model/MMFloorOutline.h index e74d1a4..c66ee58 100644 --- a/mapview/model/MMFloorOutline.h +++ b/mapview/model/MMFloorOutline.h @@ -3,37 +3,38 @@ #include "MapLayer.h" #include "MMFloorOutlinePolygon.h" -#include "../3D/MV3DElementFloorOutline.h" +#include "MMFloorOutlinePolygonCombined.h" +#include "../3D/MV3DElementFloorOutline.h" #include /** * layer containing all elements describing a floor's outline */ -class MMFloorOutline : public MapLayer, public MapModelElement { +class MMFloorOutline : public MapLayer { private: /** the underlying model */ Floorplan::Floor* floor; - MV3DElementFloorOutline mv3d; public: /** ctor with the underlying model */ MMFloorOutline(MapLayer* parent, Floorplan::Floor* floor) : - MapLayer(parent, MapLayerType::FLOOR_GROUND), MapModelElement(parent), floor(floor), mv3d(floor, &floor->outline) { + MapLayer(parent, MapLayerType::FLOOR_GROUND), floor(floor) { - // the outline + // each polygon that is part of the outline for (Floorplan::FloorOutlinePolygon* poly : floor->outline) { - elements.push_back(new MMFloorOutlinePolygon(this, floor, poly)); + addElement(new MMFloorOutlinePolygon(this, floor, poly)); } - } + // for 3D, alle polygons [add/remove] are combined into one renderable polygons + addElement(new MMFloorOutlinePolygonCombined(this, floor)); - MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} + } /** get the corresponding floor from the underlying model */ @@ -46,7 +47,7 @@ public: floor->outline.push_back(poly); // add to myself as element - elements.push_back(new MMFloorOutlinePolygon(this, floor, poly)); + addElement(new MMFloorOutlinePolygon(this, floor, poly)); } diff --git a/mapview/model/MMFloorOutlinePolygonCombined.h b/mapview/model/MMFloorOutlinePolygonCombined.h new file mode 100644 index 0000000..7fa59a7 --- /dev/null +++ b/mapview/model/MMFloorOutlinePolygonCombined.h @@ -0,0 +1,41 @@ +#ifndef MMFLOOROUTLINEPOLYGONCOMBINED_H +#define MMFLOOROUTLINEPOLYGONCOMBINED_H + +#include "MapLayer.h" +#include "MMFloorOutlinePolygon.h" +#include "../3D/MV3DElementFloorOutline.h" + + +#include + +/** + * this element combines all polygons of one layer + * into one large, 3D renderable polygon + */ +class MMFloorOutlinePolygonCombined : public MapModelElement { + +private: + + /** the underlying model */ + Floorplan::Floor* floor; + + MV3DElementFloorOutline mv3d; + +public: + + /** ctor with the underlying model */ + MMFloorOutlinePolygonCombined(MapLayer* parent, Floorplan::Floor* floor) : + MapModelElement(parent), floor(floor), mv3d(floor, &floor->outline) { + + ; + + } + + MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;} + + /** get the corresponding floor from the underlying model */ + Floorplan::Floor* getFloor() {return floor;} + +}; + +#endif // MMFLOOROUTLINEPOLYGONCOMBINED_H diff --git a/mapview/model/MMFloorPOIs.h b/mapview/model/MMFloorPOIs.h index efaa8d7..7b06945 100644 --- a/mapview/model/MMFloorPOIs.h +++ b/mapview/model/MMFloorPOIs.h @@ -20,7 +20,7 @@ public: // the POIs for (Floorplan::POI* poi : floor->pois) { - elements.push_back(new MMFloorPOI(this, floor, poi)); + addElement(new MMFloorPOI(this, floor, poi)); } } @@ -35,7 +35,7 @@ public: floor->pois.push_back(poi); // add to myself as element - elements.push_back(new MMFloorPOI(this, floor, poi)); + addElement(new MMFloorPOI(this, floor, poi)); } diff --git a/mapview/model/MMFloorStairs.h b/mapview/model/MMFloorStairs.h index ace1fce..1d81c7a 100644 --- a/mapview/model/MMFloorStairs.h +++ b/mapview/model/MMFloorStairs.h @@ -23,7 +23,7 @@ public: // add all floors for (Floorplan::Stair* stair : floor->stairs) { if (dynamic_cast(stair)) { - elements.push_back( new MMFloorStair(this, floor, (Floorplan::StairFreeform*)stair) ); + addElement( new MMFloorStair(this, floor, (Floorplan::StairFreeform*)stair) ); } } @@ -35,7 +35,7 @@ public: floor->stairs.push_back(stair); // add to myself as element - elements.push_back(new MMFloorStair(this, floor, stair)); + addElement(new MMFloorStair(this, floor, stair)); } diff --git a/mapview/model/MMFloorUnderlays.h b/mapview/model/MMFloorUnderlays.h index da25db0..e50260d 100644 --- a/mapview/model/MMFloorUnderlays.h +++ b/mapview/model/MMFloorUnderlays.h @@ -23,7 +23,7 @@ public: // the underlays for (Floorplan::UnderlayImage* img : floor->underlays) { - elements.push_back(new MMFloorUnderlayImage(this, floor, img)); + addElement(new MMFloorUnderlayImage(this, floor, img)); } } @@ -38,7 +38,7 @@ public: // add to myself as element MMFloorUnderlayImage* img = new MMFloorUnderlayImage(this, floor, elem); - elements.push_back(img); + addElement(img); img->setAnchor(center); img->setScale(0.1, 0.1); diff --git a/mapview/model/MMFloors.h b/mapview/model/MMFloors.h index ec40cad..3a84f06 100644 --- a/mapview/model/MMFloors.h +++ b/mapview/model/MMFloors.h @@ -22,7 +22,7 @@ public: // add all floors for (Floorplan::Floor* floor : map->floors) { - new MMFloor(this, floor); + new MMFloor(this, map, floor); } } @@ -32,6 +32,22 @@ public: /** get the underlying model */ Floorplan::IndoorMap* getMap() {return map;} + MMFloor* createFloor() { + + // add to underlying model + Floorplan::Floor* floor = new Floorplan::Floor(); + floor->name = "floor"; + map->floors.push_back(floor); + + // add to UI model + MMFloor* mmfloor = new MMFloor(this, map, floor); + //elements.push_back(mmfloor); + + return mmfloor; + + } + + }; #endif // MMFLOORS_H diff --git a/mapview/model/MapLayer.h b/mapview/model/MapLayer.h index d61382f..ce4376d 100644 --- a/mapview/model/MapLayer.h +++ b/mapview/model/MapLayer.h @@ -5,6 +5,8 @@ #include #include +#include "MapLayerListener.h" + class MapModelElement; @@ -28,14 +30,17 @@ enum class MapLayerType { class MapLayer { + /** this layer's elements */ + std::vector elements; + + /** attached listeners (if any) */ + std::vector listeners; + protected: /** this layer's parent */ MapLayer* parent; - /** this layer's elements */ - std::vector elements; - /** this layer's sublayers */ std::vector sublayers; @@ -60,6 +65,11 @@ public: /** dtor */ virtual ~MapLayer() {;} + /** attach a listener to this layer [usually only added to the root layer] */ + void addListener(MapLayerListener* listener) { + listeners.push_back(listener); + } + /** get the layer's parent */ MapLayer* getParent() const {return parent;} @@ -77,14 +87,28 @@ public: 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()); } + void removeElement(const MapModelElement* elem) { + elements.erase(std::remove(elements.begin(), elements.end(), elem), elements.end()); + onElemRemoved(elem); + } + /** add a new element to this layer */ + void addElement(MapModelElement* el) { + elements.push_back(el); + onElemAdded(el); + } + + /** remove the given sublayer from this layer */ + void removeSublayer(const MapLayer* layer) { sublayers.erase(std::remove(sublayers.begin(), sublayers.end(), layer), sublayers.end()); } /** is this layer currently visible? */ bool isVisible() const {return visible;} /** make this layer visible */ - void setVisible(const bool visible) {this->visible = visible;} + void setVisible(const bool visible) { + this->visible = visible; + onVisibilityChanged(visible); + } /** get all sub-layers within this layer */ @@ -94,7 +118,7 @@ public: /** helper method to get all elements and those of all sub-layers */ void getVisibleElementsRecursive(std::vector& el) { if (isVisible()) { - std::vector local = getElements(); + const std::vector local = getElements(); el.insert(el.end(), local.begin(), local.end()); for (MapLayer* sub : getSubLayers()) { sub->getVisibleElementsRecursive(el); @@ -102,9 +126,7 @@ public: } } -protected: - - +public: /** add a new sublayer to this layer */ void addSublayer(MapLayer* ml) { @@ -115,8 +137,35 @@ protected: } +private: + + void onElemAdded(MapModelElement* e) { + if (parent) {parent->onElemAdded(e);} + for (MapLayerListener* listener : listeners) { + listener->onLayerChanged(this); + listener->onLayerElementAdded(this, e); + } + } + + void onElemRemoved(const MapModelElement* e) { + if (parent) {parent->onElemRemoved(e);} + for (MapLayerListener* listener : listeners) { + listener->onLayerChanged(this); + listener->onLayerElementRemoved(this, e); + } + } + + void onVisibilityChanged(const bool visibile) { + if (parent) {parent->onVisibilityChanged(visibile);} + for (MapLayerListener* listener : listeners) { + listener->onLayerChanged(this); + listener->onLayerVisibilityChanged(this, visibile); + } + } + }; + class MapLayerRoot : public MapLayer { public: diff --git a/mapview/model/MapLayerListener.h b/mapview/model/MapLayerListener.h new file mode 100644 index 0000000..72e29cd --- /dev/null +++ b/mapview/model/MapLayerListener.h @@ -0,0 +1,25 @@ +#ifndef MAPLAYERLISTENER_H +#define MAPLAYERLISTENER_H + +class MapLayer; +class MapModelElement; + +class MapLayerListener { + +public: + + /** the map layer has changed. e.g. new elements were added */ + virtual void onLayerChanged(MapLayer* layer) = 0; + + /** a new element was added to a layer */ + virtual void onLayerElementAdded(MapLayer* layer, MapModelElement* elem) = 0; + + /** an element was removed from the layer */ + virtual void onLayerElementRemoved(MapLayer* layer, const MapModelElement* elem) = 0; + + /** the layer's visibility has changed */ + virtual void onLayerVisibilityChanged(MapLayer* layer, const bool visible) = 0; + +}; + +#endif // MAPLAYERLISTENER_H diff --git a/mapview/model/MapModel.h b/mapview/model/MapModel.h index 4d2da38..19a9be2 100644 --- a/mapview/model/MapModel.h +++ b/mapview/model/MapModel.h @@ -5,6 +5,7 @@ #include "MapLayer.h" #include "MapModelElement.h" +#include "MapModelListener.h" #include "MMRoot.h" @@ -13,7 +14,7 @@ #include #include -class MapModel : public QObject { +class MapModel : public QObject, public MapLayerListener { Q_OBJECT @@ -31,6 +32,9 @@ private: /** the loaded floorplan */ Floorplan::IndoorMap* im; + /** listener */ + std::vector listeners; + public: /** ctor */ @@ -42,6 +46,19 @@ public: cleanup(); } + /** attach a listener */ + void addListener(MapModelListener* l) { + listeners.push_back(l); + } + + /** create a new, empty root node */ + void startEmpty() { + im = new Floorplan::IndoorMap(); + root = new MMRoot(nullptr, im); + root->addListener(this); + //root->addSublayer(new MMFloors(root, im)); + } + void cleanup() { selLayer = nullptr; //selElements.clear(); @@ -57,6 +74,7 @@ public: // load the indoor-map using the given XML-file im = Floorplan::Reader::readFromFile(file); root = new MMRoot(nullptr, im); + root->addListener(this); emit reset(); @@ -68,6 +86,22 @@ public: } + void onLayerChanged(MapLayer* layer) override { + for (MapModelListener* l : listeners) {l->onLayerChanged(layer);} + } + + void onLayerElementAdded(MapLayer* layer, MapModelElement* elem) override { + for (MapModelListener* l : listeners) {l->onLayerElementAdded(layer, elem);} + } + + void onLayerElementRemoved(MapLayer* layer, const MapModelElement* elem) override { + for (MapModelListener* l : listeners) {l->onLayerElementRemoved(layer, elem);} + } + + void onLayerVisibilityChanged(MapLayer* layer, const bool visible) override { + for (MapModelListener* l : listeners) {l->onLayerVisibilityChanged(layer, visible);} + } + /** get the constructed map */ Floorplan::IndoorMap* getMap() const { return im; @@ -76,18 +110,21 @@ public: /** 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 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(); + std::vector elements; + root->getVisibleElementsRecursive(elements); + return elements; } /** set the currently selected layer */ diff --git a/mapview/model/MapModelListener.h b/mapview/model/MapModelListener.h new file mode 100644 index 0000000..fca2878 --- /dev/null +++ b/mapview/model/MapModelListener.h @@ -0,0 +1,13 @@ +#ifndef MAPMODELLISTENER_H +#define MAPMODELLISTENER_H + +#include "MapLayerListener.h" + +/** + * listen for changes to the map model + */ +class MapModelListener : public MapLayerListener { + +}; + +#endif // MAPMODELLISTENER_H diff --git a/params/EditFields.h b/params/EditFields.h new file mode 100644 index 0000000..3c6b42e --- /dev/null +++ b/params/EditFields.h @@ -0,0 +1,143 @@ +#ifndef EDITFIELDS_H +#define EDITFIELDS_H + +#include +#include +#include +#include +#include "../mapview/model/IHasParams.h" + +class EditFields { + +public: + + static void get(QGridLayout* lay, IHasParams* elem) { + + int r = 0; + + for(int i = 0; i < elem->getNumParams(); ++i) { + + const Param param = elem->getParamDesc(i); + const ParamValue value = elem->getParamValue(i); + + // skip Not-Available entries + if (param.type == ParamType::NOT_AVAILABLE) {continue;} + + lay->addWidget(new QLabel(param.name.c_str()),r,0); + + switch(param.type) { + + case ParamType::BOOL: { + QCheckBox* chk = new QCheckBox( ); + chk->setChecked(value.toBool()); + if (param.readOnly) { + chk->setEnabled(false); + } else { + chk->connect(chk, &QCheckBox::clicked, [i,elem] (const bool checked) { + elem->setParamValue(i, ParamValue(checked)); + }); + } + lay->addWidget(chk,r,1); + break; + } + + case ParamType::FLOAT: { + const std::string str = std::to_string(value.toFloat()); + if (param.readOnly) { + lay->addWidget(new QLabel(str.c_str()),r,1); + } else { + QLineEdit* le = new QLineEdit( str.c_str() ); + le->connect(le, &QLineEdit::textChanged, [i,elem] (const QString& str) { + const float val = str.toFloat(); + elem->setParamValue(i, ParamValue(val)); + }); + lay->addWidget(le,r,1); + } + break; + } + + case ParamType::INT: { + const std::string str = std::to_string(value.toInt()); + QLineEdit* le = new QLineEdit( str.c_str() ); + le->connect(le, &QLineEdit::textChanged, [i,elem] (const QString& str) { + const int val = str.toInt(); + elem->setParamValue(i, ParamValue(val)); + }); + lay->addWidget(le,r,1); + break; + } + + case ParamType::STRING: { + const std::string str = value.toString(); + QLineEdit* le = new QLineEdit( str.c_str() ); + le->connect(le, &QLineEdit::textChanged, [i,elem] (const QString& str) { + elem->setParamValue(i, ParamValue(str.toStdString())); + }); + lay->addWidget(le,r,1); + break; + } + + case ParamType::FILE: { + const std::string str = value.toString(); + QLabel* lblFile = new QLabel(str.c_str()); + QPushButton* btn = new QPushButton("<"); + btn->setMaximumSize(32,32); + btn->connect(btn, &QPushButton::clicked, [i,elem,lblFile] (const bool checked) { + QString res = QFileDialog::getOpenFileName(); + elem->setParamValue(i, ParamValue(res.toStdString())); + lblFile->setText(res); + }); + lay->addWidget(lblFile,r,1); + lay->addWidget(btn,r,2); + break; + } + + case ParamType::POINT2: { + const Point2 p2 = value.toPoint2(); + QWidget* subWidget = new QWidget(); + QGridLayout* laySub = new QGridLayout(subWidget); laySub->setMargin(0); + QLineEdit* txtX = new QLineEdit(QString::number(p2.x)); + QLineEdit* txtY = new QLineEdit(QString::number(p2.y)); + laySub->addWidget(txtX,0,0); + laySub->addWidget(txtY,0,1); + lay->addWidget(subWidget,r,1); + auto onChange = [i,elem,txtX,txtY] (const QString& str) { + elem->setParamValue(i, ParamValue( Point2(txtX->text().toFloat(), txtY->text().toFloat()) )); + }; + txtX->connect(txtX, &QLineEdit::textChanged, onChange); + txtY->connect(txtY, &QLineEdit::textChanged, onChange); + break; + } + + case ParamType::POINT3: { + const Point3 p3 = value.toPoint3(); + QWidget* subWidget = new QWidget(); + QGridLayout* laySub = new QGridLayout(subWidget); laySub->setMargin(0); + QLineEdit* txtX = new QLineEdit(QString::number(p3.x)); + QLineEdit* txtY = new QLineEdit(QString::number(p3.y)); + QLineEdit* txtZ = new QLineEdit(QString::number(p3.z)); + laySub->addWidget(txtX,0,0); + laySub->addWidget(txtY,0,1); + laySub->addWidget(txtZ,0,2); + lay->addWidget(subWidget,r,1); + auto onChange = [i,elem,txtX,txtY,txtZ] (const QString& str) { + (void) str; + elem->setParamValue(i, ParamValue( Point3(txtX->text().toFloat(), txtY->text().toFloat(), txtZ->text().toFloat()) )); + }; + txtX->connect(txtX, &QLineEdit::textChanged, onChange); + txtY->connect(txtY, &QLineEdit::textChanged, onChange); + txtZ->connect(txtZ, &QLineEdit::textChanged, onChange); + break; + } + + } + + ++r; + + } + + } + +}; + +#endif // EDITFIELDS_H diff --git a/params/ElementParamWidget.cpp b/params/ElementParamWidget.cpp index af0279e..a6cff09 100644 --- a/params/ElementParamWidget.cpp +++ b/params/ElementParamWidget.cpp @@ -4,9 +4,10 @@ #include "../mapview/model/MMFloorObstacleLine.h" #include "../mapview/model/MMFloorOutlinePolygon.h" -#include "../mapview/model/IHasMAC.h" -#include "../mapview/model/IHasFile.h" #include "../mapview/model/IHasParams.h" +#include "../mapview/model/IHasEditableMeta.h" + +#include "MetaEditWidget.h" #include @@ -68,11 +69,14 @@ ElementParamWidget::ElementParamWidget(QWidget *parent) : QWidget(parent) { this->lay = new QGridLayout(this); - setMinimumSize(100, 100); - setMaximumWidth(250); + //setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + //lay->setSizeConstraint(QLayout::SetMinAndMaxSize); + setMinimumSize(200, 25); + setMaximumSize(200, 9999); + //setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); //setTitle("MapElement Parameters"); - QGridLayout* lay = new QGridLayout(); + lay = new QGridLayout(); // start empty setElement(nullptr); @@ -90,7 +94,6 @@ void ElementParamWidget::refresh() { while ( QWidget* w = this->findChild() ) {delete w;} delete this->layout(); - this->lay = new QGridLayout(); this->setLayout(lay); int r = 0; @@ -136,13 +139,15 @@ void ElementParamWidget::refresh() { lay->addWidget(cmb,r,1); cmb->setCurrentIndex((int)elem->getMethod()); connect(cmb, static_cast(&QComboBox::currentIndexChanged), [elem, cmb] (int idx) { + (void) idx; elem->setMethod( (Floorplan::OutlineMethod) cmb->currentData().toInt() ); }); ++r; } } - { + + { // does the element have "parameters" ? IHasParams* elem = dynamic_cast(el); if (elem) { @@ -158,6 +163,9 @@ void ElementParamWidget::refresh() { switch(param.type) { + case ParamType::NOT_AVAILABLE: + break; + case ParamType::BOOL: { QCheckBox* chk = new QCheckBox( ); chk->setChecked(value.toBool()); @@ -214,6 +222,7 @@ void ElementParamWidget::refresh() { QPushButton* btn = new QPushButton("<"); btn->setMaximumSize(32,32); connect(btn, &QPushButton::clicked, [i,elem,lblFile] (const bool checked) { + (void) checked; QString res = QFileDialog::getOpenFileName(); elem->setParamValue(i, ParamValue(res.toStdString())); lblFile->setText(res); @@ -233,6 +242,7 @@ void ElementParamWidget::refresh() { laySub->addWidget(txtY,0,1); lay->addWidget(subWidget,r,1); auto onChange = [i,elem,txtX,txtY] (const QString& str) { + (void) str; elem->setParamValue(i, ParamValue( Point2(txtX->text().toFloat(), txtY->text().toFloat()) )); }; connect(txtX, &QLineEdit::textChanged, onChange); @@ -267,10 +277,39 @@ void ElementParamWidget::refresh() { } } + + { // does the element have editable metadata? + IHasEditableMeta* elem = dynamic_cast(el); + if (elem) { + + QPushButton* btn = new QPushButton("edit"); + connect(btn, &QPushButton::clicked, [elem] (const bool checked) { + (void) checked; + if (!elem->getMeta()) {elem->setMeta(new Floorplan::Meta());} // ensure meta-object is present + MetaEditWidget* mew = new MetaEditWidget(elem->getMeta()); // edit + mew->show(); + }); + lay->addWidget(new QLabel("Meta"),r,0); + lay->addWidget(btn,r,1); + } + } + } + //setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + //lay->setSizeConstraint(QLayout::SetMinimumSize); + //setLayout(lay); + setMinimumSize(150, 30+r*24); + setMaximumSize(250, 30+r*24); + //resize(100, 20+r*22); + //emit update(); + //setVisible(false); + //setVisible(true); + + } + void ElementParamWidget::onMaterialChange() { IHasMaterial* el = dynamic_cast(this->curElement); if (el) {el->setMaterial( (Floorplan::Material) material.cmb->currentData().toInt() );} @@ -280,23 +319,3 @@ 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.txt->setText(res); -//} diff --git a/params/ElementParamWidget.h b/params/ElementParamWidget.h index 8d756f6..aa8fe4b 100644 --- a/params/ElementParamWidget.h +++ b/params/ElementParamWidget.h @@ -21,6 +21,7 @@ public: /** refresh the currently selected element */ void refresh(); + private: QGridLayout* lay; diff --git a/params/LayerParamWidget.cpp b/params/LayerParamWidget.cpp index 9cdffab..42354b6 100644 --- a/params/LayerParamWidget.cpp +++ b/params/LayerParamWidget.cpp @@ -7,13 +7,15 @@ #include "mapview/model/IHasName.h" #include "mapview/model/MMFloor.h" +#include "EditFields.h" + LayerParamWidget::LayerParamWidget(QWidget *parent) : QWidget(parent) { - setMinimumSize(100, 100); + setMinimumSize(100, 50); setMaximumWidth(250); //setTitle("MapLayer Parameters"); - QGridLayout* lay = new QGridLayout(this); + lay = new QGridLayout(this); int r = 0; @@ -53,35 +55,50 @@ 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("-"); - } +// if (l) { +// std::string info = l->getLayerName() + " (" + std::to_string(l->getNumElements()) + " elements)"; +// selected.info->setText(info.c_str()); +// } else { +// selected.info->setText("-"); +// } + + while ( QWidget* w = this->findChild() ) {delete w;} + delete this->layout(); + this->lay = new QGridLayout(); + this->setLayout(lay); + int r = 0; + + +// { +// 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());} +// } + { - 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() ); + IHasParams* elem = dynamic_cast(l); + if (elem) { + EditFields::get(lay, elem); } } - // has name - { - IHasName* elem = dynamic_cast(l); - name.lbl->setVisible(elem); - name.txt->setVisible(elem); - if (elem) {name.txt->setText(elem->getName().c_str());} - } - } diff --git a/params/LayerParamWidget.h b/params/LayerParamWidget.h index a445299..214bb00 100644 --- a/params/LayerParamWidget.h +++ b/params/LayerParamWidget.h @@ -8,11 +8,16 @@ class MapLayer; class QLabel; class QLineEdit; class QComboBox; +class QGridLayout; class LayerParamWidget : public QWidget { Q_OBJECT +private: + + QGridLayout* lay; + public: explicit LayerParamWidget(QWidget *parent = 0); diff --git a/params/LayerTree.cpp b/params/LayerTree.cpp new file mode 100644 index 0000000..df6ede8 --- /dev/null +++ b/params/LayerTree.cpp @@ -0,0 +1,92 @@ +#include "LayerTree.h" + +#include +#include +#include +#include + +#include "../tree/MapTreeModel.h" +#include "../UIHelper.h" + +LayerTree::LayerTree(QWidget *parent) : QWidget(parent) { + + QGridLayout* layGrid = new QGridLayout(this); + + tree = new QTreeView(this); + tree->setMinimumSize(150, 200); + + QWidget* wButtons = new QWidget(); + QHBoxLayout* layButtons = new QHBoxLayout(wButtons); + + btnNew = new QPushButton(this); + btnNew->setIcon(UIHelper::getIcon("add")); + btnNew->setMaximumSize(32,32); + btnNew->setMinimumSize(32,32); + btnNew->setEnabled(false); + layButtons->addWidget(btnNew); + + + btnDelete = new QPushButton(this); + btnDelete->setIcon(UIHelper::getIcon("remove")); + btnDelete->setMaximumSize(32,32); + btnDelete->setMinimumSize(32,32); + btnDelete->setEnabled(false); + layButtons->addWidget(btnDelete); + + layGrid->addWidget(tree, 0,0,1,1); + layGrid->addWidget(wButtons, 1, 0, 1, 1); + + connect(btnNew, SIGNAL(clicked(bool)), this, SLOT(onBtnNew())); + connect(btnDelete, SIGNAL(clicked(bool)), this, SLOT(onBtnDelete())); + + +} + +void LayerTree::expandAll() { + tree->expandAll(); +} + +void LayerTree::setModel(MapTreeModel* model) { + + this->model = model; + this->tree->setModel(model); + + // tree model selection events + if (!connect(tree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(onLayerSelected(QModelIndex)))) {throw "error";} + +} + +void LayerTree::onLayerSelected(QModelIndex idx) { + + MapLayer* ml = static_cast(idx.internalPointer()); + + MMFloors* floors = dynamic_cast(ml); + btnNew->setEnabled( floors != nullptr ); + + MMFloor* floor = dynamic_cast(ml); + btnDelete->setEnabled( floor != nullptr ); + + emit layerSelected(idx); + +} + + +void LayerTree::onBtnNew() { + QModelIndex idx = tree->selectionModel()->selectedIndexes().first(); + MapLayer* ml = static_cast(idx.internalPointer()); + MMFloors* floors = dynamic_cast(ml); + if (floors) { + floors->createFloor(); + model->getMapModel()->reset(); + } +} + +void LayerTree::onBtnDelete() { + QModelIndex idx = tree->selectionModel()->selectedIndexes().first(); + MapLayer* ml = static_cast(idx.internalPointer()); + MMFloor* floor= dynamic_cast(ml); + if (floor) { + floor->deleteMe(); + model->getMapModel()->reset(); + } +} diff --git a/params/LayerTree.h b/params/LayerTree.h new file mode 100644 index 0000000..0044940 --- /dev/null +++ b/params/LayerTree.h @@ -0,0 +1,49 @@ +#ifndef LAYERTREE_H +#define LAYERTREE_H + +#include + +class QTreeView; +class QPushButton; +class MapTreeModel; + +#include + +class LayerTree : public QWidget { + + Q_OBJECT + +public: + + explicit LayerTree(QWidget *parent = 0); + + //QTreeView* getTree() const {return tree;} + + void setModel(MapTreeModel* model); + + void expandAll(); + +signals: + + void layerSelected(QModelIndex); + +private slots: + + void onLayerSelected(QModelIndex); + + void onBtnNew(); + + void onBtnDelete(); + +private: + + QTreeView* tree; + QPushButton* btnNew; + QPushButton* btnDelete; + MapTreeModel* model; + +public slots: + +}; + +#endif // LAYERTREE_H diff --git a/params/MetaEditModel.cpp b/params/MetaEditModel.cpp new file mode 100644 index 0000000..0667c9c --- /dev/null +++ b/params/MetaEditModel.cpp @@ -0,0 +1,87 @@ +#include "MetaEditModel.h" + + +MetaEditModel::MetaEditModel(QObject* parent) : QAbstractTableModel(parent) { + + setHeaderData(0, Qt::Horizontal, QObject::tr("ID")); + setHeaderData(1, Qt::Horizontal, QObject::tr("Name")); +} + +void MetaEditModel::setSource(Floorplan::Meta* meta) { + beginResetModel(); + this->meta = meta; + endResetModel(); +} + +int MetaEditModel::rowCount(const QModelIndex &parent) const { + return (meta) ? (meta->size()) : (0); +} + +int MetaEditModel::columnCount(const QModelIndex &parent) const { + return 2; +} + +QVariant MetaEditModel::data(const QModelIndex &index, int role) const { + + if (role == Qt::DisplayRole) { + switch(index.column()) { + case 0: return meta->getKey(index.row()).c_str(); + case 1: return meta->getVal(index.row()).c_str(); + } + } + + return QVariant(); + +} + +bool MetaEditModel::setData(const QModelIndex & index, const QVariant &value, int role) { + + if (role == Qt::EditRole) { + switch(index.column()) { + case 0: meta->setKey(index.row(), value.toString().toStdString()); return true; + case 1: meta->setVal(index.row(), value.toString().toStdString()); return true; + } + } + + return false; + +} + +void MetaEditModel::deleteEntry(const int idx) { + beginResetModel(); + meta->deleteEntry(idx); + endResetModel(); +} + +void MetaEditModel::addEntry() { + beginResetModel(); + meta->add("key", "val"); + endResetModel(); +} + +Qt::ItemFlags MetaEditModel::flags(const QModelIndex &index) const { + + if (!index.isValid()) {return Qt::ItemIsEnabled;} + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; + +} + + + + +QVariant MetaEditModel::headerData(int section, Qt::Orientation orientation, int role) const { + + if (role == Qt::DisplayRole) { + if(orientation == Qt::Horizontal) { + if (section == 0) {return "key";} + if (section == 1) {return "value";} + } else { + return QString::number(section); + } + } + + return QVariant(); + +} + diff --git a/params/MetaEditModel.h b/params/MetaEditModel.h new file mode 100644 index 0000000..22f9fb1 --- /dev/null +++ b/params/MetaEditModel.h @@ -0,0 +1,41 @@ +#ifndef METAEDITMODEL_H +#define METAEDITMODEL_H + +#include +#include + +class MetaEditModel : public QAbstractTableModel { + + Q_OBJECT + +private: + + Floorplan::Meta* meta = nullptr; + +public: + + MetaEditModel(QObject* parent = nullptr); + + /** delete the idx-th entry */ + void deleteEntry(const int idx); + + /** add a new entry at the end */ + void addEntry(); + + void setSource(Floorplan::Meta* meta); + + int rowCount(const QModelIndex& parent) const override; + + int columnCount(const QModelIndex& parent) const override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + bool setData(const QModelIndex & index, const QVariant &value, int role = Qt::EditRole) override; + +}; + +#endif // METAEDITMODEL_H diff --git a/params/MetaEditWidget.cpp b/params/MetaEditWidget.cpp new file mode 100644 index 0000000..a574113 --- /dev/null +++ b/params/MetaEditWidget.cpp @@ -0,0 +1,67 @@ +#include "MetaEditWidget.h" +#include "MetaEditModel.h" + +#include +#include +#include +#include + +MetaEditWidget::MetaEditWidget(Floorplan::Meta* meta) : QWidget(nullptr), metaOrig(meta) { + + // local copy. for the abort button [orig is unchanged] + metaCopy.params = metaOrig->params; + + QGridLayout* lay = new QGridLayout(this); + + tbl = new QTableView(); + lay->addWidget(tbl, 0, 0, 1, 3); + + model = new MetaEditModel(); + model->setSource(&metaCopy); // we edit the copy + + tbl->setModel(model); + tbl->setSelectionBehavior(QAbstractItemView::SelectRows); + tbl->setSelectionMode(QAbstractItemView::SingleSelection); + + + // events + QPushButton* btnAdd = new QPushButton("add entry"); + lay->addWidget(btnAdd, 1, 0); + btnAdd->connect(btnAdd, &QPushButton::clicked, [this] (const bool) { + model->addEntry(); + }); + btnAdd->setToolTip("add a new, empty entry. delete an entry using the keyboard"); + + QPushButton* btnCancel = new QPushButton("abort"); + lay->addWidget(btnCancel, 1, 1); + btnCancel->connect(btnCancel, &QPushButton::clicked, [this] (const bool) { + // do not apply changes. juts close + close(); + }); + btnCancel->setToolTip("close the dialog without committing the changes"); + + QPushButton* btnOK = new QPushButton("OK"); + lay->addWidget(btnOK, 1, 2); + btnOK->connect(btnOK, &QPushButton::clicked, [this] (const bool) { + metaOrig->params = metaCopy.params; // apply changed + close(); + }); + btnOK->setToolTip("commit the changes and close the dialog"); + + + // sizing + resize(500,400); + +} + +void MetaEditWidget::keyPressEvent(QKeyEvent* e) { + + if (e->key() == Qt::Key_Delete) { + QModelIndexList indices = tbl->selectionModel()->selectedIndexes(); + for (const QModelIndex& idx : indices) { + model->deleteEntry(idx.row()); + break; // the list contains one entry per column! + } + } + +} diff --git a/params/MetaEditWidget.h b/params/MetaEditWidget.h new file mode 100644 index 0000000..6e4d6ec --- /dev/null +++ b/params/MetaEditWidget.h @@ -0,0 +1,35 @@ +#ifndef METAEDITWIDGET_H +#define METAEDITWIDGET_H + +#include +#include + +class MetaEditModel; +class QTableView; + +/** + * helper class to edit the Floorplan::Meta + * key value attribute + */ +class MetaEditWidget : public QWidget { + + Q_OBJECT + +private: + + Floorplan::Meta* metaOrig; + Floorplan::Meta metaCopy; // used for the abort button + + MetaEditModel* model; + + QTableView* tbl; + +public: + + MetaEditWidget(Floorplan::Meta* meta); + + void keyPressEvent(QKeyEvent* e); + +}; + +#endif // METAEDITWIDGET_H diff --git a/params/ToolBox.cpp b/params/ToolBox.cpp index 58b27a4..abefdad 100644 --- a/params/ToolBox.cpp +++ b/params/ToolBox.cpp @@ -14,8 +14,19 @@ #include "../mapview/model/MMFloorBeacon.h" #include "../mapview/model/MMFloorGroundTruthPoints.h" +#include "../mapview/2D/tools/ToolMeasure.h" + #include "../UIHelper.h" +QSplitter* getSplitter() { + + QSplitter* splt = new QSplitter(); + splt->setStyleSheet("background-color:black;"); + splt->setMinimumHeight(1); + return splt; + +} + ToolBoxWidget::ToolBoxWidget(MapView2D* view, QWidget *parent) : QWidget(parent), view(view) { const int s = 32; @@ -31,6 +42,16 @@ ToolBoxWidget::ToolBoxWidget(MapView2D* view, QWidget *parent) : QWidget(parent) connect(btnSelect, SIGNAL(clicked(bool)), this, SLOT(onSelect())); + // MEASURE + btnMeasure = new QPushButton(UIHelper::getIcon("ruler"), ""); + btnMeasure->setMinimumSize(s,s); + lay->addWidget(btnMeasure, r++, 0, 1,1,Qt::AlignTop); + connect(btnMeasure, SIGNAL(clicked(bool)), this, SLOT(onMeasure())); + + + // splitter + lay->addWidget(getSplitter(), r++, 0, 1,1,Qt::AlignTop); + // OBSTACLES btnGround = new QPushButton(UIHelper::getIcon("floor"), ""); @@ -64,6 +85,10 @@ ToolBoxWidget::ToolBoxWidget(MapView2D* view, QWidget *parent) : QWidget(parent) connect(btnElevator, SIGNAL(clicked(bool)), this, SLOT(onNewElevator())); + // splitter + lay->addWidget(getSplitter(), r++, 0, 1,1,Qt::AlignTop); + + // TRANSMITTERS btnWifi = new QPushButton(UIHelper::getIcon("wifi"), ""); btnWifi->setMinimumSize(s,s); @@ -350,116 +375,25 @@ public: }; -//struct NewLineTool : public Tool { - -// /** add another line after this one? */ -// bool multiple = true; - -// /** register this tool into the given tools-queue */ -// Tools& tools; - -// /** the layer to add the new line to */ -// MapLayer* layer; - -// /** currently edited line */ -// Floorplan::FloorObstacleLine* foLine; -// MMFloorObstacleLine* mmLine; - -// /** new line type */ -// Floorplan::ObstacleType type; - -// /** new line material */ -// Floorplan::Material mat; - -// /** currently edited line node (has 2) */ -// int idx = 0; - - -// NewLineTool(Tools& tools, MapLayer* layer, Floorplan::ObstacleType type, Floorplan::Material mat) : tools(tools), layer(layer), type(type), mat(mat) { -// createEmptyLine(); -// tools.addFront(this); -// } - -// virtual bool mousePressEvent(MapView2D* m, QMouseEvent* e) override { -// (void) m; (void) e; -// return true; -// } - -// virtual bool mouseMoveEvent(MapView2D* m, QMouseEvent* e) override { -// const Point2 onScreen(e->x(), e->y()); -// Point2 onMap = m->getScaler().sm(onScreen); -// onMap = m->getScaler().snap(onMap); -// if (idx == 0) { foLine->from = onMap; foLine->to = onMap; } -// if (idx == 1) { foLine->to = onMap; } -// return true; -// } - -// virtual bool mouseReleaseEvent(MapView2D* m, QMouseEvent* e) override { -// (void) m; (void) e; -// ++idx; -// if (idx == 2) { - -// finalizeLine(); - -// if (multiple) { -// idx = 0; -// createEmptyLine(); -// } else { -// disableMe(); -// } -// } -// return true; -// } - -// virtual bool keyPressEvent(MapView2D* m, QKeyEvent* e) override { -// (void) m; -// if (e->key() == Qt::Key_Escape) { -// if (mmLine) {mmLine->deleteMe();} -// disableMe(); return true; -// } -// return false; -// } - -//private: - -// /** finalize the current line */ -// void finalizeLine() { -// if (!mmLine) {return;} -// mmLine->getMV2D()->unfocus(); -// mmLine = nullptr; -// } - -// /** stop creating new lines */ -// void disableMe() { -// finalizeLine(); -// tools.remove(this); -// delete this; -// } - -// /** create a new, empty line */ -// void createEmptyLine() { -// foLine = new Floorplan::FloorObstacleLine(type, mat, Point2(0, 0), Point2(0, 0)); -// MMFloorObstacles* obs = (MMFloorObstacles*)layer; -// mmLine = obs->createLine(foLine); -// mmLine->getMV2D()->focus(); -// } - -//}; - - - - void ToolBoxWidget::onSelect() { +} + +void ToolBoxWidget::onMeasure() { + + new ToolMeasure(view->getTools()); } + void ToolBoxWidget::onNewWall() { + new NewWallTool(view->getTools(), curLayer); //view->getModel()->reselect(); + } void ToolBoxWidget::onNewPillar() { @@ -484,21 +418,6 @@ void ToolBoxWidget::onNewDoor() { new NewDoorTool(view->getTools(), curLayer); -// const Point2 center = view->getScaler().getCenter(); -// float s = view->getScaler().sm(50); - -// Floorplan::FloorObstacleDoor* door = new Floorplan::FloorObstacleDoor( -// Floorplan::DoorType::SWING, -// Floorplan::Material::WOOD, -// Point2(center.x-s, center.y), -// Point2(center.x+s, center.y) -// ); - -// MMFloorObstacles* obs = (MMFloorObstacles*)curLayer; -// obs->createDoor(door); - - //view->getModel()->reselect(); - } void ToolBoxWidget::onNewStair() { diff --git a/params/ToolBoxWidget.h b/params/ToolBoxWidget.h index f8d4c9b..cc4ff9f 100644 --- a/params/ToolBoxWidget.h +++ b/params/ToolBoxWidget.h @@ -8,7 +8,9 @@ class QPushButton; class MapView2D; /** - * gui element with actions to perform + * the toolbox on the left of the map. + * gui element with actions to perform. + * add new elements, etc. */ class ToolBoxWidget : public QWidget { @@ -32,6 +34,7 @@ private: int r = 0; QPushButton* btnSelect; + QPushButton* btnMeasure; QPushButton* btnGround; QPushButton* btnWall; @@ -50,6 +53,7 @@ private: private slots: void onSelect(); + void onMeasure(); void onNewGround(); void onNewWall(); diff --git a/res.qrc b/res.qrc index ac3722c..4be2843 100644 --- a/res.qrc +++ b/res.qrc @@ -14,6 +14,9 @@ res/icons/door.svg res/icons/cursor.svg res/icons/elevator.svg + res/icons/add.svg + res/icons/remove.svg + res/icons/ruler.svg res/icons/gtp.svg diff --git a/res/icons/add.svg b/res/icons/add.svg new file mode 100644 index 0000000..5d43b92 --- /dev/null +++ b/res/icons/add.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/remove.svg b/res/icons/remove.svg new file mode 100644 index 0000000..97830d3 --- /dev/null +++ b/res/icons/remove.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/ruler.svg b/res/icons/ruler.svg new file mode 100644 index 0000000..e1b6e7c --- /dev/null +++ b/res/icons/ruler.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/icons/sources.txt b/res/icons/sources.txt new file mode 100644 index 0000000..b4be463 --- /dev/null +++ b/res/icons/sources.txt @@ -0,0 +1,8 @@ +www.flaticon.com +https://icons8.com/web-app/for/all/Ibeacon + + +# add +http://www.flaticon.com/free-icon/add-button-inside-black-circle_60740 +# remove +http://www.flaticon.com/free-icon/rounded-remove-button_60951#term=remove%20button&page=1&position=6 diff --git a/tree/MapTreeModel.h b/tree/MapTreeModel.h index e9fcae3..9ff442a 100644 --- a/tree/MapTreeModel.h +++ b/tree/MapTreeModel.h @@ -25,6 +25,10 @@ public: virtual ~MapTreeModel() {;} + MapModel* getMapModel() { + return mdl; + } + virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override { MapLayer* _parent = nullptr;