1 /** @file gsBSplineAlgorithms.h
2
3 @brief Implementation of common algorithms for B-splines
4
5 This file is part of the G+Smo library.
6
7 This Source Code Form is subject to the terms of the Mozilla Public
8 License, v. 2.0. If a copy of the MPL was not distributed with this
9 file, You can obtain one at http://mozilla.org/MPL/2.0/.
10
11 Author(s): A. Mantzaflaris
12 */
13
14 #pragma once
15
16
17 namespace gismo
18 {
19
20 /** @namespace gismo::bspline
21
22 @brief
23 This namespace contains implementation of B-spline related algorithms.
24
25 \ingroup Nurbs
26 */
27 namespace bspline
28 {
29 /// Input: an iterator pointing to the the biggest knot less than \a u,
30 /// evaluation point \a u, output the value of all basis functions
31 /// which are active at \a u.
32 /// Uses B-spline recursion for evaluation
33 template <class T, typename KnotIterator, typename Derived>
evalBasis(T u,KnotIterator knot,int deg,Eigen::MatrixBase<Derived> const & result)34 void evalBasis( T u,
35 KnotIterator knot,
36 int deg,
37 Eigen::MatrixBase<Derived> const & result )
38 {
39 STACK_ARRAY(T, left, deg + 1);
40 STACK_ARRAY(T, right, deg + 1);
41 evalBasis( u, knot, deg, left, right, result);
42 }
43
44 template <class T, typename KnotIterator, typename Derived>
evalBasis(T u,KnotIterator knot,int deg,T left[],T right[],Eigen::MatrixBase<Derived> const & result)45 void evalBasis( T u,
46 KnotIterator knot,
47 int deg,
48 T left[], T right[],
49 Eigen::MatrixBase<Derived> const & result )
50 {
51 Eigen::MatrixBase<Derived>& res = const_cast<Eigen::MatrixBase<Derived>&>(result);
52
53 res(0,0)= T(1.0); // 0-th degree function value
54
55 for(int j=1; j<= deg; j++) // For all degrees ( ndu column)
56 {
57 left[j] = u - *(knot+1-j);
58 right[j] = *(knot+j) - u;
59 T saved = T(0) ;
60 for(int r=0; r<j ; r++) // For all (except the last) basis functions of degree j ( ndu row)
61 {
62 const T temp = res(r,0) / ( right[r+1] + left[j-r] );
63 res(r,0) = saved + right[r+1] * temp ;// r-th function value of degree j
64 saved = left[j-r] * temp ;
65 }
66 res(j,0) = saved;
67 }
68 }
69
70 /// Evaluation for degree 1 B-spline basis
71 template <class T, typename KnotIterator, typename Derived>
evalDeg1Basis(const T & u,const KnotIterator knot,Eigen::MatrixBase<Derived> const & result)72 void evalDeg1Basis( const T & u,
73 const KnotIterator knot,
74 Eigen::MatrixBase<Derived> const & result )
75 {
76 result(0,0) = u - (*knot+1);
77 result(1,0) = (*knot) - u ;
78 const T dn = (*knot) - (*knot+1) ;
79 if( dn != 0.0 )
80 result.array /= dn;
81 }
82
83 /// Evaluation for degree 2 B-spline basis
84 template <class T, typename KnotIterator, typename Derived>
evalDeg2Basis(const T & u,const KnotIterator knot,Eigen::MatrixBase<Derived> const & result)85 void evalDeg2Basis( const T & u,
86 const KnotIterator knot,
87 Eigen::MatrixBase<Derived> const & result )
88 {
89
90 }
91
92 /// Evaluation for degree 3 B-spline basis
93 template <class T, typename KnotIterator, typename MatrixType>
evalDeg3Basis(const T & u,const KnotIterator knot,MatrixType & result)94 void evalDeg3Basis( const T & u,
95 const KnotIterator knot,
96 MatrixType & result )
97 {
98
99 }
100
101
102 /// Input: parameter position \a u, KnotIterator \a knot identifying the active interval,
103 /// degree \a deg, Output: table \a N.
104 /// Computes deBoor table used in Algorithm A2.5 in NURBS book.
105 /// Used by evalDerSingle_into(...), evalAllDerSingle_into(...) and evalSingle_into(...).
106 template <class T, typename KnotIterator>
deBoorTriangle(T u,KnotIterator knot,int deg,T N[])107 void deBoorTriangle( T u,
108 KnotIterator knot,
109 int deg,
110 T N[] )
111 {
112 const int table_size = deg+1;
113 T saved;
114
115 for( int j = 0; j <= deg; j++ ) // Initialize zeroth-degree functions
116 if( u >= *(knot+j) && u < *(knot+j+1) )
117 N[ j*table_size ] = 1;
118 else
119 N[ j*table_size ] = 0;
120 for( int k = 1; k <= deg; k++ ) // Compute full triangular table
121 {
122 if( N[(k-1)] == 0 )
123 saved = 0;
124 else
125 saved = ( (u-*(knot) )*N[ k-1 ])/( *(knot+k)- *(knot));
126 for( int j = 0; j < deg-k+1; j++)
127 {
128 if( N[ (j+1)*table_size + (k-1) ] == 0 )
129 {
130 N[ j*table_size + k ] = saved;
131 saved = 0;
132 }
133 else
134 {
135 const T Uleft = *(knot+j+1);
136 const T Uright = *(knot+j+k+1);
137 const T temp = N[ (j+1)*table_size + (k-1) ]/(Uright-Uleft);
138 N[ j*table_size + k ] = saved + (Uright-u)*temp;
139 saved = (u-Uleft)*temp;
140 }
141 }
142 }
143 }
144
145
146 /// Input: a sequence of 2*p+1 knots starting at iterator position
147 /// \a knot, evaluation point \a u, output the value of the
148 /// B-spline defined by \a coefs
149 /// Uses DeBoor's algorithm for evaluation
150 /// \todo add an overload with stride parameter for \a coefs
151 template <class T, typename KnotIterator, typename MatrixType>
evalGeo(const T & u,const KnotIterator & knot,int deg,MatrixType coefs,MatrixType & result)152 void evalGeo( const T & u,
153 const KnotIterator & knot,
154 int deg,
155 MatrixType coefs, // makes a local copy of coefs
156 MatrixType & result )
157 {
158 result.resize(deg+1, 1 ) ;
159 T tmp;
160
161 for ( int r=0; r!= deg; r++ ) // triangle step
162 //for ( int r=s; r< deg; r++ ) // TO DO: improve using multiplicity s
163 for ( int i=deg; i>r; i-- ) // recursive computation
164 {
165 tmp= ( u - *(knot+i) ) / ( *(knot+i+deg-r) - *(knot+i) );
166 coefs.row(i) = (T(1)-tmp)*coefs.row(i-1) + tmp* coefs.row(i) ;
167 }
168 result.noalias() = coefs.row(deg);
169 }
170
171 /// Input: a sequence of 2*p+1 knots starting at iterator position \a knot,
172 /// evaluation point \a u, output the value of all basis functions
173 /// which are active on \a u.
174 /// Uses DeBoor's algorithm for evaluation
175 template <class T, typename KnotIterator, typename MatrixType>
evalBasis2(const T & u,const KnotIterator & knot,int deg,MatrixType & result)176 void evalBasis2( const T & u,
177 const KnotIterator & knot,
178 int deg,
179 MatrixType & result )
180 {
181 MatrixType pseudo_coefs;
182 pseudo_coefs.setIdentity(deg+1,deg+1);
183 evalGeoAlg(u, knot, deg, pseudo_coefs, result);
184 }
185
186
187 /// Input: a sequence of p+2 knots, evaluation point. Output: the
188 /// value of the basis function supported on those knots
189 template <class T, typename KnotIterator, typename MatrixType>
evalBasisSingle()190 void evalBasisSingle( )
191 {
192
193
194 }
195
196 /// Input: a sequence of 2*p+1 knots, evaluation point, output
197 /// the derivatives evaluated at all functions supported on those knots
198 template <class T, typename KnotIterator, typename MatrixType>
derivBasis()199 void derivBasis( )
200 { }
201
202 /// Input: a sequence of p+2 knots, evaluation point. Output: the
203 /// value of the derivative of basis function supported on those knots
204 template <class T, typename KnotIterator, typename MatrixType>
derivBasisSingle()205 void derivBasisSingle( )
206 { }
207
208 /// Input: a sequence of 2*p+1 knots, evaluation point, output
209 /// the value of all derivatives up to order k supported on those knots
210 template <class T, typename KnotIterator, typename MatrixType>
allDersBasis()211 void allDersBasis( )
212 { }
213
214 /// Input: a sequence of p+2 knots, evaluation point, output
215 /// the value of all derivatives up to order k supported on those knots
216 template <class T, typename KnotIterator, typename MatrixType>
allDersSingle()217 void allDersSingle( )
218 { }
219
220
221
222 /// Increase the degree of a 1D B-spline from degree p to degree p + m.
223 template<class Basis_t>
degreeElevateBSpline(Basis_t & basis,gsMatrix<typename Basis_t::Scalar_t> & coefs,short_t m)224 void degreeElevateBSpline(Basis_t &basis,
225 gsMatrix<typename Basis_t::Scalar_t> & coefs,
226 short_t m)
227 {
228 typedef typename Basis_t::Scalar_t T;
229
230 GISMO_ASSERT(m >= 0 && m<512, "Can only elevate degree by a positive (not enormous) amount.");
231 GISMO_ASSERT(basis.size() == coefs.rows(), "Invalid coefficients");
232
233 if (m==0) return;
234
235 const short_t p = basis.degree();
236 const index_t ncoefs = coefs.rows();
237 const index_t n = coefs.cols();
238 const gsKnotVector<T> & knots = basis.knots();
239
240 // compute original derivative coefficients P (recurrence)
241 // original derivative coefficients
242 // # if defined(__GNUC__)
243 // STACK_ARRAY(gsMatrix<T>,P,p+1);
244 // # else // Note: No non-POD VLAs in clang/MSVC compiler
245 std::vector<gsMatrix<T> > P(p+1);
246 // # endif
247 for(short_t i=0;i<p+1;i++)
248 P[i].setZero(ncoefs - i, n);
249
250 // insert first row
251 P[0].swap(coefs);
252 // fill table of derivative coefficients
253 for(short_t j=1; j<=p;j++)
254 for(index_t i=0; i<ncoefs-j; i++)
255 {
256 if(knots[i+p+1]>knots[i+j])
257 P[j].row(i).noalias() =
258 ( P[j-1].row(i+1) - P[j-1].row(i) ) / ( knots[i+p+1] - knots[i+j] );
259 }
260
261 // loop over unique knot values (exept last one)
262 const typename gsKnotVector<T>::multContainer &
263 mult = knots.multiplicities(); // vector of mulitplicities
264
265 // degree elevate basis
266 basis.degreeElevate(m);
267 const index_t ncoefs_new = basis.size();
268 const short_t p_new = basis.degree();
269
270 // new (elevated) derivative coefficients
271 // # if defined(__GNUC__)
272 // STACK_ARRAY(gsMatrix<T>,Q,p_new+1);
273 // # else // Note: No non-POD VLAs in clang/MSVC compiler
274 std::vector<gsMatrix<T> > Q(p_new+1);
275 // # endif
276 for(short_t i=0; i<p_new+1; i++)
277 Q[i].setZero(ncoefs_new - i, n);
278
279 // loop over knot intervals (with positive measure):
280 // prescibe known coefficients (see Huang Theorem 2) and fill
281 // corresponding triangular table using backwards recurrence
282
283 // precompute factors
284 gsVector<T> factor = gsVector<T>::Ones(p+1);
285 for(short_t j=0; j<=p; j++)
286 for(short_t l=1; l<=j; l++)
287 factor[j] *= static_cast<T>((p+1-l)) / (p_new+1-l);
288
289 //set known coefficients
290 // for(int j=0; j<=p; ++j)
291 // Q[j].row(0)=factor[j]*P[j].row(0);
292
293 int betak = 0; // sum of interior mulitplicities
294 for(size_t k=0; k<mult.size()-1; k++)
295 {
296 //set known coefficients
297 for(short_t j=p+1-mult[k]; j<=p; ++j)
298 Q[j].row(betak+k*m) = factor[j] * P[j].row(betak);
299
300 for(short_t j=p_new-1; j>=0;j--)
301 {
302 // fill triangular table
303 for(short_t i=1; i<=p_new-j; i++)
304 {
305 const int ik= i+betak+k*m; // update index i for the considered knot value
306 if(knots[ik+p_new]>knots[ik+j])
307 Q[j].row(ik).noalias() =
308 Q[j].row(ik-1) + Q[j+1].row(ik-1) * (knots[ik-1+p_new+1]-knots[ik-1+j+1]);
309
310 }
311 }
312
313 betak += mult[k+1];
314 }
315
316 // Insert new coefficients into coefs
317 coefs.swap( Q[0] );
318 }
319
320
321 } // namespace bspline
322
323 }// namespace gismo
324