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