Coverage Control Library
Loading...
Searching...
No Matches
voronoi.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the CoverageControl library
3 *
4 * Author: Saurav Agarwal
5 * Contact: sauravag@seas.upenn.edu, agr.saurav1@gmail.com
6 * Repository: https://github.com/KumarRobotics/CoverageControl
7 *
8 * Copyright (c) 2024, Saurav Agarwal
9 *
10 * The CoverageControl library is free software: you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or (at your
13 * option) any later version.
14 *
15 * The CoverageControl library is distributed in the hope that it will be
16 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
18 * Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along with
21 * CoverageControl library. If not, see <https://www.gnu.org/licenses/>.
22 */
23
29#include <omp.h>
30
31#include <functional>
32#include <iostream>
33
38
39namespace CoverageControl {
40
42 if (mass_ < kEps) {
43 Polygon_2 polygon;
44 for (auto const &p : cell) {
45 polygon.push_back(CGAL_Point2{p[0], p[1]});
46 }
47 auto cgal_centroid = CGAL::centroid(polygon.begin(), polygon.end());
48 centroid_ = CGALtoCC(cgal_centroid);
49 centroid_ += origin_shift;
50 } else {
51 centroid_ = centroid_ / mass_;
52 }
53}
54
55void Voronoi::CellNavigator(
56 VoronoiCell const &vcell,
57 std::function<void(double, Point2)> evaluator_func) {
58 auto const &cell = vcell.cell;
59 int n = cell.size();
60 int left_id = 0;
61 int right_id = 0;
62 for (int i = 1; i < n; ++i) {
63 if (cell[i].x() < cell[left_id].x()) {
64 left_id = i;
65 }
66 if (cell[i].x() > cell[right_id].x()) {
67 right_id = i;
68 }
69 }
70
71 int min_i = std::round(cell[left_id].x() / resolution_);
72 min_i = min_i < 0 ? 0 : min_i;
73
74 int max_i = std::round(cell[right_id].x() / resolution_);
75 max_i = max_i > map_size_.x() ? map_size_.x() : max_i;
76
77 auto next_id = [=](int const id) { return (id + 1) % n; };
78 auto prev_id = [=](int const id) { return id == 0 ? (n - 1) : (id - 1); };
79
80 auto cc_pt_id = next_id(left_id); // Counter-clockwise pointer
81 auto c_pt_id = prev_id(left_id); // Clockwise pointer
82
83 for (int i = min_i; i < max_i; ++i) {
84 double x = i * resolution_ + resolution_ / 2.;
85 while (true) {
86 if (cc_pt_id == left_id) {
87 break;
88 }
89 if (cell[cc_pt_id].x() < x) {
90 cc_pt_id = next_id(cc_pt_id);
91 } else {
92 break;
93 }
94 }
95 while (true) {
96 if (c_pt_id == left_id) {
97 break;
98 }
99 if (cell[c_pt_id].x() < x) {
100 c_pt_id = prev_id(c_pt_id);
101 } else {
102 break;
103 }
104 }
105
106 if (cell[cc_pt_id].x() < x) {
107 break;
108 }
109 if (cell[c_pt_id].x() < x) {
110 break;
111 }
112
113 auto prev_pt = cell[prev_id(cc_pt_id)];
114 auto cc_pt = cell[cc_pt_id];
115 auto x1 = prev_pt.x();
116 auto y1 = prev_pt.y();
117 auto x2 = cc_pt.x();
118 auto y2 = cc_pt.y();
119
120 if ((x2 - x1) < kEps) {
121 throw std::runtime_error{"Unexpected error!"};
122 }
123 auto y_lower = y1 + (x - x1) * (y2 - y1) / (x2 - x1);
124
125 auto next_pt = cell[next_id(c_pt_id)];
126 auto c_pt = cell[c_pt_id];
127 x1 = next_pt.x();
128 y1 = next_pt.y();
129 x2 = c_pt.x();
130 y2 = c_pt.y();
131
132 if ((x2 - x1) < kEps) {
133 throw std::runtime_error{"Unexpected error!"};
134 }
135 auto y_upper = y1 + (x - x1) * (y2 - y1) / (x2 - x1);
136
137 int min_j = std::round(y_lower / resolution_);
138 min_j = min_j < 0 ? 0 : min_j;
139
140 int max_j = std::round(y_upper / resolution_);
141 max_j = max_j > map_size_.y() ? map_size_.y() : max_j;
142
143 for (int j = min_j; j < max_j; ++j) {
144 double y = j * resolution_ + resolution_ / 2.;
145 Point2 curr_pt(x, y);
146 auto map_val = map_->operator()(i, j);
147 evaluator_func(map_val, curr_pt);
148 }
149 }
150}
151
152void Voronoi::ComputeMassCentroid(VoronoiCell &vcell) {
153 vcell.SetZero();
154 if (compute_single_ == true) {
155 vcell.origin_shift = origin_shift_;
156 }
157
158 auto fp = std::bind(&VoronoiCell::MassCentroidFunctional, &vcell,
159 std::placeholders::_1, std::placeholders::_2);
160 CellNavigator(vcell, fp);
161 vcell.ComputeFinalCentroid();
162 /* auto fp1 = std::bind(&VoronoiCell::GoalObjFunctional, &vcell,
163 * std::placeholders::_1, std::placeholders::_2); */
164 /* CellNavigator(vcell, fp1); */
165}
166
168 if (num_sites_ == 1) {
169 VoronoiCell vcell;
170 vcell.site = sites_[0];
171 vcell.cell = PointVector{Point2{0, 0}, Point2{map_size_.x(), 0},
172 Point2{map_size_.x(), map_size_.y()},
173 Point2{0, map_size_.y()}};
174 ComputeMassCentroid(vcell);
175 if (compute_single_ == true) {
176 voronoi_cell_ = vcell;
177 } else {
178 voronoi_cells_[0] = vcell;
179 }
180 return;
181 }
182
183 /* std::cout << "d2 begin" << std::endl; */
185 std::vector<CGAL_Point2> CGAL_sites;
186 CGAL_sites.reserve(num_sites_);
187 /* std::cout << "Number of sites: " << sites_.size() << std::endl; */
188 for (auto const &pt : sites_) {
189 CGAL_sites.push_back(CGAL_Point2(pt.x(), pt.y()));
190 }
191 dt2.insert(CGAL_sites.begin(), CGAL_sites.end());
192
194 dt2.draw_dual(vor);
195 /* std::cout << "d2 end" << std::endl; */
196
197 /* std::cout << "map size" << std::endl; */
198 /* std::cout << map_size_.x() << " " << map_size_.y() << std::endl; */
199 vor.segments_.push_back(
200 Segment_2(CGAL_Point2(0, 0), CGAL_Point2(map_size_.x(), 0)));
201 vor.segments_.push_back(Segment_2(CGAL_Point2(map_size_.x(), 0),
202 CGAL_Point2(map_size_.x(), map_size_.y())));
203 vor.segments_.push_back(Segment_2(CGAL_Point2(map_size_.x(), map_size_.y()),
204 CGAL_Point2(0, map_size_.y())));
205 vor.segments_.push_back(
206 Segment_2(CGAL_Point2(0, map_size_.y()), CGAL_Point2(0, 0)));
207
208 Arrangement_2 arr;
209 for (auto const &seg : vor.segments_) {
210 /* std::cout << seg << std::endl; */
211 if (seg.is_degenerate()) {
212 continue;
213 }
214 CGAL::insert(arr, seg);
215 }
216 /* std::cout << "segments pushed" << std::endl; */
217
218 CGAL::insert(arr, vor.rays_.begin(), vor.rays_.end());
219 /* for (auto const &ray : vor.rays_) { */
220 /* if (ray.is_degenerate()) { */
221 /* continue; */
222 /* } */
223 /* CGAL::insert(arr, ray); */
224 /* } */
225 /* std::cout << "rays inserted" << std::endl; */
226 CGAL::insert(arr, vor.lines_.begin(), vor.lines_.end());
227 /* for (auto const &line : vor.lines_) { */
228 /* if (line.is_degenerate()) { */
229 /* continue; */
230 /* } */
231 /* CGAL::insert(arr, line); */
232 /* } */
233 /* std::cout << "lines inserted" << std::endl; */
234 /* CGAL::insert(arr, vor.segments_.begin(), vor.segments_.end()); */
235 /* std::cout << "arr end" << std::endl; */
236 // CGAL_pl cgal_pl(arr);
237 /* std::cout << "cgal_pl end" << std::endl; */
238
239 std::vector<CGAL_Query_result> query_results_vor, query_results_vor_sorted;
240 if (compute_single_ == true) {
241 auto pt = CGAL_sites[robot_id_];
242 std::list<CGAL_Point2> temp_site; temp_site.push_back(pt);
243 CGAL::locate(arr, temp_site.begin(), temp_site.end(), std::back_inserter(query_results_vor));
244 Polygon_2 polygon;
245 const Arrangement_2::Face_const_handle* f;
246 if ((f = std::get_if<Arrangement_2::Face_const_handle>(&query_results_vor[0].second))) {
247 if((*f)->is_unbounded()) {
248 throw std::runtime_error{"inside the unbounded face."};
249 }
250 } else {
251 throw std::runtime_error{"Invalid object."};
252 }
253 CGAL_CCBTraversal<Arrangement_2>((*f)->outer_ccb(), polygon);
254 if (not polygon.is_counterclockwise_oriented()) {
255 polygon.reverse_orientation();
256 }
257 PointVector poly_points;
258 for (auto const &p : (polygon)) {
259 poly_points.push_back(CGALtoCC(p));
260 }
261 voronoi_cell_.site = CGALtoCC(pt);
262 voronoi_cell_.cell = poly_points;
263
264 ComputeMassCentroid(voronoi_cell_);
265 return;
266 }
267
268 /* std::list <Polygon_2> polygon_list; */
269 /* CGAL_GeneratePolygons(arr, polygon_list); */
270
271 /* PrunePolygons(polygon_list, map_size_); */
272 // Create voronoi_cells_ such that the correct cell is assigned to the robot
273 /* std::cout << "Before parallel for" << std::endl; */
274 CGAL::locate(arr, CGAL_sites.begin(), CGAL_sites.end(), std::back_inserter(query_results_vor));
275 /* Results (point, object) are in xy-lexicographic order */
276 /* Need to sort the results to match the order of the sites */
277 for (int i = 0; i < num_sites_; ++i) {
278
279 auto it = std::find_if(query_results_vor.begin(), query_results_vor.end(),
280 [i, CGAL_sites](CGAL_Query_result const &qr) {
281 return qr.first == CGAL_sites[i];
282 });
283 if (it == query_results_vor.end()) {
284 throw std::runtime_error{"Could not find a query result"};
285 }
286 query_results_vor_sorted.push_back(*it);
287 }
288
289#pragma omp parallel for num_threads(num_sites_)
290 for (int iSite = 0; iSite < num_sites_; ++iSite) {
291 const Arrangement_2::Face_const_handle* f;
292 if ((f = std::get_if<Arrangement_2::Face_const_handle>(&query_results_vor_sorted[iSite].second))) {
293 if((*f)->is_unbounded()) {
294 throw std::runtime_error{"inside the unbounded face."};
295 }
296 } else {
297 throw std::runtime_error{"Invalid object."};
298 }
299 /* CGAL_CCBTraversal<Arrangement_2> ((*f)->outer_ccb(), polygon); */
300 Polygon_2 polygon;
301 typename Arrangement_2::Ccb_halfedge_const_circulator circ =
302 (*f)->outer_ccb();
303 typename Arrangement_2::Ccb_halfedge_const_circulator curr = circ;
304 auto curr_pt = curr->source()->point();
305 do {
306 auto he = curr;
307 curr_pt = he->target()->point();
308 polygon.push_back(curr_pt);
309 ++curr;
310 } while (curr != circ);
311
312 if (polygon.size() == 0) {
313 throw std::runtime_error{"Zero size polygon"};
314 }
315 /* if (not polygon.is_counterclockwise_oriented()) { */
316 /* polygon.reverse_orientation(); */
317 /* } */
318 VoronoiCell vcell;
319 vcell.site = CGALtoCC(CGAL_sites[iSite]);
320 vcell.cell.reserve(polygon.size());
321 for (auto const &p : polygon) {
322 vcell.cell.push_back(CGALtoCC(p));
323 }
324 ComputeMassCentroid(vcell);
325 voronoi_cells_[iSite] = vcell;
326 }
327}
328
329} /* namespace CoverageControl */
Contains the configuration for the CGAL library.
CGAL::Arrangement_2< Traits_2 > Arrangement_2
Definition config.h:61
CGAL::Polygon_2< K > Polygon_2
Definition config.h:57
K::Segment_2 Segment_2
Definition config.h:52
std::pair< CGAL_Point2, CGAL_Point_location_result::Type > CGAL_Query_result
Definition config.h:70
CGAL::Delaunay_triangulation_2< K > Delaunay_triangulation_2
Definition config.h:55
K::Point_2 CGAL_Point2
Definition config.h:50
Constants for the CoverageControl library.
double const kEps
Definition constants.h:48
std::vector< Point2 > PointVector
Definition typedefs.h:51
Eigen::Vector2d Point2
Definition typedefs.h:44
Namespace for the CoverageControl library.
void CGAL_CCBTraversal(typename Arrangement::Ccb_halfedge_const_circulator circ, Polygon_2 &polygon)
Definition utilities.h:58
Point2 CGALtoCC(CGAL_Point2 const pt)
Definition utilities.h:44
std::vector< Line_2 > lines_
Definition utilities.h:49
std::vector< Segment_2 > segments_
Definition utilities.h:50
Struct for Voronoi cell.
Definition voronoi.h:52
void MassCentroidFunctional(double const &map_val, Point2 pt)
Definition voronoi.h:89
Contains utility functions for CGAL.
Class for computing Voronoi cells.