psemek/libs/geom/include/psemek/geom/gradient.hpp

67 lines
1.4 KiB
C++

#pragma once
#include <psemek/geom/easing.hpp>
#include <psemek/geom/math.hpp>
#include <vector>
#include <algorithm>
namespace psemek::geom
{
template <typename T, typename R = T>
struct gradient
{
using result_type = R;
gradient() = default;
template <typename ... Args>
gradient(Args const & ... args)
{
init(args...);
}
gradient(std::vector<std::pair<T, R>> points, std::vector<easing_type> segments)
{
assert(points.size() == segments.size() + 1);
points_ = std::move(points);
segments_ = std::move(segments);
}
R operator()(T const & t) const
{
assert(!points_.empty());
auto it = std::upper_bound(points_.begin(), points_.end(), t, [](T const & t, auto const & p){ return t < p.first; });
if (it == points_.begin())
return points_.front().second;
if (it == points_.end())
return points_.back().second;
std::size_t i = (it - points_.begin()) - 1;
T s = easing(segments_[i], (t - points_[i].first) / (points_[i + 1].first - points_[i].first));
return lerp(points_[i].second, points_[i + 1].second, s);
}
private:
std::vector<std::pair<T, R>> points_;
std::vector<easing_type> segments_;
void init(std::pair<T, R> const & p)
{
points_.push_back(p);
}
template <typename ... Args>
void init(std::pair<T, R> const & p, easing_type t, Args const & ... args)
{
points_.push_back(p);
segments_.push_back(t);
init(args...);
}
};
}