+ * \brief Copy data from a _LayerEdge of other SOLID and based on the same node;
+ * this and the other _LayerEdge are inflated along a FACE or an EDGE
+ */
+//================================================================================
+
+gp_XYZ _LayerEdge::Copy( _LayerEdge& other,
+ _EdgesOnShape& eos,
+ SMESH_MesherHelper& helper )
+{
+ _nodes = other._nodes;
+ _normal = other._normal;
+ _len = 0;
+ _lenFactor = other._lenFactor;
+ _cosin = other._cosin;
+ _2neibors = other._2neibors;
+ _curvature = 0; std::swap( _curvature, other._curvature );
+ _2neibors = 0; std::swap( _2neibors, other._2neibors );
+
+ gp_XYZ lastPos( 0,0,0 );
+ if ( eos.SWOLType() == TopAbs_EDGE )
+ {
+ double u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes[0] );
+ _pos.push_back( gp_XYZ( u, 0, 0));
+
+ u = helper.GetNodeU( TopoDS::Edge( eos._sWOL ), _nodes.back() );
+ lastPos.SetX( u );
+ }
+ else // TopAbs_FACE
+ {
+ gp_XY uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes[0]);
+ _pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0));
+
+ uv = helper.GetNodeUV( TopoDS::Face( eos._sWOL ), _nodes.back() );
+ lastPos.SetX( uv.X() );
+ lastPos.SetY( uv.Y() );
+ }
+ return lastPos;
+}
+
+//================================================================================
+/*!
+ * \brief Set _cosin and _lenFactor
+ */
+//================================================================================
+
+void _LayerEdge::SetCosin( double cosin )
+{
+ _cosin = cosin;
+ cosin = Abs( _cosin );
+ //_lenFactor = ( cosin < 1.-1e-12 ) ? Min( 2., 1./sqrt(1-cosin*cosin )) : 1.0;
+ _lenFactor = ( cosin < 1.-1e-12 ) ? 1./sqrt(1-cosin*cosin ) : 1.0;
+}
+
+//================================================================================
+/*!
+ * \brief Check if another _LayerEdge is a neighbor on EDGE
+ */
+//================================================================================
+
+bool _LayerEdge::IsNeiborOnEdge( const _LayerEdge* edge ) const
+{
+ return (( this->_2neibors && this->_2neibors->include( edge )) ||
+ ( edge->_2neibors && edge->_2neibors->include( this )));
+}
+
+//================================================================================
+/*!
+ * \brief Fills a vector<_Simplex >
+ */
+//================================================================================
+
+void _Simplex::GetSimplices( const SMDS_MeshNode* node,
+ vector<_Simplex>& simplices,
+ const set<TGeomID>& ingnoreShapes,
+ const _SolidData* dataToCheckOri,
+ const bool toSort)
+{
+ simplices.clear();
+ SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face);
+ while ( fIt->more() )
+ {
+ const SMDS_MeshElement* f = fIt->next();
+ const TGeomID shapeInd = f->getshapeId();
+ if ( ingnoreShapes.count( shapeInd )) continue;
+ const int nbNodes = f->NbCornerNodes();
+ const int srcInd = f->GetNodeIndex( node );
+ const SMDS_MeshNode* nPrev = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd-1, nbNodes ));
+ const SMDS_MeshNode* nNext = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+1, nbNodes ));
+ const SMDS_MeshNode* nOpp = f->GetNode( SMESH_MesherHelper::WrapIndex( srcInd+2, nbNodes ));
+ if ( dataToCheckOri && dataToCheckOri->_reversedFaceIds.count( shapeInd ))
+ std::swap( nPrev, nNext );
+ simplices.push_back( _Simplex( nPrev, nNext, ( nbNodes == 3 ? 0 : nOpp )));
+ }
+
+ if ( toSort )
+ SortSimplices( simplices );
+}
+
+//================================================================================
+/*!
+ * \brief Set neighbor simplices side by side
+ */
+//================================================================================
+
+void _Simplex::SortSimplices(vector<_Simplex>& simplices)
+{
+ vector<_Simplex> sortedSimplices( simplices.size() );
+ sortedSimplices[0] = simplices[0];
+ size_t nbFound = 0;
+ for ( size_t i = 1; i < simplices.size(); ++i )
+ {
+ for ( size_t j = 1; j < simplices.size(); ++j )
+ if ( sortedSimplices[i-1]._nNext == simplices[j]._nPrev )
+ {
+ sortedSimplices[i] = simplices[j];
+ nbFound++;
+ break;
+ }
+ }
+ if ( nbFound == simplices.size() - 1 )
+ simplices.swap( sortedSimplices );
+}
+
+//================================================================================
+/*!
+ * \brief DEBUG. Create groups contating temorary data of _LayerEdge's
+ */
+//================================================================================
+
+void _ViscousBuilder::makeGroupOfLE()
+{
+#ifdef _DEBUG_
+ for ( size_t i = 0 ; i < _sdVec.size(); ++i )
+ {
+ if ( _sdVec[i]._n2eMap.empty() ) continue;
+
+ dumpFunction( SMESH_Comment("make_LayerEdge_") << i );
+ TNode2Edge::iterator n2e;
+ for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
+ {
+ _LayerEdge* le = n2e->second;
+ // for ( size_t iN = 1; iN < le->_nodes.size(); ++iN )
+ // dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[iN-1]->GetID()
+ // << ", " << le->_nodes[iN]->GetID() <<"])");
+ if ( le ) {
+ dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <<le->_nodes[0]->GetID()
+ << ", " << le->_nodes.back()->GetID() <<"]) # " << le->_flags );
+ }
+ }
+ dumpFunctionEnd();
+
+ dumpFunction( SMESH_Comment("makeNormals") << i );
+ for ( n2e = _sdVec[i]._n2eMap.begin(); n2e != _sdVec[i]._n2eMap.end(); ++n2e )
+ {
+ _LayerEdge* edge = n2e->second;
+ SMESH_TNodeXYZ nXYZ( edge->_nodes[0] );
+ nXYZ += edge->_normal * _sdVec[i]._stepSize;
+ dumpCmd(SMESH_Comment("mesh.AddEdge([ ") << edge->_nodes[0]->GetID()
+ << ", mesh.AddNode( "<< nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])");
+ }
+ dumpFunctionEnd();
+
+ dumpFunction( SMESH_Comment("makeTmpFaces_") << i );
+ dumpCmd( "faceId1 = mesh.NbElements()" );
+ TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE );
+ for ( ; fExp.More(); fExp.Next() )
+ {
+ if ( const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current() ))
+ {
+ if ( sm->NbElements() == 0 ) continue;
+ SMDS_ElemIteratorPtr fIt = sm->GetElements();
+ while ( fIt->more())
+ {
+ const SMDS_MeshElement* e = fIt->next();
+ SMESH_Comment cmd("mesh.AddFace([");
+ for ( int j = 0; j < e->NbCornerNodes(); ++j )
+ cmd << e->GetNode(j)->GetID() << (j+1 < e->NbCornerNodes() ? ",": "])");
+ dumpCmd( cmd );
+ }
+ }
+ }
+ dumpCmd( "faceId2 = mesh.NbElements()" );
+ dumpCmd( SMESH_Comment( "mesh.MakeGroup( 'tmpFaces_" ) << i << "',"
+ << "SMESH.FACE, SMESH.FT_RangeOfIds,'=',"
+ << "'%s-%s' % (faceId1+1, faceId2))");
+ dumpFunctionEnd();
+ }
+#endif
+}
+
+//================================================================================
+/*!
+ * \brief Find maximal _LayerEdge length (layer thickness) limited by geometry
+ */
+//================================================================================
+
+void _ViscousBuilder::computeGeomSize( _SolidData& data )
+{
+ data._geomSize = Precision::Infinite();
+ double intersecDist;
+ const SMDS_MeshElement* face;
+ SMESH_MesherHelper helper( *_mesh );
+
+ SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
+ ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
+ data._proxyMesh->GetFaces( data._solid )));
+
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( eos._edges.empty() )
+ continue;
+ // get neighbor faces, intersection with which should not be considered since
+ // collisions are avoided by means of smoothing
+ set< TGeomID > neighborFaces;
+ if ( eos._hyp.ToSmooth() )
+ {
+ SMESH_subMeshIteratorPtr subIt =
+ eos._subMesh->getDependsOnIterator(/*includeSelf=*/eos.ShapeType() != TopAbs_FACE );
+ while ( subIt->more() )
+ {
+ SMESH_subMesh* sm = subIt->next();
+ PShapeIteratorPtr fIt = helper.GetAncestors( sm->GetSubShape(), *_mesh, TopAbs_FACE );
+ while ( const TopoDS_Shape* face = fIt->next() )
+ neighborFaces.insert( getMeshDS()->ShapeToIndex( *face ));
+ }
+ }
+ // find intersections
+ double thinkness = eos._hyp.GetTotalThickness();
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
+ eos._edges[i]->SetMaxLen( thinkness );
+ eos._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon, eos, &face );
+ if ( intersecDist > 0 && face )
+ {
+ data._geomSize = Min( data._geomSize, intersecDist );
+ if ( !neighborFaces.count( face->getshapeId() ))
+ eos[i]->SetMaxLen( Min( thinkness, intersecDist / ( face->GetID() < 0 ? 3. : 2. )));
+ }
+ }
+ }
+
+ data._maxThickness = 0;
+ data._minThickness = 1e100;
+ list< const StdMeshers_ViscousLayers* >::iterator hyp = data._hyps.begin();
+ for ( ; hyp != data._hyps.end(); ++hyp )
+ {
+ data._maxThickness = Max( data._maxThickness, (*hyp)->GetTotalThickness() );
+ data._minThickness = Min( data._minThickness, (*hyp)->GetTotalThickness() );
+ }
+
+ // Limit inflation step size by geometry size found by intersecting
+ // normals of _LayerEdge's with mesh faces
+ if ( data._stepSize > 0.3 * data._geomSize )
+ limitStepSize( data, 0.3 * data._geomSize );
+
+ if ( data._stepSize > data._minThickness )
+ limitStepSize( data, data._minThickness );
+
+
+ // -------------------------------------------------------------------------
+ // Detect _LayerEdge which can't intersect with opposite or neighbor layer,
+ // so no need in detecting intersection at each inflation step
+ // -------------------------------------------------------------------------
+
+ int nbSteps = data._maxThickness / data._stepSize;
+ if ( nbSteps < 3 || nbSteps * data._n2eMap.size() < 100000 )
+ return;
+
+ vector< const SMDS_MeshElement* > closeFaces;
+ int nbDetected = 0;
+
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( eos._edges.empty() || eos.ShapeType() != TopAbs_FACE )
+ continue;
+
+ for ( size_t i = 0; i < eos.size(); ++i )
+ {
+ SMESH_NodeXYZ p( eos[i]->_nodes[0] );
+ double radius = data._maxThickness + 2 * eos[i]->_maxLen;
+ closeFaces.clear();
+ searcher->GetElementsInSphere( p, radius, SMDSAbs_Face, closeFaces );
+
+ bool toIgnore = true;
+ for ( size_t iF = 0; iF < closeFaces.size() && toIgnore; ++iF )
+ if ( !( toIgnore = ( closeFaces[ iF ]->getshapeId() == eos._shapeID ||
+ data._ignoreFaceIds.count( closeFaces[ iF ]->getshapeId() ))))
+ {
+ // check if a _LayerEdge will inflate in a direction opposite to a direction
+ // toward a close face
+ bool allBehind = true;
+ for ( int iN = 0; iN < closeFaces[ iF ]->NbCornerNodes() && allBehind; ++iN )
+ {
+ SMESH_NodeXYZ pi( closeFaces[ iF ]->GetNode( iN ));
+ allBehind = (( pi - p ) * eos[i]->_normal < 0.1 * data._stepSize );
+ }
+ toIgnore = allBehind;
+ }
+
+
+ if ( toIgnore ) // no need to detect intersection
+ {
+ eos[i]->Set( _LayerEdge::INTERSECTED );
+ ++nbDetected;
+ }
+ }
+ }
+
+ debugMsg( "Nb LE to intersect " << data._n2eMap.size()-nbDetected << ", ignore " << nbDetected );
+
+ return;
+}
+
+//================================================================================
+/*!
+ * \brief Increase length of _LayerEdge's to reach the required thickness of layers
+ */
+//================================================================================
+
+bool _ViscousBuilder::inflate(_SolidData& data)
+{
+ SMESH_MesherHelper helper( *_mesh );
+
+ const double tgtThick = data._maxThickness;
+
+ if ( data._stepSize < 1. )
+ data._epsilon = data._stepSize * 1e-7;
+
+ debugMsg( "-- geomSize = " << data._geomSize << ", stepSize = " << data._stepSize );
+ _pyDump->Pause();
+
+ findCollisionEdges( data, helper );
+
+ limitMaxLenByCurvature( data, helper );
+
+ _pyDump->Resume();
+
+ // limit length of _LayerEdge's around MULTI_NORMAL _LayerEdge's
+ for ( size_t i = 0; i < data._edgesOnShape.size(); ++i )
+ if ( data._edgesOnShape[i].ShapeType() == TopAbs_VERTEX &&
+ data._edgesOnShape[i]._edges.size() > 0 &&
+ data._edgesOnShape[i]._edges[0]->Is( _LayerEdge::MULTI_NORMAL ))
+ {
+ data._edgesOnShape[i]._edges[0]->Unset( _LayerEdge::BLOCKED );
+ data._edgesOnShape[i]._edges[0]->Block( data );
+ }
+
+ const double safeFactor = ( 2*data._maxThickness < data._geomSize ) ? 1 : theThickToIntersection;
+
+ double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite();
+ int nbSteps = 0, nbRepeats = 0;
+ while ( avgThick < 0.99 )
+ {
+ // new target length
+ double prevThick = curThick;
+ curThick += data._stepSize;
+ if ( curThick > tgtThick )
+ {
+ curThick = tgtThick + tgtThick*( 1.-avgThick ) * nbRepeats;
+ nbRepeats++;
+ }
+
+ double stepSize = curThick - prevThick;
+ updateNormalsOfSmoothed( data, helper, nbSteps, stepSize ); // to ease smoothing
+
+ // Elongate _LayerEdge's
+ dumpFunction(SMESH_Comment("inflate")<<data._index<<"_step"<<nbSteps); // debug
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[iS];
+ if ( eos._edges.empty() ) continue;
+
+ const double shapeCurThick = Min( curThick, eos._hyp.GetTotalThickness() );
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ eos._edges[i]->SetNewLength( shapeCurThick, eos, helper );
+ }
+ }
+ dumpFunctionEnd();
+
+ if ( !updateNormals( data, helper, nbSteps, stepSize )) // to avoid collisions
+ return false;
+
+ // Improve and check quality
+ if ( !smoothAndCheck( data, nbSteps, distToIntersection ))
+ {
+ if ( nbSteps > 0 )
+ {
+#ifdef __NOT_INVALIDATE_BAD_SMOOTH
+ debugMsg("NOT INVALIDATED STEP!");
+ return error("Smoothing failed", data._index);
+#endif
+ dumpFunction(SMESH_Comment("invalidate")<<data._index<<"_step"<<nbSteps); // debug
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[iS];
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ eos._edges[i]->InvalidateStep( nbSteps+1, eos );
+ }
+ dumpFunctionEnd();
+ }
+ break; // no more inflating possible
+ }
+ nbSteps++;
+
+ // Evaluate achieved thickness
+ avgThick = 0;
+ int nbActiveEdges = 0;
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[iS];
+ if ( eos._edges.empty() ) continue;
+
+ const double shapeTgtThick = eos._hyp.GetTotalThickness();
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ if ( eos._edges[i]->_nodes.size() > 1 )
+ avgThick += Min( 1., eos._edges[i]->_len / shapeTgtThick );
+ else
+ avgThick += shapeTgtThick;
+ nbActiveEdges += ( ! eos._edges[i]->Is( _LayerEdge::BLOCKED ));
+ }
+ }
+ avgThick /= data._n2eMap.size();
+ debugMsg( "-- Thickness " << curThick << " ("<< avgThick*100 << "%) reached" );
+
+#ifdef BLOCK_INFLATION
+ if ( nbActiveEdges == 0 )
+ {
+ debugMsg( "-- Stop inflation since all _LayerEdge's BLOCKED " );
+ break;
+ }
+#else
+ if ( distToIntersection < tgtThick * avgThick * safeFactor && avgThick < 0.9 )
+ {
+ debugMsg( "-- Stop inflation since "
+ << " distToIntersection( "<<distToIntersection<<" ) < avgThick( "
+ << tgtThick * avgThick << " ) * " << safeFactor );
+ break;
+ }
+#endif
+
+ // new step size
+ limitStepSize( data, 0.25 * distToIntersection );
+ if ( data._stepSizeNodes[0] )
+ data._stepSize = data._stepSizeCoeff *
+ SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]);
+
+ } // while ( avgThick < 0.99 )
+
+ if ( nbSteps == 0 )
+ return error("failed at the very first inflation step", data._index);
+
+ if ( avgThick < 0.99 )
+ {
+ if ( !data._proxyMesh->_warning || data._proxyMesh->_warning->IsOK() )
+ {
+ data._proxyMesh->_warning.reset
+ ( new SMESH_ComputeError (COMPERR_WARNING,
+ SMESH_Comment("Thickness ") << tgtThick <<
+ " of viscous layers not reached,"
+ " average reached thickness is " << avgThick*tgtThick));
+ }
+ }
+
+ // Restore position of src nodes moved by inflation on _noShrinkShapes
+ dumpFunction(SMESH_Comment("restoNoShrink_So")<<data._index); // debug
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[iS];
+ if ( !eos._edges.empty() && eos._edges[0]->_nodes.size() == 1 )
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ restoreNoShrink( *eos._edges[ i ] );
+ }
+ }
+ dumpFunctionEnd();
+
+ return safeFactor > 0; // == true (avoid warning: unused variable 'safeFactor')
+}
+
+//================================================================================
+/*!
+ * \brief Improve quality of layer inner surface and check intersection
+ */
+//================================================================================
+
+bool _ViscousBuilder::smoothAndCheck(_SolidData& data,
+ const int infStep,
+ double & distToIntersection)
+{
+ if ( data._nbShapesToSmooth == 0 )
+ return true; // no shapes needing smoothing
+
+ bool moved, improved;
+ double vol;
+ vector< _LayerEdge* > movedEdges, badEdges;
+ vector< _EdgesOnShape* > eosC1; // C1 continues shapes
+ vector< bool > isConcaveFace;
+
+ SMESH_MesherHelper helper(*_mesh);
+ Handle(ShapeAnalysis_Surface) surface;
+ TopoDS_Face F;
+
+ for ( int isFace = 0; isFace < 2; ++isFace ) // smooth on [ EDGEs, FACEs ]
+ {
+ const TopAbs_ShapeEnum shapeType = isFace ? TopAbs_FACE : TopAbs_EDGE;
+
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( !eos._toSmooth ||
+ eos.ShapeType() != shapeType ||
+ eos._edges.empty() )
+ continue;
+
+ // already smoothed?
+ // bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= infStep+1 );
+ // if ( !toSmooth ) continue;
+
+ if ( !eos._hyp.ToSmooth() )
+ {
+ // smooth disabled by the user; check validy only
+ if ( !isFace ) continue;
+ badEdges.clear();
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* edge = eos._edges[i];
+ for ( size_t iF = 0; iF < edge->_simplices.size(); ++iF )
+ if ( !edge->_simplices[iF].IsForward( edge->_nodes[0], edge->_pos.back(), vol ))
+ {
+ // debugMsg( "-- Stop inflation. Bad simplex ("
+ // << " "<< edge->_nodes[0]->GetID()
+ // << " "<< edge->_nodes.back()->GetID()
+ // << " "<< edge->_simplices[iF]._nPrev->GetID()
+ // << " "<< edge->_simplices[iF]._nNext->GetID() << " ) ");
+ // return false;
+ badEdges.push_back( edge );
+ }
+ }
+ if ( !badEdges.empty() )
+ {
+ eosC1.resize(1);
+ eosC1[0] = &eos;
+ int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
+ if ( nbBad > 0 )
+ return false;
+ }
+ continue; // goto the next EDGE or FACE
+ }
+
+ // prepare data
+ if ( eos.SWOLType() == TopAbs_FACE )
+ {
+ if ( !F.IsSame( eos._sWOL )) {
+ F = TopoDS::Face( eos._sWOL );
+ helper.SetSubShape( F );
+ surface = helper.GetSurface( F );
+ }
+ }
+ else
+ {
+ F.Nullify(); surface.Nullify();
+ }
+ const TGeomID sInd = eos._shapeID;
+
+ // perform smoothing
+
+ if ( eos.ShapeType() == TopAbs_EDGE )
+ {
+ dumpFunction(SMESH_Comment("smooth")<<data._index << "_Ed"<<sInd <<"_InfStep"<<infStep);
+
+ if ( !eos._edgeSmoother->Perform( data, surface, F, helper ))
+ {
+ // smooth on EDGE's (normally we should not get here)
+ int step = 0;
+ do {
+ moved = false;
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ moved |= eos._edges[i]->SmoothOnEdge( surface, F, helper );
+ }
+ dumpCmd( SMESH_Comment("# end step ")<<step);
+ }
+ while ( moved && step++ < 5 );
+ }
+ dumpFunctionEnd();
+ }
+
+ else // smooth on FACE
+ {
+ eosC1.clear();
+ eosC1.push_back( & eos );
+ eosC1.insert( eosC1.end(), eos._eosC1.begin(), eos._eosC1.end() );
+
+ movedEdges.clear();
+ isConcaveFace.resize( eosC1.size() );
+ for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
+ {
+ isConcaveFace[ iEOS ] = data._concaveFaces.count( eosC1[ iEOS ]->_shapeID );
+ vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
+ for ( size_t i = 0; i < edges.size(); ++i )
+ if ( edges[i]->Is( _LayerEdge::MOVED ) ||
+ edges[i]->Is( _LayerEdge::NEAR_BOUNDARY ))
+ movedEdges.push_back( edges[i] );
+
+ makeOffsetSurface( *eosC1[ iEOS ], helper );
+ }
+
+ int step = 0, stepLimit = 5, nbBad = 0;
+ while (( ++step <= stepLimit ) || improved )
+ {
+ dumpFunction(SMESH_Comment("smooth")<<data._index<<"_Fa"<<sInd
+ <<"_InfStep"<<infStep<<"_"<<step); // debug
+ int oldBadNb = nbBad;
+ badEdges.clear();
+
+#ifdef INCREMENTAL_SMOOTH
+ bool findBest = false; // ( step == stepLimit );
+ for ( size_t i = 0; i < movedEdges.size(); ++i )
+ {
+ movedEdges[i]->Unset( _LayerEdge::SMOOTHED );
+ if ( movedEdges[i]->Smooth( step, findBest, movedEdges ) > 0 )
+ badEdges.push_back( movedEdges[i] );
+ }
+#else
+ bool findBest = ( step == stepLimit || isConcaveFace[ iEOS ]);
+ for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
+ {
+ vector< _LayerEdge* > & edges = eosC1[ iEOS ]->_edges;
+ for ( size_t i = 0; i < edges.size(); ++i )
+ {
+ edges[i]->Unset( _LayerEdge::SMOOTHED );
+ if ( edges[i]->Smooth( step, findBest, false ) > 0 )
+ badEdges.push_back( eos._edges[i] );
+ }
+ }
+#endif
+ nbBad = badEdges.size();
+
+ if ( nbBad > 0 )
+ debugMsg(SMESH_Comment("nbBad = ") << nbBad );
+
+ if ( !badEdges.empty() && step >= stepLimit / 2 )
+ {
+ if ( badEdges[0]->Is( _LayerEdge::ON_CONCAVE_FACE ))
+ stepLimit = 9;
+
+ // resolve hard smoothing situation around concave VERTEXes
+ for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
+ {
+ vector< _EdgesOnShape* > & eosCoVe = eosC1[ iEOS ]->_eosConcaVer;
+ for ( size_t i = 0; i < eosCoVe.size(); ++i )
+ eosCoVe[i]->_edges[0]->MoveNearConcaVer( eosCoVe[i], eosC1[ iEOS ],
+ step, badEdges );
+ }
+ // look for the best smooth of _LayerEdge's neighboring badEdges
+ nbBad = 0;
+ for ( size_t i = 0; i < badEdges.size(); ++i )
+ {
+ _LayerEdge* ledge = badEdges[i];
+ for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
+ {
+ ledge->_neibors[iN]->Unset( _LayerEdge::SMOOTHED );
+ nbBad += ledge->_neibors[iN]->Smooth( step, true, /*findBest=*/true );
+ }
+ ledge->Unset( _LayerEdge::SMOOTHED );
+ nbBad += ledge->Smooth( step, true, /*findBest=*/true );
+ }
+ debugMsg(SMESH_Comment("nbBad = ") << nbBad );
+ }
+
+ if ( nbBad == oldBadNb &&
+ nbBad > 0 &&
+ step < stepLimit ) // smooth w/o chech of validity
+ {
+ dumpFunctionEnd();
+ dumpFunction(SMESH_Comment("smoothWoCheck")<<data._index<<"_Fa"<<sInd
+ <<"_InfStep"<<infStep<<"_"<<step); // debug
+ for ( size_t i = 0; i < movedEdges.size(); ++i )
+ {
+ movedEdges[i]->SmoothWoCheck();
+ }
+ if ( stepLimit < 9 )
+ stepLimit++;
+ }
+
+ improved = ( nbBad < oldBadNb );
+
+ dumpFunctionEnd();
+
+ if (( step % 3 == 1 ) || ( nbBad > 0 && step >= stepLimit / 2 ))
+ for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
+ {
+ putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1, step, /*moveAll=*/step == 1 );
+ }
+
+ } // smoothing steps
+
+ // project -- to prevent intersections or fix bad simplices
+ for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
+ {
+ if ( ! eosC1[ iEOS ]->_eosConcaVer.empty() || nbBad > 0 )
+ putOnOffsetSurface( *eosC1[ iEOS ], infStep, eosC1 );
+ }
+
+ //if ( !badEdges.empty() )
+ {
+ badEdges.clear();
+ for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
+ {
+ for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
+ {
+ if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
+
+ _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
+ edge->CheckNeiborsOnBoundary( & badEdges );
+ if (( nbBad > 0 ) ||
+ ( edge->Is( _LayerEdge::BLOCKED ) && edge->Is( _LayerEdge::NEAR_BOUNDARY )))
+ {
+ SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
+ gp_XYZ prevXYZ = edge->PrevCheckPos();
+ for ( size_t j = 0; j < edge->_simplices.size(); ++j )
+ if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
+ {
+ debugMsg("Bad simplex ( " << edge->_nodes[0]->GetID()
+ << " "<< tgtXYZ._node->GetID()
+ << " "<< edge->_simplices[j]._nPrev->GetID()
+ << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
+ badEdges.push_back( edge );
+ break;
+ }
+ }
+ }
+ }
+
+ // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
+ nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
+
+ if ( nbBad > 0 )
+ return false;
+ }
+
+ } // // smooth on FACE's
+ } // loop on shapes
+ } // smooth on [ EDGEs, FACEs ]
+
+ // Check orientation of simplices of _LayerEdge's on EDGEs and VERTEXes
+ eosC1.resize(1);
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( eos.ShapeType() == TopAbs_FACE ||
+ eos._edges.empty() ||
+ !eos._sWOL.IsNull() )
+ continue;
+
+ badEdges.clear();
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* edge = eos._edges[i];
+ if ( edge->_nodes.size() < 2 ) continue;
+ SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
+ //SMESH_TNodeXYZ prevXYZ = edge->_nodes[0];
+ gp_XYZ prevXYZ = edge->PrevCheckPos( &eos );
+ //const gp_XYZ& prevXYZ = edge->PrevPos();
+ for ( size_t j = 0; j < edge->_simplices.size(); ++j )
+ if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
+ {
+ debugMsg("Bad simplex on bnd ( " << edge->_nodes[0]->GetID()
+ << " "<< tgtXYZ._node->GetID()
+ << " "<< edge->_simplices[j]._nPrev->GetID()
+ << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
+ badEdges.push_back( edge );
+ break;
+ }
+ }
+
+ // try to fix bad simplices by removing the last inflation step of some _LayerEdge's
+ eosC1[0] = &eos;
+ int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
+ if ( nbBad > 0 )
+ return false;
+ }
+
+
+ // Check if the last segments of _LayerEdge intersects 2D elements;
+ // checked elements are either temporary faces or faces on surfaces w/o the layers
+
+ SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
+ ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(),
+ data._proxyMesh->GetFaces( data._solid )) );
+
+#ifdef BLOCK_INFLATION
+ const bool toBlockInfaltion = true;
+#else
+ const bool toBlockInfaltion = false;
+#endif
+ distToIntersection = Precision::Infinite();
+ double dist;
+ const SMDS_MeshElement* intFace = 0;
+ const SMDS_MeshElement* closestFace = 0;
+ _LayerEdge* le = 0;
+ bool is1stBlocked = true; // dbg
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( eos._edges.empty() || !eos._sWOL.IsNull() )
+ continue;
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ if ( eos._edges[i]->Is( _LayerEdge::INTERSECTED ) ||
+ eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL ))
+ continue;
+ if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
+ {
+ return false;
+ // commented due to "Illegal hash-positionPosition" error in NETGEN
+ // on Debian60 on viscous_layers_01/B2 case
+ // Collision; try to deflate _LayerEdge's causing it
+ // badEdges.clear();
+ // badEdges.push_back( eos._edges[i] );
+ // eosC1[0] = & eos;
+ // int nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
+ // if ( nbBad > 0 )
+ // return false;
+
+ // badEdges.clear();
+ // if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
+ // {
+ // if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
+ // {
+ // const SMDS_MeshElement* srcFace =
+ // eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
+ // SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
+ // while ( nIt->more() )
+ // {
+ // const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
+ // TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
+ // if ( n2e != data._n2eMap.end() )
+ // badEdges.push_back( n2e->second );
+ // }
+ // eosC1[0] = eof;
+ // nbBad = invalidateBadSmooth( data, helper, badEdges, eosC1, infStep );
+ // if ( nbBad > 0 )
+ // return false;
+ // }
+ // }
+ // if ( eos._edges[i]->FindIntersection( *searcher, dist, data._epsilon, eos, &intFace ))
+ // return false;
+ // else
+ // continue;
+ }
+ if ( !intFace )
+ {
+ SMESH_Comment msg("Invalid? normal at node "); msg << eos._edges[i]->_nodes[0]->GetID();
+ debugMsg( msg );
+ continue;
+ }
+
+ const bool isShorterDist = ( distToIntersection > dist );
+ if ( toBlockInfaltion || isShorterDist )
+ {
+ // ignore intersection of a _LayerEdge based on a _ConvexFace with a face
+ // lying on this _ConvexFace
+ if ( _ConvexFace* convFace = data.GetConvexFace( intFace->getshapeId() ))
+ if ( convFace->_isTooCurved && convFace->_subIdToEOS.count ( eos._shapeID ))
+ continue;
+
+ // ignore intersection of a _LayerEdge based on a FACE with an element on this FACE
+ // ( avoid limiting the thickness on the case of issue 22576)
+ if ( intFace->getshapeId() == eos._shapeID )
+ continue;
+
+ // ignore intersection with intFace of an adjacent FACE
+ if ( dist > 0.1 * eos._edges[i]->_len )
+ {
+ bool toIgnore = false;
+ if ( eos._toSmooth )
+ {
+ const TopoDS_Shape& S = getMeshDS()->IndexToShape( intFace->getshapeId() );
+ if ( !S.IsNull() && S.ShapeType() == TopAbs_FACE )
+ {
+ TopExp_Explorer sub( eos._shape,
+ eos.ShapeType() == TopAbs_FACE ? TopAbs_EDGE : TopAbs_VERTEX );
+ for ( ; !toIgnore && sub.More(); sub.Next() )
+ // is adjacent - has a common EDGE or VERTEX
+ toIgnore = ( helper.IsSubShape( sub.Current(), S ));
+
+ if ( toIgnore ) // check angle between normals
+ {
+ gp_XYZ normal;
+ if ( SMESH_MeshAlgos::FaceNormal( intFace, normal, /*normalized=*/true ))
+ toIgnore = ( normal * eos._edges[i]->_normal > -0.5 );
+ }
+ }
+ }
+ if ( !toIgnore ) // check if the edge is a neighbor of intFace
+ {
+ for ( size_t iN = 0; !toIgnore && iN < eos._edges[i]->_neibors.size(); ++iN )
+ {
+ int nInd = intFace->GetNodeIndex( eos._edges[i]->_neibors[ iN ]->_nodes.back() );
+ toIgnore = ( nInd >= 0 );
+ }
+ }
+ if ( toIgnore )
+ continue;
+ }
+
+ // intersection not ignored
+
+ if ( toBlockInfaltion &&
+ dist < ( eos._edges[i]->_len * theThickToIntersection ))
+ {
+ if ( is1stBlocked ) { is1stBlocked = false; // debug
+ dumpFunction(SMESH_Comment("blockIntersected") <<data._index<<"_InfStep"<<infStep);
+ }
+ eos._edges[i]->Set( _LayerEdge::INTERSECTED ); // not to intersect
+ eos._edges[i]->Block( data ); // not to inflate
+
+ if ( _EdgesOnShape* eof = data.GetShapeEdges( intFace->getshapeId() ))
+ {
+ // block _LayerEdge's, on top of which intFace is
+ if ( const _TmpMeshFace* f = dynamic_cast< const _TmpMeshFace*>( intFace ))
+ {
+ const SMDS_MeshElement* srcFace =
+ eof->_subMesh->GetSubMeshDS()->GetElement( f->getIdInShape() );
+ SMDS_ElemIteratorPtr nIt = srcFace->nodesIterator();
+ while ( nIt->more() )
+ {
+ const SMDS_MeshNode* srcNode = static_cast<const SMDS_MeshNode*>( nIt->next() );
+ TNode2Edge::iterator n2e = data._n2eMap.find( srcNode );
+ if ( n2e != data._n2eMap.end() )
+ n2e->second->Block( data );
+ }
+ }
+ }
+ }
+
+ if ( isShorterDist )
+ {
+ distToIntersection = dist;
+ le = eos._edges[i];
+ closestFace = intFace;
+ }
+
+ } // if ( toBlockInfaltion || isShorterDist )
+ } // loop on eos._edges
+ } // loop on data._edgesOnShape
+
+ if ( !is1stBlocked )
+ dumpFunctionEnd();
+
+ if ( closestFace && le )
+ {
+#ifdef __myDEBUG
+ SMDS_MeshElement::iterator nIt = closestFace->begin_nodes();
+ cout << "#Shortest distance: _LayerEdge nodes: tgt " << le->_nodes.back()->GetID()
+ << " src " << le->_nodes[0]->GetID()<< ", intersection with face ("
+ << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
+ << ") distance = " << distToIntersection<< endl;
+#endif
+ }
+
+ return true;
+}
+
+//================================================================================
+/*!
+ * \brief try to fix bad simplices by removing the last inflation step of some _LayerEdge's
+ * \param [in,out] badSmooEdges - _LayerEdge's to fix
+ * \return int - resulting nb of bad _LayerEdge's
+ */
+//================================================================================
+
+int _ViscousBuilder::invalidateBadSmooth( _SolidData& data,
+ SMESH_MesherHelper& helper,
+ vector< _LayerEdge* >& badSmooEdges,
+ vector< _EdgesOnShape* >& eosC1,
+ const int infStep )
+{
+ if ( badSmooEdges.empty() || infStep == 0 ) return 0;
+
+ dumpFunction(SMESH_Comment("invalidateBadSmooth")<<"_S"<<eosC1[0]->_shapeID<<"_InfStep"<<infStep);
+
+ enum {
+ INVALIDATED = _LayerEdge::UNUSED_FLAG,
+ TO_INVALIDATE = _LayerEdge::UNUSED_FLAG * 2,
+ ADDED = _LayerEdge::UNUSED_FLAG * 4
+ };
+ data.UnmarkEdges( TO_INVALIDATE & INVALIDATED & ADDED );
+
+ double vol;
+ bool haveInvalidated = true;
+ while ( haveInvalidated )
+ {
+ haveInvalidated = false;
+ for ( size_t i = 0; i < badSmooEdges.size(); ++i )
+ {
+ _LayerEdge* edge = badSmooEdges[i];
+ _EdgesOnShape* eos = data.GetShapeEdges( edge );
+ edge->Set( ADDED );
+ bool invalidated = false;
+ if ( edge->Is( TO_INVALIDATE ) && edge->NbSteps() > 1 )
+ {
+ edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
+ edge->Block( data );
+ edge->Set( INVALIDATED );
+ edge->Unset( TO_INVALIDATE );
+ invalidated = true;
+ haveInvalidated = true;
+ }
+
+ // look for _LayerEdge's of bad _simplices
+ int nbBad = 0;
+ SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
+ gp_XYZ prevXYZ1 = edge->PrevCheckPos( eos );
+ //const gp_XYZ& prevXYZ2 = edge->PrevPos();
+ for ( size_t j = 0; j < edge->_simplices.size(); ++j )
+ {
+ if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol ))/* &&
+ ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
+ continue;
+
+ bool isBad = true;
+ _LayerEdge* ee[2] = { 0,0 };
+ for ( size_t iN = 0; iN < edge->_neibors.size() && !ee[1] ; ++iN )
+ if ( edge->_simplices[j].Includes( edge->_neibors[iN]->_nodes.back() ))
+ ee[ ee[0] != 0 ] = edge->_neibors[iN];
+
+ int maxNbSteps = Max( ee[0]->NbSteps(), ee[1]->NbSteps() );
+ while ( maxNbSteps > edge->NbSteps() && isBad )
+ {
+ --maxNbSteps;
+ for ( int iE = 0; iE < 2; ++iE )
+ {
+ if ( ee[ iE ]->NbSteps() > maxNbSteps &&
+ ee[ iE ]->NbSteps() > 1 )
+ {
+ _EdgesOnShape* eos = data.GetShapeEdges( ee[ iE ] );
+ ee[ iE ]->InvalidateStep( ee[ iE ]->NbSteps(), *eos, /*restoreLength=*/true );
+ ee[ iE ]->Block( data );
+ ee[ iE ]->Set( INVALIDATED );
+ haveInvalidated = true;
+ }
+ }
+ if (( edge->_simplices[j].IsForward( &prevXYZ1, &tgtXYZ, vol )) /*&&
+ ( &prevXYZ1 == &prevXYZ2 || edge->_simplices[j].IsForward( &prevXYZ2, &tgtXYZ, vol ))*/)
+ isBad = false;
+ }
+ nbBad += isBad;
+ if ( !ee[0]->Is( ADDED )) badSmooEdges.push_back( ee[0] );
+ if ( !ee[1]->Is( ADDED )) badSmooEdges.push_back( ee[1] );
+ ee[0]->Set( ADDED );
+ ee[1]->Set( ADDED );
+ if ( isBad )
+ {
+ ee[0]->Set( TO_INVALIDATE );
+ ee[1]->Set( TO_INVALIDATE );
+ }
+ }
+
+ if ( !invalidated && nbBad > 0 && edge->NbSteps() > 1 )
+ {
+ _EdgesOnShape* eos = data.GetShapeEdges( edge );
+ edge->InvalidateStep( edge->NbSteps(), *eos, /*restoreLength=*/true );
+ edge->Block( data );
+ edge->Set( INVALIDATED );
+ edge->Unset( TO_INVALIDATE );
+ haveInvalidated = true;
+ }
+ } // loop on badSmooEdges
+ } // while ( haveInvalidated )
+
+ // re-smooth on analytical EDGEs
+ for ( size_t i = 0; i < badSmooEdges.size(); ++i )
+ {
+ _LayerEdge* edge = badSmooEdges[i];
+ if ( !edge->Is( INVALIDATED )) continue;
+
+ _EdgesOnShape* eos = data.GetShapeEdges( edge );
+ if ( eos->ShapeType() == TopAbs_VERTEX )
+ {
+ PShapeIteratorPtr eIt = helper.GetAncestors( eos->_shape, *_mesh, TopAbs_EDGE );
+ while ( const TopoDS_Shape* e = eIt->next() )
+ if ( _EdgesOnShape* eoe = data.GetShapeEdges( *e ))
+ if ( eoe->_edgeSmoother && eoe->_edgeSmoother->isAnalytic() )
+ {
+ // TopoDS_Face F; Handle(ShapeAnalysis_Surface) surface;
+ // if ( eoe->SWOLType() == TopAbs_FACE ) {
+ // F = TopoDS::Face( eoe->_sWOL );
+ // surface = helper.GetSurface( F );
+ // }
+ // eoe->_edgeSmoother->Perform( data, surface, F, helper );
+ eoe->_edgeSmoother->_anaCurve.Nullify();
+ }
+ }
+ }
+
+
+ // check result of invalidation
+
+ int nbBad = 0;
+ for ( size_t iEOS = 0; iEOS < eosC1.size(); ++iEOS )
+ {
+ for ( size_t i = 0; i < eosC1[ iEOS ]->_edges.size(); ++i )
+ {
+ if ( !eosC1[ iEOS ]->_sWOL.IsNull() ) continue;
+ _LayerEdge* edge = eosC1[ iEOS ]->_edges[i];
+ SMESH_TNodeXYZ tgtXYZ = edge->_nodes.back();
+ gp_XYZ prevXYZ = edge->PrevCheckPos( eosC1[ iEOS ]);
+ for ( size_t j = 0; j < edge->_simplices.size(); ++j )
+ if ( !edge->_simplices[j].IsForward( &prevXYZ, &tgtXYZ, vol ))
+ {
+ ++nbBad;
+ debugMsg("Bad simplex remains ( " << edge->_nodes[0]->GetID()
+ << " "<< tgtXYZ._node->GetID()
+ << " "<< edge->_simplices[j]._nPrev->GetID()
+ << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
+ }
+ }
+ }
+ dumpFunctionEnd();
+
+ return nbBad;
+}
+
+//================================================================================
+/*!
+ * \brief Create an offset surface
+ */
+//================================================================================
+
+void _ViscousBuilder::makeOffsetSurface( _EdgesOnShape& eos, SMESH_MesherHelper& helper )
+{
+ if ( eos._offsetSurf.IsNull() ||
+ eos._edgeForOffset == 0 ||
+ eos._edgeForOffset->Is( _LayerEdge::BLOCKED ))
+ return;
+
+ Handle(ShapeAnalysis_Surface) baseSurface = helper.GetSurface( TopoDS::Face( eos._shape ));
+
+ // find offset
+ gp_Pnt tgtP = SMESH_TNodeXYZ( eos._edgeForOffset->_nodes.back() );
+ /*gp_Pnt2d uv=*/baseSurface->ValueOfUV( tgtP, Precision::Confusion() );
+ double offset = baseSurface->Gap();
+
+ eos._offsetSurf.Nullify();
+
+ try
+ {
+ BRepOffsetAPI_MakeOffsetShape offsetMaker;
+ offsetMaker.PerformByJoin( eos._shape, -offset, Precision::Confusion() );
+ if ( !offsetMaker.IsDone() ) return;
+
+ TopExp_Explorer fExp( offsetMaker.Shape(), TopAbs_FACE );
+ if ( !fExp.More() ) return;
+
+ TopoDS_Face F = TopoDS::Face( fExp.Current() );
+ Handle(Geom_Surface) surf = BRep_Tool::Surface( F );
+ if ( surf.IsNull() ) return;
+
+ eos._offsetSurf = new ShapeAnalysis_Surface( surf );
+ }
+ catch ( Standard_Failure )
+ {
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Put nodes of a curved FACE to its offset surface
+ */
+//================================================================================
+
+void _ViscousBuilder::putOnOffsetSurface( _EdgesOnShape& eos,
+ int infStep,
+ vector< _EdgesOnShape* >& eosC1,
+ int smooStep,
+ int moveAll )
+{
+ _EdgesOnShape * eof = & eos;
+ if ( eos.ShapeType() != TopAbs_FACE ) // eos is a boundary of C1 FACE, look for the FACE eos
+ {
+ eof = 0;
+ for ( size_t i = 0; i < eosC1.size() && !eof; ++i )
+ {
+ if ( eosC1[i]->_offsetSurf.IsNull() ||
+ eosC1[i]->ShapeType() != TopAbs_FACE ||
+ eosC1[i]->_edgeForOffset == 0 ||
+ eosC1[i]->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
+ continue;
+ if ( SMESH_MesherHelper::IsSubShape( eos._shape, eosC1[i]->_shape ))
+ eof = eosC1[i];
+ }
+ }
+ if ( !eof ||
+ eof->_offsetSurf.IsNull() ||
+ eof->ShapeType() != TopAbs_FACE ||
+ eof->_edgeForOffset == 0 ||
+ eof->_edgeForOffset->Is( _LayerEdge::BLOCKED ))
+ return;
+
+ double preci = BRep_Tool::Tolerance( TopoDS::Face( eof->_shape )), vol;
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* edge = eos._edges[i];
+ edge->Unset( _LayerEdge::MARKED );
+ if ( edge->Is( _LayerEdge::BLOCKED ) || !edge->_curvature )
+ continue;
+ if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
+ {
+ if ( !edge->Is( _LayerEdge::UPD_NORMAL_CONV ))
+ continue;
+ }
+ else if ( !moveAll && !edge->Is( _LayerEdge::MOVED ))
+ continue;
+
+ int nbBlockedAround = 0;
+ for ( size_t iN = 0; iN < edge->_neibors.size(); ++iN )
+ nbBlockedAround += edge->_neibors[iN]->Is( _LayerEdge::BLOCKED );
+ if ( nbBlockedAround > 1 )
+ continue;
+
+ gp_Pnt tgtP = SMESH_TNodeXYZ( edge->_nodes.back() );
+ gp_Pnt2d uv = eof->_offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
+ if ( eof->_offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
+ edge->_curvature->_uv = uv;
+ if ( eof->_offsetSurf->Gap() < 10 * preci ) continue; // same pos
+
+ gp_XYZ newP = eof->_offsetSurf->Value( uv ).XYZ();
+ gp_XYZ prevP = edge->PrevCheckPos();
+ bool ok = true;
+ if ( !moveAll )
+ for ( size_t iS = 0; iS < edge->_simplices.size() && ok; ++iS )
+ {
+ ok = edge->_simplices[iS].IsForward( &prevP, &newP, vol );
+ }
+ if ( ok )
+ {
+ SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( edge->_nodes.back() );
+ n->setXYZ( newP.X(), newP.Y(), newP.Z());
+ edge->_pos.back() = newP;
+
+ edge->Set( _LayerEdge::MARKED );
+ if ( moveAll == _LayerEdge::UPD_NORMAL_CONV )
+ {
+ edge->_normal = ( newP - prevP ).Normalized();
+ }
+ }
+ }
+
+
+
+#ifdef _DEBUG_
+ // dumpMove() for debug
+ size_t i = 0;
+ for ( ; i < eos._edges.size(); ++i )
+ if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
+ break;
+ if ( i < eos._edges.size() )
+ {
+ dumpFunction(SMESH_Comment("putOnOffsetSurface_S") << eos._shapeID
+ << "_InfStep" << infStep << "_" << smooStep );
+ for ( ; i < eos._edges.size(); ++i )
+ {
+ if ( eos._edges[i]->Is( _LayerEdge::MARKED ))
+ dumpMove( eos._edges[i]->_nodes.back() );
+ }
+ dumpFunctionEnd();
+ }
+#endif
+
+ _ConvexFace* cnvFace;
+ if ( moveAll != _LayerEdge::UPD_NORMAL_CONV &&
+ eos.ShapeType() == TopAbs_FACE &&
+ (cnvFace = eos.GetData().GetConvexFace( eos._shapeID )) &&
+ !cnvFace->_normalsFixedOnBorders )
+ {
+ // put on the surface nodes built on FACE boundaries
+ SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
+ while ( smIt->more() )
+ {
+ SMESH_subMesh* sm = smIt->next();
+ _EdgesOnShape* subEOS = eos.GetData().GetShapeEdges( sm->GetId() );
+ if ( !subEOS->_sWOL.IsNull() ) continue;
+ if ( std::find( eosC1.begin(), eosC1.end(), subEOS ) != eosC1.end() ) continue;
+
+ putOnOffsetSurface( *subEOS, infStep, eosC1, smooStep, _LayerEdge::UPD_NORMAL_CONV );
+ }
+ cnvFace->_normalsFixedOnBorders = true;
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Return a curve of the EDGE to be used for smoothing and arrange
+ * _LayerEdge's to be in a consequent order
+ */
+//================================================================================
+
+Handle(Geom_Curve) _Smoother1D::CurveForSmooth( const TopoDS_Edge& E,
+ _EdgesOnShape& eos,
+ SMESH_MesherHelper& helper)
+{
+ SMESHDS_SubMesh* smDS = eos._subMesh->GetSubMeshDS();
+
+ TopLoc_Location loc; double f,l;
+
+ Handle(Geom_Line) line;
+ Handle(Geom_Circle) circle;
+ bool isLine, isCirc;
+ if ( eos._sWOL.IsNull() ) /////////////////////////////////////////// 3D case
+ {
+ // check if the EDGE is a line
+ Handle(Geom_Curve) curve = BRep_Tool::Curve( E, f, l);
+ if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve )))
+ curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve();
+
+ line = Handle(Geom_Line)::DownCast( curve );
+ circle = Handle(Geom_Circle)::DownCast( curve );
+ isLine = (!line.IsNull());
+ isCirc = (!circle.IsNull());
+
+ if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
+ {
+ isLine = SMESH_Algo::IsStraight( E );
+
+ if ( isLine )
+ line = new Geom_Line( gp::OX() ); // only type does matter
+ }
+ if ( !isLine && !isCirc && eos._edges.size() > 2) // Check if the EDGE is close to a circle
+ {
+ // TODO
+ }
+ }
+ else //////////////////////////////////////////////////////////////////////// 2D case
+ {
+ if ( !eos._isRegularSWOL ) // 23190
+ return NULL;
+
+ const TopoDS_Face& F = TopoDS::Face( eos._sWOL );
+
+ // check if the EDGE is a line
+ Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l );
+ if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve )))
+ curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve();
+
+ Handle(Geom2d_Line) line2d = Handle(Geom2d_Line)::DownCast( curve );
+ Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve );
+ isLine = (!line2d.IsNull());
+ isCirc = (!circle2d.IsNull());
+
+ if ( !isLine && !isCirc ) // Check if the EDGE is close to a line
+ {
+ Bnd_B2d bndBox;
+ SMDS_NodeIteratorPtr nIt = smDS->GetNodes();
+ while ( nIt->more() )
+ bndBox.Add( helper.GetNodeUV( F, nIt->next() ));
+ gp_XY size = bndBox.CornerMax() - bndBox.CornerMin();
+
+ const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() );
+ for ( int i = 0; i < 2 && !isLine; ++i )
+ isLine = ( size.Coord( i+1 ) <= lineTol );
+ }
+ if ( !isLine && !isCirc && eos._edges.size() > 2 ) // Check if the EDGE is close to a circle
+ {
+ // TODO
+ }
+ if ( isLine )
+ {
+ line = new Geom_Line( gp::OX() ); // only type does matter
+ }
+ else if ( isCirc )
+ {
+ gp_Pnt2d p = circle2d->Location();
+ gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX());
+ circle = new Geom_Circle( ax, 1.); // only center position does matter
+ }
+ }
+
+ if ( isLine )
+ return line;
+ if ( isCirc )
+ return circle;
+
+ return Handle(Geom_Curve)();
+}
+
+//================================================================================
+/*!
+ * \brief Smooth edges on EDGE
+ */
+//================================================================================
+
+bool _Smoother1D::Perform(_SolidData& data,
+ Handle(ShapeAnalysis_Surface)& surface,
+ const TopoDS_Face& F,
+ SMESH_MesherHelper& helper )
+{
+ if ( _leParams.empty() || ( !isAnalytic() && _offPoints.empty() ))
+ prepare( data );
+
+ findEdgesToSmooth();
+ if ( isAnalytic() )
+ return smoothAnalyticEdge( data, surface, F, helper );
+ else
+ return smoothComplexEdge ( data, surface, F, helper );
+}
+
+//================================================================================
+/*!
+ * \brief Find edges to smooth
+ */
+//================================================================================
+
+void _Smoother1D::findEdgesToSmooth()
+{
+ _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
+ for ( int iEnd = 0; iEnd < 2; ++iEnd )
+ if ( leOnV[iEnd]->Is( _LayerEdge::NORMAL_UPDATED ))
+ _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
+
+ _eToSmooth[0].first = _eToSmooth[0].second = 0;
+
+ for ( size_t i = 0; i < _eos.size(); ++i )
+ {
+ if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
+ {
+ if ( needSmoothing( _leOnV[0]._cosin,
+ _eos[i]->_len * leOnV[0]->_lenFactor, _curveLen * _leParams[i] ) ||
+ isToSmooth( i )
+ )
+ _eos[i]->Set( _LayerEdge::TO_SMOOTH );
+ else
+ break;
+ }
+ _eToSmooth[0].second = i+1;
+ }
+
+ _eToSmooth[1].first = _eToSmooth[1].second = _eos.size();
+
+ for ( int i = _eos.size() - 1; i >= _eToSmooth[0].second; --i )
+ {
+ if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH ))
+ {
+ if ( needSmoothing( _leOnV[1]._cosin,
+ _eos[i]->_len * leOnV[1]->_lenFactor, _curveLen * ( 1.-_leParams[i] )) ||
+ isToSmooth( i ))
+ _eos[i]->Set( _LayerEdge::TO_SMOOTH );
+ else
+ break;
+ }
+ _eToSmooth[1].first = i;
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Check if iE-th _LayerEdge needs smoothing
+ */
+//================================================================================
+
+bool _Smoother1D::isToSmooth( int iE )
+{
+ SMESH_NodeXYZ pi( _eos[iE]->_nodes[0] );
+ SMESH_NodeXYZ p0( _eos[iE]->_2neibors->srcNode(0) );
+ SMESH_NodeXYZ p1( _eos[iE]->_2neibors->srcNode(1) );
+ gp_XYZ seg0 = pi - p0;
+ gp_XYZ seg1 = p1 - pi;
+ gp_XYZ tangent = seg0 + seg1;
+ double tangentLen = tangent.Modulus();
+ double segMinLen = Min( seg0.Modulus(), seg1.Modulus() );
+ if ( tangentLen < std::numeric_limits<double>::min() )
+ return false;
+ tangent /= tangentLen;
+
+ for ( size_t i = 0; i < _eos[iE]->_neibors.size(); ++i )
+ {
+ _LayerEdge* ne = _eos[iE]->_neibors[i];
+ if ( !ne->Is( _LayerEdge::TO_SMOOTH ) ||
+ ne->_nodes.size() < 2 ||
+ ne->_nodes[0]->GetPosition()->GetDim() != 2 )
+ continue;
+ gp_XYZ edgeVec = SMESH_NodeXYZ( ne->_nodes.back() ) - SMESH_NodeXYZ( ne->_nodes[0] );
+ double proj = edgeVec * tangent;
+ if ( needSmoothing( 1., proj, segMinLen ))
+ return true;
+ }
+ return false;
+}
+
+//================================================================================
+/*!
+ * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE
+ */
+//================================================================================
+
+bool _Smoother1D::smoothAnalyticEdge( _SolidData& data,
+ Handle(ShapeAnalysis_Surface)& surface,
+ const TopoDS_Face& F,
+ SMESH_MesherHelper& helper)
+{
+ if ( !isAnalytic() ) return false;
+
+ size_t iFrom = 0, iTo = _eos._edges.size();
+
+ if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
+ {
+ if ( F.IsNull() ) // 3D
+ {
+ SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
+ SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
+ //const gp_XYZ lineDir = pSrc1 - pSrc0;
+ //_LayerEdge* vLE0 = getLEdgeOnV( 0 );
+ //_LayerEdge* vLE1 = getLEdgeOnV( 1 );
+ // bool shiftOnly = ( vLE0->Is( _LayerEdge::NORMAL_UPDATED ) ||
+ // vLE0->Is( _LayerEdge::BLOCKED ) ||
+ // vLE1->Is( _LayerEdge::NORMAL_UPDATED ) ||
+ // vLE1->Is( _LayerEdge::BLOCKED ));
+ for ( int iEnd = 0; iEnd < 2; ++iEnd )
+ {
+ iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
+ if ( iFrom >= iTo ) continue;
+ SMESH_TNodeXYZ p0( _eos[iFrom]->_2neibors->tgtNode(0) );
+ SMESH_TNodeXYZ p1( _eos[iTo-1]->_2neibors->tgtNode(1) );
+ double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
+ double param1 = _leParams[ iTo ];
+ for ( size_t i = iFrom; i < iTo; ++i )
+ {
+ _LayerEdge* edge = _eos[i];
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
+ double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
+ gp_XYZ newPos = p0 * ( 1. - param ) + p1 * param;
+
+ // if ( shiftOnly || edge->Is( _LayerEdge::NORMAL_UPDATED ))
+ // {
+ // gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
+ // double shift = ( lineDir * ( newPos - pSrc0 ) -
+ // lineDir * ( curPos - pSrc0 ));
+ // newPos = curPos + lineDir * shift / lineDir.SquareModulus();
+ // }
+ if ( edge->Is( _LayerEdge::BLOCKED ))
+ {
+ SMESH_TNodeXYZ pSrc( edge->_nodes[0] );
+ double curThick = pSrc.SquareDistance( tgtNode );
+ double newThink = ( pSrc - newPos ).SquareModulus();
+ if ( newThink > curThick )
+ continue;
+ }
+ edge->_pos.back() = newPos;
+ tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ dumpMove( tgtNode );
+ }
+ }
+ }
+ else // 2D
+ {
+ _LayerEdge* eV0 = getLEdgeOnV( 0 );
+ _LayerEdge* eV1 = getLEdgeOnV( 1 );
+ gp_XY uvV0 = eV0->LastUV( F, *data.GetShapeEdges( eV0 ));
+ gp_XY uvV1 = eV1->LastUV( F, *data.GetShapeEdges( eV1 ));
+ if ( eV0->_nodes.back() == eV1->_nodes.back() ) // closed edge
+ {
+ int iPeriodic = helper.GetPeriodicIndex();
+ if ( iPeriodic == 1 || iPeriodic == 2 )
+ {
+ uvV1.SetCoord( iPeriodic, helper.GetOtherParam( uvV1.Coord( iPeriodic )));
+ if ( uvV0.Coord( iPeriodic ) > uvV1.Coord( iPeriodic ))
+ std::swap( uvV0, uvV1 );
+ }
+ }
+ for ( int iEnd = 0; iEnd < 2; ++iEnd )
+ {
+ iFrom = _eToSmooth[ iEnd ].first, iTo = _eToSmooth[ iEnd ].second;
+ if ( iFrom >= iTo ) continue;
+ _LayerEdge* e0 = _eos[iFrom]->_2neibors->_edges[0];
+ _LayerEdge* e1 = _eos[iTo-1]->_2neibors->_edges[1];
+ gp_XY uv0 = ( e0 == eV0 ) ? uvV0 : e0->LastUV( F, _eos );
+ gp_XY uv1 = ( e1 == eV1 ) ? uvV1 : e1->LastUV( F, _eos );
+ double param0 = ( iFrom == 0 ) ? 0. : _leParams[ iFrom-1 ];
+ double param1 = _leParams[ iTo ];
+ gp_XY rangeUV = uv1 - uv0;
+ for ( size_t i = iFrom; i < iTo; ++i )
+ {
+ if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
+ double param = ( _leParams[i] - param0 ) / ( param1 - param0 );
+ gp_XY newUV = uv0 + param * rangeUV;
+
+ gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
+ tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ dumpMove( tgtNode );
+
+ SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
+ pos->SetUParameter( newUV.X() );
+ pos->SetVParameter( newUV.Y() );
+
+ gp_XYZ newUV0( newUV.X(), newUV.Y(), 0 );
+
+ if ( !_eos[i]->Is( _LayerEdge::SMOOTHED ))
+ {
+ _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
+ if ( _eos[i]->_pos.size() > 2 )
+ {
+ // modify previous positions to make _LayerEdge less sharply bent
+ vector<gp_XYZ>& uvVec = _eos[i]->_pos;
+ const gp_XYZ uvShift = newUV0 - uvVec.back();
+ const double len2 = ( uvVec.back() - uvVec[ 0 ] ).SquareModulus();
+ int iPrev = uvVec.size() - 2;
+ while ( iPrev > 0 )
+ {
+ double r = ( uvVec[ iPrev ] - uvVec[0] ).SquareModulus() / len2;
+ uvVec[ iPrev ] += uvShift * r;
+ --iPrev;
+ }
+ }
+ }
+ _eos[i]->_pos.back() = newUV0;
+ }
+ }
+ }
+ return true;
+ }
+
+ if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Circle )))
+ {
+ Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( _anaCurve );
+ gp_Pnt center3D = circle->Location();
+
+ if ( F.IsNull() ) // 3D
+ {
+ if ( getLEdgeOnV( 0 )->_nodes.back() == getLEdgeOnV( 1 )->_nodes.back() )
+ return true; // closed EDGE - nothing to do
+
+ // circle is a real curve of EDGE
+ gp_Circ circ = circle->Circ();
+
+ // new center is shifted along its axis
+ const gp_Dir& axis = circ.Axis().Direction();
+ _LayerEdge* e0 = getLEdgeOnV(0);
+ _LayerEdge* e1 = getLEdgeOnV(1);
+ SMESH_TNodeXYZ p0 = e0->_nodes.back();
+ SMESH_TNodeXYZ p1 = e1->_nodes.back();
+ double shift1 = axis.XYZ() * ( p0 - center3D.XYZ() );
+ double shift2 = axis.XYZ() * ( p1 - center3D.XYZ() );
+ gp_Pnt newCenter = center3D.XYZ() + axis.XYZ() * 0.5 * ( shift1 + shift2 );
+
+ double newRadius = 0.5 * ( newCenter.Distance( p0 ) + newCenter.Distance( p1 ));
+
+ gp_Ax2 newAxis( newCenter, axis, gp_Vec( newCenter, p0 ));
+ gp_Circ newCirc( newAxis, newRadius );
+ gp_Vec vecC1 ( newCenter, p1 );
+
+ double uLast = newAxis.XDirection().AngleWithRef( vecC1, newAxis.Direction() ); // -PI - +PI
+ if ( uLast < 0 )
+ uLast += 2 * M_PI;
+
+ for ( size_t i = 0; i < _eos.size(); ++i )
+ {
+ if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
+ //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
+ double u = uLast * _leParams[i];
+ gp_Pnt p = ElCLib::Value( u, newCirc );
+ _eos._edges[i]->_pos.back() = p.XYZ();
+
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
+ tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
+ dumpMove( tgtNode );
+ }
+ return true;
+ }
+ else // 2D
+ {
+ const gp_XY center( center3D.X(), center3D.Y() );
+
+ _LayerEdge* e0 = getLEdgeOnV(0);
+ _LayerEdge* eM = _eos._edges[ 0 ];
+ _LayerEdge* e1 = getLEdgeOnV(1);
+ gp_XY uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ) );
+ gp_XY uvM = eM->LastUV( F, *data.GetShapeEdges( eM ) );
+ gp_XY uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ) );
+ gp_Vec2d vec0( center, uv0 );
+ gp_Vec2d vecM( center, uvM );
+ gp_Vec2d vec1( center, uv1 );
+ double uLast = vec0.Angle( vec1 ); // -PI - +PI
+ double uMidl = vec0.Angle( vecM );
+ if ( uLast * uMidl <= 0. )
+ uLast += ( uMidl > 0 ? +2. : -2. ) * M_PI;
+ const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() );
+
+ gp_Ax2d axis( center, vec0 );
+ gp_Circ2d circ( axis, radius );
+ for ( size_t i = 0; i < _eos.size(); ++i )
+ {
+ if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
+ //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
+ double newU = uLast * _leParams[i];
+ gp_Pnt2d newUV = ElCLib::Value( newU, circ );
+ _eos._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 );
+
+ gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() );
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
+ tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() );
+ dumpMove( tgtNode );
+
+ SMDS_FacePosition* pos = static_cast<SMDS_FacePosition*>( tgtNode->GetPosition() );
+ pos->SetUParameter( newUV.X() );
+ pos->SetVParameter( newUV.Y() );
+
+ _eos[i]->Set( _LayerEdge::SMOOTHED ); // to check in refine() (IPAL54237)
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+//================================================================================
+/*!
+ * \brief smooth _LayerEdge's on a an EDGE
+ */
+//================================================================================
+
+bool _Smoother1D::smoothComplexEdge( _SolidData& data,
+ Handle(ShapeAnalysis_Surface)& surface,
+ const TopoDS_Face& F,
+ SMESH_MesherHelper& helper)
+{
+ if ( _offPoints.empty() )
+ return false;
+
+ // ----------------------------------------------
+ // move _offPoints along normals of _LayerEdge's
+ // ----------------------------------------------
+
+ _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
+ if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED ))
+ _leOnV[0]._normal = getNormalNormal( e[0]->_normal, _edgeDir[0] );
+ if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED ))
+ _leOnV[1]._normal = getNormalNormal( e[1]->_normal, _edgeDir[1] );
+ _leOnV[0]._len = e[0]->_len;
+ _leOnV[1]._len = e[1]->_len;
+ for ( size_t i = 0; i < _offPoints.size(); i++ )
+ {
+ _LayerEdge* e0 = _offPoints[i]._2edges._edges[0];
+ _LayerEdge* e1 = _offPoints[i]._2edges._edges[1];
+ const double w0 = _offPoints[i]._2edges._wgt[0];
+ const double w1 = _offPoints[i]._2edges._wgt[1];
+ gp_XYZ avgNorm = ( e0->_normal * w0 + e1->_normal * w1 ).Normalized();
+ double avgLen = ( e0->_len * w0 + e1->_len * w1 );
+ double avgFact = ( e0->_lenFactor * w0 + e1->_lenFactor * w1 );
+ if ( e0->Is( _LayerEdge::NORMAL_UPDATED ) ||
+ e1->Is( _LayerEdge::NORMAL_UPDATED ))
+ avgNorm = getNormalNormal( avgNorm, _offPoints[i]._edgeDir );
+
+ _offPoints[i]._xyz += avgNorm * ( avgLen - _offPoints[i]._len ) * avgFact;
+ _offPoints[i]._len = avgLen;
+ }
+
+ double fTol = 0;
+ if ( !surface.IsNull() ) // project _offPoints to the FACE
+ {
+ fTol = 100 * BRep_Tool::Tolerance( F );
+ //const double segLen = _offPoints[0].Distance( _offPoints[1] );
+
+ gp_Pnt2d uv = surface->ValueOfUV( _offPoints[0]._xyz, fTol );
+ //if ( surface->Gap() < 0.5 * segLen )
+ _offPoints[0]._xyz = surface->Value( uv ).XYZ();
+
+ for ( size_t i = 1; i < _offPoints.size(); ++i )
+ {
+ uv = surface->NextValueOfUV( uv, _offPoints[i]._xyz, fTol );
+ //if ( surface->Gap() < 0.5 * segLen )
+ _offPoints[i]._xyz = surface->Value( uv ).XYZ();
+ }
+ }
+
+ // -----------------------------------------------------------------
+ // project tgt nodes of extreme _LayerEdge's to the offset segments
+ // -----------------------------------------------------------------
+
+ const int updatedOrBlocked = _LayerEdge::NORMAL_UPDATED | _LayerEdge::BLOCKED;
+ if ( e[0]->Is( updatedOrBlocked )) _iSeg[0] = 0;
+ if ( e[1]->Is( updatedOrBlocked )) _iSeg[1] = _offPoints.size()-2;
+
+ gp_Pnt pExtreme[2], pProj[2];
+ bool isProjected[2];
+ for ( int is2nd = 0; is2nd < 2; ++is2nd )
+ {
+ pExtreme[ is2nd ] = SMESH_TNodeXYZ( e[is2nd]->_nodes.back() );
+ int i = _iSeg[ is2nd ];
+ int di = is2nd ? -1 : +1;
+ bool & projected = isProjected[ is2nd ];
+ projected = false;
+ double uOnSeg, distMin = Precision::Infinite(), dist, distPrev = 0;
+ int nbWorse = 0;
+ do {
+ gp_Vec v0p( _offPoints[i]._xyz, pExtreme[ is2nd ] );
+ gp_Vec v01( _offPoints[i]._xyz, _offPoints[i+1]._xyz );
+ uOnSeg = ( v0p * v01 ) / v01.SquareMagnitude(); // param [0,1] along v01
+ projected = ( Abs( uOnSeg - 0.5 ) <= 0.5 );
+ dist = pExtreme[ is2nd ].SquareDistance( _offPoints[ i + ( uOnSeg > 0.5 )]._xyz );
+ if ( dist < distMin || projected )
+ {
+ _iSeg[ is2nd ] = i;
+ pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
+ distMin = dist;
+ }
+ else if ( dist > distPrev )
+ {
+ if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
+ break;
+ }
+ distPrev = dist;
+ i += di;
+ }
+ while ( !projected &&
+ i >= 0 && i+1 < (int)_offPoints.size() );
+
+ if ( !projected )
+ {
+ if (( is2nd && _iSeg[1] != _offPoints.size()-2 ) || ( !is2nd && _iSeg[0] != 0 ))
+ {
+ _iSeg[0] = 0;
+ _iSeg[1] = _offPoints.size()-2;
+ debugMsg( "smoothComplexEdge() failed to project nodes of extreme _LayerEdge's" );
+ return false;
+ }
+ }
+ }
+ if ( _iSeg[0] > _iSeg[1] )
+ {
+ debugMsg( "smoothComplexEdge() incorrectly projected nodes of extreme _LayerEdge's" );
+ return false;
+ }
+
+ // adjust length of extreme LE (test viscous_layers_01/B7)
+ gp_Vec vDiv0( pExtreme[0], pProj[0] );
+ gp_Vec vDiv1( pExtreme[1], pProj[1] );
+ double d0 = vDiv0.Magnitude();
+ double d1 = isProjected[1] ? vDiv1.Magnitude() : 0;
+ if ( e[0]->Is( _LayerEdge::BLOCKED )) {
+ if ( e[0]->_normal * vDiv0.XYZ() < 0 ) e[0]->_len += d0;
+ else e[0]->_len -= d0;
+ }
+ if ( e[1]->Is( _LayerEdge::BLOCKED )) {
+ if ( e[1]->_normal * vDiv1.XYZ() < 0 ) e[1]->_len += d1;
+ else e[1]->_len -= d1;
+ }
+
+ // ---------------------------------------------------------------------------------
+ // compute normalized length of the offset segments located between the projections
+ // ---------------------------------------------------------------------------------
+
+ // temporary replace extreme _offPoints by pExtreme
+ gp_XYZ opXYZ[2] = { _offPoints[ _iSeg[0] ]._xyz,
+ _offPoints[ _iSeg[1]+1 ]._xyz };
+ _offPoints[ _iSeg[0] ]._xyz = pExtreme[0].XYZ();
+ _offPoints[ _iSeg[1]+ 1]._xyz = pExtreme[1].XYZ();
+
+ size_t iSeg = 0, nbSeg = _iSeg[1] - _iSeg[0] + 1;
+ vector< double > len( nbSeg + 1 );
+ len[ iSeg++ ] = 0;
+ len[ iSeg++ ] = pProj[ 0 ].Distance( _offPoints[ _iSeg[0]+1 ]._xyz );
+ for ( size_t i = _iSeg[0]+1; i <= _iSeg[1]; ++i, ++iSeg )
+ {
+ len[ iSeg ] = len[ iSeg-1 ] + _offPoints[i].Distance( _offPoints[i+1] );
+ }
+ // if ( isProjected[ 1 ])
+ // len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
+ // else
+ // len[ nbSeg ] += pExtreme[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
+
+ double fullLen = len.back() - d0 - d1;
+ for ( iSeg = 0; iSeg < len.size(); ++iSeg )
+ len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
+
+ // -------------------------------------------------------------
+ // distribute tgt nodes of _LayerEdge's between the projections
+ // -------------------------------------------------------------
+
+ iSeg = 0;
+ for ( size_t i = 0; i < _eos.size(); ++i )
+ {
+ if ( _eos[i]->Is( _LayerEdge::BLOCKED )) continue;
+ //if ( !_eos[i]->Is( _LayerEdge::TO_SMOOTH )) continue;
+ while ( iSeg+2 < len.size() && _leParams[i] > len[ iSeg+1 ] )
+ iSeg++;
+ double r = ( _leParams[i] - len[ iSeg ]) / ( len[ iSeg+1 ] - len[ iSeg ]);
+ gp_XYZ p = ( _offPoints[ iSeg + _iSeg[0] ]._xyz * ( 1 - r ) +
+ _offPoints[ iSeg + _iSeg[0] + 1 ]._xyz * r );
+
+ if ( surface.IsNull() )
+ {
+ _eos[i]->_pos.back() = p;
+ }
+ else // project a new node position to a FACE
+ {
+ gp_Pnt2d uv ( _eos[i]->_pos.back().X(), _eos[i]->_pos.back().Y() );
+ gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
+
+ p = surface->Value( uv2 ).XYZ();
+ _eos[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
+ }
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos[i]->_nodes.back() );
+ tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
+ dumpMove( tgtNode );
+ }
+
+ _offPoints[ _iSeg[0] ]._xyz = opXYZ[0];
+ _offPoints[ _iSeg[1]+1 ]._xyz = opXYZ[1];
+
+ return true;
+}
+
+//================================================================================
+/*!
+ * \brief Prepare for smoothing
+ */
+//================================================================================
+
+void _Smoother1D::prepare(_SolidData& data)
+{
+ const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
+ _curveLen = SMESH_Algo::EdgeLength( E );
+
+ // sort _LayerEdge's by position on the EDGE
+ data.SortOnEdge( E, _eos._edges );
+
+ // compute normalized param of _eos._edges on EDGE
+ _leParams.resize( _eos._edges.size() + 1 );
+ {
+ double curLen;
+ gp_Pnt pPrev = SMESH_TNodeXYZ( getLEdgeOnV( 0 )->_nodes[0] );
+ _leParams[0] = 0;
+ for ( size_t i = 0; i < _eos._edges.size(); ++i )
+ {
+ gp_Pnt p = SMESH_TNodeXYZ( _eos._edges[i]->_nodes[0] );
+ curLen = p.Distance( pPrev );
+ _leParams[i+1] = _leParams[i] + curLen;
+ pPrev = p;
+ }
+ double fullLen = _leParams.back() + pPrev.Distance( SMESH_TNodeXYZ( getLEdgeOnV(1)->_nodes[0]));
+ for ( size_t i = 0; i < _leParams.size()-1; ++i )
+ _leParams[i] = _leParams[i+1] / fullLen;
+ _leParams.back() = 1.;
+ }
+
+ _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
+
+ // get cosin to use in findEdgesToSmooth()
+ _edgeDir[0] = getEdgeDir( E, leOnV[0]->_nodes[0], data.GetHelper() );
+ _edgeDir[1] = getEdgeDir( E, leOnV[1]->_nodes[0], data.GetHelper() );
+ _leOnV[0]._cosin = Abs( leOnV[0]->_cosin );
+ _leOnV[1]._cosin = Abs( leOnV[1]->_cosin );
+ if ( _eos._sWOL.IsNull() ) // 3D
+ for ( int iEnd = 0; iEnd < 2; ++iEnd )
+ _leOnV[iEnd]._cosin = Abs( _edgeDir[iEnd].Normalized() * leOnV[iEnd]->_normal );
+
+ if ( isAnalytic() )
+ return;
+
+ // divide E to have offset segments with low deflection
+ BRepAdaptor_Curve c3dAdaptor( E );
+ const double curDeflect = 0.1; //0.01; // Curvature deflection == |p1p2]*sin(p1p2,p1pM)
+ const double angDeflect = 0.1; //0.09; // Angular deflection == sin(p1pM,pMp2)
+ GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
+ if ( discret.NbPoints() <= 2 )
+ {
+ _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
+ return;
+ }
+
+ const double u0 = c3dAdaptor.FirstParameter();
+ gp_Pnt p; gp_Vec tangent;
+ if ( discret.NbPoints() >= (int) _eos.size() + 2 )
+ {
+ _offPoints.resize( discret.NbPoints() );
+ for ( size_t i = 0; i < _offPoints.size(); i++ )
+ {
+ double u = discret.Parameter( i+1 );
+ c3dAdaptor.D1( u, p, tangent );
+ _offPoints[i]._xyz = p.XYZ();
+ _offPoints[i]._edgeDir = tangent.XYZ();
+ _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
+ }
+ }
+ else
+ {
+ std::vector< double > params( _eos.size() + 2 );
+
+ params[0] = data.GetHelper().GetNodeU( E, leOnV[0]->_nodes[0] );
+ params.back() = data.GetHelper().GetNodeU( E, leOnV[1]->_nodes[0] );
+ for ( size_t i = 0; i < _eos.size(); i++ )
+ params[i+1] = data.GetHelper().GetNodeU( E, _eos[i]->_nodes[0] );
+
+ if ( params[1] > params[ _eos.size() ] )
+ std::reverse( params.begin() + 1, params.end() - 1 );
+
+ _offPoints.resize( _eos.size() + 2 );
+ for ( size_t i = 0; i < _offPoints.size(); i++ )
+ {
+ const double u = params[i];
+ c3dAdaptor.D1( u, p, tangent );
+ _offPoints[i]._xyz = p.XYZ();
+ _offPoints[i]._edgeDir = tangent.XYZ();
+ _offPoints[i]._param = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / _curveLen;
+ }
+ }
+
+ // set _2edges
+ _offPoints [0]._2edges.set( &_leOnV[0], &_leOnV[0], 0.5, 0.5 );
+ _offPoints.back()._2edges.set( &_leOnV[1], &_leOnV[1], 0.5, 0.5 );
+ _2NearEdges tmp2edges;
+ tmp2edges._edges[1] = _eos._edges[0];
+ _leOnV[0]._2neibors = & tmp2edges;
+ _leOnV[0]._nodes = leOnV[0]->_nodes;
+ _leOnV[1]._nodes = leOnV[1]->_nodes;
+ _LayerEdge* eNext, *ePrev = & _leOnV[0];
+ for ( size_t iLE = 0, i = 1; i < _offPoints.size()-1; i++ )
+ {
+ // find _LayerEdge's located before and after an offset point
+ // (_eos._edges[ iLE ] is next after ePrev)
+ while ( iLE < _eos._edges.size() && _offPoints[i]._param > _leParams[ iLE ] )
+ ePrev = _eos._edges[ iLE++ ];
+ eNext = ePrev->_2neibors->_edges[1];
+
+ gp_Pnt p0 = SMESH_TNodeXYZ( ePrev->_nodes[0] );
+ gp_Pnt p1 = SMESH_TNodeXYZ( eNext->_nodes[0] );
+ double r = p0.Distance( _offPoints[i]._xyz ) / p0.Distance( p1 );
+ _offPoints[i]._2edges.set( ePrev, eNext, 1-r, r );
+ }
+
+ // replace _LayerEdge's on VERTEX by _leOnV in _offPoints._2edges
+ for ( size_t i = 0; i < _offPoints.size(); i++ )
+ if ( _offPoints[i]._2edges._edges[0] == leOnV[0] )
+ _offPoints[i]._2edges._edges[0] = & _leOnV[0];
+ else break;
+ for ( size_t i = _offPoints.size()-1; i > 0; i-- )
+ if ( _offPoints[i]._2edges._edges[1] == leOnV[1] )
+ _offPoints[i]._2edges._edges[1] = & _leOnV[1];
+ else break;
+
+ // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
+
+ int iLBO = _offPoints.size() - 2; // last but one
+
+ if ( leOnV[ 0 ]->Is( _LayerEdge::MULTI_NORMAL ))
+ _leOnV[ 0 ]._normal = getNormalNormal( _eos._edges[1]->_normal, _edgeDir[0] );
+ else
+ _leOnV[ 0 ]._normal = getNormalNormal( leOnV[0]->_normal, _edgeDir[0] );
+ if ( leOnV[ 1 ]->Is( _LayerEdge::MULTI_NORMAL ))
+ _leOnV[ 1 ]._normal = getNormalNormal( _eos._edges.back()->_normal, _edgeDir[1] );
+ else
+ _leOnV[ 1 ]._normal = getNormalNormal( leOnV[1]->_normal, _edgeDir[1] );
+ _leOnV[ 0 ]._len = 0;
+ _leOnV[ 1 ]._len = 0;
+ _leOnV[ 0 ]._lenFactor = _offPoints[1 ]._2edges._edges[1]->_lenFactor;
+ _leOnV[ 1 ]._lenFactor = _offPoints[iLBO]._2edges._edges[0]->_lenFactor;
+
+ _iSeg[0] = 0;
+ _iSeg[1] = _offPoints.size()-2;
+
+ // initialize OffPnt::_len
+ for ( size_t i = 0; i < _offPoints.size(); ++i )
+ _offPoints[i]._len = 0;
+
+ if ( _eos._edges[0]->NbSteps() > 1 ) // already inflated several times, init _xyz
+ {
+ _leOnV[0]._len = leOnV[0]->_len;
+ _leOnV[1]._len = leOnV[1]->_len;
+ for ( size_t i = 0; i < _offPoints.size(); i++ )
+ {
+ _LayerEdge* e0 = _offPoints[i]._2edges._edges[0];
+ _LayerEdge* e1 = _offPoints[i]._2edges._edges[1];
+ const double w0 = _offPoints[i]._2edges._wgt[0];
+ const double w1 = _offPoints[i]._2edges._wgt[1];
+ double avgLen = ( e0->_len * w0 + e1->_len * w1 );
+ gp_XYZ avgXYZ = ( SMESH_TNodeXYZ( e0->_nodes.back() ) * w0 +
+ SMESH_TNodeXYZ( e1->_nodes.back() ) * w1 );
+ _offPoints[i]._xyz = avgXYZ;
+ _offPoints[i]._len = avgLen;
+ }
+ }
+}
+
+//================================================================================
+/*!
+ * \brief return _normal of _leOnV[is2nd] normal to the EDGE
+ */
+//================================================================================
+
+gp_XYZ _Smoother1D::getNormalNormal( const gp_XYZ & normal,
+ const gp_XYZ& edgeDir)
+{
+ gp_XYZ cross = normal ^ edgeDir;
+ gp_XYZ norm = edgeDir ^ cross;
+ double size = norm.Modulus();
+
+ // if ( size == 0 ) // MULTI_NORMAL _LayerEdge
+ // return gp_XYZ( 1e-100, 1e-100, 1e-100 );
+
+ return norm / size;
+}
+
+//================================================================================
+/*!
+ * \brief Writes a script creating a mesh composed of _offPoints
+ */
+//================================================================================
+
+void _Smoother1D::offPointsToPython() const
+{
+ const char* fname = "/tmp/offPoints.py";
+ cout << "execfile('"<<fname<<"')"<<endl;
+ ofstream py(fname);
+ py << "import SMESH" << endl
+ << "from salome.smesh import smeshBuilder" << endl
+ << "smesh = smeshBuilder.New(salome.myStudy)" << endl
+ << "mesh = smesh.Mesh( 'offPoints' )"<<endl;
+ for ( size_t i = 0; i < _offPoints.size(); i++ )
+ {
+ py << "mesh.AddNode( "
+ << _offPoints[i]._xyz.X() << ", "
+ << _offPoints[i]._xyz.Y() << ", "
+ << _offPoints[i]._xyz.Z() << " )" << endl;
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Sort _LayerEdge's by a parameter on a given EDGE
+ */
+//================================================================================
+
+void _SolidData::SortOnEdge( const TopoDS_Edge& E,
+ vector< _LayerEdge* >& edges)
+{
+ map< double, _LayerEdge* > u2edge;
+ for ( size_t i = 0; i < edges.size(); ++i )
+ u2edge.insert( u2edge.end(),
+ make_pair( _helper->GetNodeU( E, edges[i]->_nodes[0] ), edges[i] ));
+
+ ASSERT( u2edge.size() == edges.size() );
+ map< double, _LayerEdge* >::iterator u2e = u2edge.begin();
+ for ( size_t i = 0; i < edges.size(); ++i, ++u2e )
+ edges[i] = u2e->second;
+
+ Sort2NeiborsOnEdge( edges );
+}
+
+//================================================================================
+/*!
+ * \brief Set _2neibors according to the order of _LayerEdge on EDGE
+ */
+//================================================================================
+
+void _SolidData::Sort2NeiborsOnEdge( vector< _LayerEdge* >& edges )
+{
+ if ( edges.size() < 2 || !edges[0]->_2neibors ) return;
+
+ for ( size_t i = 0; i < edges.size()-1; ++i )
+ if ( edges[i]->_2neibors->tgtNode(1) != edges[i+1]->_nodes.back() )
+ edges[i]->_2neibors->reverse();
+
+ const size_t iLast = edges.size() - 1;
+ if ( edges.size() > 1 &&
+ edges[iLast]->_2neibors->tgtNode(0) != edges[iLast-1]->_nodes.back() )
+ edges[iLast]->_2neibors->reverse();
+}
+
+//================================================================================
+/*!
+ * \brief Return _EdgesOnShape* corresponding to the shape
+ */
+//================================================================================
+
+_EdgesOnShape* _SolidData::GetShapeEdges(const TGeomID shapeID )
+{
+ if ( shapeID < (int)_edgesOnShape.size() &&
+ _edgesOnShape[ shapeID ]._shapeID == shapeID )
+ return _edgesOnShape[ shapeID ]._subMesh ? & _edgesOnShape[ shapeID ] : 0;
+
+ for ( size_t i = 0; i < _edgesOnShape.size(); ++i )
+ if ( _edgesOnShape[i]._shapeID == shapeID )
+ return _edgesOnShape[i]._subMesh ? & _edgesOnShape[i] : 0;
+
+ return 0;
+}
+
+//================================================================================
+/*!
+ * \brief Return _EdgesOnShape* corresponding to the shape
+ */
+//================================================================================
+
+_EdgesOnShape* _SolidData::GetShapeEdges(const TopoDS_Shape& shape )
+{
+ SMESHDS_Mesh* meshDS = _proxyMesh->GetMesh()->GetMeshDS();
+ return GetShapeEdges( meshDS->ShapeToIndex( shape ));
+}
+
+//================================================================================
+/*!
+ * \brief Prepare data of the _LayerEdge for smoothing on FACE
+ */
+//================================================================================
+
+void _SolidData::PrepareEdgesToSmoothOnFace( _EdgesOnShape* eos, bool substituteSrcNodes )
+{
+ SMESH_MesherHelper helper( *_proxyMesh->GetMesh() );
+
+ set< TGeomID > vertices;
+ TopoDS_Face F;
+ if ( eos->ShapeType() == TopAbs_FACE )
+ {
+ // check FACE concavity and get concave VERTEXes
+ F = TopoDS::Face( eos->_shape );
+ if ( isConcave( F, helper, &vertices ))
+ _concaveFaces.insert( eos->_shapeID );
+
+ // set eos._eosConcaVer
+ eos->_eosConcaVer.clear();
+ eos->_eosConcaVer.reserve( vertices.size() );
+ for ( set< TGeomID >::iterator v = vertices.begin(); v != vertices.end(); ++v )
+ {
+ _EdgesOnShape* eov = GetShapeEdges( *v );
+ if ( eov && eov->_edges.size() == 1 )
+ {
+ eos->_eosConcaVer.push_back( eov );
+ for ( size_t i = 0; i < eov->_edges[0]->_neibors.size(); ++i )
+ eov->_edges[0]->_neibors[i]->Set( _LayerEdge::DIFFICULT );
+ }
+ }
+
+ // SetSmooLen() to _LayerEdge's on FACE
+ // for ( size_t i = 0; i < eos->_edges.size(); ++i )
+ // {
+ // eos->_edges[i]->SetSmooLen( Precision::Infinite() );
+ // }
+ // SMESH_subMeshIteratorPtr smIt = eos->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
+ // while ( smIt->more() ) // loop on sub-shapes of the FACE
+ // {
+ // _EdgesOnShape* eoe = GetShapeEdges( smIt->next()->GetId() );
+ // if ( !eoe ) continue;
+
+ // vector<_LayerEdge*>& eE = eoe->_edges;
+ // for ( size_t iE = 0; iE < eE.size(); ++iE ) // loop on _LayerEdge's on EDGE or VERTEX
+ // {
+ // if ( eE[iE]->_cosin <= theMinSmoothCosin )
+ // continue;
+
+ // SMDS_ElemIteratorPtr segIt = eE[iE]->_nodes[0]->GetInverseElementIterator(SMDSAbs_Edge);
+ // while ( segIt->more() )
+ // {
+ // const SMDS_MeshElement* seg = segIt->next();
+ // if ( !eos->_subMesh->DependsOn( seg->getshapeId() ))
+ // continue;
+ // if ( seg->GetNode(0) != eE[iE]->_nodes[0] )
+ // continue; // not to check a seg twice
+ // for ( size_t iN = 0; iN < eE[iE]->_neibors.size(); ++iN )
+ // {
+ // _LayerEdge* eN = eE[iE]->_neibors[iN];
+ // if ( eN->_nodes[0]->getshapeId() != eos->_shapeID )
+ // continue;
+ // double dist = SMESH_MeshAlgos::GetDistance( seg, SMESH_TNodeXYZ( eN->_nodes[0] ));
+ // double smooLen = getSmoothingThickness( eE[iE]->_cosin, dist );
+ // eN->SetSmooLen( Min( smooLen, eN->GetSmooLen() ));
+ // eN->Set( _LayerEdge::NEAR_BOUNDARY );
+ // }
+ // }
+ // }
+ // }
+ } // if ( eos->ShapeType() == TopAbs_FACE )
+
+ for ( size_t i = 0; i < eos->_edges.size(); ++i )
+ {
+ eos->_edges[i]->_smooFunction = 0;
+ eos->_edges[i]->Set( _LayerEdge::TO_SMOOTH );
+ }
+ bool isCurved = false;
+ for ( size_t i = 0; i < eos->_edges.size(); ++i )
+ {
+ _LayerEdge* edge = eos->_edges[i];
+
+ // get simplices sorted
+ _Simplex::SortSimplices( edge->_simplices );
+
+ // smoothing function
+ edge->ChooseSmooFunction( vertices, _n2eMap );
+
+ // set _curvature
+ double avgNormProj = 0, avgLen = 0;
+ for ( size_t iS = 0; iS < edge->_simplices.size(); ++iS )
+ {
+ _Simplex& s = edge->_simplices[iS];
+
+ gp_XYZ vec = edge->_pos.back() - SMESH_TNodeXYZ( s._nPrev );
+ avgNormProj += edge->_normal * vec;
+ avgLen += vec.Modulus();
+ if ( substituteSrcNodes )
+ {
+ s._nNext = _n2eMap[ s._nNext ]->_nodes.back();
+ s._nPrev = _n2eMap[ s._nPrev ]->_nodes.back();
+ }
+ }
+ avgNormProj /= edge->_simplices.size();
+ avgLen /= edge->_simplices.size();
+ if (( edge->_curvature = _Curvature::New( avgNormProj, avgLen )))
+ {
+ edge->Set( _LayerEdge::SMOOTHED_C1 );
+ isCurved = true;
+ SMDS_FacePosition* fPos = dynamic_cast<SMDS_FacePosition*>( edge->_nodes[0]->GetPosition() );
+ if ( !fPos )
+ for ( size_t iS = 0; iS < edge->_simplices.size() && !fPos; ++iS )
+ fPos = dynamic_cast<SMDS_FacePosition*>( edge->_simplices[iS]._nPrev->GetPosition() );
+ if ( fPos )
+ edge->_curvature->_uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
+ }
+ }
+
+ // prepare for putOnOffsetSurface()
+ if (( eos->ShapeType() == TopAbs_FACE ) &&
+ ( isCurved || !eos->_eosConcaVer.empty() ))
+ {
+ eos->_offsetSurf = helper.GetSurface( TopoDS::Face( eos->_shape ));
+ eos->_edgeForOffset = 0;
+
+ double maxCosin = -1;
+ for ( TopExp_Explorer eExp( eos->_shape, TopAbs_EDGE ); eExp.More(); eExp.Next() )
+ {
+ _EdgesOnShape* eoe = GetShapeEdges( eExp.Current() );
+ if ( !eoe || eoe->_edges.empty() ) continue;
+
+ vector<_LayerEdge*>& eE = eoe->_edges;
+ _LayerEdge* e = eE[ eE.size() / 2 ];
+ if ( e->_cosin > maxCosin )
+ {
+ eos->_edgeForOffset = e;
+ maxCosin = e->_cosin;
+ }
+ }
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Add faces for smoothing
+ */
+//================================================================================
+
+void _SolidData::AddShapesToSmooth( const set< _EdgesOnShape* >& eosToSmooth,
+ const set< _EdgesOnShape* >* edgesNoAnaSmooth )
+{
+ set< _EdgesOnShape * >::const_iterator eos = eosToSmooth.begin();
+ for ( ; eos != eosToSmooth.end(); ++eos )
+ {
+ if ( !*eos || (*eos)->_toSmooth ) continue;
+
+ (*eos)->_toSmooth = true;
+
+ if ( (*eos)->ShapeType() == TopAbs_FACE )
+ {
+ PrepareEdgesToSmoothOnFace( *eos, /*substituteSrcNodes=*/false );
+ (*eos)->_toSmooth = true;
+ }
+ }
+
+ // avoid _Smoother1D::smoothAnalyticEdge() of edgesNoAnaSmooth
+ if ( edgesNoAnaSmooth )
+ for ( eos = edgesNoAnaSmooth->begin(); eos != edgesNoAnaSmooth->end(); ++eos )
+ {
+ if ( (*eos)->_edgeSmoother )
+ (*eos)->_edgeSmoother->_anaCurve.Nullify();
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Limit _LayerEdge::_maxLen according to local curvature
+ */
+//================================================================================
+
+void _ViscousBuilder::limitMaxLenByCurvature( _SolidData& data, SMESH_MesherHelper& helper )
+{
+ // find intersection of neighbor _LayerEdge's to limit _maxLen
+ // according to local curvature (IPAL52648)
+
+ // This method must be called after findCollisionEdges() where _LayerEdge's
+ // get _lenFactor initialized in the case of eos._hyp.IsOffsetMethod()
+
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eosI = data._edgesOnShape[iS];
+ if ( eosI._edges.empty() ) continue;
+ if ( !eosI._hyp.ToSmooth() )
+ {
+ for ( size_t i = 0; i < eosI._edges.size(); ++i )
+ {
+ _LayerEdge* eI = eosI._edges[i];
+ for ( size_t iN = 0; iN < eI->_neibors.size(); ++iN )
+ {
+ _LayerEdge* eN = eI->_neibors[iN];
+ if ( eI->_nodes[0]->GetID() < eN->_nodes[0]->GetID() ) // treat this pair once
+ {
+ _EdgesOnShape* eosN = data.GetShapeEdges( eN );
+ limitMaxLenByCurvature( eI, eN, eosI, *eosN, eosI._hyp.ToSmooth() );
+ }
+ }
+ }
+ }
+ else if ( eosI.ShapeType() == TopAbs_EDGE )
+ {
+ const TopoDS_Edge& E = TopoDS::Edge( eosI._shape );
+ if ( SMESH_Algo::IsStraight( E, /*degenResult=*/true )) continue;
+
+ _LayerEdge* e0 = eosI._edges[0];
+ for ( size_t i = 1; i < eosI._edges.size(); ++i )
+ {
+ _LayerEdge* eI = eosI._edges[i];
+ limitMaxLenByCurvature( eI, e0, eosI, eosI, eosI._hyp.ToSmooth() );
+ e0 = eI;
+ }
+ }
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Limit _LayerEdge::_maxLen according to local curvature
+ */
+//================================================================================
+
+void _ViscousBuilder::limitMaxLenByCurvature( _LayerEdge* e1,
+ _LayerEdge* e2,
+ _EdgesOnShape& eos1,
+ _EdgesOnShape& eos2,
+ const bool isSmoothable )
+{
+ if (( e1->_nodes[0]->GetPosition()->GetDim() !=
+ e2->_nodes[0]->GetPosition()->GetDim() ) &&
+ ( e1->_cosin < 0.75 ))
+ return; // angle > 90 deg at e1
+
+ gp_XYZ plnNorm = e1->_normal ^ e2->_normal;
+ double norSize = plnNorm.SquareModulus();
+ if ( norSize < std::numeric_limits<double>::min() )
+ return; // parallel normals
+
+ // find closest points of skew _LayerEdge's
+ SMESH_TNodeXYZ src1( e1->_nodes[0] ), src2( e2->_nodes[0] );
+ gp_XYZ dir12 = src2 - src1;
+ gp_XYZ perp1 = e1->_normal ^ plnNorm;
+ gp_XYZ perp2 = e2->_normal ^ plnNorm;
+ double dot1 = perp2 * e1->_normal;
+ double dot2 = perp1 * e2->_normal;
+ double u1 = ( perp2 * dir12 ) / dot1;
+ double u2 = - ( perp1 * dir12 ) / dot2;
+ if ( u1 > 0 && u2 > 0 )
+ {
+ double ovl = ( u1 * e1->_normal * dir12 -
+ u2 * e2->_normal * dir12 ) / dir12.SquareModulus();
+ if ( ovl > theSmoothThickToElemSizeRatio )
+ {
+ const double coef = 0.75;
+ e1->SetMaxLen( Min( e1->_maxLen, coef * u1 / e1->_lenFactor ));
+ e2->SetMaxLen( Min( e2->_maxLen, coef * u2 / e2->_lenFactor ));
+ }
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Fill data._collisionEdges
+ */
+//================================================================================
+
+void _ViscousBuilder::findCollisionEdges( _SolidData& data, SMESH_MesherHelper& helper )
+{
+ data._collisionEdges.clear();
+
+ // set the full thickness of the layers to LEs
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[iS];
+ if ( eos._edges.empty() ) continue;
+ if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
+
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ if ( eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
+ double maxLen = eos._edges[i]->_maxLen;
+ eos._edges[i]->_maxLen = Precision::Infinite(); // avoid blocking
+ eos._edges[i]->SetNewLength( 1.5 * maxLen, eos, helper );
+ eos._edges[i]->_maxLen = maxLen;
+ }
+ }
+
+ // make temporary quadrangles got by extrusion of
+ // mesh edges along _LayerEdge._normal's
+
+ vector< const SMDS_MeshElement* > tmpFaces;
+
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( eos.ShapeType() != TopAbs_EDGE )
+ continue;
+ if ( eos._edges.empty() )
+ {
+ _LayerEdge* edge[2] = { 0, 0 }; // LE of 2 VERTEX'es
+ SMESH_subMeshIteratorPtr smIt = eos._subMesh->getDependsOnIterator(/*includeSelf=*/false);
+ while ( smIt->more() )
+ if ( _EdgesOnShape* eov = data.GetShapeEdges( smIt->next()->GetId() ))
+ if ( eov->_edges.size() == 1 )
+ edge[ bool( edge[0]) ] = eov->_edges[0];
+
+ if ( edge[1] )
+ {
+ _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge[0], edge[1], --_tmpFaceID );
+ tmpFaces.push_back( f );
+ }
+ }
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* edge = eos._edges[i];
+ for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges
+ {
+ const SMDS_MeshNode* src2 = edge->_2neibors->srcNode(j);
+ if ( src2->GetPosition()->GetDim() > 0 &&
+ src2->GetID() < edge->_nodes[0]->GetID() )
+ continue; // avoid using same segment twice
+
+ // a _LayerEdge containg tgt2
+ _LayerEdge* neiborEdge = edge->_2neibors->_edges[j];
+
+ _TmpMeshFaceOnEdge* f = new _TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID );
+ tmpFaces.push_back( f );
+ }
+ }
+ }
+
+ // Find _LayerEdge's intersecting tmpFaces.
+
+ SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(),
+ tmpFaces.end()));
+ SMESHUtils::Deleter<SMESH_ElementSearcher> searcher
+ ( SMESH_MeshAlgos::GetElementSearcher( *getMeshDS(), fIt ));
+
+ double dist1, dist2, segLen, eps = 0.5;
+ _CollisionEdges collEdges;
+ vector< const SMDS_MeshElement* > suspectFaces;
+ const double angle45 = Cos( 45. * M_PI / 180. );
+
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( eos.ShapeType() == TopAbs_FACE || !eos._sWOL.IsNull() )
+ continue;
+ // find sub-shapes whose VL can influence VL on eos
+ set< TGeomID > neighborShapes;
+ PShapeIteratorPtr fIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_FACE );
+ while ( const TopoDS_Shape* face = fIt->next() )
+ {
+ TGeomID faceID = getMeshDS()->ShapeToIndex( *face );
+ if ( _EdgesOnShape* eof = data.GetShapeEdges( faceID ))
+ {
+ SMESH_subMeshIteratorPtr subIt = eof->_subMesh->getDependsOnIterator(/*includeSelf=*/false);
+ while ( subIt->more() )
+ neighborShapes.insert( subIt->next()->GetId() );
+ }
+ }
+ if ( eos.ShapeType() == TopAbs_VERTEX )
+ {
+ PShapeIteratorPtr eIt = helper.GetAncestors( eos._shape, *_mesh, TopAbs_EDGE );
+ while ( const TopoDS_Shape* edge = eIt->next() )
+ neighborShapes.erase( getMeshDS()->ShapeToIndex( *edge ));
+ }
+ // find intersecting _LayerEdge's
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ if ( eos._edges[i]->Is( _LayerEdge::MULTI_NORMAL )) continue;
+ _LayerEdge* edge = eos._edges[i];
+ gp_Ax1 lastSegment = edge->LastSegment( segLen, eos );
+ segLen *= 1.2;
+
+ gp_Vec eSegDir0, eSegDir1;
+ if ( edge->IsOnEdge() )
+ {
+ SMESH_TNodeXYZ eP( edge->_nodes[0] );
+ eSegDir0 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(0) ) - eP;
+ eSegDir1 = SMESH_TNodeXYZ( edge->_2neibors->srcNode(1) ) - eP;
+ }
+ suspectFaces.clear();
+ searcher->GetElementsInSphere( SMESH_TNodeXYZ( edge->_nodes.back()), edge->_len * 2,
+ SMDSAbs_Face, suspectFaces );
+ collEdges._intEdges.clear();
+ for ( size_t j = 0 ; j < suspectFaces.size(); ++j )
+ {
+ const _TmpMeshFaceOnEdge* f = (const _TmpMeshFaceOnEdge*) suspectFaces[j];
+ if ( f->_le1 == edge || f->_le2 == edge ) continue;
+ if ( !neighborShapes.count( f->_le1->_nodes[0]->getshapeId() )) continue;
+ if ( !neighborShapes.count( f->_le2->_nodes[0]->getshapeId() )) continue;
+ if ( edge->IsOnEdge() ) {
+ if ( edge->_2neibors->include( f->_le1 ) ||
+ edge->_2neibors->include( f->_le2 )) continue;
+ }
+ else {
+ if (( f->_le1->IsOnEdge() && f->_le1->_2neibors->include( edge )) ||
+ ( f->_le2->IsOnEdge() && f->_le2->_2neibors->include( edge ))) continue;
+ }
+ dist1 = dist2 = Precision::Infinite();
+ if ( !edge->SegTriaInter( lastSegment, f->_nn[0], f->_nn[1], f->_nn[2], dist1, eps ))
+ dist1 = Precision::Infinite();
+ if ( !edge->SegTriaInter( lastSegment, f->_nn[3], f->_nn[2], f->_nn[0], dist2, eps ))
+ dist2 = Precision::Infinite();
+ if (( dist1 > segLen ) && ( dist2 > segLen ))
+ continue;
+
+ if ( edge->IsOnEdge() )
+ {
+ // skip perpendicular EDGEs
+ gp_Vec fSegDir = SMESH_TNodeXYZ( f->_nn[0] ) - SMESH_TNodeXYZ( f->_nn[3] );
+ bool isParallel = ( isLessAngle( eSegDir0, fSegDir, angle45 ) ||
+ isLessAngle( eSegDir1, fSegDir, angle45 ) ||
+ isLessAngle( eSegDir0, fSegDir.Reversed(), angle45 ) ||
+ isLessAngle( eSegDir1, fSegDir.Reversed(), angle45 ));
+ if ( !isParallel )
+ continue;
+ }
+
+ // either limit inflation of edges or remember them for updating _normal
+ // double dot = edge->_normal * f->GetDir();
+ // if ( dot > 0.1 )
+ {
+ collEdges._intEdges.push_back( f->_le1 );
+ collEdges._intEdges.push_back( f->_le2 );
+ }
+ // else
+ // {
+ // double shortLen = 0.75 * ( Min( dist1, dist2 ) / edge->_lenFactor );
+ // edge->SetMaxLen( Min( shortLen, edge->_maxLen ));
+ // }
+ }
+
+ if ( !collEdges._intEdges.empty() )
+ {
+ collEdges._edge = edge;
+ data._collisionEdges.push_back( collEdges );
+ }
+ }
+ }
+
+ for ( size_t i = 0 ; i < tmpFaces.size(); ++i )
+ delete tmpFaces[i];
+
+ // restore the zero thickness
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[iS];
+ if ( eos._edges.empty() ) continue;
+ if ( eos.ShapeType() != TopAbs_EDGE && eos.ShapeType() != TopAbs_VERTEX ) continue;
+
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ eos._edges[i]->InvalidateStep( 1, eos );
+ eos._edges[i]->_len = 0;
+ }
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Find _LayerEdge's located on boundary of a convex FACE whose normal
+ * will be updated at each inflation step
+ */
+//================================================================================
+
+void _ViscousBuilder::findEdgesToUpdateNormalNearConvexFace( _ConvexFace & convFace,
+ _SolidData& data,
+ SMESH_MesherHelper& helper )
+{
+ const TGeomID convFaceID = getMeshDS()->ShapeToIndex( convFace._face );
+ const double preci = BRep_Tool::Tolerance( convFace._face );
+ Handle(ShapeAnalysis_Surface) surface = helper.GetSurface( convFace._face );
+
+ bool edgesToUpdateFound = false;
+
+ map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
+ for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
+ {
+ _EdgesOnShape& eos = * id2eos->second;
+ if ( !eos._sWOL.IsNull() ) continue;
+ if ( !eos._hyp.ToSmooth() ) continue;
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* ledge = eos._edges[ i ];
+ if ( ledge->Is( _LayerEdge::UPD_NORMAL_CONV )) continue; // already checked
+ if ( ledge->Is( _LayerEdge::MULTI_NORMAL )) continue; // not inflatable
+
+ gp_XYZ tgtPos = ( SMESH_NodeXYZ( ledge->_nodes[0] ) +
+ ledge->_normal * ledge->_lenFactor * ledge->_maxLen );
+
+ // the normal must be updated if distance from tgtPos to surface is less than
+ // target thickness
+
+ // find an initial UV for search of a projection of tgtPos to surface
+ const SMDS_MeshNode* nodeInFace = 0;
+ SMDS_ElemIteratorPtr fIt = ledge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
+ while ( fIt->more() && !nodeInFace )
+ {
+ const SMDS_MeshElement* f = fIt->next();
+ if ( convFaceID != f->getshapeId() ) continue;
+
+ SMDS_ElemIteratorPtr nIt = f->nodesIterator();
+ while ( nIt->more() && !nodeInFace )
+ {
+ const SMDS_MeshElement* n = nIt->next();
+ if ( n->getshapeId() == convFaceID )
+ nodeInFace = static_cast< const SMDS_MeshNode* >( n );
+ }
+ }
+ if ( !nodeInFace )
+ continue;
+ gp_XY uv = helper.GetNodeUV( convFace._face, nodeInFace );
+
+ // projection
+ surface->NextValueOfUV( uv, tgtPos, preci );
+ double dist = surface->Gap();
+ if ( dist < 0.95 * ledge->_maxLen )
+ {
+ ledge->Set( _LayerEdge::UPD_NORMAL_CONV );
+ if ( !ledge->_curvature ) ledge->_curvature = new _Curvature;
+ ledge->_curvature->_uv.SetCoord( uv.X(), uv.Y() );
+ edgesToUpdateFound = true;
+ }
+ }
+ }
+
+ if ( !convFace._isTooCurved && edgesToUpdateFound )
+ {
+ data._convexFaces.insert( make_pair( convFaceID, convFace )).first->second;
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with
+ * _LayerEdge's on neighbor EDGE's
+ */
+//================================================================================
+
+bool _ViscousBuilder::updateNormals( _SolidData& data,
+ SMESH_MesherHelper& helper,
+ int stepNb,
+ double stepSize)
+{
+ updateNormalsOfC1Vertices( data );
+
+ if ( stepNb > 0 && !updateNormalsOfConvexFaces( data, helper, stepNb ))
+ return false;
+
+ // map to store new _normal and _cosin for each intersected edge
+ map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp > edge2newEdge;
+ map< _LayerEdge*, _LayerEdge, _LayerEdgeCmp >::iterator e2neIt;
+ _LayerEdge zeroEdge;
+ zeroEdge._normal.SetCoord( 0,0,0 );
+ zeroEdge._maxLen = Precision::Infinite();
+ zeroEdge._nodes.resize(1); // to init _TmpMeshFaceOnEdge
+
+ set< _EdgesOnShape* > shapesToSmooth, edgesNoAnaSmooth;
+
+ double segLen, dist1, dist2, dist;
+ vector< pair< _LayerEdge*, double > > intEdgesDist;
+ _TmpMeshFaceOnEdge quad( &zeroEdge, &zeroEdge, 0 );
+
+ for ( int iter = 0; iter < 5; ++iter )
+ {
+ edge2newEdge.clear();
+
+ for ( size_t iE = 0; iE < data._collisionEdges.size(); ++iE )
+ {
+ _CollisionEdges& ce = data._collisionEdges[iE];
+ _LayerEdge* edge1 = ce._edge;
+ if ( !edge1 /*|| edge1->Is( _LayerEdge::BLOCKED )*/) continue;
+ _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
+ if ( !eos1 ) continue;
+
+ // detect intersections
+ gp_Ax1 lastSeg = edge1->LastSegment( segLen, *eos1 );
+ double testLen = 1.5 * edge1->_maxLen * edge1->_lenFactor;
+ double eps = 0.5;
+ intEdgesDist.clear();
+ double minIntDist = Precision::Infinite();
+ for ( size_t i = 0; i < ce._intEdges.size(); i += 2 )
+ {
+ if ( edge1->Is( _LayerEdge::BLOCKED ) &&
+ ce._intEdges[i ]->Is( _LayerEdge::BLOCKED ) &&
+ ce._intEdges[i+1]->Is( _LayerEdge::BLOCKED ))
+ continue;
+ double dot = edge1->_normal * quad.GetDir( ce._intEdges[i], ce._intEdges[i+1] );
+ double fact = ( 1.1 + dot * dot );
+ SMESH_TNodeXYZ pSrc0( ce.nSrc(i) ), pSrc1( ce.nSrc(i+1) );
+ SMESH_TNodeXYZ pTgt0( ce.nTgt(i) ), pTgt1( ce.nTgt(i+1) );
+ gp_XYZ pLast0 = pSrc0 + ( pTgt0 - pSrc0 ) * fact;
+ gp_XYZ pLast1 = pSrc1 + ( pTgt1 - pSrc1 ) * fact;
+ dist1 = dist2 = Precision::Infinite();
+ if ( !edge1->SegTriaInter( lastSeg, pSrc0, pLast0, pSrc1, dist1, eps ) &&
+ !edge1->SegTriaInter( lastSeg, pSrc1, pLast1, pLast0, dist2, eps ))
+ continue;
+ dist = dist1;
+ if ( dist > testLen || dist <= 0 )
+ {
+ dist = dist2;
+ if ( dist > testLen || dist <= 0 )
+ continue;
+ }
+ // choose a closest edge
+ gp_Pnt intP( lastSeg.Location().XYZ() + lastSeg.Direction().XYZ() * ( dist + segLen ));
+ double d1 = intP.SquareDistance( pSrc0 );
+ double d2 = intP.SquareDistance( pSrc1 );
+ int iClose = i + ( d2 < d1 );
+ _LayerEdge* edge2 = ce._intEdges[iClose];
+ edge2->Unset( _LayerEdge::MARKED );
+
+ // choose a closest edge among neighbors
+ gp_Pnt srcP( SMESH_TNodeXYZ( edge1->_nodes[0] ));
+ d1 = srcP.SquareDistance( SMESH_TNodeXYZ( edge2->_nodes[0] ));
+ for ( size_t j = 0; j < intEdgesDist.size(); ++j )
+ {
+ _LayerEdge * edgeJ = intEdgesDist[j].first;
+ if ( edge2->IsNeiborOnEdge( edgeJ ))
+ {
+ d2 = srcP.SquareDistance( SMESH_TNodeXYZ( edgeJ->_nodes[0] ));
+ ( d1 < d2 ? edgeJ : edge2 )->Set( _LayerEdge::MARKED );
+ }
+ }
+ intEdgesDist.push_back( make_pair( edge2, dist ));
+ // if ( Abs( d2 - d1 ) / Max( d2, d1 ) < 0.5 )
+ // {
+ // iClose = i + !( d2 < d1 );
+ // intEdges.push_back( ce._intEdges[iClose] );
+ // ce._intEdges[iClose]->Unset( _LayerEdge::MARKED );
+ // }
+ minIntDist = Min( edge1->_len * edge1->_lenFactor - segLen + dist, minIntDist );
+ }
+
+ //ce._edge = 0;
+
+ // compute new _normals
+ for ( size_t i = 0; i < intEdgesDist.size(); ++i )
+ {
+ _LayerEdge* edge2 = intEdgesDist[i].first;
+ double distWgt = edge1->_len / intEdgesDist[i].second;
+ // if ( edge1->Is( _LayerEdge::BLOCKED ) &&
+ // edge2->Is( _LayerEdge::BLOCKED )) continue;
+ if ( edge2->Is( _LayerEdge::MARKED )) continue;
+ edge2->Set( _LayerEdge::MARKED );
+
+ // get a new normal
+ gp_XYZ dir1 = edge1->_normal, dir2 = edge2->_normal;
+
+ double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
+ double wgt1 = ( cos1 + 0.001 ) / ( cos1 + cos2 + 0.002 );
+ double wgt2 = ( cos2 + 0.001 ) / ( cos1 + cos2 + 0.002 );
+ // double cos1 = Abs( edge1->_cosin ), cos2 = Abs( edge2->_cosin );
+ // double sgn1 = 0.1 * ( 1 + edge1->_cosin ), sgn2 = 0.1 * ( 1 + edge2->_cosin );
+ // double wgt1 = ( cos1 + sgn1 ) / ( cos1 + cos2 + sgn1 + sgn2 );
+ // double wgt2 = ( cos2 + sgn2 ) / ( cos1 + cos2 + sgn1 + sgn2 );
+ gp_XYZ newNormal = wgt1 * dir1 + wgt2 * dir2;
+ newNormal.Normalize();
+
+ // get new cosin
+ double newCos;
+ double sgn1 = edge1->_cosin / cos1, sgn2 = edge2->_cosin / cos2;
+ if ( cos1 < theMinSmoothCosin )
+ {
+ newCos = cos2 * sgn1;
+ }
+ else if ( cos2 > theMinSmoothCosin ) // both cos1 and cos2 > theMinSmoothCosin
+ {
+ newCos = ( wgt1 * cos1 + wgt2 * cos2 ) * edge1->_cosin / cos1;
+ }
+ else
+ {
+ newCos = edge1->_cosin;
+ }
+
+ e2neIt = edge2newEdge.insert( make_pair( edge1, zeroEdge )).first;
+ e2neIt->second._normal += distWgt * newNormal;
+ e2neIt->second._cosin = newCos;
+ e2neIt->second.SetMaxLen( 0.7 * minIntDist / edge1->_lenFactor );
+ if ( iter > 0 && sgn1 * sgn2 < 0 && edge1->_cosin < 0 )
+ e2neIt->second._normal += dir2;
+
+ e2neIt = edge2newEdge.insert( make_pair( edge2, zeroEdge )).first;
+ e2neIt->second._normal += distWgt * newNormal;
+ if ( Precision::IsInfinite( zeroEdge._maxLen ))
+ {
+ e2neIt->second._cosin = edge2->_cosin;
+ e2neIt->second.SetMaxLen( 1.3 * minIntDist / edge1->_lenFactor );
+ }
+ if ( iter > 0 && sgn1 * sgn2 < 0 && edge2->_cosin < 0 )
+ e2neIt->second._normal += dir1;
+ }
+ }
+
+ if ( edge2newEdge.empty() )
+ break; //return true;
+
+ dumpFunction(SMESH_Comment("updateNormals")<< data._index << "_" << stepNb << "_it" << iter);
+
+ // Update data of edges depending on a new _normal
+
+ data.UnmarkEdges();
+ for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
+ {
+ _LayerEdge* edge = e2neIt->first;
+ _LayerEdge& newEdge = e2neIt->second;
+ _EdgesOnShape* eos = data.GetShapeEdges( edge );
+ if ( edge->Is( _LayerEdge::BLOCKED && newEdge._maxLen > edge->_len ))
+ continue;
+
+ // Check if a new _normal is OK:
+ newEdge._normal.Normalize();
+ if ( !isNewNormalOk( data, *edge, newEdge._normal ))
+ {
+ if ( newEdge._maxLen < edge->_len && iter > 0 ) // limit _maxLen
+ {
+ edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true );
+ edge->SetMaxLen( newEdge._maxLen );
+ edge->SetNewLength( newEdge._maxLen, *eos, helper );
+ }
+ continue; // the new _normal is bad
+ }
+ // the new _normal is OK
+
+ // find shapes that need smoothing due to change of _normal
+ if ( edge->_cosin < theMinSmoothCosin &&
+ newEdge._cosin > theMinSmoothCosin )
+ {
+ if ( eos->_sWOL.IsNull() )
+ {
+ SMDS_ElemIteratorPtr fIt = edge->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face);
+ while ( fIt->more() )
+ shapesToSmooth.insert( data.GetShapeEdges( fIt->next()->getshapeId() ));
+ }
+ else // edge inflates along a FACE
+ {
+ TopoDS_Shape V = helper.GetSubShapeByNode( edge->_nodes[0], getMeshDS() );
+ PShapeIteratorPtr eIt = helper.GetAncestors( V, *_mesh, TopAbs_EDGE, &eos->_sWOL );
+ while ( const TopoDS_Shape* E = eIt->next() )
+ {
+ gp_Vec edgeDir = getEdgeDir( TopoDS::Edge( *E ), TopoDS::Vertex( V ));
+ double angle = edgeDir.Angle( newEdge._normal ); // [0,PI]
+ if ( angle < M_PI / 2 )
+ shapesToSmooth.insert( data.GetShapeEdges( *E ));
+ }
+ }
+ }
+
+ double len = edge->_len;
+ edge->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true );
+ edge->SetNormal( newEdge._normal );
+ edge->SetCosin( newEdge._cosin );
+ edge->SetNewLength( len, *eos, helper );
+ edge->Set( _LayerEdge::MARKED );
+ edge->Set( _LayerEdge::NORMAL_UPDATED );
+ edgesNoAnaSmooth.insert( eos );
+ }
+
+ // Update normals and other dependent data of not intersecting _LayerEdge's
+ // neighboring the intersecting ones
+
+ for ( e2neIt = edge2newEdge.begin(); e2neIt != edge2newEdge.end(); ++e2neIt )
+ {
+ _LayerEdge* edge1 = e2neIt->first;
+ _EdgesOnShape* eos1 = data.GetShapeEdges( edge1 );
+ if ( !edge1->Is( _LayerEdge::MARKED ))
+ continue;
+
+ if ( edge1->IsOnEdge() )
+ {
+ const SMDS_MeshNode * n1 = edge1->_2neibors->srcNode(0);
+ const SMDS_MeshNode * n2 = edge1->_2neibors->srcNode(1);
+ edge1->SetDataByNeighbors( n1, n2, *eos1, helper );
+ }
+
+ if ( !edge1->_2neibors || !eos1->_sWOL.IsNull() )
+ continue;
+ for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors
+ {
+ _LayerEdge* neighbor = edge1->_2neibors->_edges[j];
+ if ( neighbor->Is( _LayerEdge::MARKED ) /*edge2newEdge.count ( neighbor )*/)
+ continue; // j-th neighbor is also intersected
+ _LayerEdge* prevEdge = edge1;
+ const int nbSteps = 10;
+ for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction
+ {
+ if ( neighbor->Is( _LayerEdge::BLOCKED ) ||
+ neighbor->Is( _LayerEdge::MARKED ))
+ break;
+ _EdgesOnShape* eos = data.GetShapeEdges( neighbor );
+ if ( !eos ) continue;
+ _LayerEdge* nextEdge = neighbor;
+ if ( neighbor->_2neibors )
+ {
+ int iNext = 0;
+ nextEdge = neighbor->_2neibors->_edges[iNext];
+ if ( nextEdge == prevEdge )
+ nextEdge = neighbor->_2neibors->_edges[ ++iNext ];
+ }
+ double r = double(step-1)/nbSteps/(iter+1);
+ if ( !nextEdge->_2neibors )
+ r = Min( r, 0.5 );
+
+ gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r);
+ newNorm.Normalize();
+ if ( !isNewNormalOk( data, *neighbor, newNorm ))
+ break;
+
+ double len = neighbor->_len;
+ neighbor->InvalidateStep( stepNb + 1, *eos, /*restoreLength=*/true );
+ neighbor->SetNormal( newNorm );
+ neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) );
+ if ( neighbor->_2neibors )
+ neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], *eos, helper );
+ neighbor->SetNewLength( len, *eos, helper );
+ neighbor->Set( _LayerEdge::MARKED );
+ neighbor->Set( _LayerEdge::NORMAL_UPDATED );
+ edgesNoAnaSmooth.insert( eos );
+
+ if ( !neighbor->_2neibors )
+ break; // neighbor is on VERTEX
+
+ // goto the next neighbor
+ prevEdge = neighbor;
+ neighbor = nextEdge;
+ }
+ }
+ }
+ dumpFunctionEnd();
+ } // iterations
+
+ data.AddShapesToSmooth( shapesToSmooth, &edgesNoAnaSmooth );
+
+ return true;
+}
+
+//================================================================================
+/*!
+ * \brief Check if a new normal is OK
+ */
+//================================================================================
+
+bool _ViscousBuilder::isNewNormalOk( _SolidData& data,
+ _LayerEdge& edge,
+ const gp_XYZ& newNormal)
+{
+ // check a min angle between the newNormal and surrounding faces
+ vector<_Simplex> simplices;
+ SMESH_TNodeXYZ n0( edge._nodes[0] ), n1, n2;
+ _Simplex::GetSimplices( n0._node, simplices, data._ignoreFaceIds, &data );
+ double newMinDot = 1, curMinDot = 1;
+ for ( size_t i = 0; i < simplices.size(); ++i )
+ {
+ n1.Set( simplices[i]._nPrev );
+ n2.Set( simplices[i]._nNext );
+ gp_XYZ normFace = ( n1 - n0 ) ^ ( n2 - n0 );
+ double normLen2 = normFace.SquareModulus();
+ if ( normLen2 < std::numeric_limits<double>::min() )
+ continue;
+ normFace /= Sqrt( normLen2 );
+ newMinDot = Min( newNormal * normFace, newMinDot );
+ curMinDot = Min( edge._normal * normFace, curMinDot );
+ }
+ bool ok = true;
+ if ( newMinDot < 0.5 )
+ {
+ ok = ( newMinDot >= curMinDot * 0.9 );
+ //return ( newMinDot >= ( curMinDot * ( 0.8 + 0.1 * edge.NbSteps() )));
+ // double initMinDot2 = 1. - edge._cosin * edge._cosin;
+ // return ( newMinDot * newMinDot ) >= ( 0.8 * initMinDot2 );
+ }
+
+ return ok;
+}
+
+//================================================================================
+/*!
+ * \brief Modify normals of _LayerEdge's on FACE to reflex smoothing
+ */
+//================================================================================
+
+bool _ViscousBuilder::updateNormalsOfSmoothed( _SolidData& data,
+ SMESH_MesherHelper& helper,
+ const int nbSteps,
+ const double stepSize )
+{
+ if ( data._nbShapesToSmooth == 0 || nbSteps == 0 )
+ return true; // no shapes needing smoothing
+
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eos = data._edgesOnShape[ iS ];
+ if ( //!eos._toSmooth || _eosC1 have _toSmooth == false
+ !eos._hyp.ToSmooth() ||
+ eos.ShapeType() != TopAbs_FACE ||
+ eos._edges.empty() )
+ continue;
+
+ bool toSmooth = ( eos._edges[ 0 ]->NbSteps() >= nbSteps+1 );
+ if ( !toSmooth ) continue;
+
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* edge = eos._edges[i];
+ if ( !edge->Is( _LayerEdge::SMOOTHED ))
+ continue;
+ if ( edge->Is( _LayerEdge::DIFFICULT ) && nbSteps != 1 )
+ continue;
+
+ const gp_XYZ& pPrev = edge->PrevPos();
+ const gp_XYZ& pLast = edge->_pos.back();
+ gp_XYZ stepVec = pLast - pPrev;
+ double realStepSize = stepVec.Modulus();
+ if ( realStepSize < numeric_limits<double>::min() )
+ continue;
+
+ edge->_lenFactor = realStepSize / stepSize;
+ edge->_normal = stepVec / realStepSize;
+ edge->Set( _LayerEdge::NORMAL_UPDATED );
+ }
+ }
+
+ return true;
+}
+
+//================================================================================
+/*!
+ * \brief Modify normals of _LayerEdge's on C1 VERTEXes
+ */
+//================================================================================
+
+void _ViscousBuilder::updateNormalsOfC1Vertices( _SolidData& data )
+{
+ for ( size_t iS = 0; iS < data._edgesOnShape.size(); ++iS )
+ {
+ _EdgesOnShape& eov = data._edgesOnShape[ iS ];
+ if ( eov._eosC1.empty() ||
+ eov.ShapeType() != TopAbs_VERTEX ||
+ eov._edges.empty() )
+ continue;
+
+ gp_XYZ newNorm = eov._edges[0]->_normal;
+ double curThick = eov._edges[0]->_len * eov._edges[0]->_lenFactor;
+ bool normChanged = false;
+
+ for ( size_t i = 0; i < eov._eosC1.size(); ++i )
+ {
+ _EdgesOnShape* eoe = eov._eosC1[i];
+ const TopoDS_Edge& e = TopoDS::Edge( eoe->_shape );
+ const double eLen = SMESH_Algo::EdgeLength( e );
+ TopoDS_Shape oppV = SMESH_MesherHelper::IthVertex( 0, e );
+ if ( oppV.IsSame( eov._shape ))
+ oppV = SMESH_MesherHelper::IthVertex( 1, e );
+ _EdgesOnShape* eovOpp = data.GetShapeEdges( oppV );
+ if ( !eovOpp || eovOpp->_edges.empty() ) continue;
+ if ( eov._edges[0]->Is( _LayerEdge::BLOCKED )) continue;
+
+ double curThickOpp = eovOpp->_edges[0]->_len * eovOpp->_edges[0]->_lenFactor;
+ if ( curThickOpp + curThick < eLen )
+ continue;
+
+ double wgt = 2. * curThick / eLen;
+ newNorm += wgt * eovOpp->_edges[0]->_normal;
+ normChanged = true;
+ }
+ if ( normChanged )
+ {
+ eov._edges[0]->SetNormal( newNorm.Normalized() );
+ eov._edges[0]->Set( _LayerEdge::NORMAL_UPDATED );
+ }
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Modify normals of _LayerEdge's on _ConvexFace's
+ */
+//================================================================================
+
+bool _ViscousBuilder::updateNormalsOfConvexFaces( _SolidData& data,
+ SMESH_MesherHelper& helper,
+ int stepNb )
+{
+ SMESHDS_Mesh* meshDS = helper.GetMeshDS();
+ bool isOK;
+
+ map< TGeomID, _ConvexFace >::iterator id2face = data._convexFaces.begin();
+ for ( ; id2face != data._convexFaces.end(); ++id2face )
+ {
+ _ConvexFace & convFace = (*id2face).second;
+ convFace._normalsFixedOnBorders = false; // to update at each inflation step
+
+ if ( convFace._normalsFixed )
+ continue; // already fixed
+ if ( convFace.CheckPrisms() )
+ continue; // nothing to fix
+
+ convFace._normalsFixed = true;
+
+ BRepAdaptor_Surface surface ( convFace._face, false );
+ BRepLProp_SLProps surfProp( surface, 2, 1e-6 );
+
+ // check if the convex FACE is of spherical shape
+
+ Bnd_B3d centersBox; // bbox of centers of curvature of _LayerEdge's on VERTEXes
+ Bnd_B3d nodesBox;
+ gp_Pnt center;
+
+ map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.begin();
+ for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
+ {
+ _EdgesOnShape& eos = *(id2eos->second);
+ if ( eos.ShapeType() == TopAbs_VERTEX )
+ {
+ _LayerEdge* ledge = eos._edges[ 0 ];
+ if ( convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
+ centersBox.Add( center );
+ }
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ nodesBox.Add( SMESH_TNodeXYZ( eos._edges[ i ]->_nodes[0] ));
+ }
+ if ( centersBox.IsVoid() )
+ {
+ debugMsg( "Error: centersBox.IsVoid()" );
+ return false;
+ }
+ const bool isSpherical =
+ ( centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
+
+ int nbEdges = helper.Count( convFace._face, TopAbs_EDGE, /*ignoreSame=*/false );
+ vector < _CentralCurveOnEdge > centerCurves( nbEdges );
+
+ if ( isSpherical )
+ {
+ // set _LayerEdge::_normal as average of all normals
+
+ // WARNING: different density of nodes on EDGEs is not taken into account that
+ // can lead to an improper new normal
+
+ gp_XYZ avgNormal( 0,0,0 );
+ nbEdges = 0;
+ id2eos = convFace._subIdToEOS.begin();
+ for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
+ {
+ _EdgesOnShape& eos = *(id2eos->second);
+ // set data of _CentralCurveOnEdge
+ if ( eos.ShapeType() == TopAbs_EDGE )
+ {
+ _CentralCurveOnEdge& ceCurve = centerCurves[ nbEdges++ ];
+ ceCurve.SetShapes( TopoDS::Edge( eos._shape ), convFace, data, helper );
+ if ( !eos._sWOL.IsNull() )
+ ceCurve._adjFace.Nullify();
+ else
+ ceCurve._ledges.insert( ceCurve._ledges.end(),
+ eos._edges.begin(), eos._edges.end());
+ }
+ // summarize normals
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ avgNormal += eos._edges[ i ]->_normal;
+ }
+ double normSize = avgNormal.SquareModulus();
+ if ( normSize < 1e-200 )
+ {
+ debugMsg( "updateNormalsOfConvexFaces(): zero avgNormal" );
+ return false;
+ }
+ avgNormal /= Sqrt( normSize );
+
+ // compute new _LayerEdge::_cosin on EDGEs
+ double avgCosin = 0;
+ int nbCosin = 0;
+ gp_Vec inFaceDir;
+ for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+ {
+ _CentralCurveOnEdge& ceCurve = centerCurves[ iE ];
+ if ( ceCurve._adjFace.IsNull() )
+ continue;
+ for ( size_t iLE = 0; iLE < ceCurve._ledges.size(); ++iLE )
+ {
+ const SMDS_MeshNode* node = ceCurve._ledges[ iLE ]->_nodes[0];
+ inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
+ if ( isOK )
+ {
+ double angle = inFaceDir.Angle( avgNormal ); // [0,PI]
+ ceCurve._ledges[ iLE ]->_cosin = Cos( angle );
+ avgCosin += ceCurve._ledges[ iLE ]->_cosin;
+ nbCosin++;
+ }
+ }
+ }
+ if ( nbCosin > 0 )
+ avgCosin /= nbCosin;
+
+ // set _LayerEdge::_normal = avgNormal
+ id2eos = convFace._subIdToEOS.begin();
+ for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
+ {
+ _EdgesOnShape& eos = *(id2eos->second);
+ if ( eos.ShapeType() != TopAbs_EDGE )
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ eos._edges[ i ]->_cosin = avgCosin;
+
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ eos._edges[ i ]->SetNormal( avgNormal );
+ eos._edges[ i ]->Set( _LayerEdge::NORMAL_UPDATED );
+ }
+ }
+ }
+ else // if ( isSpherical )
+ {
+ // We suppose that centers of curvature at all points of the FACE
+ // lie on some curve, let's call it "central curve". For all _LayerEdge's
+ // having a common center of curvature we define the same new normal
+ // as a sum of normals of _LayerEdge's on EDGEs among them.
+
+ // get all centers of curvature for each EDGE
+
+ helper.SetSubShape( convFace._face );
+ _LayerEdge* vertexLEdges[2], **edgeLEdge, **edgeLEdgeEnd;
+
+ TopExp_Explorer edgeExp( convFace._face, TopAbs_EDGE );
+ for ( int iE = 0; edgeExp.More(); edgeExp.Next(), ++iE )
+ {
+ const TopoDS_Edge& edge = TopoDS::Edge( edgeExp.Current() );
+
+ // set adjacent FACE
+ centerCurves[ iE ].SetShapes( edge, convFace, data, helper );
+
+ // get _LayerEdge's of the EDGE
+ TGeomID edgeID = meshDS->ShapeToIndex( edge );
+ _EdgesOnShape* eos = data.GetShapeEdges( edgeID );
+ if ( !eos || eos->_edges.empty() )
+ {
+ // no _LayerEdge's on EDGE, use _LayerEdge's on VERTEXes
+ for ( int iV = 0; iV < 2; ++iV )
+ {
+ TopoDS_Vertex v = helper.IthVertex( iV, edge );
+ TGeomID vID = meshDS->ShapeToIndex( v );
+ eos = data.GetShapeEdges( vID );
+ vertexLEdges[ iV ] = eos->_edges[ 0 ];
+ }
+ edgeLEdge = &vertexLEdges[0];
+ edgeLEdgeEnd = edgeLEdge + 2;
+
+ centerCurves[ iE ]._adjFace.Nullify();
+ }
+ else
+ {
+ if ( ! eos->_toSmooth )
+ data.SortOnEdge( edge, eos->_edges );
+ edgeLEdge = &eos->_edges[ 0 ];
+ edgeLEdgeEnd = edgeLEdge + eos->_edges.size();
+ vertexLEdges[0] = eos->_edges.front()->_2neibors->_edges[0];
+ vertexLEdges[1] = eos->_edges.back() ->_2neibors->_edges[1];
+
+ if ( ! eos->_sWOL.IsNull() )
+ centerCurves[ iE ]._adjFace.Nullify();
+ }
+
+ // Get curvature centers
+
+ centersBox.Clear();
+
+ if ( edgeLEdge[0]->IsOnEdge() &&
+ convFace.GetCenterOfCurvature( vertexLEdges[0], surfProp, helper, center ))
+ { // 1st VERTEX
+ centerCurves[ iE ].Append( center, vertexLEdges[0] );
+ centersBox.Add( center );
+ }
+ for ( ; edgeLEdge < edgeLEdgeEnd; ++edgeLEdge )
+ if ( convFace.GetCenterOfCurvature( *edgeLEdge, surfProp, helper, center ))
+ { // EDGE or VERTEXes
+ centerCurves[ iE ].Append( center, *edgeLEdge );
+ centersBox.Add( center );
+ }
+ if ( edgeLEdge[-1]->IsOnEdge() &&
+ convFace.GetCenterOfCurvature( vertexLEdges[1], surfProp, helper, center ))
+ { // 2nd VERTEX
+ centerCurves[ iE ].Append( center, vertexLEdges[1] );
+ centersBox.Add( center );
+ }
+ centerCurves[ iE ]._isDegenerated =
+ ( centersBox.IsVoid() || centersBox.SquareExtent() < 1e-6 * nodesBox.SquareExtent() );
+
+ } // loop on EDGES of convFace._face to set up data of centerCurves
+
+ // Compute new normals for _LayerEdge's on EDGEs
+
+ double avgCosin = 0;
+ int nbCosin = 0;
+ gp_Vec inFaceDir;
+ for ( size_t iE1 = 0; iE1 < centerCurves.size(); ++iE1 )
+ {
+ _CentralCurveOnEdge& ceCurve = centerCurves[ iE1 ];
+ if ( ceCurve._isDegenerated )
+ continue;
+ const vector< gp_Pnt >& centers = ceCurve._curvaCenters;
+ vector< gp_XYZ > & newNormals = ceCurve._normals;
+ for ( size_t iC1 = 0; iC1 < centers.size(); ++iC1 )
+ {
+ isOK = false;
+ for ( size_t iE2 = 0; iE2 < centerCurves.size() && !isOK; ++iE2 )
+ {
+ if ( iE1 != iE2 )
+ isOK = centerCurves[ iE2 ].FindNewNormal( centers[ iC1 ], newNormals[ iC1 ]);
+ }
+ if ( isOK && !ceCurve._adjFace.IsNull() )
+ {
+ // compute new _LayerEdge::_cosin
+ const SMDS_MeshNode* node = ceCurve._ledges[ iC1 ]->_nodes[0];
+ inFaceDir = getFaceDir( ceCurve._adjFace, ceCurve._edge, node, helper, isOK );
+ if ( isOK )
+ {
+ double angle = inFaceDir.Angle( newNormals[ iC1 ] ); // [0,PI]
+ ceCurve._ledges[ iC1 ]->_cosin = Cos( angle );
+ avgCosin += ceCurve._ledges[ iC1 ]->_cosin;
+ nbCosin++;
+ }
+ }
+ }
+ }
+ // set new normals to _LayerEdge's of NOT degenerated central curves
+ for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+ {
+ if ( centerCurves[ iE ]._isDegenerated )
+ continue;
+ for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
+ {
+ centerCurves[ iE ]._ledges[ iLE ]->SetNormal( centerCurves[ iE ]._normals[ iLE ]);
+ centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
+ }
+ }
+ // set new normals to _LayerEdge's of degenerated central curves
+ for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+ {
+ if ( !centerCurves[ iE ]._isDegenerated ||
+ centerCurves[ iE ]._ledges.size() < 3 )
+ continue;
+ // new normal is an average of new normals at VERTEXes that
+ // was computed on non-degenerated _CentralCurveOnEdge's
+ gp_XYZ newNorm = ( centerCurves[ iE ]._ledges.front()->_normal +
+ centerCurves[ iE ]._ledges.back ()->_normal );
+ double sz = newNorm.Modulus();
+ if ( sz < 1e-200 )
+ continue;
+ newNorm /= sz;
+ double newCosin = ( 0.5 * centerCurves[ iE ]._ledges.front()->_cosin +
+ 0.5 * centerCurves[ iE ]._ledges.back ()->_cosin );
+ for ( size_t iLE = 1, nb = centerCurves[ iE ]._ledges.size() - 1; iLE < nb; ++iLE )
+ {
+ centerCurves[ iE ]._ledges[ iLE ]->SetNormal( newNorm );
+ centerCurves[ iE ]._ledges[ iLE ]->_cosin = newCosin;
+ centerCurves[ iE ]._ledges[ iLE ]->Set( _LayerEdge::NORMAL_UPDATED );
+ }
+ }
+
+ // Find new normals for _LayerEdge's based on FACE
+
+ if ( nbCosin > 0 )
+ avgCosin /= nbCosin;
+ const TGeomID faceID = meshDS->ShapeToIndex( convFace._face );
+ map< TGeomID, _EdgesOnShape* >::iterator id2eos = convFace._subIdToEOS.find( faceID );
+ if ( id2eos != convFace._subIdToEOS.end() )
+ {
+ int iE = 0;
+ gp_XYZ newNorm;
+ _EdgesOnShape& eos = * ( id2eos->second );
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* ledge = eos._edges[ i ];
+ if ( !convFace.GetCenterOfCurvature( ledge, surfProp, helper, center ))
+ continue;
+ for ( size_t i = 0; i < centerCurves.size(); ++i, ++iE )
+ {
+ iE = iE % centerCurves.size();
+ if ( centerCurves[ iE ]._isDegenerated )
+ continue;
+ newNorm.SetCoord( 0,0,0 );
+ if ( centerCurves[ iE ].FindNewNormal( center, newNorm ))
+ {
+ ledge->SetNormal( newNorm );
+ ledge->_cosin = avgCosin;
+ ledge->Set( _LayerEdge::NORMAL_UPDATED );
+ break;
+ }
+ }
+ }
+ }
+
+ } // not a quasi-spherical FACE
+
+ // Update _LayerEdge's data according to a new normal
+
+ dumpFunction(SMESH_Comment("updateNormalsOfConvexFaces")<<data._index
+ <<"_F"<<meshDS->ShapeToIndex( convFace._face ));
+
+ id2eos = convFace._subIdToEOS.begin();
+ for ( ; id2eos != convFace._subIdToEOS.end(); ++id2eos )
+ {
+ _EdgesOnShape& eos = * ( id2eos->second );
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* & ledge = eos._edges[ i ];
+ double len = ledge->_len;
+ ledge->InvalidateStep( stepNb + 1, eos, /*restoreLength=*/true );
+ ledge->SetCosin( ledge->_cosin );
+ ledge->SetNewLength( len, eos, helper );
+ }
+ if ( eos.ShapeType() != TopAbs_FACE )
+ for ( size_t i = 0; i < eos._edges.size(); ++i )
+ {
+ _LayerEdge* ledge = eos._edges[ i ];
+ for ( size_t iN = 0; iN < ledge->_neibors.size(); ++iN )
+ {
+ _LayerEdge* neibor = ledge->_neibors[iN];
+ if ( neibor->_nodes[0]->GetPosition()->GetDim() == 2 )
+ {
+ neibor->Set( _LayerEdge::NEAR_BOUNDARY );
+ neibor->Set( _LayerEdge::MOVED );
+ neibor->SetSmooLen( neibor->_len );
+ }
+ }
+ }
+ } // loop on sub-shapes of convFace._face
+
+ // Find FACEs adjacent to convFace._face that got necessity to smooth
+ // as a result of normals modification
+
+ set< _EdgesOnShape* > adjFacesToSmooth;
+ for ( size_t iE = 0; iE < centerCurves.size(); ++iE )
+ {
+ if ( centerCurves[ iE ]._adjFace.IsNull() ||
+ centerCurves[ iE ]._adjFaceToSmooth )
+ continue;
+ for ( size_t iLE = 0; iLE < centerCurves[ iE ]._ledges.size(); ++iLE )
+ {
+ if ( centerCurves[ iE ]._ledges[ iLE ]->_cosin > theMinSmoothCosin )
+ {
+ adjFacesToSmooth.insert( data.GetShapeEdges( centerCurves[ iE ]._adjFace ));
+ break;
+ }
+ }
+ }
+ data.AddShapesToSmooth( adjFacesToSmooth );
+
+ dumpFunctionEnd();
+
+
+ } // loop on data._convexFaces
+
+ return true;
+}
+
+//================================================================================
+/*!
+ * \brief Return max curvature of a FACE
+ */
+//================================================================================
+
+double _ConvexFace::GetMaxCurvature( _SolidData& data,
+ _EdgesOnShape& eof,
+ BRepLProp_SLProps& surfProp,
+ SMESH_MesherHelper& helper)
+{
+ double maxCurvature = 0;
+
+ TopoDS_Face F = TopoDS::Face( eof._shape );
+
+ const int nbTestPnt = 5;
+ const double oriFactor = ( F.Orientation() == TopAbs_REVERSED ? +1. : -1. );
+ SMESH_subMeshIteratorPtr smIt = eof._subMesh->getDependsOnIterator(/*includeSelf=*/true);
+ while ( smIt->more() )
+ {
+ SMESH_subMesh* sm = smIt->next();
+ const TGeomID subID = sm->GetId();
+
+ // find _LayerEdge's of a sub-shape
+ _EdgesOnShape* eos;
+ if (( eos = data.GetShapeEdges( subID )))
+ this->_subIdToEOS.insert( make_pair( subID, eos ));
+ else
+ continue;
+
+ // check concavity and curvature and limit data._stepSize
+ const double minCurvature =
+ 1. / ( eos->_hyp.GetTotalThickness() * ( 1 + theThickToIntersection ));
+ size_t iStep = Max( 1, eos->_edges.size() / nbTestPnt );
+ for ( size_t i = 0; i < eos->_edges.size(); i += iStep )
+ {
+ gp_XY uv = helper.GetNodeUV( F, eos->_edges[ i ]->_nodes[0] );
+ surfProp.SetParameters( uv.X(), uv.Y() );
+ if ( surfProp.IsCurvatureDefined() )
+ {
+ double curvature = Max( surfProp.MaxCurvature() * oriFactor,
+ surfProp.MinCurvature() * oriFactor );
+ maxCurvature = Max( maxCurvature, curvature );
+
+ if ( curvature > minCurvature )
+ this->_isTooCurved = true;
+ }
+ }
+ } // loop on sub-shapes of the FACE
+
+ return maxCurvature;
+}
+
+//================================================================================
+/*!
+ * \brief Finds a center of curvature of a surface at a _LayerEdge
+ */
+//================================================================================
+
+bool _ConvexFace::GetCenterOfCurvature( _LayerEdge* ledge,
+ BRepLProp_SLProps& surfProp,
+ SMESH_MesherHelper& helper,
+ gp_Pnt & center ) const
+{
+ gp_XY uv = helper.GetNodeUV( _face, ledge->_nodes[0] );
+ surfProp.SetParameters( uv.X(), uv.Y() );
+ if ( !surfProp.IsCurvatureDefined() )
+ return false;
+
+ const double oriFactor = ( _face.Orientation() == TopAbs_REVERSED ? +1. : -1. );
+ double surfCurvatureMax = surfProp.MaxCurvature() * oriFactor;
+ double surfCurvatureMin = surfProp.MinCurvature() * oriFactor;
+ if ( surfCurvatureMin > surfCurvatureMax )
+ center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMin * oriFactor );
+ else
+ center = surfProp.Value().Translated( surfProp.Normal().XYZ() / surfCurvatureMax * oriFactor );
+
+ return true;
+}
+
+//================================================================================
+/*!
+ * \brief Check that prisms are not distorted
+ */
+//================================================================================
+
+bool _ConvexFace::CheckPrisms() const
+{
+ double vol = 0;
+ for ( size_t i = 0; i < _simplexTestEdges.size(); ++i )
+ {
+ const _LayerEdge* edge = _simplexTestEdges[i];
+ SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() );
+ for ( size_t j = 0; j < edge->_simplices.size(); ++j )
+ if ( !edge->_simplices[j].IsForward( edge->_nodes[0], tgtXYZ, vol ))
+ {
+ debugMsg( "Bad simplex of _simplexTestEdges ("
+ << " "<< edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID()
+ << " "<< edge->_simplices[j]._nPrev->GetID()
+ << " "<< edge->_simplices[j]._nNext->GetID() << " )" );
+ return false;
+ }
+ }
+ return true;
+}
+
+//================================================================================
+/*!
+ * \brief Try to compute a new normal by interpolating normals of _LayerEdge's
+ * stored in this _CentralCurveOnEdge.
+ * \param [in] center - curvature center of a point of another _CentralCurveOnEdge.
+ * \param [in,out] newNormal - current normal at this point, to be redefined
+ * \return bool - true if succeeded.
+ */
+//================================================================================
+
+bool _CentralCurveOnEdge::FindNewNormal( const gp_Pnt& center, gp_XYZ& newNormal )
+{
+ if ( this->_isDegenerated )
+ return false;
+
+ // find two centers the given one lies between
+
+ for ( size_t i = 0, nb = _curvaCenters.size()-1; i < nb; ++i )
+ {
+ double sl2 = 1.001 * _segLength2[ i ];
+
+ double d1 = center.SquareDistance( _curvaCenters[ i ]);
+ if ( d1 > sl2 )
+ continue;
+
+ double d2 = center.SquareDistance( _curvaCenters[ i+1 ]);
+ if ( d2 > sl2 || d2 + d1 < 1e-100 )
+ continue;
+
+ d1 = Sqrt( d1 );
+ d2 = Sqrt( d2 );
+ double r = d1 / ( d1 + d2 );
+ gp_XYZ norm = (( 1. - r ) * _ledges[ i ]->_normal +
+ ( r ) * _ledges[ i+1 ]->_normal );
+ norm.Normalize();
+
+ newNormal += norm;
+ double sz = newNormal.Modulus();
+ if ( sz < 1e-200 )
+ break;
+ newNormal /= sz;
+ return true;
+ }
+ return false;
+}
+
+//================================================================================
+/*!
+ * \brief Set shape members
+ */
+//================================================================================
+
+void _CentralCurveOnEdge::SetShapes( const TopoDS_Edge& edge,
+ const _ConvexFace& convFace,
+ _SolidData& data,
+ SMESH_MesherHelper& helper)
+{
+ _edge = edge;
+
+ PShapeIteratorPtr fIt = helper.GetAncestors( edge, *helper.GetMesh(), TopAbs_FACE );
+ while ( const TopoDS_Shape* F = fIt->next())
+ if ( !convFace._face.IsSame( *F ))
+ {
+ _adjFace = TopoDS::Face( *F );
+ _adjFaceToSmooth = false;
+ // _adjFace already in a smoothing queue ?
+ if ( _EdgesOnShape* eos = data.GetShapeEdges( _adjFace ))
+ _adjFaceToSmooth = eos->_toSmooth;
+ break;
+ }
+}
+
+//================================================================================
+/*!
+ * \brief Looks for intersection of it's last segment with faces
+ * \param distance - returns shortest distance from the last node to intersection
+ */
+//================================================================================
+
+bool _LayerEdge::FindIntersection( SMESH_ElementSearcher& searcher,
+ double & distance,
+ const double& epsilon,
+ _EdgesOnShape& eos,
+ const SMDS_MeshElement** intFace)
+{
+ vector< const SMDS_MeshElement* > suspectFaces;
+ double segLen;
+ gp_Ax1 lastSegment = LastSegment( segLen, eos );
+ searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces );
+
+ bool segmentIntersected = false;
+ distance = Precision::Infinite();
+ int iFace = -1; // intersected face
+ for ( size_t j = 0 ; j < suspectFaces.size() /*&& !segmentIntersected*/; ++j )
+ {
+ const SMDS_MeshElement* face = suspectFaces[j];
+ if ( face->GetNodeIndex( _nodes.back() ) >= 0 ||
+ face->GetNodeIndex( _nodes[0] ) >= 0 )
+ continue; // face sharing _LayerEdge node
+ const int nbNodes = face->NbCornerNodes();
+ bool intFound = false;
+ double dist;
+ SMDS_MeshElement::iterator nIt = face->begin_nodes();
+ if ( nbNodes == 3 )
+ {
+ intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon );
+ }
+ else
+ {
+ const SMDS_MeshNode* tria[3];
+ tria[0] = *nIt++;
+ tria[1] = *nIt++;
+ for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 )
+ {
+ tria[2] = *nIt++;
+ intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon );
+ tria[1] = tria[2];
+ }
+ }
+ if ( intFound )
+ {
+ if ( dist < segLen*(1.01) && dist > -(_len*_lenFactor-segLen) )
+ segmentIntersected = true;
+ if ( distance > dist )
+ distance = dist, iFace = j;
+ }
+ }
+ if ( intFace ) *intFace = ( iFace != -1 ) ? suspectFaces[iFace] : 0;
+
+ distance -= segLen;
+
+ if ( segmentIntersected )
+ {
+#ifdef __myDEBUG
+ SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes();
+ gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * ( distance+segLen ));
+ cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID()
+ << ", intersection with face ("
+ << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()
+ << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z()
+ << ") distance = " << distance << endl;
+#endif
+ }
+
+ return segmentIntersected;
+}
+
+//================================================================================
+/*!
+ * \brief Returns a point used to check orientation of _simplices