GteBasisFunction.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.1 (2016/08/25)
7 
8 #pragma once
9 
10 #include <LowLevel/GteArray2.h>
11 #include <LowLevel/GteLogger.h>
12 #include <cmath>
13 #include <cstring>
14 
15 namespace gte
16 {
17 
18 template <typename Real>
19 struct UniqueKnot
20 {
21  Real t;
23 };
24 
25 template <typename Real>
27 {
28  // The members are uninitialized.
30 
31  // Construct an open uniform curve with t in [0,1].
32  BasisFunctionInput(int inNumControls, int inDegree);
33 
35  int degree;
36  bool uniform;
37  bool periodic;
39  std::vector<UniqueKnot<Real>> uniqueKnots;
40 };
41 
42 template <typename Real>
44 {
45 public:
46  // Let n be the number of control points. Let d be the degree, where
47  // 1 <= d <= n-1. The number of knots is k = n + d + 1. The knots are
48  // t[i] for 0 <= i < k and must be nondecreasing, t[i] <= t[i+1], but a
49  // knot value can be repeated. Let s be the number of distinct knots.
50  // Let the distinct knots be u[j] for 0 <= j < s, so u[j] < u[j+1] for
51  // all j. The set of u[j] is called a 'breakpoint sequence'. Let
52  // m[j] >= 1 be the multiplicity; that is, if t[i] is the first occurrence
53  // of u[j], then t[i+r] = t[i] for 1 <= r < m[j]. The multiplicities have
54  // the constraints m[0] <= d+1, m[s-1] <= d+1, and m[j] <= d for
55  // 1 <= j <= s-2. Also, k = sum_{j=0}^{s-1} m[j], which says the
56  // multiplicities account for all k knots.
57  //
58  // Given a knot vector (t[0],...,t[n+d]), the domain of the corresponding
59  // B-spline curve is the interval [t[d],t[n]].
60  //
61  // The corresponding B-spline or NURBS curve is characterized as follows.
62  // See "Geometric Modeling with Splines: An Introduction" by Elaine Cohen,
63  // Richard F. Riesenfeld, and Gershon Elber, AK Peters, 2001, Natick MA.
64  // The curve is 'open' when m[0] = m[s-1] = d+1; otherwise, it is
65  // 'floating'. An open curve is uniform when the knots t[d] through
66  // t[n] are equally spaced; that is, t[i+1] - t[i] are a common value
67  // for d <= i <= n-1. By implication, s = n-d-1 and m[j] = 1 for
68  // 1 <= j <= s-2. An open curve that does not satisfy these conditions
69  // is said to be nonuniform. The aforementioned book does not define
70  // subclasses of 'floating' curves, but it is convenient to have a finer
71  // classification. Let us say that a floating curve is uniform when
72  // m[j] = 1 for 0 <= j <= s-1 and t[i+1] - t[i] are a common value for
73  // 0 <= i <= k-2; otherwise, the floating curve is nonuniform.
74  //
75  // A special case of a floating curve is a periodic curve. The intent
76  // is that the curve is closed, so the first and last control points
77  // should be the same, which ensures C^{0} continuity. Higher-order
78  // continuity is obtained by repeating more control points. If the
79  // control points are P[0] through P[n-1], append the points P[0]
80  // through P[d-1] to ensure C^{d-1} continuity. Additionally the knots
81  // must be chosen properly. You may choose t[d] through t[n] as you
82  // wish. The other knots are defined by
83  // t[i] - t[i-1] = t[n-d+i] - t[n-d+i-1]
84  // t[n+i] - t[n+i-1] = t[d+i] - t[d+i-1]
85  // for 1 <= i <= d.
86 
87  // Construction and destruction. The determination that the curve is
88  // open or floating is based on the multiplicities. The 'uniform' input
89  // is used to avoid misclassifications due to floating-point rounding
90  // errors. Specifically, the breakpoints might be equally spaced
91  // (uniform) as real numbers, but the floating-point representations can
92  // have rounding errors that cause the knot differences not to be exactly
93  // the same constant. A periodic curve can have uniform or nonuniform
94  // knots. This object makes copies of the input arrays.
95 
96  ~BasisFunction();
97  BasisFunction();
99 
100  // No copying is allowed.
101  BasisFunction(BasisFunction const&) = delete;
102  BasisFunction& operator=(BasisFunction const&) = delete;
103 
104  // Support for explicit creation in classes that have std::array
105  // members involving BasisFunction. This is a call-once function.
106  void Create(BasisFunctionInput<Real> const& input);
107 
108  // The inputs have complicated validation tests. You should create a
109  // BasisFunction object as shown:
110  // BasisFunction<Real> function(parameters);
111  // if (!function) { <constructor failed, respond accordingly>; }
112  inline operator bool() const;
113 
114  // Member access.
115  inline int GetNumControls() const;
116  inline int GetDegree() const;
117  inline int GetNumUniqueKnots() const;
118  inline int GetNumKnots() const;
119  inline Real GetMinDomain() const;
120  inline Real GetMaxDomain() const;
121  inline bool IsOpen() const;
122  inline bool IsUniform() const;
123  inline bool IsPeriodic() const;
124  inline UniqueKnot<Real> const* GetUniqueKnots() const;
125  inline Real const* GetKnots() const;
126 
127  // Evaluation of the basis function and its derivatives through order 3.
128  // For the function value only, pass order 0. For the function and first
129  // derivative, pass order 1, and so on.
130  void Evaluate(Real t, unsigned int order, int& minIndex, int& maxIndex)
131  const;
132 
133  // Access the results of the call to Evaluate(...). The index i must
134  // satisfy minIndex <= i <= maxIndex. If it is not, the function returns
135  // zero. The separation of evaluation and access is based on local
136  // control of the basis function; that is, only the accessible values are
137  // (potentially) not zero.
138  Real GetValue(unsigned int order, int i) const;
139 
140 private:
141  // Determine the index i for which knot[i] <= t < knot[i+1]. The t-value
142  // is modified (wrapped for periodic splines, clamped for nonperiodic
143  // splines).
144  int GetIndex(Real& t) const;
145 
146  // Constructor inputs and values derived from them.
148  int mDegree;
149  Real mTMin, mTMax, mTLength;
150  bool mOpen;
151  bool mUniform;
152  bool mPeriodic;
154  std::vector<UniqueKnot<Real>> mUniqueKnots;
155  std::vector<Real> mKnots;
156 
157  // Lookup information for the GetIndex() function. The first element of
158  // the pair is a unique knot value. The second element is the index in
159  // mKnots[] for the last occurrence of that knot value. NOTE: This is
160  // a heavily used function during Evaluate calls. This member was
161  // initially set to be std::vector<*>, but in debug mode the range-based
162  // for-loop in GetIndex was taking an extremely long time due to checked
163  // calls involving iterators. We modified this to a regular array to
164  // ensure that debug performance is better.
165  std::vector<std::pair<Real, int>> mKeys;
166 
167  // Storage for the basis functions and their first three derivatives.
168  mutable Array2<Real> mValue[4]; // mValue[i] is array[d+1][n+d]
169 };
170 
171 
172 template <typename Real>
174 {
175 }
176 
177 template <typename Real>
178 BasisFunctionInput<Real>::BasisFunctionInput(int inNumControls, int inDegree)
179  :
180  numControls(inNumControls),
181  degree(inDegree),
182  uniform(true),
183  periodic(false),
184  numUniqueKnots(numControls - degree + 1),
185  uniqueKnots(numUniqueKnots)
186 {
187  uniqueKnots.front().t = (Real)0;
188  uniqueKnots.front().multiplicity = degree + 1;
189  for (int i = 1; i <= numUniqueKnots - 2; ++i)
190  {
191  uniqueKnots[i].t = i / (numUniqueKnots - (Real)1);
192  uniqueKnots[i].multiplicity = 1;
193  }
194  uniqueKnots.back().t = (Real)1;
195  uniqueKnots.back().multiplicity = degree + 1;
196 }
197 
198 template <typename Real>
200 {
201 }
202 
203 template <typename Real>
205  :
206  mNumControls(0),
207  mDegree(0),
208  mTMin((Real)0),
209  mTMax((Real)0),
210  mTLength((Real)0),
211  mOpen(false),
212  mUniform(false),
213  mPeriodic(false),
214  mConstructed(false)
215 {
216 }
217 
218 template <typename Real>
220  :
221  mConstructed(false)
222 {
223  Create(input);
224 }
225 
226 
227 template <typename Real>
229 {
230  if (mConstructed)
231  {
232  LogError("Object already created.");
233  return;
234  }
235 
236  mNumControls = (input.periodic ? input.numControls + input.degree : input.numControls);
237  mDegree = input.degree;
238  mTMin = (Real)0;
239  mTMax = (Real)0;
240  mTLength = (Real)0;
241  mOpen = false;
242  mUniform = input.uniform;
243  mPeriodic = input.periodic;
244  for (int i = 0; i < 4; ++i)
245  {
246  mValue[i] = Array2<Real>();
247  }
248 
249  if (input.numControls < 2)
250  {
251  LogError("Invalid number of control points.");
252  return;
253  }
254 
255  if (input.degree < 1 || input.degree >= input.numControls)
256  {
257  LogError("Invalid degree.");
258  return;
259  }
260 
261  if (input.numUniqueKnots < 2)
262  {
263  LogError("Invalid number of unique knots.");
264  return;
265  }
266 
267  mUniqueKnots.resize(input.numUniqueKnots);
268  std::copy(input.uniqueKnots.begin(), input.uniqueKnots.begin() + input.numUniqueKnots,
269  mUniqueKnots.begin());
270 
271  Real u = mUniqueKnots.front().t;
272  for (int i = 1; i < input.numUniqueKnots - 1; ++i)
273  {
274  Real uNext = mUniqueKnots[i].t;
275  if (u >= uNext)
276  {
277  LogError("Unique knots are not strictly increasing.");
278  return;
279  }
280  u = uNext;
281  }
282 
283  int mult0 = mUniqueKnots.front().multiplicity;
284  if (mult0 < 1 || mult0 > mDegree + 1)
285  {
286  LogError("Invalid first multiplicity.");
287  return;
288  }
289 
290  int mult1 = mUniqueKnots.back().multiplicity;
291  if (mult1 < 1 || mult1 > mDegree + 1)
292  {
293  LogError("Invalid last multiplicity.");
294  return;
295  }
296 
297  for (int i = 1; i <= input.numUniqueKnots - 2; ++i)
298  {
299  int mult = mUniqueKnots[i].multiplicity;
300  if (mult < 1 || mult > mDegree)
301  {
302  LogError("Invalid interior multiplicity.");
303  return;
304  }
305  }
306 
307  mOpen = (mult0 == mult1 && mult0 == mDegree + 1);
308 
309  mKnots.resize(mNumControls + mDegree + 1);
310  mKeys.resize(input.numUniqueKnots);
311  int sum = 0;
312  for (int i = 0, j = 0; i < input.numUniqueKnots; ++i)
313  {
314  Real tCommon = mUniqueKnots[i].t;
315  int mult = mUniqueKnots[i].multiplicity;
316  for (int k = 0; k < mult; ++k, ++j)
317  {
318  mKnots[j] = tCommon;
319  }
320 
321  mKeys[i].first = tCommon;
322  mKeys[i].second = sum - 1;
323  sum += mult;
324  }
325 
326  mTMin = mKnots[mDegree];
328  mTLength = mTMax - mTMin;
329 
330  size_t numRows = mDegree + 1;
331  size_t numCols = mNumControls + mDegree;
332  size_t numBytes = numRows * numCols * sizeof(Real);
333  for (int i = 0; i < 4; ++i)
334  {
335  mValue[i] = Array2<Real>(numCols, numRows);
336  memset(mValue[i][0], 0, numBytes);
337  }
338 
339  mConstructed = true;
340 }
341 
342 template <typename Real> inline
344 {
345  return mConstructed;
346 }
347 
348 template <typename Real> inline
350 {
351  return mNumControls;
352 }
353 
354 template <typename Real> inline
356 {
357  return mDegree;
358 }
359 
360 template <typename Real> inline
362 {
363  return static_cast<int>(mUniqueKnots.size());
364 }
365 
366 template <typename Real> inline
368 {
369  return static_cast<int>(mKnots.size());
370 }
371 
372 template <typename Real> inline
374 {
375  return mTMin;
376 }
377 
378 template <typename Real> inline
380 {
381  return mTMax;
382 }
383 
384 template <typename Real> inline
386 {
387  return mOpen;
388 }
389 
390 template <typename Real> inline
392 {
393  return mUniform;
394 }
395 
396 template <typename Real> inline
398 {
399  return mPeriodic;
400 }
401 
402 template <typename Real> inline
404 {
405  return &mUniqueKnots[0];
406 }
407 
408 template <typename Real> inline
409 Real const* BasisFunction<Real>::GetKnots() const
410 {
411  return &mKnots[0];
412 }
413 
414 template <typename Real>
415 void BasisFunction<Real>::Evaluate(Real t, unsigned int order, int& minIndex,
416  int& maxIndex) const
417 {
418  if (!mConstructed)
419  {
420  // Errors were already generated during construction. Return an index
421  // range that leads to zero-valued positions and derivatives.
422  minIndex = -1;
423  maxIndex = -1;
424  return;
425  }
426 
427  if (order > 3)
428  {
429  LogError("Only derivatives through order 3 are supported.");
430  minIndex = 0;
431  maxIndex = 0;
432  return;
433  }
434 
435  int i = GetIndex(t);
436  mValue[0][0][i] = (Real)1;
437 
438  if (order >= 1)
439  {
440  mValue[1][0][i] = (Real)0;
441  if (order >= 2)
442  {
443  mValue[2][0][i] = (Real)0;
444  if (order >= 3)
445  {
446  mValue[3][0][i] = (Real)0;
447  }
448  }
449  }
450 
451  Real n0 = t - mKnots[i], n1 = mKnots[i + 1] - t;
452  Real e0, e1, d0, d1, invD0, invD1;
453  int j;
454  for (j = 1; j <= mDegree; j++)
455  {
456  d0 = mKnots[i + j] - mKnots[i];
457  d1 = mKnots[i + 1] - mKnots[i - j + 1];
458  invD0 = (d0 > (Real)0 ? (Real)1 / d0 : (Real)0);
459  invD1 = (d1 > (Real)0 ? (Real)1 / d1 : (Real)0);
460 
461  e0 = n0*mValue[0][j - 1][i];
462  mValue[0][j][i] = e0*invD0;
463  e1 = n1*mValue[0][j - 1][i - j + 1];
464  mValue[0][j][i - j] = e1*invD1;
465 
466  if (order >= 1)
467  {
468  e0 = n0 * mValue[1][j - 1][i] + mValue[0][j - 1][i];
469  mValue[1][j][i] = e0 * invD0;
470  e1 = n1 * mValue[1][j - 1][i - j + 1] - mValue[0][j - 1][i - j + 1];
471  mValue[1][j][i - j] = e1 * invD1;
472 
473  if (order >= 2)
474  {
475  e0 = n0 * mValue[2][j - 1][i] + ((Real)2) * mValue[1][j - 1][i];
476  mValue[2][j][i] = e0 * invD0;
477  e1 = n1 * mValue[2][j - 1][i - j + 1] - ((Real)2) * mValue[1][j - 1][i - j + 1];
478  mValue[2][j][i - j] = e1 * invD1;
479 
480  if (order >= 3)
481  {
482  e0 = n0 * mValue[3][j - 1][i] + ((Real)3) * mValue[2][j - 1][i];
483  mValue[3][j][i] = e0 * invD0;
484  e1 = n1 * mValue[3][j - 1][i - j + 1] - ((Real)3) * mValue[2][j - 1][i - j + 1];
485  mValue[3][j][i - j] = e1 * invD1;
486  }
487  }
488  }
489  }
490 
491  for (j = 2; j <= mDegree; ++j)
492  {
493  for (int k = i - j + 1; k < i; ++k)
494  {
495  n0 = t - mKnots[k];
496  n1 = mKnots[k + j + 1] - t;
497  d0 = mKnots[k + j] - mKnots[k];
498  d1 = mKnots[k + j + 1] - mKnots[k + 1];
499  invD0 = (d0 >(Real)0 ? (Real)1 / d0 : (Real)0);
500  invD1 = (d1 > (Real)0 ? (Real)1 / d1 : (Real)0);
501 
502  e0 = n0*mValue[0][j - 1][k];
503  e1 = n1*mValue[0][j - 1][k + 1];
504  mValue[0][j][k] = e0 * invD0 + e1 * invD1;
505 
506  if (order >= 1)
507  {
508  e0 = n0 * mValue[1][j - 1][k] + mValue[0][j - 1][k];
509  e1 = n1 * mValue[1][j - 1][k + 1] - mValue[0][j - 1][k + 1];
510  mValue[1][j][k] = e0 * invD0 + e1 * invD1;
511 
512  if (order >= 2)
513  {
514  e0 = n0 * mValue[2][j - 1][k] + ((Real)2) * mValue[1][j - 1][k];
515  e1 = n1 * mValue[2][j - 1][k + 1] - ((Real)2) * mValue[1][j - 1][k + 1];
516  mValue[2][j][k] = e0 * invD0 + e1 * invD1;
517 
518  if (order >= 3)
519  {
520  e0 = n0 * mValue[3][j - 1][k] + ((Real)3) * mValue[2][j - 1][k];
521  e1 = n1 * mValue[3][j - 1][k + 1] - ((Real)3) * mValue[2][j - 1][k + 1];
522  mValue[3][j][k] = e0 * invD0 + e1 * invD1;
523  }
524  }
525  }
526  }
527  }
528 
529  minIndex = i - mDegree;
530  maxIndex = i;
531 }
532 
533 template <typename Real>
534 Real BasisFunction<Real>::GetValue(unsigned int order, int i) const
535 {
536  if (!mConstructed)
537  {
538  // Errors were already generated during construction. Return a value
539  // that leads to zero-valued positions and derivatives.
540  return (Real)0;
541  }
542 
543  if (order < 4)
544  {
545  if (0 <= i && i < mNumControls + mDegree)
546  {
547  return mValue[order][mDegree][i];
548  }
549  }
550 
551  LogError("Invalid input.");
552  return (Real)0;
553 }
554 
555 template <typename Real>
557 {
558  // Find the index i for which knot[i] <= t < knot[i+1].
559  if (mPeriodic)
560  {
561  // Wrap to [tmin,tmax].
562  Real r = fmod(t - mTMin, mTLength);
563  if (r < (Real)0)
564  {
565  r += mTLength;
566  }
567  t = mTMin + r;
568  }
569 
570  // Clamp to [tmin,tmax]. For the periodic case, this handles small
571  // numerical rounding errors near the domain endpoints.
572  if (t <= mTMin)
573  {
574  t = mTMin;
575  return mDegree;
576  }
577  if (t >= mTMax)
578  {
579  t = mTMax;
580  return mNumControls - 1;
581  }
582 
583  // At this point, tmin < t < tmax.
584  for (auto const& key : mKeys)
585  {
586  if (t < key.first)
587  {
588  return key.second;
589  }
590  }
591 
592  // We should not reach this code.
593  LogError("Unexpected condition.");
594  t = mTMin;
595  return mDegree;
596 }
597 
598 }
GLfixed GLfixed GLint GLint order
Definition: glext.h:4927
std::vector< std::pair< Real, int > > mKeys
Real GetMaxDomain() const
bool IsOpen() const
Real GetMinDomain() const
bool IsPeriodic() const
std::vector< UniqueKnot< Real > > mUniqueKnots
std::vector< Real > mKnots
int GetDegree() const
int GetNumUniqueKnots() const
Real const * GetKnots() const
Real GetValue(unsigned int order, int i) const
#define LogError(message)
Definition: GteLogger.h:92
void Create(BasisFunctionInput< Real > const &input)
GLdouble GLdouble t
Definition: glext.h:239
Array2< Real > mValue[4]
UniqueKnot< Real > const * GetUniqueKnots() const
void Evaluate(Real t, unsigned int order, int &minIndex, int &maxIndex) const
int GetNumControls() const
GLboolean r
Definition: glcorearb.h:1217
GLenum GLenum GLenum input
Definition: glext.h:9913
int GetNumKnots() const
std::vector< UniqueKnot< Real > > uniqueKnots
int GetIndex(Real &t) const
bool IsUniform() const


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