+ 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 = eos._offsetSurf->NextValueOfUV( edge->_curvature->_uv, tgtP, preci );
+ if ( eos._offsetSurf->Gap() > edge->_len ) continue; // NextValueOfUV() bug
+ edge->_curvature->_uv = uv;
+ if ( eos._offsetSurf->Gap() < 10 * preci ) continue; // same pos
+
+ gp_XYZ newP = eos._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 );
+ }
+ }
+
+#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_F") << 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
+}
+
+//================================================================================
+/*!
+ * \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 _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;
+
+ const size_t iFrom = 0, iTo = _eos._edges.size();
+
+ if ( _anaCurve->IsKind( STANDARD_TYPE( Geom_Line )))
+ {
+ if ( F.IsNull() ) // 3D
+ {
+ SMESH_TNodeXYZ p0 ( _eos._edges[iFrom]->_2neibors->tgtNode(0) );
+ SMESH_TNodeXYZ p1 ( _eos._edges[iTo-1]->_2neibors->tgtNode(1) );
+ SMESH_TNodeXYZ pSrc0( _eos._edges[iFrom]->_2neibors->srcNode(0) );
+ SMESH_TNodeXYZ pSrc1( _eos._edges[iTo-1]->_2neibors->srcNode(1) );
+ gp_XYZ newPos;
+ for ( size_t i = iFrom; i < iTo; ++i )
+ {
+ _LayerEdge* edge = _eos._edges[i];
+ SMDS_MeshNode* tgtNode = const_cast<SMDS_MeshNode*>( edge->_nodes.back() );
+ newPos = p0 * ( 1. - _leParams[i] ) + p1 * _leParams[i];
+
+ if ( _eos._edges[i]->Is( _LayerEdge::NORMAL_UPDATED ))
+ {
+ gp_XYZ curPos = SMESH_TNodeXYZ ( tgtNode );
+ gp_XYZ lineDir = pSrc1 - pSrc0;
+ double shift = ( lineDir * ( newPos - pSrc0 ) -
+ lineDir * ( curPos - pSrc0 ));
+ newPos = curPos + lineDir * shift / lineDir.SquareModulus();
+ }
+ if ( _eos._edges[i]->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
+ {
+ _LayerEdge* e0 = getLEdgeOnV( 0 );
+ _LayerEdge* e1 = getLEdgeOnV( 1 );
+ gp_XY uv0 = e0->LastUV( F, *data.GetShapeEdges( e0 ));
+ gp_XY uv1 = e1->LastUV( F, *data.GetShapeEdges( e1 ));
+ if ( e0->_nodes.back() == e1->_nodes.back() ) // closed edge
+ {
+ int iPeriodic = helper.GetPeriodicIndex();
+ if ( iPeriodic == 1 || iPeriodic == 2 )
+ {
+ uv1.SetCoord( iPeriodic, helper.GetOtherParam( uv1.Coord( iPeriodic )));
+ if ( uv0.Coord( iPeriodic ) > uv1.Coord( iPeriodic ))
+ std::swap( uv0, uv1 );
+ }
+ }
+ const gp_XY rangeUV = uv1 - uv0;
+ for ( size_t i = iFrom; i < iTo; ++i )
+ {
+ if ( _eos._edges[i]->Is( _LayerEdge::BLOCKED )) continue;
+ gp_XY newUV = uv0 + _leParams[i] * rangeUV;
+ _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() );
+ }
+ }
+ 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 = iFrom; i < iTo; ++i )
+ {
+ if ( _eos._edges[i]->Is( _LayerEdge::BLOCKED )) 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 = iFrom; i < iTo; ++i )
+ {
+ if ( _eos._edges[i]->Is( _LayerEdge::BLOCKED )) 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() );
+ }
+ }
+ 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 to a new position
+
+ _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;
+ 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();
+ 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;
+ }
+ 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)
+{
+ // sort _LayerEdge's by position on the EDGE
+ const TopoDS_Edge& E = TopoDS::Edge( _eos._shape );
+ data.SortOnEdge( E, _eos._edges );
+
+ // 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(); ++i )
+ _leParams[i] = _leParams[i+1] / fullLen;
+ }
+
+ 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;