IntpAkimaUniform2.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  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 <algorithm>
  12. #include <array>
  13. #include <cstring>
  14. // The interpolator is for uniformly spaced (x,y)-values. The input samples
  15. // F must be stored in row-major order to represent f(x,y); that is,
  16. // F[c + xBound*r] corresponds to f(x,y), where c is the index corresponding
  17. // to x and r is the index corresponding to y.
  18. namespace WwiseGTE
  19. {
  20. template <typename Real>
  21. class IntpAkimaUniform2
  22. {
  23. public:
  24. // Construction and destruction.
  25. IntpAkimaUniform2(int xBound, int yBound, Real xMin, Real xSpacing,
  26. Real yMin, Real ySpacing, Real const* F)
  27. :
  28. mXBound(xBound),
  29. mYBound(yBound),
  30. mQuantity(xBound* yBound),
  31. mXMin(xMin),
  32. mXSpacing(xSpacing),
  33. mYMin(yMin),
  34. mYSpacing(ySpacing),
  35. mF(F),
  36. mPoly(xBound - 1, mYBound - 1)
  37. {
  38. // At least a 3x3 block of data points is needed to construct the
  39. // estimates of the boundary derivatives.
  40. LogAssert(mXBound >= 3 && mYBound >= 3 && mF != nullptr, "Invalid input.");
  41. LogAssert(mXSpacing > (Real)0 && mYSpacing > (Real)0, "Invalid input.");
  42. mXMax = mXMin + mXSpacing * static_cast<Real>(mXBound - 1);
  43. mYMax = mYMin + mYSpacing * static_cast<Real>(mYBound - 1);
  44. // Create a 2D wrapper for the 1D samples.
  45. Array2<Real> Fmap(mXBound, mYBound, const_cast<Real*>(mF));
  46. // Construct first-order derivatives.
  47. Array2<Real> FX(mXBound, mYBound), FY(mXBound, mYBound);
  48. GetFX(Fmap, FX);
  49. GetFY(Fmap, FY);
  50. // Construct second-order derivatives.
  51. Array2<Real> FXY(mXBound, mYBound);
  52. GetFXY(Fmap, FXY);
  53. // Construct polynomials.
  54. GetPolynomials(Fmap, FX, FY, FXY);
  55. }
  56. ~IntpAkimaUniform2() = default;
  57. // Member access.
  58. inline int GetXBound() const
  59. {
  60. return mXBound;
  61. }
  62. inline int GetYBound() const
  63. {
  64. return mYBound;
  65. }
  66. inline int GetQuantity() const
  67. {
  68. return mQuantity;
  69. }
  70. inline Real const* GetF() const
  71. {
  72. return mF;
  73. }
  74. inline Real GetXMin() const
  75. {
  76. return mXMin;
  77. }
  78. inline Real GetXMax() const
  79. {
  80. return mXMax;
  81. }
  82. inline Real GetXSpacing() const
  83. {
  84. return mXSpacing;
  85. }
  86. inline Real GetYMin() const
  87. {
  88. return mYMin;
  89. }
  90. inline Real GetYMax() const
  91. {
  92. return mYMax;
  93. }
  94. inline Real GetYSpacing() const
  95. {
  96. return mYSpacing;
  97. }
  98. // Evaluate the function and its derivatives. The functions clamp the
  99. // inputs to xmin <= x <= xmax and ymin <= y <= ymax. The first
  100. // operator is for function evaluation. The second operator is for
  101. // function or derivative evaluations. The xOrder argument is the
  102. // order of the x-derivative and the yOrder argument is the order of
  103. // the y-derivative. Both orders are zero to get the function value
  104. // itself.
  105. Real operator()(Real x, Real y) const
  106. {
  107. x = std::min(std::max(x, mXMin), mXMax);
  108. y = std::min(std::max(y, mYMin), mYMax);
  109. int ix, iy;
  110. Real dx, dy;
  111. XLookup(x, ix, dx);
  112. YLookup(y, iy, dy);
  113. return mPoly[iy][ix](dx, dy);
  114. }
  115. Real operator()(int xOrder, int yOrder, Real x, Real y) const
  116. {
  117. x = std::min(std::max(x, mXMin), mXMax);
  118. y = std::min(std::max(y, mYMin), mYMax);
  119. int ix, iy;
  120. Real dx, dy;
  121. XLookup(x, ix, dx);
  122. YLookup(y, iy, dy);
  123. return mPoly[iy][ix](xOrder, yOrder, dx, dy);
  124. }
  125. private:
  126. class Polynomial
  127. {
  128. public:
  129. Polynomial()
  130. {
  131. for (size_t i = 0; i < 4; ++i)
  132. {
  133. mCoeff[i].fill((Real)0);
  134. }
  135. }
  136. // P(x,y) = (1,x,x^2,x^3)*A*(1,y,y^2,y^3). The matrix term A[ix][iy]
  137. // corresponds to the polynomial term x^{ix} y^{iy}.
  138. Real& A(int ix, int iy)
  139. {
  140. return mCoeff[ix][iy];
  141. }
  142. Real operator()(Real x, Real y) const
  143. {
  144. std::array<Real, 4> B;
  145. for (int i = 0; i <= 3; ++i)
  146. {
  147. B[i] = mCoeff[i][0] + y * (mCoeff[i][1] + y * (mCoeff[i][2] + y * mCoeff[i][3]));
  148. }
  149. return B[0] + x * (B[1] + x * (B[2] + x * B[3]));
  150. }
  151. Real operator()(int xOrder, int yOrder, Real x, Real y) const
  152. {
  153. std::array<Real, 4> xPow;
  154. switch (xOrder)
  155. {
  156. case 0:
  157. xPow[0] = (Real)1;
  158. xPow[1] = x;
  159. xPow[2] = x * x;
  160. xPow[3] = x * x * x;
  161. break;
  162. case 1:
  163. xPow[0] = (Real)0;
  164. xPow[1] = (Real)1;
  165. xPow[2] = (Real)2 * x;
  166. xPow[3] = (Real)3 * x * x;
  167. break;
  168. case 2:
  169. xPow[0] = (Real)0;
  170. xPow[1] = (Real)0;
  171. xPow[2] = (Real)2;
  172. xPow[3] = (Real)6 * x;
  173. break;
  174. case 3:
  175. xPow[0] = (Real)0;
  176. xPow[1] = (Real)0;
  177. xPow[2] = (Real)0;
  178. xPow[3] = (Real)6;
  179. break;
  180. default:
  181. return (Real)0;
  182. }
  183. std::array<Real, 4> yPow;
  184. switch (yOrder)
  185. {
  186. case 0:
  187. yPow[0] = (Real)1;
  188. yPow[1] = y;
  189. yPow[2] = y * y;
  190. yPow[3] = y * y * y;
  191. break;
  192. case 1:
  193. yPow[0] = (Real)0;
  194. yPow[1] = (Real)1;
  195. yPow[2] = (Real)2 * y;
  196. yPow[3] = (Real)3 * y * y;
  197. break;
  198. case 2:
  199. yPow[0] = (Real)0;
  200. yPow[1] = (Real)0;
  201. yPow[2] = (Real)2;
  202. yPow[3] = (Real)6 * y;
  203. break;
  204. case 3:
  205. yPow[0] = (Real)0;
  206. yPow[1] = (Real)0;
  207. yPow[2] = (Real)0;
  208. yPow[3] = (Real)6;
  209. break;
  210. default:
  211. return (Real)0;
  212. }
  213. Real p = (Real)0;
  214. for (size_t iy = 0; iy <= 3; ++iy)
  215. {
  216. for (size_t ix = 0; ix <= 3; ++ix)
  217. {
  218. p += mCoeff[ix][iy] * xPow[ix] * yPow[iy];
  219. }
  220. }
  221. return p;
  222. }
  223. private:
  224. std::array<std::array<Real, 4>, 4> mCoeff;
  225. };
  226. // Support for construction.
  227. void GetFX(Array2<Real> const& F, Array2<Real>& FX)
  228. {
  229. Array2<Real> slope(mXBound + 3, mYBound);
  230. Real invDX = (Real)1 / mXSpacing;
  231. int ix, iy;
  232. for (iy = 0; iy < mYBound; ++iy)
  233. {
  234. for (ix = 0; ix < mXBound - 1; ++ix)
  235. {
  236. slope[iy][ix + 2] = (F[iy][ix + 1] - F[iy][ix]) * invDX;
  237. }
  238. slope[iy][1] = (Real)2 * slope[iy][2] - slope[iy][3];
  239. slope[iy][0] = (Real)2 * slope[iy][1] - slope[iy][2];
  240. slope[iy][mXBound + 1] = (Real)2 * slope[iy][mXBound] - slope[iy][mXBound - 1];
  241. slope[iy][mXBound + 2] = (Real)2 * slope[iy][mXBound + 1] - slope[iy][mXBound];
  242. }
  243. for (iy = 0; iy < mYBound; ++iy)
  244. {
  245. for (ix = 0; ix < mXBound; ++ix)
  246. {
  247. FX[iy][ix] = ComputeDerivative(slope[iy] + ix);
  248. }
  249. }
  250. }
  251. void GetFY(Array2<Real> const& F, Array2<Real>& FY)
  252. {
  253. Array2<Real> slope(mYBound + 3, mXBound);
  254. Real invDY = (Real)1 / mYSpacing;
  255. int ix, iy;
  256. for (ix = 0; ix < mXBound; ++ix)
  257. {
  258. for (iy = 0; iy < mYBound - 1; ++iy)
  259. {
  260. slope[ix][iy + 2] = (F[iy + 1][ix] - F[iy][ix]) * invDY;
  261. }
  262. slope[ix][1] = (Real)2 * slope[ix][2] - slope[ix][3];
  263. slope[ix][0] = (Real)2 * slope[ix][1] - slope[ix][2];
  264. slope[ix][mYBound + 1] = (Real)2 * slope[ix][mYBound] - slope[ix][mYBound - 1];
  265. slope[ix][mYBound + 2] = (Real)2 * slope[ix][mYBound + 1] - slope[ix][mYBound];
  266. }
  267. for (ix = 0; ix < mXBound; ++ix)
  268. {
  269. for (iy = 0; iy < mYBound; ++iy)
  270. {
  271. FY[iy][ix] = ComputeDerivative(slope[ix] + iy);
  272. }
  273. }
  274. }
  275. void GetFXY(Array2<Real> const& F, Array2<Real>& FXY)
  276. {
  277. int xBoundM1 = mXBound - 1;
  278. int yBoundM1 = mYBound - 1;
  279. int ix0 = xBoundM1, ix1 = ix0 - 1, ix2 = ix1 - 1;
  280. int iy0 = yBoundM1, iy1 = iy0 - 1, iy2 = iy1 - 1;
  281. int ix, iy;
  282. Real invDXDY = (Real)1 / (mXSpacing * mYSpacing);
  283. // corners
  284. FXY[0][0] = (Real)0.25 * invDXDY * (
  285. (Real)9 * F[0][0]
  286. - (Real)12 * F[0][1]
  287. + (Real)3 * F[0][2]
  288. - (Real)12 * F[1][0]
  289. + (Real)16 * F[1][1]
  290. - (Real)4 * F[1][2]
  291. + (Real)3 * F[2][0]
  292. - (Real)4 * F[2][1]
  293. + F[2][2]);
  294. FXY[0][xBoundM1] = (Real)0.25 * invDXDY * (
  295. (Real)9 * F[0][ix0]
  296. - (Real)12 * F[0][ix1]
  297. + (Real)3 * F[0][ix2]
  298. - (Real)12 * F[1][ix0]
  299. + (Real)16 * F[1][ix1]
  300. - (Real)4 * F[1][ix2]
  301. + (Real)3 * F[2][ix0]
  302. - (Real)4 * F[2][ix1]
  303. + F[2][ix2]);
  304. FXY[yBoundM1][0] = (Real)0.25 * invDXDY * (
  305. (Real)9 * F[iy0][0]
  306. - (Real)12 * F[iy0][1]
  307. + (Real)3 * F[iy0][2]
  308. - (Real)12 * F[iy1][0]
  309. + (Real)16 * F[iy1][1]
  310. - (Real)4 * F[iy1][2]
  311. + (Real)3 * F[iy2][0]
  312. - (Real)4 * F[iy2][1]
  313. + F[iy2][2]);
  314. FXY[yBoundM1][xBoundM1] = (Real)0.25 * invDXDY * (
  315. (Real)9 * F[iy0][ix0]
  316. - (Real)12 * F[iy0][ix1]
  317. + (Real)3 * F[iy0][ix2]
  318. - (Real)12 * F[iy1][ix0]
  319. + (Real)16 * F[iy1][ix1]
  320. - (Real)4 * F[iy1][ix2]
  321. + (Real)3 * F[iy2][ix0]
  322. - (Real)4 * F[iy2][ix1]
  323. + F[iy2][ix2]);
  324. // x-edges
  325. for (ix = 1; ix < xBoundM1; ++ix)
  326. {
  327. FXY[0][ix] = (Real)0.25 * invDXDY * (
  328. (Real)3 * (F[0][ix - 1] - F[0][ix + 1])
  329. - (Real)4 * (F[1][ix - 1] - F[1][ix + 1])
  330. + (F[2][ix - 1] - F[2][ix + 1]));
  331. FXY[yBoundM1][ix] = (Real)0.25 * invDXDY * (
  332. (Real)3 * (F[iy0][ix - 1] - F[iy0][ix + 1])
  333. - (Real)4 * (F[iy1][ix - 1] - F[iy1][ix + 1])
  334. + (F[iy2][ix - 1] - F[iy2][ix + 1]));
  335. }
  336. // y-edges
  337. for (iy = 1; iy < yBoundM1; ++iy)
  338. {
  339. FXY[iy][0] = (Real)0.25 * invDXDY * (
  340. (Real)3 * (F[iy - 1][0] - F[iy + 1][0])
  341. - (Real)4 * (F[iy - 1][1] - F[iy + 1][1])
  342. + (F[iy - 1][2] - F[iy + 1][2]));
  343. FXY[iy][xBoundM1] = (Real)0.25 * invDXDY * (
  344. (Real)3 * (F[iy - 1][ix0] - F[iy + 1][ix0])
  345. - (Real)4 * (F[iy - 1][ix1] - F[iy + 1][ix1])
  346. + (F[iy - 1][ix2] - F[iy + 1][ix2]));
  347. }
  348. // interior
  349. for (iy = 1; iy < yBoundM1; ++iy)
  350. {
  351. for (ix = 1; ix < xBoundM1; ++ix)
  352. {
  353. FXY[iy][ix] = (Real)0.25 * invDXDY * (F[iy - 1][ix - 1] -
  354. F[iy - 1][ix + 1] - F[iy + 1][ix - 1] + F[iy + 1][ix + 1]);
  355. }
  356. }
  357. }
  358. void GetPolynomials(Array2<Real> const& F, Array2<Real> const& FX,
  359. Array2<Real> const& FY, Array2<Real> const& FXY)
  360. {
  361. int xBoundM1 = mXBound - 1;
  362. int yBoundM1 = mYBound - 1;
  363. for (int iy = 0; iy < yBoundM1; ++iy)
  364. {
  365. for (int ix = 0; ix < xBoundM1; ++ix)
  366. {
  367. // Note the 'transposing' of the 2x2 blocks (to match
  368. // notation used in the polynomial definition).
  369. Real G[2][2] =
  370. {
  371. { F[iy][ix], F[iy + 1][ix] },
  372. { F[iy][ix + 1], F[iy + 1][ix + 1] }
  373. };
  374. Real GX[2][2] =
  375. {
  376. { FX[iy][ix], FX[iy + 1][ix] },
  377. { FX[iy][ix + 1], FX[iy + 1][ix + 1] }
  378. };
  379. Real GY[2][2] =
  380. {
  381. { FY[iy][ix], FY[iy + 1][ix] },
  382. { FY[iy][ix + 1], FY[iy + 1][ix + 1] }
  383. };
  384. Real GXY[2][2] =
  385. {
  386. { FXY[iy][ix], FXY[iy + 1][ix] },
  387. { FXY[iy][ix + 1], FXY[iy + 1][ix + 1] }
  388. };
  389. Construct(mPoly[iy][ix], G, GX, GY, GXY);
  390. }
  391. }
  392. }
  393. Real ComputeDerivative(Real const* slope) const
  394. {
  395. if (slope[1] != slope[2])
  396. {
  397. if (slope[0] != slope[1])
  398. {
  399. if (slope[2] != slope[3])
  400. {
  401. Real ad0 = std::fabs(slope[3] - slope[2]);
  402. Real ad1 = std::fabs(slope[0] - slope[1]);
  403. return (ad0 * slope[1] + ad1 * slope[2]) / (ad0 + ad1);
  404. }
  405. else
  406. {
  407. return slope[2];
  408. }
  409. }
  410. else
  411. {
  412. if (slope[2] != slope[3])
  413. {
  414. return slope[1];
  415. }
  416. else
  417. {
  418. return (Real)0.5 * (slope[1] + slope[2]);
  419. }
  420. }
  421. }
  422. else
  423. {
  424. return slope[1];
  425. }
  426. }
  427. void Construct(Polynomial& poly, Real const F[2][2], Real const FX[2][2],
  428. Real const FY[2][2], Real const FXY[2][2])
  429. {
  430. Real dx = mXSpacing;
  431. Real dy = mYSpacing;
  432. Real invDX = (Real)1 / dx, invDX2 = invDX * invDX;
  433. Real invDY = (Real)1 / dy, invDY2 = invDY * invDY;
  434. Real b0, b1, b2, b3;
  435. poly.A(0, 0) = F[0][0];
  436. poly.A(1, 0) = FX[0][0];
  437. poly.A(0, 1) = FY[0][0];
  438. poly.A(1, 1) = FXY[0][0];
  439. b0 = (F[1][0] - poly(0, 0, dx, (Real)0)) * invDX2;
  440. b1 = (FX[1][0] - poly(1, 0, dx, (Real)0)) * invDX;
  441. poly.A(2, 0) = (Real)3 * b0 - b1;
  442. poly.A(3, 0) = ((Real)-2 * b0 + b1) * invDX;
  443. b0 = (F[0][1] - poly(0, 0, (Real)0, dy)) * invDY2;
  444. b1 = (FY[0][1] - poly(0, 1, (Real)0, dy)) * invDY;
  445. poly.A(0, 2) = (Real)3 * b0 - b1;
  446. poly.A(0, 3) = ((Real)-2 * b0 + b1) * invDY;
  447. b0 = (FY[1][0] - poly(0, 1, dx, (Real)0)) * invDX2;
  448. b1 = (FXY[1][0] - poly(1, 1, dx, (Real)0)) * invDX;
  449. poly.A(2, 1) = (Real)3 * b0 - b1;
  450. poly.A(3, 1) = ((Real)-2 * b0 + b1) * invDX;
  451. b0 = (FX[0][1] - poly(1, 0, (Real)0, dy)) * invDY2;
  452. b1 = (FXY[0][1] - poly(1, 1, (Real)0, dy)) * invDY;
  453. poly.A(1, 2) = (Real)3 * b0 - b1;
  454. poly.A(1, 3) = ((Real)-2 * b0 + b1) * invDY;
  455. b0 = (F[1][1] - poly(0, 0, dx, dy)) * invDX2 * invDY2;
  456. b1 = (FX[1][1] - poly(1, 0, dx, dy)) * invDX * invDY2;
  457. b2 = (FY[1][1] - poly(0, 1, dx, dy)) * invDX2 * invDY;
  458. b3 = (FXY[1][1] - poly(1, 1, dx, dy)) * invDX * invDY;
  459. poly.A(2, 2) = (Real)9 * b0 - (Real)3 * b1 - (Real)3 * b2 + b3;
  460. poly.A(3, 2) = ((Real)-6 * b0 + (Real)3 * b1 + (Real)2 * b2 - b3) * invDX;
  461. poly.A(2, 3) = ((Real)-6 * b0 + (Real)2 * b1 + (Real)3 * b2 - b3) * invDY;
  462. poly.A(3, 3) = ((Real)4 * b0 - (Real)2 * b1 - (Real)2 * b2 + b3) * invDX * invDY;
  463. }
  464. // Support for evaluation.
  465. void XLookup(Real x, int& xIndex, Real& dx) const
  466. {
  467. for (xIndex = 0; xIndex + 1 < mXBound; ++xIndex)
  468. {
  469. if (x < mXMin + mXSpacing * (xIndex + 1))
  470. {
  471. dx = x - (mXMin + mXSpacing * xIndex);
  472. return;
  473. }
  474. }
  475. --xIndex;
  476. dx = x - (mXMin + mXSpacing * xIndex);
  477. }
  478. void YLookup(Real y, int& yIndex, Real& dy) const
  479. {
  480. for (yIndex = 0; yIndex + 1 < mYBound; ++yIndex)
  481. {
  482. if (y < mYMin + mYSpacing * (yIndex + 1))
  483. {
  484. dy = y - (mYMin + mYSpacing * yIndex);
  485. return;
  486. }
  487. }
  488. yIndex--;
  489. dy = y - (mYMin + mYSpacing * yIndex);
  490. }
  491. int mXBound, mYBound, mQuantity;
  492. Real mXMin, mXMax, mXSpacing;
  493. Real mYMin, mYMax, mYSpacing;
  494. Real const* mF;
  495. Array2<Polynomial> mPoly;
  496. };
  497. }