#pragma once
#include <optional>
#include <utility>
#include <gcem.hpp>
#include <wpi/array.h>
#include "frc/EigenCore.h"
#include "frc/geometry/Pose2d.h"
#include "units/curvature.h"
#include "units/length.h"
namespace frc {
template <int Degree>
class Spline {
public:
using PoseWithCurvature = std::pair<Pose2d, units::curvature_t>;
constexpr Spline() = default;
constexpr Spline(const Spline&) = default;
constexpr Spline& operator=(const Spline&) = default;
constexpr Spline(Spline&&) = default;
constexpr Spline& operator=(Spline&&) = default;
constexpr virtual ~Spline() = default;
struct ControlVector {
wpi::array<double, (Degree + 1) / 2> x;
wpi::array<double, (Degree + 1) / 2> y;
};
std::optional<PoseWithCurvature> GetPoint(double t) const {
Vectord<Degree + 1> polynomialBases;
for (int i = 0; i <= Degree; i++) {
polynomialBases(i) = gcem::pow(t, Degree - i);
}
Vectord<6> combined = Coefficients() * polynomialBases;
double dx, dy, ddx, ddy;
if (t == 0.0) {
dx = Coefficients()(2, Degree - 1);
dy = Coefficients()(3, Degree - 1);
ddx = Coefficients()(4, Degree - 2);
ddy = Coefficients()(5, Degree - 2);
} else {
dx = combined(2) / t;
dy = combined(3) / t;
ddx = combined(4) / t / t;
ddy = combined(5) / t / t;
}
if (gcem::hypot(dx, dy) < 1e-6) {
return std::nullopt;
}
const auto curvature =
(dx * ddy - ddx * dy) / ((dx * dx + dy * dy) * gcem::hypot(dx, dy));
return PoseWithCurvature{
{FromVector(combined.template block<2, 1>(0, 0)), Rotation2d{dx, dy}},
units::curvature_t{curvature}};
}
constexpr virtual Matrixd<6, Degree + 1> Coefficients() const = 0;
constexpr virtual const ControlVector& GetInitialControlVector() const = 0;
constexpr virtual const ControlVector& GetFinalControlVector() const = 0;
protected:
static constexpr Eigen::Vector2d ToVector(const Translation2d& translation) {
return Eigen::Vector2d{{translation.X().value()},
{translation.Y().value()}};
}
static constexpr Translation2d FromVector(const Eigen::Vector2d& vector) {
return Translation2d{units::meter_t{vector(0)}, units::meter_t{vector(1)}};
}
};
}