BasisFunction.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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/Logger.h>
  9. #include <Mathematics/Math.h>
  10. #include <Mathematics/Array2.h>
  11. #include <array>
  12. #include <cstring>
  13. namespace WwiseGTE
  14. {
  15. template <typename Real>
  16. struct UniqueKnot
  17. {
  18. Real t;
  19. int multiplicity;
  20. };
  21. template <typename Real>
  22. struct BasisFunctionInput
  23. {
  24. // The members are uninitialized.
  25. BasisFunctionInput()
  26. {
  27. }
  28. // Construct an open uniform curve with t in [0,1].
  29. BasisFunctionInput(int inNumControls, int inDegree)
  30. :
  31. numControls(inNumControls),
  32. degree(inDegree),
  33. uniform(true),
  34. periodic(false),
  35. numUniqueKnots(numControls - degree + 1),
  36. uniqueKnots(numUniqueKnots)
  37. {
  38. uniqueKnots.front().t = (Real)0;
  39. uniqueKnots.front().multiplicity = degree + 1;
  40. for (int i = 1; i <= numUniqueKnots - 2; ++i)
  41. {
  42. uniqueKnots[i].t = i / (numUniqueKnots - (Real)1);
  43. uniqueKnots[i].multiplicity = 1;
  44. }
  45. uniqueKnots.back().t = (Real)1;
  46. uniqueKnots.back().multiplicity = degree + 1;
  47. }
  48. int numControls;
  49. int degree;
  50. bool uniform;
  51. bool periodic;
  52. int numUniqueKnots;
  53. std::vector<UniqueKnot<Real>> uniqueKnots;
  54. };
  55. template <typename Real>
  56. class BasisFunction
  57. {
  58. public:
  59. // Let n be the number of control points. Let d be the degree, where
  60. // 1 <= d <= n-1. The number of knots is k = n + d + 1. The knots
  61. // are t[i] for 0 <= i < k and must be nondecreasing, t[i] <= t[i+1],
  62. // but a knot value can be repeated. Let s be the number of distinct
  63. // knots. Let the distinct knots be u[j] for 0 <= j < s, so u[j] <
  64. // u[j+1] for all j. The set of u[j] is called a 'breakpoint
  65. // sequence'. Let m[j] >= 1 be the multiplicity; that is, if t[i] is
  66. // the first occurrence of u[j], then t[i+r] = t[i] for 1 <= r < m[j].
  67. // The multiplicities have the constraints m[0] <= d+1, m[s-1] <= d+1,
  68. // and m[j] <= d for 1 <= j <= s-2. Also, k = sum_{j=0}^{s-1} m[j],
  69. // which says the multiplicities account for all k knots.
  70. //
  71. // Given a knot vector (t[0],...,t[n+d]), the domain of the
  72. // corresponding B-spline curve is the interval [t[d],t[n]].
  73. //
  74. // The corresponding B-spline or NURBS curve is characterized as
  75. // follows. See "Geometric Modeling with Splines: An Introduction" by
  76. // Elaine Cohen, Richard F. Riesenfeld and Gershon Elber, AK Peters,
  77. // 2001, Natick MA. The curve is 'open' when m[0] = m[s-1] = d+1;
  78. // otherwise, it is 'floating'. An open curve is uniform when the
  79. // knots t[d] through t[n] are equally spaced; that is, t[i+1] - t[i]
  80. // are a common value for d <= i <= n-1. By implication, s = n-d+1
  81. // and m[j] = 1 for 1 <= j <= s-2. An open curve that does not
  82. // satisfy these conditions is said to be nonuniform. A floating
  83. // curve is uniform when m[j] = 1 for 0 <= j <= s-1 and t[i+1] - t[i]
  84. // are a common value for 0 <= i <= k-2; otherwise, the floating curve
  85. // is nonuniform.
  86. //
  87. // A special case of a floating curve is a periodic curve. The intent
  88. // is that the curve is closed, so the first and last control points
  89. // should be the same, which ensures C^{0} continuity. Higher-order
  90. // continuity is obtained by repeating more control points. If the
  91. // control points are P[0] through P[n-1], append the points P[0]
  92. // through P[d-1] to ensure C^{d-1} continuity. Additionally, the
  93. // knots must be chosen properly. You may choose t[d] through t[n] as
  94. // you wish. The other knots are defined by
  95. // t[i] - t[i-1] = t[n-d+i] - t[n-d+i-1]
  96. // t[n+i] - t[n+i-1] = t[d+i] - t[d+i-1]
  97. // for 1 <= i <= d.
  98. // Construction and destruction. The determination that the curve is
  99. // open or floating is based on the multiplicities. The 'uniform'
  100. // input is used to avoid misclassifications due to floating-point
  101. // rounding errors. Specifically, the breakpoints might be equally
  102. // spaced (uniform) as real numbers, but the floating-point
  103. // representations can have rounding errors that cause the knot
  104. // differences not to be exactly the same constant. A periodic curve
  105. // can have uniform or nonuniform knots. This object makes copies of
  106. // the input arrays.
  107. BasisFunction()
  108. :
  109. mNumControls(0),
  110. mDegree(0),
  111. mTMin((Real)0),
  112. mTMax((Real)0),
  113. mTLength((Real)0),
  114. mOpen(false),
  115. mUniform(false),
  116. mPeriodic(false)
  117. {
  118. }
  119. BasisFunction(BasisFunctionInput<Real> const& input)
  120. :
  121. mNumControls(0),
  122. mDegree(0),
  123. mTMin((Real)0),
  124. mTMax((Real)0),
  125. mTLength((Real)0),
  126. mOpen(false),
  127. mUniform(false),
  128. mPeriodic(false)
  129. {
  130. Create(input);
  131. }
  132. ~BasisFunction()
  133. {
  134. }
  135. // No copying is allowed.
  136. BasisFunction(BasisFunction const&) = delete;
  137. BasisFunction& operator=(BasisFunction const&) = delete;
  138. // Support for explicit creation in classes that have std::array
  139. // members involving BasisFunction. This is a call-once function.
  140. void Create(BasisFunctionInput<Real> const& input)
  141. {
  142. LogAssert(mNumControls == 0 && mDegree == 0, "Object already created.");
  143. LogAssert(input.numControls >= 2, "Invalid number of control points.");
  144. LogAssert(1 <= input.degree && input.degree < input.numControls, "Invalid degree.");
  145. LogAssert(input.numUniqueKnots >= 2, "Invalid number of unique knots.");
  146. mNumControls = (input.periodic ? input.numControls + input.degree : input.numControls);
  147. mDegree = input.degree;
  148. mTMin = (Real)0;
  149. mTMax = (Real)0;
  150. mTLength = (Real)0;
  151. mOpen = false;
  152. mUniform = input.uniform;
  153. mPeriodic = input.periodic;
  154. for (int i = 0; i < 4; ++i)
  155. {
  156. mJet[i] = Array2<Real>();
  157. }
  158. mUniqueKnots.resize(input.numUniqueKnots);
  159. std::copy(input.uniqueKnots.begin(),
  160. input.uniqueKnots.begin() + input.numUniqueKnots,
  161. mUniqueKnots.begin());
  162. Real u = mUniqueKnots.front().t;
  163. for (int i = 1; i < input.numUniqueKnots - 1; ++i)
  164. {
  165. Real uNext = mUniqueKnots[i].t;
  166. LogAssert(u < uNext, "Unique knots are not strictly increasing.");
  167. u = uNext;
  168. }
  169. int mult0 = mUniqueKnots.front().multiplicity;
  170. LogAssert(mult0 >= 1 && mult0 <= mDegree + 1, "Invalid first multiplicity.");
  171. int mult1 = mUniqueKnots.back().multiplicity;
  172. LogAssert(mult1 >= 1 && mult1 <= mDegree + 1, "Invalid last multiplicity.");
  173. for (int i = 1; i <= input.numUniqueKnots - 2; ++i)
  174. {
  175. int mult = mUniqueKnots[i].multiplicity;
  176. LogAssert(mult >= 1 && mult <= mDegree + 1, "Invalid interior multiplicity.");
  177. }
  178. mOpen = (mult0 == mult1 && mult0 == mDegree + 1);
  179. mKnots.resize(mNumControls + mDegree + 1);
  180. mKeys.resize(input.numUniqueKnots);
  181. int sum = 0;
  182. for (int i = 0, j = 0; i < input.numUniqueKnots; ++i)
  183. {
  184. Real tCommon = mUniqueKnots[i].t;
  185. int mult = mUniqueKnots[i].multiplicity;
  186. for (int k = 0; k < mult; ++k, ++j)
  187. {
  188. mKnots[j] = tCommon;
  189. }
  190. mKeys[i].first = tCommon;
  191. mKeys[i].second = sum - 1;
  192. sum += mult;
  193. }
  194. mTMin = mKnots[mDegree];
  195. mTMax = mKnots[mNumControls];
  196. mTLength = mTMax - mTMin;
  197. size_t numRows = mDegree + 1;
  198. size_t numCols = mNumControls + mDegree;
  199. size_t numBytes = numRows * numCols * sizeof(Real);
  200. for (int i = 0; i < 4; ++i)
  201. {
  202. mJet[i] = Array2<Real>(numCols, numRows);
  203. std::memset(mJet[i][0], 0, numBytes);
  204. }
  205. }
  206. // Member access.
  207. inline int GetNumControls() const
  208. {
  209. return mNumControls;
  210. }
  211. inline int GetDegree() const
  212. {
  213. return mDegree;
  214. }
  215. inline int GetNumUniqueKnots() const
  216. {
  217. return static_cast<int>(mUniqueKnots.size());
  218. }
  219. inline int GetNumKnots() const
  220. {
  221. return static_cast<int>(mKnots.size());
  222. }
  223. inline Real GetMinDomain() const
  224. {
  225. return mTMin;
  226. }
  227. inline Real GetMaxDomain() const
  228. {
  229. return mTMax;
  230. }
  231. inline bool IsOpen() const
  232. {
  233. return mOpen;
  234. }
  235. inline bool IsUniform() const
  236. {
  237. return mUniform;
  238. }
  239. inline bool IsPeriodic() const
  240. {
  241. return mPeriodic;
  242. }
  243. inline UniqueKnot<Real> const* GetUniqueKnots() const
  244. {
  245. return &mUniqueKnots[0];
  246. }
  247. inline Real const* GetKnots() const
  248. {
  249. return &mKnots[0];
  250. }
  251. // Evaluation of the basis function and its derivatives through
  252. // order 3. For the function value only, pass order 0. For the
  253. // function and first derivative, pass order 1, and so on.
  254. void Evaluate(Real t, unsigned int order, int& minIndex, int& maxIndex) const
  255. {
  256. LogAssert(order <= 3, "Invalid order.");
  257. int i = GetIndex(t);
  258. mJet[0][0][i] = (Real)1;
  259. if (order >= 1)
  260. {
  261. mJet[1][0][i] = (Real)0;
  262. if (order >= 2)
  263. {
  264. mJet[2][0][i] = (Real)0;
  265. if (order >= 3)
  266. {
  267. mJet[3][0][i] = (Real)0;
  268. }
  269. }
  270. }
  271. Real n0 = t - mKnots[i], n1 = mKnots[i + 1] - t;
  272. Real e0, e1, d0, d1, invD0, invD1;
  273. int j;
  274. for (j = 1; j <= mDegree; j++)
  275. {
  276. d0 = mKnots[i + j] - mKnots[i];
  277. d1 = mKnots[i + 1] - mKnots[i - j + 1];
  278. invD0 = (d0 > (Real)0 ? (Real)1 / d0 : (Real)0);
  279. invD1 = (d1 > (Real)0 ? (Real)1 / d1 : (Real)0);
  280. e0 = n0 * mJet[0][j - 1][i];
  281. mJet[0][j][i] = e0 * invD0;
  282. e1 = n1 * mJet[0][j - 1][i - j + 1];
  283. mJet[0][j][i - j] = e1 * invD1;
  284. if (order >= 1)
  285. {
  286. e0 = n0 * mJet[1][j - 1][i] + mJet[0][j - 1][i];
  287. mJet[1][j][i] = e0 * invD0;
  288. e1 = n1 * mJet[1][j - 1][i - j + 1] - mJet[0][j - 1][i - j + 1];
  289. mJet[1][j][i - j] = e1 * invD1;
  290. if (order >= 2)
  291. {
  292. e0 = n0 * mJet[2][j - 1][i] + ((Real)2) * mJet[1][j - 1][i];
  293. mJet[2][j][i] = e0 * invD0;
  294. e1 = n1 * mJet[2][j - 1][i - j + 1] - ((Real)2) * mJet[1][j - 1][i - j + 1];
  295. mJet[2][j][i - j] = e1 * invD1;
  296. if (order >= 3)
  297. {
  298. e0 = n0 * mJet[3][j - 1][i] + ((Real)3) * mJet[2][j - 1][i];
  299. mJet[3][j][i] = e0 * invD0;
  300. e1 = n1 * mJet[3][j - 1][i - j + 1] - ((Real)3) * mJet[2][j - 1][i - j + 1];
  301. mJet[3][j][i - j] = e1 * invD1;
  302. }
  303. }
  304. }
  305. }
  306. for (j = 2; j <= mDegree; ++j)
  307. {
  308. for (int k = i - j + 1; k < i; ++k)
  309. {
  310. n0 = t - mKnots[k];
  311. n1 = mKnots[k + j + 1] - t;
  312. d0 = mKnots[k + j] - mKnots[k];
  313. d1 = mKnots[k + j + 1] - mKnots[k + 1];
  314. invD0 = (d0 > (Real)0 ? (Real)1 / d0 : (Real)0);
  315. invD1 = (d1 > (Real)0 ? (Real)1 / d1 : (Real)0);
  316. e0 = n0 * mJet[0][j - 1][k];
  317. e1 = n1 * mJet[0][j - 1][k + 1];
  318. mJet[0][j][k] = e0 * invD0 + e1 * invD1;
  319. if (order >= 1)
  320. {
  321. e0 = n0 * mJet[1][j - 1][k] + mJet[0][j - 1][k];
  322. e1 = n1 * mJet[1][j - 1][k + 1] - mJet[0][j - 1][k + 1];
  323. mJet[1][j][k] = e0 * invD0 + e1 * invD1;
  324. if (order >= 2)
  325. {
  326. e0 = n0 * mJet[2][j - 1][k] + ((Real)2) * mJet[1][j - 1][k];
  327. e1 = n1 * mJet[2][j - 1][k + 1] - ((Real)2) * mJet[1][j - 1][k + 1];
  328. mJet[2][j][k] = e0 * invD0 + e1 * invD1;
  329. if (order >= 3)
  330. {
  331. e0 = n0 * mJet[3][j - 1][k] + ((Real)3) * mJet[2][j - 1][k];
  332. e1 = n1 * mJet[3][j - 1][k + 1] - ((Real)3) * mJet[2][j - 1][k + 1];
  333. mJet[3][j][k] = e0 * invD0 + e1 * invD1;
  334. }
  335. }
  336. }
  337. }
  338. }
  339. minIndex = i - mDegree;
  340. maxIndex = i;
  341. }
  342. // Access the results of the call to Evaluate(...). The index i must
  343. // satisfy minIndex <= i <= maxIndex. If it is not, the function
  344. // returns zero. The separation of evaluation and access is based on
  345. // local control of the basis function; that is, only the accessible
  346. // values are (potentially) not zero.
  347. Real GetValue(unsigned int order, int i) const
  348. {
  349. if (order < 4)
  350. {
  351. if (0 <= i && i < mNumControls + mDegree)
  352. {
  353. return mJet[order][mDegree][i];
  354. }
  355. LogError("Invalid index.");
  356. }
  357. LogError("Invalid order.");
  358. }
  359. private:
  360. // Determine the index i for which knot[i] <= t < knot[i+1]. The
  361. // t-value is modified (wrapped for periodic splines, clamped for
  362. // nonperiodic splines).
  363. int GetIndex(Real& t) const
  364. {
  365. // Find the index i for which knot[i] <= t < knot[i+1].
  366. if (mPeriodic)
  367. {
  368. // Wrap to [tmin,tmax].
  369. Real r = std::fmod(t - mTMin, mTLength);
  370. if (r < (Real)0)
  371. {
  372. r += mTLength;
  373. }
  374. t = mTMin + r;
  375. }
  376. // Clamp to [tmin,tmax]. For the periodic case, this handles
  377. // small numerical rounding errors near the domain endpoints.
  378. if (t <= mTMin)
  379. {
  380. t = mTMin;
  381. return mDegree;
  382. }
  383. if (t >= mTMax)
  384. {
  385. t = mTMax;
  386. return mNumControls - 1;
  387. }
  388. // At this point, tmin < t < tmax.
  389. for (auto const& key : mKeys)
  390. {
  391. if (t < key.first)
  392. {
  393. return key.second;
  394. }
  395. }
  396. // We should not reach this code.
  397. LogError("Unexpected condition.");
  398. }
  399. // Constructor inputs and values derived from them.
  400. int mNumControls;
  401. int mDegree;
  402. Real mTMin, mTMax, mTLength;
  403. bool mOpen;
  404. bool mUniform;
  405. bool mPeriodic;
  406. std::vector<UniqueKnot<Real>> mUniqueKnots;
  407. std::vector<Real> mKnots;
  408. // Lookup information for the GetIndex() function. The first element
  409. // of the pair is a unique knot value. The second element is the
  410. // index in mKnots[] for the last occurrence of that knot value.
  411. std::vector<std::pair<Real, int>> mKeys;
  412. // Storage for the basis functions and their first three derivatives;
  413. // mJet[i] is array[d+1][n+d].
  414. mutable std::array<Array2<Real>, 4> mJet;
  415. };
  416. }