1 /** @file gsBSpline.h 2 3 @brief Represents a B-spline curve/function with one parameter 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 #include <gsCore/gsLinearAlgebra.h> 17 #include <gsCore/gsGeometry.h> 18 19 #include <gsNurbs/gsBSplineBasis.h> 20 #include <gsNurbs/gsBoehm.h> 21 #include <gsNurbs/gsBSplineSolver.h> 22 23 #include <gsUtils/gsPointGrid.h> 24 25 26 namespace gismo 27 { 28 29 /** \brief 30 A B-spline function of one argument, with arbitrary target dimension. 31 32 This is the geometry type associated with gsBSplineBasis. 33 34 \tparam T coefficient type 35 36 \ingroup geometry 37 \ingroup Nurbs 38 */ 39 40 41 template<class T> 42 class gsBSpline : public gsGeoTraits<1,T>::GeometryBase 43 { 44 public: 45 typedef gsKnotVector<T> KnotVectorType; 46 47 typedef typename gsGeoTraits<1,T>::GeometryBase Base; 48 49 typedef gsBSplineBasis<T> Basis; 50 51 /// Shared pointer for gsBSpline 52 typedef memory::shared_ptr< gsBSpline > Ptr; 53 54 /// Unique pointer for gsBSpline 55 typedef memory::unique_ptr< gsBSpline > uPtr; 56 57 public: 58 59 /// Default empty constructor. gsBSpline()60 gsBSpline() { } 61 62 // enable swap 63 using gsGeometry<T>::swap; 64 65 /// Construct B-Spline by basis and coefficient matrix. gsBSpline(const Basis & basis,gsMatrix<T> coefs)66 gsBSpline( const Basis & basis, gsMatrix<T> coefs ) 67 : Base( basis, give(coefs) ) 68 { 69 if( this->basis().isPeriodic() ) 70 this->basis().expandCoefs(m_coefs); 71 } 72 73 /// Construct B-Spline by a knot vector and coefficient matrix. 74 gsBSpline(KnotVectorType KV, gsMatrix<T> coefs, bool periodic = false ) 75 { 76 this->m_basis = new Basis(give(KV)); 77 m_coefs.swap(coefs); 78 79 if( periodic ) 80 { 81 const index_t sz = this->basis().size(); 82 83 this->basis().setPeriodic(); 84 85 if ( m_coefs.rows() == sz ) 86 { 87 this->basis().trimCoefs(m_coefs); 88 } 89 else if ( m_coefs.rows() == this->basis().size() ) 90 { 91 this->basis().expandCoefs(m_coefs); 92 } 93 else 94 { 95 GISMO_ERROR("Wrong number of coefficients for periodic basis."); 96 } 97 } 98 else // non-periodic 99 { 100 if( this->m_coefs.rows() + KV.degree() + 1 != static_cast<int>( KV.size() ) ) 101 gsWarn << "gsBSpline Warning: #Knots="<< KV.size()<< ", #coefs="<< this->m_coefs.rows() <<"\n"; 102 } 103 } 104 105 106 /// @brief Construct a B-spline from an interval and knot vector specification. 107 /// \param u0 starting parameter 108 /// \param u1 end parameter 109 /// \param interior number of interior knots 110 /// \param degree degree of the spline space 111 /// \param coefs coefficients of the spline space 112 /// \param mult_interior multiplicity at the interior knots 113 /// \param periodic specifies whether the B-spline is periodic 114 /// 115 /// \ingroup Nurbs 116 gsBSpline(T u0, T u1, unsigned interior, int degree, 117 gsMatrix<T> coefs, unsigned mult_interior=1, bool periodic = false) 118 { 119 this->m_basis = new Basis(u0, u1, interior, degree, mult_interior, periodic ); 120 if( periodic ) 121 { 122 GISMO_ASSERT( this->basis().numCrossingFunctions() == 1, 123 "Knot vector with clamped knots should lead to m_periodic == 1."); 124 125 this->m_coefs = this->basis().perCoefs(coefs); 126 GISMO_ASSERT( this->m_coefs.rows() >= this->basis().size(), 127 "You should give at least as many coefficients as the size of the basis after converting to periodic."); 128 if( this->m_coefs.rows() > this->basis().size() ) 129 gsWarn << "Some of the last coefficients are going to be lost during conversion to periodic basis.\n"; 130 } 131 else // non-periodic 132 { 133 this->m_coefs.swap(coefs); 134 GISMO_ASSERT( this->m_coefs.rows() == this->basis().size(), 135 "Number of coefficients does not match the size of the basis."); 136 } 137 } 138 GISMO_CLONE_FUNCTION(gsBSpline)139 GISMO_CLONE_FUNCTION(gsBSpline) 140 141 GISMO_BASIS_ACCESSORS 142 143 public: 144 145 /// Prints the object as a string. 146 std::ostream &print(std::ostream &os) const 147 { 148 os << "BSpline curve "<< "of degree "<< this->basis().degree()<< ", "<< this->basis().knots() <<".\n"; 149 os << "with control points "<< this->m_coefs.row(0)<<" ... "<<this->m_coefs.bottomRows(1) << ".\n"; 150 if( this->basis().isPeriodic() ) 151 os << "Periodic with overlay " << this->basis().numCrossingFunctions() << ".\n"; 152 return os; 153 } 154 155 /// Returns the starting value of the domain of the basis domainStart()156 T domainStart() const { return this->basis().knots().first(); } 157 158 /// Returns the end value of the domain of the basis domainEnd()159 T domainEnd() const { return this->basis().knots().last(); } 160 161 /// Returns a reference to the knot vector 162 KnotVectorType & knots(const int i = 0) 163 { 164 GISMO_UNUSED(i); 165 GISMO_ASSERT( i==0, "Requested knots of invalid direction "<< i ); 166 return this->basis().knots(); 167 } 168 169 /// Returns a (const )reference to the knot vector 170 const KnotVectorType & knots(const int i = 0) const 171 { 172 GISMO_UNUSED(i); 173 GISMO_ASSERT( i==0, "Requested knots of invalid direction "<< i ); 174 return this->basis().knots(); 175 } 176 177 /// Returns the degree of the B-spline 178 short_t degree(short_t i = 0) const 179 { 180 GISMO_UNUSED(i); 181 GISMO_ASSERT( i==0, "Requested knots of invalid direction "<< i ); 182 return this->basis().degree(); 183 } 184 185 // compatible curves: same degree, same first/last p+1 knots isCompatible(gsGeometry<T> *)186 void isCompatible( gsGeometry<T> * ) 187 { GISMO_NO_IMPLEMENTATION } 188 189 // compatible curves: same degree, same first/last p+1 knots makeCompatible(gsGeometry<T> *)190 void makeCompatible( gsGeometry<T> * ) 191 { GISMO_NO_IMPLEMENTATION } 192 193 194 /// Merge other B-spline into this one. 195 void merge( gsGeometry<T> * otherG ); 196 197 /// Insert the given new knot (multiplicity \a i) without changing the curve. 198 void insertKnot( T knot, int i = 1) 199 { 200 if (i==0) return; 201 //if ( i==1) 202 //single knot insertion: Boehm's algorithm 203 //else 204 //knot with multiplicity: Oslo algorithm 205 if( this->basis().isPeriodic() ) 206 { 207 int borderKnotMult = this->basis().borderKnotMult(); 208 KnotVectorType & knots = this->knots(); 209 unsigned deg = this->basis().degree(); 210 211 212 GISMO_ASSERT( knot != knots[deg] && knot != knots[knots.size() - deg - 1], 213 "You are trying to increase the multiplicity of the p+1st knot but the code is not ready for that.\n"); 214 215 // If we would be inserting to "passive" regions, we rather insert the knot into the mirrored part. 216 // Adjustment of the mirrored knots is then desirable. 217 if( knot < knots[deg - borderKnotMult + 1] ) 218 { 219 knot += this->basis()._activeLength(); 220 } 221 else if( knot > knots[knots.size() - deg + borderKnotMult - 2] ) 222 { 223 knot -= this->basis()._activeLength(); 224 } 225 // If necessary, we update the mirrored part of the knot vector. 226 if((knot < knots[2*deg + 1 - borderKnotMult]) || (knot >= knots[knots.size() - 2*deg - 2 + borderKnotMult])) 227 this->basis().enforceOuterKnotsPeriodic(); 228 229 // We copy some of the control points to pretend for a while that the basis is not periodic. 230 //gsMatrix<T> trueCoefs = this->basis().perCoefs( this->coefs() ); 231 gsBoehm( this->basis().knots(), this->coefs(), knot, i ); 232 //this->coefs() = trueCoefs; 233 //this->coefs().conservativeResize( this->basis().size(), this->coefs().cols() ); 234 } 235 else // non-periodic 236 gsBoehm( this->basis().knots(), this->coefs() , knot, i); 237 } 238 239 /// Insert the given new knots in the range \a [\em inBegin .. \em inEend ) 240 /// without changing the curve. 241 /// \param inBegin iterator pointing to the first knot to be inserted 242 /// \param inEnd iterator pointing to one position after the last 243 /// knot to be inserted 244 template <class It> insertKnots(It inBegin,It inEnd)245 void insertKnots( It inBegin, It inEnd) 246 { 247 if( this->basis().isPeriodic() ) 248 { 249 // We assume we got valid (i.e., non-NULL) iterators; I don't think we have a reasonable way to test it in GISMO_ASSERT. 250 251 GISMO_ASSERT( (*inBegin > *(this->knots().begin())) 252 && (*(inEnd-1) < *(this->knots().end()-1)), 253 "Please, ask me to insert knots inside the knot interval only." ); 254 255 std::vector<T> newKnots(inBegin,inEnd); 256 257 // It can happen that user would ask us to insert knots outside the active range. 258 // Should it happen, we shift the values and then sort the knot vector to remain non-decreasing. 259 260 T activeLength = this->basis()._activeLength(); 261 T blue1 = *(this->basis().knots().begin() + this->degree() ); 262 T blue2 = *(this->basis().knots().end() - this->degree() ); 263 typename std::vector<T>::iterator begin = newKnots.begin(); 264 typename std::vector<T>::iterator end = newKnots.end(); 265 for( typename std::vector<T>::iterator it = begin; it != end; ++it ) 266 { 267 if( *it < blue1 ) 268 *it += activeLength; 269 else if( *it > blue2 ) 270 *it -= activeLength; 271 else if( (*it == blue1) || (*it == blue2) ) 272 { 273 // I would like to erase it and go on but for that the iterator is not enough. 274 // ANGELOS, wouldn't it be better to put a GISMO_ASSERT here instead? Or throw an exception? 275 gsWarn << "Interrupting insertKnots(): Cannot insert value equal to the p+1st knot from the beginning or end into a periodic curve.\n"; 276 return; 277 } 278 } 279 /* // This way it would be nicer but it does not cover all the dubious cases. 280 for( It it = begin; *it < blue1; ++it ) 281 *it += activeLength; 282 for( It it = end-1; *it > blue2; --it ) 283 *it -= activeLength;*/ 284 285 std::sort( begin, end ); 286 287 // We copy some of the control points to pretend for a while that the basis is not periodic. 288 //gsMatrix<T> trueCoefs = this->basis().perCoefs( this->coefs() ); 289 gsBoehmRefine( this->basis().knots(), this->coefs(), this->degree(), begin, end ); 290 //this->basis().enforceOuterKnotsPeriodic(); 291 //this->coefs() = trueCoefs; // Isn't this memory-leaking? 292 // Periodic basis is restored using the so-called Student's Algorithm: forget as much as possible =). 293 //this->coefs().conservativeResize( this->basis().size(), this->coefs().cols() ); 294 } 295 else // non-periodic 296 gsBoehmRefine( this->basis().knots(), this->coefs(), this->degree(), inBegin, inEnd); 297 } 298 299 // Look at gsGeometry class for a description 300 void degreeElevate(short_t const i = 1, short_t const dir = -1); 301 302 /// @brief Returns true iff the point p is contained (approximately) on 303 /// the curve, with the given tolerance. 304 // Under Construction..( TO DO ) 305 bool contains( gsMatrix<T> const & p, T const & tol = 1e-6 ) 306 { 307 assert( p.cols()==1 ); 308 gsBSplineSolver<T> slv; 309 std::vector<T> roots; 310 short_t dim = this->geoDim(); 311 gsMatrix<T> ev, tmp(1,1); 312 int i(1); 313 314 slv.allRoots( *this, roots, 0, p(0,0), 1e-6, 100); 315 for ( typename std::vector<T>::const_iterator it=roots.begin(); 316 it != roots.end(); ++it) 317 { 318 tmp(0,0)= *it; 319 this->eval_into(tmp,ev); 320 for (i=1; i!=dim; ++i ) 321 if ( math::abs( ev(i,0) - p(i,0) ) > tol ) 322 break; 323 if (i==dim) 324 return true; 325 } 326 return false; 327 } 328 329 /// Sample \a npoints uniformly distributed (in parameter domain) points on the curve. 330 gsMatrix<T> sample(int npoints = 50) const 331 { 332 gsMatrix<T> images; 333 gsMatrix<T> interval = this->parameterRange(); 334 const gsMatrix<T> pts = gsPointGrid(interval, npoints ); 335 this->eval_into( pts, images ); 336 return images; 337 } 338 339 /// Tries to convert the curve into periodic. 340 void setPeriodic(bool flag = true) 341 { 342 this->basis().setPeriodic(flag); 343 this->m_coefs = this->basis().perCoefs( this->m_coefs ); 344 } 345 numCoefs()346 inline int numCoefs() const { return this->m_coefs.rows() - this->basis().numCrossingFunctions(); } 347 isPeriodic()348 inline bool isPeriodic() const { return this->basis().isPeriodic(); } 349 350 351 /// Returns true if this curve is closed 352 bool isClosed(T tol = 1e-10) const 353 { 354 return ( this->basis().isPeriodic() || 355 (m_coefs.row(0) - m_coefs.row(m_coefs.rows()-1)).squaredNorm()<tol ); 356 } 357 358 /// \brief Return true if point \a u is on the curve with 359 /// tolerance \a tol 360 bool isOn(gsMatrix<T> const &u, T tol = 1e-3) const; 361 362 /// \brief Return true if point \a u is an endpoint (corner) of 363 /// the curve with tolerance \a tol 364 bool isPatchCorner(gsMatrix<T> const &v, T tol = 1e-3) const; 365 366 /// \brief returns the tensor-index \a curr of the corner control 367 /// point \a v, or an invalid index if the corner is not found 368 /// within the tolerance \a tol 369 void findCorner(const gsMatrix<T> & v, 370 gsVector<index_t,1> & curr, 371 T tol = 1e-3); 372 373 /// \brief Modifies the parameterization such that the point \a v 374 /// is the starting point of the curve. Assumes that \a v is 375 /// either the starting or the ending point of the curve 376 void setOriginCorner(gsMatrix<T> const &v); 377 378 /// \brief Modifies the parameterization such that the point \a v 379 /// is the ending point of the curve. Assumes that \a v is 380 /// either the starting or the ending point of the curve 381 void setFurthestCorner(gsMatrix<T> const &v); 382 383 void swapDirections(const unsigned i, const unsigned j); 384 385 protected: 386 387 using Base::m_coefs; 388 389 // TODO Check function 390 // check function: check the coefficient number, degree, knot vector ... 391 392 }; // class gsBSpline 393 394 395 /* 396 // Product of spline functions 397 template<class T> 398 gsBSpline<T> operator*(const gsBSpline<T> & lhs, const gsBSpline<T> & rhs) 399 { 400 // Note: quick-fix implementation. Better algorithms exist 401 gsKnotVector<T> kv = lhs.knots().knotUnion( lhs.knots() ); 402 kv.degreeElevate( lhs.degree() + rhs.degree() - kv.degree() ); 403 404 gsMatrix<T> pts; 405 kv.greville_into(pts); 406 const gsMatrix<T> ev = (lhs.eval(pts)).array() * (rhs.eval(pts)).array(); 407 408 // fixme: avoid temporaries here 409 return *(static_cast<gsBSpline<T>*>(gsBSplineBasis<T>(kv).interpolateData(ev,pts))); 410 } 411 */ 412 413 } // namespace gismo 414 415 416 #ifndef GISMO_BUILD_LIB 417 #include GISMO_HPP_HEADER(gsBSpline.hpp) 418 #endif 419