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:
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 {
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)
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)
00064 return;
00065 consumer.push_back(Intersection(loc, x, inters_y));
00066 }
00067 };
00068
00069 public:
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:
00090 Intersections find_intersections(const Beam &beam,
00091 const Rectangle &bnds, bool is_occ) {
00092 Intersections intersections;
00093
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
00116 return bnds.area() / 2;
00117 }
00118
00119 assert(inters.size() == 2 || inters.size() == 4);
00120 if (inters.size() == 4) {
00121
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
00129
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
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
00149
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
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
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