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