/* * © 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 #include 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::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 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(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 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 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 MV2DElementFloorObstacleWall::getMoveableNodes() const { std::vector 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 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) { const int idx = getSelectedNode() - 1000; fo->doors.erase(fo->doors.begin()+idx); return true; } return false; }