NaturalSplineCurve.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/GMatrix.h>
  9. #include <Mathematics/ParametricCurve.h>
  10. namespace WwiseGTE
  11. {
  12. template <int N, typename Real>
  13. class NaturalSplineCurve : public ParametricCurve<N, Real>
  14. {
  15. public:
  16. // Construction and destruction. The object copies the input arrays.
  17. // The number of points M must be at least 2. The first constructor
  18. // is for a spline with second derivatives zero at the endpoints
  19. // (isFree = true) or a spline that is closed (isFree = false). The
  20. // second constructor is for clamped splines, where you specify the
  21. // first derivatives at the endpoints. Usually, derivative0 =
  22. // points[1] - points[0] at the first point and derivative1 =
  23. // points[M-1] - points[M-2]. To validate construction, create an
  24. // object as shown:
  25. // NaturalSplineCurve<N, Real> curve(parameters);
  26. // if (!curve) { <constructor failed, handle accordingly>; }
  27. NaturalSplineCurve(bool isFree, int numPoints,
  28. Vector<N, Real> const* points, Real const* times)
  29. :
  30. ParametricCurve<N, Real>(numPoints - 1, times)
  31. {
  32. if (numPoints < 2 || !points)
  33. {
  34. LogError("Invalid input.");
  35. return;
  36. }
  37. auto const numSegments = numPoints - 1;
  38. mCoefficients.resize(4 * numPoints + 1);
  39. mA = &mCoefficients[0];
  40. mB = mA + numPoints;
  41. mC = mB + numSegments;
  42. mD = mC + numSegments + 1;
  43. for (int i = 0; i < numPoints; ++i)
  44. {
  45. mA[i] = points[i];
  46. }
  47. if (isFree)
  48. {
  49. CreateFree();
  50. }
  51. else
  52. {
  53. CreateClosed();
  54. }
  55. this->mConstructed = true;
  56. }
  57. NaturalSplineCurve(int numPoints, Vector<N, Real> const* points,
  58. Real const* times, Vector<N, Real> const& derivative0,
  59. Vector<N, Real> const& derivative1)
  60. :
  61. ParametricCurve<N, Real>(numPoints - 1, times)
  62. {
  63. if (numPoints < 2 || !points)
  64. {
  65. LogError("Invalid input.");
  66. return;
  67. }
  68. auto const numSegments = numPoints - 1;
  69. mCoefficients.resize(numPoints + 3 * numSegments + 1);
  70. mA = &mCoefficients[0];
  71. for (int i = 0; i < numPoints; ++i)
  72. {
  73. mA[i] = points[i];
  74. }
  75. CreateClamped(derivative0, derivative1);
  76. this->mConstructed = true;
  77. }
  78. virtual ~NaturalSplineCurve() = default;
  79. // Member access.
  80. inline int GetNumPoints() const
  81. {
  82. return static_cast<int>((mCoefficients.size() - 1) / 4);
  83. }
  84. inline Vector<N, Real> const* GetPoints() const
  85. {
  86. return &mA[0];
  87. }
  88. // Evaluation of the curve. The function supports derivative
  89. // calculation through order 3; that is, order <= 3 is required. If
  90. // you want/ only the position, pass in order of 0. If you want the
  91. // position and first derivative, pass in order of 1, and so on. The
  92. // output array 'jet' must have enough storage to support the maximum
  93. // order. The values are ordered as: position, first derivative,
  94. // second derivative, third derivative.
  95. virtual void Evaluate(Real t, unsigned int order, Vector<N, Real>* jet) const override
  96. {
  97. unsigned int const supOrder = ParametricCurve<N, Real>::SUP_ORDER;
  98. if (!this->mConstructed || order >= supOrder)
  99. {
  100. // Return a zero-valued jet for invalid state.
  101. for (unsigned int i = 0; i < supOrder; ++i)
  102. {
  103. jet[i].MakeZero();
  104. }
  105. return;
  106. }
  107. int key = 0;
  108. Real dt = (Real)0;
  109. GetKeyInfo(t, key, dt);
  110. // Compute position.
  111. jet[0] = mA[key] + dt * (mB[key] + dt * (mC[key] + dt * mD[key]));
  112. if (order >= 1)
  113. {
  114. // Compute first derivative.
  115. jet[1] = mB[key] + dt * ((Real)2 * mC[key] + (Real)3 * dt * mD[key]);
  116. if (order >= 2)
  117. {
  118. // Compute second derivative.
  119. jet[2] = (Real)2 * mC[key] + (Real)6 * dt * mD[key];
  120. if (order == 3)
  121. {
  122. jet[3] = (Real)6 * mD[key];
  123. }
  124. }
  125. }
  126. }
  127. protected:
  128. // Support for construction.
  129. void CreateFree()
  130. {
  131. int numSegments = GetNumPoints() - 1;
  132. WorkingData wd(numSegments);
  133. for (int i = 0; i < numSegments; ++i)
  134. {
  135. wd.dt[i] = this->mTime[i + 1] - this->mTime[i];
  136. }
  137. std::vector<Real> d2t(numSegments);
  138. for (int i = 1; i < numSegments; ++i)
  139. {
  140. wd.d2t[i] = this->mTime[i + 1] - this->mTime[i - 1];
  141. }
  142. std::vector<Vector<N, Real>> alpha(numSegments);
  143. for (int i = 1; i < numSegments; ++i)
  144. {
  145. Vector<N, Real> numer = (Real)3 * (wd.dt[i - 1] * mA[i + 1] - wd.d2t[i] * mA[i] + wd.dt[i] * mA[i - 1]);
  146. Real invDenom = (Real)1 / (wd.dt[i - 1] * wd.dt[i]);
  147. wd.alpha[i] = invDenom * numer;
  148. }
  149. std::vector<Real> ell(numSegments + 1);
  150. std::vector<Real> mu(numSegments);
  151. std::vector<Vector<N, Real>> z(numSegments + 1);
  152. Real inv;
  153. wd.ell[0] = (Real)1;
  154. wd.mu[0] = (Real)0;
  155. wd.z[0].MakeZero();
  156. for (int i = 1; i < numSegments; ++i)
  157. {
  158. wd.ell[i] = (Real)2 * wd.d2t[i] - wd.dt[i - 1] * wd.mu[i - 1];
  159. inv = (Real)1 / wd.ell[i];
  160. wd.mu[i] = inv * wd.dt[i];
  161. wd.z[i] = inv * (wd.alpha[i] - wd.dt[i - 1] * wd.z[i - 1]);
  162. }
  163. wd.ell[numSegments] = (Real)1;
  164. wd.z[numSegments].MakeZero();
  165. Real const oneThird = (Real)1 / (Real)3;
  166. mC[numSegments].MakeZero();
  167. for (int i = numSegments - 1; i >= 0; --i)
  168. {
  169. mC[i] = wd.z[i] - wd.mu[i] * mC[i + 1];
  170. inv = (Real)1 / wd.dt[i];
  171. mB[i] = inv * (mA[i + 1] - mA[i]) - oneThird * wd.dt[i] * (mC[i + 1] + (Real)2 * mC[i]);
  172. mD[i] = oneThird * inv * (mC[i + 1] - mC[i]);
  173. }
  174. }
  175. void CreateClosed()
  176. {
  177. // TODO: A general linear system solver is used here. The matrix
  178. // corresponding to this case is actually "cyclic banded", so a
  179. // faster linear solver can be used. The current linear system
  180. // code does not have such a solver.
  181. int numSegments = GetNumPoints() - 1;
  182. std::vector<Real> dt(numSegments);
  183. for (int i = 0; i < numSegments; ++i)
  184. {
  185. dt[i] = this->mTime[i + 1] - this->mTime[i];
  186. }
  187. // Construct matrix of system.
  188. GMatrix<Real> mat(numSegments + 1, numSegments + 1);
  189. mat(0, 0) = (Real)1;
  190. mat(0, numSegments) = (Real)-1;
  191. for (int i = 1; i <= numSegments - 1; ++i)
  192. {
  193. mat(i, i - 1) = dt[i - 1];
  194. mat(i, i) = (Real)2 * (dt[i - 1] + dt[i]);
  195. mat(i, i + 1) = dt[i];
  196. }
  197. mat(numSegments, numSegments - 1) = dt[numSegments - 1];
  198. mat(numSegments, 0) = (Real)2 * (dt[numSegments - 1] + dt[0]);
  199. mat(numSegments, 1) = dt[0];
  200. // Construct right-hand side of system.
  201. mC[0].MakeZero();
  202. Real inv0, inv1;
  203. for (int i = 1; i <= numSegments - 1; ++i)
  204. {
  205. inv0 = (Real)1 / dt[i];
  206. inv1 = (Real)1 / dt[i - 1];
  207. mC[i] = (Real)3 * (inv0 * (mA[i + 1] - mA[i]) -
  208. inv1 * (mA[i] - mA[i - 1]));
  209. }
  210. inv0 = (Real)1 / dt[0];
  211. inv1 = (Real)1 / dt[numSegments - 1];
  212. mC[numSegments] = (Real)3 * (inv0 * (mA[1] - mA[0]) -
  213. inv1 * (mA[0] - mA[numSegments - 1]));
  214. // Solve the linear systems.
  215. GMatrix<Real> invMat = Inverse(mat);
  216. GVector<Real> input(numSegments + 1);
  217. GVector<Real> output(numSegments + 1);
  218. for (int j = 0; j < N; ++j)
  219. {
  220. for (int i = 0; i <= numSegments; ++i)
  221. {
  222. input[i] = mC[i][j];
  223. }
  224. output = invMat * input;
  225. for (int i = 0; i <= numSegments; ++i)
  226. {
  227. mC[i][j] = output[i];
  228. }
  229. }
  230. Real const oneThird = (Real)1 / (Real)3;
  231. for (int i = 0; i < numSegments; ++i)
  232. {
  233. inv0 = (Real)1 / dt[i];
  234. mB[i] = inv0 * (mA[i + 1] - mA[i]) - oneThird * (mC[i + 1] + (Real)2 * mC[i]) * dt[i];
  235. mD[i] = oneThird * inv0 * (mC[i + 1] - mC[i]);
  236. }
  237. }
  238. void CreateClamped(Vector<N, Real> const& derivative0, Vector<N, Real> const& derivative1)
  239. {
  240. int numSegments = GetNumPoints() - 1;
  241. std::vector<Real> dt(numSegments);
  242. for (int i = 0; i < numSegments; ++i)
  243. {
  244. dt[i] = this->mTime[i + 1] - this->mTime[i];
  245. }
  246. std::vector<Real> d2t(numSegments);
  247. for (int i = 1; i < numSegments; ++i)
  248. {
  249. d2t[i] = this->mTime[i + 1] - this->mTime[i - 1];
  250. }
  251. std::vector<Vector<N, Real>> alpha(numSegments + 1);
  252. Real inv = (Real)1 / dt[0];
  253. alpha[0] = (Real)3 * (inv * (mA[1] - mA[0]) - derivative0);
  254. inv = (Real)1 / dt[numSegments - 1];
  255. alpha[numSegments] = (Real)3 * (derivative1 -
  256. inv * (mA[numSegments] - mA[numSegments - 1]));
  257. for (int i = 1; i < numSegments; ++i)
  258. {
  259. Vector<N, Real> numer = (Real)3 * (dt[i - 1] * mA[i + 1] -
  260. d2t[i] * mA[i] + dt[i] * mA[i - 1]);
  261. Real invDenom = (Real)1 / (dt[i - 1] * dt[i]);
  262. alpha[i] = invDenom * numer;
  263. }
  264. std::vector<Real> ell(numSegments + 1);
  265. std::vector<Real> mu(numSegments);
  266. std::vector<Vector<N, Real>> z(numSegments + 1);
  267. ell[0] = (Real)2 * dt[0];
  268. mu[0] = (Real)0.5;
  269. inv = (Real)1 / ell[0];
  270. z[0] = inv * alpha[0];
  271. for (int i = 1; i < numSegments; ++i)
  272. {
  273. ell[i] = (Real)2 * d2t[i] - dt[i - 1] * mu[i - 1];
  274. inv = (Real)1 / ell[i];
  275. mu[i] = inv * dt[i];
  276. z[i] = inv * (alpha[i] - dt[i - 1] * z[i - 1]);
  277. }
  278. ell[numSegments] = dt[numSegments - 1] * ((Real)2 - mu[numSegments - 1]);
  279. inv = (Real)1 / ell[numSegments];
  280. z[numSegments] = inv * (alpha[numSegments] - dt[numSegments - 1] *
  281. z[numSegments - 1]);
  282. Real const oneThird = (Real)1 / (Real)3;
  283. mC[numSegments] = z[numSegments];
  284. for (int i = numSegments - 1; i >= 0; --i)
  285. {
  286. mC[i] = z[i] - mu[i] * mC[i + 1];
  287. inv = (Real)1 / dt[i];
  288. mB[i] = inv * (mA[i + 1] - mA[i]) - oneThird * dt[i] * (mC[i + 1] +
  289. (Real)2 * mC[i]);
  290. mD[i] = oneThird * inv * (mC[i + 1] - mC[i]);
  291. }
  292. }
  293. // Determine the index i for which times[i] <= t < times[i+1].
  294. void GetKeyInfo(Real t, int& key, Real& dt) const
  295. {
  296. int numSegments = GetNumPoints() - 1;
  297. if (t <= this->mTime[0])
  298. {
  299. key = 0;
  300. dt = (Real)0;
  301. }
  302. else if (t >= this->mTime[numSegments])
  303. {
  304. key = numSegments - 1;
  305. dt = this->mTime[numSegments] - this->mTime[numSegments - 1];
  306. }
  307. else
  308. {
  309. for (int i = 0; i < numSegments; ++i)
  310. {
  311. if (t < this->mTime[i + 1])
  312. {
  313. key = i;
  314. dt = t - this->mTime[i];
  315. break;
  316. }
  317. }
  318. }
  319. }
  320. // Polynomial coefficients. mA are the points (constant coefficients of
  321. // polynomials. mB are the degree 1 coefficients, mC are the degree 2
  322. // coefficients, and mD are the degree 3 coefficients.
  323. Vector<N, Real>* mA;
  324. Vector<N, Real>* mB;
  325. Vector<N, Real>* mC;
  326. Vector<N, Real>* mD;
  327. private:
  328. std::vector<Vector<N, Real>> mCoefficients;
  329. struct WorkingData
  330. {
  331. WorkingData(int numSegments)
  332. {
  333. data.resize(4 * numSegments + 1 + N * (2 * numSegments + 1));
  334. dt = &data[0];
  335. d2t = dt + numSegments;
  336. alpha = reinterpret_cast<Vector<N, Real>*>(d2t + numSegments);
  337. ell = reinterpret_cast<Real*>(alpha + numSegments);
  338. mu = ell + numSegments + 1;
  339. z = reinterpret_cast<Vector<N, Real>*>(mu + numSegments);
  340. }
  341. Real* dt;
  342. Real* d2t;
  343. Vector<N, Real>* alpha;
  344. Real* ell;
  345. Real* mu;
  346. Vector<N, Real>* z;
  347. std::vector<Real> data;
  348. };
  349. };
  350. }