polygons.cpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2018, Locus Robotics
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <nav_2d_utils/polygons.h>
37 #include <algorithm>
38 #include <limits>
39 #include <string>
40 #include <vector>
41 
42 namespace nav_2d_utils
43 {
44 
45 using nav_2d_msgs::Point2D;
46 using nav_2d_msgs::Polygon2D;
47 
48 std::vector<std::vector<double> > parseVVD(const std::string& input)
49 {
50  std::vector<std::vector<double> > result;
51 
52  std::stringstream input_ss(input);
53  int depth = 0;
54  std::vector<double> current_vector;
55  while (input_ss.good())
56  {
57  switch (input_ss.peek())
58  {
59  case EOF:
60  break;
61  case '[':
62  depth++;
63  if (depth > 2)
64  {
65  throw PolygonParseException("Array depth greater than 2");
66  }
67  input_ss.get();
68  current_vector.clear();
69  break;
70  case ']':
71  depth--;
72  if (depth < 0)
73  {
74  throw PolygonParseException("More close ] than open [");
75  }
76  input_ss.get();
77  if (depth == 1)
78  {
79  result.push_back(current_vector);
80  }
81  break;
82  case ',':
83  case ' ':
84  case '\t':
85  input_ss.get();
86  break;
87  default: // All other characters should be part of the numbers.
88  if (depth != 2)
89  {
90  std::stringstream err_ss;
91  err_ss << "Numbers at depth other than 2. Char was '" << char(input_ss.peek()) << "'.";
92  throw PolygonParseException(err_ss.str());
93  }
94  double value;
95  if (input_ss >> value)
96  {
97  current_vector.push_back(value);
98  }
99  break;
100  }
101  }
102 
103  if (depth != 0)
104  {
105  throw PolygonParseException("Unterminated vector string.");
106  }
107 
108  return result;
109 }
110 
111 Polygon2D polygonFromRadius(const double radius, const unsigned int num_points)
112 {
113  Polygon2D polygon;
114  Point2D pt;
115  for (unsigned int i = 0; i < num_points; ++i)
116  {
117  double angle = i * 2 * M_PI / num_points;
118  pt.x = cos(angle) * radius;
119  pt.y = sin(angle) * radius;
120  polygon.points.push_back(pt);
121  }
122 
123  return polygon;
124 }
125 
126 Polygon2D polygonFromString(const std::string& polygon_string)
127 {
128  Polygon2D polygon;
129  // Will throw PolygonParseException if problematic
130  std::vector<std::vector<double> > vvd = parseVVD(polygon_string);
131 
132  // convert vvd into points.
133  if (vvd.size() < 3)
134  {
135  throw PolygonParseException("You must specify at least three points for the polygon.");
136  }
137 
138  polygon.points.resize(vvd.size());
139  for (unsigned int i = 0; i < vvd.size(); i++)
140  {
141  if (vvd[ i ].size() != 2)
142  {
143  std::stringstream err_ss;
144  err_ss << "Points in the polygon specification must be pairs of numbers. Point index " << i << " had ";
145  err_ss << int(vvd[ i ].size()) << " numbers.";
146  throw PolygonParseException(err_ss.str());
147  }
148 
149  polygon.points[i].x = vvd[i][0];
150  polygon.points[i].y = vvd[i][1];
151  }
152 
153  return polygon;
154 }
155 
156 
161 {
162  if (value.getType() == XmlRpc::XmlRpcValue::TypeInt)
163  {
164  return static_cast<double>(static_cast<int>(value));
165  }
166  else if (value.getType() == XmlRpc::XmlRpcValue::TypeDouble)
167  {
168  return static_cast<double>(value);
169  }
170 
171  std::stringstream err_ss;
172  err_ss << "Values in the polygon specification must be numbers. Found value: " << value.toXml();
173  throw PolygonParseException(err_ss.str());
174 }
175 
180 {
182  {
183  throw PolygonParseException("Subarray must have type list.");
184  }
185  std::vector<double> array;
186  for (int i = 0; i < value.size(); i++)
187  {
188  array.push_back(getNumberFromXMLRPC(value[i]));
189  }
190  return array;
191 }
192 
193 Polygon2D polygonFromXMLRPC(XmlRpc::XmlRpcValue& polygon_xmlrpc)
194 {
195  if (polygon_xmlrpc.getType() == XmlRpc::XmlRpcValue::TypeString &&
196  polygon_xmlrpc != "" && polygon_xmlrpc != "[]")
197  {
198  return polygonFromString(std::string(polygon_xmlrpc));
199  }
200 
201  if (polygon_xmlrpc.getType() == XmlRpc::XmlRpcValue::TypeStruct)
202  {
203  if (!polygon_xmlrpc.hasMember("x") || !polygon_xmlrpc.hasMember("y"))
204  {
205  throw PolygonParseException("Dict-like Polygon must specify members x and y.");
206  }
207  std::vector<double> xs = getNumberVectorFromXMLRPC(polygon_xmlrpc["x"]);
208  std::vector<double> ys = getNumberVectorFromXMLRPC(polygon_xmlrpc["y"]);
209  return polygonFromParallelArrays(xs, ys);
210  }
211 
212  // Make sure we have an array of at least 3 elements.
213  if (polygon_xmlrpc.getType() != XmlRpc::XmlRpcValue::TypeArray)
214  {
215  std::stringstream err_ss;
216  err_ss << "Polygon must be specified as a list of lists. Found object of type " << polygon_xmlrpc.getType()
217  << " instead.";
218  throw PolygonParseException(err_ss.str());
219  }
220  else if (polygon_xmlrpc.size() < 3)
221  {
222  throw PolygonParseException("You must specify at least three points for the polygon.");
223  }
224 
225  Polygon2D polygon;
226  Point2D pt;
227  for (int i = 0; i < polygon_xmlrpc.size(); ++i)
228  {
229  // Make sure each element of the list is an array of size 2. (x and y coordinates)
230  XmlRpc::XmlRpcValue& point_xml = polygon_xmlrpc[i];
231  if (point_xml.getType() != XmlRpc::XmlRpcValue::TypeArray)
232  {
233  std::stringstream err_ss;
234  err_ss << "Each point must be specified as a list. Found object of type " << point_xml.getType() << " instead.";
235  throw PolygonParseException(err_ss.str());
236  }
237  else if (point_xml.size() != 2)
238  {
239  throw PolygonParseException("Each point must have two numbers (x and y).");
240  }
241 
242  pt.x = getNumberFromXMLRPC(point_xml[0]);
243  pt.y = getNumberFromXMLRPC(point_xml[1]);
244  polygon.points.push_back(pt);
245  }
246  return polygon;
247 }
248 
249 Polygon2D polygonFromParams(const ros::NodeHandle& nh, const std::string parameter_name, bool search)
250 {
251  std::string full_param_name;
252  if (search)
253  {
254  nh.searchParam(parameter_name, full_param_name);
255  }
256  else
257  {
258  full_param_name = parameter_name;
259  }
260 
261  if (!nh.hasParam(full_param_name))
262  {
263  std::stringstream err_ss;
264  err_ss << "Parameter " << parameter_name << "(" + nh.resolveName(parameter_name) << ") not found.";
265  throw PolygonParseException(err_ss.str());
266  }
267  XmlRpc::XmlRpcValue polygon_xmlrpc;
268  nh.getParam(full_param_name, polygon_xmlrpc);
269  return polygonFromXMLRPC(polygon_xmlrpc);
270 }
271 
275 XmlRpc::XmlRpcValue vectorToXMLRPC(const std::vector<double>& array)
276 {
278  xml.setSize(array.size());
279  for (unsigned int i = 0; i < array.size(); ++i)
280  {
281  xml[i] = array[i];
282  }
283  return xml;
284 }
285 
286 XmlRpc::XmlRpcValue polygonToXMLRPC(const nav_2d_msgs::Polygon2D& polygon, bool array_of_arrays)
287 {
289  if (array_of_arrays)
290  {
291  xml.setSize(polygon.points.size());
292  for (unsigned int i = 0; i < polygon.points.size(); ++i)
293  {
294  xml[i].setSize(2);
295  const Point2D& p = polygon.points[i];
296  xml[i][0] = p.x;
297  xml[i][1] = p.y;
298  }
299  }
300  else
301  {
302  std::vector<double> xs, ys;
303  polygonToParallelArrays(polygon, xs, ys);
304  xml["x"] = vectorToXMLRPC(xs);
305  xml["y"] = vectorToXMLRPC(ys);
306  }
307  return xml;
308 }
309 
310 void polygonToParams(const nav_2d_msgs::Polygon2D& polygon, const ros::NodeHandle& nh, const std::string parameter_name,
311  bool array_of_arrays)
312 {
313  nh.setParam(parameter_name, polygonToXMLRPC(polygon, array_of_arrays));
314 }
315 
316 nav_2d_msgs::Polygon2D polygonFromParallelArrays(const std::vector<double>& xs, const std::vector<double>& ys)
317 {
318  if (xs.size() < 3)
319  {
320  throw PolygonParseException("You must specify at least three points for the polygon.");
321  }
322  else if (xs.size() != ys.size())
323  {
324  throw PolygonParseException("Length of x array does not match length of y array.");
325  }
326 
327  Polygon2D polygon;
328  polygon.points.resize(xs.size());
329  for (unsigned int i = 0; i < xs.size(); i++)
330  {
331  Point2D& pt = polygon.points[i];
332  pt.x = xs[i];
333  pt.y = ys[i];
334  }
335  return polygon;
336 }
337 
338 void polygonToParallelArrays(const nav_2d_msgs::Polygon2D polygon, std::vector<double>& xs, std::vector<double>& ys)
339 {
340  xs.clear();
341  ys.clear();
342  for (const Point2D& pt : polygon.points)
343  {
344  xs.push_back(pt.x);
345  ys.push_back(pt.y);
346  }
347 }
348 
349 bool equals(const nav_2d_msgs::Polygon2D& polygon0, const nav_2d_msgs::Polygon2D& polygon1)
350 {
351  if (polygon0.points.size() != polygon1.points.size())
352  {
353  return false;
354  }
355  for (unsigned int i=0; i < polygon0.points.size(); i++)
356  {
357  if (polygon0.points[i].x != polygon1.points[i].x ||polygon0.points[i].y != polygon1.points[i].y)
358  {
359  return false;
360  }
361  }
362  return true;
363 }
364 
365 nav_2d_msgs::Polygon2D movePolygonToPose(const nav_2d_msgs::Polygon2D& polygon,
366  const geometry_msgs::Pose2D& pose)
367 {
368  nav_2d_msgs::Polygon2D new_polygon;
369  new_polygon.points.resize(polygon.points.size());
370  double cos_th = cos(pose.theta);
371  double sin_th = sin(pose.theta);
372  for (unsigned int i = 0; i < polygon.points.size(); ++i)
373  {
374  nav_2d_msgs::Point2D& new_pt = new_polygon.points[i];
375  new_pt.x = pose.x + polygon.points[i].x * cos_th - polygon.points[i].y * sin_th;
376  new_pt.y = pose.y + polygon.points[i].x * sin_th + polygon.points[i].y * cos_th;
377  }
378  return new_polygon;
379 }
380 
381 bool isInside(const nav_2d_msgs::Polygon2D& polygon, const double x, const double y)
382 {
383  // Determine if the given point is inside the polygon using the number of crossings method
384  // https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
385  int n = polygon.points.size();
386  int cross = 0;
387  // Loop from i = [0 ... n - 1] and j = [n - 1, 0 ... n - 2]
388  // Ensures first point connects to last point
389  for (int i = 0, j = n - 1; i < n; j = i++)
390  {
391  // Check if the line to x,y crosses this edge
392  if ( ((polygon.points[i].y > y) != (polygon.points[j].y > y))
393  && (x < (polygon.points[j].x - polygon.points[i].x) * (y - polygon.points[i].y) /
394  (polygon.points[j].y - polygon.points[i].y) + polygon.points[i].x) )
395  {
396  cross++;
397  }
398  }
399  // Return true if the number of crossings is odd
400  return cross % 2 > 0;
401 }
402 
403 void calculateMinAndMaxDistances(const nav_2d_msgs::Polygon2D& polygon, double& min_dist, double& max_dist)
404 {
405  min_dist = std::numeric_limits<double>::max();
406  max_dist = 0.0;
407  auto& points = polygon.points;
408  if (points.size() == 0)
409  {
410  return;
411  }
412 
413  for (unsigned int i = 0; i < points.size() - 1; ++i)
414  {
415  // check the distance from the robot center point to the first vertex
416  double vertex_dist = hypot(points[i].x, points[i].y);
417  double edge_dist = distanceToLine(0.0, 0.0, points[i].x, points[i].y,
418  points[i + 1].x, points[i + 1].y);
419  min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
420  max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
421  }
422 
423  // we also need to do the last vertex and the first vertex
424  double vertex_dist = hypot(points.back().x, points.back().y);
425  double edge_dist = distanceToLine(0.0, 0.0, points.back().x, points.back().y,
426  points.front().x, points.front().y);
427  min_dist = std::min(min_dist, std::min(vertex_dist, edge_dist));
428  max_dist = std::max(max_dist, std::max(vertex_dist, edge_dist));
429 }
430 
431 } // namespace nav_2d_utils
double distanceToLine(double pX, double pY, double x0, double y0, double x1, double y1)
Distance from point (pX, pY) to closest point on line from (x0, y0) to (x1, y1)
Definition: geometry_help.h:52
nav_2d_msgs::Polygon2D polygonFromXMLRPC(XmlRpc::XmlRpcValue &polygon_xmlrpc)
Create a polygon from the given XmlRpcValue.
Definition: polygons.cpp:193
std::vector< std::vector< double > > parseVVD(const std::string &input)
Parse a vector of vectors of doubles from a string. Syntax is [[1.0, 2.0], [3.3, 4.4, 5.5], ...].
Definition: polygons.cpp:48
nav_2d_msgs::Polygon2D polygonFromParams(const ros::NodeHandle &nh, const std::string parameter_name, bool search=true)
Load a polygon from a parameter, whether it is a string or array, or two arrays.
Definition: polygons.cpp:249
bool equals(const nav_2d_msgs::Polygon2D &polygon0, const nav_2d_msgs::Polygon2D &polygon1)
check if two polygons are equal. Used in testing
Definition: polygons.cpp:349
TFSIMD_FORCE_INLINE Vector3 cross(const Vector3 &v) const
XmlRpc::XmlRpcValue vectorToXMLRPC(const std::vector< double > &array)
Helper method to convert a vector of doubles.
Definition: polygons.cpp:275
int size() const
std::string resolveName(const std::string &name, bool remap=true) const
Exception to throw when Polygon doesn&#39;t load correctly.
Definition: polygons.h:51
Type const & getType() const
TFSIMD_FORCE_INLINE const tfScalar & y() const
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
double getNumberFromXMLRPC(XmlRpc::XmlRpcValue &value)
Helper function. Convert value to double.
Definition: polygons.cpp:160
void polygonToParallelArrays(const nav_2d_msgs::Polygon2D polygon, std::vector< double > &xs, std::vector< double > &ys)
Create two parallel arrays from a polygon.
Definition: polygons.cpp:338
std::string toXml() const
void setSize(int size)
nav_2d_msgs::Polygon2D polygonFromRadius(const double radius, const unsigned int num_points=16)
Create a "circular" polygon from a given radius.
Definition: polygons.cpp:111
std::vector< double > getNumberVectorFromXMLRPC(XmlRpc::XmlRpcValue &value)
Helper function. Convert value to double array.
Definition: polygons.cpp:179
bool searchParam(const std::string &key, std::string &result) const
void polygonToParams(const nav_2d_msgs::Polygon2D &polygon, const ros::NodeHandle &nh, const std::string parameter_name, bool array_of_arrays=true)
Save a polygon to a parameter.
Definition: polygons.cpp:310
TFSIMD_FORCE_INLINE const tfScalar & x() const
nav_2d_msgs::Polygon2D polygonFromParallelArrays(const std::vector< double > &xs, const std::vector< double > &ys)
Create a polygon from two parallel arrays.
Definition: polygons.cpp:316
bool hasMember(const std::string &name) const
bool getParam(const std::string &key, std::string &s) const
void calculateMinAndMaxDistances(const nav_2d_msgs::Polygon2D &polygon, double &min_dist, double &max_dist)
Calculate the minimum and maximum distance from (0, 0) to any point on the polygon.
Definition: polygons.cpp:403
bool hasParam(const std::string &key) const
nav_2d_msgs::Polygon2D polygonFromString(const std::string &polygon_string)
Make a polygon from the given string. Format should be bracketed array of arrays of doubles...
Definition: polygons.cpp:126
nav_2d_msgs::Polygon2D movePolygonToPose(const nav_2d_msgs::Polygon2D &polygon, const geometry_msgs::Pose2D &pose)
Translate and rotate a polygon to a new pose.
Definition: polygons.cpp:365
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
XmlRpc::XmlRpcValue polygonToXMLRPC(const nav_2d_msgs::Polygon2D &polygon, bool array_of_arrays=true)
Create XMLRPC Value for writing the polygon to the parameter server.
Definition: polygons.cpp:286
bool isInside(const nav_2d_msgs::Polygon2D &polygon, const double x, const double y)
Check if a given point is inside of a polygon.
Definition: polygons.cpp:381


nav_2d_utils
Author(s):
autogenerated on Wed Jun 26 2019 20:06:11