← Back to file list Raw

src/CSpline.h

#pragma once
#include <vector>
#include <map>
#include <unordered_map>
#include <glm/glm.hpp>
#include <glm/gtx/spline.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
enum class InterpolationType
{
Linear,
Cosine,
Cubic
};
template <typename T>
struct SplineMath
{
static T InterpolateLinear(const T& a, const T& b, float t)
{
return glm::mix(a, b, t);
}
static T InterpolateCubic(const T& p0, const T& p1, const T& p2, const T& p3, float t)
{
return glm::catmullRom(p0, p1, p2, p3, t);
}
};
template <>
struct SplineMath<glm::quat>
{
static glm::quat InterpolateLinear(const glm::quat& a, const glm::quat& b, float t)
{
return glm::slerp(a, b, t);
}
static glm::quat InterpolateCubic(const glm::quat& p0, const glm::quat& p1, const glm::quat& p2, const glm::quat& p3, float t)
{
glm::quat res = glm::catmullRom(p0, p1, p2, p3, t);
return glm::normalize(res);
}
};
template<typename TKey, typename TValue>
class CSpline
{
public:
void AddNode(TKey x, TValue y)
{
Nodes[x] = y;
}
TValue GetValue(TKey x, InterpolationType type = InterpolationType::Linear) const
{
if (Nodes.empty()) { return TValue(); }
if (Nodes.size() == 1) { return Nodes.begin()->second; }
auto it = Nodes.lower_bound(x);
if (it == Nodes.begin()) { return it->second; }
if (it == Nodes.end()) { return std::prev(Nodes.end())->second; }
auto p2_it = it;
auto p1_it = std::prev(it);
TKey t_raw = (x - p1_it->first) / (p2_it->first - p1_it->first);
float t = static_cast<float>(t_raw);
switch (type)
{
case InterpolationType::Linear:
return SplineMath<TValue>::InterpolateLinear(p1_it->second, p2_it->second, t);
case InterpolationType::Cosine:
{
float t_smooth = glm::smoothstep(0.0f, 1.0f, t);
return SplineMath<TValue>::InterpolateLinear(p1_it->second, p2_it->second, t_smooth);
}
case InterpolationType::Cubic:
{
const TValue& p1 = p1_it->second;
const TValue& p2 = p2_it->second;
const TValue& p0 = (p1_it == Nodes.begin()) ? p1 : std::prev(p1_it)->second;
auto p3_it = std::next(p2_it);
const TValue& p3 = (p3_it == Nodes.end()) ? p2 : p3_it->second;
return SplineMath<TValue>::InterpolateCubic(p0, p1, p2, p3, t);
}
}
return TValue();
}
private:
std::map<TKey, TValue> Nodes;
};