#pragma once
#include <algorithm>
#include <functional>
#include <optional>
#include <utility>
#include <vector>
#include <wpi/MathExtras.h>
#include <wpi/SymbolExports.h>
#include "frc/geometry/Pose2d.h"
#include "frc/geometry/Pose3d.h"
#include "frc/geometry/Rotation3d.h"
#include "units/time.h"
namespace frc {
template <typename T>
class TimeInterpolatableBuffer {
public:
TimeInterpolatableBuffer(units::second_t historySize,
std::function<T(const T&, const T&, double)> func)
: m_historySize(historySize), m_interpolatingFunc(func) {}
explicit TimeInterpolatableBuffer(units::second_t historySize)
: m_historySize(historySize),
m_interpolatingFunc([](const T& start, const T& end, double t) {
return wpi::Lerp(start, end, t);
}) {}
void AddSample(units::second_t time, T sample) {
if (m_pastSnapshots.size() == 0 || time > m_pastSnapshots.back().first) {
m_pastSnapshots.emplace_back(time, sample);
} else {
auto first_after = std::upper_bound(
m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
[](auto t, const auto& pair) { return t < pair.first; });
if (first_after == m_pastSnapshots.begin()) {
m_pastSnapshots.insert(first_after, std::pair{time, sample});
} else if (auto last_not_greater_than = first_after - 1;
last_not_greater_than == m_pastSnapshots.begin() ||
last_not_greater_than->first < time) {
m_pastSnapshots.insert(first_after, std::pair{time, sample});
} else {
last_not_greater_than->second = sample;
}
}
while (time - m_pastSnapshots[0].first > m_historySize) {
m_pastSnapshots.erase(m_pastSnapshots.begin());
}
}
void Clear() { m_pastSnapshots.clear(); }
std::optional<T> Sample(units::second_t time) const {
if (m_pastSnapshots.empty()) {
return {};
}
if (time <= m_pastSnapshots.front().first) {
return m_pastSnapshots.front().second;
}
if (time > m_pastSnapshots.back().first) {
return m_pastSnapshots.back().second;
}
if (m_pastSnapshots.size() < 2) {
return m_pastSnapshots[0].second;
}
auto upper_bound = std::lower_bound(
m_pastSnapshots.begin(), m_pastSnapshots.end(), time,
[](const auto& pair, auto t) { return t > pair.first; });
if (upper_bound == m_pastSnapshots.begin()) {
return upper_bound->second;
}
auto lower_bound = upper_bound - 1;
double t = ((time - lower_bound->first) /
(upper_bound->first - lower_bound->first));
return m_interpolatingFunc(lower_bound->second, upper_bound->second, t);
}
std::vector<std::pair<units::second_t, T>>& GetInternalBuffer() {
return m_pastSnapshots;
}
const std::vector<std::pair<units::second_t, T>>& GetInternalBuffer() const {
return m_pastSnapshots;
}
private:
units::second_t m_historySize;
std::vector<std::pair<units::second_t, T>> m_pastSnapshots;
std::function<T(const T&, const T&, double)> m_interpolatingFunc;
};
template <>
inline TimeInterpolatableBuffer<Pose2d>::TimeInterpolatableBuffer(
units::second_t historySize)
: m_historySize(historySize),
m_interpolatingFunc([](const Pose2d& start, const Pose2d& end, double t) {
if (t < 0) {
return start;
} else if (t >= 1) {
return end;
} else {
Twist2d twist = start.Log(end);
Twist2d scaledTwist = twist * t;
return start.Exp(scaledTwist);
}
}) {}
template <>
inline TimeInterpolatableBuffer<Pose3d>::TimeInterpolatableBuffer(
units::second_t historySize)
: m_historySize(historySize),
m_interpolatingFunc([](const Pose3d& start, const Pose3d& end, double t) {
if (t < 0) {
return start;
} else if (t >= 1) {
return end;
} else {
Twist3d twist = start.Log(end);
Twist3d scaledTwist = twist * t;
return start.Exp(scaledTwist);
}
}) {}
}