a whole lotta work!!
- refactoring - completely changed the tooling (adding elements) - better re-use for more stable editing - new elements - ui adjustments - LINT for stair-editing - many more changes
This commit is contained in:
64
mapview/2D/HasMoveableNodes.h
Normal file
64
mapview/2D/HasMoveableNodes.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef MV2D_IHASMOVEABLENODES_H
|
||||
#define MV2D_IHASMOVEABLENODES_H
|
||||
|
||||
#include <Indoor/geo/Point2.h>
|
||||
#include "../2D/MapView2D.h"
|
||||
|
||||
/** the selectable/moveable node */
|
||||
struct MoveableNode {
|
||||
|
||||
/** user-defined index */
|
||||
int userIdx;
|
||||
|
||||
/** the node's position */
|
||||
Point2 pos;
|
||||
|
||||
|
||||
/** ctor */
|
||||
MoveableNode(const int userIdx, const Point2 pos) : userIdx(userIdx), pos(pos) {;}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* base for all 2D elements that have selectable and moveable nodes.
|
||||
* the ToolSelector is able to get, select, and move those nodes
|
||||
*/
|
||||
class HasMoveableNodes {
|
||||
|
||||
protected:
|
||||
|
||||
/** currently selected node */
|
||||
int selectedUserIdx = -1;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** get a list of all nodes that are selectable / moveable */
|
||||
virtual std::vector<MoveableNode> getMoveableNodes() const = 0;
|
||||
|
||||
/** the given node was moved */
|
||||
virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) = 0;
|
||||
|
||||
|
||||
/** the given node was selected */
|
||||
virtual void onNodeSelect(MapView2D* v, const int userIdx) {
|
||||
(void) v;
|
||||
this->selectedUserIdx = userIdx;
|
||||
}
|
||||
|
||||
/** unselect any selected node */
|
||||
virtual void onNodeUnselect(MapView2D* v) {
|
||||
(void) v;
|
||||
this->selectedUserIdx = -1;
|
||||
}
|
||||
|
||||
/** get the currently selected node */
|
||||
virtual int getSelectedNode() const {
|
||||
return selectedUserIdx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2D_IHASMOVEABLENODES_H
|
||||
75
mapview/2D/MV2DElement.h
Normal file
75
mapview/2D/MV2DElement.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef MV2DELEMENT_H
|
||||
#define MV2DELEMENT_H
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "../2D/MapView2D.h"
|
||||
#include "../2D/Painter.h"
|
||||
|
||||
#include <Indoor/geo/BBox2.h>
|
||||
#include <Indoor/geo/Line2.h>
|
||||
|
||||
/**
|
||||
* represents one drawable, selectable, editable, ...
|
||||
* element shown within the MapView2D
|
||||
*/
|
||||
class MV2DElement {
|
||||
|
||||
private:
|
||||
|
||||
bool _focused = false;
|
||||
|
||||
public:
|
||||
|
||||
/** dtor */
|
||||
virtual ~MV2DElement() {;}
|
||||
|
||||
|
||||
/** get the element's 2D bounding box */
|
||||
virtual BBox2 getBoundingBox() const = 0;
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
virtual float getMinDistanceXY(const Point2 p) const = 0;
|
||||
|
||||
/** repaint me */
|
||||
virtual void paint(Painter& p) = 0;
|
||||
|
||||
|
||||
|
||||
/** got focus */
|
||||
void focus() {
|
||||
_focused = true;
|
||||
onFocus();
|
||||
}
|
||||
|
||||
/** lost focus */
|
||||
void unfocus() {
|
||||
_focused = false;
|
||||
onUnfocus();
|
||||
}
|
||||
|
||||
bool hasFocus() {return _focused;}
|
||||
|
||||
|
||||
/** mouse pressed at the given point */
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) {(void) v; (void) p;}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) {(void) v; (void) p;}
|
||||
|
||||
/** mouse released */
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) {(void) v; (void) p;}
|
||||
|
||||
/** key pressed. NOTE: return true when consumed. */
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent* e) {(void) v; (void) e; return false;}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual void onFocus() = 0;
|
||||
|
||||
virtual void onUnfocus() = 0;
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENT_H
|
||||
114
mapview/2D/MV2DElementAccessPoint.h
Normal file
114
mapview/2D/MV2DElementAccessPoint.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef MV2DELEMENTACCESSPOINT_H
|
||||
#define MV2DELEMENTACCESSPOINT_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
#include "../../UIHelper.h"
|
||||
|
||||
class MV2DElementAccessPoint : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
//bool sel = false;
|
||||
Floorplan::AccessPoint* ap;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the AP to render/edit */
|
||||
MV2DElementAccessPoint(Floorplan::AccessPoint* ap) : ap(ap) {;}
|
||||
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
bbox.add(Point2(ap->pos.x, ap->pos.y));
|
||||
bbox.grow(Point2(0.1, 0.1));
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
return p.getDistance(ap->pos.xy());
|
||||
}
|
||||
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
static const QPixmap& pixmapUnfocused = UIHelper::getPixmapColored("wifi", CFG::UNFOCUS_COLOR, 16);
|
||||
static const QPixmap& pixmapFocused = UIHelper::getPixmapColored("wifi", CFG::FOCUS_COLOR, 16);
|
||||
static const QPixmap& pixmapSel = UIHelper::getPixmapColored("wifi", CFG::SEL_COLOR, 16);
|
||||
|
||||
|
||||
if (selectedUserIdx == 0) {
|
||||
//p.setPenBrush(Qt::black, CFG::SEL_COLOR);
|
||||
//p.drawCircle(ap->pos.xy());
|
||||
p.drawPixmap(ap->pos.xy(), pixmapSel);
|
||||
} else if (hasFocus()) {
|
||||
//p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
//p.drawCircle(ap->pos.xy());
|
||||
p.drawPixmap(ap->pos.xy(), pixmapFocused);
|
||||
} else {
|
||||
//p.setPenBrush(Qt::gray, Qt::NoBrush);
|
||||
//p.drawCircle(ap->pos.xy());
|
||||
p.drawPixmap(ap->pos.xy(), pixmapUnfocused);
|
||||
}
|
||||
|
||||
// label
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
p.drawDot(ap->pos.xy());
|
||||
if (p.getScaler().getScale() >= 25) {
|
||||
const std::string str = ap->name + " (" + ap->name + ")";
|
||||
p.p->drawText(p.getScaler().xms(ap->pos.x) + 10, p.getScaler().yms(ap->pos.y) + 5, str.c_str());
|
||||
} else if (p.getScaler().getScale() >= 10) {
|
||||
const std::string str = ap->name;
|
||||
p.p->drawText(p.getScaler().xms(ap->pos.x) + 10, p.getScaler().yms(ap->pos.y) + 5, str.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual std::vector<MoveableNode> getMoveableNodes() const override {
|
||||
return { MoveableNode(0, ap->pos.xy()) };
|
||||
}
|
||||
|
||||
virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override {
|
||||
(void) v;
|
||||
if (userIdx == 0) {ap->pos.x = newPos.x; ap->pos.y = newPos.y;}
|
||||
}
|
||||
|
||||
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
(void) v;
|
||||
(void) e;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTACCESSPOINT_H
|
||||
115
mapview/2D/MV2DElementBeacon.h
Normal file
115
mapview/2D/MV2DElementBeacon.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef MV2DELEMENTBEACON_H
|
||||
#define MV2DELEMENTBEACON_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
#include "../../UIHelper.h"
|
||||
|
||||
class MV2DElementBeacon : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
Floorplan::Beacon* b;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the Beacon to render/edit */
|
||||
MV2DElementBeacon(Floorplan::Beacon* b) : b(b) {;}
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
bbox.add(Point2(b->pos.x, b->pos.y));
|
||||
bbox.grow(Point2(0.1, 0.1));
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
return p.getDistance(b->pos.xy());
|
||||
}
|
||||
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
static const QPixmap& pixmapUnfocused = UIHelper::getPixmapColored("beacon", CFG::UNFOCUS_COLOR, 16);
|
||||
static const QPixmap& pixmapFocused = UIHelper::getPixmapColored("beacon", CFG::FOCUS_COLOR, 16);
|
||||
static const QPixmap& pixmapSel = UIHelper::getPixmapColored("beacon", CFG::SEL_COLOR, 16);
|
||||
|
||||
if (selectedUserIdx == 0) {
|
||||
//p.setPenBrush(Qt::black, CFG::SEL_COLOR);
|
||||
//p.drawCircle(b->pos.xy());
|
||||
p.drawPixmap(b->pos.xy(), pixmapSel);
|
||||
|
||||
} else if (hasFocus()) {
|
||||
//p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
//p.drawCircle(b->pos.xy());
|
||||
p.drawPixmap(b->pos.xy(), pixmapFocused);
|
||||
} else {
|
||||
//p.setPenBrush(Qt::gray, Qt::NoBrush);
|
||||
//p.drawCircle(b->pos.xy());
|
||||
p.drawPixmap(b->pos.xy(), pixmapUnfocused);
|
||||
}
|
||||
|
||||
// label
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
p.drawDot(b->pos.xy());
|
||||
if (p.getScaler().getScale() >= 25) {
|
||||
const std::string str = b->name + " (" + b->mac + ")";
|
||||
p.p->drawText(p.getScaler().xms(b->pos.x) + 10, p.getScaler().yms(b->pos.y) + 5, str.c_str());
|
||||
} else if (p.getScaler().getScale() >= 10) {
|
||||
const std::string str = b->name;
|
||||
p.p->drawText(p.getScaler().xms(b->pos.x) + 10, p.getScaler().yms(b->pos.y) + 5, str.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual std::vector<MoveableNode> getMoveableNodes() const override {
|
||||
return { MoveableNode(0, b->pos.xy()) };
|
||||
}
|
||||
|
||||
virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override {
|
||||
(void) v;
|
||||
if (userIdx == 0) {b->pos.x = newPos.x; b->pos.y = newPos.y;}
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
/** mouse pressed at the given point */
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
/** mouse released */
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
(void) v;
|
||||
(void) e;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTBEACON_H
|
||||
108
mapview/2D/MV2DElementFloorObstacleCircle.h
Normal file
108
mapview/2D/MV2DElementFloorObstacleCircle.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef MV2DELEMENTFLOOROBSTACLECIRCLE_H
|
||||
#define MV2DELEMENTFLOOROBSTACLECIRCLE_H
|
||||
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementFloorObstacleCircle : public MV2DElement {
|
||||
|
||||
private:
|
||||
|
||||
int selPoint = -1;
|
||||
Floorplan::FloorObstacleCircle* c;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MV2DElementFloorObstacleCircle(Floorplan::FloorObstacleCircle* c) : c(c) {;}
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
bbox.add(c->center);
|
||||
bbox.grow(Point2(c->radius, c->radius));
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
return std::min(p.getDistance(getSelPoints()[0]), p.getDistance(getSelPoints()[1]));
|
||||
}
|
||||
|
||||
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
QPen pen;// = MapElementHelper::getPen(c->material, c->type, hasFocus());
|
||||
|
||||
p.setPenBrush(pen, Qt::NoBrush);
|
||||
p.drawCircle(c->center, c->radius);
|
||||
|
||||
//QBrush brush = MapElementHelper::getBru(c->material, c->type, _focused);
|
||||
|
||||
// selected endpoints?
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR);
|
||||
if (selPoint == 0) {p.drawCircle(getSelPoints()[0]);}
|
||||
if (selPoint == 1) {p.drawCircle(getSelPoints()[1]);}
|
||||
}
|
||||
|
||||
// available endpoints
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
p.drawCircle(getSelPoints()[0]);
|
||||
p.drawCircle(getSelPoints()[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<Point2> getSelPoints() const {
|
||||
return {c->center, (c->center + Point2(c->radius,0))};
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
selPoint = -1;
|
||||
}
|
||||
|
||||
/** mouse pressed at the given point */
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
|
||||
}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
if (selPoint == -1) {return;}
|
||||
const Point2 p = v->getScaler().snap(_p);
|
||||
if (selPoint == 0) {c->center = p;}
|
||||
if (selPoint == 1) {c->radius = p.getDistance(c->center);}
|
||||
}
|
||||
|
||||
/** mouse released */
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 _p) override {
|
||||
const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX);
|
||||
const float l1 = _p.getDistance(getSelPoints()[0]);
|
||||
const float l2 = _p.getDistance(getSelPoints()[1]);
|
||||
if (l1 < l2 && l1 <= t) {selPoint = 0;}
|
||||
else if (l2 < l1 && l2 <= t) {selPoint = 1;}
|
||||
else {selPoint = -1;}
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
(void) v;
|
||||
(void) e;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTFLOOROBSTACLECIRCLE_H
|
||||
146
mapview/2D/MV2DElementFloorObstacleDoor.h
Normal file
146
mapview/2D/MV2DElementFloorObstacleDoor.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#ifndef MV2DELEMENTFLOOROBSTACLEDOOR_H
|
||||
#define MV2DELEMENTFLOOROBSTACLEDOOR_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "MapViewElementHelper.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementFloorObstacleDoor : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
//int selPoint = -1;
|
||||
Floorplan::FloorObstacleDoor* fo;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MV2DElementFloorObstacleDoor(Floorplan::FloorObstacleDoor* fo) : fo(fo) {;}
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
bbox.add(fo->from);
|
||||
bbox.add(fo->to);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
return MapElementHelper::getLineDistanceXY(fo->from, fo->to, p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
// selected endpoints?
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR);
|
||||
if (selectedUserIdx == 0) {p.drawCircle(fo->from);}
|
||||
if (selectedUserIdx == 1) {p.drawCircle(fo->to);}
|
||||
}
|
||||
|
||||
QPen pen;
|
||||
pen.setColor(QColor(0.5,0.5,0.5));
|
||||
pen.setStyle(Qt::PenStyle::DotLine);
|
||||
p.setPenBrush(pen, Qt::NoBrush);
|
||||
|
||||
// opening indicator
|
||||
const float open = (fo->swap) ? (-M_PI * 0.5) : (+M_PI * 0.5);
|
||||
const float len = (fo->to - fo->from).length();
|
||||
const float angle1 = std::atan2(fo->to.y-fo->from.y, fo->to.x-fo->from.x);
|
||||
const float angle2 = angle1 + open;
|
||||
const Point2 pOpen = Point2( std::cos(angle2) * len, std::sin(angle2) * len ) + fo->from;
|
||||
|
||||
pen.setWidth(2); p.setPen(pen);
|
||||
p.drawLine(fo->from, fo->to);
|
||||
|
||||
pen.setWidth(1); p.setPen(pen);
|
||||
p.drawLine(fo->from, pOpen);
|
||||
p.drawArc(fo->from, len, angle1, open);
|
||||
|
||||
// available endpoints
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
p.drawCircle(fo->from);
|
||||
p.drawCircle(fo->to);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
virtual void onFocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
selectedUserIdx = -1; // clear selection
|
||||
}
|
||||
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
// virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
// (void) v;
|
||||
// if (selPoint == -1) {return;}
|
||||
// const Point2 p = v->getScaler().snap(_p);
|
||||
// if (selPoint == 0) {fo->from.x = p.x; fo->from.y = p.y;}
|
||||
// if (selPoint == 1) {fo->to.x = p.x; fo->to.y = p.y;}
|
||||
// emit v->onElementChange(this);
|
||||
// }
|
||||
|
||||
/** mouse released */
|
||||
// virtual void mouseReleased(MapView2D* v, const Point2 _p) override {
|
||||
// // select a new point on mouse-release (more robust than on mouse-press)
|
||||
// const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX);
|
||||
// const float l1 = _p.getDistance(fo->from);
|
||||
// const float l2 = _p.getDistance(fo->to);
|
||||
// if (l1 < l2 && l1 <= t) {selPoint = 0;}
|
||||
// else if (l2 < l1 && l2 <= t) {selPoint = 1;}
|
||||
// else {selPoint = -1;}
|
||||
|
||||
// }
|
||||
|
||||
/** get a list of all nodes that are selectable / moveable */
|
||||
virtual std::vector<MoveableNode> getMoveableNodes() const override {
|
||||
std::vector<MoveableNode> nodes = {
|
||||
MoveableNode(0, fo->from),
|
||||
MoveableNode(1, fo->to)
|
||||
};
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/** the given node was moved */
|
||||
virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override {
|
||||
switch (userIdx) {
|
||||
case 0: fo->from = newPos; break;
|
||||
case 1: fo->to = newPos; break;
|
||||
}
|
||||
emit v->onElementChange(this);
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
(void) v;
|
||||
(void) e;
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTFLOOROBSTACLEDOOR_H
|
||||
113
mapview/2D/MV2DElementFloorObstacleLine.h
Normal file
113
mapview/2D/MV2DElementFloorObstacleLine.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#ifndef MV2DELEMENTFLOOROBSTACLELINE_H
|
||||
#define MV2DELEMENTFLOOROBSTACLELINE_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementFloorObstacleLine : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
Floorplan::FloorObstacleLine* fo;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MV2DElementFloorObstacleLine(Floorplan::FloorObstacleLine* fo) : fo(fo) {;}
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
bbox.add(fo->from);
|
||||
bbox.add(fo->to);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
return MapElementHelper::getLineDistanceXY(fo->from, fo->to, p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
// selected endpoints?
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR);
|
||||
if (selectedUserIdx == 0) {p.drawCircle(fo->from);}
|
||||
if (selectedUserIdx == 1) {p.drawCircle(fo->to);}
|
||||
}
|
||||
|
||||
|
||||
p.setPenBrush(MapElementHelper::getPen(fo->material, fo->type, hasFocus()), Qt::NoBrush);
|
||||
p.drawLine(fo->from, fo->to);
|
||||
|
||||
// // door?
|
||||
// if (fo->type == Floorplan::ObstacleType::DOOR) {
|
||||
// paintDoor(p);
|
||||
// } else {
|
||||
// p.setPenBrush(MapElementHelper::getPen(fo->material, fo->type, hasFocus()), Qt::NoBrush);
|
||||
// p.drawLine(fo->from, fo->to);
|
||||
// }
|
||||
|
||||
// available endpoints
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
p.drawCircle(fo->from);
|
||||
p.drawCircle(fo->to);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 onFocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
void onUnfocus() override {
|
||||
selectedUserIdx = -1; // clear selection
|
||||
}
|
||||
|
||||
virtual std::vector<MoveableNode> getMoveableNodes() const override {
|
||||
return {
|
||||
MoveableNode(0, fo->from),
|
||||
MoveableNode(1, fo->to)
|
||||
};
|
||||
}
|
||||
|
||||
void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override {
|
||||
(void) v;
|
||||
if (userIdx == 0) {fo->from.x = newPos.x; fo->from.y = newPos.y;}
|
||||
if (userIdx == 1) {fo->to.x = newPos.x; fo->to.y = newPos.y;}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTFLOOROBSTACLELINE_H
|
||||
168
mapview/2D/MV2DElementFloorOutlinePolygon.h
Normal file
168
mapview/2D/MV2DElementFloorOutlinePolygon.h
Normal file
@@ -0,0 +1,168 @@
|
||||
#ifndef MV2DELEMENTFLOOROUTLINEPOLYGON_H
|
||||
#define MV2DELEMENTFLOOROUTLINEPOLYGON_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
#include "MapViewElementHelper.h"
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementFloorOutlinePolygon : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
Floorplan::FloorOutlinePolygon& fo;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MV2DElementFloorOutlinePolygon(Floorplan::FloorOutlinePolygon& fo) : fo(fo) {;}
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
for (const Point2 p : fo.poly.points) { bbox.add(p); }
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
float min = 999999;
|
||||
for (int i = 0; i < (int)fo.poly.points.size()-1; ++i) {
|
||||
const Point2 p1 = fo.poly.points[i];
|
||||
const Point2 p2 = fo.poly.points[i+1];
|
||||
const float dst = MapElementHelper::getLineDistanceXY(p1, p2, p);
|
||||
if (dst < min) {min = dst;}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
selectedUserIdx = -1; // clear selection
|
||||
}
|
||||
|
||||
void paint(Painter& p) override {
|
||||
|
||||
QBrush brush;
|
||||
|
||||
// fill-style (depends on the mode)
|
||||
switch (fo.method) {
|
||||
case Floorplan::OutlineMethod::ADD:
|
||||
brush.setStyle(Qt::BrushStyle::SolidPattern);
|
||||
brush.setColor(QColor(0,0,0,24));
|
||||
break;
|
||||
case Floorplan::OutlineMethod::REMOVE:
|
||||
brush.setStyle(Qt::BrushStyle::DiagCrossPattern);
|
||||
brush.setColor(QColor(0,0,0));
|
||||
break;
|
||||
default:
|
||||
brush.setStyle(Qt::BrushStyle::SolidPattern);
|
||||
brush.setColor(QColor(255,0,0));
|
||||
}
|
||||
|
||||
// outline + filled area
|
||||
p.setPenBrush(Qt::black, brush);
|
||||
p.drawPolygon(fo.poly.points);
|
||||
|
||||
// selected endpoints?
|
||||
if (hasFocus() && selectedUserIdx != -1) {
|
||||
p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR);
|
||||
p.drawCircle(fo.poly.points[selectedUserIdx]);
|
||||
}
|
||||
|
||||
// available endpoints
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
for (const Point2 pt : fo.poly.points) {
|
||||
p.drawCircle(pt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/** get a list of all nodes that are selectable / moveable */
|
||||
virtual std::vector<MoveableNode> getMoveableNodes() const override {
|
||||
std::vector<MoveableNode> nodes;
|
||||
for (int i = 0; i < (int) fo.poly.points.size(); ++i) {
|
||||
nodes.push_back(MoveableNode(i, fo.poly.points[i]));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/** the given node was moved */
|
||||
virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override {
|
||||
(void) v;
|
||||
fo.poly.points[userIdx].x = newPos.x;
|
||||
fo.poly.points[userIdx].y = newPos.y;
|
||||
}
|
||||
|
||||
// virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
// (void) v;
|
||||
// (void) p;
|
||||
// }
|
||||
|
||||
// virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
// (void) v;
|
||||
// if (selPoint == -1) {return;}
|
||||
// const Point2 p = v->getScaler().snap(_p);
|
||||
// fo.poly.points[selPoint].x = p.x;
|
||||
// fo.poly.points[selPoint].y = p.y;
|
||||
// }
|
||||
|
||||
// virtual void mouseReleased(MapView2D* v, const Point2 _p) override {
|
||||
|
||||
// (void) v;
|
||||
|
||||
// // if (selPoint != -1) {
|
||||
// // const Point3 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
// // fo.poly.points[selPoint].x = p.x;
|
||||
// // fo.poly.points[selPoint].y = p.y;
|
||||
// // }
|
||||
|
||||
// // select a new point on mouse-release (more robust than on mouse-press)
|
||||
// const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX);
|
||||
// auto comp = [&] (const Point2 a, const Point2 b) {return a.getDistance(_p) < b.getDistance(_p);};
|
||||
// auto it = std::min_element(fo.poly.points.begin(), fo.poly.points.end(), comp);
|
||||
// if (it == fo.poly.points.end()) {selPoint = -1;} // none found -> skip
|
||||
// else if ((*it).getDistance(_p) > t) {selPoint = -1;} // nearest distance is above threshold -> skip
|
||||
// else {selPoint = it - fo.poly.points.begin();}
|
||||
|
||||
// }
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
(void) v;
|
||||
|
||||
|
||||
if (e->key() == Qt::Key_Delete) {
|
||||
|
||||
// delete the currently selected vertex?
|
||||
if (selectedUserIdx != -1) {
|
||||
fo.poly.points.erase(fo.poly.points.begin() + selectedUserIdx);
|
||||
selectedUserIdx = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (e->key() == Qt::Key_Plus && selectedUserIdx != -1) {
|
||||
int idx1 = selectedUserIdx;
|
||||
int idx2 = (selectedUserIdx + 1) % fo.poly.points.size();
|
||||
int idxNew = idx2;
|
||||
Point2 pNew = (fo.poly.points[idx1] + fo.poly.points[idx2]) / 2.0f;
|
||||
fo.poly.points.insert(fo.poly.points.begin() + idxNew, pNew);
|
||||
selectedUserIdx = idxNew;
|
||||
return true;
|
||||
}
|
||||
|
||||
// not consumed
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTFLOOROUTLINEPOLYGON_H
|
||||
149
mapview/2D/MV2DElementFloorUnderlay.h
Normal file
149
mapview/2D/MV2DElementFloorUnderlay.h
Normal file
@@ -0,0 +1,149 @@
|
||||
#ifndef MV2DELEMENTFLOORUNDERLAY_H
|
||||
#define MV2DELEMENTFLOORUNDERLAY_H
|
||||
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "MapViewElementHelper.h"
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
/**
|
||||
* display e.g. a PNG-file below the map [as reference]
|
||||
*/
|
||||
class MV2DElementFloorUnderlay : public MV2DElement {
|
||||
|
||||
private:
|
||||
|
||||
QImage img;
|
||||
std::string tmpFile;
|
||||
Floorplan::UnderlayImage* underlay;
|
||||
BBox2 bbox;
|
||||
|
||||
int selPoint = -1;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MV2DElementFloorUnderlay(Floorplan::UnderlayImage* underlay) : underlay(underlay) {
|
||||
missing();
|
||||
}
|
||||
|
||||
void missing() {
|
||||
//img = QImage(QSize(64, 64), QImage::Format_ARGB32);
|
||||
//img.fill(0xFF000000);
|
||||
img = QImage("://res/icons/cross.png");
|
||||
}
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
(void) p;
|
||||
return CFG::SEL_THRESHOLD_SIZE_PX; // we do not know the distance from the image
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
selPoint = -1; // clear selection
|
||||
}
|
||||
|
||||
void paint(Painter& p) override {
|
||||
(void) p;
|
||||
|
||||
if (tmpFile != underlay->filename) {
|
||||
img = QImage(underlay->filename.c_str());
|
||||
if (img.size().width() == 0) {
|
||||
missing();
|
||||
}
|
||||
tmpFile = underlay->filename;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// map coordinates
|
||||
float mx1 = underlay->anchor.x;
|
||||
float my1 = underlay->anchor.y;
|
||||
float mw = img.width() * underlay->scaleX;
|
||||
float mh = img.height() * underlay->scaleY;
|
||||
float mx2 = mx1+mw;
|
||||
float my2 = my1+mh;
|
||||
|
||||
// screen coordinates
|
||||
float sx1 = p.s.xms(mx1);
|
||||
float sy1 = p.s.yms(my1);
|
||||
float sx2 = p.s.xms(mx2);
|
||||
float sy2 = p.s.yms(my2);
|
||||
float sw = std::abs(sx1-sx2);
|
||||
float sh = std::abs(sy1-sy2);
|
||||
|
||||
bbox = BBox2();
|
||||
bbox.add(Point2(mx1, my1));
|
||||
bbox.add(Point2(mx2, my2));
|
||||
|
||||
|
||||
float opacity = p.p->opacity();
|
||||
p.p->setOpacity(0.50f);
|
||||
p.p->drawImage(QRectF(sx1, sy1-sh, sw, sh), img, QRectF(0,0,img.width(),img.height()));
|
||||
p.p->setOpacity(opacity);
|
||||
|
||||
// selected endpoint(s)?
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::NoPen, CFG::SEL_COLOR);
|
||||
if (selPoint == 0) {p.drawCircle(bbox.getMin());}
|
||||
}
|
||||
|
||||
// just focused?
|
||||
if (hasFocus()) {
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
p.drawRect(mx1,my1,mx2,my2);
|
||||
p.drawCircle(bbox.getMin());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
if (selPoint == -1) {return;}
|
||||
const Point2 p = v->getScaler().snap(_p);
|
||||
if (selPoint == 0) {underlay->anchor = p;}
|
||||
}
|
||||
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 _p) override {
|
||||
// select a new point on mouse-release (more robust than on mouse-press)
|
||||
const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX);
|
||||
const float l1 = _p.getDistance(bbox.getMin());
|
||||
if (l1 <= t) {selPoint = 0;}
|
||||
else {selPoint = -1;}
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
|
||||
(void) v;
|
||||
|
||||
const float s = (e->modifiers() & Qt::ShiftModifier) ? (1.0f) : (0.1f);
|
||||
|
||||
if (e->key() == Qt::Key_Up) {underlay->anchor += Point2(0, +s); return true;}
|
||||
if (e->key() == Qt::Key_Down) {underlay->anchor += Point2(0, -s); return true;}
|
||||
|
||||
if (e->key() == Qt::Key_Left) {underlay->anchor += Point2(-s, 0); return true;}
|
||||
if (e->key() == Qt::Key_Right) {underlay->anchor += Point2(+s, 0); return true;}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTFLOORUNDERLAY_H
|
||||
119
mapview/2D/MV2DElementPOI.h
Normal file
119
mapview/2D/MV2DElementPOI.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#ifndef MV2DELEMENTPOI_H
|
||||
#define MV2DELEMENTPOI_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
#include "../../UIHelper.h"
|
||||
|
||||
class MV2DElementPOI : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
//bool sel = false;
|
||||
Floorplan::POI* poi;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the AP to render/edit */
|
||||
MV2DElementPOI(Floorplan::POI* poi) : poi(poi) {;}
|
||||
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
bbox.add(Point2(poi->pos.x, poi->pos.y));
|
||||
bbox.grow(Point2(0.1, 0.1));
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
return p.getDistance(poi->pos);
|
||||
}
|
||||
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
static const QPixmap& pixmapUnfocused = UIHelper::getPixmapColored("poi", CFG::UNFOCUS_COLOR, 16);
|
||||
static const QPixmap& pixmapFocused = UIHelper::getPixmapColored("poi", CFG::FOCUS_COLOR, 16);
|
||||
static const QPixmap& pixmapSel = UIHelper::getPixmapColored("poi", CFG::SEL_COLOR, 16);
|
||||
|
||||
|
||||
if (selectedUserIdx == 0) {
|
||||
// p.setPenBrush(Qt::black, CFG::SEL_COLOR);
|
||||
// p.drawCircle(poi->pos);
|
||||
p.drawPixmap(poi->pos, pixmapSel);
|
||||
} else if (hasFocus()) {
|
||||
// p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
// p.drawCircle(poi->pos);
|
||||
p.drawPixmap(poi->pos, pixmapFocused);
|
||||
} else {
|
||||
// p.setPenBrush(Qt::gray, Qt::NoBrush);
|
||||
// p.drawCircle(poi->pos);
|
||||
p.drawPixmap(poi->pos, pixmapUnfocused);
|
||||
}
|
||||
|
||||
// label
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
p.drawDot(poi->pos);
|
||||
if (p.getScaler().getScale() >= 10) {
|
||||
const std::string str = poi->name;
|
||||
p.p->drawText(p.getScaler().xms(poi->pos.x) + 10, p.getScaler().yms(poi->pos.y) + 5, str.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
virtual std::vector<MoveableNode> getMoveableNodes() const override {
|
||||
return { MoveableNode(0, poi->pos) };
|
||||
}
|
||||
|
||||
virtual void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override {
|
||||
(void) v;
|
||||
if (userIdx == 0) {poi->pos = newPos;}
|
||||
}
|
||||
|
||||
|
||||
/** mouse pressed at the given point */
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
// if (sel) {
|
||||
// const Point2 p = v->getScaler().snap(_p);
|
||||
// poi->pos.x = p.x;
|
||||
// poi->pos.y = p.y;
|
||||
// }
|
||||
}
|
||||
|
||||
/** mouse released */
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
(void) v;
|
||||
(void) e;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTPOI_H
|
||||
256
mapview/2D/MV2DElementStair.h
Normal file
256
mapview/2D/MV2DElementStair.h
Normal file
@@ -0,0 +1,256 @@
|
||||
#ifndef MV2DELEMENTSTAIR_H
|
||||
#define MV2DELEMENTSTAIR_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementStair : public MV2DElement {
|
||||
|
||||
private:
|
||||
|
||||
bool sel = false;
|
||||
Floorplan::Floor* floor;
|
||||
Floorplan::Stair* stair;
|
||||
int selPart = -1;
|
||||
int selNode = -1;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the AP to render/edit */
|
||||
MV2DElementStair(Floorplan::Floor* floor, Floorplan::Stair* stair) : floor(floor), stair(stair) {;}
|
||||
|
||||
|
||||
/** get the element's 3D bounding box */
|
||||
BBox2 getBoundingBox() const override {
|
||||
BBox2 bbox;
|
||||
if (dynamic_cast<Floorplan::StairFreeform*>(stair)) {
|
||||
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
|
||||
for (const Floorplan::StairPart p : stair->parts) {
|
||||
bbox.add(p.start.xy());
|
||||
bbox.add(p.end.xy());
|
||||
}
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/** get the element's minimal distance (nearest whatsoever) to the given point */
|
||||
float getMinDistanceXY(const Point2 p) const override {
|
||||
|
||||
auto comp = [p] (const Floorplan::StairPart& p1, const Floorplan::StairPart& p2) {
|
||||
const float d1 = MapElementHelper::getLineDistanceXY(p1.start.xy(), p1.end.xy(), p);
|
||||
const float d2 = MapElementHelper::getLineDistanceXY(p2.start.xy(), p2.end.xy(), p);
|
||||
return d1 < d2;
|
||||
};
|
||||
|
||||
auto parts = stair->getParts();
|
||||
auto min = std::min_element(parts.begin(), parts.end(), comp);
|
||||
return MapElementHelper::getLineDistanceXY(min->start.xy(), min->end.xy(), p);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int getSelPart() const {return selPart;}
|
||||
int getSelNode() const {return selNode;}
|
||||
|
||||
static inline float clamp01(const float val) {
|
||||
if (val < 0) {return 0;}
|
||||
if (val > 1) {return 1;}
|
||||
return val;
|
||||
}
|
||||
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// if (sel) {
|
||||
// p.setPenBrush(Qt::black, CFG::SEL_COLOR);
|
||||
// p.drawCircle(stair->center);
|
||||
// } else if (hasFocus()) {
|
||||
// p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
// p.drawCircle(stair->center);
|
||||
// } else {
|
||||
// p.setPenBrush(Qt::gray, Qt::NoBrush);
|
||||
// p.drawCircle(stair->center);
|
||||
// }
|
||||
|
||||
// draw all parts of the stair (all polygons)
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
|
||||
std::vector<Floorplan::StairPart> parts = stair->getParts();
|
||||
std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(parts, floor);
|
||||
|
||||
for (int i = 0; i < (int) parts.size(); ++i) {
|
||||
|
||||
const Floorplan::StairPart& part = parts[i];
|
||||
const Floorplan::Quad3& quad = quads[i];
|
||||
|
||||
// fill the polygon with a gradient corresponding with the stair's height relative to the floor's height
|
||||
QLinearGradient gradient(p.s.xms(part.start.x), p.s.yms(part.start.y), p.s.xms(part.end.x), p.s.yms(part.end.y));
|
||||
const float p1 = 0.1 + clamp01( part.start.z / floor->height) * 0.8;
|
||||
const float p2 = 0.1 + clamp01( part.end.z / floor->height) * 0.8;
|
||||
gradient.setColorAt(0, QColor(p1*255, p1*255, p1*255, 128));
|
||||
gradient.setColorAt(1, QColor(p2*255, p2*255, p2*255, 128));
|
||||
p.setBrush(gradient);
|
||||
p.setPen(QColor(0,0,0,128));
|
||||
|
||||
// polygon-construction
|
||||
//const Floorplan::Quad3 quad = part.getQuad(floor);
|
||||
const std::vector<Point3> points = {quad.p1, quad.p2, quad.p3, quad.p4};
|
||||
p.drawPolygon(points);
|
||||
|
||||
|
||||
QPen pen;
|
||||
pen.setWidth(5);
|
||||
pen.setColor(QColor(255,0,0));
|
||||
p.setPen(pen);
|
||||
|
||||
// LINT
|
||||
if (i == 0) {
|
||||
if (quad.p1.z != floor->getStartingZ()) {
|
||||
p.drawLine(quad.p1, quad.p2);
|
||||
}
|
||||
} else if (i == (int) parts.size() - 1) {
|
||||
if (quad.p3.z != floor->getEndingZ()) {
|
||||
p.drawLine(quad.p3, quad.p4);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
if (quads[i-1].p4.z != quads[i-0].p1.z) {
|
||||
p.drawLine(quad.p1, quad.p2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hasFocus()) {
|
||||
int cnt = 0;
|
||||
std::vector<Floorplan::StairPart> parts = stair->getParts();
|
||||
for (const Floorplan::StairPart& part : parts) {
|
||||
p.setPenBrush(Qt::black, (cnt == selPart && selNode == 0) ? CFG::SEL_COLOR : Qt::NoBrush); // part start
|
||||
p.drawCircle(part.start.xy());
|
||||
p.setPenBrush(Qt::black, (cnt == selPart && selNode == 1) ? CFG::SEL_COLOR : Qt::NoBrush); // part end
|
||||
p.drawRect(part.end.xy());
|
||||
p.setPenBrush(Qt::blue, Qt::NoBrush);
|
||||
Point2 ctr = (part.start+part.end).xy() / 2;
|
||||
p.drawText(ctr, "p" + std::to_string(cnt+1)); // part name
|
||||
++cnt;
|
||||
}
|
||||
for (int i = 0; i < (int)parts.size() - 1; ++i) {
|
||||
const Point3 p1 = parts[i+0][1];
|
||||
const Point3 p2 = parts[i+1][0];
|
||||
p.drawLine(p1.xy(), p2.xy());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** mouse pressed at the given point */
|
||||
virtual void mousePressed(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
|
||||
}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
if (selPart == -1) {return;}
|
||||
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
|
||||
const Point2 p = v->getScaler().snap(_p);
|
||||
stair->parts[selPart][selNode].x = p.x;
|
||||
stair->parts[selPart][selNode].y = p.y;
|
||||
}
|
||||
|
||||
/** mouse released */
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
(void) _p;
|
||||
// select a new point on mouse-release (more robust than on mouse-press)
|
||||
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
|
||||
const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX);
|
||||
// auto comp = [&] (const Point3 a, const Point3 b) {return a.xy().getDistance(_p) < b.xy().getDistance(_p);};
|
||||
// auto it = std::min_element(stair->nodes.begin(), stair->nodes.end(), comp);
|
||||
// if (it == stair->nodes.end()) {selIdx = -1;} // none found -> skip
|
||||
// else if ((*it).xy().getDistance(_p) > t) {selIdx = -1;} // nearest distance is above threshold -> skip
|
||||
// else {selIdx = it - stair->nodes.begin();}
|
||||
|
||||
float best = 999999;
|
||||
int minPart; int minNode;
|
||||
for (int part = 0; part < (int) stair->parts.size(); ++part) {
|
||||
for (int node = 0; node < 2; ++node) {
|
||||
const float dist = stair->parts[part][node].xy().getDistance(_p);
|
||||
if (dist < best) {best = dist; minPart = part; minNode = node;}
|
||||
}
|
||||
}
|
||||
|
||||
if (best <= t) {
|
||||
selPart = minPart; selNode = minNode;
|
||||
} else {
|
||||
selPart = -1; selNode = -1;
|
||||
}
|
||||
|
||||
emit v->onElementChange(this);
|
||||
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
(void) v;
|
||||
(void) e;
|
||||
|
||||
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
|
||||
|
||||
if (e->key() == Qt::Key_Delete) {
|
||||
|
||||
// delete the currently selected vertex?
|
||||
if (selPart != -1) {
|
||||
// stair->nodes.erase(stair->nodes.begin() + selIdx);
|
||||
// selIdx = -1;
|
||||
// return true;
|
||||
}
|
||||
|
||||
} else if (e->key() == Qt::Key_Plus && selPart != -1) {
|
||||
// int idx1 = selIdx;
|
||||
// int idx2 = (selIdx + 1) % stair->nodes.size();
|
||||
// int idxNew = idx2;
|
||||
// Point3 pNew = (stair->nodes[idx1] + stair->nodes[idx2]) / 2.0f;
|
||||
// stair->nodes.insert(stair->nodes.begin() + idxNew, pNew);
|
||||
// selIdx = idxNew;
|
||||
// return true;
|
||||
const int idxNew = selPart + 1;
|
||||
const Point3 p0 = stair->parts[selPart][1];
|
||||
const Point3 p1 = p0 + Point3(1,1,0);
|
||||
const Point3 p2 = p1 + Point3(2,2,0);
|
||||
const float w = stair->parts[selPart].width;
|
||||
stair->parts.insert(stair->parts.begin() + idxNew, Floorplan::StairPart(p1, p2, w));
|
||||
return true;
|
||||
}
|
||||
|
||||
// not consumed
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
selPart = -1;
|
||||
selNode = -1;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
sel = false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV2DELEMENTSTAIR_H
|
||||
83
mapview/2D/MapView2D.h
Normal file
83
mapview/2D/MapView2D.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#ifndef MAP2D_H
|
||||
#define MAP2D_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QOpenGLWidget>
|
||||
|
||||
#include "Scaler.h"
|
||||
class MapModel;
|
||||
class MV2DElement;
|
||||
|
||||
#include "tools/Tools.h"
|
||||
|
||||
|
||||
/**
|
||||
* view to render (and edit) MapElements
|
||||
*/
|
||||
class MapView2D : public QOpenGLWidget {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** scaling within the map */
|
||||
Scaler s;
|
||||
|
||||
/** the currently active tool (if any) */
|
||||
Tools tools;
|
||||
|
||||
/** the underlying data-model */
|
||||
MapModel* model = nullptr;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MapView2D(QWidget* parent = nullptr);
|
||||
|
||||
void layerChange() {
|
||||
update();
|
||||
tools.layerChange(this);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void paintGL();
|
||||
void initializeGL();
|
||||
void resizeGL();
|
||||
|
||||
|
||||
Tools& getTools() {return tools;}
|
||||
|
||||
/** get the underlying data-model */
|
||||
MapModel* getModel() {return model;}
|
||||
|
||||
/** set the underlying data-model */
|
||||
void setModel(MapModel* mdl) {
|
||||
this->model = mdl;
|
||||
update();
|
||||
}
|
||||
|
||||
/** get the underlying scaling device */
|
||||
Scaler& getScaler() {return s;}
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
void onElementChange(MV2DElement*e);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
void mousePressEvent(QMouseEvent* e);
|
||||
void mouseMoveEvent(QMouseEvent* e);
|
||||
void mouseReleaseEvent(QMouseEvent* e);
|
||||
|
||||
void wheelEvent(QWheelEvent* e);
|
||||
|
||||
void keyPressEvent(QKeyEvent* e);
|
||||
|
||||
};
|
||||
|
||||
#endif // MAP2D_H
|
||||
81
mapview/2D/MapViewElementHelper.h
Normal file
81
mapview/2D/MapViewElementHelper.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef MAPELEMENTHELPER_H
|
||||
#define MAPELEMENTHELPER_H
|
||||
|
||||
#include <Indoor/geo/Point2.h>
|
||||
#include <Indoor/geo/Point3.h>
|
||||
#include <Indoor/geo/Line2.h>
|
||||
|
||||
#include <QColor>
|
||||
#include <QPen>
|
||||
#include <QBrush>
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
/** configuration */
|
||||
namespace CFG {
|
||||
const float MOVE_SNAP_SIZE_M = 0.1f; // in meter (= map-space)
|
||||
const int SEL_THRESHOLD_SIZE_PX = 15; // in screen-pixels (-> should depend on the current zoom)
|
||||
const QColor FOCUS_COLOR = Qt::black;
|
||||
const QColor UNFOCUS_COLOR = Qt::gray;
|
||||
const QColor SEL_COLOR = Qt::blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* contains some common helper-methods
|
||||
*/
|
||||
class MapElementHelper {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* get the minimal distance of dst to the line denoted by (p1, p2).
|
||||
* this will generate line l perpendicular to (p1, p2)
|
||||
* move l into dst
|
||||
* and calculate the cut-point between l and (p1, p2)
|
||||
*/
|
||||
static float getLineDistanceXY(Point2 p1, Point2 p2, Point2 dst) {
|
||||
|
||||
// the line (p1, p2)
|
||||
const Line2 line(p1, p2);
|
||||
|
||||
// 90 degree rotation L of the line (p1, p2)
|
||||
const Point2 vec = (p1 - p2).perpendicular();
|
||||
|
||||
// the line L
|
||||
const Line2 perb(dst-vec*100, dst+vec*100);
|
||||
|
||||
// calculate the cut betwen L and (p1,p2) (if any)
|
||||
Point2 cut(0,0);
|
||||
if (line.getSegmentIntersection(perb, cut)) {
|
||||
|
||||
// distance between cut-point and mouse
|
||||
return cut.getDistance(dst);
|
||||
|
||||
} else {
|
||||
|
||||
// no cut detected
|
||||
const float d1 = p1.getDistance(dst);
|
||||
const float d2 = p2.getDistance(dst);
|
||||
return std::min(d1, d2);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static QPen getPen(Floorplan::Material mat, Floorplan::ObstacleType type, bool focus) {
|
||||
using namespace Floorplan;
|
||||
QPen pen; pen.setColor(Qt::darkGray);
|
||||
if (focus) {pen.setColor(Qt::black);}
|
||||
if (mat == Material::CONCRETE) {pen.setWidth(3);}
|
||||
if (mat == Material::GLASS) {pen.setStyle(Qt::PenStyle::DotLine);}
|
||||
if (type == ObstacleType::HANDRAIL) {pen.setStyle(Qt::PenStyle::DashLine);}
|
||||
if (type == ObstacleType::UNKNOWN) {pen.setColor(Qt::red); pen.setWidth(5);}
|
||||
if (type == ObstacleType::PILLAR) {pen.setColor(Qt::red); pen.setWidth(5);}
|
||||
|
||||
return pen;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // MAPELEMENTHELPER_H
|
||||
137
mapview/2D/Painter.h
Normal file
137
mapview/2D/Painter.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#ifndef PAINTER_H
|
||||
#define PAINTER_H
|
||||
|
||||
#include <QPainter>
|
||||
#include <QBitmap>
|
||||
|
||||
#include <Indoor/geo/Point2.h>
|
||||
#include <Indoor/geo/Point3.h>
|
||||
|
||||
#include "Scaler.h"
|
||||
|
||||
class Painter {
|
||||
|
||||
public:
|
||||
|
||||
Scaler& s;
|
||||
QPainter* p;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Painter(Scaler& s, QPainter* p, int w, int h) : s(s), p(p), w(w), h(h) {
|
||||
p->setPen(Qt::black);
|
||||
p->setBrush(Qt::NoBrush);
|
||||
}
|
||||
|
||||
int width() {return w;}
|
||||
|
||||
int height() {return h;}
|
||||
|
||||
void drawLine(const Point2 p1, const Point2 p2) {
|
||||
p->drawLine(s.xms(p1.x), s.yms(p1.y), s.xms(p2.x), s.yms(p2.y));
|
||||
}
|
||||
void drawLine(const Point3 p1, const Point3 p2) {
|
||||
p->drawLine(s.xms(p1.x), s.yms(p1.y), s.xms(p2.x), s.yms(p2.y));
|
||||
}
|
||||
|
||||
float radToDeg(const float rad) const {
|
||||
return rad * 180 / M_PI;
|
||||
}
|
||||
|
||||
void drawArc(const Point2 center, const float radius, const float startAngleRad, const float spanAngleRad) {
|
||||
const float wh = s.ms(radius) * 2;
|
||||
const float x1 = s.xms(center.x) - wh/2;
|
||||
const float y1 = s.yms(center.y) - wh/2;
|
||||
p->drawArc(x1, y1, wh, wh, radToDeg(startAngleRad)*16, radToDeg(spanAngleRad)*16);
|
||||
}
|
||||
|
||||
void drawCircle(const Point3 center) {
|
||||
int r = 5;
|
||||
p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r);
|
||||
}
|
||||
|
||||
void drawCircle(const Point2 center) {
|
||||
int r = 5;
|
||||
p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r);
|
||||
}
|
||||
|
||||
/** draw a dot at the given map coordinates */
|
||||
void drawDot(const Point2 center) {
|
||||
int r = 1;
|
||||
p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r);
|
||||
}
|
||||
|
||||
void drawCircle(const Point2 center, const float size_m) {
|
||||
int r = s.ms(size_m);
|
||||
p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r);
|
||||
}
|
||||
|
||||
void drawLine(const float x1, const float y1, const float x2, const float y2) {
|
||||
p->drawLine(s.xms(x1), s.yms(y1), s.xms(x2), s.yms(y2));
|
||||
}
|
||||
|
||||
void drawRect(const Point2 p1, const Point2 p2) {
|
||||
drawRect(p1.x, p1.y, p2.x, p2.y);
|
||||
}
|
||||
|
||||
void drawRect(const float x1, const float y1, const float x2, const float y2) {
|
||||
float w = x2-x1;
|
||||
float h = y1-y2;
|
||||
p->drawRect(s.xms(x1), s.yms(y1), s.ms(w), s.ms(h));
|
||||
}
|
||||
|
||||
void drawRect(const Point2 center) {
|
||||
float r = s.sm(5); // 5 pixel
|
||||
drawRect(center-Point2(r,r), center+Point2(r,r));
|
||||
}
|
||||
|
||||
void drawText(const Point2 pos, const std::string& text) {
|
||||
p->drawText(s.xms(pos.x), s.yms(pos.y), text.c_str());
|
||||
}
|
||||
|
||||
void drawPolygon(const std::vector<Point2>& points) {
|
||||
std::vector<QPointF> vec;
|
||||
for (const Point2 p : points) {
|
||||
vec.push_back(QPointF(s.xms(p.x), s.yms(p.y)));
|
||||
}
|
||||
p->drawPolygon(vec.data(), vec.size());
|
||||
}
|
||||
void drawPolygon(const std::vector<Point3>& points) {
|
||||
std::vector<QPointF> vec;
|
||||
for (const Point3 p : points) {
|
||||
vec.push_back(QPointF(s.xms(p.x), s.yms(p.y)));
|
||||
}
|
||||
p->drawPolygon(vec.data(), vec.size());
|
||||
}
|
||||
|
||||
void drawPixmap(const Point2 pt, const QPixmap& img) {
|
||||
p->drawPixmap(s.xms(pt.x)-img.width()/2, s.yms(pt.y)-img.height()/2, img);
|
||||
}
|
||||
|
||||
void drawImage(const Point2 pt, const QImage& img) {
|
||||
p->drawImage(s.xms(pt.x)-img.width()/2, s.yms(pt.y)-img.height()/2, img);
|
||||
}
|
||||
|
||||
void setBrush(const QBrush& brush) { p->setBrush(brush); }
|
||||
void setBrush(const Qt::BrushStyle& brush) { p->setBrush(brush); }
|
||||
|
||||
void setPen(const QPen& pen) {p->setPen(pen); }
|
||||
void setPen(const QColor& pen) {p->setPen(pen); }
|
||||
void setPen(const Qt::PenStyle& pen) {p->setPen(pen); }
|
||||
|
||||
|
||||
|
||||
template <typename Pen, typename Brush> void setPenBrush(const Pen& pen, const Brush& brush) {setPen(pen); setBrush(brush);}
|
||||
|
||||
const Scaler& getScaler() {return s;}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // PAINTER_H
|
||||
120
mapview/2D/Scaler.h
Normal file
120
mapview/2D/Scaler.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#ifndef SCALER_H
|
||||
#define SCALER_H
|
||||
|
||||
#include <Indoor/geo/Point3.h>
|
||||
|
||||
#include <QRectF>
|
||||
|
||||
struct Rect {
|
||||
float x0;
|
||||
float y0;
|
||||
float x1;
|
||||
float y1;
|
||||
};
|
||||
|
||||
/**
|
||||
* - scale between map- and screen-space.
|
||||
* - inverts y-axis (for openGL)
|
||||
*/
|
||||
class Scaler {
|
||||
|
||||
private:
|
||||
|
||||
int w;
|
||||
int h;
|
||||
float _scale = 50;
|
||||
float _snap = 0.1;
|
||||
Point3 _offset;
|
||||
|
||||
public:
|
||||
|
||||
/** map->screen (no offset) */
|
||||
float ms(const float m) const {return m*_scale;}
|
||||
/** x map->screen */
|
||||
float xms(const float x) const {return w/2+(x+_offset.x)*_scale;}
|
||||
/** x map->screen */
|
||||
float yms(const float y) const {return h/2-(y-_offset.y)*_scale;}
|
||||
|
||||
|
||||
/** screen->map (no offset) */
|
||||
float sm(const float s) const {return (s/_scale);}
|
||||
/** x screen->map */
|
||||
float xsm(const float x) const {return ((x-w/2)/_scale)-_offset.x;}
|
||||
/** y screen->map */
|
||||
float ysm(const float y) const {return ((h/2-y)/_scale)+_offset.y;}
|
||||
/** x,y screen->map */
|
||||
Point2 sm(const Point2 p) const {return Point2(xsm(p.x), ysm(p.y));}
|
||||
|
||||
|
||||
|
||||
void setScale(const float s) {_scale = s;}
|
||||
float getScale() const {return _scale;}
|
||||
|
||||
void setOffset(Point3 p) {_offset = p;}
|
||||
Point3 getOffset() const {return _offset;}
|
||||
void addOffset(int px, int py) {_offset.x += sm(px); _offset.y += sm(py);}
|
||||
|
||||
float getLODstep() const {
|
||||
float step = 0.01;
|
||||
if (_scale <= 1000) {step = 0.1;}
|
||||
if (_scale <= 500) {step = 0.5;}
|
||||
if (_scale <= 100) {step = 1.0;}
|
||||
if (_scale <= 50) {step = 5.0;}
|
||||
if (_scale <= 10) {step = 10.0;}
|
||||
if (_scale <= 1) {step = 100.0;}
|
||||
return step;
|
||||
}
|
||||
|
||||
/** get the currently visible map-region given the provided screen-size */
|
||||
Rect getMapVisible(const int screenW, const int screenH) const {
|
||||
Rect r;
|
||||
r.x0 = xsm(0);
|
||||
r.x1 = xsm(screenW);
|
||||
r.y0 = ysm(screenH);
|
||||
r.y1 = ysm(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
/** get the currently visible map-region given the provided screen-size, SNAPED to the given grid-size */
|
||||
Rect getMapVisible(const int screenW, const int screenH, const float mapGridSize) const {
|
||||
Rect r = getMapVisible(screenW, screenH);
|
||||
r.x0 = snapFloor(r.x0, mapGridSize);
|
||||
r.y0 = snapFloor(r.y0, mapGridSize);
|
||||
r.x1 = snapCeil(r.x1, mapGridSize);
|
||||
r.y1 = snapCeil(r.y1, mapGridSize);
|
||||
return r;
|
||||
}
|
||||
|
||||
//Point2 getOffset() const {return _offset.xy();}
|
||||
Point2 getCenter() const {return Point2(-_offset.x, _offset.y);}
|
||||
|
||||
void setScreenSize(const int w, const int h) {this->w = w; this->h = h;}
|
||||
|
||||
public:
|
||||
|
||||
float majorGridLineStep() const {return 1.0f * getLODstep();}
|
||||
float minorGridLineStep() const {return majorGridLineStep() / 5.0f;}
|
||||
|
||||
/** snap everything to minor grid lines */
|
||||
float snap(const float v) const { return snap(v, minorGridLineStep()); }
|
||||
|
||||
Point2 snap(const Point2 p) const { return Point2(snap(p.x), snap(p.y)); }
|
||||
Point3 snap(const Point3 p) const { return Point3(snap(p.x), snap(p.y), snap(p.z)); }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
static float snap(const float v, const float grid) { return std::round(v/grid)*grid; }
|
||||
//static Point2 snap(const Point2 p, const float grid) { return Point2(snap(p.x, grid), snap(p.y, grid)); }
|
||||
//tatic Point3 snap(const Point3 p, const float grid) { return Point3(snap(p.x, grid), snap(p.y, grid), snap(p.z, grid)); }
|
||||
|
||||
static float snapCeil(const float v, const float grid) { return std::ceil(v/grid) * grid; }
|
||||
static float snapFloor(const float v, const float grid) { return std::floor(v/grid) * grid; }
|
||||
|
||||
//float snap(const float v) const { return v; }
|
||||
|
||||
};
|
||||
|
||||
#endif // SCALER_H
|
||||
|
||||
|
||||
0
mapview/2D/tools/Tool.cpp
Normal file
0
mapview/2D/tools/Tool.cpp
Normal file
58
mapview/2D/tools/Tool.h
Normal file
58
mapview/2D/tools/Tool.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef TOOL_H
|
||||
#define TOOL_H
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QObject>
|
||||
|
||||
#include "../Painter.h"
|
||||
|
||||
class MapView2D;
|
||||
|
||||
/**
|
||||
* interface for every MapView tool.
|
||||
* a tool may be a background tool (e.g. a ruler-display, grid, ..)
|
||||
* or a foreground (active) one, which e.g. selects nodes, creates a new element, ...
|
||||
*/
|
||||
class Tool : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Tool() {;}
|
||||
|
||||
virtual ~Tool() {;}
|
||||
|
||||
/** the tool must return its name here */
|
||||
virtual const std::string getName() const = 0;
|
||||
|
||||
/** the tool was activated (by user or by code) */
|
||||
virtual void becomesActive() {;}
|
||||
|
||||
/** the tool was deactivated (by user or by code) */
|
||||
virtual void becomesInactive() {;}
|
||||
|
||||
|
||||
/** events: return TRUE when you consumed the event */
|
||||
|
||||
virtual bool mousePressEvent(MapView2D* m, QMouseEvent* e) { (void) m; (void) e; return false; }
|
||||
virtual bool mouseMoveEvent(MapView2D* m, QMouseEvent* e) { (void) m; (void) e; return false; }
|
||||
virtual bool mouseReleaseEvent(MapView2D* m, QMouseEvent* e) { (void) m; (void) e; return false; }
|
||||
|
||||
virtual bool wheelEvent(MapView2D* m, QWheelEvent* e) { (void) m; (void) e; return false; }
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* m, QKeyEvent* e) { (void) m; (void) e; return false; }
|
||||
|
||||
virtual void paintBefore(MapView2D* m, Painter& p) { (void) m; (void) p; }
|
||||
virtual void paintAfter(MapView2D* m, Painter& p) { (void) m; (void) p; }
|
||||
|
||||
virtual void layerChange(MapView2D* m) { (void) m; }
|
||||
|
||||
signals:
|
||||
|
||||
void onHelpTextChange(QString str);
|
||||
|
||||
};
|
||||
|
||||
#endif // TOOL_H
|
||||
87
mapview/2D/tools/ToolMapGrid.h
Normal file
87
mapview/2D/tools/ToolMapGrid.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef TOOLMAPGRID_H
|
||||
#define TOOLMAPGRID_H
|
||||
|
||||
#include "Tool.h"
|
||||
|
||||
class ToolMapGrid : public Tool {
|
||||
|
||||
private:
|
||||
|
||||
bool show = true;
|
||||
|
||||
public:
|
||||
|
||||
const std::string getName() const override {
|
||||
return "MapGrid";
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* m, QKeyEvent* e) override {
|
||||
(void) m;
|
||||
if (e->key() == Qt::Key_NumberSign) { show = !show; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
void paintBefore(MapView2D* m, Painter& p) override {
|
||||
|
||||
(void) m;
|
||||
|
||||
if (!show) {return;}
|
||||
|
||||
static const QColor cB(245,245,245);
|
||||
static const QColor cN(225,225,225);
|
||||
static const QColor c0(128,128,128);
|
||||
|
||||
int w = p.width();
|
||||
int h = p.height();
|
||||
|
||||
// grid-size
|
||||
const float step = p.getScaler().getLODstep();
|
||||
const float sStep = step/5;
|
||||
|
||||
// map-region visible on the screen
|
||||
const Rect r = p.getScaler().getMapVisible(w, h, step);
|
||||
|
||||
// x-lines
|
||||
for (float x = r.x0; x < r.x1; x += step) {
|
||||
|
||||
// major-lines (without center)
|
||||
if (std::abs(x) > 0.001) {
|
||||
p.setPenBrush(cN, Qt::NoBrush);
|
||||
p.drawLine(x, r.y0, x, r.y1);
|
||||
}
|
||||
|
||||
// minor-lines
|
||||
p.setPenBrush(cB, Qt::NoBrush);
|
||||
for (float x1 = x+sStep; x1 < x+step; x1 += sStep) {
|
||||
p.drawLine(x1, r.y0, x1, r.y1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// y-lines
|
||||
for (float y = r.y0; y < r.y1; y += step) {
|
||||
|
||||
// major-lines (without center)
|
||||
if (std::abs(y) > 0.001) {
|
||||
p.setPenBrush(cN, Qt::NoBrush);
|
||||
p.drawLine(r.x0, y, r.x1, y);
|
||||
}
|
||||
|
||||
// minor-lines
|
||||
p.setPenBrush(cB, Qt::NoBrush);
|
||||
for (float y1 = y+sStep; y1 < y+step; y1 += sStep) {
|
||||
p.drawLine(r.x0, y1, r.x1, y1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// thick center lines
|
||||
p.setPenBrush(c0, Qt::NoBrush);
|
||||
p.drawLine(0, r.y0, 0, r.y1);
|
||||
p.drawLine(r.x0, 0, r.x1, 0);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // TOOLMAPGRID_H
|
||||
37
mapview/2D/tools/ToolMapZoom.h
Normal file
37
mapview/2D/tools/ToolMapZoom.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef TOOLMAPZOOM_H
|
||||
#define TOOLMAPZOOM_H
|
||||
|
||||
#include "Tool.h"
|
||||
#include "../MapView2D.h"
|
||||
|
||||
/**
|
||||
* this tool allows zooming in and out of the map
|
||||
*/
|
||||
class ToolMapZoom : public Tool {
|
||||
|
||||
public:
|
||||
|
||||
const std::string getName() const override {
|
||||
return "MapZoom";
|
||||
}
|
||||
|
||||
virtual bool wheelEvent(MapView2D* m, QWheelEvent* e) override {
|
||||
|
||||
Scaler& s = m->getScaler();
|
||||
|
||||
if (e->delta() < 0) {
|
||||
s.setScale(s.getScale() * 0.5);
|
||||
} else {
|
||||
s.setScale(s.getScale() / 0.5);
|
||||
}
|
||||
|
||||
if (s.getScale() > 1000) {s.setScale(1000);}
|
||||
if (s.getScale() < 5) {s.setScale(5);}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // TOOLMAPZOOM_H
|
||||
59
mapview/2D/tools/ToolMoveMap.h
Normal file
59
mapview/2D/tools/ToolMoveMap.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef TOOLMOVEMAP_H
|
||||
#define TOOLMOVEMAP_H
|
||||
|
||||
#include "Tool.h"
|
||||
#include "../MapView2D.h"
|
||||
|
||||
/**
|
||||
* this tool allows moving the 2D map
|
||||
*/
|
||||
class ToolMoveMap : public Tool {
|
||||
|
||||
private:
|
||||
|
||||
bool mouseIsDown = false;
|
||||
Point3 startOffset;
|
||||
int sx;
|
||||
int sy;
|
||||
|
||||
const std::string getName() const override {
|
||||
return "MapMove";
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
virtual bool mousePressEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
if (e->button() == Qt::MouseButton::MidButton) {
|
||||
mouseIsDown = true;
|
||||
this->sx = e->x();
|
||||
this->sy = e->y();
|
||||
this->startOffset = m->getScaler().getOffset();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool mouseMoveEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
if (!mouseIsDown) {return false;}
|
||||
m->getScaler().setOffset(startOffset);
|
||||
m->getScaler().addOffset(e->x()-sx, e->y()-sy);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool mouseReleaseEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
(void) m;
|
||||
(void) e;
|
||||
mouseIsDown = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// virtual void keyPressEvent(MapView2D* m, QKeyEvent* e) override {
|
||||
// (void) m;
|
||||
// (void) e;
|
||||
// // TODO: move on arrow keys?
|
||||
// }
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // TOOLMOVEMAP_H
|
||||
154
mapview/2D/tools/ToolRuler.h
Normal file
154
mapview/2D/tools/ToolRuler.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#ifndef TOOLRULER_H
|
||||
#define TOOLRULER_H
|
||||
|
||||
#include "Tool.h"
|
||||
|
||||
/**
|
||||
* draw a ruler into the 2D map view
|
||||
*/
|
||||
class ToolRuler : public Tool {
|
||||
|
||||
private:
|
||||
|
||||
int mouseX;
|
||||
int mouseY;
|
||||
|
||||
const bool fullSize = true;
|
||||
const int rs = 40; // ruler size
|
||||
const int tsMajor = 20; // major-tick size
|
||||
const int tsMinor = 5; // minor-tick size
|
||||
|
||||
public:
|
||||
|
||||
const std::string getName() const override {
|
||||
return "MapRuler";
|
||||
}
|
||||
|
||||
virtual bool mouseMoveEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
|
||||
(void) m;
|
||||
|
||||
this->mouseX = e->x();
|
||||
this->mouseY = e->y();
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
virtual void paintBefore(MapView2D* m, Painter& p) override {
|
||||
|
||||
(void) m;
|
||||
|
||||
// mouse-indicator within the image?
|
||||
if (fullSize) {
|
||||
const int w = p.width();
|
||||
const int h = p.height();
|
||||
p.setPenBrush(Qt::lightGray, Qt::NoBrush);
|
||||
p.p->drawLine(rs, mouseY, w, mouseY);
|
||||
p.p->drawLine(mouseX, rs, mouseX, h);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void paintAfter(MapView2D* m, Painter& p) override {
|
||||
|
||||
(void) m;
|
||||
|
||||
static const QColor c1(240,240,240);
|
||||
static const QColor c2(200,200,200);
|
||||
|
||||
const int w = p.width();
|
||||
const int h = p.height();
|
||||
|
||||
// ruler step-size depending on the current zoom level
|
||||
const float step = p.getScaler().getLODstep();
|
||||
const float sStep = step / 10.0f;
|
||||
|
||||
// the visible map-rect
|
||||
const Rect r = p.getScaler().getMapVisible(w, h, step);
|
||||
|
||||
QRect rx(rs,0,w-1-rs,rs);
|
||||
QRect ry(0,rs,rs,h-1-rs);
|
||||
|
||||
// background
|
||||
{
|
||||
|
||||
// samller font
|
||||
QFont f = p.p->font(); f.setPointSize(8);
|
||||
p.p->setFont(f);
|
||||
|
||||
// outline
|
||||
p.setPen(Qt::darkGray);
|
||||
|
||||
// x-axis
|
||||
QLinearGradient gx(0,0,0,rs); gx.setColorAt(0, c1); gx.setColorAt(1, c2); p.setBrush(gx);
|
||||
p.p->drawRect(rx);
|
||||
|
||||
// y-axis
|
||||
QLinearGradient gy(0,0,rs,0); gy.setColorAt(0, c2); gy.setColorAt(1, c1); p.setBrush(gy);
|
||||
p.p->drawRect(ry);
|
||||
|
||||
}
|
||||
|
||||
// mouse-indicator
|
||||
p.setPenBrush(Qt::darkGray, Qt::NoBrush);
|
||||
p.p->drawLine(0, mouseY, rs, mouseY);
|
||||
p.p->drawLine(mouseX, 0, mouseX, rs);
|
||||
|
||||
|
||||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||||
char buf[128];
|
||||
|
||||
// y-axis
|
||||
p.p->setClipRect(ry);
|
||||
for (float y = r.y0; y <= r.y1; y += step) {
|
||||
|
||||
// major-lines
|
||||
const float yMajor = p.s.yms(y);
|
||||
//if (yMajor < 0 || yMajor > h) {continue;} // stop at the end
|
||||
p.p->drawLine(0,yMajor, tsMajor,yMajor);
|
||||
|
||||
// minor-lines
|
||||
for (float y1 = y+sStep; y1 < y+step; y1 += sStep) {
|
||||
const float yMinor = p.s.yms(y1);
|
||||
p.p->drawLine(0,yMinor, tsMinor,yMinor);
|
||||
}
|
||||
|
||||
// text-label
|
||||
std::sprintf(buf, "%.1f", y);
|
||||
p.p->drawText(6, yMajor-2, buf);
|
||||
}
|
||||
|
||||
// x-axis
|
||||
p.p->setClipRect(rx);
|
||||
for (float x = r.x0; x <= r.x1; x += step) {
|
||||
|
||||
// major-lines
|
||||
const float xMajor = p.s.xms(x);
|
||||
//if (xMajor < 0 || xMajor > w) {continue;} // stop at the end
|
||||
p.p->drawLine(xMajor,0, xMajor,tsMajor);
|
||||
|
||||
// minor-lines
|
||||
for (float x1 = x+sStep; x1 < x+step; x1 += sStep) {
|
||||
const float xMinor = p.s.xms(x1);
|
||||
p.p->drawLine(xMinor,0, xMinor,tsMinor);
|
||||
}
|
||||
|
||||
// text-label
|
||||
std::sprintf(buf, "%.1f", x);
|
||||
p.p->drawText(xMajor+2, 18, buf);
|
||||
}
|
||||
|
||||
p.p->setClipping(false);
|
||||
|
||||
// snapped dot
|
||||
const Point2 mouseOnScreen(mouseX, mouseY);
|
||||
const Point2 mouseInMap = p.s.sm(mouseOnScreen);
|
||||
const Point2 snappedMouseInMap = p.s.snap(mouseInMap);
|
||||
p.drawDot(snappedMouseInMap);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // TOOLRULER_H
|
||||
0
mapview/2D/tools/ToolSelector.cpp
Normal file
0
mapview/2D/tools/ToolSelector.cpp
Normal file
304
mapview/2D/tools/ToolSelector.h
Normal file
304
mapview/2D/tools/ToolSelector.h
Normal file
@@ -0,0 +1,304 @@
|
||||
#ifndef TOOLSELECTOR_H
|
||||
#define TOOLSELECTOR_H
|
||||
|
||||
|
||||
#include "Tool.h"
|
||||
#include "../MapView2D.h"
|
||||
|
||||
#include "../../model/MapModelElement.h"
|
||||
#include "../../model/MapModel.h"
|
||||
|
||||
|
||||
/**
|
||||
* this tool allows:
|
||||
* - selecting elements within the 2D view (focus/unfocus)
|
||||
* - selecting and moving nodes of elements inheriting from HasMoveableNodes
|
||||
*
|
||||
*/
|
||||
class ToolSelector : public Tool {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
/** the currently focused MapElement (if any) */
|
||||
MapModelElement* focused = nullptr;
|
||||
|
||||
/** whether the mouse-button is currently pressed */
|
||||
bool mouseIsDown = false;
|
||||
|
||||
public:
|
||||
|
||||
virtual ~ToolSelector() {;}
|
||||
|
||||
|
||||
const std::string getName() const override {
|
||||
return "Selection";
|
||||
}
|
||||
|
||||
void becomesActive() override {
|
||||
showHelp();
|
||||
}
|
||||
|
||||
/** needed eg. when the focused element gets invalid (switching visible layers) */
|
||||
void layerChange(MapView2D* m) override {
|
||||
(void) m;
|
||||
setFocused(m, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void processUnfocus(MapView2D* m, MapModelElement* elem) {
|
||||
|
||||
MV2DElement* me = elem->getMV2D();
|
||||
if (!me) {return;}
|
||||
|
||||
// elements has selectedable nodes? unselect them
|
||||
if (dynamic_cast<HasMoveableNodes*>(me)) {
|
||||
dynamic_cast<HasMoveableNodes*>(me)->onNodeUnselect(m);
|
||||
}
|
||||
|
||||
// let the element itself process the unfocus
|
||||
me->unfocus();
|
||||
|
||||
}
|
||||
|
||||
void processPress(MapView2D* m, const Point2 p, MapModelElement* elem) {
|
||||
|
||||
MV2DElement* me = elem->getMV2D();
|
||||
if (!me) {return;}
|
||||
|
||||
// let the element itself process all events
|
||||
me->mousePressed(m, p);
|
||||
|
||||
}
|
||||
|
||||
void processMove(MapView2D* m, const Point2 p, MapModelElement* elem) {
|
||||
|
||||
MV2DElement* me = elem->getMV2D();
|
||||
if (!me) {return;}
|
||||
|
||||
// elements has selectedable nodes? try to process them
|
||||
if (dynamic_cast<HasMoveableNodes*>(me)) {
|
||||
if (moveNode(m, p, dynamic_cast<HasMoveableNodes*>(me))) {return;}
|
||||
}
|
||||
|
||||
// otherwise: let the element itself process all events
|
||||
me->mouseMove(m, p);
|
||||
|
||||
}
|
||||
|
||||
void processRelease(MapView2D* m, const Point2 p, MapModelElement* elem) {
|
||||
|
||||
MV2DElement* me = elem->getMV2D();
|
||||
if (!me) {return;}
|
||||
|
||||
// element has selectedable nodes? try to process them
|
||||
if (dynamic_cast<HasMoveableNodes*>(me)) {
|
||||
if (selectNode(m, p, dynamic_cast<HasMoveableNodes*>(me))) {return;}
|
||||
}
|
||||
|
||||
// otherwise: let the element itself process all events
|
||||
me->mouseReleased(m, p);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** the given element has selectable nodes. try to select the one near to the mouse. true if successful */
|
||||
bool selectNode(MapView2D* v, Point2 p, HasMoveableNodes* elem) {
|
||||
|
||||
// select a new point on mouse-release (more robust than on mouse-press)
|
||||
|
||||
// find the node nearest to p
|
||||
auto comp = [&] (const MoveableNode& n1, const MoveableNode& n2) {return n1.pos.getDistance(p) < n2.pos.getDistance(p);};
|
||||
auto lst = elem->getMoveableNodes();
|
||||
auto it = std::min_element(lst.begin(), lst.end(), comp);
|
||||
|
||||
// is the nearest point below the threshold?
|
||||
const float t = v->getScaler().sm(CFG::SEL_THRESHOLD_SIZE_PX);
|
||||
const float d = it->pos.getDistance(p);
|
||||
if (d < t) {
|
||||
elem->onNodeSelect(v, it->userIdx);
|
||||
return true;
|
||||
} else {
|
||||
elem->onNodeSelect(v, -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** move the currently selected node. true if successful */
|
||||
bool moveNode(MapView2D* v, Point2 p, HasMoveableNodes* elem) {
|
||||
|
||||
// no node selected? -> done
|
||||
if (elem->getSelectedNode() == -1) {return false;}
|
||||
|
||||
// snap the node
|
||||
const Point2 pSnapped = v->getScaler().snap(p);
|
||||
|
||||
// move
|
||||
elem->onNodeMove(v, elem->getSelectedNode(), pSnapped);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void showHelp() {
|
||||
if (focused) {emit onHelpTextChange("select one of the element's nodes");}
|
||||
if (!focused) {emit onHelpTextChange("select an element by clicking on it");}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief change the currently focused element. throws an event.
|
||||
* @param el the to-be-focused element or null
|
||||
* @return true if the focused element has changed. false otherwise
|
||||
*/
|
||||
bool setFocused(MapView2D* v, MapModelElement* el) {
|
||||
|
||||
// whether the focus has changed or not
|
||||
const bool focusChanged = (focused != el);
|
||||
|
||||
if (focusChanged) {
|
||||
|
||||
// unfocus the old one (if any)
|
||||
if (focused) {processUnfocus(v, focused);}
|
||||
|
||||
// set the currently focused object (if any)
|
||||
focused = el;
|
||||
|
||||
// focus the new one (if any)
|
||||
if (focused) {focused->getMV2D()->focus();}
|
||||
|
||||
// update the help-text
|
||||
showHelp();
|
||||
|
||||
emit onMapElementSelected(el);
|
||||
|
||||
}
|
||||
|
||||
// done
|
||||
return focusChanged;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
virtual bool mousePressEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
|
||||
if (e->button() != Qt::MouseButton::LeftButton) {return false;}
|
||||
|
||||
mouseIsDown = true;
|
||||
|
||||
const Scaler& s = m->getScaler();
|
||||
const Point2 p2(s.xsm(e->x()), s.ysm(e->y()));
|
||||
|
||||
const float g = m->getScaler().sm(15); // increase each BBox by 15 px (needed mainly for hor/ver lines)
|
||||
|
||||
// get all elements with bounding-box matchings
|
||||
std::vector<MapModelElement*> possible;
|
||||
for (MapModelElement* el : m->getModel()->getSelectedLayerElements()) {
|
||||
if (!el->getMV2D()) {continue;}
|
||||
BBox2 bbox = el->getMV2D()->getBoundingBox(); // elements 2D bbox
|
||||
bbox.grow(Point2(g, g)); // grow a little (needed for straight lines)
|
||||
if (bbox.contains(p2)) {possible.push_back(el);} // intersection?
|
||||
}
|
||||
|
||||
// among those, find the best-matching one (smallest distance to an intersection)
|
||||
auto lambda = [&] (const MapModelElement* el1, const MapModelElement* el2) {return el1->getMV2D()->getMinDistanceXY(p2) < el2->getMV2D()->getMinDistanceXY(p2);};
|
||||
auto it = std::min_element(possible.begin(), possible.end(), lambda);
|
||||
MapModelElement* el = (it == possible.end()) ? (nullptr) : (*it);
|
||||
|
||||
|
||||
|
||||
// focus changed? -> unfocus the old one (if any)
|
||||
if (setFocused(m, el)) {
|
||||
|
||||
;
|
||||
|
||||
} else {
|
||||
|
||||
// focus kept. provide the currently focused element with events
|
||||
if (focused) {
|
||||
processPress(m, p2, focused);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
virtual bool mouseMoveEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
|
||||
const Scaler& s = m->getScaler();
|
||||
Point2 p2(s.xsm(e->x()), s.ysm(e->y()));
|
||||
|
||||
// mouse pressed?
|
||||
if (mouseIsDown) {
|
||||
|
||||
// provide the focused element with the mouse event?
|
||||
if (focused) {processMove(m, p2, focused);}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
virtual bool mouseReleaseEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
|
||||
const Scaler& s = m->getScaler();
|
||||
Point2 p2(s.xsm(e->x()), s.ysm(e->y()));
|
||||
|
||||
// provide the focused element with the mouse event?
|
||||
if (focused) {processRelease(m, p2, focused);}
|
||||
|
||||
mouseIsDown = false;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* m, QKeyEvent* e) override {
|
||||
|
||||
(void) m;
|
||||
|
||||
if (focused) {
|
||||
|
||||
// pass it to the element which may consume this event
|
||||
if (focused->getMV2D()->keyPressEvent(m, e)) {return false;}
|
||||
|
||||
// not consumed -> additional options
|
||||
|
||||
// ESCAPE -> unfocus
|
||||
if (e->key() == Qt::Key_Escape) {setFocused(m, nullptr);}
|
||||
|
||||
// DELETE -> delete
|
||||
if (e->key() == Qt::Key_Delete) {
|
||||
focused->deleteMe();
|
||||
setFocused(m, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
signals:
|
||||
|
||||
/** map element was selected */
|
||||
void onMapElementSelected(MapModelElement* el);
|
||||
|
||||
};
|
||||
|
||||
#endif // TOOLSELECTOR_H
|
||||
0
mapview/2D/tools/Tools.cpp
Normal file
0
mapview/2D/tools/Tools.cpp
Normal file
126
mapview/2D/tools/Tools.h
Normal file
126
mapview/2D/tools/Tools.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#ifndef TOOLS_H
|
||||
#define TOOLS_H
|
||||
|
||||
#include <vector>
|
||||
#include "Tool.h"
|
||||
|
||||
/**
|
||||
* combine several tools under the interface for one tool
|
||||
*/
|
||||
class Tools : public QObject { //public Tool {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
/** all added tools */
|
||||
std::vector<Tool*> backgroundTools;
|
||||
Tool* mainTool = nullptr;
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
void mainToolChanged();
|
||||
|
||||
void onHelpTextChange(QString str);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Tools() {
|
||||
;
|
||||
}
|
||||
|
||||
virtual ~Tools() {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const std::string getName() const {//override {
|
||||
return (mainTool) ? (mainTool->getName()) : ("");
|
||||
}
|
||||
|
||||
|
||||
/** add a new background-helper-tool (ruler, grid, ..) those are always processed! */
|
||||
void addBackground(Tool* t) {
|
||||
backgroundTools.push_back(t);
|
||||
}
|
||||
|
||||
/** remove this tool */
|
||||
void removeBackground(Tool* t) {
|
||||
backgroundTools.erase(std::remove(backgroundTools.begin(), backgroundTools.end(), t));
|
||||
}
|
||||
|
||||
/** set the currently active main tool. there is only one that can be active! (e.g. selector, new wall, new polygon, ..) */
|
||||
void setMain(Tool* t) {
|
||||
if (this->mainTool) {
|
||||
this->mainTool->becomesInactive();
|
||||
disconnect(this->mainTool, &Tool::onHelpTextChange, this, &Tools::onHelpTextChange);
|
||||
}
|
||||
this->mainTool = t;
|
||||
emit mainToolChanged();
|
||||
if (this->mainTool) {
|
||||
connect(this->mainTool, &Tool::onHelpTextChange, this, &Tools::onHelpTextChange);
|
||||
this->mainTool->becomesActive();
|
||||
}
|
||||
}
|
||||
|
||||
/** get the current main tool */
|
||||
Tool* getMain() {
|
||||
return this->mainTool;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
virtual bool mousePressEvent(MapView2D* m, QMouseEvent* e) {//override {
|
||||
if (mainTool) {mainTool->mousePressEvent(m, e);}
|
||||
for (Tool* t : backgroundTools) { if(t->mousePressEvent(m, e)) {return true;} }
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool mouseMoveEvent(MapView2D* m, QMouseEvent* e) {//override {
|
||||
if (mainTool) {mainTool->mouseMoveEvent(m, e);}
|
||||
for (Tool* t : backgroundTools) { if(t->mouseMoveEvent(m, e)) {return true;} }
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool mouseReleaseEvent(MapView2D* m, QMouseEvent* e) {//override {
|
||||
if (mainTool) {mainTool->mouseReleaseEvent(m, e);}
|
||||
for (Tool* t : backgroundTools) { if(t->mouseReleaseEvent(m, e)) {return true;} }
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool wheelEvent(MapView2D* m, QWheelEvent* e) {//override {
|
||||
if (mainTool) {mainTool->wheelEvent(m, e);}
|
||||
for (Tool* t : backgroundTools) { if(t->wheelEvent(m, e)) {return true;} }
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* m, QKeyEvent* e) {//override {
|
||||
if (mainTool) {mainTool->keyPressEvent(m, e);}
|
||||
for (Tool* t : backgroundTools) { if(t->keyPressEvent(m, e)) {return true;} }
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
virtual void paintBefore(MapView2D* m, Painter& p) {//override {
|
||||
for (Tool* t : backgroundTools) {t->paintBefore(m, p);}
|
||||
if (mainTool) {mainTool->paintBefore(m, p);}
|
||||
}
|
||||
|
||||
virtual void paintAfter(MapView2D* m, Painter& p) {//override {
|
||||
for (Tool* t : backgroundTools) {t->paintAfter(m, p);}
|
||||
if (mainTool) {mainTool->paintAfter(m, p);}
|
||||
}
|
||||
|
||||
virtual void layerChange(MapView2D* m) {//override {
|
||||
for (Tool* t : backgroundTools) {t->layerChange(m);}
|
||||
if (mainTool) {mainTool->layerChange(m);}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // TOOLS_H
|
||||
Reference in New Issue
Block a user