1 /*=============================================================================
2         File: nurbsS.cpp
3      Purpose:
4     Revision: $Id: nurbsS.cpp,v 1.2 2002/05/13 21:07:46 philosophil Exp $
5   Created by: Philippe Lavoie          (3 Oct, 1996)
6  Modified by:
7 
8  Copyright notice:
9           Copyright (C) 1996-1997 Philippe Lavoie
10 
11           This library is free software; you can redistribute it and/or
12           modify it under the terms of the GNU Library General Public
13           License as published by the Free Software Foundation; either
14           version 2 of the License, or (at your option) any later version.
15 
16           This library is distributed in the hope that it will be useful,
17           but WITHOUT ANY WARRANTY; without even the implied warranty of
18           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19           Library General Public License for more details.
20 
21           You should have received a copy of the GNU Library General Public
22           License along with this library; if not, write to the Free
23           Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 =============================================================================*/
25 
26 #include <string.h>
27 #include <matrixRT.h>
28 #include <math.h>
29 #include <nurbsS.h>
30 #include "integrate.h"
31 
32 #ifdef USING_VCC
33 #include <stdlib.h>
34 #endif
35 
36 /*!
37  */
38 namespace PLib {
39 
40 /*!
41   \brief  Default constructor
42 
43   \warning The surface is initialized to invalid values. Use a reset or
44            a read function to set them to correct values.
45   \author Philippe Lavoie
46   \date 24 January, 1997
47 */
48 template <class T, int N>
NurbsSurface()49 NurbsSurface<T,N>::NurbsSurface():
50   ParaSurface<T,N>(), U(1),V(1),P(1,1),degU(0),degV(0)
51 {
52 }
53 
54 /*!
55   \brief  the copy constructor
56 
57   \param s the NurbsSurface<T,N> to copy
58 
59   \author Philippe Lavoie
60   \date 24 January, 1997
61 */
62 template <class T, int N>
NurbsSurface(const NurbsSurface<T,N> & s)63 NurbsSurface<T,N>::NurbsSurface(const NurbsSurface<T,N>& s):
64   ParaSurface<T,N>(), U(s.U), V(s.V), P(s.P), degU(s.degU), degV(s.degV)
65 {
66 }
67 
68 
69 /*!
70   \brief constructor with points in homogenous space
71 
72   \param DegU  the degree in the $u$ direction
73   \param DegV  the degree in the $v$ direction
74   \param   Uk  the $u$ knot vector
75   \param   Vk  the $v$ knot vector
76   \param   Cp  the matrix of control points in 4D
77 
78   \author Philippe Lavoie
79   \date 24 January, 1997
80 */
81 template <class T, int N>
NurbsSurface(int DegU,int DegV,const Vector<T> & Uk,const Vector<T> & Vk,const Matrix<HPoint_nD<T,N>> & Cp)82 NurbsSurface<T,N>::NurbsSurface(int DegU, int DegV, const Vector<T>& Uk, const Vector<T>& Vk, const Matrix< HPoint_nD<T,N> >& Cp) :
83   ParaSurface<T,N>(), U(Uk),V(Vk),P(Cp),degU(DegU),degV(DegV)
84 {
85   int bad = 0 ;
86 
87   if(U.n() != P.rows()+degU+1){
88 #ifdef USE_EXCEPTION
89     throw NurbsSizeError(P.rows(),U.n(),degU) ;
90 #else
91     Error err("NurbsSurface<T,N> constructor") ;
92     err << "The U knot vector and the number of rows of the control points are incompatible\n" ;
93     err << "P.rows() = " << P.rows() << ", U.n() = " << U.n() << endl ;
94     bad = 1 ;
95     err.fatal() ;
96 #endif
97   }
98   if(V.n() != P.cols()+degV+1){
99 #ifdef USE_EXCEPTION
100     throw NurbsSizeError(P.cols(),V.n(),degV);
101 #else
102     Error err("NurbsSurface<T,N> constructor") ;
103     err << "The V knot vector and the number of columns of the control points are incompatible\n" ;
104     err << "P.cols() = " << P.cols() << ", V.n() = " << V.n() << endl ;
105     bad = 1 ;
106     err.fatal() ;
107 #endif
108   }
109   if(bad)
110     exit(-1) ;
111 }
112 
113 /*!
114   \brief constructor with points in 3D
115 
116   \param DegU  the degree of the surface in the U direction
117   \param DegV  the degree of the surface in the V direction
118   \param Uk  the U knot vector
119   \param Vk  the V knot vector
120   \param Cp  the matrix of 3D control points
121   \param W  the weight value for each control points
122 
123   \author Philippe Lavoie
124   \date 24 January, 1997
125 */
126 template <class T, int N>
NurbsSurface(int DegU,int DegV,Vector<T> & Uk,Vector<T> & Vk,Matrix<Point_nD<T,N>> & Cp,Matrix<T> & W)127 NurbsSurface<T,N>::NurbsSurface(int DegU, int DegV, Vector<T>& Uk, Vector<T>& Vk, Matrix< Point_nD<T,N> >& Cp, Matrix<T>& W) :
128   ParaSurface<T,N>(), U(Uk),V(Vk),P(Cp.rows(),Cp.cols()),degU(DegU),degV(DegV)
129 {
130   int bad = 0 ;
131 
132   if(U.n() != Cp.rows()+degU+1){
133 #ifdef USE_EXCEPTION
134     throw NurbsSizeError(P.rows(),U.n(),degU);
135 #else
136     Error err("NurbsSurface<T,N> constructor") ;
137     err << "The U knot vector and the number of rows of the control points are incompatible\n" ;
138     err << "P.rows() = " << P.rows() << ", U.n() = " << U.n() << endl ;
139     bad = 1 ;
140     err.fatal() ;
141 #endif
142   }
143   if(V.n() != Cp.cols()+degV+1){
144 #ifdef USE_EXCEPTION
145     throw NurbsSizeError(P.cols(),V.n(),degV);
146 #else
147     Error err("NurbsSurface<T,N> constructor") ;
148     err << "The V knot vector and the number of columns of the control points are incompatible\n" ;
149     err << "P.cols() = " << P.cols() << ", V.n() = " << V.n() << endl ;
150     bad = 1 ;
151     err.fatal() ;
152 #endif
153   }
154   if(W.rows() != Cp.rows()){
155 #ifdef USE_EXCEPTION
156     throw NurbsInputError(W.rows(),Cp.rows()) ;
157 #else
158     Error err("NurbsSurface<T,N> constructor") ;
159     err << "The dimension of the weights are incompatible with the dimension of the control poitns\n" ;
160     err << "W( " << W.rows() << ", " << W.cols() << ") and P( " << P.rows() << ", " << P.cols() << ") \n" ;
161     bad = 1 ;
162     err.fatal() ;
163 #endif
164   }
165   if(W.cols() != Cp.cols()){
166 #ifdef USE_EXCEPTION
167     throw NurbsInputError(W.cols(),Cp.cols()) ;
168 #else
169     Error err("NurbsSurface<T,N> constructor") ;
170     err << "The dimension of the weights are incompatible with the dimension of the control poitns\n" ;
171     err << "W( " << W.rows() << ", " << W.cols() << ") and P( " << P.rows() << ", " << P.cols() << ") \n" ;
172     bad = 1 ;
173     err.fatal() ;
174 #endif
175   }
176 
177   for(int i=0;i<Cp.rows();i++)
178     if(bad)
179       break ;
180     else{
181       for(int j=0;j<Cp.cols();j++)
182 	if(W(i,j)==0.0){
183 #ifdef USE_EXCEPTION
184 	  throw NurbsInputError();
185 #else
186 	  Error err("NurbsSurface<T,N> constructor") ;
187 	  err << "Error initializing a NurbsSurface.\n" ;
188 	  err << "The weight W(" << i << ", " << j << ") is equal to 0.\n" ;
189 	  bad = 1 ;
190 	  err.fatal() ;
191 	  break ;
192 #endif
193 	}
194 	else {
195 	  P(i,j) = Cp(i,j) ;
196 	  P(i,j) *= W(i,j) ;
197 	}
198     }
199 
200   if(bad)
201     exit(-1) ;
202 }
203 
204 /*!
205   \brief NurbsSurface<T,N> assignment
206   \param nS the NURBS surface to copy
207 
208   \author Philippe Lavoie
209   \date 24 January, 1997
210 */
211 template <class T, int N>
operator =(const NurbsSurface<T,N> & nS)212 NurbsSurface<T,N>& NurbsSurface<T,N>::operator=(const NurbsSurface<T,N>& nS){
213   P = nS.P ;
214   U = nS.U ;
215   V = nS.V ;
216   degU = nS.degU ;
217   degV = nS.degV ;
218   return *this ;
219 }
220 
221 template <class T, int N>
222 struct AreaData {
223   T v;
224   T eps;
225   T knotUi;
226   T knotUii;
227   T knotVj;
228   T knotVjj;
229   const NurbsSurface<T,N>& s ;
230   const BasicArray<T>  w ;
AreaDataPLib::AreaData231   AreaData(const NurbsSurface<T,N>& surf,T e,
232            const BasicArray<T>& ww):
233     s(surf),v(T(0)),eps(e),w(ww),
234     knotUi(T(0)), knotUii(T(1)) {}
235 };
236 
237 template <class T, int N>
238 struct OpAreaAuxFcn : public ClassPOvoid<T> {
operator ()PLib::OpAreaAuxFcn239   T operator()(T u, void* data){
240     AreaData<T,N>* p = (AreaData<T,N>*)data ;
241     return (p->s).areaF(u,p->v) ;
242   }
243 };
244 
245 template <class T, int N>
246 struct OpAreaFcn : public ClassPOvoid<T> {
operator ()PLib::OpAreaFcn247   T operator()(T v, void* data){
248     static Vector<T> w;
249     T err;
250     OpAreaAuxFcn<T,N>  f;
251     AreaData<T,N>* Data = (AreaData<T,N>*)data ;
252     Data->v = v ;
253     return intcc2((ClassPOvoid<T>*)&f,Data,
254 		  Data->knotUi,Data->knotUii,
255 		  Data->eps,Data->w,err);
256   }
257 };
258 
259 /*!
260   \brief Computes the area of the surface
261 
262   Computes an approximation of the area of the surface
263   using a numerical automatic integrator.
264 
265   That integrator uses a Chebyshev Series Expansion
266   to perform its approximation. This is why you can
267   change the value \a n which sets the number of
268   elements in the series.
269 
270   The method is simple, integrate between each span.
271   This is necessary in case the tangant of a point
272   at u_i is undefined. Add the result and return
273   this as the approximation.
274 
275   \param eps  the accepted relative error
276   \param n  the number of element in the Chebyshev series
277 
278   \return the area of the NURBS surface.
279 
280   \author Alejandro Frangi
281   \date 20 January 1999
282 */
283 template <class T, int N>
284 T NurbsSurface<T,N>::area(T eps,int n) const {
285   T a = T(0.0) ;
286   T err ;
287   static Vector<T> bufFcn(0) ;
288 
289   if(bufFcn.n() != n){
290     bufFcn.resize(n) ;
291     intccini(bufFcn) ;
292   }
293 
294   AreaData<T,N> data(*this,eps,bufFcn) ;
295   OpAreaFcn<T,N> op;
296 
297 
298   for(int i=degU;i<P.rows();++i){
299     if(U[i] >= U[i+1] || U[i]>=T(1.0))
300       continue ;
301     data.knotUi  = U[i] ;
302     data.knotUii = U[i+1] ;
303     for(int j=degV;j<P.cols();++j){
304       if(V[j] >= V[j+1] || V[j]>=T(1.0))
305         continue ;
306       data.knotVj  = V[j] ;
307       data.knotVjj = V[j+1] ;
308       a += intcc2((ClassPOvoid<T>*)&op,(void*)&data,
309 		  data.knotVj,data.knotVjj,
310 		  eps,bufFcn,err) ;
311     }
312   }
313   return a ;
314 }
315 
316 /*!
317   \brief Computes the area of the surface inside [u_s,u_e]
318 
319   Computes an approximation of the area of the surface
320   using a numerical automatic integrator. The area
321   is computed for the range [u_s,u_e]
322 
323   That integrator uses a Chebyshev Series Expansion
324   to perform its approximation. This is why you can
325   change the value \a n which sets the number of
326   elements in the series.
327 
328   The method is similar to the one used by area
329   excepted that it needs to check for the range.
330 
331   \param  us  the starting range
332   \param  ue  the end of the range
333   \param  vs  the starting range
334   \param  ve  the end of the range
335   \param  n  the number of element in the Chebyshev series
336   \param  eps  the accepted relative error
337 
338   \return the area of the NURBS surface.
339 
340   \warning ue (ve) must be greater than us (vs) and both must be in a valid range.
341 
342   \author Alejandro  Frangi
343   \date   20 January 1999
344 */
345 template <class T, int N>
346 T NurbsSurface<T,N>::areaIn(T us, T ue, T vs, T ve, T eps, int n) const {
347   T l = T() ;
348   T err ;
349   T a ;
350   bool bLastU = false;
351   bool bLastV = false;
352   static Vector<T> bufFcn ;
353 
354   if(bufFcn.n() != n){
355     bufFcn.resize(n) ;
356     intccini(bufFcn) ;
357   }
358 
359   AreaData<T,N> data(*this,eps,bufFcn) ;
360   OpAreaFcn<T,N> op;
361   for(int i=degU;i<P.rows();++i){
362     if(U[i] >= U[i+1] || U[i] >= T(1))
363       continue;
364     if(i<findSpanU(us))
365       continue ;
366     if(us>=U[i] && ue<=U[i+findMultU(i)]){
367       data.knotUi  = us;
368       data.knotUii = ue;
369       bLastU = true;
370       goto Integrate_I;
371     }
372     if(us>=U[i]){
373       data.knotUi  = us;
374       data.knotUii = U[i+findMultU(i)];
375       bLastU = false;
376       goto Integrate_I;
377     }
378     if(ue<=U[i+1]){
379       data.knotUi  = U[i];
380       data.knotUii = ue;
381       bLastU = true;
382       goto Integrate_I;
383     }
384     data.knotUi  = U[i] ;
385     data.knotUii = U[i+findMultU(i)] ;
386   Integrate_I:
387     for(int j=degV;j<P.cols();++j){
388       if(V[j] >= V[j+1] || V[j]>=T(1))
389         continue ;
390       if(j<findSpanV(vs))
391         continue ;
392       if(vs>=V[j] && ve<=V[j+findMultV(j)]){
393         data.knotVj  = vs;
394         data.knotVjj = ve;
395         bLastV = true;
396         goto Integrate_II;
397       }
398       if(vs>=V[j]){
399         data.knotVj  = vs;
400         data.knotVjj = V[j+findMultV(j)];
401         bLastV = false;
402         goto Integrate_II;
403       }
404       if(ve<=V[j+1]){
405         data.knotVj  = V[j];
406         data.knotVjj = ve;
407         bLastV = true;
408         goto Integrate_II;
409       }
410       data.knotVj  = V[j] ;
411       data.knotVjj = V[j+findMultV(j)] ;
412     Integrate_II:
413       a += intcc2((ClassPOvoid<T>*)&op,(void*)&data,data.knotVj,data.knotVjj,eps,bufFcn,err) ;
414       if (bLastU && bLastV)
415         return a;
416       if (bLastV)
417         break;
418     }
419   }
420   return a ;
421 }
422 
423 // the definitions are in f_nurbs.cpp and d_nurbs.cpp
424 
425 
426 /*!
427 
428   \a area needs to integrate a function over an interval
429   to determine the area of the NURBS surface.
430   Well, this is the function.
431 
432   \param u  the parameter
433   \param v  the parameter
434 
435   \return the elemental area at (u,v)
436 
437   \author Alejandro Frangi
438   \date   20 January 1999
439 */
440 template <class T, int N>
441   T NurbsSurface<T,N>::areaF(T u, T v) const {
442 
443   Matrix<Point_nD<T,N> > Skl(2,2) ;
444   deriveAt(u,v,1,Skl);
445   T tmp = norm(crossProduct(Skl(1,0),Skl(0,1)));
446   return tmp ;
447 }
448 
449 /*!
450   \brief Determines if the surface is valid
451 
452   Determines if the surface is valid. The routine only verifies
453   if the number of control points in the U and V direction
454   matches the area of the U and V knot vectors.
455 
456   \return 1 if the surface is valid, 0 otherwise
457 
458   \author Philippe Lavoie
459   \date 24 January, 1997
460 */
461 template <class T, int N>
ok()462 int NurbsSurface<T,N>::ok() {
463   if(P.rows() <= degU)
464     return 0 ;
465   if(P.cols() <= degV)
466     return 0 ;
467   if(P.rows() != U.n()+degU+1)
468     return 0 ;
469   if(P.cols() != V.n()+degV+1)
470     return 0 ;
471   return 1 ;
472 }
473 
474 /*!
475   \brief Resize the surface
476 
477   Resize the surface. Proper values must be assigned once this
478   function has been called since the resize operator is
479   destructive.
480 
481   \param  Pu  the number of control points in the U direction
482   \param  Pv  the number of control points in the V direction
483   \param  DegU  the degree of the surface in the U direction
484   \param  DegV  the degree of the surface in the V direction
485 
486   \author Philippe Lavoie
487   \date 24 January, 1997
488 */
489 template <class T, int N>
resize(int Pu,int Pv,int DegU,int DegV)490 void NurbsSurface<T,N>::resize(int Pu, int Pv, int DegU, int DegV){
491   P.resize(Pu,Pv) ;
492   degU = DegU ;
493   degV = DegV ;
494   U.resize(Pu+DegU+1) ;
495   V.resize(Pv+DegV+1) ;
496 }
497 
498 /*!
499   \brief Resize the surface while keeping the old values.
500 
501   \param  Pu  the number of control points in the U direction
502   \param  Pv  the number of control points in the V direction
503   \param  DegU  the degree of the surface in the U direction
504   \param  DegV  the degree of the surface in the V direction
505 
506   \author Philippe Lavoie
507   \date 24 January, 1997
508 */
509 template <class T, int N>
resizeKeep(int Pu,int Pv,int DegU,int DegV)510 void NurbsSurface<T,N>::resizeKeep(int Pu, int Pv, int DegU, int DegV){
511   P.resizeKeep(Pu,Pv) ;
512   degU = DegU ;
513   degV = DegV ;
514   U.resize(Pu+DegU+1) ;
515   V.resize(Pv+DegV+1) ;
516 }
517 
518 /*!
519   \brief Generates a NURBS surface from  skinning
520 
521   The NURBS surface is generated from skinning. The skinning is performed
522   in the V direction.
523 
524   \param   ca  an array of NURBS curves
525   \param  degV  the degree to skin in the V direction
526   \param  surf  the skinned surface
527 
528   \return 0 if an error occurs, 1 otherwise
529 
530   \warning The number of curves to skin from must be greater than degV
531 
532   \author Philippe Lavoie
533   \date 24 January, 1997
534 */
535 template <class T, int D>
skinV(NurbsCurveArray<T,D> & ca,int dV)536 int NurbsSurface<T,D>::skinV(NurbsCurveArray<T,D>& ca, int dV) {
537   Vector<T> vk(ca.n()) ;
538   //Vector<T> d(ca.n()) ;
539   T* d ;
540   int i,k,N,K ;
541 
542   if(ca.n()<dV){
543 #ifdef USE_EXCEPTION
544     throw NurbsInputError();
545 #else
546     Error err("NurbsSurface<T,D> skinV") ;
547     err << "The number of curves are insufficient for the degree of interpolation specified.\n" ;
548     err << "n of curves = " << ca.n() << ", degree of interpolation = " << dV << endl ;
549     err.warning() ;
550     return 0 ;
551 #endif
552   }
553 
554   generateCompatibleCurves(ca) ;
555 
556   K = ca.n() ;
557   N = ca[0].ctrlPnts().n() ;
558 
559   resize(N,K,ca[0].degree(),dV) ;
560 
561   //d.resize(ca[0].ctrlPnts().n()) ;
562   d = new T[ca[0].ctrlPnts().n()] ;
563   for(i=0;i<N;i++){
564     d[i] = 0 ;
565     for(k=1;k<K;k++){
566       d[i] += norm(ca[k].ctrlPnts(i)-ca[k-1].ctrlPnts(i)) ;
567     }
568   }
569 
570   vk[0] = 0 ;
571   for(k=1;k<K;k++){
572     vk[k] = 0 ;
573     for(i=0;i<N;i++)
574       vk[k] += norm(ca[k].ctrlPnts(i)-ca[k-1].ctrlPnts(i))/d[i] ;
575     vk[k] /= (T)N ;//+ 1.0 ;
576     vk[k] += vk[k-1] ;
577   }
578   vk[vk.n()-1] = 1.0 ;
579 
580   for(i=1;i<K-degV;i++){
581     V[i+degV] = 0 ;
582     for(k=i;k<i+degV;k++)
583       V[i+degV] += vk[k] ;
584     V[i+degV] /= (T)degV ;
585   }
586   for(i=0;i<=degV;i++) V[i] = 0.0 ;
587   for(i=V.n()-degV-1;i<V.n();i++) V[i] = 1.0 ;
588 
589 
590   Vector< HPoint_nD<T,D> > Q(K) ;
591   NurbsCurve<T,D> i_curve ;
592 
593 
594   for(i=0;i<N;i++){
595     for(k=0;k<K;k++)
596       Q[k] = ca[k].ctrlPnts(i) ;
597     i_curve.globalInterpH(Q,vk,V,degV);
598     for(k=0;k<K;k++)
599       P(i,k) = i_curve.ctrlPnts(k) ;
600   }
601 
602   U = ca[0].knot() ;
603 
604   delete []d ;
605   return 1 ;
606 }
607 
608 /*!
609   \brief Generates a NURBS surface from  skinning
610 
611   The NURBS surface is generates from skinning.
612   The skinning is performed in the U direction.
613 
614   \param    ca  an array of NURBS curves
615   \param   degU  the degree to skin in the Udirection
616 
617   \return 0 if an error occurs, 1 otherwise
618 
619   \warning The number of curves to skin from must be greater than degU
620 
621   \author Philippe Lavoie
622   \date 24 January, 1997
623 */
624 template <class T, int nD>
skinU(NurbsCurveArray<T,nD> & ca,int dU)625 int NurbsSurface<T,nD>::skinU(NurbsCurveArray<T,nD>& ca, int dU) {
626   Vector<T> uk(ca.n());
627   //Vector<T> d(ca.n()) ;
628   T* d;
629   int i,k,N,K ;
630 
631   if(ca.n()<dU){
632 #ifdef USE_EXCEPTION
633     throw NurbsInputError();
634 #else
635     Error err("NurbsSurface<T,N> skinU") ;
636     err << "The number of curves are insufficient for the degree of interpolation specified.\n" ;
637     err << "n of curves = " << ca.n() << ", degree of interpolation = " << dU << endl ;
638     err.warning() ;
639     return 0 ;
640 #endif
641   }
642 
643   generateCompatibleCurves(ca) ;
644 
645   K = ca.n() ;
646   N = ca[0].ctrlPnts().n() ;
647 
648   resize(K,N,dU,ca[0].degree()) ;
649 
650   //d.resize(ca[0].ctrlPnts().n()) ;
651   d = new T[ca[0].ctrlPnts().n()] ;
652   for(i=0;i<N;i++){
653     d[i] = 0 ;
654     for(k=1;k<K;k++){
655       d[i] += norm(ca[k].ctrlPnts(i)-ca[k-1].ctrlPnts(i)) ;
656     }
657   }
658 
659   uk[0] = 0 ;
660   for(k=1;k<K;k++){
661     uk[k] = 0 ;
662     for(i=0;i<N;i++)
663       uk[k] += norm(ca[k].ctrlPnts(i)-ca[k-1].ctrlPnts(i))/d[i] ;
664     uk[k] /= (T)N ;//+ 1.0 ;
665     uk[k] += uk[k-1] ;
666   }
667   uk[uk.n()-1] = 1.0 ;
668 
669   for(i=1;i<K-degU;i++){
670     U[i+degU] = 0 ;
671     for(k=i;k<i+degU;k++)
672       U[i+degU] += uk[k] ;
673     U[i+degU] /= (T)degU ;
674   }
675   for(i=0;i<=degU;i++) U[i] = 0.0 ;
676   for(i=U.n()-degU-1;i<U.n();i++) U[i] = 1.0 ;
677 
678 
679   Vector< HPoint_nD<T,nD> > Q(K) ;
680   NurbsCurve<T,nD> i_curve ;
681 
682 
683   for(i=0;i<N;i++){
684     for(k=0;k<K;k++)
685       Q[k] = ca[k].ctrlPnts(i) ;
686     i_curve.globalInterpH(Q,uk,U,degU) ;
687     for(k=0;k<K;k++)
688       P(k,i) = i_curve.ctrlPnts(k) ;
689   }
690 
691   V = ca[0].knot() ;
692 
693   delete []d ;
694 
695   return 1 ;
696 }
697 
698 /*!
699   \relates NurbsSurface
700   \brief Computes the parameters for global surface interpolation
701 
702   Computes the parameters for global surface interpolation.
703   For more information, see A9.3 on p377 on the NURBS book.
704 
705   \param Q   the matrix of 3D points
706   \param uk  the knot coefficients in the U direction
707   \param vl  the knot coefficients in the V direction
708 
709   \return 0 if an error occurs, 1 otherwise
710 
711   \warning
712 
713   \author Philippe Lavoie
714   \date 24 January, 1997
715 */
716 template <class T, int N>
surfMeshParams(const Matrix<Point_nD<T,N>> & Q,Vector<T> & uk,Vector<T> & vl)717 int surfMeshParams(const Matrix< Point_nD<T,N> >& Q, Vector<T>& uk, Vector<T>& vl){
718   int n,m,k,l,num ;
719   double d,total ;
720   //Vector<T> cds(Q.rows()) ;
721   T* cds = new T[maximum(Q.rows(),Q.cols())] ; // alloca might not have enough space
722 
723   n = Q.rows() ;
724   m = Q.cols() ;
725   uk.resize(n) ;
726   vl.resize(m) ;
727   num = m ;
728 
729 
730   // Compute the uk
731   uk.reset(0) ;
732 
733   for(l=0;l<m;l++){
734     total = 0.0 ;
735     for(k=1;k<n;k++){
736       cds[k] = norm(Q(k,l)-Q(k-1,l)) ;
737       total += cds[k] ;
738     }
739     if(total==0.0)
740       num-- ;
741     else {
742       d = 0.0 ;
743       for(k=1;k<n;k++){
744 	d += cds[k] ;
745 	uk[k] += d/total ;
746       }
747     }
748   }
749 
750   if(num==0) {
751     delete []cds ;
752     return 0 ;
753   }
754   for(k=1;k<n-1;k++)
755     uk[k] /= num ;
756   uk[n-1] = 1.0 ;
757 
758   // Compute the vl
759   vl.reset(0) ;
760 
761   //cds.resize(m) ; // this line removed since the maximum is allocated at the beginning
762 
763   num = n ;
764 
765   for(k=0;k<n;k++){
766     total = 0.0 ;
767     for(l=1;l<m;l++){
768       cds[l] = norm(Q(k,l)-Q(k,l-1)) ;
769       total += cds[l] ;
770     }
771     if(total==0.0)
772       num-- ;
773     else {
774       d = 0.0 ;
775       for(l=1;l<m;l++){
776 	d += cds[l] ;
777 	vl[l] += d/total ;
778       }
779     }
780   }
781 
782   delete []cds ;
783 
784   if(num==0)
785     return 0 ;
786   for(l=1;l<m-1;l++)
787     vl[l] /= num ;
788   vl[m-1] = 1.0 ;
789 
790 
791   return 1 ;
792 }
793 
794 /*!
795   \relates NurbsSurface
796   \brief Computes the parameters for global surface interpolation
797 
798   Computes the parameters for global surface interpolation.
799   For more information, see A9.3 on p377 on the NURBS book.
800 
801   \param Q   the matrix of 3D points
802   \param uk  the knot coefficients in the U direction
803   \param vl  the knot coefficients in the V direction
804 
805   \return 0 if an error occurs, 1 otherwise
806 
807   \author Philippe Lavoie
808   \date 24 January, 1997
809 */
810 template <class T, int N>
surfMeshParamsH(const Matrix<HPoint_nD<T,N>> & Q,Vector<T> & uk,Vector<T> & vl)811 int surfMeshParamsH(const Matrix< HPoint_nD<T,N> >& Q, Vector<T>& uk, Vector<T>& vl){
812   int n,m,k,l,num ;
813   double d,total ;
814   //Vector<T> cds(Q.rows()) ;
815   T* cds = new T[maximum(Q.rows(),Q.cols())] ;
816 
817   n = Q.rows() ;
818   m = Q.cols() ;
819   uk.resize(n) ;
820   vl.resize(m) ;
821   num = m ;
822 
823 
824   // Compute the uk
825   uk.reset(0) ;
826 
827   for(l=0;l<m;l++){
828     total = 0.0 ;
829     for(k=1;k<n;k++){
830       cds[k] = norm(Q(k,l)-Q(k-1,l)) ;
831       total += cds[k] ;
832     }
833     if(total==0.0)
834       num-- ;
835     else {
836       d = 0.0 ;
837       for(k=1;k<n;k++){
838 	d += cds[k] ;
839 	uk[k] += d/total ;
840       }
841     }
842   }
843 
844   if(num==0) {
845     delete []cds ;
846     return 0 ;
847   }
848   for(k=1;k<n-1;k++)
849     uk[k] /= num ;
850   uk[n-1] = 1.0 ;
851 
852   // Compute the vl
853   vl.reset(0) ;
854   //cds.resize(m) ; // taking the maximum so this is not needed
855 
856   num = n ;
857 
858   for(k=0;k<n;k++){
859     total = 0.0 ;
860     for(l=1;l<m;l++){
861       cds[l] = norm(Q(k,l)-Q(k,l-1)) ;
862       total += cds[l] ;
863     }
864     if(total==0.0)
865       num-- ;
866     else {
867       d = 0.0 ;
868       for(l=1;l<m;l++){
869 	d += cds[l] ;
870 	vl[l] += d/total ;
871       }
872     }
873   }
874 
875   delete []cds ;
876 
877   if(num==0)
878     return 0 ;
879   for(l=1;l<m-1;l++)
880     vl[l] /= num ;
881   vl[m-1] = 1.0 ;
882   return 1 ;
883 }
884 
885 /*!
886   \brief Generates a surface using global interpolation
887 
888   \param    Q  a matrix of 3D points
889   \param pU  the degree of interpolation in the U direction
890   \param pV  the degree of interpolation in the V direction
891 
892   \author Philippe Lavoie
893   \date 24 January, 1997
894 */
895 template <class T, int N>
globalInterp(const Matrix<Point_nD<T,N>> & Q,int pU,int pV)896 void NurbsSurface<T,N>::globalInterp(const Matrix< Point_nD<T,N> >& Q, int pU, int pV){
897   Vector<T> vk,uk ;
898 
899   resize(Q.rows(),Q.cols(),pU,pV) ;
900 
901   surfMeshParams(Q,uk,vk) ;
902   knotAveraging(uk,pU,U) ;
903   knotAveraging(vk,pV,V) ;
904 
905 
906   Vector< HPoint_nD<T,N> > Pts(Q.rows()) ;
907   NurbsCurve<T,N> R ;
908 
909   int i,j ;
910 
911   for(j=0;j<Q.cols();j++){
912     for(i=0;i<Q.rows();i++)
913       Pts[i] = Q(i,j) ;
914     R.globalInterpH(Pts,uk,U,pU);
915     for(i=0;i<Q.rows();i++)
916       P(i,j) = R.ctrlPnts(i) ;
917   }
918 
919   Pts.resize(Q.cols()) ;
920   for(i=0;i<Q.rows();i++){
921     for(j=0;j<Q.cols();j++)
922       Pts[j] = P(i,j) ;
923     R.globalInterpH(Pts,vk,V,pV) ;
924     for(j=0;j<Q.cols();j++)
925       P(i,j) = R.ctrlPnts(j) ;
926   }
927 }
928 
929 /*!
930   \brief Generates a surface using global interpolation with homogenous points
931 
932   \param  Q  a matrix of 4D points
933   \param pU  the degree of interpolation in the U direction
934   \param pV  the degree of interpolation in the V direction
935 
936   \author Philippe Lavoie
937   \date 24 January, 1997
938 */
939 template <class T, int N>
globalInterpH(const Matrix<HPoint_nD<T,N>> & Q,int pU,int pV)940 void NurbsSurface<T,N>::globalInterpH(const Matrix< HPoint_nD<T,N> >& Q, int pU, int pV){
941   Vector<T> vk,uk ;
942 
943   resize(Q.rows(),Q.cols(),pU,pV) ;
944 
945   surfMeshParamsH(Q,uk,vk) ;
946   knotAveraging(uk,pU,U) ;
947   knotAveraging(vk,pV,V) ;
948 
949 
950   Vector< HPoint_nD<T,N> > Pts(Q.rows()) ;
951   NurbsCurve<T,N> R ;
952 
953   int i,j ;
954 
955   for(j=0;j<Q.cols();j++){
956     for(i=0;i<Q.rows();i++)
957       Pts[i] = Q(i,j) ;
958     R.globalInterpH(Pts,uk,U,pU);
959     for(i=0;i<Q.rows();i++)
960       P(i,j) = R.ctrlPnts(i) ;
961   }
962 
963   Pts.resize(Q.cols()) ;
964   for(i=0;i<Q.rows();i++){
965     for(j=0;j<Q.cols();j++)
966       Pts[j] = P(i,j) ;
967     R.globalInterpH(Pts,vk,V,pV) ;
968     for(j=0;j<Q.cols();j++)
969       P(i,j) = R.ctrlPnts(j) ;
970   }
971 }
972 
973 /*!
974   \brief generates a surface using global least squares approximation.
975 
976   \param   Q  a matrix of 3D points
977   \param  pU  the degree of interpolation in the U direction
978   \param  pV  the degree of interpolation in the V direction
979   \param  nU  the number of points in the U direction
980   \param  nV  the number of poitns in the V direction
981 
982   \author Philippe Lavoie
983   \date 24 January, 1997
984 */
985 template <class T, int N>
leastSquares(const Matrix<Point_nD<T,N>> & Q,int pU,int pV,int nU,int nV)986 void NurbsSurface<T,N>::leastSquares(const Matrix< Point_nD<T,N> >& Q, int pU, int pV, int nU, int nV){
987   Vector<T> vk,uk ;
988 
989   resize(nU,nV,pU,pV) ;
990 
991   surfMeshParams(Q,uk,vk) ;
992 
993   Vector< HPoint_nD<T,N> > Pts(Q.rows()) ;
994   NurbsCurve<T,N> R ;
995 
996   int i,j ;
997 
998   Matrix< HPoint_nD<T,N> > P2 ;
999 
1000   P2.resize(nU,Q.cols()) ;
1001 
1002   for(j=0;j<Q.cols();j++){
1003     for(i=0;i<Q.rows();i++)
1004       Pts[i] = Q(i,j) ;
1005     R.leastSquaresH(Pts,pU,nU,uk);
1006     for(i=0;i<P.rows();i++)
1007       P2(i,j) = R.ctrlPnts(i) ;
1008     if(j==0)
1009       U = R.knot() ;
1010   }
1011 
1012   Pts.resize(Q.cols()) ;
1013   for(i=0;i<P.rows();i++){
1014     for(j=0;j<Q.cols();j++)
1015       Pts[j] = P2(i,j) ;
1016     R.leastSquaresH(Pts,pV,nV,vk) ;
1017     for(j=0;j<P.cols();j++)
1018       P(i,j) = R.ctrlPnts(j) ;
1019     if(i==0)
1020       V = R.knot() ;
1021   }
1022 }
1023 
1024 /*!
1025   \relates NurbsSurface
1026   \brief Generates a surface using global interpolation
1027 
1028 
1029   Generates a NURBS surface using global interpolation.
1030   The data points are assumed to be part of grided points
1031   in x-y. \e i.e. the original data set as points covering
1032   the xy plane at regular \a x and \a y intervals with only the \a z
1033   being a free variable
1034 
1035   \param    Q a matrix of 3D points
1036   \param  pU  the degree of interpolation in the U direction
1037   \param pV  the degree of interpolation in the V direction
1038   \param S  the interpolated surface
1039 
1040   \warning Q(0,0) in x-y should be the smallest corner and
1041                Q(Q.rows()-1,Q.cols()-1) the biggest.
1042 
1043   \author Philippe Lavoie
1044   \date 24 January, 1997
1045 */
1046 template <class T, int N>
globalSurfInterpXY(const Matrix<Point_nD<T,N>> & Q,int pU,int pV,NurbsSurface<T,N> & S)1047 void globalSurfInterpXY(const Matrix< Point_nD<T,N> >& Q, int pU, int pV, NurbsSurface<T,N>& S) {
1048   Vector<T> uk,vk ;
1049   T um,uM ;
1050   T vm,vM ;
1051 
1052   um = Q(0,0).y() ;
1053   vm = Q(0,0).x() ;
1054   uM = Q(Q.rows()-1,0).y() ;
1055   vM = Q(0,Q.cols()-1).x() ;
1056 
1057   uk.resize(Q.rows()) ;
1058   vk.resize(Q.cols()) ;
1059 
1060   uk[0] = 0.0 ;
1061   vk[0] = 0.0 ;
1062   uk[uk.n()-1] = 1.0 ;
1063   vk[vk.n()-1] = 1.0 ;
1064 
1065   T dU = uM-um ;
1066   T dV = vM-vm ;
1067 
1068   int i ;
1069   for(i=1;i<uk.n()-1;++i){
1070     uk[i] = Q(i,0).y()/dU ;
1071   }
1072 
1073   for(i=1;i<vk.n()-1;++i){
1074     vk[i] = Q(0,i).x()/dV ;
1075   }
1076 
1077   globalSurfInterpXY(Q,pU,pV,S,uk,vk) ;
1078 }
1079 
1080 /*!
1081   \relates NurbsSurface
1082   \brief generates a surface using global interpolation
1083 
1084   Generates a NURBS surface using global interpolation.
1085   The data points are assumed to be part of grided points
1086   in x-y. i.e. the original data set as points covering
1087   the xy plane at regular \a x and \a y intervals with only the \a z
1088   being a free variable
1089 
1090   \param  Q  a matrix of 3D points
1091   \param pU  the degree of interpolation in the U direction
1092   \param pV  the degree of interpolation in the V direction
1093   \param  S  the interpolated surface
1094 
1095   \warning  Q(0,0) in x-y should be the smallest corner and
1096                 Q(Q.rows()-1,Q.cols()-1) the biggest.
1097 
1098   \author    Philippe Lavoie
1099   \date 24 January, 1997
1100 */
1101 template <class T, int N>
globalSurfInterpXY(const Matrix<Point_nD<T,N>> & Q,int pU,int pV,NurbsSurface<T,N> & S,const Vector<T> & uk,const Vector<T> & vk)1102 void globalSurfInterpXY(const Matrix< Point_nD<T,N> >& Q, int pU, int pV, NurbsSurface<T,N>& S, const Vector<T>& uk, const Vector<T>& vk){
1103   Vector<T> V,U ;
1104   int i,j ;
1105 
1106 
1107   knotAveraging(uk,pU,U) ;
1108   knotAveraging(vk,pV,V) ;
1109 
1110   Vector< HPoint_nD<T,N> > P(Q.rows()) ;
1111   NurbsCurve<T,N> R ;
1112 
1113   S.resize(Q.rows(),Q.cols(),pU,pV) ;
1114   S.U = U ;
1115   S.V = V ;
1116 
1117   for(j=0;j<Q.cols();j++){
1118     for(i=0;i<Q.rows();i++)
1119       P[i] = Q(i,j) ;
1120     R.globalInterpH(P,uk,U,pU) ;
1121     for(i=0;i<Q.rows();i++)
1122       S.P(i,j) = R.ctrlPnts(i) ;
1123   }
1124 
1125   P.resize(Q.cols()) ;
1126   for(i=0;i<Q.rows();i++){
1127     for(j=0;j<Q.cols();j++)
1128       P[j] = S.P(i,j) ;
1129     R.globalInterpH(P,vk,V,pV) ;
1130     for(j=0;j<Q.cols();j++)
1131       S.P(i,j) = R.ctrlPnts(j) ;
1132   }
1133 }
1134 
1135 
1136 /*!
1137   \relates NurbsSurface
1138   \brief Generates a surface using global approximation
1139 
1140   \param  Q  a matrix of 3D points
1141   \param pU  the degree of interpolation in the U direction
1142   \param pV  the degree of interpolation in the V direction
1143   \param  S  the interpolated surface
1144 
1145   \warning This routine is still in a research phase
1146 
1147   \author Philippe Lavoie
1148   \date 24 January, 1997
1149 */
1150 template <class T, int N>
globalSurfApprox(const Matrix<Point_nD<T,N>> & Q,int pU,int pV,NurbsSurface<T,N> & S,double error)1151 void globalSurfApprox(const Matrix< Point_nD<T,N> >& Q, int pU, int pV, NurbsSurface<T,N>& S, double error){
1152 
1153   NurbsCurveArray<T,N> R ;
1154   Vector< Point_nD<T,N> > P ;
1155 
1156   Matrix< HPoint_nD<T,N> > St ;
1157   Vector<T> Ut,Vt ;
1158 
1159   Vector<T> vk,uk ;
1160 
1161   surfMeshParams(Q,uk,vk) ;
1162 
1163   R.resize(Q.cols()) ;
1164   P.resize(Q.rows()) ;
1165 
1166   int i,j ;
1167 
1168   for(j=0;j<Q.cols();j++){
1169     for(i=0;i<Q.rows();i++)
1170       P[i] = Q(i,j) ;
1171     R[j].globalApproxErrBnd(P,uk,pU,error) ;
1172   }
1173 
1174 
1175   generateCompatibleCurves(R) ;
1176 
1177   Ut.resize(R[0].knot().n()) ;
1178   Ut = R[0].knot() ;
1179 
1180   St.resize(R[0].ctrlPnts().n(),R.n()) ;
1181 
1182   for(i=0;i<R[0].ctrlPnts().n();i++){
1183     for(j=0;j<R.n();j++)
1184       St(i,j) = R[j].ctrlPnts(i) ;
1185   }
1186 
1187   P.resize(St.cols()) ;
1188   R.resize(St.rows()) ;
1189   for(i=0;i<St.rows();i++){
1190     for(j=0;j<St.cols();j++)
1191       P[j] = project(St(i,j)) ;
1192     R[i].globalApproxErrBnd(P,vk,pV,error) ;
1193   }
1194 
1195   generateCompatibleCurves(R) ;
1196 
1197   Vt.resize(R[0].knot().n()) ;
1198   Vt = R[0].knot() ;
1199 
1200   S.resize(St.rows(),R[0].ctrlPnts().n(),pU,pV) ;
1201   for(i=0;i<S.ctrlPnts().rows();i++)
1202     for(j=0;j<S.ctrlPnts().cols();j++){
1203       S.P(i,j) = R[i].ctrlPnts(j) ;
1204     }
1205   S.U = Ut ;
1206   S.V = Vt ;
1207 }
1208 
1209 
1210 /*!
1211   \brief Degree elevate the surface in the U and V direction
1212 
1213   \param tU  elevate the degree of the surface in the u direction
1214 	              by this amount.
1215   \param tV  elevate the degree of the surface in the v direction
1216 	              by this amount.
1217 
1218   \author Philippe Lavoie
1219   \date 24 January, 1997
1220 */
1221 template <class T, int N>
degreeElevate(int tU,int tV)1222 void NurbsSurface<T,N>::degreeElevate(int tU, int tV) {
1223   degreeElevateU(tU) ;
1224   degreeElevateV(tV) ;
1225 }
1226 
1227 /*!
1228   \brief Degree elevate the surface in the U direction
1229 
1230   \param t elevate the degree in the u direction by this amount.
1231 
1232   \author Philippe Lavoie
1233   \date 24 January, 1997
1234 */
1235 template <class T, int N>
degreeElevateU(int t)1236 void NurbsSurface<T,N>::degreeElevateU(int t) {
1237   if(t<=0){
1238     return ;
1239   }
1240 
1241   NurbsSurface<T,N> S(*this) ;
1242 
1243   int i,j,k ;
1244   int n = S.ctrlPnts().rows()-1;
1245   int p = S.degU ;
1246   int m = n+p+1;
1247   int ph = p+t ;
1248   int ph2 = ph/2 ;
1249   Matrix<T> bezalfs(p+t+1,p+1) ; // coefficients for degree elevating the Bezier segment
1250   Matrix< HPoint_nD<T,N> > bpts(p+1,S.P.cols()) ; // pth-degree Bezier control points of the current segment
1251   Matrix< HPoint_nD<T,N> > ebpts(p+t+1,S.P.cols()) ; // (p+t)th-degree Bezier control points of the  current segment
1252   Matrix< HPoint_nD<T,N> > Nextbpts(p-1,S.P.cols()) ; // leftmost control points of the next Bezier segment
1253   Vector<T> alphas(p-1) ; // knot instertion alphas.
1254 
1255   // Compute the Binomial coefficients
1256   Matrix<T> Bin(ph+1,ph2+1) ;
1257   binomialCoef(Bin) ;
1258 
1259   // Compute Bezier degree elevation coefficients
1260   T inv,mpi ;
1261   bezalfs(0,0) = bezalfs(ph,p) = 1.0 ;
1262   for(i=1;i<=ph2;i++){
1263     inv= 1.0/Bin(ph,i) ;
1264     mpi = minimum(p,i) ;
1265     for(j=maximum(0,i-t); j<=mpi; j++){
1266       bezalfs(i,j) = inv*Bin(p,j)*Bin(t,i-j) ;
1267     }
1268   }
1269 
1270   for(i=ph2+1;i<ph ; i++){
1271     mpi = minimum(p,i) ;
1272     for(j=maximum(0,i-t); j<=mpi ; j++)
1273       bezalfs(i,j) = bezalfs(ph-i,p-j) ;
1274   }
1275 
1276   // Allocate more control points than necessary
1277   resize(S.P.rows()+S.P.rows()*t,S.P.cols(),ph,S.degV) ;
1278 
1279   int colJ ;
1280   int mh = ph ;
1281   int kind = ph+1 ;
1282   T ua = S.U[0] ;
1283   T ub = 0.0 ;
1284   int r=-1 ;
1285   int oldr ;
1286   int a = p ;
1287   int b = p+1 ;
1288   int cind = 1 ;
1289   int rbz,lbz = 1 ;
1290   int mul,save,s;
1291   T alf ;
1292   int first, last, kj ;
1293   T den,bet,gam,numer ;
1294 
1295   for(i=0;i<S.P.cols();i++)
1296     P(0,i) = S.P(0,i) ;
1297   for(i=0; i <= ph ; i++){
1298     U[i] = ua ;
1299   }
1300 
1301   // Initialize the first Bezier segment
1302 
1303   for(i=0;i<=p ;i++)
1304     for(j=0;j<S.P.cols();j++)
1305       bpts(i,j) = S.P(i,j);
1306 
1307   while(b<m){ // Big loop thru knot vector
1308     i=b ;
1309     while(b<m && S.U[b] >= S.U[b+1]) // for some odd reasons... == doesn't work
1310       b++ ;
1311     mul = b-i+1 ;
1312     mh += mul+t ;
1313     ub = S.U[b] ;
1314     oldr = r ;
1315     r = p-mul ;
1316     if(oldr>0)
1317       lbz = (oldr+2)/2 ;
1318     else
1319       lbz = 1 ;
1320     if(r>0)
1321       rbz = ph-(r+1)/2 ;
1322     else
1323       rbz = ph ;
1324     if(r>0){ // Insert knot to get Bezier segment
1325       numer = ub-ua ;
1326       for(k=p;k>mul;k--){
1327 	alphas[k-mul-1] = numer/(S.U[a+k]-ua) ;
1328       }
1329       for(j=1;j<=r;j++){
1330 	save = r-j ; s = mul+j ;
1331 	for(k=p;k>=s;k--){
1332 	  for(colJ=0;colJ<S.P.cols();colJ++){
1333 	    bpts(k,colJ) = alphas[k-s]*bpts(k,colJ)+(1.0-alphas[k-s])*bpts(k-1,colJ) ;}
1334 	}
1335 	if(Nextbpts.rows()>0)
1336 	  for(colJ=0;colJ<S.P.cols();colJ++){
1337 	    Nextbpts(save,colJ) = bpts(p,colJ) ;}
1338       }
1339     }
1340 
1341     for(i=lbz;i<=ph;i++){ // Degree elevate Bezier,  only the points lbz,...,ph are used
1342       for(colJ=0;colJ<S.P.cols();colJ++){
1343 	ebpts(i,colJ) = 0.0 ;}
1344       mpi = minimum(p,i) ;
1345       for(j=maximum(0,i-t); j<=mpi ; j++)
1346 	for(colJ=0;colJ<S.P.cols();colJ++){
1347 	  ebpts(i,colJ) += bezalfs(i,j)*bpts(j,colJ) ;}
1348     }
1349 
1350     if(oldr>1){ // Must remove knot u=c.U[a] oldr times
1351       // if(oldr>2) // Alphas on the right do not change
1352       //	alfj = (ua-nc.U[kind-1])/(ub-nc.U[kind-1]) ;
1353       first = kind-2 ; last = kind ;
1354       den = ub-ua ;
1355       bet = (ub-U[kind-1])/den ;
1356       for(int tr=1; tr<oldr; tr++){ // Knot removal loop
1357 	i = first ; j = last ;
1358 	kj = j-kind+1 ;
1359 	while(j-i>tr){ // Loop and compute the new control points for one removal step
1360 	  if(i<cind){
1361 	    alf=(ub-U[i])/(ua-U[i]) ;
1362 	    for(colJ=0;colJ<S.P.cols();colJ++){
1363 	      P(i,colJ) = alf*P(i,colJ) + (1.0-alf)*P(i-1,colJ) ;}
1364 	  }
1365 	  if( j>= lbz){
1366 	    if(j-tr <= kind-ph+oldr){
1367 	      gam = (ub-U[j-tr])/den ;
1368 	      for(colJ=0;colJ<S.P.cols();colJ++){
1369 		ebpts(kj,colJ) = gam*ebpts(kj,colJ) + (1.0-gam)*ebpts(kj+1,colJ) ;}
1370 	    }
1371 	    else{
1372 	      for(colJ=0;colJ<S.P.cols();colJ++){
1373 		ebpts(kj,colJ) = bet*ebpts(kj,colJ)+(1.0-bet)*ebpts(kj+1,colJ) ;}
1374 	    }
1375 	  }
1376 	  ++i ; --j; --kj ;
1377 	}
1378 	--first ; ++last ;
1379       }
1380     }
1381 
1382     if(a!=p) // load the knot u=c.U[a]
1383       for(i=0;i<ph-oldr; i++){
1384 	U[kind++] = ua ;
1385       }
1386 
1387     for(j=lbz; j<=rbz ; j++) { // load control points onto nc
1388       for(colJ=0;colJ<S.P.cols();colJ++)
1389 	P(cind,colJ) = ebpts(j,colJ) ;
1390       ++cind ;
1391     }
1392 
1393     if(b<m){ // Set up for next pass thru loop
1394       for(colJ=0;colJ<S.P.cols();colJ++){
1395 	for(j=0;j<r;j++)
1396 	  bpts(j,colJ) = Nextbpts(j,colJ) ;
1397 	for(j=r;j<=p;j++)
1398 	  bpts(j,colJ) = S.P(b-p+j,colJ) ;
1399       }
1400       a=b ;
1401       b++ ;
1402       ua = ub ;
1403     }
1404     else{
1405       for(i=0;i<=ph;i++)
1406 	U[kind+i] = ub ;
1407     }
1408   }
1409   // Resize to the proper number of control points
1410   resizeKeep(mh-ph,S.P.cols(),ph,S.degV) ;
1411 }
1412 
1413 /*!
1414   \brief Degree elevate the surface in the V direction
1415 
1416   \param t  elevate the degree in the v direction by this amount.
1417 
1418   \author Philippe Lavoie
1419   \date 24 January, 1997
1420 */
1421 template <class T, int N>
degreeElevateV(int t)1422 void NurbsSurface<T,N>::degreeElevateV(int t) {
1423   if(t<=0){
1424     return ;
1425   }
1426 
1427   NurbsSurface<T,N> S(*this) ;
1428 
1429   int i,j,k ;
1430   int n = S.ctrlPnts().cols()-1;
1431   int p = S.degV ;
1432   int m = n+p+1;
1433   int ph = p+t ;
1434   int ph2 = ph/2 ;
1435   Matrix<T> bezalfs(p+t+1,p+1) ; // coefficients for degree elevating the Bezier segment
1436   Matrix< HPoint_nD<T,N> > bpts(p+1,S.P.rows()) ; // pth-degree Bezier control points of the current segment
1437   Matrix< HPoint_nD<T,N> > ebpts(p+t+1,S.P.rows()) ; // (p+t)th-degree Bezier control points of the  current segment
1438   Matrix< HPoint_nD<T,N> > Nextbpts(p-1,S.P.rows()) ; // leftmost control points of the next Bezier segment
1439   //Vector<T> alphas(p-1) ; // knot instertion alphas.
1440   T* alphas = (T*) alloca((p-1)*sizeof(T)) ;
1441 
1442   // Compute the Binomial coefficients
1443   Matrix<T> Bin(ph+1,ph2+1) ;
1444   binomialCoef(Bin) ;
1445 
1446   // Compute Bezier degree elevation coefficients
1447   T inv,mpi ;
1448   bezalfs(0,0) = bezalfs(ph,p) = 1.0 ;
1449   for(i=1;i<=ph2;i++){
1450     inv= 1.0/Bin(ph,i) ;
1451     mpi = minimum(p,i) ;
1452     for(j=maximum(0,i-t); j<=mpi; j++){
1453       bezalfs(i,j) = inv*Bin(p,j)*Bin(t,i-j) ;
1454     }
1455   }
1456 
1457   for(i=ph2+1;i<ph ; i++){
1458     mpi = minimum(p,i) ;
1459     for(j=maximum(0,i-t); j<=mpi ; j++)
1460       bezalfs(i,j) = bezalfs(ph-i,p-j) ;
1461   }
1462 
1463   resize(S.P.rows(),S.P.cols()+S.P.cols()*t,S.degU,ph) ; // Allocate more control points than necessary
1464 
1465   int rowJ ;
1466   int mh = ph ;
1467   int kind = ph+1 ;
1468   T va = S.V[0] ;
1469   T vb = 0.0 ;
1470   int r=-1 ;
1471   int oldr ;
1472   int a = p ;
1473   int b = p+1 ;
1474   int cind = 1 ;
1475   int rbz,lbz = 1 ;
1476   int mul,save,s;
1477   T alf ;
1478   int first, last, kj ;
1479   T den,bet,gam,numer ;
1480 
1481   for(i=0;i<S.P.rows();i++)
1482     P(i,0) = S.P(i,0) ;
1483   for(i=0; i <= ph ; i++){
1484     V[i] = va ;
1485   }
1486 
1487   // Initialize the first Bezier segment
1488 
1489   for(i=0;i<=p ;i++)
1490     for(j=0;j<S.P.rows();j++)
1491       bpts(i,j) = S.P(j,i);
1492 
1493   while(b<m){ // Big loop thru knot vector
1494     i=b ;
1495     while(b<m && S.V[b] >= S.V[b+1]) // for some odd reasons... == doesn't work
1496       b++ ;
1497     mul = b-i+1 ;
1498     mh += mul+t ;
1499     vb = S.V[b] ;
1500     oldr = r ;
1501     r = p-mul ;
1502     if(oldr>0)
1503       lbz = (oldr+2)/2 ;
1504     else
1505       lbz = 1 ;
1506     if(r>0)
1507       rbz = ph-(r+1)/2 ;
1508     else
1509       rbz = ph ;
1510     if(r>0){ // Insert knot to get Bezier segment
1511       numer = vb-va ;
1512       for(k=p;k>mul;k--){
1513 	alphas[k-mul-1] = numer/(S.V[a+k]-va) ;
1514       }
1515       for(j=1;j<=r;j++){
1516 	save = r-j ; s = mul+j ;
1517 	for(k=p;k>=s;k--){
1518 	  for(rowJ=0;rowJ<S.P.rows();rowJ++){
1519 	    bpts(k,rowJ) = alphas[k-s]*bpts(k,rowJ)+(1.0-alphas[k-s])*bpts(k-1,rowJ) ;}
1520 	}
1521 	if(Nextbpts.rows()>0)
1522 	  for(rowJ=0;rowJ<S.P.rows();rowJ++){
1523 	    Nextbpts(save,rowJ) = bpts(p,rowJ) ;}
1524       }
1525     }
1526 
1527     for(i=lbz;i<=ph;i++){ // Degree elevate Bezier,  only the points lbz,...,ph are used
1528       for(rowJ=0;rowJ<S.P.rows();rowJ++){
1529 	ebpts(i,rowJ) = 0.0 ;}
1530       mpi = minimum(p,i) ;
1531       for(j=maximum(0,i-t); j<=mpi ; j++)
1532 	for(rowJ=0;rowJ<S.P.rows();rowJ++){
1533 	  ebpts(i,rowJ) += bezalfs(i,j)*bpts(j,rowJ) ;}
1534     }
1535 
1536     if(oldr>1){ // Must remove knot V=S.V[a] oldr times
1537       // if(oldr>2) // Alphas on the right do not change
1538       //	alfj = (va-nc.U[kind-1])/(vb-V[kind-1]) ;
1539       first = kind-2 ; last = kind ;
1540       den = vb-va ;
1541       bet = (vb-V[kind-1])/den ;
1542       for(int tr=1; tr<oldr; tr++){ // Knot removal loop
1543 	i = first ; j = last ;
1544 	kj = j-kind+1 ;
1545 	while(j-i>tr){ // Loop and compute the new control points for one removal step
1546 	  if(i<cind){
1547 	    alf=(vb-V[i])/(va-V[i]) ;
1548 	    for(rowJ=0;rowJ<S.P.rows();rowJ++){
1549 	      P(rowJ,i) = alf*P(rowJ,i) + (1.0-alf)*P(rowJ,i-1) ;}
1550 	  }
1551 	  if( j>= lbz){
1552 	    if(j-tr <= kind-ph+oldr){
1553 	      gam = (vb-V[j-tr])/den ;
1554 	      for(rowJ=0;rowJ<S.P.rows();rowJ++){
1555 		ebpts(kj,rowJ) = gam*ebpts(kj,rowJ) + (1.0-gam)*ebpts(kj+1,rowJ) ;}
1556 	    }
1557 	    else{
1558 	      for(rowJ=0;rowJ<S.P.rows();rowJ++){
1559 		ebpts(kj,rowJ) = bet*ebpts(kj,rowJ)+(1.0-bet)*ebpts(kj+1,rowJ) ;}
1560 	    }
1561 	  }
1562 	  ++i ; --j; --kj ;
1563 	}
1564 	--first ; ++last ;
1565       }
1566     }
1567 
1568     if(a!=p) // load the knot v=S.V[a]
1569       for(i=0;i<ph-oldr; i++){
1570 	V[kind++] = va ;
1571       }
1572 
1573     for(j=lbz; j<=rbz ; j++) { // load control points onto nc
1574       for(rowJ=0;rowJ<S.P.rows();rowJ++)
1575 	P(rowJ,cind) = ebpts(j,rowJ) ;
1576       ++cind ;
1577     }
1578 
1579     if(b<m){ // Set up for next pass thru loop
1580       for(rowJ=0;rowJ<S.P.rows();rowJ++){
1581 	for(j=0;j<r;j++)
1582 	  bpts(j,rowJ) = Nextbpts(j,rowJ) ;
1583 	for(j=r;j<=p;j++)
1584 	  bpts(j,rowJ) = S.P(rowJ,b-p+j) ;
1585       }
1586       a=b ;
1587       b++ ;
1588       va = vb ;
1589     }
1590     else{
1591       for(i=0;i<=ph;i++)
1592 	V[kind+i] = vb ;
1593     }
1594   }
1595   // Resize to the proper number of control points
1596   resizeKeep(S.P.rows(),mh-ph,S.degU,ph) ;
1597 }
1598 /*!
1599   \brief Finds the multiplicity of a knot in the U knot
1600 
1601   \param r the knot to observe
1602 
1603   \return the multiplicity of the knot
1604   \warning \a r must be a valid U knot index
1605 
1606   \author  Philippe Lavoie
1607   \date 24 January, 1997
1608 */
1609 template <class T, int N>
findMultU(int r) const1610 int NurbsSurface<T,N>::findMultU(int r) const {
1611   int s=1 ;
1612   for(int i=r;i>degU+1;i--)
1613     if(U[i]<=U[i-1])
1614       s++ ;
1615     else
1616       return s ;
1617   return s ;
1618 }
1619 
1620 /*!
1621   \brief finds the multiplicity of a knot in the V knot
1622 
1623   \param r  the knot to observe
1624 
1625   \return the multiplicity of the V knot
1626 
1627   \warning \a r must be a valid knot index
1628 
1629   \author  Philippe Lavoie
1630   \date 24 January, 1997
1631 */
1632 template <class T, int N>
findMultV(int r) const1633 int NurbsSurface<T,N>::findMultV(int r) const {
1634   int s=1 ;
1635   for(int i=r;i>degV+1;i--)
1636     if(V[i]<=V[i-1])
1637       s++ ;
1638     else
1639       return s ;
1640   return s ;
1641 }
1642 
1643 
1644 
1645 
1646 /*!
1647   \brief finds the span in the U and V direction
1648 
1649   Finds the span in the U and V direction. The spanU is the index
1650   \a k for which the parameter \a u is valid in the \a [u_k,u_{k+1}]
1651   range. The spanV is the index \a k for which the parameter \a v is
1652   valid in the \a [v_k,v_{k+1}] range.
1653 
1654   \param u  find the U span for this parametric value
1655   \param v  find the V span for this parametric value
1656   \param spanU  the U span
1657   \param spanV  the V span
1658 
1659   \author Philippe Lavoie
1660   \date 24 January, 1997
1661 */
1662 template <class T, int N>
findSpan(T u,T v,int & spanU,int & spanV) const1663 void NurbsSurface<T,N>::findSpan(T u, T v, int& spanU, int& spanV) const{
1664   spanU = findSpanU(u) ;
1665   spanV = findSpanV(v) ;
1666 }
1667 
1668 /*!
1669   \brief finds the span in the U direction
1670 
1671   Finds the span in the U direction. The span is the index
1672   \a k for which the parameter \a u is valid in the \a [u_k,u_{k+1}]
1673   range.
1674 
1675   \param u --> find the span for this parametric value
1676 
1677   \return the span for \a u
1678 
1679   \author    Philippe Lavoie
1680   \date 24 January, 1997
1681 
1682   \modified 20 January, 1999 (Alejandro Frangi)
1683 */
1684 template <class T, int N>
findSpanU(T u) const1685 int NurbsSurface<T,N>::findSpanU(T u) const{
1686   if(u>=U[P.rows()])
1687     return P.rows()-1 ;
1688   if(u<=U[degU])
1689     return degU ;
1690 
1691   //AF
1692   int low = 0 ;
1693   int high = P.rows()+1 ;
1694   int mid = (low+high)/2 ;
1695 
1696   while(u<U[mid] || u>= U[mid+1]){
1697     if(u<U[mid])
1698       high = mid ;
1699     else
1700       low = mid ;
1701     mid = (low+high)/2 ;
1702   }
1703   return mid ;
1704 }
1705 
1706 /*!
1707   \brief finds the span in the V direction
1708 
1709   Finds the span in the V direction. The span is the index
1710   \a k for which the parameter \a v is valid in the \a [v_k,v_{k+1}]
1711   range.
1712 
1713   \param v  find the span for this parametric value
1714 
1715   \return the span for \a v
1716 
1717   \author Philippe Lavoie
1718   \date 24 January, 1997
1719   \modified 20 January, 1999 (Alejandro Frangi)
1720 
1721 */
1722 template <class T, int N>
findSpanV(T v) const1723 int NurbsSurface<T,N>::findSpanV(T v) const{
1724   if(v>=V[P.cols()])
1725     return P.cols()-1 ;
1726   if(v<=V[degV])
1727     return degV ;
1728 
1729   //AF
1730   int low  = 0 ;
1731   int high = P.cols()+1 ;
1732   int mid = (low+high)/2 ;
1733 
1734   while(v<V[mid] || v>= V[mid+1]){
1735     if(v<V[mid])
1736       high = mid ;
1737     else
1738       low = mid ;
1739     mid = (low+high)/2 ;
1740   }
1741   return mid ;
1742 }
1743 
1744 /*!
1745   \brief Find the non-zero basis functions in the U and V direction
1746 
1747   \param   u  the u parametric value
1748   \param   v  the v parametric value
1749   \param spanU  the span of u
1750   \param spanV  the span of v
1751   \param    Nu  the vector containing the U non-zero basis functions
1752   \param    Nv  the vector containing the V non-zero basis functions
1753 
1754   \author Philippe Lavoie
1755   \date 24 January, 1997
1756 */
1757 template <class T, int N>
basisFuns(T u,T v,int spanU,int spanV,Vector<T> & Nu,Vector<T> & Nv) const1758 void NurbsSurface<T,N>::basisFuns(T u, T v, int spanU, int spanV, Vector<T>& Nu, Vector<T> &Nv) const{
1759   basisFunsU(u,spanU,Nu) ;
1760   basisFunsV(v,spanV,Nv) ;
1761 }
1762 
1763 /*!
1764   \brief Finds the non-zero basis function in the U direction
1765 
1766   \param   u  the u parametric value
1767   \param span  the span of u
1768   \param    N  the vector containing the basis functions
1769 
1770   \author    Philippe Lavoie
1771   \date 24 January, 1997
1772 */
1773 template <class T, int nD>
basisFunsU(T u,int span,Vector<T> & N) const1774 void NurbsSurface<T,nD>::basisFunsU(T u, int span, Vector<T>& N) const {
1775   //Vector<T> left(degU+1), right(degU+1) ;
1776   T* left = (T*) alloca((degU+1)*sizeof(T)) ;
1777   T* right = (T*) alloca((degU+1)*sizeof(T)) ;
1778   T temp,saved ;
1779 
1780 
1781   N.resize(degU+1) ;
1782 
1783   N[0] = 1.0 ;
1784   for(int j=1; j<= degU ; j++){
1785     left[j] = u-U[span+1-j] ;
1786     right[j] = U[span+j]-u ;
1787     saved = 0.0 ;
1788     for(int r=0 ; r<j; r++){
1789       temp = N[r]/(right[r+1]+left[j-r]) ;
1790       N[r] = saved+right[r+1]*temp ;
1791       saved = left[j-r]*temp ;
1792     }
1793     N[j] = saved ;
1794   }
1795 
1796 }
1797 
1798 /*!
1799   \brief Finds the non-zero basis function in the V direction
1800 
1801   \param    v  the v parametric value
1802   \param span  the span of v
1803   \param    N  the vector containing the basis functions
1804 
1805   \author Philippe Lavoie
1806   \date 24 January, 1997
1807 */
1808 template <class T, int nD>
basisFunsV(T v,int span,Vector<T> & N) const1809 void NurbsSurface<T,nD>::basisFunsV(T v, int span, Vector<T>& N) const {
1810   //Vector<T> left(degV+1), right(degV+1) ;
1811   T* left = (T*) alloca((degV+1)*sizeof(T)) ;
1812   T* right = (T*) alloca((degV+1)*sizeof(T)) ;
1813   T temp,saved ;
1814 
1815 
1816   N.resize(degV+1) ;
1817 
1818   N[0] = 1.0 ;
1819   for(int j=1; j<= degV ; j++){
1820     left[j] = v-V[span+1-j] ;
1821     right[j] = V[span+j]-v ;
1822     saved = 0.0 ;
1823     for(int r=0 ; r<j; r++){
1824       temp = N[r]/(right[r+1]+left[j-r]) ;
1825       N[r] = saved+right[r+1]*temp ;
1826       saved = left[j-r]*temp ;
1827     }
1828     N[j] = saved ;
1829   }
1830 
1831 }
1832 
1833 /*!
1834   \brief computes the point and the derivatives of degree
1835          \a d and below at \a (u,v)
1836 
1837   Computes the matrix of derivatives at \a u,v .
1838   The value of skl(k,l) represents the
1839   derivative of the surface \a S(u,v) with respect to
1840   \a u \a k times and to $v$ $l$ times.
1841 
1842   \param  u  the u parametric value
1843   \param  v  the v parametric value
1844   \param  d  the derivative is computed up to and including to this value
1845   \param skl  the matrix containing the derivatives
1846 
1847   \author    Philippe Lavoie
1848   \date 24 January, 1997
1849 */
1850 template <class T, int nD>
deriveAtH(T u,T v,int d,Matrix<HPoint_nD<T,nD>> & skl) const1851 void NurbsSurface<T,nD>::deriveAtH(T u, T v, int d, Matrix< HPoint_nD<T,nD> > &skl) const {
1852   int k,l,du,dv;
1853   skl.resize(d+1,d+1) ;
1854 
1855   du = minimum(d,degU) ;
1856   for(k=degU+1;k<=d;++k)
1857     for(l=0;l<=d-k;++l)
1858       skl(k,l) = 0.0 ;
1859   dv=minimum(d,degV) ;
1860   for(l=degV+1;l<=d;++l)
1861     for(k=0;k<=d-l;++k)
1862       skl(k,l) = 0.0 ;
1863   int uspan = findSpanU(u) ;
1864   int vspan = findSpanV(v) ;
1865   Matrix<T> Nu,Nv ;
1866   nurbsDersBasisFuns(du,u,uspan,degU,U,Nu) ;
1867   nurbsDersBasisFuns(dv,v,vspan,degV,V,Nv) ;
1868 
1869   Vector< HPoint_nD<T,nD> > temp(degV+1) ;
1870   int dd,r,s ;
1871   for(k=0;k<=du;++k){
1872     for(s=0;s<=degV;++s){
1873       temp[s] = 0.0 ;
1874       for(r=0;r<=degU;++r)
1875 	temp[s] += Nu(k,r)*P(uspan-degU+r,vspan-degV+s) ;
1876     }
1877     dd = minimum(d-k,dv) ;
1878     for(l=0;l<=dd;++l){
1879       skl(k,l) = 0.0 ;
1880       for(s=0;s<=degV;++s)
1881 	skl(k,l) += Nv(l,s)*temp[s] ;
1882     }
1883   }
1884 }
1885 
1886 /*!
1887   \brief Computes the point and the derivatives of degree
1888          \a d and below at \a (u,v)
1889 
1890   Computes the matrix of derivatives at \a u,v .
1891   The value of skl(k,l) represents the
1892   derivative of the surface \a S(u,v) with respect to
1893   \a u, \a k times and to \a v, \a l times.
1894 
1895   \param   u  the u parametric value
1896   \param   v  the v parametric value
1897   \param   d  the derivative is computed up to and including to  this value
1898   \param skl  the matrix containing the derivatives
1899 
1900   \author    Philippe Lavoie
1901   \date 24 January, 1997
1902 */
1903 template <class T, int N>
deriveAt(T u,T v,int d,Matrix<Point_nD<T,N>> & skl) const1904 void NurbsSurface<T,N>::deriveAt(T u, T v, int d, Matrix< Point_nD<T,N> > &skl) const {
1905   int k,l ;
1906   Matrix< HPoint_nD<T,N> > ders ;
1907   Point_nD<T,N> pv,pv2 ;
1908 
1909   skl.resize(d+1,d+1) ;
1910 
1911   deriveAtH(u,v,d,ders) ;
1912 
1913   Matrix<T> Bin(d+1,d+1) ;
1914   binomialCoef(Bin) ;
1915   int i,j ;
1916 
1917   for(k=0;k<=d;++k){
1918     for(l=0;l<=d-k;++l){
1919       pv.x() = ders(k,l).x() ;
1920       pv.y() = ders(k,l).y() ;
1921       pv.z() = ders(k,l).z() ;
1922       for(j=1;j<=l;j++)
1923 	pv -= Bin(l,j)*ders(0,j).w()*skl(k,l-j) ;
1924       for(i=1;i<=k;i++){
1925 	pv -= Bin(k,i)*ders(i,0).w()*skl(k-i,l) ;
1926 	pv2 = 0.0 ;
1927 	for(j=1;j<=l;j++)
1928 	  pv2 += Bin(l,j)*ders(i,j).w()*skl(k-i,l-j) ;
1929 	pv -= Bin(k,i)*pv2 ;
1930       }
1931       skl(k,l) = pv/ders(0,0).w() ;
1932     }
1933   }
1934 }
1935 
1936 /*!
1937   \brief Computes the normal of the surface at \a (u,v)
1938 
1939   \param  u  the u parametric value
1940   \param  v  the v parametric value
1941 
1942   \return the normal at \a (u,v) .
1943 
1944   \author    Philippe Lavoie
1945   \date 24 January, 1997
1946 */
1947 template <class T, int N>
normal(T u,T v) const1948 Point_nD<T,N> NurbsSurface<T,N>::normal(T u, T v) const {
1949   Matrix< Point_nD<T,N> > ders ;
1950 
1951   deriveAt(u,v,1,ders) ;
1952 
1953   return crossProduct(ders(1,0),ders(0,1)) ;
1954 }
1955 
1956 /*!
1957   \brief Returns the point on the surface at \a u,v
1958 
1959   Returns the point on the surface at \a u,v
1960 
1961   \param u  the u parametric value
1962   \param v  the v parametric value
1963 
1964   \return The homogenous point at \a u,v
1965 
1966   \author    Philippe Lavoie
1967   \date 24 January, 1997
1968 */
1969 template <class T, int N>
operator ()(T u,T v) const1970 HPoint_nD<T,N> NurbsSurface<T,N>::operator()(T u, T v) const{
1971   int uspan = findSpanU(u) ;
1972   int vspan = findSpanV(v) ;
1973   Vector<T> Nu,Nv ;
1974   Vector< HPoint_nD<T,N> > temp(degV+1)  ;
1975 
1976   basisFuns(u,v,uspan,vspan,Nu,Nv) ;
1977 
1978   int l;
1979   for(l=0;l<=degV;l++){
1980     temp[l] =0.0 ;
1981     for(int k=0;k<=degU;k++){
1982       temp[l] += Nu[k]*P(uspan-degU+k,vspan-degV+l) ;
1983     }
1984   }
1985   HPoint_nD<T,N> sp(0,0,0,0) ;
1986   for(l=0;l<=degV;l++){
1987     sp += Nv[l]*temp[l] ;
1988   }
1989   return sp ;
1990 }
1991 
1992 inline
max3(int a,int b,int c)1993 int max3(int a,int b, int c){
1994   int m = a ;
1995   if(m <b)
1996     m = b ;
1997   if(m <c)
1998     m = c ;
1999   return m ;
2000 }
2001 
2002 /*!
2003   \relates NurbsSurface
2004   \brief Interpolation of a surface from 2 sets of orthogonal curves
2005 
2006   Interpolation of a surface from 2 sets of orthogonal curves.
2007   See A10.3 at page 494 on the NURBS book for more details about
2008   the implementation.
2009 
2010   \param lU  an array of curves in the U direction
2011   \param lV  an array of curves in the V direction
2012   \param intersections  a matrix of 3D points specifying where the
2013                 the curves intersect
2014   \param gS  the Gordon surface
2015 
2016   \author    Philippe Lavoie
2017   \date 24 January, 1997
2018 */
2019 template <class T, int N>
gordonSurface(NurbsCurveArray<T,N> & lU,NurbsCurveArray<T,N> & lV,const Matrix<Point_nD<T,N>> & intersections,NurbsSurface<T,N> & gS)2020 void gordonSurface(NurbsCurveArray<T,N>& lU, NurbsCurveArray<T,N>& lV, const Matrix< Point_nD<T,N> >& intersections, NurbsSurface<T,N>& gS){
2021   NurbsSurface<T,N> sU,sV,sI ;
2022   sU.skinU(lU,3) ;
2023   sV.skinV(lV,3) ;
2024   sI.globalInterp(intersections,3,3) ;
2025 
2026   int du = max3(sU.degU,sV.degU,sI.degU) ;
2027   int dv = max3(sU.degV,sV.degV,sI.degV) ;
2028 
2029 
2030   NurbsSurface<T,N> SU,SV,SI ;
2031   degreeElevate(sU,du-sU.degU,dv-sU.degV,SU) ;
2032   degreeElevate(sV,du-sV.degU,dv-sV.degV,SV) ;
2033   degreeElevate(sI,du-sI.degU,dv-sI.degV,SI) ;
2034 
2035 
2036   Vector<T> U,V ;
2037   U = knotUnion(SU.knotU(),SV.knotU()) ;
2038   U = knotUnion(U,SI.knotU()) ;
2039   V = knotUnion(SU.knotV(),SV.knotV()) ;
2040   V = knotUnion(V,SI.knotV()) ;
2041 
2042   SU.mergeKnots(U,V) ;
2043   SV.mergeKnots(U,V) ;
2044   SI.mergeKnots(U,V) ;
2045 
2046   gS = SU ;
2047 
2048   for(int i=0;i<gS.P.rows();i++)
2049     for(int j=0;j<gS.P.cols();j++){
2050       gS.P(i,j) += SV.P(i,j) ;
2051       gS.P(i,j) -= SI.P(i,j) ;
2052     }
2053 }
2054 
2055 template <class T>
insert(T u,Vector<T> & v)2056 inline void insert(T u, Vector<T>& v){
2057   int i ;
2058   if(u<v[0] || u>v[v.n()-1])
2059     return ;
2060   v.resize(v.n()+1) ;
2061   i = v.n()-1 ;
2062   while(v[i-1]>u){
2063     v[i] = v[i-1] ;
2064     --i ;
2065   }
2066   v[i] = u ;
2067 }
2068 
2069 
2070 /*!
2071   \brief Generates a surface by sweeping a curve along a trajectory
2072 
2073   Sweeping consists of creating a surface by moving a curve
2074   profile through a trajectory. The method uses here consists of
2075   using skinning of \a K instances of the curve \a C(u) along
2076   \a T(u). The \a K value should be viewed as the minimum number of
2077   sections required.
2078 
2079   The profile curve \a C(u) should lie on the xz-plane.
2080   It follows the trajectory curve \a T(u) along its y-axis.
2081 
2082   The scaling function is used to modify the shape of the curve
2083   profile while it's being swept. It can scale in any of the
2084   4 dimensions to obtain the desired effects on the profile.
2085 
2086   See A10.1 on page 476 of the NURBS book for more details
2087   about the implementation.
2088 
2089   You might have to play with the useAy and invAz variables
2090   to obtain a satisfactory result for the sweep operation.
2091   This is either because there is an error in the code or
2092   because it is the way it is supposed to work.
2093 
2094   \param   T  the trajectory of the curve
2095   \param  C  the curve profile to sweep
2096   \param  Sv  a scaling function
2097   \param  K  the minimum number of insertion
2098   \param  useAy  a 0 indicates that rotation angle around the $y$-axis
2099 	            is not computed, otherwise it is computed. Results
2100 		    are usually better with a value of 0.
2101   \param  invAz  a 1 indicates that the computed rotation around the
2102 	               $z$-axis should be inversed, a 0 indicates it
2103 		       stays as it is.
2104   \return the NURBS surface representing the sweep of $C$ along $T$
2105   \warning This will not yield a correct value for a closed trajectory
2106            curve.
2107 
2108   \author Philippe Lavoie
2109   \date 25 July, 1997
2110 */
2111 template <class T, int N>
sweep(const NurbsCurve<T,N> & Trj,const NurbsCurve<T,N> & C,const NurbsCurve<T,N> & Sv,int K,int useAy,int invAz)2112 void NurbsSurface<T,N>::sweep(const NurbsCurve<T,N>& Trj, const NurbsCurve<T,N>& C, const NurbsCurve<T,N>& Sv, int K, int useAy, int invAz) {
2113   int i,j,k,m,q,ktv,nsect ;
2114   q = Trj.degree() ;
2115   ktv = Trj.knot().n() ;
2116   nsect = K  ;
2117 
2118   V.resize(Trj.knot().n()) ;
2119   V = Trj.knot() ;
2120 
2121   if(ktv <= nsect+q){
2122     m = nsect+q-ktv+1 ;
2123     // insert m knots into T(v)
2124     // locations are not critical so inserting in the middle of the
2125     // biggest span is used.
2126     for(i=0;i<m;++i){
2127       T md,mt ;
2128       T mu = -1;
2129       md = 0 ;
2130       for(j=1;j<V.n();++j){
2131 	mt = V[j]-V[j-1] ;
2132 	if(mt>md){
2133 	  md = mt ;
2134 	  mu = (V[j]+V[j-1])/2.0 ;
2135 	}
2136       }
2137       insert(mu,V) ;
2138     }
2139   }
2140   else{
2141     if(ktv>nsect){ // must increase the number of instances of C(u)
2142       nsect = ktv-q-1 ;
2143     }
2144   }
2145 
2146   Vector<T> v;
2147 
2148   // Compute the parameters by averaging the knots
2149   v.resize(nsect) ;
2150   v[0] = Trj.knot(Trj.degree()) ;
2151   v[nsect-1] = Trj.knot(Trj.knot().n()-Trj.degree()-1) ;
2152   for(k=1;k<nsect-1;++k){
2153     v[k] = 0.0 ;
2154     for(i=k+1;i<k+q+1;++i)
2155       v[k] += V[i] ;
2156     v[k] /= q ;
2157   }
2158 
2159   resize(C.ctrlPnts().n(),nsect,C.degree(),Trj.degree()) ;
2160 
2161   U = C.knot() ;
2162 
2163   // setup Bv ;
2164   Vector< Point_nD<T,N> > B ;
2165   NurbsCurve<T,N> Bv ;
2166   Vector< Point_nD<T,N> > Td ;
2167 
2168   B.resize(v.n()) ;
2169   Trj.deriveAt(v[0],1,Td) ;
2170 
2171   B[0] = Td[1] ;
2172   if(Td[1].y() ==0)
2173     B[0] = crossProduct(Point_nD<T,N>(0,1,0),B[0]) ;
2174   else
2175     B[0] = crossProduct(Point_nD<T,N>(1,0,0),B[0]) ;
2176   B[0] /= norm(B[0]) ;
2177 
2178 
2179   for(i=1;i<v.n();++i){
2180     Trj.deriveAt(v[i],1,Td);
2181     Point_nD<T,N> Ti(Td[1]) ;
2182     Ti = Ti/norm(Ti) ;
2183     Point_nD<T,N> bi ;
2184     bi = B[i-1]-(B[i-1]*Ti)*Ti ;
2185     B[i] = bi/norm(bi) ;
2186   }
2187 
2188   Bv.globalInterp(B,v,minimum(3,B.n()-1)) ;
2189 
2190   Vector< HPoint_nD<T,N> > Q(C.ctrlPnts().n()) ;
2191   for(k=0;k<nsect;++k){
2192     // scale the control points by Sv(v[k])
2193     for(i=0;i<Q.n();++i){
2194       HPoint_nD<T,N> sk(Sv(v[k])) ;
2195       Q[i].x() = sk.x()*C.ctrlPnts(i).x() ;
2196       Q[i].y() = sk.y()*C.ctrlPnts(i).y() ;
2197       Q[i].z() = sk.z()*C.ctrlPnts(i).z() ;
2198       Q[i].w() = sk.w()*C.ctrlPnts(i).w() ;
2199     }
2200     // compute o(v[k])
2201     Point_nD<T,N> o = Trj.pointAt(v[k]) ;
2202     //T w = T(v[k]).w() ;
2203 
2204     // compute x(v[k])
2205     Trj.deriveAt(v[k],1,Td) ;
2206 
2207     Point_nD<T,N> x = Td[1]/norm(Td[1]) ;
2208 
2209     // compute z(v[k])
2210     Point_nD<T,N> z = Bv.pointAt(v[k]) ;
2211     z /= norm(z) ;
2212 
2213     // compute y(v[k])
2214     Point_nD<T,N> y = crossProduct(z,x) ;
2215 
2216     /*
2217     // compute the transform matrix
2218     double az = M_PI+atan2(y.y(),y.x()) ;
2219     double ax = -atan2(z.y(),z.z()) ;
2220     double ay = 0 ;
2221     if(useAy){
2222       ay = atan2(x.z(),x.x()) ;
2223     }
2224     if(invAz){
2225       az = -1.0*az ;
2226     }
2227     MatrixRT_DOUBLE A(ax,ay,az,o.x(),o.y(),o.z()) ;
2228     */
2229 
2230     MatrixRT_DOUBLE R ; // M(4,4)
2231     R(0,0) = y.x();
2232     R(1,0) = y.y();
2233     R(2,0) = y.z();
2234     R(0,1) = x.x();
2235     R(1,1) = x.y();
2236     R(2,1) = x.z();
2237     R(0,2) = z.x();
2238     R(1,2) = z.y();
2239     R(2,2) = z.z();
2240     //R(3,3) = 1.0;
2241 
2242     MatrixRT_DOUBLE Tx ;
2243     Tx.translate(o.x(),o.y(),o.z());
2244     //MatrixRT_DOUBLE R(M) ;
2245     MatrixRT_DOUBLE A ;
2246     A = Tx * R ;
2247 
2248     for(i=0;i<Q.n();++i){
2249       P(i,k) = A*(Q[i]) ;
2250     }
2251   }
2252 
2253   // interpolate along V
2254   Q.resize(P.cols()) ;
2255   NurbsCurve<T,N> R;
2256   for(i=0;i<P.rows();++i){
2257     for(k=0;k<P.cols();++k)
2258       Q[k] = P(i,k) ;
2259     R.globalInterpH(Q,v,V,degV) ;
2260     for(k=0;k<P.cols();++k)
2261       P(i,k) = R.ctrlPnts(k) ;
2262   }
2263 
2264 }
2265 
2266 /*!
2267   \brief Generates a surface by sweeping a curve along a  trajectory
2268 
2269   Sweeping consists of creating a surface by moving a curve
2270   through a trajectory. The method uses here consists of using
2271   skinning of $K$ instances of the curve \a C(u) along \a T(u).
2272   The \a K value should be viewed as a minimum.
2273 
2274   The cross-sectional curve \a C(u) should lie on the xz-plane.
2275   It follows the trajectory curve \a T(u) along its y-axis.
2276 
2277   You might have to play with the useAy and invAz variables
2278   to obtain a satisfactory result for the sweep operation.
2279   This is either because there is an error in the code or
2280   because it is the way it is supposed to work.
2281 
2282   \param T  the trajectory of the curve
2283   \param C  the curve to sweep
2284   \param K  the minimum number of insertion
2285   \param useAy  a 0 indicates that rotation angle around the y-axis
2286 	            is not computed, otherwise it is computed.
2287   \param invAz  a 1 indicates that the computed rotation around the
2288 	            z-axis should be inversed, a 0 indicates it
2289 		    stays as it is.
2290 
2291   \return the NURBS surface representing the sweep of \a C along \a T
2292   \warning This will not yield a correct value for a closed trajectory
2293            curve.
2294 
2295   \author Philippe Lavoie
2296   \date 25 July, 1997
2297 */
2298 template <class T, int N>
sweep(const NurbsCurve<T,N> & Trj,const NurbsCurve<T,N> & C,int K,int useAy,int invAz)2299 void NurbsSurface<T,N>::sweep(const NurbsCurve<T,N>& Trj, const NurbsCurve<T,N>& C, int K, int useAy,int invAz) {
2300   // setup Sv
2301   Vector< HPoint_nD<T,N> > p(2) ;
2302   p[0] = HPoint_nD<T,N>(1,1,1,1) ;
2303   p[1] = HPoint_nD<T,N>(1,1,1,1) ;
2304   Vector<T> u(4) ;
2305   u[0] = u[1] = 0.0 ; u[2] = u[3] = 1.0 ;
2306 
2307   NurbsCurve<T,N> Sv(p,u,1) ;
2308 
2309   sweep(Trj,C,Sv,K,useAy,invAz) ;
2310 }
2311 
2312 /*!
2313   \brief Performs geometrical modifications
2314 
2315   Each control points will be modified by a rotation-translation
2316   matrix.
2317 
2318   \param A  the rotation-translation matrix
2319 
2320   \author Philippe Lavoie
2321   \date 22 August 1997
2322 */
2323 template <class T, int N>
transform(const MatrixRT<T> & A)2324 void NurbsSurface<T,N>::transform(const MatrixRT<T>& A){
2325   for(int i=0;i<P.rows();++i)
2326     for(int j=0;j<P.cols();++j)
2327       P(i,j) = A*P(i,j) ;
2328 }
2329 
2330 /*!
2331   \brief Refine both knot vectors
2332 
2333   \param nU  the U knot vector to refine from
2334   \param nV  the V knot vector to refine from
2335 
2336   \author Philippe Lavoie
2337   \date 24 January, 1997
2338 */
2339 template <class T, int N>
refineKnots(const Vector<T> & nU,const Vector<T> & nV)2340 void NurbsSurface<T,N>::refineKnots(const Vector<T>& nU, const Vector<T>& nV){
2341   refineKnotU(nU) ;
2342   refineKnotV(nV) ;
2343 }
2344 
2345 /*!
2346   \brief  Refines the U knot vector
2347 
2348   \param X  the knot vector to refine from
2349 
2350   \author Philippe Lavoie
2351   \date 24 January, 1997
2352 */
2353 template <class T, int N>
refineKnotU(const Vector<T> & X)2354 void NurbsSurface<T,N>::refineKnotU(const Vector<T>& X) {
2355   if(X.n()<=0)
2356     return ;
2357   int n = P.rows()-1 ;
2358   int p = degU;
2359   int m = n+p+1 ;
2360   int a,b ;
2361   int r = X.n()-1 ;
2362   NurbsSurface<T,N> nS ;
2363 
2364   nS = *this ;
2365   nS.resize(r+1+n+1,P.cols(),degU,degV) ;
2366 
2367   a = findSpanU(X[0]) ;
2368   b = findSpanU(X[r]) ;
2369   ++b ;
2370 
2371   int j,col ;
2372   for(col=0;col<P.cols();col++){
2373     for(j=0; j<=a-p ; j++)
2374       nS.P(j,col) = P(j,col);
2375     for(j = b-1 ; j<=n ; j++)
2376       nS.P(j+r+1,col) = P(j,col) ;
2377   }
2378   for(j=0; j<=a ; j++)
2379     nS.U[j] = U[j] ;
2380   for(j=b+p ; j<=m ; j++)
2381     nS.U[j+r+1] = U[j] ;
2382   int i = b+p-1 ;
2383   int k = b+p+r ;
2384   for(j=r; j>=0 ; j--){
2385     while(X[j] <= U[i] && i>a){
2386       for(col=0;col<P.cols();col++)
2387 	nS.P(k-p-1,col) = P(i-p-1,col) ;
2388       nS.U[k] = U[i] ;
2389       --k ;
2390       --i ;
2391     }
2392     for(col=0;col<P.cols();col++)
2393 	nS.P(k-p-1,col) = nS.P(k-p,col) ;
2394     for(int l=1; l<=p ; l++){
2395       int ind = k-p+l ;
2396       T alpha = nS.U[k+l] - X[j] ;
2397       if(alpha==0.0)
2398 	for(col=0;col<P.cols();col++)
2399 	  nS.P(ind-1,col) = nS.P(ind,col) ;
2400       else
2401 	alpha /= nS.U[k+l]-U[i-p+l] ;
2402       for(col=0;col<P.cols();col++)
2403 	nS.P(ind-1,col) = alpha*nS.P(ind-1,col) + (1.0-alpha)*nS.P(ind,col) ;
2404     }
2405     nS.U[k] = X[j] ;
2406     --k ;
2407   }
2408   *this = nS ;
2409 }
2410 
2411 /*!
2412   \brief Refines the V knot vector
2413 
2414   \param X  the knot vector to refine from
2415 
2416   \author Philippe Lavoie
2417   \date 24 January, 1997
2418 */
2419 template <class T, int N>
refineKnotV(const Vector<T> & X)2420 void NurbsSurface<T,N>::refineKnotV(const Vector<T>& X) {
2421   if(X.n()<=0)
2422     return ;
2423   int n = P.cols()-1 ;
2424   int p = degV;
2425   int m = n+p+1 ;
2426   int a,b ;
2427   int r = X.n()-1 ;
2428   NurbsSurface<T,N> nS ;
2429 
2430   try {
2431     nS = *this ;
2432     nS.resize(P.rows(),r+1+n+1,degU,degV) ;
2433   }
2434   catch(...){
2435     cerr << "Out of memory\n" ;
2436   }
2437 
2438   a = findSpanV(X[0]) ;
2439   b = findSpanV(X[r]) ;
2440   ++b ;
2441 
2442   int j,row ;
2443   for(row=0;row<P.rows();row++){
2444     for(j=0; j<=a-p ; j++)
2445       nS.P(row,j) = P(row,j);
2446     for(j = b-1 ; j<=n ; j++)
2447       nS.P(row,j+r+1) = P(row,j) ;
2448   }
2449   for(j=0; j<=a ; j++)
2450     nS.V[j] = V[j] ;
2451   for(j=b+p ; j<=m ; j++)
2452     nS.V[j+r+1] = V[j] ;
2453   int i = b+p-1 ;
2454   int k = b+p+r ;
2455   for(j=r; j>=0 ; j--){
2456     while(X[j] <= V[i] && i>a){
2457       for(row=0;row<P.rows();row++)
2458 	nS.P(row,k-p-1) = P(row,i-p-1) ;
2459       nS.V[k] = V[i] ;
2460       --k ;
2461       --i ;
2462     }
2463     for(row=0;row<P.rows();row++)
2464 	nS.P(row,k-p-1) = nS.P(row,k-p) ;
2465     for(int l=1; l<=p ; l++){
2466       int ind = k-p+l ;
2467       T alpha = nS.V[k+l] - X[j] ;
2468       if(alpha==0.0)
2469 	for(row=0;row<P.rows();row++)
2470 	  nS.P(row,ind-1) = nS.P(row,ind) ;
2471       else
2472 	alpha /= nS.V[k+l]-V[i-p+l] ;
2473       for(row=0;row<P.rows();row++)
2474 	nS.P(row,ind-1) = alpha*nS.P(row,ind-1) + (1.0-alpha)*nS.P(row,ind) ;
2475     }
2476     nS.V[k] = X[j] ;
2477     --k ;
2478   }
2479   *this = nS ;
2480 }
2481 
2482 
2483 /*!
2484   \brief merges a U and V knot vector with the surface knot vectors
2485 
2486   \param  nU  the U knot vector to merge with
2487   \param  nV  the V knot vector to merge with
2488 
2489   \warning  The nU and nV knot vectors must be compatible with the
2490                 current vectors
2491 
2492   \author    Philippe Lavoie
2493   \date 24 January, 1997
2494 */
2495 template <class T, int N>
mergeKnots(const Vector<T> & nU,const Vector<T> & nV)2496 void NurbsSurface<T,N>::mergeKnots(const Vector<T>& nU, const Vector<T>& nV) {
2497   mergeKnotU(nU) ;
2498   mergeKnotV(nV) ;
2499 }
2500 
2501 /*!
2502   \brief merges the U knot vector with another one
2503 
2504   \param X  a knot vector
2505 
2506   \warning The knot vector must be compatible with the U knot vector
2507 
2508   \author Philippe Lavoie
2509   \date 24 January, 1997
2510 */
2511 template <class T, int N>
mergeKnotU(const Vector<T> & X)2512 void NurbsSurface<T,N>::mergeKnotU(const Vector<T>& X){
2513   int i,ia,ib ;
2514   // Find the knots to insert
2515   Vector<T> I(U.n()) ;
2516   int done = 0 ;
2517   i = ia = ib = 0 ;
2518   while(!done) {
2519     if(X[ib] == U[ia]){
2520       ++ib ; ++ia ;
2521     }
2522     else{
2523       I[i++] = X[ib] ;
2524       ib++ ;
2525     }
2526     done = (ia>=U.n() || ib >= X.n()) ;
2527   }
2528   I.resize(i) ;
2529 
2530   if(I.n()>0)
2531     refineKnotU(I) ;
2532 }
2533 
2534 /*!
2535   \brief merges the V knot vector with another one
2536 
2537   \param X  a knot vector
2538 
2539   \warning The knot vector must be compatible with the V knot vector
2540 
2541   \author Philippe Lavoie
2542   \date 24 January, 1997
2543 */
2544 template <class T, int N>
mergeKnotV(const Vector<T> & X)2545 void NurbsSurface<T,N>::mergeKnotV(const Vector<T>& X){
2546   int i,ia,ib ;
2547   // Find the knots to insert
2548   Vector<T> I(V.n()) ;
2549   int done = 0 ;
2550   i = ia = ib = 0 ;
2551   while(!done) {
2552     if(X[ib] == V[ia]){
2553       ++ib ; ++ia ;
2554     }
2555     else{
2556       I[i++] = X[ib] ;
2557       ib++ ;
2558     }
2559     done = (ia>=V.n() || ib >= X.n()) ;
2560   }
2561   I.resize(i) ;
2562 
2563   if(I.n()>0)
2564     refineKnotV(I) ;
2565 }
2566 
2567 /*!
2568   \brief Read a surface from an input stream
2569 
2570   \param fin  the input file stream
2571 
2572   \return 0 if an error occurs, 1 otherwise
2573 
2574   \author Philippe Lavoie
2575   \date 24 January, 1997
2576 */
2577 template <class T, int N>
read(ifstream & fin)2578 int NurbsSurface<T,N>::read(ifstream &fin){
2579   if(!fin) {
2580     return 0 ;
2581   }
2582   int nu,nv,du,dv;
2583   char *type ;
2584   type = new char[3] ;
2585   if(!fin.read(type,sizeof(char)*3)) { delete []type ; return 0 ;}
2586   int r1 = strncmp(type,"ns3",3) ;
2587   int r2 = strncmp(type,"ns4",3) ;
2588   if(!(r1 || r2))
2589     return 0 ;
2590   int st ;
2591   char stc ;
2592   if(!fin.read((char*)&stc,sizeof(char))) { delete []type ; return 0 ;}
2593   if(!fin.read((char*)&nu,sizeof(int))) { delete []type ; return 0 ;}
2594   if(!fin.read((char*)&nv,sizeof(int))) { delete []type ; return 0 ;}
2595   if(!fin.read((char*)&du,sizeof(int))) { delete []type ; return 0 ;}
2596   if(!fin.read((char*)&dv,sizeof(int))) { delete []type ; return 0 ;}
2597 
2598   st = stc - '0' ;
2599   if(st != sizeof(T)){ // not of the same type size
2600     delete []type ;
2601     return 0 ;
2602   }
2603 
2604   resize(nu,nv,du,dv) ;
2605 
2606   if(!fin.read((char*)U.memory(),sizeof(T)*U.n())) { delete []type ; return 0 ;}
2607   if(!fin.read((char*)V.memory(),sizeof(T)*V.n())) { delete []type ; return 0 ;}
2608 
2609 
2610   T *p,*p2 ;
2611   if(!r1){
2612     p = new T[3*nu*nv] ;
2613     if(!fin.read((char*)p,sizeof(T)*3*nu*nv)) { delete []type ; return 0 ;}
2614     p2 = p ;
2615     for(int i=0;i<nu;i++)
2616       for(int j=0;j<nv;j++){
2617 	P(i,j).x() = *(p++) ;
2618 	P(i,j).y() = *(p++) ;
2619 	P(i,j).z() = *(p++) ;
2620 	P(i,j).w() = 1.0 ;
2621       }
2622     delete []p2 ;
2623   }
2624   else{
2625     p = new T[4*nu*nv] ;
2626     if(!fin.read((char*)p,sizeof(T)*4*nu*nv)) { delete []type ; return 0 ;}
2627     p2 = p ;
2628     for(int i=0;i<nu;i++)
2629       for(int j=0;j<nv;j++){
2630 	P(i,j).x() = *(p++) ;
2631 	P(i,j).y() = *(p++) ;
2632 	P(i,j).z() = *(p++) ;
2633 	P(i,j).w() = *(p++) ;
2634       }
2635     delete []p2 ;
2636   }
2637 
2638   delete []type ;
2639   return 1 ;
2640 }
2641 
2642 /*!
2643   \brief read a surface from a file
2644 
2645   \param filename  the filename to read from
2646 
2647   \return 0 if an error occurs, 1 otherwise
2648 
2649   \author Philippe Lavoie
2650   \date 24 January, 1997
2651 */
2652 template <class T, int N>
read(const char * filename)2653 int NurbsSurface<T,N>::read(const char* filename){
2654   ifstream fin(filename) ;
2655   if(!fin) {
2656     return 0 ;
2657   }
2658 
2659   return read(fin) ;
2660 }
2661 
2662 /*!
2663   \brief Write a surface to a file stream
2664 
2665   \param fout  the output filestream to write to.
2666 
2667   \return 1 on success, 0 on failure
2668 
2669   \author Philippe Lavoie
2670   \date 24 January, 1997
2671 */
2672 template <class T, int N>
write(ofstream & fout) const2673 int NurbsSurface<T,N>::write(ofstream &fout) const {
2674   if(!fout)
2675     return 0 ;
2676   int prows = P.rows();
2677   int pcols = P.cols();
2678   char st = '0' + sizeof(T) ;
2679   if(!fout.write((char*)&"ns4",sizeof(char)*3)) return 0 ;
2680   if(!fout.write((char*)&st,sizeof(char))) return 0 ;
2681   if(!fout.write((char*)&prows,sizeof(int))) return 0 ;
2682   if(!fout.write((char*)&pcols,sizeof(int))) return 0 ;
2683   if(!fout.write((char*)&degU,sizeof(int))) return 0 ;
2684   if(!fout.write((char*)&degV,sizeof(int))) return 0 ;
2685   if(!fout.write((char*)U.memory(),sizeof(T)*U.n())) return 0 ;
2686   if(!fout.write((char*)V.memory(),sizeof(T)*V.n())) return 0 ;
2687 
2688   T *p,*p2 ;
2689   p = new T[P.rows()*P.cols()*4] ;
2690   p2 = p ;
2691   for(int i=0;i<P.rows();i++)
2692     for(int j=0;j<P.cols();j++){
2693       *p = P(i,j).x() ; p++ ;
2694       *p = P(i,j).y() ; p++ ;
2695       *p = P(i,j).z() ; p++ ;
2696       *p = P(i,j).w() ; p++ ;
2697     }
2698   if(!fout.write((char*)p2,sizeof(T)*P.rows()*P.cols()*4)) return 0 ;
2699   delete []p2 ;
2700   return 1 ;
2701 }
2702 
2703 /*!
2704   \brief Write a surface to a file
2705 
2706   \param filename  the filename to write to
2707 
2708   \return 1 on success, 0 on failure
2709 
2710   \author Philippe Lavoie
2711   \date 24 January, 1997
2712 */
2713 template <class T, int N>
write(const char * filename) const2714 int NurbsSurface<T,N>::write(const char* filename) const {
2715   ofstream fout(filename) ;
2716   if(!fout)
2717     return 0 ;
2718   return write(fout);
2719 }
2720 
2721 /*!
2722   \brief Transpose the U and V coordinates of a surface
2723 
2724   Transpose the U and V coordinates of a surface. After this
2725   operation the \a (u,v) points correspond to \a (v,u).
2726 
2727   \return A reference to itself
2728   \warning This is not completely tested
2729 
2730   \author Philippe Lavoie
2731   \date 24 January, 1997
2732 */
2733 template <class T, int N>
transpose(void)2734 NurbsSurface<T,N>& NurbsSurface<T,N>::transpose(void){
2735   Vector<T> t(U) ;
2736   int d ;
2737   U = V ;
2738   V = t ;
2739   d = degU ;
2740   degU = degV ;
2741   degV = d ;
2742   P = ::transpose(P) ;
2743   return *this ;
2744 }
2745 
2746 /*!
2747   \brief Moves a point on the surface
2748 
2749   This moves the point \a s(u,v) by delta.
2750 
2751   \param u  the parameter in the u direction
2752   \param v  the parameter in the v direction
2753   \param delta  the displacement of the point at S(u,v)
2754 
2755   \return 1 if the operation is possible, 0 if the problem is ill defined
2756                \e i.e. there isn't enough information to find a unique
2757 	       solution (the system is overdetermined) or that the system
2758 	       has non-independant components.
2759 
2760   \author Philippe Lavoie
2761   \date 24 January 1997
2762 */
2763 template <class T, int N>
movePoint(T u,T v,const Point_nD<T,N> & delta)2764 int NurbsSurface<T,N>::movePoint(T u, T v, const Point_nD<T,N>& delta){
2765   // setup B
2766   Matrix_DOUBLE B(1,(degU+1)*(degV+1)) ;
2767   int i,j,k ;
2768 
2769   int spanU,spanV ;
2770 
2771   Vector<T> Ru,Rv ;
2772 
2773   B.reset(0.0) ;
2774 
2775   findSpan(u,v,spanU,spanV) ;
2776   nurbsBasisFuns(u,spanU,degU,U,Ru) ;
2777   nurbsBasisFuns(v,spanV,degV,V,Rv) ;
2778   for(j=0;j<=degU;++j){
2779     for(k=0;k<=degV;++k){
2780       B(0,j*(degV+1)+k)
2781 	= (double)Ru[j]*(double)Rv[k] ;
2782     }
2783   }
2784 
2785   Matrix_DOUBLE A  ;
2786   Matrix_DOUBLE Bt(::transpose(B)) ;
2787   Matrix_DOUBLE BBt ;
2788 
2789   BBt = inverse(B*Bt) ;
2790   A = Bt*BBt ;
2791 
2792   Matrix_DOUBLE dD(1,3) ;
2793 
2794   for(j=0;j<3;++j)
2795     dD(0,j) = (double)delta.data[j] ;
2796 
2797   Matrix_DOUBLE dP ;
2798 
2799   dP = A*dD ;
2800 
2801   i= 0 ;
2802 
2803   for(j=0;j<=degU;++j){
2804     for(k=0;k<=degV;++k){
2805       T w = P(spanU-degU+j,spanV-degV+k).w() ;
2806       P(spanU-degU+j,spanV-degV+k).x() += dP(i,0)*w ;
2807       P(spanU-degU+j,spanV-degV+k).y() += dP(i,1)*w ;
2808       P(spanU-degU+j,spanV-degV+k).z() += dP(i,2)*w ;
2809       ++i ;
2810     }
2811   }
2812 
2813   return 1 ;
2814 }
2815 
2816 /*!
2817   \brief Moves a point with some constraint
2818 
2819   This will modify the NURBS surface by respecting a certain
2820   number of constraints. \a u_r and \a v_r specifies the parameters
2821   on which the constraints should be applied.
2822   The constraint are defined by \a D_i(u,v) which requires 3
2823   vectors to fully qualify. $D$ specifies the value
2824   of the constraint and \a Du and \a Dv are used to specify
2825   on which parameter the constraint is applied.
2826 
2827   ur and vr should be in an increasing order.
2828 
2829   \param ur  the vector of parameters in the u direction
2830   \param vr  the vector of parameters in the v direction
2831   \param  D  a vector of the value of \a D_i^(k,l)(u,v)
2832   \param Du  a vector specifying the index of the value of u for \a D_i
2833   \param Dv  a vector specifying the index of the value of v for \a D_i
2834 
2835   \return 1 if the operation is possible, 0 if the problem is ill defined
2836                \e i.e. there isn't enough information to find a unique
2837 	       solution (the system is overdetermined) or that the system
2838 	       has non-independant components.
2839 
2840   \warning The vectors defining \a D_i(u,v) should all be of the
2841                same size.
2842 
2843   \author Philippe Lavoie
2844   \date 24 January 1997
2845 */
2846 template <class T, int N>
movePoint(const Vector<T> & ur,const Vector<T> & vr,const Vector<Point_nD<T,N>> & D,const Vector_INT & Du,const Vector_INT & Dv)2847 int NurbsSurface<T,N>::movePoint(const Vector<T>& ur, const Vector<T>& vr, const Vector< Point_nD<T,N> >& D, const Vector_INT& Du, const Vector_INT& Dv) {
2848   Vector_INT Dk(Du.n()),Dl(Dv.n()) ;
2849   BasicArray<Coordinate> fixCP(0) ;
2850   Dk.reset(0) ;
2851   Dl.reset(0) ;
2852   return movePoint(ur,vr,D,Du,Dv,Dk,Dl,fixCP) ;
2853 }
2854 
2855 /*!
2856   \brief Moves a point with some constraint
2857 
2858   This will modify the NURBS surface by respecting a certain
2859   number of constraints. \a u_r and \a v_r specifies the parameters
2860   on which the constraints should be applied.
2861   The constraint are defined by \a D_i^{(k,l)}(u,v) which requires
2862   5 vectors to fully qualify. \a D specifies the value
2863   of the constraint and \a Du and \a Dv are used to specify
2864   on which parameter the constraint is applied and
2865   \a Dk and \a Dl specify the partial degree of the constraint.
2866 
2867   The values in \a D should be ordered in respect with i,k and l.
2868   ur and vr should be in an increasing order.
2869 
2870   \param ur  the vector of parameters in the u direction
2871   \param vr  the vector of parameters in the v direction
2872   \param  D  a vector of the value of \a D_i^(k,l)(u,v)
2873   \param Du  a vector specifying the index of the value of u for \a D_i
2874   \param Dv  a vector specifying the index of the value of v for \a D_i
2875   \param Dk  a vector specifying the value of \a k for \a D_i
2876   \param Dl  a vector specifying the value of \a l for \a D_i
2877 
2878   \return 1 if the operation is possible, 0 if the problem is ill defined
2879                \e i.e. there isn't enough information to find a unique
2880 	       solution (the system is overdetermined) or that the system
2881 	       has non-independant components.
2882 
2883   \warning The vectors defining \a D_i^{(k,l)}(u,v) should all be of the
2884                same size.
2885 
2886   \author Philippe Lavoie
2887   \date 24 January 1997
2888 */
2889 template <class T, int N>
movePoint(const Vector<T> & ur,const Vector<T> & vr,const Vector<Point_nD<T,N>> & D,const Vector_INT & Du,const Vector_INT & Dv,const Vector_INT & Dk,const Vector_INT & Dl)2890 int NurbsSurface<T,N>::movePoint(const Vector<T>& ur, const Vector<T>& vr, const Vector< Point_nD<T,N> >& D, const Vector_INT& Du, const Vector_INT& Dv, const Vector_INT& Dk, const Vector_INT& Dl) {
2891   BasicArray<Coordinate> fixCP(0) ;
2892   return movePoint(ur,vr,D,Du,Dv,Dk,Dl,fixCP) ;
2893 }
2894 
2895 
2896 
2897 /*!
2898   \brief Moves a point with some constraint
2899 
2900   This will modify the NURBS surface by respecting a certain
2901   number of constraints. \a u_r and \a v_r specifies the parameters
2902   on which the constraints should be applied.
2903   The constraint are defined by $D_i^{(k,l)}(u,v)$ which requires
2904   5 vectors to fully qualify. \a D specifies the value
2905   of the constraint and \a Du and \a Dv are used to specify
2906   on which parameter the constraint is applied and
2907   \a Dk and \a Dl specify the partial degree of the constraint.
2908 
2909   A second constraint \a fixCP consists of specifying which
2910   control points can not be moved by the routine.
2911 
2912   The values in D should be ordered in respect with i,k and l.
2913   ur and vr should be in an increasing order.
2914 
2915   \param ur  the vector of parameters in the u direction
2916   \param vr  the vector of parameters in the v direction
2917   \param  D  a vector of the value of \a D_i^(k,l)(u,v)
2918   \param Du  a vector specifying the index of the value of u for \a D_i
2919   \param Dv  a vector specifying the index of the value of v for \a D_i
2920   \param Dk  a vector specifying the value of \a k for \a D_i
2921   \param Dl  a vector specifying the value of \a l for \a D_i
2922   \param fixCP  a vector specifying which control points can \e not be
2923 	              modified.
2924 
2925   \return 1 if the operation is possible, 0 if the problem is ill defined
2926                \e i.e. there isn't enough information to find a unique
2927 	       solution (the system is overdetermined) or that the system
2928 	       has non-independant components.
2929 
2930   \warning The vectors defining $D_i^{(k,l)}(u,v)$ should all be of the
2931                same size.
2932 
2933   \author Philippe Lavoie
2934   \date 24 January 1997
2935 */
2936 template <class T, int N>
movePoint(const Vector<T> & ur,const Vector<T> & vr,const Vector<Point_nD<T,N>> & D,const Vector_INT & Du,const Vector_INT & Dv,const Vector_INT & Dk,const Vector_INT & Dl,const BasicArray<Coordinate> & fixCP)2937 int NurbsSurface<T,N>::movePoint(const Vector<T>& ur, const Vector<T>& vr, const Vector< Point_nD<T,N> >& D, const Vector_INT& Du, const Vector_INT& Dv, const Vector_INT& Dk, const Vector_INT& Dl, const BasicArray<Coordinate>& fixCP) {
2938   int i,j,k,n ;
2939 
2940   if(D.n() != Du.n() || D.n() !=Du.n() || D.n() != Dv.n() || D.n() != Dv.n()){
2941 #ifdef USE_EXCEPTION
2942     throw NurbsInputError();
2943 #else
2944     Error err("movePoint(ur,D,Dr,Dk,fixCP)");
2945     err << "The D,Dr,Dk vectors are not of the same size\n" ;
2946     err << "D.n()= " << D.n() << ", Du.n() = " << Du.n()
2947 	<< ", Dk.n() = " << Dk.n() << ", Dv.n() = " << Dv.n()
2948 	<< ", Dl.n() = " << Dl.n() << endl ;
2949     err.fatal() ;
2950 #endif
2951   }
2952 
2953   // setup B
2954   Matrix_DOUBLE B ;
2955 
2956   B.resize(D.n(),P.rows()*P.cols()) ;
2957 
2958   int spanU,spanV ;
2959 
2960   Matrix<T> Ru,Rv ;
2961 
2962   B.reset(0.0) ;
2963 
2964   for(i=0;i<D.rows();++i){
2965     findSpan(ur[Du[i]],vr[Dv[i]],spanU,spanV) ;
2966     nurbsDersBasisFuns(Dk[i],ur[Du[i]],spanU,degU,U,Ru) ;
2967     nurbsDersBasisFuns(Dl[i],vr[Dv[i]],spanV,degV,V,Rv) ;
2968     for(j=0;j<=degU;++j){
2969       for(k=0;k<=degV;++k){
2970 	B(i,(spanU-degU+j)*P.cols()+spanV-degV+k)
2971 	  = (double)Ru(Dk[i],j)*(double)Rv(Dl[i],k) ;
2972       }
2973     }
2974   }
2975 
2976   // optimize B
2977   Vector_INT remove(B.cols()) ;
2978   BasicArray<Coordinate> map(B.cols()) ;
2979   remove.reset((int)1.0) ;
2980 
2981   for(j=0;j<B.cols();++j){
2982     for(i=0;i<B.rows();++i)
2983       if((B(i,j)*B(i,j))>1e-10){
2984 	remove[j] = 0 ;
2985 	break ;
2986       }
2987   }
2988 
2989   for(i=0;i<fixCP.n();++i){
2990     remove[fixCP[i].i*P.cols()+fixCP[i].j] = 1 ;
2991   }
2992 
2993   n = 0 ;
2994   for(i=0;i<B.cols();++i){
2995     if(!remove[i]){
2996       map[n].i = i/P.cols() ;
2997       map[n].j = i%P.cols() ;
2998       ++n ;
2999     }
3000   }
3001 
3002   map.resize(n) ;
3003 
3004   Matrix_DOUBLE Bopt(B.rows(),n) ;
3005   for(j=0;j<n;++j){
3006     for(i=0;i<B.rows();++i)
3007       Bopt(i,j) = B(i,map[j].i*P.cols()+map[j].j) ;
3008   }
3009 
3010   Matrix_DOUBLE A  ;
3011   Matrix_DOUBLE Bt(::transpose(Bopt)) ;
3012   Matrix_DOUBLE BBt ;
3013 
3014   BBt = inverse(Bopt*Bt) ;
3015 
3016   A = Bt*BBt ;
3017 
3018   Matrix_DOUBLE dD(D.rows(),3) ;
3019 
3020   for(i=0;i<D.rows();++i){
3021     const Point_nD<T,N>& d = D[i] ; // this makes the SGI compiler happy
3022     for(j=0;j<3;++j)
3023       dD(i,j) = (double)d.data[j] ;
3024   }
3025 
3026   Matrix_DOUBLE dP ;
3027 
3028   dP = A*dD ;
3029 
3030   for(i=0;i<map.n();++i){
3031     P(map[i].i,map[i].j).x() += dP(i,0)*P(map[i].i,map[i].j).w() ;
3032     P(map[i].i,map[i].j).y() += dP(i,1)*P(map[i].i,map[i].j).w() ;
3033     P(map[i].i,map[i].j).z() += dP(i,2)*P(map[i].i,map[i].j).w() ;
3034   }
3035 
3036   return 1 ;
3037 }
3038 
3039 
3040 /*!
3041   \brief Projects a point on a line
3042 
3043   \param S a point on the line
3044   \param T the tangent vector of the line
3045   \param pnt the point to project
3046   \param p the point projected on the line
3047 
3048   \author Philippe Lavoie
3049   \date 25 July, 1997
3050 */
3051 template <class T, int N>
projectToLine(const Point_nD<T,N> & S,const Point_nD<T,N> & Trj,const Point_nD<T,N> & pnt,Point_nD<T,N> & p)3052 void projectToLine(const Point_nD<T,N>& S, const Point_nD<T,N>& Trj, const Point_nD<T,N>& pnt, Point_nD<T,N>& p) {
3053 
3054   Point_nD<T,N> a = pnt-S ;
3055   //p = S+ norm(a)*cos(angle(a,Trj))*Trj.unitLength() ;
3056   T fraction, denom ;
3057   denom = norm2(Trj) ;
3058   fraction = (denom == 0.0) ? 0.0 : (Trj*a) / denom ;
3059   p =  fraction * Trj ;
3060   p += S ;
3061 }
3062 
3063 /*!
3064   \brief Generates a surface of revolution
3065 
3066   Generates a surface of revolution of a profile around an
3067   arbitrary axis (specified by a starting point S and a
3068   tangent T) with a certain angle.
3069 
3070   The angle is specified in radians.
3071 
3072   \param profile  the curves to rotate around the axis
3073   \param S  a point on the axis
3074   \param T  the tangent vector of the rotation axis
3075   \param theta  the angle of the revolution (in radians)
3076 
3077   \warning If a point is within a distance of 1e-7 from the axis,
3078            it will be assumed to be on the axis. The angle theta
3079 	   is only valid in the range [0,2 PI].
3080 
3081   \author Philippe Lavoie
3082   \date 7 October, 1997
3083 */
3084 template <class T, int N>
makeFromRevolution(const NurbsCurve<T,N> & profile,const Point_nD<T,N> & S,const Point_nD<T,N> & Tvec,double theta)3085 void NurbsSurface<T,N>::makeFromRevolution(const NurbsCurve<T,N>& profile, const Point_nD<T,N>& S, const Point_nD<T,N>& Tvec, double theta){
3086   double angle,dtheta ;
3087   int narcs ;
3088   int i,j ;
3089 
3090   //while(theta>2.0*M_PI)
3091   //  theta -= 2.0*M_PI ;
3092 
3093   if(theta>2.0*M_PI)
3094     theta = 2.0*M_PI ;
3095 
3096   if(theta <= 0)
3097     theta = 0 ;
3098 
3099   if(theta<M_PI/2.0)
3100     narcs = 1 ;
3101   else{
3102     if(theta<M_PI)
3103       narcs = 2 ;
3104     else{
3105       if(theta<1.5*M_PI)
3106 	narcs = 3 ;
3107       else
3108 	narcs = 4 ;
3109     }
3110   }
3111   dtheta = theta/(double)narcs ;
3112 
3113   int n = 2*narcs+1 ; // n control points ;
3114   resize(n,profile.ctrlPnts().n(),2,profile.degree()) ;
3115 
3116   switch(narcs){
3117   case 1: break ;
3118   case 2:
3119     U[3] = U[4] = 0.5 ;
3120     break ;
3121   case 3:
3122     U[3] = U[4] = 1.0/3.0 ;
3123     U[5] = U[6] = 2.0/3.0 ;
3124     break ;
3125   case 4:
3126     U[3] = U[4] = 0.25 ;
3127     U[5] = U[6] = 0.50 ;
3128     U[7] = U[8] = 0.75 ;
3129     break ;
3130   }
3131 
3132   j = 3 + 2*(narcs-1) ;  // loading the end knots
3133   for(i=0;i<3;++j,++i){
3134     U[i] = 0.0 ;
3135     U[j] = 1.0 ;
3136   }
3137 
3138   V = profile.knot() ;
3139 
3140   double wm = cos(dtheta/2.0) ; // dtheta/2.0 is base angle
3141 
3142   Vector_DOUBLE cosines(narcs+1),sines(narcs+1) ;
3143   angle = 0 ;
3144   for(i=1;i<=narcs;++i){
3145     angle = dtheta*(double)i ;
3146     cosines[i] = cos(angle) ;
3147     sines[i] = sin(angle) ;
3148   }
3149 
3150   Point_nD<T,N> P0,T0,P2,T2,P1 ;
3151 
3152   for(j=0;j<P.cols();++j){
3153     Point_nD<T,N> O ;
3154     T wj = profile.ctrlPnts(j).w() ;
3155 
3156     projectToLine(S,Tvec,project(profile.ctrlPnts(j)),O) ;
3157     //projectToLine(S,Tvec,profile.ctrlPnts(j).projectW(),O) ;
3158     Point_nD<T,N> X,Y ;
3159     X = project(profile.ctrlPnts(j))-O ;
3160     //X = profile.ctrlPnts(j).projectW() - O ;
3161 
3162     double r = norm(X) ;
3163 
3164     if(r < 1e-7){
3165       for(i=0;i<P.rows();++i){
3166 	P(i,j) = O ;
3167 	P(i,j) *= wj ;
3168       }
3169       continue ;
3170     }
3171 
3172     T b1 = X.norm2() ;
3173     T b2 = X.norm() ;
3174     T b3 = sqrt(b1) ;
3175     T b4 = norm(X) ;
3176 
3177     X = X.unitLength() ;
3178     Y = crossProduct(Tvec,X) ;
3179     Y = Y.unitLength() ;
3180 
3181     P0 = project(profile.ctrlPnts(j)) ;
3182     //P0 = profile.ctrlPnts(j).projectW() ;
3183     P(0,j) = profile.ctrlPnts(j) ;
3184 
3185     T0 = Y ;
3186     int index = 0 ;
3187 
3188     for(i=1;i<=narcs;++i){
3189       angle = dtheta*(double)i ;
3190       P2 = O+ r*cosines[i]*X + r*sines[i]*Y ;
3191       //P2 = O+ r*cos(angle)*X + r*sin(angle)*Y ;
3192       P(index+2,j) = P2 ;
3193       P(index+2,j) *= wj ;
3194       T2 = -sines[i]*X + cosines[i]*Y ;
3195       //T2 = -sin(angle)*X + cos(angle)*Y ;
3196       intersectLine(P0,T0,P2,T2,P1) ;
3197       P(index+1,j) = P1 ;
3198       P(index+1,j) *= wm*wj ;
3199       index += 2 ;
3200       P0 = P2 ;
3201       T0 = T2 ;
3202     }
3203   }
3204 }
3205 
3206 /*!
3207   \brief Generates a surface of revolution
3208 
3209   Generates a surface of revolution of a profile around an
3210   arbitrary axis (specified by a starting point S and a
3211   tangent T).
3212 
3213   \param profile  the curves to rotate around the axis
3214   \param S  a point on the axis
3215   \param T  the tangent vector of the rotation axis
3216 
3217   \warning If a point is within a distance of 1e-7 from the axis,
3218            it will be assumed to be on the axis.
3219 
3220   \author Philippe Lavoie
3221   \date 3 May, 1999
3222 */
3223 template <class T, int N>
makeFromRevolution(const NurbsCurve<T,N> & profile,const Point_nD<T,N> & S,const Point_nD<T,N> & Tvec)3224 void NurbsSurface<T,N>::makeFromRevolution(const NurbsCurve<T,N>& profile, const Point_nD<T,N>& S, const Point_nD<T,N>& Tvec){
3225   double angle,dtheta ;
3226   int narcs ;
3227   int i,j ;
3228 
3229   int n = 9 ; // n control points ;
3230   resize(n,profile.ctrlPnts().n(),2,profile.degree()) ;
3231 
3232   U[0] = U[1] = U[2] = 0 ;
3233   U[3] = U[4] = 0.25 ;
3234   U[5] = U[6] = 0.50 ;
3235   U[7] = U[8] = 0.75 ;
3236   U[9] = U[10] = U[11] = 1 ;
3237 
3238   V = profile.knot() ;
3239 
3240 
3241   const T wm = T(0.707106781185) ; // sqrt(2)/2
3242 
3243   for(j=0;j<P.cols();++j){
3244     Point_nD<T,N> O ;
3245     T wj = profile.ctrlPnts(j).w() ;
3246 
3247     projectToLine(S,Tvec,project(profile.ctrlPnts(j)),O) ;
3248     //projectToLine(S,Tvec,profile.ctrlPnts(j).projectW(),O) ;
3249     Point_nD<T,N> X,Y ;
3250     X = project(profile.ctrlPnts(j))-O ;
3251     //X = profile.ctrlPnts(j).projectW() - O ;
3252 
3253     double r = norm(X) ;
3254 
3255     if(r < 1e-7){
3256       for(i=0;i<P.rows();++i){
3257 	P(i,j) = O ;
3258 	P(i,j) *= wj ;
3259       }
3260       continue ;
3261     }
3262 
3263     X = X.unitLength() ;
3264     Y = crossProduct(Tvec,X) ;
3265     Y = Y.unitLength() ;
3266 
3267     P(0,j) = O + r*X ;
3268     P(1,j) = O + r*X + r*Y ;
3269     P(2,j) = O + r*Y ;
3270     P(3,j) = O - r*X + r*Y ;
3271     P(4,j) = O - r*X ;
3272     P(5,j) = O - r*X - r*Y ;
3273     P(6,j) = O - r*Y ;
3274     P(7,j) = O + r*X - r*Y ;
3275     P(8,j) = P(0,j) ;
3276     P(0,j) *= wj ;
3277     P(1,j) *= wj*wm ;
3278     P(2,j) *= wj ;
3279     P(3,j) *= wj*wm ;
3280     P(4,j) *= wj ;
3281     P(5,j) *= wj*wm ;
3282     P(6,j) *= wj ;
3283     P(7,j) *= wj*wm ;
3284     P(8,j) *= wj ;
3285   }
3286 }
3287 
3288 /*!
3289   \brief Generates a surface of revolution
3290 
3291   Generates a surface of revolution of a profile around the z axis.
3292 
3293   \param profile  the curves to rotate around the z-axis
3294 
3295   \author Philippe Lavoie
3296   \date 3 May, 1999
3297 */
3298 template <class T, int N>
makeFromRevolution(const NurbsCurve<T,N> & profile)3299 void NurbsSurface<T,N>::makeFromRevolution(const NurbsCurve<T,N>& profile){
3300   int j ;
3301   int n = 9 ; // n control points ;
3302   resize(n,profile.ctrlPnts().n(),2,profile.degree()) ;
3303 
3304   U[0] = U[1] = U[2] = 0 ;
3305   U[3] = U[4] = 0.25 ;
3306   U[5] = U[6] = 0.50 ;
3307   U[7] = U[8] = 0.75 ;
3308   U[9] = U[10] = U[11] = 1 ;
3309 
3310   V = profile.knot() ;
3311 
3312 
3313   const T wm = T(0.707106781185) ; // sqrt(2)/2
3314 
3315   for(j=0;j<P.cols();++j){
3316     T wj = profile.ctrlPnts(j).w() ;
3317 
3318     Point_nD<T,N> p = project(profile.ctrlPnts(j)) ;
3319     T r = T(sqrt(p.x()*p.x()+p.y()*p.y()));
3320 
3321     T wjm = wj*wm ;
3322     T rwjm = r*wj*wm ;
3323     T rwj = r*wj ;
3324     p.z() *= wj ;
3325 
3326     P(0,j) = HPoint_nD<T,N>(rwj,0,p.z(),wj) ;
3327     P(1,j) = HPoint_nD<T,N>(rwjm,rwjm,p.z()*wm,wjm) ;
3328     P(2,j) = HPoint_nD<T,N>(0,rwj,p.z(),wj) ;
3329     P(3,j) = HPoint_nD<T,N>(-rwjm,rwjm,p.z()*wm,wjm) ;
3330     P(4,j) = HPoint_nD<T,N>(-rwj,0,p.z(),wj) ;
3331     P(5,j) = HPoint_nD<T,N>(-rwjm,-rwjm,p.z()*wm,wjm) ;
3332     P(6,j) = HPoint_nD<T,N>(0,-rwj,p.z(),wj) ;
3333     P(7,j) = HPoint_nD<T,N>(rwjm,-rwjm,p.z()*wm,wjm) ;
3334     P(8,j) = HPoint_nD<T,N>(rwj,0,p.z(),wj) ;
3335   }
3336 }
3337 
3338 /*!
3339   \brief Generates an iso curve in the U direction
3340 
3341   Generates an iso-parametric curve which goes through the
3342   parametric value u along the U direction.
3343 
3344   \param u  the U parametric value
3345   \param c  the iso-parametric curve
3346 
3347   \warning the parametric value $u$ must be in a valid range
3348 
3349   \author Philippe Lavoie
3350   \date 7 October, 1997
3351 */
3352 template <class T, int D>
isoCurveU(T u,NurbsCurve<T,D> & c) const3353 void NurbsSurface<T,D>::isoCurveU(T u, NurbsCurve<T,D>& c) const {
3354   c.resize(P.cols(),degV) ;
3355 
3356   c.modKnot(V) ;
3357 
3358   if(u>U[U.n()-1])
3359     u = U[U.n()-1] ;
3360   if(u<U[0])
3361     u = U[0] ;
3362 
3363 
3364 
3365   int span = findSpanU(u) ;
3366 
3367   Vector<T> N ;
3368   basisFunsU(u,span,N) ;
3369 
3370   HPoint_nD<T,D> p ;
3371   for(int i=0;i<P.cols();++i){
3372     p = 0 ;
3373     for(int j=0;j<=degU;++j)
3374       p += N[j]*P(span-degU+j,i) ;
3375     c.modCP(i,p) ;
3376   }
3377 
3378 }
3379 
3380 /*!
3381   \brief Generates an iso curve in the V direction
3382 
3383   Generates an iso-parametric curve which goes through the
3384   parametric value v along the V direction.
3385 
3386   \param v  the V parametric value
3387   \param c  the iso-parametric curve
3388 
3389   \warning the parametric value $v$ must be in a valid range
3390 
3391   \author Philippe Lavoie
3392   \date 7 October, 1997
3393 */
3394 template <class T, int D>
isoCurveV(T v,NurbsCurve<T,D> & c) const3395 void NurbsSurface<T,D>::isoCurveV(T v, NurbsCurve<T,D>& c) const {
3396   c.resize(P.rows(),degU) ;
3397 
3398   c.modKnot(U) ;
3399 
3400   if(v>V[V.n()-1])
3401     v = V[V.n()-1] ;
3402   if(v<V[0])
3403     v = V[0] ;
3404 
3405   int span = findSpanV(v) ;
3406 
3407   Vector<T> N ;
3408   basisFunsV(v,span,N) ;
3409 
3410   HPoint_nD<T,D> p ;
3411   for(int i=0;i<P.rows();++i){
3412     p = 0  ;
3413     for(int j=0;j<=degV;++j)
3414       p += N[j]*P(i,span-degV+j) ;
3415     c.modCP(i,p) ;
3416   }
3417 }
3418 
3419 /*!
3420   \brief Decompose the surface into B�zier patches
3421 
3422   This function decomposes the curve into an array of homogenous B�zier
3423   patches.
3424 
3425   \param S  an array of B�zier segments
3426   \return The number of B�zier strips in the u direction.
3427 
3428   \author Philippe Lavoie
3429   \date 8 October, 1997
3430 */
3431 template <class T, int N>
decompose(NurbsSurfaceArray<T,N> & S) const3432 int NurbsSurface<T,N>::decompose(NurbsSurfaceArray<T,N>& S) const {
3433   int i,m,a,b,nb,mult,j,r,save,s,k,row,col ;
3434   T numer,alpha ;
3435 
3436 
3437   //Vector<T> alphas(degU+1) ;
3438   T* alphas = (T*) alloca((maximum(degU,degV)+1)*sizeof(T)) ;
3439   // all the surfaces will have the same knot vector in both the U and V
3440   // direction
3441   Vector<T> nU ;
3442   nU.resize(2*(degU+1)) ;
3443   for(i=0;i<nU.n()/2;++i)
3444     nU[i] = 0 ;
3445   for(i=nU.n()/2;i<nU.n();++i)
3446     nU[i] = 1 ;
3447 
3448   Vector<T> nV ;
3449   nV.resize(2*(degV+1)) ;
3450   for(i=0;i<nV.n()/2;++i)
3451     nV[i] = 0 ;
3452   for(i=nV.n()/2;i<nV.n();++i)
3453     nV[i] = 1 ;
3454 
3455   NurbsSurfaceArray<T,N> Su ;
3456   Su.resize(P.rows()-degU) ; // )*(P.cols()-degV)) ;
3457   for(i=0;i<Su.n();i++){
3458     Su[i].resize(degU+1,P.cols(),degU,degV) ;
3459     Su[i].U = nU ;
3460     Su[i].V = V ;
3461   }
3462 
3463   m = P.rows()+degU ;
3464   a = degU ;
3465   b = degU+1 ;
3466   nb = 0 ;
3467 
3468   for(i=0;i<=degU;i++)
3469     for(col=0;col<P.cols();col++)
3470       Su[nb].P(i,col) = P(i,col) ;
3471   while(b<m){
3472     i = b ;
3473     while(b<m && U[b+1] <= U[b]) b++ ;
3474     mult = b-i+1 ;
3475     if(mult<degU){
3476       numer = U[b]-U[a] ; // the enumerator of the alphas
3477       for(j=degU;j>mult;j--) // compute and store the alphas
3478 	alphas[j-mult-1] = numer/(U[a+j]-U[a]) ;
3479       r = degU-mult ; // insert knot r times
3480       for(j=1;j<=r;j++){
3481 	save=r-j;
3482 	s=mult+j; // this many new points
3483 	for(k=degU;k>=s;k--){
3484 	  alpha = alphas[k-s] ;
3485 	  for(col=0;col<P.cols();++col)
3486 	    Su[nb].P(k,col) = alpha*Su[nb].P(k,col)+(1.0-alpha)*Su[nb].P(k-1,col);
3487 	}
3488 	if(b<m) // control point of next patch
3489 	  for(col=0;col<P.cols();++col)
3490 	    Su[nb+1].P(save,col) = Su[nb].P(degU,col) ;
3491       }
3492     }
3493     ++nb ;
3494     if(b<m){ // initialize for next segment
3495       for(i=degU-mult; i<= degU ; ++i)
3496 	for(col=0;col<P.cols();++col)
3497 	  Su[nb].P(i,col) = P(b-degU+i,col) ;
3498       a=b ;
3499       ++b ;
3500     }
3501   }
3502   Su.resize(nb) ;
3503 
3504   S.resize(Su.n()*(P.cols()-degV)) ;
3505 
3506   for(i=0;i<S.n();i++){
3507     S[i].resize(degU+1,degV+1,degU,degV) ;
3508     S[i].U = nU ;
3509     S[i].V = nV ;
3510   }
3511 
3512   nb = 0 ;
3513 
3514   for(int np=0;np<Su.n();++np){
3515     for(i=0;i<=degU;i++)
3516       for(j=0;j<=degV;++j)
3517 	S[nb].P(i,j) = Su[np].P(i,j) ;
3518     m = P.cols()+degV ;
3519     a = degV ;
3520     b = degV+1 ;
3521     while(b<m){
3522       i = b ;
3523       while(b<m && V[b+1] <= V[b]) b++ ;
3524       mult = b-i+1 ;
3525       if(mult<degV){
3526 	numer = V[b]-V[a] ; // the enumerator of the alphas
3527 	for(j=degV;j>mult;j--) // compute and store the alphas
3528 	  alphas[j-mult-1] = numer/(V[a+j]-V[a]) ;
3529 	r = degV-mult ; // insert knot r times
3530 	for(j=1;j<=r;j++){
3531 	  save=r-j;
3532 	  s=mult+j; // this many new points
3533 	  for(k=degV;k>=s;k--){
3534 	    alpha = alphas[k-s] ;
3535 	    for(row=0;row<=degU;++row)
3536 	      S[nb].P(row,k) = alpha*S[nb].P(row,k)+(1.0-alpha)*S[nb].P(row,k-1);
3537 	  }
3538 	  if(b<m) // control point of next patch
3539 	    for(row=0;row<=degU;++row)
3540 	      S[nb+1].P(row,save) = S[nb].P(row,degV) ;
3541 	}
3542       }
3543       ++nb ;
3544       if(b<m){ // initialize for next patch
3545 	for(i=degV-mult; i<= degV ; ++i)
3546 	  for(row=0;row<=degU;++row)
3547 	    S[nb].P(row,i) = Su[np].P(row,b-degV+i) ;
3548 	a=b ;
3549 	++b ;
3550       }
3551     }
3552   }
3553 
3554   S.resize(nb) ;
3555 
3556   return Su.n() ;
3557 }
3558 
3559 /*!
3560   \brief Writes a set of povray bicubic patches to the ostream.
3561 
3562   \param patch_type  the type of the patch
3563   \param flatness  the flatness level
3564   \param  num_u_steps  the minimum number of triangles in the U
3565 	                   direction
3566   \param num_v_steps  the minimum number of triangles in the V
3567 	                   direction
3568   \param povray  the output stream
3569 
3570   \return 1 on success, 0 on failure.
3571 
3572   \warning POVRAY only accepts rational spline patches. Thus you can't
3573                have a value other then 1.0 for the weights of your surface.
3574 	       A warning message will be generated if this is the case.
3575 
3576 	       POVRAY only deals with surfaces of degree 3. If the surface
3577 	       as a degree below 3 either in the U or V direction it will be
3578 	       elevated to be at 3 in both directions. If the surface as a
3579 	       degree higher then 3, then the function aborts.
3580 
3581   \author Philippe Lavoie
3582   \date 8 October, 1997
3583 */
3584 template <class T, int N>
writePOVRAY(ostream & povray,int patch_type,double flatness,int num_u_steps,int num_v_steps) const3585 int NurbsSurface<T,N>::writePOVRAY(ostream& povray, int patch_type, double flatness, int num_u_steps, int num_v_steps) const {
3586   if(degU>3 || degV>3){
3587 #ifdef USE_EXCEPTION
3588     throw NurbsInputError();
3589 #else
3590     Error err("NurbsSurface<T,N> writePOVRAY") ;
3591     err << "The degree of the surface is higher than 3!\n"
3592 	<< "A povrary file can not be generated.\n" ;
3593     err.warning() ;
3594     return 0;
3595 #endif
3596   }
3597 
3598   NurbsSurface<T,N> S(*this) ;
3599 
3600   S.degreeElevate(3-degU,3-degV) ;
3601 
3602   NurbsSurfaceArray<T,N> Sa ;
3603   S.decompose(Sa) ;
3604   int bad = 0 ;
3605 
3606   povray << "//\n//Generated for POV-Ray(tm) 3.0 by Phil's NURBS library\n" ;
3607   povray << "//   http://yukon.genie.uottawa.ca/info/soft/nurbs\n//\n" ;
3608 
3609   for(int i=0;i<Sa.n();++i){
3610     povray << "bicubic_patch {\n\ttype " << patch_type << endl ;
3611     povray << "\tflatness " << flatness << endl ;
3612     povray << "\tu_steps " << num_u_steps << endl ;
3613     povray << "\tv_steps " << num_v_steps << endl ;
3614     for(int j=0;j<4;++j){
3615       for(int k=0;k<4;++k){
3616 	Point_nD<T,N> p = project(Sa[i].ctrlPnts(j,k)) ;
3617 	if(Sa[i].ctrlPnts(j,k).w()>1.0001 || Sa[i].ctrlPnts(j,k).w()<0.9999)
3618 	  bad = 1 ;
3619 	povray << "\t<" << p.x() << ", " << p.y() << ", " << p.z() << ">" ;
3620 	if(j==3 && k==3)
3621 	  povray << "\n}\n\n" ;
3622 	else
3623 	  povray << ",\n " ;
3624       }
3625       povray << endl ;
3626     }
3627   }
3628   if(bad){
3629 #ifdef USE_EXCEPTION
3630     throw NurbsWarning();
3631 #else
3632     Error err("NurbsSurface<T,N> writePOVRAY") ;
3633     err << "Warning: at least one of the control point was not rational\n" ;
3634     err << "The resulting surface will NOT be the same as the one which\n" ;
3635     err << "generated it.\n" ;
3636     err.warning() ;
3637 #endif
3638   }
3639   return bad ;
3640 }
3641 
3642 
3643 /*!
3644   \brief Writes the surface as a mesh of triangles
3645 
3646   Writes the surface as a mesh of triangles. You might have
3647   to change the values for the tolerance to get exactly what
3648   you're looking for.
3649 
3650   \param povray  the output stream
3651   \param tolerance  the tolerance when performing the tesselation
3652   \param color  the color of the object
3653   \param diffuse  the diffuse factor
3654   \param ambient  the ambient factor
3655   \param smooth  flags which indicates if we generate smooth triangles
3656 
3657   \return 1 on success, 0 on failure
3658 
3659   \warning It doesn't work very well.
3660 
3661   \author Philippe Lavoie
3662   \date 8 October, 1997
3663 */
3664 template <class T, int N>
writePOVRAY(T tolerance,ostream & povray,const Color & color,int smooth,T ambient,T diffuse) const3665 int NurbsSurface<T,N>::writePOVRAY(T tolerance, ostream& povray,const Color& color, int smooth, T ambient, T diffuse) const {
3666   BasicList<Point_nD<T,N> > points ;
3667   BasicList<int> connect ;
3668   BasicList<Point_nD<T,N> > norm ;
3669 
3670   if(smooth)
3671     tesselate(tolerance,points,connect,&norm) ;
3672   else
3673     tesselate(tolerance,points,connect) ;
3674 
3675   povray << "mesh {\n" ;
3676 
3677   BasicNode<int> *node ;
3678 
3679   BasicNode<Point_nD<T,N> > *nodeP ;
3680   BasicNode<Point_nD<T,N> > *nodeN ;
3681   nodeP = points.goToFirst() ;
3682   nodeN = 0 ;
3683 
3684   if(smooth)
3685     nodeN = norm.goToFirst() ;
3686 
3687   Vector< Point_nD<T,N> > Pts(points.size()) ;
3688   Vector< Point_nD<T,N> > Norm(norm.size()) ;
3689   for(int i=0;i<Pts.n();++i){
3690     Pts[i] = *nodeP->data ;
3691     nodeP = points.goToNext() ;
3692     if(smooth){
3693       Norm[i] = *nodeN->data ;
3694       nodeN = norm.goToNext() ;
3695     }
3696   }
3697 
3698   node = connect.goToFirst() ;
3699   while(node){
3700     if(smooth)
3701       povray << "\tsmooth_triangle { " ;
3702     else
3703       povray << "\ttriangle { " ;
3704     povray << "< " << Pts[*node->data].x() << ", " << Pts[*node->data].y() << ", " << Pts[*node->data].z() << "> , " ;
3705     if(smooth)
3706       povray << "< " << Norm[*node->data].x() << ", " << Norm[*node->data].y() << ", " << Norm[*node->data].z() << "> , " ;
3707     node = connect.goToNext() ;
3708     povray << "< " << Pts[*node->data].x() << ", " << Pts[*node->data].y() << ", " << Pts[*node->data].z() << "> , " ;
3709     if(smooth)
3710       povray << "< " << Norm[*node->data].x() << ", " << Norm[*node->data].y() << ", " << Norm[*node->data].z() << "> , " ;
3711     node = connect.goToNext() ;
3712     povray << "< " << Pts[*node->data].x() << ", " << Pts[*node->data].y() << ", " << Pts[*node->data].z() << "> " ;
3713     if(smooth)
3714       povray << ", < " << Norm[*node->data].x() << ", " << Norm[*node->data].y() << ", " << Norm[*node->data].z() << "> " ;
3715     node = connect.goToNext() ; // skip the -1 value
3716     node = connect.goToNext() ;
3717     povray << "}\n" ;
3718   }
3719 
3720   povray << "\ttexture{ pigment { rgb < " << (double)color.r/255.0 << ", " << (double)color.g/255.0 << ", " << (double)color.b/255.0 << " >}\n" ;
3721   povray << "\t\tfinish { ambient " << ambient << " diffuse " << diffuse << " }\n\t}\n" ;
3722 
3723   povray << "}\n\n" ;
3724   return povray.good() ;
3725 }
3726 
3727 /*!
3728   \brief Writes a set of povray bicubic patches
3729 
3730   \param patch_type  the type of the patch
3731   \param flatness  the flatness level
3732   \param num_u_steps  the minimum number of triangles in the U direction
3733   \param num_v_steps  the minimum number of triangles in the V direction
3734 
3735   \return an ostream containing the definition of the surface
3736 
3737   \warning POVRAY only accepts rational spline patches. Thus you can't
3738                have a value other then 1.0 for the weights of your surface.
3739 	       A warning message will be generated if this is the case.
3740 
3741 	       POVRAY only deals with surfaces of degree 3. If the surface
3742 	       as a degree below 3 either in the U or V direction it will be
3743 	       elevated to be at 3 in both directions. If the surface as a
3744 	       degree higher then 3, then the function aborts.
3745 
3746   \author Philippe Lavoie
3747   \date 8 October, 1997
3748 */
3749 template <class T, int N>
writePOVRAY(const char * filename,const Color & col,const Point_nD<T,N> & cView,const Point_nD<T,N> & up,int patch_type,double flatness,int num_u_steps,int num_v_steps) const3750 int NurbsSurface<T,N>::writePOVRAY(const char *filename, const Color& col, const Point_nD<T,N>& cView, const Point_nD<T,N>& up, int patch_type, double flatness, int num_u_steps, int num_v_steps) const {
3751   ofstream fout(filename) ;
3752   if(!fout)
3753     return 0 ;
3754 
3755   Point_nD<T,N> view(T(-1.0)*cView) ;
3756 
3757   fout << "//\n//Generated for POV-Ray(tm) 3.0 by Phil's NURBS library\n//\n" ;
3758   fout << "\n#include \"colors.inc\"\n" ;
3759 
3760   // we want the camera to look at the center of the object
3761   // and be able to view the whole object
3762   // we use and angle of 36 to view the object
3763   // and position the rest according to this.
3764   Point_nD<T,N> minP, maxP ;
3765   minP.x() = this->extremum(1,coordX) ;
3766   minP.y() = this->extremum(1,coordY) ;
3767   minP.z() = this->extremum(1,coordZ) ;
3768   maxP.x() = this->extremum(0,coordX) ;
3769   maxP.y() = this->extremum(0,coordY) ;
3770   maxP.z() = this->extremum(0,coordZ) ;
3771 
3772   Point_nD<T,N> lookAt  ;
3773   lookAt.x() = (minP.x()+maxP.x())/2.0 ;
3774   lookAt.y() = (minP.y()+maxP.y())/2.0 ;
3775   lookAt.z() = (minP.z()+maxP.z())/2.0 ;
3776 
3777   Point_nD<T,N> camera1, camera2 ;
3778 
3779   Point_nD<T,N> q1 = minP-lookAt ;
3780   Point_nD<T,N> q2 = maxP-lookAt ;
3781   T D1 = absolute(dot(q1,view))/norm(view) ;
3782   T D2 = absolute(dot(q2,view))/norm(view) ;
3783 
3784   T a1 = norm(q1)*cos(angle(view,q1)) ;
3785   T a2 = norm(q2)*cos(angle(view,q2)) ;
3786 
3787   T b1 = D1/tan(18.0*M_PI/180.0) ;
3788   T b2 = D2/tan(18.0*M_PI/180.0) ; // this gives the 36 degree angle
3789 
3790   camera1 = lookAt+(a1+b1)*view.unitLength() ;
3791   camera2 = lookAt+(a2+b2)*view.unitLength() ;
3792 
3793   Point_nD<T,N> right ;
3794 
3795   right = crossProduct(view,up) ; // inversed because pov-ray uses a left-handed system
3796 
3797   fout << "camera {\n\tlocation <" ;
3798   if(norm2(camera1-lookAt)>norm2(camera2-lookAt))
3799     fout << camera1.x() << ", " << camera1.y() << ", " << camera1.z() << ">\n" ;
3800   else
3801     fout << camera2.x() << ", " << camera2.y() << ", " << camera2.z() << ">\n" ;
3802   fout << "\tup < " << up.x() << ", " << up.y() << ", " << up.z() << ">\n" ;
3803   fout << "\tright < " << right.x() << ", " << right.y() << ", " << right.z() << ">\n" ;
3804   fout << "\tlook_at < " << lookAt.x() << ", " << lookAt.y() << ", " << lookAt.z() << ">\n\tangle 36\n}\n\n" ;
3805 
3806   fout << "union {\n" ;
3807   writePOVRAY(fout,patch_type,flatness,num_u_steps,num_v_steps) ;
3808   fout << " texture {\n\tpigment {\n\t\tcolor rgb < " << (double)col.r/255.0 <<
3809     ", " << (double)col.g/255.0 << ", " << (double)col.b/255.0 << "> \n" <<
3810     "\t}\n\tfinish { \n\t\tambient .2\n\t\tdiffuse .6\n\t}\n }\n" ;
3811   fout << "\n}\n" ;
3812 
3813   fout << "light_source { < " ;
3814   if(norm2(camera1-lookAt)>norm2(camera2-lookAt))
3815     fout << camera1.x()+view.x() << ", " << camera1.y()+view.y() << ", " << camera1.z()+view.z() << "> color White}\n\n" ;
3816   else
3817     fout << camera2.x()+view.x() << ", " << camera2.y()+view.y() << ", " << camera2.z()+view.z() << "> color White}\n\n" ;
3818 
3819   return fout.good() ;
3820 }
3821 
3822 
3823 /*!
3824   \brief Writes a set of povray bicubic patches
3825 
3826   \param tolerance  the tolerance when performing the tesselation
3827   \param filename the file to write to
3828   \param color  the color of the object
3829   \param diffuse  the diffuse factor
3830   \param ambient  the ambient factor
3831   \param smooth  flags which indicates if we generate smooth triangles
3832 
3833   \return an ostream containing the definition of the surface
3834 
3835   \warning POVRAY only accepts rational spline patches. Thus you can't
3836                have a value other then 1.0 for the weights of your surface.
3837 	       A warning message will be generated if this is the case.
3838 
3839 	       POVRAY only deals with surfaces of degree 3. If the surface
3840 	       as a degree below 3 either in the U or V direction it will be
3841 	       elevated to be at 3 in both directions. If the surface as a
3842 	       degree higher then 3, then the function aborts.
3843 
3844   \author Philippe Lavoie
3845   \date 8 October, 1997
3846 */
3847 template <class T, int N>
writePOVRAY(T tolerance,const char * filename,const Color & col,const Point_nD<T,N> & cView,const Point_nD<T,N> & up,int smooth,T ambient,T diffuse) const3848 int NurbsSurface<T,N>::writePOVRAY(T tolerance, const char *filename, const Color& col, const Point_nD<T,N>& cView, const Point_nD<T,N>& up, int smooth, T ambient, T diffuse) const {
3849   ofstream fout(filename) ;
3850   if(!fout)
3851     return 0 ;
3852 
3853   Point_nD<T,N> view(T(-1)*cView) ;
3854 
3855   fout << "//\n//Generated for POV-Ray(tm) 3.0 by Phil's NURBS library\n//\n" ;
3856   fout << "\n#include \"colors.inc\"\n" ;
3857 
3858   // we want the camera to look at the center of the object
3859   // and be able to view the whole object
3860   // we use and angle of 36 to view the object
3861   // and position the rest according to this.
3862   Point_nD<T,N> minP, maxP ;
3863   minP.x() = this->extremum(1,coordX) ;
3864   minP.y() = this->extremum(1,coordY) ;
3865   minP.z() = this->extremum(1,coordZ) ;
3866   maxP.x() = this->extremum(0,coordX) ;
3867   maxP.y() = this->extremum(0,coordY) ;
3868   maxP.z() = this->extremum(0,coordZ) ;
3869 
3870   Point_nD<T,N> lookAt  ;
3871   lookAt.x() = (minP.x()+maxP.x())/2.0 ;
3872   lookAt.y() = (minP.y()+maxP.y())/2.0 ;
3873   lookAt.z() = (minP.z()+maxP.z())/2.0 ;
3874 
3875   Point_nD<T,N> camera1, camera2 ;
3876 
3877   Point_nD<T,N> q1 = minP-lookAt ;
3878   Point_nD<T,N> q2 = maxP-lookAt ;
3879   T D1 = absolute(dot(q1,view))/norm(view) ;
3880   T D2 = absolute(dot(q2,view))/norm(view) ;
3881 
3882   T a1 = norm(q1)*cos(angle(view,q1)) ;
3883   T a2 = norm(q2)*cos(angle(view,q2)) ;
3884 
3885   T b1 = D1/tan(18.0*M_PI/180.0) ;
3886   T b2 = D2/tan(18.0*M_PI/180.0) ; // this gives the 36 degree angle
3887 
3888   camera1 = lookAt+(a1+b1)*view.unitLength() ;
3889   camera2 = lookAt+(a2+b2)*view.unitLength() ;
3890 
3891   Point_nD<T,N> right ;
3892 
3893   right = crossProduct(view,up) ; // inversed because pov-ray uses a left-handed system
3894 
3895   fout << "camera {\n\tlocation <" ;
3896   if(norm2(camera1-lookAt)>norm2(camera2-lookAt))
3897     fout << camera1.x() << ", " << camera1.y() << ", " << camera1.z() << ">\n" ;
3898   else
3899     fout << camera2.x() << ", " << camera2.y() << ", " << camera2.z() << ">\n" ;
3900   fout << "\tup < " << up.x() << ", " << up.y() << ", " << up.z() << ">\n" ;
3901   fout << "\tright < " << right.x() << ", " << right.y() << ", " << right.z() << ">\n" ;
3902   fout << "\tlook_at < " << lookAt.x() << ", " << lookAt.y() << ", " << lookAt.z() << ">\n\tangle 36\n}\n\n" ;
3903 
3904   writePOVRAY(tolerance,fout,col,smooth,ambient,diffuse) ;
3905 
3906 
3907   fout << "light_source { < " ;
3908   if(norm2(camera1-lookAt)>norm2(camera2-lookAt))
3909     fout << camera1.x()+view.x() << ", " << camera1.y()+view.y() << ", " << camera1.z()+view.z() << "> color White}\n\n" ;
3910   else
3911     fout << camera2.x()+view.x() << ", " << camera2.y()+view.y() << ", " << camera2.z()+view.z() << "> color White}\n\n" ;
3912 
3913   return fout.good() ;
3914 }
3915 
3916 
3917 
3918 
3919 /*!
3920   \brief Writes a NuPatch for render man
3921 
3922   Writes a stream which is compatible with Render Man
3923   specifications of a NURBS surface.
3924 
3925   \latexonly
3926 	       \begin{center}
3927 	       The RenderMan \Pisymbol{psy}{226}
3928 	       Interface Procedures and RIB Protocol are:
3929 	       Copyright 1988, 1989, Pixar.  All rights reserved.
3930 	       RenderMan \Pisymbol{psy}{226} is a registered trademark of
3931 	       Pixar.
3932 	       \end{center}
3933   \endlatexonly
3934   \htmlonly
3935   <verbatim>
3936      The RenderMan (R) Interface Procedures and RIB Protocol are:
3937 	       Copyright 1988, 1989, Pixar.  All rights reserved.
3938 	       RenderMan (R) is a registered trademark of Pixar.
3939   </verbatim>
3940   \endhtmlonly
3941 
3942   \param rib the rib ostream
3943 
3944   \return 0 if an error occurs, 1 otherwise
3945 
3946   \author Philippe Lavoie
3947   \date 8 October, 1997
3948 */
3949 template <class T, int N>
writeRIB(ostream & rib) const3950 int NurbsSurface<T,N>::writeRIB(ostream& rib) const {
3951   rib << "NuPatch " << P.rows() << ' ' << degU+1 << " [ " ;
3952   int k;
3953   // I have to loop since RIB is new line sensitive and
3954   // by default a vector adds a new line at the end when using cout
3955   for(k=0;k<U.n();++k)
3956     rib << U[k] << ' ' ;
3957   rib << " ] " << U[0] << ' ' << U[U.n()-1] << ' ' << P.cols() << ' ' << degV+1 << " [ " ;
3958   for(k=0;k<V.n();++k)
3959     rib << V[k] << ' ' ;
3960   rib << " ] " << V[0] << ' ' << V[V.n()-1] << " \"Pw\" [ " ;
3961   for(int j=0;j<P.cols();++j)
3962     for(int i=0;i<P.rows();++i)
3963       rib << P(i,j) ;
3964   rib << " ]\n" ;
3965   return rib.good() ;
3966 }
3967 
3968 /*!
3969   \brief Writes a NuPatch for render man
3970 
3971   Writes a file whith follows the RIB protocol of RenderMan.
3972   It generates a file which views the whole object.
3973   The material used for rendering is plastic.
3974 
3975   \latexonly
3976 	       \begin{center}
3977 	       The RenderMan \Pisymbol{psy}{226}
3978 	       Interface Procedures and RIB Protocol are:
3979 	       Copyright 1988, 1989, Pixar.  All rights reserved.
3980 	       RenderMan \Pisymbol{psy}{226} is a registered trademark of
3981 	       Pixar.
3982 	       \end{center}
3983   \endlatexonly
3984   \htmlonly
3985   <verbatim>
3986      The RenderMan (R) Interface Procedures and RIB Protocol are:
3987 	       Copyright 1988, 1989, Pixar.  All rights reserved.
3988 	       RenderMan (R) is a registered trademark of Pixar.
3989   </verbatim>
3990   \endhtmlonly
3991 
3992   \param filename  the file to write to
3993   \param col  the color of the object
3994   \param view  the view point
3995 
3996   \return 0 if an error occurs, 1 otherwise
3997 
3998   \author Philippe Lavoie
3999   \date 8 October, 1997
4000 */
4001 template <class T, int N>
writeRIB(const char * filename,const Color & col,const Point_nD<T,N> & view) const4002 int NurbsSurface<T,N>::writeRIB(const char* filename, const Color& col, const Point_nD<T,N>& view) const {
4003   ofstream fout(filename) ;
4004   if(!fout)
4005     return 0;
4006 
4007   // The following code is based on Listing 8.2 from the RenderMan Companion
4008   // http://pete.cs.caltech.edu/RMR/Pixar/ch8/listing8_2.c
4009   int i,j ;
4010 
4011   // aimZ
4012   MatrixRT<double> Rx ;
4013   double xzlen, yzlen, yrot, xrot;
4014 
4015   //
4016   // The initial rotation about the y axis is given by the projection of
4017   // the direction vector onto the x,z plane: the x and z components
4018   // of the direction.
4019   xzlen = sqrt(view.x()*view.x()+view.z()*view.z()) ;
4020   if(xzlen == 0)
4021     yrot = (view.y() < 0) ? M_PI : 0;
4022   else
4023     yrot = acos(view.y()/xzlen);
4024 
4025   //
4026   // The second rotation, about the x axis, is given by the projection on
4027   // the y,z plane of the y-rotated direction vector: the original y
4028   // component, and the rotated x,z vector from above.
4029   //
4030   yzlen = sqrt(view.y()*view.y()+xzlen*xzlen);
4031   xrot = acos(xzlen/yzlen);       /* yzlen should never be 0 */
4032 
4033   // A rotation around y first
4034   if (view.y() > 0){
4035     if(view.x()>0)
4036       Rx.rotate(xrot,yrot,0.0) ;
4037     else
4038       Rx.rotate(xrot,-yrot,0.0) ;
4039   }
4040   else{
4041     if(view.x()>0)
4042       Rx.rotate(-xrot,yrot,0.0) ;
4043     else
4044       Rx.rotate(-xrot,-yrot,0.0) ;
4045   }
4046 
4047   Point_nD<T,N> minP, maxP ;
4048   minP.x() = this->extremum(1,coordX) ;
4049   minP.y() = this->extremum(1,coordY) ;
4050   minP.z() = this->extremum(1,coordZ) ;
4051   maxP.x() = this->extremum(0,coordX) ;
4052   maxP.y() = this->extremum(0,coordY) ;
4053   maxP.z() = this->extremum(0,coordZ) ;
4054 
4055   Point_nD<T,N> lookAt  ;
4056   lookAt.x() = (minP.x()+maxP.x())/2.0 ;
4057   lookAt.y() = (minP.y()+maxP.y())/2.0 ;
4058   lookAt.z() = (minP.z()+maxP.z())/2.0 ;
4059 
4060   Point_nD<T,N> camera1, camera2 ;
4061 
4062   Point_nD<T,N> q1 = minP-lookAt ;
4063   Point_nD<T,N> q2 = maxP-lookAt ;
4064   T D1 = absolute(dot(q1,view))/norm(view) ;
4065   T D2 = absolute(dot(q2,view))/norm(view) ;
4066 
4067   T a1 = norm(q1)*cos(angle(view,q1)) ;
4068   T a2 = norm(q2)*cos(angle(view,q2)) ;
4069 
4070   T b1 = D1/tan(18.0*M_PI/180.0) ;
4071   T b2 = D2/tan(18.0*M_PI/180.0) ; // this gives the 36 degree angle
4072 
4073   camera1 = lookAt+(a1+b1)*view.unitLength() ;
4074   camera2 = lookAt+(a2+b2)*view.unitLength() ;
4075 
4076   Point_nD<T,N> camera ;
4077 
4078   if(norm(camera1-lookAt)>norm(camera2-lookAt))
4079     camera = camera1 ;
4080   else
4081     camera = camera2 ;
4082 
4083   char front[1024] ;
4084 
4085   char *ext ;
4086   ext = strstr(filename,".rib") ;
4087   if(ext){
4088     for(i=0;i<1024;++i){
4089       if(&filename[i] == ext)
4090 	break ;
4091       else
4092 	front[i] = filename[i] ;
4093       if(!front[i])
4094 	break ;
4095     }
4096   }
4097   else{
4098     strcpy(front,filename) ;
4099   }
4100 
4101 
4102   fout << "##RenderMan RIB-Structure 1.0\n" ;
4103   fout << "#" << filename << endl;
4104   fout << "Format 400 400 1\n";
4105   fout << "Display \"" << front << ".tif\" \"file\" \"rgba\"\n" ;
4106   fout << "Projection \"perspective\" \"fov\" [36]\n" ;
4107   fout << "Translate 0 0 " << norm(camera-lookAt) << endl ;
4108   fout << "Option \"render\" \"prmanspecular\" [1]\n" ;
4109 
4110   fout << "\nWorldBegin\n" ;
4111   fout << "LightSource \"ambientlight\" 0 \"intensity\" [0.3]\n" ;
4112   fout << "LightSource \"distantlight\" 1 \"to\" [ " << view.x() << ' ' << view.y() << ' ' << view.z()  << "]\n" ;
4113   //fout << "LightSource \"spotlight\" 1 \"intensity\" [300] \"from\" [ " ;
4114   //fout << camera.x()+view.x() << ' ' << camera.y()+view.y() << ' ' << camera.z()+view.z() << " ]\n\n" ;
4115   fout << "AttributeBegin\nSurface \"plastic\"\n" ;
4116   fout << "Color [ " << (double)col.r/255.0 << ' ' << (double)col.g/255.0 << ' ' << double(col.b)/255.0 << "]\n" ;
4117 
4118   fout << "Transform [ " ;
4119 
4120   for(j=0;j<4;++j)
4121     for(i=0;i<4;++i)
4122       fout << Rx(i,j) << ' ' ;
4123   fout << "]\n" ;
4124   fout << "Translate " << -lookAt.x() << ' ' << -lookAt.y() << ' ' << -lookAt.z() << endl ;
4125 
4126 
4127   writeRIB(fout) ;
4128 
4129   fout << "AttributeEnd\nWorldEnd\n\n" ;
4130 
4131   return fout.good() ;
4132 }
4133 
4134 
4135 /*!
4136   \brief Generates a list of triangles for a surface
4137 
4138   This function is deprecated, please use the NurbsSubSurface class which
4139   implements everything that this function was suppose to do.
4140 
4141   \param tolerance  the tolerance for the tesselation.
4142   \param points  the list of points
4143   \param connect  how the points should be connected
4144 
4145   \author Philippe Lavoie
4146   \date 8 October, 1997
4147 */
4148 template <class T, int N>
tesselate(T tolerance,BasicList<Point_nD<T,N>> & points,BasicList<int> & connect,BasicList<Point_nD<T,N>> * Norm) const4149 void NurbsSurface<T,N>::tesselate(T tolerance, BasicList<Point_nD<T,N> > &points, BasicList<int> &connect, BasicList<Point_nD<T,N> > *Norm) const {
4150 
4151 #ifdef USE_EXCEPTION
4152   throw NurbsError() ;
4153 #else
4154   Error err("NurbsSurface<T,N>::tesselate");
4155   err << "The tesselate member function is deprecated. Please use\n"
4156     "the NurbsSubSurface class member functions instead.\n" ;
4157   err.fatal();
4158 #endif
4159 }
4160 
4161 
4162 /*!
4163   \brief Generates a sphere
4164 
4165   The NURBS surface is now a sphere of radius \a r located
4166   at \a O.
4167 
4168   \param O  the location of the center of the sphere
4169   \param r  the radius of the sphere
4170 
4171   \author Philippe Lavoie
4172   \date 8 May, 1998
4173 */
4174 template <class T, int N>
makeSphere(const Point_nD<T,N> & O,T r)4175 void NurbsSurface<T,N>::makeSphere(const Point_nD<T,N>& O, T r) {
4176   NurbsCurve<T,N> c ;
4177 
4178   const T wm = T(0.707106781185) ;  // sqrt(2)/2
4179 
4180   c.resize(5,2) ;
4181 
4182   c.modCP(0,HPoint_nD<T,N>(0,0,r,1)) ;
4183   c.modCP(1,HPoint_nD<T,N>(-r*wm,0,r*wm,wm)) ;
4184   c.modCP(2,HPoint_nD<T,N>(-r,0,0,1)) ;
4185   c.modCP(3,HPoint_nD<T,N>(-r*wm,0,-r*wm,wm)) ;
4186   c.modCP(4,HPoint_nD<T,N>(0,0,-r,1)) ;
4187 
4188   Vector<T> k(5+2+1) ;
4189   k[0] = k[1] = k[2] = 0 ;
4190   k[3] = k[4] = 0.5 ;
4191   k[5] = k[6] = k[7] = 1 ;
4192 
4193   c.modKnot(k) ;
4194 
4195   makeFromRevolution(c) ;
4196   MatrixRT<T> Tx ;
4197   Tx.translate(O.x(),O.y(),O.z()) ;
4198   transform(Tx) ;
4199 }
4200 
4201 /*!
4202   \brief Writes a post-script file representing the curve
4203 
4204   \param filename  the file to write the postscript file to
4205   \param nu  the number of lines in the U direction
4206   \param nv  the number of lines in the V direction
4207   \param camera  where the camera is
4208   \param lookAt  where the camera is looking at
4209   \param plane  where is the projection plane from the camera
4210   \param cp  a flag indicating if the control points should be
4211 	     drawn, 0 = no and 1 = yes
4212   \param magFact  a magnification factor, the 2D point of the control
4213                   points will be magnified by this value. The size is
4214 		  measured in postscript points. If the magFact is
4215 		  set to a value smaller or equal to 0, than the
4216 		  program will try to guess a magnification factor
4217 		  such that the curve is large enough to fill the
4218 		  page.
4219   \param dash  the size of the dash in postscript points . A size
4220 	smaller or equal to 0 indicates that
4221 	the line joining the control points is plain.
4222 
4223   \return 0 if an error occurs, 1 otherwise
4224 
4225   \warning If the weights of the curve are not all at 1, the result might
4226                not be representative of the true NURBS curve.
4227 
4228   \author Philippe Lavoie
4229   \date 7 October 1998
4230 */
4231 template <class T, int N>
writePS(const char * filename,int nu,int nv,const Point_nD<T,N> & camera,const Point_nD<T,N> & lookAt,int cp,T magFact,T dash) const4232 int NurbsSurface<T,N>::writePS(const char* filename, int nu, int nv, const Point_nD<T,N>& camera, const Point_nD<T,N>& lookAt, int cp,T magFact,T dash) const {
4233   NurbsCurveArray<T,N> Ca ;
4234   if(nu<=0 || nv<=0)
4235     return 0 ;
4236 
4237 
4238   // We need to find the rotation matrix to have z axis along nv
4239   Point_nD<T,N> np  = lookAt-camera ;
4240   np = np.unitLength() ;
4241   np *= -1 ;
4242 
4243   T rx = atan2(np.z(),np.y()) ;
4244   T ry = atan2(np.z(),np.x()) ;
4245 
4246   MatrixRT<T> Tx(rx,ry,0,camera.x(),camera.y(),camera.z()) ;
4247   //MatrixRT<T,N> Sc ; Sc.scale(1,1,T(norm(lookAt-camera))/plane) ;
4248   //MatrixRT<T,N> Tg(Sc*Tx) ;
4249 
4250   Ca.resize(nu+nv+2) ;
4251   int i ;
4252   for(i=0;i<=nu;++i){
4253     T u = U[0]+T(i)*(U[U.n()-1]-U[0])/T(nu) ;
4254     isoCurveU(u , Ca[i]) ;
4255     Ca[i].transform(Tx) ;
4256   }
4257   for(;i<=nv+nu+1;++i){
4258     T v = V[0]+T(i-nu-1)*(V[V.n()-1]-V[0])/T(nv) ;
4259     isoCurveV(v , Ca[i]) ;
4260     Ca[i].transform(Tx) ;
4261   }
4262 
4263 
4264   return Ca.writePS(filename,cp,magFact,dash) ;
4265 }
4266 
4267 /*!
4268   \brief writes a post-script file representing the curve
4269 
4270   Writes the curve in the postscript format to a file, it also
4271   draws the points defined in \a points with their associated
4272   vectors if \a vector is used.
4273 
4274   \param filename  the file to write the postscript file to
4275   \param nu  the number of lines in the U direction
4276   \param nv  the number of lines in the V direction
4277   \param camera  where the camera is
4278   \param lookAt  where the camera is looking at
4279   \param plane  where is the projection plane from the camera
4280   \param points  draws these additional points as empty circles
4281   \param vectors  specify a vector associated with the points
4282 		   (this can be an empty vector)
4283   \param cp  a flag indicating if the control points should be
4284 		         drawn, 0 = no and 1 = yes
4285   \param magFact  a magnification factor, the 2D point of the control
4286 		  points will be magnified by this value. The size
4287 		  is measured in postscript points. If the magFact
4288 		  is set to a value smaller or equal to 0, than the
4289 		  program will try to guess a magnification factor
4290 		  such that the curve is large enough to fill the
4291 		  page.
4292   \param dash  the size of the dash in postscript points . A size
4293 	smaller or equal to 0 indicates that
4294 	the line joining the control points is plain.
4295 
4296   \return 0 if an error occurs, 1 otherwise
4297 
4298   \warning If the weights of the curve are not all at 1, the result might
4299                not be representative of the true NURBS curve. If vectors is
4300 	       used, then it must be of the same size as points. If a vector
4301 	       element is (0,0,0) it will not be drawn.
4302 
4303   \author Philippe Lavoie
4304   \date 7 October 1998
4305 */
4306 template <class T, int N>
writePSp(const char *,int nu,int nv,const Point_nD<T,N> & camera,const Point_nD<T,N> & lookAt,const Vector<Point_nD<T,N>> &,const Vector<Point_nD<T,N>> &,int cp,T magFact,T dash) const4307 int NurbsSurface<T,N>::writePSp(const char*, int nu, int nv, const Point_nD<T,N>& camera, const Point_nD<T,N>& lookAt, const Vector< Point_nD<T,N> >&,const Vector< Point_nD<T,N> >&, int cp,T magFact,T dash) const {
4308   cerr << "Not implemented. Not sure what is different between this and writePS\n";
4309   return 0;
4310 }
4311 
4312 /*!
4313   \brief Sends the NURBS Surface to ostream for display
4314 
4315   \return the ostream
4316 
4317   \author Philippe Lavoie
4318   \date 9 November 1998
4319 */
4320 template <class T, int N>
print(ostream & o) const4321 ostream& NurbsSurface<T,N>::print(ostream& o) const {
4322   o << "Degree: " << degU << ' ' << degV << endl;
4323   o << "U : " << U << endl;
4324   o << "V: " << V << endl ;
4325   o << "matrix size: " << P.rows() << ' ' << P.cols() << endl ;
4326   o << P << endl;
4327   return o;
4328 }
4329 
4330 /*!
4331   \brief Computes the parameters for global surface interpolation closed
4332          in the u direction
4333 
4334   Computes the parameters for global surface interpolation.
4335   For more information, see A9.3 on p377 on the NURBS book.
4336 
4337   \param   Q  the matrix of 3D points (wrapped in the u dir.- rows)
4338   \param  uk  the knot coefficients in the U direction
4339   \param  vl  the knot coefficients in the V direction
4340 
4341   \return 0 if an error occurs, 1 otherwise
4342 
4343   \author Alejandro Frangi
4344   \date 24 January, 1997
4345 */
4346 template <class T, int N>
surfMeshParamsClosedU(const Matrix<Point_nD<T,N>> & Q,Vector<T> & uk,Vector<T> & vl,int degU)4347 int surfMeshParamsClosedU(const Matrix< Point_nD<T,N> >& Q, Vector<T>& uk, Vector<T>& vl, int degU){
4348 
4349   int n,m,k,l,num ;
4350   double d,total ;
4351   Vector<T> cds(Q.rows()) ;
4352 
4353   n = Q.rows() ;
4354   m = Q.cols() ;
4355   uk.resize(n) ;
4356   vl.resize(m) ;
4357   num = m ;
4358 
4359   // Compute the uk
4360   uk.reset(0) ;
4361 
4362   for(l=0;l<m;l++){
4363     total = 0.0 ;
4364     for(k=1;k<=n-degU;k++){
4365       cds[k] = norm(Q(k,l)-Q(k-1,l)) ;
4366       total += cds[k] ;
4367     }
4368     for(k=n-degU+1; k<n ;k++)
4369       cds[k] = norm(Q(k,l)-Q(k-1,l)) ;
4370 
4371     if(total==0.0)
4372       num-- ;
4373     else {
4374       d = 0.0 ;
4375       for(k=1;k<n;k++){
4376         d += cds[k] ;
4377         uk[k] += d/total ;
4378       }
4379     }
4380   }
4381   if(num==0)
4382     return 0 ;
4383   for(k=1;k<n;k++)
4384     uk[k] /= num ;
4385 
4386   // Compute the vl
4387   vl.reset(0) ;
4388   cds.resize(m) ;
4389 
4390   num = n ;
4391 
4392   for(k=0;k<n;k++){
4393     total = 0.0 ;
4394     for(l=1;l<m;l++){
4395       cds[l] = norm(Q(k,l)-Q(k,l-1)) ;
4396       total += cds[l] ;
4397     }
4398     if(total==0.0)
4399       num-- ;
4400     else {
4401       d = 0.0 ;
4402       for(l=1;l<m;l++){
4403         d += cds[l] ;
4404         vl[l] += d/total ;
4405       }
4406     }
4407   }
4408   if(num==0)
4409     return 0 ;
4410   for(l=1;l<m-1;l++)
4411     vl[l] /= num ;
4412   vl[m-1] = 1.0 ;
4413 
4414 
4415   return 1 ;
4416 }
4417 
4418 /*!
4419   \biref Computes the parameters for global surface interpolation closed
4420          in u dir
4421 
4422   Computes the parameters for global surface interpolation.
4423   For more information, see A9.3 on p377 on the NURBS book.
4424 
4425   \param  Q  the matrix of 3D points (wrapped in u dir - rows)
4426   \param uk  the knot coefficients in the U direction
4427   \param vl  the knot coefficients in the V direction
4428 
4429   \return 0 if an error occurs, 1 otherwise
4430 
4431   \author Alejandro Frangi
4432   \date 24 January, 1997
4433 */
4434 template <class T, int N>
surfMeshParamsClosedUH(const Matrix<HPoint_nD<T,N>> & Q,Vector<T> & uk,Vector<T> & vl,int degU)4435 int surfMeshParamsClosedUH(const Matrix< HPoint_nD<T,N> >& Q, Vector<T>& uk, Vector<T>& vl, int degU){
4436 
4437   int n,m,k,l,num ;
4438   double d,total ;
4439   Vector<T> cds(Q.rows()) ;
4440 
4441   n = Q.rows() ;
4442   m = Q.cols() ;
4443   uk.resize(n) ;
4444   vl.resize(m) ;
4445   num = m ;
4446 
4447   // Compute the uk
4448   uk.reset(0) ;
4449 
4450   for(l=0;l<m;l++){
4451     total = 0.0 ;
4452     // Normalization factor
4453     for(k=1;k<=n-degU;k++){
4454       cds[k] = norm(Q(k,l)-Q(k-1,l)) ;
4455       total += cds[k] ;
4456     }
4457     for(k=n-degU+1; k<n ;k++)
4458       cds[k] = norm(Q(k,l)-Q(k-1,l)) ;
4459     if(total==0.0)
4460       num-- ;
4461     else {
4462       d = 0.0 ;
4463       for(k=1;k<n;k++){
4464         d += cds[k] ;
4465         uk[k] += d/total ;
4466       }
4467     }
4468   }
4469   if(num==0)
4470     return 0 ;
4471   for(k=1;k<n;k++)
4472     uk[k] /= num ;
4473 
4474   // Compute the vl
4475   vl.reset(0) ;
4476   cds.resize(m) ;
4477 
4478   num = n ;
4479 
4480   for(k=0;k<n;k++){
4481     total = 0.0 ;
4482     for(l=1;l<m;l++){
4483       cds[l] = norm(Q(k,l)-Q(k,l-1)) ;
4484       total += cds[l] ;
4485     }
4486     if(total==0.0)
4487       num-- ;
4488     else {
4489       d = 0.0 ;
4490       for(l=1;l<m;l++){
4491         d += cds[l] ;
4492         vl[l] += d/total ;
4493       }
4494     }
4495   }
4496   if(num==0)
4497     return 0 ;
4498   for(l=1;l<m-1;l++)
4499     vl[l] /= num ;
4500   vl[m-1] = 1.0 ;
4501 
4502   return 1 ;
4503 }
4504 
4505 
4506 /*!
4507   \brief Generates a closed surface using global interpolation.
4508 
4509   Generates a NURBS surface using global interpolation. In the u
4510   direction the curve will be closed and with C(pU-1)
4511   continuity. Each column in Q indicates the points
4512   for a closed curve in the u direction. First and
4513   last point have to be equal.
4514 
4515   \param  Q  a matrix of 3D points (wrapped in u dir. -rows)
4516   \param pU  the degree of interpolation in the U direction
4517   \param pV  the degree of interpolation in the V direction
4518 
4519   \author Alejandro Frangi
4520   \date 30 June, 1998
4521 */
4522 template <class T, int N>
globalInterpClosedU(const Matrix<Point_nD<T,N>> & Q,int pU,int pV)4523 void NurbsSurface<T,N>::globalInterpClosedU(const Matrix< Point_nD<T,N> >& Q, int pU, int pV){
4524   Vector<T> vk,uk ;
4525 
4526   resize(Q.rows(),Q.cols(),pU,pV) ;
4527 
4528   surfMeshParamsClosedU(Q,uk,vk,pU) ;
4529   knotAveragingClosed(uk,pU,U) ;
4530   knotAveraging(vk,pV,V) ;
4531 
4532 
4533   Vector< HPoint_nD<T,N> > Pts(Q.cols()) ;
4534   NurbsCurve<T,N> R ;
4535 
4536   int i,j ;
4537   for(i=0;i<Q.rows();i++){
4538     for(j=0;j<Q.cols();j++)
4539       Pts[j] = Q(i,j) ;
4540     R.globalInterpH(Pts,vk,V,pV) ;
4541     for(j=0;j<Q.cols();j++)
4542       P(i,j) = R.ctrlPnts(j) ;
4543   }
4544 
4545   Pts.resize(Q.rows()) ;
4546   for(j=0;j<Q.cols();j++){
4547     for(i=0;i<Q.rows();i++)
4548       Pts[i] = P(i,j) ;
4549 
4550     R.globalInterpClosedH(Pts,uk,U,pU);
4551     for(i=0;i<Q.rows();i++)
4552       P(i,j) = R.ctrlPnts(i) ;
4553   }
4554 
4555 }
4556 
4557 /*!
4558   \brief Generates a surface using global interpolation.
4559 
4560   Generates a NURBS surface using global interpolation. In the u direction
4561   the curve will be closed and with C(pU-1) continuity. Each column in Q
4562   indicates the points for a closed curve in the u
4563   direction. First and last point have to be equal.
4564 
4565   \param  Q  a matrix of 4D points (wrapped in u dir. -rows)
4566   \param pU  the degree of interpolation in the U direction
4567   \param pV  the degree of interpolation in the V direction
4568 
4569   \author Alejandro Frangi
4570   \date 30 June, 1998
4571 */
4572 template <class T, int N>
globalInterpClosedUH(const Matrix<HPoint_nD<T,N>> & Q,int pU,int pV)4573 void NurbsSurface<T,N>::globalInterpClosedUH(const Matrix< HPoint_nD<T,N> >& Q, int pU, int pV){
4574   Vector<T> vk,uk ;
4575 
4576   resize(Q.rows(),Q.cols(),pU,pV) ;
4577 
4578   surfMeshParamsClosedUH(Q,uk,vk,pU) ;
4579   knotAveragingClosed(uk,pU,U) ;
4580   knotAveraging(vk,pV,V) ;
4581 
4582   Vector< HPoint_nD<T,N> > Pts(Q.rows()) ;
4583   NurbsCurve<T,N> R ;
4584 
4585   int i,j ;
4586 
4587   for(j=0;j<Q.cols();j++){
4588     for(i=0;i<Q.rows();i++)
4589       Pts[i] = Q(i,j) ;
4590     R.globalInterpH(Pts,uk,U,pU);
4591     for(i=0;i<Q.rows();i++)
4592       P(i,j) = R.ctrlPnts(i) ;
4593   }
4594 
4595   Pts.resize(Q.cols()) ;
4596   for(i=0;i<Q.rows();i++){
4597     for(j=0;j<Q.cols();j++)
4598       Pts[j] = P(i,j) ;
4599     R.globalInterpClosedH(Pts,vk,V,pV) ;
4600     for(j=0;j<Q.cols();j++)
4601       P(i,j) = R.ctrlPnts(j) ;
4602   }
4603 }
4604 
4605 
4606 /*!
4607   \brief Generates a closed surface using global least squares approximation.
4608 
4609   Generates a NURBS surface using global least squares
4610   approximation. This will be closed in the u direction and open
4611   in the v direction. At u=0("="1) the
4612   surface will have C(pU-1) continuity
4613   in the u direction.
4614 
4615   \param  Q  a matrix of 3D points (wrapped in u dir. -rows)
4616   \param pU  the degree of interpolation in the U direction
4617   \param pV  the degree of interpolation in the V direction
4618   \param nU  the number of points in the U direction
4619   \param nV  the number of poitns in the V direction
4620 
4621   \author Alejandro Frangi
4622   \date 1 August 1998
4623 */
4624 template <class T, int N>
leastSquaresClosedU(const Matrix<Point_nD<T,N>> & Q,int pU,int pV,int nU,int nV)4625 void NurbsSurface<T,N>::leastSquaresClosedU(const Matrix< Point_nD<T,N> >& Q, int pU, int pV, int nU, int nV){
4626   Vector<T> vk,uk ;
4627 
4628   resize(nU+pU,nV,pU,pV) ;
4629 
4630   surfMeshParamsClosedU(Q,uk,vk,pU) ;
4631 
4632 
4633   Vector< HPoint_nD<T,N> > Pts(Q.rows()) ;
4634   NurbsCurve<T,N> R ;
4635 
4636   int i,j ;
4637 
4638   Matrix< HPoint_nD<T,N> > P2 ;
4639 
4640   P2.resize(nU+pU,Q.cols()) ;
4641 
4642   for(j=0;j<Q.cols();j++){
4643     for(i=0;i<Q.rows();i++)
4644       Pts[i] = Q(i,j) ;
4645     R.leastSquaresClosedH(Pts,pU,nU,uk);
4646     for(i=0;i<P.rows();i++)
4647       P2(i,j) = R.ctrlPnts(i) ;
4648     if(j==0)
4649       U = R.knot() ;
4650   }
4651 
4652   Pts.resize(Q.cols()) ;
4653   for(i=0;i<P.rows();i++){
4654     for(j=0;j<Q.cols();j++)
4655       Pts[j] = P2(i,j) ;
4656     R.leastSquaresH(Pts,pV,nV,vk) ;
4657     for(j=0;j<P.cols();j++)
4658       P(i,j) = R.ctrlPnts(j) ;
4659     if(i==0)
4660       V = R.knot() ;
4661   }
4662 
4663 }
4664 
4665 /*!
4666   \brief Write the NURBS surface to a OOGL mesh file
4667 
4668   Writes a OOGL mesh file which represents the surface for the
4669   parametric space [fBu,fEu] and [fBv,fEv].
4670   It does not optimize the number of points required to
4671   represent the surface.
4672 
4673   \param filename  the file name for the output OOGL file
4674   \param fDu  the parameter step size in u
4675   \param fDv  the parameter step size in v
4676   \param OOGLheader_options  OOGL mesh format header options
4677                (evrything before the MESH keyword). If you want the
4678                mesh to be closed in the u/v direction you have to
4679                give a string containing [u][v] where [] means optional
4680                and indicates the direction to be wrapped. See Geomview
4681                documentation.
4682   \param fBu  the initial parameter value in u
4683   \param fBv  the initial parameter value in v
4684   \param fEu  the end parameter value in u
4685   \param fEv  the end parameter value in v
4686   \param bDrawCP   draws the control points as circles
4687 
4688   \return 1 on success, 0 otherwise
4689 
4690   \warning The parametric surface must be valid
4691   \author Alejandro Frangi
4692   \date 19 May, 1998
4693 */
4694 template <class T, int N>
writeOOGL(const char * filename,T fDu,T fDv,T fBu,T fBv,T fEu,T fEv,bool bDrawCP) const4695 int NurbsSurface<T,N>::writeOOGL(const char* filename,
4696 				 T fDu,
4697 				 T fDv,
4698 				 T fBu, T fBv,
4699 				 T fEu, T fEv,
4700 				 bool  bDrawCP) const {
4701   ofstream fout(filename) ;
4702   if(!fout)
4703     return 0 ;
4704 
4705   // Write file header
4706   fout << "{ LIST \n";
4707   fout << "\t{ appearance { shading smooth } \n " ;
4708   fout << "\tNMESH" << endl;
4709   T Nu = (fEu-fBu)/fDu + 1;
4710   T Nv = (fEv-fBv)/fDv + 1;
4711   fout << "\t"<< Nu << ' ' << Nv << endl;
4712 
4713   // Write mesh vertexes
4714   Point_nD<T,N> Sp, Np;
4715   T u,v;
4716   for (u = fBu; u<fEu+fDu/2; u+=fDu)
4717     for (v = fBv; v<fEv+fDv/2; v+=fDv){
4718       Sp = this->pointAt(u,v);
4719       Np = normal(u,v);
4720       Np = (norm(Np)!=0)?Np.unitLength():Point_nD<T,N>(0.0);
4721       fout << "\t" << Sp << "\t " << Np << endl;
4722     }
4723   fout << "\t}" << endl << std::flush;
4724 
4725   // Write the control points
4726   if (bDrawCP){
4727     fout << "\t{ " << endl;
4728     fout << "\t  appearance {shading smooth linewidth 5 } " << endl;
4729 
4730     fout << "\t" << "SKEL" << endl;
4731     fout <<  P.rows()*P.cols() << ' ' << P.rows()*P.cols() << endl;
4732     for (int i = 0; i<P.rows(); i++)
4733       for (int j = 0; j<P.cols(); j++)
4734         fout << "\t" << project(P(i,j)) << endl;
4735     fout << endl;
4736     for (int i = 0; i<P.rows()*P.cols(); i++)
4737       fout << "\t" << "1 " << i << " 0 0 1 0.5 " << endl;
4738       fout << "\t" << " }" << endl;
4739   }
4740   fout << "} " << endl;
4741   fout << std::flush;
4742 
4743   return 1 ;
4744 }
4745 
4746 /*!
4747   \brief Write the NURBS surface to a mesh file
4748 
4749   This function writes a surface in QUADMESH ascii format to
4750   interface with Display (Copyright 1993,1994,1995 David MacDonald,
4751   McConnell Brain Imaging Centre), Montreal Neurological Institute,
4752   McGill University.
4753 
4754   \param filename  the file name for the output OOGL file
4755 
4756   \return 1 on success, 0 otherwise
4757 
4758   \warning The parametric surface must be valid
4759 
4760   \author Alejandro Frangi
4761   \date 19 May, 1998
4762 */
4763 template <class T, int N>
writeDisplayQUADMESH(const char * filename,int iNu,int iNv,const Color & color,T fA,T fO) const4764 int NurbsSurface<T,N>::writeDisplayQUADMESH(const char* filename, int iNu,int iNv,const Color& color,T fA, T fO) const
4765 {
4766 
4767   T fDu = 1/T(iNu);
4768   T fDv = 1/T(iNv-1);
4769 
4770   ofstream fout(filename) ;
4771   if(!fout)
4772     return 0 ;
4773 
4774   // Save the object type
4775   const char QUADMESH='q'+ ('A' - 'a');
4776   fout << QUADMESH << ' ';       ;
4777 
4778   // Compute surface properties
4779   T a, d, s, se, t;
4780 
4781   // Ambient reflectance coefficient
4782   a  = 0.3;
4783   // Diffusse reflectance coefficient
4784   d  = 0.3;
4785   // Specularity reflectance coeficcient
4786   s  = 0.4;
4787   // Specularity reflectance exponent
4788   se = 10;
4789   // Opacity
4790   t  = fO;
4791 
4792   // Save surface properties
4793   fout << a << ' '
4794        << d << ' '
4795        << s << ' '
4796        << se << ' '
4797        << t  << ' ';
4798 
4799   // Save mesh dimensions
4800   fout << iNu << ' '
4801        << iNv << ' ' ;
4802 
4803   // Save wrapp status in each direction (v,u)
4804   fout << "F T ";
4805 
4806   // New line
4807   fout << endl ;
4808 
4809   // Surface color RGBA (one color for the whole surface)
4810   T fR= T(color.r)/255;
4811   T fG= T(color.g)/255;
4812   T fB= T(color.b)/255;
4813 
4814   /* Colour flag = ONE_COLOUR */
4815   fout << 0 << ' ' ;
4816   /* The colour */
4817   fout << fR << ' '
4818        << fG << ' '
4819        << fB << ' '
4820        << fA << endl;
4821 
4822   // New line
4823   fout << endl ;
4824 
4825   // Now the list of 3D points
4826   T u,v;
4827   Point_nD<T,N> Sp;
4828   for (v = 0; v<1+fDv/2; v+=fDv)
4829     for (u = 0; u<1-fDu/2; u+=fDu){
4830       // The change in sign and the swap of y and z coordinates is
4831       // for conversion to MINC format.
4832       Sp = -(T)1.0 * this->pointAt(u,v) ;
4833       fout << Sp.x() << ' ' << Sp.z() << ' ' << Sp.y() << endl;
4834     }
4835 
4836   // New line
4837   fout << endl ;
4838 
4839   // Now the normal vectors
4840   Point_nD<T,N> Np;
4841   for (v = 0; v<1+fDv/2; v+=fDv)
4842     for (u = 0; u<1-fDu/2; u+=fDu){
4843       Np = normal(u,v);
4844       Np = (norm(Np)!=0)?Np.unitLength():Point_nD<T,N>(0.0);
4845       fout << Np.x() << ' ' << Np.z() << ' ' << Np.y() << endl;
4846     }
4847 
4848   // New line
4849   fout << endl ;
4850 
4851 
4852   return 1;
4853 }
4854 
4855 /*!
4856   \brief Write the NURBS surface to a OOGL mesh file
4857 
4858   Writes a OOGL bezier file which represents the NURBS
4859   surface decomposed into its Bezier patches.
4860 
4861   \param filename  the file name for the output OOGL file
4862 
4863   \return 1 on success, 0 otherwise
4864 
4865   \warning The parametric surface must be valid
4866   \author Alejandro Frangi
4867   \date 19 May, 1998
4868 */
4869 template <class T, int N>
writeOOGL(const char * filename) const4870 int NurbsSurface<T,N>::writeOOGL(const char* filename) const {
4871   ofstream fout(filename) ;
4872   if(!fout)
4873     return 0 ;
4874 
4875   // Write file header
4876   int iPointDim = 4;
4877   fout << "BEZ" << degU << degV << iPointDim << endl;
4878 
4879   // Decompose surface in its Bezier Patches
4880   NurbsSurfaceArray<T,N> S;
4881   NurbsSurface<T,N>      surface(*this);
4882   surface.decompose(S);
4883 
4884   // Write patch vertexes
4885   for (int iPatch = 0; iPatch < S.n(); iPatch++){
4886     for(int iu = 0; iu < degU + 1; iu++){
4887       for(int iv = 0; iv < degV + 1; iv++)
4888         fout << S[iPatch].ctrlPnts(iu,iv).x() << ' '
4889              << S[iPatch].ctrlPnts(iu,iv).y() << ' '
4890              << S[iPatch].ctrlPnts(iu,iv).z() << ' '
4891              << S[iPatch].ctrlPnts(iu,iv).w() << endl;
4892     }
4893     fout << endl;
4894   }
4895   fout << std::flush;
4896 
4897   return 1 ;
4898 }
4899 
4900 
4901 /*!
4902   \brief Wraps d points to the end of a point matrix in a given direction
4903 
4904   Qw contains the same points that Q and wraps the end is
4905   padded with the first d points from Q
4906 
4907   \param Q  a matrix of 3D points
4908   \param d  number of wrapped points
4909   \param dir  direction 0=rows, 1=cols
4910   \param Qw  a wrapped matrix of 4D points
4911 
4912   \author    Alejandro Frangi
4913   \date 14 July, 1998
4914 */
4915 template <class T, int N>
wrapPointMatrix(const Matrix<Point_nD<T,N>> & Q,int d,int dir,Matrix<Point_nD<T,N>> & Qw)4916 void wrapPointMatrix(const Matrix< Point_nD<T,N> >& Q, int d, int dir,  Matrix< Point_nD<T,N> >& Qw){
4917   int i, row, col;
4918 
4919   Qw = Q;
4920 
4921   if (dir==0){
4922     //    cout << " Wrapping in U dir " << endl << std::flush ;
4923     Qw.resizeKeep(Q.rows()+d,Q.cols());
4924     for (col=0; col < Q.cols(); col++)
4925       for (i=0; i<d; i++)
4926         Qw(i+Q.rows(),col) = Q(i,col);
4927   }
4928   else{
4929     //    cout << " Wrapping in V dir " << endl << std::flush ;
4930     Qw.resizeKeep(Q.rows(),Q.cols()+d);
4931     for (row=0; row < Q.rows(); row++)
4932       for (i=0; i<d; i++)
4933         Qw(row,i+Q.cols()) = Q(row,i);
4934   }
4935 
4936 }
4937 
4938 /*!
4939   \brief Compute the derivatives functions at \a u,v of the basis  functions of the NURBS surface
4940   \relates NurbsCurve, nurbsDersBasisFuns
4941 
4942   \param dU, dV  the degrees of the derivation
4943   \param u,v  the parametric values
4944   \param uspan,vspan  the span for the basis functions
4945   \param Niku  A matrix containing the derivatives of the
4946 basis functions in the u direction.
4947   \param Njku  A matrix containing the derivatives of the
4948 basis functions in the v direction.
4949 
4950   \warning \a dU,dV, \a u,v and \a uspan and vspan must be
4951 in a valid range.
4952   \author Alejandro Frangi
4953   \date 15 January 1998
4954 */
4955 template <class T, int N>
dersBasisFuns(T u,T v,int dU,int dV,int uspan,int vspan,Matrix<T> & Niku,Matrix<T> & Njkv) const4956 void NurbsSurface<T,N>::dersBasisFuns(T u, T v, int dU, int dV, int uspan, int vspan, Matrix<T> & Niku, Matrix<T> & Njkv ) const {
4957   // Get derivatives
4958   nurbsDersBasisFuns(dU,u,uspan,degU,U,Niku) ;
4959   nurbsDersBasisFuns(dV,v,vspan,degV,V,Njkv) ;
4960 }
4961 
4962 /*!
4963   \brief Generates a torus
4964 
4965   The NURBS surface is now a torus with major radius \a R, minor
4966   radius \a r and located at \a O. The torus goes around the z-axis.
4967   This routine is an adaptation of a routine created by John W. Peterson.
4968 
4969   \param O  the location of the center of the torus
4970   \param R  the major radius of the torus
4971   \param r  the minor radius of the torus
4972 
4973   \author Philippe Lavoie
4974   \date 4 May, 1999
4975 */
4976 template <class T, int N>
makeTorus(const Point_nD<T,N> & O,T R,T r)4977 void NurbsSurface<T,N>::makeTorus(const Point_nD<T,N>& O, T R, T r) {
4978   // These define the shape of a unit torus centered about the origin.
4979   T xvalues[] = { 0.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0 };
4980   T yvalues[] = { 1.0, 1.0, 0.0, -1.0, -1.0, -1.0, 0.0, 1.0, 1.0 };
4981   T zvalues[] = { 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0, -1.0, 0.0 };
4982   T offsets[] = { -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0 };
4983 
4984   // Piecewise Bezier knot vector for a quadratic curve with four segments
4985   T knotsMem[] = { 0, 0, 0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75 , 1, 1, 1 };
4986   Vector<T> knots(knotsMem,12) ;
4987 
4988   resize(9,9,2,2);
4989 
4990   int i, j;
4991 
4992   double r2over2 = sqrt( 2.0 ) / 2.0;
4993   double weight;
4994 
4995   for (i = 0; i < 9; i++){
4996     for (j = 0; j < 9; j++) {
4997       HPoint_nD<T,N> hp ;
4998       weight = ((j & 1) ? r2over2 : 1.0) * ((i & 1) ? r2over2 : 1.0);
4999       // Notice how the weights are pre-multiplied with the x, y and z values
5000       P(i,j).x() = xvalues[j]* (R + offsets[i] * r) * weight;
5001       P(i,j).y() = yvalues[j]* (R + offsets[i] * r) * weight;
5002       P(i,j).z() = (zvalues[i] * r) * weight;
5003       P(i,j).w() = weight;
5004     }
5005   }
5006 
5007   // The knot vectors define piecewise Bezier segments
5008   // (the same in both U and V).
5009 
5010   U = knots ;
5011   V = knots ;
5012 
5013   MatrixRT<T> Tx ;
5014   Tx.translate(O.x(),O.y(),O.z()) ;
5015   transform(Tx) ;
5016 }
5017 
5018 
5019 } // end namespace
5020 
5021