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*)°U,sizeof(int))) return 0 ;
2684 if(!fout.write((char*)°V,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