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