place_profile_utils.cpp
Go to the documentation of this file.
00001 #include <lama_common/place_profile_utils.h>
00002 
00003 namespace lama_common
00004 {
00005 
00008 void normalizeExcludeSegments(PlaceProfile& profile)
00009 {
00010   if (profile.exclude_segments.empty())
00011   {
00012     return;
00013   }
00014 
00015   if (is_sorted(profile.exclude_segments.begin(), profile.exclude_segments.end()))
00016   {
00017     bool need_erase = false;
00018     for (int i = 0; i < ((int) profile.exclude_segments.size()) - 1; ++i)
00019     {
00020       // Check for doubles in exclude_segments.
00021       if (profile.exclude_segments[i] == profile.exclude_segments[i + 1])
00022       {
00023         need_erase = true;
00024         break;
00025       }
00026       // Check for out-of-bound elements.
00027       if ((profile.exclude_segments[i] < 0) || (profile.exclude_segments[i] >= profile.exclude_segments.size()))
00028       {
00029         need_erase = true;
00030         break;
00031       }
00032     }
00033     need_erase |= (profile.exclude_segments.back() < 0);
00034     need_erase |= (profile.exclude_segments.back() >= profile.exclude_segments.size());
00035     if (!need_erase)
00036     {
00037       return;
00038     }
00039   }
00040 
00041   std::sort(profile.exclude_segments.begin(), profile.exclude_segments.end());
00042 
00043   for (vector<int32_t>::iterator s = profile.exclude_segments.begin(); s != profile.exclude_segments.end();)
00044   {
00045     // Ignore negative elements and too large elements.
00046     if ((*s < 0) || (*s >= profile.polygon.points.size()))
00047     {
00048       s = profile.exclude_segments.erase(s);
00049       continue;
00050     }
00051 
00052     // Remove doubles.
00053     std::vector<int32_t>::iterator last = --profile.exclude_segments.end();
00054     if (s != last)
00055     {
00056       std::vector<int>::iterator next = s + 1;
00057       if (*s == *next)
00058       {
00059         s = profile.exclude_segments.erase(s);
00060         continue;
00061       }
00062     }
00063     ++s;
00064   }
00065 }
00066 
00069 inline bool pointInCCWOrder(const vector<AngularPoint>& angular_points)
00070 {
00071   double sum_angle_increments = 0;
00072   for (size_t i = 0; i < std::min((int)angular_points.size() - 1, 3); ++i)
00073   {
00074     const double dtheta = angular_points[i + 1].angle - angular_points[i].angle;
00075     if (std::abs(dtheta) < 0.99 * M_PI)
00076     {
00077       sum_angle_increments += dtheta;
00078     }
00079   }
00080   return sum_angle_increments > 0;
00081 }
00082 
00099 void normalizePlaceProfile(PlaceProfile& profile, bool sort)
00100 {
00101   if (sort)
00102   {
00103     vector<AngularPoint> angular_points = toAngularPoints(profile);
00104     size_t point_count = profile.polygon.points.size();
00105 
00106     if (!is_sorted(angular_points.begin(), angular_points.end()))
00107     {
00108       // If points are clock-wise, exclude_segments needs to be changed because
00109       // after with clock-wise direction means before with counterclock-wise direction.
00110       const bool counterclockwise = pointInCCWOrder(angular_points);
00111 
00112       // TODO: Improve sort performance.
00113       // Appart from the position of the step from +pi to -pi (CCW) which can be in
00114       // the middle of the array instead of being absent, angular_points
00115       // should be sorted. Use a circular_iterator, if this exists.
00116       std::sort(angular_points.begin(), angular_points.end());
00117 
00118       PlaceProfile old_profile = profile;
00119       for (size_t i = 0; i < point_count; ++i)
00120       {
00121         profile.polygon.points[i] = old_profile.polygon.points[angular_points[i].index];
00122       }
00123 
00124       vector<int32_t> inverse_permutation;
00125       if (!profile.exclude_segments.empty())
00126       {
00127         inverse_permutation.resize(point_count);
00128         for (size_t i = 0; i < point_count; ++i)
00129         {
00130           inverse_permutation[angular_points[i].index] = i;
00131         }
00132       }
00133       if (!counterclockwise)
00134       {
00135         // v[i] = v[i]+1, does the trick to get the correct renumbering.
00136         for (size_t i = 0; i < old_profile.exclude_segments.size(); ++i)
00137         {
00138           old_profile.exclude_segments[i] = (old_profile.exclude_segments[i] + 1) % point_count;
00139         }
00140       }
00141       for (size_t i = 0; i < profile.exclude_segments.size(); ++i)
00142       {
00143         profile.exclude_segments[i] = inverse_permutation[old_profile.exclude_segments[i]];
00144       }
00145     }
00146   }
00147 
00148   normalizeExcludeSegments(profile);
00149 }
00150 
00158 PlaceProfile normalizedPlaceProfile(const PlaceProfile& profile, bool sort)
00159 {
00160   PlaceProfile new_profile = profile;
00161   normalizePlaceProfile(new_profile, sort);
00162   return new_profile;
00163 }
00164 
00169 bool isClosed(const PlaceProfile& profile, double max_frontier_width)
00170 {
00171   const size_t size = profile.polygon.points.size();
00172   const double width2 = max_frontier_width * max_frontier_width;
00173 
00174   for(size_t i = 0; i < size; ++i)
00175   {
00176     Point32 a = profile.polygon.points[i];
00177     Point32 b = profile.polygon.points[(i + 1) % size];
00178 
00179     const double dx = b.x - a.x;
00180     const double dy = b.y - a.y;
00181 
00182     if (dx * dx + dy * dy > width2)
00183     {
00184       return false;
00185     }
00186   }
00187   return true;
00188 }
00189 
00190 
00200 void closePlaceProfile(PlaceProfile& profile, double max_frontier_width)
00201 {
00202   if (isClosed(profile, max_frontier_width))
00203   {
00204     return;
00205   }
00206 
00207   const size_t size = profile.polygon.points.size();
00208   const double width2 = max_frontier_width * max_frontier_width;
00209 
00210   PlaceProfile old_profile = profile;
00211   profile.polygon.points.clear();
00212   profile.polygon.points.reserve(size);
00213   profile.exclude_segments.clear();
00214 
00215   for(size_t i = 0; i < size; ++i)
00216   {
00217     Point32 a = old_profile.polygon.points[i];
00218     Point32 b = old_profile.polygon.points[(i + 1) % size];
00219 
00220     const double dx = b.x - a.x;
00221     const double dy = b.y - a.y;
00222 
00223     if (dx * dx + dy * dy > width2)
00224     {
00225       Point32 point;
00226       const double norm = std::sqrt(dx * dx + dy * dy);
00227       // Unit vector from point[i] to point[j].
00228       const double ux = dx / norm;
00229       const double uy = dy / norm;
00230       for (double s = 0; s <= norm; s += max_frontier_width) 
00231       {
00232         point.x = a.x + s * ux;
00233         point.y = a.y + s * uy;
00234         profile.polygon.points.push_back(point);
00235       }
00236     }
00237     else
00238     {
00239       profile.polygon.points.push_back(a);
00240     }
00241   }
00242 }
00243 
00253 PlaceProfile closedPlaceProfile(const PlaceProfile& profile, double max_frontier_width)
00254 {
00255   if (isClosed(profile, max_frontier_width))
00256   {
00257     return profile;
00258   }
00259 
00260   // closePlaceProfile is not used here to avoid an extra copy of the polygon points.
00261 
00262   PlaceProfile new_profile;
00263   const size_t size = profile.polygon.points.size();
00264   const double width2 = max_frontier_width * max_frontier_width;
00265 
00266   new_profile.polygon.points.reserve(size);
00267 
00268   for(size_t i = 0; i < size; ++i)
00269   {
00270     Point32 a(profile.polygon.points[i]);
00271     Point32 b(profile.polygon.points[(i + 1) % size]);
00272 
00273     const double dx = b.x - a.x;
00274     const double dy = b.y - a.y;
00275 
00276     if (dx * dx + dy * dy > width2)
00277     {
00278       Point32 point;
00279       const double norm = std::sqrt(dx * dx + dy * dy);
00280       // Unit vector from point[i] to point[j].
00281       const double ux = dx / norm;
00282       const double uy = dy / norm;
00283       for (double s = 0; s <= norm; s += max_frontier_width) 
00284       {
00285         point.x = a.x + s * ux;
00286         point.y = a.y + s * uy;
00287         new_profile.polygon.points.push_back(point);
00288       }
00289     }
00290     else
00291     {
00292       new_profile.polygon.points.push_back(a);
00293     }
00294   }
00295 
00296   return new_profile;
00297 }
00298 
00299 struct IndexedDouble
00300 {
00301   size_t index;
00302   double value;
00303   IndexedDouble(size_t i, double v) : index(i), value(v) {}
00304 
00305   bool operator<(const IndexedDouble& other) {return value < other.value;}
00306 };
00307 
00314 inline double getRelevance(const Point32& pi, const Point32& pj, const Point32& pk)
00315 {
00316   double relevance;
00317   double dx;
00318   double dy;
00319   dx = pi.x - pj.x;
00320   dy = pi.y - pj.y;
00321   relevance = sqrt(dx * dx + dy * dy);
00322   dx = pj.x - pk.x;
00323   dy = pj.y - pk.y;
00324   relevance += sqrt(dx * dx + dy * dy);
00325   dx = pi.x - pk.x;
00326   dy = pi.y - pk.y;
00327   relevance -= sqrt(dx * dx+ dy * dy);
00328   return std::abs(relevance);
00329 }
00330 
00337 double getRelevance(const std::list<IndexedDouble>& l, std::list<IndexedDouble>::const_iterator it,
00338     const vector<Point32>& points)
00339 {
00340   std::list<IndexedDouble>::const_iterator pred = it;
00341   std::list<IndexedDouble>::const_iterator succ = it;
00342   pred--;
00343   succ++;
00344 
00345   double relevance = -1;
00346   if (it != l.begin() && succ != l.end())
00347   {
00348     relevance = getRelevance(points[pred->index], points[it->index], points[succ->index]);
00349   } 
00350   return relevance;
00351 }
00352 
00362 vector<Point32> simplifyPath(const vector<Point32>& points, size_t begin, size_t end, double min_relevance)
00363 {
00364   if(begin >= points.size())
00365   {
00366     std::ostringstream msg;
00367     msg << "begin index (" << begin << ") should but strictly smaller than length of argument 1 (" <<
00368       points.size() << ")";
00369     throw std::runtime_error(msg.str());
00370   }
00371   if(end > points.size())
00372   {
00373     std::ostringstream msg;
00374     msg << "end index (" << end << ") should but smaller than or equal to length of argument 1 (" <<
00375       points.size() << ")";
00376     throw std::runtime_error(msg.str());
00377   }
00378   if(begin > end)
00379   {
00380     std::ostringstream msg;
00381     msg << "end index (" << end << ") should but larger than or equal to begin index (" <<
00382       begin << ")";
00383     throw std::runtime_error(msg.str());
00384   }
00385 
00386   if (end - begin < 3)
00387   {
00388     // Two points or less, just copy the points.
00389     vector<Point32> filtered_points;
00390     if ((end - begin) > 0)
00391     {
00392       filtered_points.reserve((end - begin) - 1);
00393     }
00394     for (size_t i = begin; i < end; ++i)
00395     {
00396       filtered_points.push_back(points[i]);
00397     }
00398     return filtered_points;
00399   }
00400 
00401   std::list<IndexedDouble> relevances;
00402   double relevance = std::numeric_limits<double>::min();
00403   for (size_t j = begin; j < end; ++j)
00404   {
00405     if (j != begin && j != end - 1)
00406     {
00407       relevance = getRelevance(points[j - 1], points[j], points[j + 1]);
00408     }
00409     relevances.push_back(IndexedDouble(j, relevance));
00410   }
00411   relevances.front().value = 10 * min_relevance;
00412   relevances.back().value = 10 * min_relevance;
00413 
00414   do
00415   {
00416     if (relevances.size() < 3)
00417     {
00418       break;
00419     }
00420 
00421     std::list<IndexedDouble>::iterator me = std::min_element(relevances.begin(), relevances.end());
00422 
00423     if (me == relevances.end())
00424     {
00425       // relevances is empty, finish.
00426       break;
00427     }
00428     if (me->value >= min_relevance)
00429     {
00430       // All points are relevant, finish.
00431       break;
00432     }
00433     // Because of the last test, the smallest relevance cannot be on the first
00434     // nor the last point, so the following is not risky.
00435     std::list<IndexedDouble>::iterator pred = me;
00436     --pred;
00437     std::list<IndexedDouble>::iterator succ = me;
00438     ++succ;
00439     relevances.erase(me);
00440     if (pred != --relevances.end())
00441     {
00442       // The point removed was not the before-the-last point.
00443       relevance = getRelevance(relevances, pred, points);
00444       if (relevance >= 0)
00445       {
00446         pred->value = relevance;
00447       }
00448     }
00449     if (succ != relevances.begin())
00450     {
00451       // The point removed was not the 2nd point.
00452       relevance = getRelevance(relevances, succ, points);
00453       if (relevance >= 0)
00454       {
00455         succ->value = relevance;
00456       }
00457     }
00458   } while (true);
00459 
00460   vector<Point32> filtered_points;
00461   filtered_points.reserve(relevances.size());
00462   for(std::list<IndexedDouble>::const_iterator it = relevances.begin(); it != relevances.end(); ++it)
00463   {
00464     filtered_points.push_back(points[it->index]);
00465   }
00466   return filtered_points;
00467 }
00468 
00477 void simplifyPlaceProfile(PlaceProfile& profile, double min_relevance)
00478 {
00479   // Note: we do not simplify the segment between the last point and the first one.
00480 
00481   if (profile.polygon.points.size() < 3)
00482   {
00483     return;
00484   }
00485 
00486   if (profile.exclude_segments.empty())
00487   {
00488     profile.polygon.points = simplifyPath(profile.polygon.points, 0, profile.polygon.points.size(), min_relevance);
00489     return;
00490   }
00491 
00492   PlaceProfile old_profile = profile;
00493 
00494   // We need sorted excluded segments (points do not need to be sorted).
00495   normalizeExcludeSegments(old_profile);
00496 
00497   profile.polygon.points.clear();
00498   profile.polygon.points.reserve(old_profile.polygon.points.size());
00499   profile.exclude_segments.clear();
00500   profile.exclude_segments.reserve(old_profile.exclude_segments.size());
00501 
00502   size_t path_start = 0;
00503   for (size_t i = 0; i < old_profile.exclude_segments.size(); ++i)
00504   {
00505     const vector<Point32> filtered_points = simplifyPath(old_profile.polygon.points, path_start, old_profile.exclude_segments[i] + 1, min_relevance);
00506     std::copy(filtered_points.begin(), filtered_points.end(), std::back_inserter(profile.polygon.points));
00507     profile.exclude_segments.push_back(((int)profile.polygon.points.size()) - 1);
00508     path_start = old_profile.exclude_segments[i] + 1;
00509   }
00510   if (path_start < old_profile.polygon.points.size())
00511   {
00512     // If path_start is not after the last point, what happens if last point is excluded.
00513     const vector<Point32> filtered_points = simplifyPath(old_profile.polygon.points, path_start, old_profile.polygon.points.size(), min_relevance);
00514     std::copy(filtered_points.begin(), filtered_points.end(), std::back_inserter(profile.polygon.points));
00515   }
00516 }
00517 
00525 PlaceProfile simplifiedPlaceProfile(const PlaceProfile& profile, double min_relevance)
00526 {
00527   PlaceProfile new_profile = profile;
00528   simplifyPlaceProfile(new_profile, min_relevance);
00529   return new_profile;
00530 }
00531 
00537 void curtailPlaceProfile(PlaceProfile& profile, double max_distance)
00538 {
00539   const size_t size = profile.polygon.points.size();
00540   const double dist2 = max_distance * max_distance;
00541   vector<bool> in_range(size, false);
00542   for (size_t i = 0; i < size; ++i)
00543   {
00544     if ((profile.polygon.points[i].x * profile.polygon.points[i].x +
00545           profile.polygon.points[i].y * profile.polygon.points[i].y) < dist2)
00546     {
00547       in_range[i] = true;
00548     }
00549   }
00550 
00551   PlaceProfile old_profile = profile;
00552   profile.polygon.points.clear();
00553   profile.exclude_segments.clear();
00554   for (size_t i = 0; i < size; ++i)
00555   {
00556     if (in_range[i] && in_range[(i + 1) % size])
00557     {
00558       // point i and next point are included.
00559       profile.polygon.points.push_back(old_profile.polygon.points[i]);
00560     }
00561     else if (in_range[i] && !in_range[(i + 1) % size])
00562     {
00563       // point i is included, next point (which will not be part of
00564       // profile.polygon) is excluded.
00565       profile.polygon.points.push_back(old_profile.polygon.points[i]);
00566       profile.exclude_segments.push_back(profile.polygon.points.size() - 1);
00567     }
00568   }
00569 
00570   if (profile.polygon.points.size() < 3)
00571   {
00572     profile.exclude_segments.clear();
00573   }
00574   else if (!profile.exclude_segments.empty() && (profile.exclude_segments.front() == -1))
00575   {
00576     // If segment between last point and point 0 is excluded.
00577     // This happens if the first range(s) is(are) out of range.
00578     profile.exclude_segments[0] = profile.polygon.points.size() - 1;
00579   }
00580 }
00581 
00587 PlaceProfile curtailedPlaceProfile(const PlaceProfile& profile, double max_distance)
00588 {
00589   PlaceProfile new_profile = profile;
00590   curtailPlaceProfile(new_profile, max_distance);
00591   return new_profile;
00592 }
00593 
00594 } // namespace lama_common
00595 
00596 


lama_common
Author(s): Gaël Ecorchard , Karel Košnar , Vojtěch Vonásek
autogenerated on Thu Jun 6 2019 22:02:03