Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
splines.cpp
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2019 Heiko Bauke.
4 Copyright (C) 2019-2020 Pascal Obry.
5
6 darktable is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 darktable is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with darktable. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "math/splines.h"
21#include <algorithm>
22#include <cmath>
23#include <limits>
24#include <stdexcept>
25#include <tuple>
26#include <vector>
27
28namespace interpol
29{
30
31template <typename T> struct point
32{
33 T x{ 0 }; // knot x position
34 T y{ 0 }; // function value at x
35 point() = default;
36 point(T x_, T y_) : x{ x_ }, y{ y_ }
37 {
38 }
39};
40
41template <typename T> struct base_point
42{
43 T x{ 0 }; // knot x position
44 T y{ 0 }; // function value at x
45 T dy{ 0 }; // 1st derivative of interpolating spline at x
46};
47
48template <typename T> struct limits
49{
50 T min{ -std::numeric_limits<T>::infinity() };
51 T max{ +std::numeric_limits<T>::infinity() };
52 limits() = default;
53 limits(T min_, T max_) : min{ std::min(min_, max_) }, max{ std::max(min_, max_) }
54 {
55 }
56};
57
58template <typename T> constexpr limits<T> infinity()
59{
60 return limits<T>{};
61}
62
63template <typename T> class spline_base
64{
65protected:
66 using size_type = typename std::vector<base_point<T> >::size_type;
67 std::vector<base_point<T> > points;
70 bool periodic{ false };
71
72 template <typename iter> spline_base(iter i_begin, iter i_end)
73 {
74 for(iter i{ i_begin }; i != i_end; ++i) points.push_back({ i->x, i->y, 0 });
75 if(points.empty()) throw std::invalid_argument("empty set of interpolation points");
76 std::sort(points.begin(), points.end(),
77 [](const base_point<T> &a, const base_point<T> &b) { return a.x < b.x; });
78 x_lim = { points.front().x, points.back().x };
79 }
80
81 template <typename iter>
82 spline_base(iter i_begin, iter i_end, const limits<T> &x_lim_, const limits<T> &y_lim_, bool periodic_ = false)
83 : x_lim{ x_lim_ }, y_lim{ y_lim_ }, periodic{ periodic_ }
84 {
85 if(periodic)
86 {
87 const T period{ x_lim.max - x_lim.min };
88 for(iter i{ i_begin }; i != i_end; ++i)
89 {
90 T x{ std::fmod(i->x, period) };
91 if(x < 0) x += period;
92 points.push_back({ x, i->y, 0 });
93 }
94 }
95 else
96 {
97 for(iter i{ i_begin }; i != i_end; ++i)
98 {
99 if(x_lim.min <= i->x and i->x <= x_lim.max) points.push_back({ i->x, i->y, 0 });
100 }
101 }
102 if(points.empty()) throw std::invalid_argument("empty set of interpolation points");
103 std::sort(points.begin(), points.end(),
104 [](const base_point<T> &a, const base_point<T> &b) { return a.x < b.x; });
105 }
106
107 spline_base(const std::initializer_list<point<T> > &I) : spline_base(I.begin(), I.end())
108 {
109 }
110
111 spline_base(const std::initializer_list<point<T> > &I, const limits<T> &x_lim_, const limits<T> &y_lim_,
112 bool periodic_ = false)
113 : spline_base(I.begin(), I.end(), x_lim_, y_lim_, periodic_)
114 {
115 }
116
117public:
118 T operator()(T x) const
119 {
120 if(points.size() == 1) return points[0].y;
121 size_type n0{ 0 };
122 size_type n1{ 0 };
123 T h{ 0 };
124 // find the knot indices n0 and n1 for value x
125 if(periodic)
126 {
127 const T period{ x_lim.max - x_lim.min };
128 x = std::fmod(x, period);
129 if(x < points.front().x) x += period;
130 n0 = std::upper_bound(points.begin(), points.end(), base_point<T>{ x, 0, 0 },
131 [](const base_point<T> &a, const base_point<T> &b) { return a.x < b.x; })
132 - points.begin();
133 n0 = n0 > 0 ? n0 - 1 : points.size() - 1;
134 n1 = n0 + 1 < points.size() ? n0 + 1 : 0;
135 if(n1 > n0)
136 h = points[n1].x - points[n0].x;
137 else
138 h = points[n1].x - (points[n0].x - period);
139 }
140 else
141 {
142 x = std::max(x, x_lim.min);
143 x = std::min(x, x_lim.max);
144 if(x >= points.front().x)
145 {
146 n0 = std::upper_bound(points.begin(), points.end(), base_point<T>{ x, 0, 0 },
147 [](const base_point<T> &a, const base_point<T> &b) { return a.x < b.x; })
148 - points.begin();
149 if(n0 > 0) n0 = std::min(n0 - 1, points.size() - 2);
150 }
151 n1 = n0 + 1;
152 h = points[n1].x - points[n0].x;
153 }
154 T y;
155 if((not periodic) and (x <= points.front().x or x >= points.back().x))
156 {
157 // use linear extrapolation for off-grid points
158 const base_point<T> &P{ x <= points.front().x ? points.front() : points.back() };
159 y = P.y + (x - P.x) * P.dy;
160 }
161 else
162 {
163 const T dx{ (x - points[n0].x) / h };
164 const T dx2{ dx * dx };
165 const T dx3{ dx2 * dx };
166 // calculate the 4 cubic Hermite splines, see
167 // https://en.wikipedia.org/wiki/Cubic_Hermite_spline
168 const T h00{ 2 * dx3 - 3 * dx2 + 1 };
169 const T h10{ dx3 - 2 * dx2 + dx };
170 const T h01{ -2 * dx3 + 3 * dx2 };
171 const T h11{ dx3 - dx2 };
172 // finally get the interpolation value as a weighted sum of h00, h10, h01 and h11
173 y = h00 * points[n0].y + h10 * h * points[n0].dy + h01 * points[n1].y + h11 * h * points[n1].dy;
174 }
175 y = std::max(y, y_lim.min);
176 y = std::min(y, y_lim.max);
177 return y;
178 }
179};
180
181// cubic hermite spline interpolation
182// tangents at the interpolation points given by with central difference formula,
183// see https://en.wikipedia.org/wiki/Cubic_Hermite_spline
184// https://de.wikipedia.org/wiki/Kubisch_Hermitescher_Spline
185template <typename T> class Catmull_Rom_spline : public spline_base<T>
186{
188 using base::periodic;
189 using base::points;
190 using base::x_lim;
191 using base::y_lim;
192 using typename base::size_type;
193
194 void init()
195 {
196 if(points.size() == 1)
197 points[0].dy = 0;
198 else
199 {
200 const size_type N{ points.size() };
201 if(periodic)
202 {
203 const T period{ x_lim.max - x_lim.min };
204 points[0].dy = (points[1].y - points[N - 1].y) / (points[1].x - points[N - 1].x + period);
205 for(size_type i{ 1 }; i < N - 1; ++i)
206 points[i].dy = (points[i + 1].y - points[i - 1].y) / (points[i + 1].x - points[i - 1].x);
207 points[N - 1].dy = (points[0].y - points[N - 2].y) / (points[0].x - points[N - 2].x + period);
208 }
209 else
210 {
211 points[0].dy = (points[1].y - points[0].y) / (points[1].x - points[0].x);
212 for(size_type i{ 1 }; i < N - 1; ++i)
213 points[i].dy = (points[i + 1].y - points[i - 1].y) / (points[i + 1].x - points[i - 1].x);
214 points[N - 1].dy = (points[N - 1].y - points[N - 2].y) / (points[N - 1].x - points[N - 2].x);
215 }
216 }
217 }
218
219public:
220 template <typename iter>
221 Catmull_Rom_spline(iter i_begin, iter i_end) : spline_base<T>::spline_base(i_begin, i_end)
222 {
223 init();
224 }
225
226 template <typename iter>
227 Catmull_Rom_spline(iter i_begin, iter i_end, const limits<T> &x_lim_, const limits<T> &y_lim_,
228 bool periodic_ = false)
229 : spline_base<T>::spline_base(i_begin, i_end, x_lim_, y_lim_, periodic_)
230 {
231 init();
232 }
233
234 Catmull_Rom_spline(const std::initializer_list<point<T> > &I) : spline_base<T>::spline_base(I)
235 {
236 init();
237 }
238
239 Catmull_Rom_spline(const std::initializer_list<point<T> > &I, const limits<T> &x_lim_, const limits<T> &y_lim_,
240 bool periodic_ = false)
241 : spline_base<T>::spline_base(I, x_lim_, y_lim_, periodic_)
242 {
243 init();
244 }
245};
246
247// cubic hermite spline interpolation
248// tangents at the interpolation points are determined such that the resulting
249// interpolating function is monotonous between successive interpolation points,
250// see https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
251template <typename T> class monotone_hermite_spline : public spline_base<T>
252{
254 using base::periodic;
255 using base::points;
256 using base::x_lim;
257 using base::y_lim;
258 using typename base::size_type;
259
260 void init()
261 {
262 if(points.size() == 1)
263 points[0].dy = 0;
264 else
265 {
266 const size_type N{ points.size() };
267 if(periodic)
268 {
269 const T period{ x_lim.max - x_lim.min };
270 std::vector<T> Delta;
271 Delta.reserve(N);
272 for(size_type i{ 0 }; i < N - 1; ++i)
273 Delta.push_back((points[i + 1].y - points[i].y) / (points[i + 1].x - points[i].x));
274 Delta.push_back((points[0].y - points[N - 1].y) / (points[0].x - points[N - 1].x + period));
275 if(Delta[N - 1] * Delta[0] <= 0)
276 points[0].dy = 0;
277 else
278 points[0].dy = (Delta[N - 1] + Delta[0]) / 2;
279 for(size_type i{ 1 }; i < N; ++i)
280 if(Delta[i - 1] * Delta[i] <= 0)
281 points[i].dy = 0;
282 else
283 points[i].dy = (Delta[i - 1] + Delta[i]) / 2;
284 for(size_type i{ 0 }; i < N; ++i)
285 {
286 const size_type i_1{ i + 1 < N ? i + 1 : 0 };
287 if(std::abs(Delta[i]) < std::numeric_limits<T>::epsilon())
288 points[i].dy = points[i_1].dy = 0;
289 else
290 {
291 const T alpha{ points[i].dy / Delta[i] };
292 const T beta{ points[i_1].dy / Delta[i] };
293 const T tau{ alpha * alpha + beta * beta };
294 if(tau > 9)
295 {
296 points[i].dy = 3 * alpha * Delta[i] / std::sqrt(tau);
297 points[i_1].dy = 3 * beta * Delta[i] / std::sqrt(tau);
298 }
299 }
300 }
301 }
302 else
303 {
304 std::vector<T> Delta;
305 Delta.reserve(N - 1);
306 for(size_type i{ 0 }; i < N - 1; ++i)
307 Delta.push_back((points[i + 1].y - points[i].y) / (points[i + 1].x - points[i].x));
308 points[0].dy = Delta[0];
309 for(size_type i{ 1 }; i < N - 1; ++i)
310 if(Delta[i - 1] * Delta[i] <= 0)
311 points[i].dy = 0;
312 else
313 points[i].dy = (Delta[i - 1] + Delta[i]) / 2;
314 if(N >= 2) points[N - 1].dy = Delta[N - 2];
315 for(size_type i{ 0 }; i < N - 1; ++i)
316 if(std::abs(Delta[i]) < std::numeric_limits<T>::epsilon())
317 points[i].dy = points[i + 1].dy = 0;
318 else
319 {
320 const T alpha{ points[i].dy / Delta[i] };
321 const T beta{ points[i + 1].dy / Delta[i] };
322 const T tau{ alpha * alpha + beta * beta };
323 if(tau > 9)
324 {
325 points[i].dy = 3 * alpha * Delta[i] / std::sqrt(tau);
326 points[i + 1].dy = 3 * beta * Delta[i] / std::sqrt(tau);
327 }
328 }
329 }
330 }
331 }
332
333public:
334 template <typename iter>
335 monotone_hermite_spline(iter i_begin, iter i_end) : spline_base<T>::spline_base(i_begin, i_end)
336 {
337 init();
338 }
339
340 template <typename iter>
341 monotone_hermite_spline(iter i_begin, iter i_end, const limits<T> &x_lim_, const limits<T> &y_lim_,
342 bool periodic_ = false)
343 : spline_base<T>::spline_base(i_begin, i_end, x_lim_, y_lim_, periodic_)
344 {
345 init();
346 }
347
348 monotone_hermite_spline(const std::initializer_list<point<T> > &I) : spline_base<T>::spline_base(I)
349 {
350 init();
351 }
352
353 monotone_hermite_spline(const std::initializer_list<point<T> > &I, const limits<T> &x_lim_,
354 const limits<T> &y_lim_, bool periodic_ = false)
355 : spline_base<T>::spline_base(I, x_lim_, y_lim_, periodic_)
356 {
357 init();
358 }
359};
360
361// cubic hermite spline interpolation
362// tangents at the interpolation points are determined such that the resulting
363// interpolating function is monotonous between successive interpolation points,
364// see SIAM J. Sci. Stat. Comput., Vol. 5, pp. 300-304 (1984)
365// https://doi.org/10.1137/0905021
366// gives similar but sometimes more pleasing results than
367// monotone_hermite_spline above
368template <typename T> class monotone_hermite_spline_variant : public spline_base<T>
369{
371 using base::periodic;
372 using base::points;
373 using base::x_lim;
374 using base::y_lim;
375 using typename base::size_type;
376
377 static T G(const T S1, const T S2, const T h1, const T h2)
378 {
379 if(S1 * S2 > 0)
380 {
381 const T alpha{ (h1 + 2 * h2) / (3 * (h1 + h2)) };
382 return S1 * S2 / (alpha * S2 + (1 - alpha) * S1);
383 }
384 return 0;
385 }
386 void init()
387 {
388 if(points.size() == 1)
389 points[0].dy = 0;
390 else
391 {
392 const size_type N{ points.size() };
393 if(periodic)
394 {
395 const T period{ x_lim.max - x_lim.min };
396 std::vector<T> h, Delta;
397 h.reserve(N);
398 Delta.reserve(N);
399 for(size_type i{ 0 }; i < N - 1; ++i)
400 {
401 h.push_back(points[i + 1].x - points[i].x);
402 Delta.push_back((points[i + 1].y - points[i].y) / (points[i + 1].x - points[i].x));
403 }
404 h.push_back(points[0].x - points[N - 1].x + period);
405 Delta.push_back((points[0].y - points[N - 1].y) / (points[0].x - points[N - 1].x + period));
406 points[0].dy = G(Delta[N - 1], Delta[0], h[N - 1], h[0]);
407 for(size_type i{ 1 }; i < N; ++i) points[i].dy = G(Delta[i - 1], Delta[i], h[i - 1], h[i]);
408 }
409 else
410 {
411 std::vector<T> h, Delta;
412 h.reserve(N - 1);
413 Delta.reserve(N - 1);
414 for(size_type i{ 0 }; i < N - 1; ++i)
415 {
416 h.push_back(points[i + 1].x - points[i].x);
417 Delta.push_back((points[i + 1].y - points[i].y) / (points[i + 1].x - points[i].x));
418 }
419 points[0].dy = Delta[0];
420 for(size_type i{ 1 }; i < N - 1; ++i) points[i].dy = G(Delta[i - 1], Delta[i], h[i - 1], h[i]);
421 if(N >= 2) points[N - 1].dy = Delta[N - 2];
422 }
423 }
424 }
425
426public:
427 template <typename iter>
428 monotone_hermite_spline_variant(iter i_begin, iter i_end) : spline_base<T>::spline_base(i_begin, i_end)
429 {
430 init();
431 }
432
433 template <typename iter>
434 monotone_hermite_spline_variant(iter i_begin, iter i_end, const limits<T> &x_lim_, const limits<T> &y_lim_,
435 bool periodic_ = false)
436 : spline_base<T>::spline_base(i_begin, i_end, x_lim_, y_lim_, periodic_)
437 {
438 init();
439 }
440
441 monotone_hermite_spline_variant(const std::initializer_list<point<T> > &I) : spline_base<T>::spline_base(I)
442 {
443 init();
444 }
445
446 monotone_hermite_spline_variant(const std::initializer_list<point<T> > &I, const limits<T> &x_lim_,
447 const limits<T> &y_lim_, bool periodic_ = false)
448 : spline_base<T>::spline_base(I, x_lim_, y_lim_, periodic_)
449 {
450 init();
451 }
452};
453
454// cubic hermite spline interpolation
455// tangents at the interpolation points are determined such that the resulting
456// interpolating function has continuous 1st and 2nd derivatives over the whole
457// interval, natural boundary conditions are assumed in the non-periodic case
458// see https://de.wikipedia.org/wiki/Spline-Interpolation
459template <typename T> class smooth_cubic_spline : public spline_base<T>
460{
462 using base::periodic;
463 using base::points;
464 using base::x_lim;
465 using base::y_lim;
466 using typename base::size_type;
467
468 using vector = std::vector<T>;
469
470 class matrix
471 {
472 using size_type = typename std::vector<T>::size_type;
473 const size_type N{ 0 };
474 const bool is_banded{ false };
475 std::vector<T> A;
476
477 public:
478 explicit matrix(size_type N_, bool is_banded_ = false)
479 : N{ N_ }, is_banded{ is_banded_ }, A(is_banded_ ? 3 * N_ : N_ * N_, 0)
480 {
481 }
482
484 {
485 if(is_banded)
486 {
487 if(i == j) return A[i + N];
488 if(i + 1 == j) return A[i];
489 if(i == j + 1) return A[i + 2 * N];
490 }
491 return A[i + N * j];
492 }
493
494 const T &operator()(size_type i, size_type j) const
495 {
496 if(is_banded)
497 {
498 if(i == j) return A[i + N];
499 if(i + 1 == j) return A[i];
500 if(i == j + 1) return A[i + 2 * N];
501 }
502 return A[i + N * j];
503 }
504
506 {
507 return N;
508 }
509 bool isbanded() const
510 {
511 return is_banded;
512 }
513 };
514
515 // LU factorization without pivoting
516 // returns false if matrix A is singular, see
517 // https://de.wikipedia.org/wiki/Gau%C3%9Fsches_Eliminationsverfahren
518 static bool LU_factor(matrix &A)
519 {
520 if(A.size() < 1) return false;
521 const size_type n{ A.size() };
522 if(A.isbanded())
523 {
524 for(size_type i{ 0 }; i + 1 < n; ++i)
525 {
526 const T t1{ A(i, i) };
527 if(t1 != 0)
528 {
529 if(i + 1 < n)
530 {
531 A(i + 1, i) /= t1;
532 A(i + 1, i + 1) -= A(i + 1, i) * A(i, i + 1);
533 }
534 }
535 else
536 // the matrix is singular
537 return false;
538 }
539 }
540 else
541 {
542 for(size_type i{ 0 }; i + 1 < n; ++i)
543 {
544 const T t1{ A(i, i) };
545 if(t1 != 0)
546 {
547 for(size_type k{ i + 1 }; k < n; ++k)
548 {
549 A(k, i) /= t1;
550 for(size_type j{ i + 1 }; j < n; ++j) A(k, j) -= A(k, i) * A(i, j);
551 }
552 }
553 else
554 // the matrix is singular
555 return false;
556 }
557 }
558 return true;
559 }
560
561 // substitution after LU factorization
562 static void LU_solve(const matrix &A, vector &b)
563 {
564 const size_type n{ A.size() };
565 if(n < 1 or A.size() != b.size()) return;
566 if(A.isbanded())
567 {
568 // forward substitution
569 for(size_type i{ 0 }; i < n; ++i)
570 {
571 if(i > 0) b[i] -= A(i, i - 1) * b[i - 1];
572 }
573 // backward substitution
574 for(size_type i{ n - 1 };; --i)
575 {
576 if(i + 1 < n) b[i] -= A(i, i + 1) * b[i + 1];
577 b[i] /= A(i, i);
578 if(i == 0) break;
579 }
580 }
581 else
582 {
583 // forward substitution
584 for(size_type i{ 0 }; i < n; ++i)
585 for(size_type k{ 0 }; k < i; ++k) b[i] -= A(i, k) * b[k];
586 // backward substitution
587 for(size_type i{ n - 1 };; --i)
588 {
589 for(size_type k{ i + 1 }; k < n; ++k) b[i] -= A(i, k) * b[k];
590 b[i] /= A(i, i);
591 if(i == 0) break;
592 }
593 }
594 }
595
596 // solve linear system
597 static bool gauss_solve(matrix &A, vector &b)
598 {
599 // matrix A is diagonal dominant, thus no pivoting required
600 const bool ok{ LU_factor(A) };
601 if(ok) LU_solve(A, b);
602 return ok;
603 }
604
605 void init()
606 {
607 // base constructor ensures that there is a non-empty set of knots
608 if(points.size() == 1)
609 points[0].dy = 0; // with only one data point assume horizontal line as interpolant
610 else
611 {
612 const size_type N{ points.size() };
613 std::vector<T> Delta_x, Delta_y;
614 Delta_x.reserve(periodic ? N : N - 1);
615 Delta_y.reserve(periodic ? N : N - 1);
616 for(size_type i{ 0 }; i < N - 1; ++i)
617 {
618 Delta_x.push_back(points[i + 1].x - points[i].x);
619 Delta_y.push_back(points[i + 1].y - points[i].y);
620 }
621 if(periodic)
622 {
623 const T period{ x_lim.max - x_lim.min };
624 Delta_x.push_back(points[0].x - points[N - 1].x + period);
625 Delta_y.push_back(points[0].y - points[N - 1].y);
626 }
627 // set up and solve the set of linear equations to determine the 2nd derivative of the
628 // interpolating function at the knots
629 matrix A(N, not periodic);
630 std::vector<T> b(N);
631 for(size_type i{ 1 }; i < N - 1; ++i)
632 {
633 A(i, i - 1) = Delta_x[i - 1] / 6;
634 A(i, i) = (Delta_x[i - 1] + Delta_x[i]) / 3;
635 A(i, i + 1) = Delta_x[i] / 6;
636 b[i] = Delta_y[i] / Delta_x[i] - Delta_y[i - 1] / Delta_x[i - 1];
637 }
638 if(periodic)
639 {
640 A(0, 0) = (Delta_x[N - 1] + Delta_x[0]) / 3;
641 A(N - 1, N - 1) = (Delta_x[N - 2] + Delta_x[N - 1]) / 3;
642 b[0] = Delta_y[0] / Delta_x[0] - Delta_y[N - 1] / Delta_x[N - 1];
643 b[N - 1] = Delta_y[N - 1] / Delta_x[N - 1] - Delta_y[N - 2] / Delta_x[N - 2];
644 if(N > 2)
645 {
646 A(0, 1) = Delta_x[0] / 6;
647 A(N - 1, N - 2) = Delta_x[N - 2] / 6;
648 A(0, N - 1) = A(N - 1, 0) = Delta_x[N - 1] / 6;
649 }
650 else
651 {
652 A(0, 1) = A(1, 0) = (Delta_x[0] + Delta_x[1]) / 6;
653 }
654 }
655 else
656 {
657 A(0, 0) = 1;
658 A(N - 1, N - 1) = 1;
659 b[0] = 0;
660 b[N - 1] = 0;
661 }
662 gauss_solve(A, b);
663 // calculate the 1st derivative of the interpolating function at the knots
664 T c_i{ 0 };
665 for(size_type i{ 0 }; i < N - 1; ++i)
666 {
667 c_i = Delta_y[i] / Delta_x[i] - Delta_x[i] / 6 * (b[i + 1] - b[i]);
668 points[i].dy = -Delta_x[i] * b[i] / 2 + c_i;
669 }
670 if(periodic)
671 points[N - 1].dy = Delta_x[N - 2] * b[N - 1] / 2 + c_i;
672 else
673 points[N - 1].dy = c_i;
674 }
675 }
676
677public:
678 template <typename iter>
679 smooth_cubic_spline(iter i_begin, iter i_end) : spline_base<T>::spline_base(i_begin, i_end)
680 {
681 init();
682 }
683
684 template <typename iter>
685 smooth_cubic_spline(iter i_begin, iter i_end, const limits<T> &x_lim_, const limits<T> &y_lim_,
686 bool periodic_ = false)
687 : spline_base<T>::spline_base(i_begin, i_end, x_lim_, y_lim_, periodic_)
688 {
689 init();
690 }
691
692 smooth_cubic_spline(const std::initializer_list<point<T> > &I) : spline_base<T>::spline_base(I)
693 {
694 init();
695 }
696
697 smooth_cubic_spline(const std::initializer_list<point<T> > &I, const limits<T> &x_lim_, const limits<T> &y_lim_,
698 bool periodic_ = false)
699 : spline_base<T>::spline_base(I, x_lim_, y_lim_, periodic_)
700 {
701 init();
702 }
703};
704
705} // namespace interpol
706
707
708float interpolate_val_V2(int n, CurveAnchorPoint Points[], float x, unsigned int type)
709{
710 if(type == CUBIC_SPLINE)
711 {
712 interpol::smooth_cubic_spline<float> s(Points, Points + n);
713 return s(x);
714 }
715 else if(type == CATMULL_ROM)
716 {
717 interpol::Catmull_Rom_spline<float> s(Points, Points + n);
718 return s(x);
719 }
720 else if(type == MONOTONE_HERMITE)
721 {
723 return s(x);
724 }
725 return NAN;
726}
727
728
729float interpolate_val_V2_periodic(int n, CurveAnchorPoint Points[], float x, unsigned int type, float period)
730{
731 if(type == CUBIC_SPLINE)
732 {
733 interpol::smooth_cubic_spline<float> s(Points, Points + n, { 0.f, period }, interpol::infinity<float>(), true);
734 return s(x);
735 }
736 else if(type == CATMULL_ROM)
737 {
738 interpol::Catmull_Rom_spline<float> s(Points, Points + n, { 0.f, period }, interpol::infinity<float>(), true);
739 return s(x);
740 }
741 else if(type == MONOTONE_HERMITE)
742 {
743 interpol::monotone_hermite_spline<float> s(Points, Points + n, { 0.f, period }, interpol::infinity<float>(),
744 true);
745 return s(x);
746 }
747 return NAN;
748}
749
750
752{
753 try
754 {
755 const float box_width = curve->m_max_x - curve->m_min_x;
756 const float box_height = curve->m_max_y - curve->m_min_y;
757
758 std::vector<interpol::point<float> > v;
759 // build arrays for processing
760 if(curve->m_numAnchors == 0)
761 {
762 // just a straight line using box coordinates
763 v.push_back({ curve->m_min_x, curve->m_min_y });
764 v.push_back({ curve->m_max_x, curve->m_max_y });
765 }
766 else
767 {
768 for(int i = 0; i < curve->m_numAnchors; i++)
769 v.push_back({ curve->m_anchors[i].x * box_width + curve->m_min_x,
770 curve->m_anchors[i].y * box_height + curve->m_min_y });
771 }
772
773 const float res = 1.0f / (sample->m_samplingRes - 1);
774 const int firstPointX = v.front().x * (sample->m_samplingRes - 1);
775 const int firstPointY = v.front().y * (sample->m_outputRes - 1);
776 const int lastPointX = v.back().x * (sample->m_samplingRes - 1);
777 const int lastPointY = v.back().y * (sample->m_outputRes - 1);
778 const int maxY = curve->m_max_y * (sample->m_outputRes - 1);
779 const int minY = curve->m_min_y * (sample->m_outputRes - 1);
780 const int n = sample->m_samplingRes;
781 if(curve->m_spline_type == CUBIC_SPLINE)
782 {
783 interpol::smooth_cubic_spline<float> s(v.begin(), v.end(), { v.front().x, v.back().x },
784 { curve->m_min_y, curve->m_max_y }, false);
785 for(int i = 0; i < n; ++i)
786 {
787 if(i < firstPointX)
788 sample->m_Samples[i] = firstPointY;
789 else if(i > lastPointX)
790 sample->m_Samples[i] = lastPointY;
791 else
792 {
793 int val = static_cast<int>(std::round(s(i * res) * (sample->m_outputRes - 1)));
794 if(val > maxY) val = maxY;
795 if(val < minY) val = minY;
796 sample->m_Samples[i] = val;
797 }
798 }
799 }
800 else if(curve->m_spline_type == CATMULL_ROM)
801 {
802 interpol::Catmull_Rom_spline<float> s(v.begin(), v.end(), { v.front().x, v.back().x },
803 { curve->m_min_y, curve->m_max_y }, false);
804 for(int i = 0; i < n; ++i)
805 {
806 if(i < firstPointX)
807 sample->m_Samples[i] = firstPointY;
808 else if(i > lastPointX)
809 sample->m_Samples[i] = lastPointY;
810 else
811 {
812 int val = static_cast<int>(std::round(s(i * res) * (sample->m_outputRes - 1)));
813 if(val > maxY) val = maxY;
814 if(val < minY) val = minY;
815 sample->m_Samples[i] = val;
816 }
817 }
818 }
819 else if(curve->m_spline_type == MONOTONE_HERMITE)
820 {
821 interpol::monotone_hermite_spline<float> s(v.begin(), v.end(), { v.front().x, v.back().x },
822 { curve->m_min_y, curve->m_max_y }, false);
823 for(int i = 0; i < n; ++i)
824 {
825 if(i < firstPointX)
826 sample->m_Samples[i] = firstPointY;
827 else if(i > lastPointX)
828 sample->m_Samples[i] = lastPointY;
829 else
830 {
831 int val = std::round(s(i * res) * (sample->m_outputRes - 1));
832 if(val > maxY) val = maxY;
833 if(val < minY) val = minY;
834 sample->m_Samples[i] = val;
835 }
836 }
837 }
838 return CT_SUCCESS;
839 }
840 catch(...)
841 {
842 return CT_ERROR;
843 }
844}
845
846
848{
849 try
850 {
851 const float box_width = curve->m_max_x - curve->m_min_x;
852 const float box_height = curve->m_max_y - curve->m_min_y;
853
854 std::vector<interpol::point<float> > v;
855 // build arrays for processing
856 if(curve->m_numAnchors == 0)
857 {
858 // just a straight line using box coordinates
859 v.push_back({ curve->m_min_x, curve->m_min_y });
860 v.push_back({ curve->m_max_x, curve->m_max_y });
861 }
862 else
863 {
864 for(int i = 0; i < curve->m_numAnchors; i++)
865 v.push_back({ curve->m_anchors[i].x * box_width + curve->m_min_x,
866 curve->m_anchors[i].y * box_height + curve->m_min_y });
867 }
868
869 const float res = 1.0f / (sample->m_samplingRes - 1);
870 if(curve->m_spline_type == CUBIC_SPLINE)
871 {
872 interpol::smooth_cubic_spline<float> s(v.begin(), v.end(), { curve->m_min_x, curve->m_max_x },
873 { curve->m_min_y, curve->m_max_y }, true);
874 for(unsigned int i = 0; i < sample->m_samplingRes; ++i)
875 sample->m_Samples[i] = static_cast<unsigned short int>(std::round(s(i * res) * (sample->m_outputRes - 1)));
876 }
877 else if(curve->m_spline_type == CATMULL_ROM)
878 {
879 interpol::Catmull_Rom_spline<float> s(v.begin(), v.end(), { curve->m_min_x, curve->m_max_x },
880 { curve->m_min_y, curve->m_max_y }, true);
881 for(unsigned int i = 0; i < sample->m_samplingRes; ++i)
882 sample->m_Samples[i] = static_cast<unsigned short int>(std::round(s(i * res) * (sample->m_outputRes - 1)));
883 }
884 else if(curve->m_spline_type == MONOTONE_HERMITE)
885 {
886 interpol::monotone_hermite_spline_variant<float> s(v.begin(), v.end(), { curve->m_min_x, curve->m_max_x },
887 { curve->m_min_y, curve->m_max_y }, true);
888 for(unsigned int i = 0; i < sample->m_samplingRes; ++i)
889 sample->m_Samples[i] = static_cast<unsigned short int>(std::round(s(i * res) * (sample->m_outputRes - 1)));
890 }
891 return CT_SUCCESS;
892 }
893 catch(...)
894 {
895 return CT_ERROR;
896 }
897}
Catmull_Rom_spline(const std::initializer_list< point< T > > &I, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:239
Catmull_Rom_spline(iter i_begin, iter i_end)
Definition splines.cpp:221
std::vector< base_point< T > > points
Definition splines.cpp:67
typename std::vector< base_point< T > >::size_type size_type
Definition splines.cpp:66
Catmull_Rom_spline(iter i_begin, iter i_end, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:227
Catmull_Rom_spline(const std::initializer_list< point< T > > &I)
Definition splines.cpp:234
static T G(const T S1, const T S2, const T h1, const T h2)
Definition splines.cpp:377
std::vector< base_point< T > > points
Definition splines.cpp:67
typename std::vector< base_point< T > >::size_type size_type
Definition splines.cpp:66
monotone_hermite_spline_variant(const std::initializer_list< point< T > > &I, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:446
monotone_hermite_spline_variant(iter i_begin, iter i_end, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:434
monotone_hermite_spline_variant(const std::initializer_list< point< T > > &I)
Definition splines.cpp:441
monotone_hermite_spline_variant(iter i_begin, iter i_end)
Definition splines.cpp:428
monotone_hermite_spline(const std::initializer_list< point< T > > &I, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:353
std::vector< base_point< T > > points
Definition splines.cpp:67
typename std::vector< base_point< T > >::size_type size_type
Definition splines.cpp:66
monotone_hermite_spline(const std::initializer_list< point< T > > &I)
Definition splines.cpp:348
monotone_hermite_spline(iter i_begin, iter i_end)
Definition splines.cpp:335
monotone_hermite_spline(iter i_begin, iter i_end, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:341
T & operator()(size_type i, size_type j)
Definition splines.cpp:483
typename std::vector< T >::size_type size_type
Definition splines.cpp:472
const T & operator()(size_type i, size_type j) const
Definition splines.cpp:494
matrix(size_type N_, bool is_banded_=false)
Definition splines.cpp:478
static bool LU_factor(matrix &A)
Definition splines.cpp:518
smooth_cubic_spline(const std::initializer_list< point< T > > &I, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:697
std::vector< base_point< T > > points
Definition splines.cpp:67
typename std::vector< base_point< T > >::size_type size_type
Definition splines.cpp:66
smooth_cubic_spline(const std::initializer_list< point< T > > &I)
Definition splines.cpp:692
smooth_cubic_spline(iter i_begin, iter i_end)
Definition splines.cpp:679
static void LU_solve(const matrix &A, vector &b)
Definition splines.cpp:562
static bool gauss_solve(matrix &A, vector &b)
Definition splines.cpp:597
smooth_cubic_spline(iter i_begin, iter i_end, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:685
std::vector< base_point< T > > points
Definition splines.cpp:67
typename std::vector< base_point< T > >::size_type size_type
Definition splines.cpp:66
T operator()(T x) const
Definition splines.cpp:118
limits< T > y_lim
Definition splines.cpp:69
spline_base(iter i_begin, iter i_end)
Definition splines.cpp:72
spline_base(const std::initializer_list< point< T > > &I, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:111
spline_base(const std::initializer_list< point< T > > &I)
Definition splines.cpp:107
spline_base(iter i_begin, iter i_end, const limits< T > &x_lim_, const limits< T > &y_lim_, bool periodic_=false)
Definition splines.cpp:82
limits< T > x_lim
Definition splines.cpp:68
static const float x
const float v
#define A(y, x)
#define P(V, params)
#define CATMULL_ROM
Definition curve_tools.h:35
#define CT_ERROR
Definition curve_tools.h:44
#define CT_SUCCESS
Definition curve_tools.h:43
#define CUBIC_SPLINE
Definition curve_tools.h:34
#define MONOTONE_HERMITE
Definition curve_tools.h:36
const int res
Definition dtpthread.h:351
_lib_location_type_t type
Definition location.c:1
float *const restrict const size_t k
constexpr limits< T > infinity()
Definition splines.cpp:58
#define N
float interpolate_val_V2_periodic(int n, CurveAnchorPoint Points[], float x, unsigned int type, float period)
Definition splines.cpp:729
int CurveDataSampleV2Periodic(CurveData *curve, CurveSample *sample)
Definition splines.cpp:847
int CurveDataSampleV2(CurveData *curve, CurveSample *sample)
Definition splines.cpp:751
float interpolate_val_V2(int n, CurveAnchorPoint Points[], float x, unsigned int type)
Definition splines.cpp:708
float m_min_x
Definition curve_tools.h:70
float m_max_x
Definition curve_tools.h:71
unsigned char m_numAnchors
Definition curve_tools.h:76
unsigned int m_spline_type
Definition curve_tools.h:67
float m_max_y
Definition curve_tools.h:73
float m_min_y
Definition curve_tools.h:72
unsigned int m_outputRes
Definition curve_tools.h:88
unsigned int m_samplingRes
Definition curve_tools.h:87
unsigned short int * m_Samples
Definition curve_tools.h:91
limits(T min_, T max_)
Definition splines.cpp:53
limits()=default
point(T x_, T y_)
Definition splines.cpp:36
point()=default