Main Page   Class Hierarchy   Compound List   File List   Compound Members  

nurbsSub.cpp

00001 /*=============================================================================
00002         File: nurbsSub.cpp
00003      Purpose:       
00004     Revision: $Id: nurbsSub.cpp,v 1.3 2003/01/13 19:42:10 philosophil Exp $
00005   Created by: Philippe Lavoie          (20 Januray, 1999)
00006  Modified by: 
00007 
00008  Copyright notice:
00009           Copyright (C) 1999 Philippe Lavoie
00010  
00011           This library is free software; you can redistribute it and/or
00012           modify it under the terms of the GNU Library General Public
00013           License as published by the Free Software Foundation; either
00014           version 2 of the License, or (at your option) any later version.
00015  
00016           This library is distributed in the hope that it will be useful,
00017           but WITHOUT ANY WARRANTY; without even the implied warranty of
00018           MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019           Library General Public License for more details.
00020  
00021           You should have received a copy of the GNU Library General Public
00022           License along with this library; if not, write to the Free
00023           Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00024 =============================================================================*/
00025 
00026 #ifndef PLIB_NURBS_NURBSSUB_SOURCE
00027 #define PLIB_NURBS_NURBSSUB_SOURCE
00028 
00029 #include "nurbsSub.h"
00030 #include <iostream>
00031 #include <cstdio>
00032 #include <stdio.h>
00033 
00036 namespace PLib {
00037 
00038   
00039   const int FALSE_ = 0 ;
00040   const int TRUE_ = 1 ; 
00041 
00042   struct BOOL{
00043     int value ;
00044     BOOL(): value(FALSE_) {;}
00045     BOOL(int a) { if(a>0) value=TRUE_; else value=FALSE_;}
00046     BOOL(const BOOL& b) { value = b.value ; }
00047     int operator!() { if(value>0) return FALSE_; return TRUE_ ;}
00048     operator int() const {return value;}
00049     int& operator=(int a) { value = a ;  return value ; }
00050   };
00051 
00062   template <class T>
00063     NurbsSubSurface<T>::NurbsSubSurface(const NurbsSurface<T,3>& s): rsurf(s) {
00064     surf = 0 ;
00065     render = 0 ; 
00066     //initSurf();
00067     
00068   }
00069 
00070 #define MAXORDER 20         /* Maximum order allowed (for local array sizes) */
00071 
00072   template <class T>
00073     struct NurbSurface {
00074       /* Number of Points in the U and V directions, respectivly */
00075       int numU, numV;
00076       /* Order of the surface in U and V (must be >= 2, < MAXORDER) */
00077       int orderU, orderV;
00078       /* Knot vectors, indexed as [0..numU+orderU-1] and [0..numV+orderV-1] */
00079       T * kvU, * kvV;
00080       /* Control points, indexed as points[0..numV-1][0..numU-1] */
00081       /* Note the w values are *premultiplied* with the x, y and z values */
00082       Matrix<HPoint_nD<T,3> >& points;
00083       
00084       /* These fields are added to support subdivision */
00085       BOOL strV0, strVn,   /* Edge straightness flags for subdivision */
00086       strU0, strUn;
00087       BOOL flatV, flatU;   /* Surface flatness flags for subdivision */
00088       SurfSample<T> c00, c0n,
00089       cn0, cnn;    /* Corner data structures for subdivision */
00090       RenderMesh<T> *render;
00091       
00092       static T epsilon ;
00093       
00094       NurbSurface(): points(tmpPoints) {   render = 0 ; }
00095       NurbSurface(Matrix<HPoint_nD<T,3> >& s, int du, int dv);
00096       
00097     protected:
00098       Matrix<HPoint_nD<T,3> > tmpPoints ;
00099       
00100     };
00101 
00102 #define CHECK( n ) \
00103   { if (!(n)) { fprintf( stderr, "Ran out of memory\n" ); exit(-1); } }
00104 
00105 #define DIVW( rpt, pt ) \
00106   { (pt)->x() = (rpt)->x() / (rpt)->w(); \
00107   (pt)->y() = (rpt)->y() / (rpt)->w(); \
00108   (pt)->z() = (rpt)->z() / (rpt)->w(); }
00109   
00110   
00111   /* Function prototypes */
00112   
00113   template <class T> void DrawSubdivision( NurbSurface<T> *, T tolerance );
00114   template <class T> void DrawEvaluation( NurbSurface<T> * );
00115   
00116   template <class T> int FindBreakPoint( T u, T * kv, int m, int k );
00117   template <class T> void AllocNurb( NurbSurface<T> *, T *, T * );
00118   template <class T> void CloneNurb( NurbSurface<T> *, NurbSurface<T> * );
00119   template <class T> void FreeNurb( NurbSurface<T> * );
00120   template <class T> void RefineSurface( NurbSurface<T> *, NurbSurface<T> *, BOOL );
00121   
00122   template <class T> void CalcPoint( T, T, NurbSurface<T> *, Point_nD<T,3> *, Point_nD<T,3> *, Point_nD<T,3> * );
00123   
00124   
00135   template <class T>
00136     void NurbsSubSurface<T>::drawSubdivision(T tolerance)
00137     {
00138       initSurf();
00139       surf->render = render ; 
00140       DrawSubdivision(surf,tolerance);
00141     }
00142   
00152   template <class T>
00153     void NurbsSubSurface<T>::drawSubdivisionPS(const char* f, T tolerance)
00154     {
00155       ofstream fout ;
00156       fout.open(f) ;
00157       if(fout)
00158         drawSubdivisionPS(fout,tolerance) ;
00159       fout.close();
00160     }
00161 
00172   template <class T>
00173     void NurbsSubSurface<T>::drawSubdivisionVRML(const char* f, T tolerance, const Color& col)
00174     {
00175       ofstream fout ;
00176       fout.open(f) ;
00177       if(fout)
00178         drawSubdivisionVRML(fout,tolerance,col) ;
00179       fout.close();
00180     }
00181 
00192   template <class T>
00193     void NurbsSubSurface<T>::drawSubdivisionVRML97(const char* f, T tolerance, const Color& col)
00194     {
00195       ofstream fout ;
00196       fout.open(f) ;
00197       if(fout)
00198         drawSubdivisionVRML97(fout,tolerance,col) ;
00199       fout.close();
00200     }
00201 
00212   template <class T>
00213     void NurbsSubSurface<T>::drawSubdivisionPS(ostream &os, T tolerance)
00214     {
00215       if(render)
00216         delete render ;
00217       render = new RenderMeshPS<T>(os) ;
00218       drawSubdivision(tolerance) ;
00219     }
00220 
00231   template <class T>
00232     void NurbsSubSurface<T>::drawSubdivisionVRML(ostream &os, T tolerance, const Color& col)
00233     {
00234       if(render) 
00235         delete render ;
00236       render = new RenderMeshVRML<T>(os,col) ;
00237       drawSubdivision(tolerance) ;
00238     }
00239 
00250   template <class T>
00251     void NurbsSubSurface<T>::drawSubdivisionVRML97(ostream &os, T tolerance, const Color& col)
00252     {
00253       if(render) 
00254         delete render ;
00255       render = new RenderMeshVRML97<T>(os,col) ;
00256       drawSubdivision(tolerance) ;
00257     }
00258 
00269   template <class T>
00270   //void NurbsSubSurface<T>::drawSubdivisionPoints(vector<Point_nD<T,3> > &pnts, T tolerance)
00271   void NurbsSubSurface<T>::drawSubdivisionPoints(BasicArray<Point_nD<T,3> >&pnts, T tolerance)
00272     {
00273       if(render) 
00274         delete render ;
00275       render = new RenderMeshPoints<T>(pnts) ;
00276       drawSubdivision(tolerance) ;
00277     }
00278 
00288   template <class T>
00289     void NurbsSubSurface<T>::initSurf()
00290     {
00291       if(surf)
00292         delete surf ;
00293       surf = new NurbSurface<T> ;
00294       surf->numU = rsurf.ctrlPnts().rows() ;
00295       surf->numV = rsurf.ctrlPnts().cols() ; 
00296       surf->orderU = rsurf.degreeU()+1 ;
00297       surf->orderV = rsurf.degreeV()+1 ;
00298       surf->kvU = new T[surf->numU + surf->orderU];
00299       surf->kvV = new T[surf->numV + surf->orderV];
00300       surf->points.resize(surf->numV,surf->numU);
00301       
00302       surf->flatV = FALSE_;
00303       surf->flatU = FALSE_;
00304       surf->strU0 = FALSE_;
00305       surf->strUn = FALSE_;
00306       surf->strV0 = FALSE_;
00307       surf->strVn = FALSE_;
00308 
00309       surf->render = render ; 
00310      
00311       int i;
00312       for(i=rsurf.knotU().n()-1;i>=0;--i){
00313         surf->kvU[i] = rsurf.knotU()[i] ;
00314       }
00315       for(i=rsurf.knotV().n()-1;i>=0;--i){
00316         surf->kvV[i] = rsurf.knotV()[i] ;
00317       }
00318       for(i=rsurf.ctrlPnts().rows()-1;i>=0;--i)
00319         for(int j=rsurf.ctrlPnts().cols()-1;j>=0;--j)
00320           surf->points(j,i) = rsurf.ctrlPnts()(i,j) ; 
00321     }
00322   
00323   template <class T>
00324     NurbSurface<T>::NurbSurface(Matrix<HPoint_nD<T,3> >& s, int du, int dv): points(s), orderU(du+1), orderV(dv+1){
00325     numU = points.rows() ;
00326     numV = points.cols() ;
00327     kvU = new T[numU + orderU] ;
00328     kvV = new T[numV + orderV] ; 
00329     render = 0 ; 
00330   }
00331   
00338   template <class T>
00339     NurbsSubSurface<T>::~NurbsSubSurface()
00340     {      
00341       if(surf){
00342         delete []surf->kvU ;
00343         delete []surf->kvV ;
00344       }
00345     }
00346   
00361   template <class T>
00362     void RenderMeshPS<T>::screenProject(const HPoint_nD<T,3> &worldPt, Point_nD<T,3> &screenPt )
00363     {
00364       screenPt.x() = worldPt.x() / worldPt.w() * 100 + 200;
00365       screenPt.y() = worldPt.y() / worldPt.w() * 100 + 200;
00366       screenPt.z() = worldPt.z() / worldPt.w() * 100 + 200;
00367     }
00368 
00381   template <class T>
00382     void RenderMeshVRML<T>::screenProject(const HPoint_nD<T,3> &worldPt, Point_nD<T,3> &screenPt )
00383     {
00384       screenPt = project(worldPt) ;
00385     }
00386 
00387 
00400   template <class T>
00401     void RenderMeshVRML97<T>::screenProject(const HPoint_nD<T,3> &worldPt, Point_nD<T,3> &screenPt )
00402     {      
00403       screenPt = project(worldPt) ;
00404       if(init){
00405         p_min = p_max = screenPt ;
00406         init = 0 ; 
00407       }
00408       if(screenPt.x()<p_min.x()) p_min.x() = screenPt.x();
00409       if(screenPt.y()<p_min.y()) p_min.y() = screenPt.y();
00410       if(screenPt.z()<p_min.z()) p_min.z() = screenPt.z();
00411       if(screenPt.x()>p_max.x()) p_max.x() = screenPt.x();
00412       if(screenPt.y()>p_max.y()) p_max.y() = screenPt.y();
00413       if(screenPt.z()>p_max.z()) p_max.z() = screenPt.z();
00414     }
00415 
00416 
00429   template <class T>
00430     void RenderMeshPoints<T>::screenProject(const HPoint_nD<T,3> &worldPt, Point_nD<T,3> &screenPt )
00431     {
00432       screenPt = project(worldPt) ;
00433     }
00434 
00435 
00436   inline void LineTo( ostream& out, short x, short y )
00437     {
00438       out << (int)x << " " << (int)y << " lineto\n" ; 
00439     }
00440   
00441   inline void MoveTo( ostream& out, short x, short y )
00442     {
00443       out << (int)x << " " << (int)y << " moveto\n" ; 
00444     }
00445   
00452   template <class T>
00453   void RenderMeshPS<T>::drawHeader(){
00454     out << "%!PS-Adobe-2.1\n";
00455     out << "%%Title: code5_0.ps (20)\n" ;
00456     out << "%%Creator: color_grid_generator\n" ; 
00457     out << "%%BoundingBox: 0 0 500 500\n" ;
00458     out << "%%Pages: 0\n" ;
00459     out << "%%EndComments\n" ;
00460     out << "0 setlinewidth\n" ;
00461     out << "0 0 0 setrgbcolor\n" ;
00462   }
00463 
00470   template <class T>
00471     void RenderMeshPS<T>::drawFooter(){
00472     out << "showpage\n%%EOF\n" ; 
00473   }
00474 
00475 
00486   template <class T>
00487     void RenderMeshPS<T>::drawTriangle( const SurfSample<T> &v0, const SurfSample<T> &v1, const SurfSample<T> & v2 )
00488     {
00489       out << "newpath\n" ;
00490       MoveTo( out, (short) (v0.point.x() * 100 + 200), (short) (v0.point.y() * 100 + 200) );
00491       LineTo( out, (short) (v1.point.x() * 100 + 200), (short) (v1.point.y() * 100 + 200) );
00492       LineTo( out, (short) (v2.point.x() * 100 + 200), (short) (v2.point.y() * 100 + 200) );
00493       LineTo( out, (short) (v0.point.x() * 100 + 200), (short) (v0.point.y() * 100 + 200) );
00494       out << "stroke\n" ;
00495     }
00496 
00506   template <class T>
00507     void RenderMeshPS<T>::drawLine( const SurfSample<T> &v0, const SurfSample<T> &v1)
00508     {
00509       out << "newpath\n" ; 
00510       MoveTo(out , (short) (v0.point.x() * 100 + 200), (short) (v0.point.y() * 100 + 200) );
00511       LineTo( out, (short) (v1.point.x() * 100 + 200), (short) (v1.point.y() * 100 + 200) );
00512       out << "stroke\n" ; 
00513     }
00514 
00515 
00522   template <class T>
00523     void RenderMeshVRML97<T>::drawHeader()
00524     {
00525       size = 0 ; 
00526       out << "#VRML V2.0 utf8\n" ;
00527       out << "#  Generated by Phil's NURBS library\n" ;
00528       out << "\nGroup {\n" ;
00529       out << "\n  children [\n" ; 
00530       out << "\tDEF T Transform {\n"; 
00531       out << "\t  children [\n" ;
00532       out << "\t\tShape {\n" ;
00533       out << "\t\t appearance Appearance {\n" ; 
00534       out << "\t\t\tmaterial Material{ diffuseColor " << float(color.r/255.0) << ' ' << float(color.g/255.0) << ' ' << float(color.b/255.0) << " }\n" ;
00535       out << "\t\t }\n" ; 
00536       out << "\t\t geometry IndexedFaceSet {\n" ;
00537       out << "\t\t\tsolid FALSE\n" ; 
00538       out << "\t\t\tcoord Coordinate {\n" ;
00539       out << "\t\t\t point [\n" ;
00540     }
00541  
00550   template <class T>
00551     void RenderMeshVRML97<T>::drawFooter(){
00552 
00553     out << "\t\t\t ]\n" ; // point [
00554     out << "\t\t\t}\n" ; // coord
00555     
00556     out << "\t\t\t coordIndex [\n" ;
00557   
00558     for(int i=0;i<size;++i){
00559       out << "\t\t\t\t" << 3*i << ", " << 3*i+1 << ", " << 3*i+2 << ", -1,\n" ;
00560     }
00561     out << "\t\t\t ]\n" ;
00562     out << "\t\t\t}\n" ; // IndexedFaceSet
00563     out << "\t\t}\n" ;
00564     out << "\t ]\n" ; 
00565     out << "\t}\n" ;
00566     out << "  ]\n" ; 
00567     out << "}\n" ; 
00568     
00569     Point_nD<T,3> p_mid((p_max.x()+p_min.x())/T(2),
00570                         (p_max.y()+p_min.y())/T(2),
00571                         (p_max.z()+p_min.z())/T(2));
00572     
00573     T x_axis = p_max.x() - p_min.x() ;
00574     T y_axis = p_max.y() - p_min.y() ; 
00575     
00576     T axis = (x_axis< y_axis) ? y_axis : x_axis ;
00577     axis *= T(2) ;
00578     
00579     out << "Viewpoint {\n\t position " << p_mid.x() << ' ' << p_mid.y() << ' ' << p_max.z()+axis << "\n\t description \"top\"\n}\n" ; 
00580     
00581     
00582     out << "NavigationInfo { type \"EXAMINE\" }\n" ; 
00583 
00584   }
00585 
00592   template <class T>
00593     void RenderMeshVRML<T>::drawHeader()
00594     {
00595       size = 0 ; 
00596       out << "#VRML V1.0 ascii\n" ;
00597       out << "#  Generated by Phil's NURBS library\n" ;
00598       out << "\nSeparator {\n" << "\tMaterialBinding { value PER_VERTEX_INDEXED }\n"  ;
00599       out << "\tMaterial{ ambientColor 0.25 0.25 0.25\n\t\tdiffuseColor " << float(color.r/255.0) << ' ' << float(color.g/255.0) << ' ' << float(color.b/255.0) << " }\n" ;
00600       out << "\tCoordinate3 {\n" ;
00601       out << "\t\tpoint [\n" ;
00602     }
00603  
00612   template <class T>
00613     void RenderMeshVRML<T>::drawFooter(){
00614 
00615     out << "\t\t]\n" ; // point [
00616     out << "\t}\n" ; // cordinate3
00617     
00618     out << "\tIndexedFaceSet{\n" ;
00619     out << "\t\tcoordIndex[\n" ;
00620     
00621     for(int i=0;i<size;++i){
00622       out << "\t\t\t" << 3*i << ", " << 3*i+1 << ", " << 3*i+2 << ", -1,\n" ;
00623     }
00624     out << "\t\t]\n" ;
00625     out << "\t}\n" ; // IndexedFaceSet
00626     
00627     out << "}\n" ;
00628   }
00629 
00630 
00643   template <class T>
00644     void RenderMeshVRML<T>::drawTriangle(const SurfSample<T> &v0, const SurfSample<T> &v1, const SurfSample<T> & v2 )
00645     {
00646       ++size ;
00647       out << "\t\t\t" << v0.point.x() << " " << v0.point.y() << " " << v0.point.z() << ",\n" ;
00648       out << "\t\t\t" << v1.point.x() << " " << v1.point.y() << " " << v1.point.z() << ",\n" ;
00649       out << "\t\t\t" << v2.point.x() << " " << v2.point.y() << " " << v2.point.z() << ",\n" ;
00650       
00651     }
00652   
00665   template <class T>
00666     void RenderMeshVRML97<T>::drawTriangle(const SurfSample<T> &v0, const SurfSample<T> &v1, const SurfSample<T> & v2 )
00667     {
00668       ++size ;
00669       out << "\t\t\t\t" << v0.point.x() << " " << v0.point.y() << " " << v0.point.z() << ",\n" ;
00670       out << "\t\t\t\t" << v1.point.x() << " " << v1.point.y() << " " << v1.point.z() << ",\n" ;
00671       out << "\t\t\t\t" << v2.point.x() << " " << v2.point.y() << " " << v2.point.z() << ",\n" ;
00672       
00673     }
00674   
00681   template <class T>
00682     void RenderMeshPoints<T>::drawHeader()
00683     {
00684       points.clear();
00685     }
00686  
00694   template <class T>
00695     void RenderMeshPoints<T>::drawFooter(){
00696   }
00697 
00698 
00711   template <class T>
00712     void RenderMeshPoints<T>::drawTriangle(const SurfSample<T> &v0, const SurfSample<T> &v1, const SurfSample<T> & v2 )
00713     {
00714       // naive method
00715       points.push_back(v0.point);
00716       points.push_back(v1.point);
00717       points.push_back(v2.point);
00718     }
00719   
00720 template <class T>
00721 int
00722 FindBreakPoint( T u, T * kv, int m, int k )
00723 {
00724   int i;
00725   
00726   if (u == kv[m+1])     /* Special case for closed interval */
00727     return m;
00728   
00729   i = m + k;
00730   while ((u < kv[i]) && (i > 0))
00731     i--;
00732   return( i );
00733 }
00734 
00735 /*
00736  * Compute Bi,k(u), for i = 0..k.
00737  *  u           is the parameter of the spline to find the basis functions for
00738  *  brkPoint    is the start of the knot interval ("segment")
00739  *  kv          is the knot vector
00740  *  k           is the order of the curve
00741  *  bvals       is the array of returned basis values.
00742  *
00743  * (From Bartels, Beatty & Barsky, p.387)
00744  */
00745 
00746 template <class T> void
00747 BasisFunctions( T u, int brkPoint, T * kv, int k, T * bvals )
00748 {
00749   int r, s, i;
00750   T omega;
00751   
00752   bvals[0] = 1.0;
00753   for (r = 2; r <= k; r++)
00754     {
00755       i = brkPoint - r + 1;
00756       bvals[r - 1] = 0.0;
00757       for (s = r-2; s >= 0; s--)
00758         {
00759           i++;
00760           if (i < 0)
00761             omega = 0;
00762           else
00763             omega = (u - kv[i]) / (kv[i + r - 1] - kv[i]);
00764           bvals[s + 1] = bvals[s + 1] + (1 - omega) * bvals[s];
00765           bvals[s] = omega * bvals[s];
00766         }
00767     }
00768 }
00769 
00770 /*
00771  * Compute derivatives of the basis functions Bi,k(u)'
00772  */
00773 template <class T> void
00774 BasisDerivatives( T u, int brkPoint, T * kv, int k, T * dvals )
00775 {
00776   register int s, i;
00777   T omega, knotScale;
00778 
00779   BasisFunctions( u, brkPoint, kv, k - 1, dvals );
00780   
00781   dvals[k-1] = 0.0;         /* BasisFunctions misses this */
00782   
00783   knotScale = kv[brkPoint + 1] - kv[brkPoint];
00784   
00785   i = brkPoint - k + 1;
00786   for (s = k - 2; s >= 0; s--)
00787     {
00788       i++;
00789       omega = knotScale * ((T)(k-1)) / (kv[i+k-1] - kv[i]);
00790       dvals[s + 1] += -omega * dvals[s];
00791       dvals[s] *= omega;
00792     }
00793 }
00794 
00795 /*
00796  * Calculate a point p on NurbSurface<T> n at a specific u, v using the tensor product.
00797  * If utan and vtan are not nil, compute the u and v tangents as well.
00798  *
00799  * Note the valid parameter range for u and v is
00800  * (kvU[orderU] <= u < kvU[numU), (kvV[orderV] <= v < kvV[numV])
00801  */
00802 
00803 template <class T>
00804 void
00805 CalcPoint(T u, T v, NurbSurface<T> * n,
00806            Point_nD<T,3> * p, Point_nD<T,3> * utan, Point_nD<T,3> * vtan)
00807 {
00808   int i, j, ri, rj;
00809   HPoint_nD<T,3> * cp;
00810   T tmp;
00811   T wsqrdiv;
00812   int ubrkPoint, ufirst;
00813   T bu[MAXORDER], buprime[MAXORDER];
00814   int vbrkPoint, vfirst;
00815   T bv[MAXORDER], bvprime[MAXORDER];
00816   HPoint_nD<T,3> r, rutan, rvtan;
00817   
00818   r.x() = 0.0;
00819   r.y() = 0.0;
00820   r.z() = 0.0;
00821   r.w() = 0.0;
00822   
00823   rutan = r;
00824   rvtan = r;
00825   
00826   /* Evaluate non-uniform basis functions (and derivatives) */
00827   
00828   ubrkPoint = FindBreakPoint( u, n->kvU, n->numU-1, n->orderU );
00829   ufirst = ubrkPoint - n->orderU + 1;
00830   BasisFunctions( u, ubrkPoint, n->kvU, n->orderU, bu );
00831   if (utan)
00832     BasisDerivatives( u, ubrkPoint, n->kvU, n->orderU, buprime );
00833   
00834   vbrkPoint = FindBreakPoint( v, n->kvV, n->numV-1, n->orderV );
00835   vfirst = vbrkPoint - n->orderV + 1;
00836   BasisFunctions( v, vbrkPoint, n->kvV, n->orderV, bv );
00837   if (vtan)
00838     BasisDerivatives( v, vbrkPoint, n->kvV, n->orderV, bvprime );
00839   
00840   /* Weight control points against the basis functions */
00841   
00842   for (i = 0; i < n->orderV; i++)
00843     for (j = 0; j < n->orderU; j++)
00844         {
00845           ri = n->orderV - 1 - i;
00846           rj = n->orderU - 1 - j;
00847           
00848           tmp = bu[rj] * bv[ri];
00849           cp = &( n->points(i+vfirst,j+ufirst) );
00850           r.x() += cp->x() * tmp;
00851           r.y() += cp->y() * tmp;
00852           r.z() += cp->z() * tmp;
00853           r.w() += cp->w() * tmp;
00854 
00855           if (utan)
00856             {
00857               tmp = buprime[rj] * bv[ri];
00858               rutan.x() += cp->x() * tmp;
00859               rutan.y() += cp->y() * tmp;
00860               rutan.z() += cp->z() * tmp;
00861               rutan.w() += cp->w() * tmp;
00862             }
00863           if (vtan)
00864             {
00865               tmp = bu[rj] * bvprime[ri];
00866               rvtan.x() += cp->x() * tmp;
00867               rvtan.y() += cp->y() * tmp;
00868               rvtan.z() += cp->z() * tmp;
00869               rvtan.w() += cp->w() * tmp;
00870             }
00871         }
00872   
00873   /* Project tangents, using the quotient rule for differentiation */
00874   
00875   wsqrdiv = 1.0 / (r.w() * r.w());
00876   if (utan)
00877     {
00878       utan->x() = (r.w() * rutan.x() - rutan.w() * r.x()) * wsqrdiv;
00879       utan->y() = (r.w() * rutan.y() - rutan.w() * r.y()) * wsqrdiv;
00880       utan->z() = (r.w() * rutan.z() - rutan.w() * r.z()) * wsqrdiv;
00881     }
00882   if (vtan)
00883     {
00884       vtan->x() = (r.w() * rvtan.x() - rvtan.w() * r.x()) * wsqrdiv;
00885       vtan->y() = (r.w() * rvtan.y() - rvtan.w() * r.y()) * wsqrdiv;
00886       vtan->z() = (r.w() * rvtan.z() - rvtan.w() * r.z()) * wsqrdiv;
00887     }
00888   
00889   p->x() = r.x() / r.w();
00890   p->y() = r.y() / r.w();
00891   p->z() = r.z() / r.w();
00892 }
00893 
00894 /*
00895  * Draw a mesh of points by evaluating the surface at evenly spaced
00896  * points.
00897  */
00898 template <class T>
00899 void
00900 DrawEvaluation( NurbSurface<T> * n )
00901 {
00902   Point_nD<T,3> p, utan, vtan;
00903   register int i, j;
00904   register T u, v, d;
00905   SurfSample<T> ** pts ;
00906   
00907   int Granularity = 10;  /* Controls the number of steps in u and v */
00908   
00909   /* Allocate storage for the grid of points generated */
00910   
00911   CHECK( pts = new (SurfSample<T>*)[Granularity+1]);
00912   CHECK( pts[0] = new SurfSample<T>[(Granularity+1)*(Granularity+1)]);
00913   
00914   for (i = 1; i <= Granularity; i++)
00915     pts[i] = &(pts[0][(Granularity+1) * i]);
00916   
00917   /* Compute points on curve */
00918   
00919   for (i = 0; i <= Granularity; i++)
00920     {
00921       v = ((T) i / (T) Granularity)
00922             * (n->kvV[n->numV] - n->kvV[n->orderV-1])
00923         + n->kvV[n->orderV-1];
00924       
00925       for (j = 0; j <= Granularity; j++)
00926         {
00927           u = ((T) j / (T) Granularity)
00928             * (n->kvU[n->numU] - n->kvU[n->orderU-1])
00929             + n->kvU[n->orderU-1];
00930           
00931           CalcPoint( u, v, n, &(pts[i][j].point), &utan, &vtan );
00932           p = crossProduct(utan,vtan) ; //(void) V3Cross( &utan, &vtan, &p );
00933           d = norm(p) ; // d = V3Length( &p );
00934           if (d != 0.0)
00935             {
00936               p.x() /= d;
00937               p.y() /= d;
00938               p.z() /= d;
00939             }
00940           else
00941             {
00942               p.x() = 0;
00943               p.y() = 0;
00944               p.z() = 0;
00945             }
00946           pts[i][j].normLen = d;
00947           pts[i][j].normal = p;
00948           pts[i][j].u = u;
00949           pts[i][j].v = v;
00950         }
00951     }
00952   
00953   /* Draw the grid */
00954   
00955   for (i = 0; i < Granularity; i++)
00956     for (j = 0; j < Granularity; j++)
00957       {
00958         n->render->drawTriangle( pts[i][j], pts[i+1][j+1], pts[i+1][j] );
00959         n->render->drawTriangle( pts[i][j], pts[i][j+1],   pts[i+1][j+1] );
00960       }
00961   
00962   delete [] pts[0];
00963   delete [] pts ;
00964 }
00965 
00966 /*
00967  * NurbRefine.c - Given a refined knot vector, add control points to a surface.
00968  *
00969  * John Peterson
00970  */
00971 
00972 
00973 /*
00974  * Given the original knot vector ukv, and a new knotvector vkv, compute
00975  * the "alpha matrix" used to generate the corresponding new control points.
00976  * This routines allocates the alpha matrix if it isn't allocated already.
00977  *
00978  * This is from Bartels, Beatty & Barsky, p. 407
00979  */
00980 template <class T> void
00981 CalcAlpha( T * ukv, T * wkv, int m, int n, int k, T *** alpha )
00982 {
00983   int i, j;
00984   int brkPoint, r, rm1, last, s;
00985   T omega;
00986   T aval[MAXORDER];
00987   
00988   if (! *alpha) /* Must allocate alpha */
00989     {
00990       CHECK( *alpha = new (T*)[k+1]);
00991       for (i = 0; i <= k; i++)
00992         CHECK( (*alpha)[i] = new T[(m + n + 1)]);
00993     }
00994   
00995   for (j = 0; j <= m + n; j++)
00996     {
00997       brkPoint = FindBreakPoint( wkv[j], ukv, m, k );
00998       aval[0] = 1.0;
00999       for (r = 2; r <= k; r++)
01000         {
01001           rm1 = r - 1;
01002           last = minimum( rm1, brkPoint );
01003           i = brkPoint - last;
01004           if (last < rm1)
01005             aval[last] = aval[last] * (wkv[j + r - 1] - ukv[i])
01006               / (ukv[i + r - 1] - ukv[i]);
01007           else
01008             aval[last] = 0.0;
01009           
01010           for (s = last - 1; s >= 0; s-- )
01011             {
01012               i++;
01013               omega = (wkv[j + r - 1] - ukv[i]) / (ukv[i + r - 1] - ukv[i]);
01014               aval[s + 1] = aval[s+1] + (1 - omega) * aval[s];
01015               aval[s] = omega * aval[s];
01016             }
01017         }
01018       last = minimum( k - 1, brkPoint );
01019       for (i = 0; i <= k; i++)
01020         (*alpha)[i][j] = 0.0;
01021       for (s = 0; s <= last; s++)
01022         (*alpha)[last - s][j] = aval[s];
01023     }
01024 }
01025 
01026 /*
01027  * Apply the alpha matrix computed above to the rows (or columns)
01028  * of the surface.  If dirflag is TRUE_ do the U's (row), else do V's (col).
01029  */
01030 template <class T>
01031 void
01032 RefineSurface( NurbSurface<T> * src, NurbSurface<T> * dest, BOOL dirflag )
01033 {
01034   int i, j, out;
01035   HPoint_nD<T,3> * dp, * sp;
01036   int i1, brkPoint, maxj, maxout;
01037   register T tmp;
01038   T ** alpha = 0;
01039   
01040   // Compute the alpha matrix and indexing variables for the requested direction 
01041   
01042   if (dirflag)
01043     {
01044       CalcAlpha( src->kvU, dest->kvU, src->numU - 1, dest->numU - src->numU,
01045                  src->orderU, &alpha );
01046       maxj = dest->numU;
01047       maxout = src->numV;
01048     }
01049   else
01050     {
01051       CalcAlpha( src->kvV, dest->kvV, src->numV - 1, dest->numV - src->numV,
01052                  src->orderV, &alpha );
01053       maxj = dest->numV;
01054       maxout = dest->numU;
01055     }
01056   
01057   /* Apply the alpha matrix to the original control points, generating new ones */
01058   
01059   for (out = 0; out < maxout; out++)
01060     for (j = 0; j < maxj; j++)
01061       {
01062         if (dirflag)
01063           {
01064             dp = &(dest->points(out,j));
01065             brkPoint = FindBreakPoint( dest->kvU[j], src->kvU,
01066                                        src->numU-1, src->orderU );
01067             i1 = maximum( brkPoint - src->orderU + 1, 0 );
01068             sp = &(src->points(out,i1));
01069           } else {
01070             dp = &(dest->points(j,out));
01071             brkPoint = FindBreakPoint( dest->kvV[j], src->kvV,
01072                                        src->numV-1, src->orderV );
01073             i1 = maximum( brkPoint - src->orderV + 1, 0 );
01074             sp = &(src->points(i1,out));
01075           }
01076         dp->x() = 0.0;
01077         dp->y() = 0.0;
01078         dp->z() = 0.0;
01079         dp->w() = 0.0;
01080         for (i = i1; i <= brkPoint; i++)
01081           {
01082             tmp = alpha[i - i1][j];
01083             sp = (dirflag ? &(src->points(out,i)) : &(src->points(i,out)) );
01084             dp->x() += tmp * sp->x();
01085             dp->y() += tmp * sp->y();
01086             dp->z() += tmp * sp->z();
01087             dp->w() += tmp * sp->w();
01088           }
01089       }
01090   
01091   /* Free up the alpha matrix */
01092   for (i = 0; i <= (dirflag ? src->orderU : src->orderV); i++)
01093     delete [] alpha[i] ;
01094   delete [] alpha ;
01095 }
01096 
01097 /*
01098  * NurbUtils.c - Code for Allocating, freeing, & copying NURB surfaces.
01099  *
01100  * John Peterson
01101  */
01102 
01103 
01104 /*
01105  * Allocate memory for a NURB (assumes numU, numV, orderU
01106  * and orderV have been set).  If ukv or vkv are not NIL, they
01107  * are assumed to be pointers to valid knot vectors.
01108  */
01109 
01110 template <class T>
01111 void
01112 AllocNurb( NurbSurface<T> * n, T * ukv, T * vkv )
01113 {
01114   int i;
01115   
01116   if (! ukv)
01117     n->kvU = new T[n->numU + n->orderU] ;
01118   else
01119     n->kvU = ukv;
01120   if (! vkv)
01121     n->kvV = new T[n->numV + n->orderV];
01122   else
01123     n->kvV = vkv;
01124   
01125   n->points.resize(n->numV,n->numU) ;
01126 }
01127 
01128 /*
01129  * Release storage for a patch
01130  */
01131 
01132 template <class T>
01133 void
01134 FreeNurb( NurbSurface<T> * n )
01135 {
01136   int i;
01137   
01138   if (n->kvU) delete [] n->kvU ;
01139   n->kvU = 0;
01140   if (n->kvV) delete [] n->kvV ;
01141   n->kvV = 0;
01142   delete n ; 
01143   n = 0 ; 
01144   // Don't touch render, it might still be used.
01145 }
01146 
01147 /*
01148  * Clone a nurb (deep copy)
01149  */
01150 
01151 template <class T>
01152 void
01153 CloneNurb( NurbSurface<T> * src, NurbSurface<T> * dst )
01154 {
01155   int i, j;
01156   T * srcp, *dstp;
01157   
01158   //*dst = *src;        /* Copy fields that don't change */
01159   dst->numU = src->numU ;
01160   dst->numV = src->numV ;
01161   dst->orderU = src->orderU ;
01162   dst->orderV = src->orderV ;
01163   
01164   dst->strU0 = src->strU0 ; 
01165   dst->strUn = src->strUn ; 
01166   dst->strV0 = src->strV0 ; 
01167   dst->strVn = src->strVn ; 
01168   
01169   dst->kvU = 0;
01170   dst->kvV = 0; /* So they get allocated */
01171   dst->points = 0;
01172   
01173   AllocNurb( dst, (T*)0, (T*)0 );
01174   
01175   /* Copy kv's */
01176   srcp = src->kvU;
01177   dstp = dst->kvU;
01178   for (i = 0; i < src->numU + src->orderU; i++)
01179     *dstp++ = *srcp++;
01180   
01181   srcp = src->kvV;
01182   dstp = dst->kvV;
01183   for (i = 0; i < src->numV + src->orderV; i++)
01184     *dstp++ = *srcp++;
01185 
01186   /* Copy control points */
01187   for (i = 0; i < src->numV; i++)
01188     for (j = 0; j < src->numU; j++)
01189       dst->points(i,j) = src->points(i,j);
01190 }
01191 
01192 /*
01193  * NurbSubdiv.c - Perform adaptive subdivision on a NURB surface.
01194  *
01195  * John Peterson
01196  *
01197  */
01198 
01199 #define DIVPT( p, dn ) { ((p).x()) /= (dn); ((p).y()) /= (dn); ((p).z()) /= (dn); }
01200 
01201 #define maxV(surf) ((surf)->numV-1)
01202 #define maxU(surf) ((surf)->numU-1)
01203 
01204 /*
01205  * Split a knot vector at the center, by adding multiplicity k knots near
01206  * the middle of the parameter range.  Tries to start with an existing knot,
01207  * but will add a new knot value if there's nothing in "the middle" (e.g.,
01208  * a Bezier curve).
01209  */
01210 template <class T> int
01211 SplitKV( T * srckv,
01212          T ** destkv,
01213          int * splitPt,    /* Where the knot interval is split */
01214          int m, int k )
01215 {
01216   int i, last;
01217   int middex, extra, same;      /* "middex" ==> "middle index" */
01218   T midVal;
01219   
01220   extra = 0;
01221   last = (m + k);
01222   
01223   middex = last / 2;
01224   midVal = srckv[middex];
01225   
01226   /* Search forward and backward to see if multiple knot is already there */
01227   
01228   i = middex+1;
01229   same = 1;
01230   while ((i < last) && (srckv[i] == midVal)) {
01231     i++;
01232     same++;
01233   }
01234   
01235   i = middex-1;
01236   while ((i > 0) && (srckv[i] == midVal)) {
01237     i--;
01238     middex--;       /* middex is start of multiple knot */
01239     same++;
01240   }
01241   
01242   if (i <= 0)       /* No knot in middle, must create it */
01243     {
01244       midVal = (srckv[0] + srckv[last]) / 2.0;
01245       middex = last / 2;
01246       while (srckv[middex + 1] < midVal)
01247         middex++;
01248       same = 0;
01249     }
01250   
01251   extra = k - same;
01252   *destkv = new T[m+k+extra+1];
01253   
01254   if (same < k)     /* Must add knots */
01255     {
01256       for (i = 0; i <= middex; i++)
01257         (*destkv)[i] = srckv[i];
01258       
01259       for (i = middex+1; i <= middex+extra; i++)
01260         (*destkv)[i] = midVal;
01261       
01262       for (i = middex + k - same + 1; i <= m + k + extra; i++)
01263         (*destkv)[i] = srckv[i - extra];
01264     }
01265   else
01266     {
01267       for (i = 0; i <= m + k; i++)
01268         (*destkv)[i] = srckv[i];
01269     }
01270   
01271   *splitPt = (extra < k) ? middex - 1 : middex;
01272   return( extra );
01273 }
01274 
01275 /*
01276  * Given a line defined by firstPt and lastPt, project midPt onto
01277  * that line.  Used for fixing "cracks".
01278  */
01279 template <class T> void
01280 ProjectToLine( Point_nD<T,3> * firstPt, Point_nD<T,3> * lastPt, Point_nD<T,3> * midPt )
01281 {
01282   Point_nD<T,3> base, v0, vm;
01283   T fraction, denom;
01284   
01285   base = *firstPt;
01286   
01287   v0 = *lastPt - base ; // (void) V3Sub( lastPt, &base, &v0 );
01288   vm = *midPt - base ; // (void) V3Sub( midPt, &base, &vm );
01289   
01290   denom = norm2(v0) ; // V3SquaredLength( &v0 );
01291   //fraction = (denom == 0.0) ? 0.0 : (V3Dot( &v0, &vm ) / denom);
01292   fraction = (denom == 0.0) ? 0.0 : (v0*vm ) / denom;
01293   
01294   midPt->x() = base.x() + fraction * v0.x();
01295   midPt->y() = base.y() + fraction * v0.y();
01296   midPt->z() = base.z() + fraction * v0.z();
01297 }
01298 
01299 /*
01300  * If a normal has collapsed to zero (normLen == 0.0) then try
01301  * and fix it by looking at its neighbors.  If all the neighbors
01302  * are sick, then re-compute them from the plane they form.
01303  * If that fails too, then we give up...
01304  */
01305 template <class T> void
01306 FixNormals( SurfSample<T> * s0, SurfSample<T> * s1, SurfSample<T> * s2 )
01307 {
01308   BOOL goodnorm;
01309   int i, j, ok;
01310   T dist;
01311   SurfSample<T> * V[3];
01312   Point_nD<T,3> normal;
01313   
01314   V[0] = s0; V[1] = s1; V[2] = s2;
01315   
01316   /* Find a reasonable normalal */
01317   for (ok = 0, goodnorm = FALSE_;
01318        (ok < 3L) && !(goodnorm = (V[ok]->normLen > 0.0)); ok++);
01319   
01320   if (! goodnorm)       /* All provided normals are zilch, try and invent one */
01321     {
01322       normal.x() = 0.0; normal.y() = 0.0; normal.z() = 0.0;
01323       
01324       for (i = 0; i < 3L; i++)
01325         {
01326           j = (i + 1) % 3L;
01327           normal.x() += (V[i]->point.y() - V[j]->point.y()) * (V[i]->point.z() + V[j]->point.z());
01328           normal.y() += (V[i]->point.z() - V[j]->point.z()) * (V[i]->point.x() + V[j]->point.x());
01329           normal.z() += (V[i]->point.x() - V[j]->point.x()) * (V[i]->point.y() + V[j]->point.y());
01330         }
01331       //dist = V3Length( &norm );
01332       dist = norm(normal) ; 
01333       if (dist == 0.0)
01334         return;         /* This sucker's hopeless... */
01335       
01336       DIVPT( normal, dist );
01337       
01338       for (i = 0; i < 3; i++)
01339         {
01340           V[i]->normal = normal;
01341           V[i]->normLen = dist;
01342         }
01343     }
01344   else      /* Replace a sick normal with a healthy one nearby */
01345     {
01346       for (i = 0; i < 3; i++)
01347         if ((i != ok) && (V[i]->normLen == 0.0))
01348           V[i]->normal = V[ok]->normal;
01349     }
01350   return;
01351 }
01352 
01353 /*
01354  * Normalize the normal in a sample.  If it's degenerate,
01355  * flag it as such by setting the normLen to 0.0
01356  */
01357 template <class T> void
01358 AdjustNormal( SurfSample<T> * samp )
01359 {
01360   // If it's not degenerate, do the normalization now */
01361   samp->normLen = norm(samp->normal) ; // V3Length( &(samp->normal) );
01362 
01363   if (samp->normLen < samp->epsilon )
01364     samp->normLen = 0.0;
01365   else
01366     DIVPT( (samp->normal), samp->normLen );
01367 }
01368 
01369 /*
01370  * Compute the normal of a corner point of a mesh.  The
01371  * base is the value of the point at the corner, indU and indV
01372  * are the mesh indices of that point (either 0 or numU|numV).
01373  */
01374 template <class T> void
01375 GetNormal( NurbSurface<T> * n, int indV, int indU )
01376 {
01377   Point_nD<T,3> tmpL, tmpR;     /* "Left" and "Right" of the base point */
01378   SurfSample<T> * crnr;
01379   
01380   if ( (indU && indV) || ((! indU) && (!indV)) )
01381     {
01382       if (indU)
01383         crnr = &(n->cnn);
01384       else
01385         crnr = &(n->c00);
01386       DIVW( &(n->points(indV,(indU ? (indU-1) : 1))), &tmpL );
01387       DIVW( &(n->points((indV ? (indV-1) : 1),indU)), &tmpR );
01388     }
01389   else
01390     {
01391       if (indU)
01392         crnr = &(n->c0n);
01393       else
01394         crnr = &(n->cn0);
01395       DIVW( &(n->points(indV,(indU ? (indU-1) : 1))), &tmpR );
01396       DIVW( &(n->points((indV ? (indV-1) : 1),indU)), &tmpL );
01397     }
01398   
01399   tmpL -= crnr->point ; //(void) V3Sub( &tmpL, &(crnr->point), &tmpL );
01400   tmpR -= crnr->point ;//(void) V3Sub( &tmpR, &(crnr->point), &tmpR );
01401   crnr->normal = crossProduct(tmpL,tmpR); //(void) V3Cross( &tmpL, &tmpR, &(crnr->normal) );
01402   AdjustNormal( crnr );
01403 }
01404 
01405 /*
01406  * Build the new corners in the two new surfaces, computing both
01407  * point on the surface aint with the normal.   Prevent cracks that may occur.
01408  */
01409 template <class T> void
01410 MakeNewCorners( NurbSurface<T> * parent,
01411                 NurbSurface<T> * kid0,
01412                 NurbSurface<T> * kid1,
01413                 BOOL dirflag )
01414 {
01415   DIVW( &(kid0->points(maxV(kid0),maxU(kid0))), &(kid0->cnn.point) );
01416   GetNormal( kid0, maxV(kid0), maxU(kid0) );
01417   
01418   if (dirflag)
01419     {
01420       kid0->strUn = FALSE_;     /* Must re-test new edge straightness */
01421       
01422       DIVW( &(kid0->points(0,maxU(kid0))), &(kid0->c0n.point) );
01423       GetNormal( kid0, 0, maxU(kid0) );
01424       /*
01425        * Normals must be re-calculated for kid1 in case the surface
01426        * was split at a c1 (or c0!) discontinutiy
01427        */
01428       kid1->c00.point = kid0->c0n.point;
01429       GetNormal( kid1, 0, 0 );
01430       kid1->cn0.point = kid0->cnn.point;
01431       GetNormal( kid1, maxV(kid1), 0 );
01432       
01433       /*
01434        * Prevent cracks from forming by forcing the points on the seam to
01435        * lie aint any straight edges.  (Must do this BEFORE finding normals)
01436        */
01437       if (parent->strV0)
01438         ProjectToLine( &(parent->c00.point),
01439                        &(parent->c0n.point),
01440                        &(kid0->c0n.point) );
01441       if (parent->strVn)
01442         ProjectToLine( &(parent->cn0.point),
01443                        &(parent->cnn.point),
01444                        &(kid0->cnn.point) );
01445       
01446       kid1->c00.point = kid0->c0n.point;
01447       kid1->cn0.point = kid0->cnn.point;
01448       kid1->strU0 = FALSE_;
01449     }
01450   else
01451     {
01452       kid0->strVn = FALSE_;
01453       
01454       DIVW( &(kid0->points(maxV(kid0),0)), &(kid0->cn0.point) );
01455       GetNormal( kid0, maxV(kid0), 0 );
01456       kid1->c00.point = kid0->cn0.point;
01457       GetNormal( kid1, 0, 0 );
01458       kid1->c0n.point = kid0->cnn.point;
01459       GetNormal( kid1, 0, maxU(kid1) );
01460       
01461       if (parent->strU0)
01462         ProjectToLine( &(parent->c00.point),
01463                        &(parent->cn0.point),
01464                        &(kid0->cn0.point) );
01465       if (parent->strUn)
01466         ProjectToLine( &(parent->c0n.point),
01467                        &(parent->cnn.point),
01468                        &(kid0->cnn.point) );
01469       
01470       kid1->c00.point = kid0->cn0.point;
01471       kid1->c0n.point = kid0->cnn.point;
01472       kid1->strV0 = FALSE_;
01473     }
01474 }
01475 
01476 /*
01477  * Split a surface into two halves.  First inserts multiplicity k knots
01478  * in the center of the parametric range.  After refinement, the two
01479  * resulting surfaces are copied into separate data structures.  If the
01480  * parent surface had straight edges, the points of the children are
01481  * projected onto those edges.
01482  */
01483 template <class T> void
01484 SplitSurface( NurbSurface<T> * parent,
01485               NurbSurface<T> * kid0, NurbSurface<T> * kid1,
01486               BOOL dirflag )        /* If TRUE_ subdivided in U, else in V */
01487 {
01488   NurbSurface<T> *tmp;
01489   T * newkv;
01490   int i, j, splitPt;
01491   
01492   tmp = new NurbSurface<T> ;
01493 
01494   //
01495   // Add a multiplicty k knot to the knot vector in the direction
01496   // specified by dirflag, and refine the surface.  This creates two
01497   // adjacent surfaces with c0 discontinuity at the seam.
01498   //
01499 
01500   //tmp = *parent;      // Copy order, # of points, etc. 
01501   tmp->numU = parent->numU ;
01502   tmp->numV = parent->numV ;
01503   tmp->orderU = parent->orderU ;
01504   tmp->orderV = parent->orderV ;
01505   
01506   tmp->strU0 = parent->strU0 ; 
01507   tmp->strUn = parent->strUn ; 
01508   tmp->strV0 = parent->strV0 ; 
01509   tmp->strVn = parent->strVn ; 
01510   
01511   tmp->render = parent->render ; 
01512   
01513   if (dirflag)
01514     {
01515       tmp->numU = parent->numU + SplitKV( parent->kvU,
01516                                          &newkv,
01517                                          &splitPt,
01518                                          maxU(parent),
01519                                          parent->orderU );
01520       AllocNurb( tmp, newkv, (T*)0 );
01521       for (i = 0; i < tmp->numV + tmp->orderV; i++)
01522         tmp->kvV[i] = parent->kvV[i];
01523     }
01524   else
01525     {
01526       tmp->numV = parent->numV + SplitKV( parent->kvV,
01527                                          &newkv,
01528                                          &splitPt,
01529                                          maxV(parent),
01530                                          parent->orderV );
01531       AllocNurb( tmp, (T*)0, newkv );
01532       for (i = 0; i < tmp->numU + tmp->orderU; i++)
01533         tmp->kvU[i] = parent->kvU[i];
01534     }
01535   RefineSurface( parent, tmp, dirflag );
01536   
01537   //
01538   // Build the two child surfaces, and copy the data from the refined
01539   // version of the parent (tmp) into the two children
01540   //
01541 
01542   // First half 
01543   
01544   //    *kid0 = *parent;        // copy various edge flags and orders 
01545   kid0->orderU = parent->orderU ;
01546   kid0->orderV = parent->orderV ;
01547   
01548   kid0->strU0 = parent->strU0 ; 
01549   kid0->strUn = parent->strUn ; 
01550   kid0->strV0 = parent->strV0 ; 
01551   kid0->strVn = parent->strVn ; 
01552   
01553   kid0->c00 = parent->c00 ;
01554   kid0->c0n = parent->c0n ;
01555   kid0->cn0 = parent->cn0 ;
01556   kid0->cnn = parent->cnn ;
01557   
01558   kid0->render = parent->render ; 
01559   
01560   kid0->numU = dirflag ? splitPt+1 : parent->numU;
01561   kid0->numV = dirflag ? parent->numV : splitPt+1;
01562   kid0->kvU = kid0->kvV = 0;
01563   AllocNurb( kid0, (T*)0, (T*)0 );
01564   
01565   for (i = 0; i < kid0->numV; i++)      // Copy the point and kv data 
01566     for (j = 0; j < kid0->numU; j++)
01567       kid0->points(i,j) = tmp->points(i,j) ;
01568   for (i = 0; i < kid0->orderU + kid0->numU; i++)
01569     kid0->kvU[i] = tmp->kvU[i];
01570   for (i = 0; i < kid0->orderV + kid0->numV; i++)
01571     kid0->kvV[i] = tmp->kvV[i];
01572   
01573   // Second half 
01574   
01575   splitPt++;
01576   //*kid1 = *parent;
01577   kid1->orderU = parent->orderU ;
01578   kid1->orderV = parent->orderV ;
01579   
01580   kid1->strU0 = parent->strU0 ; 
01581   kid1->strUn = parent->strUn ; 
01582   kid1->strV0 = parent->strV0 ; 
01583   kid1->strVn = parent->strVn ; 
01584 
01585   kid1->c00 = parent->c00 ;
01586   kid1->c0n = parent->c0n ;
01587   kid1->cn0 = parent->cn0 ;
01588   kid1->cnn = parent->cnn ;
01589   
01590   kid1->render = parent->render ; 
01591   
01592   kid1->numU = dirflag ? tmp->numU - splitPt : parent->numU;
01593   kid1->numV = dirflag ? parent->numV : tmp->numV - splitPt;
01594   kid1->kvU = kid1->kvV = 0;
01595   AllocNurb( kid1, (T*)0, (T*)0 );
01596   
01597   for (i = 0; i < kid1->numV; i++)      // Copy the point and kv data 
01598     for (j = 0; j < kid1->numU; j++)
01599       kid1->points(i,j)
01600         = tmp->points(dirflag ? i: (i + splitPt) ,dirflag ? (j + splitPt) : j);
01601   for (i = 0; i < kid1->orderU + kid1->numU; i++)
01602     kid1->kvU[i] = tmp->kvU[dirflag ? (i + splitPt) : i];
01603   for (i = 0; i < kid1->orderV + kid1->numV; i++)
01604     kid1->kvV[i] = tmp->kvV[dirflag ? i : (i + splitPt)];
01605     
01606   // Construct new corners on the boundry between the two kids 
01607   MakeNewCorners( parent, kid0, kid1, dirflag );
01608   
01609   FreeNurb( tmp );          // Get rid of refined parent 
01610     
01611 }
01612 
01613 /*
01614  * Test if a particular row or column of control points in a mesh
01615  * is "straight" with respect to a particular tolerance.  Returns TRUE_
01616  * if it is.
01617  */
01618 
01619 #define GETPT( i )  (( dirflag ? (n->points(crvInd,i)) : (n->points(i,crvInd)) ))
01620 
01621 #define EPSILON n->epsilon
01622 
01623 template <class T> BOOL
01624 IsCurveStraight( NurbSurface<T> * n,
01625                  T tolerance,
01626                  int crvInd,
01627                  BOOL dirflag )  /* If TRUE_, test in U direction, else test in V */
01628 {
01629   Point_nD<T,3> p, vec, prod;
01630   Point_nD<T,3> cp, e0;
01631   int i, last;
01632   T linelen, dist;
01633   
01634   /* Special case: lines are automatically straight. */
01635   if ((dirflag ? n->numU : n->numV) == 2)
01636     return( TRUE_ );
01637   
01638   last = (dirflag ? n->numU : n->numV) - 1;
01639   n->render->screenProject( GETPT( 0 ), e0 );
01640   
01641   /* Form an initial line to test the other points against (skiping degen lines) */
01642   
01643   linelen = 0.0;
01644   for (i = last; (i > 0) && (linelen < EPSILON); i--)
01645     {
01646       n->render->screenProject( GETPT( i ), cp );
01647       vec = cp - e0 ; 
01648       linelen = norm(vec) ;
01649     }
01650   
01651   DIVPT( vec, linelen );
01652 
01653   if (linelen > EPSILON)        /* If no non-degenerate lines found, it's all degen */
01654     for (i = 1; i <= last; i++)
01655       {
01656         /* The cross product of the vector defining the
01657          * initial line with the vector of the current point
01658          * gives the distance to the line. */
01659         n->render->screenProject( GETPT( i ), cp );
01660         p = cp - e0 ; 
01661         
01662         prod = crossProduct(p,vec) ; 
01663         dist = norm(prod) ; 
01664         
01665         if (dist > tolerance)
01666           return( FALSE_ );
01667       }
01668   
01669   return( TRUE_ );
01670 }
01671 
01672 /*
01673  * Check to see if a surface is flat.  Tests are only performed on edges and
01674  * directions that aren't already straight.  If an edge is flagged as straight
01675  * (from the parent surface) it is assumed it will stay that way.
01676  */
01677 template <class T> BOOL
01678 TestFlat( NurbSurface<T> * n, T tolerance )
01679 {
01680   int i;
01681   BOOL straight;
01682   Point_nD<T,3> cp00, cp0n, cpn0, cpnn, planeEqn;
01683   T dist,d ;
01684   
01685   /* Check edge straightness */
01686   
01687   if (! n->strU0)
01688     n->strU0 = IsCurveStraight( n, tolerance, 0, FALSE_ );
01689   if (! n->strUn)
01690     n->strUn = IsCurveStraight( n, tolerance, maxU(n), FALSE_ );
01691   if (! n->strV0)
01692     n->strV0 = IsCurveStraight( n, tolerance, 0, TRUE_ );
01693   if (! n->strVn)
01694     n->strVn = IsCurveStraight( n, tolerance, maxV(n), TRUE_ );
01695   
01696   /* Test to make sure control points are straight in U and V */
01697   
01698   straight = TRUE_;
01699   if ( (! n->flatU) && (n->strV0) && (n->strVn) )
01700     for (i = 1;
01701          (i < maxV(n)) && (straight = IsCurveStraight( n, tolerance, i, TRUE_ ));
01702          i++);
01703   
01704   if (straight && n->strV0 && n->strVn)
01705     n->flatU = TRUE_;
01706   
01707   straight = TRUE_;
01708   if ( (! n->flatV) && (n->strU0) && (n->strUn) )
01709     for (i = 1;
01710          (i < maxU(n)) && (straight = IsCurveStraight( n, tolerance, i, FALSE_ ));
01711          i++);
01712   
01713   if (straight && n->strU0 && n->strUn)
01714     n->flatV = TRUE_;
01715   
01716   if ( (! n->flatV) || (! n->flatU) )
01717     return( FALSE_ );
01718   
01719   // The surface can pass the above tests but still be twisted. 
01720   
01721   n->render->screenProject( (n->points(0,0)),       cp00 );
01722   n->render->screenProject( (n->points(0,maxU(n))),         cp0n );
01723   n->render->screenProject( (n->points(maxV(n),0)),         cpn0 );
01724   n->render->screenProject( (n->points(maxV(n),maxU(n))),  cpnn );
01725   
01726   cp0n -= cp00 ; // Make edges into vectors
01727   cpn0 -= cp00 ; 
01728   
01729   
01730   // Compute the plane equation from two adjacent sides, and
01731   // measure the distance from the far point to the plane.  If it's
01732   // larger than tolerance, the surface is twisted.
01733   
01734   planeEqn = crossProduct(cpn0,cp0n) ; 
01735   planeEqn = planeEqn.unitLength() ; // Normalize to keep adds in sync w/ mults 
01736   
01737   d = planeEqn * cp00 ; 
01738   dist = fabs( ( planeEqn * cpnn ) - d );
01739   
01740   if ( dist > tolerance ) // Surface is twisted 
01741     return( FALSE_ );
01742   else
01743     return( TRUE_ );
01744 }
01745 
01746 /*
01747  * Turn a sufficiently flat surface into triangles.
01748  */
01749 template <class T> 
01750 void EmitTriangles( NurbSurface<T> * n )
01751 {
01752   Point_nD<T,3> vecnn, vec0n;   // Diagonal vectors 
01753   T len2nn, len20n;             // Diagonal lengths squared 
01754   T u0, un, v0, vn;             // Texture coords;
01755                                    
01756   //
01757   // Measure the distance aint the two diagonals to decide the best
01758   // way to cut the rectangle into triangles.
01759   //
01760   
01761   vecnn = n->c00.point - n->cnn.point ; 
01762   vec0n = n->c0n.point - n->cn0.point ; 
01763   
01764   len2nn = norm2(vecnn) ; 
01765   len20n = norm2(vec0n) ; 
01766   
01767   if (maximum(len2nn, len20n) < n->epsilon)
01768     return;                             // Triangles are too small to render 
01769   
01770   //
01771   // Assign the texture coordinates
01772   //
01773   u0 = n->kvU[n->orderU-1];
01774   un = n->kvU[n->numU];
01775   v0 = n->kvV[n->orderV-1];
01776   vn = n->kvV[n->numV];
01777   n->c00.u = u0; n->c00.v = v0;
01778   n->c0n.u = un; n->c0n.v = v0;
01779   n->cn0.u = u0; n->cn0.v = vn;
01780   n->cnn.u = un; n->cnn.v = vn;
01781   
01782   //
01783   // If any normals are sick, fix them now.
01784   //
01785   if ((n->c00.normLen == 0.0) || (n->cnn.normLen == 0.0) || (n->cn0.normLen == 0.0))
01786     FixNormals( &(n->c00), &(n->cnn), &(n->cn0) );
01787   if (n->c0n.normLen == 0.0)
01788     FixNormals( &(n->c00), &(n->c0n), &(n->cnn) );
01789   
01790   if ( len2nn < len20n )
01791     {
01792       n->render->drawTriangle( n->c00, n->cnn, n->cn0 );
01793       n->render->drawTriangle( n->c00, n->c0n, n->cnn );
01794     }                                            
01795   else                                   
01796     {                                            
01797       n->render->drawTriangle( n->c0n, n->cnn, n->cn0 );
01798       n->render->drawTriangle( n->c0n, n->cn0, n->c00 );
01799     }
01800 }
01801 
01802 /*
01803  * The recursive subdivision algorithm.  Test if the surface is flat.
01804  * If so, split it into triangles.  Otherwise, split it into two halves,
01805  * and invoke the procedure on each half.
01806  */
01807 template <class T> void
01808 DoSubdivision( NurbSurface<T> * n, T tolerance, BOOL dirflag, int level )
01809 {
01810   NurbSurface<T> *left, *right;
01811   
01812   left = new NurbSurface<T>;
01813   right = new NurbSurface<T>;
01814 
01815   if (TestFlat( n, tolerance ))
01816     {
01817       EmitTriangles( n );
01818     }
01819   else
01820     {
01821       if ( ((! n->flatV) && (! n->flatU)) || ((n->flatV) && (n->flatU)) )
01822         dirflag = !dirflag;    // If twisted or curved in both directions, 
01823       else                     // then alternate subdivision direction 
01824         {
01825           if (n->flatU)        // Only split in directions that aren't flat 
01826             dirflag = FALSE_;
01827           else
01828             dirflag = TRUE_;
01829         }
01830       SplitSurface( n, left, right, dirflag );
01831       DoSubdivision( left, tolerance, dirflag, level + 1 );
01832       DoSubdivision( right, tolerance, dirflag, level + 1 );
01833       FreeNurb( left );
01834       FreeNurb( right );
01835     }
01836 }
01837 
01838 /*
01839  * Main entry point for subdivision */
01840 template <class T> void
01841 DrawSubdivision( NurbSurface<T> * surf, T tolerance )
01842 {
01843   surf->flatV = FALSE_;
01844   surf->flatU = FALSE_;
01845   surf->strU0 = FALSE_;
01846   surf->strUn = FALSE_;
01847   surf->strV0 = FALSE_;
01848   surf->strVn = FALSE_;
01849   
01850   //
01851   // Initialize the projected corners of the surface
01852   // and the normals.
01853   //
01854   DIVW( &(surf->points(0,0)), &surf->c00.point );
01855   DIVW( &(surf->points(0,surf->numU-1)), &surf->c0n.point );
01856   DIVW( &(surf->points(surf->numV-1,0)), &surf->cn0.point );
01857   DIVW( &(surf->points(surf->numV-1,surf->numU-1)), &surf->cnn.point );
01858 
01859   GetNormal( surf, 0, 0 );
01860   GetNormal( surf, 0, maxU(surf) );
01861   GetNormal( surf, maxV(surf), 0 );
01862   GetNormal( surf, maxV(surf), maxU(surf) );
01863   
01864   RenderMesh<T> *render ;
01865 
01866   render = surf->render ;
01867   render->drawHeader();
01868   DoSubdivision( surf, tolerance, TRUE_, 0 );
01869   // Note surf is deallocated by the subdivision process 
01870   render->drawFooter();
01871 }
01872 
01881 template <class T>
01882 SurfSample<T>& SurfSample<T>::operator=(const SurfSample<T>& s) {
01883   point = s.point ;
01884   normal = s.normal ;
01885   normLen = s.normLen ;
01886   u = s.u ;
01887   v = s.v ;
01888   return *this ;
01889 }
01890 
01891 /*
01892 template <class T>
01893 class NurbsCurveTess : public NurbsCurve<T,2> {
01894 public:
01895   NurbsCurveTess(const NurbsSurface<T,3>& rs) ;
01896 
01897   void tesselate(T tol) ; 
01898 
01899 protected:
01900   int isStraight() ; 
01901 
01902   const NurbsSurface<T,3>& rsurf ; 
01903   T tolerance ; 
01904 }
01905 
01906 template <class T>
01907 NurbsCurveTess::NurbsCurveTess(const NurbsSurface<T,3>& rs) : rsurf(rs) {
01908   tolerance = 0.01 ; 
01909 }
01910 
01911 template <class T>
01912 void NurbsCurveTess::tesselate(T tol){
01913   tolerance = tol ; 
01914   if(knot.n() != 2*(deg_+1)){
01915     
01916   }
01917 }
01918 */
01919 
01920 }
01921 
01922 #endif // #define PLIB_NURBS_NURBSSUB_SOURCE

Generated on Tue Jun 24 13:26:58 2003 for NURBS++ by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002