blob: f9645d5ba4cd27611b352daa9228ef69a95790e2 [file] [log] [blame]
Austin Schuhad596222018-01-31 23:34:03 -08001#ifndef Y2018_CONTORL_LOOPS_PYTHON_ARM_BOUNDS_H_
2#define Y2018_CONTORL_LOOPS_PYTHON_ARM_BOUNDS_H_
3
4#include <CGAL/Bbox_2.h>
5#include <CGAL/Boolean_set_operations_2.h>
6#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
7#include <CGAL/Polygon_2.h>
8#include <CGAL/Polygon_2_algorithms.h>
9#include <CGAL/Polygon_with_holes_2.h>
10#include <CGAL/squared_distance_2.h>
11
12#include <Eigen/Dense>
13
14// Prototype level code to find the nearest point and distance to a polygon.
15
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080016namespace y2018::control_loops {
Austin Schuhad596222018-01-31 23:34:03 -080017
18typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
19typedef K::Point_2 Point;
20typedef K::Segment_2 Segment;
21typedef CGAL::Bbox_2 Bbox;
22typedef CGAL::Polygon_2<K> SimplePolygon;
23typedef CGAL::Polygon_with_holes_2<K> Polygon;
24typedef K::Line_2 Line;
25typedef K::Vector_2 Vector;
26
Austin Schuhad596222018-01-31 23:34:03 -080027// Returns true if the point p3 is to the left of the vector from p1 to p2.
28inline bool is_left(Point p1, Point p2, Point p3) {
29 switch (CGAL::orientation(p1, p2, p3)) {
30 case CGAL::LEFT_TURN:
31 case CGAL::COLLINEAR:
32 return true;
33 case CGAL::RIGHT_TURN:
34 return false;
35 }
36}
37
38// Returns true if the segments intersect.
39inline bool intersects(Segment s1, Segment s2) {
40 return CGAL::do_intersect(s1, s2);
41}
42
43class BoundsCheck {
44 public:
45 BoundsCheck(const std::vector<Point> &points)
46 : points_(points), grid_(points_, 6) {}
47
48 double min_distance(Point point, ::Eigen::Matrix<double, 2, 1> *normal) const;
49
50 const std::vector<Point> &points() const { return points_; }
51
52 private:
53 static Bbox ToBbox(const std::vector<Point> &points) {
54 Bbox out;
55 out += Segment(points.back(), points.front()).bbox();
56 for (size_t i = 0; i < points.size() - 1; ++i) {
57 out += Segment(points[i], points[i + 1]).bbox();
58 }
59 return out;
60 }
61
62 static SimplePolygon ToPolygon(Bbox bbox) {
63 Point points[4]{{bbox.xmin(), bbox.ymin()},
64 {bbox.xmax(), bbox.ymin()},
65 {bbox.xmax(), bbox.ymax()},
66 {bbox.xmin(), bbox.ymax()}};
67 return SimplePolygon(&points[0], &points[4]);
68 }
69
70 static double min_dist(Point pt, const std::vector<Point> &points,
71 Segment *best_segment) {
72 *best_segment = Segment(points.back(), points.front());
73 double min_dist_sqr = CGAL::squared_distance(pt, *best_segment);
74 for (size_t i = 0; i < points.size() - 1; ++i) {
75 Segment s(points[i], points[i + 1]);
76 double segment_distance = CGAL::squared_distance(pt, s);
77 if (segment_distance < min_dist_sqr) {
78 min_dist_sqr = segment_distance;
79 *best_segment = s;
80 }
81 }
82 return sqrt(min_dist_sqr);
83 }
84
85 static std::vector<Segment> ToSegment(Bbox bbox) {
86 Point points[4]{{bbox.xmin(), bbox.ymin()},
87 {bbox.xmax(), bbox.ymin()},
88 {bbox.xmax(), bbox.ymax()},
89 {bbox.xmin(), bbox.ymax()}};
90
91 return std::vector<Segment>({{points[0], points[1]},
Philipp Schrader790cb542023-07-05 21:06:52 -070092 {points[1], points[2]},
93 {points[2], points[3]},
94 {points[3], points[0]}});
Austin Schuhad596222018-01-31 23:34:03 -080095 }
96
97 static bool check_inside(Point pt, const std::vector<Point> &points) {
98 switch (CGAL::bounded_side_2(&points[0], &points[points.size()], pt, K())) {
99 case CGAL::ON_BOUNDED_SIDE:
100 case CGAL::ON_BOUNDARY:
101 return true;
102 case CGAL::ON_UNBOUNDED_SIDE:
103 return false;
104 }
105 return false;
106 }
107
108 const std::vector<Point> points_;
109
110 class GridCell {
111 public:
112 GridCell(const std::vector<Point> &points, Bbox bbox) {
113 bool has_intersect = false;
114
115 Point center{(bbox.xmin() + bbox.xmax()) / 2,
116 (bbox.ymin() + bbox.ymax()) / 2};
117 // Purposefully overestimate.
118 double r = bbox.ymax() - bbox.ymin();
119
120 Segment best_segment;
121 double best = min_dist(center, points, &best_segment);
122 dist_upper_bound_ = best + 2 * r;
123 dist_lower_bound_ = std::max(best - 2 * r, 0.0);
124
125 double sq_upper_bound = dist_upper_bound_ * dist_upper_bound_;
126
127 auto try_add_segment = [&](Segment segment) {
128 for (const auto &bbox_segment : ToSegment(bbox)) {
129 if (CGAL::do_intersect(bbox_segment, segment)) {
130 has_intersect = true;
131 }
132 }
133
134 double dist_sqr = CGAL::squared_distance(center, segment);
135 if (dist_sqr < sq_upper_bound) {
136 segments_.push_back(segment);
137 }
138 };
139
140 try_add_segment(Segment(points.back(), points.front()));
141 for (size_t i = 0; i < points.size() - 1; ++i) {
142 try_add_segment(Segment(points[i], points[i + 1]));
143 }
144 if (has_intersect) {
145 is_borderline = true;
146 } else {
147 is_inside = check_inside(center, points);
148 }
149 }
150
151 bool IsInside(Point pt) const {
152 (void)pt;
153 return is_inside;
154 }
155
156 bool IsBorderline() const { return is_borderline; }
157
158 double DistanceSqr(Point pt, Segment *best_segment) const {
159 double min_dist_sqr = CGAL::squared_distance(pt, segments_[0]);
160 *best_segment = segments_[0];
161 for (size_t i = 1; i < segments_.size(); ++i) {
162 double new_distance = CGAL::squared_distance(pt, segments_[i]);
163 if (new_distance < min_dist_sqr) {
164 min_dist_sqr = new_distance;
165 *best_segment = segments_[i];
166 }
167 }
168 return min_dist_sqr;
169 }
170 double Distance(Point pt, Segment *best_segment) const {
171 return sqrt(DistanceSqr(pt, best_segment));
172 }
173
174 bool is_inside = false;
175 bool is_borderline = false;
176 double dist_upper_bound_;
177 double dist_lower_bound_;
178 std::vector<Segment> segments_;
179 std::vector<std::vector<Point>> polygons_;
180 };
181
182 class GridSystem {
183 public:
184 // Precision is really 2**-precision and must be positive.
185 GridSystem(const std::vector<Point> &points, int precision)
186 : points_(points), scale_factor_(1 << precision) {
187 auto bbox = ToBbox(points);
188 fprintf(stderr, "%g %g, %g %g\n", bbox.xmin(), bbox.ymin(), bbox.xmax(),
189 bbox.ymax());
190 x_min_ = static_cast<int>(std::floor(bbox.xmin() * scale_factor_)) - 1;
191 y_min_ = static_cast<int>(std::floor(bbox.ymin() * scale_factor_)) - 1;
192
193 stride_ = static_cast<int>(bbox.xmax() * scale_factor_) + 3 - x_min_;
194 height_ = static_cast<int>(bbox.ymax() * scale_factor_) + 3 - y_min_;
195
196 fprintf(stderr, "num_cells: %d\n", stride_ * height_);
197 cells_.reserve(stride_ * height_);
198 for (int y_cell = 0; y_cell < height_; ++y_cell) {
199 for (int x_cell = 0; x_cell < stride_; ++x_cell) {
200 cells_.push_back(
201 GridCell(points, Bbox(static_cast<double>(x_cell + x_min_) /
202 static_cast<double>(scale_factor_),
203 static_cast<double>(y_cell + y_min_) /
204 static_cast<double>(scale_factor_),
205 static_cast<double>(x_cell + x_min_ + 1) /
206 static_cast<double>(scale_factor_),
207 static_cast<double>(y_cell + y_min_ + 1) /
208 static_cast<double>(scale_factor_))));
209 }
210 }
211 }
212
213 const GridCell *GetCell(Point pt) const {
214 int x_cell =
215 static_cast<int>(std::floor(pt.x() * scale_factor_)) - x_min_;
216 int y_cell =
217 static_cast<int>(std::floor(pt.y() * scale_factor_)) - y_min_;
218 if (x_cell < 0 || x_cell >= stride_) return nullptr;
219 if (y_cell < 0 || y_cell >= height_) return nullptr;
220 return &cells_[stride_ * y_cell + x_cell];
221 }
222
223 const std::vector<Point> &points() const { return points_; }
224
225 private:
226 std::vector<Point> points_;
227 int scale_factor_;
228 int x_min_;
229 int y_min_;
230 int stride_;
231 int height_;
232 std::vector<GridCell> cells_;
233 };
234
235 GridSystem grid_;
236};
237
238BoundsCheck MakeClippedArmSpace();
239BoundsCheck MakeFullArmSpace();
240
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -0800241} // namespace y2018::control_loops
Austin Schuhad596222018-01-31 23:34:03 -0800242
243#endif // Y2018_CONTORL_LOOPS_PYTHON_ARM_BOUNDS_H_