worked on floorplan (v2)
worked on grid-generation (v2) new helper methods for geometry new test cases
This commit is contained in:
49
grid/Grid.h
49
grid/Grid.h
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../Exception.h"
|
||||
#include "GridPoint.h"
|
||||
@@ -53,6 +54,12 @@ public:
|
||||
/** no-copy */
|
||||
Grid(const Grid& o) = delete;
|
||||
|
||||
/** reset the grid (empty) */
|
||||
void reset() {
|
||||
nodes.clear();
|
||||
hashes.clear();
|
||||
}
|
||||
|
||||
/** no-assign */
|
||||
void operator = (const Grid& o) = delete;
|
||||
|
||||
@@ -77,14 +84,16 @@ public:
|
||||
}
|
||||
|
||||
/** add the given (not necessarly aligned) element to the grid */
|
||||
int addUnaligned(const T& elem) {
|
||||
const UID uid = getUID(elem); // get the UID for this new element
|
||||
Assert::isTrue(hashes.find(uid) == hashes.end(), "node's UID is already taken!"); // avoid potential errors
|
||||
const int idx = nodes.size(); // next free index
|
||||
nodes.push_back(elem); // add it to the grid
|
||||
nodes.back()._idx = idx; // let the node know his own index
|
||||
hashes[uid] = idx; // add an UID->index lookup
|
||||
return idx; // done
|
||||
int addUnaligned(const T& elem, const bool check=true) {
|
||||
const UID uid = getUID(elem); // get the UID for this new element
|
||||
if (check) {
|
||||
Assert::isTrue(hashes.find(uid) == hashes.end(), "node's UID is already taken!"); // avoid potential errors
|
||||
}
|
||||
const int idx = nodes.size(); // next free index
|
||||
nodes.push_back(elem); // add it to the grid
|
||||
nodes.back()._idx = idx; // let the node know his own index
|
||||
hashes[uid] = idx; // add an UID->index lookup
|
||||
return idx; // done
|
||||
}
|
||||
|
||||
/** connect (uni-dir) i1 -> i2 */
|
||||
@@ -94,9 +103,11 @@ public:
|
||||
|
||||
/** connect (uni-dir) i1 -> i2 */
|
||||
void connectUniDir(T& n1, const T& n2) {
|
||||
Assert::isFalse(n1.hasNeighbor(n2._idx), "this neighbor is already connected"); // already connected?
|
||||
Assert::notEqual(n1.getIdx(), n2.getIdx(), "can not connect node with itself");
|
||||
n1._neighbors[n1._numNeighbors] = n2._idx;
|
||||
++n1._numNeighbors;
|
||||
Assert::isBetween(n1._numNeighbors, 0, 10, "number of neighbors out of bounds!");
|
||||
Assert::isBetween(n1._numNeighbors, (uint8_t) 0, (uint8_t) 10, "number of neighbors out of bounds!");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,11 +176,25 @@ public:
|
||||
}
|
||||
|
||||
/** get the center-node the given Point belongs to. or nullptr if not present */
|
||||
const T* getNodePtrFor(const GridPoint& p) {
|
||||
const T* getNodePtrFor(const GridPoint& p) const {
|
||||
auto it = hashes.find(getUID(p));
|
||||
return (it == hashes.end()) ? (nullptr) : (&nodes[it->second]);
|
||||
}
|
||||
|
||||
/** get the node nearest to the given positon */
|
||||
const T& getNearestNode(const GridPoint& p) const {
|
||||
auto comp = [p] (const T& a, const T& b) { return a.getDistanceInMeter(p) < b.getDistanceInMeter(p); };
|
||||
auto it = std::min_element(nodes.begin(), nodes.end(), comp);
|
||||
return nodes[it->getIdx()];
|
||||
}
|
||||
|
||||
/** get the node nearest to the given positon, examining only the nodes given by the provided index-array */
|
||||
const T& getNearestNode(const GridPoint& p, const std::vector<int>& indices) const {
|
||||
auto comp = [&] (const int a, const int b) { return nodes[a].getDistanceInMeter(p) < nodes[b].getDistanceInMeter(p); };
|
||||
auto it = std::min_element(indices.begin(), indices.end(), comp);
|
||||
return nodes[it->getIdx()];
|
||||
}
|
||||
|
||||
/** get the BBox for the given node */
|
||||
GridNodeBBox getBBox(const int idx) const {
|
||||
return getBBox(nodes[idx]);
|
||||
@@ -188,7 +213,7 @@ public:
|
||||
UID getUID(const GridPoint& p) const {
|
||||
const uint64_t x = std::round(p.x_cm / (float)gridSize_cm);
|
||||
const uint64_t y = std::round(p.y_cm / (float)gridSize_cm);
|
||||
const uint64_t z = std::round(p.z_cm / (float)gridSize_cm);
|
||||
const uint64_t z = std::round(p.z_cm / (float)gridSize_cm * 5); // z is usually much lower and not always aligned -> allow more room for hashes
|
||||
return (z << 40) | (y << 20) | (x << 0);
|
||||
}
|
||||
|
||||
@@ -446,7 +471,7 @@ private:
|
||||
void assertAligned(const T& elem) {
|
||||
if (((int)elem.x_cm % gridSize_cm) != 0) {throw Exception("element's x is not aligned!");}
|
||||
if (((int)elem.y_cm % gridSize_cm) != 0) {throw Exception("element's y is not aligned!");}
|
||||
if (((int)elem.z_cm % gridSize_cm) != 0) {throw Exception("element's z is not aligned!");}
|
||||
//if (((int)elem.z_cm % gridSize_cm) != 0) {throw Exception("element's z is not aligned!");}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -21,15 +21,26 @@ private:
|
||||
/** grant full access to the grid */
|
||||
template<typename> friend class Grid;
|
||||
|
||||
/** INTERNAL: array-index */
|
||||
/** INTERNAL: node's index array-index */
|
||||
int _idx;
|
||||
|
||||
/** semantic annotation for this node */
|
||||
uint8_t _type;
|
||||
|
||||
/** INTERNAL: number of neighbors */
|
||||
int _numNeighbors;
|
||||
uint8_t _numNeighbors;
|
||||
|
||||
/** INTERNAL: store neighbors (via index) */
|
||||
int _neighbors[10];
|
||||
|
||||
|
||||
public:
|
||||
|
||||
static const uint8_t TYPE_FLOOR = 0;
|
||||
static const uint8_t TYPE_STAIR = 1;
|
||||
static const uint8_t TYPE_ELEVATOR = 2;
|
||||
static const uint8_t TYPE_DOOR = 3;
|
||||
|
||||
public:
|
||||
|
||||
GridNode() : _idx(-1), _numNeighbors(0), _neighbors() {;}
|
||||
@@ -40,6 +51,23 @@ public:
|
||||
/** get the number of neighbors for this node */
|
||||
int getNumNeighbors() const {return _numNeighbors;}
|
||||
|
||||
/** reached neighbor limit? */
|
||||
bool fullyConnected() const {return _numNeighbors >= 10;}
|
||||
|
||||
/** is this node connected to the given index? */
|
||||
bool hasNeighbor(const int idx) const {
|
||||
for (int i = 0; i < _numNeighbors; ++i) {
|
||||
if (_neighbors[i] == idx) {return true;}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** get the node's semantic type */
|
||||
uint8_t getType() const {return _type;}
|
||||
|
||||
/** set the node's semantic type */
|
||||
void setType(const uint8_t type) {this->_type = type;}
|
||||
|
||||
// /** get the n-th neighbor for this node */
|
||||
// template <int gridSize_cm, typename T> inline T& getNeighbor(const int nth, const Grid<gridSize_cm, T>& grid) const {
|
||||
// return grid.getNeighbor(_idx, nth);
|
||||
|
||||
@@ -30,12 +30,22 @@ struct GridPoint {
|
||||
return x_cm == o.x_cm && y_cm == o.y_cm && z_cm == o.z_cm;
|
||||
}
|
||||
|
||||
/** not equal? */
|
||||
bool operator != (const GridPoint& o) const {
|
||||
return x_cm != o.x_cm || y_cm != o.y_cm || z_cm != o.z_cm;
|
||||
}
|
||||
|
||||
/** get the distance (in meter) betwen this and the given point */
|
||||
float getDistanceInMeter(const GridPoint& other) const {
|
||||
return getDistanceInCM(other) / 100.0f;
|
||||
}
|
||||
|
||||
/** get the distance (in centimeter) betwen this and the given point */
|
||||
float getDistanceInCM(const GridPoint& other) const {
|
||||
const int dx = x_cm - other.x_cm;
|
||||
const int dy = y_cm - other.y_cm;
|
||||
const int dz = z_cm - other.z_cm;
|
||||
return std::sqrt(dx*dx + dy*dy + dz*dz) / 100.0f;
|
||||
return std::sqrt(dx*dx + dy*dy + dz*dz);
|
||||
}
|
||||
|
||||
/** cast to Point3 */
|
||||
|
||||
507
grid/factory/v2/GridFactory.h
Executable file
507
grid/factory/v2/GridFactory.h
Executable file
@@ -0,0 +1,507 @@
|
||||
#ifndef GRIDFACTORY_V2_H
|
||||
#define GRIDFACTORY_V2_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "Stairs.h"
|
||||
|
||||
#include "../../../geo/Units.h"
|
||||
#include "../../GridNodeBBox.h"
|
||||
#include "../../Grid.h"
|
||||
|
||||
#include "../../../misc/Debug.h"
|
||||
#include <functional>
|
||||
|
||||
|
||||
/** listen for events during the build process */
|
||||
class GridFactoryListener {
|
||||
|
||||
public:
|
||||
|
||||
virtual void onGridBuildUpdateMajor(const std::string& what) = 0;
|
||||
virtual void onGridBuildUpdateMajor(const int cnt, const int cur) = 0;
|
||||
|
||||
virtual void onGridBuildUpdateMinor(const std::string& what) = 0;
|
||||
virtual void onGridBuildUpdateMinor(const int cnt, const int cur) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename T> class GridFactory {
|
||||
|
||||
/** logging name */
|
||||
static constexpr const char* name = "GridFac2";
|
||||
|
||||
private:
|
||||
|
||||
/** the grid to build into */
|
||||
Grid<T>& grid;
|
||||
|
||||
/** calculation helper */
|
||||
Helper<T> helper;
|
||||
|
||||
/** stair builder */
|
||||
Stairs<T> stairs;
|
||||
|
||||
|
||||
bool _buildStairs = true;
|
||||
bool _removeIsolated = true;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor with the grid to fill */
|
||||
GridFactory(Grid<T>& grid) : grid(grid), helper(grid), stairs(grid) {;}
|
||||
|
||||
|
||||
/** whether or not to build stairs */
|
||||
void setBuildStairs(const bool build) {this->_buildStairs = build;}
|
||||
|
||||
/** whether or not to remove isolated nodes */
|
||||
void setRemoveIsolated(const bool remove) {this->_removeIsolated = remove;}
|
||||
|
||||
|
||||
/** build using the given map */
|
||||
void build(const Floorplan::IndoorMap* map, GridFactoryListener* listener = nullptr) {
|
||||
|
||||
Log::add(name, "building grid from IndoorMap", true);
|
||||
|
||||
const int total = map->floors.size()*2 + 1;
|
||||
int cur = 0;
|
||||
|
||||
// build all the floors
|
||||
if (listener) {listener->onGridBuildUpdateMajor("adding floors");}
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
addFloor(f, listener);
|
||||
if (listener) {listener->onGridBuildUpdateMajor(total, ++cur);}
|
||||
}
|
||||
|
||||
// build all stairs
|
||||
if (listener) {listener->onGridBuildUpdateMajor("adding stairs");}
|
||||
if (_buildStairs) {
|
||||
for (Floorplan::Floor* f : map->floors) {
|
||||
buildStairs(f, listener);
|
||||
if (listener) {listener->onGridBuildUpdateMajor(total, ++cur);}
|
||||
}
|
||||
}
|
||||
|
||||
// remove isolated nodes
|
||||
if (_removeIsolated) {
|
||||
if (listener) {listener->onGridBuildUpdateMajor("removing isolated nodes");}
|
||||
removeIsolatedNodes();
|
||||
if (listener) {listener->onGridBuildUpdateMajor(total, ++cur);}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/** get the 2D-bbox for the given floor's outline */
|
||||
BBox2 getFloorOutlineBBox(const Floorplan::FloorOutline& outline) {
|
||||
BBox2 bb;
|
||||
for (Floorplan::FloorOutlinePolygon* poly : outline) {
|
||||
for (Point2 p : poly->poly.points) {
|
||||
bb.add(p * 100); // convert m to cm
|
||||
}
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
|
||||
bool isPartOfFloorOutline(const int x_cm, const int y_cm, const Floorplan::FloorOutline& outline) {
|
||||
|
||||
bool add = false;
|
||||
for (Floorplan::FloorOutlinePolygon* poly : outline) {
|
||||
if (poly->method != Floorplan::OutlineMethod::ADD) {continue;}
|
||||
HelperPoly pol(*poly);
|
||||
if (pol.contains(Point2(x_cm, y_cm))) {add = true;}
|
||||
}
|
||||
if (!add) {return false;}
|
||||
for (Floorplan::FloorOutlinePolygon* poly : outline) {
|
||||
if (poly->method != Floorplan::OutlineMethod::REMOVE) {continue;}
|
||||
HelperPoly pol(*poly);
|
||||
if (pol.contains(Point2(x_cm, y_cm))) {add = false;} // TODO
|
||||
}
|
||||
return add;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** add the given floor to the grid */
|
||||
void addFloor(const Floorplan::Floor* floor, GridFactoryListener* listener = nullptr) {
|
||||
|
||||
Log::add(name, "adding floor " + floor->name, true);
|
||||
if (listener) {listener->onGridBuildUpdateMinor("adding floor " + floor->name);}
|
||||
|
||||
const BBox2 bbox = getFloorOutlineBBox(floor->outline);
|
||||
const int x1 = helper.align(bbox.getMin().x);
|
||||
const int x2 = helper.align(bbox.getMax().x);
|
||||
const int y1 = helper.align(bbox.getMin().y);
|
||||
const int y2 = helper.align(bbox.getMax().y);
|
||||
const int z_cm = (floor->atHeight*100);
|
||||
|
||||
const int total = (x2-x1) / helper.gridSize();
|
||||
int cur = 0;
|
||||
|
||||
// build grid-points for floor-outline
|
||||
for(int x_cm = x1; x_cm < x2; x_cm += helper.gridSize()) {
|
||||
for (int y_cm = y1; y_cm < y2; y_cm += helper.gridSize()) {
|
||||
|
||||
// does the outline-polygon contain this position?
|
||||
if (!isPartOfFloorOutline(x_cm, y_cm, floor->outline)) {continue;}
|
||||
|
||||
// check intersection with the floorplan
|
||||
GridNodeBBox bbox(GridPoint(x_cm, y_cm, z_cm), helper.gridSize());
|
||||
|
||||
// slightly grow the bbox to ensure even obstacles that are directly aligned to the bbox are hit
|
||||
bbox.grow(0.012345);
|
||||
if (intersects(bbox, floor)) {continue;}
|
||||
|
||||
// add to the grid
|
||||
T t(x_cm, y_cm, z_cm);
|
||||
t.setType(getType(bbox, floor));
|
||||
if (grid.hasNodeFor(t)) {continue;}
|
||||
grid.add(t);
|
||||
|
||||
}
|
||||
if (listener) {listener->onGridBuildUpdateMinor(total, ++cur);}
|
||||
}
|
||||
|
||||
// connect the g
|
||||
connectAdjacent(z_cm);
|
||||
|
||||
}
|
||||
|
||||
void buildStairs(const Floorplan::Floor* floor, GridFactoryListener* listener = nullptr) {
|
||||
|
||||
const int total = floor->stairs.size();
|
||||
int cur = 0;
|
||||
|
||||
// process each stair within the floor
|
||||
for (const Floorplan::Stair* stair : floor->stairs) {
|
||||
if (listener) {listener->onGridBuildUpdateMinor("adding " + floor->name + " stair " + std::to_string(cur+1));}
|
||||
stairs.build(floor, stair);
|
||||
if (listener) {listener->onGridBuildUpdateMinor(total, ++cur);}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// void addWithin(const Floorplan::Floor* floor, std::vector<Entry>& nodeList) {
|
||||
|
||||
// const int fz1_cm = floor->atHeight*100;
|
||||
// const int fz2_cm = (floor->atHeight+floor->height)*100;
|
||||
|
||||
// for (Entry& e : nodeList) {
|
||||
|
||||
// if ( (e.pos.z_cm <= fz1_cm) || (e.pos.z_cm >= fz2_cm) ) {
|
||||
// e.idx = grid.getNearestNode(e.pos).getIdx();
|
||||
// e.part = {9999};
|
||||
// } else {
|
||||
// const T t(e.pos.x_cm, e.pos.y_cm, e.pos.z_cm);
|
||||
// e.idx = grid.addUnaligned(t, false);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// void filter(std::vector<Entry>& nodeList) {
|
||||
|
||||
// const int gs_cm = grid.getGridSize_cm();
|
||||
// const int limit_cm = gs_cm * 0.50;
|
||||
|
||||
// // remove duplicate nodes or nodes, that are too close to each other
|
||||
// for(auto it = nodeList.begin(); it != nodeList.end(); ) {
|
||||
|
||||
// // currently examined node
|
||||
// const Entry& e1 = *it;
|
||||
|
||||
// // matches for nodes that are NOT the same instance AND nearby (< grid-size)
|
||||
// auto matches = [&] (const Entry& e2) { return (e1.pos != e2.pos) && (e1.pos.getDistanceInCM(e2.pos) <= limit_cm); };
|
||||
// auto match = std::find_if(nodeList.begin(), nodeList.end(), matches);
|
||||
|
||||
// // remove if this node has a nearby neighbor
|
||||
// if (match != nodeList.end()) {
|
||||
|
||||
// // combine both nodes within one:
|
||||
// // the node belongs to more than one stair-part
|
||||
// Entry& e2 = *match;
|
||||
// e2.part.insert(e2.part.end(), e1.part.begin(), e1.part.end());
|
||||
// it = nodeList.erase(it);
|
||||
|
||||
// } else {
|
||||
// ++it;
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// std::vector<Entry> minOnly(const std::vector<Entry>& lst) {
|
||||
// auto comp = [&] (const Entry& a, const Entry& b) {return grid[a.idx].z_cm < grid[b.idx].z_cm;};
|
||||
// auto it = std::min_element(lst.begin(), lst.end(), comp);
|
||||
// T& min = grid[it->idx];
|
||||
// std::vector<Entry> res;
|
||||
// for (const Entry& e : lst) {
|
||||
// if (grid[e.idx].z_cm == min.z_cm) {res.push_back(e);}
|
||||
// }
|
||||
// return res;
|
||||
// }
|
||||
|
||||
/** connect all neighboring nodes part of the given index-vector */
|
||||
void connectAdjacent(const std::vector<int>& indices) {
|
||||
|
||||
for (const int idx : indices) {
|
||||
|
||||
// connect the node with its neighbors
|
||||
connectAdjacent(grid[idx]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** connect all neighboring nodes located on the given height-plane */
|
||||
void connectAdjacent(const float z_cm) {
|
||||
|
||||
Log::add(name, "connecting all adjacent nodes at height " + std::to_string(z_cm), false);
|
||||
Log::tick();
|
||||
|
||||
// connect adjacent grid-points
|
||||
for (T& n1 : grid) {
|
||||
|
||||
// not the floor we are looking for? -> skip (ugly.. slow(er))
|
||||
if (n1.z_cm != z_cm) {continue;}
|
||||
|
||||
// connect the node with its neighbors
|
||||
connectAdjacent(n1);
|
||||
|
||||
}
|
||||
|
||||
Log::tock();
|
||||
|
||||
}
|
||||
|
||||
/** connect the given node with its neighbors */
|
||||
void connectAdjacent(T& n1) {
|
||||
|
||||
const int gridSize_cm = grid.getGridSize_cm();
|
||||
|
||||
// square around the node
|
||||
for (int x = -1; x <= +1; ++x) {
|
||||
for (int y = -1; y <= +1; ++y) {
|
||||
|
||||
// skip the center (node itself)
|
||||
if ((x == y) && (x == 0)) {continue;}
|
||||
|
||||
// position of the potential neighbor
|
||||
const int ox = n1.x_cm + x * gridSize_cm;
|
||||
const int oy = n1.y_cm + y * gridSize_cm;
|
||||
const GridPoint p(ox, oy, n1.z_cm);
|
||||
|
||||
// does the grid contain the potential neighbor?
|
||||
const T* n2 = grid.getNodePtrFor(p);
|
||||
if (n2 != nullptr) {
|
||||
grid.connectUniDir(n1, *n2); // UNI-dir connection as EACH node is processed!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** add the inverted version of the given z-layer */
|
||||
void addInverted(const Grid<T>& gIn, const float z_cm) {
|
||||
|
||||
// get the original grid's bbox
|
||||
BBox3 bb = gIn.getBBox();
|
||||
|
||||
// ensure we have an outer boundary
|
||||
bb.grow(gIn.getGridSize_cm() * 2);
|
||||
|
||||
const int gridSize_cm = grid.getGridSize_cm();
|
||||
|
||||
// build new grid-points
|
||||
for(int x_cm = bb.getMin().x; x_cm <= bb.getMax().x; x_cm += gridSize_cm) {
|
||||
for (int y_cm = bb.getMin().y; y_cm < bb.getMax().y; y_cm += gridSize_cm) {
|
||||
|
||||
// does the input-grid contain such a point?
|
||||
GridPoint gp(x_cm, y_cm, z_cm);
|
||||
if (gIn.hasNodeFor(gp)) {continue;}
|
||||
|
||||
// add to the grid
|
||||
grid.add(T(x_cm, y_cm, z_cm));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// // TODO: how to determine the starting index?!
|
||||
|
||||
// // IDEAS: find all segments:
|
||||
// // start at a random point, add all connected points to the set
|
||||
// // start at a NEW random point ( not part of the already processed points), add connected points to a new set
|
||||
// // repeat until all points processed
|
||||
// // how to handle multiple floor layers?!?!
|
||||
// // run after all floors AND staircases were added??
|
||||
// // OR: random start, check segment size, < 50% of all nodes? start again
|
||||
|
||||
// void removeIsolatedNodes() {
|
||||
|
||||
// Log::add(name, "searching for isolated nodes");
|
||||
|
||||
// // get largest connected region
|
||||
// std::unordered_set<int> set;
|
||||
// do {
|
||||
// const int idxStart = rand() % grid.getNumNodes();
|
||||
// set.clear();
|
||||
// Log::add(name, "getting connected region starting at " + (std::string) grid[idxStart]);
|
||||
// getConnected(grid[idxStart], set);
|
||||
// Log::add(name, "region size is " + std::to_string(set.size()) + " nodes");
|
||||
|
||||
// } while (set.size() < 0.5 * grid.getNumNodes());
|
||||
|
||||
// // remove all other
|
||||
// Log::add(name, "removing the isolated nodes");
|
||||
// for (int i = 0; i < grid.getNumNodes(); ++i) {
|
||||
// if (set.find(i) == set.end()) {grid.remove(i);}
|
||||
// }
|
||||
|
||||
// // clean the grid
|
||||
// grid.cleanup();
|
||||
|
||||
// }
|
||||
|
||||
void removeIsolatedNodes() {
|
||||
|
||||
// try to start at the first stair
|
||||
for (T& n : grid) {
|
||||
if (n.getType() == GridNode::TYPE_STAIR) {removeIsolatedNodes(n); return;}
|
||||
}
|
||||
|
||||
// no stair found? try to start at the first node
|
||||
removeIsolatedNodes(grid[0]);
|
||||
|
||||
}
|
||||
|
||||
/** remove all nodes not connected to n1 */
|
||||
void removeIsolatedNodes(T& n1) {
|
||||
|
||||
// get the connected region around n1
|
||||
Log::add(name, "getting set of all nodes connected to " + (std::string) n1, false);
|
||||
Log::tick();
|
||||
std::unordered_set<int> set;
|
||||
getConnected(n1, set);
|
||||
Log::tock();
|
||||
|
||||
// remove all other
|
||||
Log::add(name, "removing all nodes NOT connected to " + (std::string) n1, false);
|
||||
Log::tick();
|
||||
for (T& n2 : grid) {
|
||||
if (set.find(n2.getIdx()) == set.end()) {grid.remove(n2);}
|
||||
}
|
||||
Log::tock();
|
||||
|
||||
// clean the grid (physically delete the removed nodes)
|
||||
grid.cleanup();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/** recursively get all connected nodes and add them to the set */
|
||||
void getConnected(T& n1, std::unordered_set<int>& visited) {
|
||||
|
||||
std::unordered_set<int> toVisit;
|
||||
toVisit.insert(n1.getIdx());
|
||||
|
||||
// run while there are new nodes to visit
|
||||
while(!toVisit.empty()) {
|
||||
|
||||
// get the next node
|
||||
int nextIdx = *toVisit.begin();
|
||||
toVisit.erase(nextIdx);
|
||||
visited.insert(nextIdx);
|
||||
T& next = grid[nextIdx];
|
||||
|
||||
// get all his (unprocessed) neighbors and add them to the region
|
||||
for (const T& n2 : grid.neighbors(next)) {
|
||||
if (visited.find(n2.getIdx()) == visited.end()) {
|
||||
toVisit.insert(n2.getIdx());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
/** does the bbox intersect with any of the floor's walls? */
|
||||
static inline bool intersects(const GridNodeBBox& bbox, const Floorplan::Floor* floor) {
|
||||
|
||||
// process each obstacle
|
||||
// (obstacles use meter, while the bbox is in centimeter!
|
||||
for (Floorplan::FloorObstacle* fo : floor->obstacles) {
|
||||
|
||||
// depends on the type of obstacle
|
||||
if (dynamic_cast<Floorplan::FloorObstacleLine*>(fo)) {
|
||||
const Floorplan::FloorObstacleLine* line = (Floorplan::FloorObstacleLine*) fo;
|
||||
const Line2 l2(line->from*100, line->to*100);
|
||||
if (bbox.intersects(l2)) {return true;}
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleCircle*>(fo)) {
|
||||
const Floorplan::FloorObstacleCircle* circle = (Floorplan::FloorObstacleCircle*) fo;
|
||||
const Point2 center = bbox.getCenter();
|
||||
const float dist = center.getDistance(circle->center*100);
|
||||
if (dist < circle->radius*100) {return true;}
|
||||
|
||||
} else if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
|
||||
// DOORS ARE NOT AN OBSTACLE
|
||||
|
||||
} else {
|
||||
throw Exception("TODO: not yet implemented obstacle type");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** does the bbox intersect with any of the floor's walls? */
|
||||
static inline int getType(const GridNodeBBox& bbox, const Floorplan::Floor* floor) {
|
||||
|
||||
// process each obstacle
|
||||
for (Floorplan::FloorObstacle* fo : floor->obstacles) {
|
||||
|
||||
if (dynamic_cast<Floorplan::FloorObstacleDoor*>(fo)) {
|
||||
const Floorplan::FloorObstacleDoor* door = (Floorplan::FloorObstacleDoor*) fo;
|
||||
const Line2 l2(door->from*100, door->to*100);
|
||||
if (bbox.intersects(l2)) {return GridNode::TYPE_DOOR;}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return GridNode::TYPE_FLOOR;
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // GRIDFACTORY_V2_H
|
||||
219
grid/factory/v2/Helper.h
Normal file
219
grid/factory/v2/Helper.h
Normal file
@@ -0,0 +1,219 @@
|
||||
#ifndef GRID_FACTORY_HELPER_H
|
||||
#define GRID_FACTORY_HELPER_H
|
||||
|
||||
#include "../../../geo/Point2.h"
|
||||
#include "../../../geo/Point3.h"
|
||||
#include "../../../geo/BBox2.h"
|
||||
#include "../../../geo/BBox3.h"
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
#include "../../../grid/Grid.h"
|
||||
|
||||
/** helper class for polygon methods */
|
||||
struct HelperPoly {
|
||||
|
||||
BBox2 bbox_cm;
|
||||
std::vector<Point2> points_cm;
|
||||
|
||||
/** empty ctor */
|
||||
HelperPoly() {
|
||||
;
|
||||
}
|
||||
|
||||
/** ctor from floorplan-polygon */
|
||||
HelperPoly(const Floorplan::FloorOutlinePolygon& poly) {
|
||||
for (Point2 p : poly.poly.points) { add(p * 100); }
|
||||
}
|
||||
|
||||
/** ctor from floorplan-quad */
|
||||
HelperPoly(const Floorplan::Quad3& quad) {
|
||||
add(quad.p1); add(quad.p2); add(quad.p3); add(quad.p4);
|
||||
}
|
||||
|
||||
void add(const Point2 p) {
|
||||
points_cm.push_back(p);
|
||||
bbox_cm.add(p);
|
||||
}
|
||||
|
||||
void add(const Point3& p) {
|
||||
points_cm.push_back(p.xy());
|
||||
bbox_cm.add(p.xy());
|
||||
}
|
||||
|
||||
/** does the polygon contain the given point (in cm)? */
|
||||
bool contains(const Point2 p_cm) const {
|
||||
|
||||
// not within bbox? -> not within polygon
|
||||
if (!bbox_cm.contains(p_cm)) {return false;}
|
||||
|
||||
// ensure the point is at least a bit outside of the polygon
|
||||
const float x1_cm = bbox_cm.getMin().x - 17.71920;
|
||||
const float y1_cm = bbox_cm.getMin().y - 23.10923891;
|
||||
|
||||
// construct line between point outside of the polygon and the point in question
|
||||
const Line2 l(x1_cm, y1_cm, p_cm.x, p_cm.y);
|
||||
|
||||
// determine the number of intersections
|
||||
int hits = 0;
|
||||
const int cnt = points_cm.size();
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
const Point2 p1 = points_cm[(i+0)%cnt];
|
||||
const Point2 p2 = points_cm[(i+1)%cnt];
|
||||
const Line2 l12(p1, p2);
|
||||
if (l12.getSegmentIntersection(l)) {++hits;}
|
||||
}
|
||||
|
||||
// inside or outside?
|
||||
return ((hits % 2) == 1);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename T> class Helper {
|
||||
|
||||
private:
|
||||
|
||||
Grid<T>& grid;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Helper(Grid<T>& grid) : grid(grid) {
|
||||
|
||||
}
|
||||
|
||||
/** connect the given node to all its neighbors */
|
||||
void connectToNeighbors(T& n1) {
|
||||
|
||||
const int gs_cm = grid.getGridSize_cm();
|
||||
|
||||
for (int y = -gs_cm; y <= +gs_cm; y += gs_cm) {
|
||||
for (int x = -gs_cm; x <= +gs_cm; x += gs_cm) {
|
||||
|
||||
// skip the node itself
|
||||
if (x == 0 && y == 0) {continue;}
|
||||
|
||||
// try to find a matching neighbor
|
||||
const GridPoint gp(n1.x_cm + x, n1.y_cm + y, n1.z_cm);
|
||||
const T* n2 = grid.getNodePtrFor(gp);
|
||||
if (!n2) {continue;}
|
||||
|
||||
// connect
|
||||
if (n1.hasNeighbor(n2->getIdx())) {continue;}
|
||||
grid.connectUniDir(n1, *n2);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gridSize() const {
|
||||
return grid.getGridSize_cm();;
|
||||
}
|
||||
|
||||
float align(const float val) {
|
||||
const float gridSize_cm = gridSize();
|
||||
return std::round(val/gridSize_cm) * gridSize_cm;
|
||||
}
|
||||
|
||||
float alignF(const float val) {
|
||||
const float gridSize_cm = gridSize();
|
||||
return std::floor(val/gridSize_cm) * gridSize_cm;
|
||||
}
|
||||
|
||||
float alignC(const float val) {
|
||||
const float gridSize_cm = gridSize();
|
||||
return std::ceil(val/gridSize_cm) * gridSize_cm;
|
||||
}
|
||||
|
||||
Point3 align(const Point3 p) {
|
||||
return Point3( align(p.x), align(p.y), (p.z) ); // TODO: align z or not?
|
||||
}
|
||||
|
||||
float min(float a, float b, float c, float d) {
|
||||
if (a < b && a < c && a < d) {return a;}
|
||||
if (b < a && b < c && b < d) {return b;}
|
||||
if (c < a && c < b && c < d) {return c;}
|
||||
if (d < a && d < b && d < c) {return d;}
|
||||
}
|
||||
float max(float a, float b, float c, float d) {
|
||||
if (a > b && a > c && a > d) {return a;}
|
||||
if (b > a && b > c && b > d) {return b;}
|
||||
if (c > a && c > b && c > d) {return c;}
|
||||
if (d > a && d > b && d > c) {return d;}
|
||||
}
|
||||
|
||||
void clamp(float& val, const int min, const int max) {
|
||||
while (val < min) {val += gridSize();}
|
||||
while (val > max) {val -= gridSize();}
|
||||
}
|
||||
|
||||
|
||||
void limit(Floorplan::Quad3& q2, const Floorplan::Quad3& q1) {
|
||||
const float x1 = min(q1.p1.x, q1.p2.x, q1.p3.x, q1.p4.x);
|
||||
const float x2 = max(q1.p1.x, q1.p2.x, q1.p3.x, q1.p4.x);
|
||||
const float y1 = min(q1.p1.y, q1.p2.y, q1.p3.y, q1.p4.y);
|
||||
const float y2 = max(q1.p1.y, q1.p2.y, q1.p3.y, q1.p4.y);
|
||||
const float z1 = min(q1.p1.z, q1.p2.z, q1.p3.z, q1.p4.z);
|
||||
const float z2 = max(q1.p1.z, q1.p2.z, q1.p3.z, q1.p4.z);
|
||||
// clamp(q2.p1.x, x1, x2); clamp(q2.p2.x, x1, x2); clamp(q2.p3.x, x1, x2); clamp(q2.p4.x, x1, x2);
|
||||
// clamp(q2.p1.y, y1, y2); clamp(q2.p2.y, y1, y2); clamp(q2.p3.y, y1, y2); clamp(q2.p4.y, y1, y2);
|
||||
clamp(q2.p1.z, z1, z2); clamp(q2.p2.z, z1, z2); clamp(q2.p3.z, z1, z2); clamp(q2.p4.z, z1, z2);
|
||||
}
|
||||
|
||||
Floorplan::Quad3 align(const Floorplan::Quad3& q) {
|
||||
Floorplan::Quad3 q2 = Floorplan::Quad3(align(q.p1), align(q.p2), align(q.p3), align(q.p4));
|
||||
//limit(q2, q);
|
||||
return q2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static float dot(const Point2 a, const Point2 b) {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
static float area(const Point2 a, const Point2 b, const Point2 c) {
|
||||
//Point2 ab = b-a;
|
||||
//Point2 ac = c-a;
|
||||
//return 0.5f * std::sqrt(dot(ab,ab)*dot(ac,ac) - dot(ab,ac)*dot(ab,ac));
|
||||
Point2 p1 = b-a;
|
||||
Point2 p2 = c-a;
|
||||
return std::abs(p1.x*p2.y - p2.x*p1.y) * 0.5;
|
||||
}
|
||||
|
||||
static bool bary(Point2 p, Point2 a, Point2 b, Point2 c, float &u, float &v, float &w) {
|
||||
Point2 v0 = b - a, v1 = c - a, v2 = p - a;
|
||||
float d00 = dot(v0, v0);
|
||||
float d01 = dot(v0, v1);
|
||||
float d11 = dot(v1, v1);
|
||||
float d20 = dot(v2, v0);
|
||||
float d21 = dot(v2, v1);
|
||||
float denom = d00 * d11 - d01 * d01;
|
||||
v = (d11 * d20 - d01 * d21) / denom;
|
||||
w = (d00 * d21 - d01 * d20) / denom;
|
||||
u = 1.0f - v - w;
|
||||
return (u <= 1 && v <= 1 && w <= 1) && (u >= 0 && v >= 0 && w >= 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // GRID_FACTORY_HELPER_H
|
||||
217
grid/factory/v2/Importance.h
Normal file
217
grid/factory/v2/Importance.h
Normal file
@@ -0,0 +1,217 @@
|
||||
#ifndef IMPORTANCE_H
|
||||
#define IMPORTANCE_H
|
||||
|
||||
#include "../../Grid.h"
|
||||
|
||||
#include "../../../misc/KNN.h"
|
||||
#include "../../../misc/KNNArray.h"
|
||||
|
||||
#include "../../../math/MiniMat2.h"
|
||||
#include "../../../math/Distributions.h"
|
||||
|
||||
|
||||
class Importance {
|
||||
|
||||
private:
|
||||
|
||||
static constexpr const char* name = "GridImp";
|
||||
|
||||
public:
|
||||
|
||||
|
||||
template <typename T> static void addOutlineNodes(Grid<T>& dst, Grid<T>& src) {
|
||||
|
||||
for (const T& n : src) {
|
||||
if (n.getNumNeighbors() < 8) {
|
||||
if (!dst.hasNodeFor(n)) {
|
||||
dst.add(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** attach importance-factors to the grid */
|
||||
template <typename T> static void addImportance(Grid<T>& g, const float z_cm) {
|
||||
|
||||
Log::add(name, "adding importance information to all nodes at height " + std::to_string(z_cm));
|
||||
|
||||
// get an inverted version of the grid
|
||||
Grid<T> inv(g.getGridSize_cm());
|
||||
addOutlineNodes(inv, g);
|
||||
//GridFactory<T> fac(inv);
|
||||
//fac.addInverted(g, z_cm);
|
||||
|
||||
// sanity check
|
||||
Assert::isFalse(inv.getNumNodes() == 0, "inverted grid is empty!");
|
||||
|
||||
// construct KNN search
|
||||
KNN<Grid<T>, 3> knn(inv);
|
||||
|
||||
// the number of neighbors to use
|
||||
static constexpr int numNeighbors = 12;
|
||||
|
||||
// create list of all doors
|
||||
std::vector<T> doors;
|
||||
|
||||
// process each node
|
||||
for (T& n1 : g) {
|
||||
|
||||
// is the current node a door?
|
||||
//if (isDoor(n1, neighbors)) {doors.push_back(n1);} // OLD
|
||||
if (n1.getType() == GridNode::TYPE_DOOR) {doors.push_back(n1);} // NEW!
|
||||
|
||||
// favor stairs just like doors
|
||||
//if (isStaircase(g, n1)) {doors.push_back(n1);} // OLD
|
||||
if (n1.getType() == GridNode::TYPE_STAIR) {doors.push_back(n1);} // NEW
|
||||
|
||||
}
|
||||
|
||||
KNNArray<std::vector<T>> knnArrDoors(doors);
|
||||
KNN<KNNArray<std::vector<T>>, 3> knnDoors(knnArrDoors);
|
||||
|
||||
Distribution::Normal<float> favorDoors(0.0f, 0.6f);
|
||||
|
||||
// process each node again
|
||||
for (T& n1 : g) {
|
||||
|
||||
// skip nodes on other than the requested floor-level
|
||||
//if (n1.z_cm != z_cm) {continue;}
|
||||
|
||||
// get the 10 nearest neighbors and their distance
|
||||
size_t indices[numNeighbors];
|
||||
float squaredDist[numNeighbors];
|
||||
float point[3] = {n1.x_cm, n1.y_cm, n1.z_cm};
|
||||
knn.get(point, numNeighbors, indices, squaredDist);
|
||||
|
||||
// get the neighbors
|
||||
std::vector<T*> neighbors;
|
||||
for (int i = 0; i < numNeighbors; ++i) {
|
||||
neighbors.push_back(&inv[indices[i]]);
|
||||
}
|
||||
|
||||
n1.imp = 1.0f;
|
||||
|
||||
//if (n1.getType() == GridNode::TYPE_FLOOR) {
|
||||
|
||||
// get the distance to the nearest door
|
||||
const float distToWall_m = Units::cmToM(std::sqrt(squaredDist[0]) + g.getGridSize_cm());
|
||||
|
||||
// get the distance to the nearest door
|
||||
const float distToDoor_m = Units::cmToM(knnDoors.getNearestDistance( {n1.x_cm, n1.y_cm, n1.z_cm} ));
|
||||
|
||||
n1.imp =
|
||||
1 +
|
||||
getWallImportance( distToWall_m ) +
|
||||
favorDoors.getProbability(distToDoor_m);
|
||||
|
||||
|
||||
//}
|
||||
//addDoor(n1, neighbors);
|
||||
|
||||
// importance for this node (based on the distance from the next door)
|
||||
//n1.imp += favorDoors.getProbability(dist_m) * 0.30;
|
||||
|
||||
|
||||
//n1.imp = (dist_m < 0.2) ? (1) : (0.5);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** is the given node connected to a staircase? */
|
||||
template <typename T> static bool isStaircase(Grid<T>& g, T& node) {
|
||||
|
||||
return node.getType() == GridNode::TYPE_STAIR;
|
||||
|
||||
// // if this node has a neighbor with a different z, this is a stair
|
||||
// for (T& neighbor : g.neighbors(node)) {
|
||||
// if (neighbor.z_cm != node.z_cm) {return true;}
|
||||
// }
|
||||
// return false;
|
||||
|
||||
}
|
||||
|
||||
/** is the given node (and its inverted neighbors) a door? */
|
||||
template <typename T> static bool isDoor( T& nSrc, std::vector<T*> neighbors ) {
|
||||
|
||||
if (nSrc.getType() != GridNode::TYPE_FLOOR) {return false;}
|
||||
|
||||
MiniMat2 m1;
|
||||
// MiniMat2 m2;
|
||||
Point3 center = nSrc;
|
||||
|
||||
// calculate the centroid of the nSrc's nearest-neighbors
|
||||
Point3 centroid(0,0,0);
|
||||
for (const T* n : neighbors) {
|
||||
centroid = centroid + (Point3)*n;
|
||||
}
|
||||
centroid /= neighbors.size();
|
||||
|
||||
// if nSrc is too far from the centroid, this does not make sense
|
||||
if ((centroid-center).length() > 40) {return false;}
|
||||
|
||||
// build covariance of the nearest-neighbors
|
||||
int used = 0;
|
||||
for (const T* n : neighbors) {
|
||||
|
||||
const Point3 d1 = (Point3)*n - centroid;
|
||||
if (d1.length() > 100) {continue;} // radius search
|
||||
m1.addSquared(d1.x, d1.y);
|
||||
|
||||
// const Point3 d2 = (Point3)*n - center;
|
||||
// if (d2.length() > 100) {continue;} // radius search
|
||||
// m2.addSquared(d2.x, d2.y);
|
||||
|
||||
++used;
|
||||
|
||||
}
|
||||
|
||||
// we need at least two points for the covariance
|
||||
if (used < 6) {return false;}
|
||||
|
||||
// check eigenvalues
|
||||
MiniMat2::EV ev1 = m1.getEigenvalues();
|
||||
// MiniMat2::EV ev2 = m2.getEigenvalues();
|
||||
|
||||
// ensure e1 > e2
|
||||
if (ev1.e1 < ev1.e2) {std::swap(ev1.e1, ev1.e2);}
|
||||
// if (ev2.e1 < ev2.e2) {std::swap(ev2.e1, ev2.e2);}
|
||||
|
||||
// door?
|
||||
const float ratio1 = (ev1.e2/ev1.e1);
|
||||
// const float ratio2 = (ev2.e2/ev2.e1);
|
||||
// const float ratio3 = std::max(ratio1, ratio2) / std::min(ratio1, ratio2);
|
||||
|
||||
return (ratio1 < 0.30 && ratio1 > 0.05) ;
|
||||
|
||||
}
|
||||
|
||||
/** get the importance of the given node depending on its nearest wall */
|
||||
static float getWallImportance(float dist_m) {
|
||||
|
||||
// avoid sticking too close to walls (unlikely)
|
||||
static Distribution::Normal<float> avoidWalls(0.0, 0.5);
|
||||
|
||||
// favour walking near walls (likely)
|
||||
static Distribution::Normal<float> stickToWalls(0.9, 0.5);
|
||||
|
||||
// favour walking far away (likely)
|
||||
static Distribution::Normal<float> farAway(2.2, 0.5);
|
||||
if (dist_m > 2.0) {dist_m = 2.0;}
|
||||
|
||||
// overall importance
|
||||
// return - avoidWalls.getProbability(dist_m) * 0.30 // avoid walls
|
||||
// + stickToWalls.getProbability(dist_m) * 0.15 // walk near walls
|
||||
// + farAway.getProbability(dist_m) * 0.15 // walk in the middle
|
||||
return - avoidWalls.getProbability(dist_m) // avoid walls
|
||||
//+ stickToWalls.getProbability(dist_m) // walk near walls
|
||||
//+ farAway.getProbability(dist_m) // walk in the middle
|
||||
;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // IMPORTANCE_H
|
||||
281
grid/factory/v2/Stairs.h
Normal file
281
grid/factory/v2/Stairs.h
Normal file
@@ -0,0 +1,281 @@
|
||||
#ifndef STAIRS_H
|
||||
#define STAIRS_H
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "../../Grid.h"
|
||||
#include "Helper.h"
|
||||
#include "../../../floorplan/v2/Floorplan.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename T> class Stairs {
|
||||
|
||||
private:
|
||||
|
||||
/** the grid to build into */
|
||||
Grid<T>& grid;
|
||||
|
||||
/** calculation helper */
|
||||
Helper<T> helper;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// struct Entry {
|
||||
// GridPoint pos;
|
||||
// std::vector<int> part;
|
||||
// int idx;
|
||||
// Entry(const GridPoint pos, int part) : pos(pos) {
|
||||
// this->part.push_back(part);
|
||||
// }
|
||||
// };
|
||||
|
||||
/** 2D integer point */
|
||||
struct XY {
|
||||
int x;
|
||||
int y;
|
||||
XY() : x(-1), y(-1) {;}
|
||||
XY(const int x, const int y) : x(x), y(y) {;}
|
||||
bool operator == (const XY& o) const {return (x == o.x) && (y == o.y);}
|
||||
};
|
||||
|
||||
|
||||
/** 3D integer point */
|
||||
struct XYZ {
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
XYZ() : x(-1), y(-1), z(-1) {;}
|
||||
XYZ(const int x, const int y, const int z) : x(x), y(y), z(z) {;}
|
||||
bool operator == (const XYZ& o) const {return (x == o.x) && (y == o.y) && (z == o.z);}
|
||||
};
|
||||
|
||||
/** and intermediate grid node */
|
||||
struct Intermediate {
|
||||
XY xy;
|
||||
T node;
|
||||
int idx;
|
||||
std::vector<XY> con;
|
||||
Intermediate(const XY xy, const T node) : xy(xy),node(node), idx(-1) {;}
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
Stairs(Grid<T>& grid) : grid(grid), helper(grid) {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void build(const Floorplan::Floor* floor, const Floorplan::Stair* stair) {
|
||||
|
||||
const int gs_cm = grid.getGridSize_cm();
|
||||
|
||||
std::vector<Intermediate> stairNodes;
|
||||
|
||||
// process each part
|
||||
int partIdx = 0;
|
||||
|
||||
const std::vector<Floorplan::StairPart> parts = stair->getParts();
|
||||
const std::vector<Floorplan::Quad3> quads = Floorplan::getQuads(parts, floor);
|
||||
|
||||
for (int i = 0; i < (int)parts.size(); ++i) {
|
||||
|
||||
//const Floorplan::StairPart& part = parts[i];
|
||||
const Floorplan::Quad3& quad = helper.align(quads[i]*100);
|
||||
|
||||
const Point3 p1 = quad.p1;
|
||||
const Point3 p2 = quad.p2;
|
||||
const Point3 p3 = quad.p3;
|
||||
const Point3 p4 = quad.p4;
|
||||
|
||||
HelperPoly poly; poly.add(p1.xy()); poly.add(p2.xy()); poly.add(p3.xy()); poly.add(p4.xy());
|
||||
|
||||
const int x1 = helper.align(poly.bbox_cm.getMin().x); // ALIGNF?
|
||||
const int y1 = helper.align(poly.bbox_cm.getMin().y);
|
||||
const int x2 = helper.align(poly.bbox_cm.getMax().x); // ALIGNC?
|
||||
const int y2 = helper.align(poly.bbox_cm.getMax().y);
|
||||
|
||||
std::vector<XYZ> points;
|
||||
|
||||
// get all points that are part of the stair-part-polygon
|
||||
for (int x = x1; x <= x2; x+=gs_cm) {
|
||||
for (int y = y1; y <= y2; y+=gs_cm) {
|
||||
if (!poly.contains( Point2(x,y) )) {continue;}
|
||||
points.push_back(XYZ(x,y,-1));
|
||||
}
|
||||
}
|
||||
|
||||
// now determine each corresponding z-coordinate
|
||||
for (XYZ& xy : points) {
|
||||
|
||||
const Point2 p(xy.x, xy.y);
|
||||
float u,v,w;
|
||||
|
||||
if (helper.bary(p, p1.xy(), p2.xy(), p3.xy(), u, v, w)) {
|
||||
xy.z = p1.z*u + p2.z*v + p3.z*w;
|
||||
} else {
|
||||
helper.bary(p, p1.xy(), p3.xy(), p4.xy(), u, v, w);
|
||||
xy.z = p1.z*u + p3.z*v + p4.z*w;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// stair start, ensure min snaps to the floor
|
||||
if (partIdx == 0) {
|
||||
const float minz_cm = minZ(points);
|
||||
const float maxz_cm = maxZ(points);
|
||||
const int mz = floor->atHeight * 100;
|
||||
if (minz_cm != mz) {
|
||||
for (XYZ& xyz : points) {
|
||||
const float percent = ((xyz.z - minz_cm) / (maxz_cm - minz_cm));
|
||||
xyz.z = mz + percent * (maxz_cm - mz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stair end, ensure max snaps to the floor
|
||||
if (partIdx == (int) parts.size() - 1) {
|
||||
const float minz_cm = minZ(points);
|
||||
const float maxz_cm = maxZ(points);
|
||||
const int mz = (floor->atHeight+floor->height) * 100;
|
||||
if (maxz_cm != mz) {
|
||||
for (XYZ& xyz : points) {
|
||||
const float percent = ((xyz.z - minz_cm) / (maxz_cm - minz_cm));
|
||||
xyz.z = minz_cm + percent * (mz - minz_cm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (XYZ& xy : points) {
|
||||
|
||||
// construct a grid-node
|
||||
T node(xy.x, xy.y, xy.z);
|
||||
|
||||
// construct an intermediate-node containing all desired connections
|
||||
Intermediate iNode(XY(xy.x, xy.y), node);
|
||||
for (int ox = -1; ox <= +1; ++ox) {
|
||||
for (int oy = -1; oy <= +1; ++oy) {
|
||||
if (ox == 0 && oy == 0) {continue;}
|
||||
const int nx = xy.x + ox * gs_cm;
|
||||
const int ny = xy.y + oy * gs_cm;
|
||||
iNode.con.push_back( XY(nx, ny) );
|
||||
}
|
||||
}
|
||||
stairNodes.push_back(iNode);
|
||||
|
||||
}
|
||||
|
||||
|
||||
++partIdx;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// keep a list of all vertices below stairwells and remove them hereafter
|
||||
std::vector<T*> toDelete;
|
||||
|
||||
// add all stair nodes or replace them with already existing ones
|
||||
for (Intermediate& iNode : stairNodes) {
|
||||
|
||||
// nearer than minDiff? -> use existing node
|
||||
const float minDiff = gs_cm * 0.49;
|
||||
|
||||
// nearer than maxDiff? -> delete as this one is unreachable below the stair
|
||||
const float maxDiff = 150;
|
||||
|
||||
// distance between the stair node and the floor above / below
|
||||
const float zDiff1 = std::abs(iNode.node.z_cm - 100*floor->getStartingZ());
|
||||
const float zDiff2 = std::abs(iNode.node.z_cm - 100*floor->getEndingZ());
|
||||
|
||||
iNode.idx = -1;
|
||||
|
||||
// try to connect the stair-node to the floor below
|
||||
if (zDiff1 < minDiff) {
|
||||
const T* n2 = grid.getNodePtrFor(GridPoint(iNode.node.x_cm, iNode.node.y_cm, 100*floor->getStartingZ()));
|
||||
if (n2) {
|
||||
iNode.idx = n2->getIdx();
|
||||
} else {
|
||||
iNode.idx = grid.add(iNode.node); // add a new one
|
||||
helper.connectToNeighbors(grid[iNode.idx]); // and connect to the floor's grid nodes
|
||||
}
|
||||
|
||||
// try to connect the stair-node to the floor above
|
||||
} else if (zDiff2 < minDiff) {
|
||||
const T* n2 = grid.getNodePtrFor(GridPoint(iNode.node.x_cm, iNode.node.y_cm, 100*floor->getEndingZ()));
|
||||
if (n2) {
|
||||
iNode.idx = n2->getIdx();
|
||||
} else {
|
||||
iNode.idx = grid.add(iNode.node); // add a new one
|
||||
helper.connectToNeighbors(grid[iNode.idx]); // and connect to the floor's grid nodes
|
||||
}
|
||||
|
||||
// remove nodes directly below the stair
|
||||
} else if (zDiff1 < maxDiff) {
|
||||
T* n2 = (T*) grid.getNodePtrFor(GridPoint(iNode.node.x_cm, iNode.node.y_cm, 100*floor->getStartingZ()));
|
||||
if (n2) {toDelete.push_back(n2);}
|
||||
|
||||
// remove nodes directly above the stair
|
||||
} else if (zDiff2 < maxDiff) {
|
||||
T* n2 = (T*) grid.getNodePtrFor(GridPoint(iNode.node.x_cm, iNode.node.y_cm, 100*floor->getEndingZ()));
|
||||
if (n2) {toDelete.push_back(n2);}
|
||||
}
|
||||
|
||||
// no matching node found -> add a new one to the grid
|
||||
if (iNode.idx == -1) {
|
||||
iNode.idx = grid.add(iNode.node);
|
||||
}
|
||||
|
||||
// add semantic information
|
||||
grid[iNode.idx].setType(GridNode::TYPE_STAIR);
|
||||
|
||||
}
|
||||
|
||||
// connect to-be-connected stair-nodes (each node with its [previously configured] neighbors)
|
||||
for (const Intermediate& t1 : stairNodes) {
|
||||
for (const XY& connectTo : t1.con) {
|
||||
for (const Intermediate& t2 : stairNodes) {
|
||||
if (connectTo == t2.xy) {
|
||||
T& n1 = grid[t1.idx]; // use the nodes already added to the grid
|
||||
T& n2 = grid[t2.idx]; // use the nodes already added to the grid
|
||||
if (n1.hasNeighbor(n2.getIdx())) {continue;}
|
||||
if (n1.getIdx() == n2.getIdx()) {continue;}
|
||||
if (n1.getNumNeighbors() >= 10) {continue;}
|
||||
grid.connectUniDir(n1, n2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete all pending nodes and perform a cleanup
|
||||
for (T* n : toDelete) {grid.remove(*n);}
|
||||
grid.cleanup();
|
||||
|
||||
|
||||
}
|
||||
|
||||
static float minZ(const std::vector<XYZ>& points) {
|
||||
auto comp = [] (const XYZ& a, const XYZ& b) {return a.z < b.z;};
|
||||
auto it = std::min_element(points.begin(), points.end(), comp);
|
||||
return it->z;
|
||||
}
|
||||
|
||||
static float maxZ(const std::vector<XYZ>& points) {
|
||||
auto comp = [] (const XYZ& a, const XYZ& b) {return a.z < b.z;};
|
||||
auto it = std::max_element(points.begin(), points.end(), comp);
|
||||
return it->z;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // STAIRS_H
|
||||
Reference in New Issue
Block a user