geometry.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  *
00029  */
00030 
00039 #include <occupancy_grid_utils/geometry.h>
00040 #include <queue>
00041 #include <set>
00042 #include <boost/foreach.hpp>
00043 
00044 namespace occupancy_grid_utils
00045 {
00046 
00047 namespace gm=geometry_msgs;
00048 namespace nm=nav_msgs;
00049 using std::vector;
00050 typedef std::set<Cell> Cells;
00051 
00052 struct Line
00053 {
00054   Line (const gm::Point32& p1, const gm::Point32& p2)
00055   {
00056     const float dx = p2.x-p1.x;
00057     const float dy = p2.y-p1.y;
00058     if (fabs(dx) < 1e-3 && fabs(dy) < 1e-3)
00059     {
00060       boost::format e("Points (%.2f, %.2f) and (%.2f, %.2f) are too close");
00061       throw GridUtilsException(e % p1.x % p1.y % p2.x % p2.y);
00062     }      
00063     a = dy;
00064     b = -dx;
00065     c = p1.y*dx - p1.x*dy;
00066   }
00067   
00068   // Line intersects a convex polygon if two of the vertices have opposite signs
00069   bool intersects (const gm::Polygon& poly) const
00070   {
00071     bool seen_nonnegative = false;
00072     bool seen_nonpositive = false;
00073     BOOST_FOREACH (const gm::Point32& p, poly.points) 
00074     {
00075       const float d = a*p.x+b*p.y+c;
00076       if (d>=0)
00077         seen_nonnegative = true;
00078       if (d<=0)
00079         seen_nonpositive = true;
00080       if (seen_nonnegative && seen_nonpositive)
00081         return true;
00082     }
00083     return false;
00084   }
00085 
00086   // Coefficients of line equation ax+by+c=0
00087   float a;
00088   float b;
00089   float c;
00090 };
00091 
00092 // Visitor for the flood fill
00093 struct CellsInPolygon
00094 {
00095   CellsInPolygon (const nm::MapMetaData& info, const gm::Polygon& poly) :
00096     info(info)
00097   {
00098     const size_t n = poly.points.size();
00099     for (size_t i=0; i<n; i++)
00100     {
00101       const size_t j = i>0 ? i-1 : n-1;
00102       sides.push_back(Line(poly.points[i], poly.points[j]));
00103     }
00104   }
00105 
00106   // Add cell to the set of visited cells.  If cell doesn't intersect the
00107   // boundary of the polygon, return true (continue propagating), else false.
00108   bool operator() (const Cell& c)
00109   {
00110     cells.insert(c);
00111     const gm::Polygon cell_poly = cellPolygon(info, c);
00112     BOOST_FOREACH (const Line& s, sides)
00113     {
00114       if (s.intersects(cell_poly))
00115         return false;
00116     }
00117     return true;
00118   }
00119 
00120   Cells cells;
00121   const nm::MapMetaData& info;
00122   std::vector<Line> sides;
00123 };
00124 
00125 
00126 
00127 // Generic flood fill
00128 template <class Visitor>
00129 void flood_fill (const nm::MapMetaData& info, const std::set<Cell>& start,
00130                  Visitor& vis)
00131 {
00132   Cells seen;
00133   std::queue<Cell> q;
00134   BOOST_FOREACH (const Cell& c, start) 
00135     q.push(c);
00136   while (!q.empty())
00137   {
00138     const Cell c = q.front();
00139     q.pop();
00140     ROS_DEBUG_STREAM_NAMED ("flood_fill", "Cell " << c);
00141     if (seen.find(c)==seen.end())
00142     {
00143       seen.insert(c);
00144       ROS_DEBUG_NAMED ("flood_fill", "  Visiting");
00145       if (vis(c))
00146       {
00147         ROS_DEBUG_NAMED ("flood_fill", "  Adding neighbors");
00148         for (int dy=-1; dy<=1; dy++)
00149         {
00150           for (int dx=-1; dx<=1; dx++)
00151           {
00152             if (dx!=0 || dy!=0)
00153             {
00154               const Cell c2(c.x+dx, c.y+dy);
00155               if (withinBounds(info, c2))
00156               {
00157                 q.push(c2);
00158                 ROS_DEBUG_STREAM_NAMED ("flood_fill", "    Added " << c2);
00159               }
00160             }
00161           }
00162         }
00163       }
00164     }
00165   }
00166 }
00167 
00168 // Generic flood fill 
00169 template <class Visitor>
00170 void flood_fill (const nm::MapMetaData& info, const Cell& start,
00171                  Visitor& vis)
00172 {
00173   std::set<Cell> s;
00174   s.insert(start);
00175   flood_fill(info, s, vis);
00176 }
00177 
00178 
00179 Cell center(const nm::MapMetaData& info,
00180             const gm::Polygon& poly)
00181 {
00182   float sx=0;
00183   float sy=0;
00184   BOOST_FOREACH (const gm::Point32& p, poly.points) 
00185   {
00186     sx += p.x;
00187     sy += p.y;
00188   }
00189   gm::Point p;
00190   p.x = sx/poly.points.size();
00191   p.y = sy/poly.points.size();
00192   return pointCell(info, p);
00193 }
00194 
00195 // We do this by starting at the center and flood-filling outwards till we
00196 // reach the boundary
00197 Cells cellsInConvexPolygon (const nm::MapMetaData& info,
00198                             const gm::Polygon& poly)
00199 {
00200   CellsInPolygon visitor(info, poly);
00201   flood_fill(info, center(info, poly), visitor);
00202   return visitor.cells;
00203 }
00204 
00205 struct DistanceQueueItem
00206 {
00207   DistanceQueueItem (const Cell& c, const float distance) :
00208     c(c), distance(distance)
00209   {}
00210 
00211   inline
00212   bool operator< (const DistanceQueueItem& other) const
00213   {
00214     // Higher priority means lower distance
00215     return (distance > other.distance);
00216   }
00217   
00218   Cell c;
00219   float distance;
00220 };
00221 
00222 DistanceField distanceField (const nav_msgs::OccupancyGrid& m,
00223                              const float max_dist)
00224 {
00225   // Initialize
00226   vector<unsigned> dims;
00227   dims.push_back(m.info.width);
00228   dims.push_back(m.info.height);
00229   DistanceField::ArrayPtr distances(new DistanceField::Array(dims));
00230   for (unsigned x=0; x<m.info.width; x++)
00231     for (unsigned y=0; y<m.info.height; y++)
00232       (*distances)[x][y] = max_dist;
00233   std::set<Cell> seen; // Set of cells that have already been added to distance field
00234   std::priority_queue<DistanceQueueItem> q;
00235 
00236   // Sweep over the grid and add all obstacles to the priority queue
00237   for (index_t i=0; i<m.info.width*m.info.height; i++)
00238   {
00239     if (m.data[i]!=UNOCCUPIED && m.data[i]!=UNKNOWN)
00240     {
00241       DistanceQueueItem item(indexCell(m.info, i), 0);
00242       q.push(item);
00243     }
00244   }
00245 
00246   // Iteration guaranteed to be in nondecreasing order of distance
00247   while (!q.empty())
00248   {
00249     DistanceQueueItem item = q.top();
00250     ROS_DEBUG_THROTTLE_NAMED (1.0, "distance_field",
00251                               "Distance %.4f, %zu seen",
00252                               item.distance, seen.size());
00253     q.pop();
00254 
00255     // If not already seen, add this cell to distance field, and 
00256     // add its neighbors to the queue
00257     if (seen.find(item.c)==seen.end())
00258     {
00259       seen.insert(item.c);
00260       const Cell& c = item.c;
00261       (*distances)[c.x][c.y] = item.distance;
00262       if (max_dist>0 && item.distance>max_dist)
00263         continue;
00264       for (char vertical=0; vertical<2; vertical++)
00265       {
00266         for (char d=-1; d<=1; d+=2)
00267         {
00268           const char dx = vertical ? 0 : d;
00269           const char dy = vertical ? d : 0;
00270           const Cell neighbor(c.x+dx, c.y+dy);
00271           if (withinBounds(m.info, neighbor))
00272           {
00273             const float dist = item.distance+m.info.resolution;
00274             q.push(DistanceQueueItem(neighbor, dist));
00275           }
00276         }
00277       }
00278     }
00279   }
00280   
00281   return DistanceField(distances);
00282 }
00283 
00284 
00285 } // namespace


occupancy_grid_utils
Author(s): Bhaskara Marthi
autogenerated on Thu Dec 12 2013 13:17:54