Salome HOME
Merge from V6_main 13/12/2012
[modules/smesh.git] / src / SMESH / SMESH_MesherHelper.cxx
1 // Copyright (C) 2007-2012  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.
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:      SMESH_MesherHelper.cxx
24 // Created:   15.02.06 15:22:41
25 // Author:    Sergey KUUL
26 //
27 #include "SMESH_MesherHelper.hxx"
28
29 #include "SMDS_EdgePosition.hxx"
30 #include "SMDS_FaceOfNodes.hxx"
31 #include "SMDS_FacePosition.hxx" 
32 #include "SMDS_IteratorOnIterators.hxx"
33 #include "SMDS_VolumeTool.hxx"
34 #include "SMESH_ProxyMesh.hxx"
35 #include "SMESH_subMesh.hxx"
36
37 #include <BRepAdaptor_Curve.hxx>
38 #include <BRepAdaptor_Surface.hxx>
39 #include <BRepClass3d_SolidClassifier.hxx>
40 #include <BRepTools.hxx>
41 #include <BRepTools_WireExplorer.hxx>
42 #include <BRep_Tool.hxx>
43 #include <Geom2d_Curve.hxx>
44 #include <GeomAPI_ProjectPointOnCurve.hxx>
45 #include <GeomAPI_ProjectPointOnSurf.hxx>
46 #include <Geom_Curve.hxx>
47 #include <Geom_RectangularTrimmedSurface.hxx>
48 #include <Geom_Surface.hxx>
49 #include <ShapeAnalysis.hxx>
50 #include <TopExp.hxx>
51 #include <TopExp_Explorer.hxx>
52 #include <TopTools_ListIteratorOfListOfShape.hxx>
53 #include <TopTools_MapIteratorOfMapOfShape.hxx>
54 #include <TopTools_MapOfShape.hxx>
55 #include <TopoDS.hxx>
56 #include <gp_Ax3.hxx>
57 #include <gp_Pnt2d.hxx>
58 #include <gp_Trsf.hxx>
59
60 #include <Standard_Failure.hxx>
61 #include <Standard_ErrorHandler.hxx>
62
63 #include <utilities.h>
64
65 #include <limits>
66
67 using namespace std;
68
69 #define RETURN_BAD_RESULT(msg) { MESSAGE(msg); return false; }
70
71 namespace {
72
73   gp_XYZ XYZ(const SMDS_MeshNode* n) { return gp_XYZ(n->X(), n->Y(), n->Z()); }
74
75   enum { U_periodic = 1, V_periodic = 2 };
76 }
77
78 //================================================================================
79 /*!
80  * \brief Constructor
81  */
82 //================================================================================
83
84 SMESH_MesherHelper::SMESH_MesherHelper(SMESH_Mesh& theMesh)
85   : myParIndex(0), myMesh(&theMesh), myShapeID(0), myCreateQuadratic(false),
86     myFixNodeParameters(false)
87 {
88   myPar1[0] = myPar2[0] = myPar1[1] = myPar2[1] = 0;
89   mySetElemOnShape = ( ! myMesh->HasShapeToMesh() );
90 }
91
92 //=======================================================================
93 //function : ~SMESH_MesherHelper
94 //purpose  : 
95 //=======================================================================
96
97 SMESH_MesherHelper::~SMESH_MesherHelper()
98 {
99   {
100     TID2ProjectorOnSurf::iterator i_proj = myFace2Projector.begin();
101     for ( ; i_proj != myFace2Projector.end(); ++i_proj )
102       delete i_proj->second;
103   }
104   {
105     TID2ProjectorOnCurve::iterator i_proj = myEdge2Projector.begin();
106     for ( ; i_proj != myEdge2Projector.end(); ++i_proj )
107       delete i_proj->second;
108   }
109 }
110
111 //=======================================================================
112 //function : IsQuadraticSubMesh
113 //purpose  : Check submesh for given shape: if all elements on this shape 
114 //           are quadratic, quadratic elements will be created.
115 //           Also fill myTLinkNodeMap
116 //=======================================================================
117
118 bool SMESH_MesherHelper::IsQuadraticSubMesh(const TopoDS_Shape& aSh)
119 {
120   SMESHDS_Mesh* meshDS = GetMeshDS();
121   // we can create quadratic elements only if all elements
122   // created on sub-shapes of given shape are quadratic
123   // also we have to fill myTLinkNodeMap
124   myCreateQuadratic = true;
125   mySeamShapeIds.clear();
126   myDegenShapeIds.clear();
127   TopAbs_ShapeEnum subType( aSh.ShapeType()==TopAbs_FACE ? TopAbs_EDGE : TopAbs_FACE );
128   if ( aSh.ShapeType()==TopAbs_COMPOUND )
129   {
130     TopoDS_Iterator subIt( aSh );
131     if ( subIt.More() )
132       subType = ( subIt.Value().ShapeType()==TopAbs_FACE ) ? TopAbs_EDGE : TopAbs_FACE;
133   }
134   SMDSAbs_ElementType elemType( subType==TopAbs_FACE ? SMDSAbs_Face : SMDSAbs_Edge );
135
136
137   int nbOldLinks = myTLinkNodeMap.size();
138
139   if ( !myMesh->HasShapeToMesh() )
140   {
141     if (( myCreateQuadratic = myMesh->NbFaces( ORDER_QUADRATIC )))
142     {
143       SMDS_FaceIteratorPtr fIt = meshDS->facesIterator();
144       while ( fIt->more() )
145         AddTLinks( static_cast< const SMDS_MeshFace* >( fIt->next() ));
146     }
147   }
148   else
149   {
150     TopExp_Explorer exp( aSh, subType );
151     TopTools_MapOfShape checkedSubShapes;
152     for (; exp.More() && myCreateQuadratic; exp.Next()) {
153       if ( !checkedSubShapes.Add( exp.Current() ))
154         continue; // needed if aSh is compound of solids
155       if ( SMESHDS_SubMesh * subMesh = meshDS->MeshElements( exp.Current() )) {
156         if ( SMDS_ElemIteratorPtr it = subMesh->GetElements() ) {
157           while(it->more()) {
158             const SMDS_MeshElement* e = it->next();
159             if ( e->GetType() != elemType || !e->IsQuadratic() ) {
160               myCreateQuadratic = false;
161               break;
162             }
163             else {
164               // fill TLinkNodeMap
165               switch ( e->NbNodes() ) {
166               case 3:
167                 AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(2)); break;
168               case 6:
169                 AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(3));
170                 AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(4));
171                 AddTLinkNode(e->GetNode(2),e->GetNode(0),e->GetNode(5)); break;
172               case 8:
173                 AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(4));
174                 AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(5));
175                 AddTLinkNode(e->GetNode(2),e->GetNode(3),e->GetNode(6));
176                 AddTLinkNode(e->GetNode(3),e->GetNode(0),e->GetNode(7));
177                 break;
178               default:
179                 myCreateQuadratic = false;
180                 break;
181               }
182             }
183           }
184         }
185       }
186     }
187   }
188
189   if ( nbOldLinks == myTLinkNodeMap.size() )
190     myCreateQuadratic = false;
191
192   if(!myCreateQuadratic) {
193     myTLinkNodeMap.clear();
194   }
195   SetSubShape( aSh );
196
197   return myCreateQuadratic;
198 }
199
200 //=======================================================================
201 //function : SetSubShape
202 //purpose  : Set geomerty to make elements on
203 //=======================================================================
204
205 void SMESH_MesherHelper::SetSubShape(const int aShID)
206 {
207   if ( aShID == myShapeID )
208     return;
209   if ( aShID > 0 )
210     SetSubShape( GetMeshDS()->IndexToShape( aShID ));
211   else
212     SetSubShape( TopoDS_Shape() );
213 }
214
215 //=======================================================================
216 //function : SetSubShape
217 //purpose  : Set geomerty to create elements on
218 //=======================================================================
219
220 void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh)
221 {
222   if ( myShape.IsSame( aSh ))
223     return;
224
225   myShape = aSh;
226   mySeamShapeIds.clear();
227   myDegenShapeIds.clear();
228
229   if ( myShape.IsNull() ) {
230     myShapeID  = 0;
231     return;
232   }
233   SMESHDS_Mesh* meshDS = GetMeshDS();
234   myShapeID = meshDS->ShapeToIndex(aSh);
235   myParIndex = 0;
236
237   // treatment of periodic faces
238   for ( TopExp_Explorer eF( aSh, TopAbs_FACE ); eF.More(); eF.Next() )
239   {
240     const TopoDS_Face& face = TopoDS::Face( eF.Current() );
241     TopLoc_Location loc;
242     Handle(Geom_Surface) surface = BRep_Tool::Surface( face, loc );
243
244     if ( surface->IsUPeriodic() || surface->IsVPeriodic() ||
245          surface->IsUClosed()   || surface->IsVClosed() )
246     {
247       //while ( surface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface )))
248       //surface = Handle(Geom_RectangularTrimmedSurface)::DownCast( surface )->BasisSurface();
249       GeomAdaptor_Surface surf( surface );
250
251       for (TopExp_Explorer exp( face, TopAbs_EDGE ); exp.More(); exp.Next())
252       {
253         // look for a seam edge
254         const TopoDS_Edge& edge = TopoDS::Edge( exp.Current() );
255         if ( BRep_Tool::IsClosed( edge, face )) {
256           // initialize myPar1, myPar2 and myParIndex
257           gp_Pnt2d uv1, uv2;
258           BRep_Tool::UVPoints( edge, face, uv1, uv2 );
259           if ( Abs( uv1.Coord(1) - uv2.Coord(1) ) < Abs( uv1.Coord(2) - uv2.Coord(2) ))
260           {
261             myParIndex |= U_periodic;
262             myPar1[0] = surf.FirstUParameter();
263             myPar2[0] = surf.LastUParameter();
264           }
265           else {
266             myParIndex |= V_periodic;
267             myPar1[1] = surf.FirstVParameter();
268             myPar2[1] = surf.LastVParameter();
269           }
270           // store seam shape indices, negative if shape encounters twice
271           int edgeID = meshDS->ShapeToIndex( edge );
272           mySeamShapeIds.insert( IsSeamShape( edgeID ) ? -edgeID : edgeID );
273           for ( TopExp_Explorer v( edge, TopAbs_VERTEX ); v.More(); v.Next() ) {
274             int vertexID = meshDS->ShapeToIndex( v.Current() );
275             mySeamShapeIds.insert( IsSeamShape( vertexID ) ? -vertexID : vertexID );
276           }
277         }
278
279         // look for a degenerated edge
280         if ( BRep_Tool::Degenerated( edge )) {
281           myDegenShapeIds.insert( meshDS->ShapeToIndex( edge ));
282           for ( TopExp_Explorer v( edge, TopAbs_VERTEX ); v.More(); v.Next() )
283             myDegenShapeIds.insert( meshDS->ShapeToIndex( v.Current() ));
284         }
285       }
286       if ( !myDegenShapeIds.empty() && !myParIndex ) {
287         if ( surface->IsUPeriodic() || surface->IsUClosed() ) {
288           myParIndex |= U_periodic;
289           myPar1[0] = surf.FirstUParameter();
290           myPar2[0] = surf.LastUParameter();
291         }
292         else if ( surface->IsVPeriodic() || surface->IsVClosed() ) {
293           myParIndex |= V_periodic;
294           myPar1[1] = surf.FirstVParameter();
295           myPar2[1] = surf.LastVParameter();
296         }
297       }
298     }
299   }
300 }
301
302 //=======================================================================
303 //function : GetNodeUVneedInFaceNode
304 //purpose  : Check if inFaceNode argument is necessary for call GetNodeUV(F,..)
305 //           Return true if the face is periodic.
306 //           If F is Null, answer about sub-shape set through IsQuadraticSubMesh() or
307 //           * SetSubShape()
308 //=======================================================================
309
310 bool SMESH_MesherHelper::GetNodeUVneedInFaceNode(const TopoDS_Face& F) const
311 {
312   if ( F.IsNull() ) return !mySeamShapeIds.empty();
313
314   if ( !F.IsNull() && !myShape.IsNull() && myShape.IsSame( F ))
315     return !mySeamShapeIds.empty();
316
317   TopLoc_Location loc;
318   Handle(Geom_Surface) aSurface = BRep_Tool::Surface( F,loc );
319   if ( !aSurface.IsNull() )
320     return ( aSurface->IsUPeriodic() || aSurface->IsVPeriodic() );
321
322   return false;
323 }
324
325 //=======================================================================
326 //function : IsMedium
327 //purpose  : 
328 //=======================================================================
329
330 bool SMESH_MesherHelper::IsMedium(const SMDS_MeshNode*      node,
331                                   const SMDSAbs_ElementType typeToCheck)
332 {
333   return SMESH_MeshEditor::IsMedium( node, typeToCheck );
334 }
335
336 //=======================================================================
337 //function : GetSubShapeByNode
338 //purpose  : Return support shape of a node
339 //=======================================================================
340
341 TopoDS_Shape SMESH_MesherHelper::GetSubShapeByNode(const SMDS_MeshNode* node,
342                                                    const SMESHDS_Mesh*  meshDS)
343 {
344   int shapeID = node ? node->getshapeId() : 0;
345   if ( 0 < shapeID && shapeID <= meshDS->MaxShapeIndex() )
346     return meshDS->IndexToShape( shapeID );
347   else
348     return TopoDS_Shape();
349 }
350
351
352 //=======================================================================
353 //function : AddTLinkNode
354 //purpose  : add a link in my data structure
355 //=======================================================================
356
357 void SMESH_MesherHelper::AddTLinkNode(const SMDS_MeshNode* n1,
358                                       const SMDS_MeshNode* n2,
359                                       const SMDS_MeshNode* n12)
360 {
361   // add new record to map
362   SMESH_TLink link( n1, n2 );
363   myTLinkNodeMap.insert( make_pair(link,n12));
364 }
365
366 //================================================================================
367 /*!
368  * \brief Add quadratic links of edge to own data structure
369  */
370 //================================================================================
371
372 void SMESH_MesherHelper::AddTLinks(const SMDS_MeshEdge* edge)
373 {
374   if ( edge->IsQuadratic() )
375     AddTLinkNode(edge->GetNode(0), edge->GetNode(1), edge->GetNode(2));
376 }
377
378 //================================================================================
379 /*!
380  * \brief Add quadratic links of face to own data structure
381  */
382 //================================================================================
383
384 void SMESH_MesherHelper::AddTLinks(const SMDS_MeshFace* f)
385 {
386   if ( !f->IsPoly() )
387     switch ( f->NbNodes() ) {
388     case 6:
389       AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(3));
390       AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(4));
391       AddTLinkNode(f->GetNode(2),f->GetNode(0),f->GetNode(5)); break;
392     case 8:
393       AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(4));
394       AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(5));
395       AddTLinkNode(f->GetNode(2),f->GetNode(3),f->GetNode(6));
396       AddTLinkNode(f->GetNode(3),f->GetNode(0),f->GetNode(7));
397     default:;
398     }
399 }
400
401 //================================================================================
402 /*!
403  * \brief Add quadratic links of volume to own data structure
404  */
405 //================================================================================
406
407 void SMESH_MesherHelper::AddTLinks(const SMDS_MeshVolume* volume)
408 {
409   if ( volume->IsQuadratic() )
410   {
411     SMDS_VolumeTool vTool( volume );
412     const SMDS_MeshNode** nodes = vTool.GetNodes();
413     set<int> addedLinks;
414     for ( int iF = 1; iF < vTool.NbFaces(); ++iF )
415     {
416       const int nbN = vTool.NbFaceNodes( iF );
417       const int* iNodes = vTool.GetFaceNodesIndices( iF );
418       for ( int i = 0; i < nbN; )
419       {
420         int iN1  = iNodes[i++];
421         int iN12 = iNodes[i++];
422         int iN2  = iNodes[i];
423         if ( iN1 > iN2 ) std::swap( iN1, iN2 );
424         int linkID = iN1 * vTool.NbNodes() + iN2;
425         pair< set<int>::iterator, bool > it_isNew = addedLinks.insert( linkID );
426         if ( it_isNew.second )
427           AddTLinkNode( nodes[iN1], nodes[iN2], nodes[iN12] );
428         else
429           addedLinks.erase( it_isNew.first ); // each link encounters only twice
430       }
431     }
432   }
433 }
434
435 //================================================================================
436 /*!
437  * \brief Return true if position of nodes on the shape hasn't yet been checked or
438  * the positions proved to be invalid
439  */
440 //================================================================================
441
442 bool SMESH_MesherHelper::toCheckPosOnShape(int shapeID ) const
443 {
444   map< int,bool >::const_iterator id_ok = myNodePosShapesValidity.find( shapeID );
445   return ( id_ok == myNodePosShapesValidity.end() || !id_ok->second );
446 }
447
448 //================================================================================
449 /*!
450  * \brief Set validity of positions of nodes on the shape.
451  * Once set, validity is not changed
452  */
453 //================================================================================
454
455 void SMESH_MesherHelper::setPosOnShapeValidity(int shapeID, bool ok ) const
456 {
457   ((SMESH_MesherHelper*)this)->myNodePosShapesValidity.insert( make_pair( shapeID, ok));
458 }
459
460 //=======================================================================
461 //function : ToFixNodeParameters
462 //purpose  : Enables fixing node parameters on EDGEs and FACEs in 
463 //           GetNodeU(...,check=true), GetNodeUV(...,check=true), CheckNodeUV() and
464 //           CheckNodeU() in case if a node lies on a shape set via SetSubShape().
465 //           Default is False
466 //=======================================================================
467
468 void SMESH_MesherHelper::ToFixNodeParameters(bool toFix)
469 {
470   myFixNodeParameters = toFix;
471 }
472
473
474 //=======================================================================
475 //function : GetUVOnSeam
476 //purpose  : Select UV on either of 2 pcurves of a seam edge, closest to the given UV
477 //=======================================================================
478
479 gp_Pnt2d SMESH_MesherHelper::GetUVOnSeam( const gp_Pnt2d& uv1, const gp_Pnt2d& uv2 ) const
480 {
481   gp_Pnt2d result = uv1;
482   for ( int i = U_periodic; i <= V_periodic ; ++i )
483   {
484     if ( myParIndex & i )
485     {
486       double p1 = uv1.Coord( i );
487       double dp1 = Abs( p1-myPar1[i-1]), dp2 = Abs( p1-myPar2[i-1]);
488       if ( myParIndex == i ||
489            dp1 < ( myPar2[i-1] - myPar2[i-1] ) / 100. ||
490            dp2 < ( myPar2[i-1] - myPar2[i-1] ) / 100. )
491       {
492         double p2 = uv2.Coord( i );
493         double p1Alt = ( dp1 < dp2 ) ? myPar2[i-1] : myPar1[i-1];
494         if ( Abs( p2 - p1 ) > Abs( p2 - p1Alt ))
495           result.SetCoord( i, p1Alt );
496       }
497     }
498   }
499   return result;
500 }
501
502 //=======================================================================
503 //function : GetNodeUV
504 //purpose  : Return node UV on face
505 //=======================================================================
506
507 gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face&   F,
508                                     const SMDS_MeshNode* n,
509                                     const SMDS_MeshNode* n2,
510                                     bool*                check) const
511 {
512   gp_Pnt2d uv( Precision::Infinite(), Precision::Infinite() );
513   const SMDS_PositionPtr Pos = n->GetPosition();
514   bool uvOK = false;
515   if(Pos->GetTypeOfPosition()==SMDS_TOP_FACE)
516   {
517     // node has position on face
518     const SMDS_FacePosition* fpos =
519       static_cast<const SMDS_FacePosition*>(n->GetPosition());
520     uv.SetCoord(fpos->GetUParameter(),fpos->GetVParameter());
521     if ( check )
522       uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F ));
523   }
524   else if(Pos->GetTypeOfPosition()==SMDS_TOP_EDGE)
525   {
526     // node has position on edge => it is needed to find
527     // corresponding edge from face, get pcurve for this
528     // edge and retrieve value from this pcurve
529     const SMDS_EdgePosition* epos =
530       static_cast<const SMDS_EdgePosition*>(n->GetPosition());
531     int edgeID = n->getshapeId();
532     TopoDS_Edge E = TopoDS::Edge(GetMeshDS()->IndexToShape(edgeID));
533     double f, l, u = epos->GetUParameter();
534     Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(E, F, f, l);
535     bool validU = ( f < u && u < l );
536     if ( validU )
537       uv = C2d->Value( u );
538     else
539       uv.SetCoord( Precision::Infinite(),0.);
540     if ( check || !validU )
541       uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F ),/*force=*/ !validU );
542
543     // for a node on a seam edge select one of UVs on 2 pcurves
544     if ( n2 && IsSeamShape( edgeID ) )
545     {
546       uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0, check ));
547     }
548     else
549     { // adjust uv to period
550       TopLoc_Location loc;
551       Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
552       Standard_Boolean isUPeriodic = S->IsUPeriodic();
553       Standard_Boolean isVPeriodic = S->IsVPeriodic();
554       if ( isUPeriodic || isVPeriodic ) {
555         Standard_Real UF,UL,VF,VL;
556         S->Bounds(UF,UL,VF,VL);
557         if(isUPeriodic)
558           uv.SetX( uv.X() + ShapeAnalysis::AdjustToPeriod(uv.X(),UF,UL));
559         if(isVPeriodic)
560           uv.SetY( uv.Y() + ShapeAnalysis::AdjustToPeriod(uv.Y(),VF,VL));
561       }
562     }
563   }
564   else if(Pos->GetTypeOfPosition()==SMDS_TOP_VERTEX)
565   {
566     if ( int vertexID = n->getshapeId() ) {
567       const TopoDS_Vertex& V = TopoDS::Vertex(GetMeshDS()->IndexToShape(vertexID));
568       try {
569         uv = BRep_Tool::Parameters( V, F );
570         uvOK = true;
571       }
572       catch (Standard_Failure& exc) {
573       }
574       if ( !uvOK ) {
575         for ( TopExp_Explorer vert(F,TopAbs_VERTEX); !uvOK && vert.More(); vert.Next() )
576           uvOK = ( V == vert.Current() );
577         if ( !uvOK ) {
578 #ifdef _DEBUG_
579           MESSAGE ( "SMESH_MesherHelper::GetNodeUV(); Vertex " << vertexID
580                << " not in face " << GetMeshDS()->ShapeToIndex( F ) );
581 #endif
582           // get UV of a vertex closest to the node
583           double dist = 1e100;
584           gp_Pnt pn = XYZ( n );
585           for ( TopExp_Explorer vert(F,TopAbs_VERTEX); !uvOK && vert.More(); vert.Next() ) {
586             TopoDS_Vertex curV = TopoDS::Vertex( vert.Current() );
587             gp_Pnt p = BRep_Tool::Pnt( curV );
588             double curDist = p.SquareDistance( pn );
589             if ( curDist < dist ) {
590               dist = curDist;
591               uv = BRep_Tool::Parameters( curV, F );
592               uvOK = ( dist < DBL_MIN );
593             }
594           }
595         }
596         else {
597           uvOK = false;
598           TopTools_ListIteratorOfListOfShape it( myMesh->GetAncestors( V ));
599           for ( ; it.More(); it.Next() ) {
600             if ( it.Value().ShapeType() == TopAbs_EDGE ) {
601               const TopoDS_Edge & edge = TopoDS::Edge( it.Value() );
602               double f,l;
603               Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(edge, F, f, l);
604               if ( !C2d.IsNull() ) {
605                 double u = ( V == TopExp::FirstVertex( edge ) ) ?  f : l;
606                 uv = C2d->Value( u );
607                 uvOK = true;
608                 break;
609               }
610             }
611           }
612         }
613       }
614       if ( n2 && IsSeamShape( vertexID ) )
615         uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0 ));
616     }
617   }
618   else
619   {
620     uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F ));
621   }
622
623   if ( check )
624     *check = uvOK;
625
626   return uv.XY();
627 }
628
629 //=======================================================================
630 //function : CheckNodeUV
631 //purpose  : Check and fix node UV on a face
632 //=======================================================================
633
634 bool SMESH_MesherHelper::CheckNodeUV(const TopoDS_Face&   F,
635                                      const SMDS_MeshNode* n,
636                                      gp_XY&               uv,
637                                      const double         tol,
638                                      const bool           force,
639                                      double               distXYZ[4]) const
640 {
641   int shapeID = n->getshapeId();
642   bool infinit = ( Precision::IsInfinite( uv.X() ) || Precision::IsInfinite( uv.Y() ));
643   if ( force || toCheckPosOnShape( shapeID ) || infinit )
644   {
645     // check that uv is correct
646     TopLoc_Location loc;
647     Handle(Geom_Surface) surface = BRep_Tool::Surface( F,loc );
648     gp_Pnt nodePnt = XYZ( n ), surfPnt(0,0,0);
649     double dist = 0;
650     if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() );
651     if ( infinit ||
652          (dist = nodePnt.Distance( surfPnt = surface->Value( uv.X(), uv.Y() ))) > tol )
653     {
654       setPosOnShapeValidity( shapeID, false );
655       if ( !infinit && distXYZ ) {
656         surfPnt.Transform( loc );
657         distXYZ[0] = dist;
658         distXYZ[1] = surfPnt.X(); distXYZ[2] = surfPnt.Y(); distXYZ[3]=surfPnt.Z();
659       }
660       // uv incorrect, project the node to surface
661       GeomAPI_ProjectPointOnSurf& projector = GetProjector( F, loc, tol );
662       projector.Perform( nodePnt );
663       if ( !projector.IsDone() || projector.NbPoints() < 1 )
664       {
665         MESSAGE( "SMESH_MesherHelper::CheckNodeUV() failed to project" );
666         return false;
667       }
668       Quantity_Parameter U,V;
669       projector.LowerDistanceParameters(U,V);
670       uv.SetCoord( U,V );
671       surfPnt = surface->Value( U, V );
672       dist = nodePnt.Distance( surfPnt );
673       if ( distXYZ ) {
674         surfPnt.Transform( loc );
675         distXYZ[0] = dist;
676         distXYZ[1] = surfPnt.X(); distXYZ[2] = surfPnt.Y(); distXYZ[3]=surfPnt.Z();
677       }
678       if ( dist > tol )
679       {
680         MESSAGE( "SMESH_MesherHelper::CheckNodeUV(), invalid projection" );
681         return false;
682       }
683       // store the fixed UV on the face
684       if ( myShape.IsSame(F) && shapeID == myShapeID && myFixNodeParameters )
685         const_cast<SMDS_MeshNode*>(n)->SetPosition
686           ( SMDS_PositionPtr( new SMDS_FacePosition( U, V )));
687     }
688     else if ( uv.Modulus() > numeric_limits<double>::min() )
689     {
690       setPosOnShapeValidity( shapeID, true );
691     }
692   }
693   return true;
694 }
695
696 //=======================================================================
697 //function : GetProjector
698 //purpose  : Return projector intitialized by given face without location, which is returned
699 //=======================================================================
700
701 GeomAPI_ProjectPointOnSurf& SMESH_MesherHelper::GetProjector(const TopoDS_Face& F,
702                                                              TopLoc_Location&   loc,
703                                                              double             tol ) const
704 {
705   Handle(Geom_Surface) surface = BRep_Tool::Surface( F,loc );
706   int faceID = GetMeshDS()->ShapeToIndex( F );
707   TID2ProjectorOnSurf& i2proj = const_cast< TID2ProjectorOnSurf&>( myFace2Projector );
708   TID2ProjectorOnSurf::iterator i_proj = i2proj.find( faceID );
709   if ( i_proj == i2proj.end() )
710   {
711     if ( tol == 0 ) tol = BRep_Tool::Tolerance( F );
712     double U1, U2, V1, V2;
713     surface->Bounds(U1, U2, V1, V2);
714     GeomAPI_ProjectPointOnSurf* proj = new GeomAPI_ProjectPointOnSurf();
715     proj->Init( surface, U1, U2, V1, V2, tol );
716     i_proj = i2proj.insert( make_pair( faceID, proj )).first;
717   }
718   return *( i_proj->second );
719 }
720
721 namespace
722 {
723   gp_XY AverageUV(const gp_XY& uv1, const gp_XY& uv2) { return ( uv1 + uv2 ) / 2.; }
724   gp_XY_FunPtr(Added); // define gp_XY_Added pointer to function calling gp_XY::Added(gp_XY)
725   gp_XY_FunPtr(Subtracted); 
726 }
727
728 //=======================================================================
729 //function : applyIn2D
730 //purpose  : Perform given operation on two 2d points in parameric space of given surface.
731 //           It takes into account period of the surface. Use gp_XY_FunPtr macro
732 //           to easily define pointer to function of gp_XY class.
733 //=======================================================================
734
735 gp_XY SMESH_MesherHelper::applyIn2D(const Handle(Geom_Surface)& surface,
736                                     const gp_XY&                uv1,
737                                     const gp_XY&                uv2,
738                                     xyFunPtr                    fun,
739                                     const bool                  resultInPeriod)
740 {
741   Standard_Boolean isUPeriodic = surface.IsNull() ? false : surface->IsUPeriodic();
742   Standard_Boolean isVPeriodic = surface.IsNull() ? false : surface->IsVPeriodic();
743   if ( !isUPeriodic && !isVPeriodic )
744     return fun(uv1,uv2);
745
746   // move uv2 not far than half-period from uv1
747   double u2 = 
748     uv2.X()+(isUPeriodic ? ShapeAnalysis::AdjustByPeriod(uv2.X(),uv1.X(),surface->UPeriod()) :0);
749   double v2 = 
750     uv2.Y()+(isVPeriodic ? ShapeAnalysis::AdjustByPeriod(uv2.Y(),uv1.Y(),surface->VPeriod()) :0);
751
752   // execute operation
753   gp_XY res = fun( uv1, gp_XY(u2,v2) );
754
755   // move result within period
756   if ( resultInPeriod )
757   {
758     Standard_Real UF,UL,VF,VL;
759     surface->Bounds(UF,UL,VF,VL);
760     if ( isUPeriodic )
761       res.SetX( res.X() + ShapeAnalysis::AdjustToPeriod(res.X(),UF,UL));
762     if ( isVPeriodic )
763       res.SetY( res.Y() + ShapeAnalysis::AdjustToPeriod(res.Y(),VF,VL));
764   }
765
766   return res;
767 }
768 //=======================================================================
769 //function : GetMiddleUV
770 //purpose  : Return middle UV taking in account surface period
771 //=======================================================================
772
773 gp_XY SMESH_MesherHelper::GetMiddleUV(const Handle(Geom_Surface)& surface,
774                                       const gp_XY&                p1,
775                                       const gp_XY&                p2)
776 {
777   // NOTE:
778   // the proper place of getting basic surface seems to be in applyIn2D()
779   // but we put it here to decrease a risk of regressions just before releasing a version
780   Handle(Geom_Surface) surf = surface;
781   while ( !surf.IsNull() && surf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface )))
782     surf = Handle(Geom_RectangularTrimmedSurface)::DownCast( surf )->BasisSurface();
783
784   return applyIn2D( surf, p1, p2, & AverageUV );
785 }
786
787 //=======================================================================
788 //function : GetNodeU
789 //purpose  : Return node U on edge
790 //=======================================================================
791
792 double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge&   E,
793                                     const SMDS_MeshNode* n,
794                                     const SMDS_MeshNode* inEdgeNode,
795                                     bool*                check)
796 {
797   double param = 0;
798   const SMDS_PositionPtr pos = n->GetPosition();
799   if ( pos->GetTypeOfPosition()==SMDS_TOP_EDGE )
800   {
801     const SMDS_EdgePosition* epos = static_cast<const SMDS_EdgePosition*>( pos );
802     param =  epos->GetUParameter();
803   }
804   else if( pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
805   {
806     if ( inEdgeNode && TopExp::FirstVertex( E ).IsSame( TopExp::LastVertex( E ))) // issue 0020128
807     {
808       Standard_Real f,l;
809       BRep_Tool::Range( E, f,l );
810       double uInEdge = GetNodeU( E, inEdgeNode );
811       param = ( fabs( uInEdge - f ) < fabs( l - uInEdge )) ? f : l;
812     }
813     else
814     {
815       SMESHDS_Mesh * meshDS = GetMeshDS();
816       int vertexID = n->getshapeId();
817       const TopoDS_Vertex& V = TopoDS::Vertex(meshDS->IndexToShape(vertexID));
818       param =  BRep_Tool::Parameter( V, E );
819     }
820   }
821   if ( check )
822   {
823     double tol = BRep_Tool::Tolerance( E );
824     double f,l;  BRep_Tool::Range( E, f,l );
825     bool force = ( param < f-tol || param > l+tol );
826     if ( !force && pos->GetTypeOfPosition()==SMDS_TOP_EDGE )
827       force = ( GetMeshDS()->ShapeToIndex( E ) != n->getshapeId() );
828
829     *check = CheckNodeU( E, n, param, 2*tol, force );
830   }
831   return param;
832 }
833
834 //=======================================================================
835 //function : CheckNodeU
836 //purpose  : Check and fix node U on an edge
837 //           Return false if U is bad and could not be fixed
838 //=======================================================================
839
840 bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge&   E,
841                                     const SMDS_MeshNode* n,
842                                     double&              u,
843                                     const double         tol,
844                                     const bool           force,
845                                     double               distXYZ[4]) const
846 {
847   int shapeID = n->getshapeId();
848   if ( force || toCheckPosOnShape( shapeID ))
849   {
850     TopLoc_Location loc; double f,l;
851     Handle(Geom_Curve) curve = BRep_Tool::Curve( E,loc,f,l );
852     if ( curve.IsNull() ) // degenerated edge
853     {
854       if ( u+tol < f || u-tol > l )
855       {
856         double r = Max( 0.5, 1 - tol*n->GetID()); // to get a unique u on edge
857         u =  f*r + l*(1-r);
858       }
859     }
860     else
861     {
862       gp_Pnt nodePnt = SMESH_TNodeXYZ( n );
863       if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() );
864       gp_Pnt curvPnt = curve->Value( u );
865       double dist = nodePnt.Distance( curvPnt );
866       if ( distXYZ ) {
867         curvPnt.Transform( loc );
868         distXYZ[0] = dist;
869         distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z();
870       }
871       if ( dist > tol )
872       {
873         setPosOnShapeValidity( shapeID, false );
874         // u incorrect, project the node to the curve
875         int edgeID = GetMeshDS()->ShapeToIndex( E );
876         TID2ProjectorOnCurve& i2proj = const_cast< TID2ProjectorOnCurve&>( myEdge2Projector );
877         TID2ProjectorOnCurve::iterator i_proj =
878           i2proj.insert( make_pair( edgeID, (GeomAPI_ProjectPointOnCurve*) 0 )).first;
879         if ( !i_proj->second  )
880         {
881           i_proj->second = new GeomAPI_ProjectPointOnCurve();
882           i_proj->second->Init( curve, f, l );
883         }
884         GeomAPI_ProjectPointOnCurve* projector = i_proj->second;
885         projector->Perform( nodePnt );
886         if ( projector->NbPoints() < 1 )
887         {
888           MESSAGE( "SMESH_MesherHelper::CheckNodeU() failed to project" );
889           return false;
890         }
891         Quantity_Parameter U = projector->LowerDistanceParameter();
892         u = double( U );
893         curvPnt = curve->Value( u );
894         dist = nodePnt.Distance( curvPnt );
895         if ( distXYZ ) {
896           curvPnt.Transform( loc );
897           distXYZ[0] = dist;
898           distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z();
899         }
900         if ( dist > tol )
901         {
902           MESSAGE( "SMESH_MesherHelper::CheckNodeU(), invalid projection" );
903           MESSAGE("distance " << dist << " " << tol );
904           return false;
905         }
906         // store the fixed U on the edge
907         if ( myShape.IsSame(E) && shapeID == myShapeID && myFixNodeParameters )
908           const_cast<SMDS_MeshNode*>(n)->SetPosition
909             ( SMDS_PositionPtr( new SMDS_EdgePosition( U )));
910       }
911       else if ( fabs( u ) > numeric_limits<double>::min() )
912       {
913         setPosOnShapeValidity( shapeID, true );
914       }
915       if (( u < f-tol || u > l+tol ) && force )
916       {
917         // node is on vertex but is set on periodic but trimmed edge (issue 0020890)
918         try
919         {
920           // do not use IsPeriodic() as Geom_TrimmedCurve::IsPeriodic () returns false
921           double period = curve->Period();
922           u = ( u < f ) ? u + period : u - period;
923         }
924         catch (Standard_Failure& exc)
925         {
926           return false;
927         }
928       }
929     }
930   }
931   return true;
932 }
933
934 //=======================================================================
935 //function : GetMediumPos
936 //purpose  : Return index and type of the shape  (EDGE or FACE only) to
937 //          set a medium node on
938 //=======================================================================
939
940 std::pair<int, TopAbs_ShapeEnum> SMESH_MesherHelper::GetMediumPos(const SMDS_MeshNode* n1,
941                                                                   const SMDS_MeshNode* n2)
942 {
943   TopAbs_ShapeEnum shapeType = TopAbs_SHAPE;
944   int              shapeID = -1;
945   TopoDS_Shape     shape;
946
947   if (( myShapeID == n1->getshapeId() || myShapeID == n2->getshapeId() ) && myShapeID > 0 )
948   {
949     shapeType = myShape.ShapeType();
950     shapeID   = myShapeID;
951   }
952   else if ( n1->getshapeId() == n2->getshapeId() )
953   {
954     shapeID = n2->getshapeId();
955     shape = GetSubShapeByNode( n1, GetMeshDS() );
956   }
957   else
958   {
959     const SMDS_TypeOfPosition Pos1 = n1->GetPosition()->GetTypeOfPosition();
960     const SMDS_TypeOfPosition Pos2 = n2->GetPosition()->GetTypeOfPosition();
961
962     if ( Pos1 == SMDS_TOP_3DSPACE || Pos2 == SMDS_TOP_3DSPACE )
963     {
964     }
965     else if ( Pos1 == SMDS_TOP_FACE || Pos2 == SMDS_TOP_FACE )
966     {
967       if ( Pos1 != SMDS_TOP_FACE || Pos2 != SMDS_TOP_FACE )
968       {
969         if ( Pos1 != SMDS_TOP_FACE ) std::swap( n1,n2 );
970         TopoDS_Shape F = GetSubShapeByNode( n1, GetMeshDS() );
971         TopoDS_Shape S = GetSubShapeByNode( n2, GetMeshDS() );
972         if ( IsSubShape( S, F ))
973         {
974           shapeType = TopAbs_FACE;
975           shapeID   = n1->getshapeId();
976         }
977       }
978     }
979     else if ( Pos1 == SMDS_TOP_EDGE && Pos2 == SMDS_TOP_EDGE )
980     {
981       TopoDS_Shape E1 = GetSubShapeByNode( n1, GetMeshDS() );
982       TopoDS_Shape E2 = GetSubShapeByNode( n2, GetMeshDS() );
983       shape = GetCommonAncestor( E1, E2, *myMesh, TopAbs_FACE );
984     }
985     else if ( Pos1 == SMDS_TOP_VERTEX && Pos2 == SMDS_TOP_VERTEX )
986     {
987       TopoDS_Shape V1 = GetSubShapeByNode( n1, GetMeshDS() );
988       TopoDS_Shape V2 = GetSubShapeByNode( n2, GetMeshDS() );
989       shape = GetCommonAncestor( V1, V2, *myMesh, TopAbs_EDGE );
990       if ( shape.IsNull() ) shape = GetCommonAncestor( V1, V2, *myMesh, TopAbs_FACE );
991     }
992     else // VERTEX and EDGE
993     {
994       if ( Pos1 != SMDS_TOP_VERTEX ) std::swap( n1,n2 );
995       TopoDS_Shape V = GetSubShapeByNode( n1, GetMeshDS() );
996       TopoDS_Shape E = GetSubShapeByNode( n2, GetMeshDS() );
997       if ( IsSubShape( V, E ))
998         shape = E;
999       else
1000         shape = GetCommonAncestor( V, E, *myMesh, TopAbs_FACE );
1001     }
1002   }
1003
1004   if ( !shape.IsNull() )
1005   {
1006     if ( shapeID < 1 )
1007       shapeID = GetMeshDS()->ShapeToIndex( shape );
1008     shapeType = shape.ShapeType();
1009   }
1010   return make_pair( shapeID, shapeType );
1011 }
1012
1013 //=======================================================================
1014 //function : GetMediumNode
1015 //purpose  : Return existing or create new medium nodes between given ones
1016 //=======================================================================
1017
1018 const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1,
1019                                                        const SMDS_MeshNode* n2,
1020                                                        bool                 force3d)
1021 {
1022   // Find existing node
1023
1024   SMESH_TLink link(n1,n2);
1025   ItTLinkNode itLN = myTLinkNodeMap.find( link );
1026   if ( itLN != myTLinkNodeMap.end() ) {
1027     return (*itLN).second;
1028   }
1029
1030   // Create medium node
1031
1032   SMDS_MeshNode* n12;
1033   SMESHDS_Mesh* meshDS = GetMeshDS();
1034
1035   if ( IsSeamShape( n1->getshapeId() ))
1036     // to get a correct UV of a node on seam, the second node must have checked UV
1037     std::swap( n1, n2 );
1038
1039   // get type of shape for the new medium node
1040   int faceID = -1, edgeID = -1;
1041   TopoDS_Edge E; double u [2];
1042   TopoDS_Face F; gp_XY  uv[2];
1043   bool uvOK[2] = { false, false };
1044
1045   pair<int, TopAbs_ShapeEnum> pos = GetMediumPos( n1, n2 );
1046
1047   // get positions of the given nodes on shapes
1048   if ( pos.second == TopAbs_FACE )
1049   {
1050     F = TopoDS::Face(meshDS->IndexToShape( faceID = pos.first ));
1051     uv[0] = GetNodeUV(F,n1,n2, force3d ? 0 : &uvOK[0]);
1052     uv[1] = GetNodeUV(F,n2,n1, force3d ? 0 : &uvOK[1]);
1053   }
1054   else if ( pos.second == TopAbs_EDGE )
1055   {
1056     const SMDS_PositionPtr Pos1 = n1->GetPosition();
1057     const SMDS_PositionPtr Pos2 = n2->GetPosition();
1058     if ( Pos1->GetTypeOfPosition()==SMDS_TOP_EDGE &&
1059          Pos2->GetTypeOfPosition()==SMDS_TOP_EDGE &&
1060          n1->getshapeId() != n2->getshapeId() )
1061     {
1062       // issue 0021006
1063       return getMediumNodeOnComposedWire(n1,n2,force3d);
1064     }
1065     E = TopoDS::Edge(meshDS->IndexToShape( edgeID = pos.first ));
1066     u[0] = GetNodeU(E,n1,n2, force3d ? 0 : &uvOK[0]);
1067     u[1] = GetNodeU(E,n2,n1, force3d ? 0 : &uvOK[1]);
1068   }
1069
1070   if ( !force3d & uvOK[0] && uvOK[1] )
1071   {
1072     // we try to create medium node using UV parameters of
1073     // nodes, else - medium between corresponding 3d points
1074     if( ! F.IsNull() )
1075     {
1076       //if ( uvOK[0] && uvOK[1] )
1077       {
1078         if ( IsDegenShape( n1->getshapeId() )) {
1079           if ( myParIndex & U_periodic ) uv[0].SetCoord( 1, uv[1].Coord( 1 ));
1080           else                           uv[0].SetCoord( 2, uv[1].Coord( 2 ));
1081         }
1082         else if ( IsDegenShape( n2->getshapeId() )) {
1083           if ( myParIndex & U_periodic ) uv[1].SetCoord( 1, uv[0].Coord( 1 ));
1084           else                           uv[1].SetCoord( 2, uv[0].Coord( 2 ));
1085         }
1086
1087         TopLoc_Location loc;
1088         Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
1089         gp_XY UV = GetMiddleUV( S, uv[0], uv[1] );
1090         gp_Pnt P = S->Value( UV.X(), UV.Y() ).Transformed(loc);
1091         n12 = meshDS->AddNode(P.X(), P.Y(), P.Z());
1092         meshDS->SetNodeOnFace(n12, faceID, UV.X(), UV.Y());
1093         myTLinkNodeMap.insert(make_pair(link,n12));
1094         return n12;
1095       }
1096     }
1097     else if ( !E.IsNull() )
1098     {
1099       double f,l;
1100       Handle(Geom_Curve) C = BRep_Tool::Curve(E, f, l);
1101       if(!C.IsNull())
1102       {
1103         Standard_Boolean isPeriodic = C->IsPeriodic();
1104         double U;
1105         if(isPeriodic) {
1106           Standard_Real Period = C->Period();
1107           Standard_Real p = u[1]+ShapeAnalysis::AdjustByPeriod(u[1],u[0],Period);
1108           Standard_Real pmid = (u[0]+p)/2.;
1109           U = pmid+ShapeAnalysis::AdjustToPeriod(pmid,C->FirstParameter(),C->LastParameter());
1110         }
1111         else
1112           U = (u[0]+u[1])/2.;
1113
1114         gp_Pnt P = C->Value( U );
1115         n12 = meshDS->AddNode(P.X(), P.Y(), P.Z());
1116         meshDS->SetNodeOnEdge(n12, edgeID, U);
1117         myTLinkNodeMap.insert(make_pair(link,n12));
1118         return n12;
1119       }
1120     }
1121   }
1122
1123   // 3d variant
1124   double x = ( n1->X() + n2->X() )/2.;
1125   double y = ( n1->Y() + n2->Y() )/2.;
1126   double z = ( n1->Z() + n2->Z() )/2.;
1127   n12 = meshDS->AddNode(x,y,z);
1128
1129   if ( !F.IsNull() )
1130   {
1131     gp_XY UV = ( uv[0] + uv[1] ) / 2.;
1132     CheckNodeUV( F, n12, UV, 2*BRep_Tool::Tolerance( F ), /*force=*/true);
1133     meshDS->SetNodeOnFace(n12, faceID, UV.X(), UV.Y() );
1134   }
1135   else if ( !E.IsNull() )
1136   {
1137     double U = ( u[0] + u[1] ) / 2.;
1138     CheckNodeU( E, n12, U, 2*BRep_Tool::Tolerance( E ), /*force=*/true);
1139     meshDS->SetNodeOnEdge(n12, edgeID, U);
1140   }
1141   else if ( myShapeID > 0 )
1142   {
1143     meshDS->SetNodeInVolume(n12, myShapeID);
1144   }
1145
1146   myTLinkNodeMap.insert( make_pair( link, n12 ));
1147   return n12;
1148 }
1149
1150 //================================================================================
1151 /*!
1152  * \brief Makes a medium node if nodes reside different edges
1153  */
1154 //================================================================================
1155
1156 const SMDS_MeshNode* SMESH_MesherHelper::getMediumNodeOnComposedWire(const SMDS_MeshNode* n1,
1157                                                                      const SMDS_MeshNode* n2,
1158                                                                      bool                 force3d)
1159 {
1160   gp_Pnt middle = 0.5 * XYZ(n1) + 0.5 * XYZ(n2);
1161   SMDS_MeshNode* n12 = AddNode( middle.X(), middle.Y(), middle.Z() );
1162
1163   // To find position on edge and 3D position for n12,
1164   // project <middle> to 2 edges and select projection most close to <middle>
1165
1166   double u = 0, distMiddleProj = Precision::Infinite(), distXYZ[4];
1167   int iOkEdge = 0;
1168   TopoDS_Edge edges[2];
1169   for ( int is2nd = 0; is2nd < 2; ++is2nd )
1170   {
1171     // get an edge
1172     const SMDS_MeshNode* n = is2nd ? n2 : n1;
1173     TopoDS_Shape shape = GetSubShapeByNode( n, GetMeshDS() );
1174     if ( shape.IsNull() || shape.ShapeType() != TopAbs_EDGE )
1175       continue;
1176
1177     // project to get U of projection and distance from middle to projection
1178     TopoDS_Edge edge = edges[ is2nd ] = TopoDS::Edge( shape );
1179     double node2MiddleDist = middle.Distance( XYZ(n) );
1180     double foundU = GetNodeU( edge, n );
1181     CheckNodeU( edge, n12, foundU, 2*BRep_Tool::Tolerance(edge), /*force=*/true, distXYZ );
1182     if ( distXYZ[0] < node2MiddleDist )
1183     {
1184       distMiddleProj = distXYZ[0];
1185       u = foundU;
1186       iOkEdge = is2nd;
1187     }
1188   }
1189   if ( Precision::IsInfinite( distMiddleProj ))
1190   {
1191     // both projections failed; set n12 on the edge of n1 with U of a common vertex
1192     TopoDS_Vertex vCommon;
1193     if ( TopExp::CommonVertex( edges[0], edges[1], vCommon ))
1194       u = BRep_Tool::Parameter( vCommon, edges[0] );
1195     else
1196     {
1197       double f,l, u0 = GetNodeU( edges[0], n1 );
1198       BRep_Tool::Range( edges[0],f,l );
1199       u = ( fabs(u0-f) < fabs(u0-l) ) ? f : l;
1200     }
1201     iOkEdge = 0;
1202     distMiddleProj = 0;
1203   }
1204
1205   // move n12 to position of a successfull projection
1206   double tol = BRep_Tool::Tolerance(edges[ iOkEdge ]);
1207   if ( !force3d && distMiddleProj > 2*tol )
1208   {
1209     TopLoc_Location loc; double f,l;
1210     Handle(Geom_Curve) curve = BRep_Tool::Curve( edges[iOkEdge],loc,f,l );
1211     gp_Pnt p = curve->Value( u );
1212     GetMeshDS()->MoveNode( n12, p.X(), p.Y(), p.Z() );
1213   }
1214
1215   GetMeshDS()->SetNodeOnEdge(n12, edges[iOkEdge], u);
1216
1217   myTLinkNodeMap.insert( make_pair( SMESH_TLink(n1,n2), n12 ));
1218
1219   return n12;
1220 }
1221
1222 //=======================================================================
1223 //function : AddNode
1224 //purpose  : Creates a node
1225 //=======================================================================
1226
1227 SMDS_MeshNode* SMESH_MesherHelper::AddNode(double x, double y, double z, int ID,
1228                                            double u, double v)
1229 {
1230   SMESHDS_Mesh * meshDS = GetMeshDS();
1231   SMDS_MeshNode* node = 0;
1232   if ( ID )
1233     node = meshDS->AddNodeWithID( x, y, z, ID );
1234   else
1235     node = meshDS->AddNode( x, y, z );
1236   if ( mySetElemOnShape && myShapeID > 0 ) {
1237     switch ( myShape.ShapeType() ) {
1238     case TopAbs_SOLID:  meshDS->SetNodeInVolume( node, myShapeID);       break;
1239     case TopAbs_SHELL:  meshDS->SetNodeInVolume( node, myShapeID);       break;
1240     case TopAbs_FACE:   meshDS->SetNodeOnFace(   node, myShapeID, u, v); break;
1241     case TopAbs_EDGE:   meshDS->SetNodeOnEdge(   node, myShapeID, u);    break;
1242     case TopAbs_VERTEX: meshDS->SetNodeOnVertex( node, myShapeID);       break;
1243     default: ;
1244     }
1245   }
1246   return node;
1247 }
1248
1249 //=======================================================================
1250 //function : AddEdge
1251 //purpose  : Creates quadratic or linear edge
1252 //=======================================================================
1253
1254 SMDS_MeshEdge* SMESH_MesherHelper::AddEdge(const SMDS_MeshNode* n1,
1255                                            const SMDS_MeshNode* n2,
1256                                            const int            id,
1257                                            const bool           force3d)
1258 {
1259   SMESHDS_Mesh * meshDS = GetMeshDS();
1260   
1261   SMDS_MeshEdge* edge = 0;
1262   if (myCreateQuadratic) {
1263     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1264     if(id)
1265       edge = meshDS->AddEdgeWithID(n1, n2, n12, id);
1266     else
1267       edge = meshDS->AddEdge(n1, n2, n12);
1268   }
1269   else {
1270     if(id)
1271       edge = meshDS->AddEdgeWithID(n1, n2, id);
1272     else
1273       edge = meshDS->AddEdge(n1, n2);
1274   }
1275
1276   if ( mySetElemOnShape && myShapeID > 0 )
1277     meshDS->SetMeshElementOnShape( edge, myShapeID );
1278
1279   return edge;
1280 }
1281
1282 //=======================================================================
1283 //function : AddFace
1284 //purpose  : Creates quadratic or linear triangle
1285 //=======================================================================
1286
1287 SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1,
1288                                            const SMDS_MeshNode* n2,
1289                                            const SMDS_MeshNode* n3,
1290                                            const int id,
1291                                            const bool force3d)
1292 {
1293   SMESHDS_Mesh * meshDS = GetMeshDS();
1294   SMDS_MeshFace* elem = 0;
1295
1296   if( n1==n2 || n2==n3 || n3==n1 )
1297     return elem;
1298
1299   if(!myCreateQuadratic) {
1300     if(id)
1301       elem = meshDS->AddFaceWithID(n1, n2, n3, id);
1302     else
1303       elem = meshDS->AddFace(n1, n2, n3);
1304   }
1305   else {
1306     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1307     const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d);
1308     const SMDS_MeshNode* n31 = GetMediumNode(n3,n1,force3d);
1309
1310     if(id)
1311       elem = meshDS->AddFaceWithID(n1, n2, n3, n12, n23, n31, id);
1312     else
1313       elem = meshDS->AddFace(n1, n2, n3, n12, n23, n31);
1314   }
1315   if ( mySetElemOnShape && myShapeID > 0 )
1316     meshDS->SetMeshElementOnShape( elem, myShapeID );
1317
1318   return elem;
1319 }
1320
1321 //=======================================================================
1322 //function : AddFace
1323 //purpose  : Creates quadratic or linear quadrangle
1324 //=======================================================================
1325
1326 SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1,
1327                                            const SMDS_MeshNode* n2,
1328                                            const SMDS_MeshNode* n3,
1329                                            const SMDS_MeshNode* n4,
1330                                            const int            id,
1331                                            const bool           force3d)
1332 {
1333   SMESHDS_Mesh * meshDS = GetMeshDS();
1334   SMDS_MeshFace* elem = 0;
1335
1336   if( n1==n2 ) {
1337     return AddFace(n1,n3,n4,id,force3d);
1338   }
1339   if( n1==n3 ) {
1340     return AddFace(n1,n2,n4,id,force3d);
1341   }
1342   if( n1==n4 ) {
1343     return AddFace(n1,n2,n3,id,force3d);
1344   }
1345   if( n2==n3 ) {
1346     return AddFace(n1,n2,n4,id,force3d);
1347   }
1348   if( n2==n4 ) {
1349     return AddFace(n1,n2,n3,id,force3d);
1350   }
1351   if( n3==n4 ) {
1352     return AddFace(n1,n2,n3,id,force3d);
1353   }
1354
1355   if(!myCreateQuadratic) {
1356     if(id)
1357       elem = meshDS->AddFaceWithID(n1, n2, n3, n4, id);
1358     else
1359       elem = meshDS->AddFace(n1, n2, n3, n4);
1360   }
1361   else {
1362     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1363     const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d);
1364     const SMDS_MeshNode* n34 = GetMediumNode(n3,n4,force3d);
1365     const SMDS_MeshNode* n41 = GetMediumNode(n4,n1,force3d);
1366
1367     if(id)
1368       elem = meshDS->AddFaceWithID(n1, n2, n3, n4, n12, n23, n34, n41, id);
1369     else
1370       elem = meshDS->AddFace(n1, n2, n3, n4, n12, n23, n34, n41);
1371   }
1372   if ( mySetElemOnShape && myShapeID > 0 )
1373     meshDS->SetMeshElementOnShape( elem, myShapeID );
1374
1375   return elem;
1376 }
1377
1378 //=======================================================================
1379 //function : AddPolygonalFace
1380 //purpose  : Creates polygon, with additional nodes in quadratic mesh
1381 //=======================================================================
1382
1383 SMDS_MeshFace* SMESH_MesherHelper::AddPolygonalFace (const vector<const SMDS_MeshNode*>& nodes,
1384                                                      const int                           id,
1385                                                      const bool                          force3d)
1386 {
1387   SMESHDS_Mesh * meshDS = GetMeshDS();
1388   SMDS_MeshFace* elem = 0;
1389
1390   if(!myCreateQuadratic) {
1391     if(id)
1392       elem = meshDS->AddPolygonalFaceWithID(nodes, id);
1393     else
1394       elem = meshDS->AddPolygonalFace(nodes);
1395   }
1396   else {
1397     vector<const SMDS_MeshNode*> newNodes;
1398     for ( int i = 0; i < nodes.size(); ++i )
1399     {
1400       const SMDS_MeshNode* n1 = nodes[i];
1401       const SMDS_MeshNode* n2 = nodes[(i+1)%nodes.size()];
1402       const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1403       newNodes.push_back( n1 );
1404       newNodes.push_back( n12 );
1405     }
1406     if(id)
1407       elem = meshDS->AddPolygonalFaceWithID(newNodes, id);
1408     else
1409       elem = meshDS->AddPolygonalFace(newNodes);
1410   }
1411   if ( mySetElemOnShape && myShapeID > 0 )
1412     meshDS->SetMeshElementOnShape( elem, myShapeID );
1413
1414   return elem;
1415 }
1416
1417 //=======================================================================
1418 //function : AddVolume
1419 //purpose  : Creates quadratic or linear prism
1420 //=======================================================================
1421
1422 SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1,
1423                                                const SMDS_MeshNode* n2,
1424                                                const SMDS_MeshNode* n3,
1425                                                const SMDS_MeshNode* n4,
1426                                                const SMDS_MeshNode* n5,
1427                                                const SMDS_MeshNode* n6,
1428                                                const int id,
1429                                                const bool force3d)
1430 {
1431   SMESHDS_Mesh * meshDS = GetMeshDS();
1432   SMDS_MeshVolume* elem = 0;
1433   if(!myCreateQuadratic) {
1434     if(id)
1435       elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, n5, n6, id);
1436     else
1437       elem = meshDS->AddVolume(n1, n2, n3, n4, n5, n6);
1438   }
1439   else {
1440     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1441     const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d);
1442     const SMDS_MeshNode* n31 = GetMediumNode(n3,n1,force3d);
1443
1444     const SMDS_MeshNode* n45 = GetMediumNode(n4,n5,force3d);
1445     const SMDS_MeshNode* n56 = GetMediumNode(n5,n6,force3d);
1446     const SMDS_MeshNode* n64 = GetMediumNode(n6,n4,force3d);
1447
1448     const SMDS_MeshNode* n14 = GetMediumNode(n1,n4,force3d);
1449     const SMDS_MeshNode* n25 = GetMediumNode(n2,n5,force3d);
1450     const SMDS_MeshNode* n36 = GetMediumNode(n3,n6,force3d);
1451
1452     if(id)
1453       elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, n5, n6, 
1454                                      n12, n23, n31, n45, n56, n64, n14, n25, n36, id);
1455     else
1456       elem = meshDS->AddVolume(n1, n2, n3, n4, n5, n6,
1457                                n12, n23, n31, n45, n56, n64, n14, n25, n36);
1458   }
1459   if ( mySetElemOnShape && myShapeID > 0 )
1460     meshDS->SetMeshElementOnShape( elem, myShapeID );
1461
1462   return elem;
1463 }
1464
1465 //=======================================================================
1466 //function : AddVolume
1467 //purpose  : Creates quadratic or linear tetrahedron
1468 //=======================================================================
1469
1470 SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1,
1471                                                const SMDS_MeshNode* n2,
1472                                                const SMDS_MeshNode* n3,
1473                                                const SMDS_MeshNode* n4,
1474                                                const int id, 
1475                                                const bool force3d)
1476 {
1477   SMESHDS_Mesh * meshDS = GetMeshDS();
1478   SMDS_MeshVolume* elem = 0;
1479   if(!myCreateQuadratic) {
1480     if(id)
1481       elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, id);
1482     else
1483       elem = meshDS->AddVolume(n1, n2, n3, n4);
1484   }
1485   else {
1486     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1487     const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d);
1488     const SMDS_MeshNode* n31 = GetMediumNode(n3,n1,force3d);
1489
1490     const SMDS_MeshNode* n14 = GetMediumNode(n1,n4,force3d);
1491     const SMDS_MeshNode* n24 = GetMediumNode(n2,n4,force3d);
1492     const SMDS_MeshNode* n34 = GetMediumNode(n3,n4,force3d);
1493
1494     if(id)
1495       elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, n12, n23, n31, n14, n24, n34, id);
1496     else
1497       elem = meshDS->AddVolume(n1, n2, n3, n4, n12, n23, n31, n14, n24, n34);
1498   }
1499   if ( mySetElemOnShape && myShapeID > 0 )
1500     meshDS->SetMeshElementOnShape( elem, myShapeID );
1501
1502   return elem;
1503 }
1504
1505 //=======================================================================
1506 //function : AddVolume
1507 //purpose  : Creates quadratic or linear pyramid
1508 //=======================================================================
1509
1510 SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1,
1511                                                const SMDS_MeshNode* n2,
1512                                                const SMDS_MeshNode* n3,
1513                                                const SMDS_MeshNode* n4,
1514                                                const SMDS_MeshNode* n5,
1515                                                const int id, 
1516                                                const bool force3d)
1517 {
1518   SMDS_MeshVolume* elem = 0;
1519   if(!myCreateQuadratic) {
1520     if(id)
1521       elem = GetMeshDS()->AddVolumeWithID(n1, n2, n3, n4, n5, id);
1522     else
1523       elem = GetMeshDS()->AddVolume(n1, n2, n3, n4, n5);
1524   }
1525   else {
1526     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1527     const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d);
1528     const SMDS_MeshNode* n34 = GetMediumNode(n3,n4,force3d);
1529     const SMDS_MeshNode* n41 = GetMediumNode(n4,n1,force3d);
1530
1531     const SMDS_MeshNode* n15 = GetMediumNode(n1,n5,force3d);
1532     const SMDS_MeshNode* n25 = GetMediumNode(n2,n5,force3d);
1533     const SMDS_MeshNode* n35 = GetMediumNode(n3,n5,force3d);
1534     const SMDS_MeshNode* n45 = GetMediumNode(n4,n5,force3d);
1535
1536     if(id)
1537       elem = GetMeshDS()->AddVolumeWithID ( n1,  n2,  n3,  n4,  n5,
1538                                             n12, n23, n34, n41,
1539                                             n15, n25, n35, n45,
1540                                             id);
1541     else
1542       elem = GetMeshDS()->AddVolume( n1,  n2,  n3,  n4,  n5,
1543                                      n12, n23, n34, n41,
1544                                      n15, n25, n35, n45);
1545   }
1546   if ( mySetElemOnShape && myShapeID > 0 )
1547     GetMeshDS()->SetMeshElementOnShape( elem, myShapeID );
1548
1549   return elem;
1550 }
1551
1552 //=======================================================================
1553 //function : AddVolume
1554 //purpose  : Creates quadratic or linear hexahedron
1555 //=======================================================================
1556
1557 SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1,
1558                                                const SMDS_MeshNode* n2,
1559                                                const SMDS_MeshNode* n3,
1560                                                const SMDS_MeshNode* n4,
1561                                                const SMDS_MeshNode* n5,
1562                                                const SMDS_MeshNode* n6,
1563                                                const SMDS_MeshNode* n7,
1564                                                const SMDS_MeshNode* n8,
1565                                                const int id,
1566                                                const bool force3d)
1567 {
1568   SMESHDS_Mesh * meshDS = GetMeshDS();
1569   SMDS_MeshVolume* elem = 0;
1570   if(!myCreateQuadratic) {
1571     if(id)
1572       elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, n5, n6, n7, n8, id);
1573     else
1574       elem = meshDS->AddVolume(n1, n2, n3, n4, n5, n6, n7, n8);
1575   }
1576   else {
1577     const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1578     const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d);
1579     const SMDS_MeshNode* n34 = GetMediumNode(n3,n4,force3d);
1580     const SMDS_MeshNode* n41 = GetMediumNode(n4,n1,force3d);
1581
1582     const SMDS_MeshNode* n56 = GetMediumNode(n5,n6,force3d);
1583     const SMDS_MeshNode* n67 = GetMediumNode(n6,n7,force3d);
1584     const SMDS_MeshNode* n78 = GetMediumNode(n7,n8,force3d);
1585     const SMDS_MeshNode* n85 = GetMediumNode(n8,n5,force3d);
1586
1587     const SMDS_MeshNode* n15 = GetMediumNode(n1,n5,force3d);
1588     const SMDS_MeshNode* n26 = GetMediumNode(n2,n6,force3d);
1589     const SMDS_MeshNode* n37 = GetMediumNode(n3,n7,force3d);
1590     const SMDS_MeshNode* n48 = GetMediumNode(n4,n8,force3d);
1591
1592     if(id)
1593       elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, n5, n6, n7, n8,
1594                                      n12, n23, n34, n41, n56, n67,
1595                                      n78, n85, n15, n26, n37, n48, id);
1596     else
1597       elem = meshDS->AddVolume(n1, n2, n3, n4, n5, n6, n7, n8,
1598                                n12, n23, n34, n41, n56, n67,
1599                                n78, n85, n15, n26, n37, n48);
1600   }
1601   if ( mySetElemOnShape && myShapeID > 0 )
1602     meshDS->SetMeshElementOnShape( elem, myShapeID );
1603
1604   return elem;
1605 }
1606
1607 //=======================================================================
1608 //function : AddVolume
1609 //purpose  : Creates LINEAR!!!!!!!!! octahedron
1610 //=======================================================================
1611
1612 SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1,
1613                                                const SMDS_MeshNode* n2,
1614                                                const SMDS_MeshNode* n3,
1615                                                const SMDS_MeshNode* n4,
1616                                                const SMDS_MeshNode* n5,
1617                                                const SMDS_MeshNode* n6,
1618                                                const SMDS_MeshNode* n7,
1619                                                const SMDS_MeshNode* n8,
1620                                                const SMDS_MeshNode* n9,
1621                                                const SMDS_MeshNode* n10,
1622                                                const SMDS_MeshNode* n11,
1623                                                const SMDS_MeshNode* n12,
1624                                                const int id, 
1625                                                bool force3d)
1626 {
1627   SMESHDS_Mesh * meshDS = GetMeshDS();
1628   SMDS_MeshVolume* elem = 0;
1629   if(id)
1630     elem = meshDS->AddVolumeWithID(n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,id);
1631   else
1632     elem = meshDS->AddVolume(n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12);
1633   if ( mySetElemOnShape && myShapeID > 0 )
1634     meshDS->SetMeshElementOnShape( elem, myShapeID );
1635   return elem;
1636 }
1637
1638 //=======================================================================
1639 //function : AddPolyhedralVolume
1640 //purpose  : Creates polyhedron. In quadratic mesh, adds medium nodes
1641 //=======================================================================
1642
1643 SMDS_MeshVolume*
1644 SMESH_MesherHelper::AddPolyhedralVolume (const std::vector<const SMDS_MeshNode*>& nodes,
1645                                          const std::vector<int>&                  quantities,
1646                                          const int                                id,
1647                                          const bool                               force3d)
1648 {
1649   SMESHDS_Mesh * meshDS = GetMeshDS();
1650   SMDS_MeshVolume* elem = 0;
1651   if(!myCreateQuadratic)
1652   {
1653     if(id)
1654       elem = meshDS->AddPolyhedralVolumeWithID(nodes, quantities, id);
1655     else
1656       elem = meshDS->AddPolyhedralVolume(nodes, quantities);
1657   }
1658   else
1659   {
1660     vector<const SMDS_MeshNode*> newNodes;
1661     vector<int> newQuantities;
1662     for ( int iFace=0, iN=0; iFace < quantities.size(); ++iFace)
1663     {
1664       int nbNodesInFace = quantities[iFace];
1665       newQuantities.push_back(0);
1666       for ( int i = 0; i < nbNodesInFace; ++i )
1667       {
1668         const SMDS_MeshNode* n1 = nodes[ iN + i ];
1669         newNodes.push_back( n1 );
1670         newQuantities.back()++;
1671         
1672         const SMDS_MeshNode* n2 = nodes[ iN + ( i+1==nbNodesInFace ? 0 : i+1 )];
1673 //         if ( n1->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE &&
1674 //              n2->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE )
1675         {
1676           const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d);
1677           newNodes.push_back( n12 );
1678           newQuantities.back()++;
1679         }
1680       }
1681       iN += nbNodesInFace;
1682     }
1683     if(id)
1684       elem = meshDS->AddPolyhedralVolumeWithID( newNodes, newQuantities, id );
1685     else
1686       elem = meshDS->AddPolyhedralVolume( newNodes, newQuantities );
1687   }
1688   if ( mySetElemOnShape && myShapeID > 0 )
1689     meshDS->SetMeshElementOnShape( elem, myShapeID );
1690
1691   return elem;
1692 }
1693
1694 namespace
1695 {
1696   //================================================================================
1697   /*!
1698    * \brief Check if a node belongs to any face of sub-mesh
1699    */
1700   //================================================================================
1701
1702   bool isNodeInSubMesh( const SMDS_MeshNode* n, const SMESHDS_SubMesh* sm )
1703   {
1704     SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator( SMDSAbs_Face );
1705     while ( fIt->more() )
1706       if ( sm->Contains( fIt->next() ))
1707         return true;
1708     return false;
1709   }
1710 }
1711
1712 //=======================================================================
1713 //function : LoadNodeColumns
1714 //purpose  : Load nodes bound to face into a map of node columns
1715 //=======================================================================
1716
1717 bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap,
1718                                          const TopoDS_Face& theFace,
1719                                          const TopoDS_Edge& theBaseEdge,
1720                                          SMESHDS_Mesh*      theMesh,
1721                                          SMESH_ProxyMesh*   theProxyMesh)
1722 {
1723   return LoadNodeColumns(theParam2ColumnMap,
1724                          theFace,
1725                          std::list<TopoDS_Edge>(1,theBaseEdge),
1726                          theMesh,
1727                          theProxyMesh);
1728 }
1729
1730 //=======================================================================
1731 //function : LoadNodeColumns
1732 //purpose  : Load nodes bound to face into a map of node columns
1733 //=======================================================================
1734
1735 bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap &            theParam2ColumnMap,
1736                                          const TopoDS_Face&            theFace,
1737                                          const std::list<TopoDS_Edge>& theBaseSide,
1738                                          SMESHDS_Mesh*                 theMesh,
1739                                          SMESH_ProxyMesh*              theProxyMesh)
1740 {
1741   // get a right submesh of theFace
1742
1743   const SMESHDS_SubMesh* faceSubMesh = 0;
1744   if ( theProxyMesh )
1745   {
1746     faceSubMesh = theProxyMesh->GetSubMesh( theFace );
1747     if ( !faceSubMesh ||
1748          faceSubMesh->NbElements() == 0 ||
1749          theProxyMesh->IsTemporary( faceSubMesh->GetElements()->next() ))
1750     {
1751       // can use a proxy sub-mesh with not temporary elements only
1752       faceSubMesh = 0;
1753       theProxyMesh = 0;
1754     }
1755   }
1756   if ( !faceSubMesh )
1757     faceSubMesh = theMesh->MeshElements( theFace );
1758   if ( !faceSubMesh || faceSubMesh->NbElements() == 0 )
1759     return false;
1760
1761   // get data of edges for normalization of params
1762
1763   vector< double > length;
1764   double fullLen = 0;
1765   list<TopoDS_Edge>::const_iterator edge;
1766   {
1767     for ( edge = theBaseSide.begin(); edge != theBaseSide.end(); ++edge )
1768     {
1769       double len = std::max( 1e-10, SMESH_Algo::EdgeLength( *edge ));
1770       fullLen += len;
1771       length.push_back( len );
1772     }
1773   }
1774
1775   // get nodes on theBaseEdge sorted by param on edge and initialize theParam2ColumnMap with them
1776   edge = theBaseSide.begin();
1777   for ( int iE = 0; edge != theBaseSide.end(); ++edge, ++iE )
1778   {
1779     map< double, const SMDS_MeshNode*> sortedBaseNodes;
1780     SMESH_Algo::GetSortedNodesOnEdge( theMesh, *edge,/*noMedium=*/true, sortedBaseNodes);
1781     if ( sortedBaseNodes.empty() ) continue;
1782
1783     map< double, const SMDS_MeshNode*>::iterator u_n = sortedBaseNodes.begin();
1784     if ( theProxyMesh ) // from sortedBaseNodes remove nodes not shared by faces of faceSubMesh
1785     {
1786       const SMDS_MeshNode* n1 = sortedBaseNodes.begin()->second;
1787       const SMDS_MeshNode* n2 = sortedBaseNodes.rbegin()->second;
1788       bool allNodesAreProxy = ( n1 != theProxyMesh->GetProxyNode( n1 ) &&
1789                                 n2 != theProxyMesh->GetProxyNode( n2 ));
1790       if ( allNodesAreProxy )
1791         for ( u_n = sortedBaseNodes.begin(); u_n != sortedBaseNodes.end(); u_n++ )
1792           u_n->second = theProxyMesh->GetProxyNode( u_n->second );
1793
1794       if ( u_n = sortedBaseNodes.begin(), !isNodeInSubMesh( u_n->second, faceSubMesh ))
1795       {
1796         while ( ++u_n != sortedBaseNodes.end() && !isNodeInSubMesh( u_n->second, faceSubMesh ));
1797         sortedBaseNodes.erase( sortedBaseNodes.begin(), u_n );
1798       }
1799       else if ( u_n = --sortedBaseNodes.end(), !isNodeInSubMesh( u_n->second, faceSubMesh ))
1800       {
1801         while ( u_n != sortedBaseNodes.begin() && !isNodeInSubMesh( (--u_n)->second, faceSubMesh ));
1802         sortedBaseNodes.erase( ++u_n, sortedBaseNodes.end() );
1803       }
1804       if ( sortedBaseNodes.empty() ) continue;
1805     }
1806
1807     double f, l;
1808     BRep_Tool::Range( *edge, f, l );
1809     if ( edge->Orientation() == TopAbs_REVERSED ) std::swap( f, l );
1810     const double coeff = 1. / ( l - f ) * length[iE] / fullLen;
1811     const double prevPar = theParam2ColumnMap.empty() ? 0 : theParam2ColumnMap.rbegin()->first;
1812     for ( u_n = sortedBaseNodes.begin(); u_n != sortedBaseNodes.end(); u_n++ )
1813     {
1814       double par = prevPar + coeff * ( u_n->first - f );
1815       TParam2ColumnMap::iterator u2nn =
1816         theParam2ColumnMap.insert( theParam2ColumnMap.end(), make_pair( par, TNodeColumn()));
1817       u2nn->second.push_back( u_n->second );
1818     }
1819   }
1820   if ( theParam2ColumnMap.empty() )
1821     return false;
1822
1823
1824   int nbRows = 1 + faceSubMesh->NbElements() / ( theParam2ColumnMap.size()-1 );
1825
1826   // fill theParam2ColumnMap column by column by passing from nodes on
1827   // theBaseEdge up via mesh faces on theFace
1828
1829   TParam2ColumnMap::iterator par_nVec_1, par_nVec_2;
1830   par_nVec_2 = theParam2ColumnMap.begin();
1831   par_nVec_1 = par_nVec_2++;
1832   TIDSortedElemSet emptySet, avoidSet;
1833   for ( ; par_nVec_2 != theParam2ColumnMap.end(); ++par_nVec_1, ++par_nVec_2 )
1834   {
1835     vector<const SMDS_MeshNode*>& nCol1 = par_nVec_1->second;
1836     vector<const SMDS_MeshNode*>& nCol2 = par_nVec_2->second;
1837     nCol1.resize( nbRows );
1838     nCol2.resize( nbRows );
1839
1840     int i1, i2, iRow = 0;
1841     const SMDS_MeshNode *n1 = nCol1[0], *n2 = nCol2[0];
1842     // find face sharing node n1 and n2 and belonging to faceSubMesh
1843     while ( const SMDS_MeshElement* face =
1844             SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2))
1845     {
1846       if ( faceSubMesh->Contains( face ))
1847       {
1848         int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes();
1849         if ( nbNodes != 4 )
1850           return false;
1851         n1 = face->GetNode( (i2+2) % 4 ); // opposite corner of quadrangle face
1852         n2 = face->GetNode( (i1+2) % 4 );
1853         if ( ++iRow >= nbRows )
1854           return false;
1855         nCol1[ iRow ] = n1;
1856         nCol2[ iRow ] = n2;
1857         avoidSet.clear();
1858       }
1859       avoidSet.insert( face );
1860     }
1861     // set a real height
1862     nCol1.resize( iRow + 1 );
1863     nCol2.resize( iRow + 1 );
1864   }
1865   return theParam2ColumnMap.size() > 1 && theParam2ColumnMap.begin()->second.size() > 1;
1866 }
1867
1868 //=======================================================================
1869 //function : NbAncestors
1870 //purpose  : Return number of unique ancestors of the shape
1871 //=======================================================================
1872
1873 int SMESH_MesherHelper::NbAncestors(const TopoDS_Shape& shape,
1874                                     const SMESH_Mesh&   mesh,
1875                                     TopAbs_ShapeEnum    ancestorType/*=TopAbs_SHAPE*/)
1876 {
1877   TopTools_MapOfShape ancestors;
1878   TopTools_ListIteratorOfListOfShape ansIt( mesh.GetAncestors(shape) );
1879   for ( ; ansIt.More(); ansIt.Next() ) {
1880     if ( ancestorType == TopAbs_SHAPE || ansIt.Value().ShapeType() == ancestorType )
1881       ancestors.Add( ansIt.Value() );
1882   }
1883   return ancestors.Extent();
1884 }
1885
1886 //=======================================================================
1887 //function : GetSubShapeOri
1888 //purpose  : Return orientation of sub-shape in the main shape
1889 //=======================================================================
1890
1891 TopAbs_Orientation SMESH_MesherHelper::GetSubShapeOri(const TopoDS_Shape& shape,
1892                                                       const TopoDS_Shape& subShape)
1893 {
1894   TopAbs_Orientation ori = TopAbs_Orientation(-1);
1895   if ( !shape.IsNull() && !subShape.IsNull() )
1896   {
1897     TopExp_Explorer e( shape, subShape.ShapeType() );
1898     if ( shape.Orientation() >= TopAbs_INTERNAL ) // TopAbs_INTERNAL or TopAbs_EXTERNAL
1899       e.Init( shape.Oriented(TopAbs_FORWARD), subShape.ShapeType() );
1900     for ( ; e.More(); e.Next())
1901       if ( subShape.IsSame( e.Current() ))
1902         break;
1903     if ( e.More() )
1904       ori = e.Current().Orientation();
1905   }
1906   return ori;
1907 }
1908
1909 //=======================================================================
1910 //function : IsSubShape
1911 //purpose  : 
1912 //=======================================================================
1913
1914 bool SMESH_MesherHelper::IsSubShape( const TopoDS_Shape& shape,
1915                                      const TopoDS_Shape& mainShape )
1916 {
1917   if ( !shape.IsNull() && !mainShape.IsNull() )
1918   {
1919     for ( TopExp_Explorer exp( mainShape, shape.ShapeType());
1920           exp.More();
1921           exp.Next() )
1922       if ( shape.IsSame( exp.Current() ))
1923         return true;
1924   }
1925   SCRUTE((shape.IsNull()));
1926   SCRUTE((mainShape.IsNull()));
1927   return false;
1928 }
1929
1930 //=======================================================================
1931 //function : IsSubShape
1932 //purpose  : 
1933 //=======================================================================
1934
1935 bool SMESH_MesherHelper::IsSubShape( const TopoDS_Shape& shape, SMESH_Mesh* aMesh )
1936 {
1937   if ( shape.IsNull() || !aMesh )
1938     return false;
1939   return
1940     aMesh->GetMeshDS()->ShapeToIndex( shape ) ||
1941     // PAL16202
1942     (shape.ShapeType() == TopAbs_COMPOUND && aMesh->GetMeshDS()->IsGroupOfSubShapes( shape ));
1943 }
1944
1945 //================================================================================
1946 /*!
1947  * \brief Return maximal tolerance of shape
1948  */
1949 //================================================================================
1950
1951 double SMESH_MesherHelper::MaxTolerance( const TopoDS_Shape& shape )
1952 {
1953   double tol = Precision::Confusion();
1954   TopExp_Explorer exp;
1955   for ( exp.Init( shape, TopAbs_FACE ); exp.More(); exp.Next() )
1956     tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Face( exp.Current())));
1957   for ( exp.Init( shape, TopAbs_EDGE ); exp.More(); exp.Next() )
1958     tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Edge( exp.Current())));
1959   for ( exp.Init( shape, TopAbs_VERTEX ); exp.More(); exp.Next() )
1960     tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Vertex( exp.Current())));
1961
1962   return tol;
1963 }
1964
1965 //================================================================================
1966 /*!
1967  * \brief Check if the first and last vertices of an edge are the same
1968  * \param anEdge - the edge to check
1969  * \retval bool - true if same
1970  */
1971 //================================================================================
1972
1973 bool SMESH_MesherHelper::IsClosedEdge( const TopoDS_Edge& anEdge )
1974 {
1975   if ( anEdge.Orientation() >= TopAbs_INTERNAL )
1976     return IsClosedEdge( TopoDS::Edge( anEdge.Oriented( TopAbs_FORWARD )));
1977   return TopExp::FirstVertex( anEdge ).IsSame( TopExp::LastVertex( anEdge ));
1978 }
1979
1980 //================================================================================
1981 /*!
1982  * \brief Wrapper over TopExp::FirstVertex() and TopExp::LastVertex() fixing them
1983  *  in the case of INTERNAL edge
1984  */
1985 //================================================================================
1986
1987 TopoDS_Vertex SMESH_MesherHelper::IthVertex( const bool  is2nd,
1988                                              TopoDS_Edge anEdge,
1989                                              const bool  CumOri )
1990 {
1991   if ( anEdge.Orientation() >= TopAbs_INTERNAL )
1992     anEdge.Orientation( TopAbs_FORWARD );
1993
1994   const TopAbs_Orientation tgtOri = is2nd ? TopAbs_REVERSED : TopAbs_FORWARD;
1995   TopoDS_Iterator vIt( anEdge, CumOri );
1996   while ( vIt.More() && vIt.Value().Orientation() != tgtOri )
1997     vIt.Next();
1998
1999   return ( vIt.More() ? TopoDS::Vertex(vIt.Value()) : TopoDS_Vertex() );
2000 }
2001
2002 //================================================================================
2003 /*!
2004  * \brief Return type of shape contained in a group 
2005  *  \param group - a shape of type TopAbs_COMPOUND
2006  *  \param avoidCompound - not to return TopAbs_COMPOUND
2007  */
2008 //================================================================================
2009
2010 TopAbs_ShapeEnum SMESH_MesherHelper::GetGroupType(const TopoDS_Shape& group,
2011                                                   const bool          avoidCompound)
2012 {
2013   if ( !group.IsNull() )
2014   {
2015     if ( group.ShapeType() != TopAbs_COMPOUND )
2016       return group.ShapeType();
2017
2018     // iterate on a compound
2019     TopoDS_Iterator it( group );
2020     if ( it.More() )
2021       return avoidCompound ? GetGroupType( it.Value() ) : it.Value().ShapeType();
2022   }
2023   return TopAbs_SHAPE;
2024 }
2025
2026 //=======================================================================
2027 //function : IsQuadraticMesh
2028 //purpose  : Check mesh without geometry for: if all elements on this shape are quadratic,
2029 //           quadratic elements will be created.
2030 //           Used then generated 3D mesh without geometry.
2031 //=======================================================================
2032
2033 SMESH_MesherHelper:: MType SMESH_MesherHelper::IsQuadraticMesh()
2034 {
2035   int NbAllEdgsAndFaces=0;
2036   int NbQuadFacesAndEdgs=0;
2037   int NbFacesAndEdges=0;
2038   //All faces and edges
2039   NbAllEdgsAndFaces = myMesh->NbEdges() + myMesh->NbFaces();
2040   if ( NbAllEdgsAndFaces == 0 )
2041     return SMESH_MesherHelper::LINEAR;
2042   
2043   //Quadratic faces and edges
2044   NbQuadFacesAndEdgs = myMesh->NbEdges(ORDER_QUADRATIC) + myMesh->NbFaces(ORDER_QUADRATIC);
2045
2046   //Linear faces and edges
2047   NbFacesAndEdges = myMesh->NbEdges(ORDER_LINEAR) + myMesh->NbFaces(ORDER_LINEAR);
2048   
2049   if (NbAllEdgsAndFaces == NbQuadFacesAndEdgs) {
2050     //Quadratic mesh
2051     return SMESH_MesherHelper::QUADRATIC;
2052   }
2053   else if (NbAllEdgsAndFaces == NbFacesAndEdges) {
2054     //Linear mesh
2055     return SMESH_MesherHelper::LINEAR;
2056   }
2057   else
2058     //Mesh with both type of elements
2059     return SMESH_MesherHelper::COMP;
2060 }
2061
2062 //=======================================================================
2063 //function : GetOtherParam
2064 //purpose  : Return an alternative parameter for a node on seam
2065 //=======================================================================
2066
2067 double SMESH_MesherHelper::GetOtherParam(const double param) const
2068 {
2069   int i = myParIndex & U_periodic ? 0 : 1;
2070   return fabs(param-myPar1[i]) < fabs(param-myPar2[i]) ? myPar2[i] : myPar1[i];
2071 }
2072
2073 namespace {
2074
2075   //=======================================================================
2076   /*!
2077    * \brief Iterator on ancestors of the given type
2078    */
2079   //=======================================================================
2080
2081   struct TAncestorsIterator : public SMDS_Iterator<const TopoDS_Shape*>
2082   {
2083     TopTools_ListIteratorOfListOfShape _ancIter;
2084     TopAbs_ShapeEnum                   _type;
2085     TopTools_MapOfShape                _encountered;
2086     TAncestorsIterator( const TopTools_ListOfShape& ancestors, TopAbs_ShapeEnum type)
2087       : _ancIter( ancestors ), _type( type )
2088     {
2089       if ( _ancIter.More() ) {
2090         if ( _ancIter.Value().ShapeType() != _type ) next();
2091         else _encountered.Add( _ancIter.Value() );
2092       }
2093     }
2094     virtual bool more()
2095     {
2096       return _ancIter.More();
2097     }
2098     virtual const TopoDS_Shape* next()
2099     {
2100       const TopoDS_Shape* s = _ancIter.More() ? & _ancIter.Value() : 0;
2101       if ( _ancIter.More() )
2102         for ( _ancIter.Next();  _ancIter.More(); _ancIter.Next())
2103           if ( _ancIter.Value().ShapeType() == _type && _encountered.Add( _ancIter.Value() ))
2104             break;
2105       return s;
2106     }
2107   };
2108
2109 } // namespace
2110
2111 //=======================================================================
2112 /*!
2113  * \brief Return iterator on ancestors of the given type
2114  */
2115 //=======================================================================
2116
2117 PShapeIteratorPtr SMESH_MesherHelper::GetAncestors(const TopoDS_Shape& shape,
2118                                                    const SMESH_Mesh&   mesh,
2119                                                    TopAbs_ShapeEnum    ancestorType)
2120 {
2121   return PShapeIteratorPtr( new TAncestorsIterator( mesh.GetAncestors(shape), ancestorType));
2122 }
2123
2124 //=======================================================================
2125 //function : GetCommonAncestor
2126 //purpose  : Find a common ancestors of two shapes of the given type
2127 //=======================================================================
2128
2129 TopoDS_Shape SMESH_MesherHelper::GetCommonAncestor(const TopoDS_Shape& shape1,
2130                                                    const TopoDS_Shape& shape2,
2131                                                    const SMESH_Mesh&   mesh,
2132                                                    TopAbs_ShapeEnum    ancestorType)
2133 {
2134   TopoDS_Shape commonAnc;
2135   if ( !shape1.IsNull() && !shape2.IsNull() )
2136   {
2137     PShapeIteratorPtr ancIt = GetAncestors( shape1, mesh, ancestorType );
2138     while ( const TopoDS_Shape* anc = ancIt->next() )
2139       if ( IsSubShape( shape2, *anc ))
2140       {
2141         commonAnc = *anc;
2142         break;
2143       }
2144   }
2145   return commonAnc;
2146 }
2147
2148 //#include <Perf_Meter.hxx>
2149
2150 //=======================================================================
2151 namespace { // Structures used by FixQuadraticElements()
2152 //=======================================================================
2153
2154 #define __DMP__(txt) \
2155   //cout << txt
2156 #define MSG(txt) __DMP__(txt<<endl)
2157 #define MSGBEG(txt) __DMP__(txt)
2158
2159   //const double straightTol2 = 1e-33; // to detect straing links
2160   bool isStraightLink(double linkLen2, double middleNodeMove2)
2161   {
2162     // straight if <node move> < 1/15 * <link length>
2163     return middleNodeMove2 < 1/15./15. * linkLen2;
2164   }
2165
2166   struct QFace;
2167   // ---------------------------------------
2168   /*!
2169    * \brief Quadratic link knowing its faces
2170    */
2171   struct QLink: public SMESH_TLink
2172   {
2173     const SMDS_MeshNode*          _mediumNode;
2174     mutable vector<const QFace* > _faces;
2175     mutable gp_Vec                _nodeMove;
2176     mutable int                   _nbMoves;
2177
2178     QLink(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshNode* nm):
2179       SMESH_TLink( n1,n2 ), _mediumNode(nm), _nodeMove(0,0,0), _nbMoves(0) {
2180       _faces.reserve(4);
2181       //if ( MediumPos() != SMDS_TOP_3DSPACE )
2182         _nodeMove = MediumPnt() - MiddlePnt();
2183     }
2184     void SetContinuesFaces() const;
2185     const QFace* GetContinuesFace( const QFace* face ) const;
2186     bool OnBoundary() const;
2187     gp_XYZ MiddlePnt() const { return ( XYZ( node1() ) + XYZ( node2() )) / 2.; }
2188     gp_XYZ MediumPnt() const { return XYZ( _mediumNode ); }
2189
2190     SMDS_TypeOfPosition MediumPos() const
2191     { return _mediumNode->GetPosition()->GetTypeOfPosition(); }
2192     SMDS_TypeOfPosition EndPos(bool isSecond) const
2193     { return (isSecond ? node2() : node1())->GetPosition()->GetTypeOfPosition(); }
2194     const SMDS_MeshNode* EndPosNode(SMDS_TypeOfPosition pos) const
2195     { return EndPos(0) == pos ? node1() : EndPos(1) == pos ? node2() : 0; }
2196
2197     void Move(const gp_Vec& move, bool sum=false) const
2198     { _nodeMove += move; _nbMoves += sum ? (_nbMoves==0) : 1; }
2199     gp_XYZ Move() const { return _nodeMove.XYZ() / _nbMoves; }
2200     bool IsMoved() const { return (_nbMoves > 0 /*&& !IsStraight()*/); }
2201     bool IsStraight() const
2202     { return isStraightLink( (XYZ(node1())-XYZ(node2())).SquareModulus(),
2203                              _nodeMove.SquareMagnitude());
2204     }
2205     bool operator<(const QLink& other) const {
2206       return (node1()->GetID() == other.node1()->GetID() ?
2207               node2()->GetID() < other.node2()->GetID() :
2208               node1()->GetID() < other.node1()->GetID());
2209     }
2210 //     struct PtrComparator {
2211 //       bool operator() (const QLink* l1, const QLink* l2 ) const { return *l1 < *l2; }
2212 //     };
2213   };
2214   // ---------------------------------------------------------
2215   /*!
2216    * \brief Link in the chain of links; it connects two faces
2217    */
2218   struct TChainLink
2219   {
2220     const QLink*         _qlink;
2221     mutable const QFace* _qfaces[2];
2222
2223     TChainLink(const QLink* qlink=0):_qlink(qlink) {
2224       _qfaces[0] = _qfaces[1] = 0;
2225     }
2226     void SetFace(const QFace* face) const { int iF = _qfaces[0] ? 1 : 0; _qfaces[iF]=face; }
2227
2228     bool IsBoundary() const { return !_qfaces[1]; }
2229
2230     void RemoveFace( const QFace* face ) const
2231     { _qfaces[(face == _qfaces[1])] = 0; if (!_qfaces[0]) std::swap(_qfaces[0],_qfaces[1]); }
2232
2233     const QFace* NextFace( const QFace* f ) const
2234     { return _qfaces[0]==f ? _qfaces[1] : _qfaces[0]; }
2235
2236     const SMDS_MeshNode* NextNode( const SMDS_MeshNode* n ) const
2237     { return n == _qlink->node1() ? _qlink->node2() : _qlink->node1(); }
2238
2239     bool operator<(const TChainLink& other) const { return *_qlink < *other._qlink; }
2240
2241     operator bool() const { return (_qlink); }
2242
2243     const QLink* operator->() const { return _qlink; }
2244
2245     gp_Vec Normal() const;
2246
2247     bool IsStraight() const;
2248   };
2249   // --------------------------------------------------------------------
2250   typedef list< TChainLink > TChain;
2251   typedef set < TChainLink > TLinkSet;
2252   typedef TLinkSet::const_iterator TLinkInSet;
2253
2254   const int theFirstStep = 5;
2255
2256   enum { ERR_OK, ERR_TRI, ERR_PRISM, ERR_UNKNOWN }; // errors of QFace::GetLinkChain()
2257   // --------------------------------------------------------------------
2258   /*!
2259    * \brief Face shared by two volumes and bound by QLinks
2260    */
2261   struct QFace: public TIDSortedNodeSet
2262   {
2263     mutable const SMDS_MeshElement* _volumes[2];
2264     mutable vector< const QLink* >  _sides;
2265     mutable bool                    _sideIsAdded[4]; // added in chain of links
2266     gp_Vec                          _normal;
2267 #ifdef _DEBUG_
2268     mutable const SMDS_MeshElement* _face;
2269 #endif
2270
2271     QFace( const vector< const QLink*>& links, const SMDS_MeshElement* face=0 );
2272
2273     void SetVolume(const SMDS_MeshElement* v) const { _volumes[ _volumes[0] ? 1 : 0 ] = v; }
2274
2275     int NbVolumes() const { return !_volumes[0] ? 0 : !_volumes[1] ? 1 : 2; }
2276
2277     void AddSelfToLinks() const {
2278       for ( int i = 0; i < _sides.size(); ++i )
2279         _sides[i]->_faces.push_back( this );
2280     }
2281     int LinkIndex( const QLink* side ) const {
2282       for (int i=0; i<_sides.size(); ++i ) if ( _sides[i] == side ) return i;
2283       return -1;
2284     }
2285     bool GetLinkChain( int iSide, TChain& chain, SMDS_TypeOfPosition pos, int& err) const;
2286
2287     bool GetLinkChain( TChainLink& link, TChain& chain, SMDS_TypeOfPosition pos, int& err) const
2288     {
2289       int i = LinkIndex( link._qlink );
2290       if ( i < 0 ) return true;
2291       _sideIsAdded[i] = true;
2292       link.SetFace( this );
2293       // continue from opposite link
2294       return GetLinkChain( (i+2)%_sides.size(), chain, pos, err );
2295     }
2296     bool IsBoundary() const { return !_volumes[1]; }
2297
2298     bool Contains( const SMDS_MeshNode* node ) const { return count(node); }
2299
2300     bool IsSpoiled(const QLink* bentLink ) const;
2301
2302     TLinkInSet GetBoundaryLink( const TLinkSet&      links,
2303                                 const TChainLink&    avoidLink,
2304                                 TLinkInSet *         notBoundaryLink = 0,
2305                                 const SMDS_MeshNode* nodeToContain = 0,
2306                                 bool *               isAdjacentUsed = 0,
2307                                 int                  nbRecursionsLeft = -1) const;
2308
2309     TLinkInSet GetLinkByNode( const TLinkSet&      links,
2310                               const TChainLink&    avoidLink,
2311                               const SMDS_MeshNode* nodeToContain) const;
2312
2313     const SMDS_MeshNode* GetNodeInFace() const {
2314       for ( int iL = 0; iL < _sides.size(); ++iL )
2315         if ( _sides[iL]->MediumPos() == SMDS_TOP_FACE ) return _sides[iL]->_mediumNode;
2316       return 0;
2317     }
2318
2319     gp_Vec LinkNorm(const int i, SMESH_MesherHelper* theFaceHelper=0) const;
2320
2321     double MoveByBoundary( const TChainLink&   theLink,
2322                            const gp_Vec&       theRefVec,
2323                            const TLinkSet&     theLinks,
2324                            SMESH_MesherHelper* theFaceHelper=0,
2325                            const double        thePrevLen=0,
2326                            const int           theStep=theFirstStep,
2327                            gp_Vec*             theLinkNorm=0,
2328                            double              theSign=1.0) const;
2329   };
2330
2331   //================================================================================
2332   /*!
2333    * \brief Dump QLink and QFace
2334    */
2335   ostream& operator << (ostream& out, const QLink& l)
2336   {
2337     out <<"QLink nodes: "
2338         << l.node1()->GetID() << " - "
2339         << l._mediumNode->GetID() << " - "
2340         << l.node2()->GetID() << endl;
2341     return out;
2342   }
2343   ostream& operator << (ostream& out, const QFace& f)
2344   {
2345     out <<"QFace nodes: "/*<< &f << "  "*/;
2346     for ( TIDSortedNodeSet::const_iterator n = f.begin(); n != f.end(); ++n )
2347       out << (*n)->GetID() << " ";
2348     out << " \tvolumes: "
2349         << (f._volumes[0] ? f._volumes[0]->GetID() : 0) << " "
2350         << (f._volumes[1] ? f._volumes[1]->GetID() : 0);
2351     out << "  \tNormal: "<< f._normal.X() <<", "<<f._normal.Y() <<", "<<f._normal.Z() << endl;
2352     return out;
2353   }
2354
2355   //================================================================================
2356   /*!
2357    * \brief Construct QFace from QLinks 
2358    */
2359   //================================================================================
2360
2361   QFace::QFace( const vector< const QLink*>& links, const SMDS_MeshElement* face )
2362   {
2363     _volumes[0] = _volumes[1] = 0;
2364     _sides = links;
2365     _sideIsAdded[0]=_sideIsAdded[1]=_sideIsAdded[2]=_sideIsAdded[3]=false;
2366     _normal.SetCoord(0,0,0);
2367     for ( int i = 1; i < _sides.size(); ++i ) {
2368       const QLink *l1 = _sides[i-1], *l2 = _sides[i];
2369       insert( l1->node1() ); insert( l1->node2() );
2370       // compute normal
2371       gp_Vec v1( XYZ( l1->node2()), XYZ( l1->node1()));
2372       gp_Vec v2( XYZ( l2->node1()), XYZ( l2->node2()));
2373       if ( l1->node1() != l2->node1() && l1->node2() != l2->node2() )
2374         v1.Reverse(); 
2375       _normal += v1 ^ v2;
2376     }
2377     double normSqSize = _normal.SquareMagnitude();
2378     if ( normSqSize > numeric_limits<double>::min() )
2379       _normal /= sqrt( normSqSize );
2380     else
2381       _normal.SetCoord(1e-33,0,0);
2382
2383 #ifdef _DEBUG_
2384     _face = face;
2385 #endif
2386   }
2387   //================================================================================
2388   /*!
2389    * \brief Make up a chain of links
2390    *  \param iSide - link to add first
2391    *  \param chain - chain to fill in
2392    *  \param pos   - postion of medium nodes the links should have
2393    *  \param error - out, specifies what is wrong
2394    *  \retval bool - false if valid chain can't be built; "valid" means that links
2395    *                 of the chain belongs to rectangles bounding hexahedrons
2396    */
2397   //================================================================================
2398
2399   bool QFace::GetLinkChain( int iSide, TChain& chain, SMDS_TypeOfPosition pos, int& error) const
2400   {
2401     if ( iSide >= _sides.size() ) // wrong argument iSide
2402       return false;
2403     if ( _sideIsAdded[ iSide ]) // already in chain
2404       return true;
2405
2406     if ( _sides.size() != 4 ) { // triangle - visit all my continous faces
2407       MSGBEG( *this );
2408       TLinkSet links;
2409       list< const QFace* > faces( 1, this );
2410       while ( !faces.empty() ) {
2411         const QFace* face = faces.front();
2412         for ( int i = 0; i < face->_sides.size(); ++i ) {
2413           if ( !face->_sideIsAdded[i] && face->_sides[i] ) {
2414             face->_sideIsAdded[i] = true;
2415             // find a face side in the chain
2416             TLinkInSet chLink = links.insert( TChainLink(face->_sides[i])).first;
2417 //             TChain::iterator chLink = chain.begin();
2418 //             for ( ; chLink != chain.end(); ++chLink )
2419 //               if ( chLink->_qlink == face->_sides[i] )
2420 //                 break;
2421 //             if ( chLink == chain.end() )
2422 //               chLink = chain.insert( chain.begin(), TChainLink(face->_sides[i]));
2423             // add a face to a chained link and put a continues face in the queue
2424             chLink->SetFace( face );
2425             if ( face->_sides[i]->MediumPos() == pos )
2426               if ( const QFace* contFace = face->_sides[i]->GetContinuesFace( face ))
2427                 if ( contFace->_sides.size() == 3 )
2428                   faces.push_back( contFace );
2429           }
2430         }
2431         faces.pop_front();
2432       }
2433       if ( error < ERR_TRI )
2434         error = ERR_TRI;
2435       chain.insert( chain.end(), links.begin(),links.end() );
2436       return false;
2437     }
2438     _sideIsAdded[iSide] = true; // not to add this link to chain again
2439     const QLink* link = _sides[iSide];
2440     if ( !link)
2441       return true;
2442
2443     // add link into chain
2444     TChain::iterator chLink = chain.insert( chain.begin(), TChainLink(link));
2445     chLink->SetFace( this );
2446     MSGBEG( *this );
2447
2448     // propagate from quadrangle to neighbour faces
2449     if ( link->MediumPos() >= pos ) {
2450       int nbLinkFaces = link->_faces.size();
2451       if ( nbLinkFaces == 4 || (/*nbLinkFaces < 4 && */link->OnBoundary())) {
2452         // hexahedral mesh or boundary quadrangles - goto a continous face
2453         if ( const QFace* f = link->GetContinuesFace( this ))
2454           if ( f->_sides.size() == 4 )
2455             return f->GetLinkChain( *chLink, chain, pos, error );
2456       }
2457       else {
2458         TChainLink chLink(link); // side face of prismatic mesh - visit all faces of iSide
2459         for ( int i = 0; i < nbLinkFaces; ++i )
2460           if ( link->_faces[i] )
2461             link->_faces[i]->GetLinkChain( chLink, chain, pos, error );
2462         if ( error < ERR_PRISM )
2463           error = ERR_PRISM;
2464         return false;
2465       }
2466     }
2467     return true;
2468   }
2469
2470   //================================================================================
2471   /*!
2472    * \brief Return a boundary link of the triangle face
2473    *  \param links - set of all links
2474    *  \param avoidLink - link not to return
2475    *  \param notBoundaryLink - out, neither the returned link nor avoidLink
2476    *  \param nodeToContain - node the returned link must contain; if provided, search
2477    *                         also performed on adjacent faces
2478    *  \param isAdjacentUsed - returns true if link is found in adjacent faces
2479    *  \param nbRecursionsLeft - to limit recursion
2480    */
2481   //================================================================================
2482
2483   TLinkInSet QFace::GetBoundaryLink( const TLinkSet&      links,
2484                                      const TChainLink&    avoidLink,
2485                                      TLinkInSet *         notBoundaryLink,
2486                                      const SMDS_MeshNode* nodeToContain,
2487                                      bool *               isAdjacentUsed,
2488                                      int                  nbRecursionsLeft) const
2489   {
2490     TLinkInSet linksEnd = links.end(), boundaryLink = linksEnd;
2491
2492     typedef list< pair< const QFace*, TLinkInSet > > TFaceLinkList;
2493     TFaceLinkList adjacentFaces;
2494
2495     for ( int iL = 0; iL < _sides.size(); ++iL )
2496     {
2497       if ( avoidLink._qlink == _sides[iL] )
2498         continue;
2499       TLinkInSet link = links.find( _sides[iL] );
2500       if ( link == linksEnd ) continue;
2501       if ( (*link)->MediumPos() > SMDS_TOP_FACE )
2502         continue; // We work on faces here, don't go inside a solid
2503
2504       // check link
2505       if ( link->IsBoundary() ) {
2506         if ( !nodeToContain ||
2507              (*link)->node1() == nodeToContain ||
2508              (*link)->node2() == nodeToContain )
2509         {
2510           boundaryLink = link;
2511           if ( !notBoundaryLink ) break;
2512         }
2513       }
2514       else if ( notBoundaryLink ) {
2515         *notBoundaryLink = link;
2516         if ( boundaryLink != linksEnd ) break;
2517       }
2518
2519       if ( boundaryLink == linksEnd && nodeToContain ) // collect adjacent faces
2520         if ( const QFace* adj = link->NextFace( this ))
2521           if ( adj->Contains( nodeToContain ))
2522             adjacentFaces.push_back( make_pair( adj, link ));
2523     }
2524
2525     if ( isAdjacentUsed ) *isAdjacentUsed = false;
2526     if ( boundaryLink == linksEnd && nodeToContain && nbRecursionsLeft) // check adjacent faces
2527     {
2528       if ( nbRecursionsLeft < 0 )
2529         nbRecursionsLeft = nodeToContain->NbInverseElements();
2530       TFaceLinkList::iterator adj = adjacentFaces.begin();
2531       for ( ; boundaryLink == linksEnd && adj != adjacentFaces.end(); ++adj )
2532         boundaryLink = adj->first->GetBoundaryLink( links, *(adj->second), 0, nodeToContain,
2533                                                     isAdjacentUsed, nbRecursionsLeft-1);
2534       if ( isAdjacentUsed ) *isAdjacentUsed = true;
2535     }
2536     return boundaryLink;
2537   }
2538   //================================================================================
2539   /*!
2540    * \brief Return a link ending at the given node but not avoidLink
2541    */
2542   //================================================================================
2543
2544   TLinkInSet QFace::GetLinkByNode( const TLinkSet&      links,
2545                                    const TChainLink&    avoidLink,
2546                                    const SMDS_MeshNode* nodeToContain) const
2547   {
2548     for ( int i = 0; i < _sides.size(); ++i )
2549       if ( avoidLink._qlink != _sides[i] &&
2550            (_sides[i]->node1() == nodeToContain || _sides[i]->node2() == nodeToContain ))
2551         return links.find( _sides[ i ]);
2552     return links.end();
2553   }
2554
2555   //================================================================================
2556   /*!
2557    * \brief Return normal to the i-th side pointing outside the face
2558    */
2559   //================================================================================
2560
2561   gp_Vec QFace::LinkNorm(const int i, SMESH_MesherHelper* /*uvHelper*/) const
2562   {
2563     gp_Vec norm, vecOut;
2564 //     if ( uvHelper ) {
2565 //       TopoDS_Face face = TopoDS::Face( uvHelper->GetSubShape());
2566 //       const SMDS_MeshNode* inFaceNode = uvHelper->GetNodeUVneedInFaceNode() ? GetNodeInFace() : 0;
2567 //       gp_XY uv1 = uvHelper->GetNodeUV( face, _sides[i]->node1(), inFaceNode );
2568 //       gp_XY uv2 = uvHelper->GetNodeUV( face, _sides[i]->node2(), inFaceNode );
2569 //       norm.SetCoord( uv1.Y() - uv2.Y(), uv2.X() - uv1.X(), 0 );
2570
2571 //       const QLink* otherLink = _sides[(i + 1) % _sides.size()];
2572 //       const SMDS_MeshNode* otherNode =
2573 //         otherLink->node1() == _sides[i]->node1() ? otherLink->node2() : otherLink->node1();
2574 //       gp_XY pIn = uvHelper->GetNodeUV( face, otherNode, inFaceNode );
2575 //       vecOut.SetCoord( uv1.X() - pIn.X(), uv1.Y() - pIn.Y(), 0 );
2576 //     }
2577 //     else {
2578       norm = _normal ^ gp_Vec( XYZ(_sides[i]->node1()), XYZ(_sides[i]->node2()));
2579       gp_XYZ pIn = ( XYZ( _sides[0]->node1() ) +
2580                      XYZ( _sides[0]->node2() ) +
2581                      XYZ( _sides[1]->node1() )) / 3.;
2582       vecOut.SetXYZ( _sides[i]->MiddlePnt() - pIn );
2583       //}
2584     if ( norm * vecOut < 0 )
2585       norm.Reverse();
2586     double mag2 = norm.SquareMagnitude();
2587     if ( mag2 > numeric_limits<double>::min() )
2588       norm /= sqrt( mag2 );
2589     return norm;
2590   }
2591   //================================================================================
2592   /*!
2593    * \brief Move medium node of theLink according to its distance from boundary
2594    *  \param theLink - link to fix
2595    *  \param theRefVec - movement of boundary
2596    *  \param theLinks - all adjacent links of continous triangles
2597    *  \param theFaceHelper - helper is not used so far
2598    *  \param thePrevLen - distance from the boundary
2599    *  \param theStep - number of steps till movement propagation limit
2600    *  \param theLinkNorm - out normal to theLink
2601    *  \param theSign - 1 or -1 depending on movement of boundary
2602    *  \retval double - distance from boundary to propagation limit or other boundary
2603    */
2604   //================================================================================
2605
2606   double QFace::MoveByBoundary( const TChainLink&   theLink,
2607                                 const gp_Vec&       theRefVec,
2608                                 const TLinkSet&     theLinks,
2609                                 SMESH_MesherHelper* theFaceHelper,
2610                                 const double        thePrevLen,
2611                                 const int           theStep,
2612                                 gp_Vec*             theLinkNorm,
2613                                 double              theSign) const
2614   {
2615     if ( !theStep )
2616       return thePrevLen; // propagation limit reached
2617
2618     int iL; // index of theLink
2619     for ( iL = 0; iL < _sides.size(); ++iL )
2620       if ( theLink._qlink == _sides[ iL ])
2621         break;
2622
2623     MSG(string(theStep,'.')<<" Ref( "<<theRefVec.X()<<","<<theRefVec.Y()<<","<<theRefVec.Z()<<" )"
2624         <<" thePrevLen " << thePrevLen);
2625     MSG(string(theStep,'.')<<" "<<*theLink._qlink);
2626
2627     gp_Vec linkNorm = -LinkNorm( iL/*, theFaceHelper*/ ); // normal to theLink
2628     double refProj = theRefVec * linkNorm; // project movement vector to normal of theLink
2629     if ( theStep == theFirstStep )
2630       theSign = refProj < 0. ? -1. : 1.;
2631     else if ( theSign * refProj < 0.4 * theRefVec.Magnitude())
2632       return thePrevLen; // to propagate movement forward only, not in side dir or backward
2633
2634     int iL1 = (iL + 1) % 3, iL2 = (iL + 2) % 3; // indices of the two other links of triangle
2635     TLinkInSet link1 = theLinks.find( _sides[iL1] );
2636     TLinkInSet link2 = theLinks.find( _sides[iL2] );
2637     if ( link1 == theLinks.end() || link2 == theLinks.end() )
2638       return thePrevLen;
2639     const QFace* f1 = link1->NextFace( this ); // adjacent faces
2640     const QFace* f2 = link2->NextFace( this );
2641
2642     // propagate to adjacent faces till limit step or boundary
2643     double len1 = thePrevLen + (theLink->MiddlePnt() - _sides[iL1]->MiddlePnt()).Modulus();
2644     double len2 = thePrevLen + (theLink->MiddlePnt() - _sides[iL2]->MiddlePnt()).Modulus();
2645     gp_Vec linkDir1(0,0,0); // initialize to avoid valgrind error ("Conditional jump...")
2646     gp_Vec linkDir2(0,0,0);
2647     try {
2648       OCC_CATCH_SIGNALS;
2649       if ( f1 && theLink->MediumPos() <= (*link1)->MediumPos() )
2650         len1 = f1->MoveByBoundary
2651           ( *link1, theRefVec, theLinks, theFaceHelper, len1, theStep-1, &linkDir1, theSign);
2652       else
2653         linkDir1 = LinkNorm( iL1/*, theFaceHelper*/ );
2654     } catch (...) {
2655       MSG( " --------------- EXCEPTION");
2656       return thePrevLen;
2657     }
2658     try {
2659       OCC_CATCH_SIGNALS;
2660       if ( f2 && theLink->MediumPos() <= (*link2)->MediumPos() )
2661         len2 = f2->MoveByBoundary
2662           ( *link2, theRefVec, theLinks, theFaceHelper, len2, theStep-1, &linkDir2, theSign);
2663       else
2664         linkDir2 = LinkNorm( iL2/*, theFaceHelper*/ );
2665     } catch (...) {
2666       MSG( " --------------- EXCEPTION");
2667       return thePrevLen;
2668     }
2669
2670     double fullLen = 0;
2671     if ( theStep != theFirstStep )
2672     {
2673       // choose chain length by direction of propagation most codirected with theRefVec
2674       bool choose1 = ( theRefVec * linkDir1 * theSign > theRefVec * linkDir2 * theSign );
2675       fullLen = choose1 ? len1 : len2;
2676       double r = thePrevLen / fullLen;
2677
2678       gp_Vec move = linkNorm * refProj * ( 1 - r );
2679       theLink->Move( move, true );
2680
2681       MSG(string(theStep,'.')<<" Move "<< theLink->_mediumNode->GetID()<<
2682           " by " << refProj * ( 1 - r ) << " following " <<
2683           (choose1 ? *link1->_qlink : *link2->_qlink));
2684
2685       if ( theLinkNorm ) *theLinkNorm = linkNorm;
2686     }
2687     return fullLen;
2688   }
2689
2690   //================================================================================
2691   /*!
2692    * \brief Checks if the face is distorted due to bentLink
2693    */
2694   //================================================================================
2695
2696   bool QFace::IsSpoiled(const QLink* bentLink ) const
2697   {
2698     // code is valid for convex faces only
2699     gp_XYZ gc(0,0,0);
2700     for ( TIDSortedNodeSet::const_iterator n = begin(); n!=end(); ++n)
2701       gc += XYZ( *n ) / size();
2702     for (unsigned i = 0; i < _sides.size(); ++i )
2703     {
2704       if ( _sides[i] == bentLink ) continue;
2705       gp_Vec linkNorm = _normal ^ gp_Vec( XYZ(_sides[i]->node1()), XYZ(_sides[i]->node2()));
2706       gp_Vec vecOut( gc, _sides[i]->MiddlePnt() );
2707       if ( linkNorm * vecOut < 0 )
2708         linkNorm.Reverse();
2709       double mag2 = linkNorm.SquareMagnitude();
2710       if ( mag2 > numeric_limits<double>::min() )
2711         linkNorm /= sqrt( mag2 );
2712       gp_Vec vecBent    ( _sides[i]->MiddlePnt(), bentLink->MediumPnt());
2713       gp_Vec vecStraight( _sides[i]->MiddlePnt(), bentLink->MiddlePnt());
2714       if ( vecBent * linkNorm > -0.1*vecStraight.Magnitude() )
2715         return true;
2716     }
2717     return false;
2718     
2719   }
2720
2721   //================================================================================
2722   /*!
2723    * \brief Find pairs of continues faces 
2724    */
2725   //================================================================================
2726
2727   void QLink::SetContinuesFaces() const
2728   {
2729     //       x0         x - QLink, [-|] - QFace, v - volume
2730     //   v0  |   v1   
2731     //       |          Between _faces of link x2 two vertical faces are continues
2732     // x1----x2-----x3  and two horizontal faces are continues. We set vertical faces
2733     //       |          to _faces[0] and _faces[1] and horizontal faces to
2734     //   v2  |   v3     _faces[2] and _faces[3] (or vise versa).
2735     //       x4
2736
2737     if ( _faces.empty() )
2738       return;
2739     int iFaceCont = -1, nbBoundary = 0, iBoundary[2]={-1,-1};
2740     if ( _faces[0]->IsBoundary() )
2741       iBoundary[ nbBoundary++ ] = 0;
2742     for ( int iF = 1; iFaceCont < 0 && iF < _faces.size(); ++iF )
2743     {
2744       // look for a face bounding none of volumes bound by _faces[0]
2745       bool sameVol = false;
2746       int nbVol = _faces[iF]->NbVolumes();
2747       for ( int iV = 0; !sameVol && iV < nbVol; ++iV )
2748         sameVol = ( _faces[iF]->_volumes[iV] == _faces[0]->_volumes[0] ||
2749                     _faces[iF]->_volumes[iV] == _faces[0]->_volumes[1]);
2750       if ( !sameVol )
2751         iFaceCont = iF;
2752       if ( _faces[iF]->IsBoundary() )
2753         iBoundary[ nbBoundary++ ] = iF;
2754     }
2755     // Set continues faces: arrange _faces to have
2756     // _faces[0] continues to _faces[1]
2757     // _faces[2] continues to _faces[3]
2758     if ( nbBoundary == 2 ) // bnd faces are continues
2759     {
2760       if (( iBoundary[0] < 2 ) != ( iBoundary[1] < 2 ))
2761       {
2762         int iNear0 = iBoundary[0] < 2 ? 1-iBoundary[0] : 5-iBoundary[0];
2763         std::swap( _faces[ iBoundary[1] ], _faces[iNear0] );
2764       }
2765     }
2766     else if ( iFaceCont > 0 ) // continues faces found
2767     {
2768       if ( iFaceCont != 1 )
2769         std::swap( _faces[1], _faces[iFaceCont] );
2770     }
2771     else if ( _faces.size() > 1 ) // not found, set NULL by the first face
2772     {
2773       _faces.insert( ++_faces.begin(), 0 );
2774     }
2775   }
2776   //================================================================================
2777   /*!
2778    * \brief Return a face continues to the given one
2779    */
2780   //================================================================================
2781
2782   const QFace* QLink::GetContinuesFace( const QFace* face ) const
2783   {
2784     for ( int i = 0; i < _faces.size(); ++i ) {
2785       if ( _faces[i] == face ) {
2786         int iF = i < 2 ? 1-i : 5-i;
2787         return iF < _faces.size() ? _faces[iF] : 0;
2788       }
2789     }
2790     return 0;
2791   }
2792   //================================================================================
2793   /*!
2794    * \brief True if link is on mesh boundary
2795    */
2796   //================================================================================
2797
2798   bool QLink::OnBoundary() const
2799   {
2800     for ( int i = 0; i < _faces.size(); ++i )
2801       if (_faces[i] && _faces[i]->IsBoundary()) return true;
2802     return false;
2803   }
2804   //================================================================================
2805   /*!
2806    * \brief Return normal of link of the chain
2807    */
2808   //================================================================================
2809
2810   gp_Vec TChainLink::Normal() const {
2811     gp_Vec norm;
2812     if (_qfaces[0]) norm  = _qfaces[0]->_normal;
2813     if (_qfaces[1]) norm += _qfaces[1]->_normal;
2814     return norm;
2815   }
2816   //================================================================================
2817   /*!
2818    * \brief Test link curvature taking into account size of faces
2819    */
2820   //================================================================================
2821
2822   bool TChainLink::IsStraight() const
2823   {
2824     bool isStraight = _qlink->IsStraight();
2825     if ( isStraight && _qfaces[0] && !_qfaces[1] )
2826     {
2827       int i = _qfaces[0]->LinkIndex( _qlink );
2828       int iOpp = ( i + 2 ) % _qfaces[0]->_sides.size();
2829       gp_XYZ mid1 = _qlink->MiddlePnt();
2830       gp_XYZ mid2 = _qfaces[0]->_sides[ iOpp ]->MiddlePnt();
2831       double faceSize2 = (mid1-mid2).SquareModulus();
2832       isStraight = _qlink->_nodeMove.SquareMagnitude() < 1/10./10. * faceSize2;
2833     }
2834     return isStraight;
2835   }
2836   
2837   //================================================================================
2838   /*!
2839    * \brief Move medium nodes of vertical links of pentahedrons adjacent by side faces
2840    */
2841   //================================================================================
2842
2843   void fixPrism( TChain& allLinks )
2844   {
2845     // separate boundary links from internal ones
2846     typedef set<const QLink*/*, QLink::PtrComparator*/> QLinkSet;
2847     QLinkSet interLinks, bndLinks1, bndLink2;
2848
2849     bool isCurved = false;
2850     for ( TChain::iterator lnk = allLinks.begin(); lnk != allLinks.end(); ++lnk ) {
2851       if ( (*lnk)->OnBoundary() )
2852         bndLinks1.insert( lnk->_qlink );
2853       else
2854         interLinks.insert( lnk->_qlink );
2855       isCurved = isCurved || !lnk->IsStraight();
2856     }
2857     if ( !isCurved )
2858       return; // no need to move
2859
2860     QLinkSet *curBndLinks = &bndLinks1, *newBndLinks = &bndLink2;
2861
2862     while ( !interLinks.empty() && !curBndLinks->empty() )
2863     {
2864       // propagate movement from boundary links to connected internal links
2865       QLinkSet::iterator bnd = curBndLinks->begin(), bndEnd = curBndLinks->end();
2866       for ( ; bnd != bndEnd; ++bnd )
2867       {
2868         const QLink* bndLink = *bnd;
2869         for ( int i = 0; i < bndLink->_faces.size(); ++i ) // loop on faces of bndLink
2870         {
2871           const QFace* face = bndLink->_faces[i]; // quadrange lateral face of a prism
2872           if ( !face ) continue;
2873           // find and move internal link opposite to bndLink within the face
2874           int interInd = ( face->LinkIndex( bndLink ) + 2 ) % face->_sides.size();
2875           const QLink* interLink = face->_sides[ interInd ];
2876           QLinkSet::iterator pInterLink = interLinks.find( interLink );
2877           if ( pInterLink == interLinks.end() ) continue; // not internal link
2878           interLink->Move( bndLink->_nodeMove );
2879           // treated internal links become new boundary ones
2880           interLinks. erase( pInterLink );
2881           newBndLinks->insert( interLink );
2882         }
2883       }
2884       curBndLinks->clear();
2885       std::swap( curBndLinks, newBndLinks );
2886     }
2887   }
2888
2889   //================================================================================
2890   /*!
2891    * \brief Fix links of continues triangles near curved boundary
2892    */
2893   //================================================================================
2894
2895   void fixTriaNearBoundary( TChain & allLinks, SMESH_MesherHelper& /*helper*/)
2896   {
2897     if ( allLinks.empty() ) return;
2898
2899     TLinkSet linkSet( allLinks.begin(), allLinks.end());
2900     TLinkInSet linkIt = linkSet.begin(), linksEnd = linkSet.end();
2901
2902     for ( linkIt = linkSet.begin(); linkIt != linksEnd; ++linkIt)
2903     {
2904       if ( linkIt->IsBoundary() && !linkIt->IsStraight() && linkIt->_qfaces[0])
2905       {
2906         // move iff a boundary link is bent towards inside of a face (issue 0021084)
2907         const QFace* face = linkIt->_qfaces[0];
2908         gp_XYZ pIn = ( face->_sides[0]->MiddlePnt() +
2909                        face->_sides[1]->MiddlePnt() +
2910                        face->_sides[2]->MiddlePnt() ) / 3.;
2911         gp_XYZ insideDir( pIn - (*linkIt)->MiddlePnt());
2912         bool linkBentInside = ((*linkIt)->_nodeMove.Dot( insideDir ) > 0 );
2913         //if ( face->IsSpoiled( linkIt->_qlink ))
2914         if ( linkBentInside )
2915           face->MoveByBoundary( *linkIt, (*linkIt)->_nodeMove, linkSet );
2916       }
2917     }
2918   }
2919
2920   //================================================================================
2921   /*!
2922    * \brief Detect rectangular structure of links and build chains from them
2923    */
2924   //================================================================================
2925
2926   enum TSplitTriaResult {
2927     _OK, _NO_CORNERS, _FEW_ROWS, _MANY_ROWS, _NO_SIDELINK, _BAD_MIDQUAD, _NOT_RECT,
2928     _NO_MIDQUAD, _NO_UPTRIA, _BAD_SET_SIZE, _BAD_CORNER, _BAD_START, _NO_BOTLINK, _TWISTED_CHAIN };
2929
2930   TSplitTriaResult splitTrianglesIntoChains( TChain &            allLinks,
2931                                              vector< TChain> &   resultChains,
2932                                              SMDS_TypeOfPosition pos )
2933   {
2934     // put links in the set and evalute number of result chains by number of boundary links
2935     TLinkSet linkSet;
2936     int nbBndLinks = 0;
2937     for ( TChain::iterator lnk = allLinks.begin(); lnk != allLinks.end(); ++lnk ) {
2938       linkSet.insert( *lnk );
2939       nbBndLinks += lnk->IsBoundary();
2940     }
2941     resultChains.clear();
2942     resultChains.reserve( nbBndLinks / 2 );
2943
2944     TLinkInSet linkIt, linksEnd = linkSet.end();
2945
2946     // find a boundary link with corner node; corner node has position pos-2
2947     // i.e. SMDS_TOP_VERTEX for links on faces and SMDS_TOP_EDGE for
2948     // links in volume
2949     SMDS_TypeOfPosition cornerPos = SMDS_TypeOfPosition(pos-2);
2950     const SMDS_MeshNode* corner = 0;
2951     for ( linkIt = linkSet.begin(); linkIt != linksEnd; ++linkIt )
2952       if ( linkIt->IsBoundary() && (corner = (*linkIt)->EndPosNode(cornerPos)))
2953         break;
2954     if ( !corner)
2955       return _NO_CORNERS;
2956
2957     TLinkInSet           startLink = linkIt;
2958     const SMDS_MeshNode* startCorner = corner;
2959     vector< TChain* >    rowChains;
2960     int iCol = 0;
2961
2962     while ( startLink != linksEnd) // loop on columns
2963     {
2964       // We suppose we have a rectangular structure like shown here. We have found a
2965       //               corner of the rectangle (startCorner) and a boundary link sharing  
2966       //    |/  |/  |  the startCorner (startLink). We are going to loop on rows of the   
2967       //  --o---o---o  structure making several chains at once. One chain (columnChain)   
2968       //    |\  |  /|  starts at startLink and continues upward (we look at the structure 
2969       //  \ | \ | / |  from such point that startLink is on the bottom of the structure). 
2970       //   \|  \|/  |  While going upward we also fill horizontal chains (rowChains) we   
2971       //  --o---o---o  encounter.                                                         
2972       //   /|\  |\  |
2973       //  / | \ | \ |  startCorner
2974       //    |  \|  \|,'
2975       //  --o---o---o
2976       //          `.startLink
2977
2978       if ( resultChains.size() == nbBndLinks / 2 )
2979         return _NOT_RECT;
2980       resultChains.push_back( TChain() );
2981       TChain& columnChain = resultChains.back();
2982
2983       TLinkInSet botLink = startLink; // current horizontal link to go up from
2984       corner = startCorner; // current corner the botLink ends at
2985       int iRow = 0;
2986       while ( botLink != linksEnd ) // loop on rows
2987       {
2988         // add botLink to the columnChain
2989         columnChain.push_back( *botLink );
2990
2991         const QFace* botTria = botLink->_qfaces[0]; // bottom triangle bound by botLink
2992         if ( !botTria )
2993         { // the column ends
2994           if ( botLink == startLink )
2995             return _TWISTED_CHAIN; // issue 0020951
2996           linkSet.erase( botLink );
2997           if ( iRow != rowChains.size() )
2998             return _FEW_ROWS; // different nb of rows in columns
2999           break;
3000         }
3001         // find the link dividing the quadrangle (midQuadLink) and vertical boundary
3002         // link ending at <corner> (sideLink); there are two cases:
3003         // 1) midQuadLink does not end at <corner>, then we easily find it by botTria,
3004         //   since midQuadLink is not at boundary while sideLink is.
3005         // 2) midQuadLink ends at <corner>
3006         bool isCase2;
3007         TLinkInSet midQuadLink = linksEnd;
3008         TLinkInSet sideLink = botTria->GetBoundaryLink( linkSet, *botLink, &midQuadLink,
3009                                                         corner, &isCase2 );
3010         if ( isCase2 ) { // find midQuadLink among links of botTria
3011           midQuadLink = botTria->GetLinkByNode( linkSet, *botLink, corner );
3012           if ( midQuadLink->IsBoundary() )
3013             return _BAD_MIDQUAD;
3014         }
3015         if ( sideLink == linksEnd || midQuadLink == linksEnd || sideLink == midQuadLink )
3016           return sideLink == linksEnd ? _NO_SIDELINK : _NO_MIDQUAD;
3017
3018         // fill chains
3019         columnChain.push_back( *midQuadLink );
3020         if ( iRow >= rowChains.size() ) {
3021           if ( iCol > 0 )
3022             return _MANY_ROWS; // different nb of rows in columns
3023           if ( resultChains.size() == nbBndLinks / 2 )
3024             return _NOT_RECT;
3025           resultChains.push_back( TChain() );
3026           rowChains.push_back( & resultChains.back() );
3027         }
3028         rowChains[iRow]->push_back( *sideLink );
3029         rowChains[iRow]->push_back( *midQuadLink );
3030
3031         const QFace* upTria = midQuadLink->NextFace( botTria ); // upper tria of the rectangle
3032         if ( !upTria)
3033           return _NO_UPTRIA;
3034         if ( iRow == 0 ) {
3035           // prepare startCorner and startLink for the next column
3036           startCorner = startLink->NextNode( startCorner );
3037           if (isCase2)
3038             startLink = botTria->GetBoundaryLink( linkSet, *botLink, 0, startCorner );
3039           else
3040             startLink = upTria->GetBoundaryLink( linkSet, *midQuadLink, 0, startCorner );
3041           // check if no more columns remains
3042           if ( startLink != linksEnd ) {
3043             const SMDS_MeshNode* botNode = startLink->NextNode( startCorner );
3044             if ( (isCase2 ? botTria : upTria)->Contains( botNode ))
3045               startLink = linksEnd; // startLink bounds upTria or botTria
3046             else if ( startLink == botLink || startLink == midQuadLink || startLink == sideLink )
3047               return _BAD_START;
3048           }
3049         }
3050         // find bottom link and corner for the next row
3051         corner = sideLink->NextNode( corner );
3052         // next bottom link ends at the new corner
3053         linkSet.erase( botLink );
3054         botLink = upTria->GetLinkByNode( linkSet, (isCase2 ? *sideLink : *midQuadLink), corner );
3055         if ( botLink == linksEnd || botLink == midQuadLink || botLink == sideLink)
3056           return _NO_BOTLINK;
3057         if ( midQuadLink == startLink || sideLink == startLink )
3058           return _TWISTED_CHAIN; // issue 0020951
3059         linkSet.erase( midQuadLink );
3060         linkSet.erase( sideLink );
3061
3062         // make faces neighboring the found ones be boundary
3063         if ( startLink != linksEnd ) {
3064           const QFace* tria = isCase2 ? botTria : upTria;
3065           for ( int iL = 0; iL < 3; ++iL ) {
3066             linkIt = linkSet.find( tria->_sides[iL] );
3067             if ( linkIt != linksEnd )
3068               linkIt->RemoveFace( tria );
3069           }
3070         }
3071         if ( botLink->_qfaces[0] == upTria || botLink->_qfaces[1] == upTria )
3072           botLink->RemoveFace( upTria ); // make next botTria first in vector
3073
3074         iRow++;
3075       } // loop on rows
3076
3077       iCol++;
3078     }
3079     // In the linkSet, there must remain the last links of rowChains; add them
3080     if ( linkSet.size() != rowChains.size() )
3081       return _BAD_SET_SIZE;
3082     for ( int iRow = 0; iRow < rowChains.size(); ++iRow ) {
3083       // find the link (startLink) ending at startCorner
3084       corner = 0;
3085       for ( startLink = linkSet.begin(); startLink != linksEnd; ++startLink ) {
3086         if ( (*startLink)->node1() == startCorner ) {
3087           corner = (*startLink)->node2(); break;
3088         }
3089         else if ( (*startLink)->node2() == startCorner) {
3090           corner = (*startLink)->node1(); break;
3091         }
3092       }
3093       if ( startLink == linksEnd )
3094         return _BAD_CORNER;
3095       rowChains[ iRow ]->push_back( *startLink );
3096       linkSet.erase( startLink );
3097       startCorner = corner;
3098     }
3099
3100     return _OK;
3101   }
3102
3103   //================================================================================
3104   /*!
3105    * \brief Place medium nodes at the link middle for elements whose corner nodes
3106    *        are out of geometrical boundary to prevent distorting elements.
3107    *        Issue 0020982, note 0013990
3108    */
3109   //================================================================================
3110
3111   void force3DOutOfBoundary( SMESH_MesherHelper&    theHelper,
3112                              SMESH_ComputeErrorPtr& theError)
3113   {
3114     SMESHDS_Mesh* meshDS = theHelper.GetMeshDS();
3115     TopoDS_Shape  shape = theHelper.GetSubShape().Oriented( TopAbs_FORWARD );
3116     if ( shape.IsNull() ) return;
3117
3118     if ( !theError ) theError = SMESH_ComputeError::New();
3119
3120     gp_XYZ faceNorm;
3121
3122     if ( shape.ShapeType() == TopAbs_FACE ) // 2D
3123     {
3124       if ( theHelper.GetMesh()->NbTriangles( ORDER_QUADRATIC ) < 1 ) return;
3125
3126       SMESHDS_SubMesh* faceSM = meshDS->MeshElements( shape );
3127       if ( !faceSM ) return;
3128
3129       const TopoDS_Face&      face = TopoDS::Face( shape );
3130       Handle(Geom_Surface) surface = BRep_Tool::Surface( face );
3131
3132       TopExp_Explorer edgeIt( face, TopAbs_EDGE );
3133       for ( ; edgeIt.More(); edgeIt.Next() ) // loop on EDGEs of a FACE
3134       {
3135         // check if the EDGE needs checking
3136         const TopoDS_Edge& edge = TopoDS::Edge( edgeIt.Current() );
3137         if ( BRep_Tool::Degenerated( edge ) )
3138           continue;
3139         if ( theHelper.IsRealSeam( edge ) &&
3140              edge.Orientation() == TopAbs_REVERSED )
3141           continue;
3142
3143         SMESHDS_SubMesh* edgeSM = meshDS->MeshElements( edge );
3144         if ( !edgeSM ) continue;
3145
3146         double f,l;
3147         Handle(Geom2d_Curve) pcurve  = BRep_Tool::CurveOnSurface( edge, face, f, l );
3148         BRepAdaptor_Curve    curve3D( edge );
3149         switch ( curve3D.GetType() ) {
3150         case GeomAbs_Line: continue;
3151         case GeomAbs_Circle:
3152         case GeomAbs_Ellipse:
3153         case GeomAbs_Hyperbola:
3154         case GeomAbs_Parabola:
3155           try
3156           {
3157             gp_Vec D1, D2, Du1, Dv1; gp_Pnt p;
3158             curve3D.D2( 0.5 * ( f + l ), p, D1, D2 );
3159             gp_Pnt2d uv = pcurve->Value( 0.5 * ( f + l ) );
3160             surface->D1( uv.X(), uv.Y(), p, Du1, Dv1 );
3161             gp_Vec fNorm = Du1 ^ Dv1;
3162             if ( fNorm.IsParallel( D2, M_PI * 25./180. ))
3163               continue; // face is normal to the curve3D
3164
3165             gp_Vec curvNorm = fNorm ^ D1;
3166             if ( edge.Orientation() == TopAbs_REVERSED ) curvNorm.Reverse();
3167             if ( curvNorm * D2 > 0 )
3168               continue; // convex edge
3169           }
3170           catch ( Standard_Failure )
3171           {
3172             continue;
3173           }
3174         }
3175         // get nodes shared by faces that may be distorted
3176         SMDS_NodeIteratorPtr nodeIt;
3177         if ( edgeSM->NbNodes() > 0 ) {
3178           nodeIt = edgeSM->GetNodes();
3179         }
3180         else {
3181           SMESHDS_SubMesh* vertexSM = meshDS->MeshElements( theHelper.IthVertex( 0, edge ));
3182           if ( !vertexSM )
3183             vertexSM = meshDS->MeshElements( theHelper.IthVertex( 1, edge ));
3184           if ( !vertexSM ) continue;
3185           nodeIt = vertexSM->GetNodes();
3186         }
3187
3188         // find suspicious faces
3189         TIDSortedElemSet checkedFaces;
3190         vector< const SMDS_MeshNode* > nOnEdge( 2 );
3191         const SMDS_MeshNode*           nOnFace;
3192         while ( nodeIt->more() )
3193         {
3194           const SMDS_MeshNode* n      = nodeIt->next();
3195           SMDS_ElemIteratorPtr faceIt = n->GetInverseElementIterator( SMDSAbs_Face );
3196           while ( faceIt->more() )
3197           {
3198             const SMDS_MeshElement* f = faceIt->next();
3199             if ( !faceSM->Contains( f ) ||
3200                  f->NbNodes() != 6      || // check quadratic triangles only
3201                  !checkedFaces.insert( f ).second )
3202               continue;
3203
3204             // get nodes on EDGE and on FACE of a suspicious face
3205             nOnEdge.clear(); nOnFace = 0;
3206             SMDS_MeshElement::iterator triNode = f->begin_nodes();
3207             for ( int nbN = 0; nbN < 3; ++triNode, ++nbN )
3208             {
3209               n = *triNode;
3210               if ( n->GetPosition()->GetDim() == 2 )
3211                 nOnFace = n;
3212               else
3213                 nOnEdge.push_back( n );
3214             }
3215
3216             // check if nOnFace is inside the FACE
3217             if ( nOnFace && nOnEdge.size() == 2 )
3218             {
3219               theHelper.AddTLinks( static_cast< const SMDS_MeshFace* > ( f ));
3220               if ( !SMESH_Algo::FaceNormal( f, faceNorm, /*normalized=*/false ))
3221                 continue;
3222               gp_XYZ edgeDir  = SMESH_TNodeXYZ( nOnEdge[0] ) - SMESH_TNodeXYZ( nOnEdge[1] );
3223               gp_XYZ edgeNorm = faceNorm ^ edgeDir;
3224               n = theHelper.GetMediumNode( nOnEdge[0], nOnEdge[1], true );
3225               gp_XYZ pN0     = SMESH_TNodeXYZ( nOnEdge[0] );
3226               gp_XYZ pMedium = SMESH_TNodeXYZ( n );                   // on-edge node location
3227               gp_XYZ pFaceN  = SMESH_TNodeXYZ( nOnFace );             // on-face node location
3228               double hMedium = edgeNorm * gp_Vec( pN0, pMedium ).XYZ();
3229               double hFace   = edgeNorm * gp_Vec( pN0, pFaceN ).XYZ();
3230               if ( Abs( hMedium ) > Abs( hFace * 0.6 ))
3231               {
3232                 // nOnFace is out of FACE, move a medium on-edge node to the middle
3233                 gp_XYZ pMid3D = 0.5 * ( pN0 + SMESH_TNodeXYZ( nOnEdge[1] ));
3234                 meshDS->MoveNode( n, pMid3D.X(), pMid3D.Y(), pMid3D.Z() );
3235                 MSG( "move OUT of face " << n );
3236                 theError->myBadElements.push_back( f );
3237               }
3238             }
3239           }
3240         }
3241       }
3242       if ( !theError->myBadElements.empty() )
3243         theError->myName = EDITERR_NO_MEDIUM_ON_GEOM;
3244       return;
3245
3246     } // 2D ==============================================================================
3247
3248     if ( shape.ShapeType() == TopAbs_SOLID ) // 3D
3249     {
3250       if ( theHelper.GetMesh()->NbTetras  ( ORDER_QUADRATIC ) < 1 &&
3251            theHelper.GetMesh()->NbPyramids( ORDER_QUADRATIC ) < 1 ) return;
3252
3253       SMESHDS_SubMesh* solidSM = meshDS->MeshElements( shape );
3254       if ( !solidSM ) return;
3255
3256       // check if the SOLID is bound by concave FACEs
3257       vector< TopoDS_Face > concaveFaces;
3258       TopExp_Explorer faceIt( shape, TopAbs_FACE );
3259       for ( ; faceIt.More(); faceIt.Next() ) // loop on FACEs of a SOLID
3260       {
3261         const TopoDS_Face&  face = TopoDS::Face( faceIt.Current() );
3262         if ( !meshDS->MeshElements( face )) continue;
3263
3264         BRepAdaptor_Surface surface( face );
3265         switch ( surface.GetType() ) {
3266         case GeomAbs_Plane: continue;
3267         case GeomAbs_Cylinder:
3268         case GeomAbs_Cone:
3269         case GeomAbs_Sphere:
3270           try
3271           {
3272             double u = 0.5 * ( surface.FirstUParameter() + surface.LastUParameter() );
3273             double v = 0.5 * ( surface.FirstVParameter() + surface.LastVParameter() );
3274             gp_Vec Du1, Dv1, Du2, Dv2, Duv2; gp_Pnt p;
3275             surface.D2( u,v, p, Du1, Dv1, Du2, Dv2, Duv2 );
3276             gp_Vec fNorm = Du1 ^ Dv1;
3277             if ( face.Orientation() == TopAbs_REVERSED ) fNorm.Reverse();
3278             bool concaveU = ( fNorm * Du2 > 1e-100 );
3279             bool concaveV = ( fNorm * Dv2 > 1e-100 );
3280             if ( concaveU || concaveV )
3281               concaveFaces.push_back( face );
3282           }
3283           catch ( Standard_Failure )
3284           {
3285             concaveFaces.push_back( face );
3286           }
3287         }
3288       }
3289       if ( concaveFaces.empty() )
3290         return;
3291
3292       // fix 2D mesh on the SOLID
3293       for ( faceIt.ReInit(); faceIt.More(); faceIt.Next() ) // loop on FACEs of a SOLID
3294       {
3295         SMESH_MesherHelper faceHelper( *theHelper.GetMesh() );
3296         faceHelper.SetSubShape( faceIt.Current() );
3297         force3DOutOfBoundary( faceHelper, theError );
3298       }
3299
3300       // get an iterator over faces on concaveFaces
3301       vector< SMDS_ElemIteratorPtr > faceIterVec( concaveFaces.size() );
3302       for ( size_t i = 0; i < concaveFaces.size(); ++i )
3303         faceIterVec[i] = meshDS->MeshElements( concaveFaces[i] )->GetElements();
3304       typedef SMDS_IteratorOnIterators
3305         < const SMDS_MeshElement*, vector< SMDS_ElemIteratorPtr > > TIterOnIter;
3306       SMDS_ElemIteratorPtr faceIter( new TIterOnIter( faceIterVec ));
3307
3308       // a seacher to check if a volume is close to a concave face
3309       std::auto_ptr< SMESH_ElementSearcher > faceSearcher
3310         ( SMESH_MeshEditor( theHelper.GetMesh() ).GetElementSearcher( faceIter ));
3311
3312       // classifier
3313       //BRepClass3d_SolidClassifier solidClassifier( shape );
3314
3315       TIDSortedElemSet checkedVols, movedNodes;
3316       for ( faceIt.ReInit(); faceIt.More(); faceIt.Next() ) // loop on FACEs of a SOLID
3317       {
3318         const TopoDS_Shape& face = faceIt.Current();
3319         SMESHDS_SubMesh*  faceSM = meshDS->MeshElements( face );
3320         if ( !faceSM ) continue;
3321
3322         // get nodes shared by volumes (tet and pyra) on the FACE that may be distorted
3323         SMDS_NodeIteratorPtr nodeIt;
3324         if ( faceSM->NbNodes() > 0 ) {
3325           nodeIt = faceSM->GetNodes();
3326         }
3327         else {
3328           TopExp_Explorer vertex( face, TopAbs_VERTEX );
3329           SMESHDS_SubMesh* vertexSM = meshDS->MeshElements( vertex.Current() );
3330           if ( !vertexSM ) continue;
3331           nodeIt = vertexSM->GetNodes();
3332         }
3333
3334         // find suspicious volumes adjacent to the FACE
3335         vector< const SMDS_MeshNode* >    nOnFace( 4 );
3336         const SMDS_MeshNode*              nInSolid;
3337         //vector< const SMDS_MeshElement* > intersectedFaces;
3338         while ( nodeIt->more() )
3339         {
3340           const SMDS_MeshNode* n     = nodeIt->next();
3341           SMDS_ElemIteratorPtr volIt = n->GetInverseElementIterator( SMDSAbs_Volume );
3342           while ( volIt->more() )
3343           {
3344             const SMDS_MeshElement* vol = volIt->next();
3345             int nbN = vol->NbCornerNodes();
3346             if ( ( nbN != 4 && nbN != 5 )  ||
3347                  !solidSM->Contains( vol ) ||
3348                  !checkedVols.insert( vol ).second )
3349               continue;
3350
3351             // get nodes on FACE and in SOLID of a suspicious volume
3352             nOnFace.clear(); nInSolid = 0;
3353             SMDS_MeshElement::iterator volNode = vol->begin_nodes();
3354             for ( int nb = nbN; nb > 0; ++volNode, --nb )
3355             {
3356               n = *volNode;
3357               if ( n->GetPosition()->GetDim() == 3 )
3358                 nInSolid = n;
3359               else
3360                 nOnFace.push_back( n );
3361             }
3362             if ( !nInSolid || nOnFace.size() != nbN - 1 )
3363               continue;
3364
3365             // get size of the vol
3366             SMESH_TNodeXYZ pInSolid( nInSolid ), pOnFace0( nOnFace[0] );
3367             double volLength = pInSolid.SquareDistance( nOnFace[0] );
3368             for ( size_t i = 1; i < nOnFace.size(); ++i )
3369             {
3370               volLength = Max( volLength, pOnFace0.SquareDistance( nOnFace[i] ));
3371             }
3372
3373             // check if vol is close to concaveFaces
3374             const SMDS_MeshElement* closeFace =
3375               faceSearcher->FindClosestTo( pInSolid, SMDSAbs_Face );
3376             if ( !closeFace ||
3377                  pInSolid.SquareDistance( closeFace->GetNode(0) ) > 4 * volLength )
3378               continue;
3379
3380             // check if vol is distorted, i.e. a medium node is much closer
3381             // to nInSolid than the link middle
3382             bool isDistorted = false;
3383             SMDS_FaceOfNodes onFaceTria( nOnFace[0], nOnFace[1], nOnFace[2] );
3384             if ( !SMESH_Algo::FaceNormal( &onFaceTria, faceNorm, /*normalized=*/false ))
3385               continue;
3386             theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* > ( vol ));
3387             vector< pair< SMESH_TLink, const SMDS_MeshNode* > > links;
3388             for ( size_t i = 0; i < nOnFace.size(); ++i ) // loop on links between nOnFace
3389               for ( size_t j = i+1; j < nOnFace.size(); ++j )
3390               {
3391                 SMESH_TLink link( nOnFace[i], nOnFace[j] );
3392                 TLinkNodeMap::const_iterator linkIt =
3393                   theHelper.GetTLinkNodeMap().find( link );
3394                 if ( linkIt != theHelper.GetTLinkNodeMap().end() )
3395                 {
3396                   links.push_back( make_pair( linkIt->first, linkIt->second  ));
3397                   if ( !isDistorted ) {
3398                     // compare projections of nInSolid and nMedium to face normal
3399                     gp_Pnt pMedium = SMESH_TNodeXYZ( linkIt->second );
3400                     double hMedium = faceNorm * gp_Vec( pOnFace0, pMedium ).XYZ();
3401                     double hVol    = faceNorm * gp_Vec( pOnFace0, pInSolid ).XYZ();
3402                     isDistorted = ( Abs( hMedium ) > Abs( hVol * 0.5 ));
3403                   }
3404                 }
3405               }
3406             // move medium nodes to link middle
3407             if ( isDistorted )
3408             {
3409               for ( size_t i = 0; i < links.size(); ++i )
3410               {
3411                 const SMDS_MeshNode* nMedium = links[i].second;
3412                 if ( movedNodes.insert( nMedium ).second )
3413                 {
3414                   gp_Pnt pMid3D = 0.5 * ( SMESH_TNodeXYZ( links[i].first.node1() ) +
3415                                           SMESH_TNodeXYZ( links[i].first.node2() ));
3416                   meshDS->MoveNode( nMedium, pMid3D.X(), pMid3D.Y(), pMid3D.Z() );
3417                   MSG( "move OUT of solid " << nMedium );
3418                 }
3419               }
3420               theError->myBadElements.push_back( vol );
3421             }
3422           } // loop on volumes sharing a node on FACE
3423         } // loop on nodes on FACE
3424       }  // loop on FACEs of a SOLID
3425
3426       if ( !theError->myBadElements.empty() )
3427         theError->myName = EDITERR_NO_MEDIUM_ON_GEOM;
3428     } // 3D case
3429   }
3430
3431 } //namespace
3432
3433 //=======================================================================
3434 /*!
3435  * \brief Move medium nodes of faces and volumes to fix distorted elements
3436  * \param error - container of fixed distorted elements
3437  * \param volumeOnly - to fix nodes on faces or not, if the shape is solid
3438  * 
3439  * Issue 0020307: EDF 992 SMESH : Linea/Quadratic with Medium Node on Geometry
3440  */
3441 //=======================================================================
3442
3443 void SMESH_MesherHelper::FixQuadraticElements(SMESH_ComputeErrorPtr& compError,
3444                                               bool                   volumeOnly)
3445 {
3446   // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
3447   if ( getenv("NO_FixQuadraticElements") )
3448     return;
3449
3450   // 0. Apply algorithm to SOLIDs or FACEs
3451   // ----------------------------------------------
3452   if ( myShape.IsNull() ) {
3453     if ( !myMesh->HasShapeToMesh() ) return;
3454     SetSubShape( myMesh->GetShapeToMesh() );
3455
3456 #ifdef _DEBUG_
3457     int nbSolids = 0;
3458     TopTools_IndexedMapOfShape solids;
3459     TopExp::MapShapes(myShape,TopAbs_SOLID,solids);
3460     nbSolids = solids.Extent();
3461 #endif
3462     TopTools_MapOfShape faces; // faces not in solid or in not meshed solid
3463     for ( TopExp_Explorer f(myShape,TopAbs_FACE,TopAbs_SOLID); f.More(); f.Next() ) {
3464       faces.Add( f.Current() ); // not in solid
3465     }
3466     for ( TopExp_Explorer s(myShape,TopAbs_SOLID); s.More(); s.Next() ) {
3467       if ( myMesh->GetSubMesh( s.Current() )->IsEmpty() ) { // get faces of solid
3468         for ( TopExp_Explorer f( s.Current(), TopAbs_FACE); f.More(); f.Next() )
3469           faces.Add( f.Current() ); // in not meshed solid
3470       }
3471       else { // fix nodes in the solid and its faces
3472 #ifdef _DEBUG_
3473         MSG("FIX SOLID " << nbSolids-- << " #" << GetMeshDS()->ShapeToIndex(s.Current()));
3474 #endif
3475         SMESH_MesherHelper h(*myMesh);
3476         h.SetSubShape( s.Current() );
3477         h.ToFixNodeParameters(true);
3478         h.FixQuadraticElements( compError, false );
3479       }
3480     }
3481     // fix nodes on geom faces
3482 #ifdef _DEBUG_
3483     int nbfaces = faces.Extent(); /*avoid "unused varianbles": */ nbfaces++, nbfaces--; 
3484 #endif
3485     for ( TopTools_MapIteratorOfMapOfShape fIt( faces ); fIt.More(); fIt.Next() ) {
3486       MSG("FIX FACE " << nbfaces-- << " #" << GetMeshDS()->ShapeToIndex(fIt.Key()));
3487       SMESH_MesherHelper h(*myMesh);
3488       h.SetSubShape( fIt.Key() );
3489       h.ToFixNodeParameters(true);
3490       h.FixQuadraticElements( compError, true);
3491     }
3492     //perf_print_all_meters(1);
3493     if ( compError && compError->myName == EDITERR_NO_MEDIUM_ON_GEOM )
3494       compError->myComment = "during conversion to quadratic, "
3495         "some medium nodes were not placed on geometry to avoid distorting elements";
3496     return;
3497   }
3498
3499   // 1. Find out type of elements and get iterator on them
3500   // ---------------------------------------------------
3501
3502   SMDS_ElemIteratorPtr elemIt;
3503   SMDSAbs_ElementType elemType = SMDSAbs_All;
3504
3505   SMESH_subMesh* submesh = myMesh->GetSubMeshContaining( myShapeID );
3506   if ( !submesh )
3507     return;
3508   if ( SMESHDS_SubMesh* smDS = submesh->GetSubMeshDS() ) {
3509     elemIt = smDS->GetElements();
3510     if ( elemIt->more() ) {
3511       elemType = elemIt->next()->GetType();
3512       elemIt = smDS->GetElements();
3513     }
3514   }
3515   if ( !elemIt || !elemIt->more() || elemType < SMDSAbs_Face )
3516     return;
3517
3518   // 2. Fill in auxiliary data structures
3519   // ----------------------------------
3520
3521   set< QLink > links;
3522   set< QFace > faces;
3523   set< QLink >::iterator pLink;
3524   set< QFace >::iterator pFace;
3525
3526   bool isCurved = false;
3527   //bool hasRectFaces = false;
3528   //set<int> nbElemNodeSet;
3529   SMDS_VolumeTool volTool;
3530
3531   TIDSortedNodeSet apexOfPyramid;
3532   const int apexIndex = 4;
3533
3534   // Issue 0020982
3535   // Move medium nodes to the link middle for elements whose corner nodes
3536   // are out of geometrical boundary to fix distorted elements.
3537   force3DOutOfBoundary( *this, compError );
3538
3539   if ( elemType == SMDSAbs_Volume )
3540   {
3541     while ( elemIt->more() ) // loop on volumes
3542     {
3543       const SMDS_MeshElement* vol = elemIt->next();
3544       if ( !vol->IsQuadratic() || !volTool.Set( vol ))
3545         return;
3546       double volMinSize2 = -1.;
3547       for ( int iF = 0; iF < volTool.NbFaces(); ++iF ) // loop on faces of volume
3548       {
3549         int nbN = volTool.NbFaceNodes( iF );
3550         //nbElemNodeSet.insert( nbN );
3551         const SMDS_MeshNode** faceNodes = volTool.GetFaceNodes( iF );
3552         vector< const QLink* > faceLinks( nbN/2 );
3553         for ( int iN = 0; iN < nbN; iN += 2 ) // loop on links of a face
3554         {
3555           // store QLink
3556           QLink link( faceNodes[iN], faceNodes[iN+2], faceNodes[iN+1] );
3557           pLink = links.insert( link ).first;
3558           faceLinks[ iN/2 ] = & *pLink;
3559
3560           if ( link.MediumPos() == SMDS_TOP_3DSPACE )
3561           {
3562             if ( !link.IsStraight() )
3563               return; // already fixed
3564           }
3565           else if ( !isCurved )
3566           {
3567             if ( volMinSize2 < 0 ) volMinSize2 = volTool.MinLinearSize2();
3568             isCurved = !isStraightLink( volMinSize2, link._nodeMove.SquareMagnitude() );
3569           }
3570         }
3571         // store QFace
3572         pFace = faces.insert( QFace( faceLinks )).first;
3573         if ( pFace->NbVolumes() == 0 )
3574           pFace->AddSelfToLinks();
3575         pFace->SetVolume( vol );
3576 //         hasRectFaces = hasRectFaces ||
3577 //           ( volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_HEXA ||
3578 //             volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_PENTA );
3579 #ifdef _DEBUG_
3580         if ( nbN == 6 )
3581           pFace->_face = GetMeshDS()->FindFace(faceNodes[0],faceNodes[2],faceNodes[4]);
3582         else
3583           pFace->_face = GetMeshDS()->FindFace(faceNodes[0],faceNodes[2],
3584                                                faceNodes[4],faceNodes[6] );
3585 #endif
3586       }
3587       // collect pyramid apexes for further correction
3588       if ( vol->NbCornerNodes() == 5 )
3589         apexOfPyramid.insert( vol->GetNode( apexIndex ));
3590     }
3591     set< QLink >::iterator pLink = links.begin();
3592     for ( ; pLink != links.end(); ++pLink )
3593       pLink->SetContinuesFaces();
3594   }
3595   else
3596   {
3597     while ( elemIt->more() ) // loop on faces
3598     {
3599       const SMDS_MeshElement* face = elemIt->next();
3600       if ( !face->IsQuadratic() )
3601         continue;
3602       //nbElemNodeSet.insert( face->NbNodes() );
3603       int nbN = face->NbNodes()/2;
3604       vector< const QLink* > faceLinks( nbN );
3605       for ( int iN = 0; iN < nbN; ++iN ) // loop on links of a face
3606       {
3607         // store QLink
3608         QLink link( face->GetNode(iN), face->GetNode((iN+1)%nbN), face->GetNode(iN+nbN) );
3609         pLink = links.insert( link ).first;
3610         faceLinks[ iN ] = & *pLink;
3611         if ( !isCurved &&
3612              link.node1()->GetPosition()->GetTypeOfPosition() < 2 &&
3613              link.node2()->GetPosition()->GetTypeOfPosition() < 2 )
3614           isCurved = !link.IsStraight();
3615       }
3616       // store QFace
3617       pFace = faces.insert( QFace( faceLinks )).first;
3618       pFace->AddSelfToLinks();
3619       //hasRectFaces = ( hasRectFaces || nbN == 4 );
3620     }
3621   }
3622   if ( !isCurved )
3623     return; // no curved edges of faces
3624
3625   // 3. Compute displacement of medium nodes
3626   // ---------------------------------------
3627
3628   // two loops on QFaces: the first is to treat boundary links, the second is for internal ones
3629   TopLoc_Location loc;
3630   // not treat boundary of volumic submesh
3631   int isInside = ( elemType == SMDSAbs_Volume && volumeOnly ) ? 1 : 0;
3632   for ( ; isInside < 2; ++isInside ) {
3633     MSG( "--------------- LOOP (inside=" << isInside << ") ------------------");
3634     SMDS_TypeOfPosition pos = isInside ? SMDS_TOP_3DSPACE : SMDS_TOP_FACE;
3635     SMDS_TypeOfPosition bndPos = isInside ? SMDS_TOP_FACE : SMDS_TOP_EDGE;
3636
3637     for ( pFace = faces.begin(); pFace != faces.end(); ++pFace ) {
3638       if ( bool(isInside) == pFace->IsBoundary() )
3639         continue;
3640       for ( int dir = 0; dir < 2; ++dir ) // 2 directions of propagation from the quadrangle
3641       {
3642         MSG( "CHAIN");
3643         // make chain of links connected via continues faces
3644         int error = ERR_OK;
3645         TChain rawChain;
3646         if ( !pFace->GetLinkChain( dir, rawChain, pos, error) && error ==ERR_UNKNOWN ) continue;
3647         rawChain.reverse();
3648         if ( !pFace->GetLinkChain( dir+2, rawChain, pos, error ) && error ==ERR_UNKNOWN ) continue;
3649
3650         vector< TChain > chains;
3651         if ( error == ERR_OK ) { // chain contains continues rectangles
3652           chains.resize(1);
3653           chains[0].splice( chains[0].begin(), rawChain );
3654         }
3655         else if ( error == ERR_TRI ) {  // chain contains continues triangles
3656           TSplitTriaResult res = splitTrianglesIntoChains( rawChain, chains, pos );
3657           if ( res != _OK ) { // not quadrangles split into triangles
3658             fixTriaNearBoundary( rawChain, *this );
3659             break;
3660           }
3661         }
3662         else if ( error == ERR_PRISM ) { // quadrangle side faces of prisms
3663           fixPrism( rawChain );
3664           break;
3665         }
3666         else {
3667           continue;
3668         }
3669         for ( int iC = 0; iC < chains.size(); ++iC )
3670         {
3671           TChain& chain = chains[iC];
3672           if ( chain.empty() ) continue;
3673           if ( chain.front().IsStraight() && chain.back().IsStraight() ) {
3674             MSG("3D straight - ignore");
3675             continue;
3676           }
3677           if ( chain.front()->MediumPos() > bndPos ||
3678                chain.back() ->MediumPos() > bndPos ) {
3679             MSG("Internal chain - ignore");
3680             continue;
3681           }
3682           // mesure chain length and compute link position along the chain
3683           double chainLen = 0;
3684           vector< double > linkPos;
3685           MSGBEG( "Link medium nodes: ");
3686           TChain::iterator link0 = chain.begin(), link1 = chain.begin(), link2;
3687           for ( ++link1; link1 != chain.end(); ++link1, ++link0 ) {
3688             MSGBEG( (*link0)->_mediumNode->GetID() << "-" <<(*link1)->_mediumNode->GetID()<<" ");
3689             double len = ((*link0)->MiddlePnt() - (*link1)->MiddlePnt()).Modulus();
3690             while ( len < numeric_limits<double>::min() ) { // remove degenerated link
3691               link1 = chain.erase( link1 );
3692               if ( link1 == chain.end() )
3693                 break;
3694               len = ((*link0)->MiddlePnt() - (*link1)->MiddlePnt()).Modulus();
3695             }
3696             chainLen += len;
3697             linkPos.push_back( chainLen );
3698           }
3699           MSG("");
3700           if ( linkPos.size() < 2 )
3701             continue;
3702
3703           gp_Vec move0 = chain.front()->_nodeMove;
3704           gp_Vec move1 = chain.back ()->_nodeMove;
3705
3706           TopoDS_Face face;
3707           bool checkUV = true;
3708           if ( !isInside )
3709           {
3710             // compute node displacement of end links of chain in parametric space of face
3711             TChainLink& linkOnFace = *(++chain.begin());
3712             const SMDS_MeshNode* nodeOnFace = linkOnFace->_mediumNode;
3713             TopoDS_Shape f = GetSubShapeByNode( nodeOnFace, GetMeshDS() );
3714             if ( !f.IsNull() && f.ShapeType() == TopAbs_FACE )
3715             {
3716               face = TopoDS::Face( f );
3717               Handle(Geom_Surface) surf = BRep_Tool::Surface(face,loc);
3718               bool isStraight[2];
3719               for ( int is1 = 0; is1 < 2; ++is1 ) // move0 or move1
3720               {
3721                 TChainLink& link = is1 ? chain.back() : chain.front();
3722                 gp_XY uvm = GetNodeUV( face, link->_mediumNode, nodeOnFace, &checkUV);
3723                 gp_XY uv1 = GetNodeUV( face, link->node1(), nodeOnFace, &checkUV);
3724                 gp_XY uv2 = GetNodeUV( face, link->node2(), nodeOnFace, &checkUV);
3725                 gp_XY uv12 = GetMiddleUV( surf, uv1, uv2);
3726                 // uvMove = uvm - uv12
3727                 gp_XY uvMove = applyIn2D(surf, uvm, uv12, gp_XY_Subtracted, /*inPeriod=*/false);
3728                 ( is1 ? move1 : move0 ).SetCoord( uvMove.X(), uvMove.Y(), 0 );
3729                 if ( !is1 ) // correct nodeOnFace for move1 (issue 0020919)
3730                   nodeOnFace = (*(++chain.rbegin()))->_mediumNode;
3731                 isStraight[is1] = isStraightLink( (uv2-uv1).SquareModulus(),
3732                                                   10 * uvMove.SquareModulus());
3733               }
3734               if ( isStraight[0] && isStraight[1] ) {
3735                 MSG("2D straight - ignore");
3736                 continue; // straight - no need to move nodes of internal links
3737               }
3738
3739               // check if a chain is already fixed
3740               gp_XY uvm = GetNodeUV( face, linkOnFace->_mediumNode, 0, &checkUV);
3741               gp_XY uv1 = GetNodeUV( face, linkOnFace->node1(), nodeOnFace, &checkUV);
3742               gp_XY uv2 = GetNodeUV( face, linkOnFace->node2(), nodeOnFace, &checkUV);
3743               gp_XY uv12 = GetMiddleUV( surf, uv1, uv2);
3744               if (( uvm - uv12 ).SquareModulus() > 1e-10 )
3745               {
3746                 MSG("Already fixed - ignore");
3747                 continue;
3748               }
3749             }
3750           }
3751           gp_Trsf trsf;
3752           if ( isInside || face.IsNull() )
3753           {
3754             // compute node displacement of end links in their local coord systems
3755             {
3756               TChainLink& ln0 = chain.front(), ln1 = *(++chain.begin());
3757               trsf.SetTransformation( gp_Ax3( gp::Origin(), ln0.Normal(),
3758                                               gp_Vec( ln0->MiddlePnt(), ln1->MiddlePnt() )));
3759               move0.Transform(trsf);
3760             }
3761             {
3762               TChainLink& ln0 = *(++chain.rbegin()), ln1 = chain.back();
3763               trsf.SetTransformation( gp_Ax3( gp::Origin(), ln1.Normal(),
3764                                               gp_Vec( ln0->MiddlePnt(), ln1->MiddlePnt() )));
3765               move1.Transform(trsf);
3766             }
3767           }
3768           // compute displacement of medium nodes
3769           link2 = chain.begin();
3770           link0 = link2++;
3771           link1 = link2++;
3772           for ( int i = 0; link2 != chain.end(); ++link0, ++link1, ++link2, ++i )
3773           {
3774             double r = linkPos[i] / chainLen;
3775             // displacement in local coord system
3776             gp_Vec move = (1. - r) * move0 + r * move1;
3777             if ( isInside || face.IsNull()) {
3778               // transform to global
3779               gp_Vec x01( (*link0)->MiddlePnt(), (*link1)->MiddlePnt() );
3780               gp_Vec x12( (*link1)->MiddlePnt(), (*link2)->MiddlePnt() );
3781               gp_Vec x = x01.Normalized() + x12.Normalized();
3782               trsf.SetTransformation( gp_Ax3( gp::Origin(), link1->Normal(), x), gp_Ax3() );
3783               move.Transform(trsf);
3784             }
3785             else {
3786               // compute 3D displacement by 2D one
3787               Handle(Geom_Surface) s = BRep_Tool::Surface(face,loc);
3788               gp_XY oldUV   = GetNodeUV( face, (*link1)->_mediumNode, 0, &checkUV);
3789               gp_XY newUV   = applyIn2D( s, oldUV, gp_XY( move.X(),move.Y()), gp_XY_Added);
3790               gp_Pnt newPnt = s->Value( newUV.X(), newUV.Y());
3791               move = gp_Vec( XYZ((*link1)->_mediumNode), newPnt.Transformed(loc) );
3792 #ifdef _DEBUG_
3793               if ( (XYZ((*link1)->node1()) - XYZ((*link1)->node2())).SquareModulus() <
3794                    move.SquareMagnitude())
3795               {
3796                 gp_XY uv0 = GetNodeUV( face, (*link0)->_mediumNode, 0, &checkUV);
3797                 gp_XY uv2 = GetNodeUV( face, (*link2)->_mediumNode, 0, &checkUV);
3798                 MSG( "TOO LONG MOVE \t" <<
3799                      "uv0: "<<uv0.X()<<", "<<uv0.Y()<<" \t" <<
3800                      "uv2: "<<uv2.X()<<", "<<uv2.Y()<<" \t" <<
3801                      "uvOld: "<<oldUV.X()<<", "<<oldUV.Y()<<" \t" <<
3802                      "newUV: "<<newUV.X()<<", "<<newUV.Y()<<" \t");
3803               }
3804 #endif
3805             }
3806             (*link1)->Move( move );
3807             MSG( "Move " << (*link1)->_mediumNode->GetID() << " following "
3808                  << chain.front()->_mediumNode->GetID() <<"-"
3809                  << chain.back ()->_mediumNode->GetID() <<
3810                  " by " << move.Magnitude());
3811           }
3812         } // loop on chains of links
3813       } // loop on 2 directions of propagation from quadrangle
3814     } // loop on faces
3815   } // fix faces and/or volumes
3816
3817   // 4. Move nodes
3818   // -------------
3819
3820   for ( pLink = links.begin(); pLink != links.end(); ++pLink ) {
3821     if ( pLink->IsMoved() ) {
3822       gp_Pnt p = pLink->MiddlePnt() + pLink->Move();
3823       GetMeshDS()->MoveNode( pLink->_mediumNode, p.X(), p.Y(), p.Z());
3824     }
3825   }
3826
3827   // Issue 0020982
3828   // Move the apex of pyramid together with the most curved link.
3829   // TIDSortedNodeSet::iterator apexIt = apexOfPyramid.begin();
3830   // for ( ; apexIt != apexOfPyramid.end(); ++apexIt )
3831   // {
3832   //   SMESH_TNodeXYZ apex = *apexIt;
3833
3834   //   gp_Vec maxMove( 0,0,0 );
3835   //   double maxMoveSize2 = 0;
3836
3837   //   // shift of node index to get medium nodes between the base nodes
3838   //   const int base2MediumShift = 5;
3839
3840   //   // find maximal movement of medium node
3841   //   SMDS_ElemIteratorPtr volIt = apex._node->GetInverseElementIterator( SMDSAbs_Volume );
3842   //   vector< const SMDS_MeshElement* > pyramids;
3843   //   while ( volIt->more() )
3844   //   {
3845   //     const SMDS_MeshElement* pyram = volIt->next();
3846   //     if ( pyram->GetEntityType() != SMDSEntity_Quad_Pyramid ) continue;
3847   //     pyramids.push_back( pyram );
3848
3849   //     for ( int iBase = 0; iBase < apexIndex; ++iBase )
3850   //     {
3851   //       SMESH_TNodeXYZ medium = pyram->GetNode( iBase + base2MediumShift );
3852   //       if ( medium._node->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE )
3853   //       {
3854   //         SMESH_TNodeXYZ n1 = pyram->GetNode( iBase );
3855   //         SMESH_TNodeXYZ n2 = pyram->GetNode( ( iBase+1 ) % 4 );
3856   //         gp_Pnt middle = 0.5 * ( n1 + n2 );
3857   //         gp_Vec move( middle, medium );
3858   //         double moveSize2 = move.SquareMagnitude();
3859   //         if ( moveSize2 > maxMoveSize2 )
3860   //           maxMove = move, maxMoveSize2 = moveSize2;
3861   //       }
3862   //     }
3863   //   }
3864
3865   //   // move the apex
3866   //   if ( maxMoveSize2 > 1e-20 )
3867   //   {
3868   //     apex += maxMove.XYZ();
3869   //     GetMeshDS()->MoveNode( apex._node, apex.X(), apex.Y(), apex.Z());
3870
3871   //     // move medium nodes neighboring the apex to the middle
3872   //     const int base2MediumShift_2 = 9;
3873   //     for ( unsigned i = 0; i < pyramids.size(); ++i )
3874   //       for ( int iBase = 0; iBase < apexIndex; ++iBase )
3875   //       {
3876   //         SMESH_TNodeXYZ         base = pyramids[i]->GetNode( iBase );
3877   //         const SMDS_MeshNode* medium = pyramids[i]->GetNode( iBase + base2MediumShift_2 );
3878   //         gp_XYZ middle = 0.5 * ( apex + base );
3879   //         GetMeshDS()->MoveNode( medium, middle.X(), middle.Y(), middle.Z());
3880   //       }
3881   //   }
3882   // }
3883 }
3884