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/MV2DElementFloorObstacleWall.cpp
2019-12-13 19:10:14 +01:00

361 lines
9.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* © 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;
}