From 3868e42937f59a7355853e0f59cd155ed712b213 Mon Sep 17 00:00:00 2001 From: kazu Date: Mon, 20 Mar 2017 12:10:34 +0100 Subject: [PATCH 1/2] started adding floorplan LINT new helper methods --- floorplan/v2/FloorplanLINT.h | 79 ++++++++++++++++++++++++++++++++++ floorplan/v2/FloorplanReader.h | 6 +-- geo/BBox2.h | 5 ++- geo/Point2.h | 4 ++ 4 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 floorplan/v2/FloorplanLINT.h diff --git a/floorplan/v2/FloorplanLINT.h b/floorplan/v2/FloorplanLINT.h new file mode 100644 index 0000000..a2a840d --- /dev/null +++ b/floorplan/v2/FloorplanLINT.h @@ -0,0 +1,79 @@ +#ifndef FLOORPLANLINT_H +#define FLOORPLANLINT_H + +#include "Floorplan.h" +#include "../../geo/BBox2.h" + +namespace Floorplan { + + class LINT { + + public: + + static void check(IndoorMap* map) { + + for (const Floor* floor : map->floors) { + + // outline present? + if (floor->outline.empty()) {throw Exception("floor " + floor->name + " has no outline");} + + // check outline + for (FloorOutlinePolygon* poly : floor->outline) { + checkOutline(floor, poly); + } + + // check obstacles + for (FloorObstacle* obs : floor->obstacles) { + checkObstacle(floor, obs); + } + + } + + } + + private: + + /** check a floor's outline */ + static void checkOutline(const Floor* floor, const FloorOutlinePolygon* poly) { + + // number of points valid? + if (poly->poly.points.size() < 3) {throw Exception("floor '" + floor->name + "' outline '" + poly->name + "' needs at least 3 edges");} + if (poly->poly.points.size() > 1024) {throw Exception("floor '" + floor->name + "' outline '" + poly->name + "' has too many edges");} + + // outline size [bbox] valid? + BBox2 outline; + for (const Point2 pt : poly->poly.points) { outline.add(pt); } + const Point2 size = outline.getSize(); + if (size.x < 1.0 || size.y < 1.0) {throw Exception("floor '" + floor->name + "' outline '" + poly->name + "' seems too small");} + + } + + /** check walls, doors, ... */ + static void checkObstacle(const Floor* floor, const FloorObstacle* fo) { + + // line? -> check + const FloorObstacleLine* line = dynamic_cast(fo); + if (line) { + const float len_m = line->from.getDistance(line->to); + if (len_m < 0.15) { + throw Exception("floor '" + floor->name + "' line-obstacle is too short: " + std::to_string(len_m) + " meter from " + line->from.asString() + " to " + line->to.asString()); + } + } + + // door? -> check + const FloorObstacleDoor* door = dynamic_cast(fo); + if (door) { + const float len_m = door->from.getDistance(door->to); + if (len_m < 0.40) { + throw Exception("floor '" + floor->name + "' door is too narrow: " + std::to_string(len_m) + " meter from " + door->from.asString() + " to " + door->to.asString()); + } + } + + } + + }; + +} + + +#endif // FLOORPLANLINT_H diff --git a/floorplan/v2/FloorplanReader.h b/floorplan/v2/FloorplanReader.h index 5d19af6..4959145 100644 --- a/floorplan/v2/FloorplanReader.h +++ b/floorplan/v2/FloorplanReader.h @@ -4,6 +4,7 @@ #include #include "Floorplan.h" +#include "FloorplanLINT.h" #include "../../misc/Debug.h" #include "../../Assertions.h" @@ -40,6 +41,7 @@ namespace Floorplan { ); } IndoorMap* map = parse(doc); + Floorplan::LINT::check(map); return map; } @@ -57,6 +59,7 @@ namespace Floorplan { ); } IndoorMap* map = parse(doc); + Floorplan::LINT::check(map); return map; } @@ -431,9 +434,6 @@ namespace Floorplan { poly.points.push_back(p2); } } - if (poly.points.size() < 4 || poly.points.size() > 1024) { - throw Exception("detected invalid outline-polygon during XML parsing"); - } return poly; } diff --git a/geo/BBox2.h b/geo/BBox2.h index c4833c3..1503f01 100644 --- a/geo/BBox2.h +++ b/geo/BBox2.h @@ -37,7 +37,7 @@ public: } /** returns true if the bbox is not yet configured */ - const bool isInvalid() const { + bool isInvalid() const { return p1.x == MAX || p1.y == MAX || p2.x == MIN || p2.y == MIN; } @@ -47,6 +47,9 @@ public: /** get the bbox's maximum */ const Point2& getMax() const {return p2;} + /** get the bbox's size [max-min] */ + const Point2 getSize() const {return p2-p1;} + /** get the bbox's center point */ Point2 getCenter() const { return (p1+p2) / 2; } diff --git a/geo/Point2.h b/geo/Point2.h index 3b19fd7..0d94f12 100644 --- a/geo/Point2.h +++ b/geo/Point2.h @@ -61,6 +61,10 @@ struct Point2 { return std::sqrt(dx*dx + dy*dy); } + std::string asString() const { + return "(" + std::to_string(x) + "," + std::to_string(y) + ")"; + } + }; inline void swap(Point2& p1, Point2& p2) { From 4ac248d08ecf9452d18e2f2f6e6f6dc524f310d4 Mon Sep 17 00:00:00 2001 From: FrankE Date: Mon, 20 Mar 2017 14:44:17 +0100 Subject: [PATCH 2/2] adjusted floorplan-lint for GUI integration --- floorplan/v2/FloorplanLINT.h | 155 ++++++++++++++++++++++++++++++--- floorplan/v2/FloorplanReader.h | 2 - 2 files changed, 142 insertions(+), 15 deletions(-) diff --git a/floorplan/v2/FloorplanLINT.h b/floorplan/v2/FloorplanLINT.h index a2a840d..ebb5db9 100644 --- a/floorplan/v2/FloorplanLINT.h +++ b/floorplan/v2/FloorplanLINT.h @@ -4,59 +4,132 @@ #include "Floorplan.h" #include "../../geo/BBox2.h" +#include +#include + namespace Floorplan { class LINT { public: - static void check(IndoorMap* map) { + /** possible issue types */ + enum class Type { + WARN, + ERROR, + }; + + /** type -> string */ + static std::string getTypeStr(const Type t) { + switch(t) { + case Type::WARN: return "WARNING"; + case Type::ERROR: return "ERROR"; + default: throw Exception("code error. invalid type. todo!"); + } + } + + /** a detected issue */ + struct Issue { + + Type type; + const Floor* floor; + std::string desc; + + Issue(const Type type, const Floor* floor, const std::string& desc) : type(type), floor(floor), desc(desc) {;} + + /** issue to string */ + std::string asString() const { + return getTypeStr(type) + ": " + "floor '" + floor->name + "': " + desc; + } + + }; + + using Issues = std::vector; + + + + public: + + /** throw in case of errors within the map */ + static void assertOK(IndoorMap* map) { + + const Issues issues = check(map); + int err = 0; + for (const Issue& i : issues) { + std::cout << i.asString() << std::endl; + if (i.type == Type::ERROR) {++err;} + } + if (err > 0) { + throw Exception("detected floorplan errors"); + } + + } + + /** get all errors within the map */ + static Issues check(IndoorMap* map) { + + Issues res; for (const Floor* floor : map->floors) { // outline present? - if (floor->outline.empty()) {throw Exception("floor " + floor->name + " has no outline");} + if (floor->outline.empty()) { + res.push_back(Issue(Type::ERROR, floor, "has no outline")); + } // check outline - for (FloorOutlinePolygon* poly : floor->outline) { - checkOutline(floor, poly); + for (const FloorOutlinePolygon* poly : floor->outline) { + checkOutline(res, floor, poly); } // check obstacles - for (FloorObstacle* obs : floor->obstacles) { - checkObstacle(floor, obs); + for (const FloorObstacle* obs : floor->obstacles) { + checkObstacle(res, floor, obs); + } + + // check fingerprints + for (const FingerprintLocation* fpl : floor->fpLocations) { + checkFingerprintLoc(res, floor, fpl); + } + + // check stairs + for (const Stair* s : floor->stairs) { + checkStair(res, floor, s); } } + // done + return res; + } private: /** check a floor's outline */ - static void checkOutline(const Floor* floor, const FloorOutlinePolygon* poly) { + static void checkOutline(Issues& res, const Floor* floor, const FloorOutlinePolygon* poly) { // number of points valid? - if (poly->poly.points.size() < 3) {throw Exception("floor '" + floor->name + "' outline '" + poly->name + "' needs at least 3 edges");} - if (poly->poly.points.size() > 1024) {throw Exception("floor '" + floor->name + "' outline '" + poly->name + "' has too many edges");} + if (poly->poly.points.size() < 3) {res.push_back(Issue(Type::ERROR, floor, "' outline '" + poly->name + "' needs at least 3 edges"));} + if (poly->poly.points.size() > 1024) {res.push_back(Issue(Type::ERROR, floor, "' outline '" + poly->name + "' has too many edges"));} // outline size [bbox] valid? BBox2 outline; for (const Point2 pt : poly->poly.points) { outline.add(pt); } const Point2 size = outline.getSize(); - if (size.x < 1.0 || size.y < 1.0) {throw Exception("floor '" + floor->name + "' outline '" + poly->name + "' seems too small");} + if (size.x < 1.0 || size.y < 1.0) {res.push_back(Issue(Type::ERROR, floor, "' outline '" + poly->name + "' seems too small"));} } /** check walls, doors, ... */ - static void checkObstacle(const Floor* floor, const FloorObstacle* fo) { + static void checkObstacle(Issues& res, const Floor* floor, const FloorObstacle* fo) { // line? -> check const FloorObstacleLine* line = dynamic_cast(fo); if (line) { const float len_m = line->from.getDistance(line->to); if (len_m < 0.15) { - throw Exception("floor '" + floor->name + "' line-obstacle is too short: " + std::to_string(len_m) + " meter from " + line->from.asString() + " to " + line->to.asString()); + res.push_back(Issue(Type::WARN, floor, "' line-obstacle is too short: " + std::to_string(len_m) + " meter from " + line->from.asString() + " to " + line->to.asString())); } } @@ -65,10 +138,66 @@ namespace Floorplan { if (door) { const float len_m = door->from.getDistance(door->to); if (len_m < 0.40) { - throw Exception("floor '" + floor->name + "' door is too narrow: " + std::to_string(len_m) + " meter from " + door->from.asString() + " to " + door->to.asString()); + res.push_back(Issue(Type::ERROR, floor, "' door is too narrow: " + std::to_string(len_m) + " meter from " + door->from.asString() + " to " + door->to.asString())); } } + // pillar? -> check + const FloorObstacleCircle* circle = dynamic_cast(fo); + if (circle) { + const float len_m = circle->radius; + if (len_m < 0.20) { + res.push_back(Issue(Type::ERROR, floor, "' pillar is too narrow: " + std::to_string(len_m) + " meter at " + circle->center.asString())); + } + } + + } + + static void checkFingerprintLoc(Issues& res, const Floor* floor, const FingerprintLocation* fpl) { + + const std::string note = "does it belong to a stair? if so: fine! Note: fingerprints are currently measured using smartphones and smartphone are held by the pedestian. thus: fingerprints are ~1.3 meter above ground"; + if (fpl->heightAboveFloor < 0.8) { + res.push_back(Issue(Type::ERROR, floor, std::string() + "fingerprint " + fpl->name + " @ " + fpl->getPosition(*floor).asString() + " is too near to the floor. " + note)); + } else if (fpl->heightAboveFloor > 2.0) { + res.push_back(Issue(Type::WARN, floor, std::string() + "fingerprint " + fpl->name + " @ " + fpl->getPosition(*floor).asString() + " is too high above the floor. " + note)); + } + + } + + static void checkStair(Issues& res, const Floor* floor, const Stair* stair) { + + const std::vector parts = stair->getParts(); + const std::vector quads = Floorplan::getQuads(parts, floor); + + const Floorplan::Quad3& quadS = quads.front(); + const Floorplan::Quad3& quadE = quads.back(); + + // disconnected start? + if (quadS.p1.z != floor->getStartingZ()) { + res.push_back(Issue(Type::ERROR, floor, "stair is not connected to the starting floor's ground! [open stair start]")); + } + + // disconnected end? + if (quadE.p3.z != floor->getEndingZ()) { + res.push_back(Issue(Type::ERROR, floor, "stair is not connected to the ending floor's ground! [open stair end]")); + } + + + for (int i = 0; i < (int) parts.size(); ++i) { + + const Floorplan::Quad3& quad = quads[i]; + + // disconnected within? + if (i > 0) { + if (quads[i-1].p4.z != quads[i-0].p1.z) { + res.push_back(Issue(Type::ERROR, floor, "stair is disconnected within!")); + } + } + + } + + + } }; diff --git a/floorplan/v2/FloorplanReader.h b/floorplan/v2/FloorplanReader.h index 4959145..b34d9ec 100644 --- a/floorplan/v2/FloorplanReader.h +++ b/floorplan/v2/FloorplanReader.h @@ -41,7 +41,6 @@ namespace Floorplan { ); } IndoorMap* map = parse(doc); - Floorplan::LINT::check(map); return map; } @@ -59,7 +58,6 @@ namespace Floorplan { ); } IndoorMap* map = parse(doc); - Floorplan::LINT::check(map); return map; }