7#ifndef __SABLIB_CUBIC_SPLINE_H__
8#define __SABLIB_CUBIC_SPLINE_H__
22template <
typename Scalar>
25 static_assert(std::is_floating_point_v<Scalar>,
"Scalar must be a floating-point type.");
39 CubicSpline(
const Eigen::VectorX<Scalar> & x,
const Eigen::VectorX<Scalar> & y);
48 void Fit(
const Eigen::VectorX<Scalar> & x,
const Eigen::VectorX<Scalar> & y);
70 Eigen::VectorX<Scalar> a, b, c, d;
71 Eigen::VectorX<Scalar> sp_x, sp_y;
82 Eigen::VectorX<Scalar> SolveTridiagonal(
83 const Eigen::VectorX<Scalar>& lower,
const Eigen::VectorX<Scalar>& diag,
84 const Eigen::VectorX<Scalar>& upper,
const Eigen::VectorX<Scalar>& rhs
91template <
typename Scalar>
100template <
typename Scalar>
103 if (x.size() != y.size()) {
104 throw std::invalid_argument(
"CubicSpline::Fit(): x and y must have the same size.");
108 throw std::invalid_argument(
"CubicSpline::Fit(): At least 2 points are required.");
114 int n = x.size() - 1;
115 Eigen::VectorX<Scalar> h(n);
117 for(
int i = 0; i < n; i++) {
118 h(i) = x(i + 1) - x(i);
120 throw std::invalid_argument(
"CubicSpline::Fit(): x must be strictly increasing.");
124 Eigen::VectorX<Scalar> lower = Eigen::VectorX<Scalar>::Zero(n + 1);
125 Eigen::VectorX<Scalar> diag = Eigen::VectorX<Scalar>::Ones(n + 1);
126 Eigen::VectorX<Scalar> upper = Eigen::VectorX<Scalar>::Zero(n + 1);
127 Eigen::VectorX<Scalar> rhs = Eigen::VectorX<Scalar>::Zero(n + 1);
129 for(
int i = 1; i < n; i++) {
131 diag(i) = 2 * (h(i - 1) + h(i));
133 rhs(i) = 6 * ((y(i + 1) - y(i)) / h(i) - (y(i) - y(i - 1)) / h(i - 1));
136 Eigen::VectorX<Scalar> m = SolveTridiagonal(lower, diag, upper, rhs);
143 for(
int i=0;i<n;i++) {
145 b(i) = (y(i + 1) - y(i)) / h(i) - h(i) * (2 * m(i) + m(i + 1)) / 6.0;
147 d(i) = (m(i + 1) - m(i)) / (6.0 * h(i));
154template <
typename Scalar>
155Eigen::VectorX<Scalar> CubicSpline<Scalar>::SolveTridiagonal(
156 const Eigen::VectorX<Scalar>& lower,
const Eigen::VectorX<Scalar>& diag,
157 const Eigen::VectorX<Scalar>& upper,
const Eigen::VectorX<Scalar>& rhs
161 Eigen::VectorX<Scalar> cp = Eigen::VectorX<Scalar>::Zero(n);
162 Eigen::VectorX<Scalar> dp = Eigen::VectorX<Scalar>::Zero(n);
163 Eigen::VectorX<Scalar> x = Eigen::VectorX<Scalar>::Zero(n);
166 cp(0) = upper(0) / diag(0);
167 dp(0) = rhs(0) / diag(0);
169 for (
int i = 1; i < n; i++) {
170 Scalar m = diag(i) - lower(i) * cp(i - 1);
171 cp(i) = upper(i) / m;
172 dp(i) = (rhs(i) - lower(i) * dp(i - 1)) / m;
176 x(n - 1) = dp(n - 1);
177 for (
int i = n - 2; i >= 0; i--) {
178 x(i) = dp(i) - cp(i) * x(i + 1);
187template <
typename Scalar>
190 int n = sp_x.size() - 1;
193 for(
int j = 0; j < n; j++) {
194 if(x >= sp_x(j) && x <= sp_x(j + 1)) {
200 double dx = x - sp_x(i);
202 return a(i) + b(i) * dx + c(i) * dx * dx + d(i) * dx * dx * dx;
void Fit(const Eigen::VectorX< Scalar > &x, const Eigen::VectorX< Scalar > &y)
Fits a cubic spline to the given data points.
CubicSpline()=default
Default constructor.
Scalar operator()(const double x) const
Interpolates the value at a given x-coordinate.
Scalar Interpolate(const double x) const
Interpolates the value at a given x-coordinate.