+ if ( _offPoints.empty() )
+ return false;
+
+ // move _offPoints to positions along normals of _LayerEdge's
+
+ _LayerEdge* e[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
+ if ( e[0]->Is( _LayerEdge::NORMAL_UPDATED )) setNormalOnV( 0, helper );
+ if ( e[1]->Is( _LayerEdge::NORMAL_UPDATED )) setNormalOnV( 1, helper );
+ _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 );
+
+ _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
+
+ gp_Pnt pExtreme[2], pProj[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 = false;
+ double uOnSeg, uOnSegDiff, uOnSegBestDiff = Precision::Infinite(), uOnSegPrevDiff = 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();
+ uOnSegDiff = Abs( uOnSeg - 0.5 );
+ projected = ( uOnSegDiff <= 0.5 );
+ if ( uOnSegDiff < uOnSegBestDiff )
+ {
+ _iSeg[ is2nd ] = i;
+ pProj[ is2nd ] = _offPoints[i]._xyz + ( v01 * uOnSeg ).XYZ();
+ uOnSegBestDiff = uOnSegDiff;
+ }
+ else if ( uOnSegDiff > uOnSegPrevDiff )
+ {
+ if ( ++nbWorse > 3 ) // avoid projection to the middle of a closed EDGE
+ break;
+ }
+ uOnSegPrevDiff = uOnSegDiff;
+ 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;
+ }
+
+ // compute normalized length of the offset segments located between the projections
+
+ 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] );
+ }
+ len[ nbSeg ] -= pProj[ 1 ].Distance( _offPoints[ _iSeg[1]+1 ]._xyz );
+
+ double d0 = pProj[0].Distance( pExtreme[0]);
+ double d1 = pProj[1].Distance( pExtreme[1]);
+ double fullLen = len.back() - d0 - d1;
+ for ( iSeg = 0; iSeg < len.size(); ++iSeg )
+ len[iSeg] = ( len[iSeg] - d0 ) / fullLen;
+
+ // temporary replace extreme _offPoints by pExtreme
+ gp_XYZ op[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();
+
+ // distribute tgt nodes of _LayerEdge's between the projections
+
+ iSeg = 0;
+ for ( size_t i = 0; i < _eos._edges.size(); ++i )
+ {
+ if ( _eos._edges[i]->Is( _LayerEdge::BLOCKED )) 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._edges[i]->_pos.back() = p;
+ }
+ else // project a new node position to a FACE
+ {
+ gp_Pnt2d uv ( _eos._edges[i]->_pos.back().X(), _eos._edges[i]->_pos.back().Y() );
+ gp_Pnt2d uv2( surface->NextValueOfUV( uv, p, fTol ));
+
+ p = surface->Value( uv2 ).XYZ();
+ _eos._edges[i]->_pos.back().SetCoord( uv2.X(), uv2.Y(), 0 );
+ }
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( _eos._edges[i]->_nodes.back() );
+ tgtNode->setXYZ( p.X(), p.Y(), p.Z() );
+ dumpMove( tgtNode );
+ }
+
+ _offPoints[ _iSeg[0] ]._xyz = op[0];
+ _offPoints[ _iSeg[1]+1 ]._xyz = op[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 );
+
+ SMESH_MesherHelper& helper = data.GetHelper();
+
+ // compute normalized param of _eos._edges on EDGE
+ _leParams.resize( _eos._edges.size() + 1 );
+ {
+ double curLen, prevLen = _leParams[0] = 1.0;
+ 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 = prevLen * _eos._edges[i]->_2neibors->_wgt[1] / _eos._edges[i]->_2neibors->_wgt[0];
+ curLen = p.Distance( pPrev );
+ _leParams[i+1] = _leParams[i] + curLen;
+ prevLen = 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;
+ }
+
+ // find intersection of neighbor _LayerEdge's to limit _maxLen
+ // according to EDGE curvature (IPAL52648)
+ _LayerEdge* e0 = _eos._edges[0];
+ for ( size_t i = 1; i < _eos._edges.size(); ++i )
+ {
+ _LayerEdge* ei = _eos._edges[i];
+ gp_XYZ plnNorm = e0->_normal ^ ei->_normal;
+ gp_XYZ perp0 = e0->_normal ^ plnNorm;
+ double dot0i = perp0 * ei->_normal;
+ if ( Abs( dot0i ) > std::numeric_limits<double>::min() )
+ {
+ SMESH_TNodeXYZ srci( ei->_nodes[0] ), src0( e0->_nodes[0] );
+ double ui = ( perp0 * ( src0 - srci )) / dot0i;
+ if ( ui > 0 )
+ {
+ ei->_maxLen = Min( ei->_maxLen, 0.75 * ui / ei->_lenFactor );
+ if ( ei->_maxLen < ei->_len )
+ {
+ ei->InvalidateStep( ei->NbSteps(), _eos, /*restoreLength=*/true );
+ ei->SetNewLength( ei->_maxLen, _eos, helper );
+ ei->Block( data );
+ }
+ gp_Pnt pi = srci + ei->_normal * ui;
+ double u0 = pi.Distance( src0 );
+ e0->_maxLen = Min( e0->_maxLen, 0.75 * u0 / e0->_lenFactor );
+ if ( e0->_maxLen < e0->_len )
+ {
+ e0->InvalidateStep( e0->NbSteps(), _eos, /*restoreLength=*/true );
+ e0->SetNewLength( e0->_maxLen, _eos, helper );
+ e0->Block( data );
+ }
+ }
+ }
+ e0 = ei;
+ }
+
+ if ( isAnalytic() )
+ return;
+
+ // divide E to have offset segments with low deflection
+ BRepAdaptor_Curve c3dAdaptor( E );
+ const double curDeflect = 0.1; //0.3; // 0.01; // Curvature deflection
+ const double angDeflect = 0.1; //0.2; // 0.09; // Angular deflection
+ GCPnts_TangentialDeflection discret(c3dAdaptor, angDeflect, curDeflect);
+ if ( discret.NbPoints() <= 2 )
+ {
+ _anaCurve = new Geom_Line( gp::OX() ); // only type does matter
+ return;
+ }
+
+ const double edgeLen = SMESH_Algo::EdgeLength( E );
+ const double u0 = c3dAdaptor.FirstParameter();
+ _offPoints.resize( discret.NbPoints() );
+ for ( size_t i = 0; i < _offPoints.size(); i++ )
+ {
+ _offPoints[i]._xyz = discret.Value( i+1 ).XYZ();
+ // use OffPnt::_len to TEMPORARY store normalized param of an offset point
+ double u = discret.Parameter( i+1 );
+ _offPoints[i]._len = GCPnts_AbscissaPoint::Length( c3dAdaptor, u0, u ) / edgeLen;
+ }
+
+ _LayerEdge* leOnV[2] = { getLEdgeOnV(0), getLEdgeOnV(1) };
+
+ // 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]._len > _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 );
+ }
+
+ int iLBO = _offPoints.size() - 2; // last but one
+ _offPoints[iLBO]._2edges._edges[1] = & _leOnV[1];
+
+ // {
+ // TopoDS_Face face[2]; // FACEs sharing the EDGE
+ // PShapeIteratorPtr fIt = helper.GetAncestors( _eos._shape, *helper.GetMesh(), TopAbs_FACE );
+ // while ( const TopoDS_Shape* F = fIt->next() )
+ // {
+ // TGeomID fID = helper.GetMeshDS()->ShapeToIndex( *F );
+ // if ( ! data._ignoreFaceIds.count( fID ))
+ // face[ !face[0].IsNull() ] = *F;
+ // }
+ // if ( face[0].IsNull() ) return;
+ // if ( face[1].IsNull() ) face[1] = face[0];
+ // }
+
+
+ // set _normal of _leOnV[0] and _leOnV[1] to be normal to the EDGE
+
+ setNormalOnV( 0, data.GetHelper() );
+ setNormalOnV( 1, data.GetHelper() );
+ _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 set _normal of _leOnV[is2nd] to be normal to the EDGE
+ */
+//================================================================================
+
+void _Smoother1D::setNormalOnV( const bool is2nd,
+ SMESH_MesherHelper& helper)
+{
+ _LayerEdge* leOnV = getLEdgeOnV( is2nd );
+ const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
+ TopoDS_Shape V = helper.GetSubShapeByNode( leOnV->_nodes[0], helper.GetMeshDS() );
+ gp_XYZ eDir = getEdgeDir( E, TopoDS::Vertex( V ));
+ gp_XYZ cross = leOnV->_normal ^ eDir;
+ gp_XYZ norm = eDir ^ cross;
+ double size = norm.Modulus();
+
+ _leOnV[ is2nd ]._normal = norm / size;
+}
+
+//================================================================================
+/*!
+ * \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 )))
+ {
+ 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 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;
+ }
+ }