MOAB: Mesh Oriented datABase  (version 5.2.1)
SmoothCurve.cpp
Go to the documentation of this file.
00001 /*
00002  * SmoothCurve.cpp
00003  *
00004  */
00005 
00006 #include "SmoothCurve.hpp"
00007 //#include "SmoothVertex.hpp"
00008 #include "SmoothFace.hpp"
00009 #include "assert.h"
00010 #include <iostream>
00011 #include "moab/GeomTopoTool.hpp"
00012 
00013 namespace moab
00014 {
00015 
00016 SmoothCurve::SmoothCurve( Interface* mb, EntityHandle curve, GeomTopoTool* gTool )
00017     : _mb( mb ), _set( curve ), _gtt( gTool )
00018 {
00019     //_mbOut->create_meshset(MESHSET_ORDERED, _oSet);
00020     /*_cmlEdgeMesher = new CMLEdgeMesher (this, CML::STANDARD);
00021      _cmlEdgeMesher->set_sizing_function(CML::LINEAR_SIZING);*/
00022     _leng    = 0;  // not initialized
00023     _edgeTag = 0;  // not initialized
00024 }
00025 SmoothCurve::~SmoothCurve()
00026 {
00027     // TODO Auto-generated destructor stub
00028 }
00029 
00030 double SmoothCurve::arc_length()
00031 {
00032 
00033     return _leng;
00034 }
00035 
00036 //! \brief Get the parametric status of the curve.
00037 //!
00038 //! \return \a true if curve is parametric, \a false otherwise.
00039 bool SmoothCurve::is_parametric()
00040 {
00041     return true;
00042 }
00043 
00044 //! \brief Get the periodic status of the curve.
00045 //!
00046 //! \param period The period of the curve if periodic.
00047 //!
00048 //! \return \a true if curve is periodic, \a false otherwise.
00049 bool SmoothCurve::is_periodic( double& period )
00050 {
00051     // assert(_ref_edge);
00052     // return _ref_edge->is_periodic(   period);
00053     Range vsets;
00054     _mb->get_child_meshsets( _set, vsets );  // num_hops =1
00055     if( vsets.size() == 1 )
00056     {
00057         period = _leng;
00058         return true;  //  true , especially for ice sheet data
00059     }
00060     return false;
00061 }
00062 
00063 //! \brief Get the parameter range of the curve.
00064 //!
00065 //! \param u_start The beginning curve parameter
00066 //! \param u_end The ending curve parameter
00067 //!
00068 //! \note The numerical value of \a u_start may be greater
00069 //! than the numerical value of \a u_end.
00070 void SmoothCurve::get_param_range( double& u_start, double& u_end )
00071 {
00072     // assert(_ref_edge);
00073     u_start = 0;
00074     u_end   = 1.;
00075 
00076     return;
00077 }
00078 
00079 //! Compute the parameter value at a specified distance along the curve.
00080 //!
00081 //! \param u_root The start parameter from which to compute the distance
00082 //! along the curve.
00083 //! \param arc_length The distance to move along the curve.
00084 //!
00085 //! \note For positive values of \a arc_length the distance will be
00086 //! computed in the direction of increasing parameter value along the
00087 //! curve.  For negative values of \a arc_length the distance will be
00088 //! computed in the direction of decreasing parameter value along the
00089 //! curve.
00090 //!
00091 //! \return The parametric coordinate u along the curve
00092 double SmoothCurve::u_from_arc_length( double u_root, double arc_leng )
00093 {
00094 
00095     if( _leng <= 0 ) return 0;
00096     return u_root + arc_leng / _leng;
00097 }
00098 
00099 //! \brief Evaluate the curve at a specified parameter value.
00100 //!
00101 //! \param u The parameter at which to evaluate the curve
00102 //! \param x The x coordinate of the evaluated point
00103 //! \param y The y coordinate of the evaluated point
00104 //! \param z The z coordinate of the evaluated point
00105 bool SmoothCurve::position_from_u( double u, double& x, double& y, double& z, double* tg )
00106 {
00107 
00108     // _fractions are increasing, so find the
00109     double* ptr         = std::lower_bound( &_fractions[0], ( &_fractions[0] ) + _fractions.size(), u );
00110     int index           = ptr - &_fractions[0];
00111     double nextFraction = _fractions[index];
00112     double prevFraction = 0;
00113     if( index > 0 ) { prevFraction = _fractions[index - 1]; }
00114     double t = ( u - prevFraction ) / ( nextFraction - prevFraction );
00115 
00116     EntityHandle edge = _entities[index];
00117 
00118     CartVect position, tangent;
00119     ErrorCode rval = evaluate_smooth_edge( edge, t, position, tangent );
00120     if( MB_SUCCESS != rval ) return false;
00121     assert( rval == MB_SUCCESS );
00122     x = position[0];
00123     y = position[1];
00124     z = position[2];
00125     if( tg )
00126     {
00127         // we need to do some scaling,
00128         double dtdu = 1 / ( nextFraction - prevFraction );
00129         tg[0]       = tangent[0] * dtdu;
00130         tg[1]       = tangent[1] * dtdu;
00131         tg[2]       = tangent[2] * dtdu;
00132     }
00133 
00134     return true;
00135 }
00136 //! \brief Move a point near the curve to the closest point on the curve.
00137 //!
00138 //! \param x The x coordinate of the point
00139 //! \param y The y coordinate of the point
00140 //! \param z The z coordinate of the point
00141 void SmoothCurve::move_to_curve( double& x, double& y, double& z )
00142 {
00143 
00144     // find closest point to the curve, and the parametric position
00145     // must be close by, but how close ???
00146     EntityHandle v;
00147     int edgeIndex;
00148     double u = u_from_position( x, y, z, v, edgeIndex );
00149     position_from_u( u, x, y, z );
00150 
00151     return;
00152 }
00153 
00154 //! Get the u parameter value on the curve closest to x,y,z
00155 //! and the point on the curve.
00156 //!
00157 //! \param x The x coordinate of the point
00158 //! \param y The y coordinate of the point
00159 //! \param z The z coordinate of the point
00160 //!
00161 //! \return The parametric coordinate u on the curve
00162 double SmoothCurve::u_from_position( double x, double y, double z, EntityHandle& v, int& edgeIndex )
00163 {
00164     // this is an iterative process, expensive usually
00165     // get first all nodes , and their positions
00166     // find the closest node (and edge), and from there do some
00167     // iterations up to a point
00168     // do not exaggerate with convergence criteria
00169 
00170     v = 0;  // we do not have a close by vertex yet
00171     CartVect initialPos( x, y, z );
00172     double u    = 0;
00173     int nbNodes = (int)_entities.size() * 2;  // the mesh edges are stored
00174     std::vector< EntityHandle > nodesConnec;
00175     nodesConnec.resize( nbNodes );
00176     ErrorCode rval = this->_mb->get_connectivity( &( _entities[0] ), nbNodes / 2, nodesConnec );
00177     if( MB_SUCCESS != rval )
00178     {
00179         std::cout << "error in getting connectivity\n";
00180         return 0;
00181     }
00182     // collapse nodesConnec, nodes should be in order
00183     for( int k = 0; k < nbNodes / 2; k++ )
00184     {
00185         nodesConnec[k + 1] = nodesConnec[2 * k + 1];
00186     }
00187     int numNodes = nbNodes / 2 + 1;
00188     std::vector< CartVect > coordNodes;
00189     coordNodes.resize( numNodes );
00190 
00191     rval = _mb->get_coords( &( nodesConnec[0] ), numNodes, (double*)&( coordNodes[0] ) );
00192     if( MB_SUCCESS != rval )
00193     {
00194         std::cout << "error in getting node positions\n";
00195         return 0;
00196     }
00197     // find the closest node, then find the closest edge, based on closest node
00198 
00199     int indexNode  = 0;
00200     double minDist = 1.e30;
00201     // expensive linear search
00202     for( int i = 0; i < numNodes; i++ )
00203     {
00204         double d1 = ( initialPos - coordNodes[i] ).length();
00205         if( d1 < minDist )
00206         {
00207             indexNode = i;
00208             minDist   = d1;
00209         }
00210     }
00211     double tolerance = 0.00001;  // what is the unit?
00212     // something reasonable
00213     if( minDist < tolerance )
00214     {
00215         v = nodesConnec[indexNode];
00216         // we are done, just return the proper u (from fractions)
00217         if( indexNode == 0 )
00218         {
00219             return 0;  // first node has u = 0
00220         }
00221         else
00222             return _fractions[indexNode - 1];  // fractions[0] > 0!!)
00223     }
00224     // find the mesh edge; could be previous or next edge
00225     edgeIndex = indexNode;  // could be the previous one, though!!
00226     if( edgeIndex == numNodes - 1 )
00227         edgeIndex--;  // we have one less edge, and do not worry about next edge
00228     else
00229     {
00230         if( edgeIndex > 0 )
00231         {
00232             // could be the previous; decide based on distance to the other
00233             // nodes of the 2 connected edges
00234             CartVect prevNodePos = coordNodes[edgeIndex - 1];
00235             CartVect nextNodePos = coordNodes[edgeIndex + 1];
00236             if( ( prevNodePos - initialPos ).length_squared() < ( nextNodePos - initialPos ).length_squared() )
00237             { edgeIndex--; }
00238         }
00239     }
00240     // now, we know for sure that the closest point is somewhere on edgeIndex edge
00241     //
00242 
00243     // do newton iteration for local t between 0 and 1
00244 
00245     // copy from evaluation method
00246     CartVect P[2];              // P0 and P1
00247     CartVect controlPoints[3];  // edge control points
00248     double t4, t3, t2, one_minus_t, one_minus_t2, one_minus_t3, one_minus_t4;
00249 
00250     P[0] = coordNodes[edgeIndex];
00251     P[1] = coordNodes[edgeIndex + 1];
00252 
00253     if( 0 == _edgeTag )
00254     {
00255         rval = _mb->tag_get_handle( "CONTROLEDGE", 9, MB_TYPE_DOUBLE, _edgeTag );
00256         if( rval != MB_SUCCESS ) return 0;
00257     }
00258     rval = _mb->tag_get_data( _edgeTag, &( _entities[edgeIndex] ), 1, (double*)&controlPoints[0] );
00259     if( rval != MB_SUCCESS ) return rval;
00260 
00261     // starting point
00262     double tt      = 0.5;  // between the 2 ends of the edge
00263     int iterations = 0;
00264     // find iteratively a better point
00265     int maxIterations = 10;  // not too many
00266     CartVect outv;
00267     // we will solve minimize F = 0.5 * ( ini - r(t) )^2
00268     // so solve  F'(t) = 0
00269     // Iteration:  t_ -> t - F'(t)/F"(t)
00270     // F'(t) = r'(t) (ini-r(t) )
00271     // F"(t) = r"(t) (ini-r(t) ) - (r'(t))^2
00272     while( iterations < maxIterations )
00273     //
00274     {
00275         t2           = tt * tt;
00276         t3           = t2 * tt;
00277         t4           = t3 * tt;
00278         one_minus_t  = 1. - tt;
00279         one_minus_t2 = one_minus_t * one_minus_t;
00280         one_minus_t3 = one_minus_t2 * one_minus_t;
00281         one_minus_t4 = one_minus_t3 * one_minus_t;
00282 
00283         outv = one_minus_t4 * P[0] + 4. * one_minus_t3 * tt * controlPoints[0] +
00284                6. * one_minus_t2 * t2 * controlPoints[1] + 4. * one_minus_t * t3 * controlPoints[2] + t4 * P[1];
00285 
00286         CartVect out_tangent = -4. * one_minus_t3 * P[0] +
00287                                4. * ( one_minus_t3 - 3. * tt * one_minus_t2 ) * controlPoints[0] +
00288                                12. * ( tt * one_minus_t2 - t2 * one_minus_t ) * controlPoints[1] +
00289                                4. * ( 3. * t2 * one_minus_t - t3 ) * controlPoints[2] + 4. * t3 * P[1];
00290 
00291         CartVect second_deriv =
00292             12. * one_minus_t2 * P[0] +
00293             4. * ( -3. * one_minus_t2 - 3. * one_minus_t2 + 6. * tt * one_minus_t ) * controlPoints[0] +
00294             12. * ( one_minus_t2 - 4 * tt * one_minus_t + t2 ) * controlPoints[1] +
00295             4. * ( 6. * tt - 12 * t2 ) * controlPoints[2] + 12. * t2 * P[1];
00296         CartVect diff = outv - initialPos;
00297         double F_d    = out_tangent % diff;
00298         double F_dd   = second_deriv % diff + out_tangent.length_squared();
00299 
00300         if( 0 == F_dd ) break;  // get out, we found minimum?
00301 
00302         double delta_t = -F_d / F_dd;
00303 
00304         if( fabs( delta_t ) < 0.000001 ) break;
00305         tt = tt + delta_t;
00306         if( tt < 0 )
00307         {
00308             tt = 0.;
00309             v  = nodesConnec[edgeIndex];  // we are at end of mesh edge
00310             break;
00311         }
00312         if( tt > 1 )
00313         {
00314             tt = 1;
00315             v  = nodesConnec[edgeIndex + 1];  // we are at one end
00316             break;
00317         }
00318         iterations++;
00319     }
00320     // so we have t on the segment, convert to u, which should
00321     // be between _fractions[edgeIndex] numbers
00322     double prevFraction = 0;
00323     if( edgeIndex > 0 ) prevFraction = _fractions[edgeIndex - 1];
00324 
00325     u = prevFraction + tt * ( _fractions[edgeIndex] - prevFraction );
00326     return u;
00327 }
00328 
00329 //! \brief Get the starting point of the curve.
00330 //!
00331 //! \param x The x coordinate of the start point
00332 //! \param y The y coordinate of the start point
00333 //! \param z The z coordinate of the start point
00334 void SmoothCurve::start_coordinates( double& x, double& y, double& z )
00335 {
00336 
00337     int nnodes                = 0;
00338     const EntityHandle* conn2 = NULL;
00339     _mb->get_connectivity( _entities[0], conn2, nnodes );
00340     double c[3];
00341     _mb->get_coords( conn2, 1, c );
00342 
00343     x = c[0];
00344     y = c[1];
00345     z = c[2];
00346 
00347     return;
00348 }
00349 
00350 //! \brief Get the ending point of the curve.
00351 //!
00352 //! \param x The x coordinate of the start point
00353 //! \param y The y coordinate of the start point
00354 //! \param z The z coordinate of the start point
00355 void SmoothCurve::end_coordinates( double& x, double& y, double& z )
00356 {
00357 
00358     int nnodes                = 0;
00359     const EntityHandle* conn2 = NULL;
00360     _mb->get_connectivity( _entities[_entities.size() - 1], conn2, nnodes );
00361     double c[3];
00362     // careful, the second node here
00363     _mb->get_coords( &conn2[1], 1, c );
00364 
00365     x = c[0];
00366     y = c[1];
00367     z = c[2];
00368     return;
00369 }
00370 
00371 // this will recompute the 2 tangents for each edge, considering the geo edge they are into
00372 void SmoothCurve::compute_tangents_for_each_edge()
00373 {
00374     // will retrieve the edges in each set; they are retrieved in order they were put into, because
00375     // these sets are "MESHSET_ORDERED"
00376     // retrieve the tag handle for the tangents; it should have been created already
00377     // this tangents are computed for the chain of edges that form a geometric edge
00378     // some checks should be performed on the vertices, but we trust the correctness of the model
00379     // completely (like the vertices should match in the chain...)
00380     Tag tangentsTag;
00381     ErrorCode rval = _mb->tag_get_handle( "TANGENTS", 6, MB_TYPE_DOUBLE, tangentsTag );
00382     if( rval != MB_SUCCESS ) return;  // some error should be thrown
00383     std::vector< EntityHandle > entities;
00384     _mb->get_entities_by_type( _set, MBEDGE, entities );  // no recursion!!
00385     // basically, each tangent at a node will depend on previous tangent
00386     int nbEdges = entities.size();
00387     // now, we can advance in the loop
00388     // the only special problem is if the first node coincides with the last node, then we should
00389     // consider the closed loop; or maybe we should look at angles in that case too?
00390     // also, should we look at the 2 semi-circles case? How to decide if we need to continue the
00391     // "tangents" maybe we can do that later, and we can alter the tangents at the feature nodes, in
00392     // the directions of the loops again, do we need to decide the "closed" loop or not? Not yet...
00393     EntityHandle previousEdge = entities[0];                            // this is the first edge in the chain
00394     CartVect TP[2];                                                     // tangents for the previous edge
00395     rval = _mb->tag_get_data( tangentsTag, &previousEdge, 1, &TP[0] );  // tangents for previous edge
00396     if( rval != MB_SUCCESS ) return;                                    // some error should be thrown
00397     CartVect TC[2];                                                     // tangents for the current edge
00398     EntityHandle currentEdge;
00399     for( int i = 1; i < nbEdges; i++ )
00400     {
00401         // current edge will start after first one
00402         currentEdge = entities[i];
00403         rval        = _mb->tag_get_data( tangentsTag, &currentEdge, 1, &TC[0] );  //
00404         if( rval != MB_SUCCESS ) return;                                          // some error should be thrown
00405         // now compute the new tangent at common vertex; reset tangents for previous edge and
00406         // current edge a little bit of CPU and memory waste, but this is life
00407         CartVect T = 0.5 * TC[0] + 0.5 * TP[1];  //
00408         T.normalize();
00409         TP[1] = T;
00410         rval  = _mb->tag_set_data( tangentsTag, &previousEdge, 1, &TP[0] );  //
00411         if( rval != MB_SUCCESS ) return;                                     // some error should be thrown
00412         TC[0] = T;
00413         rval  = _mb->tag_set_data( tangentsTag, &currentEdge, 1, &TC[0] );  //
00414         if( rval != MB_SUCCESS ) return;                                    // some error should be thrown
00415         // now set the next edge
00416         previousEdge = currentEdge;
00417         TP[0]        = TC[0];
00418         TP[1]        = TC[1];
00419     }
00420     return;
00421 }
00422 
00423 void SmoothCurve::compute_control_points_on_boundary_edges( double, std::map< EntityHandle, SmoothFace* >& mapSurfaces,
00424                                                             Tag controlPointsTag, Tag markTag )
00425 {
00426     // these points really need the surfaces they belong to, because the control points on edges
00427     // depend on the normals on surfaces
00428     // the control points are averaged from different surfaces, by simple mean average
00429     // the surfaces have
00430     // do we really need min_dot here?
00431     // first of all, find out the SmoothFace for each surface set that is adjacent here
00432     // GeomTopoTool gTopoTool(_mb);
00433     std::vector< EntityHandle > faces;
00434     std::vector< int > senses;
00435     ErrorCode rval = _gtt->get_senses( _set, faces, senses );
00436     if( MB_SUCCESS != rval ) return;
00437 
00438     // need to find the smooth face attached
00439     unsigned int numSurfacesAdjacent = faces.size();
00440     // get the edges, and then get the
00441     // std::vector<EntityHandle> entities;
00442     _mb->get_entities_by_type( _set, MBEDGE, _entities );  // no recursion!!
00443     // each edge has the tangent computed already
00444     Tag tangentsTag;
00445     rval = _mb->tag_get_handle( "TANGENTS", 6, MB_TYPE_DOUBLE, tangentsTag );
00446     if( rval != MB_SUCCESS ) return;  // some error should be thrown
00447 
00448     // we do not want to search every time
00449     std::vector< SmoothFace* > smoothFaceArray;
00450     unsigned int i = 0;
00451     for( i = 0; i < numSurfacesAdjacent; i++ )
00452     {
00453         SmoothFace* sms = mapSurfaces[faces[i]];
00454         smoothFaceArray.push_back( sms );
00455     }
00456 
00457     unsigned int e = 0;
00458     for( e = 0; e < _entities.size(); e++ )
00459     {
00460         CartVect zero( 0. );
00461         CartVect ctrlP[3] = { zero, zero, zero };  // null positions initially
00462         // the control points are averaged from connected faces
00463         EntityHandle edge = _entities[e];  // the edge in the chain
00464 
00465         int nnodes;
00466         const EntityHandle* conn2;
00467         rval = _mb->get_connectivity( edge, conn2, nnodes );
00468         if( rval != MB_SUCCESS || 2 != nnodes ) return;  // or continue or return error
00469 
00470         // double coords[6]; // store the coordinates for the nodes
00471         CartVect P[2];
00472         // ErrorCode rval = _mb->get_coords(conn2, 2, coords);
00473         rval = _mb->get_coords( conn2, 2, (double*)&P[0] );
00474         if( rval != MB_SUCCESS ) return;
00475 
00476         CartVect chord = P[1] - P[0];
00477         _leng += chord.length();
00478         _fractions.push_back( _leng );
00479         CartVect N[2];
00480 
00481         // MBCartVect N0(&normalVec[0]);
00482         // MBCartVect N3(&normalVec[3]);
00483         CartVect T[2];  // T0, T3
00484         // if (edge->num_adj_facets() <= 1) {
00485         // stat = compute_curve_tangent(edge, min_dot, T0, T3);
00486         //  if (stat != CUBIT_SUCCESS)
00487         //  return stat;
00488         //} else {
00489         //}
00490         rval = _mb->tag_get_data( tangentsTag, &edge, 1, &T[0] );
00491         if( rval != MB_SUCCESS ) return;
00492 
00493         for( i = 0; i < numSurfacesAdjacent; i++ )
00494         {
00495             CartVect controlForEdge[3];
00496             rval = smoothFaceArray[i]->get_normals_for_vertices( conn2, N );
00497             if( rval != MB_SUCCESS ) return;
00498 
00499             rval = smoothFaceArray[i]->init_edge_control_points( P[0], P[1], N[0], N[1], T[0], T[1], controlForEdge );
00500             if( rval != MB_SUCCESS ) return;
00501 
00502             // accumulate those over faces!!!
00503             for( int j = 0; j < 3; j++ )
00504             {
00505                 ctrlP[j] += controlForEdge[j];
00506             }
00507         }
00508         // now divide them for the average position!
00509         for( int j = 0; j < 3; j++ )
00510         {
00511             ctrlP[j] /= numSurfacesAdjacent;
00512         }
00513         // we are done, set the control points now!
00514         // edge->control_points(ctrl_pts, 4);
00515         rval = _mb->tag_set_data( controlPointsTag, &edge, 1, &ctrlP[0] );
00516         if( rval != MB_SUCCESS ) return;
00517 
00518         this->_edgeTag = controlPointsTag;  // this is a tag that will be stored with the edge
00519         // is that a waste of memory or not...
00520         // also mark the edge for later on
00521         unsigned char used = 1;
00522         _mb->tag_set_data( markTag, &edge, 1, &used );
00523     }
00524     // now divide fractions, to make them vary from 0 to 1
00525     assert( _leng > 0. );
00526     for( e = 0; e < _entities.size(); e++ )
00527         _fractions[e] /= _leng;
00528 }
00529 
00530 ErrorCode SmoothCurve::evaluate_smooth_edge( EntityHandle eh, double& tt, CartVect& outv, CartVect& out_tangent )
00531 {
00532     CartVect P[2];              // P0 and P1
00533     CartVect controlPoints[3];  // edge control points
00534     double t4, t3, t2, one_minus_t, one_minus_t2, one_minus_t3, one_minus_t4;
00535 
00536     // project the position to the linear edge
00537     // t is from 0 to 1 only!!
00538     // double tt = (t + 1) * 0.5;
00539     if( tt <= 0.0 ) tt = 0.0;
00540     if( tt >= 1.0 ) tt = 1.0;
00541 
00542     int nnodes                = 0;
00543     const EntityHandle* conn2 = NULL;
00544     ErrorCode rval            = _mb->get_connectivity( eh, conn2, nnodes );
00545     if( rval != MB_SUCCESS ) return rval;
00546 
00547     rval = _mb->get_coords( conn2, 2, (double*)&P[0] );
00548     if( rval != MB_SUCCESS ) return rval;
00549 
00550     if( 0 == _edgeTag )
00551     {
00552         rval = _mb->tag_get_handle( "CONTROLEDGE", 9, MB_TYPE_DOUBLE, _edgeTag );
00553         if( rval != MB_SUCCESS ) return rval;
00554     }
00555     rval = _mb->tag_get_data( _edgeTag, &eh, 1, (double*)&controlPoints[0] );
00556     if( rval != MB_SUCCESS ) return rval;
00557 
00558     t2           = tt * tt;
00559     t3           = t2 * tt;
00560     t4           = t3 * tt;
00561     one_minus_t  = 1. - tt;
00562     one_minus_t2 = one_minus_t * one_minus_t;
00563     one_minus_t3 = one_minus_t2 * one_minus_t;
00564     one_minus_t4 = one_minus_t3 * one_minus_t;
00565 
00566     outv = one_minus_t4 * P[0] + 4. * one_minus_t3 * tt * controlPoints[0] + 6. * one_minus_t2 * t2 * controlPoints[1] +
00567            4. * one_minus_t * t3 * controlPoints[2] + t4 * P[1];
00568 
00569     out_tangent = -4. * one_minus_t3 * P[0] + 4. * ( one_minus_t3 - 3. * tt * one_minus_t2 ) * controlPoints[0] +
00570                   12. * ( tt * one_minus_t2 - t2 * one_minus_t ) * controlPoints[1] +
00571                   4. * ( 3. * t2 * one_minus_t - t3 ) * controlPoints[2] + 4. * t3 * P[1];
00572     return MB_SUCCESS;
00573 }
00574 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines