From d72e01e34e821065e5df3041bd77b921842bacb9 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 30 Sep 2020 18:59:47 +0300 Subject: [PATCH] Add a gradient helper class --- libs/geom/include/psemek/geom/easing.hpp | 83 ++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 libs/geom/include/psemek/geom/easing.hpp diff --git a/libs/geom/include/psemek/geom/easing.hpp b/libs/geom/include/psemek/geom/easing.hpp new file mode 100644 index 00000000..efc68db1 --- /dev/null +++ b/libs/geom/include/psemek/geom/easing.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +#include + +namespace psemek::geom +{ + + enum class easing + { + nearest, + constant_left, + constant_right, + linear, + quadratic_in, + quadratic_out, + cubic, + }; + + template + struct gradient + { + template + gradient(Args const & ... args) + { + init(args...); + } + + gradient(std::vector> points, std::vector segments) + { + assert(points.size() == segments.size() + 1); + points_ = std::move(points); + segments_ = std::move(segments); + } + + T operator()(T const & t) const + { + auto it = std::upper_bound(points_.begin(), points_.end(), t, [](T const & t, auto const & p){ return t < p[0]; }); + if (it == points_.begin()) + return points_.front()[1]; + if (it == points_.end()) + return points_.back()[1]; + + std::size_t i = (it - points_.begin()) - 1; + + T s = (t - points_[i][0]) * (points_[i + 1][1] - points_[i][1]); + + switch (segments_[i]) { + case easing::nearest: s = (2 * s < 1) ? 0 : 1; break; + case easing::constant_left: s = 0; break; + case easing::constant_right: s = 1; break; + case easing::linear: break; + case easing::quadratic_in: s = s * s; break; + case easing::quadratic_out: s = s * (2 - s); break; + case easing::cubic: s = s * s * (3 - 2 * s); break; + default: + assert(false); + } + + return points_[i][1] + s * (points_[i + 1][1] - points_[i][1]); + } + + private: + std::vector> points_; + std::vector segments_; + + void init(geom::vector const & p) + { + points_.push_back(p); + } + + template + void init(geom::vector const & p, easing t, Args const & ... args) + { + points_.push_back(p); + segments_.push_back(t); + init(args...); + } + }; + +}