a lot of work on th map-creator
This commit is contained in:
@@ -7,6 +7,12 @@
|
||||
#include "../model/MapModel.h"
|
||||
|
||||
|
||||
/**
|
||||
* this tool allows:
|
||||
* - selecting 2D elements within the view (focus/unfocus)
|
||||
* - selecting and moving nodes of elements inheriting from HasMoveableNodes
|
||||
*
|
||||
*/
|
||||
class ToolSelector : public Tool {
|
||||
|
||||
Q_OBJECT
|
||||
@@ -24,54 +30,115 @@ public:
|
||||
virtual ~ToolSelector() {;}
|
||||
|
||||
|
||||
virtual void mousePressEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
|
||||
if (e->button() != Qt::MouseButton::LeftButton) {return;}
|
||||
|
||||
mouseIsDown = true;
|
||||
|
||||
const Scaler& s = m->getScaler();
|
||||
Point2 p2(s.xsm(e->x()), s.ysm(e->y()));
|
||||
//Point3 p3(s.xsm(e->x()), s.ysm(e->y()), 0);
|
||||
|
||||
const float g = m->getScaler().sm(10); // increase each BBox by 10 px (needed mainly for hor/ver lines)
|
||||
|
||||
// get all elements with bounding-box matchings
|
||||
std::vector<MapModelElement*> possible;
|
||||
for (MapModelElement* el : m->getModel()->getSelectedLayerElements()) {
|
||||
if (!el->getMV2D()) {continue;}
|
||||
BBox2 bbox = el->getMV2D()->getBoundingBox(); // elements 3D bbox
|
||||
bbox.grow(Point2(g, g)); // grow a little (needed for straight lines)
|
||||
if (bbox.contains(p2)) {possible.push_back(el);} // intersection?
|
||||
}
|
||||
|
||||
// among those, find the best-matching one (smallest distance to an intersection)
|
||||
auto lambda = [&] (const MapModelElement* el1, const MapModelElement* el2) {return el1->getMV2D()->getMinDistanceXY(p2) < el2->getMV2D()->getMinDistanceXY(p2);};
|
||||
auto it = std::min_element(possible.begin(), possible.end(), lambda);
|
||||
MapModelElement* el = (it == possible.end()) ? (nullptr) : (*it);
|
||||
|
||||
|
||||
|
||||
// focus changed? -> unfocus the old one (if any)
|
||||
if (setFocused(el)) {
|
||||
|
||||
;
|
||||
|
||||
} else {
|
||||
|
||||
// focus kept. provide the currently focused element with events
|
||||
if (focused) {focused->getMV2D()->mousePressed(m, p2);}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** needed eg. when the focused element gets invalid (switching visible layers) */
|
||||
void layerChange(MapView2D* m) override {
|
||||
(void) m;
|
||||
setFocused(nullptr);
|
||||
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:
|
||||
|
||||
/**
|
||||
@@ -79,7 +146,7 @@ private:
|
||||
* @param el the to-be-focused element or null
|
||||
* @return true if the focused element has changed. false otherwise
|
||||
*/
|
||||
bool setFocused(MapModelElement* el) {
|
||||
bool setFocused(MapView2D* v, MapModelElement* el) {
|
||||
|
||||
// whether the focus has changed or not
|
||||
const bool focusChanged = (focused != el);
|
||||
@@ -87,7 +154,7 @@ private:
|
||||
if (focusChanged) {
|
||||
|
||||
// unfocus the old one (if any)
|
||||
if (focused) {focused->getMV2D()->unfocus();}
|
||||
if (focused) {processUnfocus(v, focused);}
|
||||
|
||||
// set the currently focused object (if any)
|
||||
focused = el;
|
||||
@@ -104,6 +171,53 @@ private:
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
virtual void mousePressEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
|
||||
if (e->button() != Qt::MouseButton::LeftButton) {return;}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void mouseMoveEvent(MapView2D* m, QMouseEvent* e) override {
|
||||
|
||||
const Scaler& s = m->getScaler();
|
||||
@@ -113,7 +227,7 @@ private:
|
||||
if (mouseIsDown) {
|
||||
|
||||
// provide the focused element with the mouse event?
|
||||
if (focused) {focused->getMV2D()->mouseMove(m, p2); return;}
|
||||
if (focused) {processMove(m, p2, focused);}
|
||||
|
||||
}
|
||||
|
||||
@@ -125,7 +239,7 @@ private:
|
||||
Point2 p2(s.xsm(e->x()), s.ysm(e->y()));
|
||||
|
||||
// provide the focused element with the mouse event?
|
||||
if (focused) {focused->getMV2D()->mouseReleased(m, p2);}
|
||||
if (focused) {processRelease(m, p2, focused);}
|
||||
|
||||
mouseIsDown = false;
|
||||
|
||||
@@ -142,12 +256,12 @@ private:
|
||||
// not consumed -> additional options
|
||||
|
||||
// ESCAPE -> unfocus
|
||||
if (e->key() == Qt::Key_Escape) {setFocused(nullptr);}
|
||||
if (e->key() == Qt::Key_Escape) {setFocused(m, nullptr);}
|
||||
|
||||
// DELETE -> delete
|
||||
if (e->key() == Qt::Key_Delete) {
|
||||
focused->deleteMe();
|
||||
setFocused(nullptr);
|
||||
setFocused(m, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user