#pragma once
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <wpi/SymbolExports.h>
#include "frc/spline/Spline.h"
#include "units/angle.h"
#include "units/curvature.h"
#include "units/length.h"
#include "units/math.h"
namespace frc {
class WPILIB_DLLEXPORT SplineParameterizer {
public:
using PoseWithCurvature = std::pair<Pose2d, units::curvature_t>;
struct MalformedSplineException : public std::runtime_error {
explicit MalformedSplineException(const char* what_arg)
: runtime_error(what_arg) {}
};
template <int Dim>
static std::vector<PoseWithCurvature> Parameterize(const Spline<Dim>& spline,
double t0 = 0.0,
double t1 = 1.0) {
constexpr const char* kMalformedSplineExceptionMsg =
"Could not parameterize a malformed spline. This means that you "
"probably had two or more adjacent waypoints that were very close "
"together with headings in opposing directions.";
std::vector<PoseWithCurvature> splinePoints;
if (auto point = spline.GetPoint(t0)) {
splinePoints.push_back(point.value());
} else {
throw MalformedSplineException(kMalformedSplineExceptionMsg);
}
std::stack<StackContents> stack;
stack.emplace(StackContents{t0, t1});
int iterations = 0;
while (!stack.empty()) {
auto current = stack.top();
stack.pop();
auto start = spline.GetPoint(current.t0);
if (!start) {
throw MalformedSplineException(kMalformedSplineExceptionMsg);
}
auto end = spline.GetPoint(current.t1);
if (!end) {
throw MalformedSplineException(kMalformedSplineExceptionMsg);
}
const auto twist = start.value().first.Log(end.value().first);
if (units::math::abs(twist.dy) > kMaxDy ||
units::math::abs(twist.dx) > kMaxDx ||
units::math::abs(twist.dtheta) > kMaxDtheta) {
stack.emplace(StackContents{(current.t0 + current.t1) / 2, current.t1});
stack.emplace(StackContents{current.t0, (current.t0 + current.t1) / 2});
} else {
splinePoints.push_back(end.value());
}
if (iterations++ >= kMaxIterations) {
throw MalformedSplineException(kMalformedSplineExceptionMsg);
}
}
return splinePoints;
}
private:
static inline constexpr units::meter_t kMaxDx = 5_in;
static inline constexpr units::meter_t kMaxDy = 0.05_in;
static inline constexpr units::radian_t kMaxDtheta = 0.0872_rad;
struct StackContents {
double t0;
double t1;
};
static constexpr int kMaxIterations = 5000;
friend class CubicHermiteSplineTest;
friend class QuinticHermiteSplineTest;
};
}