UAV Coverage Path Planner
cgutil.hpp
Go to the documentation of this file.
1 
7 /*
8  * Copyright (c) 2017 Takaki Ueno
9  * Released under the MIT license
10  */
11 
12 #ifndef INCLUDED_cgutil_hpp_
13 #define INCLUDED_cgutil_hpp_
14 
15 // c++ libraries
16 #include <algorithm>
17 #include <cmath>
18 #include <list>
19 #include <stack>
20 #include <vector>
21 
22 // CGAL
23 #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
24 #include <CGAL/Partition_is_valid_traits_2.h>
25 #include <CGAL/Partition_traits_2.h>
26 #include <CGAL/partition_2.h>
27 #include <CGAL/point_generators_2.h>
28 #include <CGAL/polygon_function_objects.h>
29 #include <CGAL/random_polygon_2.h>
30 
31 // roscpp
32 #include <ros/ros.h>
33 
34 // geometry_msgs
35 #include <geometry_msgs/Point.h>
36 
37 using PointVector = std::vector<geometry_msgs::Point>;
38 using LineSegment = std::array<geometry_msgs::Point, 2>;
39 using LineSegmentVector = std::vector<LineSegment>;
40 
41 // type aliases for cgal
42 using K = CGAL::Exact_predicates_inexact_constructions_kernel;
43 using Traits = CGAL::Partition_traits_2<K>;
46 using Vertex_iterator = Polygon_2::Vertex_const_iterator;
47 using Polygon_list = std::list<Polygon_2>;
48 
74 inline double calculateSignedArea(const geometry_msgs::Point& p1, const geometry_msgs::Point& p2,
75  const geometry_msgs::Point& p3)
76 {
77  return p1.x * (p2.y - p3.y) - p1.y * (p2.x - p3.x) + (p2.x * p3.y - p2.y * p3.x);
78 }
79 
87 bool operator==(const geometry_msgs::Point& p1, const geometry_msgs::Point& p2)
88 {
89  bool x =
90  p1.x == p2.x || std::abs(p1.x - p2.x) < std::abs(std::min(p1.x, p2.x)) * std::numeric_limits<double>::epsilon();
91  bool y =
92  p1.y == p2.y || std::abs(p1.y - p2.y) < std::abs(std::min(p1.y, p2.y)) * std::numeric_limits<double>::epsilon();
93  bool z =
94  p1.z == p2.z || std::abs(p1.z - p2.z) < std::abs(std::min(p1.z, p2.z)) * std::numeric_limits<double>::epsilon();
95 
96  return x and y and z;
97 }
98 
105 bool operator!=(const geometry_msgs::Point& p1, const geometry_msgs::Point& p2)
106 {
107  return !(p1 == p2);
108 }
109 
117 {
118  LineSegmentVector edgeVector;
119 
120  // break if vector is empty
121  if (vec.empty() == true)
122  {
123  return edgeVector;
124  }
125 
126  for (int i = 0; i < vec.size(); ++i)
127  {
128  LineSegment edge;
129 
130  edge.at(0) = vec.at(i);
131 
132  if (i < vec.size() - 1)
133  {
134  // except for the last vertex
135  edge.at(1) = vec.at(i + 1);
136  }
137  else
138  {
139  // for the last vertex
140  edge.at(1) = vec.at(0);
141  if (not isClosed)
142  {
143  break;
144  }
145  }
146 
147  edgeVector.push_back(edge);
148  }
149  return edgeVector;
150 }
151 
158 inline double calculateDistance(const geometry_msgs::Point& p1, const geometry_msgs::Point& p2)
159 {
160  return std::sqrt(std::pow(p1.x - p2.x, 2) + std::pow(p1.y - p2.y, 2));
161 }
162 
170 double calculateVertexAngle(const geometry_msgs::Point& p1, const geometry_msgs::Point& p2,
171  const geometry_msgs::Point& p3)
172 {
173  // Length of edges composed of vertices
174  // e1: (p1, p2)
175  // e2: (p2, p3)
176  // e3: (p3, p1)
177  double lenE1, lenE2, lenE3;
178  lenE1 = calculateDistance(p2, p1);
179  lenE2 = calculateDistance(p2, p3);
180  lenE3 = calculateDistance(p3, p1);
181 
182  // Cosine of angle between segment p1p2 and p1p3 (Law of cosines)
183  double cosineP1;
184  cosineP1 = (std::pow(lenE1, 2) + std::pow(lenE3, 2) - std::pow(lenE2, 2)) / (2 * lenE1 * lenE3);
185 
186  // vertex angle is 0.0 if lenE1 or lenE3 is zero
187  // that means p1 and p2 or p1 and p3 is the same point
188  if (std::isnan(cosineP1) != 0)
189  {
190  return 0.0;
191  }
192 
193  return std::acos(cosineP1);
194 }
195 
202 double calculateHorizontalAngle(const geometry_msgs::Point& p1, const geometry_msgs::Point& p2)
203 {
204  geometry_msgs::Point p3;
205  p3.x = p1.x + 1.0;
206  p3.y = p1.y;
207  return p1.y >= p2.y ? -calculateVertexAngle(p1, p2, p3) : calculateVertexAngle(p1, p2, p3);
208 }
209 
216 double calculateDistance(const LineSegment& edge, const geometry_msgs::Point& vertex)
217 {
218  // Vertices of triangle
219  geometry_msgs::Point pointA, pointB;
220  pointA = edge.front();
221  pointB = edge.back();
222 
223  // Calculate length of each edge
224  // Edge A: An edge facing vertex A
225  // Edge B: An edge facing vertex B
226  double lenEdgeA, lenEdgeB;
227  lenEdgeA = calculateDistance(pointB, vertex);
228  lenEdgeB = calculateDistance(vertex, pointA);
229 
230  // Vertex angles
231  // alpha: vertex angle of pointA
232  // beta: vertex angle of pointB
233  double alpha, beta;
234  alpha = calculateVertexAngle(pointA, pointB, vertex);
235  beta = calculateVertexAngle(pointB, pointA, vertex);
236 
237  double distance = alpha < M_PI_2 ? std::sin(alpha) * lenEdgeB : std::sin(beta) * lenEdgeA;
238 
239  return distance;
240 }
241 
250 {
251  PointVector convexHull;
252 
253  if (points.empty() or points.size() < 3)
254  {
255  return convexHull;
256  }
257 
258  // remove points that have same coodinate with other points
259  for (size_t i = 0; i < points.size() - 1; ++i)
260  {
261  if (points.at(i).x == points.at(i + 1).x and points.at(i).y == points.at(i + 1).y)
262  {
263  points.erase(points.begin() + i);
264  }
265  }
266 
267  if (points.size() < 3)
268  {
269  return convexHull;
270  }
271 
272  // sort by vertex's y coordinate in an ascending order
273  std::stable_sort(points.begin(), points.end(),
274  [](const geometry_msgs::Point& p1, const geometry_msgs::Point& p2) { return p1.y < p2.y; });
275 
276  // point with minimum y coordinate
277  geometry_msgs::Point pointMinY = points.front();
278  points.erase(points.begin());
279 
280  // sort by an angle between a segment composed of pointMinY and pj (in a set
281  // of points) and horizontal line
282  // in an ascending order
283  std::stable_sort(points.begin(), points.end(), [&](const geometry_msgs::Point& p1, const geometry_msgs::Point& p2) {
284  return calculateHorizontalAngle(pointMinY, p1) < calculateHorizontalAngle(pointMinY, p2);
285  });
286 
287  // add pointMinY in convex hull
288  convexHull.push_back(pointMinY);
289 
290  // add the point with minimum angle
291  convexHull.push_back(points.front());
292  points.erase(points.begin());
293 
294  for (const auto& point : points)
295  {
296  for (std::size_t i = convexHull.size() - 1; i > 1; --i)
297  {
298  if (calculateSignedArea(convexHull.at(i - 1), convexHull.at(i), point) >= 0)
299  {
300  break;
301  }
302 
303  convexHull.pop_back();
304  }
305  convexHull.push_back(point);
306  }
307 
308  geometry_msgs::Point origin;
309  origin.x = 0;
310  origin.y = 0;
311  std::stable_sort(convexHull.begin(), convexHull.end(),
312  [&](const geometry_msgs::Point& p1, const geometry_msgs::Point& p2) {
313  return calculateHorizontalAngle(origin, p1) < calculateHorizontalAngle(origin, p2);
314  });
315 
316  return convexHull;
317 }
318 
324 inline bool isConvex(PointVector points)
325 {
326  return computeConvexHull(points).size() == points.size();
327 }
328 
335 bool hasIntersection(const LineSegment& edge1, const LineSegment& edge2)
336 {
337  geometry_msgs::Point p1, p2, p3, p4;
338 
339  try
340  {
341  p1 = edge1.at(0);
342  p2 = edge1.at(1);
343  p3 = edge2.at(0);
344  p4 = edge2.at(1);
345  }
346  catch (std::out_of_range& ex)
347  {
348  ROS_ERROR("%s", ex.what());
349  return false;
350  }
351 
352  bool condA = ((p1.x - p2.x) * (p3.y - p1.y) + (p1.y - p2.y) * (p1.x - p3.x)) *
353  ((p1.x - p2.x) * (p4.y - p1.y) + (p1.y - p2.y) * (p1.x - p4.x)) <
354  0;
355  bool condB = ((p3.x - p4.x) * (p1.y - p3.y) + (p3.y - p4.y) * (p3.x - p1.x)) *
356  ((p3.x - p4.x) * (p2.y - p3.y) + (p3.y - p4.y) * (p3.x - p2.x)) <
357  0;
358 
359  if (condA and condB)
360  {
361  return true;
362  }
363  else
364  {
365  return false;
366  }
367 }
368 
376 {
377  for (const auto& segment1 : vec1)
378  {
379  for (const auto& segment2 : vec2)
380  {
381  if (hasIntersection(segment1, segment2) == true)
382  {
383  return true;
384  }
385  }
386  }
387 
388  return false;
389 }
390 
398 geometry_msgs::Point localizeIntersection(const LineSegment& edge1, const LineSegment& edge2)
399 {
400  geometry_msgs::Point p1, p2, p3, p4;
401 
402  try
403  {
404  p1 = edge1.at(0);
405  p2 = edge1.at(1);
406  p3 = edge2.at(0);
407  p4 = edge2.at(1);
408  }
409  catch (const std::out_of_range& ex)
410  {
411  ROS_ERROR("%s", ex.what());
412  }
413 
414  double xi, eta, delta;
415  xi = (p4.y - p3.y) * (p4.x - p1.x) - (p4.x - p3.x) * (p4.y - p1.y);
416  eta = -(p2.y - p1.y) * (p4.x - p1.x) + (p2.x - p1.x) * (p4.y - p1.y);
417  delta = (p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y);
418 
419  double lambda, mu;
420  lambda = xi / delta;
421  mu = eta / delta;
422 
423  geometry_msgs::Point intersection;
424 
425  intersection.x = p1.x + lambda * (p2.x - p1.x);
426  intersection.y = p1.y + lambda * (p2.y - p1.y);
427 
428  return intersection;
429 }
430 
437 PointVector rotatePoints(const PointVector& points, double angle_rad)
438 {
439  std::array<double, 4> rotationMatrix;
440  rotationMatrix.at(0) = std::cos(angle_rad);
441  rotationMatrix.at(1) = -std::sin(angle_rad);
442  rotationMatrix.at(2) = std::sin(angle_rad);
443  rotationMatrix.at(3) = std::cos(angle_rad);
444 
445  PointVector rotatedPoints;
446 
447  for (const auto& point : points)
448  {
449  geometry_msgs::Point pt;
450  pt.x = rotationMatrix.at(0) * point.x + rotationMatrix.at(1) * point.y;
451  pt.y = rotationMatrix.at(2) * point.x + rotationMatrix.at(3) * point.y;
452  rotatedPoints.push_back(pt);
453  }
454  return rotatedPoints;
455 }
456 
468 std::vector<PointVector> decomposePolygon(const PointVector& polygon)
469 {
470  std::vector<PointVector> decomposedPolygons;
471 
472  // generate Polygon of CGAL from PointVector
473  Polygon_2 cgalPolygon;
474  for (const auto& vertex : polygon)
475  {
476  cgalPolygon.push_back(Point_2(vertex.x, vertex.y));
477  }
478 
479  Polygon_list partialCGALPolygons;
480  Traits partitionTraits;
481 
482  // note that this function has O(n^4) time complexity and O(n^3) space complexity
483  // use approx_convex_partition_2 instead if the number of vertices are big because its time complexity is O(n)
484  // but apptox_convex_partition_2 generates more polygons
485  CGAL::optimal_convex_partition_2(cgalPolygon.vertices_begin(), cgalPolygon.vertices_end(),
486  std::back_inserter(partialCGALPolygons), partitionTraits);
487 
488  // generate std::vector<PointVector> from polygon of CGAL
489  for (const auto& partialCGALPolygon : partialCGALPolygons)
490  {
491  PointVector partialPolygon;
492  for (auto itr = partialCGALPolygon.vertices_begin(); itr != partialCGALPolygon.vertices_end(); ++itr)
493  {
494  geometry_msgs::Point pt;
495  pt.x = itr->x();
496  pt.y = itr->y();
497  partialPolygon.push_back(pt);
498  }
499  decomposedPolygons.push_back(partialPolygon);
500  }
501 
502  return decomposedPolygons;
503 }
504 
505 #endif
double calculateVertexAngle(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2, const geometry_msgs::Point &p3)
Calculates angle between segment p1p2 and p1p3.
Definition: cgutil.hpp:170
std::array< geometry_msgs::Point, 2 > LineSegment
Definition: cgutil.hpp:38
double calculateHorizontalAngle(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Calculates angle between segment p1p2 and horizontal line.
Definition: cgutil.hpp:202
CGAL::Partition_traits_2< K > Traits
Definition: cgutil.hpp:43
bool operator!=(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Check equality of two points.
Definition: cgutil.hpp:105
LineSegmentVector generateEdgeVector(const PointVector &vec, bool isClosed)
Generate Vector of line segment from given PointVector.
Definition: cgutil.hpp:116
std::vector< LineSegment > LineSegmentVector
Definition: cgutil.hpp:39
double calculateSignedArea(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2, const geometry_msgs::Point &p3)
Calculates signed area of given triangle.
Definition: cgutil.hpp:74
double calculateDistance(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Calculates distance between given two points.
Definition: cgutil.hpp:158
PointVector rotatePoints(const PointVector &points, double angle_rad)
Rotate points by given angle around the origin.
Definition: cgutil.hpp:437
bool isConvex(PointVector points)
Checks if given polygon is convex or not.
Definition: cgutil.hpp:324
std::vector< geometry_msgs::Point > PointVector
Definition: cgutil.hpp:37
PointVector computeConvexHull(PointVector points)
Returns convex hull of given points.
Definition: cgutil.hpp:249
geometry_msgs::Point localizeIntersection(const LineSegment &edge1, const LineSegment &edge2)
Find the location where given edges intersect each other.
Definition: cgutil.hpp:398
Traits::Point_2 Point_2
Definition: cgutil.hpp:45
CGAL::Exact_predicates_inexact_constructions_kernel K
Definition: cgutil.hpp:42
std::vector< PointVector > decomposePolygon(const PointVector &polygon)
Decompose given polygon.
Definition: cgutil.hpp:468
bool hasIntersection(const LineSegment &edge1, const LineSegment &edge2)
Checks if given edges intersect each other.
Definition: cgutil.hpp:335
bool operator==(const geometry_msgs::Point &p1, const geometry_msgs::Point &p2)
Check equality of two points.
Definition: cgutil.hpp:87
std::list< Polygon_2 > Polygon_list
Definition: cgutil.hpp:47
Traits::Polygon_2 Polygon_2
Definition: cgutil.hpp:44
Polygon_2::Vertex_const_iterator Vertex_iterator
Definition: cgutil.hpp:46