PolyFEM
Loading...
Searching...
No Matches
Interpolation.cpp
Go to the documentation of this file.
1#include "Interpolation.hpp"
2
5
6namespace polyfem::utils
7{
10 {{PiecewiseInterpolation::Extend::CONSTANT, "constant"}, // also default
14
15 std::shared_ptr<Interpolation> Interpolation::build(const json &params)
16 {
17 const std::string type = params["type"];
18 std::shared_ptr<Interpolation> res = nullptr;
19
20 if (type == "none")
21 res = std::make_shared<NoInterpolation>();
22 else if (type == "linear")
23 res = std::make_shared<LinearInterpolation>();
24 else if (type == "linear_ramp")
25 res = std::make_shared<LinearRamp>();
26 else if (type == "piecewise_constant")
27 res = std::make_shared<PiecewiseConstantInterpolation>();
28 else if (type == "piecewise_linear")
29 res = std::make_shared<PiecewiseLinearInterpolation>();
30 else if (type == "piecewise_cubic")
31 res = std::make_shared<PiecewiseCubicInterpolation>();
32 else
33 log_and_throw_error("Usupported interpolation type {}", type);
34
35 assert(res != nullptr);
36 res->init(params);
37
38 return res;
39 }
40
41 void LinearRamp::init(const json &params)
42 {
43 to_ = params["to"];
44 from_ = params.value("from", 0.0);
45 }
46
47 double LinearRamp::eval(const double t) const
48 {
49 if (t >= to_)
50 return to_ - from_;
51
52 if (t <= from_)
53 return 0;
54
55 return t - from_;
56 }
57
59 {
60 if (!params["points"].is_array())
61 log_and_throw_error("PiecewiseInterpolation points must be an array");
62 if (!params["values"].is_array())
63 log_and_throw_error("PiecewiseInterpolation values must be an array");
64
65 points_ = params["points"].get<std::vector<double>>();
66 assert(std::is_sorted(points_.begin(), points_.end()));
67 values_ = params["values"].get<std::vector<double>>();
68
69 assert(params.contains("extend"));
70 extend_ = params["extend"];
71 }
72
73 double PiecewiseInterpolation::eval(const double t) const
74 {
75 if (t < points_.front() || t >= points_.back())
76 return extend(t);
77
78 for (size_t i = 0; i < points_.size() - 1; ++i)
79 {
80 if (t >= points_[i] && t < points_[i + 1])
81 {
82 return eval_piece(t, i);
83 }
84 }
85
86 assert(points_.size() == 1);
87 return values_[0];
88 }
89
90 double PiecewiseInterpolation::extend(const double t) const
91 {
92 assert(points_.size() == values_.size());
93 const double t0 = points_.front(), tn = points_.back();
94 assert(t < t0 || t >= tn);
95 const double y0 = values_.front(), yn = values_.back();
96
97 double offset = 0;
98 switch (extend_)
99 {
100 case Extend::CONSTANT:
101 return (t < t0) ? y0 : yn;
102
104 {
105 if (t < t0)
106 return dy_dt(t0) * (t - t0) + y0;
107 else
108 return dy_dt(tn) * (t - tn) + yn;
109 }
110
112 offset = floor((t - t0) / (tn - t0)) * (yn - y0);
113 // NOTE: fallthrough
114 [[fallthrough]]; // suppress warning
115
116 case Extend::REPEAT:
117 {
118 double t_mod = std::fmod(t - t0, tn - t0);
119 t_mod += (t_mod < 0) ? tn : t0;
120 return eval(t_mod) + offset;
121 }
122
123 default:
124 assert(false);
125 return 0;
126 }
127 }
128
129 double PiecewiseInterpolation::dy_dt(const double t) const
130 {
131 assert(t >= points_.front() && t <= points_.back());
132
133 for (size_t i = 0; i < points_.size() - 1; ++i)
134 {
135 if (t >= points_[i] && t <= points_[i + 1])
136 {
137 return dy_dt_piece(t, i);
138 }
139 }
140
141 assert(points_.size() == 1);
142 return 0;
143 }
144
145 double PiecewiseLinearInterpolation::eval_piece(const double t, const int i) const
146 {
147 const double alpha = (t - points_[i]) / (points_[i + 1] - points_[i]);
148 return (values_[i + 1] - values_[i]) * alpha + values_[i];
149 }
150
151 double PiecewiseLinearInterpolation::dy_dt_piece(const double t, const int i) const
152 {
153 return (values_[i + 1] - values_[i]) / (points_[i + 1] - points_[i]);
154 }
155
157 {
159
160 // N+1 points ⟹ N cubic functions of the form fᵢ(x) = aᵢt³ + bᵢt² + cᵢt + dᵢ
161 // ⟹ 4N unknowns
162 const int N = points_.size() - 1;
163 Eigen::MatrixXd A = Eigen::MatrixXd::Zero(4 * N, 4 * N);
164 Eigen::VectorXd b = Eigen::VectorXd::Zero(4 * N);
165
166 // 2N equations: fᵢ(tᵢ) = yᵢ and fᵢ(tᵢ₊₁) = yᵢ₊₁
167 for (int i = 0; i < N; i++)
168 {
169 for (int j = 0; j < 2; j++)
170 {
171 // aᵢt³ + bᵢt² + cᵢt + dᵢ = yᵢ (yᵢ₊₁) at tᵢ (tᵢ₊₁)
172 const double t = points_[i + j];
173
174 A(2 * i + j, 4 * i + 0) = pow(t, 3);
175 A(2 * i + j, 4 * i + 1) = pow(t, 2);
176 A(2 * i + j, 4 * i + 2) = t;
177 A(2 * i + j, 4 * i + 3) = 1;
178
179 b(2 * i + j) = values_[i + j];
180 }
181 }
182 int offset = 2 * N;
183
184 // N - 1 equations: fᵢ'(tᵢ) = fᵢ₊₁'(tᵢ₊₁) for i = 1, ... , N - 1
185 // fᵢ'(t) = 3aᵢt² + 2bᵢt + cᵢ
186 for (int i = 0; i < N - 1; i++)
187 {
188 // 3aᵢt² + 2bᵢt + cᵢ - 3aᵢ₊₁t² - 2bᵢ₊₁t - cᵢ₊₁ = 0 at tᵢ₊₁
189 const double t = points_[i + 1];
190
191 A(offset + i, 4 * i + 0) = 3 * pow(t, 2);
192 A(offset + i, 4 * i + 1) = 2 * t;
193 A(offset + i, 4 * i + 2) = 1;
194
195 A.block<1, 3>(offset + i, 4 * (i + 1)) = -A.block<1, 3>(offset + i, 4 * i);
196 }
197 offset += N - 1;
198
199 // N - 1 equations: fᵢ"(tᵢ) = fᵢ₊₁"(tᵢ₊₁) for i = 1, ... , N - 1
200 // fᵢ"(t) = 6aᵢt + 2bᵢ
201 for (int i = 0; i < N - 1; i++)
202 {
203 // 6aᵢt + 2bᵢ - 6aᵢ₊₁t - 2bᵢ₊₁ = 0 at tᵢ₊₁
204 const double t = points_[i + 1];
205
206 A(offset + i, 4 * i + 0) = 6 * t;
207 A(offset + i, 4 * i + 1) = 2;
208
209 A.block<1, 2>(offset + i, 4 * (i + 1)) = -A.block<1, 2>(offset + i, 4 * i);
210 }
211 offset += N - 1;
212
213 // Boundary Conditions:
215 {
216 // Custom Spline
217 // f₀'(t₀) = 3a₀t₀² + 2b₀t + c₀ = 0
218 A(offset, 0) = 3 * pow(points_[0], 2);
219 A(offset, 1) = 2 * points_[0];
220 A(offset, 2) = 1;
221 offset++;
222
223 // fₙ₋₁"(tₙ) = 6aₙ₋₁tₙ + 2bₙ₋₁ = 0
224 A(offset, 4 * (N - 1) + 0) = 3 * pow(points_[N], 2);
225 A(offset, 4 * (N - 1) + 1) = 2 * points_[N];
226 A(offset, 4 * (N - 1) + 2) = 1;
227 offset++;
228 }
229 else if (extend_ == Extend::EXTRAPOLATE)
230 {
231 // Natural Spline
232 // f₀"(t₀) = 6a₀t₀ + 2b₀ = 0
233 A(offset, 0) = 6 * points_[0];
234 A(offset, 1) = 2;
235 offset++;
236
237 // fₙ₋₁"(tₙ) = 6aₙ₋₁tₙ + 2bₙ₋₁ = 0
238 A(offset, 4 * (N - 1) + 0) = 6 * points_[N];
239 A(offset, 4 * (N - 1) + 1) = 2;
240 offset++;
241 }
242 else
243 {
244 // Periodic Spline
246
247 // f₀'(t₀) = fₙ₋₁'(tₙ) and f₀"(t₀) = fₙ₋₁"(tₙ)
248 A(offset, 0) = 3 * pow(points_[0], 2);
249 A(offset, 1) = 2 * points_[0];
250 A(offset, 2) = 1;
251 A(offset, 4 * (N - 1) + 0) = -3 * pow(points_[N], 2);
252 A(offset, 4 * (N - 1) + 1) = -2 * points_[N];
253 A(offset, 4 * (N - 1) + 2) = -1;
254 offset++;
255
256 A(offset, 0) = 6 * points_[0];
257 A(offset, 1) = 2;
258 A(offset, 4 * (N - 1) + 0) = -6 * points_[N];
259 A(offset, 4 * (N - 1) + 1) = -2;
260 offset++;
261 }
262
263 assert(offset == 4 * N);
264
265 // Solve the system of linear equations
266 coeffs_ = A.lu().solve(b);
267 // unflatten coeffs so each row corresponds to a cubic function
269 assert(coeffs_.rows() == N);
270 }
271
272 double PiecewiseCubicInterpolation::eval_piece(const double t, const int i) const
273 {
274 // fᵢ(t) = aᵢt³ + bᵢt² + cᵢt + dᵢ
275 return ((coeffs_(i, 0) * t + coeffs_(i, 1)) * t + coeffs_(i, 2)) * t + coeffs_(i, 3);
276 }
277
278 double PiecewiseCubicInterpolation::dy_dt_piece(const double t, const int i) const
279 {
280 // fᵢ'(t) = 3aᵢt² + 2bᵢt + cᵢ
281 return (3 * coeffs_(i, 0) * t + 2 * coeffs_(i, 1)) * t + coeffs_(i, 2);
282 }
283
284} // namespace polyfem::utils
static std::shared_ptr< Interpolation > build(const json &params)
void init(const json &params) override
double eval(const double t) const override
double eval_piece(const double t, const int i) const override
void init(const json &params) override
double dy_dt_piece(const double t, const int i) const override
virtual double eval_piece(const double t, const int i) const =0
double eval(const double t) const override
virtual void init(const json &params) override
double dy_dt(const double t) const
double extend(const double t) const
virtual double dy_dt_piece(const double t, const int i) const =0
double eval_piece(const double t, const int i) const override
double dy_dt_piece(const double t, const int i) const override
NLOHMANN_JSON_SERIALIZE_ENUM(CollisionProxyTessellation, {{CollisionProxyTessellation::REGULAR, "regular"}, {CollisionProxyTessellation::IRREGULAR, "irregular"}})
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:71