361 lines
9.8 KiB
C++
361 lines
9.8 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 "MV2DElementFloorObstacleWall.h"
|
||
|
||
#include "MV2DElement.h"
|
||
#include "HasMoveableNodes.h"
|
||
|
||
#include "MapViewElementHelper.h"
|
||
#include <Indoor/floorplan/v2/Floorplan.h>
|
||
#include <stdio.h>
|
||
|
||
float getPosOnLine(const Floorplan::FloorObstacleWall* wall, const Point2 pos) {
|
||
|
||
Point2 perp = (wall->from - wall->to).perpendicular() * 100;
|
||
Line2 l1(pos-perp, pos+perp);
|
||
Line2 l2(wall->from, wall->to);
|
||
|
||
Point2 p;
|
||
float u = 0;
|
||
bool isects = intersects(l2, l1, true, p, &u);
|
||
if (u < 0.01) {u = 0.01;}
|
||
if (u > 0.99) {u = 0.99;}
|
||
|
||
return (isects) ? (u) : (NAN);
|
||
|
||
}
|
||
|
||
|
||
void drawDoorSwing(const Floorplan::FloorObstacleWall* wall, const Floorplan::FloorObstacleWallDoor* door, Painter& p, QPen& pen) {
|
||
|
||
const float len = door->width;
|
||
//const Point2 dir = line->to - line->from;
|
||
Point2 start = door->getStart(wall);
|
||
Point2 end = door->getEnd(wall);
|
||
|
||
// opening indicator
|
||
float open = (+M_PI * 0.5);
|
||
if (door->inOut) {open = -open;}
|
||
if (door->leftRight) {open = -open;}
|
||
|
||
const float angle1 = std::atan2(end.y - start.y, end.x - start.x);
|
||
const float angle2 = angle1 + open;
|
||
|
||
if (std::isnan(angle1)) {return;}
|
||
if (std::isnan(angle2)) {return;}
|
||
|
||
const Point2 pOpen = Point2( std::cos(angle2) * len, std::sin(angle2) * len ) + start;
|
||
|
||
pen.setWidth(2); p.setPen(pen);
|
||
p.drawLine(start, end);
|
||
|
||
pen.setWidth(1); p.setPen(pen);
|
||
p.drawLine(start, pOpen);
|
||
p.drawArc(start, door->width, angle1, open);
|
||
|
||
}
|
||
|
||
void drawDoorRevolve(const Floorplan::FloorObstacleWallDoor* door, Painter& p, QPen& pen) {
|
||
|
||
// const float angle_rad = std::atan2(fo->to.y-fo->from.y, fo->to.x-fo->from.x);
|
||
|
||
// // arcs
|
||
// const Point2 cen = (fo->from + fo->to) / 2;
|
||
// const float rad = (fo->to - fo->from).length() / 2;
|
||
// p.drawArc(cen, rad, (40-90)/180.0f*M_PI+angle_rad, 100/180.0f*M_PI);
|
||
// p.drawArc(cen, rad, (180+40-90)/180.0f*M_PI+angle_rad, 100/180.0f*M_PI);
|
||
|
||
// const int funAngle = (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() / 50) % 360;
|
||
|
||
// // inner spinner
|
||
// int numDoors = 3;
|
||
// for (int i = 0; i < numDoors; ++i) {
|
||
// const int deg = 360 * i / numDoors + angle_rad*180.0f/M_PI + funAngle;
|
||
// const float sx = std::cos(deg / 180.0f * M_PI);
|
||
// const float sy = std::sin(deg / 180.0f * M_PI);
|
||
// const Point2 dst = cen + Point2(sx,sy) * rad;
|
||
// p.drawLine(cen, dst);
|
||
// }
|
||
|
||
}
|
||
|
||
void drawDoor(const Floorplan::FloorObstacleWall* line, const Floorplan::FloorObstacleWallDoor* door, Painter& p) {
|
||
|
||
QPen pen;
|
||
pen.setColor(QColor(0.5,0.5,0.5));
|
||
pen.setStyle(Qt::PenStyle::DotLine);
|
||
p.setPenBrush(pen, Qt::NoBrush);
|
||
|
||
if (Floorplan::DoorType::SWING == door->type) {
|
||
drawDoorSwing(line, door, p, pen);
|
||
} else if (Floorplan::DoorType::REVOLVING == door->type) {
|
||
drawDoorRevolve(door, p, pen);
|
||
}
|
||
|
||
}
|
||
|
||
void drawWindow(const Floorplan::FloorObstacleWall* wall, const Floorplan::FloorObstacleWallWindow* win, Painter& p) {
|
||
|
||
const Point2 ps = win->getStart(wall);
|
||
const Point2 pe = win->getEnd(wall);
|
||
const Point2 perp = (pe-ps).perpendicular().normalized();
|
||
|
||
const float s = 0.06;
|
||
const Point2 p1 = ps - perp*s;
|
||
const Point2 p2 = pe - perp*s;
|
||
const Point2 p3 = pe + perp*s;
|
||
const Point2 p4 = ps + perp*s;
|
||
|
||
std::vector<Point2> pts = {p1, p2, p3, p4, p1};
|
||
p.setPenBrush(QColor(0,0,0), QColor(255,255,255));
|
||
p.drawPolygon(pts);
|
||
|
||
}
|
||
|
||
bool isConnected(const Point2 p, const Floorplan::Floor* f, const Floorplan::FloorObstacleWall* fo) {
|
||
const float delta = 0.001;
|
||
for (const Floorplan::FloorObstacle* fo1 : f->obstacles) {
|
||
if (fo1 == fo) {continue;}
|
||
const Floorplan::FloorObstacleWall* wall = dynamic_cast<const Floorplan::FloorObstacleWall*>(fo1);
|
||
if (wall) {
|
||
if (wall->from.eq(p, delta)) {return true;}
|
||
if (wall->to.eq(p, delta)) {return true;}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
|
||
MV2DElementFloorObstacleWall::MV2DElementFloorObstacleWall(Floorplan::Floor* f, Floorplan::FloorObstacleWall* fo) : f(f), fo(fo) {
|
||
;
|
||
}
|
||
|
||
BBox2 MV2DElementFloorObstacleWall::getBoundingBox() const {
|
||
BBox2 bbox;
|
||
bbox.add(fo->from);
|
||
bbox.add(fo->to);
|
||
return bbox;
|
||
}
|
||
|
||
ClickDist MV2DElementFloorObstacleWall::getMinDistanceXY(const Point2 p) const {
|
||
return MapElementHelper::getLineDistanceXY(fo->from, fo->to, p);
|
||
}
|
||
|
||
|
||
|
||
void MV2DElementFloorObstacleWall::mousePressed(MapView2D* v, const Point2 p) {
|
||
(void) v;
|
||
this->mouseNearLine = getPosOnLine(fo, p);
|
||
}
|
||
|
||
|
||
void MV2DElementFloorObstacleWall::paint(Painter& p) {
|
||
|
||
|
||
// convert wall's thickness from meter to pixels
|
||
const float thickness_px = p.s.ms(fo->thickness_m);
|
||
|
||
// see notes within MapElementHelper!
|
||
// lines only get thicker, but not longer!
|
||
p.setPenBrush(MapElementHelper::getPen(fo->material, fo->type, hasFocus(), thickness_px), Qt::NoBrush);
|
||
|
||
// draw the wall itself
|
||
|
||
// sort all doors by position on the line
|
||
// std::vector<Floorplan::FloorObstacleWallDoor*> doors = fo->doors;
|
||
// auto comp = [] (const Floorplan::FloorObstacleWallDoor* d1, const Floorplan::FloorObstacleWallDoor* d2) {
|
||
// return d1->atLinePos < d2->atLinePos;
|
||
// };
|
||
// std::sort(doors.begin(), doors.end(), comp);
|
||
|
||
std::vector<Point2> pts;
|
||
pts.push_back(fo->from);
|
||
for (const Floorplan::FloorObstacleWallDoor* door : fo->doors) {
|
||
pts.push_back(door->getStart(fo));
|
||
pts.push_back(door->getEnd(fo));
|
||
}
|
||
for (const Floorplan::FloorObstacleWallWindow* win : fo->windows) {
|
||
pts.push_back(win->getStart(fo));
|
||
pts.push_back(win->getEnd(fo));
|
||
}
|
||
pts.push_back(fo->to);
|
||
|
||
auto comp = [&] (const Point2 p1, const Point2 p2) {
|
||
return fo->from.getDistance(p1) < fo->from.getDistance(p2);
|
||
};
|
||
std::sort(pts.begin(), pts.end(), comp);
|
||
|
||
|
||
for (size_t i = 0; i < pts.size(); i+=2) {
|
||
p.drawLine(pts[i], pts[i+1]);
|
||
}
|
||
//p.drawLine(fo->from, fo->to);
|
||
|
||
const bool c1 = isConnected(fo->from, f, fo);
|
||
const bool c2 = isConnected(fo->to, f, fo);
|
||
|
||
// wall conencted to other walls?
|
||
if (c1 || c2) {
|
||
|
||
// QPen change is costly!
|
||
QPen pp = p.getPen();
|
||
pp.setCapStyle(Qt::RoundCap);
|
||
p.setPen(pp);
|
||
|
||
// indicate connection with other wall
|
||
if (c1) {p.drawDot(fo->from);}
|
||
if (c2) {p.drawDot(fo->to);}
|
||
|
||
}
|
||
|
||
for (const Floorplan::FloorObstacleWallDoor* door : fo->doors) {
|
||
drawDoor(fo, door, p);
|
||
}
|
||
|
||
for (const Floorplan::FloorObstacleWallWindow* window : fo->windows) {
|
||
drawWindow(fo, window, p);
|
||
}
|
||
|
||
// length info
|
||
if (hasFocus()) {
|
||
|
||
// obstacle length
|
||
p.setPenBrush(Qt::black, Qt::NoBrush);
|
||
//p.drawLength(fo->from, fo->to, fo->from.getDistance(fo->to), thickness_px/2);
|
||
|
||
for (size_t i = 0; i < pts.size(); i+=1) {
|
||
const Point2 p1 = pts[i];
|
||
const Point2 p2 = pts[i+1];
|
||
const float len = p2.getDistance(p1);
|
||
p.drawLength(p1, p2, len, thickness_px/2);
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
void MV2DElementFloorObstacleWall::onFocus() {
|
||
;
|
||
}
|
||
|
||
void MV2DElementFloorObstacleWall::onUnfocus() {
|
||
selectedUserIdx = -1; // clear selection
|
||
}
|
||
|
||
std::vector<MoveableNode> MV2DElementFloorObstacleWall::getMoveableNodes() const {
|
||
|
||
std::vector<MoveableNode> nodes;
|
||
nodes.push_back(MoveableNode(0, fo->from));
|
||
nodes.push_back(MoveableNode(1, fo->to));
|
||
|
||
int cnt = 0;
|
||
for (const Floorplan::FloorObstacleWallDoor* door : fo->doors) {
|
||
const Point2 pos = fo->from + (fo->to - fo->from) * door->atLinePos;
|
||
nodes.push_back(MoveableNode(cnt+1000, pos));
|
||
++cnt;
|
||
}
|
||
|
||
cnt = 0;
|
||
for (const Floorplan::FloorObstacleWallWindow* win : fo->windows) {
|
||
const Point2 pos = fo->from + (fo->to - fo->from) * win->atLinePos;
|
||
nodes.push_back(MoveableNode(cnt+2000, pos));
|
||
++cnt;
|
||
}
|
||
|
||
return nodes;
|
||
|
||
}
|
||
|
||
#include <iostream>
|
||
|
||
|
||
|
||
void MV2DElementFloorObstacleWall::onNodeMove(MapView2D* v, const int userIdx, const Point2 newPos) {
|
||
|
||
(void) v;
|
||
if (userIdx == 0) {fo->from.x = newPos.x; fo->from.y = newPos.y;}
|
||
if (userIdx == 1) {fo->to.x = newPos.x; fo->to.y = newPos.y;}
|
||
|
||
// Point2 perp = (fo->from - fo->to).perpendicular() * 100;
|
||
// Line2 l1(newPos-perp, newPos+perp);
|
||
// Line2 l2(fo->from, fo->to);
|
||
|
||
// Point2 p;
|
||
// float u = 0;
|
||
// bool isects = intersects(l2, l1, true, p, &u);
|
||
// if (u < 0.01) {u = 0.01;}
|
||
// if (u > 0.99) {u = 0.99;}
|
||
|
||
const float u = getPosOnLine(fo, newPos);
|
||
|
||
if (userIdx >= 1000 && userIdx < 2000) {
|
||
|
||
Floorplan::FloorObstacleWallDoor* door = fo->doors[userIdx-1000];
|
||
|
||
if (!std::isnan(u)) {
|
||
door->atLinePos = u;
|
||
}
|
||
|
||
} else if (userIdx >= 2000 && userIdx < 3000) {
|
||
|
||
Floorplan::FloorObstacleWallWindow* win = fo->windows[userIdx-2000];
|
||
|
||
if (!std::isnan(u)) {
|
||
win->atLinePos = u;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
void MV2DElementFloorObstacleWall::onNodeMoved(MapView2D* v, const int userIdx, const Point2 newPos) {
|
||
(void) userIdx;
|
||
(void) newPos;
|
||
emit v->onElementChange(this);
|
||
}
|
||
|
||
|
||
bool MV2DElementFloorObstacleWall::keyPressEvent(MapView2D* v, QKeyEvent* e) {
|
||
|
||
if (e->key() == Qt::Key_D) {
|
||
const float doorWidth = 0.9;
|
||
const float doorHeight = 2.1;
|
||
const float doorAt = std::isnan(mouseNearLine) ? (0.5) : (mouseNearLine);
|
||
Floorplan::FloorObstacleWallDoor* door = new Floorplan::FloorObstacleWallDoor(Floorplan::DoorType::SWING, Floorplan::Material::WOOD, doorAt, doorWidth, doorHeight);
|
||
fo->doors.push_back(door);
|
||
return true;
|
||
} else if (e->key() == Qt::Key_W) {
|
||
Floorplan::FloorObstacleWallWindow* win = new Floorplan::FloorObstacleWallWindow(Floorplan::WindowType::UNKNOWN, Floorplan::Material::WOOD, 0.5, 1, 1.0, 1.0);
|
||
fo->windows.push_back(win);
|
||
return true;
|
||
} else if (e->key() == Qt::Key_Delete && getSelectedNode() >= 1000) {
|
||
|
||
int userIdx = getSelectedNode();
|
||
|
||
if (userIdx >= 1000 && userIdx < 2000) {
|
||
const int idx = getSelectedNode() - 1000;
|
||
fo->doors.erase(fo->doors.begin()+idx);
|
||
return true;
|
||
} else if (userIdx >= 2000 && userIdx < 3000) {
|
||
const int idx = getSelectedNode() - 2000;
|
||
fo->windows.erase(fo->windows.begin()+idx);
|
||
return true;
|
||
}
|
||
|
||
}
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
|