+ gp_XYZ prevPosV = PrevPos();
+ if ( eov->SWOLType() == TopAbs_EDGE )
+ {
+ BRepAdaptor_Curve curve ( TopoDS::Edge( eov->_sWOL ));
+ prevPosV = curve.Value( prevPosV.X() ).XYZ();
+ }
+ else if ( eov->SWOLType() == TopAbs_FACE )
+ {
+ BRepAdaptor_Surface surface( TopoDS::Face( eov->_sWOL ));
+ prevPosV = surface.Value( prevPosV.X(), prevPosV.Y() ).XYZ();
+ }
+
+ SMDS_FacePositionPtr fPos;
+ //double r = 1. - Min( 0.9, step / 10. );
+ for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
+ {
+ _LayerEdge* edgeF = *e;
+ const gp_XYZ prevVF = edgeF->PrevPos() - prevPosV;
+ const gp_XYZ newPosF = curPosV + prevVF;
+ SMDS_MeshNode* tgtNodeF = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
+ tgtNodeF->setXYZ( newPosF.X(), newPosF.Y(), newPosF.Z() );
+ edgeF->_pos.back() = newPosF;
+ dumpMoveComm( tgtNodeF, "MoveNearConcaVer" ); // debug
+
+ // set _curvature to make edgeF updated by putOnOffsetSurface()
+ if ( !edgeF->_curvature )
+ if (( fPos = edgeF->_nodes[0]->GetPosition() ))
+ {
+ edgeF->_curvature = _Factory::NewCurvature();
+ edgeF->_curvature->_r = 0;
+ edgeF->_curvature->_k = 0;
+ edgeF->_curvature->_h2lenRatio = 0;
+ edgeF->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
+ }
+ }
+ // gp_XYZ inflationVec( SMESH_TNodeXYZ( _nodes.back() ) -
+ // SMESH_TNodeXYZ( _nodes[0] ));
+ // for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
+ // {
+ // _LayerEdge* edgeF = *e;
+ // gp_XYZ newPos = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
+ // SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
+ // tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ // edgeF->_pos.back() = newPosF;
+ // dumpMoveComm( tgtNode, "MoveNearConcaVer" ); // debug
+ // }
+
+ // smooth _LayerEdge's around moved nodes
+ //size_t nbBadBefore = badSmooEdges.size();
+ for ( set< _LayerEdge* >::iterator e = edges.begin(); e != edges.end(); ++e )
+ {
+ _LayerEdge* edgeF = *e;
+ for ( size_t j = 0; j < edgeF->_neibors.size(); ++j )
+ if ( edgeF->_neibors[j]->_nodes[0]->getshapeId() == eos->_shapeID )
+ //&& !edges.count( edgeF->_neibors[j] ))
+ {
+ _LayerEdge* edgeFN = edgeF->_neibors[j];
+ edgeFN->Unset( SMOOTHED );
+ int nbBad = edgeFN->Smooth( step, /*isConcaFace=*/true, /*findBest=*/true );
+ // if ( nbBad > 0 )
+ // {
+ // gp_XYZ newPos = SMESH_TNodeXYZ( edgeFN->_nodes[0] ) + inflationVec;
+ // const gp_XYZ& prevPos = edgeFN->_pos[ edgeFN->_pos.size()-2 ];
+ // int nbBadAfter = edgeFN->_simplices.size();
+ // double vol;
+ // for ( size_t iS = 0; iS < edgeFN->_simplices.size(); ++iS )
+ // {
+ // nbBadAfter -= edgeFN->_simplices[iS].IsForward( &prevPos, &newPos, vol );
+ // }
+ // if ( nbBadAfter <= nbBad )
+ // {
+ // SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeFN->_nodes.back() );
+ // tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ // edgeF->_pos.back() = newPosF;
+ // dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
+ // nbBad = nbBadAfter;
+ // }
+ // }
+ if ( nbBad > 0 )
+ badSmooEdges.push_back( edgeFN );
+ }
+ }
+ // move a bit not smoothed around moved nodes
+ // for ( size_t i = nbBadBefore; i < badSmooEdges.size(); ++i )
+ // {
+ // _LayerEdge* edgeF = badSmooEdges[i];
+ // SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edgeF->_nodes.back() );
+ // gp_XYZ newPos1 = SMESH_TNodeXYZ( edgeF->_nodes[0] ) + inflationVec;
+ // gp_XYZ newPos2 = 0.5 * ( newPos1 + SMESH_TNodeXYZ( tgtNode ));
+ // tgtNode->setXYZ( newPos2.X(), newPos2.Y(), newPos2.Z() );
+ // edgeF->_pos.back() = newPosF;
+ // dumpMoveComm( tgtNode, "MoveNearConcaVer 2" ); // debug
+ // }
+}
+
+//================================================================================
+/*!
+ * \brief Perform smooth of _LayerEdge's based on EDGE's
+ * \retval bool - true if node has been moved
+ */
+//================================================================================
+
+bool _LayerEdge::SmoothOnEdge(Handle(ShapeAnalysis_Surface)& surface,
+ const TopoDS_Face& F,
+ SMESH_MesherHelper& helper)
+{
+ ASSERT( IsOnEdge() );
+
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _nodes.back() );
+ SMESH_TNodeXYZ oldPos( tgtNode );
+ double dist01, distNewOld;
+
+ SMESH_TNodeXYZ p0( _2neibors->tgtNode(0));
+ SMESH_TNodeXYZ p1( _2neibors->tgtNode(1));
+ dist01 = p0.Distance( _2neibors->tgtNode(1) );
+
+ gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1];
+ double lenDelta = 0;
+ if ( _curvature )
+ {
+ //lenDelta = _curvature->lenDelta( _len );
+ lenDelta = _curvature->lenDeltaByDist( dist01 );
+ newPos.ChangeCoord() += _normal * lenDelta;
+ }
+
+ distNewOld = newPos.Distance( oldPos );
+
+ if ( F.IsNull() )
+ {
+ if ( _2neibors->_plnNorm )
+ {
+ // put newPos on the plane defined by source node and _plnNorm
+ gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ();
+ double new2srcProj = (*_2neibors->_plnNorm) * new2src;
+ newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj;
+ }
+ tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ _pos.back() = newPos.XYZ();
+ }
+ else
+ {
+ tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ gp_XY uv( Precision::Infinite(), 0 );
+ helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true );
+ _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
+
+ newPos = surface->Value( uv );
+ tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ }
+
+ // commented for IPAL0052478
+ // if ( _curvature && lenDelta < 0 )
+ // {
+ // gp_Pnt prevPos( _pos[ _pos.size()-2 ]);
+ // _len -= prevPos.Distance( oldPos );
+ // _len += prevPos.Distance( newPos );
+ // }
+ bool moved = distNewOld > dist01/50;
+ //if ( moved )
+ dumpMove( tgtNode ); // debug
+
+ return moved;
+}
+
+//================================================================================
+/*!
+ * \brief Perform 3D smooth of nodes inflated from FACE. No check of validity
+ */
+//================================================================================
+
+void _LayerEdge::SmoothWoCheck()
+{
+ if ( Is( DIFFICULT ))
+ return;
+
+ bool moved = Is( SMOOTHED );
+ for ( size_t i = 0; i < _neibors.size() && !moved; ++i )
+ moved = _neibors[i]->Is( SMOOTHED );
+ if ( !moved )
+ return;
+
+ gp_XYZ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
+
+ SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
+ n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
+ _pos.back() = newPos;
+
+ dumpMoveComm( n, SMESH_Comment("No check - ") << _funNames[ smooFunID() ]);
+}
+
+//================================================================================
+/*!
+ * \brief Checks validity of _neibors on EDGEs and VERTEXes
+ */
+//================================================================================
+
+int _LayerEdge::CheckNeiborsOnBoundary( vector< _LayerEdge* >* badNeibors, bool * needSmooth )
+{
+ if ( ! Is( NEAR_BOUNDARY ))
+ return 0;
+
+ int nbBad = 0;
+ double vol;
+ for ( size_t iN = 0; iN < _neibors.size(); ++iN )
+ {
+ _LayerEdge* eN = _neibors[iN];
+ if ( eN->_nodes[0]->getshapeId() == _nodes[0]->getshapeId() )
+ continue;
+ if ( needSmooth )
+ *needSmooth |= ( eN->Is( _LayerEdge::BLOCKED ) ||
+ eN->Is( _LayerEdge::NORMAL_UPDATED ) ||
+ eN->_pos.size() != _pos.size() );
+
+ SMESH_TNodeXYZ curPosN ( eN->_nodes.back() );
+ SMESH_TNodeXYZ prevPosN( eN->_nodes[0] );
+ for ( size_t i = 0; i < eN->_simplices.size(); ++i )
+ if ( eN->_nodes.size() > 1 &&
+ eN->_simplices[i].Includes( _nodes.back() ) &&
+ !eN->_simplices[i].IsForward( &prevPosN, &curPosN, vol ))
+ {
+ ++nbBad;
+ if ( badNeibors )
+ {
+ badNeibors->push_back( eN );
+ debugMsg("Bad boundary simplex ( "
+ << " "<< eN->_nodes[0]->GetID()
+ << " "<< eN->_nodes.back()->GetID()
+ << " "<< eN->_simplices[i]._nPrev->GetID()
+ << " "<< eN->_simplices[i]._nNext->GetID() << " )" );
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return nbBad;
+}
+
+//================================================================================
+/*!
+ * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
+ * \retval int - nb of bad simplices around this _LayerEdge
+ */
+//================================================================================
+
+int _LayerEdge::Smooth(const int step, bool findBest, vector< _LayerEdge* >& toSmooth )
+{
+ if ( !Is( MOVED ) || Is( SMOOTHED ) || Is( BLOCKED ))
+ return 0; // shape of simplices not changed
+ if ( _simplices.size() < 2 )
+ return 0; // _LayerEdge inflated along EDGE or FACE
+
+ if ( Is( DIFFICULT )) // || Is( ON_CONCAVE_FACE )
+ findBest = true;
+
+ const gp_XYZ& curPos = _pos.back();
+ const gp_XYZ& prevPos = _pos[0]; //PrevPos();
+
+ // quality metrics (orientation) of tetras around _tgtNode
+ int nbOkBefore = 0;
+ double vol, minVolBefore = 1e100;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
+ minVolBefore = Min( minVolBefore, vol );
+ }
+ int nbBad = _simplices.size() - nbOkBefore;
+
+ bool bndNeedSmooth = false;
+ if ( nbBad == 0 )
+ nbBad = CheckNeiborsOnBoundary( 0, & bndNeedSmooth );
+ if ( nbBad > 0 )
+ Set( DISTORTED );
+
+ // evaluate min angle
+ if ( nbBad == 0 && !findBest && !bndNeedSmooth )
+ {
+ size_t nbGoodAngles = _simplices.size();
+ double angle;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ if ( !_simplices[i].IsMinAngleOK( curPos, angle ) && angle > _minAngle )
+ --nbGoodAngles;
+ }
+ if ( nbGoodAngles == _simplices.size() )
+ {
+ Unset( MOVED );
+ return 0;
+ }
+ }
+ if ( Is( ON_CONCAVE_FACE ))
+ findBest = true;
+
+ if ( step % 2 == 0 )
+ findBest = false;
+
+ if ( Is( ON_CONCAVE_FACE ) && !findBest ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
+ {
+ if ( _smooFunction == _funs[ FUN_LAPLACIAN ] )
+ _smooFunction = _funs[ FUN_CENTROIDAL ];
+ else
+ _smooFunction = _funs[ FUN_LAPLACIAN ];
+ }
+
+ // compute new position for the last _pos using different _funs
+ gp_XYZ newPos;
+ bool moved = false;
+ for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
+ {
+ if ( iFun < 0 )
+ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
+ else if ( _funs[ iFun ] == _smooFunction )
+ continue; // _smooFunction again
+ else if ( step > 1 )
+ newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
+ else
+ break; // let "easy" functions improve elements around distorted ones
+
+ if ( _curvature )
+ {
+ double delta = _curvature->lenDelta( _len );
+ if ( delta > 0 )
+ newPos += _normal * delta;
+ else
+ {
+ double segLen = _normal * ( newPos - prevPos );
+ if ( segLen + delta > 0 )
+ newPos += _normal * delta;
+ }
+ // double segLenChange = _normal * ( curPos - newPos );
+ // newPos += 0.5 * _normal * segLenChange;
+ }
+
+ int nbOkAfter = 0;
+ double minVolAfter = 1e100;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
+ minVolAfter = Min( minVolAfter, vol );
+ }
+ // get worse?
+ if ( nbOkAfter < nbOkBefore )
+ continue;
+
+ if (( findBest ) &&
+ ( nbOkAfter == nbOkBefore ) &&
+ ( minVolAfter <= minVolBefore ))
+ continue;
+
+ nbBad = _simplices.size() - nbOkAfter;
+ minVolBefore = minVolAfter;
+ nbOkBefore = nbOkAfter;
+ moved = true;
+
+ SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
+ n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
+ _pos.back() = newPos;
+
+ dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
+ << (nbBad ? " --BAD" : ""));
+
+ if ( iFun > -1 )
+ {
+ continue; // look for a better function
+ }
+
+ if ( !findBest )
+ break;
+
+ } // loop on smoothing functions
+
+ if ( moved ) // notify _neibors
+ {
+ Set( SMOOTHED );
+ for ( size_t i = 0; i < _neibors.size(); ++i )
+ if ( !_neibors[i]->Is( MOVED ))
+ {
+ _neibors[i]->Set( MOVED );
+ toSmooth.push_back( _neibors[i] );
+ }
+ }
+
+ return nbBad;
+}
+
+//================================================================================
+/*!
+ * \brief Perform 'smart' 3D smooth of nodes inflated from FACE
+ * \retval int - nb of bad simplices around this _LayerEdge
+ */
+//================================================================================
+
+int _LayerEdge::Smooth(const int step, const bool isConcaveFace, bool findBest )
+{
+ if ( !_smooFunction )
+ return 0; // _LayerEdge inflated along EDGE or FACE
+ if ( Is( BLOCKED ))
+ return 0; // not inflated
+
+ const gp_XYZ& curPos = _pos.back();
+ const gp_XYZ& prevPos = _pos[0]; //PrevCheckPos();
+
+ // quality metrics (orientation) of tetras around _tgtNode
+ int nbOkBefore = 0;
+ double vol, minVolBefore = 1e100;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ nbOkBefore += _simplices[i].IsForward( &prevPos, &curPos, vol );
+ minVolBefore = Min( minVolBefore, vol );
+ }
+ int nbBad = _simplices.size() - nbOkBefore;
+
+ if ( isConcaveFace ) // alternate FUN_CENTROIDAL and FUN_LAPLACIAN
+ {
+ if ( _smooFunction == _funs[ FUN_CENTROIDAL ] && step % 2 )
+ _smooFunction = _funs[ FUN_LAPLACIAN ];
+ else if ( _smooFunction == _funs[ FUN_LAPLACIAN ] && !( step % 2 ))
+ _smooFunction = _funs[ FUN_CENTROIDAL ];
+ }
+
+ // compute new position for the last _pos using different _funs
+ gp_XYZ newPos;
+ for ( int iFun = -1; iFun < theNbSmooFuns; ++iFun )
+ {
+ if ( iFun < 0 )
+ newPos = (this->*_smooFunction)(); // fun chosen by ChooseSmooFunction()
+ else if ( _funs[ iFun ] == _smooFunction )
+ continue; // _smooFunction again
+ else if ( step > 1 )
+ newPos = (this->*_funs[ iFun ])(); // try other smoothing fun
+ else
+ break; // let "easy" functions improve elements around distorted ones
+
+ if ( _curvature )
+ {
+ double delta = _curvature->lenDelta( _len );
+ if ( delta > 0 )
+ newPos += _normal * delta;
+ else
+ {
+ double segLen = _normal * ( newPos - prevPos );
+ if ( segLen + delta > 0 )
+ newPos += _normal * delta;
+ }
+ // double segLenChange = _normal * ( curPos - newPos );
+ // newPos += 0.5 * _normal * segLenChange;
+ }
+
+ int nbOkAfter = 0;
+ double minVolAfter = 1e100;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ nbOkAfter += _simplices[i].IsForward( &prevPos, &newPos, vol );
+ minVolAfter = Min( minVolAfter, vol );
+ }
+ // get worse?
+ if ( nbOkAfter < nbOkBefore )
+ continue;
+ if (( isConcaveFace || findBest ) &&
+ ( nbOkAfter == nbOkBefore ) &&
+ ( minVolAfter <= minVolBefore )
+ )
+ continue;
+
+ nbBad = _simplices.size() - nbOkAfter;
+ minVolBefore = minVolAfter;
+ nbOkBefore = nbOkAfter;
+
+ SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() );
+ n->setXYZ( newPos.X(), newPos.Y(), newPos.Z());
+ _pos.back() = newPos;
+
+ dumpMoveComm( n, SMESH_Comment( _funNames[ iFun < 0 ? smooFunID() : iFun ] )
+ << ( nbBad ? "--BAD" : ""));
+
+ // commented for IPAL0052478
+ // _len -= prevPos.Distance(SMESH_TNodeXYZ( n ));
+ // _len += prevPos.Distance(newPos);
+
+ if ( iFun > -1 ) // findBest || the chosen _fun makes worse
+ {
+ //_smooFunction = _funs[ iFun ];
+ // cout << "# " << _funNames[ iFun ] << "\t N:" << _nodes.back()->GetID()
+ // << "\t nbBad: " << _simplices.size() - nbOkAfter
+ // << " minVol: " << minVolAfter
+ // << " " << newPos.X() << " " << newPos.Y() << " " << newPos.Z()
+ // << endl;
+ continue; // look for a better function
+ }
+
+ if ( !findBest )
+ break;
+
+ } // loop on smoothing functions
+
+ return nbBad;
+}
+
+//================================================================================
+/*!
+ * \brief Chooses a smoothing technique giving a position most close to an initial one.
+ * For a correct result, _simplices must contain nodes lying on geometry.
+ */
+//================================================================================
+
+void _LayerEdge::ChooseSmooFunction( const set< TGeomID >& concaveVertices,
+ const TNode2Edge& n2eMap)
+{
+ if ( _smooFunction ) return;
+
+ // use smoothNefPolygon() near concaveVertices
+ if ( !concaveVertices.empty() )
+ {
+ _smooFunction = _funs[ FUN_CENTROIDAL ];
+
+ Set( ON_CONCAVE_FACE );
+
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ if ( concaveVertices.count( _simplices[i]._nPrev->getshapeId() ))
+ {
+ _smooFunction = _funs[ FUN_NEFPOLY ];
+
+ // set FUN_CENTROIDAL to neighbor edges
+ for ( i = 0; i < _neibors.size(); ++i )
+ {
+ if ( _neibors[i]->_nodes[0]->GetPosition()->GetDim() == 2 )
+ {
+ _neibors[i]->_smooFunction = _funs[ FUN_CENTROIDAL ];
+ }
+ }
+ return;
+ }
+ }
+
+ // // this choice is done only if ( !concaveVertices.empty() ) for Grids/smesh/bugs_19/X1
+ // // where the nodes are smoothed too far along a sphere thus creating
+ // // inverted _simplices
+ // double dist[theNbSmooFuns];
+ // //double coef[theNbSmooFuns] = { 1., 1.2, 1.4, 1.4 };
+ // double coef[theNbSmooFuns] = { 1., 1., 1., 1. };
+
+ // double minDist = Precision::Infinite();
+ // gp_Pnt p = SMESH_TNodeXYZ( _nodes[0] );
+ // for ( int i = 0; i < FUN_NEFPOLY; ++i )
+ // {
+ // gp_Pnt newP = (this->*_funs[i])();
+ // dist[i] = p.SquareDistance( newP );
+ // if ( dist[i]*coef[i] < minDist )
+ // {
+ // _smooFunction = _funs[i];
+ // minDist = dist[i]*coef[i];
+ // }
+ // }
+ }
+ else
+ {
+ _smooFunction = _funs[ FUN_LAPLACIAN ];
+ }
+ // int minDim = 3;
+ // for ( size_t i = 0; i < _simplices.size(); ++i )
+ // minDim = Min( minDim, _simplices[i]._nPrev->GetPosition()->GetDim() );
+ // if ( minDim == 0 )
+ // _smooFunction = _funs[ FUN_CENTROIDAL ];
+ // else if ( minDim == 1 )
+ // _smooFunction = _funs[ FUN_CENTROIDAL ];
+
+
+ // int iMin;
+ // for ( int i = 0; i < FUN_NB; ++i )
+ // {
+ // //cout << dist[i] << " ";
+ // if ( _smooFunction == _funs[i] ) {
+ // iMin = i;
+ // //debugMsg( fNames[i] );
+ // break;
+ // }
+ // }
+ // cout << _funNames[ iMin ] << "\t N:" << _nodes.back()->GetID() << endl;
+}
+
+//================================================================================
+/*!
+ * \brief Returns a name of _SmooFunction
+ */
+//================================================================================
+
+int _LayerEdge::smooFunID( _LayerEdge::PSmooFun fun) const
+{
+ if ( !fun )
+ fun = _smooFunction;
+ for ( int i = 0; i < theNbSmooFuns; ++i )
+ if ( fun == _funs[i] )
+ return i;
+
+ return theNbSmooFuns;
+}
+
+//================================================================================
+/*!
+ * \brief Computes a new node position using Laplacian smoothing
+ */
+//================================================================================
+
+gp_XYZ _LayerEdge::smoothLaplacian()
+{
+ gp_XYZ newPos (0,0,0);
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev );
+ newPos /= _simplices.size();
+
+ return newPos;
+}
+
+//================================================================================
+/*!
+ * \brief Computes a new node position using angular-based smoothing
+ */
+//================================================================================
+
+gp_XYZ _LayerEdge::smoothAngular()
+{
+ vector< gp_Vec > edgeDir; edgeDir. reserve( _simplices.size() + 1 );
+ vector< double > edgeSize; edgeSize.reserve( _simplices.size() );
+ vector< gp_XYZ > points; points. reserve( _simplices.size() + 1 );
+
+ gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
+ gp_XYZ pN( 0,0,0 );
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
+ edgeDir.push_back( p - pPrev );
+ edgeSize.push_back( edgeDir.back().Magnitude() );
+ if ( edgeSize.back() < numeric_limits<double>::min() )
+ {
+ edgeDir.pop_back();
+ edgeSize.pop_back();
+ }
+ else
+ {
+ edgeDir.back() /= edgeSize.back();
+ points.push_back( p );
+ pN += p;
+ }
+ pPrev = p;
+ }
+ edgeDir.push_back ( edgeDir[0] );
+ edgeSize.push_back( edgeSize[0] );
+ pN /= points.size();
+
+ gp_XYZ newPos(0,0,0);
+ double sumSize = 0;
+ for ( size_t i = 0; i < points.size(); ++i )
+ {
+ gp_Vec toN = pN - points[i];
+ double toNLen = toN.Magnitude();
+ if ( toNLen < numeric_limits<double>::min() )
+ {
+ newPos += pN;
+ continue;
+ }
+ gp_Vec bisec = edgeDir[i] + edgeDir[i+1];
+ double bisecLen = bisec.SquareMagnitude();
+ if ( bisecLen < numeric_limits<double>::min() )
+ {
+ gp_Vec norm = edgeDir[i] ^ toN;
+ bisec = norm ^ edgeDir[i];
+ bisecLen = bisec.SquareMagnitude();
+ }
+ bisecLen = Sqrt( bisecLen );
+ bisec /= bisecLen;
+
+#if 1
+ gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * bisecLen;
+ sumSize += bisecLen;
+#else
+ gp_XYZ pNew = ( points[i] + bisec.XYZ() * toNLen ) * ( edgeSize[i] + edgeSize[i+1] );
+ sumSize += ( edgeSize[i] + edgeSize[i+1] );
+#endif
+ newPos += pNew;
+ }
+ newPos /= sumSize;
+
+ // project newPos to an average plane
+
+ gp_XYZ norm(0,0,0); // plane normal
+ points.push_back( points[0] );
+ for ( size_t i = 1; i < points.size(); ++i )
+ {
+ gp_XYZ vec1 = points[ i-1 ] - pN;
+ gp_XYZ vec2 = points[ i ] - pN;
+ gp_XYZ cross = vec1 ^ vec2;
+ try {
+ cross.Normalize();
+ if ( cross * norm < numeric_limits<double>::min() )
+ norm += cross.Reversed();
+ else
+ norm += cross;
+ }
+ catch (Standard_Failure) { // if |cross| == 0.
+ }
+ }
+ gp_XYZ vec = newPos - pN;
+ double r = ( norm * vec ) / norm.SquareModulus(); // param [0,1] on norm
+ newPos = newPos - r * norm;
+
+ return newPos;
+}
+
+//================================================================================
+/*!
+ * \brief Computes a new node position using weighted node positions
+ */
+//================================================================================
+
+gp_XYZ _LayerEdge::smoothLengthWeighted()
+{
+ vector< double > edgeSize; edgeSize.reserve( _simplices.size() + 1);
+ vector< gp_XYZ > points; points. reserve( _simplices.size() );
+
+ gp_XYZ pPrev = SMESH_TNodeXYZ( _simplices.back()._nPrev );
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ gp_XYZ p = SMESH_TNodeXYZ( _simplices[i]._nPrev );
+ edgeSize.push_back( ( p - pPrev ).Modulus() );
+ if ( edgeSize.back() < numeric_limits<double>::min() )
+ {
+ edgeSize.pop_back();
+ }
+ else
+ {
+ points.push_back( p );
+ }
+ pPrev = p;
+ }
+ edgeSize.push_back( edgeSize[0] );
+
+ gp_XYZ newPos(0,0,0);
+ double sumSize = 0;
+ for ( size_t i = 0; i < points.size(); ++i )
+ {
+ newPos += points[i] * ( edgeSize[i] + edgeSize[i+1] );
+ sumSize += edgeSize[i] + edgeSize[i+1];
+ }
+ newPos /= sumSize;
+ return newPos;
+}
+
+//================================================================================
+/*!
+ * \brief Computes a new node position using angular-based smoothing
+ */
+//================================================================================
+
+gp_XYZ _LayerEdge::smoothCentroidal()
+{
+ gp_XYZ newPos(0,0,0);
+ gp_XYZ pN = SMESH_TNodeXYZ( _nodes.back() );
+ double sumSize = 0;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ gp_XYZ p1 = SMESH_TNodeXYZ( _simplices[i]._nPrev );
+ gp_XYZ p2 = SMESH_TNodeXYZ( _simplices[i]._nNext );
+ gp_XYZ gc = ( pN + p1 + p2 ) / 3.;
+ double size = (( p1 - pN ) ^ ( p2 - pN )).Modulus();
+
+ sumSize += size;
+ newPos += gc * size;
+ }
+ newPos /= sumSize;
+
+ return newPos;
+}
+
+//================================================================================
+/*!
+ * \brief Computes a new node position located inside a Nef polygon
+ */
+//================================================================================
+
+gp_XYZ _LayerEdge::smoothNefPolygon()
+#ifdef OLD_NEF_POLYGON
+{
+ gp_XYZ newPos(0,0,0);
+
+ // get a plane to search a solution on
+
+ vector< gp_XYZ > vecs( _simplices.size() + 1 );
+ size_t i;
+ const double tol = numeric_limits<double>::min();
+ gp_XYZ center(0,0,0);
+ for ( i = 0; i < _simplices.size(); ++i )
+ {
+ vecs[i] = ( SMESH_TNodeXYZ( _simplices[i]._nNext ) -
+ SMESH_TNodeXYZ( _simplices[i]._nPrev ));
+ center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
+ }
+ vecs.back() = vecs[0];
+ center /= _simplices.size();
+
+ gp_XYZ zAxis(0,0,0);
+ for ( i = 0; i < _simplices.size(); ++i )
+ zAxis += vecs[i] ^ vecs[i+1];
+
+ gp_XYZ yAxis;
+ for ( i = 0; i < _simplices.size(); ++i )
+ {
+ yAxis = vecs[i];
+ if ( yAxis.SquareModulus() > tol )
+ break;
+ }
+ gp_XYZ xAxis = yAxis ^ zAxis;
+ // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
+ // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
+ // p0.Distance( _simplices[2]._nPrev ));
+ // gp_XYZ center = smoothLaplacian();
+ // gp_XYZ xAxis, yAxis, zAxis;
+ // for ( i = 0; i < _simplices.size(); ++i )
+ // {
+ // xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
+ // if ( xAxis.SquareModulus() > tol*tol )
+ // break;
+ // }
+ // for ( i = 1; i < _simplices.size(); ++i )
+ // {
+ // yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
+ // zAxis = xAxis ^ yAxis;
+ // if ( zAxis.SquareModulus() > tol*tol )
+ // break;
+ // }
+ // if ( i == _simplices.size() ) return newPos;
+
+ yAxis = zAxis ^ xAxis;
+ xAxis /= xAxis.Modulus();
+ yAxis /= yAxis.Modulus();
+
+ // get half-planes of _simplices
+
+ vector< _halfPlane > halfPlns( _simplices.size() );
+ int nbHP = 0;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ gp_XYZ OP1 = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
+ gp_XYZ OP2 = SMESH_TNodeXYZ( _simplices[i]._nNext ) - center;
+ gp_XY p1( OP1 * xAxis, OP1 * yAxis );
+ gp_XY p2( OP2 * xAxis, OP2 * yAxis );
+ gp_XY vec12 = p2 - p1;
+ double dist12 = vec12.Modulus();
+ if ( dist12 < tol )
+ continue;
+ vec12 /= dist12;
+ halfPlns[ nbHP ]._pos = p1;
+ halfPlns[ nbHP ]._dir = vec12;
+ halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
+ ++nbHP;
+ }
+
+ // intersect boundaries of half-planes, define state of intersection points
+ // in relation to all half-planes and calculate internal point of a 2D polygon
+
+ double sumLen = 0;
+ gp_XY newPos2D (0,0);
+
+ enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
+ typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
+ TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
+
+ vector< vector< TIntPntState > > allIntPnts( nbHP );
+ for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
+ {
+ vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
+ if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
+
+ int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
+ int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
+
+ int nbNotOut = 0;
+ const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
+
+ for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
+ {
+ if ( iHP1 == iHP2 ) continue;
+
+ TIntPntState & ips1 = intPnts1[ iHP2 ];
+ if ( ips1.second == UNDEF )
+ {
+ // find an intersection point of boundaries of iHP1 and iHP2
+
+ if ( iHP2 == iPrev ) // intersection with neighbors is known
+ ips1.first = halfPlns[ iHP1 ]._pos;
+ else if ( iHP2 == iNext )
+ ips1.first = halfPlns[ iHP2 ]._pos;
+ else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
+ ips1.second = NO_INT;
+
+ // classify the found intersection point
+ if ( ips1.second != NO_INT )
+ {
+ ips1.second = NOT_OUT;
+ for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
+ if ( i != iHP1 && i != iHP2 &&
+ halfPlns[ i ].IsOut( ips1.first, tol ))
+ ips1.second = IS_OUT;
+ }
+ vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
+ if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
+ TIntPntState & ips2 = intPnts2[ iHP1 ];
+ ips2 = ips1;
+ }
+ if ( ips1.second == NOT_OUT )
+ {
+ ++nbNotOut;
+ segEnds[ bool(segEnds[0]) ] = & ips1.first;
+ }
+ }
+
+ // find a NOT_OUT segment of boundary which is located between
+ // two NOT_OUT int points
+
+ if ( nbNotOut < 2 )
+ continue; // no such a segment
+
+ if ( nbNotOut > 2 )
+ {
+ // sort points along the boundary
+ map< double, TIntPntState* > ipsByParam;
+ for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
+ {
+ TIntPntState & ips1 = intPnts1[ iHP2 ];
+ if ( ips1.second != NO_INT )
+ {
+ gp_XY op = ips1.first - halfPlns[ iHP1 ]._pos;
+ double param = op * halfPlns[ iHP1 ]._dir;
+ ipsByParam.insert( make_pair( param, & ips1 ));
+ }
+ }
+ // look for two neighboring NOT_OUT points
+ nbNotOut = 0;
+ map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
+ for ( ; u2ips != ipsByParam.end(); ++u2ips )
+ {
+ TIntPntState & ips1 = *(u2ips->second);
+ if ( ips1.second == NOT_OUT )
+ segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
+ else if ( nbNotOut >= 2 )
+ break;
+ else
+ nbNotOut = 0;
+ }
+ }
+
+ if ( nbNotOut >= 2 )
+ {
+ double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
+ sumLen += len;
+
+ newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
+ }
+ }
+
+ if ( sumLen > 0 )
+ {
+ newPos2D /= sumLen;
+ newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
+ }
+ else
+ {
+ newPos = center;
+ }
+
+ return newPos;
+}
+#else // OLD_NEF_POLYGON
+{ ////////////////////////////////// NEW
+ gp_XYZ newPos(0,0,0);
+
+ // get a plane to search a solution on
+
+ size_t i;
+ gp_XYZ center(0,0,0);
+ for ( i = 0; i < _simplices.size(); ++i )
+ center += SMESH_TNodeXYZ( _simplices[i]._nPrev );
+ center /= _simplices.size();
+
+ vector< gp_XYZ > vecs( _simplices.size() + 1 );
+ for ( i = 0; i < _simplices.size(); ++i )
+ vecs[i] = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
+ vecs.back() = vecs[0];
+
+ const double tol = numeric_limits<double>::min();
+ gp_XYZ zAxis(0,0,0);
+ for ( i = 0; i < _simplices.size(); ++i )
+ {
+ gp_XYZ cross = vecs[i] ^ vecs[i+1];
+ try {
+ cross.Normalize();
+ if ( cross * zAxis < tol )
+ zAxis += cross.Reversed();
+ else
+ zAxis += cross;
+ }
+ catch (Standard_Failure) { // if |cross| == 0.
+ }
+ }
+
+ gp_XYZ yAxis;
+ for ( i = 0; i < _simplices.size(); ++i )
+ {
+ yAxis = vecs[i];
+ if ( yAxis.SquareModulus() > tol )
+ break;
+ }
+ gp_XYZ xAxis = yAxis ^ zAxis;
+ // SMESH_TNodeXYZ p0( _simplices[0]._nPrev );
+ // const double tol = 1e-6 * ( p0.Distance( _simplices[1]._nPrev ) +
+ // p0.Distance( _simplices[2]._nPrev ));
+ // gp_XYZ center = smoothLaplacian();
+ // gp_XYZ xAxis, yAxis, zAxis;
+ // for ( i = 0; i < _simplices.size(); ++i )
+ // {
+ // xAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
+ // if ( xAxis.SquareModulus() > tol*tol )
+ // break;
+ // }
+ // for ( i = 1; i < _simplices.size(); ++i )
+ // {
+ // yAxis = SMESH_TNodeXYZ( _simplices[i]._nPrev ) - center;
+ // zAxis = xAxis ^ yAxis;
+ // if ( zAxis.SquareModulus() > tol*tol )
+ // break;
+ // }
+ // if ( i == _simplices.size() ) return newPos;
+
+ yAxis = zAxis ^ xAxis;
+ xAxis /= xAxis.Modulus();
+ yAxis /= yAxis.Modulus();
+
+ // get half-planes of _simplices
+
+ vector< _halfPlane > halfPlns( _simplices.size() );
+ int nbHP = 0;
+ for ( size_t i = 0; i < _simplices.size(); ++i )
+ {
+ const gp_XYZ& OP1 = vecs[ i ];
+ const gp_XYZ& OP2 = vecs[ i+1 ];
+ gp_XY p1( OP1 * xAxis, OP1 * yAxis );
+ gp_XY p2( OP2 * xAxis, OP2 * yAxis );
+ gp_XY vec12 = p2 - p1;
+ double dist12 = vec12.Modulus();
+ if ( dist12 < tol )
+ continue;
+ vec12 /= dist12;
+ halfPlns[ nbHP ]._pos = p1;
+ halfPlns[ nbHP ]._dir = vec12;
+ halfPlns[ nbHP ]._inNorm.SetCoord( -vec12.Y(), vec12.X() );
+ ++nbHP;
+ }
+
+ // intersect boundaries of half-planes, define state of intersection points
+ // in relation to all half-planes and calculate internal point of a 2D polygon
+
+ double sumLen = 0;
+ gp_XY newPos2D (0,0);
+
+ enum { UNDEF = -1, NOT_OUT, IS_OUT, NO_INT };
+ typedef std::pair< gp_XY, int > TIntPntState; // coord and isOut state
+ TIntPntState undefIPS( gp_XY(1e100,1e100), UNDEF );
+
+ vector< vector< TIntPntState > > allIntPnts( nbHP );
+ for ( int iHP1 = 0; iHP1 < nbHP; ++iHP1 )
+ {
+ vector< TIntPntState > & intPnts1 = allIntPnts[ iHP1 ];
+ if ( intPnts1.empty() ) intPnts1.resize( nbHP, undefIPS );
+
+ int iPrev = SMESH_MesherHelper::WrapIndex( iHP1 - 1, nbHP );
+ int iNext = SMESH_MesherHelper::WrapIndex( iHP1 + 1, nbHP );
+
+ int nbNotOut = 0;
+ const gp_XY* segEnds[2] = { 0, 0 }; // NOT_OUT points
+
+ for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
+ {
+ if ( iHP1 == iHP2 ) continue;
+
+ TIntPntState & ips1 = intPnts1[ iHP2 ];
+ if ( ips1.second == UNDEF )
+ {
+ // find an intersection point of boundaries of iHP1 and iHP2
+
+ if ( iHP2 == iPrev ) // intersection with neighbors is known
+ ips1.first = halfPlns[ iHP1 ]._pos;
+ else if ( iHP2 == iNext )
+ ips1.first = halfPlns[ iHP2 ]._pos;
+ else if ( !halfPlns[ iHP1 ].FindIntersection( halfPlns[ iHP2 ], ips1.first ))
+ ips1.second = NO_INT;
+
+ // classify the found intersection point
+ if ( ips1.second != NO_INT )
+ {
+ ips1.second = NOT_OUT;
+ for ( int i = 0; i < nbHP && ips1.second == NOT_OUT; ++i )
+ if ( i != iHP1 && i != iHP2 &&
+ halfPlns[ i ].IsOut( ips1.first, tol ))
+ ips1.second = IS_OUT;
+ }
+ vector< TIntPntState > & intPnts2 = allIntPnts[ iHP2 ];
+ if ( intPnts2.empty() ) intPnts2.resize( nbHP, undefIPS );
+ TIntPntState & ips2 = intPnts2[ iHP1 ];
+ ips2 = ips1;
+ }
+ if ( ips1.second == NOT_OUT )
+ {
+ ++nbNotOut;
+ segEnds[ bool(segEnds[0]) ] = & ips1.first;
+ }
+ }
+
+ // find a NOT_OUT segment of boundary which is located between
+ // two NOT_OUT int points
+
+ if ( nbNotOut < 2 )
+ continue; // no such a segment
+
+ if ( nbNotOut > 2 )
+ {
+ // sort points along the boundary
+ map< double, TIntPntState* > ipsByParam;
+ for ( int iHP2 = 0; iHP2 < nbHP; ++iHP2 )
+ {
+ TIntPntState & ips1 = intPnts1[ iHP2 ];
+ if ( ips1.second != NO_INT )
+ {
+ gp_XY op = ips1.first - halfPlns[ iHP1 ]._pos;
+ double param = op * halfPlns[ iHP1 ]._dir;
+ ipsByParam.insert( make_pair( param, & ips1 ));
+ }
+ }
+ // look for two neighboring NOT_OUT points
+ nbNotOut = 0;
+ map< double, TIntPntState* >::iterator u2ips = ipsByParam.begin();
+ for ( ; u2ips != ipsByParam.end(); ++u2ips )
+ {
+ TIntPntState & ips1 = *(u2ips->second);
+ if ( ips1.second == NOT_OUT )
+ segEnds[ bool( nbNotOut++ ) ] = & ips1.first;
+ else if ( nbNotOut >= 2 )
+ break;
+ else
+ nbNotOut = 0;
+ }
+ }
+
+ if ( nbNotOut >= 2 )
+ {
+ double len = ( *segEnds[0] - *segEnds[1] ).Modulus();
+ sumLen += len;
+
+ newPos2D += 0.5 * len * ( *segEnds[0] + *segEnds[1] );
+ }
+ }
+
+ if ( sumLen > 0 )
+ {
+ newPos2D /= sumLen;
+ newPos = center + xAxis * newPos2D.X() + yAxis * newPos2D.Y();
+ }
+ else
+ {
+ newPos = center;
+ }
+
+ return newPos;
+}
+#endif // OLD_NEF_POLYGON
+
+//================================================================================
+/*!
+ * \brief Add a new segment to _LayerEdge during inflation
+ */
+//================================================================================
+
+void _LayerEdge::SetNewLength( double len, _EdgesOnShape& eos, SMESH_MesherHelper& helper )
+{
+ if ( Is( BLOCKED ))
+ return;
+
+ if ( len > _maxLen )
+ {
+ len = _maxLen;
+ Block( eos.GetData() );
+ }
+ const double lenDelta = len - _len;
+ if ( lenDelta < len * 1e-3 )
+ {
+ Block( eos.GetData() );
+ return;
+ }
+
+ SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() );
+ gp_XYZ oldXYZ = SMESH_TNodeXYZ( n );
+ gp_XYZ newXYZ;
+ if ( eos._hyp.IsOffsetMethod() )
+ {
+ newXYZ = oldXYZ;
+ gp_Vec faceNorm;
+ SMDS_ElemIteratorPtr faceIt = _nodes[0]->GetInverseElementIterator( SMDSAbs_Face );
+ while ( faceIt->more() )
+ {
+ const SMDS_MeshElement* face = faceIt->next();
+ if ( !eos.GetNormal( face, faceNorm ))
+ continue;
+
+ // translate plane of a face
+ gp_XYZ baryCenter = oldXYZ + faceNorm.XYZ() * lenDelta;
+
+ // find point of intersection of the face plane located at baryCenter
+ // and _normal located at newXYZ
+ double d = -( faceNorm.XYZ() * baryCenter ); // d of plane equation ax+by+cz+d=0
+ double dot = ( faceNorm.XYZ() * _normal );
+ if ( dot < std::numeric_limits<double>::min() )
+ dot = lenDelta * 1e-3;
+ double step = -( faceNorm.XYZ() * newXYZ + d ) / dot;
+ newXYZ += step * _normal;
+ }
+ _lenFactor = _normal * ( newXYZ - oldXYZ ) / lenDelta; // _lenFactor is used in InvalidateStep()
+ }
+ else
+ {
+ newXYZ = oldXYZ + _normal * lenDelta * _lenFactor;
+ }
+
+ n->setXYZ( newXYZ.X(), newXYZ.Y(), newXYZ.Z() );
+ _pos.push_back( newXYZ );
+
+ if ( !eos._sWOL.IsNull() )
+ {
+ double distXYZ[4];
+ bool uvOK = false;
+ if ( eos.SWOLType() == TopAbs_EDGE )
+ {
+ double u = Precision::Infinite(); // to force projection w/o distance check
+ uvOK = helper.CheckNodeU( TopoDS::Edge( eos._sWOL ), n, u,
+ /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
+ _pos.back().SetCoord( u, 0, 0 );
+ if ( _nodes.size() > 1 && uvOK )
+ {
+ SMDS_EdgePositionPtr pos = n->GetPosition();
+ pos->SetUParameter( u );
+ }
+ }
+ else // TopAbs_FACE
+ {
+ gp_XY uv( Precision::Infinite(), 0 );
+ uvOK = helper.CheckNodeUV( TopoDS::Face( eos._sWOL ), n, uv,
+ /*tol=*/2*lenDelta, /*force=*/true, distXYZ );
+ _pos.back().SetCoord( uv.X(), uv.Y(), 0 );
+ if ( _nodes.size() > 1 && uvOK )
+ {
+ SMDS_FacePositionPtr pos = n->GetPosition();
+ pos->SetUParameter( uv.X() );
+ pos->SetVParameter( uv.Y() );
+ }
+ }
+ if ( uvOK )
+ {
+ n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]);
+ }
+ else
+ {
+ n->setXYZ( oldXYZ.X(), oldXYZ.Y(), oldXYZ.Z() );
+ _pos.pop_back();
+ Block( eos.GetData() );
+ return;
+ }
+ }
+
+ _len = len;
+
+ // notify _neibors
+ if ( eos.ShapeType() != TopAbs_FACE )
+ {
+ for ( size_t i = 0; i < _neibors.size(); ++i )
+ //if ( _len > _neibors[i]->GetSmooLen() )
+ _neibors[i]->Set( MOVED );
+
+ Set( MOVED );
+ }
+ dumpMove( n ); //debug
+}
+
+//================================================================================
+/*!
+ * \brief Set BLOCKED flag and propagate limited _maxLen to _neibors
+ */
+//================================================================================
+
+void _LayerEdge::Block( _SolidData& data )
+{
+ //if ( Is( BLOCKED )) return;
+ Set( BLOCKED );
+
+ SMESH_Comment msg( "#BLOCK shape=");
+ msg << data.GetShapeEdges( this )->_shapeID
+ << ", nodes " << _nodes[0]->GetID() << ", " << _nodes.back()->GetID();
+ dumpCmd( msg + " -- BEGIN");
+
+ SetMaxLen( _len );
+ std::queue<_LayerEdge*> queue;
+ queue.push( this );
+
+ gp_Pnt pSrc, pTgt, pSrcN, pTgtN;
+ while ( !queue.empty() )
+ {
+ _LayerEdge* edge = queue.front(); queue.pop();
+ pSrc = SMESH_TNodeXYZ( edge->_nodes[0] );
+ pTgt = SMESH_TNodeXYZ( edge->_nodes.back() );
+ for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
+ {
+ _LayerEdge* neibor = edge->_neibors[iN];
+ if ( neibor->_maxLen < edge->_maxLen * 1.01 )
+ continue;
+ pSrcN = SMESH_TNodeXYZ( neibor->_nodes[0] );
+ pTgtN = SMESH_TNodeXYZ( neibor->_nodes.back() );
+ double minDist = pSrc.SquareDistance( pSrcN );
+ minDist = Min( pTgt.SquareDistance( pTgtN ), minDist );
+ minDist = Min( pSrc.SquareDistance( pTgtN ), minDist );
+ minDist = Min( pTgt.SquareDistance( pSrcN ), minDist );
+ double newMaxLen = edge->_maxLen + 0.5 * Sqrt( minDist );
+ //if ( edge->_nodes[0]->getshapeId() == neibor->_nodes[0]->getshapeId() ) viscous_layers_00/A3
+ {
+ //newMaxLen *= edge->_lenFactor / neibor->_lenFactor;
+ // newMaxLen *= Min( edge->_lenFactor / neibor->_lenFactor,
+ // neibor->_lenFactor / edge->_lenFactor );
+ }
+ if ( neibor->_maxLen > newMaxLen )
+ {
+ neibor->SetMaxLen( newMaxLen );
+ if ( neibor->_maxLen < neibor->_len )
+ {
+ _EdgesOnShape* eos = data.GetShapeEdges( neibor );
+ int lastStep = neibor->Is( BLOCKED ) ? 1 : 0;
+ while ( neibor->_len > neibor->_maxLen &&
+ neibor->NbSteps() > lastStep )
+ neibor->InvalidateStep( neibor->NbSteps(), *eos, /*restoreLength=*/true );
+ neibor->SetNewLength( neibor->_maxLen, *eos, data.GetHelper() );
+ //neibor->Block( data );