TriangularDecomposition.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2012, Rice University
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 Rice University 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 OWNER 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 /* Author: Matt Maly */
36 
37 #include "ompl/extensions/triangle/TriangularDecomposition.h"
38 #include "ompl/base/State.h"
39 #include "ompl/base/StateSampler.h"
40 #include "ompl/base/spaces/RealVectorBounds.h"
41 #include "ompl/control/planners/syclop/Decomposition.h"
42 #include "ompl/control/planners/syclop/GridDecomposition.h"
43 #include "ompl/util/RandomNumbers.h"
44 #include "ompl/util/Hash.h"
45 #include <ostream>
46 #include <vector>
47 #include <set>
48 #include <string>
49 #include <unordered_map>
50 #include <cstdlib>
51 
52 extern "C"
53 {
54  #define REAL double
55  #define VOID void
56  #define ANSI_DECLARATORS
57  #include <triangle.h>
58 }
59 
60 namespace std
61 {
62  template<>
63  struct hash<ompl::control::TriangularDecomposition::Vertex>
64  {
65  size_t operator()(const ompl::control::TriangularDecomposition::Vertex &v) const
66  {
67  std::size_t hash = std::hash<double>()(v.x);
68  ompl::hash_combine(hash, v.y);
69  return hash;
70  }
71  };
72 }
73 
75  const std::vector<Polygon> &holes, const std::vector<Polygon> &intRegs) :
76  Decomposition(2, bounds),
77  holes_(holes),
78  intRegs_(intRegs),
79  triAreaPct_(0.005),
80  locator(64, this)
81 {
82  // \todo: Ensure that no two holes overlap and no two regions of interest overlap.
83  // Report an error otherwise.
84 }
85 
86 ompl::control::TriangularDecomposition::~TriangularDecomposition(void)
87 {
88 }
89 
90 void ompl::control::TriangularDecomposition::setup(void)
91 {
92  int numTriangles = createTriangles();
93  OMPL_INFORM("Created %u triangles", numTriangles);
94  buildLocatorGrid();
95 }
96 
97 void ompl::control::TriangularDecomposition::addHole(const Polygon& hole)
98 {
99  holes_.push_back(hole);
100 }
101 
102 void ompl::control::TriangularDecomposition::addRegionOfInterest(const Polygon& region)
103 {
104  intRegs_.push_back(region);
105 }
106 
107 int ompl::control::TriangularDecomposition::getNumHoles(void) const
108 {
109  return holes_.size();
110 }
111 
112 int ompl::control::TriangularDecomposition::getNumRegionsOfInterest(void) const
113 {
114  return intRegs_.size();
115 }
116 
117 const std::vector<ompl::control::TriangularDecomposition::Polygon>&
118  ompl::control::TriangularDecomposition::getHoles(void) const
119 {
120  return holes_;
121 }
122 
123 const std::vector<ompl::control::TriangularDecomposition::Polygon>&
124  ompl::control::TriangularDecomposition::getAreasOfInterest(void) const
125 {
126  return intRegs_;
127 }
128 
130 {
131  return intRegInfo_[triID];
132 }
133 
135 {
136  Triangle& tri = triangles_[triID];
137  if (tri.volume < 0)
138  {
139  /* This triangle area formula relies on the vertices being
140  * stored in counter-clockwise order. */
141  tri.volume = 0.5*(
142  (tri.pts[0].x-tri.pts[2].x)*(tri.pts[1].y-tri.pts[0].y)
143  - (tri.pts[0].x-tri.pts[1].x)*(tri.pts[2].y-tri.pts[0].y)
144  );
145  }
146  return tri.volume;
147 }
148 
149 void ompl::control::TriangularDecomposition::getNeighbors(int triID, std::vector<int> &neighbors) const
150 {
151  neighbors = triangles_[triID].neighbors;
152 }
153 
155 {
156  std::vector<double> coord(2);
157  project(s, coord);
158  const std::vector<int>& gridTriangles = locator.locateTriangles(s);
159  int triangle = -1;
160  for (std::vector<int>::const_iterator i = gridTriangles.begin(); i != gridTriangles.end(); ++i)
161  {
162  int triID = *i;
163  if (triContains(triangles_[triID], coord))
164  {
165  if (triangle >= 0)
166  OMPL_WARN("Decomposition space coordinate (%f,%f) is somehow contained by multiple triangles. \
167  This can happen if the coordinate is located exactly on a triangle segment.\n",
168  coord[0], coord[1]);
169  triangle = triID;
170  }
171  }
172  return triangle;
173 }
174 
175 void ompl::control::TriangularDecomposition::sampleFromRegion(int triID, RNG& rng, std::vector<double>& coord) const
176 {
177  /* Uniformly sample a point from within a triangle, using the approach discussed in
178  * http://math.stackexchange.com/questions/18686/uniform-random-point-in-triangle */
179  const Triangle& tri = triangles_[triID];
180  coord.resize(2);
181  const double r1 = sqrt(rng.uniform01());
182  const double r2 = rng.uniform01();
183  coord[0] = (1-r1)*tri.pts[0].x + r1*(1-r2)*tri.pts[1].x + r1*r2*tri.pts[2].x;
184  coord[1] = (1-r1)*tri.pts[0].y + r1*(1-r2)*tri.pts[1].y + r1*r2*tri.pts[2].y;
185 }
186 
187 void ompl::control::TriangularDecomposition::print(std::ostream& out) const
188 {
189  /* For each triangle, print a line of the form
190  N x1 y1 x2 y2 x3 y3 L1 L2 ... -1
191  N is the ID of the triangle
192  L1 L2 ... is the sequence of all regions of interest to which
193  this triangle belongs. */
194  for (unsigned int i = 0; i < triangles_.size(); ++i)
195  {
196  out << i << " ";
197  const Triangle& tri = triangles_[i];
198  for (int v = 0; v < 3; ++v)
199  out << tri.pts[v].x << " " << tri.pts[v].y << " ";
200  if (intRegInfo_[i] > -1) out << intRegInfo_[i] << " ";
201  out << "-1" << std::endl;
202  }
203 }
204 
205 ompl::control::TriangularDecomposition::Vertex::Vertex(double vx, double vy) : x(vx), y(vy)
206 {
207 }
208 
209 bool ompl::control::TriangularDecomposition::Vertex::operator==(const Vertex &v) const
210 {
211  return x == v.x && y == v.y;
212 }
213 
215 {
216  /* create a conforming Delaunay triangulation
217  where each triangle takes up no more than triAreaPct_ percentage of
218  the total area of the decomposition space */
219  const base::RealVectorBounds& bounds = getBounds();
220  const double maxTriangleArea = bounds.getVolume() * triAreaPct_;
221  std::string triswitches = "pDznQA -a" + std::to_string(maxTriangleArea);
222  struct triangulateio in;
223 
224  /* Some vertices may be duplicates, such as when an obstacle has a vertex equivalent
225  to one at the corner of the bounding box of the decomposition.
226  libtriangle does not perform correctly if points are duplicated in the pointlist;
227  so, to prevent duplicate vertices, we use a hashmap from Vertex to the index for
228  that Vertex in the pointlist. We'll fill the map with Vertex objects,
229  and then we'll actually add them to the pointlist. */
230  std::unordered_map<Vertex, int> pointIndex;
231 
232  // First, add the points from the bounding box
233  pointIndex[Vertex(bounds.low[0], bounds.low[1])] = 0;
234  pointIndex[Vertex(bounds.high[0], bounds.low[1])] = 1;
235  pointIndex[Vertex(bounds.high[0], bounds.high[1])] = 2;
236  pointIndex[Vertex(bounds.low[0], bounds.high[1])] = 3;
237 
238  /* in.numberofpoints equals the total number of unique vertices.
239  in.numberofsegments is slightly different: it equals the total number of given vertices.
240  They will both be at least 4, due to the bounding box. */
241  in.numberofpoints = 4;
242  in.numberofsegments = 4;
243 
244  typedef std::vector<Polygon>::const_iterator PolyIter;
245  typedef std::vector<Vertex>::const_iterator VertexIter;
246 
247  //Run through obstacle vertices in holes_, and tally point and segment counters
248  for (PolyIter p = holes_.begin(); p != holes_.end(); ++p)
249  {
250  for (VertexIter v = p->pts.begin(); v != p->pts.end(); ++v)
251  {
252  ++in.numberofsegments;
253  /* Only assign an index to this vertex (and tally the point counter)
254  if this is a newly discovered vertex. */
255  if (pointIndex.find(*v) == pointIndex.end())
256  pointIndex[*v] = in.numberofpoints++;
257  }
258  }
259 
260  /* Run through region-of-interest vertices in intRegs_, and tally point and segment counters.
261  Here we're following the same logic as above with holes_. */
262  for (PolyIter p = intRegs_.begin(); p != intRegs_.end(); ++p)
263  {
264  for (VertexIter v = p->pts.begin(); v != p->pts.end(); ++v)
265  {
266  ++in.numberofsegments;
267  if (pointIndex.find(*v) == pointIndex.end())
268  pointIndex[*v] = in.numberofpoints++;
269  }
270  }
271 
272  //in.pointlist is a sequence (x1 y1 x2 y2 ...) of ordered pairs of points
273  in.pointlist = (REAL*) malloc(2*in.numberofpoints*sizeof(REAL));
274 
275  //add unique vertices from our map, using their assigned indices
276  typedef std::unordered_map<Vertex, int>::const_iterator IndexIter;
277  for (IndexIter i = pointIndex.begin(); i != pointIndex.end(); ++i)
278  {
279  const Vertex& v = i->first;
280  int index = i->second;
281  in.pointlist[2*index] = v.x;
282  in.pointlist[2*index+1] = v.y;
283  }
284 
285  /* in.segmentlist is a sequence (a1 b1 a2 b2 ...) of pairs of indices into
286  in.pointlist to designate a segment between the respective points. */
287  in.segmentlist = (int*) malloc(2*in.numberofsegments*sizeof(int));
288 
289  //First, add segments for the bounding box
290  for (int i = 0; i < 4; ++i)
291  {
292  in.segmentlist[2*i] = i;
293  in.segmentlist[2*i+1] = (i+1) % 4;
294  }
295 
296  /* segIndex keeps track of where we are in in.segmentlist,
297  as we fill it from multiple sources of data. */
298  int segIndex = 4;
299 
300  /* Now, add segments for each obstacle in holes_, using our index map
301  from before to get the pointlist index for each vertex */
302  for (PolyIter p = holes_.begin(); p != holes_.end(); ++p)
303  {
304  for (unsigned int j = 0; j < p->pts.size(); ++j)
305  {
306  in.segmentlist[2*segIndex] = pointIndex[p->pts[j]];
307  in.segmentlist[2*segIndex+1] = pointIndex[p->pts[(j+1)%p->pts.size()]];
308  ++segIndex;
309  }
310  }
311 
312  /* Now, add segments for each region-of-interest in intRegs_,
313  using the same logic as before. */
314  for (PolyIter p = intRegs_.begin(); p != intRegs_.end(); ++p)
315  {
316  for (unsigned int j = 0; j < p->pts.size(); ++j)
317  {
318  in.segmentlist[2*segIndex] = pointIndex[p->pts[j]];
319  in.segmentlist[2*segIndex+1] = pointIndex[p->pts[(j+1)%p->pts.size()]];
320  ++segIndex;
321  }
322  }
323 
324  /* libtriangle needs an interior point for each obstacle in holes_.
325  For now, we'll assume that each obstacle is convex, and we'll
326  generate the interior points ourselves using getPointInPoly. */
327  in.numberofholes = holes_.size();
328  in.holelist = nullptr;
329  if (in.numberofholes > 0)
330  {
331  /* holelist is a sequence (x1 y1 x2 y2 ...) of ordered pairs of interior points.
332  The i^th ordered pair is an interior point of the i^th obstacle in holes_. */
333  in.holelist = (REAL*) malloc(2*in.numberofholes*sizeof(REAL));
334  for (int i = 0; i < in.numberofholes; ++i)
335  {
336  Vertex v = getPointInPoly(holes_[i]);
337  in.holelist[2*i] = v.x;
338  in.holelist[2*i+1] = v.y;
339  }
340  }
341 
342  /* Similar to above, libtriangle needs an interior point for each
343  region-of-interest in intRegs_. We follow the same assumption as before
344  that each region-of-interest is convex. */
345  in.numberofregions = intRegs_.size();
346  in.regionlist = nullptr;
347  if (in.numberofregions > 0)
348  {
349  /* regionlist is a sequence (x1 y1 L1 -1 x2 y2 L2 -1 ...) of ordered triples,
350  each ended with -1. The i^th ordered pair (xi,yi,Li) is an interior point
351  of the i^th region-of-interest in intRegs_, which is assigned the integer
352  label Li. */
353  in.regionlist = (REAL*) malloc(4*in.numberofregions*sizeof(REAL));
354  for (unsigned int i = 0; i < intRegs_.size(); ++i)
355  {
356  Vertex v = getPointInPoly(intRegs_[i]);
357  in.regionlist[4*i] = v.x;
358  in.regionlist[4*i+1] = v.y;
359  //triangles outside of interesting regions get assigned an attribute of zero by default
360  //so let's number our attributes from 1 to numProps, then shift it down by 1 when we're done
361  in.regionlist[4*i+2] = (REAL) (i+1);
362  in.regionlist[4*i+3] = -1.;
363  }
364  }
365 
366  //mark remaining input fields as unused
367  in.segmentmarkerlist = (int*) nullptr;
368  in.numberofpointattributes = 0;
369  in.pointattributelist = nullptr;
370  in.pointmarkerlist = nullptr;
371 
372  //initialize output libtriangle structure, which will hold the results of the triangulation
373  struct triangulateio out;
374  out.pointlist = (REAL*) nullptr;
375  out.pointattributelist = (REAL*) nullptr;
376  out.pointmarkerlist = (int*) nullptr;
377  out.trianglelist = (int*) nullptr;
378  out.triangleattributelist = (REAL*) nullptr;
379  out.neighborlist = (int*) nullptr;
380  out.segmentlist = (int*) nullptr;
381  out.segmentmarkerlist = (int*) nullptr;
382  out.edgelist = (int*) nullptr;
383  out.edgemarkerlist = (int*) nullptr;
384  out.pointlist = (REAL*) nullptr;
385  out.pointattributelist = (REAL*) nullptr;
386  out.trianglelist = (int*) nullptr;
387  out.triangleattributelist = (REAL*) nullptr;
388 
389  //call the triangulation routine
390  triangulate(const_cast<char*>(triswitches.c_str()), &in, &out, nullptr);
391 
392  triangles_.resize(out.numberoftriangles);
393  intRegInfo_.resize(out.numberoftriangles);
394  for (int i = 0; i < out.numberoftriangles; ++i)
395  {
396  Triangle& t = triangles_[i];
397  for (int j = 0; j < 3; ++j)
398  {
399  t.pts[j].x = out.pointlist[2*out.trianglelist[3*i+j]];
400  t.pts[j].y = out.pointlist[2*out.trianglelist[3*i+j]+1];
401  if (out.neighborlist[3*i+j] >= 0)
402  t.neighbors.push_back(out.neighborlist[3*i+j]);
403  }
404  t.volume = -1.;
405 
406  if (in.numberofregions > 0)
407  {
408  int attribute = (int) out.triangleattributelist[i];
409  /* Shift the region-of-interest ID's down to start from zero. */
410  intRegInfo_[i] = (attribute > 0 ? attribute-1 : -1);
411  }
412  }
413 
414  trifree(in.pointlist);
415  trifree(in.segmentlist);
416  if (in.numberofholes > 0)
417  trifree(in.holelist);
418  if (in.numberofregions > 0)
419  trifree(in.regionlist);
420  trifree(out.pointlist);
421  trifree(out.pointattributelist);
422  trifree(out.pointmarkerlist);
423  trifree(out.trianglelist);
424  trifree(out.triangleattributelist);
425  trifree(out.neighborlist);
426  trifree(out.edgelist);
427  trifree(out.edgemarkerlist);
428  trifree(out.segmentlist);
429  trifree(out.segmentmarkerlist);
430 
431  return out.numberoftriangles;
432 }
433 
434 void ompl::control::TriangularDecomposition::LocatorGrid::buildTriangleMap(const std::vector<Triangle>& triangles)
435 {
436  regToTriangles_.resize(getNumRegions());
437  std::vector<double> bboxLow(2);
438  std::vector<double> bboxHigh(2);
439  std::vector<int> gridCoord[2];
440  for (unsigned int i = 0; i < triangles.size(); ++i)
441  {
442  /* for Triangle tri, compute the smallest rectangular
443  * bounding box that contains tri. */
444  const Triangle& tri = triangles[i];
445  bboxLow[0] = tri.pts[0].x;
446  bboxLow[1] = tri.pts[0].y;
447  bboxHigh[0] = bboxLow[0];
448  bboxHigh[1] = bboxLow[1];
449 
450  for (int j = 1; j < 3; ++j)
451  {
452  if (tri.pts[j].x < bboxLow[0])
453  bboxLow[0] = tri.pts[j].x;
454  else if (tri.pts[j].x > bboxHigh[0])
455  bboxHigh[0] = tri.pts[j].x;
456  if (tri.pts[j].y < bboxLow[1])
457  bboxLow[1] = tri.pts[j].y;
458  else if (tri.pts[j].y > bboxHigh[1])
459  bboxHigh[1] = tri.pts[j].y;
460  }
461 
462  /* Convert the bounding box into grid cell coordinates */
463 
464  coordToGridCoord(bboxLow, gridCoord[0]);
465  coordToGridCoord(bboxHigh, gridCoord[1]);
466 
467  /* Every grid cell within bounding box gets
468  tri added to its map entry */
469  std::vector<int> c(2);
470  for (int x = gridCoord[0][0]; x <= gridCoord[1][0]; ++x)
471  {
472  for (int y = gridCoord[0][1]; y <= gridCoord[1][1]; ++y)
473  {
474  c[0] = x;
475  c[1] = y;
476  int cellID = gridCoordToRegion(c);
477  regToTriangles_[cellID].push_back(i);
478  }
479  }
480  }
481 }
482 
483 void ompl::control::TriangularDecomposition::buildLocatorGrid()
484 {
485  locator.buildTriangleMap(triangles_);
486 }
487 
488 bool ompl::control::TriangularDecomposition::triContains(const Triangle& tri, const std::vector<double>& coord)
489 {
490  for (int i = 0; i < 3; ++i)
491  {
492  /* point (coord[0],coord[1]) needs to be to the left of
493  the vector from (ax,ay) to (bx,by) */
494  const double ax = tri.pts[i].x;
495  const double ay = tri.pts[i].y;
496  const double bx = tri.pts[(i+1)%3].x;
497  const double by = tri.pts[(i+1)%3].y;
498 
499  // return false if the point is instead to the right of the vector
500  if ((coord[0]-ax)*(by-ay) - (bx-ax)*(coord[1]-ay) > 0.)
501  return false;
502  }
503  return true;
504 }
505 
506 ompl::control::TriangularDecomposition::Vertex ompl::control::TriangularDecomposition::getPointInPoly(const Polygon& poly)
507 {
508  Vertex p;
509  p.x = 0.;
510  p.y = 0.;
511  for (std::vector<Vertex>::const_iterator i = poly.pts.begin(); i != poly.pts.end(); ++i)
512  {
513  p.x += i->x;
514  p.y += i->y;
515  }
516  p.x /= poly.pts.size();
517  p.y /= poly.pts.size();
518  return p;
519 }
double getVolume() const
Compute the volume of the space enclosed by the bounds.
std::vector< double > low
Lower bound.
virtual int locateRegion(const base::State *s) const
Returns the index of the region containing a given State. Most often, this is obtained by first calli...
virtual double getRegionVolume(int triID)
Returns the volume of a given region in this Decomposition.
virtual int createTriangles()
Helper method to triangulate the space and return the number of triangles.
virtual void getNeighbors(int triID, std::vector< int > &neighbors) const
Stores a given region's neighbors into a given vector.
A Decomposition is a partition of a bounded Euclidean space into a fixed number of regions which are ...
Definition: Decomposition.h:62
double uniform01()
Generate a random real between 0 and 1.
Definition: RandomNumbers.h:69
virtual void sampleFromRegion(int triID, RNG &rng, std::vector< double > &coord) const
Samples a projected coordinate from a given region.
int getRegionOfInterestAt(int triID) const
Returns the region of interest that contains the given triangle ID. Returns -1 if the triangle ID is ...
Random number generation. An instance of this class cannot be used by multiple threads at once (membe...
Definition: RandomNumbers.h:58
std::vector< double > high
Upper bound.
Definition of an abstract state.
Definition: State.h:50
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
The lower and upper bounds for an Rn space.
TriangularDecomposition(const base::RealVectorBounds &bounds, const std::vector< Polygon > &holes=std::vector< Polygon >(), const std::vector< Polygon > &intRegs=std::vector< Polygon >())
Creates a TriangularDecomposition over the given bounds, which must be 2-dimensional. The underlying mesh will be a conforming Delaunay triangulation. The triangulation will ignore any obstacles, given as a list of polygons. The triangulation will respect the boundaries of any regions of interest, given as a list of polygons. No two obstacles may overlap, and no two regions of interest may overlap.
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68