ChainApproximation.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstddef>
5#include <iterator>
6#include <ostream>
7#include <string>
8#include <vector>
9
10#include <Eigen/Geometry>
11
12#include <SimoxUtility/algorithm/apply.hpp>
13
15
16#include "range/v3/algorithm/find.hpp"
17#include "range/v3/numeric/iota.hpp"
18
20{
21
23 points(points), params(params)
24 {
25 // fill indices
26 indices.resize(points.size());
27 ranges::iota(indices, 0);
28 }
29
32 {
33
34 int iterations = 0;
35
36 const auto maxIterConditionReached = [&]()
37 {
38 // inactive?
39 if (params.maxIterations <= 0)
40 {
41 return false;
42 }
43
44 return iterations >= params.maxIterations;
45 };
46
47 while (true)
48 {
49 if (maxIterConditionReached())
50 {
53 .iterations = iterations};
54 }
55
56 if (not approximateStep())
57 {
60 .iterations = iterations,
61 .reductionFactor = 1.F - static_cast<float>(indices.size()) /
62 static_cast<float>(points.size())};
63 }
64
65 iterations++;
66 }
67 }
68
69 ChainApproximation::Triplets
70 ChainApproximation::getTriplets() const
71 {
72 const int nIndices = static_cast<int>(indices.size());
73
74 if (nIndices < 3)
75 {
76 return {};
77 }
78
79 Triplets triplets;
80 triplets.reserve(indices.size());
81
82 // Here, we iterate over all elements under consideration.
83 // The aim is to create a view - a sliding window - over the
84 // indices. i will always point to the centered element.
85
86 // the first element
87 triplets.emplace_back(indices.back(), indices.front(), indices.at(1));
88
89 // intermediate elements
90 for (int i = 1; i < (nIndices - 1); i++)
91 {
92 triplets.emplace_back(indices.at(i - 1), indices.at(i), indices.at(i + 1));
93 }
94
95 // the last element
96 triplets.emplace_back(indices.back(), indices.front(), indices.at(1));
97
98 return triplets;
99 }
100
101 std::vector<float>
102 ChainApproximation::computeDistances(const ChainApproximation::Triplets& triplets)
103 {
104 std::vector<float> distances;
105 distances.reserve(triplets.size());
106
107 std::transform(triplets.begin(),
108 triplets.end(),
109 std::back_inserter(distances),
110 [&](const auto& triplet) { return computeDistance(triplet); });
111
112 return distances;
113 }
114
115 float
116 ChainApproximation::computeDistance(const ChainApproximation::Triplet& triplet) const
117 {
118 using Line = Eigen::ParametrizedLine<float, 2>;
119
120 const Eigen::Vector2f& ptBefore = points.at(triplet.a);
121 const Eigen::Vector2f& ptPivot = points.at(triplet.b);
122 const Eigen::Vector2f& ptAfter = points.at(triplet.c);
123
124 const auto line = Line::Through(ptBefore, ptAfter);
125 return line.distance(ptPivot);
126 }
127
128 bool
129 ChainApproximation::approximateStep()
130 {
131 const size_t nIndices = indices.size();
132 if (nIndices <= 3)
133 {
134 return false;
135 }
136
137 const Triplets triplets = getTriplets();
138 const std::vector<float> distances = computeDistances(triplets);
139
140 ARMARX_CHECK_EQUAL(triplets.size(), distances.size());
141 const int n = static_cast<int>(triplets.size());
142
143 std::vector<int> indicesToBeRemoved;
144
145 // TODO(fabian.reister): consider boundaries
146 for (int i = 1; i < n - 1; i++)
147 {
148 const auto& distance = distances.at(i);
149
150 // check distance criterion (necessary conditio)
151 if (distance >= params.distanceTh)
152 {
153 continue;
154 }
155
156 // better remove this element than those left and right (sufficient condition)
157 if (distance <= std::min(distances.at(i - 1), distances.at(i + 1)))
158 {
159 indicesToBeRemoved.emplace_back(triplets.at(i).b);
160 }
161 }
162
163 // termination condition
164 if (indicesToBeRemoved.empty())
165 {
166 return false;
167 }
168
169 const auto isMatch = [&](const int& idx) -> bool
170 { return ranges::find(indicesToBeRemoved, idx) != indicesToBeRemoved.end(); };
171
172 indices.erase(std::remove_if(indices.begin(), indices.end(), isMatch), indices.end());
173
174 return true;
175 }
176
179 {
180 return simox::alg::apply(indices, [&](const auto& idx) { return points.at(idx); });
181 }
182
183 std::ostream&
184 detail::operator<<(std::ostream& str, const ApproximationResult& res)
185 {
186 using TerminationCondition = ApproximationResult::TerminationCondition;
187
188 const std::string condStr = [&res]() -> std::string
189 {
190 std::string repr;
191
192 switch (res.condition)
193 {
194 case TerminationCondition::Converged:
195 repr = "Converged";
196 break;
197 case TerminationCondition::IterationLimit:
198 repr = "IterationLimit";
199 break;
200 }
201 return repr;
202 }();
203
204 str << "ApproximationResult: ["
205 << "condition: " << condStr << " | "
206 << "iterations: " << res.iterations << " | "
207 << "reduction: " << res.reductionFactor * 100 << "%]";
208
209 return str;
210 }
211} // namespace armarx::navigation::algorithm
Eigen::ParametrizedLine< float, 2 > Line
std::string str(const T &t)
ChainApproximation(const Points &points, const Params &params)
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
std::ostream & operator<<(std::ostream &str, const ApproximationResult &res)
This file is part of ArmarX.
constexpr auto n() noexcept
double distance(const Point &a, const Point &b)
Definition point.hpp:95