1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 20010-2011 Hauke Heibel <hauke.heibel@gmail.com>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #ifndef EIGEN_SPLINE_H
11 #define EIGEN_SPLINE_H
12 
13 #include "SplineFwd.h"
14 
15 namespace Eigen
16 {
17     /**
18      * \ingroup Splines_Module
19      * \class Spline
20      * \brief A class representing multi-dimensional spline curves.
21      *
22      * The class represents B-splines with non-uniform knot vectors. Each control
23      * point of the B-spline is associated with a basis function
24      * \f{align*}
25      *   C(u) & = \sum_{i=0}^{n}N_{i,p}(u)P_i
26      * \f}
27      *
28      * \tparam _Scalar The underlying data type (typically float or double)
29      * \tparam _Dim The curve dimension (e.g. 2 or 3)
30      * \tparam _Degree Per default set to Dynamic; could be set to the actual desired
31      *                degree for optimization purposes (would result in stack allocation
32      *                of several temporary variables).
33      **/
34   template <typename _Scalar, int _Dim, int _Degree>
35   class Spline
36   {
37   public:
38     typedef _Scalar Scalar; /*!< The spline curve's scalar type. */
39     enum { Dimension = _Dim /*!< The spline curve's dimension. */ };
40     enum { Degree = _Degree /*!< The spline curve's degree. */ };
41 
42     /** \brief The point type the spline is representing. */
43     typedef typename SplineTraits<Spline>::PointType PointType;
44 
45     /** \brief The data type used to store knot vectors. */
46     typedef typename SplineTraits<Spline>::KnotVectorType KnotVectorType;
47 
48     /** \brief The data type used to store non-zero basis functions. */
49     typedef typename SplineTraits<Spline>::BasisVectorType BasisVectorType;
50 
51     /** \brief The data type representing the spline's control points. */
52     typedef typename SplineTraits<Spline>::ControlPointVectorType ControlPointVectorType;
53 
54     /**
55     * \brief Creates a (constant) zero spline.
56     * For Splines with dynamic degree, the resulting degree will be 0.
57     **/
Spline()58     Spline()
59     : m_knots(1, (Degree==Dynamic ? 2 : 2*Degree+2))
60     , m_ctrls(ControlPointVectorType::Zero(2,(Degree==Dynamic ? 1 : Degree+1)))
61     {
62       // in theory this code can go to the initializer list but it will get pretty
63       // much unreadable ...
64       enum { MinDegree = (Degree==Dynamic ? 0 : Degree) };
65       m_knots.template segment<MinDegree+1>(0) = Array<Scalar,1,MinDegree+1>::Zero();
66       m_knots.template segment<MinDegree+1>(MinDegree+1) = Array<Scalar,1,MinDegree+1>::Ones();
67     }
68 
69     /**
70     * \brief Creates a spline from a knot vector and control points.
71     * \param knots The spline's knot vector.
72     * \param ctrls The spline's control point vector.
73     **/
74     template <typename OtherVectorType, typename OtherArrayType>
Spline(const OtherVectorType & knots,const OtherArrayType & ctrls)75     Spline(const OtherVectorType& knots, const OtherArrayType& ctrls) : m_knots(knots), m_ctrls(ctrls) {}
76 
77     /**
78     * \brief Copy constructor for splines.
79     * \param spline The input spline.
80     **/
81     template <int OtherDegree>
Spline(const Spline<Scalar,Dimension,OtherDegree> & spline)82     Spline(const Spline<Scalar, Dimension, OtherDegree>& spline) :
83     m_knots(spline.knots()), m_ctrls(spline.ctrls()) {}
84 
85     /**
86      * \brief Returns the knots of the underlying spline.
87      **/
knots()88     const KnotVectorType& knots() const { return m_knots; }
89 
90     /**
91      * \brief Returns the knots of the underlying spline.
92      **/
ctrls()93     const ControlPointVectorType& ctrls() const { return m_ctrls; }
94 
95     /**
96      * \brief Returns the spline value at a given site \f$u\f$.
97      *
98      * The function returns
99      * \f{align*}
100      *   C(u) & = \sum_{i=0}^{n}N_{i,p}P_i
101      * \f}
102      *
103      * \param u Parameter \f$u \in [0;1]\f$ at which the spline is evaluated.
104      * \return The spline value at the given location \f$u\f$.
105      **/
106     PointType operator()(Scalar u) const;
107 
108     /**
109      * \brief Evaluation of spline derivatives of up-to given order.
110      *
111      * The function returns
112      * \f{align*}
113      *   \frac{d^i}{du^i}C(u) & = \sum_{i=0}^{n} \frac{d^i}{du^i} N_{i,p}(u)P_i
114      * \f}
115      * for i ranging between 0 and order.
116      *
117      * \param u Parameter \f$u \in [0;1]\f$ at which the spline derivative is evaluated.
118      * \param order The order up to which the derivatives are computed.
119      **/
120     typename SplineTraits<Spline>::DerivativeType
121       derivatives(Scalar u, DenseIndex order) const;
122 
123     /**
124      * \copydoc Spline::derivatives
125      * Using the template version of this function is more efficieent since
126      * temporary objects are allocated on the stack whenever this is possible.
127      **/
128     template <int DerivativeOrder>
129     typename SplineTraits<Spline,DerivativeOrder>::DerivativeType
130       derivatives(Scalar u, DenseIndex order = DerivativeOrder) const;
131 
132     /**
133      * \brief Computes the non-zero basis functions at the given site.
134      *
135      * Splines have local support and a point from their image is defined
136      * by exactly \f$p+1\f$ control points \f$P_i\f$ where \f$p\f$ is the
137      * spline degree.
138      *
139      * This function computes the \f$p+1\f$ non-zero basis function values
140      * for a given parameter value \f$u\f$. It returns
141      * \f{align*}{
142      *   N_{i,p}(u), \hdots, N_{i+p+1,p}(u)
143      * \f}
144      *
145      * \param u Parameter \f$u \in [0;1]\f$ at which the non-zero basis functions
146      *          are computed.
147      **/
148     typename SplineTraits<Spline>::BasisVectorType
149       basisFunctions(Scalar u) const;
150 
151     /**
152      * \brief Computes the non-zero spline basis function derivatives up to given order.
153      *
154      * The function computes
155      * \f{align*}{
156      *   \frac{d^i}{du^i} N_{i,p}(u), \hdots, \frac{d^i}{du^i} N_{i+p+1,p}(u)
157      * \f}
158      * with i ranging from 0 up to the specified order.
159      *
160      * \param u Parameter \f$u \in [0;1]\f$ at which the non-zero basis function
161      *          derivatives are computed.
162      * \param order The order up to which the basis function derivatives are computes.
163      **/
164     typename SplineTraits<Spline>::BasisDerivativeType
165       basisFunctionDerivatives(Scalar u, DenseIndex order) const;
166 
167     /**
168      * \copydoc Spline::basisFunctionDerivatives
169      * Using the template version of this function is more efficieent since
170      * temporary objects are allocated on the stack whenever this is possible.
171      **/
172     template <int DerivativeOrder>
173     typename SplineTraits<Spline,DerivativeOrder>::BasisDerivativeType
174       basisFunctionDerivatives(Scalar u, DenseIndex order = DerivativeOrder) const;
175 
176     /**
177      * \brief Returns the spline degree.
178      **/
179     DenseIndex degree() const;
180 
181     /**
182      * \brief Returns the span within the knot vector in which u is falling.
183      * \param u The site for which the span is determined.
184      **/
185     DenseIndex span(Scalar u) const;
186 
187     /**
188      * \brief Computes the spang within the provided knot vector in which u is falling.
189      **/
190     static DenseIndex Span(typename SplineTraits<Spline>::Scalar u, DenseIndex degree, const typename SplineTraits<Spline>::KnotVectorType& knots);
191 
192     /**
193      * \brief Returns the spline's non-zero basis functions.
194      *
195      * The function computes and returns
196      * \f{align*}{
197      *   N_{i,p}(u), \hdots, N_{i+p+1,p}(u)
198      * \f}
199      *
200      * \param u The site at which the basis functions are computed.
201      * \param degree The degree of the underlying spline.
202      * \param knots The underlying spline's knot vector.
203      **/
204     static BasisVectorType BasisFunctions(Scalar u, DenseIndex degree, const KnotVectorType& knots);
205 
206 
207   private:
208     KnotVectorType m_knots; /*!< Knot vector. */
209     ControlPointVectorType  m_ctrls; /*!< Control points. */
210   };
211 
212   template <typename _Scalar, int _Dim, int _Degree>
Span(typename SplineTraits<Spline<_Scalar,_Dim,_Degree>>::Scalar u,DenseIndex degree,const typename SplineTraits<Spline<_Scalar,_Dim,_Degree>>::KnotVectorType & knots)213   DenseIndex Spline<_Scalar, _Dim, _Degree>::Span(
214     typename SplineTraits< Spline<_Scalar, _Dim, _Degree> >::Scalar u,
215     DenseIndex degree,
216     const typename SplineTraits< Spline<_Scalar, _Dim, _Degree> >::KnotVectorType& knots)
217   {
218     // Piegl & Tiller, "The NURBS Book", A2.1 (p. 68)
219     if (u <= knots(0)) return degree;
220     const Scalar* pos = std::upper_bound(knots.data()+degree-1, knots.data()+knots.size()-degree-1, u);
221     return static_cast<DenseIndex>( std::distance(knots.data(), pos) - 1 );
222   }
223 
224   template <typename _Scalar, int _Dim, int _Degree>
225   typename Spline<_Scalar, _Dim, _Degree>::BasisVectorType
BasisFunctions(typename Spline<_Scalar,_Dim,_Degree>::Scalar u,DenseIndex degree,const typename Spline<_Scalar,_Dim,_Degree>::KnotVectorType & knots)226     Spline<_Scalar, _Dim, _Degree>::BasisFunctions(
227     typename Spline<_Scalar, _Dim, _Degree>::Scalar u,
228     DenseIndex degree,
229     const typename Spline<_Scalar, _Dim, _Degree>::KnotVectorType& knots)
230   {
231     typedef typename Spline<_Scalar, _Dim, _Degree>::BasisVectorType BasisVectorType;
232 
233     const DenseIndex p = degree;
234     const DenseIndex i = Spline::Span(u, degree, knots);
235 
236     const KnotVectorType& U = knots;
237 
238     BasisVectorType left(p+1); left(0) = Scalar(0);
239     BasisVectorType right(p+1); right(0) = Scalar(0);
240 
241     VectorBlock<BasisVectorType,Degree>(left,1,p) = u - VectorBlock<const KnotVectorType,Degree>(U,i+1-p,p).reverse();
242     VectorBlock<BasisVectorType,Degree>(right,1,p) = VectorBlock<const KnotVectorType,Degree>(U,i+1,p) - u;
243 
244     BasisVectorType N(1,p+1);
245     N(0) = Scalar(1);
246     for (DenseIndex j=1; j<=p; ++j)
247     {
248       Scalar saved = Scalar(0);
249       for (DenseIndex r=0; r<j; r++)
250       {
251         const Scalar tmp = N(r)/(right(r+1)+left(j-r));
252         N[r] = saved + right(r+1)*tmp;
253         saved = left(j-r)*tmp;
254       }
255       N(j) = saved;
256     }
257     return N;
258   }
259 
260   template <typename _Scalar, int _Dim, int _Degree>
degree()261   DenseIndex Spline<_Scalar, _Dim, _Degree>::degree() const
262   {
263     if (_Degree == Dynamic)
264       return m_knots.size() - m_ctrls.cols() - 1;
265     else
266       return _Degree;
267   }
268 
269   template <typename _Scalar, int _Dim, int _Degree>
span(Scalar u)270   DenseIndex Spline<_Scalar, _Dim, _Degree>::span(Scalar u) const
271   {
272     return Spline::Span(u, degree(), knots());
273   }
274 
275   template <typename _Scalar, int _Dim, int _Degree>
operator()276   typename Spline<_Scalar, _Dim, _Degree>::PointType Spline<_Scalar, _Dim, _Degree>::operator()(Scalar u) const
277   {
278     enum { Order = SplineTraits<Spline>::OrderAtCompileTime };
279 
280     const DenseIndex span = this->span(u);
281     const DenseIndex p = degree();
282     const BasisVectorType basis_funcs = basisFunctions(u);
283 
284     const Replicate<BasisVectorType,Dimension,1> ctrl_weights(basis_funcs);
285     const Block<const ControlPointVectorType,Dimension,Order> ctrl_pts(ctrls(),0,span-p,Dimension,p+1);
286     return (ctrl_weights * ctrl_pts).rowwise().sum();
287   }
288 
289   /* --------------------------------------------------------------------------------------------- */
290 
291   template <typename SplineType, typename DerivativeType>
derivativesImpl(const SplineType & spline,typename SplineType::Scalar u,DenseIndex order,DerivativeType & der)292   void derivativesImpl(const SplineType& spline, typename SplineType::Scalar u, DenseIndex order, DerivativeType& der)
293   {
294     enum { Dimension = SplineTraits<SplineType>::Dimension };
295     enum { Order = SplineTraits<SplineType>::OrderAtCompileTime };
296     enum { DerivativeOrder = DerivativeType::ColsAtCompileTime };
297 
298     typedef typename SplineTraits<SplineType>::ControlPointVectorType ControlPointVectorType;
299     typedef typename SplineTraits<SplineType,DerivativeOrder>::BasisDerivativeType BasisDerivativeType;
300     typedef typename BasisDerivativeType::ConstRowXpr BasisDerivativeRowXpr;
301 
302     const DenseIndex p = spline.degree();
303     const DenseIndex span = spline.span(u);
304 
305     const DenseIndex n = (std::min)(p, order);
306 
307     der.resize(Dimension,n+1);
308 
309     // Retrieve the basis function derivatives up to the desired order...
310     const BasisDerivativeType basis_func_ders = spline.template basisFunctionDerivatives<DerivativeOrder>(u, n+1);
311 
312     // ... and perform the linear combinations of the control points.
313     for (DenseIndex der_order=0; der_order<n+1; ++der_order)
314     {
315       const Replicate<BasisDerivativeRowXpr,Dimension,1> ctrl_weights( basis_func_ders.row(der_order) );
316       const Block<const ControlPointVectorType,Dimension,Order> ctrl_pts(spline.ctrls(),0,span-p,Dimension,p+1);
317       der.col(der_order) = (ctrl_weights * ctrl_pts).rowwise().sum();
318     }
319   }
320 
321   template <typename _Scalar, int _Dim, int _Degree>
322   typename SplineTraits< Spline<_Scalar, _Dim, _Degree> >::DerivativeType
derivatives(Scalar u,DenseIndex order)323     Spline<_Scalar, _Dim, _Degree>::derivatives(Scalar u, DenseIndex order) const
324   {
325     typename SplineTraits< Spline >::DerivativeType res;
326     derivativesImpl(*this, u, order, res);
327     return res;
328   }
329 
330   template <typename _Scalar, int _Dim, int _Degree>
331   template <int DerivativeOrder>
332   typename SplineTraits< Spline<_Scalar, _Dim, _Degree>, DerivativeOrder >::DerivativeType
derivatives(Scalar u,DenseIndex order)333     Spline<_Scalar, _Dim, _Degree>::derivatives(Scalar u, DenseIndex order) const
334   {
335     typename SplineTraits< Spline, DerivativeOrder >::DerivativeType res;
336     derivativesImpl(*this, u, order, res);
337     return res;
338   }
339 
340   template <typename _Scalar, int _Dim, int _Degree>
341   typename SplineTraits< Spline<_Scalar, _Dim, _Degree> >::BasisVectorType
basisFunctions(Scalar u)342     Spline<_Scalar, _Dim, _Degree>::basisFunctions(Scalar u) const
343   {
344     return Spline::BasisFunctions(u, degree(), knots());
345   }
346 
347   /* --------------------------------------------------------------------------------------------- */
348 
349   template <typename SplineType, typename DerivativeType>
basisFunctionDerivativesImpl(const SplineType & spline,typename SplineType::Scalar u,DenseIndex order,DerivativeType & N_)350   void basisFunctionDerivativesImpl(const SplineType& spline, typename SplineType::Scalar u, DenseIndex order, DerivativeType& N_)
351   {
352     enum { Order = SplineTraits<SplineType>::OrderAtCompileTime };
353 
354     typedef typename SplineTraits<SplineType>::Scalar Scalar;
355     typedef typename SplineTraits<SplineType>::BasisVectorType BasisVectorType;
356     typedef typename SplineTraits<SplineType>::KnotVectorType KnotVectorType;
357 
358     const KnotVectorType& U = spline.knots();
359 
360     const DenseIndex p = spline.degree();
361     const DenseIndex span = spline.span(u);
362 
363     const DenseIndex n = (std::min)(p, order);
364 
365     N_.resize(n+1, p+1);
366 
367     BasisVectorType left = BasisVectorType::Zero(p+1);
368     BasisVectorType right = BasisVectorType::Zero(p+1);
369 
370     Matrix<Scalar,Order,Order> ndu(p+1,p+1);
371 
372     double saved, temp;
373 
374     ndu(0,0) = 1.0;
375 
376     DenseIndex j;
377     for (j=1; j<=p; ++j)
378     {
379       left[j] = u-U[span+1-j];
380       right[j] = U[span+j]-u;
381       saved = 0.0;
382 
383       for (DenseIndex r=0; r<j; ++r)
384       {
385         /* Lower triangle */
386         ndu(j,r) = right[r+1]+left[j-r];
387         temp = ndu(r,j-1)/ndu(j,r);
388         /* Upper triangle */
389         ndu(r,j) = static_cast<Scalar>(saved+right[r+1] * temp);
390         saved = left[j-r] * temp;
391       }
392 
393       ndu(j,j) = static_cast<Scalar>(saved);
394     }
395 
396     for (j = p; j>=0; --j)
397       N_(0,j) = ndu(j,p);
398 
399     // Compute the derivatives
400     DerivativeType a(n+1,p+1);
401     DenseIndex r=0;
402     for (; r<=p; ++r)
403     {
404       DenseIndex s1,s2;
405       s1 = 0; s2 = 1; // alternate rows in array a
406       a(0,0) = 1.0;
407 
408       // Compute the k-th derivative
409       for (DenseIndex k=1; k<=static_cast<DenseIndex>(n); ++k)
410       {
411         double d = 0.0;
412         DenseIndex rk,pk,j1,j2;
413         rk = r-k; pk = p-k;
414 
415         if (r>=k)
416         {
417           a(s2,0) = a(s1,0)/ndu(pk+1,rk);
418           d = a(s2,0)*ndu(rk,pk);
419         }
420 
421         if (rk>=-1) j1 = 1;
422         else        j1 = -rk;
423 
424         if (r-1 <= pk) j2 = k-1;
425         else           j2 = p-r;
426 
427         for (j=j1; j<=j2; ++j)
428         {
429           a(s2,j) = (a(s1,j)-a(s1,j-1))/ndu(pk+1,rk+j);
430           d += a(s2,j)*ndu(rk+j,pk);
431         }
432 
433         if (r<=pk)
434         {
435           a(s2,k) = -a(s1,k-1)/ndu(pk+1,r);
436           d += a(s2,k)*ndu(r,pk);
437         }
438 
439         N_(k,r) = static_cast<Scalar>(d);
440         j = s1; s1 = s2; s2 = j; // Switch rows
441       }
442     }
443 
444     /* Multiply through by the correct factors */
445     /* (Eq. [2.9])                             */
446     r = p;
447     for (DenseIndex k=1; k<=static_cast<DenseIndex>(n); ++k)
448     {
449       for (DenseIndex j=p; j>=0; --j) N_(k,j) *= r;
450       r *= p-k;
451     }
452   }
453 
454   template <typename _Scalar, int _Dim, int _Degree>
455   typename SplineTraits< Spline<_Scalar, _Dim, _Degree> >::BasisDerivativeType
basisFunctionDerivatives(Scalar u,DenseIndex order)456     Spline<_Scalar, _Dim, _Degree>::basisFunctionDerivatives(Scalar u, DenseIndex order) const
457   {
458     typename SplineTraits< Spline >::BasisDerivativeType der;
459     basisFunctionDerivativesImpl(*this, u, order, der);
460     return der;
461   }
462 
463   template <typename _Scalar, int _Dim, int _Degree>
464   template <int DerivativeOrder>
465   typename SplineTraits< Spline<_Scalar, _Dim, _Degree>, DerivativeOrder >::BasisDerivativeType
basisFunctionDerivatives(Scalar u,DenseIndex order)466     Spline<_Scalar, _Dim, _Degree>::basisFunctionDerivatives(Scalar u, DenseIndex order) const
467   {
468     typename SplineTraits< Spline, DerivativeOrder >::BasisDerivativeType der;
469     basisFunctionDerivativesImpl(*this, u, order, der);
470     return der;
471   }
472 }
473 
474 #endif // EIGEN_SPLINE_H
475