cgv
Loading...
Searching...
No Matches
interpolate.h
1#pragma once
2
3#include <functional>
4#include <memory>
5#include <vector>
6
7#include "interval.h"
8#include "compare_float.h"
9
10namespace cgv {
11namespace math {
12
13namespace detail {
14
21template<typename ParamT = float>
22ParamT map_to_domain(ParamT t, const interval<ParamT>& domain = { 0.0f, 1.0f }) {
23 ParamT domain_size = domain.size();
24 if(!is_zero(domain_size))
25 return (t - domain.lower_bound) / domain_size;
26 return domain.lower_bound;
27}
28
45template<typename PointT, typename InterpolationOp, typename ParamT = float>
46PointT interpolate_piece(const std::vector<PointT>& points, ParamT t, InterpolationOp operation) {
47 if(t <= ParamT(0))
48 return points.front();
49
50 if(t >= ParamT(1))
51 return points.back();
52
53 size_t num_pairs = points.size() - 1;
54 size_t index = static_cast<size_t>(t * static_cast<ParamT>(num_pairs));
55 ParamT step = ParamT(1) / static_cast<ParamT>(num_pairs);
56 ParamT prev = static_cast<ParamT>(index) * step;
57 ParamT curr = static_cast<ParamT>(index + 1) * step;
58 t = (t - prev) / (curr - prev);
59 return operation(points[index], points[index + 1], t);
60}
61
80template<typename PointT, typename InterpolationOp, typename ParamT = float>
81PointT interpolate_piece(const std::vector<std::pair<ParamT, PointT>>& points, ParamT t, InterpolationOp operation) {
82 using pair_type = std::pair<ParamT, PointT>;
83
84 if(t <= points.front().first)
85 return points.front().second;
86
87 auto it = std::lower_bound(points.begin(), points.end(), t, [](const pair_type& point, float value) { return point.first < value; });
88 if(it == points.end())
89 return points.back().second;
90
91 // "it" points to the first point whose parameter position is larger than t
92 const pair_type& left = *(it == points.begin() ? it : it - 1);
93 const pair_type& right = *it;
94 t = (t - left.first) / (right.first - left.first);
95 return operation(left.second, right.second, t);
96}
97
113template<typename PointT, typename InterpolationOp, typename ParamT = float>
114std::vector<PointT> interpolate_n(const std::vector<std::pair<ParamT, PointT>>& points, InterpolationOp operation, size_t n) {
115 using pair_type = std::pair<ParamT, PointT>;
116
117 if(n == 0 || points.size() == 0)
118 return {};
119
120 if(points.size() == 1)
121 return std::vector<PointT>(n, points.front().second);
122
123 std::vector<PointT> res;
124 res.reserve(n);
125
126 size_t l = 0;
127 size_t r = 1;
128 ParamT size = points.back().first - points.front().first;
129 ParamT step = size / static_cast<ParamT>(n - 1);
130 for(size_t i = 0; i < n; ++i) {
131 ParamT x = points.front().first + step * static_cast<ParamT>(i);
132
133 while(x > points[r].first && l < points.size() - 1) {
134 l = r;
135 r = std::min(l + 1, points.size() - 1);
136 }
137
138 if(l == r) {
139 res.push_back(points[r].second);
140 } else {
141 const pair_type& p0 = points[l];
142 const pair_type& p1 = points[r];
143 ParamT t = (x - p0.first) / (p1.first - p0.first);
144 res.push_back(operation(p0.second, p1.second, t));
145 }
146 }
147 return res;
148}
149
150} // namespace detail
151
162template<typename ParamT = float, typename OutputIt, typename UnaryOp>
163static void sequence_transform(OutputIt output_first, UnaryOp operation, size_t n, ParamT a = ParamT(0), ParamT b = ParamT(1)) {
164 if(n == 1) {
165 *output_first = operation(ParamT(0.5) * (a + b));
166 ++output_first;
167 } else if(n > 1) {
168 ParamT size = b - a;
169 ParamT step = size / static_cast<ParamT>(n - 1);
170 for(size_t i = 0; i < n; ++i) {
171 ParamT t = a + step * static_cast<ParamT>(i);
172 *output_first = operation(t);
173 ++output_first;
174 }
175 }
176}
177
186template<typename PointT, typename ParamT = float>
187PointT interpolate_nearest(const PointT& p0, const PointT& p1, ParamT t) {
188 return t < ParamT(0.5) ? p0 : p1;
189};
190
199template<typename PointT, typename ParamT = float>
200PointT interpolate_nearest(const std::vector<PointT>& points, ParamT t, const interval<ParamT>& domain = { 0.0f, 1.0f }) {
201 return detail::interpolate_piece(points, t, static_cast<PointT(*)(const PointT&, const PointT&, ParamT)>(&interpolate_nearest<PointT, ParamT>));
202};
203
215template<typename PointT, typename ParamT = float>
216PointT interpolate_nearest(const std::vector<std::pair<ParamT, PointT>>& points, ParamT t) {
217 return detail::interpolate_piece(points, t, static_cast<PointT(*)(const PointT&, const PointT&, ParamT)>(&interpolate_nearest<PointT, ParamT>));
218}
219
227template<typename PointT, typename ParamT = float>
228std::vector<PointT> interpolate_nearest_n(const std::vector<PointT>& points, size_t n) {
229 std::vector<PointT> res;
230 res.reserve(n);
231 sequence_transform(std::back_inserter(res), [&points](float t) { return interpolate_nearest(points, t); }, n);
232 return res;
233};
234
245template<typename PointT, typename ParamT = float>
246std::vector<PointT> interpolate_nearest_n(const std::vector<std::pair<ParamT, PointT>>& points, size_t n) {
247 return detail::interpolate_n(points, static_cast<PointT(*)(const PointT&, const PointT&, ParamT)>(&interpolate_nearest<PointT, ParamT>), n);
248}
249
258template<typename PointT, typename ParamT = float>
259static PointT interpolate_linear(const PointT& p0, const PointT& p1, ParamT t) {
260 PointT v0 = (ParamT(1) - t) * p0;
261 PointT v1 = t * p1;
262 return v0 + v1;
263}
264
273template<typename PointT, typename ParamT = float>
274PointT interpolate_linear(const std::vector<PointT>& points, ParamT t, const interval<ParamT>& domain = { 0.0f, 1.0f }) {
275 t = detail::map_to_domain(t, domain);
276 return detail::interpolate_piece(points, t, static_cast<PointT(*)(const PointT&, const PointT&, ParamT)>(&interpolate_linear<PointT, ParamT>));
277};
278
290template<typename PointT, typename ParamT = float>
291PointT interpolate_linear(const std::vector<std::pair<ParamT, PointT>>& points, ParamT t) {
292 return detail::interpolate_piece(points, t, static_cast<PointT(*)(const PointT&, const PointT&, ParamT)>(&interpolate_linear<PointT, ParamT>));
293}
294
302template<typename PointT, typename ParamT = float>
303std::vector<PointT> interpolate_linear_n(const std::vector<PointT>& points, size_t n) {
304 std::vector<PointT> res;
305 res.reserve(n);
306 sequence_transform(std::back_inserter(res), [&points](float t) { return interpolate_linear(points, t); }, n);
307 return res;
308};
309
320template<typename PointT, typename ParamT = float>
321std::vector<PointT> interpolate_linear_n(const std::vector<std::pair<ParamT, PointT>>& points, size_t n) {
322 return detail::interpolate_n(points, static_cast<PointT(*)(const PointT&, const PointT&, ParamT)>(&interpolate_linear<PointT, ParamT>), n);
323}
324
334template<typename PointT, typename ParamT = float>
335static PointT interpolate_quadratic_bezier(const PointT& p0, const PointT& p1, const PointT& p2, ParamT t) {
336 ParamT mt = ParamT(1) - t;
337 PointT v0 = mt * mt * p0;
338 PointT v1 = ParamT(2) * mt * t * p1;
339 PointT v2 = t * t * p2;
340 return v0 + v1 + v2;
341}
342
353template<typename PointT, typename ParamT = float>
354static PointT interpolate_cubic_bezier(const PointT& p0, const PointT& p1, const PointT& p2, const PointT& p3, ParamT t) {
355 ParamT mt = ParamT(1) - t;
356 PointT v0 = mt * mt * mt * p0;
357 PointT v1 = ParamT(3) * mt * mt * t * p1;
358 PointT v2 = ParamT(3) * mt * t * t * p2;
359 PointT v3 = t * t * t * p3;
360 return v0 + v1 + v2 + v3;
361}
362
373template<typename PointT, typename ParamT = float>
374static PointT interpolate_cubic_hermite(const PointT& p0, const PointT& m0, const PointT& p1, const PointT& m1, ParamT t) {
375 ParamT t2 = t * t;
376 ParamT t3 = t * t * t;
377 PointT w0 = ParamT(2) * t3 - ParamT(3) * t2 + ParamT(1);
378 PointT w1 = t3 - ParamT(2) * t2 + t;
379 PointT w2 = ParamT(-2) * t3 + ParamT(3) * t2;
380 PointT w3 = t3 - t2;
381 return w0 * p0 + w1 * m0 + w2 * p1 + w3 * m1;
382}
383
394template<typename PointT, typename ParamT = float>
395static PointT interpolate_cubic_basis(const PointT& b0, const PointT& b1, const PointT& b2, const PointT& b3, ParamT t) {
396 ParamT t2 = t * t;
397 ParamT t3 = t * t * t;
398 PointT v0 = (ParamT(1) - ParamT(3) * t + ParamT(3) * t2 - t3) * b0;
399 PointT v1 = (ParamT(4) - ParamT(6) * t2 + ParamT(3) * t3) * b1;
400 PointT v2 = (ParamT(1) + ParamT(3) * t + ParamT(3) * t2 - ParamT(3) * t3) * b2;
401 PointT v3 = t3 * b3;
402 return (ParamT(1) / ParamT(6)) * (v0 + v1 + v2 + v3);
403}
404
417template<typename PointT, typename ParamT = float>
418PointT interpolate_smooth_cubic(const std::vector<PointT>& points, ParamT t, const interval<ParamT>& domain = { 0.0f, 1.0f }) {
419 t = detail::map_to_domain(t, domain);
420
421 size_t num_pairs = points.size() - 1;
422
423 size_t i = 0;
424 if(t <= ParamT(0)) {
425 t = ParamT(0);
426 } else if(t >= ParamT(1)) {
427 t = ParamT(1);
428 i = num_pairs - 1;
429 } else {
430 i = static_cast<size_t>(t * static_cast<float>(num_pairs));
431 }
432
433 PointT v1 = points[i];
434 PointT v2 = points[i + 1];
435 PointT v0 = i > 0 ? points[i - 1] : PointT(2) * v1 - v2;
436 PointT v3 = i < num_pairs - 1 ? points[i + 2] : PointT(2) * v2 - v1;
437 t = (t - static_cast<float>(i) / static_cast<float>(num_pairs)) * static_cast<float>(num_pairs);
438 return interpolate_cubic_basis(v0, v1, v2, v3, t);
439};
440
454template<typename PointT, typename ParamT = float>
455PointT interpolate_smooth_cubic(const std::vector<std::pair<ParamT, PointT>>& points, ParamT t) {
456 using pair_type = std::pair<ParamT, PointT>;
457
458 size_t num_pairs = points.size() - 1;
459
460 if(t <= ParamT(0))
461 return points.front().second;
462
463 auto it = std::lower_bound(points.begin(), points.end(), t, [](const pair_type& point, float value) { return point.first < value; });
464 if(it == points.end())
465 return points.back().second;
466
467 if(it != points.begin())
468 --it;
469
470 size_t i = std::distance(points.begin(), it);
471
472 pair_type v1 = points[i];
473 pair_type v2 = points[i + 1];
474 PointT v0 = i > 0 ? points[i - 1].second : PointT(2) * v1.second - v2.second;
475 PointT v3 = i < num_pairs - 1 ? points[i + 2].second : PointT(2) * v2.second - v1.second;
476 t = (t - v1.first) / (v2.first - v1.first);
477 return interpolate_cubic_basis(v0, v1.second, v2.second, v3, t);
478}
479
489template<typename PointT, typename ParamT = float>
490std::vector<PointT> interpolate_smooth_cubic_n(const std::vector<PointT>& points, size_t n) {
491 std::vector<PointT> res;
492 res.reserve(n);
493 sequence_transform(std::back_inserter(res), [&points](float t) { return interpolate_smooth_cubic(points, t); }, n);
494 return res;
495}
496
506template<typename PointT, typename ParamT = float>
507std::vector<PointT> interpolate_smooth_cubic_n(const std::vector<std::pair<ParamT, PointT>>& points, size_t n) {
508 std::vector<PointT> res;
509 res.reserve(n);
510 sequence_transform(std::back_inserter(res), [&points](float t) { return interpolate_smooth_cubic(points, t); }, n);
511 return res;
512}
513
515template<typename X, typename Y>
518 interval<X> domain = { X(0), X(1) };
520 std::vector<Y> breakpoints;
521
523 Y evaluate(X x) const {
524 x = detail::map_to_domain(x, domain);
525
526 if(x <= X(0))
527 return breakpoints.front();
528
529 if(x >= X(1))
530 return breakpoints.back();
531
532 size_t num_pieces = breakpoints.size() - 1;
533 size_t index = static_cast<size_t>(x * static_cast<X>(num_pieces));
534
535 X step = X(1) / static_cast<X>(num_pieces);
536 X prev = static_cast<X>(index) * step;
537 X curr = static_cast<X>(index + 1) * step;
538 X t = (x - prev) / (curr - prev);
539 return interpolate_linear(breakpoints[index], breakpoints[index + 1], t);
540 }
541};
542
544template<typename ValueT, typename ParamT = float>
546public:
547 virtual ~interpolator() = default;
548
549 virtual std::unique_ptr<interpolator> clone() const = 0;
550
554 virtual ValueT at(ParamT t) const = 0;
555
563 virtual std::vector<ValueT> quantize(size_t n) const {
564 std::vector<ValueT> samples;
565 samples.reserve(n);
566 sequence_transform<ParamT>(std::back_inserter(samples), [this](ParamT t) { return at(t); }, n);
567 return samples;
568 }
569};
570
571template<typename PointT, typename ValueT, typename ParamT = float>
572class piecewise_interpolator_storage : public interpolator<ValueT, ParamT> {
573public:
574 using point_type = PointT;
575
577
578 piecewise_interpolator_storage(std::initializer_list<point_type> points) : points(points) {}
579
580 piecewise_interpolator_storage(const std::vector<point_type>& points) : points(points) {}
581
582 template<class IteratorT>
583 piecewise_interpolator_storage(IteratorT first, IteratorT last) {
584 points.assign(first, last);
585 }
586
587 std::vector<point_type> points;
588};
589
590template<typename ValueT, typename ParamT = float>
591class identity_interpolator : public interpolator<ValueT, ParamT> {
592public:
594
595 std::unique_ptr<interpolator<ValueT, ParamT>> clone() const override {
596 return std::unique_ptr<interpolator<ValueT, ParamT>>(new identity_interpolator(*this));
597 }
598
599 ValueT at(ParamT t) const override {
600 return ValueT(t);
601 }
602};
603
604template<typename ValueT, typename ParamT = float>
607
608public:
609 using base::base;
610};
611
612template<typename ValueT, typename ParamT = float>
613class piecewise_interpolator : public piecewise_interpolator_storage<std::pair<ParamT, ValueT>, ValueT, ParamT> {
615
616public:
617 using base::base;
618};
619
620template<typename ValueT, typename ParamT = float>
623
624public:
625 using base::base;
626
627 std::unique_ptr<interpolator<ValueT, ParamT>> clone() const override {
628 return std::unique_ptr<interpolator<ValueT, ParamT>>(new discrete_interpolator(*this));
629 }
630
631 ValueT at(ParamT t) const override {
632 size_t num_points = this->points.size();
633
634 if(t <= ParamT(0))
635 return this->points.front();
636 else if(t >= ParamT(1))
637 return this->points.back();
638 else
639 return this->points[std::min(static_cast<size_t>(t * static_cast<ParamT>(num_points)), num_points - 1)];
640 }
641};
642
643template<typename ValueT, typename ParamT = float>
646
647public:
648 using base::base;
649
650 std::unique_ptr<interpolator<ValueT, ParamT>> clone() const override {
651 return std::unique_ptr<interpolator<ValueT, ParamT>>(new uniform_linear_interpolator(*this));
652 }
653
654 ValueT at(ParamT t) const override {
655 return interpolate_linear(this->points, t);
656 }
657
658 std::vector<ValueT> quantize(size_t n) const override {
659 return interpolate_linear_n(this->points, n);
660 }
661};
662
663template<typename ValueT, typename ParamT = float>
664class linear_interpolator : public piecewise_interpolator<ValueT, ParamT> {
666
667public:
668 using base::base;
669
670 std::unique_ptr<interpolator<ValueT, ParamT>> clone() const override {
671 return std::unique_ptr<interpolator<ValueT, ParamT>>(new linear_interpolator(*this));
672 }
673
674 ValueT at(ParamT t) const override {
675 return interpolate_linear(this->points, t);
676 }
677
678 std::vector<ValueT> quantize(size_t n) const override {
679 return interpolate_linear_n(this->points, n);
680 }
681};
682
683template<typename ValueT, typename ParamT = float>
686
687public:
688 using base::base;
689
690 std::unique_ptr<interpolator<ValueT, ParamT>> clone() const override {
691 return std::unique_ptr<interpolator<ValueT, ParamT>>(new uniform_smooth_interpolator(*this));
692 }
693
694 ValueT at(ParamT t) const override {
695 return interpolate_smooth_cubic(this->points, t);
696 }
697};
698
699template<typename ValueT, typename ParamT = float>
700class smooth_interpolator : public piecewise_interpolator<ValueT, ParamT> {
702
703public:
704 using base::base;
705
706 std::unique_ptr<interpolator<ValueT, ParamT>> clone() const override {
707 return std::unique_ptr<interpolator<ValueT, ParamT>>(new smooth_interpolator(*this));
708 }
709
710 ValueT at(ParamT t) const override {
711 return interpolate_smooth_cubic(this->points, t);
712 }
713};
714
715template<typename ValueT, typename ParamT = float>
716class function_ref_interpolator : public interpolator<ValueT, ParamT> {
717public:
718 function_ref_interpolator(std::function<ValueT(ParamT)> function) : function(function) {}
719
720 std::unique_ptr<interpolator<ValueT, ParamT>> clone() const override {
721 return std::unique_ptr<interpolator<ValueT, ParamT>>(new function_ref_interpolator(*this));
722 }
723
724 ValueT at(ParamT t) const override {
725 if(function)
726 return function(t);
727 return {};
728 }
729
730 std::function<ValueT(ParamT)> function;
731};
732
733} // namespace math
734} // namespace cgv
base class for all classes that can be registered with support for dynamic properties (see also secti...
Definition base.h:75
ValueT at(ParamT t) const override
Return the interpolated value at position t.
ValueT at(ParamT t) const override
Return the interpolated value at position t.
ValueT at(ParamT t) const override
Return the interpolated value at position t.
Template of an abstract interface for interpolators that can be evaluated at a position t and return ...
virtual ValueT at(ParamT t) const =0
Return the interpolated value at position t.
virtual std::vector< ValueT > quantize(size_t n) const
Return a sequence of n uniformly-spaced samples from the interpolator within the parameter range [0,...
The interval template represents a closed interval of two numbers, i.e.
Definition interval.h:27
std::vector< ValueT > quantize(size_t n) const override
Return a sequence of n uniformly-spaced samples from the interpolator within the parameter range [0,...
ValueT at(ParamT t) const override
Return the interpolated value at position t.
ValueT at(ParamT t) const override
Return the interpolated value at position t.
std::vector< ValueT > quantize(size_t n) const override
Return a sequence of n uniformly-spaced samples from the interpolator within the parameter range [0,...
ValueT at(ParamT t) const override
Return the interpolated value at position t.
ValueT at(ParamT t) const override
Return the interpolated value at position t.
this header is dependency free
Definition print.h:11
Template class representing a piecewise linear function with uniformly spaced breakpoints that maps f...
std::vector< Y > breakpoints
The points that define the piecewise intervals and are assumed to be spaced uniformly over the domain...
interval< X > domain
The input parameter domain.
Y evaluate(X x) const
Return the piecewise linear interpolated value at position x.