added a ruler for measuring

added support for meta-data editing
improved element selection
changed zooming
fixed some issues with layer events
fixed issue with 3D outline
fixed loading issue for old maps
some interface changes
This commit is contained in:
2017-03-10 13:44:17 +01:00
parent 2297a76c53
commit f40fc9a823
32 changed files with 809 additions and 198 deletions

View File

@@ -38,7 +38,10 @@ SOURCES += \
mapview/3D/MapView3D.cpp \
params/StairBuilder.cpp \
mapview/2D/tools/Tools.cpp \
params/LayerTree.cpp
params/LayerTree.cpp \
mapview/2D/tools/ToolMeasure.cpp \
params/MetaEditWidget.cpp \
params/MetaEditModel.cpp
HEADERS += MainWindow.h \
@@ -130,7 +133,14 @@ HEADERS += MainWindow.h \
mapview/model/MMFloorElevator.h \
params/LayerTree.h \
params/EditFields.h \
mapview/2D/ClickDist.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
FORMS += MainWindow.ui

View File

@@ -16,6 +16,7 @@
#include "params/ToolBoxWidget.h"
#include "params/ActionWidget.h"
#include "params/LayerTree.h"
#include "params/MetaEditWidget.h"
#include "tree/MapTreeModel.h"
@@ -28,6 +29,14 @@
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);
@@ -44,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);
@@ -68,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()));
@@ -83,7 +93,7 @@ MainController::MainController() {
connect(mw, &MainWindow::onGridShowEdges, [&] (const bool show) {mw->getMapView3D()->getGridRenderer()->setShowEdges(show);} );
//mapModel->load("../IndoorMap/maps/SHL25.xml");
mapModel->load("../IndoorMap/maps/SHL26.xml");
//mapModel->load("../IndoorMap/maps/test.xml");
//mapModel->load("../IndoorMap/maps/APs.xml");
@@ -95,12 +105,34 @@ MainController::MainController() {
//mapModel->load("/mnt/data/workspaces/IPIN2016/IPIN2016/competition/maps/test.xml");
//mapModel->load("/mnt/vm/workspace/IRGame/IndoorServer/maps/map6.xml");
//mapModel->load("/mnt/vm/workspace/IRGame/IndoorServer/maps/keller1.xml");
mapModel->load("/mnt/vm/workspace/IRGame/IndoorServer/maps/wohnung3.xml");
//mapModel->load("/mnt/vm/workspace/IRGame/IndoorServer/maps/wohnung4.xml");
//mapModel->startEmpty();
}
void MainController::onLayerChanged(MapLayer* layer) {
(void) layer;
mw->getMapView2D()->update();
mw->getMapView3D()->update();
}
void MainController::onLayerElementAdded(MapLayer* layer, MapModelElement* elem) {
(void) layer;
mapSelector->focus(mw->getMapView2D(), elem);
}
void MainController::onLayerElementRemoved(MapLayer* layer, const MapModelElement* elem) {
(void) layer;
(void) elem;
}
void MainController::onLayerVisibilityChanged(MapLayer *layer, const bool visible) {
(void) layer;
(void) visible;
}
void MainController::onShow3DFloorplan() {
mw->getMapView3D()->showFloorplan();
}
@@ -133,6 +165,10 @@ void MainController::onMapModelAboutToReset() {
mw->getMapView2D()->update();
}
void MainController::onMapModelNeedsRepaint() {
mw->getMapView2D()->update();
}
void MainController::onMapModelReset() {
mw->getTree()->expandAll();
}

View File

@@ -4,19 +4,44 @@
#include <QObject>
#include <QModelIndex>
#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:

View File

@@ -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);}

View File

View File

@@ -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<Point2> 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

View File

@@ -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<MapModelElement*> 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)

View File

@@ -0,0 +1,21 @@
#ifndef IHASEDITABLEMETA_H
#define IHASEDITABLEMETA_H
#include <Indoor/floorplan/v2/Floorplan.h>
/**
* 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

View File

@@ -36,7 +36,7 @@ public:
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);

View File

@@ -3,13 +3,14 @@
#include "MapModelElement.h"
#include "IHasParams.h"
#include "IHasEditableMeta.h"
#include "../2D/MV2DElementAccessPoint.h"
#include "../3D/MV3DElementAccessPoint.h"
#include <Indoor/floorplan/v2/Floorplan.h>
class MMFloorAccessPoint : public MapModelElement, public IHasParams {
class MMFloorAccessPoint : public MapModelElement, public IHasParams, public IHasEditableMeta {
private:
@@ -64,6 +65,14 @@ public:
}
}
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;}

View File

@@ -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));
}

View File

@@ -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));
}
};

View File

@@ -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));
}

View File

@@ -23,11 +23,11 @@ public:
// the obstacles
for (Floorplan::FloorObstacle* o : floor->obstacles) {
if (dynamic_cast<Floorplan::FloorObstacleLine*>(o)) {
elements.push_back(new MMFloorObstacleLine(this, floor, (Floorplan::FloorObstacleLine*)o));
addElement(new MMFloorObstacleLine(this, floor, (Floorplan::FloorObstacleLine*)o));
} else if (dynamic_cast<Floorplan::FloorObstacleCircle*>(o)) {
elements.push_back(new MMFloorObstacleCircle(this, floor, (Floorplan::FloorObstacleCircle*)o));
addElement(new MMFloorObstacleCircle(this, floor, (Floorplan::FloorObstacleCircle*)o));
} else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(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));
}

View File

@@ -3,37 +3,38 @@
#include "MapLayer.h"
#include "MMFloorOutlinePolygon.h"
#include "../3D/MV3DElementFloorOutline.h"
#include "MMFloorOutlinePolygonCombined.h"
#include "../3D/MV3DElementFloorOutline.h"
#include <Indoor/floorplan/v2/Floorplan.h>
/**
* 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));
}

View File

@@ -0,0 +1,41 @@
#ifndef MMFLOOROUTLINEPOLYGONCOMBINED_H
#define MMFLOOROUTLINEPOLYGONCOMBINED_H
#include "MapLayer.h"
#include "MMFloorOutlinePolygon.h"
#include "../3D/MV3DElementFloorOutline.h"
#include <Indoor/floorplan/v2/Floorplan.h>
/**
* 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

View File

@@ -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));
}

View File

@@ -23,7 +23,7 @@ public:
// add all floors
for (Floorplan::Stair* stair : floor->stairs) {
if (dynamic_cast<Floorplan::StairFreeform*>(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));
}

View File

@@ -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);

View File

@@ -5,6 +5,8 @@
#include <vector>
#include <algorithm>
#include "MapLayerListener.h"
class MapModelElement;
@@ -27,14 +29,17 @@ enum class MapLayerType {
class MapLayer {
/** this layer's elements */
std::vector<MapModelElement*> elements;
/** attached listeners (if any) */
std::vector<MapLayerListener*> listeners;
protected:
/** this layer's parent */
MapLayer* parent;
/** this layer's elements */
std::vector<MapModelElement*> elements;
/** this layer's sublayers */
std::vector<MapLayer*> sublayers;
@@ -59,6 +64,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;}
@@ -76,7 +86,16 @@ 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()); }
@@ -85,7 +104,10 @@ public:
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 */
@@ -95,7 +117,7 @@ public:
/** helper method to get all elements and those of all sub-layers */
void getVisibleElementsRecursive(std::vector<MapModelElement*>& el) {
if (isVisible()) {
std::vector<MapModelElement*> local = getElements();
const std::vector<MapModelElement*> local = getElements();
el.insert(el.end(), local.begin(), local.end());
for (MapLayer* sub : getSubLayers()) {
sub->getVisibleElementsRecursive(el);
@@ -105,8 +127,6 @@ public:
public:
/** add a new sublayer to this layer */
void addSublayer(MapLayer* ml) {
@@ -116,8 +136,35 @@ public:
}
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:

View File

@@ -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

View File

@@ -5,6 +5,7 @@
#include "MapLayer.h"
#include "MapModelElement.h"
#include "MapModelListener.h"
#include "MMRoot.h"
@@ -13,7 +14,7 @@
#include <Indoor/floorplan/v2/FloorplanReader.h>
#include <Indoor/floorplan/v2/FloorplanWriter.h>
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<MapModelListener*> listeners;
public:
/** ctor */
@@ -42,10 +46,16 @@ 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));
}
@@ -64,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();
@@ -75,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;
@@ -83,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<MapModelElement*> getSelectedLayerElements() {
//return selElements;
//return (selLayer) ? (selLayer->getElementsRecursive()) : (std::vector<MapModelElement*>());
std::vector<MapModelElement*> elements;
root->getVisibleElementsRecursive(elements);
return elements;
}
// /** get all elements within the currently selected layer */
// std::vector<MapModelElement*> getSelectedLayerElements() {
// //return selElements;
// //return (selLayer) ? (selLayer->getElementsRecursive()) : (std::vector<MapModelElement*>());
//// std::vector<MapModelElement*> elements;
//// root->getVisibleElementsRecursive(elements);
//// return elements;
// }
/** get all currently visible elements */
std::vector<MapModelElement*> getVisibleElements() {
return getSelectedLayerElements();
std::vector<MapModelElement*> elements;
root->getVisibleElementsRecursive(elements);
return elements;
}
/** set the currently selected layer */

View File

@@ -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

View File

@@ -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 <Indoor/floorplan/v2/Floorplan.h>
@@ -138,13 +139,15 @@ void ElementParamWidget::refresh() {
lay->addWidget(cmb,r,1);
cmb->setCurrentIndex((int)elem->getMethod());
connect(cmb, static_cast<void (QComboBox::*)(int)>(&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<IHasParams*>(el);
if (elem) {
@@ -160,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());
@@ -216,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);
@@ -235,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);
@@ -269,6 +277,23 @@ void ElementParamWidget::refresh() {
}
}
{ // does the element have editable metadata?
IHasEditableMeta* elem = dynamic_cast<IHasEditableMeta*>(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);
@@ -294,23 +319,3 @@ void ElementParamWidget::onObstacleTypeChange() {
IHasObstacleType* el = dynamic_cast<IHasObstacleType*>(this->curElement);
if (el) {el->setObstacleType((Floorplan::ObstacleType) obstacleType.cmb->currentData().toInt() );}
}
//void ElementParamWidget::onNameChange() {
// IHasName* el = dynamic_cast<IHasName*>(this->curElement);
// if (el) {el->setName(name.txt->text().toStdString());}
//}
//void ElementParamWidget::onOutlineMethodChange() {
// MMFloorOutlinePolygon* el = dynamic_cast<MMFloorOutlinePolygon*>(this->curElement);
// if (el) {el->setMethod( (Floorplan::OutlineMethod) outlineMethod.cmb->currentData().toInt() );}
//}
//void ElementParamWidget::onMACChanged() {
// dynamic_cast<IHasMAC*>(curElement)->setMAC(mac.txt->text().toStdString());
//}
//void ElementParamWidget::onSelectFileName() {
// QString res = QFileDialog::getOpenFileName(this);
// dynamic_cast<IHasFile*>(curElement)->setFileName(res.toStdString());
// fileName.txt->setText(res);
//}

87
params/MetaEditModel.cpp Normal file
View File

@@ -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();
}

41
params/MetaEditModel.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef METAEDITMODEL_H
#define METAEDITMODEL_H
#include <Indoor/floorplan/v2/Floorplan.h>
#include <QAbstractTableModel>
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

67
params/MetaEditWidget.cpp Normal file
View File

@@ -0,0 +1,67 @@
#include "MetaEditWidget.h"
#include "MetaEditModel.h"
#include <QTableView>
#include <QGridLayout>
#include <QKeyEvent>
#include <QPushButton>
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!
}
}
}

35
params/MetaEditWidget.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef METAEDITWIDGET_H
#define METAEDITWIDGET_H
#include <QWidget>
#include <Indoor/floorplan/v2/Floorplan.h>
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

View File

@@ -13,8 +13,19 @@
#include "../mapview/model/MMFloorAccessPoint.h"
#include "../mapview/model/MMFloorBeacon.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;
@@ -30,6 +41,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"), "");
@@ -63,6 +84,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);
@@ -342,116 +367,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() {
@@ -476,21 +410,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() {

View File

@@ -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;
@@ -49,6 +52,7 @@ private:
private slots:
void onSelect();
void onMeasure();
void onNewGround();
void onNewWall();

View File

@@ -16,5 +16,6 @@
<file>res/icons/elevator.svg</file>
<file>res/icons/add.svg</file>
<file>res/icons/remove.svg</file>
<file>res/icons/ruler.svg</file>
</qresource>
</RCC>

50
res/icons/ruler.svg Normal file
View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 496 496" style="enable-background:new 0 0 496 496;" xml:space="preserve">
<g>
<g>
<path d="M420.08,422.424l6.6-13.208L378.776,137.76c4.768-7.448,7.568-16.28,7.568-25.76c0-17.72-9.68-33.192-24-41.504V0h-48
v70.496c-14.32,8.312-24,23.784-24,41.504c0,9.48,2.8,18.312,7.568,25.76l-24.376,138.12L2.344,4.688V496h491.312L420.08,422.424z
M410.008,406.776l-7.664,15.336L394,405.424l-46.192-246.376c6.264-1.256,12.088-3.736,17.216-7.176L410.008,406.776z
M371.872,374.208l-56.464-56.464l22.936-122.336L371.872,374.208z M330.344,16h16v48.72c-2.608-0.44-5.272-0.72-8-0.72
c-2.728,0-5.392,0.28-8,0.72V16z M338.344,80c17.648,0,32,14.352,32,32s-14.352,32-32,32s-32-14.352-32-32S320.696,80,338.344,80z
M311.664,151.88c5.128,3.44,10.952,5.92,17.216,7.176l-46.192,246.376l-8.344,16.68l-7.664-15.328L311.664,151.88z M386.344,456
v24h-32v-40h-16v40h-32v-24h-16v24h-32v-40h-16v40h-32v-24h-16v24h-32v-40h-16v40h-32v-24h-16v24h-32v-40h-16v40h-32v-40h24v-16
h-24v-32h40v-16h-40v-32h24v-16h-24v-32h40v-16h-40v-32h24v-16h-24v-32h40v-16h-40v-32h24v-16h-24v-32h40V88h-40V43.312
l251.8,251.8l-13.576,76.936L82.344,197.824V416h128v-16h-112V236.448L253.176,391.28l-1.544,8.72h-25.288v16H253.4l20.944,41.888
L298,410.576l13.832-73.776l65.256,65.256l1.392,7.416L401.416,456H386.344z M402.344,457.888l10.192-20.384L455.032,480h-52.688
V457.888z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB