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#include <list>
34
38
39namespace CoverageControl {
40
41void VoronoiCell::ComputeFinalCentroid() {
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
167void Voronoi::ComputeVoronoiCells() {
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; */
184 Delaunay_triangulation_2 dt2;
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
193 CGAL_DelaunayHelper vor;
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 if (compute_single_ == true) {
240 auto pt = CGAL_sites[robot_id_];
241 Polygon_2 polygon;
242 auto pt_obj = cgal_pl.locate(pt);
243 auto f = boost::get<Arrangement_2::Face_const_handle>(&pt_obj);
244 CGAL_CCBTraversal<Arrangement_2>((*f)->outer_ccb(), polygon);
245 if (not polygon.is_counterclockwise_oriented()) {
246 polygon.reverse_orientation();
247 }
248 PointVector poly_points;
249 for (auto const &p : (polygon)) {
250 poly_points.push_back(CGALtoCC(p));
251 }
252 voronoi_cell_.site = CGALtoCC(pt);
253 voronoi_cell_.cell = poly_points;
254
255 ComputeMassCentroid(voronoi_cell_);
256 return;
257 }
258
259 /* std::list <Polygon_2> polygon_list; */
260 /* CGAL_GeneratePolygons(arr, polygon_list); */
261
262 /* PrunePolygons(polygon_list, map_size_); */
263 // Create voronoi_cells_ such that the correct cell is assigned to the robot
264 /* std::cout << "Before parallel for" << std::endl; */
265#pragma omp parallel for num_threads(num_sites_)
266 for (int iSite = 0; iSite < num_sites_; ++iSite) {
267 auto pt = CGAL_sites[iSite];
268 auto pt_obj = cgal_pl.locate(pt);
269 auto *f = boost::get<Arrangement_2::Face_const_handle>(&pt_obj);
270 if (not f) {
271 std::cout << pt << std::endl;
272 throw std::runtime_error{"Could not find a face for the robot"};
273 }
274 /* CGAL_CCBTraversal<Arrangement_2> ((*f)->outer_ccb(), polygon); */
275 Polygon_2 polygon;
276 typename Arrangement_2::Ccb_halfedge_const_circulator circ =
277 (*f)->outer_ccb();
278 typename Arrangement_2::Ccb_halfedge_const_circulator curr = circ;
279 auto curr_pt = curr->source()->point();
280 do {
281 auto he = curr;
282 curr_pt = he->target()->point();
283 polygon.push_back(curr_pt);
284 ++curr;
285 } while (curr != circ);
286
287 if (polygon.size() == 0) {
288 throw std::runtime_error{"Zero size polygon"};
289 }
290 /* if (not polygon.is_counterclockwise_oriented()) { */
291 /* polygon.reverse_orientation(); */
292 /* } */
293 VoronoiCell vcell;
294 vcell.site = CGALtoCC(pt);
295 vcell.cell.reserve(polygon.size());
296 for (auto const &p : polygon) {
297 vcell.cell.push_back(CGALtoCC(p));
298 }
299 ComputeMassCentroid(vcell);
300 voronoi_cells_[iSite] = vcell;
301 }
302}
303
304} /* namespace CoverageControl */
Contains the configuration for the CGAL library.
double const kEps
Definition constants.h:49
Namespace for the CoverageControl library.
Contains utility functions for CGAL.
Class for computing Voronoi cells.