#ifndef GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H #define GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H #include "../../geo/Heading.h" #include "../Grid.h" #include "../../math/DrawList.h" #include #include "../../nav/dijkstra/Dijkstra.h" #include "GridWalkState.h" #include "GridWalkHelper.h" /** * perform walks on the grid based on some sort of weighting * and drawing from the weighted elements */ template class GridWalkLightAtTheEndOfTheTunnel { friend class GridWalkHelper; private: /** per-edge: change heading with this sigma */ static constexpr float HEADING_CHANGE_SIGMA = Angle::degToRad(5); /** per-edge: allowed heading difference */ static constexpr float HEADING_DIFF_SIGMA = Angle::degToRad(30); /** allows drawing elements according to their probability */ DrawList drawer; /** fast random-number-generator */ std::minstd_rand gen; /** 0-mean normal distribution */ std::normal_distribution headingChangeDist = std::normal_distribution(0.0, HEADING_CHANGE_SIGMA); Dijkstra dijkstra; public: /** ctor with the target you want to reach */ template GridWalkLightAtTheEndOfTheTunnel(Grid& grid, const Access& acc, const T& target) { // build all shortest path to reach th target dijkstra.build(target, target, acc); // attach a corresponding weight-information to each user-grid-node for (T& node : grid) { const DijkstraNode* dn = dijkstra.getNode(node); // should never be null as all nodes were evaluated if (dn != nullptr) { node.distToTarget = dn->cumWeight/2000; } } } GridWalkState getDestination(Grid& grid, GridWalkState start, float distance_m) { return GridWalkHelper::retryOrInvert(*this, 2, grid, start, distance_m); } private: GridWalkState walk(Grid& grid, GridWalkState cur, float distRest_m) { drawer.reset();; // calculate the weight for all possible destinations from "cur" for (T& neighbor : grid.neighbors(*cur.node)) { // heading when walking from cur to neighbor const Heading potentialHeading = GridWalkHelper::getHeading(*cur.node, neighbor); // angular difference const float diff = cur.heading.getDiffHalfRAD(potentialHeading); // probability for this direction change? double prob = K::NormalDistribution::getProbability(0, HEADING_DIFF_SIGMA, diff); // perfer locations reaching the target const double shortening = cur.node->distToTarget - neighbor.distToTarget; if (shortening >= 0) {prob *= 5;} // << importance factor!! // prob = 0.1; // if (diff < Angle::degToRad(40)) {prob += 0.2;} // else if (diff < Angle::degToRad(20)) {prob += 0.5;} // if (shortening >= 0) {prob += 0.5;} //prob *= std::pow(neighbor.imp, 5); //prob = (shortening >= 0) ? (2) : (0.75); drawer.add(neighbor, prob); } GridWalkState next(nullptr, cur.heading); // pick a random destination T& nDir = drawer.get(); const Heading hDir = GridWalkHelper::getHeading(*cur.node, nDir); //next.heading += (cur.heading.getRAD() - hDir.getRAD()) * -0.5; //next.heading = Heading( cur.heading.getRAD() * 0.2 + hDir.getRAD() * 0.8 ); next.heading = hDir; next.heading += headingChangeDist(gen); next.node = &nDir; //// // compare two neighbors according to their implied heading change //// auto compp = [&] (const T& n1, const T& n2) { //// Heading h1 = GridWalkHelper::getHeading(*cur.node, n1); //// Heading h2 = GridWalkHelper::getHeading(*cur.node, n2); //// const float d1 = next.heading.getDiffHalfRAD(h1); //// const float d2 = next.heading.getDiffHalfRAD(h2); //// // same heading -> prefer nodes nearer to the target. needed for stairs!!! //// // BAD: leads to straight lines in some palces. see solution B (below) //// //return (d1 < d2) && (n1.distToTarget < n2.distToTarget); //// // VERY IMPORTANT! //// // pick the node with the smallest heading change. //// // if the heading change is the same for two nodes, pick a random one! //// return (d1 == d2) ? (rand() < RAND_MAX/2) : (d1 < d2); //// }; //// // pick the neighbor best matching the new heading //// auto it = grid.neighbors(*cur.node); //// T& nn = *std::min_element(it.begin(), it.end(), compp); //// next.node = &nn; // next.node = &GridWalkHelper::getBestNeighbor(grid, *cur.node, next.heading); //// // pervent dramatic heading changes. instead: try again //// if (cur.heading.getDiffHalfRAD(GridWalkHelper::getHeading(*cur.node, nn)) > Angle::degToRad(60)) { //// return GridWalkState(nullptr, 0); //// } // get the distance up to this neighbor distRest_m -= next.node->getDistanceInMeter(*cur.node); // done? if (distRest_m <= 0) {return next;} // another round.. return walk(grid, next, distRest_m); } }; #endif // GRIDWALKLIGHTATTHEENDOFTHETUNNEL_H