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
00021 if (profile.exclude_segments[i] == profile.exclude_segments[i + 1])
00022 {
00023 need_erase = true;
00024 break;
00025 }
00026
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
00046 if ((*s < 0) || (*s >= profile.polygon.points.size()))
00047 {
00048 s = profile.exclude_segments.erase(s);
00049 continue;
00050 }
00051
00052
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
00109
00110 const bool counterclockwise = pointInCCWOrder(angular_points);
00111
00112
00113
00114
00115
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
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
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
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
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
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
00426 break;
00427 }
00428 if (me->value >= min_relevance)
00429 {
00430
00431 break;
00432 }
00433
00434
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
00443 relevance = getRelevance(relevances, pred, points);
00444 if (relevance >= 0)
00445 {
00446 pred->value = relevance;
00447 }
00448 }
00449 if (succ != relevances.begin())
00450 {
00451
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
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
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
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
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
00564
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
00577
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 }
00595
00596