BSplineSurfaceFit.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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/BandedMatrix.h>
  9. #include <Mathematics/BasisFunction.h>
  10. #include <Mathematics/Vector3.h>
  11. // The algorithm implemented here is based on the document
  12. // https://www.geometrictools.com/Documentation/BSplineSurfaceLeastSquaresFit.pdf
  13. namespace WwiseGTE
  14. {
  15. template <typename Real>
  16. class BSplineSurfaceFit
  17. {
  18. public:
  19. // Construction. The preconditions for calling the constructor are
  20. // 1 <= degree0 && degree0 + 1 < numControls0 <= numSamples0
  21. // 1 <= degree1 && degree1 + 1 < numControls1 <= numSamples1
  22. // The sample data must be in row-major order. The control data is
  23. // also stored in row-major order.
  24. BSplineSurfaceFit(int degree0, int numControls0, int numSamples0,
  25. int degree1, int numControls1, int numSamples1, Vector3<Real> const* sampleData)
  26. :
  27. mSampleData(sampleData),
  28. mControlData(numControls0 * numControls1)
  29. {
  30. LogAssert(1 <= degree0 && degree0 + 1 < numControls0, "Invalid degree.");
  31. LogAssert(numControls0 <= numSamples0, "Invalid number of controls.");
  32. LogAssert(1 <= degree1 && degree1 + 1 < numControls1, "Invalid degree.");
  33. LogAssert(numControls1 <= numSamples1, "Invalid number of controls.");
  34. LogAssert(sampleData, "Invalid sample data.");
  35. mDegree[0] = degree0;
  36. mNumSamples[0] = numSamples0;
  37. mNumControls[0] = numControls0;
  38. mDegree[1] = degree1;
  39. mNumSamples[1] = numSamples1;
  40. mNumControls[1] = numControls1;
  41. BasisFunctionInput<Real> input;
  42. Real tMultiplier[2];
  43. int dim;
  44. for (dim = 0; dim < 2; ++dim)
  45. {
  46. input.numControls = mNumControls[dim];
  47. input.degree = mDegree[dim];
  48. input.uniform = true;
  49. input.periodic = false;
  50. input.numUniqueKnots = mNumControls[dim] - mDegree[dim] + 1;
  51. input.uniqueKnots.resize(input.numUniqueKnots);
  52. input.uniqueKnots[0].t = (Real)0;
  53. input.uniqueKnots[0].multiplicity = mDegree[dim] + 1;
  54. int last = input.numUniqueKnots - 1;
  55. Real factor = (Real)1 / (Real)last;
  56. for (int i = 1; i < last; ++i)
  57. {
  58. input.uniqueKnots[i].t = factor * (Real)i;
  59. input.uniqueKnots[i].multiplicity = 1;
  60. }
  61. input.uniqueKnots[last].t = (Real)1;
  62. input.uniqueKnots[last].multiplicity = mDegree[dim] + 1;
  63. mBasis[dim].Create(input);
  64. tMultiplier[dim] = ((Real)1) / (Real)(mNumSamples[dim] - 1);
  65. }
  66. // Fit the data points with a B-spline surface using a
  67. // least-squares error metric. The problem is of the form
  68. // A0^T*A0*Q*A1^T*A1 = A0^T*P*A1, where A0^T*A0 and A1^T*A1 are
  69. // banded matrices, P contains the sample data, and Q is the
  70. // unknown matrix of control points.
  71. Real t;
  72. int i0, i1, i2, imin, imax;
  73. // Construct the matrices A0^T*A0 and A1^T*A1.
  74. BandedMatrix<Real> ATAMat[2] =
  75. {
  76. BandedMatrix<Real>(mNumControls[0], mDegree[0] + 1, mDegree[0] + 1),
  77. BandedMatrix<Real>(mNumControls[1], mDegree[1] + 1, mDegree[1] + 1)
  78. };
  79. for (dim = 0; dim < 2; ++dim)
  80. {
  81. for (i0 = 0; i0 < mNumControls[dim]; ++i0)
  82. {
  83. for (i1 = 0; i1 < i0; ++i1)
  84. {
  85. ATAMat[dim](i0, i1) = ATAMat[dim](i1, i0);
  86. }
  87. int i1Max = i0 + mDegree[dim];
  88. if (i1Max >= mNumControls[dim])
  89. {
  90. i1Max = mNumControls[dim] - 1;
  91. }
  92. for (i1 = i0; i1 <= i1Max; ++i1)
  93. {
  94. Real value = (Real)0;
  95. for (i2 = 0; i2 < mNumSamples[dim]; ++i2)
  96. {
  97. t = tMultiplier[dim] * (Real)i2;
  98. mBasis[dim].Evaluate(t, 0, imin, imax);
  99. if (imin <= i0 && i0 <= imax && imin <= i1 && i1 <= imax)
  100. {
  101. Real b0 = mBasis[dim].GetValue(0, i0);
  102. Real b1 = mBasis[dim].GetValue(0, i1);
  103. value += b0 * b1;
  104. }
  105. }
  106. ATAMat[dim](i0, i1) = value;
  107. }
  108. }
  109. }
  110. // Construct the matrices A0^T and A1^T. A[d]^T has
  111. // mNumControls[d] rows and mNumSamples[d] columns.
  112. Array2<Real> ATMat[2];
  113. for (dim = 0; dim < 2; dim++)
  114. {
  115. ATMat[dim] = Array2<Real>(mNumSamples[dim], mNumControls[dim]);
  116. size_t numBytes = mNumControls[dim] * mNumSamples[dim] * sizeof(Real);
  117. std::memset(ATMat[dim][0], 0, numBytes);
  118. for (i0 = 0; i0 < mNumControls[dim]; ++i0)
  119. {
  120. for (i1 = 0; i1 < mNumSamples[dim]; ++i1)
  121. {
  122. t = tMultiplier[dim] * (Real)i1;
  123. mBasis[dim].Evaluate(t, 0, imin, imax);
  124. if (imin <= i0 && i0 <= imax)
  125. {
  126. ATMat[dim][i0][i1] = mBasis[dim].GetValue(0, i0);
  127. }
  128. }
  129. }
  130. }
  131. // Compute X0 = (A0^T*A0)^{-1}*A0^T and X1 = (A1^T*A1)^{-1}*A1^T
  132. // by solving the linear systems A0^T*A0*X0 = A0^T and
  133. // A1^T*A1*X1 = A1^T.
  134. for (dim = 0; dim < 2; ++dim)
  135. {
  136. bool solved = ATAMat[dim].template SolveSystem<true>(ATMat[dim][0], mNumSamples[dim]);
  137. LogAssert(solved, "Failed to solve linear system in BSplineSurfaceFit constructor.");
  138. }
  139. // The control points for the fitted surface are stored in the matrix
  140. // Q = X0*P*X1^T, where P is the matrix of sample data.
  141. for (i1 = 0; i1 < mNumControls[1]; ++i1)
  142. {
  143. for (i0 = 0; i0 < mNumControls[0]; ++i0)
  144. {
  145. Vector3<Real> sum = Vector3<Real>::Zero();
  146. for (int j1 = 0; j1 < mNumSamples[1]; ++j1)
  147. {
  148. Real x1Value = ATMat[1][i1][j1];
  149. for (int j0 = 0; j0 < mNumSamples[0]; ++j0)
  150. {
  151. Real x0Value = ATMat[0][i0][j0];
  152. Vector3<Real> sample =
  153. mSampleData[j0 + mNumSamples[0] * j1];
  154. sum += (x0Value * x1Value) * sample;
  155. }
  156. }
  157. mControlData[i0 + mNumControls[0] * i1] = sum;
  158. }
  159. }
  160. }
  161. // Access to input sample information.
  162. inline int GetNumSamples(int dimension) const
  163. {
  164. return mNumSamples[dimension];
  165. }
  166. inline Vector3<Real> const* GetSampleData() const
  167. {
  168. return mSampleData;
  169. }
  170. // Access to output control point and surface information.
  171. inline int GetDegree(int dimension) const
  172. {
  173. return mDegree[dimension];
  174. }
  175. inline int GetNumControls(int dimension) const
  176. {
  177. return mNumControls[dimension];
  178. }
  179. inline Vector3<Real> const* GetControlData() const
  180. {
  181. return &mControlData[0];
  182. }
  183. inline BasisFunction<Real> const& GetBasis(int dimension) const
  184. {
  185. return mBasis[dimension];
  186. }
  187. // Evaluation of the B-spline surface. It is defined for
  188. // 0 <= u <= 1 and 0 <= v <= 1. If a parameter value is outside
  189. // [0,1], it is clamped to [0,1].
  190. Vector3<Real> GetPosition(Real u, Real v) const
  191. {
  192. int iumin, iumax, ivmin, ivmax;
  193. mBasis[0].Evaluate(u, 0, iumin, iumax);
  194. mBasis[1].Evaluate(v, 0, ivmin, ivmax);
  195. Vector3<Real> position = Vector3<Real>::Zero();
  196. for (int iv = ivmin; iv <= ivmax; ++iv)
  197. {
  198. Real value1 = mBasis[1].GetValue(0, iv);
  199. for (int iu = iumin; iu <= iumax; ++iu)
  200. {
  201. Real value0 = mBasis[0].GetValue(0, iu);
  202. Vector3<Real> control = mControlData[iu + mNumControls[0] * iv];
  203. position += (value0 * value1) * control;
  204. }
  205. }
  206. return position;
  207. }
  208. private:
  209. // Input sample information.
  210. int mNumSamples[2];
  211. Vector3<Real> const* mSampleData;
  212. // The fitted B-spline surface, open and with uniform knots.
  213. int mDegree[2];
  214. int mNumControls[2];
  215. std::vector<Vector3<Real>> mControlData;
  216. BasisFunction<Real> mBasis[2];
  217. };
  218. }