252 lines
7.3 KiB
C++
252 lines
7.3 KiB
C++
#include "MV2DElementStair.h"
|
|
|
|
#include "MV2DElement.h"
|
|
#include "HasMoveableNodes.h"
|
|
|
|
#include "MapViewElementHelper.h"
|
|
#include <Indoor/floorplan/v2/Floorplan.h>
|
|
|
|
/** is the given stair's end connected to ANY of the floorplan's floors? */
|
|
static inline bool stairEndConnected(const Floorplan::IndoorMap* map, const Floorplan::Floor* floor, const Floorplan::Stair* stair) {
|
|
const int stairEnd_cm = std::round( (floor->atHeight + stair->getParts().back().end.z) * 100 );
|
|
std::vector<int> floorsAtHeight_cm;
|
|
for (const Floorplan::Floor* f : map->floors) {
|
|
const int height_cm = std::round(f->atHeight*100);
|
|
floorsAtHeight_cm.push_back(height_cm);
|
|
}
|
|
const bool connected = std::find(floorsAtHeight_cm.begin(), floorsAtHeight_cm.end(), stairEnd_cm) != floorsAtHeight_cm.end();
|
|
return connected;
|
|
}
|
|
|
|
static inline float clamp01(const float val) {
|
|
if (val < 0) {return 0;}
|
|
if (val > 1) {return 1;}
|
|
return val;
|
|
}
|
|
|
|
|
|
|
|
|
|
MV2DElementStair::MV2DElementStair(Floorplan::IndoorMap* map, Floorplan::Floor* floor, Floorplan::Stair* stair)
|
|
: map(map), floor(floor), stair(stair) {
|
|
;
|
|
}
|
|
|
|
|
|
BBox2 MV2DElementStair::getBoundingBox() const {
|
|
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 MV2DElementStair::getMinDistanceXY(const Point2 p) const {
|
|
|
|
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 MV2DElementStair::getSelPart() const {return getSelectedNode() < 0 ? getSelectedNode() : getSelectedNode() / 2;}
|
|
int MV2DElementStair::getSelNode() const {return getSelectedNode() < 0 ? getSelectedNode() : getSelectedNode() % 2;}
|
|
|
|
void MV2DElementStair::paint(Painter& p) {
|
|
|
|
Floorplan::StairFreeform* stair = dynamic_cast<Floorplan::StairFreeform*>(this->stair);
|
|
|
|
// 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);
|
|
|
|
// skip drawing?
|
|
BBox2 bbox;
|
|
for (const Floorplan::Quad3& q : quads) {
|
|
bbox.add(q.p1.xy());
|
|
bbox.add(q.p2.xy());
|
|
bbox.add(q.p3.xy());
|
|
bbox.add(q.p4.xy());
|
|
}
|
|
if (!p.isVisible(bbox)) {
|
|
return;
|
|
}
|
|
|
|
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*128, p1*128, p1*255, 128));
|
|
gradient.setColorAt(1, QColor(p2*128, p2*128, 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()) {
|
|
if (!stairEndConnected(map, floor, stair)) {
|
|
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()); // DEPRECATED
|
|
|
|
//p.setPenBrush(Qt::black, (cnt == getSelPart() && getSelNode() == 1) ? CFG::SEL_COLOR : Qt::NoBrush); // part end
|
|
//p.drawRect(part.end.xy()); // DEPRECATED
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool MV2DElementStair::keyPressEvent(MapView2D* v, QKeyEvent *e) {
|
|
(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;
|
|
|
|
}
|
|
|
|
void MV2DElementStair::onFocus() {
|
|
// TODO?
|
|
}
|
|
|
|
void MV2DElementStair::onUnfocus() {
|
|
sel = false;
|
|
}
|
|
|
|
std::vector<MoveableNode> MV2DElementStair::getMoveableNodes() const {
|
|
|
|
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 MV2DElementStair::onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) {
|
|
|
|
(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 MV2DElementStair::onNodeMoved(MapView2D* v, const int userIdx, const Point2 newPos) {
|
|
(void) userIdx;
|
|
(void) newPos;
|
|
emit v->onElementChange(this);
|
|
}
|
|
|
|
void MV2DElementStair::onNodeSelect(MapView2D* v, const int userIdx) {
|
|
HasMoveableNodes::onNodeSelect(v, userIdx);
|
|
emit v->onElementChange(this);
|
|
}
|
|
|