MOAB: Mesh Oriented datABase  (version 5.3.0)
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 <cassert>
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             {
00238                 edgeIndex--;
00239             }
00240         }
00241     }
00242     // now, we know for sure that the closest point is somewhere on edgeIndex edge
00243     //
00244 
00245     // do newton iteration for local t between 0 and 1
00246 
00247     // copy from evaluation method
00248     CartVect P[2];              // P0 and P1
00249     CartVect controlPoints[3];  // edge control points
00250     double t4, t3, t2, one_minus_t, one_minus_t2, one_minus_t3, one_minus_t4;
00251 
00252     P[0] = coordNodes[edgeIndex];
00253     P[1] = coordNodes[edgeIndex + 1];
00254 
00255     if( 0 == _edgeTag )
00256     {
00257         rval = _mb->tag_get_handle( "CONTROLEDGE", 9, MB_TYPE_DOUBLE, _edgeTag );
00258         if( rval != MB_SUCCESS ) return 0;
00259     }
00260     rval = _mb->tag_get_data( _edgeTag, &( _entities[edgeIndex] ), 1, (double*)&controlPoints[0] );
00261     if( rval != MB_SUCCESS ) return rval;
00262 
00263     // starting point
00264     double tt      = 0.5;  // between the 2 ends of the edge
00265     int iterations = 0;
00266     // find iteratively a better point
00267     int maxIterations = 10;  // not too many
00268     CartVect outv;
00269     // we will solve minimize F = 0.5 * ( ini - r(t) )^2
00270     // so solve  F'(t) = 0
00271     // Iteration:  t_ -> t - F'(t)/F"(t)
00272     // F'(t) = r'(t) (ini-r(t) )
00273     // F"(t) = r"(t) (ini-r(t) ) - (r'(t))^2
00274     while( iterations < maxIterations )
00275     //
00276     {
00277         t2           = tt * tt;
00278         t3           = t2 * tt;
00279         t4           = t3 * tt;
00280         one_minus_t  = 1. - tt;
00281         one_minus_t2 = one_minus_t * one_minus_t;
00282         one_minus_t3 = one_minus_t2 * one_minus_t;
00283         one_minus_t4 = one_minus_t3 * one_minus_t;
00284 
00285         outv = one_minus_t4 * P[0] + 4. * one_minus_t3 * tt * controlPoints[0] +
00286                6. * one_minus_t2 * t2 * controlPoints[1] + 4. * one_minus_t * t3 * controlPoints[2] + t4 * P[1];
00287 
00288         CartVect out_tangent = -4. * one_minus_t3 * P[0] +
00289                                4. * ( one_minus_t3 - 3. * tt * one_minus_t2 ) * controlPoints[0] +
00290                                12. * ( tt * one_minus_t2 - t2 * one_minus_t ) * controlPoints[1] +
00291                                4. * ( 3. * t2 * one_minus_t - t3 ) * controlPoints[2] + 4. * t3 * P[1];
00292 
00293         CartVect second_deriv =
00294             12. * one_minus_t2 * P[0] +
00295             4. * ( -3. * one_minus_t2 - 3. * one_minus_t2 + 6. * tt * one_minus_t ) * controlPoints[0] +
00296             12. * ( one_minus_t2 - 4 * tt * one_minus_t + t2 ) * controlPoints[1] +
00297             4. * ( 6. * tt - 12 * t2 ) * controlPoints[2] + 12. * t2 * P[1];
00298         CartVect diff = outv - initialPos;
00299         double F_d    = out_tangent % diff;
00300         double F_dd   = second_deriv % diff + out_tangent.length_squared();
00301 
00302         if( 0 == F_dd ) break;  // get out, we found minimum?
00303 
00304         double delta_t = -F_d / F_dd;
00305 
00306         if( fabs( delta_t ) < 0.000001 ) break;
00307         tt = tt + delta_t;
00308         if( tt < 0 )
00309         {
00310             tt = 0.;
00311             v  = nodesConnec[edgeIndex];  // we are at end of mesh edge
00312             break;
00313         }
00314         if( tt > 1 )
00315         {
00316             tt = 1;
00317             v  = nodesConnec[edgeIndex + 1];  // we are at one end
00318             break;
00319         }
00320         iterations++;
00321     }
00322     // so we have t on the segment, convert to u, which should
00323     // be between _fractions[edgeIndex] numbers
00324     double prevFraction = 0;
00325     if( edgeIndex > 0 ) prevFraction = _fractions[edgeIndex - 1];
00326 
00327     u = prevFraction + tt * ( _fractions[edgeIndex] - prevFraction );
00328     return u;
00329 }
00330 
00331 //! \brief Get the starting point of the curve.
00332 //!
00333 //! \param x The x coordinate of the start point
00334 //! \param y The y coordinate of the start point
00335 //! \param z The z coordinate of the start point
00336 void SmoothCurve::start_coordinates( double& x, double& y, double& z )
00337 {
00338 
00339     int nnodes                = 0;
00340     const EntityHandle* conn2 = NULL;
00341     _mb->get_connectivity( _entities[0], conn2, nnodes );
00342     double c[3];
00343     _mb->get_coords( conn2, 1, c );
00344 
00345     x = c[0];
00346     y = c[1];
00347     z = c[2];
00348 
00349     return;
00350 }
00351 
00352 //! \brief Get the ending point of the curve.
00353 //!
00354 //! \param x The x coordinate of the start point
00355 //! \param y The y coordinate of the start point
00356 //! \param z The z coordinate of the start point
00357 void SmoothCurve::end_coordinates( double& x, double& y, double& z )
00358 {
00359 
00360     int nnodes                = 0;
00361     const EntityHandle* conn2 = NULL;
00362     _mb->get_connectivity( _entities[_entities.size() - 1], conn2, nnodes );
00363     double c[3];
00364     // careful, the second node here
00365     _mb->get_coords( &conn2[1], 1, c );
00366 
00367     x = c[0];
00368     y = c[1];
00369     z = c[2];
00370     return;
00371 }
00372 
00373 // this will recompute the 2 tangents for each edge, considering the geo edge they are into
00374 void SmoothCurve::compute_tangents_for_each_edge()
00375 {
00376     // will retrieve the edges in each set; they are retrieved in order they were put into, because
00377     // these sets are "MESHSET_ORDERED"
00378     // retrieve the tag handle for the tangents; it should have been created already
00379     // this tangents are computed for the chain of edges that form a geometric edge
00380     // some checks should be performed on the vertices, but we trust the correctness of the model
00381     // completely (like the vertices should match in the chain...)
00382     Tag tangentsTag;
00383     ErrorCode rval = _mb->tag_get_handle( "TANGENTS", 6, MB_TYPE_DOUBLE, tangentsTag );
00384     if( rval != MB_SUCCESS ) return;  // some error should be thrown
00385     std::vector< EntityHandle > entities;
00386     _mb->get_entities_by_type( _set, MBEDGE, entities );  // no recursion!!
00387     // basically, each tangent at a node will depend on previous tangent
00388     int nbEdges = entities.size();
00389     // now, we can advance in the loop
00390     // the only special problem is if the first node coincides with the last node, then we should
00391     // consider the closed loop; or maybe we should look at angles in that case too?
00392     // also, should we look at the 2 semi-circles case? How to decide if we need to continue the
00393     // "tangents" maybe we can do that later, and we can alter the tangents at the feature nodes, in
00394     // the directions of the loops again, do we need to decide the "closed" loop or not? Not yet...
00395     EntityHandle previousEdge = entities[0];                            // this is the first edge in the chain
00396     CartVect TP[2];                                                     // tangents for the previous edge
00397     rval = _mb->tag_get_data( tangentsTag, &previousEdge, 1, &TP[0] );  // tangents for previous edge
00398     if( rval != MB_SUCCESS ) return;                                    // some error should be thrown
00399     CartVect TC[2];                                                     // tangents for the current edge
00400     EntityHandle currentEdge;
00401     for( int i = 1; i < nbEdges; i++ )
00402     {
00403         // current edge will start after first one
00404         currentEdge = entities[i];
00405         rval        = _mb->tag_get_data( tangentsTag, &currentEdge, 1, &TC[0] );  //
00406         if( rval != MB_SUCCESS ) return;                                          // some error should be thrown
00407         // now compute the new tangent at common vertex; reset tangents for previous edge and
00408         // current edge a little bit of CPU and memory waste, but this is life
00409         CartVect T = 0.5 * TC[0] + 0.5 * TP[1];  //
00410         T.normalize();
00411         TP[1] = T;
00412         rval  = _mb->tag_set_data( tangentsTag, &previousEdge, 1, &TP[0] );  //
00413         if( rval != MB_SUCCESS ) return;                                     // some error should be thrown
00414         TC[0] = T;
00415         rval  = _mb->tag_set_data( tangentsTag, &currentEdge, 1, &TC[0] );  //
00416         if( rval != MB_SUCCESS ) return;                                    // some error should be thrown
00417         // now set the next edge
00418         previousEdge = currentEdge;
00419         TP[0]        = TC[0];
00420         TP[1]        = TC[1];
00421     }
00422     return;
00423 }
00424 
00425 void SmoothCurve::compute_control_points_on_boundary_edges( double, std::map< EntityHandle, SmoothFace* >& mapSurfaces,
00426                                                             Tag controlPointsTag, Tag markTag )
00427 {
00428     // these points really need the surfaces they belong to, because the control points on edges
00429     // depend on the normals on surfaces
00430     // the control points are averaged from different surfaces, by simple mean average
00431     // the surfaces have
00432     // do we really need min_dot here?
00433     // first of all, find out the SmoothFace for each surface set that is adjacent here
00434     // GeomTopoTool gTopoTool(_mb);
00435     std::vector< EntityHandle > faces;
00436     std::vector< int > senses;
00437     ErrorCode rval = _gtt->get_senses( _set, faces, senses );
00438     if( MB_SUCCESS != rval ) return;
00439 
00440     // need to find the smooth face attached
00441     unsigned int numSurfacesAdjacent = faces.size();
00442     // get the edges, and then get the
00443     // std::vector<EntityHandle> entities;
00444     _mb->get_entities_by_type( _set, MBEDGE, _entities );  // no recursion!!
00445     // each edge has the tangent computed already
00446     Tag tangentsTag;
00447     rval = _mb->tag_get_handle( "TANGENTS", 6, MB_TYPE_DOUBLE, tangentsTag );
00448     if( rval != MB_SUCCESS ) return;  // some error should be thrown
00449 
00450     // we do not want to search every time
00451     std::vector< SmoothFace* > smoothFaceArray;
00452     unsigned int i = 0;
00453     for( i = 0; i < numSurfacesAdjacent; i++ )
00454     {
00455         SmoothFace* sms = mapSurfaces[faces[i]];
00456         smoothFaceArray.push_back( sms );
00457     }
00458 
00459     unsigned int e = 0;
00460     for( e = 0; e < _entities.size(); e++ )
00461     {
00462         CartVect zero( 0. );
00463         CartVect ctrlP[3] = { zero, zero, zero };  // null positions initially
00464         // the control points are averaged from connected faces
00465         EntityHandle edge = _entities[e];  // the edge in the chain
00466 
00467         int nnodes;
00468         const EntityHandle* conn2;
00469         rval = _mb->get_connectivity( edge, conn2, nnodes );
00470         if( rval != MB_SUCCESS || 2 != nnodes ) return;  // or continue or return error
00471 
00472         // double coords[6]; // store the coordinates for the nodes
00473         CartVect P[2];
00474         // ErrorCode rval = _mb->get_coords(conn2, 2, coords);
00475         rval = _mb->get_coords( conn2, 2, (double*)&P[0] );
00476         if( rval != MB_SUCCESS ) return;
00477 
00478         CartVect chord = P[1] - P[0];
00479         _leng += chord.length();
00480         _fractions.push_back( _leng );
00481         CartVect N[2];
00482 
00483         // MBCartVect N0(&normalVec[0]);
00484         // MBCartVect N3(&normalVec[3]);
00485         CartVect T[2];  // T0, T3
00486         // if (edge->num_adj_facets() <= 1) {
00487         // stat = compute_curve_tangent(edge, min_dot, T0, T3);
00488         //  if (stat != CUBIT_SUCCESS)
00489         //  return stat;
00490         //} else {
00491         //}
00492         rval = _mb->tag_get_data( tangentsTag, &edge, 1, &T[0] );
00493         if( rval != MB_SUCCESS ) return;
00494 
00495         for( i = 0; i < numSurfacesAdjacent; i++ )
00496         {
00497             CartVect controlForEdge[3];
00498             rval = smoothFaceArray[i]->get_normals_for_vertices( conn2, N );
00499             if( rval != MB_SUCCESS ) return;
00500 
00501             rval = smoothFaceArray[i]->init_edge_control_points( P[0], P[1], N[0], N[1], T[0], T[1], controlForEdge );
00502             if( rval != MB_SUCCESS ) return;
00503 
00504             // accumulate those over faces!!!
00505             for( int j = 0; j < 3; j++ )
00506             {
00507                 ctrlP[j] += controlForEdge[j];
00508             }
00509         }
00510         // now divide them for the average position!
00511         for( int j = 0; j < 3; j++ )
00512         {
00513             ctrlP[j] /= numSurfacesAdjacent;
00514         }
00515         // we are done, set the control points now!
00516         // edge->control_points(ctrl_pts, 4);
00517         rval = _mb->tag_set_data( controlPointsTag, &edge, 1, &ctrlP[0] );
00518         if( rval != MB_SUCCESS ) return;
00519 
00520         this->_edgeTag = controlPointsTag;  // this is a tag that will be stored with the edge
00521         // is that a waste of memory or not...
00522         // also mark the edge for later on
00523         unsigned char used = 1;
00524         _mb->tag_set_data( markTag, &edge, 1, &used );
00525     }
00526     // now divide fractions, to make them vary from 0 to 1
00527     assert( _leng > 0. );
00528     for( e = 0; e < _entities.size(); e++ )
00529         _fractions[e] /= _leng;
00530 }
00531 
00532 ErrorCode SmoothCurve::evaluate_smooth_edge( EntityHandle eh, double& tt, CartVect& outv, CartVect& out_tangent )
00533 {
00534     CartVect P[2];              // P0 and P1
00535     CartVect controlPoints[3];  // edge control points
00536     double t4, t3, t2, one_minus_t, one_minus_t2, one_minus_t3, one_minus_t4;
00537 
00538     // project the position to the linear edge
00539     // t is from 0 to 1 only!!
00540     // double tt = (t + 1) * 0.5;
00541     if( tt <= 0.0 ) tt = 0.0;
00542     if( tt >= 1.0 ) tt = 1.0;
00543 
00544     int nnodes                = 0;
00545     const EntityHandle* conn2 = NULL;
00546     ErrorCode rval            = _mb->get_connectivity( eh, conn2, nnodes );
00547     if( rval != MB_SUCCESS ) return rval;
00548 
00549     rval = _mb->get_coords( conn2, 2, (double*)&P[0] );
00550     if( rval != MB_SUCCESS ) return rval;
00551 
00552     if( 0 == _edgeTag )
00553     {
00554         rval = _mb->tag_get_handle( "CONTROLEDGE", 9, MB_TYPE_DOUBLE, _edgeTag );
00555         if( rval != MB_SUCCESS ) return rval;
00556     }
00557     rval = _mb->tag_get_data( _edgeTag, &eh, 1, (double*)&controlPoints[0] );
00558     if( rval != MB_SUCCESS ) return rval;
00559 
00560     t2           = tt * tt;
00561     t3           = t2 * tt;
00562     t4           = t3 * tt;
00563     one_minus_t  = 1. - tt;
00564     one_minus_t2 = one_minus_t * one_minus_t;
00565     one_minus_t3 = one_minus_t2 * one_minus_t;
00566     one_minus_t4 = one_minus_t3 * one_minus_t;
00567 
00568     outv = one_minus_t4 * P[0] + 4. * one_minus_t3 * tt * controlPoints[0] + 6. * one_minus_t2 * t2 * controlPoints[1] +
00569            4. * one_minus_t * t3 * controlPoints[2] + t4 * P[1];
00570 
00571     out_tangent = -4. * one_minus_t3 * P[0] + 4. * ( one_minus_t3 - 3. * tt * one_minus_t2 ) * controlPoints[0] +
00572                   12. * ( tt * one_minus_t2 - t2 * one_minus_t ) * controlPoints[1] +
00573                   4. * ( 3. * t2 * one_minus_t - t3 ) * controlPoints[2] + 4. * t3 * P[1];
00574     return MB_SUCCESS;
00575 }
00576 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines