// This is a combination of this: // https://stackoverflow.com/questions/25379422/b-spline-curves/25379851#25379851 // and this, but modified to operate on a spline with any number of points intead of just >= 4: // // Spline.cc // CubicSplineLib/ // // Source file for the "CubicSpline" class. This object facilitates natural // cubic spline interpolation. Once instantiated the // constructor builds the spline polynomials on the intervals of the (x, y) // data provided and retains them for later invocation. Parallelized using // OpenMP. // // Copyright (C) Geoffrey Lentner 2015. All rights reserved. // See LICENCE file. (GPL v2.0) // // contact: Geoffrey Lentner, B.S. // Graduate Student / Researcher // 102 Natural Science Building // Department of Physics & Astronomy // University of Louisville // Louisville, KY 40292 USA // // email: geoffrey.lentner@louisville.edu // // updated: 2015-1-19 13:10:30 EST // #include "EmberPch.h" #include "Spline.h" namespace EmberNs { /// /// Constructor that takes a vector of x,y points, optionally sorts them /// and builds the spline values. /// /// The vector of x,y points /// True to skip sorting, false to sort. template Spline::Spline(const std::vector& _vals, bool sorted) { n = int(_vals.size() - 1); vals = _vals; // if not suppressed, ensure 'x' elements are in ascending order if (!sorted) std::sort(vals.begin(), vals.end(), [&](const v2T & lhs, const v2T & rhs) { return lhs.x < rhs.x; }); BuildSplines(); } /// /// Compute spline values for the passed in points. /// This only needs to be done once. /// template void Spline::BuildSplines() { a.resize(n + 1); b.resize(n + 1); c.resize(n + 1); d.resize(n + 1); std::vector w(n); std::vector h(n); std::vector ftt(n + 1); for (int i = 0; i < n; i++) { w[i] = (vals[i + 1].x - vals[i].x); h[i] = (vals[i + 1].y - vals[i].y) / w[i]; } ftt[0] = 0; for (int i = 0; i < n - 1; i++) ftt[i + 1] = 3 * (h[i + 1] - h[i]) / (w[i + 1] + w[i]); ftt[n] = 0; for (int i = 0; i < n; i++) { a[i] = (ftt[i + 1] - ftt[i]) / (6 * w[i]); b[i] = ftt[i] / 2; c[i] = h[i] - w[i] * (ftt[i + 1] + 2 * ftt[i]) / 6; d[i] = vals[i].y; } } /// /// Wrapper to generate y points on the spline for a vector of passed in points. /// /// The vector of x points to generate spline points for /// The vector of computed spline y points. template std::vector Spline::Interpolate(const std::vector& newX) { std::vector output; output.resize(newX.size()); for (int i = 0; i < newX.size(); i++) output[i] = Interpolate(newX[i]); return output; } /// /// Compute a y point on the spline for a the passed in value of x. /// /// The x points to compute the spline point for /// The computed spline y points. template T Spline::Interpolate(T newX) { ClampRef(newX, vals[0].x, vals[n].x); int j = 0; while (j < n && newX > vals[j + 1].x) j++; auto xmxj = newX - vals[j].x; auto output = a[j] * (xmxj * xmxj * xmxj) + b[j] * (xmxj * xmxj) + c[j] * xmxj + d[j]; return output; } template EMBER_API class Spline; }