area_occupancy_estimator.h
Go to the documentation of this file.
1 #ifndef AREA_OCCUPANCY_ESTIMATOR_H
2 #define AREA_OCCUPANCY_ESTIMATOR_H
3 
4 #include <cassert>
5 #include <vector>
6 #include <cmath>
7 #include <tuple>
8 #include <algorithm>
9 
11 
13 private: // types
14  enum class SegmentPositionType : char {
16  };
17 public: //methods
18 
20  const Occupancy& base_empty,
21  double low_qual = 0.01, double unknown_qual = 0.5)
23  , _low_qual{low_qual}, _unknown_qual{unknown_qual} {
24  assert(_low_qual < 1);
25  }
26 
27  Occupancy estimate_occupancy(const Segment2D &beam, const Rectangle &cell,
28  bool is_occ) override {
29  Segment2D effective_beam = ensure_segment_not_on_edge(beam, cell);
30  switch (classify_segment(effective_beam, cell)) {
33  return Occupancy::invalid();
36  if (is_occ) { return Occupancy::invalid(); }
37  break;
39  return is_occ ? Occupancy::invalid() :
42  break;
43  }
44 
45  Intersections intrs = find_intersections(effective_beam, cell, is_occ);
46  if (intrs.size() == 1) {
47  if (!is_occ) { // StopsInside/FrontEdge/Vertex
49  } else { // occupied, stops at some vertex
50  auto raw_intersections = cell.find_intersections(effective_beam);
51  switch (raw_intersections.size()) {
52  case 0: assert(false && "BUG: no inters must be detected earlier");
53  case 1: // stops at front vertex -> entire cell is occupied
54  return estimate_occupancy(cell.area(), cell.area(), is_occ);
55  case 2: // stops at rear vertex, treat the cell as empty
56  intrs = raw_intersections;
57  is_occ = false;
58  break;
59  }
60  }
61  }
62  double chunk_area = compute_chunk_area(effective_beam, cell, is_occ, intrs);
63  return estimate_occupancy(chunk_area, cell.area(), is_occ);
64  }
65 
66 private: // methods
67 
69  const Rectangle &cell) const {
70  // NB: Invariant: beg/end movement should not effect intersections
71  static const double Shift_Amount = _low_qual * cell.side();
72  if (!cell.has_on_edge_line(s)) { return s; }
73 
74  Point2D shift;
75  if (s.is_horiz()) {
76  shift = {0, (are_equal(s.beg().y, cell.top()) ? -1 : 1) * Shift_Amount};
77  } else if (s.is_vert()) {
78  shift = {(are_equal(s.beg().x, cell.right()) ? -1 : 1) * Shift_Amount, 0};
79  } else {
80  assert(false && "BUG: non-axis aligned segment is on rectange edge");
81  }
82 
83  Segment2D shifted_s{s.beg() + shift, s.end() + shift};
84  assert(!cell.has_on_edge_line(shifted_s) && "BUG");
85  return shifted_s;
86  }
87 
89  const Rectangle &cell) const {
90  bool beg_is_inside, end_is_inside;
91  std::tie(beg_is_inside, end_is_inside) = modified_is_inside(s, cell);
92 
93  if (beg_is_inside ^ end_is_inside) {
94  return beg_is_inside ? SegmentPositionType::StartsInside :
96  }
97 
98  // both are eithes inside or outside
99  if (beg_is_inside) {
101  }
102 
103  Intersections intersections = cell.find_intersections(s);
104  switch (intersections.size()) {
105  case 0: return SegmentPositionType::Unrelated;
106  case 1: return SegmentPositionType::Touches;
107  case 2: return SegmentPositionType::Pierces;
108  }
109  assert(false && "BUG: unable to classify segment");
111  }
112 
113 
114  std::tuple<bool, bool> modified_is_inside(const Segment2D &s,
115  const Rectangle &cell) const {
116  // Main idea: check if the s is inside cell
117  // "extending/shrinking" s if its ends are on cell's edges
118  auto beg_edge = cell.find_containing_edge(s.beg());
119  auto end_edge = cell.find_containing_edge(s.end());
120  if (beg_edge && end_edge) {
121  return std::make_tuple(false, false); // move out of border
122  }
123 
124  // NB: contains includes edge into analysis
125  bool beg_contains = cell.contains(s.beg());
126  bool end_contains = cell.contains(s.end());
127  if (!beg_edge && !end_edge) { // contains is ok
128  return std::make_tuple(beg_contains, end_contains);
129  }
130 
131  // beg_edge, end_edge are opposite
132  // 1. Beg is on edge, End is out -> both are out,
133  // since s doesn't adds any info about cell
134  // 2. End is on edge -> extend it to the side opposide to s begin
135  return beg_edge ? std::make_tuple(false, end_contains) :
136  std::make_tuple(beg_contains, !beg_contains);
137  }
138 
140  const Rectangle &bnds, bool is_occ) {
141  // if the cell is occupied, rotate ray around beam end by 90 degrees
142  auto ray = is_occ ?
143  Ray{beam.end().x, beam.beg().y - beam.end().y,
144  beam.end().y, beam.end().x - beam.beg().x} :
145  Ray{beam.beg().x, beam.end().x - beam.beg().x,
146  beam.beg().y, beam.end().y - beam.beg().y};
147  auto inters = bnds.find_intersections(ray);
148  if (is_occ) {
149  // occupancy is intersected via fake ray, so not need to filter points
150  return inters;
151  }
152 
153  Intersections filtered_inters;
154  std::copy_if(inters.begin(), inters.end(),
155  std::back_inserter(filtered_inters),
156  [&beam](const Intersection &inters_p){
157  return beam.contains_intersection(inters_p);
158  });
159  return filtered_inters;
160  }
161 
162  double compute_chunk_area(const Segment2D &beam, const Rectangle &bnds,
163  bool is_occ,
164  const std::vector<Intersection> &inters) {
165  if (inters.size() == 0) {
166  // TODO: deal with line approx introduced by Bresenham
167  return bnds.area() / 2;
168  }
169 
170  assert(inters.size() == 2);
171  double corner_x = 0, corner_y = 0, area = 0;
172  double chunk_is_triangle = inters[0].is_horiz() ^ inters[1].is_horiz();
173  if (chunk_is_triangle) {
174  // determine "base" corner (corner of cell that
175  // is also a corner of the triangle
176  for (auto &inter : inters) {
177  switch (inter.location) {
178  case Intersection::Location::Bot: corner_y = bnds.bot(); break;
179  case Intersection::Location::Top: corner_y = bnds.top(); break;
180  case Intersection::Location::Left: corner_x = bnds.left(); break;
181  case Intersection::Location::Right: corner_x = bnds.right(); break;
182  default: assert(false && "Unexpected location type");
183  }
184  }
185  // calculate triange area
186  area = 0.5;
187  for (auto &inter : inters) {
188  if (inter.is_horiz()) {
189  area *= std::abs(inter.x - corner_x);
190  } else {
191  area *= std::abs(inter.y - corner_y);
192  }
193  }
194  } else {
195  // chunk is a trapezoid
196  // corner choise doesn't matter, so pick bottom-left one.
197  corner_x = bnds.left(), corner_y = bnds.bot();
198  double base_sum = 0;
199  for (auto &inter : inters) {
200  if (inter.is_horiz()) {
201  base_sum += std::abs(inter.x - corner_x);
202  } else {
203  base_sum += std::abs(inter.y - corner_y);
204  }
205  }
206  // NOTE: cell is supposed to be a square
207  area = 0.5 * (bnds.top() - bnds.bot()) * base_sum;
208  }
209  assert(0 <= area && area <= bnds.area() && "BUG: AOE area estimation");
210  if (is_occ &&
211  are_on_the_same_side(inters[0], inters[1],
212  beam.beg(), {corner_x, corner_y})) {
213  area = bnds.area() - area;
214  }
215  return area;
216  }
217 
218  bool are_on_the_same_side(const Point2D &line_p1, const Point2D &line_p2,
219  const Point2D &p1, const Point2D &p2) {
220  double dx = line_p2.x - line_p1.x, dy = line_p2.y - line_p1.y;
221  return 0 < (dy*p1.y - dx*p1.x + dy*p1.x - dx*p1.y) *
222  (dy*p2.y - dx*p2.x + dy*p2.x - dx*p2.y);
223  }
224 
225  Occupancy estimate_occupancy(double chunk_area, double total_area,
226  bool is_occ) {
227  double area_rate = chunk_area / total_area;
228  if (is_occ) {
229  // Far ToDo: think about experiment quality metric for an occupied case.
230  return Occupancy{std::max(area_rate, base_empty().prob_occ),
232  } else {
233  // TODO: fix scale
234  if (0.5 < area_rate) {
235  area_rate = 1 - area_rate;
236  }
237  return Occupancy{base_empty().prob_occ,
238  base_empty().estimation_quality * area_rate};
239  }
240  }
241 private:
242  double _low_qual;
244 };
245 
246 #endif
double prob_occ
Definition: state_data.h:8
CONSTEXPR bool are_equal(const T &a, const T &b, const T &eps)
Definition: math_utils.h:17
SegmentPositionType classify_segment(const Segment2D &s, const Rectangle &cell) const
Segment2D find_containing_edge(const Point2D &p) const
bool contains_intersection(const Point2D &p) const
bool are_on_the_same_side(const Point2D &line_p1, const Point2D &line_p2, const Point2D &p1, const Point2D &p2)
bool is_vert() const
Intersections find_intersections(const Segment2D &s) const
static Occupancy invalid()
Definition: state_data.h:27
const Occupancy & base_empty() const
const Point2D & end() const
std::vector< Intersection > Intersections
double compute_chunk_area(const Segment2D &beam, const Rectangle &bnds, bool is_occ, const std::vector< Intersection > &inters)
Occupancy estimate_occupancy(const Segment2D &beam, const Rectangle &cell, bool is_occ) override
double estimation_quality
Definition: state_data.h:9
std::tuple< bool, bool > modified_is_inside(const Segment2D &s, const Rectangle &cell) const
Segment2D ensure_segment_not_on_edge(const Segment2D &s, const Rectangle &cell) const
bool contains(const Point2D &p) const
bool is_horiz() const
Intersections find_intersections(const Segment2D &beam, const Rectangle &bnds, bool is_occ)
const Point2D & beg() const
Occupancy estimate_occupancy(double chunk_area, double total_area, bool is_occ)
const Occupancy & base_occupied() const
AreaOccupancyEstimator(const Occupancy &base_occupied, const Occupancy &base_empty, double low_qual=0.01, double unknown_qual=0.5)
bool has_on_edge_line(const Segment2D &s) const


slam_constructor
Author(s): JetBrains Research, OSLL team
autogenerated on Mon Jun 10 2019 15:08:25