+//================================================================================
+/*!
+ * \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 containing 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() );