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