geometry_primitives.h
Go to the documentation of this file.
1 #ifndef SLAM_CTOR_CORE_GEOMETRY_PRIMITIVES_H
2 #define SLAM_CTOR_CORE_GEOMETRY_PRIMITIVES_H
3 
4 #include <cmath>
5 #include <cassert>
6 #include <algorithm>
7 #include <ostream>
8 
9 #include "math_utils.h"
10 
11 struct Point2D {
12  Point2D(double x_par = 0, double y_par = 0) : x(x_par), y(y_par) {}
13 
14  double dist_sq(const Point2D &pt) const {
15  return std::pow(x - pt.x, 2) + std::pow(y - pt.y, 2);
16  }
17 
18  bool operator==(const Point2D &that) const {
19  return are_equal(x, that.x) && are_equal(y, that.y);
20  }
21 
22  Point2D operator*(double scalar) const { return {scalar * x, scalar * y}; }
23  Point2D operator+(const Point2D& p) const { return {x + p.x, y + p.y}; }
24  Point2D operator-(const Point2D& p) const { return {x - p.x, y - p.y}; }
25 
26 public: // fields
27  double x, y;
28 };
29 
30 inline Point2D operator*(double scalar, const Point2D &p) { return p * scalar; }
31 
32 inline std::ostream &operator<<(std::ostream &stream, const Point2D &pnt) {
33  return stream << "(" << pnt.x << ", " << pnt.y << ")";
34 }
35 
36 class Segment2D {
37 public: //methods
38 
39  static Segment2D invalid() {
40  Segment2D invalid = {{0, 0}, {0, 0}};
41  invalid.is_valid = false;
42  return invalid;
43  }
44 
45  Segment2D(const Point2D &begin, const Point2D &end) : _beg{begin}, _end{end} {
46  _is_horiz = are_equal(_beg.y, _end.y);
47  _is_vert = are_equal(_beg.x, _end.x);
48  }
49 
50  bool is_horiz() const { return _is_horiz; }
51  bool is_vert() const { return _is_vert; }
52  bool is_point() const { return _is_horiz && _is_vert; }
53 
54  const Point2D& beg() const { return _beg; }
55  const Point2D& end() const { return _end; }
56 
57  explicit operator bool() const { return is_valid; }
58 
59  bool contains(const Point2D &p) const {
60  if (is_horiz()) {
61  return are_equal(p.y, _beg.y) && are_ordered(_beg.x, p.x, _end.x);
62  }
63  if (is_vert()) {
64  return are_equal(p.x, _beg.x) && are_ordered(_beg.y, p.y, _end.y);
65  }
66  assert(false && "BUG: Contains is undefined for non-axes-aligned segment");
67  return false;
68  }
69 
70  bool contains_intersection(const Point2D &p) const {
71  return x_projection_contains(p) && y_projection_contains(p);
72  }
73 
74  double length_sq() const {
75  return std::pow(_end.x - _beg.x, 2) + std::pow(_end.y - _beg.y, 2);
76  }
77 
78 private: // methods
79 
80  bool x_projection_contains(const Point2D &p) const {
81  return are_ordered(_beg.x, p.x, _end.x) || are_ordered(_end.x, p.x, _beg.x);
82  }
83 
84  bool y_projection_contains(const Point2D &p) const {
85  return are_ordered(_beg.y, p.y, _end.y) || are_ordered(_end.y, p.y, _beg.y);
86  }
87 
88 private: // fields
89  bool is_valid = true;
90  Point2D _beg, _end;
91  bool _is_horiz, _is_vert;
92 };
93 
94 inline std::ostream &operator<<(std::ostream &stream, const Segment2D &s) {
95  return stream << "[" << s.beg() << "; " << s.end() << "]";
96 }
97 
98 struct Intersection : public Point2D {
99  enum class Location : char {
100  Bot = 0, Left = 1, Top = 2, Right = 3
101  };
102 
103  Intersection(Location loc, double x, double y) :
104  Point2D{x, y}, location(loc) {}
105 
106  bool is_horiz() const {
107  return location == Location::Bot || location == Location::Top;
108  }
109 
111 };
112 
113 using Intersections = std::vector<Intersection>;
114 
115 struct Ray { // in parametric form
116  Ray(double x_s, double x_d, double y_s, double y_d) :
117  beg{x_s, y_s}, delta{x_d, y_d} {}
118 
119  explicit Ray(const Segment2D &s) : beg(s.beg()), delta(s.end() - s.beg()) {}
120 
121 public:
123  Intersections &consumer) const {
124  if (s.is_horiz()) {
125  intersect_horiz_segm(s.beg().x, s.end().x, s.beg().y, loc, consumer);
126  return;
127  }
128  if (s.is_vert()) {
129  intersect_vert_segm(s.beg().y, s.end().y, s.beg().x, loc, consumer);
130  return;
131  }
132  assert(false && "BUG: Unable to intersect non-axes-aligned segment");
133  }
134 
135 private:
136  void intersect_horiz_segm(double st_x, double end_x, double y,
138  Intersections &consumer) const {
139  if (are_equal(delta.y, 0)) { return; }
140 
141  double inters_alpha = (y - beg.y) / delta.y;
142  double inters_x = beg.x + inters_alpha * delta.x;
143  if (inters_x < st_x || end_x < inters_x) // out of segment bounds
144  return;
145 
146  consumer.emplace_back(loc, inters_x, y);
147  }
148 
149  void intersect_vert_segm(double st_y, double end_y, double x,
151  Intersections &consumer) const {
152  if (are_equal(delta.x, 0)) { return; }
153 
154  double inters_alpha = (x - beg.x) / delta.x;
155  double inters_y = beg.y + inters_alpha * delta.y;
156  if (inters_y < st_y || end_y < inters_y) // out of segment bounds
157  return;
158 
159  consumer.emplace_back(loc, x, inters_y);
160  }
161 private: // fields
163 };
164 
166 private:
168 public:
170  LightWeightRectangle(double b, double t, double l, double r)
171  : _bot{b}, _top{t}, _left{l}, _right{r} {
172  assert(_bot <= _top);
173  assert(_left <= _right);
174  }
176  : LightWeightRectangle(p.y, p.y, p.x, p.x) {}
177 
178  double bot() const { return _bot; }
179  double top() const { return _top; }
180  double left() const { return _left; }
181  double right() const { return _right; }
182 
183  double vside_len() const { return top() - bot(); }
184  double hside_len() const { return right() - left(); }
185  bool is_square() const { return vside_len() == hside_len(); }
186  double side() const { return vside_len(); }
187  double area() const { return vside_len() * hside_len(); }
188  Point2D center() const {
189  return {left() + hside_len() / 2, bot() + vside_len() / 2};
190  }
191 
192  bool is_line() const { return (vside_len() == 0) ^ (hside_len() == 0); }
193  bool is_point() const { return (vside_len() == 0) && (hside_len() == 0); }
194 
195  bool operator==(const LightWeightRectangle &rhs) const {
196  return are_equal(top(), rhs.top()) && are_equal(bot(), rhs.bot()) &&
197  are_equal(left(), rhs.left()) && are_equal(right(), rhs.right());
198  }
199 
200  auto corners() const {
201  return std::vector<Point2D>{{left(), bot()}, {left(), top()},
202  {right(), bot()}, {right(), top()}};
203  }
204 
205  LVRect move_center(const Point2D &new_center) const {
206  double half_v = vside_len() / 2, half_h = hside_len() / 2;
207  return {new_center.y - half_v, new_center.y + half_v,
208  new_center.x - half_h, new_center.x + half_h};
209  }
210 
211  auto shrink(double factor) const {
212  auto c = center();
213  auto new_hv = vside_len() / (factor * 2),
214  new_hh = hside_len() / (factor * 2);
215  return LVRect{c.y - new_hv, c.y + new_hv,
216  c.x - new_hh, c.x + new_hh};
217 
218  }
219 
220  auto split_vert() const {
221  auto c = center();
222  return std::vector<LVRect>{
223  LVRect{bot(), c.y, left(), right()},
224  LVRect{ c.y, top(), left(), right()}
225  };
226  }
227 
228  auto split_horz() const {
229  auto c = center();
230  return std::vector<LVRect>{
231  LVRect{bot(), top(), left(), c.x},
232  LVRect{bot(), top(), c.x, right()}
233  };
234  }
235 
236  auto split4_evenly() const {
237  auto c = center();
238  return std::vector<LVRect>{
239  LVRect{ bot(), c.y, left(), c.x}, // left-bot
240  LVRect{ c.y, top(), left(), c.x}, // left-top
241  LVRect{ bot(), c.y, c.x, right()}, // right-bot
242  LVRect{ c.y, top(), c.x, right()} // right-top
243  };
244  }
245 
246  bool contains(const Point2D &p) const { return contains(p.x, p.y); }
247 
248  bool contains(double x, double y) const {
249  return are_ordered(left(), x, right()) && are_ordered(bot(), y, top());
250  }
251 
252  auto intersect(const LightWeightRectangle &that) const {
253  return intersect_internal(that, false);
254  }
255 
256  auto overlap(const LightWeightRectangle &that) const {
257  if (area()) {
258  return intersect(that).area() / area();
259  }
260  if (that.area()) {
261  return that.contains(left(), bot()) ? 1.0 : 0.0;
262  }
263  // FIXME: the assert below, see unit tests.
264  assert(is_point() && that.is_point() && "TODO: support lwr-lines");
265  return *this == that ? 1.0 : 0.0;
266  }
267 
268 private:
269 
271  bool reversed = false) const {
272  // NB: a naive implementation; straightforward and correct (I hope so).
273  // IDEA: shrink -this- to intersection rectangle.
274 
275  // FIXME: @see unit tests
276  unsigned contained_corners_nm = 0;
277  auto cleft = left(), cright = right(), ctop = top(), cbot = bot();
278 
279  #define PROCESS_CORNER(horz_id, vert_id) \
280  if (contains(that.horz_id(), that.vert_id())) { \
281  ++contained_corners_nm; \
282  c##horz_id = that.horz_id(); \
283  c##vert_id = that.vert_id(); \
284  }
285 
286  PROCESS_CORNER(left, bot);
287  PROCESS_CORNER(right, bot);
288  PROCESS_CORNER(left, top);
289  PROCESS_CORNER(right, top);
290 
291  #undef PROCESS_CORNER
292 
293  // handle corner cases based on a number of contained corners (pun intended)
294  switch (contained_corners_nm) {
295  case 0:
296  /*
297  * Either doesn't overlap or the overlap must be detected
298  * by this-in-that corners analysis (reversed).
299  */
300  // FIXME: awkward stuff
301  return reversed ? LightWeightRectangle{}
302  : that.intersect_internal(*this, true);
303  case 1: case 2: case 4:
304  break;
305  case 3: default:
306  assert(0 && "Unexpected inclusions number");
307  }
308 
309  return LightWeightRectangle{cbot, ctop, cleft, cright};
310  }
311 
312 private: // fields
313  double _bot, _top, _left, _right;
314 };
315 
316 inline std::ostream &operator<<(std::ostream &stream,
317  const LightWeightRectangle &r) {
318  stream << "LVRectangle [t:" << r.top() << ", b:" << r.bot();
319  return stream << ", l:" << r.left() << ", r:" << r.right() << "]";
320 }
321 
323 private: // consts
324  static constexpr std::size_t BOT_EDGE_IDX = 0;
325  static constexpr std::size_t TOP_EDGE_IDX = 1;
326  static constexpr std::size_t LFT_EDGE_IDX = 2;
327  static constexpr std::size_t RHT_EDGE_IDX = 3;
328 public: // methods
329 
330  Rectangle() : Rectangle(0, 0, 0, 0) {}
331  Rectangle(double b, double t, double l, double r)
332  : LightWeightRectangle{b, t, l, r}
333  , _edges{
334  Segment2D{{ left(), bot()}, {right(), bot()}}, // 0 - bot
335  Segment2D{{ left(), top()}, {right(), top()}}, // 1 - top
336  Segment2D{{ left(), bot()}, { left(), top()}}, // 2 - left
337  Segment2D{{right(), bot()}, {right(), top()}} // 3 - right
338  } {}
339 
340  Rectangle(const Rectangle &) = default;
341  Rectangle(Rectangle &&) = default;
342  Rectangle& operator=(const Rectangle &rhs) = default;
343  Rectangle& operator=(Rectangle &&rhs) = default;
344 
345  /* Inclusion predicates */
346 
347  // NB: segment is not necessary coincide with an edge of the rectangle
348  bool has_on_edge_line(const Segment2D &s) const {
349  if (s.is_vert()) {
350  return are_equal(s.beg().x, left()) || are_equal(s.beg().x, right());
351  }
352  if (s.is_horiz()) {
353  return are_equal(s.beg().y, bot()) || are_equal(s.beg().y, top());
354  }
355  return false;
356  }
357 
359  for (auto &e : edges()) {
360  if (!e.contains(p)) { continue; }
361  return e;
362  }
363  return Segment2D::invalid();
364  }
365 
366  /* Intersections lookup */
367 
369  Intersections ray_intrs = find_intersections(Ray{s});
370  Intersections inters;
371  std::copy_if(ray_intrs.begin(), ray_intrs.end(), std::back_inserter(inters),
372  [&s](const Intersection &i){ return s.contains_intersection(i); });
373  return inters;
374  }
375 
377  Intersections intersections;
378 
379  // NB: order matters for correct vertex intersection handling
380  ray.intersect(top_edge(), Intersection::Location::Top, intersections);
381  ray.intersect(left_edge(), Intersection::Location::Left, intersections);
382  ray.intersect(bot_edge(), Intersection::Location::Bot, intersections);
383  ray.intersect(right_edge(), Intersection::Location::Right, intersections);
384 
385  // filter dublicate intersection (intersection is a vertex)
386  if (1 < intersections.size() &&
387  intersections.front() == intersections.back()) {
388  intersections.pop_back();
389  }
390  auto new_last = std::unique(intersections.begin(), intersections.end());
391  intersections.erase(new_last, intersections.end());
392  assert(intersections.size() < 3);
393  return intersections;
394  }
395 
396 private: // methods
397  const Segment2D& bot_edge() const { return edges()[BOT_EDGE_IDX]; }
398  const Segment2D& top_edge() const { return edges()[TOP_EDGE_IDX]; }
399  const Segment2D& left_edge() const { return edges()[LFT_EDGE_IDX]; }
400  const Segment2D& right_edge() const { return edges()[RHT_EDGE_IDX]; }
401 
402  const std::vector<Segment2D> &edges() const { return _edges; }
403 private: // fields
404  // NB: edges as Segment2D were introduced for code clarity.
405  // They can be dropped for performance reasons
406  const std::vector<Segment2D> _edges;
407 };
408 
409 #endif
bool operator==(const LightWeightRectangle &rhs) const
void intersect(const Segment2D &s, Intersection::Location loc, Intersections &consumer) const
bool contains(double x, double y) const
Intersection(Location loc, double x, double y)
#define PROCESS_CORNER(horz_id, vert_id)
auto overlap(const LightWeightRectangle &that) const
CONSTEXPR bool are_equal(const T &a, const T &b, const T &eps)
Definition: math_utils.h:17
bool contains(const Point2D &p) const
double dist_sq(const Point2D &pt) const
void intersect_vert_segm(double st_y, double end_y, double x, Intersection::Location loc, Intersections &consumer) const
LVRect move_center(const Point2D &new_center) const
bool is_point() const
Point2D operator-(const Point2D &p) const
auto intersect(const LightWeightRectangle &that) const
bool x_projection_contains(const Point2D &p) const
Point2D(double x_par=0, double y_par=0)
const std::vector< Segment2D > _edges
Segment2D find_containing_edge(const Point2D &p) const
LightWeightRectangle(const Point2D &p)
Segment2D(const Point2D &begin, const Point2D &end)
std::ostream & operator<<(std::ostream &stream, const Point2D &pnt)
Rectangle(double b, double t, double l, double r)
bool contains_intersection(const Point2D &p) const
bool operator==(const Point2D &that) const
auto shrink(double factor) const
Ray(double x_s, double x_d, double y_s, double y_d)
Point2D operator+(const Point2D &p) const
bool is_vert() const
LightWeightRectangle(double b, double t, double l, double r)
void intersect_horiz_segm(double st_x, double end_x, double y, Intersection::Location loc, Intersections &consumer) const
Intersections find_intersections(const Segment2D &s) const
Point2D operator*(double scalar) const
const Point2D & end() const
Intersections find_intersections(const Ray &ray) const
std::vector< Intersection > Intersections
Ray(const Segment2D &s)
bool are_ordered(double a, double b, double c)
Definition: math_utils.h:52
bool contains(const Point2D &p) const
bool y_projection_contains(const Point2D &p) const
bool is_horiz() const
const Segment2D & right_edge() const
static Segment2D invalid()
const Segment2D & left_edge() const
const Segment2D & bot_edge() const
const std::vector< Segment2D > & edges() const
bool is_horiz() const
const Point2D & beg() const
double length_sq() const
bool has_on_edge_line(const Segment2D &s) const
const Segment2D & top_edge() const
Point2D delta
LightWeightRectangle intersect_internal(const LightWeightRectangle &that, bool reversed=false) const


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