35 template <
size_t NumPo
ints>
36 [[nodiscard]] SplineError generate(
const Real step_size,
37 const std::array<Real, NumPoints>& points,
38 const SplineEndCondition end_condition = SplineEndCondition::Natural,
39 const Real start_slope = 0.0,
40 const Real end_slope = 0.0)
42 if (NumPoints > MaxPoints)
44 return SplineError::TOO_MANY_POINTS;
48 return SplineError::NOT_ENOUGH_POINTS;
50 if (step_size <= FSB_TOL)
52 return SplineError::INVALID_STEP_SIZE;
55 const SplineBuildParameters parameters = {NumPoints, step_size, end_condition,
56 start_slope, end_slope};
57 const SplineBuildResult result = build_spline(points, parameters);
58 if (result.error == SplineError::SUCCESS)
61 m_step_size = step_size;
62 m_duration =
static_cast<Real
>(NumPoints - 1U) * step_size;
63 m_end_condition = end_condition;
64 m_start_slope = start_slope;
65 m_end_slope = end_slope;
66 for (
size_t i = 0U; i < NumPoints; ++i)
68 m_points[i] = points[i];
70 m_num_points = NumPoints;
71 m_spline = result.spline;
79 if (m_num_points < 2U)
84 Real t_local = t_eval - m_start_time;
89 if (t_local > m_duration)
94 const Real t_index = t_local / m_step_size;
95 const auto k =
static_cast<size_t>(t_index);
96 const size_t max_segment = m_num_points - 2U;
97 const auto k_clamped = (k > max_segment) ? max_segment : k;
99 const Real dt = t_index -
static_cast<Real
>(k_clamped);
100 const Real a = m_spline[k_clamped][0];
101 const Real b = m_spline[k_clamped][1];
102 const Real c = m_spline[k_clamped][2];
103 const Real d = m_spline[k_clamped][3];
105 const Real inv_h = 1.0 / m_step_size;
106 const Real inv_h2 = inv_h * inv_h;
107 const Real inv_h3 = inv_h2 * inv_h;
109 const Real position = (((a * dt) + b) * dt + c) * dt + d;
110 const Real velocity = (((3.0 * a * dt) + (2.0 * b)) * dt + c) * inv_h;
111 const Real acceleration = ((6.0 * a * dt) + (2.0 * b)) * inv_h2;
112 const Real jerk = (6.0 * a) * inv_h3;
114 return {position, velocity, acceleration, jerk};
139 return m_start_time + m_duration;
143 struct SplineBuildParameters
145 size_t num_points = 0U;
146 Real step_size = 0.0;
147 SplineEndCondition end_condition = SplineEndCondition::Natural;
148 Real start_slope = 0.0;
149 Real end_slope = 0.0;
152 struct SplineBuildResult
154 SplineError error = SplineError::SUCCESS;
155 std::array<Real, MaxPoints> second_derivative = {};
156 std::array<SplineCoeffs, MaxPoints> spline = {};
159 template <
size_t NumPo
ints>
160 [[nodiscard]]
static SplineBuildResult build_spline(
161 const std::array<Real, NumPoints>& points,
const SplineBuildParameters& parameters)
163 SplineBuildResult result = {};
165 if (parameters.end_condition == SplineEndCondition::Natural)
167 result = build_natural_spline(points, parameters);
171 result = build_clamped_spline(points, parameters);
187 template <
size_t NumPo
ints>
188 [[nodiscard]]
static SplineBuildResult build_natural_spline(
189 const std::array<Real, NumPoints>& points,
const SplineBuildParameters& parameters)
191 SplineBuildResult result = {};
193 if (parameters.num_points < 2U)
195 result.error = SplineError::NOT_ENOUGH_POINTS;
199 if (parameters.num_points > 2U)
201 std::array<Real, MaxPoints> c_prime = {};
202 std::array<Real, MaxPoints> d_prime = {};
204 c_prime[1] = 1.0 / 4.0;
205 d_prime[1] = 6.0 * (points[2] - (2.0 * points[1]) + points[0]) / 4.0;
207 for (
size_t i = 2U; i < (parameters.num_points - 1U); ++i)
209 const Real rhs = 6.0 * (points[i + 1U] - (2.0 * points[i]) + points[i - 1U]);
210 const Real denom = 4.0 - c_prime[i - 1U];
211 c_prime[i] = 1.0 / denom;
212 d_prime[i] = (rhs - d_prime[i - 1U]) / denom;
215 result.second_derivative[parameters.num_points - 2U] = d_prime[parameters.num_points - 2U];
216 for (
size_t i = parameters.num_points - 2U; i > 1U; --i)
218 result.second_derivative[i - 1U] = d_prime[i - 1U]
219 - (c_prime[i - 1U] * result.second_derivative[i]);
223 for (
size_t k = 0U; k < (parameters.num_points - 1U); ++k)
225 const Real m0 = result.second_derivative[k];
226 const Real m1 = result.second_derivative[k + 1U];
227 const Real y0 = points[k];
228 const Real y1 = points[k + 1U];
230 result.spline[k][0] = (m1 - m0) / 6.0;
231 result.spline[k][1] = m0 / 2.0;
232 result.spline[k][2] = (y1 - y0) - ((2.0 * m0 + m1) / 6.0);
233 result.spline[k][3] = y0;
245 template <
size_t NumPo
ints>
246 [[nodiscard]]
static SplineBuildResult build_clamped_spline(
247 const std::array<Real, NumPoints>& points,
const SplineBuildParameters& parameters)
249 SplineBuildResult result = {};
251 if (parameters.num_points < 2U)
253 result.error = SplineError::NOT_ENOUGH_POINTS;
257 const size_t n = parameters.num_points - 1U;
259 std::array<Real, MaxPoints> lower = {};
260 std::array<Real, MaxPoints> diag = {};
261 std::array<Real, MaxPoints> upper = {};
262 std::array<Real, MaxPoints> rhs = {};
264 const Real start_slope_scaled = parameters.start_slope * parameters.step_size;
265 const Real end_slope_scaled = parameters.end_slope * parameters.step_size;
269 rhs[0] = 6.0 * ((points[1] - points[0]) - start_slope_scaled);
271 for (
size_t i = 1U; i < n; ++i)
276 rhs[i] = 6.0 * (points[i + 1U] - (2.0 * points[i]) + points[i - 1U]);
281 rhs[n] = 6.0 * (end_slope_scaled - (points[n] - points[n - 1U]));
283 for (
size_t i = 1U; i <= n; ++i)
285 if ((diag[i - 1U] > -FSB_TOL) && (diag[i - 1U] < FSB_TOL))
287 result.error = SplineError::SINGULAR_SYSTEM;
291 const Real w = lower[i] / diag[i - 1U];
292 diag[i] -= w * upper[i - 1U];
293 rhs[i] -= w * rhs[i - 1U];
296 if ((diag[n] > -FSB_TOL) && (diag[n] < FSB_TOL))
298 result.error = SplineError::SINGULAR_SYSTEM;
301 result.second_derivative[n] = rhs[n] / diag[n];
303 for (
size_t i = n; i > 0U; --i)
305 if ((diag[i - 1U] > -FSB_TOL) && (diag[i - 1U] < FSB_TOL))
307 result.error = SplineError::SINGULAR_SYSTEM;
310 result.second_derivative[i - 1U] =
311 (rhs[i - 1U] - (upper[i - 1U] * result.second_derivative[i])) / diag[i - 1U];
314 for (
size_t k = 0U; k < n; ++k)
316 const Real m0 = result.second_derivative[k];
317 const Real m1 = result.second_derivative[k + 1U];
318 const Real y0 = points[k];
319 const Real y1 = points[k + 1U];
321 result.spline[k][0] = (m1 - m0) / 6.0;
322 result.spline[k][1] = m0 / 2.0;
323 result.spline[k][2] = (y1 - y0) - ((2.0 * m0 + m1) / 6.0);
324 result.spline[k][3] = y0;
331 Real m_start_time = 0.0;
332 Real m_step_size = 0.0;
333 Real m_duration = 0.0;
334 SplineEndCondition m_end_condition = SplineEndCondition::Natural;
335 Real m_start_slope = 0.0;
336 Real m_end_slope = 0.0;
338 std::array<Real, MaxPoints> m_points = {};
339 std::array<SplineCoeffs, MaxPoints> m_spline = {};
340 size_t m_num_points = 0U;