7#ifndef __SABLIB_BSPLINE_H__
8#define __SABLIB_BSPLINE_H__
22template <
typename Scalar>
25 static_assert(std::is_floating_point_v<Scalar>,
"Scalar must be a floating-point type.");
36 BSpline(
const int degree,
const Eigen::VectorX<Scalar> & knots);
47 const int degree,
const Eigen::VectorX<Scalar> & knots,
48 const Eigen::VectorX<Scalar> & x,
const Eigen::VectorX<Scalar> & y
59 const int degree,
const Eigen::VectorX<Scalar> & knots,
const Eigen::VectorX<Scalar> & coefficients
74 const Eigen::VectorX<Scalar>
Knots()
const;
89 const Eigen::SparseMatrix<Scalar>
DesignMatrix(
const Eigen::VectorX<Scalar> & x)
const;
97 void Fit(
const Eigen::VectorX<Scalar> & x,
const Eigen::VectorX<Scalar> & y);
106 const Scalar
Interpolate(
const Scalar x,
const Eigen::VectorX<Scalar> & coefficients)
const;
118 Eigen::VectorX<Scalar> sp_knots;
120 Eigen::VectorX<Scalar> sp_coefficients;
128 int FindSpan(
const Scalar x)
const;
137 const Eigen::VectorX<Scalar> BasisFunctions(
const int span,
const Scalar x)
const;
143template <
typename Scalar>
144BSpline<Scalar>::BSpline(
145 const int degree,
const Eigen::VectorX<Scalar> & knots
146) : sp_degree(degree), sp_knots(knots), basis(knots.size() - degree - 1)
153template <
typename Scalar>
154BSpline<Scalar>::BSpline(
155 const int degree,
const Eigen::VectorX<Scalar> & knots,
156 const Eigen::VectorX<Scalar> & x,
const Eigen::VectorX<Scalar> & y
157) : sp_degree(degree), sp_knots(knots), basis(knots.size() - degree - 1)
165template <
typename Scalar>
166BSpline<Scalar>::BSpline(
167 const int degree,
const Eigen::VectorX<Scalar> & knots,
const Eigen::VectorX<Scalar> & coefficients
168) : sp_degree(degree), sp_knots(knots), basis(knots.size() - degree - 1), sp_coefficients(coefficients)
175template <
typename Scalar>
184template <
typename Scalar>
193template <
typename Scalar>
196 return sp_coefficients;
202template <
typename Scalar>
203int BSpline<Scalar>::FindSpan(
const Scalar x)
const
207 if (x >= sp_knots(n + 1)) {
211 if (x <= sp_knots(sp_degree)) {
217 int mid = (low + high) / 2;
219 while (x < sp_knots(mid) || x >= sp_knots(mid + 1)) {
220 if (x < sp_knots(mid)) {
227 mid = (low + high) / 2;
236template <
typename Scalar>
237const Eigen::VectorX<Scalar> BSpline<Scalar>::BasisFunctions(
const int span,
const Scalar x)
const
239 Eigen::VectorX<Scalar> N(sp_degree + 1);
240 Eigen::VectorX<Scalar> left(sp_degree + 1);
241 Eigen::VectorX<Scalar> right(sp_degree + 1);
245 for(
int j = 1; j <= sp_degree; j++) {
246 left(j) = x - sp_knots(span + 1 - j);
247 right(j) = sp_knots(span + j) - x;
251 for(
int r = 0; r < j; r++) {
252 Scalar temp = N(r) / (right(r + 1) + left(j - r));
253 N(r) = saved + right(r + 1) * temp;
254 saved = left(j - r) * temp;
266template <
typename Scalar>
270 std::vector< Eigen::Triplet<Scalar> > triplets;
272 for(
int r = 0; r < n; r++) {
274 int span = FindSpan(xi);
275 Eigen::VectorX<Scalar> N = BasisFunctions(span, xi);
277 for(
int j = 0; j <= sp_degree; j++) {
278 int col = span - sp_degree + j;
280 if(col >=0 && col < basis) {
281 triplets.emplace_back(r, col, N(j));
286 Eigen::SparseMatrix<Scalar> B(n , basis);
287 B.setFromTriplets(triplets.begin(), triplets.end());
295template <
typename Scalar>
299 Eigen::SparseMatrix<Scalar> BTB = B.transpose() * B;
300 Eigen::SimplicialLDLT< Eigen::SparseMatrix<Scalar> > solver;
304 if(solver.info() != Eigen::Success) {
305 throw std::runtime_error(
"BSpline::Fit(): solver calculation fails.");
308 sp_coefficients = solver.solve(B.transpose() * y);
314template<
typename Scalar>
317 int span = FindSpan(x);
318 Eigen::VectorX<Scalar> N = BasisFunctions(span, x);
322 for(
int j = 0; j <= sp_degree; j++) {
323 int idx = span - sp_degree + j;
325 if(idx >=0 && idx < coefficients.size()) {
326 y += N(j) * coefficients(idx);
336template<
typename Scalar>
const Eigen::SparseMatrix< Scalar > DesignMatrix(const Eigen::VectorX< Scalar > &x) const
Constructs the design matrix (collocation matrix) for a given set of x-coordinates.
int BasisSize() const
Gets the number of basis functions.
void Fit(const Eigen::VectorX< Scalar > &x, const Eigen::VectorX< Scalar > &y)
Fits the B-Spline to the given data points by calculating the coefficients.
const Eigen::VectorX< Scalar > Coefficients() const
Returns the internal B-Spline coefficients.
const Scalar Interpolate(const Scalar x, const Eigen::VectorX< Scalar > &coefficients) const
Interpolates the value at a given x-coordinate using provided B-Spline coefficients.
const Eigen::VectorX< Scalar > Knots() const
Returns the knot vector.