262 lines
7.7 KiB
C++
262 lines
7.7 KiB
C++
/*
|
||
* © Copyright 2014 – Urheberrechtshinweis
|
||
* Alle Rechte vorbehalten / All Rights Reserved
|
||
*
|
||
* Programmcode ist urheberrechtlich geschuetzt.
|
||
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
|
||
* Keine Verwendung ohne explizite Genehmigung.
|
||
* (vgl. § 106 ff UrhG / § 97 UrhG)
|
||
*/
|
||
|
||
#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);
|
||
}
|
||
|