This repository has been archived on 2020-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
Files
IndoorMap/mapview/2D/MV2DElementStair.h

292 lines
8.7 KiB
C++

#ifndef MV2DELEMENTSTAIR_H
#define MV2DELEMENTSTAIR_H
#include "MV2DElement.h"
#include "HasMoveableNodes.h"
#include "MapViewElementHelper.h"
#include <Indoor/floorplan/v2/Floorplan.h>
class MV2DElementStair : public MV2DElement, public HasMoveableNodes {
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 */
ClickDist getMinDistanceXY(const Point2 p) const override {
auto comp = [p] (const Floorplan::StairPart& p1, const Floorplan::StairPart& p2) {
const ClickDist d1 = MapElementHelper::getLineDistanceXY(p1.start.xy(), p1.end.xy(), p);
const ClickDist 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 getSelectedNode() < 0 ? getSelectedNode() : getSelectedNode() / 2;}
int getSelNode() const {return getSelectedNode() < 0 ? getSelectedNode() : getSelectedNode() % 2;}
static inline float clamp01(const float val) {
if (val < 0) {return 0;}
if (val > 1) {return 1;}
return val;
}
/** repaint me */
void paint(Painter& p) override {
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
// if (sel) {
// p.setPenBrush(Qt::black, CFG::SEL_COLOR);
// p.drawCircle(stair->center);
// } else if (hasFocus()) {
// p.setPenBrush(Qt::black, Qt::NoBrush);
// p.drawCircle(stair->center);
// } else {
// p.setPenBrush(Qt::gray, Qt::NoBrush);
// p.drawCircle(stair->center);
// }
// draw all parts of the stair (all polygons)
p.setPenBrush(Qt::black, Qt::NoBrush);
std::vector<Floorplan::StairPart> parts = stair->getParts();
std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(parts, floor);
for (int i = 0; i < (int) parts.size(); ++i) {
const Floorplan::StairPart& part = parts[i];
const Floorplan::Quad3& quad = quads[i];
// fill the polygon with a gradient corresponding with the stair's height relative to the floor's height
QLinearGradient gradient(p.s.xms(part.start.x), p.s.yms(part.start.y), p.s.xms(part.end.x), p.s.yms(part.end.y));
const float p1 = 0.1 + clamp01( part.start.z / floor->height) * 0.8;
const float p2 = 0.1 + clamp01( part.end.z / floor->height) * 0.8;
gradient.setColorAt(0, QColor(p1*255, p1*255, p1*255, 128));
gradient.setColorAt(1, QColor(p2*255, p2*255, p2*255, 128));
p.setBrush(gradient);
p.setPen(QColor(0,0,0,128));
// polygon-construction
//const Floorplan::Quad3 quad = part.getQuad(floor);
const std::vector<Point3> points = {quad.p1, quad.p2, quad.p3, quad.p4};
p.drawPolygon(points);
QPen pen;
pen.setWidth(5);
pen.setColor(QColor(255,0,0));
p.setPen(pen);
// LINT disconnected start
if (i == 0) {
if (quad.p1.z != floor->getStartingZ()) {
p.drawLine(quad.p1, quad.p2);
}
}
// LINT disconnected end
if (i == (int) parts.size() - 1) {
if (quad.p3.z != floor->getEndingZ()) {
p.drawLine(quad.p3, quad.p4);
}
}
// LINT disconnected within
if (i > 0) {
if (quads[i-1].p4.z != quads[i-0].p1.z) {
p.drawLine(quad.p1, quad.p2);
}
}
}
if (hasFocus()) {
int cnt = 0;
std::vector<Floorplan::StairPart> parts = stair->getParts();
for (const Floorplan::StairPart& part : parts) {
p.setPenBrush(Qt::black, (cnt == getSelPart() && getSelNode() == 0) ? CFG::SEL_COLOR : Qt::NoBrush); // part start
p.drawCircle(part.start.xy());
p.setPenBrush(Qt::black, (cnt == getSelPart() && getSelNode() == 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 (getSelPart() != -1) {
// stair->nodes.erase(stair->nodes.begin() + selIdx);
// selIdx = -1;
// return true;
}
} else if (e->key() == Qt::Key_Plus && getSelPart() != -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 = getSelPart() + 1;
const Point3 p0 = stair->parts[getSelPart()][1];
const Point3 p1 = p0 + Point3(1,1,0);
const Point3 p2 = p1 + Point3(2,2,0);
const float w = stair->parts[getSelPart()].width;
stair->parts.insert(stair->parts.begin() + idxNew, Floorplan::StairPart(p1, p2, w));
return true;
}
// not consumed
return false;
}
virtual void onFocus() override {
// TODO?
}
virtual void onUnfocus() override {
sel = false;
}
virtual std::vector<MoveableNode> getMoveableNodes() const override {
std::vector<MoveableNode> nodes;
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
// get a list of all moveable nodes (2 per stair-part)
int idx = 0;
for (size_t part = 0; part < stair->parts.size(); ++part) {
for (int node = 0; node < 2; ++node) {
MoveableNode mn(idx++, stair->parts[part][node].xy());
nodes.push_back(mn);
}
}
return nodes;
}
void onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) override {
(void) v;
// convert node-index back to stair-part and node-nr within stair-part
const int selPart = userIdx / 2;
const int selNode = userIdx % 2;
// move the node
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
stair->parts[selPart][selNode].x = newPos.x;
stair->parts[selPart][selNode].y = newPos.y;
}
void onNodeSelect(MapView2D* v, const int userIdx) override {
HasMoveableNodes::onNodeSelect(v, userIdx);
emit v->onElementChange(this);
}
};
#endif // MV2DELEMENTSTAIR_H