1/*
2 * SmoothCurve.cpp
3 *
4 */56#include"SmoothCurve.hpp"7//#include "SmoothVertex.hpp"8#include"SmoothFace.hpp"9#include<cassert>10#include<iostream>11#include"moab/GeomTopoTool.hpp"1213namespace moab
14 {
1516 SmoothCurve::SmoothCurve( Interface* mb, EntityHandle curve, GeomTopoTool* gTool )
17 : _mb( mb ), _set( curve ), _gtt( gTool )
18 {
19//_mbOut->create_meshset(MESHSET_ORDERED, _oSet);20/*_cmlEdgeMesher = new CMLEdgeMesher (this, CML::STANDARD);
21 _cmlEdgeMesher->set_sizing_function(CML::LINEAR_SIZING);*/22 _leng = 0; // not initialized23 _edgeTag = 0; // not initialized24 }
25 SmoothCurve::~SmoothCurve()
26 {
27// TODO Auto-generated destructor stub28 }
2930doubleSmoothCurve::arc_length()
31 {
3233return _leng;
34 }
3536//! \brief Get the parametric status of the curve.37//!38//! \return \a true if curve is parametric, \a false otherwise.39boolSmoothCurve::is_parametric()
40 {
41returntrue;
42 }
4344//! \brief Get the periodic status of the curve.45//!46//! \param period The period of the curve if periodic.47//!48//! \return \a true if curve is periodic, \a false otherwise.49boolSmoothCurve::is_periodic( double& period )
50 {
51// assert(_ref_edge);52// return _ref_edge->is_periodic( period);53 Range vsets;
54 _mb->get_child_meshsets( _set, vsets ); // num_hops =155if( vsets.size() == 1 )
56 {
57 period = _leng;
58returntrue; // true , especially for ice sheet data59 }
60returnfalse;
61 }
6263//! \brief Get the parameter range of the curve.64//!65//! \param u_start The beginning curve parameter66//! \param u_end The ending curve parameter67//!68//! \note The numerical value of \a u_start may be greater69//! than the numerical value of \a u_end.70voidSmoothCurve::get_param_range( double& u_start, double& u_end )
71 {
72// assert(_ref_edge);73 u_start = 0;
74 u_end = 1.;
7576return;
77 }
7879//! Compute the parameter value at a specified distance along the curve.80//!81//! \param u_root The start parameter from which to compute the distance82//! along the curve.83//! \param arc_length The distance to move along the curve.84//!85//! \note For positive values of \a arc_length the distance will be86//! computed in the direction of increasing parameter value along the87//! curve. For negative values of \a arc_length the distance will be88//! computed in the direction of decreasing parameter value along the89//! curve.90//!91//! \return The parametric coordinate u along the curve92doubleSmoothCurve::u_from_arc_length( double u_root, double arc_leng )
93 {
9495if( _leng <= 0 ) return0;
96return u_root + arc_leng / _leng;
97 }
9899//! \brief Evaluate the curve at a specified parameter value.100//!101//! \param u The parameter at which to evaluate the curve102//! \param x The x coordinate of the evaluated point103//! \param y The y coordinate of the evaluated point104//! \param z The z coordinate of the evaluated point105boolSmoothCurve::position_from_u( double u, double& x, double& y, double& z, double* tg )
106 {
107108// _fractions are increasing, so find the109double* ptr = std::lower_bound( &_fractions[0], ( &_fractions[0] ) + _fractions.size(), u );
110int index = ptr - &_fractions[0];
111double nextFraction = _fractions[index];
112double prevFraction = 0;
113if( index > 0 )
114 {
115 prevFraction = _fractions[index - 1];
116 }
117double t = ( u - prevFraction ) / ( nextFraction - prevFraction );
118119 EntityHandle edge = _entities[index];
120121 CartVect position, tangent;
122 ErrorCode rval = evaluate_smooth_edge( edge, t, position, tangent );
123if( MB_SUCCESS != rval ) returnfalse;
124assert( rval == MB_SUCCESS );
125 x = position[0];
126 y = position[1];
127 z = position[2];
128if( tg )
129 {
130// we need to do some scaling,131double dtdu = 1 / ( nextFraction - prevFraction );
132 tg[0] = tangent[0] * dtdu;
133 tg[1] = tangent[1] * dtdu;
134 tg[2] = tangent[2] * dtdu;
135 }
136137returntrue;
138 }
139//! \brief Move a point near the curve to the closest point on the curve.140//!141//! \param x The x coordinate of the point142//! \param y The y coordinate of the point143//! \param z The z coordinate of the point144voidSmoothCurve::move_to_curve( double& x, double& y, double& z )
145 {
146147// find closest point to the curve, and the parametric position148// must be close by, but how close ???149 EntityHandle v;
150int edgeIndex;
151double u = u_from_position( x, y, z, v, edgeIndex );
152position_from_u( u, x, y, z );
153154return;
155 }
156157//! Get the u parameter value on the curve closest to x,y,z158//! and the point on the curve.159//!160//! \param x The x coordinate of the point161//! \param y The y coordinate of the point162//! \param z The z coordinate of the point163//!164//! \return The parametric coordinate u on the curve165doubleSmoothCurve::u_from_position( double x, double y, double z, EntityHandle& v, int& edgeIndex )
166 {
167// this is an iterative process, expensive usually168// get first all nodes , and their positions169// find the closest node (and edge), and from there do some170// iterations up to a point171// do not exaggerate with convergence criteria172173 v = 0; // we do not have a close by vertex yet174CartVect initialPos( x, y, z );
175double u = 0;
176int nbNodes = (int)_entities.size() * 2; // the mesh edges are stored177 std::vector< EntityHandle > nodesConnec;
178 nodesConnec.resize( nbNodes );
179 ErrorCode rval = this->_mb->get_connectivity( &( _entities[0] ), nbNodes / 2, nodesConnec );
180if( MB_SUCCESS != rval )
181 {
182 std::cout << "error in getting connectivity\n";
183return0;
184 }
185// collapse nodesConnec, nodes should be in order186for( int k = 0; k < nbNodes / 2; k++ )
187 {
188 nodesConnec[k + 1] = nodesConnec[2 * k + 1];
189 }
190int numNodes = nbNodes / 2 + 1;
191 std::vector< CartVect > coordNodes;
192 coordNodes.resize( numNodes );
193194 rval = _mb->get_coords( &( nodesConnec[0] ), numNodes, (double*)&( coordNodes[0] ) );
195if( MB_SUCCESS != rval )
196 {
197 std::cout << "error in getting node positions\n";
198return0;
199 }
200// find the closest node, then find the closest edge, based on closest node201202int indexNode = 0;
203double minDist = 1.e30;
204// expensive linear search205for( int i = 0; i < numNodes; i++ )
206 {
207double d1 = ( initialPos - coordNodes[i] ).length();
208if( d1 < minDist )
209 {
210 indexNode = i;
211 minDist = d1;
212 }
213 }
214double tolerance = 0.00001; // what is the unit?215// something reasonable216if( minDist < tolerance )
217 {
218 v = nodesConnec[indexNode];
219// we are done, just return the proper u (from fractions)220if( indexNode == 0 )
221 {
222return0; // first node has u = 0223 }
224else225return _fractions[indexNode - 1]; // fractions[0] > 0!!)226 }
227// find the mesh edge; could be previous or next edge228 edgeIndex = indexNode; // could be the previous one, though!!229if( edgeIndex == numNodes - 1 )
230 edgeIndex--; // we have one less edge, and do not worry about next edge231else232 {
233if( edgeIndex > 0 )
234 {
235// could be the previous; decide based on distance to the other236// nodes of the 2 connected edges237 CartVect prevNodePos = coordNodes[edgeIndex - 1];
238 CartVect nextNodePos = coordNodes[edgeIndex + 1];
239if( ( prevNodePos - initialPos ).length_squared() < ( nextNodePos - initialPos ).length_squared() )
240 {
241 edgeIndex--;
242 }
243 }
244 }
245// now, we know for sure that the closest point is somewhere on edgeIndex edge246//247248// do newton iteration for local t between 0 and 1249250// copy from evaluation method251 CartVect P[2]; // P0 and P1252 CartVect controlPoints[3]; // edge control points253double t4, t3, t2, one_minus_t, one_minus_t2, one_minus_t3, one_minus_t4;
254255 P[0] = coordNodes[edgeIndex];
256 P[1] = coordNodes[edgeIndex + 1];
257258if( 0 == _edgeTag )
259 {
260 rval = _mb->tag_get_handle( "CONTROLEDGE", 9, MB_TYPE_DOUBLE, _edgeTag );
261if( rval != MB_SUCCESS ) return0;
262 }
263 rval = _mb->tag_get_data( _edgeTag, &( _entities[edgeIndex] ), 1, (double*)&controlPoints[0] );
264if( rval != MB_SUCCESS ) return rval;
265266// starting point267double tt = 0.5; // between the 2 ends of the edge268int iterations = 0;
269// find iteratively a better point270int maxIterations = 10; // not too many271 CartVect outv;
272// we will solve minimize F = 0.5 * ( ini - r(t) )^2273// so solve F'(t) = 0274// Iteration: t_ -> t - F'(t)/F"(t)275// F'(t) = r'(t) (ini-r(t) )276// F"(t) = r"(t) (ini-r(t) ) - (r'(t))^2277while( iterations < maxIterations )
278//279 {
280 t2 = tt * tt;
281 t3 = t2 * tt;
282 t4 = t3 * tt;
283one_minus_t = 1. - tt;
284 one_minus_t2 = one_minus_t * one_minus_t;
285 one_minus_t3 = one_minus_t2 * one_minus_t;
286 one_minus_t4 = one_minus_t3 * one_minus_t;
287288 outv = one_minus_t4 * P[0] + 4. * one_minus_t3 * tt * controlPoints[0] +
2896. * one_minus_t2 * t2 * controlPoints[1] + 4. * one_minus_t * t3 * controlPoints[2] + t4 * P[1];
290291 CartVect out_tangent = -4. * one_minus_t3 * P[0] +
2924. * ( one_minus_t3 - 3. * tt * one_minus_t2 ) * controlPoints[0] +
29312. * ( tt * one_minus_t2 - t2 * one_minus_t ) * controlPoints[1] +
2944. * ( 3. * t2 * one_minus_t - t3 ) * controlPoints[2] + 4. * t3 * P[1];
295296 CartVect second_deriv =
29712. * one_minus_t2 * P[0] +
2984. * ( -3. * one_minus_t2 - 3. * one_minus_t2 + 6. * tt * one_minus_t ) * controlPoints[0] +
29912. * ( one_minus_t2 - 4 * tt * one_minus_t + t2 ) * controlPoints[1] +
3004. * ( 6. * tt - 12 * t2 ) * controlPoints[2] + 12. * t2 * P[1];
301 CartVect diff = outv - initialPos;
302double F_d = out_tangent % diff;
303double F_dd = second_deriv % diff + out_tangent.length_squared();
304305if( 0 == F_dd ) break; // get out, we found minimum?306307doubledelta_t = -F_d / F_dd;
308309if( fabs( delta_t ) < 0.000001 ) break;
310 tt = tt + delta_t;
311if( tt < 0 )
312 {
313 tt = 0.;
314 v = nodesConnec[edgeIndex]; // we are at end of mesh edge315break;
316 }
317if( tt > 1 )
318 {
319 tt = 1;
320 v = nodesConnec[edgeIndex + 1]; // we are at one end321break;
322 }
323 iterations++;
324 }
325// so we have t on the segment, convert to u, which should326// be between _fractions[edgeIndex] numbers327double prevFraction = 0;
328if( edgeIndex > 0 ) prevFraction = _fractions[edgeIndex - 1];
329330 u = prevFraction + tt * ( _fractions[edgeIndex] - prevFraction );
331return u;
332 }
333334//! \brief Get the starting point of the curve.335//!336//! \param x The x coordinate of the start point337//! \param y The y coordinate of the start point338//! \param z The z coordinate of the start point339voidSmoothCurve::start_coordinates( double& x, double& y, double& z )
340 {
341342int nnodes = 0;
343const EntityHandle* conn2 = NULL;
344 _mb->get_connectivity( _entities[0], conn2, nnodes );
345double c[3];
346 _mb->get_coords( conn2, 1, c );
347348 x = c[0];
349 y = c[1];
350 z = c[2];
351352return;
353 }
354355//! \brief Get the ending point of the curve.356//!357//! \param x The x coordinate of the start point358//! \param y The y coordinate of the start point359//! \param z The z coordinate of the start point360voidSmoothCurve::end_coordinates( double& x, double& y, double& z )
361 {
362363int nnodes = 0;
364const EntityHandle* conn2 = NULL;
365 _mb->get_connectivity( _entities[_entities.size() - 1], conn2, nnodes );
366double c[3];
367// careful, the second node here368 _mb->get_coords( &conn2[1], 1, c );
369370 x = c[0];
371 y = c[1];
372 z = c[2];
373return;
374 }
375376// this will recompute the 2 tangents for each edge, considering the geo edge they are into377voidSmoothCurve::compute_tangents_for_each_edge()
378 {
379// will retrieve the edges in each set; they are retrieved in order they were put into, because380// these sets are "MESHSET_ORDERED"381// retrieve the tag handle for the tangents; it should have been created already382// this tangents are computed for the chain of edges that form a geometric edge383// some checks should be performed on the vertices, but we trust the correctness of the model384// completely (like the vertices should match in the chain...)385 Tag tangentsTag;
386 ErrorCode rval = _mb->tag_get_handle( "TANGENTS", 6, MB_TYPE_DOUBLE, tangentsTag );
387if( rval != MB_SUCCESS ) return; // some error should be thrown388 std::vector< EntityHandle > entities;
389 _mb->get_entities_by_type( _set, MBEDGE, entities ); // no recursion!!390// basically, each tangent at a node will depend on previous tangent391int nbEdges = entities.size();
392// now, we can advance in the loop393// the only special problem is if the first node coincides with the last node, then we should394// consider the closed loop; or maybe we should look at angles in that case too?395// also, should we look at the 2 semi-circles case? How to decide if we need to continue the396// "tangents" maybe we can do that later, and we can alter the tangents at the feature nodes, in397// the directions of the loops again, do we need to decide the "closed" loop or not? Not yet...398 EntityHandle previousEdge = entities[0]; // this is the first edge in the chain399 CartVect TP[2]; // tangents for the previous edge400 rval = _mb->tag_get_data( tangentsTag, &previousEdge, 1, &TP[0] ); // tangents for previous edge401if( rval != MB_SUCCESS ) return; // some error should be thrown402 CartVect TC[2]; // tangents for the current edge403 EntityHandle currentEdge;
404for( int i = 1; i < nbEdges; i++ )
405 {
406// current edge will start after first one407 currentEdge = entities[i];
408 rval = _mb->tag_get_data( tangentsTag, ¤tEdge, 1, &TC[0] ); //409if( rval != MB_SUCCESS ) return; // some error should be thrown410// now compute the new tangent at common vertex; reset tangents for previous edge and411// current edge a little bit of CPU and memory waste, but this is life412 CartVect T = 0.5 * TC[0] + 0.5 * TP[1]; //413 T.normalize();
414 TP[1] = T;
415 rval = _mb->tag_set_data( tangentsTag, &previousEdge, 1, &TP[0] ); //416if( rval != MB_SUCCESS ) return; // some error should be thrown417 TC[0] = T;
418 rval = _mb->tag_set_data( tangentsTag, ¤tEdge, 1, &TC[0] ); //419if( rval != MB_SUCCESS ) return; // some error should be thrown420// now set the next edge421 previousEdge = currentEdge;
422 TP[0] = TC[0];
423 TP[1] = TC[1];
424 }
425return;
426 }
427428voidSmoothCurve::compute_control_points_on_boundary_edges( double,
429 std::map< EntityHandle, SmoothFace* >& mapSurfaces,
430 Tag controlPointsTag,
431 Tag markTag )
432 {
433// these points really need the surfaces they belong to, because the control points on edges434// depend on the normals on surfaces435// the control points are averaged from different surfaces, by simple mean average436// the surfaces have437// do we really need min_dot here?438// first of all, find out the SmoothFace for each surface set that is adjacent here439// GeomTopoTool gTopoTool(_mb);440 std::vector< EntityHandle > faces;
441 std::vector< int > senses;
442 ErrorCode rval = _gtt->get_senses( _set, faces, senses );
443if( MB_SUCCESS != rval ) return;
444445// need to find the smooth face attached446unsignedint numSurfacesAdjacent = faces.size();
447// get the edges, and then get the448// std::vector<EntityHandle> entities;449 _mb->get_entities_by_type( _set, MBEDGE, _entities ); // no recursion!!450// each edge has the tangent computed already451 Tag tangentsTag;
452 rval = _mb->tag_get_handle( "TANGENTS", 6, MB_TYPE_DOUBLE, tangentsTag );
453if( rval != MB_SUCCESS ) return; // some error should be thrown454455// we do not want to search every time456 std::vector< SmoothFace* > smoothFaceArray;
457unsignedint i = 0;
458for( i = 0; i < numSurfacesAdjacent; i++ )
459 {
460 SmoothFace* sms = mapSurfaces[faces[i]];
461 smoothFaceArray.push_back( sms );
462 }
463464unsignedint e = 0;
465for( e = 0; e < _entities.size(); e++ )
466 {
467CartVect zero( 0. );
468 CartVect ctrlP[3] = { zero, zero, zero }; // null positions initially469// the control points are averaged from connected faces470 EntityHandle edge = _entities[e]; // the edge in the chain471472int nnodes;
473const EntityHandle* conn2;
474 rval = _mb->get_connectivity( edge, conn2, nnodes );
475if( rval != MB_SUCCESS || 2 != nnodes ) return; // or continue or return error476477// double coords[6]; // store the coordinates for the nodes478 CartVect P[2];
479// ErrorCode rval = _mb->get_coords(conn2, 2, coords);480 rval = _mb->get_coords( conn2, 2, (double*)&P[0] );
481if( rval != MB_SUCCESS ) return;
482483 CartVect chord = P[1] - P[0];
484 _leng += chord.length();
485 _fractions.push_back( _leng );
486 CartVect N[2];
487488// MBCartVect N0(&normalVec[0]);489// MBCartVect N3(&normalVec[3]);490 CartVect T[2]; // T0, T3491// if (edge->num_adj_facets() <= 1) {492// stat = compute_curve_tangent(edge, min_dot, T0, T3);493// if (stat != CUBIT_SUCCESS)494// return stat;495//} else {496//}497 rval = _mb->tag_get_data( tangentsTag, &edge, 1, &T[0] );
498if( rval != MB_SUCCESS ) return;
499500for( i = 0; i < numSurfacesAdjacent; i++ )
501 {
502 CartVect controlForEdge[3];
503 rval = smoothFaceArray[i]->get_normals_for_vertices( conn2, N );
504if( rval != MB_SUCCESS ) return;
505506 rval = smoothFaceArray[i]->init_edge_control_points( P[0], P[1], N[0], N[1], T[0], T[1], controlForEdge );
507if( rval != MB_SUCCESS ) return;
508509// accumulate those over faces!!!510for( int j = 0; j < 3; j++ )
511 {
512 ctrlP[j] += controlForEdge[j];
513 }
514 }
515// now divide them for the average position!516for( int j = 0; j < 3; j++ )
517 {
518 ctrlP[j] /= numSurfacesAdjacent;
519 }
520// we are done, set the control points now!521// edge->control_points(ctrl_pts, 4);522 rval = _mb->tag_set_data( controlPointsTag, &edge, 1, &ctrlP[0] );
523if( rval != MB_SUCCESS ) return;
524525this->_edgeTag = controlPointsTag; // this is a tag that will be stored with the edge526// is that a waste of memory or not...527// also mark the edge for later on528unsignedchar used = 1;
529 _mb->tag_set_data( markTag, &edge, 1, &used );
530 }
531// now divide fractions, to make them vary from 0 to 1532assert( _leng > 0. );
533for( e = 0; e < _entities.size(); e++ )
534 _fractions[e] /= _leng;
535 }
536537ErrorCode SmoothCurve::evaluate_smooth_edge( EntityHandle eh, double& tt, CartVect& outv, CartVect& out_tangent )
538 {
539 CartVect P[2]; // P0 and P1540 CartVect controlPoints[3]; // edge control points541double t4, t3, t2, one_minus_t, one_minus_t2, one_minus_t3, one_minus_t4;
542543// project the position to the linear edge544// t is from 0 to 1 only!!545// double tt = (t + 1) * 0.5;546if( tt <= 0.0 ) tt = 0.0;
547if( tt >= 1.0 ) tt = 1.0;
548549int nnodes = 0;
550const EntityHandle* conn2 = NULL;
551 ErrorCode rval = _mb->get_connectivity( eh, conn2, nnodes );
552if( rval != MB_SUCCESS ) return rval;
553554 rval = _mb->get_coords( conn2, 2, (double*)&P[0] );
555if( rval != MB_SUCCESS ) return rval;
556557if( 0 == _edgeTag )
558 {
559 rval = _mb->tag_get_handle( "CONTROLEDGE", 9, MB_TYPE_DOUBLE, _edgeTag );
560if( rval != MB_SUCCESS ) return rval;
561 }
562 rval = _mb->tag_get_data( _edgeTag, &eh, 1, (double*)&controlPoints[0] );
563if( rval != MB_SUCCESS ) return rval;
564565 t2 = tt * tt;
566 t3 = t2 * tt;
567 t4 = t3 * tt;
568one_minus_t = 1. - tt;
569 one_minus_t2 = one_minus_t * one_minus_t;
570 one_minus_t3 = one_minus_t2 * one_minus_t;
571 one_minus_t4 = one_minus_t3 * one_minus_t;
572573 outv = one_minus_t4 * P[0] + 4. * one_minus_t3 * tt * controlPoints[0] + 6. * one_minus_t2 * t2 * controlPoints[1] +
5744. * one_minus_t * t3 * controlPoints[2] + t4 * P[1];
575576 out_tangent = -4. * one_minus_t3 * P[0] + 4. * ( one_minus_t3 - 3. * tt * one_minus_t2 ) * controlPoints[0] +
57712. * ( tt * one_minus_t2 - t2 * one_minus_t ) * controlPoints[1] +
5784. * ( 3. * t2 * one_minus_t - t3 ) * controlPoints[2] + 4. * t3 * P[1];
579return MB_SUCCESS;
580 }
581 } // namespace moab