Salome HOME
23418: [OCC] Mesh: Minimization of memory usage of SMESH
[modules/smesh.git] / src / StdMeshers / StdMeshers_FaceSide.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File      : StdMeshers_FaceSide.hxx
24 // Created   : Wed Jan 31 18:41:25 2007
25 // Author    : Edward AGAPOV (eap)
26 // Module    : SMESH
27 //
28 #include "StdMeshers_FaceSide.hxx"
29
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_MeshNode.hxx"
32 #include "SMESHDS_Mesh.hxx"
33 #include "SMESHDS_SubMesh.hxx"
34 #include "SMESH_Algo.hxx"
35 #include "SMESH_Mesh.hxx"
36 #include "SMESH_MesherHelper.hxx"
37 #include "SMESH_ComputeError.hxx"
38 #include "SMESH_Block.hxx"
39
40 #include <Adaptor2d_Curve2d.hxx>
41 #include <BRepAdaptor_CompCurve.hxx>
42 #include <BRep_Builder.hxx>
43 #include <BRep_Tool.hxx>
44 #include <GCPnts_AbscissaPoint.hxx>
45 #include <Geom2dAdaptor_Curve.hxx>
46 #include <Geom_Line.hxx>
47 #include <TopExp.hxx>
48 #include <TopExp_Explorer.hxx>
49 #include <TopoDS.hxx>
50 #include <TopoDS_Face.hxx>
51 #include <TopoDS_Vertex.hxx>
52 #include <TopoDS_Wire.hxx>
53
54 #include <map>
55 #include <limits>
56
57 #include "utilities.h"
58
59 using namespace std;
60
61 //================================================================================
62 /*!
63  * \brief Constructor of a side of one edge
64   * \param theFace - the face
65   * \param theEdge - the edge
66   */
67 //================================================================================
68
69 StdMeshers_FaceSide::StdMeshers_FaceSide(const TopoDS_Face&   theFace,
70                                          const TopoDS_Edge&   theEdge,
71                                          SMESH_Mesh*          theMesh,
72                                          const bool           theIsForward,
73                                          const bool           theIgnoreMediumNodes,
74                                          SMESH_MesherHelper*  theFaceHelper,
75                                          SMESH_ProxyMesh::Ptr theProxyMesh)
76 {
77   std::list<TopoDS_Edge> edges(1,theEdge);
78   StdMeshers_FaceSide tmp( theFace, edges, theMesh, theIsForward,
79                            theIgnoreMediumNodes, theFaceHelper, theProxyMesh );
80   *this = tmp;
81
82   tmp.myHelper = NULL;
83 }
84
85 //================================================================================
86 /*!
87  * \brief Constructor of a side of several edges
88  */
89 //================================================================================
90
91 StdMeshers_FaceSide::StdMeshers_FaceSide(const TopoDS_Face&            theFace,
92                                          const std::list<TopoDS_Edge>& theEdges,
93                                          SMESH_Mesh*                   theMesh,
94                                          const bool                    theIsForward,
95                                          const bool                    theIgnoreMediumNodes,
96                                          SMESH_MesherHelper*           theFaceHelper,
97                                          SMESH_ProxyMesh::Ptr          theProxyMesh)
98 {
99   int nbEdges = theEdges.size();
100   myEdge.resize      ( nbEdges );
101   myEdgeID.resize    ( nbEdges );
102   myC2d.resize       ( nbEdges );
103   myC3dAdaptor.resize( nbEdges );
104   myFirst.resize     ( nbEdges );
105   myLast.resize      ( nbEdges );
106   myNormPar.resize   ( nbEdges );
107   myEdgeLength.resize( nbEdges );
108   myIsUniform.resize ( nbEdges, true );
109   myFace               = theFace;
110   myLength             = 0;
111   myNbPonits           = myNbSegments = 0;
112   myProxyMesh          = theProxyMesh;
113   myMissingVertexNodes = false;
114   myIgnoreMediumNodes  = theIgnoreMediumNodes;
115   myDefaultPnt2d       = gp_Pnt2d( 1e+100, 1e+100 );
116   myHelper             = NULL;
117   if ( !myProxyMesh ) myProxyMesh.reset( new SMESH_ProxyMesh( *theMesh ));
118   if ( theFaceHelper && theFaceHelper->GetSubShape() == myFace )
119   {
120     myHelper           = new SMESH_MesherHelper( * myProxyMesh->GetMesh() );
121     myHelper->CopySubShapeInfo( *theFaceHelper );
122   }
123   if ( nbEdges == 0 ) return;
124
125   SMESHDS_Mesh* meshDS = myProxyMesh->GetMeshDS();
126
127   int nbDegen = 0;
128   std::list<TopoDS_Edge>::const_iterator edge = theEdges.begin();
129   for ( int index = 0; edge != theEdges.end(); ++index, ++edge )
130   {
131     int i = theIsForward ? index : nbEdges-index-1;
132     myEdgeLength[i] = SMESH_Algo::EdgeLength( *edge );
133     if ( myEdgeLength[i] < DBL_MIN ) nbDegen++;
134     myLength += myEdgeLength[i];
135     myEdge  [i] = *edge;
136     myEdgeID[i] = meshDS->ShapeToIndex( *edge );
137     if ( !theIsForward ) myEdge[i].Reverse();
138
139     if ( theFace.IsNull() )
140       BRep_Tool::Range( *edge, myFirst[i], myLast[i] );
141     else
142       myC2d[i] = BRep_Tool::CurveOnSurface( *edge, theFace, myFirst[i], myLast[i] );
143     if ( myEdge[i].Orientation() == TopAbs_REVERSED )
144       std::swap( myFirst[i], myLast[i] );
145
146     // check if the edge has a non-uniform parametrization (issue 0020705)
147     if ( !myC2d[i].IsNull() )
148     {
149       if ( myEdgeLength[i] > DBL_MIN )
150       {
151         Geom2dAdaptor_Curve A2dC( myC2d[i],
152                                   std::min( myFirst[i], myLast[i] ),
153                                   std::max( myFirst[i], myLast[i] ));
154         double p2 = myFirst[i]+(myLast[i]-myFirst[i])/2.;
155         double p4 = myFirst[i]+(myLast[i]-myFirst[i])/4.;
156         double d2 = GCPnts_AbscissaPoint::Length( A2dC, myFirst[i], p2 );
157         double d4 = GCPnts_AbscissaPoint::Length( A2dC, myFirst[i], p4 );
158         myIsUniform[i] = !( fabs(2*d2/myEdgeLength[i]-1.0) > 0.01 || fabs(2*d4/d2-1.0) > 0.01 );
159         Handle(Geom_Curve) C3d = BRep_Tool::Curve(myEdge[i],d2,d4);
160         myC3dAdaptor[i].Load( C3d, d2,d4 );
161       }
162       else
163       {
164         const TopoDS_Vertex& V = SMESH_MesherHelper::IthVertex( 0, *edge );
165         Handle(Geom_Curve) C3d = new Geom_Line( BRep_Tool::Pnt( V ), gp::DX() );
166         myC3dAdaptor[i].Load( C3d, 0, 0.5 * BRep_Tool::Tolerance( V ));
167       }
168     }
169     else if ( myEdgeLength[i] > DBL_MIN )
170     {
171       Handle(Geom_Curve) C3d = BRep_Tool::Curve(myEdge[i],myFirst[i], myLast[i] );
172       myC3dAdaptor[i].Load( C3d, myFirst[i], myLast[i] );
173       if ( myEdge[i].Orientation() == TopAbs_REVERSED )
174         std::swap( myFirst[i], myLast[i] );
175     }
176
177     // reverse a proxy sub-mesh
178     if ( !theIsForward )
179       reverseProxySubmesh( myEdge[i] );
180
181   } // loop on edges
182
183   // count nodes and segments
184   NbPoints( /*update=*/true );
185
186   if ( nbEdges > 1 && myLength > DBL_MIN ) {
187     const double degenNormLen = 1.e-5;
188     double totLength = myLength;
189     if ( nbDegen )
190       totLength += myLength * degenNormLen * nbDegen;
191     double prevNormPar = 0;
192     for ( int i = 0; i < nbEdges; ++i ) {
193       if ( myEdgeLength[ i ] < DBL_MIN )
194         myEdgeLength[ i ] = myLength * degenNormLen;
195       myNormPar[ i ] = prevNormPar + myEdgeLength[i]/totLength;
196       prevNormPar = myNormPar[ i ];
197     }
198   }
199   myNormPar[nbEdges-1] = 1.;
200   //dump();
201 }
202
203 //================================================================================
204 /*!
205  * \brief Constructor of a side for vertex using data from other FaceSide
206  */
207 //================================================================================
208
209 StdMeshers_FaceSide::StdMeshers_FaceSide(const StdMeshers_FaceSide*  theSide,
210                                          const SMDS_MeshNode*        theNode,
211                                          const gp_Pnt2d*             thePnt2d1,
212                                          const gp_Pnt2d*             thePnt2d2,
213                                          const Handle(Geom2d_Curve)& theC2d,
214                                          const double                theUFirst,
215                                          const double                theULast)
216 {
217   myEdge.resize        ( 1 );
218   myEdgeID.resize      ( 1, 0 );
219   myC2d.push_back      ( theC2d );
220   myC3dAdaptor.resize  ( 1 );
221   myFirst.push_back    ( theUFirst );
222   myLast.push_back     ( theULast );
223   myNormPar.push_back  ( 1. );
224   myIsUniform.push_back( true );
225   myHelper       = NULL;
226   myLength       = 0;
227   myProxyMesh    = theSide->myProxyMesh;
228   myDefaultPnt2d = *thePnt2d1;
229   myPoints       = theSide->GetUVPtStruct();
230   myNbPonits     = myPoints.size();
231   myNbSegments   = theSide->myNbSegments;
232   if ( thePnt2d2 )
233     for ( size_t i = 0; i < myPoints.size(); ++i )
234     {
235       double r = i / ( myPoints.size() - 1. );
236       myPoints[i].u = (1-r) * thePnt2d1->X() + r * thePnt2d2->X();
237       myPoints[i].v = (1-r) * thePnt2d1->Y() + r * thePnt2d2->Y();
238       myPoints[i].node = theNode;
239     }
240   else
241     for ( size_t i = 0; i < myPoints.size(); ++i )
242     {
243       myPoints[i].u = thePnt2d1->X();
244       myPoints[i].v = thePnt2d1->Y();
245       myPoints[i].node = theNode;
246     }
247 }
248
249 //================================================================================
250 /*
251  * Create a side from an UVPtStructVec
252  */
253 //================================================================================
254
255 StdMeshers_FaceSide::StdMeshers_FaceSide(UVPtStructVec&     theSideNodes,
256                                          const TopoDS_Face& theFace,
257                                          const TopoDS_Edge& theEdge,
258                                          SMESH_Mesh*        theMesh)
259 {
260   myEdge.resize( 1, theEdge );
261   myEdgeID.resize( 1, -1 );
262   myC2d.resize( 1 );
263   myC3dAdaptor.resize( 1 );
264   myFirst.resize( 1, 0. );
265   myLast.resize( 1, 1. );
266   myNormPar.resize( 1, 1. );
267   myIsUniform.resize( 1, 1 );
268   myMissingVertexNodes = myIgnoreMediumNodes = false;
269   myDefaultPnt2d.SetCoord( 1e100, 1e100 );
270   if ( theMesh ) myProxyMesh.reset( new SMESH_ProxyMesh( *theMesh ));
271   if ( !theEdge.IsNull() )
272   {
273     if ( theMesh ) myEdgeID[0] = theMesh->GetMeshDS()->ShapeToIndex( theEdge );
274     if ( theFace.IsNull() )
275       BRep_Tool::Range( theEdge, myFirst[0], myLast[0] );
276     else
277       myC2d[0] = BRep_Tool::CurveOnSurface( theEdge, theFace, myFirst[0], myLast[0] );
278     if ( theEdge.Orientation() == TopAbs_REVERSED )
279       std::swap( myFirst[0], myLast[0] );
280   }
281
282   myFace       = theFace;
283   myHelper     = NULL;
284   myPoints     = theSideNodes;
285   myNbPonits   = myPoints.size();
286   myNbSegments = myNbPonits + 1;
287
288   myLength = 0;
289   if ( !myPoints.empty() )
290   {
291     myPoints[0].normParam = 0;
292     if ( myPoints[0].node &&
293          myPoints.back().node &&
294          myPoints[ myNbPonits/2 ].node )
295     {
296       gp_Pnt pPrev = SMESH_TNodeXYZ( myPoints[0].node );
297       for ( size_t i = 1; i < myPoints.size(); ++i )
298       {
299         gp_Pnt p = SMESH_TNodeXYZ( myPoints[i].node );
300         myLength += p.Distance( pPrev );
301         myPoints[i].normParam = myLength;
302         pPrev = p;
303       }
304     }
305     else if ( !theFace.IsNull() )
306     {
307       TopLoc_Location loc;
308       Handle(Geom_Surface) surf = BRep_Tool::Surface( theFace, loc );
309       gp_Pnt pPrev = surf->Value( myPoints[0].u, myPoints[0].v );
310       for ( size_t i = 1; i < myPoints.size(); ++i )
311       {
312         gp_Pnt p = surf->Value( myPoints[i].u, myPoints[i].v );
313         myLength += p.Distance( pPrev );
314         myPoints[i].normParam = myLength;
315         pPrev = p;
316       }
317     }
318     else
319     {
320       gp_Pnt2d pPrev = myPoints[0].UV();
321       for ( size_t i = 1; i < myPoints.size(); ++i )
322       {
323         gp_Pnt2d p = myPoints[i].UV();
324         myLength += p.Distance( pPrev );
325         myPoints[i].normParam = myLength;
326         pPrev = p;
327       }
328     }
329     if ( myLength > std::numeric_limits<double>::min() )
330       for ( size_t i = 1; i < myPoints.size(); ++i )
331         myPoints[i].normParam /= myLength;
332   }
333   myEdgeLength.resize( 1, myLength );
334 }
335
336 //================================================================================
337 /*!
338  * \brief Destructor
339  */
340 //================================================================================
341
342 StdMeshers_FaceSide::~StdMeshers_FaceSide()
343 {
344   delete myHelper; myHelper = NULL;
345 }
346
347 //================================================================================
348 /*
349  * Return info on nodes on the side
350  */
351 //================================================================================
352
353 const std::vector<UVPtStruct>& StdMeshers_FaceSide::GetUVPtStruct(bool   isXConst,
354                                                                   double constValue) const
355 {
356   if ( myPoints.empty() )
357   {
358     if ( NbEdges() == 0 ) return myPoints;
359
360     StdMeshers_FaceSide* me = const_cast< StdMeshers_FaceSide* >( this );
361     bool paramOK = true;
362     double eps = 1e-100;
363
364     SMESH_MesherHelper  eHelper( *myProxyMesh->GetMesh() );
365     SMESH_MesherHelper& fHelper = *FaceHelper();
366
367     // sort nodes of all edges by putting them into a map
368
369     map< double, const SMDS_MeshNode*>            u2node;
370     vector< pair< double, const SMDS_MeshNode*> > u2nodeVec;
371     vector<const SMDS_MeshNode*>                  nodes;
372     set<const SMDS_MeshNode*>                     vertexNodes;
373     vector< const SMESH_ProxyMesh::SubMesh* >     proxySubMesh( myEdge.size() );
374     int nbProxyNodes = 0;
375     size_t iE;
376
377     for ( iE = 0; iE < myEdge.size(); ++iE )
378     {
379       proxySubMesh[iE] = myProxyMesh->GetProxySubMesh( myEdge[iE] );
380       if ( proxySubMesh[iE] )
381       {
382         if ( proxySubMesh[iE]->GetUVPtStructVec().empty() ) {
383           proxySubMesh[iE] = 0;
384         }
385         else {
386           nbProxyNodes += proxySubMesh[iE]->GetUVPtStructVec().size() - 1;
387           if ( iE+1 == myEdge.size() )
388             ++nbProxyNodes;
389           continue;
390         }
391       }
392
393       // Add 1st vertex node of a current edge
394       const SMDS_MeshNode* node = VertexNode( iE );
395       const double  prevNormPar = ( iE == 0 ? 0 : myNormPar[ iE-1 ]); // normalized param
396       if ( node ) // nodes on internal vertices may be missing
397       {
398         if ( vertexNodes.insert( node ).second ||
399              fHelper.IsRealSeam  ( node->getshapeId() ) ||
400              fHelper.IsDegenShape( node->getshapeId() ))
401           u2node.insert( u2node.end(), make_pair( prevNormPar, node ));
402       }
403       else if ( iE == 0 )
404       {
405         for ( ++iE; iE < myEdge.size(); ++iE )
406           if (( node = VertexNode( iE ))) {
407             u2node.insert( make_pair( prevNormPar, node ));
408             break;
409           }
410         --iE;
411
412         if ( !node )
413           return myPoints;
414         vertexNodes.insert( node );
415       }
416
417       // Add internal nodes
418       nodes.clear();
419       if ( !GetEdgeNodes( iE, nodes, /*v0=*/false, /*v1=*/false ))
420         return myPoints;
421       if ( !nodes.empty() )
422       {
423         u2nodeVec.clear();
424         double paramSize = myLast[iE] - myFirst[iE];
425         double r         = myNormPar[iE] - prevNormPar;
426         eHelper.SetSubShape( myEdge[iE] );
427         eHelper.ToFixNodeParameters( true );
428         if ( !myIsUniform[iE] )
429           for ( size_t i = 0; i < nodes.size(); ++i )
430           {
431             double     u = eHelper.GetNodeU( myEdge[iE], nodes[i], 0, &paramOK );
432             double aLenU = GCPnts_AbscissaPoint::Length( me->myC3dAdaptor[iE], myFirst[iE], u );
433             if ( myEdgeLength[iE] < aLenU ) // nonregression test "3D_mesh_NETGEN/G6"
434             {
435               u2nodeVec.clear();
436               break;
437             }
438             double normPar = prevNormPar + r * aLenU / myEdgeLength[iE];
439             u2nodeVec.push_back( make_pair( normPar, nodes[i] ));
440           }
441         if ( u2nodeVec.empty() )
442           for ( size_t i = 0; i < nodes.size(); ++i )
443           {
444             double u = eHelper.GetNodeU( myEdge[iE], nodes[i], 0, &paramOK );
445             // paramSize is signed so orientation is taken into account
446             double normPar = prevNormPar + r * ( u - myFirst[iE] ) / paramSize;
447             u2nodeVec.push_back( make_pair( normPar, nodes[i] ));
448           }
449         for ( size_t j = 0; j < u2nodeVec.size(); ++j )
450           u2node.insert( u2node.end(), u2nodeVec[j] );
451       }
452     } // loop on myEdge's
453
454     // Add 2nd VERTEX node for a last EDGE
455     if ( !proxySubMesh.back() )
456     {
457       if ( u2node.empty() ) return myPoints;
458
459       const SMDS_MeshNode* node;
460       if ( IsClosed() && !proxySubMesh[0] )
461         node = u2node.begin()->second;
462       else
463       {
464         node = VertexNode( iE );
465         if ( myProxyMesh->GetMesh()->HasModificationsToDiscard() )
466           while ( !node && iE > 1 ) // check intermediate VERTEXes
467             node = VertexNode( --iE );
468       }
469       if ( node )
470       {
471         if ( u2node.rbegin()->second == node &&
472              !fHelper.IsRealSeam  ( node->getshapeId() ) &&
473              !fHelper.IsDegenShape( node->getshapeId() ))
474           u2node.erase( --u2node.end() );
475
476         u2node.insert( u2node.end(), make_pair( 1., node ));
477       }
478     }
479
480     if ((int) u2node.size() + nbProxyNodes != myNbPonits &&
481         (int) u2node.size() + nbProxyNodes != NbPoints( /*update=*/true ))
482     {
483       return myPoints;
484     }
485     if (( u2node.size() > 0 ) &&
486         ( u2node.begin()->first < 0 || u2node.rbegin()->first > 1 ))
487     {
488       return myPoints;
489     }
490
491     // fill array of UVPtStruct
492
493     UVPtStructVec& points = me->myPoints;
494     points.resize( myNbPonits );
495
496     int iPt = 0;
497     double prevNormPar = 0, paramSize = myNormPar[ 0 ];
498     map< double, const SMDS_MeshNode*>::iterator u_node = u2node.begin();
499     for ( size_t iE = 0; iE < myEdge.size(); ++iE )
500     {
501       if ( proxySubMesh[ iE ] ) // copy data from a proxy sub-mesh
502       {
503         const UVPtStructVec& edgeUVPtStruct = proxySubMesh[iE]->GetUVPtStructVec();
504         UVPtStruct* pointsPtr = & points[iPt];
505         std::copy( edgeUVPtStruct.begin(), edgeUVPtStruct.end(), pointsPtr );
506         // check orientation
507         double du1 = edgeUVPtStruct.back().param - edgeUVPtStruct[0].param;
508         double du2 = myLast[iE] - myFirst[iE];
509         if ( du1 * du2 < 0 )
510         {
511           std::reverse( pointsPtr, pointsPtr + edgeUVPtStruct.size());
512           for ( size_t i = 0; i < edgeUVPtStruct.size(); ++i )
513             pointsPtr[i].normParam = 1. - pointsPtr[i].normParam;
514         }
515         // update normalized params
516         if ( myEdge.size() > 1 ) {
517           for ( size_t i = 0; i < edgeUVPtStruct.size(); ++i )
518           {
519             UVPtStruct & uvPt = pointsPtr[i];
520             uvPt.normParam    = prevNormPar + uvPt.normParam * paramSize;
521             uvPt.x = uvPt.y   = uvPt.normParam;
522           }
523           iPt += edgeUVPtStruct.size() - 1; // to point to the 1st VERTEX of the next EDGE
524         }
525         // update UV on a seam EDGE
526         if ( fHelper.IsRealSeam( myEdgeID[ iE ]))
527         {
528           // check if points lye on the EDGE
529           const UVPtStruct& pm = edgeUVPtStruct[ edgeUVPtStruct.size()/2 ];
530           gp_Pnt         pNode = SMESH_TNodeXYZ( pm.node );
531           gp_Pnt         pCurv = myC3dAdaptor[ iE ].Value( pm.param );
532           double           tol = BRep_Tool::Tolerance( myEdge[ iE ]) * 10;
533           bool   isPointOnEdge = ( pNode.SquareDistance( pCurv ) < tol * tol );
534           if ( isPointOnEdge )
535             for ( size_t i = 0; i < edgeUVPtStruct.size(); ++i )
536               pointsPtr[i].SetUV( myC2d[ iE ]->Value( pointsPtr[i].param ).XY() );
537         }
538       }
539       else
540       {
541         for ( ; u_node != u2node.end(); ++u_node, ++iPt )
542         {
543           if ( myNormPar[ iE ]-eps < u_node->first )
544             break; // u_node is at VERTEX of the next EDGE
545
546           UVPtStruct & uvPt = points[iPt];
547           uvPt.node       = u_node->second;
548           // -- normParam, x, y --------------------------------
549           uvPt.normParam  = u_node->first;
550           uvPt.x = uvPt.y = uvPt.normParam;
551           // -- U ----------------------------------------------
552           SMDS_EdgePositionPtr epos = uvPt.node->GetPosition();
553           if ( epos && uvPt.node->getshapeId() == myEdgeID[iE] ) {
554             uvPt.param = epos->GetUParameter();
555           }
556           else {
557             double r = ( uvPt.normParam - prevNormPar )/ paramSize;
558             uvPt.param = ( r > 0.5 ? myLast[iE] : myFirst[iE] );
559           }
560           // -- UV ---------------------------------------------
561           if ( !myC2d[ iE ].IsNull() ) {
562             gp_Pnt2d p = myC2d[ iE ]->Value( uvPt.param );
563             uvPt.u = p.X();
564             uvPt.v = p.Y();
565           }
566           else {
567             uvPt.u = uvPt.v = 1e+100;
568           }
569         }
570       }
571       // prepare for the next EDGE
572       if ( iE+1 < myEdge.size() )
573       {
574         prevNormPar = myNormPar[ iE ];
575         paramSize   = myNormPar[ iE+1 ] - prevNormPar;
576       }
577     } // loop on myEdge's
578
579     // set <constValue>
580     if ( isXConst )
581       for ( iPt = 0; iPt < (int)points.size(); ++iPt ) points[ iPt ].x = constValue;
582     else
583       for ( iPt = 0; iPt < (int)points.size(); ++iPt ) points[ iPt ].y = constValue;
584
585   } // if ( myPoints.empty())
586
587   return myPoints;
588 }
589
590 //================================================================================
591 /*!
592  * \brief Falsificate info on nodes
593  * \param nbSeg - nb of segments on the side
594  * \retval UVPtStruct* - array of data structures
595  */
596 //================================================================================
597
598 const vector<UVPtStruct>& StdMeshers_FaceSide::SimulateUVPtStruct(int    nbSeg,
599                                                                   bool   isXConst,
600                                                                   double constValue) const
601 {
602   if ( myFalsePoints.empty() )
603   {
604     if ( NbEdges() == 0 ) return myFalsePoints;
605
606     vector<uvPtStruct>* points = const_cast<vector<uvPtStruct>*>( &myFalsePoints );
607     points->resize( nbSeg+1 );
608
609     int EdgeIndex = 0;
610     double prevNormPar = 0, paramSize = myNormPar[ EdgeIndex ];
611     gp_Pnt2d p;
612     for ( size_t i = 0 ; i < myFalsePoints.size(); ++i )
613     {
614       double normPar = double(i) / double(nbSeg);
615       UVPtStruct & uvPt = (*points)[i];
616       uvPt.node = 0;
617       uvPt.x = uvPt.y = uvPt.param = uvPt.normParam = normPar;
618       if ( isXConst ) uvPt.x = constValue;
619       else            uvPt.y = constValue;
620       if ( myNormPar[ EdgeIndex ] < normPar )
621       {
622         prevNormPar = myNormPar[ EdgeIndex ];
623         ++EdgeIndex;
624         paramSize = myNormPar[ EdgeIndex ] - prevNormPar;
625       }
626       double r = ( normPar - prevNormPar )/ paramSize;
627       uvPt.param = myFirst[EdgeIndex] * ( 1 - r ) + myLast[EdgeIndex] * r;
628       if ( !myC2d[ EdgeIndex ].IsNull() )
629         p = myC2d[ EdgeIndex ]->Value( uvPt.param );
630       else
631         p = Value2d( normPar );
632       uvPt.u = p.X();
633       uvPt.v = p.Y();
634     }
635   }
636   return myFalsePoints;
637 }
638
639 //=======================================================================
640 //function : GetOrderedNodes
641 //purpose  : Return nodes in the order they encounter while walking along the side
642 //=======================================================================
643
644 std::vector<const SMDS_MeshNode*> StdMeshers_FaceSide::GetOrderedNodes(int theEdgeInd) const
645 {
646   vector<const SMDS_MeshNode*> resultNodes;
647   if ( myPoints.empty() || ( theEdgeInd >= 0 && NbEdges() > 0 ))
648   {
649     if ( NbEdges() == 0 ) return resultNodes;
650
651     //SMESHDS_Mesh* meshDS = myProxyMesh->GetMeshDS();
652     SMESH_MesherHelper  eHelper( *myProxyMesh->GetMesh() );
653     SMESH_MesherHelper& fHelper = * FaceHelper();
654     bool paramOK = true;
655
656     // Sort nodes of all edges putting them into a map
657
658     map< double, const SMDS_MeshNode*> u2node;
659     vector<const SMDS_MeshNode*>       nodes;
660     set<const SMDS_MeshNode*>          vertexNodes;
661     int iE = 0, iEnd = myEdge.size();
662     if ( theEdgeInd >= 0 )
663     {
664       iE   = theEdgeInd % NbEdges();
665       iEnd = iE + 1;
666     }
667     for ( iE = 0; iE < iEnd; ++iE )
668     {
669       double prevNormPar = ( iE == 0 ? 0 : myNormPar[ iE-1 ]); // normalized param
670
671       const SMESH_ProxyMesh::SubMesh* proxySM = myProxyMesh->GetProxySubMesh( myEdge[iE] );
672       if ( proxySM )
673       {
674         const UVPtStructVec& points = proxySM->GetUVPtStructVec();
675         for ( size_t i = 0; i < points.size(); ++i )
676           u2node.insert( make_pair( prevNormPar + points[i].normParam, points[i].node ));
677         continue;
678       }
679
680       // Add 1st vertex node of a current EDGE
681       const SMDS_MeshNode* node = VertexNode( iE );
682       if ( node ) { // nodes on internal vertices may be missing
683         if ( vertexNodes.insert( node ).second ||
684              fHelper.IsRealSeam  ( node->getshapeId() ) ||
685              fHelper.IsDegenShape( node->getshapeId() ))
686           u2node.insert( make_pair( prevNormPar, node ));
687       }
688       else if ( iE == 0 )
689       {
690         if ( nodes.empty() ) {
691           for ( ++iE; iE < iEnd; ++iE )
692             if (( node = VertexNode( iE ))) {
693               u2node.insert( make_pair( prevNormPar, node ));
694               break;
695             }
696           --iE;
697         }
698         if ( !node )
699           return resultNodes;
700         vertexNodes.insert( node );
701       }
702
703       // Add internal nodes
704       nodes.clear();
705       if ( !GetEdgeNodes( iE, nodes, /*v0=*/false, /*v1=*/false ))
706         return resultNodes;
707       if ( !nodes.empty() )
708       {
709         double paramSize = myLast[iE] - myFirst[iE];
710         double r         = myNormPar[iE] - prevNormPar;
711         eHelper.SetSubShape( myEdge[iE] );
712         eHelper.ToFixNodeParameters( true );
713         for ( size_t i = 0; i < nodes.size(); ++i )
714         {
715           double u = eHelper.GetNodeU( myEdge[iE], nodes[i], 0, &paramOK );
716           // paramSize is signed so orientation is taken into account
717           double normPar = prevNormPar + r * ( u - myFirst[iE] ) / paramSize;
718           u2node.insert( u2node.end(), make_pair( normPar, nodes[i] ));
719         }
720       }
721
722     } // loop on myEdges
723
724     if ( u2node.empty() ) return resultNodes;
725
726     // Add 2nd vertex node for a last EDGE
727     {
728       const SMDS_MeshNode* node;
729       if ( IsClosed() && theEdgeInd < 0 )
730         node = u2node.begin()->second;
731       else
732       {
733         node = VertexNode( iE );
734         while ( !node && iE > 0 )
735           node = VertexNode( --iE );
736         if ( !node )
737           return resultNodes;
738       }
739       if ( u2node.rbegin()->second == node &&
740            !fHelper.IsRealSeam  ( node->getshapeId() ) &&
741            !fHelper.IsDegenShape( node->getshapeId() ))
742         u2node.erase( --u2node.end() );
743
744       u2node.insert( u2node.end(), make_pair( 1., node ));
745     }
746
747     // Fill the result vector
748
749     if ( theEdgeInd < 0 &&
750          (int) u2node.size() != myNbPonits &&
751          (int) u2node.size() != NbPoints( /*update=*/true ))
752     {
753       u2node.clear();
754     }
755     resultNodes.reserve( u2node.size() );
756     map< double, const SMDS_MeshNode*>::iterator u2n = u2node.begin();
757     for ( ; u2n != u2node.end(); ++u2n )
758       resultNodes.push_back( u2n->second );
759   }
760   else
761   {
762     resultNodes.resize( myPoints.size() );
763     for ( size_t i = 0; i < myPoints.size(); ++i )
764       resultNodes[i] = myPoints[i].node;
765   }
766
767   return resultNodes;
768 }
769
770 //================================================================================
771 /*!
772  * \brief Return (unsorted) nodes of the i-th EDGE.
773  *        Nodes moved to other geometry by MergeNodes() are also returned.
774  *  \retval bool - is OK
775  */
776 //================================================================================
777
778 bool StdMeshers_FaceSide::GetEdgeNodes(size_t                        i,
779                                        vector<const SMDS_MeshNode*>& nodes,
780                                        bool                          inlude1stVertex,
781                                        bool                          inludeLastVertex) const
782 {
783   if ( i >= myEdge.size() )
784     return false;
785
786   SMESH_Mesh*     mesh = myProxyMesh->GetMesh();
787   SMESHDS_Mesh* meshDS = mesh->GetMeshDS();
788   SMESHDS_SubMesh*  sm = meshDS->MeshElements( myEdge[i] );
789
790   if ( inlude1stVertex )
791   {
792     if ( const SMDS_MeshNode* n0 = VertexNode( i ))
793       nodes.push_back( n0 );
794   }
795
796   if ( sm && ( sm->NbElements() > 0 || sm->NbNodes() > 0 ))
797   {
798     if ( mesh->HasModificationsToDiscard() ) // check nb of nodes on the EDGE sub-mesh
799     {
800       int iQuad    = sm->NbElements() ? sm->GetElements()->next()->IsQuadratic() : 0;
801       int nbExpect = sm->NbElements() - 1 + iQuad * sm->NbElements();
802       if ( nbExpect != sm->NbNodes() ) // some nodes are moved from the EDGE by MergeNodes()
803       {
804         // add nodes of all segments
805         typedef set< const SMDS_MeshNode* > TNodeSet;
806         TNodeSet sharedNodes;
807         SMDS_ElemIteratorPtr segIt = sm->GetElements();
808         while ( segIt->more() )
809         {
810           const SMDS_MeshElement* seg = segIt->next();
811           if ( seg->GetType() != SMDSAbs_Edge )
812             continue;
813           for ( int i = 0; i < 3-myIgnoreMediumNodes; ++i )
814           {
815             const SMDS_MeshNode* n = seg->GetNode( i );
816             if ( i == 2 ) // medium node
817             {
818               nodes.push_back( n );
819             }
820             else
821             {
822               pair<TNodeSet::iterator, bool> it2new = sharedNodes.insert( n );
823               if ( !it2new.second ) // n encounters twice == it's on EDGE, not on VERTEX
824               {
825                 nodes.push_back( n );
826                 sharedNodes.erase( it2new.first );
827               }
828             }
829           }
830         }
831       }
832     }
833     if ( nodes.size() < 2 ) // add nodes assigned to the EDGE
834     {
835       SMDS_NodeIteratorPtr nItr = sm->GetNodes();
836       while ( nItr->more() )
837       {
838         const SMDS_MeshNode* n = nItr->next();
839         if ( myIgnoreMediumNodes && SMESH_MeshEditor::IsMedium( n, SMDSAbs_Edge ))
840           continue;
841         nodes.push_back( n );
842       }
843     }
844   } // if ( sm && sm->NbElements() > 0 )
845
846   if ( inludeLastVertex )
847   {
848     if ( const SMDS_MeshNode* n1 = VertexNode( i+1 ))
849       nodes.push_back( n1 );
850   }
851   return true;
852 }
853
854 //================================================================================
855 /*!
856  * \brief Return a node from the i-th VERTEX (count starts from zero)
857  *        Nodes moved to other geometry by MergeNodes() are also returned.
858  *  \param [in] i - the VERTEX index
859  *  \param [out] isMoved - returns \c true if the found node is moved by MergeNodes()
860  *  \return const SMDS_MeshNode* - the found node
861  */
862 //================================================================================
863
864 const SMDS_MeshNode* StdMeshers_FaceSide::VertexNode(std::size_t i, bool* isMoved) const
865 {
866   TopoDS_Vertex V = ( i >= myEdge.size() ) ? LastVertex() : FirstVertex(i);
867
868   const SMDS_MeshNode* n = SMESH_Algo::VertexNode( V, myProxyMesh->GetMeshDS() );
869
870   if ( !n && !myEdge.empty() && myProxyMesh->GetMesh()->HasModificationsToDiscard() )
871   {
872     size_t iE = ( i < myEdge.size() ) ? i : myEdge.size()-1;
873     SMESHDS_SubMesh* sm = myProxyMesh->GetMeshDS()->MeshElements( myEdgeID[ iE ]);
874
875     n = SMESH_Algo::VertexNode( V, sm, myProxyMesh->GetMesh(), /*checkV=*/false );
876
877     if (( !n ) &&
878         (( i > 0 && (int) i < NbEdges() ) || IsClosed() ))
879     {
880       iE = SMESH_MesherHelper::WrapIndex( int(i)-1, NbEdges() );
881       sm = myProxyMesh->GetMeshDS()->MeshElements( myEdgeID[ iE ]);
882       n  = SMESH_Algo::VertexNode( V, sm, myProxyMesh->GetMesh(), /*checkV=*/false );
883     }
884
885     if ( n && n->GetPosition()->GetDim() == 1 ) // check that n does not lie on an EDGE of myFace
886     {
887       TopoDS_Shape S = SMESH_MesherHelper::GetSubShapeByNode( n, myProxyMesh->GetMeshDS() );
888       if ( SMESH_MesherHelper::IsSubShape( S, myFace ))
889         n = 0; // VERTEX ignored by e.g. Composite Wire Discretization algo
890     }
891     if ( isMoved )
892       *isMoved = n;
893   }
894   return n;
895 }
896
897 //================================================================================
898 /*!
899  * \brief reverse order of vector elements
900   * \param vec - vector to reverse
901  */
902 //================================================================================
903
904 template <typename T > void reverse(vector<T> & vec)
905 {
906   std::reverse( vec.begin(), vec.end() );
907 }
908
909 //================================================================================
910 /*!
911  * \brief Change orientation of side geometry
912  */
913 //================================================================================
914
915 void StdMeshers_FaceSide::Reverse()
916 {
917   int nbEdges = myEdge.size();
918   for ( int i = nbEdges-1; i >= 0; --i ) {
919     std::swap( myFirst[i], myLast[i] );
920     if ( !myEdge[i].IsNull() )
921       myEdge[i].Reverse();
922     if ( i > 0 ) // at the first loop 1. is overwritten
923       myNormPar[i] = 1 - myNormPar[i-1];
924   }
925   if ( nbEdges > 1 ) {
926     reverse( myEdge );
927     reverse( myEdgeID );
928     reverse( myC2d );
929     //reverse( myC3dAdaptor );
930     reverse( myFirst );
931     reverse( myLast );
932     reverse( myNormPar );
933     reverse( myEdgeLength );
934     reverse( myIsUniform );
935   }
936   if ( nbEdges > 0 )
937   {
938     myNormPar[nbEdges-1]=1.;
939     if ( !myEdge[0].IsNull() )
940     {
941       for ( size_t i = 0; i < myEdge.size(); ++i )
942         reverseProxySubmesh( myEdge[i] );
943       myPoints.clear();
944       myFalsePoints.clear();
945     }
946     else
947     {
948       for ( size_t i = 0; i < myPoints.size(); ++i )
949       {
950         UVPtStruct & uvPt = myPoints[i];
951         uvPt.normParam = 1 - uvPt.normParam;
952         uvPt.x         = 1 - uvPt.x;
953         uvPt.y         = 1 - uvPt.y;
954       }
955       reverse( myPoints );
956
957       for ( size_t i = 0; i < myFalsePoints.size(); ++i )
958       {
959         UVPtStruct & uvPt = myFalsePoints[i];
960         uvPt.normParam = 1 - uvPt.normParam;
961         uvPt.x         = 1 - uvPt.x;
962         uvPt.y         = 1 - uvPt.y;
963       }
964       reverse( myFalsePoints );
965     }
966   }
967   for ( size_t i = 0; i < myEdge.size(); ++i )
968   {
969     if ( myEdge[i].IsNull() ) continue; // for a side on points only
970     double fp,lp;
971     Handle(Geom_Curve) C3d = BRep_Tool::Curve(myEdge[i],fp,lp);
972     if ( !C3d.IsNull() )
973       myC3dAdaptor[i].Load( C3d, fp,lp );
974   }
975 }
976
977 //=======================================================================
978 //function : SetIgnoreMediumNodes
979 //purpose  : Make ignore medium nodes
980 //=======================================================================
981
982 void StdMeshers_FaceSide::SetIgnoreMediumNodes(bool toIgnore)
983 {
984   if ( myIgnoreMediumNodes != toIgnore )
985   {
986     myIgnoreMediumNodes = toIgnore;
987
988     if ( !myPoints.empty() )
989     {
990       UVPtStructVec newPoints;
991       newPoints.reserve( myPoints.size()/2 + 1 );
992       for ( size_t i = 0; i < myPoints.size(); i += 2 )
993         newPoints.push_back( myPoints[i] );
994
995       myPoints.swap( newPoints );
996     }
997     else
998     {
999       NbPoints( /*update=*/true );
1000     }
1001   }
1002 }
1003
1004 //=======================================================================
1005 //function : NbPoints
1006 //purpose  : Return nb nodes on edges and vertices (+1 to be == GetUVPtStruct().size() )
1007 //           Call it with update == true if mesh of this side can be recomputed
1008 //           since creation of this side
1009 //=======================================================================
1010
1011 int StdMeshers_FaceSide::NbPoints(const bool update) const
1012 {
1013   if ( !myPoints.empty() )
1014     return myPoints.size();
1015
1016   // if ( !myFalsePoints.empty() )
1017   //   return myFalsePoints.size();
1018
1019   if ( update && myEdge.size() > 0 )
1020   {
1021     StdMeshers_FaceSide* me = (StdMeshers_FaceSide*) this;
1022     me->myNbPonits = 0;
1023     me->myNbSegments = 0;
1024     me->myMissingVertexNodes = false;
1025
1026     vector<const SMDS_MeshNode*> nodes;
1027     for ( int i = 0; i < NbEdges(); ++i )
1028     {
1029       if ( const SMESHDS_SubMesh* sm = myProxyMesh->GetSubMesh( Edge(i) ))
1030       {
1031         if ( sm->NbNodes() == sm->NbElements()-1 || sm->NbElements() == 0 )
1032         {
1033           me->myNbPonits += sm->NbNodes();
1034           if ( myIgnoreMediumNodes && sm->IsQuadratic() )
1035             me->myNbPonits -= sm->NbElements();
1036         }
1037         else // nodes can be moved to other shapes by MergeNodes()
1038         {
1039           nodes.clear();
1040           GetEdgeNodes( i, nodes, /*v1=*/false, /*v2=*/false );
1041           me->myNbPonits += nodes.size();
1042         }
1043         me->myNbSegments += sm->NbElements();
1044       }
1045     }
1046
1047     SMESH_MesherHelper* helper = FaceHelper();
1048
1049     std::set< const SMDS_MeshNode* > vNodes;
1050     const int nbV = NbEdges() + !IsClosed();
1051     for ( int i = 0; i < nbV; ++i )
1052       if ( const SMDS_MeshNode* n = VertexNode( i ))
1053       {
1054         if ( !vNodes.insert( n ).second &&
1055              ( helper->IsRealSeam  ( n->getshapeId() ) ||
1056                helper->IsDegenShape( n->getshapeId() )))
1057           me->myNbPonits++;
1058       }
1059       else
1060       {
1061         me->myMissingVertexNodes = true;
1062       }
1063     me->myNbPonits += vNodes.size();
1064
1065     if ( IsClosed() )
1066       me->myNbPonits++; // closing node is repeated
1067   }
1068   return myNbPonits;
1069 }
1070
1071 //=======================================================================
1072 //function : NbSegments
1073 //purpose  : Return nb edges
1074 //           Call it with update == true if mesh of this side can be recomputed
1075 //           since creation of this side
1076 //=======================================================================
1077
1078 int StdMeshers_FaceSide::NbSegments(const bool update) const
1079 {
1080   return NbPoints( update ), myNbSegments;
1081 }
1082
1083 //================================================================================
1084 /*!
1085  * \brief Reverse UVPtStructVec if a proxy sub-mesh of E
1086  */
1087 //================================================================================
1088
1089 void StdMeshers_FaceSide::reverseProxySubmesh( const TopoDS_Edge& E )
1090 {
1091   if ( !myProxyMesh ) return;
1092   if ( const SMESH_ProxyMesh::SubMesh* sm = myProxyMesh->GetProxySubMesh( E ))
1093   {
1094     UVPtStructVec& edgeUVPtStruct = (UVPtStructVec& ) sm->GetUVPtStructVec();
1095     for ( size_t i = 0; i < edgeUVPtStruct.size(); ++i )
1096     {
1097       UVPtStruct & uvPt = edgeUVPtStruct[i];
1098       uvPt.normParam = 1 - uvPt.normParam;
1099       uvPt.x         = 1 - uvPt.x;
1100       uvPt.y         = 1 - uvPt.y;
1101     }
1102     reverse( edgeUVPtStruct );
1103   }
1104 }
1105
1106 //================================================================================
1107 /*!
1108  * \brief Show side features
1109  */
1110 //================================================================================
1111
1112 void StdMeshers_FaceSide::dump(const char* msg) const
1113 {
1114 #ifdef _DEBUG_
1115   if (msg) MESSAGE ( std::endl << msg );
1116   MESSAGE_BEGIN ("NB EDGES: "<< myEdge.size() );
1117   MESSAGE_ADD ( "nbPoints: "<<myNbPonits<<" vecSize: " << myPoints.size()<<" "<<myFalsePoints.size() );
1118   for ( size_t i = 0; i < myEdge.size(); ++i )
1119   {
1120     MESSAGE_ADD ( "\t"<<i+1 );
1121     MESSAGE_ADD ( "\tEDGE: " );
1122     if (myEdge[i].IsNull()) {
1123       MESSAGE_ADD ( "NULL" );
1124     }
1125     else {
1126       TopAbs::Print(myEdge[i].Orientation(),cout)<<" "<<myEdge[i].TShape().operator->()<<endl;
1127       MESSAGE_ADD ( "\tV1: " << TopExp::FirstVertex( myEdge[i], 1).TShape().operator->()
1128                     << "  V2: " << TopExp::LastVertex( myEdge[i], 1).TShape().operator->() );
1129     }
1130     MESSAGE_ADD ( "\tC2d: ");
1131
1132     if (myC2d[i].IsNull()) {
1133       MESSAGE_ADD ( "NULL" );
1134     }
1135     else {
1136       MESSAGE_ADD ( myC2d[i].operator->() );
1137     }
1138
1139     MESSAGE_ADD ( "\tF: "<<myFirst[i]<< " L: "<< myLast[i] );
1140     MESSAGE_END ( "\tnormPar: "<<myNormPar[i]<<endl );
1141   }
1142 #endif
1143 }
1144
1145 //================================================================================
1146 /*!
1147  * \brief Creates a Adaptor2d_Curve2d to be used in SMESH_Block
1148   * \retval Adaptor2d_Curve2d* - 
1149  */
1150 //================================================================================
1151
1152 struct Adaptor2dCurve2d : public Adaptor2d_Curve2d
1153 {
1154   const StdMeshers_FaceSide* mySide;
1155   Adaptor2dCurve2d(const StdMeshers_FaceSide* faceSide):mySide(faceSide) {}
1156   gp_Pnt2d Value(const Standard_Real U) const { return mySide->Value2d( U ); }
1157   Standard_Real FirstParameter() const { return 0; }
1158   Standard_Real LastParameter() const { return 1; }
1159 };
1160
1161 Adaptor2d_Curve2d* StdMeshers_FaceSide::GetCurve2d() const
1162 {
1163   return new Adaptor2dCurve2d( this );
1164 }
1165
1166 //================================================================================
1167 /*!
1168  * \brief Creates a fully functional Adaptor_Curve
1169  */
1170 //================================================================================
1171
1172 BRepAdaptor_CompCurve* StdMeshers_FaceSide::GetCurve3d() const
1173 {
1174   if ( myEdge.empty() )
1175     return 0;
1176
1177   TopoDS_Wire aWire;
1178   BRep_Builder aBuilder;
1179   aBuilder.MakeWire(aWire);
1180   for ( size_t i = 0; i < myEdge.size(); ++i )
1181     aBuilder.Add( aWire, myEdge[i] );
1182
1183   if ( myEdge.size() == 2 && IsClosed() )
1184     aWire.Closed(true); // issue 0021141
1185
1186   return new BRepAdaptor_CompCurve( aWire );
1187 }
1188
1189 //================================================================================
1190 /*!
1191  * \brief Return 2D point by normalized parameter
1192   * \param U - normalized parameter value
1193   * \retval gp_Pnt2d - point
1194  */
1195 //================================================================================
1196
1197 gp_Pnt2d StdMeshers_FaceSide::Value2d(double U) const
1198 {
1199   if ( !myC2d[0].IsNull() ) {
1200     int i = EdgeIndex( U );
1201     double prevU = i ? myNormPar[ i-1 ] : 0;
1202     double r = ( U - prevU )/ ( myNormPar[ i ] - prevU );
1203
1204     double par = myFirst[i] * ( 1 - r ) + myLast[i] * r;
1205     
1206     // check parametrization of curve
1207     if( !myIsUniform[i] )
1208     {
1209       double aLen3dU = r * myEdgeLength[i] * ( myFirst[i]>myLast[i] ? -1. : 1.);
1210       GCPnts_AbscissaPoint AbPnt
1211         ( const_cast<GeomAdaptor_Curve&>( myC3dAdaptor[i]), aLen3dU, myFirst[i] );
1212       if( AbPnt.IsDone() ) {
1213         par = AbPnt.Parameter();
1214       }
1215     }
1216     return myC2d[ i ]->Value(par);
1217
1218   }
1219   else if ( !myPoints.empty() )
1220   {
1221     int i = U * double( myPoints.size()-1 );
1222     while ( i > 0 && myPoints[ i ].normParam > U )
1223       --i;
1224     while ( i+1 < (int)myPoints.size() && myPoints[ i+1 ].normParam < U )
1225       ++i;
1226     double r = (( U - myPoints[ i ].normParam ) /
1227                 ( myPoints[ i+1 ].normParam - myPoints[ i ].normParam ));
1228     return ( myPoints[ i   ].UV() * ( 1 - r ) +
1229              myPoints[ i+1 ].UV() * r );
1230   }
1231   return myDefaultPnt2d;
1232 }
1233
1234 //================================================================================
1235 /*!
1236  * \brief Return XYZ by normalized parameter
1237   * \param U - normalized parameter value
1238   * \retval gp_Pnt - point
1239  */
1240 //================================================================================
1241
1242 gp_Pnt StdMeshers_FaceSide::Value3d(double U) const
1243 {
1244   int        i = EdgeIndex( U );
1245   double prevU = i ? myNormPar[ i-1 ] : 0;
1246   double     r = ( U - prevU )/ ( myNormPar[ i ] - prevU );
1247
1248   double par = myFirst[i] * ( 1 - r ) + myLast[i] * r;
1249
1250   // check parametrization of curve
1251   if( !myIsUniform[i] )
1252   {
1253     double aLen3dU = r * myEdgeLength[i] * ( myFirst[i] > myLast[i] ? -1. : 1. );
1254     GCPnts_AbscissaPoint AbPnt
1255       ( const_cast<GeomAdaptor_Curve&>( myC3dAdaptor[i]), aLen3dU, myFirst[i] );
1256     if( AbPnt.IsDone() ) {
1257       par = AbPnt.Parameter();
1258     }
1259   }
1260   return myC3dAdaptor[ i ].Value(par);
1261 }
1262
1263 //================================================================================
1264 /*!
1265  * \brief Return wires of a face as StdMeshers_FaceSide's
1266  */
1267 //================================================================================
1268
1269 TSideVector StdMeshers_FaceSide::GetFaceWires(const TopoDS_Face&   theFace,
1270                                               SMESH_Mesh &         theMesh,
1271                                               const bool           theIgnoreMediumNodes,
1272                                               TError &             theError,
1273                                               SMESH_MesherHelper*  theFaceHelper,
1274                                               SMESH_ProxyMesh::Ptr theProxyMesh,
1275                                               const bool           theCheckVertexNodes)
1276 {
1277   SMESH_MesherHelper helper( theMesh );
1278   if ( theFaceHelper && theFaceHelper->GetSubShape() == theFace )
1279     helper.CopySubShapeInfo( *theFaceHelper );
1280
1281   list< TopoDS_Edge > edges, internalEdges;
1282   list< int > nbEdgesInWires;
1283   int nbWires = SMESH_Block::GetOrderedEdges (theFace, edges, nbEdgesInWires);
1284
1285   // split list of all edges into separate wires
1286   TSideVector wires( nbWires );
1287   list< int >::iterator nbE = nbEdgesInWires.begin();
1288   list< TopoDS_Edge >::iterator from = edges.begin(), to = from;
1289   for ( int iW = 0; iW < nbWires; ++iW, ++nbE )
1290   {
1291     std::advance( to, *nbE );
1292     if ( *nbE == 0 ) // Issue 0020676
1293     {
1294       --nbWires;
1295       --iW;
1296       wires.resize( nbWires );
1297       continue;
1298     }
1299     list< TopoDS_Edge > wireEdges( from, to );
1300     // assure that there is a node on the first vertex
1301     // as StdMeshers_FaceSide::GetUVPtStruct() requires
1302     if ( wireEdges.front().Orientation() != TopAbs_INTERNAL ) // Issue 0020676
1303     {
1304       if ( theCheckVertexNodes )
1305         while ( !SMESH_Algo::VertexNode( TopExp::FirstVertex( wireEdges.front(), true),
1306                                          theMesh.GetMeshDS()))
1307         {
1308           wireEdges.splice(wireEdges.end(), wireEdges,
1309                            wireEdges.begin(), ++wireEdges.begin());
1310           if ( from->IsSame( wireEdges.front() )) {
1311             theError = TError
1312               ( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"No nodes on vertices"));
1313             return TSideVector(0);
1314           }
1315         }
1316     }
1317     else if ( *nbE > 1 ) // Issue 0020676 (Face_pb_netgen.brep) - several internal edges in a wire
1318     {
1319       internalEdges.splice( internalEdges.end(), wireEdges, ++wireEdges.begin(), wireEdges.end());
1320     }
1321
1322     StdMeshers_FaceSide* wire = new StdMeshers_FaceSide( theFace, wireEdges, &theMesh,
1323                                                          /*isForward=*/true, theIgnoreMediumNodes,
1324                                                          &helper, theProxyMesh );
1325     wires[ iW ] = StdMeshers_FaceSidePtr( wire );
1326     from = to;
1327   }
1328   while ( !internalEdges.empty() )
1329   {
1330     StdMeshers_FaceSide* wire = new StdMeshers_FaceSide( theFace, internalEdges.back(), &theMesh,
1331                                                          /*isForward=*/true, theIgnoreMediumNodes,
1332                                                          &helper, theProxyMesh );
1333     wires.push_back( StdMeshers_FaceSidePtr( wire ));
1334     internalEdges.pop_back();
1335   }
1336   return wires;
1337 }
1338
1339 //================================================================================
1340 /*!
1341  * \brief Return 1st vertex of the i-the edge
1342  */
1343 //================================================================================
1344
1345 TopoDS_Vertex StdMeshers_FaceSide::FirstVertex(int i) const
1346 {
1347   TopoDS_Vertex v;
1348   if ( i < NbEdges() )
1349   {
1350     v = myEdge[i].Orientation() <= TopAbs_REVERSED ? // FORWARD || REVERSED
1351         TopExp::FirstVertex( myEdge[i], 1 )        :
1352         TopoDS::Vertex( TopoDS_Iterator( myEdge[i] ).Value() );
1353   }
1354   return v;
1355 }
1356
1357 //================================================================================
1358 /*!
1359  * \brief Return last vertex of the i-the edge
1360  */
1361 //================================================================================
1362
1363 TopoDS_Vertex StdMeshers_FaceSide::LastVertex(int i) const
1364 {
1365   TopoDS_Vertex v;
1366   if ( i < NbEdges() )
1367   {
1368     const TopoDS_Edge& edge = i<0 ? myEdge[ NbEdges() + i ] : myEdge[i];
1369     if ( edge.Orientation() <= TopAbs_REVERSED ) // FORWARD || REVERSED
1370       v = TopExp::LastVertex( edge, 1 );
1371     else
1372       for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() )
1373         v = TopoDS::Vertex( vIt.Value() );
1374   }
1375   return v;
1376 }
1377
1378 //================================================================================
1379 /*!
1380  * \brief Return \c true if the chain of EDGEs is closed
1381  */
1382 //================================================================================
1383
1384 bool StdMeshers_FaceSide::IsClosed() const
1385 {
1386   return myEdge.empty() ? false : FirstVertex().IsSame( LastVertex() );
1387 }
1388
1389 //================================================================================
1390 /*!
1391  * \brief Return a helper initialized with the FACE
1392  */
1393 //================================================================================
1394
1395 SMESH_MesherHelper* StdMeshers_FaceSide::FaceHelper() const
1396 {
1397   StdMeshers_FaceSide* me = const_cast< StdMeshers_FaceSide* >( this );
1398   if ( !myHelper && myProxyMesh )
1399   {
1400     me->myHelper = new SMESH_MesherHelper( *myProxyMesh->GetMesh() );
1401     me->myHelper->SetSubShape( myFace );
1402   }
1403   return me->myHelper;
1404 }