CMS 3D CMS Logo

IgSoSplineTrack.cc

Go to the documentation of this file.
00001 //<<<<<< INCLUDES                                                       >>>>>>
00002 
00003 #include "Iguana/Inventor/interface/IgSoSplineTrack.h"
00004 #include <Inventor/nodes/SoCoordinate4.h>
00005 #include <Inventor/nodes/SoNurbsCurve.h>
00006 #include <cmath>
00007 #include <iostream>
00008 
00009 //<<<<<< PRIVATE DEFINES                                                >>>>>>
00010 //<<<<<< PRIVATE CONSTANTS                                              >>>>>>
00011 namespace {
00012 #ifdef TGS_VERSION
00013     static const float maxNurbAngle =  M_PI / 2.; // was M_PI/2
00014 #else
00015     static const float maxNurbAngle = M_PI / 6.;
00016 #endif
00017 }
00018 //<<<<<< PRIVATE TYPES                                                  >>>>>>
00019 namespace { //blank namespace is eqivalent to file scope static
00020     // rootFunction evaluates the formula for the equation defining
00021     // the opening angle of a helical curve in terms of the invariants.
00022     // The argument x represents
00023     // the possible values of the angle.
00024 
00025     class RootFunction : public std::unary_function<float,float>
00026     {
00027     public:
00028         RootFunction(float paramA, float paramC, float paramF) :
00029           a(paramA), c(paramC), f(paramF)
00030           {};
00031           float operator()(const float x) const
00032           {
00033               double denom = -2* (1-cos (x))* (1-a) +x*x* (cos (x)-a); 
00034               double numerator = c * (cos (x)-a);
00035               double radical = numerator / denom; //might as well return INF if denom=0
00036               if (radical > 0) {
00037                   radical = sqrt (radical);}  //return Re part
00038               else {
00039                   radical = 0; //-sqrt (-radical); //return -Im part
00040               }
00041 
00042               return static_cast<float> (f + radical * (1-a)* (-2+2*cos (x)+x*sin (x))/ (cos (x)-1));
00043           }
00044     private:
00045         const double a, c, f;
00046     };
00047 }
00048 
00049 //<<<<<< PRIVATE VARIABLE DEFINITIONS                                   >>>>>>
00050 //<<<<<< PUBLIC VARIABLE DEFINITIONS                                    >>>>>>
00051 //<<<<<< CLASS STRUCTURE INITIALIZATION                                 >>>>>>
00052 
00053 const float M_SQRT3_2 =  0.8660254037844F; // sqrt (3)/2 for sin (60 degrees)
00054 const int IgSoSplineTrack::NORDER = 3;
00055 
00056 SO_KIT_SOURCE (IgSoSplineTrack);
00057 
00058 //<<<<<< PRIVATE FUNCTION DEFINITIONS                                   >>>>>>
00059 //<<<<<< PUBLIC FUNCTION DEFINITIONS                                    >>>>>>
00060 //<<<<<< MEMBER FUNCTION DEFINITIONS                                    >>>>>>
00061 
00062 void
00063 IgSoSplineTrack::initClass (void)
00064 { SO_KIT_INIT_CLASS (IgSoSplineTrack, IgSoShapeKit, "IgSoShapeKit"); }
00065 
00066 IgSoSplineTrack::IgSoSplineTrack (void)
00067 {
00068     SO_KIT_CONSTRUCTOR (IgSoSplineTrack);
00069 
00070     SO_KIT_ADD_FIELD (points, (SbVec3f (0, 0, 0)));
00071     SO_KIT_ADD_FIELD (tangents, (SbVec3f (0, 0, 0)));
00072 
00073     SO_KIT_ADD_CATALOG_ENTRY (coordinates, SoCoordinate4, FALSE, separator, \x0, TRUE);
00074     SO_KIT_ADD_CATALOG_ENTRY (curve, SoNurbsCurve, FALSE, separator,\x0, TRUE);
00075 
00076     SO_KIT_INIT_INSTANCE ();
00077 
00078     //      We make the default shape here rather than with the SO_NODE_ADD_FIELD calls
00079 
00080     //  straight line
00081     SbVec3f defPoints[2] = {SbVec3f (0.F,0.F,0.F),
00082         SbVec3f (0.F,1.F,0.F)};
00083     SbVec3f defTangents[2] = {SbVec3f (1.F,0.F,0.F),
00084         SbVec3f (1.F,0.F,0.F)};
00085     points.setValues (0, 2, defPoints);
00086     tangents.setValues (0, 2, defTangents);
00087 
00088     setUpConnections (true, true);
00089 }
00090 
00091 void
00092 IgSoSplineTrack::refresh (void)
00093 {
00094     int npts = points.getNum ();  
00095     if (npts <= 1) 
00096     {     
00097         setPart ("coordinates", NULL);
00098         setPart ("curve", NULL);
00099         return;
00100     }
00101 
00102     SoCoordinate4 *coords = new SoCoordinate4;
00103     SoNurbsCurve  *ncurve = new SoNurbsCurve;
00104 
00105     // Start by assuming that we won't have to insert any extra points
00106     std::vector <SbVec4f> controlPoints (2 * npts - 1);
00107     std::vector <float> knotVals (2 * npts - 1 + NORDER);
00108 
00109     int ik = 0; //pointer for knots
00110     int ipi = 0; // in pointer for control points 
00111     int ik_count = 0; //current value for knot
00112     int ipo = 0;  // out pointer for control points
00113     // the end points require an extra knot
00114     knotVals[ik++] = static_cast<float>(ik_count);
00115     // Now do the loop over points, converting to control points
00116     while (ipi < (npts - 1)) 
00117     {
00118         knotVals[ik++] = static_cast<float>(ik_count);
00119         controlPoints[ipo++] = SbVec4f ( points[ipi][0], points[ipi][1], points[ipi][2], 1.F);
00120         //      Calculate and insert the mid control point(s)
00121         midpoint (points[ipi], points[ipi+1], tangents[ipi], tangents[ipi+1], ik, ik_count, ipo, controlPoints, knotVals);
00122         ipi++;
00123     }
00124     // Put in the last point
00125     controlPoints[ipo++] = SbVec4f ( points[ipi][0], points[ipi][1], points[ipi][2],1.F);
00126     knotVals[ik++] = static_cast<float>(ik_count);
00127     knotVals[ik++] = static_cast<float>(ik_count);
00128     knotVals[ik++] = static_cast<float>(ik_count);
00129 
00130     // Now stuff it all in the NURB
00131     coords->point.setValues (0,ipo,&(controlPoints[0]));
00132     ncurve->knotVector.setValues (0,ik, &(knotVals[0]));
00133     ncurve->numControlPoints.setValue (ipo);
00134 
00135     setPart ("coordinates", coords);
00136     setPart ("curve", ncurve);
00137 }
00138 
00139 void
00140 IgSoSplineTrack::midpoint (const SbVec3f pa, const SbVec3f pb, const SbVec3f ta, const SbVec3f tb,
00141                            int &ik, int &ik_count, int &ipo, std::vector<SbVec4f> &controlPoints, std::vector<float> &knotVals)
00142 {
00143     // generate the control points between two given points: pa and pb, and 
00144     // two tangents: ta and tb.
00145     // return the zero vector if there's a problem
00146 
00147     const float eps = 0.000001F;
00148 
00149     //  Will solve for an approximate helix using the available invariants.
00150     //      Parametrize result as h[phi] = rho (cos[phi], sin[phi], zeta*phi)
00151     float paramA = tb.dot (ta);
00152     float paramC = (pb-pa).dot (pb-pa);
00153     float paramD = tb.dot (ta.cross (pb-pa));
00154 
00155     // these values will be used if we bail out...
00156     SbVec3f midpt3s ((pa+pb)/2.F);
00157     SbVec4f midpt4s (midpt3s[0],midpt3s[1],midpt3s[2],1.F);
00158     //      Tests for pathological cases
00159     //      Straight Line   
00160     if (std::abs (paramC)<eps || (std::abs (paramA-1) < eps && std::abs (paramD)/paramC < eps)) {
00161         controlPoints[ipo++] = midpt4s;
00162         knotVals[ik++] = static_cast<float>(ik_count++);
00163         return;
00164     }
00165 
00166     //      Linearly dependent but not straight
00167     SbVec3f perp = ta.cross (tb);
00168     if (std::abs (perp.dot (perp)) < eps)
00169     {
00170         controlPoints[ipo++] = midpt4s;
00171         knotVals[ik++] = static_cast<float>(ik_count++);
00172         return;
00173     }
00174 
00175     float zeta = 0.F;
00176     float delta = 0.F;
00177     float rho = 0.F;
00178     //      indeterminate solution in the root finder: add a tiny bit to paramD, enough to
00179     //      kick it out of danger but not enough to perturb the solution much.
00180     if (std::abs (paramD)<eps) 
00181     {
00182         // either delta = 0 or zeta = 0 (or both)
00183         // since delta can't be zero for a good measurement,
00184         zeta = 0.F;
00185         delta = std::acos(paramA);
00186         rho = std::sqrt(paramC/2.F/(1.F-paramA));
00187         paramD = -1.5F*eps;
00188     }
00189     else
00190     {
00191         //  try for a solution
00192         bool err = true;
00193         int paramSign = 1;
00194 
00195         delta = findRoot (RootFunction(paramA, paramC, -paramD), 0.01F, static_cast< float > (M_PI), eps, err);
00196         if (err) {
00197             paramSign = -1;
00198             delta = findRoot (RootFunction(paramA, paramC, paramD), 0.01F, static_cast< float > (M_PI), eps, err);
00199             if (err) 
00200             {
00201 #if 0 //DEBUG
00202                 std::cout << "Problem in SoSplineTrack. Can't find root. " << std::endl;
00203                 float x,y,z;
00204                 pa.getValue (x,y,z);
00205                 std::cout << "pa: " << x << ", " << y << ", " << z << std::endl;
00206                 pb.getValue (x,y,z);
00207                 std::cout << "pb: " << x << ", " << y << ", " << z << std::endl;
00208                 ta.getValue (x,y,z);
00209                 std::cout << "ta: " << x << ", " << y << ", " << z << std::endl;
00210                 tb.getValue (x,y,z);
00211                 std::cout << "tb: " << x << ", " << y << ", " << z << std::endl;
00212 #endif //DEBUG                  
00213                 controlPoints[ipo++] = midpt4s;
00214                 return;
00215             }
00216         }
00217         zeta = paramSign * sqrt (std::abs ((paramA-cos (delta))/ (1.0F-paramA)));
00218         rho = sqrt (paramC/ (2+delta*delta*zeta*zeta-2*cos (delta)));
00219     }
00220     // Now calculate ta, tb, pa, pb for parametrized curve
00221     SbVec3f taMP = SbVec3f ( 0.f, rho, rho*zeta);
00222     taMP.normalize();
00223     SbVec3f paMP = SbVec3f ( rho, 0.0f, 0.f );
00224     SbVec3f tbMP = SbVec3f( -rho * sin(delta), rho * cos(delta), rho * zeta );
00225     tbMP.normalize();
00226     SbVec3f tc = ta.cross(tb);
00227     tc.normalize();
00228     SbVec3f tcMP = taMP.cross(tbMP);
00229     tcMP.normalize();
00230 
00231     // rotation to go from taMP, tbMP frame to ta, tb
00232 
00233     SbMatrix mMP(taMP[0], tbMP[0], tcMP[0], 0.f,
00234         taMP[1], tbMP[1], tcMP[1], 0.f,
00235         taMP[2], tbMP[2], tcMP[2], 0.f,
00236         0.f,  0.f,  0.f,  1.f);
00237 
00238     SbMatrix m(ta[0], tb[0], tc[0], 0.f,
00239         ta[1], tb[1], tc[1], 0.f,
00240         ta[2], tb[2], tc[2], 0.f,
00241         0.f,   0.f,   0.f,   1.f);
00242 
00243     SbMatrix mm = m.multRight(mMP.inverse()).transpose(); // OIV stores row-major but GL uses column-major
00244     // so use as transpose in reverse order
00245 
00246     // correct order to transform pts in MP back to lab is:
00247     // subtract paMP; rotate to align with ta, tb; add pa
00248     SbMatrix xform;
00249     xform.setTranslate(-paMP);
00250     xform = xform.multRight(mm);
00251     SbMatrix transBack;
00252     transBack.setTranslate(pa);
00253     xform = xform.multRight(transBack);
00254 
00255     // if we exceed the maximum extent well-rendered by the base nurb library, add more divisions
00256     const int ndiv = 1 + static_cast<int>(std::floor(std::abs(delta)/maxNurbAngle));
00257 
00258     float deltan = delta/ndiv;
00259     float wt = std::abs (cos (deltan/2) );
00260 #if 0 //DEBUG
00261     if (wt < eps) 
00262     {
00263         std::cout << "Problem in SoSplineTrack. Wt = " << wt << std::endl;
00264         std::cout << "    params: " << paramA <<","<< paramC << "," << paramD << std::endl;
00265     }
00266 #endif //DEBUG  
00267 
00268     // adjust size of array to accomodate additional inserted points
00269     if (ndiv > 1)
00270     {
00271         controlPoints.resize( controlPoints.size()+2*(ndiv-1) );
00272         knotVals.resize( knotVals.size()+2*(ndiv-1) );
00273     }
00274     //  Calculate and insert points
00275     float angle=0.F;
00276     for ( int i=0; i < ndiv; i++, angle += deltan )
00277     {
00278         if (i!=0) 
00279         {
00280             SbVec3f endpt ( rho * std::cos(angle), rho * std::sin(angle), rho * zeta * (angle) );
00281             xform.multVecMatrix ( endpt, endpt );
00282             SbVec4f endpt4 ( endpt[0], endpt[1], endpt[2], 1.F );
00283             knotVals[ik++] = static_cast<float>(ik_count);
00284             controlPoints[ipo++] = endpt4;
00285         }
00286         SbVec3f midpt3_0 ( rho * std::cos (angle+deltan/2.0F), rho * std::sin (angle+deltan/2.0F), rho * zeta*(angle+deltan/2.0F));
00287         SbVec3f midpt3 (midpt3_0[0]/wt, midpt3_0[1]/wt, midpt3_0[2]);
00288 
00289         xform.multVecMatrix (midpt3,midpt3);
00290         SbVec4f midpt (midpt3[0],midpt3[1],midpt3[2],1.F); //convert from Vec3 to Vec4
00291         midpt *= wt; //and set the correct overall weight
00292         controlPoints[ipo++] = midpt;
00293         knotVals[ik++] = static_cast<float>(ik_count++);
00294     }
00295 
00296 }
00297 
00298 float
00299 IgSoSplineTrack::findRoot (const RootFunction func, float x1, float x2, float xacc, bool &err)
00300 {
00301     // Find a root using bisection (see Numerical Recipies for complete documentation)
00302     err = false;
00303     const int JMAX = 40;
00304 
00305     func(x1);
00306     float f= func(x1);
00307     float fmid= func(x2);
00308     //      Might want to insert a check here to see if either of the endpoints or the midpoint
00309     //      are within xacc of 0...
00310     if (f*fmid >=0.0) {
00311         err = true;
00312 #if 0 //DEBUG
00313         std::cout << "SoSplineTrack f*fmid >= 0" << std::endl;
00314         std::cout << "  f="<< f << "  fmid=" << fmid << std::endl;
00315 #endif //DEBUG
00316         return 0.0;}
00317     else {
00318         float dx, xmid;
00319         float rtb = f < 0.0 ? (dx=x2-x1,x1) : (dx=x1-x2,x2);
00320         for (int j=1; j<JMAX; j++) {
00321             fmid= func(xmid=rtb + (dx *= 0.5));
00322             if (fmid <= 0.0) rtb=xmid;
00323             if (std::abs (dx) < xacc || fmid == 0.0) return rtb;
00324         }
00325         err = true;
00326 #if 0 //DEBUG
00327         std::cout << "SoSplineTrack: findRoot failed to converge" << std::endl;
00328         std::cout << "  f="<< f << "  fmid=" << fmid << "  rtb=" << rtb << std::endl;
00329 #endif //DEBUG
00330         return 0.0;
00331     }
00332 }

Generated on Tue Jun 9 17:38:47 2009 for CMSSW by  doxygen 1.5.4