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:
2016-08-29 19:05:46 +02:00
parent 86c76b1284
commit fa06320219
51 changed files with 880 additions and 318 deletions

View File

58
mapview/2D/tools/Tool.h Normal file
View 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

View 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

View 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

View 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

View 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

View File

View 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

View File

126
mapview/2D/tools/Tools.h Normal file
View 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