area_occupancy_estimator.h
Go to the documentation of this file.
00001 #ifndef __AREA_OCCUPANCY_ESTIMATOR_H
00002 #define __AREA_OCCUPANCY_ESTIMATOR_H
00003 
00004 #include <cassert>
00005 #include <vector>
00006 #include <type_traits>
00007 
00008 #include "cell_occupancy_estimator.h"
00009 
00017 class AreaOccupancyEstimator : public CellOccupancyEstimator {
00018 private: // types
00019   enum class IntersLocation : char {
00020     Bot = 0, Left = 1, Top = 2, Right = 3
00021   };
00022 
00023   struct Intersection {
00024     Intersection(IntersLocation loc, double inters_x, double inters_y) :
00025       location(loc), x(inters_x), y(inters_y) {}
00026 
00027     IntersLocation location;
00028     bool is_horiz() const {
00029       return location == IntersLocation::Bot || location == IntersLocation::Top;
00030     }
00031     double x, y;
00032   };
00033 
00034   using Intersections = std::vector<Intersection>;
00035 
00036   struct Ray { // in parametric form
00037     Ray(double x_s, double x_d, double y_s, double y_d) :
00038       x_st(x_s), x_delta(x_d), y_st(y_s), y_delta(y_d) {}
00039 
00040     double x_st, x_delta;
00041     double y_st, y_delta;
00042 
00043     void intersect_horiz_segm(double st_x, double end_x, double y,
00044                               IntersLocation loc, Intersections &consumer) {
00045       if (EQ_DOUBLE(y_delta, 0))
00046         return;
00047 
00048       double inters_alpha = (y - y_st) / y_delta;
00049       double inters_x = x_st + inters_alpha * x_delta;
00050       if (inters_x < st_x || end_x < inters_x) // out of segment bounds
00051         return;
00052 
00053       consumer.push_back(Intersection(loc, inters_x, y));
00054     }
00055 
00056     void intersect_vert_segm(double st_y, double end_y, double x,
00057                              IntersLocation loc, Intersections &consumer) {
00058       if (EQ_DOUBLE(x_delta, 0))
00059         return;
00060 
00061       double inters_alpha = (x - x_st) / x_delta;
00062       double inters_y = y_st + inters_alpha * y_delta;
00063       if (inters_y < st_y || end_y < inters_y) // out of segment bounds
00064         return;
00065       consumer.push_back(Intersection(loc, x, inters_y));
00066     }
00067   };
00068 
00069 public: //methods
00070 
00075   AreaOccupancyEstimator(double occ, double empty) :
00076     CellOccupancyEstimator(occ, empty) {}
00077 
00081   virtual Occupancy estimate_occupancy(const Beam &beam,
00082                                        const Rectangle &cell_bnds,
00083                                        bool is_occ) override {
00084     Intersections intrs = find_intersections(beam, cell_bnds, is_occ);
00085     double chunk_area = compute_chunk_area(beam, cell_bnds, is_occ, intrs);
00086     return estimate_occupancy(chunk_area, cell_bnds.area(), is_occ);
00087   }
00088 
00089 private: // methods
00090   Intersections find_intersections(const Beam &beam,
00091                                    const Rectangle &bnds, bool is_occ) {
00092     Intersections intersections;
00093     // if the cell is occupied, rotate ray around beam by 90 degrees
00094     Ray ray = is_occ ?
00095       Ray(beam.x_end, beam.y_st - beam.y_end,
00096           beam.y_end, beam.x_end - beam.x_st) :
00097       Ray(beam.x_st, beam.x_end - beam.x_st,
00098           beam.y_st, beam.y_end - beam.y_st);
00099 
00100     ray.intersect_horiz_segm(bnds.left, bnds.right, bnds.top,
00101                              IntersLocation::Top, intersections);
00102     ray.intersect_horiz_segm(bnds.left, bnds.right, bnds.bot,
00103                              IntersLocation::Bot, intersections);
00104     ray.intersect_vert_segm(bnds.bot, bnds.top, bnds.left,
00105                             IntersLocation::Left, intersections);
00106     ray.intersect_vert_segm(bnds.bot, bnds.top, bnds.right,
00107                             IntersLocation::Right, intersections);
00108     return intersections;
00109   }
00110 
00111   double compute_chunk_area(const Beam &beam, const Rectangle &bnds,
00112                             bool is_occ,
00113                             const std::vector<Intersection> inters) {
00114     if (inters.size() == 0) {
00115       // TODO: deal with line approx introduced by Bresenham
00116       return bnds.area() / 2;
00117     }
00118 
00119     assert(inters.size() == 2 || inters.size() == 4);
00120     if (inters.size() == 4) {
00121       // TODO: generalize spec. case. Looks like ray is a cells diagonal
00122       return (bnds.top - bnds.bot) * (bnds.right - bnds.left) / 2;
00123     }
00124 
00125     double corner_x = 0, corner_y = 0, area = 0;
00126     double chunk_is_triangle = inters[0].is_horiz() ^ inters[1].is_horiz();
00127     if (chunk_is_triangle) {
00128       // determine "base" corner (corner of cell that
00129       // is also a corner of the triangle
00130       for (auto &inter : inters) {
00131         switch (inter.location) {
00132         case IntersLocation::Bot: corner_y = bnds.bot; break;
00133         case IntersLocation::Top: corner_y = bnds.top; break;
00134         case IntersLocation::Left: corner_x = bnds.left; break;
00135         case IntersLocation::Right: corner_x = bnds.right; break;
00136         }
00137       }
00138       // calculate triange area
00139       area = 0.5;
00140       for (auto &inter : inters) {
00141         if (inter.is_horiz()) {
00142           area *= inter.x - corner_x;
00143         } else {
00144           area *= inter.y - corner_y;
00145         }
00146       }
00147     } else {
00148       // chunk is trapezoid
00149       // corner choise doesn't matter, so pick bottom-left one.
00150       corner_x = bnds.bot, corner_y = bnds.left;
00151       double base_sum = 0;
00152       for (auto &inter : inters) {
00153         if (inter.is_horiz()) {
00154           base_sum += inter.x - corner_x;
00155         } else {
00156           base_sum += inter.y - corner_y;
00157         }
00158       }
00159       // NOTE: cell is supposed to be a square
00160       area = 0.5 * (bnds.top - bnds.bot) * base_sum;
00161     }
00162     if (is_occ &&
00163         are_on_the_same_side(inters[0].x, inters[0].y, inters[1].x, inters[1].y,
00164                              beam.x_st, beam.y_st, corner_x, corner_y)) {
00165       area = bnds.area() - area;
00166     }
00167     return area;
00168   }
00169 
00170   bool are_on_the_same_side(double line_x1, double line_y1,
00171                             double line_x2, double line_y2,
00172                             double x1, double y1, double x2, double y2) {
00173     double dx = line_x2 - line_x1, dy = line_y2 - line_y1;
00174     return 0 < (dy*y1 - dx*x1 + dy*x1 - dx*y1) *
00175                (dy*y2 - dx*x2 + dy*x2 - dx*y2);
00176   }
00177 
00178   Occupancy estimate_occupancy(double chunk_area, double total_area,
00179                                bool is_occ) {
00180     double area_rate = chunk_area / total_area;
00181     if (is_occ) {
00182       // Far ToDo: think about experiment quality metric for an occupied case.
00183       return Occupancy{area_rate, 1.0};
00184     } else {
00185       if (0.5 < area_rate) {
00186         area_rate = 1 - area_rate;
00187       }
00188       return Occupancy{base_empty_prob(), area_rate};
00189     }
00190   }
00191 };
00192 
00193 #endif


tiny_slam
Author(s):
autogenerated on Thu Jun 6 2019 17:44:57