GteBSplineCurveFit.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #pragma once
9 
12 
13 namespace gte
14 {
15 
16 template <typename Real>
18 {
19 public:
20  // Construction. The preconditions for calling the constructor are
21  // 1 <= degree && degree < numControls <= numSamples
22  // The samples points are contiguous blocks of 'dimension' real values
23  // stored in sampleData.
24  BSplineCurveFit(int dimension, int numSamples, Real const* sampleData,
25  int degree, int numControls);
26 
27  // Access to input sample information.
28  inline int GetDimension() const;
29  inline int GetNumSamples() const;
30  inline Real const* GetSampleData() const;
31 
32  // Access to output control point and curve information.
33  inline int GetDegree() const;
34  inline int GetNumControls() const;
35  inline Real const* GetControlData() const;
36  inline BasisFunction<Real> const& GetBasis() const;
37 
38  // Evaluation of the B-spline curve. It is defined for 0 <= t <= 1. If a
39  // t-value is outside [0,1], an open spline clamps it to [0,1]. The
40  // caller must ensure that position[] has at least 'dimension' elements.
41  void GetPosition(Real t, Real* position) const;
42 
43 private:
44  // Input sample information.
47  Real const* mSampleData;
48 
49  // The fitted B-spline curve, open and with uniform knots.
50  int mDegree;
52  std::vector<Real> mControlData;
54 };
55 
56 
57 template <typename Real>
58 BSplineCurveFit<Real>::BSplineCurveFit(int dimension, int numSamples,
59  Real const* sampleData, int degree, int numControls)
60  :
61  mDimension(dimension),
62  mNumSamples(numSamples),
63  mSampleData(sampleData),
64  mDegree(degree),
65  mNumControls(numControls),
66  mControlData(dimension * numControls)
67 {
68  LogAssert(dimension >= 1, "Invalid dimension.");
69  LogAssert(1 <= degree && degree < numControls, "Invalid degree.");
70  LogAssert(sampleData, "Invalid sample data.");
71  LogAssert(numControls <= numSamples, "Invalid number of controls.");
72 
74  input.numControls = numControls;
75  input.degree = degree;
76  input.uniform = true;
77  input.periodic = false;
78  input.numUniqueKnots = numControls - degree + 1;
79  input.uniqueKnots.resize(input.numUniqueKnots);
80  input.uniqueKnots[0].t = (Real)0;
81  input.uniqueKnots[0].multiplicity = degree + 1;
82  int last = input.numUniqueKnots - 1;
83  Real factor = ((Real)1) / (Real)last;
84  for (int i = 1; i < last; ++i)
85  {
86  input.uniqueKnots[i].t = factor * (Real)i;
87  input.uniqueKnots[i].multiplicity = 1;
88  }
89  input.uniqueKnots[last].t = (Real)1;
90  input.uniqueKnots[last].multiplicity = degree + 1;
91  mBasis.Create(input);
92 
93  // Fit the data points with a B-spline curve using a least-squares error
94  // metric. The problem is of the form A^T*A*Q = A^T*P, where A^T*A is a
95  // banded matrix, P contains the sample data, and Q is the unknown vector
96  // of control points.
97  Real tMultiplier = ((Real)1) / (Real)(mNumSamples - 1);
98  Real t;
99  int i0, i1, i2, imin, imax, j;
100 
101  // Construct the matrix A^T*A.
102  int degp1 = mDegree + 1;
103  int numBands = (mNumControls > degp1 ? degp1 : mDegree);
104  BandedMatrix<Real> ATAMat(mNumControls, numBands, numBands);
105  for (i0 = 0; i0 < mNumControls; ++i0)
106  {
107  for (i1 = 0; i1 < i0; ++i1)
108  {
109  ATAMat(i0, i1) = ATAMat(i1, i0);
110  }
111 
112  int i1Max = i0 + mDegree;
113  if (i1Max >= mNumControls)
114  {
115  i1Max = mNumControls - 1;
116  }
117 
118  for (i1 = i0; i1 <= i1Max; ++i1)
119  {
120  Real value = (Real)0;
121  for (i2 = 0; i2 < mNumSamples; ++i2)
122  {
123  t = tMultiplier * (Real)i2;
124  mBasis.Evaluate(t, 0, imin, imax);
125  if (imin <= i0 && i0 <= imax && imin <= i1 && i1 <= imax)
126  {
127  Real b0 = mBasis.GetValue(0, i0);
128  Real b1 = mBasis.GetValue(0, i1);
129  value += b0 * b1;
130  }
131  }
132  ATAMat(i0, i1) = value;
133  }
134  }
135 
136  // Construct the matrix A^T.
137  Array2<Real> ATMat(mNumSamples, mNumControls);
138  memset(ATMat[0], 0, mNumControls * mNumSamples * sizeof(Real));
139  for (i0 = 0; i0 < mNumControls; ++i0)
140  {
141  for (i1 = 0; i1 < mNumSamples; ++i1)
142  {
143  t = tMultiplier * (Real)i1;
144  mBasis.Evaluate(t, 0, imin, imax);
145  if (imin <= i0 && i0 <= imax)
146  {
147  ATMat[i0][i1] = mBasis.GetValue(0, i0);
148  }
149  }
150  }
151 
152  // Compute X0 = (A^T*A)^{-1}*A^T by solving the linear system
153  // A^T*A*X = A^T.
154  bool solved = ATAMat.template SolveSystem<true>(ATMat[0], mNumSamples);
155  LogAssert(solved, "Failed to solve linear system.");
156  (void)solved;
157 
158  // The control points for the fitted curve are stored in the vector
159  // Q = X0*P, where P is the vector of sample data.
160  std::fill(mControlData.begin(), mControlData.end(), (Real)0);
161  for (i0 = 0; i0 < mNumControls; ++i0)
162  {
163  Real* Q = &mControlData[i0 * mDimension];
164  for (i1 = 0; i1 < mNumSamples; ++i1)
165  {
166  Real const* P = mSampleData + i1 * mDimension;
167  Real xValue = ATMat[i0][i1];
168  for (j = 0; j < mDimension; ++j)
169  {
170  Q[j] += xValue*P[j];
171  }
172  }
173  }
174 
175  // Set the first and last output control points to match the first and
176  // last input samples. This supports the application of fitting keyframe
177  // data with B-spline curves. The user expects that the curve passes
178  // through the first and last positions in order to support matching two
179  // consecutive keyframe sequences.
180  Real* cEnd0 = &mControlData[0];
181  Real const* sEnd0 = mSampleData;
182  Real* cEnd1 = &mControlData[mDimension * (mNumControls - 1)];
183  Real const* sEnd1 = &mSampleData[mDimension * (mNumSamples - 1)];
184  for (j = 0; j < mDimension; ++j)
185  {
186  *cEnd0++ = *sEnd0++;
187  *cEnd1++ = *sEnd1++;
188  }
189 }
190 
191 template <typename Real> inline
193 {
194  return mDimension;
195 }
196 
197 template <typename Real> inline
199 {
200  return mNumSamples;
201 }
202 
203 template <typename Real> inline
205 {
206  return mSampleData;
207 }
208 
209 template <typename Real> inline
211 {
212  return mDegree;
213 }
214 
215 template <typename Real> inline
217 {
218  return mNumControls;
219 }
220 
221 template <typename Real> inline
223 {
224  return &mControlData[0];
225 }
226 
227 template <typename Real> inline
229 {
230  return mBasis;
231 }
232 
233 template <typename Real>
234 void BSplineCurveFit<Real>::GetPosition(Real t, Real* position) const
235 {
236  int imin, imax;
237  mBasis.Evaluate(t, 0, imin, imax);
238 
239  Real const* source = &mControlData[mDimension * imin];
240  Real basisValue = mBasis.GetValue(0, imin);
241  for (int j = 0; j < mDimension; ++j)
242  {
243  position[j] = basisValue * (*source++);
244  }
245 
246  for (int i = imin + 1; i <= imax; ++i)
247  {
248  basisValue = mBasis.GetValue(0, i);
249  for (int j = 0; j < mDimension; ++j)
250  {
251  position[j] += basisValue * (*source++);
252  }
253  }
254 }
255 
256 
257 }
#define LogAssert(condition, message)
Definition: GteLogger.h:86
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
BSplineCurveFit(int dimension, int numSamples, Real const *sampleData, int degree, int numControls)
GLsizei const GLfloat * value
Definition: glcorearb.h:819
BasisFunction< Real > const & GetBasis() const
void GetPosition(Real t, Real *position) const
Real const * GetSampleData() const
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:798
GLdouble GLdouble t
Definition: glext.h:239
GLenum GLenum GLenum input
Definition: glext.h:9913
Real const * GetControlData() const
std::vector< Real > mControlData
std::vector< UniqueKnot< Real > > uniqueKnots
BasisFunction< Real > mBasis


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 03:59:59