a lot of work on th map-creator
This commit is contained in:
35
mapview/3D/MV3DElementFloorObstacleDoor.h
Normal file
35
mapview/3D/MV3DElementFloorObstacleDoor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef MV3DELEMENTFLOOROBSTACLEDOOR_H
|
||||
#define MV3DELEMENTFLOOROBSTACLEDOOR_H
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
#include <Indoor/math/Math.h>
|
||||
|
||||
#include "MV3DElement.h"
|
||||
#include "misc/Plane.h"
|
||||
|
||||
|
||||
|
||||
class MV3DElementFloorObstacleDoor : public MV3DElement {
|
||||
|
||||
Floorplan::Floor* f;
|
||||
Floorplan::FloorObstacleDoor* fo;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MV3DElementFloorObstacleDoor(Floorplan::Floor* f, Floorplan::FloorObstacleDoor* fo) : f(f), fo(fo) {
|
||||
;
|
||||
}
|
||||
|
||||
/** repaint me */
|
||||
void paintGL() override {
|
||||
|
||||
glColor3f(0,1,0);
|
||||
Plane p(fo->from, fo->to, f->atHeight, fo->height);
|
||||
p.paintGL();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV3DELEMENTFLOOROBSTACLEDOOR_H
|
||||
@@ -2,9 +2,12 @@
|
||||
#define MV3DELEMENTFLOOROBSTACLEWALL_H
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
#include <Indoor/math/Math.h>
|
||||
|
||||
#include "MV3DElement.h"
|
||||
|
||||
|
||||
|
||||
class MV3DElementFloorObstacleWall : public MV3DElement {
|
||||
|
||||
Floorplan::Floor* f;
|
||||
@@ -19,39 +22,39 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
Point3 cross(Point3 u, Point3 v) {
|
||||
float x = u.y*v.z - u.z*v.y;
|
||||
float y = u.z*v.x - u.x*v.z;
|
||||
float z = u.x*v.y - u.y*v.x;
|
||||
return Point3(x,y,z);
|
||||
}
|
||||
|
||||
/** repaint me */
|
||||
void paintGL() override {
|
||||
|
||||
float y1 = f->atHeight;
|
||||
float y2 = y1+f->height;
|
||||
|
||||
|
||||
struct Wall {
|
||||
|
||||
Point2 from;
|
||||
Point2 to;
|
||||
float atHeight;
|
||||
float height;
|
||||
|
||||
Wall(const Point2 from, const Point2 to, float atHeight, float height) :
|
||||
from(from), to(to), atHeight(atHeight), height(height) {;}
|
||||
|
||||
void paintGL() {
|
||||
|
||||
Point3 p1 = Point3(fo->from.x, y1, fo->from.y);
|
||||
Point3 p2 = Point3(fo->to.x, y1, fo->to.y);
|
||||
Point3 p3 = Point3(fo->to.x, y2, fo->to.y);
|
||||
Point3 p4 = Point3(fo->from.x, y2, fo->from.y);
|
||||
float y1 = atHeight;
|
||||
float y2 = atHeight + height;
|
||||
|
||||
Point3 v1 = p2-p1;
|
||||
Point3 v2 = p3-p1;
|
||||
Point3 n = cross(v1, v2);
|
||||
n/=n.length();
|
||||
// polygon edges
|
||||
Point3 p1 = Point3(from.x, y1, from.y);
|
||||
Point3 p2 = Point3(to.x, y1, to.y);
|
||||
Point3 p3 = Point3(to.x, y2, to.y);
|
||||
Point3 p4 = Point3(from.x, y2, from.y);
|
||||
|
||||
// align normals to virtual viewport
|
||||
Point3 view(99,99,99);
|
||||
if ((view-n).length() > (view+n).length()) {n = -n;}
|
||||
// calculate normal
|
||||
// Point3 v1 = p2-p1;
|
||||
// Point3 v2 = p3-p1;
|
||||
// Point3 n = cross(v1, v2);
|
||||
// n/=n.length();
|
||||
Point3 n = Math::normal(p2-p1, p3-p1);
|
||||
|
||||
if (fo->type == Floorplan::ObstacleType::WALL) {
|
||||
// align normals to virtual viewport
|
||||
Point3 view(99,99,99);
|
||||
if ((view-n).length() > (view+n).length()) {n = -n;}
|
||||
|
||||
// fill the wall
|
||||
glColor3f(0.75, 0.75, 0.75);
|
||||
@@ -67,17 +70,85 @@ protected:
|
||||
|
||||
}
|
||||
|
||||
glColor3f(0,0,0);
|
||||
};
|
||||
|
||||
// glDisable(GL_LIGHTING);
|
||||
// glBegin(GL_LINE_STRIP);
|
||||
// glVertex3f(p1.x, p1.y, p1.z);
|
||||
// glVertex3f(p2.x, p2.y, p2.z);
|
||||
// glVertex3f(p3.x, p3.y, p3.z);
|
||||
// glVertex3f(p4.x, p4.y, p4.z);
|
||||
// glVertex3f(p1.x, p1.y, p1.z);
|
||||
// glEnd();
|
||||
// glEnable(GL_LIGHTING);
|
||||
struct Handrail {
|
||||
|
||||
Point2 from;
|
||||
Point2 to;
|
||||
float atHeight;
|
||||
float height;
|
||||
|
||||
Handrail(const Point2 from, const Point2 to, float atHeight, float height) :
|
||||
from(from), to(to), atHeight(atHeight), height(height) {;}
|
||||
|
||||
void paintGL() {
|
||||
|
||||
float y1 = atHeight;
|
||||
float y2 = atHeight + height;
|
||||
|
||||
// polygon edges
|
||||
Point3 p1 = Point3(from.x, y1, from.y);
|
||||
Point3 p2 = Point3(to.x, y1, to.y);
|
||||
|
||||
Point3 p3 = Point3(from.x, y2, from.y);
|
||||
Point3 p4 = Point3(to.x, y2, to.y);
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glBegin(GL_LINES);
|
||||
|
||||
glColor3f(1,1,1);
|
||||
|
||||
// top
|
||||
glVertex3f(p3.x, p3.y, p3.z);
|
||||
glVertex3f(p4.x, p4.y, p4.z);
|
||||
|
||||
// start bar
|
||||
glVertex3f(p1.x, p1.y, p1.z);
|
||||
glVertex3f(p3.x, p3.y, p3.z);
|
||||
|
||||
// end bar
|
||||
glVertex3f(p2.x, p2.y, p2.z);
|
||||
glVertex3f(p4.x, p4.y, p4.z);
|
||||
|
||||
glColor3f(0.6, 0.6, 0.6);
|
||||
|
||||
// intermediate bars
|
||||
const Point3 d1 = p2-p1;
|
||||
const Point3 d2 = p4-p3;
|
||||
const int numBars = d2.length() / 1;
|
||||
for (int i = 1; i < numBars; ++i) {
|
||||
const Point3 s = p1 + d1 * i / numBars;
|
||||
const Point3 e = p3 + d2 * i / numBars;
|
||||
glVertex3f(s.x, s.y, s.z);
|
||||
glVertex3f(e.x, e.y, e.z);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/** repaint me */
|
||||
void paintGL() override {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (fo->type == Floorplan::ObstacleType::WALL) {
|
||||
|
||||
Wall wall(fo->from, fo->to, f->atHeight, f->height);
|
||||
wall.paintGL();
|
||||
|
||||
} else if (fo->type == Floorplan::ObstacleType::HANDRAIL) {
|
||||
|
||||
Handrail rail(fo->from, fo->to, f->atHeight, 1.0);
|
||||
rail.paintGL();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
59
mapview/3D/MV3DElementStair.h
Normal file
59
mapview/3D/MV3DElementStair.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef MV3DELEMENTSTAIR_H
|
||||
#define MV3DELEMENTSTAIR_H
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
#include "misc/Cube.h"
|
||||
#include "MV3DElement.h"
|
||||
|
||||
class MV3DElementStair : public MV3DElement {
|
||||
|
||||
Floorplan::Floor* floor;
|
||||
Floorplan::Stair* stair;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
MV3DElementStair(Floorplan::Floor* floor, Floorplan::Stair* stair) : floor(floor), stair(stair) {
|
||||
;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
/** repaint me */
|
||||
void paintGL() override {
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
glColor3f(1.0, 0.55, 0.55);
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
const std::vector<Floorplan::StairPart> parts = stair->getParts();
|
||||
const 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];
|
||||
|
||||
//const Floorplan::Quad3 quad = part.getQuad(floor);
|
||||
const Point3 p1 = quad.p2-quad.p1;
|
||||
const Point3 p2 = quad.p4-quad.p1;
|
||||
const Point3 n = Math::normal(p1,p2);
|
||||
glNormal3f(n.x, n.z, n.z);
|
||||
glVertex3f(quad.p1.x, quad.p1.z, quad.p1.y);
|
||||
glVertex3f(quad.p2.x, quad.p2.z, quad.p2.y);
|
||||
glVertex3f(quad.p3.x, quad.p3.z, quad.p3.y);
|
||||
glVertex3f(quad.p4.x, quad.p4.z, quad.p4.y);
|
||||
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MV3DELEMENTSTAIR_H
|
||||
@@ -21,6 +21,8 @@ MapView3D::MapView3D(QWidget *parent) : QGLWidget(parent) {
|
||||
scale.y = 0.05f;
|
||||
scale.z = 0.05f;
|
||||
|
||||
gridRenderer = new GridRenderer();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +42,10 @@ void MapView3D::initializeGL() {
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_LIGHT0);
|
||||
glEnable(GL_LIGHT1);
|
||||
//glEnable(GL_LIGHT1);
|
||||
|
||||
GLfloat light0_position [] = {+5, 5, +5, 1};
|
||||
GLfloat light1_position [] = {-5, 5, -5, 1};
|
||||
GLfloat light0_position [] = {+50, 50, +50, 1};
|
||||
GLfloat light1_position [] = {-50, 50, -50, 1};
|
||||
|
||||
glLightfv ( GL_LIGHT0, GL_POSITION, light0_position );
|
||||
glLightfv ( GL_LIGHT1, GL_POSITION, light1_position );
|
||||
@@ -75,8 +77,6 @@ void MapView3D::paintGL() {
|
||||
|
||||
glLoadIdentity();
|
||||
|
||||
|
||||
|
||||
// 3) scale
|
||||
glScalef(scale.x, scale.y, scale.z);
|
||||
|
||||
@@ -180,8 +180,7 @@ void MapView3D::draw() {
|
||||
if (gridModel) {
|
||||
|
||||
// show grid
|
||||
GridRenderer renderer(gridModel->getGrid());
|
||||
renderer.paintGL();
|
||||
gridRenderer->paintGL(gridModel->getGrid());
|
||||
|
||||
} else {
|
||||
|
||||
@@ -189,6 +188,7 @@ void MapView3D::draw() {
|
||||
for (MapModelElement* el : getModel()->getVisibleElements()) {
|
||||
if (el->getMV3D()) {el->getMV3D()->paintGL();}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
class MapModel;
|
||||
class GridModel;
|
||||
class GridRenderer;
|
||||
|
||||
class MapView3D : public QGLWidget {
|
||||
|
||||
@@ -23,20 +24,31 @@ public:
|
||||
update();
|
||||
}
|
||||
|
||||
void layerChange() {
|
||||
update();
|
||||
}
|
||||
|
||||
/** get the underlying data-model */
|
||||
MapModel* getModel() {return model;}
|
||||
|
||||
/** get the renderer to use for the grid */
|
||||
GridRenderer* getGridRenderer() {return gridRenderer;}
|
||||
|
||||
|
||||
/** show 3D rendered floorplan */
|
||||
void showFloorplan();
|
||||
|
||||
/** show 3D rendered grid derived from the floorplan */
|
||||
void showGrid();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** the underlying data-model */
|
||||
MapModel* model = nullptr;
|
||||
|
||||
GridModel* gridModel = nullptr;
|
||||
GridRenderer* gridRenderer = nullptr;
|
||||
|
||||
Point3 rot;
|
||||
Point3 center;
|
||||
|
||||
52
mapview/3D/misc/Plane.h
Normal file
52
mapview/3D/misc/Plane.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef PLANE_H
|
||||
#define PLANE_H
|
||||
|
||||
#include <Indoor/geo/Point3.h>
|
||||
#include <QtOpenGL>
|
||||
|
||||
class Plane {
|
||||
|
||||
private:
|
||||
|
||||
Point3 p1;
|
||||
Point3 p2;
|
||||
Point3 p3;
|
||||
Point3 p4;
|
||||
Point3 n;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** construct from 2D line and heights */
|
||||
Plane(const Point2 from, const Point2 to, const float atHeight, const float height) {
|
||||
|
||||
p1 = Point3(from.x, from.y, atHeight);
|
||||
p2 = Point3(to.x, to.y, atHeight);
|
||||
p3 = Point3(to.x, to.y, atHeight+height);
|
||||
p4 = Point3(from.x, from.y, atHeight+height);
|
||||
|
||||
const Point2 perp = (to-from).perpendicular();
|
||||
n = Point3(perp.x, perp.y, 0);
|
||||
|
||||
}
|
||||
|
||||
void paintGL() {
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
// bottom
|
||||
glNormal3f(n.x, n.z, n.y);
|
||||
glVertex3f(p1.x, p1.z, p1.y);
|
||||
glVertex3f(p2.x, p2.z, p2.y);
|
||||
glVertex3f(p3.x, p3.z, p3.y);
|
||||
glVertex3f(p4.x, p4.z, p4.y);
|
||||
|
||||
glEnd();
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // PLANE_H
|
||||
@@ -4,7 +4,13 @@
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
#include <Indoor/grid/Grid.h>
|
||||
#include <Indoor/grid/factory/v2/GridFactory.h>
|
||||
#include <Indoor/grid/factory/v2/Importance.h>
|
||||
|
||||
#include <QProgressDialog>
|
||||
#include <QApplication>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QProgressBar>
|
||||
|
||||
#include "MyNode.h"
|
||||
|
||||
@@ -15,7 +21,7 @@ class GridModel {
|
||||
|
||||
private:
|
||||
|
||||
int gridSize_cm = 40;
|
||||
int gridSize_cm = 31;
|
||||
Grid<MyNode> grid;
|
||||
Floorplan::IndoorMap* im;
|
||||
|
||||
@@ -27,10 +33,60 @@ public:
|
||||
|
||||
Grid<MyNode>* getGrid() {return &grid;}
|
||||
|
||||
|
||||
class Listener : public GridFactoryListener {
|
||||
|
||||
private:
|
||||
QDialog dlg;
|
||||
QLabel* lbl1;
|
||||
QLabel* lbl2;
|
||||
QProgressBar* bar1;
|
||||
QProgressBar* bar2;
|
||||
|
||||
public:
|
||||
Listener() {
|
||||
QVBoxLayout* lay = new QVBoxLayout(&dlg);
|
||||
lbl1 = new QLabel(); lay->addWidget(lbl1);
|
||||
bar1 = new QProgressBar(); lay->addWidget(bar1);
|
||||
lbl2 = new QLabel(); lay->addWidget(lbl2);
|
||||
bar2 = new QProgressBar(); lay->addWidget(bar2);
|
||||
dlg.resize(350, 120);
|
||||
dlg.show();
|
||||
}
|
||||
~Listener() {
|
||||
dlg.close();
|
||||
}
|
||||
|
||||
void onGridBuildUpdateMajor(const std::string& what) override {
|
||||
lbl1->setText(what.c_str());
|
||||
QApplication::processEvents();
|
||||
}
|
||||
void onGridBuildUpdateMajor(const int cnt, const int cur) override {
|
||||
bar1->setValue(cur*100/cnt);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
||||
void onGridBuildUpdateMinor(const std::string& what) override {
|
||||
lbl2->setText(what.c_str());
|
||||
QApplication::processEvents();
|
||||
}
|
||||
void onGridBuildUpdateMinor(const int cnt, const int cur) override {
|
||||
bar2->setValue(cur*100/cnt);
|
||||
QApplication::processEvents();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void rebuild(Floorplan::IndoorMap* im) {
|
||||
|
||||
Listener l;
|
||||
|
||||
GridFactory<MyNode> fac(grid);
|
||||
fac.build(im);
|
||||
int i = 0;(void) i;
|
||||
fac.build(im, true, &l);
|
||||
|
||||
Importance::addImportance(grid, 0);
|
||||
//Importance::addImportance(grid, 400);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -1,40 +1,125 @@
|
||||
#ifndef GRIDRENDERER_H
|
||||
#define GRIDRENDERER_H
|
||||
|
||||
#include "../3D/MV3DElement.h"
|
||||
#include "MyNode.h"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <Indoor/grid/Grid.h>
|
||||
|
||||
enum class GridRendererColorMode {
|
||||
SHOW_NODE_TYPE,
|
||||
SHOW_NODE_IMPORTANCE,
|
||||
};
|
||||
|
||||
class GridRenderer : public MV3DElement {
|
||||
class GridRenderer {
|
||||
|
||||
private:
|
||||
|
||||
Grid<MyNode>* grid;
|
||||
// settings
|
||||
GridRendererColorMode colorMode = GridRendererColorMode::SHOW_NODE_IMPORTANCE;
|
||||
bool showEdges = false;
|
||||
|
||||
struct Color {
|
||||
float r,g,b;
|
||||
Color(float r, float g, float b) : r(r), g(g), b(b) {;}
|
||||
};
|
||||
|
||||
Color colors[4] = {
|
||||
Color(0.3, 0.3, 0.3), Color(0,0,1), Color(0,0,1), Color(0,0.65,0)
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
GridRenderer(Grid<MyNode>* grid) : grid(grid) {
|
||||
/** ctor */
|
||||
GridRenderer() {
|
||||
;
|
||||
}
|
||||
|
||||
virtual void paintGL() override {
|
||||
|
||||
void setNodeColorMode(const GridRendererColorMode mode) {this->colorMode = mode;}
|
||||
void setShowEdges(const bool show) {this->showEdges = show;}
|
||||
|
||||
|
||||
/** render the given grid using GL commands */
|
||||
void paintGL(Grid<MyNode>* grid) {
|
||||
|
||||
glDisable(GL_LIGHTING);
|
||||
glColor3f(0,0,0);
|
||||
|
||||
glPointSize(0.1f);
|
||||
const float s = 2;
|
||||
|
||||
if (showEdges) {
|
||||
std::unordered_set<uint64_t> used;
|
||||
glBegin(GL_LINES);
|
||||
for (MyNode& n1 : *grid) {
|
||||
glColor3f(0.5, 0.5, 0.5);
|
||||
for (MyNode& n2 : grid->neighbors(n1)) {
|
||||
uint64_t min = std::min(n1.getIdx(), n2.getIdx());
|
||||
uint64_t max = std::max(n1.getIdx(), n2.getIdx());
|
||||
uint64_t idx = max << 32 | min;
|
||||
if (used.find(idx) == used.end()) {
|
||||
glVertex3f(n1.x_cm/100.0f, n1.z_cm/100.0f*s, n1.y_cm/100.0f);
|
||||
glVertex3f(n2.x_cm/100.0f, n2.z_cm/100.0f*s, n2.y_cm/100.0f);
|
||||
used.insert(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
glPointSize(3.0f);
|
||||
glBegin(GL_POINTS);
|
||||
for (MyNode& n : *grid) {
|
||||
glVertex3f(n.x_cm/100.0f, n.z_cm/100.0f*5, n.y_cm/100.0f);
|
||||
|
||||
// get the color to use
|
||||
switch(colorMode) {
|
||||
case GridRendererColorMode::SHOW_NODE_TYPE: {
|
||||
const Color c = colors[n.getType()];
|
||||
glColor3f(c.r, c.g, c.b);
|
||||
break;
|
||||
}
|
||||
|
||||
case GridRendererColorMode::SHOW_NODE_IMPORTANCE: {
|
||||
const float xx = n.imp - 0.6;
|
||||
glColor3f(xx, xx, xx);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
glVertex3f(n.x_cm/100.0f, n.z_cm/100.0f*s, n.y_cm/100.0f);
|
||||
|
||||
}
|
||||
glEnd();
|
||||
|
||||
// std::vector<MyNode> vec = lint();
|
||||
// glPointSize(4.0f);
|
||||
// glBegin(GL_POINTS);
|
||||
// glColor3f(1,0,0);
|
||||
// for (MyNode& n : vec) {
|
||||
// glVertex3f(n.x_cm/100.0f, n.z_cm/100.0f*s, n.y_cm/100.0f);
|
||||
// }
|
||||
// glEnd();
|
||||
|
||||
//glEnable(GL_DEPTH_TEST);
|
||||
|
||||
|
||||
glEnable(GL_LIGHTING);
|
||||
|
||||
}
|
||||
|
||||
// std::vector<MyNode> lint() {
|
||||
// std::vector<MyNode> vec;
|
||||
// lintStair(vec);
|
||||
// return vec;
|
||||
// }
|
||||
|
||||
// void lintStair(std::vector<MyNode>& vec) {
|
||||
// for (MyNode& n1 : *grid) {
|
||||
// if (n1.getNumNeighbors() <= 5) { vec.push_back(n1);}
|
||||
// }
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
#endif // GRIDRENDERER_H
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
#include <Indoor/grid/Grid.h>
|
||||
|
||||
struct MyNode : public GridNode, public GridPoint {
|
||||
|
||||
float imp;
|
||||
|
||||
MyNode(float x, float y, float z) : GridPoint(x,y,z) {;}
|
||||
|
||||
};
|
||||
|
||||
#endif // MYNODE_H
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "Scaler.h"
|
||||
class MapModel;
|
||||
class MV2DElement;
|
||||
|
||||
#include "mapview/tools/Tools.h"
|
||||
|
||||
|
||||
@@ -60,6 +62,11 @@ public:
|
||||
Scaler& getScaler() {return s;}
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
void onElementChange(MV2DElement*e);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define PAINTER_H
|
||||
|
||||
#include <QPainter>
|
||||
#include <QBitmap>
|
||||
|
||||
#include <Indoor/geo/Point2.h>
|
||||
#include <Indoor/geo/Point3.h>
|
||||
@@ -36,6 +37,17 @@ public:
|
||||
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);
|
||||
@@ -46,6 +58,11 @@ public:
|
||||
p->drawEllipse(s.xms(center.x)-r, s.yms(center.y)-r, 2*r, 2*r);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -55,14 +72,23 @@ public:
|
||||
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.xms(pos.y), text.c_str());
|
||||
p->drawText(s.xms(pos.x), s.yms(pos.y), text.c_str());
|
||||
}
|
||||
|
||||
void drawPolygon(const std::vector<Point2>& points) {
|
||||
@@ -80,6 +106,14 @@ public:
|
||||
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); }
|
||||
|
||||
|
||||
@@ -88,9 +88,21 @@ public:
|
||||
|
||||
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)); }
|
||||
static Point3 snap(const Point3 p, const float grid) { return Point3(snap(p.x, grid), snap(p.y, grid), snap(p.z, 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; }
|
||||
|
||||
64
mapview/elements/HasMoveableNodes.h
Normal file
64
mapview/elements/HasMoveableNodes.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#ifndef MV2D_IHASMOVEABLENODES_H
|
||||
#define MV2D_IHASMOVEABLENODES_H
|
||||
|
||||
#include <Indoor/geo/Point2.h>
|
||||
#include "../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
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
/** key pressed. return true when consumed. */
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent* e) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual void onFocus() = 0;
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
#define MV2DELEMENTACCESSPOINT_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementAccessPoint : public MV2DElement {
|
||||
#include "../../UIHelper.h"
|
||||
|
||||
class MV2DElementAccessPoint : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
bool sel = false;
|
||||
//bool sel = false;
|
||||
Floorplan::AccessPoint* ap;
|
||||
|
||||
public:
|
||||
@@ -34,19 +38,28 @@ public:
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
if (sel) {
|
||||
p.setPenBrush(Qt::black, CFG::SEL_COLOR);
|
||||
p.drawCircle(ap->pos.xy());
|
||||
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.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.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());
|
||||
@@ -57,6 +70,14 @@ public:
|
||||
|
||||
}
|
||||
|
||||
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;}
|
||||
}
|
||||
|
||||
|
||||
/** mouse pressed at the given point */
|
||||
@@ -67,20 +88,15 @@ public:
|
||||
}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
if (sel) {
|
||||
const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
ap->pos.x = p.x;
|
||||
ap->pos.y = p.y;
|
||||
}
|
||||
(void) p;
|
||||
}
|
||||
|
||||
/** mouse released */
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 _p) override {
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) _p;
|
||||
sel = true;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
@@ -90,11 +106,11 @@ public:
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
sel = false;
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
#define MV2DELEMENTBEACON_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementBeacon : public MV2DElement {
|
||||
#include "../../UIHelper.h"
|
||||
|
||||
class MV2DElementBeacon : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
bool sel = false;
|
||||
Floorplan::Beacon* b;
|
||||
|
||||
public:
|
||||
@@ -33,19 +36,28 @@ public:
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
if (sel) {
|
||||
p.setPenBrush(Qt::black, CFG::SEL_COLOR);
|
||||
p.drawCircle(b->pos.xy());
|
||||
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.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.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());
|
||||
@@ -56,12 +68,21 @@ public:
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
sel = false;
|
||||
;
|
||||
}
|
||||
|
||||
/** mouse pressed at the given point */
|
||||
@@ -71,20 +92,15 @@ public:
|
||||
}
|
||||
|
||||
/** mouse moved to the given point */
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
if (sel) {
|
||||
const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
b->pos.x = p.x;
|
||||
b->pos.y = p.y;
|
||||
}
|
||||
(void) p;
|
||||
}
|
||||
|
||||
/** mouse released */
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) p;
|
||||
sel = true;
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
QPen pen = MapElementHelper::getPen(c->material, c->type, hasFocus());
|
||||
QPen pen;// = MapElementHelper::getPen(c->material, c->type, hasFocus());
|
||||
|
||||
p.setPenBrush(pen, Qt::NoBrush);
|
||||
p.drawCircle(c->center, c->radius);
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
if (selPoint == -1) {return;}
|
||||
const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
const Point2 p = v->getScaler().snap(_p);
|
||||
if (selPoint == 0) {c->center = p;}
|
||||
if (selPoint == 1) {c->radius = p.getDistance(c->center);}
|
||||
}
|
||||
|
||||
146
mapview/elements/MV2DElementFloorObstacleDoor.h
Normal file
146
mapview/elements/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
|
||||
@@ -42,10 +42,18 @@ public:
|
||||
if (selPoint == 1) {p.drawCircle(fo->to);}
|
||||
}
|
||||
|
||||
// line
|
||||
|
||||
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);
|
||||
@@ -55,6 +63,29 @@ public:
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
;
|
||||
}
|
||||
@@ -74,7 +105,7 @@ public:
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
if (selPoint == -1) {return;}
|
||||
const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
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;}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
if (selPoint == -1) {return;}
|
||||
const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
const Point2 p = v->getScaler().snap(_p);
|
||||
fo.poly.points[selPoint].x = p.x;
|
||||
fo.poly.points[selPoint].y = p.y;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
virtual void mouseMove(MapView2D* v, const Point2 _p) override {
|
||||
(void) v;
|
||||
if (selPoint == -1) {return;}
|
||||
const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
const Point2 p = v->getScaler().snap(_p);
|
||||
if (selPoint == 0) {underlay->anchor = p;}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
#define MV2DELEMENTPOI_H
|
||||
|
||||
#include "MV2DElement.h"
|
||||
#include "HasMoveableNodes.h"
|
||||
#include "MapViewElementHelper.h"
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class MV2DElementPOI : public MV2DElement {
|
||||
#include "../../UIHelper.h"
|
||||
|
||||
class MV2DElementPOI : public MV2DElement, public HasMoveableNodes {
|
||||
|
||||
private:
|
||||
|
||||
bool sel = false;
|
||||
//bool sel = false;
|
||||
Floorplan::POI* poi;
|
||||
|
||||
public:
|
||||
@@ -34,19 +37,28 @@ public:
|
||||
/** repaint me */
|
||||
void paint(Painter& p) override {
|
||||
|
||||
if (sel) {
|
||||
p.setPenBrush(Qt::black, CFG::SEL_COLOR);
|
||||
p.drawCircle(poi->pos);
|
||||
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.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.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());
|
||||
@@ -55,29 +67,37 @@ public:
|
||||
}
|
||||
|
||||
|
||||
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 {
|
||||
virtual void mouseMove(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
if (sel) {
|
||||
const Point2 p = Scaler::snap(_p, CFG::MOVE_SNAP_SIZE_M);
|
||||
poi->pos.x = p.x;
|
||||
poi->pos.y = p.y;
|
||||
}
|
||||
(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 {
|
||||
virtual void mouseReleased(MapView2D* v, const Point2 p) override {
|
||||
(void) v;
|
||||
(void) _p;
|
||||
sel = true;
|
||||
(void) p;
|
||||
}
|
||||
|
||||
virtual bool keyPressEvent(MapView2D* v, QKeyEvent *e) override {
|
||||
@@ -87,11 +107,11 @@ public:
|
||||
}
|
||||
|
||||
virtual void onFocus() override {
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
virtual void onUnfocus() override {
|
||||
sel = false;
|
||||
;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
233
mapview/elements/MV2DElementStair.h
Normal file
233
mapview/elements/MV2DElementStair.h
Normal file
@@ -0,0 +1,233 @@
|
||||
#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));
|
||||
gradient.setColorAt(1, QColor(p2*255, p2*255, p2*255));
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
@@ -15,6 +15,8 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -52,7 +54,9 @@ public:
|
||||
} else {
|
||||
|
||||
// no cut detected
|
||||
return 9999999;
|
||||
const float d1 = p1.getDistance(dst);
|
||||
const float d2 = p2.getDistance(dst);
|
||||
return std::min(d1, d2);
|
||||
|
||||
}
|
||||
|
||||
|
||||
16
mapview/model/IHasDoorType.h
Normal file
16
mapview/model/IHasDoorType.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef IHASDOORTYPE_H
|
||||
#define IHASDOORTYPE_H
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
class IHasDoorType {
|
||||
public:
|
||||
|
||||
virtual void setDoorType(const Floorplan::DoorType t) = 0;
|
||||
|
||||
virtual Floorplan::DoorType getDoorType() const = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // IHASDOORTYPE_H
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <Indoor/geo/Point3.h>
|
||||
|
||||
enum class ParamType {
|
||||
NOT_AVAILABLE,
|
||||
BOOL,
|
||||
INT,
|
||||
FLOAT,
|
||||
STRING,
|
||||
@@ -18,6 +20,7 @@ class ParamValue {
|
||||
|
||||
private:
|
||||
union {
|
||||
bool _bool;
|
||||
int _int;
|
||||
float _float;
|
||||
float _arr[3];
|
||||
@@ -31,16 +34,18 @@ public:
|
||||
}
|
||||
|
||||
void setValue(const std::string& val) {_str = val;}
|
||||
void setValue(float val) {_float = val;}
|
||||
void setValue(int val) {_int = val;}
|
||||
void setValue(Point2 p) {_arr[0] = p.x; _arr[1] = p.y;}
|
||||
void setValue(Point3 p) {_arr[0] = p.x; _arr[1] = p.y; _arr[2] = p.z;}
|
||||
void setValue(const float val) {_float = val;}
|
||||
void setValue(const int val) {_int = val;}
|
||||
void setValue(const bool val) {_bool = val;}
|
||||
void setValue(const Point2 p) {_arr[0] = p.x; _arr[1] = p.y;}
|
||||
void setValue(const Point3 p) {_arr[0] = p.x; _arr[1] = p.y; _arr[2] = p.z;}
|
||||
|
||||
Point2 toPoint2() const {return Point2(_arr[0], _arr[1]);}
|
||||
Point3 toPoint3() const {return Point3(_arr[0], _arr[1], _arr[2]);}
|
||||
std::string toString() const {return _str;}
|
||||
float toFloat() const {return _float;}
|
||||
int toInt() const {return _int;}
|
||||
bool toBool() const {return _bool;}
|
||||
|
||||
};
|
||||
|
||||
@@ -66,9 +71,23 @@ public:
|
||||
//};
|
||||
|
||||
struct Param {
|
||||
|
||||
/** parameter name */
|
||||
std::string name;
|
||||
|
||||
/** parameter type */
|
||||
ParamType type;
|
||||
Param(const std::string& name, const ParamType type) : name(name), type(type) {;}
|
||||
|
||||
/** read-only parameter? */
|
||||
bool readOnly;
|
||||
|
||||
|
||||
/** ctor */
|
||||
Param(const std::string& name, const ParamType type, const bool readOnly = false) : name(name), type(type), readOnly(readOnly) {;}
|
||||
|
||||
/** special parameter */
|
||||
static Param getNA() { return Param("", ParamType::NOT_AVAILABLE); }
|
||||
|
||||
};
|
||||
|
||||
/** free parameters */
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "MMFloorBeacons.h"
|
||||
#include "MMFloorUnderlays.h"
|
||||
#include "MMFloorPOIs.h"
|
||||
#include "MMFloorStairs.h"
|
||||
|
||||
#include "IHasParams.h"
|
||||
|
||||
@@ -37,6 +38,7 @@ public:
|
||||
new MMFloorAccessPoints(this, floor);
|
||||
new MMFloorBeacons(this, floor);
|
||||
new MMFloorPOIs(this, floor);
|
||||
new MMFloorStairs(this, floor);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
|
||||
class MMFloorObstacleCircle : public MapModelElement, public IHasMaterial, public IHasObstacleType {
|
||||
class MMFloorObstacleCircle : public MapModelElement, public IHasMaterial {
|
||||
|
||||
private:
|
||||
|
||||
@@ -31,8 +31,8 @@ public:
|
||||
void setMaterial(const Floorplan::Material m) override {c->material = m;}
|
||||
Floorplan::Material getMaterial() const override {return c->material;}
|
||||
|
||||
void setObstacleType(const Floorplan::ObstacleType t) override {c->type = t;}
|
||||
Floorplan::ObstacleType getObatcleType() const override {return c->type;}
|
||||
// void setObstacleType(const Floorplan::ObstacleType t) override {c->type = t;}
|
||||
// Floorplan::ObstacleType getObatcleType() const override {return c->type;}
|
||||
|
||||
MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;}
|
||||
|
||||
|
||||
84
mapview/model/MMFloorObstacleDoor.h
Normal file
84
mapview/model/MMFloorObstacleDoor.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef MMFLOOROBSTACLEDOOR_H
|
||||
#define MMFLOOROBSTACLEDOOR_H
|
||||
|
||||
#include "MapModelElement.h"
|
||||
#include "../elements/MapViewElementHelper.h"
|
||||
|
||||
#include "IHasMaterial.h"
|
||||
#include "IHasDoorType.h"
|
||||
#include "IHasParams.h"
|
||||
|
||||
#include "../elements/MV2DElementFloorObstacleDoor.h"
|
||||
#include "../3D/MV3DElementFloorObstacleDoor.h"
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
|
||||
class MMFloorObstacleDoor : public MapModelElement, public IHasMaterial, public IHasDoorType, public IHasParams {
|
||||
|
||||
public:
|
||||
|
||||
Floorplan::Floor* mf;
|
||||
Floorplan::FloorObstacleDoor* fo;
|
||||
MV2DElementFloorObstacleDoor mv2d;
|
||||
MV3DElementFloorObstacleDoor mv3d;
|
||||
|
||||
public:
|
||||
|
||||
MMFloorObstacleDoor(MapLayer* parent, Floorplan::Floor* mf, Floorplan::FloorObstacleDoor* fo) :
|
||||
MapModelElement(parent), mf(mf), fo(fo), mv2d(fo), mv3d(mf,fo) {
|
||||
|
||||
}
|
||||
|
||||
void setMaterial(const Floorplan::Material m) override {fo->material = m;}
|
||||
Floorplan::Material getMaterial() const override {return fo->material;}
|
||||
|
||||
void setDoorType(const Floorplan::DoorType t) override {fo->type = t;}
|
||||
Floorplan::DoorType getDoorType() const override {return fo->type;}
|
||||
|
||||
MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;}
|
||||
MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;}
|
||||
|
||||
void deleteMe() const override {
|
||||
parent->removeElement(this);
|
||||
mf->obstacles.erase(std::remove(mf->obstacles.begin(), mf->obstacles.end(), fo), mf->obstacles.end());
|
||||
}
|
||||
|
||||
/** get the number of parameters */
|
||||
int getNumParams() const override {
|
||||
return 3;
|
||||
}
|
||||
|
||||
/** get the description of the idx-th parameter */
|
||||
virtual Param getParamDesc(const int idx) const override {
|
||||
switch (idx) {
|
||||
case 0: return Param("width", ParamType::FLOAT, true);
|
||||
case 1: return Param("height", ParamType::FLOAT);
|
||||
case 2: return Param("swap", ParamType::BOOL);
|
||||
default: throw Exception("out of bounds");
|
||||
}
|
||||
}
|
||||
|
||||
/** get the idx-th param's value */
|
||||
virtual ParamValue getParamValue(const int idx) const override {
|
||||
switch(idx) {
|
||||
case 0: return fo->getSize();
|
||||
case 1: return fo->height;
|
||||
case 2: return fo->swap;
|
||||
default: throw Exception("out of bounds");
|
||||
}
|
||||
}
|
||||
|
||||
/** set the idx-th param's value */
|
||||
virtual void setParamValue(const int idx, const ParamValue& val) const override {
|
||||
switch (idx) {
|
||||
case 0: break;
|
||||
case 1: fo->height = val.toFloat(); break;
|
||||
case 2: fo->swap = val.toBool(); break;
|
||||
default: throw Exception("out of bounds");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // MMFLOOROBSTACLEDOOR_H
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "MapLayer.h"
|
||||
#include "MMFloorObstacleCircle.h"
|
||||
#include "MMFloorObstacleLine.h"
|
||||
#include "MMFloorObstacleDoor.h"
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
@@ -25,6 +26,10 @@ public:
|
||||
elements.push_back(new MMFloorObstacleLine(this, floor, (Floorplan::FloorObstacleLine*)o));
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleCircle*>(o)) {
|
||||
elements.push_back(new MMFloorObstacleCircle(this, floor, (Floorplan::FloorObstacleCircle*)o));
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(o)) {
|
||||
elements.push_back(new MMFloorObstacleDoor(this, floor, (Floorplan::FloorObstacleDoor*)o));
|
||||
} else {
|
||||
throw new Exception("todo: not yet implemented obstacle type");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +38,17 @@ public:
|
||||
/** get the corresponding floor from the underlying model */
|
||||
Floorplan::Floor* getFloor() {return floor;}
|
||||
|
||||
//TODO: check
|
||||
void createDoor(Floorplan::FloorObstacleDoor* obs) {
|
||||
|
||||
// add to underlying model
|
||||
floor->obstacles.push_back(obs);
|
||||
|
||||
// add to myself as element
|
||||
elements.push_back(new MMFloorObstacleDoor(this, floor, obs));
|
||||
|
||||
}
|
||||
|
||||
//TODO: check
|
||||
void createLine(Floorplan::FloorObstacleLine* obs) {
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
|
||||
virtual Param getParamDesc(const int idx) const override {
|
||||
switch(idx) {
|
||||
case 0: return Param("anem", ParamType::STRING);
|
||||
case 0: return Param("name", ParamType::STRING);
|
||||
}
|
||||
throw 1;
|
||||
}
|
||||
|
||||
132
mapview/model/MMFloorStair.h
Normal file
132
mapview/model/MMFloorStair.h
Normal file
@@ -0,0 +1,132 @@
|
||||
#ifndef MMFLOORSTAIR_H
|
||||
#define MMFLOORSTAIR_H
|
||||
|
||||
#include "MapLayer.h"
|
||||
#include "IHasParams.h"
|
||||
#include "MMFloorOutlinePolygon.h"
|
||||
|
||||
#include "../elements/MV2DElementStair.h"
|
||||
#include "../3D/MV3DElementStair.h"
|
||||
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
/**
|
||||
* layer containing all elements describing a floor's outline
|
||||
*/
|
||||
class MMFloorStair : public MapModelElement, public IHasParams {
|
||||
|
||||
private:
|
||||
|
||||
/** the underlying model */
|
||||
Floorplan::Floor* floor;
|
||||
Floorplan::StairFreeform* stair;
|
||||
|
||||
MV2DElementStair mv2d;
|
||||
MV3DElementStair mv3d;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the underlying model */
|
||||
MMFloorStair(MapLayer* parent, Floorplan::Floor* floor, Floorplan::StairFreeform* stair) :
|
||||
MapModelElement(parent), floor(floor), stair(stair), mv2d(floor, stair), mv3d(floor, stair) {
|
||||
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
MV2DElement* getMV2D() const override {return (MV2DElement*) &mv2d;}
|
||||
MV3DElement* getMV3D() const override {return (MV3DElement*) &mv3d;}
|
||||
|
||||
|
||||
virtual int getNumParams() const override {
|
||||
const int selPart = mv2d.getSelPart();
|
||||
return (selPart >= 0) ? (3) : (0);
|
||||
}
|
||||
|
||||
virtual Param getParamDesc(const int idx) const override {
|
||||
const int selPart = mv2d.getSelPart();
|
||||
const int selNode = mv2d.getSelNode();
|
||||
switch(idx) {
|
||||
case 0: return Param("node height", ParamType::FLOAT);
|
||||
case 1: return (selPart >= 0 && selNode == 0) ? Param("part width", ParamType::FLOAT) : Param::getNA();
|
||||
case 2: return (selPart >= 0 && selNode == 0) ? Param("connect", ParamType::BOOL) : Param::getNA();
|
||||
}
|
||||
throw 1;
|
||||
}
|
||||
|
||||
virtual ParamValue getParamValue(const int idx) const override {
|
||||
const int selPart = mv2d.getSelPart();
|
||||
const int selNode = mv2d.getSelNode();
|
||||
switch(idx) {
|
||||
case 0: if (selPart >= 0) {return stair->parts[selPart][selNode].z;} else {return NAN;}
|
||||
case 1: if (selPart >= 0) {return stair->parts[selPart].width;} else {return NAN;}
|
||||
case 2: if (selPart >= 0) {return stair->parts[selPart].connectWithPrev;} else {return false;}
|
||||
}
|
||||
throw 1;
|
||||
}
|
||||
|
||||
virtual void setParamValue(const int idx, const ParamValue& val) const override {
|
||||
const int selPart = mv2d.getSelPart();
|
||||
const int selNode = mv2d.getSelNode();
|
||||
switch(idx) {
|
||||
case 0: if (selPart >= 0) {stair->parts[selPart][selNode].z = val.toFloat();} break;
|
||||
case 1: if (selPart >= 0) {stair->parts[selPart].width = val.toFloat();} break;
|
||||
case 2: if (selPart >= 0) {stair->parts[selPart].connectWithPrev = val.toBool();} break;
|
||||
}
|
||||
}
|
||||
|
||||
void deleteMe() const override {
|
||||
parent->removeElement(this);
|
||||
floor->stairs.erase(std::remove(floor->stairs.begin(), floor->stairs.end(), stair), floor->stairs.end());
|
||||
}
|
||||
|
||||
// virtual int getNumParams() const override {
|
||||
// return 4;
|
||||
// }
|
||||
|
||||
// virtual Param getParamDesc(const int idx) const override {
|
||||
// switch(idx) {
|
||||
// case 0: return Param("center", ParamType::POINT2);
|
||||
// case 1: return Param("at height", ParamType::FLOAT);
|
||||
// case 2: return Param("height", ParamType::FLOAT);
|
||||
// case 3: return Param("angle", ParamType::FLOAT);
|
||||
// }
|
||||
// throw 1;
|
||||
// }
|
||||
|
||||
// virtual ParamValue getParamValue(const int idx) const override {
|
||||
// switch(idx) {
|
||||
// case 0: return stair->center;
|
||||
// case 1: return stair->atHeight;
|
||||
// case 2: return stair->height;
|
||||
// case 3: return stair->angleDeg;
|
||||
// }
|
||||
// throw 1;
|
||||
// }
|
||||
|
||||
// virtual void setParamValue(const int idx, const ParamValue& val) const override {
|
||||
// switch(idx) {
|
||||
// case 0: stair->center = val.toPoint2(); break;
|
||||
// case 1: stair->atHeight = val.toFloat(); break;
|
||||
// case 2: stair->height = val.toFloat(); break;
|
||||
// case 3: stair->angleDeg = val.toFloat(); break;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// //TODO: check
|
||||
// void create(Floorplan::FloorOutlinePolygon* poly) {
|
||||
|
||||
// // add to underlying model
|
||||
// floor->outline.push_back(poly);
|
||||
|
||||
// // add to myself as element
|
||||
// elements.push_back(new MMFloorOutlinePolygon(this, floor, poly));
|
||||
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
#endif // MMFLOORSTAIR_H
|
||||
48
mapview/model/MMFloorStairs.h
Normal file
48
mapview/model/MMFloorStairs.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef MMFLOORSTAIRS_H
|
||||
#define MMFLOORSTAIRS_H
|
||||
|
||||
#include "MapLayer.h"
|
||||
#include "MMFloorStair.h"
|
||||
|
||||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||||
|
||||
/**
|
||||
* layer containing all stairs of one floor
|
||||
*/
|
||||
class MMFloorStairs : public MapLayer {
|
||||
|
||||
private:
|
||||
|
||||
Floorplan::Floor* floor;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the underlying model */
|
||||
MMFloorStairs(MapLayer* parent, Floorplan::Floor* floor) : MapLayer(parent, MapLayerType::FLOOR_STAIRS), floor(floor) {
|
||||
|
||||
// add all floors
|
||||
for (Floorplan::Stair* stair : floor->stairs) {
|
||||
if (dynamic_cast<Floorplan::StairFreeform*>(stair)) {
|
||||
elements.push_back( new MMFloorStair(this, floor, (Floorplan::StairFreeform*)stair) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void create(Floorplan::StairFreeform* stair) {
|
||||
|
||||
// add to underlying model
|
||||
floor->stairs.push_back(stair);
|
||||
|
||||
// add to myself as element
|
||||
elements.push_back(new MMFloorStair(this, floor, stair));
|
||||
|
||||
}
|
||||
|
||||
std::string getLayerName() const override {return "stairs";}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // MMFLOORSTAIRS_H
|
||||
@@ -19,6 +19,7 @@ enum class MapLayerType {
|
||||
FLOOR_ACCESS_POINTS,
|
||||
FLOOR_UNDERLAYS,
|
||||
FLOOR_POIS,
|
||||
FLOOR_STAIRS,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ public:
|
||||
|
||||
if (!show) {return;}
|
||||
|
||||
static const QColor cB(250,250,250);
|
||||
static const QColor cN(235,235,235);
|
||||
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();
|
||||
|
||||
@@ -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