Theoretica
A C++ numerical and automatic mathematical library
splines.h
Go to the documentation of this file.
1 
5 
6 #ifndef THEORETICA_SPLINES_H
7 #define THEORETICA_SPLINES_H
8 
9 #include "../core/constants.h"
10 #include "../algebra/algebra_types.h"
11 
12 
13 namespace theoretica {
14 
15 
17  inline real lerp(real x1, real x2, real interp) {
18  return (x1 + interp * (x2 - x1));
19  }
20 
21 
23  template<unsigned int N>
25  const vec<real, N>& P1, const vec<real, N>& P2, real interp) {
26 
27  return (P1 + (P2 - P1) * interp);
28  }
29 
30 
32  inline real invlerp(real x1, real x2, real value) {
33  return (value - x1) / (x2 - x1);
34  }
35 
36 
38  template<unsigned int N>
40  const vec<real, N>& P1, const vec<real, N>& P2, real value) {
41 
42  real t = invlerp(P1.get(0), P2.get(0), value);
43 
44  // Check that all computed t_i are the same
45  for (int i = 1; i < N; ++i) {
46 
47  real t_new = invlerp(P1.get(i), P2.get(i), value);
48 
49  if(t != t_new) {
50  TH_MATH_ERROR("invlerp", t_new, OUT_OF_DOMAIN);
51  return nan();
52  }
53  }
54 
55  return t;
56  }
57 
58 
60  inline real remap(real iFrom, real iTo, real oFrom, real oTo, real value) {
61  return lerp(oFrom, oTo, invlerp(iFrom, iTo, value));
62  }
63 
64 
66  template<unsigned int N>
68  const vec<real, N>& iFrom, const vec<real, N>& iTo,
69  const vec<real, N>& oFrom, const vec<real, N>& oTo, real value) {
70  return lerp(oFrom, oTo, invlerp(iFrom, iTo, value));
71  }
72 
73 
75  template<unsigned int N>
77  const vec<real, N>& P1, const vec<real, N>& P2, real interp) {
78 
79  return (P1 + (P2 - P1) * interp).normalized();
80  }
81 
82 
84  template<unsigned int N>
86  const vec<real, N>& P1, const vec<real, N>& P2, real t) {
87 
88  // Compute (only once) the length
89  // of the input vectors
90  const real P1_l = P1.norm();
91  const real P2_l = P2.norm();
92 
93  // Check whether one of the vectors is null,
94  // which would make the computation impossible
95  if(P1_l == 0 || P2_l == 0) {
96  TH_MATH_ERROR("slerp", P1_l ? P2_l : P1_l, IMPOSSIBLE_OPERATION);
97  return vec<real, N>(nan());
98  }
99 
100  // Angle between P1 and P2 (from the dot product)
101  real omega = acos((P1 * P2) / (P1_l * P2_l));
102  real s = sin(omega);
103 
104  // Check that the sine of the angle is not zero
105  if(s == 0) {
106  TH_MATH_ERROR("slerp", s, DIV_BY_ZERO);
107  return vec<real, N>(nan());
108  }
109 
110  return (P1 * sin((1 - t) * omega) + P2 * sin(t * omega)) / s;
111  }
112 
113 
114  // Sigmoid-like interpolation
115 
116 
118  inline real smoothstep(real x1, real x2, real interp) {
119 
120  if(x1 == x2) {
121  TH_MATH_ERROR("smoothstep", x1, DIV_BY_ZERO);
122  return nan();
123  }
124 
125  // Clamp x between 0 and 1
126  const real x = clamp((interp - x1) / (x2 - x1), 0.0, 1.0);
127 
128  // 3x^2 - 2x^3
129  return x * x * (3 - 2 * x);
130  }
131 
132 
134  inline real smootherstep(real x1, real x2, real interp) {
135 
136  if(x1 == x2) {
137  TH_MATH_ERROR("smootherstep", x1, DIV_BY_ZERO);
138  return nan();
139  }
140 
141  // Clamp x between 0 and 1
142  const real x = clamp((interp - x1) / (x2 - x1), 0.0, 1.0);
143 
144  // 6x^5 - 15x^4 + 10x^3
145  return x * x * x * (x * (x * 6 - 15) + 10);
146  }
147 
148 
149  // Bezier curves
150 
151 
153  template<unsigned int N>
155  const vec<real, N>& P0, const vec<real, N>& P1,
156  const vec<real, N>& P2, real t) {
157 
158  return lerp(lerp(P0, P1, t), lerp(P1, P2, t), t);
159  }
160 
161 
163  template<unsigned int N>
165  const vec<real, N>& P0, const vec<real, N>& P1,
166  const vec<real, N>& P2, vec<real, N> P3, real t) {
167 
168  const vec<real, N> A = lerp(P0, P1, t);
169  const vec<real, N> B = lerp(P1, P2, t);
170  const vec<real, N> C = lerp(P2, P3, t);
171 
172  const vec<real, N> D = lerp(A, B, t);
173  const vec<real, N> E = lerp(B, C, t);
174 
175  return lerp(D, E, t);
176  }
177 
178 
189  template<unsigned int N>
190  inline vec<real, N> bezier(const std::vector<vec<real, N>>& points, real t) {
191 
192  if(points.size() < 2) {
193  TH_MATH_ERROR("bezier", points.size(), INVALID_ARGUMENT);
194  return vec<real, N>(nan());
195  }
196 
197  if(t < 0 || t > 1) {
198  TH_MATH_ERROR("bezier", t, INVALID_ARGUMENT);
199  return vec<real, N>(nan());
200  }
201 
202  for (int index = points.size(); index > 1; --index) {
203 
204  for (int i = 0; i < index - 1; ++i)
205  points[i] = lerp(points[i], points[i + 1], t);
206  }
207 
208  return points[0];
209  }
210 
211 
213  struct spline_node {
214 
217 
220  real a, b, c, d;
221 
223  spline_node() : x(0), a(0), b(0), c(0), d(0) {}
224 
227  : x(x), a(a), b(b), c(c), d(d) {}
228 
229 
232  inline real operator()(real X) const {
233  const real h = X - x;
234  return a + h * (b + h * (c + h * d));
235  }
236 
237 
240  inline real deriv(real X) const {
241  const real h = X - x;
242  return b + h * (c * 2 + h * d * 3);
243  }
244  };
245 
246 
250  template<typename DataPoints = std::vector<vec2>>
251  inline std::vector<spline_node> cubic_splines(DataPoints p) {
252 
253  if(p.size() < 2) {
254  TH_MATH_ERROR("cubic_splines", p.size(), INVALID_ARGUMENT);
255  return {spline_node(nan(), nan(), nan(), nan(), nan())};
256  }
257 
258  const unsigned int n = p.size() - 1;
259 
260  std::vector<real> dx(n);
261  std::vector<real> delta(n);
262 
263  delta[0] = 0;
264 
265  std::vector<real> alpha(n + 1);
266  std::vector<real> beta(n + 1);
267  std::vector<real> gamma(n + 1);
268 
269  alpha[0] = 1;
270  beta[0] = 0;
271  gamma[0] = 0;
272 
273  std::vector<real> b(n);
274  std::vector<real> c(n);
275  std::vector<real> d(n);
276 
277  for (unsigned int i = 0; i < n; ++i)
278  dx[i] = p[i + 1][0] - p[i][0];
279 
280  for (unsigned int i = 1; i < n; ++i)
281  delta[i] = 3 * (
282  ((p[i + 1][1] - p[i][1]) / dx[i])
283  - (p[i][1] - p[i - 1][1]) / dx[i - 1]);
284 
285  for (unsigned int i = 1; i < n; ++i) {
286  alpha[i] = 2 * (p[i + 1][0] - p[i - 1][0]) - dx[i - 1] * beta[i - 1];
287  beta[i] = dx[i] / alpha[i];
288  gamma[i] = (delta[i] - dx[i] * gamma[i - 1]) / alpha[i];
289  }
290 
291  // Apply boundary conditions
292  alpha[n] = 1;
293  gamma[n] = 0;
294  c[n] = 0;
295 
296  // Solve the associated tridiagonal system
297  // using back-substitution
298  for (int i = n - 1; i >= 0; --i) {
299 
300  c[i] = gamma[i] - beta[i] * c[i + 1];
301  b[i] = (p[i + 1][1] - p[i][1]) / dx[i]
302  - dx[i] * (c[i + 1] + 2 * c[i]) / 3.0;
303  d[i] = (c[i + 1] - c[i]) / (3.0 * dx[i]);
304  }
305 
306  std::vector<spline_node> nodes(n);
307 
308  for (unsigned int i = 0; i < n; ++i)
309  nodes[i] = spline_node(p[i][0], p[i][1], b[i], c[i], d[i]);
310 
311  return nodes;
312  }
313 
314 
318  template<typename Dataset1, typename Dataset2>
319  inline std::vector<spline_node> cubic_splines(
320  const Dataset1& x, const Dataset2& y) {
321 
322  if(x.size() < 2) {
323  TH_MATH_ERROR("cubic_splines", x.size(), INVALID_ARGUMENT);
324  return {spline_node(nan(), nan(), nan(), nan(), nan())};
325  }
326 
327  if(x.size() != y.size()) {
328  TH_MATH_ERROR("cubic_splines", x.size(), INVALID_ARGUMENT);
329  return {spline_node(nan(), nan(), nan(), nan(), nan())};
330  }
331 
332  const unsigned int n = x.size() - 1;
333 
334  std::vector<real> dx(n);
335  std::vector<real> delta(n);
336 
337  delta[0] = 0;
338 
339  std::vector<real> alpha(n + 1);
340  std::vector<real> beta(n + 1);
341  std::vector<real> gamma(n + 1);
342 
343  alpha[0] = 1;
344  beta[0] = 0;
345  gamma[0] = 0;
346 
347  std::vector<real> b(n);
348  std::vector<real> c(n);
349  std::vector<real> d(n);
350 
351  for (unsigned int i = 0; i < n; ++i)
352  dx[i] = x[i + 1] - x[i];
353 
354  for (unsigned int i = 1; i < n; ++i)
355  delta[i] = 3 * (
356  ((y[i + 1] - y[i]) / dx[i])
357  - (y[i] - y[i - 1]) / dx[i - 1]);
358 
359  for (unsigned int i = 1; i < n; ++i) {
360  alpha[i] = 2 * (x[i + 1] - x[i - 1]) - dx[i - 1] * beta[i - 1];
361  beta[i] = dx[i] / alpha[i];
362  gamma[i] = (delta[i] - dx[i] * gamma[i - 1]) / alpha[i];
363  }
364 
365  // Apply boundary conditions
366  alpha[n] = 1;
367  gamma[n] = 0;
368  c[n] = 0;
369 
370  // Solve the associated tridiagonal system
371  // using back-substitution
372  for (int i = n - 1; i >= 0; --i) {
373 
374  c[i] = gamma[i] - beta[i] * c[i + 1];
375  b[i] = (y[i + 1] - y[i]) / dx[i]
376  - dx[i] * (c[i + 1] + 2 * c[i]) / 3.0;
377  d[i] = (c[i + 1] - c[i]) / (3.0 * dx[i]);
378  }
379 
380  std::vector<spline_node> nodes(n);
381 
382  for (unsigned int i = 0; i < n; ++i)
383  nodes[i] = spline_node(x[i], y[i], b[i], c[i], d[i]);
384 
385  return nodes;
386  }
387 
388 
391  class spline {
392  public:
393 
396  std::vector<spline_node> nodes;
397 
398 
401  template<typename DataPoints = std::vector<vec2>>
402  spline(const DataPoints& p) {
403  nodes = cubic_splines(p);
404  }
405 
406 
409  template<typename Dataset1, typename Dataset2>
410  spline(const Dataset1& X, const Dataset2& Y) {
411  nodes = cubic_splines(X, Y);
412  }
413 
414 
416  inline spline& operator=(const spline& other) {
417  nodes = other.nodes;
418  return *this;
419  }
420 
421 
424  inline real operator()(real x) const {
425 
426  for (int i = int(nodes.size()) - 1; i > 0; --i)
427  if(x >= nodes[i].x)
428  return nodes[i](x);
429 
430  // Extrapolation for x < x_0
431  return nodes[0](x);
432  }
433 
434 
437  inline real deriv(real x) const {
438 
439  for (unsigned int i = nodes.size() - 1; i > 0; --i)
440  if(x >= nodes[i].x)
441  return nodes[i].deriv(x);
442 
443  // Extrapolation for x < x_0
444  return nodes[0].deriv(x);
445  }
446 
447 
449  inline auto begin() {
450  return nodes.begin();
451  }
452 
453 
455  inline auto end() {
456  return nodes.end();
457  }
458  };
459 
460 
461 }
462 
463 #endif
A natural cubic spline interpolation class.
Definition: splines.h:391
spline(const Dataset1 &X, const Dataset2 &Y)
Construct the natural cubic spline interpolation from the sets of X and Y data points.
Definition: splines.h:410
real operator()(real x) const
Evaluate the natural cubic spline interpolation at a given point.
Definition: splines.h:424
real deriv(real x) const
Evaluate the derivative of the natural cubic spline interpolation at a given point.
Definition: splines.h:437
auto end()
Get an iterator to one plus the last spline element.
Definition: splines.h:455
spline & operator=(const spline &other)
Copy from another spline.
Definition: splines.h:416
auto begin()
Get an iterator to the first spline element.
Definition: splines.h:449
spline(const DataPoints &p)
Construct the natural cubic spline interpolation from a vector of coordinate pairs.
Definition: splines.h:402
std::vector< spline_node > nodes
The computed nodes of the natural cubic spline interpolation over the points.
Definition: splines.h:396
A statically allocated N-dimensional vector with elements of the given type.
Definition: vec.h:88
Type get(unsigned int i) const
Getters and setters.
Definition: vec.h:322
Type norm() const
Compute the norm of the vector (sqrt(v * v))
Definition: vec.h:292
#define TH_MATH_ERROR(F_NAME, VALUE, EXCEPTION)
TH_MATH_ERROR is a macro which throws exceptions or modifies errno (depending on which compiling opti...
Definition: error.h:219
real beta(real x1, real x2)
Beta special function of real argument.
Definition: special.h:141
real gamma(unsigned int k)
Gamma special function of positive integer argument.
Definition: special.h:23
Main namespace of the library which contains all functions and objects.
Definition: algebra.h:27
double real
A real number, defined as a floating point type.
Definition: constants.h:188
vec< real, N > nlerp(const vec< real, N > &P1, const vec< real, N > &P2, real interp)
Normalized linear interpolation.
Definition: splines.h:76
std::vector< spline_node > cubic_splines(DataPoints p)
Compute the cubic splines interpolation of a set of data points.
Definition: splines.h:251
real smootherstep(real x1, real x2, real interp)
Smootherstep interpolation.
Definition: splines.h:134
vec< real, N > slerp(const vec< real, N > &P1, const vec< real, N > &P2, real t)
Spherical interpolation.
Definition: splines.h:85
real remap(real iFrom, real iTo, real oFrom, real oTo, real value)
Remap a value from one range to another.
Definition: splines.h:60
real smoothstep(real x1, real x2, real interp)
Smoothstep interpolation.
Definition: splines.h:118
vec< real, N > bezier(const std::vector< vec< real, N >> &points, real t)
Generic Bezier curve in N dimensions.
Definition: splines.h:190
real invlerp(real x1, real x2, real value)
Inverse linear interpolation.
Definition: splines.h:32
real lerp(real x1, real x2, real interp)
Linear interpolation.
Definition: splines.h:17
constexpr real E
The Euler mathematical constant (e)
Definition: constants.h:227
vec< real, N > quadratic_bezier(const vec< real, N > &P0, const vec< real, N > &P1, const vec< real, N > &P2, real t)
Quadratic Bezier curve.
Definition: splines.h:154
dual2 acos(dual2 x)
Compute the arcosine of a second order dual number.
Definition: dual2_functions.h:206
dual2 sin(dual2 x)
Compute the sine of a second order dual number.
Definition: dual2_functions.h:70
vec< real, N > cubic_bezier(const vec< real, N > &P0, const vec< real, N > &P1, const vec< real, N > &P2, vec< real, N > P3, real t)
Cubic Bezier curve.
Definition: splines.h:164
real nan()
Return a quiet NaN number in floating point representation.
Definition: error.h:54
real clamp(real x, real a, real b)
Clamp x between a and b.
Definition: real_analysis.h:355
A cubic splines node for a given x interval.
Definition: splines.h:213
spline_node()
Default constructor.
Definition: splines.h:223
real operator()(real X) const
Evaluate the interpolating cubic spline (no check on the input value is performed!...
Definition: splines.h:232
real deriv(real X) const
Evaluate the derivative of the interpolating cubic spline (no check on the input value is performed)
Definition: splines.h:240
real x
Upper extreme of the interpolation interval .
Definition: splines.h:216
real a
Coefficients of the interpolating cubic spline .
Definition: splines.h:220
spline_node(real x, real a, real b, real c, real d)
Construct from and polynomial coefficients.
Definition: splines.h:226