Easy3D 2.6.1
Loading...
Searching...
No Matches
spline_interpolation.h
1/********************************************************************
2 * Copyright (C) 2015 Liangliang Nan <liangliang.nan@gmail.com>
3 * https://3d.bk.tudelft.nl/liangliang/
4 *
5 * This file is part of Easy3D. If it is useful in your research/work,
6 * I would be grateful if you show your appreciation by citing it:
7 * ------------------------------------------------------------------
8 * Liangliang Nan.
9 * Easy3D: a lightweight, easy-to-use, and efficient C++ library
10 * for processing and rendering 3D data.
11 * Journal of Open Source Software, 6(64), 3255, 2021.
12 * ------------------------------------------------------------------
13 *
14 * Easy3D is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License Version 3
16 * as published by the Free Software Foundation.
17 *
18 * Easy3D is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ********************************************************************/
26
27#ifndef EASY3D_CORE_SPLINE_INTERPOLATION_H
28#define EASY3D_CORE_SPLINE_INTERPOLATION_H
29
30#include <cstdio>
31#include <cassert>
32#include <vector>
33#include <algorithm>
34
35#include <easy3d/util/logging.h>
36
37
38namespace easy3d {
39
85 template<typename FT>
87 public:
93
94 public:
98 left_value_(0), right_value_(0),
99 linear_extrapolation_(false) {
100 }
101
104 void set_boundary(BoundaryType left, FT left_value,
105 BoundaryType right, FT right_value,
106 bool linear_extrapolation = false);
107
111 void set_data(const std::vector<FT> &x, const std::vector<FT> &y, bool cubic_spline = true);
112
114 FT operator()(FT x) const;
115
117 FT derivative(int order, FT x) const;
118
119 private:
120 std::vector<FT> x_, y_; // x,y coordinates of points
121 // interpolation parameters
122 // f(x) = a*(x-x_i)^3 + b*(x-x_i)^2 + c*(x-x_i) + y_i
123 std::vector<FT> a_, b_, c_; // spline coefficients
124 FT b0_, c0_; // for left extrapolation
125 BoundaryType left_, right_;
126 FT left_value_, right_value_;
127 bool linear_extrapolation_;
128 };
129
130
131
132 // \cond
139 template<typename FT>
140 class BandMatrix {
141 public:
143 BandMatrix() = default;
145 BandMatrix(int dim, int n_u, int n_l);
147 ~BandMatrix() = default;
148
149 void resize(int dim, int n_u, int n_l); // init with dim,n_u,n_l
150 int dim() const; // matrix dimension
151
152 int num_upper() const { return m_upper.size() - 1; }
153 int num_lower() const { return m_lower.size() - 1; }
154
156 FT &operator()(int i, int j); // write
158 const FT &operator()(int i, int j) const; // read
159
161 FT &saved_diag(int i);
162 const FT &saved_diag(int i) const;
163
165 void lu_decompose();
166
168 std::vector<FT> r_solve(const std::vector<FT> &b) const;
170 std::vector<FT> l_solve(const std::vector<FT> &b) const;
171 std::vector<FT> lu_solve(const std::vector<FT> &b, bool is_lu_decomposed = false);
172
173 private:
174 std::vector<std::vector<FT>> m_upper; // upper band
175 std::vector<std::vector<FT>> m_lower; // lower band
176
177 }; // BandMatrix
178 // \endcond
179
180
181 // Cubic spline implementation
182 // -----------------------
183
184 template<typename FT>
186 typename SplineInterpolation<FT>::BoundaryType right, FT right_value,
187 bool linear_extrapolation) {
188 assert(x_.size() == 0); // set_points() must not have happened yet
189 left_ = left;
190 right_ = right;
191 left_value_ = left_value;
192 right_value_ = right_value;
193 linear_extrapolation_ = linear_extrapolation;
194 }
195
196 template<typename FT>
197 void SplineInterpolation<FT>::set_data(const std::vector<FT> &x, const std::vector<FT> &y, bool cubic_spline) {
198 if (x.size() != y.size()) {
199 LOG(ERROR) << "sizes of x (" << x.size() << ") and y (" << y.size() << ") do not match";
200 return;
201 }
202 if (x.size() < 3) {
203 LOG(ERROR) << "too few data (size of x: " << x.size() << ")";
204 return;
205 }
206 x_ = x;
207 y_ = y;
208 const int n = static_cast<int>(x.size());
209 // TODO: maybe sort x and y, rather than returning an error
210 for (std::size_t i = 0; i < n - 1; i++) {
211 if (x_[i] >= x_[i + 1]) {
212 LOG_N_TIMES(3, ERROR) << "x has to be monotonously increasing (x[" << i << "]=" << x_[i] << ", x[" << i + 1 << "]=" << x_[i + 1] << "). " << COUNTER;
213 return;
214 }
215 }
216
217 if (cubic_spline) { // cubic spline interpolation
218 // setting up the matrix and right-hand side of the equation system
219 // for the parameters b[]
220 BandMatrix<FT> A(n, 1, 1);
221 std::vector<FT> rhs(n);
222 for (int i = 1; i < n - 1; i++) {
223 A(i, i - 1) = FT(1.0 / 3.0) * (x[i] - x[i - 1]);
224 A(i, i) = FT(2.0 / 3.0) * (x[i + 1] - x[i - 1]);
225 A(i, i + 1) = FT(1.0 / 3.0) * (x[i + 1] - x[i]);
226 rhs[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
227 }
228 // boundary conditions
230 // 2*b[0] = f''
231 A(0, 0) = FT(2.0);
232 A(0, 1) = FT(0.0);
233 rhs[0] = left_value_;
234 } else if (left_ == SplineInterpolation<FT>::first_deriv) {
235 // c[0] = f', needs to be re-expressed in terms of b:
236 // (2b[0]+b[1])(x[1]-x[0]) = 3 ((y[1]-y[0])/(x[1]-x[0]) - f')
237 A(0, 0) = FT(2.0) * (x[1] - x[0]);
238 A(0, 1) = FT(1.0) * (x[1] - x[0]);
239 rhs[0] = FT(3.0) * ((y[1] - y[0]) / (x[1] - x[0]) - left_value_);
240 } else {
241 assert(false);
242 }
244 // 2*b[n-1] = f''
245 A(n - 1, n - 1) = FT(2.0);
246 A(n - 1, n - 2) = FT(0.0);
247 rhs[n - 1] = right_value_;
248 } else if (right_ == SplineInterpolation<FT>::first_deriv) {
249 // c[n-1] = f', needs to be re-expressed in terms of b:
250 // (b[n-2]+2b[n-1])(x[n-1]-x[n-2])
251 // = 3 (f' - (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))
252 A(n - 1, n - 1) = FT(2.0) * (x[n - 1] - x[n - 2]);
253 A(n - 1, n - 2) = FT(1.0) * (x[n - 1] - x[n - 2]);
254 rhs[n - 1] = FT(3.0) * (right_value_ - (y[n - 1] - y[n - 2]) / (x[n - 1] - x[n - 2]));
255 } else {
256 assert(false);
257 }
258
259 // solve the equation system to obtain the parameters b[]
260 b_ = A.lu_solve(rhs);
261
262 // calculate parameters a[] and c[] based on b[]
263 a_.resize(n);
264 c_.resize(n);
265 for (std::size_t i = 0; i < n - 1; i++) {
266 a_[i] = FT(1.0 / 3.0) * (b_[i + 1] - b_[i]) / (x[i + 1] - x[i]);
267 c_[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i])
268 - FT(1.0 / 3.0) * (FT(2.0) * b_[i] + b_[i + 1]) * (x[i + 1] - x[i]);
269 }
270 } else { // linear interpolation
271 a_.resize(n);
272 b_.resize(n);
273 c_.resize(n);
274 for (std::size_t i = 0; i < n - 1; i++) {
275 a_[i] = FT(0.0);
276 b_[i] = FT(0.0);
277 c_[i] = (y_[i + 1] - y_[i]) / (x_[i + 1] - x_[i]);
278 }
279 }
280
281 // for left extrapolation coefficients
282 b0_ = linear_extrapolation_ ? FT(0.0) : b_[0];
283 c0_ = c_[0];
284
285 // for the right extrapolation coefficients
286 // f_{n-1}(x) = b*(x-x_{n-1})^2 + c*(x-x_{n-1}) + y_{n-1}
287 FT h = x[n - 1] - x[n - 2];
288 // b_[n-1] is determined by the boundary condition
289 a_[n - 1] = FT(0.0);
290 c_[n - 1] = FT(3.0) * a_[n - 2] * h * h + FT(2.0) * b_[n - 2] * h + c_[n - 2]; // = f'_{n-2}(x_{n-1})
291 if (linear_extrapolation_)
292 b_[n - 1] = FT(0.0);
293 }
294
295 template<typename FT>
297 size_t n = x_.size();
298 // find the closest point x_[idx] < x, idx=0 even if x<x_[0]
299 typename std::vector<FT>::const_iterator it;
300 it = std::lower_bound(x_.begin(), x_.end(), x);
301 int idx = std::max(int(it - x_.begin()) - 1, 0);
302
303 FT h = x - x_[idx];
304 FT interpol;
305 if (x < x_[0]) {
306 // extrapolation to the left
307 interpol = (b0_ * h + c0_) * h + y_[0];
308 } else if (x > x_[n - 1]) {
309 // extrapolation to the right
310 interpol = (b_[n - 1] * h + c_[n - 1]) * h + y_[n - 1];
311 } else {
312 // interpolation
313 interpol = ((a_[idx] * h + b_[idx]) * h + c_[idx]) * h + y_[idx];
314 }
315 return interpol;
316 }
317
318 template<typename FT>
319 FT SplineInterpolation<FT>::derivative(int order, FT x) const {
320 assert(order > 0);
321
322 size_t n = x_.size();
323 // find the closest point x_[idx] < x, idx=0 even if x<x_[0]
324 typename std::vector<FT>::const_iterator it;
325 it = std::lower_bound(x_.begin(), x_.end(), x);
326 int idx = std::max(int(it - x_.begin()) - 1, 0);
327
328 FT h = x - x_[idx];
329 FT interpol;
330 if (x < x_[0]) {
331 // extrapolation to the left
332 switch (order) {
333 case 1:
334 interpol = FT(2.0) * b0_ * h + c0_;
335 break;
336 case 2:
337 interpol = FT(2.0) * b0_ * h;
338 break;
339 default:
340 interpol = FT(0.0);
341 break;
342 }
343 } else if (x > x_[n - 1]) {
344 // extrapolation to the right
345 switch (order) {
346 case 1:
347 interpol = FT(2.0) * b_[n - 1] * h + c_[n - 1];
348 break;
349 case 2:
350 interpol = FT(2.0) * b_[n - 1];
351 break;
352 default:
353 interpol = FT(0.0);
354 break;
355 }
356 } else {
357 // interpolation
358 switch (order) {
359 case 1:
360 interpol = (FT(3.0) * a_[idx] * h + FT(2.0) * b_[idx]) * h + c_[idx];
361 break;
362 case 2:
363 interpol = FT(6.0) * a_[idx] * h + FT(2.0) * b_[idx];
364 break;
365 case 3:
366 interpol = FT(6.0) * a_[idx];
367 break;
368 default:
369 interpol = FT(0.0);
370 break;
371 }
372 }
373 return interpol;
374 }
375
376
377 // \cond
378 // BandMatrix implementation
379 // -------------------------
380
381 template<typename FT>
382 BandMatrix<FT>::BandMatrix(int dim, int n_u, int n_l) {
383 resize(dim, n_u, n_l);
384 }
385
386 template<typename FT>
387 void BandMatrix<FT>::resize(int dim, int n_u, int n_l) {
388 assert(dim > 0);
389 assert(n_u >= 0);
390 assert(n_l >= 0);
391 m_upper.resize(n_u + 1);
392 m_lower.resize(n_l + 1);
393 for (size_t i = 0; i < m_upper.size(); i++) {
394 m_upper[i].resize(dim);
395 }
396 for (size_t i = 0; i < m_lower.size(); i++) {
397 m_lower[i].resize(dim);
398 }
399 }
400
401 template<typename FT>
402 int BandMatrix<FT>::dim() const {
403 if (m_upper.size() > 0) {
404 return m_upper[0].size();
405 } else {
406 return 0;
407 }
408 }
409
410
411 // defines the new operator (), so that we can access the elements
412 // by A(i,j), index going from i=0,...,dim()-1
413 template<typename FT>
414 FT &BandMatrix<FT>::operator()(int i, int j) {
415 int k = j - i; // what band is the entry
416 assert((i >= 0) && (i < dim()) && (j >= 0) && (j < dim()));
417 assert((-num_lower() <= k) && (k <= num_upper()));
418 // k=0 -> diagonal, k<0 lower left part, k>0 upper right part
419 if (k >= 0) return m_upper[k][i];
420 else return m_lower[-k][i];
421 }
422
423 template<typename FT>
424 const FT &BandMatrix<FT>::operator()(int i, int j) const {
425 int k = j - i; // what band is the entry
426 assert((i >= 0) && (i < dim()) && (j >= 0) && (j < dim()));
427 assert((-num_lower() <= k) && (k <= num_upper()));
428 // k=0 -> diagonal, k<0 lower left part, k>0 upper right part
429 if (k >= 0) return m_upper[k][i];
430 else return m_lower[-k][i];
431 }
432
433 // second diag (used in LU decomposition), saved in m_lower
434 template<typename FT>
435 const FT &BandMatrix<FT>::saved_diag(int i) const {
436 assert((i >= 0) && (i < dim()));
437 return m_lower[0][i];
438 }
439
440 template<typename FT>
441 FT &BandMatrix<FT>::saved_diag(int i) {
442 assert((i >= 0) && (i < dim()));
443 return m_lower[0][i];
444 }
445
446 // LR-Decomposition of a band matrix
447 template<typename FT>
448 void BandMatrix<FT>::lu_decompose() {
449 int i_max, j_max;
450 int j_min;
451 FT x;
452
453 // preconditioning
454 // normalize column i so that a_ii=1
455 for (int i = 0; i < this->dim(); i++) {
456 assert(this->operator()(i, i) != 0.0);
457 this->saved_diag(i) = FT(1.0) / this->operator()(i, i);
458 j_min = std::max(0, i - this->num_lower());
459 j_max = std::min(this->dim() - 1, i + this->num_upper());
460 for (int j = j_min; j <= j_max; j++) {
461 this->operator()(i, j) *= this->saved_diag(i);
462 }
463 this->operator()(i, i) = FT(1.0); // prevents rounding errors
464 }
465
466 // Gauss LR-Decomposition
467 for (int k = 0; k < this->dim(); k++) {
468 i_max = std::min(this->dim() - 1, k + this->num_lower()); // num_lower not a mistake!
469 for (int i = k + 1; i <= i_max; i++) {
470 assert(this->operator()(k, k) != FT(0.0));
471 x = -this->operator()(i, k) / this->operator()(k, k);
472 this->operator()(i, k) = -x; // assembly part of L
473 j_max = std::min(this->dim() - 1, k + this->num_upper());
474 for (int j = k + 1; j <= j_max; j++) {
475 // assembly part of R
476 this->operator()(i, j) = this->operator()(i, j) + x * this->operator()(k, j);
477 }
478 }
479 }
480 }
481
482 // solves Ly=b
483 template<typename FT>
484 std::vector<FT> BandMatrix<FT>::l_solve(const std::vector<FT> &b) const {
485 assert(this->dim() == (int) b.size());
486 std::vector<FT> x(this->dim());
487 int j_start;
488 FT sum;
489 for (int i = 0; i < this->dim(); i++) {
490 sum = FT(0);
491 j_start = std::max(0, i - this->num_lower());
492 for (int j = j_start; j < i; j++) sum += this->operator()(i, j) * x[j];
493 x[i] = (b[i] * this->saved_diag(i)) - sum;
494 }
495 return x;
496 }
497
498 // solves Rx=y
499 template<typename FT>
500 std::vector<FT> BandMatrix<FT>::r_solve(const std::vector<FT> &b) const {
501 assert(this->dim() == (int) b.size());
502 std::vector<FT> x(this->dim());
503 int j_stop;
504 FT sum;
505 for (int i = this->dim() - 1; i >= 0; i--) {
506 sum = 0;
507 j_stop = std::min(this->dim() - 1, i + this->num_upper());
508 for (int j = i + 1; j <= j_stop; j++) sum += this->operator()(i, j) * x[j];
509 x[i] = (b[i] - sum) / this->operator()(i, i);
510 }
511 return x;
512 }
513
514 template<typename FT>
515 std::vector<FT> BandMatrix<FT>::lu_solve(const std::vector<FT> &b, bool is_lu_decomposed) {
516 assert(this->dim() == (int) b.size());
517 std::vector<FT> x, y;
518 if (!is_lu_decomposed) {
519 this->lu_decompose();
520 }
521 y = this->l_solve(b);
522 x = this->r_solve(y);
523 return x;
524 }
525 // \endcond
526
527}
528
529
530#endif //EASY3D_CORE_SPLINE_INTERPOLATION_H
SplineInterpolation()
Definition spline_interpolation.h:97
void set_data(const std::vector< FT > &x, const std::vector< FT > &y, bool cubic_spline=true)
Definition spline_interpolation.h:197
FT operator()(FT x) const
Evaluates the spline at x.
Definition spline_interpolation.h:296
FT derivative(int order, FT x) const
Returns the order -th derivative of the spline at x.
Definition spline_interpolation.h:319
void set_boundary(BoundaryType left, FT left_value, BoundaryType right, FT right_value, bool linear_extrapolation=false)
Definition spline_interpolation.h:185
BoundaryType
Boundary condition type.
Definition spline_interpolation.h:89
@ first_deriv
first derivative
Definition spline_interpolation.h:90
@ second_deriv
second derivative
Definition spline_interpolation.h:91
Definition collider.cpp:182
std::vector< FT > sum(const Matrix< FT > &)
Definition matrix.h:1485